拓冰建站拓冰建站
首页 / 资讯中心 / 正文

深度学习注意力机制全解析:从SE、CBAM到坐标注意力和自注意力

注意力机制这几个字现在做深度学习的估计没人不眼熟。无论是搞图像的、搞文本的、搞时序的都会在自己的模型里加个注意力模块来提点。早几年大家还停留在别人加了SE涨点了我也加一个的阶段等到后面CA、CBAM、多头自注意力这些概念满天飞不少人其实已经被搞晕了——到底该用哪种为什么有时候加进去涨点有时候反而掉点我自己的经验是绝大多数问题出在不理解注意力到底在解决什么问题。这篇文章我想把目前最常见的几类注意力机制一次性讲清楚从它们各自要解决的痛点、核心原理、实现细节到实战中的选型建议都过一遍。内容不追求数学上的极度严谨而是从工程落地和调参经验的角度去拆解适合正在做视觉任务、时序任务或者想给模型加注意力但不知道怎么选的人参考。1. 注意力到底在注意什么先搞懂那个本质公式先把底层的逻辑说透后面所有机制其实都是这个逻辑的变体。注意力机制最初是受到人类视觉系统启发。人眼看一张图并不会从头到尾均匀扫描每个像素而是快速锁定有信息的区域——比如一辆车、一张脸、一句话里的关键词。这种选择性关注就是注意力。放到神经网络里本质就是一件事让模型学会对输入的不同部分分配不同的权重。重要部分给大权重无关部分给小权重输出的结果变成带权重的加权求和。所有注意力机制都能被统一到一个框架里理解我们有三组变量Query查询、Key键、Value值。Query表示我现在想找什么Key表示输入里每个位置的特征标识Value就是输入里每个位置真正的信息。注意力计算的本质分三步计算Query和每个Key的相似度得到注意力分数。把注意力分数过softmax归一化成和为1的权重。用权重对Value做加权求和得到输出。公式写出来是Attention(Q, K, V) softmax(QK^T / √d) · V这个框架是理解一切注意力机制的钥匙。SE通道注意力是怎么来的是把Query换成全局描述向量从通道维度做加权。CBAM又是怎么来的在通道注意力基础上加了一步空间维度的加权。自注意力呢Q、K、V都来自输入本身让每个位置和所有其他位置算相关性。时序注意力则是把位置换成时间步在时间维度上算权重。我见过不少人把注意力理解成一个给特征图乘权重的模块这种理解太窄了。注意力是一个非常通用的特征重标定手段可以作用于通道、空间、时间甚至任意组合。想用好它先得抛开具体形式理解它只是加权求和这一件事。从工程角度还有个隐性问题注意力机制的参数量和计算量各有多少这两个指标经常被混淆。参数量决定模型文件大小和训练时显存占用计算量决定推理时开销。不同注意力机制在这两个维度上的表现差异很大。后面我会逐个算给你看。2. SE通道注意力一个全局池化就能涨点的经典模块SESqueeze-and-Excitation是2018年提出的通道注意力机制也是目前最简单、引用量最高的注意力模块之一。它的核心思想就一句话给每个特征通道学习一个权重告诉模型哪些通道重要、哪些通道可以忽略。2.1 Squeeze和Excitation分别做了什么SE模块分两个阶段。Squeeze阶段做的事是全局平均池化Global Average Pooling。假设输入特征图是C×H×W也就是有C个通道每个通道是H×W的平面。全局平均池化把每个通道的H×W个数值取平均压缩成一个值。这样你就得到了一个长度为C的向量这个向量的每个元素代表对应通道的全局响应强度。为什么用平均池化而不是最大池化一个直观的解释是平均池化捕捉的是通道的整体响应水平能反映这个通道平均激活了多少。最大池化只关心最强烈的那个响应点容易丢掉整体分布信息。SE作者在论文里也试过最大池化效果不如平均池化稳定。但后来的CBAM把两种池化都用了原因是它们能互补——一个偏全局平均感知一个偏最强刺激感知。这就引出一个经验不同任务对统计量的偏好不同分类任务通常平均池化更稳检测任务里最大池化偶尔有奇效。Excitation阶段做的事是两个全连接层。第一个全连接层把C维压缩成C/r维过ReLU激活第二个全连接层再恢复成C维过Sigmoid激活。r是缩减比例通常取16。这样每个通道得到一个0到1之间的权重乘回到原始特征图的每个通道上就完成了重标定。这里有两个关键设计值得深挖。为什么中间要压缩维度直接用一个C×C的全连接层不就行了吗压缩是为了减少参数量和计算量。如果直接C到C参数量是C²当通道数是1024时光这个全连接层就是一百万参数模块就不轻量了。引入瓶颈结构后参数量变成2×C×(C/r)r16时是原来的2/r也就是八分之一。而且这个瓶颈结构还能强制模型学习通道之间的非线性关系在低维空间里提炼共性特征相当于加了正则化。为什么最后用Sigmoid而不是Softmax因为Sigmoid允许多个通道同时被增强而Softmax会强制通道之间竞争所有输出和为1。在实际特征图中往往多个通道都包含有用信息强制竞争反而会抑制表达。2.2 SE的实现和参数量计算PyTorch代码大概长这样import torch import torch.nn as nn class SEBlock(nn.Module): def __init__(self, channels, reduction16): super().__init__() self.squeeze nn.AdaptiveAvgPool2d(1) self.excitation nn.Sequential( nn.Linear(channels, channels // reduction), nn.ReLU(inplaceTrue), nn.Linear(channels // reduction, channels), nn.Sigmoid() ) def forward(self, x): b, c, h, w x.shape y self.squeeze(x).view(b, c) y self.excitation(y).view(b, c, 1, 1) return x * y参数量算起来很简单第一个线性层是(C/r)×C第二个是C×(C/r)共2C²/r。以ResNet50为例最后一个stage的通道数是2048r16时单模块参数量约2×2048²/16≈52万。这个量级放在整个ResNet50的2500万参数里不算多。SE模块能嵌入几乎所有主流网络结构加在残差分支里、加在卷积之后都行。我实际测试的经验是加在网络的深层比加在浅层收益更明显。原因也好解释浅层特征图分辨率高空间细节更重要通道间的区分度不够深层特征图语义信息强通道维度更能体现是什么类别的差异通道注意力在这里发挥空间更大。2.3 SE的短板完全丢掉了位置信息SE最大的问题在于它只看通道维度上的全局平均响应完全忽略了空间位置信息。两个极端例子能说明问题一张图里左上角有一只猫右下角有一片天空。SE会把猫通道和天空通道分别加权但它不知道猫在左上角、天空在右下角。如果目标识别需要猫和天空的相对位置这个信息SE就无能为力。两个像素在所有通道上的响应完全相同SE会认为它们一样重要但从空间位置看一个在目标中心、一个在背景角落重要性显然不同。这就是为什么后来出现了空间注意力、坐标注意力等一系列改进方案。理解了SE的边界你才能明白后面这些模块到底是在补什么坑。3. CBAM通道注意力加空间注意力顺序为什么是先通道后空间CBAMConvolutional Block Attention Module的思路很清楚既然SE只管通道那我再加一路空间注意力通道和空间都管问题不就解决了整体结构也确实直白——输入特征图依次经过通道注意力模块和空间注意力模块输出就是精炼后的特征图。3.1 通道子模块比SE多了什么CBAM的通道注意力部分和SE长得非常像唯一明显区别是CBAM同时用全局平均池化和全局最大池化两个分支各自过共享的全连接层然后把两个输出逐元素相加再过Sigmoid。这里为什么要加一条最大池化分支我前面说了平均池化反映通道整体的激活水平最大池化反映通道最强响应的显著性。这两个信息有不同的语义平均池化的响应可能被大量中等强度的激活拉高最大池化的响应则说明这个通道在某个局部区域有很强的响应——这种强响应往往对应目标的判别性特征。把两者相加相当于同时考虑整体表现和局部亮点。一个容易忽略的细节是两个池化分支必须共享同一个全连接层不能各学各的。如果各学各的两个分支就学成了两个独立的通道评价器相加时尺度不一致训练不稳定。共享参数强制两个统计量映射到同一个度量空间相加才有意义。3.2 空间注意力模块为什么用7×7卷积通道注意力输出的结果是一个C×H×W的、通道已加权过的特征图。空间注意力要在这上面算出H×W的权重图。做法是对特征图在通道维度上分别做平均池化和最大池化得到两个H×W的平面把两个平面concat起来得到2×H×W再用一个7×7的卷积把它们融合成1×H×W过Sigmoid然后乘回特征图。这里有两个选型问题。第一个问题为什么在通道维度上池化通道维度的平均池化把所有通道的信息压缩成一张综合响应图最大池化压缩成最强响应图这两张图从不同角度刻画了哪些空间位置包含值得关注的信息。第二个问题为什么用7×7卷积而不是3×3空间注意力的本质是让模型感知到某个局部区域是否重要这需要一定的感受野。7×7卷积能覆盖更大范围的上下文帮助判断某个位置是目标的一部分还是孤立噪声。实测经验是小目标检测任务中7×7的稳定性确实优于3×3。不过它也带来了约49倍于3×3的计算开销对单通道2×H×W输入而言在资源紧张的场景下可以降级到5×5或3×3效果折扣通常在1%以内。还有一个更应该记住的顺序问题CBAM内部是先通道注意力、后空间注意力。这个顺序不是随手定的。逻辑是通道注意力先在是什么层面筛选有价值的通道空间注意力再在在哪里层面精确定位通道中需要强化的区域。如果反过来先做空间加权会平等地对待所有通道等做通道加权时已经丢失了部分空间区分度。作者在论文里做过消融实验通道在前、空间在后的组合效果最优。3.3 CBAM代码实现与计算量对比import torch import torch.nn as nn class ChannelAttention(nn.Module): def __init__(self, channels, reduction16): super().__init__() self.mlp nn.Sequential( nn.Linear(channels, channels // reduction), nn.ReLU(inplaceTrue), nn.Linear(channels // reduction, channels) ) self.pool_avg nn.AdaptiveAvgPool2d(1) self.pool_max nn.AdaptiveMaxPool2d(1) def forward(self, x): b, c, h, w x.shape avg_out self.mlp(self.pool_avg(x).view(b, c)) max_out self.mlp(self.pool_max(x).view(b, c)) weight torch.sigmoid(avg_out max_out).view(b, c, 1, 1) return x * weight class SpatialAttention(nn.Module): def __init__(self, kernel_size7): super().__init__() self.conv nn.Conv2d(2, 1, kernel_size, paddingkernel_size // 2) def forward(self, x): avg_out torch.mean(x, dim1, keepdimTrue) max_out, _ torch.max(x, dim1, keepdimTrue) attn torch.cat([avg_out, max_out], dim1) attn torch.sigmoid(self.conv(attn)) return x * attn class CBAM(nn.Module): def __init__(self, channels, reduction16, kernel_size7): super().__init__() self.channel_attn ChannelAttention(channels, reduction) self.spatial_attn SpatialAttention(kernel_size) def forward(self, x): x self.channel_attn(x) x self.spatial_attn(x) return x从计算量角度看CBAM比SE多了一个空间注意力模块额外开销主要来自7×7卷积。但这个卷积作用在2×H×W上输入通道极小所以总计算量增加并不大。以224×224输入为例SE的FLOPs增加大约0.1%CBAM大约0.3%都属于性价比极高的范畴。实际使用中CBAM在检测和分割任务上的表现通常比纯SE好一点这是因为这类任务本身对空间位置敏感。但如果你是做细粒度图像分类只关心这是什么品种而不关心它在哪里SE和CBAM的差距往往很小考虑到部署复杂度用SE就够了。4. 坐标注意力CA把位置信息塞进通道注意力CBAM虽然同时考虑了通道和空间但它有个先天缺陷空间注意力部分用的是2D卷积虽然能感知哪里重要却没有把精确的位置坐标信息编码进特征。更关键的是CBAM在MobileNet这类轻量网络上会带来额外的卷积开销移动端部署不友好。CACoordinate Attention就是为了补这个坑提出的。它不搞复杂的卷积分支而是把通道注意力拆成两个方向让模型在计算权重时同时感知空间坐标。4.1 从全局池化到两个方向的池化SE用全局平均池化把H×W压缩成一个点这个操作一步到位但也把空间结构全扔了。CA的做法是既然直接压成点会丢信息那就先把H和W分开处理。具体来说输入C×H×W的特征图CA做两次池化沿水平方向对每一行做平均池化得到C×H×1的特征每个位置编码了这一行所有列的平均响应。沿垂直方向对每一列做平均池化得到C×1×W的特征每个位置编码了这一列所有行的平均响应。这样一来水平分支保留了每一行在哪些列上有强响应的信息垂直分支保留了每一列在哪些行上有强响应的信息。两个分支配合就能重构出一个大致的2D位置感知。接下来的操作有点巧妙。两个分支的特征先各自变形拼接到一起过1×1卷积降维再过BN和激活函数。然后沿着原来的方向把特征再拆开各过一个1×1卷积恢复通道数过Sigmoid得到两组权重。最后把水平权重和垂直权重做外积实际上是逐元素相乘的广播形式乘回原始特征图。这么设计的精妙之处在于最终的权重同时包含了某个通道在水平方向的哪些位置重要和垂直方向的哪些位置重要这两方面信息二者相乘后就近似得到了这个通道在2D空间的哪里重要。整个过程没有任何2D卷积计算开销极小特别适合移动端网络。import torch import torch.nn as nn class CoordAttention(nn.Module): def __init__(self, channels, reduction32): super().__init__() self.pool_h nn.AdaptiveAvgPool2d((None, 1)) self.pool_w nn.AdaptiveAvgPool2d((1, None)) hidden max(8, channels // reduction) self.conv1 nn.Conv2d(channels, hidden, 1) self.bn1 nn.BatchNorm2d(hidden) self.act nn.ReLU(inplaceTrue) self.conv_h nn.Conv2d(hidden, channels, 1) self.conv_w nn.Conv2d(hidden, channels, 1) def forward(self, x): b, c, h, w x.shape x_h self.pool_h(x) # b,c,h,1 x_w self.pool_w(x).permute(0, 1, 3, 2) # b,c,w,1 y torch.cat([x_h, x_w], dim2) # b,c,hw,1 y self.conv1(y) y self.bn1(y) y self.act(y) x_h, x_w torch.split(y, [h, w], dim2) x_w x_w.permute(0, 1, 3, 2) # b,c,1,w a_h torch.sigmoid(self.conv_h(x_h)) a_w torch.sigmoid(self.conv_w(x_w)) return x * a_h * a_w4.2 CA的适用场景和实际收益CA目前最典型的应用是语义分割、目标检测和姿态估计这类需要位置信息的任务。为什么因为这些任务的输出本身就有很强的空间结构——人和背景的区别不仅仅在有没有人更在人在哪个位置。SE在人在图中有强响应这个层面上能给出权重但CA能进一步给出人在图片中偏左还是偏右、偏上还是偏下的感知。我在一个行人检测项目里做过对比实验以MobileNetV3为backbone不额外增加计算量的前提下把SE换CAmAP涨了1.4%。这个涨幅在检测任务里算不错的了关键是CA带来的参数增量几乎可以忽略。CA也有它的弱点。因为它把空间信息压缩成了两个方向的统计量本质上还是一个方向级而非像素级的空间感知。对于需要精确定位到像素的任务比如实例分割的mask预测CA的粒度不够细。这种场景下要么用自注意力要么在CA基础上再叠加一个轻量的空间注意力模块。顺手提一个CA的实现细节代码里把池化后的w方向分支做了permute目的是让两个分支在拼接时有相同的形状b,c,d,1方便concat。很多人自己复现CA时踩过这里的坑拼接维度对不上报错其实就是permute顺序忘了加。5. 自注意力与多头机制从给特征加权到特征之间互相加权前面讲的SE、CBAM、CA注意力来源都是特征图自身的全局统计信息属于对特征图做重标定。自注意力Self-Attention的思路完全不同让每个位置和所有其他位置直接计算相关性根据相关性加权聚合信息。这套机制是Transformer的核心也是目前大模型的基础构件。5.1 Query、Key、Value到底从哪来很多初学者第一次接触自注意力就被Q/K/V这三个字母劝退了。其实从概念上理解很简单。假设输入是一个序列每个位置有一个特征向量比如一句话里的每个词对应一个embedding或者一张特征图上的每个像素对应一个C维向量。Query查询我想找什么信息由当前位置的特征向量通过一个线性变换得到。Key键我有什么信息可以被找到由每个位置的特征向量通过另一个线性变换得到。Value值找到之后我能拿到什么内容通过第三个线性变换得到。计算过程是当前位置的Query去和所有位置的Key做点积点积结果越大说明这个位置有我需要的相关信息。经过softmax归一化后用这些权重对所有位置的Value做加权求和得到当前位置的最终输出。为什么要用三个不同的线性变换而不是直接用原始特征做点积线性变换的目的是把检索和内容分离开。原始特征里既包含这个位置是谁的信息也包含这个位置带着什么内容的信息混在一起做相似度计算会互相干扰。通过三个可学习的变换模型可以自由调整度量空间让Query和Key的点积更准确地反映是否相关。自注意力里有个关键操作点积结果要除以√dd是每个头的维度。为什么假设Q和K的每个元素是均值为0、方差为1的随机变量那么d维向量的点积结果的方差是d标准差是√d。如果不缩放点积值会随维度增大而变得很大softmax函数的梯度会进入饱和区几乎推不动参数。除以√d相当于把方差拉回1让softmax工作在线性区附近训练稳定性大幅提升。这个细节很微妙但忘了它你的模型很可能训不上去。5.2 多头注意力每头学一种关系模式多头注意力就是在自注意力的基础上把Q/K/V拆成h组head每组单独做自注意力计算最后把h个结果拼起来再过一个线性变换。为什么要拆多头单头自注意力在计算每个位置的输出时只能建立一种相似度度量。但输入特征之间的关系往往是多维的在一句话里一个词可能既和语法上的主语相关又和语义上的宾语相关在一张图里一个像素可能既和颜色相近的像素相关又和属于同一物体的远程像素相关。单头注意力只能抓其中一种相关模式多头让模型并行学习多种模式。拿八头注意力来说有的头可能学到位置相邻关系有的头学到语义相似关系有的头学到颜色一致性关系。最终拼接时这些信息被综合起来表达能力远强于单头。实现多头注意力的PyTorch写法有技巧import torch import torch.nn as nn import torch.nn.functional as F class MultiHeadAttention(nn.Module): def __init__(self, d_model, num_heads): super().__init__() assert d_model % num_heads 0 self.d_k d_model // num_heads self.num_heads num_heads self.w_q nn.Linear(d_model, d_model) self.w_k nn.Linear(d_model, d_model) self.w_v nn.Linear(d_model, d_model) self.out_proj nn.Linear(d_model, d_model) def forward(self, x, maskNone): b, n, _ x.shape q self.w_q(x).view(b, n, self.num_heads, self.d_k).transpose(1, 2) k self.w_k(x).view(b, n, self.num_heads, self.d_k).transpose(1, 2) v self.w_v(x).view(b, n, self.num_heads, self.d_k).transpose(1, 2) scores torch.matmul(q, k.transpose(-2, -1)) / (self.d_k ** 0.5) if mask is not None: scores scores.masked_fill(mask 0, float(-inf)) attn F.softmax(scores, dim-1) out torch.matmul(attn, v) out out.transpose(1, 2).contiguous().view(b, n, -1) return self.out_proj(out)核心是把特征维度d_model先变形为(num_heads, d_k)用transpose把头的维度挪到batch后面这样所有头的计算能一次性并行完成前提是特征维度能被头数整除。这个整除约束是个容易忽略的坑——设了奇数个head或者d_model不是head数的整数倍代码直接崩。5.3 自注意力的成本和定位和SE/CBAM不是替代关系自注意力的最大优势是感受野极大。SE、CBAM、CA都还局限于局部操作或者全局统计自注意力则是每个位置都和所有位置直接交互能建模长距离依赖。这也是为什么Transformer在机器翻译、图像分类这些任务上能碾压纯卷积网络。但它贵得也非常明显。假设序列长度是n自注意力的计算复杂度是O(n²)。对于一张224×224的图像像素级自注意力的n是50176计算量是不可接受的。这也是为什么视觉TransformerViT要把图像切成patch——把n从5万降到19614×14的patch计算量直接降了几个数量级。所以我的个人判断是SE/CBAM/CA这类轻量注意力模块和自注意力解决的是不同层次的问题。前者适合作为卷积网络的即插即用模块用很小的代价提升baseline后者适合作为骨干网络的核心构件承担全局特征交互。大多数实际项目不需要把backbone换成Transformer先用SE或CBAM把baseline提上去再评估是否值得上自注意力这个顺序是性价比最高的。6. 时序注意力当特征维变成时间维前面讨论的都是图像任务注意力在时间序列里的应用常常被视觉从业者忽略。时序注意力机制的原理并不复杂但应用方式和视觉里的注意力差别很大值得单独拿出来讲。6.1 时序注意力的两种形态时序注意力的第一种形态是时间步注意力输入是一个时间序列每个时间步有一个特征向量给每个时间步学习一个权重加权求和得到整个序列的表示。这种形态适合分类或者回归任务——比如用一段脑电信号判断是否异常不需要保留每一时刻的细节只关心哪些时刻最有判别性。第二种形态是编码器-解码器注意力解码器在生成某个时刻的输出时需要从编码器的所有时间步里检索相关信息。经典的Bahdanau Attention就是这种形态它计算解码器当前时刻的Query和编码器所有时间步的Key之间的相关性用相关性权重去加权编码器的Value。原理上时序注意力和SE的计算流程几乎一致区别只在权重作用在哪个维度。SE的权重作用在通道维度时序注意力的权重作用在时间维度。6.2 实现一个简单的时间步注意力假设输入形状是B×T×D也就是批量B、时间长度T、每步特征维度D。我们想给T个时间步各学一个权重import torch import torch.nn as nn import torch.nn.functional as F class TemporalAttention(nn.Module): def __init__(self, d_model): super().__init__() self.score_net nn.Sequential( nn.Linear(d_model, d_model // 2), nn.Tanh(), nn.Linear(d_model // 2, 1) ) def forward(self, x): # x: b,t,d scores self.score_net(x).squeeze(-1) # b,t weights F.softmax(scores, dim-1) # b,t context torch.bmm(weights.unsqueeze(1), x).squeeze(1) # b,d return context, weights这里有个值得注意的细节打分函数用了Tanh而不是ReLU。因为时间步的权重理论上应该允许正负贡献ReLU把负数全截断了会让某些时间步被迫无视而不是抑制效果往往不如Tanh。时序注意力的一个常见误用是直接把所有时间步的特征做softmax加权求和丢掉了时间顺序。时间序列之所以是时间序列就是因为顺序本身包含信息——一个上升趋势和一个下降趋势即使数值分布相同含义也完全不同。如果你的模型用注意力把所有时间步揉成一个向量就再也分不清先涨后跌和先跌后涨了。正确做法是在注意力输入前先通过一层循环网络或卷积网络把时间顺序编码进特征或者保留时序注意力的同时额外拼接一个位置编码。6.3 时序注意力在项目里的实践经验我在做工业设备振动信号分类的项目时用过这个模块。原始信号按窗口切分成每秒2048个采样点每个窗口提取时频特征后得到T×D的序列。加时间步注意力前后分类准确率从91.2%提升到93.8%提升主要来自模型学会了忽略启动阶段的异常抖动重点关注稳态阶段的特征。另一个经验是时序注意力模块放的位置很关键。放在特征提取之前注意力分数还停留在原始信号层面噪声影响大放在特征提取之后、分类头之前此时的特征已经更有语义区分度注意力更容易学到有意义的时间权重。我通常建议放在最后那个全局池化层之前作为一个软选择层替代简单的平均池化。7. 选型实战不同任务下我推荐用哪种注意力把常见机制都过了一遍之后到了最实际的问题我的项目到底该用哪一个我根据自己跑过的项目和一些公开的论文结论整理了下面的选型建议。7.1 各注意力机制对比机制作用维度额外参数量级核心优势主要限制SE通道低极轻量即插即用无空间感知CBAM通道空间低兼顾通道和空间7×7卷积增加少量计算CA通道位置编码极低保留坐标信息移动端友好空间感知精度不足自注意力全局时空高长距离依赖建模O(n²)计算数据需求大多头自注意力全局时空高多关系模式并行同上且超参更多时序注意力时间步低突出判别性时刻需配合时序编码器使用7.2 按任务类型的推荐图像分类大模型训练如果你的backbone已经是ResNet50以上数据量也够大可以先试SE。数据量在百万级以下SE比自注意力更稳因为自注意力更容易过拟合。目标检测优先试CBAM。检测任务对位置敏感通道注意力和空间注意力协同作用比单用SE稳定涨点。特别提醒CBAM加在FPN的每一层输出上比只加在backbone上效果更好代价是IO开销增大需要评估推理速度。语义分割/姿态估计CA是性价比最高的选择它能在几乎不增加参数的情况下给模型提供位置线索。如果分割任务对小目标要求极高考虑在CA基础上叠加一层自注意力但要注意显存占用。移动端轻量模型无脑选CA。ME的7×7卷积在Deep-wise网络上有下采样倾向CA的两个1×1卷积几乎是零成本。MobileNetV3默认架构里就带SE很多工程实践表明换CA后相同FLOPs约束下精度更高。时间序列分类用6.2节的时间步注意力放在特征提取层之后、分类头之前。如果序列极长先用一维卷积降采样否则时间步注意力在几千步长度上容易变成均匀分布——softmax的输出会趋于扁平学了等于没学。7.3 几个容易踩的坑注意力模块不是加得越多越好。我见过有人把SE、CBAM、CA全部串在同一个block里结果训练时梯度消散loss不降反升。原因是这些模块都是乘法门控多个门控串联会不断缩放特征值前向传播时数值越乘越小反向传播时梯度越乘越细。一个block里最多放一个通道注意力和一个轻量空间注意力不要再叠第三种。Sigmoid输出的注意力权重分布容易出现饱和。当通道数极大且训练初期权重更新过猛时Sigmoid很容易输出0.99甚至1.0乘法门控直接退化成恒等映射注意力模块完全失效。解决办法是给注意力权重视情况加一点L2正则或者把Sigmoid换成带温度参数的版本初始温度大一点让权重往0.5附近分布训练中再逐渐降温。注意力可视化不能只看热力图。很多人把特征图加权后的热力图直接当模型的关注区域但热力图只能反映权重大的位置不能反映权重小的位置是否被正确忽略。比如一个模型预测出猫时热力图集中在猫头上但这不代表它没有错误地关注了背景区域——可能在某个通道里背景的信息权重也很高只是被softmax或者其他通道的数值掩盖了。要做严谨的归因分析应该用Grad-CAM或者积分梯度这类方法而不是简单的注意力热力图。7.4 我个人的选型经验总结最后说点没有写在论文里的经验。注意力机制本质上是在已有特征不太够用的时候帮你把信息重新分配它不能凭空创造新信息。如果你的模型本身特征提取能力很弱比如浅层网络训练不充分加什么注意力都救不回来。先确保baseline是收敛的、可复现的再上注意力模块否则你根本分不清涨点是因为注意力还是因为训练过程本身的变化。另外一个心态上的建议别迷信最新。CA出来之后视觉社区很快又出了几十种变体各种Coordinate Attention、Efficient Attention层出不穷。但很多变体只在一两个数据集上微涨泛化性存疑。工程上最稳妥的做法是先建立一个标准化的评估流程把SE、CBAM、CA这三种在固定配置下跑一遍选择一个稳定涨点的然后去优化数据、增强和损失函数——这些往往比换一个更新奇的注意力模块收益更大。我在多个项目里反复验证过这个观点主流的SE、CBAM、CA之间在同一baseline上的精度差异通常在2%以内但数据质量、增强策略和训练调度带来的差异动辄5%以上。注意力机制值得用但别把它当成解决所有问题的银弹。先把基本功做好再加注意力这才是最务实的路。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门