简介:这是一篇聚焦深度学习技术在水稻叶部病害图像识别中应用的学术论文,适合农业工程、计算机视觉及机器学习领域的研究者阅读。资源为单个PDF文件,大小约3.14MB,内容基于Caffe深度学习平台展开:作者先构建水稻病害图像库并使用PCA主成分分析降维,再设计包含4个卷积层、3个池化层和1个全连接层的深度网络结构,最后利用2000幅图像训练并用10重交叉验证测试,对北方寒地稻瘟病、纹枯病等常见病害的平均识别率达到96.9%。文档还对比了传统图像处理与机器学习方法的局限,并给出具体实验参数与结果分析,可作为相关课题的方法参考与复现依据。目前已有74人学习,适合需要了解深度学习在农作物病害识别领域落地思路的读者。
1. 水稻叶部病害识别:为什么说准确率不是这个课题的第一道门槛
做智慧农业项目时,最容易被深度学习光环带偏的,就是把自己的稻田照片丢给 CNN,然后等一个从天而降的准确率。水稻叶部病害识别不是一个单纯堆模型的问题:稻瘟病斑与褐斑病在图像上高度相似,同一片叶片在不同生长阶段、不同光照下呈现的颜色差异,可能比不同病害间的差异还大。这个方向能解决的具体问题很明确——早期发现病斑、区分主要叶部病害、减少田间人工巡检压力。它适合两类人:接了农业视觉项目需要快速落地的工程师,以及做植保研究但不想在数据上翻车的研究生。但动手前要先想清楚一件事:准确率是目标,数据才是门槛。
2. 数据决定天花板:水稻叶部病害数据集的清洗、划分与增强
2.1 先认清要分几类:水稻叶部病害的常见类别与混淆难点
做识别之前,先把类别定义清楚。最常见的几类叶部病害是稻瘟病(Pyricularia oryzae)、白叶枯病(Xanthomonas oryzae pv. oryzae)、纹枯病(Rhizoctonia solani)、胡麻斑病(Bipolaris oryzae)和稻曲病(Ustilaginoidea virens)。其中稻瘟病和胡麻斑病是视觉上最容易混淆的:稻瘟病典型的梭形病斑边缘有黄色晕圈,胡麻斑病则是芝麻粒大小的褐色斑点,但在叶片老化或者拍照分辨率不足时,两者在图像上几乎看不出差别。白叶枯病又不一样,它是从叶缘开始的枯白条带,早期症状和缺氮的黄化很接近。
分类任务的设计直接影响数据采集策略。如果只做二分类“有病/没病”,数据集压力小很多,但对生产没太大价值;做多分类要面对类间相似、类内差异大的双重麻烦。我一般会建议先定 5 类左右(4 类病害加 1 类健康叶片),后续再根据实际场景扩展。深度学习中类别定得越细,对数据质量的要求越高,尤其是那种只在病斑细节上有差异的细粒度分类,纯靠整张叶片缩略图是学不出来的。
2.2 公开数据集不够用:自采数据的采集与目录整理
公开数据集选择有限,Kaggle 和 UCI 的植物病害仓库里能找到水稻叶片子集,但普遍存在两个问题:一是图片分辨率低,病斑细节不足;二是拍摄环境单一,多为实验室背景,和田间逆光、尘土、露水、遮挡等真实场景差距很大。所以做这个课题,自采数据几乎是绕不开的。
自采数据时我一般建议按“病种—田块—日期—植株编号”的规则组织目录,而不是一股脑塞进一个文件夹。原因很直接:后期做训练集和验证集划分时,如果随机切分,同一个田块同一天拍的叶子会同时出现在训练集和验证集里,模型记住的是拍摄环境而不是病害特征,验证分数虚高。目录结构从采集那天就规划好,后面能省掉大量返工。
# 推荐目录结构示例 rice_leaf_data/ ├── 01_稻瘟病/ │ ├── 田块A_20230615_植株01.JPG │ ├── 田块A_20230615_植株02.JPG │ └── 田块B_20230702_植株11.JPG ├── 02_白叶枯病/ ├── 03_纹枯病/ ├── 04_胡麻斑病/ └── 05_健康/文件名里的“田块”和“日期”字段是后面做分组划分的依据。采集时还要注意:每片叶子至少拍两个角度,病斑区域要尽量充满画面或单独拍一张局部特写。只拍远景的整株照片,病斑占总像素比例小,分类模型很难捕捉到有效特征。拍完后用脚本快速检查一下图片完整性,去掉模糊、过曝和完全没有病斑的误标图片。
2.3 数据清洗实操:用脚本剔除模糊图和重复图
数据清洗这步看似琐碎,实际影响非常大。模糊图在训练集里占比超过 10% 时,模型会学到“模糊=某种病害”这个错误关联,测试时把对焦不准的健康叶判成病叶。常见做法是用 Laplacian 算子的方差来判断清晰度,方差低于阈值的直接剔除。
import cv2 import numpy as np from pathlib import Path from imagededup.methods import PHash # 1. 清晰度筛选:Laplacian 方差低于阈值的图片标记为模糊 img_dir = Path("rice_leaf_data") for img_path in img_dir.rglob("*.JPG"): img = cv2.imread(str(img_path), cv2.IMREAD_GRAYSCALE) if img is None: print(f"损坏文件: {img_path}") continue lap_var = cv2.Laplacian(img, cv2.CV_64F).var() if lap_var < 80: # 阈值需要根据实际图片分辨率调整 print(f"模糊图: {img_path} laplacian={lap_var:.1f}") # 2. 感知哈希去重:去除同一叶片的多张重复照片 phasher = PHash() duplicates = phasher.find_duplicates(image_dir=str(img_dir)) for orig, dup_list in duplicates.items(): for dup in dup_list: print(f"重复图: {dup} 与 {orig} 相似")逻辑说明:第一步用 Laplacian 算子求图像二阶导的方差,方差大说明边缘锐利、图像清晰;方差小说明图像模糊。阈值 80 是经验值,对 512×512 左右的叶片图适用,分辨率越高阈值要适当调高,可以先跑几批看分布再定。第二步用感知哈希为每张图生成指纹,汉明距离小于阈值的判定为重复图。重复图不删干净,会让模型在训练时反复见到同一片叶子,加剧过拟合。参数说明:清晰度阈值和哈希距离阈值都没有唯一正确答案,要结合自己的数据分布跑一轮统计再定,不要照抄任何项目的默认值。
2.4 数据增强配置:翻转、旋转、Cutout 与颜色扰动怎么搭配
水稻叶片病害识别的增强策略和通用图像分类不太一样。叶片是长条形,垂直翻转和水平翻转都有物理意义,但旋转角度要考虑叶片朝向的合理性——旋转 90 度可以,旋转 45 度会让病斑形状和纹理发生非真实畸变,模型可能在训练时学到扭曲的特征。颜色扰动很关键但也是最需要小心的:水稻叶片本身颜色就是重要特征,黄化可能是病害也可能是老化,颜色扰动幅度过大,模型就分不清颜色差异到底来自病害还是光照。
from torchvision import transforms train_transform = transforms.Compose([ transforms.Resize((256, 256)), transforms.RandomRotation(degrees=15), # 小幅旋转,避免病斑形状畸变 transforms.RandomHorizontalFlip(p=0.5), transforms.RandomVerticalFlip(p=0.3), transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.15, hue=0.05), # 颜色扰动宁小勿大 transforms.RandomCrop(224, pad_if_needed=True, padding_mode="reflect"), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ])逻辑说明:Resize 到 256 再做 RandomCrop 到 224,等于给模型提供随机裁剪的局部视野,既模拟了不同拍摄距离,也让模型不能只依赖叶片整体轮廓做判断。旋转限制在 15 度内,是因为水稻叶片在自然图像里基本是竖直或略有倾斜的状态,旋转角度过大会生成现实中不会出现的叶片姿态。颜色扰动里的色相(hue)只给 0.05,饱和度和亮度控制在 0.15~0.2,这是为了模拟不同天气、不同光照下的色差,但又不至于把健康绿叶变成黄叶。
Cutout 和 Mixup 我放在第二种方案里。水稻叶片病害的病斑通常是小区域,Cutout 随机遮掉一块 32×32 的区域,可以强迫模型不要只依赖某一个强特征做判断,对稻瘟病这种病斑分散的类型尤其有效。Mixup 做线性插值混合两张图,本质是软标签,能缓解标注噪声,但用在病害识别上有个副作用——混合后叶片纹理变得不真实,模型在真实照片上的泛化有时反而变差。我的建议是:Cutout 优先加,Mixup 先跑一轮对比实验再决定要不要留。
3. 模型选型与训练策略:从迁移学习到类别不均衡的落地取舍
3.1 为什么默认从 ResNet 开始:预训练权重和输入尺寸的选择
水稻叶部病害识别这个任务,图像特征集中于叶片纹理和病斑形状,本质上是一个中等难度的图像分类问题。ViT 和 Swin Transformer 在大型数据集上表现好,但在几千张图片的训练集上,如果没有大规模预训练权重,效果往往不如 CNN。ResNet50 是实际项目里最常见的起点:结构成熟、PyTorch 里直接下载 ImageNet 预训练权重、在中等数据集上不容易过拟合。
用预训练权重不是为了省训练时间,而是因为 ImageNet 上预训练过的浅层卷积核已经学会了边缘、纹理、颜色渐变这些通用特征,水稻叶片的病斑辨识正好用得上这些底层表征。简单算一笔账:从零训练 ResNet50 在几千张图上收敛到可用状态,一般需要几万步;用预训练权重只微调全连接层和最后几个 block,几千步就能稳定下来,而且泛化明显更好。
输入尺寸方面,224×224 是 ResNet 的默认输入,但对水稻叶片来说偏小。病斑细节在 224 分辨率下可能只有 8×8 像素,卷积下采样后基本丢失。我常用的做法是输入 Resize 到 384×384,虽然训练速度慢 20% 左右,但模型对细小病斑的敏感度明显提升。显存不够时再用 256×256 做折中,不建议低于 224。
3.2 训练策略的核心参数:冻结层、学习率、Batch Size 与迭代节奏
训练策略的取舍是深度学习项目中真正拉开效果差距的地方。水稻叶部病害图像和 ImageNet 自然图像比较接近,不需要微调全部层。我一般先冻结 ResNet 的 stem 和前三个 stage,只训练最后一个 stage 和替换后的分类头,等验证准确率进入平台期后,再解冻全部层用小学习率微调。这个做法叫分层微调,能有效避免在数据量不足时把预训练特征破坏掉。
import torch import torch.nn as nn from torchvision import models # 加载预训练 ResNet50,替换分类头 model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V2) num_classes = 5 # 4 类病害 + 1 类健康 model.fc = nn.Linear(model.fc.in_features, num_classes) # 分层冻结:冻结前三个 stage,只训练最后 block + fc for name, param in model.named_parameters(): if not ("layer4" in name or "fc" in name): param.requires_grad = False # 优化器:冻结层不参与权重更新 optimizer = torch.optim.SGD( [p for p in model.parameters() if p.requires_grad], lr=1e-3, momentum=0.9, weight_decay=5e-4 ) scheduler = torch.optim.lr_scheduler.CosineAnnealingLR( optimizer, T_max=30, eta_min=1e-5 ) # 损失函数:初始用普通交叉熵,确认类别不平衡后再换加权版 criterion = nn.CrossEntropyLoss()逻辑说明:SGD 配合 momentum 0.9 是迁移学习的常见配置,比 Adam 在微调场景下泛化更稳定。学习率 1e-3 适用于只训练最后 stage 和 fc 的阶段,解冻全部层后要降到 1e-4 或 1e-5,否则容易把预训练权重震荡坏。CosineAnnealingLR 的 T_max 设为 30,表示 30 个 epoch 内学习率从 1e-3 余弦下降至 eta_min,配合早停用。weight_decay 5e-4 是 ImageNet 微调的标准配置,防止全连接层过拟合。
训练迭代中要关注的指标不是训练准确率,而是验证准确率和验证损失。每次保存验证准确率最高的权重,不要保留最后一次 epoch 的权重。训练集准确率先到 99% 不用高兴,要看验证集有没有同步上升;如果验证集在第 10 个 epoch 后不再上升且训练集还在涨,就是过拟合信号,此时应加大 weight_decay 或增强数据扩充,而不是继续调学习率。
3.3 类别不均衡:加权交叉熵和 Focal Loss 哪个值得用
水稻叶部病害数据天然不均衡。稻瘟病样本好采集,白叶枯病和稻曲病需要特定天气和生育期才能遇到,样本数可能只有稻瘟病的五分之一。直接用 CrossEntropyLoss,模型会把所有不确定的样本都判成稻瘟病,因为这样总损失最小,但实际使用毫无价值。
处理类别不均衡有两条路。第一条是加权交叉熵,根据每个类别的样本数反比设置权重,等价于让小类别的错误在损失里占更大比重。第二条是 Focal Loss,它在交叉熵基础上引入调制因子,让模型专注难分样本。水稻病害这个场景,加权交叉熵通常已经够用,Focal Loss 的优势集中在“难例极多、易例极多”的目标检测里,分类任务里调参成本较高,gamma 取 1 或 2 效果差异不大,还多了两个超参要调。
from torch.nn import CrossEntropyLoss # 计算各类别样本数后设置权重 class_counts = torch.tensor([1200, 450, 380, 260, 600], dtype=torch.float) total = class_counts.sum() weights = total / (num_classes * class_counts) # 反比归一化 criterion_weighted = CrossEntropyLoss(weight=weights.to("cuda"))逻辑说明:权重公式用的是 total / (C * class_count),这样各类权重总和为 1,不会让总损失的尺度偏移太大。加权后小类别的梯度会被放大,训练时要注意监控小类别在验证集上的准确率是否提升,而不是只看整体准确率。如果加了权重后整体准确率掉了 2 个点,但白叶枯病和稻曲病的精准率从 40% 提到 70%,这个 trade-off 在植保场景通常值得接受——漏检稻瘟病可能只是多打一次药,漏检稻曲病可能整批稻谷都受影响。
3.4 输出概率校准:别把 softmax 分数直接当置信度
训练完的模型,softmax 输出的概率分布并不等于真实置信度。CNN 在分布内数据上往往会过度自信,在分布外数据上又不会合理降级。水稻叶部病害识别落地时会遇到大量训练集里没见过的背景、叶片角度和生长阶段,直接用 softmax 分数做阈值判断,会出现“识别错了但置信度 0.98”的现象。
常见做法是 Temperature Scaling 做后处理校准。在保留的验证集上学习一个温度系数 T,把 softmax 的 logits 除以 T 再做 softmax。T 大于 1 会让概率分布更平滑,T 小于 1 更尖锐。用验证集优化 T 使得校准误差(ECE)最小,这样输出概率才能当作物联网预警系统的置信度来用。这个步骤在论文里未必会写,但做工程部署时必须加,不然田间报警系统会被大量假阳性折腾到被人手动关掉。
4. 避坑排查:水稻叶部病害识别最常见的 5 个翻车点
4.1 病斑太小,全局分类模型学不到特征
现象:训练准确率很高,但实际识别时对早期病斑(占叶片面积 5% 以下)基本失灵,验证集上表现尚可,因为验证集里的病斑大多在中后期,面积较大。
原因:Resize 到 224×224 后,早期病斑只剩几个像素,卷积特征图里已经完全丢失。模型学到的其实是叶片颜色和纹理的宏观差异,而不是病斑形态。这和许多论文里“准确率 97%”的差别在于,公开数据集里的图片多是病斑明显的标准照。
解决:两种方案任选。第一是提高输入分辨率到 384 或 512,让早期病斑保留更多像素;第二是改用目标检测或 patch 分类思路,先定位病斑区域再分类,相当于把问题从“全图分类”变成“局部精细分类”。
提示:如果目标是早期预警,务必在测试集里单独留一组早期病斑照片做评估,否则你的模型可能只是“晚期病害识别器”。
4.2 叶片生长阶段干扰:黄化不等于有病
现象:测试集里健康老叶被大量误判为病害,特别是白叶枯病,因为白叶枯病的早期形态就是叶片边缘发黄。
原因:训练集里健康叶片多采自苗期或分蘖期的绿叶,没有覆盖穗期黄化的健康叶。模型学到“叶缘黄化”和“白叶枯病”的强相关,而在自然条件下叶缘黄化更多是氮素代谢和叶龄老化的结果。
解决:自采数据时专门采集不同生育阶段的健康叶片,特别是黄化但无病斑的老叶;训练时做颜色扰动时对绿色通道多做偏移,让模型不把绿色缺失当作唯一依据。这类问题只能靠数据补全,调模型结构没有用。
4.3 标注噪声:非专家标注的数据让测试分数失真
现象:两次训练同一批数据,验证准确率波动超过 3 个点,而且翻来覆去都是同一批图片在变。
原因:水稻病害标注需要一定的植保知识,稻瘟病和胡麻斑病在早期形态上非常接近,非专业人员标注时错误率可能达到 10% 以上。噪声标注会影响模型学到的决策边界,也让验证集失去参考价值。
解决:用“标注—训练—找分歧”的三轮流程。第一轮让标注者给出标注和置信度;第二轮训练后用 CAM 热力图检查高置信度错误样本,把模型和标注有分歧的图片单独挑出来,请植保专家复核。
4.4 训练集和验证集同源,田间效果急剧下滑
现象:实验时验证集准确率 96%,拿着同一套权重去另一个田块测试,准确率掉到 60% 出头。
原因:随机划分数据时,同一个田块、同一天拍摄的照片被拆进训练集和验证集,验证集其实就是训练集的“换了个增强版本”。模型学到的背景纹理、光照条件被当作叶片特征,换到新环境立刻失效。这是农业视觉项目里最容易踩的坑,比模型选型问题杀伤力大得多。
解决:划分数据集必须按田块和日期分组,保证同一田块的照片只出现在训练集或只出现在验证集中,不同生育期也要尽量分割开。更严格的做法是训练集用早稻数据、验证集用晚稻数据,模拟真正的跨时间部署。
4.5 光照和背景干扰:反光叶片让白叶枯病细节丢失
现象:识别正确率在晴天中午明显低于早晨和傍晚,误判集中在白叶枯病上。
原因:水稻叶片在强光下有蜡质反光,反光区域的纹理细节被高光淹没,白叶枯病那种从叶缘延伸的半透明枯斑,在高光下直接变成亮白色,模型分不清是病还是光。
解决:采集时尽量避开正午强光,或用一面小挡板遮光拍摄;数据增强中把亮度扰动幅度适当加大,让模型见过不同光照条件的叶片;如果部署环境是固定的田间摄像头,可以在图像处理前加一个简单的亮度归一化,把高光区域的像素值压回正常范围。
5. 部署验证与迭代技巧:用混淆矩阵和 CAM 图给模型做体检
5.1 混淆矩阵:比 Top-1 准确率更有说服力的验证方法
水稻叶部病害识别里,整体准确率掩盖了太多问题。5 类分类任务里健康叶片占 40%,只要健康叶片全对,其他四类全错,准确率也有 40%。真实场景更要命的是,不同病害的处置方案完全不同,稻瘟病需要预防性喷药,稻曲病需要调整灌水,混淆矩阵才能暴露“哪两类在打架”。
import matplotlib.pyplot as plt from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay import numpy as np # y_true 为真值列表,y_pred 为模型预测列表 cm = confusion_matrix(y_true, y_pred, labels=[0, 1, 2, 3, 4]) disp = ConfusionMatrixDisplay(cm, display_labels=["稻瘟病", "白叶枯病", "纹枯病", "胡麻斑病", "健康"]) disp.plot(cmap="Blues", values_format="d") plt.title("水稻叶部病害混淆矩阵") plt.show() # 输出每个类别的召回率 recall = cm.diagonal() / cm.sum(axis=1) for name, r in zip(["稻瘟病", "白叶枯病", "纹枯病", "胡麻斑病", "健康"], recall): print(f"{name} 召回率: {r:.2%}")逻辑说明:混淆矩阵直接展示了每一对类别之间的混淆程度。如果胡麻斑病那行里被预测成稻瘟病的数量占比很高,说明这类问题的根源在数据层面——两张类别的样本形态本来就接近,下一步要补充的是极端形态样本或重新设计类别定义。参数说明:labels 参数要按训练时类别 id 的顺序传,display_labels 设成中文便于汇报;如果做跨田块泛化测试,只跑单个测试集不够,要把混淆矩阵按田块分组输出,对比同一模型在不同地块的表现差异。
5.2 用 CAM 定位模型到底在看哪里
每次训练完我都要做一轮“模型体检”,核心方法是查看类激活映射(CAM)。CAM 能告诉我们,模型判断“稻瘟病”时,注意力是否真的落在病斑上。如果注意力落在了叶片边缘、露珠或背景土壤,说明模型学到了不该学的关联,这个模型在真实场景里会莫名其妙翻车。
import torch from torchvision.transforms.functional import to_pil_image import matplotlib.pyplot as plt # 注册 forward hook 提取最后一个卷积层的特征图 activation = {} def hook_fn(module, input, output): activation["map"] = output.detach() model.layer4[-1].register_forward_hook(hook_fn) # 推理并生成 CAM model.eval() img_tensor = preprocess(sample_img).unsqueeze(0).cuda() with torch.no_grad(): logits = model(img_tensor) pred_class = logits.argmax(dim=1).item() weights = model.fc.weight[pred_class] # 分类头权重 feature_map = activation["map"][0] # [C, H, W] cam = torch.einsum("c,chw->hw", weights, feature_map) cam = cam.clamp(min=0).numpy() cam = (cam - cam.min()) / (cam.max() - cam.min() + 1e-8) plt.imshow(to_pil_image(img_tensor[0].cpu())) plt.imshow(cam, cmap="jet", alpha=0.5) plt.title(f"Pred: {class_names[pred_class]}") plt.show()逻辑说明:CAM 的本质是用分类头的权重,对最后一个卷积层输出的每个通道做加权求和,得到的空间位置就是模型做判断时的“证据来源”。权重的维度等于类别数,第 pred_class 行的权重向量指示了每个通道对该类别的贡献。einsum 的结构就是把通道维和特征图宽高维做矩阵乘。参数说明:cls_head 可以是 fc.weight 或者 GAP 之后的一维卷积权重,取决于模型结构;如果用的是 Transformer,同理可以通过注意力权重做可视化,但原理相同、实现位置不同。
看 CAM 时重点关注:健康叶片上的注意力分布是否集中在叶脉;病叶上的热力图是否覆盖病斑区域。如果热力图集中在背景、水珠或叶片边缘,则说明训练数据里有隐性偏差,需要回去补数据而不是调模型。
5.3 模型轻量化:剪枝与量化在植保边缘设备上的落地
水稻病害识别要落地到田间,模型实际运行环境通常是 Jetson Nano、RK3588 这类边缘盒子,而不是带 A100 的服务器。ResNet50 的 25.6M 参数在边缘设备上推理一帧在 30~50 毫秒,看起来能跑,但一旦要同时跑多路摄像头,算力压力立刻显现。
常见的轻量化路径是先做通道剪枝再量化。剪枝把对输出影响小的通道去掉,ResNet50 可以剪掉 30% 通道而不掉点;之后再导出 ONNX 转 INT8 量化,模型体积缩小到四分之一左右,推理速度提升明显。这里有个血泪教训:量化必须用部署场景的真实图片做校准集,不能用训练集;训练集图像做过增强,亮度分布和真实田间图像不一致,用训练集校准的量化参数会在真实数据上掉点,且很难查出来。
# PyTorch → ONNX → TensorRT 部署常用命令 python -m torch.onnx.export \ --model model_resnet50.pth \ --output model_resnet50.onnx \ --input-size 3 384 384 \ --opset 17 # 用 trtexec 做 INT8 量化(需要准备校准图片目录) trtexec --onnx=model_resnet50.onnx \ --saveEngine=model_resnet50_int8.engine \ --int8 \ --calib=calibration_data.txt逻辑说明:ONNX 导出时输入尺寸必须和训练时一致,我用的 3×384×384 就是训练时的输入分辨率,不能导出时为了提速改成 224,否则模型内部参数和输入不匹配,输出直接乱掉。INT8 量化需要 read-only 的校准数据文件,每类病害图片都要覆盖,单独用几十张健康叶片做校准会让其他类别误差放大。部署后要对比量化前后每一类的召回率,INT8 相对 FP16 通常掉点在 1% 以内是正常,部分类别掉 3% 以上说明校准集没覆盖到位。
边缘设备部署还有一个容易忽略的细节:田间摄像头拍到的叶片常有遮挡和重叠,单帧识别前先做一个简单的背景分割或用检测框框出叶片区域,能显著提高分类准确率。这也是为什么真正商用的系统普遍是“检测+分类”两级结构——第一级 YOLO 检测叶片位置,第二级对裁剪后的叶片区域做病害分类。单模型端到端识别在实验里好看,到了自然田块场景波动太大。如果一开始就明确要部署,架构上尽早按两级结构设计,避免后面推倒重来。
做这个方向半年后,我自己养成的一个习惯是:每次训练完固定导出三类产物——带 CAM 可视化的分析报告、分田块评估的混淆矩阵、量化前后的对比表。不带这三样东西,训练结果就只能算是实验,谈不上有效验证。给边缘设备做模型时用真实场景图片跑一遍推理,看输出的注意力位置是否和预期一致,这个环节省不掉。希望这些踩坑经验能帮你少走一些弯路。
本文还有配套的精品资源,点击获取