第三章 数据集准备:小目标数据集的标注规范与增强策略
说实话,小目标检测的难点,有一半都出在数据上。
模型再强,数据不行,照样白搭。我见过太多同学拿着通用目标检测的数据集直接训小目标,结果mAP惨不忍睹。为什么?因为小目标在图像里就那么几个像素,标注稍微偏一点,IoU就直接掉到0.3以下了。
这一章,咱们就聊聊小目标数据集该怎么准备。从标注规范到增强策略,我把这几年踩过的坑都摊开来讲。
3.1 小目标标注的“毫米级”要求
先问个问题:你觉得小目标的标注框,误差允许多大?
对于大目标,比如一辆汽车占满半个画面,框偏个5个像素,IoU可能还有0.9。但对于小目标,比如一个行人只有30x30像素,框偏5个像素,IoU可能就掉到0.6以下了。
所以,小目标标注的第一条铁律:精度必须控制在2个像素以内。
小目标标注核心指标:
- 标注框边缘误差 ≤ 2像素
- 遮挡目标必须标注可见部分(不要脑补完整框)
- 模糊目标:如果人眼都看不清,建议直接丢弃
- 密集场景:每个实例都必须独立标注,不能漏标
我在做无人机航拍数据集时遇到过一个问题:一群鸟在天空飞,每只鸟只有10x10像素左右。标注员为了省事,直接画一个大框把一群鸟框住。这种数据喂给YOLO,模型根本学不会“一只鸟”是什么概念。后来我强制要求:每个独立实例必须单独标注,哪怕只有5x5像素。
3.2 标注格式与工具选择
YOLO系列最常用的标注格式是txt文件,每行一个目标:
class_id x_center y_center width height
注意:这里的坐标都是归一化到[0,1]的。对于小目标,width和height的值会非常小,比如0.01甚至0.005。这时候浮点数精度就很重要了。
我曾经踩过的坑:
用LabelImg标注小目标时,默认保存的坐标只保留6位小数。对于1920x1080的图像,6位小数对应约0.001的精度,换算成像素就是1-2个像素。看起来够用?但如果你做Mosaic增强或者随机裁剪,坐标经过多次变换后,精度损失会累积。我建议:标注坐标至少保留8位小数。
工具方面,我个人推荐:
- LabelImg:轻量级,适合小规模数据
- CVAT:支持半自动标注,适合大规模项目
- Roboflow:在线标注+增强一条龙,适合快速验证
对于小目标,我特别推荐CVAT的“分块标注”功能。把大图切成小块,标注员可以放大到像素级去画框,精度高很多。
3.3 小目标数据增强策略
数据增强是小目标检测的“救命稻草”。但要注意:不是所有增强都适合小目标。
举个例子,RandomResizedCrop(随机裁剪缩放)是分类任务的标配,但在小目标检测里,它可能直接把目标裁掉或者缩得更小。我见过一个项目,用了RandomResizedCrop后,小目标的召回率直接掉了15%。
下面是我总结的小目标专属增强策略:
| 增强方法 | 适用场景 | 推荐参数 | 注意事项 |
|---|---|---|---|
| Mosaic | 通用 | 4张图拼接 | 小目标可能被切碎,建议配合Copy-Paste |
| Copy-Paste | 小目标专用 | 每张图粘贴3-5个小目标 | 注意背景融合,避免边缘生硬 |
| RandomScale | 多尺度训练 | 0.5~1.5倍 | 不要缩太小,下限建议0.8 |
| Cutout | 遮挡鲁棒性 | 遮挡区域≤图像面积5% | 避免遮挡小目标本身 |
| MixUp | 类别平衡 | alpha=0.2 | 小目标混合后可能更难识别 |
3.4 Copy-Paste:小目标增强的“核武器”
在所有增强方法里,我最想重点聊的是Copy-Paste。说白了,就是把小目标从一张图里抠出来,贴到另一张图上去。
为什么它特别适合小目标?
- 小目标在原始数据里往往数量不足,Copy-Paste可以直接增加样本量
- 可以控制粘贴的位置,避免目标重叠
- 可以调整粘贴目标的尺度,模拟不同距离
我分享一个我在项目中用过的代码片段:
def copy_paste_augmentation(image, labels, paste_objects):
"""
paste_objects: 从其他图像中提取的小目标列表
每个元素: (bbox, class_id, image_patch)
"""
h, w = image.shape[:2]
for obj in paste_objects:
patch, bbox, cls_id = obj
ph, pw = patch.shape[:2]
# 随机选择粘贴位置
x = random.randint(0, w - pw)
y = random.randint(0, h - ph)
# 简单的alpha融合
alpha = 0.7
image[y:y+ph, x:x+pw] = (1-alpha)*image[y:y+ph, x:x+pw] + alpha*patch
# 添加标注
x_center = (x + pw/2) / w
y_center = (y + ph/2) / h
width = pw / w
height = ph / h
labels.append([cls_id, x_center, y_center, width, height])
return image, labels
我的经验:
Copy-Paste时,尽量选择背景相似的场景。比如把天空中的鸟贴到另一张天空图上,效果就很好。如果把鸟贴到草地上,模型可能会学到“鸟在草地上”这种错误关联。我一般会准备一个“背景相似度”的筛选逻辑,低于阈值的就不贴。
3.5 多尺度训练与测试
小目标检测还有一个经典技巧:多尺度训练。
YOLO系列支持在训练时动态调整输入尺寸。比如YOLOv8,可以设置一个尺寸范围,每次迭代随机选一个。这样做的好处是:模型能学到不同尺度下的特征表达。
我建议的配置:
- 基础尺寸:640x640
- 多尺度范围:0.5~1.5倍(即320x320到960x960)
- 步长:32的倍数(因为YOLO的下采样倍数是32)
测试时,可以用TTA(Test Time Augmentation)。说白了就是多尺度推理,然后融合结果。我实测过,TTA能让小目标的AP提升2-3个点。代价就是推理时间翻倍,嗯,看你的场景能不能接受。
3.6 数据质量检查清单
最后,我整理了一份数据质量的检查清单。每次准备数据集时,我都会过一遍:
- 标注完整性:有没有漏标的小目标?特别是密集场景
- 标注精度:随机抽100个框,检查边缘误差是否≤2像素
- 类别平衡:小目标类别和大目标类别的数量比是否合理?
- 尺度分布:小目标(<32x32像素)占比是否足够?建议至少20%
- 遮挡情况:是否有足够的遮挡样本?建议10%-20%
- 背景多样性:小目标是否出现在不同背景中?避免过拟合
我曾经犯过的错:
有一次做交通标志检测,小标志(比如限速牌)在训练集里只有200个,大标志(比如广告牌)有2000个。模型训出来,大标志mAP 0.95,小标志只有0.3。后来我做了两件事:一是用Copy-Paste把小标志数量补到2000,二是对小标志类别加了3倍的loss权重。最终小标志的mAP提到了0.78。
数据准备这一步,说白了就是“磨刀不误砍柴工”。我见过太多人急着调模型,结果数据一塌糊涂,调了两个月还不如别人好好准备一周数据的效果。你想想看,是不是这个道理?
下一章,咱们聊聊YOLO模型结构怎么改,才能更好地适配小目标。到时候我会分享一些我在实际项目中用过的魔改方案,保证干货满满。