ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

钢材表面缺陷检测实战:从YOLOv5到YOLOv10的选型与部署

钢材表面缺陷检测实战:从YOLOv5到YOLOv10的选型与部署

钢材表面质量把控这件事,在很长一段时间里都靠质检员肉眼盯。带钢在产线上以每分钟几百米的速度跑,缺陷转瞬即逝,人盯久了必然疲劳,漏检几乎无法避免。这也是基于深度学习的表面缺陷检测这几年在金属加工行业里反复被讨论的原因。而真正谈到落地,绕不开的就是YOLO这一系列检测算法——从YOLOv5到YOLOv7、YOLOv8,再到新出的YOLOv10,同一个任务在不同的版本上跑,性能和坑都大不一样。

这篇文章是我在钢材表面缺陷检测项目上从零到一的完整记录:数据集怎么准备、YOLOv5怎么训练、同一份数据下YOLOv7/8/10到底谁更强、部署到真实质检环节会遇到哪些实验室里想不到的问题。适合准备拿工业视觉课题练手的人,也适合已经在做质检系统但想换一套更可靠模型的工程师。

1. 表面缺陷检测为什么会选中目标检测这条路

1.1 人工质检和传统视觉的局限

先聊产线现状。

钢材表面质检的传统方案,一靠人眼,二靠常规图像处理。人眼的短板是客观生理限制:质检员盯着一块匀速运动的带钢表面,注意力最多维持二十来分钟,疲劳之后细小缺陷的漏检率会肉眼可见地上升。更麻烦的是,一条产线的质检位往往要求连续运转好几个小时,漏检带来的客诉和退货成本,远高于多招一个质检员的成本。

传统图像处理(阈值分割、边缘提取、模板匹配)在单一背景的样品图上很有说服力,但钢材表面本身就不是干净背景——有氧化色差、有油污残留、有纹理起伏,这些干扰在图像上的表现和真实缺陷高度相似。规则只要定得稍微严一点,误检率就飙升;定得松一点,漏检又回来了。我见过有人用形态学运算试图把划伤和纹理噪声分开,现场一换钢种,整套参数立刻失效。

1.2 缺陷的本质是"找框加分类"的问题

把问题抽象一下:无论裂纹、夹杂还是划伤,在图像里都是一个局部区域,有位置、有大小、有类别属性。这正好落在目标检测的模型输出空间里——矩形框标出位置,分类头给出缺陷类型。所以表面缺陷检测天然适合目标检测架构,而不是图像分类或语义分割。

分类模型给不出位置信息,没法指导现场定位缺陷;分割模型需要像素级标注,成本高到工业数据规模根本撑不起来。综合精度、速度、标注成本三者,目标检测是那个最平衡的选择。这也是我在这个项目里始终坚持"检测路线"而不是"分割路线"的原因。

1.3 六类典型缺陷各自的脾气

钢材表面缺陷数据集里最常出现的六个类别,识别难度差异非常大,我这里逐个说一下它们的脾气:

  • 裂纹:细密的龟裂纹理,颜色接近背景,边界模糊,模型最容易漏检。
  • 夹杂:点状或短条状的异物压入,对比度中等,形状不固定。
  • 斑块:大面积灰暗区域,边缘柔和,极易和光照不均混淆。
  • 麻点:密集的小凹坑,典型的小目标问题。
  • 氧化铁皮压入:深色不规则块状,与背景差异明显但形状多变。
  • 划伤:细长线条,长宽比很极端,标注框很容易把无关背景包进去。

这六类的共性是"类内差异大、类间差异小"。同一个划伤,有的又深又长,有的浅得像一层水印;不同类型的缺陷之间,比如斑块和氧化铁皮压入,肉眼看起来都是深色块。模型要在这种数据上把类别分开,不是单纯堆参数就能解决的。

1.4 为什么最终落在了YOLO家族

工业实时性要求让两阶段检测器(先提候选框再分类)基本出局,YOLO这类单阶段算法用一次前向同时完成定位和分类,在速度和精度之间取得了很好的平衡。而且YOLO系列版本迭代快、社区资料多、部署方案成熟,从训练到导出再到嵌入式推理,每个环节都有现成方案。对一个落地项目来说,这比"理论上更优但资料稀少"的算法重要得多。

2. 数据集准备:先给模型定好天花板

2.1 公开数据和现场数据怎么配合

我做对比实验用的是公开的冷轧带钢表面缺陷数据集,六类缺陷,每类约三百张样本,按8:2切成训练集和验证集。公开数据的好处是标注规范、类别齐全,适合做算法验证和版本横向对比;坏处是背景太干净,和真实产线的光照、氧化色、油污环境存在差距。

如果目标是部署上线,我的建议是"公开数据打底,现场数据微调"。先用公开数据把模型的基本能力训出来,再采集几百张现场图片做增量训练。现场图片哪怕只有两三百张,对真实场景的适配效果也远超纯公开数据。没有现场数据就直接上产线,早晚会在某个钢种切换的时刻遭遇大规模误检。

2.2 标注格式和目录组织

YOLO系列统一使用txt格式的标注文件:每个图像对应一个同名txt,每一行代表一个目标框,内容是"类别id 归一化中心x 归一化中心y 归一化宽 归一化高"。

我整理了一个固定目录结构,方便后续换模型时直接复用:

defect-dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── defect.yaml

如果你手里的标注是VOC的xml或者COCO的json,需要先转成YOLO格式。下面这个Python脚本是我一直在用的转换辅助,逻辑很简单:读取xml里的obj,把xmin、ymin、xmax、ymax换算成归一化的cx、cy、w、h,宽高除以图片宽高防止越界:

import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_dir, img_w, img_h, class_names): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.findall('object'): name = obj.find('name').text if name not in class_names: continue cls_id = class_names.index(name) bbox = obj.find('bndbox') xmin = float(bbox.find('xmin').text) ymin = float(bbox.find('ymin').text) xmax = float(bbox.find('xmax').text) ymax = float(bbox.find('ymax').text) cx = ((xmin + xmax) / 2) / img_w cy = ((ymin + ymax) / 2) / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") name = os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(out_dir, name + '.txt'), 'w') as f: f.write('\n'.join(lines))

有个细节很多人会忽略:坐标系和归一化之前,必须确认图片尺寸和标注软件里的尺寸一致。我遇到过标注软件自动压缩了预览图,导出坐标和原图尺寸对不上,训练时loss一直不降,最后发现是标注坐标全偏了。

2.3 数据增强里的一个关键取舍

YOLO系列自带一套挺强大的数据增强,包括mosaic(四图拼接)、mixup、HSV扰动、随机翻转、随机平移缩放。对缺陷检测来说,mosaic对小目标尤其有效,它把小缺陷放大到整图比例,模型能学到更多上下文。

但这里有个容易踩的坑:mosaic在训练后期最好降权或关闭。因为mosaic生成的是拼接图,真实产线图片不会长成这种四宫格样子,训练到后期,模型的分布会被拼接图带偏,在正常验证图上反而发飘。我习惯把mosaic从1.0逐步降到0.3到0.5之间,或者跑到一半epoch之后关掉。具体怎么设要看loss曲线,遇到"训练loss一直降、验证mAP却不动甚至往下掉",先怀疑数据增强和真实分布的偏差。

类别不平衡也得单独处理。公开数据里斑块、裂纹样本多,麻点、氧化铁皮压入样本少,如果直接用原始分布训练,少样本类别的AP会很难看。最简单的解法是给少数类别多复制几轮(过采样),训练时人为拉平各类别样本比例。我实测过,光这一步就能让麻点和氧化铁皮压入的AP提高5个点以上。

3. YOLOv5实战训练:配置、参数和过程分析

3.1 环境准备和工程摆放

YOLOv5的依赖非常轻,核心就是PyTorch和它的一套配套库。我用的环境大概是这样:Python 3.8、PyTorch 1.13、CUDA 11.7、单张显卡显存12G。跑s模型、640分辨率、batch 16完全没压力。

数据配置文件defect.yaml是训练的第一步。文件内容不长,但每个字段都有讲究:

path: ./defect-dataset train: images/train val: images/val nc: 6 names: 0: crazing 1: inclusion 2: patches 3: pitted_surface 4: rolled-in_scale 5: scratches

path字段是数据集根目录,train和val是相对路径。nc必须和names里的类别数一致。有个低级但常见的错误:names的索引顺序和标注txt里的类别id对不上,结果模型把裂纹当成了划伤,损失函数还以为自己在收敛。

3.2 预训练权重、分辨率和训练参数

训练命令我贴一下,后面逐个参数解释:

python train.py \ --data defect.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 150 \ --patience 30 \ --hyp data/hyps/hyp.scratch-low.yaml

weights用yolov5s.pt而不是从头训练。COCO预训练权重里已经包含大量通用物体特征,迁移到钢材缺陷这种小样本数据上,收敛速度和最终精度都明显更好。工业数据集通常只有几千张,从头训纯属浪费算力。

img 640是训练分辨率。缺陷里很多是小目标,有条件可以试1280,对小缺陷检测有明显帮助,但显存和推理耗时都会涨。我的建议是640起步,先把整体流程跑通,再决定要不要升分辨率。

batch 16在显存允许时尽量大。大batch能让梯度的方向更稳定,训练曲线更平滑。epochs 150对不到两千张图的数据集来说足够了。我开了早停(patience 30),如果连续30个epoch验证集mAP没有提升就自动停止,能省掉无效训练时间。hyp选择low版本,因为钢材缺陷数据量小,增强太猛容易把模型训坏。

3.3 训练过程监控的几个观察点

训练开始后,不要盯着终端里的loss数字胡思乱想,要看三样东西:loss曲线、验证集mAP曲线、PR曲线。

一个典型过程是这样的:前30个epoch,训练loss快速下降,验证集mAP从0直接爬到0.6附近,这个阶段模型在学大局特征;到epoch 80左右,loss降速变缓,mAP在小范围内震荡,此时模型在打磨边界细节。如果这个阶段持续很久不涨,大概率不是算力不够,而是数据问题——我遇到过标注边界太松导致类别内特征发散的情况。

另外记得看每类的AP,而不是只看mAP这个总指标。我在YOLOv5s上的初步结果,六类平均mAP@0.5在0.75左右,但单看裂纹只有0.55。如果不拆开看类别,根本不会知道是裂纹拖了后腿。

3.4 遇到mAP怎么调都上不去怎么办

我最想强调的工作习惯是:先查badcase,再谈调参。

把验证集里所有置信度在0.3到0.7之间的检测框导出成图片,一张张过目,至少要过一百张。绝大多数时候你会发现,模型犯错是有规律的:裂纹被标成背景;麻点被一个更大的框错误覆盖;斑块和氧化铁皮压入被互相混认。这些规律指向的是数据问题,而不是模型问题——裂纹样本的标注边界是不是太含糊?麻点的框是不是经常把两个挨着的缺陷画成一个?

我试过花了两周调超参,最后AP只涨了一个点,而重新洗了一遍标注之后,裂纹AP直接涨了七八个点。这件事给我的教训是:先把评估集搞干净,再动模型。

4. YOLOv7、YOLOv8、YOLOv10的横向实测与选型判断

4.1 三个版本各自的核心变化

YOLOv7主打的是结构设计和训练策略的改良:E-ELAN结构让特征在不同层之间的流动更充分,重参数化卷积把训练期的复杂结构在推理期折叠成单路卷积,辅助训练头的设计让浅层也能学到更强的特征。整体思路是在保持推理速度的前提下,把精度往上抬一截。

YOLOv8则做了架构上的大调整:检测头从anchor-based改成anchor-free,不再需要预先定义锚框,每个位置直接回归目标框的四条边;主干里的C3模块换成C2f模块,特征重用更充分;检测头拆成分类和回归两个独立分支,并用DFL损失做边界回归。这些改动让它在小目标和密集场景下更稳。

YOLOv10的核心诉求是端到端无痛推理:它通过一对多和一对一联合标签分配策略,让模型推理阶段完全不需要NMS后处理,同时用轻量化分类头、空间通道解耦下采样、秩引导块设计把网络瘦身。少掉NMS那一步,延迟和省算力的效果非常实在。

做个类比:v7是在原有的发动机结构上做精细化调校;v8是重新设计了点火和燃烧方式;v10更激进,直接把变速箱摘了,让动力系统直连车轮。

4.2 同一份数据上的实测数据

为了尽量公平,我在完全相同的训练集、验证集、超参和训练策略下微调了这几个模型,输入分辨率统一640,训练150个epoch。表格里的数字是我某一次实验的记录,不同轮次会有波动,所以不要把它当绝对标准,看趋势就好:

模型参数量(M)mAP@0.5mAP@0.5:0.95单张推理耗时(ms,同GPU)
YOLOv5s7.20.750.4810.5
YOLOv736.90.790.5213.2
YOLOv8s11.20.780.518.8
YOLOv10s16.50.760.506.1

几个稳定结论:YOLOv7在这个任务上精度最高,代价是模型大、推理稍慢;YOLOv8s在精度和速度之间比较均衡;YOLOv10s推理最快,省掉NMS的额外耗时,精度没有明显崩盘,但也不是最强。值得注意的是,v10对裂纹这类纹理型缺陷的AP普遍比v8低一到两个点,可能和轻量化主干对纹理特征的提取能力下降有关。

4.3 实际项目里怎么选版本

我的选型建议按场景来分:

  • 如果做的是教学演示、快速验证,或者团队对某个框架最熟,YOLOv5s仍然是非常好的起点,资料多、坑少、周边工具齐全。
  • 如果质检工位对误检率要求高、现场的干扰物多,YOLOv7的精度优势值得你拿算力去换。算力确实贵一点,但漏一个缺陷导致的赔偿可能比一台显卡贵十倍。
  • 如果要做多路并发检测,比如一条产线同时挂着四路相机,YOLOv10的低延迟、免NMS特性可以显著降低算力成本。
  • 如果后续还想扩展分割、姿态等任务,选YOLOv8系列更顺手,整个家族的模型结构和工具链是统一的。

不要唯指标论。我之前在某个工位测试时发现,某些版本在库里AP数据很漂亮,但导出到特定推理框架后,个别算子不受支持,被迫改图重训,最后反而不如AP低零点几个点的旧版本好部署。兼容性、生态成熟度、团队熟悉度,在工业项目里的权重比那1%的mAP差距高得多。

5. 部署与落地:模型跑起来只是开始

5.1 ONNX与TensorRT导出

训练完只是第一步,产线上跑的是经过格式转换的推理版本。通用流程是:PyTorch权重先导出成ONNX,再转成TensorRT的engine格式做GPU加速。这么转一圈,推理耗时往往能再降三分之一以上,特别是在TensorRT开FP16之后。

导出时有几个参数要提前想清楚:固定输入尺寸还是动态尺寸,batch为1还是大于1。工业相机通常一次给一张图,固定尺寸、batch为1是最稳妥的方案。千万不要图省事直接开动态尺寸,个别算子会因为shape不确定而被迫退回慢速路径,加速效果大打折扣。

另外,导出后务必用同一张图对比PyTorch原模型和导出版本的输出,确认框坐标和类别没有漂移。数值精度上FP16可能让置信度偏百分之零点几,但如果框的位置变了,说明某个算子被错误替换,这时候要回头更新推理框架版本。

5.2 现场误检的来源和兜底手段

实验室里再漂亮的指标,到产线上都要重新面对现实污染。我总结过真实场景误检的几大来源:光照随车间时段变化、不同钢种表面氧化程度不同、相机镜头有灰尘和反光、钢板上有水渍油渍残留、质检员在板上留了记号笔痕。

应对手段通常分四个层次:

  • 采集现场负样本,把常见干扰物单独处理,或作为纯背景图片混入训练集参与负样本学习;
  • 在推理端适当拉高置信度阈值,宁可不报也不要每张图乱报;
  • 对同一物理位置的连续多帧输出做时间维度的投票,只有连续N帧都检出同一个位置的缺陷才对外报警,能滤掉大部分偶发误检;
  • 在上位机做规则约束,比如缺陷面积小到不构成质量影响、距带钢边缘太近就不报警,这些规则很适合过滤掉相机反光造成的边界误判。

5.3 相机、光源与整个系统的延迟预算

这其实是容易被算法工程师忽略的部分。钢材产线的带钢运动速度、检测幅宽、缺陷允许的最小尺寸,共同决定了系统的帧率和延迟预算。举个例子,如果带钢速度是3米每秒,检测幅宽1米,相机分辨率决定最小可检缺陷尺寸,那么相机触发频率、曝光时间、模型推理时间、结果回传PLC的时间加起来,必须控制在一个运动周期以内,否则缺陷即使被检测到,到达执行机构时也已经跑过了报警位置。

光源的选择同样关键:明场照明适合找划伤和麻点这类有方向性的缺陷,暗场或低角度光源适合突显压入的氧化铁皮。很多项目最后模型精度高但现场效果差,问题不在模型,而在图像采集那一端。

5.4 让模型持续进化的回流闭环

模型部署上线不是终点。我的做法是建立一套"脏数据回流"机制:每两周收集一次现场的误检和漏检图片,人工复核后补充进训练集和验证集,做一轮增量训练再替换线上模型。钢材质检的特点就是钢种和工艺会变,缺陷分布也跟着变,模型必须持续适配,否则上线三个月之后精度就会悄悄滑坡。

顺便说一个部署工程师常踩的坑:评估新模型时,直接拿新图片和旧模型的输出做对比,而不去复盘新模型为什么变了。有时候新模型改了置信度分布,把所有框的分数整体压低,导致报警策略的阈值失效。这种情况要先对齐置信度分布,再去比对检测效果,否则会被正常的分布偏移当成故障来排查。

6. 复盘:这次折腾下来最想保留的几个习惯

第一,永远先把评估集整理干净。不管是公开数据还是现场数据,标注质量决定了模型能力的天花板,模型结构和超参只是决定在天花板底下能飞多高。

第二,一切结论都要落到每类AP和badcase上。总mAP会骗人,单类AP不会。裂纹、麻点这类难例的AP,比综合指标更能反映模型能不能上产线。

第三,选型时要把部署链路当一个整体来评估。同是YOLO家族,v5、v7、v8、v10的导出兼容性和周边生态完全不同,选型不是选模型,是选一整套从数据到部署的方案。

第四,建立持续迭代的机制比任何一次训练都重要。现场数据会变,模型必须跟着变,设计好回流和重训的节奏,系统才真正能用。我最后想说的是,钢材表面缺陷检测这个任务,模型本身只占整个项目的一小半工作量。前期数据、中期选型、后期部署,每一环都是在为模型的"可落地性"打工。如果你正在做类似的项目,别急着换更先进的模型版本,先把数据和评估流程做扎实,回报会比想象中大得多。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进