萬全網(wǎng)站建設(shè)怎么在百度上推廣產(chǎn)品
注意力機制
- 1 SENet
- 2 ECANet
- 3 CBAM
- 3.1 通道注意力
- 3.2 空間注意力
- 3.3 CBAM
- 4 展示網(wǎng)絡(luò)層具體信息
1 SENet
SE注意力機制(Squeeze-and-Excitation Networks)
:是一種通道類型的注意力機制,就是在通道維度上增加注意力機制,主要內(nèi)容是是squeeze和excitation.
就是使用另外一個新的神經(jīng)網(wǎng)絡(luò)(兩個Linear層),針對通道維度的數(shù)據(jù)進(jìn)行學(xué)習(xí),獲取到特征圖每個通道的重要程度,然后再和原始通道數(shù)據(jù)相乘即可。
具體參考Blog:
CNN中的注意力機制
小結(jié):
-
SENet的核心思想是通過全連接網(wǎng)絡(luò)根據(jù)loss損失來自動學(xué)習(xí)特征權(quán)重,而不是直接根據(jù)特征通道的數(shù)值分配來判斷,使有效的特征通道的權(quán)重大。
-
論文認(rèn)為excitation操作中使用兩個全連接層相比直接使用一個全連接層,它的好處在于,具有更多的非線性,可以更好地擬合通道間的復(fù)雜關(guān)聯(lián)。
代碼:
拆解步驟,forward代碼寫的比較細(xì)節(jié)
import torch
from torch import nn
from torchstat import stat # 查看網(wǎng)絡(luò)參數(shù)# 定義SE注意力機制的類
class se_block(nn.Module):# 初始化, in_channel代表輸入特征圖的通道數(shù), ratio代表第一個全連接下降通道的倍數(shù)def __init__(self, in_channel, ratio=4):# 繼承父類初始化方法super(se_block, self).__init__()# 屬性分配# 全局平均池化,輸出的特征圖的寬高=1self.avg_pool = nn.AdaptiveAvgPool2d(output_size=1)# 第一個全連接層將特征圖的通道數(shù)下降4倍self.fc1 = nn.Linear(in_features=in_channel, out_features=in_channel//ratio, bias=False)# relu激活self.relu = nn.ReLU()# 第二個全連接層恢復(fù)通道數(shù)self.fc2 = nn.Linear(in_features=in_channel//ratio, out_features=in_channel, bias=False)# sigmoid激活函數(shù),將權(quán)值歸一化到0-1self.sigmoid = nn.Sigmoid()# 前向傳播def forward(self, inputs): # inputs 代表輸入特征圖# 獲取輸入特征圖的shapeb, c, h, w = inputs.shape# 全局平均池化 [b,c,h,w]==>[b,c,1,1]x = self.avg_pool(inputs)# 維度調(diào)整 [b,c,1,1]==>[b,c]x = x.view([b,c])# 第一個全連接下降通道 [b,c]==>[b,c//4] # 這里也是使用Linear層的原因,只是對Channel進(jìn)行線性變換x = self.fc1(x)x = self.relu(x)# 第二個全連接上升通道 [b,c//4]==>[b,c] # 再通過Linear層恢復(fù)Channel數(shù)目x = self.fc2(x)# 對通道權(quán)重歸一化處理 # 將數(shù)值轉(zhuǎn)化為(0,1)之間,體現(xiàn)不同通道之間重要程度x = self.sigmoid(x)# 調(diào)整維度 [b,c]==>[b,c,1,1] x = x.view([b,c,1,1])# 將輸入特征圖和通道權(quán)重相乘outputs = x * inputsreturn outputs
結(jié)果展示:
提示:
in_channel/ratio需要大于0,否則線性層輸入是0維度,沒有意義,可以根據(jù)自己需求調(diào)整ratio的大小。
2 ECANet
作者表明 SENet
中的降維會給通道注意力機制帶來副作用,并且捕獲所有通道之間的依存關(guān)系是效率不高的,而且是不必要的。
參考Blog:
CNN中的注意力機制
代碼:
詳細(xì)版本:在forward中,介紹了每一步的作用
import torch
from torch import nn
import math
from torchstat import stat # 查看網(wǎng)絡(luò)參數(shù)# 定義ECANet的類
class eca_block(nn.Module):# 初始化, in_channel代表特征圖的輸入通道數(shù), b和gama代表公式中的兩個系數(shù)def __init__(self, in_channel, b=1, gama=2):# 繼承父類初始化super(eca_block, self).__init__()# 根據(jù)輸入通道數(shù)自適應(yīng)調(diào)整卷積核大小kernel_size = int(abs((math.log(in_channel, 2)+b)/gama))# 如果卷積核大小是奇數(shù),就使用它if kernel_size % 2:kernel_size = kernel_size# 如果卷積核大小是偶數(shù),就把它變成奇數(shù)else:kernel_size = kernel_size + 1# 卷積時,為例保證卷積前后的size不變,需要0填充的數(shù)量padding = kernel_size // 2# 全局平均池化,輸出的特征圖的寬高=1self.avg_pool = nn.AdaptiveAvgPool2d(output_size=1)# 1D卷積,輸入和輸出通道數(shù)都=1,卷積核大小是自適應(yīng)的# 這個1維卷積需要好好了解一下機制,這是改進(jìn)SENet的重要不同點self.conv = nn.Conv1d(in_channels=1, out_channels=1, kernel_size=kernel_size,bias=False, padding=padding)# sigmoid激活函數(shù),權(quán)值歸一化self.sigmoid = nn.Sigmoid()# 前向傳播def forward(self, inputs):# 獲得輸入圖像的shapeb, c, h, w = inputs.shape# 全局平均池化 [b,c,h,w]==>[b,c,1,1]x = self.avg_pool(inputs)# 維度調(diào)整,變成序列形式 [b,c,1,1]==>[b,1,c]x = x.view([b,1,c]) # 這是為了給一維卷積# 1D卷積 [b,1,c]==>[b,1,c]x = self.conv(x)# 權(quán)值歸一化x = self.sigmoid(x)# 維度調(diào)整 [b,1,c]==>[b,c,1,1]x = x.view([b,c,1,1])# 將輸入特征圖和通道權(quán)重相乘[b,c,h,w]*[b,c,1,1]==>[b,c,h,w]outputs = x * inputsreturn outputs
精簡版:
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchinfo import summary
import mathclass EfficientChannelAttention(nn.Module): # Efficient Channel Attention moduledef __init__(self, c, b=1, gamma=2):super(EfficientChannelAttention, self).__init__()t = int(abs((math.log(c, 2) + b) / gamma))k = t if t % 2 else t + 1self.avg_pool = nn.AdaptiveAvgPool2d(1)self.conv1 = nn.Conv1d(1, 1, kernel_size=k, padding=int(k/2), bias=False)self.sigmoid = nn.Sigmoid()def forward(self, x):x = self.avg_pool(x)# 這里可以對照上一版代碼,理解每一個函數(shù)的作用x = self.conv1(x.squeeze(-1).transpose(-1, -2)).transpose(-1, -2).unsqueeze(-1)out = self.sigmoid(x)return out
效果展示:
總結(jié):
ECANet參數(shù)更少!
3 CBAM
CBAM注意力機制是由**通道注意力機制(channel)和空間注意力機制(spatial)**組成。
先通道注意力,后空間注意力的順序注意力模塊!
3.1 通道注意力
輸入數(shù)據(jù),對數(shù)據(jù)分別做最大池化操作和平均池化操作(輸出都是batchchannel11),然后使用SENet的方法,針對channel進(jìn)行先降維后升維操作,之后將輸出的兩個結(jié)果相加,再使用Sigmoid得到通道權(quán)重,再之后使用View函數(shù)恢復(fù)**(batchchannel11)**維度,和原始數(shù)據(jù)相乘得到通道注意力結(jié)果!
通道注意力代碼:
#(1)通道注意力機制
class channel_attention(nn.Module):# 初始化, in_channel代表輸入特征圖的通道數(shù), ratio代表第一個全連接的通道下降倍數(shù)def __init__(self, in_channel, ratio=4):# 繼承父類初始化方法super(channel_attention, self).__init__()# 全局最大池化 [b,c,h,w]==>[b,c,1,1]self.max_pool = nn.AdaptiveMaxPool2d(output_size=1)# 全局平均池化 [b,c,h,w]==>[b,c,1,1]self.avg_pool = nn.AdaptiveAvgPool2d(output_size=1)# 第一個全連接層, 通道數(shù)下降4倍self.fc1 = nn.Linear(in_features=in_channel, out_features=in_channel//ratio, bias=False)# 第二個全連接層, 恢復(fù)通道數(shù)self.fc2 = nn.Linear(in_features=in_channel//ratio, out_features=in_channel, bias=False)# relu激活函數(shù)self.relu = nn.ReLU()# sigmoid激活函數(shù)self.sigmoid = nn.Sigmoid()# 前向傳播def forward(self, inputs):# 獲取輸入特征圖的shapeb, c, h, w = inputs.shape# 輸入圖像做全局最大池化 [b,c,h,w]==>[b,c,1,1]max_pool = self.max_pool(inputs)# 輸入圖像的全局平均池化 [b,c,h,w]==>[b,c,1,1]avg_pool = self.avg_pool(inputs)# 調(diào)整池化結(jié)果的維度 [b,c,1,1]==>[b,c]max_pool = max_pool.view([b,c])avg_pool = avg_pool.view([b,c])# 第一個全連接層下降通道數(shù) [b,c]==>[b,c//4]x_maxpool = self.fc1(max_pool)x_avgpool = self.fc1(avg_pool)# 激活函數(shù)x_maxpool = self.relu(x_maxpool)x_avgpool = self.relu(x_avgpool)# 第二個全連接層恢復(fù)通道數(shù) [b,c//4]==>[b,c]x_maxpool = self.fc2(x_maxpool)x_avgpool = self.fc2(x_avgpool)# 將這兩種池化結(jié)果相加 [b,c]==>[b,c]x = x_maxpool + x_avgpool# sigmoid函數(shù)權(quán)值歸一化x = self.sigmoid(x)# 調(diào)整維度 [b,c]==>[b,c,1,1]x = x.view([b,c,1,1])# 輸入特征圖和通道權(quán)重相乘 [b,c,h,w]outputs = inputs * xreturn outputs
3.2 空間注意力
針對輸入數(shù)據(jù),分別選取數(shù)據(jù)中最大值所在的維度(batch1h*w),和按照維度進(jìn)行數(shù)據(jù)平均操作(batch1hw),然后將兩個數(shù)據(jù)做通道連接(batch2hw),使用卷積操作,將channel維度降為1,之后對結(jié)果取sigmoid,得到空間注意力權(quán)重,和原始數(shù)據(jù)相乘得到空間注意力結(jié)果。
代碼:
#(2)空間注意力機制
class spatial_attention(nn.Module):# 初始化,卷積核大小為7*7def __init__(self, kernel_size=7):# 繼承父類初始化方法super(spatial_attention, self).__init__()# 為了保持卷積前后的特征圖shape相同,卷積時需要paddingpadding = kernel_size // 2# 7*7卷積融合通道信息 [b,2,h,w]==>[b,1,h,w]self.conv = nn.Conv2d(in_channels=2, out_channels=1, kernel_size=kernel_size,padding=padding, bias=False)# sigmoid函數(shù)self.sigmoid = nn.Sigmoid()# 前向傳播def forward(self, inputs):# 在通道維度上最大池化 [b,1,h,w] keepdim保留原有深度# 返回值是在某維度的最大值和對應(yīng)的索引x_maxpool, _ = torch.max(inputs, dim=1, keepdim=True)# 在通道維度上平均池化 [b,1,h,w]x_avgpool = torch.mean(inputs, dim=1, keepdim=True)# 池化后的結(jié)果在通道維度上堆疊 [b,2,h,w]x = torch.cat([x_maxpool, x_avgpool], dim=1)# 卷積融合通道信息 [b,2,h,w]==>[b,1,h,w]x = self.conv(x)# 空間權(quán)重歸一化x = self.sigmoid(x)# 輸入特征圖和空間權(quán)重相乘outputs = inputs * xreturn outputs
3.3 CBAM
將通道注意力模塊和空間注意力模塊順序串聯(lián)得到CBAM模塊!
代碼:
class cbam(nn.Module):# 初始化,in_channel和ratio=4代表通道注意力機制的輸入通道數(shù)和第一個全連接下降的通道數(shù)# kernel_size代表空間注意力機制的卷積核大小def __init__(self, in_channel, ratio=4, kernel_size=7):# 繼承父類初始化方法super(cbam, self).__init__()# 實例化通道注意力機制self.channel_attention = channel_attention(in_channel=in_channel, ratio=ratio)# 實例化空間注意力機制self.spatial_attention = spatial_attention(kernel_size=kernel_size)# 前向傳播def forward(self, inputs):# 先將輸入圖像經(jīng)過通道注意力機制x = self.channel_attention(inputs)# 然后經(jīng)過空間注意力機制x = self.spatial_attention(x)return x
結(jié)果:
4 展示網(wǎng)絡(luò)層具體信息
安裝包
pip install torchstat
使用
from torchstat import stat net = cbam(16)
stat(net, (16, 256, 256)) # 不需要Batch維度
注意力機制后期學(xué)習(xí)到再持續(xù)更新!!
參考博客:
CNN注意力機制
ECANet