用PyTorch对以MNIST数据集进行卷积神经网络

Posted Paul-Huang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用PyTorch对以MNIST数据集进行卷积神经网络相关的知识,希望对你有一定的参考价值。

PyTorch构建深度学习网络一般步骤

  1. 加 载 数 据 集 \\color{red}加载数据集
  2. 定 义 网 络 结 构 模 型 \\color{red}定义网络结构模型
  3. 定 义 损 失 函 数 \\color{red}定义损失函数
  4. 定 义 优 化 算 法 \\color{red}定义优化算法
  5. 迭 代 训 练 \\color{red}迭代训练
    在训练阶段主要分为四个部分。
    1. 前向过程,计算输入到输出的结果
    2. 由结果和labels计算损失函数的值
    3. 后向过程,由损失计算各个变量的梯度
    4. 优化器根据梯度进行参数的更新
  6. 测 试 集 验 证 \\color{red}测试集验证

1. 配置库

#配置库
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

解释:

  1. optim是优化器模块,里面包括具体的优化算法有:SGD、Momentum、RMSProp、AdaGrad和Adam

    其中Momentum是加速梯度下降算法,其他三个是改进学习率。常用的有:SGD和Adam。

  2. Variable是对tensor的封装,用于放入计算图进行前向传播、反向传播和自动求导,是一个非常最重要的基本对象。包含三个重要属性:datagradcreator。其中:
    1. data表示Tensor的本身;
    2. grad表示传播方向的梯度,
    3. creator是创建这个Variable的Function引用,该引用用于回溯整个创建链路。

    如果是用户创建的Variable,则creator为None,同时这种Variable称为Leaf Variable,autograd只会给这种Variable分配梯度。

  3. DataLoaderDataset是一个包装类,用来将数据包装为Dataset类,然后传入DataLoader中,我们再使用DataLoader这个类来更加快捷的对数据进行操作。

    具体来说,就是将数据集包装成特定的格式,便于处理和引用。

  4. torchvision.transforms是pytorch中的图像预处理模块,包含了很多种对图像数据进行变换的函数,这些都是在进行图像数据读入步骤中必不可少的。
  5. datasets顾名思义,是一系列数据集,可以通过相应的命令加载诸如MNIST等的数据集。

2. 配置超参数

#配置参数
torch.manual_seed(1)
Learning_rate = 1e-2
Batch_Size = 16   # 没批处理的数据
Device = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # 用CPU还是GPU
Epochs = 100     # 训练数据集的轮次

解释:

  1. torch.manual_seed()是设置随机数种子,保证程序数字的可重复性,便于测试等。
  2. batch_size是批处理大小(批尺寸),即每次处理的数据量。 迭代次数 = 样本总数 批尺寸 \\color{red}\\text{迭代次数}=\\frac{\\text{样本总数}}{\\text{批尺寸}} 迭代次数=批尺寸样本总数;在一定范围内,批尺寸越大,跑完一次全数据集(一个epoch)需要的迭代次数越少,处理数据速度也越快。但盲目增大会导致达到一定精度所需时间更大。
  3. learning_rate为学习率。学习率越小,每次梯度下降的步伐越小,要达到目标精度所需训练次数也更多;但学习率过大会导致梯度下降不收敛,达不到学习的目的。
  4. num_epocher 是数据集训练次数,即跑几遍数据集。

3. 加载数据集

## 3.构建pipeline,对图像做处理,transform
pipeline = transforms.Compose([
    transforms.ToTensor(),   # 将图片转换成Tensor
    transforms.Normalize((0.1307,), (0.3081, )) # 正则化,降低模型的复杂度
])

## 4.下载、加载数据
# 下载数据集
train_set = datasets.MNIST(root="data_Mnist", train=True, download=True, transform=pipeline)
test_set = datasets.MNIST(root="data_Mnist", train=False, download=True, transform=pipeline)

# 加载数据
train_loader = DataLoader(train_set, batch_size=Batch_Size, shuffle=True)    # 加载图片,进行打乱
test_loader = DataLoader(test_set, batch_size=Batch_Size, shuffle=True)

解释:

  1. 加载datasets中的MNIST手写数字数据集,可以加载本地下载好的数据集。
    • root为数据集存放的位置;
    • train表示是否是训练集;
    • transform.ToTensor()是将数据集数据归一化,将值为[0,255]的Image转化为[0,1.0]的Tensor数据,归一化能够提高梯度下降算法的收敛速度;
    • downlaod代表是否需要在线下载数据集,若本地没有则需要为True。
      加载测试集,参数设置与训练集类似。
  2. DataLoader用于包装数据,并提供对数据的快捷处理,其中shuffle参数为是否打乱顺序,训练集必须要打乱数据的次序,防止过拟合。

4. 定义卷积网络结构模型(重点)

##5. 构建网络模型
class Digit(nn.Module):
    def __init__(self):  # 定义自己的构造方法
        super().__init__()  # 继承构造方法
        self.conv1 = nn.Conv2d(1, 10, 5)  # 1:灰度图片的通道    10:输出通道    5:kernel  padding默认为0,输出:10*26*26
        self.conv2 = nn.Conv2d(10, 20, 3)  # 10:输入通道    20:输出通道      3:Kernel   padding默认为0,输出:20*24*24
        self.fc1 = nn.Linear(20 * 10 * 10, 500)  # 20*10*10:输入通道     500:输出通道
        self.fc2 = nn.Linear(500, 10)  # 500:输入通道   10:输出通道

    def forward(self, x):
        input_size = x.size(0)          # batch_size
        x = self.conv1(x)               # 输入:batch*1*28*28, 输出:batch*10*24*24(28-5+1=24)
        x = F.relu(x)                   # 激活函数shape不变
        x = F.max_pool2d(x, 2, 2)       # 输入:batch*10*24*24  输出:batch*10*12*12
        x = self.conv2(x)               # 输入:batch*10*12*12 输出:batch*20*10*1012-3+1=10)
        x = F.relu(x)  #
        x = x.view(input_size, -1)      # view:拉平,-1 自动计算维度:20*10*10 = 2000
        x = self.fc1(x)                 # 输入:batch*2000 输出:batch*500
        x = F.relu(x)                   # 保持shape不变
        x = self.fc2(x)                 # 输入: batch*500 输出:batch*10
        output = F.log_softmax(x, dim=1)  # 计算分类后每个数字的概率值

        return output

解释:

  1. nn.Moudle是十分重要的类,包含网络各层的定义,以及forword函数的定义 自 定 义 的 网 络 结 构 模 型 都 需 要 继 承 M o u d l e 类 \\color{red}自定义的网络结构模型都需要继承Moudle类 Moudle

    自 定 义 网 络 结 构 \\color{blue}自定义网络结构

    1. 需要继承nn.Module类,并实现forward方法。
    2. 一般把网络中具有可学习参数的层放在构造函数__init__()中,
    3. 不具有可学习参数的层(如ReLU)可放在构造函数中,也可不放在构造函数中(而在forward中使用nn.functional来代替)
    4. 只要在nn.Module的子类中定义了forward函数,backward函数就会被自动实现,而不需要像forword那样需要重新定义。
  2. super类的作用是 继 承 \\color{red}继承 ,调用含super的各个的基类__init__函数。

    如果不使用super,就不会调用这些类的__init__函数,除非显式声明。而且使用super可以避免基类被重复调用。

  3. nn.sequential官方的解释是一个有序的容器,神经网络模块将按照在传入构造器的顺序依次被添加到计算图中执行,同时以神经网络模块为元素的有序字典也可以作为传入参数。在这里的作用是构造神经网络结构。

  4. nn.Conv2d是卷积神经网络的卷积层函数,其默认参数如下:

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

    程序中的参数解释:
      - in_channels: 输入数据通道数,RGB图片通道数为3,黑白图片的通道数1,MNIST数据集中是黑白图像,参数为1;
      - out_channel: 输出数据通道数,也称为卷积核数量,和提取的特征数量相关,这里取6;
      - kennel_size: 卷积核大小;这里取卷积核大小为3*3;
      - stride:步长,默认为1;
      - padding:填充数量,为保持输出尺寸不变,这里取1。

卷积的计算公式:
Input: ( N , C in  , H in  , W in  ) \\left(N, C_{\\text {in }}, H_{\\text {in }}, W_{\\text {in }}\\right) (N,Cin ,Hin ,Win )
Output: ( N , C out  , H out  , W out  ) \\left(N, C_{\\text {out }}, H_{\\text {out }}, W_{\\text {out }}\\right) (N,Cout ,Hout ,Wout ) where
H out  = ⌊ H in  + 2 ×  padding  [ 0 ] −  dilation  [ 0 ] × ( k e r n e l    s i z e [ 0 ] − 1 ) − 1  stride  [ 0 ] + 1 ⌋ . W out  = ⌊ W in  + 2 ×  padding  [ 1 ] −  dilation  [ 1 ] × ( k e r n e l    s i z e [ 1 ] − 1 ) − 1  stride  [ 1 ] + 1 ⌋ . \\begin{aligned} H_{\\text {out }} &=\\left\\lfloor\\frac{H_{\\text {in }}+2 \\times \\text { padding }[0]-\\text { dilation }[0] \\times(kernel\\;size[0]-1)-1}{\\text { stride }[0]}+1\\right\\rfloor.\\\\ W_{\\text {out }} &=\\left\\lfloor\\frac{W_{\\text {in }}+2 \\times \\text { padding }[1]-\\text { dilation }[1] \\times(kernel\\;size[1]-1)-1}{\\text { stride }[1]}+1\\right\\rfloor. \\end{aligned} Hout Wout = stride [0]Hin +2× padding [0] dilation [0]×(kernelsize[0]1)1+1.= stride [1]Win +2× padding [1] dilation [1]×(kernelsize[1]1)1+1.

  1. nn.Relu是激活函数,具有减少计算量,缓解过拟合的作用。

  2. nn.MaxPool2d是卷积神经网络的池化层,具有降采样,减少计算量的功能。这里采用的是最大池化层,卷积核大小为2*2,步长为2。池化层输出尺寸计算方法为:

    W:图像宽,H:图像高,D:图像深度(通道数)F:卷积核宽高,S:步长
    池化后输出图像大小:
    W = ( W − F ) S + 1 H = ( H − F ) S + 1 W=\\frac{(W-F)}{S}+1\\\\H=\\frac{(H-F)}{S}+1 W=S(WF)+1H=S(HF)+1
    输出的图像通道数(深度)不变。