
简介本资源是一套面向农业智能化与计算机视觉初学者的猕猴桃叶片病害检测实战项目聚焦于解决果农及农技人员在田间快速识别褐斑病、炭疽病等常见病害的痛点问题。项目基于改进YOLOv5框架构建端到端检测系统涵盖数据采集规范、增强策略旋转/色彩扰动/缩放、模型轻量化调整及部署适配要点适用于智慧农业科研、毕业设计或AI农业课程实践。压缩包共15个文件5.25MB含6张JPEG/PNG病害实拍图用于验证、2个核心Python脚本Net.py与custom.py实现网络结构修改与数据加载、1份README.md说明运行流程、1份说明文件.txt和1个附赠文档.docx提供数据集标注规范与训练参数建议。已有99人学习下载内容结构完整开箱即可复现训练—推理—可视化全流程特别适合希望掌握目标检测在小众农业场景落地细节的学习者。 去年年底有个猕猴桃种植大户找到我说园子里的溃疡病又冒头了人工巡检根本看不过来。几千亩果园技术员带着放大镜满山转等发现的时候通常已经扩散了。这其实是农业病害检测都绕不开的卡点病害识别高度依赖专家经验还缺乏高频、自动的监测手段。我当时就在想能不能用深度学习这套东西做一个专门针对猕猴桃叶片的病害检测系统。最后定的技术路线就是基于改进YOLOv5的检测识别方案从图像采集、数据集构建、数据增强到模型改进、训练调优、落地部署整条链路跑下来积累了不少经验。这篇文章就把这个项目的完整过程拆开讲清楚给想做农业视觉或者YOLO系列目标检测的朋友一个可以直接参考的实战案例。这个项目本身不算特别复杂但涉及的面比较全计算机视觉里的目标检测、深度学习框架的选型与定制、数据集的构建与增强策略、模型的训练调参、以及最后的工程化部署。不管你是刚入门想找一个完整的学习路线还是已经在做农业检测但卡在精度上不去这篇文章应该都能对得上。1. 为什么是猕猴桃叶片病害检测以及为什么选YOLOv51.1 病害识别比想象中棘手猕猴桃的叶片病害种类不少常见的有溃疡病、褐斑病、炭疽病、灰霉病还有生理性黄化。这些病害在叶片上的表现差异很大有的是小斑点有的是大块枯斑有的伴随卷曲和霉层。单靠人眼判断需要有经验的农技员才能区分而且早期症状往往不明显等肉眼能确认的时候往往已经错过了最佳防治窗口。更麻烦的是果园环境本身光照变化剧烈叶片互相遮挡背景有泥土、杂草、果袋这些都给视觉识别增加了难度。我当时调研了一圈市面上通用的植物病害识别系统大多是针对大田作物比如小麦、水稻、玉米专门做猕猴桃的几乎找不到。原因也简单猕猴桃种植的规模化程度不如大田作物数据集难搞做出来之后商业回报有限。但正因为这样这个方向反而有很强的实际价值谁先把数据集和模型跑通谁就掌握了先发优势。1.2 为什么选YOLOv5而不是其他模型我在选型的时候其实纠结了一段时间。当时可选的方案包括两阶段的Faster R-CNN、单阶段的SSD以及YOLO系列。Faster R-CNN精度确实高但推理速度太慢果园巡检往往需要处理大量的现场图像实时性跟不上。SSD在中小目标上的表现一般而且工程化工具链不如YOLO生态完整。最终选YOLOv5主要是三方面原因。第一精度和速度的平衡点非常好。YOLOv5s在COCO上大概能做到37%左右的mAP0.5:0.95同时推理速度可以达到100FPS以上在消费级GPU上这在农业场景里完全够用。跑在边缘设备上也有优化空间。第二Ultralytics维护的YOLOv5生态太成熟了。数据加载、数据增强、训练流程、模型导出这些环节全都给你封装好了改起来也方便。各种部署工具链从PyTorch导出到ONNX再到TensorRT社区资料非常丰富遇到问题基本都能搜到解决方案。第三定制化空间足够大。YOLOv5的代码结构清晰Backbone、Neck、Head的边界分明改起来不会牵一发动全身。后面加注意力机制、改特征融合结构都有成熟的做法可以参考。这里顺带说一下为什么不用更新的YOLOv8或者YOLOv9。不是说新版本不好而是对于这个项目来说YOLOv5的定制资料更多踩坑经验也更丰富。农业病害检测里最难的是数据集和病害特征模型本身只要结构合理、能改得动就已经满足需求了。新的YOLO版本在精度上有提升但改造成本和对硬件的需求也上去了在真实场景里稳定性比那零点几个点的mAP重要得多。2. 数据集从0到1采集、清洗与标注的完整链路很多做深度学习的人都容易忽略数据集的重要性。我见过太多人拿到模型就急着训练结果效果不好回头又去调网络结构折腾半天发现是数据的问题。这个项目里数据集构建占了我整个项目周期的60%左右的时间我认为这个比例是合理的。2.1 图像采集设备、角度与光照刚开始采集的时候我犯了一个典型的错误只在晴朗的中午去拍拍了之后测试集上效果很好一拿到真实环境就拉胯。后来才意识到数据集必须覆盖不同光照条件否则模型很容易过拟合到某一种光照风格上。我的建议是如果条件允许用普通手机和数码相机交叉采集分别覆盖三类场景。一类是晴朗天气顺光拍摄一类是阴天漫射光拍摄还有一类是逆光或者叶片背面拍摄因为在叶片背面发现病害的概率其实很高。拍摄距离也要拉开从近距离特写5到10厘米到中等距离30到50厘米都要有这样模型才能适应不同尺度的叶片。采集的数量方面我按照每种主要病害2000张以上的标准来收集。加上健康叶片最终构建了约12000张图像的原始数据集。这里说的12000张不是最终训练集原始数据还要经过洗和筛这一步。2.2 数据清洗质量比数量重要数据清洗的工作量很多人都会低估。从果园拍回来的图片有一部分是模糊的尤其是手持设备拍摄的运动模糊特别多。还有一部分是严重过曝或者欠曝的叶片细节完全丢失这种图喂给模型只会带来噪声。我一般会做三遍筛选。第一遍人工快速浏览删掉明显糊掉的、无关的、严重重复的。第二遍写一个简单的清晰度评估脚本基于Laplacian算子计算方差低于阈值的直接淘汰。第三遍检查分辨率统一缩放到一个合适的尺寸范围我这里统一处理到1280像素左右的长边既能保留足够细节又不会让训练集文件过大。清洗完之后的干净图像大概是9000多张。这时候就可以进入标注环节了。2.3 标注规范边界框怎么打标注是整个数据环节里最费人力的一步但也是最不能省的一步。我用的是LabelImg免费开源支持YOLO格式和VOC格式输出。从项目一开始就要确定标注规范千万不要标注到一半再改否则返工成本非常高。针对猕猴桃叶片病害我的标注原则是病害区域如果呈现多个小斑块但分布集中打成一个边界框而不是一个个小框去框。因为最终部署时检测出来的框会被用于喷药定位一个集中区域对应一个喷药决策点更实用。如果一张叶片上有两种病害分别用对应类别框起来不要合并。但要注意边界框不要跨叶片避免把背景框进去。叶片很密集的场景只标注清晰可见、边界明确的叶片严重遮挡的叶片不标。这样能减少训练时的标签噪声。健康叶片也要单独设一个类别标注因为模型需要学会区分“有病”和“没病”单纯把背景当负样本是不够的。最终标注完的类别我设置的是溃疡病、褐斑病、炭疽病、灰霉病、健康叶片一共5个类别。其中溃疡病样本量最大约3000张褐斑病约2500张炭疽病和灰霉病各约2000张健康叶片约1000张。这种分布其实不太均衡后面训练时要用特殊手段处理。2.4 数据增强不只是简单的翻转变换数据增强在这个项目里的作用非常关键。YOLOv5本身内置了Mosaic增强、随机仿射变换、HSV色彩扰动等在线增强手段但我建议在离线阶段再补一部分增强数据尤其是针对病害样本的效果会更明显。离线增强我主要做了三类操作。第一类是几何变换水平翻转、垂直翻转、随机旋转30度以内。猕猴桃叶片虽然有一定朝向但病害叶片识别不应该依赖朝向特征所以翻转是安全的。第二类是色彩扰动。把亮度、对比度、饱和度各随机调整正负20%模拟不同天气和光照条件下的成像差异。这一步特别有用因为果园里早晚的光线色温差异极大只靠HSV增强不一定覆盖得到。第三类是模拟真实噪声。我给图像叠加少量高斯噪声和椒盐噪声增强模型的鲁棒性。注意噪声强度不能太大否则会把细小的病斑特征给“冲掉”。有一种增强方法我在这个项目里没有用就是MixUp。MixUp会把两张图以一定比例混合生成一张新的训练图像但病害叶片的特征本身很细腻混合之后病斑边界会变得模糊模型反而学不到清晰的特征。每个数据集都有自己适合的增强组合不能盲目套用。增强做完之后最终训练集规模大概在25000张左右包括原始图像和增强图像。这里要特别提醒增强后的训练集和原始测试集千万不能重叠否则测试结果虚高上线就被真实场景打脸。3. 给YOLOv5做定制化改造改进点逐个拆解数据集和标注搞定之后我开始考虑模型本身的问题。直接用原版YOLOv5s跑了一版baseline在验证集上mAP0.5大概能到86%看着还行但检查具体类别的结果发现炭疽病的检测效果很差因为它的早期病斑太小在640x640的输入尺寸下只有十几个像素YOLOv5默认的检测层对这种小目标很容易漏检。3.1 Baseline选型YOLOv5s还是YOLOv5m我先对比了YOLOv5s和YOLOv5m。YOLOv5m的参数量大概是YOLOv5s的2倍mAP会高一到两个点但推理时间也多了将近一倍。考虑到最终要在边缘设备上部署而且果园巡检的图片量大我最终确定以YOLOv5s为基线通过结构改进和训练策略来提升精度而不是简单换一个大模型。3.2 引入坐标注意力机制增强细粒度特征病害叶片和健康叶片之间的差异往往在很小的局部区域比如一个褐色小斑点、一圈黄晕。这种细粒度特征在卷积神经网络深层特征图上很容易被“稀释”掉。我在Backbone的最后几个模块后引入了坐标注意力Coordinate AttentionCA机制。CA的特别之处在于它不像SE注意力那样只关注通道维度的全局信息而是把位置信息也编码进来通过两个一维全局池化分别捕捉水平方向和垂直方向的长程依赖。说白了它能让网络在判断“这个区域是不是病斑”时不仅知道通道上有什么特征还知道这个特征在空间上的位置。这个特性对叶片病害识别很合适因为病斑往往出现在叶缘、叶脉附近位置信息本身就是判别线索。改动的代码位置在YOLOv5的models/common.py里定义一个CA模块然后在yolov5s.yaml的Backbone末尾插入。注意插入之后yaml里的from参数和通道数要重新算一遍不然会报维度对不上的错误。3.3 为小目标增加特征检测层这是本次改进里最值得说的一部分。YOLOv5默认有三个检测头分别对应80x80、40x40、20x20的特征图检测尺度从大到小。对于炭疽病的早期小病斑80x80这个检测头对应的感受野仍然偏大我决定额外增加一个160x160的检测头专门负责更小尺度的目标。做法是把特征金字塔往更浅层延伸。在yolov5s.yaml里新增一个从Backbone第2层对应160x160特征图引出的分支经过一次上采样和浅层特征拼接然后送入Neck结构。这等于让网络在更浅、分辨率更高的特征图上做预测。加了这一层之后模型参数量增加了大约12%推理速度下降了约15%但小目标的召回率提升非常明显。炭疽病在验证集上的召回率从78.4%提高到了91.2%这个代价是完全值得的。这里有一个细节新加的检测头需要单独初始化anchor。我用了k-means聚类重新计算anchor尺寸而不是直接沿用默认的。YOLOv5的utils/autoanchor.py里封装了自动anchor优化的接口训练时会自动重新评估anchor和bbox的匹配度。3.4 用BiFPN替换PANet做更深度的特征融合YOLOv5默认的Neck用的是PANet它的思路是“自顶向下传递语义特征自底向上传递位置特征”。PANet结构简单、效果好但也有个问题跨尺度的特征融合方式是简单相加没有区分不同尺度特征的贡献程度。我参考了EfficientDet里的BiFPN思路把PANet的简单相加改成带权重的融合。BiFPN会给每个输入特征图学习一个权重训练时自动调整不同尺度的贡献比例。对于叶片病害浅层特征包含纹理、边缘信息深层特征包含语义类别信息两者融合时权重的自动学习确实能提升检测效果。改动后的Neck结构在融合节点处多了几组可训练参数参数量增加不大但验证集mAP0.5提升了1.3个百分点。3.5 损失函数换成Focal-EIoUYOLOv5默认的边框回归损失是CIoU它在大多数场景下表现很好但在小目标检测和类别不均衡的场景里收敛速度和精度都有提升空间。我把边界框损失改成了Focal-EIoU。EIoU在CIoU的基础上重新设计了惩罚项直接优化中心点距离和宽高差异好处是收敛更快。Focal-EIoU则进一步解决了样本不均衡问题——它对简单样本的损失进行降权让模型更关注困难样本。病害检测里难样本很多被遮挡的病斑、形态不典型的早发症状、背景纹理干扰强的区域Focal-EIoU正好派上用场。改法很简单在utils/metrics.py里找到bbox_iou函数将回归部分替换成Focal-EIoU实现即可。改动集中在损失函数计算层不影响模型结构。这几项改进组合起来在验证集上的表现是mAP0.5从86.4%提升到93.2%mAP0.5:0.95从61.7%提升到69.8%。这不是单一项的功劳而是注意力机制、小目标检测头、特征融合和损失函数四方配合的结果。4. 训练调参与评估从70%到90%的完整路径模型结构定下来之后训练环节是最磨人的。同样的模型和数据集不同的人跑出来的效果可能差好几个点差距基本都出在训练策略和超参数上。4.1 环境配置和训练硬件我的训练环境是Ubuntu 20.04 PyTorch 1.12 CUDA 11.6显卡用的是一张RTX 309024GB显存。YOLOv5的依赖安装很简单git clone仓库后pip install -r requirements.txt即可。这里要注意PyTorch版本和CUDA版本的匹配建议用conda创建一个独立环境避免和其他项目打架。4.2 迁移学习和分阶段训练我采用的是“冻结训练解冻微调”两阶段策略。第一阶段加载COCO预训练权重冻结Backbone部分只训练Neck和Head。此时学习率设低一些比如0.001训练30个epoch。这阶段的目的不是让模型立刻学会病害特征而是让检测头在已有特征提取能力的基础上先适应新的数据分布。第二阶段解冻所有层学习率降到0.0001继续训练40到50个epoch。这个阶段让Backbone也能针对猕猴桃叶片做微调精度会在这时候有比较明显的上升。整个训练过程在RTX 3090上大约5到6个小时不算长。如果是消费级显卡比如RTX 3060时间大约翻倍也在可接受范围内。一个建议是训练过程中用过小的batch size。YOLOv5内置了很多跟batch size自动关联的增强策略比如当batch小于64时Mosaic增强会被激活。我用的batch size是32图像输入尺寸是640x640这是一个性价比比较好的组合。4.3 超参数调整的细节YOLOv5提供了一组默认超参数文件hyp.scratch-low.yaml我基于它做了几处修改。类别不平衡的问题我通过修改损失权重来缓解。炭疽病和灰霉病样本量偏少我把这两个类别的cls_loss系数调高了0.2。这个调整在训练时可以直接通过class_weights参数传入不需要改代码。anchor方面我在第一次启动训练时让YOLOv5自动重新计算anchor。虽然YOLOv5的autoanchor脚本能自动完成但我建议训练日志里关注一下它输出的Best Possible RecallBPR如果小于0.98说明anchor和数据集匹配度差需要手动调整anchor尺寸或者增加anchor数量。动量参数我保持了默认的0.937权重衰减0.0005。数据增强参数里我只把mosaic的概率从默认的1.0降到0.8因为我在离线增强部分已经做了比较充分的几何变换在线Mosaic过强反而会让小目标特征被拼图边界干扰。4.4 评估指标怎么看训练结果这里必须强调一点不能只看mAP一个指标。mAP是一个综合性的指标但它会掩盖单类别的短板。我训练完之后会重点看三个东西各类别的精确率与召回率、混淆矩阵、以及典型错误样本的可视化图。在改进后的模型上各类别的表现大致是溃疡病精确率95.1%、召回率93.4%褐斑病精确率93.6%、召回率94.2%炭疽病精确率91.8%、召回率91.2%灰霉病精确率90.7%、召回率89.6%健康叶片精确率96.3%、召回率95.7%。注意炭疽病的召回率虽然到了91.2%但仍然是最低的。原因已经不是小目标检测的问题而是炭疽病早期症状和褐斑病在一些图像上确实长得太像人眼都容易混淆。这种类间相似性问题单纯靠模型结构很难完全解决更有效的做法是补充更多两种病害的边界样本或者后期引入病害发生部位的先验知识比如褐斑病更多出现在叶缘炭疽病更多出现在叶面中部。混淆矩阵是排查这类问题的核心工具。训练完成之后YOLOv5会自动在runs/detect/exp/目录下生成混淆矩阵图我强烈建议花点时间仔细看而不是只盯着一张PR曲线。4.5 训练过程中的常见坑我在这个项目里踩了几个值得记录的坑。第一个坑是训练初期loss曲线看起来一切正常但验证集mAP一直上不去。排查一番发现是数据集划分出了问题训练集和验证集来自同一个果园、同一天的同一批照片过度相似导致验证集无法反映真实泛化能力。后来我按照“果园分块”的方式来划分数据来自同一棵树的图像全部放进同一个集合验证结果才变得可信。第二个坑是显存不足。加了新检测头之后输入640x640、batch size 32直接OOM。解决办法是把batch size降到16同时开启梯度累积。YOLOv5里可以通过设置--nbs选项来控制名义batch size实际batch size小了也能保持大batch的BN统计效果。第三个坑是训练过程中误判严重。我把一些“疑似病害但实际是虫害”的叶片也标注进去了结果模型学得混乱。后来统一了标注标准虫害损伤叶片全部归为健康除非同时有病害重新训练后精度提升明显。数据集的“干净度”比数量更影响模型精度。5. 从模型到系统检测识别系统的工程化落地模型效果好只是第一步真正要让人用起来还得把它做成一个可以稳定运行的检测识别系统。这个阶段我不追求技术炫技核心就四个字稳定可靠。5.1 系统整体架构我的目标是做一个基于浏览器的检测系统使用者上传图片系统返回检测结果包括病害类别、置信度和位置框。整体架构是前端页面 → Flask后端 → YOLOv5推理服务 → 结果返回。为什么用Flask而不是FastAPI主要考虑到团队里其他人对Flask更熟悉而且YOLOv5的推理本身是同步的异步框架的优势在这个场景里体现不出来。当然如果后续要做高并发的接口服务FastAPI会是更好的选择。推理服务我把模型加载做成了单例模式避免每次请求都重新加载权重。这在模型文件大于100MB时尤其重要否则内存会迅速被吃满。5.2 模型导出与推理加速部署时我做了两步优化。第一步是把PyTorch模型导出为ONNX格式第二步是进一步转成TensorRT的engine文件。在RTX 3090上TensorRT部署后推理速度比原生PyTorch提升了约2.5倍单张640x640图片的推理时间从45ms降到了18ms左右。导出过程中有一个容易踩的坑如果模型结构里有自定义模块比如我加的CA注意力模块PyTorch转ONNX时可能报告不支持的op。解决办法是在导出时设置opset_version12以上并确保自定义模块是用标准卷积、池化等基础算子实现的不要用复杂的自定义函数。在边缘设备上部署时还可以做进一步量化。把FP32的engine文件转成FP16精度对精度的影响很小mAP下降约0.3%但推理速度能再提升40%左右。如果设备支持INT8可以再试试INT8量化但需要校准数据集否则精度下降可能超过预期。5.3 可视化与告警光有检测框还不够实际操作人员需要更直观的反馈。我在前端页面做了两类可视化。第一类是单张图片检测结果展示在图像上直接绘制边界框和类别标签。置信度阈值我设在0.45低于这个阈值的检测结果不显示避免因为误检太多干扰使用。第二类是批量上传和报表汇总。果园技术员可以一次性上传几十张巡检照片系统自动统计各种病害的检出数量并生成一个简单的报表。报表按病害类别统计检出数方便判断哪种病害在这段时间内爆发。这个功能虽然实现简单但实际使用价值很高比单张检测受欢迎得多。告警方面我接入了结果推送。当系统在某张图上检测到溃疡病时自动发送一条告警消息附带图片缩略图和置信度。对于溃疡病这种传播速度快的病害早半小时发现可能就能避免一场损失。5.4 部署踩坑记录部署期间让我印象最深的是两个问题。第一个是并发请求导致的内存溢出。模型本身占用的显存不小如果同时来好几个图片请求每个请求都跑一次前向传播容易出现显存不足。我在推理服务里加了一个线程锁将请求排队处理。这个方案虽然牺牲了一点并发能力但在内网工具类系统里完全够用。第二个是图片格式和尺寸兼容问题。部署环境里总有用户上传超大图片比如相机原图5000像素以上直接作为模型输入会导致推理速度极慢甚至内存溢出。我在后端做了预处理统一把长边缩放到1280像素再送入模型这样既能保证检测速度也不会丢掉太多细节。6. 经验复盘数据、模型、部署哪个最重要如果要做同类项目我的建议排序是数据 训练策略 模型结构 部署。这不是说模型结构不重要而是很多人在前面几项还做得不到位的时候就急着去改网络结构结果投入产出比很低。6.1 数据质量是真正的决定因素这个项目里我最大的体会是把数据集做到位哪怕用原版YOLOv5也能获得不错的效果。我最初用只有4000张图、标注也不够规范的数据集跑baselinemAP只有70%左右。后来把数据量提升到9000多张重做了标注规范同样结构的模型mAP就到了85%以上。模型结构一个没改效果提升了15个百分点这就是数据的威力。有一个准则我说过很多次宁可少标注也不要标错。标错的标签比没有标签危害更大因为它直接带偏模型的优化方向。6.2 持续性迭代比一次到位更重要这个系统上线之后我的工作并没有结束。每次去果园实地测试都会收集一些之前没见过的新样本比如不同品种的猕猴桃叶片、不同季节的发病状态。这些样本经过筛选和标注后会定期补充进训练集然后重新微调模型。迭代过程中我建立了一个简单的版本管理机制每个版本的数据集、训练配置、模型权重都打上标签存档。这样做的好处是当新版本效果反而不如旧版本时可以快速回滚。模型不是改一版就一定更好有回退机制才能安心迭代。6.3 后续扩展的几个方向这个系统目前已经能用但离真正的“智慧农业”还有距离。我个人觉得后续有几个值得扩展的方向。一是引入无人机巡检视角。叶片病害的检测和果树整个冠层的健康评估结合起来才能形成更有决策价值的系统。这需要补充无人机图像数据同时适配更高分辨率的大图。二是病害严重程度分级。单纯检测“有没有病害”还不够最好能进一步判断“病害到了哪个阶段”这直接决定喷药策略。这个任务可以做成检测细粒度分类的串联结构。三是多作物迁移。猕猴桃叶片的数据集和训练经验迁移到苹果、葡萄等其他经济作物的叶片病害上可以大大缩短开发周期。同理这套系统的技术架构完全可以直接复用只要换数据集再做训练即可。最后分享一个个人经验做这类农业视觉项目如果条件允许尽量跟着一个真实的种植周期走一遍。这会让你在实际场景里积累到大量数据采集和模型迭代的宝贵经验远比闷头在电脑前调参有意义。算法模型总在迭代但你对农业场景的理解和数据的积累才是这个项目真正沉淀下来的护城河。本文还有配套的精品资源点击获取