【CBAM Pytorch实现】注意力机制综述阅读推荐

11 篇文章 6 订阅
8 篇文章 1 订阅

注意力机制推荐阅读:(Attention Mechanism)


2021年11月20日15:54:26
  今天来实现CBAM模块,真心建议在使用CBAM前,先学习SE-Net,不然会很难理解!SE-Net不仅仅在CBAM中有使用,在MobileNetV3中也有使用,如果能 熟悉SE模块的话,那么能更快实现相关系列结构。


在这里插入图片描述

原文【CBAM: Convolutional Block Attention Module
会议:(ECCV 2018 paper)

ECCV的全称是European Conference on Computer Vision(欧洲计算机视觉国际会议)


摘要:
在这里插入图片描述

  我们提出了卷积块注意力模块(CBAM),一种简单而有效的卷积神经网络注意力模块。给定一个中间特征映射(feature map),我们的模块沿着两个独立的维度(通道和空间)顺序推断注意映射,然后将注意映射乘以输入特征映射以进行自适应特征细化。因为CBAM是一个轻量级的通用模块,它可以无缝地集成到任何CNN架构中,开销可以忽略不计,并且可以与基础CNN一起进行端到端的训练。我们通过在ImageNet-1K、MS COCO检测和VOC 2007检测数据集上的大量实验来验证我们的CBAM。我们的实验表明,各种模型在分类和检测性能方面都有一致性的提升,证明了CBAM的广泛适用性。代码和模型将公开提供。


  • 注意力机制可以告诉你去关注”where“并提高模型表现力
  • 关注重要的特性并抑制不必要特性
  • 本文采用CBAM模块来强调通道(channel)和空间(spatial)信息

在这里插入图片描述
  CBAM该模块有两个子模块:通道和空间(学习”what“和”where“),它能够自适应的细化特征图的映射


本文的 主要贡献
在这里插入图片描述

  1. 提出简单而有效的CBAM模块,能够提高CNN的特征表现能力
  2. 通过大量的对比试验,验证了CBAM的有效性
  3. 在不同的模型中插入轻量级的CBAM结构能够在不同数据集上均取得较好结果

CBAM:

  给一个特征图F(C×H×W)作为输入, M c M_c Mc是一维(C×1×1)的通道注意力图 M s M_s Ms是2维(1×H×W)的空间注意力图
F ′ = M c ( F ) ⊗ F F ′ ′ = M s ( F ′ ) ⊗ F ′ \begin{aligned} \mathbf{F}^{\prime} &=\mathbf{M}_{\mathbf{c}}(\mathbf{F}) \otimes \mathbf{F} \\ \mathbf{F}^{\prime \prime} &=\mathbf{M}_{\mathbf{s}}\left(\mathbf{F}^{\prime}\right) \otimes \mathbf{F}^{\prime} \end{aligned} FF=Mc(F)F=Ms(F)F
  其中 ⊗ \otimes 是点乘,下图分别表示通道注意力Module和空间注意力Module。


Channel Attention Module:

在这里插入图片描述

  我们利用通道间的关系生成通道注意力图,它更加关注什么(what)是有意义的;同时使用MaxPool和AvgPool操作它是更加有效的!

在这里插入图片描述
通道注意力可以通过如下进行计算
在这里插入图片描述

σ \sigma σ代表Sigmoid激活函数, W 0 和 W 1 W_0和W_1 W0W1为多层感知机MPL神经元的权重。

结构说明

  看通道注意力之前,我觉得要先把SE-Net读完,这个结构就是在SE-Net为基础的补充!

结构简述:
  对输入特征图F(C×H×W),分别在每个channel上进行全局最大池化和全局平均池化,分别得到C个值!然后把C个值作为全连接神经网络输入层的输入,中间隐含层神经元个数压缩设为C/r(r为压缩倍数),输出层神经元个数为C,分别得到结果(隐含层使用Relu激活函数、输出层使用Sigmoid激活函数)!全局最大池化得到1×1×C的权重,全局平均池化得到1×1×C的权重,然后对这2个1×1×C的权重图对应位置相加,最后在使用Sigmoid激活函数输出得到Channel Attention的结果 M c M_c Mc维数为1×1×C。


Spatial Attention Modul:
在这里插入图片描述
  我们根据特征图的空间关系产生空间注意力图,和通道注意力不同,空间注意力更加关注”where“,是对通道注意力的补充!
我们使用2个池化层对输入特征图进行操作,得到一个二维的特征图!
M s ( F ) = σ ( f 7 × 7 ( [ AvgPool ⁡ ( F ) ; MaxPool ⁡ ( F ) ] ) ) = σ ( f 7 × 7 ( [ F avg  s ; F max ⁡ s ] ) ) \begin{aligned} \mathbf{M}_{\mathbf{s}}(\mathbf{F}) &=\sigma\left(f^{7 \times 7}([\operatorname{AvgPool}(\mathbf{F}) ; \operatorname{MaxPool}(\mathbf{F})])\right) \\ &=\sigma\left(f^{7 \times 7}\left(\left[\mathbf{F}_{\text {avg }}^{\mathbf{s}} ; \mathbf{F}_{\max }^{\mathbf{s}}\right]\right)\right) \end{aligned} Ms(F)=σ(f7×7([AvgPool(F);MaxPool(F)]))=σ(f7×7([Favg s;Fmaxs]))

σ \sigma σ代表Sigmoid激活函数, f 7 × 7 f^{7×7} f7×7代表使用7×7大小的卷积核。

结构说明:

  我看得时候,最大池化和平均池化,怎么对维度上进行操作的不太明白,试了试用nn.maxpool操作是不行的,所以想到了用torch.mean和torch.max指定dim对维度进行操作,比如F=C×H×W用torch.max(F, dim=0)、F=N×C×H×W用torch.max(F, dim=1),指定维度channel通道进行操作!但我不确定到底是不是这样操作的,这样子能够算池化吗?
我去搜了搜,发现有人实现CBAM就是用的这样的方法:
ResNet_CBAM源码
  他实现了代码,但是我还不能看,不然就写得和人家一样了,就看了眼空间注意力这儿,它是不是使用的torch.max/torch.mean操作,确定了是这样操作的,就等自己实现完成后再去学习人家的源码!
  首先我们对输入特征图(C×H×W)在通道上分别进行MaxPool和AvgPool池化操作分别得到2张特征图1×H×C,然后在通道上进行拼接,得到2×H×C的特征图,接下来使用7×7卷积对特征图进行操作,输出维度为1×H×C,得到空间注意力的结果 M s M_s Ms维数为1×H×C。

  原文描述
在这里插入图片描述
  为了产生2维的空间注意力特征图,对每个像素的所有通道进行编码(max or average),然后使用1个卷积层对2维的特征图进行操作,得到原始空间注意力机制,最后使用Sigmoid激活函数。


模型设置

  两个注意力模块,分别关注”what“和”where“,他们可以平行或者按照顺序放置,作者发现顺序放置要比平行效果更好,实验表明,通道注意力然后在空间注意力可以拥有更好的效果。
在这里插入图片描述

  我们可以给任意的CNN模型,加入CBAM模块用于联合训练增强模型。下图是ResBlock和CBAM在ResNet中的一个例子:
在这里插入图片描述

代码实现

"""
Author: yida
Time is: 2021/11/21 11:40 
this Code: 实现CBAM模块
"""
import os

import torch
import torch.nn as nn

os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"


class CBAM(nn.Module):
    def __init__(self, in_channel):
        super(CBAM, self).__init__()
        self.Cam = ChannelAttentionModul(in_channel=in_channel)  # 通道注意力模块
        self.Sam = SpatialAttentionModul(in_channel=in_channel)  # 空间注意力模块

    def forward(self, x):
        x = self.Cam(x)
        x = self.Sam(x)
        return x


class ChannelAttentionModul(nn.Module):  # 通道注意力模块
    def __init__(self, in_channel, r=0.5):  # channel为输入的维度, r为全连接层缩放比例->控制中间层个数
        super(ChannelAttentionModul, self).__init__()
        # 全局最大池化
        self.MaxPool = nn.AdaptiveMaxPool2d(1)

        self.fc_MaxPool = nn.Sequential(
            nn.Linear(in_channel, int(in_channel * r)),  # int(channel * r)取整数, 中间层神经元数至少为1, 如有必要可设为向上取整
            nn.ReLU(),
            nn.Linear(int(in_channel * r), in_channel),
            nn.Sigmoid(),
        )

        # 全局均值池化
        self.AvgPool = nn.AdaptiveAvgPool2d(1)

        self.fc_AvgPool = nn.Sequential(
            nn.Linear(in_channel, int(in_channel * r)),  # int(channel * r)取整数, 中间层神经元数至少为1, 如有必要可设为向上取整
            nn.ReLU(),
            nn.Linear(int(in_channel * r), in_channel),
            nn.Sigmoid(),
        )

        # 激活函数
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        # 1.最大池化分支
        max_branch = self.MaxPool(x)
        # 送入MLP全连接神经网络, 得到权重
        max_in = max_branch.view(max_branch.size(0), -1)
        max_weight = self.fc_MaxPool(max_in)

        # 2.全局池化分支
        avg_branch = self.AvgPool(x)
        # 送入MLP全连接神经网络, 得到权重
        avg_in = avg_branch.view(avg_branch.size(0), -1)
        avg_weight = self.fc_AvgPool(avg_in)

        # MaxPool + AvgPool 激活后得到权重weight
        weight = max_weight + avg_weight
        weight = self.sigmoid(weight)

        # 将维度为b, c的weight, reshape成b, c, 1, 1 与 输入x 相乘
        h, w = weight.shape
        # 通道注意力Mc
        Mc = torch.reshape(weight, (h, w, 1, 1))

        # 乘积获得结果
        x = Mc * x

        return x


class SpatialAttentionModul(nn.Module):  # 空间注意力模块
    def __init__(self, in_channel):
        super(SpatialAttentionModul, self).__init__()
        self.conv = nn.Conv2d(2, 1, 7, padding=3)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        # x维度为 [N, C, H, W] 沿着维度C进行操作, 所以dim=1, 结果为[N, H, W]
        MaxPool = torch.max(x, dim=1).values  # torch.max 返回的是索引和value, 要用.values去访问值才行!
        AvgPool = torch.mean(x, dim=1)

        # 增加维度, 变成 [N, 1, H, W]
        MaxPool = torch.unsqueeze(MaxPool, dim=1)
        AvgPool = torch.unsqueeze(AvgPool, dim=1)

        # 维度拼接 [N, 2, H, W]
        x_cat = torch.cat((MaxPool, AvgPool), dim=1)  # 获得特征图

        # 卷积操作得到空间注意力结果
        x_out = self.conv(x_cat)
        Ms = self.sigmoid(x_out)

        # 与原图通道进行乘积
        x = Ms * x

        return x


if __name__ == '__main__':
    inputs = torch.randn(10, 100, 224, 224)
    model = CBAM(in_channel=100)  # CBAM模块, 可以插入CNN及任意网络中, 输入特征图in_channel的维度
    print(model)
    outputs = model(inputs)
    print("输入维度:", inputs.shape)
    print("输出维度:", outputs.shape)


实验和总结

  • 通道注意力,同时使用均值池化和最大池化有更好的效果
  • 仅使用均值池化,和最大池化效果差不多
    在这里插入图片描述
  • 对通道注意力进行总结,同时使用均值和池化特征,全连接神经网络的压缩率设为r=16(隐含层神经元个数C/16)
  • 空间注意力,分别使用了1×1、3×3卷积但发现使用更大的7×7卷积更好,这意味着空间上重要的区域需要更加广阔的感受野

在这里插入图片描述

  • 最终目标是为了将两个模块同时使用,所以先使用通道注意力,然后在使用空间注意力更好
  • 对空间注意力总结,使用平均和最大池化对维度进行操作,然后使用7×7卷积获得我们的空间注意力模型
  • 同时使用通道和空间注意力更好,先使用通道注意力然后在使用空间注意力,比并行或者现使用空间注意力效果更好
  • 实验表明这两种注意力机制都是至关重要的,我们提出的结构能够提高模型的能力

实验结果:Classification results on ImageNet-1K
在这里插入图片描述
  我们发现,使用了CBAM结构的模型总是有提升的!(比使用SE有一点点提升,比什么都不用的基准模型提升多一点点,参数量变化不大,总的来说在参数量不怎么变化的情况下,提高了点点准确率吧)

CBAM在ILSVRC2017任务中是成功的方法。

轻量级网络中的表现:
在这里插入图片描述
  基于MobileNet轻量级网络中,效果很好, 参数及计算的开销都非常小!这表明CBAM在未来的移动端设备上有较大的潜力!

定量评价:

  在实践中,给每种方法都给出完整的输入图像、地面真实标签、和两个区域交接处,如下图左1。下表二是25个受访者对50个问题集和图像的投票结果,更多的人认为使用CBAM模块获得的结果能有更好的表现!
在这里插入图片描述
在这里插入图片描述
  分别比较了以ResNet50为基准、ResNet50-SE、ResNet50-CBAM,计算最后一个卷积层的梯度CAM可视化结果,真实的标签在图像顶部,P代表softmax预测为真实类标签的概率。

  最后,CBAM能够强调抑制内容和位置,并有效的细化中间特征,我们希望CBAM能够成为各种网络结构中重要的组成部分!


【推荐阅读】

Pytorch-GPU安装教程大合集(Perfect完美系列)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈嘿萌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值