- 填充可以增加输出的高和宽。这常用来使输出与输入具有相同的高和宽。
- 步幅可以减小输出的高和宽,例如输出的高和宽仅为输入的高和宽的1/n(n为大于1的整数)。
多输入通道和多输出通道
前面我们用到的输入和输出都是二维数组,但真实数据的维度经常更高。例如,彩色图像在高和宽2个维度外还有RGB(红、绿、蓝)3个颜色通道。假设彩色图像的高和宽分别是h和w(像素),那么它可以表示为一个3×h×w的多维数组。我们将大小为3的这一维称为通道(channel)维。本节介绍含多个输入通道或多个输出通道的卷积核。
多输入通道
多输出通道
1×1卷积层
- 使用多通道可以拓展卷积层的模型参数。
- 假设将通道维当作特征维,将高和宽维度上的元素当成数据样本,那么1×11×1卷积层的作用与全连接层等价。
- 1×11×1卷积层通常用来调整网络层之间的通道数,并控制模型复杂度。
池化层
构造卷积核从而精确地找到了像素变化的位置。设任意二维数组X
的i
行j
列的元素为X[i, j]
。如果我们构造的卷积核输出Y[i, j]=1
,那么说明输入中X[i, j]
和X[i, j+1]
数值不一样。这可能意味着物体边缘通过这两个元素之间。但实际图像里,我们感兴趣的物体不会总出现在固定位置:即使我们连续拍摄同一个物体也极有可能出现像素位置上的偏移。这会导致同一个边缘对应的输出可能出现在卷积输出Y
中的不同位置,进而对后面的模式识别造成不便。
在本节中介绍池化(pooling)层,它的提出是为了缓解卷积层对位置的过度敏感性。
二维最大池化层和平均池化层
填充和步幅
同卷积层一样,池化层也可以在输入的高和宽两侧的填充并调整窗口的移动步幅来改变输出形状。池化层填充和步幅与卷积层填充和步幅的工作机制一样。
多通道
在处理多通道输入数据时,池化层对每个输入通道分别池化,而不是像卷积层那样将各通道的输入按通道相加。这意味着池化层的输出通道数与输入通道数相等。
- 最大池化和平均池化分别取池化窗口中输入元素的最大值和平均值作为输出。
- 池化层的一个主要作用是缓解卷积层对位置的过度敏感性。
- 可以指定池化层的填充和步幅。
- 池化层的输出通道数跟输入通道数相同。
卷积神经网络(LeNet)
对Fashion-MNIST数据集中的图像进行分类。每张图像高和宽均是28像素。我们将图像中的像素逐行展开,得到长度为784的向量,并输入进全连接层中。然而,这种分类方法有一定的局限性。
- 图像在同一列邻近的像素在这个向量中可能相距较远。它们构成的模式可能难以被模型识别。
- 对于大尺寸的输入图像,使用全连接层容易导致模型过大。假设输入是高和宽均为1,000像素的彩色照片(含3个通道)。即使全连接层输出个数仍是256,该层权重参数的形状也是3,000,000×256:它占用了大约3GB的内存或显存。这会带来过于复杂的模型和过高的存储开销。
卷积层尝试解决这两个问题。一方面,卷积层保留输入形状,使图像的像素在高和宽两个方向上的相关性均可能被有效识别;另一方面,卷积层通过滑动窗口将同一卷积核与不同位置的输入重复计算,从而避免参数尺寸过大。
卷积神经网络就是含卷积层的网络。本节里我们将介绍一个早期用来识别手写数字图像的卷积神经网络:LeNet。这个名字来源于LeNet论文的第一作者Yann LeCun。LeNet展示了通过梯度下降训练卷积神经网络可以达到手写数字识别在当时最先进的结果。这个奠基性的工作第一次将卷积神经网络推上舞台,为世人所知。
LeNet模型
eNet分为卷积层块和全连接层块两个部分。下面我们分别介绍这两个模块。
卷积层块里的基本单位是卷积层后接最大池化层:卷积层用来识别图像里的空间模式,如线条和物体局部,之后的最大池化层则用来降低卷积层对位置的敏感性。卷积层块由两个这样的基本单位重复堆叠构成。在卷积层块中,每个卷积层都使用5×5的窗口,并在输出上使用sigmoid激活函数。第一个卷积层输出通道数为6,第二个卷积层输出通道数则增加到16。这是因为第二个卷积层比第一个卷积层的输入的高和宽要小,所以增加输出通道使两个卷积层的参数尺寸类似。卷积层块的两个最大池化层的窗口形状均为2×2,且步幅为2。由于池化窗口与步幅形状相同,池化窗口在输入上每次滑动所覆盖的区域互不重叠。
卷积层块的输出形状为(批量大小, 通道, 高, 宽)。当卷积层块的输出传入全连接层块时,全连接层块会将小批量中每个样本变平(flatten)。也就是说,全连接层的输入形状将变成二维,其中第一维是小批量中的样本,第二维是每个样本变平后的向量表示,且向量长度为通道、高和宽的乘积。全连接层块含3个全连接层。它们的输出个数分别是120、84和10,其中10为输出的类别个数。
- 卷积神经网络就是含卷积层的网络。
- LeNet交替使用卷积层和最大池化层后接全连接层来进行图像分类。
LeNet模型代码实现
1 #Lenet模型实现 2 import d2lzh as d2l 3 import mxnet as mx 4 from mxnet import autograd, gluon, init, nd 5 from mxnet.gluon import loss as gloss, nn 6 import time 7 8 net = nn.Sequential() 9 net.add(nn.Conv2D(channels=6, kernel_size=5, activation='sigmoid'), 10 nn.MaxPool2D(pool_size=2, strides=2), 11 nn.Conv2D(channels=16, kernel_size=5, activation='sigmoid'), 12 nn.MaxPool2D(pool_size=2, strides=2), 13 # Dense会默认将(批量大小, 通道, 高, 宽)形状的输入转换成 14 # (批量大小, 通道 * 高 * 宽)形状的输入 15 nn.Dense(120, activation='sigmoid'), 16 nn.Dense(84, activation='sigmoid'), 17 nn.Dense(10)) 18 19 20 # In[11]: 21 22 23 #构造一个高和宽均为28的单通道数据样本,并逐层进行前向计算来查看每个层的输出形状。 24 X = nd.random.uniform(shape=(1, 1, 28, 28)) 25 net.initialize() 26 for layer in net: 27 X = layer(X) 28 print(layer.name, 'output shape:\t', X.shape) 29 30 31 # In[12]: 32 33 34 batch_size = 256 35 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size) 36 37 38 # In[13]: