
简介这份成人与小孩图像数据集面向计算机视觉入门与目标检测实践者尤其适合正在做YOLO系列模型训练、需要二分类人物检测样本的开发者与学习者。数据集共1738张原始图片全部按yolov9格式完成标注可直接投入训练流程省去自行标注与格式转换的时间成本。压缩包内文件总数约2000个以1738个txt标注文件、261个jpg图像文件及1个yaml配置文件为主yaml用于定义类别与数据路径txt对应每张图的边界框标注jpg为原始图像整体包大小约91.41MB结构规整便于快速接入训练脚本。该数据集在成人与小孩两类目标上的识别率约为70.9%可作为基线参考帮助读者评估模型表现并针对性调优。目前已有167人学习下载适合用于课堂实验、课程设计或轻量级人物检测项目的样本积累与对比实验。1. 1738 张图、70.9% 识别率这个成人和小孩数据集到底能不能用手里只有 1738 张原始图片标注成 yolov9 格式官方给的识别率是 70.9%任务是区分成人和小孩——这就是这个数据集最真实的样子。很多人第一次看到「70.9%」会本能地皱眉觉得太低但如果你真在边缘设备或者家庭场景里跑过人体检测就会知道这个数字背后其实藏着一个很具体的取舍数据量小、类别语义接近、目标尺度跨度大能到 70.9% 已经说明标注质量在线剩下的差距要靠训练策略和场景约束去补而不是靠堆数据。这篇文章面向三类人手里已经拿到这份数据集、想直接跑通 yolov9 训练的人正在评估「要不要用这份数据做二次标注和扩充」的人以及做家庭看护、儿童区域预警、商场客流分析这类需要区分成人和小孩的从业者。我会把数据集的检查方法、yolov9 格式的目录组织、训练参数怎么设、70.9% 这个基线怎么复现和往上抬以及最容易翻车的几个点按能照着做的顺序讲清楚。数据集不是黑匣子70.9% 也不是终点关键是你知不知道它卡在哪。2. 先搞清楚这份数据集的结构和标注质量2.1 yolov9 格式到底长什么样yolov9 沿用 YOLO 系列的标注约定每张图片对应一个同名.txt文件每行一个目标格式是class_id x_center y_center width height后四个值都是归一化到 0~1 的相对坐标。这份数据集是二分类常见约定是0表示 adult、1表示 child但不同来源可能反过来拿到手第一件事就是确认类别顺序别默认。标准目录结构一般长这样dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml是训练入口内容大致如下path: /home/user/dataset train: images/train val: images/val test: images/test nc: 2 names: 0: adult 1: child这里nc是类别数names的顺序必须和标注文件里的class_id严格对应。我见过太多人训练 loss 一直不降最后发现是names写反了模型把 adult 学成 child这种坑没有任何报错只能靠可视化排查。2.2 用脚本体检类别分布、框尺寸、坏标注1738 张图不算多但人工一张张看也不现实写个脚本先做体检。下面这段代码统计每个类别的框数量、宽高分布并找出越界或零面积的坏标注import os import glob from collections import Counter label_dir dataset/labels/train img_dir dataset/images/train cls_counter Counter() bad_files [] wh_list [] for txt in glob.glob(os.path.join(label_dir, *.txt)): with open(txt) as f: lines [l.strip() for l in f if l.strip()] # 图片存在性检查 img_name os.path.splitext(os.path.basename(txt))[0] .jpg if not os.path.exists(os.path.join(img_dir, img_name)): bad_files.append((txt, missing image)) continue for line in lines: parts line.split() if len(parts) ! 5: bad_files.append((txt, field count)) continue c, x, y, w, h int(parts[0]), *map(float, parts[1:]) cls_counter[c] 1 wh_list.append((w, h)) # 越界或退化框 if not (0 x 1 and 0 y 1 and 0 w 1 and 0 h 1): bad_files.append((txt, fout of range {line})) print(类别分布:, cls_counter) print(坏标注数量:, len(bad_files)) for b in bad_files[:10]: print(b) # 框尺寸粗统计 if wh_list: ws [w for w, _ in wh_list] hs [h for _, h in wh_list] print(f宽 min/mean/max: {min(ws):.3f}/{sum(ws)/len(ws):.3f}/{max(ws):.3f}) print(f高 min/mean/max: {min(hs):.3f}/{sum(hs)/len(hs):.3f}/{max(hs):.3f})逻辑说明先遍历所有 label 文件逐行解析五个字段统计类别计数同时检查对应图片是否存在、坐标是否越界。参数上0 w 1这个判断很关键因为归一化后宽高理论上不会超过 1超过基本就是标注工具导出时没做归一化。跑完你会得到两个关键信息类别是否严重不平衡以及框的尺度分布。如果 child 的框普遍偏小比如宽高均值低于 0.1那 70.9% 的识别率里小孩漏检大概率是主因。2.3 70.9% 这个基线是怎么算出来的识别率 70.9% 通常指 mAP0.5 或者某个 IoU 阈值下的准确率但不同人报的口径不一样。你要复现这个数字必须固定三件事验证集划分、IoU 阈值、置信度阈值。常见做法是按 8:1:1 划分 train/val/test用 mAP0.5 作为主指标。如果对方用的是 mAP0.5:0.95那 70.9% 其实相当高如果是简单 accuracy那参考价值就有限。我的建议是拿到数据后自己重新划一次验证集用同一套评估脚本跑一遍得到你自己的基线。别直接信别人报的数字因为划分方式一变结果能差 5 到 10 个点。这一步做完你才知道后面调优到底有没有效果。3. 用 yolov9 把这份数据集跑起来3.1 环境准备和权重选择yolov9 官方仓库提供了几个规格从轻到重大致是 yolov9-t、yolov9-s、yolov9-m、yolov9-c、yolov9-e。1738 张图属于小数据集直接上 yolov9-e 基本必过拟合我一般从 yolov9-s 起步显存够的话用 yolov9-c 做对比。环境上PyTorch 版本要和 CUDA 对齐别用太新的 torch 配旧驱动这类玄学问题排查起来很费时间。# 克隆官方仓库示例按你实际拿到的代码包为准 git clone yolov9-repo cd yolov9 pip install -r requirements.txt # 确认 GPU 可用 python -c import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))如果torch.cuda.is_available()返回 False先别急着改代码八成是 CUDA 和 torch 版本不匹配。用nvidia-smi看驱动支持的 CUDA 上限再去 torch 官网找对应版本这一步没有捷径。3.2 训练命令和关键参数yolov9 的训练入口通常是train.py核心命令如下python train.py \ --weights yolov9-s.pt \ --data dataset/data.yaml \ --img 640 \ --batch 16 \ --epochs 100 \ --device 0 \ --workers 8 \ --patience 20 \ --name adult_child_s参数逐个说--img 640是输入分辨率1738 张图里如果小孩目标偏小可以提到 768 甚至 896但显存和速度会明显下降--batch 16在 8G 显存上跑 yolov9-s 基本安全爆显存就降到 8--epochs 100配合--patience 20意思是 20 轮验证指标不涨就早停小数据集上这个设置能省不少时间--workers 8是数据加载线程CPU 核少就降到 4。训练过程中重点盯三个东西box_loss是否稳定下降、mAP0.5在第几轮开始平台、验证集 loss 有没有反弹。如果训练 loss 一直降但验证 mAP 卡在 0.6 左右不动基本就是过拟合前兆这时候要么加数据增强要么换更小的模型。3.3 数据增强怎么配才不帮倒忙yolov9 默认开了 mosaic、mixup、随机缩放等增强。对成人和小孩这种语义接近的类别mosaic 用 4 张图拼接会引入大量小目标反而可能让小孩更难学。我的经验是小数据集上把 mosaic 关掉或者降低概率重点开随机缩放和水平翻转。# 在 hyp 配置里调整示例字段 mosaic: 0.3 # 默认 1.0小数据集降到 0.3 mixup: 0.0 # 语义接近的类别不建议开 scale: 0.5 # 随机缩放幅度 fliplr: 0.5 # 水平翻转概率 hsv_h: 0.015 hsv_s: 0.7 hsv_v: 0.4逻辑上mosaic 和 mixup 是为大数据集设计的靠拼接增加样本多样性1738 张图本身多样性不足再拼只会让单张图里的目标更碎。关掉之后你会发现验证 mAP 反而更稳。这个结论不是绝对的但值得作为第一组对比实验。4. 70.9% 往上抬调优和排查的实操路径4.1 先定位是漏检还是误分类70.9% 这个数字本身不告诉你问题在哪。你需要把验证集预测结果可视化分两类看adult 被漏检、child 被漏检、adult 被误判成 child、child 被误判成 adult。用下面的脚本导出混淆矩阵的原始统计from ultralytics import YOLO import numpy as np model YOLO(runs/train/adult_child_s/weights/best.pt) results model.val(datadataset/data.yaml, splitval) # 打印每类 AP for i, name in enumerate(results.names.values()): print(f{name}: AP0.5 {results.box.ap50[i]:.4f})如果 adult 的 AP 明显高于 child说明小孩样本学得不够方向是补小孩数据或提高小目标分辨率如果两类 AP 接近但都低说明整体特征区分度不够方向是换更大模型或加训练轮次。这一步是分水岭方向错了后面全白费。4.2 提高输入分辨率对小目标的影响小孩在画面里往往比成人小尤其是远景。把--img从 640 提到 896小目标召回通常能涨 3 到 8 个点。代价是显存翻倍、训练时间增加。如果显存不够可以用--batch 8配合梯度累积或者只对含小目标的图做高分辨率采样。python train.py \ --weights yolov9-s.pt \ --data dataset/data.yaml \ --img 896 \ --batch 8 \ --epochs 120 \ --device 0 \ --name adult_child_s_896跑完和 640 的基线对比 mAP0.5 和 mAP0.5:0.95如果只有前者涨、后者不涨说明框定位精度没提升只是召回变好这时候要回头检查标注框是否贴合。4.3 类别不平衡的处理统计完类别分布如果 adult 和 child 的框数量比超过 3:1就要处理不平衡。常见做法有三种对 child 类别过采样、在 loss 里给 child 更高权重、或者用 focal loss 替代默认分类损失。yolov9 默认没有直接暴露类别权重参数最省事的是过采样——把含 child 的图片在训练列表里重复一份。import os, random img_dir dataset/images/train child_imgs [] for txt in os.listdir(dataset/labels/train): with open(fdataset/labels/train/{txt}) as f: if any(line.startswith(1 ) for line in f): child_imgs.append(os.path.splitext(txt)[0] .jpg) # 生成重复列表追加到训练集 with open(dataset/train_oversample.txt, w) as f: for img in os.listdir(img_dir): f.write(fdataset/images/train/{img}\n) for img in child_imgs: f.write(fdataset/images/train/{img}\n)逻辑说明先扫描所有 label找出含 class_id 为 1child的图片然后在训练列表里再写一遍。参数上重复倍数控制在 1 到 2 倍之间太多会让模型对 child 过拟合。这个做法简单粗暴但在小数据集上往往比调 loss 更快见效。5. 避坑这份数据集最容易翻车的 5 个地方5.1 类别顺序写反训练全程无报错现象训练 loss 正常下降但验证时 adult 和 child 的预测几乎对调mAP 看着还行实际全错。原因data.yaml里names的顺序和标注文件里的class_id不一致模型学到的映射是反的。解决拿一张图用标注工具打开确认 adult 对应的 id再核对data.yaml改完重新训练。这个坑没有任何日志提示只能靠可视化发现。5.2 图片和 label 文件名不匹配现象训练时提示找不到 label或者某些图被静默跳过实际参与训练的图片远少于 1738 张。原因图片是.jpglabel 是.txt但有些文件名带空格、中文或大小写不一致。解决用 2.2 的体检脚本跑一遍把所有 missing image 和 missing label 列出来统一重命名。别小看这个丢几十张图对 1738 的规模来说就是几个点的差距。5.3 验证集里混入了训练集图片现象验证 mAP 虚高换一批新图测试直接掉 20 个点。原因划分 train/val 时用了随机划分但没去重同一张图或同一段视频的相邻帧同时进了训练和验证。解决如果数据来自视频抽帧按视频源划分而不是按帧随机划分。这个坑在家庭监控类数据里特别常见。5.4 置信度阈值没调误检一堆现象模型 mAP 看着有 0.7实际部署时画面上到处是框。原因评估用的是默认置信度阈值部署时没调低置信度的误检全放出来了。解决在验证集上画 precision-recall 曲线找 F1 最大的阈值通常比默认 0.25 高不少。对成人和小孩这种场景宁可漏检也别误检阈值可以再往上提。5.5 直接拿 70.9% 当部署指标现象实验室 70.9%装到设备上实际可用率感觉只有一半。原因70.9% 是在特定验证集上的 mAP和真实场景的分布不一致光照、遮挡、拍摄角度都会让指标掉。解决部署前一定用现场采集的图做一次测试别信实验室数字。如果现场掉得厉害优先补现场数据做微调而不是继续在原有数据集上刷点。6. 把 1738 张用到极致半自动标注扩充和现场微调1738 张图的天花板很明显想突破 70.9%最实际的路径不是换模型而是扩数据。但人工从零标 1000 张成人和小孩的图成本不低。我的做法是半自动用当前 70.9% 的模型去推理新采集的未标注图片把高置信度的预测导出成预标注人工只做修正。from ultralytics import YOLO import os model YOLO(runs/train/adult_child_s/weights/best.pt) src_dir new_images out_dir pseudo_labels os.makedirs(out_dir, exist_okTrue) for img in os.listdir(src_dir): results model.predict(os.path.join(src_dir, img), conf0.6, iou0.5) lines [] for box in results[0].boxes: c int(box.cls) x, y, w, h box.xywhn[0].tolist() # 归一化中心宽高 lines.append(f{c} {x:.6f} {y:.6f} {w:.6f} {h:.6f}) with open(os.path.join(out_dir, os.path.splitext(img)[0] .txt), w) as f: f.write(\n.join(lines))逻辑说明conf0.6是只保留高置信度预测宁可少标也别引入太多噪声iou0.5控制 NMS 合并程度。导出的预标注必须人工过一遍重点看 child 的小框有没有漏。参数上置信度阈值可以按场景调家庭室内场景 0.6 比较稳室外远景可以降到 0.5 多召回一些。扩充完之后用「原 1738 张 新标注数据」一起微调学习率调小到原来的十分之一训练 20 到 30 轮就够。我一般会保留一份原始权重的备份新数据如果引入噪声导致指标下降还能回滚。这套流程跑下来把识别率从 70.9% 抬到 80% 以上是现实的但前提是新增数据要覆盖原数据集缺失的场景——比如不同光照、不同拍摄高度、小孩的侧脸和背影。最后说个我自己的习惯每次训练完我都会把验证集里置信度在 0.3 到 0.6 之间的样本单独挑出来看一遍。这批「模棱两可」的样本往往就是下一轮标注的重点也是模型真正卡住的地方。70.9% 不是这份数据集的终点它只是告诉你哪些场景还没学好。希望帮到你。本文还有配套的精品资源点击获取