购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

3.1 实战MNIST手写体识别

第2章对MNIST数据集做了介绍,描述了其构成方式及其数据特征和标签含义等。了解这些信息有助于编写合适的程序来对MNIST数据集进行分析和分类识别。本节将实现MNIST数据集分类的任务。

3.1.1 数据图像的获取与标签的说明

第2章已经详细介绍了MNIST数据集,我们可以使用下面代码获取数据:

     import numpy as np
     x_train = np.load("./dataset/mnist/x_train.npy")
     y_train_label = np.load("./dataset/mnist/y_train_label.npy")

基本数据的获取在第2章也做了介绍,这里不再过多阐述。需要注意的是,我们在第2章介绍MNIST数据集时,只使用了图像数据,没有对标签进行说明,在这里重点对数据标签,也就是y_train_labe进行介绍。

下面使用print(y_train_label[:10])打印出数据集的前10个标签,结果如下:

     [5 0 4 1 9 2 1 3 1 4]

可以很清楚地看到,这里打印出了10个字符,每个字符对应相应数字的数据图像所对应的数字标签,即图像3的标签,对应的就是3这个数字字符。

可以说,训练集中每个实例的标签对应0~9的任意一个数字,用以对图片进行标注。另外,需要注意的是,对于提取出来的MNIST的特征值,默认使用一个0~9的数值进行标注,但是这种标注方法并不能使得损失函数获得一个好的结果,因此常用one_hot计算方法,将其值具体落在某个标注区间中。

one_hot的标注方法请读者查找材料自行学习。这里主要介绍将单一序列转换成one_hot的方法。一般情况下,可以用NumPy实现one_hot的表示方法,但是这样转换生成的是numpy.array格式的数据,并不适合直接输入到PyTorch中。

如果读者能够自行编写将序列值转换成one_hot的函数,那么你的编程功底真不错,不过PyTorch提供了已经编写好的转换函数:

     torch.nn.functional.one_hot

完整的one_hot使用方法如下:

     import numpy as np
     import torch
     x_train = np.load("./dataset/mnist/x_train.npy")
     y_train_label = np.load("./dataset/mnist/y_train_label.npy")
     x = torch.tensor(y_train_label[:5],dtype=torch.int64)
     # 定义一个张量输入,因为此时有 5 个数值,且最大值为9,类别数为10
     # 所以我们可以得到 y 的输出结果的形状为 shape=(5,10),5行12列
     y = torch.nn.functional.one_hot(x, 10)  # 一个参数张量x, 10 为类别数

运行结果如图3-1所示。

可以看到,one_hot的作用是将一个序列转换成以one_hot形式表示的数据集。所有的行或者列都被设置成0,而每个特定的位置都用一个1来表示,如图3-2所示。

图3-1 运行结果

图3-2 one-hot数据集

简单来说,MNIST数据集的标签实际上就是一个表示60 000幅图片的60 000×10大小的矩阵张量[60000,10]。前面的行数指的是数据集中的图片为60 000幅,后面的10是指10个列向量。

3.1.2 实战基于PyTorch 2.0的手写体识别模型

本小节使用PyTorch 2.0框架完成MNIST手写体数字的识别。

1.模型的准备(多层感知机)

第2章讲过了,PyTorch最重要的一项内容是模型的准备与设计,而模型的设计最关键的一点就是了解输出和输入的数据结构类型。

通过第2章图像降噪的演示,读者已经了解到我们输入的数据是一个[28,28]大小的二维图像。而通过对数据结构的分析可以得知,对于每个图形都有一个确定的分类结果,也就是一个0~9之间的确定数字。

因此为了实现对输入图像进行数字分类这个想法,必须设计一个合适的判别模型。而从上面对图像的分析来看,最直观的想法就将图形作为一个整体结构直接输入到模型中进行判断。基于这种思路,简单的模型设计就是同时对图像所有参数进行计算,即使用一个多层感知机(Multilayer Perceptron, MLP)来对图像进行分类。整体的模型设计结构如图3-3所示。

图3-3 整体的模型设计结构

从图3-3可以看到,一个多层感知机模型就是将数据输入后,分散到每个模型的节点(隐藏层),进行数据计算后,即可将计算结果输出到对应的输出层中。多层感知机的模型结构如下:

2.损失函数的表示与计算

在第2章中,我们使用MSE作为目标图形与预测图形的损失值,而在本例中,我们需要预测的目标是图形的“分类”,而不是图形表示本身,因此我们需要寻找并使用一种新的能够对类别归属进行“计算”的函数。

本例所使用的损失函数为torch.nn.CrossEntropyLoss。PyTorch官网对其介绍如下:

     CLASS torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-
100,reduce=None, reduction='mean', label_smoothing=0.0)

该损失函数计算输入值(Input)和目标值(Target)之间的交叉熵损失。交叉熵损失函数可用于训练一个单标签或者多标签类别的分类问题。给定参数weight时,其为分配给每个类别的权重的一维张量(Tensor)。当数据集分布不均衡时,这个参数很有用。

同样需要注意的是,因为torch.nn.CrossEntropyLoss内置了Softmax运算,而Softmax的作用是计算分类结果中最大的那个类。从图3-4所示的代码实现中可以看到,此时CrossEntropyLoss已经在实现计算时完成了Softmax计算,因此在使用torch.nn.CrossEntropyLoss作为损失函数时,不需要在网络的最后添加Softmax层。此外,label应为一个整数,而不是one-hot编码形式。

图3-4 交叉熵损失

代码如下:

     import torch
     
     y = torch.LongTensor([0])
     z = torch.Tensor([[0.2,0.1,-0.1]])
     criterion = torch.nn.CrossEntropyLoss()
     loss = criterion(z,y)
     print(loss)

目前读者需要掌握的就是这些内容,CrossEntropyLoss的数学公式较为复杂,建议学有余力的读者查阅相关资料进行学习。

3.基于PyTorch的手写体数字识别

下面开始实现基于PyTorch的手写体数字识别。通过前文的介绍,我们还需要定义深度学习的优化器部分,在这里采用Adam优化器,代码如下:

     model = NeuralNetwork()
     optimizer = torch.optim.Adam(model.parameters(), lr=2e-5)  #设定优化函数

在这里首先需要定义模型,然后将模型参数传入优化器中,lr是对学习率的设定,根据设定的学习率进行模型计算。完整的手写体数字识别模型如下:

此时模型的训练结果如图3-5所示。

图3-5 训练结果

可以看到,随着模型循环次数的增加,模型的损失值在降低,而准确率在逐渐增高,具体请读者自行验证测试。

3.1.3 基于Netron库的PyTorch 2.0模型可视化

前面章节带领读者完成了基于PyTorch 2.0的MNIST模型的设计,并基于此完成了MNIST手写体数字的识别。此时可能有读者对我们自己设计的模型结构感到好奇,如果能够可视化地显示模型结构就更好了。

读者可以自行在百度搜索Netron。Netron是一个深度学习模型可视化库,支持可视化地表示PyTorch 2.0的模型存档文件。因此,我们可以把3.1.2节中PyTorch的模型结构保存为文件,并通过Netron进行可视化展示。保存模型的代码如下:

     import torch
     device = "cuda"          #在这里默认使用GPU模式,如果出现运行问题,可以将其改成CPU模式
     
     #设定多层感知机网络模型
     class NeuralNetwork(torch.nn.Module):
        def __init__(self):
           super(NeuralNetwork, self).__init__()
           self.flatten = torch.nn.Flatten()
           self.linear_relu_stack = torch.nn.Sequential(
              torch.nn.Linear(28*28,312),
              torch.nn.ReLU(),
              torch.nn.Linear(312, 256),
              torch.nn.ReLU(),
              torch.nn.Linear(256, 10)
           )
        def forward(self, input):
           x = self.flatten(input)
           logits = self.linear_relu_stack(x)
     
           return logits
     
     #进行模型的保存
     model = NeuralNetwork()
     torch.save(model, './model.pth')     #将模型保存为pth文件

建议读者从GitHub上下载Netron,其主页提供了基于不同版本的安装方式,如图3-6所示。

图3-6 基于不同版本的安装方式

读者可以依照操作系统的不同下载对应的文件,在这里安装的是基于Windows的.exe文件,安装后是一个图形界面,直接在界面上单击file操作符号打开我们刚才保存的.pth文件,显示结果如图3-7所示。

图3-7 显示结果

可以看到,此时我们定义的模型结构被可视化地展示出来了,每个模块的输入输出维度在图3-7上都展示出来了,单击深色部分可以看到每个模块更详细的说明,如图3-8所示。

图3-8 模块的详细说明

感兴趣的读者可以自行安装查看。 BtZ++hDxtyhc6KYSlSEjlXvoPbhEhbpogDPaKno1/1rVuFIg4rEhwL7ib2mlAWCW

点击中间区域
呼出菜单
上一章
目录
下一章
×