ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

YOLO谢韦尔钢材缺陷检测数据集:VOC/COCO/YOLO格式转换与训练实战

YOLO谢韦尔钢材缺陷检测数据集:VOC/COCO/YOLO格式转换与训练实战 简介本资源为YOLO谢韦尔钢材缺陷检测数据集面向从事工业质检、目标检测算法学习与竞赛实践的学生、研究人员及工程师解决钢材表面缺陷样本获取难、标注格式不统一的问题。压缩包共2000个文件约12.38MB包含1000张真实场景高质量图片以及vocxml、cocojson和yolotxt三种格式标签分别存放于不同文件夹可直接用于YOLO系列目标检测训练。此外还附赠数据集划分脚本、YOLO环境搭建与训练教程支持按需划分训练集、验证集和测试集并配有yaml配置文件与说明文档方便快速复现实验。目前已有587人学习下载适合需要完整数据与配套脚本的读者参考使用。1. 谢韦尔钢材缺陷检测数据集到底能解决什么问题钢材表面缺陷检测是工业质检里最典型的视觉任务之一产线速度快、缺陷形态杂、正负样本极度不均衡。谢韦尔Severstal那场公开钢铁缺陷检测竞赛把四类高频缺陷——划痕、夹杂物、斑块、凹坑——摆到了台面上也让一批人第一次意识到工业缺陷检测的难点不在模型结构而在数据组织。你手上这份「YOLO谢韦尔钢材缺陷检测数据集(含1000张图片)对应voc、coco和yolo三种格式标签划分脚本训练教程.rar」本质是把一份竞赛级数据整理成了可直接喂给 YOLO 的工程包1000 张图、三套标签、一个划分脚本、一份训练教程。它解决的不是「有没有数据」而是「拿到数据后怎么在半小时内跑通第一条训练曲线」。适合两类人一类是想入门工业缺陷检测但被数据格式卡住的算法新手另一类是手里有产线图、想快速验证 YOLO 能不能用的落地工程师。下面我按自己实际处理这类数据包的顺序把格式转换、划分、训练、排错讲透。2. 三种标签格式的差异与转换逻辑2.1 VOC、COCO、YOLO 到底差在哪很多人拿到三套标签第一反应是「重复劳动」其实这三套格式服务的是不同环节。VOC 用 XML 存绝对坐标xmin/ymin/xmax/ymax一目了然适合人工核对和可视化COCO 用单个 JSON 存所有图带images/annotations/categories三段结构是很多检测框架和评测脚本的通用输入YOLO 用每图一个 txt存归一化的class cx cy w h直接对应 YOLO 的 dataloader。钢材缺陷里「斑块」这类缺陷边界模糊VOC 的矩形框在人工复核时最直观而 YOLO 格式在训练时读取最快。三套并存的意义是标注阶段用 VOC 核对训练阶段用 YOLO跨框架评测时用 COCO。格式存储单位坐标类型典型用途VOC每图一个 XML绝对像素人工核对、可视化COCO全量一个 JSON绝对像素跨框架评测、mmdetectionYOLO每图一个 txt归一化 0-1YOLO 训练直接读取2.2 用脚本把 VOC 转成 YOLO 并校验数据包里通常已经带好三套标签但你要会自己转才能处理后续新增的图。下面这段脚本把 VOC 的 XML 转成 YOLO 的 txt同时做一次坐标越界校验。import os import xml.etree.ElementTree as ET # 类别映射钢材四类缺陷顺序必须和训练时的 data.yaml 一致 CLASS_MAP {scratch: 0, inclusion: 1, patch: 2, pitted: 3} def voc_to_yolo(xml_dir, out_dir, img_w, img_h): os.makedirs(out_dir, exist_okTrue) for name in os.listdir(xml_dir): if not name.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, name)) root tree.getroot() lines [] for obj in root.findall(object): cls obj.find(name).text.strip() if cls not in CLASS_MAP: continue # 跳过未定义类别避免训练时报 index 越界 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) # 越界裁剪钢材图边缘缺陷常被标到图外 xmin, xmax max(0, xmin), min(img_w, xmax) ymin, ymax max(0, ymin), min(img_h, ymax) cx (xmin xmax) / 2 / img_w cy (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h if w 0 or h 0: continue # 裁剪后无效框直接丢弃 lines.append(f{CLASS_MAP[cls]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(os.path.join(out_dir, name.replace(.xml, .txt)), w) as f: f.write(\n.join(lines)) voc_to_yolo(Annotations, labels, 1600, 256)逻辑说明CLASS_MAP的顺序决定训练时类别索引一旦和data.yaml里的names顺序不一致模型会把划痕学成凹坑。img_w/img_h必须传真实图像尺寸谢韦尔原始图是 1600×256 的长条图如果你在预处理里 resize 过这里要传 resize 后的尺寸。参数上cx/cy/w/h保留 6 位小数足够YOLO 读取时对精度不敏感但w/h为 0 的框一定要丢否则训练时 loss 会出 NaN。2.3 COCO 转 YOLO 时最容易忽略的 id 映射COCO 的category_id往往不是从 0 连续开始的比如谢韦尔原始 COCO 里可能是 1、2、3、4。直接拿category_id当 YOLO 的 class 会得到一个从 1 开始的索引训练时第一类永远学不到。正确做法是建一个category_id - 连续索引的映射表转换时查表。import json with open(annotations.json) as f: coco json.load(f) # 建立连续映射sorted 保证顺序稳定 cat_ids sorted(c[id] for c in coco[categories]) id2idx {cid: i for i, cid in enumerate(cat_ids)} img_id2name {img[id]: img[file_name] for img in coco[images]} img_id2size {img[id]: (img[width], img[height]) for img in coco[images]} for ann in coco[annotations]: idx id2idx[ann[category_id]] w, h img_id2size[ann[image_id]] x, y, bw, bh ann[bbox] # COCO bbox 是左上角 宽高 cx (x bw / 2) / w cy (y bh / 2) / h # 后续按 image_id 聚合写入对应 txt这里的关键是 COCO 的bbox是[x, y, width, height]不是[xmin, ymin, xmax, ymax]很多人第一次转会在这里翻车框整体偏移半个宽度。另外id2idx用sorted保证每次运行映射一致避免今天训练和明天训练类别顺序不同。3. 数据集划分脚本与训练集构建3.1 按 8:1:1 划分并保证缺陷类别均衡钢材缺陷数据有个特点一张图可能同时有多个缺陷且「凹坑」这类样本远少于「划痕」。如果纯随机划分验证集里可能一个凹坑都没有mAP 评估就失真。我一般用分层抽样先按「图中包含的缺陷类别组合」分组再在组内按比例抽。import os import random from collections import defaultdict random.seed(42) # 固定种子保证划分可复现 def load_label_classes(label_dir): 返回 {图片名: set(类别)} img2cls {} for name in os.listdir(label_dir): if not name.endswith(.txt): continue cls_set set() with open(os.path.join(label_dir, name)) as f: for line in f: if line.strip(): cls_set.add(int(line.split()[0])) img2cls[name.replace(.txt, )] cls_set return img2cls def split_dataset(img2cls, train_r0.8, val_r0.1): # 按类别组合分组保证稀有组合在三个集合都出现 groups defaultdict(list) for img, cls_set in img2cls.items(): key tuple(sorted(cls_set)) groups[key].append(img) train, val, test [], [], [] for key, imgs in groups.items(): random.shuffle(imgs) n len(imgs) n_train int(n * train_r) n_val int(n * val_r) train imgs[:n_train] val imgs[n_train:n_train n_val] test imgs[n_train n_val:] return train, val, test逻辑说明seed42是血泪经验不固定种子的话每次划分结果不同你复现不了上周的 mAP。groups按类别组合分组比如「只有划痕」和「划痕凹坑」是两组这样稀有组合不会被随机划分全丢进训练集。参数上train_r/val_r按 8:1:1 设如果数据量小于 1000建议改成 7:2:1给验证集多留点样本。3.2 生成 YOLO 训练所需的目录结构和 data.yamlYOLO 训练要求固定的目录结构图片和标签分开放且路径要写进data.yaml。划分脚本跑完后按下面结构组织dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml内容如下path写数据集根目录绝对路径names顺序必须和转换脚本里的CLASS_MAP完全一致path: /data/severstal train: images/train val: images/val test: images/test nc: 4 names: 0: scratch 1: inclusion 2: patch 3: pitted提示nc和names数量必须一致少写一个类别 YOLO 会在加载时直接报错不会静默跳过。3.3 用 YOLO 官方命令跑通第一条训练曲线环境配好后训练命令本身很短但参数决定你能不能收敛。钢材缺陷图是 1600×256 的长条图直接 resize 到 640×640 会把缺陷压扁我一般用rectTrue保持长宽比配合imgsz640。yolo detect train \ data/data/severstal/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ rectTrue \ lr00.01 \ patience20 \ projectruns/severstal \ nameexp1参数说明modelyolov8n.pt是 nano 版预训练权重1000 张图用 nano 足够换 s/m 容易过拟合rectTrue对长条图很关键不开的话默认方形填充缺陷区域被压缩patience20表示 20 轮 mAP 不涨就早停钢材数据噪声大不设早停容易在后期震荡lr00.01是 YOLOv8 默认值如果 loss 前几轮就爆 NaN降到 0.001 再试。训练开始后重点看cls_loss和box_loss是否同步下降只降 box 不降 cls 说明类别映射有问题。4. 训练中常见的翻车点与排查4.1 现象loss 从第一轮就是 NaN原因标签里有w或h为 0 的框或者坐标没归一化直接写了像素值。谢韦尔数据里边缘缺陷被标到图外转换时没裁剪就会产生负宽高。解决回到 2.2 的转换脚本确认w 0 or h 0的框被丢弃并检查所有 txt 里数值是否都在 0-1 之间。用一行命令快速排查awk {if($40 || $50 || $41 || $51) print FILENAME: $0} labels/train/*.txt4.2 现象mAP 一直是 0但 loss 在降原因data.yaml里的names顺序和标签里的 class 索引对不上模型学的是「索引 0 是划痕」但验证时按names去匹配类别全错。解决打印一张图的标签和data.yaml对照确认索引 0 对应的类别名一致。另一个可能是验证集路径写错YOLO 找不到图会静默跳过验证mAP 恒为 0。4.3 现象训练到一半 BN 层崩溃报NaN in batch norm原因batch 太小比如batch4加上长条图单个 batch 内像素分布差异大BN 统计量失稳。解决把batch提到 16 以上或者改用rectTrue减少填充带来的分布偏移。如果显存不够用梯度累积模拟大 batch而不是硬降 batch。4.4 现象验证集 mAP 高但实际推理漏检严重原因划分时验证集和训练集来自同一批连续拍摄的图缺陷形态高度相似mAP 虚高。解决划分脚本里按拍摄批次或时间分组而不是纯随机。如果数据包里没有批次信息至少保证seed固定后人工抽查验证集确认里面有独立场景的图。4.5 现象训练速度极慢GPU 利用率不到 30%原因dataloader 的workers默认是 8但长条图解码慢CPU 成了瓶颈。解决把workers提到 16并确认图片是 JPEG 而不是 PNGPNG 解码慢一倍。另外cacheTrue可以把图缓存到内存1000 张 1600×256 的图约占 1.2GB 内存机器扛得住就开。5. 把 1000 张图用到极致的几个进阶技巧数据量只有 1000 张时模型很容易过拟合我一般从三个方向榨干数据价值。第一是离线增强钢材缺陷的方向不敏感划痕横竖都有所以flipud和fliplr都可以开但mosaic要慎用四图拼接会让长条图的缺陷比例失真建议mosaic0.5而不是默认的 1.0。第二是类别权重如果「凹坑」只有几十个样本在data.yaml同级目录放一个hyp.yaml把cls_pw调高或者直接在损失里对稀有类加权。第三是预训练权重的选择yolov8n.pt是在 COCO 上训的COCO 里没有钢材缺陷但底层边缘特征可迁移如果你能找到工业缺陷相关的预训练权重替换掉 backbone 的前几层收敛会快很多。验证方法上别只看 mAP50钢材缺陷更关心漏检率。我习惯在验证后单独跑一遍推理统计每类的召回yolo detect val \ modelruns/severstal/exp1/weights/best.pt \ data/data/severstal/data.yaml \ imgsz640 \ rectTrue \ conf0.25 \ iou0.5 \ save_jsonTrueconf0.25是推理阈值工业场景宁可误检不可漏检的话降到 0.15 再跑一次对比召回。save_jsonTrue会输出 COCO 格式的预测结果方便你用 pycocotools 单独算每类 AP。如果某一类 AP 明显低先别改模型回去看这类样本的标注框是不是普遍偏大或偏小钢材缺陷的「斑块」边界模糊标注一致性差是 mAP 上不去的头号原因。最后一个习惯每次改完划分或增强参数先跑 10 个 epoch 看 loss 曲线趋势别一上来就 100 轮。我早期图省事直接跑满结果发现标签映射错了白等两小时。现在固定先epochs10快速验证流程通不通通了再放大。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进