ARTICLE DETAIL

资讯详情

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

交通标志数据集训练YOLO:VOC/COCO/YOLO格式转换与划分脚本

交通标志数据集训练YOLO:VOC/COCO/YOLO格式转换与划分脚本 简介本资源面向目标检测初学者与需要交通标志识别实战项目的开发者提供一套真实场景下的YOLO交通标志识别数据集可直接用于YOLO系列模型训练与验证。数据经labelimg精细标注标注框质量高场景覆盖丰富并同步提供voc(xml)、coco(json)与yolo(txt)三种格式标签分别存放于独立文件夹便于不同框架直接读取。压缩包共约2000个文件以1985个xml标注文件为主另含少量txt、html与py脚本整体约807.7MB。资源附带YOLO环境搭建教程、训练案例教程以及训练集、验证集、测试集划分脚本可按需自行划分数据快速跑通从环境配置到模型训练的全流程。目前已有800人学习下载适合希望掌握目标检测数据准备与训练流程、需要现成交通标志数据做课程设计或项目实践的用户参考使用。1. 3000 张交通标志图片到手后先别急着喂给 YOLO很多人拿到一个交通标志识别数据集压缩包第一反应是解压、找images和labels、改个data.yaml路径就开训。我见过太多人卡在第一步图片有了标签格式对不上标签有了训练集和验证集划分不合理划分完了类别编号和names列表错位。最后模型训出来 mAP 看着还行一上路测试全是误检。这个标题里的核心不是“3000 张图片”而是“voc、coco、yolo 三种格式标签 划分脚本 训练教程”这一整套配套。它解决的是从原始标注到 YOLO 可训练数据之间的格式鸿沟和流程断层。适合手里有标注数据、想跑通交通标志检测但被格式转换和划分逻辑卡住的工程师也适合想拿一个完整数据集练手 YOLO 训练全流程的新手。下面按我实际处理这类数据集的顺序把格式转换、划分脚本、训练参数和踩坑点拆开讲。2. 三种标签格式到底怎么选VOC、COCO、YOLO 的转换逻辑与实操交通标志识别数据集里同时给 VOC、COCO、YOLO 三种格式不是让你三选一而是让你根据手头工具链灵活切换。VOC 是 XML 单文件描述一张图COCO 是一个大 JSON 管所有图YOLO 是每张图一个 txt。很多人拿到后直接找 YOLO 格式的 txt 开训结果发现类别编号从 1 开始而 YOLO 默认从 0 开始训练时类别全错位。这一章把三种格式的转换和校验讲透。2.1 VOC 转 YOLO坐标归一化和类别映射的两个关键参数VOC 的 XML 里bndbox是绝对像素坐标xmin, ymin, xmax, ymaxYOLO 需要的是归一化后的x_center, y_center, width, height且值在 0 到 1 之间。转换时最容易翻车的地方是类别名到类别 ID 的映射表没有统一导致同一类交通标志在不同图片里被编成不同 ID。import xml.etree.ElementTree as ET import os # 类别映射表必须与后续 data.yaml 中的 names 顺序完全一致 CLASS_MAP { speed_limit: 0, stop: 1, yield: 2, pedestrian: 3, traffic_light: 4 } def voc_to_yolo(xml_path, img_w, img_h, output_txt_path): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): cls_name obj.find(name).text.strip() if cls_name not in CLASS_MAP: continue # 忽略未定义类别避免训练时越界 cls_id CLASS_MAP[cls_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) # 归一化并计算中心点和宽高 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}) with open(output_txt_path, w) as f: f.write(\n.join(lines))这段代码里CLASS_MAP是全局唯一的类别映射必须和后面data.yaml里的names列表顺序一模一样。img_w和img_h要从对应图片读取不能硬编码因为交通标志数据集里图片分辨率可能不统一。归一化时保留 6 位小数足够YOLO 训练时对精度不敏感但类别 ID 错一位就是灾难。常见做法是先用PIL或cv2批量读取图片尺寸再逐张转换。2.2 COCO 转 YOLO处理iscrowd和类别 ID 重映射COCO 格式的 JSON 里annotations中的category_id往往不是从 0 连续排列的比如交通标志数据集可能用 1 到 5 表示五类但中间缺了某个 ID。YOLO 要求类别 ID 从 0 开始连续所以必须做重映射。另外iscrowd1的标注通常表示密集小目标交通标志场景下建议直接跳过否则归一化后框会重叠严重。import json import os def coco_to_yolo(coco_json_path, output_dir, img_dir): with open(coco_json_path, r) as f: data json.load(f) # 建立原始 category_id 到连续 0-based id 的映射 categories sorted(data[categories], keylambda x: x[id]) cat_id_map {cat[id]: idx for idx, cat in enumerate(categories)} # 建立 image_id 到文件名的映射 img_id_to_info {img[id]: img for img in data[images]} # 按 image_id 分组 annotations from collections import defaultdict img_anns defaultdict(list) for ann in data[annotations]: if ann.get(iscrowd, 0) 1: continue img_anns[ann[image_id]].append(ann) for img_id, anns in img_anns.items(): info img_id_to_info[img_id] img_w, img_h info[width], info[height] file_name os.path.splitext(info[file_name])[0] .txt lines [] for ann in anns: cls_id cat_id_map[ann[category_id]] x, y, w, h ann[bbox] # COCO bbox 是左上角 x,y 和宽高 x_center (x w / 2.0) / img_w y_center (y h / 2.0) / img_h width w / img_w height h / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(os.path.join(output_dir, file_name), w) as f: f.write(\n.join(lines))注意 COCO 的bbox格式是[x, y, width, height]不是[xmin, ymin, xmax, ymax]这是和 VOC 最大的区别。cat_id_map用排序后的 categories 重建连续 ID保证和data.yaml的names顺序一致。如果数据集里某些类别在 JSON 中没出现但names里写了训练时不会报错但该类永远学不到建议先统计各类别实例数再决定是否保留。2.3 用划分脚本切分训练集和验证集别让同一场景的图片同时出现在两边交通标志数据集里同一段路连续拍摄的图片高度相似。如果随机按 8:2 划分训练集和验证集里会出现几乎一样的图片验证集 mAP 虚高实际部署就翻车。我一般用划分脚本按“场景”或“连续帧”分组后再切分保证验证集里的交通标志场景在训练集中没有完全重复的。import os import random import shutil from collections import defaultdict def split_dataset(img_dir, label_dir, output_dir, train_ratio0.8, seed42): random.seed(seed) # 假设图片文件名前缀代表场景如 scene01_0001.jpg scene_groups defaultdict(list) for fname in os.listdir(img_dir): if not fname.lower().endswith((.jpg, .png, .jpeg)): continue scene_key fname.split(_)[0] # 按实际命名规则调整 scene_groups[scene_key].append(fname) scenes list(scene_groups.keys()) random.shuffle(scenes) split_idx int(len(scenes) * train_ratio) train_scenes set(scenes[:split_idx]) val_scenes set(scenes[split_idx:]) for phase, scene_set in [(train, train_scenes), (val, val_scenes)]: img_out os.path.join(output_dir, images, phase) lbl_out os.path.join(output_dir, labels, phase) os.makedirs(img_out, exist_okTrue) os.makedirs(lbl_out, exist_okTrue) for scene in scene_set: for fname in scene_groups[scene]: shutil.copy(os.path.join(img_dir, fname), os.path.join(img_out, fname)) lbl_name os.path.splitext(fname)[0] .txt lbl_src os.path.join(label_dir, lbl_name) if os.path.exists(lbl_src): shutil.copy(lbl_src, os.path.join(lbl_out, lbl_name))这个脚本的关键是scene_key的提取规则要根据数据集实际命名来改。如果文件名没有场景前缀可以用图片的感知哈希或简单的颜色直方图做聚类把相似图片分到同一组。seed固定后划分结果可复现方便对比不同训练参数。划分完成后检查train和val的类别分布是否接近如果验证集里某一类完全缺失需要调整划分比例或重新分组。3. 训练教程里的参数怎么设从 data.yaml 到 YOLO 训练命令格式转换和划分做完接下来是配置训练。很多人直接抄一份data.yaml就开跑结果类别数对不上、路径写错、预训练模型加载失败。这一章把data.yaml的每个字段、训练命令的关键参数和常见报错讲清楚。3.1 data.yaml 的五个必填字段与路径陷阱YOLO 训练依赖一个 YAML 文件描述数据集路径和类别信息。交通标志数据集常见做法是path: /home/user/traffic_sign_dataset train: images/train val: images/val nc: 5 names: [speed_limit, stop, yield, pedestrian, traffic_light]path是数据集根目录train和val是相对于path的图片目录。注意这里写的是图片目录YOLO 会自动去找同级的labels目录但要求images/train对应的标签在labels/train目录结构必须严格对应。nc是类别数必须等于names列表长度也等于转换时CLASS_MAP的最大 ID 加一。如果nc写错训练时分类头维度不对会直接报IndexError或静默学错类别。提示path建议用绝对路径相对路径在不同工作目录下执行训练命令时容易找不到文件。如果数据集在 Windows 上准备、Linux 上训练注意路径分隔符和大小写。3.2 训练命令与 batch size 的显存换算假设用 YOLOv8常见训练命令是yolo detect train \ data/home/user/traffic_sign_dataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ device0 \ workers4 \ projecttraffic_sign_runs \ nameexp01modelyolov8n.pt是预训练权重交通标志数据集 3000 张不算大用 nano 或 small 版本足够从零训练容易过拟合。imgsz640是输入分辨率如果图片里交通标志很小可以提到 1280但显存占用翻倍。batch16在 8GB 显存上跑 640 分辨率通常够用如果报CUDA out of memory先降 batch 到 8 或 4再考虑降imgsz。workers是数据加载线程数设成 CPU 核心数的一半左右太高反而拖慢。训练过程中重点看mAP50和mAP50-95曲线。交通标志识别里小目标多mAP50-95通常比mAP50低不少如果两者差距过大说明框回归不准可以检查标注框是否贴合标志边缘。另外cls_loss如果一直不降大概率是类别映射错了回头查data.yaml的names和转换脚本的CLASS_MAP是否一致。3.3 用混淆矩阵验证类别是否学混训练结束后YOLO 会在runs/detect/exp01下生成confusion_matrix.png。交通标志里形状相似的类别比如限速 50 和限速 60容易互相混淆。如果混淆矩阵显示这两类交叉严重说明特征区分度不够可以尝试增加这两类的样本数量、在数据增强里加入随机缩放和色彩抖动、或者换更大的模型。注意混淆矩阵的归一化方式不同数值会变但相对趋势一致不要纠结绝对值。4. 避坑与排查交通标志数据集训练中最容易翻车的五个点这一章按我实际踩过的坑来写每条按现象、原因、解决三段式。交通标志数据集有它的特殊性类别不平衡、小目标多、背景重复下面五个问题几乎每次都会遇到至少两个。4.1 现象训练 loss 正常下降但验证集 mAP 始终为 0原因验证集标签路径不对YOLO 找不到验证集的 txt 文件或者 txt 文件为空。常见情况是划分脚本只复制了图片没复制对应标签或者标签文件名和图片文件名不一致比如图片是.jpg标签是.JPG.txt。解决在data.yaml同级目录下跑一个检查脚本统计images/val和labels/val的文件数量是否一致并抽查几个 txt 文件内容是否非空。如果标签缺失回到划分脚本补上复制逻辑。4.2 现象训练到一半报CUDA out of memory但 batch 已经很小原因YOLO 在训练过程中会缓存数据加载器的内存如果workers设得太大或者图片分辨率不统一导致某些 batch 实际占用更高就会在后期爆显存。交通标志数据集里如果混入了高分辨率图片即使imgsz640数据加载时也会先读原图再缩放峰值内存可能超限。解决先把workers降到 2 或 0 试跑确认不是数据加载线程导致。然后在训练前用脚本统一把所有图片缩放到最长边不超过 1280再重新生成标签归一化坐标不变但图片尺寸变了要重新算。如果还不行用batch4加accumulate4模拟大 batch。4.3 现象模型把背景里的圆形广告牌识别成交通标志原因交通标志数据集里负样本不足模型没学会区分“圆形标志”和“圆形广告牌”。YOLO 默认会把所有非目标区域当背景但如果训练集里背景单一模型泛化差。解决在训练集里加入不含交通标志的负样本图片或者用数据增强里的mosaic和mixup增加背景多样性。另外可以调低conf阈值观察误检如果误检框的置信度普遍在 0.3 到 0.5 之间说明模型不确定需要更多负样本。4.4 现象训练完导出模型部署推理速度远低于预期原因训练时用了imgsz1280部署时没改或者模型导出成 ONNX 后没做量化。交通标志识别在车载端通常要求实时YOLOv8n 在 640 分辨率下 GPU 推理能到几百 FPS但 1280 就掉到几十。解决部署前用yolo export导出 ONNX 或 TensorRT指定imgsz640和halfTrueFP16 量化。如果精度掉得厉害再考虑 INT8 量化但需要校准集。实测交通标志场景下 FP16 量化精度损失很小速度提升明显。4.5 现象同一张图片多次推理结果框的类别偶尔跳变原因模型对某些模糊交通标志的置信度在阈值附近波动后处理时 NMS 的iou阈值设得太高或太低都会导致框合并异常。另外如果图片预处理时归一化方式不一致训练用 0-1推理用 0-255也会导致输出不稳定。解决固定推理时的预处理流程确保和训练时一致。NMS 的iou阈值交通标志场景建议设 0.5 到 0.6太低会漏检相邻标志太高会保留重复框。如果类别跳变严重可以在后处理里对同一位置的框做类别投票取置信度最高的类别。5. 进阶技巧用 TTA 和类别权重把交通标志 mAP 再提几个点训练跑通之后如果想把 mAP 从 0.75 推到 0.85 以上单靠调学习率不够。交通标志数据集里小目标多、类别不平衡我一般会加两个手段测试时增强TTA和类别权重调整。TTA 是在推理时对同一张图做多种变换水平翻转、多尺度把结果融合能稳定提升 1 到 3 个点。YOLO 官方支持augmentTrue开启 TTA但速度会慢两三倍适合离线评估或对实时性要求不高的场景。from ultralytics import YOLO model YOLO(traffic_sign_runs/exp01/weights/best.pt) # 开启 TTA 推理augmentTrue 会做多尺度翻转融合 results model.predict( sourcetest_images/, imgsz640, conf0.25, iou0.5, augmentTrue, # TTA 开关 saveTrue )augmentTrue时 YOLO 会对每张图做水平翻转和不同尺度缩放然后对框做加权融合。注意 TTA 对已经过拟合的模型提升有限如果验证集 mAP 和训练集差很多先解决过拟合再上 TTA。类别权重调整则是针对样本少的类别在损失函数里给更高权重。YOLO 默认不直接暴露类别权重参数常见做法是过采样少数类图片或者用copy_paste增强把少数类标志粘贴到更多背景上。另一个实用技巧是冻结 backbone 做微调。如果交通标志数据集只有 3000 张从头训练容易过拟合可以先用 COCO 预训练权重冻结前几层只训练检测头。YOLOv8 里通过freeze参数控制yolo detect train \ datadata.yaml \ modelyolov8n.pt \ freeze10 \ epochs50 \ imgsz640 \ batch16freeze10表示冻结前 10 层具体层数要看模型结构nano 版本总共就几十层冻太多会导致学不动。我一般先冻 5 到 10 层跑 20 个 epoch观察验证集 mAP 是否还在涨如果涨了就解冻全部再跑 30 个 epoch。这套流程在交通标志数据集上比直接全量训练稳定得多尤其当图片数量少于 5000 张时。最后说一个我自己的习惯每次改完数据或参数先跑 5 个 epoch 看 loss 曲线和验证集 mAP 趋势确认没有报错和明显异常再跑完整训练。交通标志识别这个方向数据质量比模型结构重要得多3000 张标注准确的图片加上合理的划分比 10000 张脏数据训出来的模型靠谱。希望帮到你。本文还有配套的精品资源点击获取
返回列表