简介:TinyPerson数据集是专为小目标检测设计的图像数据集,共1532个样本,全部提供VOC xml与YOLO txt双格式标注,可直接服务于YOLO系列算法的小目标检测训练,适合计算机视觉研究人员、算法工程师及相关专业学生使用。资源包共2000个文件,包含1532个txt标签文件、467个xml标注文件及1个data.yaml配置文件,共78.69MB;其中YOLO txt已按训练集、验证集、测试集划分完毕,并配备data.yaml,可无缝接入YOLO训练流程,省去繁琐的数据转换与配置工作。数据集聚焦于小型人物目标,标注精细,对安防监控、无人车、机器人视觉等领域的小目标识别任务具有实用价值,尤其适用于人群计数、行为分析等场景。已有736人学习下载,适合希望快速获取高质量小目标检测数据并开展模型训练与评估的开发者。
1. 从TinyPerson数据集说起:小目标检测的硬骨头
做目标检测的朋友应该都有这种感受:大目标怎么测都顺手,一旦遇到小目标,模型就像老花眼一样,漏检、误检轮着来。TinyPerson数据集就是专门冲着这个问题来的,它收录了大量远距离、低分辨率的人体目标,很多目标在整张图里只有几个像素。这类场景在安防监控、无人机航拍、体育赛事直播里太常见了,所以这个数据集在行业内口碑一直不错。
我最初接触TinyPerson,是在做一个高空巡检项目,需要在无人机视角下检测人员目标。常规COCO预训练权重一上来,小目标几乎全部漏掉,mAP惨不忍睹。后来换成TinyPerson数据集做微调,效果立竿见影。更关键的是,这套数据集的标注本来就是公开的,而且有人将其整理成了YOLO格式,拿来就能直接训练,省去了大量转换格式的体力活。今天这篇博文,我就结合自己跑通TinyPerson + YOLO的完整经历,把数据集特点、格式转换、训练参数、常见坑一次说清楚。
内容适合正在做小目标检测、想用YOLO微调提升精度的朋友,也适合刚接触数据集标注格式转换的新手。我会把每一步的“为什么”讲透,而不是扔给你一堆命令就完事。
2. 数据集深挖:TinyPerson为什么能打
2.1 它到底特殊在哪
TinyPerson数据集最早由远距离人体检测研究公开,图像主要采集自海岸、海边、山地等开放场景,人员目标密集且尺度极小。和COCO、VOC这类常规数据集相比,它的核心特点是:目标宽高通常在20像素以下,甚至只有5到10像素。传统检测器在训练时容易把这些小目标当成噪点忽略掉,而TinyPerson正是逼着模型去学会捕捉微弱的纹理和轮廓信息。
数据量方面,官方版本包含数百张高分辨率图像,标注了数以万计的人体实例。虽然绝对数量和COCO的20万张没法比,但它的“小目标密度”极高。一张4096x2160的大图里可能分布上百个人,每个人都只占很小一块区域。这个分布特点决定了它特别适合做小目标检测的专项训练,也适合用来验证YOLO系列在不同分辨率下的性能差异。
2.2 为什么YOLO格式这么受欢迎
YOLO格式本质上就是一个txt文件,每行代表一个目标,格式是:class_id x_center y_center width height,其中四个坐标都是相对于图片宽高的归一化值。这种格式的最大好处是:不依赖图像尺寸,缩放图片后标签依然有效,训练时省去了很多预处理。而且Ultralytics YOLO的代码原生支持这种格式,数据准备好之后,直接改一个yaml文件就能开始训练。
对比一下COCO格式(JSON),如果你用过Labelbox或者CVAT标注工具,导出的往往是COCO格式,那你就得写脚本把JSON里的bbox字段转换成归一化的xywh。这个转换本身不难,但特别容易出错,比如COCO的bbox是[x, y, width, height],而YOLO需要中心点坐标,很多人在这里踩坑。所以TinyPerson数据集如果直接提供YOLO格式,相当于是帮你把最脏最累的活干掉了。
3. 动手准备:把TinyPerson整理成YOLO能吃的格式
3.1 目录结构这样建最省心
不管你是从官方源下载原始标注,还是直接获取YOLO格式的版本,建议先统一目录结构。
dataset/ ├── images/ │ ├── train/ │ ├── val/ ├── labels/ │ ├── train/ │ ├── val/ ├── data.yaml把所有训练图片放到images/train,对应标签放到labels/train。图片和标签的文件名要严格一一对应(不带后缀),否则Ultralytics会直接跳过这些样本。我见过不少初学者把标签文件名写错,或者一张图漏标一个txt,训练时silence不说,mAP还莫名低,排查半天才发现是数据没对齐。
3.2 从COCO或其他格式转YOLO格式
如果你手里的TinyPerson是COCO标注,别慌,转格式的核心就三件事:读JSON、算坐标、写txt。
COCO标注中目标框是bbox: [x, y, width, height],左上角坐标和宽高。要转成YOLO的x_center y_center width height,公式如下:
x_center = (x + width / 2) / image_width y_center = (y + height / 2) / image_height norm_width = width / image_width norm_height = height / image_height我用Python脚本处理过一次,关键代码长这样:
import json import os def convert_coco_to_yolo(coco_json, img_dir, label_dir): with open(coco_json, 'r') as f: data = json.load(f) images = {img['id']: img for img in data['images']} categories = {cat['id']: idx for idx, cat in enumerate(data['categories'])} # 初始化每个图片的标签列表 img_labels = {img_id: [] for img_id in images.keys()} for ann in data['annotations']: img_id = ann['image_id'] img_info = images[img_id] img_w = img_info['width'] img_h = img_info['height'] cat_id = ann['category_id'] cls_id = categories[cat_id] x, y, w, h = ann['bbox'] # 防止越界 w = min(w, img_w - x) h = min(h, img_h - y) x_center = (x + w / 2) / img_w y_center = (y + h / 2) / img_h nw = w / img_w nh = h / img_h # 过滤掉完全在图像外的目标 if x_center < 0 or x_center > 1 or y_center < 0 or y_center > 1: continue img_labels[img_id].append(f"{cls_id} {x_center:.6f} {y_center:.6f} {nw:.6f} {nh:.6f}") for img_id, lines in img_labels.items(): img_info = images[img_id] img_name = img_info['file_name'] label_name = os.path.splitext(img_name)[0] + '.txt' with open(os.path.join(label_dir, label_name), 'w') as f: f.write('\n'.join(lines))这里有几个容易忽略的细节:
- 类别ID一定要从0开始。COCO原始
category_id可能是1到80,YOLO要求从0起连续编号,所以必须做一次映射。 - 转换时要做越界保护。有些标注框会超出图像边界,虽然图上看不出来,但归一化后坐标会大于1,训练时YOLO会报错或者直接过滤,影响数据量。
- 空标签文件必须存在。如果一张图没有目标,也要生成一个空txt,否则训练时容易报列表越界。
3.3 配置文件这么写
data.yaml是YOLO训练的入口,内容很简单:
path: /your_absolute_path/dataset train: images/train val: images/val nc: 1 names: ['person']注意path尽量写绝对路径,避免相对路径在不同环境下跑不通。nc要和类别数一致,names列表里的顺序必须和标签txt里的class_id对应。
4. 训练实操:让YOLO在小目标上不再“瞎”
4.1 环境搭建和基础命令
我用的是Ultralytics YOLOv8,安装太简单了,一行命令:
pip install ultralytics如果要用GPU训练,提前装好CUDA和PyTorch。然后直接跑:
yolo train data=data.yaml model=yolov8s.pt epochs=100 imgsz=640 batch=16这里model=yolov8s.pt用的是COCO预训练权重,在小目标上已经有一定基础,再用TinyPerson微调,收敛速度快很多。如果你完全从零训练,也可以换成yolov8s.yaml,但对小目标检测来说,预训练权重基本是必需品,纯随机初始化很难训出理想效果。
4.2 针对小目标调整的关键参数
如果你直接跑默认参数,大概率会失望,因为TinyPerson里大量目标在640x640输入下只有几个像素,YOLO的下采样倍数太高(V8是32倍),特征图上一个网格对应原图32x32区域,小目标很容易被“平均”掉。这里有三个我实测有效的调整方向:
第一,增大输入分辨率。把imgsz从640调到1280甚至1536,小目标在特征图上占的像素数一下子多出4倍。代价是显存占用飙升,训练速度变慢。我的实践是先用640预训练几个epoch,再在1280上微调,这样既省时间,又能提升精度。如果你的显卡显存不够,可以降低batch,或者用rect=True按比例统一边长,尽可能利用显存。
第二,使用SAHI切片辅助。SAHI的意思是Slicing Aided Hyper Inference,简单说就是把原图切成多张小图,分别检测再合并结果。在推理阶段特别有效。训练阶段也可以在数据加载时自动切片,Ultralytics的ultralytics.data里没有内置SAHI,但我自己写过一个预处理脚本:先把大图按固定步长切块,过滤掉目标过小的区域,再送入YOLO训练。实测mAP能提升5个点以上。
第三,数据增强不要开太猛。YOLO默认的hsv_h、hsv_s这些色彩增强对小目标影响不大,但scale如果设得太大,会把小目标缩到几个像素甚至消失,导致标签失效。我建议把scale限制在0.3到0.5,mosaic可以保留,因为它能增加目标上下文信息,但mixup对小目标不太友好,容易把目标搅糊,可以关掉。
4.3 训练过程的监控与判断
训练时不要只盯着loss曲线,YOLO每个epoch还会输出box_loss、cls_loss、dfl_loss以及mAP50、mAP50-95。你需要重点看mAP50-95,因为它对小目标更敏感,数值越低越说明模型在小目标上挣扎。
我自己训练时的典型曲线是:前20个epoch mAP50快速上升,之后小目标类的mAP50-95涨幅变缓,这时可以加大epochs或者降低学习率继续磨。如果发现训练到一半loss不降反升,很可能是学习率过大或者数据增强过度,这时候优先调低lr0,或者把mosaic关掉再试。
还有一个细节:Ultralytics训练结束后会在runs/detect/train目录下生成weights/best.pt和last.pt。很多人直接拿来用last.pt,其实应该用best.pt——它是验证集上最优的结果,泛化能力更好。
5. 避坑实录:那些能让你血压飙升的问题
5.1 训练时参数量统计不一致是怎么回事
这个问题在社区里经常被问:“为什么训练一开始打印的参数量,和训练完保存的模型参数量不一样?”其实这不是Bug,而是因为Ultralytics在训练开始时显示的是预训练权重的总参数,包括检测头等所有模块;而训练过程中,如果你设置了freeze冻结某些层,或者模型结构中有Detect模块里的某些层在训练过程中被静态量化或剪枝,最终保存的best.pt可能包含的是经过EMA(指数移动平均)的权重,整体统计时会略有差异。但绝大多数情况下,总数应该是保持一致的。如果发现明显不一致,多半是你在训练脚本里改动了模型结构,比如替换了主干网络,导致参数量本身就变了。可以用torchinfo打印每一层的参数来核对。
5.2 重叠框和重复检测怎么处理
小目标检测里重叠框很常见,因为目标密集,两个相邻的人可能边界重叠。YOLO默认的NMS阈值是0.5,也就是两个框重叠超过50%时只保留一个。当你发现一个目标被输出两个框时,可以降低conf阈值或提高iou阈值。在预测代码里这样调:
from ultralytics import YOLO model = YOLO('best.pt') results = model.predict(source='test.jpg', conf=0.25, iou=0.6)conf=0.25表示阈值以上才保留,iou=0.6表示重叠超过60%才合并。小目标因为置信度普遍偏低,如果conf设太高会漏检,设太低会误检,需要自己调一个平衡点。我的经验是0.3左右比较稳,再配合max_det参数限制输出数量,防止一张图输出几百个框导致后处理卡顿。
5.3 小目标漏检严重怎么办
如果你训练完还是漏检,优先排查三个方向:
- 是不是输入分辨率太低?把推理时的
imgsz也调大,训练和推理的分辨率应该保持一致。 - 是不是类别不平衡?TinyPerson里小目标多,但如果你自己采集的数据里目标尺寸分布不均衡,可以考虑对只有小目标的图片做过采样。
- 是不是NMS把低级联输出过滤掉了?小目标的置信度天然偏低,可以在后处理时对低于阈值的框再按类别单独设一个更低的阈值。
另外,我强烈建议用SAHI做推理切片。TinyPerson的测试图像动辄4000x3000,直接一次性送入YOLO就算调大分辨率也不够。用SAHI切成512x512的小块,每一块独立检测再合并,效果提升非常直观。
5.4 模型导出后怎么给QT调用
这个热词也被问烂了:训练好的YOLO模型如何导出,方便QT调用?不管你是做桌面端还是嵌入式,标准做法都是先把.pt转成ONNX,再根据目标平台转成TensorRT或OpenVINO。Ultralytics一行命令导出ONNX:
yolo export model=best.pt format=onnx导出后在QT里用ONNX Runtime加载运行,核心代码就是初始化session,预处理图像到640x640,然后取输出做解码。如果你用的是TensorRT,还要在导出ONNX时固定输入尺寸,比如opset=12,再通过trtexec转成engine。这里有个坑:TensorRT要求输入输出维度是静态的,所以导出ONNX时要注意dynamic=True和dynamic=False的区别。QT里如果用动态维度,每次推理都要重新分配内存,性能很差,建议导出静态维度。
5.5 常见问题速查表
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 训练loss不降 | 学习率过大、数据标签错误 | 降低lr0,检查txt标签是否越界、类别ID是否正确 |
| 验证mAP很低 | 数据集划分不当、图片和标签不对应 | 重建目录结构,确认train/val无交集 |
| 推理时大量重复框 | NMS阈值过低 | 提高iou阈值,合理设置conf |
| 导出ONNX失败 | 模型包含动态尺寸或自定义算子 | 固定imgsz,更新ultralytics版本 |
| 显存不足 | 输入分辨率太高、batch太大 | 降低batch,使用rect=True,或梯度累积 |
| 目标太小测不出来 | 下采样倍数过大 | 使用SAHI切片、提升分辨率、尝试P2层特征 |
6. 最后的实操心得
在把TinyPerson和YOLO结合的过程中,我最大的感受是:小目标检测不是单纯靠堆模型就能解决的事,数据、分辨率、后处理缺一不可。TinyPerson数据集之所以受欢迎,是因为它把“小”这件事做到了极致,让你的模型不得不去关注细节。而YOLO格式的便捷性,则让训练门槛降低了很多。
如果你打算在这个方向继续扩展,我建议把TinyPerson当成基础训练集,再混入你自己场景的少量数据做二次微调。比如你要做道路裂缝检测,可以先在TinyPerson上让模型学会捕捉小纹理,再用裂缝数据微调,往往比直接用裂缝数据训练效果更好。这个迁移思路是我在多个场景里验证过的,属于典型的“先让小目标检测能力入脑,再学具体业务”的路线。
最后再提醒一句:训练之前,务必打开几张标签可视化图看看,确认标注框和目标的贴合度。这一步花不了几分钟,但能避开百分之八十的数据问题。数据这关过了,后面的路就顺多了。
本文还有配套的精品资源,点击获取