
简介这份交通道路物体图像目标检测数据集面向计算机视觉初学者与目标检测实战开发者尤其适合正在使用 YOLO 系列做道路场景训练与调优的人群。数据已完成标注共覆盖汽车、警告标志、红色交通灯等 11 个类别可直接投入模型训练与验证省去自行标注的时间成本。压缩包内共 2000 个文件以 1420 个 txt 标注文件、579 张 jpg 图像和 1 个 py 脚本为主整体约 231.32MB并已按训练集与验证集完成划分运行 show 脚本即可可视化查看标注效果。目前已有 40 人学习下载。对于想快速跑通检测流程、验证网络改进效果的读者这份数据可作为稳定的实验底座配合作者主页的 YOLOv5 改进实战内容能进一步理解数据组织方式与训练调参思路。1. 交通道路物体图像目标检测数据1400 张已标注 YOLO 格式到底能干什么手头拿到一批约 1,400 张的交通道路物体图像目标检测数据标注已经是 YOLO 格式这件事的价值不在于「有数据」而在于「省掉了最贵的那一段」。做过目标检测的人都知道模型结构、损失函数、训练脚本这些都能抄唯独标注是抄不来的——一张道路图里同时有轿车、公交车、行人、非机动车、交通标志、锥桶框要贴边、遮挡要判断、截断要不要标全是人力堆出来的。1,400 张不算大但足够把一条从数据校验到训练到导出的链路完整跑通也足够验证一个垂直场景的可行性。这批数据适合谁一是想入门 YOLO 目标检测但卡在「没有自己的数据」这一步的人拿它练手比拿 COCO 更贴近真实业务二是做交通道路场景的团队想快速验证某个检测方向值不值得投入三是做数据集划分、格式转换、增强策略研究的人1,400 张的体量刚好能在单卡上反复实验。下面按「先看清数据长什么样 → 怎么校验和划分 → 怎么配环境训练 → 怎么避坑 → 怎么把效果再往上抬」的顺序讲透每一步都给能直接抄的命令和参数。2. 先搞懂 YOLO 标注格式和这批道路数据的结构2.1 YOLO 标注格式到底长什么样YOLO 格式的核心是「一行一个目标归一化坐标」和 VOC 的 XML、COCO 的 JSON 完全不同。每张图片对应一个同名.txt文件文件里每一行是class_id x_center y_center width height五个字段全部是相对图片宽高的归一化值范围 0~1。class_id是从 0 开始的整数对应一个classes.txt或data.yaml里的类别名列表。这里最容易翻车的是坐标系x_center是框中心点的横坐标除以图片宽度不是左上角width是框宽除以图片宽度。很多人从 VOC 转过来时忘了归一化或者把中心点当成左上角训练时 loss 一直不降查半天才发现是标注错了。一个典型的道路数据目录结构是这样的dataset/ ├── images/ │ ├── train/ │ │ ├── road_0001.jpg │ │ └── ... │ └── val/ │ └── ... ├── labels/ │ ├── train/ │ │ ├── road_0001.txt │ │ └── ... │ └── val/ │ └── ... └── data.yaml注意images和labels是两棵平行的树文件名不含扩展名必须一一对应。YOLO 训练时是按文件名去labels目录找同名 txt 的图片叫road_0001.jpg标注就必须叫road_0001.txt差一个字符这张图就会被当成「无标注负样本」处理这是新手最常踩的坑之一。2.2 道路场景的类别设计要提前想清楚交通道路物体检测的类别设计直接决定后面模型能不能用。常见做法是分成几大类car、bus、truck、person、bicycle、motorcycle、traffic_light、traffic_sign、cone。但 1,400 张数据如果类别铺得太开每个类可能只有几十个实例模型学不动。我的建议是先看数据里实际有哪些类、每类多少个框再决定要不要合并。合并的典型操作把car、suv、van合并成car把bicycle和motorcycle合并成two_wheeler把各种交通标志合并成一个traffic_sign大类。类别越少单类样本越多小数据集上效果越稳。反过来如果业务就是要区分轿车和卡车那 1,400 张可能不够得考虑补充数据或做迁移学习。data.yaml是 YOLO 训练的入口配置长这样path: /home/user/dataset train: images/train val: images/val nc: 6 names: [car, bus, truck, person, two_wheeler, traffic_sign]nc是类别数必须和names长度一致也必须和标注里出现的最大class_id 1一致。如果标注里出现了class_id6但nc6合法 id 是 0~5训练会直接报索引越界。这个错误信息有时候不直观所以校验脚本里一定要单独查一遍。2.3 用脚本把数据体检一遍再动手拿到数据别急着训练先跑一遍校验。下面这个脚本检查四件事图片和标注是否一一对应、坐标是否越界、类别 id 是否超范围、每类实例数分布。import os from pathlib import Path from collections import Counter IMG_EXTS {.jpg, .jpeg, .png, .bmp} root Path(dataset) img_dir root / images / train lbl_dir root / labels / train nc 6 # 与 data.yaml 保持一致 imgs {p.stem for p in img_dir.iterdir() if p.suffix.lower() in IMG_EXTS} lbls {p.stem for p in lbl_dir.glob(*.txt)} # 1. 一一对应检查 print(图片无标注:, sorted(imgs - lbls)[:10]) print(标注无图片:, sorted(lbls - imgs)[:10]) cls_counter Counter() bad_lines [] for txt in lbl_dir.glob(*.txt): for i, line in enumerate(txt.read_text().strip().splitlines()): parts line.split() if len(parts) ! 5: bad_lines.append((txt.name, i, 字段数不对)) continue cid int(parts[0]) vals list(map(float, parts[1:])) # 2. 类别 id 范围 if cid 0 or cid nc: bad_lines.append((txt.name, i, f类别越界 {cid})) # 3. 坐标范围与宽高合法性 if any(v 0 or v 1 for v in vals): bad_lines.append((txt.name, i, 坐标越界)) if vals[2] 0 or vals[3] 0: bad_lines.append((txt.name, i, 宽高非正)) cls_counter[cid] 1 print(异常行数:, len(bad_lines)) for b in bad_lines[:10]: print(b) # 4. 类别分布 for cid, cnt in sorted(cls_counter.items()): print(fclass {cid}: {cnt} 个框)逻辑说明imgs - lbls是集合差能一次列出所有「有图没标注」的文件这些图在训练时会被当负样本如果数量大说明数据不完整。坐标检查里vals[2]和vals[3]是宽高必须为正出现 0 或负数说明标注时框被拖没了。类别分布那一段最关键——如果某个类只有十几个框基本可以判定这个类训不出来要么合并要么补数据。参数说明nc必须和data.yaml里的nc手动对齐脚本不会自动读 yaml这是故意的逼你确认一遍。IMG_EXTS按实际数据补全有些数据集混了.webp或.tif漏掉会导致误报「图片无标注」。3. 数据集划分与增强1,400 张怎么切才不浪费3.1 训练验证划分的三种切法和适用场景1,400 张的体量划分方式直接影响评估可信度。常见三种划分方式比例适用场景风险简单随机8:2数据来源单一、场景均匀同场景图片泄漏到验证集指标虚高按视频/路段分组8:2数据来自连续视频抽帧需要记录每张图的来源分组分层抽样7:1.5:1.5类别极不均衡实现稍复杂小类可能仍不足道路数据如果是行车记录仪抽帧来的相邻帧几乎一样简单随机划分会让验证集里出现训练集的「近邻」mAP 虚高十几个点都有可能。这种情况必须按视频段或时间段分组同一段视频的帧只能进同一个集合。判断方法很简单看文件名有没有连续编号或者看图片内容是否高度相似。分层抽样适合类别不均衡的情况保证每个类在训练集和验证集里的比例接近。1,400 张如果某个类只有 50 个实例随机划分可能验证集里只剩 5 个评估噪声极大。用sklearn的StratifiedShuffleSplit按「每张图的主类别」分层能缓解这个问题。3.2 用脚本做可复现的划分import random import shutil from pathlib import Path random.seed(42) # 固定种子保证每次划分一致 root Path(dataset) src_img root / images / all src_lbl root / labels / all val_ratio 0.2 stems sorted(p.stem for p in src_img.glob(*.jpg)) random.shuffle(stems) n_val int(len(stems) * val_ratio) val_stems set(stems[:n_val]) for split in [train, val]: (root / images / split).mkdir(parentsTrue, exist_okTrue) (root / labels / split).mkdir(parentsTrue, exist_okTrue) for stem in stems: split val if stem in val_stems else train shutil.copy(src_img / f{stem}.jpg, root / images / split / f{stem}.jpg) lbl src_lbl / f{stem}.txt if lbl.exists(): shutil.copy(lbl, root / labels / split / f{stem}.txt) print(f训练集 {len(stems) - n_val} 张验证集 {n_val} 张)逻辑说明random.seed(42)是后悔药划分结果可复现换台机器跑出来一样方便对比实验。先shuffle再切片避免按文件名顺序切导致场景偏斜。复制而不是移动原始all目录保留划分错了能重来。参数说明val_ratio一般 0.15~0.21,400 张取 0.2 约 280 张验证够评估但不至于浪费训练数据。如果做三路划分再加一个test_ratio但 1,400 张不建议切三份验证集太小评估不稳。3.3 小数据集增强哪些增强有用哪些是负优化1,400 张属于小数据集增强几乎是必须的。但道路场景有它的特殊性不是所有增强都能用。有用的mosaic四图拼接对 YOLO 系列提升明显能变相增加小目标和场景多样性HSV色调饱和度亮度扰动应对不同天气和光照translate平移和scale缩放模拟车辆距离变化flipud慎用上下翻转会让天空跑到下面不符合物理fliplr水平翻转可以用但要注意交通标志和车牌文字会反如果类别里有依赖文字的方向性目标翻转要关掉。负优化的mixup在检测任务上对小数据集有时反而降点因为两张图叠加后框的语义变模糊cutout大面积遮挡在道路场景容易把关键目标盖掉导致模型学到「看不到就猜」过度旋转超过 15 度会让车辆姿态失真实际道路摄像头基本是水平安装旋转增强收益低。YOLO 训练时的增强参数在data.yaml同级或命令行里配常见写法yolo train modelyolov8n.pt datadata.yaml epochs100 imgsz640 \ hsv_h0.015 hsv_s0.7 hsv_v0.4 \ degrees0.0 translate0.1 scale0.5 shear0.0 \ flipud0.0 fliplr0.5 mosaic1.0 mixup0.0参数说明hsv_h0.015是色调扰动幅度道路场景别调太大否则红绿灯颜色会失真fliplr0.5一半概率水平翻转mosaic1.0表示始终启用小数据集建议开mixup0.0先关掉等基线跑通再试。degrees0.0和shear0.0是保守设置如果数据里摄像头角度变化大可以适当放开到 5~10 度。4. 从零配环境到跑通第一次训练4.1 环境配置CUDA、PyTorch、Ultralytics 的版本对齐环境这块翻车最多的是版本不匹配。常见做法是用 conda 建独立环境先装对 CUDA 版本的 PyTorch再装 ultralytics。步骤conda create -n yolo python3.10 -y conda activate yolo # 按显卡驱动支持的 CUDA 版本装 PyTorch下面以 CUDA 11.8 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics # 验证 python -c import torch; print(torch.__version__, torch.cuda.is_available())逻辑说明先建环境隔离依赖避免和系统 Python 冲突。PyTorch 的 index-url 必须和本机 CUDA 版本对应装错了torch.cuda.is_available()会返回 False训练直接掉到 CPU 上慢到怀疑人生。ultralytics装完会自带yolo命令行工具。参数说明python3.10是当前兼容性最好的版本3.12 有些依赖还没跟上。CUDA 版本用nvidia-smi看右上角的CUDA Version那是驱动支持的最高版本装 PyTorch 时选不超过它的。如果torch.cuda.is_available()是 False先查驱动再查 PyTorch 版本最后查是不是装成了 CPU 版。4.2 第一次训练用最小配置跑通再调参别一上来就堆参数先用最小配置确认整条链路通。命令yolo detect train \ modelyolov8n.pt \ datadataset/data.yaml \ epochs50 \ imgsz640 \ batch16 \ device0 \ projectruns/road \ namebaseline逻辑说明modelyolov8n.pt用 nano 版预训练权重小数据集上 nano 往往比 large 更稳因为参数量少不容易过拟合。epochs50先跑个短周期看 loss 曲线趋势别直接上 300。device0指定第一块 GPU。project和name决定输出目录方便多次实验对比。参数说明imgsz640是 YOLO 的默认输入尺寸道路场景如果小目标多远处行人、锥桶可以提到 960 或 1280但显存占用和训练时间会涨。batch16按显存调8G 显存跑 640 大概能到 16跑 1280 可能只能到 4。如果报显存不足先降 batch 再降 imgsz。训练启动后重点看三样box_loss和cls_loss是否稳定下降、mAP50是否在涨、验证集 loss 有没有先降后升过拟合信号。50 轮跑完如果 mAP50 还在涨再加到 150~300 轮如果 20 轮就不动了先查数据而不是加轮数。4.3 训练结果怎么读哪些指标能信哪些是幻觉训练完runs/road/baseline/下会有一堆输出重点看results.csv、confusion_matrix.png、val_batch*_pred.jpg。results.csv里metrics/mAP50-95是最综合的指标但小数据集上波动大别只看这一个。metrics/precision和metrics/recall要一起看precision 高 recall 低说明模型保守漏检多反过来说明误检多。道路场景通常更在意 recall漏检一个行人比多标一个框严重得多。confusion_matrix.png能看出类别混淆。如果car大量被预测成truck说明这两类在数据里长得太像或者标注不一致要么合并要么补样本。val_batch*_pred.jpg是验证集的可视化预测直接看图比看数字快——框歪了、漏了、重复框一眼就能看出来。提示小数据集上单次训练的 mAP 波动可能有 3~5 个点别拿一次结果下结论。固定随机种子跑三次取平均或者用交叉验证结论才靠谱。5. 道路目标检测的避坑清单5 个真实翻车现场5.1 现象训练 loss 正常下降但 mAP 一直是 0原因标注文件的class_id从 1 开始而不是 0。有些标注工具默认类别从 1 编号但 YOLO 要求从 0 开始导致所有框的类别都越界模型学不到任何有效类别。解决跑第 2 章的校验脚本看class_id最大值。如果最大是nc而不是nc-1把所有标注的class_id减 1。批量处理# 把所有 txt 第一列减 1 for f in labels/train/*.txt; do awk {$1$1-1; print} $f $f.tmp mv $f.tmp $f done5.2 现象验证集 mAP 很高实际用起来一塌糊涂原因数据泄漏。行车记录仪抽帧的数据相邻帧几乎一样随机划分后训练集和验证集里有大量近邻帧模型等于在「背答案」。解决按视频段或时间分组划分同一段视频的帧只进一个集合。如果文件名有连续编号按编号分块如果没有用图片感知哈希pHash聚类相似的归到同一组再划分。5.3 现象小目标远处行人、锥桶几乎检测不到原因640 输入尺寸下远处目标可能只有十几个像素经过下采样后在特征图上几乎消失。这是小目标检测的经典难题。解决三个方向。一是提高输入尺寸到 960 或 1280代价是显存和速度二是用带 P2 小目标检测层的模型结构比如在 YOLO 配置里加一层更浅的特征图输出三是数据层面对含小目标的图做裁剪放大再作为额外样本加入训练集。先试提尺寸成本最低。5.4 现象训练到一半 loss 突然变成 nan原因学习率过大或者标注里有非法值坐标越界、宽高为 0。非法标注会让梯度爆炸loss 直接 nan。解决先跑校验脚本清掉非法标注再把学习率降一档。YOLO 默认用余弦退火初始 lr 一般 0.01小数据集可以降到 0.001。如果已经 nan从上一个 checkpoint 恢复别从头再来。5.5 现象模型在白天数据上很好晚上/雨天全崩原因1,400 张数据如果集中在某几种光照条件模型学到的特征和光照强相关换个天气就失效。这是数据分布问题不是模型问题。解决先统计数据的场景分布白天/夜晚/雨天/晴天如果某类严重缺失要么补数据要么用增强模拟HSV 扰动、亮度对比度调整、加噪声。增强只能缓解根治还得靠数据覆盖。如果业务必须覆盖夜间1,400 张可能不够得考虑扩充或迁移学习。6. 把 1,400 张榨干迁移学习、模型导出与效果验证的进阶技巧数据量固定的时候提升效果的空间主要在「怎么用」而不是「加多少」。第一个技巧是两阶段训练先用 COCO 预训练权重在全部 1,400 张上跑一个基线然后把 backbone 冻结只训检测头 20 轮再解冻全部微调 50 轮。冻结阶段学习率可以设大一点0.01解冻后降到 0.001。这个套路在小数据集上通常比直接端到端训多涨 2~4 个点因为检测头先适应了你的类别再微调时不会把预训练特征带偏。第二个技巧是模型导出后的推理验证。训练时的 mAP 是带增强的评估实际部署要看导出模型的表现。导出 ONNXyolo export modelruns/road/baseline/weights/best.pt formatonnx imgsz640 opset12 simplifyTrueopset12兼容性最好simplifyTrue会做图优化去掉冗余算子。导出后用onnxruntime跑一遍验证集对比 PyTorch 和 ONNX 的输出差异如果 mAP 掉超过 1 个点检查是不是有算子不支持或者输入预处理不一致。道路场景如果要做实时检测还可以导出 TensorRT但 TensorRT 对动态 shape 和自定义算子支持有限导出前先确认模型结构没有特殊算子。第三个技巧是错误分析驱动迭代。把验证集里所有预测错误的图挑出来按错误类型分类漏检、误检、框不准、类别错。统计哪类错误最多针对性处理。漏检多就补该类样本或提高 recall 相关参数误检多就加负样本或提高置信度阈值框不准就检查标注质量。这个流程比盲目调参有效得多1,400 张数据尤其经不起瞎试。验证方法上除了 mAP建议加一个业务指标。比如交通场景关心「每帧漏检的目标数」那就统计验证集上平均每张图漏了几个框这个数字比 mAP 直观也更容易和业务方对齐。我自己的习惯是每次实验都记录三个数mAP50、recall、每图漏检数三个一起看才不会自欺欺人。最后说个血泪经验小数据集上数据质量的重要性远大于模型选择。我见过太多人花一周调模型结构涨了 0.5 个点结果回头清理了 50 张错标直接涨 3 个点。1,400 张的体量把标注质量抠到极致比换任何模型都值。希望帮到你。本文还有配套的精品资源点击获取