☰
基于yolov5的钢材表面缺陷检测与产线部署全流程解析
2026/10/15 11:47:57 网站建设 项目流程

简介:面向钢材表面缺陷检测场景的YOLOv5工程资源包,将深度学习目标检测算法与真实工业标注数据融为一体,既适合计算机视觉学习者系统理解YOLOv5原理,也适合工业视觉工程师在产线质检项目上做算法选型与验证。压缩包共2000个文件,整体约501MB,核心内容是1971张缺陷jpg原图及配套txt、xml标注文件,覆盖裂纹、凹坑、氧化皮、划痕等典型缺陷类型;另含Python训练脚本、pt预训练权重、yaml参数配置与onnx导出模型,形成从数据组织、模型训练、验证评估到推理导出的完整流程。目前已有2503人学习下载,包内保留YOLOv5原生工程目录,提供数据集划分、模型结构定义、训练入口等关键脚本,内置翻转、裁剪、缩放等多尺度训练策略;对照源码可理解CSPNet、SPP、FPN、PANet等模块的作用,也可替换成自有钢材表面数据做迁移学习与调参。相比零散代码,这份整合包省去大量环境适配与格式对齐工作,适合毕业设计、课题复现及企业技术预研等场景。

1. 钢材表面缺陷检测:yolov5 从训练到产线上线的完整路径

某条热轧带钢产线上,一个指甲盖大小的氧化皮压入缺陷就能让整卷钢材降级,而钢板以每秒十几米的速度掠过,肉眼根本来不及看全。用 yolov5 配合钢材表面缺陷数据集做检测,是工业视觉里最直接的解法:这个项目把六类常见缺陷的识别、定位、置信度输出一条龙打通,从数据划分、模型微调到 ONNX 导出,每一步都有现成代码可抄。不管你是要做产线质检的工程师,还是想学目标检测在真实场景落地的开发者,都能沿这套流程把模型跑起来,再按自己现场的情况做二次改造。这里要拆的就是这样一个项目。

2. 项目架构与数据集:从缺陷样本到检测模型的完整链路

2.1 钢材缺陷数据集的构成与标注规范

钢材表面缺陷数据集覆盖热轧带钢产线上最典型的六类缺陷:氧化铁皮压入(rolled-in scale)、划伤(scratches)、麻点(pitted surface)、夹杂(inclusion)、斑块(patches)和轧制氧化皮(oxidation scale)。各类缺陷在形态上差异明显——氧化铁皮压入是大面积的不规则片状区域,划伤是细长的线性纹理,麻点是密集的小圆斑。类别间区分度大,对目标检测模型来说是好事,模型不需要做太精细的语义边界划分。真正增加难度的,是钢板表面本身的纹理噪声和光照不均,会让不少缺陷和背景的边界变得模糊,标注和检测都容易被干扰。

整个数据集规模在千张量级,单张图像分辨率不高,大约 200 像素见方。这个体量直接训练一个目标检测模型是不够的,所以迁移学习是必然选择:用 yolov5 在 COCO 上预训练好的权重初始化骨干网络,再在这个小数据集上做全参微调,收敛速度和最终精度都远好于从零训练。这也是我遇到小数据集时的默认套路。

标注格式是 YOLO 标准的 txt 文件,每张图片对应一个同名 txt,每一行描述一个缺陷目标:类别 ID、归一化中心点 x、归一化中心点 y、归一化宽 w、归一化高 h。这种格式的好处是直接对齐 yolov5 训练脚本的数据加载逻辑,不需要额外写转换代码。但拿到数据后别急着开训,我习惯先写一个小脚本把标注整体过一遍,筛掉坐标越界、宽高为负、类别 ID 越界的脏数据,顺手统计每类缺陷的实例数量分布,后面调损失权重和增强策略都要用这个数。

2.2 技术选型:为什么是 yolov5

目标检测框架可选的不算少,Faster R-CNN 精度高但推理速度在产线场景很难达标,SSD 推理快但小目标检测能力偏弱,yolov5 在工业落地上下文里几乎是默认选项。我的判断标准是看三件事:训练到部署的链路是否完整、模型规格是否灵活、内置增强是否够用。yolov5 在这三方面都做得相当顺手。

第一,官方仓库里 train.py、detect.py、export.py 一条龙齐全,从数据集配置到模型导出不需要自己拼装管线。第二,n/s/m/l/x 五个规格覆盖了从端侧实时检测到离线高精度抽检的所有档位,产线实时场景用 n 或 s,离线质检可以用 m 甚至 l。第三,内置数据增强丰富,mosaic 四图拼接、HSV 色域扰动、随机翻转、随机缩放默认开启,省掉了不少自己写增强代码的功夫。这个项目的定位是落地,不是刷榜,yolov5 的工程便利性刚好匹配。

yolov5 也有短板,主要集中在两个地方:小目标检测和类别不平衡处理,钢材缺陷场景偏偏两个都碰上了。选型时要提前有预期,后面避坑章节会展开讲这两个问题怎么解决。简单说,yolov5 是工程优先,学术上限不是它的卖点,但配合数据层面的策略调整,在钢材缺陷这个场景足够用。

2.3 项目目录结构与模块划分

标准的 yolov5 仓库加上自己补充的数据处理脚本,整理后的目录结构如下:

steel_defect_detection/ ├── data/ │ ├── steel_defect.yaml # 数据集配置:路径、类别数、类别名 │ └── hyps/ │ └── hyp.steel.yaml # 定制超参数:类别权重、增强强度 ├── models/ │ ├── yolov5s.yaml # 标准模型结构定义 │ └── yolov5s6.yaml # P6 结构,针对小目标优化 ├── scripts/ │ ├── check_annotations.py # 标注质量检查 │ ├── split_dataset.py # 分层划分训练/验证集 │ └── analyze_bbox.py # 标注框尺寸分布统计 ├── runs/ │ ├── train/ # 训练日志、权重、指标曲线 │ └── detect/ # 推理结果输出 ├── train.py # 训练入口 ├── detect.py # 推理入口 └── export.py # 模型导出

其中最关键的是 data/steel_defect.yaml 和 scripts 下的三个脚本。yaml 文件定义了训练和验证集路径、类别数量和类别名列表,模型的输出维度直接依赖它。scripts 里三个脚本不参与训练,但在数据处理阶段几乎必用:check_annotations.py 排查标注脏数据,split_dataset.py 做分层划分,analyze_bbox.py 统计标注框尺寸分布,为后面重聚类 anchor 提供依据。runs 目录是 yolov5 自动生成的,训练后的 best.pt、last.pt 权重、TensorBoard 日志、验证曲线全部在里面,推理结果也会输出到 runs/detect 下。

3. 核心实现拆解:数据管线、训练配置与推理输出

3.1 数据预处理:分层划分与增强策略

拿到的原始数据集通常是平铺目录,图像和标注在同一层。第一步不是直接开训,而是先把样本按 8:2 切成训练集和验证集。直接按目录顺序切是坑——原始数据往往按同一批次、同一卷钢排列,顺序切分会让训练集和验证集分布重叠,验证指标虚高。我一般按图像级类别标签做分层采样:

# scripts/split_dataset.py from pathlib import Path from collections import defaultdict import random labels_dir = Path('labels') image_labels = {} for label_file in labels_dir.glob('*.txt'): image_id = label_file.stem cls_set = set() for line in label_file.read_text().strip().splitlines(): if not line.strip(): continue cls_id = int(line.split()[0]) cls_set.add(cls_id) image_labels[image_id] = cls_set # 按类别组合分组,保证每类缺陷在训练和验证集都有样本 groups = defaultdict(list) for img_id, cls_set in image_labels.items(): groups[tuple(sorted(cls_set))].append(img_id) train_ids, val_ids = [], [] random.seed(42) for cls_group, img_ids in groups.items(): random.shuffle(img_ids) split = max(1, int(len(img_ids) * 0.8)) train_ids.extend(img_ids[:split]) val_ids.extend(img_ids[split:])

这段代码按每张图像包含的缺陷类别组合分组,再在组内各自按 8:2 切分。这样即使某个类别组合只有少数几张图,训练集和验证集各能分到样本,避免验证集缺失某一类导致 mAP 失真。random.seed(42) 固定随机种子,多次运行切分结果一致,这是可复现实验的基本要求。切完后记得把图像和标注同步移动,只切标注不切图像是最常见的低级错误。

训练阶段的数据增强基本不用自己写,yolov5 默认开启的 mosaic 增强会在训练时把四张图随机裁剪拼接成一张。对小缺陷检测来说,mosaic 的作用很明显:小目标在拼接图中出现得更密集,模型对上下文感知更强。需要手工调的是超参数文件里 HSV 扰动范围,hsv_h 控制色相、hsv_s 控制饱和度、hsv_v 控制明度。钢材表面反光和光照不均比较突出,我会把明度扰动从默认值 0.4 调到 0.6,让模型对光照变化更鲁棒,这个改动通常几十行代码量就能带来明显的效果差异。

3.2 训练配置:数据集 yaml 与命令行参数解读

训练配置分两块:数据集 yaml 和 train.py 命令行参数。先看 yaml:

# data/steel_defect.yaml train: /data/steel/images/train val: /data/steel/images/val nc: 6 names: ['rolled-in_scale', 'scratches', 'pitted_surface', 'inclusion', 'patches', 'oxidation_scale']

train 和 val 路径指向图像目录,yolov5 会按路径替换规则(images 换成 labels,.jpg 换成 .txt)自动找对应标注。nc 必须和 names 列表长度一致,否则模型输出通道数与标注类别数对不上,训练直接报错。names 的排列顺序必须和标注 txt 里的 class_id 一致,这个顺序错了,模型类别就会全部错位,而且训练过程中不会有任何报错提示,只能靠验证阶段识别。

训练命令的典型写法如下:

python train.py \ --data data/steel_defect.yaml \ --weights yolov5s.pt \ --img 640 \ --batch-size 16 \ --epochs 100 \ --device 0 \ --hyp data/hyps/hyp.steel.yaml

--weights 用 yolov5s.pt 预训练权重,模型前几层的特征提取能力直接迁移到钢材缺陷场景,比从零训练收敛快得多。--img 640 是训练输入尺寸,虽然原图只有 200 像素,yolov5 会把输入统一 resize 到 640,mosaic 增强也在 640 尺度下做。这意味着小缺陷的纹理信息在缩放过程中有一定损失,后面避坑章节会讲补偿手段。--batch-size 16 是常见显存下的起步值,不够就降到 8 并适当增加 epochs。--hyp 指定自己调的 hyp.steel.yaml,在默认值基础上修改了类别损失权重和明度扰动范围。

训练过程重点盯两个指标:mAP@0.5 和 mAP@0.5:0.95。前者是 IoU 阈值固定 0.5 时的平均精度,后者是多个 IoU 阈值平均值,更严格。钢材缺陷形状不规则,预测框与真实框轻微错位时 mAP@0.5 可能仍然很高,但 mAP@0.5:0.95 会下降明显,这说明模型定位精度不够,只答对了位置大概对、框不准。

3.3 推理输出:detect.py 与 Python API 调用

训练完成后,用 detect.py 对验证集做一轮推理:

python detect.py \ --weights runs/train/exp/weights/best.pt \ --source /data/steel/images/val \ --img 640 \ --conf-thres 0.25 \ --iou-thres 0.45 \ --save-txt \ --save-conf

--conf-thres 是置信度阈值,低于 0.25 的检测框直接丢弃。这个值调高能降低误检但会增加漏检,产线场景怎么设取决于你对漏检和误检的容忍度,一般缺陷检测偏保守,我会降到 0.2 左右。--iou-thres 是 NMS 的 IoU 阈值,重叠超过 0.45 则合并,小目标密集时这个值要调低到 0.3 附近,否则相邻缺陷会被误合并成一个框。--save-txt 让推理结果保存为和训练标注一样的 txt 格式,--save-conf 额外写入置信度,方便后面定量分析。

更灵活的做法是用 torch.hub 在自研代码里直接调用:

import torch model = torch.hub.load('ultralytics/yolov5', 'custom', path='runs/train/exp/weights/best.pt', force_reload=True) model.conf = 0.25 model.iou = 0.45 results = model('/data/steel/images/val/001.jpg') boxes = results.pandas().xyxy[0] print(boxes[['xmin', 'ymin', 'xmax', 'ymax', 'confidence', 'class']])

这里 model.conf 和 model.iou 是推理时的置信度和 NMS 阈值,给 model 对象赋属性即可,不需要每次调用传参数。results.pandas().xyxy[0] 返回 DataFrame,每行一个检测框,包含坐标、置信度和类别 ID。产线二次开发时,这个 DataFrame 就是对接数据库或数据中台的直接数据源,可以序列化成 JSON 推到上层系统。

4. 运行指南:从零搭建环境跑通缺陷检测

4.1 环境准备:Python 版本与依赖安装

环境搭建是新手最容易翻车的地方,翻车点集中在 PyTorch 和 CUDA 版本不匹配。我建议用 conda 隔离环境,Python 固定在 3.10 左右:

conda create -n steel python=3.10 conda activate steel conda install pytorch torchvision pytorch-cuda=11.8 -c pytorch -c nvidia pip install -r requirements.txt

requirements.txt 是仓库自带的,核心依赖有 opencv-python、matplotlib、pandas、pyyaml、tqdm 等。这里的关键是 conda 安装 PyTorch 时指定 pytorch-cuda,确保 CUDA 算子一起装上,否则即使显卡驱动正常,PyTorch 也会以 CPU 模式运行,训练速度慢一个数量级。没有独立显卡的话,CPU 版本也能跑,但 100 个 epoch 可能要十几个小时,CPU 版本只建议用来验证推理链路。

装完依赖验证一下环境是否通顺:跑一个 epoch 的训练命令,能正常运行说明依赖没问题。这一步我一般会提前做,不要等训练到一半才发现环境缺包,那才是真正的折磨。

4.2 数据集组织:目录结构与 yaml 路径配置

yolov5 对数据集目录结构有约定,需要保持 images 和 labels 两个平级目录:

steel_dataset/ ├── images/ │ ├── train/ # 训练图像 │ └── val/ # 验证图像 ├── labels/ │ ├── train/ # 训练标注 │ └── val/ # 验证标注 └── steel_defect.yaml

train 和 val 的最小要求是图像文件与标注文件一一对应。yolov5 的数据加载器按图片路径推导标注路径,规则是把路径中的 images 替换成 labels、后缀 .jpg 换成 .txt,这个约定写死在代码里,不要自己改。切分数据时注意 train 和 val 两个目录同步切分,只切图像不切标注是常见错误,训练时一半图像找不到标注直接报错。

steel_defect.yaml 里路径用绝对路径还是相对路径看习惯。我偏向绝对路径,省得切换工作目录时路径失效。注意克隆到别的机器时要同步修改这两个路径,否则训练会报数据集不存在,这是传送项目给别人时最容易犯的低级错误。

4.3 训练验证与断点续训

首次跑通用 10 个 epoch 验证链路,别一上来就 100 个 epoch,浪费时间又难排查问题:

python train.py \ --data data/steel_defect.yaml \ --weights yolov5s.pt \ --img 640 \ --batch-size 16 \ --epochs 10 \ --device 0

前 10 个 epoch 跑完,重点看 runs/train/exp 目录下的 results.csv,每行对应一个 epoch,包含 box_loss、cls_loss、obj_loss、mAP@0.5、mAP@0.5:0.95 等指标。如果 loss 前几个 epoch 就在低位震荡不下降,大概率是学习率或数据加载问题,先调试链路通畅再跑正式训练。正式训练期间用 TensorBoard 盯曲线:

tensorboard --logdir runs/train

训练中断时,yolov5 会在 weights 目录保存 last.pt 和 best.pt,last.pt 是最近一个 epoch 的状态,best.pt 是验证集指标最好的权重。断点续训用 --resume 参数指向 last.pt,优化器状态会自动恢复,不用从头再跑前面几十个 epoch。这个功能在训练到一半断电或显存溢出时是救命稻草。

4.4 模型导出:ONNX 转换与部署验证

PyTorch 训练出来的权重部署到产线,第一步是导出 ONNX:

python export.py \ --weights runs/train/exp/weights/best.pt \ --img 640 \ --batch 1 \ --include onnx

导出后用 onnxruntime 验证推理链路:

import onnxruntime as ort import numpy as np import cv2 sess = ort.InferenceSession('best.onnx') input_name = sess.get_inputs()[0].name img = cv2.imread('test.jpg') img = cv2.resize(img, (640, 640)) img = img[:, :, ::-1].transpose(2, 0, 1) img = np.ascontiguousarray(img, dtype=np.float32) img /= 255.0 img = np.expand_dims(img, axis=0) outputs = sess.run(None, {input_name: img})

这里有个隐藏很深的坑:yolov5 在 PyTorch 推理时会对输入做 RGB 通道转换、归一化和维度调整,导出 ONNX 后这些预处理不会包含在模型里,推理代码必须手动复现同样步骤。具体就是 BGR 转 RGB、HWC 转 CHW、除以 255 归一化,三步少一步检测结果都会偏差。另一个常见错误是忘记转颜色空间,检测框位置全乱了,但程序不报错,排查半天才发现是通道顺序问题。outputs 的 shape 是 1x25200x(nc+5),25200 是三个尺度特征图的 anchor 总数,还需要做阈值过滤和 NMS 才能得到最终检测框。

5. 实战避坑:钢材缺陷检测中的四个高频问题

5.1 类别不平衡导致误检漏检

现象:训练完的模型对样本量大的氧化铁皮压入和斑块检测得很好,对夹杂这种样本少的类别几乎不输出检测框,类别精度看着高但召回率很低,产线上夹杂缺陷漏过去好几个。

原因:钢材缺陷数据集六类缺陷样本量天然不均,氧化铁皮压入的图像多,夹杂和麻点少。yolov5 默认按均匀分布计算类别损失,样本量大的类别在损失中占主导,少样本类别的梯度贡献被淹没,模型几乎学不到稳定的特征表达。而产线上少样本缺陷往往是最需要检测的,因为它们出现频率低、现场工艺问题更大。

解决:先统计每类实例数,在超参数文件里把 cls_pw 从默认 1.0 改成与各类别样本数倒数相关的值,提高少样本类别的梯度权重。数据层面用复制粘贴增强,把少见缺陷实例从原图抠出来,随机旋转缩放后粘贴到无缺陷的钢板图像上,这样既补样本量又不破坏形态特征。两个手段组合用,我实际跑下来少样本类别的 mAP 能从 0.5 提升到 0.8 以上。

5.2 小目标缺陷漏检

现象:对斑块、氧化皮压入这样的大面积缺陷检测没问题,但对麻点和细小划伤频繁漏检,尤其缺陷与背景纹理相近时,模型几乎没反应。一张图上十几个麻点,只检出两三个。

原因:麻点缺陷可能只有几十像素大小,占整图比重极低。yolov5s 下采样倍数最高 32 倍,小缺陷经过多层卷积后特征图上的响应微乎其微。另外 yolov5 默认 anchor 按 COCO 数据集统计,偏向中等尺寸,和钢材缺陷尺寸分布差距大,导致小缺陷与 anchor 的 IoU 匹配率很低。

解决:先把输入分辨率从 640 加到 960,小目标在特征图上的像素占比变大,但推理时间随之增加,要按产线节拍取舍。再用 analyze_bbox.py 统计训练集所有标注框的宽高分布,做 k-means 重聚类 anchor,把结果写回 models/yolov5s.yaml。第三个方案是换 P6 模型结构 yolov5s6,它多一个高分辨率特征输出层,小目标检测能力明显强于标准版。实际项目里三个改动组合用,麻点召回率能从 50% 提上去不少。

5.3 训练 loss 震荡不收敛

现象:loss 曲线剧烈抖动,mAP 一直上不去,验证集指标比训练集差一大截,典型的过拟合曲线。模型好像学到了,又好像什么都没学到。

原因:数据集小是根本原因。加上标注本身存在噪声,部分缺陷边界模糊,标注框位置和大小不一致,模型被迫拟合互相矛盾的标注。学习率过大或 warmup 阶段太短也会加剧震荡,优化器在早期就在损失曲面上来回跳动。

解决:先用脚本把标注框的位置和大小分布打印出来,抽样看一批标注与图像的实际对应,剔除明显错标和漏标样本。然后把初始学习率从默认 0.01 降到 0.005,warmup epoch 从 3 拉长到 5。数据集小的时候,收敛稳定性比收敛速度重要。我还会打开早停机制,patience 设为 30,验证集 mAP 连续 30 个 epoch 不提升就自动结束训练,避免白跑一堆无效 epoch。

5.4 推理速度达不到产线节拍

现象:模型精度没问题,但 GPU 推理一帧要 50ms 以上,产线带钢速度按米每秒算,相机帧率跟不上,缺陷来不及检出。

原因:输入分辨率设得过高、模型选了 l 这种大规格,或者推理机器根本没有可用独立显卡,模型意外以 CPU 模式推理。还有隐藏很深的一种情况:训练机器和推理机器 PyTorch 版本不一致,导出模型在其他机器上运行效率打折。

解决:先用 nvidia-smi 确认推理进程是否真在 GPU 上跑。模型规格降到 yolov5n、输入分辨率降到 480 或 320,观察精度跌幅是否在可接受范围。最后用 TensorRT 做 FP16 量化转 engine 格式,推理速度比 ONNX 能快两到三倍,精度损失通常不到 2%。产线部署时我会把 TensorRT 引擎加输入分辨率、置信度阈值做成配置项,现场调参不用重新编译。

6. 二次开发:面向产线场景的定制化改造

6.1 数据增强与样本回流迭代

不同钢种、不同光照下的缺陷形态差异很大,基础模型上线后发现误检漏检,不要急着改模型结构,先完善数据回流机制。产线每天采集到的图像、缺陷记录、模型误检漏检结果都要沉淀下来,每周或每月重新标注并入训练集,增量训练后更新模型。关键是要有清晰的版本管理,我的做法是最简单的:每次迭代按日期命名目录,包含图像、标注、训练配置和权重,一个批次一个归档包。这样模型出问题时能快速回退到上一版,不至于手忙脚乱。

6.2 检测头与 anchor 定制

新增缺陷类别时,在 steel_defect.yaml 的 names 列表追加名称,nc 改成对应数字,用之前的 best.pt 作为预训练权重继续训练。yolov5 会自动扩展新增类别的输出通道,旧类别特征不会丢失,这是迁移学习最实用的价值点。同时重新跑 analyze_bbox.py 统计新数据集的标注框尺寸分布,对 anchor 做一次 k-means 重聚类写回模型配置。这两个调整是新增类别时性价比最高的操作。

6.3 部署端优化与迭代节奏

端侧设备算力有限时,先用 yolov5n 压到极限,再考虑把 NMS 后处理从 Python 移到 C++ 部署端实现,推理管线整体封装成服务,Python 只负责配置和接口。多相机场景下把多路图像拼成一个 batch 一次推理,GPU 利用率和吞吐量都能显著提升。配置化部署是我在产线上反复验证过的方式:置信度阈值、IoU 阈值、输入分辨率、ROI 区域全部做进配置文件,现场调参不需要改代码重新编译。

钢材缺陷检测这类项目,说到底比的不只是模型精度,更是数据质量和迭代效率。我第一次做产线部署时,模型在验证集上 mAP 很好看,上线后漏检率却压不下来,排查了很久才发现是产线相机视角和训练图像视角有偏差,模型在真实场景里泛化不佳。从那以后我每次部署视觉检测模型,都强制把产线实拍样本回流写进交付流程,用真实场景图片重新标注、增量训练、再验证,走完这一轮才敢说模型真正落地。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询