注意力机制(SEnet、CBAM)附pytorch代码实现

CNN中使用注意力机制是一个有效的提高网络性能的方法,它的核心重点就是让网络关注到它更需要关注的地方。我们使用注意力机制让网络可以自适应地关注某些区域,主要的实现就是加权值,我们的代码也是通过给通道或像素加权值实现注意力机制。

一般而言,注意力机制分为通道注意力和空间注意力,或者将两者结合使用。

一、通道注意力

CNN中的特征图往往包含宽(W)、高(H)和通道数(C)。如下图所示:

 不同通道提取不同的特征信息,比如第一个通道提取水平特征,第二个特征提取垂直特征等。假如我们的任务比较关注垂直特征,则需要给第二个通道附更高的权值,来放大这个特征。

二、空间注意力

空间注意力在特征图中,在同一个通道中,不是所有的区域都是同等重要的,比如下图中,蓝色标记的位置是我们所重点关注的位置,我们就需要通过空间注意力机制,给相应的像素附更高的权值,实现重点关注该区域的功能。

三、集中注意力机制介绍(SEnet、CBAM)

在这里主要介绍SEnet和CBAM注意力机制以及相关pytorch代码实现。

1、SEnet

(1)论文来源

SEnet来自于论文《Squeeze-and-Excitation Networks》

论文链接是https://arxiv.org/abs/1709.01507

(2)SEnet结构组成

SEnet结构如下图所示,该模型可以实现通道注意力。

输入的特征尺寸是W×H×C, SEnet实现步骤包括以下几步:

1、全局平均池化:对每一个通道进行全局平均池化得到一个尺寸为1×1×C的特征。

2、Squeeze操作:将全局平均池化后的特征向量通过一个全连接层(一般是多层感知机)映射到一个较小的维度。这个过程被称为"Squeeze",用于学习通道的全局表示。一般来说,这个全连接层的输出维度可以通过一个超参数进行控制,以决定压缩的比例。

3、Excitation操作:将"Squeeze"操作的输出再经过一个全连接层,将维度恢复到原始的通道数。然后,通过一个激活函数(如Sigmoid函数)激活这个全连接层的输出,产生通道注意力权重。

4、重标定特征:将通道注意力权重与原始特征相乘,将注意力权重应用于原始特征,以重新加权特征图中的每个通道。这个过程可以通过将通道注意力权重扩展为与原始特征图相同的形状,然后将它们逐通道相乘实现。

实现代码如下:代码通过gpt生成,真的很好用哇

import torch
import torch.nn as nn

class SEBlock(nn.Module):
    def __init__(self, channels, reduction=16):
        super(SEBlock, self).__init__()

        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.fc1 = nn.Linear(channels, channels // reduction)
        self.relu = nn.ReLU(inplace=True)
        self.fc2 = nn.Linear(channels // reduction, channels)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        b, c, _, _ = x.size()
        out = self.avg_pool(x).view(b, c)
        out = self.fc1(out)
        out = self.relu(out)
        out = self.fc2(out)
        out = self.sigmoid(out).view(b, c, 1, 1)
        out = x * out.expand_as(x)
        return out


class SENet(nn.Module):
    def __init__(self, num_classes=10):
        super(SENet, self).__init__()

        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1)
        self.bn1 = nn.BatchNorm2d(32)
        self.se_block = SEBlock(32)
        self.flatten = nn.Flatten()
        self.fc = nn.Linear(32 * 32 * 32, num_classes)

    def forward(self, x):
        out = self.conv1(x)
        out = self.bn1(out)
        out = nn.ReLU(inplace=True)(out)
        out = self.se_block(out)
        out = self.flatten(out)
        out = self.fc(out)
        return out


# 创建模型实例
model = SENet()

 2、CBAM

(1)论文来源

CBAM来自于论文《CBAM: Convolutional Block Attention Module》

原文链接:

https://arxiv.org/pdf/1807.06521.pdf

(2)CBAM结构组成

CBAM注意力机制包含通道注意力模块和空间注意力模块。主要包含以下步骤:

1、共享全局平均池化:对输入特征图进行两次全局平均池化操作,分别用于计算空间注意力和通道注意力。全局平均池化能够捕捉到整个特征图的统计信息。

2、空间注意力:对全局平均池化后的特征进行两个不同的处理分支。一个分支是通过一个1x1卷积层产生空间注意力权重,用于学习特征图不同位置的重要性。另一个分支是通过一个1x1卷积层产生空间注意力的缩放因子,用于调整特征图中每个通道的权重。这两个分支的输出通过逐元素相乘操作,得到最终的空间注意力。

3、通道注意力:对全局平均池化后的特征进行两个不同的处理分支。一个分支是通过一个全连接层产生通道注意力权重,用于学习每个通道的重要性。另一个分支是通过一个全连接层产生通道注意力的缩放因子,用于调整特征图中每个通道的权重。这两个分支的输出通过逐元素相乘操作,得到最终的通道注意力。

4、重标定特征:将空间注意力和通道注意力分别应用于原始特征图。首先,将空间注意力权重通过扩展和重复操作,使其与特征图具有相同的形状。然后,将通道注意力权重通过扩展操作,使其具有相同的维度。最后,将两者逐元素相乘,将注意力权重应用于原始特征图,得到最终的重标定特征。

实现代码如下:

import torch
import torch.nn as nn

class ChannelAttention(nn.Module):
    def __init__(self, channels, reduction=16):
        super(ChannelAttention, self).__init__()

        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.fc1 = nn.Conv2d(channels, channels // reduction, kernel_size=1, stride=1, padding=0)
        self.relu = nn.ReLU(inplace=True)
        self.fc2 = nn.Conv2d(channels // reduction, channels, kernel_size=1, stride=1, padding=0)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        out = self.avg_pool(x)
        out = self.fc1(out)
        out = self.relu(out)
        out = self.fc2(out)
        out = self.sigmoid(out)
        out = x * out
        return out


class SpatialAttention(nn.Module):
    def __init__(self):
        super(SpatialAttention, self).__init__()

        self.conv = nn.Conv2d(2, 1, kernel_size=7, stride=1, padding=3)
        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)
        out = torch.cat([avg_out, max_out], dim=1)
        out = self.conv(out)
        out = self.sigmoid(out)
        out = x * out
        return out


class CBAM(nn.Module):
    def __init__(self, channels, reduction=16):
        super(CBAM, self).__init__()

        self.channel_att = ChannelAttention(channels, reduction)
        self.spatial_att = SpatialAttention()

    def forward(self, x):
        out = self.channel_att(x)
        out = self.spatial_att(out)
        return out


# 创建模型实例
model = CBAM(channels=64, reduction=16)

 

  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
SENetCBAM都是用于图像分类的注意力机制模块,可以提高模型的性能。 SENet(Squeeze-and-Excitation Networks)通过引入一个称为“Squeeze-and-Excitation block”的模块来增强卷积神经网络的表示能力。SE block包含两个步骤:第一步是全局平均池化,将每个通道的特征图转换为一个标量;第二步是使用两个全连接层来学习每个通道的权重,以增强有用信息并削减噪声。SENet的优化是通过提高通道相关性来实现的。 CBAM(Convolutional Block Attention Module)是另一种注意力机制模块,它通过在通道和空间维度上使用注意力来提高卷积神经网络的性能。CBAM模块包含两个分支:一个分支用于学习通道级别的注意力,另一个分支用于学习空间级别的注意力。通过这种方式,CBAM可以提高模型的表示能力并消除噪声。 以下是SENetCBAMPytorch实现代码SENet: ```python import torch.nn as nn import torch class SEBlock(nn.Module): def __init__(self, channel, reduction=16): super(SEBlock, self).__init__() self.avg_pool = nn.AdaptiveAvgPool2d(1) self.fc = nn.Sequential( nn.Linear(channel, channel // reduction, bias=False), nn.ReLU(inplace=True), nn.Linear(channel // reduction, channel, bias=False), nn.Sigmoid() ) def forward(self, x): b, c, _, _ = x.size() y = self.avg_pool(x).view(b, c) y = self.fc(y).view(b, c, 1, 1) return x * y.expand_as(x) ``` CBAM: ```python import torch.nn as nn import torch class ChannelAttention(nn.Module): def __init__(self, channel, reduction=16): super(ChannelAttention, self).__init__() self.avg_pool = nn.AdaptiveAvgPool2d(1) self.max_pool = nn.AdaptiveMaxPool2d(1) self.fc1 = nn.Conv2d(channel, channel // reduction, 1, bias=False) self.relu1 = nn.ReLU() self.fc2 = nn.Conv2d(channel // reduction, channel, 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(nn.Module): def __init__(self, channel, reduction=16, kernel_size=7): super(CBAM, self).__init__() self.ChannelGate = ChannelAttention(channel, reduction=reduction) self.SpatialGate = SpatialAttention(kernel_size=kernel_size) def forward(self, x): x_out = self.ChannelGate(x) * x x_out = self.SpatialGate(x_out) * x_out return x_out ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值