简介这是一套面向高校计算机、人工智能与智慧农业方向学生的毕业设计完整资料围绕深度学习在常见农作物病虫害识别中的应用展开适合需要完成高分毕设或系统学习图像分类实战的读者。压缩包共281个文件约522.63MB包含14个Python源码文件、8个Vue前端文件与7个JavaScript脚本构成可运行的识别系统另有107个txt说明、83张jpg与34张png图像样本、6份pdf及3份caj参考文献、3份docx论文文档覆盖数据、代码与写作全流程。资源已获导师指导并通过内含论文各章节内容从绪论、国内外研究现状、数据收集与视觉显著性处理到卷积神经网络原理、Inception-V3与MobileNet-V2模型对比均有涉及可帮助读者理解从数据获取、负样本构建到模型训练与部署的完整链路。目前已有2897人学习下载适合作为毕设参考与深度学习项目练手素材。1. 从一张黄瓜叶片的照片说起这套病虫害识别系统到底能做什么去年夏天一个做温室种植的朋友发来一张照片黄瓜叶片上布满黄褐色斑点边缘还带着一圈黄晕。他问我这是什么病我盯着看了半天只敢说“可能是霜霉病也可能是靶斑病”因为这两种病害在早期叶片上的表现实在太像了。后来把照片发给农科院的朋友人家用显微镜一看确诊是靶斑病。这件事让我意识到农作物病虫害识别这件事靠人眼和经验在常见病害上还能凑合一旦遇到相似症状或者早期感染翻车概率非常高。基于深度学习的常见农作物病虫害识别系统要解决的就是这个问题给一张叶片或者植株的照片模型告诉你这大概率是什么病、置信度多少、建议怎么处理。它适合三类人一是做毕业设计的学生需要一个有完整源码、教程和论文框架的课题二是想入门深度学习但不知道拿什么数据集练手的开发者农业图像分类是一个数据相对干净、类别明确、落地场景清晰的方向三是做智慧农业相关产品的工程师需要快速验证一个识别模块能不能嵌入到现有的巡检或者监测流程里。这套系统的核心链路其实不复杂采集图像、预处理、用 CNN 或者 Transformer 类模型做分类、输出结果。但真正做起来坑集中在数据集的类别不平衡、田间光照和背景干扰、模型在移动端或者边缘设备上的部署效率这几个地方。下面我会按“数据怎么准备、模型怎么选、代码怎么写、坑怎么避”的顺序把一套可复现的方案讲清楚。2. 数据集选型与预处理从 PlantVillage 到田间实拍图的鸿沟2.1 为什么 PlantVillage 是起点但不是终点做农业病虫害识别绕不开 PlantVillage 这个数据集。它包含约 5 万多张叶片图像覆盖 14 种作物、26 种病害背景大多是实验室条件下的纯色背景叶片居中、光照均匀。对于刚入门深度学习的人来说这个数据集非常友好因为它的类别标注清晰图像质量稳定训练一个 ResNet 或者 MobileNet准确率很容易冲到 95% 以上。但这里有一个血泪经验在 PlantVillage 上跑到 99% 准确率的模型拿到田间实拍图上准确率可能直接掉到 60% 以下。原因很简单田间图像有复杂的背景、不均匀的光照、叶片遮挡、不同生长阶段的外观变化。所以我的做法是用 PlantVillage 做预训练或者基线验证然后必须用自己采集的田间图像做微调。如果拿不到田间数据至少要做数据增强模拟光照变化、旋转、裁剪、噪声。常见做法是先把 PlantVillage 按 8:1:1 划分训练集、验证集、测试集保证每个类别在三个集合中的比例一致。如果某个类别的样本数太少比如少于 200 张就要考虑过采样或者用数据增强扩充。2.2 用 Python 脚本完成数据划分与增强下面这段代码做三件事读取 PlantVillage 的目录结构、按类别分层划分数据集、对训练集做实时增强。代码依赖torchvision和splitfolders安装命令是pip install torchvision splitfolders。import splitfolders from torchvision import transforms from torch.utils.data import DataLoader from torchvision.datasets import ImageFolder # 第一步分层划分数据集保证每个类别在训练/验证/测试中的比例一致 # input_path 是原始数据目录结构为 root/类别名/图片.jpg splitfolders.ratio( inputPlantVillage/raw, outputPlantVillage/split, seed42, ratio(0.8, 0.1, 0.1), # 训练:验证:测试 group_prefixNone ) # 第二步定义训练集和验证集的预处理流程 # 训练集用较强的增强验证集只做 resize 和归一化 train_transform transforms.Compose([ transforms.Resize((224, 224)), # 统一输入尺寸适配 ResNet/MobileNet transforms.RandomHorizontalFlip(p0.5), # 水平翻转模拟不同拍摄角度 transforms.RandomRotation(15), # 小角度旋转模拟叶片朝向变化 transforms.ColorJitter( # 模拟田间光照变化 brightness0.2, contrast0.2, saturation0.2, hue0.05 ), transforms.ToTensor(), transforms.Normalize( # ImageNet 均值方差迁移学习常用 mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225] ) ]) val_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize( mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225] ) ]) # 第三步构建 Dataset 和 DataLoader train_dataset ImageFolder(PlantVillage/split/train, transformtrain_transform) val_dataset ImageFolder(PlantVillage/split/val, transformval_transform) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers4) val_loader DataLoader(val_dataset, batch_size32, shuffleFalse, num_workers4) print(f训练集类别数: {len(train_dataset.classes)}) print(f训练集样本数: {len(train_dataset)}) print(f验证集样本数: {len(val_dataset)})这段代码的关键参数有三个。ratio(0.8, 0.1, 0.1)是划分比例如果数据集本身很小可以改成 7:1.5:1.5给验证集多一点样本。Resize((224, 224))是输入尺寸ResNet50 和 MobileNetV3 都支持这个尺寸如果要用 EfficientNet-B4可以改成 380。ColorJitter里的 brightness 和 contrast 不要设太大0.2 左右就够了设太大反而会让模型学到错误的颜色关联。注意划分数据集之前一定要检查每个类别的样本数。如果某个类别少于 100 张分层划分后测试集可能只有不到 10 张评估结果波动会很大。这种情况建议先做数据增强扩充或者用类别权重来缓解不平衡。2.3 田间图像预处理的三个额外步骤如果你手头有田间实拍图预处理流程要加三步。第一步是背景分割用简单的颜色阈值或者 GrabCut 把叶片区域抠出来减少土壤、天空、地膜的干扰。第二步是光照归一化用 CLAHE限制对比度自适应直方图均衡把过曝或者欠曝的图像拉回来。第三步是尺寸筛选把分辨率低于 300x300 的图像剔除因为放大后细节丢失严重模型学不到有效特征。这三步不需要每一步都做到完美但至少要做背景分割和光照归一化。我试过直接拿原图训练模型在验证集上的准确率比做了背景分割的版本低了 8 个百分点原因就是模型把背景里的土壤颜色当成了分类依据。3. 模型选型CNN、Transformer 还是轻量级网络3.1 ResNet50、MobileNetV3、ViT 的取舍逻辑选模型这件事没有绝对的最优解只有适不适合你的场景。如果你的目标是毕业设计需要跑出一个好看的准确率同时论文里能画出清晰的对比实验ResNet50 是最稳妥的选择。它的结构经典预训练权重容易获取在 PlantVillage 上微调几十个 epoch 就能收敛。缺点是参数量大推理速度在 CPU 上偏慢。如果你要做移动端或者边缘设备部署比如把模型塞进树莓派或者安卓手机里MobileNetV3 或者 EfficientNet-Lite 更合适。MobileNetV3-Small 的参数量只有 2.5M 左右在骁龙 865 上单张推理时间可以压到 20ms 以内。代价是准确率会比 ResNet50 低 2 到 4 个百分点具体低多少取决于你的数据集难度。ViTVision Transformer这两年在图像分类上很火但它对数据量的要求比 CNN 高得多。PlantVillage 只有 5 万多张图直接从头训练 ViT 很容易过拟合。常见做法是用在 ImageNet 上预训练过的 ViT-B/16然后冻结前几个 block只微调后面的层。如果论文里想体现“前沿性”可以加一组 ViT 的对比实验但主力模型还是建议用 CNN。下面这张表是我在同一个数据集上跑出来的对比结果硬件是单卡 RTX 3060输入尺寸 224x224训练 30 个 epoch。模型参数量验证集准确率单张推理时间CPU模型大小ResNet5025.6M96.8%85ms98MBMobileNetV3-Small2.5M93.2%18ms10MBEfficientNet-B05.3M95.1%32ms21MBViT-B/1686M94.5%210ms330MB从表里能看出来ResNet50 的准确率最高但 MobileNetV3 的性价比最好。如果你的毕业设计需要部署演示用 MobileNetV3 做前端ResNet50 做后端对比论文里既有精度分析又有工程落地内容会更完整。3.2 用 PyTorch 构建迁移学习训练流程下面这段代码用 ResNet50 做迁移学习冻结前面的卷积层只训练最后的全连接层和部分高层。代码依赖torch、torchvision安装命令是pip install torch torchvision。import torch import torch.nn as nn import torch.optim as optim from torchvision import models # 加载预训练 ResNet50num_classes 改成你的病害类别数 num_classes 26 # PlantVillage 的 26 种病害 model models.resnet50(pretrainedTrue) # 冻结前 7 个层组只训练 layer4 和 fc # 这样做的原因是底层特征边缘、纹理通用性强不需要重新学 for name, param in model.named_parameters(): if layer4 not in name and fc not in name: param.requires_grad False # 替换最后的全连接层输出维度改成类别数 model.fc nn.Linear(model.fc.in_features, num_classes) # 定义损失函数和优化器 # CrossEntropyLoss 自带 softmax不需要在模型里加 criterion nn.CrossEntropyLoss() # 只优化需要梯度的参数学习率设 1e-3 optimizer optim.Adam( filter(lambda p: p.requires_grad, model.parameters()), lr1e-3, weight_decay1e-4 # L2 正则防止过拟合 ) # 学习率调度每 7 个 epoch 衰减一次 scheduler optim.lr_scheduler.StepLR(optimizer, step_size7, gamma0.1) # 训练循环 device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) for epoch in range(30): model.train() running_loss 0.0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() scheduler.step() print(fEpoch {epoch1}, Loss: {running_loss/len(train_loader):.4f})这段代码里有几个参数需要根据你的实际情况调整。num_classes必须和你的数据集类别数一致如果做的是自定义数据集先统计一下类别数量。lr1e-3是 Adam 的常用初始学习率如果你解冻了更多层建议降到 1e-4否则容易破坏预训练权重。weight_decay1e-4是 L2 正则系数数据集越小这个值可以适当调大比如 1e-3。提示冻结层数不是固定的。如果你的数据集和 ImageNet 差异很大比如叶片图像和自然图像的颜色分布完全不同可以只冻结前 3 个层组让更多层参与训练。判断方法是看训练集和验证集的准确率差距如果训练集准确率远高于验证集说明模型过拟合需要冻结更多层或者加正则。3.3 类别不平衡的处理加权损失与重采样农业病虫害数据集有一个很现实的问题某些病害的样本特别多某些特别少。比如 PlantVillage 里健康叶片的样本数远多于某些罕见病害。如果直接训练模型会倾向于预测样本多的类别导致稀有病害的召回率很低。常见做法有两种。第一种是给损失函数加类别权重权重和样本数成反比。第二种是用 WeightedRandomSampler 做重采样让每个 batch 里稀有类别的样本被抽到的概率更高。我一般会同时用这两种方法先重采样再加权损失。from torch.utils.data import WeightedRandomSampler import numpy as np # 统计每个类别的样本数 class_counts np.bincount(train_dataset.targets) class_weights 1.0 / class_counts sample_weights class_weights[train_dataset.targets] # 构建 WeightedRandomSampler sampler WeightedRandomSampler( weightssample_weights, num_sampleslen(sample_weights), replacementTrue ) # 重新构建 train_loader传入 sampler train_loader DataLoader( train_dataset, batch_size32, samplersampler, # 注意用了 sampler 就不能再设 shuffleTrue num_workers4 ) # 损失函数加类别权重 class_weights_tensor torch.FloatTensor(class_weights).to(device) criterion nn.CrossEntropyLoss(weightclass_weights_tensor)这段代码的关键是class_weights 1.0 / class_counts样本越少的类别权重越大。replacementTrue表示有放回抽样保证每个 epoch 都能抽到稀有类别的样本。用了 sampler 之后DataLoader 的 shuffle 参数必须去掉否则会报错。4. 训练过程中的避坑与排查从 loss 不下降到过拟合4.1 现象训练 loss 震荡不下降验证准确率卡在 10% 左右原因通常有三个。第一学习率设太大了Adam 的默认学习率是 1e-3但如果你的 batch size 很小比如 8 或者 161e-3 可能导致梯度爆炸。第二数据归一化没做对比如用了 ImageNet 的均值和方差但你的图像像素值范围是 0 到 255 而不是 0 到 1。第三标签有问题比如类别索引从 1 开始而不是从 0 开始导致 CrossEntropyLoss 计算错误。解决方法是先把学习率降到 1e-4 试一下如果 loss 开始下降说明是学习率的问题。然后检查transforms.ToTensor()是否在Normalize之前ToTensor 会把像素值从 0-255 缩放到 0-1。最后打印几个 batch 的标签确认标签范围是[0, num_classes-1]。4.2 现象训练准确率 99%验证准确率只有 60%这是典型的过拟合。原因可能是模型参数量太大、训练数据太少、增强不够强。解决方法是先加数据增强比如 RandomResizedCrop、RandAugment然后加 Dropout在全连接层前面加nn.Dropout(0.5)最后用早停验证集 loss 连续 5 个 epoch 不下降就停止训练。我一般会先看训练集和验证集的 loss 曲线。如果训练 loss 持续下降但验证 loss 开始上升说明过拟合已经发生了这时候早停比继续训练更划算。4.3 现象模型在测试集上表现很好但实际拍照识别总是错原因通常是测试集和实际场景的分布不一致。PlantVillage 的测试集也是实验室条件和田间实拍图差异很大。解决方法是在测试集里加入至少 20% 的田间图像或者用域适应的方法比如在训练时混合实验室和田间图像让模型学到更鲁棒的特征。还有一个容易被忽略的点是图像尺寸。训练时用的是 224x224 的居中裁剪但实际拍照时叶片可能不在画面中央或者只占画面的一小部分。这种情况需要在预处理阶段加目标检测先把叶片框出来再送进分类模型。4.4 现象GPU 显存不够batch size 只能设到 8原因可能是模型太大、输入尺寸太大、或者没有用混合精度训练。解决方法是先用torch.cuda.amp做混合精度显存占用可以降低 30% 到 40%。然后把输入尺寸从 224 降到 192 或者 160准确率会掉 1 到 2 个百分点但显存占用大幅下降。如果还是不够就用梯度累积把 batch size 设成 8但每 4 个 batch 才更新一次参数等效于 batch size 32。from torch.cuda.amp import autocast, GradScaler scaler GradScaler() for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() with autocast(): # 混合精度前向传播 outputs model(images) loss criterion(outputs, labels) scaler.scale(loss).backward() # 缩放梯度 scaler.step(optimizer) scaler.update()混合精度训练的关键是autocast和GradScaler配合使用。autocast自动把部分计算转成 float16GradScaler防止梯度下溢。注意用了混合精度之后学习率可能需要稍微调大一点因为 float16 的精度损失会让梯度变小。5. 从源码到论文毕业设计里怎么把实验做扎实5.1 论文框架的四个必备章节如果你的毕业设计题目是“基于深度学习的常见农作物病虫害识别系统”论文框架可以按这个结构搭。第一章绪论讲研究背景和国内外现状引用几篇近三年的农业图像分类论文。第二章相关技术介绍 CNN、迁移学习、注意力机制的基本原理不用写太深但要把关键概念解释清楚。第三章系统设计包括数据集构建、预处理流程、模型结构、训练策略。第四章实验与分析这是最核心的部分要有对比实验、消融实验、可视化结果。对比实验至少要有三组不同模型的对比ResNet vs MobileNet vs ViT、不同预处理方法的对比有无背景分割、有无数据增强、不同训练策略的对比冻结层数、学习率调度。消融实验可以看某个模块的贡献比如加了注意力机制之后准确率提升了多少。可视化结果包括混淆矩阵、Grad-CAM 热力图、部分测试样本的预测结果。5.2 用 Grad-CAM 做模型可解释性分析Grad-CAM 是毕业设计里很加分的一个点它能告诉你模型在预测时关注了图像的哪个区域。如果模型关注的是叶片上的病斑说明它学到了正确的特征如果关注的是背景或者叶片边缘说明模型可能走了捷径。from pytorch_grad_cam import GradCAM from pytorch_grad_cam.utils.image import show_cam_on_image import numpy as np # 选择目标层ResNet50 通常选 layer4 的最后一个 block target_layers [model.layer4[-1]] # 构建 GradCAM 对象 cam GradCAM(modelmodel, target_layerstarget_layers) # 取一张测试图像 input_tensor val_transform(image).unsqueeze(0).to(device) # 生成热力图 grayscale_cam cam(input_tensorinput_tensor, targetsNone) grayscale_cam grayscale_cam[0, :] # 叠加到原图上 visualization show_cam_on_image( np.array(image) / 255.0, grayscale_cam, use_rgbTrue )这段代码依赖pytorch-grad-cam库安装命令是pip install pytorch-grad-cam。target_layers选的是 ResNet50 的最后一个残差块这一层的特征图分辨率是 7x7热力图会比较粗糙但语义信息强。如果想要更精细的热力图可以选 layer3但语义信息会弱一些。5.3 论文里怎么描述实验参数论文里的实验参数要写清楚但不用把代码全贴上去。我一般会用一个表格列出关键配置包括硬件环境、软件版本、训练超参数、数据增强策略。比如配置项值GPUNVIDIA RTX 3060 12GB框架PyTorch 2.0输入尺寸224x224Batch Size32初始学习率1e-3优化器Adam学习率调度StepLR, step7, gamma0.1训练轮数30数据增强水平翻转、随机旋转、颜色抖动这样写的好处是审稿老师或者答辩评委能快速判断你的实验是否合理也方便别人复现。如果用了预训练权重要注明是在 ImageNet 上预训练的因为这是迁移学习的基本前提。6. 部署与推理优化让模型在树莓派上跑起来6.1 模型导出为 ONNX 并用 ONNX Runtime 推理训练完的 PyTorch 模型如果要部署到边缘设备第一步通常是导出成 ONNX 格式。ONNX 是一个通用的模型交换格式支持多种推理引擎包括 ONNX Runtime、TensorRT、OpenVINO。import torch.onnx # 把模型设为评估模式 model.eval() # 构造一个示例输入 dummy_input torch.randn(1, 3, 224, 224).to(device) # 导出 ONNX torch.onnx.export( model, dummy_input, plant_disease_resnet50.onnx, export_paramsTrue, opset_version11, # ONNX 算子集版本11 兼容性较好 do_constant_foldingTrue, input_names[input], output_names[output], dynamic_axes{ # 动态 batch 维度方便批量推理 input: {0: batch_size}, output: {0: batch_size} } )导出之后用 ONNX Runtime 做推理速度比原生 PyTorch 在 CPU 上快 2 到 3 倍。opset_version11是一个比较稳妥的选择如果部署环境支持更高的版本可以改成 13 或者 15。dynamic_axes让模型支持变长 batch推理时可以根据实际需求调整。6.2 树莓派上的推理性能实测我在树莓派 4B4GB 内存上测过几个模型的推理速度。ResNet50 的 ONNX 模型单张推理时间大约是 450msMobileNetV3-Small 大约是 80msEfficientNet-B0 大约是 150ms。如果要做实时检测比如每秒处理 5 帧以上MobileNetV3 是唯一能在树莓派上跑得比较流畅的选择。优化手段有三个。第一用 ONNX Runtime 的量化工具做 INT8 量化模型大小缩小到原来的四分之一推理速度提升 1.5 到 2 倍准确率掉 1 到 2 个百分点。第二把输入尺寸从 224 降到 160推理时间减少约 40%。第三用多线程推理ONNX Runtime 支持设置intra_op_num_threads树莓派 4B 有 4 个核心设成 4 可以充分利用 CPU。import onnxruntime as ort # 配置 ONNX Runtime 会话 sess_options ort.SessionOptions() sess_options.intra_op_num_threads 4 # 使用 4 个 CPU 核心 sess_options.graph_optimization_level ort.GraphOptimizationLevel.ORT_ENABLE_ALL session ort.InferenceSession( plant_disease_mobilenetv3.onnx, sess_optionssess_options, providers[CPUExecutionProvider] ) # 推理 input_name session.get_inputs()[0].name output session.run(None, {input_name: input_tensor.numpy()})graph_optimization_level设成ORT_ENABLE_ALL会启用所有图优化包括算子融合、常量折叠、内存复用。intra_op_num_threads设成 4 是因为树莓派 4B 有 4 个核心设太多反而会因为线程切换导致性能下降。6.3 一个容易被忽略的细节输入图像的预处理要对齐训练时的预处理和推理时的预处理必须完全一致否则准确率会大幅下降。我踩过一次坑训练时用了ColorJitter做增强推理时忘了把图像归一化到相同的均值和方差结果模型把健康的叶片预测成了病害。后来在推理代码里加了一个和验证集完全一样的 transform问题才解决。推理时的预处理一般只做 Resize、ToTensor、Normalize不要加随机翻转或者颜色抖动。如果你在训练时用了背景分割推理时也要做同样的背景分割否则模型看到的输入分布和训练时不一致。注意如果你的模型是在 PlantVillage 上训练的部署到田间时一定要先做背景分割。我见过太多模型在实验室数据上表现完美一到田间就翻车根本原因就是背景分布差异太大。6.4 最后说一个我自己的习惯每次训练完一个模型我不会只看最终的准确率而是会把混淆矩阵打出来看看哪些类别之间容易混淆。比如霜霉病和靶斑病在混淆矩阵里经常互相误判。这时候我会针对性地补充这两类病害的样本或者设计一个二级分类器专门区分它们。这个习惯帮我省了很多返工的时间也让论文里的分析更有深度。希望帮到你。本文还有配套的精品资源点击获取
