
简介面向YOLO系列目标检测算法的实战型人像数据集收录2859张带标签图像适合用于行人检测、人流统计等视觉任务的模型训练与效果验证。数据集已完成训练集/验证集划分并附带data.yaml配置文件可直接接入YOLOv5、YOLOv7、YOLOv8、YOLOv9、YOLOv10、YOLO11等主流YOLO系列进行训练与测试降低上手门槛。压缩包约185.64MB共2000个文件内容以XML标注文件为主同时提供YOLO格式TXT标签和VOC格式XML标签两种格式便于在不同工具链中切换使用。标签采用纯文本规范格式其中YOLO文本每行记录一个目标依次包含类别索引、归一化后的中心点x与y坐标及宽高比例坐标均落在0到1之间可直接用于模型训练与验证。目前已有203人学习阅读适合刚接触目标检测的初学者或需要现成人像数据集进行算法对比、课程实验的开发者可有效省去人工标注与数据预处理的繁琐步骤。1. 拿到 2859 张带标签的人像图先别急着训 YOLO这份数据集能做什么第一次解压yolo算法-人数据集-2859张图像带标签.zip的人多半以为把文件夹拖进训练脚本就能出结果。我见过不少同事直接把数据丢给 YOLOv8跑完发现模型对“半身人”“背影”“密集人群”完全不感冒翻车点根本不在算法而在数据集没有先做格式校验。这份数据集的目标是给行人检测或人体检测提供监督训练素材2859 张图在深度学习里算小样本但标签只要有效足够微调出一个能投入部署的模型。它特别适合两类人一类想用 YOLO 做人体定位、人群计数等目标检测实验一类想验证迁移学习和数据增强手段的收益。如果你手里已经有一套旧标注这份数据还能拿来当补充样本。需要先说清楚2859 张带标签图像并不是“打开即用”的炼丹材料它更像一块待验证的毛坯。拿到手要做的第一件事不是写训练代码而是检查目录结构、标签格式、类别编号和边界框是否合法。下面这几章就是我从解压到训练完成会完整走一遍的流程你可以直接照着落到自己的项目里。2. 把 zip 变成可训练的 YOLO 数据集目录规范与 3 个必查字段YOLO 官方训练脚本对数据目录有约定图像和标签最好分开两个顶层文件夹文件名一一对应标签是.txt文件每行描述一个目标。很多人拿到的压缩包是图像和标注混排的甚至标签还带中文注释这种必须先规整再训练否则跑起来全是报错。2.1 解压后的目录结构长什么样images 与 labels 的对应关系先把压缩包解开用一条命令看清目录层级。我一般在 Linux 或 macOS 下操作Windows 用户用 Git Bash 或者 WSL 执行同样命令即可unzip yolo算法-人数据集-2859张图像带标签.zip -d person_ds cd person_ds tree -L 2 . echo ----- 图像数量 ----- find . -type f \( -name *.jpg -o -name *.png \) | wc -l echo ----- 标签数量 ----- find . -type f -name *.txt ! -name classes.txt | wc -ltree -L 2只会显示两层目录能快速看清有没有images和labels这对兄弟目录。find统计的时候排除classes.txt因为那个文件记录类名而不是标注框。2859 张图像对应标签数量应该也接近 2859如果标签少了说明有部分图像没有被标注后面训练时会因为找不到标签而报错或者静默跳过。如果图像和标签分布在不同文件夹还需要做一一对应检查。常见的失败形态是图像叫img_001.jpg标签叫img_001.txt但中间被程序重命名过导致找不到匹配文件。可以用一个循环找出所有缺失标签的图像for f in images/*; do base$(basename $f) stem${base%.*} if [ ! -f labels/${stem}.txt ]; then echo missing label: $base fi done这里的${base%.*}是 Shell 参数扩展作用是去掉最后一个点之后的后缀把person_001.jpg变成person_001。这样做完之后你得到的结果里如果只有零星几条输出说明大部分标签对得上如果输出成百上千条那就要回到压缩包的原始结构重新核对了。我常用的做法是把这个检查结果输出到文件里然后手动抽查几个样本避免被终端刷屏带偏判断。2.2 标签文件的 YOLO 格式类别 id 和归一化坐标如何读取YOLO 标签每行只有五个数字格式是固定的类别 id、归一化中心点 x、归一化中心点 y、归一化宽、归一化高。用head看前几行head -3 labels/person_001.txt正常输出类似0 0.519531 0.437500 0.243750 0.835938 0 0.812500 0.546875 0.162500 0.578125第一列0表示这个目标的类别编号。在只有“人”这一类别的数据集里类别 id 应该只有0。后面四列全部是 0 到 1 之间的小数它们不是像素坐标而是相对图像宽高的比值。举例来说0.519531是目标的中心点 x 坐标除以图像宽度之后的结果0.437500是中心点 y 坐标除以图像高度后的结果0.243750是框宽除以图像宽度0.835938是框高除以图像高度。这里有一个容易被绕进去的点有些标注工具导出的是像素坐标也就是左上角x1, y1, x2, y2看起来也是数字但直接拿去训练会得到一堆越界或者扭曲的框。我习惯写一个小脚本把每个标签文件读进来检查数值是否都在合法区间with open(labels/person_001.txt) as f: for line in f: parts line.split() if not parts: continue cid, xc, yc, w, h map(float, parts) print(fcid{cid}, center({xc:.3f}, {yc:.3f}), wh({w:.3f}, {h:.3f})) assert 0 cid 0, single-class dataset should only have class 0 assert 0 xc 1 and 0 yc 1, center out of range assert 0 w 1 and 0 h 1, size out of range这段脚本的设计逻辑很直白先把单行文本拆成五个浮点数然后逐项判断。cid 0是因为人数据集只有一类如果打印出来的类别编号里出现了1或更高那就说明这份数据可能还带其他类别或者标签和data.yaml中的names对不上。注意我用了,而不是这是故意留余地浮点归一化过程中极少会出现中心点刚好等于 1 的情况但如果出现0.9999这种接近边界但合法的值也不会误杀。真正需要报警的是大范围越界比如中心点跑到 1.5 或者宽高为负那种标签进训练就是灾难。2.3 3 个必查字段类别 id 是否连续、bounding box 是否越界、空标签是否合理这一节是整份数据集能不能直接用的分水岭我会一次性检查三个维度。写成一个独立的 Python 脚本check_labels.py放在数据集根目录下运行import os bad [] class_ids set() total_boxes 0 empty_files 0 for root, _, files in os.walk(labels): for name in files: if not name.endswith(.txt): continue path os.path.join(root, name) with open(path) as f: lines [x.strip() for x in f if x.strip()] if not lines: empty_files 1 continue for line in lines: vals list(map(float, line.split())) if len(vals) ! 5: bad.append((name, 列数不是5)) continue cid, xc, yc, w, h vals class_ids.add(int(cid)) total_boxes 1 if xc - w / 2 -0.01 or xc w / 2 1.01: bad.append((name, x方向越界)) if yc - h / 2 -0.01 or yc h / 2 1.01: bad.append((name, y方向越界)) print(ftotal_boxes{total_boxes}, class_ids{sorted(class_ids)}, empty_files{empty_files}) if bad: print(---- 发现问题 ----) for f, reason in bad[:50]: print(f, reason) else: print(标签格式检查通过)这份脚本会输出三组关键信息。第一行里class_ids会列出所有出现过的类别编号如果人数据集里出现{0, 1}就说明标签混了类别需要确认0和1分别代表什么不能假装只有一类。empty_files是空标签文件数量这个反而没那么可怕因为空标签代表“这张图没有目标”在训练中可以作为负样本帮助模型减少误检但如果 2859 张图里有几百张全是空的就要考虑训练集的正样本是否过少。bad列表收集所有越界和格式错的标签前 50 条足够判断问题规模。越界的检查我用了xc - w / 2和xc w / 2因为 YOLO 格式存的是中心点和宽高真正的框边界需要换算。允许-0.01到1.01是因为有些标注工具的坐标会有一点点浮点误差超过这个幅度再判定越界能把误报降到最低。做完这一步你已经把数据集从“压缩包”变成了“可验证的训练素材”。接下来就可以正式拆分和训练了。3. 按 YOLOv8 训练自己的数据集的完整命令从 data.yaml 到第一次 mAP数据格式没问题之后剩下的就是在 YOLOv8 上跑通完整训练流程。这一章我会走一遍拆分、配置、训练、看曲线的全过程。前后顺序不要乱尤其是拆分和写配置之间不要跳步。3.1 用脚本把数据划分为 train/val固定随机种子别把同一段视频的帧分进两个集合训练集和验证集必须按照一定比例拆开。2859 张图像这种量级我习惯按 85% 训练、15% 验证来分训练集约 2430 张验证集约 429 张。如果数据量超过 1 万张可以放宽到 90/10少于 1000 张则要优先用交叉验证而不是简单随机切。拆分的脚本split_data.py如下import os import random from shutil import move random.seed(42) images sorted(os.listdir(images)) labels sorted(os.listdir(labels)) # 只保留 image 和 label 都存在的样本 valid [] for img in images: stem img.rsplit(., 1)[0] if stem .txt in labels: valid.append(img) random.shuffle(valid) train_count int(len(valid) * 0.85) for split_name, split_list in [(train, valid[:train_count]), (val, valid[train_count:])]: os.makedirs(fimages/{split_name}, exist_okTrue) os.makedirs(flabels/{split_name}, exist_okTrue) for i, img in enumerate(valid): stem img.rsplit(., 1)[0] label stem .txt split_name train if i train_count else val move(fimages/{img}, fimages/{split_name}/{img}) move(flabels/{label}, flabels/{split_name}/{label}) print(split done: train , train_count, , val , len(valid) - train_count)脚本先是把图像名和标签名做了一次过滤只有两边都存在的样本才进入后续划分避免缺标签文件导致训练中断。random.seed(42)固定随机种子保证每次运行结果一致这样你在不同机器上重现训练时才不会因为数据顺序不同产生莫名其妙的差异。最后把图像和标签同步移动到各自的train和val子目录下。需要特别提醒如果 2859 张图像其实是同一段视频里连续抽取的帧那么随机划分会让同一人的相近帧同时出现在训练集和验证集验证指标虚高部署到新视频上却立刻滑坡。这种情况不能用随机划分要按视频片段或者时间段分桶每一桶整体进入一个集合。常见做法是先按文件名前缀分组然后对组而不是对单张图做划分。你在拆自己的数据时要格外留意这一点。3.2 写 data.yaml路径、类别数和类别名的三种写法YOLOv8 用 YAML 文件描述训练配置最小可用版长这样path: person_ds train: images/train val: images/val nc: 1 names: [person]path指向项目根目录train和val是相对path的路径。有的教程喜欢用绝对路径但绝对路径在不同电脑上会失效我一般先用相对路径等训练命令报错说找不到路径时再手动转绝对路径。nc是类别数量人数据集就写1。names是类别名字列表必须和标签中的类别 id 对应第一个名字对应0第二个对应1。这里如果写成[0, person]或者把nc写成2模型会按两个类别去训练最后 mAP 对不上属于最隐蔽的类型错配。如果你手头的数据集还带了classes.txt可以把它改成这种格式但要注意顺序。classes.txt的行号如果是第 3 行是person那么标签里的类别 id 就是2而不是0。这种“包含子集数据集”中的 session 非常常见不能盲目套[person]。3.3 开始训练关键超参数epochs、batch、imgsz、patience和最小可用命令配置写好后直接跑训练。我用 YOLOv8s 作为起步模型因为 2859 张图不足以支撑 YOLOv8x 这类大模型发挥优势反而更容易过拟合。命令如下yolo detect train \ datadata.yaml \ modelyolov8s.pt \ epochs120 \ imgsz640 \ batch16 \ patience20 \ cacheTrue \ device0modelyolov8s.pt表示从 COCO 预训练权重开始微调。COCO 里人员类别的 id 正好是0和这个人数据集的标签 id 对齐所以预训练模型里关于人体的特征能被直接复用收敛明显快于随机初始化。如果你的标签类别 id 不是0迁移效果会打折扣这也是为什么上一章的类别检查很重要。epochs120对小数据集是比较合理的区间。batch16是 8GB 显存下 YOLOv8s 640 分辨率的安全值显存更大可以提到 32太小就降到 8。imgsz640是标准分辨率如果目标以小目标为主可以试试imgsz960但训练时间会增加近一倍。patience20表示 20 个 epoch 内验证指标没有提升就停止省时间。cacheTrue会把图片提前加载进内存2859 张图完全塞得下避免每次读盘把 GPU 饿死。训练过程中终端里会滚动显示box_loss、cls_loss、dfl_loss和验证集指标。第一次训练的人容易被“loss 数字越来越小”感动实际上要看的是验证集指标是否同步上升。真正的危险信号是train/box_loss一直降而验证 mAP 停滞那说明模型正在背训练集而不是学泛化特征。3.4 训练过程里看什么loss 曲线、验证集 mAP 和过拟合信号训练结束后结果会保存在runs/detect/train/目录下里面有results.csv和weights/best.pt。用一条 Python 命令就能把指标变化读出来import pandas as pd df pd.read_csv(runs/detect/train/results.csv) df.columns [c.strip() for c in df.columns] cols [c for c in df.columns if mAP50 in c or box_loss in c] print(df[[epoch] cols].tail(10))results.csv的列名里经常带着多余的空格所以先strip一遍。mAP50是 IoU 阈值 0.5 时的平均精度对框位置要求相对宽松最能反映“有没有找到人”mAP50-95是多个 IoU 阈值下的平均值要求框更精确实战部署参考价值更高。小数据集上常见的情况是mAP50涨得不错mAP50-95涨得很慢这是正常的不必强求后者一时半会儿追上前者。如果看到训练到第 60 个 epoch 时mAP50还在缓慢爬升但train/box_loss已经低到接近 0我一般会直接停掉把增强参数调强再开一轮。训练做一次不是终点后面还要根据结果回头调数据增强和模型规模。4. 2859 张图太少了数据增强的 4 个关键参数与一个扩展策略小数据集最怕过拟合。2859 张图对 YOLO 来说不算多如果直接硬训模型很容易记住训练集里的固定姿态、固定光照和固定背景换到真实场景就崩。数据增强是解决这个问题最便宜的手段。4.1 先分清“离线增强”和“在线增强”再谈参数离线增强是先把图片翻转、旋转、调亮度生成一批新图片存到磁盘上再拿去训练。它的好处是直观你能看到增强后的图片长什么样坏处是文件量翻倍、训练变慢而且在 YOLOv8 上其实没必要。YOLOv8 把增强内置在训练流里每读取一个 batch 都会在内存里做随机增强同一张图每个 epoch 看到的版本都不一样这种在线增强效率更高也不额外占磁盘。我见过有人先手动把 2859 张图翻转成 5718 张又把亮度调一档变成 8577 张结果训练时间涨了一倍mAP 却只涨了不到一个点。原因就是 YOLOv8 默认的在线增强已经包含了随机的水平翻转、缩放和色彩变换手动离线增强只是把随机过程提前固定下来反而减少了多样性。所以除非你想做特殊的数据集合成否则直接用在线增强参数更划算。4.2 最值得动的 4 个参数fliplr、scale、mosaic 和 hsv在 YOLOv8 的训练命令里大部分增强参数都写在 ultralytics 的默认配置文件中也可以用命令行直接覆盖。我针对人数据集会重点调整这四个参数含义人数据集建议值fliplr水平翻转概率0.5scale图像缩放幅度0.5mosaic四图拼一图概率1.0hsv_h / hsv_s / hsv_v色调、饱和度、明度扰动0.015 / 0.7 / 0.4调用方式是在训练命令后面追加参数yolo detect train datadata.yaml modelyolov8s.pt epochs120 batch16 \ fliplr0.5 scale0.5 mosaic1.0 \ hsv_h0.015 hsv_s0.7 hsv_v0.4fliplr0.5意思是每个 batch 里约一半图片会做左右镜像。行人检测天然适合水平翻转因为人的左右两侧外观差异不大而且标签框不会因为翻转失效。scale0.5允许目标在 0.5 到 1.5 倍之间缩放模拟相机远近变化让模型适应不同大小的行人。这个值不建议再加大超过 0.7 容易把目标切出画面标签中心点跑到图外增强反而制造脏数据。mosaic1.0会把四张训练图随机拼成一张是 YOLOv8 在中小数据集上提升最明显的增强手段。四个图里的目标交错出现等于一张图里塞了多个人和多种背景小目标数量也变多了。缺点是当四个图都很暗或者光照差异过大时模型会被迫学习到不真实的拼接边界。如果训练后期发现验证损失震荡可以在最后 10 个 epoch 把mosaic0.0关掉让模型在真实分布上稳定收敛这是 ultralytics 社区里常见的“退火”做法。hsv_*三个参数控制颜色扰动。人的衣服颜色变化很大增强肤色和衣服颜色能降低对颜色的敏感度。数值太大图片会偏色严重影响标注框的可信度一般保持默认值就够了只有在训练集整体偏暗或者偏亮时才单独调hsv_v。4.3 当增强不够时用拼接、裁剪和伪标签扩数据但不越界如果增强参数调到合理范围后验证集 mAP 还是上不去就该考虑从数据本身找增量。最稳妥的做法是收集更多未标注的现场图片用已经训练好的模型跑一遍推理把置信度大于 0.8 的框作为伪标签加入训练集。这在模型精度已经可用时是快速扩数据的方法但有个硬前提新图片必须和你的应用场景分布一致不能拿网图硬凑否则模型会被伪标签里隐含的错误拖偏。伪标签的具体过滤条件可以这样设conf 0.8且框宽高比在正常人体比例范围内避免把柱状物、牌匾这类竖长物体当成人的误检也学进去。加入伪标签后重新训练要对比加入前后的验证集 mAP如果出现 mAP 下降了就要立刻回滚这批伪标签不要恋战。另一个扩展策略是“负样本补充”。如果你的 2859 张图里基本都有人模型会在背景上产生虚惊误检。这时候额外找几百张没有人的街道、室内背景图放进images目录同时提供一个空的labels文件让模型学会“没有目标时不要输出任何框”。这个操作成本很低但对实际部署的误检率改善非常明显。5. 训练后必做的验证与避坑清单从 loss 很好看但全错到标签越界这一章是我每次做小型自定义数据集都会踩的坑写出来能帮你省掉至少两天的返工时间。5.1 现象 1训练完 mAP 是 0但单独看推理结果似乎有框原因训练过程本身没有报错但data.yaml里的names和标签里的类别 id 对不上。比如标签里的1代表 person但 YAML 里names列表第一个元素是background第二个才是person模型学到的是“类别 1 是人”而验证代码用名称索引去算 mAP两边错位自然得 0。解决办法是回到第 2 章的检查脚本打印所有标签类别 id再看data.yaml中names的顺序。不要靠肉眼猜直接用脚本输出两边的对应关系。5.2 现象 2loss 一路下降但验证集 mAP 长期不涨原因小样本过拟合。模型把训练集里人的姿态、背景、光线都背下来了验证集上稍微变一下就认不出来。解决方向有两个一是把第 4 章的增强参数调强尤其是mosaic和scale二是换更小的模型比如从 YOLOv8s 换到 YOLOv8n。不要以为模型越大越好2859 张图的体量撑不起大模型的参数量小模型反而更容易学到通用的行人特征。还有一个容易忽略的点如果patience设得太小比如小于 10模型还在爬坡就被早停打断了mAP 也会看起来“不涨”。我一般先设patience30跑完一轮再根据实际收敛情况缩小。5.3 现象 3标签里出现中心点越界或宽高为负数原因标注工具导出格式不是 YOLO 格式或者某一步把归一化坐标和像素坐标混用了。最典型的错误是把 x1、y1、x2、y2 的像素值直接当成中心点宽高保存四舍五入后数值远超正常范围。解决办法是在训练前跑第 2 章的check_labels.py把bad列表打印出来的文件单独挑出来重新用 LabelImg 或 Roboflow 导出成 YOLO 格式。如果只有零星几个越界可以写脚本把越界的框裁剪回图像边界再训练如果越界比例超过 5%说明导出过程有系统性问题必须从源头修复。5.4 现象 4训练时显卡利用率只有 40%一个 epoch 跑得很慢原因图片读取和标签解析速度跟不上 GPU 计算速度。2859 张图虽然不大但如果放在机械硬盘上每轮训练都要重新读一遍IO 就拖了后腿。解决办法有两个一是在训练命令里加cacheTrue把图片一次性加载进内存训练速度能提升一倍二是给 dataloader 指定更多workers例如在命令里加workers8让多个进程并行加载。如果你的电脑内存不够 16GB建议只用workers4内存不够时反而会拖慢系统。5.5 现象 5推理时同一个行人被输出两个重叠框原因目标检测后处理阶段通过 NMS 去重但 YOLOv8 默认的 NMS 参数在密集人群场景下会出现漏合并。推理时手动调低iou阈值比如从默认的 0.7 调到 0.5会要求两个框的 IoU 超过 0.5 才合并对密集人群能减少重复框但要注意如果调太低两个真的极近的行人也会被合并成一个。这类 bug 不属于训练数据问题调整后建议重新跑一次验证集 mAP确认精度没掉再部署。以上五条是我实际项目中踩过的坑。它们有一个共同点训练代码不报错不代表训练结果正确。跑完一轮训练后永远要先抽几张图看看预测框画在哪再信指标。6. 用测试图片和视频帧验收模型一套能直接抄的推理与导出命令训练完成后验证集 mAP 只是一个数字最终能不能用还要看真实场景里表现如何。我会用一段极简的 Python 脚本把验证集图片跑一遍保存结果带框的图再人工过目一遍from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict( sourcedata/val_images, conf0.25, iou0.5, saveTrue, save_txtTrue, ) for r in results: boxes r.boxes print(f图像: {r.path}, 目标数: {len(boxes)}, 置信度: {boxes.conf.tolist()})conf0.25是置信度阈值低于它的预测框会被丢掉。在人员检测场景里我一般先用 0.25 看召回也就是漏检少不少如果误检太多再提到 0.4。iou0.5是 NMS 的 IoU 阈值前面避坑章节里说过密集人群可以再调低一点。输出的图片会存到runs/detect/predict/下你要做的一件事就是随机抽几十张盯着看三个点有没有漏人、有没有把树或灯拢当成人、有没有框没有贴住人体。看完图片后再用一条命令在验证集上拿到正式指标yolo detect val modelruns/detect/train/weights/best.pt datadata.yaml splitval这会输出一整套 mAP50、mAP50-95 和每个类别的精度召回数。对比训练日志里的最佳验证指标如果差距很小说明训练过程可复现如果差距大大概率是predict的conf和iou参数与训练时不一致造成的偏差。确认效果后如果要给别的机器或者边缘设备用我会把权重导出成 ONNX 而不是直接拿.pt部署。导出命令同样很直接yolo export modelruns/detect/train/weights/best.pt formatonnx imgsz640导出后得到一个.onnx文件在 Python 里用 ONNX Runtime 加载推理速度和环境兼容性都比原版 PyTorch 权重好。如果部署平台是 NVIDIA 显卡还可以进一步导出成 TensorRT 的 engine 格式吞吐能提升不少但那是另一个话题了。我的习惯是拿到任何新数据集都先随机抽 50 张跑一轮推理把框画出来肉眼扫一遍再谈指标和优化。这一步看起来原始却能把 label 错位、过拟合幻觉和部署场差异一次性暴露出来。希望这套从数据检查到训练验收的流程能帮你在 2859 张图上少走几个弯路。本文还有配套的精品资源点击获取