简介:这份资源是面向目标检测方向的BDD100K人车识别数据集,适合深度学习入门与进阶开发者、算法工程师及学生用于YOLO系列、Faster R-CNN、SSD等模型的训练与验证。数据集共15807张图片,涵盖pedestrian、car、bus、rider、motorcycle、truck、bicycle七类目标,同时提供YOLO与VOC两种标注格式,并附有指定类别信息的yaml配置文件,图片与txt标签已按训练集、验证集、测试集划分完毕,可直接投入YOLOv5至YOLOv10等算法使用。资源包共2000个文件,以1999个txt标签文件和1个yaml配置文件为主,压缩包大小约917.9MB,因体积超过1G已上传至百度网盘,分享链接永久有效。目前已有417人学习下载,适合需要快速获取标准化人车识别数据、开展模型对比实验或课程项目的读者,省去自行标注与格式转换的时间成本。
1. 拿到 BDD100K 人车识别数据集:15807 张图、7 类目标,为什么我第一件事是查标签格式
上周帮一个做智慧交通检测的朋友排查训练不收敛的问题,翻完他的数据目录我就明白了——他把一份 VOC 格式的标注直接喂给了 YOLOv8,labels/里全是 XML,模型当然学不到东西。这类翻车在目标检测项目里太常见了,而 BDD100K 人车识别数据集恰好是那种「格式给得很全、但你得先搞清楚用哪套」的资源。这份数据集包含 15807 张图片,覆盖 pedestrian、car、bus、rider、motorcycle、truck、bicycle 七类道路目标,同时提供了 YOLO 格式的 txt 标签、VOC 格式的 xml 标签、指定类别信息的 yaml 文件,并且已经按训练集、验证集、测试集切分好。它解决的核心问题不是「有没有数据」,而是「拿到就能直接开训,不用自己写转换脚本」。适合谁?做车辆行人检测的算法工程师、跑 YOLO 系列课程设计的学生、需要快速验证检测模型 baseline 的从业者。如果你正在找一份开箱即用的人车识别数据集,这份东西的完成度比我见过的大多数「整理版」要高一个档次。
2. 拆开目录看门道:txt、xml、yaml 三套标注怎么配合
2.1 三种标注格式各自的职责
很多人拿到数据集第一反应是「文件好多」,其实这份资源的目录逻辑很清晰,三套标注各司其职。YOLO 格式的 txt 标签是给 YOLO 系列(v5 到 v10)直接训练用的,每行是class_id x_center y_center width height,坐标全部归一化到 0~1。VOC 格式的 xml 标签是给 Faster R-CNN、SSD 这类框架用的,里面存的是绝对像素坐标的xmin/ymin/xmax/ymax。yaml 文件则是 YOLO 训练时的数据配置入口,告诉训练脚本图片在哪、类别名是什么、类别数是多少。
这三套东西不是冗余,而是覆盖了主流检测框架的两种标注范式。你不需要同时用三套,选一套跟你训练框架匹配的就行。我一般会先确认训练框架吃哪种格式,再决定保留哪套标签,另一套留着做交叉验证或者格式转换的对照。
2.2 目录结构与文件对应关系
一份典型的切分后目录大概长这样,我按常见组织方式还原一下:
bdd100k_person_car/ ├── images/ │ ├── train/ # 训练集图片 │ ├── val/ # 验证集图片 │ └── test/ # 测试集图片 ├── labels/ │ ├── train/ # YOLO 格式 txt,与 images/train 一一对应 │ ├── val/ │ └── test/ ├── annotations/ # VOC 格式 xml(可选保留) │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml # YOLO 训练配置关键点是images/train/xxx.jpg必须和labels/train/xxx.txt同名同数量,YOLO 训练时是按文件名去配对标签的,少一个或者名字对不上,那张图就会被当成无标签背景图处理,直接影响召回。我见过有人解压后图片在train/images/、标签在train/labels/,路径层级不一致,训练脚本找不到标签,loss 一直降不下去。
2.3 data.yaml 里到底写了什么
yaml 文件是 YOLO 训练的入口配置,内容结构一般是这样:
# data.yaml path: ./bdd100k_person_car # 数据集根目录 train: images/train # 训练集图片相对路径 val: images/val # 验证集图片相对路径 test: images/test # 测试集图片相对路径 nc: 7 # 类别数 names: # 类别名,顺序必须和 txt 里的 class_id 对应 0: pedestrian 1: car 2: bus 3: rider 4: motorcycle 5: truck 6: bicycle这里最容易出问题的是names的顺序。txt 标签里的class_id是数字,模型训练时靠 yaml 里的names把数字映射回类别名。如果 yaml 里0写的是car,但 txt 里0实际标注的是pedestrian,训练不会报错,但推理出来的框全是错的类别。这种错误隐蔽性极强,我建议拿到数据集后先抽 5 张图可视化验证一遍再开训。
提示:
path字段在 YOLOv8 里支持相对路径和绝对路径,但相对路径是相对于你执行训练的当前工作目录,不是相对于 yaml 文件位置。跑训练前先pwd确认一下,或者直接写绝对路径省心。
3. 从零跑通 YOLOv8 训练:环境、命令与参数怎么设
3.1 环境准备与依赖安装
这份数据集官方说明支持 YOLOv5 到 YOLOv10,我以目前用得最多的 Ultralytics YOLOv8 为例走一遍完整流程。环境用 conda 隔离,避免和系统里的其他包打架:
# 创建独立环境,python 版本建议 3.9~3.11 conda create -n bdd_yolo python=3.10 -y conda activate bdd_yolo # 安装 ultralytics,会自动带上 torch 等依赖 pip install ultralytics # 验证安装,能打印版本号就说明环境通了 yolo version如果你有 GPU,装完 torch 后确认一下 CUDA 是否可用:
import torch print(torch.cuda.is_available()) # True 表示 GPU 可用 print(torch.cuda.get_device_name(0))torch.cuda.is_available()返回False是最常见的环境坑,通常是 torch 版本和 CUDA 驱动不匹配。这种情况要么重装对应 CUDA 版本的 torch,要么先用 CPU 跑一个小 epoch 验证数据管线通不通,别一上来就怀疑数据集有问题。
3.2 启动训练:一条命令背后的参数含义
环境通了之后,训练命令本身很简单:
yolo detect train \ data=./bdd100k_person_car/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ device=0 \ workers=4 \ project=runs/bdd \ name=exp1逐个说参数。data指向你的 yaml 配置文件,路径写错会直接报Dataset not found。model是预训练权重,yolov8n.pt是最小的 nano 版本,适合先跑通流程;确认没问题再换yolov8s.pt或yolov8m.pt提精度。epochs=100是训练轮数,15807 张图的数据量,100 轮在单卡 3090 上大概几个小时。imgsz=640是输入分辨率,YOLO 系列默认 640,如果你的目标偏小(比如远处行人),可以提到 1280,但显存占用会翻倍。batch=16根据显存调,8G 显存跑 640 分辨率大概能到 16,爆显存就往下调。device=0指定第一块 GPU,CPU 训练写device=cpu。workers=4是数据加载线程数,设太高在 Windows 上容易出问题,Linux 下可以到 8。
训练启动后你会看到每个 epoch 的输出,重点关注三个指标:box_loss、cls_loss、mAP50。box_loss降不下去说明框回归有问题,cls_loss降不下去说明分类有困难,mAP50是验证集上的主指标,正常情况应该稳步上升。
3.3 训练完怎么验证和推理
训练结束后权重存在runs/bdd/exp1/weights/best.pt,用验证集跑一遍评估:
yolo detect val \ model=runs/bdd/exp1/weights/best.pt \ data=./bdd100k_person_car/data.yaml \ split=val \ imgsz=640split=val指定用验证集,也可以换成test看测试集表现。输出里会给出每一类的 precision、recall、mAP50、mAP50-95。人车识别这种场景,car和pedestrian的 mAP 通常最高,rider和motorcycle因为样本相对少、目标形态多变,指标会低一些,这是正常的,不用一看到某类指标低就以为数据坏了。
推理单张图片或者整个文件夹:
# 单张图片 yolo detect predict \ model=runs/bdd/exp1/weights/best.pt \ source=test.jpg \ conf=0.25 \ save=True # 整个测试集文件夹 yolo detect predict \ model=runs/bdd/exp1/weights/best.pt \ source=./bdd100k_person_car/images/test \ conf=0.25 \ save=Trueconf=0.25是置信度阈值,低于这个值的框会被过滤掉。人车检测场景如果漏检严重就调低到 0.1,误检太多就调到 0.4 以上。结果默认存在runs/detect/predict/下。
4. 换框架不换数据:VOC 标签喂给 Faster R-CNN 的改法
4.1 为什么有人非要用 xml 标签
YOLO 系列虽然火,但不少团队的生产管线还是 Faster R-CNN 或者 SSD,尤其是需要两阶段检测精度、或者已有基于 Detectron2、MMDetection 的代码库时。这份数据集提供了 xml 标签,就是为了让这批人不用自己从 txt 反推 VOC 格式。xml 里存的是绝对坐标,结构长这样:
<annotation> <filename>abc123.jpg</filename> <size> <width>1280</width> <height>720</height> </size> <object> <name>car</name> <bndbox> <xmin>320</xmin> <ymin>210</ymin> <xmax>580</xmax> <ymax>400</ymax> </bndbox> </object> </annotation>name是类别名,bndbox是框的左上角和右下角绝对坐标。Faster R-CNN 的很多实现(比如 torchvision 的参考代码)直接读这种格式。
4.2 从 txt 反推 xml 的转换脚本
如果你手上的版本只保留了 txt,但临时要切到 VOC 管线,可以自己转。核心逻辑是读图片尺寸,把归一化坐标乘回去:
import os import cv2 from xml.etree.ElementTree import Element, SubElement, ElementTree # 类别映射,顺序必须和训练时一致 CLASSES = ['pedestrian', 'car', 'bus', 'rider', 'motorcycle', 'truck', 'bicycle'] def yolo_to_voc(img_path, txt_path, xml_path): img = cv2.imread(img_path) h, w = img.shape[:2] # 拿到图片真实宽高 root = Element('annotation') SubElement(root, 'filename').text = os.path.basename(img_path) size = SubElement(root, 'size') SubElement(size, 'width').text = str(w) SubElement(size, 'height').text = str(h) with open(txt_path, 'r') as f: for line in f.readlines(): parts = line.strip().split() if len(parts) != 5: continue # 跳过空行或异常行 cid, xc, yc, bw, bh = map(float, parts) # 归一化坐标还原成绝对像素 xmin = int((xc - bw / 2) * w) ymin = int((yc - bh / 2) * h) xmax = int((xc + bw / 2) * w) ymax = int((yc + bh / 2) * h) obj = SubElement(root, 'object') SubElement(obj, 'name').text = CLASSES[int(cid)] box = SubElement(obj, 'bndbox') SubElement(box, 'xmin').text = str(max(0, xmin)) SubElement(box, 'ymin').text = str(max(0, ymin)) SubElement(box, 'xmax').text = str(min(w, xmax)) SubElement(box, 'ymax').text = str(min(h, ymax)) ElementTree(root).write(xml_path)CLASSES列表的顺序必须和 txt 里class_id的语义完全一致,错一位整个数据集的类别就全乱了。max(0, xmin)和min(w, xmax)是防止坐标越界,YOLO 标注偶尔会有框超出图片边缘的情况,不裁剪的话某些框架会直接报错。转换完建议抽几张用labelImg打开看一眼,确认框的位置和类别都对。
4.3 训练集/验证集/测试集切分的注意事项
这份数据集已经切好了 train/val/test,但你要清楚它的切分比例和随机种子是固定的。如果你要做交叉验证或者调整切分比例,得自己重新划分。重新划分时有个血泪经验:同一个视频序列的连续帧不能同时出现在训练集和验证集里,否则验证指标会虚高,因为模型见过几乎一样的画面。BDD100K 原始数据是按视频采集的,虽然这份整理版可能已经打散了,但如果你发现验证集 mAP 高得离谱(比如 0.95 以上),先怀疑是不是数据泄漏。
5. 避坑排查:训练不收敛、标签错位、显存爆炸的六个真实案例
5.1 现象:loss 一直是 nan 或者不下降
原因通常有三个。第一,yaml 里的nc和实际类别数不一致,比如你只保留了 3 类但 yaml 还写着 7,模型输出维度对不上。第二,txt 标签里有空文件或者格式错误的行,YOLO 解析时产生异常值。第三,学习率设太大,前几个 batch 就把权重跑飞了。
解决办法:先用脚本扫一遍所有 txt,统计每个文件的类别 id 范围,确认没有超出nc-1的值;再检查有没有 0 字节的标签文件;学习率用默认的lr0=0.01起步,不要一上来就调大。
5.2 现象:训练正常但推理框全错位
原因基本锁定在 yaml 的names顺序和 txt 的class_id语义不匹配。比如 txt 里0是pedestrian,但 yaml 里0写成了car,模型学到的映射就是错的。
解决办法:抽 10 张训练图,用下面的脚本把框画出来,人眼确认类别和位置:
import cv2 img = cv2.imread('images/train/xxx.jpg') h, w = img.shape[:2] with open('labels/train/xxx.txt') as f: for line in f: cid, xc, yc, bw, bh = map(float, line.split()) x1 = int((xc - bw/2) * w); y1 = int((yc - bh/2) * h) x2 = int((xc + bw/2) * w); y2 = int((yc + bh/2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(int(cid)), (x1, y1-5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite('check.jpg', img)画出来一看便知,类别 id 和实际目标对不上就改 yaml。
5.3 现象:训练到一半 CUDA out of memory
原因不一定是 batch 太大。YOLOv8 在训练过程中会缓存验证集的预测结果,如果验证集图片分辨率不一致,某些 batch 会突然占用更多显存。另外workers设太高,每个 worker 都会预加载数据到内存,间接影响显存。
解决办法:先把batch降到 8 试,还爆就把imgsz从 640 降到 512。如果都不行,加cache=False关掉数据缓存。长期方案是统一把所有图片 resize 到同一分辨率再训练。
5.4 现象:验证集 mAP 很高但测试集一塌糊涂
这是典型的数据泄漏或者过拟合。先检查训练集和验证集有没有重复图片(用 md5 去重),再检查验证集是不是太小。15807 张图如果按 8:1:1 切,验证集大概 1500 张左右,这个量级是够的。如果验证集只有几百张,指标波动会很大,不能作为选模型的依据。
5.5 现象:某些类别完全检测不到
rider、motorcycle、bicycle这三类在 BDD100K 里样本量天然比car少很多。如果训练完发现这几类 mAP 接近 0,先统计一下每类的标注框数量:
# 统计每个类别出现的次数 cat labels/train/*.txt | awk '{print $1}' | sort | uniq -c如果某类只有几十个框,模型学不到是正常的。解决办法是用copy_paste或者mosaic增强,或者在 yaml 里给这类加权重(YOLOv8 支持cls_pd之类的损失权重调整,具体看版本)。
5.6 现象:解压后文件名乱码或者路径带中文
项目正文里列出的文件名是类似c3a18207-504667c7.txt这种哈希串,说明原始文件可能是用 UUID 命名的。解压到 Windows 上如果路径带中文,OpenCV 读图会失败返回 None,进而导致训练时报assert img is not None之类的错误。解决办法是把数据集放到纯英文路径下,比如D:/datasets/bdd100k/,别放在桌面或者「我的文档」里。
6. 进阶技巧:用子集快速验证管线,再全量开训
6.1 为什么要先跑子集
15807 张图全量训练一轮,在单卡上也要不少时间。如果你只是想验证「数据管线通不通、yaml 写得对不对、标签有没有问题」,完全没必要等全量跑完。我的习惯是先从训练集里抽 500 张图建一个 mini 子集,跑 5 个 epoch,看 loss 有没有正常下降、验证能不能出结果。这一步能把 90% 的配置错误提前暴露出来。
import os import random import shutil src_img = 'bdd100k_person_car/images/train' src_lbl = 'bdd100k_person_car/labels/train' dst_img = 'mini/images/train' dst_lbl = 'mini/labels/train' os.makedirs(dst_img, exist_ok=True) os.makedirs(dst_lbl, exist_ok=True) files = [f for f in os.listdir(src_img) if f.endswith('.jpg')] random.seed(42) # 固定种子,保证可复现 sample = random.sample(files, 500) # 抽 500 张 for f in sample: shutil.copy(os.path.join(src_img, f), os.path.join(dst_img, f)) lbl = f.replace('.jpg', '.txt') lbl_path = os.path.join(src_lbl, lbl) if os.path.exists(lbl_path): shutil.copy(lbl_path, os.path.join(dst_lbl, lbl))random.seed(42)是为了让每次抽的子集一样,方便对比不同配置的效果。抽完记得同步改一份 mini 版的 yaml,把train和val都指向 mini 目录,跑通后再换回全量。
6.2 用 TensorBoard 看训练过程
YOLOv8 训练时默认会把指标写到runs/bdd/exp1/下,可以直接用 TensorBoard 打开:
tensorboard --logdir runs/bdd浏览器打开localhost:6006,重点看metrics/mAP50(B)和train/box_loss两条曲线。正常的训练曲线是 mAP 稳步上升、loss 稳步下降,中间有波动是正常的。如果 mAP 在某个 epoch 突然掉到 0 又弹回来,通常是那一个 batch 的数据有问题,去查对应的图片和标签。
6.3 一个我踩过的坑:验证集指标选模型
YOLO 默认保存best.pt和last.pt,best.pt是按验证集 mAP 选的。但如果你后面要换测试集评估,别直接用best.pt的验证集指标当最终结果,一定要在测试集上重新跑一遍val。我有一次汇报时用了验证集 mAP,被问到测试集表现时才发现差了 5 个点,原因是验证集和测试集的类别分布不完全一致。从那以后我每次选模型都强制在测试集上再跑一遍yolo detect val split=test,确认没有过拟合才敢用。
这份 BDD100K 人车识别数据集的完成度在于它把格式转换和切分这些脏活都做完了,你拿到手只需要选对格式、配好 yaml、跑通管线。如果你正在做人车检测相关的项目或者课程设计,直接拿它当 baseline 能省掉至少两天的数据整理时间。希望帮到你。
本文还有配套的精品资源,点击获取