Pytorch机器学习(一)——FashionMNIST学习第一个神经网络

Pytorch机器学习(一)——FashionMNIST分类实现


前言

知道机器学习的,应该都对FashionMNIST数据集不陌生,这里就不对此数据集展开介绍了,直接从代码层面一步步的讲解,如何搭建自己第一个神经网络。
本文只是一个简单的对神经网络的搭建有个基础的认识,过细的知识就不讲了。

一、代码

1.引入库

import argparse
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torchvision.transforms import ToTensor
from torch.utils.data import DataLoader
from matplotlib import pyplot as plt
from torchsummary import summary

2.加载数据集

加载数据集主要有以下两步,调用datasets下载数据集,调用DataLoader去创建minibatch

2.1调用datasets

    #加载数据集
    transform=transforms.Compose([
        transforms.ToTensor(),
        #target_transform = Lambda(lambda y: torch.zeros(
    #10, dtype=torch.float).scatter_(dim=0, index=torch.tensor(y), value=1))
        ])
    training_data = datasets.FashionMNIST(
        root="data",
        train=True,
        download=True,
        transform=transform
    )

    test_data = datasets.FashionMNIST(
        root="data",
        train=False,
        download=True,
        transform=transform
    )

其中数据集是官方Pytorch自带的,给初学者有很大的帮助。
datasets里面还有很多常用的数据集

__all__ = ('LSUN', 'LSUNClass',
           'ImageFolder', 'DatasetFolder', 'FakeData',
           'CocoCaptions', 'CocoDetection',
           'CIFAR10', 'CIFAR100', 'EMNIST', 'FashionMNIST', 'QMNIST',
           'MNIST', 'KMNIST', 'STL10', 'SVHN', 'PhotoTour', 'SEMEION',
           'Omniglot', 'SBU', 'Flickr8k', 'Flickr30k',
           'VOCSegmentation', 'VOCDetection', 'Cityscapes', 'ImageNet',
           'Caltech101', 'Caltech256', 'CelebA', 'WIDERFace', 'SBDataset',
           'VisionDataset', 'USPS', 'Kinetics400', 'HMDB51', 'UCF101',
           'Places365')

而在里面可以设计的几个参数也是比较清楚的

参数 含义
root 数据集存放地址
train 是否设置为训练集
download 是否选择从指定网站下载,如果检测到root处包含数据集,则不下载
transform 是否对数据集中的数据和标签进行变化

其中transform可以设置对数据或者标签进行操作
transforms.ToTensor():将dataset类中__getitem__()方法内读入的PIL或CV的图像数据转换为torch.FloatTensor

而下面这段代码即可以将label原来的标签,假如说是9,变成独热编码(one-hot)

 target_transform = Lambda(lambda y: torch.zeros(
    10, dtype=torch.float).scatter_(dim=0, index=torch.tensor(y), value=1)

将feature变成tensor后,我们可以打印一下其属性和其代表的东西看看,有个直观的了解

feature,label = training_data[0]
print(feature.shape)
print(label)
plt.figure()
plt.imshow(feature.squeeze(),cmap='gray')  #1*28*28格式需要压缩成28*28形式
plt.title(label)
plt.show()

输出如下

torch.Size([1, 28, 28])
9

在这里插入图片描述

2.2 调用DataLoader构建minibatch

我们得到基础的数据集后,我们需要对数据集进行一些简单的处理,其中pytorch也给了我们很方便的方法,就是构建一个DataLoader的对象

train_loader = DataLoader(training_data,batch_size=64,shuffle=True)
test_loader = DataLoader(test_data, batch_size=64,shuffle=True)

其中这里面参数较多,挑两个经常使用的

参数 含义
dataset 数据集
batch_size 每次训练batch中样本的个数
shuffle 在每个epoch开始的时候,是否对数据进行重新排序

其中DataLoader只可用迭代的方法访问

for batch_index,(feature,label) in enumerate(train_loader):
    print(batch_index)
    print(feature.shape)
    print(label.shape)

输出

0
torch.Size([10, 1, 28, 28])
torch.Size([10])

可以看到我们的数据已经被打包起来了,成为一个个batch。

我们也可以看看这个时候是否被打乱了

for batch_index,(feature,label) in enumerate(train_loader):
    plt.figure()
    for i in range(9):
        plt.subplot(3,3,i+1)
        plt.imshow(feature[i].squeeze(),cmap="gray")
        plt.title(label[i].item())
        plt.axis("off")
    plt.show()

输出
在这里插入图片描述
至此我们就算是处理好我们的数据集了。

3.定义模型

定义模型也是十分简单的,我们只需要定义两部分即可,一部分是自己构建的子模块,另一部分是我们用子模块构建的前向计算图forward

这里就不细说这个模型是什么了,因为对于向FashionMNIST这种简单的数据集来说,随便构建一个模型,基本都能跑的85%的准确率,读者可以自行构建动手试试。

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 64, 3,padding=1)
        self.conv2 = nn.Conv2d(64, 64, 3,padding=1)
        self.conv3 = nn.Conv2d(64,128,3,padding=1)
        self.conv4 = nn.Conv2d(128,128,3,padding=1)

        self.dropout1 = nn.Dropout(0.25)

        self.fc1 = nn.Linear(128*7*7, 512)
        self.fc2 = nn.Linear(512, 10)

        self.bn1 = nn.BatchNorm2d(64)
        self.bn2 = nn.BatchNorm2d(128)

        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = F.max_pool2d(x, 2)
        x = self.bn1(x)
        x = self.relu(x)

        x = self.conv3(x)
        x = self.conv4(x)
        x = F.max_pool2d(x, 2)
        x = self.bn2(x)
        x = self.relu(x)

        x = torch.flatten(x, 1)
        x = self.fc1(x)
        x = self.relu(x)
        x = self.dropout1(x)
        x = self.fc2(x)
        #output = F.softmax(x)
        return x

我们定义完模型后,可以去实例化我们的模型,并告诉电脑,这个模型将使用GPU进行计算

device = "cuda" if torch.cuda.is_available() else "cpu"
model = Net().to(device)

我们实例化模型后,我们可以通过一些方法去看里面到底有什么,比如想看看参数

for para in model.parameters():
    print(para)

输出

Parameter containing:
tensor([[[[-2.1682e-01, -1.7200e-01,  6.2761e-02],
          [-5.9057e-02, -2.9793e-01,  2.4450e-01],
          [ 3.0849e-01, -2.5328e-01, -3.0286e-01]]],


        [[[-3.2513e-01, -2.2943e-01, -2.9522e-02],
          [ 1.2432e-02,  2.9955e-01,  6.0680e-02],
          [-2.2615e-01,  2.4802e-01, -3.3225e-01]]],
.....

可以看到我们初始的参数都是随机的,准确的说是符合正态分布的。

我们也可以用summary来看网络的整体结构

summary(model,(1,28,28))

输出如下

----------------------------------------------------------------
        Layer (type)               Output Shape         Param #
================================================================
            Conv2d-1           [-1, 64, 28, 28]             640
            Conv2d-2           [-1, 64, 28, 28]          36,928
       BatchNorm2d-3           [-1, 64, 14, 14]             128
              ReLU-4           [-1, 64, 14, 14]               0
            Conv2d-5          [-1, 128, 14, 14]          73,856
            Conv2d-6          [-1, 128, 14, 14]         147,584
       BatchNorm2d-7            [-1, 128, 7, 7]             256
              ReLU-8            [-1, 128, 7, 7]               0
            Linear-9                  [-1, 512]       3,211,776
             ReLU-10                  [-1, 512]               0
          Dropout-11                  [-1, 512]               0
           Linear-12                   [-1, 10]           5,130
================================================================
Total params: 3,476,298
Trainable params: 3,476,298
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.00
Forward/backward pass size (MB): 1.45
Params size (MB): 13.26
Estimated Total Size (MB): 14.71
----------------------------------------------------------------

4.开始训练模型

训练模型实质上也十分简单,一是使用网络去计算我们的输出,二是将网络的输出与输入使用损失函数进行评价,三是反向传播计算梯度,四是用优化器进行参数更新。

def train(args, model, device, train_loader, loss_fn, optimizer, epoch):
    model.train()           #打开batch normalize和dropout进行更新
    for batch_idx, (data, target) in enumerate(train_loader):       #取出数据
        data, target = data.to(device), target.to(device)			#放到GPU中计算
        optimizer.zero_grad()                                       #清空上一个batch中的梯度
        output = model(data)                                        #计算网络输出结果
        pred = output.argmax(dim=1)                                 #对网络输出结果进行argmax计算,选出当前确认值
        loss = loss_fn(output,target)                               #计算损失函数
        loss.backward()                                             #反向传播计算梯度
        optimizer.step()                                            #使用优化器根据梯度更新参数
        if batch_idx % 100 == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), loss.item()))

4.对模型训练结果进行评估

训练完模型后,我们可以用test训练集对模型进行评估

def test(model, device,loss_fn, test_loader):
    model.eval()                                                #关闭batch normalize和dropout防止更新
    total_loss = 0
    correct = 0
    with torch.no_grad():                                       #关闭梯度更新
        for data, target in test_loader:                        #载入训练集
            data, target = data.to(device), target.to(device)   #放入GPU中计算
            output = model(data)                                #计算网络计算结果
            total_loss +=loss_fn(output, target).item()         #计算损失
            pred = output.argmax(dim=1, keepdim=True)           #得到网络计算出最可能的结果
            correct += pred.eq(target.view_as(pred)).sum().item()   #判断是否正常

    Average_loss = total_loss / len(test_loader.dataset)

    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        Average_loss, correct, len(test_loader.dataset),
        100. * correct / len(test_loader.dataset)))
    

5.对模型进行多次训练

我们需要对模型进行多次训练已达到比较高的准确率

   #设置模型,损失函数,优化器等
    model = Net().to(device)
    optimizer = optim.Adadelta(model.parameters(), lr=args.lr)
    loss_fn = nn.CrossEntropyLoss()
    #scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)

    #训练epoch
    for epoch in range(1, args.epochs + 1):
        train(args, model, device, train_loader, loss_fn, optimizer, epoch)
        test(model, device, loss_fn,test_loader)

6.保存模型

#保存模型
torch.save(model.state_dict(), "mnist_cnn.pt")

总结

总的来说搭建一个神经网络就包含以上步骤,这个文章出的比较简略,因为只是相当于是一个对神经网络的初认识,后面会更新比较详细的文章。

Guess you like

Origin blog.csdn.net/lzzzzzzm/article/details/118675164