动手学深度学习 - 5.2. 填充和步幅

动手学深度学习 - 5.2. 填充和步幅

动手学深度学习 - Dive into Deep Learning
Aston Zhang, Zachary C. Lipton, Mu Li, and Alexander J. Smola
https://zh.d2l.ai/

5.2. 填充和步幅

在上一节的例子里,我们使用高和宽为 3 的输入与高和宽为 2 的卷积核得到高和宽为 2 的输出。一般来说,假设输入形状是 n h × n w n_h\times n_w nh×nw,卷积核窗口形状是 k h × k w k_h\times k_w kh×kw,那么输出形状将会是

( n h − k h + 1 ) × ( n w − k w + 1 ) . (n_h-k_h+1) \times (n_w-k_w+1). (nhkh+1)×(nwkw+1).

所以卷积层的输出形状由输入形状和卷积核窗口形状决定。填充和步幅也可以对给定形状的输入和卷积核改变输出形状。

5.2.1. 填充

填充 (padding) 是指在输入高和宽的两侧填充元素 (通常是 0 元素)。图 5.2 里我们在原输入高和宽的两侧分别添加了值为 0 的元素,使得输入高和宽从 3 变成了 5,并导致输出高和宽由 2 增加到 4。图 5.2 中的阴影部分为第一个输出元素及其计算所使用的输入和核数组元素: 0 × 0 + 0 × 1 + 0 × 2 + 0 × 3 = 0 0\times0+0\times1+0\times2+0\times3=0 0×0+0×1+0×2+0×3=0

动手学深度学习 - 5.2. 填充和步幅_第1张图片

图 5.2 在输入的高和宽两侧分别填充了 0 元素的二维互相关计算

一般来说,如果在高的两侧一共填充 p h p_h ph行,在宽的两侧一共填充 p w p_w pw列,那么输出形状将会是

( n h − k h + p h + 1 ) × ( n w − k w + p w + 1 ) , (n_h-k_h+p_h+1)\times(n_w-k_w+p_w+1), (nhkh+ph+1)×(nwkw+pw+1),

也就是说,输出的高和宽会分别增加 p h p_h ph p w p_w pw

在很多情况下,我们会设置 p h = k h − 1 p_h=k_h-1 ph=kh1 p w = k w − 1 p_w=k_w-1 pw=kw1 来使输入和输出具有相同的高和宽。这样会方便在构造网络时推测每个层的输出形状。假设这里 k h k_h kh 是奇数,我们会在高的两侧分别填充 p h / 2 p_h/2 ph/2 行。如果 k h k_h kh 是偶数,一种可能是在输入的顶端一侧填充 ⌈ p h / 2 ⌉ \lceil p_h/2\rceil ph/2 行,而在底端一侧填充 ⌊ p h / 2 ⌋ \lfloor p_h/2\rfloor ph/2 行。在宽的两侧填充同理。

卷积神经网络经常使用奇数高宽的卷积核,如 1、3、5 和 7,所以两端上的填充个数相等。对任意的二维数组 X,设它的第 i 行第 j 列的元素为 X[i,j]。当两端上的填充个数相等,并使输入和输出具有相同的高和宽时,我们就知道输出 Y[i,j] 是由输入以 X[i,j] 为中心的窗口同卷积核进行互相关计算得到的。

下面的例子里我们创建一个高和宽为 3 的二维卷积层,然后设输入高和宽两侧的填充数分别为 1。给定一个高和宽为 8 的输入,我们发现输出的高和宽也是 8。

In [1]:
from mxnet import nd
from mxnet.gluon import nn

# 定义一个函数来计算卷积层。它初始化卷积层权重,并对输入和输出做相应的升维和降维
def comp_conv2d(conv2d, X):
    conv2d.initialize()
    # (1, 1) 代表批量大小和通道数均为 1
    X = X.reshape((1, 1) + X.shape)
    Y = conv2d(X)
    return Y.reshape(Y.shape[2:])  # 排除不关心的前两维:批量和通道

# 注意这里是两侧分别填充 1 行或列,所以在两侧一共填充 2 行或列
conv2d = nn.Conv2D(1, kernel_size=3, padding=1)
X = nd.random.uniform(shape=(8, 8))
comp_conv2d(conv2d, X).shape

Out[1]:
(8, 8)

当卷积核的高和宽不同时,我们也可以通过设置高和宽上不同的填充数使输出和输入具有相同的高和宽。

In [2]:
# 使用高为 5、宽为 3 的卷积核。在高和宽两侧的填充数分别为 2 和 1
conv2d = nn.Conv2D(1, kernel_size=(5, 3), padding=(2, 1))
comp_conv2d(conv2d, X).shape

Out[2]:
(8, 8)

5.2.2. 步幅

二维互相关运算的卷积窗口从输入数组的最左上方开始,按从左往右、从上往下的顺序,依次在输入数组上滑动。我们将每次滑动的行数和列数称为步幅 (stride)。

目前我们看到的例子里,在高和宽两个方向上步幅均为 1。我们也可以使用更大步幅。图 5.3 展示了在高上步幅为 3、在宽上步幅为 2 的二维互相关运算。可以看到,输出第一列第二个元素时,卷积窗口向下滑动了 3 行,而在输出第一行第二个元素时卷积窗口向右滑动了 2 列。当卷积窗口在输入上再向右滑动 2 列时,由于输入元素无法填满窗口,无结果输出。图 5.3 中的阴影部分为输出元素及其计算所使用的输入和核数组元素: 0 × 0 + 0 × 1 + 1 × 2 + 2 × 3 = 8 0\times0+0\times1+1\times2+2\times3=8 0×0+0×1+1×2+2×3=8 0 × 0 + 6 × 1 + 0 × 2 + 0 × 3 = 6 0\times0+6\times1+0\times2+0\times3=6 0×0+6×1+0×2+0×3=6

动手学深度学习 - 5.2. 填充和步幅_第2张图片

图 5.3 高和宽上步幅分别为 3 和 2 的二维互相关运算

一般来说,当高上步幅为 s h s_h sh,宽上步幅为 s w s_w sw 时,输出形状为

⌊ ( n h − k h + p h + s h ) / s h ⌋ × ⌊ ( n w − k w + p w + s w ) / s w ⌋ . \lfloor(n_h-k_h+p_h+s_h)/s_h\rfloor \times \lfloor(n_w-k_w+p_w+s_w)/s_w\rfloor. (nhkh+ph+sh)/sh×(nwkw+pw+sw)/sw.

如果设置 p h = k h − 1 p_h=k_h-1 ph=kh1 p w = k w − 1 p_w=k_w-1 pw=kw1,那么输出形状将简化为 ⌊ ( n h + s h − 1 ) / s h ⌋ × ⌊ ( n w + s w − 1 ) / s w ⌋ \lfloor(n_h+s_h-1)/s_h\rfloor \times \lfloor(n_w+s_w-1)/s_w\rfloor (nh+sh1)/sh×(nw+sw1)/sw。更进一步,如果输入的高和宽能分别被高和宽上的步幅整除,那么输出形状将是 ( n h / s h ) × ( n w / s w ) (n_h/s_h) \times (n_w/s_w) (nh/sh)×(nw/sw)

下面我们令高和宽上的步幅均为 2,从而使输入的高和宽减半。

In [3]:
conv2d = nn.Conv2D(1, kernel_size=3, padding=1, strides=2)
comp_conv2d(conv2d, X).shape

Out[3]:
(4, 4)

接下来是一个稍微复杂点儿的例子。

In [4]:
conv2d = nn.Conv2D(1, kernel_size=(3, 5), padding=(0, 1), strides=(3, 4))
comp_conv2d(conv2d, X).shape

Out[4]:
(2, 2)

为了表述简洁,当输入的高和宽两侧的填充数分别为 p h p_h ph p w p_w pw 时,我们称填充为 ( p h , p w ) (p_h, p_w) (ph,pw)。特别地,当 p h = p w = p p_h = p_w = p ph=pw=p 时,填充为 p p p。当在高和宽上的步幅分别为 s h s_h sh s w s_w sw 时,我们称步幅为 ( s h , s w ) (s_h, s_w) (sh,sw)。特别地,当 s h = s w = s s_h = s_w = s sh=sw=s 时,步幅为 s s s。在默认情况下,填充为 0,步幅为 1。

5.2.3. 小结

  • 填充可以增加输出的高和宽。这常用来使输出与输入具有相同的高和宽。
  • 步幅可以减小输出的高和宽,例如输出的高和宽仅为输入的高和宽的 1 / n 1/n 1/n ( n n n 为大于 1 的整数)。

你可能感兴趣的:(deep,learning,-,深度学习)