YOLOv9实战:成人-小孩识别数据集mAP 70.9%分析与提升
简介一套面向目标检测与人群分析的成人与小孩数据集围绕1738张原始图片构建YOLOv9格式标注模型识别率约70.9%适合需要快速验证检测算法的研究者与开发者。压缩包共2000个文件以1738个txt标注文件、261张jpg图片和1个yaml配置文件为主整体大小91.41MBtxt标注遵循YOLOv9坐标格式yaml用于定义类别名称与训练路径目录结构可直接套用到YOLOv9工程。压缩包内文件命名规范统一便于用户自行划分训练集和验证集也适合批量数据增强和预处理。资源特别适合刚入门计算机视觉的学员用来完整走一遍数据加载、模型训练、精度评估的流程有经验者也可利用这些标注数据补充模型训练样本服务安防监控、客流统计等实际项目。目前已有167人学习下载后即可获得图片、标签与配置模板减少人工采集标注的时间帮助聚焦模型调参与部署环节。1. 成人-小孩识别数据集1738张原图与70.9%识别率的真实分量一份1738张的成人-小孩二分类检测数据集识别率卡在70.9%——这个数字放到目标检测任务里属于“能跑到但不够用”的档位。很多团队拿到这种结果后的第一反应是加训练轮数把epoch从200拉到500或者把模型从yolov9-s换成yolov9-e结果mAP纹丝不动。问题通常不在模型容量而在数据集本身的构成、标注一致性和训练配置这三处。这篇文章要做的就是沿着YOLOv9格式标注这条线把这份数据集的成色怎么评估、如何复现训练、70.9%的坑在哪、以及怎么把它往上抬一步步拆开讲清楚。适合手里有一批自采数据但识别率上不去的工程师也适合刚接触YOLO格式、想把数据整理成标准训练集的初学者。2. 这份数据集的成色该看什么类别构成、标注格式与划分策略2.1 1738张原始图片里藏着哪些信息量拿到一份标注好的数据集第一件事不是急着训练而是先做数据体检。1738张原始图片听着不少但放到深度学习里只算小样本。关键在于这1738张的“有效信息量”有多大。我一般会先统计每个类的目标数量而不是图片数量。一张图片里可能同时有3个成人和1个小孩而另一张图只有1个成人。如果按图片算类别看起来平衡按目标框算可能某类数量翻倍。尤其对于“成人”和“小孩”这种二分类样本不平衡会直接导致模型偏向高频类——后期的识别率上不去很多时候是这里埋下的根子。其次要看图片的来源是否单一。如果1738张图片都来自相近场景、相近角度、相近光照那模型学到的其实是场景特征而非目标特征。这类数据训练出来的模型换个环境很容易崩。常见做法是统计图片的分辨率分布、目标框的尺寸分布、以及每张图的目标数量这三个指标能快速判断数据多样性。目标框尺寸尤其关键——在YOLOv9训练中小目标框占比过高时mAP通常会被拖低好几个点。2.2 YOLOv9格式标注到底长什么样YOLOv9延续了YOLO系列的标注规范用的是txt文件每一行代表一个目标。格式是类别ID、归一化中心点x、归一化中心点y、归一化宽度w、归一化高度h。以“成人-小孩”二分类为例假设类别0对应adult类别1对应child一行标注可能长这样0 0.532156 0.441677 0.185232 0.324571 1 0.731452 0.682514 0.112458 0.271238注意这里所有坐标都是相对于图片宽高的比值范围在0到1之间不是像素坐标。比如x_center 0.532156表示目标中心点在图片宽度的53.2%位置。使用归一化坐标的好处是训练时无论输入图片被缩放到640x640还是1280x1280标注都不需要跟着变。每一张图片对应一个同名txt文件。如果图片叫image_001.jpg标注文件就是image_001.txt放在同一目录或分开的labels目录下都可以但yaml配置里要写对路径。如果一个目标跨了图片边界YOLO格式允许坐标超出0到1的范围但我建议标注时尽量避开的这种框它会给训练带来不必要的噪声。2.3 数据集划分训练集、验证集怎么切才不翻车数据划分看起来简单但这里有个常见的坑直接shuffle后按比例切没有检查重复或高度相似的图片。对于1738张这种小规模数据集如果训练集和验证集里出现同一场景的相邻帧验证指标会虚高部署到真实场景立刻现原形。常见做法是确保划分时图片的随机种子固定先清洗再去重再以目标为单位进行stratified划分保证每个类别的目标在训练集和验证集中占比相近。验证集比例我一般留15%到20%。1758张的话取15%大约260张图用于验证。切分前要把标注文件一并移动并且检查有没有“有图无标”或“有标无图”的孤立文件。下面的脚本会做一次完整划分并校验import os import random from shutil import copyfile random.seed(42) img_dir datasets/adult_kid/images label_dir datasets/adult_kid/labels train_img_dir datasets/adult_kid/images/train val_img_dir datasets/adult_kid/images/val train_label_dir datasets/adult_kid/labels/train val_label_dir datasets/adult_kid/labels/val os.makedirs(train_img_dir, exist_okTrue) os.makedirs(val_img_dir, exist_okTrue) os.makedirs(train_label_dir, exist_okTrue) os.makedirs(val_label_dir, exist_okTrue) imgs [f for f in os.listdir(img_dir) if f.endswith(.jpg)] imgs.sort() random.shuffle(imgs) split_idx int(len(imgs) * 0.85) train_imgs imgs[:split_idx] val_imgs imgs[split_idx:] def move_dataset(img_list, img_dst, label_dst): for img in img_list: label img.replace(.jpg, .txt) img_src os.path.join(img_dir, img) label_src os.path.join(label_dir, label) if not os.path.exists(label_src): print(f警告{img} 缺少标注文件 {label}) continue if os.path.getsize(label_src) 0: print(f警告{img} 的标注文件为空跳过) continue copyfile(img_src, os.path.join(img_dst, img)) copyfile(label_src, os.path.join(label_dst, label)) move_dataset(train_imgs, train_img_dir, train_label_dir) move_dataset(val_imgs, val_img_dir, val_label_dir) print(f训练集图片: {len(os.listdir(train_img_dir))}) print(f验证集图片: {len(os.listdir(val_img_dir))})脚本里做了两件容易被忽略的事检查标注文件是否存在检查标注文件是否为空。空标注文件在YOLO训练中会被当成负样本处理数量多了会让模型倾向什么都不检测。随机种子设为42方便复现。如果后续想调整划分比例只需要把split_idx的系数从0.85改成0.8之类的值。3. 自己造一份同类数据采集、标注与格式转换的落地步骤3.1 原始图片采集从手机到监控帧注意三个维度虽然标题里给的是现成数据集但多数人拿到之后会想往里补数据。补数据时采集维度比数量更重要。第一个维度是距离——成人和小孩在近距离特征差异明显但距离拉到15米以上两者的头肩比在视觉上会非常接近这是模型最容易混淆的区间。第二个维度是姿态——小孩蹲着、坐着、被抱着这些形态在训练数据里如果占比太低模型基本学不会。第三个维度是光照——逆光、夜间红外、室内暖光这三类光照下的肤色和轮廓特征差异极大。我自己的习惯是先看现有数据在哪个维度缺失再针对性地补拍。盲目增加图片数量不如补充边界样本。一个常见的翻车场景是数据里全是白天户外场景模型上线后遇到夜间低照度mAP直接掉20个点以上。采集过程中保留原始帧不要先压缩再采集压缩会丢失纹理细节对小孩这种小目标影响尤其明显。3.2 用labelimg标注转YOLO格式完整工作流标注工具上我一般用labelimg做精细标注用CATV或x-anylabeling做预标注再人工修正。labelimg使用起来直接打开图片后选择YOLO格式保存画框标成人或小孩。整个流程里的关键不是画框而是保证框的规则一致。一个需要统一的标准是框从哪里开始到哪里结束。成人目标有的标注习惯框到整个身体含脚有的只框到躯干。小孩目标有的框包含婴儿车有的不包含。这些不一致会让检测器学到的特征产生偏移。遇到这个情况解决方法是重新审视标注规则——我通常只框身体本身不框延伸物。标注完成后labelimg默认会生成与图片同名的txt文件。如果之前用的是Pascal VOC的xml标注批量转换一般用如下脚本import os import xml.etree.ElementTree as ET classes [adult, child] def convert_voc_to_yolo(xml_path, out_dir, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() txt_name os.path.basename(xml_path).replace(.xml, .txt) with open(os.path.join(out_dir, txt_name), w) as f: for obj in root.iter(object): name obj.find(name).text if name not in classes: continue cls_id classes.index(name) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h f.write(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n) xml_dir annotations/xml txt_dir annotations/yolo os.makedirs(txt_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue img_name xml_file.replace(.xml, .jpg) img_w 1920 img_h 1080 convert_voc_to_yolo( os.path.join(xml_dir, xml_file), txt_dir, img_w, img_h )这个脚本假设图片统一为1920x1080。如果图片尺寸不统一需要读图片的实际宽高再传入否则坐标会全部错位。转换完成后建议抽查几十个txt手动对比目标位置是否与图片内容一致。标注坐标转换的坑不在转换代码本身而在宽度和高度信息是否真实对应。3.3 CVAT与x-anylabeling自动标注后如何对齐格式如果1738张图片全靠人工标注熟练工也需要两天左右。更高效的做法是用自动标注模型先跑一遍再人工修正。CVAT支持导入已有模型做自动标注导出时选择YOLO格式即可拿到txt。x-anylabeling则集成了多种预训练模型标注界面会直接显示预测框确认后保存。自动标注有它的局限性。成人-小孩检测这类任务预训练模型可能在小孩类别上召回率偏低漏标率比较高。漏标的框在人工审查时很容易被忽略最终训练出来的模型对小孩的召回会比成人差一截。强烈建议用脚本统计两类目标的框数比如成人2000框、小孩1200框如果比例偏离这个水平回头去查是不是漏标。格式对齐这一步也容易出现“看似成功、实际失败”的情况。CVAT导出YOLO格式得到的txt坐标已经是归一化的可以直接用。x-anylabeling输出的是JSON格式需要先转成YOLO格式。转换时同样的坑——确认像素坐标除以的是原图宽高不是JSON里某个resize后的尺寸。4. 训练复现与70.9%识别率排查参数设置与避坑记录4.1 用YOLOv9训练成人-小孩二分类的最小配置YOLOv9的训练入口和YOLOv5系列很接近核心是先写好数据集的yaml配置。下面是一个可以直接用的data配置train: datasets/adult_kid/images/train val: datasets/adult_kid/images/val nc: 2 names: [adult, child]train和val指向图片目录YOLO会自动在同级目录下找labels文件夹。nc是类别数names是类别名顺序必须与标注txt里的类别ID一一对应。如果names列表里的顺序和标注时定义的顺序不一致模型训练时不会报错但推理结果的类别会互换——这种错误隐蔽且致命。训练命令通常长这样python train.py \ --data adult_kid.yaml \ --weights yolov9c.pt \ --epochs 300 \ --batch 16 \ --imgsz 640 \ --patience 50参数说明--weights yolov9c.pt使用预训练权重对小数据集收敛帮助很大--batch 16在单张24G显存的卡上刚好能跑显存不够降到8--imgsz 640是YOLO系列的默认训练尺寸如果数据里小目标多可以考虑提高到768但训练时间会显著增加--patience 50表示验证指标连续50轮不提升就提前结束防止过拟合。1738张图、2类目标、训练300轮整个过程在单张消费级显卡上大约需要2到3小时。训练完成后最佳权重一般存为best.pt用它做推理而不是用last.pt。我在实际项目中发生过多次忘换权重导致推理结果不对的情况这类细节建议形成一个固定习惯——推理只认best.pt。4.2 70.9%这个数字背后mAP还是准确率含义完全不同标题里的“识别率在70.9%”在不同语境下可能指三种完全不同的指标mAP0.5、mAP0.5:0.95或分类准确率。很多团队汇报时说的“识别率”往往是mAP0.5也就是IoU阈值0.5下的平均精度均值。这个数值对定位精度的要求很低只要预测框和真实框交集过半就算对。如果70.9%是mAP0.5那这个模型几乎处于不可用状态。正常训练的YOLOv9二分类模型即使数据量不大mAP0.5也应该到85%以上。真正需要警惕的是另一个可能70.9%是mAP0.5:0.95。这个指标对定位和分类都更严格数值天然比mAP0.5低10到20个点。如果记录的是这个指标那模型性能反而不算太差。排查时我一般会同时看验证集上的混淆矩阵和PR曲线。混淆矩阵能看出误差方向——成人被预测成小孩还是小孩被预测成成人。两种误差的解决路径完全不同前者往往是特征重叠、距离远导致的区分度不足后者往往是小孩样本量不足模型对它的表达不够。PR曲线的曲线下面积如果主要损失在高召回区间说明漏检居多对应置信度阈值设置不够合理。4.3 数据量不足时的三个必调参数数据量只有1738张时默认训练参数不一定是最优选择。第一个必调的是mosaic增强的概率。YOLOv9默认使用mosaic增强它把四张图拼成一张训练。小数据集上mosaic能有效扩充样本多样性但如果数据里目标尺寸跨度大mosaic会让小目标变得更小反而降低召回。我一般会先把mosaic概率设为1.0跑一轮观察验证集mAP再降到0.5对比一轮。第二个必调的是warmup轮数。默认的warmup_epochs是3.0这个值在大数据集上够用但数据少时模型容易在前期剧烈震荡。把warmup_epochs提高到5或6让学习率更平缓地爬升能明显改善收敛稳定性。第三个需要关注的是lr0初始学习率默认0.01小数据集上可以降到0.005权重更新更保守减少前期在错误方向上走太远。这三个参数不需要同时都改。一个稳妥的顺序是先只调mosaic概率对比一轮确认没有过拟合后再调warmup和lr0。一次只改一个变量才能知道哪一步真正起作用。4.4 避坑记录训练成人-小孩模型常见的5个坑坑一标注框覆盖范围不一致现象是mAP卡在70%左右上不去且主要在成人类别上表现差。原因是部分标注框框到了整个人体部分只框到头肩训练时模型学到的目标边界是模糊的。解决方法是统一标注规范重新检查并修正覆盖范围不一致的框通常修正100到200个框就能看到mAP明显回升。坑二验证集图片与训练集有重复现象是训练时验证mAP很高但实际部署效果差得一塌糊涂。原因是数据划分前没有做去重同一场景的不同帧被分别分到了训练集和验证集。解决方法是先计算图片的感知哈希把相似度高的图片归到同一组整组划分不要按单张划分。坑三类别样本不平衡现象是小孩类别的召回率显著低于成人。原因是成人目标数量可能是小孩的两倍以上模型天然偏向学高频类。解决方法是先把两类目标框的数量统计出来如果严重失衡要么补充小孩样本要么在数据加载时对小孩类别做过采样。通过简单的复制粘贴来增加样本也能起到一定作用但效果不如重新采集。坑四推理尺寸和训练尺寸不一致现象是验证指标还行但视频流推理时小目标漏检严重。原因是训练用了640x640推理时为了省显存把输入压到了416x416小目标在这个尺寸下只有几个像素。解决方法是推理时保持imgsz与训练一致至少不要低于训练尺寸。用TensorRT加速时同样要检查输入尺寸是否匹配。坑五把早停的best.pt当成最终模型现象是训练过程中best.pt在某个epoch后就没再更新但训练还在继续。原因是patience设得太大训练早停在已经过拟合的模型上。我一般在数据量小的情况下把patience设为30宁可少训一点也不让模型在验证集上退化太多。早停不是越晚越好它只是防止过拟合的手段。5. 把识别率往上抬的三个验证技巧验证集上的mAP不是终点真实场景里的漏检和误检才是评估模型价值的标准。第一个技巧是把验证集的预测结果按置信度排序阈值设高比如0.6筛一遍再设低比如0.3筛一遍观察漏检目标里有多少是小孩、有多少是远距离小目标。这一步能直接定位模型的能力边界。第二个技巧是用混淆矩阵指导数据补充方向。如果矩阵显示“成人误判为小孩”的格子数值偏高说明需要补充成人远距离、背光、遮挡场景的样本如果“小孩误判为成人”偏高则要补充小孩蹲姿、坐姿、与成人同框的样本。数据补充要有方向盲加数据最后都会反馈在训练时长和标注成本上。第三个技巧是调整置信度阈值来匹配业务需求。如果把漏检看得比误检严重阈值可以降到0.3让模型多输出一些候选框靠后续逻辑过滤如果误检会造成更大麻烦阈值可以提高到0.6模型只输出高置信度的目标。这个阈值不需要重新训练只是推理阶段的一个参数可以快速对比效果。我自己的习惯是每次调完一轮训练都把三个指标记录下来验证集mAP0.5、验证集mAP0.5:0.95、以及硬阈值下的误检数。只看前两个会漏掉实际场景里的细节问题。数据量只有1738张时与其反复调参不如花时间把标注质量和数据多样性做扎实这两件事对识别率的影响远超模型结构的选择。希望这些经验能帮到你。本文还有配套的精品资源点击获取