ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

钢表面缺陷检测数据集:YOLO/VOC/COCO三套标签与训练全流程解析

钢表面缺陷检测数据集:YOLO/VOC/COCO三套标签与训练全流程解析 简介YOLO钢表面缺陷检测数据集收录真实工业场景下10000张钢表面图片覆盖划痕、麻点、氧化皮等常见缺陷形态适合目标检测算法学习者、工业视觉开发者及课题研究使用。压缩包内含VOC(xml)、COCO(json)、YOLO(txt)三种标签格式分别存放于独立文件夹可直接对接YOLO系列训练流程省去自行转换标注的时间。同时附赠划分训练集、验证集、测试集的Python脚本以及Windows、Linux两种环境下的YOLO搭建与训练教程帮助使用者按需分配数据、配置环境并快速复现模型训练。资源共2000个文件以xml标签为主配合py脚本、txt清单和html图文教程总大小约212MB目录结构清晰便于逐项检索学习。目前已有549人学习下载对于希望快速构建钢表面缺陷检测模型或入门YOLO实战的读者是一份即取即用的完整资料。1. 钢表面缺陷检测的10000张赌注三套标签和一套能直接跑的流程做钢表面缺陷检测的人都知道标注比模型本身更磨人。一张图里可能同时挂着麻点、划痕和氧化皮标注框差几个像素训练出来的就是一堆假阳性。我拆过不少这类数据集真正愿意反复复现的是那种“图片、标签、脚本、教程”四件套齐全的——YOLO钢表面缺陷检测数据集就是这种。它包含10000张真实场景的高质量图片使用labelimg标注标注框质量高同时给出VOCxml、COCOjson和YOLOtxt三种格式标签分别存放在不同文件夹拿到手省去自己写转换脚本的麻烦文末附赠三套数据集划分脚本和Linux/Windows双版本的YOLO环境搭建、案例训练教程。适合两类人刚进工业质检视觉项目、想快速跑通一个完整闭环的新手手里有自己数据、想找个干净基线的工程师。2. VOC/COCO/YOLO三种标签格式结构差异、读取逻辑与训练前的选型2.1 VOC的xml绝对像素坐标与object结构VOC格式的核心是一个xml文件每个文件对应一张图片所有目标框都挂在object节点下。先看一段典型的标注文件annotation folderJPEGImages/folder filenamesteel_20241011_023.jpg/filename size width640/width height480/height depth3/depth /size object namepitted_surface/name difficult0/difficult bndbox xmin120/xmin ymin85/ymin xmax230/xmax ymax190/ymax /bndbox /object /annotation读取时务必注意三点bndbox里存的是绝对像素坐标xmax - xmin才是真实框宽name是类别名要和你的类别列表严格一致大小写写错会在训练时报“class not in names”size里的宽高是标注时的原图尺寸如果训练前做了resize要么重新算坐标要么让数据加载器自己处理。很多VOC转YOLO的脚本在最后一步出错根因就是拿resize后的图去对原始xml的绝对像素框。2.2 COCO的jsonimage_id、category_id与bbox的映射关系COCO把整份数据集的标注汇总到一个json文件里结构分三段images记录图片id和文件名annotations记录每个目标框归属哪张图、属于哪个类别categories给出类别id和类别名的映射。看下面的示意结构{ images: [ {id: 1, file_name: steel_20241011_023.jpg, width: 640, height: 480} ], annotations: [ {id: 1, image_id: 1, category_id: 2, bbox: [120, 85, 110, 105], area: 11550} ], categories: [ {id: 1, name: crazing}, {id: 2, name: pitted_surface} ] }读COCO最容易踩的坑是bbox的格式它存的是[x, y, width, height]不是VOC那种左上右下两个点。我见过有人直接把COCO的bbox塞给一个按[x1, y1, x2, y2]设计的可视化函数画出来的框全是歪的还以为是标注质量问题。另外category_id是从1开始编号有的转换工具会从0开始而YOLO的类别id是从0开始跨格式转换时这个偏移量是经典的“差一错误”来源。2.3 YOLO的txt归一化坐标与类别id的对应YOLO格式最简洁每行一个目标五个字段类别id、中心点x、中心点y、框宽、框高全部除以图片宽高做了归一化。一行数据长这样2 0.3438 0.2917 0.1719 0.2188前一个2是类别id对应names列表里的第3个名字索引从0开始后面四个浮点数都是0到1之间的小数读取的时候必须乘以图片的width和height才能还原成像素坐标。检验一个YOLO txt是否干净我一般会写个三行脚本把所有框的数值范围打出来如果出现负数或大于1的数说明要么是手工标注时填错要么是转换脚本没有归一化。这类脏标签不会让训练直接报错但会在推理阶段产生大量落在图片外的预测框表现就是mAP莫名其妙地低。2.4 训练前到底选哪套标签按框架读取习惯决定很多初学者会在一个数据集里同时保留三套标签然后纠结“是不是都要用”。实际上训练时只用一套。我的选择逻辑很简单用Ultralytics YOLO系列仓库直接读取YOLO txt用mmdetection读VOC或COCO更顺自己写加载器则选自己最熟的那套格式。项目里三套标签分文件夹存放真正的价值在于省去格式转换的时间并且可以交叉验证标注一致性——比如随机抽50张图把VOC的xml还原成框、YOLO的txt还原成框叠到同一张图上对比偏差超过3个像素就说明某套标签生成时出了错。标签格式文件组织坐标形式适合的框架VOC xml每图一个xml绝对像素[x1,y1,x2,y2]mmdetection、老版Faster R-CNN生态COCO json全量数据一个json[x,y,width,height]Detectron2、mmdetection、COCO APIYOLO txt每图一个txt归一化[cx,cy,w,h]YOLOv5/v8系列官方仓库# 交叉验证三套标签一致性时我常用这条命令把YOLO txt画到图上 python draw_yolo_boxes.py --img-dir JPEGImages --label-dir labels --out-dir check_visual命令背后的逻辑是实际画出来的框和原图上缺陷边缘对得上这组标签才算可用。如果只信训练loss不看可视化等模型部署到产线才发现框偏了返工成本反而更高。3. 三个划分脚本怎么用从train_list.txt到ImageSets目录的完整分工3.1 图片标签一起搬按比例生成train/val/test三个物理目录项目里最直观的一个脚本是把图片和标签按比例复制到新目录生成的目录结构能被YOLO官方仓库直接读取。核心逻辑是先取全部图片文件名随机打乱按比例切成三段再把每段的图片连同同名txt一并复制过去。我重写了一个等价的最小版本方便你理解它的行为import os import random import shutil # 路径配置根据自己的实际目录改 img_src JPEGImages # 原始图片目录 label_src labels # YOLO格式txt标签目录 out_root dataset # 划分结果输出根目录 # 划分比例先留测试集再在剩余里拆train/val train_ratio, val_ratio 0.8, 0.1 # 余下0.1自动作为test random.seed(42) # 固定随机种子保证可复现 imgs [f for f in os.listdir(img_src) if f.lower().endswith((.jpg, .png, .jpeg))] random.shuffle(imgs) n len(imgs) n_train int(n * train_ratio) n_val int(n * val_ratio) bucket { train: imgs[:n_train], val: imgs[n_train:n_train n_val], test: imgs[n_train n_val:], } for split_name, files in bucket.items(): # 每个split下都建 images/ 和 labels/ 两个子目录 img_out os.path.join(out_root, split_name, images) lb_out os.path.join(out_root, split_name, labels) os.makedirs(img_out, exist_okTrue) os.makedirs(lb_out, exist_okTrue) for f in files: stem os.path.splitext(f)[0] shutil.copy(os.path.join(img_src, f), os.path.join(img_out, f)) shutil.copy(os.path.join(label_src, stem .txt), os.path.join(lb_out, stem .txt)) print(f{split_name}: {len(files)} 张图片)这个脚本有三个参数需要你按实际情况改img_src和label_src是数据集的原始路径注意labels里放的是YOLO格式的txt不是VOC的xmltrain_ratio和val_ratio是划分比例常见配置是0.8/0.1如果你想用五五开做对比实验改成0.5/0.2即可剩余部分会自动归到test。random.seed(42)这行的价值在于复现同一份数据、同一个种子两次划分结果完全一致方便你排查“为什么我用这个脚本跑出来的效果跟教程不一样”。3.2 split_train_val生成ImageSets下的txtVOC工具链的索引文件第二个脚本做的事和第一个完全不同它不复制任何文件只在ImageSets/Main/目录下生成三个txt——train.txt、val.txt、test.txt每行是图片文件名不带扩展名和路径。这类索引文件是给VOC风格的工具链用的模型训练时按这个列表去对应目录里找图而不是遍历整个文件夹。脚本本身的逻辑很简单就是读取全部图片名、按比例打散、分别写入三个txtimport os import random img_dir JPEGImages out_dir ImageSets/Main os.makedirs(out_dir, exist_okTrue) imgs [os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .png, .jpeg))] random.seed(42) random.shuffle(imgs) total len(imgs) line_train \n.join(imgs[:int(total * 0.8)]) line_val \n.join(imgs[int(total * 0.8):int(total * 0.9)]) line_test \n.join(imgs[int(total * 0.9):]) for name, text in [(train, line_train), (val, line_val), (test, line_test)]: with open(os.path.join(out_dir, f{name}.txt), w) as fp: fp.write(text)使用这个脚本时有一个容易被忽略的点生成的txt里不能有多余的空行或标题信息每行干干净净只有文件名。有些编辑器会在文件末尾自动加空行YOLO的datasets.py读到最后一行时解析出一个空字符串经常报“AssertionError: train.txt从未找到图片”。如果你遇到类似错误先检查txt尾部是不是有看不见的换行符。3.3 train_list.txt与两类划分脚本的边界什么时候用哪个很多下载这份资源的人会被三个脚本名绕晕train_list.txt是生成列表的入口配置不是脚本本体两个“划分脚本”一个是物理复制文件一个是生成索引txt。我的判断标准很简单用YOLO官方仓库物理目录结构最快因为datasets.py本身就支持按images/train和labels/train这种结构递归寻找用老式VOC训练链路就生成ImageSets索引txt再配合voc_label.py把xml转成训练用的txt。train_list.txt这种文件给的是纯文件名清单一般配合自定义数据加载器使用它不是你想直接跑就能跑的脚本。这里还牵涉一个选型问题物理复制和索引文件哪个更好物理复制会占用双倍磁盘空间但调试起来直观删掉某个split的目录也不会影响其他部分索引文件省空间但前提是你的图片和标签目录必须保持整洁不能存在“图片在但txt被误删”的孤儿文件。我自己在本地实验更喜欢物理复制因为可以随时打开目录数一下文件总数心里有底在服务器上跑大规模实验则用索引文件避免重复复制上千张图浪费IO。3.4 给划分脚本加一层校验图片与标签一一对应的兜底检查拆过几个数据集之后我养成了一个习惯不论用哪个划分脚本跑完后必须先过一遍校验再启动训练。校验的核心是检查每个split里图片和标签的数量、文件名是否一一对应。最常见的翻车是label文件名是*.txt但图片是.jpg而脚本只做了stem .txt拼装若某张图漏标注复制时就会报FileNotFoundError。我在原脚本基础上加了一段最朴素的检查missing_label 0 for split_name, files in bucket.items(): for f in files: stem os.path.splitext(f)[0] lb_path os.path.join(label_src, stem .txt) if not os.path.exists(lb_path): missing_label 1 print(f[{split_name}] 缺少标签: {f}) print(f检查完成共 {n} 张图片缺失标签 {missing_label} 张)这段脚本本身不复杂但价值在于把“文件不存在”的异常从训练阶段提前到准备阶段。训练跑到一半才发现2%的图片没标签损失的不只是时间还有试错的心情。另一个值得检查的维度是空标签文件一个txt文件大小为0训练时读取不到任何目标YOLO会把这张图当背景训练如果空文件比例超过5%模型的召回率会被明显拉低。4. Linux与Windows双环境跑通YOLO环境搭建、案例改造与断点续训4.1 Ubuntu侧驱动、CUDA、conda的安装顺序不能乱项目附带的Linux教程里把Ubuntu环境安装放在最前面这个顺序很关键。NVIDIA驱动、CUDA Toolkit、PyTorch三者的关系是驱动负责让显卡能被系统识别CUDA提供运行时的底层库PyTorch通过CUDA调用显卡计算。装错顺序最常见的现象是nvidia-smi能显示显卡信息但PyTorch里torch.cuda.is_available()返回False。我的建议是先装驱动重启后确认nvidia-smi正常再装CUDA最后建conda虚拟环境装PyTorch# 1. 安装驱动后确认显卡状态 nvidia-smi # 2. 创建独立虚拟环境避免污染系统Python conda create -n yolo python3.9 -y conda activate yolo # 3. 安装与CUDA版本匹配的PyTorch pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118安装完PyTorch后强烈建议先跑一条验证命令再做别的python -c import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))输出True和你的显卡型号才说明环境通了。如果输出False不要急着重装全套先检查nvcc -V看CUDA版本是否和PyTorch的编译版本对应很多时候只是PATH环境变量没刷新。4.2 Windows侧先看驱动再装包避免CUDA版本白折腾Windows环境搭建和Linux没有本质区别但有个血泪经验值得单独说不要一上来就装最新版CUDA先看显卡驱动支持的CUDA版本。nvidia-smi右上角会显示“CUDA Version: 12.x”这个数字是驱动能支持的上限不是说你必须装这个版本而是你装的CUDA不能比它新。教程里Windows版本的路径依赖做得比较细我按它的顺序在本地跑过一遍关键步骤是装好显卡驱动后在conda里新建环境然后直接用pip安装PyTorch而不是先去NVIDIA官网下完整版CUDA。PyTorch的pip包会自带CUDA运行时对绝大多数训练任务够用了。Windows上比Linux多一档麻烦路径。数据集千万不要放在带中文或空格的路径下D:\缺陷检测\数据集这种目录会在数据加载时报一堆诡异错误最典型的报错是FileNotFoundError但文件明明就在那里。我一般会在项目根目录建一个纯英文的datasets/steel_defect/所有训练操作都在这个目录里做。4.3 把案例训练改成自己的数据集数据yaml、类别数与权重路径三处必改教程里说“根据案例修改训练自己的数据集”实际需要动的就三个地方数据配置文件、类别数量、预训练权重。先写一个数据yaml这是YOLO读取数据集的唯一入口# steel_defect.yaml path: ./datasets/steel_defect # 数据集根目录 train: images/train # 训练图片目录 val: images/val # 验证图片目录 nc: 4 # 类别数改成你labels里的实际缺陷类别数 names: [crazing, inclusion, patch, pitted_surface] # 顺序必须和txt里的类别id一致nc和names是训练脚本里最容易出错的两个字段。nc填错会导致最后一层卷积输出维度不匹配训练直接报错names的顺序错了更隐蔽训练能跑但预测出来的类别标签全是错的——比如txt里id为0的缺陷是crazing你的names列表第一项却是inclusion模型就会把crazing识别成inclusion。我在改完yaml后的第一件事永远是打开一张标注好的图确认txt里的类别id和yaml里的names能对上。接下来是训练命令。以YOLO官方仓库常见的训练入口为例一份可以直接套用的命令长这样python train.py --data steel_defect.yaml --weights yolov5s.pt \ --epochs 100 --batch-size 16 --img 640 --project runs/steel几个参数的实际意义--weights指定预训练权重建议优先用官方提供的COCO权重它能显著加快收敛速度--epochs对钢表面缺陷这类小目标任务100轮是及格线200轮能更稳--batch-size按你的显存调整16G显存跑yolov5s用16没问题显存不够就降到8--project是输出路径所有训练日志和权重都会存到这里面。4.4 训练中断与断点续训不用每次从零开始工业场景里训练100轮要跑好几个小时中间断电、显存溢出、手动停掉都是常事。YOLO的断点续训很成熟不需要额外装任何工具训练中断后用同一条命令加上--resume参数即可python train.py --data steel_defect.yaml --weights yolov5s.pt \ --epochs 100 --batch-size 16 --img 640 --project runs/steel \ --resume runs/steel/exp/weights/last.pt--resume支持两种写法给last.pt的完整路径或者直接写--resume让它自动找runs/steel/exp/下的last.pt。我习惯显式写出路径因为一次实验可能在多个exp目录里留有历史记录自动寻找有找错的风险。续训后要注意的是学习率脚本会按上次保存的状态恢复学习率调度器不会从初始值重新开始。5. 避坑排查标签错位、划分失真与loss不收敛的5条记录5.1 标注框与图片错位训练loss正常但预测框全偏现象训练过程loss曲线一路下降看起来非常顺利验证集上mAP也不低但把预测结果画到原图上发现框和真正的缺陷边缘错开一大截。原因数据集里某张图片被人为resize过但它的txt标签仍按原尺寸归一化。比如原图是640×480实际缺陷中心点在像素(320, 240)归一化是(0.5, 0.5)图片被压缩成320×240后缺陷中心像素挪到了(160,120)若归一化结果还是按640算画出来就偏了。解决训练前批量读取所有txt的归一化坐标范围确认都在0到1之间且分布均匀再用可视化脚本随机抽100张图叠加画框人眼扫一遍错位问题一眼就能暴露。5.2 测试集里某类缺陷一张都没有随机划分的分层陷阱现象划分比例设的0.8/0.1/0.1训练集和验证集都正常测试集上某一类缺陷的AP直接为0。原因数据划分时只做了整体随机shuffle没有按类别分布做分层抽样。钢表面缺陷里麻点和划痕出现频率可能相差数倍小样本类全部落在训练集里测试集一张都没有算出的AP自然失真。解决在划分前统计每张图片包含的类别按“图片级类别标签”做分层抽样保证每个split里各类别占比接近原始数据分布。如果某类样本太少直接把它全部留在训练集测试集只评估样本足够的类别并在报告里注明。5.3 中文路径导致DatasetNotFound一类典型的启动报错现象训练命令一执行就报Dataset not found或者图片读取数量为0。原因数据集放在带中文、空格的路径下Windows下最常见的触发点是桌面文件夹叫“新建文件夹”或“我的数据集”。Python的路径库在不同系统间处理非ASCII字符的表现不一致轻则警告重则直接读不到文件。解决把整个数据集挪到纯英文、无空格的路径比如F:\steel_defect\。这个操作看似简单但我见过有人因为嫌麻烦不挪连续两天卡在环境报错上最后挪完三分钟就能跑了。5.4 loss变成nan学习率、batch与BN的连锁反应现象训练到第20到30轮时box_loss或cls_loss突然变成nan之后所有指标全部失效。原因学习率设置过大导致梯度爆炸或batch太小、BN层的滑动统计不稳定。钢表面缺陷数据里如果存在大量空白背景图BN的均值和方差会被带偏进一步加剧数值不稳定性。解决先把学习率降到默认值的十分之一试跑50轮batch小于8时开启梯度累积另外检查label里是否混入了归一化后边界越界的框——一张图只有0.5×0.5的框反而比没有框更危险。5.5 预训练权重与类别数不匹配改nc就报错现象用官方预训练权重微调自己的数据集加载权重时报维度不一致或者训练能启动但每个epoch开头打印一串“Error loading state_dict”。原因COCO预训练模型输出层有80个类别你的数据集只有4类最后一层卷积的参数维度完全不同。解决训练命令里带上--freeze参数冻结backbone层或者使用官方提供的迁移学习策略——加载预训练权重时跳过输出层从零初始化head部分。两种做法都能跑区别在主分支上冻结backbone收敛更快但最终精度可能被锁死只跳过head收敛稍慢但模型对钢表面缺陷的适配能力更强。6. 不看单次loss用混淆矩阵和mAP给训练结果做一次“复检”训练完成不代表交付完成。我第一次独立做钢表面缺陷模型时看训练曲线挺漂亮就直接部署结果现场报告一晚上17条误检全是把麻点认成划痕。从那以后我每次训练完都强制走一遍下面的复检流程。先重跑一次验证让模型在测试集上完整推理得到官方的结果热力图python val.py --data steel_defect.yaml --weights runs/steel/exp/weights/best.pt --img 640跑完后在runs/steel/exp/目录下会生成confusion_matrix.png和results.png。我看这两张图的顺序是固定的先看混淆矩阵找到对角线之外最亮的格子——那一格就是最容易互相误检的缺陷对再看PR曲线看每个类别的召回率拐点出现在哪里。如果混淆矩阵里pitted_surface被大量识别成patch我不会急着加大epoch而是先抽20张误检图看具体形态确认是两类缺陷本身太像还是标注框边界切得太接近。确认是标注问题就回去修数据确认是纹理相似就针对这两个类别单独做数据增强或增加样本而不是盲目调阈值。对精度要求更高的场景我还会用脚本按类别分别统计AP只看平均mAP会掩盖某个小类完全没学会的事实。逐类AP表上低于0.5的类别才是下一轮迭代真正要补的短板。这一套流程走下来模型的真实水平才算被测透部署时心里才有点底。希望帮到你。本文还有配套的精品资源点击获取
返回列表