
简介面向目标检测研究与应用场景的街道监控行人数据集共包含1200张实拍图片已按YOLO系列算法要求完成标注与训练集、验证集、测试集划分。数据集为单类别person标注所有标签均为yolo格式txt文件并附带数据集配置文件yaml从YOLOv3到YOLOv10无需任何转换即可直接加载训练。压缩包共2000个文件包含788张jpg原图、1211个txt标签文件及1个yaml配置整体约138.6MB文件结构清晰可直接投入模型训练。素材全部来自街道监控视角贴近真实场景测试显示基于YOLOv9训练准确率达96.4%可支撑科研实验、课设毕设以及实际落地项目。目前已有723人学习下载适合需要快速获取规范行人检测数据的研究者与算法开发者使用。1. 拿到这份数据集的第一天先别急着开训1200张街道监控视角行人检测数据集带着yolo格式的txt标签还已经划分好了训练集、验证集和测试集这几乎是给YOLO系列算法量身定做的开箱物料。很多人的第一反应是直接解压开训但我建议你先花半小时做一次数据体检监控视角下行人目标普遍偏小、密集、遮挡多数据本身的标注质量比模型参数量更影响最终mAP。这篇文章就围绕“怎么用、怎么调、坑在哪”展开适合正在做智慧城市、路口安防、客流统计这类项目的开发者。如果你是第一次跑YOLO也能照着步骤把环境、命令、验证一次走通。2. 打开压缩包先做三件事核对标签格式、校验划分、跑数据体检2.1 yolo格式的txt标签到底长什么样YOLO系列的txt标签是纯文本格式每一行代表一个目标框五个字段按空格分隔类别ID、中心点x坐标、中心点y坐标、框宽度、框高度。关键在于这五个数值的取值范围类别ID从0开始计数四个坐标值全部做了归一化也就是用真实像素值除以图片的宽或高最终落在0到1之间。这样做的好处是模型输入尺寸无论如何缩放640、1280甚至更高标签都不需要跟着重新计算。在监控视角下行人框通常呈现“高大于宽”的特点例如一行合法的标签可能长这样0 0.624251 0.448172 0.083021 0.243289这里0代表第0类后面四个数分别是归一化后的中心点x、中心点y、框宽、框高。解压后你随便打开一个txt文件如果看到每行都是五个数、且全部在0到1范围内说明这份数据集的标签格式没有造假。如果有任何一行出现大于1的坐标后面训练时loss会直接发散这类文件需要立刻挑出来。2.2 训练集、验证集、测试集的划分比例与存放规则标题里写明“已划分为训练集、验证集、测试集”这省掉了自己做split的麻烦。常见的划分做法是训练集、验证集、测试集比例接近8:1:1或7:2:1监控类项目里验证集不宜太小否则训练过程中早停和调参都不可靠。你打开压缩包后应该能看到类似下面的目录结构dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/images和labels目录一一对应同名图片与同名txt共享文件名前缀。绝大多数YOLO训练脚本都要求标签文件与图片文件保持同名只有扩展名不同例如frame_000001.jpg对应frame_000001.txt。如果压缩包内目录结构与上述不同比如把图片和标签混放在同一个文件夹里你就需要先写脚本把它们拆开再交给训练脚本处理。验证集的存在感经常被新手忽略。训练时模型每完成一个epoch都会在验证集上算一次mAP用来判断模型有没有过拟合、要不要早停而测试集只在你训练结束后跑一次模拟真实场景下的泛化表现。如果这份数据集的测试集看起来和训练集同分布不要觉得奇怪小规模数据集普遍如此后续真实部署时再补一份跨时段、跨路口的验证数据才是关键。2.3 用一段脚本给数据集做体检类别数、bbox分布、图片一致性我拿到任何数据集的第一件事都是跑体检而不是直接训练。下面这个脚本只依赖Python标准库不装任何包逐个读取训练集txt统计类别分布、检查坐标是否越界、核对图片文件是否存在并计算所有框的宽高均值。把这些信息打印出来你对这份数据的真实质量心里就有数了。import os from collections import Counter from glob import glob label_dir dataset/labels/train img_dir dataset/images/train cls_counter Counter() box_sizes [] bad_files [] for txt in sorted(glob(os.path.join(label_dir, *.txt))): img_file txt.replace(labels, images).replace(.txt, .jpg) if not os.path.exists(img_file): bad_files.append((os.path.basename(txt), jpg missing)) with open(txt, r, encodingutf-8) as f: lines [l.strip() for l in f if l.strip() and not l.startswith(#)] if not lines: bad_files.append((os.path.basename(txt), empty txt)) continue for line in lines: parts line.split() if len(parts) ! 5: bad_files.append((os.path.basename(txt), f字段数不对: {line})) continue cid int(float(parts[0])) cx, cy, w, h map(float, parts[1:]) if not (0 cx 1 and 0 cy 1 and 0 w 1 and 0 h 1): bad_files.append((os.path.basename(txt), f坐标越界: {line})) cls_counter[cid] 1 box_sizes.append((w, h)) print(类别分布:, dict(cls_counter)) print(f异常文件数: {len(bad_files)}) for item in bad_files[:10]: print(item) if box_sizes: avg_w sum(b[0] for b in box_sizes) / len(box_sizes) avg_h sum(b[1] for b in box_sizes) / len(box_sizes) print(f平均框宽: {avg_w:.4f}, 平均框高: {avg_h:.4f})脚本逻辑不复杂对每个txt先检查同名的jpg是否存在再检查文件是否为空、每行字段是否为5个、坐标是否越界最后按类别ID计数并累计宽高。这里有几个关键参数需要你按实际情况修改label_dir和img_dir要指向真实路径如果图片是png把.replace(.jpg, .png)改掉即可。脚本跑出来的结果里最值得关注的是平均框高是否明显大于框宽。监控视角下行人的框高通常是框宽的2到3倍如果平均宽高比接近1说明数据里混杂了大量车辆或其他方形目标你需要打开几张图确认标注是否真的集中在行人身上。另外如果存在空txt说明这张图里没有任何人训练时该图只贡献背景信息这类文件数量占比过高会让模型倾向于把背景预测为目标需要留意。3. 把数据集接进本机YOLO环境目录组织、data.yaml与训练命令3.1 标准目录结构images、labels、data.yaml缺一不可YOLOv5和YOLOv8在读取自定义数据集时都依赖一个data.yaml文件它告诉训练脚本数据放在哪里、有几类、类别叫什么。我一般会为每个项目单独建一个文件夹把压缩包里的images和labels原样放进去再补一个data.yaml结构如下dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml的内容是这套流程的命门。假设这个数据集只标注了person一类文件内容大概是这样path: /home/user/dataset train: images/train val: images/val test: images/test nc: 1 names: 0: personpath是数据集的绝对路径train、val、test都是相对path的路径。nc是类别总数这里写1。names是类别名列表索引顺序必须和txt里的类别ID一一对应names的顺序一旦写反模型学到的特征和标签就对不上训练过程看起来正常推理结果却完全不可信。所以data.yaml写完后建议反复核对一遍nc和names。3.2 用软链接组织数据集避免重复拷贝浪费磁盘大规模数据集动辄几十GB但1200张监控图的体积可能只有几百MB到1GB左右直接拷贝问题不大。不过我很推荐用软链接来组织数据因为做实验时你可能同时测试多个yaml配置软链接可以让你在不动原始数据的情况下快速切换目录结构。命令如下mkdir -p dataset/images/{train,val,test} dataset/labels/{train,val,test} ln -s /data/raw/street_dataset/images/train dataset/images/train ln -s /data/raw/street_dataset/images/val dataset/images/val ln -s /data/raw/street_dataset/images/test dataset/images/test ln -s /data/raw/street_dataset/labels/train dataset/labels/train ln -s /data/raw/street_dataset/labels/val dataset/labels/val ln -s /data/raw/street_dataset/labels/test dataset/labels/test第一条命令把目标目录建好后面六条命令把压缩包解压后的真实图片和标签目录软链到位。注意/data/raw/street_dataset要替换成你解压后的真实路径。软链接的好处是训练脚本读的是dataset/下的路径实际数据还在原位置后续想换数据集时直接重链不污染原始文件。3.3 直接能跑的训练命令yolov5与yolov8两套都给你YOLO系列目前主流的两套实现是yolov5和yolov8两者的数据输入方式基本一致都吃data.yaml。yolov5的启动命令如下cd yolov5 python train.py \ --data /home/user/dataset/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 150 \ --workers 8 \ --device 0参数含义分别是--data指向刚写的data.yaml--weights用yolov5s.pt做预训练权重--img输入图片分辨率640是默认值--batch批大小按显存调--epochs训练轮数小数据集150轮足够--workers数据加载线程数Linux下8到16都可以--device 0用第一张显卡训练。如果显存不够把batch降到8或4同时调低workers。yolov8的命令风格更简洁参数用等号传值cd ultralytics yolo detect train \ data/home/user/dataset/data.yaml \ modelyolov8s.pt \ imgsz640 \ batch16 \ epochs150 \ workers8 \ device0modelyolov8s.pt指定预训练权重yolov8的s版本是精度和速度的平衡点监控场景里通常够用。第一次跑通不要急着上大模型用s版本把流程走通再考虑换m或l版本。跑起来后日志里会打印每轮的box_loss、cls_loss和验证集mAP如果epochs推到一半mAP50还在低位徘徊那就是前面体检或标签有隐藏问题。4. 第一次训练跑通后看点在哪关键参数、验证指标与止损策略4.1 训练参数怎么设epochs、batch-size、imgsz、workers参数设得对不对直接决定你这一趟训练是出成果还是白烧电。针对1200张这类中小规模数据集我常用的参数组合如下表参数常用值设置原则epochs100-200小数据集训练过快早停机制建议开启batch-size8-32显存小于8G用816G用16越大收敛越快imgsz640或1280监控小目标多时优先1280但显存占用翻倍workers4-16按CPU核数设过高会导致IO瓶颈device0多卡用0,1无显卡用cpu但极慢epochs设150是我在这个量级数据上的习惯。1200张图训练集大概950张模型在50轮左右基本能收敛设150轮是为了给早停留出缓冲。imgsz这里值得多说一句监控画面里的行人往往只占几十个像素640分辨率下小目标会被下采样到几乎不可见如果你发现验证集mAP上不去先把imgsz提到1280试一次损失函数下降速度会明显不同。4.2 训练日志与验证指标从P/R到mAP50、mAP50-95训练跑起来后你会看到一系列指标在翻滚更新。精确率PPrecision代表模型预测出的所有框中真正是人的比例召回率RRecall代表所有真实行人中被模型找出来的比例。mAP50是IoU阈值取0.5时的平均精度mAP50-95则取0.5到0.95每隔0.05算一次再平均。监控场景下mAP50容易刷到0.95以上但这不代表部署可用我更看重mAP50-95和R。监控视角有个实际样本行人在远处只有二三十像素高近处能占满半个画面这种尺度分布极不均匀的问题会直接拉低mAP50-95。如果训练日志显示R偏低而P很高说明模型倾向于“宁缺毋滥”宁可漏检也不误检如果P低R高说明模型把很多背景误判成了人。行人检测项目里漏检比误检更危险调参思路通常是在保证P不低于0.8的前提下尽量拉高R。4.3 mAP上不去时优先动这三个参数验证集mAP卡住不动时我建议按顺序排查三个点。第一个是imgsz这是监控行人检测最容易被忽略的变量远距离小目标在低分辨率输入下特征几乎被抹平直接把imgsz从640拉到1280往往立竿见影。第二个是batch-sizebatch太小导致BN统计量不稳定模型在验证集上忽高忽低我见过batch4时loss曲线像锯齿一样跳动的情况调到16后曲线立刻平滑了。第三个是数据增强的hsv和flipud参数监控画面里行人不会倒立如果把flipud打开等于给模型灌入一半不真实的倒置样本干扰非常大。如果三个参数都调过mAP还是上不去这就不是参数问题了。回头用体检脚本重新检查标签尤其是那些坐标越界、类别号错乱的txt通常是训练集里混入了格式错误的数据。拿一份错误标签配一份正确标签对比模型会被来回拉扯最终loss在收敛和发散之间反复横跳。这种情况重训不如先修数据。5. 避坑监控视角行人检测最容易翻车的5个地方5.1 类别号对不上txt里写0data.yaml里排第1现象训练过程loss正常下降验证集mAP也正常但推理时模型把行人识别成其他物体或者完全漏检。原因txt标签里的类别ID是数字它对应的是data.yaml里names列表的下标。如果txt里写了0但data.yaml的names写成了[background, person]那模型学到的0类其实是背景预测结果自然全乱。压缩包里的标签如果是从其他工具导出的类别ID和names顺序不匹配的情况非常常见。解决训练前先写个小命令打印data.yaml的names再随机抽三个txt文件核对第一行的类别数字。最稳妥的做法是在体检脚本里输出类别分布确认最大类别ID小于data.yaml里的nc。一个简单的验证命令python -c import yaml; dyaml.safe_load(open(dataset/data.yaml)); print(d[names], d[nc])这里输出的names列表长度必须等于nc且类别名顺序与txt里的ID含义一致。5.2 jpg和txt配对错位断点续跑的隐藏风险现象训练了几十轮发现验证集里某张图上明明有人模型就是检测不到手动打开图片和同名txt发现框的位置与画面内容完全对不上。原因解压过程中某些文件被改名或者有人用同步工具拷贝时只拷了图片没拷对应txt。更隐蔽的情况是有人用Excel或批量重命名工具把txt统一加过前缀而图片没加导致同名配对错位。解决训练前跑一次严格配对检查遍历所有txt找对应jpg遍历所有jpg找对应txt两边都缺一不可。针对上面那个体检脚本把jpg missing和txt missing都统计出来一旦出现立即停下修复不要带着错位数据开训。另外养成一个习惯任何重命名操作都同时作用于images和labels两层目录别只改一半。5.3 小目标漏检监控视角的行人只有几十像素高现象模型在测试集上mAP50很好看但实际部署到路口时30米外的行人完全检测不到拉近的图很正常一旦目标变小就失效。原因监控摄像头的安装高度通常在3到6米俯视角度下远处行人的像素高度往往不足图像高度的5%。默认的640输入分辨率经过5次下采样后小目标的特征图只剩几个像素信息量不足以支撑分类和定位。这是监控视角行人检测的物理瓶颈不是模型没训练好。解决训练阶段把imgsz提高到1280即使显存不足也要在推理阶段采用缩放或切片方式。部署时可以用原图推理并开启augment模式的多尺度测试。另外训练时开启数据增强里的随机缩放模拟不同距离下的行人尺度分布能显著改善远距离漏检。如果仍然不行那就需要加入更大视野的负样本或考虑换用专门的small-object分支结构。5.4 密集行人场景NMS误杀一群人并肩走只剩一个框现象街道上三五个行人并肩行走画面上只有一个检测框或者两个紧挨着的人被识别成一个。原因YOLO系模型预测出大量候选框后NMS非极大值抑制会把重叠度高的框合并。监控视角下行人天然存在大量局部遮挡两个紧挨的行人IoU可能超过0.5默认的NMS阈值就会把其中一个框当作冗余抑制掉。这在CrowdHuman这类密集行人数据集上尤其明显普通场景训练出来的模型很容易在密集场景翻车。解决推理时把NMS的IoU阈值从默认的0.45调低到0.35到0.4保留更多高重叠框减少漏检。yolov5的推理命令里这样加python detect.py --weights best.pt --source test_images --iou-thres 0.4yolov8则是在模型输入里传nms_iou参数。代价是误检框会变多需要在conf阈值上做平衡。训练角度可以尝试在数据里混入密集行人片段让模型提前适应这种分布。5.5 测试集指标虚高同场景泄漏换个路口立刻现原形现象测试集mAP50高达0.98你以为模型已经能上线了结果拿到另一个路口的监控画面一测mAP掉到0.6。原因数据划分时如果用的是全局随机洗牌同一个摄像头连续帧的画面会被同时分进训练集和测试集测试集几乎在“开卷考试”。模型已经见过同场景的背景、光照和目标姿态测试指标虚高是必然的。1200张的数据量里如果大量图片来自同一段监控视频这个问题会更严重。解决先确认压缩包里的划分是否按场景做了分组。如果发现是随机划分我的做法是手动挑出几十张不同时段、不同角度的图作为独立评估集把原始test目录当作参考指标另做一份“真实泛化测试”。验证时优先看mAP50-95而不是mAP50因为前者对场景变化更敏感。部署后连续记录一周的漏检和误检日志比起玩指标那才是模型能不能上线的事实依据。6. 跑完测试集之后验证、部署与下一步扩展训练完毕后先用测试集做最终验证。yolov8的命令是yolo detect val \ data/home/user/dataset/data.yaml \ modelruns/detect/train/weights/best.pt \ imgsz640这一步输出的是模型在未参与训练的数据上的最终mAP。这里要特别留意一个小习惯看我保存下来的best.pt和last.pt在测试集上的差距。如果best.pt明显优于last.pt说明训练后期验证集上已经出现过拟合苗头best.pt反而更可信如果两者几乎持平说明训练过程比较健康。监控场景里我通常会额外跑一次视频推理看连续的帧上检测框是否抖动频繁这能暴露单帧指标看不到的时间域问题。部署阶段我会把推理参数明确固定下来conf设为0.25到0.3nms_iou设为0.4这是监控行人场景中相对稳的一组组合。如果后续要把这个检测器接到人流统计或跟踪任务里我的建议是不要只看检测框本身可以先套一个轻量ReID模型给每个检测框提特征再做跨帧关联这样能大幅减少遮挡场景下的计数误差。再往后如果项目需要区分行人的姿态或朝向可以在现有检测结果上裁剪出行人区域接一个分类头或实例分割模型本质上是把目标检测当作前置任务而不是换一个模型重训。我现在拿到任何数据集第一件事永远是跑体检脚本不换人、不跳过这个习惯救过我太多次。1200张的体量不算大但监控视角数据只要用对方法产出的模型完全可以在真实路口扛住一段时间。希望上面的步骤和这五条避坑经验对你有用训练顺利。本文还有配套的精品资源点击获取