【深度学习】(8) CNN中的通道注意力机制(SEnet、ECAnet),附Tensorflow完整代码
各位同學好,今天和大家分享一下attention注意力機制在CNN卷積神經網絡中的應用,重點介紹三種注意力機制,及其代碼復現。
在我之前的神經網絡專欄的文章中也使用到過注意力機制,比如在MobileNetV3、EfficientNet網絡中都是用了SE注意力機制,感興趣的可以看一下:https://blog.csdn.net/dgvv4/category_11517910.html。那么今天就和大家來聊一聊注意力機制。
1. 引言
注意力機制源于對人類視覺的研究。在認知科學中,由于信息處理的瓶頸,人類會選擇性地關注所有信息中的一部分,同時忽略其他可見信息。為了合理利用有限的視覺信息處理資源,人類需要選擇視覺區域中的特定部分,然后重點關注它。
注意力機制沒有嚴格的數學定義,例如傳統的局部圖像特征提取、滑動窗口方法等都可以看作是一種注意力機制。在神經網絡中,注意力機制通常是一個額外的神經網絡,能夠硬性選擇輸入的某些部分,或者給輸入的不同部分分配不同的權重。注意力機制能夠從大量的信息中篩選出重要的信息。
在神經網絡中引入注意力機制有很多種方法,以卷積神經網絡為例,可以在空間維度增加引入注意力機制,也可以在通道維度增加注意力機制(SE),當然也有混合維度(CBAM)即空間維度和通道維度增加注意力機制。
2. SENet注意力機制
2.1 方法介紹
SE注意力機制(Squeeze-and-Excitation Networks)在通道維度增加注意力機制,關鍵操作是squeeze和excitation。
通過自動學習的方式,即使用另外一個新的神經網絡,獲取到特征圖的每個通道的重要程度,然后用這個重要程度去給每個特征賦予一個權重值,從而讓神經網絡重點關注某些特征通道。提升對當前任務有用的特征圖的通道,并抑制對當前任務用處不大的特征通道。
如下圖所示,在輸入SE注意力機制之前(左側白圖C2),特征圖的每個通道的重要程度都是一樣的,通過SENet之后(右側彩圖C2),不同顏色代表不同的權重,使每個特征通道的重要性變得不一樣了,使神經網絡重點關注某些權重值大的通道。
2.2 實現過程:
(1)Squeeze(Fsq):通過全局平均池化,將每個通道的二維特征(H*W)壓縮為1個實數,將特征圖從 [h, w, c] ==> [1,1,c]
(2)excitation(Fex):給每個特征通道生成一個權重值,論文中通過兩個全連接層構建通道間的相關性,輸出的權重值數目和輸入特征圖的通道數相同。[1,1,c] ==> [1,1,c]
(3)Scale(Fscale):將前面得到的歸一化權重加權到每個通道的特征上。論文中使用的是乘法,逐通道乘以權重系數。[h,w,c]*[1,1,c] ==> [h,w,c]
下面我用EfficientNet中的SE注意力機制來說明一下這個流程。
squeeze操作:特征圖經過全局平均池化,將特征圖壓縮成特征向量[1,1,c]
excitation操作:FC1層+Swish激活+FC2層+Sigmoid激活。通過全連接層(FC1),將特征圖向量的通道維度降低為原來的1/r,即[1,1,c*1/r];然后經過Swish激活函數;再通過一個全連接層(FC2),將特征圖向量的特征圖上升回原來[1,1,c];然后經過sigmoid函數轉化為一個0-1之間的歸一化權重向量。
scale操作:將歸一化權重和原輸入特征圖逐通道相乘,生成加權后的特征圖。
小節:
(1)SENet的核心思想是通過全連接網絡根據loss損失來自動學習特征權重,而不是直接根據特征通道的數值分配來判斷,使有效的特征通道的權重大。當然SE注意力機制不可避免的增加了一些參數和計算量,但性價比還是挺高的。
(2)論文認為excitation操作中使用兩個全連接層相比直接使用一個全連接層,它的好處在于,具有更多的非線性,可以更好地擬合通道間的復雜關聯。
2.3 代碼復現
import tensorflow as tf
from tensorflow.keras import layers, Model, Input# se注意力機制
def se_block(inputs, ratio=4): # ratio代表第一個全連接層下降通道數的系數# 獲取輸入特征圖的通道數in_channel = inputs.shape[-1]# 全局平均池化[h,w,c]==>[None,c]x = layers.GlobalAveragePooling2D()(inputs)# [None,c]==>[1,1,c]x = layers.Reshape(target_shape=(1,1,in_channel))(x)# [1,1,c]==>[1,1,c/4]x = layers.Dense(in_channel//ratio)(x) # 全連接下降通道數# relu激活x = tf.nn.relu(x)# [1,1,c/4]==>[1,1,c]x = layers.Dense(in_channel)(x) # 全連接上升通道數# sigmoid激活,權重歸一化x = tf.nn.sigmoid(x)# [h,w,c]*[1,1,c]==>[h,w,c]outputs = layers.multiply([inputs, x]) # 歸一化權重和原輸入特征圖逐通道相乘return outputs # 測試SE注意力機制
if __name__ == '__main__':# 構建輸入inputs = Input([56,56,24])x = se_block(inputs) # 接收SE返回值model = Model(inputs, x) # 構建網絡模型print(x.shape) # (None, 56, 56, 24)model.summary() # 輸出SE模塊的結構
查看SE模塊的結構框架
Model: "model"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_1 (InputLayer) [(None, 56, 56, 24)] 0
__________________________________________________________________________________________________
global_average_pooling2d (Globa (None, 24) 0 input_1[0][0]
__________________________________________________________________________________________________
reshape (Reshape) (None, 1, 1, 24) 0 global_average_pooling2d[0][0]
__________________________________________________________________________________________________
dense (Dense) (None, 1, 1, 6) 150 reshape[0][0]
__________________________________________________________________________________________________
tf.nn.relu (TFOpLambda) (None, 1, 1, 6) 0 dense[0][0]
__________________________________________________________________________________________________
dense_1 (Dense) (None, 1, 1, 24) 168 tf.nn.relu[0][0]
__________________________________________________________________________________________________
tf.math.sigmoid (TFOpLambda) (None, 1, 1, 24) 0 dense_1[0][0]
__________________________________________________________________________________________________
multiply (Multiply) (None, 56, 56, 24) 0 input_1[0][0] tf.math.sigmoid[0][0]
==================================================================================================
Total params: 318
Trainable params: 318
Non-trainable params: 0
__________________________________________________________________________________________________
3. ECANet注意力機制
3.1 方法介紹
ECANet是通道注意力機制的一種實現形式,ECANet可以看做是SENet的改進版。
作者表明SENet中的降維會給通道注意力機制帶來副作用,并且捕獲所有通道之間的依存關系是效率不高的且是不必要的。
ECA注意力機制模塊直接在全局平均池化層之后使用1x1卷積層,去除了全連接層。該模塊避免了維度縮減,并有效捕獲了跨通道交互。并且ECA只涉及少數參數就能達到很好的效果。
ECANet通過一維卷積 layers.Conv1D來完成跨通道間的信息交互,卷積核的大小通過一個函數來自適應變化,使得通道數較大的層可以更多地進行跨通道交互。自適應函數為:,其中
3.2 實現過程
(1)將輸入特征圖經過全局平均池化,特征圖從[h,w,c]的矩陣變成[1,1,c]的向量
(2)計算得到自適應的一維卷積核大小kernel_size
(3)將kernel_size用于一維卷積中,得到對于特征圖的每個通道的權重
(4)將歸一化權重和原輸入特征圖逐通道相乘,生成加權后的特征圖
3.3 代碼實現
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import Model, layers
import mathdef eca_block(inputs, b=1, gama=2):# 輸入特征圖的通道數in_channel = inputs.shape[-1]# 根據公式計算自適應卷積核大小kernel_size = int(abs((math.log(in_channel, 2) + b) / gama))# 如果卷積核大小是偶數,就使用它if kernel_size % 2:kernel_size = kernel_size# 如果卷積核大小是奇數就變成偶數else:kernel_size = kernel_size + 1# [h,w,c]==>[None,c] 全局平均池化x = layers.GlobalAveragePooling2D()(inputs)# [None,c]==>[c,1]x = layers.Reshape(target_shape=(in_channel, 1))(x)# [c,1]==>[c,1]x = layers.Conv1D(filters=1, kernel_size=kernel_size, padding='same', use_bias=False)(x)# sigmoid激活x = tf.nn.sigmoid(x)# [c,1]==>[1,1,c]x = layers.Reshape((1,1,in_channel))(x)# 結果和輸入相乘outputs = layers.multiply([inputs, x])return outputs# 驗證ECA注意力機制
if __name__ == '__main__':# 構造輸入層inputs = keras.Input(shape=[26,26,512])x = eca_block(inputs) # 接收ECA輸出結果model = Model(inputs, x) # 構造模型model.summary() # 查看網絡架構
查看ECA模塊,和SENet相比大大減少了參數量,參數量等于一維卷積的kernel_size的大小
Model: "model_1"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_2 (InputLayer) [(None, 26, 26, 512) 0
__________________________________________________________________________________________________
global_average_pooling2d_1 (Glo (None, 512) 0 input_2[0][0]
__________________________________________________________________________________________________
reshape_1 (Reshape) (None, 512, 1) 0 global_average_pooling2d_1[0][0]
__________________________________________________________________________________________________
conv1d (Conv1D) (None, 512, 1) 5 reshape_1[0][0]
__________________________________________________________________________________________________
tf.math.sigmoid_1 (TFOpLambda) (None, 512, 1) 0 conv1d[0][0]
__________________________________________________________________________________________________
reshape_2 (Reshape) (None, 1, 1, 512) 0 tf.math.sigmoid_1[0][0]
__________________________________________________________________________________________________
multiply_1 (Multiply) (None, 26, 26, 512) 0 input_2[0][0] reshape_2[0][0]
==================================================================================================
Total params: 5
Trainable params: 5
Non-trainable params: 0
__________________________________________________________________________________________________
總結
以上是生活随笔為你收集整理的【深度学习】(8) CNN中的通道注意力机制(SEnet、ECAnet),附Tensorflow完整代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【神经网络】(17) Efficient
- 下一篇: 【深度学习理论】(4) 权重初始化,Ba