Educoder 机器学习 神经网络 第四关:使用pytorch搭建卷积神经网络识别手写数字

  • 任务描述
  • 相关知识
    • 卷积神经网络
      • 为什么使用卷积神经网络
      • 卷积
      • 池化
      • 全连接网络
      • 卷积神经网络大致结构
    • pytorch构建卷积神经网络项目流程
      • 数据集介绍与加载数据
      • 构建模型
      • 训练模型
      • 保存模型
      • 加载模型
  • 编程要求
  • 测试说明

任务描述

本关任务:使用pytorch搭建出卷积神经网络并对手写数字进行识别。

相关知识

为了完成本关任务,你需要掌握:1.卷积神经网络,2.pytorch构建卷积神经网络项目流程。

卷积神经网络

为什么使用卷积神经网络

卷积神经网络最早主要用来处理图像信息。如果用全连接前馈网络来处理图像时,会存在以下两个问题:


  1. 参数太多:如果输入图像大小为100 × 100 × 3。在全连接前馈网络中,第一个隐藏层的每个神经元到输入层都有 30000个相互独立的连接,每个连接都对应一个权重参数。随着隐藏层神经元数量的增多,参数的规模也会急剧增加。这会导致整个神经网络的训练效率会非常低,也很容易出现过拟合。
  2. 局部不变性特征: 自然图像中的物体都具有局部不变性特征,比如在尺度缩放、平移、旋转等操作不影响其语义信息。而全连接前馈网络很难提取这些局部不变特征,一般需要进行数据增强来提高性能。

卷积神经网络是一种具有局部连接、 权重共享等特性的深层前馈神经网络。想要识别图像中的物体,就需要提取出比较好的特征,该特征应能很好地描述想要识别的物体。所以物体的特征提取是一项非常重要的工作。而图像中物体的特征以下几种特点:

1.物体的特征可能只占图像中的一小部分。比如下图中狗的鼻子只是图像中很小的一部分。

Educoder 机器学习 神经网络 第四关:使用pytorch搭建卷积神经网络识别手写数字_第1张图片

2.同样的特征可能出现在不同图像中的不同位置,比如下图中狗的鼻子在两幅图中出现的位置不同。

Educoder 机器学习 神经网络 第四关:使用pytorch搭建卷积神经网络识别手写数字_第2张图片

3.缩放图像的大小对物体特征的影响可能不大,比如下图是缩小后的图,但依然能很清楚的辨认出狗的鼻子。

Educoder 机器学习 神经网络 第四关:使用pytorch搭建卷积神经网络识别手写数字_第3张图片

而卷积神经网络中的卷积池化操作能够较好地抓住物体特征的以上3种特点。

卷积

卷积说白了就是有一个卷积核(其实就是一个带权值的滑动窗口)在图像上从左到右,从上到下地扫描,每次扫描的时候都会将卷积核里的值所构成的矩阵与图像被卷积核覆盖的像素值矩阵做内积。整个过程如下图所示,其中黄色方框代表卷积核,绿色部分代表单通道图像,红色部分代表卷积计算后的结果,通常称为特征图:

Educoder 机器学习 神经网络 第四关:使用pytorch搭建卷积神经网络识别手写数字_第4张图片

那为什么说卷积能够提取图像中物体的特征呢?其实很好理解,上图中的卷积核中值的分布如下:

Educoder 机器学习 神经网络 第四关:使用pytorch搭建卷积神经网络识别手写数字_第5张图片

当这个卷积核卷积的时候就会在3行3列的小范围内计算出图像中几乎所有的33列子图像与卷积核的相似程度(也就是内积的计算结果)。相似程度越高说明该区域中的像素值与卷积核越相似。(上图的特征图中值为4的位置所对应到的源图像子区域中像素值的分布与卷积核值的分布最为接近)这也就说明了卷积在提取特征时能够考虑到特征可能只占图像的一小部分,以及同样的特征可能出现在不同的图像中不同的位置这两个特点。

PS:卷积核的值是怎么确定下来的?很明显是训练出来的!

池化

池化就是将输入图像进行缩小,减少像素信息,只保留重要信息。 池化的操作也很简单,通常情况下,池化区域是22列的大小,然后按一定规则转换成相应的值,例如最常用的最大池化(max pooling)。最大池化保留了每一小块内的最大值,也就是相当于保留了这一块最佳的匹配结果。举个例子,如下图中图像是44列的,池化区域是22列的,所以最终池化后的特征图是22列的。图像中粉色区域最大的值是6,所以池化后特征图中粉色位置的值是6,图像中绿色区域最大的值是8,所以池化后特征图中绿色位置的值是8,以此类推。

Educoder 机器学习 神经网络 第四关:使用pytorch搭建卷积神经网络识别手写数字_第6张图片

从上图可以看出,最大池化不仅仅缩小了图像的大小,减少后续卷积的计算量,而且保留了最佳的特征(如果图像是经过卷积后的特征图)。也就相当于把图缩小了,但主要特征还在,这就考虑到了缩放图像的大小对物体的特征影响可能不大的特点。

全连接网络

卷积与池化能够很好的提取图像中物体的特征,当提取好特征之后就可以着手开始使用全连接网络来进行分类了。全连接网络的大致结构如下:

Educoder 机器学习 神经网络 第四关:使用pytorch搭建卷积神经网络识别手写数字_第7张图片

其中输入层通常指的是对图像进行卷积,池化等计算之后并进行扁平后的特征图。隐藏层中每个方块代表一个神经元,每一个神经元可以看成是一个很简单的线性分类器和激活函数的组合。输出层中神经元的数量一般为标签类别的数量,激活函数为softmax(因为将该图像是猫或者狗的得分进行概率化)。因此我们可以讲全连接网络理解成很多个简单的分类器的组合,来构建成一个非常强大的分类器。

卷积神经网络大致结构

将卷积,池化,全连接网络进行合理的组合,就能构建出属于自己的神经网络来识别图像中是猫还是狗。通常来说卷积,池化可以多叠加几层用来提取特征,然后接上一个全连接网络来进行分类。大致结构如下:

Educoder 机器学习 神经网络 第四关:使用pytorch搭建卷积神经网络识别手写数字_第8张图片

pytorch构建卷积神经网络项目流程

数据集介绍与加载数据

本次使用数据集为mnist手写数字数据集,简单来讲就是如下的东西:

Educoder 机器学习 神经网络 第四关:使用pytorch搭建卷积神经网络识别手写数字_第9张图片

数据集分为训练集与测试集,训练集中一共有60000张图片,测试集中一共有10000张图片,每张图片大小为28X28x1。图片标签为对应的数字,如8对应的label8,若使用onehot编码则对应的label为:[0,0,0,0,0,0,0,0,1,0]。 为节约计算时间,我们取训练集中的6000张图片用来训练,测试集中的600张进行测试。使用pytorch加载数据集方法如下:


  1. #加载数据
  2. import torchvision
  3. train_data = torchvision.datasets.MNIST(
  4. root='./step3/mnist/',
  5. train=True, # this is training data
  6. transform=torchvision.transforms.ToTensor(), # Converts a PIL.Image or numpy.ndarray to
  7. download=False,
  8. )
  9. #取6000个样本为训练集
  10. train_data_tiny = []
  11. for i in range(6000):
  12. train_data_tiny.append(train_data[i])
  13. train_data = train_data_tiny

构建模型

加载好数据集,就需要构建卷积神经网络模型:


  1. #构建卷积神经网络模型
  2. class CNN(nn.Module):
  3. def __init__(self):
  4. super(CNN, self).__init__()
  5. self.conv1 = nn.Sequential( # input shape (1, 28, 28)
  6. nn.Conv2d(
  7. in_channels=1, # input height
  8. out_channels=16, # n_filters
  9. kernel_size=5, # filter size
  10. stride=1, # filter movement/step
  11. padding=2, # if want same width and length of this image after con2d, padding=(kernel_size-1)/2 if stride=1
  12. ), # output shape (16, 28, 28)
  13. nn.ReLU(), # activation
  14. nn.MaxPool2d(kernel_size=2), # choose max value in 2x2 area, output shape (16, 14, 14)
  15. )
  16. self.conv2 = nn.Sequential( # input shape (16, 14, 14)
  17. nn.Conv2d(16, 32, 5, 1, 2), # output shape (32, 14, 14)
  18. nn.ReLU(), # activation
  19. nn.MaxPool2d(2), # output shape (32, 7, 7)
  20. )
  21. self.out = nn.Linear(32 * 7 * 7, 10) # fully connected layer, output 10 classes
  22. def forward(self, x):
  23. x = self.conv1(x)
  24. x = self.conv2(x)
  25. x = x.view(x.size(0), -1) # flatten the output of conv2 to (batch_size, 32 * 7 * 7)
  26. output = self.out(x)
  27. return output
  28. cnn = CNN()

需要指出的几个地方:


  1. class CNN需要继承Module
  2. 需要调用父类的构造方法:super(CNN, self).__init__()
  3. 在Pytorch中激活函数Relu也算是一层layer
  4. 需要实现forward()方法,用于网络的前向传播,而反向传播只需要调用Variable.backward()即可。

定义好模型后还要构建优化器与损失函数: torch.optim是一个实现了各种优化算法的库。使用方法如下:


  1. #SGD表示使用随机梯度下降方法,lr为学习率,momentum为动量项系数
  2. optimizer = torch.optim.SGD(model.parameters(), lr = 0.01, momentum=0.9)
  3. #交叉熵损失函数
  4. loss_func = nn.CrossEntropyLoss()

训练模型

在定义好模型后,就可以根据反向传播计算出来的梯度,对模型参数进行更新,在pytorch中实现部分代码如下:


  1. #将梯度清零
  2. optimizer.zero_grad()
  3. #对损失函数进行反向传播
  4. loss.backward()
  5. #训练
  6. optimizer.step()

保存模型

pytorch中使用torch.save保存模型,有两种方法,第一种: 保存整个模型和参数,方法如下:


  1. torch.save(model, PATH)

第二种为官方推荐,只保存模型的参数,方法如下:


  1. torch.save(model.state_dict(), PATH)

加载模型

对应两种保存模型的方法,加载模型也有两种方法,第一种如下:


  1. model = torch.load(PATH)

第二种:


  1. #CNN()为你搭建的模型
  2. model = CNN()
  3. model.load_state_dict(torch.load(PATH))

如果要对加载的模型进行测试,需将模型切换为验证模式


  1. model.eval()

编程要求

使用pytorch搭建出卷积神经网络模型,再对模型进行训练,并将训练好的模型保存至./step3/cnn.pkl中。

测试说明

我们会加载你训练好的模型,并对测试集数据进行预测,预测正确率高于85%视为过关。

提示:平台使用torch版本为0.4input需要为Variable类型,使用代码如下:


  1. #mini_batch
  2. train_loader = Data.DataLoader(dataset=train_data, batch_size=64, shuffle=True)

ps:训练时间较长,需耐心等待!

开始你的任务吧,祝你成功!

#encoding=utf8
import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.utils.data as Data
import torchvision
import os
if os.path.exists('./step3/cnn.pkl'):
    os.remove('./step3/cnn.pkl')
    
#加载数据             
train_data = torchvision.datasets.MNIST(
    root='./step3/mnist/',
    train=True,                                     # this is training data
    transform=torchvision.transforms.ToTensor(),    # Converts a PIL.Image or numpy.ndarray to                                                    
    download=False,
)
#取6000个样本为训练集
train_data_tiny = []

for i in range(6000):
    train_data_tiny.append(train_data[i])

train_data = train_data_tiny

#********* Begin *********#
#mini_batch
train_loader = Data.DataLoader(dataset=train_data, batch_size=64, shuffle=True)

#构建卷积神经网络模型
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Sequential(         # input shape (1, 28, 28)
            nn.Conv2d(
                in_channels=1,              # input height
                out_channels=16,            # n_filters
                kernel_size=5,              # filter size
                stride=1,                   # filter movement/step
                padding=2,                  # if want same width and length of this image after con2d, padding=(kernel_size-1)/2 if stride=1
            ),                              # output shape (16, 28, 28)
            nn.ReLU(),                      # activation
            nn.MaxPool2d(kernel_size=2),    # choose max value in 2x2 area, output shape (16, 14, 14)
        )
        self.conv2 = nn.Sequential(         # input shape (16, 14, 14)
            nn.Conv2d(16, 32, 5, 1, 2),     # output shape (32, 14, 14)
            nn.ReLU(),                      # activation
            nn.MaxPool2d(2),                # output shape (32, 7, 7)
        )
        self.out = nn.Linear(32 * 7 * 7, 10)   # fully connected layer, output 10 classes
    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = x.view(x.size(0), -1)           # flatten the output of conv2 to (batch_size, 32 * 7 * 7)
        output = self.out(x)
        return output
cnn = CNN()
#SGD表示使用随机梯度下降方法,lr为学习率,momentum为动量项系数
optimizer = torch.optim.SGD(cnn.parameters(), lr = 0.1)# (momentum=0.9我不理解wei'sha'bu'yao)
#交叉熵损失函数
loss_func = nn.CrossEntropyLoss()
for step, (x, y) in enumerate(train_loader): 
    b_x = Variable(x)
    b_y = Variable(y) 
    output = cnn(b_x)
    loss = loss_func(output, b_y)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
          
           
#********* End *********#
#保存模型
torch.save(cnn.state_dict(), './step3/cnn.pkl')

你可能感兴趣的:(educoder,神经网络,pytorch,机器学习)