动手学深度学习 5.5 卷积神经网络(LeNet)
前言
从零开始学习ai文章系列计划是个人在《动手学深度学习》和《磨菇书》两本书的学习中的个人笔记,文章也会以课本中的章节分开,即每个章节一片笔记。我会尽量的把主要内容以及遇到的难点进行记录与解决,如果哪里有错误的欢迎指正。或者不清晰的可以直接查看原文部分。
《动手学深度学习》原文(课本):https://tangshusen.me/Dive-into-DL-PyTorch/#/
《动手学深度学习》代码:https://github.com/ShusenTang/Dive-into-DL-PyTorch
(由于有时候公式太多,可能会直接贴图片)
在3.9节(多层感知机的从零开始实现)里我们构造了一个含单隐藏层的多层感知机模型来对Fashion-MNIST数据集中的图像进行分类。每张图像高和宽均是28像素。我们将图像中的像素逐行展开,得到长度为784的向量,并输入进全连接层中。然而,这种分类方法有一定的局限性。
- 图像在同一列邻近的像素在这个向量中可能相距较远。它们构成的模式可能难以被模型识别。
- 对于大尺寸的输入图像,使用全连接层容易造成模型过大。假设输入是高和宽均为1000像素的彩色照片(含3个通道)。即使全连接层输出个数仍是256,该层权重参数的形状是3,000,000×256:它占用了大约3 GB的内存或显存。这带来过复杂的模型和过高的存储开销。
卷积层尝试解决这两个问题。
一方面,卷积层保留输入形状,使图像的像素在高和宽两个方向上的相关性均可能被有效识别;
另一方面,卷积层通过滑动窗口将同一卷积核与不同位置的输入重复计算,从而避免参数尺寸过大。
卷积神经网络就是含卷积层的网络。
本节里我们将介绍一个早期用来识别手写数字图像的卷积神经网络:LeNet 。这个名字来源于LeNet论文的第一作者Yann LeCun。

1. LeNet模型
LeNet分为 卷积层块 和 全连接层块 两个部分。
在卷积层块中,每个卷积层都使用5×5的窗口,并在输出上使用sigmoid激活函数。
第一个卷积层输出通道数为6,第二个卷积层输出通道数则增加到16。这是因为第二个卷积层比第一个卷积层的输入的高和宽要小,所以增加输出通道使两个卷积层的参数尺寸类似。
卷积层块的两个最大池化层的窗口形状均为2×2,且步幅为2。由于池化窗口与步幅形状相同,池化窗口在输入上每次滑动所覆盖的区域互不重叠。
卷积层块的输出形状为(批量大小, 通道, 高, 宽)。当卷积层块的输出传入全连接层块时,全连接层块会将小批量中每个样本变平(flatten)。也就是说,全连接层的输入形状将变成二维,其中第一维是小批量中的样本,第二维是每个样本变平后的向量表示,且向量长度为通道、高和宽的乘积。
全连接层块含3个全连接层。它们的输出个数分别是120、84和10,其中10为输出的类别个数。
下面我们通过Sequential类来实现LeNet模型。
1 | class LeNet(nn.Module): |
2. 获取数据和训练模型
下面我们来实验LeNet模型。实验中,我们仍然使用Fashion-MNIST作为训练数据集。
1 | batch_size = 256 |
因为卷积神经网络计算比多层感知机要复杂,建议使用GPU来加速计算。因此,我们对3.6节(softmax回归的从零开始实现)中描述的evaluate_accuracy函数略作修改,使其支持GPU计算。
PyTorch 要求张量在 CPU 上才能提取成普通数值,如果直接 .item(),会报错。所以必须先 .cpu() 把它搬回 CPU,然后 .item() 转成标准 Python 数值

我们同样对3.6节中定义的train_ch3函数略作修改,确保计算使用的数据和模型同在内存或显存上。

学习率采用0.001,训练算法使用Adam算法,损失函数使用交叉熵损失函数。
1 | net = LeNet() |
