近期 关于移动网络设计的研究已经证明了 通道注意力 在提升模型性能方面的显著效果,但他们通常忽略了位置信息,而位置信息嵌入通道注意力中,我们称之为**“坐标注意力”。与通道注意力通过二维全局池化将一个特征张量转换为单个特征向量的方法不同,坐标注意力将通道注意力分解为两个一维特征编码过程,分别沿着两个空间方向聚合特征。通过这种方式,可以捕捉到一个空间方向上的长程依赖关系,并同时保留另一个空间方向上的精确位置信息。然后,得到的特征图分别编码为一对方向感知和位置敏感的注意力图,可以互补地应用于输入特征图**,以增强感兴趣对象的表示。我们的坐标注意力简单,并且可以灵活地插入到经典的移动网络中,如MobileNetv2、MobileNeXt和EfficientNet,几乎没有计算开销。大量的实验证明,我们的坐标注意力不仅对ImageNet分类有益,而且更加有趣的是,在目标检测和语义分割等下游任务中表现更好。
论文地址:https://arxiv.org/pdf/2103.02907.pdf
注意力机制被用于指导模型**“what”和“where”进行关注,已经得到广泛的研究,并且在现代深度神经网络的性能中被广泛应用。然而,它们在移动网络(具有有限的模型大小)中的应用远远落后于大型网络。这主要是因为大多数注意力机制带来的计算开销对于移动网络来说是无法承受的。
考虑到移动网络的计算能力受限,迄今为止,最受欢迎的移动网络注意力机制仍然是SE注意力,该机制通过2D全局池化来计算通道注意力,并在相对较低的计算成本下提供显著的性能提升,然而,SE注意力仅考虑编码的通道间信息,忽视了位置的重要性,而位置信息对于捕捉视觉任务中的对象结构至关重要。后续的工作:如BAM和CBAM,尝试通过减少张量的通道维度,然后使用卷积计算空间注意力来利用位置信息,然而,卷积只能捕捉局部关系,对于视觉任务中的长程依赖建模效果不佳[48,14]。
在本文中,除了前面的研究成果,我们提出了一种新颖且高效的注意力机制,将位置信息嵌入通道注意力中**,使移动网络能够关注较大区域而避免产生显著的计算开销。为了解决2D全局池化引起的位置信息丢失问题,我们将通道注意力分解为两个平行的1D特征编码过程,以将空间坐标信息有效地整合到生成的注意力图中。
利用两个1D全局池化操作分别沿垂直和水平方向聚合输入特征,形成两个单独的方向感知特征图。这两个嵌入的方向特定信息的特征图分别编码为两个注意力图,每个图在一个空间方向上捕捉输入特征图的长程依赖关系,从而保留位置信息。然后,将这两个注意力图通过乘法应用于输入特征图,以强调感兴趣的表示。它区分了空间方向,并生成了具有坐标感知能力的注意力图。
在本节中,我们对文本进行了简要的文献综述,包括有关高效网络架构设计和注意力或非局部建模的先前研究。
最近的最先进移动网络大多基于深度可分离和残差倒置。
HBONet在每个倒置残差块内部引入了下采样操作,以建模代表性的空间信息。
ShuffleNetV2在倒置残差块之前和之后使用了通道分离模块和通道随机模块。
MobileNetV3结合了神经架构搜索算法,来搜索不同深度的倒置残差块的最佳激活函数和扩展系统,此外,MixNet、EfficientNet和ProxylessNAS也采用了不同的搜索策略,来搜索深度可分离卷积的最佳核大小或用于控制网络权重的标量,这些权重与扩展系数、输入分辨率、网络深度和宽度有关。
最近,Zhou等人对利用深度可分离卷积的方法进行了重新思考,提出了MobileNeXt,该网络采用了经典的瓶颈结构用于移动网络
注意力机制在各种计算机视觉任务中被证明是有帮助的,例如,图像分类和图像分割。其中一个成功的例子是SENet,它简单的将每个二维特征图压缩以有效的建立通道之间的相互依赖关系,CBAM通过引入具有大型卷积核的卷积来进一步推进这个思想,从而引入了空间信息编码。后来的工作,如GENet、GALA、AA和TA通过采用不同的空间注意力机制或设计先进的注意力块扩展这个思想。
近年来,非局部或自注意力网络因其建立空间或通道注意力的能力而变得非常流行,典型的例子:NLNet、GCNet等他们都是利用非局部机制来捕捉不同类型的空间信息。然而,由于自注意力模块内部的大量计算,他们通常被用于大型模型,而不适合移动网络。
与利用昂贵且沉重的非局部或自注意力块的这些方法不同,我们的方法考虑了一种更有效的方法来捕捉位置信息和通道关系,以增强移动网络的特征表示,通过将二维全局池化操作分解为两个一位编码过程,我们的方法在轻量级性质(如SENet、CBAM、TA)上表现比其他注意力方法性能更好。
一个坐标注意力可以被视为一个计算单元,旨在增强移动网络学习特征的表达能力。
正如[18]中所示,标准卷积本身很难建模通道之间的关系,显式的构建通道间的依赖关系可以增加模型对有信息贡献的通道的敏感性,进而提高最终分类决策的准确性,此外,使用全局平均池化可以帮助模型捕捉全局信息,而这是卷积缺乏的。
从结构上看,SE块可以分解为两个步骤:挤压和激励,分别用于全局信息嵌入和自适应地重新校准通道之间的关系。
给定输入X,对于第c个通道的Squeeze可以表述如下:
其中,Zc是与第c个通道相关联的输出,输入X直接来自具有固定卷积核大小的卷积层,因此可以看作是一组局部描述符。Squeeze使得收集全局信息称为可能。
其中**·表示逐通道乘法,
SE块在最近的移动网络中被广泛使用,并被证明是实现最新进性能的关键组件。然而,它仅通过建模通道关系来重新评估每个通道的重要性**,但是 忽视了位置信息
坐标注意力模块通过两个步骤编码通道关系和远程依赖关系,并且提供准确的位置信息:坐标信息嵌入和坐标注意力生成
Global Pooling 通常用于通道注意力中,以全局方式编码空间信息,但它将全局空间信息压缩到通道描述符中,因此很难保留位置信息,在捕捉视觉任务中的空间结构方面是至关重要的,为了鼓励注意力模块以精确的位置信息在空间上捕捉长距离交互,将全局池化分解,如下公式
上述两个转换分别沿着两个空间方向聚合特征,得到一对方向感知的特征图。这与通道注意力方法中产生单个特征向量的压缩操作非常不同。这两个转换还使我们的注意力模块能够沿着一个空间方向捕捉长距离依赖关系,并沿另一个空间方向保留精确的位置信息,从而帮助网络更准确地定位感兴趣对象。
如上所述,方程(4)(5)实现了全局感受野并编码了精确的位置信息。为了充分利用得到的表达性表示,我们提出了第二次转换,称为坐标注意力的生成。我们的设计参考了以下三个标准:
首先:描述实验设置,然后设置消融实验
然后:与一些基本注意力机制进行了比较
训练过程中,使用标准的SGD优化器,衰减和动量都设置为0.9来训练所有模型。权重衰减始终设为4X10-5,采用余弦学习调度,初始学习率为0.05。使用4个NVIDIA GPU来进行训练,批量大小设置为256。在没有额外声明的情况下,我们将MobileNetV2作为 基准线,将所有模型训练200个epoch。在数据增强方面,我们采用了与MobileNetv2相同的方法,我们在ImageNet数据集上进行分类。
为了展示提出的坐标注意力的性能,我们进行了一些列消融实验,相应结果均在下表:
我们分别移除水平注意力或垂直注意力,以观察编码坐标信息的重要性。沿任意一个方向进行注意力的模型与具有SE注意力的模型具有相媲美的性能。然而,当水平注意力和垂直注意力同时加入时,获取了突出显示的最佳结果。
以两个经典的移动网络(包括具有方向残差块的MobileNetV2和具有沙漏瓶颈块的MobileNext)作为基准,来比较所提出的方法在不同权重乘数下与SE注意力和CBAM的性能。在这个实验中,我们采用了三种典型的权重乘数,包括{1.0,0.75,0.5}。
在以MobileNetV2网络为基准时,带有CBAM的模型与带有SE注意力的模型具有类似的结果,然而,在每种设置下,具有我们提出的坐标注意力的模型都取得了最好的结果。
当使用MobileNext网络时,也可以观察到类似的现象。这表明,无论考虑沙漏瓶颈块还是反向残差块,无论选择哪种权重乘数,坐标注意力都能够以先进的方式同时编码位置和通道间的信息,因此表现最佳。
为了探究不同注意力块减少比例对模型性能的影响,我们尝试减小比例的大小,并观察性能的变化。当将r减少到原始大小的一半时,模型的大小增加了,但是性能得到了提升。这表明:通过减少比例来增加更多的参数对于提高模型性能很重要,更重要的是,我们的坐标注意力在这个实验中仍优于SE注意力和CBAM,这反映了所提出的坐标注意力对于减少比例的鲁棒性。
将坐标注意力模块与其他轻量级的注意力方法进行比较,包括广泛采用的SE和CBAM,如表二所示,可以看出,添加SE注意力已经提高了分类性能1%,对于CBAM,似乎 其空间注意力模块在移动网络中并没有像SE那样发挥作用。然而,当考虑到我们提出的坐标注意力时,我们获得了最好的结果,我们还通过图4可视化了我们具有不同注意力方法 的模型生成的特征图。显然,坐标注意力可以更好地帮助定位感兴趣的对象,而不像SE注意力和CBAM那样。
与CBAM相比,所提出的位置信息编码方式的优势有两个:
# CA
class h_sigmoid(nn.Module):
def __init__(self, inplace=True):
super(h_sigmoid, self).__init__()
self.relu = nn.ReLU6(inplace=inplace)
def forward(self, x):
return self.relu(x + 3) / 6
class h_swish(nn.Module):
def __init__(self, inplace=True):
super(h_swish, self).__init__()
self.sigmoid = h_sigmoid(inplace=inplace)
def forward(self, x):
return x * self.sigmoid(x)
class CoordAtt(nn.Module):
def __init__(self, inp, oup, reduction=32):
super(CoordAtt, self).__init__()
self.pool_h = nn.AdaptiveAvgPool2d((None, 1))
self.pool_w = nn.AdaptiveAvgPool2d((1, None))
mip = max(8, inp // reduction)
self.conv1 = nn.Conv2d(inp, mip, kernel_size=1, stride=1, padding=0)
self.bn1 = nn.BatchNorm2d(mip)
self.act = h_swish()
self.conv_h = nn.Conv2d(mip, oup, kernel_size=1, stride=1, padding=0)
self.conv_w = nn.Conv2d(mip, oup, kernel_size=1, stride=1, padding=0)
def forward(self, x):
identity = x
n, c, h, w = x.size()
x_h = self.pool_h(x)
x_w = self.pool_w(x).permute(0, 1, 3, 2)
y = torch.cat([x_h, x_w], dim=2)
y = self.conv1(y)
y = self.bn1(y)
y = self.act(y)
x_h, x_w = torch.split(y, [h, w], dim=2)
x_w = x_w.permute(0, 1, 3, 2)
a_h = self.conv_h(x_h).sigmoid()
a_w = self.conv_w(x_w).sigmoid()
out = identity * a_w * a_h
return out