赞
踩
这篇论文出自CVPR2020,是一个通道注意力模块。
浅浅的翻译一下摘要,通过论文的摘要能大致知道这篇文章要讲什么,算是非常重要的一个环节。
近年来,通道注意机制在提高深度卷积神经网络(CNNs)性能方面发挥了巨大的潜力。然而,现有的方法大多致力于开发更复杂的注意力模块以获得更好的性能,这不可避免地增加了模型的复杂性。为了克服性能与复杂度权衡的矛盾,本文提出了一种高效通道注意力(ECA)模块,该模块只涉及少数几个参数,但却能带来明显的性能提升。通过对SENet中通道注意模块的分析,我们实证表明避免降维对于学习通道注意非常重要,适当的跨通道交互可以在显著降低模型复杂度的同时保持性能。因此,我们提出了一种不降维的局部跨信道交互策略,该策略可以通过一维卷积有效实现。此外,我们发展了一种自适应选择一维卷积核大小的方法,确定局部跨通道交互的覆盖范围。提出的ECA模块是高效而有效的,例如,我们的模块对ResNet50骨干的参数和计算是80 vs. 24.37M, 4.7e-4 GFLOPs vs. 3 GFLOPs,在Top-1准确率方面,性能提升超过2%。我们以ResNets和MobileNetV2为骨干,对ECA模块在图像分类、目标检测和实例分割方面进行了广泛的评估。实验结果表明,该模块的性能优于同类模块,效率更高。
解读:提出了一种高效通道注意力模块,只有少数几个参数,但性能能很大提升。通过实验发现,不降维的通道注意很重要,本文也是通过一维卷积实现。
由于ECANet是基于SENet的扩展,所以文章先回顾了一下同为通道注意力机制的SENet。
在SENet中,输入特征首先主通道经过全局平均池化(形成1x1),然后经过两层全连接层(捕获非线性跨通道交互),最后经过Sigmoid激活函数后产生每一个通道的权重。
通过代码就知道了:
class SE_Block(nn.Module): # Squeeze-and-Excitation block def __init__(self, in_planes): super(SE_Block, self).__init__() self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) self.conv1 = nn.Conv2d(in_planes, in_planes // 16, kernel_size=1) self.relu = nn.ReLU() self.conv2 = nn.Conv2d(in_planes // 16, in_planes, kernel_size=1) self.sigmoid = nn.Sigmoid() def forward(self, x): x = self.avgpool(x) x = self.conv1(x) x = self.relu(x) x = self.conv2(x) out = self.sigmoid(x) return out
可以看到,中间是进行了维度的转变的。但是降维会给通道注意力预测带来副作用,降维虽然可以降低模型复杂度,但破坏了信道与其权值的直接对应关系,并且捕获所有通道之间的依存关系效率不高而且没有必要。例如,单个FC层使用所有信道的线性组合来预测每个信道的权值。但是首先将信道特征投影到低维空间,然后再映射回来,使得信道与其权重之间的对应是间接的。
同时作者表示,基于SENet的扩展大多都只致力于开发复杂的attention模型,这就无可避免地增加了模型的复杂性。本文的ECANet只涉及少量的参数,可以达到两个目的:(1)避免特征维度的缩减;(2)增加channel间信息的交互,在降低复杂度的同时保持性能(通过一维卷积)。
如图所示,ECA避免了维度缩减,并有效捕获了跨通道交互。在不降低维度的情况下进行逐通道全局平均池化后,我们的ECA通过考虑每个通道及其k个近邻来捕获本地跨通道交互。实践证明,这种方法可以保证效率和有效性。请注意,我们的ECA可以通过大小为k的快速一维卷积有效实现,其中内核大小k代表本地跨通道交互的覆盖范围,即有多少个相近邻参与一个通道的注意力预测。我们提出了一种自适应确定k的方法,其中交互作用的覆盖范围(即内核大小k)与通道维成比例。
具体看下代码吧。
# 定义ECANet的类 class eca_block(nn.Module): # 初始化, in_channel代表特征图的输入通道数, b和gama代表公式中的两个系数 def __init__(self, in_channel, b=1, gama=2): # 继承父类初始化 super(eca_block, self).__init__() # 根据输入通道数自适应调整卷积核大小 kernel_size = int(abs((math.log(in_channel, 2)+b)/gama)) # 如果卷积核大小是奇数,就使用它 if kernel_size % 2: kernel_size = kernel_size # 如果卷积核大小是偶数,就把它变成奇数 else: kernel_size = kernel_size + 1 # 卷积时,为例保证卷积前后的size不变,需要0填充的数量 padding = kernel_size // 2 # 全局平均池化,输出的特征图的宽高=1 self.avg_pool = nn.AdaptiveAvgPool2d(output_size=1) # 1D卷积,输入和输出通道数都=1,卷积核大小是自适应的 # 这个1维卷积需要好好了解一下机制,这是改进SENet的重要不同点 self.conv = nn.Conv1d(in_channels=1, out_channels=1, kernel_size=kernel_size, bias=False, padding=padding) # sigmoid激活函数,权值归一化 self.sigmoid = nn.Sigmoid() # 前向传播 def forward(self, inputs): # 获得输入图像的shape b, c, h, w = inputs.shape # 全局平均池化 [b,c,h,w]==>[b,c,1,1] x = self.avg_pool(inputs) # 维度调整,变成序列形式 [b,c,1,1]==>[b,1,c] x = x.view([b,1,c]) # 这是为了给一维卷积 # 1D卷积 [b,1,c]==>[b,1,c] x = self.conv(x) # 权值归一化 x = self.sigmoid(x) # 维度调整 [b,1,c]==>[b,c,1,1] x = x.view([b,c,1,1]) # 将输入特征图和通道权重相乘[b,c,h,w]*[b,c,1,1]==>[b,c,h,w] outputs = x * inputs return outputs
通过代码也可以看到,首先进行了一个全局平均池化,然后进行1x1的卷积和sigmoid激活函数,最后和原输入相乘。
自适应hernel_size:
注意,一维卷积的卷积核大小通过一个函数来自适应,使得channel数较大的层可以更多地进行cross channel 交互。自适应卷积核大小的计算公式为: k = ψ ( C ) = ∣ log 2 ( C ) γ + b γ ∣ \mathrm{k}=\psi(\mathrm{C})=\left|\frac{\log _{2}(\mathrm{C})}{\gamma}+\frac{\mathrm{b}}{\gamma}\right| k=ψ(C)= γlog2(C)+γb ,其中 γ = 2 {\gamma}=2 γ=2, b = 1 {b}=1 b=1。这里相当于是进行了一个维度转换,将C x 1 x 1变成了1 x C了,将channel放到了后面,而输入channel变成了1。
在本文中,我们专注于为模型复杂度较低的深层CNN学习有效的渠道关注度。 为此,我们提出了一种有效的通道注意力(ECA)模块,该模块通过快速的1D卷积生成通道注意力,其内核大小可以通过通道尺寸的非线性映射来自适应确定。 实验结果表明,我们的ECA是一种极其轻巧的即插即用模块,可提高各种深度CNN架构的性能,包括广泛使用的ResNets和轻巧的MobileNetV2。
ECANet:通道注意力的超强改进
【论文阅读笔记】【CVPR2020】ECA-Net: Efficient Channel Attention for Deep Convolutional Neural Networks
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。