這是一種用於前饋卷積神經網絡的簡單而有效的注意模塊。 給定一箇中間特徵圖,咱們的模塊會沿着兩個獨立的維度(通道和空間)依次推斷注意力圖,而後將注意力圖乘以輸入特徵圖以進行自適應特徵修飾。 因爲CBAM是輕量級的通用模塊,所以能夠以可忽略的開銷將其無縫集成到任何CNN架構中,而且能夠與基礎CNN一塊兒進行端到端訓練。網絡
爲了實現這一目標,咱們依次應用頻道和空間關注模塊(如圖1所示),以便每一個分支機構均可以分別學習在頻道和空間軸上參與的「內容」和「位置」。結果,咱們的模塊經過學習要強調或抑制的信息來有效地幫助網絡中的信息流。將結果先經過通道加權模塊,再經過空間位置加權模塊架構
這裏對網絡作一個實際性的分析,ide
channel attention Module 主要是關注哪些通道對網絡的最後輸出結果起到做用,即文章中提到的‘什麼’,即哪些特徵對最終的預測起到了決定性的做用 學習
channel 特徵分析,輸入經過一個最大值池化和均值池化spa
最大值池化分析:首先經過對寬度和高度進行最大值池化,而後對特徵通道進行全鏈接,爲了減小參數,這裏的輸出通道爲 channel / 8, 下一步再進行全鏈接,使得輸出通道爲 channel。code
均值池化分析:首先經過對寬度和高度進行均值池化,而後對特徵通道進行全鏈接,爲了減小參數,這裏的輸出通道爲channel / 8, 下一步再使用全鏈接,使得輸出通道爲channel。blog
下一步:將兩個進行加和,而後經過sigmoid進行輸出,最後的結果與輸入結果進行相乘操做,進行注意機制加權。get
Spatial Attention Module 主要是關注哪些位置對網絡的最後輸出結果起到做用,即文章中提到的‘哪裏’,即哪些位置信息對最終的預測起到了決定性的做用input
spatial 特徵分析:輸入經過一個最大值池化和均值池化it
最大值池化分析:對通道求取最大值池化
均值池化:對通道求取均值池化
下一步:將兩個特徵進行axis=3的通道串接,進行卷積操做,保證axis=3的特徵數爲1,進行sigmoid輸出,最後結果與輸入進行相乘操做,進行注意機制加權
attention_module.py
import tensorflow as tf def cbam_block(input_feature, name, ratio=8): """Contains the implementation of Convolutional Block Attention Module(CBAM) block. As described in https://arxiv.org/abs/1807.06521. """ with tf.variable_scope(name): attention_feature = channel_attention(input_feature, 'ch_at', ratio) # 通道注意機制 attention_feature = spatial_attention(attention_feature, 'sp_at') print("CBAM Hello") return attention_feature def channel_attention(input_feature, name, ratio=8): kernel_initializer = tf.contrib.layers.variance_scaling_initializer() # 通道的參數卷積初始化 bias_initializer = tf.constant_initializer(value=0.0) # 偏置的初始化 with tf.variable_scope(name): channel = input_feature.get_shape()[-1] # 輸入的通道數 avg_pool = tf.reduce_mean(input_feature, axis=[1, 2], keepdims=True) # 進行均值平均 assert avg_pool.get_shape()[1:] == (1, 1, channel) avg_pool = tf.layers.dense(inputs=avg_pool, units=channel // ratio, activation=tf.nn.relu, kernel_initializer=kernel_initializer, bias_initializer=bias_initializer, name='mlp_0', reuse=None) assert avg_pool.get_shape()[1:] == (1, 1, channel // ratio) avg_pool = tf.layers.dense(inputs=avg_pool, units=channel, kernel_initializer=kernel_initializer, bias_initializer=bias_initializer, name='mlp_1', reuse=None) assert avg_pool.get_shape()[1:] == (1, 1, channel) max_pool = tf.reduce_max(input_feature, axis=[1, 2], keepdims=True) assert max_pool.get_shape()[1:] == (1, 1, channel) max_pool = tf.layers.dense(inputs=max_pool, units=channel // ratio, activation=tf.nn.relu, name='mlp_0', reuse=True) assert max_pool.get_shape()[1:] == (1, 1, channel // ratio) max_pool = tf.layers.dense(inputs=max_pool, units=channel, name='mlp_1', reuse=True) assert max_pool.get_shape()[1:] == (1, 1, channel) scale = tf.sigmoid(avg_pool + max_pool, 'sigmoid') return input_feature * scale def spatial_attention(input_feature, name): kernel_size = 7 kernel_initializer = tf.contrib.layers.variance_scaling_initializer() with tf.variable_scope(name): avg_pool = tf.reduce_mean(input_feature, axis=[3], keepdims=True) assert avg_pool.get_shape()[-1] == 1 max_pool = tf.reduce_max(input_feature, axis=[3], keepdims=True) assert max_pool.get_shape()[-1] == 1 concat = tf.concat([avg_pool, max_pool], 3) assert concat.get_shape()[-1] == 2 concat = tf.layers.conv2d(concat, filters=1, kernel_size=[kernel_size, kernel_size], strides=[1, 1], padding="same", activation=None, kernel_initializer=kernel_initializer, use_bias=False, name='conv') assert concat.get_shape()[-1] == 1 concat = tf.sigmoid(concat, 'sigmoid') return input_feature * concat