
配置命名带“MQA”的核心目的就是让后续代码、权重、日志都能一眼识别。YAML中我用的模块名是C2fMQA这个名称需要在后面改代码时保持一致大小写敏感。# YOLO26-MQA 颈部集成配置片段基于常见YOLO仓库的模块注册与YAML语法 backbone: [ [-1, 1, Conv, [64, 3, 2]], [-1, 1, Conv, [128, 3, 2]], [-1, 3, C2f, [128, True]], [-1, 1, Conv, [256, 3, 2]], [-1, 6, C2f, [256, True]], [-1, 1, Conv, [512, 3, 2]], [-1, 6, C2fMQA, [512, True]], # P4/16 引入 MQA [-1, 1, Conv, [1024, 3, 2]], [-1, 3, C2fMQA, [1024, True]], # P5/32 引入 MQA ] head: [ [-1, 1, Conv, [512, 1, 1]], [-1, 1, nn.Upsample, [None, 2, nearest]], [[-1, 6], 1, Concat, [1]], [-1, 3, C2f, [512, False]], [-1, 1, Conv, [256, 1, 1]], [-1, 1, nn.Upsample, [None, 2, nearest]], [[-1, 4], 1, Concat, [1]], [-1, 3, C2f, [256, False]], [-1, 1, Conv, [256, 3, 2]], [[-1, 13], 1, Concat, [1]], [-1, 3, C2fMQA, [256, False]], # 自顶向下路径的融合层 [-1, 1, Conv, [512, 3, 2]], [[-1, 10], 1, Concat, [1]], [-1, 3, C2f, [512, False]], ]2.3 模块注册与代码接入的完整流程看到这里真正动手改的时候很多人会卡在“代码写好了怎么让YOLO认它”这一步。我用的是最标准的做法在模块解析文件里注册。以常见的YOLO工程结构为例需要修改的地方集中在ultralytics/nn/modules下的__init__.py、block.py或者conv.py外加一个tasks.py里的模块映射表。具体分四步把上面实现的MobileMQA类和C2fMQA类整理成一个独立文件比如mqa_module.py放进modules目录。在modules/__init__.py中显式导入并加入__all__列表from .mqa_module import C2fMQA, MobileMQA。在tasks.py的模型解析函数中找到类似parse_model的字典映射添加C2fMQA的解析分支。这里要注意C2fMQA正常解析时MobileMQA会在该分支内部被调用所以YAML里不会出现MobileMQA这个层级。如果仓库的block.py里已经存在C2f类你完全可以参考它来写C2fMQA只替换瓶颈内部的Bottleneck为MobileMQA即可。这里有个非常容易踩的坑MobileMQA继承的父类选择。我去翻了很多已有的开源实现发现部分人直接把整个模块定义成nn.Module的普通子类这没问题但在YOLO的模型解析流程里如果涉及BaseModel的fuse()操作比如推理阶段BatchNorm折叠模块内部没有标准的forward格式会直接导致torch.fx解析失败。所以我的建议是MobileMQA内部所有可训练参数的子模块严格使用nn.Conv2d和nn.BatchNorm2d的组合别为了省事把BN去掉否则后期做部署转换时会非常痛苦。注册完成后再跑一句from ultralytics import YOLO; model YOLO(yolo26-mqa.yaml)这种加载命令只要能打印出模型结构说明接入成功。我习惯用四行最简验证代码来测单次前向确认没报错再进训练环节。3. 实际训练中的配置与收益验证3.1 跨阶段公平对比流程模块验证方法论自己和自己的模型比很容易得出“提升了1个点”这种乐观结论。但老手都懂注意力模块的收益验证最怕的就是基线不公平。我在实际验证Mobile MQA时专门做了一套跨阶段的对比流程这里分享给你参考。第一步先跑通原版YOLO26在同一数据集上的baseline完整训练一轮记录mAP50、mAP50-95、BFLOPs、参数量、单张推理耗时。然后在这个baseline的检查点上只加载原版权重再以YOLO26-MQA模型结构重新训练同样epoch数。注意这里有个细节加载预训练权重时如果脖子或检测头的通道数变了strictFalse模式下会跳过大块权重导致早期loss波动剧烈。我在移植时会刻意让MQA所在的最后一阶段之外的所有层保持原通道不变这样就能加载大部分骨干权重收敛速度快很多。第二步如果条件允许把数据集按难度分层。比如目标尺寸均值、亮度、遮挡程度分别统计然后从每层抽相同数量样本构成验证集。这样能看出Mobile MQA究竟在哪些目标上带来增益而不是只盯着一个总mAP。我在某次对比中发现MQA对大目标的反而是略微下降的主要增益集中在中小目标上。这件事不对比分层的验证集根本看不出来。第三步参考实验记录采用的表格式汇总我习惯把同一次实验中所有变体的数据和训练曲线保存到一个目录里后续对比时直接引用不中途改超参。这种做法的好处是你不必每次重新跑全流程也能在整个项目周期里保持对模型的掌控感。3.2 参数与BFLOPs变化参考下面这组数据是参考我实际测试记录整理的相对变化范围不同数据集、不同硬件下会有波动但趋势可以参考项目原版YOLO26nYOLO26n Mobile MQAP4/P5颈部融合变化趋势参数量基准约减少10%~15%降低BFLOPs基准约减少15%~20%降低单张推理耗时小批量无TensorRT基准约降低8%~15%降低mAP50-95同等训练配置基准约提升0.8%~2.0%提升小目标子集mAP基准约提升1.5%~3.0%提升大目标子集mAP基准约下降0.2%~0.8%略降注意参数量的降低主要来自最后一个C2f块中Mobile MQA的查询共享设计使得该层参数量显著小于同通道数的标准BottleneckBFLOPs的降低则来自低分辨率分支的Fused-MBConv卷积和相对较小的Q投影开销。从趋势上看轻量化的优势确实被完整继承了。不过要强调的是如果你把Mobile MQA加得太多比如每个C2f都换掉BFLOPs确实还能再降但精度收益会明显回落因为特征交互的频次让它和原版形成了互补而不是替代关系。这点我在第4部分会展开。3.3 训练细节与收敛经验实际训练MQA改造后的YOLO26有几个配置和原版不太一样。第一是学习率。原版YOLO26如果设成默认的lr00.01加上MQA后前期loss曲线会比原版偏高但后续掉得更快。我实测中把lr0降到了0.005配合warmup_epochs从3提到5整体收敛更平滑。如果你是初学建议就在默认配置基础上微调别一次改太多。第二是数据增强策略。MQA在训练过程中会对空间细节更敏感如果又开了大尺度Mosaic部分小目标在高分辨率分支里重叠厉害反而容易把注意力学偏。我的做法是保持Mosaic开启但把mosaic概率从默认的1.0降到0.7让正常的单图样本占更多比例。这个改动对MQA类模块的收敛稳定性有明显帮助。第三是训练轮数。我的建议是至少在200个epoch左右观察完整的收益。MQA引入的新参数不多但如果只看100轮可能刚好卡在换阶段的波动期误判效果。实际我在多个数据集上都发现MQA的精度增益在150轮之后才逐渐稳定下来这属于正常现象别中途删模块重跑。再补充一个细节推理阶段记得关掉训练时的辅助头或额外分支具体以你的仓库为准。我调试时曾发现加了MQA后训练正常但验证时超时最后定位到是某个测试分支里多跑了一次MobileMQA的forward。这种低级错误往往比模型本身的问题更耗时间。4. 常见问题与排查技巧实录4.1 训练不收敛、损失震荡或精度反而下降这是注意力改造最常见的翻车现场。我梳理了三个高频原因一是梯度流问题。Mobile MQA内部有多个分支相加和拆通道操作如果某个分支的初始化不合适可能在早期让梯度幅度差异过大。排查方法是在训练前用随机输入跑一次backward打印各层梯度的均值与标准差。如果发现Q/K/V相关卷积的梯度比其他层大出两个数量级就给对应的卷积层换初始化或者先降低学习率再观察。二是位置选择不对。比如把MQA直接加到检测头前的最后一层大特征图上那里通道数只有256但分辨率最高注意力计算会增加不少访存开销。更重要的是检测头本来就要输出边界框如果在这之前强行引导“全局注意力”反而会让位置编码信息被平滑掉。我实测下来最稳妥的位置是P4、P5这两个深层阶段外加深层颈部融合层。三是数据与任务不匹配。Mobile MQA对中等以上尺寸目标的特征交互是有效但如果你检测的全是几十像素内的小目标而且数据集本身就存在严重的类别不均衡MQA对“该关注谁”的作用可能被类别损失主导。这种情况我的建议是别迷信注意力模块回去调数据增强和损失函数的类别权重可能更直接。4.2 显存占用不降反升或推理速度异常理论上Mobile MQA是轻量化模块为什么有些人接入后显存反而高了问题通常出在实现细节上。最常见的外因是你把多尺度分支中的低分辨率特征图又用nn.Upsample还原回原尺寸然后与高分辨率特征做拼接或相加。这样虽然只多了半个分支但整张大特征图都需要驻留在显存里。我的实现里特意让两个分支最后直接相加在低分辨率上完成最后再一次性上采样这样显存占用非常干净。另一个隐性问题如果你在训练阶段没有引入torch.cuda.amp混合精度Mobile MQA内部的多次加法和通道数变化会让FP32的访存开销上升导致训练速度反而不如原版。我在调试时发现开启AMP后MQA部分的速度提升接近两倍所以强烈建议训练时开启AMP。如果推理阶段发现单张耗时没有下降查一下模块有没有被torch.jit或onnx的自动优化解析。某些版本的仓库在导出时会对带有多个分支且分支间有相加操作的模块做“保守展开”导致推理图变复杂。解决办法是在导出ONNX后手动检查节点数如果发现膨胀可以尝试将低分辨率分支里的Fused-MBConv改成不带残差的普通卷积节点数会明显回落。4.3 小目标收益有限什么时候该用别的方式这条算是我踩过坑之后特别想提醒你的。Mobile MQA的整体设计思路偏向于“让模型在较深阶段用较低成本建立全局关系”所以它对中小目标有一定帮助但对那种几十个像素、密集排列的极小目标作用远不如直接增加高分辨率特征图或引入更细粒度的检测层。如果你明确要解决小目标检测我建议不要只加Mobile MQA而是组合优化最浅层继续保持高分辨率特征输出、在颈部增加一层更小的检测头、并使用更贴合小目标的损失函数设计。MQA可以作为深层头部在降低计算量方面的一个补充但别指望一个注意力模块包打天下。另外对于极端移动端部署如果目标设备对算子支持比较老旧Mobile MQA中的分组卷积和低分辨率分支可能没法完全走硬件加速此时反而应该退回到经典SE模块或者用重参数化卷积来换取实际速度。这些都要以目标芯片上的真实测速为准理论BFLOPs只是参考。最后想说的话我在不同数据集上反复试过Mobile MQA的改造最深的体会是注意力模块不是越复杂越好关键是在合适的阶段引入合适的计算结构。Mobile MQA的价值在于它提供了一种几乎不伤害推理速度的精度提升路径并且代码上很干净极易嵌入YOLO26现有工程。如果你想让模型再轻一点可以把低分辨率分支的Fused-MBConv换成单层3x3卷积如果你想让精度再高一点可以把只加在P5层的MQA也复制到P4层但记得用分层验证集看收益。Mobile MQA算是我最近接触到的难得的“加进去不后悔”的轻量化模块希望这篇记录能帮你少走点弯路。