CNN注意力機制的進化史:深度解析10種注意力模塊如何重塑卷積神經網絡

🌟 引言:注意力為何改變CNN的命運?

就像人類視覺會優先聚焦于重要信息,深度學習模型也需要"學會看重點"。從2018年SENet首提通道注意力,到2024年SSCA探索空間-通道協同效應,注意力機制正成為CNN性能提升的核武器,這些注意力機制思想不僅不斷改造提升著CNN,也影響到了Transformer架構中的注意力設計。本文帶大家穿越6年技術迭代,揭秘那些改變視覺模型的注意力模塊!

第一站:SENet(2018 CVPR)- 通道注意力的開山之作

image-20250408223306839

核心思想

image-20250408223338738
  • Squeeze操作:用全局平均池化將H×W×C特征圖壓縮成1×1×C的通道描述符
  • Excitation機制:通過全連接層+Sigmoid生成通道權重,實現特征的"有選擇放大"

性能提升:在ResNet基礎上Top-1準確率提升2.3%,參數僅增加0.5M

代碼實現:

import torch
import torch.nn as nnclass SE(nn.Module):def __init__(self, channel, reduction= 16):super(SE, self).__init__()# part 1:(H, W, C) -> (1, 1, C)self.avg_pool = nn.AdaptiveAvgPool2d(1)# part 2, compute weight of each channelself.fc = nn.Sequential(nn.Linear(channel, channel // reduction, bias=False),nn.ReLU(inplace=True),nn.Linear(channel // reduction, channel, bias=False),nn.Sigmoid(),  # nn.Softmax is OK here)def forward(self, x) :b, c, _, _ = x.size()y = self.avg_pool(x).view(b, c)y = self.fc(y).view(b, c, 1, 1)return x * yif __name__ == '__main__':# 創建隨機輸入張量,假設輸入形狀為(batch_size, channels, height, width)input_tensor = torch.randn(1, 64, 32, 32)  # batch_size=1, channels=64, height=32, width=32# 創建SE塊實例se_block = SE(channel=64)# 測試SE塊output_se = se_block(input_tensor)print("Output shape of SE block:", output_se.shape)

第二站:CBAM(2018 ECCV)- 空間×通道的雙重視角

image-20250408223809018

創新點

image-20250408223904465
  1. 通道注意力:融合最大池化+平均池化,通過MLP學習通道間相關性。
  2. 空間注意力:在空間維度上進行注意力分配。它通過對輸入特征圖進行通道間的全局平均池化和全局最大池化,然后將這兩個結果進行拼接,并通過一個7x7的卷積層來學習空間位置之間的相關性,最后使用sigmoid函數生成空間注意力權重。

亮點:在ResNet50上僅增加1.4%計算量,準確率提升3.1%

結構示意:輸入 → 通道注意力 → 空間注意力 → 輸出

image-20250408224039845

代碼實現:

import torch.nn as nn
import torchclass CBAM(nn.Module):def __init__(self,in_chans,reduction= 16,kernel_size= 7,min_channels= 8,):super(CBAM, self).__init__()# channel-wise attentionhidden_chans = max(in_chans // reduction, min_channels)self.mlp_chans = nn.Sequential(nn.Conv2d(in_chans, hidden_chans, kernel_size=1, bias=False),nn.ReLU(),nn.Conv2d(hidden_chans, in_chans, kernel_size=1, bias=False),)# space-wise attentionself.mlp_spaces = nn.Conv2d(2, 1, kernel_size=kernel_size, padding=3, bias=False)self.gate = nn.Sigmoid()def forward(self, x: torch.Tensor) -> torch.Tensor:# (B, C, 1, 1)avg_x_s = x.mean((2, 3), keepdim=True)# (B, C, 1, 1)max_x_s = x.max(dim=2, keepdim=True)[0].max(dim=3, keepdim=True)[0]# (B, C, 1, 1)x = x * self.gate(self.mlp_chans(avg_x_s) + self.mlp_chans(max_x_s))# (B, 1, H, W)avg_x_c = x.mean(dim=1, keepdim=True)max_x_c = x.max(dim=1, keepdim=True)[0]x = x * self.gate(self.mlp_spaces(torch.cat((avg_x_c, max_x_c), dim=1)))return xif __name__ == '__main__':input_tensor = torch.randn(1, 64, 32, 32)ca_module = CBAM(in_chans=64)output_tensor = ca_module(input_tensor)print(output_tensor.shape)

第三站:DA(2019 CVPR)- 空間與通道的雙重依賴

image-20250408224638153

核心思想

  • 位置注意力模塊:通過自注意力機制捕捉特征圖中任意兩個位置之間的空間依賴性
  • 通道注意力模塊:通過自注意力機制捕捉任意兩個通道圖之間的通道依賴性
  • 融合策略:將兩種注意力模塊的輸出進行融合,增強特征表示

結構示意:

image-20250408224900775
  • 在傳統的擴張FCN之上附加了兩種類型的注意力模塊,分別模擬空間和通道維度中的語義相互依賴性

  • 位置注意力模塊通過自注意力機制捕捉特征圖中任意兩個位置之間的空間依賴

  • 通道注意力模塊使用類似的自注意力機制捕捉任意兩個通道圖之間的通道依賴性

  • 最后,將這兩個注意力模塊的輸出進行融合,以進一步增強特征表示

代碼實現:

import torch
import torch.nn as nnclass PAM(nn.Module):"""position attention module with self-attention mechanism位置注意力模塊,使用自注意力機制"""def __init__(self, in_chans: int):super(PAM, self).__init__()self.in_chans = in_chans# 定義三個卷積層:q、k、v,分別用于計算查詢(Query)、鍵(Key)、值(Value)特征。self.q = nn.Conv2d(in_chans, in_chans // 8, kernel_size=1)  # 查詢卷積self.k = nn.Conv2d(in_chans, in_chans // 8, kernel_size=1)  # 鍵卷積self.v = nn.Conv2d(in_chans, in_chans, kernel_size=1)       # 值卷積# 定義一個可學習的縮放因子gammaself.gamma = nn.Parameter(torch.zeros(1))# Softmax操作用于計算注意力權重self.softmax = nn.Softmax(dim=-1)def forward(self, x: torch.Tensor):b, c, h, w = x.size()  # 獲取輸入張量的尺寸,b為batch大小,c為通道數,h為高度,w為寬度# (B, HW, C) 計算查詢q的形狀,B是batch size,HW是特征圖的空間展平后長度,C是通道數q = self.q(x).view(b, -1, h * w).permute(0, 2, 1)  # 計算查詢q,并將其展平成(B, HW, C)的形狀,并做轉置# (B, C, HW) 計算鍵k的形狀,展平特征圖為(B, C, HW)k = self.k(x).view(b, -1, h * w)# (B, C, HW) 計算值v的形狀,展平特征圖為(B, C, HW)v = self.v(x).view(b, -1, h * w)# (B, HW, HW) 計算注意力矩陣,q與k的乘積,再做softmax處理,得到注意力權重attn = self.softmax(torch.bmm(q, k))  # q和k的乘積,計算注意力分數并應用softmax# (B, C, HW) 計算輸出結果,注意力矩陣與值v相乘,得到最終加權后的特征out = torch.bmm(v, attn.permute(0, 2, 1))  # 計算加權后的輸出,注意力矩陣需要轉置# 將輸出的形狀還原為原來的尺寸(B, C, H, W)out = out.view(b, c, h, w)# 使用gamma進行縮放,并將原始輸入x與輸出相加,形成殘差連接out = self.gamma * out + xreturn outclass CAM(nn.Module):"""channel attention module with self-attention mechanism通道注意力模塊,使用自注意力機制"""def __init__(self, in_chans: int):super(CAM, self).__init__()self.in_chans = in_chans# 定義一個可學習的縮放因子gammaself.gamma = nn.Parameter(torch.zeros(1))# Softmax操作用于計算注意力權重self.softmax = nn.Softmax(dim=-1)def forward(self, x: torch.Tensor) -> torch.Tensor:b, c, w, h = x.size()  # 獲取輸入張量的尺寸,b為batch大小,c為通道數,w為寬度,h為高度# (B, C, HW) 將輸入張量展平為(B, C, HW),HW表示寬度和高度的展平q = x.view(b, c, -1)# (B, HW, C) 轉置得到(B, HW, C)形狀,以便后續計算注意力k = x.view(b, c, -1).permute(0, 2, 1)# (B, C, HW) 直接用輸入張量展平得到(B, C, HW)v = x.view(b, c, -1)# (B, C, C) 計算q和k的矩陣乘積,得到能量矩陣energy = torch.bmm(q, k)# 通過max操作處理能量矩陣,找到每個通道的最大能量,進行縮放energy_new = torch.max(energy, dim=-1, keepdim=True)[0].expand_as(energy) - energy# 計算注意力權重,使用softmax對縮放后的能量進行歸一化attn = self.softmax(energy_new)# (B, C, HW) 使用注意力權重對v加權,得到加權后的輸出out = torch.bmm(attn, v)# 將輸出的形狀還原為原來的尺寸(B, C, H, W)out = out.view(b, c, h, w)# 使用gamma進行縮放,并將原始輸入x與輸出相加,形成殘差連接out = self.gamma * out + xreturn outclass DualAttention(nn.Module):def __init__(self, in_chans: int):super(DualAttention, self).__init__()self.in_chans = in_chansself.pam = PAM(in_chans)self.cam = CAM(in_chans)def forward(self, x: torch.Tensor) -> torch.Tensor:pam = self.pam(x)cam = self.cam(x)return pam + camif __name__ == '__main__':model = DualAttention(in_chans=64)x = torch.rand((3, 64, 40, 40))print(model(x).size())

第四站:SK(2019 CVPR)- 選擇性核注意力

image-20250408225444530

核心思想

  • 多核選擇:通過不同大小的卷積核(如3×3、5×5)提取多尺度特征
  • 自適應融合:根據輸入特征動態選擇最優卷積核大小,平衡計算復雜度和特征表達能力

結構示意:

image-20250408225459268
  • 輕量級和高效性:ECA模塊只涉及少量參數,同時帶來明顯的性能提升。它避免了傳統注意力機制中的復雜降維和升維過程,通過局部跨通道交互策略,以一維卷積的方式高效實現。

  • 局部跨通道交互:ECA模塊通過一維卷積捕捉通道間的依賴關系,這種策略不需要降維,從而顯著降低了模型復雜性。

  • 自適應卷積核大小:ECA模塊開發了一種方法來自適應選擇一維卷積的核大小,以確定局部跨通道交互的覆蓋范圍。

性能表現:在ResNet50基礎上Top-1準確率提升1.5%,計算量僅增加2.3%

代碼實現:

import torch
import torch.nn as nn
import torch.nn.functional as Fdef auto_pad(k, d=1):"""自動計算卷積操作所需的填充量,確保輸出尺寸符合要求。:param k: 卷積核的大小 (kernel size),通常為單一整數或者元組。:param d: 膨脹系數 (dilation),通常默認為1。膨脹系數影響卷積核的有效大小。:return: 填充量 (padding)"""# 如果卷積核大小是整數,轉換為元組if isinstance(k, int):k = (k, k)# 如果膨脹系數大于1,卷積核的有效大小會增大effective_kernel_size = (k[0] - 1) * d + 1  # 計算膨脹后的卷積核大小# 假設我們希望使用“same”填充來保持輸出尺寸和輸入尺寸相同padding = (effective_kernel_size - 1) // 2  # 保證輸出尺寸與輸入尺寸一致return paddingclass  ConvModule(nn.Module):def __init__(self,in_chans, hidden_chans, kernel_size,stride,groups=1,padding=0, dilation=0,norm_cfg='BN', act_cfg='ReLU'):super().__init__()self.conv = nn.Conv2d(in_chans,hidden_chans,kernel_size=kernel_size,stride=stride,groups=groups,padding=padding,dilation=dilation)if norm_cfg == 'BN':self.norm = nn.BatchNorm2d(hidden_chans)elif norm_cfg == 'GN':self.norm = nn.GroupNorm(32, hidden_chans)if act_cfg == 'ReLU':self.act = nn.ReLU(inplace=True)elif act_cfg == 'LeakyReLU':self.act = nn.LeakyReLU(inplace=True)elif act_cfg == 'HSwish':self.act = nn.Hardswish(inplace=True)def forward(self, x):x = self.conv(x)x = self.norm(x)x = self.act(x)return xclass SK(nn.Module):def __init__(self,in_chans,out_chans,num= 2,kernel_size= 3,stride= 1,groups= 1,reduction= 16,norm_cfg='BN',act_cfg='ReLU'):super(SK, self).__init__()self.num = numself.out_chans = out_chansself.kernel_size = kernel_sizeself.conv = nn.ModuleList()for i in range(num):self.conv.append(ConvModule(in_chans, out_chans, kernel_size, stride=stride, groups=groups, dilation=1 + i,padding=auto_pad(k=kernel_size, d=1 + i), norm_cfg=norm_cfg, act_cfg=act_cfg))# fc can be implemented by 1x1 convself.fc = nn.Sequential(# use relu act to improve nonlinear expression abilityConvModule(in_chans, out_chans // reduction, kernel_size=1,stride=stride, norm_cfg=norm_cfg, act_cfg=act_cfg),nn.Conv2d(out_chans // reduction, out_chans * self.num, kernel_size=1, bias=False))# compute channels weightself.softmax = nn.Softmax(dim=1)def forward(self, x: torch.Tensor) -> torch.Tensor:# use different convolutional kernel to convtemp_feature = [conv(x) for conv in self.conv]x = torch.stack(temp_feature, dim=1)# fuse different output and squeezeattn = x.sum(1).mean((2, 3), keepdim=True)# excitationattn = self.fc(attn)batch, c, h, w = attn.size()attn = attn.view(batch, self.num, self.out_chans, h, w)attn = self.softmax(attn)# selectx = x * attnx = torch.sum(x, dim=1)return xif __name__ == "__main__":model = SK(in_chans=64,out_chans=64)x = torch.rand((3, 64, 40, 40))print(model(x).size())

第五站:ECA-Net(2020 CVPR)- 用1D卷積顛覆傳統注意力

image-20250408224340924

革命性設計

  • 用1D卷積替代全連接層,降低98%參數量
  • 自適應核大小選擇算法,動態調整感受野

性能對比

模塊參數量Top-1提升
SENet1.5M+2.3%
ECA-Net2.8k+2.7%

核心結構:

image-20250408224432331
  • 輕量級和高效性:ECA模塊只涉及少量參數,同時帶來明顯的性能提升。它避免了傳統注意力機制中的復雜降維和升維過程,通過局部跨通道交互策略,以一維卷積的方式高效實現。

  • 局部跨通道交互:ECA模塊通過一維卷積捕捉通道間的依賴關系,這種策略不需要降維,從而顯著降低了模型復雜性。

  • 自適應卷積核大小:ECA模塊開發了一種方法來自適應選擇一維卷積的核大小,以確定局部跨通道交互的覆蓋范圍。

第六站:CA(2021 CVPR)- 坐標注意力的巧妙設計

image-20250408225840172

核心思想

  • 方向感知特征圖:通過沿垂直和水平方向的全局平均池化,將特征圖分解為兩個獨立的方向感知特征圖
  • 長距離依賴:在保留位置信息的同時,捕獲特征圖中不同位置的長距離依賴關系
  • 高效融合:將兩個方向感知特征圖編碼為注意力張量,實現空間和通道信息的高效融合

結構示意:

image-20250408225903057
  • 考慮了空間維度注意力和通道維度注意力,這有助于模型定位、識別和增強更有趣的對象。

  • CA利用兩個2-D全局平均池化(GAP)操作分別沿著垂直和水平方向聚合輸入特征到兩個獨立的方向感知特征圖中。然后,分別將這兩個特征圖編碼成一個注意力張量。

  • 在這兩個特征圖(Cx1xW和CxHx1)中,一個使用GAP來模擬特征圖在空間維度上的長距離依賴,同時在另一個空間維度保留位置信息。在這種情況下,兩個特征圖、空間信息和長距離依賴相互補充。

性能表現:在MobileNetV2上Top-1準確率提升5.1%,計算量僅增加1.2%

代碼實現:

import torch
import torch.nn as nn
import torch.nn.functional as Fclass  ConvModule(nn.Module):def __init__(self,in_chans, hidden_chans, kernel_size=1, norm_cfg='BN', act_cfg='HSwish'):super().__init__()self.conv = nn.Conv2d(in_chans, hidden_chans, kernel_size=kernel_size)if norm_cfg == 'BN':self.norm = nn.BatchNorm2d(hidden_chans)elif norm_cfg == 'GN':self.norm = nn.GroupNorm(32, hidden_chans)if act_cfg == 'ReLU':self.act = nn.ReLU(inplace=True)elif act_cfg == 'LeakyReLU':self.act = nn.LeakyReLU(inplace=True)elif act_cfg == 'HSwish':self.act = nn.Hardswish(inplace=True)def forward(self, x):x = self.conv(x)x = self.norm(x)x = self.act(x)return xclass CoordinateAttention(nn.Module):"""坐標注意力模塊,用于將位置信息嵌入到通道注意力中。"""def __init__(self,in_chans,reduction=32,norm_cfg='BN',act_cfg='HSwish'):super(CoordinateAttention, self).__init__()self.in_chans = in_chanshidden_chans = max(8, in_chans // reduction)self.conv = ConvModule(in_chans, hidden_chans, kernel_size=1, norm_cfg=norm_cfg, act_cfg=act_cfg)self.attn_h = nn.Conv2d(hidden_chans, in_chans, 1)self.attn_w = nn.Conv2d(hidden_chans, in_chans, 1)self.sigmoid = nn.Sigmoid()def forward(self, x):b, c, h, w = x.size()# (b, c, h, 1)x_h = x.mean(3, keepdim=True)# (b, c, 1, w) -> (b, c, w, 1)x_w = x.mean(2, keepdim=True).permute(0, 1, 3, 2)# (b, c, h + w, 1)y = torch.cat((x_h, x_w), dim=2)y = self.conv(y)# split# x_h: (b, c, h, 1),  x_w: (b, c, w, 1)x_h, x_w = torch.split(y, [h, w], dim=2)# (b, c, 1, w)x_w = x_w.permute(0, 1, 3, 2)# compute attentiona_h = self.sigmoid(self.attn_h(x_h))a_w = self.sigmoid(self.attn_w(x_w))return x * a_w * a_h# 示例用法
if __name__ == "__main__":# 創建一個輸入特征圖,假設批次大小為1,通道數為64,高度和寬度為32input_tensor = torch.randn(1, 64, 32, 32)# 創建CA模塊實例,假設輸入通道數為64,中間通道數為16ca_module = CoordinateAttention(in_chans=64, reduction=32)# 前向傳播,獲取帶有坐標注意力的特征圖output_tensor = ca_module(input_tensor)print(output_tensor.shape)  # 輸出形狀應與輸入特征圖相同

第七站:SA(2021 ICASSP)- 洗牌注意力的創新

image-20250408230154393

核心思想

  • 通道重排:通過通道重排操作打亂通道順序,增強模型對通道特征的表達能力
  • 分組處理:將輸入特征圖的通道維度分成多個小組,分別計算通道注意力和空間注意力
  • 信息交流:通過通道重排操作將不同子特征之間的信息進行交流

結構示意:

image-20250408230216913
  • 結合空間和通道注意力

  • 通道重排技術:引入了通道重排,通過通道重排操作將通道順序打亂,以增強模型對通道特征的表達能力。

  • SA先將輸入特征圖的通道維度分成多個小組,并進行分組處理。然后,對于每個子特征,使用全局平均池化和組歸一化分別計算通道注意力和空間注意力。最后,使用通道重排操作將不同子特征之間的信息進行交流

性能表現:在輕量級模型中Top-1準確率提升3.8%,計算量僅增加1.5%

代碼實現:

import torch
import torch.nn as nnclass SA(nn.Module):"""Shuffle Attention"""def __init__(self, in_chans: int, group_num: int = 64):super(SA, self).__init__()  # 調用父類 (nn.Module) 的構造函數self.in_chans = in_chans  # 輸入的通道數self.group_num = group_num  # 分組數,默認為64# channel weight and bias:定義通道的權重和偏置self.c_w = nn.Parameter(torch.zeros((1, in_chans // (2 * group_num), 1, 1)), requires_grad=True)  # 通道的權重self.c_b = nn.Parameter(torch.ones((1, in_chans // (2 * group_num), 1, 1)), requires_grad=True)  # 通道的偏置# spatial weight and bias:定義空間的權重和偏置self.s_w = nn.Parameter(torch.zeros((1, in_chans // (2 * group_num), 1, 1)), requires_grad=True)  # 空間的權重self.s_b = nn.Parameter(torch.ones((1, in_chans // (2 * group_num), 1, 1)), requires_grad=True)  # 空間的偏置self.gn = nn.GroupNorm(in_chans // (2 * group_num), in_chans // (2 * group_num))  # 定義 GroupNorm 層,用于歸一化self.gate = nn.Sigmoid()  # 定義 Sigmoid 激活函數,用于生成門控權重@staticmethoddef channel_shuffle(x: torch.Tensor, groups: int):b, c, h, w = x.shape  # 獲取輸入張量的尺寸,b = batch size,c = 通道數,h = 高度,w = 寬度x = x.reshape(b, groups, -1, h, w)  # 將輸入張量按照 groups 分組,重塑形狀x = x.permute(0, 2, 1, 3, 4)  # 交換維度,使得通道分組在第二維# flatten:將分組后的張量展平x = x.reshape(b, -1, h, w)  # 將分組后的張量恢復為原始的通道數維度return x  # 返回重新排列后的張量def forward(self, x: torch.Tensor) -> torch.Tensor:b, c, h, w = x.size()  # 獲取輸入 x 的尺寸,b = batch size,c = 通道數,h = 高度,w = 寬度# (B, C, H, W) -> (B * G, C // G, H, W):將輸入張量按照分組數進行重塑x = x.reshape(b * self.group_num, -1, h, w)  # 將輸入按分組數分組并重新形狀x_0, x_1 = x.chunk(2, dim=1)  # 將張量 x 沿著通道維度 (dim=1) 切分成兩個部分,分別命名為 x_0 和 x_1# (B * G, C // 2G, H, W) -> (B * G, C // 2G, 1, 1):計算通道權重xc = x_0.mean(dim=(2, 3), keepdim=True)  # 對 x_0 在高寬維度 (dim=(2, 3)) 上取均值,得到每個通道的平均值xc = self.c_w * xc + self.c_b  # 乘以通道權重并加上通道偏置xc = x_0 * self.gate(xc)  # 使用 Sigmoid 激活函數進行門控操作,對 x_0 應用門控權重# (B * G, C // 2G, H, W) -> (B * G, C // 2G, 1, 1):計算空間權重xs = self.gn(x_1)  # 對 x_1 進行 GroupNorm 歸一化xs = self.s_w * xs + self.s_b  # 乘以空間權重并加上空間偏置xs = x_1 * self.gate(xs)  # 使用 Sigmoid 激活函數進行門控操作,對 x_1 應用門控權重out = torch.cat((xc, xs), dim=1)  # 在通道維度上拼接 xc 和 xs,得到最終的輸出out = out.reshape(b, -1, h, w)  # 恢復為原始的形狀 (B, C, H, W)out = self.channel_shuffle(out, 2)  # 對輸出張量進行通道洗牌,增加模型的表現力return out  # 返回最終的輸出張量if __name__ == '__main__':model = SA(in_chans=64,group_num=8)x = torch.rand((3, 64, 40, 40))print(model(x).size())

第八站 2023雙子星CPCA(Computers in Biology and Medicine 2023)

image-20250408230521030

核心貢獻:

  • 醫學圖像專用的通道先驗機制
  • 在腦腫瘤分割任務Dice系數提升6.8%

結構示意:

image-20250408230740385
  • 動態分配注意力權重:在通道和空間兩個維度上動態分配注意力權重,CBAM只是在通道動態。

  • 深度可分離卷積模塊:采用不同尺度的條形卷積核來提取像素間的空間映射關系,降低計算復雜度的同時,確保了有效信息的提取。

  • 通道先驗:通過輸入特征和通道注意力圖的元素相乘來獲得通道先驗,然后將通道先驗輸入到深度卷積模塊中以生成空間注意力圖

代碼實現:

import torch
import torch.nn as nnclass ChannelAttention(nn.Module):"""Channel attention module based on CPCAuse hidden_chans to reduce parameters instead of conventional convolution"""def __init__(self, in_chans: int, hidden_chans: int):super().__init__()self.fc1 = nn.Conv2d(in_chans, hidden_chans, kernel_size=1, stride=1, bias=True)self.fc2 = nn.Conv2d(hidden_chans, in_chans, kernel_size=1, stride=1, bias=True)self.act = nn.ReLU(inplace=True)self.in_chans = in_chansdef forward(self, x: torch.Tensor) -> torch.Tensor:"""x dim is (B, C, H, W)"""# (B, C, 1, 1)# 均值池化# 得到通道的全局描述,保留了通道的整體信息,但沒有考慮局部的最強特征x1 = x.mean(dim=(2, 3), keepdim=True)x1 = self.fc2(self.act(self.fc1(x1)))x1 = torch.sigmoid(x1)# (B, C, 1, 1)# 最大池化# 保留了通道中的局部最強特征,能夠突出圖像中最顯著的部分,而抑制較弱的特征x2 = x.max(dim=2, keepdim=True)[0].max(dim=3, keepdim=True)[0]x2 = self.fc2(self.act(self.fc1(x2)))x2 = torch.sigmoid(x2)x = x1 + x2x = x.view(-1, self.in_chans, 1, 1)return xclass CPCA(nn.Module):"""Channel Attention and Spatial Attention based on CPCA"""def __init__(self, in_chans, reduction= 16):super(CPCA, self).__init__()self.in_chans = in_chanshidden_chans = in_chans // reduction# 通道注意力(Channel Attention)self.ca = ChannelAttention(in_chans, hidden_chans)# 空間注意力(Spatial Attention)# 使用不同大小的卷積核來捕捉不同感受野的空間信息# 5x5深度可分離卷積self.dwc5_5 = nn.Conv2d(in_chans, in_chans, kernel_size=5, padding=2, groups=in_chans)# 1x7深度可分離卷積self.dwc1_7 = nn.Conv2d(in_chans, in_chans, kernel_size=(1, 7), padding=(0, 3), groups=in_chans)# 7x1深度可分離卷積self.dwc7_1 = nn.Conv2d(in_chans, in_chans, kernel_size=(7, 1), padding=(3, 0), groups=in_chans)# 1x11深度可分離卷積self.dwc1_11 = nn.Conv2d(in_chans, in_chans, kernel_size=(1, 11), padding=(0, 5), groups=in_chans)# 11x1深度可分離卷積self.dwc11_1 = nn.Conv2d(in_chans, in_chans, kernel_size=(11, 1), padding=(5, 0), groups=in_chans)# 1x21深度可分離卷積self.dwc1_21 = nn.Conv2d(in_chans, in_chans, kernel_size=(1, 21), padding=(0, 10), groups=in_chans)# 21x1深度可分離卷積self.dwc21_1 = nn.Conv2d(in_chans, in_chans, kernel_size=(21, 1), padding=(10, 0), groups=in_chans)# 用于建模不同感受野之間的特征連接self.conv = nn.Conv2d(in_chans, in_chans, kernel_size=1, padding=0)self.act = nn.GELU()def forward(self, x):x = self.conv(x)  # 先用1x1卷積壓縮通道x = self.act(x)channel_attn = self.ca(x) # 計算通道注意力x = channel_attn * x # 通道注意力加權輸入特征# 計算空間注意力部分x_init = self.dwc5_5(x) # 先通過5x5卷積x1 = self.dwc1_7(x_init) # 1x7卷積x1 = self.dwc7_1(x1) # 7x1卷積x2 = self.dwc1_11(x_init) # 1x11卷積x2 = self.dwc11_1(x2) # 11x1卷積x3 = self.dwc1_21(x_init)  # 1x21卷積x3 = self.dwc21_1(x3)  # 21x1卷積# 合并不同卷積結果,形成空間注意力spatial_atn = x1 + x2 + x3 + x_init # 將不同感受野的特征求和spatial_atn = self.conv(spatial_atn)  # 再通過1x1卷積進行處理y = x * spatial_atn # 將通道加權后的特征與空間注意力加權的特征相乘y = self.conv(y)return yif __name__ == '__main__':input_tensor = torch.randn(1, 64, 32, 32)attention_module = CPCA(in_chans=64)output_tensor = attention_module(input_tensor)print(output_tensor.shape)

第九站 :2023雙子星之EMA**(ICASSP 2023)**

image-20250408230928389

核心思想:

  • 多尺度并行子網絡捕獲跨維度交互
  • 在目標檢測任務中mAP提升4.2%

結構示意:

image-20250408230955135
  • 通道和空間注意力的結合

  • 多尺度并行子網絡:包括一個處理1x1卷積核和一個處理3x3卷積核的并行子網絡。這種結構有助于有效捕獲跨維度交互作用。

  • 坐標注意力(CA)的再審視:EMA模塊在坐標注意力(CA)的基礎上進行了改進和優化。CA模塊通過將位置信息嵌入通道注意力圖中,實現了跨通道和空間信息的融合。EMA模塊在此基礎上進一步發展,通過并行子網絡塊有效捕獲跨維度交互作用,建立不同維度之間的依賴關系

代碼實現:

import torch
import torch.nn as nnclass EMA(nn.Module):"""EMA: Exponential Moving Average (指數移動平均)"""def __init__(self, channels, factor=32):super(EMA, self).__init__()  # 調用父類 (nn.Module) 的構造函數self.groups = factor  # 將 factor 賦值給 groups,決定分組數量assert channels // self.groups > 0  # 確保通道數可以被分組數整除,避免出錯self.softmax = nn.Softmax(-1)  # 定義 Softmax 激活函數,在最后一維進行歸一化self.agp = nn.AdaptiveAvgPool2d((1, 1))  # 定義自適應平均池化層,將輸入大小壓縮為 1x1self.pool_h = nn.AdaptiveAvgPool2d((None, 1))  # 定義自適應池化層,按高度進行池化self.pool_w = nn.AdaptiveAvgPool2d((1, None))  # 定義自適應池化層,按寬度進行池化self.gn = nn.GroupNorm(channels // self.groups, channels // self.groups)  # 定義 GroupNorm 層self.conv1x1 = nn.Conv2d(channels // self.groups, channels // self.groups, kernel_size=1, stride=1,padding=0)  # 定義 1x1 卷積層self.conv3x3 = nn.Conv2d(channels // self.groups, channels // self.groups, kernel_size=3, stride=1,padding=1)  # 定義 3x3 卷積層def forward(self, x):b, c, h, w = x.size()  # 獲取輸入 x 的尺寸,分別為 batch size (b), 通道數 (c), 高度 (h), 寬度 (w)group_x = x.reshape(b * self.groups, -1, h, w)  # 將輸入 x 重塑為 (b * groups, c//groups, h, w),用于分組處理x_h = self.pool_h(group_x)  # 對每組的輸入進行高度方向上的池化x_w = self.pool_w(group_x).permute(0, 1, 3, 2)  # 對每組的輸入進行寬度方向上的池化,并調整維度順序hw = self.conv1x1(torch.cat([x_h, x_w], dim=2))  # 將池化后的結果在維度2 (高度和寬度) 上拼接,然后通過 1x1 卷積層x_h, x_w = torch.split(hw, [h, w], dim=2)  # 將卷積后的結果分割為高度和寬度兩個部分x1 = self.gn(group_x * x_h.sigmoid() * x_w.permute(0, 1, 3, 2).sigmoid())  # 對分組后的輸入做加權操作并通過 GroupNormx2 = self.conv3x3(group_x)  # 對分組后的輸入做 3x3 卷積操作x11 = self.softmax(self.agp(x1).reshape(b * self.groups, -1, 1).permute(0, 2, 1))  # 對 x1 進行池化,Softmax 歸一化x12 = x2.reshape(b * self.groups, c // self.groups, -1)  # 將 x2 重塑為形狀 (b*g, c//g, hw)x21 = self.softmax(self.agp(x2).reshape(b * self.groups, -1, 1).permute(0, 2, 1))  # 對 x2 進行池化,Softmax 歸一化x22 = x1.reshape(b * self.groups, c // self.groups, -1)  # 將 x1 重塑為形狀 (b*g, c//g, hw)# 計算加權矩陣weights = (torch.matmul(x11, x12) + torch.matmul(x21, x22)).reshape(b * self.groups, 1, h, w)# 將加權后的結果與輸入相乘,并恢復為原始輸入形狀return (group_x * weights.sigmoid()).reshape(b, c, h, w)if __name__ == '__main__':model = EMA(channels=64)x = torch.rand((3, 64, 40, 40))print(model(x).size())

終章:SSCA(2024 arXiv)- 空間×通道的協同革命

image-20250408231253619

技術亮點

  • 空間注意力(SMSA)

    • 4路特征拆分+多核1D卷積(3/5/7/9)

    • Concat+GN+Sigmoid生成空間權重

  • 通道注意力(PCSA)

    • 漸進式特征池化+自注意力矩陣計算

    • 在MobileNetV2上Top-1準確率提升5.1%

結構示意:

image-20250408231405525

SMSA空間注意力實現原理

  • **平均池化:**分別沿高度和寬度方向

  • **特征拆分:**4等分

  • **特征提取:**四個深度共享1D卷積(卷積核大小分別為3,5,7,9)對各部分特征進行處理

  • 空間注意力計算:Concat+GN+Sigmoid

PCSA 通道自注意力實現原理(漸進式)

  • 下采樣:可選平均池化、最大池化或重組合

  • 特征歸一化與變換:GN+1×1深度卷積生成查詢(q)、鍵(k)和值(v)

  • 注意力矩陣計算:

代碼實現:

import typing as t
import torch
import torch.nn as nn
from einops import rearrangeclass SCSA(nn.Module):def __init__(self,dim: int,head_num: int,window_size: int = 7,group_kernel_sizes: t.List[int] = [3, 5, 7, 9],qkv_bias: bool = False,fuse_bn: bool = False,down_sample_mode: str = 'avg_pool',attn_drop_ratio: float = 0.,gate_layer: str = 'sigmoid',):super(SCSA, self).__init__()  # 調用 nn.Module 的構造函數self.dim = dim  # 特征維度self.head_num = head_num  # 注意力頭數self.head_dim = dim // head_num  # 每個頭的維度self.scaler = self.head_dim ** -0.5  # 縮放因子self.group_kernel_sizes = group_kernel_sizes  # 分組卷積核大小self.window_size = window_size  # 窗口大小self.qkv_bias = qkv_bias  # 是否使用偏置self.fuse_bn = fuse_bn  # 是否融合批歸一化self.down_sample_mode = down_sample_mode  # 下采樣模式assert self.dim % 4 == 0, 'The dimension of input feature should be divisible by 4.'  # 確保維度可被4整除self.group_chans = group_chans = self.dim // 4  # 分組通道數# 定義局部和全局深度卷積層self.local_dwc = nn.Conv1d(group_chans, group_chans, kernel_size=group_kernel_sizes[0],padding=group_kernel_sizes[0] // 2, groups=group_chans)self.global_dwc_s = nn.Conv1d(group_chans, group_chans, kernel_size=group_kernel_sizes[1],padding=group_kernel_sizes[1] // 2, groups=group_chans)self.global_dwc_m = nn.Conv1d(group_chans, group_chans, kernel_size=group_kernel_sizes[2],padding=group_kernel_sizes[2] // 2, groups=group_chans)self.global_dwc_l = nn.Conv1d(group_chans, group_chans, kernel_size=group_kernel_sizes[3],padding=group_kernel_sizes[3] // 2, groups=group_chans)# 注意力門控層self.sa_gate = nn.Softmax(dim=2) if gate_layer == 'softmax' else nn.Sigmoid()self.norm_h = nn.GroupNorm(4, dim)  # 水平方向的歸一化self.norm_w = nn.GroupNorm(4, dim)  # 垂直方向的歸一化self.conv_d = nn.Identity()  # 直接連接self.norm = nn.GroupNorm(1, dim)  # 通道歸一化# 定義查詢、鍵和值的卷積層self.q = nn.Conv2d(in_channels=dim, out_channels=dim, kernel_size=1, bias=qkv_bias, groups=dim)self.k = nn.Conv2d(in_channels=dim, out_channels=dim, kernel_size=1, bias=qkv_bias, groups=dim)self.v = nn.Conv2d(in_channels=dim, out_channels=dim, kernel_size=1, bias=qkv_bias, groups=dim)self.attn_drop = nn.Dropout(attn_drop_ratio)  # 注意力丟棄層self.ca_gate = nn.Softmax(dim=1) if gate_layer == 'softmax' else nn.Sigmoid()  # 通道注意力門控# 根據窗口大小和下采樣模式選擇下采樣函數if window_size == -1:self.down_func = nn.AdaptiveAvgPool2d((1, 1))  # 自適應平均池化else:if down_sample_mode == 'recombination':self.down_func = self.space_to_chans  # 重組合下采樣# 維度降低self.conv_d = nn.Conv2d(in_channels=dim * window_size ** 2, out_channels=dim, kernel_size=1, bias=False)elif down_sample_mode == 'avg_pool':self.down_func = nn.AvgPool2d(kernel_size=(window_size, window_size), stride=window_size)  # 平均池化elif down_sample_mode == 'max_pool':self.down_func = nn.MaxPool2d(kernel_size=(window_size, window_size), stride=window_size)  # 最大池化def forward(self, x: torch.Tensor) -> torch.Tensor:"""輸入張量 x 的維度為 (B, C, H, W)"""# 計算空間注意力優先級b, c, h_, w_ = x.size()  # 獲取輸入的形狀# (B, C, H)x_h = x.mean(dim=3)  # 沿著寬度維度求平均l_x_h, g_x_h_s, g_x_h_m, g_x_h_l = torch.split(x_h, self.group_chans, dim=1)  # 拆分通道# (B, C, W)x_w = x.mean(dim=2)  # 沿著高度維度求平均l_x_w, g_x_w_s, g_x_w_m, g_x_w_l = torch.split(x_w, self.group_chans, dim=1)  # 拆分通道# 計算水平注意力x_h_attn = self.sa_gate(self.norm_h(torch.cat((  self.local_dwc(l_x_h),self.global_dwc_s(g_x_h_s),self.global_dwc_m(g_x_h_m),self.global_dwc_l(g_x_h_l),), dim=1)))x_h_attn = x_h_attn.view(b, c, h_, 1)  # 調整形狀# 計算垂直注意力x_w_attn = self.sa_gate(self.norm_w(torch.cat((  self.local_dwc(l_x_w),self.global_dwc_s(g_x_w_s),self.global_dwc_m(g_x_w_m),self.global_dwc_l(g_x_w_l)), dim=1)))x_w_attn = x_w_attn.view(b, c, 1, w_)  # 調整形狀# 計算最終的注意力加權x = x * x_h_attn * x_w_attn# 基于自注意力的通道注意力# 減少計算量y = self.down_func(x)  # 下采樣y = self.conv_d(y)  # 維度轉換_, _, h_, w_ = y.size()  # 獲取形狀# 先歸一化,然后重塑 -> (B, H, W, C) -> (B, C, H * W),并生成 q, k 和 vy = self.norm(y)  # 歸一化q = self.q(y)  # 計算查詢k = self.k(y)  # 計算鍵v = self.v(y)  # 計算值# (B, C, H, W) -> (B, head_num, head_dim, N)q = rearrange(q, 'b (head_num head_dim) h w -> b head_num head_dim (h w)', head_num=int(self.head_num),head_dim=int(self.head_dim))k = rearrange(k, 'b (head_num head_dim) h w -> b head_num head_dim (h w)', head_num=int(self.head_num),head_dim=int(self.head_dim))v = rearrange(v, 'b (head_num head_dim) h w -> b head_num head_dim (h w)', head_num=int(self.head_num),head_dim=int(self.head_dim))# 計算注意力attn = q @ k.transpose(-2, -1) * self.scaler  # 點積注意力計算attn = self.attn_drop(attn.softmax(dim=-1))  # 應用注意力丟棄# (B, head_num, head_dim, N)attn = attn @ v  # 加權值# (B, C, H_, W_)attn = rearrange(attn, 'b head_num head_dim (h w) -> b (head_num head_dim) h w', h=int(h_), w=int(w_))# (B, C, 1, 1)attn = attn.mean((2, 3), keepdim=True)  # 求平均attn = self.ca_gate(attn)  # 應用通道注意力門控return attn * x  # 返回加權后的輸入if __name__ == "__main__":#參數: dim特征維度; head_num注意力頭數; window_size = 7 窗口大小scsa = SCSA(dim=32, head_num=8, window_size=7)# 隨機生成輸入張量 (B, C, H, W)input_tensor = torch.rand(1, 32, 256, 256)# 打印輸入張量的形狀print(f"輸入張量的形狀: {input_tensor.shape}")# 前向傳播output_tensor = scsa(input_tensor)# 打印輸出張量的形狀print(f"輸出張量的形狀: {output_tensor.shape}")

后續發展:注意力機制的三個趨勢

  1. 高效性:向輕量化、無參數方向發展(如ECA、ELA)
  2. 多模態融合:探索視覺+語言的跨模態注意力
  3. 自適應機制:動態調整計算資源分配

結語

從SE到SSCA,注意力機制正從"單一增強"走向"協同進化"。下一個突破點會是動態可重構的注意力嗎?讓我們共同見證深度學習的新篇章!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/76139.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/76139.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/76139.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux/樹莓派網絡配置、遠程登錄與圖形界面訪問實驗

一.準備工作 1.修改網絡適配器(選擇本機網卡) 2.創建一個新的用戶。 3.使用新用戶登錄,使用ip a指令查看IP(現代 Linux 發行版(如 Ubuntu、Debian、CentOS、Fedora 等))。 通過sudo arp-sca…

Python----TensorFlow(TensorFlow介紹,安裝,主要模塊,高級功能)

一、TensorFlow TensorFlow 是由谷歌大腦團隊于 2015 年推出的開源機器學習框架。作為深度學習的第二代系統,TensorFlow 支持多種編程語言,包括 Python、C、Java 和 Go,廣泛應用于 CNN、RNN 和 GAN 等深度學習算法。 TensorFlow 可以…

【動態規劃】 深入動態規劃 回文子串問題

文章目錄 前言例題一、回文子串二、 最長回文子串三、回文串分割IV四、分割回文串II五、最長回文子序列六、讓字符串成為回文串的最小插入次數 結語 前言 那么,什么是動態規劃中的回文子串問題呢? 動態規劃中的回文子串問題是一個經典的字符串處理問題。…

lodash庫介紹(一個現代JavaScript實用工具庫,提供模塊化、性能優化和額外功能)JavaScript庫(防抖、節流、函數柯里化)JS庫

https://www.lodashjs.com/ 文章目錄 Lodash庫全解析簡介核心優勢一致性API模塊化設計性能優化 常用功能分類數組操作對象操作函數增強 高級應用場景數據轉換鏈函數組合 性能考量大數據集處理 最佳實踐按需引入利用FP模塊 結語 Lodash庫全解析 簡介 Lodash是一個現代JavaScri…

Spring MVC 國際化機制詳解(MessageSource 接口體系)

Spring MVC 國際化機制詳解(MessageSource 接口體系) 1. 核心接口與實現類詳解 接口/類名描述功能特性適用場景MessageSource核心接口,定義消息解析能力支持參數化消息(如{0}占位符)所有國際化場景的基礎接口Resource…

PyTorch張量范數計算終極指南:從基礎到高階實戰

在深度學習領域,張量范數計算是模型正則化、梯度裁剪、特征歸一化的核心技術。本文將以20代碼實例,深度剖析torch.norm的9大核心用法,并揭示其在Transformer模型中的關鍵應用場景。 🚀 快速入門(5分鐘掌握核心操作&…

榮耀90 GT信息

外觀設計 屏幕:采用 6.7 英寸 AMOLED 榮耀綠洲護眼屏,超窄邊框設計,其上邊框 1.6mm,左右黑邊 1.25mm,屏占較高,帶來更廣闊的視覺體驗。屏幕還支持 120Hz 自由刷新率,可根據使用場景自動切換刷新…

【Java中級】11章、枚舉 - java引用數據類型,枚舉介紹、快速入門,了解枚舉類的基本使用方式【1】

文章內容: 自定義實現枚舉enum關鍵字實現枚舉 ??內容涉及枚舉的定義,快速入門,注意事項和小題鞏固知識點 🌈 跟著B站一位老師學習的內部類內容,現寫這篇文章為學習內部類的小伙伴提供思路支持,希望可以一…

局域網訪問 Redis 方法

局域網訪問 Redis 方法 默認情況下,Redis 只允許本機 (127.0.0.1) 訪問。如果你想讓局域網中的其他設備訪問 Redis,需要 修改 Redis 配置,并確保 防火墻放行端口。 方法 1:修改 Redis 配置 1. 修改 redis.conf(或 me…

如何應對客戶頻繁變更需求

如何應對客戶頻繁變更需求?要點包括: 快速響應、深入溝通、靈活規劃、過程記錄、風險管控。這些策略既能降低項目失控風險,也能幫助團隊在變動環境中保持高效率。其中深入溝通尤為關鍵,它不僅能夠讓團隊第一時間了解客戶意圖&…

Set 集合

默認情況下, Scala 使用的是不可變集合, 如果你想使用可變集合, 需要引用 scala.collection.mutable.Set Set 默認是不可變集合,數據無序 數據不可重復 遍歷集合 創建可變集合 mutable.Set 打印集合 集合添加元素 向集合中…

最新 OpenHarmony 系統一二級目錄整理

我們在學習 OpenHarmony 的時候,如果對系統的目錄結構了解,那么無疑會提升自己對 OpenHarmony 更深層次的認識。 于是就有了今天的整理。 首先在此之前,我們要獲取源碼 獲取源碼的方式 OpenHarmony 主干代碼獲取 方式一(推薦&am…

STL常用容器整理

STL常用容器操作整理 STL常用容器操作整理(string/vector/set/map)一、string(字符串)構造函數元素訪問修改操作容量操作子串與查找 二、vector(動態數組)構造函數元素訪問修改操作容量操作 三、set&#x…

Unity 實現傷害跳字

核心組件: Dotween TextMeshPro 過程軌跡如下圖: 代碼如下: using System.Collections; using System.Collections.Generic; using DG.Tweening; using TMPro; using UnityEngine; using UnityEngine.Pool;public class …

Ubuntu 22.04 AI大模型環境配置及常用工具安裝

一、基礎環境準備 1.1 系統準備 建議使用 Ubuntu22.04 以下配置皆以 Ubuntu22.04 系統版本為例 1.2 安裝git apt-get update && apt-get install git -y1.3 安裝 Python 3.9 【建議安裝 3.10】(安裝miniconda或者conda來管理虛擬環境) wget …

STM32單片機入門學習——第27節: [9-3] USART串口發送串口發送+接收

寫這個文章是用來學習的,記錄一下我的學習過程。希望我能一直堅持下去,我只是一個小白,只是想好好學習,我知道這會很難,但我還是想去做! 本文寫于:2025.04.08 STM32開發板學習——第27節: [9-3] USART串口發送&串口發送接收 前言開發板說…

前端實現docx格式word文件預覽,可以兼容原生、vue2、以及uni-app 項目,詳細步驟。

上一篇記錄了PDF文件預覽功能。這一篇記錄下docx文件預覽。 核心文件 doc.html <script src"./build/polyfill.min.js"></script> <script src"./build/jszip.min.js"></script> <script src"./build/docx-preview.js&…

Java中的ArrayList方法

1. 創建 ArrayList 實例 你可以通過多種方式創建 ArrayList 實例&#xff1a; <JAVA> ArrayList<String> list new ArrayList<>(); // 創建一個空的 ArrayList ArrayList<String> list new ArrayList<>(10); // 創建容量為 10 的 ArrayList …

【anaconda下py】

38 https://repo.anaconda.com/archive/Anaconda3-2020.11-Windows-x86.exe 39 https://repo.anaconda.com/archive/Anaconda3-2022.10-Windows-x86_64.exe 310https://repo.anaconda.com/archive/Anaconda3-2023.03-0-Windows-x86_64.exe 歷史列表Index of /archive 遠程&…

linux--------------進程控制(下)

一、進程等待 1.1 進程等待必要性 子進程退出后&#xff0c;若父進程不管不顧&#xff0c;可能會產生 “僵尸進程”&#xff0c;進而造成內存泄漏。進程一旦變為僵尸狀態&#xff0c;即使使用 kill -9 也無法將其殺死&#xff0c;因為無法殺死一個已死的進程。父進程需要了解…