卷积神经网络 (convolutional neural network, CNN )是一类强大的为处理图像数据而设计的神经网络,是机器学习利用自然图像中一些已知结构的创造性方法。它除了能够高效采样从而获得精确模型,还能够高效地计算。
卷积 对卷积的理解:
一个系统若输入不稳定(f f f 函数),输出稳定(g g g 函数),则可使用卷积来求系统存量
∫ − ∞ ∞ f ( τ ) g ( x − τ ) d τ \int_{-\infty}^{\infty} f(\tau) g(x-\tau) d \tau ∫ − ∞ ∞ f ( τ ) g ( x − τ ) d τ
周围像素点对当前像素点会产生怎样的影响(如何影响,用g g g 函数表示)
一个像素点如何试探周围的像素点,如何筛选图像的特征
在卷积神经网络中,卷积的主要作用即是抽象化图像的内容、压缩数据量、匹配特定的纹理。
互相关运算 在深度学习中,「卷积」实际上表达的运算是互相关运算(cross-correlation)
严格意义下的「卷积」运算中,需要进行卷积核翻转,也就说输入图像索引在增大,卷积核的索引却在减小
暂时忽略通道(第三维)下,二维互相关运算举例如下:
在图像处理中,卷积常作为特征提取 的有效方法。一幅图像经卷积操作后得到的结果成为特征映射(Feature Map)
卷积层 卷积层中,输入张量和核张量通过互相关运算,并在添加标量偏置之后产生输出。
卷积层的作用是提取一个局部区域的特征,不同的卷积核相当于不同的特征提取器
对于输入矩阵X : n h × n w \mathbf{X}:n_h \times n_w X : n h × n w 、卷积核矩阵W : k h × k w \mathbf{W}:k_h \times k_w W : k h × k w ,偏差标量b b b ,
则输出矩阵:Y : ( n h − k h + 1 ) × ( n w − k w + 1 ) \mathbf{Y}:(n_h-k_h+1) \times (n_w-k_w+1) Y : ( n h − k h + 1 ) × ( n w − k w + 1 ) ,且:
Y = X ⋆ W + b \mathbf{Y} = \mathbf{X} \star \mathbf{W} + b Y = X ⋆ W + b
卷积层的两个重要性质 :
**局部连接:**第l l l 层的卷积层中每一个神经元都只和第l − 1 l - 1 l − 1 层中某个局部窗口内的神经元相连,构成一个局部连接网络。 **权重共享:**作为参数的卷积核W ( l ) \mathbf{W}^{(l)} W ( l ) 对于第l l l 层的所有神经元都是相同的 使用 PyTorch 提供的 Conv2d() 方法,学习由X \mathbf{X} X 生成Y \mathbf{Y} Y 的卷积核(即已知输入X \mathbf{X} X 以及对应的输出Y \mathbf{Y} Y ,核是未知的)
Pytorch中的卷积默认是带有偏差标量的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 conv2d = nn.Conv2d(1 , 1 , kernel_size=(1 , 2 ), bias = False ) X = X.reshape((1 , 1 , 6 , 8 )) Y = Y.reshape((1 , 1 , 6 , 7 ))for i in range (10 ): Y_hat = conv2d(X) l = (Y_hat - Y) ** 2 conv2d.zero_grad() l.sum ().backward() conv2d.weight.data[:] -= 3e-2 * conv2d.weight.grad
填充与步幅 填充(padding) 与 步幅(stride) 是卷积层的超参数
其中,填充能够尽可能地控制输出形状的减少量;步幅,能够解决“大量计算才能得到较小输出”的问题,成倍地减少输出形状。
对于输入图像尺寸为n h × n w n_h \times n_w n h × n w ,卷积核(Filter)尺寸为k h × k w k_h \times k_w k h × k w ,填充尺寸为p h × p w p_h \times p_w p h × p w ,步幅尺寸为s h × s w s_h \times s_w s h × s w ,对应输出的 Feature Map 尺寸为:
⌊ n h + 2 p h − k h s h + 1 ⌋ × ⌊ n w + 2 p w − k w s w + 1 ⌋ \lfloor \frac{n_h + 2p_h - k_h}{s_h} + 1 \rfloor \times \lfloor \frac{n_w + 2p_w - k_w}{s_w} + 1 \rfloor ⌊ s h n h + 2 p h − k h + 1 ⌋ × ⌊ s w n w + 2 p w − k w + 1 ⌋
填充 应用多层卷积时,常丢失边缘像素。
为解决该问题,可以使用填充(padding):输入图像的边界填充元素(通常为0),如下举例:
代码实现(使用 PyTorch 的 nn.Conv2d()):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import torchfrom torch import nndef comp_conv2d (conv2d, X ): X = X.reshape((1 , 1 ) + X.shape) Y = conv2d(X) return Y.reshape(Y.shape[2 :]) conv2d = nn.Conv2d(1 , 1 , kernel_size=3 , padding=1 ) X = torch.rand(size=(8 , 8 )) comp_conv2d(conv2d, X).shape
当然,还能够进行高度不等于宽度的填充:
1 2 3 conv2d = nn.Conv2d(1 , 1 , kernel_size=(5 , 3 ), padding=(2 , 1 )) comp_conv2d(conv2d, X).shape
步幅 有时候为了得到较小的输出,需要经过大量层的计算。如给定输入大小为224 × 224 224\times224 224 × 224 ,在使用5 × 5 5\times5 5 × 5 卷积核的情况下,需要 44 层才能将输出降低至4 × 4 4\times 4 4 × 4
为了高效计算或缩减采样次数,卷积窗口可以跳过中间位置,每次滑动多个元素,此时将每次滑动元素的数量称为 步幅(stride),如下举例:高度为 3,宽度为 2的步幅
通常情况下,若(经过填充、普通卷积后的)高度与宽度都能够被步幅整除,则输出形状为:
( n h / s h ) × ( n w / s w ) (n_h / s_h) \times (n_w / s_w) ( n h / s h ) × ( n w / s w )
代码实现:
1 2 3 4 5 X = torch.rand(size=(8 , 8 )) conv2d = nn.Conv2d(1 , 1 , kernel_size=3 , stride=2 ) comp_conv2d(conv2d, X).shape
1 2 3 4 5 X = torch.rand(size=(8 , 8 )) conv2d = nn.Conv2d(1 , 1 , kernel_size=3 , padding=1 , stride=2 ) comp_conv2d(conv2d, X).shape
感受野 感受野(Receptive Field)的定义为:卷积神经网络每一层输出的特征图(feature map)上的像素点映射回 输入图像上的区域大小。
也就说,特征图上一点,相对于原图的大小,也是卷积神经网络特征所能看到输入图像的区域。
其计算方式是从最后一层 feature map 开始,从下往上计算,先计算最深层在前一层的感受野,再逐层递推直至第一层
计算公式:$RF_i = (RF_{i + 1} - 1) \times s_i +K_{size_i} $
特别地,最后一层的 feature map 感受野大小R F RF RF = 卷积核大小K s i z e K_{size} K s i ze
应用:
多输入多输出通道 多输入通道 当输入包含多个通道时(如 RGB 输入图像具有3 × h × w 3\times h \times w 3 × h × w 的形状),需要构造一个与输入数据具有相同输入通道数目 的卷积核,以便与输入数据进行互相关运算。
每一个通道都应对应一个大小相同但值不同的卷积核 ,从而使得每个通道能够学习到不同的模式
举例如下:
故,对于输入X : c i × n h × n w \mathbf{X}:c_i\times n_h \times n_w X : c i × n h × n w ,核W : c i × k h × k w \mathbf{W:c_i\times k_h \times k_w} W : c i × k h × k w ,
则输出Y : m h × m w \mathbf{Y}:m_h \times m_w Y : m h × m w
即先对每个通道输入的二维张量、卷积核的二维张量进行互相关运算,再对通道求和 ,得到二维 张量
代码实现:
1 2 3 def corr2d_multi_in (X, K ): return sum (d2l.corr2d(x, k) for x, k in zip (X, K))
多输出通道 在最流行的神经网络架构中,随着神经网络层数的加深,常常会增加输出通道的维数,通过减少空间分辨率以获得更大的通道深度。
对于输入X : c i × n h × n w \mathbf{X}:c_i \times n_h \times n_w X : c i × n h × n w ,核W : c o × c i × k h × k w \mathbf{W}:c_o\times c_i \times k_h \times k_w W : c o × c i × k h × k w
即有多个三维 卷积核,每个核生成一个输出通道
则输出Y : c o × m h × m w \mathbf{Y}:c_o\times m_h \times m_w Y : c o × m h × m w
代码实现:
1 2 3 4 5 6 def corr2d_multi_in_out (X, K ): return torch.stack( [corr2d_multi_in(X, k) for k in K], 0 )
每个输出通道能够识别特定模式,而输入通道内核识别并组合输入中的模式
⭐️总结:
每个输入通道都有独立的二维卷积核,所有输入通道的互相关运算结果相加,得到一个输出通道结果 每个输出通道都有独立的三维卷积核 1*1 卷积层 Lin, Min, Qiang Chen, and Shuicheng Yan. “Network in network.” arXiv preprint arXiv:1312.4400 (2013).
对于k h = k w = 1 k_h = k_w = 1 k h = k w = 1 的卷积核,它不识别空间模式,而是融合通道。
相当于具有输入n h n w × c i n_h n_w \times c_i n h n w × c i 、权重为c o × c i c_o \times c_i c o × c i 的全连接层
每个三维卷积核,相当于与输入进行一个加权和
⭐️作用:
升维或降维:Feature Map 大小不变,但深度发生变化(取决于三维卷积核的数量) 跨通道信息交融 减少参数量 增加模型深度,提高非线性表达能力 **应用举例:**GoogLeNet
每一个Inception块中由四条并行路径组成:1 × 1 、 3 × 3 、 5 × 5 1\times 1、3\times 3、5\times 5 1 × 1 、 3 × 3 、 5 × 5 的卷积层,以及3 × 3 3\times 3 3 × 3 的汇聚层,从不同的空间中提取信息。
每一分支,若不使用1 × 1 1\times 1 1 × 1 卷积层,乘法运算量(FeatureMap元素个数 × 感受野)将会非常庞大 使用1 × 1 1\times 1 1 × 1 卷积层对输入层进行降维(大小没变、深度减少),从而大大减少参数数量 池化(汇聚) 类似于卷积层,池化层除了有一个固定形状的窗口(即池化窗口),能够根据步幅 大小在输入的所有区域上滑动。当然,它也有填充 操作
池化窗口从输入张量的左上角开始,从左至右、从上至下地在输入张量内滑动。在池化窗口到达的每个位置,它计算该窗口中输入子张量的最大值或平均值,具体取决于使用了最大池化层 (maximum pooling),还是平均池化层 (average pooling)。
池化(pooling)的双重目的:
池化窗口形状为p × q p\times q p × q 的池化层,其池化操作称为p × q p \times q p × q 池化。
最大池化操作举例如下:
池化层没有可学习的参数,并且在每个输入通道中应用池化层(即:汇聚层在每个输入通道上单独运算,而无需在通道上对输入进行汇总),由此获得相应的输出通道
因此,池化层的输出通道数量,等于输入通道数量
GAP Lin, Min, Qiang Chen, and Shuicheng Yan. “Network in network.” arXiv preprint arXiv:1312.4400 (2013).
GAP(Global Average Pooling):将每一张 Feature Map 计算所有像素点的均值,输出对应的特征点,将这些特征点组成最后的特征向量
简单来说,就是每一个通道选出一个代表出来
目的:替代卷积神经网络中传统的全连接层,减少参数量
优点:
通过强制使特征图和类别之间相对应,对于卷积结构更来说这个转换更自然。 没有参数用于优化,避免了过拟合 自适应池化 自适应 (Adaptive):只需要给定输入数据和输出数据的大小,自适应算法就能够自动地帮助计算核的大小和每次移动的步长。
torch.nn.AdaptiveAvgPool2d(output_size):提供二维的自适应平均 池化操作,传入的是输出尺寸代码实现 从零实现池化层的正向传播:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import torchfrom torch import nnfrom d2l import torch as d2ldef pool2d (X, pool_size, mode='max' ): p_h, p_w = pool_size Y = torch.zeros((X.shape[0 ] - p_h + 1 , X.shape[1 ] - p_w + 1 )) for i in range (Y.shape[0 ]): for j in range (Y.shape[1 ]): if mode == 'max' : Y[i, j] = X[i : i+p_h, j : j+p_w].max () elif mode == 'avg' : Y[i, j] = X[i : i+p_h, j : j+p_w].mean() return Y X = torch.tensor([[0.0 , 1.0 , 2.0 ], [3.0 , 4.0 , 5.0 ], [6.0 , 7.0 , 8.0 ]]) pool2d(X, (2 , 2 )), pool2d(X, (2 , 2 ), 'avg' )
1 2 3 4 (tensor([[4., 5.], [7., 8.]]), tensor([[2., 3.], [5., 6.]]))
使用 PyTorch 实现池化层:
1 2 3 4 5 6 7 X = torch.arange(16 , dtype = torch.float32).reshape((1 , 1 , 4 , 4 )) pool2d = nn.MaxPool2d(3 ) pool2d_b = nn.MaxPool2d(3 , padding=1 , stride=2 ) X, pool2d(X), pool2d_b(X)
1 2 3 4 5 6 7 (tensor([[[[ 0., 1., 2., 3.], [ 4., 5., 6., 7.], [ 8., 9., 10., 11.], [12., 13., 14., 15.]]]]), tensor([[[[10.]]]]), tensor([[[[ 5., 7.], [13., 15.]]]]))
对于多通道输入,池化层分别对每个输入通道进行运算:
1 2 3 X = torch.cat((X, X+1 ), 1 ) pool2d = nn.MaxPool2d(3 , padding=1 , stride=2 ) X, pool2d(X)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 (tensor([[[[ 0., 1., 2., 3.], [ 4., 5., 6., 7.], [ 8., 9., 10., 11.], [12., 13., 14., 15.]], [[ 1., 2., 3., 4.], [ 5., 6., 7., 8.], [ 9., 10., 11., 12.], [13., 14., 15., 16.]]]]), tensor([[[[ 5., 7.], [13., 15.]], [[ 6., 8.], [14., 16.]]]]))
卷积网络的整体结构 典型的卷积网络是由卷积层、池化层、全连接层交叉堆叠而成
拓展资料 CNN可视化:https://poloclub.github.io/cnn-explainer/