ARTICLE DETAIL

资讯详情

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

夜间交通目标检测实战:9000张YOLO标注数据训练与避坑指南

夜间交通目标检测实战:9000张YOLO标注数据训练与避坑指南 简介这份夜间交通场景目标检测数据集面向计算机视觉学习者与YOLO实战开发者聚焦夜间低照度环境下车辆、行人与动物的识别难题可用于训练、验证及改进目标检测网络。数据共约9000张图像并附带标注类别包含bicycle、car、dog、person四类已按7:3划分训练集与验证集标注采用YOLO格式可直接接入主流检测框架。压缩包为7z格式共2000个文件其中1999个txt标注文件与1个py脚本整体约371.05MB脚本可用于标注可视化与数据检查便于快速核对标签质量。已有34人学习下载。对于希望复现夜间检测实验、验证模型泛化能力或开展YOLOv5改进实践的读者这份数据能省去采集与标注成本直接投入训练与调参并配合作者主页的改进项目形成完整学习路径。1. 夜间交通目标检测9000 张已标注数据能解决什么实际问题白天跑得挺欢的 YOLO 模型一到晚上就集体“失明”——这是做过交通场景目标检测的人几乎都踩过的坑。路灯照度不均、车灯眩光、尾灯拖影、行人穿深色衣服贴着阴影走这些因素叠加起来让夜间图像的对比度和纹理信息急剧退化。你拿 COCO 或者白天交通数据集训出来的权重直接推理夜间画面mAP 掉十几个点都算客气的。而这次要聊的就是一份约 9000 张、已完成 YOLO 格式标注的夜间交通车辆与行人图像目标检测数据集它到底能干什么、怎么用、值不值得投入。这份数据的核心价值在于场景聚焦只做夜间只标车辆和行人标注格式直接对齐 YOLO 训练管线。它适合三类人一是做智慧交通、园区安防、自动驾驶感知模块的算法工程师需要快速验证夜间检测方案二是带学生做课题的高校老师或研究生想要一份拿来就能训的数据三是刚入门目标检测、想跑通“数据到模型”全流程的新手。9000 张的体量不算大但胜在场景一致、标注规范做微调和验证足够用。2. 夜间数据集的标注格式与训练前处理从 txt 到 DataLoader2.1 YOLO 标注格式到底长什么样YOLO 格式的标注文件是每张图对应一个同名.txt每行代表一个目标格式为class_id x_center y_center width height其中坐标全部是归一化到 0~1 的相对值x_center和y_center是边界框中心点相对于图像宽高的比例width和height是框的宽高比例。类别 ID 从 0 开始这份数据里通常是0代表行人、1代表车辆具体以数据附带的classes.txt或data.yaml为准拿到手第一件事就是确认这个映射关系。这种格式的好处是解析极快训练时不需要像 COCO JSON 那样做复杂的嵌套解析。但要注意归一化坐标在图像增强比如 Mosaic、随机裁剪之后必须重新计算否则框会漂移。这也是很多人自己写增强脚本时翻车的地方。2.2 目录结构组织与 data.yaml 配置拿到数据后建议按以下结构组织这是 Ultralytics YOLOv8/v11 系列最省心的方式night_traffic/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml的内容path: /home/user/night_traffic train: images/train val: images/val test: images/test nc: 2 names: 0: pedestrian 1: vehicle这里nc是类别数names的键值必须和标注文件里的class_id严格对应。我一般会先写一个校验脚本扫一遍所有 label 文件确认没有越界的 class_id、没有坐标超出 [0,1] 的异常行。2.3 用 Python 做标注完整性校验在正式开训之前花十分钟做一次数据体检能省掉后面几小时的无效训练。下面这个脚本检查四件事图片和标签是否一一对应、标签是否为空、坐标是否越界、类别 ID 是否合法。import os from pathlib import Path from PIL import Image IMG_DIR Path(night_traffic/images/train) LBL_DIR Path(night_traffic/labels/train) VALID_CLASS_IDS {0, 1} # 与 data.yaml 保持一致 problems [] for img_path in IMG_DIR.glob(*.jpg): lbl_path LBL_DIR / (img_path.stem .txt) # 1. 标签文件是否存在 if not lbl_path.exists(): problems.append(f缺失标签: {img_path.name}) continue # 2. 标签是否为空 lines lbl_path.read_text().strip().splitlines() if len(lines) 0: problems.append(f空标签: {lbl_path.name}) continue # 3. 逐行检查坐标和类别 for i, line in enumerate(lines): parts line.strip().split() if len(parts) ! 5: problems.append(f格式错误 {lbl_path.name} 第{i1}行: {line}) continue cls_id int(parts[0]) coords [float(x) for x in parts[1:]] if cls_id not in VALID_CLASS_IDS: problems.append(f非法类别 {lbl_path.name} 第{i1}行: class_id{cls_id}) if any(c 0 or c 1 for c in coords): problems.append(f坐标越界 {lbl_path.name} 第{i1}行: {coords}) print(f共发现 {len(problems)} 个问题) for p in problems[:20]: print(p)逻辑说明脚本遍历训练集图片目录对每张图找同名 txt。空标签在夜间数据里很常见——有些图确实没有行人或车辆但如果空标签比例超过 15%说明数据筛选有问题。坐标越界通常来自标注工具导出时的精度问题轻微越界比如 1.0001可以 clip 修复大幅越界必须回查原图。参数说明VALID_CLASS_IDS必须和data.yaml里的names键一致。如果你的数据里还有别的类别比如自行车、三轮车需要同步修改。IMG_DIR和LBL_DIR按实际路径替换。2.4 夜间图像增强别把关键信息增没了夜间图像的增强策略和白天有本质区别。白天常用的随机亮度、对比度抖动在夜间可能把仅有的行人轮廓彻底抹掉。我一般用这套组合import albumentations as A train_transform A.Compose([ A.RandomBrightnessContrast( brightness_limit0.15, # 夜间亮度变化幅度要小 contrast_limit0.15, p0.5 ), A.HueSaturationValue( hue_shift_limit5, sat_shift_limit10, val_shift_limit10, p0.3 ), A.GaussNoise(var_limit(5.0, 25.0), p0.3), # 模拟夜间传感器噪声 A.MotionBlur(blur_limit5, p0.2), # 模拟车辆运动模糊 A.Resize(640, 640), ], bbox_paramsA.BboxParams(formatyolo, label_fields[class_labels]))逻辑说明亮度对比度抖动幅度控制在 0.15 以内避免把暗部细节压死。高斯噪声模拟高 ISO 下的传感器噪点运动模糊模拟夜间低快门速度下的拖影。这两个增强对夜间场景的泛化帮助很大。参数说明var_limit控制噪声方差范围太大超过 50会让图像不可读。blur_limit5对应约 5 像素的模糊核再大行人就糊成一团了。所有增强的p值都不宜过高夜间数据本身分布就窄过度增强反而引入噪声。3. 用 YOLOv8 在夜间数据上跑通训练参数、显存与收敛判断3.1 环境配置与预训练权重选择环境这块Python 3.9、PyTorch 2.0、CUDA 11.8 或 12.1 是当前比较稳的组合。Ultralytics 的安装就一行pip install ultralytics预训练权重我建议从 YOLOv8m 或 YOLOv8l 起步。n 和 s 版本在夜间小目标上召回率偏低x 版本对 9000 张的体量来说容易过拟合。m 版本在精度和速度之间平衡得比较好单卡 8G 显存就能跑起来。注意不要用 COCO 预训练权重直接推理夜间数据然后觉得“效果还行”就跳过微调。COCO 里夜间交通场景占比极低直接推理的漏检率在暗光行人上非常明显。3.2 训练命令与关键参数解读yolo detect train \ datanight_traffic/data.yaml \ modelyolov8m.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.001 \ lrf0.01 \ warmup_epochs3 \ patience20 \ mosaic0.8 \ mixup0.1 \ copy_paste0.1 \ device0 \ projectruns/night_traffic \ nameexp_v1逐个说关键参数。lr00.001是初始学习率微调场景下比默认的 0.01 更稳夜间数据量不大学习率太高容易在早期就把预训练特征破坏掉。lrf0.01是最终学习率因子配合余弦退火策略训练末期学习率降到 1e-5 量级。warmup_epochs3让模型在前 3 个 epoch 慢慢把学习率从极低值升上来避免一开始就大梯度冲击。mosaic0.8表示 80% 的概率做 Mosaic 增强这个值在夜间数据上可以适当降低到 0.5~0.6因为 Mosaic 拼接后的图像亮度分布会更混乱。mixup0.1和copy_paste0.1都是轻度使用主要为了防止过拟合。patience20是早停耐心值20 个 epoch 验证集指标不提升就停。9000 张数据在 m 模型上通常 60~80 个 epoch 就收敛了100 个 epoch 是留余量。3.3 显存不够时的降级方案8G 显存跑batch16和imgsz640的 YOLOv8m如果同时开了 AMP 混合精度一般能稳住。如果 OOM按这个顺序降调整项从到影响batch168训练速度变慢收敛略慢imgsz640512小目标召回下降modelyolov8myolov8s精度下降约 2~3 个点accumulate12等效 batch 不变速度略慢优先降 batch其次考虑梯度累积。imgsz 尽量别动夜间行人本身像素就少降到 512 后 20 像素以下的行人基本检不出来。3.4 看什么指标判断收敛和过拟合训练日志里重点盯三个box_loss、cls_loss、mAP50-95。正常情况下 box_loss 在前 10 个 epoch 快速下降之后缓慢收敛。如果 box_loss 震荡剧烈检查学习率是否偏高或 batch 是否太小。验证集的 mAP50 在 40~60 个 epoch 达到峰值后趋于平稳。如果训练集 mAP 还在涨但验证集 mAP 开始掉就是过拟合信号这时候早停该触发就触发别硬训。夜间数据尤其容易过拟合因为场景重复度高——同一条路不同时间拍的图模型很容易记住背景而不是学到目标特征。4. 夜间检测的避坑与排查那些让模型“失明”的细节4.1 坑一标注框在暗部区域偏移现象训练 loss 正常下降但验证时暗部行人的框总是偏上或偏左。原因标注人员在暗光图像上肉眼定位边界时对暗部边缘的判断存在系统性偏差。夜间图像中行人脚部和地面阴影融为一体标注框底部容易画高。解决抽 50~100 张暗部场景图用脚本把标注框叠加回原图可视化人工复核。如果发现系统性偏移要么重新标要么在训练时用A.ShiftScaleRotate做小幅位移增强来提升鲁棒性。4.2 坑二车灯眩光区域被误检为车辆现象模型在远光灯、路灯直射区域产生大量误检框。原因眩光区域的高亮斑块在浅层特征上和车辆尾部/前脸有相似响应。夜间数据里这类样本如果标注时没被排除模型会学到错误关联。解决检查标注文件确认眩光区域没有被标成车辆。如果已标注需要清洗。训练时适当增加RandomBrightnessContrast的上限让模型见过更多高光变化。推理时可以把置信度阈值从 0.25 提到 0.4 来过滤一部分误检。4.3 坑三验证集 mAP 虚高但实际推理漏检严重现象验证集 mAP50 到 0.85但拿视频跑推理行人漏检肉眼可见。原因验证集和训练集来自同一批数据分布如果划分时没有按场景/时间段分层验证集可能恰好都是光照较好的样本。另外mAP 是平均指标小目标的低 AP 被大目标的高 AP 拉平了。解决划分验证集时按光照条件分层采样——把数据按平均亮度分三档每档按比例抽验证集。另外单独统计小目标面积 32×32 像素的 AP这个指标比整体 mAP 更能反映夜间行人检测的真实水平。4.4 坑四训练中途 loss 突然变 NaN现象训练到某个 epochloss 突然变成 NaN之后所有输出都是 NaN。原因夜间图像里如果有极端暗部或过曝区域经过某些增强后可能产生极端像素值导致梯度爆炸。另外如果标注文件里有坐标恰好为 0 或 1 的框计算 IoU 时可能出现除零。解决在数据校验脚本里加一条坐标严格在 (0, 1) 开区间内等于 0 或 1 的 clip 到 0.001 和 0.999。训练时开启ampTrue默认开启混合精度对梯度爆炸有一定抑制作用。如果已经 NaN降低学习率到 0.0005 重跑。4.5 坑五类别不平衡导致行人检测效果差现象车辆 AP 0.9行人 AP 0.5差距悬殊。原因夜间交通场景里车辆数量天然多于行人9000 张数据里行人样本可能只占 20%~30%。YOLO 的损失函数对类别不平衡没有内置的强补偿机制。解决在data.yaml里给行人类别加权重或者用copy_paste增强专门复制粘贴行人实例。另一个办法是过采样含行人的图像但要注意别过拟合到那几张图上。我一般会把copy_paste0.3专门针对行人小类做增强。5. 从 9000 张到可部署模型验证策略与一个提点技巧5.1 分层验证集划分脚本前面提到验证集要按光照分层这里给一个可执行的划分脚本import numpy as np from pathlib import Path from PIL import Image import shutil IMG_DIR Path(night_traffic/images/all) LBL_DIR Path(night_traffic/labels/all) OUT_DIR Path(night_traffic) # 计算每张图的平均亮度 records [] for img_path in sorted(IMG_DIR.glob(*.jpg)): img np.array(Image.open(img_path).convert(L)) mean_brightness img.mean() records.append((img_path, mean_brightness)) # 按亮度分三档 brightness_values [r[1] for r in records] low_thresh np.percentile(brightness_values, 33) high_thresh np.percentile(brightness_values, 66) buckets {dark: [], mid: [], bright: []} for img_path, b in records: if b low_thresh: buckets[dark].append(img_path) elif b high_thresh: buckets[mid].append(img_path) else: buckets[bright].append(img_path) # 每档按 8:1:1 划分 train/val/test for bucket_name, paths in buckets.items(): n len(paths) n_val max(1, int(n * 0.1)) n_test max(1, int(n * 0.1)) splits { val: paths[:n_val], test: paths[n_val:n_val n_test], train: paths[n_val n_test:] } for split_name, split_paths in splits.items(): img_out OUT_DIR / images / split_name lbl_out OUT_DIR / labels / split_name img_out.mkdir(parentsTrue, exist_okTrue) lbl_out.mkdir(parentsTrue, exist_okTrue) for p in split_paths: shutil.copy(p, img_out / p.name) lbl LBL_DIR / (p.stem .txt) if lbl.exists(): shutil.copy(lbl, lbl_out / lbl.name) print(分层划分完成) print(f暗光阈值: {low_thresh:.1f}, 亮光阈值: {high_thresh:.1f})逻辑说明用 PIL 读灰度图算平均亮度按 33/66 分位数切三档保证每档都有训练和验证样本。这样验证集里的光照分布和真实场景更接近mAP 才有参考价值。参数说明分位数 33/66 是经验值如果数据里极暗样本特别多可以改成 25/75。n_val和n_test各取 10%9000 张数据对应约 900 张验证、900 张测试足够评估。5.2 推理阶段的置信度与 NMS 调参训练完导出模型后推理时的两个参数对夜间检测影响很大yolo detect predict \ modelruns/night_traffic/exp_v1/weights/best.pt \ sourcetest_night_video.mp4 \ conf0.35 \ iou0.5 \ imgsz640 \ saveTrueconf0.35比默认的 0.25 高目的是过滤夜间眩光产生的低置信度误检。iou0.5控制 NMS 的合并阈值夜间车辆密集时如果 iou 设太高比如 0.7相邻车辆可能被合并成一个框。如果发现漏检多把 conf 降到 0.25 试试如果误检多提到 0.4~0.45。5.3 一个提点技巧用测试时增强TTA换 1~2 个点Ultralytics 支持推理时开启 TTA对同一张图做多尺度翻转推理再融合结果yolo detect predict \ modelbest.pt \ sourcetest_images/ \ augmentTrue \ conf0.3 \ imgsz640augmentTrue就是 TTA 开关。代价是推理速度慢 2~3 倍但夜间小目标的召回率通常能提 1~2 个点。如果部署端算力允许这个技巧值得开。如果追求实时性就别开了老老实实调模型结构。5.4 我踩过的最大的坑说一个血泪教训。最早做夜间检测时我拿到一份标注数据直接开训mAP 跑到 0.88觉得稳了。部署到实际路口跑了一周发现傍晚 6 点到 7 点这个时间段漏检率飙升。回头查数据才发现这份“夜间”数据集里大部分样本是深夜 11 点以后拍的傍晚黄昏时段的光照条件完全不在分布内。模型没学过那个亮度区间自然抓瞎。后来我养成了一个习惯拿到任何场景数据集先画一张亮度分布直方图再画一张目标尺寸分布图。这两张图能告诉你数据覆盖了什么、没覆盖什么。9000 张听起来不少但如果全集中在某几个光照条件下实际泛化能力会大打折扣。数据集的“场景覆盖度”比“数量”重要得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表