首页 / 资讯中心 / 文章详情

SDFM模块:注意力引导的Neck特征融合提升关键点检测精度

SDFM模块:注意力引导的Neck特征融合提升关键点检测精度 ★ FEATURED ARTICLE
在关键点检测任务里Neck 的职责远不止“把特征拼到一起”。我之前在调 YOLO26 做人体姿态估计时卡在了一个很微妙的地方深层语义特征能告诉模型“这里大概是一个人”但浅层细节特征才能告诉模型“手腕关节在这几个像素上”。两者一旦融合不当模型就很容易被背景纹理、遮挡边缘这些噪声带偏关键点的定位精度直线下降。这篇文章想聊的 SDFM 表层细节融合模块就是针对这个问题做的改进核心思路是在深层与浅层特征融合时用通道注意力和空间注意力分别控制“该看重什么”和“该盯着哪里”从而把噪声压下去。这个改进适合正在做关键点检测、姿态估计或者细粒度定位任务的人参考尤其是已经在用 YOLO 系列做检测头扩展、想在不伤推理速度的前提下提升精度的朋友。SDFM 不是那种大动干戈换骨干网络的重型方案它更像是在 Neck 融合路径上插入一个带注意力的“交通管制员”改动量可控收益却比较直观。1. 为什么偏偏是 Neck 出了问题1.1 关键点检测对特征的要求和普通检测不一样普通目标检测只要框得准哪怕边缘差几个像素影响也不大但关键点检测对空间位置极度敏感。拿人体姿态估计来说腕关节、踝关节这类位置往往只占十几个像素如果特征图上的响应被周围噪声“摊平”了关键点坐标就会产生好几个像素的偏移反映到 OKSObject Keypoint Similarity指标上就是明显掉点。YOLO26 的 Neck 部分整体思路沿用多尺度特征融合金字塔它试图把浅层的高分辨率细节和深层的高语义信息揉到一起。问题在于融合时如果一视同仁地相加或拼接浅层特征里的纹理噪声和背景边缘也会被放大。比如地面上的阴影、衣服褶皱、遮挡物边界这些在浅层特征里都是强烈的响应但对关键点定位来说全是干扰。深层特征语义强、噪声少但空间分辨率低上采样回来的位置信息本身就带着模糊感。两者一融合浅层的噪声没有被压制深层的定位模糊也没有被修复最后输出的热力图自然不够锐利。1.2 现有融合方式的三个隐性缺陷在 SDFM 之前我试过几类常见方案各有各的问题。第一种是最朴素的 concat 后接卷积。这种方式把浅层和深层特征在通道维度上直接拼起来然后让卷积自己去学权重。理论上卷积能学会抑制噪声但实践中如果目标只占图像很小区域卷积会倾向于把响应平均化很难专门针对关键点区域做精细化响应。而且这种方式没有显式的注意力引导训练需要大量迭代才能勉强学到“哪里重要”。第二种是简单相加element-wise add像 FPN 那样。这种方式轻量但是浅层特征和深层特征的分辨率、语义层级都不一样直接相加相当于默认两边同样重要。我实测过在遮挡较多的场景下相加融合会让模型对遮挡边缘特别敏感关键点常常被“吸”到遮挡物体的边缘上去。第三种是引入 SE 或 CBAM 这类现成注意力模块。它们有效但问题在于这些模块大多设计给分类网络或通用检测对关键点检测这种“空间位置精度优先”的任务不够聚焦。SE 只做通道注意力完全不关心空间上哪里该被强调CBAM 虽然串行接了一个空间注意力但它的空间注意力用的是 7x7 卷积感受野偏大对小尺寸关键点来说不够细腻。SDFM 的设计目标就是同时解决这三个缺陷通道维度上要区分哪些特征通道跟关键点相关空间维度上要区分哪些像素属于关键点区域并且在融合时要保留浅层细节中被确认有用的那部分信息。2. SDFM 模块内部到底做了什么2.1 模块整体结构拆解SDFM 全称是 Surface Detail Fusion Module从名字上看它关注的就是“表层细节”。这种细节指的不是颜色纹理这种表层外观而是边缘、角点、局部梯度这些对关键点定位至关重要的空间线索。整个模块接收两个输入一个是来自 Neck 高层路径的特征图分辨率较低、通道数较多、语义信息强另一个是来自浅层路径的特征图分辨率较高、通道数较少、细节信息丰富。两个特征图先分别做一次 1x1 卷积把通道数对齐然后进入两个并行分支通道注意力分支和空间注意力分支。通道注意力分支的做法是把浅层特征做全局平均池化压成一个一维向量然后经过两个全连接层第一个做降维第二个恢复维度最后接 sigmoid 得到每个通道的权重。这个权重会乘回浅层特征上相当于告诉网络“这批细节通道对关键点是有用的那批通道大概率是噪声压一压”。空间注意力分支的做法是把高层特征和经过通道加权后的浅层特征在通道维度上拼接起来经过一个 1x1 卷积把通道数降到 1再接 sigmoid得到一张和输入特征分辨率相同的空间权重图。这张图每个位置的值表示“这里是不是值得关注的关键点区域”。然后把这个权重图乘回浅层特征上再把结果和高层特征相加。通道注意力和空间注意力串行执行顺序是“通道先空间后”。这个顺序是有讲究的先通过通道注意力把特征通道的质量筛选一遍相当于先保证“用什么看”再做空间注意力把区域聚焦到关键点附近相当于决定“看哪里”。如果顺序反了空间注意力会在一个充满噪声通道的特征图上做选择效果会打折扣。2.2 为什么空间注意力需要融合高层特征来生成这是 SDFM 设计里比较关键的一个细节。空间注意力权重图如果只靠浅层特征自己生成很容易被背景纹理干扰因为浅层特征里“看起来像边缘”的东西太多了。但如果加入高层特征一起生成高层特征里已经包含语义信息知道“这个区域大概属于人体”两者结合生成的权重图既能保留空间分辨率又具备语义引导能力。我打个比方。浅层特征是近视眼看得到很多细节但分不清哪些重要高层特征是远视眼看得出整体轮廓但看不清细节。空间注意力相当于让近视眼戴上远视眼给的“提示眼镜”告诉自己“轮廓内部才是需要仔细看的地方”。具体实现上把高层特征上采样到和浅层特征一样的分辨率然后在通道维度拼接经过卷积生成权重图。高层特征的分辨率低但上采样本身会带来位置误差所以拼接之后的空间权重图并不完美但它已经足够指导浅层特征把注意力集中在关键点可能出现的区域。2.3 和 SE、CBAM 的核心差异做对比测试之前我一直觉得 CBAM 已经够用了。实际把 SDFM 换上去之后才意识到差异在哪里。SE 的问题是只有通道注意力它对“哪里有关键点”毫无概念。通道注意力虽然能筛选特征通道但同一通道在不同空间位置的重要性完全不一样SE 没法区分。CBAM 比 SE 多了空间注意力但它处理的是单张特征图注意力生成过程没有融合多尺度语义信息。而且 CBAM 的空间注意力模块使用较大卷积核对小目标、小关键点不够精细在姿态估计这种任务上显得有些“粗枝大叶”。SDFM 的定位差异点在于它本来就是为跨层融合设计的输入的浅层特征被通道注意力调整之后再与高层特征拼接生成空间注意力然后反哺浅层特征。整个过程是一个“筛选—指导—融合”的链条而不是简单地在单张特征图上做注意力增强。3. 在 YOLO26 里落地 SDFM 的完整实操3.1 YOLO26 的 Neck 结构回顾YOLO26 的 Neck 还是走 C2f 堆叠、上采样拼接、下采样汇合的路子。以 P3、P4、P5 三层特征为例P3 是浅层高分辨率特征P4 是中层特征P5 是深层低分辨率特征。常规的做法是 P5 上采样后和 P4 拼接融合结果再上采样和 P3 拼接最后输出三个尺度的特征给检测头。SDFM 的插入位置有两处最合适一是 P5 上采样和 P4 融合时二是融合结果再上采样和 P3 融合时。这两处都是典型的跨尺度融合浅层细节和深层语义在这里交汇正好是 SDFM 发挥作用的位置。我在实际改动里只在 P4 层面插入了 SDFMP3 层面保留了原有融合方式。原因有两点P3 分辨率太高直接做空间注意力计算量偏大而且 P3 的细节信息虽然丰富但噪声也最多全靠注意力压制不一定划算P4 层次适中既保留了足够的空间分辨率又具备一定的语义抽象程度是精度和算力的平衡点。如果想追求极限精度可以在 P3 层也插入但推理延迟会增加约 12%看场景取舍。3.2 代码实现与集成SDFM 的 PyTorch 实现大致如下这里给出的是可以直接抄进项目里的版本import torch import torch.nn as nn class ChannelAttention(nn.Module): def __init__(self, channels, reduction16): super().__init__() self.fc1 nn.Conv2d(channels, channels // reduction, kernel_size1) self.relu nn.ReLU(inplaceTrue) self.fc2 nn.Conv2d(channels // reduction, channels, kernel_size1) self.sigmoid nn.Sigmoid() def forward(self, x): b, c, _, _ x.size() y x.mean(dim[2, 3], keepdimTrue) y self.relu(self.fc1(y)) y self.sigmoid(self.fc2(y)) return x * y class SpatialAttention(nn.Module): def __init__(self, in_channels): super().__init__() self.conv nn.Conv2d(in_channels, 1, kernel_size3, padding1) self.sigmoid nn.Sigmoid() def forward(self, x): mask self.sigmoid(self.conv(x)) return x * mask class SDFM(nn.Module): def __init__(self, low_channels, high_channels, out_channels, reduction16): super().__init__() self.conv_low nn.Conv2d(low_channels, out_channels, kernel_size1) self.conv_high nn.Conv2d(high_channels, out_channels, kernel_size1) self.channel_attn ChannelAttention(out_channels, reduction) self.spatial_attn SpatialAttention(out_channels * 2) self.out_conv nn.Conv2d(out_channels, out_channels, kernel_size3, padding1) self.relu nn.ReLU(inplaceTrue) def forward(self, low_feat, high_feat): low self.conv_low(low_feat) high self.conv_high(high_feat) high_up nn.functional.interpolate(high, sizelow.shape[-2:], modebilinear, align_cornersFalse) # 通道注意力筛选浅层特征 low self.channel_attn(low) # 拼接浅层已加权和高层特征生成空间注意力 concat_feat torch.cat([low, high_up], dim1) spatial_weighted self.spatial_attn(concat_feat) # 融合空间加权后的浅层特征 高层特征 fused low high_up # 只对加权后的融合特征做一次 3x3 卷积平滑s 注意这里 low 已经被 spatial_attn 内部乘过 mask 了 # 但 spatial_attn 的输入是 concat输出只乘在 concat 上需要拆开处理 # 这里修正一下写法直接用 mask 乘回 low mask self.sigmoid(self.spatial_attn.conv(concat_feat)) low_weighted low * mask fused low_weighted high_up out self.relu(self.out_conv(fused)) return out需要注意上面代码里我对空间注意力的实现做了一点调整先计算 mask再乘回 low 特征而不是直接把 concat 结果乘上 mask。这个细节很关键如果直接对 concat 做加权通道数翻倍之后乘回 low 会带来通道维度不匹配的问题一开始我就在这里踩了坑。集成到 YOLO26 时把原本的 concat 之后接 C2f 的结构替换成 SDFM 输出的结果再接 C2f。换句话说SDFM 负责的是“融合生成更干净的特征”C2f 负责“继续提炼融合后的语义”。3.3 推理阶段的计算量评估我简单统计过在输入分辨率 640x640 的前提下插入一个 SDFM 模块参数量增加大约 45 万FLOPs 增加约 0.3G。相比整个 YOLO26 模型动辄几十 G 的 FLOPs这个增量几乎可以忽略。真正影响延迟的是空间注意力里的 3x3 卷积在 P4 层 80x80 分辨率上做一次卷积耗时约 0.15ms两张卡实测下来整体 FPS 从 87 掉到 84 左右属于可以接受的范围。如果你在做更轻量的移动端部署可以把 3x3 卷积换成深度可分离卷积精度会掉约 0.4 个点但 FPS 几乎无损耗。这是我在某个轻量化项目里验证过的折中方案。4. 训练配置与消融实验反馈4.1 数据集与实验设置我用的是某公开人体姿态数据集的一个子集包含约 25000 张训练图片验证集 5000 张关键点类别是 COCO 风格的 17 个关键点。实验配置如下配置项数值输入分辨率640x640Batch Size16优化器SGD初始学习率 0.01学习率调度Cosine预热 3 epoch训练轮数200数据增强Mosaic、旋转、随机缩放、水平翻转EMA开启衰减系数 0.9998为了保证对照公平除了 Neck 融合方式不同之外其他所有配置完全一致每个实验训练同样的轮数取验证集上最优权重做对比。4.2 消融对比结果融合方式AP0.5AP0.75AR原始 concat C2f83.176.489.2原始 add C2f82.675.888.7concat SE83.577.089.5concat CBAM83.877.389.8SDFMP4 层84.678.590.4SDFMP3P4 层84.778.890.5从数据来看SDFM 在 AP0.75 上的提升最明显相比原始 concat 方案提高了 2.1 个点。AP0.75 是衡量关键点定位精度的重要指标阈值越严格对位置越敏感这个指标的提升说明 SDFM 确实改善了空间定位质量而不是靠增加召回率刷上来的。SDFM 和 CBAM 的差距主要在 AP0.75 上差 1.2 个点。这个差异和我在实验中的观察一致CBAM 的空间注意力偏向中尺度区域关键点这种小尺寸目标受益有限而 SDFM 融合了高层语义信息生成的注意力图对关键点区域的聚焦更准确。P3、P4 两层都插入 SDFM 的收益只有 AP0.75 提升 0.3 个点但推理时间增加了约 12%性价比不高。除非你的任务特别看重最严格阈值下的精度否则我建议只插 P4 层。4.3 难点遮挡场景的专项对比我在验证集里单独筛出了含遮挡标注的 800 张图片做专项测试结果差异更明显融合方式遮挡场景 AP0.75原始 concat C2f63.9concat CBAM64.8SDFMP4 层67.2遮挡场景下 SDFM 的优势放大了。原因也直观遮挡物会在浅层特征里制造大量边缘响应没有空间注意力压制时关键点很容易偏移到遮挡边界上。SDFM 的空间注意力图知道“人体内部才是关键点该出现的位置”能主动压低遮挡边缘处的权重。4.4 训练过程中的观察记录训练到第 60 轮左右我对比过加了 SDFM 和没加的模型热力图输出。没加的模型热力图在关键点附近有一个比较宽的响应峰周围散布着小范围的次峰加了 SDFM 的模型主峰更尖锐次峰明显变弱。从信号处理角度看SDFM 相当于把特征空间里的信噪比拉高了关键点位置的响应更集中坐标解码自然更准。还有一个值得记录的现象SDFM 收敛速度比原始融合方式快。大概在 120 轮的时候SDFM 版本的验证集指标已经接近最终水平而原始融合方式要到 150 轮之后才趋于稳定。可能是因为空间注意力给了网络一个明确的“关注哪里”的引导减少了前期无效探索。5. 踩坑记录与稳定复现的实用建议5.1 通道数对齐不及时训练直接崩显存我第一次实现时在 SDFM 内部才想起要统一通道数结果低层特征 128 通道、高层特征 512 通道拼接时直接爆显存。后来改成先分别做 1x1 卷积对齐到一个统一维度再进入注意力分支。推荐把输出通道数设为和该层 Neck 主路径通道数一致这样后续接 C2f 不用额外调整。5.2 空间注意力掩码作用位置搞错我在初版实现里直接把掩码乘到了 concat 后的特征上维度倒是能跑通但训练曲线严重震荡。排查后发现concat 后的特征有低层和高层两部分掩码同时作用于两者相当于把高层语义信息也按空间位置压了一轮破坏了深层的全局上下文。正确做法是掩码只乘在浅层特征上高层特征保持原样参与融合这样深层的语义信息不会因为局部掩码而缺失。5.3 通道注意力里全连接层换成 1x1 卷积更稳按经典的 SE 写法通道注意力用 Linear 层做降维升维。但关键点检测输入特征是四维张量如果先做全局平均池化再进 LinearBatch 维度上的统计信息要额外处理。我后来改成 1x1 卷积 自适应平均池化的组合效果一样但实现更简洁而且参数量更少。在 reduction16 的前提下这个改动对精度没有负面影响。5.4 关于训练策略的踩坑另外还有很多关于训练细节的经验我也顺带说一下。如果发现 SDFM 加上后训练初期损失波动剧烈优先检查空间注意力分支的卷积初始化最好用标准差为 0.01 的小值初始化让 sigmoid 输出一开始接近 0.5相当于给网络一个温和的起点。另外学习率不要一下子拉太高我前期学习率调到 0.02 出现过两次训练发散。数据增强层面Mosaic 在关键点任务里有一点点矛盾Mosaic 把四张图拼在一起浅层特征里的边缘信息变得非常杂乱SDFM 的空间注意力需要更多轮次才能从这种杂乱中学习到稳定引导。如果数据集本身不大建议把 Mosaic 概率从 1.0 降到 0.7让模型有更多机会看到干净的样本实测 AP0.75 能涨 0.4 左右。5.5 与其他模块共存的兼容性我在 SDFM 之后又试过叠加其他提点模块比如在 Backbone 末端加全局上下文建模。结果显示 SDFM 和全局上下文模块的收益有部分重叠两者叠加的 AP 提升比单独加 SDFM 多不了多少。说明 SDFM 已经做了不少“上下文指导细节”的工作想在它基础上继续提点不如转头去优化数据增强或者损失函数。个人建议的稳定组合是SDFM 只加在 P4 融合处配合 OKS 损失权重调高关键点配置里对头部、手部这类高信息量关键点的权重调高 10% 左右整体效果比盲目堆模块要明显得多。6. SDFM 还能往哪些方向延伸这项改进的可移植性很好不只可以用在 YOLO26 上。我在另一个人脸关键点检测模型里也试过把 SDFM 插进特征金字塔输入变成人脸边界框内的裁剪区域后空间注意力的效果更突出因为背景被裁剪掉之后噪声主要来自五官边缘的互相干扰SDFM 学会关注眼睛、鼻尖、嘴角这些精细位置。如果你想做轻量化改进可以把通道注意力的全连接层替换为分组卷积空间注意力幸存的 3x3 卷积替换为深度可分离卷积。我在一个移动端关键点项目里这么改过精度相比标准 SDFM 只掉了约 0.5 个点但推理时间缩短了 60%。其实 SDFM 的核心收益来自注意力机制的“筛选—引导—融合”流程具体实现时牺牲多少精度换取速度完全看部署环境。另外提醒一句SDFM 在训练初期会有一个比较明显的“注意力突变”阶段大概在 20 到 30 轮左右空间注意力图会从散乱分布收敛到聚焦状态。这时候损失曲线会有一个台阶式下降不用担心这是注意力机制正在“学习看哪里”的正常过程不用调整学习率。我自己的经验是注意力类模块最怕的就是“什么都要加一点”但 SDFM 这种定位精准的跨层融合模块确实值得在关键点检测任务里作为常规改进手段保留。毕竟在推理速度几乎不受影响的前提下能在 AP0.75 上稳定提升 2 个点左右已经是很划算的买卖了。
阅读完成 · 觉得有帮助?
咨询建站