
前一阵子我接手了一个智慧工地的安全帽识别项目设备装在某个在建物流园区的几个塔吊和围挡立杆上。原本模型在近景抓拍场景下表现还行但一旦切到全场俯视的球机画面问题就全出来了距离一远人头只有十几个像素稍有遮挡安全帽就会被误判成普通头部或者把钢构件的弧形阴影认成帽子。甲方每周都在问误报率为什么这么高最后我们干脆做了一轮系统性优化从数据、模型结构、损失函数到推理后处理全部过了一遍把误报率相对下降了41%。这篇文章就把整个优化过程的思路、参数和踩坑点完整复盘一下给正在做类似工地视觉方案的朋友一个参考。1. 远距离与遮挡场景下的误报根源分析1.1 远距离小目标的特征退化在智慧工地里安全帽检测和一般的目标检测任务有一个很明显的差异摄像机的安装位置几乎决定了问题难度。塔吊上的球机要兼顾整个作业面视野覆盖半径经常在30米以上一个成年人的头部在1080P画面里只有约18×18像素极端情况下甚至不足12×12像素。在这种尺度下安全帽的纹理、接缝、帽檐细节全部丢失模型能看到的只有一个模糊的色块。这时候网络很容易把颜色相近的圆形物体当成正样本红色灭火器、蓝色塑料桶、混凝土搅拌罐顶部的弧形反光面都成了误报的来源。一开始我们以为只要把模型换成更强的主干网络就能解决但跑了几个版本后发现效果很有限。原因也不复杂模型参数量上去了特征提取能力确实变强了但在小目标上缺乏足够的监督信号底层卷积很难从十几个像素里学到真正区分类别的判别特征。换句话说对着一张只有色块轮廓的图片不管网络多深它都分不清那是帽子还是水管弯头。1.2 遮挡导致的特征残缺第二个大问题是遮挡。工地现场根本不存在干净的画面脚手架钢管、安全网、围挡立柱、来往的钢筋运输车都会把人员头部遮掉一部分。在近景画面里遮挡可能只是帽檐被钢架挡了一点但远距离叠加遮挡后可见区域可能只剩原本的30%而且剩下的部分刚好是帽子的顶部弧线缺少了帽檐和头部轮廓的相对关系。模型这时会产生两种典型错误一是把可见部分误判为安全帽但实际上那人根本没戴帽子只是头部被阴影遮住了二是明明戴了帽子因为可见特征太少被直接漏检。我后来统计过误报样本的类型分布排在第一位的是反光背心肩部阴影组合误判为黄色安全帽占了接近三成。这类样本的共性问题是在远距离画面里肩部的弧形区域和黄色反光材料叠加后形状和颜色都非常接近安全帽的局部特征。单纯靠增加数据量并不能解决这个问题得从标注策略和模型设计两个方向一起入手这个后面细说。1.3 误报的统计口径与改进目标做优化之前一定要先统一评价口径否则后面所有对比都是空的。团队里日常提到的误报率其实有两种算法一种是按帧算误报帧数除以总检测帧数另一种是按目标算误报目标数除以正确目标数误报目标数。我在这篇文章里统一用后者误报率 FP / (TP FP)。在这个口径下优化前模型在验证集上的误报率大约在5.4%左右也就是每检测出100个戴帽人员其中有约5.4个其实是误报。我们的目标不是追求零误报在工地这种开放场景里不现实而是把误报压到3%出头的水平同时保证漏报不能反弹。41%这个数字就是这么来的优化后误报率降到约3.2%相对下降了约40.7%按四舍五入就是41%。2. 数据侧改造让训练集真正覆盖看不清的场景2.1 采集与筛选远场景样本不能靠裁近景图片替代这是整轮优化里投入最大也最容易被低估的一步。很多团队的常规做法是从近景视频里截帧然后通过缩小图片模拟远距离目标。我们试过之后发现效果很差因为单纯缩放会保留清晰的边缘轮廓而真实远距离画面里的模糊来自镜头光学、空气灰尘、运动拖影等多个因素叠加二者在频谱特征上差异很大。模型在合成小目标上训练得再好到真实远距离画面里照样误报。正确的做法是从工地现场的各个机位同时采集尤其是从塔吊球机和围挡立杆这两个典型视角。我们连续采集了两周的24小时视频数据然后按时间段抽帧白天强光、黄昏逆光、夜间补光各占一部分天气尽量覆盖晴天、阴天和轻度雾霾。筛选时有一个原则保留那些人眼勉强能分辨是否戴帽的样本不要只挑清晰的。这些模糊样本正是模型误报的高发区也是提升鲁棒性的关键。2.2 数据增强策略遮挡模拟与小目标复制数据增强是这轮优化的第二大功臣。我们在原有马赛克增强的基础上新增了两类针对性增强。第一类是遮挡模拟。在训练时随机用若干个不规则四边形色块遮挡目标区域遮挡面积从10%到50%不等。这个操作模拟了脚手架钢管和安全网遮挡强迫模型不再依赖某个固定部位的局部特征而是学会从剩余的可见区域去判断。实测发现遮挡增强从30%提升到50%后重度遮挡样本的召回率提升明显但超过50%后开始出现误报上升因为模型被迫在极少量信息上进行猜测。第二类是小目标复制。把训练图中标注好的安全帽实例按原尺寸复制到其他图片的随机位置每个目标可以重复粘贴多次。这个策略的思路是远距离画面里同类目标往往成批出现比如一群工人同时进入镜头通过复制既增加了小目标的数量又制造了密集目标的场景。复制时需要注意同步复制对应的文字标签并且在粘贴位置生成一个随机的亮度扰动否则模型会学到新增目标周围有一条明显的粘贴边界反而导致误报。2.3 标注规范模糊样本不要删边界框贴可见部分标注规范这块我们吃了不少亏。最开始标注团队拿到模糊样本第一反应就是删掉或者忽略理由是看不清。后来我专门拉了一次标注培训定了一条硬性规则只要人眼能判断出头部位置不管多模糊都要标如果目标被遮挡边界框只框可见部分不要凭想象补全完整人头。这条规则对后续的模型优化帮助很大。因为模型在训练时看到的监督信号全部来自标注框如果模糊样本全部被删模型就永远不会主动在模糊区域寻找目标如果遮挡样本的边界框把不可见部分也框进去模型就会被错误的定位信息带偏导致预测框漂移。标注耗时确实增加了平均一帧需要多花3到5秒但换来的是验证集上远距离准确率大约4个百分点的提升这笔账值得算。2.4 类别平衡与难例挖掘工地的安全帽颜色很杂常见的有黄、白、红、蓝四种另外还有少量橙色的。只统计数量的话黄色帽子的样本可能是蓝色帽子的五倍。这种不平衡的直接后果是模型对蓝色帽子的召回率偏低而且蓝色帽子被误判成普通头部的概率明显更高。我们没有简单地对少数类做过采样因为那会导致模型过拟合到特定颜色值上。更稳妥的办法是给少数类别的损失加上一个权重这个在第四章会具体讲。难例挖掘我用的是比较原始但有效的方式用当前模型跑一遍训练集把预测置信度在0.3到0.6之间的样本全部抽出来人工检查一遍。凡是标错的、漏标的、框不准的统一修正以后混回训练集。这一轮我意外发现了一个规律相当一部分安全帽误报其实来自原始标注中把站着没戴帽子的工人错标成了戴帽模型只不过是把错误的标注学到了。修正这些标签后误报率直接下降了一截。3. 模型结构微调YOLO26检测头与特征层的专项改造3.1 在P2层增加小目标专用检测头YOLO26这个叫法是我们项目组内部的代号指的是在开源YOLO系列基础上二次开发的版本。它默认的检测头是从P3、P4、P5三个尺度出发的对应的是8倍、16倍、32倍下采样特征图。在近景场景里这套配置够用但放到30米外的球机画面上16倍下采样特征图上的一个目标只剩下约1到2个特征点信息量严重不足。我们的做法是新增一个P2层检测头对应4倍下采样特征图。这一步背后有一个很实在的代价考量增加P2头会显著提高计算量尤其在输入分辨率较高的时候。为了平衡我们把主干网络在浅层的通道数做了裁剪同时把P2头的检测框只负责小目标分支。具体来说在训练时给每个样本计算目标框的宽高小于32像素的框只分配给P2头大于128像素的只分配给P5头中间范围的由P3和P4处理。这种按尺寸分工的设计比在所有检测头上同时监督所有目标效果更稳定也更容易收敛。3.2 轻量注意力机制让网络聚焦帽檐与顶部弧线单纯增加检测头还不够小目标特征图本身携带的语义信息太少需要让网络学会主动关注有区分度的局部区域。我们尝试了几种注意力模块最终保留了类似EMA风格的轻量注意力结构只插入到主干网络的第3到第5个阶段。它的思路可以概括为把特征图在空间上分成几个小组每组内部先做一次局部注意力计算跨组之间再做一次信息交换最后合并输出。为什么不用更常见的通道注意力我们对比过在近景大目标上通道注意力的提升很稳定但在远距离小目标上效果平平。后来分析了一下原因小目标在经过多层下采样后通道维度的信息交错非常严重单靠通道权重很难把帽檐弧线这种高频空间特征分离出来。而空间注意力可以直接告诉网络关注这个像素邻域对只有十几个像素的目标更有效。EMA结构在增加约6%计算量的前提下把远距离小目标的AP提升了约2.1个百分点。3.3 解耦检测头与遮挡目标的定位策略遮挡问题主要体现在定位维度尤其是严重遮挡时预测框经常偏移半个身位。我们在检测头里把分类和回归完全解耦并且给回归分支单独加了一个IoU预测分支这个分支输出的数值作为回归质量的置信度在后续NMS和后处理中会用到。解耦的思路本身不复杂但针对遮挡场景有个小改动值得记录在为回归分支分配训练样本时我们对被遮挡目标采用了可见区域优先的策略。也就是说如果一个目标的可见面积小于完整面积的40%就只让回归分支在可见部分上计算损失分类分支则继续使用完整类别标签。这样做可以避免模型被迫去推测被遮挡部分的精确坐标——在极度遮挡时推测坐标本身就是不可靠的强行拟合反而会带来训练震荡。4. 损失函数与训练策略从框得准到少误报的关键手法4.1 使用WIoU v3替代普通IoU损失损失函数的选择在整个优化链路里看似不起眼实际上对误报率的影响非常直接。一开始我们用的标准CIoU损失在近景数据集上表现正常但换到多尺度训练后小目标样本的梯度贡献明显偏小——CIoU的惩罚项在目标框只有十几个像素时几乎不起作用模型学不到精确定位的信息。我们最终换成了WIoU v3它的核心思想是动态调整样本权重在训练前期重点优化普通样本让网络先收敛到一个合理的状态到了训练后期自动把注意力转向难样本。这个后期转向难样本的机制对误报问题格外有效因为误报样本本身就是难样本它们往往是那些和真实目标高度相似的背景区域。通过增大难样本的梯度权重模型被迫去学会区分这些容易混淆的模式而不是单纯记住圆形且红色就是安全帽这种粗暴规则。配置上需要注意一点WIoU v3有几个超参数需要调我们用的是论文推荐参数的默认值基础上略微改动但发现不同数据集上最优参数差异很大。建议在换数据集时不要直接沿用而是用验证集做一轮小的网格搜索。4.2 置信度损失加权与类别损失聚焦误报率的另一个重要控制点在置信度损失。标准YOLO系损失里前景和背景的平衡主要靠anchor分配来解决但在远距离小目标场景背景样本占据压倒性多数置信度损失容易被带偏。我们的方案是给负样本的置信度损失加了一个动态权重当前模型的误报率越高这个权重越大。实现上就是每个epoch记录验证集上的FP数量然后按比例放大负样本损失系数。类别损失这块针对蓝色和橙色安全帽样本不足的问题我们直接调整了交叉熵损失中的类别权重。具体权重表如下类别训练样本占比损失权重黄色安全帽45%0.8白色安全帽22%1.0红色安全帽18%1.0蓝色安全帽10%1.5橙色安全帽5%1.8这里权重不是严格按照样本占比的倒数来设的而是留了余量。如果完全按倒数设置少数类会学得过猛出现把所有弧形色块都识别成橙色帽子的新误报。这个度需要反复试我们的经验是权重上限不要超过2.0。4.3 训练策略组合冻结主干、多尺度与EMA训练策略上我们排除了不少弯路。最初是直接从预训练权重整体微调后来发现主干网络在远距离小目标上很快就会过拟合到工地场景的特定颜色分布泛化反而变差。我们改成了分段训练前20个epoch冻结主干网络只训练检测头和新增的P2分支让模型先学会在已有特征上做正确的检测决策从第21个epoch开始解冻主干整个网络用较低的学习率继续训练20个epoch。多尺度训练对这类任务几乎是必须的。我们在每个epoch里以0.5的概率随机把输入分辨率从640调整到832再以0.3的概率调整到480其余时间保持640。这样做让模型同时适应近距离大目标和远距离小目标两种分布最终效果比单独用固定分辨率好了不少。EMA模型参数指数移动平均也开了衰减系数0.99能稳定训练后期由于难样本权重增大带来的震荡。4.4 负样本池与难例回灌机制训练过程中还有个容易被忽略的机制我单独拎出来说一下负样本池。我们把所有模型高置信度预测但实际是背景的图片区域保存下来每600个epoch版本收集一批然后在下一轮训练时作为难例样本混入训练集。这个机制刚开始有一个隐患混入的难例大多来自当前模型的盲区如果过度强化会让模型变得过度保守误报率是降了但召回率也掉了。我们的做法是把难例样本占比控制在总batch的10%以内并且只回灌一次下一轮模型更新后重新收集新的难例形成循环而不是堆积。5. 推理侧优化置信度阈值、NMS与时序策略的联动5.1 置信度阈值不是越高越好要配合误报率曲线选很多工程师习惯把置信度阈值设成0.5然后就不动了。实际上在远距离场景这个选择很可能是错的。我们在验证集上绘制了置信度阈值从0.3到0.7范围内的误报率和召回率曲线发现随着阈值提高误报率下降的速率远低于召回率下降的速率。换句话说在这个数据集上把阈值从0.5提高到0.6误报率只下降了0.3个百分点但召回率掉了1.8个百分点完全不划算。最终我们选择的阈值是0.42略微低于默认值。原因在于优化后的模型在分类置信度的分布上比原来更尖锐真实目标的预测置信度集中在0.6到0.9之间而误报样本的置信度大多在0.3到0.5之间。把阈值定在0.42可以拦住大部分误报同时不伤害真实目标。这个判断不能拍脑袋定一定要基于自己数据集的置信度分布来选。5.2 用DIoU-NMS替换普通NMS缓解遮挡重叠问题NMS在遮挡场景里是双刃剑。普通的IoU-NMS在两个人并排站着都戴了帽子但互相遮挡时很容易把其中一个框直接抑制掉因为两个框的重叠度太高。但放低NMS阈值又会放出大量互相重叠的误报框。我们采用的是DIoU-NMS它除了看两个框的IoU还会考虑两个框中心点的距离。两个框虽然重叠但如果中心点相距足够远通常代表两个不同目标不应该被抑制。在实际测试中这个改动让近距离并排行走的工人安全帽召回率约提升了5%同时误报率没有恶化。实现上只需要在NMS的得分计算里把惩罚项从纯IoU改成IoU减去中心距离归一化项几乎不增加推理耗时。5.3 时序过滤利用视频帧间一致性过滤抖动的误报工地监控是连续视频流单纯对单帧做检测会丢掉一个非常有价值的信息目标在时间维度的连续性。误报的一个典型特征是它在帧间闪烁比如某个反光点在这一帧看着像帽子下一帧光线变了就消失。真实的目标则应该连续出现几帧并保持稳定的位置和尺寸。我们的实现是在检测之后加了一个轻量级的时序跟踪模块对每个检测框建立短时的轨迹ID。规则不复杂一个目标要在连续3帧中至少2帧被检测到且位置偏移不超过前一帧框宽高的1.5倍才会被确认输出。反之一个检测框如果只出现1帧就消失直接标记为疑似误报并丢弃。这个策略把误报率又压低了约10%代价是检测输出有约1帧的延迟在实时监控场景里完全可接受。5.4 部署侧的实际速度代价这部分属于工程落地的老生常谈但还是要强调一下。所有的优化都不能脱离部署环境来谈我们最终是TensorRT FP16推理输入分辨率固定为1280在单张消费级显卡上跑到了约45 FPS。新增的P2检测头让计算量增加了约15%时序过滤模块因为是纯CPU操作对GPU推理几乎没有影响。如果你的项目部署在边缘设备上需要慎重考虑P2头带来的算力开销可以考虑只在距离较近的机位启用P2分支远距离机位则保持原结构。6. 结果复盘41%的误报率下降是怎么算出来的6.1 验证集设计与分场景评估最后说说评估方式。如果只看总体误报率很容易被平均数字蒙蔽我们按两个维度做了切分距离段近景小于10米、中景10到25米、远景25米以上和遮挡程度无遮挡、轻度遮挡、重度遮挡。验证集里的远景和重度遮挡样本是特意从训练周期之外的时间段抽出来的确保模型没在训练时见过这些画面。评估结果要分开看才有意义。总体误报率从5.4%降到3.2%只是一个维度真正让我满意的是远景误报率降低了约一半。这印证了这轮优化的方向是对的数据增强和P2头解决的正是这类样本的检测能力而时序过滤和阈值调整则把边缘误报压制住了。6.2 误报来源构成的变化我们重新统计了优化后的误报样本发现构成结构发生了明显变化。之前占比最高的反光背心肩部阴影误判为帽子从约30%降到了约12%说明模型确实学会了利用帽檐和头部轮廓的相对空间关系来做判断而不是只看颜色和形状。但新的误报来源也浮现出来了比如夜间补光下安全帽的反光点被拉长后形成的条状高光会偶发地被识别成帽檐。这个结果其实说明了一件事误报不可能被完全消灭只能不断转移和压缩。每轮优化解决一批误报总会暴露出下一批更难的误报。我们的应对是保留了误报样本的实时收集通道并且每两周做一次错题本复盘把新出现的误报类型回灌到训练集里。6.3 仍然存在的漏报边界与后续方向即使误报率下降了41%当前的模型在三个场景下仍然不稳定第一是强逆光且戴深色安全帽的情况目标几乎和背景融为一体第二是夜间灯光直射镜头导致的大面积过曝区域目标信息完全丢失第三是雨雪天气里帽檐积水后形成的高光反射模型会倾向于不报而非错报。后续我打算尝试两个方向一个是在输入端加入针对恶劣天气的图像增强预处理另一个是在模型训练阶段引入部分红外或热成像数据做跨域学习。这两个方向的周期都比较长不是一两周能见到效果的但至少明确了下一步的发力点。最后分享一个回头看起来特别值得的小技巧在整个优化过程中我习惯把每次调整后的模型在同一个固定评测脚本上跑一遍把误报率、召回率、各类别AP存成一张趋势表。很多调参操作单看一次实验很难判断好坏但放到连续的趋势图里哪些改动真正有效、哪些是随机波动会清晰很多。这也是我们最后能准确说出误报率下降41%而不是感觉降低了的根本原因。