1.简介
2021年11月16日,清华大学计图团队和南开大学程明明教授团队、卡迪夫大学Ralph R. Martin教授合作,在ArXiv上发布关于计算机视觉中的注意力机制的综述文章[1]。该综述系统地介绍了注意力机制在计算机视觉领域中相关工作,并创建了一个仓库
https://github.com/MenghaoGuo/Awesome-Vision-Attentions
专门用于收集注意力机制的相关论文。
该综述论文的第一作者是胡事民教授的博士生国孟昊。他也是计图团队发布的点云Transformer(PCT)一文的第一作者;其他作者还包括清华大学张松海副教授、穆太江博士,以及清华和南开的多名博士生。
如今注意力机制已经成功的运用在了许多的视觉任务当中,包括图像分类、目标检测、语义分割、视频理解、图像生成、3D视觉、多模态任务、自监督学习等。在这篇综述中,对计算机视觉中许多不同的注意力机制进行了总结,像通道注意力、空间注意力、时间注意力和分支注意力及他们的组合。
将注意力转移到一张图片最重要的地方从而忽视一些不重要的部分的方法叫做注意力机制。
注意力机制的发展过程大致分为四个阶段,
第一个阶段是从RAM开始的,它将深度神经网络与注意力机制结合起来,它循环的预测重要的区域并且更新整个网络通过策略梯度以端到端的方法。RNNs是这种方法的重要文章。
第二个阶段是Jaderberg提出STN,介绍了一个子网络去预测一个仿射变换去选择输入中重要的区域。显式的预测输入特征的差异是第二阶段的主要特征。DCNs是这一阶段的代表性工作。
第三阶段开始于SENet,它提出了一个新型的channel-attention网络。隐式、自适应的预测了潜在的关键特征。CBAM和ECANet在这个阶段是代表性工作。
最后一个阶段是自注意力的时代,自注意力机制首先是通过‘Attenion is all you need’提出的并且在NLP领域迅速取得了巨大的进展。Wang首先将自注意力引入到了计算机视觉领域中,并提出了一个新型的网络non-local network,并且在视频理解和目标检测中取得了巨大成功。紧跟着一系列网络比如EMANet、CCNet、HamNet和Stand-Alone Network,这些网络都改善了速度、精度和泛化能力。现在,不同的纯自注意力网络(ViT)开始出现,展现了基于注意力的模型的巨大潜力。在计算机视觉领域,基于注意力的网络取代卷积网络成为更强大和通用的架构的潜力已经越来越明显了。
这篇文章通过data domain方式将现有的注意力方法分为了六类,包括四个基础类别: channel attention (what to pay attention to ), spatial attention (where to pay attention), temporal attention (when to pay attention) and branch channel (which to pay attention to), 和两个混合类别: channel & spatial attention and spatial & temporal attention.
这篇文章的主要贡献是:
2.计算机视觉中的注意力方法
其中g(x)表示产生的注意力,f (g(x), x)表示根据g(x)产生的注意力来处理输入图片后得到的结果。根据上述的公式,几乎所有现存的注意力机制都可以用这个公式来描述。文章列举了self-attention的Non-Local和spatial attention的SENet。
Non-Local可以写成:
SENet可以写成:
2.1 channel attention
2.1.1 SENet
SENet开创了通道注意力的先河。SENet的核心是一个压缩和激励(SE)块,用于收集全局信息、捕获通道关系和提高表示能力。
SE模块分为两部分,挤压模块和激励模块。挤压模块通过全局平均池化收集全局空间信息。激励模块通过使用全连接层和非线性层(ReLU和sigmoid)捕捉通道关系并输出注意力向量。然后,通过乘以注意向量中的相应元素来缩放输入特征的每个通道。总的来说,以X为输入,以Y为输出的压缩和激励块Fse(参数θ)可以表示为:
SE模块在抑制噪声的同时,起着强调重要通道的作用。由于SE块的计算资源需求较低,因此可以在每个残差单元之后添加SE块。然而,SE块也有缺点。在挤压模块中,全局平均池过于简单,无法捕获复杂的全局信息。在激励模块中,全连接层增加了模型的复杂性。后续工作试图改进挤压模块的输出(例如GSoP网络),通过改进励磁模块(例如ECANet)降低模型的复杂性,或者同时改进挤压模块和励磁模块(例如SRM)。
2.1.2 GSoP-Net
SE块仅通过使用全局平均池化(即一阶统计量)来捕获全局信息,这限制了其建模能力,尤其是捕获高阶特征的能力。 为了解决这个问题,Gao等人提出通过使用全局二阶池化(GSoP)块在收集全局信息的同时对高阶特征数据建模来改进squeeze模块。
与SE块一样,GSoP块也有一个挤压模块和一个激励模块。在压缩模块中,GSoP块首先使用1x1卷积将通道数从c 减少到c′(c ′< c ),然后计算不同通道的c×c协方差矩阵,以获得它们的相关性。接下来,对协方差矩阵执行逐行归一化。归一化协方差矩阵中的每个(i,j)明确地将通道i与通道j相关联。
在激励模块中,GSoP块执行逐点卷积以保持结构信息并输出向量。然后利用全连接层和sigmoid函数得到c维注意向量。最后,它将输入特征乘以注意向量,就像在SE块中一样。GSoP块可以表示为:
squeeze模块:
1×1卷积(Conv)将通道维度从[C,H,W]->[C′,H,W],(C′<C)
协方差矩阵(Cov)C ′ × C ′ ,计算各通道间的相关性
接下来,对协方差矩阵执行逐行归一化。归一化协方差矩阵中的每个( i , j )表示信道i与信道j相关联
excitation模块:
1.行卷积(RC),RC(·)表示逐点卷积以保持结构信息并输出向量
2. 利用全连接层(W)和sigmoid函数(σ)得到C维注意力向量(通道权重)
将得到的结果和原特征图相乘,为每一个通道给不同的权重
通过使用全局二阶池化(GSoP),GSoPBlock提高了通过SEBlock收集全局信息的能力。然而,这是以额外计算为代价的。因此,通常在几个剩余块之后添加单个GSoPBlock。
2.1.3 SRM
在transfer成功的推动下,Lee等人提出了基于轻量级风格的再校准模块(SRM)。SRM将风格转换与注意力机制结合起来。它的主要贡献是style pooling,它利用输入特征的均值和标准差来提高捕获全局信息的能力。为了降低计算层(CFC)的完全连接要求,在全连接的地方也采用了CFC。
动机:以风格迁移的成功为动机,即提升精度的同时,减少计算量,提出了新的squeeze模块和轻量级全连接层。
squeeze模块:使用style pooling(SP),它结合了全局平均池化和全局标准差池化。(输出为C × d :当只用全局平均池化就是C × 1 ;当用了全局平均池化和全局标准差池化就是C × 2 ;当用了全局平均池化和全局标准差池化和全局最大池化就是C × 3
excitation模块:与通道等宽的全连接层CFC(Channel-wise fully-connected layer) ,含义:通道维度由[ C , d ]变为[ C , 1 ],即对于每一个通道,都有一个全连接层输入为d,输出为1(原文:This operation can be viewed as a channel-independent, fully connected layer with d input nodes and a single output)
利用BN层和sigmoid函数(σ)得到C维注意力向量
class SRMLayer(nn.Module):
def __init__(self, channel):
super(SRMLayer, self).__init__()
self.cfc = Parameter(torch.Tensor(channel, 2))
self.cfc.data.fill_(0)
self.bn = nn.BatchNorm2d(channel)
self.activation = nn.Sigmoid()
setattr(self.cfc, 'srm_param', True)
setattr(self.bn.weight, 'srm_param', True)
setattr(self.bn.bias, 'srm_param', True)
def _style_pooling(self, x, eps=1e-5):
N, C, _, _ = x.size()
channel_mean = x.view(N, C, -1).mean(dim=2, keepdim=True)
channel_var = x.view(N, C, -1).var(dim=2, keepdim=True) + eps
channel_std = channel_var.sqrt()
t = torch.cat((channel_mean, channel_std), dim=2)
return t
def _style_integration(self, t):
z = t * self.cfc[None, :, :] # B x C x 2
z = torch.sum(z, dim=2)[:, :, None, None] # B x C x 1 x 1
z_hat = self.bn(z)
g = self.activation(z_hat)
return g
def forward(self, x):
# B x C x 2
t = self._style_pooling(x)
# B x C x 1 x 1
g = self._style_integration(t)
return x * g
2.1.4 GCT
由于激励模块中全连接层的计算需求和参数数量,在每个卷积层之后使用SE块是不切实际的。此外,使用全连接的层来建模通道关系是一个隐式过程。为了克服上述问题,Yang等人提出了门控通道转换(GCT),以有效地收集信息,同时显式地建模通道关系。
与以前的方法不同,GCT首先通过计算每个通道的l2范数来收集全局信息。接下来,应用可学习向量α来缩放特征。然后通过通道规范化引入竞争机制,实现通道之间的互动。与其他常见的归一化方法一样,可学习的尺度参数γ和偏差β用于重新缩放归一化。然而,与之前的注意力向量激活方法不同,tanh采用的是注意力向量激活方法。最后,它不仅增加了输入注意向量,但也增加了身份映射。GCT可以写成:
L2-normalization(Norm),对输入特征图Norm,通道数从C , H , W -->[ C , 1 , 1 ] ,乘以可训练权重α,输出结果作为第二部分的输入用sin表示
channel normalization(CN),对应图中中间部分,具体操作为
乘以可训练权重γ 和偏置β ,输出结果用s ′ 表示
GCT block的参数比SE block少,而且由于它很轻量,可以添加到CNN的每个卷积层之后。
def forward(self, x, epsilon=1e-5):
# x: input features with shape [N,C,H,W]
# alpha, gamma, beta: embedding weight, gating weight,
# gating bias with shape [1,C,1,1]
embedding = (x.pow(2).sum((2,3), keepdim=True)
+ epsilon).pow(0.5) * self.alpha
norm = self.gamma / (embedding.pow(2).mean(dim=1,
keepdim=True) + epsilon).pow(0.5)
gate = 1. + torch.tanh(embedding * norm + self.beta)
return x * gate
2.1.5 ECANet
为了避免高模型复杂度,SENet减少了通道的数量。然而,这种策略无法直接建模权重向量和输入之间的对应关系,从而降低了结果的质量。为了克服这一缺点,Wang等人提出了高效通道注意(ECA)块,该块使用一维卷积来确定通道之间的相互作用,而不是降维。
ECA块具有与SE块类似的公式,包括用于聚合全局空间信息的挤压模块和用于建模跨通道交互的有效激励模块。ECA块只考虑每个通道与其k近邻之间的直接交互,而不是间接对应,以控制模型复杂性。总体而言,ECA区块的公式如下:
Conv1D(·)表示1维卷积,在通道域中具有形状为k的核,以模拟局部交叉通道交互。参数k决定交互的覆盖范围,在ECA中,内核大小k通过通道维度C自适应确定,而不是通过手动调整,使用交叉验证:
注:文中对卷积核大小有自适应算法,即根据通道的长度,调整卷积核k的大小。原论文给出超参数γ = 2,b = 1。∣∣odd表示k只能取奇整数
与SENet相比,ECANet有一个改进的激励模块,并提供了一个高效、有效的模块,可以很容易地集成到各种CNN中。
import torch
from torch import nn
from torch.nn.parameter import Parameter
class eca_layer(nn.Module):
"""Constructs a ECA module.
Args:
channel: Number of channels of the input feature map
k_size: Adaptive selection of kernel size
"""
def __init__(self, channel, k_size=3):
super(eca_layer, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.conv = nn.Conv1d(1, 1, kernel_size=k_size, padding=(k_size - 1) // 2, bias=False)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
# x: input features with shape [b, c, h, w]
b, c, h, w = x.size()
# feature descriptor on the global spatial information
y = self.avg_pool(x)
# Two different branches of ECA module
y = self.conv(y.squeeze(-1).transpose(-1, -2)).transpose(-1, -2).unsqueeze(-1)
# Multi-scale information fusion
y = self.sigmoid(y)
return x * y.expand_as(x)
2.1.6 FcaNet
在挤压模块中仅使用全局平均池化限制了表达能力。为了获得更强大的表示能力,秦等人重新思考了从压缩角度捕获的全局信息,并分析了频域中的全局平均池化。他们证明了全局平均池化是离散余弦变换(DCT)的一个特例,并利用这一观察结果提出了一种新的多光谱通道注意力
给定一个输入特征映射X ∈ R(C×H×W),多光谱通道注意力首先将X分成许多部分xi∈ R(C0×H×W) 。然后对每个部分应用2D DCT。请注意,2D DCT可以使用预处理结果来减少计算量。处理后,所有结果都被连接到一个向量中。最后,使用全连接层、ReLU激活和sigmoid来获得SE块中的注意向量。这可以表述为:
将输入特征图x ∈ R(C × H × W)分解(Group)为许多部分xi ∈ R (C i × H × W),每一段长度相等
对每一段xi 应用2D 离散余弦变换(DCT, discrete cosine transform)。2D DCT可以使用预处理结果来减少计算
在处理完每个部分后,所有结果都被连接到一个向量中
后接全连接层(W 1)->ReLU层(δ)->全连接层(W 2)->Sigmoid(σ)
将得到的结果和原特征图相乘,为每一个通道给不同的权重
class FCABlock(nn.Module):
"""
FcaNet: Frequency Channel Attention Networks
https://arxiv.org/pdf/2012.11879.pdf
"""
def __init__(self, channel, reduction=16, dct_weight=None):
super(FCABlock, self).__init__()
mid_channel = channel // reduction
self.dct_weight = dct_weight
self.excitation = nn.Sequential(
nn.Linear(channel, mid_channel, bias=False),
nn.ReLU(inplace=True),
nn.Linear(mid_channel, channel, bias=False),
nn.Sigmoid()
)
def forward(self, x):
b, c, _, _ = x.size()
y = torch.sum(x*self.dct_weight, dim=[2,3])
z = self.excitation(y).view(b, c, 1, 1)
return x * z.expand_as(x)
2.1.7 EncNet
动机:受SENet的启发,提出了上下文编码模块(CEM, context encoding module),该模块结合了语义编码损失(SE-loss, semantic encoding loss),以建模场景上下文和对象类别概率之间的关系,从而利用全局场景上下文信息进行语义分割。
给定一个输入特征映射,CEM首先在训练阶段学习K个聚类中心D,D = { d 1 , . . . , d K }和一组平滑因子S,S = { s 1 , . . . , s K }。接下来,它使用软分配权重对输入中的局部描述子和相应的聚类中心之间的差异进行求和,以获得置换不变描述子。然后,为了提高计算效率,它将聚合应用于K个簇中心的描述符,而不是级联。形式上,CEM可以写成如上公式。
2.1.8 channel attention block 总结
版权归原作者 Orange_sparkle 所有, 如有侵权,请联系我们删除。