原文地址:Dual Attention Network for Scene Segmentation
源代码pytorch:https://github.com/junfu1115/DANet/
复现代码keras:https://github.com/niecongchong/DANet-keras
Abstract
提出了双重注意网络(DANet)来自适应地集成局部特征和全局依赖。在传统的扩张FCN之上附加两种类型的注意力模块,分别模拟空间和通道维度中的语义相互依赖性。
- 位置注意力模块通过所有位置处的特征的加权和来选择性地聚合每个位置的特征。无论距离如何,类似的特征都将彼此相关。
- 同通道注意力模块通过整合所有通道映射之间的相关特征来选择性地强调存在相互依赖的通道映射。
- 将两个注意模块的输出相加以进一步改进特征表示,这有助于更精确的分割结果
1. Introduction
- 提出了一个双重注意网络(DANet)来捕捉空间和通道维度中的全局特征依赖关系
- 建议使用位置注意力模块来学习特征的空间相互依赖性,并设计通道注意力模块来模拟通道相互依赖性。通过在局部特征上建模丰富的上下文依赖关系,显著改善了分割结果
- 在三个数据集Cityscapes, PASCAL Context和COCO Stuff上实现了state-of-the-art的结果
2. Dual Attention Network
2.1 Overview
- 采用 Dilated ResNet (DeepLab V2的主干网络)作为主干,删除了下采样操作并在最后两个ResNet块中使用了空洞卷积,最终特征映射的大小是输入图像的1/8
- Dilated ResNet 产生的特征图再送入两个平行的注意力模块中
- 最后汇总两个注意力模块的输出特征,以获得更好的像素级预测特征表示
2.2 Position Attention Module
- 特征图A(C×H×W)首先分别通过3个卷积层得到3个特征图B,C,D,然后将B,C,D reshape为C×N,其中N=H×W
- 之后将reshape后的B的转置(NxC)与reshape后的C(CxN)相乘,再通过softmax得到spatial attention map S(N×N)
- 接着在reshape后的D(CxN)和S的转置(NxN)之间执行矩阵乘法,再乘以尺度系数α,再reshape为原来形状,最后与A相加得到最后的输出E
- 其中α初始化为0,并逐渐的学习得到更大的权重
2.3 Channel Attention Module
- 分别对A做reshape(CxN)和reshape与transpose(NxC)
- 将得到的两个特征图相乘,再通过softmax得到channel attention map X(C×C)
- 接着把X的转置(CxC)与reshape的A(CxN)做矩阵乘法,再乘以尺度系数β,再reshape为原来形状,最后与A相加得到最后的输出E
- 其中β初始化为0,并逐渐的学习得到更大的权重
2.4 Attention Module Embedding with Networks
- 两个注意力模块的输出先进行元素求和以完成特征融合
- 再进行一次卷积生成最终预测图
3. 实验设置
我们的实现基于Pytorch。采用poly 学习率策略,其中初始学习率在每次迭代之后乘以( 1 − i t e r / t o t a l i t e r ) 0.9 (1-iter/total_iter)^{0.9}(1−iter/totaliter)0.9。Cityscapes数据集的基本学习率设置为0.01。动量衰减系数为0.9,重量衰减系数为0.0001。批处理大小对Cityscapes设置为8,对其他数据集设置为16。在采用多尺度增强时,将COCO的训练时间设置为180个epoch,其他数据集的训练时间设置为240个epoch。当使用两个注意模块时,我们在网络的顶端采用多个损失函数。为了增加数据,我们在Cityscapes数据集的消融研究中采用随机裁剪(裁剪大小768)和随机左右翻转。
keras, TensorFlow中加入CBAM注意力机制
CBAM: Convolutional Block Attention Module
1 作者提出了一个简单但有效的注意力模块 CBAM,给定一个中间特征图,我们沿着空间和通道两个维度依次推断出注意力权重,然后与原特征图相乘来对特征进行自适应调整。
2 由于 CBAM 是一个轻量级的通用模块,它可以无缝地集成到任何 CNN 架构中,额外开销忽略不计,并且可以与基本 CNN 一起进行端到端的训练。
3 在不同的分类和检测数据集上,将 CBAM 集成到不同的模型中后,模型的表现都有了一致的提升,展示了其广泛的可应用性。
通道注意力模块
特征的每一个通道都代表着一个专门的检测器,因此,通道注意力是关注什么样的特征是有意义的。为了汇总空间特征,作者采用了全局平均池化和最大池化两种方式来分别利用不同的信息;
如输入是一个 H×W×C 的特征 F,我们先分别进行一个空间的全局平均池化和最大池化得到两个 1×1×C 的通道描述。接着,再将它们分别送入一个两层的神经网络,第一层神经元个数为 C/r,激活函数为 Relu,第二层神经元个数为 C。注意,这个两层的神经网络是共享的。然后,再将得到的两个特征相加后经过一个 Sigmoid 激活函数得到权重系数 Mc。最后,拿权重系数和原来的特征 F 相乘即可得到缩放后的新特征。参考SEnet;
空间注意力模块
在通道注意力模块之后,我们再引入空间注意力模块来关注哪里的特征是有意义的。
与通道注意力相似,给定一个 H×W×C 的特征 F‘,我们先分别进行一个通道维度的平均池化和最大池化得到两个 H×W×1 的通道描述,并将这两个描述按照通道拼接在一起。然后,经过一个 7×7 的卷积层,激活函数为 Sigmoid,得到权重系数 Ms。最后,拿权重系数和特征 F’ 相乘即可得到缩放后的新特征。
CBAM
通道注意力和空间注意力这两个模块可以以并行或者顺序的方式组合在一起,但是作者发现顺序组合并且将通道注意力放在前面可以取得更好的效果。
参考:
https://blog.csdn.net/qq_38410428/article/details/101550613
第一步:找到要修改文件的源代码
在里面添加通道注意力机制和空间注意力机制
所需库
from keras.layers import GlobalAveragePooling2D, GlobalMaxPooling2D, Reshape, Dense, multiply, Permute, Concatenate, Conv2D, Add, Activation, Lambda
from keras import backend as K
from keras.activations import sigmoid
通道注意力机制
def channel_attention(input_feature, ratio=8):
channel_axis = 1 if K.image_data_format() == "channels_first" else -1
channel = input_feature._keras_shape[channel_axis]
shared_layer_one = Dense(channel//ratio,
kernel_initializer='he_normal',
activation = 'relu',
use_bias=True,
bias_initializer='zeros')
shared_layer_two = Dense(channel,
kernel_initializer='he_normal',
use_bias=True,
bias_initializer='zeros')
avg_pool = GlobalAveragePooling2D()(input_feature)
avg_pool = Reshape((1,1,channel))(avg_pool)
assert avg_pool._keras_shape[1:] == (1,1,channel)
avg_pool = shared_layer_one(avg_pool)
assert avg_pool._keras_shape[1:] == (1,1,channel//ratio)
avg_pool = shared_layer_two(avg_pool)
assert avg_pool._keras_shape[1:] == (1,1,channel)
max_pool = GlobalMaxPooling2D()(input_feature)
max_pool = Reshape((1,1,channel))(max_pool)
assert max_pool._keras_shape[1:] == (1,1,channel)
max_pool = shared_layer_one(max_pool)
assert max_pool._keras_shape[1:] == (1,1,channel//ratio)
max_pool = shared_layer_two(max_pool)
assert max_pool._keras_shape[1:] == (1,1,channel)
cbam_feature = Add()([avg_pool,max_pool])
cbam_feature = Activation('hard_sigmoid')(cbam_feature)
if K.image_data_format() == "channels_first":
cbam_feature = Permute((3, 1, 2))(cbam_feature)
return multiply([input_feature, cbam_feature])
空间注意力机制
def spatial_attention(input_feature):
kernel_size = 7
if K.image_data_format() == "channels_first":
channel = input_feature._keras_shape[1]
cbam_feature = Permute((2,3,1))(input_feature)
else:
channel = input_feature._keras_shape[-1]
cbam_feature = input_feature
avg_pool = Lambda(lambda x: K.mean(x, axis=3, keepdims=True))(cbam_feature)
assert avg_pool._keras_shape[-1] == 1
max_pool = Lambda(lambda x: K.max(x, axis=3, keepdims=True))(cbam_feature)
assert max_pool._keras_shape[-1] == 1
concat = Concatenate(axis=3)([avg_pool, max_pool])
assert concat._keras_shape[-1] == 2
cbam_feature = Conv2D(filters = 1,
kernel_size=kernel_size,
activation = 'hard_sigmoid',
strides=1,
padding='same',
kernel_initializer='he_normal',
use_bias=False)(concat)
assert cbam_feature._keras_shape[-1] == 1
if K.image_data_format() == "channels_first":
cbam_feature = Permute((3, 1, 2))(cbam_feature)
return multiply([input_feature, cbam_feature])
构建CBAM
def cbam_block(cbam_feature,ratio=8):
"""Contains the implementation of Convolutional Block Attention Module(CBAM) block.
As described in https://arxiv.org/abs/1807.06521.
"""
cbam_feature = channel_attention(cbam_feature, ratio)
cbam_feature = spatial_attention(cbam_feature, )
return cbam_feature
在相应的位置添加CBAM
inputs = x
residual = layers.Conv2D(filter, kernel_size = (1, 1), strides = strides, padding = 'same')(inputs)
residual = layers.BatchNormalization(axis = bn_axis)(residual)
cbam = cbam_block(residual)
x = layers.add([x, residual, cbam])
这样就在任意位置加入了注意力机制啦。
CBAM的pytorch实现:https://blog.csdn.net/DD_PP_JJ/article/details/103318617