做工业视觉项目这么多年,我有一个越来越强烈的感受:模型在评测集上涨点再高,落不了地就是废纸。最近正好把一个基于YOLOv11的工业级实时目标检测系统从实验台搬到了产线上,过程中把架构优化和多任务集成的坑基本踩了个遍。这篇文章想把这套方案的完整思路、关键参数和实战记录整理出来,尤其是关于主干网络的改动、推理加速、多个任务头如何共用一套特征的工程细节,希望能给正在做类似检测项目的同行一些参考。
这套系统并不单单是“检测出物体”那么简单。工业现场的真实需求往往是复合的:既要框出缺陷区域,又要对缺陷做像素级分割,还要区分缺陷类型,同时统计数量、判断等级。如果每一个需求都单独训练一个模型,产线上那点算力根本扛不住。所以整个方案从一开始就定了一个基调:基于YOLOv11做架构优化,把检测、分割、分类等任务集成到一个模型里,在保证实时性的前提下尽可能提高各项指标。
1. 项目背景与整体设计思路
1.1 为什么选YOLOv11而不是其他框架
动手之前,团队也评估过其他几类方案。两阶段检测器精度确实高,但速度在工业场景里往往顶不住;基于Transformer的检测模型在大型数据集上表现惊艳,可部署到边缘设备时,显存占用和延迟都是问题。反观YOLOv11,它在设计之初就把“实时性”放在了和精度同等重要的位置,而且代码工程化程度很高。
我选择它的核心原因有三个。第一,YOLOv11延续了anchor-free的设计思路,对密集小目标比较友好,省去了大量anchor超参数调试。第二,它的解耦头把分类和回归分支分开,多任务扩展时不需要推倒重来,可以直接在原有Head旁边加分支。第三,社区生态比较成熟,从训练到导出ONNX再到TensorRT部署的链路都有人趟过,遇到问题能快速找到参考。
当然,YOLOv11也不是万能药。它的官方预训练权重主要面向自然图像,工业现场那种光照变化剧烈、纹理单一、目标形态固定的场景,直接拿来用效果并不理想。所以严格来说,我们并不是“用了YOLOv11”,而是“以YOLOv11为底座,做了一套深度改造”。
1.2 工业级系统与学术Demo的差别
学术Demo追求的是SOTA精度,工业系统追求的是可复现、可维护、可长期稳定运行。这两者的思考方式完全不同。做学术时,只要在一两个测试视频上跑出漂亮结果就行;做工业项目时,你得考虑产线的节拍,比如每秒处理多少帧,考虑运动模糊、曝光异常、灰尘遮挡,甚至要考虑设备连续运行一个月后显存会不会泄漏。
我记得早期在Demo阶段,模型检测效果很好,准确率到了98%。结果上了产线,第一天就频出问题:金属表面的反光导致大量误检,暗光环境下的召回率掉得厉害。后来专门针对光照鲁棒性做了数据增强,又调整了图像预处理,才算稳下来。
所以这篇文章里提到的架构优化,不是为了刷分,而是为了让系统在真实工业环境里扛得住。整条数据管线、训练策略、推理优化都要围绕“稳定”这个关键词展开。
1.3 多任务集成的目标拆解
项目需求方最初只要求做缺陷检测,把有问题的区域框出来就行。但沟通了几轮后发现,现场质检员真正需要的是:
- 缺陷定位:在图像里找到缺陷区域,给一个边界框
- 缺陷分割:把缺陷的精确轮廓分割出来,方便后续测量面积和形状
- 缺陷分类:判断这个缺陷是划伤、凹坑还是脏污
- 缺陷分级:根据严重程度分成轻、中、重三个等级
如果每个需求都单独训练模型,推理时串行跑多个模型,产线节拍完全跟不上。而且多个模型的误差会累积,前面漏检了后面再准也没用。最佳做法就是把检测、分割、分类整合在一个模型里,共享底层的特征提取网络,只在最后分出不同的任务头。
多任务集成最大的难点不是网络结构,而是不同任务之间往往存在竞争关系。比如检测头关注的是前景和背景的分类,分割头更在意像素级别的边界,分类头则要求特征具有更强的类别判别性。这些目标在反向传播时会互相拉扯,搞不好就会顾此失彼。后面会详细说明我们是怎么通过损失函数和训练策略来平衡这些矛盾的。
2. 架构优化的核心细节
2.1 主干网络与Neck的调优策略
官方YOLOv11的默认结构在工业场景下最大的短板是小目标特征不足。产线上的缺陷往往非常小,比如一块电池表面的针孔,在1280分辨率下也只占几十个像素。默认的Backbone和Neck对这类目标的响应很弱。
我们做的第一项改动是调整Neck结构。标准FPN是从高分辨率到低分辨率逐层融合,但小目标更需要浅层的高分辨率特征图。团队在原有PAN结构的基础上增加了一个轻量级的P2输出层,把更浅、分辨率更高的特征引入到检测头中。代价是计算量增加了一些,但对小目标的召回率提升非常明显。这个改动说起来简单,实际做的时候要注意:P2层的感受野太小,直接加进来容易引入大量背景噪声,所以我们在P2层后接了一个小的注意力模块,让网络学会自动筛选重要特征。
第二项改动是给Backbone的最后几层加入轻量注意力。这里没有选择所有层都加,因为那样会显著拖慢推理速度。我们只在C3和C4输出的位置添加了通道注意力,用降维后的通道描述符对特征做重标定。这个操作比较克制,属于“花小钱办大事”的类型。
第三项改动是重新设计检测头的感受野。YOLOv11默认的解耦头对不同尺度的目标使用相同的卷积配置,但工业场景中目标尺寸往往比较极端。我们在回归分支里加入了一个可学习的空洞卷积残差结构,让分支能够动态调整感受野。这一项改动在实验里带来的mAP提升不算大,但对长条形缺陷的边界框回归帮助很大。
2.2 推理加速与量化部署
工业实时系统的硬指标是端到端延迟。我们现场的工控机是一块消费级显卡,要求单帧延迟稳定在20毫秒以内。如果直接跑FP32模型,延迟怎么也压不到目标值。所以架构优化之外,又做了一层部署优化。
第一步是把PyTorch模型导出到ONNX,然后在TensorRT上做网络层融合。TensorRT会把卷积、BN、激活函数合并成一个层,减少内核启动的开销。这一步做完,延迟能下降40%左右。
第二步是切换到FP16推理。FP16在精度损失可以接受的前提下,吞吐量能翻倍。但工业现场有些老显卡对FP16的支持不好,容易出现精度骤降,所以必须在目标设备上重新校准验证。
第三步是尝试INT8量化。我们用的是PTQ方案,选了一批有代表性的真实产线图片做校准。校准集大概3000张,覆盖了各种光照、角度和缺陷类型。量化后模型体积压缩到原来的四分之一,延迟又降了一截。但INT8在小目标检测上确实有精度回退,后来通过敏感层回退的方式,只把检测头部分保持FP16,其余层用INT8,效果比较理想。
这里要特别提醒一下:量化校准不能只用干净的标准图片,必须用现场采集的原始图像。标准图库里的图像分布和产线实时画面的分布差别很大,用错校准集轻则掉点,重则出现严重的系统性偏差。
2.3 数据管线与增强策略
算法再好,数据不行全是白搭。工业缺陷检测经常面临正样本稀缺的问题,一条产线跑一个月,良品照片上万张,带缺陷的照片可能只有几百张。一开始我们试图直接训练,结果模型严重偏向背景类,几乎所有目标都漏掉了。
为了解决这个问题,我们把数据增强提到了很高的优先级。首先做的是传统几何增强:随机翻转、旋转、缩放、平移,这些操作能大幅增加样本多样性。然后是颜色增强:调整亮度、对比度、色相、饱和度,模拟不同批次产品的色差和现场光源波动。这里有一个小技巧,工业图像往往有固定的纹理方向,比如拉丝金属表面的划痕总是沿一个方向,如果随机旋转90度,反而会让模型学到错误的知识。所以我在这类增强上做了约束,只允许小角度旋转和镜像翻转。
Mosaic增强在训练初期效果很好,可以把四张图拼在一起,让模型在有限的数据量下看到更丰富的小目标分布。但Mosaic会导致部分目标被截断,边界框标签不准,所以我们在最后10个epoch把它关掉了,让模型在真实分布上做最后的收敛。
针对极不平衡的缺陷类别,我们用了复制粘贴增强:把标注好的缺陷区域裁切下来,随机粘贴到纯背景图上。这样可以人为增加缺陷样本数量,但要注意缺陷的透视和光照要尽量自然,否则模型会学习到“抠图边缘”这种假特征。
3. 多任务集成方案落地
3.1 检测、分割、分类的多头设计
多任务集成听起来是把多个Head拼在一起,实际设计时需要考虑各任务的输出维度和特征需求差异。我们最终的模型结构可以概括为:
- 共享Backbone:负责提取通用特征
- 轻量Neck:负责多尺度特征融合
- 检测Head:输出边界框和置信度
- 分割Head:输出像素级mask
- 分类Head:输出缺陷类别和等级
检测Head沿用YOLOv11的解耦结构,分类和回归分支独立。分割Head则是在Neck的最深层特征上接了一个轻量语义分割头,用1x1卷积把特征投影到类别维度,再通过双线性插值恢复到输入分辨率。这里没有用UNet那种复杂的编码器-解码器结构,因为工业现场只需要对检测框内部的区域做精细分割,不需要全图分割,所以一个轻量头就足够了。
分类Head和检测Head共享了一部分特征,但在最后一层是独立的。这样设计的好处是:分类任务能为检测任务提供更强的语义线索,比如当模型判断出某个区域是“凹坑”时,它对“目标”的响应会更强烈;同时,分类任务不会直接干扰边界框回归的几何预测。
三个Head放到一起后,模型参数量比单独检测模型只增加了不到30%,但推理时由于卷积操作是可以并行计算的,整体延迟增长很小。这也是多任务集成相对多模型串联最明显的优势。
3.2 多任务损失函数的权重平衡
多任务训练最头疼的问题是损失函数怎么写。三个Head的损失量纲不同:检测Loss通常是几的量级,分割Loss可能是零点几,分类Loss又可能是几十。直接把三者相加,大的Loss会完全淹没小的Loss,导致某个任务不收敛。
我们采用了不确定性加权法,为每个任务引入一个可学习的噪声参数,让网络自己学习在训练过程中的任务权重。简单解释就是:哪个任务的噪声大、不确定性高,就自动降低它在总损失里的权重。这种方法实现起来并不复杂,训练时只需要把原始Loss除以噪声参数的平方,再加上对数项做正则。
初始时我们给检测、分割、分类三个任务设置的权重比例是1:0.5:0.2。训练过程中观察到分割Loss下降比较快,检测Loss下降较慢。如果发现某个任务在验证集上的指标停滞,会停下来调整这个权重比例。还有一个工程经验:多任务模型的初始学习率要比单任务模型设置得低一些,因为多个任务的梯度叠加后很容易震荡。我们最终用1e-3预热10个epoch,再降到5e-4。
另外,不同任务对难样本的定义不同。检测任务关注难负样本,分割任务关注边界像素。统一用一个Focal Loss可以让模型把精力放在难分样本上,但alpha参数要按任务分开调节。我们检测头用的是alpha=0.5,分割头用的是0.8,分类头用的是0.25,这些都是通过小规模消融实验确定的参考值。
3.3 工程化实现:从训练到推理
训练阶段,我们使用分布式数据并行在4张卡上跑了80个epoch,batch size设为64。前30个epoch冻结Backbone,只训练Neck和Head,让新加的P2层和注意力模块先适应数据分布;30个epoch后解冻全部层,以较低学习率微调整个模型。
推理阶段,我们设计了一个异步管线。图像采集线程从相机获取RGB图像,预处理线程完成缩放、归一化、颜色空间转换,推理线程运行TensorRT引擎,后处理线程负责阈值过滤、NMS、mask解码和结果上报。四个线程之间用环形缓冲区连接,实测让单帧延迟从25毫秒降到了15毫秒,吞吐量也提升了一倍。
后处理里有一个很容易被忽略的点:多任务集成后,NMS不能只对检测框做,分割mask和分类结果必须跟检测框一一对应。我们选择以检测框为锚点,mask只解析框内区域的像素分类结果,分类结果只取框内最高置信度的类别。这样做避免了一个目标同时被不同Head输出不同标签的冲突,也保证了输出结构的鲁棒性。
工程代码层面,训练脚本和推理引擎都做了配置化。模型结构、数据增强策略、Loss权重、TensorRT精度都通过yaml文件控制,换一个产线项目时不需要改代码,只需要换配置和数据。这个设计在后续维护中省了大量时间。
4. 实测过程与关键参数
4.1 硬件环境与性能基线
整个系统最终部署在一台工控机上,配置是消费级显卡、32GB内存、工业相机和千兆网口。这套配置在工业现场算是很常见的“中低配”,没有用昂贵的数据中心显卡,也是想验证方案在普通设备上的可用性。
模型的输入分辨率设为1280x1280,这个尺寸是经过权衡的。原始相机图像是2448x2048,直接resize到1280x1280会丢失很多细节,导致小目标检测率下降;如果保持2240x2240,推理时间又会超标。后来我们用了带等比缩放和边缘填充的预处理策略,让目标在resize后尽量保留原始比例,同时把无效填充区域通过mask机制在Loss里排除掉,效果介于两者之间。
最终性能基线的数据大概是这样的:TensorRT FP16引擎下,单帧推理耗时约13到15毫秒,加上预处理和后处理,端到端延迟稳定在18到20毫秒。如果把量化调到INT8+FP16混合精度,端到端延迟可以压到10毫秒以内,但小缺陷的召回率会掉2个百分点左右。
4.2 关键训练与推理参数表
我把这套系统里比较关键的参数整理成了一个表,方便大家参考。这些参数不需要完全照搬,因为不同项目的数据分布差异很大,但同一个行业里的场景通常可以复用。
| 参数项 | 配置值 | 备注 |
|---|---|---|
| 输入分辨率 | 1280x1280 | 小目标场景建议不低于960 |
| Backbone冻结轮数 | 30 epoch | 先稳定新加分支 |
| 总训练轮数 | 80 epoch | 余弦退火 |
| 优化器 | AdamW | 配合较保守的weight decay |
| 初始学习率 | 1e-3 | 预热10个epoch后衰减 |
| Weight decay | 5e-4 | 防止多任务过拟合 |
| Batch size | 64 | 4卡分布式 |
| 损失权重 | 检测1.0 / 分割0.5 / 分类0.2 | 初始值,训练中微调 |
| Mosaic增强 | 前70个epoch开启 | 保留10个epoch关闭 |
| TensorRT精度 | FP16为主,敏感层INT8 | 小目标场景注意验证 |
| 推理线程数 | 4 | 采集/预处理/推理/后处理 |
训练过程中,每一轮结束都会在验证集上分别记录检测mAP、分割mIoU和分类准确率。多媒体场景下,这三项指标不会同时单调上升,所以不能只看总Loss,要分任务观察。我们实际上是每隔5个epoch做一次全量验证,把三个指标画在同一个图上,当某两个指标还在上升但第三个指标停滞时,就说明可能需要调整损失权重。
4.3 实验对比与结果分析
为了验证架构优化的有效性,我做了三组对比。第一组是官方YOLOv11权重直接微调,不添加任何自定义结构。第二组是加上P2层和注意力模块,但保持单任务检测。第三组是完整的多任务集成模型。
结果很有意思。第二组相比第一组,在专门的小目标测试集上mAP提升了4.8个百分点,但推理时间增加了约8%。第三组在检测指标上与第二组基本持平,分割和分类指标都达到了生产可用的水平。也就是说,多任务集成并没有让检测精度“付出惨痛代价”,这主要得益于共享Backbone为分割任务提供了更丰富的语义信息,反过来也增强了检测特征。
还有一个对比是量化前后的指标变化。FP16模型相比FP32模型,检测mAP下降了0.1个百分点,几乎可以忽略;但混合精度量化后下降了1.7个百分点,主要集中在面积小于10像素的极微缺陷上。后来我们通过把检测Head回退到FP16,把下降控制在了0.5个百分点以内。
5. 常见问题与排查技巧实录
5.1 小目标漏检严重怎么破
工业项目里小目标漏检是最常被提到的问题。我遇到过最夸张的情况是一种针孔缺陷在1280分辨率下只有5x5像素,人眼在屏幕上都要凑近看,模型更是经常无视它。
排查思路分三步。第一步看数据增强里有没有破坏小目标的语义,比如过强的模糊或降采样。第二步看特征图分辨率,如果Neck的浅层特征没有被送到检测头,小目标信息早就在深层卷积里被稀释了。第三步看NMS阈值,过高的IoU阈值会把密集的小目标框合并掉,这种情况下需要调低置信度阈值,或者在NMS前按目标面积分组处理。
实际项目中我们靠的是三条组合拳:增加P2特征层、把输入分辨率提到1280、针对小目标做了过采样训练。前两条属于架构和推理策略,最后一条是数据层面的操作——训练时专门把包含小目标的样本多采样几轮,让模型对这类目标更敏感。
5.2 多任务训练不收敛或震荡
多任务训练最烦人的是Loss已经很低但指标上不去,或者训练到一半突然发散。我第一次遇到时排查了很久,最后发现原因是不同任务的学习率不匹配。分类任务收敛快,检测任务收敛慢,如果学习率太大,分类分支的梯度会干扰检测分支的特征提取。
解决办法是给不同的Head设置不同的学习率倍数。检测Head保持1.0,分类Head设为0.7,分割Head设为0.5。这样就避免了收敛快的任务“带偏”共享层。另一个常见原因是梯度爆炸,尤其在训练前期。我们给模型设置了梯度裁剪,max_norm设为1.0,效果很直接。
还有一种情况是共享Backbone被某一任务“绑架”。比如分割任务对纹理信息非常敏感,如果分割分支的梯度权重太高,Backbone会过度优化纹理特征,导致检测分支对几何边界不敏感。这种问题靠损失权重很难一次调对,建议定期看共享层输出的特征图激活,如果某个通道全部激活或者全部寂静,就要警惕了。
5.3 部署后延迟抖动和显存持续增长
模型单帧推理正常,但产线跑几个小时后延迟开始抖动,甚至逐渐升高,这种问题往往是显存碎片化或内存泄漏。PyTorch在CPU端做预处理时会在每个线程里缓存中间张量,如果线程退出不干净,显存就不会释放。
解决方法是把预处理和后处理尽量改成C++实现,减少Python层面的张量分配。TensorRT引擎本身相对稳定,但如果你在推理循环里反复创建ExecutionContext,会导致上下文堆积,应该只创建一次然后在循环内重复使用。包装好之后建议开个48小时的长稳测试,每隔一小时记录一次延迟和显存,确认曲线是一条平线再上线。
5.4 误检率居高不下的原因分析
工业现场误检的代价可能比漏检还大,因为会把良品当成次品,严重影响产线的良率统计。有一阵我们模型的误检框特别多,后来发现是图像预处理里的归一化参数和训练时不匹配。我们用训练集的均值和标准差做了归一化,但现场相机输出的已经是8位BGR图像,灰度分布和训练集差异很大,导致模型对亮度变化极其敏感。
于是我们把归一化改成在线自适应直方图均衡,让每帧图像在送入模型前都统一样亮度分布。这个改动让误检率下降了一半。另外,NMS的置信度阈值也可以单独优化,工业场景如果任务比较单纯,可以把阈值从默认的0.25提到0.4,减少大量低置信度误检。
写在最后的小经验
整个项目做下来,我最大的体会是:多任务集成不是把任务头堆上去那么简单,而是要让各个分支的梯度在共享层里“和平共处”。YOLOv11给了不错的底座,但真正决定系统能否长期稳定运行的是对数据的理解,对部署环境的敬畏。
最后分享一个自己踩过坑换来的建议:上线前一定要连续跑48小时长稳测试,把显存监控、温度监控全部打开。很多系统的问题不是一开始就暴露的,而是跑着跑着,延迟从18毫秒慢慢涨到30毫秒,最终把产线节拍拖垮。这种隐性故障不靠长期观察根本发现不了。多任务模型更是如此,任务多了,分支间的相互影响会随着时间推移不断放大,只有提前做压力测试,才能真正做到“工业级”。