AI筑基录——注意力机制篇
2026/8/2 8:27:35 网站建设 项目流程

前言

回顾之前的内容,我们介绍了神经网络的基础知识,并且带着大家简单的完成了卷积神经网络的搭建;接下来,我们打算介绍一种优化方式——注意力机制;注意,此注意力并非 Transformer 中的注意力机制;

如果前面几期的内容没有看的,可以点击下面的链接!觉得内容有帮助的,希望可以关注一下博主!博主将持续更新专栏 AI筑基录

AI筑基录——卷积神经网络篇-CSDN博客

注意力机制

在谈论注意力机制之前,我们先明白注意力是什么?我们生活中,老师可能会说集中注意力看黑板,这个时候我们的注意力通常就会落在黑板上面,把关注的权重更多的放在黑板上面从而降低其他部分的权重;

注意力机制做的就是:面对很多信息时,模型自动判断哪些信息更重要,并给重要信息更大的权重;具体的做法就是通过加权的形式

为什么需要注意力机制?原因很简单,因为不是每一个位置或者通道的信息都是重要的,因此我们要把目光放在重要的区域;所以我们需要注意力机制;

通道注意力机制

经过 CNN 神经网路,假设卷积层输出:[ B,64,128,128 ],通道数是 64 对应特征图也是 64 张,因为每一张特征图所代表的信息是不一样的,于是通道注意力会为每一个通道生成一个权重;

通道注意力通常会先生成一个注意力权重,接着广播乘法,把每一个通道赋予对应的权重参数,这样做形状不变,但重要通道被增强,不重要通道被抑制

对于一个原始特征图 x:[B, C, H, W] ,每一个通道里面有 HxW个数;为了判断整个通道重不重要,首先需要把空间信息压缩掉,常见方法是全局平均池化;接着经过一个小的 MLP 层,目的是学习各个通道之间存在什么关系,以及最终应该给每个通道多大的权重;最后经过 Sigmoid函数输出权重参数;

下面是 SE Attention 的代码示例:

class ChannelAttention(nn.Module): def __init__(self, channels, reduction=16): super().__init__() hidden_channels = max(channels // reduction, 1) # [B,C,H,W] -> [B,C,1,1] self.avg_pool = nn.AdaptiveAvgPool2d(1) # 用1×1卷积实现通道维度上的MLP self.mlp = nn.Sequential( nn.Conv2d(channels, hidden_channels, kernel_size=1), nn.ReLU(inplace=True), nn.Conv2d(hidden_channels, channels, kernel_size=1) ) self.sigmoid = nn.Sigmoid() def forward(self, x): weight = self.avg_pool(x) weight = self.mlp(weight) weight = self.sigmoid(weight) return x * weight

我们学习不要被动的接受内容,我们不妨想一下,由于采用了全局平均池化,就算目标虽然在局部位置激活很强,但是经过平均池化后目标特征可能被冲淡;那么换句话,全局池化适合小目标任务吗?(因为背景信息占据主要内容);因此我们得换一个形式去描述通道,那这个部分就是一个小小的优化过程了;

空间注意力机制

前者解决哪一个通道重要,空间注意力机制解决的就是哪个位置重要;因此它首先是把通道数压缩,一般采用的方法是分别沿通道最大池化和平均池化,拼接后经过卷积层,最后 Sigmoid 输出权重

为什么要同时做平均和最大池化呢?原因是,平均池化可以表示:这个位置在所有通道上的整体响应如何;最大池化可以表示:这个位置是否至少在某一个通道上产生了非常强的响应;因此两者的结合会比只有一种表示的信息丰富;

代码示例:

class SpatialAttention(nn.Module): def __init__(self, kernel_size=7): super().__init__() assert kernel_size in (3, 7) padding = kernel_size // 2 self.conv = nn.Conv2d( in_channels=2, out_channels=1, kernel_size=kernel_size, padding=padding, bias=False ) self.sigmoid = nn.Sigmoid() def forward(self, x): # 沿通道维求平均 # [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,1,H,W] + [B,1,H,W] # -> [B,2,H,W] pooled = torch.cat([avg_out, max_out], dim=1) # [B,2,H,W] -> [B,1,H,W] weight = self.conv(pooled) weight = self.sigmoid(weight) return x * weight

CBAM 注意力机制

CBAM 注意力就是把前面两个合并在一起;但是顺序一般固定为先通道注意力,后空间注意力;这个是研发者实验发现,串联方式优于并行方式,而在两种串联顺序中,通道优先略好于空间优先

代码示例:

import torch import torch.nn as nn class ChannelAttention(nn.Module): def __init__(self, channels, reduction=16): super().__init__() hidden_channels = max(channels // reduction, 1) self.avg_pool = nn.AdaptiveAvgPool2d(1) self.max_pool = nn.AdaptiveMaxPool2d(1) # 两个池化分支共享同一个MLP self.shared_mlp = nn.Sequential( nn.Conv2d( channels, hidden_channels, kernel_size=1, bias=False ), nn.ReLU(inplace=True), nn.Conv2d( hidden_channels, channels, kernel_size=1, bias=False ) ) self.sigmoid = nn.Sigmoid() def forward(self, x): # 两个分支都是 [B,C,H,W] -> [B,C,1,1] avg_out = self.shared_mlp(self.avg_pool(x)) max_out = self.shared_mlp(self.max_pool(x)) weight = self.sigmoid(avg_out + max_out) # [B,C,H,W] * [B,C,1,1] return x * weight class SpatialAttention(nn.Module): def __init__(self, kernel_size=7): super().__init__() assert kernel_size in (3, 7) padding = kernel_size // 2 self.conv = nn.Conv2d( 2, 1, kernel_size=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) # [B,1,H,W] 和 [B,1,H,W] # -> [B,2,H,W] pooled = torch.cat([avg_out, max_out], dim=1) # -> [B,1,H,W] weight = self.sigmoid(self.conv(pooled)) # [B,C,H,W] * [B,1,H,W] return x * weight class CBAM(nn.Module): def __init__(self, channels, reduction=16, kernel_size=7): super().__init__() self.channel_attention = ChannelAttention( channels, reduction ) self.spatial_attention = SpatialAttention( kernel_size ) def forward(self, x): # 先选择重要通道 x = self.channel_attention(x) # 再选择重要空间位置 x = self.spatial_attention(x) return x

但是,有一个点我们要注意,不是说添加了对应的优化模块,我们就一定可以涨点;添加优化模块,我们难免引入一个问题,就是模型的参数量增加,这个问题就会引出更多问题,比如计算成本、拟合问题、存储问题等,这也就是创新中常见的权衡

所以就算没有涨点也很正常,但是这也不代表我们这么做就是没有用,我们可以注意力存图,观察模型的注意主要是落在哪一个方面,后面对其进一步做优化;

小思考

我们不妨先归一这三个机制,理想情况下,我们是希望通道和空间都被赋予一个独立的权重,这样做同一个空间位置对不同通道的重要性不同,同一个通道在不同空间位置的重要性也不同,但是直接预测成本太高(注意力图,计算和内存成本),因此我们通常单独抓一个进行建模,像是 CBAM 也是两者的近似组合;现在的问题就是转化成为:如何在低计算成本下,建模更强的通道&空间耦合关系呢?大家有想法的可以在评论区讨论一下!

总结

这一讲主要是介绍卷积神经网络的一个常见优化方式,我们介绍了三种常见的注意力机制,本源都是来自于我们卷积层输出维度产生的;一般放置的位置是卷积层后面,残差连接的前面;

AI筑基录的专栏会慢慢更新完,带大家有体系的理解神经网络的由来!制作不易,喜欢博主文章的可以点赞收藏关注,这些都是我持续更新的动力!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询