尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

农作物病害识别源码包:从模型训练到云部署的完整技术链路

发布时间:2026/9/24 19:57:28

资讯中心
01
ARTICLE

农作物病害识别源码包:从模型训练到云部署的完整技术链路

农作物病害识别源码包:从模型训练到云部署的完整技术链路
简介一份面向毕业设计与深度学习入门者的完整源码包实现基于云技术与深度学习的农作物病虫害识别系统。项目覆盖图像预处理调整大小、裁剪、数据增强、卷积神经网络模型训练与优化、云端服务搭建及用户端上传识别全流程并涉及TensorFlow、Keras、PyTorch等常见框架适合作为课程设计、毕设或工程实战参考。压缩包共60个文件约88.75MB内含9个ipynb模型训练与对比笔记ResNet50、VGG16/19、DenseNet121等经典网络、Python服务端脚本、前端展示页面与CSS/JS样式、Dockerfile及AWS/GCP部署配置并附有示例图片、Markdown说明文档等目录结构清晰便于按模块查阅。源码中提供了完整的云端部署指南与Flask本地运行示例可快速启动。目前已有126人学习浏览对需要系统了解深度学习图像识别落地流程的开发者很有帮助。1. 云上跑通农作物病害识别这份源码包把训练到部署的链路都备齐了拿到一个农作物病虫害识别项目最烦的不是写网络结构而是把图像预处理、模型训练、Web 服务、云上部署这条线串起来。这份基于 Python 和深度学习的源码包把这四段链路都备齐了notebook 里有 ResNet50、DenseNet121、VGG16/19 多份训练脚本TensorFlow、Keras、PyTorch、FastAI 四套框架都有对应实现服务端是 Flask 应用部署层给了 Dockerfile、AWS 和 GCP 的完整文档。你做毕业设计想跑对比实验或者想快速验证一个病害识别落地方案直接基于它改数据就行不用从头搭。省掉的是踩环境坑、调部署配置的时间这部分通常占项目周期的三分之一不止。2. 技术栈选型与项目结构双框架多模型加 Flask从训练到部署是闭环2.1 目录结构训练脚本、服务代码、部署文档各司其职把压缩包解开后先把目录结构摸清楚再动手能少走很多弯路。核心结构是这样的Plant_Disease_Detection-master ├── app/ # 视图、模型、静态资源 │ ├── view/ # 前端模板上传页/结果页 │ ├── models/ # 训练好的模型文件 │ └── static/ # CSS/JS/用户上传的图片 ├── server.py # Flask 服务入口 ├── notebook/ # 模型训练 Notebook │ ├── Plant_Disease_RESNET50.ipynb │ ├── Plant_Disease_DenseNet121.ipynb │ ├── Plant_Disease_VGG16.ipynb │ ├── Plant_Disease_VGG19.ipynb │ ├── Plant_Disease_Detection_TensorFlow.ipynb │ ├── Plant_Disease_Detection_Keras.ipynb │ ├── Plant_Detect_PyTorch.ipynb │ ├── Plant_Disease_Detection_Fastai.ipynb │ └── plant_disease_detector.ipynb ├── Dockerfile ├── requirements.txt ├── app.yaml # GCP App Engine 配置 ├── deployment_guide/ │ ├── local_flask # 本地启动说明 │ ├── aws_deployment.md # AWS 部署文档 │ └── gcp_deployment.md # GCP 部署文档 ├── images/ # 文档配图 └── README.md这份目录是典型的「三段式」结构notebook 负责模型实验app 和 server.py 负责服务化deployment_guide 与 Dockerfile 负责环境一致性和上云。我拆过的不少毕设项目最大的问题就是训练代码和服务代码混在一起改一个参数要翻整份 notebook。而这份源码把服务入口放在独立的 server.py 里模型单独放 app/models 目录部署文档单独成 md 文件——你要改模型、改接口还是改部署环境入口是清晰的。特别说一下 app.yaml 这个文件。它是 GCP App Engine 的配置文件定义了运行环境、入口命令和资源规格。仓库里同时在 deployment_guide 下给了 aws_deployment.md 和 gcp_deployment.md说明作者本来就是按「一条链路两条部署路径」来设计的本地用 Flask 跑通上云时二选一。2.2 多框架 Notebook 的定位同数据跑多套组合的价值看 notebook 目录会发现作者给的不是一份训练脚本而是多个网络 × 多套框架的组合。我按实际用途整理成下面这张表Notebook骨干网络框架建议用途Plant_Disease_RESNET50ResNet50Keras/TensorFlow默认主线先跑这个Plant_Disease_DenseNet121DenseNet121Keras/TensorFlow数据集偏小时优先Plant_Disease_VGG16VGG16Keras/TensorFlow学习用结构直观Plant_Disease_VGG19VGG19Keras/TensorFlow效果与 VGG16 差别不大Plant_Detect_PyTorch自定义 CNNPyTorch需要动态图调试时Plant_Disease_Detection_FastaiResNet 系列FastAI快速跑基线Plant_Disease_Detection_TensorFlow自定义 CNNTensorFlow想从底层构建时plant_disease_detector未指定通用综合基线版本为什么要同时给这么多两个原因。第一做毕设或课程设计时对比实验是答辩的硬指标在同一个数据集上你比较了 ResNet50 和 DenseNet121说明你做过选型分析第二工程上不同框架的部署链路差别不小TensorFlow 的 SavedModel 在服务端加载最省事PyTorch 用 TorchScript 也能上线FastAI 训练方便但部署时通常要导出成底层框架的格式。我的建议是别好奇地每个都跑一遍那样几天就没了。优先跑 RESNET50拿到准确率基线后根据数据量判断要不要换 DenseNet121。数据量在几千张这个量级DenseNet121 的稠密连接结构收敛更稳不容易过拟合。VGG 系列当学习材料看看就行VGG19 在大部分病害识别场景下比 VGG16 没有明显优势但参数量和推理时间都涨了不少。2.3 Flask 服务与模型解耦分离设计带来的改造成本优势server.py 是整个 Web 服务的入口app/view 放前端 HTML 模板app/static 放样式和上传图片app/models 放训练好的权重文件。用户流程很直接浏览器打开上传页选择一张病害叶片图片POST 请求打到 Flask 路由服务端加载模型做推理把类别和置信度传给结果页渲染。这个架构最值得抄作业的一点是模型文件与 Web 代码物理分离。你在 notebook 里重新训练了一个 DenseNet121导出 h5 文件丢进 app/models服务端代码一行都不用改。反过来你想把识别结果从网页扩展成 JSON 接口给小程序调用也只需在 server.py 里加一个 API 路由不影响现有页面。注意如果后续要换模型不要只替换文件而忽略类别列表。类别数量和顺序是在训练时定的换了模型必须同步更新服务端的类别映射否则推理结果会对不上。3. 本地先把模型跑起来环境配置与训练 Notebook 复现顺序3.1 环境依赖pip 和 Docker 两条路怎么选先说明一下复现顺序先在本地跑通一份训练 notebook再启动 Flask 服务最后才是上云。本地环境这一步最常翻车的是 Python 版本和框架版本互相打架这问题有时候像个玄学明明照着文档装的就是跑不起来。所以我一般会用虚拟环境隔离。# 用 conda 创建独立的 Python 3.9 虚拟环境 conda create -n plant python3.9 -y conda activate plant # 先装基础依赖再装深度学习框架 pip install -r requirements.txt # 如果你有 NVIDIA GPU建议单独装 CUDA 版 TensorFlow pip install tensorflow-gpu2.10.0requirements.txt 这个文件在仓库根目录里面列的是 numpy、pandas、matplotlib、scikit-learn、tensorflow、keras、pillow、flask 这些核心库。第一次跑项目我不推荐直接用 Docker原因很简单notebook 训练阶段你大概率要反复调整代码直接 pip 安装改动最快Docker 放在后面部署阶段再用那时候环境一致性才变成关键诉求。如果你用的不是 conda 而是系统 Python务必先建虚拟环境。我见过不少人图省事直接在系统环境里 pip install结果把系统 Python 的依赖搞乱后面装什么都报错只能重装系统或者花半天排查。这一步没有后悔药提前隔离是最省事的。3.2 图像预处理尺寸、裁剪、归一化参数为什么这么定import numpy as np from PIL import Image def preprocess_image(img): 统一预处理函数训练和 Web 推理都走这里。 支持传文件路径、PIL Image 对象或 Flask 上传的文件对象。 # 统一转成 PIL Image 并保证 RGB 通道 if isinstance(img, str): img Image.open(img) elif hasattr(img, read): img Image.open(img) img img.convert(RGB) # 先缩放到 256x256再中心裁剪到 224x224 img img.resize((256, 256)) crop 224 left (256 - crop) // 2 top (256 - crop) // 2 img img.crop((left, top, left crop, top crop)) # 转数组归一化到 [0,1]再做 ImageNet 标准化 arr np.array(img, dtypenp.float32) / 255.0 mean [0.485, 0.456, 0.406] std [0.229, 0.224, 0.225] arr (arr - mean) / std # 加 batch 维度模型需要 (1, 224, 224, 3) return np.expand_dims(arr, axis0)这段逻辑是几乎所有预训练模型的标准输入流程。先缩放再裁剪直接 resize 到 224 会把叶片拉伸变形先放大到 256 再取中心 224能在保持主体内容的同时引入一点平移不变性。mean和std用 ImageNet 统计值不是随便挑的——预训练权重是在 ImageNet 上学的输入分布和训练时保持一致特征提取能力才能完整继承。这里注意两点一是如果你的模型输入尺寸不是 224这个函数里的crop变量和裁剪起点都要跟着改二是预处理函数最好在训练和推理两端复用同一个模块避免两边各写一份然后参数不一致。后面第 5 章会专门讲这个坑。3.3 模型训练以 ResNet50 为例的迁移学习参数组合import tensorflow as tf from tensorflow.keras.applications import ResNet50 from tensorflow.keras.layers import Dense, Dropout, GlobalAveragePooling2D from tensorflow.keras.models import Model NUM_CLASSES 10 # 按你自己的类别数修改 EPOCHS 30 BATCH_SIZE 32 LR 1e-4 # 微调阶段学习率不宜过大 # 加载 ImageNet 预训练权重去掉顶部分类层 base_model ResNet50(weightsimagenet, include_topFalse, input_shape(224, 224, 3)) # 先冻结基础层只训练新加的分类头 base_model.trainable False x base_model.output x GlobalAveragePooling2D()(x) x Dropout(0.5)(x) x Dense(128, activationrelu)(x) x Dropout(0.3)(x) output Dense(NUM_CLASSES, activationsoftmax)(x) model Model(inputsbase_model.input, outputsoutput) model.compile( optimizertf.keras.optimizers.Adam(learning_rateLR), losscategorical_crossentropy, metrics[accuracy] )include_topFalse的作用是丢弃 ResNet50 原有的 1000 类分类器只保留前面的卷积特征提取部分。GlobalAveragePooling2D把最后一层卷积输出的特征图压成一维向量相比 Flatten 参数更少也不容易过拟合。两个 Dropout 层分别是 0.5 和 0.3用于抑制分类头的过拟合这个值在数据量不大时是安全的起手配置。训练节奏上我一般这样控制先冻结 backbone 训练 10 个 epoch 左右等验证集 loss 不再下降再把base_model.trainable设为 True解冻最后 20 层左右用 1e-5 的学习率微调。这一步是迁移学习里最容易出效果的技巧——直接解冻全部层会导致前期梯度震荡结果往往不如只微调尾部。4. 把识别服务推上云Flask 应用、Docker 镜像与部署实操4.1 server.py 的推理链路从上传图片到返回类别import os from flask import Flask, request, render_template from tensorflow.keras.models import load_model app Flask(__name__, template_folderapp/view, static_folderapp/static) # 模型只加载一次放在模块级别而不是函数里 MODEL_PATH os.path.join(os.path.dirname(__file__), app, models, plant_resnet50.h5) model load_model(MODEL_PATH, compileFalse) # 按训练数据集的类别顺序填顺序错一个结果全乱 CLASS_NAMES [Apple_scab, Black_rot, Cedar_rust, healthy] app.route(/, methods[GET, POST]) def predict(): if request.method POST: file request.files[image] img preprocess_image(file) # 复用 3.2 节的预处理 preds model.predict(img)[0] idx int(preds.argmax()) confidence float(preds[idx]) return render_template(result.html, labelCLASS_NAMES[idx], confidenceconfidence) return render_template(upload.html) if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse)模型加载放在模块顶层整个进程生命周期只加载一次这能省掉每次请求重新 load_model 的几秒开销。compileFalse是因为推理阶段不需要编译优化器能省一点内存。CLASS_NAMES的列表顺序必须和训练时保持一致——模型输出的只是索引索引到类名的映射在服务端这一段顺序错一位识别结果就全乱了。本地验证时直接python server.py浏览器访问 5000 端口就能看到上传页面。仓库里 deployment_guide/local_flask 有启动步骤说明。4.2 Dockerfile 解读构建顺序与层缓存FROM python:3.9-slim WORKDIR /app # 先拷贝依赖声明文件利用 Docker 层缓存 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 再拷贝服务端代码与模型目录 COPY server.py . COPY app/ ./app/ COPY notebook/ ./notebook/ EXPOSE 5000 CMD [python, server.py]Dockerfile 的指令顺序不是随便排的。Docker 构建时每一行指令都会生成一个缓存层如果 requirements.txt 没变RUN pip install这一层就能直接命中缓存构建时间会大幅缩短。如果把COPY app/放在前面每次改动代码都会导致 pip install 重新执行本地调试还行CI 里每次构建就是灾难。CMD用了列表形式而不是字符串形式[python, server.py]这种 exec 形式能保证进程收到正确的信号方便 Docker 在停止容器时优雅退出。如果发现容器 stop 后要等很久才杀掉多半就是 CMD 写成了 shell 字符串形式。4.3 AWS 与 GCP 部署对比两条路径的配置要点维度AWS EC2GCP App Engine服务类型IaaS 虚拟机PaaS 托管平台环境控制完全自主受限但免运维GPU 支持有 GPU 实例可选原生不支持需搭配其他服务适合场景要跑训练又要推理只做在线推理配置入口安全组 / 入口规则app.yamlAWS 这条路是标准的 Docker 远程部署流程先把镜像推到 ECR再到 EC2 上拉取运行# 本地构建并推送镜像到 AWS ECR docker build -t plant-disease . docker tag plant-disease:latest $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/plant-disease:latest docker push $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/plant-disease:latest # SSH 登录 EC2 实例拉取镜像并启动容器 ssh ec2-user$EC2_IP docker pull $ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/plant-disease:latest docker run -d --name plant -p 80:5000 plant-diseaseGCP App Engine 走的则是声明式配置仓库里已经有 app.yaml里面指定了运行时、入口命令和服务配置。执行gcloud app deploy即可完成部署不需要手动管理服务器。选哪条路取决于你的使用场景如果只是把训练好的模型变成一个在线接口App Engine 省运维但要注意它的实例规格上限如果要挂 GPU 推理服务必须走 AWS 这类提供 GPU 实例的平台。提示云服务器上拉取镜像后第一次访问会有模型加载的延迟建议部署完成后先手动触发一次推理让模型常驻内存再对外暴露入口。5. 避坑指南我在复现这套源码时踩过的五个坑5.1 加载 h5 模型报错Keras 与 TensorFlow 版本对不上现象用 tensorflow.keras.models.load_model 加载训练好的 h5 文件时抛出一堆类似 Unknown metric function 或者 Unknown layer 的反序列化错误模型权重明明没损坏。原因训练时用的 Keras API 和加载时的版本不一致。典型场景是 notebook 里用 Keras 2.x 训练保存的 h5部署时环境里装的 TensorFlow 2.16 自带 Keras 3.x函数路径和序列化格式都变了旧权重找不到对应的符号。解决加载时显式声明自定义对象load_model(path, custom_objects{...})或者更稳的做法——训练结束后用model.save_weights只保存权重部署端用代码重建同样的网络结构再load_weights。前者一步到位后者结构可控能避开大部分序列化兼容问题。5.2 上传图片后 404静态资源路径配置不一致现象Flask 服务启动正常上传页能打开但页面样式全丢点上传后路由返回 404或者图片预览加载不出来。原因模板里引用的静态资源路径和 Flask 配置不一致。这个项目里 server.py 在根目录而静态资源在 app/static如果不手动指定static_folderFlask 默认会去根目录找 static自然是 404。解决创建 Flask 实例时显式指定static_folderapp/static和template_folderapp/view。模板里统一用{{ url_for(static, filenamestyle.css) }}生成路径不要手写绝对路径。5.3 云端推理超时开发服务器扛不住并发现象本地跑得好好的部署到云上后第一次请求要十几秒之后并发一上来就开始超时页面转半天没结果。原因CPU 实例推理一张 224x224 图片大概需要 1 到 2 秒加上模型加载时间默认的 Flask 开发服务器是单线程的请求全部排队超时是必然的。解决换生产级 WSGI 服务器用 gunicorn 起多个 worker。命令大致是gunicorn -w 4 -b 0.0.0.0:5000 server:app4 个 worker 并发处理吞吐量提升明显。模型加载到共享内存各 worker 复用。5.4 识别准确率骤降训练和推理预处理不一致现象同一张测试图在 notebook 里预测是对的部署成 Web 服务后预测就错了第一反应是模型坏了仔细排查发现模型没坏。原因训练时做的是 resize 256 中心裁剪 ImageNet 标准化推理端可能只做了 resize 到 224或者忘了先归一化到 [0,1] 再标准化。输入分布变了模型的输出自然不稳定。解决把 3.2 节的preprocess_image函数抽成一个公共模块训练和 Web 推理共用同一份代码不要各写一遍。这个坑排查起来最费时间因为它不报错只是结果不对。5.5 结果偏向常见类别数据集分布不均衡现象训练准确率看着还行但实际用的时候发现模型几乎不判某些类别健康叶片也频繁被识别成常见病害。原因数据集类别分布不均某类图片数量是另一些类的数倍。模型学到的是「多数类先验」在不确定时倾向于输出样本量大的类别。解决训练前先统计每个类别的图片数在model.fit里传class_weight参数给少数类加大权重或者对少数类做数据增强的过采样。我先打印数据分布再决定怎么调不做这步直接训结果不可信。6. 进阶用自定义数据集微调并跑通端到端验证闭环前面都是基于仓库自带数据流演示真正要把这个项目用在你的场景里一定得换自己的数据。我建议做一次「数据替换 → 微调 → 服务验证」的闭环整个过程控制在半天内。先按类别分文件夹组织数据用 ImageDataGenerator 加载并做数据增强from tensorflow.keras.preprocessing.image import ImageDataGenerator train_gen ImageDataGenerator( rescale1./255, # 简化版归一化和推理端保持一致 rotation_range20, # 随机旋转 ±20 度 width_shift_range0.2, # 水平平移 20% height_shift_range0.2, # 垂直平移 20% zoom_range0.2, # 随机缩放 horizontal_flipTrue, # 水平翻转 validation_split0.2 # 留出 20% 做验证集 ) train_data train_gen.flow_from_directory( data/train, target_size(224, 224), batch_size32, subsettraining, class_modecategorical )flow_from_directory会自动把每个子目录名映射成类别索引class_modecategorical输出 one-hot 标签配合前面 3.3 节的 ResNet50 模型直接 fit。注意这里为了演示用了简化的rescale方案和 3.2 节的手动标准化是两套方案选一套后训练与推理必须一致。训练完成后用save_weights导出权重再在 server.py 里把CLASS_NAMES改成你的子目录列表改动集中在两处。微调结束后做一次端到端验证从训练集里抽出几十张模型没见过的图走一遍「上传 → 预处理 → 预测 → 结果页」的完整流程确认类别名称和置信度都对得上。这一步能同时暴露预处理不一致和类别映射错位这两个最隐蔽的问题。我从那以后每次换数据集都强制先跑一轮这个闭环再谈部署时间花得很值。这份源码的坑我已经替你踩过一遍了照着上面的步骤来能省不少时间希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。