简介:中国交通标志TT100K检测数据集面向从事智能交通、自动驾驶与计算机视觉研究的开发者及学生,提供交通标志识别与检测任务的训练与测试素材。资源包共2000个文件,以1999个XML标注文件为主,另含1个使用说明txt,压缩包约401.64MB,图片与标注一一对应,兼容Pascal VOC与YOLO两种主流格式,可直接接入常见检测框架。数据集覆盖45类中国常见交通标志,共7962张交通场景图片,每张均配有VOC格式XML与YOLO格式TXT标注,便于开展卷积神经网络特征提取、分类与实时检测算法实验,也适合大规模深度学习模型训练以提升泛化能力。目前已有1337人学习下载,配套博文提供使用指南与问题解答,帮助读者快速上手并减少排错成本,为智能交通相关技术研发提供扎实的数据支撑。
1. 7962 张中国交通标志图,VOC 与 YOLO 双格式到底怎么落地
做交通标志检测的同行大概都有过这种体验:公开数据集要么是国外路况,要么类别少得可怜,想训一个能认中国路牌的模型,光找数据就得耗掉大半天。这份 TT100K 检测数据集把 7962 张交通场景 jpg 图片、7962 个 Pascal VOC 格式 xml、7962 个 YOLO 格式 txt 一次性打包齐了,45 个中国常见交通标志类别,图片和标注一一对应,不用自己再去做格式转换的脏活。它适合三类人:刚入门目标检测想找个真实场景练手的新手、要快速验证 YOLO 系列模型效果的老手、以及做智能交通或辅助驾驶原型需要现成标注数据的开发者。压缩包解压后就是图片加标注的平铺结构,没有多余的目录层级,拿到手就能直接喂给训练脚本,省掉的是最枯燥也最容易出错的那段准备工作。
2. 双格式标注拆解:VOC 的 xml 和 YOLO 的 txt 各管什么
2.1 两种格式的定位差异
Pascal VOC 格式用 xml 描述每张图里每个目标的类别和边界框,坐标是绝对像素值,结构清晰、可读性强,适合做数据审查、可视化验证和需要精确坐标回算的场景。YOLO 格式用 txt 描述,每行一个目标,格式是类别索引 中心x 中心y 宽 高,坐标全部归一化到 0 到 1 之间,直接对应 YOLO 系列训练时的标签读取逻辑。这份数据集同时给了两套,意味着你不需要在训练前跑转换脚本,VOC 那套可以用来核对标注质量,YOLO 那套直接进 dataloader。
实际用的时候有个细节要注意:YOLO 的类别索引是从 0 开始的整数,而 VOC 的 xml 里写的是类别名称字符串。两者之间的映射关系必须自己维护一份,否则训练出来的模型类别会对不上。常见做法是先从所有 xml 里把类别名提取出来,排好序生成一个classes.txt,YOLO 的索引就按这个顺序来。
2.2 从 xml 提取类别清单并生成映射
下面这段脚本遍历所有 xml,统计类别出现次数并输出排序后的类别列表,同时生成 YOLO 训练需要的classes.txt:
import os import glob import xml.etree.ElementTree as ET from collections import Counter # 指向解压后存放 xml 的目录 xml_dir = "./TT100K/annotations" xml_files = glob.glob(os.path.join(xml_dir, "*.xml")) counter = Counter() for xf in xml_files: tree = ET.parse(xf) root = tree.getroot() for obj in root.findall("object"): name = obj.find("name").text.strip() counter[name] += 1 # 按类别名排序,保证索引稳定 classes = sorted(counter.keys()) print(f"共 {len(classes)} 类") for i, c in enumerate(classes): print(i, c, counter[c]) # 写出 classes.txt,YOLO 训练时按行读取 with open("classes.txt", "w", encoding="utf-8") as f: for c in classes: f.write(c + "\n")逻辑说明:ET.parse逐个解析 xml,findall("object")拿到所有标注目标,find("name").text取类别名。用Counter统计每类出现次数,方便判断有没有长尾类别。排序是为了让索引固定,避免每次跑出来顺序不一样导致标签错位。参数上唯一要改的是xml_dir,指向你实际解压的路径。跑完会得到一份classes.txt,45 行,每行一个类别名,这个文件后面训练和推理都要用。
2.3 校验图片与标注的一一对应
数据集号称 7962 张图对应 7962 个 xml 和 7962 个 txt,但实际解压后偶尔会因为文件名大小写、扩展名不一致导致对不上。训练前花两分钟校验一遍,比训到一半报错强:
import os img_dir = "./TT100K/images" xml_dir = "./TT100K/annotations" txt_dir = "./TT100K/labels" imgs = {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith(".jpg")} xmls = {os.path.splitext(f)[0] for f in os.listdir(xml_dir) if f.endswith(".xml")} txts = {os.path.splitext(f)[0] for f in os.listdir(txt_dir) if f.endswith(".txt")} print("图片数:", len(imgs)) print("xml 数:", len(xmls)) print("txt 数:", len(txts)) print("图片有但 xml 缺:", imgs - xmls) print("xml 有但图片缺:", xmls - imgs) print("图片有但 txt 缺:", imgs - txts)逻辑说明:用集合做差集,能直接定位到缺失的文件名。如果三个集合完全相等,说明数据完整。如果有差异,优先检查是不是扩展名大小写问题,比如.JPG和.jpg。这一步不涉及复杂参数,纯粹是数据完整性检查,建议每次拿到新数据集都跑一遍。
3. 把数据集接进 YOLO 训练流程:目录结构与配置文件
3.1 整理成 YOLO 要求的目录树
YOLO 系列训练时对目录结构有固定要求,图片和标签要分开放,且路径要能对应上。推荐整理成下面这样:
TT100K_YOLO/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── tt100k.yaml图片放images/train和images/val,对应的 txt 放labels/train和labels/val,文件名保持一致,只是扩展名不同。划分比例常见做法是 8:2 或 9:1,交通标志场景下类别分布可能不均匀,建议按类别分层抽样,避免某些稀有类别在验证集里一个都没有。
3.2 编写数据集配置文件
YOLO 训练需要一个 yaml 文件告诉它数据在哪、有几类、类别名是什么:
# tt100k.yaml path: ./TT100K_YOLO train: images/train val: images/val nc: 45 names: 0: 限速40 1: 限速50 # ... 按 classes.txt 的顺序补全 45 行逻辑说明:path是数据集根目录,train和val是相对路径。nc是类别数,必须和classes.txt行数一致。names是索引到类别名的映射,顺序必须和生成classes.txt时的排序完全一致,否则模型学到的类别会张冠李戴。参数上唯一容易错的是nc,写多了或写少了训练时不会立刻报错,但推理结果会乱。
3.3 启动训练与关键参数
以 YOLOv8 为例,一条命令就能跑起来:
yolo detect train \ data=tt100k.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ device=0 \ project=runs/tt100k \ name=exp1逻辑说明:data指向刚写的 yaml,model用预训练权重起步,交通标志这种中等规模数据集从yolov8n或yolov8s开始比较稳。epochs设 100 是常见起点,如果验证集 mAP 还在涨可以加到 200。imgsz设 640 是 YOLO 的默认输入尺寸,交通标志在图中占比通常不大,如果发现小目标漏检多,可以提到 960 或 1280,但显存占用会明显上升。batch根据显存调,16 在 8G 显存上跑 640 尺寸基本够用。device=0指定第一块 GPU,没有 GPU 就改成cpu,但训练时间会拉长很多。
训练过程中重点看三个指标:box_loss是否稳定下降、mAP50是否在涨、mAP50-95是否跟着动。如果box_loss震荡厉害,先把batch调小或学习率降一档。如果mAP50早早卡住不动,检查类别映射有没有错位。
4. 避坑与排查:标注、类别和训练里最容易翻车的地方
4.1 类别索引错位导致模型学偏
现象:训练 loss 正常下降,但推理时所有标志都被识别成同一类,或者类别完全对不上。
原因:classes.txt的排序和 yaml 里names的顺序不一致。比如classes.txt是按拼音排序,yaml 里手写时按别的顺序填了,索引就对不上了。
解决:生成classes.txt后直接用它来生成 yaml 的names段,不要手动敲。写个小脚本把classes.txt转成 yaml 格式,保证两边同源。
4.2 图片和标签文件名不匹配
现象:训练启动后报No labels found或者大量图片被跳过。
原因:图片是.jpg,标签是.txt,但有些文件名里带了空格或特殊字符,或者大小写不一致。Windows 解压时有时会把文件名转成小写,而标注文件保留原大小写。
解决:跑一遍第 2.3 节的校验脚本,把差集里的文件名列出来,统一改成小写或统一去掉特殊字符。批量重命名用 Python 的os.rename几行就能搞定。
4.3 小目标漏检严重
现象:大标志能检出,远处的小标志基本漏掉,mAP 上不去。
原因:TT100K 里不少图片是广角交通场景,标志在图中只占几十个像素,640 输入尺寸下经过多次下采样后特征几乎消失。
解决:把imgsz提到 960 或 1280,同时开 YOLO 的多尺度训练multi_scale=True。如果显存不够,改用yolov8s或更小的模型,或者把batch降到 8。另一个思路是在数据增强里加mosaic和copy_paste,增加小目标的出现频率。
4.4 验证集类别分布不均导致指标虚高
现象:mAP50 看着不错,但实际跑视频发现某些类别几乎检不出来。
原因:随机划分验证集时,稀有类别可能全被分到训练集,验证集里没有样本,指标只反映了常见类别的表现。
解决:划分时按类别分层抽样,保证每个类别在验证集里至少有几十个样本。用sklearn的train_test_split加stratify参数,或者自己写个按类别分组的划分逻辑。
4.5 训练中途 BN 层崩溃
现象:loss 突然变成 NaN,训练中断。
原因:学习率太大或者 batch 太小,导致 BatchNorm 的统计量不稳定。交通标志数据集里有些图片对比度很高,梯度容易炸。
解决:把初始学习率降到0.001或更低,开warmup_epochs=3让模型先稳几轮。如果 batch 只能设到 4 或 8,考虑用yolov8n这种小模型,或者把imgsz降到 512 换取更大的 batch。
5. 进阶用法:用这份数据做迁移学习和效果验证
5.1 从 TT100K 预训练到自定义场景微调
如果你手头有自己采集的交通标志数据,但量不大,可以先用这份 7962 张的数据集训一个基础模型,再在自己的小数据集上微调。具体做法是:先用tt100k.yaml跑 100 到 200 轮,得到一个best.pt,然后把自己的数据整理成同样的 YOLO 格式,新建一个 yaml,把model指向best.pt,学习率调到0.0001,跑 30 到 50 轮。这样比从 COCO 预训练权重起步更贴近交通标志的域,收敛更快。
# 第一步:在 TT100K 上预训练 yolo detect train data=tt100k.yaml model=yolov8s.pt epochs=150 imgsz=960 batch=8 # 第二步:在自定义小数据集上微调 yolo detect train data=my_signs.yaml model=runs/tt100k/exp1/weights/best.pt \ epochs=50 lr0=0.0001 imgsz=960 batch=8逻辑说明:第一步的imgsz=960是为了让模型适应小目标,第二步保持一致避免尺寸跳变。lr0=0.0001是微调时的常见学习率,比从头训练低一个数量级,防止把预训练学到的特征冲掉。
5.2 用验证集做一轮完整的效果核查
训练完不能只看 mAP 数字,建议跑一遍验证并导出混淆矩阵和 PR 曲线:
yolo detect val model=runs/tt100k/exp1/weights/best.pt data=tt100k.yaml imgsz=960跑完会在runs/tt100k/exp1/下生成confusion_matrix.png和PR_curve.png。重点看混淆矩阵里哪些类别之间互相误判,比如限速 40 和限速 50 容易混,说明模型对数字区域的细节分辨不够,可以考虑在数据增强里加随机裁剪和缩放,或者换更大的输入尺寸。
5.3 一个我踩过的坑
有次我图省事,直接把classes.txt里的类别名按字母序排了,yaml 里names却按中文拼音顺序手写的,结果训练完模型把所有圆形标志都认成同一类。排查了半天才定位到是索引错位。从那以后我每次生成classes.txt和 yaml 都强制走同一个脚本,绝不手动敲类别名。另外,验证集划分我固定用stratify按类别分层,跑完第一轮先看混淆矩阵再决定要不要调参,不盲目加 epoch。希望这些经验能帮你少走点弯路,这份数据集本身质量不错,把格式和类别映射这两步做扎实,后面训练基本就是调参的事了。
本文还有配套的精品资源,点击获取