
简介Traffic-Net 是一套面向计算机视觉与深度学习入门者的交通场景图像数据集可用于训练交通状态识别、事故检测与火灾预警等模型适合做目标检测、图像分类实验或课程项目。数据集共 4400 张图像划分为事故、交通密集、火灾、稀疏交通四个类别每类 1100 张其中 900 张用于训练、200 张用于测试兼顾训练与评估需求。资源包共 10 个文件以 jpg 图像为主体另含 Python 脚本、json 类别配置、md 说明文档与 license 授权文件压缩包约 707KB体积轻便便于快速下载与本地部署。目前已有 686 人学习下载可作为交通监控与智能安防方向的练手数据。借助配套脚本与类别映射文件读者能直接搭建数据加载与训练流程理解多类别图像分类的完整链路并在此基础上扩展更多交通场景类别。1. 交通、火灾和事故图像数据集为什么它比通用数据集更难训城市安防、高速巡检、保险定损、厂区消防这几个场景里模型要认的不是猫狗而是「车撞了没」「火起了没」「路上堵成什么样」。这类任务有个共同点正样本稀少、类间差异大、背景极度不均衡。你拿 COCO 或 ImageNet 预训练权重直接微调mAP 往往卡在 0.3 上下上不去不是模型不行是数据分布和任务目标对不上。交通、火灾和事故图像数据集就是为这类任务准备的专用集合通常包含车辆检测、碰撞事故、火焰与烟雾、道路异常等类别标注格式以 VOC XML 或 YOLO txt 为主。它解决的核心问题是让检测模型在「事件发生的那几帧」上学到足够判别力而不是被大量正常背景稀释掉梯度。适合谁做智慧交通、消防预警、保险理赔自动化的算法工程师以及想用 YOLOv8、Mask2Former 这类框架跑通自己数据集的学生和独立开发者。下面从拿到数据到跑出第一个可用权重把路径拆开讲。2. 拿到数据集先别急着训清洗、划分与格式转换2.1 先看清目录结构和标注格式下载下来的包常见结构是images/加annotations/标注可能是 Pascal VOC 的 XML也可能是已经转好的 YOLO txt。第一步不是写训练脚本而是统计类别分布和图片尺寸。事故类数据集最典型的坑是「正常道路图占了七成」直接训会让模型偏向背景类。import os from collections import Counter import xml.etree.ElementTree as ET # 统计 VOC 标注里每个类别的框数量 def count_voc_classes(anno_dir): counter Counter() for f in os.listdir(anno_dir): if not f.endswith(.xml): continue tree ET.parse(os.path.join(anno_dir, f)) for obj in tree.getroot().findall(object): name obj.find(name).text.strip() counter[name] 1 return counter print(count_voc_classes(./annotations))这段代码遍历所有 XML把每个object的类别名累加。跑完你会得到一张类别频次表。如果fire只有 200 个框而car有 8000 个后面就必须做重采样或加权。参数上没什么可调的重点是看结果——频次低于总框数 5% 的类单独拎出来处理。2.2 按 8:1:1 划分但别用随机划分随机划分在事故数据集上会翻车同一段视频抽出的连续帧被分到训练和验证集验证指标虚高上线就崩。正确做法是按「事件」或「视频源」划分同一事件的帧只能进一个集合。import random from pathlib import Path # 按视频/事件前缀分组保证同源帧不跨集 def split_by_group(img_dir, out_txt, ratios(0.8, 0.1, 0.1)): files sorted(Path(img_dir).glob(*.jpg)) groups {} for f in files: key f.stem.split(_)[0] # 假设文件名前缀是事件ID groups.setdefault(key, []).append(f) keys list(groups.keys()) random.seed(42) random.shuffle(keys) n len(keys) train_k keys[:int(n * ratios[0])] val_k keys[int(n * ratios[0]):int(n * (ratios[0] ratios[1]))] test_k keys[int(n * (ratios[0] ratios[1])):] with open(out_txt, w) as fp: for k in train_k: for f in groups[k]: fp.write(f{f}\n) return len(train_k), len(val_k), len(test_k) print(split_by_group(./images, train.txt))seed42保证可复现split(_)[0]是分组键你的命名规则不同就改这里。输出是三个 txt 文件里面是图片绝对或相对路径YOLO 训练直接吃这种列表。2.3 VOC 转 YOLO四个边界坑VOC 的坐标是xmin, ymin, xmax, ymax绝对值YOLO 要的是归一化的cx, cy, w, h。转换本身简单坑在边界。import xml.etree.ElementTree as ET from PIL import Image def voc_to_yolo(xml_path, img_path, class_map): tree ET.parse(xml_path) root tree.getroot() w int(root.find(size/width).text) h int(root.find(size/height).text) lines [] for obj in root.findall(object): cls obj.find(name).text.strip() if cls not in class_map: continue b obj.find(bndbox) xmin max(0, float(b.find(xmin).text)) ymin max(0, float(b.find(ymin).text)) xmax min(w, float(b.find(xmax).text)) ymax min(h, float(b.find(ymax).text)) if xmax xmin or ymax ymin: continue # 丢弃退化框 cx (xmin xmax) / 2 / w cy (ymin ymax) / 2 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{class_map[cls]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) return lines四个坑分别是坐标越界用max/min夹住、退化框宽高为负或零直接丢、类别不在映射表跳过而不是报错、浮点精度保留 6 位足够。class_map是你自己定义的{car:0, fire:1, ...}顺序一旦定了就不能改否则权重全废。提示转换完随机抽 20 张用可视化脚本画框核对一遍比看日志靠谱得多。3. 用 YOLOv8 跑通第一个基线配置、命令与参数3.1 环境与数据 yamlYOLOv8 对环境要求不苛刻Python 3.8 以上、装好ultralytics即可。数据配置文件是训练的入口。# traffic_fire.yaml path: /data/traffic_fire train: train.txt val: val.txt test: test.txt nc: 4 names: [car, accident, fire, smoke]path是根目录train/val/test是相对路径的 txt。nc必须和names长度一致写错会直接报维度错误。类别顺序和转换时的class_map必须完全对应。3.2 训练命令与关键参数yolo detect train \ datatraffic_fire.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ device0modelyolov8s.pt用官方预训练权重小数据集别上 l 或 x容易过拟合。imgsz640是速度和精度的平衡点事故目标偏小可以试 960但显存翻倍。patience20是早停验证指标 20 轮不升就停省时间。lr00.01是初始学习率微调任务可以降到 0.001。3.3 训练过程看什么终端会打印每轮的box_loss、cls_loss、mAP50。重点看三条cls_loss是否持续下降不降说明类别太难分或标注有错、mAP50是否在 30 轮后还在涨早停别设太小、验证 loss 是否开始反弹过拟合信号。如果fire类的 AP 一直是 0回去查标注里有没有这个类或者框是不是画得太小。from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.val(datatraffic_fire.yaml, splittest) for i, name in enumerate(results.names): print(name, results.box.ap50[i])这段加载最优权重在测试集上验证逐类打印 AP50。哪个类拖后腿一目了然比只看总体 mAP 有用。4. 类别不均衡与误报三个能落地的调优手段4.1 过采样与 copy-paste 增强火灾和事故样本少最直接的办法是把这些类的图片在训练列表里重复列几次。更讲究的做法是 copy-paste把火焰区域抠出来贴到正常道路图上扩充正样本。import cv2 import numpy as np import random def paste_aug(bg_path, obj_path, obj_box, out_path): bg cv2.imread(bg_path) obj cv2.imread(obj_path) x1, y1, x2, y2 obj_box patch obj[y1:y2, x1:x2] bh, bw bg.shape[:2] ph, pw patch.shape[:2] if ph bh or pw bw: return x random.randint(0, bw - pw) y random.randint(0, bh - ph) bg[y:yph, x:xpw] patch cv2.imwrite(out_path, bg) return (x, y, xpw, yph) # 返回新框坐标记得同步写标注obj_box是目标在原图中的框贴完必须把新坐标写进标注文件否则框和内容对不上模型学到的就是噪声。贴的位置别压到已有目标简单做法是记录已占用区域做碰撞检测。4.2 损失加权与 focal lossYOLOv8 默认的分类损失对类别不均衡不敏感。样本极不均衡时可以在配置里调cls损失的增益或者换用带 focal 思想的实现。实操上更稳的是先过采样再观察cls_loss曲线不要一上来就改损失函数容易引入新问题。4.3 降低误报置信度阈值和 NMS 的配合火灾预警最怕误报一张晚霞图被判成火灾就够喝一壶。推理时conf阈值从默认 0.25 提到 0.5iou从 0.7 降到 0.5能压掉一批重叠误检。results model.predict(sourcetest_imgs, conf0.5, iou0.5, saveTrue)conf提高会漏检iou降低会合并更多框。这两个参数要在验证集上扫一遍画 PR 曲线找平衡点别拍脑袋定。烟雾类目标边界模糊iou可以单独放宽到 0.6。5. 避坑与排查训练事故数据集时最常翻的五个车现象训练 loss 正常下降但验证 mAP 始终接近 0。原因训练集和验证集的类别映射不一致或者验证 txt 里的路径写错导致加载了空图。 解决打印验证集加载后的类别直方图确认names顺序和标注一致路径用绝对路径试一次。现象模型把路灯、车灯全判成 fire。原因火灾样本里大量夜间火焰模型学到了「亮斑火」的捷径特征。 解决补充白天火焰样本或在增强里加入亮度扰动让模型不能只靠亮度判别。现象同一段视频的相邻帧分别进了训练和验证集指标虚高。原因随机划分没有按事件分组。 解决回到 2.2 的分组划分用事件 ID 做 key重新生成三个 txt。现象训练到一半显存爆了。原因batch设太大或者imgsz提到 960 后没降 batch。 解决batch减半或用batch-1让 ultralytics 自动选同时开ampTrue混合精度。现象转换后的标注框整体偏移。原因VOC 的size里宽高和实际图片尺寸不符常见于图片被裁剪过但 XML 没更新。 解决转换时用PIL重新读图片真实宽高而不是信 XML 里的size。注意每次改完数据划分或类别映射先跑 5 个 epoch 看 loss 有没有正常下降再开长训练。省下的时间比省下的电费值钱。6. 从基线到可用验证策略与一个提点技巧基线跑通只是起点。判断一个交通火灾事故模型能不能用不能只看 mAP要看业务指标。火灾预警关心的是「在可接受误报率下的召回」事故检测关心的是「小目标召回」。我一般会单独切一个「困难集」夜间、雨雾、远距离小目标、遮挡严重的事故帧专门评估。验证时用下面的脚本按尺寸分桶统计召回能看出模型是不是只对大目标有效。from ultralytics import YOLO import cv2 model YOLO(best.pt) buckets {small: [0, 0], medium: [0, 0], large: [0, 0]} def size_bucket(w, h): area w * h if area 32**2: return small if area 96**2: return medium return large # 遍历验证集按 GT 框尺寸统计命中情况 # 命中判定预测框与 GT 的 IoU 0.5 且类别一致分桶后如果 small 的召回只有 0.2说明imgsz不够或 anchor 不匹配可以试切片推理SAHI 思路或提高输入分辨率。另一个提点技巧是测试时增强TTA推理时对图片做翻转、多尺度把结果融合通常能涨 1 到 3 个点代价是推理变慢。results model.predict(sourcetest_imgs, augmentTrue, conf0.4)augmentTrue就是开 TTA。上线前权衡延迟实时预警场景慎用离线定损场景可以开。最后说个我自己的习惯每训完一版把误检和漏检的图各存 50 张到一个badcase/目录按日期归档。下次改数据或调参前先翻这个目录比看任何指标都直观。事故和火灾这类任务数据质量的天花板远低于模型结构的天花板把标注和划分做扎实比换更大的骨干网络管用得多。希望帮到你。本文还有配套的精品资源点击获取