pytorch实现MNIST识别——全流程

主要实现

  1. 掌握pytorch自带数据集的导入
  2. 初步编写DataLoader
  3. 定义模型、损失和优化器
  4. 训练简单神经网络
  5. 将模型结果保存至本地

参考https://zhuanlan.zhihu.com/p/128137225

  1. 库函数
import torch
import torchvision
from torch.autograd import Variable
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import numpy as np
import cv2
  1. 数据部分

1.1 Dataset

torchvision.datasets自带的数据集中下载、导入MNIST数据集

train_dataset = datasets.MNIST(
    root = 'data/',
    train = True,
    transform = transforms.ToTensor(),
    download = True
)
test_dataset = datasets.MNIST(
    root = 'data/',
    train = False,
    transform = transforms.ToTensor(),
    download = True
)

1.2 DataLoader

使用 torch.utils.data.DataLoader()声明数据发生器

train_loader = DataLoader(
    dataset=train_dataset,
    batch_size=100,
    shuffle=True
)

test_loader = DataLoader(
    dataset=test_dataset,
    batch_size=100,
    shuffle=True
)

1.3 数据可视化


plt.figure("Image")
plt.figure(figsize=(4,4))
plt.imshow(np.array(train_dataset[0][0])[0], cmap=plt.cm.binary)
plt.axis('on')
plt.title('image')
plt.show()

pytorch实现MNIST识别——全流程
  1. 定义模型
class Model(torch.nn.Module):
    '''
    自定义模型继承自torch.nn.Module
    将各种层定义到self中
    要复写forward()
    '''
    def __init__(self):
        super(Model, self).__init__()

        self.conv1 = torch.nn.Sequential(
            torch.nn.Conv2d(1, 64, 3, 1, 1),
            torch.nn.ReLU(),
            torch.nn.Conv2d(64, 128, 3, 1, 1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(2, 2)
        )

        self.dense = torch.nn.Sequential(
            torch.nn.Linear(14*14*128, 1024),
            torch.nn.ReLU(),
            torch.nn.Dropout(p = 0.5),
            torch.nn.Linear(1024, 10)
        )

    def forward(self, x):
        x = self.conv1(x)
        x = x.view(-1, 14*14*128)
        x = self.dense(x)
        return x

2.1 顺序容器——Sequential()

torch.nn.Sequential()是一个有序的容器。自定义的神经网络模块按照Sequential()的参数顺序依次执行。

因为自定义的Model继承自 nn.Module下,所以在forward函数中通过for循环依次执行Sequential()中的各模块。

Sequential参考:https://blog.csdn.net/dss_dssssd/article/details/82980222

2.2 卷积层——Conv2d

nn.Conv2d:对由多个输入平面组成的输入信号做 二维卷积。(Applies a 2D convolution over an input signal composed of several input planes.)

函数定义为

torch.nn.Conv2d(
    in_channels,
    out_channels,
    kernel_size,
    stride=1,
    padding=0,
    dilation=1,
    groups=1,
    bias=True
)

参数

参数参数类型infoin_channelint输入图像通道数out_channelint输出图像通道数(同时也是卷积核个数)kernel_sizeint or (tuple)卷积核大小strideint or (tuple)卷积步长,默认为1paddingint or (tuple)填充操作个数padding_modestring填充模式dilationint or (tuple)扩张操作(空洞卷积)groupsint控制分组卷积biasbool是否添加可学习的偏置

卷积参考:https://blog.csdn.net/qq_34243930/article/details/107231539

2.3 全连接层——dense

将第一个卷积网络层(self.conv1)的输出 压缩至一维后输入dense层。

  • nn.Linear()是类似于Ax+b的函数,可以设置bias=True or False表示是否有偏置值b。
  • nn.Dropout(x, p)确保没有过拟合。参数x是输入(上述代码中因为Dropout在Sequential中,所以省去了x);p是需要删除的神经元的比例( p=0时保留全部神经元;p=1时神经元输出值为0)

dense层输入值计算

输 出 = ( i n p u t − k e r n e l + 1 + 2 ∗ p a d d i n g ) / s t r i d e 输出 = (input – kernel + 1 + 2 * padding)/ stride 输出=(i n p u t −k e r n e l +1 +2 ∗p a d d i n g )/s t r i d e

  • 第一个卷积层使用了64个3*3的卷积核,步长为1,填充数为1。计算得到的输出大小为( 28 − 3 + 1 + 1 ∗ 2 ) / 1 = 28 (28 – 3 + 1 + 1 * 2) / 1 = 28 (2 8 −3 +1 +1 ∗2 )/1 =2 8。 即64 * 28 * 28
  • 第二个卷积层输出大小同理为28。即128 * 28 *28
  • 第三个池化层,kernel为2, 步长为2。输出大小为14 = 28 / 2。

由此,卷积网络最后的得到的是128 * 14 * 14的输出

2.4 前馈——forward

将从卷积网络得到的输出x经过变形后送给全连接层
x = x.view(-1, 14*14*128)

  1. 损失函数与优化方法
cuda_available = torch.cuda.is_available()

if cuda_available:
    print("cuda loaded")
    device = torch.device('cuda')
    model = Model().to(device)
else:
    model = Model()

cost = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())
cuda loaded

3.1 实例化模型

model = Model().to(torch.device('cuda'))使用gpu算力。

3.2 交叉熵损失

公式为H ( p , q ) = − ∑ i p i ∗ l o g q i H(p, q) = – \sum_{i}{p_{i} * log q_{i}}H (p ,q )=−∑i ​p i ​∗l o g q i ​

3.3 优化器

使用Adam优化器,结合RMSProp(自适应调整学习率)和Momentum(动量)。

  • RMSProp方法不断更新xuexil
  • Momentum通过上一次的更新增强或削弱梯度。如果梯度方向与上一次相同,则增强;不同,则削减。

  • 训练与测试

if __name__ == '__main__':

    model.train()
    epochs = 5
    for epoch in range(epochs):

        sum_loss = 0.0
        train_correct = 0

        for data in train_loader:

            inputs, labels = data
            inputs, labels = Variable(inputs).cuda(), Variable(labels).cuda()
            optimizer.zero_grad()
            outputs = model(inputs)
            loss = cost(outputs, labels)
            loss.backward()
            optimizer.step()

            _, id = torch.max(outputs.data, 1)
            sum_loss += loss.data
            train_correct += torch.sum(id == labels.data)
        print('[%d,%d] loss:%.03f' % (epoch + 1, epochs, sum_loss / len(train_loader)))
        print('        correct:%.03f%%' % (100 * train_correct / len(train_dataset)))

    model.eval() 测试需要用到model的eval()模式,以免将测试数据也用于训练。
    with torch.no_grad():
        test_correct = 0
        for data in test_loader:
            inputs, lables = data
            inputs, lables = Variable(inputs).cuda(), Variable(lables).cuda()
            outputs = model(inputs)
            _, id = torch.max(outputs.data, 1)
            test_correct += torch.sum(id == lables.data)
        print("\ntest correct:%.3f%%" % (100 * test_correct / len(test_dataset)))

输出结果

[1,5] loss:0.006
        correct:99.803%
[2,5] loss:0.006
        correct:99.800%
[3,5] loss:0.006
        correct:99.798%
[4,5] loss:0.005
        correct:99.835%
[5,5] loss:0.005
        correct:99.865%

test correct:99.160%

4.1 训练

本轮训练进行了5次,即5个epoch。

在训练每个batch时,要依次进行

  1. 清零上一个batch计算得到的梯度,以免梯度累加
  2. 计算损失loss
  3. 利用backward()对损失loss进行反向传播
  4. 利用step()对优化器进行梯度下降(参数数据都保存在optimizer中)

打印精度

torch.max(a,b)对a中的固定第b维的情况下,计算最大值,返回最大值及其索引。这里是固定outputs的列,对行求最大值。outputs返回的值可以看作是归属每个类的概率,取最大概率作为最终结果。

Variable

autograd.Variable()包装一个Tensor,使其包括 .data.grad两种属性。用 Variable()封装好的张量可以进行 .backward()反向传播运算。

Variable():https://blog.csdn.net/scutjy2015/article/details/71214928/

4.2 测试

在模型中,我们通常会加上Dropout层和batch normalization层,在模型预测阶段,我们需要将这些层设置到预测模式。否则会导致不一致的预测结果。
通常也使用 torch.no_grad()关闭梯度的计算,如

model.eval()

with torch.no_grad():
    ...

    out_data = model(data)
    ...

详细model.eval():https://zhuanlan.zhihu.com/p/356500543

  1. 保存模型

将训练结果保存至本地,下次训练直接load结果


torch.save(model.state_dict(), "parameter.pkl")

model.load_state_dict(torch.load('parameter.pkl'))

输出结果

< All keys matched successfully >

Original: https://blog.csdn.net/m0_56945333/article/details/123161867
Author: KimJuneJune
Title: pytorch实现MNIST识别——全流程

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/691491/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球