不用怀疑,农业领域现在确实是最适合深度学习落地、也最难落地的场景之一。我说它“适合”,是因为病害识别这个需求足够刚性、场景足够封闭,不像开放世界的自动驾驶那样有无穷多的长尾角落;说它“难”,是因为作物病害的视觉特征差异极小,早期病斑可能就几个像素点,而且田间光照、遮挡、尘土干扰比任何公开数据集里的“干净照片”都残酷得多。这篇文章我拿YOLOv11来做一次完整的农业病害识别实战,数据集选自公开的PlantDoc,目标明确:识别番茄和玉米的常见叶部病害。整个流程我会按照数据准备、环境搭建、模型训练、评估调优、部署导出一条线走下来,期间会穿插大量我在实际跑实验中踩过的坑——这些才是常规教程里最不会告诉你的东西。
网上关于YOLOv11的教程很多,但多数停留在COCO类目或者人检测这类通用目标上,真正把模型训到农业病害这种“细粒度、小目标、类别不均衡”数据集上的文章很少。恰好这三座大山,农业病害全占了。所以刷一遍这个项目,你等于同时掌握了目标检测里最难啃的几个实战场景:小目标怎么增强、类别不均衡怎么调权重、数据不干净怎么清洗、以及模型过拟合时的止损策略。这篇文章适合两类人:一是计算机视觉入门之后,想找一个有真实业务价值、能写进简历的落地项目的同学;二是已经跑通YOLO流程、但对特定垂直场景的调优方法论还比较模糊的工程师。看完你不仅能复现训练流程,还能理解每一步背后的决策逻辑。
1. 整体设计思路:为什么选YOLOv11、为什么选PlantDoc
动手之前先把选型逻辑讲清楚。很多新手上来就莽,拿个模型就开训,训完效果差就换模型,来回折腾一个月,最后连问题出在数据还是模型都没搞明白。这种“炼丹玄学”式操作,恰恰是实战项目里最要不得的做事方式。
先说模型选型。YOLOv11是Ultralytics团队在YOLOv8和YOLOv9基础上迭代出来的新一代检测框架,它在C2f模块上引入了C2fA结构,也就是加入了Attention机制,让特征提取阶段通过自注意力信息增强特征表达。说得直白一点,模型学会了在特征图上“更关注哪里”,这个特性对农业病害识别价值非常大——因为病斑通常不是图像里最显眼的区域,草叶的纹理、土壤的杂色往往比早期病斑的视觉显著性更强。
而且YOLOv11的检测头做了更精细的拆分,用DW卷积替换了部分标准卷积,在保持精度的前提下把参数量和计算量压了下来。这意味着什么?意味着我们推理端的部署成本更低,边缘设备(Jetson、树莓派加NPU、甚至手机端)跑起来更从容。农业场景的落地,很多时候就是手持设备或者低成本摄像头,吃不下大模型的算力开销,YOLOv11这种性价比路线就是最优解。
再讲数据。PlantDoc是印度研究者公开的一个植物病害检测数据集,基于真实农田环境采集,涵盖13种植物和27种病害类别,其中番茄、玉米是我们这次重点关注的作物。为什么不用实验室那种纯色背景、固定角度拍摄的数据集?因为那些数据集真实场景迁移能力极差,模型在实验室数据上刷到99%的mAP,下地一拍现场照片直接跌到三四十个点,毫无意义。PlantDoc的优势就在于它是“脏数据”——不同光照、不同角度、不同生长阶段、叶片相互遮挡、背景里有泥土和杂草。虽然给训练带来麻烦,但换来的是模型在真实场景里的可用性。
这里要额外提醒一点:PlantDoc的标注质量并不完美。毕竟是众包标注,存在少量错标漏标、框不贴合叶片、类别混淆的情况。这一点很多教程都轻描淡写地带过,实际训练时却直接影响模型效果。所以数据处理阶段,我们要做“清洗+增强”两步操作,这部分放在下一节细讲。
2. 数据集准备与预处理:PlantDoc的坑和清洗方案
2.1 下载与目录结构搭建
PlantDoc可以在公开渠道找到,网上有一些镜像和封装好的版本,注意选择原版数据即可。下载后你会看到它的原始标注格式是XML(类似VOC格式),图像是JPG,分辨率参差不齐,从几百像素到上千像素都有。这里先别急着转YOLO格式,先做清洗。
先把项目目录规划好:
plant-doc-yolo/ ├── dataset/ │ ├── images/ │ │ ├── train/ │ │ ├── val/ │ ├── labels/ │ │ ├── train/ │ │ ├── val/ │ ├── data.yaml ├── runs/ ├── scripts/ │ ├── clean_data.py │ ├── voc2yolo.py │ ├── visualize_aug.py我习惯把清洗脚本和转换脚本分开写,每步做完都可视化检查一次,宁可多花半小时看图,也不要闷头往下跑。数据这种事,差之毫厘谬以千里,标注错一个框,后面全错。
2.2 数据清洗三板斧
清洗分三步走。第一步是过滤破损图片和空标注。PlantDoc里有少量图片打不开、或者标注文件存在但没有对应目标的情况,这类样本直接剔除。不要觉得无所谓,一张损坏的图在训练时可能会导致加载进程崩溃,而空标注文件可能让loss计算出现NaN,排查起来相当头疼。
第二步是处理类别分布。PlantDoc整体的类别不均衡问题比较严重,番茄和玉米的相关类别样本数差别很大。我跑了一个简单的统计脚本,打印出每个类别的样本量和目标框数量。结果跟预料的一致:番茄叶斑病、番茄早疫病这类常见病害样本充足,而一些稀有类别只有几十张图。类别不均衡会导致模型对稀有类别的召回率极低,这种问题在测试集上经常被忽略,因为AP值会被多数类拉高,看起来整体不错,一上实际场景才发现稀有类别基本全废。
处理方案我倾向“重采样+在线增强”双管齐下。重采样就是让每个类别的参与训练次数尽量均衡,通过调整每个epoch中采样各类别样本的比例来实现;在线增强就是让模型每轮看到的同一样本都不一样,视觉形态更加多样化,变相扩充稀有类别的样本量。
第三步是标注框校验。这一步是最枯燥但最关键的。需要检查有没有超出图像边界的框、有没有宽高为零的退化框、有没有类别ID越界的标注。我写了一个小脚本,把这些异常框全部标出来打印成日志,然后人工判断是保留、裁剪还是删除。PlantDoc里面边界框超出图像范围的情况确实存在,比例大概在1%到2%,虽然不多,但足以在训练时让anchor匹配逻辑出问题,框哪怕只越界几个像素,都可能让loss在后期突然跳高。
2.3 VOC转YOLO格式
数据清洗完毕之后,做标注格式转换。YOLO格式每一行是:class_id x_center y_center width height,坐标全部归一化到0到1之间,基于图像宽高计算。转换逻辑很简单:
import os import xml.etree.ElementTree as ET from PIL import Image def voc2yolo(xml_path, img_path, class_map, out_dir): tree = ET.parse(xml_path) root = tree.getroot() img_w = int(root.find('size/width').text) img_h = int(root.find('size/height').text) lines = [] for obj in root.iter('object'): cls = obj.find('name').text if cls not in class_map: continue box = obj.find('bndbox') xmin = int(float(box.find('xmin').text)) ymin = int(float(box.find('ymin').text)) xmax = int(float(box.find('xmax').text)) ymax = int(float(box.find('ymax').text)) # 裁剪越界 xmin = max(0, xmin); ymin = max(0, ymin) xmax = min(img_w, xmax); ymax = min(img_h, ymax) # 过滤退化框 if xmax <= xmin or ymax <= ymin: continue x_center = (xmin + xmax) / 2.0 / img_w y_center = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{class_map[cls]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") with open(os.path.join(out_dir, os.path.basename(xml_path).replace('.xml', '.txt')), 'w') as f: f.write('\n'.join(lines))这个脚本看起来简单,实战中反复改了很多版。核心逻辑是:转换前先读取图像真实宽高,避免XML中尺寸与图片实际尺寸不一致导致归一化坐标漂移;裁剪越界框时保留有效区域,而不是直接丢弃整个样本;转换完成后要做一次反向可视化验证,把YOLO的txt标注画回原图,肉眼确认每个框是否贴合目标。很多人在这个环节偷懒,后面吃亏了才知道返工的成本比验证高十倍。
2.4 数据集切分与增强策略
数据切分上,我采用的是按图像切分而不是按目标切分。8:1:1的比例划分train/val/test,注意同一个叶片图像不要同时出现在训练集和验证集,避免数据泄漏。按植物种类和生长阶段做分层抽样,保证每个子集的类别分布大致相同。
增强策略这里,我直接用Ultralytics内置的增强配置,但针对小目标病害做了一些自定义调整。默认增强里,mosaic(拼接)是默认开启的,这个对通用目标检测效果好,但对病害这种“小而细”的目标要谨慎开启。为什么?因为mosaic会把四张图拼接成一张,小目标被进一步压缩,细节信息直接丢失;而且拼接边界处的病斑会被截断,产生错误的标注信息。我的经验是:mosaic在训练初期可以开(epoch小于总训练的20%),用来提升模型对全局特征的鲁棒性,后期关闭,让模型聚焦细节。
flip direction(翻转方向)默认是开启的,对叶片识别问题不大,水平翻转不会改变病害的形态学本质,开启没问题。但旋转和缩放增强对细粒度病害要小心,旋转超过15度,叶片的纹理方向特征可能失真;缩小到一定比例后,小病斑的像素数量不够,模型学到的可能只是“模糊色块”而不是“病理特征”。
我最终的增强配置是:mosaic在前50个epoch打开,后面关闭;hsv_h和hsv_s增强略微降低,因为病害颜色很重要——灰色霉病和早期枯斑在颜色上有细微差异,过强的颜色抖动可能把这种差异洗掉;scale范围控制在0.5到1.5倍之间,避免过度缩放到细节丢失;shear和rotate角度控制在10度以内。这部分配置是在验证集上对比出来的,后面在调优章节我会给出具体对比数据。
3. 训练环境与参数配置
3.1 硬件环境说明
先交代我使用的实验环境:训练机器是单张消费级显卡,显存有限,这个限制其实很接近大多数个人开发者的真实条件。CPU和内存配置属于中等水平,NVMe固态保证数据读取速度不是瓶颈。
如果是首次配置深度学习环境,你需要准备好:Python环境、PyTorch(CUDA版)、Ultralytics库。安装命令不复杂,但版本匹配问题值得留意。基于Ultralytics的YOLOv11,直接pip install ultralytics就能拉到最新的yolo11系列模型权重。要注意CUDA版本和PyTorch版本的匹配,装错的话训练速度慢得离谱,或者干脆CUDA不可用。
3.2 预训练权重选择
YOLOv11有n、s、m、l、x五个尺寸版本。农业病害识别不是通用物体检测,不需要x级别的超大模型来堆精度,反而要考虑过拟合风险和推理速度。我在这个项目上测试了n和s两个版本,s版本在精度上比n高出约3到4个mAP点,但推理速度慢了一倍多。考虑到实际部署可能跑在边缘设备上,最终选的是s版本。如果数据量实在小(比如自定义数据集只有几百张),优先考虑用n加更强的正则化,或者用s的预训练权重做迁移学习时冻结backbone前几层,降低过拟合风险。
预训练权重用的是官方在COCO上的模型。有同学可能会问,为什么不用在农业数据上预训练的模型?因为公开可用的农业预训练模型很少,即使有,其训练数据分布和你的目标域也不一定匹配。COCO模型学到了丰富的通用纹理、边缘、颜色特征,这些低级特征对病害识别依然有效,通过迁移学习可以快速适应农业场景。关键是微调策略:freeze参数设为10,冻结前10层,保留通用特征提取能力,后面的层充分学习农业病害的特定特征。
3.3 核心训练参数解读
接下来是训练参数的详细解读。这些参数是在多次实验中确定的,每个参数都有它的逻辑。
imgsz=640:输入图像尺寸。640是速度和精度的折中点,过大的尺寸(如1280)能保留更多病斑细节,但显存消耗翻倍,训练时间拉长,收益却不明显。PlantDoc原始图像分辨率不高,强行放大到1280只会增加计算量,不会增加信息量。
batch=16:根据显存倒推出来的。显存不够时报OOM,粗暴调小batch到8或4也能跑,但精度会受影响。batch太小,BN统计不稳定,模型收敛慢。如果显存有限,优先考虑减小输入尺寸,而不是把batch压得太低。
epochs=300:初始设置300,配合早停机制(patience=30)。病害检测任务收敛速度比通用目标慢,因为细粒度特征的学习需要更多迭代。300轮看起来多,但实际一大半epoch会在过拟合前就触发早停,真正有效训练大概在100到150轮之间。
optimizer=AdamW:YOLOv11默认是SGD,实测AdamW在这个数据集上收敛更快,前期loss下降明显更陡。但AdamW后期需要配合weight decay控制过拟合,我设的是0.0005。
lr0=0.001:初始学习率。不要贪心,大学习率在预训练权重微调场景下很容易把学好的特征破坏掉。warmup_epochs保持默认的3.0,让模型在前几轮用较小学习率稳定热身。
close_mosaic=10:最后10个epoch关闭mosaic,这个参数和前面说的增强策略对应。在训练后期关闭mosaic可以让模型在更接近真实分布的数据上做精细调整。
3.4 data.yaml 的正确姿势
data.yaml虽然只是一个配置文件,但格式错误会导致训练直接报错,非常影响心态。整理一下标准格式:
path: ../dataset train: images/train val: images/val test: images/test names: 0: Tomato_Leaf_Mold 1: Tomato_Early_Blight 2: Tomato_Late_Blight 3: Tomato_Leaf_Curl 4: Corn_Common_Rust 5: Corn_Gray_Leaf_Spot ...一个关键注意点:names里的顺序必须和转换标注时的class_map顺序完全一致,否则模型训练时类别ID对不上,所有的框都标错了,训练效果自然稀烂。数据集路径尽量用相对路径,或者绝对路径也可以,但要保证训练脚本的工作目录正确,路径写错会让Ultralytics在检查数据时报一脸冷漠的FileNotFoundError。另外务必确认所有图片都能正常打开,我曾经因为一张损坏的PNG图,训练跑到一半才报错,直接浪费三个小时算力。
4. 训练执行全流程与日志解读
4.1 启动训练的命令与检查项
我习惯在跑完整训练之前,先做一个两三个epoch的“冒烟测试”,看一眼数据加载是否正常、loss是否能降下去、显存占用是否在安全范围。冒烟测试通过之后再跑完整训练,省下的时间远比多等两轮多。
正式训练命令如下:
yolo train model=yolo11s.pt data=plantdoc.yaml epochs=300 imgsz=640 batch=16 lr0=0.001 optimizer=AdamW freeze=10 close_mosaic=10 patience=30 project=runs name=plantdoc_yolo11s跑起来之后,盯着输出的日志看几个关键信号。第一段epoch是热身阶段,这个阶段的loss会快速下降,下降幅度可能超过50%,这属于正常现象。进入稳定训练期后,损失下降速度会明显放缓,这时如果验证集的mAP还在持续上升,说明模型还在有效学习。如果训练经过一段时间后,训练损失还在下降但验证损失开始反弹,那几乎可以断定过拟合已经开始,早停机制应该会在几十轮内触发。
loss曲线不是直线下降的,它是阶梯式下降的,具体表现为“平台期+突降”的组合。每个平台期对应着模型在重新调整特征表征,突降往往发生在某个学习率衰减节点或者数据增强策略改变时,例如mosaic关闭后loss会有一个明显的下落。
4.2 训练中断与恢复
训练中途因为各种原因中断是很正常的事——断电、显存溢出、手滑关掉了终端。Ultralytics会自动保存last.pt,恢复训练的命令也很简单:
yolo train resume model=runs/plantdoc_yolo11s/weights/last.ptrecuser会自动从上一次保存的checkpoint位置继续训练,不需要额外指定epoch和数据集配置,它会从checkpoint里的信息恢复。这个小功能在长训练周期时是救命稻草。
但我还是建议每50个epoch手动备份一次best.pt。为什么?因为last.pt和best.pt在不断覆盖更新,如果最后一个保存点恰好处于过拟合后期,那恢复训练得到的模型可能已经劣化了。手动备份中间版本,可以在调优对比时回退到某个表现最好的中间状态。这个习惯在炼丹界叫“存点”,关键时刻能救命。
4.3 训练结果核心指标解读
训练结束后,Ultralytics会输出一组验证集指标:mAP50、mAP50-95、Precision、Recall。这四个指标的表达逻辑如下。
mAP50是IoU阈值设为0.5时的平均精度,它衡量的是“大致框对”的能力,农业场景中的病害检测通常不需要像素级精确边界,mAP50是核心指标。mAP50-95是IoU从0.5到0.95的均值,更严苛,更能反映框的定位精度,对后续做精细分析(比如病斑面积统计)有意义。
Precision表征“预测的框里有多少是对的”,Recall表征“真实的病害有多少被找出来了”。在病害识别场景中,Recall优先于Precision——漏检一棵病株,可能导致整片田的病害暴发而错过防治窗口;误检一次,农户多喷一次药,损失相对可控。所以调参时我是优先保Recall的,CIoU loss的阈值和NMS的置信度阈值都会偏向降低漏检。
当前模型如果跑出mAP50在75到85之间,mAP50-95在50到60之间,对PlantDoc这种“脏数据”来说已经是可以接受的水平。不要拿COCO的mAP标准来苛求,COCO是自然图像,目标显著且类别边界清晰;农业病害是小目标加类内差异小,同等mAP下的难度完全不是一个量级。
5. 模型评估、可视化与常见问题排查
5.1 混淆矩阵分析
只会看mAP曲线不算会评估模型。我拿到训练结果后的第一个动作,是打开混淆矩阵图。混淆矩阵能直观告诉你模型把哪两类病害互相搞混了。在PlantDoc上,最典型的就是番茄叶霉病和番茄早疫病,它们早期症状都是叶片上出现不规则褐斑,区分度极小,混淆频率高是正常的。如果混淆矩阵显示这两类大量互混,可以考虑收集更多两者的差异样本,或者在类别定义时把两者的判定标准写得更加明确。
另一个常见混淆是对“健康叶片”和“早期病斑”的混淆。健康叶片在特定光照下可能出现颜色不均,模型会把它误判为初期病斑。这个在混淆矩阵上表现为健康类的Recall被拉低。处理方法是在训练集中加入更多弱病斑样本,这个可以在推理时提高置信度阈值来过滤误检。
5.2 PR曲线和置信度阈值调整
PR曲线(Precision-Recall曲线)提供了一个关键的调参信息:置信度阈值的合理取值。曲线越靠近右上角(Precision和Recall都高),模型越好。如果你的应用场景是巡检防疫(需要尽量少漏检),可以在PR曲线上找到Recall比较高而Precision还能维持在可接受范围的点,取对应的置信度阈值作为推理参数;如果场景是研究统计(更看重精确率),则相反选Precision更高的点。
不用重新训练就能调整阈值,这一点容易被忽视。推理时yolo predict conf=0.25就是调整置信度阈值。我实测PlantDoc模型conf从默认0.25调到0.15,Recall提升约6个百分点,Precision下降约3个百分点,在巡检场景下这个交换是划算的。
5.3 典型问题排查:类别不均衡、小目标漏检、过拟合
类别不均衡:症状是稀有类别的Recall极低,混淆矩阵相应行几乎全零。解法不仅是前面说的重采样加权,还可以在loss层面处理。Ultralytics支持每个类别的loss权重调整,为稀有类别增加loss惩罚,让模型在训练时对稀有类别“更敏感”。我没有在Ultralytics的YOLOv11里用自定义损失函数,而是用数据层面的重采样解决问题,因为改loss对框架侵入性太强,容易引入新问题。
小目标漏检:病斑早期的框可能只有十几个像素宽,在imgsz=640下小目标特征图分辨率不足以保留足够的语义信息。实测下来对于小目标,一是增大输入尺寸(比如切换到imgsz=960),二是调整增强策略关掉过度的缩放过拟合。引用一句同行的话:“小目标不是模型检测出来的,是增强策略放出来的。”这句话在农业病害场景下尤其成立。
过拟合:最典型的信号是训练loss持续下降,验证mAP在某个点后开始下滑。早期应对是加大数据增强、调大weight decay、早停;如果还压不住,就是数据量确实太少,要走少样本学习的路线了,比如用已训练好的PlantDoc模型作为预训练权重,在少量自采数据上做微调,而不是从COCO权重重新开始训。这样能大幅减少所需数据量,对自建数据集特别有用。
6. 模型导出与轻量化部署
6.1 导出ONNX
训练完的PyTorch模型不适合直接上生产环境。部署的第一步是把模型导出为ONNX格式,ONNX是一个开源的模型中间表示,被几乎所有推理框架支持。Ultralytics提供了一键导出功能:
yolo export model=runs/plantdoc_yolo11s/weights/best.pt format=onnx opset=12 simplify=True这里有个关键的坑,如果不做simplify(简化),导出图里会有很多冗余的reshape和transpose节点,推理性能会差不少。simplify=True会在导出过程中对计算图做优化合并,这一步能削减大概20%到30%的推理耗时。
导出完成后,用onnxruntime做一个快速的正确性验证,确保导出的ONNX模型推理结果和PyTorch模型一致。我用一张验证集的图片分别跑PyTorch和ONNX推理,对比输出的检测框坐标,坐标误差在1e-4级别就算通过。
6.2 TensorRT和边缘设备实测对比
ONNX是通用格式,但要榨干显卡性能,TensorRT是真正的利器。TensorRT是NVIDIA的推理优化引擎,它会在模型加载阶段对计算图做层融合、精度校准、内存复用等优化。把ONNX转成TensorRT引擎后,推理速度通常比ONNX快2到3倍。
这里要介绍一下实际测试的数据:同一张测试图片,PyTorch模型推理耗时约30到50毫秒(取决于GPU型号),ONNX Runtime约20到30毫秒,TensorRT优化后能压到10到15毫秒。如果换到边缘设备,比如Jetson系列,TensorRT的优势会更明显,瓶颈主要在内存带宽和算子调度上。
如果目标是部署到手机或嵌入式设备,模型结构还可以进一步蒸馏压缩到n版本甚至更小。YOLOv11s在裁剪后可以降到n的推理速度,精度损失在一两个点以内,这对农业设备的低成本部署诱惑力巨大。NCNN和TNN在端侧CPU/ NPU上也能跑,但需要额外做算子的适配和量化,这部分工作量属于工程范畴,本文先不展开。
6.3 部署后推理逻辑的注意事项
部署并不止于模型文件输出。实际推理代码里还有几个容易深陷的细节。第一个是输入图像的预处理必须和训练时完全一致:缩放到640x640、归一化到0到1之间、RGB通道顺序要和训练数据一致。很多人在这一步出错,读进来的是BGR图像或者做了错误的归一化,导致推理结果极差,还以为是模型没训好。
第二个是NMS(非极大值抑制)参数要预先确认。实际部署环境不一定方便做动态NMS调参,建议在模型配置文件里预设NMS参数,导出时固化。
第三个是推理后处理要与业务层融合。农业巡检不是把框画出来就结束了,通常需要把检测框输出的类别、置信度、坐标与GPS信息、时间戳绑定,写入数据库或者上报云端。某些巡检设备还会结合检测结果做自动喷药控制,这种场景就需要增加防抖逻辑——同一位置的病斑连续多帧出现才能触发喷药指令,避免误操作。
7. 参数速查表与复现建议
把本次实战的核心参数整理成速查表,方便你直接抄作业。硬性参数按你的显存情况微调即可,数据增强和训练策略参数可以直接参考。
| 参数 | 设置 | 说明 |
|---|---|---|
| model | yolo11s.pt | 在精度与速度间取平衡 |
| imgsz | 640 | 过小丢细节,过大增加显存压力 |
| batch | 16(8GB显存) | 根据显存倒推,优先保batch |
| epochs | 300 + patience=30 | 早停兜底,防止无效训练 |
| lr0 | 0.001 | 微调预训练模型不宜过大 |
| optimizer | AdamW | 收敛快,配合weight decay防过拟合 |
| freeze | 10 | 冻结backbone前段,保留通用特征 |
| close_mosaic | 10 | 后10个epoch关闭mosaic,回归真实分布 |
| cache | True(有足够内存) | 全部载入内存,省去IO等待 |
要复现这个项目,建议调整几步你自己的操作顺序:第一步下载并清洗PlantDoc数据,第二步转YOLO格式并做可视化验证,第三步按上面的参数跑几十个epoch的冒烟测试,第四步确认一切正常后跑完整训练。如果你有自己的实地采集数据,可以把PlantDoc作为预训练来源,在你的小数据集上做迁移学习微调,这比从零训练的效果要稳得多。
8. 我个人的实操体会
整个项目跑下来,我最深刻的感受是:农业病害识别模型的瓶颈,从来都不在模型结构上,而在数据的处理和业务场景的理解上。YOLOv11当然是一个好工具,它让目标检测的落地门槛降到了一个令人兴奋的程度,但同样的模型,有人跑出80个点的mAP,有人只跑出40个点,差距基本全在数据处理和调优策略上。
我自己在清洗PlantDoc数据上花的时间,比训练和调参加起来还多。这听起来很枯燥,但恰恰是这个环节决定了最终模型的上限。我遇到最典型的一个教训是早期对稀有病害类别没有做任何处理,模型训完看起来mAP50有78,但把每个类别的AP打出来看,三个稀有类别的AP不到20。后来做了重采样和增强策略调整,模型总mAP只涨了两三个点,但这几个稀有类别的AP直接翻倍,这种提升才是真正有业务价值的提升。
如果你打算在自己所在地区采集真实数据来做更贴合实际的病害识别,我可以给你一个建议:采集时尽量覆盖不同的光照条件、不同的生长周期和不同的拍摄设备,同时记录叶片的正反面。很多病害正面症状不明显,反而是叶片背面的孢子堆更典型,训练数据里如果没有背面样本,模型在田野里的实用性会大打折扣。
做农业AI项目,不要总指望有个完美的公开数据集让你一次性训出理想的模型。真实的田间情况永远比数据集复杂得多,能把模型在一个“不完美”的数据集上训练到一个可用状态,并知道如何针对业务场景做调整,这本身就是比多刷几个点的mAP重要得多的能力。