简介这是一套完整的基于深度学习的水果识别系统Python项目面向毕业设计、课程设计及期末大作业等场景提供从模型训练到Web界面展示的完整链路。资源包含源代码、文档说明、数据集与训练好的模型前端采用Bootstrap、Layui等框架构建后端为Python逻辑代码注释详尽新手也能快速上手。压缩包共277个文件涵盖js/css/html前端资源、py核心代码、jpg/png/gif图片样本、h5模型文件以及md/txt说明文档整体仅17.53MB部署轻量便捷。目前已有344人学习浏览。项目界面美观、功能齐全支持图片上传与识别结果展示下载后简单配置即可运行也可根据数据集自行训练或优化模型具有较高的参考与实用价值。1. 深度学习水果识别系统毕设选题里少有的「代码能跑、文档能交、数据能训」的完整包做毕设选「水果识别」这个方向的人不少但真正能把完整链路跑通的人不多。很多同学下载的所谓项目要么只有训练脚本没有界面要么数据集残缺导致训练到一半报错要么模型文件失效根本加载不出来。这个基于深度学习的水果识别系统属于少见的完整形态Python 写的后端识别逻辑配了 Layui 风格的管理界面训练好的模型文件直接可用还带了一份能看懂的数据集。对做毕设、课设、期末大作业的人来说它的价值在于你不需要从零搭环境、凑数据、调网络下载下来按文档走一遍就能看到一个能交互、能识别、能出结果的东西。这套资源适合两类人一类是时间紧、想把主要精力放在论文和答辩上的应届生另一类是刚入门深度学习、想看看完整项目长什么样的新手。前者可以直接部署跑通后者可以从代码里学数据加载、模型调用、结果展示这条完整链路。下面我从文件结构、部署流程、训练调参、常见坑这几个维度逐一拆开讲。2. 系统架构与文件构成Layui 前端 Python 后端 数据集/模型的三层结构2.1 从静态资源看前后端分工资源包里的 CSS 文件名透露了不少信息。bootstrap.css、layui.css、swiper-bundle.min.css说明界面层用了 Bootstrap 和 Layui 两套框架——Layui 主导后台管理界面Bootstrap 做响应式布局layer.js负责弹窗交互swiper大概率用于图片展示轮播。这些前端资源是直接打包好的不需要联网加载 CDN本地就能跑。这种结构的实际意义是前端和后端完全解耦。前端负责上传图片、展示识别结果、管理历史记录后端 Python 负责图像预处理、模型推理、返回分类标签和置信度。对毕设答辩来说这个架构有一个天然的优势——可以分开演示展示界面操作流畅性再展示模型识别的准确性两者互不干扰。project_root/ ├── static/ # CSS、JS、图片等静态资源 │ ├── layui.css │ ├── bootstrap.css │ └── swiper-bundle.min.css ├── models/ # 训练好的模型权重 ├── datasets/ # 水果分类数据集 ├── app.py # Flask 入口路由与识别接口 ├── train.py # 训练脚本 ├── requirements.txt # 依赖清单 └── README.md # 部署说明提示以上目录命名是最常见的组织方式。如果下载后结构略有差异比如入口文件叫main.py或run.py以 README 里的启动命令为准。2.2 数据集组织与模型文件格式水果识别本质上是个图像分类任务。这个场景下数据集的标准组织方式是根目录下按类别分子文件夹每个文件夹里放该类别的图片文件名不强制统一。训练时用torchvision.datasets.ImageFolder或者keras.preprocessing.image.ImageDatasetFromDirectory读目录结构自动生成类别标签。模型文件分两种形态一种是完整权重.pth或.h5一种带网络结构如model.onnx。毕设场景下pth格式最常用因为它配合 PyTorch 的torch.load()加载里面保存的是网络各层的权重参数。你拿到的资源里如果只有权重需要配合代码里的模型类定义来恢复网络结构。2.3 训练脚本与预测脚本的分工边界# predict.py - 核心推理逻辑 import torch import torchvision.transforms as transforms from PIL import Image def predict(img_path, model, class_names): 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]) ]) img Image.open(img_path).convert(RGB) img_tensor transform(img).unsqueeze(0) with torch.no_grad(): outputs model(img_tensor) _, predicted torch.max(outputs, 1) return class_names[predicted.item()]这段代码的关键在三处第一所有输入图片统一缩放到 224×224这是 ResNet、MobileNet 这类预训练模型的默认输入尺寸第二归一化用的 mean 和 std 是 ImageNet 的统计值因为迁移学习加载的是在 ImageNet 上预训练的权重输入分布必须对齐第三torch.no_grad()在推理时关闭梯度计算省显存、提速。这三个点中任何一个写错识别结果都会偏离预期且问题不容易直观发现。3. 部署复现完整流程从环境配置到界面跑通的每一步3.1 Python 环境与依赖版本搭配第一步是确认 Python 版本。深度学习项目对版本敏感Python 3.6 到 3.8 是 PyTorch 兼容性最稳的区间3.9 以上部分老版本 PyTorch 直接装不上。先执行下面的命令检查编译器和版本情况python --version pip --version python -c import platform; print(platform.architecture())注意Windows 上 Python 3.8 PyTorch 1.8~2.x CUDA 10.2 或 CPU 版均可。没有 NVIDIA 显卡的机器安装 CPU 版本即可水果识别这种小数据量任务CPU 推理单张图片在 1-2 秒内完全可以接受。依赖安装时建议用虚拟环境隔离避免把系统 Python 搞乱python -m venv fruit_env # Windows 激活 fruit_env\Scripts\activate # Linux/Mac 激活 source fruit_env/bin/activate pip install -r requirements.txt如果网速受限加国内镜像源pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/。3.2 启动 Flask 服务并验证接口确认依赖无误后启动入口文件python app.py看到类似* Running on http://127.0.0.1:5000的输出就是成功了。浏览器打开这个地址能看到上传图片的交互界面。先不用急着选各类水果图拿一张数据集中任意的图片试试确认整个链路是通的。初次跑通后再看控制台日志Flask 会打印每个请求的 URL 和状态码。3.3 数据集文件的放置与路径解析数据集虽然打包在资源里但路径不对是新手最常见的翻车点。常见做法是数据集目录与训练脚本在同级目录训练时脚本用相对路径读取# train.py 中的数据加载片段 train_dir datasets/train val_dir datasets/val train_dataset datasets.ImageFolder( train_dir, transformtrain_transform )ImageFolder的标签读取规则是「按子目录名排序后生成索引」所以类目文件夹的命名要简洁规范不要用中文名或带空格的名字。比如香蕉就叫banana不要叫香蕉 2024否则排序会乱类别映射也会跟着错。4. 训练自己的模型数据划分、迁移学习选型与关键参数调整4.1 数据集划分三七开与分层采样下载资源里的数据已经帮你做了初步划分但如果你想补充自己的图片重新训练划分比例和策略要心里有数。通用的做法是训练集 70%、验证集 15%、测试集 15%。水果图片数量不大时这个比例最稳妥。划分时要注意分层采样——保证每个类别的图片在训练集、验证集、测试集中的比例一致。比如苹果 100 张、香蕉 80 张按 70/15/15 切苹果训练集 70 张香蕉训练集 56 张。如果随机切分不管类别有可能苹果全进了训练集而香蕉全进了测试集模型对香蕉的识别能力完全没法评估。import os import shutil import random from collections import defaultdict def split_dataset(root, train_ratio0.7, val_ratio0.15): for cls in os.listdir(root): cls_path os.path.join(root, cls) if not os.path.isdir(cls_path): continue images os.listdir(cls_path) random.shuffle(images) train_end int(len(images) * train_ratio) val_end train_end int(len(images) * val_ratio) # 按类别分层切分 for subset, idx_range in zip([train, val, test], [(0, train_end), (train_end, val_end), (val_end, len(images))]): target os.path.join(root, .., subset, cls) os.makedirs(target, exist_okTrue) for i in range(*idx_range): shutil.copy( os.path.join(cls_path, images[i]), os.path.join(target, images[i]) )4.2 迁移学习ResNet50 还是 MobileNetV2水果分类属于细粒度图像识别但类目差异较大苹果和香蕉的区别很明显不需要用到特别重的网络。两类网络选型逻辑不同ResNet50分类准确率高对训练集要求低在几千张图的小数据集上也有不错表现权重文件约 100MBCPU 推理稍慢但可接受。MobileNetV2模型轻量权重约 15MBCPU 推理快适合做前端展示流畅性演示准确率比 ResNet 略低但通常在可接受的范围内。常见做法是用 torchvision 加载预训练权重替换最后一层全连接为你的类别数import torchvision.models as models model models.resnet50(pretrainedTrue) num_features model.fc.in_features model.fc torch.nn.Linear(num_features, num_classes) # MobileNetV2 则改 classifier # model.classifier[1] torch.nn.Linear(last_features, num_classes)pretrainedTrue时会自动下载在 ImageNet 上训练好的权重。如果网络下载慢手动下载权重文件放到~/.cache/torch/hub/checkpoints/目录或者改用国内的模型镜像源。4.3 超参数设置batch_size、学习率与 epoch 次数的经验值train_loader DataLoader(train_dataset, batch_size32, shuffleTrue) val_loader DataLoader(val_dataset, batch_size32, shuffleFalse) optimizer optim.Adam(model.parameters(), lr0.0001) criterion nn.CrossEntropyLoss() scheduler optim.lr_scheduler.StepLR(optimizer, step_size5, gamma0.5) # 训练 20 轮每轮结束验证一次 for epoch in range(20): train_one_epoch(model, train_loader, criterion, optimizer) val_acc evaluate(model, val_loader) scheduler.step() print(fEpoch {epoch1}/20, Val Acc: {val_acc:.4f})参数经验值说明batch_size 在 16~64 之间都行取决于显存大小显存不够就降到 16学习率用 0.0001 配合 Adam 比较稳SGD 的话要 0.01 起步并配动量epoch 数要看验证集准确率的曲线如果连续 5 轮验证准确率不再上升就是收敛了不用等跑满全部轮数。如果训练过程中发现 loss 震荡不下降优先调低学习率发现过拟合训练准确率高、验证准确率低增加数据增强或减少训练轮数不要一开始就换网络结构。5. 避坑指南部署和训练中经常踩的七个问题5.1 启动报错ModuleNotFoundError: No module named torch现象python app.py运行后提示找不到 torch 模块。原因虚拟环境没有激活或者 requirements.txt 安装失败中断了。解决确认在fruit_env环境下执行pip list检查 torch 是否在列表中。如果确实没装上单独执行pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu。5.2 启动成功但浏览器打不开页面现象终端显示Running on http://127.0.0.1:5000但浏览器访问无响应。原因端口被占用或防火墙拦截了本地访问也可能是你访问的 IP 不对比如服务器上配了0.0.0.0却用127.0.0.1访问。解决先看终端有没有报错回溯再检查 5000 端口是否被其他服务占用netstat -ano | findstr 5000。如果被占用在代码里改端口app.run(port5001)重新启动。5.3 上传图片后识别结果一直是「未知」现象界面能上传图片也能返回结果但类别标签永远显示 unknown 或错误类别。原因模型加载成功但类别映射反了。ImageFolder按子目录名字母序生成索引和模型训练时的索引顺序不一致。解决打印预测结果对应的索引和class_names列表对照# debug 模式输出索引与类别映射 print(fpredicted index: {predicted.item()}) print(fclass mapping: {class_names})如果是映射错位调整class_names的排序方式为sorted(os.listdir(train_dir))保持训练和推理两端一致。5.4 预训练权重下载失败卡死现象第一次运行训练脚本长时间停在下载进度条不前进。原因PyTorch 默认从 download.pytorch.org 下载权重国内网络环境下经常超时。解决网速快的场景多试几次仍不行就手动下载对应的resnet50-0676ba61.pth权重文件放到C:\Users\你的用户名\.cache\torch\hub\checkpoints\Windows或~/.cache/torch/hub/checkpoints/Linux/Mac重跑脚本时会跳过下载。5.5 训练时显存溢出CUDA out of memory现象训练进行到第几轮时突然报RuntimeError: CUDA out of memory。原因batch_size 设置过大显卡显存尤其笔记本 4G 显存不够用。解决batch_size从 32 降到 16再不够就 8同时检查代码里是否同时加载了多个数据集副本。如果用了 PyTorch 2.x 还可以用torch.compile的reduce-overhead模式不过毕设场景下最简单的方式就是调小 batch。5.6 验证集准确率一直停留在 20%~30%接近随机水平现象训练 loss 在下降但验证准确率不见涨。原因数据集里的图片有问题——可能大量图片是网图、截图大小不一致图片损坏打不开或者类别标签文件被改过。解决先检查数据集的图片完整性遍历每个目录读取图片遇到损坏的直接移除或替换python -c from PIL import Image import os broken [] for root, _, files in os.walk(datasets/train): for f in files: try: img Image.open(os.path.join(root, f)) img.verify() except Exception: broken.append(os.path.join(root, f)) print(broken images:, len(broken)) for p in broken: print(p) 5.7 Flask 启动后识别速度特别慢CPU 占用 100%现象界面能正常交互但每次识别都要等好几秒CPU 一直满载。原因没有 GPU 的环境下模型跑在 CPU 上且没有做推理优化。解决确认你的 torch 是 CPU 版如果是 GPU 版但没装 CUDA也会退回到 CPU。CPU 推理场景下可以把输入图片直方图均衡化减少预处理耗时。但本质上 CPU 推理速度有限毕设答辩时提前把要识别的图片准备好现场演示时直接传图等待几秒是正常的。6. 进阶验证技巧用混淆矩阵和单张预测脚本把「能跑」升级成「可靠」跑通系统只是第一步答辩时老师会问「你这个模型到底准不准」不能只回一句「准确率很高」。用混淆矩阵可以直观地看到每个类别的识别情况还能发现哪些水果容易被搞混。# evaluate_confusion.py import numpy as np import torch from sklearn.metrics import confusion_matrix import seaborn as sns import matplotlib.pyplot as plt def generate_confusion(model, val_loader, class_names): model.eval() all_preds, all_labels [], [] with torch.no_grad(): for inputs, labels in val_loader: outputs model(inputs) _, preds torch.max(outputs, 1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(labels.cpu().numpy()) cm confusion_matrix(all_labels, all_preds) plt.figure(figsize(10, 8)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabelsclass_names, yticklabelsclass_names) plt.xlabel(Predicted) plt.ylabel(True) plt.title(Fruit Classification Confusion Matrix) plt.savefig(confusion_matrix.png, dpi150) # 按类别输出精确率与召回率 per_class_acc cm.diagonal() / cm.sum(axis1) for name, acc in zip(class_names, per_class_acc): print(f{name}: recall {acc:.2%})这段代码的价值在于它能把模型的问题定位到具体类别。你看混淆矩阵非对角线上的数字就能知道是哪两类水果互相混淆——比如苹果和红蛇果外观接近混淆概率高。答辩时主动说出来「苹果和蛇果之间存在 8% 的误判原因是两者颜色纹理接近后续可以收集更多这类样本做数据增强」比任何嘴上的解释都有说服力。还有一种验证方式是单张图片命令行预测把模型推理从 Web 界面里剥离出来用脚本直接测python predict_single.py --image test.jpg --model best.pth这样做的习惯我从最初的几个项目一直保留到现在。以前我拿到一套别人写的代码习惯性直接跑 app.py看到界面出来就默认一切正常直到有一次被答辩老师当场指出识别结果和图片不符——那次之后我每次部署完第自己的流程严格走一遍「训练一个 epoch 验证梯度在动 → 跑混淆矩阵看分类精度 → 单张图片命令行预言确认输入输出无偏移」三段流程。这套验证流程也建议你拿到资源后先做一遍不要急着改代码先确认原始代码在你机器上能稳定复现出合理的结果。希望帮到你。本文还有配套的精品资源点击获取