简介:本资源为面向目标检测学习者的YOLO红绿灯检测数据集,适用于智能交通、自动驾驶感知等场景的模型训练与课程实践,可帮助读者快速获得高质量标注数据,省去自行采集与标注的成本。压缩包共约2000个文件,以1986个xml标注文件为主,另含少量html说明文档、txt列表与py脚本,整体约946.4MB,涵盖voc、coco和yolo三种格式标签,分别存放于不同文件夹,可直接接入YOLO系列模型训练。资源同时附赠环境搭建与训练案例教程,覆盖Windows与Linux版本,并提供训练集、验证集、测试集划分脚本,可按需生成ImageSets下的txt划分文件,方便复现实验流程。目前已有757人学习下载,适合具备一定深度学习基础、希望快速上手红绿灯检测任务的读者参考使用。
1. 红绿灯检测数据集怎么选:5000 张图 + 三格式标签到底省了哪些事
做交通场景目标检测的同行大概率都经历过这个阶段:模型结构调了半天,mAP 死活上不去,最后发现是数据集的坑。红绿灯检测尤其典型——目标小、遮挡多、逆光过曝、夜间偏色,随便一个因素就能让训练曲线变成玄学。这份 YOLO 红绿灯目标检测数据集,核心价值不在于 5000 张图片这个数字本身,而在于它把 VOC、COCO、YOLO 三种标签格式和划分脚本一起打包了。这意味着你拿到手之后,不需要再花两三天写格式转换脚本、检查标注对齐、手动切分训练验证集,直接进入模型训练环节。适合刚入门 YOLO 目标检测、想跑通完整流程的开发者,也适合需要快速验证改进算法效果、不想在数据准备上耗时间的老手。下面从数据组织、格式转换、训练配置到踩坑排查,把这份资源拆开讲清楚。
2. 数据集结构与三种标签格式的对应关系
2.1 5000 张图片的目录组织与划分逻辑
拿到压缩包解压后,常见的目录结构是这样的:
dataset/ ├── images/ │ ├── 000001.jpg │ ├── 000002.jpg │ └── ... ├── labels_voc/ │ ├── 000001.xml │ └── ... ├── labels_coco/ │ └── annotations.json ├── labels_yolo/ │ ├── 000001.txt │ └── ... └── split.py图片统一放在images/下,三种标签格式各自独立目录。VOC 格式是每张图对应一个 XML 文件,COCO 格式是一个大的 JSON 文件,YOLO 格式是每张图对应一个 TXT 文件。划分脚本split.py负责按比例生成train.txt、val.txt、test.txt三个索引文件。
这里有个容易忽略的点:图片文件名和标签文件名必须严格一一对应。VOC 和 YOLO 格式下,000001.jpg对应000001.xml或000001.txt,差一个字符就会在训练时被跳过,而且 YOLO 默认不报错,只是静默丢弃。我一般会在训练前跑一遍文件名匹配检查,确认没有孤儿图片或孤儿标签。
提示:解压后先别急着改目录结构,划分脚本里的路径是按默认结构写死的,改了路径就要同步改脚本里的
img_dir和label_dir变量。
2.2 VOC、COCO、YOLO 三种格式的字段差异
三种格式的本质区别在于坐标表示方式和文件组织粒度。VOC 用绝对像素坐标,COCO 也用绝对像素坐标但统一存一个 JSON,YOLO 用归一化后的中心点坐标加宽高。具体对比如下:
| 维度 | VOC (XML) | COCO (JSON) | YOLO (TXT) |
|---|---|---|---|
| 坐标格式 | xmin, ymin, xmax, ymax | x, y, width, height | cx, cy, w, h(归一化) |
| 坐标单位 | 像素绝对值 | 像素绝对值 | 0~1 相对值 |
| 文件粒度 | 每图一个 XML | 全部图片一个 JSON | 每图一个 TXT |
| 类别字段 | <name>标签 | categories数组 | 行首数字索引 |
| 是否含图像尺寸 | 含 width/height | 含 image width/height | 不含,需从图片读取 |
YOLO 格式最精简,但代价是丢失了图像尺寸信息。如果你后续要把 YOLO 标签转回 VOC 做可视化验证,必须从图片文件重新读取宽高,否则坐标会全错。常见做法是用 OpenCV 或 PIL 读图获取尺寸,再乘以归一化坐标还原。
COCO 格式的 JSON 结构相对复杂,包含images、annotations、categories三个顶层字段。annotations里的image_id关联到images里的id,category_id关联到categories里的id。很多格式转换脚本翻车就翻在这个关联关系上——image_id从 0 开始还是从 1 开始,不同脚本实现不一样,对不上就全乱。
2.3 划分脚本的参数配置与执行
划分脚本通常支持按比例切分,核心参数是训练集、验证集、测试集的比例。常见做法是 7:2:1 或 8:1:1。执行方式:
python split.py \ --img_dir ./images \ --label_dir ./labels_yolo \ --train_ratio 0.7 \ --val_ratio 0.2 \ --test_ratio 0.1 \ --output_dir ./splits \ --seed 42参数说明:--img_dir指向图片目录,--label_dir指向 YOLO 格式标签目录(划分脚本一般只处理 YOLO 格式,因为训练时用的是这个),--seed固定随机种子保证可复现。脚本执行后会生成train.txt、val.txt、test.txt,每行是一个图片的绝对路径或相对路径。
这里有个血泪经验:划分脚本默认可能只按图片切分,不检查对应标签是否存在。如果某张图没有标签文件,切进训练集后 YOLO 会把它当负样本处理,如果负样本比例过高,模型会偏向预测背景。我一般会在划分后统计一下三个集合的标签文件数量,确认和图片数量一致。
import os def check_label_match(img_dir, label_dir, split_file): with open(split_file, 'r') as f: img_paths = [line.strip() for line in f if line.strip()] missing = [] for img_path in img_paths: base = os.path.splitext(os.path.basename(img_path))[0] label_path = os.path.join(label_dir, base + '.txt') if not os.path.exists(label_path): missing.append(base) print(f"缺失标签的图片数量: {len(missing)}") if missing: print("前10个:", missing[:10]) return missing check_label_match('./images', './labels_yolo', './splits/train.txt')这段代码遍历划分文件里的每张图片,检查对应 TXT 标签是否存在。输出缺失列表后,可以选择补标、删除或移到单独目录。别小看这一步,5000 张图里混进几十张无标签图,训练时 loss 曲线会莫名其妙抖动。
3. 从零跑通 YOLO 训练:配置文件与关键参数
3.1 数据配置文件 data.yaml 的写法
YOLO 系列(v5/v8/v11 等)训练时需要一个 YAML 文件描述数据集路径和类别信息。典型写法:
path: /home/user/dataset train: splits/train.txt val: splits/val.txt test: splits/test.txt nc: 3 names: 0: red 1: green 2: yellowpath是数据集根目录,train/val/test是相对于根目录的索引文件路径。nc是类别数,names是类别名到索引的映射。红绿灯检测通常分三类:红灯、绿灯、黄灯。如果数据集只标了红绿两类,nc改成 2,names同步删掉 yellow。
容易翻车的地方:names的顺序必须和标签文件里的类别索引一致。YOLO 标签每行第一个数字是类别索引,如果训练时names写反了,模型会把红灯学成绿灯。我一般会从标签文件里统计一下类别分布,确认索引和名称的对应关系。
import os from collections import Counter def count_classes(label_dir): counter = Counter() for fname in os.listdir(label_dir): if not fname.endswith('.txt'): continue with open(os.path.join(label_dir, fname), 'r') as f: for line in f: parts = line.strip().split() if parts: counter[int(parts[0])] += 1 print("类别分布:", dict(counter)) return counter count_classes('./labels_yolo')输出类似{0: 3200, 1: 2800, 2: 900},说明索引 0 的类别样本最多,索引 2 最少。如果黄灯样本只有 900 个,训练时可以考虑过采样或调整 loss 权重,否则黄灯检测效果会明显偏弱。
3.2 训练命令与超参数设置
以 YOLOv8 为例,训练命令:
yolo detect train \ data=./data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ device=0 \ project=./runs/traffic_light \ name=exp1参数逐个说:model=yolov8n.pt用 nano 版本做基线,速度快、显存占用低,适合先跑通流程;imgsz=640是输入分辨率,红绿灯目标偏小,如果显存够可以提到 1280,但训练时间会翻倍;batch=16根据显存调整,8G 显存跑 640 分辨率一般能到 16,跑 1280 可能只能到 4;lr0=0.01是初始学习率,YOLOv8 默认用 SGD 时这个值比较稳,用 Adam 的话建议降到 0.001;patience=20是早停耐心值,20 个 epoch 验证指标不提升就停。
注意:如果从 COCO 预训练权重开始微调,
lr0可以设小一点(0.001~0.005),避免把预训练特征打散。如果从头训练,0.01 是常见起点。
训练过程中重点看三个指标:box_loss是否稳定下降、mAP50是否持续上升、precision和recall是否平衡。如果box_loss下降但mAP不涨,大概率是过拟合,需要加数据增强或减模型复杂度。如果mAP震荡剧烈,检查学习率是否过大或 batch size 是否太小。
3.3 用 COCO 预训练权重做迁移学习的配置差异
如果不想从头训,可以用 COCO 预训练权重做迁移学习。YOLOv8 的yolov8n.pt本身就是 COCO 预训练权重,直接加载即可。但要注意类别数不匹配的问题:COCO 有 80 类,红绿灯只有 3 类,加载时 YOLO 会自动替换检测头,只保留 backbone 和 neck 的权重。
如果想用更大的模型,比如yolov8m.pt或yolov8l.pt,显存占用会明显增加。以 640 分辨率、batch=16 为例,nano 版本大概占 4G 显存,medium 版本占 8G 左右,large 版本可能超过 12G。选模型的原则是:先跑通 nano,确认数据和流程没问题,再逐步换大模型看收益。
from ultralytics import YOLO model = YOLO('yolov8n.pt') results = model.train( data='./data.yaml', epochs=100, imgsz=640, batch=16, lr0=0.01, patience=20, device=0, pretrained=True, optimizer='SGD', cos_lr=True, close_mosaic=10 )close_mosaic=10表示最后 10 个 epoch 关闭 Mosaic 数据增强,让模型在更接近真实分布的图像上微调,通常能涨 1~2 个点 mAP。cos_lr=True启用余弦学习率调度,比阶梯下降更平滑。这些是 YOLOv8 训练红绿灯数据的常用配置,不是必须,但加上去大概率有收益。
4. 避坑与排查:红绿灯数据集训练中的五个高频问题
4.1 训练 loss 不下降或直接变 NaN
现象:启动训练后box_loss一直是nan或者前几个 epoch 就爆掉。
原因:最常见的是标签坐标越界。YOLO 格式要求归一化坐标在 0~1 之间,如果标注时框超出了图像边界,转换后可能出现负值或大于 1 的值。另一个原因是学习率过大,尤其是从头训练时。
解决:先跑一遍标签合法性检查,把所有坐标 clamp 到 [0,1]。然后降低初始学习率到 0.001 再试。如果还不行,检查图片是否有损坏文件,用 PIL 逐张打开验证。
from PIL import Image import os def validate_images(img_dir): bad = [] for fname in os.listdir(img_dir): if not fname.lower().endswith(('.jpg', '.png', '.jpeg')): continue try: img = Image.open(os.path.join(img_dir, fname)) img.verify() except Exception as e: bad.append((fname, str(e))) print(f"损坏图片数量: {len(bad)}") for item in bad[:10]: print(item) return bad validate_images('./images')4.2 mAP 虚高但实际检测效果差
现象:验证集mAP50跑到 0.9 以上,但拿真实路测视频跑,漏检和误检都很严重。
原因:训练集和验证集分布太接近,甚至可能来自同一段视频的连续帧。连续帧之间差异极小,模型相当于在“背答案”。另外,如果验证集里红灯样本占绝大多数,mAP会被红灯拉高,绿灯和黄灯的实际表现被掩盖。
解决:划分数据集时按视频来源或时间段切分,确保验证集和训练集来自不同场景。同时看每个类别的 AP,不要只看总体 mAP。YOLO 训练日志里会输出每个类别的AP,如果某一类明显偏低,针对性地补样本。
4.3 小目标漏检严重
现象:远处红绿灯或者画面中占比很小的灯,模型基本检测不到。
原因:640 分辨率下,小目标经过 backbone 多次下采样后,特征图上的响应区域可能只有几个像素。YOLO 的 P3 检测头负责小目标,但如果目标小于 8x8 像素,P3 也很吃力。
解决:提高输入分辨率到 1280,或者用带 P2 检测头的 YOLO 变体(如 YOLOv8-p2)。另一个办法是切片推理(SAHI),把大图切成小块分别检测再合并,对小目标效果明显。如果显存不够,优先保证分辨率,batch 降到 4 或 2 也能跑。
4.4 类别不平衡导致黄灯几乎检不到
现象:红灯绿灯检测正常,黄灯几乎全部漏检。
原因:黄灯样本数量远少于红灯绿灯,且黄灯持续时间短,标注时容易漏标。损失函数被多数类主导,少数类梯度被淹没。
解决:过采样黄灯样本,或者在 loss 里给黄灯更高权重。YOLOv8 支持通过cls_pw参数调整类别权重,但更直接的办法是在数据集层面做平衡。我一般会把黄灯样本复制 2~3 份加入训练集,同时确保验证集里黄灯样本足够评估。
4.5 格式转换后标注框整体偏移
现象:用转换脚本把 VOC 转 YOLO 后,可视化发现框整体偏移或缩放不对。
原因:VOC 的xmin, ymin, xmax, ymax转 YOLO 的cx, cy, w, h时,忘记除以图像宽高,或者宽高读错了(比如把 height 和 width 搞反)。另一个常见错误是坐标原点理解不一致,VOC 是左上角原点,YOLO 也是左上角原点,但有些脚本会误用中心点原点。
解决:转换后随机抽 20 张图做可视化,把 YOLO 标签画回原图,肉眼确认框是否对齐。这一步不能省,我见过太多人转换完直接训练,跑了半天发现框全是偏的。
import cv2 def visualize_yolo_label(img_path, label_path, save_path): img = cv2.imread(img_path) h, w = img.shape[:2] with open(label_path, 'r') as f: for line in f: parts = line.strip().split() if len(parts) < 5: continue cls, cx, cy, bw, bh = map(float, parts[:5]) x1 = int((cx - bw / 2) * w) y1 = int((cy - bh / 2) * h) x2 = int((cx + bw / 2) * w) y2 = int((cy + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(int(cls)), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(save_path, img) visualize_yolo_label('./images/000001.jpg', './labels_yolo/000001.txt', './vis_000001.jpg')这段代码把 YOLO 归一化坐标还原成像素坐标并画框,保存可视化结果。抽几张不同场景的图跑一遍,确认框的位置和类别都对。
5. 进阶技巧:用验证集反推数据质量与模型边界
训练跑通之后,真正拉开差距的是对验证结果的细粒度分析。我习惯在训练结束后做三件事:按类别看 AP、按目标尺寸看召回、按场景看误检。
按类别看 AP 直接读 YOLO 输出的results.csv或val日志,找到每个类别的AP50和AP50-95。如果红灯 AP 0.95、绿灯 AP 0.93、黄灯 AP 0.45,那问题就很明确——黄灯数据不够或标注质量差。这时候补黄灯样本比调模型结构有效得多。
按目标尺寸看召回需要自己写脚本,把验证集里的标注框按面积分成小、中、大三档,分别统计每档的召回率。红绿灯检测里,小目标通常是远处路口的灯,如果小目标召回明显低于中大型目标,说明输入分辨率不够或者 P3 检测头容量不足。常见做法是把imgsz从 640 提到 1024 或 1280,或者换用带 P2 的模型。
按场景看误检更依赖人工,从验证集里挑出误检样本,按场景归类:夜间过曝、逆光、雨雾、遮挡、多灯并列。如果误检集中在夜间,检查训练集里夜间样本比例;如果集中在多灯并列,检查标注时是否把多个灯标成了一个框。这些分析做完,下一轮迭代的方向就清楚了,比盲目调参高效得多。
还有一个容易被忽略的点:验证集本身的质量。我遇到过验证集里标注框偏移的情况,导致模型明明检对了却被判为误检,mAP 被拉低。从那以后我每次拿到新数据集,都强制走一遍可视化验证——随机抽 50 张验证集图片,把标注框画出来,逐张确认。这个习惯帮我省了至少两次“模型没问题但指标上不去”的排查时间。
希望帮到你。
本文还有配套的精品资源,点击获取