news 2026/10/15 19:48:38

Day 50 CBAM 注意力机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Day 50 CBAM 注意力机制

1.1 什么是 CBAM?
CBAM (Convolutional Block Attention Module) 是一种能够集成到任何卷积神经网络架构中的注意力模块。

1.2 核心目标
通过学习的方式,自动获取特征图在通道和空间维度上的重要性
对特征图进行自适应调整,增强重要特征,抑制不重要特征
提升模型的特征表达能力和性能
简单来说,CBAM 就像是给模型装上了"智能眼镜",让模型能够更精准地看到图像中关键的部分。

1.3 CBAM 的组成
CBAM 由两个主要部分组成:

  • 通道注意力模块(Channel Attention):分析"哪些通道的特征更关键"
  • 空间注意力模块(Spatial Attention):定位"关键特征在图像中的具体位置"
1.4 CBAM vs SE 注意力
  • SE 通道注意力的局限:仅关注"哪些通道重要",未考虑"重要信息在空间中的位置"
  • CBAM 的突破:二者结合,让模型同时学会"关注什么"和"关注哪里"
1. 第一步:通道注意力(Channel Attention Module, CAM)

核心目标:判断「哪些通道的特征更重要」。比如在识别猫的任务中,「猫耳朵」「猫爪子」对应的通道特征比「背景纹理」对应的通道特征更重要。

工作流程(超简化版):

  1. 对特征图[C, H, W]做全局池化,把每个通道的H×W大小的特征,压缩成1 个数值,得到一个[C, 1, 1]的向量(这个向量代表了每个通道的「全局重要性」)。
    • 这里用了两种池化:全局平均池化(GAP)和全局最大池化(GMP),目的是获取更全面的信息。
  2. 把这两个池化后的向量,输入一个简单的两层神经网络(MLP),分别得到两个[C, 1, 1]的权重向量。
  3. 把两个权重向量相加,再经过一个sigmoid函数(把数值压缩到 0~1 之间),得到最终的通道注意力权重。
  4. 把这个权重和原来的特征图相乘—— 重要通道的特征会被放大,不重要的会被削弱。
2. 第二步:空间注意力(Spatial Attention Module, SAM)

核心目标:判断「特征图的哪些空间位置更重要」。还是识别猫的任务,特征图里「猫所在的位置」比「背景位置」更重要。

工作流程(超简化版):

  1. 把第一步通道注意力处理后的特征图[C, H, W],在通道维度做池化:计算每个空间位置(每个H×W点)的平均值和最大值,得到两个[1, H, W]的特征图。
  2. 把这两个特征图拼接在一起,变成[2, H, W]。
  3. 用一个3×3 的卷积层对拼接后的特征图降维,把通道数从 2 变回 1,得到[1, H, W]的特征图。
  4. 经过sigmoid函数得到空间注意力权重(0~1 之间)。
  5. 把这个权重和第一步处理后的特征图相乘—— 重要空间位置的特征会被放大,不重要的会被削弱。
3. 最终输出

经过「通道注意力 → 空间注意力」两步处理后,得到的特征图就是被注意力机制增强后的特征,可以直接传入下一个卷积块继续训练。

三、CBAM 的优势:为啥大家都爱用?

  1. 轻量级:参数量极少,几乎不会增加模型的计算负担,适合嵌入各种 CNN 模型(比如 ResNet、MobileNet)。
  2. 即插即用:不需要修改原网络的主体结构,直接加在卷积块的末尾或开头就行。
  3. 双维度注意力:同时关注「通道」和「空间」两个维度,比只做单维度注意力的效果更好。

四、小白友好的总结

CBAM 就像一个「特征筛选器」:

  • 先筛选有用的特征种类(通道注意力),再筛选有用的特征位置(空间注意力);
  • 让模型把精力集中在关键信息上,从而提升识别、分类等任务的准确率。
import torch import torch.nn as nn import torch.nn.functional as F # 第一步:实现通道注意力模块(CAM) class ChannelAttention(nn.Module): def __init__(self, in_channels, reduction_ratio=16): """ 参数说明(小白版): - in_channels: 输入特征图的通道数(比如ResNet的卷积块输出通道数是64/128等) - reduction_ratio: 压缩系数(默认16,目的是减少MLP的参数量,不用改) """ super(ChannelAttention, self).__init__() # 全局平均池化:[B, C, H, W] → [B, C, 1, 1] self.avg_pool = nn.AdaptiveAvgPool2d(1) # 全局最大池化:[B, C, H, W] → [B, C, 1, 1] self.max_pool = nn.AdaptiveMaxPool2d(1) # 两层MLP(全连接层):压缩通道数再还原,减少计算量 self.fc = nn.Sequential( # 第一层:把通道数从 C 压缩到 C//reduction_ratio nn.Linear(in_channels, in_channels // reduction_ratio, bias=False), nn.ReLU(inplace=True), # 激活函数,增加非线性 # 第二层:把通道数还原回 C nn.Linear(in_channels // reduction_ratio, in_channels, bias=False) ) # sigmoid函数:把数值压缩到0~1之间,作为注意力权重 self.sigmoid = nn.Sigmoid() def forward(self, x): """ 前向传播(核心逻辑): x: 输入特征图,形状 [B, C, H, W] """ # 平均池化 + MLP avg_out = self.fc(self.avg_pool(x).view(x.size(0), -1)) # [B, C] # 最大池化 + MLP max_out = self.fc(self.max_pool(x).view(x.size(0), -1)) # [B, C] # 两个结果相加 → [B, C] → 变形为 [B, C, 1, 1] out = avg_out + max_out out = self.sigmoid(out).view(x.size(0), x.size(1), 1, 1) # 注意力权重 × 原特征图(逐通道相乘) return x * out # 第二步:实现空间注意力模块(SAM) class SpatialAttention(nn.Module): def __init__(self, kernel_size=3): """ 参数说明: - kernel_size: 卷积核大小(默认3,必须是奇数,不用改) """ super(SpatialAttention, self).__init__() # 卷积层:把通道数从2(平均+最大池化)降为1 self.conv = nn.Conv2d(2, 1, kernel_size, padding=kernel_size//2, bias=False) self.sigmoid = nn.Sigmoid() def forward(self, x): """ 前向传播: x: 通道注意力处理后的特征图,形状 [B, C, H, W] """ # 在通道维度做平均池化:[B, C, H, W] → [B, 1, H, W] avg_out = torch.mean(x, dim=1, keepdim=True) # 在通道维度做最大池化:[B, C, H, W] → [B, 1, H, W] max_out, _ = torch.max(x, dim=1, keepdim=True) # 拼接两个池化结果:[B, 2, H, W] out = torch.cat([avg_out, max_out], dim=1) # 卷积降维 + sigmoid → [B, 1, H, W](空间注意力权重) out = self.sigmoid(self.conv(out)) # 注意力权重 × 原特征图(逐空间位置相乘) return x * out # 第三步:组合通道+空间注意力,实现完整的CBAM class CBAM(nn.Module): def __init__(self, in_channels, reduction_ratio=16, kernel_size=3): super(CBAM, self).__init__() self.channel_att = ChannelAttention(in_channels, reduction_ratio) # 通道注意力 self.spatial_att = SpatialAttention(kernel_size) # 空间注意力 def forward(self, x): """ 完整CBAM前向传播:先通道注意力,再空间注意力 x: 输入特征图 [B, C, H, W] return: 增强后的特征图 [B, C, H, W] """ x = self.channel_att(x) # 第一步:通道注意力 x = self.spatial_att(x) # 第二步:空间注意力 return x # --------------- 测试代码:小白可以直接运行看效果 --------------- if __name__ == "__main__": # 模拟一个CNN的特征图:批量大小=2,通道数=64,高=32,宽=32 # (这个形状是CNN中很常见的,比如ResNet18的第一层卷积输出) fake_feature = torch.randn(2, 64, 32, 32) # 创建CBAM模块:输入通道数=64(和特征图通道数一致) cbam = CBAM(in_channels=64) # 用CBAM处理特征图 enhanced_feature = cbam(fake_feature) # 打印输入/输出形状(验证:形状不变,只是特征被增强) print(f"输入特征图形状: {fake_feature.shape}") print(f"输出特征图形状: {enhanced_feature.shape}")

@浙大疏锦行

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 2:25:14

【C/C++】C语言内存函数

memcpy使用和模拟实现memcpy可以代替strcpy代码语言&#xff1a;javascriptAI代码解释void * memcpy ( void * destination, const void * source, size_t num );//void*来接受任意指针,size_t 单位是字节 //memcpy的头文件为<string.h> mem是memory的缩写 是内存的意思…

作者头像 李华
网站建设 2026/10/15 6:50:17

【C/C++】字符函数和字符串函数

字符函数和字符串函数1.字符分类函数C语⾔中有⼀系列的函数是专⻔做字符分类的&#xff0c;也就是⼀个字符是属于什么类型的字符的。 这些函数的使⽤都需要包含⼀个头⽂件是 ctype.h在这里插入图片描述这些函数的使⽤⽅法⾮常类似&#xff0c;我们就讲解⼀个函数的事情&#xf…

作者头像 李华
网站建设 2026/10/15 15:05:07

【C/C++】深入理解指针(一)

1.1 内存在讲内存和地址之前&#xff0c;我们想有个⽣活中的案例&#xff1a; 假设有⼀栋宿舍楼&#xff0c;把你放在楼⾥&#xff0c;楼上有100个房间&#xff0c;但是房间没有编号&#xff0c;你的⼀个朋友来找你玩&#xff0c; 如果想找到你&#xff0c;就得挨个房⼦去找&am…

作者头像 李华
网站建设 2026/10/15 15:47:37

PyTorch-CUDA-v2.6镜像部署Flask API对外提供模型服务

PyTorch-CUDA-v2.6 镜像部署 Flask API 对外提供模型服务 在深度学习模型从实验室走向生产环境的过程中&#xff0c;一个常见但棘手的问题是&#xff1a;为什么训练好的模型一到线上就“水土不服”&#xff1f; 可能是依赖版本不一致、GPU 环境缺失、CUDA 编译失败&#xff0c;…

作者头像 李华
网站建设 2026/10/14 18:40:27

CSS3 新增文本属性

一、文本阴影二、文本换行三、文本溢出四、文本修饰

作者头像 李华