做农业视觉项目的朋友,基本都经历过从“模型跑通”到“田间可用”之间的巨大落差。我们最近负责的青香蕉尺寸分类与检测项目,就是把cascade-rcnn_hrnetv2p-w40-20e_coco这套配置从官方的模型库挪到了真实的果园环境里,整个过程最大的感受是:真正难的从来不是把模型训练收敛,而是小尺寸香蕉手在整幅图中的存在感。这篇内容没有花哨的包装,只讲我们怎么拆需求、做数据、调模型、部署上线,以及踩过的那些坑。如果你也被农业场景里的小目标检测折磨过,或者想了解Cascade R-CNN和HRNet这对组合为什么适合做精度敏感的分级任务,这篇应该能给你一些参考。
1. 从“香蕉分级”业务到“检测小目标”的技术映射
1.1 需求拆解:尺寸分类到底分什么
“青香蕉尺寸分类”这几个字,第一反应是用图像分类模型对每根香蕉分类,但真正到产线需求时,业务方要的是“检到每一只手,再决定这只手属于哪个尺寸等级”。这里的“手”指的是香蕉果穗上自然分出的一组果实,通常是5到10根果指围成一个扇形簇,整串香蕉由若干只手组成。产线上分拣机器人或者分级传送带需要知道每只手的位置,才能决定是否切分、流向哪个包装通道。所以本质上这是一个目标检测任务,只不过类别不是“香蕉”,而是“香蕉手”;标签不只是位置,还包括尺寸等级。
尺寸等级到底怎么划分?我们对接的企业给的标准是按果指长度和手宽综合判断:单根果指长度超过20厘米的算大果,15到20厘米算中果,低于15厘米算小果。但这个标准在图像上不能直接量出来,需要通过检测框的宽高、果指数量以及果指的间距来做回归。我们前期尝试过直接回归“手宽像素”,后来发现更稳的办法是先用检测框把目标锁住,再用框内的像素信息(比如骨架线)估算果指长度,而不是让端到端模型直接吐一个尺寸值。
所以技术链路拆开就是两段:第一段用检测模型输出每一个香蕉手的外接框,第二段根据检测结果里的几何特征做尺寸分级。第一段是瓶颈,因为如果你连小尺寸的手都漏掉了,后续分级写得再好也没用。也正因为这样,项目的大头全部压在目标检测上。
1.2 为什么小尺寸目标是最大的拦路虎
把“香蕉手”作为检测目标,它的尺寸天然就比“整串香蕉”小一个量级。在固定高度3米左右拍摄的图像里,一只靠近远端的小手可能只有40×80像素,即便在1080P的图上,占比也远低于COCO标准里的小目标定义。更麻烦的是香蕉叶和背景都是绿色,青色香蕉的轮廓对比度不高,在逆光条件下,果指之间的间隔几乎看不清。
这里要说明一个常识:检测框架的数据集预训练权重大部分来自COCO,COCO的默认anchor和FPN层都是按自然图像的常见目标尺度设计的。拿过来直接跑青香蕉,小目标特征会随着网络下采样被“磨”得越来越淡,最后在FPN的P2层也未必能留下足够强的响应。我们后来统计过初版模型的漏检样本,80%以上是远端小手和被叶子遮挡一半的手,很少是那种已经长得很大、轴部露出来的目标。
还有一个业务层面的麻烦:分类和检测是一体化需求。尺寸等级判断依赖边框的像素大小和比例,如果模型给出的框有摆动,分级结果就会在阈值附近跳来跳去。一阶段模型比如YOLO系列在同等FLOPs下框的稳定性通常不如两阶段模型,这也是后面选型的核心考虑之一。
2. 数据准备:标注规范与“看不见”问题
2.1 采集和筛选:尽量把坏事先挡在相机外
这个项目的数据来自南方某香蕉种植企业的两个园区,我们要求现场拍摄使用固定高度相机,机位距离香蕉串约1.5到2米,画面覆盖一到两串完整香蕉,不要故意把整片果园拍进去。分辨率至少1920×1080,最好用原图保存,不要经过社交软件压缩。拍摄时段选在上午10点到下午3点之间,避免早晚低角度光把果指缝隙照出一片高光。
采集完成后,按质量清洗了一批问题图像:严重过曝的、模糊的、被麻袋包装完全遮挡的,直接删除。这一步非常重要,你不清洗,后面标注员就会按自己的理解把遮挡目标标成不同形状,造成框的标准不一致。我们最后留存有效图像约12000张,其中青香蕉状态占90%左右,另外补了一些半熟状态,目的是让模型不至于在青色特征上过拟合。
这里有一个容易被忽略的坑:很多现场照片是竖屏拍摄的,而检测框架预设的训练输入通常是横屏。我们做了统一处理,长边统一缩放到1333,短边对应的最长不超过800,如果竖图短边太小,就按短边补边到800,而不是强行压缩,否则目标会变形。补边操作在推理时也要保持一致,否则模型结果会打折扣。
2.2 标注标准:边界怎么定直接决定训练稳定性
香蕉手的边界在图像里不是天然清晰的。我们给标注员定的标准是:从果穗主轴的分叉位置开始,沿外围所有果指的轮廓做多边形,直到最末端的果指曲率转折处为止。一只“手”必须包含轴上的分叉起点,如果只标果实不标轴部,训练出来的框会整体偏上,且框高会漂移,直接影响后续尺寸估计。
标注格式用COCO的json格式,每个目标一个category_id,其中类别包括bunch_hand、small_hand、medium_hand、large_hand四种。但是问题来了:如果标注阶段就直接写死尺寸等级,模型不仅要做位置检测,还要做尺寸分类。这么做有个隐患——不同尺度等级在图像上的分布不均匀,小尺寸类别的样本远少于大尺寸。我们最终先按统一的目标类别banana_hand标注,检测模型只负责找出手的位置,尺寸等级交给后处理根据检测框的尺度和果指视觉特征回归。
这样标完大约产生了2.8万个目标框,其中小于64×64的目标占45%。后期为了不浪费这部分数据,我们把图像里的目标都做了裁剪,以目标为中心,周围加40%的上下文,生成2000多个小图patch。之后再用混合策略把这些patch随机贴回大图参与训练,让模型看到更多小目标样本。这个操作看起来土,但对小尺寸AP的提升非常直接。
2.3 数据增强:不是越多越好,要跟小目标匹配
我们采用的数据增强主要围绕“让目标以不同尺度出现”来设计。随机翻转、随机光照扰动、随机HSV色彩抖动是基础。重点是多尺度随机缩放:训练时输入图的短边随机取640到1024,长边对应缩放,这样同一只手在一半的epoch里是小目标,在另一半里是中等目标,避免模型只对大目标敏感。
另一个有效手段是把两张小图混合成一个batch,比如Mosaic增强。香蕉图像之间没有特别强的语义依赖,Mosaic之后背景噪点变多,对提升泛化有一定帮助,但对小目标并不总是正向作用。我们在消融实验里发现,如果Mosaic增强权重过大,小目标会被几何变换“撕”得更碎,反而降低AP,所以最终只在前面一半的epoch开启Mosaic,后面关掉,让模型专注在精调上。
这里想提醒一句:别把数据增强当成万能药。先检查你的目标是活下来了还是被增强操作破坏了。我们在调试过程中,把增强后的图像可视化过一遍,发现随机旋转超过15度时,香蕉手的果指排列会严重变形,如果旋转中心又不在目标附近,就会产生大量无意义的伪样本。所以我们把旋转角度限制在±10度以内,水平翻转对半开。
3. cascade-rcnn_hrnetv2p-w40-20e_coco这套配置的选型逻辑
3.1 为什么不用YOLO,而是把精度放在第一位
选择Cascade R-CNN之前,我们其实用YOLO系模型快速跑过一版基线,在训练集subsample上的速度确实快得多,但最终的评估结果不理想。主要原因有三个:第一,一阶段模型对小于32×32的目标召回率明显低,很多远端小手在特征金字塔末端没有被分配为正样本;第二,我们的分级需要精确的边框去估算手宽,YOLO回归出的框在边界有轻微收缩或扩张,站在尺寸阈值附近时会产生约一个等级的误判;第三,农业场景对实时性没有想象中高,产线上一张图的处理时间只要在100毫秒内就满足,用两阶段模型完全能扛住。
这里不是说YOLO不好,而是选型要考虑任务类型。目标检测在通用场景下追求“找得到、找得快”,分级场景则更看重“框得稳、边界准”。Cascade R-CNN通过多级回归反复修正框,最终输出的位置和大小都比单级回归更稳定。我们也在测试集上统计了框与人工标注框的IoU分布,Cascade R-CNN在IoU=0.75上的表现比YOLO同精度模型高出约6个百分点,这对后面的尺寸分级非常关键。
| 模型 | 推理耗时 | AP50总 | 小目标AP50 | 框稳定性(IoU≥0.75比例) |
|---|---|---|---|---|
| YOLO系基线 | 约22ms | 79.3 | 64.1 | 61% |
| Cascade R-CNN + ResNet50 | 约65ms | 82.7 | 69.8 | 70% |
| Cascade R-CNN + HRNetV2p-W40 | 约80ms | 86.4 | 78.2 | 78% |
3.2 Cascade R-CNN的级联机制如何帮上小目标
Cascade R-CNN最核心的设计是串联多个检测头,每个头使用递增的IoU阈值(从0.5到0.6再到0.7)训练。这样做的好处是,前一个头输出的候选框会经过较高的阈值过滤,留下质量更高的正样本给下一个头继续回归,整体输出的框分布更集中,假阳性和低质量框会逐级减少。对小目标来说,这个机制格外有价值,因为小目标的可用像素少,单级分类器很容易被背景噪声带偏,级联机制相当于把每一步的特征都做了重采样,每次都在更严格的标准下重新学习“什么是真正的香蕉手”。
我们尝试过直接使用单个Faster R-CNN作为基线,小目标类的AP50只有61.2,换成Cascade R-CNN后直接到了68.3,虽然计算量提升了约1.4倍,但并没有带来显存压力。要注意的是级联头数并不是越多越好,三个阶段已经足够,再多会导致训练时正样本数量骤减,反而拉垮训练效率。我们试过把检测头堆到四个,结果AP不升反降,问题就出在第四个头的IoU阈值设到0.8之后,小目标样本本来就少,能满足这个阈值的匹配框数量急剧下降,梯度更新时小目标类的学习信号就更弱了。
3.3 HRNetV2p-W40:高分辨率特征的不可替代性
骨干网络也是针对小目标专门选的。HRNetV2p-W40的关键在于从输入到输出始终维持高分辨率分支,并通过多分辨率子网反复交换信息,不会像ResNet那样一路降分辨率到最后才恢复。对于青香蕉这种纹理细节集中在果指缝隙和轮廓边界的对象,保住高分辨率特征几乎等同于保住了小目标的“身份信息”。
W40指的是第一层通道数40的宽度策略,相比W18,W40有更宽的特征通道,能容纳更多语义信息,同时显存占用又比W48友好。实际跑下来,HRNetV2p-W40加上FPN后,P2层特征对小型香蕉手的响应明显强于ResNet50-FPN,尤其是在果指间隙的灰度接近背景时,高分辨率分支还能提供一组可用的边缘响应。我们对比过W32和W40,W32速度快约20%,但小目标AP低了约3个点,考虑到产线对速度的容忍度较高,最终还是选了W40。
当然也要承认,HRNet的缺点是训练和部署都比ResNet重。我们最初在单张消费级GPU上训练,batch size只能设为2,后来降到1加梯度累积才稳定下来。如果你手头算力有限,可以先用W18或W32把流程跑通,确定数据增强和后处理方案后再升级到W40,避免一上来就在参数调优上浪费时间。
3.4 20e和COCO预训练:这个后缀到底该怎么理解
标题里的20e表示训练20个epoch,_coco表示使用在COCO数据集上预训练的权重来初始化模型。COCO预训练对农业目标不是直接可用的,但它能提供可靠的边缘、纹理和objectness先验,启动速度远快于随机初始化。我们采用的训练策略是:加载cascade_rcnn_hrnetv2p_w40_20e_coco的权重,冻结前两个stage的骨干参数,只训练后面的stage和检测头,初始学习率设为0.0025,在第12和第16个epoch分别降为原来的十分之一。
为什么选20e而不是更长的训练?因为我们的数据量只有万级,模型容量已经足够,更长的epoch容易在小目标类别上过拟合,导致验证集AP先升后降。20e是一个相对平衡的经验值,配合学习率后,模型在最后几个epoch处于精调阶段,正好适合小目标的边界回归。如果数据量大或场景差异特别大,可以考虑30e甚至40e,但超过40e基本不会再带来正收益。我们后期还对比过12e的短训练,AP50比20e低约2.4个点,说明这个任务确实需要稍微充分一点的学习率衰减空间。
4. 训练细节与小目标优化实操
4.1 输入尺度和anchor修改:从默认值出发,但要打破默认
COCO预训练模型的默认输入尺度是长边1333、短边800,这个尺度对青香蕉其实偏小。我们把长边提高到1600,短边对应到1000,让远端小手在输入图像里至少保留50×100像素以上。这样做显存占用会变大,我们的batch size从4降到了2,但换来的是小目标AP提升了接近4个点。如果你显存不够,也可以采用“大图裁剪成patch”的方式,而不是整图直接放大,我们两种方案都验证过,patch方式对小目标的提升会更明显,只是标注和推理都要跟着改,工程成本更高。
Anchor设置上,默认的RPN anchor基础尺寸是8,配合FPN可以覆盖到32×32以上的目标。我额外增加了16、32两个更小的anchor尺码,并把长宽比从[0.5,1.0,2.0]扩展为[0.5,0.75,1.0,1.5,2.0],以匹配香蕉手“横向宽、纵向窄”的形态特点。调整后RPN的召回率(IoU=0.5)从72.3提升到79.6,说明很多小目标确实是被默认anchor漏掉的。
| anchor配置 | 小目标AP50 | RPN召回率 |
|---|---|---|
| 默认8尺度,ratio 3种 | 71.2 | 72.3% |
| 增加小尺度,ratio 5种 | 75.4 | 78.1% |
| 增加小尺度+调整正负样本比例 | 78.2 | 79.6% |
还要调整正负样本分配比例。默认RPN在每张图上保留256个proposal,其中正样本比例约0.5,对于小目标居多的数据集,我改成保留512个proposal,正样本比例上限0.7。这个动作牺牲了一点训练速度,但显著增加了小目标进入后续检测头的机会。
4.2 训练过程中的常见坑:loss下降不代表AP上涨
第一次训练跑满20个epoch,total loss从0.9降到0.15,看起来一切正常,但验证集AP一直卡在73左右。可视化检查后发现问题出在定位分支:小目标被检测到,但边框绕香蕉手轮廓来回抖动。原因是分类loss在快速下降过程中主导了梯度,回归分支对微小偏移不敏感。解决办法有两个:一是把回归loss权重从默认的1.0提到2.0,让模型更愿意去修正框位置;二是在RPN阶段提高IoU阈值,让进入检测头的proposal一开始就更贴合目标。这两个调整配合起来,AP75从61.1提升到66.9,说明框的稳定性有明显改进。
另一个很隐蔽的坑是BN层统计量的融合。HRNet在ImageNet和COCO上预训练时的BN统计量,迁移到香蕉场景后如果不重新统计,会在前几个epoch产生比较大的激活漂移。我们选择在前5个epoch冻结BN层,之后解冻,让模型在做迁移学习时先稳定特征提取器,再更新归一化参数。这个细节在没有分布式验证的时候看不出来,但确实影响最终收敛精度。
还有一点是关于学习率的。迁移学习任务里初始学习率太大会把预训练的特征结构直接冲乱,尤其HRNet这种高分辨率网络对学习率更敏感。我们最终选的是0.0025,配合Warmup前500步,训练过程比较稳定。如果你从随机初始化开始训练,可能需要把学习率放大到0.01或者更高,但那样整个收敛曲线会完全不同。
4.3 测试时的多尺度与Soft-NMS
到了推理阶段,我们也没有直接单模型单尺度过。测试时用了三个尺度:短边分别为800、1000、1333,每个尺度做水平翻转推理,最后把三次结果按Soft-NMS合并。多尺度融合的效果对小目标尤其明显,因为同一只手在更大尺度下会暴露出边缘细节,在更小尺度下会有稳定的全局上下文,合并后漏检率降低了约5个百分点。当然代价是推理耗时变成原来的6倍,产线上一旦接受不了,也可以只做两尺度,精度收益大概能保住六成。
Soft-NMS替代普通NMS也很关键。香蕉园场景里香蕉手之间靠得很近,普通NMS在IoU阈值0.5时会把相邻的两个手合并掉一个,我们换成Soft-NMS并按距离衰减置信度,能让紧密相邻的小手同时保留下来。这个操作在标注框重叠比例超过30%的测试片段上,每张图平均多找回0.8个漏检目标。
如果推理速度还有余量,建议对置信度在0.3到0.5之间的模糊框做第二次“放大后重检”。具体做法是把该框区域裁剪出来,放大到原图两倍大小,再过一次模型,如果第二次分类置信度超过0.5就保留,否则丢弃。代价是每张图多了约20毫秒,但对小目标的召回提升非常直观。我们最后产线部署时保留了这个逻辑,实际平均每张图高置信度目标增加约6%。
5. 评估、部署与田间实战
5.1 用AP而不是损失值衡量模型
评估脚本不能再只看total loss,我们按小、中、大目标分别统计mAP,因为COCO的默认metrics里包含AP small、AP medium、AP large。项目验收时也要求单独给出小目标召回率,这个指标比总体mAP更能反映产线真实情况。我们最终模型的AP50总体在86.4,其中小目标AP50是78.2,中目标AP50是90.1,大目标AP50是95.6。真实业务里,小目标漏掉一只,后续分拣就会把该手当成无果空位处理,带来直接经济损失,所以小目标AP50 78.2看着不如大目标好看,但已经是我们多轮优化后在实际应用里能接受的平衡点。
我们还画了不同置信度阈值下的精确率和召回率曲线,发现小目标类别在高置信度区间仍然有逼近65%的精确率,但召回率偏低,说明模型并没有把背景乱当成小手,更多是不敢确认。针对这个现象,我们把小目标类的分类阈值单独调低0.1,再配合后处理重检,召回率能进一步上升,而精确率只损失不到1.5个点。
5.2 从训练框架到部署:模型转换和精度问题
部署时把训练好的Pytorch模型导出成开放格式,再用推理引擎转换成生产环境需要的版本。HRNetV2p-W40里面有不少高分辨率分支之间的element-wise add和upsample操作,转换时需要用固定分辨率导出,否则动态shape下的上采样算子会报校验错误。我们最后固定输入为1×3×1600×1000,并用推理引擎的FP16模式测试。
FP16模式下整体模型显存占用降低了40%,但小目标AP从78.2掉到73.5,因为半精度在梯度传播和上采样时的数值截断对小目标响应非常敏感。产线上一旦AP掉了5个点,就意味着每天多漏掉不少小把手,得不偿失。最终我们选择FP32推理,单张图像的推理时间在80毫秒左右,满足产线要求。如果你一定要用FP16,可以考虑对检测头里的回归层单独保留FP32,而不是整模型半精度,这个折中方案在一些推理引擎里可以实现。
5.3 尺寸分级后处理的回归逻辑
检测完成之后,尺寸分级不是一个简单的按检测框面积阈值切分。我们结合业务标准训练了一个简单逻辑:对每个检测框计算宽高比、框内果指数量估计、沿果轴方向的颜色梯度,再用一个线性回归模型映射到果指长度,最后根据长度落入哪个区间来定等级。这样做的好处是鲁棒。检测框本身随着拍摄距离会缩放,如果直接按像素面积划分,同一个物理尺寸在不同拍摄距离下会被判成不同等级。我们额外标定了一个深度归一化系数,把像素测量值转换成以厘米为单位的实际尺寸,再代入分级阈值。
这个后处理还兼顾了“手”被遮挡的情况。比如一只手只有四根果指可见,框的面积看起来偏小,但果指的间距和颜色梯度仍然能推断出它已经属于中果档位,线性回归模型会给出一个比面积法更合理的预测。我们对比过两种方案:单纯面积法在人工抽查里的准确率是84.3%,加上几何特征回归后提高到91.7%,明显降低了产线误分级。
5.4 真实验收中的额外经验
项目验收阶段我们才发现,数据集里的“小目标”和产线实际场景里的“小目标”含义并不完全一致。产线上目标更靠近相机,小目标更多是“部分被遮挡的小手”,而不是“远离相机的小手”。因此我们在验收前专门采集了一段遮挡场景的视频,加入测试集后小目标AP又掉了约2个点。后来在训练集里补充了人工合成遮挡样本,把塑料袋、叶片和绳子贴在目标上,这才把测试指标拉回来。这个对比说明,做农业视觉项目,测试集的组成要尽量贴近真实部署位形,否则实验室指标好看,上线该崩还是崩。
最后分享一个非常朴素的体会:模型配置只是开始,真正决定项目落地的是数据采集标准、标注规范以及后处理逻辑的闭环。cascade-rcnn_hrnetv2p-w40-20e_coco这套组合适合精度要求高、推理设备有富余的场景,对我们这样的小规模农业视觉场景是完全够用的。如果未来要上移动端,我会优先考虑把HRNet换成更轻量的骨干,并用模型蒸馏去弥补精度损失。