简介:针对夜间监控场景中行人检测精度不足、低光数据稀缺的问题,这份配套说明PDF提供了一套完整的夜间行人检测数据集方案,面向计算机视觉与安防监控方向的开发者。资料共1个PDF文件,包体约6.07MB,内附数据集基本情况介绍、标注规范说明、缩略图与标注截图预览,以及百度网盘获取方式。数据集由5000张真实场景图片构成,覆盖夜间街景、道路、遮挡及严重遮挡行人等丰富样本,均采用LabelImg人工精细标注,提供VOC、COCO、YOLO三种通用格式标签,可直接用于YOLO等主流算法训练。同时附赠YOLO11一键训练脚本,支持GPU、CPU、Mac三平台,另有博主完整训练结果日志可供对比参考。目前已有611人学习下载,适合安防监控、自动驾驶等场景下从事夜间行人检测研究的开发者快速上手使用。
1. 夜间行人目标检测数据集:5000张图解决什么,又要怎么用
如果你做过夜间行人目标检测数据集,大概率经历过这种尴尬:白天跑得很好的模型,一到晚上就像瞎了一样漏检。原因是光照不足、对比度低、行人与背景融合,加上夜间车辆灯光干扰。现在拿到一份5000张夜间行人图片的数据集,而且带VOC、COCO、YOLO三种格式标签,配套YOLO11一键训练脚本,支持GPU(GPUs)、CPU、Mac三平台,新手也能直接跑。我按自己实际做训练的顺序,把数据集怎么核对、训练脚本怎么改、三平台怎么适配、以及哪些坑一定要避开,一步步讲清楚。适合想快速验证夜间行人检测方案、不想自己标数据,或者正在做安防、自动驾驶相关项目的人。
2. 数据集构成与三种标签格式:VOC、COCO、YOLO分别怎么选
拿到一份带三种格式标签的数据集,第一件事不是急着训练,而是把格式差异搞清楚。VOC、COCO、YOLO是目标检测标注最常见的三种存储方式,分别对应XML、JSON、TXT三种文件形态,用错一个字段,后面的训练全是白费。
2.1 三格式标签的底层差异:XML、JSON、TXT的使用边界
VOC格式每张图对应一个XML文件,里面用<bndbox>记录物体框的绝对像素坐标,比如<xmin>10</xmin>这样,人类可读,但每张图一个文件,遍历时要拼路径。COCO格式是整个数据集汇总到一个JSON文件里,结构分images、annotations、categories三段,每个标注是一个bbox: [x, y, width, height],同样是绝对像素。YOLO格式最“薄”,每张图对应一个TXT文件,一行一个目标,五个数字分别是class, x_center, y_center, width, height,四个坐标全部归一化到0-1之间。
为什么一个数据集要给三种格式?因为不同工具链吃不同口味。用LabelImg标注顺手,导出就是VOC;转到mmdetection或Detectron2,要COCO JSON;用ultralytics训练YOLO11,直接吃YOLO TXT。把你常用的框架告诉对方,对方给对应格式,迁移成本最低。就算三种格式齐全,我也建议先做标签体检,防止坐标写错、类别名不一致。
下表是三种格式的快速对照:
| 格式 | 存储方式 | 框坐标 | 归一化 | 典型工具 |
|---|---|---|---|---|
| VOC | 每图一个XML | xmin/ymin/xmax/ymax | 绝对像素 | LabelImg, mmdetection |
| COCO | 整个集一个JSON | [x, y, width, height] | 绝对像素 | Detectron2, mmdetection |
| YOLO | 每图一个TXT | class, x_center, y_center, w, h | 0-1 | ultralytics, Darknet |
YOLO格式在训练时最省内存,因为不需要一次性加载一个大JSON文件,每张图读一个小txt,对显存和内存都友好。但它的坐标是归一化的,一旦和原图尺寸对不上,训练时框就会飘。
还有一个容易忽略的点:三种格式的文件命名必须和图片一一对应。VOC的XML要和jpg同名,YOLO的txt也要和jpg同名。如果数据集里顺序乱了,训练时会自动匹配到错误的图,框画在完全不相关的对象上。检查方法很简单:ls images/train | wc -l和ls labels/train | wc -l,数量相等后抽样几个对照文件名,确认后缀匹配。
下面是一个常见的数据集目录结构,5000张图通常按4:1分训练和验证集:
night_pedestrian/ ├── images/ │ ├── train/ # 约4000张 │ └── val/ # 约1000张 ├── voc/ │ ├── Annotations/ # 每张图的XML标签 │ └── JPEGImages/ # 原图(或软链接) ├── coco/ │ ├── annotations/ │ │ ├── train.json │ │ └── val.json │ └── images/ └── yolo/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml拿到的目录不一定长这样,但核心思想不变:原图按split分好,标签按框架分好。如果你的包只给了VOC,想转成YOLO,别靠手算公式,先看一下2.2的体检脚本,确认原图尺寸和标注坐标到底对不对。
2.2 拿到数据集后先做三件事:数图、查类别、验坐标
我习惯在训练前跑一个体检脚本,否则到训练报错再回头找标签,时间全浪费了。先看数量和类别,再验证YOLO坐标是否越界。下面这个脚本可以直接跑:
from pathlib import Path yolo_root = Path('night_pedestrian/yolo') for split in ['train', 'val']: img_dir = yolo_root / 'images' / split lbl_dir = yolo_root / 'labels' / split imgs = list(img_dir.glob('*.jpg')) print(f'[YOLO] {split}: {len(imgs)} imgs') bad = 0 class_ids = set() for img in imgs: txt = lbl_dir / (img.stem + '.txt') if not txt.exists(): print(f' missing label: {img.name}') bad += 1 continue for line in txt.read_text().splitlines(): parts = line.strip().split() if len(parts) != 5: bad += 1 break cls_id = parts[0] x, y, w, h = map(float, parts[1:]) class_ids.add(cls_id) if not (0.0 <= x <= 1.0 and 0.0 <= y <= 1.0 and 0.0 < w <= 1.0 and 0.0 < h <= 1.0): bad += 1 break print(f' classes: {sorted(class_ids)}') print(f' invalid label lines: {bad}')这段代码做了三件事:统计每个分片下的图片数量;检查每个jpg是否都有对应的txt标签;按行解析YOLO标签,验证坐标都在0-1之间。bad变量用来记录缺失标签和越界坐标,一旦有越界,就说明转换时除以了错误的图像宽高。很多翻车就是从这一步开始的:VOC的绝对像素除以了缩放后的图宽,而不是原图宽,结果看起来正常的框其实已经出界。
如果检测出来有问题,或者你想把VOC/COCO转成YOLO,手动换算也不难。设图像宽W高H,VOC的框是xmin, ymin, xmax, ymax,COCO的框是x, y, w, h,换算公式如下:
# VOC -> YOLO x_center = ((xmin + xmax) / 2) / W y_center = ((ymin + ymax) / 2) / H w = (xmax - xmin) / W h = (ymax - ymin) / H # COCO -> YOLO x_center = (x + w / 2) / W y_center = (y + h / 2) / H w_norm = w / W h_norm = h / H注意:COCO的bbox是左上角坐标加宽高,很多新手误以为是中心点坐标,结果画出来的框偏移半个身位。转换后一定要再用上面的体检脚本跑一遍,确认没有越界。
COCO格式也可以用Python快速检查:
import json with open('night_pedestrian/coco/annotations/train.json') as f: coco = json.load(f) print('images:', len(coco['images'])) print('annotations:', len(coco['annotations'])) print('categories:', coco['categories'])这里只统计了数量和类别,你可以再遍历annotations,检查每个bbox长度是否为4,以及是否落在对应图像的宽高范围内。如果一张图有多个标注,还可以顺便看看有没有重复框。
2.3 用YOLO格式训练时,data.yaml怎么写
YOLO11训练的第一步是喂一个数据集描述文件data.yaml,它告诉模型图像和标签在哪儿、类别有几类。常见写法如下:
# night_pedestrian/yolo/data.yaml path: . # 相对于本yaml文件的根目录 train: images/train val: images/val names: 0: personpath是根目录路径,建议直接写成.,因为上面目录结构里data.yaml就放在night_pedestrian/yolo/下,images/train直接相对它。如果你把data.yaml放在别处,就要写相对路径或绝对路径。names里只列了person一个类别,这是因为夜间行人数据集通常只标注行人。如果你的数据集还有骑行者标注,就要多写几类,并且类别顺序必须和txt标签里的第一个数字一致,否则训练会“指鹿为马”。
写好后,可以先用下面的命令扫描标签里实际出现的类别ID,再回头核对names:
cat night_pedestrian/yolo/labels/train/*.txt | awk '{print $1}' | sort -u如果输出0和1,而names里只有0: person,就说明有标签把背景当成了一个类,要么删掉无效行,要么修改yaml。这种问题通常在训练开始前就要解决,拖到训练中报错再返工,心态容易崩。
3. 用YOLO11做一键训练:从数据集目录到训练命令
数据集格式确认无误后,下一步就是写训练脚本。标题里要求“支持GPU(GPUs)/CPU/Mac三平台”,所以脚本不能写死device='0',必须自动检测设备,同时把训练参数暴露成命令行参数,这样不用改代码就能换配置。
3.1 为什么选择YOLO11而不是YOLOv8?以及环境的取舍
YOLO11是ultralytics维护的YOLO系列当前实现,相比YOLOv8在特征提取模块和注意力机制上做了优化,小目标检测表现更好。在夜间行人这种低光、小尺寸、遮挡多的场景,YOLO11的改进更实用。模型规格有n/s/m/l/x几个档次,夜间行人检测我一般用yolo11s.pt起步,m可以冲精度,n图快但容易漏,x在普通显卡上训练时间太长。如果你的显卡显存只有8GB,就选yolo11n或yolo11s,不要碰m以上。
这里要说明一点:ultralytics的YOLO11不是单独安装一个叫yolo11的包,而是通过pip install ultralytics获得。安装之后,用YOLO('yolo11s.pt')会自动下载对应预训练权重,前提是网络能连默认权重仓库。如果网络受限,也可以手动把.pt文件放到项目目录,路径写成./yolo11s.pt。
3.2 一键训练脚本:设备自动检测与参数透传
下面这个Python脚本是训练入口,核心是pick_device函数,它按优先级返回计算设备:手动指定 > CUDA > MPS > CPU。MPS对应Mac的GPU加速,CPU是最后兜底。
import torch import argparse from ultralytics import YOLO def pick_device(force=None): if force: return force if torch.cuda.is_available(): return '0' if hasattr(torch.backends, 'mps') and torch.backends.mps.is_available(): return 'mps' return 'cpu' if __name__ == '__main__': parser = argparse.ArgumentParser(description='YOLO11 train for night pedestrian') parser.add_argument('--weights', default='yolo11s.pt') parser.add_argument('--data', default='night_pedestrian/yolo/data.yaml') parser.add_argument('--imgsz', type=int, default=640) parser.add_argument('--epochs', type=int, default=100) parser.add_argument('--batch', type=int, default=16) parser.add_argument('--device', default=None) args = parser.parse_args() device = pick_device(args.device) print(f'[Device] {device}') model = YOLO(args.weights) model.train( data=args.data, imgsz=args.imgsz, epochs=args.epochs, batch=args.batch, device=device, )pick_device的优先级已经写清楚:--device参数可以强制指定,比如--device mps或--device '0,1'。多GPU传入时需要加引号,否则shell会把逗号当作分隔符。启动命令很简单:
python train_yolo11.py --data night_pedestrian/yolo/data.yaml --weights yolo11s.pt --epochs 100 --batch 16Mac机器上,如果脚本检测不到MPS,就自动跑CPU,但你会看到[Device] cpu。GPU机器想用第二块卡,就--device 1。两卡並行,可以用--device '0,1'。
3.3 训练参数里那些“玄学”:imgsz、epochs、batch、patience
imgsz是输入分辨率,夜间行人普遍偏小,建议不要低于640。显存够的话试--imgsz 1280,小目标召回能涨几个点,但训练时间翻倍。epochs默认100对5000张图足够,但夜间数据方差大,建议加--patience 20,连续20个epoch验证指标不涨就早停。batch的影响不是线性的:batch太大容易训爆,太小loss震荡。我的习惯是batch=16配640分辨率;如果显存不够,先用--batch -1让脚本自动探测最大batch。
YOLO11默认开启马赛克增强,但夜间低光场景下,马赛克拼接出的图可能让模型学到纹理错觉。我血泪经验是把mosaic降到0.5,再打开亮度扰动,效果比默认好。完整的train调用可以这样扩展:
model.train( data=args.data, imgsz=args.imgsz, epochs=args.epochs, batch=args.batch, device=device, mosaic=0.5, hsv_h=0.015, hsv_s=0.4, hsv_v=0.4, patience=20, )hsv_h/s/v分别是色调、饱和度、亮度的随机扰动范围。夜间图像本身暗,把亮度扰动开大一点能模拟不同光照强度,但不要调太狠,否则行人会被随机调成黑色,反而学不到特征。
3.4 训练日志怎么看:loss曲线和验证指标
训练结束后,ultralytics在runs/detect/train/下生成last.pt、best.pt和results.csv。不要只盯最终mAP,要看曲线:train/box_loss和train/cls_loss是否稳步下降;val/box_loss是否和train保持同步。如果两者差距越来越大,说明过拟合了。夜间行人这种单一类别任务,val/box_loss一般在2左右,mAP50在0.5-0.7之间都算正常。如果mAP低到0.2,基本是标签有问题或训练集验证集划分不一致,先回头查数据集。
训练完用best.pt在验证集上单独跑一次,确认不是中间某个epoch的偶然结果:
python -c "from ultralytics import YOLO; YOLO('runs/detect/train/weights/best.pt').val(data='night_pedestrian/yolo/data.yaml', split='val')"这个命令会输出完整验证报告,包括各类别AP。如果你想看预测效果,用predict方法抽几张夜间图,同时保存可视化结果。
4. 三平台环境适配:GPU(GPUs)/CPU/Mac上的坑与验证
标题里写了“GPU(GPUs)/CPU/Mac三平台”,说明这套脚本要能在不同设备上无障碍切换。但环境适配的坑比想象中多,最常见的不是代码问题,而是PyTorch装错了版本。
4.1 环境检测:一条命令看清CUDA、MPS和PyTorch版本
在跑训练之前,先用命令确认环境,别一上来就装包。我习惯按顺序检查:
python -c "import torch; print(torch.__version__); print(torch.cuda.is_available())" nvidia-smi如果torch.cuda.is_available()输出False,但nvidia-smi能看到显卡,说明你装的是CPU版PyTorch。这是最经典的翻车点,去PyTorch官网按自己的CUDA版本选安装命令,不要复制网上过时的命令。驱动方面,nvidia-smi显示的CUDA版本是驱动支持的版本,PyTorch自带的CUDA runtime只要不大于这个数就能用,不需要严格相等。
Mac平台检查MPS:
python -c "import torch; print(hasattr(torch.backends, 'mps') and torch.backends.mps.is_available())"输出True说明可用。注意:PyTorch的MPS支持还在完善,某些算子没有实现,训练到一半报错很正常,后面避坑章节专门讲。
4.2 多GPU训练:batch、显存与分布式配置
多卡训练在ultralytics里只是把device设为'0,1',但有一个重要细节:每张卡的batch是总batch除以卡数。比如--batch 32 --device '0,1',每卡16张。如果你的显卡是8GB显存,16张640x640的图很可能溢出,报CUDA out of memory。我一般先降到--batch 16 --device '0,1',即每卡8张。
如果还想用更大的总batch,开梯度累积:
model.train(..., batch=16, device='0,1', accumulate=4)accumulate=4表示每4次反向传播才更新一次权重,等效总batch变成64,但显存占用不变。还有一个实用参数是cache=True,把图像缓存到内存,减少硬盘IO等待,但只对CPU内存有效,显存不足时用不上。
多卡训练不需要改data.yaml,因为每张卡共享数据集路径。只要path是相对路径,且脚本是从night_pedestrian/yolo/目录启动,就能找到。
4.3 CPU和Mac的取舍:先跑通再上量
CPU训练5000张图、100个epoch会非常慢,我一般用subset=0.1先跑5个epoch验证整个流程:数据读取、模型搭建、前向反向、验证都正常,再全量跑。在Mac上,MPS比CPU快很多,但内存和显存共享,batch别设太大。我的经验是Mac上用yolo11n.pt、imgsz=640、batch=8,训练比较稳;如果报内存不足,就降到4。
还有一个坑:Mac上MPS的浮点精度和CUDA不完全一样,loss曲线会有些许差异,这正常,不影响最终权重。但如果你在Mac上训练后把权重拿到GPU机器上推理,精度基本一致,只是速度差异明显。
4.4 三平台环境适配清单
| 平台 | 推荐设备 | 常见检查命令 | 建议模型 | 建议batch |
|---|---|---|---|---|
| NVIDIA GPU | cuda | nvidia-smi;torch.cuda.is_available() | yolo11s | 16-32 |
| 纯CPU | cpu | torch.backends.mps不适用 | yolo11n | 4-8 |
| Mac Apple Silicon | mps | torch.backends.mps.is_available() | yolo11n/s | 4-8 |
如果你在公共环境中无法用nvidia-smi,也可以看torch.version.cuda和torch.cuda.get_device_name(0)。注意:虚拟环境下装PyTorch时,很多人漏装torchvision配套版本,导致ultralytics导入时报错,所以建议用pip list | grep torch检查三个包:torch、torchvision、ultralytics。
5. 避坑与常见问题:夜间行人检测训练必须注意的五个坑
夜间行人检测涉及大量低光、小目标和遮挡样本,训练过程中有不少坑。我把自己踩过的、身边同事掉过的坑整理成下面五条,每条都按“现象 -> 原因 -> 解决”的方式说,方便你直接对照。
5.1 训练时loss不降,但验证指标正常?先查数据增强和预训练权重
现象:训练loss一直忽上忽下,甚至越来越大,但验证mAP还在缓慢上升。
原因:YOLO11默认开启马赛克等强增强,夜间低光图经过几何拼接后本身就难学习,loss震荡是正常的,但“只涨不降”就危险。另一个原因是没加载预训练权重,从yolo11s.yaml随机初始化,夜间这种单一类别任务很难学。
解决:确认--weights yolo11s.pt,不要写错成yaml文件。然后把mosaic从默认1.0降到0.5,mixup调到0.0,先关掉部分增强,让loss曲线稳定下来,再逐步打开。
5.2 标签坐标越界:训练里“行人”全成废标签
现象:训练日志出现大量WARNING: Ignoring corrupted image或loss为NaN。
原因:从VOC/COCO转YOLO时,归一化除以了错误的宽高,比如图像被resize过但标注坐标还是原图尺寸。也有可能是把两个XML合并到了一个txt里,导致一行解析失败。
解决:用2.2里的体检脚本扫描,打印越界行。如果是单张图越界,找到原图的真实宽高,重新计算归一化值;如果是合并问题,把多行拆分。修正后重新体检,直到invalid label lines: 0再训练。
5.3 Mac上MPS训练到一半报错:不是你的问题
现象:训练到第30个epoch时突然报NotImplementedError: MPS not implemented for 'add_'。
原因:PyTorch的MPS后端对部分算子不完善,ultralytics某些增强或loss用到了不兼容算子,往往和具体版本有关。
解决:先升级PyTorch到最新版,很多算子问题在更新后就解决了。还不行,就改用device='cpu',慢但稳。另一个办法是把mosaic关掉,我遇到过马赛克拼接触发不支持的算子,关掉后MPS反而能跑到底。
5.4 多GPU训练显存不足:不要只调batch
现象:CUDA error: out of memory,或者训练进程直接被系统杀掉。
原因:除了batch,还有可能是数据加载进程占用显存,或者模型在验证时也占了一份显存。有些人只调batch,从32降到8还是爆,但没看workers。
解决:先把--batch 8 --device '0,1',再把workers=2(数据加载线程数)调小,避免子进程抢占显存。如果不够,开cache=True和accumulate=4,前者加快IO,后者等效扩大batch但不增显存。
5.5 夜间召回率低:小目标才是主角
现象:mAP50到了0.6,但视频里远处的行人总是漏检。
原因:夜间行人在图像里可能只有十几个像素,训练时默认640分辨率下小目标特征太弱,标注里小目标占比又低,模型学不到。
解决:训练时用--imgsz 1280,同时设置scale=0.5,让模型见更多尺度的目标。验证时单独统计小目标AP,用6章的代码。我上次就是只看了整体mAP,实际一跑车灯眩光场景就露陷,之后把输入尺寸加大,才把夜间召回拉回来。
6. 进阶验证与调优:用帧序列和低光指标评估夜间行人模型
光看mAP50不够。夜间场景的模型,我会多跑两步验证:第一步是用model.val()拿每个类别的AP,按面积分段看小目标表现;第二步是把best.pt放到一段夜间视频上跑,看连续帧的框是否跳来跳去。训练好的权重在runs/detect/train/weights/best.pt,用下面代码快速输出分尺寸指标:
from ultralytics import YOLO model = YOLO('runs/detect/train/weights/best.pt') metrics = model.val(data='night_pedestrian/yolo/data.yaml', split='val') print('mAP50:', metrics.box.map50) # 按面积段统计:小目标(area < 32^2)、中目标(32^2 ~ 96^2)、大目标 if hasattr(metrics.box, 'ap50'): for i in range(3): ap50 = metrics.box.ap50[i] print(f'area category {i}: AP50 {ap50:.3f}')这段代码打印的结果里,如果小目标AP明显低于中目标,就需要继续调。我遇到过mAP50达到0.7但小目标AP只有0.15的情况,于是把imgsz从640改成1280,小目标AP翻倍。但这样做推理速度下降,要权衡。
我个人的教训是:夜间行人不能只看平均精度,还要看在阴影里、车灯亮斑旁的行人能不能稳定检出。曾经有个项目我只看mAP,结果在夜间停车场测试时路灯下的行人也漏了一半。后来把验证集单独收集了200张低光图,每次训练完都跑一遍,模型确实更“抗造”。上面这段代码就是每次训练完的必做检查,你可以把它写进一个eval.sh,跑完训练自动执行。希望帮到你。
本文还有配套的精品资源,点击获取