【論文解讀】用于卷積神經(jīng)網(wǎng)絡(luò)的注意力機(jī)制(Attention)----CBAM: Convolutional Block Attention Module

論文:CBAM: Convolutional Block Attention Module
收錄于:ECCV 2018

摘要

論文提出了Convolutional Block Attention Module(CBAM),這是一種為卷積神將網(wǎng)絡(luò)設(shè)計(jì)的踱阿,簡單有效的注意力模塊(Attention Module)。對于卷積神經(jīng)網(wǎng)絡(luò)生成的feature map,CBAM從通道和空間兩個維度計(jì)算feature map的attention map,然后將attention map與輸入的feature map相乘來進(jìn)行特征的自適應(yīng)學(xué)習(xí)纯出。CBAM是一個輕量的通用模塊抛虫,可以將其融入到各種卷積神經(jīng)網(wǎng)絡(luò)中進(jìn)行端到端的訓(xùn)練。

主要思想

對于一個中間層的feature map:F \in\mathbb R^{C*H*W}允跑,CBAM將會順序推理出1維的channel attention map M_c \in\mathbb R^{C*1*1}以及2維的spatial attention map M_s \in\mathbb R^{1*H*W},整個過程如下所示:
F^{'} = M_c(F) \otimes F
F^{''}=M_s(F^{'}) \otimes F^{'}
其中\otimes為element-wise multiplication搪柑,首先將channel attention map與輸入的feature map相乘得到F^{'}聋丝,之后計(jì)算F^{'}的spatial attention map,并將兩者相乘得到最終的輸出F^{''}工碾。下圖為CBAM的示意圖:

CBAM的結(jié)構(gòu)圖

Channel attention module

feature map 的每個channel都被視為一個feature detector弱睦,channel attention主要關(guān)注于輸入圖片中什么(what)是有意義的。為了高效地計(jì)算channel attention渊额,論文使用最大池化平均池化對feature map在空間維度上進(jìn)行壓縮况木,得到兩個不同的空間背景描述:F^{c}_{max}F^{c}_{avg}垒拢。使用由MLP組成的共享網(wǎng)絡(luò)對這兩個不同的空間背景描述進(jìn)行計(jì)算得到channel attention map:M_c \in\mathbb R^{C*1*1}。計(jì)算過程如下:
M_c(F) = \sigma(MLP(AvgPool(F)) + MLP(MaxPool(F)))
M_c(F) = \sigma(W_1(W_0(F^{c}_{avg})) + W_1(W_0(F^{c}_{max})))

其中W_0 \in \mathbb R^{C/r * C}火惊,W_1 \in \mathbb R^{C * C/r}求类,W_0后使用了Relu作為激活函數(shù)。

Spatial attention module.

與channel attention不同屹耐,spatial attention主要關(guān)注于位置信息(where)尸疆。為了計(jì)算spatial attention,論文首先在channel的維度上使用最大池化平均池化得到兩個不同的特征描述F^{s}_{max} \in \mathbb R_{1*H*W}F^{s}_{avg} \in \mathbb R_{1*H*W}张症,然后使用concatenation將兩個特征描述合并仓技,并使用卷積操作生成spatial attention map M_s(F) \in \mathbb R_{H*W}。計(jì)算過程如下:
M_s(F) = \sigma(f^{7*7}([AvgPool(F); MaxPool(F)]))
M_s(F) = \sigma(f^{7*7}([F^{s}_{avg}; F^{s}_{max}]))
其中俗他,f^{7*7}表示7*7的卷積層

下圖為channel attention和spatial attention的示意圖:


(上)channel attention module脖捻;(下)spatial attention module

代碼

環(huán)境:tensorflow 1.9

"""
@Time   : 2018/10/19
@Author : Li YongHong
@Email  : lyh_robert@163.com
@File   : test.py
"""
import tensorflow as tf
import numpy as np

slim = tf.contrib.slim

def combined_static_and_dynamic_shape(tensor):
  """Returns a list containing static and dynamic values for the dimensions.

  Returns a list of static and dynamic values for shape dimensions. This is
  useful to preserve static shapes when available in reshape operation.

  Args:
    tensor: A tensor of any type.

  Returns:
    A list of size tensor.shape.ndims containing integers or a scalar tensor.
  """
  static_tensor_shape = tensor.shape.as_list()
  dynamic_tensor_shape = tf.shape(tensor)
  combined_shape = []
  for index, dim in enumerate(static_tensor_shape):
    if dim is not None:
      combined_shape.append(dim)
    else:
      combined_shape.append(dynamic_tensor_shape[index])
  return combined_shape

def convolutional_block_attention_module(feature_map, index, inner_units_ratio=0.5):
    """
    CBAM: convolution block attention module, which is described in "CBAM: Convolutional Block Attention Module"
    Architecture : "https://arxiv.org/pdf/1807.06521.pdf"
    If you want to use this module, just plug this module into your network
    :param feature_map : input feature map
    :param index : the index of convolution block attention module
    :param inner_units_ratio: output units number of fully connected layer: inner_units_ratio*feature_map_channel
    :return:feature map with channel and spatial attention
    """
    with tf.variable_scope("cbam_%s" % (index)):
        feature_map_shape = combined_static_and_dynamic_shape(feature_map)
        # channel attention
        channel_avg_weights = tf.nn.avg_pool(
            value=feature_map,
            ksize=[1, feature_map_shape[1], feature_map_shape[2], 1],
            strides=[1, 1, 1, 1],
            padding='VALID'
        )
        channel_max_weights = tf.nn.max_pool(
            value=feature_map,
            ksize=[1, feature_map_shape[1], feature_map_shape[2], 1],
            strides=[1, 1, 1, 1],
            padding='VALID'
        )
        channel_avg_reshape = tf.reshape(channel_avg_weights,
                                         [feature_map_shape[0], 1, feature_map_shape[3]])
        channel_max_reshape = tf.reshape(channel_max_weights,
                                         [feature_map_shape[0], 1, feature_map_shape[3]])
        channel_w_reshape = tf.concat([channel_avg_reshape, channel_max_reshape], axis=1)

        fc_1 = tf.layers.dense(
            inputs=channel_w_reshape,
            units=feature_map_shape[3] * inner_units_ratio,
            name="fc_1",
            activation=tf.nn.relu
        )
        fc_2 = tf.layers.dense(
            inputs=fc_1,
            units=feature_map_shape[3],
            name="fc_2",
            activation=None
        )
        channel_attention = tf.reduce_sum(fc_2, axis=1, name="channel_attention_sum")
        channel_attention = tf.nn.sigmoid(channel_attention, name="channel_attention_sum_sigmoid")
        channel_attention = tf.reshape(channel_attention, shape=[feature_map_shape[0], 1, 1, feature_map_shape[3]])
        feature_map_with_channel_attention = tf.multiply(feature_map, channel_attention)
        # spatial attention
        channel_wise_avg_pooling = tf.reduce_mean(feature_map_with_channel_attention, axis=3)
        channel_wise_max_pooling = tf.reduce_max(feature_map_with_channel_attention, axis=3)

        channel_wise_avg_pooling = tf.reshape(channel_wise_avg_pooling,
                                              shape=[feature_map_shape[0], feature_map_shape[1], feature_map_shape[2],
                                                     1])
        channel_wise_max_pooling = tf.reshape(channel_wise_max_pooling,
                                              shape=[feature_map_shape[0], feature_map_shape[1], feature_map_shape[2],
                                                     1])

        channel_wise_pooling = tf.concat([channel_wise_avg_pooling, channel_wise_max_pooling], axis=3)
        spatial_attention = slim.conv2d(
            channel_wise_pooling,
            1,
            [7, 7],
            padding='SAME',
            activation_fn=tf.nn.sigmoid,
            scope="spatial_attention_conv"
        )
        feature_map_with_attention = tf.multiply(feature_map_with_channel_attention, spatial_attention)
        return feature_map_with_attention

#example
feature_map = tf.constant(np.random.rand(2,8,8,32), dtype=tf.float16)
feature_map_with_attention = convolutional_block_attention_module(feature_map, 1)

with tf.Session() as sess:
    init = tf.global_variables_initializer()
    sess.run(init)
    result = sess.run(feature_map_with_attention)
    print(result.shape)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市兆衅,隨后出現(xiàn)的幾起案子地沮,更是在濱河造成了極大的恐慌,老刑警劉巖羡亩,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摩疑,死亡現(xiàn)場離奇詭異,居然都是意外死亡畏铆,警方通過查閱死者的電腦和手機(jī)雷袋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辞居,“玉大人楷怒,你說我怎么就攤上這事⊥咴睿” “怎么了鸠删?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長贼陶。 經(jīng)常有香客問我刃泡,道長,這世上最難降的妖魔是什么碉怔? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任烘贴,我火速辦了婚禮,結(jié)果婚禮上撮胧,老公的妹妹穿的比我還像新娘桨踪。我一直安慰自己,他們只是感情好趴樱,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布馒闷。 她就那樣靜靜地躺著,像睡著了一般叁征。 火紅的嫁衣襯著肌膚如雪纳账。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天捺疼,我揣著相機(jī)與錄音疏虫,去河邊找鬼。 笑死啤呼,一個胖子當(dāng)著我的面吹牛卧秘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播官扣,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼翅敌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了惕蹄?” 一聲冷哼從身側(cè)響起蚯涮,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎卖陵,沒想到半個月后遭顶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泪蔫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年棒旗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撩荣。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡铣揉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出婿滓,到底是詐尸還是另有隱情老速,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布凸主,位于F島的核電站橘券,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏卿吐。R本人自食惡果不足惜旁舰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嗡官。 院中可真熱鬧箭窜,春花似錦、人聲如沸衍腥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至竹捉,卻和暖如春芜辕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背块差。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工侵续, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人憨闰。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓状蜗,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鹉动。 傳聞我的和親對象是個殘疾皇子轧坎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容