
简介这是一份面向自动驾驶感知算法开发者的多类别交通目标检测数据集适用于L2至L4级自动驾驶算法训练、智能交通监控与ADAS功能优化等场景。数据集共包含训练集802张、验证集229张、测试集114张图像覆盖汽车、公交车、卡车、警车、救护车、行人、自行车、摩托车、交通标志、路灯、建筑物、树木、山脉、绿化带等30个交通场景核心类别并特别纳入东南亚特色交通工具提升模型在多元交通环境中的适应性。资源包共2000个文件以1145个YOLO格式txt标注文件、853张jpg图像为主另含1个yaml配置文件与1份docx说明文档压缩包约65.55MB兼容主流目标检测框架。标注经专业团队校验交通标志定位精确、车辆重叠场景完整场景涵盖日间夜间、城市山区、静态动态等多样条件。已有75人学习下载可用于交通要素分布热力图、特殊车辆优先级识别及道路场景语义理解等扩展任务。1. 拆开「自动驾驶多类别交通目标检测数据集7.zip」它到底能解决什么问题如果你正在做自动驾驶感知方向的目标检测大概率经历过这个阶段模型结构调完了loss 曲线也收敛了但一上路测试就发现——车、人、骑行者、交通锥、红绿灯混在一起模型要么漏检要么把远处的广告牌认成车。问题往往不在网络本身而在训练数据的类别覆盖和标注质量上。自动驾驶多类别交通目标检测数据集7.zip这个标题指向的就是这类场景下的一份多类别交通目标检测数据集核心价值在于「多类别」三个字它把城市道路中常见的机动车、非机动车、行人、交通标志、交通灯等目标放在同一套标注体系里让模型在一次训练中学会区分它们。这份数据适合谁一是刚入门目标检测、想找一个真实道路场景数据集跑通 YOLO 全流程的工程师二是已经在用公开数据集但发现类别不够、需要补充特定交通目标类别的从业者三是做自动驾驶仿真或端到端感知预研、需要快速验证检测模块的团队。它不能直接让你上车但能让你在本地把「数据清洗→格式转换→训练→评估→可视化」这条链路完整走一遍知道每个环节的坑在哪。下面按「先看清数据长什么样再动手转格式、训模型、排错」的顺序展开。2. 先看清数据多类别交通目标的标注体系与目录结构2.1 多类别标注的常见组织方式拿到一个交通目标检测数据集第一件事不是急着写训练脚本而是搞清楚它的标注格式和类别定义。常见做法有两种一种是每张图片对应一个同名的.txt或.xml标注文件另一种是全部标注集中在一个.json或.csv里。自动驾驶多类别交通目标检测数据集7.zip这类命名通常意味着解压后是一个按类别或按场景分文件夹的结构里面包含images/和labels/两个平行目录或者JPEGImages/和Annotations/的 VOC 风格。我一般会先跑一段脚本统计类别分布因为多类别数据集最容易出现的问题就是类别不均衡——车有几千个框交通锥可能只有几十个。如果不提前知道训练时模型会直接偏向多数类少数类几乎学不到。下面这段代码假设标注是 YOLO 格式的.txt每行class_id x_center y_center width height坐标归一化到 0-1扫描整个 labels 目录输出每个类别的框数量。import os from collections import Counter # 假设解压后的根目录结构为 dataset/images 和 dataset/labels label_dir dataset/labels class_names [car, bus, truck, person, cyclist, traffic_light, traffic_sign, cone] counter Counter() for txt_file in os.listdir(label_dir): if not txt_file.endswith(.txt): continue with open(os.path.join(label_dir, txt_file), r) as f: for line in f: parts line.strip().split() if len(parts) 5: continue # 跳过空行或异常行 cls_id int(parts[0]) counter[cls_id] 1 for cls_id, count in sorted(counter.items()): name class_names[cls_id] if cls_id len(class_names) else funknown_{cls_id} print(f{name}: {count} boxes)这段代码的逻辑很直接遍历所有标注文件按类别 ID 累加框数。参数上唯一需要注意的是class_names的顺序必须和数据集定义的一致否则统计结果会张冠李戴。如果发现某个类别框数少于总数的 1%就要考虑在训练时用类别权重或者过采样。另外如果标注是 VOC 的 XML把解析部分换成xml.etree.ElementTree读取objectname即可思路一样。2.2 目录结构核对与图片-标注配对检查统计完类别下一步是确认图片和标注是否一一对应。多类别数据集在打包时经常出现图片有、标注丢或者标注文件名和图片名差一个后缀的情况。我习惯用集合运算快速找出不配对的样本避免训练时 DataLoader 报FileNotFoundError才发现。import os img_dir dataset/images label_dir dataset/labels # 去掉扩展名只保留文件名主体 img_stems {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .png, .jpeg))} label_stems {os.path.splitext(f)[0] for f in os.listdir(label_dir) if f.endswith(.txt)} only_img img_stems - label_stems only_label label_stems - img_stems print(f图片总数: {len(img_stems)}, 标注总数: {len(label_stems)}) print(f有图无标注: {len(only_img)} 个示例: {list(only_img)[:5]}) print(f有标注无图: {len(only_label)} 个示例: {list(only_label)[:5]})这里的关键参数是扩展名过滤有些数据集图片是.jpg但标注是.txt文件名主体必须完全一致。如果only_img数量很大说明标注缺失严重要么联系数据提供方要么直接把这些图从训练集剔除。only_label则说明标注文件是多余的删掉即可。这一步做完你才对这份数据的可用性有底。2.3 类别定义与训练配置的映射关系多类别数据集的类别 ID 通常从 0 开始连续编号但不同数据集的顺序可能不同。比如有的把person放在 0有的把car放在 0。如果你直接拿一份data.yaml去训而names列表顺序和标注里的 ID 对不上模型学到的就是错位的类别。常见做法是从数据集自带的classes.txt或data.yaml里读取类别名按行号对应 ID然后写进你自己的训练配置。# data.yaml 示例路径根据实际解压位置修改 path: ./dataset train: images/train val: images/val nc: 8 names: 0: car 1: bus 2: truck 3: person 4: cyclist 5: traffic_light 6: traffic_sign 7: conenc必须等于names的长度names的键必须从 0 连续递增。如果数据集里某个类别没有训练样本但你又写了进去训练时不会报错但评估时该类别的指标会是 0容易误导。所以统计完类别分布后只保留有足够样本的类别或者把极少数类合并到相近类。3. 从压缩包到可训练格式YOLO 数据转换与划分的完整操作3.1 解压后的第一轮清洗剔除无效标注多类别交通数据集里无效标注比想象中多。常见的有框的宽高为 0、坐标超出 0-1 范围、类别 ID 超出nc范围、同一张图里重复标注同一个目标。这些脏数据不清理训练时 loss 会突然飙高或者直接 NaN。我一般写一个清洗脚本把有问题的行直接删掉并记录被修改的文件。import os label_dir dataset/labels nc 8 # 类别总数 fixed_files [] for txt_file in os.listdir(label_dir): if not txt_file.endswith(.txt): continue path os.path.join(label_dir, txt_file) valid_lines [] with open(path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls_id int(parts[0]) x, y, w, h map(float, parts[1:]) # 过滤条件类别越界、宽高为0、坐标越界 if cls_id 0 or cls_id nc: continue if w 0 or h 0: continue if not (0 x 1 and 0 y 1): continue valid_lines.append(f{cls_id} {x:.6f} {y:.6f} {w:.6f} {h:.6f}\n) if len(valid_lines) ! sum(1 for _ in open(path)): fixed_files.append(txt_file) with open(path, w) as f: f.writelines(valid_lines) print(f清洗了 {len(fixed_files)} 个标注文件)这段代码的过滤条件覆盖了最常见的四类脏数据。参数nc必须和你的data.yaml一致否则会把合法类别误删。清洗后如果某个文件变成空文件说明这张图里所有标注都无效建议把对应的图片也移到unused/目录不参与训练。这一步做完数据质量会明显提升训练时的玄学崩溃会少很多。3.2 按场景或随机划分训练集与验证集交通目标检测数据集如果按随机划分容易出现同一段路的连续帧被分到训练和验证两边导致验证指标虚高。更稳妥的做法是按视频片段或场景划分同一段路的图片要么全在训练集要么全在验证集。如果数据集没有提供场景标签退而求其次用随机划分但要把随机种子固定保证可复现。import os import random import shutil random.seed(42) # 固定种子保证每次划分一致 img_dir dataset/images label_dir dataset/labels train_img_dir dataset/images/train val_img_dir dataset/images/val train_label_dir dataset/labels/train val_label_dir dataset/labels/val for d in [train_img_dir, val_img_dir, train_label_dir, val_label_dir]: os.makedirs(d, exist_okTrue) all_imgs [f for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .png))] random.shuffle(all_imgs) split_idx int(len(all_imgs) * 0.8) # 80% 训练20% 验证 for i, img_file in enumerate(all_imgs): stem os.path.splitext(img_file)[0] label_file stem .txt if i split_idx: shutil.move(os.path.join(img_dir, img_file), os.path.join(train_img_dir, img_file)) if os.path.exists(os.path.join(label_dir, label_file)): shutil.move(os.path.join(label_dir, label_file), os.path.join(train_label_dir, label_file)) else: shutil.move(os.path.join(img_dir, img_file), os.path.join(val_img_dir, img_file)) if os.path.exists(os.path.join(label_dir, label_file)): shutil.move(os.path.join(label_dir, label_file), os.path.join(val_label_dir, label_file)) print(f训练集: {split_idx} 张, 验证集: {len(all_imgs) - split_idx} 张)random.seed(42)是后悔药保证你下次再跑划分结果一样。0.8这个比例不是固定的如果数据量少于 2000 张建议用 0.7如果超过 10000 张0.9 也可以。注意shutil.move会改变原目录结构所以最好在解压后的副本上操作别直接动原始压缩包解压出来的文件。3.3 生成 YOLO 训练配置并跑通一次前向推理数据划分完写data.yaml指向新的目录结构然后就可以用 YOLO 系列模型跑一次训练。这里以常见的 YOLOv8 为例因为它的训练脚本对新手最友好一条命令就能启动。如果你用的是其他版本把命令里的模型名换掉即可。# 安装 ultralytics假设已装好 PyTorch 环境 pip install ultralytics # 启动训练imgsz 根据显存调整batch 根据显存调整 yolo detect train \ datadataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ projectruns/traffic \ nameexp1modelyolov8n.pt是轻量版适合先跑通流程如果显存够换成yolov8s.pt或yolov8m.pt精度更高。imgsz640是交通目标检测的常用输入尺寸小目标多的话可以提到 1280但显存占用会翻倍。batch16在 8GB 显存上跑 640 尺寸基本安全如果 OOM 就降到 8。训练启动后重点看mAP50和每个类别的instances数量如果某个类别 instances 一直是 0说明数据划分时该类样本全进了验证集需要重新划分。4. 训练多类别交通目标时的避坑与排查4.1 类别不均衡导致少数类指标全零现象训练日志里car的 mAP 有 0.8但cone和traffic_sign的 mAP 一直是 0验证集可视化里这些目标也框不出来。原因少数类样本太少模型在早期就被多数类主导梯度更新几乎不照顾少数类。加上 YOLO 的默认损失没有类别权重少数类直接躺平。解决两个方向。一是数据层面对少数类做过采样把包含少数类的图片复制多份或者用 mosaic、mixup 增强时提高少数类出现概率。二是损失层面在训练配置里加cls权重或者改用带类别平衡的损失函数。我一般先试过采样因为改动最小效果也最直接。4.2 图片和标注不同步导致训练中途报错现象训练到第几个 epoch 突然报FileNotFoundError或IndexError提示某个标注文件读不到或者行数不对。原因清洗或划分时只移动了图片忘了移动对应的标注或者标注文件里有空行、编码错误。多类别数据集在打包时经常混入非 UTF-8 编码的标注文件。解决在 DataLoader 之前加一道校验用 2.2 节的配对检查脚本跑一遍确保images/train和labels/train的文件名主体完全一致。另外读标注时用errorsignore忽略编码错误或者统一转成 UTF-8。训练前花五分钟检查比训练到一半崩了再回头找强。4.3 输入尺寸与锚框不匹配导致小目标漏检现象远处的小车、交通锥、行人检测不到但近处的大车框得很准。原因默认锚框是基于 COCO 数据集统计的交通场景里小目标比例更高锚框尺寸偏大。加上输入尺寸 640 下采样 32 倍后小目标在特征图上只剩几个像素。解决把imgsz提到 1024 或 1280同时用kmeans重新聚类自己数据集的锚框。YOLOv8 已经不用手动设锚框了但输入尺寸的影响依然很大。如果显存不够可以只对包含小目标的图片做裁剪增强把远处区域放大后再送入训练。4.4 验证集指标虚高但实际测试翻车现象验证集 mAP 0.85但拿一段新视频跑推理漏检和误检都很严重。原因训练集和验证集来自同一段路的连续帧画面风格、光照、角度高度相似模型过拟合到这段路的特征。换一段路分布偏移直接暴露。解决划分数据时按视频片段或日期划分确保验证集来自不同场景。如果数据集本身没有场景标签至少把连续帧打散别让相邻帧跨集。另外验证时除了看 mAP还要看confusion_matrix确认类别之间有没有系统性混淆比如bus和truck互相认错。4.5 显存溢出与 batch 设置的取舍现象训练启动几秒后报CUDA out of memory或者跑几个 epoch 后显存逐渐涨满。原因batch设太大或者imgsz太高或者 DataLoader 的workers太多导致内存泄漏。多类别数据集图片尺寸不一致时默认的 resize 策略可能产生超大张量。解决先把batch降到 4 或 8确认能跑通再逐步往上加。imgsz从 640 起步别一上来就 1280。workers设成 CPU 核心数的一半太多反而拖慢。如果还是 OOM检查是否有图片分辨率异常大比如 4000x3000 的图单独把它们缩到 1920 宽再训练。5. 进阶用混淆矩阵和类别权重把多类别检测调到可用5.1 用混淆矩阵定位类别混淆的根源训练跑完别只看 mAP。YOLO 训练结束后会在runs/traffic/exp1/下生成confusion_matrix.png这张图能告诉你哪个类别被误判成了哪个类别。交通场景里最常见的混淆是bus和truck、car和van、traffic_light和traffic_sign。如果混淆矩阵显示bus有 30% 被认成truck说明这两个类别的视觉特征在您的数据里区分度不够要么标注时定义模糊要么样本太少。我一般会挑出混淆最严重的两个类别把它们的验证集预测结果可视化看是标注问题还是模型问题。如果是标注问题比如同一辆车在不同图片里一会儿标bus一会儿标truck那就得回去统一标注。如果是模型问题可以在训练时给这两个类别更高的cls权重强迫模型更关注它们的差异。5.2 类别权重的手动设置与验证YOLOv8 默认的类别权重是均等的但你可以通过自定义损失函数或者修改数据集配置来调整。一个简单做法是在data.yaml同级目录下建一个weights.txt每行一个浮点数对应每个类别的权重然后训练时用cls_weights参数加载。权重值可以按类别样本数的倒数来设比如car有 5000 个框cone有 100 个框那cone的权重就是car的 50 倍。但别设得太极端否则模型会疯狂误检少数类。# 根据类别框数计算权重并做归一化 import numpy as np counts np.array([5000, 800, 600, 3000, 1200, 400, 200, 100]) # 按类别顺序 weights 1.0 / (counts 1e-6) weights weights / weights.sum() * len(weights) # 归一化均值约为1 print(类别权重:, weights.round(3))这段代码输出的权重可以直接写进训练配置。注意1e-6是防止除零。归一化后权重均值约为 1不会整体放大 loss。设完权重后重新训练并对比混淆矩阵看少数类的召回率有没有提升同时确认多数类的精度没有掉太多。5.3 一个具体技巧用切片推理提升小目标召回交通目标检测里小目标漏检是老大难。除了提高输入尺寸还有一个技巧是切片推理把一张大图切成若干重叠的小图分别推理后再把框合并回去。这对远处的小车、锥桶特别有效。YOLO 官方没有直接提供切片推理但可以用sahi库或者自己写一个简单的滑动窗口。from ultralytics import YOLO import cv2 import numpy as np model YOLO(runs/traffic/exp1/weights/best.pt) img cv2.imread(test_road.jpg) h, w img.shape[:2] tile_size 640 overlap 128 boxes [] for y in range(0, h, tile_size - overlap): for x in range(0, w, tile_size - overlap): tile img[y:ytile_size, x:xtile_size] if tile.shape[0] 32 or tile.shape[1] 32: continue results model(tile, verboseFalse) for box in results[0].boxes: xyxy box.xyxy[0].cpu().numpy() xyxy[0] x xyxy[1] y xyxy[2] x xyxy[3] y boxes.append(xyxy) # 这里省略 NMS 合并步骤实际使用需用 cv2.dnn.NMSBoxes 或 torchvision.ops.nms print(f切片推理共得到 {len(boxes)} 个候选框)tile_size和overlap是核心参数。tile_size一般设成训练时的imgszoverlap设成tile_size的 20% 左右保证边缘目标不被切掉。切片推理的代价是推理时间成倍增加适合对召回率要求高、对实时性要求不苛刻的场景。合并框时记得做 NMS否则同一个目标会被重复检测。5.4 验证方法用新场景视频做一次端到端测试训练指标再好最终还是要拿新数据验证。我习惯找一段和训练集不同路段、不同天气的行车视频抽帧后跑推理然后人工看漏检和误检。重点看三类一是远处小目标有没有框出来二是被遮挡的目标有没有漏三是类别有没有混淆。如果这三类问题在可接受范围内这份数据集和模型才算真正可用。最后说一个我的习惯每次训完模型把best.pt和对应的data.yaml、类别权重、训练命令一起存到一个带日期的文件夹里。过一个月回头看能清楚知道当时用了什么配置不用靠回忆去猜。多类别交通目标检测这件事数据质量决定上限训练技巧只是逼近上限。希望帮到你。本文还有配套的精品资源点击获取