ARTICLE DETAIL

资讯详情

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

轮胎缺陷检测数据集实战:VOC转YOLO、类别不均衡与YOLOv8训练避坑指南

轮胎缺陷检测数据集实战:VOC转YOLO、类别不均衡与YOLOv8训练避坑指南 简介一份面向轮胎外观质检与工业视觉缺陷检测场景的专用数据集围绕2154张轮胎图片提供VOC与YOLO两种格式的标注文件覆盖debris、ground、side、side_cut四类缺陷共2844个标注框可直接用于YOLO系列、Faster R-CNN、SSD等目标检测模型的训练与评估。压缩包共2000个文件以XML标注文件为主另含说明类txt文档整体大小约105.65MB图片与标签均按统一命名对应便于批量读取和处理。已有667人学习/下载适合工业质检算法开发、轮胎表面缺陷识别研究以及目标检测数据集处理入门者。获取后可省去自行整理与格式转换的时间快速获得可被常见检测框架直接加载的标注数据方便开展模型训练、交叉验证与算法效果对比。1. 轮胎缺陷检测数据集2154张图、2844个框以及最容易被忽视的类别失衡做轮胎缺陷检测最痛苦的往往不是模型不收敛而是你手头根本凑不齐一套能直接开训的标注数据。这份轮胎缺陷检测数据集一共2154张jpg每张图配好Pascal VOC和YOLO两种格式的标签文件4个类别debris、ground、side、side_cut合计2844个框解压后可以马上喂给YOLOv8跑基线。它适合正在搭工业质检检测流程、想先复现一轮完整训练的开发者。先说一个关键结论四个类别的框数严重不均衡side类只有188框debris有1599框小类目不做处理大概率直接学不出来后面第5章专门讲怎么处理。2. 解压前先读这份清单VOC与YOLO双格式的目录结构和类目分布2.1 压缩包内部长什么样文件组成与目录结构这份数据是7z格式打包的解压后你会看到一个名字和压缩包一致的目录里面放着一份“使用前必读.txt”、2154张jpg图片、2154个xml文件、2154个txt文件。文件命名采用统一的前缀加编号像firc_tire_478.jpg对应firc_tire_478.xml和firc_tire_478.txt一一对应没有多余的子文件夹干扰。目录结构大概是下面这样轮胎缺陷检测数据集VOCYOLO格式2154张4类别/ ├── 使用前必读.txt ├── firc_tire_1.jpg ├── firc_tire_1.xml ├── firc_tire_1.txt ├── firc_tire_2.jpg ├── firc_tire_2.xml ├── firc_tire_2.txt ├── ... ├── firc_tire_2154.jpg ├── firc_tire_2154.xml └── firc_tire_2154.txt解压之后第一件事不是打开某个 xml 看标注而是先把“使用前必读.txt”从头到尾读一遍。里面一般会写明这套数据的标注约定、类别语义和来源说明比任何二次转述都可靠。我遇到过不止一次因为跳过这种说明文件结果把类别顺序理解错最后整个训练白跑的情况。三组文件的对应关系是这套数据能直接用的基础。xml 文件是 labelImg 工具标注时默认生成的 Pascal VOC 格式txt 文件是给 YOLO 系列训练直接吃的检测格式。注意这里的 txt 是检测格式每行只有 5 列类别ID 中心点x 中心点y 宽 高不是实例分割那种带分割路径的多列txt别拿去做分割任务的输入。2.2 四类缺陷的框数分布与不均衡程度4个类别的框数分布摘要里给得很明确我整理成了一张表训练前最好先把这张表刻在脑子里类别名称框数占比debris159956.2%ground56419.8%side1886.6%side_cut49317.3%合计2844100%占比是我按总框数2844算出来的目的就是让你直观看到不均衡有多严重。最大的 debris 类有1599框最小的 side 类只有188框两者相差约8.5倍。放在工业缺陷检测里这个悬殊程度会直接影响训练模型很容易把 side 当成背景学掉或者为了压低损失干脆把所有框都预测成 debris。从命名习惯和轮胎场景推测debris 更像是异物或碎屑类缺陷ground 应该对应接地胎面区域side 和 side_cut 分别指向胎侧和胎侧切割损伤。我的建议是训练时直接保留英文标签不要自己翻译后再重新映射多一次映射就多一次翻车机会后面避坑章节会详细说。2.3 为什么同时保留VOC和YOLO两份标注很多刚接触检测的人会问既然 YOLO 训练只吃 txt那 xml 是不是多余实际上两份标注各有用途。labelImg 默认导出的是 VOC 的 xml里面记录了图片尺寸、通道数、每个目标的名字和像素级坐标人类可读性好方便复核标注质量。YOLO 的 txt 是相对坐标喂给网络之前要做归一化机器算起来快但人眼几乎没法直接读。同时保留两份的好处是当你需要做数据分析、按像素坐标裁图、或者转成 COCO 等其他格式时xml 是权威来源当你直接开训 YOLO 时txt 是现成的。而且 xml 里的 size 节点保留了原始图片尺寸一旦训练前做了缩放或裁剪你可以随时用像素坐标重新计算归一化标签比从 txt 反推要可靠得多。3. 7z解压与三件套校验Windows与Linux命令、文件名对齐检查3.1 Windows下解压7z文件工具选择与解压参数Windows 自带的资源管理器不支持 7z 格式双击只会提示无法打开。常见做法是装一个 7-Zip或者用 Bandizip。如果你主要用 PyCharm 写代码更省事的方案是直接在 PyCharm 的 Terminal 里跑命令前提是系统 PATH 里能识别到 7z 命令装过 7-Zip 之后默认就有。7z x 轮胎缺陷检测数据集VOCYOLO格式2154张4类别.7z -oD:\tire_dataset -y这段命令的含义x 表示解压并保留压缩包内的目录结构-o 指定输出目录注意-o和路径之间没有空格-y 表示遇到同名文件自动覆盖。如果只想先看压缩包里有什么别急着解压用7z l列目录能少走很多弯路7z l 轮胎缺陷检测数据集VOCYOLO格式2154张4类别.7z | head -307z 格式相比 zip 的优势在压缩率图片这类重复数据多的文件用 LZMA 压缩能明显变小。代价就是解压时 CPU 占用高一些2154张图加标注解压出来也就几十秒到一两分钟属于完全可以接受的范围。3.2 Linux服务器解压p7zip安装与x命令的常用参数训练一般是在 Linux 服务器上做的所以解压这步也经常发生在服务器端。Ubuntu/Debian 默认没有 7z 命令需要先装 p7zip-fullCentOS 那套则要装 p7zip 和 p7zip-plugins否则会报“Cannot open the file as archive”。# Debian / Ubuntu sudo apt update sudo apt install -y p7zip-full # CentOS / RHEL sudo yum install -y p7zip p7zip-plugins mkdir -p /data/tire_defect 7z x 轮胎缺陷检测数据集VOCYOLO格式2154张4类别.7z -o/data/tire_defect -yp7zip-full 装完后才有完整的 7z 命令支持CentOS 上如果只装 p7zip 不带 plugins解压 7z 文件时会直接失败。-o参数指定解压目标目录如果目录不存在7z 会自动创建不用先 mkdir。这里我写了一个 mkdir 是为了保险也方便后面把训练数据单独放一个干净目录。这里要特别提醒一个常见误用7z x和7z e的区别。x 保留压缩包内部的目录层级e 会把所有文件平铺到当前目录。对这种带目录结构的压缩包务必用 x用 e 解压后2154个jpg和2154个xml全混在一起文件名前缀一样还好换别的数据集直接就是灾难现场。3.3 解压后的第一道校验jpg、xml、txt三件套是否一一对应解压完成先别急着训练花两分钟做一次文件完整性校验。我一般用一段 bash 循环检查每个 jpg 是否都有同名的 xml 和 txt同时统计各类文件的总数cd /data/tire_defect for f in *.jpg; do base${f%.jpg} [ -f $base.xml ] || echo MISSING xml: $base [ -f $base.txt ] || echo MISSING txt: $base done echo jpg文件总数: $(ls *.jpg | wc -l) echo xml文件总数: $(ls *.xml | wc -l) echo txt文件总数: $(ls *.txt | wc -l)这段脚本的逻辑很简单${f%.jpg}把文件名后缀去掉得到前缀然后检查同前缀的 xml 和 txt 是否存在。如果循环过程中没有任何 MISSING 输出且三种文件总数都等于2154才说明这一包数据是完整的。我一般还会顺手统计一下类别框数确认和摘要描述一致cat *.txt | awk {print $1} | sort | uniq -cawk 取每行第一列也就是类别IDsort 加 uniq -c 输出每个ID的出现次数。正常结果应该是四行分别对应4个类别的框数ID从0到3。如果发现第五行或者ID超过3说明 txt 里有脏数据后面训练会莫名报错趁早排查。4. 把VOC转成YOLO格式坐标归一化脚本与四个边界坑4.1 VOC和YOLO坐标体系的本质差异标注意味着什么VOC 格式里目标框用像素绝对值表示xmin、ymin、xmax、ymax都是图片上的真实坐标单位是像素。YOLO 格式则完全不同它要求的是归一化相对坐标目标中心点的横纵坐标、框的宽度和高度分别除以图片宽高最终数值都落在 0 到 1 之间。为什么要改成相对坐标因为检测网络训练时会把任意尺寸的图片缩放到统一输入大小绝对像素坐标在缩放后必须跟着变而相对坐标天然不受影响。这也是 YOLO 训练能把不同分辨率图片混在一起的原因。理解这一点你就明白转换的核心就四个字除以宽高。具体换算关系是这样中心点 x 等于(xmin xmax) / 2 / 图片宽度中心点 y 等于(ymin ymax) / 2 / 图片高度框宽等于(xmax - xmin) / 图片宽度框高等于(ymax - ymin) / 图片高度。4.2 可复用的xml转txt脚本含clip和类别校验这套数据集本身已经带了 txt按理说不用转。但我强烈建议你把这个脚本保存下来因为你迟早要标自己的数据或者要把这份数据转成其他格式。下面这个脚本我每次做 VOC 转 YOLO 都会用处理了出界、类别映射和空标注三种情况import xml.etree.ElementTree as ET from pathlib import Path CLASSES [debris, ground, side, side_cut] # 顺序必须和yaml一致 def convert(xml_path: Path, out_dir: Path): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASSES: print(f未知类别 {name} 在 {xml_path.name}已跳过) continue cls_id CLASSES.index(name) bnd obj.find(bndbox) xmin float(bnd.find(xmin).text) ymin float(bnd.find(ymin).text) xmax float(bnd.find(xmax).text) ymax float(bnd.find(ymax).text) # 边界钳制防止标注出界导致归一化后出现负数或大于1 xmin max(0, min(xmin, img_w)) xmax max(xmin, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(ymin, min(ymax, img_h)) x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) if lines: out_path out_dir / (xml_path.stem .txt) out_path.write_text(\n.join(lines), encodingutf-8) else: print(f空标注已跳过: {xml_path.name}) xml_dir Path(VOC标注目录) out_dir Path(YOLO标注目录) out_dir.mkdir(exist_okTrue) for xml_file in xml_dir.glob(*.xml): convert(xml_file, out_dir)这段脚本里值得注意的参数和逻辑有三处。一是CLASSES列表顺序就是类别IDdebris固定映射为0ground为1side为2side_cut为3这个顺序必须和之后 data.yaml 里的 names 完全一致否则训练出来全是张冠李戴。二是钳制操作把xmin限制在 0 到图片宽度之间并强制xmax不小于xmin防止标注框出界。三是6f保留了6位小数精度完全够用全转出来文件也不会太大。还有一个容易被忽略的点脚本里图片尺寸直接取自 xml 的 size 节点没有去读图。省IO是好事但代价是万一 xml 里的 size 和真实图片尺寸不一致坐标就会整体偏移。严谨的做法是抽查几张图用 PIL 读出实际宽高和 xml 对比from PIL import Image img Image.open(firc_tire_478.jpg) print(实际尺寸:, img.size)4.3 转换后自查坐标范围、类别ID、空标注文件转换完成后不要直接开训先跑一遍检查。坐标值必须在 0 到 1 之间且 width 和 height 不能为负类别ID只能是0、1、2、3。用一行 awk 就能全目录检查# 检查坐标越界正常应该无输出 awk { if ($20 || $21 || $30 || $31 || $40 || $41 || $50 || $51) print FILENAME, $0 } *.txt # 检查类别ID是否超出范围正常应该无输出 awk { if ($10 || $13) print FILENAME, $0 } *.txt这两条命令的原理是逐行读取 txt 并检查第2到第5列是否越界第二段检查第一列的类别ID。如果第一条命令有输出说明原标注里存在出界框训练时网络读到异常标签轻则警告重则 loss 飞升。如果第二条命令有输出说明转换时类别映射出了遗漏。空标注文件也值得单独统计一个 txt 是0字节说明这张图没有目标训练时算背景没问题但如果超过总文件数的5%就要怀疑是不是漏标了。5. 训练避坑类别不均衡、标签错位和精度归零的五个排查点5.1 类别名顺序错位debris、ground、side、side_cut一个都不能改这是我见过翻车最多的地方。现象是训练时 loss 降得挺正常验证集 mAP 也不低但拿训练集里的图实际推理发现 side 和 side_cut 互相混淆或者所有框都被预测成 debris。原因几乎都是类别顺序不一致。VOC 转 YOLO 时脚本里 CLASSES 的顺序和 data.yaml 里 names 列表的顺序如果有一个地方手滑把ground, debris换了位置ID 就全错位了。YOLO 训练时读的是 ID不读名字网络眼里 ID 为1的类别永远对应同一个语义。解决方法是把顺序固化成唯一的硬性规定转换脚本的 CLASSES、data.yaml 的 names、以及验证时标签的名字三处完全一致。我拿到任何新数据集的第一件事就是打开一个 txt 看第一列数字再打开对应 xml 看类别名人工对齐一次。5.2 精度为0或loss为nan的常见原因另一种典型现象是训练一开始 loss 直接输出 nan或者训了几轮验证集 mAP 一直是0看起来像是玄学问题实际大部分是标签数据的问题。先说 loss 为 nan。常见原因是标签里出现了异常值比如某个 txt 里的坐标不在 0 到 1 区间或者类别ID跳到了7、8这类越界值。网络在计算损失时拿到非法目标梯度直接爆炸。解决办法是用第4章末尾的 awk 命令全量扫描一遍标签文件。再说验证精度为0。最常见原因是数据集目录结构不对train 和 val 里图片与标签文件没有一一对应或者 dataloader 把空标签文件当成了图片没有目标。YOLOv8 对这种情况往往不报错只是默默把损失记成背景最后 mAP 自然为0。检查手段是单独打印数据集统计信息确认训练集里有正样本框别急着调模型结构。5.3 side类只有188框类别不均衡与数据增强现象很规律训练10轮以后 debris 的 AP 能到0.8side 的 AP 还在0.1以下甚至预测时完全没有 side 框。原因就是类别不平衡占6.6%的 side 类在训练中被背景和其他大类淹没了。解决有两条路。第一是数据层面含 side 标注的图片做增强副本把 188 框扩充到至少 400 框以上。复制粘贴原始标注不是一个好做法更好的方式是重复采样含 side 的图片让每个 epoch 里 side 的出现频率更高配合 YOLOv8 自带的 Mosaic 增强等于变相做了拼接增广。第二是算法层面训练时调大稀少类别的损失权重但说实话在样本量差8倍以上的情况下调整损失函数的效果远不如先把数据补起来。这里强调一点不要指望只靠改 yolo 损失函数里的某个权重参数就解决全部问题。工业场景下最有效的还是把小类目样本多收集、多标注模型能看到的正样本多了AP 自然就上来了。5.4 7z解压失败或文件损坏服务器上解压报Cannot open the file as archive或者解压到一半报 CRC 失败现象明确得很。前者的原因通常是 p7zip 组件不完整CentOS 上只有 p7zip 没有 p7zip-plugins没有解 7z 格式的解码器。后者原因几乎都是文件在传输过程中损坏了。解决办法也直接装全组件重试解压前先做一次完整性测试。7z t命令可以只测试不输出文件7z t 轮胎缺陷检测数据集VOCYOLO格式2154张4类别.7z如果输出里出现 CRC Error 或者 Data Error说明压缩包已经损坏直接重新下载别浪费时间尝试修复。这个习惯我现在是强制自己每次下载完大文件都跑一遍因为在训练平台上浪费两小时才发现数据是坏的比什么都憋屈。5.5 标注框出界与difficult标志的处理还有一批坑藏在标注细节里。VOC xml 里经常能看到xmax比图片宽度还大或者ymin为负数的情况labelImg 标注时鼠标拖过头就会这样。另外有些 xml 里带difficult1/difficult标志表示这框很难辨认当年 VOC 比赛的规则是这类框不参与评估。现象是不管怎么调参某些类别的 AP 总是异常低而且正好是那些框靠近图片边缘的类别。原因是出界框归一化后出现负坐标或超1坐标模型学到了一批错得离谱的回归目标。解决方法是转换时做钳制并且把 difficult 框直接剔除。我的做法是在转换脚本里加一行过滤if obj.find(difficult) is not None and int(obj.find(difficult).text) 1: continue这行代码的作用是读取 difficult 节点值为1时跳过这个框。对于这套数据如果你发现某个类 AP 异常先查是不是出界框被原样转换进去了。多花五分钟写这段过滤能省掉后面几个小时的瞎调参。6. 用一份data.yaml跑通YOLOv8单图对比验证的小技巧6.1 data.yaml的写法与类别顺序约束数据集到手格式确认无误就该进训练了。YOLOv8 训练入口很简单但 data.yaml 写错一样白搭。我一般是按图片编号尾号划分训练验证集避免同一条轮胎的相邻帧全分到一边划分完再写配置path: /data/tire_defect train: images/train val: images/val nc: 4 names: 0: debris 1: ground 2: side 3: side_cutnames 的顺序必须和训练标签里的ID一一对应这是整条流水线里最不能错的一环。路径建议用绝对路径别用相对路径YOLOv8 在不同工作目录下启动时相对路径经常出幺蛾子。6.2 验证技巧用训练集单图predict对比训练完成后很多人直接看验证集 mAP觉得数字够高就算完事。我自己的习惯是强制加一道单图对比拿一张训练集原图比如firc_tire_478.jpg用训好的权重推理一次把预测结果的 txt 和原标注 txt 逐行对比。yolo predict modelruns/detect/train/weights/best.pt sourcefirc_tire_478.jpg save_txtTrue cat runs/detect/predict/labels/firc_tire_478.txt对比时重点看三件事类别ID是否和原标注一致、框中心坐标是否接近、框大小量级是否相同。如果模型在训练集图上连自己学过的框都检测不对那验证集 mAP 再高也有水分。从那以后我每次拿到新数据集都会强制自己走一遍解压校验、格式检查、单图对比这三步再开始跑长训练。虽然每次只多花几分钟但拦下过无数次看起来像玄学的精度问题这份数据集你拿到手也可以照这个流程走一遍。希望帮到你。本文还有配套的精品资源点击获取
返回列表