填充和步幅
给定32✖32的输入图像,应用5✖5大小的卷积核(第一层输出28✖28,第7层输出大小是4✖4,即每一层都-4)
更大的卷积核可以更快地减小输出大小:形状从nk✖nk减少到(nh-kn+1)✖(nw-kw+1)
1.填充
在输入的周围添加额外的行/列,考虑了角落里的特征
(1) 填充ph行和ph列,输出形状是(nh-kh+ph+1)✖(nw-kw+pw+1)
(2) 通常取ph=kh-1, pw=kw-1,核的高宽-1,即-kh+ph+1和-kw+pw+1都消除为0
(3) 当kh为奇数:在上下两侧填充ph/2
(4) 当kh为偶数:在上侧填充ph/2,在下侧填充ph/2
2.步幅
填充减小的输出大小与层数线性相关。
给定输入大小224✖224,在使用5✖5卷积核的情况下,需要55层将输出降低为4✖4,即需要大量计算才能得到较小的输出。
步幅是指行/列的滑动步长。
(1)给定高度sh,宽度sw,输出形状:[(nh-kh+ph+sh)/sh]✖[(nw-kw+pw+sw)/sw]
(2)如果ph=kh-1, pw=kw-1,则[(nh+sh-1)/sh]✖[(nw+sw-1)/sw]
(3)如果输入高度和宽度可以被步幅整除:(nh/sh)✖(nw/sw)
(另外:步幅通常取2)
总结
(1)填充和步幅是卷积层的超参数
(2)填充在输入周围添加额外的行/列,来控制输出形状的减少量
(3)步幅是每次滑动核窗口时的行/列的步长,可以成倍的减少输出形状
代码实现填充和步幅
在所有侧边填充1个像素。
import torch
from torch import nn
def 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) # 核大小3✖3,上下左右各填充1行
X=torch.rand(size=(8,8)) # p是2✖2,k是3✖3,满足p=k-1,所有输出的形状不变
comp_conv2d(conv2d,X).shape
torch.Size([8, 8])
填充不同的高度和宽度。
conv2d=nn.Conv2d(1, 1, kernel_size=(5,3), padding=(2,1)) # 核5✖3,为保证输出形状不变,应该填充上下各2,左右各1
comp_conv2d(conv2d, X).shape
torch.Size([8, 8])
将高度和宽度的步幅设置为2
conv2d=nn.Conv2d(1,1,kernel_size=3,padding=1,stride=2) # 当步幅为2,一般输出的形状缩小为原来一半
comp_conv2d(conv2d,X).shape
torch.Size([4, 4])
一个稍微复杂的例子
conv2d=nn.Conv2d(1,1,kernel_size=(3,5),padding=(0,1), stride=(3,4))
comp_conv2d(conv2d,X).shape # (8-3+0+3)/3=2; (8-5+1*2+4)/4=2
torch.Size([2, 2])