凌晨两点半,产线上的摄像头还在不知疲倦地扫着每一个工件,屏幕上滚动着一张张检测框。而我盯了一整天的YOLOv11训练日志,最新一版模型还是把反光当成划痕,把正常的拉丝纹理标成了缺陷。这就是工业视觉落地最真实的一面——算法在公开榜单上刷分容易,到了产线上跟光影、粉尘、模具磨损较劲,才是真正磨人的地方。
这篇东西写给正在做工业质检算法、或者准备把目标检测搬上产线的朋友。我会用自己做过的一个微小瑕疵检测项目当主线,完整讲一遍从数据采集、标注策略、模型训练调参,到推理部署和问题排查的整个过程。用到的模型是YOLOv11,场景覆盖了金属结构件表面的划痕、凹坑、脏污这类小目标缺陷。看完你可以直接照着这套思路去搭自己的检测方案,也能避开我踩过的那些效率黑洞。
1. 项目整体设计:为什么工业小瑕疵检测绕不开YOLOv11
1.1 微小瑕疵到底难在哪
在正式讨论模型之前,得先把问题本身说清楚。很多人第一次做视觉检测,容易拿通用目标检测的思路来套产线场景,结果被现实教育得不轻。产线上的微小瑕疵,通常指的是那些在整幅图像里像素占比极小、对比度又很低的目标——比如一枚金属连接件表面的一道细微划痕,可能只有十几个像素长,在500万像素相机拍出来的图里,它跟周围金属纹理的灰度差非常微弱。
这类缺陷有几个典型的难点:
- 目标尺寸小,很多瑕疵长边只有整图尺寸的2%到5%,属于名副其实的“小目标”。
- 类内外观差异大,同样是划痕,可能是直的、弯的、断续的,也可能带轻微毛刺,模型很难用一个统一特征框住。
- 背景干扰严重,金属表面自带拉丝纹理、喷砂颗粒,这些正常特征在视觉上跟轻微划痕非常相似。
- 样本极度不均衡,良品数量远远大于不良品,而且不同批次的零件表面状态还会有差异。
这些小目标问题反映在训练里,就是模型很容易出现两个极端——要么什么都检不出来,要么把背景纹理全当成缺陷,误检率居高不下。
1.2 为什么最终选择YOLOv11
前期的技术选型阶段,我其实对比过几类方案:传统机器视觉、两阶段检测网络,以及YOLO系列。传统视觉的思路是纯图像处理,用阈值分割加形态学算子去提取划痕边缘。这种方案在特定角度、特定光照下的单一品种件上能用,但只要换一个批次、换一台机加工设备出来的表面状态,参数马上失效,维护成本极高。
两阶段检测网络检测精度确实高,但产线上往往是多工位并发检测,对推理速度有硬指标要求,而且工业现场的计算资源并不宽裕。相比之下,YOLOv11这种单阶段检测模型就有明显优势——它在保持实时推理能力的同时,结构上做了不少针对小目标的优化。YOLOv11最大的特点是把模型解耦成了主干网络、颈部网络和检测头三块,检测头部分在特征融合和尺寸自适应上做了调整,对于小目标的位置回归更友好。
另外一个务实的理由是生态成熟度。YOLOv11的预训练模型可以直接复用,从公开检测数据集上迁移过来的特征对工业场景非常有价值。哪怕只是上千张现场数据,配合预训练权重微调,效果也远好于随机初始化从头训练。这一点对工业项目来说太重要了,因为现场能拿到的有效缺陷样本通常非常少。
1.3 项目目标与技术指标
开始动手前,我先和产线那边明确了技术指标,这一步非常重要。很多项目做到一半出问题,都是因为一开始没约定好“什么算成功”。这个项目定的指标是:
- 漏检率为0,也就是不允许任何真实缺陷逃过检测。
- 误检率控制在8%以内,允许一定误报,但必须控制在产线人工复检可承受的范围。
- 单张图像推理耗时在30毫秒以内,满足产线节拍需求。
- 模型能稳定运行至少30天,适应不同批次零件的表面差异。
这套指标决定了后续所有调参的方向。比如因为漏检率硬性要求为0,那我宁可让模型多报一些疑似区域,也不能让真缺陷溜过去。这在置信度阈值设置和Loss设计上都有直接影响。
2. 数据准备:产线缺陷检测的胜负手
2.1 现场数据采集的几个关键细节
很多做算法的朋友习惯先从公开数据集找灵感,但工业缺陷检测的场景特异性太强了,现场数据永远比搬来的数据集更有价值。数据采集阶段,我和产线工艺人员一起定了一套采集规范,几个细节我觉得值得聊一聊。
首先是相机的架设高度和角度。很多微小划痕在某个光照角度下几乎隐形,换个角度就非常明显。我们用的是环形低角度光源加同轴光组合,通过几次对比实验,最后确定了一个能同时突出划痕和凹坑的光照方案。建议有条件的话,现场采集时多试几组光源角度,不要偷懒只拍一组。
然后是图像分辨率。理论上分辨率越高,小目标的像素越多,对检测越有利。但分辨率升高随之而来的是推理耗时增加和数据存储压力变大。我们现场用的是500万像素相机,传到算法侧统一缩放到1280像素宽。这个尺寸在检测精度和推理速度之间取了平衡点。
还需要特别注意负样本的数量和多样性。负样本就是正常的工件表面。模型如果没见过足够多正常表面,上线后就会把正常纹理、反光、灰尘误检成缺陷。我见过不少项目只在产品库里存了几百张良品图,结果模型在产线上误检率飙到30%以上,怎么调阈值都压不下来。我在采集时特意让产线攒了三个批次的良品图,包含不同光泽度、不同模具磨损状况下的表面状态。
2.2 标注细节决定模型上限
数据标注看起来是纯搬砖工作,但标注质量直接决定模型的上限。YOLOv11用的是矩形框标注,对于划痕这类细长目标,标注框的松紧度对训练效果影响很大。
我标注时定了一条规则:框要贴着瑕疵的实际边缘,宁可稍微紧一点,也不要大范围包含背景。原因很简单——如果框里混入大量背景,模型学到的特征就不纯,推理时会把包含类似背景纹理的区域也判成目标。尤其是金属表面的拉丝纹理,会让模型误认为“凡是纹理密集的区域都算划痕”。
另一个需要注意的点是类别平衡。我们现场最常见的缺陷类型可能有七八种,但某些缺陷样本特别少(比如凹坑只有几十个实例)。这种情况下,我建议不要一上来就分那么多细类,而是先把相似缺陷合并成大类,等后续数据积累多了再细分。类别分的太细而样本不够,模型很容易在少数类别上完全不收敛。
标注完成后,我习惯用脚本检查一遍所有标注框的尺寸分布。这是一个很多人忽略但很关键的步骤。如果发现绝大多数标注框的面积小于32x32像素,那就要在数据策略上想办法了——比如提升输入分辨率,或者增加切图操作,把原始大图切成多个子图再去训练。直接拿小目标占比过高的数据集训练,YOLOv11的效果会非常差。
2.3 数据增强:针对小目标的特殊策略
数据增强是弥补工业样本不足的核心手段。YOLOv11本身自带一套数据增强管线,但默认配置面向的是通用目标检测。工业小瑕疵场景里,有些增强操作要特别注意。
Mosaic增强(四张图拼接)能大大提高模型泛化能力,但在小目标场景下有个副作用——四张图拼在一起时,每个目标都会被缩小。原本就只有十几个像素的划痕,经过Mosaic后再缩一圈,几乎就变成像素点了,模型很难学到有效特征。我的做法是保留Mosaic,同时把输入尺寸适当调大,或者降低Mosaic的概率,给模型更多机会看到完整大小的目标。
还有一个很有效的增广是“粘贴复制”,也就是把缺陷区域抠出来,贴到不同的正常工件背景上。这样既扩充了缺陷样本,又让模型学会“在不同背景下识别同一缺陷”。我用这种方式把每个缺陷实例扩了三倍,效果非常显著,比单纯旋转缩放带来的收益大得多。
颜色抖动类增强在工业场景要慎用。很多金属表面的缺陷靠灰度和纹理区分,颜色变化并不大,过度调亮度、对比度反而可能把正常表面变成“伪缺陷”,让模型学偏。我的选择是把亮度扰动幅度调得很小,只做轻微的变化。
3. 训练与调参:YOLOv11在产线小缺陷上的实战记录
3.1 训练环境与模型结构选择
这次训练用的是单张消费级显卡,显存24GB,说实话这个配置在工业算法团队里算很常见。YOLOv11我选了中等的模型规格,理由是它在精度和速度之间最平衡。更大的模型在小目标检测上确实有一定精度优势,但推理速度会往下掉,而且工业部署阶段通常还会做量化,过大的模型在量化后精度损失更明显。
数据集划分上,我按批次数来划分而不是按图片数随机划分。因为同一批次的零件表面状态高度相似,如果同一个批次既出现在训练集又出现在验证集,验证指标会虚高。先按批次分好,再从训练集中随机抽一部分做验证,这样得到的指标上线后更有参考价值。
训练时我直接用官方预训练权重做初始化。对于工业数据来说,哪怕缺陷形态和公开数据集里的物体差异很大,预训练模型在前几层学到的边缘、纹理特征依然通用,这部分特征可以很好地迁移过来。从头训练的话,不仅需要更多数据,收敛速度也慢得多。
3.2 关键超参数的设置逻辑
YOLOv11的超参数不少,但真正需要精细调整的核心就几个。第一个是输入尺寸。我直接从640起步测试,发现小目标漏检严重,然后把输入尺寸提高到1280,漏检率明显下降,但显存占用和推理耗时也上来了。于是我又做了二次验证——把输入调到1536,发现精度提升非常有限,说明1280已经接近当前模型的拟合极限,再往上走只是浪费算力。
第二个是训练轮数。小目标场景的收敛速度比通用目标检测更慢,我一开始按默认值只训练了100轮,发现验证集loss还在缓慢下降,尤其是小目标那一部分的置信度还在提升。后来改到200轮,同时开启早停策略,训练到160轮左右就自动停了。
batch size和初始学习率需要联动调整。YOLOv11默认的初始学习率是基于一定batch size设置的,batch size翻倍时学习率也要相应调大。我在实验中发现,小目标检测对大学习率特别敏感,学习率稍高,模型就容易在早期把特征提取层带偏,后面很难纠正回来。所以实际训练时我把初始学习率调低了三分之一,配合warmup轮数,让模型平稳度过前期。
关于损失函数的那几个权重参数,我也做了实验。YOLO系列在分类和回归损失里通常有调节系数,在两个候选池里分别对应大目标和小目标的行为。我发现小目标的位置回归比分类更重要——瑕疵本来就在图像里只占一点点位置,框定位准了,至少产线那边可以准确切出区域给人工复核。所以我把回归损失的权重调高了一些,牺牲了一点分类置信度的平滑性,换取框的位置更准。
3.3 调参过程记录与消融实验
整个调参过程里我建了一个实验记录表,每个版本改了哪些参数、验证集指标如何变化,都实时记下来。这份记录最后帮了大忙——当模型效果反复横跳的时候,能查出来到底是哪个改动带来的收益。
大概的消融实验我没有全部跑完,因为时间实在有限,但几个关键结论比较明确:
| 实验项 | 验证集mAP | 漏检率 | 误检率 | 备注 |
|---|---|---|---|---|
| 基线:640输入,默认增强,100轮 | 0.61 | 12.4% | 6.2% | 小目标基本没检出来 |
| +1280输入 | 0.72 | 5.7% | 7.8% | 分辨率是最大提升点 |
| +低概率Mosaic + 粘贴复制增强 | 0.78 | 2.6% | 6.1% | 小目标增强收益明显 |
| +200轮 + 调整回归权重 | 0.81 | 1.2% | 5.5% | 回归头更稳定 |
| +灰度扰动(替代颜色抖动) | 0.83 | 0.9% | 5.2% | 误检下降 |
从这张表能看出来,分辨率提升是小目标检测最有效的一招,数据增强第二,训练策略排在第三。很多人一上来就调学习率、调NMS,这些细枝末节的影响远不如把输入尺寸提上去来得实在。
4. 推理部署与产线实战适配
4.1 从PyTorch到ONNX再到TensorRT
模型训练完只是第一步,真正上产线还要解决推理性能的问题。我们现场用的是一个内置显卡的工控机,算力不算强,要把模型跑进30毫秒以内,直接拿PyTorch推理肯定不行。我走了通用的优化路径:把模型导出为ONNX格式,再用推理优化引擎做加速。
导出过程中值得注意的一个坑是动态输入形状。PyTorch训练时输入尺寸是动态的,导出ONNX时如果保留动态维度,后续在推理优化引擎里反而可能因为形状不定而无法开启某些优化。我的做法是直接在导出时固定成1280x1280,虽然灵活性降了,但换来了更快的推理速度。
半精度推理在工业场景里争议很大,主要担心精度损失。我在验证集上做了对比,把Float32和半精度的检测结果逐张看了一遍,发现大部分值得关注的小目标都保留住了,偶尔有个别置信度轻微下降,但没出现漏检的情况。于是放心开启了半精度。实测下来,单张推理从裸模型的90毫秒左右降到了25毫秒左右,顺利达标。
4.2 阈值设置与NMS参数调整
推理阶段的置信度阈值和NMS阈值,看起来是推理时参数,但它们的设置会直接改变产线实际效果。这个环节我花了差不多整整一个晚上,所以这篇文章的标题才会有“深夜调参”这四个字。
面对漏检率必须为0的要求,我的策略是全局置信度阈值设置得比较低(具体到咱们这个场景,初始值定在了0.15附近),尽可能让模型多输出候选框。但这么做会让误检数量上升,所以我又加了一个“小目标复核”逻辑——置信度在低位区间的候选框,如果面积特别小,不直接放行,而是把对应图像区域切出来交给后续的人工复检队列。这样既保证真缺陷不漏,又不会把误检直接混进合格品里。
NMS阈值也做了一个小改动。针对细长型划痕,两个预测框可能会有比较大的重叠区域,如果NMS阈值设得太低,会误删掉本来正确的检测框。这种现象对工业细长目标特别明显。我对比了几个候选值,最后选了一个折中参数,既能让互相重叠的冗余框被抑制,又不至于把相邻的两个真实划痕合并成一个。
这里还有一个经验要分享:不要相信验证集挑选出来的阈值可以直接照搬上线。产线现场的光照、角度、震动跟实验室差别很大,实测阈值需要拿现场一个小时的连续数据重新标定一遍。我后来写了一个小脚本,可以加载现场视频流做实时统计,输出不同置信度下的漏检和误检变化曲线,用这个曲线去确定最终阈值,比手动调靠谱得多。
4.3 产线实际表现与人工复检闭环
模型上线以后,我统计了连续三天的运行数据,整体情况是:漏检率控制在0.5%以下,满足不是0但已经非常接近0了;误检率在6%到8%之间波动,跟不同批次的表面状态有关系。误检出的区域会切成小图保存下来,自动推送给人机交互界面,由质检员快速扫一眼判断是否真缺陷。
这个“自动检测加人工复核”的闭环在工业场景里非常实用。因为瑕疵检测这种任务,很多边界案例是模糊的,强求模型做到100%准确不现实,但让模型把明显缺陷全部找出来、把模棱两可的区域汇总给人工,就能同时满足漏检和误检两个指标。
工程师最怕的其实是模型上线后面对新批次数据性能崩塌。我们的经验是用“影子模式”持续采集现场图像,每天在后台用新批次数据跑一遍旧模型,如果检测结果分布出现明显变化,就触发预警提醒工艺那边先查设备状态。这相当于给模型装了一个“泛化漂移”的监测器。
5. 常见问题与排查技巧实录
5.1 训练Loss不降该怎么办
如果训练初期分类和回归损失一直在高位震荡,先别急着调学习率。我遇到这种情况时,第一反应往往是回头查数据标注。有一批次数据,我发现有几个标注框明显标歪了,还有两个类别标反了,这种脏数据会让模型无所适从,表现在Loss上就是忽高忽低。
如果确认数据没问题,再看是不是正负样本比例失衡。YOLOv11训练时内部会做正负样本匹配,如果一张图里只有零零星星几个小目标,大量区域都是负样本,模型很容易收敛到“全输出背景”的状态。解决办法可以是调整匹配策略参数,让更多预测框有机会参与正样本匹配。
5.2 小目标一直漏检怎么排查
漏检的重灾区永远是小目标。我排查时会按下面几步走:
- 先看训练样本的目标尺寸分布,如果长边小于16像素的目标占比超过一成,说明数据端的“可见性”就不够。
- 再看验证集上漏检目标的具体位置,很多情况下是目标出现在图像边缘。YOLOv11的特征图经过多层下采样后,图像边缘位置的特征信息流失严重,导致检测不到。
- 尝试对原始大图做切片推理。把1280x1280的输入切分成四个更有重叠区域的子图,分别推理再合并结果。这个做法能明显提升边缘小目标的检出率,代价是推理时间翻倍。
5.3 批次变化导致误检率飙升
这个现象在产线上最常见。同一个型号的工件,换了模具、换了材料批次,表面纹理状态变了,模型就开始把正常的纹路当缺陷报。我遇到这个问题后的处理方式分两步:一是收集新批次的良品图,把它们加入训练集重新微调一轮,等于让模型“认识”新朋友;二是把新批次的图像拿去做特征分布对比,看是整体分布漂移还是某个局部区域变化,这样能判断要不要调整光源和相机角度。
5.4 一张实用的排查速查表
| 现象 | 优先排查项 | 常见解决办法 |
|---|---|---|
| Loss高但不降 | 标注质量、数据脏样本 | 清洗数据集、修正错误框 |
| 小目标漏检 | 输入分辨率、目标尺寸分布 | 提高输入尺寸、切片推理、增强小目标样本 |
| 误检点多 | 负样本不足、增强过度 | 补充良品负样本、降低颜色扰动、增加灰度扰动 |
| 换批次性能崩 | 特征分布漂移 | 收集新批次良品微调、检查光源 |
| 推理速度慢 | 未量化、输入太大、NMS太重 | 导出ONNX转优化引擎、半精度、压缩推理尺寸 |
个人建议把这几个问题当成“已知清单”记在项目文档里。因为这类问题几乎每个工业视觉项目都会碰到,跳过排查步骤直接调参数,往往只是在原地绕圈。
6. 深夜调参后的整体心得
对我来说,这次YOLOv11做产线微小瑕疵检测的项目里,最大的体会是:模型只是放大器,真正的功夫都在数据和处理细节上。分辨率策略、增强策略、标注规范,每一项都比单纯调学习率带来的收益大得多。我在这个项目里学到的一个小技巧是——每次训练前先跑一遍标注尺寸分布脚本,你就会知道哪些目标是模型“根本看不见”的,针对性地去解决,而不是盲目堆算力。
如果你也在做类似的项目,建议给自己做一个实验记录表,把每个版本的参数、数据、验证指标、现场表现都记录下来。深夜调参最怕的不是调不出来,而是明天醒来忘了今天改了什么。