YOLOv8实战:交通信号灯COCO数据集转换与训练全攻略

发布时间:2026/9/11 22:17:32
YOLOv8实战:交通信号灯COCO数据集转换与训练全攻略 简介一套面向红绿黄三色交通信号灯检测的图像数据集适合计算机视觉学习者、科研人员及智能驾驶算法开发者使用也可作为高校人工智能课程的实践数据。资源包共2000个文件主体为1995张信号灯图片并提供3个COCO格式的JSON标注文件包含目标框坐标、类别ID等标注信息以及2个TXT文件可用于记录类别名称或划分训练/验证集合压缩包整体大小约223.94MB。目前已有593人浏览学习可支撑交通信号灯识别、车路协同和辅助驾驶等方向的模型训练与算法验证。使用该数据集能省去自行采集和标注的流程结合COCO工具链即可快速完成数据预处理、模型训练与效果评估对搭建目标检测基线或验证新算法均有实用价值是一份兼顾规范性与实用性的视觉数据集。1. 交通信号灯数据集的定位小目标、强光照干扰与类别边界问题交通信号灯检测是自动驾驶与辅助驾驶系统里一个容易被低估、却直接关系到安全决策的视觉任务。和行人、车辆检测不同信号灯目标极小在 1080P 图像里通常只占几十个像素夜间存在强光晕、过曝和反光白天又面临逆光、背光和镜头眩光。更棘手的是红绿黄三类信号灯之间的视觉区分高度依赖颜色与上下文单一灰度信息根本无法工作。因此这个带 COCO 格式标注的交通信号灯数据集价值不在于能跑通 YOLO而在于它给出了一个干净的起点类别定义统一红绿黄三色、目标边界明确、标注结构直接对齐主流训练框架。拿到 zip 包后真正要做的事包括核对标注合法性、将 COCO JSON 转成训练所需格式、按信号灯的小目标特性调整预处理策略以及最终在验证集上定量检验检测精度。这篇文章就按这条路径展开。2. COCO 格式标注解析从 zip 压缩包到可训练的数据目录2.1 解压后的数据文件组织与合法性检查一个规范的 COCO 格式数据集压缩包解压之后通常呈现为以下典型目录结构traffic_light_dataset/ ├── annotations/ │ ├── instances_train.json │ └── instances_val.json ├── train/ │ ├── 000001.jpg │ └── ... └── val/ ├── 000101.jpg └── ...拿到手第一步先不要急着写训练脚本而是做三件基础检查图片文件能否完整解码、标注 JSON 能否被 json.load 正常解析、图片文件名与 JSON 中的 file_name 字段是否一一对应。zip 包在传输或解压过程中出现文件缺失是常见情况尤其当压缩包由某些网盘工具生成时偶发截断难以避免。我一般会先执行一次全量校验而不是等到训练中途才报 IO 错误。# 校验 zip 包完整性与解压 unzip -t traffic_light_dataset.zip unzip traffic_light_dataset.zip -d ./datasetunzip -t会逐文件测试压缩包完整性若输出包含 No errors detected 说明解压前提成立。实际使用中还要注意路径嵌套问题有些压缩包把内容放在顶层同名文件夹里有些则直接放在根目录这会影响后续路径前缀的拼接建议先tree -L 2确认再决定用绝对路径还是相对路径。2.2 COCO JSON 核心字段解读与类别注册COCO 格式的标注文件是一个 JSON 对象顶层包含四类信息images记录每张图的 id、宽高和文件名annotations记录每个目标框的类别、多边形分割和对应图片 idcategories声明当前数据集里所有类别licenses与info是附加元数据与训练无关。写一个解析脚本来统计交通信号灯数据集的类别分布是理解这份数据集到底标了什么的最快方式。import json from collections import Counter with open(dataset/annotations/instances_train.json, r, encodingutf-8) as f: coco json.load(f) cat_id_to_name {cat[id]: cat[name] for cat in coco[categories]} print(Categories:, cat_id_to_name) ann_per_cat Counter() img_id_to_name {img[id]: img[file_name] for img in coco[images]} for ann in coco[annotations]: ann_per_cat[ann[category_id]] 1 for cat_id, cnt in ann_per_cat.most_common(): print(f{cat_id_to_name[cat_id]}: {cnt} instances)这里用collections.Counter快速统计各颜色类别下的目标实例总数。如果打印出的结果是三个类别各数千个实例说明样本体量基本可行如果某个类别只有个位数训练时就会出现严重的类别不平衡。另一个值得检查的点是category_id是否与 COCO 官方预训练权重里的类别 id 冲突——如果这份信号灯数据集沿用了 COCO 的原始 id比如 1 对应 person迁移学习时就要做类别映射或直接替换模型头部。2.3 将 COCO 标注转换为 YOLO txt 格式YOLO 系列训练框架原生不支持 COCO 的标注文件需要转换为每张图片一个同名 txt 的格式每一行是class_id x_center y_center width height四个数值都是相对图片宽高的比例取值 0 到 1。转换时有两个容易出错的地方一是 COCO 的 bbox 是[x, y, width, height]绝对像素值而 YOLO 的输入是归一化的中心点格式二是必须裁剪掉越界框尤其是位于图像边缘的目标可能在标注时超出了边界。import json import os def convert_coco_to_yolo(json_path, img_dir, out_dir): with open(json_path, r, encodingutf-8) as f: coco json.load(f) img_id_to_info {img[id]: img for img in coco[images]} os.makedirs(out_dir, exist_okTrue) for img in coco[images]: txt_path os.path.join(out_dir, img[file_name].rsplit(., 1)[0] .txt) with open(txt_path, w) as f: for ann in coco[annotations]: if ann[image_id] ! img[id]: continue x, y, w, h ann[bbox] x_center (x w / 2) / img[width] y_center (y h / 2) / img[height] # 归一化到 0~1防止越界 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) w min(w / img[width], 1.0) h min(h / img[height], 1.0) f.write(f{ann[category_id]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n)转换逻辑并不复杂关键在于归一化计算时优先取中心点而不是左上角因为裁切越界时中心点语义仍然有效而左上角坐标会被负值干扰。另外一个实用建议是保留一份原始 COCO JSON 做数据审计不要在转换过程中原地删除YOLO txt 格式丢失了多边形标注信息一旦后续想要做语义分割或旋转框检测就需要重新解压原始文件。3. 用 YOLOv8 训练红绿黄三色交通信号灯检测模型3.1 数据集 YAML 配置与最小的训练命令YOLOv8 使用 YAML 文件描述数据集的路径与类别映射。针对这份交通信号灯数据集我需要先确认 COCO JSON 中抽取出的category_id是否连续。若标注文件里的 id 是 1、2、3分别对应 red、green、yellow则直接沿用若 id 不连续或存在 0 号类别需要在 YAML 中做一次顺排映射否则训练器会因为类别索引与标签数值不符而报错或导致错标。# traffic_light.yaml path: ./dataset train: train val: val nc: 3 names: 0: red 1: green 2: yellow注意这里的names是从 0 开始编号的。如果 COCO JSON 里 red 的 id 是 1转换脚本应统一做减一处理将 category_id 映射到 0/1/2。这一步看似微小但漏掉的话模型训练过程不会直接崩溃而是 silently 把所有类别学错一个偏移量最终推断结果整体错位。训练命令本身并不复杂yolo detect train \ datatraffic_light.yaml \ modelyolov8s.pt \ imgsz640 \ epochs100 \ batch16 \ device0 \ projecttraffic_light_run使用yolov8s.pt做预训练权重是最稳妥的选择s 模型参数量适中不会像 tiny 那样欠拟合小目标也不会像 large 那样在小型数据集上快速过拟合。imgsz640是一个需要结合信号灯目标尺寸反复斟酌的参数——见 3.2 节的说明。3.2 针对信号灯小目标的训练参数调整交通信号灯是典型的小目标检测场景训练时不能机械照搬 COCO 通用检测的默认参数配置。以下是几个我实际使用时重点调整的训练超参参数默认值建议值理由imgsz640800 或 960信号灯在 1080P 原图中像素占比小增大输入分辨率能保留更多细节mosaic1.00.5Mosaic 增强会把小目标进一步缩小0.5 可降低小目标被截断或缩没的概率hsv_h0.0150.0色相抖动会改变红绿颜色值对颜色敏感的类别设置 0 或极小值close_mosaic1015最后 15 个 epoch 关闭 mosaic让模型在接近真实分布的数据上收敛patience10050信号灯数据集通常较小早停设置太大会浪费时间关于hsv_h参数值得展开。红绿黄三类的核心判别维度是色调 hue训练时默认的 HSV 色相增强会在 ±1.5% 范围内随机改变颜色这原本是为了提升颜色鲁棒性——但交通信号灯的语义完全建立在精确颜色上。若把hsv_h设为 0.015图像里的红色灯可能被轻微推向黄色或紫色干扰类别边界。我一般会直接在配置文件里将hsv_h: 0.0关闭仅保留饱和度与明度的小幅增强来模拟不同天气条件下的颜色退化。此外imgsz从 640 调到 960 会显著增加显存消耗。如果 GPU 显存不足以支撑大批量训练把 batch 从 16 降到 8比降低分辨率更好——小目标检测时分辨率是第一优先级。3.3 类别不平衡与背景误检的处理策略信号灯数据集的类别分布天然不均衡红灯和黄灯的出现频率远高于绿灯在交叉路口场景中的频率而有些数据集中还混入了大量的No Traffic Light负样本图像。训练时若不处理不平衡模型会倾向于把模糊目标预测为多数类。处理这个问题的常用做法分为数据层面和损失层面。数据层面在按 3.2 节调整增强策略后对样本较少的黄色类别做额外的复制增强——注意这里不是简单地重复图片而是叠加轻微的尺度变换和亮度扰动让同一样本参与多次训练但每次形态略有不同。损失层面SGD 无法直接控制类别权重改用 YOLOv8 的cls损失加权不可用时最简单的做法是调整置信度阈值训练完成后在验证集上统计各类别 P/R 曲线若绿色类 P 偏低但 R 偏高可在推理阶段对绿色类单独提高置信度阈值。这种方法实现上只需要在predict时对类别 1 应用自定义阈值其他类别维持默认值即可。4. 标注质量审计格式合法性与边界场景排查4.1 标注框越界、空标注与文件缺失的三重校验COCO 格式数据集的标注质量直接决定模型上限。训练前花半小时做质量审计往往能省下后续若干个 epoch 的试错成本。我每次在新数据集上落地训练任务时都会跑一个三重校验脚本检查 bbox 是否完全落在图像范围内、检查是否存在没有任何标注的负样本图片、检查标注文件名与图片文件名是否完全匹配。import json import os from PIL import Image def audit_coco(json_path, img_dir): with open(json_path, r, encodingutf-8) as f: coco json.load(f) img_id_to_info {img[id]: img for img in coco[images]} anns_by_img {} for ann in coco[annotations]: anns_by_img.setdefault(ann[image_id], []).append(ann) issues [] for img_id, img_info in img_id_to_info.items(): file_path os.path.join(img_dir, img_info[file_name]) if not os.path.exists(file_path): issues.append(fmissing file: {img_info[file_name]}) continue with Image.open(file_path) as im: real_w, real_h im.size if real_w ! img_info[width] or real_h ! img_info[height]: issues.append(fsize mismatch: {img_info[file_name]}) anns anns_by_img.get(img_id, []) for ann in anns: x, y, w, h ann[bbox] if x 0 or y 0 or x w real_w or y h real_h: issues.append(fbbox out of range: {img_info[file_name]} ann {ann[id]}) return issues issues audit_coco(annotations/instances_train.json, train) for issue in issues[:20]: print(issue) print(fTotal issues: {len(issues)})real_w与real_h是用 PIL 实际打开图片获取的尺寸而不是直接信任 JSON 里记录的 width/height——这是为了发现标注时图片被压缩中转导致的元数据不一致。输出结果里 bbox 越界是最需要优先修正的问题因为 YOLO 训练时这类框会以错误位置参与损失计算产生对边界区域的假阳性响应。越界框的修正策略分两种。若越界程度轻微如框的最右边界超出图像几个像素直接裁剪坐标让框完整落在图内即可若越界严重超过框宽度的 20%说明该标注本身不可靠建议直接删除该标注。经验法则是保留越界不超过 5% 的框并裁剪否则丢弃。4.2 箭头灯与圆灯混标的隐患交通信号灯数据集的标注中常出现一个领域特有的问题箭头指示灯如左转箭头、直行箭头被标为普通圆形灯的类别。这两种灯的颜色含义相同但几何结构差异明显若不区分会造成模型对红色这个类别内部特征方差过大。从检测任务的角度看若数据集只要求识别颜色而非灯类型混合标注不会显著损害 mAP但若后续要区分箭头朝向这种混标就是致命缺陷。排查方法不复杂对每个 bbox 区域做连通域特征统计计算目标内部的宽高比是否有显著双峰分布。更快的做法是直接可视化——用 OpenCV 绘制所有 bbox 并输出成网格图人工抽检 200~300 个标注是否符合预期。具体命令python - EOF import json, cv2, os with open(annotations/instances_val.json) as f: coco json.load(f) for img in coco[images][:100]: path os.path.join(val, img[file_name]) im cv2.imread(path) for ann in coco[annotations]: if ann[image_id] ! img[id]: continue x, y, w, h [int(v) for v in ann[bbox]] cv2.rectangle(im, (x, y), (x w, y h), (0, 255, 0), 2) cv2.imwrite(fvis/{img[file_name]}, im) EOF每张图叠加绿色检测框并写入vis目录快速滚动查看即可发现明显异常。这类可视化审计是任何数据预处理环节都不能省略的动作因为数值层面的统计往往无法暴露语义标错。4.3 用类别分布直方图指导数据补充当某个颜色类别比如 yellow的实例数远少于其他类别时模型的召回率会严重偏向多数类。标注审计阶段输出一张直方图可以直观呈现分布差距。类别实例数占比red486254.3%green332037.1%yellow7718.6%当 minority 类别占比低于 10% 时直接的过采样可能无法完全解决问题。此时如果数据集内还包含其他信号灯形态比如倒计时器、横排与竖排灯组优先补充这些形态样本来增加类别内多样性比单纯复制已有图片更有效。另外在 3.2 节中把 mosaic 概率降低也是为了减少黄色小目标在训练中被拼贴操作稀释的概率。5. 验证集上的定量评估与三种实用的漏检排查方法5.1 精确率、召回率与 F1 曲线的读取训练完成后验证集评估会输出一组指标。对于交通信号灯检测精确率 Precision 与召回率 Recall 之外还要特别关注 F1 曲线的整体形状若 F1 峰值出现在置信度阈值 0.3 左右说明模型整体置信度偏低适合在部署时把置信度阈值调低若峰值出现在 0.7 以上说明模型对大多数目标有明确判断可将置信度阈值适当调高来换取更少的误检。from ultralytics import YOLO model YOLO(traffic_light_run/weights/best.pt) metrics model.val(datatraffic_light.yaml, splitval, imgsz960) print(metrics.box.map) # mAP50-95 print(metrics.box.map50) # mAP50 print(metrics.box.p) # Precision print(metrics.box.r) # Recallmap是 COCO 标准下的 mAP50:95对 bbox 定位精度非常敏感交通信号灯这类小目标通常 mAP50-95 偏低但 mAP50 较高。如果 mAP50 超过 0.9 但 mAP50-95 不足 0.5说明预测框虽然大致位置正确但边界贴合度不够优先调整 NMS 的 IoU 阈值或增大输入分辨率比调整损失函数更有效。5.2 混淆矩阵解读红绿互换与黄色漏检信号灯的颜色识别错误通常集中在两类红色误判为黄色、黄色误判为红色。前者多发生于夜间红色灯过曝发橙后者多发生于黄色灯在强阳光下饱和度降低。混淆矩阵输出后如果发现 red 到 yellow 的误判率超过 3%建议从数据层面补充偏色样本而不是直接调低推断置信度。另一个常见问题是: 黄色类别召回率远低于红绿。若测试集的 yellow 实例数不足F1 指标的置信区间很宽单次评估结果不可靠。此时可以用 K 折验证替代单次划分将训练数据随机切成 5 份训练 5 个模型统计黄色类别在每个折上的召回率波动范围。波动幅度超过 15% 说明该类别样本量不够或分布过于集中比如所有黄色灯都来自同一拍摄地点。5.3 序列帧去抖利用信号灯的状态转移特性弥补单帧检测波动单帧检测的稳定性是有限的而交通信号灯是一个具有明确状态转移规律的对象红绿黄三类状态的跳变频率极低不可能在相邻视频帧间出现红色→绿色→红色这样来回翻转。因此部署推理时一个非常有效的技巧是加入时序投票机制。class SignalLightStateFilter: def __init__(self, n5, confidence0.7): self.n n self.confidence confidence self.deque [] def update(self, frame_detections): # frame_detections: {red: 0.9, green: 0.2, yellow: 0.1} self.deque.append(frame_detections) if len(self.deque) self.n: self.deque.pop(0) avg_conf {} for key in [red, green, yellow]: avg_conf[key] sum(d[key] for d in self.deque) / len(self.deque) best max(avg_conf, keyavg_conf.get) if avg_conf[best] self.confidence: return best return NoneSignalLightStateFilter维护最近 5 帧的各类别平均置信度只有平均置信度超过阈值时才会输出当前灯色。这种做法的收益在于单帧的随机误检会被平均操作平滑掉同时因为信号灯状态的持续性5 帧窗口引入的延迟约 0.2 秒假设 25fps不会对下游决策产生实质影响。若视频帧率更高可以适当将窗口扩展到 7 或 9 帧进一步提升稳定性。在自动驾驶或 V2X 场景里此类输出往往还要和一个高置信度clear状态配合使用——即所有类别置信度均低于阈值时输出未知而绝不强行选择一个颜色。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询