简介:面向YOLO系列算法训练与验证的行人检测数据集,共包含七千二百零三张带标注图像,压缩包内文件总数为两千个,以XML标注文件为主,整体大小约二百三十三点七六MB。数据已划分好训练、验证与测试集,并附带data.yaml配置文件,可直接用于YOLOv5、YOLOv8、YOLOv9、YOLOv7等常见版本的模型训练、验证与测试,省去标注格式转换和数据集整理的繁琐步骤。每个XML文件都记录着目标所属类别及边界框坐标,信息结构规范,便于二次处理或迁移至其他检测框架。数据集围绕行人目标构建,可应用于行人检测、人流统计等计算机视觉任务,也能作为算法改进与对比实验的基准数据。目前已有138人学习使用,适合目标检测入门开发者、算法研究人员以及需要高质量行人样本进行项目实践的人群。
1. 行人数据集最坑的不是模型:7203 张带标签图像直接可跑
做行人检测的都知道,模型选型再纠结,最后 90% 的时间都耗在数据上。我见过太多人下载 yolo算法行人数据集,解压之后要么只有图片没有标签,要么全是 xml、训练脚本根本读不进去,还有的标签和图片文件名对不上,光是清洗就花掉一整天。这份资源是 7203 张带标签图像,txt 和 xml 两种格式各自独立成文件夹,data.yaml 已经写好路径和类别,解压完可以直接喂给 yolov5、yolov7、yolov8、yolov9、yolov10、yolo11 系列算法。适合做视频监控、人流密度统计、辅助驾驶行人识别的从业者,不管你只是想快速验证检测效果,还是要微调一个正式上线的模型,都不需要再从零整理数据。
2. 两种标签格式:从 VOC xml 到 YOLO txt 的坐标换算与校验
2.1 VOC xml:给人读的绝对坐标标签
这份资源里同时提供 VOC 格式的 xml 标签,文件名和图片名严格对应,比如img_0380_420.xml对应的就是同编号图片的标注。VOC xml 是 labelImg 这类标注工具导出的标准产物,打开后长这样:
<annotation> <folder>person_dataset</folder> <filename>img_0380_420.jpg</filename> <size> <width>1920</width> <height>1080</height> <depth>3</depth> </size> <object> <name>person</name> <bndbox> <xmin>100</xmin> <ymin>200</ymin> <xmax>300</xmax> <ymax>600</ymax> </bndbox> </object> </annotation>注意<filename>和<size>里的图片宽高,后续做坐标换算时要拿它们当分母,很多人转格式翻车正是因为分子分母用错了尺寸。xml 里所有坐标都是绝对像素值,xmin/ymin 是左上角,xmax/ymax 是右下角,给人看非常直观,标注错在哪里一眼能看出来;缺点是跟图像分辨率强耦合,图片一缩放,所有坐标都要重新算。
那为什么老团队还在用 xml?因为历史标注流水线、开源标注脚本和不少 ToB 项目里的旧工具链只认 VOC。这份资源同时保留两种格式,就是为了让你不需要为了兼容老工具去额外写转换脚本。
2.2 YOLO txt:归一化坐标的换算与可视化校验
YOLO 系列训练脚本读取的 txt 标签每行只有五个数字,含义按摘要给出的标准定义是class x_center y_center width height,后四个值全是归一化浮点数。从 xml 换算到 txt 的公式是固定的:
img_w, img_h = 1920, 1080 # 来自 xml 的 <size> 字段 xmin, ymin, xmax, ymax = 100, 200, 300, 600 x_center = ((xmin + xmax) / 2) / img_w y_center = ((ymin + ymax) / 2) / img_h box_w = (xmax - xmin) / img_w box_h = (ymax - ymin) / img_h # 类别索引从 0 开始,这份数据只有 person 一类 print(f"0 {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}") # 输出:0 0.104167 0.370370 0.104167 0.370370这段逻辑很直白:中心点 x 坐标取 (xmin+xmax)/2 再除以图像宽度,框宽用像素宽度除以图像宽度。保留六位小数是 Ultralytics 训练时读取标签的常见精度,位数太少放大缩小时容易丢精度。归一化之后标签与分辨率解耦,同一个 txt 文件喂给 imgsz=640 或 imgsz=1280 都能直接复用,不需要因为改训练尺寸而重写标签。
拿到 txt 之后不要急着开训,先做一次合法性校验:
bad_count = 0 with open("train/labels/img_0380_420.txt", "r", encoding="utf-8") as f: for line in f: parts = list(map(float, line.strip().split())) if len(parts) != 5: print("字段数不对:", line) bad_count += 1 continue cls, xc, yc, w, h = parts if not (0 <= xc <= 1 and 0 <= yc <= 1 and 0 <= w <= 1 and 0 <= h <= 1): print(f"越界:{xc}, {yc}, {w}, {h}") bad_count += 1 if w <= 0 or h <= 0: print("宽高异常:", line) bad_count += 1 print("异常数量:", bad_count)这段脚本是每个数据集进训练前我都会跑一遍的体检项。len(parts) != 5抓出多列少列的标签;范围判断抓出把绝对坐标直接当归一化坐标写的低级错误;w <= 0抓出标注时手抖拉出来的反框。如果输出异常数量是 0,标签在格式层面才算合格。
数字校验还不够直观,我一般还会把 txt 画回原图,人眼对比最靠谱:
from PIL import Image, ImageDraw img = Image.open("images/train/img_0380_420.jpg") draw = ImageDraw.Draw(img) for cls, xc, yc, w, h in lines: xmin = (xc - w / 2) * img.width ymin = (yc - h / 2) * img.height xmax = (xc + w / 2) * img.width ymax = (yc + h / 2) * img.height draw.rectangle([xmin, ymin, xmax, ymax], outline="red", width=3) img.save("check_img_0380_420.jpg")这个脚本把归一化坐标重新乘回原图宽高,再画框存图。我建议每 1000 张抽查一张,能发现脚本检测不出来的语义错误,比如框框住了半个身体、漏标了远处行人这些情况。
2.3 两种格式怎么选:看训练框架,不看个人习惯
下面是两种标签格式的对照,方便你判断自己的流程该用哪一份:
| 对比项 | VOC xml | YOLO txt |
|---|---|---|
| 坐标类型 | 绝对像素 | 归一化浮点 |
| 可读性 | 高,文本编辑器直接查 | 低,需要画框工具才能直观 |
| 与分辨率关系 | 强相关,缩放后重算 | 无关,换 imgsz 不用改 |
| 训练框架 | YOLO 原生脚本不直接吃 | YOLO 训练直接读 |
| 老工具兼容 | labelImg、部分标注平台导出 | Roboflow 及新框架默认 |
结论是:跑 Ultralytics 系训练脚本直接用 txt;排查某一帧标注是否出错,翻 xml 更快。资源包里两份标签来自同一标注结果,理论上框位置一致,但建议按 2.2 的方式抽查几个文件,确认两份标签没有被人为改动过,后面跑训练才不会被玄学指标骗到。
3. 训练前检查:data.yaml 写对,少跑三小时冤枉路
3.1 解压后的目录结构:先看清再动手
拿到 zip 后不要解压完就敲yolo train,先看目录结构。数据集解压后通常是这样的布局:
person_dataset/ ├── images/ │ ├── train/ │ │ └── img_0380_420.jpg │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ │ └── img_0380_420.txt │ ├── val/ │ └── test/ ├── voc_xml/ │ ├── train/ │ │ └── img_0380_420.xml │ ├── val/ │ └── test/ ├── data.yaml └── README.txt我会用两条命令确认整体结构是否符合预期:
find person_dataset -type d | sort ls person_dataset/images/train | head -n 5 ls person_dataset/labels/train | head -n 5find把目录树完整打出来,重点看 train/val/test 是否在 images 和 labels 里都存在;ls两条看两边文件名前缀是否一致。文件名对不上是训练跑不起来的最低级门槛,这个过不了,data.yaml 写得再对也没用。
这里有个常见误解:有人以为 images 和 labels 必须在同一级目录才算合格。其实 Ultralytics 只认 data.yaml 里写的路径,标签放在哪一层都能跑,只要路径和实际一致。这份资源默认 images 和 labels 并排摆放,符合多数人的习惯,排查问题也最容易。
3.2 data.yaml 逐项拆解:每个字段都是坑
data.yaml 是这个资源包里最重要的配置,它写得好不好,直接决定训练命令敲完之后能不能安心睡觉。一份可用的配置长这样:
# 数据集根目录,建议改成你本机的绝对路径 path: D:/datasets/person_dataset train: images/train val: images/val test: images/test # 类别数只有一个:person nc: 1 names: 0: person字段逐一说明。path是数据集根目录,train/val/test 都是相对它的路径。它最阴的地方在于:如果只写了train: images/train而没写path,Ultralytics 会把当前工作目录当根目录,然后去当前目录找 images/train,找不到直接报错。所以我拿到手第一件事就是把path改成绝对路径,让训练命令和环境解耦。
注意:如果你的训练脚本在别的目录下执行,
path写相对路径几乎必出错,改成D:/datasets/person_dataset这种绝对路径最省事。
nc是类别数量,names是类别名。这份数据集只有行人一类,所以nc: 1,names 里只有0: person。这里不能仿照 COCO 写 80 类,标签文件里的 class 编号和 names 是严格对应的,写多了或顺序乱了,后面验证集的 mAP 会直接变 0。
如果你用 yolov5 的老仓库,它的 data yaml 不认path字段,要写成train: ./images/train的形式;yolov8、yolov9、yolov10、yolo11 这些 Ultralytics 新接口则统一用path字段。同一份数据要适配两种写法,改完记得检查一遍。
3.3 一键核对图片与标签的对应关系
很多数据集表面完整,实际标签比图片少几百个。我准备了一个五分钟就能跑完的核对脚本,每次拿到新数据集都先执行:
from pathlib import Path img_dir = Path("images/train") label_dir = Path("labels/train") img_ids = {p.stem for p in img_dir.glob("*.jpg")} label_ids = {p.stem for p in label_dir.glob("*.txt")} print("图片总数:", len(img_ids)) print("标签总数:", len(label_ids)) print("有图无标签:", len(img_ids - label_ids)) print("有标签无图:", len(label_ids - img_ids)) empty_files = [p.name for p in label_dir.glob("*.txt") if p.stat().st_size == 0] print("空标签文件:", len(empty_files))脚本逻辑简单,.stem取文件名去掉后缀,把图片和标签分别构造成集合,做差集就看出了哪些 ID 没对齐。空标签文件那个检查容易被忽略:标注工具偶尔会导出 0 字节 txt,训练时相当于这张图没有目标,Ultralytics 直接把它从训练里丢弃,肉眼根本发现不了。
如果脚本输出「有图无标签」大于 0,先确认原数据集是不是漏标;如果全部为 0,说明文件层面配对没问题。接下来用 2.2 的标签内容校验把每类异常也过一遍,数据这道关卡就打通了。
4. 实操:YOLOv8 与 YOLO11 训练行人检测模型,命令与参数一次说清
4.1 环境准备:ultralytics 装完先跑 yolo checks
训练部分以 Ultralytics 系的 yolov8 和 yolo11 为例,两个版本命令行接口完全一致;yolov9、yolov10 也可以用同样的命令行加载这份数据,data.yaml 和标签格式通用,不需要额外转格式。yolov5、yolov7 的命令略不同,后面单独说明。
python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install -U ultralytics yolo checksyolo checks会打印当前环境的 torch、CUDA 版本以及 GPU 是否可用。这一步不能省:如果 CUDA 不可用,后续训练会静默降到 CPU,7203 张图的训练时长会从一小时变成一个通宵。看到类似GPU: NVIDIA GeForce RTX 3060 (12GB)的输出再继续。
4.2 训练命令:从 nano 到 large,显存与精度怎么权衡
环境就绪后,第一条训练命令我建议用参数量最小的 n 系模型跑通全流程,确认数据和配置都没问题,再换大模型提升精度。示例命令:
yolo detect train \ data=data.yaml \ model=yolov8n.pt \ epochs=100 \ batch=16 \ imgsz=640 \ device=0参数说明。model=yolov8n.pt从预训练权重开始微调,n 是 nano 版本,参数量最小,适合先验证数据健康度;后续要提精度,把 model 换成yolov8s.pt、yolov8m.pt或yolo11s.pt,配合批次调整,mAP 会有可感知的提升。batch=16是批大小,它和图像尺寸一起决定显存压力,8G 显存建议 16 或 8,12G 显存可以试 32。imgsz=640是训练分辨率,行人属于中小目标,640 性价比最高,盲目上 1280 对显存和时间都不友好。
yolo11 的启动命令几乎一模一样,也顺便验证了这个数据集跨版本兼容的能力:
yolo detect train \ data=data.yaml \ model=yolo11n.pt \ epochs=100 \ batch=16 \ imgsz=640 \ project=runs/person_yolo11project参数让每次实验输出到独立目录,否则默认全挤在runs/detect/train,多次训练后权重和图表互相覆盖,后面对比实验根本分不清结果属于哪一版。这个参数我几乎是必加的。
如果还在用 yolov5 或 yolov7 仓库,命令形式是python train.py --data data.yaml --weights yolov5s.pt --batch 16 --imgsz 640。标签需求同样是归一化 txt,data.yaml 里类别只有 person,迁移几乎不需要改标签。
提示:首次运行指定
yolov8n.pt或yolo11n.pt时,ultralytics 会自动下载预训练权重,需要联网;下载失败可以手动把权重放到当前目录再指绝对路径。
4.3 验证与推理:mAP 到底怎么看
训练结束后,拿best.pt做验证是判断数据质量最直接的参考:
yolo detect val \ data=data.yaml \ model=runs/person_yolo11/weights/best.pt \ batch=16 \ imgsz=640跑完终端会输出mAP50和mAP50-95。对行人检测任务,mAP50在 0.9 以上说明框基本全准,mAP50-95在 0.7 以上定位精度也不错。如果mAP50低于 0.5,通常不是模型能力问题,而是标签混入了脏数据,回到第二章的校验脚本去查。
推理验证我习惯直接看默认输出目录里生成的val_batch1_pred.jpg,人眼扫一遍框的位置。如果框没偏、置信度正常,再批量预测:
yolo detect predict \ model=runs/person_yolo11/weights/best.pt \ source=images/test \ conf=0.25 \ save=Trueconf=0.25是置信度阈值,低于它的检测结果被过滤。行人密集且遮挡严重时建议降到 0.1 看原始输出,小目标天然置信度偏低,阈值太高会把真实行人滤掉,制造「模型漏检」的假象。
5. 避坑排查:训练崩掉、mAP 为 0、标签越界的五个真实案例
以下五类问题都是我在实际训练中反复遇到过的,按出现频率从高到低排序。每一条我都给出现象、原因和解决方案,可以直接对照排查。
5.1 loss 变成 nan,训练整体崩掉
现象:跑到第十几个 epoch,loss 突然变成 nan,之后所有指标全部失效,图表曲线直接断掉。
原因:多数是 batch 或学习率过猛导致梯度爆炸;少部分是某个标签坐标异常,把正负样本边界破坏,模型优化方向彻底乱掉。数据里的极端窄框、坐标为负数或大于 1 的框都容易触发这类问题。
解决:先带amp=False重跑,排除混合精度引入的数值溢出;不行就把 batch 减半再固定随机种子跑一次;还不行就用 2.2 的校验脚本把标签全部扫一遍,删掉 w、h 大于 1 或有负坐标的文件,让这些图变成无标注图,训练时自动跳过。我要强调的是,这个排查顺序不能反,很多人一上来就怀疑代码有问题,实际上概率最高的还是数据。
5.2 训练不报错,但 mAP 永远是 0
现象:整个训练流程完全正常,loss 下降、图表输出、权重保存全部正常,唯独验证集的 mAP 长时间躺平在 0。
原因:几乎所有这类案例都是类别索引对不上。比如标签文件的 class 是 0,而 data.yaml 里nc误写成 2,或者 names 的顺序和标签里的类别编号不一致。单类数据最典型的就是把nc: 1写成了nc: 2,模型预测的 person 和标签的 person 对不上号,AP 自然归零。
解决:检查 data.yaml 的nc和names,单类就是nc: 1、names: {0: person};再随机抽三个 txt 文件看第一列数字,确认全是 0。这条防线每次换数据集都要走一遍,因为合并多个子数据集时类别索引常被忽略或改乱。
5.3 训练能跑,但预测框整体偏移到离谱位置
现象:loss 正常、mAP 也能看,但可视化输出里的框全部歪到一侧,或者整体缩小了一倍。
原因:这是坐标换算时分母用错了。最常见的是把图像宽度和高度对调,或者减法方向写反算出负框。少量样本下这类错误不容易暴露,样本一多误差被放大,视觉上就是框全部偏斜。
解决:用第二章的换算公式手算一个框,再拉回到像素坐标和 xml 原始值对比,误差在 1 像素以上就说明公式有误。这个数据集自带 txt 和 xml 两份标签,可以直接拿同编号文件做交叉验证,不需要自己手工造数据,这也是双格式资源最有价值的场景之一。
5.4 报 CUDA out of memory,训练秒挂
现象:训练命令刚执行不到几秒,终端直接报 CUDA 内存不足退出。
原因:显存是硬约束。7203 张图分辨率高,即使内部做了 resize,Dataloader 缓存和中间张量仍然吃满显存。很多人以为 8G 显存跑 640 没问题,实际上 batch 32 加上关闭 AMP 后瞬间超限。
解决:把 batch 从 4 开始,逐步往上加,观察显存占用稳定在 80% 左右为止。Ultralytics 支持batch=-1自动探测最大安全值,但不建议首跑用,自动探测本身也可能爆一次。另外后台跑和前台跑在显存上没有区别,省显存唯一有效的方式是降 batch 或换小模型。
5.5 data.yaml 报路径不存在,但文件明明在那
现象:Windows 上报 FileNotFoundError,找不到 images/train,但你打开资源管理器确认路径存在且目录名完全一致。
原因:Ultralytics 解读path字段依赖命令执行时的当前工作目录。数据解压在桌面,训练脚本却在项目目录里运行,相对路径从项目目录开始找,自然失败。中文目录名还会叠加编码问题,报错信息更隐蔽。
解决:data.yaml 的path改成纯英文绝对路径,比如path: D:/datasets/person_dataset,路径分隔符用正斜杠或双反斜杠。这个习惯养成了,后面换机器、换服务器都能减少一大类路径型坑。
6. 一个万用脚本:批量把老 VOC 数据集转成 YOLO 格式并自检
这份资源本身双格式齐全,但以后你从公司内部或别的老项目拿到的数据,大概率只有 VOC xml。下面这个脚本可以直接复用,它不只是转换,还带越界检查,一次运行把脏标签挡在训练集外面:
from pathlib import Path import xml.etree.ElementTree as ET def voc2yolo(xml_path, class_map, out_dir): tree = ET.parse(xml_path) root = tree.getroot() img_w = int(root.find("size/width").text) img_h = int(root.find("size/height").text) lines = [] for obj in root.findall("object"): name = obj.find("name").text if name not in class_map: continue box = obj.find("bndbox") xmin = float(box.find("xmin").text) ymin = float(box.find("ymin").text) xmax = float(box.find("xmax").text) ymax = float(box.find("ymax").text) xc = ((xmin + xmax) / 2) / img_w yc = ((ymin + ymax) / 2) / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h if not (0 <= xc <= 1 and 0 <= yc <= 1 and 0 <= w <= 1 and 0 <= h <= 1): print(f"越界跳过:{xml_path.stem}, {name}") continue lines.append(f"{class_map[name]} {xc:.6f} {yc:.6f} {w:.6f} {h:.6f}") out_path = out_dir / f"{xml_path.stem}.txt" out_path.write_text("\n".join(lines), encoding="utf-8") # 使用:类别名到索引的映射,单类行人最简 class_map = {"person": 0} xml_dir = Path("voc_xml/train") out_dir = Path("labels/train") out_dir.mkdir(parents=True, exist_ok=True) for xml_file in xml_dir.glob("*.xml"): voc2yolo(xml_file, class_map, out_dir)脚本核心是标准库xml.etree.ElementTree解析,size 字段读原始宽高,object 循环逐框换算。class_map是类别名到索引的映射,以后处理戴帽子、背包等多类场景,在字典里加几行就行。越界检测放在写入前,坏框不会进训练集,比训练时再让模型自己扛靠谱得多。
换成真实场景,我拿到这套行人数据之后第一件事并不是马上训练,而是按顺序做三步:把所有 txt 过一遍合法性校验、把 data.yaml 的 path 改成绝对路径、用 nano 模型跑 20 个 epoch 验证集通过。这三步全绿,才敢上正式训练。从那以后我每次换数据集都强制走一遍这个流程,再没被「训练半天 mAP 为 0」这种事坑过。这套双格式资源包配合这篇文章里的校验脚本,基本能让你把精力全部放在模型效果上。希望帮到你。
本文还有配套的精品资源,点击获取