6.4 填充和步幅

🏷️sec0604_Convolutional_PddingStride

在上一小节中,我们提到神经网络中的卷积并不是严格意义上数学文献中标准的离散卷积,而是互相关运算。虽然互相关运算与卷积运算只相差一个翻转的操作,并且被用在模型的训练中输出结果也差不多,但它们还是略有不同的。这里会我们简单讨论一下这些差异,并对神经网络中一些函数的重要性质进行说明,从而引申出基本卷积函数的一些常见变体,包括带填充的卷积和步幅可调的卷积。

6.4.1 填充

在任何关于卷积神经网络的定义中都存在一个重要的性质,那就是对输入 X\bf{X} 进行零填充(zero-padding),使实际输入比原始样本稍微大一些。如果没有这个性质,输出特征图的尺度就会随着网络层次的增加而逐渐缩减。对输入进行零填充允许我们对卷积核的宽度和输出的尺度进行独立的控制,而不必受输入尺度和网络深度的制约。否则就必须面临一个二选一的局面,要么接受特征图空间尺度的快速缩减,要么选择一个小型的核来进行特征提取。这两种选择都会极大地限制网络的表达能力。 图6-19 给出了一个例子。

图6-19 卷积核的尺度控制输出特征图缩小的速度

图6-19 卷积核的尺度控制输出特征图缩小的速度

假设给定一个 32×3232×32 的输入图像,然后使用一个 5×55×5 的卷积核对它进行提取特征。由于卷积运算的规则是点积加求和,所以原来 5×55×5 的区域就会变成一个 1×11×1 的标量,这就使得输出尺度在高度和宽度上都减少了4。所以每经过一层,输出就会减少 4×44×4。因此,在经过一次卷积之后,第一个输出层Layer 1就变成了 28×2828×28 的特征图。以此类推,经过了7个卷积层之后,输出会缩小成 4×44×4 的小方块。用公式表示就是 32(4×7)=432-(4×7)=4。很显然,此时无法再使用 5×55×5 的卷积核来进行特征提取了,因为卷积核已经无法完整覆盖输入了。也就是说,该模型的层数最多不能超过七层。当然,如果使用更大的卷积核,特征图缩小的速度将变得更快,模型深度也会相应减少。也就是说,卷积的输出形状取决于输入形状和卷积核的形状。

根据上面的分析,我们可以得到一个结论,应用卷积可能会导致输入边缘信息的丢失。不过因为作为常用设置的小卷积核的应用,单个卷积层丢失的像素并不是很多。但随着多个卷积层的连续堆叠,累积丢失的像素就不能再忽略了。我们知道,整个深度学习技术研究的就是怎么将模型做得更深,比如用几百个卷积层来进行目标检测。如果卷积真的存在这种因为尺度逐级缩小而带来的信息丢失的问题,那么该怎么办呢?解决这个问题最简单的办法就是填充(padding)。

常见的与填充相关的卷积设定主要有三种。第一种是完全不使用填充对输入进行扩展,并且卷积核只允许访问那些可以完全包含整个核的位置,这种卷积称为有效(VALID)卷积。如 图6-20(a) 所示,输出中的所有像素都来源于相同数量的原始像素的输入,这使得输出像素的表示更加规范。在这种机制下,输出的尺度在每一层都会缩减。假设输入的高度和宽度是 nh×nwn_h×n_w,并且卷积核的高度和宽度分别是 kh×kwk_h×k_w,那么输出形状将变为 (nhkh+1)×(nwkw+1)(n_h−k_h+1) × (n_w−k_w+1)。当卷积核比较大时,缩减率就会变得非常显著,这限制了卷积网络深度的增加。随着卷积层数量的增加,生成的卷积特征图的空间维度最终将会缩减到 1×11×1。此时,如果再增加层就没有任何意义了。第二种特殊卷积通过增加足够的零填充来保证输出特征图和输入具有相同的尺度,这种卷积称为同等(SAME)卷积。简单的说,就是在输入的四周添加一些额外的值为零的行和列,以达到输出尺度不变的目的。理论上来说,只要硬件资源充足,模型就能够包含任意多的卷积层。如 图6-20(b) 所示,中间的黄色区域是原始输入,它是一个 5×55×5 的二维矩阵。我们可以在它的上下左右分别填充了一些零。比方说,在上面和下面各填充了一行,在左边和右边也各填充一列。在零填充之后,还是使用原来的 3×33×3 的核来做卷积,并且运算规则和填充之前完全相同,依然是在感知域内进行点积运算。例如,对红色方框部分的卷积结果是34。当卷积核从填充后的输入的左上角开始Z字形扫描到右下角时,最终生成的激活特征图尺度依然是 5×55×5,尺度并没有改变。这就是填充的效果。反过来说,在同等填充模式下,输入矩阵中靠近外边界的部分像素会比中间部分的像素对输出的影响要小一些。由于零填充的存在,来自于原始输入的信息在一定程度上被稀释了,从而导致边界区域的像素存在一定程度的欠表达。第三种特殊卷积更为极端,叫做(FULL)卷积(注意区分后面介绍的全卷积网络)。这种卷积使用更多的零来进行外边界填充,它只需要保证至少有一个像素来源于输入像素即可,其最终的输出特征图尺度为 (nh+kh1)×(nw+kw1)(n_h+k_h-1) × (n_w+k_w-1),通常会比输入还要大一些。不过这种卷积要在每个位置都表现不错是比较困难的,所以一般很少使用。

图6-20 有效卷积和同等卷积

图6-20 有效卷积和同等卷积

在实际应用中,我们更关注尺度不变的 同等卷积。此时,可以填充 2ph2p_h 行(一半位于顶部,一半位于底部)和 2pw2p_w 列(一半位于左侧,一半位于右侧),则输出特征图的形状为:
(nhkh+2ph+1)×(nwkw+2pw+1)(6.10)(n_h-k_h+2p_h+1) × (n_w-k_w+2p_w+1) \tag{6.10}

这意味着输出的高度和宽度在上下和左右四个位置都分别增加 php_hpwp_w。如果填充 ph=0,pw=0p_h=0, p_w=0,则输出将退化为原始的输出尺度 (nhkh+1)×(nwkw+1)(n_ℎ-k_ℎ+1) × (n_w-k_w+1),也就是 有效卷积

在大多数设计中,我们需要设置 ph=(kh1)/2,pw=(kw1)/2p_h=(k_h−1)/2, p_w=(k_w−1)/2,可以使得输出和输入具有相同的高度 nhn_h 和宽度 nwn_w。如此设置的好处是,无论选取多大的核,都不会改变输出的大小。这样在构建网络时,能够更容易地预测每层特征图的输出形状。需要注意的是,填充是双边的,因此无论是 php_h 还是 pwp_w,通常都会设置为上下填充相同行,左右也填充相同的列。

在卷积神经网络的设计中,卷积核的高度和宽度通常都设置为奇数,例如1、3、5、7等。选择奇数的好处是,通过在输入四周进行零填充后可以保持空间维度的不变。对于任意二维张量 XX,当满足:1.卷积核的尺度是奇数;2.所有边的填充量相同;3.输入与输出具有相同的维度时,则可以得出结论:输出 Y[i,j]Y[i,j] 是通过以输入 X[i,j]X[i,j] 为中心、以卷积核为过滤器的互相关运算获得的张量。

在下面的代码中,我们同时实现了与 图6-20 中具有相同维度的有效卷积和同等卷积。两种卷积的卷积核尺度都为 3×33×3,且输出都是 5×55×5 的张量。不同的是,同等卷积在输入的四周都增加了 11 个像素的零填充,记作:(ph,pw)=(1,1)(p_h, p_w) = (1, 1)

程序清单6-9 定义带填充的卷积函数

# codes06009_padding
import paddle

# 1. 定义卷积层计算函数,参数为卷积函数和输入张量,返回输出张量
def compute_conv(conv2d, X=paddle.rand((1,1,5,5))): # 生成5×5的输入,维度为[NCHW]
    Y = conv2d(X)              # 执行卷积函数
    Y = Y.reshape(Y.shape[2:]) # 排除不关心的维度:批量和通道
    return Y

# 2. 定义有效卷积函数,padding=0
conv2d_valid = paddle.nn.Conv2D(in_channels=1, out_channels=1, kernel_size=3)
# 3. 定义同等卷积函数,padding=1
conv2d_same = paddle.nn.Conv2D(in_channels=1, out_channels=1, kernel_size=3, padding=1)
# 4. 输出卷积结果
print('有效卷积(padding=0)的形态:{}'.format(compute_conv(conv2d_valid).shape))
print('同等卷积(padding=1)的形态:{}'.format(compute_conv(conv2d_same).shape))

有效卷积(padding=0)的形态:[3, 3]
同等卷积(padding=1)的形态:[5, 5]

在某些任务中,我们可能会使用高度和宽度不一样的卷积核。此时,只需要对输入增加不同高度和宽度的零填充就可以使输出和输入具有相同的尺度。在以下代码中,我们使用 5×35×3 的矩形卷积核,并在高度和宽度的两边都分别填充 22 个和 11 个像素。通过不同的填充设置,即使使用高度和宽度不相同的矩形卷积核,依然能够输出尺度不变的特征图。

程序清单6-10 可视化不同填充的卷积输出

# codes06010_PaddingRectangle
# 定义同等卷积函数,卷积核形态为3×5,padding=(1,2)
conv2d_same2 = paddle.nn.Conv2D(in_channels=1, out_channels=1, kernel_size=(3,5), padding=(1,2))
print('同等卷积(kernel=(3,3),padding=(1,1))的形态:{}'.format(compute_conv(conv2d_same).shape))
print('同等卷积(kernel=(3,5),padding=(1,2))的形态:{}'.format(compute_conv(conv2d_same2).shape))

同等卷积(kernel=(3,3),padding=(1,1))的形态:[5, 5]
同等卷积(kernel=(3,5),padding=(1,2))的形态:[5, 5]

6.4.2 步幅

假设存在一个四维的核张量 KK,它的每个元素都可以用符号 Ki,j,k,lK_{i,j,k,l} 表示,其中 iijj 表示第 ii 个输出通道中的一个神经元与第 jj 个输入通道中的神经元的连接强度,下标 kkll 表示卷积核 KK 的高度和宽度,同时也表示输入单元和输出单元之间有 kkll 列个偏置。假定输入的观测数据为 XX,它的每个元素 Xi,j,kX_{i,j,k} 表示第 ii 个通道中第 jj 行、第 kk 列的值。假定输出 HH 和输入 XX 具有相同的形式,则输出 HH 可以表示为输入 XX 与核张量 KK 进行互相关运算的结果:
Hi,j,k=l,m,nXl,j+m1,k+n1Ki,l,m,n(6.11)H_{i,j,k} = \sum_{l,m,n} X_{l, j+m-1, k+n-1} K_{i,l,m,n} \tag{6.11}

在上式中对所有 l,m,nl,m,n 进行求和表示对张量中所有索引位置互相关运算的标量结果进行求和。在线性代数中,向量的索引通常从 11 开始,这与PythonC等程序设计语言从 00 开始的设定略有差异,这也是公式中 1-1 的原因。

图6-21 有效卷积和同等卷积

图6-21 完全基于有效卷积的神经网络

在实际应用中,我们可能会希望跳过输入的一些位置来降低计算开销,并快速地实现特征图尺度的缩小。如 图6-21 所示,假设存在一幅 224×224224×224 的输入图片,如果使用一系列 3×33×3 的卷积核来进行特征提取,并且希望最后输入到分类器中的特征图尺度为 4×44×4。那么,即使使用有效卷积 也需要110层才能完成特征图尺度的缩减。对于这样一幅不算特别大的图像,都需要110层之深的网络才能实现尺度的缩小,这将给硬件带来极大的负担。并且该模型必然会因为参数过多而产生严重的过拟合。如果想要实现特征图的快速缩小,又不让模型过深,一种比较简单的方法是增大卷积核。但我们现在已经知道,通常 5×55×53×33×3 这样的小卷积是比较合理。所以,如果选择使用小卷积核,最终还是得需要大量的计算。另外一种有效的思想是通过跳过一些位置来快速降低特征图的尺度。当然,这种方法会带来一定的信息损失。

假设我们在水平和垂直的方向都按照每隔 ss 个像素来进行采样,那么公式 6.12 可以被重新定义为:
Hi,j,k=l,m,nXl,(j1)×s+m,(k1)×s+nKi,l,m,n(6.11)H_{i,j,k} = \sum_{l,m,n} X_{l, (j-1)×s+m, (k-1)×s+n} K_{i,l,m,n} \tag{6.11}

在这里,超参数 ss 被称为卷积的步幅(stride)。通过步幅的设置,原来和层数线性相关的输出减小尺度,现在变成了和指数相关。所以步幅可以有效加速特征图的缩小。此处,在水平和垂直方向上我们使用的都是相同的定义,在实际应用中卷积核移动的每个方向也可以使用不同的步幅。

下面,我们用一个直观的例子来看看步幅是如何实现特征图的快速缩小。为了确保特征图的尺度缩小只受步幅影响,我们使用 SAME 卷积对输入进行特征提取。到目前位置,我们使用的都是高度和宽度相同,且都为 11 的步幅,那么如何使用较大且高宽不相同的步幅呢?图 6-22 是水平步幅为3,垂直步幅为2的二维互相关运算。着色部分是用于内相关运算的输入张量、核张量与输出张量,以左上角(第一行、第一列)的输出为例:0×0+0×1+0×2+0×3+2×4+7×3+0×2+5×1+2×0=340×0+0×1+0×2+0×3+2×4+7×3+0×2+5×1+2×0=34

为了获得右下角(第三行、第二列)的输出元素,卷积核将分别向右滑动三列、向下滑动四行,也就是向右滑动1个步幅单位(stridex=1×3=3stride_x=1×3=3)、向下滑动2个步幅单位(stridey=2×2=4stride_y=2×2=4)。此时,我们会发现在输入张量的最右边还有一列没有进行卷积运算。但是,如果继续让卷积核向右滑动3列,就会出现错误而无法产生输出。因为输入元素无法完全填充卷积核窗口,除非我们继续在输入的右边添加另外的2列填充。此时,该列将会被忽略。

图6-22 步幅卷积示例图

图6-22 步幅卷积示例图

6.4.1 小节中,我们已知在包含填充的卷积中,输出特征图的形状为:(nhkh+2ph+1)×(nwkw+2pw+1)(n_h-k_h+2p_h+1)×(n_w-k_w+2p_w+1)。此处,假设垂直步幅为 shs_h、水平步幅为 sws_w,则输出形状可以改写为:
(nhkh+2ph)/sh+1×(nwkw+2pw)/sw+1(6.12)\lfloor (n_h-k_h+2p_h)/s_h + 1 \rfloor × \lfloor (n_w-k_w+2p_w)/s_w + 1 \rfloor \tag{6.12}

此时按照前面的分析,设置 ph=(kh1)/2,pw=(kw1)/2p_h=(k_h−1)/2, p_w=(k_w−1)/2,则输出形状将简化为 (nh+sh1)/sh×(nw+sw1)/sw(6.13)\lfloor (n_ℎ+s_h-1)/s_h \rfloor × \lfloor (n_w+s_w-1)/s_w \rfloor \tag{6.13}

更进一步,如果设置输入的高度和宽度可以分别被垂直和水平步幅整除,则输出形状将变为:
(nh/sh)×(nw/sw)(6.14)(n_ℎ/s_h) × (n_w/s_w) \tag{6.14}

简单的说,输出特征图的尺度等于输入特征图的尺度除以步幅。下面的代码实现高度和宽度减半的卷积,此时输入尺度为 6×66×6,填充为 11,水平和垂直的步幅都为 22,记作:(sh,sw)=(2,2)(s_h, s_w)=(2,2)

程序清单6-11 可视化不同步长的卷积输出

# codes06011_Stride
# 定义同等卷积函数,卷积核形态为3×5,padding=(1,2)
conv2d_whole = paddle.nn.Conv2D(in_channels=1, out_channels=1, kernel_size=3, padding=1, stride=(2,3))
print('同等卷积(stride=(1,1))的形态:{}'.format(compute_conv(conv2d_same).shape))
print('同等卷积(stride=(2,3))的形态:{}'.format(compute_conv(conv2d_whole).shape))

同等卷积(stride=2,input=[6,6])的形态:[3, 3]

以下代码实现了图 6-22 的示例,即输出尺度为 6×66×6,水平步幅为 33、垂直步幅为 22

程序清单6-12 可视化矩形步幅卷积的输出

# codes06012_StrideDifferent
# 定义同等卷积函数,卷积核形态为3×3,stride=(2,3)
conv2d_whole2 = paddle.nn.Conv2D(in_channels=1, out_channels=1, kernel_size=3, padding=1, stride=(2,3))
print('同等卷积(stride=(1,1),input=[5,5])的形态:{}'.format(compute_conv(conv2d_same).shape))
print('同等卷积(stride=(2,3),input=[5,5])的形态:{}'.format(compute_conv(conv2d_whole2).shape))

同等卷积(stride=(1,1),input=[5,5])的形态:[5, 5]
同等卷积(stride=(2,3),input=[5,5])的形态:[3, 2]

在实践中,我们很少使用不一致的步幅和填充,也就是说,通常有 p=ph=pwp=p_h=p_ws=sh=sws=s_h=s_w。默认情况下,填充 p=0p=0,步幅s=1s=1

6.4.3 小结

6.4.4 练习

  1. 设输入图像的维度为32×32×3,若存在1个尺度为5×5的卷积核,其步长stride=2,则卷积运算之后的特征图的深度为()。
    A. 1
    B. 3
    C. 5
    D. 32

  2. 设特征图的尺度为224×224,若卷积核的尺度为5×5,步长stride=3,则卷积运算之后的特征图尺度为()。
    A. 224
    B. 74
    C. 219
    D. 73

  3. 设特征图的尺度为227×227×10,若卷积核的尺度为3×3×10,步长stride=2,填充padding=1,则卷积运算之后的特征图尺度为()。
    A. 227×227×10
    B. 224×224×10
    C. 114×114×10
    D. 113×113×10

  4. 设特征图的尺度为32×32×10,若卷积核的尺度为4×4×10,步长stride=2,填充padding=2,则该层的参数个数是多少(忽略偏置项)。
    A. 32×32×10×4×4×10=1638400
    B. 4×4×10=160
    C. (32+2×2-4)/2+1=17
    D. ((4+2)×(4+2)+2×2)×10=400

  5. 设特征图的尺度为48×48×32,若卷积核的尺度为1×1×32,步长stride=1,填充padding=0,则该层需要进行()次卷积运算。
    A. 32
    B. 48×48×32=73728
    C. 48×48=2304
    D. 1×1=1

  6. (多选)下列有关卷积层说法正确的是()。
    A. 卷积核必须是n×n的形式,即长宽相同
    B. 卷积后得到的特征图可以和原图尺寸一致
    C. 1×1卷积核常用来做降维,无法对原图提取特征
    D. 在卷积层中可以加入激活函数

  7. 假设将填充 php_hpwp_w 同时增加1倍,则最终的计算时间复杂度会(),内存占用量会()。
    A. 不变 不变
    B. 不变 增加
    C. 增加 不变
    D. 增加 增加

6.4 填充和步幅