1.最大池化和平均池化
池化层的一个主要作用是缓解卷积层对位置的过度敏感性。即实际图像里,我们感兴趣的物体不会总出现在固定位置:即使我们连续拍摄同一个物体也极有可能出现像素位置上的偏移。这会导致同一个边缘对应的输出可能出现在卷积输出Y中的不同位置,进而对后面的模式识别造成不便。
图5.6展示了池化窗口形状为2×2的最大池化,阴影部分为第一个输出元素及其计算所使用的输入元素。输出数组的高和宽分别为2,其中的4个元素由取最大值运算max得出:
平均池化求平均即可,取池化窗口中输入元素的平均值。
代码实现:
import torch from torch import nn def pool2d(X, pool_size, mode='max'): p_h, p_w = pool_size Y = torch.zeros((X.shape[0] - p_h + 1, X.shape[1] - p_w + 1)) for i in range(Y.shape[0]): for j in range(Y.shape[1]): if mode == 'max': Y[i, j] = X[i: i + p_h, j: j + p_w].max() elif mode == 'avg': Y[i, j] = X[i: i + p_h, j: j + p_w].mean() return Y
验证结果:
X = torch.tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]], dtype=torch.float32) pool2d(X, (2, 2))
tensor([[4., 5.], [7., 8.]])
平均池化:
pool2d(X, (2, 2), 'avg') tensor([[2., 3.], [5., 6.]])
2.填充和步幅
同卷积层一样,池化层也可以在输入的高和宽两侧的填充并调整窗口的移动步幅来改变输出形状。
代码:
X = torch.arange(16, dtype=torch.float32).reshape((1, 1, 4, 4)) print(X)
tensor([[[[ 0., 1., 2., 3.], [ 4., 5., 6., 7.], [ 8., 9., 10., 11.], [12., 13., 14., 15.]]]])
默认情况下,MaxPool2D实例里步幅和池化窗口形状相同。下面使用形状为(3, 3)的池化窗口,默认获得形状为(3, 3)的步幅。
pool2d = nn.MaxPool2d(3) pool2d(X) tensor([[[[10.]]]])
步幅和填充也可以手动定义。
pool2d = nn.MaxPool2d(3, padding=1, stride=2) pool2d(X)
结果:
tensor([[[[ 5., 7.], [13., 15.]]]])
也可以指定非正方形的池化窗口,并分别指定高和宽上的填充和步幅。
pool2d = nn.MaxPool2d((2, 3), padding=(1, 1), stride=(2, 3)) pool2d(X) tensor([[[[ 1., 3.], [ 9., 11.], [13., 15.]]]])
3.多通道
池化层对每个输入通道分别池化,即池化层的输出通道数与输入通道数相等。
下面将数组X和X+1在通道维上连结来构造通道数为2的输入。
代码:
X = torch.cat((X, X + 1), dim=1) X tensor([[[[ 0., 1., 2., 3.], [ 4., 5., 6., 7.], [ 8., 9., 10., 11.], [12., 13., 14., 15.]], [[ 1., 2., 3., 4.], [ 5., 6., 7., 8.], [ 9., 10., 11., 12.], [13., 14., 15., 16.]]]])
池化:
pool2d = nn.MaxPool2d(3, padding=1, stride=2) pool2d(X) tensor([[[[ 5., 7.], [13., 15.]], [[ 6., 8.], [14., 16.]]]])
池化后通道数仍为2。