
简介面向钢材表面缺陷检测场景的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 到 640mosaic 增强也在 640 尺度下做。这意味着小缺陷的纹理信息在缩放过程中有一定损失后面避坑章节会讲补偿手段。--batch-size 16 是常见显存下的起步值不够就降到 8 并适当增加 epochs。--hyp 指定自己调的 hyp.steel.yaml在默认值基础上修改了类别损失权重和明度扰动范围。训练过程重点盯两个指标mAP0.5 和 mAP0.5:0.95。前者是 IoU 阈值固定 0.5 时的平均精度后者是多个 IoU 阈值平均值更严格。钢材缺陷形状不规则预测框与真实框轻微错位时 mAP0.5 可能仍然很高但 mAP0.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, pathruns/train/exp/weights/best.pt, force_reloadTrue) 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 python3.10 conda activate steel conda install pytorch torchvision pytorch-cuda11.8 -c pytorch -c nvidia pip install -r requirements.txtrequirements.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.yamltrain 和 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、mAP0.5、mAP0.5:0.95 等指标。如果 loss 前几个 epoch 就在低位震荡不下降大概率是学习率或数据加载问题先调试链路通畅再跑正式训练。正式训练期间用 TensorBoard 盯曲线tensorboard --logdir runs/train训练中断时yolov5 会在 weights 目录保存 last.pt 和 best.ptlast.pt 是最近一个 epoch 的状态best.pt 是验证集指标最好的权重。断点续训用 --resume 参数指向 last.pt优化器状态会自动恢复不用从头再跑前面几十个 epoch。这个功能在训练到一半断电或显存溢出时是救命稻草。4.4 模型导出ONNX 转换与部署验证PyTorch 训练出来的权重部署到产线第一步是导出 ONNXpython 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, dtypenp.float32) img / 255.0 img np.expand_dims(img, axis0) outputs sess.run(None, {input_name: img})这里有个隐藏很深的坑yolov5 在 PyTorch 推理时会对输入做 RGB 通道转换、归一化和维度调整导出 ONNX 后这些预处理不会包含在模型里推理代码必须手动复现同样步骤。具体就是 BGR 转 RGB、HWC 转 CHW、除以 255 归一化三步少一步检测结果都会偏差。另一个常见错误是忘记转颜色空间检测框位置全乱了但程序不报错排查半天才发现是通道顺序问题。outputs 的 shape 是 1x25200x(nc5)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.005warmup 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 很好看上线后漏检率却压不下来排查了很久才发现是产线相机视角和训练图像视角有偏差模型在真实场景里泛化不佳。从那以后我每次部署视觉检测模型都强制把产线实拍样本回流写进交付流程用真实场景图片重新标注、增量训练、再验证走完这一轮才敢说模型真正落地。希望帮到你。本文还有配套的精品资源点击获取