
简介这份资源是面向深度学习入门者、人工智能课程设计与毕业设计学生的农作物病害识别完整项目包围绕卷积神经网络对小麦、水稻、玉米、番茄、棉花、大豆、油菜等作物病害图像进行分类识别帮助读者理解从数据采集、模型训练到验证测试的完整流程。压缩包共455个文件约119.76MB包含93个java源码、118个xml配置、95个png与6个jpg图像样本、76个csv数据集、38个so库及12个jar依赖另有gradle构建脚本、properties配置与README说明目录结构规范便于按模块查阅。已有76人学习下载。读者可据此掌握图像处理、模式识别与模型调参思路并参考现成的工程组织方式完成自己的课程或毕设项目。1. 农作物病害识别项目从数据集到推理一份能跑通的深度学习课程设计资源农作物病害识别这个方向每年到了课程设计和毕业设计选题季就会被翻出来。原因不复杂数据集公开、任务定义清晰、模型选型成熟做出来还能拍几张识别效果图放进论文里。但真正动手的人都知道从下载数据集到跑出一个能看的准确率中间隔着一堆琐碎的坑——图片尺寸不统一、类别样本不均衡、训练集和验证集划分方式不对导致指标虚高、推理时预处理和训练时对不上。这份「基于深度学习的农作物病害识别.zip」就是冲着这些环节来的它把数据组织、模型搭建、训练脚本和推理入口打包在一起适合正在做课程设计或毕业设计、需要一份能直接跑通并在此基础上改代码的从业者和学生。下面我按实际拆包和复现的顺序把这份资源讲清楚。2. 拆开压缩包先看什么目录结构与技术栈判断拿到一个深度学习项目压缩包我一般不会急着装环境跑代码而是先花十分钟把目录结构和依赖文件翻一遍。这一步能判断出项目是「真能跑」还是「只放了几个脚本」也能提前发现版本冲突的隐患。2.1 典型目录布局与各目录职责这类农作物病害识别项目解压后常见的结构大致如下不同版本可能略有出入以实际为准crop_disease_recognition/ ├── data/ │ ├── train/ │ │ ├── 类别A/ │ │ └── 类别B/ │ └── val/ │ ├── 类别A/ │ └── 类别B/ ├── models/ │ └── model.py ├── utils/ │ ├── dataset.py │ └── transforms.py ├── train.py ├── predict.py ├── config.py └── requirements.txtdata/下按类别分文件夹这是ImageFolder能直接读取的格式省去了自己写标签映射的麻烦。models/model.py放网络定义utils/dataset.py和utils/transforms.py分别管数据加载和预处理train.py和predict.py是两个入口config.py集中管理超参数。这种拆分方式在课程设计里算比较规范的改起来不用满文件找参数。提示如果解压后发现data/是空的说明数据集需要单独下载先看README或config.py里有没有数据路径说明别对着空目录硬跑。2.2 依赖版本与框架判断打开requirements.txt重点看三样东西深度学习框架及版本、图像处理库、以及有没有额外的训练工具库。常见组合是 PyTorch torchvision Pillow numpy有些项目会带tqdm做进度条、matplotlib画曲线、scikit-learn算混淆矩阵。# 查看依赖清单 cat requirements.txt # 常见输出示例 torch1.10.0 torchvision0.11.0 Pillow8.0.0 numpy1.21.0 tqdm matplotlib scikit-learn这里有个血泪经验torch和torchvision的版本必须匹配比如 torch 1.10 对应 torchvision 0.11装错版本会在导入时直接报RuntimeError。如果requirements.txt里只写了torch没写版本建议手动指定一个稳定组合别让 pip 自己解析。# 推荐用虚拟环境隔离避免污染全局 python -m venv venv source venv/bin/activate # Linux/Mac # venv\Scripts\activate # Windows # 指定版本安装避免解析出奇怪组合 pip install torch1.10.0 torchvision0.11.0 -i https://pypi.tuna.tsinghua.edu.cn/simple pip install -r requirements.txt参数说明-i后面跟的是国内镜像源能明显加快下载速度虚拟环境的作用是把这份项目的依赖和系统里其他项目隔开后面如果装崩了直接删掉venv重来不用重装系统 Python。2.3 配置文件里的关键参数config.py是判断项目可改性的窗口。我一般会重点看这几个字段num_classes类别数、img_size输入尺寸、batch_size、lr学习率、epochs、data_root数据根目录。这些参数决定了你换数据集或换模型时要动哪里。# config.py 常见内容示意 class Config: data_root ./data num_classes 10 # 类别数换数据集必须改 img_size 224 # 输入尺寸要和模型匹配 batch_size 32 lr 0.001 epochs 30 device cuda if torch.cuda.is_available() else cpu逻辑说明num_classes如果和实际数据类别数对不上训练时会在计算损失那一步报维度错误img_size要和网络最后几层的下采样倍数配合比如 224 是 ResNet 系列的常用输入device这一行是自动判断有没有 GPU没有就退回 CPU课程设计阶段用 CPU 跑小数据集也能出结果只是慢。3. 数据准备与增强让模型真正学到病害特征数据这一环是农作物病害识别项目里最容易被低估的部分。很多人把数据集往data/一丢就开始训练结果准确率卡在某个值上不去回头查半天才发现是划分方式或者增强策略出了问题。3.1 数据集组织与划分策略ImageFolder要求每个类别一个文件夹文件夹名就是类别标签。假设你手头的数据集是原始图片加一个标签文件需要先转成这种结构。import os import shutil import pandas as pd from sklearn.model_selection import train_test_split # 假设原始数据images/ 下所有图片labels.csv 两列 filename, label df pd.read_csv(labels.csv) train_df, val_df train_test_split( df, test_size0.2, stratifydf[label], random_state42 ) def copy_to_folder(sub_df, split): for _, row in sub_df.iterrows(): dst_dir os.path.join(data, split, str(row[label])) os.makedirs(dst_dir, exist_okTrue) shutil.copy( os.path.join(images, row[filename]), os.path.join(dst_dir, row[filename]) ) copy_to_folder(train_df, train) copy_to_folder(val_df, val)逻辑说明stratifydf[label]是关键参数它保证训练集和验证集里每个类别的比例一致。农作物病害数据集往往存在类别不均衡比如健康样本远多于病害样本如果不分层验证集里可能某个类别只有一两张图算出来的准确率波动极大。random_state42固定随机种子保证每次划分结果一致方便复现。注意不要用训练集里的图片做验证。有些项目为了省事直接从训练集里随机抽几张当验证集这样得到的准确率会虚高答辩时被问到泛化能力就露馅了。3.2 训练集与验证集的增强差异预处理这块训练集和验证集必须用不同的 transform。训练集做随机增强验证集只做 resize 和归一化这是标准做法但新手经常两边用同一套。from torchvision import transforms # 训练集随机裁剪、翻转、颜色抖动 train_transform transforms.Compose([ transforms.RandomResizedCrop(224, scale(0.8, 1.0)), transforms.RandomHorizontalFlip(p0.5), transforms.RandomVerticalFlip(p0.3), 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]) ]) # 验证集只做确定性的 resize 和归一化 val_transform 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]) ])参数说明RandomResizedCrop的scale(0.8, 1.0)表示随机裁剪面积占原图的 80% 到 100%这个范围别设太宽否则可能把病斑裁掉RandomVerticalFlip概率设 0.3 而不是 0.5因为农作物叶片图像上下翻转后语义可能不自然Normalize用的均值和方差是 ImageNet 的统计值如果你从零训练而不是用预训练权重可以改成自己数据集的统计值但用预训练权重时保持默认。3.3 类别不均衡的处理农作物病害数据集里某些病害样本可能只有几十张而健康样本有上千张。直接训练会导致模型偏向多数类。常见做法有两种在损失函数里加类别权重或者用重采样。import torch from torch.utils.data import DataLoader, WeightedRandomSampler from collections import Counter # 统计各类别样本数 targets [s[1] for s in dataset.samples] class_counts Counter(targets) print(类别分布:, class_counts) # 方案一损失函数加权 weights [1.0 / class_counts[i] for i in range(num_classes)] class_weights torch.FloatTensor(weights).to(device) criterion torch.nn.CrossEntropyLoss(weightclass_weights) # 方案二WeightedRandomSampler 重采样 sample_weights [1.0 / class_counts[t] for t in targets] sampler WeightedRandomSampler(sample_weights, num_sampleslen(sample_weights), replacementTrue) train_loader DataLoader(dataset, batch_size32, samplersampler)逻辑说明方案一改的是损失函数让少数类样本的损失被放大方案二改的是采样策略让每个 batch 里各类别比例更均衡。两者选一个就行同时用可能矫枉过正。我一般先用方案一改动小不影响 DataLoader 的其他参数。4. 模型搭建与训练从预训练权重到学习率调度模型这块课程设计里最常见的选择是 ResNet 或 EfficientNet 加预训练权重。从零训练一个小卷积网络也能跑但准确率通常差一截而且收敛慢调参时间成本高。4.1 用预训练模型替换分类头以 ResNet50 为例加载 ImageNet 预训练权重把最后的全连接层换成自己的类别数。import torch.nn as nn from torchvision import models def build_model(num_classes, pretrainedTrue): model models.resnet50(pretrainedpretrained) # 冻结前面的层可选 for param in model.parameters(): param.requires_grad False # 替换最后的全连接层 in_features model.fc.in_features model.fc nn.Sequential( nn.Linear(in_features, 256), nn.ReLU(), nn.Dropout(0.5), nn.Linear(256, num_classes) ) return model model build_model(num_classes10).to(device)参数说明pretrainedTrue会下载 ImageNet 权重第一次运行需要联网冻结前面层是为了在小数据集上防止过拟合等训练几轮后再解冻做微调Dropout(0.5)是常用的正则化手段如果发现训练集准确率远高于验证集可以把这个值调大。4.2 训练循环与学习率调度训练循环本身不复杂但有几个细节决定成败优化器选 Adam 还是 SGD、学习率怎么衰减、什么时候保存模型。import torch.optim as optim from torch.optim.lr_scheduler import CosineAnnealingLR optimizer optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr1e-3) scheduler CosineAnnealingLR(optimizer, T_maxconfig.epochs) best_acc 0.0 for epoch in range(config.epochs): model.train() for imgs, labels in train_loader: imgs, labels imgs.to(device), labels.to(device) optimizer.zero_grad() outputs model(imgs) loss criterion(outputs, labels) loss.backward() optimizer.step() scheduler.step() # 验证 model.eval() correct, total 0, 0 with torch.no_grad(): for imgs, labels in val_loader: imgs, labels imgs.to(device), labels.to(device) outputs model(imgs) preds outputs.argmax(dim1) correct (preds labels).sum().item() total labels.size(0) acc correct / total print(fEpoch {epoch1}, Val Acc: {acc:.4f}) if acc best_acc: best_acc acc torch.save(model.state_dict(), best_model.pth)逻辑说明filter(lambda p: p.requires_grad, ...)只把需要更新的参数传给优化器冻结的层不参与更新CosineAnnealingLR让学习率按余弦曲线下降比固定学习率更容易收敛到好的局部最优保存best_model.pth而不是最后一个 epoch 的模型是因为验证准确率可能在后期波动最好的模型不一定在最后一轮。提示如果训练 loss 一直不降先检查学习率是不是太大比如设成了 0.1再检查数据标签有没有对错。我遇到过标签文件和图片文件名差一个后缀导致全部标签错位的情况loss 卡在 log(num_classes) 附近不动。4.3 训练过程监控与早停课程设计里不需要搞太复杂的监控但至少要能看出模型有没有过拟合。把每个 epoch 的训练 loss 和验证准确率记下来画个曲线就够。import matplotlib.pyplot as plt train_losses, val_accs [], [] # ... 在训练循环里 append ... plt.figure(figsize(10, 4)) plt.subplot(1, 2, 1) plt.plot(train_losses, labelTrain Loss) plt.xlabel(Epoch); plt.ylabel(Loss); plt.legend() plt.subplot(1, 2, 2) plt.plot(val_accs, labelVal Acc) plt.xlabel(Epoch); plt.ylabel(Accuracy); plt.legend() plt.savefig(training_curve.png)如果验证准确率连续多个 epoch 不升反降而训练 loss 还在降说明过拟合了。这时候可以提前停掉或者加大 Dropout、加权重衰减。早停的逻辑很简单记录最佳准确率如果连续 5 个 epoch 没超过最佳值就 break。5. 避坑与排查那些让准确率卡住的常见问题这一章是我自己复现这类项目时踩过的坑按「现象 → 原因 → 解决」整理你对着排查能省不少时间。5.1 验证准确率异常高但推理时全错现象训练日志里验证准确率 95% 以上但用predict.py跑单张图片结果全是同一类。原因推理时的预处理和验证集不一致。常见情况是predict.py里只做了ToTensor没做Normalize或者 resize 的尺寸和训练时不同。解决把predict.py里的 transform 直接复用val_transform不要重新写一套。检查Resize和CenterCrop的参数是否和训练时一致。5.2 训练 loss 不下降卡在 2.3 左右现象loss 从第一个 epoch 就稳定在 2.3 附近准确率接近随机猜。原因标签错位或类别数配置错误。比如num_classes设成了 10 但实际只有 5 类或者ImageFolder读到的类别顺序和标签文件对不上。解决打印dataset.classes看类别列表打印几个 batch 的标签值确认范围。用len(dataset.classes)替换硬编码的num_classes。5.3 GPU 显存不足报 CUDA out of memory现象训练开始几秒后报RuntimeError: CUDA out of memory。原因batch_size太大或者图片尺寸设得过高或者没有及时释放中间变量。解决先把batch_size减半试比如从 32 降到 16如果还不行把img_size从 224 降到 192 或 160在验证循环里用with torch.no_grad()包住减少显存占用。5.4 验证集准确率波动大每次跑结果差很多现象同样的代码和数据两次训练得到的验证准确率差 10 个点以上。原因随机种子没固定或者验证集太小。数据划分时如果没设random_state每次划分结果不同验证集如果只有几十张图准确率本身就不稳定。解决在训练脚本开头固定所有随机种子包括torch.manual_seed、np.random.seed、random.seed验证集至少保证每个类别有 20 张以上图片。import torch, numpy as np, random def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) torch.backends.cudnn.deterministic True set_seed(42)5.5 推理速度慢单张图片要好几秒现象predict.py跑一张图要等好几秒批量推理更慢。原因没有把模型切到 eval 模式或者没有用torch.no_grad()导致每次推理都在建计算图。解决推理前加model.eval()并用with torch.no_grad():包住前向传播。如果还慢检查是不是在用 CPU 推理而模型在 GPU 上训练把模型.to(device)统一一下。6. 进阶技巧用混淆矩阵和单张推理验证模型真实水平训练完拿到一个准确率数字其实说明不了太多问题。我习惯做两件事来验证模型是不是真的学到了东西一是画混淆矩阵看哪些类别容易混二是拿几张训练集里没有的图片做单张推理看输出概率分布。6.1 混淆矩阵定位易混类别from sklearn.metrics import confusion_matrix, classification_report import seaborn as sns model.eval() all_preds, all_labels [], [] with torch.no_grad(): for imgs, labels in val_loader: imgs imgs.to(device) outputs model(imgs) preds outputs.argmax(dim1).cpu().numpy() all_preds.extend(preds) all_labels.extend(labels.numpy()) cm confusion_matrix(all_labels, all_preds) plt.figure(figsize(10, 8)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabelsdataset.classes, yticklabelsdataset.classes) plt.xlabel(Predicted); plt.ylabel(True) plt.savefig(confusion_matrix.png) print(classification_report(all_labels, all_preds, target_namesdataset.classes))逻辑说明混淆矩阵的对角线是正确分类的数量非对角线是错分。如果发现某两类之间互相错分很多说明这两个病害在图像上确实相似可以考虑增加这两类的样本量或者用更强的数据增强。classification_report会输出每个类别的精确率、召回率和 F1比总体准确率更有参考价值。6.2 单张图片推理与概率输出from PIL import Image import torch.nn.functional as F def predict_single(img_path, model, transform, classes, device): model.eval() img Image.open(img_path).convert(RGB) tensor transform(img).unsqueeze(0).to(device) with torch.no_grad(): outputs model(tensor) probs F.softmax(outputs, dim1)[0] top_prob, top_idx probs.max(0) print(f预测类别: {classes[top_idx.item()]}, 置信度: {top_prob.item():.4f}) # 打印前3个概率 top3 probs.topk(3) for p, i in zip(top3.values, top3.indices): print(f {classes[i.item()]}: {p.item():.4f}) predict_single(test_leaf.jpg, model, val_transform, dataset.classes, device)参数说明unsqueeze(0)是给图片加一个 batch 维度因为模型期望输入是[N, C, H, W]F.softmax把输出转成概率方便看置信度。如果置信度普遍偏低比如最高才 0.4说明模型没训好或者这张图确实不属于任何已知类别。6.3 我自己的验证习惯从那以后我每次训完一个分类模型都强制走一遍「混淆矩阵 单张推理」这两步。准确率数字可以骗人但混淆矩阵里哪两类在互相错分、单张推理的置信度分布骗不了人。有一次课程设计答辩评委老师直接拿了一张手机拍的叶片照片让我现场推理因为提前做过单张推理测试预处理流程是通的当场跑出了正确结果。如果只盯着验证集准确率遇到这种现场测试很容易翻车。这份资源的价值在于它把数据组织、训练脚本和推理入口都串好了你不需要从零搭框架把精力放在改类别数、换模型和调参上就行。希望帮到你。本文还有配套的精品资源点击获取