目录
- 池化层
- 二维最大池化
- 填充、步幅和多个通道
- 平均池化层
- 总结
- 池化层的代码部分
- 实现池化层的正向传播
- 验证二维最大池化层的输出
- 该部分总代码
- 验证平均池化层
- 该部分总代码
- 填充和步幅
- 深度学习框架中的步幅与池化窗口的大小相同
- 该部分总代码
- 填充和步幅可以手动设定
- 该部分总代码
- 自定义池化窗口、填充、步幅
- 该部分总代码
- 多个通道
- 该部分总代码
- 池化后输出通道数量不变(输入=输出)
- 该部分总代码
- 小结
- 问题
池化层
①二维卷积层可以帮助我们检测图像物体边缘。
②无论是原始图片,还是经过卷积核处理的中间特征,都是基于像素的基础进行的数学运算。
③实际图像里,我们感兴趣的物体不会总出现在固定像素位置:即使我们用三脚架固定相机去连续拍摄同一个物体也极有可能出现像素位置上的偏移。
④绝大多数计算机视觉任务对图像处理终极目标是识别图片内的物体,所以不需要细致到对每个像素进行检测,只需要找到图片中物体的大概轮廓就好了。
⑤池化层可以缓解卷积层对位置的过度敏感性。
如下所示,例如1X2的卷积核[1,-1],会使得下图中Y输出的第二列为1,其他为0,如果像素偏移,会导致边缘检测的1在其他位置输出,所以说卷积对像素的位置是非常敏感的。
这里本质上讲的是池化层对于像素偏移的容忍性。
二维最大池化
2×2的池化窗口
跟卷积一样,用滑动窗口来计算输出,但没有进行求和而是选其中的最大值。
返回滑动窗口的最大值
输入:进行边缘检测
卷积输出
2×2最大池化(可容1像素移位)
填充、步幅和多个通道
①池化层与卷积层类似,都具有填充和步幅。(填充上下左右都是1)
②没有可学习的参数。
③在每个输入通道应用池化层以获得相应的输出通道(不像卷积一样可以融合多个通道)
④输出通道数=输入通道数
平均池化层
最大池化层:每个窗口中最强的模式信号。
平均池化层:将最大池化层中的“最大”操作替换“平均”
总结
①池化层返回窗口中最大或平均值
②缓解卷积层对位置的敏感性
③同样有窗口大小、填充和步幅作为超参数
池化层的代码部分
实现池化层的正向传播
import torch
from torch import nn
from d2l import torch as d2l
# X:输入的特征图,pool_size:池化窗口的大小,以及mode(池化模式,默认为'max'
def pool2d(X, pool_size, mode='max'):
# 池化窗口的高度和宽度
p_h, p_w = pool_size
# 根据输入特征图X的形状和池化窗口的大小计算得出Y的形状
Y = torch.zeros((X.shape[0] - p_h + 1, X.shape[1] - p_w + 1))
# 遍历输出特征图Y的每一个位置(i, j)
for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
if mode == 'max':
# 判断池化模式是否为'max'。如果是,就在输入特征图X上对应的池化窗口内执行最大池化操作,并将最大值赋值给Y[i, j]
Y[i, j] = X[i: i + p_h, j: j + p_w].max()
elif mode == 'avg':
# 判断池化模式是否为'avg'。如果是,就在输入特征图X上对应的池化窗口内执行平均池化操作,并将平均值赋值给Y[i, j]。
Y[i, j] = X[i: i + p_h, j: j + p_w].mean()
return Y
解释一下:(X.shape[0] - p_h + 1, X.shape[1] - p_w + 1)
这是一个输入特征图,即X,它是4×5的形状
这里
X.shape[0]=4
X.shape[1]=5
p_h=p_w=2
∴这里的Y的形状是3×4的形状,(然后代码中是对Y进行一个全零的初始化。)
验证二维最大池化层的输出
X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
pool2d(X, (2, 2))
输出:
该部分总代码
import torch
# X:输入的特征图,pool_size:池化窗口的大小,以及mode(池化模式,默认为'max'
def pool2d(X, pool_size, mode='max'):
# 池化窗口的高度和宽度
p_h, p_w = pool_size
# 根据输入特征图X的形状和池化窗口的大小计算得出Y的形状
Y = torch.zeros((X.shape[0] - p_h + 1, X.shape[1] - p_w + 1))
# 遍历输出特征图Y的每一个位置(i, j)
for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
if mode == 'max':
# 判断池化模式是否为'max'。如果是,就在输入特征图X上对应的池化窗口内执行最大池化操作,并将最大值赋值给Y[i, j]
Y[i, j] = X[i: i + p_h, j: j + p_w].max()
elif mode == 'avg':
# 判断池化模式是否为'avg'。如果是,就在输入特征图X上对应的池化窗口内执行平均池化操作,并将平均值赋值给Y[i, j]。
Y[i, j] = X[i: i + p_h, j: j + p_w].mean()
return Y
X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
print(pool2d(X, (2, 2)))
验证平均池化层
pool2d(X, (2, 2), 'avg')
输出:
该部分总代码
import torch
# X:输入的特征图,pool_size:池化窗口的大小,以及mode(池化模式,默认为'max'
def pool2d(X, pool_size, mode='max'):
# 池化窗口的高度和宽度
p_h, p_w = pool_size
# 根据输入特征图X的形状和池化窗口的大小计算得出Y的形状
Y = torch.zeros((X.shape[0] - p_h + 1, X.shape[1] - p_w + 1))
# 遍历输出特征图Y的每一个位置(i, j)
for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
if mode == 'max':
# 判断池化模式是否为'max'。如果是,就在输入特征图X上对应的池化窗口内执行最大池化操作,并将最大值赋值给Y[i, j]
Y[i, j] = X[i: i + p_h, j: j + p_w].max()
elif mode == 'avg':
# 判断池化模式是否为'avg'。如果是,就在输入特征图X上对应的池化窗口内执行平均池化操作,并将平均值赋值给Y[i, j]。
Y[i, j] = X[i: i + p_h, j: j + p_w].mean()
return Y
X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
print(pool2d(X, (2, 2), 'avg'))
填充和步幅
与卷积层一样,池化层也可以改变输出形状。和以前一样,我们可以通过填充和步幅以获得所需的输出形状。 下面,我们用深度学习框架中内置的二维最大汇聚层,来演示汇聚层中填充和步幅的使用。 我们首先构造了一个输入张量X,它有四个维度,其中样本数和通道数都是1。
# 样本数和通道数都是1,高度和宽度都是4
X = torch.arange(16, dtype=torch.float32).reshape((1, 1, 4, 4))
print(X)
输出:
深度学习框架中的步幅与池化窗口的大小相同
默认情况下,深度学习框架中的步幅与池化窗口的大小相同。 因此,如果我们使用形状为(3, 3)的池化窗口,那么默认情况下,我们得到的步幅形状为(3, 3)。
pool2d = nn.MaxPool2d(3)
pool2d(X)
结果:
该部分总代码
import torch
from torch import nn
X = torch.arange(16, dtype=torch.float32).reshape((1, 1, 4, 4))
pool2d = nn.MaxPool2d(3)
print(pool2d(X))
填充和步幅可以手动设定
pool2d = nn.MaxPool2d(3, padding=1, stride=2)
pool2d(X)
结果:
该部分总代码
import torch
from torch import nn
X = torch.arange(16, dtype=torch.float32).reshape((1, 1, 4, 4))
pool2d = nn.MaxPool2d(3, padding=1, stride=2)
print(pool2d(X))
自定义池化窗口、填充、步幅
设定一个任意大小的矩形池化窗口,并分别设定填充和步幅的高度和宽度
# 池化窗口的大小,高度为2,宽度为3
pool2d = nn.MaxPool2d((2, 3), padding=(1, 1), stride=(2, 3))
print(pool2d(X))
结果:
该部分总代码
import torch
from torch import nn
X = torch.arange(16, dtype=torch.float32).reshape((1, 1, 4, 4))
# 池化窗口的大小,高度为2,宽度为3
pool2d = nn.MaxPool2d((2, 3), padding=(1, 1), stride=(2, 3))
print(pool2d(X))
多个通道
在处理多通道输入数据时,池化层在每个输入通道上单独运算,而不是像卷积层一样在通道上对输入进行汇总。 这意味着池化层的输出通道数与输入通道数相同。下面,我们将在通道维度上连结张量X和X + 1,以构建具有2个通道的输入。
# 创建了一个与 X 形状相同的新张量X+1,将 X 和 X + 1 这两个张量沿着它们的第二个维度(索引为1的维度,记住索引是从0开始的)拼接起来。形成一个新的张量。
X = torch.cat((X, X + 1), 1)
print(X)
结果:
假设 X 是一个形状为 (N, C, H, W) 的四维张量
N 是批量大小(batch size)
C 是通道数(channel)
H 是高度(height)
W 是宽度(width)
执行 torch.cat((X, X + 1), 1) 后,结果张量的形状将是 (N, 2*C, H, W)。这是因为在第二个维度(即通道维度)上,我们将 X 和 X + 1 拼接了起来,所以通道数从 C 变成了 2×C。
stack是增加维度(即:stack是升维拼接)
cat是在当前维度上连接(cat是等维拼接)
该部分总代码
import torch
X = torch.arange(16, dtype=torch.float32).reshape((1, 1, 4, 4))
X = torch.cat((X, X + 1), 1)
print(X)
池化后输出通道数量不变(输入=输出)
pool2d = nn.MaxPool2d(3, padding=1, stride=2)
pool2d(X)
结果:
该部分总代码
import torch
from torch import nn
X = torch.arange(16, dtype=torch.float32).reshape((1, 1, 4, 4))
X = torch.cat((X, X + 1), 1)
pool2d = nn.MaxPool2d(3, padding=1, stride=2)
print(pool2d(X))
小结
①对于给定输入元素,最大池化层会输出该窗口内的最大值,平均池化层会输出该窗口内的平均值。
②池化层的主要优点之一是减轻卷积层对位置的过度敏感。
③我们可以指定池化层的填充和步幅。
④使用最大池化层以及大于1的步幅,可减少空间维度(如高度和宽度)。
⑤池化层的输出通道数与输入通道数相同。
问题
①池化层一般是放在卷积后面吗?
一般放在后面,因为使卷积层不那么对位置敏感。
②为什么Padding里面用的是一个元组?
因为Padding里面是有个高和宽的,是2D的形状。假设一个值的话,即它的高和宽都是这个值。