起因

之前搞无人机烟火巡检,用 D-Fire 公开数据集微调了个 YOLO11n,测试集上成绩单相当体面:smoke 类 mAP50 = 0.838,80 多分的优秀卷面。

今天拿了 5 段无人机实拍视频想验证一下效果,然后就被现实按在地上摩擦:逐帧看,只有 56% 的帧里烟雾被检出来,而且框忽隐忽现——这一帧有、下一帧没、再下一帧又有,跟信号不好的电视一样。

先做排除法:不是阈值和分辨率的锅

遇到「效果不好」,第一步永远是量化,别急着猜。我写了段小脚本,在烟最多的那段视频上扫了一遍置信度阈值和推理分辨率:

配置 帧检出率 平均置信度
conf=0.25, imgsz=640(当前) 56% 0.52
conf=0.1, imgsz=640 69% 0.39
conf=0.25, imgsz=1280 29% 0.47

两个反直觉的结论:

  1. 阈值从 0.25 降到 0.1 能救回一些帧,但天花板就在 70% 附近;
  2. 把分辨率提高一倍反而暴跌。如果模型是「看不清小目标」,提高分辨率应该更好——结果是更差,说明它压根没学会这类烟长什么样,分辨率高了只会让它更困惑。

再回头看 D-Fire 测试集:0.838 的 mAP50 明明白白。数据集上 84 分、实拍 56 分——教科书级的域差(domain gap)。

打开视频一看,我懂了

我点开抽出来的帧,哭笑不得:视频里全是无人机在草地上空拍的粉红色烟饼烟雾。而 D-Fire 数据集里 smoke 类长什么样?山火、秸秆焚烧、灰色烟柱。模型没见过粉色的、贴地铺开的、从绿草地里冒出来的烟——它不是检测能力不行,是压根没见过这个物种。

顺便说,这也解释了为什么两个视频「什么都没检测到」——那两段里基本只有烟没有明火,而粉烟恰好是模型最眼生的形态。

想微调?先踩过「空标签陷阱」

解决方案很直接:把实拍帧混进训练集做领域微调。流程听起来简单——抽帧、用现有模型伪标注(pseudo-labeling)、合并训练。我抽了 710 帧,模型高置信度检出 634 个框。

但这里有个非常隐蔽的坑,值得单独一节。

伪标注时,模型没检出的帧会被写成空标签——在 YOLO 里,空 label 文件的意思是「这张图里什么都没有」。可是模型没检出 ≠ 真的没有!恰恰相反,这些帧是模型最该学而没学会的样本。把它们当背景喂回去,等于亲手教模型「这种烟不算烟」——用模型自己的偏见再强化一遍模型自己,越训越歪。

我抽查了 12 张「空标签」帧:8 张里明明白白有烟,远处粉色的、淡淡的一层,模型连 0.05 的置信度都给不出来。这要是不处理直接训,数据集越大,模型越瞎。

用颜色把漏掉的烟捞回来

怎么把「模型漏了但真有烟」的帧和「真背景」的帧分开?人工复查 710 张?可以但没必要——烟饼的烟有个天然优势:它是粉色的,和绿色草地、灰色天空的颜色分布几乎不重叠。

于是在伪标注流水线里加了个传统 CV 兜底:HSV 空间做个粉色掩码(色相 0-8°/172-180°,饱和度和明度卡个区间),形态学闭运算去噪后取连通域外接框:

  • 模型高置信检出 → 用模型标注
  • 模型没把握、但粉色像素显著 → 用掩码生成标注框(这些恰恰是最珍贵的难样本)
  • 两者都没有 → 才配当负样本

结果:救回 295 帧,其中一段视频 87% 的帧都是模型完全检不出、靠掩码捞回来的。再抽查一遍标注质量,框基本都老老实实套在烟上。

换更大的模型重训

nano 模型 2.58M 参数,这次换成 yolo26s(10M 参数,4 倍容量,NMS-free 的端到端新架构),训练集 = D-Fire 1.7 万张 + 实拍 710 帧混合。写这篇的时候训练还在 GPU 上跑,前 8 轮 mAP50 从 0.48 爬到 0.61,曲线健康。效果如何,下篇见分晓。

不重训也能先治「框闪烁」

框忽隐忽现还有一半原因很朴素:逐帧独立检测,弥散烟雾的置信度天然在阈值附近抖动。这在部署端就能治——加个滞后锁定(hysteresis),思路和施密特触发器一模一样:

  • 新目标要 conf ≥ 0.2 才激活(防止噪声乱拉框)
  • 已激活的目标只要 conf ≥ 0.05 就维持(IoU 匹配延续)
  • 连续 10 帧找不到才移除,期间沿用上次的框

改完同一帧序列,烟框从「蹦迪」变成「焊在那」,报警逻辑也不用再去做抖动滤波了。便宜的改进先落袋,模型的事交给训练。

小结

这次翻车给我留了三条经验:

  1. 测试集分数是「见过面的题」的分数。域一换,84 分可以瞬间变 56 分,而且降阈值、提分辨率这类「调参手段」救不了「没见过」。
  2. 伪标注最大的坑不在标错的框,而在没标的框。空标签是有语义的,喂之前先想清楚每一张「空白」是真理还是模型的遮羞布。
  3. 老办法(HSV 颜色掩码)在新流程里照样能打。深度学习并没有让传统 CV 失业——颜色先验这种东西,恰恰是纯数据驱动的模型最缺的那口气。(这条是趁训练没跑完写的,训练完了我就知道该不该删。)