☰
自动驾驶数据集如何转换为YOLOv5目录格式?一文讲清验收与避坑
2026/10/5 11:28:14 网站建设 项目流程

简介:面向目标检测学习者和自动驾驶开发者,一份按YOLOv5目录规范整理的大型自动驾驶道路信息检测数据集,覆盖卡车、行人、交通信号灯、车辆等11个常见类别,训练集与验证集划分完整,可直接用于YOLO系列模型训练与评估。压缩包约493MB,共计2000个文件,其中绝大多数为YOLO格式的txt标注文件(1999个),另有1个Python可视化脚本,无需修改即可随机读取图片绘制并保存边框结果,便于快速核查标注质量;每个标签对应一张图像中的目标类别与归一化边界框坐标。训练集包含21031张图像的标注,验证集包含5266张,规模充足;目录按datasets-images-train与datasets-images-val分开组织,11个类别名称的txt文本文件也一并提供,方便对照编号。标注边界框清晰完整,覆盖道路、车辆、人员、交通设施等多样场景,每幅图像通常包含多个目标,适合自动驾驶多目标密集检测研究,能省去自行收集、清洗与格式转换的时间。目前已有174人学习下载,对入门自动驾驶感知或目标检测实战均具有一定的实用价值。

1. 拿到手先别急着训练:这个数据集到底能解决什么

做自动驾驶目标检测的都知道,模型能不能收敛、收敛后能不能落地,一半的命都押在数据集上。很多人从网上下载或采购数据集时,第一眼只关心“有多少张图”“类多不多”,真正把压缩包解开、跑通train.py之后才发现:标注格式不对、类别顺序和训练脚本对不上、验证集压根没划分,甚至图像的宽高和标注的归一化坐标根本不是一个坐标系。这个标题里写得很清楚的一个点,就是“YOLOV5目录格式”——意味着 images 和 labels 已经按 YOLO 的训练习惯排好,train/val 也切好了,拿过来改一下数据配置就能开跑。11 类别对于自动驾驶道路场景属于中等粒度,既不会像 80 类那样稀疏难收敛,也不会像“车、人”二分类那样失去工程意义。本文就围绕这套格式,讲清楚如何验收它、怎么把它转成标准 YOLOV5 训练目录,以及那些最容易「翻车」的细节。

2. YOLOV5目录格式到底长什么样:从目录树到单行标注

2.1 images 与 labels 的镜像结构是第一道门槛

YOLOV5 的数据集约定,核心是 images 和 labels 两个目录镜像存在。训练时dataset.py会根据图片路径自动找同名的 txt 标注文件:如果图片在train/images/0001.jpg,标注必须在train/labels/0001.txt。文件同名、后缀不同,目录一一对应。大多数打回票的数据集,问题就出在“我明明给了标注,但训练时一条目标都没读到”,十有八九是 labels 目录没和 images 对齐。

一个合格的 YOLOV5 目录格式,一般长这样:

dataset/ ├── images/ │ ├── train/ │ │ ├── 0001.jpg │ │ ├── 0002.jpg │ │ └── ... │ └── val/ │ ├── 0001.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 0001.txt │ │ ├── 0002.txt │ │ └── ... │ └── val/ │ ├── 0001.txt │ └── ... ├── data.yaml └── classes.txt

注意这里的data.yaml是训练入口,classes.txt是给人看的类别清单。两者必须完全一致,类别顺序错一个,整个模型的输出头就跟着错位。这个目录结构里,最容易忽略的是“同名同前缀”:0001.jpg必须对应0001.txt,不能出现0001.jpg配0001_label.txt这种情况。如果你拿到手的压缩包解压后是这种命名,我是建议写个脚本统一批量改名,而不是手动改几十个文件。

2.2 单行标注的五个数字:类别ID、x/y、宽高各是什么含义

YOLO 标注格式,每个 txt 文件里有若干行,每行五个数字,用空格分隔:

0 0.45 0.35 0.20 0.30

从左到右分别是:类别 ID、归一化后的中心点 x 坐标、归一化后的中心点 y 坐标、归一化后的框宽、归一化后的框高。所有坐标都是相对于图片原始宽高的比例,取值范围在 0 到 1 之间。这是 YOLO 格式与 VOC 的 xmin/ymin/xmax/ymax 最大的区别,也是后续所有校验脚本的检查核心。

类别 ID 从 0 开始计数,不是从 1 开始。标题里说 11 类别,那类别 ID 的范围就是 0 到 10,共 11 个数字。很多人写data.yaml时习惯把类别写成 1 到 11,这会让第一个类别直接被吞掉,训练出来所有类别都错位一个索引,且 mAP 表现极其诡异。这种错误从 loss 曲线上基本看不出来,因为模型能正常收敛,但推理输出的框全部对应到错误的类名上,属于“最阴间的翻车现场”。

我一般拿到数据集后,第一件事不是打开图片看标注画得准不准,而是写个脚本扫描所有 txt,统计每个类别 ID 的出现频次,顺便检查有没有超出 0~1 范围的坐标。这一步能过滤掉大部分低级损坏。

2.3 data.yaml 里的类别顺序和训练脚本强绑定

YOLOV5 的data.yaml通常长这样:

train: ./images/train val: ./images/val nc: 11 names: ['person', 'car', 'truck', 'bus', 'motorcycle', 'bicycle', 'traffic_light', 'traffic_sign', 'road_cone', 'barrier', 'others']

这里的names列表顺序,必须和所有标注 txt 里的类别 ID 严格对应。训练时 YOLO 会自动把第一个名字映射为类别 0,第二个映射为类别 1。如果你的标注 txt 里类别 0 实际上代表的是“car”,但names列表第一位写的却是“person”,模型训练不会报错,但所有评估结果都会“张冠李戴”。

这个顺序问题,常见于从多个数据源拼凑出来的数据集。比如某个数据源的类别顺序是“car, person, bike”,另一个是“person, car, bike”,两组标注文件混在同一目录下,就会出现同一个类别 ID 对应不同语义的灾难。如何避免?我的做法是写一个校验脚本,从每个标注文件里提取出现过的类别 ID,与classes.txt做交叉比对,再抽样打开几张图人工确认几个框的语义是否匹配。不要省这一步,省了后面全是泪。

还有一个细节:train和val的路径,在data.yaml里建议写绝对路径,或者写相对于运行位置的路径。我之前有段时间习惯写绝对路径,但数据集迁移到服务器后路径全变了,又得改一遍。后来统一改成相对路径:train: ./images/train,并保证在项目根目录下运行训练命令。这两种方式都可以,但建议整个团队统一一个规范,不然每次换机器都在改配置文件。

3. 大型自动驾驶道路数据集的验收:11类别怎么分布才算健康

3.1 类别设计合理性:先看道路要素覆盖,再看平衡性

标题里写“大型自动驾驶道路信息检测(11类别)”。那么在验收时,首先要判断这 11 个类别是否覆盖了目标场景的核心要素。自动驾驶道路场景通常关注:车辆类(小汽车、卡车、货车、公交车)、行人类(行人、骑行者)、道路设施类(交通灯、交通标志、路锥桶、护栏、道路边界)。这 11 类是否合理,很大程度上取决于项目需求——如果你的产品只需要检测红绿灯和行人,那 11 类里再多的车辆类别都是噪音;如果你做的是辅助驾驶的通用感知,那么车辆、行人、骑行者的细分类别就很有必要。

判断原则其实很简单:这 11 个类别分别对应下游哪个模块?每个类别被调用的频率是多少?没有下游需求支撑的类别,建议直接从训练集里排除或合并,否则白白增加模型的输出头数量,还可能在推理时产生误检。反过来说,如果 11 类里缺少关键要素,比如完全没有“行人”这一类别,那这个数据集做得再大再“大型”,对你的项目也要打个大大的问号。

3.2 写脚本统计类别分布:识别长尾类别和标注瓶颈

数据集验收必须量化,不能靠肉眼“看着挺多的”。我常用的验收脚本是一个 Python 脚本,遍历所有标注文件,统计每个类别出现的框数量与覆盖的图片张数。这个脚本不复杂,十几行就能跑出结果:

import os from collections import Counter label_dir = "labels/train" cls_counter = Counter() img_counter = Counter() for txt_file in os.listdir(label_dir): if not txt_file.endswith(".txt"): continue img_counter[txt_file] = 0 with open(os.path.join(label_dir, txt_file), "r") as f: for line in f: parts = line.strip().split() cls_id = int(parts[0]) cls_counter[cls_id] += 1 img_counter[txt_file] += 1 print("类别 ID 出现次数:", cls_counter) print("有标注的图片数:", len(img_counter))

跑完这个脚本,重点看两个数:每个类别的框总数、有标注的图片数。如果某一个类别框数量特别少,比如只有一两百个,而其他类别都有几千个,这就是长尾类别。长尾类别会让模型对该类的召回率非常差,训练时 loss 会被高频类别主导。

我在实际项目里遇到过最典型的例子:11 类数据里,“交通标志”这个类别框数量占了 43%,而“自行车”只占 0.8%。模型训练出来,自行车类别在验证集上的 mAP 通常是 0——不是模型不行,是数据里这个类别太稀少了。这种情况下的短期解法是给这个类别加重复采样权重,长期解法是定向补充该类别的新数据。如果没有补充数据的条件,建议把过稀少的类别合并到相近的父类中,比如“自行车”和“摩托车”合并成“两轮车”,这样模型压力会小很多。

3.3 train/val 划分的隐藏规则:同一个场景不能同时出现在两边

标题里写了“包含训练集、验证集”,但切分质量才是关键。很多人只看验证集图片数量够不够,却不问一句:同一个路口、同一台车同时出没在训练集和验证集里,验证指标还有意义吗?数据泄漏是目标检测数据集里最隐蔽的坑——如果训练集和验证集里包含同一段视频的相邻帧,模型在验证集上的表现会被严重高估,部署到新的真实场景时立刻打回原形。

判断方法也很直接:抽样比对两侧图片的文件名前缀。自动驾驶数据集的图片通常按“路段/时间戳”命名,比如road001_0001.jpg、road001_0002.jpg。如果 train 和 val 目录里都大量出现road001_前缀的图片,说明划分时是按文件顺序随机切的,而不是按场景切分。这个随机切分法在通用目标检测里问题不大,但自动驾驶数据的场景相关性太强,前后几帧几乎同一个角度,泄漏不可避免。

我处理自动驾驶数据集时,会先按“场景分组”再做划分。把同一路段、同一时间段拍到的图片视作一个 group,然后按 group 分配训练和验证——保证同一个 group 的图片要么全进 train,要么全进 val。至于 8:2 还是 9:1,要看数据总量,数据量大可以留 20% 做验证,数据量小、类别偏长尾,我一般留 10%,但基础上会配合类别采样。

4. 把非标准标注转成YOLOV5目录格式:转换脚本与四个边界坑

4.1 从 VOC XML 或 COCO JSON 转出来的常见做法

虽然这个标题明确说是 YOLOV5 目录格式,但实际能直接上手的比例不高。很多时候你下载到的“自动驾驶数据集”原始标注是 COCO JSON 格式(一个大的 annotations.json 挂所有图片)或者 VOC XML 格式(每张图片一个 XML 文件)。这时需要先把标注转换成 YOLO 的 txt 格式,再把图片和标注拷贝到对应的 train/val 目录下。

一个像样的转换脚本,核心工作是完成三件事:读取原始标注、计算归一化中心点与宽高、按图片所属划分写入目标目录。我常写的 Python 转换脚本大致是这个思路:

import json import os import shutil def convert_coco_json(json_path, img_root, target_root, split): """把 COCO 格式的标注 JSON 转换成 YOLO 格式 txt。""" with open(json_path, "r") as f: coco = json.load(f) # 建立图片 id -> 文件名的映射 img_id2name = {} for img in coco["images"]: img_id2name[img["id"]] = img["file_name"] # 类别顺序按 JSON 里的 categories 顺序,这是必须保持稳定的 cat_id2new_id = {cat["id"]: idx for idx, cat in enumerate(coco["categories"])} for img in coco["images"]: img_id = img["id"] filename = img_id2name[img_id] txt_name = os.path.splitext(filename)[0] + ".txt" # 原图拷贝到 images/{split} src_img = os.path.join(img_root, filename) dst_img = os.path.join(target_root, "images", split, filename) os.makedirs(os.path.dirname(dst_img), exist_ok=True) shutil.copy(src_img, dst_img) img_w = img["width"] img_h = img["height"] lines = [] for ann in coco["annotations"]: if ann["image_id"] != img_id: continue # COCO 的 bbox 是 [x, y, width, height],左上角坐标系 x, y, w, h = ann["bbox"] # 归一化中心点坐标 cx = (x + w / 2) / img_w cy = (y + h / 2) / img_h # 归一化宽高 nw = w / img_w nh = h / img_h new_cat = cat_id2new_id[ann["category_id"]] lines.append(f"{new_cat} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}") with open(os.path.join(target_root, "labels", split, txt_name), "w") as f: f.write("\n".join(lines)) # 同时对 XML 格式的转换做同样的处理,注意 XML 的坐标是 xmin/ymin/xmax/ymax。

脚本的关键参数有三个:json_path指向 COCO JSON 文件,img_root指向原图所在根目录,target_root指向你要构建的 YOLO 数据集根目录。split参数决定当前处理的是 train 还是 val。

4.2 坐标异常的排查:负数、超出 1.0、宽高为零

转换脚本跑完后,不能直接训练。最常见的坐标异常有三类:浮点精度误差导致坐标略微超过 1.0、某些框 x/y 为负数、宽或高为零的退化框。这些异常不会让 YOLOV5 训练直接崩溃,但会造成极大的 loss 抖动,或者模型训练后期反复波动。

我的处理方式是加一个校验代码,遍历所有生成的 txt:

def validate_yolo_labels(label_dir): errors = [] for f in os.listdir(label_dir): if not f.endswith(".txt"): continue with open(os.path.join(label_dir, f), "r") as fh: for line_num, line in enumerate(fh, 1): parts = line.strip().split() if len(parts) != 5: errors.append(f"{f}:{line_num} 列数不对: {line}") continue cls, cx, cy, w, h = parts cx, cy, w, h = map(float, (cx, cy, w, h)) if w <= 0 or h <= 0: errors.append(f"{f}:{line_num} 宽高小于等于0: {line}") if cx < 0 or cx > 1 or cy < 0 or cy > 1 or w > 1 or h > 1: errors.append(f"{f}:{line_num} 坐标越界: {line}") if errors: for e in errors[:30]: print(e) print(f"共 {len(errors)} 条异常") else: print("标注文件全部合法")

这个脚本有点像一个“体检器”。不要拿全部标注文件体检,跑得慢,可以随机抽 1000 个文件抽样检查,或者全量检查但异常一多就会卡在打印上。我一般先全量跑一遍,但把异常数量上限控制住,比如每类问题只打印前 10 个,用来定位是单个文件的问题,还是转换脚本的逻辑错误。

4.3 数据集切割时注意保持 train/val 的标注一致性

从 COCO 或 VOC 转换时,很多人会犯一个错:转换脚本只处理了 train 部分的 JSON,val 部分没有同步转换,导致 val 目录下 images 里有图,labels 里却空荡荡。更隐蔽的问题是,data.yaml中val路径写的是./images/val,但 YOLO 训练时还会检查labels/val/是否存在对应 txt——如果缺失,YOLOV5 会跳过这些图片,并给出“WARNING: imges without labels”之类提示,这种警告刷屏时你很容易误以为数据集很大、很丰富,实际进到训练里的有效数据少得可怜。

我的习惯是,转换脚本无论处理 train 还是 val,都走同一套流程,跑完后直接统计两边的 txt 文件数量和 jpg 文件数量,做一次累计校验:每个 split 下,images 里的文件数应当等于 labels 里的文件数。如果不相等,立即找出缺的是哪一边。这一步能挡住大多数低级错误,也能避免训练进度到一半才发现验证集全是空跑。

5. 常见翻车与排查:自动驾驶数据集训练中最容易出现的问题

5.1 类别 ID 错位:训练不报错,验证结果却全错

现象:训练 loss 正常下降,验证集的 mAP 看起来也有 0.6 以上,但打开推理结果图片,发现检测出的类别名和框内容完全对不上——明明框着汽车,标签却写着“person”;框着红绿灯,标签却写着“truck”。

原因:标注 txt 里的类别 ID 与data.yaml里的names顺序不一致。常见于从多来源拼凑的 11 类数据集,有的标注从 1 开始编号类别,有的从 0 开始。模型只学习“ID 0 对应什么形状”,并不知道你期望它叫什么名字。

解决:写一个脚本,将每个类别的代表性图片抽取出来画框,人工确认 ID 与语义的对应关系。如果你的标注是从 1 开始的,把所有 txt 里的类别 ID 减 1,改完再跑一次校验。这个改动一句话,但造成的后果非常隐蔽,值得在训练前消耗 20 分钟确认。

5.2 长尾类别导致验证集 mAP 为 0

现象:训练结束后,模型在训练集上表现很好,train 的 loss 也下降到 0.0x 级别,但 val 集上某一个或几个不常见类别(比如骑行者)的 mAP 为 0,且无论如何增加训练轮数都不见好转。

原因:类别不平衡严重,该类别在训练集里本身就少见,模型把对应的输出头学成了“总是输出背景”。验证集里的该类目标几乎全部漏检。

解决:先按 3.2 节的脚本跑出每个类别的框数量,明确哪个类别是长尾。再通过--hyp参数调整 loss 权重,或对长尾类别的数据做过采样。但这些都是权宜之计——最靠谱的做法是给数据集补充大量该类别的新样本。如果补充不了,就把它合并到更粗粒度类别里,保住整体可用性。

5.3 val 目录下标注缺失或为空文件

现象:训练日志显示数据加载正常,但到验证阶段时没有评估结果,或者在 eval 时异常退出。打开 val 的 labels 目录,发现有一部分 txt 文件大小是 0 字节,或者部分图片根本没有对应 txt。

原因:数据集的验证集是从某个大型数据源切出来的,切分脚本只拷贝了 images 分支,没有同步拷贝 labels 分支。或者转换脚本在跑 val 时没有执行成功,因为某个 JSON 子文件路径错误。

解决:训练前做一次一致性统计——统计每个 split 下 jpg 与 txt 的文件数,并比对同名文件是否一一对应。发现缺失后,找到是哪个环节丢文件:如果是转换脚本跳过了一批图片,查看转换日志里有没有异常报错;如果是目录结构问题,重新组织目录后重跑转换。

5.4 图片分辨率差异过大影响训练稳定性

现象:训练时 loss 一直偏高,模型反复震荡,且显存占用波动剧烈。查看数据集,发现有的图片是 1920×1080,有的是 640×360,甚至还有 3840×2160 的大图。

原因:YOLOV5 默认会做 letterbox 缩放,但如果图片之间宽高比差异过大,缩放后的有效区域占比会差异很多,导致模型每轮看到的物体尺度分布完全不同,训练不稳定。

解决:训练前按最小边长做统计,把明显畸形的图片过滤掉。如果目标场景里确实同时存在不同分辨率的摄像头,建议在数据增强里加入--cache-images并在hyp.yaml中调整随机尺度范围,让模型在多次迭代中适应不同尺寸。最彻底的方法是把分辨率进行分组训练或归一化到统一尺寸,但在实际工程中优先保证同一批数据的宽高比不要差两三倍以上。

6. 训练前的最后一道验证与关键超参数校准

当数据集通过前面的验收、转换和排查之后,我通常不会直接开跑完整的训练,而是先做一个“冒烟测试”——用很小的图片尺寸、很少的训练轮数,快速验证数据链路是否通畅。常见的命令是在单张 GPU 上跑 10 个 epoch,看看 loss 是否流畅下降、验证集能否正常评估。

python train.py --data data.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 10

这轮冒烟测试不追求精度,只求“不报错、能收敛、有正常评估输出”。如果连这一步都跑不完,问题一定出在数据或环境配置上,先把冒烟测试过掉再考虑调优。

冒烟测试通过后,再进入正式训练。针对这种 11 类自动驾驶数据集,我会把hyp.yaml里的几个关键参数做小幅调整:hsv_h、hsv_s、hsv_v适当调高,因为自动驾驶场景需要适应一天中不同时段的光照变化;fliplr默认是 0.5,但如果你的场景包含车道通行方向,过高的水平翻转会让“靠左行驶”的标志变得不合常理,建议对交通标志和红绿灯类别减少翻转概率。角度增强degrees的建议不超过 10,过大的旋转会破坏道路目标的基础几何特征。

另外,验证集的作用不止于评估——我习惯在训练结束后保存每一轮验证集上的 PR 曲线和混淆矩阵,用它们来反推数据集的薄弱环节。如果某个类别的混淆主要发生在“行人”和“骑行行人”之间,可能意味着这两类的视觉差异本身就不够,数据标注的边界判断不一致,或者类的定义粒度太细,可以尝试把这两个类合并。如果你已经决定长期做自动驾驶感知方向,这种基于数据问题的迭代闭环,比不停调模型结构重要得多。数据集的验收、清洗、划分、转换,这些琐碎的“脏活累活”,才是模型精度的真正上限。

几年下来,我最深刻的教训是:拿到手的数据集再大,也要在第一天做好目录结构、类别顺序、分布统计这三件事,否则后面所有训练、调参、部署都建立在流沙上。希望这篇文章里的步骤和踩坑记录,能帮你少走这段弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询