
简介面向轮胎制造与工业视觉质检场景的缺陷检测数据集对应 2154 张轮胎图像覆盖 debris、ground、side、side_cut 四个缺陷类别标注框总数 2844 个其中 debris 类 1599 框、ground 类 564 框、side 类 188 框、side_cut 类 493 框可支撑缺陷分类与定位模型的训练和评估也有助于分析缺陷类别分布不平衡问题。数据同时提供 Pascal VOC 与 YOLO 两种标准标注格式标注文件由 labelImg 工具生成便于直接适配 YOLOv5/YOLOv8、Faster R-CNN 等常见检测框架免去格式转换工作降低数据预处理门槛。压缩包共 2000 个文件以 1999 个 XML 标注文件和 1 个使用说明 TXT 文件为主整体大小约 105.65MB说明文件包含“使用前必读”内容有助于快速掌握目录结构与标注规范适合刚接触工业缺陷检测的读者做迁移学习或基准实验。该资源当前已有 662 人学习是练习目标检测数据准备、格式解析与模型评估的实用数据集。1. 轮胎缺陷检测数据集2154张双格式样本的工程化拆解拿到一份带标注的工业视觉数据集第一步往往不是急着开训练而是先搞清楚格式、数量和类别分布能不能喂给手头的模型。这份2154张轮胎缺陷检测数据集的特殊之处在于它同时提供了Pascal VOC和YOLO两种标注格式jpg、xml、txt三个文件一一对应总计2844个标注框覆盖debris、ground、side、side_cut四个类别。对正在做轮胎外观质检、工业缺陷检测、或者刚上手YOLO系列目标检测的工程师来说这套数据可以直接跳过标注环节用来做模型选型验证、训练pipeline调试甚至是多标签分类的对比基线。更关键的是VOC和YOLO两种格式同时存在意味着不需要额外写转换脚本就能分别跑通基于Detection和基于txt标签的两类训练流程。接下来按实际使用顺序从文件格式、标注结构、训练配置到验证排查逐层拆开来看。2. Pascal VOC XML与YOLO TXT的字段级对照2.1 文件名关联逻辑与目录组织拿到压缩包解压后首先注意到的是firc_tire_xxx这种命名规律的xml文件每个xml对应同一前缀的jpg图片和txt文件。完整数据组织是2154张jpg图片、2154个xml标注、2154个txt标注三者文件名严格一一对应不存在缺失或冗余。数据集根目录 ├── firc_tire_478.jpg ├── firc_tire_478.xml ├── firc_tire_478.txt ├── firc_tire_1654.jpg ├── firc_tire_1654.xml ├── firc_tire_1654.txt └── ...提示实际下载的压缩包内可能会有多个子目录但图片、xml、txt三者的文件名关联关系是确定的拷贝到其他路径也不影响读取。2.2 一张VOC XML里能读到什么用任意文本编辑器打开firc_tire_478.xml看到的是标准的labelImg导出结构annotation foldertire_defect/folder filenamefirc_tire_478.jpg/filename pathC:/Users/admin/Desktop/tire_defect/firc_tire_478.jpg/path source databaseUnknown/database /source size width1024/width height1024/height depth3/depth /size segmented0/segmented object nameside_cut/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin412/xmin ymin389/ymin xmax587/xmax ymax502/ymax /bndbox /object /annotation这里有几个容易忽略的字段需要说明folder和path在训练阶段通常不参与计算但filename必须和实际文件名一致width和height用于后续归一化坐标计算方向是x轴对应width、y轴对应heightobject可能在一个xml中重复出现多次每多一个object就代表一张图里有多个目标difficult标记为1的目标很多训练脚本默认会跳过统计时要留意2.3 YOLO TXT的坐标归一化规则对应同一个对象的txt文件内容是3 0.487793 0.435059 0.170898 0.110352每一行代表一个目标共5个数值按顺序依次是类别ID、中心点x坐标归一化、中心点y坐标归一化、目标宽度w归一化、目标高度h归一化。这四个数值的计算公式如下在中间章开始前先立住这个换算关系。x_center (xmin xmax) / 2 / width y_center (ymin ymax) / 2 / height w (xmax - xmin) / width h (ymax - ymin) / height代入上面XML里的bbox和1024x1024尺寸得到x_center (412 587) / 2 / 1024 0.4878 y_center (389 502) / 2 / 1024 0.4351 w (587 - 412) / 1024 0.1709 h (502 - 389) / 1024 0.1104可以看到txt中第一位的类ID为3对应类别名称列表[debris, ground, side, side_cut]中索引为3的side_cut。这里最大的坑是类别顺序VOC XML里存的是字符串名称YOLO TXT里存的是从0开始的整数索引索引对应关系完全取决于标签列表的定义顺序而不是字母序。如果自己重新排列类别名称列表会导致加载模型时验证集的数据标签错位训练时loss曲线看似正常但mAP评估一塌糊涂。所以拿到数据集后第一件事就是核对类别列表顺序和txt中的类别ID是否一致。2.4 2844个标注框的类别分布与不均衡问题先看官方统计类别名称标注框数量占全部框比例debris159956.2%side_cut49317.4%ground56419.8%side1886.6%合计2844100%debris独占一半以上side类只有188个框这是典型的类别不均衡分布。后面训练时不能只看总体mAP要分类别看PR Curve和F1分数否则模型会很自然地偏向学成debris检测器。3. VOC与YOLO格式互转脚本化的统一校验方案3.1 为什么双格式下还需要写转换脚本很多人会想数据本来就提供了两种格式直接用不就行了但实际上VOC XML和YOLO TXT中一旦出现某一个文件标注缺失、坐标越界、格式损坏训练会在读取阶段直接报错而且报错信息不一定明确。一个常见的操作是写一个Python脚本把两份格式同时读进来做交叉校验检测文件名是否对齐、坐标系是否一致、类别索引是否越界。另一个场景是如果你想把这份数据转成COCO格式喂给Detection2或mmdetectionVOC XML可以直接走现成工具但YOLO TXT需要二次转换此时先用自己的脚本统一格式是更稳妥的。3.2 解析VOC XML并统计类别分布下面先写一个最小化的XML解析脚本输出每个类别的框数量、每张图的框数量、以及图像尺寸分布。import os import xml.etree.ElementTree as ET from collections import Counter xml_dir ./tire_defect_xml files [f for f in os.listdir(xml_dir) if f.endswith(.xml)] class_counter Counter() boxes_per_image [] size_set set() for f in files: tree ET.parse(os.path.join(xml_dir, f)) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) size_set.add((w, h)) objects root.findall(object) boxes_per_image.append(len(objects)) for obj in objects: name obj.find(name).text class_counter[name] 1 print(类别统计:, dict(class_counter)) print(单图框数分布:, Counter(boxes_per_image)) print(图片尺寸集合:, size_set)脚本逻辑分三层先是遍历xml目录然后用ET.parse解析读size字段和object字段最后用一个Counter做累计。输出里最需要关心的是图片尺寸集合正常情况下应该只有一种或少数几种分辨率如果出现大量不一致的尺寸说明原图没统一resize过后续训练时batch内图像会做letterbox填充如果模型输入尺寸和原图长宽比差太远会损失边缘目标信息。3.3 把VOC XML转成YOLO TXT并做坐标越界检查如果自己接收一批同事标注好的新数据但对方只给了VOC格式就需要自己写转换脚本。下面这个函数是完整的转换逻辑import os import xml.etree.ElementTree as ET def convert_voc_to_yolo(xml_path, txt_path, class_list): tree ET.parse(xml_path) 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): name obj.find(name).text if name not in class_list: continue class_id class_list.index(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) # 边界约束防止出现负数坐标或超出图像宽高 xmin max(0, min(xmin, w - 1)) xmax max(xmin 1, min(xmax, w)) ymin max(0, min(ymin, h - 1)) ymax max(ymin 1, min(ymax, h)) x_center (xmin xmax) / 2 / w y_center (ymin ymax) / 2 / h box_w (xmax - xmin) / w box_h (ymax - ymin) / h # 如果归一化后坐标越界打印告警 if not (0 x_center 1 and 0 y_center 1): print(f坐标越界: {xml_path}, class{name}, x_center{x_center}) lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) with open(txt_path, w) as f: f.write(\n.join(lines))这个脚本比常规转换多做了两个细节一是对xmin、xmax做了clip防止人工标注时手滑画出图像边界二是每算一个归一化坐标就检查是否在[0,1]区间内一旦越界就打印文件名便于定位。YOLO训练框架一般把越界坐标当作一个warning而不会直接报错但越界框参与anchor匹配时会产生大量低质量正样本导致训练出来的置信度偏保守检测结果漏检率高。所以在转格式时就把越界问题挡在前面比等训练结果出来再排查效率高得多。3.4 两份格式连续性的交叉验证继续把问题向前推一步既然数据自带两份格式就利用它们的冗余性互相验证。def verify_pairs(xml_path, txt_path, class_list): # 读取XML中的框信息 tree ET.parse(xml_path) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) xml_boxes [] for obj in root.findall(object): name obj.find(name).text 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) xml_boxes.append((name, xmin, ymin, xmax, ymax)) # 读取TXT中的框信息并反算回像素坐标 txt_boxes [] with open(txt_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f格式错误: {txt_path}) continue class_id int(parts[0]) x_center float(parts[1]) y_center float(parts[2]) box_w float(parts[3]) box_h float(parts[4]) xmin (x_center - box_w / 2) * w xmax (x_center box_w / 2) * w ymin (y_center - box_h / 2) * h ymax (y_center box_h / 2) * h txt_boxes.append((class_list[class_id], xmin, ymin, xmax, ymax)) # 对比数量 if len(xml_boxes) ! len(txt_boxes): print(f数量不一致: {xml_path}, xml{len(xml_boxes)}, txt{len(txt_boxes)})交叉验证的价值在于发现那些单看任一份格式都不觉得有问题的损坏。比如xml里某个框坐标写反了xmin大于xmax直接用xml训练时很多模型会静默跳过这个框导致训练图像的质量悄悄下降。但如果同时对比txt里的归一化坐标反算出的框位置差异很容易暴露出来。4. YOLO训练流程落地数据划分、配置文件与训练执行4.1 训练集/验证集划分的原则工业场景下划分数据不能直接用shuf随机打乱要结合数据采集场景做分层划分。这份轮胎缺陷数据集如果来自多条产线、多个时段、多个轮胎批次同一个型号、同一批次轮胎的图像特征相似度很高。如果随机划分导致同一批次的图像同时出现在训练集和验证集验证损失会偏低但实际部署到新批次上时效果明显退化这就是所谓的相似帧污染。一个稳妥做法是从文件名中提取批次信息按batch进行分组采样。import os import random from collections import defaultdict def split_by_group(file_list, val_ratio0.2): # 假设文件名形式为 firc_tire_478.jpg # 这里用文件名的数值部分估算批次实际生产中应替换为真实的批次ID groups defaultdict(list) for f in file_list: num int(f.split(_)[-1].split(.)[0]) group_id num // 100 # 每100个文件一组生产场景可以替换成拍摄时间戳 groups[group_id].append(f) train_files [] val_files [] for group_id, files in groups.items(): random.shuffle(files) val_count int(len(files) * val_ratio) val_files.extend(files[:val_count]) train_files.extend(files[val_count:]) return train_files, val_files这种划分方式保证同组的图片要么全进训练集要么全进验证集不让同一工况下的相似样本跨set验证指标更接近真实部署环境。4.2 训练数据配置文件写法如果你用Ultralytics YOLOv8、YOLOv5做训练需要一个data yaml最简配置如下# tire_defect.yaml path: ./tire_defect_dataset # 数据集根目录 train: images/train # 训练集图片相对路径 val: images/val # 验证集图片相对路径 test: # 留空表示不指定测试集 nc: 4 # 类别数 names: [debris, ground, side, side_cut] # 类别名称顺序必须与txt中的class_id一致注意names列表顺序与上文txt中class_id的定义严格对应。YOLOv5到YOLOv8的Ultralytics框架都支持这种方式nc可以不写直接通过names长度推断。训练启动命令yolo train datatire_defect.yaml modelyolov8n.pt epochs100 imgsz640 batch16 device0各参数含义如下datatire_defect.yaml指定数据配置modelyolov8n.pt使用YOLOv8nano预训练权重作为起点可以用yolov8s.pt、yolov8m.pt替换nano/small/middle参数量递增epochs100训练轮数如果时间有限可以先跑50轮看loss曲线收敛情况imgsz640:训练时输入图像统一resize到640x640。如果xml里标注的原始图像尺寸远大于640网络会压缩小目标尺寸检测debris这个小缺陷可能困难尝试imgsz960或imgsz1280batch16批量大小根据GPU显存调整显存不足时减到8或4device0用第一块GPUCPU训练把这一项去掉但速度会很慢4.3 类别不均衡场景下的超参数调整前面提到debris有1599个框而side只有188个框明显的类别不均衡。Ultralytics框架默认使用cls损失权重1.0建议开启focal loss相关选项。yolo train datatire_defect.yaml modelyolov8s.pt epochs100 imgsz640 batch16 device0 \ loss_cls2.0 loss_box1.2 \ hsv_h0.015 hsv_s0.7 hsv_v0.4 \ fliplr0.5 scale0.5 translate0.1这里解释一下几个关键参数的调整思路loss_cls2.0提高分类损失的权重让网络更关注把类别分对对少数类这种小框多的类别有帮助loss_box1.2略微提高box回归损失权重适合标注框小且多的数据场景hsv_h0.015色调增强幅度光照变化大的产线场景可以适当增大但过大会让轮胎颜色失真scale0.5图像缩放增强比例轮胎是固定的圆形结构缩放太大会改变缺陷的真实尺度分布注意这些增强参数在原版YAML里的默认值已经过广泛验证建议先在默认参数上训练一轮作为baseline再开启增强对比mAP50和mAP50-95不要一开始就堆参数。4.4 从零训练与迁移学习的取舍用预训练权重yolov8s.pt是迁移学习如果想完全从随机权重开始用yolov8s.yaml。工业数据集的实际做法是优先用预训练权重因为轮胎纹理、缺陷边缘等低层特征在COCO上已经学到迁移后只需要在种场景下做适配训练时间直接减半。但有一种情况必须从零训练如果部署到嵌入式设备上需要自己剪枝或改结构预训练权重里的结构和自定义结构不一致就只能从头开始。看一下训练日志中几个关键指标怎么解读Epoch GPU_mem Box_Loss Cls_Loss DFL_Loss Instances Size 20 6.21G 0.9832 0.8120 1.2103 123 640Box_Loss持续下降是好事Cls_Loss如果下跌到一定程度不再变化说明模型分类能力到瓶颈了。Instances表示这一批输入图像里共包含多少个GT框如果一直很小说明类别不均衡导致小类别的框很少参与训练此时可以从上面提到loss_cls参数的调整上找解法。4.5 训练完成后如何产出量产模型训练结束后Ultralytics会在runs/detect/train/weights/下生成best.pt和last.pt。best.pt是按验证集指标保存的last.pt是最后一轮的结果。导成部署用的ONNX、TensorRT格式命令如下yolo export modelruns/detect/train/weights/best.pt formatonnx dynamicTrue imgsz640 yolo export modelruns/detect/train/weights/best.pt formatengine device0 imgsz640dynamicTrue只对ONNX导出有效表示允许输入尺寸动态变化。转成engine后TensorRT会做层融合和精度校准在Jetson设备或工业PC上推理速度能提升2到3倍。生产部署时还要注意输入图像的预处理要和训练时保持一致YOLO模型的letterbox填充操作如果在部署端漏了检测框会整体偏移。5. 进阶技巧用小技巧压榨这套数据集的验证价值5.1 用交叉验证替代单次划分2154张属于中小规模数据集单次划分的训练/验证集结果受划分随机性影响很大。常见做法是用5-Fold或K-Fold交叉验证做更稳定的效果评估。把数据一份为5份轮流做验证集训练5次最终取mAP的平均值和标准差这才是这套数据在当前模型下的真实水平。对小样本工业数据集来说这个操作能在不改模型的情况下把指标评估的可靠性提升一个档次。5.2 用mAP曲线定位过拟合的具体时间点训练日志中每轮会记录验证集的mAP值直接看results.png的mAP50曲线变化。通常mAP50还在涨而mAP50-95开始横盘或下跌说明模型已经开始过拟合大目标对精确框位置的能力开始下降。这时候把epoch从100减到60或70或者调大lr0初始学习率让训练更早收敛到稳定区。5.3 写一个最小可视化验证脚本把xml里标注框画回图上训练前用随机抽样的方式把xml解析出的坐标画在原图上目检标注质量是最直接的方法。import cv2 import xml.etree.ElementTree as ET color_map {debris: (0, 0, 255), ground: (0, 255, 0), side: (255, 0, 0), side_cut: (0, 255, 255)} xml_path firc_tire_478.xml img_path firc_tire_478.jpg img cv2.imread(img_path) tree ET.parse(xml_path) for obj in tree.getroot().findall(object): name obj.find(name).text bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) cv2.rectangle(img, (xmin, ymin), (xmax, ymax), color_map[name], 2) cv2.putText(img, name, (xmin, ymin - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color_map[name], 2) cv2.imwrite(visual_check.jpg, img)一个常见的查看结果是ground类别的标注框边界与轮胎胎面边缘贴合不紧密框住了一部分背景这类框虽然不影响分类但对box回归的训练会产生噪声。发现这种系统性标注偏差后可以统一把对应类别的xmin加5个像素、xmax减5个像素做收缩修正比重新标注更省时间。5.4 在Ultralytics框架下做类别均衡抽样如果想尽量缓解不均衡可以不改增强直接用Ultralytics提供的dataset_callback机制。下面的写法是按cls_weights设定不同类别参与训练的概率import numpy as np from ultralytics.data.dataset import YOLODataset cls_weights {0: 1.0, 1: 1.5, 2: 2.0, 3: 1.5} # side类别权重调高 # 在训练代码里通过缓存sampler实现 class WeightedSampler: def __init__(self, labels, weights): self.indices [] for i, lb in enumerate(labels): for cls_id in lb[:, 0]: self.indices.extend([i] * int(weights[cls_id])) np.random.shuffle(self.indices)实际做法是重写YOLODataset的__getitem__或直接用torch.utils.data.WeightedRandomSampler按每张图的最大类别做加权。只适合小型实验正式训练时还是推荐调整loss权重或使用focal loss因为过采样容易在少量样本的类别上过拟合。这份数据集的四种缺陷里side和side_cut的量偏少做数据增强时优先对这两类样本做小幅旋转、左右翻转和局部亮度扰动可以有效弥补量少带来的不稳定性。数据本身质量整体不错标签字段完整VOC与YOLO双格式的关联关系也没有明显断裂配合上述脚本检查和参数调优思路可以直接作为轮胎外观质检落地的起始数据集。本文还有配套的精品资源点击获取