YOLO格式TinyPerson数据集:COCO转YOLO与YOLOv8小目标训练实战
简介面向计算机视觉与深度学习开发者这份TinyPerson数据集针对小目标检测与人群计数场景设计包含1532张已标注图片省去手工标注和格式整理的麻烦。压缩包内共2000个文件包含1532个txt标签、467个xml标签及1个yaml配置文件整体大小78.54MB其中YOLO txt已按训练、验证、测试集划分完毕可直接放入YOLOv5/YOLOv8等工程训练使用。xml标签便于转换为VOC格式输入其它检测框架配合yaml文件可快速启动训练流程双格式同时覆盖主流检测框架的标注需求。数据集聚焦密集小尺度行人为小目标识别提供高质量监督信号能有效支撑小目标检测算法研究、对比实验与毕业设计也可作为模型鲁棒性评估的基准数据。目前已有2915人学习下载适合需要规范标注数据、希望减少数据预处理成本的深度学习学习者与研究人员。 YOLO格式的TinyPerson数据集最近好几个做检测的朋友都在问这事。这数据集不是新东西但因为它专攻远距离小目标人群检测在实际项目里复用率很高所以隔三差五就有人翻出来重新折腾一遍。我去年在一批无人机低空航拍图像上做行人检测时用过它做预训练踩了不少格式转换和数据清洗的坑今天把这套东西从下载到训练、从踩坑到调优完整捋一遍。1. 小目标人群检测为什么绕不开TinyPerson先交代背景。常规的行人检测数据集比如Caltech、CityPersons、COCO的person类覆盖的场景基本都是行人占据画面较大比例的情况。一旦把检测器搬到开阔海面、无人机俯瞰、监控探头远距离拍摄这类场景行人往往只有几十个甚至十几个像素很多模型精度会断崖式下跌。TinyPerson就是冲这个问题来的它把“远距离小尺寸人群密集”作为核心标注对象。这数据集的原始来源是公开的海滩和野外场景图像图像分辨率高标注框非常小。官方论文里给过一个参考TinyPerson的平均框高大概在20像素以内比常规数据集里动辄上百像素的实例框小一个数量级。这种尺度分布决定了它和COCO、VOC的训练策略不能照搬后面YOLO训练时的anchor设置、输入分辨率、mosaic增强参数都会跟着变。另外TinyPerson的标注是COCO格式的JSON不是YOLO的txt。这点很关键因为网上一搜TinyPerson出来的下载包往往是原始COCO格式而YOLO系训练脚本默认吃的是每张图对应一个txt的格式。很多人卡在第一步不是模型不会调而是数据根本没喂进去。2. 从COCO格式到YOLO格式的完整转换过程2.1 认清三种标注格式的本质区别COCO格式是一个大JSON文件里面通过images、annotations、categories三个数组维护所有信息。每个annotation里有image_id、category_id、bbox四元组[x, y, width, height]以及segmentation等字段。YOLO格式则是一张图片对应一个同名txt每一行是 class_id x_center y_center width height全部归一化到0到1之间。转换的核心就三件事把JSON里的图片ID对应到具体文件名把bbox的xywh原样归一化把category_id映射到连续的class_id。有人在转换后训练时发现全部框都没了或者框的位置偏得离谱基本都是归一化时用了像素值而不是归一化坐标或者图像宽高取错了。2.2 转换脚本的设计思路我直接给出一个可用脚本逻辑核心是用Python读JSON配合PIL或OpenCV拿每张图的宽高。不要用JSON里存的width和height字段去归一化因为部分重新打包的数据集这两项可能为空甚至和实际图像不一致。import json import os from pathlib import Path def coco_to_yolo(coco_json_path, image_dir, output_dir): with open(coco_json_path, r, encodingutf-8) as f: coco json.load(f) # 建立 image_id 到文件信息的映射 image_info {} for img in coco[images]: image_info[img[id]] img # 建立 category_id 到连续 class_id 的映射 categories {} for idx, cat in enumerate(coco[categories]): categories[cat[id]] idx os.makedirs(output_dir, exist_okTrue) # 按图片维度聚合所有标注 annotations_by_image {} for ann in coco[annotations]: image_id ann[image_id] annotations_by_image.setdefault(image_id, []).append(ann) for image_id, anns in annotations_by_image.items(): img image_info[image_id] file_name img[file_name] image_path os.path.join(image_dir, file_name) img_w img.get(width, 0) img_h img.get(height, 0) # 如果JSON里的宽高不可靠用实际图片读取 if not img_w or not img_h: from PIL import Image with Image.open(image_path) as im: img_w, img_h im.size txt_path os.path.join(output_dir, Path(file_name).stem .txt) with open(txt_path, w, encodingutf-8) as f: for ann in anns: if ann.get(area, 0) 0: continue bbox ann[bbox] x, y, w, h bbox x_center (x w / 2) / img_w y_center (y h / 2) / img_h w_norm w / img_w h_norm h / img_h class_id categories[ann[category_id]] f.write(f{class_id} {x_center:.6f} {y_center:.6f} {w_norm:.6f} {h_norm:.6f}\n) # 顺便生成yaml文件 with open(os.path.join(output_dir, tiny_person.yaml), w, encodingutf-8) as f: f.write(path: ./\n) f.write(train: images/train\n) f.write(val: images/val\n) f.write(names:\n) for cat_id, cat in categories.items(): f.write(f {cat_id}: {cat[name]}\n)注意脚本里的几个细节。area小于等于0的标注直接跳过这类标注通常是crowd或ignore类型的残留不参与训练反而更稳。归一化保留六位小数别省YOLO在训练时读txt是按float解析的精度不足会在小目标框上产生几个像素的偏差放在TinyPerson这种小尺寸场景里就是相对误差被放大。2.3 转换后必须做的验证转换完别急着训练先做一轮可视化验证。推荐用OpenCV在图上把txt里的框画出来抽查几十张重点看行人框是否贴近人体轮廓。另一个更快的方法是直接在YOLO训练时开启save_images参数让YOLO自己把GT框画出来。还要检查有没有空标注文件。TinyPerson原始数据里存在少量图像没有任何标注转换后对应的txt就是0字节。YOLOv8默认会跳过空标签并提示WARNING不影响训练但如果这类图像占比太高建议直接剔掉否则训练时每个epoch都会浪费一次无效读取。find labels -name *.txt -size 0 | wc -l如果不想用脚本也可以使用网上开源的COCO转YOLO工具但用的时候一定要确认目标txt格式。不同YOLO版本的标签格式略有差异比如YOLO v5和v8在核心标注格式上是一致的YOLOv5早期版本还兼容过归一化xywh以外的格式后续版本基本都统一了。以官方文档为准不要凭记忆。3. YOLOv8在TinyPerson上的训练配置与参数调整3.1 数据划分与目录组织整理后的数据集结构建议按YOLO惯例来images和labels分开放train/val子目录各一份。tiny_person_yolo/ ├── images/ │ ├── train/ │ ├── val/ ├── labels/ │ ├── train/ │ ├── val/ ├── tiny_person.yamlTinyPerson官方有提供train/val划分的JSON转换脚本里直接按对应JSON分别处理即可。如果自己划分注意一点TinyPerson里同一场景的不同帧是序列连续的随机划分可能导致训练集和验证集出现几乎相同的图像内容测出来的精度会虚高。正确做法是按场景分组划分一个场景的所有帧要么全在训练集要么全在验证集。我用官方JSON划分时没有这问题但如果自己从原始图像切分务必先按场景聚类。3.2 训练参数的核心调整点小目标检测在YOLOv8上最有效的几个调整方向按优先级排列输入分辨率TinyPerson远距离小目标居多把imgsz从默认640提高到960甚至1280很有效。YOLOv8本身是按多尺度训练的设置了imgsz1280后推理时输入大图小目标的特征保留明显更多。mosaic与copy_paste增强小目标场景下mosaic效果很好但mosaic的拼接缩放会把目标压得更小。建议开启mosaic的同时把copy_paste设成0.5以上复制粘贴同一图像中的小目标去做增强比单纯靠mosaic拼四张图的效果更稳定。anchor相关YOLOv8是无anchor的anchor-free架构所以不需要手动调anchor尺寸。如果你的网络是YOLOv5或YOLOv7需要另算anchor这个后面单独说。epoch数TinyPerson单个类别数据量不大从零训练300轮足够收敛。如果是在COCO预训练权重上微调150轮左右就能到平台期。实际跑的时候我给出一组起步参数yolo detect train \ --data tiny_person.yaml \ --model yolov8s.pt \ --imgsz 960 \ --epochs 200 \ --batch 16 \ --mosaic 0.8 \ --copy_paste 0.5 \ --close_mosaic 10 \ --device 0close_mosaic这个参数值得单独说。mosaic增强在最后若干个epoch应该关闭让模型在接近真实分布的数据上做微调否则验证时虽然mAP不错实际部署到真实视频流里会发现小目标漏检率偏高。YOLOv8默认会在最后10个epoch关闭mosaic这里手动指定了10。3.3 小目标分支与检测头选择在YOLOv8里小目标检测比较直接的优化是使用P2检测层。P2层对应输入图像的1/4分辨率特征图更大空间信息更细。YOLOv8官方权重默认从P3开始下采样P2层需要额外在网络结构里加一层可以通过修改yaml文件实现。不过用了P2后推理开销明显增加TinyPerson场景如果是在嵌入式设备上做实时检测需要权衡。有一个更轻量的替代把原始图像切成若干小块分别检测再用后处理合并结果。比如把1080p图像切成四块960x540的重叠块每块独立跑检测最后用NMS合并。这种方式在小目标上效果立竿见影代价是推理时间翻几倍。代码里实现切图检测时需要注意坐标转换。每块的偏移量要记录然后映射回原图坐标NMS前把坐标统一。还有切块边缘的目标可能会被切一半所以块与块之间要有重叠区重叠宽度建议不小于目标最大尺寸的一半TinyPerson目标小一般设50像素够了。4. 训练过程中最容易翻车的三个细节4.1 标签框的边界溢出YOLO标签文件的归一化坐标严格限制在0到1之间但TinyPerson里有些标注框紧贴图像边缘转换时四舍五入可能导致坐标变成1.000001之类的越界值。YOLOv8读取这类标签时会在日志里报Out of bounds warning然后自动clip到有效范围但如果是YOLOv5早期版本这类标签直接会被过滤掉造成漏检目标。处理办法是在转换脚本里多做一步clip同时保留原始txt。如果做数据清洗还可以把超出边界过多的框直接丢弃比如x_center小于0.01或大于0.99的框往往是被误标或截断严重的训练时会给模型带来噪声。4.2 类别数量变化导致的权重加载问题TinyPerson原始类别其实只有一个person但有些人下载的版本里还把face、head这类细粒度标了进来类别数量可能是2或3。如果你的YOLO权重是COCO的80类预训练模型直接改yaml的nc去微调会报最后一层类别数不匹配的错误。解决办法是加载预训练权重时忽略最后一层这在不同框架里写法不同。YOLOv8里最简单的方式是直接用你想用的类别数配置新模型然后调用torch.load手动加载部分权重。如果嫌麻烦也可以从零开始训练TinyPerson数据量不大单类从零训练到可用精度并不难但收敛速度会慢一些。4.3 验证集mAP虚高的判断方法小目标数据集的mAP指标非常容易虚高尤其是TinyPerson这种目标密集且尺寸小的场景。val上mAP50好看但实际用起来漏检严重这往往是因为验证集里标注本身就不全。TinyPerson官方标注里对密集人群有漏标现象如果你拿原标注直接做val模型的误检会被当成正确检测mAP自然高。我常用的做法是抽200张图手动数一遍真实行人数量再用模型跑一遍计算实际召回率。如果召回率明显低于val mAP反映的水平说明数据集标注有偏差需要做人工复核。这一步没人能替你省模型部署到真实场景前务必做。5. 从TinyPerson到自有数据的迁移技巧5.1 预训练权重怎么选把TinyPerson训练好的模型作为预训练权重迁移到自己的场景比从COCO迁移更合适。原因在于TinyPerson和很多远方小目标场景的尺度分布一致。比如我从TinyPerson迁移到无人机航拍行人检测迁移后只用500张标注图微调mAP50就超过了之前用COCO预训练权重训练5000张的效果。具体做法是先下载TinyPerson原始数据转换格式后用YOLOv8s训练一个基础模型保存best.pt。然后用这个权重作为pretrained去训练自己的数据集。yolo detect train \ --data my_dataset.yaml \ --model /path/to/tiny_person_best.pt \ --imgsz 960 \ --epochs 100 \ --batch 165.2 数据增强策略在迁移时的调整迁移训练时mosaic比例可以降一些毕竟TinyPerson预训练已经让模型见过了大量小目标不需要再用强增强去逼模型记住小目标特征。copy_paste可以保留对密集人群场景特别有用。另外如果自有数据是大图建议设定好训练分辨率后统一resize不要一会儿960一会儿1280会导致batch内尺度差异过大迁移训练不稳定。5.3 蒸馏和伪标签的进阶玩法如果自有数据标注很少可以先用TinyPerson预训练模型对未标注数据做一次推理生成伪标签然后选择置信度高的框作为额外训练数据。这种半监督方式在迁移场景里非常实用。我用这个方案做过一次道路监控行人检测只标了300张图用伪标签补充了1200张最终精度比只用300张训练提升了约9个点。做伪标签时要注意类别平衡和置信度阈值通常TinyPerson迁移的模型在自有场景上置信度会偏低阈值设在0.5左右比较合适。同时还要做NMS去重避免同一目标多个框都进训练集。6. 推理时的尺度问题与后处理优化训练完之后推理阶段同样有一套针对小目标的优化空间。直接拿训练时的imgsz去推理是最省心的但实际项目中摄像头输入分辨率往往不是固定值。TinyPerson这类小目标如果输入图像过大建议先做letterbox归一化再推理。letterbox会在图像周围补灰边如果灰边过大可能把原本就小的目标进一步缩小所以推理时如果原图接近正方形我一般不做letterbox直接resize到训练尺寸。还有一个后处理细节小目标密集场景下NMS的IoU阈值要适当放宽比如从默认0.7降到0.5否则密集人群的相邻框容易被抑制掉。代价是可能多出一些误检框结合类别置信度阈值一起调总体漏检率会更低。如果部署在嵌入式设备上FP16量化对小目标的影响比较明显。TinyPerson里很多目标在FP16推理下可能因为特征值精度损失而消失。我用TensorRT FP16跑过一次mAP掉了约3个点换成FP32或INT8后反而更稳。这一点部署前务必实测不要迷信FP16的推理加速。7. 数据集版权与合规使用的提醒TinyPerson数据集的版权条款在官方GitHub仓库里写了主要用于学术研究。用来做商业项目前务必核对许可证条款。很多公开数据集在商用边界上都有严格限制一旦过了授权的边界后续法律问题很麻烦。另外数据集中涉及的人脸和身体图像都是真实拍摄的处理时要遵守隐私保护相关规范不能把原始图像随意公开或二次分发。自己构建数据集时建议把数据来源、标注版本、清洗规则都记录下来。这不仅仅是合规要求也对复现和追踪训练效果有帮助。我自己的项目里每个数据集文件夹下都会放一个README记录转换脚本版本、数据来源、处理日期半年后再看还能知道当时的处理逻辑。最后提醒一句TinyPerson虽然好用但它只覆盖了海滩和野外场景背景相对简单。如果你的目标场景是复杂城市道路或室内迁移时不要指望直接拿来就能用必须配合一定量的自有数据微调。做小目标检测数据集的尺度分布永远比模型结构更决定上限把这套数据准备和训练流程跑通了换成其他小目标数据集也只是换汤不换药。本文还有配套的精品资源点击获取