做小目标检测的都知道一个让人头皮发麻的场景:模型训练完,mAP看着还凑合,可一翻检测结果图,小目标几乎全灭了——漏检一个接一个,偶尔检出来的框还偏移得离谱。我在Visidron数据集上用YOLOv8训练就撞上了这堵墙:640分辨率输入下,小目标的AP指标惨不忍睹,肉眼可见的误检和漏检一起爆。这篇文章就围绕“YOLOv8训练Visidron小目标检测数据集”这条主线,把我从baseline跑到精度明显提升的完整过程、参数取舍和踩坑记录都摊开讲,适合正在拿YOLOv8折腾小目标、想系统提升AP指标、或者刚入手目标检测想少走弯路的读者参考。
Visidron数据集本身不算大,但目标尺寸分布非常极端,小目标数量占了大头,这正好是检验小目标检测手段的好素材。我最后把mAP50从0.56拉到0.73,mAP50-95从0.31提到0.46,小目标AP_s从0.18翻到0.29,算不上什么惊艳成绩,但每一步的改动逻辑和代价都摸清楚了,拿出来复盘是值得的。
1. 为什么Visidron这样的数据集会让YOLOv8“失效”
1.1 小目标检测难的三个根因
小目标检测难,不是某一个环节出了问题,而是从数据采集、标注到模型结构都在吃亏。Visidron数据里的目标大量只有十几个像素宽,在隐空间里特征响应极弱,检测头很难从背景中把它们分离出来。我把原因拆成三个层面:
第一是像素占比太低。一张1920x1080的图,目标只有20x20像素,实际有效像素不到总像素的0.02%。模型下采样四到八次之后,目标在特征图上可能就剩一到两个像素点了,连卷积核都“看不清”它,更别提准确回归框的边界。
第二是上下文信息不够。大目标之所以好检测,是因为它自带丰富的纹理、边缘、场景关系;小目标往往只占据几个像素,能和它关联的上下文基本被池化和stride吃掉,剩下的特征很容易被背景噪声淹没。
第三是先验分布极端。像Visidron这种数据集,目标的宽高比和尺寸范围可能跨越数十倍,正样本框的面积分布严重长尾。模型在训练时会更倾向于“照顾”数量多、面积大的目标,小目标天然处于弱势。
1.2 Visidron数据集的目标尺寸分布带来了什么影响
我拿到Visidron数据后做的第一件事就是统计目标的像素面积分布。用脚本跑了一遍标注框,结果让我重新调整了整套训练策略:数据集中大约六成的目标面积小于32x32像素,其中又有接近三成的目标小于16x16像素。这个比例在通用目标检测数据集里很少见,属于典型的小目标长尾分布。
这个分布直接决定了训练方案的走向:如果照搬COCO那套默认参数,小目标会很难学到稳定特征,AP_s上不去,最终mAP也会被拖累。再往后看实验也证明了这一点,我第一次用YOLOv8m在640分辨率下训练,AP_s只有0.18,和数据集里小目标占比高的情况完全不成比例,说明模型对小目标严重“欠拟合”。
1.3 我的实验环境与初始baseline
先交代一下环境:单张显卡训练,Windows下跑的,PyTorch和Ultralytics版本都是当前稳定版。显卡显存32GB,这个配置够用但也不算宽裕。
基线实验我用的是YOLOv8m模型,输入分辨率640,batch size 16,训练100个epoch,优化器AdamW,初始学习率0.001。验证集上的结果如下:
| 指标 | 数值 |
|---|---|
| mAP50 | 0.56 |
| mAP50-95 | 0.31 |
| AP_s (小目标) | 0.18 |
| AP_m (中目标) | 0.42 |
| AP_l (大目标) | 0.61 |
大目标的AP接近0.61,中目标0.42,小目标只有0.18,三档之间差距非常明显。这就是典型的小目标欠拟合现象。拿到这个baseline之后,我开始逐项排查问题出在哪里,而不是无脑加模型复杂度。
2. 训练前的数据体检:预处理阶段就能预见一半的问题
2.1 标注质量:宁可漏标也不能错标
Visidron数据集的标注质量总体还行,但小目标场景下标注出错对模型的伤害远比大目标场景严重。大目标即使边界框有几像素偏差,对IOU计算的影响有限;小目标本身只有十几像素,标注框稍微多框几个像素或少框几个像素,IOU就会剧烈变化,训练出来的回归头会持续收到相互矛盾的梯度。
我写了个检查脚本,统计了每个标注框的宽高、面积,并把异常标注挑出来人工确认。主要查三类问题:面积小于3像素的标注、长宽比超过10:1的细长条框、重叠面积超过80%的重复框。这三类数据在小目标场景下往往不是“难例”,而是“错例”,直接修正或剔除效果更好。
提示:在数据集中如果发现大量面积小于3x3像素的标注,建议人工筛一遍再训练。这种框即使标注正确,模型也学不动;如果标注本身偏移了,危害更大。
2.2 小目标数据的增强策略要提前想清楚
数据增强对通用目标检测很有效,但小目标场景里很多增强手段是双刃剑。比如mosaic增强,把四张图拼在一起,每个目标被裁剪的概率变高,大量小目标可能在拼接过程中被裁掉或缩得更小,等于变相丢失训练样本。Visidron数据集里小目标占比已经很高,如果再用默认的mosaic参数,很容易把本来就稀少的正样本进一步“洗掉”。
另外copy-paste增强在小目标场景反而有用:把前景目标扣出来,随机粘贴到背景图上,相当于用小目标“扩充”数据集。但对Visidron这种目标尺寸极小的数据,直接用copy-paste可能会贴上大量重复目标,需要注意控制粘贴数量,避免模型学到堆叠伪影。
2.3 要不要做切片/滑窗预处理
很多朋友遇到小目标第一反应是“把大图切成小块再训练”,也就是滑窗切图思路。Visidron数据集我试过不切图、直接加大输入分辨率的效果,结论是:切图虽然有效,但未必是最优解,因为滑窗会切割目标的上下文,而且推理阶段要拼接结果,处理性能开销不小。
我自己的建议是:先尝试提升输入分辨率,如果显存允许甚至可以直接1280或1536;如果数据集里同时有超大图和极小目标,再做滑窗切图给模型“补充小目标样本”。Visidron这套数据里图片尺寸相对均匀,我没有做切图,而是把精力集中在分辨率、模型结构和后处理上。
3. YOLOv8训练小目标时的针对性调整
3.1 模型规模的选择:不是越大越好
YOLOv8系列有n/s/m/l/x五个规模,很多人习惯性用yolov8m或yolov8l,觉得“大模型总归更好”。在小目标场景,这个直觉需要修正:模型太大会带来更强的拟合能力,但也更容易过拟合,尤其在Visidron这种数据量不算特别大的数据集上,小目标样本又少,大模型的泛化能力反而可能下降。
我在baseline之后依次试过yolov8n、yolov8s、yolov8m、yolov8l四个规模,固定输入分辨率1280来对比。实验室结果显示:从n到l,小目标AP_s在稳步上升,m模型以下的提升很有限,而l模型在epoch数足够时能明显拉开差距。但x模型在我的显存和epoch配置下没有提升,反而训练时间翻倍。所以最终选择了yolov8l作为精度方向的收敛选择。
下面是几个规模在1280分辨率下的小目标AP_s对照(只调模型规模,其余参数保持一致):
| 模型 | AP_s | 训练耗时/100epoch |
|---|---|---|
| yolov8n | 0.20 | 约4h |
| yolov8s | 0.23 | 约6h |
| yolov8m | 0.26 | 约9h |
| yolov8l | 0.29 | 约14h |
x模型我跑了80个epoch没等到提升,性价比低,直接放弃了。
3.2 输入分辨率imgsz:小目标检测最立竿见影的参数
YOLOv8默认输入是640x640,但Visidron里大量小于32x32的目标经过模型下采样后,在特征图上只剩下不到1个像素,检测头根本没有足够信息。提高imgsz是目前公认对小目标最直接有效的方向,我实测的提升幅度也是最大的。
我用yolov8m做了一组同参数对照实验:
| imgsz | mAP50 | mAP50-95 | AP_s | 显存占用 |
|---|---|---|---|---|
| 640 | 0.56 | 0.31 | 0.18 | 约12GB |
| 1280 | 0.65 | 0.38 | 0.23 | 约20GB |
| 1536 | 0.66 | 0.39 | 0.24 | 约28GB |
从640到1280,AP_s涨了5个点,非常可观;但1280到1536的提升就停滞了,说明已经到了模型能力和数据标注质量的边界。最终我把imgsz定在1280,兼顾精度和训练速度。
3.3 训练超参的针对性调整
这里分享一份我在Visidron上实际使用的训练配置,都是针对小目标场景调过的,不是默认值:
# visidron_yolov8l.yaml 训练配置参考 task: detect mode: train model: yolov8l.pt data: visidron.yaml epochs: 200 imgsz: 1280 batch: 8 optimizer: AdamW lr0: 0.0005 lrf: 0.01 weight_decay: 0.0005 warmup_epochs: 2 mosaic: 0.0 close_mosaic: 10 copy_paste: 0.3 hsv_h: 0.015 hsv_s: 0.5 hsv_v: 0.3 degrees: 0.0 translate: 0.1 scale: 0.2 fliplr: 0.5几个关键点:mosaic我直接关到了0,因为小目标本来就容易被mosaic裁掉;close_mosaic保留了10,用于正常化收敛。学习率从默认0.01降到了0.0005,这个改动帮我避免了loss震荡和nan问题。scale抖动也调小了,因为对已经很小的小目标做进一步缩放等于“雪上加霜”。
4. 一轮完整训练实录:loss曲线、验证和常见坑
4.1 第一次试跑就翻车:loss不降还震荡
我调整完参数第一次正式训练,跑了30个epoch后发现问题很大:box_loss一直在震荡,cls_loss没有持续下降,验证集上的mAP50在0.4附近徘徊。排查了半天,问题定位在学习率0.01 + large model + 高分辨率的组合上。
YOLOv8的默认学习率是针对COCO这种数据分布设计的,Visidron的小目标分布导致梯度方差更大,过大的学习率让优化过程反复横跳。把lr0降到0.0005之后,loss曲线立刻平滑了很多。如果你在训练小目标数据集时也遇到loss震荡,优先检查学习率,而不是怀疑模型结构。
4.2 训练过程的关键指标怎么看
训练过程中我主要盯四个指标:train/box_loss、train/cls_loss、val/box_loss和val/mAP50-95。小目标场景下box_loss更值得关注,因为回归框质量直接影响小目标的定位精度。
一个有用的习惯是边训练边拿验证集里的典型小目标图看预测结果,而不是只看loss曲线。有些情况下loss很低但小目标依然漏检,这说明模型其实是在“放弃”小目标,只学出了大目标的表现。我跑到第120个epoch时出现过这种状态:box_loss降下来了,但AP_s没有同步上升,说明模型过度拟合大目标,忽视了小目标。
这个问题的解法通常是:进一步提高输入分辨率、加P2检测头,或者在损失权重上做调整。我后面就是靠加P2层把AP_s从0.24拉到了0.29。
4.3 验证命令和评估细节
训练完成后,我用下面的命令做验证:
yolo detect val model=runs/detect/train/weights/best.pt data=visidron.yaml imgsz=1280注意验证时imgsz要和训练时保持一致,否则特征尺度不一致,结果会产生偏差。另外验证集结果要分尺寸区间看,不能只看mAP。Ultralytics在验证结束后会输出每个类别的AP,但没有直接区分小/中/大目标的表格,我会自己写脚本来统计AP_s,这样才能真正看到小目标的提升。
5. 从0.56到0.73:我实际试过的六个精度提升手段
5.1 提高输入分辨率:收益最大、成本也最直接
前面已经论证过,imgsz从640到1280直接让小目标特征在特征图上从“不可用”变成“可用”。这一步带来的提升远超其他手段的组合。不过代价是显存和训练时间的翻倍,如果你的卡支持不到1280,可以考虑在推理时用TTA或半精度来弥补一部分精度。
5.2 给模型加一个P2检测头
这是YOLOv8在小目标检测中最值得尝试的结构修改。YOLOv8的默认检测头包含P3、P4、P5三层,对应的下采样倍数分别是8、16、32。小目标经过下采样后特征图太小,信息基本丢了。加一个P2层(下采样4倍,对应输入分辨率1280时特征图为320x320),可以让浅层高分辨率特征直接参与目标预测。
具体实现需要修改模型yaml文件,在backbone中额外输出P2特征,并在head中添加一个对应的检测分支。Ultralytics的官方仓库里有针对P2的示例配置,照着改并不难,关键是数据输入输出维度要对齐。这个改动让我的AP_s从0.24提升到了0.29,是整个调优过程中第二大收益。
5.3 关闭mosaic增强:别让小目标被“拼图”拼没了
这个手段更像是止损。默认mosaic=1.0时,四张图随机裁剪拼接,小目标被切掉的概率非常高,有效样本反而减少了。我把mosaic设为0后,训练稳定性和小目标AP都有提升。对Visidron这种小目标密集的数据集,牺牲mosaic带来的多样性换取样本完整性,总体是划算的。
5.4 使用TTA多尺度预测
TTA(测试时增强)在小目标检测里也有效。Ultralytics自带TTA模式,我实测可以把AP_s再提1到2个点。
yolo predict model=runs/detect/train/weights/best.pt source=val_images imgsz=1280 augment=True代价是推理时间变成原来的2到3倍。如果做线下分析可以开着TTA,但线上实时推理就要权衡一下了。这个提升本质上是把多个尺度下的检测结果做融合,对小目标这种尺度敏感的场景非常实用。
5.5 调整NMS参数和后处理逻辑
YOLOv8推理时的NMS阈值默认设置偏向通用场景。在小目标密集区域,很多真实目标会被当成重叠框被NMS抑制掉。我实验把iou阈值从默认0.7调整到0.5左右,小目标召回率有一定提升,但误检也会增加。多试几组值,结合你自己的误检容忍度来定。
另一个实用技巧是放宽置信度阈值,推理时把conf设置低一些(比如0.05),再用类别先验或空间约束过滤掉明显误检,这在调查阶段很有用。线上使用时要小心,低置信度会带来大量噪声。
5.6 数据层面:做一次简单的困难样本挖掘
最后一步提升来自“补样本”:我单独挑了验证集里大量漏检的小目标区域,做了小范围裁剪和仿射变换,生成了一批补充训练样本回填到训练集里。这种“困难样本挖掘”的方式很朴素,但在Visidron这种小目标数据集上很直接:把模型漏掉的目标显式喂回去,训练效率比盲目调参高得多。这也让我理解了为什么很多工业项目里的目标检测团队会专门维护一份“困难样本库”。
6. 写在最后的个人经验小结
折腾完整个项目,我的体会是:YOLOv8训练小目标数据集,最有效的方向排序是输入分辨率、P2检测头、数据增强调整、后处理参数,其他花哨技巧在小目标场景里收益都很微弱。Visidron数据集的特殊性也让我意识到,一个通用模型在小目标场景下“失效”不是模型不行,而是没有针对数据分布做适配。
另外想分享一个经验:不要一上来就堆模型复杂度。yolov8x比yolov8l在Visidron上几乎没有改善,反而耗费了大量训练时间。先把imgsz和mosaic这类“环境参数”调整到位,再考虑修改模型结构,最后才上TTA这类推理阶段技巧,每一步都要用控制变量法验证效果。
如果你也在小目标检测上被折磨得头大,不妨按这个顺序排查一遍:数据标注检查 -> 输入分辨率 -> 模型结构 -> 数据增强 -> 后处理。这套流程在Visidron上帮我走了不少弯路,希望对你也有参考价值。