做钢材表面缺陷检测这个项目,我最早就是从YOLOv5入手的。钢材在热轧、冷轧、酸洗、镀锌这些工序之后,表面会出现氧化铁皮、划痕、麻点、夹杂、辊印等各种缺陷,靠人眼盯监控屏不仅费神,漏检率还很难压下来。用误差检测模型去替代人工,几乎是产线升级的必经之路。前前后后折腾了几个月,我把YOLOv5、YOLOv7、YOLOv8、YOLOv10都在这套缺陷数据上跑了一遍,有些结果和社区里传的“新版本一定更好”并不完全一样。这篇文章就把完整过程写出来,给做工业视觉检测的朋友做个参考,尤其是刚入行准备选型的同学,应该能省不少弯路。
1. 产线质检的真实痛点:为什么最后选了YOLO系列
1.1 钢材表面缺陷到底长什么样
先对齐一下缺陷类别。钢材表面缺陷不是一个东西,而是好几类视觉特征差异很大的东西叠加在一起。最常见的几类大概是这些:
- 氧化铁皮压入(麻点/麻面):热轧过程中氧化皮被压入钢板表面,呈现点状、片状分布,颜色偏暗,和基材对比度低。这类缺陷分布广,单个体积小,有时候整块板密密麻麻,让人标注到崩溃。
- 划痕:轧辊或导卫设备刮擦造成,呈线状,方向性强,大多沿轧制方向延伸。有的划痕宽而深,有的细到只有几个像素宽。
- 夹杂:轧制前或轧制过程中非金属夹杂物压入表面,形状不规则,边界模糊,灰度变化不大,属于最难检的类型之一。
- 裂纹:板坯在冷却或后续加工时产生的线状裂纹,细、浅、线条扭曲,背景稍微复杂一点就看不见。
- 辊印:轧辊表面局部损伤后,在钢板表面周期性重复出现的压印,有固定的间距,算是有迹可循,但容易和多处随机缺陷混淆。
这些缺陷的尺度差异非常大,大的有几十厘米长,小的几个毫米;对比度也完全不同,有的黑得明显,有的只比背景深一点点。这决定了对模型的要求不只是“能不能检测”,而是“在高速运动、光照不均的现场能不能稳定检出小目标和弱纹理目标”。
1.2 传统视觉方案的瓶颈
很多早期项目用传统图像处理做这类检测:阈值分割、边缘检测、频域滤波、模板匹配。固定光源、固定背景下,规则写得仔细一点,确实能跑通。但一到现场就露馅——产线震动导致相机抖动、钢材高速运动产生运动模糊、水面油污反光、光照波动、钢种变化导致底色不同,任何一条都让阈值失效。
我给某项目做过一次对比测试:把传统算法调了大半个月,干净样本上能到90%以上检出率,一换钢种直接掉到60%。规则是死的,表面纹理是活的,这就是传统方案在钢材质检上推动不了的原因。
1.3 为什么是YOLO,而不是Faster R-CNN或SSD
工业项目不是发论文,它有三个硬约束:速度、稳定、可维护。两阶段检测器精度是高,但推理速度很难满足产线节拍;另外,它在小目标上虽然理论更强,但部署复杂度也更高,四周的依赖库、选择更重,交付时间压不住。
SSD当时已经不是主流了,社区生态弱,遇到问题连参考都不好找。YOLO单阶段结构平衡了速度和精度,而且从YOLOv5开始整个生态比较成熟:标注、训练、导出、部署工具链都是现成的,人员上手快,后期维护也有大量资料。选它做工业视觉项目,输了模型还能换版本迭代,不用推翻重来。
2. YOLOv5当baseline:从数据采集到训练调参的完整流程
2.1 数据采集和标注决定上限
模型结构再先进,数据不行都是白搭。钢材检测的数据采集有几个关键点:
- 用线扫相机而不是面阵相机:钢板是连续运动的,线扫配合编码器做行触发,才能得到无拉伸的清晰图像。
- 光源很关键:我见过一些项目把光源随便装在产线上,结果反光斑和缺陷在图上长得一模一样。后来改用低频闪照明加偏振片,反光才压下去。
- 样本要有代表性:不同钢种、不同厚度、不同速度下拍出来的图差异很大。如果只拿一个班次、一个钢种的数据训练,模型一到换钢种就崩。
标注这一步也踩过坑。最开始对划痕这种长条目标直接拉一个大方框,结果一个框里大半是背景,模型学出来又飘又不稳定。后来统一规则:长宽比超过5比1的条状缺陷拆成多个实例,相邻实例预留一点重叠。虽然标注量上去了,但定位精度明显改善。
还有数据集划分的问题。同一卷钢板上反复出现的周期性缺陷,如果直接把裁切图随机分到训练集和验证集,同一缺陷的多个图像就会同时出现在两边,验证指标会虚高得离谱。某模拟项目中我一开始就被这个假象骗了,验证集mAP干到90%以上,一上线漏检一大堆。正确做法是以“卷”或“最小缺陷单元”为单位划分,保证同一物理缺陷只出现在一侧。
2.2 YOLOv5模型选择和输入尺寸
YOLOv5有n/s/m/l/x几个规格。我当时先用YOLOv5s跑通全链路,因为工业项目先要有一个端到端可用的版本,再去谈精度。s跑通了之后,再对比m和l在精度速度上的收益,最终发现对于钢材表面缺陷这个场景,输入分辨率比模型宽度更影响结果。
默认大家都喜欢用640x640输入。但对直径只有几十像素的麻点、宽度只有几个像素的裂纹,640分辨率下这些目标摊到特征图上的有效像素太少,模型容易学不到细节。我把输入分辨率提到1280x1280后,mAP50在模拟项目X上提升了将近7个百分点。代价是推理耗时接近翻倍。所以最终取舍要看产线速度和算力:不能只看模型卡,要看“在允许的推理耗时下,哪个分辨率能最高”。
另外,YOLOv5会自动根据训练集聚类anchors。但钢材缺陷的长宽比极端,自动聚类出来的anchor分布仍然偏向普通目标。我在自己的脚本里统计过标注框尺寸分布,发现大部分缺陷集中在几个特定尺度区间,元素长宽比分布跨度很大。因此我建议在训练好第一版之后,重新跑一下anchor分析,必要时手动指定anchor的尺度和比率,让它们照顾到5:1以上的长条缺陷。
2.3 训练时的关键参数和坑
YOLOv5的默认超参数在一般目标检测上表现不错,但钢材缺陷有自己的脾性。我的配置大致是这样:
- 输入尺寸:1280x1280,模型规格选s优先,后期升级到m。
- epochs:100起步,小数据集不要一次拉太长,容易过拟合;用早停机制盯着验证集。
- batch size:尽量大,显存不够就减小输入或梯度累积,稳定BN统计量对钢材这种背景纹理复杂的图很重要。
- 优化器:SGD配余弦退火,初始lr 0.01,weight_decay 0.0005。
- 数据增强:Mosaic增强在YOLOv5训练里默认开,但有个隐藏问题——Mosaic把四张图拼一起,很多小缺陷被裁出边界,模型反而学不到完整目标。我在后半段训练阶段关闭Mosaic,只留基本翻转、缩放和HSV扰动,验证集小目标召回有明显回升。
还有类别不平衡。钢材缺陷里划痕样本可能占了一半,裂纹样本可能只有几十张。光靠简单采样,模型会把裂纹当成背景。我采取了两手策略:第一,对稀有类别做过采样,让每个epoch里裂纹图重复出现;第二,给稀有类别的loss加一个权重系数。两种办法叠加之后,裂纹的召回率从42%左右提到了67%。
2.4 baseline效果与瓶颈
跑完一波调参,YOLOv5s在1280输入、某模拟项目X数据集上的结果大概是:mAP50约83~85,mAP50-95约55~58,TensorRT FP16下单张推理速度约60FPS上下。这个结果能上线吗?保守说,能跑但不够稳。主要漏检集中在两类:细裂纹和低对比度麻点。这些目标实在太细、太浅,模型特征表达不够,往下调置信度阈值又会带来一堆误报。所以这只能当baseline,接着试新版本。
3. YOLOv7带来的变化:重参数化和小目标缺陷的取舍
3.1 结构升级在哪里
YOLOv7在结构上的核心变化是E-ELAN(Extended Efficient Layer Aggregation Network)和重参数化卷积(RepConv)。简单理解,E-ELAN让网络在不同层之间做更丰富的特征聚合,低层细节和高层语义合并得更好;RepConv则把训练时的多分支结构在推理时合并成单一卷积,部署时计算量更低。
这对钢材缺陷检测是有实际意义的。裂纹、麻点这种小目标依赖的是低层细粒度特征,同时又要借助高层语义来区分“这是裂纹”和“这只是一个背景纹路”。YOLOv7的特征聚合方式比YOLOv5的跨层连接更充分,理论上小目标特征表达更强。
3.2 训练和部署的不对称性
用YOLOv7有一个很直接的体验:训练时慢,部署时快。因为重参数化结构在训练阶段是多个分支叠加,显存和计算开销都不小;训练完成后要把RepConv分支重参数化还原成单分支结构,模型才变成真正的部署形态。
这里有一个值得提醒的坑:训练完一定要做权重转换,然后用转换后的模型重新跑一遍全部验证集,别拿训练状态下的模型直接报指标。我在某项目里曾经直接拿训练产物做TensorRT导出,结果算子不兼容,走了不少弯路才意识到要先做重参数化合并。如果工具链不支持自动合并,需要自己写脚本处理或者换一个导出工具。
3.3 针对钢材缺陷的改进尝试
YOLOv7本身的精度不错,但为了拉高裂纹这类低对比度缺陷的召回,我还做了两处改动:
- 加注意力机制:在backbone和neck之间加SE或CBAM模块,让网络更关注通道维度上有信息量的特征。实测中,CBAM对裂纹和麻点的提升比SE更明显,大概mAP50涨了1.5个百分点,代价是训练时间长了十分之一。
- 调整anchor比例:钢材缺陷长宽比极端,我把anchor的宽高比从默认的[0.5, 1, 2]扩展到[0.25, 0.5, 1, 2, 4, 8],又针对划痕单独加了一个超宽anchor。定位准了,但负样本误报也略增,需要后续调NMS参数来平衡。
从YOLOv5切到YOLOv7时,不要想当然地把YOLOv5的权重拿来微调。两个网络结构差异很大,迁移学习收益有限,更靠谱的做法是用官方COCO预训练权重,在自己的缺陷数据上重新训练全部层。
3.4 实际表现:精度高了,部署成本也高了
在模拟项目X上,YOLOv7的mAP50确实比YOLOv5提升了不少,大概到87%左右,mAP50-95到61%。但它有两个明显的代价:一是模型参数和计算量明显上升,大约36.9M参数,训练显存需求更大;二是在TensorRT上部署时,重参数化后的算子偶尔有兼容性问题,要花时间调。
最终判断:YOLOv7适合离线分析或者算力充足的工控机场景。如果目标是追求极致精度、每分钟需要分析的量不大,它很能打;但如果是高速产线实时检测,后面出来的YOLOv8和YOLOv10更划算。
4. YOLOv8与YOLOv10:anchor-free、解耦头和端到端推理
4.1 YOLOv8为什么把小目标漏检降下来
YOLOv8带来的变化里,我认为有三个对钢材缺陷最重要:
- C2f模块:比之前的CSP结构更强调梯度流的分支融合,小目标特征不容易在深层被稀释。
- anchor-free检测头:直接把目标中心点当正样本,不需要预设anchor比例。对极端长宽比的划痕来说,anchor-free天然更灵活,省掉了聚类anchor这一步。
- 解耦分类和回归头:分类任务和定位任务各用各的分支,互不干扰。钢材缺陷里很多目标对比度低、边界模糊,分类和定位的特征需求不同,解耦之后两个任务都能收敛得更好。
在实践中,YOLOv8s在1280输入下的mAP50达到了约86.7,mAP50-95约59.6,和YOLOv7已经非常接近,但参数量只有11.2M,推理速度更快。它最明显的变化是漏检少了,低对比度麻点和细小裂纹的召回都有改善。
4.2 YOLOv10的端到端设计
YOLOv10给人的第一印象是结构图比之前复杂,但核心理念反而是“简化”:主分支采用NMS-free端到端设计,双标签分配策略让网络在训练时有一个辅助分支帮助学习,推理时只走主分支、去掉NMS后处理。
去掉NMS对工业检测影响很大。传统检测器完成推理后还要做NMS,多了一步后处理不说,NMS的参数也依赖人工调试。YOLOv10端到端输出时直接给最终框,一方面延迟更低,另一方面也减少了后处理把正确框滤掉的概率。
但有个细节要清醒:NMS-free并不是免费的。YOLOv10在某些目标密集的场景会比带NMS的结构多一点重复输出,需要额外用置信度阈值控制。另外,它的端到端训练比较敏感,最好按照官方推荐的训练配置跑,不要自己乱改标签分配部分。
4.3 一碗水端平的对比,先看数据再说
四代模型要在同等条件下对比,否则没有意义。下面这张表是模拟项目X上的实测结果,输入分辨率均为1280x1280,部署平台为同一套推理优化工具链(TensorRT FP16,静态batch=1):
| 模型 | 参数量(M) | mAP50(%) | mAP50-95(%) | 推理FPS | 备注 |
|---|---|---|---|---|---|
| YOLOv5s | 7.2 | 84.2 | 56.3 | 60 | 基线,稳定但小目标偏弱 |
| YOLOv7 | 36.9 | 87.6 | 61.2 | 38 | 精度最高,显存和耗时压力大 |
| YOLOv8s | 11.2 | 86.4 | 59.8 | 72 | 均衡,推荐默认选择 |
| YOLOv10s | 8.0 | 87.1 | 60.9 | 88 | 端到端,速度快,精度靠近YOLOv7 |
看数字就知道,社区里“新版碾压旧版”的说法不能全信。YOLOv7小目标精度依然能打,但它的时效比不够好;YOLOv8和YOLOv10把速度和精度平衡到了更适合产线的区间。如果让我选:追求极致精度的离线分析用YOLOv7,实时产线首选YOLOv8s或YOLOv10s,再根据误报和漏检的具体表现决定。
5. 部署在线质检系统时那些文档里不会写的事
5.1 模型导出与精度对齐
训练完了不等于能上线。YOLO模型从PyTorch到部署板卡,一般要经过ONNX导出再到目标推理引擎。这个环节的坑比训练多得多。
首先是动态shape和静态shape的选择。钢材表面图像尺寸不一定固定,很多项目用动态shape图方便。动态shape在CPU上还好,到了GPU TensorRT上要么编译慢,要么在切换分辨率时触发重复优化,推理性能不稳定。我的做法是固定输入尺寸,上线前把所有验证集的批次、分辨率都固定好,换来的是部署稳定性和更高的吞吐。
其次是精度对齐。同一份模型,PyTorch直接推理和TensorRT FP16推理的结果可能差0.5~2个mAP。FP16对小数值梯度不敏感,钢材背景的纹理波动可能被量化掉,导致裂纹检出率下跌。我遇到过INT8量化后裂纹类别mAP直接腰斩的情况,最后老老实实用FP16部署,保留了性能和安全之间的平衡。
最后是版本问题。推理引擎、工具链版本一旦升级,权重导出产物就可能不兼容。这点在工业项目里很容易被忽略,上线半年后想增加一个类别,结果发现原先的训练环境没了,导出链路全断。最好从第一天起把环境版本和导出脚本都固化到文档里,甚至打包成容器镜像。
5.2 切图拼接与坐标映射
相机拍出来的整幅钢板图像往往很大,比如12000像素宽、10000像素高。检测器没法直接塞进这么高的分辨率,需要先切图再检测。切图会产生边界问题——一个缺陷恰好被切成两半,两个裁切块各检测到半个目标。
解决思路是重叠切图:相邻块之间留出一定重叠区域,检测后再把重叠区域内的重复框合并掉。重叠比例我一般设5%~10%,太小了防止不了目标跨边界,太大了重复计算太多、检测耗时上升。合并的策略不是简单做NMS,而是先把候选框映射回原图坐标,再做一次跨块NMS。
还有坐标映射。模型输出的是像素坐标,产线要的是缺陷在钢卷上的物理位置,比如距离带头多少米、距边部多少毫米。这一步需要在相机标定阶段完成像素到物理坐标的换算。如果有多台相机拼接覆盖整幅宽度,还要考虑相邻相机的重叠视野和标定误差。很多项目在模型上花了大时间,却在这个机械但关键的环节翻车。
5.3 连续运行稳定性
实验室里跑100张图、算完指标就宣布成功,这在工业现场是远远不够的。连续运行会暴露出显存泄漏、队列堵塞、偶发卡死等一堆问题。检测结果也有抖动,同一缺陷上一帧置信度0.6,下一帧0.4,再下一帧又0.7,直接按单帧阈值报警会频繁误报。
我后来做的方案是加一层简单的多帧确认机制:连续三帧中至少两帧检出同一位置的缺陷,才触发报警。这样做牺牲了一点单帧灵敏度,但误报率大幅下降,现场能接受。定位框也用轻量的平滑方式处理,避免画面里框的位置来回跳。
部署后还要盯性能衰减。模型在线上跑几个月,环境变了、相机亮度和位置微偏,整体指标会慢慢下滑。建议设定一个周期性的样本回测流程:定期抽当天数据回放,快速算一遍mAP和漏检率,一旦发现异常就重新校准或更新模型。这一点看起来不起眼,却是产线项目能不能长期稳定交付的分水岭。
5.4 从哪个版本开始项目更容易
如果完全让我重新做一个钢材表面缺陷检测项目,我会这样走:
- 第一版直接用YOLOv5s搭起全流程,数据、标注、训练、导出、部署、报警全部打通。
- 稳定跑两周之后,收集线上漏检和误报样本,补齐训练集短板。
- 再并行尝试YOLOv8s和YOLOv10s,各保存一次完整评测,对比表格和可视化结果。
- 如果算力富余且对精度有极致要求,最后考虑YOLOv7,但提前评估重参数化部署的额外工作量。
YOLOv8s的算子兼容性在主流推理引擎上比另外几个版本更好,遇到奇奇怪怪的算子问题少一些,适合当作“兜底方案”。YOLOv10s虽然跑得最快,但端到端训练和阈值行为都需要单独调,上新项目时最好不要一上来就选最激进的方案。
6. 经验沉淀:几个比选模型更重要的习惯
整个项目做完,我最深的体会是:数据质量比模型结构重要得多。有一段时间我连续换了好几个模型,mAP始终卡在原来的水平上,后来把最难检的那一批裂纹样本单独拉出来补充标注、重新训练,指标立刻涨了一截。模型是在表达数据里的规律,数据本身没覆盖到的规律,再先进的网络也发现不了。
对比实验要做成“一套脚本、一套数据集、一套评测指标”。很多项目换模型时顺手改了输入分辨率、改了增强策略、改了训练步数,结果根本说不清性能变化来自哪一部分。我只在完全相同的评测脚本和相同的数据划分下比较各模型,这样才能得到可信的横向对比。
不要只看mAP。工业缺陷检测里,每类召回率才是真正要盯的东西。尤其裂纹和麻点这两类稀有缺陷,如果总样本只有几十个,一个漏检对mAP的影响没有想象中大,但对现场来说漏一个就是一次质量事故。我会把每一类的precision、recall单独列出来,再配合可视化错误样本的图看,比单看总指标有效得多。
线上误报是一座更大的山。模型训练指标再好,上线后误报率一旦高,操作工很快就不信任系统,最后变成“检测系统在跑,但没人按它报警处理”。一定在离线阶段就把误报样本池建起来,把常误报的背景纹理样本加入训练集,反复迭代。这一步的工程量可能比换模型还大,但它是真正决定系统能不能被现场接受的关键。
如果以后继续在这个方向深入,我会优先尝试半监督或自监督预训练。钢材本身的背景纹理花样很多,带标注的缺陷数据永远不够,但无标注的钢板图像几乎取之不尽。让模型先在大规模无标注钢材图像上学一遍底层纹理规律,再做下游检测微调,是比单纯换更新版本模型更有潜力的路线。
最后分享一个实操小经验:所有训练过程和评测结果,我都用带时间戳的目录保存,包括超参数、模型权重、评测日志、可视化结果。项目做到后面会同时存在十几个模型目录,没有这套记录体系,你很快就会分不清哪个版本是真正可以上线的那个。别嫌麻烦,这个习惯在关键时刻能帮你省下好几天的排查时间。