池化层/汇聚层
摘自《动手学深度学习》:
通常当我们处理图像时,我们希望逐渐降低隐藏表示的空间分辨率、聚集信息,这样随着我们在神经网络中层叠的上升,每个神经元对其敏感的感受野(输入)就越大。
而我们的机器学习任务通常会跟全局图像的问题有关(例如,“图像是否包含一只猫呢?”),所以我们最后一层的神经元应该对整个输入的全局敏感。通过逐渐聚合信息,生成越来越粗糙的映射,最终实现学习全局表示的目标,同时将卷积图层的所有优势保留在中间层。
汇聚(pooling)层,它具有双重目的:降低卷积层对位置的敏感性,同时降低对空间降采样表示的敏感性。
我也觉得翻译成汇聚层可能更好,但是已经改口改不过来了。下面的文字会出现汇聚与池化混用的情况。
卷积层与池化层
与卷积层类似,汇聚层运算符由一个固定形状的窗口组成,该窗口根据其步幅大小在输入的所有区域上滑动,为固定形状窗口(有时称为汇聚窗口)遍历的每个位置计算一个输出。 然而,不同于卷积层中的输入与卷积核之间的互相关计算,汇聚层不包含参数。 相反,池运算是确定性的,我们通常计算汇聚窗口中所有元素的最大值或平均值。这些操作分别称为最大汇聚层(maximum pooling)和平均汇聚层(average pooling)。
在Resnet中,网络的开始有一个3x3大小,stride为2的最大池化层。在pytroch实现的resnet中也可以看到,其具有kernel_size, stride, padding等与卷积层一样的参数。而Resnet最后的平均池化,可以看作其核大小为7。
最大汇聚与平均汇聚
首先它们都具有汇聚的特点,那就是减少数据量。最大池化是一个窗口得到一个最大值,平均池化是一个窗口得到一个平均值,都是一个窗口得到一个值,多个汇聚成一个。具体的降采样效率,那就取决于窗口大小,步长等参数了。
最大汇聚
最大池化选择池化窗口中数值最大的元素,这意味着它优先保留局部区域中最显著的特征,忽略其他较小的值,保留图像中的关键信息,所以对与边缘、轮廓等高对比度的信息十分有效。并且池化后的特征对小幅度的平移、旋转等变化有较强的鲁棒性。
不过由于最大汇聚忽略了较小的值,可能会导致某些背景信息或对比度的特征丢失。而且,相对于平均池化,其只关心局部最大值,而丢失了更多的上下文信息。
平均汇聚
平均池化计算池化窗口内所有元素的平均值,考虑了池化窗口中所有像素的贡献,从而保留了局部区域的整体信息,所以其具有更强的平滑效果,并且能够保留更多的信息,比如背景信息。
因为由于它计算的是均值,可能会忽略局部的显著特征,尤其是对于高对比度的特征,平均池化会削弱其效果。
在Resnet中,网络的开始阶段使用了一个步长为2的最大池化层,主要目的是为了降低数据量,并且保留轮廓等显著特征。在网络的最后,特征送入全连接层之前,进行一次全局平均池化,一个7x7的特征,降低为1x1的特征,这里是为了给经过一层层抽象后的特征保留更多的信息。
其他池化方法
在一篇论文中,其使用L2池化去
For normalization and pooling we use Euclidean norm which is defined by F i = F i max ( ∥ F i ∥ 2 , ϵ ) F_i=\frac{F_i}{\max \left(\left\|F_i\right\|_2, \epsilon\right)} Fi=max(∥Fi∥2,ϵ)Fi followed by a l 2 l_2 l2 pooling layer which has been used to demonstrate the behavior of complex cells in primary visual cortex. The l 2 l_2 l2 pooling layer defines by:
P ( x ) = g ∗ ( x ⊙ x ) P(x)=\sqrt{g *(x \odot x)} P(x)=g∗(x⊙x)
where ⊙ \odot ⊙ denotes point-wise product, and the blurring kernel g ( . ) g(.) g(.) is implemented via a Hamming window that approximately applies the Nyquist criterion.归一化使用欧氏范数,接下来跟一个 l 2 l_2 l2池化,这种池化方式已经被用于展示初级视觉皮层中复杂细胞的行为。
相对与进行 l 2 l_2 l2池化,这里还加了个 g g g函数,叫做模糊核,使用应用了Nyquist准则的Hamming窗口实现。
一开始对与 l 2 l_2 l2池化层感觉十分疑惑,这是干什么的,还有这种池化吗?问了问GPT,它这样说:
L2池化(L2 pooling)是一种用于降采样特征图的池化技术,常用于卷积神经网络(CNN)中。与更常见的最大池化(Max Pooling)和平均池化(Average Pooling)不同,L2池化通过计算池化窗口内所有数值的L2范数(即欧几里得范数)来进行特征聚合。
或许它叫L2汇聚就容易一眼看出来它的意义了。将一个窗口汇聚成最大值,汇聚成平均值,汇聚成L2范数,都是汇聚,都是池化。优点就是它比最大池化和平均池化对噪声更加鲁棒,在处理平滑过渡或者具有复杂特征的图像时表现得更好,可以保持更多的特征信息。
至于加了一个模糊核,是为了保留更多的信息,让数据处理更加平滑。
实现:
class L2pooling(nn.Module):
def __init__(self, filter_size=5, stride=1, channels=None, pad_off=0):
super(L2pooling, self).__init__()
self.padding = (filter_size - 2)//2
self.stride = stride
self.channels = channels
a = np.hanning(filter_size)[1:-1] # filter_size为5时,a的长度是3,一个numpy的1维ndarray
g = torch.Tensor(a[:, None]*a[None, :]) # 3*1和1*3的矩阵相乘,得到3*3的矩阵
g = g/torch.sum(g) # 归一化
self.register_buffer('filter', g[None, None, :, :].repeat((self.channels, 1, 1, 1))) # shape: [channels, 1, size, size]
# register_buffer是 PyTorch 用来注册一个不会被视为模型参数(不会更新)但需要存储的变量的方法。
def forward(self, input): # input's shape: [batch_size, in_channels, height, width]
input = input**2
out = F.conv2d(input, self.filter, stride=self.stride, padding=self.padding, groups=input.shape[1])
return (out+1e-12).sqrt()
在conv2d
参数中,第二个位置的形状为
(
o
u
t
p
u
t
_
c
h
a
n
n
e
l
s
,
i
n
_
c
h
a
n
n
e
l
s
g
r
o
u
p
s
,
h
e
i
g
h
t
,
w
i
d
t
h
)
(output\_channels,\frac{in\_channels}{groups}, height, width)
(output_channels,groupsin_channels,height,width),这里用了分组卷积,分的组数等于输入通道数,也就是一个通道一组,每个通道使用各自的filter进行卷积。
# 我们平时会这样使用一个卷积层,并且都是用的groups=1,没有将输入按照通道进行分组。进行分组的话就是在每组上分别进行卷积,最后连接起来。
class Net:
def __init__(self):
self.conv2d_1 = nn.Conv2d(128, 256, 3, 2, 1)
def forward(self, input):
out = self.conv2d_1(input)
return out
# 当我们定义这样一个网络,并输入一个shape为 (128, 56, 56)的张量
# 前向传播后变为(256, 28, 28)
# 使用torch.nn.functional去实现,这样方便自己去设置卷积核
input = torch.rand((128, 56, 56))
filter = torch.rand((256, 128, 3, 3)) # 也就是准备了256个128x3x3的卷积核