填充和步幅
假设输入形状是 \(n_h \times n_w\),卷积核窗口形状是 \(k_h \times k_w\),则输出形状是
这里介绍卷积层的两个超参数填充和步幅,它们可以改变输出形状。
1. 填充
填充(\(padding\))是指在输入高和宽的两侧填充元素(通常是0元素)。
输入形状 \(n_h \times n_w\),卷积核 \(k_h \times k_w\),如果在高的两侧一共填充 \(p_h\) 行,宽的两侧一共填充 \(p_w\) 列,那么输出形状是
输出的高和宽会分别增加 \(p_h、p_w\)。
大多情况下,设置 \(p_h = k_h -1\)、\(p_w = k_w -1\) 来使输入和输出具有相同的高和宽。假设 \(k_h\) 是奇数,在高的两侧填充 \(\frac{p_h}{2}\) 行。\(k_h\) 是偶数,在输入的顶端一侧填充 \(\lceil \frac{p_h}{2} \rceil\),在底端一侧填充 \(\lfloor \frac{p_h}{2} \rfloor\)。在宽的两侧填充同理。
示例:输入形状 \(n_h = n_w = 8\),二维卷积层 \(k_h = k_w = 3\),填充数 \(p_h = p_w = 1\)。求输出形状。
import torch
from torch import nn
# 定义一个函数来计算卷积层。它对输入和输出做相应的升维和降维
def comp_conv2d(conv2d, X):
X = X.view((1, 1) + X.shape) # (1, 1)代表批量大小和通道数均为1
Y = conv2d(X)
return Y.view(Y.shape[2:]) # 排除不关心的前两维:批量和通道
# 注意这里是两侧分别填充1行或列,所以在两侧一共填充2行或列
conv2d = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=(3,3), padding=1)
X = torch.rand(8, 8)
print(comp_conv2d(conv2d, X).shape)
torch.Size([8, 8])
当卷积核的高、宽不同时,可以设置高、宽不同的填充数使输出和输入有相同的形状。
示例 1:卷积核 \(k_h = 5, k_w = 3\),填充数 \(p_h=2, p_w=1\)。求输出形状。
# 使用高为5、宽为3的卷积核。在高和宽两侧的填充数分别为2和1
conv2d = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=(5, 3), padding=(2, 1))
print(comp_conv2d(conv2d, X).shape)
torch.Size([8, 8])
2. 步幅
卷积窗口每次滑动的行数和列数称为步幅(\(stride\))。
图 \(5.3\) 展示了在高上步幅为 \(3\)、宽上步幅为 \(2\) 的二维互相关运算。
示例 2:令高和宽上的步幅均为 \(2\),从而使输入的高和宽减半。
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1, stride=2)
comp_conv2d(conv2d, X).shape
torch.Size([4, 4])
示例 3:
conv2d = nn.Conv2d(1, 1, kernel_size=(3, 5), padding=(0, 1), stride=(3, 4))
comp_conv2d(conv2d, X).shape
torch.Size([2, 2])
3. 小结
- 填充可以增加输出的高和宽。这常用来使输出与输入具有相同的高和宽。
- 步幅可以减小输出的高和宽,例如输出的高和宽仅为输入的高和宽的 \(1/n\)(\(n\) 为大于 \(1\) 的整数)。
来自:《动手学深度学习》