news 2026/3/2 4:47:20

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/2/27 4:44:07

【Hot100-Java中等】:字母异位词分组

在 LeetCode 的字符串题目中,“字母异位词” (Anagrams) 是一个非常高频的概念。这道第 49 题不仅考察了哈希表(HashMap)的应用,更是一个理解 Java 对象机制的绝佳案例。1. 解决方案一:排序数组分类 (Sorting)这是最符…

作者头像 李华
网站建设 2026/2/28 12:25:46

猜测心跳包机制的核心逻辑

一、心跳包的作用定时发送一个数据包,等待对方回复。如果对方在规定时间内回复,说明连接正常;否则出现异常或者收不到回复,说明工作不正常。二、主要逻辑(一)定义心跳包的格式1、发送1字节类型(值为10)2字节标识码(值为0xAABB)2、…

作者头像 李华
网站建设 2026/2/27 22:54:24

ZStack网络层配置实战案例解析

ZStack网络层配置实战:从零搭建高可用私有云网络一场“虚拟机上不了网”的深夜救火凌晨两点,运维群里弹出一条消息:“新创建的生产环境虚拟机拿不到IP,服务部署卡住了!”——这不是演习,而是某金融企业私有…

作者头像 李华
网站建设 2026/2/28 21:10:18

PyTorch-CUDA-v2.6镜像部署ChatGLM3-6B对话模型完整流程

PyTorch-CUDA-v2.6镜像部署ChatGLM3-6B对话模型完整流程 在当前大模型快速落地的浪潮中,如何高效、稳定地将一个千亿参数级的语言模型从实验环境推向生产服务,是每个AI工程师都面临的现实挑战。尤其是在中文场景下,ChatGLM3-6B 作为一款具备强…

作者头像 李华
网站建设 2026/3/2 2:59:24

Grid与vh单位协同工作的原理图解说明

Grid 与vh单位的完美搭档:构建真正自适应全屏布局你有没有遇到过这样的问题——明明写了height: 100%,页面却没撑满屏幕?或者在手机上打开网页时,底部突然被裁掉一截,用户根本看不到“确认”按钮?这背后&am…

作者头像 李华