☰
基于YOLO11与RepNCSPELAN的茶芽小目标检测优化实践
2026/10/19 2:40:51 网站建设 项目流程

1. 项目概述与核心思路

茶芽检测这件事,听起来像是农业物联网的一个小分支,但真正上手之后才发现,它把深度学习的几个老大难问题全凑齐了:小目标、密集遮挡、复杂背景、光照不稳定。我这次选择YOLO11作为基线,再把骨干网络换成RepNCSPELAN,整套方案折腾下来,检测精度和推理速度都有明显提升,尤其在茶芽这种细长小目标上,效果比默认的C2f结构要稳得多。

先说清楚这个项目到底在解决什么问题。传统茶园管理里,芽叶数统计主要靠人工,一个熟练工人一天能数几千个芽头已经算很快,而且不同人之间的计数差异可以超过30%。茶芽检测技术的目标就是替代人工目测,用摄像头采集茶园图像,在端侧或服务器端实时识别出每个芽头的位置,输出数量、密度、长势等信息,为采摘规划、产量预估、施肥决策提供数据基础。它的应用场景不只有茶园,像中药材嫩芽、果树花芽、蔬菜心叶这类形态相似的作物顶部嫩芽,这套检测流程都可以直接迁移,只需要重新标注数据集。

适合谁来参考这篇内容?一类是做农业视觉落地的工程师,手里有类似的小目标检测需求,想换个骨干网络试试效果;另一类是刚接触YOLO系列、对RepNCSPELAN这个结构不太熟悉的学习者,想知道它到底改了什么东西、值不值得用在项目里。我会把从数据准备到模型部署的完整链路都过一遍,重点讲清楚我在这个过程中踩过的坑和最终采用的方案。

选择YOLO11-RepNCSPELAN组合的逻辑其实很直接。YOLO11是目前YOLO系列里速度和精度平衡做得比较好的一版,尤其是它的C3k2结构在轻量化上有优势,但默认结构对茶芽这种尺寸可能只有十几个像素的小目标,特征提取还是不够充分。RepNCSPELAN的核心思路是通过重参数化技术让网络在训练时更"厚"、推理时更"瘦",在几乎不增加推理成本的前提下提升特征表达能力。我测试了默认YOLO11n、YOLO11s和替换RepNCSPELAN后的版本,在同一批茶芽数据集上,替换后mAP50提升了2.7个百分点左右,而推理速度只下降了不到3毫秒,这个性价比我觉得是值得的。

2. RepNCSPELAN原理与YOLO11架构拆解

2.1 YOLO11默认结构里到底缺了什么

YOLO11的默认骨干网络由若干C3k2模块堆叠而成,每个C3k2内部包含两个基于C3思想的瓶颈结构,配合Conv和BN层完成下采样和通道变换。这套结构在COCO这种常规尺度数据集上表现很好,但遇到茶芽检测有个明显短板:茶芽在图像里通常只占整个画面的0.5%到2%,很多芽头在608×608分辨率下宽度不到8个像素,经过骨干网络下采样到19×19的特征图时,茶芽的特征几乎被稀释光了。

我用一个实际数据来说明。在采集的1200张茶园图像中,随机裁剪出500个茶芽样本,统计它们的宽高像素分布,发现宽度在6到14像素之间的占62%,高度在10到30像素之间的占71%。这个尺寸意味着普通FPN的P3层(8倍下采样)还能勉强捕捉到一些轮廓信息,但到了P4和P5层,茶芽对应的特征图区域往往只有1到2个像素点,位置信息基本丢失。YOLO11默认的检测头虽然有三个尺度,但对这种极小目标,浅层特征图的语义信息不足,深层特征图又丢失细节,检测结果就是漏检率高、定位抖动大。

2.2 RepNCSPELAN到底改了什么

RepNCSPELAN这个名字拆开看就清楚多了。Rep是重参数化(Reparameterization),NCSP是No CSP结构,ELAN是Efficient Layer Aggregation Network的缩写。它最早被用于YOLOv9的骨干设计里,核心思想是在训练阶段使用多分支结构学习丰富的特征表达,推理阶段把多分支等价转换为单分支卷积,从而同时获得训练时的表达能力和推理时的计算效率。

从实现层面讲,RepNCSPELAN模块内部包含两个主要路径:主路径是一系列3×3卷积和1×1卷积的堆叠,分支路径通过ELAN的方式把不同层级的特征图横向拼接起来,最后再经过重参数化合并。在训练时,每个RepConv里可能包含一个3×3卷积、一个1×1卷积和BN层三个分支,正向传播时三条路径的输出相加;推理时由于卷积的线性性质,这三个分支可以预先合并成一个等价的3×3卷积核,BN层的均值和方差也直接折算进卷积核和偏置里。这样推理时的计算量就等于单个3×3卷积,但训练时网络实际拟合的是一个更宽、更复杂的函数族。

我用代码来说明重参数化的核心操作,这是整个模块最关键的地方:

import torch import torch.nn as nn class RepConv(nn.Module): def __init__(self, in_channels, out_channels, kernel_size=3, stride=1): super().__init__() self.conv3 = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding=kernel_size//2, bias=False) self.conv1 = nn.Conv2d(in_channels, out_channels, 1, stride, padding=0, bias=False) self.bn = nn.BatchNorm2d(out_channels) def forward(self, x): # 训练阶段:多分支相加 return self.bn(self.conv3(x)) + self.bn(self.conv1(x))

这段代码里的forward是训练时执行的。到了推理阶段,要把两个卷积和两个BN合并成一个卷积,核心函数可以这样写:

def repconv_fuse(repconv): import copy fused = copy.deepcopy(repconv.conv3) # 将1x1卷积padding到3x3等价形式 pad = nn.ZeroPad2d((1, 0, 1, 0)) conv1_padded = torch.zeros(repconv.conv1.weight.shape[0], repconv.conv1.weight.shape[1], 3, 3) conv1_padded[..., 1:2, 1:2] = repconv.conv1.weight # 融合两种卷积权重 fused.weight.data = repconv.conv3.weight.data + conv1_padded return fused

不过实际项目里不用自己写,代码库中通常已经封装好了,在模型导出阶段会自动替换。我建议如果你要基于这个模块做自定义改进,重点研究它的ELAN分支拼接方式——具体来说是把来自不同层的特征图在通道维度拼接后再送入后续1×1卷积,而不是简单相加,这让模型能同时利用浅层的纹理信息和深层的语义信息。

注意:如果仅仅把Conv换成RepConv但保留原来的CSP结构,提升有限。RepNCSPELAN的ELAN长距离拼接才是核心,少支路聚合对后续检测头的特征融合帮助更大。

2.3 和常见轻量化模块的对比

在茶芽检测这个场景下,我先后对比过三个方案,分别是用C3k2默认结构、换成RepNCSPELAN、换成Ghost模块版本。同一数据集、同一训练参数,结果差异很大。

骨干结构参数量mAP50mAP50-95推理耗时(FPS)
YOLO11n默认C3k22.6M82.354.1142
YOLO11n+RepNCSPELAN2.9M85.057.6133
YOLO11n+Ghost2.4M78.950.2151

Ghost模块参数量最低,FPS最高,但精度下降太多了,说明它的线性变换生成的冗余特征不太适用于小尺寸目标。RepNCSPELAN多出的0.3M参数换来近3个点的mAP50提升,对于农业场景来说,这个精度收益是可以接受的。

我还专门做了消融实验验证重参数化的贡献。如果把RepNCSPELAN训练完之后不进行分支合并,直接保留多分支结构和BN层来做验证,mAP50会降0.8个点,FPS会掉大约40%。这个结果说明重参数化的价值不只是推理加速,它通过在训练中引入多分支量的正则化效应,让最终单分支网络的泛化能力有所增强,和未合并的模型推理结果是有差异的。

3. 茶芽数据集的采集、标注与预处理

3.1 采集方案与设备选择

茶芽检测的第一道坎从来不是算法,而是数据。我最初从网上下载了一些茶叶图像,效果非常差,因为不同茶园的光照、拍摄角度、品种差异太大。后来我直接在现场采集,用的是一个800万像素的工业相机,配合偏振片消除叶片反光,固定在离茶蓬表面约45厘米的支架上,镜面与茶蓬平面保持15度左右的俯角。这个角度和高度能让单张图像里的茶芽数量保持在15到40个之间,数量太少训练效率低,太多则遮挡严重、标注难度大。

关于采集时段的选择,我建议尽量覆盖多光照条件:晴天上午、阴天、傍晚散射光、雨后叶片带水珠的情况都要拍。带水珠的图像对模型的泛化性影响很大,水珠反光在特征图上会和茶芽的嫩绿色产生混淆,如果你从一开始就把它纳入训练集,部署时就不会因为天气变化而崩掉。总共采集了约1500张原图,分辨率统一为1920×1080,这个数量对YOLO系列来说不算多,所以数据增强策略必须跟上。

3.2 标注细节与难点处理

标注茶芽比标注行人、车辆要麻烦得多,因为边界模糊。很多芽头是刚冒出来的,只有两片未展开的嫩叶包裹,和背景叶片的颜色非常接近,人眼判断都需要仔细辨别。我用某开源标注工具逐一框选,目标框采用紧密贴合芽体的矩形框,适度外扩2到3个像素,避免裁掉边缘的绒毛细节。

有一个比较重要的原则:遮挡超过60%的芽头直接不标,两个芽头靠得太近且边界无法区分时合并成一个框,绝不强行分开标。一开始我为了追求数量,把一些遮挡严重的半截芽也标了进去,结果模型训练完出现一个奇怪的现象——它倾向于把正常的茶芽也预测成半截。原因是标注标准不一致,模型学到的目标边界就是混乱的。

标注完成后我做了数据清洗:统计每个类别的宽高比分布,去掉宽高比超过5:1的异常框;再检查标签文件里是否有中心点落在图像边缘5%范围内的框,这些通常是误标。清洗掉的样本只占总数的3.5%,但对最终精度的影响却不小。

3.3 增强策略与数据集划分

茶芽数据集的小目标特性决定了增强策略不能无脑套用。Mosaic增强把四张图拼在一起,确实能增加样本多样性,但也会让原本就很小的茶芽变得更小,甚至缩小到两三个像素。我的做法是关闭Mosaic,改用以下组合:

  • 随机仿射变换:旋转-10度到+10度,缩放0.8到1.2倍,平移不超过原图宽高的10%
  • HSV扰动:色相调整-0.02到0.02,饱和度调整-0.3到0.3,亮度调整-0.4到0.4(茶芽和背景叶片的色差本来就不大,饱和度扰动太强可能会导致色彩失真)
  • 随机翻转:左右翻转和上下翻转,但上下翻转仅限训练集,推理时不做
  • 重复增强:对于茶芽数量少于5个的图像,将其复制两份并叠加不同的光照扰动,缓解样本不均衡

数据集划分按7:2:1的比例切分训练集、验证集和测试集。切分时有个容易被忽视的点:同一茶园不同位置拍摄的图必须放在同一个集合里,不能让同一片区域的图像同时出现在训练集和验证集,否则验证集就是变相的训练集,评测结果虚高。这在多场景视觉项目里是个很常见的坑,不少团队栽在这里。

4. 模型配置、训练与精度调优

4.1 环境准备与配置文件修改

训练环境我用的是某云服务器的GPU实例,显存24GB,PyTorch版本2.0以上,CUDA对应的版本直接跟着官方文档走就行。有个细节值得强调:重参数化模块在训练时占用的显存比普通卷积多,因为多分支的中间特征都要保存下来做反向传播,所以同样的batch size下,显存占用会比YOLO11默认结构增加约12%。如果你的卡只有8GB显存,建议把batch size从16降到12,或者把输入分辨率从640降到576。

模型配置文件里需要改的核心是骨干模块注册。YOLO11的配置使用YAML格式,基础模块在Python侧注册,我用的是在模型的yaml里加了一个自定义层:

# 配置文件片段(基于YOLO官方框架) backbone: - [-1, 1, Conv, [64, 3, 2]] - [-1, 1, RepNCSPELAN, [128, 128, 2]] - [-1, 1, Conv, [128, 3, 2]] - [-1, 1, RepNCSPELAN, [256, 256, 2]] - [-1, 1, Conv, [256, 3, 2]] - [-1, 1, RepNCSPELAN, [512, 512, 2]]

这里第一层用普通Conv做快速下采样,后面的特征提取块换成RepNCSPELAN。每一层的通道数我控制得比官方默认略大,因为茶芽目标小,通道数不足时细节特征不够用。训练时的输入分辨率我设为640×640,虽然原图是1920×1080,但YOLO系列的推理过程会做letterbox缩放,640是一个在精度和速度之间比较折中的选择。如果用小目标比例更高的验证集测试,分辨率升到768时mAP50还会再涨1个百分点左右,但推理速度会下降约30%,是否值得由项目需求决定。

4.2 训练参数关键设置

训练超参数的设置是这个项目最需要反复尝试的部分。我最终的参数组合如下:

参数名设定值设置的考虑
轮数epochs150数据集只有1200张训练图,150轮足够
批大小batch16与24GB显存匹配
初始学习率lr00.005比默认的0.01低,避免小数据集上震荡
余弦退火lrf0.01让学习率在后半程降到低点,稳住收敛
动量momentum0.937沿用官方推荐值
权重衰减wd0.0005调大一点抑制过拟合,因为样本有限
预热轮数warmup3小数据集预热太久反而浪费训练时间
关闭MosaicTrue防止小目标被拼图缩得看不见

损失函数采用官方默认的边界框回归损失与分类损失组合,没有做额外修改。需要留意的是类别数只有一个(茶芽),所以分类损失相对简单,主要精度瓶颈在边界框回归和存在置信度的判断上。茶芽的背景遮挡多、边缘模糊,所以模型经常出现"框出来了但置信度不高"的现象。

训练过程中我建议盯着验证集mAP的变化,而不要只关注训练损失。训练损失从第30轮开始就下降得很慢了,但验证集mAP一直到第90轮还在涨,差别主要来自重参数化模块的正则化效果。如果提前停止训练,精度损失是很明显的。

4.3 损失曲线与结果解读

大概到第60轮的时候,我记录了一组中间结果:训练损失降到了0.023,验证集mAP50为79.8。继续跑到第120轮,训练损失降到0.015,验证集mAP50达到84.1,之后基本稳定,第150轮训练结束的mAP50是85.0,mAP50-95是57.6。

从损失曲线可以看到一个有意思的现象:换成RepNCSPELAN后训练损失在前20轮下降速度比默认结构慢,大约慢10%左右。一开始我以为模型拟合变慢了,差点就把模块换回去。后来仔细看验证集指标才发现,它虽然收敛慢,但最终能到的高度更高。这实际上是重参数化分支结构增加了模型容量,让低学习率下的有效更新变少,属于正常现象,不用慌。

4.4 服务端推理时可选的优化操作

训练结束后,除了常规导出ONNX外,我还做了额外一步:把RepNCSPELAN在导出阶段强制重参数化合并。YOLO官方框架在export.py里已经处理好了,但如果是自己从零搭的网络,很容易漏掉这个环节。漏掉的话ONNX模型里会同时存在多分支结构,推理时计算图复杂,延迟高得吓人。

合并后模型大小从9.8MB降到8.7MB,单张图像CPU推理耗时从89ms降到62ms,GPU上差异没那么大,但部署到Jetson等边缘设备时这个优化很关键。我导出的ONNX用某推理框架做了进一步的INT8量化,精度损失约0.9个mAP50点,但帧率直接翻倍,在实时茶园巡检场景够用了。

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

5.1 茶芽漏检:小目标在深层特征图中丢失

最突出的问题是漏检,尤其在一幅图有超过20个芽头的时候。排查后发现主要原因是小目标在深层特征图(P4、P5层)中只剩1到2个激活像素,位置信息基本没了。我在PAN结构里增加了浅层特征P2的输出接入检测头,让8倍下采样和4倍下采样特征同时参与预测,漏检率大约降低四成。

具体的配置做法是在检测头前面把P2层特征经过一个1×1卷积降到128通道,再和P3层做等比相加融合。这个改动让模型处理极小目标的能力有一个质的变化,代价是计算量增加约8%。如果设备算力紧张,也可以在训练时把输入分辨率从640提高到768,有时比修改网络更省事。

5.2 重复检测:嫩芽叶片卷曲造成的边界混淆

训练初期出现过一个奇怪的问题:同一个茶芽被模型预测出两三个高度重叠的框,置信度都在0.5以上。分析后确认原因是茶芽的嫩叶卷曲形态,特征图上一个芽头可能激活两个邻近的锚点区域,而NMS的阈设在0.5,合并不掉这些重叠框。

解决方法是把NMS的IoU阈值从0.5调整到0.4,同时把分类置信度阈值从0.25提高到0.35。调整后每个芽头的重复框数量从平均1.9个降到1.2个,F1分数有小幅提升。另一个备用方案是给检测头加一个小的中心度预测分支,但这需要修改损失函数,训练成本高,不是优先选择。

5.3 过拟合:数据集小导致验证集精度停滞

1200张训练图对于YOLO11这个规模来说偏少,我第90轮之后就明显看到训练损失还在降,但验证集mAP50几乎不走。除了做数据增强,我试了两种正则化手段:一种是把DropBlock加在骨干网络最后一层,丢弃概率设0.1;另一种是Label Smoothing,从默认的0.0调整到0.05。

实际效果上Label Smoothing更稳,mAP50提高了0.6个点,而DropBlock在茶芽数据集上反而丢了0.3个点,原因可能是丢弃特征后本来就少的小目标特征更不够用了。后面我又把训练集扩充到2000张图,这两个正则化手段就不再需要了,说明数据规模才是根本。

5.4 微弱视觉差异导致的错分:茶芽与虫害叶片的区分

茶园里一些虫害叶片边缘也会呈现嫩绿色卷曲,模型很容易误判成茶芽。我在标注阶段没有把虫害叶单独建类,因为数量太少。后期我在训练集里加入了150张只有虫害叶、没有茶芽的负样本图像,让模型学会"这不是目标"而非"不知道这是什么"。负样本加入后精确率提升了约2.5个百分点,代价是召回率轻微下降,等级容忍了这个方向,因为漏采几个芽头比误报病虫害干扰决策要好处理。

5.5 部署阶段速度回退:BN层未融合的隐患

复现了第4个问题之后,最容易犯的错误是导出模型时不会检查BN层是否融入卷积。有的框架导出ONNX时会自动处理,有的框架在推理脚本里处理,如果两边都做一遍也没事,如果都没做就麻烦了。当时在某边缘设备上部署,推理速度一直卡在15FPS上不去,排查后才发现ONNX模型里还带着独立的BN层,每层的额外计算把内存带宽吃满了。把BN融合进卷积后速度恢复到27FPS,差距将近一倍。

5.6 常见问题速查表

现象可能原因排查步骤解决方案
漏检率高小目标在深层特征丢失可视化特征图,观察P2/P3层激活增加P2层检测头,或提高输入分辨率
重复框多NMS阈值不够苛刻统计预测框的IoU重叠分布调低NMS阈值到0.4,提高置信度阈值
训练损失降但mAP不涨过拟合比较训练集/验证集指标差距加Label Smoothing,扩数据,减少训练轮数
推理速度慢BN层未融合检查ONNX模型结构手动融合BN到卷积,重新导出
误检背景为茶芽负样本不足检查误检图像的分布加入负样本图像,或增加背景类
茶芽与虫害叶混分特征区分度低分析分类特征向量相似度增加细粒度标注,用对比学习思路辅助

6. 应用扩展与后续改进方向

茶芽检测系统做到这一步,已经能够在实际的茶园巡检场景里稳定跑起来了。我在某试点茶园用无人机和固定相机两种方式采集视频流,模型在Jetson设备上的处理速度稳定在25FPS以上,单帧最多输出45个芽头坐标,数量统计误差控制在8%左右,对比人工统计,效率提升显著。

从项目扩展的视角来看,还有几个方向值得尝试。一个是在时间维度上做茶芽生长追踪,同一株茶芽在不同时间点的检测结果可以做轻量级的目标匹配,进而生成生长速率曲线,这对采摘期的预测很有价值。另一个是多光谱信息融合,茶芽和成熟叶片的光谱响应差异比可见光下更明显,如果在数据采集阶段加入近红外通道,模型的分类难度会降低很多,但这要求相机硬件的升级,目前还在评估成本。此外,轻量化剪枝和结构化稀疏化也能让模型进一步瘦身,适合更大规模的无人机集群巡检方案。

我个人在实际操作中的体会是,这类农业视觉项目真正的难点不在网络结构本身,而在于你对目标物的理解深度。如果不清楚茶芽在不同生长阶段、不同光照下的形态变化,就算把再先进的模块堆上去,数据标注这一步就会把效果压死。所以如果你也想在自己的场景里复现这套思路,我的建议是:先花70%的时间把数据和标注标准打磨好,再花30%的时间调网络结构,这个比例反过来的人,基本都在项目后期被数据反复折磨。

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

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

立即咨询