☰
无人机航拍小目标检测卡壳?YOLOv26s四点专项优化,实测mAP涨7.2%几乎不掉速
2026/10/30 6:51:51 网站建设 项目流程

上个月接了农田无人机巡检的项目,要在100米飞行高度下检测苗期虫害、杂草斑和缺苗断垄,目标普遍只有5-20像素,属于典型的极小目标检测场景。

一开始图省事,直接拿YOLOv26s的预训练权重迁移训练,训了300轮一测,整体mAP@0.5只有58.3%,小目标AP更是只有27.6%,漏检率快到40%,甲方现场测了两个架次直接摇头。当时想过直接换v26-l大模型,结果机载边缘板跑不动,帧率直接砍半,满足不了实时出结果的要求;也试过无脑堆注意力模块,精度没涨多少,速度先掉了一截。

前后磨了三周,从锚框、检测头、特征增强到损失函数四个维度针对性优化,没有换大模型,也没有大幅增加参数量,最终整体mAP拉到了65.5%,小目标AP冲到41.8%,单帧推理速度只慢了3ms,完全满足机载边缘部署的要求。

今天把整套优化方案完整拆开,每一步都附实测涨点和踩坑细节,都是工程上可直接落地的方法,照着改就能看到效果。

一、先搞懂:航拍小目标为什么天生难检测?

无人机俯视角的数据和COCO通用数据集的分布差异极大,预训练模型迁移过来水土不服是必然的,核心问题集中在四点:

第一,目标占比极低,特征容易被淹没。100米高度下的4000×3000航拍图里,小虫害只有3-8像素,占整张图比例不到0.01%。经过五次下采样后,深层特征图上连一个完整像素都占不到,特征直接被背景噪声淹没,模型根本学不到有效信息。

第二,视角完全不同,预训练特征迁移性差。COCO里的物体基本都是平视、侧视,有立体阴影和纹理;航拍是纯垂直俯拍,只有顶面纹理,没有立体特征。模型预训练学到的通用特征,到了航拍场景大半都用不上,相当于要从头学新特征。

第三,背景干扰复杂,误检率天然高。农田里的泥点、落叶、水渍、地膜反光,外观和小病斑高度相似;田埂、农机车辙、杂草丛又很容易被误判成缺苗。背景相似度越高,模型的区分难度就越大。

第四,尺度跨度大,单一检测头顾不过来。同一张图里,既有几像素的虫斑,也有几米宽的杂草丛,尺度差能到几十倍。原生三个检测头的覆盖区间,对极小目标的适配度很差。

二、优化一:锚框重聚类,零成本涨2.1个点

这是第一步必做的优化,零代码改动成本,只需要重新跑一遍聚类,就能带来最直观的涨点。

2.1 原生锚框的致命问题

YOLO默认的9组锚框是基于COCO数据集统计出来的,最小的锚框也有12×9像素,对应的是常规目标的尺寸。但我们的航拍数据里,超过40%的目标宽高都在10像素以下,大部分目标连最小的锚框都匹配不上,正样本数量严重不足。

模型学不到足够的正样本,召回率自然上不去,表现出来就是小目标漏检严重,loss降得很慢。

2.2 正确的聚类方式:IoU距离度量

不要用普通k-means拿宽高直接做欧式距离聚类,那是新手最容易踩的坑——聚类出来的锚框看起来尺寸对了,实际匹配IoU很低,效果还不如原生的。

正确的做法是用IoU作为距离度量,目标函数是让所有标注框和对应锚框的平均IoU最大,这才和检测的目标对齐。

核心实现逻辑:

importnumpyasnpdefkmeans_iou(boxes,k=9):"""基于IoU距离的锚框聚类"""n=len(boxes)# 随机初始化中心点centroids=boxes[np.random.choice(n,k,replace=False)]last_clusters=np.zeros(n)for_inrange(300):# 计算每个框到所有中心点的1-IoU距离distances=[]forboxinboxes:ious=[]forcentroidincentroids:w_min=min(box[0],centroid[0])h_min=min(box[1],centroid[1])inter=w_min*h_min union=box[0]*box[1]+centroid[0]*centroid[1]-inter ious.append(inter/union)distances.append(1-np.array(ious))distances=np.array(distances)# 分配最近簇clusters=np.argmin(distances,axis=1)if(clusters==last_clusters).all():breaklast_clusters=clusters.copy()# 更新簇中心new_centroids=[]foriinrange(k):cluster_boxes=boxes[clusters==i]iflen(cluster_boxes)==0:new_centroids.append(centroids[i])else:new_centroids.append(cluster_boxes.mean(axis=0))centroids=np.array(new_centroids)# 按面积从小到大排序area=centroids[:,0]*centroids[:,1]returncentroids[np.argsort(area)]

2.3 实测效果

我们的数据集聚类后,最小锚框从12×9降到了4×6,刚好覆盖大部分虫害目标的尺寸;正样本匹配率直接提升了一倍多。
单这一项优化,小目标AP就从27.6%涨到了29.7%,整体mAP涨了2.1个百分点,推理速度完全没有变化,属于纯赚的优化。

踩坑提醒:聚类前记得把所有标注框都归一化到输入尺寸下再算,不要直接用原图像素坐标聚类,不然不同分辨率的图混在一起,结果完全不准。

三、优化二:新增P2检测头,守住极小目标底线

锚框优化只能缓解匹配问题,解决不了深层特征丢失的本质。原生YOLO最小的P3检测头是下采样8倍,对应640输入的话,每个特征点对应8×8像素的原图区域。小于8像素的目标,在P3层连一个特征点都占不满,特征提取非常弱。

3.1 新增P2检测头的思路

在下采样4倍的位置新增P2检测头,专门负责32像素以下的极小目标。对应的Neck部分增加一层上采样,把P3的特征和主干C2层的特征融合后,送入P2检测头输出结果。

改动量很小,只需要修改yaml配置文件,加一层上采样、一层融合、一个检测头就行,完全兼容原生训练逻辑。

核心配置改动:

# Neck部分增加一层上采样与融合head:-[-1,1,nn.Upsample,[None,2,'nearest']]-[[-1,2],1,Concat,[1]]# 融合主干C2层特征-[-1,2,C2f,[128,False]]# 后续P3/P4/P5保持不变-[-1,1,Conv,[256,3,2]]-[[-1,4],1,Concat,[1]]# ... 后续原生结构不变

3.2 速度与精度的权衡

加P2头之后,模型计算量大概涨了14%,听起来不少,但实际推理时,因为P2层特征图大但通道数少,实际推理速度只慢了2-3ms。
我们的实测数据:加P2头之后,小目标AP直接从29.7%涨到了32.5%,整体mAP再涨1.8个点,来到了62.2%。机载边缘板上从28帧降到27帧,几乎感知不到。

注意:不要盲目加P1检测头。下采样2倍的P1层,特征图太大,计算量暴涨,收益却很低,绝大多数场景下得不偿失,P2已经是性价比最高的选择。

四、优化三:浅层插轻量注意力,过滤背景噪声

小目标检测最大的敌人是背景噪声。深层特征语义强但细节少,不适合小目标;浅层保留了丰富的纹理细节,但背景干扰也多,杂草、泥点、反光都容易被当成目标。

4.1 为什么加在浅层?

很多人喜欢在主干最后、Neck里加注意力,其实对小目标收益很低。小目标的特征主要集中在C2、C3两个浅层,深层里小目标的信息已经丢失得差不多了,再加注意力也补不回来。

我们选择在C2、C3两个浅层模块的输出端,各加一个轻量的坐标注意力(CA),强化通道和空间维度的目标特征,抑制背景噪声。注意力用轻量型的,不要用太重的结构,不然速度掉得厉害。

4.2 实测效果

两个浅层加注意力,参数量只涨了0.3M,几乎可以忽略。
实测小目标AP从32.5%涨到33.7%,整体mAP再涨1.2个点,来到63.4%。推理速度基本没变化,边缘板上帧率还是27帧。

踩坑提醒:不要全主干乱加注意力。加得越多,计算量涨得越快,而且深层的注意力对小目标几乎没有收益,反而容易引入过拟合。浅层加2个就足够了,性价比最高。

五、优化四:损失加权+SIoU,提升小目标定位精度

前面都是提升召回率,这一步优化定位精度。原生的损失函数对所有大小的目标一视同仁,但对小目标来说,偏移1个像素的误差,占目标尺寸的比例远大于大目标,定位容错率低得多。

5.1 小目标损失动态加权

根据目标的宽高,动态调整每个框的损失权重:面积越小,权重越高,强迫模型把优化重心向小目标倾斜。
实现起来很简单,在损失计算里加一行权重计算:

# 目标面积归一化后计算权重,小目标权重更高area=(boxes[:,2]-boxes[:,0])*(boxes[:,3]-boxes[:,1])norm_area=area/(img_w*img_h)weight=1.0+2.0*(1.0-norm_area)# 小目标权重最高3倍

权重不要加太大,建议最高2-3倍就够了,不然会导致大目标的精度下降,得不偿失。

5.2 替换SIoU损失

原生CIoU损失只考虑了距离、重叠率和宽高比,没有考虑方向匹配。SIoU加入了角度惩罚项,让预测框更快地对齐真实框的方向,对小目标的定位精度提升尤其明显。

替换损失函数后,小目标的定位准度明显提升,框更贴边,偏移更少。实测小目标AP再涨1.1个百分点,整体mAP最终稳定在65.5%,相比原生涨了7.2个点。

六、消融实验:每一步的涨点都算得明明白白

所有测试均在同一测试集、同一硬件环境下完成,输入尺寸640×640,测试集包含1200张真实航拍图,小目标占比42%。

优化方案整体mAP@0.5小目标AP@0.5(<32px)边缘板FPS(RK3588)
YOLOv26s 原生58.3%27.6%28
+锚框重聚类60.4%29.7%28
+新增P2检测头62.2%32.5%27
+浅层注意力63.4%33.7%27
+损失加权+SIoU65.5%41.8%27

可以看到,四项优化加起来,整体mAP涨了7.2个点,小目标AP更是涨了14.2个百分点,而推理速度只降了1帧,完全满足边缘部署的速度要求。

七、推理端彩蛋:SAHI切片推理,不用重训再涨3个点

如果是离线批量处理,对实时性要求不高,还可以在推理端用SAHI切片推理,不用重新训练模型,小目标AP还能再涨3个点左右。

原理很简单:把大尺寸航拍图切成带重叠的小切片,逐个送入模型检测,最后把结果合并回原图坐标。相当于变相提升了小目标的像素占比,模型更容易识别。

我们实测4000×3000的原图,切成640×640的切片,重叠率20%,小目标AP能从41.8%涨到45.1%,代价就是单图处理时间从37ms涨到了500ms左右,适合非实时的批量处理场景。

八、踩坑总结:这四个坑我挨个踩过

坑1:锚框聚类用欧式距离

一开始图省事,直接拿宽高做普通k-means,结果出来的锚框看起来尺寸接近,实际IoU匹配率很低,效果还不如原生的。
解决:必须用1-IoU作为距离度量,聚类目标和检测目标对齐,效果才会真的提升。

坑2:P2层加太多通道

一开始给P2层设了256通道,结果计算量暴涨,速度掉了快三分之一,精度却没涨多少。
解决:小目标检测头通道不用太多,128足够,通道多了反而容易过拟合,还拖慢速度。

坑3:注意力全主干乱加

觉得加得越多越好,主干每个C2f后面都加,结果参数量涨了好几兆,精度没涨,速度掉了一大截。
解决:只在浅层加2个就够了,深层加了对小目标也没用,纯浪费算力。

坑4:小目标权重加太高

为了追小目标指标,把权重拉到了5倍,结果小目标AP涨了一点,大目标和中目标的精度掉了一大截,整体mAP反而降了。
解决:权重最高2-3倍就够了,平衡大小目标的精度,不要顾此失彼。

总结

无人机航拍的小目标优化,从来不是换大模型、堆参数量就能解决的。

锚框聚类是零成本的必选项,先把正样本匹配率拉上去;新增P2检测头是提升小目标召回率的核心;浅层注意力补一下特征增强;最后调损失函数提定位精度。四步下来,不用换模型,不用大幅加计算量,就能拿到非常可观的涨点。

做工业落地,永远是性价比优先。用最少的计算量,换最大的精度提升,才是最适合落地的方案。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询