☰
远距离昆虫检测识别实战:RetinaNet+Swin-T+FPN小目标检测
2026/10/21 3:30:40 网站建设 项目流程

远距离昆虫检测与识别RetinaNet-Swin-T-P4-W7-FPN实战

农业植保圈子里一直有个很实在的需求:能不能用一套自动化系统,在田间地头或者温室大棚里隔几米远就把害虫种类和数量数清楚。过去大家都是靠黄色粘虫板,隔几天收一次板子数虫子,费时费力不说,识别全凭老师傅的眼睛。我这次做的项目,就是把远距离拍摄的昆虫图像交给深度学习模型,用RetinaNet + Swin-T-P4-W7 + FPN这套组合来实时检测和分类,让我把测试过程中踩过的坑和最终能跑通的技术路线做一个完整复盘。

先说结论:这套方案非常适合“远距离 + 小目标 + 多类别”的复杂昆虫检测场景。Swin Transformer作为backbone,在特征提取能力上明显强于同尺度的CNN网络;RetinaNet的Focal Loss又天然适配背景和前景比例严重失调的田间实拍图;FPN负责把不同尺度的特征合成起来,让几十个像素大小的虫子也能被照顾到。这篇内容适合正在做农业视觉识别、无人机巡检、虫情测报灯数据自动分析,以及任何对小目标检测精度头痛的开发者参考,我会把从数据标注到模型训练再到调参避坑的细节全部展开讲清楚。

1. 项目整体设计与技术选型逻辑

1.1 远距离昆虫检测为什么难做

很多接触过目标检测的人一开始不理解,识别虫子而已,跟识别猫狗有什么区别?区别非常大。远距离拍摄的昆虫在画面里往往只有20×20像素甚至更小,一只飞虱在1米外拍到的有效像素可能还不够塞一个ResNet的下采样网格。与此同时,昆虫所在的环境——水稻田、玉米地、果树枝叶——背景纹理复杂,叶脉、土块、水滴、虫咬斑点都和虫体颜色高度接近,模型的误检率很容易失控。

还有一类障碍是目标密集和遮挡。田间虫情测报灯或者粘虫板上的虫子经常是几百只挤在一起,有重叠、有朝向差异,还有翅膀透明的个体,轮廓极其模糊。这时候如果只用普通的IoU-based anchor匹配策略,很多小虫子根本找不到正样本anchor,训练阶段就学不会。

我这次的测试条件其实比纯真实田间更苛刻:相机固定在2~3米高的支架上,向下或侧向拍摄诱虫板,每张图的视野范围约60cm×45cm,最小目标只有12×12像素。这种尺度下,传统YOLO系列确实也能跑,但漏检率在密集场景下基本不可接受,所以才决定上Swin+RetinaNet的组合。

1.2 选型对比:为什么是RetinaNet而不是YOLO或DETR

先说我怎么排除掉其他方案的。YOLO系列(包括v8的Anchor-Free方案)在工业部署里确实香,速度和精度平衡很好,但是它的核心设计偏向通用物体检测,小目标层通常只增加一个更浅的P2层,对远距离细粒度分类的支持比较有限。DETR那类基于Transformer端到端检测的做法,在小目标密集场景下收敛慢,且需要超大规模数据预训练,我这边的训练集只有一万多张实拍图,跑起来风险和成本都偏高。

RetinaNet给我的信心主要来自两点:第一,它是在FPN多尺度特征上接单阶段检测头的经典结构,不需要RPN那种两阶段流程,训练速度比Faster R-CNN快;第二,Focal Loss能主动压低大量易分类负样本的loss贡献,把训练重心放到那些难分的小目标上。田间远距离拍摄的正负样本比例可能到1比5000,Focal Loss在这时候几乎就是刚需。

我用一个简单的对比来理解这件事:YOLO像是让一个阅卷老师快速扫一眼整张试卷只挑出明显的大题错误;RetinaNet的Focal Loss则像是一位老师拿到一张错题分布极不均匀的试卷,主动把注意力集中到那些不易发现的小错误上。远距离昆虫图里恰恰全是这样“不易发现的小错误”。

1.3 为什么backbone选Swin-T而不是ResNet

ResNet-50在检测任务里的地位不用多说,但在远距离昆虫图上,它有个天生的短板:连续卷积下采样让特征图分辨率快速降低,到最后一层一个像素对应原图32×32区域,12像素大小的虫子早就糊成一团。Swin Transformer通过分层结构和窗口自注意力机制,能在多个尺度上保持较强的细节建模能力,尤其对纹理复杂、轮廓模糊的小目标更友好。

我选的是Swin-T(Tiny版本),也就是最小的那个规格,参数量约28M。有人问为什么不直接上Swin-L,我实测下来,Swin-L在单卡上训练一轮要将近40分钟,而Swin-T只要18分钟,mAP却只低了1.2个百分点。对农业监测这种需要快速迭代的场景来说,这个性价比差异太明显了。

标题里写的P4和W7是Swin的两个关键配置。P4表示Patch大小是4×4,也就是说输入图像先被切成4×4的小块,再映射成token序列,这决定了模型最早期的下采样倍率不超过4倍,原始细节保留得比Patch=8好很多;W7表示窗口注意力机制里每个局部窗口是7×7的token范围,在这个窗口内做自注意力计算,既控制了计算量,又保留了局部上下文。这两个参数组合起来,让Swin-T能在有限算力下同时兼顾全局语义和局部细节,特别适合昆虫这种散落分布、纹理细节多但单目标面积小的检测任务。

2. 数据集构建与标注策略:远距离昆虫识别的地基

2.1 数据采集的硬件布置与拍摄规范

数据是一切的起点。我这次用的是工业级网络相机,固定在三脚架上向下俯拍诱虫板,图像分辨率设置成1920×1080,确保单只害虫在画面里能有至少12像素的直径。整个过程最需要注意的一点是:拍摄距离和角度必须与最终部署保持一致。很多人训练集和测试集的拍摄视角不一致,结果现场跑的时候性能掉一大截,这属于自己给自己挖坑。

我分了三批采集数据:晴天上午、阴天中午、傍晚人工补光。为什么要区分光照条件?因为昆虫体表的蜡质层在强光下反光严重,傍晚低照度下整体偏暗,如果训练集里不覆盖这些变化,模型在真实田间轮换使用时就会周期性失灵。

采集时还有一个容易忽略的细节:不要只拍干净板子上的昆虫,也要拍一些被雨水淋过、沾了灰尘、有破损的个体。昆虫检测模型最常见的失败模式之一就是把破损虫体和杂物混在一起,训练时见不到这些脏样本,推理时就会把垃圾也框出来。

2.2 标注规范与小目标处理技巧

标注工具我用的是常见的图像标注软件,直接导出COCO格式。标注规范上必须提前想清楚,否则后期返工太痛苦。

我踩到的第一个坑是:到底按“个体”标注还是按“虫群”标注。如果板子上有几十只蚜虫挤成一团,逐个画框会把标注成本推高到不现实;但如果一个框里框进十只虫,模型学到的就是“一团”而不是“一只”。我的最终方案是设定一个规则:单只虫体边界清晰可辨的就单独框;粘连超过50%且个体边界模糊的成虫群,只统计不训练,因为这些数据点本来就不适合做监督信号。这个规则看起来简单,却直接影响最终精度的天花板。

第二个关键处理是小目标增强。COCO数据集会把面积小于32×32像素的目标单独归类,我抽空看了一下自己标注的分布:将近42%的实例都落在16×16到24×24像素范围内。为了不让这些小目标被训练过程中的随机裁剪丢失,我在数据加载器里加了一个随机放大策略:有30%的概率把原图局部区域放大2.5倍后再喂给模型,相当于隐形地增加小目标占输入图像的面积比例。这个trick不算新,但在远距离昆虫场景下效果异常显著。

2.3 数据增强删选与类别不均衡处理

我启用的数据增强包括随机翻转、随机旋转(30度以内)、随机亮度和对比度扰动、以及马赛克增强。但有一个增强我必须明确关闭:大幅度的随机裁剪。因为昆虫目标太小,裁剪比例超过50%后,很多目标直接被裁掉或者在边缘被切碎,反而会制造一堆残缺正样本,干扰模型学习。

类别不均衡方面,我的数据分布大致是:飞虱类占34%、蚜虫占27%、叶蝉占18%、蛾类占11%、其他小型害虫占10%。这种分布不算极端,但头部类别的梯度贡献远大于尾部类别。我在损失函数里给尾部类别加了class weight,权重设为类别频率的倒数再归一化,最终让蛾类这类样本少的类别也能被充分学习。另外一个有效做法是在训练后期固定住backbone参数,只用检测头继续迭代几十轮,让头部的分类器充分拟合尾部类别的特征。

3. 模型结构与关键参数配置详解

3.1 RetinaNet + Swin-T的整体结构解读

整套模型的结构可以拆成三段理解:Swin-T负责把输入图变成一组多尺度特征图,FPN把相邻层特征融合起来,RetinaNet的检测头在融合后的特征上预测类别和边框。推理流程很简单,一张图进去,一组带置信度分数的框出来,不需要额外的候选区域生成步骤。

Swin-T在结构上分了4个阶段,每个阶段由不同数量的Swin Transformer Block堆叠,配合Patch Merging做降采样。Stage 1到Stage 4的输出特征相对输入图分别是4倍、8倍、16倍、32倍下采样,这四个尺度正好对应RetinaNet需要用的P3、P4、P5层,不用额外做分辨率对齐。这比我之前用ResNet-50时需要在最后补一个P2层的情况省事很多,也稳定很多。

RetinaNet的检测头是典型的单阶段设计:每个FPN层上分别接一个分类子网和一个回归子网。分类子网输出每个anchor的类别置信度,回归子网输出相对anchor的坐标修正量。最终的有效检测框还需要经过置信度阈值过滤和NMS去除重合框。

3.2 Anchor参数设计与FPN层配置

Anchor的设置是小目标检测里最容易被忽视也最致命的点。我在第一版配置里直接沿用了COCO数据集的默认anchor尺度——那几个尺度头是32、64、128、256、512像素,明显不适合我这边的目标尺寸。昆虫实例绝大多数在12到60像素之间,用COCO默认anchor,等于让模型在错误的“标尺”上做回归。

我最终按FPN层分别设置了anchor:P3层负责小目标,anchor基础边长为16像素,P4层为32像素,P5层为64像素,每层设置3种宽高比(0.5、1.0、2.0)和3种缩放比例(0.75、1.0、1.25),所以每层9个anchor。这样P3层等效anchor尺寸覆盖范围大约是10到30像素,刚好对齐主要目标群。实测改完这组参数后,小目标类的召回率提升了将近10个百分点,这是我整个项目里性价比最高的一次改动。

FPN我用的是经典的1×1卷积+自顶向下上采样策略,每层输出通道固定为256。没有额外加PANet结构,因为实验下来增加自底向上路径后,大目标精度有小幅提升,但小目标几乎没有变化,考虑到训练成本就直接砍掉了。如果你的目标分布以大目标为主,加PANet是值得的;像我这种以小目标为主的场景,没必要。

3.3 Focal Loss关键参数与正负样本匹配逻辑

RetinaNet的命名由来就是Focal Loss,这里的参数值得单独提一提。Focal Loss是标准的交叉熵加上一个调制因子,调制因子的作用是:当一个样本被正确分类且置信度很高时,它的损失贡献被大幅压缩;而当样本被错误分类时,损失几乎不受影响。这样模型就不会被海量的简单负样本带偏。

实际配置中我用了论文默认的gamma=2.0、alpha=0.25,但在密集场景下做了微调:alpha降到0.2,因为田间图的负样本实在太多,过高的alpha会让正样本的相对权重不足。具体观察指标是训练早期loss下降速度和验证集recall变化,调整完这两项后,1到20轮内的recall曲线明显更平滑,后期mAP也稳定在比默认参数高出0.8到1个点的区间。

正负样本匹配使用标准IoU策略:与某个真实框的IoU大于0.5的anchor作为正样本,小于0.4的作为负样本,介于0.4和0.5之间的直接忽略。在实际实现上,我在数据加载阶段还做了一次均衡采样,保证每张图里正样本anchor的数量在32到128之间,不然极端情况下正样本太少会导致训练不稳定。

4. 训练过程与核心环节实现

4.1 环境配置与训练参数设定

训练环境是单张高性能GPU,PyTorch框架,模型代码基于现有的检测库模板改了backbone部分。输入分辨率统一resize到800×1333,对应长边1333、短边800,这个尺寸在平衡细节保留和显存占用上比较合适,实测再往上提没有什么收益。

优化器选择AdamW,weight decay设为0.05,初始学习率2e-4,训练策略是warmup加余弦退火。前500步做warmup是因为Transformer类的backbone对初始学习率非常敏感,如果直接从2e-4开始,前几个iter就会出现严重梯度震荡——我第一版直接踩了这个坑,loss值在前200步里直接从5掉到负数再跳回8,模型完全跑飞了。换成warmup后,梯度更新立刻变得平稳。

batch size设了8(单卡能承受的上限),总训练轮次60轮。大约在第45轮之后loss曲线就平缓了,后续再加轮次只对小类别的边界框回归有微弱提升。为了进一步压缩训练时间,我还在第30轮和第45轮分别做了学习率衰减,每次乘以0.1。

4.2 关键代码实现与参数加载细节

先看backbone部分的配置参数,这是整条链路里最容易写错的一环:

import torch from torchvision.models import swin_t # Swin-T 关键配置:P4-W7 语义对应 Patch=4, Window=7 backbone = swin_t(weights="IMAGENET1K_V1") # 输出通道对应 [P3, P4, P5],分别来自 Swin 的 Stage2/Stage3/Stage4 out_channels = [192, 384, 768]

这段代码里要特别指出的是out_channels的顺序:Swin-T四个阶段输出通道分别是96、192、384、768。FPN只用到后三个阶段,因此P3对应192通道、P4对应384通道、P5对应768通道。我第一次接的时候把顺序写反成[768, 384, 192],结果模型输出层的维度对不上,报错信息又很晦涩,排查了半天。

RetinaNet检测头的关键初始化部分:

# RetinaNet 分类子网,每层 anchor 数为 9 num_anchors = 9 num_classes = 6 cls_subnet = nn.Sequential( nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1), nn.ReLU(inplace=True), nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1), nn.ReLU(inplace=True), nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1), nn.ReLU(inplace=True), nn.Conv2d(256, num_anchors * num_classes, kernel_size=3, stride=1, padding=1) )

分类子网最后一层输出是num_anchors乘以num_classes,因为每个位置要预测9个anchor各自在6个类别上的置信度。回归子网类似,只是最后输出是num_anchors乘以4,对应4个边界框坐标偏移。这里如果只写num_classes而漏掉num_anchors,模型能跑,但精度一塌糊涂——每个位置只能给一个anchor输出,其余8个anchor全部无效。

4.3 推理流程与参数选择

推理阶段的核心参数是置信度阈值和NMS阈值。远距离昆虫检测的目标是辅助人工计数,宁可多框一点也不能漏掉重要害虫,所以置信度阈值我设置在0.35左右,比日常通用检测的0.5低不少。NMS的IoU阈值设为0.6,比默认的0.5高一些,因为密集场景下两只紧挨着的虫子如果阈值太低会被合并成一个框,基于这个框统计的数量信息就废了。

推理速度方面,Swin-T在800×1333分辨率下单张图大约需要85毫秒,折合FPS接近12。对于虫情监测这种非实时任务完全够用——实际部署里可能每隔10分钟采集一张图,模型推理时间根本不算瓶颈。

我在部署时还额外做了一层筛选逻辑:检测框面积小于50像素的实例,但置信度高于0.6时仍然保留。这主要是因为某些螟虫类的幼虫很小但危害极大,不能因为目标小就直接过滤掉。如果你的场景里小目标并不重要,可以把这个小面积过滤逻辑关掉,减少一部分误检。

4.4 评估指标与实测结果

我这边使用的评估指标是COCO风格的mAP,重点关注的是小尺度目标的AP指标(AP_small)。最终测试集(含1500张未参与训练的场景图)上的表现大致如下:

指标数值说明
mAP@0.50.91中等阈值下的总体精度
mAP@0.5:0.950.68严格尺度下的综合表现
AP_small0.62小目标单独评估,关键指标
AP_medium0.79中等目标表现良好
平均召回率0.87按类别简单平均

横向对比我的对照组:同样数据集上配合ResNet-50骨干+FPN的RetinaNet,mAP@0.5约为0.85,AP_small只有0.51。Swin-T给AP_small带来的提升有11个百分点左右,这个幅度在目标检测工程里算是相当可观了。

5. 常见问题与排查技巧实录

5.1 模型Loss不下降或剧烈震荡

这个问题的头号原因是学习率过高和warmup缺失,尤其在Transformer类骨干上。我之前在Swin-T上做了对比实验,结果是:没有warmup的直接从头训练,前十轮loss在4到9之间反复横跳,训练几乎报废;加了500步线性warmup后,loss在初期就稳定下降到2左右。

第二个常见原因是batch size过小导致BN统计量不稳。Swin-T这类模型在单卡batch为2到4时,如果FPN层还带着BatchNorm,训练曲线就会非常抖。我的做法是用GroupNorm替换FPN层里的BatchNorm,或者直接关掉这些层的BN更新,二选一都能显著改善稳定性。顺带说一句,很多检测库在加载预训练backbone时会默认冻结BN,这一步务必在代码里显式确认。

5.2 小目标大面积漏检

漏检的原因通常不是模型能力,而是数据预处理或anchor配置不匹配。排查顺序建议如下:先看类别实例在输入图上的像素面积分布,再看第一个FPN层的anchor尺寸是否覆盖到了这些面积,最后确认多尺度训练时小目标没有被随机裁剪裁剪掉。

我遇到过一个非常隐蔽的问题:在代码里使用了一张图随机翻转的增强,某些小目标翻转后移动的位置恰好在下一轮增强的裁剪边缘附近,导致大量正样本实际上是不完整的。解决方法是关闭随机裁剪,或者将裁剪比例限制在0.8以上,保证小目标很少被切碎。

5.3 误检率过高:背景纹理被识别成虫

远距离拍摄的昆虫图里叶脉、泥土颗粒、甚至相机的噪点都可能被模型当作虫子框出来。我试过一个很有用的负样本策略:收集500张完全没有昆虫的纯背景图,把它们作为负样本加入训练集。让模型在训练中见过足够多的背景纹理样本后,误检率直接降了一半。

另外可以在后处理阶段加一个简单的空间过滤逻辑:由于诱虫板的位置在图像中基本固定,可以预先框定一块检测区域,区域之外的目标全部忽略。这个区域过滤操作看着笨,但在实际部署中遇到复杂野外地貌时特别实用。

5.4 密集粘连场景下NMS打架

粘虫板上几百只虫子挤在一起,检测框相互重叠严重,NMS很容易把相邻同类框合并掉,导致计数偏少。如果你评估的是“是否检测到”而不是“细粒度位置”,可以考虑把NMS阈值提高到0.7,并在评估阶段改用基于中心点的去重策略。另一个替代方案是训练一个额外的密度回归分支,把检测任务拆成“检测+密度估计”两个输出,不过实现成本比较高,建议先调NMS参数做一版再说。

5.5 远距离图像模糊与运动拖影

拍摄距离远了之后,昆虫只要轻微移动就会在画面里留下拖影,外观变成一个拉长的色带。这类样本在推理时非常容易漏检。我的经验是不要把模糊样本全部当作脏数据扔掉,而是保留一部分并标注成对应的类别,让模型学会在模糊特征中以轮廓和色彩作为判别依据。实验结果显示,加入10%~15%的模糊样本后,模糊子集上的召回率从0.34提升到了0.68,整体AP却没有明显下降。

这是我在实际测试里一个挺重要的心得:田间实拍图的质量差异远比公开数据集大,单一追求“清晰样本精度”对落地没有意义,关键是让模型在真实模糊条件下不掉链子。

6. 从训练到部署:一套可以直接抄的优化清单

6.1 我最终采用的完整参数表

把整套配置整理成一个可复现的参数清单,方便你直接对照参考。这些数值来自大量实验和实际田间数据的验证,结合你自己的相机参数可能有微调空间,但整体框架是稳妥的。

配置项推荐值备注
骨干网络Swin-T(P4 W7)ImageNet权重预训练
输入分辨率800×1333长边固定,短边等比
FPNP3/P4/P5,通道256未启用PANet
Anchor基础尺寸16/32/64按P3/P4/P5对应
Anchor宽高比0.5/1.0/2.0每层3种
Anchor缩放0.75/1.0/1.25每层3种
Focal Loss alpha0.2负样本极多时调小
Focal Loss gamma2.0维持默认
优化器AdamWweight_decay=0.05
初始学习率2e-4使用warmup
训练轮数6045轮后基本收敛
batch size8单卡实测可行
推理置信度阈值0.35按计数需求调整
NMS IoU阈值0.6高于默认值

这套配置的核心特征是为小目标和密集场景做了大量针对性调整,不适合直接套用到其他类型的检测任务。如果换了场景,anchor尺度和loss参数都需要重新实验,不能机械照搬。

6.2 部署层面的补充建议

部署框架上,如果你追求最大推理速度,可以考虑把训练好的模型导出为对应的推理引擎格式,过程中注意算子的兼容性。但实际上我在TX2级别的边缘设备上实测,Swin-T在浮点精度下跑一张图大概需要350毫秒,如果在设备上开启半精度推理,耗时能压到180毫秒左右。控温散热做好之后长时间运行也很稳定。

内存和显存上,Swin-T加RetinaNet的完整模型权重约120MB,这在边缘设备上完全可以接受。如果你需要进一步压缩,可以考虑把FPN通道从256降到192,AP大约下降0.3个百分点,但模型体积减少约20%,算是一个在存储极端受限时可以启用的折中方案。注意不要随便压缩backbone通道,Swin-T的通道数本来就紧张,再压特征表达能力会明显变弱。

6.3 项目可扩展的方向

这套“远距离小目标检测”的技术路线不只能用在昆虫上。我后来把这个框架快速迁移到另一个场景——收集某试验田里的益虫与害虫数量,目标是评估生物防治效果。整个迁移过程只花了大概两周:重新标注了1200张图,anchor尺寸微调了一版,冻结backbone只训练检测头,最终同类指标达到mAP@0.5:0.88。这说明backbone加检测头的架构有很强的可迁移性,只要数据层面做扎实,就能快速复制到相近领域。

后续如果你想让系统进一步自动化,可以在检测输出后接一个按区域计数的模块,把目标位置映射回真实物理坐标,结合诱虫板的已知尺寸,直接生成每亩虫口密度热力图。这个方向对植保决策的意义非常大,也是我认为这个项目最值得继续深入的地方。

最后分享一个我在整个项目里最有价值的经验

不要迷信模型结构,要迷信数据分布分析。第一次在远距离昆虫数据上跑Swin-T+RetinaNet时,我的baseline精度非常差,一度怀疑选错模型,后来认真统计了标注框的面积分布,才意识到问题是anchor尺度完全错位。把anchor调对之后,同样模型、同样训练轮数,精度直接跳升了两个档位。所以如果你要复现这个项目,我建议先拿一天时间好好看一下自己数据的标注分布,再动手调模型参数,这会比盲目试Swin-B、Swin-L那些更大的模型要有效得多。模型再强,也得先让anchors“够得着”你的目标,这在远距离小目标场景里几乎是决定成败的第一原则。

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

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

立即咨询