去年做磁瓦缺陷检测项目时,产线上一排排弧面磁瓦从传送带送过来,质检员要在不到5秒的节拍里判断表面有没有崩边、裂纹、气孔,一个人盯一两个小时眼睛就开始花。后来我们基于YOLOv8把这套检测流程完整跑了一轮实验,从数据采集、标注规范、训练调参到部署上线,把整个链路都踩了一遍。这篇文章就把这次实验之旅的关键节点完整记录下来,适合正在做工业视觉检测、或者准备用YOLO系列模型做缺陷检测项目的朋友参考。先说明一点,文章里的实验数据来自我们自己搭建的采集环境加上部分公开的磁瓦缺陷图像,具体数字只代表这套流程下的结果,不代表所有数据集都能复现。
这趟实验做下来,我最深的体会是:模型的训练代码反而是整个项目里最简单的一环,真正的功夫全在数据定义和后续迭代上。下面按实验推进的顺序来讲,每一步都附上我当时的取舍逻辑和踩坑过程。
1. 为什么磁瓦缺陷检测选YOLOv8这趟实验?
1.1 磁瓦检测问题的特殊性
磁瓦是电机、传感器里常用的小型磁性陶瓷件,形状像一块弯曲的瓦片。表面质量直接影响装配后的噪音和寿命,常见的表面缺陷包括崩边、裂纹、气孔、起皮和氧化斑。崩边是边缘处受力磕碰导致的块状缺失,肉眼容易发现但形态极不规则;裂纹是细线状开裂,有时只有零点几毫米宽,光线稍微偏一点就看不清;气孔是烧结过程中产生的圆形小坑;起皮是表层材料小范围剥落;氧化斑则是颜色深浅不一的斑块。
这类缺陷的共同点是对比度低、形态多变。磁瓦本身是灰黑色材质,缺陷和正常表面之间的灰度差很小。裂纹在图像上往往只占几十个像素,如果检测模型的分辨率或者感受野不合适,很容易整条漏掉。人工质检虽然能适应各种缺陷,但节拍快、强度大,漏检率会随疲劳程度明显波动。
工业端需要自动检测方案,但传统机器视觉在这类场景里很别扭。我最开始试过打光后做阈值分割加形态学处理,对固定形态的崩边勉强能用,一旦遇到走向随机的裂纹或者表面反光干扰,误检率就压不住。换用深度学习目标检测之后,效果上升了一个明显的台阶,这也是我决定用YOLOv8做完整实验的根本原因。
1.2 YOLO家族里为什么选中YOLOv8
YOLOv8是Ultralytics维护的目标检测框架,发布之后把训练、验证、导出流程整合得非常顺。对比上一代YOLOv5,v8的C2f模块在同等算力下特征提取能力更强,检测头改成了解耦结构,分类和回归各走各的分支,标签分配策略也变成动态匹配,对大小目标混合的场景更友好。
实际操作中我的体验是:YOLOv5和YOLOv8在同一批磁瓦数据上各跑一轮,v8在裂纹类上的召回率提升明显。原因在于动态标签分配让细长缺陷的正样本分配更稳定,而v5的静态匹配方式容易把这类目标归成背景。YOLOv7那种重参数化结构训练和部署都更麻烦,在精度要求能被满足的前提下,我优先选择维护活跃、导出链路完善的版本。
另一个实际考虑是算力。产线部署通常是一台普通工控机加一张中等性能显卡。YOLOv8s在640分辨率下能跑到单帧30毫秒左右,如果切图推理,TensorRT加速后也能稳定在40毫秒左右,完全可以跟上产线节拍。所以后面的实验主要围绕YOLOv8s和YOLOv8m做对比。
1.3 实验目标和评估口径
这次实验要回答三个问题:第一,YOLOv8在这批磁瓦样本上能达到什么精度;第二,哪类缺陷最容易被漏掉、哪类缺陷最容易误报;第三,模型直接部署到产线需要做哪些适配。
对应地,评估指标不能只看mAP。我额外关注每个类别单独的平均精度、召回率,以及固定置信度阈值下的精确率。这一点很关键,因为mAP是把所有置信度下的表现综合起来的结果,而产线只会在一个阈值下运行。如果只盯着mAP就上线,现场很可能出现误检多到没法用的情况。后面会用表格把各模型和输入尺寸的结果汇总起来看。
2. 数据集工程的完整链路:从工业图片到可训练标注
2.1 原始样本筛选、清洗与类别平衡
数据是模型的上限,YOLOv8再强也救不了脏数据。第一批拿到的磁瓦图像里混了大量虚焦和过度曝光的照片,还有一些是同一块磁瓦从不同角度反复拍摄的高度重复样本。
我的做法是先把图像按清晰度筛一遍。用Laplacian算子的响应值做参考,低于阈值的模糊图直接淘汰。过曝和反光严重的图不全部清除,而是保留一部分作为困难样本,因为产线现场本来就可能有反光灯点。高度重复的样本按图像特征做聚类去重,避免训练集被某一块磁瓦的数据主导。
类别不平衡也必须在这个阶段处理。崩边类样本接近总量一半,裂纹和气孔明显偏少,起皮和氧化斑更少。如果不干预,训练出来的模型会对少数类视而不见,验证集上少数类AP会低得难看。我的调整策略是先按类别统计数量,再对少数类做重复采样,后续用增强手段继续补权重。
2.2 缺陷类型定义与标注规范
开始标注之前,我们先把五类缺陷的边界定义写死,避免每个人理解不一样:
- 表面裂纹:线状或树枝状开裂,长度与宽度比通常大于5;
- 崩边:边缘处块状缺失,缺失区域边界不规则;
- 气孔:近似圆形的小孔洞,直径一般小于2毫米;
- 起皮:表层材料翘起或剥落,面积较大但深度较浅;
- 氧化斑:颜色突变的斑块,边界模糊,面积不定。
概念理清之后,bbox的标法更需要统一。我用开源标注工具X-AnyLabeling完成标注,导出YOLO格式。标注规则定了三条:框必须紧贴缺陷可见部分的最外侧像素;对细长裂纹,允许一个目标只框连通域里对比度最高的主体段,不要把整条几十像素的弱边界都强行包进去;同一个缺陷如果形态上明显分成两段,按两个目标标,不强行合成一个。
这些规则不是随便定的。第一次标注时,几个人对崩边范围的理解不一致,有人把缺陷周围一圈正常表面都框进去了,有人只框掉块中心。结果训练出来的模型预测框大小分布很散,同一个崩边,有时候框很大,有时候框很小。后来我把bbox宽高比分布拉出来看,才发现是标注不一致导致的。
2.3 标注质量校验与数据集划分
标注完第一版之后,有两件事不能省。
第一件事是抽检加复标。随机抽10%的样本由另一名标注员独立标注一遍,计算两组标注框的IoU,低于0.9的图全部回炉重标。缺陷检测项目的标注质量直接影响最终mAP,这个环节省了,后面所有调参都是在脏数据上白费力气。
第二件事是划分数据集时按磁瓦实例分组,而不是按图像文件随机切。同一块磁瓦在不同角度、不同光照下拍的图内容高度相似,如果训练集和验证集里混了同一块磁瓦的不同照片,验证分数会虚高,一到现场就被现实数据打脸。我按磁瓦编号分组,保证同一个编号只在训练集或者验证集里出现一次。
最终划分比例是训练集、验证集、测试集8:1:1。测试集完全冻结,只在全部实验结束后用来确认最终结论。
2.4 数据增强:哪些该用、哪些不能用
YOLOv8默认的增强管线包括mosaic、翻转、HSV色域变换、随机缩放平移等。直接拿默认配置在磁瓦数据上跑,效果并不理想。
问题出在HSV色域增强。磁瓦正常表面和缺陷的颜色分布相对稳定,把色相、饱和度、亮度的变换幅度开太大以后,模型会学到现实中不存在的颜色组合,验证集分数看着不错,一到现场就是误检。我把增强参数调低,只保留少量亮度扰动,用来模拟产线光照抖动。
Mosaic增强对磁瓦检测是有用的,它把四张图拼在一起,让模型看到更多小目标上下文。我的做法是前180轮开启mosaic,最后20轮关闭,让模型在接近真实单图分布的数据上稳定统计量。对气孔这类数量少的小目标,我还额外做了局部贴图增强:从已知样本中把气孔区域裁剪出来,随机粘贴到没有缺陷的正常磁瓦图上,并生成对应标签。这个操作对少数类召回率提升很明显,气孔类召回率从约88%提升到了95%。粘贴时要注意避开已有缺陷区域,我在生成脚本里加了一个简单的重叠检查。
3. 训练实验配置与关键参数决策
3.1 硬件软件环境与基线实验
实验机器并不夸张,一张中端显存的GPU配合PyTorch环境,使用Ultralytics提供的YOLOv8代码仓库,数据按标准YOLO目录结构组织。我的实验配置如下所示:
yolo detect train data=magwa.yaml model=yolov8s.pt imgsz=960 epochs=200 batch=8 optimizer=SGD lr0=0.01我先跑了两组基线:YOLOv8n和YOLOv8s,输入分辨率640,训练200个epoch,batch size 16。YOLOv8n的mAP50在0.942左右,s版本在0.965左右,精度差距真实存在。考虑到产线要求,后面把s作为主力模型,m作为候选对比。
小目标加细长缺陷的任务里,输入分辨率是一个关键杠杆。YOLOv8默认常用640,我把分辨率拉到960重新训练,裂纹召回率从88.7%提高到93.2%,代价是训练耗时增加、推理时间变长。960的整图直接输入显存压力大,训练时把batch size减半,部署时则采用切图推理,这个后面细说。
3.2 优化器、学习率与训练策略
YOLOv8默认优化器是SGD,动量0.937,初始学习率0.01。我在磁瓦数据上对比了AdamW,发现AdamW前期收敛更快,但后期mAP50-95略低于SGD。最终采用SGD加余弦退火学习率,初始学习率0.01,配合前3个epoch的warmup。这个组合给我的经验是:数据量不大、分类任务相对集中时,SGD的最终精度更稳,AdamW更适合大模型和大数据场景。
早停和最优权重保存也要设置好。我设了patience=30,验证集mAP50连续30个epoch不涨就停止。实际训练通常跑到160到180个epoch触发早停,重复训练得到的结果基本一致,说明训练稳定性可以接受。
显存受限时大家习惯调小batch size,但我提醒自己不能把batch调太小,否则BN统计量会抖动。960输入下我用batch size 8,并开启了子batch训练参数,把输入图切成多个子图块喂进去,平衡了显存占用和统计稳定性。
3.3 训练过程中的几个观察点
训练中途我习惯盯三类曲线:loss曲线、每类别的mAP曲线、以及验证集上预测框的宽高分布。
loss曲线从3左右降到0.6附近时,第一轮实验就停了。但只看总loss不够,把每个类别的AP单独拉出来才发现,氧化斑和裂纹明显偏低。可视化预测结果之后,发现原因是这两个类别内部的目标尺度变化太大,有的裂纹短而粗,有的细而长,模型要同时兼顾两种形态很吃力。
另一个观察是验证集出现虚高。用默认增强训练出的模型在验证集上mAP50能到0.97,但打开badcase时发现,模型把一些正常磁瓦表面的灰暗区域当成了气孔。这个误检就是过度颜色增强的直接体现。关闭过强的HSV参数之后,验证分数略降,但badcase明显变少。分数不是越高越好,必须结合badcase去理解。
3.4 最终模型配置汇总
最终采用的训练配置整理成表格,方便复现:
| 配置项 | 设定值 |
|---|---|
| 模型 | YOLOv8s |
| 输入分辨率 | 960×960 |
| 优化器 | SGD,初始学习率0.01,动量0.937 |
| 学习率策略 | warmup 3轮 + 余弦退火 |
| 训练epochs | 200,早停patience 30 |
| batch size | 8 |
| 数据增强 | mosaic前180轮开启后20轮关闭,HSV低幅度,局部贴图增强 |
这个配置跑出来的模型,在部署和测试集上表现稳定,没有出现明显的训练测试偏差。
4. 结果分析:从mAP到产线真正关心的指标
4.1 先看mAP,再看精确率和召回率
模型训练完成后,验证集上YOLOv8s的mAP50达到0.973,mAP50-95约0.887,分数确实漂亮。但产线负责人不关心这两个数字,他只问两件事:会不会漏掉坏件,会不会把好件拦下来。
漏检对应召回率,误检对应精确率,这两个指标都依赖同一个置信度阈值。我把验证集上预测结果的PR曲线画出来,尝试在精确率和召回率之间找到平衡点,最终选定置信度0.45。此时整体精确率96.3%,召回率95.8%,意味着每100个真实缺陷大约漏4个,每100次报警里约4次是误报,整体可接受。
不同类别的表现差异才是关键,汇总如下表:
| 缺陷类别 | 召回率 | 精确率 |
|---|---|---|
| 崩边 | 99.1% | 97.0% |
| 表面裂纹 | 93.5% | 94.2% |
| 气孔 | 95.6% | 96.8% |
| 起皮 | 94.8% | 95.1% |
| 氧化斑 | 91.7% | 92.6% |
裂纹和氧化斑是短板,接下来重点分析这两块的badcase。
4.2 误检与漏检:两类badcase的完整排查
漏检集中在裂纹上。我把所有漏掉的正样本拉出来,发现两种典型场景。一是裂纹对比度极低,和磁瓦表面的灰暗纹理混在一起,人眼都要仔细看才分得清,模型预测置信度低于阈值。二是裂纹两头颜色逐渐变淡,模型只预测出中间高置信度段,低置信度的延伸段被NMS过滤或者直接判为背景。
这两类漏检不好靠调阈值解决。阈值调低,裂纹召回率会上去,但氧化斑的误检同步增加。我尝试在训练集里给低对比度裂纹标注得更完整,同时往数据里加入对比度拉伸的图像,让模型见到更多低对比度裂纹样本,最终把裂纹召回率从88.7%提到93.5%。
误检集中在氧化斑和起皮。氧化斑误检的典型画像,是磁瓦表面有轻微反光形成一块高亮度区域,模型把它当成了颜色突变斑。起皮误检则来自图像失焦产生的模糊纹理。这类badcase说明,数据集里的正常样本也要拍够,并且要覆盖不同光照、不同摆放角度,否则模型分不清真实缺陷和光线伪影。
4.3 置信度阈值怎么定才不容易翻车
我定阈值时没有直接取验证集最优,而是专门留了一块独立测试集。在测试集上把0.3到0.6的阈值都跑一遍,画出漏检数量和误检数量的对应变化曲线,然后让生产侧的人一起看图。生产侧确认能接受多高的漏检率、能容忍多大的误检率,再反推出阈值。
最终选0.45,这个阈值在测试集上的表现和验证集基本一致,没有出现分数崩塌。这里有个经验:阈值不要定死在模型交付那一刻,至少要留一个可配置的接口。产线更换光源、增加新缺陷类型或者改变相机角度之后,阈值大概率要重新调,这个动作比重训模型成本低得多,效果却是立竿见影的。
5. 从模型文件到产线相机:部署与迭代环节的实战细节
5.1 模型导出、切片推理与加速
训练好的YOLOv8s权重需要导出成ONNX,再转成TensorRT engine。导出时我把输入尺寸固定为960×960,opset保持较高版本。YOLOv8的导出接口非常顺滑,一条命令就能完成:
yolo export model=best.pt format=onnx imgsz=960 opset=17但实际部署时还有个关键问题。960×960直接输入模型,对一块完整磁瓦来说分辨率仍然不足,细裂纹在整图里占的比例太小。我的方案是切片推理:把磁瓦ROI按2乘2切成四块,每块约640×640并且带重叠区域,分别送进模型做检测,最后合并NMS结果。这个方案让小目标的实际有效像素翻倍,裂纹召回率又提升了约2个百分点。代价是推理次数变成四倍,TensorRT加速后整块磁瓦的总耗时约40毫秒,在产线节拍内可以接受。
FP16精度下模型精度几乎不掉。INT8量化我也试过,崩边这类边缘明显的缺陷不受影响,但裂纹这种低对比度细长线目标偶尔会丢失,为了稳妥最终选了FP16。
5.2 产线环境的干扰与稳定性处理
实验室验证做得再好,一上产线还是会遇到新问题。第一次现场测试时,磁瓦位置有轻微偏移,导致ROI切割不准确,裂纹漏检突然增多。后来加了基于轮廓的磁瓦定位模块,先把磁瓦从背景里分割出来,再做旋转校正,保证切割区域稳定。这一步在产线部署里比算法本身还重要。
光照是另一个大变量。现场日光灯频闪、光源角度偏差会让磁瓦表面出现渐变亮度,模型对亮度变化比想象中敏感。我加了一层亮度归一化预处理,按磁瓦表面均值和方差做标准化,实测误检率下降了约两个百分点。
部署后还要持续观察模型输出的置信度分布。我每两周拉一次统计,如果中低置信度区间的目标比例持续上升,说明数据分布已经漂移,该考虑增量采集和重训了。
5.3 数据闭环:难例回流与周期性重训
模型上线不是终点,而是下一个版本数据的起点。我设计了很简单的回流流程:产线每天把报警图片自动保存下来,同时按一定比例随机截留无报警的正常样本。每两周由标注人员对新增样本做标注,重点标记两类图片——模型报错的,以及模型没报出来的。
这些难例合并进原始训练集,做增量训练。增量训练不是从头来,是从现有最优权重继续训练,学习率调低,跑30到50个epoch就够。我保持每四周重训一次,模型效果稳步提升,氧化斑经过两轮迭代后召回率从91.7%提升到94.3%。
这个闭环里最容易出问题的还是标注一致性。新增标注人员没仔细看旧规范,或者对同一缺陷理解有偏差,会把上一个版本学好的边界又带偏。所以每轮重训前,我都会用旧模型去预测新标注数据,把标注框与预测框差异大的样本抽出来人工确认。这个动作比调任何超参数都值钱。
整套实验跑下来,我最大的感受是:YOLOv8本身的门槛已经很低,网上随便一个教程都能教你训练出一个模型。真正决定磁瓦缺陷检测项目能不能落地的,是数据和问题定义这两件事。数据侧要较真到标注一致性、类别平衡、划分泄漏这些细节;问题定义侧要搞清楚产线到底怕漏检还是怕误检、节拍允许多少毫秒、光照条件是稳定还是漂移。如果你也在做类似的缺陷检测项目,我建议先用最朴素的方案把数据链路跑通,再回来折腾模型结构和阈值策略。把这些想清楚,YOLOv8会是一个相当可靠的工业检测伙伴。