news 2026/6/15 19:53:01

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/6/15 12:19:39

PyTorch-CUDA-v2.6镜像部署OCR识别系统实战案例

PyTorch-CUDA-v2.6镜像部署OCR识别系统实战案例 在智能文档处理、自动化办公和工业质检等场景中,光学字符识别(OCR)正从“辅助功能”演变为关键的生产力引擎。然而,许多团队在落地OCR系统时仍面临一个共同困境:模型明…

作者头像 李华
网站建设 2026/6/15 11:19:24

PyTorch-CUDA-v2.6镜像部署RAG检索增强生成系统实战

PyTorch-CUDA-v2.6镜像部署RAG检索增强生成系统实战 在当前大模型驱动的AI浪潮中,如何快速构建一个既能准确回答问题、又能实时调用最新知识的智能系统,已经成为企业与研究团队的核心诉求。传统的语言模型虽然生成能力强,但容易“一本正经地胡…

作者头像 李华
网站建设 2026/6/15 12:26:42

PyTorch-CUDA-v2.6镜像中实现梯度裁剪防止训练爆炸

PyTorch-CUDA-v2.6镜像中实现梯度裁剪防止训练爆炸 在深度学习模型日益复杂、参数量动辄上亿的今天,一个看似微小的技术细节——梯度值异常增大,却可能让数小时甚至数天的训练功亏一篑。你是否曾遇到过这样的场景:模型刚开始训练,…

作者头像 李华
网站建设 2026/6/15 12:30:18

PyTorch-CUDA-v2.6镜像中使用Optuna进行超参数搜索

PyTorch-CUDA-v2.6 镜像中集成 Optuna 实现高效超参数搜索 在深度学习项目开发过程中,一个常见的瓶颈并非模型设计本身,而是如何快速找到一组能让模型性能显著提升的超参数组合。更棘手的是,即便你找到了“好”的参数,换一台机器或…

作者头像 李华
网站建设 2026/6/15 11:35:08

内存管理:避免内存泄漏的方法

在 JavaScript 开发中,内存管理是一个至关重要的话题,合理的内存管理能够避免内存泄漏,提高应用程序的性能和稳定性。本文将深入探讨 JavaScript 中的内存管理机制,以及如何避免内存泄漏的发生。1. 内存管理基础 1.1 内存生命周期…

作者头像 李华
网站建设 2026/6/15 11:36:02

事件委托:优化事件处理性能

在前端开发中,事件处理是构建交互性页面的关键部分。然而,随着页面元素数量的增加和交互复杂度的提升,事件处理的性能问题逐渐凸显。事件委托作为一种有效的优化策略,可以显著提升事件处理的效率,减少内存占用。本文将…

作者头像 李华