AI筑基录——注意力机制篇
前言回顾之前的内容我们介绍了神经网络的基础知识并且带着大家简单的完成了卷积神经网络的搭建接下来我们打算介绍一种优化方式——注意力机制注意此注意力并非 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, reduction16): 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_size1), nn.ReLU(inplaceTrue), nn.Conv2d(hidden_channels, channels, kernel_size1) ) 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_size7): super().__init__() assert kernel_size in (3, 7) padding kernel_size // 2 self.conv nn.Conv2d( in_channels2, out_channels1, kernel_sizekernel_size, paddingpadding, biasFalse ) self.sigmoid nn.Sigmoid() def forward(self, x): # 沿通道维求平均 # [B,C,H,W] - [B,1,H,W] avg_out torch.mean(x, dim1, keepdimTrue) # 沿通道维取最大值 # [B,C,H,W] - [B,1,H,W] max_out, _ torch.max(x, dim1, keepdimTrue) # [B,1,H,W] [B,1,H,W] # - [B,2,H,W] pooled torch.cat([avg_out, max_out], dim1) # [B,2,H,W] - [B,1,H,W] weight self.conv(pooled) weight self.sigmoid(weight) return x * weightCBAM 注意力机制CBAM 注意力就是把前面两个合并在一起但是顺序一般固定为先通道注意力后空间注意力这个是研发者实验发现串联方式优于并行方式而在两种串联顺序中通道优先略好于空间优先代码示例import torch import torch.nn as nn class ChannelAttention(nn.Module): def __init__(self, channels, reduction16): 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_size1, biasFalse ), nn.ReLU(inplaceTrue), nn.Conv2d( hidden_channels, channels, kernel_size1, biasFalse ) ) 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_size7): super().__init__() assert kernel_size in (3, 7) padding kernel_size // 2 self.conv nn.Conv2d( 2, 1, kernel_sizekernel_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) # [B,1,H,W] 和 [B,1,H,W] # - [B,2,H,W] pooled torch.cat([avg_out, max_out], dim1) # - [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, reduction16, kernel_size7): 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筑基录的专栏会慢慢更新完带大家有体系的理解神经网络的由来制作不易喜欢博主文章的可以点赞收藏关注这些都是我持续更新的动力