简介这是一套面向农业信息化、计算机视觉方向的开发者与学生的农作物病虫害识别分类完整项目资源围绕图像数据采集、预处理、特征提取、模型训练、评估与部署全流程展开适合课程设计、毕业设计或入门深度学习实战。包内共61个文件以ipynb实验笔记、md说明文档、png与jpg图像素材为主辅以py脚本、js与css前端资源、yml与yaml配置、pkl模型文件及Dockerfile等压缩包约88.66MB目录结构清晰便于按模块查阅。项目覆盖ResNet50、VGG16、VGG19、DenseNet121、PyTorch、TensorFlow、Keras、Fastai等多种实现路径并附带Flask服务端与部署指南可帮助读者理解从数据到上线的完整链路。目前已有306人学习下载适合希望快速复现病虫害分类方案、积累模型对比与工程部署经验的学习者参考。1. 农作物病虫害识别分类项目从数据集到推理一套能跑通的 Python 方案田里拍一张叶片照片几秒钟后手机告诉你这是稻瘟病还是纹枯病——这件事在实验室里早就不新鲜但真正落到种植户手里中间隔着的不是模型精度而是「数据怎么标、模型怎么选、部署到哪」这三道坎。基于 Python 的农作物病虫害识别分类项目核心就是把这套流程用最通用的工具链串起来数据集负责喂饱模型源码负责训练和推理说明文档负责让下一个人能接手。它适合两类人一类是想拿一个完整 CV 项目练手的 Python 学习者另一类是想把病虫害识别真正用到田间地头的农业技术从业者。热词里反复出现的 python、数据集、分类、源码恰好对应了这个项目的四个组成部分缺一个都跑不起来。下面按「先搞清楚数据长什么样再动手训练最后踩坑排查」的顺序展开。2. 数据集怎么选、怎么标、怎么切决定模型上限的三件事2.1 公开数据集和自采数据的取舍农作物病虫害识别领域最常被引用的公开数据集是 PlantVillage它包含约 5 万多张叶片图像覆盖 38 个类别涵盖苹果、玉米、葡萄、番茄等作物的健康与患病叶片。这个数据集的好处是背景干净、光照均匀、标注质量高适合用来验证模型结构是否合理。但它的短板也很明显全是实验室条件下拍摄的单叶图像背景被抠得很干净跟田间实拍的复杂场景差距很大。如果你直接拿 PlantVillage 训出来的模型去识别田里拍的照片准确率会断崖式下跌这是最常见的翻车场景之一。我的建议是分两步走先用 PlantVillage 把训练流程跑通确认模型结构和超参没问题再逐步混入自采的田间图像做微调。自采数据不需要一上来就几万张每个类别有 200 到 500 张真实场景图就能显著改善模型的泛化能力。采集时注意三点同一病害在不同光照、不同角度、不同生育期各拍一些健康叶片也要拍否则模型会把「没病」当成一个不存在的类别尽量让背景里出现土壤、杂草、其他叶片别只拍一片叶子。2.2 数据标注的格式和工具选择分类任务和检测任务的标注方式完全不同。分类只需要把图片放进对应类别的文件夹文件夹名就是标签检测才需要画框。这个项目标题写的是「识别分类」所以默认按分类任务处理目录结构长这样dataset/ ├── train/ │ ├── 稻瘟病/ │ │ ├── img_001.jpg │ │ └── img_002.jpg │ ├── 纹枯病/ │ └── 健康/ ├── val/ │ ├── 稻瘟病/ │ └── ... └── test/ └── ...如果后续想升级成检测任务可以用 LabelImg 或 Roboflow 标注成 YOLO 格式但那是另一个方向了。分类任务用文件夹分目录是最省事的做法PyTorch 的ImageFolder和 TensorFlow 的image_dataset_from_directory都能直接读。标注时最容易犯的错是类别定义模糊。比如「叶斑病」和「褐斑病」在早期症状上很像如果标注的人自己都分不清模型学出来的决策边界就是乱的。解决办法是提前写一份标注手册每个类别配 3 到 5 张典型图标注人员先试标 50 张统一口径后再批量做。2.3 训练集、验证集、测试集的切分比例常见做法是 7:2:1 或 8:1:1。但农作物病虫害数据有个特殊性同一片田、同一天拍的照片高度相似如果随机切分训练集和验证集里会出现几乎一样的图验证准确率虚高。正确的做法是按「田块」或「拍摄批次」切分保证验证集里的图来自训练集没见过的田块。这一点在论文里经常被忽略但在实际落地时影响很大。切分完之后检查一下类别分布。如果某个类别只有几十张图要么做数据增强补到至少 200 张要么在损失函数里加类别权重。别指望模型能从 30 张图里学会一个类别那不是机器学习那是玄学。import os import random import shutil def split_dataset(src_dir, dst_dir, ratios(0.7, 0.2, 0.1)): 按类别分层切分数据集保证每个类别在 train/val/test 中比例一致 classes os.listdir(src_dir) for cls in classes: cls_dir os.path.join(src_dir, cls) images [f for f in os.listdir(cls_dir) if f.lower().endswith((.jpg, .png, .jpeg))] random.shuffle(images) n len(images) n_train int(n * ratios[0]) n_val int(n * ratios[1]) splits { train: images[:n_train], val: images[n_train:n_train n_val], test: images[n_train n_val:] } for split, files in splits.items(): out_dir os.path.join(dst_dir, split, cls) os.makedirs(out_dir, exist_okTrue) for f in files: shutil.copy(os.path.join(cls_dir, f), os.path.join(out_dir, f)) print(切分完成) split_dataset(./raw_dataset, ./dataset)这段代码的关键点是按类别分别切分而不是把所有图片混在一起随机分。ratios参数控制训练、验证、测试的比例类别样本少的时候可以把验证集比例调低到 0.15把更多数据留给训练。random.shuffle之前最好设一下随机种子保证每次切分结果可复现。3. 模型选型与训练从 ResNet 到轻量网络的落地路径3.1 为什么先用 ResNet50 而不是一上来就追新农作物病虫害分类本质上是一个细粒度图像分类问题不同病害之间的差异可能只在叶斑的形状和颜色上。ResNet50 在 ImageNet 上预训练过的权重已经学到了丰富的纹理和颜色特征拿来做迁移学习通常用几百张图就能达到不错的精度。相比之下Vision Transformer 这类新结构在小数据集上更容易过拟合训练成本也更高。我一般会先用 ResNet50 跑一个 baseline记录准确率、混淆矩阵和推理耗时。如果准确率够用但推理太慢再考虑换成 MobileNetV3 或 EfficientNet-B0 这类轻量网络。如果准确率不够再分析是数据问题还是模型容量问题。别一上来就调模型结构大多数时候问题出在数据上。import torch import torch.nn as nn from torchvision import models, transforms from torch.utils.data import DataLoader from torchvision.datasets import ImageFolder # 数据增强训练集用随机裁剪、翻转、颜色抖动验证集只做缩放和归一化 train_tf transforms.Compose([ transforms.RandomResizedCrop(224, scale(0.7, 1.0)), transforms.RandomHorizontalFlip(), transforms.RandomVerticalFlip(), transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) val_tf transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) train_ds ImageFolder(./dataset/train, transformtrain_tf) val_ds ImageFolder(./dataset/val, transformval_tf) train_loader DataLoader(train_ds, batch_size32, shuffleTrue, num_workers4) val_loader DataLoader(val_ds, batch_size32, shuffleFalse, num_workers4) # 加载预训练 ResNet50替换最后一层全连接 model models.resnet50(weightsmodels.ResNet50_Weights.IMAGENET1K_V2) num_classes len(train_ds.classes) model.fc nn.Linear(model.fc.in_features, num_classes) model model.cuda() criterion nn.CrossEntropyLoss() optimizer torch.optim.AdamW(model.parameters(), lr1e-4, weight_decay1e-4) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max30)RandomResizedCrop的scale参数控制裁剪区域占原图的比例设成 0.7 到 1.0 意味着模型能看到叶片的不同局部。ColorJitter模拟不同光照条件对田间图像尤其重要。归一化用的均值和标准差是 ImageNet 的统计值因为用了预训练权重这一步必须保持一致。AdamW的weight_decay设 1e-4 是为了抑制过拟合如果训练集很小可以调到 1e-3。3.2 训练循环里必须监控的三个指标训练过程中只看 loss 是不够的。我一般会同时记录训练 loss、验证 loss 和验证准确率。如果训练 loss 持续下降但验证 loss 开始上升说明过拟合了这时候要么加数据增强要么提前停止。如果两个 loss 都不降说明学习率可能太大或者模型容量不够。def train_one_epoch(model, loader, criterion, optimizer): model.train() total_loss, correct, total 0, 0, 0 for imgs, labels in loader: imgs, labels imgs.cuda(), labels.cuda() optimizer.zero_grad() outputs model(imgs) loss criterion(outputs, labels) loss.backward() optimizer.step() total_loss loss.item() * imgs.size(0) correct (outputs.argmax(1) labels).sum().item() total imgs.size(0) return total_loss / total, correct / total def evaluate(model, loader, criterion): model.eval() total_loss, correct, total 0, 0, 0 with torch.no_grad(): for imgs, labels in loader: imgs, labels imgs.cuda(), labels.cuda() outputs model(imgs) loss criterion(outputs, labels) total_loss loss.item() * imgs.size(0) correct (outputs.argmax(1) labels).sum().item() total imgs.size(0) return total_loss / total, correct / total best_acc 0 for epoch in range(30): train_loss, train_acc train_one_epoch(model, train_loader, criterion, optimizer) val_loss, val_acc evaluate(model, val_loader, criterion) scheduler.step() print(fEpoch {epoch1}: train_loss{train_loss:.4f} train_acc{train_acc:.4f} fval_loss{val_loss:.4f} val_acc{val_acc:.4f}) if val_acc best_acc: best_acc val_acc torch.save(model.state_dict(), best_model.pth)evaluate函数里用了torch.no_grad()这是必须的否则验证阶段会白白占用显存。保存模型时只存state_dict而不是整个模型对象这样加载时更灵活也不容易因为代码结构变化导致加载失败。best_acc用来记录历史最佳验证准确率只有超过它才保存避免把过拟合的模型存下来。3.3 学习率调度和早停策略CosineAnnealingLR 让学习率按余弦曲线从初始值降到接近零前期下降慢、后期下降快适合迁移学习场景。如果验证 loss 连续 5 个 epoch 不下降就可以停了再训下去只会过拟合。早停的耐心值设 5 到 10 都行取决于数据集大小和 batch size。还有一个容易被忽略的点冻结和解冻。迁移学习初期可以只训练最后的全连接层等 loss 稳定后再解冻整个网络做微调。这样能防止随机初始化的分类层在训练初期产生大梯度把预训练权重带偏。具体做法是先设model.fc.requires_grad_(True)和其他层requires_grad_(False)训 5 个 epoch 后再全部解冻。4. 推理部署与性能优化让模型在真实设备上跑起来4.1 从 PyTorch 模型到 ONNX 的导出与验证训练完的.pth文件只能在 PyTorch 环境里用如果要在手机或嵌入式设备上推理通常要转成 ONNX 格式。导出时注意输入尺寸要和训练时一致动态轴设置好 batch 维度。import torch.onnx model.load_state_dict(torch.load(best_model.pth)) model.eval() dummy_input torch.randn(1, 3, 224, 224).cuda() torch.onnx.export( model, dummy_input, crop_disease.onnx, input_names[input], output_names[output], dynamic_axes{input: {0: batch}, output: {0: batch}}, opset_version11 )导出后一定要用 onnxruntime 跑一遍验证对比 PyTorch 和 ONNX 的输出差异。如果差异超过 1e-3说明某些算子导出有问题需要检查是否有不支持的操作。import onnxruntime as ort import numpy as np sess ort.InferenceSession(crop_disease.onnx) test_input np.random.randn(1, 3, 224, 224).astype(np.float32) onnx_out sess.run(None, {input: test_input})[0] with torch.no_grad(): torch_out model(torch.from_numpy(test_input).cuda()).cpu().numpy() print(最大差异:, np.abs(onnx_out - torch_out).max())4.2 推理速度优化的三个实用手段第一个是量化。把 FP32 模型转成 INT8推理速度能提升 2 到 4 倍精度损失通常在 1% 以内。PyTorch 支持动态量化和静态量化静态量化需要校准数据集精度更好但流程稍复杂。第二个是输入分辨率。训练时用 224x224推理时如果设备算力有限可以降到 160x160 或 128x128。精度会掉一些但速度提升明显。具体降多少要看你的精度底线在哪里。第三个是批处理。如果服务端推理把多张图攒成一个 batch 一起送进去GPU 利用率会高很多。但移动端通常一次只处理一张这个手段用不上。4.3 部署到移动端或边缘设备的注意事项如果目标是手机 App可以用 PyTorch Mobile 或 ONNX Runtime Mobile。如果是嵌入式设备比如树莓派ONNX Runtime 加 INT8 量化是比较稳妥的组合。部署前一定要在目标设备上实测推理耗时和内存占用别只在开发机上跑通了就以为万事大吉。还有一个血泪经验模型文件别直接放在 App 的 assets 里有些平台对 assets 大小有限制。可以放到服务端App 启动时下载或者用模型压缩工具把文件压到几 MB 以内。5. 避坑与排查病虫害识别项目里最容易翻车的五个地方5.1 验证准确率很高田间实测一塌糊涂现象在 PlantVillage 上验证准确率 98%拿到田里拍的照片识别准确率不到 50%。原因训练集和真实场景的数据分布差异太大。PlantVillage 是实验室单叶图背景干净、光照均匀田间图有土壤、杂草、阴影、遮挡。解决混入至少 20% 的自采田间图像做微调数据增强里加上随机遮挡和亮度变化。如果条件允许用 CycleGAN 做风格迁移把实验室图转成田间风格再训练。5.2 某个类别总是被误判成另一个类别现象混淆矩阵显示「稻瘟病」和「纹枯病」互相误判的比例很高。原因这两个类别在早期症状上确实相似标注时边界不清晰模型学到的特征区分度不够。解决先检查标注是否有误把模棱两可的图挑出来重新确认。如果标注没问题考虑加一个细粒度分类头或者用注意力机制让模型关注病斑区域而不是整片叶子。5.3 训练 loss 不下降准确率卡在随机水平现象训练了十几个 epochloss 一直在 2.3 左右准确率跟瞎猜差不多。原因最常见的是标签和图片没对上比如文件夹名和类别索引映射错了。其次是学习率太大梯度爆炸。解决先打印几个 batch 的图片和标签肉眼确认对应关系。然后把学习率降到 1e-5 试试。如果还不行检查数据预处理里有没有把图片归一化两次。5.4 模型文件太大部署时内存不够现象ResNet50 的.pth文件接近 100MB转到移动端后 App 启动就崩。原因FP32 权重占空间大移动端内存有限。解决用 INT8 量化把模型压到 25MB 左右或者换 MobileNetV3 这类轻量结构。如果还不行考虑把模型放到服务端客户端只负责拍照上传和接收结果。5.5 推理结果不稳定同一张图两次识别结果不一样现象同一张叶片照片连续识别两次一次说是稻瘟病一次说是健康。原因推理时没有设model.eval()BatchNorm 和 Dropout 还在训练模式。或者输入图片的预处理和训练时不一致。解决推理前必须调model.eval()并且用torch.no_grad()包住。预处理流程要和验证集完全一致特别是归一化的均值和标准差。6. 把模型精度再往上推一档类别不平衡与难例挖掘的实操技巧类别不平衡是农作物病虫害数据集里的常态。健康叶片往往最多某些罕见病害可能只有几十张。直接用交叉熵损失训练模型会倾向于预测多数类少数类的召回率很低。我一般会用加权交叉熵权重按类别样本数的倒数来设。from sklearn.utils.class_weight import compute_class_weight import numpy as np labels [train_ds.classes.index(cls) for cls in train_ds.classes for _ in range(len(os.listdir(os.path.join(./dataset/train, cls))))] class_weights compute_class_weight(balanced, classesnp.unique(labels), ylabels) weights torch.tensor(class_weights, dtypetorch.float32).cuda() criterion nn.CrossEntropyLoss(weightweights)compute_class_weight的balanced模式会自动按样本数反比设权重样本越少的类别权重越高。这样模型在训练时会更关注少数类代价是多数类的准确率可能略微下降但整体 F1 分数通常会提升。难例挖掘是另一个实用技巧。每个 epoch 结束后把验证集里预测错误的样本挑出来复制到训练集里再训一轮。这些难例往往是标注模糊、光照极端或者病斑不典型的图模型从它们身上学到的东西比从简单样本多得多。我一般会做两到三轮难例挖掘每轮把验证集里置信度低于 0.6 的样本也加进去。还有一个技巧是测试时增强TTA。推理时对同一张图做多次不同变换翻转、裁剪、缩放把多次预测结果平均。这个方法不需要重新训练精度通常能提升 1 到 2 个百分点代价是推理时间翻几倍。如果服务端推理这个开销可以接受移动端就不太合适了。最后说一个我自己的习惯每次训练完我都会把混淆矩阵和每个类别的 F1 分数打印出来而不是只看总体准确率。总体准确率 95% 听起来很好但如果某个重要病害的召回率只有 60%这个模型在田间就是不可用的。病虫害识别的容错率很低漏判一个病害可能导致整片田减产所以宁可牺牲一点总体准确率也要保证每个类别的召回率都在可接受范围内。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
手机识别数据集:COCO JSON转YOLOv8实战与避坑指南 简介:这份手机识别数据集面向计算机视觉开发者、目标检测学习者及需要手机类样本的算法训练人员,用于解决手机目标检测与识别任务中样本不足、标注不规范的问题。资源包共2000个文件,以1997张jpg原始图片为主,另附3个json标注文件… · 2026/9/23 21:39:09
Python实战:BERT多分类图书数据集,完整源码与混淆矩阵分析 简介:这份资源是面向高校学生与Python初学者的一套基于BERT的图书多分类课程设计完整方案,适合作为期末大作业、课设提交或NLP入门实战练习。项目以预训练语言模型BERT为核心,围绕图书文本的多类别预测任务,提供从数据加载、模型定… · 2026/9/23 21:39:09
Yii 2.0 从 1.1 版本升级指南:核心差异、重构要点与迁移实战 后端Web框架 【免费下载链接】yii2 Yii 2: The Fast, Secure and Professional PHP Framework 项目地址: https://gitcode.com/gh_mirrors/yi/yii2 点击查看 免费下载 本篇升级指南以当前仓库(GitHub 加速计划 / yi / yii2)中的 docs/guide-… · 2026/9/23 22:21:26
RecRecNet广角图像畸变矫正:端到端可微网格变换与细节重建实战解析 简介:基于RecRecNet算法的广角图像畸变矫正Python项目,提供完整源码、预训练模型与训练代码,面向计算机视觉相关专业的毕设、课程设计及工程入门人群。项目已稳定运行验证,可直接复现或在理解原理后进行二次开发。包内共26个文件&… · 2026/9/23 22:20:48
YOLO火车轨道手推车数据集实战:从标签解析到训练避坑指南 简介:这份数据集面向YOLO系列目标检测算法开发者,专注于火车、轨道、手推车三类物体的检测任务,提供三千七百九十三张图像对应的完整标注。资源已经按照训练和验证需求划分好,并附带数据配置文件,可以直接用于主流YOLO… · 2026/9/23 22:20:48
10吨锅炉配多大的脱硫塔?风量、直径、高度怎么算 开篇结论:脱硫塔选多大,不是看感觉,是看两个数:烟气量定塔径,入口SO₂浓度定塔高和层数。1蒸吨锅炉约2500–3500 m/h烟气,10吨约25000–35000 m/h,参考塔径2.0–2.6米。浓度高就加喷淋层。1. 塔… · 2026/9/23 22:20:29
RedwoodJS 教程实战:从 Prisma 建模到 Service 测试,为博客添加完整评论功能 RedwoodJS 教程实战:从 Prisma 建模到 Service 测试,为博客添加完整评论功能 【免费下载链接】redwood RedwoodGraphQL 项目地址: https://gitcode.com/gh_mirrors/re/redwood
本篇技术指南以 RedwoodJS 官方教程第 6 章为核心,完整演… · 2026/9/23 22:20:17
3招搞定手机怎么下载微信面试难题实战项目解析 3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29