简介面向深度学习初学者的VGG植物生长阶段分类项目基于PyTorch实现解决图像分类任务中数据准备、模型训练与界面展示的完整流程。资源包共9个文件包含3个Python脚本数据集txt生成、CNN训练、PyQt界面、依赖清单txt、说明文档docx以及4张类别示例图压缩包仅227KB轻量易用。目前已有128人学习代码采用逐行中文注释配合说明文档和requirement.txt可帮助零基础读者快速搭建PyTorch环境并理解模型逻辑。项目不含数据集图片需自行按类别文件夹收集图片每个文件夹内置提示图指导放置位置分类目录可自由扩展适合课程设计、毕业设计或深度学习中图像分类方向的入门实战。1. 从 PyTorch 到 VGG植物生长阶段分类到底怎么落地植物生长阶段分类这件事很多人第一反应是上最新的 Transformer但真正落地时你会发现VGG 模型在这个任务上远比想象中耐用。这套基于 pytorch 的深度学习资源就是一条完整的分类链路——三个 py 文件分别负责生成数据集 txt、训练 CNN、跑 PyQt5 界面代码逐行中文注释非常适合第一次完整跑通「训练到推理」的从业者。它解决的是最典型的一类图像分类问题把种子、幼苗、开花、结果四个阶段区分开类别可以自己加数据集图片自己收集环境装好就能复现。适合那种已经装好 python 和 pytorch、但还没独立组织过完整训练流程的人。2. 代码结构与模型选型三个 py 文件的分工和 VGG 的取舍整套代码的逻辑非常直白先准备好带标签的图片再让模型记住每个类别的视觉特征最后拿训练好的权重去做推理。围绕这条主线三个 py 文件的职责边界划得很清楚。2.1 文件清单与运行顺序拿到压缩包解压后目录里大致是这几个核心文件文件作用运行时机01生成txt.py扫描数据集文件夹生成 train.txt 和 val.txt第一步02CNN训练数据集.py读取 txt训练 VGG 模型并保存权重第二步03pyqt界面.py加载权重提供图形界面做单张图片识别第三步requirement.txt记录依赖包及版本装环境时用说明文档.docx环境安装和运行流程说明随时查阅这个顺序就是项目的完整主线。很多人第一次拿到手会直接去跑训练脚本结果报找不到文件原因就是没有先生成 txt。txt 在这里扮演的是数据索引角色——告诉模型哪些图片属于哪个文件夹而不是让模型自己去翻目录这能避开很多 OSError 路径问题。2.2 VGG 网络为什么这种笨结构在分类任务里依然稳VGG 的网络结构用一个词概括就是规整全部使用 3x3 卷积核通过堆叠卷积层和池化层提取特征最后接三层全连接做分类。虽然它的参数量比 ResNet 大计算量也偏高但在数据量不大的植物分类场景里有三个实打实的优点。第一结构透明。每一层做了什么非常直观配合逐行注释新手能看清楚特征是怎么从像素变成类别概率的。这不是黑匣子是活教材。第二迁移学习效果好。torchvision 里有官方预训练权重直接加载 ImageNet 上的特征比自己从零训收敛快得多。第三在特征差异明显的任务上VGG 的冗余参数反而变成了优势因为植物不同生长期的叶片形态、颜色、纹理差异足够大不需要特别精巧的结构也能学明白。如果项目里用的是 vgg16模型定义部分通常是这样的import torch.nn as nn from torchvision import models def build_vgg(num_classes: int 4): # weights 参数直接加载 ImageNet 预训练权重比旧版 pretrainedTrue 写法更清晰 model models.vgg16(weightsmodels.VGG16_Weights.IMAGENET1K_V1) # vgg16 的分类头是 classifier[6]是一个 Linear 层 # 把它的输入维度取出来再替换成自己的类别数 in_features model.classifier[6].in_features model.classifier[6] nn.Linear(in_features, num_classes) return model这里最关键的操作是替换最后一层。VGG 原生分类头是 1000 类对应 ImageNet 的类别数我们的任务只有 4 类所以必须把 out_features 改成 4。很多新手容易漏掉这一步直接拿原模型训练loss 能算出来但输出维度对不上。in_features 从 classifier[6] 取是通用写法不管前面的全连接层怎么变最后一层的输入维度总是固定的 4096。2.3 版本匹配逻辑python 3.7/3.8 与 pytorch 1.7.1/1.8.1这个项目的环境搭配是 python 3.7 或 3.8pytorch 1.7.1 或 1.8.1。这套组合看起来版本偏老但恰恰是兼容性最稳的区间。pytorch 1.8.1 对 CUDA 11.1 的支持很成熟torchvision 版本也对得上不会出现 import 报错这种玄学问题。用 Anaconda 创建环境的时候常见做法是conda create -n plant python3.8 conda activate plant pip install torch1.8.1 torchvision0.9.1 pip install -r requirement.txt注意 torch 和 torchvision 是绑定版本下载的不能随便装。torchvision 0.9.1 对应 torch 1.8.1如果 torch 装成 1.9 而 torchvision 还是 0.9.1模型加载预训练权重的时候会出现版本不匹配的警告甚至报错。另外如果电脑没有 NVIDIA 显卡装 CPU 版本就行——pip install torch1.8.1cpu训练慢一点但能跑通全流程。提示装环境之前先确认自己的 CUDA 版本。pytorch 1.8.1 对 CUDA 10.2 和 11.1 都支持驱动太新或太旧都可能踩坑。3. 数据集与 01生成txt.py把文件夹变成训练清单很多人在这一步会卡住因为项目说明里写得很明白——不含数据集图片。这其实不是坏事自己收集的数据集反而更贴合真实场景。关键是先把文件夹结构搭对。3.1 文件夹即类别种子、幼苗、开花、结果怎么组织数据集的目录结构非常朴素图片放在数据集根目录下每个子文件夹就是一类文件夹名就是类别名data/ ├── 种子/ │ ├── 1.jpg │ └── ... ├── 幼苗/ ├── 开花/ └── 结果/这个设计的好处是类别可扩展。你想加一个枯萎阶段直接在 dataset 目录下新建一个文件夹把图片放进去代码会自动识别新类别不需要改任何配置文件。每个文件夹里放一张提示图告诉你把图片放这这是给后续补充数据的人看的训练时会自动忽略。3.2 01生成txt.py生成 train.txt 和 val.txt 的核心逻辑这个脚本要做的事情就是把上述目录结构转成模型能读的标签格式。每行记录图片路径和类别 IDID 按文件夹遍历顺序自动分配。核心逻辑我重新组织过一遍大致长这样import os import random dataset_dir data # 数据集根目录保持和训练脚本一致 train_ratio 0.8 # 80% 图片作为训练集20% 作为验证集 with open(train.txt, w) as f_train, open(val.txt, w) as f_val: classes os.listdir(dataset_dir) # 列出所有子文件夹即类别 for cls_id, cls_name in enumerate(classes): img_dir os.path.join(dataset_dir, cls_name) if not os.path.isdir(img_dir): continue # 跳过提示图之类的非文件夹文件 images os.listdir(img_dir) random.shuffle(images) # 打乱顺序避免同批次拍摄的图全部切到训练集或验证集 for i, img in enumerate(images): line f{cls_name}/{img} {cls_id}\n if i len(images) * train_ratio: f_train.write(line) else: f_val.write(line)这段代码有三处值得细看。第一enumerate(classes)自动生成类别 ID0、1、2、3 就对应着文件夹遍历顺序所以训练和推理时的类别顺序必须一致一般靠固定的文件夹名保证。第二路径写的是相对路径cls_name/img这意味着训练脚本的工作目录必须在数据集根目录的上级或者在代码里 join 一下绝对路径。我在实际跑的时候习惯把数据集文件夹和 py 脚本放在同一层省得路径出错。第三train/val 按 8:2 比例划分如果某一类的图片特别少可以把train_ratio提到 0.9或者干脆手动挑选验证集。3.3 自建数据集搜集图片、一致性检查与类别扩展既然数据集要自己找那么找图的质量直接决定模型上限。我一般遵循三条标准分辨率不低于 300x300因为 VGG 会把输入缩放到 224x224太糊的图特征直接丢了类别间要有区分度找的图别都是差不多的角度否则训练集准确率虚高一到真实环境就翻车每类至少 100 张。这里有个经验值——深度学习分类任务单类别低于 60 张时VGG 这种大模型很容易过拟合训练集 99% 准确率验证集只有 70%。类别扩展时要注意一个隐藏坑txt 文件重新生成后类别的 ID 顺序变了。比如原来种子是 ID 0你新增一个文件夹后遍历顺序变了之前训练的模型权重就不能直接用了需要重新训练。所以我的习惯是类别一旦定下来就先建好空文件夹再开始收集图片训练过程中不乱动目录。4. 02CNN训练数据集.py从预处理到权重保存的完整链路这一步跑起来之后你才能真正看到深度学习模型是怎么一步步学会分类的。训练脚本可以说是整个项目的心脏。4.1 数据加载与预处理参数训练前要把图片统一成模型能接受的尺寸和数值范围。VGG 的输入惯例是 224x224 的 RGB 三通道图这个尺寸不是拍脑袋定的而是 VGG 原文和预训练权重约定俗成的标准。预处理通常这样写from torchvision import transforms from torch.utils.data import Dataset, DataLoader from PIL import Image train_transform transforms.Compose([ transforms.Resize((224, 224)), # 强制缩放简单粗暴但有效 transforms.RandomHorizontalFlip(), # 随机水平翻转相当于免费扩充数据 transforms.ColorJitter(brightness0.2, contrast0.2), # 微调亮度对比度 transforms.ToTensor(), # 转 Tensor并把像素归一化到 [0,1] transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) # 用 ImageNet 的均值和方差 ])ColorJitter里的两个 0.2 是幅度参数表示亮度和对比度在正负 20% 范围内扰动。这个值不宜太大0.5 以上的话图片会失真反而影响训练。Normalize这一步经常有人漏掉但它极其重要——ImageNet 预训练权重是在这套均值和方差下学出来的你输入的数据分布必须和它对齐否则权重相当于作废了一半。验证集一般不做翻转和色彩抖动只用 Resize、ToTensor 和 Normalize保持评估时输入稳定。数据加载部分不需要重写 Dataset 类因为 txt 文件里已经写好了路径和标签自带的套路就能处理class PlantDataset(Dataset): def __init__(self, txt_path, transformNone): self.lines open(txt_path).readlines() self.transform transform def __len__(self): return len(self.lines) def __getitem__(self, idx): img_path, label self.lines[idx].strip().split() image Image.open(img_path).convert(RGB) # 统一转 RGB if self.transform: image self.transform(image) return image, int(label) train_dataset PlantDataset(train.txt, train_transform) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers2)这里必须写.convert(RGB)否则遇到灰度图或被破坏的图会直接报错。num_workers2表示用两个子进程预加载数据能缩短每个 epoch 的等待时间但 Windows 系统建议设 0否则多进程容易报 RuntimeError。4.2 训练参数学习率、batch_size、冻结层的搭配图片数据量不大时从头训整个 VGG 既慢又容易过拟合。更聪明的做法是冻结特征提取层只训练分类头。这样显存占用小训练速度快而且效果往往更好import torch import torch.nn as nn from torchvision import models model models.vgg16(weightsmodels.VGG16_Weights.IMAGENET1K_V1) in_features model.classifier[6].in_features model.classifier[6] nn.Linear(in_features, 4) # 换成自己的类别数 # 冻结 features 部分只训练 classifier for param in model.features.parameters(): param.requires_grad False criterion nn.CrossEntropyLoss() optimizer torch.optim.SGD(model.classifier.parameters(), lr0.001, momentum0.9) scheduler torch.optim.lr_scheduler.StepLR(optimizer, step_size5, gamma0.1)学习率 0.001 是微调场景下的标准起点。如果 loss 震荡不收敛降到 0.0003如果收敛太慢可以试 0.003。StepLR的意思是每 5 个 epoch 学习率乘以 0.1这种做法可以让模型在后期用小步长精细调整。冻结 features 层的判断依据是数据量——当你的数据集只有几百张图时冻结特征层是安全的如果你攒了上千张各个角度的图可以解冻最后两三个卷积层做全模型微调效果会更好。训练循环本身不复杂但有几个细节会影响成败for epoch in range(15): model.train() # 切到训练模式BN 层会更新统计量 for images, labels in train_loader: outputs model(images) loss criterion(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() model.eval() # 切到评估模式BN 层使用训练好的统计量 with torch.no_grad(): # 在验证集上计算准确率 correct, total 0, 0 for images, labels in val_loader: preds model(images).argmax(dim1) correct (preds labels).sum().item() total labels.size(0) print(fepoch {epoch}, acc: {correct / total:.4f})model.train()和model.eval()这对切换是关键很多人忘了 eval 导致 BN 层表现异常验证准确率忽高忽低。torch.no_grad()关闭梯度计算推理时省显存也加速。argmax(dim1)把网络输出的 logits 转成类别索引dim1 表示沿着类别维度取最大值的下标。4.3 训练观察点怎样才算真的训练起来了训练不是一把梭跑完就完事必须盯着几个信号。第一个信号是 loss 曲线的走势前几个 epoch 应该明显下降如果 loss 纹丝不动八成是学习率太低或者预处理出了问题。第二个信号是训练集和验证集准确率的差值如果训练集 98%、验证集只有 75%就是过拟合了解决办法是加数据增强或者提前停止训练。第三个信号是 loss 出现 NaN通常意味着学习率过高导致梯度爆炸把学习率除以 10 再试。保存权重时有个细节光存model.state_dict()还不够最好把类别顺序也存一份。因为前面说过txt 重新生成后类别 ID 可能变推理时不知道 ID 对应哪个类别加载模型就只能瞎猜。我的做法是把类别名写进一个 json 文件和权重放一起推理时读出来这样换数据集目录也不会乱。提示训练过程中如果中途中断重新跑之前最好把旧权重文件改名备份。有些训练代码会在 load 时找不到上一次的 state_dict 直接报错但更常见的情况是覆盖写入一半导致文件损坏。5. 常见问题排查环境、数据与训练的五个真坑项目能跑通是一回事能稳定复现是另一回事。下面这五个问题是我实际踩过、也在答疑时反复见到的按现象 → 原因 → 解决写清楚。5.1 环境装好但 import torch 报错现象import torch提示 DLL load failed 或者找不到指定模块pip list 里明明能看到 torch。原因最常见的是 torch、torchvision 版本与 python 或 CUDA 版本不匹配。python 3.9 以上装 torch 1.7.1 可能会拉到一个兼容性差版本的安装包CUDA 驱动太新旧版 torch 的运行时环境不认。解决严格按项目推荐来python 3.8 torch 1.8.1 是经过验证的组合。如果之前用 pip 装过别的版本先pip uninstall torch torchvision再重新安装避免残留文件干扰。5.2 训练时 loss 一直不降现象模型 fit 迭代了十几个 epochloss 一直在 1.3 左右徘徊验证准确率始终在 25%等于随机猜 4 类。原因最常见的是学习率过大导致 loss 在局部震荡或者数据集本身没有归一化输入像素值范围不对。另一个隐蔽原因是标签错位——txt 里的类别 ID 和实际文件夹的遍历顺序不一致模型学到的映射是乱的。解决先把学习率降到 0.0001 试跑 3 个 epoch观察 loss 是否下降。同时人工抽查 train.txt 的前 20 行确认路径能打开、标签和文件夹对应。如果还不行检查预处理里有没有 Normalize。5.3 加载模型权重时报 state_dict 关键字不匹配现象load_state_dict抛错提示 size mismatch 或者 missing keys / unexpected keys。原因训练时分类头是 4 类加载权重的模型分类头还是 1000 类。这个错出现在你换了一台机器或重新定义了模型但没改分类头。解决加载前用和你训练时完全相同的代码重新构建模型——先建 vgg16再把分类头改成 4 类最后再load_state_dict(torch.load(best.pt))。另外加载时加strictFalse能跳过不匹配的层但这样会留下随机初始化的分类头需要重新微调。5.4 PyQt5 界面点识别按钮直接闪退现象界面能正常打开选好图片点识别程序没有任何报错就退出。原因图片路径是中文或者原图被转成 PIL 后是 RGBA 四通道Image.open(img_path).convert(RGB)这行如果漏了 convertVGG 的预训练权重会因通道数不匹配直接崩。另外没有torch.no_grad()包裹推理代码显存溢出也可能被 Qt 框架吞掉异常导致闪退。解决推理代码里强制.convert(RGB)图片加载后打印一下 shape 确认是 3 通道。关键推理逻辑整个放进 try-except 里捕获到异常时弹一个 QMessageBox 而不是直接退出。如果是显存问题把 batch_size 改为 1 再试。5.5 验证集准确率 90%实际用的时候经常判错现象跑验证集准确率相当漂亮但拿手机在真实环境拍一张同样长出果实的照片模型大概率把它判成开花。原因验证集里的图和训练集来自同一批搜集的图片光线、角度、背景高度相似模型学到的是背景特征而不是植物本身的特征。这就是数据集分布和真实场景的偏差深度学习里最常见也最难防的坑。解决收集数据集时每类至少留 30 张完全独立拍摄的图做测试集训练完单独跑一次。增加 ColorJitter 的幅度到 0.3模拟不同光线再叠加 RandomResizedCrop让模型学会关注主体而不是固定位置。如果还不行需要去收集更多不同场景的数据前提是类别标签仍然一致。6. 界面推理与验证技巧用混淆矩阵代替单张抽检权重训练好之后03pyqt界面.py 负责把模型装进一个图形界面里。它的工作流程很直接加载保存的模型权重把用户选择的图片走一遍预处理forward 得到 logitsargmax输出类别名。界面逻辑本身不复杂我在这里不多展开真正想说的是验证模型的方法。很多人验证模型是打开界面随便挑几张图点一下看结果对就完事。这种做法太粗糙了随机挑的图往往集中在容易分类的样本上。我后来养成一个习惯训练完一定在验证集上完整跑一遍输出混淆矩阵import torch confusion torch.zeros(4, 4) # 4 是类别数这里假设已经改成和 num_classes 一致 model.eval() with torch.no_grad(): for images, labels in val_loader: preds model(images).argmax(dim1) for t, p in zip(labels, preds): confusion[t, p] 1 print(confusion) # 每行代表真实类别每列代表预测类别 # 对角线是正确预测的数量非对角线就是混淆情况混淆矩阵告诉你两件事哪些类别之间在互相打架以及错判是单向还是双向。比如结果类的 50 张图里有 15 张被判成开花说明这两个阶段的视觉特征确实相近你需要为结果类补充更有代表性的图片或者把分类粒度调粗。如果某个类别的正确率已经接近 100%在类别特别难分的情况下反而要考虑是不是数据有重复。从那以后我每训完一个模型都强迫自己走一遍这个流程只在界面里抽查结果因为界面只能证明模型能跑混淆矩阵才能证明模型能信。整套资源的核心价值也在于此——它把数据准备、训练、界面推理串成了一根完整的链你拿到手先复现再把混淆矩阵这个习惯加进去就真正把这个项目掌握在手里了希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
搞定微信小程序界面布局的3个底层逻辑,面试必问不再慌 搞定微信小程序界面布局的3个底层逻辑,面试必问不再慌 面试官盯着你的眼睛问:“为什么这个列表滑动卡顿?你的布局是怎么写的?”你支支吾吾答不上来,心里直打鼓。这种尴尬场景,相信不少做前端的同学都经历过。微信小程序界面布局看似简单,实则坑多,更… · 2026/9/23 18:36:29
ZFS实战指南:从数据完整性到快照备份的可靠存储方案 聊文件系统,大多数人第一反应是ext4、xfs,好像日常用着也没啥大问题。但如果你管过几百TB的数据,或者被“静默数据损坏”坑过一次——文件还在,目录还在,打开却发现某些字节已经烂掉了,而系统日志里什么都没… · 2026/9/23 18:36:29
自动控制原理教案:17次课教学框架与重难点实操指南 简介:这份《自动控制原理教(学)案》面向自动化、电气工程等专业的本科生与授课教师,用于课程备课、课堂讲授与复习备考。文档系统梳理了教学大纲、教案、讲稿三者的区别与联系,并给出教案编写应具备的教学目的、教学内… · 2026/9/23 18:36:29
RobotGo 开发者协作指南:构建测试、架构与工程实践全解析 RobotGo 开发者协作指南:构建测试、架构与工程实践全解析 【免费下载链接】robotgo RobotGo, Go Native cross-platform RPA, GUI automation, Auto test and Computer use vcaesar 项目地址: https://gitcode.com/gh_mirrors/ro/robotgo
导读
RobotGo 是一… · 2026/9/23 19:09:04
wired-elements 之 wired-search-input:手绘风格搜索输入框组件的使用与源码解析 UI组件前端 【免费下载链接】wired-elements Collection of custom elements that appear hand drawn. Great for wireframes or a fun look. 项目地址: https://gitcode.com/gh_mirrors/wi/wired-elements 点击查看 免费下载 wired-search-input 是 wired-element… · 2026/9/23 19:09:04
SpringBoot+Vue+MySQL在线考试与学习交流平台完整源码方案 带过不少课程设计项目,也帮人排查过无数次"明明代码没问题但就是跑不起来"的现场。语言在线考试与学习交流网页平台这类题目,在Java课程设计里出现频率极高,但真正能让人眼前一亮、逻辑完整、还能当场演示的系统并不多。这篇就专门… · 2026/9/23 19:09:04
PHPStan 错误标识符 `new.deprecatedEnum` 全解析:废弃枚举实例化检测与修复 开发工具代码质量静态分析 【免费下载链接】phpstan PHP Static Analysis Tool - discover bugs in your code without running it! 项目地址: https://gitcode.com/gh_mirrors/ph/phpstan 点击查看 免费下载 导读
new.deprecatedEnum 是 PHPStan 静态分析框架中由… · 2026/9/23 19:09:04
CephFS 架构解析与 Kubernetes 集成指南 教程云原生容器编排 【免费下载链接】kubernetes-handbook Kubernetes 架构与生态:从云原生到 AI 原生基础设施的构建指南 项目地址: https://gitcode.com/gh_mirrors/ku/kubernetes-handbook 点击查看 免费下载 CephFS 是构建在 Ceph 集群之上、兼容 P… · 2026/9/23 19:08:58
5年开发避坑指南:2013061核心考点与最佳实践全解析 5年开发避坑指南:2013061核心考点与最佳实践全解析 面试时被问底层原理答不上来,这种尴尬谁懂? 别慌,这正是大多数转岗从业者卡壳的痛点。 今天拆解2013061的高频考点,帮你把最佳实践装进脑子。 考点梳理:高频问题与岗位差异… · 2026/9/23 19:08:58
3招搞定手机怎么下载微信面试难题实战项目解析 3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29