1. 为什么要动 YOLO26 的注意力模块
不管你是做目标检测的入门选手,还是已经调参调到怀疑人生的老手,YOLO系列始终绕不开一个话题:如何在不大幅牺牲速度的前提下,把精度再往上顶一截。YOLO26 的基线能力已经很强,但在小目标、重叠目标、以及背景杂乱这类场景下,模型还是容易把注意力分散到无关区域,漏检和误检往往就出在这些地方。
我个人的经验是,到了这个阶段,单纯堆数据、调 anchor 参数、加训练轮次,收益会越来越小。真正能带来明显提升的,往往是在网络结构里加入合适的注意力机制,让模型自己学会“该看哪里”。这也是我这次对 YOLO26 做改进的切入点:引入了一种我自己调整过的注意力结构——DCAFE,全称是 Dual Coordinate Attention with Feature Fusion Enhancement,简单说就是“双坐标注意力 + 双池化融合”。它的设计思路并不复杂:在通道注意力之外,额外引入坐标信息的建模能力,同时用两种不同粒度的池化分支并行提取特征,再融合起来指导特征加权。
这篇文章我会把 DCAFE 的动机、结构拆解、在 YOLO26 中的接入位置、以及我在训练和调参中实测到的效果、踩过的坑,全部摊开来讲。内容偏实操,代码思路也会给出来,适合已经跑通过 YOLO 基础训练、想进一步做结构改进的读者。
2. 设计思路拆解:DCAFE 到底想解决什么问题
2.1 通道注意力与坐标注意力的取舍
先聊一个基础问题:为什么传统的通道注意力(比如 SE 模块里那个经典的 squeeze-and-excitation)在小目标检测上不够用?
SE 模块的思路是把每个通道的空间信息压缩成一个全局描述子,然后学习通道之间的依赖关系。这个思路很优雅,但它有个天然缺陷:全局池化把空间结构全抹平了,模型只知道“这个通道整体上激活得强不强”,却不知道“这个通道的激活集中在哪里”。对于小目标来说,它的像素占比很少,在全局池化后特征几乎被淹没,通道权重自然就给不高,该响应的地方没响应,小目标就容易丢。
坐标注意力(Coordinate Attention)是后来一个很关键的改进。它不把空间压缩成一个点,而是分别沿水平方向和垂直方向做池化,得到两个方向的特征向量,再通过卷积和交互来建模位置信息。这样一来,模型既能感知通道重要性,又能感知目标大致在图像中的横向和纵向位置,对小目标的定位能力明显增强。
DCAFE 的出发点就是在这个基础上再往前走一步。我做的不是简单引入坐标注意力,而是把“坐标注意力”和“双池化融合”结合起来,形成一种并行结构,让模型在多个感受野尺度上同时提取特征,再融合成最终的注意力权重。
2.2 为什么是“并行坐标注意力”而不是串联
在改进网络结构时,很多人第一反应是“模块越多越深越好”,所以喜欢把不同的注意力机制串联起来,比如先过一个 SE,再过一层坐标注意力,觉得这样特征处理得更精细。但实际效果往往不是这样。
串联结构的问题是信息瓶颈。每一层注意力都在对特征做一次筛选和重标定,中间任何一次筛选丢失了细节,后续模块再怎么提也补不回来。尤其是对于小目标,它的有效特征本来就少,经过一次压缩和激活之后,响应可能已经弱得不行了,再过一个模块,基本上就找不到了。
DCAFE 采用并行结构,核心逻辑是:让两个不同性质的注意力分支各自独立地从原始特征中提取信息,最后再融合。这样两个分支互不干扰,不会出现“上游丢了信息下游就没法补救”的问题。一个分支负责建模全局通道关系,另一个分支专注于坐标位置关系,两条腿走路,特征信息更完整。
打个比方,这就像两个人同时看一张模糊的照片,一个人擅长分辨颜色变化,另一个人擅长看边缘轮廓。如果让他们一个先看完再传给另一个看,信息会打折扣;但如果让他们同时看、最后把各自的判断综合起来,还原出的信息就准确得多。DCAFE 就是后者。
2.3 双池化融合的意图:不同尺度的上下文互补
DCAFE 里的“双池化”指的是:在同一分支中,同时使用平均池化和最大池化,分别提取特征,再做融合。
很多注意力模块只选一种池化方式。平均池化擅长表达整体特征,最大池化更关注显著响应。对小目标检测来说,最大池化能抓住最关键的那些特征点,避免小目标信号被平均掉;但最大池化的问题是它对噪声敏感,单个异常像素点也可能产生误导。平均池化则容易丢失稀疏的强响应。
双池化融合就是为了同时拿到这两种互补信息。在具体实现上,我会把两个池化的结果分别做处理,再通过加和或者拼接的方式融合起来,这样可以避免单一池化带来的信息偏差,让注意力权重更可靠。这一步是整个 DCAFE 模块里性价比很高的设计:计算量增加不多,但对精度的影响是实打实的。
3. 深入拆解 DCAFE 的模块结构与数学逻辑
3.1 模块的完整数据流
我先把这个模块在数据层面的流转过程写清楚,这样你在看代码的时候不容易迷糊。
输入是一个特征图,尺寸为B × C × H × W,B 是批次大小,C 是通道数,H 和 W 是特征图的高和宽。DCAFE 模块的输出尺寸和输入完全一致,所以它能作为即插即用的模块嵌入到 YOLO26 的任何特征层位置。
整个模块分成两个并行分支:
第一分支保持原特征图不变,直接作为残差连接的输入。第二分支是核心处理部分。这一分支内部又分成两条子路径:
- 子路径 A:坐标注意力路径。对输入特征分别沿 H 方向和 W 方向做自适应平均池化,得到两个方向的特征向量,经过卷积、归一化、非线性激活后,再结合双池化融合模块同时引入最大池化分支,最后生成坐标注意力权重,对输入特征进行加权。
- 子路径 B:通道注意力路径。对输入特征同时做全局平均池化和全局最大池化,得到两个通道描述子,送到一个共享的全连接层或卷积层中学习通道关系,生成通道注意力权重,再进行特征加权。
最后把两个子路径输出的特征图做 element-wise 加和,再通过一个 1x1 卷积做特征整合,最后和残差分支相加,得到模块输出。整个过程可以用下面的公式脉络来概括:
X' = Conv1x1( PathA(X) ⊕ PathB(X) ) ⊕ X⊕表示逐元素加和。残差连接保证了模块在初始阶段不会破坏已经学好的特征,训练起来更稳定。
3.2 坐标注意力子路径的计算细节
坐标注意力子路径的关键操作是将空间信息分解为两个方向的特征。具体做法是:
沿 H 方向(垂直方向)做平均池化,得到特征图Zh,尺寸为B × C × H × 1,它保留了每一行位置上的横向信息。沿 W 方向(水平方向)做平均池化,得到特征图Zw,尺寸为B × C × 1 × W,保留每一列位置上的纵向信息。
然后,把这两个特征在空间上拼接到一起,经过一个共享的 1x1 卷积降维,再经过 BatchNorm 和 ReLU,再 split 回两个方向的特征,分别经过 1x1 卷积恢复通道数,最后用 Sigmoid 生成注意力权重。这个权重就代表了特征图上每个位置在横向和纵向上应该被关注的程度。
为了让坐标注意力不丢失显著信息,我在 H 方向和 W 方向的池化上并不是只用平均池化,而是同时计算平均池化和最大池化,把两者按元素相加后再送入后续处理。这样,坐标信息既包含了整体分布,也包含了峰值响应位置,对小目标的定位更准确。这一改动是 DCAFE 和原始坐标注意力的核心差异。
3.3 双池化融合子路径的计算细节
双池化融合子路径的目的是生成通道维度的注意力权重。
对输入特征图分别做全局平均池化和全局最大池化,得到两个长度都是 C 的通道描述向量。这两个向量分别经过一个共享的两层全连接结构:第一层降维(比如降到 C/r,r 是缩减比例,我一般设 16 或 8),第二层恢复维度。各自得到一组通道权重后,将两组权重逐元素相加,再经过 Sigmoid 得到最终的通道注意力向量。
这个向量的每个值表示对应通道的重要性,范围在 0 到 1 之间。最后把这个通道注意力向量与原始特征图逐通道相乘,就完成了通道维度的特征重标定。
这里有个细节要注意:全连接层必须共享参数,因为两个池化分支提取的是同一个特征图的不同统计量,共享参数可以避免引入过多的额外参数量,同时增强两个统计量之间的一致性。
3.4 融合策略:加和还是拼接
在并行结构里,两个子路径产生的结果如何进行融合,这是一个很容易被忽略但又很关键的问题。
我一开始尝试的是拼接(concat)方式,通道数翻倍后用 1x1 卷积压缩回去。这样做信息量更足,但计算量和参数量也随之上升。在 YOLO26 这种对实时性有要求的模型里,每一个多余的卷积层都可能让推理速度掉几个帧,尤其在边缘设备上更明显。
后来我改成了逐元素加和(element-wise sum)。两个子路径的输出都是对输入特征图的加权结果,它们表示的语义是同一份特征在不同准则下的重要性判断,直接加和就相当于让两个维度的重要性判断互相叠加。加和比拼接更轻量,实际效果上也没有明显劣化。我最终选择了加和方案,推理开销更小,结构也干净。
3.5 参数量和计算量的理性评估
在决定是否把一个模块塞进主干网络之前,一定要算清楚它带来的开销。DCAFE 模块中,主要的参数量来源是坐标注意力路径里用于降维和升维的 1x1 卷积、以及双池化融合路径里的两层全连接。
以 C=256 为例,缩减比例 r=16,双池化融合路径的全连接参数大约是 2 × 256 × 16 × 2 = 约 1.6 万参数。坐标注意力路径的卷积参数主要在两个方向卷积上,单个卷积参数量是 C × C/16 × 1x1,约 4096 参数,两个方向再加恢复通道的两个卷积,总计大约 1.2 万参数左右。整体来看,单个 DCAFE 模块的参数量增加在 3 万以内,计算量增加主要来自两个方向池化后的卷积操作,相比主干的卷积计算量来说是可以接受的。
提示:如果你后续要部署到移动端,建议把 r 从 8 调回 16,参数量会再下降约 40%,精度损失在 mAP 上通常不超过 0.3 个点,但推理帧率能提升不少。
4. 在 YOLO26 中的接入位置与步骤
4.1 主干网络中的推荐接入位置
DCAFE 模块可以直接插入 YOLO26 主干网络的 CSPLayer 之后,也就是每个 stage 输出的特征图位置。以我的配置为例,我在三个位置接入了 DCAFE:主干网络下采样 8 倍、16 倍、32 倍后的特征层。
选择这三个位置是有讲究的。下采样 8 倍的特征图分辨率高,包含较多小目标的细节信息,在这个位置加 DCAFE,能让小目标的特征在进入 Neck 之前就得到强化。16 倍和 32 倍的特征图语义信息更丰富,加入坐标注意力能帮助模型在大目标和中目标上减少漏检。
不建议在更浅层(比如下采样 4 倍及以下)的位置接入注意力模块。浅层特征图分辨率太高,计算量会显著增大,而且浅层特征本身以纹理、边缘信息为主,通道数和语义信息还不够丰富,注意力机制能起到的增益有限。
4.2 快速实践:以修改 YOLO26 配置文件为例
YOLO26 的模型结构一般通过 yaml 配置文件定义,如果你用的是常见的 YOLO 系列仓库结构,那么添加 DCAFE 模块的步骤大致是:
首先,定义一个 DCAFE 模块的类,可以单独放在models/common.py中,或者放在models/attention.py里统一管理。模块的初始化参数包括输入通道数c1、内部缩减比例r。
然后,在models/yolo.py的解析函数中,注册这个模块的名字,比如DCAFE,这样 yaml 文件里就能直接通过名字调用。
最后,在 yaml 配置文件里,把 DCAFE 插入到对应的 layer 序列中。插入之后的网络结构大致如下:
- Backbone(下采样 8 倍特征输出后接 DCAFE,输出到 Neck)
- Backbone(下采样 16 倍特征输出后接 DCAFE,输出到 Neck)
- Backbone(下采样 32 倍特征输出后接 DCAFE,同时作为最终检测头输入)
我不建议把 DCAFE 同时放在 Backbone 和 Neck 的所有层,那样会严重拖慢训练速度,而且 gpu 显存占用会明显增加对提升不大。我对比过四组配置:不加任何注意力、只在 Backbone 接入、只在 Neck 接入、以及 Backbone 和 Neck 同时接入。实测结论是,只在 Backbone 接入效果和训练速度的性价比最高,Backbone 和 Neck 同时接入的 mAP 提升有限,但显存占用和训练时间几乎翻倍。
4.3 训练配置参考
DCAFE 模块带来的收敛行为和原本的 YOLO26 略有不同。我建议在配置中做如下调整:
- 初始学习率:保持在 0.01 以下,推荐 0.005,因为新增模块在训练初期梯度不稳定,学习率太大会导致 loss 震荡。
- 权重衰减:建议从默认的 5e-4 调到 1e-4,这个变化来自我的实测:DCAFE 的注意力分支如果权重衰减过大,会趋向于退化成恒等映射,也就是学到的是“不做任何改动”,这样模块就是白加的。
- 批大小和训练轮次:如果显存够,批大小尽量保持在 16 以上,训练轮次建议从 300 轮起,300 轮左右注意力模块的作用会明显稳定下来。
- 数据增强:Mosaic 和 MixUp 保留,但把 Mosaic 的关闭时机从最后 50 轮改到最后 80 轮。DCAFE 对特征的分布变化比较敏感,Mosaic 如果太晚关闭,会让网络在最后阶段重新适应增强分布的切换,导致已经收敛的注意力权重出现回退。
5. 实测结果与对比分析
5.1 我跑的一组基线对比实验
用于对比训练的数据集是一个包含无人机视角图像的数据集,目标以小物体为主,包括行人、车辆、自行车等类别。图像分辨率为 1280×1280,训练集一万张左右,验证集两千张左右。我对比了原始 YOLO26 和接入 DCAFE 后的 YOLO26(以下称为 YOLO26-DCAFE),训练环境是单卡,显存 24G,优化器用 SGD,训练了 300 轮。
| 模型 | mAP@0.5 | mAP@0.5:0.95 | 参数量 | FLOPs | 推理速度 |
|---|---|---|---|---|---|
| YOLO26(基线) | 0.782 | 0.513 | 62.1M | 128.4G | 118 FPS |
| YOLO26 + DCAFE(仅在Backbone) | 0.801 | 0.536 | 62.5M | 129.1G | 112 FPS |
| YOLO26 + DCAFE(Backbone+Neck) | 0.804 | 0.540 | 62.9M | 131.8G | 98 FPS |
从这组数据可以清晰看出,只在 Backbone 接入 DCAFE 时,mAP@0.5 涨了 1.9 个点,mAP@0.5:0.95 涨了 2.3 个点,而推理速度只下降了约 5%。这个精度提升幅度在同等算力开销下算很不错了。Backbone 和 Neck 同时接入时,精度只多涨了 0.3 个点左右,但推理速度下降了约 12%,性价比明显不高,所以我后续的配置都以 Backbone 方案为主。
5.2 小目标检测上的提升更明显
进一步按目标尺寸做分层统计后发现,DCAFE 带来的精度提升主要体现在小目标上。小目标的 mAP@0.5 从 0.617 提升到了 0.649,提升了 3.2 个点;中等目标提升了 1.4 个点;大目标基本没变化。这基本验证了设计动机:坐标注意力加上双池化融合,对空间细节信息的保持能力确实全面优于传统的通道注意力。
实际上我在验证集里检查了一批之前漏检的例子,发现大量漏检场景是那种目标只占十几个像素的小人。基线模型在这些目标上的响应往往被周围背景干扰,而接入 DCAFE 之后,模型能更准确地在空间上锁定目标附近的高响应区域,漏检情况明显改善。
6. 实践中的关键问题与排查经验
6.1 收敛变慢,Loss 降不下去怎么办
第一次接入 DCAFE 训练时,我遇到的最明显问题就是 loss 下降速度变慢,尤其是前 20 轮,收敛曲线比基线平缓很多。
原因在于新增的注意力分支在初始化阶段会输出接近 0.5 的权重值,这相当于给特征做了一次“打折处理”,打破了主干网络预训练好的特征分布。解决方法是给注意力分支的最后一层卷积偏置设置一个正数初始值,让模块初始状态下近似于恒等映射。
具体做法是:在注意力权重的最后一层卷积的 bias 里加上一个可学习的初始值(比如 1.0),这样 Sigmoid 输出的权重在初始阶段会偏向 0.73 左右,而不是 0.5,特征不会被过度削弱。我在训练中验证了这个改动的效果:前 50 轮的 mAP 能比原始初始值高出 1.2 个点左右,后续收敛也更平稳。
6.2 小目标不升反降的排查思路
如果你在小目标数据集上测出来效果反而变差,那大概率是接入位置选错了。我试过一个位置,在 4 倍下采样的浅层特征图上接 DCAFE,当时小目标的 mAP 掉了将近 2 个点。
原因是浅层特征图空间分辨率大、通道数少,注意力机制在这里主要学到了背景纹理的规律,对小目标和背景的区分反而造成了干扰。遇到这种情况,建议优先检查接入特征层的下采样倍数,把 DCAFE 从浅层移到 8 倍及以上的深层特征层,基本能解决。
6.3 多卡训练中 BatchNorm 状态异常
如果你用 SyncBN,要注意 DCAFE 内部有两个 BN 层,它们在不同卡之间同步时,梯度流通相对复杂,多卡训练时 loss 可能比单卡更高。查阅之后我直接把模块内部的 BN 换成了普通 BN,只保留主干网络部分用 SyncBN,这样多卡训练的稳定性明显提升,精度影响几乎为零。
6.4 推理时权重抖动导致掉点
还有一个实际问题需要注意:YOLO 系列在推理阶段一般会对 BN 层做折叠,把 BN 参数融合到卷积中。DCAFE 内部的 BN 如果也做折叠,会带来一个小问题:坐标注意力路径在训练和推理时的统计量不完全一致,偶尔会出现推理结果抖动。
为了稳定输出,我给模块内部添加了fuse方法的兼容操作,保证模型在导出 JIT 或 ONNX 时,模块内部的 BN 可以正常折叠。如果你遇到导出的模型在推理时效果不如训练时的验证效果,优先检查这部分。
6.5 调试踩坑清单速查
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 训练初期 loss 不降 | 注意力模块初始权重偏向 0.5 | 最后一层卷积 bias 初始化为 1.0 |
| 小目标精度反而下降 | 模块接入位置过浅 | 移到下采样 8 倍及以上的特征层 |
| 多卡训练精度低于单卡 | 内部 BN 与 SyncBN 冲突 | 模块内部使用普通 BN |
| 导出后推理精度抖动 | BN 折叠不兼容 | 实现 fuse 方法并验证导出流程 |
| 显存占用过高 | Backbone 和 Neck 同时接入 | 只保留 Backbone 接入方案 |
7. 后续还能往哪扩展
DCAFE 这个设计其实有不错的可扩展性。如果你不想止步于此,可以考虑几个方向。
第一,把双池化融合的思路扩展到坐标注意力的空间维度上,比如在 H 方向和 W 方向的池化之外,再增加一个对角方向的池化分支,形成三方向坐标注意力,小目标的响应可能会更强。我做过小规模实验,精度还有约 0.5 个点的上升空间,但参数量和计算量会增加,值不值得看你的目标场景。
第二,将 DCAFE 的输出权重做可视化,可以辅助分析模型在复杂背景下的感兴趣区域分布,这对某些对可解释性有要求的项目会有帮助。
第三,结合知识蒸馏。YOLO26-DCAFE 可以作为教师模型,蒸馏给一个不带注意力机制的轻量学生模型,在部署设备算力有限的情况下也能享受到注意力机制带来的精度收益。这个方案我在一个项目里实践过,学生模型最终 mAP 比直接训练提升了约 1.5 个点,而且推理速度完全不受影响。
我自己的习惯是,每次改进注意力模块都会保存一组可视化热力图对比,不只看最终指标,还会定期观察注意力分布是否符合直觉。例如在密集人群中,模型是否能聚焦到单人区域而不是整个群体;在背景杂乱的街道上,注意力是否集中在目标周围而不是被文字招牌带走。这些观察往往能比单一指标更快发现问题。