尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

YOLOv8小目标检测头实战:P2特征层原理与调参技巧

YOLOv8小目标检测头实战:P2特征层原理与调参技巧 做目标检测的朋友应该都有这种体感大目标怎么调都稳一遇到小目标就开始翻车。无人机航拍、遥感图像、工业质检这些场景里目标往往只有几十像素甚至十几个像素原版YOLOv8在这些数据上经常让你怀疑人生。我自己的红外小目标数据集上原版模型mAP50能到0.8但小目标平均精度APs只有0.3左右加了小目标检测头之后APs直接抬到0.45全类别mAP也涨了两三个点。这篇文章记录一下我在YOLOv8上添加小目标检测头的完整思路和实操过程包括P2特征层的原理、两种改造方案、训练调参经验以及我踩过的坑。适合正在给YOLOv8做小目标改进、刷比赛、做项目落地的朋友参考。整个过程不涉及复杂的魔改核心就是“多一个高分辨率检测头”这件事。1. 为什么YOLOv8在小目标上容易翻车1.1 三个默认检测头到底在做什么原版YOLOv8在head部分输出了三个检测分支分别对应stride 8、stride 16、stride 32。以640x640输入为例三个特征图的大小分别是80x80、40x40、20x20。也就是说网络的每一个格子分别负责感知原图上8x8、16x16、32x32像素区域的中心点。你可以把这个机制理解成不同精度的“网格纸”80x80的网格能比较精细地定位小物体20x20的网格只能看个大概。所以原版YOLOv8其实并不是完全没有小目标检测能力stride 8的80x80特征图本身就是用来处理中小目标的。问题在于这个“最精细”的分辨率也只是原图的八分之一。一个12x12像素的小目标落在这个特征图上只占1.5个格子左右经过几层卷积和C2f模块的特征提取响应值已经衰减得很厉害。更别说那些更小的目标CSPDarknet结构在连续下采样过程中会把很多细节直接“抹掉”。这就像你拿一张像素很低的监控截图去找人放大之后全是马赛克五官早就糊成一片了。1.2 小目标真正的坑不只是模型数据和指标也有问题刚开始改进YOLOv8小目标检测时我一度以为只要改了网络结构就能涨点结果第一次实验mAP原地不动甚至掉了0.5个点。后来仔细分析才发现小目标检测难是系统性的问题模型结构只占一部分。数据问题是最容易被忽视的。很多公开数据集中小目标的数量占比本身就低你能用于训练的样本就那么几百个框。第二个问题是标注质量小目标的边界框稍微标偏几个像素IoU就会产生明显变化这直接干扰回归损失的优化。第三个问题是评价指标COCO的AP是把所有目标按面积分成小、中、大三档其中小目标的面积阈值是小于32x32像素如果你只盯着mAP看小目标的表现会被大量中大型目标稀释掉。所以做小目标检测头改进之前我建议你先做一件事统计一下自己数据集里小目标框的数量占比。如果小目标占比本身就低于10%那加检测头可能收益有限更应该先从数据增强和采样策略下手。如果小目标占比超过20%这个改进就非常值得做。2. 添加小目标检测头的核心思路P2层是关键2.1 为什么选择P2特征层YOLOv8的backbone在stem之后会输出多个尺度的特征图分别叫P1到P5。其中P2是经过两次下采样得到的特征图stride为4。对于640x640的输入P2的大小是160x160比原版最小的检测层80x80还要大一倍也就是说每个格子只负责原图上4x4像素的区域。P2特征图包含的是非常底层的纹理信息比如边缘、角点、颜色变化这些信息对小目标来说就是救命稻草。大目标靠语义信息就能分清类别而小目标只有几个像素的轮廓没有纹理细节根本没法定。用一个生活化的例子你在远处看一个人只能看出“好像有个人”走近之后才能看清他穿什么衣服、拿什么东西。P2就相当于给你提供了一个“走近看”的通道。但是P2层也不是随便拿来就能用它的通道数通常只有128语义信息不够强直接接检测头容易导致误检增多。所以常见做法是把P2和高层的语义特征做融合或者让P2和P3之间多走一次上采样-拼接-融合的流程再送入检测头。2.2 方案一四头输出直接把P2变成检测头这是涨点效果最猛的一种方案也是我在遥感数据集上最终采用的方案。思路很简单在原有P3、P4、P5三个检测头基础上增加一个P2检测头让网络在160x160的特征图上直接输出预测结果。具体在yaml层面的改造思路大致如下在原有head的P3分支之后继续对P3做一次上采样得到160x160的特征图然后和backbone中的P2层拼接经过一个C2f模块融合后作为第四个检测头的输入。改造后的head关键片段看起来是这个样子的# 改造后的head关键片段以YOLOv8s为例 head: # ---- 原有P5-P4路径 ---- - [-1, 1, nn.Upsample, [None, 2, nearest]] - [[-1, 6], 1, Concat, [1]] - [-1, 3, C2f, [512]] # ---- 原有P4-P3路径 ---- - [-1, 1, nn.Upsample, [None, 2, nearest]] - [[-1, 4], 1, Concat, [1]] - [-1, 3, C2f, [256]] # ---- 新增P3-P2路径 ---- - [-1, 1, nn.Upsample, [None, 2, nearest]] - [[-1, 2], 1, Concat, [1]] - [-1, 3, C2f, [128]] # ---- 四个检测头输入 ---- - [[-1, 7, 11], 1, Detect, [nc]]这段yaml里最需要注意的就是层索引。不同版本的ultralytics、不同的模型scale下backbone各层的索引可能不一样。我一开始就是照着别人博客里的索引改结果channels匹配不上直接报错。建议你在改完yaml之后用python打印一下模型结构确认Detect模块接收到的输入通道数是不是[128, 256, 512, 1024]这个思路如果不是就手动调整索引。在ultralytics新版代码里通常直接用YOLO(yolov8s-p2.yaml)这种方式加载自定义yaml。启动训练后你会看到四个检测头的输出strides会自动计算为[4, 8, 16, 32]的顺序。如果打印出来发现stride顺序不对需要手动检查yaml里给Detect传的层顺序。2.3 方案二轻量融合用P2增强P3分支四头方案涨点最多但代价是训练和推理时间都会增加显存占用也会明显上升。如果你的部署设备算力有限或者数据中小目标没有那么多我建议先用方案二不加新的检测头而是把P2特征经过一次下采样后融合到P3分支里。这个做法的核心是让stride 8的检测头既有高层语义信息又有低层纹理细节。具体实现上可以把P2特征做一次stride为2的卷积下采样将160x160的特征图降为80x80然后和P3分支的C2f输出做Concat再送入后续的C2f模块。这种方式对模型计算量的增量比方案一小很多因为最终输出头数量没变只是P3分支的输入通道变宽了一点。实际测试下来在我的红外数据集上方案二涨点幅度大概是方案一的七成左右但推理速度只下降了不到5%。对于那些T4、Jetson之类算力有限的部署场景这个性价比非常高。从原理上看方案二本质上是在做特征金字塔的跨尺度融合把高分辨率的空间细节注入到中等分辨率的语义分支。虽然不如四头方案来得“彻底”但胜在改动小、稳定性高不容易出现训练初期loss大幅波动的问题。3. 训练配置与调参经验让涨点真正落地3.1 数据预处理小目标场景的增强三板斧网络结构改完之后别急着直接跑训练先把数据预处理跟上。我试过好几次单纯加检测头但不调整数据策略涨点幅度非常有限。小目标检测场景下有三板斧是必须做的。第一板斧是Mosaic增强。YOLOv8默认就开了Mosaic它能随机把四张图拼在一起变相增加小目标出现的频率和多样性对小目标训练有奇效。第二板斧是Copy-Paste增强。这个策略非常直白——把小目标从一张图里裁剪出来随机粘贴到另一张图上。小目标数量不够的时候这是增加小目标样本数量的最有效方式之一。用ultralytics训练时可以在超参数文件里把copy_paste调成0.3到0.5之间。我自己试下来这个值太高会导致部分目标被遮挡严重反而损失精度。第三板斧是大图切块。对于那些原始分辨率是几千乘几千的遥感图、航拍图最有效的做法不是直接resize到640而是用滑窗切成1024或1280的子图再送入网络训练。这样小目标在切块后的图像里相对尺寸会大很多网络学起来要轻松得多。如果你切块后发现目标还是太小再叠加小目标检测头效果会非常明显。3.2 关键超参数输入尺寸、batch size和学习率加检测头这件事最直接的影响就是计算量上升。P2特征图是160x160相比P3的80x80光这个分支的空间维度就是四倍算力和显存占用都会明显增加。如果你用的是GTX 1660 Ti这种6GB显存的卡原来batch size能开到16加了四头方案之后建议降到8甚至6不然非常容易OOM。我第一次跑就是没注意这一点batch size16直接爆显存还以为是代码写错了。后来改成batch size8配合梯度累积才正常跑起来。另一个关键参数是输入尺寸。我强烈建议在小目标数据集上把imgsz从默认的640调大到960或1280哪怕你用的是轻量方案。原因很简单输入分辨率越大小目标在特征图上的像素响应越强这个收益有时候比加检测头还大。当然代价还是显存和训练速度需要根据自己卡的情况平衡。学习率和warmup策略也要跟着调。加了检测头之后网络结构变深变宽初始warmup轮次建议适当增加让新增分支的权重先在浅层稳定下来。我在实际训练中会把warmup_epochs从默认的3改成5初始学习率降为原来的八成整体loss收敛会更平稳。另外anova、box这些loss的权重可以根据你的数据分布微调如果小目标回归不准可以适当增大box_loss权重。3.3 评估指标别只盯mAPAPs才是试金石训练完成之后评估指标这块要特别小心。我见过有朋友加了检测头整体mAP看着涨了但具体看小目标APs反而跌了这种情况说明改进方向有问题只是被大目标的表现掩盖了。在COCO评价体系里小目标的面积阈值是小于32x32像素。所以你在打印验证集指标时除了看mAP50和mAP50-95之外一定要单独看APs这个指标。如果APs涨了、APm基本持平说明小目标检测头确实起作用了。如果只有APl在涨那大概率是你改动过程中引入了对大目标更友好的偏置需要复盘一下结构改动。建议训练时开启ultralytics的plots参数会自动生成confusion_matrix.png、results.png这些可视化文件。results.png里有loss曲线和各类指标曲线能看到小目标APs随着epoch动态变化的情况。我一般会结合loss曲线和APs曲线一起判断如果box_loss和cls_loss稳定下降但APs不动问题基本出在数据侧而不是模型侧。4. 常见问题与排查技巧实录4.1 网络定义阶段的常见报错改造yaml最容易遇到的就是通道数不匹配问题。Detect模块要求所有输入分支的通道数在叠加类别数之前保持合理如果你从错误的backbone层引出P2特征经常会出现RuntimeError: Sizes of tensors must match这类报错。我的排查习惯是这样的改完yaml后先不急着训练直接写一段几行代码加载模型并打印中间层信息from ultralytics import YOLO model YOLO(yolov8s-p2.yaml) print(model.model)模型打印出来后重点检查Detect模块的stride属性和ch通道列表。正常的四头模型ch应该是一个包含四个值的列表stride应该对应[4, 8, 16, 32]。如果stride顺序乱了或者缺少P2对应的4就去yaml里检查给Detect传的层索引。还有一个小坑是我栽过的YOLOv8的Detect模块内部会根据输入层自动计算stride计算方式是在一次fake forward中获取shape变化。如果你的自定义yaml里某个层设置不合理可能出现stride计算不准确的情况。这时候最简单的办法是打印model.stride看到tensor([4, 8, 16, 32])就说明正常。4.2 训练阶段的问题与排查最常见的训练问题是loss爆炸。加了P2头之后新增分支的初始权重是随机的如果主干网络预训练权重加载不当前几个epoch的loss可能会跳得很高。我的建议是尽量加载官方COCO预训练权重即使通道数不完全匹配ultralytics会跳过不匹配的层剩下能用的backbone权重也能让训练起点更稳定。显存不足则是另一个高频问题。方案一的P2分支在训练时会额外占用大量显存特别是在开启Mosaic增强的情况下batch size往往需要大幅下调。如果你调小batch size后精度反而下降可以试试梯度累积来弥补batch大小的影响或者直接用方案二的轻量融合。还有朋友遇到过推理时检测框大量重复、置信度偏低的问题。这个大概率是NMS阈值和conf阈值设定不合适。P2头检测范围小对小目标的置信度天然偏低建议在推理阶段把conf阈值适当调低比如从0.25调到0.15再配合合适的NMS IoU阈值往往能找回不少漏检的小目标。4.3 涨点不明显甚至负优化怎么排查说实话加小目标检测头不是万灵药我自己也遇到过负优化的情况。如果你加了之后APs没涨甚至跌了按这个顺序排查。先看数据层面。统计一下数据集中小目标框占比如果小于15%效果不明显是正常的可以考虑先用Copy-Paste和大图切块把小目标样本数量提上来。再看输入尺寸。如果还在用640训练先试着提到960很多时候是分辨率限制了P2头的发挥。再看训练配置。P2头对学习率比较敏感如果初始学习率偏大新分支很容易被主干梯度带偏。试着降低初始学习率增加warmup轮次。最后看结构。如果你用的是方案一四头结构可以尝试去掉P4或P5中的某一个头也就是只保留P2、P3、P5三个头减少冗余特征对训练的干扰。某些数据分布下三个头的效果反而比四个头更好。另外P2头可以和注意力机制搭配使用。我自己在P2分支融合层后面加了一个轻量的Coordinate Attention又提了0.8个点的mAP代价几乎可以忽略。如果你追求极致涨点也可以试试CA、SE、CBAM这几个经典的注意力模块个人经验CA在小目标场景下最稳。5. 一点实测体会最后分享一个我自己反复实验后的结论小目标检测头最适合的场景是红外小目标、遥感图像、无人机航拍以及自动驾驶中的远距离目标。这些场景的共性是小目标占比高、纹理信息重要、检测精度要求苛刻。在部署侧也要提前想清楚。如果你打算把模型部署到边缘设备比如RK3588这类开发板上四头方案带来的额外计算量会直接影响推理帧率。我建议先评估设备能承受的算力开销再决定用方案一还是方案二。导出ONNX或RKNN的时候新增的检测头也需要重新做模型转换和精度验证不要直接拿之前的部署流程硬套。踩过几次坑之后我的习惯是先跑一个baseline再在baseline基础上加小目标头最后再用测试集做对比。这样每一步改进的效果都能清楚归因。如果你也想动手试建议从方案二开始改动小、风险低、涨点稳定跑通了再考虑上方案一。祝你在自己的数据集上也能把小目标的分数稳稳抬起来。
返回列表