十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

VOC转YOLO全流程:轮胎缺陷数据集训练与部署实践

VOC转YOLO全流程:轮胎缺陷数据集训练与部署实践 简介面向深度学习目标检测任务的轮胎缺陷数据集采用VOCPASCAL VOC格式组织包含训练集884张、测试集46张共930张真实轮胎图像配套880个XML标注文件和880张JPG图片压缩包约117.9MB。每份XML记录缺陷位置边界框与类别标签如裂缝、磨损等可直接用于YOLO、Faster R-CNN等主流检测框架的训练与验证。数据集已按标准划分训练/测试集便于直接评估模型泛化能力无需额外拆分。目前已有1174人学习下载适合正在做工业质检、轮胎表面缺陷识别相关课题的研究人员或开发者可用于复现论文实验、快速搭建检测流程或验证算法改进效果。 先说我看到这个标题的第一反应这不就是工业视觉里特别典型的一个“有数据但没流程”的案例吗VOC格式的轮胎缺陷数据集训练集884张、测试集46张规模说大不大说小也够入门到进阶折腾一轮。很多人拿到这类数据集后第一步就是急着改模型、调参数结果折腾半天发现数据根本没处理好标注格式跟训练框架对不上坐标转换出了错训练出来的模型在测试集上一塌糊涂最后还以为是算法不行。实际上这类固定格式的数据集真正的分水岭恰恰在数据准备这一步。这篇文章我就围绕这套轮胎缺陷数据把从VOC格式校验、转成YOLO训练所需格式、划分数据集、配置训练、再到模型评估和部署的完整链路走一遍。适合刚入门目标检测、或者手里刚好有一批VOC格式数据但不知道怎么喂给YOLO系模型的朋友参考内容不挑框架YOLOv5、YOLOv8、YOLOv9、YOLO11都适用。1. 数据集基本盘先看清手里有什么1.1 这88446意味着什么先说数字。884张训练图、46张测试图合起来930张。测试集占比约4.95%这是一个相当小的测试集。如果你拿这46张图去评估模型结果会很不稳定可能同一套权重多跑几次验证mAP波动都能超过2个百分点。所以我的建议是标题虽然只写了训练集和测试集但你在实操时一定要从训练集里再拆出一部分做验证集比如按9:1拆从884张里抽80到90张当val。别小看这一步。YOLO系框架在训练时依赖验证集来做早停early stopping、学习率调整和最优权重选择。你不拆验证集框架要么直接拿测试集当验证要么干脆不监控收敛情况。前者会让你的测试集被污染后者会让你无法判断训练到底好没好。另外930张对工业缺陷检测来说属于“小样本中的入门规模”。轮胎表面的缺陷五花八门气泡、裂纹、夹杂、缺胶、划伤、接头开裂等如果类别多每类平摊下来可能只有几十上百个实例。这种数据规模下我一般不会直接上大模型而是老老实实从轻量级模型YOLOv8n/s开始配合预训练权重和合理的增广策略来训练。1.2 为什么VOC格式至今还是绕不开VOC格式是Pascal VOC竞赛定义的标注格式结构上是“图片 XML标注文件”的配对。每张图对应一个同名XML文件里面记录图片尺寸、目标类别名称 和边界框坐标bndbox坐标是绝对像素值比如xmin、ymin、xmax、ymax。这套格式到今天仍然大量存在于公开数据集和工业数据交付中不是因为它多先进而是因为兼容性最好。数据标注工具LabelImg、X-AnyLabeling、Roboflow导出几乎都支持VOC很多工厂和算法外包团队交付的数据也是VOC格式。你拿到的这套轮胎缺陷数据集走的就是这条老路所以第一步不是抱怨“为什么不是YOLO的txt”而是老老实实把VOC转成YOLO训练需要的格式。话说回来VOC格式虽然老有个好处是信息量比YOLO的txt标注大。XML里有图片尺寸有完整的对象属性结构你甚至可以手动往里面加额外字段比如缺陷等级、是否被遮挡等。这些信息在你做数据清洗、做难例分析时非常有用。2. 动手前的准备工作数据集验收与清洗2.1 文件结构第一关JPEGImages Annotations拿到数据集后先看目录结构。正规的VOC数据集至少应该有这三个目录或其中一部分JPEGImages存放所有图片文件命名通常为000001.jpg这种六位编号Annotations存放所有XML标注文件文件名和图片名一一对应ImageSets/Main存放train.txt、val.txt、test.txt记录各分区的文件名列表我这套轮胎缺陷数据如果结构完整train.txt里应该有884行test.txt里有46行。如果没提供ImageSets目录那就默认884张在JPEGImages、46张在Annotations里的某些文件对应该看着办反正最终转换后你要自己重新划分。验收时我习惯写一条命令核对标注和图片是否一一对应# 在数据集根目录下执行 ls JPEGImages | sed s/\.jpg$// | sort /tmp/img_ids.txt ls Annotations | sed s/\.xml$// | sort /tmp/xml_ids.txt comm -3 /tmp/img_ids.txt /tmp/xml_ids.txt这条命令会把“有图没标注”或“有标注没图”的文件名列出来。如果有输出说明数据对不齐训练时会报FileNotFoundError或者干脆残废必须先处理。常见的坑是图片是.jpg但XML里记录的是.jpeg或者文件名有大小写不一致的问题。2.2 标注质量抽查类别和框不能想当然VOC格式数据集的另一个暗坑是类别名称不统一。比如同一个缺陷某几张图里叫bubble另外几张叫Bubble还有的标成gas_pore那训练时模型会当成两个甚至三个类别来处理。所以转换格式之前先扫描所有XML里的 标签统计类别分布。import xml.etree.ElementTree as ET import glob from collections import Counter names Counter() for xml_path in glob.glob(Annotations/*.xml): root ET.parse(xml_path).getroot() for obj in root.findall(object): names[obj.find(name).text.strip()] 1 print(names)跑完这个脚本你就能清楚看到所有类别名称和实例数量。若发现同一类缺陷存在多种写法直接写个字典做归一化映射把这些名称统一。缺陷检测的场景里类别写错了是最冤的事故——标注里有个类叫“正常”训练集里全是有缺陷的图这个类就几乎没有正样本模型根本学不会。还有一点要留神检查框坐标是否越界。VOC的bndbox坐标理论上应该在图片范围内但实际交付的数据经常出现xmax超过图片宽度、ymax超过图片高度甚至出现xmin xmax这种低级错误。转换YOLO格式时越界坐标会导致归一化后的w或h出现负值或大于1训练时会直接报错或让loss变成NaN。我建议在转换脚本里直接加上坐标裁剪把xmin、ymin、xmax、ymax都限制在[0, width]和[0, height]内把非法框直接丢弃。3. 核心实操VOC转YOLO格式并划分数据集3.1 归一化坐标的计算原理YOLO系列使用的标注格式是txt文件每张图对应一个同名txt文件里每一行代表一个目标格式是class_id x_center y_center width height注意这四个数值都是归一化到0~1之间的相对值而且要除以图片宽和高。转换公式不复杂x_center (xmin xmax) / 2 / widthy_center (ymin ymax) / 2 / heightbox_width (xmax - xmin) / widthbox_height (ymax - ymin) / height这里有一个经典误区有人把width和height当成图片的宽高直接拿bbox的绝对像素宽高除以416或640这种训练尺寸来归一化这是错的。归一化必须基于原图的真实宽高跟训练时的resize无关。因为模型加载图片时会把原图等比缩放到训练尺寸并同步调整标注你如果提前用训练尺寸归一化经过模型内部的letterbox后坐标就错位了。3.2 转换脚本一套能直接用代码下面是一个精简但完整的VOC转YOLO脚本我平时拿这种脚本改改就能用核心就三件事解析XML、归一化坐标、写入txt。import xml.etree.ElementTree as ET import glob import os CLASS_NAMES [bubble, crack, inclusion, lack_of_glue, scratch] # 按你的类别清单改 def convert_voc_bbox(xml_path, out_dir): root ET.parse(xml_path).getroot() img_width int(root.find(size/width).text) img_height int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in CLASS_NAMES: continue class_id CLASS_NAMES.index(name) bndbox obj.find(bndbox) xmin max(int(float(bndbox.find(xmin).text)), 0) ymin max(int(float(bndbox.find(ymin).text)), 0) xmax min(int(float(bndbox.find(xmax).text)), img_width) ymax min(int(float(bndbox.find(ymax).text)), img_height) if xmax xmin or ymax ymin: continue x_center (xmin xmax) / 2 / img_width y_center (ymin ymax) / 2 / img_height box_w (xmax - xmin) / img_width box_h (ymax - ymin) / img_height lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) txt_path os.path.join(out_dir, os.path.splitext(os.path.basename(xml_path))[0] .txt) with open(txt_path, w) as f: f.write(\n.join(lines)) xml_files glob.glob(Annotations/*.xml) os.makedirs(labels, exist_okTrue) for xml_file in xml_files: convert_voc_bbox(xml_file, labels)这段代码有几个细节值得解释bndbox里的坐标在部分数据集中可能被标成浮点数所以先用float再转int防止解析报错坐标越界通过max/min做裁剪类别不在清单里的对象直接跳过这样可以顺手滤掉你不关心的标注。如果不想自己写YOLOv5官方仓库里的voc_label.py也能干这事但那个脚本比较老还要手动改类别列表我自己更喜欢上面这种简单直接的版本。3.3 数据切分别把验证集忘了转换完成后标签文件就和图片文件一一对应了。接下来把整个数据分成训练、验证、测试三个集合。原数据集里的46张测试图原封不动当test但要把884张训练图再拆出约10%做val。我一般是按9比1随机划分并保证每个类别在val里都有一定实例。如果你的缺陷类别分布很不均匀最好用分层抽样的思路按类别分布去取。import random, os from collections import defaultdict image_files [f for f in os.listdir(JPEGImages) if f.endswith(.jpg)] random.seed(42) random.shuffle(image_files) val_count int(len(image_files) * 0.1) # 约88张 val_files image_files[:val_count] train_files image_files[val_count:] with open(train.txt, w) as f: f.write(\n.join([os.path.abspath(os.path.join(JPEGImages, name)) for name in train_files])) with open(val.txt, w) as f: f.write(\n.join([os.path.abspath(os.path.join(JPEGImages, name)) for name in val_files])) # test.txt 直接用原始提供的46张图片路径划分完之后记得把三个列表文件放到一个容易找的位置因为YOLO训练时要用。Ultralytics框架其实不强制要求这三个文件它更习惯的目录结构是images/train、images/val、labels/train、labels/val这种。我自己用下来还是按Ultralytics的习惯把图片和标签按分区目录放好更省心dataset/ ├── images/ │ ├── train/ 796张 │ ├── val/ 88张 │ └── test/ 46张 └── labels/ ├── train/ ├── val/ └── test/这样组织好后写一个data.yaml就能直接开训。4. YOLOv8训练自己的轮胎缺陷模型4.1 数据集配置data.yaml怎么写Ultralytics YOLOv8的数据配置文件非常简单我直接贴一个实例path: /path/to/your/dataset train: images/train val: images/val test: images/test names: 0: bubble 1: crack 2: inclusion 3: lack_of_glue 4: scratchnames这个列表必须和转换脚本里的CLASS_NAMES顺序完全一致否则模型会把类别名张冠李戴。这个错误特别隐蔽因为训练不会报错但推理时输出的类别就全乱了。4.2 训练参数选择小样本别头铁针对930张规模的数据集训练参数我有几条默认建议使用预训练权重从yolov8n.pt或yolov8s.pt开始不要从零训练。小数据从零训骨干网络根本学不到有效的底层特征收敛极慢泛化也差。输入分辨率设为640起步。轮胎缺陷有些是小目标如果显存允许建议试试1280精度会明显提升但训练时间成倍增加我一般是在640训完后再用1280微调几个epoch。epochs设100左右配合patience20的早停。小数据很容易过拟合训练曲线通常在40~60轮后开始收敛太早停学不到位太晚冒风险过拟合。batch size能多大就多大但不要超过显存。16或32都可以batch太小BN层统计量不稳收敛会抖。数据增强用Ultralytics默认配置就行。mosaic、hsv扰动、随机翻转对小样本训练几乎是必需品。但注意训练后期如果loss不再下降可以尝试关掉mosaic再训几轮有时能稳定收敛。下面是我常用的训练命令yolo train data/path/to/data.yaml modelyolov8n.pt epochs120 batch16 imgsz640 patience20 projectruns/tire_defect nameexp1训练过程中盯住val loss而不是train loss。train loss低但val loss不降就是过拟合的信号。这个时候不要盲目加epoch而是返回去检查数据是不是某个类样本太少是不是标注框有大量漏标小样本训练中数据质量的影响远大于模型结构。5. 评估结果与部署的一点体会5.1 测试集指标怎么读才靠谱训练结束后Ultralytics会在runs/tire_defect/exp1/weights/下生成best.pt和last.pt。评估记得用best.ptyolo predict modelruns/tire_defect/exp1/weights/best.pt sourcedataset/images/test save_txtTrue save_confTrue测试集46张图最终结果会有mAP50和mAP50-95两项指标。mAP50对框位置没那么敏感只要IoU超过0.5就算正确适合看“有没有检测到”mAP50-95是IoU从0.5到0.95的平均更严格能看出框的贴合程度。在轮胎缺陷这种小目标为主的任务里mAP50和mAP50-95差距如果太大说明框的回归精度不行可以试试调高输入分辨率或者用CIoU loss调整。还有一件事必须做可视化预测结果。统计指标再好看也要肉眼过一遍预测图。重点看这些有没有把轮胎纹理、胎毛、水渍误判成缺陷有没有同一缺陷被重复框出多个框有没有两个靠得很近的缺陷被合并成一个框。这些现象在指标上可能只体现为precision或recall掉一点但在工厂现场就是致命的误检或漏检。5.2 工业场景部署的思路参考模型训完部署才是大头。如果是要跑到产线的工控机上我建议先导出ONNX再转成TensorRTyolo export modelbest.pt formatonnx imgsz640ONNX导出后可以用TensorRT加速特别是工控机如果用的是NVIDIA显卡推理速度能比PyTorch快不少。轮胎产线上相机一般固定安装光源稳定背景变化不大这种场景下可以适当调低置信度阈值比如0.25提高recall再用较大的NMS IoU阈值比如0.7减少重叠框。但具体阈值一定要基于你们现场的实际过检率来调不能只看测试集表现。如果是纯CPU环境NCNN或OpenVINO是更现实的选择。YOLOv8n在CPU上单张推理大约几十毫秒到一百多毫秒对于节拍不快、一次拍一张的产线勉强够用。不过要是相机分辨率高、检测点多我还是建议上GPU。6. 实操中的常见坑与避坑心得这个数据集看似简单实操中的坑比想象中多。我列几个典型的都是自己踩过的标注框错位。转换后不检查直接训练训练集loss死活降不下来可视化一看框全偏了。原因可能是XML里的坐标基于原图分辨率但图片被人为压缩过标注没跟着同步缩放。解决办法是训练前写脚本统计所有标注框面积与图片面积的比值如果出现异常大的框或者大量框集中在图片边缘就要警惕。类别名大小写不一致。前面提过这种问题最常见于多次标注、多人协作的数据集。最好在转换前统一小写并且把明显的同义词合并掉。漏标的负样本。有些正常的轮胎面没有标注模型会把正常区域当成背景但如果测试集里出现的“正常”图偏少模型容易把正常纹理当作缺陷。这时候可以在训练集中加入一批没有标注的“干净”图片让模型见见正常的样本长什么样。这个步骤对降低误检率立竿见影。小数据集下训练抖动大。884张图训练即便有验证集每轮验证集的loss也可能波动明显。最好的办法是多跑几次对比或者用K折交叉验证评估稳定性别因为一次训练结果好就急着定方案。我在实际训练这套轮胎缺陷数据时比较顺手的路径是先用yolov8n跑通流程确认数据和格式都没问题再切到yolov8s或yolov8m冲一下精度最后根据实时性要求决定部署版本。如果项目时间紧张直接在yolov8n上把分辨率和增广调好效果通常也够用。说到底一套规范的数据处理流程比反复尝试新模型带来的提升更实在。本文还有配套的精品资源点击获取
返回列表