保姆级教程用PyTorch手写CBAM注意力模块附完整代码与避坑指南在深度学习领域注意力机制已经成为提升模型性能的重要工具。CBAMConvolutional Block Attention Module作为一种轻量级的注意力模块能够同时关注通道和空间两个维度的关键信息显著提升卷积神经网络的表达能力。本文将带你从零开始实现CBAM模块通过代码理解原理并解决实际集成中可能遇到的各种问题。1. 理解CBAM的核心思想CBAM由两个关键组件构成通道注意力模块和空间注意力模块。这两个模块采用串联方式工作先处理通道维度信息再处理空间维度信息。为什么这种顺序更有效实验表明先关注哪些通道重要再关注这些通道中哪些位置重要的处理流程更符合人类视觉系统的认知逻辑。想象一下当你观察一幅画时会先识别颜色和纹理通道维度再聚焦于特定区域空间维度。1.1 通道注意力机制详解通道注意力的核心思想是让网络学会重视重要的特征通道。其实现步骤可分解为双路池化同时计算全局平均池化和全局最大池化共享MLP使用同一个两层神经网络处理两种池化结果特征融合将两种处理结果相加后通过Sigmoid激活# 通道注意力计算过程伪代码 avg_pool GlobalAvgPool2D(input) max_pool GlobalMaxPool2D(input) avg_out shared_MLP(avg_pool) max_out shared_MLP(max_pool) channel_attention sigmoid(avg_out max_out) output input * channel_attention1.2 空间注意力机制解析空间注意力则关注特征图中的重要位置其关键步骤包括通道压缩沿通道维度进行最大池化和平均池化特征拼接将两种池化结果在通道维度拼接卷积处理使用7×7卷积生成空间权重图# 空间注意力计算过程伪代码 avg_pool ChannelWiseAvgPool(input) max_pool ChannelWiseMaxPool(input) concat concatenate([avg_pool, max_pool], axis1) spatial_attention sigmoid(Conv7x7(concat)) output input * spatial_attention2. PyTorch实现CBAM模块现在让我们用PyTorch完整实现CBAM模块。我们将采用面向对象的方式分别构建通道注意力和空间注意力类。2.1 通道注意力模块实现import torch import torch.nn as nn import torch.nn.functional as F class ChannelAttention(nn.Module): def __init__(self, in_channels, reduction_ratio16): super(ChannelAttention, self).__init__() self.avg_pool nn.AdaptiveAvgPool2d(1) self.max_pool nn.AdaptiveMaxPool2d(1) # 使用1x1卷积代替全连接层便于处理任意尺寸输入 self.fc nn.Sequential( nn.Conv2d(in_channels, in_channels // reduction_ratio, 1, biasFalse), nn.ReLU(), nn.Conv2d(in_channels // reduction_ratio, in_channels, 1, biasFalse) ) self.sigmoid nn.Sigmoid() def forward(self, x): avg_out self.fc(self.avg_pool(x)) max_out self.fc(self.max_pool(x)) out avg_out max_out return self.sigmoid(out)关键实现细节使用AdaptiveAvgPool2d和AdaptiveMaxPool2d实现全局池化采用1×1卷积模拟全连接操作保持空间维度不变共享权重同一组卷积层处理两种池化结果最终输出与输入特征图尺寸相同仅通道权重不同2.2 空间注意力模块实现class SpatialAttention(nn.Module): def __init__(self, kernel_size7): super(SpatialAttention, self).__init__() assert kernel_size in (3, 7), kernel size must be 3 or 7 padding 3 if kernel_size 7 else 1 self.conv nn.Conv2d(2, 1, kernel_size, paddingpadding, biasFalse) self.sigmoid nn.Sigmoid() def forward(self, x): avg_out torch.mean(x, dim1, keepdimTrue) max_out, _ torch.max(x, dim1, keepdimTrue) x torch.cat([avg_out, max_out], dim1) x self.conv(x) return self.sigmoid(x)实现要点解析torch.mean和torch.max实现通道维度的池化keepdimTrue保持维度一致性避免后续拼接出错7×7卷积核能捕获较大范围的上下文信息输出空间权重图与输入特征图尺寸相同2.3 完整CBAM模块集成将两个注意力模块串联构建完整的CBAMclass CBAM(nn.Module): def __init__(self, in_channels, reduction_ratio16, kernel_size7): super(CBAM, self).__init__() self.channel_attention ChannelAttention(in_channels, reduction_ratio) self.spatial_attention SpatialAttention(kernel_size) def forward(self, x): x x * self.channel_attention(x) x x * self.spatial_attention(x) return x3. 与现有网络集成实战CBAM的美妙之处在于它可以无缝集成到各种CNN架构中。下面以ResNet为例展示如何将CBAM插入到残差块中。3.1 改造ResNet基本块原始ResNet的基本残差块结构如下输入 → 卷积1 → BN → ReLU → 卷积2 → BN → 残差连接 → ReLU加入CBAM后的改进版本输入 → 卷积1 → BN → ReLU → 卷积2 → BN → CBAM → 残差连接 → ReLU具体实现代码class BasicBlockWithCBAM(nn.Module): expansion 1 def __init__(self, in_channels, out_channels, stride1): super(BasicBlockWithCBAM, self).__init__() self.conv1 nn.Conv2d(in_channels, out_channels, kernel_size3, stridestride, padding1, biasFalse) self.bn1 nn.BatchNorm2d(out_channels) self.conv2 nn.Conv2d(out_channels, out_channels, kernel_size3, stride1, padding1, biasFalse) self.bn2 nn.BatchNorm2d(out_channels) self.cbam CBAM(out_channels) self.shortcut nn.Sequential() if stride ! 1 or in_channels ! self.expansion * out_channels: self.shortcut nn.Sequential( nn.Conv2d(in_channels, self.expansion * out_channels, kernel_size1, stridestride, biasFalse), nn.BatchNorm2d(self.expansion * out_channels) ) def forward(self, x): residual self.shortcut(x) out F.relu(self.bn1(self.conv1(x))) out self.bn2(self.conv2(out)) out self.cbam(out) # 加入CBAM模块 out residual return F.relu(out)3.2 在完整ResNet中应用构建完整的ResNet-18 with CBAMclass ResNetWithCBAM(nn.Module): def __init__(self, block, num_blocks, num_classes1000): super(ResNetWithCBAM, self).__init__() self.in_channels 64 self.conv1 nn.Conv2d(3, 64, kernel_size7, stride2, padding3, biasFalse) self.bn1 nn.BatchNorm2d(64) self.maxpool nn.MaxPool2d(kernel_size3, stride2, padding1) self.layer1 self._make_layer(block, 64, num_blocks[0], stride1) self.layer2 self._make_layer(block, 128, num_blocks[1], stride2) self.layer3 self._make_layer(block, 256, num_blocks[2], stride2) self.layer4 self._make_layer(block, 512, num_blocks[3], stride2) self.avgpool nn.AdaptiveAvgPool2d(1) self.fc nn.Linear(512 * block.expansion, num_classes) def _make_layer(self, block, out_channels, num_blocks, stride): strides [stride] [1]*(num_blocks-1) layers [] for stride in strides: layers.append(block(self.in_channels, out_channels, stride)) self.in_channels out_channels * block.expansion return nn.Sequential(*layers) def forward(self, x): x F.relu(self.bn1(self.conv1(x))) x self.maxpool(x) x self.layer1(x) x self.layer2(x) x self.layer3(x) x self.layer4(x) x self.avgpool(x) x torch.flatten(x, 1) x self.fc(x) return x def resnet18_with_cbam(num_classes1000): return ResNetWithCBAM(BasicBlockWithCBAM, [2,2,2,2], num_classes)4. 常见问题与解决方案在实际实现和应用CBAM时可能会遇到各种问题。以下是几个典型场景及其解决方案。4.1 维度不匹配问题问题现象当尝试将CBAM插入到不同网络时经常会出现维度不匹配的错误如RuntimeError: The size of tensor a (64) must match the size of tensor b (128) at non-singleton dimension 1解决方案检查通道数一致性确保CBAM的输入通道数与特征图通道数匹配验证池化操作全局池化应产生[B, C, 1, 1]形状的输出调试技巧在forward方法中添加shape打印语句def forward(self, x): print(fInput shape: {x.shape}) # 调试用 avg_out self.avg_pool(x) print(fAvg pool shape: {avg_out.shape}) # ...其余代码4.2 梯度消失/爆炸问题表现训练过程中损失不收敛或出现NaN值解决方法适当调整reduction_ratio过大的压缩比可能导致信息丢失对于小模型(如ResNet18)建议使用16对于大模型(如ResNet50)可尝试8或4初始化策略对注意力模块中的卷积层使用特定初始化# 添加在ChannelAttention的__init__中 nn.init.kaiming_normal_(self.fc[0].weight, modefan_out, nonlinearityrelu) nn.init.constant_(self.fc[2].weight, 0) # 初始化为0使注意力初始为中性4.3 计算效率优化CBAM虽然轻量但在某些场景下仍需优化池化操作融合将平均池化和最大池化合并计算并行计算利用PyTorch的并行处理能力优化后的通道注意力实现def forward(self, x): # 同时计算两种池化 avg_pool self.avg_pool(x) max_pool self.max_pool(x) # 并行处理 avg_out self.fc(avg_pool) max_out self.fc(max_pool) # 元素相加替代张量相加 out torch.add(avg_out, max_out) return self.sigmoid(out)4.4 注意力可视化技巧理解CBAM的工作机制可视化很有帮助def visualize_attention(model, input_tensor): # 获取中间输出 activations {} def hook_fn(name): def hook(model, input, output): activations[name] output.detach() return hook # 注册钩子 model.channel_attention.register_forward_hook(hook_fn(channel)) model.spatial_attention.register_forward_hook(hook_fn(spatial)) # 前向传播 with torch.no_grad(): _ model(input_tensor) # 可视化 plt.figure(figsize(12,4)) plt.subplot(131) plt.imshow(input_tensor[0].permute(1,2,0).cpu().numpy()) plt.subplot(132) plt.imshow(activations[channel][0].mean(dim0).cpu().numpy()) plt.subplot(133) plt.imshow(activations[spatial][0][0].cpu().numpy())5. 进阶应用与性能调优掌握了基础实现后让我们探讨一些高级应用技巧和性能优化策略。5.1 多尺度注意力融合传统CBAM处理单一尺度特征我们可以扩展为多尺度版本class MultiScaleCBAM(nn.Module): def __init__(self, in_channels, scales[1,2,4]): super(MultiScaleCBAM, self).__init__() self.scales scales self.channel_attentions nn.ModuleList([ ChannelAttention(in_channels) for _ in scales ]) self.spatial_attentions nn.ModuleList([ SpatialAttention() for _ in scales ]) self.merge_conv nn.Conv2d(len(scales)*in_channels, in_channels, 1) def forward(self, x): attention_maps [] for scale, ca, sa in zip(self.scales, self.channel_attentions, self.spatial_attentions): # 多尺度特征提取 pooled F.avg_pool2d(x, kernel_sizescale, stridescale) # 注意力计算 channel_att ca(pooled) spatial_att sa(pooled * channel_att) # 上采样回原尺寸 att_map F.interpolate(spatial_att, sizex.shape[2:], modebilinear) attention_maps.append(att_map * x) # 多尺度融合 out self.merge_conv(torch.cat(attention_maps, dim1)) return out5.2 轻量化CBAM变体针对移动端或嵌入式设备可以设计更轻量的版本class LiteCBAM(nn.Module): def __init__(self, in_channels): super(LiteCBAM, self).__init__() # 通道注意力简化版 self.channel_conv nn.Sequential( nn.Conv2d(in_channels, 1, 1), # 使用1x1卷积替代MLP nn.Sigmoid() ) # 空间注意力简化版 self.spatial_conv nn.Sequential( nn.Conv2d(in_channels, 1, 3, padding1), nn.Sigmoid() ) def forward(self, x): # 通道注意力 channel_att self.channel_conv(x.mean(dim(2,3), keepdimTrue)) # 空间注意力 spatial_att self.spatial_conv(x) return x * channel_att * spatial_att5.3 注意力机制组合策略CBAM可以与其他注意力机制组合使用常见组合方式组合方式优点适用场景CBAM SE增强通道注意力能力分类任务CBAM Non-local捕获长距离依赖视频分析、大尺寸图像CBAM SK多尺度特征自适应选择检测、分割任务示例组合实现class CBAMWithSE(nn.Module): def __init__(self, in_channels, reduction16): super(CBAMWithSE, self).__init__() self.se nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Conv2d(in_channels, in_channels//reduction, 1), nn.ReLU(), nn.Conv2d(in_channels//reduction, in_channels, 1), nn.Sigmoid() ) self.cbam CBAM(in_channels) def forward(self, x): se_att self.se(x) cbam_att self.cbam(x) return x * se_att * cbam_att5.4 训练技巧与超参数选择优化CBAM模型的实用技巧学习率调整CBAM模块的学习率可以设为基础网络的2-5倍使用分层学习率策略optimizer torch.optim.SGD([ {params: model.backbone.parameters(), lr: 0.1}, {params: model.cbam.parameters(), lr: 0.3} ], momentum0.9)注意力权重初始化将最后一个Sigmoid前的卷积层权重初始化为0这样初始状态下注意力模块相当于恒等映射nn.init.constant_(self.fc2.weight, 0) # ChannelAttention中 nn.init.constant_(self.conv1.weight, 0) # SpatialAttention中正则化策略对注意力权重施加L1正则促进稀疏性使用Dropout防止过拟合class RegularizedCBAM(CBAM): def forward(self, x): channel_att self.channel_attention(x) spatial_att self.spatial_attention(x * channel_att) # L1正则化 l1_reg torch.mean(torch.abs(channel_att)) torch.mean(torch.abs(spatial_att)) output x * channel_att * spatial_att return output, l1_reg # 使用时 output, l1_reg model(input) loss criterion(output, target) 0.01 * l1_reg
保姆级教程:用PyTorch手写CBAM注意力模块(附完整代码与避坑指南)
保姆级教程用PyTorch手写CBAM注意力模块附完整代码与避坑指南在深度学习领域注意力机制已经成为提升模型性能的重要工具。CBAMConvolutional Block Attention Module作为一种轻量级的注意力模块能够同时关注通道和空间两个维度的关键信息显著提升卷积神经网络的表达能力。本文将带你从零开始实现CBAM模块通过代码理解原理并解决实际集成中可能遇到的各种问题。1. 理解CBAM的核心思想CBAM由两个关键组件构成通道注意力模块和空间注意力模块。这两个模块采用串联方式工作先处理通道维度信息再处理空间维度信息。为什么这种顺序更有效实验表明先关注哪些通道重要再关注这些通道中哪些位置重要的处理流程更符合人类视觉系统的认知逻辑。想象一下当你观察一幅画时会先识别颜色和纹理通道维度再聚焦于特定区域空间维度。1.1 通道注意力机制详解通道注意力的核心思想是让网络学会重视重要的特征通道。其实现步骤可分解为双路池化同时计算全局平均池化和全局最大池化共享MLP使用同一个两层神经网络处理两种池化结果特征融合将两种处理结果相加后通过Sigmoid激活# 通道注意力计算过程伪代码 avg_pool GlobalAvgPool2D(input) max_pool GlobalMaxPool2D(input) avg_out shared_MLP(avg_pool) max_out shared_MLP(max_pool) channel_attention sigmoid(avg_out max_out) output input * channel_attention1.2 空间注意力机制解析空间注意力则关注特征图中的重要位置其关键步骤包括通道压缩沿通道维度进行最大池化和平均池化特征拼接将两种池化结果在通道维度拼接卷积处理使用7×7卷积生成空间权重图# 空间注意力计算过程伪代码 avg_pool ChannelWiseAvgPool(input) max_pool ChannelWiseMaxPool(input) concat concatenate([avg_pool, max_pool], axis1) spatial_attention sigmoid(Conv7x7(concat)) output input * spatial_attention2. PyTorch实现CBAM模块现在让我们用PyTorch完整实现CBAM模块。我们将采用面向对象的方式分别构建通道注意力和空间注意力类。2.1 通道注意力模块实现import torch import torch.nn as nn import torch.nn.functional as F class ChannelAttention(nn.Module): def __init__(self, in_channels, reduction_ratio16): super(ChannelAttention, self).__init__() self.avg_pool nn.AdaptiveAvgPool2d(1) self.max_pool nn.AdaptiveMaxPool2d(1) # 使用1x1卷积代替全连接层便于处理任意尺寸输入 self.fc nn.Sequential( nn.Conv2d(in_channels, in_channels // reduction_ratio, 1, biasFalse), nn.ReLU(), nn.Conv2d(in_channels // reduction_ratio, in_channels, 1, biasFalse) ) self.sigmoid nn.Sigmoid() def forward(self, x): avg_out self.fc(self.avg_pool(x)) max_out self.fc(self.max_pool(x)) out avg_out max_out return self.sigmoid(out)关键实现细节使用AdaptiveAvgPool2d和AdaptiveMaxPool2d实现全局池化采用1×1卷积模拟全连接操作保持空间维度不变共享权重同一组卷积层处理两种池化结果最终输出与输入特征图尺寸相同仅通道权重不同2.2 空间注意力模块实现class SpatialAttention(nn.Module): def __init__(self, kernel_size7): super(SpatialAttention, self).__init__() assert kernel_size in (3, 7), kernel size must be 3 or 7 padding 3 if kernel_size 7 else 1 self.conv nn.Conv2d(2, 1, kernel_size, paddingpadding, biasFalse) self.sigmoid nn.Sigmoid() def forward(self, x): avg_out torch.mean(x, dim1, keepdimTrue) max_out, _ torch.max(x, dim1, keepdimTrue) x torch.cat([avg_out, max_out], dim1) x self.conv(x) return self.sigmoid(x)实现要点解析torch.mean和torch.max实现通道维度的池化keepdimTrue保持维度一致性避免后续拼接出错7×7卷积核能捕获较大范围的上下文信息输出空间权重图与输入特征图尺寸相同2.3 完整CBAM模块集成将两个注意力模块串联构建完整的CBAMclass CBAM(nn.Module): def __init__(self, in_channels, reduction_ratio16, kernel_size7): super(CBAM, self).__init__() self.channel_attention ChannelAttention(in_channels, reduction_ratio) self.spatial_attention SpatialAttention(kernel_size) def forward(self, x): x x * self.channel_attention(x) x x * self.spatial_attention(x) return x3. 与现有网络集成实战CBAM的美妙之处在于它可以无缝集成到各种CNN架构中。下面以ResNet为例展示如何将CBAM插入到残差块中。3.1 改造ResNet基本块原始ResNet的基本残差块结构如下输入 → 卷积1 → BN → ReLU → 卷积2 → BN → 残差连接 → ReLU加入CBAM后的改进版本输入 → 卷积1 → BN → ReLU → 卷积2 → BN → CBAM → 残差连接 → ReLU具体实现代码class BasicBlockWithCBAM(nn.Module): expansion 1 def __init__(self, in_channels, out_channels, stride1): super(BasicBlockWithCBAM, self).__init__() self.conv1 nn.Conv2d(in_channels, out_channels, kernel_size3, stridestride, padding1, biasFalse) self.bn1 nn.BatchNorm2d(out_channels) self.conv2 nn.Conv2d(out_channels, out_channels, kernel_size3, stride1, padding1, biasFalse) self.bn2 nn.BatchNorm2d(out_channels) self.cbam CBAM(out_channels) self.shortcut nn.Sequential() if stride ! 1 or in_channels ! self.expansion * out_channels: self.shortcut nn.Sequential( nn.Conv2d(in_channels, self.expansion * out_channels, kernel_size1, stridestride, biasFalse), nn.BatchNorm2d(self.expansion * out_channels) ) def forward(self, x): residual self.shortcut(x) out F.relu(self.bn1(self.conv1(x))) out self.bn2(self.conv2(out)) out self.cbam(out) # 加入CBAM模块 out residual return F.relu(out)3.2 在完整ResNet中应用构建完整的ResNet-18 with CBAMclass ResNetWithCBAM(nn.Module): def __init__(self, block, num_blocks, num_classes1000): super(ResNetWithCBAM, self).__init__() self.in_channels 64 self.conv1 nn.Conv2d(3, 64, kernel_size7, stride2, padding3, biasFalse) self.bn1 nn.BatchNorm2d(64) self.maxpool nn.MaxPool2d(kernel_size3, stride2, padding1) self.layer1 self._make_layer(block, 64, num_blocks[0], stride1) self.layer2 self._make_layer(block, 128, num_blocks[1], stride2) self.layer3 self._make_layer(block, 256, num_blocks[2], stride2) self.layer4 self._make_layer(block, 512, num_blocks[3], stride2) self.avgpool nn.AdaptiveAvgPool2d(1) self.fc nn.Linear(512 * block.expansion, num_classes) def _make_layer(self, block, out_channels, num_blocks, stride): strides [stride] [1]*(num_blocks-1) layers [] for stride in strides: layers.append(block(self.in_channels, out_channels, stride)) self.in_channels out_channels * block.expansion return nn.Sequential(*layers) def forward(self, x): x F.relu(self.bn1(self.conv1(x))) x self.maxpool(x) x self.layer1(x) x self.layer2(x) x self.layer3(x) x self.layer4(x) x self.avgpool(x) x torch.flatten(x, 1) x self.fc(x) return x def resnet18_with_cbam(num_classes1000): return ResNetWithCBAM(BasicBlockWithCBAM, [2,2,2,2], num_classes)4. 常见问题与解决方案在实际实现和应用CBAM时可能会遇到各种问题。以下是几个典型场景及其解决方案。4.1 维度不匹配问题问题现象当尝试将CBAM插入到不同网络时经常会出现维度不匹配的错误如RuntimeError: The size of tensor a (64) must match the size of tensor b (128) at non-singleton dimension 1解决方案检查通道数一致性确保CBAM的输入通道数与特征图通道数匹配验证池化操作全局池化应产生[B, C, 1, 1]形状的输出调试技巧在forward方法中添加shape打印语句def forward(self, x): print(fInput shape: {x.shape}) # 调试用 avg_out self.avg_pool(x) print(fAvg pool shape: {avg_out.shape}) # ...其余代码4.2 梯度消失/爆炸问题表现训练过程中损失不收敛或出现NaN值解决方法适当调整reduction_ratio过大的压缩比可能导致信息丢失对于小模型(如ResNet18)建议使用16对于大模型(如ResNet50)可尝试8或4初始化策略对注意力模块中的卷积层使用特定初始化# 添加在ChannelAttention的__init__中 nn.init.kaiming_normal_(self.fc[0].weight, modefan_out, nonlinearityrelu) nn.init.constant_(self.fc[2].weight, 0) # 初始化为0使注意力初始为中性4.3 计算效率优化CBAM虽然轻量但在某些场景下仍需优化池化操作融合将平均池化和最大池化合并计算并行计算利用PyTorch的并行处理能力优化后的通道注意力实现def forward(self, x): # 同时计算两种池化 avg_pool self.avg_pool(x) max_pool self.max_pool(x) # 并行处理 avg_out self.fc(avg_pool) max_out self.fc(max_pool) # 元素相加替代张量相加 out torch.add(avg_out, max_out) return self.sigmoid(out)4.4 注意力可视化技巧理解CBAM的工作机制可视化很有帮助def visualize_attention(model, input_tensor): # 获取中间输出 activations {} def hook_fn(name): def hook(model, input, output): activations[name] output.detach() return hook # 注册钩子 model.channel_attention.register_forward_hook(hook_fn(channel)) model.spatial_attention.register_forward_hook(hook_fn(spatial)) # 前向传播 with torch.no_grad(): _ model(input_tensor) # 可视化 plt.figure(figsize(12,4)) plt.subplot(131) plt.imshow(input_tensor[0].permute(1,2,0).cpu().numpy()) plt.subplot(132) plt.imshow(activations[channel][0].mean(dim0).cpu().numpy()) plt.subplot(133) plt.imshow(activations[spatial][0][0].cpu().numpy())5. 进阶应用与性能调优掌握了基础实现后让我们探讨一些高级应用技巧和性能优化策略。5.1 多尺度注意力融合传统CBAM处理单一尺度特征我们可以扩展为多尺度版本class MultiScaleCBAM(nn.Module): def __init__(self, in_channels, scales[1,2,4]): super(MultiScaleCBAM, self).__init__() self.scales scales self.channel_attentions nn.ModuleList([ ChannelAttention(in_channels) for _ in scales ]) self.spatial_attentions nn.ModuleList([ SpatialAttention() for _ in scales ]) self.merge_conv nn.Conv2d(len(scales)*in_channels, in_channels, 1) def forward(self, x): attention_maps [] for scale, ca, sa in zip(self.scales, self.channel_attentions, self.spatial_attentions): # 多尺度特征提取 pooled F.avg_pool2d(x, kernel_sizescale, stridescale) # 注意力计算 channel_att ca(pooled) spatial_att sa(pooled * channel_att) # 上采样回原尺寸 att_map F.interpolate(spatial_att, sizex.shape[2:], modebilinear) attention_maps.append(att_map * x) # 多尺度融合 out self.merge_conv(torch.cat(attention_maps, dim1)) return out5.2 轻量化CBAM变体针对移动端或嵌入式设备可以设计更轻量的版本class LiteCBAM(nn.Module): def __init__(self, in_channels): super(LiteCBAM, self).__init__() # 通道注意力简化版 self.channel_conv nn.Sequential( nn.Conv2d(in_channels, 1, 1), # 使用1x1卷积替代MLP nn.Sigmoid() ) # 空间注意力简化版 self.spatial_conv nn.Sequential( nn.Conv2d(in_channels, 1, 3, padding1), nn.Sigmoid() ) def forward(self, x): # 通道注意力 channel_att self.channel_conv(x.mean(dim(2,3), keepdimTrue)) # 空间注意力 spatial_att self.spatial_conv(x) return x * channel_att * spatial_att5.3 注意力机制组合策略CBAM可以与其他注意力机制组合使用常见组合方式组合方式优点适用场景CBAM SE增强通道注意力能力分类任务CBAM Non-local捕获长距离依赖视频分析、大尺寸图像CBAM SK多尺度特征自适应选择检测、分割任务示例组合实现class CBAMWithSE(nn.Module): def __init__(self, in_channels, reduction16): super(CBAMWithSE, self).__init__() self.se nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Conv2d(in_channels, in_channels//reduction, 1), nn.ReLU(), nn.Conv2d(in_channels//reduction, in_channels, 1), nn.Sigmoid() ) self.cbam CBAM(in_channels) def forward(self, x): se_att self.se(x) cbam_att self.cbam(x) return x * se_att * cbam_att5.4 训练技巧与超参数选择优化CBAM模型的实用技巧学习率调整CBAM模块的学习率可以设为基础网络的2-5倍使用分层学习率策略optimizer torch.optim.SGD([ {params: model.backbone.parameters(), lr: 0.1}, {params: model.cbam.parameters(), lr: 0.3} ], momentum0.9)注意力权重初始化将最后一个Sigmoid前的卷积层权重初始化为0这样初始状态下注意力模块相当于恒等映射nn.init.constant_(self.fc2.weight, 0) # ChannelAttention中 nn.init.constant_(self.conv1.weight, 0) # SpatialAttention中正则化策略对注意力权重施加L1正则促进稀疏性使用Dropout防止过拟合class RegularizedCBAM(CBAM): def forward(self, x): channel_att self.channel_attention(x) spatial_att self.spatial_attention(x * channel_att) # L1正则化 l1_reg torch.mean(torch.abs(channel_att)) torch.mean(torch.abs(spatial_att)) output x * channel_att * spatial_att return output, l1_reg # 使用时 output, l1_reg model(input) loss criterion(output, target) 0.01 * l1_reg