点击上方“ AI算法修炼营 ”,选择加星标或“置顶”
标题以下,全是干货
前面的文章中,我们关注了Non-local网络模块和 视觉注意力机制在分类网络中的应用 —— SENet、SKNet、CBAM等。 他们构成了视觉注意力机制中的基本模块,本节中,我们将主要介绍融合Non-local模块和SENet模块的全局上下文建模网络(Global Context Network,简称 GCNet),以及Non-local模块与CBAM模块融合变形在语义分割中的应用——双重注意力网络DANet。
前面的文章有详细讨论Non-local模块,Non-local Network(NLNet) 使用自注意力机制来建模远程依赖 。对于每个查询点(query position),NLNet 首先 计算查询点与所有点之间的成对关系 以得到注意力图,然后通过 加权和的方式聚合 所有点的特征,从而得到与此查询点相关的 全局特征 ,最终再分别将全局特征加到每个查询点的特征中,完成远程依赖的建模过程。但是,NLNet存在着 计算量大 的问题。
SENet模块中提出的SE block是使用全局上下文对不同通道进行权值重标定,对通道依赖进行调整。但是采用这种方法,并 没有充分利用全局上下文信息。
CBAM将注意力过程分为两个独立的部分, 通道注意力模块(look what) 和 空间注意力模块(look where) 。不仅可以节约参数和计算力,而且保证了其可以作为即插即用的模块集成到现有的网络架构中去。但是CBAM需要手工设计pooling,多层感知器等复杂操作。
下面,将主要介绍 GCNet和DANet 。
论文地址:https://arxiv.org/abs/1904.11492v1
代码地址:https://github.com/xvjiarui/GCNet
为了捕获长距离依赖关系,产生了 两类方法 :
- 采用 自注意力机制 来建模query对的关系。
- 对query-independent(可以理解为无query依赖)的 全局上下文 建模。
NLNet就是采用自注意力机制来建模像素对关系。然而NLNet 对于每一个位置学习不受位置依赖的attention map,造成了大量的计算浪费。
SENet用 全局上下文对不同通道进行权值重标定 ,来调整通道依赖。然而,采用权值重标定的特征融合,不能充分利用全局上下文。
GCNet作者对NLNet进行试验,选择COCO数据集中的6幅图,对于 不同的查询点(query point) 分别对Attention maps进行可视化,得到以下结果:
可以看出,对于不同的查询点,其attention map是几乎一致的,这说明NLNet学习到的是 独立于查询的依赖(query-independent dependency) ,这说明虽然NLNet想要对每一个位置进行特定的全局上下文计算,但是可视化结果以及实验数据证明, non-local network的全局上下文在不同位置几乎是相同的,这表明学习到了无位置依赖的全局上下文。
基于以上发现,作者希望能够减少不必要的计算量,降低计算,并结合SENet设计,提出了GCNet融合了两者的优点, 既能够有用NLNet的全局上下文建模能力,又能够像SENet一样轻量 。
简化的Non-local模块
作者 通过计算一个全局的attention map来简化non-local block ,并且 对所有位置共享 这个全局attention map。忽略
,简化版的non-local block定义为:
和
表示为线性转换矩阵。
为了进一步减少简化版non-local block的计算量,
将
移到attention pooling的外面
,表示为:
这样修改之后,1x1卷积
的FLOPs从
降到了
。
不同于原始的non-local block,简化版non-local block的 第二项是不受位置依赖的,所有位置共享这一项。 因此,作者直接 将全局上下文建模为所有位置特征的加权平均值,然后聚集全局上下文特征到每个位置的特征上 。
简化版的non-local block可以抽象为3个步骤:
- 全局attention pooling
:采用1x1卷积
和softmax函数来获取attention权值,然后执行attention pooling来获得全局上下文特征。
- 特征转换
:采用1x1卷积
。
- 特征聚合 :采用相加操作将全局上下文特征聚合到每个位置的特征上。
SE模块
前面讲过,SE block如下图所示,也可以抽象成3个步骤:
- 全局平均池化 用于上下文建模(即squeeze operation)。
- bottleneck transform 用于计算每个通道的重要程度(即excitation operation)。
- rescaling function 用于通道特征重标定(即element-wise multiplication)。
GCNet模块
作者提出了一种新的 全局上下文建模框架 ,global context block(简写GCNet), 即能够像Non-local block一样建立有效的长距离依赖,又能够像SE block一样省计算量。
GC block的3个步骤为:
- global attention pooling用于 上下文建模 。
- bottleneck transform来 捕获通道间依赖 。
- broadcast element-wise addition用于 特征融合 。
在简化版的non-local block中,transform模块有大量的参数。为了获得SE block轻量的优点, 1x1卷积用bottleneck transform模块来取代 ,能够显著的降低参数量(其中r是降低率)。因为两层bottleneck transform增加了优化难度,所以 在ReLU前面增加一个layer normalization层(降低优化难度且作为正则提高了泛化性)。
GC block在ResNet中的使用位置是每两个Stage之间的连接部分,下边是GC block的官方实现(基于mmdetection进行修改):
import torch
from torch import nn
class ContextBlock(nn.Module):
def \_\_init\_\_(self,inplanes,ratio,pooling\_type='att',
fusion\_types=('channel\_add', )):
super(ContextBlock, self).__init__()
valid_fusion_types = ['channel\_add', 'channel\_mul']
assert pooling_type in ['avg', 'att']
assert isinstance(fusion_types, (list, tuple))
assert all([f in valid_fusion_types for f in fusion_types])
assert len(fusion_types) > 0, 'at least one fusion should be used'
self.inplanes = inplanes
self.ratio = ratio
self.planes = int(inplanes * ratio)
self.pooling_type = pooling_type
self.fusion_types = fusion_types
if pooling_type == 'att':
self.conv_mask = nn.Conv2d(inplanes, 1, kernel_size=1)
self.softmax = nn.Softmax(dim=2)
else:
self.avg_pool = nn.AdaptiveAvgPool2d(1)
if 'channel\_add' in fusion_types:
self.channel_add_conv = nn.Sequential(
nn.Conv2d(self.inplanes, self.planes, kernel_size=1),
nn.LayerNorm([self.planes, 1, 1]),
nn.ReLU(inplace=True), # yapf: disable
nn.Conv2d(self.planes, self.inplanes, kernel_size=1))
else:
self.channel_add_conv = None
if 'channel\_mul' in fusion_types:
self.channel_mul_conv = nn.Sequential(
nn.Conv2d(self.inplanes, self.planes, kernel_size=1),
nn.LayerNorm([self.planes, 1, 1]),
nn.ReLU(inplace=True), # yapf: disable
nn.Conv2d(self.planes, self.inplanes, kernel_size=1))
else:
self.channel_mul_conv = None
def spatial\_pool(self, x):
batch, channel, height, width = x.size()
if self.pooling_type == 'att':
input_x = x
# [N, C, H * W]
input_x = input_x.view(batch, channel, height * width)
# [N, 1, C, H * W]
input_x = input_x.unsqueeze(1)
# [N, 1, H, W]
context_mask = self.conv_mask(x)
# [N, 1, H * W]
context_mask = context_mask.view(batch, 1, height * width)
# [N, 1, H * W]
context_mask = self.softmax(context_mask)
# [N, 1, H * W, 1]
context_mask = context_mask.unsqueeze(-1)
# [N, 1, C, 1]
context = torch.matmul(input_x, context_mask)
# [N, C, 1, 1]
context = context.view(batch, channel, 1, 1)
else:
# [N, C, 1, 1]
context = self.avg_pool(x)
return context
def forward(self, x):
# [N, C, 1, 1]
context = self.spatial_pool(x)
out = x
if self.channel_mul_conv is not None:
# [N, C, 1, 1]
channel_mul_term = torch.sigmoid(self.channel_mul_conv(context))
out = out * channel_mul_term
if self.channel_add_conv is not None:
# [N, C, 1, 1]
channel_add_term = self.channel_add_conv(context)
out = out + channel_add_term
return out
if __name__ == "\_\_main\_\_":
in_tensor = torch.ones((12, 64, 128, 128))
cb = ContextBlock(inplanes=64, ratio=1./16.,pooling_type='att')
out_tensor = cb(in_tensor)
print(in_tensor.shape)
print(out_tensor.shape)
论文地址:https://arxiv.org/pdf/1809.02983.pdf
代码地址:https://github.com/junfu1115/DANet
DANet 是一种经典的应用self-Attention的网络,它引入了一种 自注意力机制来分别捕获空间维度和通道维度中的特征依赖关系。
场景分割需要预测出图像中的像素点属于某一目标类或场景类,其图像场景的复杂多样(光照,视角,尺度,遮挡等)对于场景的理解和像素点的判别造成很大困难。
主流场景分割方法大致可分为以下两种类型:一是通过 使用多尺度特征融合的方式增强特别的表达 ,例如空间金字塔结构 (PSP,ASPP) 或者高层浅层特征融合 (RefineNet)。但是这些方式 没有考虑到不同特征之间的关联依赖, 而这对于场景的理解确实十分重要。另一是利用 RNN 网络构建特征长范围的特征关联,但这种关联往往受限于 RNN 的 long-term memorization。
双重注意网络(DANet)来 自适应地集成局部特征和全局依赖 。在传统的扩张FCN之上附加两种类型的注意力模块,分别模拟空间和通道维度中的语义相互依赖性。
从其结构图中可以看到,它由两个并列的attention module组成,第一个得到的是特征图中任意两个位置的依赖关系,称为Position Attention Module(PAM);第二个是任意两个通道间的依赖关系,称为Channel Attention Module(CAM)。
从其具体的模块中来看, PAM中的attention_map的大小为B×(W×H)×(W×H),而CAM中的attention_map大小为B×C×C,这就是PAM与CAM的区别 ,他们所代表的一个是任意两个位置之间的依赖关系,一个代表的是任意两个通道之间的依赖关系。
-
位置注意力模块(PAM)通过所有位置处的特征的加权和来 选择性地聚合每个位置的特征 。无论距离如何,类似的特征都将彼此相关。
-
通道注意力模块(CAM)通过整合所有通道映射之间的相关特征来 选择性地强调存在相互依赖的通道映射。
-
将两个注意模块的输出 相加 以进一步改进特征表示,这有助于更精确的分割结果。
位置注意力模块(PAM)
问题: 传统FCNs生成的特征会导致对物体的错误分类。
解决: 引入位置注意模块 在局部特征上建立丰富的上下文关系, 将更广泛的上下文信息编码为局部特征,进而增强他们的表示能力。
位置注意力模块 旨在利用任意两点特征之间的关联,来相互增强各自特征的表达 。
具体来说,首先 计算出任意两点特征之间关联强度矩阵 ,即原始特征 A 经过卷积降维获得特征 B 和特征 C,然后改变特征维度 B 为 ((HxW)xC') 和 C 为 (C'x(HxW)) 然后矩阵乘积获得任意两点特征之间的 关联强度矩 ((HxW)x(HxW))。然后 经过 softmax 操作归一化获得每个位置对其他位置的 attention 图 S, 其中越相似的两点特征之间,其响应值越大。接着将 attention 图中响应值作为 加权 对特征 D 进行加权融合,这样对于各个位置的点,其通过 attention 图在全局空间中的融合相似特征。
通道注意力模块(CAM)
问题: 每个high level特征的通道图都可以看作是一个特定于类的响应,通过挖掘通道图之间的相互依赖关系,可以突出相互依赖的特征图,提高特定语义的特征表示。
解决: 建立一个通道注意力模块来显式地建模通道之间的依赖关系。
通道注意力模块旨 在通过建模通道之间的关联,增强通道下特定语义响应能力。
具体过程与位置注意力模块相似,不同的是在获得特征注意力图 X 时,是 将任意两个通道特征进行维度变换和矩阵乘积 ,获得任意两个 通道 的关联强度,然后同样经过 softmax 操作获得的通道间的 attention 图。最后通过通道之间的 attention 图加权进行融合,使得各个通道之间能产生全局的关联,获得更强的语义响应的特征。
为了进一步获得全局依赖关系的特征,将两个模块的输出结果进行 相加融合 ,获得最终的特征用于像素点的分类。
总的来说,DANet网络主要思想是 CBAM 和 non-local 的融合变形 。 把deep feature map进行 spatial-wise self-attention ,同时也进行 channel-wise self-attetnion ,最后将两个结果进行 element-wise sum 融合。
在 CBAM 分别进行空间和通道 self-attention的思想上, 直接使用了 non-local 的自相关矩阵 Matmul 的形式进行运算 ,避免了 CBAM 手工设计 pooling,多层感知器等复杂操作。
参考:
1.https://zhuanlan.zhihu.com/p/54510782
2.https://zhuanlan.zhihu.com/p/64988633
3.https://blog.csdn.net/wumenglu1018/article/details/95949039
声明:转载请说明出处
下方为小生公众号,还望包容接纳和关注,非常期待与您的美好相遇,让我们以梦为马,砥砺前行。
希望技术与灵魂可以一路同行
长按识别二维码关注一下
更多精彩内容可回复关键词
每篇文章的主题即可
▲长按关注我们
觉得好看对你有帮助,就点个在看吧