图片模式识别两个原则:
卷积神经网络(convolutional neural network)是含有卷积层(convolutional layer)的神经网络。
一、二维卷积层
二维卷积层有高和宽两个空间维度,常用来处理图像数据。
1、二维互相关运算
卷积层的核心运算是卷积运算,但在实际应用中,通常会使用一个更易于理解和实现的变体——互相关(cross-correlation)运算。
在二维卷积层中,一个二维输入数组和一个二维核(kernel)数组通过互相关运算输出一个二维数组。核数组在卷积计算中又称卷积核或过滤器(filter),卷积核窗口(又称卷积窗口)的形状取决于卷积核的高和宽。
在二维互相关运算中,卷积窗口从输入数组的最左上方开始,按从左往右、从上往下的顺序,依次在输入数组上滑动。
简单实现代码:
def corr2d(X, K): # 本函数已保存在d2lzh_pytorch包中方便以后使用
h, w = K.shape
X, K = X.float(), K.float()
Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
Y[i, j] = (X[i: i + h, j: j + w] * K).sum()
return Y
2、二维卷积层
二维卷积层将输入和卷积核做互相关运算,并加上一个标量偏差来得到输出。
卷积层的模型参数包括了卷积核和标量偏差。在训练模型的时候,通常我们先对卷积核随机初始化,然后不断迭代卷积核和偏差。
class Conv2D(nn.Module):
def __init__(self, kernel_size):
super(Conv2D, self).__init__()
self.weight = nn.Parameter(torch.randn(kernel_size))
self.bias = nn.Parameter(torch.randn(1))
def forward(self, x):
return corr2d(x, self.weight) + self.bias
3、图像中物体边缘检测
卷积层可通过重复使用卷积核有效地表征局部空间。
4、互相关运算和卷积运算
为了得到卷积运算的输出,我们只需将核数组左右翻转并上下翻转,再与输入数组做互相关运算。
5、特征图和感受野
- 特征图(feature map):特征图是卷积层输出的二维数组。这个二维数组表示输入图像在空间维度(宽和高)上经过卷积操作后的一种“表征”,也就是输入图像在该卷积层上的某种特征。
- 感受野(receptive field):感受野是指输出的某个元素在输入中所“看到”的区域。具体来说,输出中的某个元素是由输入中某个区域经过卷积操作产生的,这个输入区域就是这个元素的感受野。通过增加网络的层数,可以扩大感受野,捕捉更大区域的输入特征,提升模型捕捉全局信息的能力。
二、填充和步幅
1、卷积层的输出形状
卷积层的输出形状由输入形状和卷积核窗口形状决定。‘而卷积层的两个超参数,即填充和步幅。可以对给定形状的输入和卷积核改变输出形状。
2、填充
①定义
填充(padding)是指在输入高和宽的两侧填充元素(通常是0元素)。
②填充方法
填充后输出的形状,输出的高和宽会分别填充ph和pw:
在很多情况下,我们会设置ph=kh−1和pw=kw−1来使输入和输出具有相同的高和宽。假设这里kh是奇数,我们会在高的两侧分别填充ph/2行。如果kh是偶数,一种可能是在输入的顶端一侧填充⌈ph/2⌉行,而在底端一侧填充⌊ph/2⌋行。在宽的两侧填充同理。
卷积神经网络经常使用奇数高和宽的卷积核,所以两端上的填充个数相等,当两端上的填充个数相等,并使输入和输出具有相同的高和宽时,我们就知道输出Y[i,j]
是由输入以X[i,j]
为中心的窗口同卷积核进行互相关计算得到的。
# 定义一个函数来计算卷积层。它对输入和输出做相应的升维和降维
def comp_conv2d(conv2d, X):
# (1, 1)代表批量大小和通道数(“多输入通道和多输出通道”一节将介绍)均为1
X = X.view((1, 1) + X.shape)
Y = conv2d(X)
return Y.view(Y.shape[2:]) # 排除不关心的前两维:批量和通道
# 注意这里是两侧分别填充1行或列,所以在两侧一共填充2行或列
conv2d = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, padding=1)
X = torch.rand(8, 8)
comp_conv2d(conv2d, X).shape
③功能
填充可以增加输出的高和宽,这常用来使输出与输入具有相同的高和宽。
3、步幅
①定义
卷积窗口从输入数组的最左上方开始,按从左往右、从上往下的顺序,依次在输入数组上滑动。我们将每次滑动的行数和列数称为步幅(stride)。
②步幅公式
当高上步幅为sh,宽上步幅为sw时,输出形状为:
取ph=kh-1,pw=kw-1。如果输入的高和宽能分别被高和宽上的步幅整除,那么输出形状将是:
conv2d = nn.Conv2d(1, 1, kernel_size=(3, 5), padding=(0, 1), stride=(3, 4))
comp_conv2d(conv2d, X).shape
③功能
步幅可以减小输出的高和宽,例如输出的高和宽仅为输入的高和宽的1/n(n为大于1的整数)。
当输入的高和宽两侧的填充数分别为ph和pw时,我们称填充为(ph,pw)。特别地,当ph=pw=p时,填充为p。当在高和宽上的步幅分别为sh和sw时,我们称步幅为(sh,sw)。特别地,当sh=sw=s时,步幅为s。在默认情况下,填充为0,步幅为1。
三、多输入通道和多输出通道
例如,彩色图像在高和宽2个维度外还有RGB(红、绿、蓝)3个颜色通道。假设彩色图像的高和宽分别是h和w(像素),那么它可以表示为一个3×h×w的多维数组。我们将大小为3的这一维称为通道(channel)维。使用多通道可以拓展卷积层的模型参数。
1、多输入通道
当输入数据含多个通道时,我们需要构造一个与输入数据的通道数相同的卷积核,从而能够与含多通道的输入数据做互相关运算。在各个通道上对输入的二维数组和卷积核的二维核数组做互相关运算,再将这几个互相关运算的二维输出按通道相加得到总输出。
实现的代码逻辑:
def corr2d_multi_in(X, K):
# 沿着X和K的第0维(通道维)分别计算再相加
res = d2l.corr2d(X[0, :, :], K[0, :, :])
for i in range(1, X.shape[0]):
res += d2l.corr2d(X[i, :, :], K[i, :, :])
return res
2、多输出通道
当输入通道有多个时,因为我们对各个通道的结果做了累加,所以不论输入通道数是多少,输出通道数总是为1。
而当输出通道不为1时,设卷积核输入通道数和输出通道数分别为ci和co,高和宽分别为kh和kw。如果希望得到含多个通道的输出,我们可以为每个输出通道分别创建形状为ci×kh×kw的核数组。将它们在输出通道维上连结,卷积核的形状即co×ci×kh×kw。在做互相关运算时,每个输出通道上的结果由卷积核在该输出通道上的核数组与整个输入数组计算而来。
K = torch.stack([K, K + 1, K + 2])
K.shape
3、1×1卷积层
由于 1×1卷积不会改变空间维度(即输入的高和宽保持不变),它的主要计算发生在通道维度。它通过卷积核的权重对输入的多个通道进行线性组合,生成新的输出通道。
1×1卷积层通常用来调整网络层之间的通道数,并控制模型复杂度。
四、池化层
池化(pooling)层,它的提出是为了缓解卷积层对位置的过度敏感性。
池化层的输出通道数与输入通道数相同。
1、二维最大池化层和平均池化层
①池化窗口
同卷积层一样,池化层每次对输入数据的一个固定形状窗口(又称池化窗口)中的元素计算输出。
②池化层计算逻辑
池化层直接计算池化窗口内元素的最大值或者平均值,该运算也分别叫做最大池化或平均池化。
③池化窗口滑动顺序
在二维最大池化中,池化窗口从输入数组的最左上方开始,按从左往右、从上往下的顺序,依次在输入数组上滑动。
2、填充和步幅
同卷积层一样,池化层也可以在输入的高和宽两侧的填充并调整窗口的移动步幅来改变输出形状。
默认情况下,MaxPool2D
实例里步幅和池化窗口形状相同。
所以下面代码行的池化窗口大小为3*3,而stride也是3。
pool2d = nn.MaxPool2d(3)
pool2d(X)
指定填充和步幅,高两侧填充1行,宽两侧填充2列:
pool2d = nn.MaxPool2d((2, 4), padding=(1, 2), stride=(2, 3))
pool2d(X)
3、多通道
在处理多通道输入数据时,池化层对每个输入通道分别池化,这意味着池化层的输出通道数与输入通道数相等。
4、最大池化层和平均池化层的区别
五、卷积神经网络(LeNet)
1、使用卷积层的好处
-
卷积层保留输入的空间结构:卷积层通过滑动卷积核操作,能够保留图像中的空间信息,从而使得神经网络能够有效识别图像中的局部特征,如边缘、纹理和形状。
-
卷积层通过参数共享减少参数规模:卷积层通过使用固定大小的卷积核在整个图像上滑动,并共享同一组参数来进行计算,这极大地减少了参数的数量,相比于全连接层具有更高的计算效率和较少的参数。
2、LeNet模型
LeNet分为卷积层块和全连接层块两个部分。
①卷积层块
- 基本单位:卷积层后接最大池化层。卷积层用来识别图像里的空间模式,如线条和物体局部,之后的最大池化层则用来降低卷积层对位置的敏感性。
- 组成:卷积层块由两个这样的基本单位重复堆叠构成。
在卷积层块中,每个卷积层都使用5×5的窗口,并在输出上使用sigmoid激活函数。
第一个卷积层输出通道数为6,第二个卷积层输出通道数则增加到16。这是因为第二个卷积层比第一个卷积层的输入的高和宽要小,所以第一个卷积层输出通道数为6,第二个卷积层输出通道数则增加到16。这是因为第二个卷积层比第一个卷积层的输入的高和宽要小,所以增加输出通道使两个卷积层的参数尺寸类似。【如果不增加通道数,模型可能会失去对更高层次特征的表达能力。增加通道数有助于补偿特征图尺寸缩小带来的信息损失。】
卷积层块的两个最大池化层的窗口形状均为2×2,且步幅为2。由于池化窗口与步幅形状相同,池化窗口在输入上每次滑动所覆盖的区域互不重叠。
卷积层块的输出形状为(批量大小, 通道, 高, 宽)。
②全连接层块
当卷积层块的输出传入全连接层块时,全连接层块会将小批量中每个样本变平(flatten)。也就是说,全连接层的输入形状将变成二维,其中第一维是小批量中的样本,第二维是每个样本变平后的向量表示,且向量长度为通道、高和宽的乘积。全连接层块含3个全连接层。它们的输出个数分别是120、84和10,其中10为输出的类别个数。
六、深度卷积神经网络(AlexNet)
AlexNet是浅层神经网络和深度神经网络的分界线。
1、学习特征表示
①特征表示
输入的逐级表示由多层模型中的参数决定,而这些参数都是学出来的。
②例子
以图像分类为例,在多层神经网络中,图像的第一级的表示可以是在特定的位置和⻆度是否出现边缘;而第二级的表示说不定能够将这些边缘组合出有趣的模式,如花纹;在第三级的表示中,也许上一级的花纹能进一步汇合成对应物体特定部位的模式。这样逐级表示下去,最终,模型能够较容易根据最后一级的表示完成分类任务。
2、AlexNet
第一,与相对较小的LeNet相比,AlexNet包含8层变换,其中有5层卷积和2层全连接隐藏层,以及1个全连接输出层。
- AlexNet第一层中的卷积窗口形状是11×11。因为ImageNet中绝大多数图像的高和宽均比MNIST图像的高和宽大10倍以上,ImageNet图像的物体占用更多的像素,所以需要更大的卷积窗口来捕获物体。第二层中的卷积窗口形状减小到5×5,之后全采用3×3。此外,第一、第二和第五个卷积层之后都使用了窗口形状为3×3、步幅为2的最大池化层。而且,AlexNet使用的卷积通道数也大于LeNet中的卷积通道数数十倍。
- 紧接着最后一个卷积层的是两个输出个数为4,096的全连接层。这两个巨大的全连接层带来将近1 GB的模型参数。由于早期显存的限制,最早的AlexNet使用双数据流的设计使一块GPU只需要处理一半模型。幸运的是,显存在过去几年得到了长足的发展,因此通常我们不再需要这样的特别设计了。
第二,AlexNet将sigmoid激活函数改成了更加简单的ReLU激活函数,缓解梯度消失。
第三,AlexNet通过丢弃法来控制全连接层的模型复杂度,hidden层之后加入。而LeNet并没有使用丢弃法。
第四,AlexNet引入了大量的图像增广,如翻转、裁剪和颜色变化,从而进一步扩大数据集来缓解过拟合。我们将在后面的“图像增广”一节详细介绍这种方法。
七、使用重复元素的网络(VGG)
VGG-11通过5个可以重复使用的卷积块来构造网络。根据每块里卷积层个数和输出通道数的不同可以定义出不同的VGG模型。
1、VGG块
VGG块的组成规律是:连续使用数个相同的填充为1、窗口形状为3×3的卷积层后接上一个步幅为2、窗口形状为2×2的最大池化层。(数个相同的模块组成其卷积层,因此被称为使用重复元素的网络VGG)卷积层保持输入的高和宽不变,而池化层则对其减半。
def vgg_block(num_convs, in_channels, out_channels):
blk = []
for i in range(num_convs):
if i == 0:
blk.append(nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1))
else:
blk.append(nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1))
blk.append(nn.ReLU())
blk.append(nn.MaxPool2d(kernel_size=2, stride=2))
return nn.Sequential(*blk)
2、VGG网络
VGG网络由卷积层模块后接全连接层模块构成。卷积层模块串联数个vgg_block
,其超参数由变量conv_arch
定义。该变量指定了每个VGG块里卷积层个数和输出通道数。全连接模块则与AlexNet中的一样。