5.2 填充和步幅
填充和步幅
假设输入形状是\(n_h\times n_w\),卷积核窗口形状是\(k_h\times k_w\),那么输出形状将会是
卷积层的输出形状由输入形状和卷积核窗口形状决定。
填充
填充(padding
)是指在输入高和宽的两侧填充元素(通常是\(0\)元素).
一般来说,如果在高的两侧一共填充\(p_h\)行,在宽的两侧一共填充\(p_w\)列,那么输出形状将会是
也就是说,输出的高和宽会分别增加\(p_h\)和\(p_w\)。
很多情况下,我们会设置\(p_h=k_h-1\)和\(p_w=k_w-1\)来使输入和输出具有相同的高和宽。这样会方便在构造网络时推测每个层的输出形状.
假设这里\(k_h\)是奇数,我们会在高的两侧分别填充\(p_h/2\)行。如果\(k_h\)是偶数,一种可能是在输入的顶端一侧填充\(\lceil p_h/2\rceil\)行,而在底端一侧填充\(\lfloor p_h/2\rfloor\)行。在宽的两侧填充同理。
卷积神经网络经常使用奇数高和宽的卷积核,如1、3、5和7,所以两端上的填充个数相等。
创建一个高和宽为\(3\)的二维卷积层,然后设输入高和宽两侧的填充数分别为\(1\)。给定一个高和宽为\(8\)的输入,我们发现输出的高和宽也是\(8\)
from mxnet import nd
from mxnet.gluon import nn
# 定义一个函数来计算卷积层。它初始化卷积层权重,并对输入和输出做相应的升维和降维
def comp_conv2d(conv2d, X):
conv2d.initialize()
# (1, 1)代表批量大小和通道数(“多输入通道和多输出通道”一节将介绍)均为1
# 将输入的shape加入批量大小和通道数,现在的shape是(1,1,8,8)
X = X.reshape((1, 1) + X.shape)
# 利用卷积层得到输出
Y = conv2d(X)
# 排除前两维,那么输出的shape=(8,8)
return Y.reshape(Y.shape[2:]) # 排除不关心的前两维:批量和通道
lass mxnet.gluon.nn.Conv2D(channels, kernel_size, strides=(1, 1), padding=(0, 0), dilation=(1, 1), groups=1, layout='NCHW', activation=None, use_bias=True, weight_initializer=None, bias_initializer='zeros', in_channels=0, **kwargs)
#通道数
channels (int) – The dimensionality of the output space, i.e. the number of output channels (filters) in the convolution.
# 卷积层尺寸
kernel_size (int or tuple/list of 2 int) – Specifies the dimensions of the convolution window.
#有无填充
padding (int or a tuple/list of 2 int,) – If padding is non-zero, then the input is implicitly zero-padded on both sides for padding number of points
# 注意这里是两侧分别填充1行或列,所以在两侧一共填充2行或列
conv2d = nn.Conv2D(1, kernel_size=3, padding=1)
#初始化输入shape=(8,8)
X = nd.random.uniform(shape=(8, 8))
comp_conv2d(conv2d, X).shape
当卷积核的高和宽不同时,也可以通过设置高和宽上不同的填充数使输出和输入具有相同的高和宽。
# 使用高为5、宽为3的卷积核。在高和宽两侧的填充数分别为2和1
# 使用上面的计算公式:k_h = 5,(5 - 1) / 2 = 2,k_w = 3,(3 - 1) / 2 = 1
conv2d = nn.Conv2D(1, kernel_size=(5, 3), padding=(2, 1))
comp_conv2d(conv2d, X).shape
步幅
卷积窗口从输入数组的最左上方开始,按从左往右、从上往下的顺序,依次在输入数组上滑动。将每次滑动的行数和列数称为步幅(stride
)。
一般来说,当高上步幅为\(s_h\),宽上步幅为\(s_w\)时,输出形状为
如果设置\(p_h=k_h-1\)和\(p_w=k_w-1\),那么输出形状将简化为\(\lfloor(n_h+s_h-1)/s_h\rfloor \times \lfloor(n_w+s_w-1)/s_w\rfloor\)。更进一步,如果输入的高和宽能分别被高和宽上的步幅整除,那么输出形状将是\((n_h/s_h) \times (n_w/s_w)\)。
令高和宽上的步幅均为\(2\),从而使输入的高和宽减半:
conv2d = nn.Conv2D(1, kernel_size=3, padding=1, strides=2)
comp_conv2d(conv2d, X).shape
#out:
(4, 4)
个稍微复杂点儿的例子:
conv2d = nn.Conv2D(1, kernel_size=(3, 5), padding=(0, 1), strides=(3, 4))
comp_conv2d(conv2d, X).shape
为了表述简洁,当输入的高和宽两侧的填充数分别为\(p_h\)和\(p_w\)时,我们称填充为\((p_h, p_w)\)。特别地,当\(p_h = p_w = p\)时,填充为\(p\)。当在高和宽上的步幅分别为\(s_h\)和\(s_w\)时,我们称步幅为\((s_h, s_w)\)。特别地,当\(s_h = s_w = s\)时,步幅为\(s\)。在默认情况下,填充为0,步幅为1。