【即插即用】CBAM注意力机制(附源码)

原文链接:

https://arxiv.org/abs/1807.06521

摘要简介:

他们提出了一种名为“卷积块注意力模块”(CBAM)的注意力模块,这是一个简单但高效的工具,专门用于增强前馈卷积神经网络的功能。当接收到一个中间特征图时,CBAM能够沿着通道和空间这两个维度独立地推断出注意力图。随后,这些注意力图会与输入的特征图相乘,实现自适应的特征优化。

由于CBAM是一个轻量级且通用的模块,它可以毫不费力地集成到任何CNN架构中,几乎不增加额外的计算负担。更值得一提的是,CBAM可以与基础CNN无缝衔接,进行端到端的训练。

为了验证CBAM的有效性,他们在ImageNet-1K、MS COCO检测和VOC 2007检测等多个大型数据集上进行了广泛的实验。实验结果表明,CBAM在各种模型中的分类和检测性能均得到了显著的提升,这充分展示了其广泛的适用性。

模型结构:

Pytorch版源码:
import torch
import torch.nn as nn

#CBAM,通道注意力机制+空间注意力机制
class ChannelAttention(nn.Module):
    def __init__(self, in_planes, ratio=8):
        super(ChannelAttention, self).__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.max_pool = nn.AdaptiveMaxPool2d(1)

        # 利用1x1卷积代替全连接
        self.fc1   = nn.Conv2d(in_planes, in_planes // ratio, 1, bias=False)
        self.relu1 = nn.ReLU()
        self.fc2   = nn.Conv2d(in_planes // ratio, in_planes, 1, bias=False)

        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        avg_out = self.fc2(self.relu1(self.fc1(self.avg_pool(x))))
        max_out = self.fc2(self.relu1(self.fc1(self.max_pool(x))))
        out = avg_out + max_out
        return self.sigmoid(out)

class SpatialAttention(nn.Module):
    def __init__(self, kernel_size=7):
        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.conv1 = nn.Conv2d(2, 1, kernel_size, padding=padding, bias=False)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        avg_out = torch.mean(x, dim=1, keepdim=True)
        max_out, _ = torch.max(x, dim=1, keepdim=True)
        x = torch.cat([avg_out, max_out], dim=1)
        x = self.conv1(x)
        return self.sigmoid(x)

class cbam_block(nn.Module):
    def __init__(self, channel, ratio=8, kernel_size=7):
        super(cbam_block, self).__init__()
        self.channelattention = ChannelAttention(channel, ratio=ratio)
        self.spatialattention = SpatialAttention(kernel_size=kernel_size)

    def forward(self, x):
        x = x*self.channelattention(x)
        x = x*self.spatialattention(x)
        return x

if __name__ == '__main__':
    input = torch.randn(2, 32, 512, 512)
    CBAM = cbam_block(channel=input.size(1), ratio=8, kernel_size=7)
    output = CBAM(input)
    print(output.shape)

### 如何在 nnUNet V2 中加入 CBAM 注意力机制 CBAM(Convolutional Block Attention Module)是一种有效的注意力机制,能够通过通道和空间两个维度提升特征表达能力[^4]。为了在 nnUNet V2 中集成 CBAM,需要对动态架构库 `dynamic-network-architectures` 进行修改,并调整配置文件以适配新的模型类名。 以下是具体的实现步骤和代码示例: --- #### 1. 安装并克隆 `dynamic-network-architectures` 首先,确保安装了 `dynamic-network-architectures` 库的源码版本。这一步是为了方便我们对其进行修改[^1]。 ```bash git clone https://github.com/FabianIsensee/dynamic-network-architectures.git cd dynamic-network-architectures pip install -e . ``` --- #### 2. 实现 CBAM 模块 我们需要定义一个 CBAM 模块,用于在网络的不同阶段插入注意力机制。以下是一个基于 PyTorch 的 CBAM 实现: ```python import torch import torch.nn as nn from torch.nn import functional as F class ChannelAttention(nn.Module): def __init__(self, in_planes, ratio=16): super(ChannelAttention, self).__init__() self.avg_pool = nn.AdaptiveAvgPool2d(1) self.max_pool = nn.AdaptiveMaxPool2d(1) self.shared_mlp = nn.Sequential( nn.Conv2d(in_planes, in_planes // ratio, kernel_size=1), nn.ReLU(), nn.Conv2d(in_planes // ratio, in_planes, kernel_size=1) ) self.sigmoid = nn.Sigmoid() def forward(self, x): avgout = self.shared_mlp(self.avg_pool(x)) maxout = self.shared_mlp(self.max_pool(x)) return self.sigmoid(avgout + maxout) class SpatialAttention(nn.Module): def __init__(self, kernel_size=7): 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, padding=padding, bias=False) self.sigmoid = nn.Sigmoid() def forward(self, x): avgout = torch.mean(x, dim=1, keepdim=True) maxout, _ = torch.max(x, dim=1, keepdim=True) out = torch.cat([avgout, maxout], dim=1) out = self.conv(out) return self.sigmoid(out) class CBAM(nn.Module): def __init__(self, in_planes, ratio=16, kernel_size=7): super(CBAM, self).__init__() self.channel_attention = ChannelAttention(in_planes, ratio) self.spatial_attention = SpatialAttention(kernel_size) def forward(self, x): out = self.channel_attention(x) * x out = self.spatial_attention(out) * out return out ``` 上述代码分别实现了 **Channel Attention** 和 **Spatial Attention**,并通过组合形成完整的 CBAM 模块。 --- #### 3. 修改 UNet 架构 接下来,在 `dynamic-network-architectures/architectures/unet.py` 文件中定义一个新的子类,继承自现有的 UNet 结构,并在其适当位置插入 CBAM 模块。 ```python from dynamic_network_architectures.building_blocks.unet_decoder import UnetDecoder from dynamic_network_architectures.building_blocks.residual_encoders import ResidualEncoder from cbam_module import CBAM # 假设上面的 CBAM 已保存为单独模块 class CBAMPlainConvUNet(ResidualEncoder, UnetDecoder): def __init__(self, input_channels, n_stages, base_num_features, num_classes, conv_op, norm_op, dropout_op=None, nonlin=torch.nn.LeakyReLU, nonlin_kwargs={'negative_slope': 1e-2}, deep_supervision=False, upscale_logits=False, final_nonlin=lambda x: x, block=None, initial_conv_kernel_size=3, pool_op_kernel_sizes=((2, 2, 2), (2, 2, 2), (2, 2, 2)), conv_kernel_sizes=((3, 3, 3), (3, 3, 3), (3, 3, 3)), strides=((1, 1, 1), (2, 2, 2), (2, 2, 2))): super().__init__(input_channels=input_channels, n_stages=n_stages, base_num_features=base_num_features, num_classes=num_classes, conv_op=conv_op, norm_op=norm_op, dropout_op=dropout_op, nonlin=nonlin, nonlin_kwargs=nonlin_kwargs, deep_supervision=deep_supervision, upscale_logits=upscale_logits, final_nonlin=final_nonlin, block=block, initial_conv_kernel_size=initial_conv_kernel_size, pool_op_kernel_sizes=pool_op_kernel_sizes, conv_kernel_sizes=conv_kernel_sizes, strides=strides) def forward(self, x): skips = [] for i, down in enumerate(self.downsample_ops): # Encoder 部分 x = down(x) if i != len(self.downsample_ops) - 1: # 不对最后一层应用跳跃连接 skips.append(x) # 在编码器的最后一层输出后添加 CBAM cbam_layer = CBAM(channel=x.shape[1]) # 动态获取输入通道数 x = cbam_layer(x) for i, up in enumerate(self.upsample_ops): # Decoder 部分 skip_connection = skips[-(i+1)] x = up(x, skip_connection) return self.seg_output(x) ``` 在此处,我们将 CBAM 插入到了编码器的最后一层输出之后[^4]。 --- #### 4. 更新配置文件 完成模型定义后,需要更新实验配置文件以加载新创建的模型类。进入对应任务的数据预处理目录下的 JSON 文件(如 `nnUNetPlans.json`),找到 `"network_class_name"` 字段,并将其指向刚才编写的自定义类路径[^2]。 例如: ```json { "plans_per_stage": [ { ... "network_class_name": "dynamic_network_architectures.architectures.cbamunet.CBAMPlainConvUNet", ... } ] } ``` --- #### 5. 开始训练 最后,使用以下命令启动带有 CBAM 注意力机制的新模型训练过程: ```bash nnUNetv2_train DatasetName 3d_fullres FoldNumber ``` 例如: ```bash nnUNetv2_train Task04_Hippocampus 3d_fullres 0 nnUNetv2_train Task04_Hippocampus 3d_fullres 1 nnUNetv2_train Task04_Hippocampus 3d_fullres 2 nnUNetv2_train Task04_Hippocampus 3d_fullres 3 nnUNetv2_train Task04_Hippocampus 3d_fullres 4 ``` --- ### 注意事项 - 如果使用的是 Windows 系统,请确保设置了正确的环境变量路径;Linux 用户可参照官方文档设置等效命令。 - 自定义模型可能会影响收敛速度或最终性能表现,建议先从小规模数据集测试起始效果再逐步扩大范围。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值