ARTICLE DETAIL

资讯详情

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

自动驾驶多类别交通目标检测数据集处理与YOLO训练实战

自动驾驶多类别交通目标检测数据集处理与YOLO训练实战 简介一份面向自动驾驶感知、智能交通与车载高级驾驶辅助系统开发者的多类别交通目标检测数据集覆盖汽车、公交车、警车、救护车、交通标志、行人、自行车、摩托车等30类核心交通要素场景涵盖日间/夜间、城市/山区、静态/动态并纳入东南亚特色交通工具可用于L2-L4级自动驾驶多目标实时检测、交通流量监控、异常事件识别等模型训练。资源包共2000个文件以JPG图像与TXT标注文件为主配套YOLO格式的yaml配置及docx说明文档整体约65.55MB。已有75人学习下载。数据集按训练802张、验证229张、测试114张划分标注经专业团队校验对交通标志定位精确、车辆重叠场景标注完整可直接适配YOLO等主流目标检测框架支持生成交通要素分布热力图、识别特殊车辆优先级、开展复杂场景鲁棒性研究便于从算法开发到工程落地快速验证。1. 下载了这个数据集先别急着喂给检测模型拿到「自动驾驶多类别交通目标检测数据集7.zip」这类压缩包第一反应不是解压后立刻开训。多类别交通目标检测是自动驾驶感知里最吃数据质量的环节压缩包里几十个类别、几万张图如果直接丢进 YOLO 训练大概率会在类别 ID 错位、标注越界、小目标丢失这些坑里翻车。这份笔记按我平时处理这类数据集的工作流来写从解压看结构、跑质检脚本、转 YOLO 格式、切分训练集到最后的难例挖掘和部署侧验证每一步都给可直接复用的代码和参数。适合正在做自动驾驶感知算法验证、用 YOLO 系列跑自己的交通目标检测数据集、或者被数据标注格式折腾过的工程师。2. 解压后先看结构目录、标注文件与多类别类别体系2.1 目录层级与文件命名规则这类数据集解压后先别急着数图片第一步是把目录结构完整列出来。常见做法是先用tree或者find摸清层级关系同时统计图片数量和标注文件数量是否对上。# 解压保留权限和目录结构 unzip 自动驾驶多类别交通目标检测数据集7.zip -d autodata # 统计图片数量和标注数量 find autodata -type f \( -name *.jpg -o -name *.png -o -name *.jpeg -o -name *.bmp \) | wc -l find autodata -type f \( -name *.xml -o -name *.json -o -name *.txt \) | wc -l # 输出前两层目录结构方便快速看清组织方式 find autodata -maxdepth 2 -type d | sort这里find的两个统计命令要分别执行如果图片数和标注文件数不一致说明有漏标或空标注样本后续训练时这些样本会被当作负样本或直接报错。目录层级决定数据加载方式如果每类一个文件夹是 ImageNet 式组织如果所有图片平铺、标注文件同名不同后缀是 VOC 式组织如果标注全部汇总成一个大 JSON则是 COCO 式组织。交通目标检测数据集常见的是平铺结构标注文件与图片同名只是后缀从.jpg变成.xml或.txt。文件名里常带采集时间、路段编号或天气标记比如city_road_a_day_000123.jpg这种命名里往往藏着场景信息后续分层切分要用到。2.2 标注格式框、类别、截断与遮挡字段多类别交通数据集的标注格式不外乎三种VOC XML、COCO JSON、KITTI Txt。打开一个标注文件看一眼马上就能判断属于哪种。VOC 格式用 XML 描述object节点每个目标包含name、bndboxKITTI 格式用纯文本行每行依次是类别、截断程度、遮挡程度、2D 框坐标COCO 格式则把图片信息放images数组、目标信息放annotations数组。我一般会写一个小脚本把三种格式统一解析成内存里的通用结构再做后续处理。以下代码兼容 VOC 和 KITTI 两种文本格式COCO JSON 也一并支持import json import xml.etree.ElementTree as ET from dataclasses import dataclass, field dataclass class Target: class_name: str x1: float y1: float x2: float y2: float truncated: float 0.0 # 截断比例 0~1 occluded: int 0 # 遮挡等级 0~3 dataclass class Sample: image_path: str width: int height: int targets: list field(default_factorylist) def parse_voc(xml_path, image_dir): tree ET.parse(xml_path) root tree.getroot() sample Sample( image_pathimage_dir / root.findtext(filename), widthint(root.findtext(size/width)), heightint(root.findtext(size/height)), ) for obj in root.iter(object): sample.targets.append(Target( class_nameobj.findtext(name), x1float(obj.findtext(bndbox/xmin)), y1float(obj.findtext(bndbox/ymin)), x2float(obj.findtext(bndbox/xmax)), y2float(obj.findtext(bndbox/ymax)), )) return sample def parse_kitti(txt_path, image_dir): sample Sample(image_pathtxt_path.replace(.txt, .jpg), width-1, height-1) with open(txt_path, r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) 15: continue sample.targets.append(Target( class_nameparts[0], truncatedfloat(parts[1]), occludedint(parts[2]), x1float(parts[4]), y1float(parts[5]), x2float(parts[6]), y2float(parts[7]), )) return sampleKITTI 格式缺图片宽高需要后续用cv2.imread补上不能依赖-1直接做归一化。VOC 和 KITTI 的坐标都是像素坐标COCO 的 bbox 是[x, y, width, height]也是像素坐标。所有格式在转 YOLO 前必须统一成像素坐标系下的x1, y1, x2, y2否则归一化会出错。2.3 类别体系与不均衡分布多类别交通数据集典型类别包括car、truck、bus、motorcycle、bicycle、pedestrian、rider、traffic light、traffic sign 这几大类。如果数据集采集自欧洲或日本路况可能还有 tram、train自卸卡车和拖车有时单独成类。先跑一次类别统计看看每类有多少目标、多少张图包含该类from collections import Counter def count_classes(samples): class_counter Counter() class_image_counter Counter() for sample in samples: seen set() for t in sample.targets: class_counter[t.class_name] 1 if t.class_name not in seen: class_image_counter[t.class_name] 1 seen.add(t.class_name) return class_counter, class_image_counter如果 car 有三万目标而 tram 只有两百个后面训练时tram的 AP 基本靠运气只能靠数据增强兜底或者干脆合并成粗粒度类别。另外注意类别名大小写混用Person和person会被当成两个类需要先做一次小写归一化。提示别忽略被截断目标和被遮挡目标。truncated 0.5的框在转 YOLO 时可以直接过滤掉这类样本对训练贡献小反而会引入背景噪声。3. 跑自动化质检与可视化复核不让脏标注进训练3.1 标注质量统计脚本数据集的坑往往藏在标注里而不是图片里。最常见的四类问题坐标越界框超出图片边界、坐标倒置x2 小于 x1、零尺寸框、类别名与预期类别表不一致。这些脏标注如果不清洗训练时 loss 会异常震荡表现就是验证集 mAP 上不去但训练 loss 也不降。先写一个遍历全部标注文件的质检脚本import cv2 def validate_sample(sample, class_list, report): h, w sample.height, sample.width if h 0 or w 0: # 自动读图补尺寸 img cv2.imread(sample.image_path) h, w img.shape[:2] sample.height, sample.width h, w for t in sample.targets: cls t.class_name.lower() if cls not in class_list: report.append(f{sample.image_path}: 未知类别 {t.class_name}) continue if t.x1 0 or t.y1 0: report.append(f{sample.image_path}: 框越界({t.class_name})) if t.x2 w or t.y2 h: report.append(f{sample.image_path}: 框超出右/下边界({t.class_name})) if t.x2 t.x1 or t.y2 t.y1: report.append(f{sample.image_path}: 坐标倒置或零面积({t.class_name}))执行时把报告重定向到report.txt逐行看。越界分两种情况轻微越界 1~2 像素直接 clamp 到边界严重越界超过图片宽高 20%说明标注工具崩了该样本直接剔除。零面积框大多是标注员误操作产生的残留节点这类框会直接破坏 YOLO 的 Anchor 匹配逻辑必须删。3.2 类别维度分布与难例定位按天气、时段、道路类型统计目标数量是这个阶段最值得做的事。很多多类别数据集的类别总量均衡但拆到夜间场景就严重偏斜——白天 car 占 60%夜间只拍到车灯和局部轮廓标注数量掉到 5%。如果最终要评估夜间检测效果必须在切分时保证夜间样本比例。我会在质检脚本里顺便输出一张按类别的统计表按目标数量降序排类别目标数图片数平均每图目标数car31245189021.65pedestrian15234101201.51traffic light421028151.50tram156891.75平均每图目标数小于 1 的类别要特别注意说明大量图片里该类只出现一次且多半是小目标。交通灯、交通标志就属于典型的小目标类别它们在图里只占几十个像素转 YOLO 后如果输入分辨率低几乎必丢。3.3 随机抽样可视化复核统计脚本查不出类别贴错这种语义问题只能靠人眼抽样。我一般每类抽 20~40 张图用 OpenCV 把框画出来拼成网格图一次看一个类别import cv2 import math def draw_grid(samples, grid(4, 4), cell_size(640, 640)): rows, cols grid canvas [] for i, sample in enumerate(samples[: rows * cols]): img cv2.imread(sample.image_path) img cv2.resize(img, cell_size) for t in sample.targets: x1, y1, x2, y2 map(int, (t.x1, t.y1, t.x2, t.y2)) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, t.class_name, (x1, max(20, y1 - 6)), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) canvas.append(img) canvas [cv2.hconcat(canvas[r * cols:(r 1) * cols]) for r in range(rows)] grid_img cv2.vconcat(canvas) return grid_img看可视化图时重点确认三件事框是否紧贴目标轮廓、类别名是否贴错、互相遮挡的目标是否被标全。交通灯和交通标志是重灾区红色圆灯被标成 stop sign、远处行人只标了上半身这些错误靠脚本查不出来只能抽样看。一个类别如果错标率超过 5%建议回头跟标注方核对别硬着头皮训完再后悔。4. 转成 YOLO 格式并切分最小可用训练流程4.1 VOC/COCO 转 YOLO 的核心映射YOLO 系列从 v5 到 v11需要的标注是归一化格式每行一个目标五个数值依次是class_id, x_center, y_center, width, height全部除以图片宽高归一化到 0~1。转换的坑不在于公式在于类别 ID 的映射表——必须用一个固定的classes.txt把所有类别名按行排序索引即 ID。先创建一个类别映射文件然后写转换脚本# classes.txt 示例每一行一个类别名顺序即 ID # car # pedestrian # traffic light # ... def convert_to_yolo(sample, class_to_id, out_txt_path): lines [] for t in sample.targets: cls_id class_to_id[t.class_name.lower()] x1, y1, x2, y2 t.x1, t.y1, t.x2, t.y2 x_center (x1 x2) / 2 / sample.width y_center (y1 y2) / 2 / sample.height box_w (x2 - x1) / sample.width box_h (y2 - y1) / sample.height # 过滤非法框防止训练崩溃 if box_w 0 or box_h 0 or box_w 1 or box_h 1: continue lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) with open(out_txt_path, w, encodingutf-8) as f: f.write(\n.join(lines))class_to_id字典必须只保留classes.txt中出现的类别遇到未知类别直接抛异常而不是静默跳过否则会出现漏标但不知道漏在哪的情况。前面统计脚本里的未知类别告警这里就是转 YOLO 时的硬校验。小目标转完 YOLO 后数值会很小比如宽度只占图片 0.02这时检查精度——0.020000和0.019999在浮点上没有实质影响但在做分析时可以留意。4.2 训练/验证/测试切分脚本切分最忌讳纯随机。交通数据是从连续视频帧抽出来的相邻帧几乎一样如果随机切分同一条路上的相似帧会同时落在训练集和验证集验证集指标虚高部署到新路段立刻崩。正确做法是先按采集时间段或路段分组再把组随机分到三个集合里import random, os, shutil from collections import defaultdict def split_by_group(sample_list, group_key_fn, ratios(0.8, 0.1, 0.1), seed42): random.seed(seed) groups defaultdict(list) for s in sample_list: groups[group_key_fn(s)].append(s) train_groups, val_groups, test_groups [], [], [] all_groups list(groups.keys()) random.shuffle(all_groups) n len(all_groups) n_train int(n * ratios[0]) n_val int(n * ratios[1]) train_groups all_groups[:n_train] val_groups all_groups[n_train:n_train n_val] test_groups all_groups[n_train n_val:] return ( [s for g in train_groups for s in groups[g]], [s for g in val_groups for s in groups[g]], [s for g in test_groups for s in groups[g]], )group_key_fn可以取文件名里的时间戳前缀或路段名。如果文件名没带这些信息就按文件名的哈希值模一个组号至少保证同序列相邻帧大概率落进同组。切分完记得重构目录结构images/train、images/val、images/test和对应的labels/目录YOLO 训练器的数据配置直接指向这两个根目录。提示验证集和测试集不要用同一批组。很多人在 COCO 上习惯只用 train/val但多类别交通数据集最后要做车型或场景泛化评估单独留一个 test 集能救命。4.3 数据增强与类别平衡转完格式、切完数据后先别研究 Mosaic 和 MixUp 参数。第一优先是核对每类目标在 train/val/test 三个集合中的分布是否一致。写一行统计就能验证# 统计三个集合中每个类别的目标数并输出占比 # 如果某个类别在 train 中占 5%在 val 中却占 12%说明切分不均匀需要重新调 seed 或按类别分层切分不均匀时重新换 seed 不一定有效更可靠的是做类别分层切分按图片里包含的主类别比例来分配。交通灯这种小目标类别在训练集里的占比如果低于验证集训练出来 AP 被拉低是必然的。数据增强方面我一般只用随机翻转和轻度色彩抖动交通数据集不像通用目标检测那么耐剪裁——裁掉交通灯的上文语义就变了。5. 训练与推理时常见的坑一次说清这些问题5.1 类别 ID 与配置文件错位现象loss 正常下降mAP 也正常但推理时 car 的框标成了 pedestrian。 原因转 YOLO 时按classes.txt的某一行顺序生成了 ID但训练用的 YAML 里names列表顺序跟它不一致于是class_id0在两个文件里指向不同类别。 解决用训练配置的names文件反向生成一次class_to_id让转换脚本读同一个yaml的names列表而不是自己维护一份。改完重新转换标注不要只改配置文件——YOLO 训练时标签文件里的数字已经写死改配置文件治标不治本。5.2 小目标直接消失下采样与输入分辨率现象验证集上 car 和 truck 的 AP 能到 0.85traffic light 只有 0.1几乎全部漏检。 原因YOLO 的下采样倍数通常到 32输入 640x640 时特征图只有 20x20一个 10x10 像素的交通灯在原图上映射不到一个网格Anchor 匹配直接失败。 解决把输入分辨率提到 1280 或 1536代价是显存占用翻倍、推理时延增加或者针对小目标类别单独加一层 P2 检测头。工程上先提分辨率验证 AP 提升后再评估时延是否还能接受。TRT 部署时分辨率写死训练和推理必须用同一个尺寸。5.3 验证集泄题相似帧串集合现象验证集 mAP 0.92测试集换一批新路段只有 0.61差距大得离谱。 原因纯随机切分导致同一车辆在同一路段拍摄的连续帧一部分进了训练集、一部分进了验证集。验证集相当于开卷考试模型见过的场景直接背答案。 解决按文件名的采集时间戳归组同一秒内的帧归到同一组以组为单位做切分前面split_by_group就是用在这里。如果文件名没带时间可以用图像哈希求相似度把相似度超过阈值的图片聚成同一组。这件事在动工前做事后补会废掉一半标注。5.4 挡风玻璃反光与夜间泛化场景分布太单一现象白天模型放到夜间路测行人 AP 掉一半车辆检测也漏。 原因数据集里夜间样本只占很小比例且夜间标注框常被反光和灯光干扰有些目标直接标成了背景。 解决把天气和光照信息做成数据集的元数据字段训练时按场景比例上采样——夜间样本重复 3 到 5 个 epoch或对夜间图做亮度增强、Gamma 变换扩样。如果数据集里带了雨雾样本也照此办理。自动驾驶的视觉感知本质上是多个场景模型的集合一个模型吃遍所有天气是理想情况多数落地靠的是针对夜间工况微调一个分支。5.5 标注文件编码与路径大小写现象训练时报Label Error或读不到图片且报错样本随机出现看起来像玄学。 原因部分标注文件是 UTF-8 带 BOM或 GBK 编码脚本按 UTF-8 解析时首字符混入不可见字符另外 Linux 下路径大小写敏感City_Road.jpg和city_road.jpg指向不同文件。 解决转换脚本统一用encodingutf-8-sig打开所有文本文件自动吞掉 BOM文件名在解压后先做一次批量小写归一化图片路径和标注路径保持完全一致。压缩包解压后如果是在 Windows 上转换过再拷贝到 Linux 训练路径分隔符也容易出问题建议所有路径处理统一用os.path.join而不是手写斜杠。6. 进阶用难例挖掘和部署侧验证把模型推到能上车数据集清洗干净、模型训练收敛之后别急着写报告。自动驾驶场景的验收标准不是 mAP 有多高而是决策延迟能不能卡住。常见参考指标是决策延迟 32.8 毫秒——差不多对应 30 FPS 的处理节奏城市道路的 L2/L2 场景按这个预算做推理时序是够用的如果模型分辨率提到 1280 以上或者用了大模型检测头这个预算会被吃掉不少需要 TensorRT FP16 甚至 INT8 量化来换时间。精度再高延迟卡不住系统就不考虑上车。难例挖掘是这个阶段最值钱的操作。拿已经训练好的模型在验证集上跑一遍推理把置信度在 0.2~0.5 之间的预测全部导出来。这些不一定是误检很多是模型「犹豫」的目标——真实存在但特征不清晰。把它们按类别聚类挑出重复出现的难例帧加入训练集重训一轮# 用训练好的模型跑验证集输出低置信度预测 # 按类别统计出现频次频次最高的前 1000 帧提取为难例集 # 与原始训练集合并重训一个 epoch 做增量学习增量重训时把学习率调低到初始值的 1/10防止难例把已学到的特征冲掉。同时监控每一类的 AP 变化如果某个类别在难例集加入后 AP 不升反降说明难例和原始样本分布冲突要检查是标注错位还是类别定义边界模糊。另一件值得做的事是类别合并与重定义。多类别数据集里的 pedestrian 和 rider 如果标注边界不统一模型训练时就会持续摇摆。我习惯在难例挖掘之后做一次类别归并决策把 motor、motorcycle、rider 合并成一类two_wheeler把 bus 和 truck 合并为heavy_vehicle类别少了每个类的样本量涨上去AP 往往比强行维持细粒度更高。这是做自动驾驶感知数据集的落地经验——多类别不是越多越好类别体系要和下游规划模块的需求对齐决策模块关心的是「前方路面上有没有可移动障碍物、它有多快」而不是「这辆是丰田还是大众」。做清楚这一步整个数据集的价值才算真正兑现。最后落地前一定要做的验证随机抽一段模型从未见过的连续帧序列跑一遍推理记录每一帧的漏检和误检数一下连续多少帧能稳定锁定同一个目标。单帧 mAP 高不代表时序上可用目标在第 3 帧丢了又出现在第 7 帧这种抖动在自动驾驶里比稳定误检更危险。我习惯把这个验证脚本固化下来每次换数据集、换模型都先跑一遍省得在车上翻车。希望这个流程能帮你在自己的多类别交通目标检测数据集上少走几趟弯路。本文还有配套的精品资源点击获取
返回列表