简介:在工业质检领域,钢材表面缺陷识别长期依赖人工目检,效率与稳定性难以兼顾。机器视觉与目标检测技术的成熟,为产线自动化质检提供了新路径。YOLOv5作为单阶段检测模型的代表,凭借其推理速度快、工程生态完善的特性,成为工业视觉落地的热门选择。其基于CSPDarknet的特征提取结构与PANet多尺度融合机制,能够有效平衡精度与实时性。通过NEU-DET公开数据集对裂纹、夹杂、斑块等六类典型缺陷进行模型训练与调优,并配合数据增强、锚框优化及ONNX导出等手段,可实现毫秒级推理的在线检测系统。该方案不仅适用于钢材产线,亦可迁移至其他表面缺陷检测场景,为智能制造的品质管控提供可靠技术支撑。本文围绕这套完整项目,梳理从环境搭建、格式转换到参数调优的实操要点。 质量检测在制造业里一直是块硬骨头,尤其是钢材这种连续生产线上的表面缺陷。传统人工目检容易疲劳漏检,机器视觉加传统图像处理又对光照和环境变化过于敏感,换个产线就要重新调参。用YOLOv5这类目标检测模型来做钢材表面缺陷识别,属于目前工业视觉落地里性价比很高的方案——数据集相对成熟,模型推理速度快,单张图片在普通GPU上就能跑到毫秒级,而且源码生态完善,从训练到部署都有现成轮子。这个标题里的压缩包,其实就是一套完整的“数据集+训练代码+检测系统”的组合,适合刚接触工业视觉的算法工程师、相关专业的研究生,以及想在产线上试点AI质检的自动化工程师参考。
我拿到的这套项目,核心内容是围绕NEU-DET这个公开的钢材表面缺陷数据集展开的,六个典型缺陷类别:裂纹、夹杂、斑块、麻点、氧化铁皮压入和划痕。整套系统把数据准备、模型训练、指标评估和推理检测串成了一条完整的链路。这篇文章我会按照实际做项目的顺序,把从数据集处理到模型部署的完整过程拆开讲清楚,包括环境搭建的坑、训练参数的调优思路、以及我在跑这个项目时踩过的几个典型问题。
1. 项目整体设计与思路拆解
1.1 为什么选YOLOv5做钢材缺陷检测
钢材表面缺陷检测这个场景,对模型有几个硬性要求:第一是实时性,产线传送带速度很快,检测必须跟得上节奏;第二是误检漏检率要低,这直接影响出厂质量评级;第三是模型要足够鲁棒,产线光照变化、钢材表面反光、氧化皮颜色差异都是干扰因素。
YOLOv5在这几个维度上表现比较均衡。相比Faster R-CNN这类两阶段检测器,YOLOv5的单阶段架构天然在推理速度上有优势;相比同系列更早的YOLOv3/YOLOv4,YOLOv5在工程化方面做得更完善——数据增强策略内置了Mosaic和Copy-paste,训练过程自动锚框计算,导出ONNX/TensorRT也都有现成脚本,这对工业落地非常友好。
另一个很实际的原因是社区生态。YOLOv5的教程、预训练权重、部署方案在开源社区里非常丰富,遇到问题基本都能搜到解决方案。对于工业项目来说,可维护性和可复现性往往比模型精度指标更要紧。
1.2 六个缺陷类别的定义与识别难点
NEU-DET数据集包含六类典型缺陷,每类大概300张图片,总共1800张。我在标注和生产验证中,对每个类别都做了细致分析:
| 缺陷类别 | 英文标识 | 形态特征 | 识别难点 |
|---|---|---|---|
| 裂纹 | Crazing | 网状细纹,方向随机 | 对比度低,容易和背景纹理混淆 |
| 夹杂 | Inclusion | 颗粒状异物,颜色偏深 | 目标小,密集出现时容易漏检 |
| 斑块 | Patches | 大面积不均匀区域,边界模糊 | 形状不规则,与背景灰度接近 |
| 麻点 | Pitted Surface | 点状凹坑,呈簇状分布 | 目标小且密,边缘特征弱 |
| 氧化铁皮压入 | Rolled-in Scale | 鳞片状,颜色深浅不一 | 与背景颜色相近,标注难度大 |
| 划痕 | Scratches | 细长线性纹路 | 长宽比极端,常规锚框难以匹配 |
这六类缺陷里,划痕和裂纹是最难处理的。划痕是典型的长条状目标,长宽比可能达到10:1以上,YOLO默认的锚框尺寸对这种极端形状不太友好,需要额外调整。裂纹则是细线状纹理,在低分辨率下很容易被下采样过程抹掉特征。
1.3 系统的整体架构设计
这套检测系统按功能划分为四个模块:数据处理模块负责把原始图片和标注文件转换为YOLO格式,并完成训练集/验证集划分;模型训练模块封装了YOLOv5的训练入口和超参数配置;评估模块输出mAP、精确率、召回率等核心指标,并生成混淆矩阵;推理检测模块支持单张图片、视频流和批量文件夹三种输入方式。
这种模块划分方式在实际项目中很实用。数据、训练、评估、推理四个环节解耦之后,无论是调试单独某个环节,还是后续替换更先进的模型架构,都只需要改动对应模块,无需重构整个系统。
2. 核心内容拆解与实操要点
2.1 数据集的下载与标注格式转换
NEU-DET数据集的原始标注格式是VOC格式的XML文件,而YOLOv5训练需要的是TXT格式的归一化坐标标注。这一步格式转换是很多新手第一个卡住的地方,其实逻辑很简单:VOC格式存的是目标框的左上角和右下角绝对像素坐标,YOLO格式存的是中心点坐标和宽高占图片宽高的比例。
转换脚本的核心逻辑如下:
import xml.etree.ElementTree as ET import os def convert_voc_to_yolo(xml_path, out_dir, class_names): tree = ET.parse(xml_path) root = tree.getroot() img_w = int(root.find('size').find('width').text) img_h = int(root.find('size').find('height').text) lines = [] for obj in root.iter('object'): cls_name = obj.find('name').text if cls_name not in class_names: continue cls_id = class_names.index(cls_name) box = obj.find('bndbox') x_min = float(box.find('xmin').text) y_min = float(box.find('ymin').text) x_max = float(box.find('xmax').text) y_max = float(box.find('ymax').text) # 转换成YOLO格式 x_center = (x_min + x_max) / 2 / img_w y_center = (y_min + y_max) / 2 / img_h w = (x_max - x_min) / img_w h = (y_max - y_min) / img_h lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") xml_name = os.path.basename(xml_path).replace('.xml', '.txt') with open(os.path.join(out_dir, xml_name), 'w') as f: f.write('\n'.join(lines))这里要注意一点:NEU-DET原始图片是200x200像素的小图。这个分辨率对YOLOv5来说偏低,因为模型的默认输入尺寸是640x640,直接把小图放大到640会损失很多细节,尤其是裂纹和划痕这类细线特征。后面我会细讲针对这个问题的处理方案。
2.2 数据集划分与目录结构
训练集、验证集、测试集的划分比例我建议用8:1:1。1800张图按这个比例划分,训练集1440张,验证集和测试集各180张。划分时要注意用随机打乱的方式,避免同一生产批次或相近样本集中在某个集合里。
YOLOv5的目录结构官方推荐如下:
datasets/ └── steel_defect/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/数据集准备好后,还需要在项目里创建data.yaml配置文件:
train: datasets/steel_defect/images/train val: datasets/steel_defect/images/val test: datasets/steel_defect/images/test nc: 6 names: ['crazing', 'inclusion', 'patches', 'pitted_surface', 'rolled-in_scale', 'scratches']2.3 模型配置文件调整
YOLOv5的模型结构通过YAML文件定义。官方默认有n、s、m、l、x五个尺寸,从n到x参数量和精度逐渐增大。对于钢材缺陷检测这个任务,我的经验是从YOLOv5s开始,如果精度不够再往上换。
但在修改模型配置文件时,有一个关键点很多人会忽略:类别数。默认的coco.yaml里nc是80,必须改成6。修改方法是直接编辑models/yolov5s.yaml,把nc字段从80改成6。如果忘记改,训练会在类别数不匹配的地方报错。
YOLOv5s的模型结构主要由三部分组成:CSPDarknet骨干网络负责提取特征,PANet颈部负责多尺度特征融合,YOLO检测头输出三个不同尺度上的预测结果。针对钢材缺陷小目标多、细长目标多的特点,可以重点关注小尺度特征图上的输出。
2.4 关键训练参数的选择与理解
训练参数直接决定模型的收敛质量和最终效果。我常用的参数配置如下:
| 参数名 | 推荐值 | 说明 |
|---|---|---|
| img-size | 640 | 输入图像尺寸,小图需配合上采样策略 |
| batch-size | 16 | 根据显存调整,显存不够就降到8 |
| epochs | 100-200 | 数据集小,训练轮次可以适当增加 |
| lr0 | 0.01 | 初始学习率,可以用0.001更稳 |
| optimizer | SGD | 小数据集上SGD比Adam更稳 |
| workers | 4-8 | 数据加载线程数,Windows下建议0 |
| patience | 50 | 早停耐心值,防止过拟合 |
| weights | yolov5s.pt | 预训练权重,迁移学习的关键 |
训练命令:
python train.py \ --data datasets/steel_defect/data.yaml \ --cfg models/yolov5s.yaml \ --weights yolov5s.pt \ --batch-size 16 \ --epochs 150 \ --img-size 640 \ --name steel_defect_run1这里用到预训练权重yolov5s.pt非常关键。官方在COCO数据集上预训练过的模型已经学习到了丰富的通用特征,在小数据集上微调比从头训练收敛快得多,精度也更高。我在测试中发现,用预训练权重从零训练,同样的epoch数下mAP能高出5-8个百分点。
2.5 数据增强策略对缺陷检测的影响
YOLOv5内置了丰富的数据增强策略,包括Mosaic、Copy-paste、随机仿射变换、HSV色域变换、水平翻转等。默认配置对自然场景很有效,但钢材缺陷有它的特殊性。
Mosaic增强是YOLOv5的招牌策略,把四张图拼成一张训练,相当于扩大了batch size,还能增加目标尺度的多样性。但钢材缺陷这种小图数据集,Mosaic拼图时会进一步缩小每个目标的尺寸,反而不利。实践中我把Mosaic关闭了,改为使用RandomPerspective和HSV增强。
HSV色域增强对钢材表面检测很有帮助。因为不同产线、不同批次的钢材,表面颜色和光照差异很大。通过在HSV空间做随机扰动,模型对这些颜色变化会更鲁棒。注意不要增强过度,否则真实场景下会误检。
# hyp.scratch-low.yaml 部分调整 mosaic: 0.0 # 关闭Mosaic mixup: 0.0 # 关闭Mixup hsv_h: 0.015 # 色调扰动 hsv_s: 0.7 # 饱和度扰动 hsv_v: 0.4 # 明度扰动 degrees: 10.0 # 小幅旋转 translate: 0.1 # 平移 scale: 0.5 # 缩放3. 实操过程与核心实现
3.1 环境搭建与依赖安装
YOLOv5的官方环境要求是Python 3.8+和PyTorch 1.8+。我实测下来,PyTorch 2.0以上版本也完全兼容,而且推理速度会快一些。建议用conda创建独立环境,避免污染系统Python:
conda create -n yolov5 python=3.9 conda activate yolov5 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118然后克隆YOLOv5仓库并安装依赖:
git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt这里有个容易踩的坑:如果提示import torch失败,先检查CUDA是否装好,可以用python -c "import torch; print(torch.cuda.is_available())"验证。如果返回False,说明PyTorch装的是CPU版本,需要重新安装对应CUDA版本的PyTorch。
3.2 小图高分辨率的处理策略
前面提到NEU-DET原始图片只有200x200,直接放大到640x640训练会损失细节。这个问题我当时纠结了很久,测试了三种方案:
第一种方案是把图片放大到640输入,再配合锐化滤波增强边缘。放大的方法建议用cv2.resize加上cv2.INTER_CUBIC插值,比默认的线性插值在纹理保持上更好。
第二种方案是把图片放大到416或480。这个思路是减少放大倍数,保留更多原始信息,代价是目标在图上占的面积比例更接近自然场景。我拿480测试过,效果比640稍差一点,但推理速度更快。
第三种方案是用滑窗裁剪。把小图切成重叠的patch,每个patch作为一个独立样本。这其实是工业场景常用的做法,相当于用空间换精度。但训练和推理都要额外处理patch拼接逻辑,工程复杂度上升。
最终我选的是第一种方案,并做了增强:先把200x200放大到640x640,然后在训练时用较小的min-iou锚框参数(-1表示自动)让模型更容易匹配小目标。如果你跑出来的模型对小目标漏检多,可以试试把锚框的iou阈值调低到0.2:
python train.py \ --data datasets/steel_defect/data.yaml \ --cfg models/yolov5s.yaml \ --weights yolov5s.pt \ --batch-size 16 \ --epochs 150 \ --img-size 640 \ --anchor-min-iou 0.23.3 训练过程的监控与指标解读
训练开始后,终端会实时打印每个epoch的loss、精度、召回率和mAP指标。如果安装了wandb,还可以网页端可视化管理训练曲线。
重点要看的几个指标:
- box_loss:目标框回归损失,反映框定位精度,越低越好
- obj_loss:目标置信度损失,反映是否存在目标的判断能力
- cls_loss:分类损失,反映类别判断能力
- mAP@0.5:IoU阈值0.5时的平均精度均值,工业界最看重的指标
- mAP@0.5:0.95:更严格的评估,IoU从0.5到0.95取平均
我跑完150个epoch后,测试集上的结果大致在mAP@0.5=0.75-0.82这个区间。不同类别的精度差异挺大,其中划痕的AP相对较低,原因和前面分析的一样,细长目标对锚框和NMS都不太友好。
3.4 模型评估与混淆矩阵分析
训练结束后,用val.py做模型评估,会生成混淆矩阵、PR曲线、F1曲线等报告:
python val.py \ --data datasets/steel_defect/data.yaml \ --weights runs/train/steel_defect_run1/weights/best.pt \ --img-size 640 \ --conf-thres 0.25 \ --iou-thres 0.45混淆矩阵能直观反映哪些类别之间容易互相混淆。我跑出来的结果显示,斑块和氧化铁皮压入之间存在一定的混淆,因为两者都表现为大面积的灰度异常区域,边界不明显。如果出现这种问题,可以考虑在数据层面增加难例挖掘,或者给易混淆类别单独调权重。
3.5 推理检测与结果可视化
训练好的模型可以直接用detect.py做推理:
python detect.py \ --weights runs/train/steel_defect_run1/weights/best.pt \ --source datasets/steel_defect/images/test \ --img-size 640 \ --conf-thres 0.25 \ --iou-thres 0.45 \ --save-txt \ --save-conf推理结果默认保存在runs/detect/exp目录下。加上--save-txt可以把检测框坐标保存成TXT文件,方便后续自动化流程读取。--save-conf会顺便保存置信度分数。
如果想把模型集成到自己的Web系统或上位机程序里,推荐用YOLOv5的detect.py做原型,但正式环境建议直接用PyTorch的torch.hub加载权重:
import torch model = torch.hub.load('ultralytics/yolov5', 'custom', path='runs/train/steel_defect_run1/weights/best.pt') model.conf = 0.25 model.iou = 0.45 model.classes = [0, 1, 2, 3, 4, 5] results = model('钢铁表面图片.jpg') results.show() results.print()3.6 导出为ONNX加速推理
工业部署场景下,很多环境没有PyTorch,或者需要在边缘设备上跑推理。这时候把模型导出为ONNX格式就很有必要了:
python export.py \ --weights runs/train/steel_defect_run1/weights/best.pt \ --img-size 640 \ --batch-size 1 \ --include onnxONNX模型可以用ONNX Runtime或者OpenVINO加载推理,也可以进一步转成TensorRT的engine模型,在NVIDIA GPU上获得数倍加速。我实测在Jetson设备上,TensorRT优化后的推理速度能达到毫秒级,完全满足产线实时性要求。
4. 常见问题与排查技巧实录
4.1 类别不平衡导致部分缺陷检测效果差
NEU-DET数据集中,六类缺陷各300张图,类别数量基本平衡。但每个类别内部的目标数量和大小差异很大,有些图只有一个大目标,有些图有几十个小目标。这会导致模型对多目标小尺寸的类别(如麻点、夹杂)学习不充分。
解决思路有两个:一是用YOLOv5的类别权重功能,在train.py里通过--cls参数设置类别损失权重,给样本少的类别更高的权重;二是做数据增强时增加复制粘贴策略,把目标数少的类别复制到其它图中,人为增加该类目标的出现频率。
4.2 训练时loss不下降或直接爆掉
这种情况我遇到过一次,原因是学习率设置过高。YOLOv5默认初始学习率0.01,在小数据集上有时不够稳。如果loss曲线是横向震荡或直接NAN,先把学习率降到0.001,然后观察前10个epoch的loss变化。另外确认数据标注格式是否正确,尤其是坐标归一化后是否有值超过0-1范围。
4.3 检测时漏检或误检率偏高
漏检和误检是工业质检项目里最头疼的两个问题,几乎不可能一次性调好。漏检主要发生在小目标和对比度低的缺陷上,对策是降低置信度阈值、调整锚框、增加小目标样本。误检则往往是因为背景复杂,模型把钢材表面的纹路误判为缺陷,对策是增加难负样本、提高置信度阈值、做消融分析。
我给一个经验值:如果检测场景对误检容忍度低,把conf-thres设到0.35-0.45;如果对漏检容忍度低,把conf-thres设到0.15-0.25。实际阈值需要用一批真实生产样本做验证集来标定,不要直接套默认值。
4.4 Windows环境下训练速度慢的问题
Windows下训练YOLOv5经常比Linux慢一大截,主要原因是DataLoader的多线程支持问题。workers参数在Windows下如果设置过大,反而会因为进程调度开销导致速度下降,建议直接设为0。另外建议开启--cache参数把数据提前缓存到内存中,减少磁盘I/O等待。
python train.py \ --data datasets/steel_defect/data.yaml \ --cfg models/yolov5s.yaml \ --weights yolov5s.pt \ --batch-size 16 \ --epochs 150 \ --img-size 640 \ --workers 0 \ --cache4.5 细长目标漏检的针对性优化
划痕这类细长目标,YOLOv5默认的锚框很难匹配。我在实际调优中用了两个有效手段:
第一个是调低NMS的IoU阈值。默认0.45对密集细长目标偏严,会抑制相邻的检测框,可以调到0.3左右。这个可以在val.py和detect.py中用--iou-thres参数调整。
第二个是修改模型配置文件中的锚框。YOLOv5支持--anchor-gen给自定义数据集重新计算锚框,所以训练前可以加这个参数让模型自动适配数据:
python train.py \ --data datasets/steel_defect/data.yaml \ --cfg models/yolov5s.yaml \ --weights yolov5s.pt \ --batch-size 16 \ --epochs 150 \ --img-size 640 \ --anchor-gen4.6 模型过拟合的处理
数据集只有1440张训练图,模型很容易过拟合。判断标准是训练集loss持续下降但验证集mAP不再提升甚至下降。处理方法优先级从高到低:早停法(patience参数);数据增强更强一些,特别是HSV扰动和翻转;用正则化更强的SGD配合weight_decay=0.0005;减小模型复杂度,从YOLOv5s降到YOLOv5n;最后才考虑用更多数据。
我个人在实际跑这个项目时,最深的体会是:工业检测项目,数据质量比模型结构重要得多。YOLOv5作为基线模型已经足够强了,很多时候精度上不去,问题不在模型,而在数据的标注一致性、场景覆盖度、难例样本的丰富度上。如果你也想做类似的项目,建议先把手里的缺陷图片整理透彻,把每类缺陷的形态变化都覆盖到,再去调模型和参数,这条路会顺很多。
从这套源码出发,后续还可以往几个方向扩展:一是部署到边缘计算设备,用TensorRT或OpenVINO做推理加速,接入产线实时视频流;二是用YOLOv5的模型蒸馏或剪枝能力,压缩模型体积,放进更小的嵌入式设备;三是把检测结果和生产管理系统打通,自动统计缺陷率并生成报表。这些扩展都有现成的开源方案可以组合,难度是逐步递增的,但底子就是这套训练检测系统本身。
本文还有配套的精品资源,点击获取