news 2026/9/28 16:33:06

钢材缺陷检测数据集:VOC/COCO/YOLO三格式与YOLO训练全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
钢材缺陷检测数据集:VOC/COCO/YOLO三格式与YOLO训练全流程

简介:本资源为YOLO谢韦尔钢材缺陷检测数据集,面向从事工业质检、目标检测算法学习与竞赛实践的学生及开发者,解决钢材表面缺陷样本获取难、标注格式不统一的问题。包内共2000个文件,以1000个xml标注、990个txt标签为主,另含yaml配置、py脚本与html说明文档,压缩包约12.38MB,规模轻量便于快速上手。数据均来自真实场景,经labelimg高质量标注,同时提供voc、coco、yolo三种格式标签,分目录存放,可直接接入YOLO系列训练流程。资源还附赠环境搭建与训练案例教程,以及训练集、验证集、测试集划分脚本,可按需自行切分数据。目前已有587人学习下载,适合希望快速跑通缺陷检测基线、验证模型效果并积累工业场景实战经验的读者参考使用。

1. 钢材缺陷检测数据集:从1000张图片到三种标签格式的完整落地路径

拿到一个钢材缺陷检测数据集,最怕的不是图片少,而是标签格式对不上、划分脚本跑不通、训练教程语焉不详。谢韦尔钢材缺陷检测数据集打包了1000张工业现场图片,同时提供VOC、COCO和YOLO三种格式标签,外加划分脚本和训练教程,这个组合恰好覆盖了从数据准备到模型启动的完整链路。钢材表面缺陷检测在工业质检里是刚需场景——热轧板、冷轧板在生产线上高速运行,人工目检漏检率高、疲劳快,用YOLO做实时检测是当前性价比最高的方案之一。这个数据集适合三类人:刚接触工业缺陷检测想跑通全流程的新手、需要快速验证YOLO改进算法效果的算法工程师、以及要把检测模型部署到产线边缘设备的落地开发者。1000张图片不算多,但胜在三种标签格式齐全,省去了自己写转换脚本的麻烦,能把精力集中在模型训练和调参上。

2. 三种标签格式的选型逻辑与转换实操

2.1 VOC、COCO、YOLO格式的本质差异

VOC格式用XML文件描述每张图片的标注信息,结构清晰但文件数量多,1000张图就是1000个XML,读取时I/O开销大。COCO格式把所有标注集中到一个JSON文件里,适合大规模数据集管理,但单文件体积大,修改标注需要重写整个JSON。YOLO格式最简洁,每张图对应一个TXT文件,每行是类别 中心x 中心y 宽 高,坐标全部归一化到0到1之间,训练时读取最快。

选哪种格式取决于你的训练框架。Ultralytics系的YOLOv5/v8/v11直接吃YOLO格式,不用转。Detectron2和MMDetection偏好COCO格式。如果要用TensorFlow Object Detection API,VOC格式最省事。这个数据集三种都给了,意味着你不需要从零写转换脚本,但理解转换逻辑仍然重要——因为实际项目中你总会遇到只有一种格式、需要转成另一种的情况。

2.2 用Python脚本做VOC到YOLO的格式转换

假设你手头只有VOC格式的XML,需要转成YOLO的TXT,下面这个脚本可以直接用:

import xml.etree.ElementTree as ET import os from pathlib import Path # 类别映射:根据数据集实际类别修改 CLASS_MAP = {"crazing": 0, "inclusion": 1, "patches": 2, "pitted_surface": 3, "rolled-in_scale": 4, "scratches": 5} def voc_to_yolo(xml_dir, img_dir, out_dir): """将VOC格式XML转为YOLO格式TXT""" os.makedirs(out_dir, exist_ok=True) for xml_file in Path(xml_dir).glob("*.xml"): tree = ET.parse(xml_file) root = tree.getroot() # 获取图片尺寸,用于归一化 size = root.find("size") w = int(size.find("width").text) h = int(size.find("height").text) lines = [] for obj in root.findall("object"): cls_name = obj.find("name").text if cls_name not in CLASS_MAP: continue cls_id = CLASS_MAP[cls_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.0 / w cy = (ymin + ymax) / 2.0 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") # 写入同名TXT txt_path = Path(out_dir) / (xml_file.stem + ".txt") txt_path.write_text("\n".join(lines)) voc_to_yolo("./annotations/xmls", "./images", "./labels/yolo")

这段代码的核心逻辑是坐标变换:VOC用左上角和右下角绝对坐标,YOLO用中心点加宽高的归一化值。CLASS_MAP必须和你的data.yaml里的names顺序完全一致,否则训练时类别全乱。归一化时除以的是图片原始宽高,不是标注框的宽高,这个点新手最容易搞反。如果XML里出现宽高为0的异常框,脚本会直接报除零错误,建议在循环里加个if w == 0 or h == 0: continue的保护。

2.3 COCO格式转YOLO的注意事项

COCO的JSON结构是images、annotations、categories三个主键。转YOLO时要注意COCO的bbox格式是[x_min, y_min, width, height],已经是绝对坐标,但需要自己算中心点。另外COCO的category_id不一定从0开始连续,要建立映射表转成从0开始的连续整数。转换脚本网上很多,但建议自己写一遍,因为每个数据集的类别命名和ID分配都有差异,抄来的脚本往往在category_id映射上翻车。

3. 数据集划分脚本的编写与训练集验证集比例选择

3.1 划分脚本的核心逻辑与随机种子固定

拿到1000张图片和对应标签后,不能直接全丢进去训练,必须划分训练集、验证集,有时还要留测试集。常见比例是7:2:1或8:2。划分脚本的关键是固定随机种子,保证每次运行结果一致,否则调参时验证集变了,指标波动你根本分不清是模型改了还是数据换了。

import random import shutil from pathlib import Path def split_dataset(img_dir, label_dir, out_dir, ratios=(0.7, 0.2, 0.1), seed=42): """按比例划分数据集,复制图片和标签到对应目录""" random.seed(seed) # 固定种子,保证可复现 img_dir = Path(img_dir) label_dir = Path(label_dir) # 只取有对应标签的图片 images = sorted([f for f in img_dir.glob("*.jpg") if (label_dir / (f.stem + ".txt")).exists()]) random.shuffle(images) n = len(images) n_train = int(n * ratios[0]) n_val = int(n * ratios[1]) splits = { "train": images[:n_train], "val": images[n_train:n_train + n_val], "test": images[n_train + n_val:] } for split_name, files in splits.items(): img_out = Path(out_dir) / split_name / "images" lbl_out = Path(out_dir) / split_name / "labels" img_out.mkdir(parents=True, exist_ok=True) lbl_out.mkdir(parents=True, exist_ok=True) for f in files: shutil.copy(f, img_out / f.name) shutil.copy(label_dir / (f.stem + ".txt"), lbl_out / (f.stem + ".txt")) print(f"{split_name}: {len(files)} images") split_dataset("./images", "./labels/yolo", "./dataset", ratios=(0.7, 0.2, 0.1))

seed=42是习惯用法,你可以改成任意整数,但一旦定了就别再动。ratios三个数加起来必须等于1,否则最后一组会多出或漏掉图片。脚本里用shutil.copy而不是move,是为了保留原始数据,万一划分错了还能重来。如果数据集里有些图片没有对应标签文件,脚本里的if条件会自动过滤掉,避免训练时找不到标签报错。

3.2 划分后的目录结构检查

划分完成后,目录结构应该是这样的:

dataset/ ├── train/ │ ├── images/ (700张) │ └── labels/ (700个txt) ├── val/ │ ├── images/ (200张) │ └── labels/ (200个txt) └── test/ ├── images/ (100张) └── labels/ (100个txt)

检查时重点看三个地方:图片和标签数量是否一致、标签文件名是否和图片文件名一一对应、有没有空标签文件。空标签文件意味着这张图没有标注任何缺陷,如果数量多,说明数据集里负样本比例高,训练时容易把模型带偏。

注意:如果验证集里某类缺陷一张图都没有,训练时该类别的mAP会显示为0,不是模型没学好,是验证集没覆盖到。划分后建议统计一下每个类别的分布。

4. YOLO训练教程:从环境配置到模型收敛

4.1 环境配置与预训练模型选择

YOLO训练环境的核心依赖是PyTorch和Ultralytics包。用conda建虚拟环境,Python版本选3.8到3.10之间,太新或太旧都可能遇到包不兼容。安装命令如下:

conda create -n steel_defect python=3.9 -y conda activate steel_defect pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics

cu118对应CUDA 11.8,如果你的显卡驱动支持更高版本,可以换成cu121。装完后用python -c "import torch; print(torch.cuda.is_available())"验证GPU是否可用,返回True才算配置成功。

预训练模型选yolov8n.pt或yolov8s.pt。nano版最快,适合先跑通流程;small版精度更高,适合最终交付。钢材缺陷检测的类别数少、特征相对固定,nano版通常够用,但如果缺陷尺寸很小(比如细裂纹),建议上s版甚至m版。预训练模型在Ultralytics的发布页可以下载,也可以让训练脚本自动拉取。

4.2 data.yaml的编写与关键参数设置

Ultralytics训练需要一个data.yaml文件指定数据路径和类别信息:

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

path是数据集根目录,train、val、test是相对路径。names的键必须从0开始连续,和标签文件里的类别ID一一对应。如果标签里出现了names中没有的ID,训练时会直接报错。

启动训练的命令:

yolo detect train data=./data.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16 lr0=0.01 device=0

epochs=100是起步值,钢材缺陷数据集1000张图,通常50到80轮就能收敛,但设100轮留足余量。imgsz=640是YOLO的标准输入尺寸,如果缺陷目标很小,可以提到imgsz=1024,但显存占用会翻倍。batch=16在8GB显存上跑640尺寸基本安全,显存不够就降到8。lr0=0.01是初始学习率,配合余弦退火调度,大部分情况不用改。device=0指定第一块GPU,多卡用device=0,1。

4.3 训练过程监控与指标解读

训练启动后,终端会实时打印每轮的损失值和mAP。重点看三个指标:box_loss(边界框回归损失)、cls_loss(分类损失)、mAP50(IoU阈值为0.5时的平均精度)。正常情况下,两个loss在前10轮快速下降,之后缓慢收敛;mAP50在前20轮快速上升,之后趋于平稳。

如果box_loss震荡不降,检查学习率是不是太大,或者标注框有没有越界。如果cls_loss降不下去,大概率是类别不平衡——某些缺陷样本太少,模型学不到特征。这时候可以开copy_paste增强,或者手动过采样少数类。如果mAP50卡在某个值上不去,先别急着改模型结构,把验证集的可视化结果调出来看看,很多时候是标注质量问题,不是模型问题。

训练完成后,权重保存在runs/detect/train/weights/best.pt。用这个权重做推理:

yolo detect predict model=runs/detect/train/weights/best.pt source=./test/images save=True

推理结果会保存在runs/detect/predict/下,带标注框的图片可以直接用来做人工复核。

5. 避坑与排查:钢材缺陷检测训练中最容易翻车的五个点

5.1 标签坐标越界导致训练报错

现象:训练启动后几轮内报AssertionError或IndexError,提示坐标超出范围。

原因:VOC转YOLO时,如果标注框的xmax或ymax等于图片宽高,归一化后中心点加半宽可能刚好等于1.0,浮点精度问题导致略微超过1.0。YOLO要求所有坐标严格在0到1之间。

解决:在转换脚本里加裁剪逻辑,cx = min(max(cx, 0), 1),bw = min(bw, 1 - cx),确保不越界。或者训练前用脚本批量检查所有TXT文件,发现越界就修正。

5.2 类别ID不连续导致mAP计算异常

现象:训练正常跑完,但mAP50始终为0或极低,混淆矩阵里类别全混在一起。

原因:data.yaml里的names键从0开始连续,但标签文件里的类别ID跳号了,比如只有0、2、5三个类别,中间缺了1、3、4。YOLO按names的长度分配分类头,ID跳号会导致分类头维度对不上。

解决:统一重映射。写个脚本遍历所有标签文件,把出现的类别ID收集起来,建立旧ID到新ID的映射表,批量替换。确保names的键和标签里的ID完全一致。

5.3 图片和标签文件名不匹配

现象:训练时提示找不到标签文件,或者某张图被跳过。

原因:图片是.jpg,标签是.txt,但文件名大小写不一致,或者图片名里有空格、中文,标签名里没有。Windows下大小写不敏感,Linux下直接报错。

解决:划分脚本里用f.stem取文件名主干,确保图片和标签用同一个主干。批量重命名时统一转小写、去空格、去中文。训练前跑一遍检查脚本,列出所有不匹配的文件。

5.4 验证集mAP波动大

现象:每轮验证的mAP50忽高忽低,没有稳定上升趋势。

原因:验证集太小,1000张图按7:2:1划分,验证集只有200张,某些类别可能只有几张图,指标随机性大。另外如果验证集里混入了训练集图片,指标会虚高。

解决:增大验证集比例到3:1:1,或者用K折交叉验证。划分时严格检查训练集和验证集的图片文件名没有重叠。如果数据集本身类别不平衡,验证集里每个类别至少保证5到10张图。

5.5 训练loss正常但推理效果差

现象:训练时box_loss和cls_loss都降得很好,mAP50也高,但拿best.pt去推理新图片,检测框乱飞或漏检严重。

原因:过拟合。1000张图对YOLO来说偏少,模型可能记住了训练集的纹理而不是缺陷特征。另外如果训练集和验证集来自同一批图片的不同裁剪,验证集指标会虚高,实际泛化能力差。

解决:开强数据增强,mosaic=1.0、mixup=0.2、copy_paste=0.3,增加几何变换和色彩抖动。如果还不行,换更小的模型(nano换tiny),或者冻结骨干网络前几层。最根本的办法是补数据,1000张图做工业缺陷检测确实紧张,能补到3000张以上会有质变。

6. 用混淆矩阵和PR曲线定位钢材缺陷检测的薄弱类别

训练跑完不是终点,看懂评估结果是调优的起点。Ultralytics训练结束后会在runs/detect/train/下生成confusion_matrix.png和PR_curve.png,这两张图比mAP数值更能说明问题。

混淆矩阵的横轴是预测类别,纵轴是真实类别。对角线越深越好,非对角线上的亮斑就是误判。钢材缺陷检测里最常见的混淆是crazing(龟裂)和scratches(划痕)——两者都是细长纹理,模型在低分辨率下容易搞混。如果混淆矩阵显示这两个类别互相误判严重,把输入尺寸从640提到1024,或者专门针对这两类做裁剪增强,让模型看到更多局部细节。

PR曲线看每个类别的曲线下面积。如果某个类别的曲线明显低于其他类别,说明该类别的检测难度高或者样本太少。rolled-in_scale(氧化铁皮压入)在钢材表面缺陷里通常是最难的一类,因为它的形态和背景纹理接近,边界模糊。针对这种情况,我一般会做两件事:一是把该类别的标注框重新过一遍,确保边界框紧贴缺陷区域,不要留太多背景;二是在训练时给这个类别更高的损失权重,Ultralytics支持在data.yaml里加cls_pw参数做类别加权。

还有一个容易被忽略的指标是F1_curve.png。F1是精确率和召回率的调和平均,曲线峰值对应的置信度阈值就是推理时的最佳阈值。默认推理阈值是0.25,但如果F1曲线峰值在0.4,说明0.25阈值下误检偏多,把conf=0.4传给推理命令能明显减少误报。这个技巧在产线部署时特别有用——误报太多工人会直接关掉报警。

最后说个血泪教训:别在训练完只看一眼mAP就收工。把验证集的预测结果可视化出来,一张一张翻,你会发现标注错误、漏标、框不准这些问题,这些才是限制模型上限的真正瓶颈。数据集的质量决定了模型的天花板,模型结构只是逼近这个天花板的手段。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 16:32:33

双路可调稳压电源设计与实战:LM317T/LM337T深度解析

1. 为什么双路可调电源是电子爱好者绕不开的“第一台真实验室设备”你拆过多少块废旧电源?焊过多少个USB充电模块?用过多少个“稳压模块”却在调试运放电路时被噪声拖垮整板信号?我见过太多人把“能输出电压”和“能支撑可靠实验”混为一谈—…

作者头像 李华
网站建设 2026/9/28 16:31:42

LeetCode两数之和全解析:从暴力到哈希表的面试最优解

刚点开LeetCode准备刷题的人,十个有九个第一道题碰到的都是“两数之和”。这题简单到连题目描述都只有一句话,但它在面试里出现的频率一点不比那些难题低。作为LeetCode开篇第一题,它承载的意义不只是“入门友好”,而是帮你建立起…

作者头像 李华
网站建设 2026/9/28 16:29:34

STM32一键生成HEX与自动烧录原理及实战

1. 为什么“一键生成HEX并自动烧录”不是功能噱头,而是开发效率的分水岭在STM32嵌入式开发中,我见过太多人卡在“编译完→找HEX文件→打开ST-Link Utility→选文件→点烧录→等进度条→再点验证”这个循环里。尤其当项目进入调试中期,一天要反…

作者头像 李华
网站建设 2026/9/28 16:29:34

harness-sdk实测:LLM应用系统评估与量化指南

先直接给结论:如果你想给 LLM 应用做系统性的效果评估,harness-sdk 是一个值得花一晚上研究的东西。它解决的不是“能不能跑通”的问题,而是“跑通之后,凭什么说它好、好到什么程度、换一个模型之后会不会变差”的问题。这个项目非…

作者头像 李华
网站建设 2026/9/28 16:28:48

大麦盒子DM4036线刷固件与当贝桌面优化全攻略

1. 大麦盒子DM4036刷机这件事,到底值不值得折腾大麦盒子DM4036这台设备,放在今天看硬件确实不算新,但它的底子并不差——晶晨S905系列芯片、1GB到2GB的运行内存、8GB上下的存储空间,跑个轻量级安卓系统绰绰有余。问题出在原厂固件…

作者头像 李华
网站建设 2026/9/28 16:28:10

CLI-Anything:插件化命令行框架,让重复运维工作自动化

先说说我为什么折腾这个项目。干了这么多年开发和运维,我最深的感受就是:GUI 操作是给“人”看的,命令行操作是给“效率”用的。打开图形界面点十个按钮才能完成的事,命令行一句话就做完了。但现实问题是,日常工作中的…

作者头像 李华