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

毕业设计级水果识别系统:PyTorch+ResNet18全流程实战

发布时间:2026/9/28 16:53:30

资讯中心
01
ARTICLE

毕业设计级水果识别系统:PyTorch+ResNet18全流程实战

毕业设计级水果识别系统:PyTorch+ResNet18全流程实战
简介这是一套面向计算机相关专业本科生的毕业设计级水果识别实战项目专为大作业与毕设开发需求打造解决图像分类场景下的端到端模型训练与部署问题。资源包含完整可运行的Python源码、预处理数据集、训练好的深度学习模型及配套Web前端界面代码经本地编译调试评审得分98分内容由助教审定难度适中且具备教学示范性。压缩包共277个文件约17.53MB其中8个.py文件构成核心训练与推理逻辑114个.js和84个.gif支撑前端交互与动态展示26个.css与多个字体/图标文件woff/ttf/eot/svg保障UI渲染另有jpg/png图片样本及html主页面构成可视化演示系统。目前已有141人学习下载读者可直接复现水果图像采集→数据增强→CNN模型训练→Flask/Django后端集成→BootstrapLayui前端部署的全流程附带清晰目录结构与注释便于理解模块划分与工程组织方式。1. 水果识别不是调个 API 就完事一个能跑通、能答辩、能改模型的毕业设计级 Python 项目你是不是也试过在 GitHub 上搜“水果识别”结果下了一堆只有 3 行 predict.py 的“项目”一运行就报ModuleNotFoundError: No module named torchvision.transforms或者RuntimeError: Expected 4-dimensional input, got 3—— 这根本不是项目是幻灯片截图打包。而眼前这个「基于深度学习的水果识别系统」是真正在 Windows PyTorch 环境下本地编译、逐行调试、带完整训练/验证/推理闭环的毕业设计实体。它不依赖云服务、不调外部 API、不靠预装环境从pip install -r requirements.txt开始到双击main.py弹出带摄像头实时识别界面为止全程可复现。98 分答辩成绩不是噱头——评审老师现场用手机拍苹果、香蕉、橙子系统 0.3 秒内返回类别置信度还能导出识别日志 Excel。适合计算机/人工智能方向本科生做毕设、大三课程设计、实训项目交付也适合想亲手跑通一个 CNN 图像分类全流程的新手数据增强怎么写、验证集怎么划分、模型怎么保存加载、GUI 怎么嵌入预测逻辑全在源码里埋了注释和断点。它不是玩具是能当“作品集硬货”贴进简历的完整工程。2. 从数据到模型为什么选 ResNet18 而不是 ViT训练脚本里的四个关键决策点这个项目没用最火的 ViT 或 Swin Transformer也没上 YOLOv8 做检测——它专注“单图多类分类”这一毕业设计最稳妥、最易讲清原理、最容易调参落地的场景。整个 pipeline 是典型的端到端监督学习原始图像 → 数据增强 → 特征提取ResNet18→ 分类头 → Softmax 输出。下面拆解训练脚本train.py中影响最终效果的四个硬核决策点它们直接决定你能不能在 4GB 显存笔记本上训出 92% 准确率。2.1 数据路径与目录结构必须严格对齐否则 DataLoader 直接静默失败项目自带的数据集按标准 ImageFolder 格式组织这是 PyTorch DataLoader 能自动识别类别的前提dataset/ ├── train/ │ ├── apple/ │ │ ├── apple_001.jpg │ │ └── ... │ ├── banana/ │ └── orange/ ├── val/ │ ├── apple/ │ ├── banana/ │ └── orange/ └── test/ # 可选用于最终评估注意train.py中data_dir ./dataset/train是硬编码路径如果你把数据集放在 D:\fruits\ 下必须同步修改该行。更稳妥的做法是用argparse动态传参但本项目为降低毕设复杂度采用固定路径——这也是它“开箱即用”的代价路径错训练不报错但准确率卡在 33%纯随机水平因为 DataLoader 读不到图片batch 全是空 tensor。2.2 数据增强策略不是越多越好毕业设计要平衡泛化与过拟合train.py中transforms.Compose定义了训练集增强链这里每一步都有教学意义train_transform transforms.Compose([ transforms.Resize((256, 256)), # 统一分辨率避免后续卷积层尺寸错乱 transforms.RandomHorizontalFlip(p0.5), # 随机翻转增加视角多样性水果无方向性 transforms.RandomRotation(degrees15), # ±15°旋转模拟摆放角度偏差 transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.2, hue0.1), # 模拟光照/色差 transforms.ToTensor(), # 转 Tensor 并归一化到 [0,1] transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) # ImageNet 标准化 ])RandomHorizontalFlip对水果有效但RandomVerticalFlip就没必要——苹果倒着放还是苹果但垂直翻转会引入非自然姿态ColorJitter参数值经过实测hue0.1是上限再高会导致香蕉变绿、橙子发紫破坏语义一致性Normalize的 mean/std 必须用 ImageNet 预训练权重对应的值否则迁移学习失效——ResNet18 是从 torchvision 加载的预训练权重这点不能改。2.3 模型选择与微调ResNet18 的三个可改参数毕业答辩必问项目默认使用models.resnet18(pretrainedTrue)但真正体现你理解深度的部分在于如何改它的分类头model models.resnet18(pretrainedTrue) num_ftrs model.fc.in_features # 获取原 fc 层输入维度512 model.fc nn.Sequential( nn.Dropout(0.5), # 防止过拟合毕业设计常被问“为什么加 Dropout” nn.Linear(num_ftrs, 128), # 降维减少参数量适配小数据集 nn.ReLU(), nn.Linear(128, len(class_names)) # 最终输出层class_names 来自 dataset/train 目录名 )pretrainedTrue是关键不用从零训收敛快、显存占用低4GB GPU 能跑nn.Dropout(0.5)不是玄学——在验证集 loss 波动大时把它调成 0.3如果训练 loss 下降但验证 loss 卡住说明过拟合可加到 0.7len(class_names)动态获取类别数意味着你增删水果种类比如加个“梨”文件夹无需改代码只要重新运行train.py即可。2.4 训练超参设置batch_size32 在 4GB 显存上的血泪经验train.py中batch_size 32是经过反复测试的平衡点batch_size4GB GPU 显存占用训练速度epoch/min验证准确率val_acc是否推荐162.1 GB1.891.2%✅ 稳定新手首选323.6 GB2.992.7%✅ 推荐需关闭其他程序64OOM显存溢出——❌ 不可行如果你的 GPU 是 GTX 1050 Ti4GB务必确认任务管理器中没有 Chrome、微信等吃显存进程num_workers4是 CPU 多线程加载数设太高反而因 I/O 瓶颈拖慢设太低如 0会卡在DataLoader等待学习率lr0.001是迁移学习常用值若发现 loss 不下降先检查pretrainedTrue是否生效打印model.fc.weight前几行应是非零值。3. GUI 界面与实时推理LayUI OpenCV 的轻量级组合为何比 PyQt 更适合毕设项目前端用的是 LayUI不是 Electron 或 Vue后端推理用 OpenCV PyTorch这套组合不是炫技而是针对毕业设计场景的务实选择部署包体积小50MB、启动快双击即开、无依赖冲突不碰 Qt DLL 版本地狱。main.py是入口它启动一个基于 Flask 的轻量 Web 服务前端 HTML 通过 LayUI 渲染所有交互逻辑都在static/js/app.js里。下面拆解三个核心模块的协作逻辑。3.1 Flask 后端只暴露/predict接口不做 RESTful 全套main.py启动 Flask 服务关键逻辑极简from flask import Flask, request, jsonify, render_template import torch from PIL import Image import numpy as np import cv2 app Flask(__name__) model torch.load(best_model.pth, map_locationcpu) # 强制 CPU 推理兼容无 GPU 环境 model.eval() app.route(/) def index(): return render_template(index.html) app.route(/predict, methods[POST]) def predict(): file request.files[image] # 前端 form-data 上传 img Image.open(file.stream).convert(RGB) # 预处理同训练时 transform但去掉 RandomXXX transform transforms.Compose([ transforms.Resize((256, 256)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) img_tensor transform(img).unsqueeze(0) # add batch dim with torch.no_grad(): output model(img_tensor) prob torch.nn.functional.softmax(output, dim1)[0] pred_class torch.argmax(prob).item() confidence prob[pred_class].item() return jsonify({ class: class_names[pred_class], confidence: round(confidence * 100, 2), all_probs: {class_names[i]: round(prob[i].item() * 100, 2) for i in range(len(class_names))} })map_locationcpu是关键避免学生电脑没 GPU 时torch.load报错unsqueeze(0)补 batch 维度否则模型输入 shape 错误torch.no_grad()关闭梯度计算提速 30%且防止内存泄漏。3.2 LayUI 前端用layui.upload实现“拍照/选图/识别”三合一templates/index.html中上传控件用 LayUI 原生组件不依赖第三方库div classlayui-form-item label classlayui-form-label上传图片/label div classlayui-input-block button typebutton classlayui-btn iduploadBtn i classlayui-icon#xe67c;/i 选择图片 /button button typebutton classlayui-btn layui-btn-normal idcameraBtn i classlayui-icon#xe63d;/i 拍照识别 /button div classlayui-form-mid layui-word-aux支持 JPG/PNG建议分辨率 ≥ 320x320/div /div /div对应 JS 逻辑在static/js/app.js// 选择图片上传 layui.use(upload, function(){ var upload layui.upload; upload.render({ elem: #uploadBtn, url: /predict, auto: true, accept: images, field: image, done: function(res){ $(#result).html( pstrong识别结果/strong${res.class}/p pstrong置信度/strong${res.confidence}%/p pstrong详细概率/strong${JSON.stringify(res.all_probs)}/p ); } }); }); // 拍照识别调用浏览器摄像头 document.getElementById(cameraBtn).onclick function() { const video document.getElementById(video); const canvas document.getElementById(canvas); const context canvas.getContext(2d); navigator.mediaDevices.getUserMedia({video: true}) .then(stream { video.srcObject stream; video.play(); }); // 截图并上传 document.getElementById(captureBtn).onclick function() { context.drawImage(video, 0, 0, 640, 480); canvas.toBlob(function(blob) { let formData new FormData(); formData.append(image, blob, snapshot.jpg); fetch(/predict, { method: POST, body: formData }).then(r r.json()).then(res { $(#result).html(...); // 同上 }); }, image/jpeg, 0.8); }; };LayUI 的upload.render自动处理 multipart/form-data 编码省去手动构造 FormDatacanvas.toBlob生成 JPEG 二进制流比canvas.toDataURL()内存占用低 60%避免大图上传超时navigator.mediaDevices.getUserMedia是现代浏览器标准 API无需额外插件。3.3 实时摄像头推理OpenCV 的cv2.VideoCapture如何绕过权限黑匣子项目还提供realtime_predict.py用 OpenCV 直接调用摄像头做实时识别比 Web 版延迟更低import cv2 import torch from PIL import Image import numpy as np cap cv2.VideoCapture(0) # 默认摄像头 if not cap.isOpened(): print(错误无法打开摄像头请检查设备或权限) exit() while True: ret, frame cap.read() if not ret: break # OpenCV 读取是 BGR转 RGB rgb_frame cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) pil_img Image.fromarray(rgb_frame) # 同 train.py 的预处理 transform transforms.Compose([...]) img_tensor transform(pil_img).unsqueeze(0) with torch.no_grad(): output model(img_tensor) prob torch.nn.functional.softmax(output, dim1)[0] pred_idx torch.argmax(prob).item() conf prob[pred_idx].item() # 在画面叠加文字 cv2.putText(frame, f{class_names[pred_idx]}: {conf:.1%}, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.imshow(Fruit Recognition, frame) if cv2.waitKey(1) 0xFF ord(q): # 按 q 退出 break cap.release() cv2.destroyAllWindows()cv2.VideoCapture(0)的0是设备索引外接 USB 摄像头可能是1或2需实测cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)必不可少否则颜色通道错乱香蕉识别成橙子cv2.putText的坐标(10,30)是左上角偏移字体大小1和粗细2经测试在 720p 画面上清晰可读。4. 避坑 / 常见问题 / 排查98 分项目也踩过的五个真实翻车现场这个项目虽经导师验收但在学生实际部署时仍高频出现以下问题。每个都是我帮 3 届学生 debug 过的真实案例按“现象 → 原因 → 解决”结构整理拒绝模糊描述。4.1 现象ImportError: cannot import name ResNet from torchvision.models原因torchvision版本与 PyTorch 不匹配。常见于pip install torchvision单独安装未指定版本。本项目要求torch1.13.1torchvision0.14.1高版本 torchvision 已重命名模块。解决执行pip uninstall torch torchvision然后一次性安装匹配版本pip install torch1.13.1cu117 torchvision0.14.1cu117 -f https://download.pytorch.org/whl/torch_stable.html提示CUDA 版本必须与你显卡驱动匹配。不确定时用torch1.13.1cpu替代牺牲速度保稳定。4.2 现象训练时loss一直为nan且val_acc停在 33.3%原因transforms.Normalize的std参数含 0 值如std[0.229, 0, 0.225]导致除零错误。这是复制粘贴代码时手误删掉了一个数字。解决检查train.py和predict.py中所有Normalize调用确认std三个值均非零。标准 ImageNet 值为[0.229, 0.224, 0.225]缺一不可。4.3 现象LayUI 界面点击“拍照识别”无反应控制台报NotAllowedError: Permission denied原因Chrome 浏览器在file://协议下禁止调用摄像头安全策略。项目必须通过http://localhost:5000访问不能双击index.html打开。解决确保main.py正在运行python main.py然后在浏览器地址栏输入http://localhost:5000而非file:///D:/project/index.html。4.4 现象realtime_predict.py运行后黑屏cv2.imshow窗口无图像原因OpenCV 的cv2.imshow在某些 Windows 环境下需配合cv2.waitKey()才刷新画面但waitKey(1)时间太短窗口来不及渲染。解决将cv2.waitKey(1)改为cv2.waitKey(10)或在cap.read()后加一行cv2.waitKey(1)强制刷新ret, frame cap.read() if not ret: break cv2.waitKey(1) # 关键强制刷新窗口缓冲区4.5 现象模型预测结果全是“unknown”或类别名乱码如红苹原因class_names列表是从os.listdir(./dataset/train)动态读取的但 Windows 文件系统默认按创建时间排序而非字母序。若文件夹建的顺序是orange→apple→banana则class_names[0]是orange但模型输出索引0对应apple。解决在train.py中显式排序train_dir ./dataset/train class_names sorted(os.listdir(train_dir)) # 加 sorted() 保证字母序血泪经验答辩前务必用print(class_names)确认顺序并与best_model.pth的state_dict中fc.weight形状核对。5. 模型导出与跨平台部署把.pth转成 ONNX让毕设能在树莓派上跑毕业设计答辩常被问“这个模型能部署到边缘设备吗”——光说“理论上可以”不够硬气。本项目已预留export_onnx.py脚本能把训练好的best_model.pth导出为 ONNX 格式这是跨平台Windows/Linux/ARM推理的通用中间表示。导出后你甚至能在树莓派 4B4GB RAM上用 ONNX Runtime 实时识别延迟 800ms。下面是一步到位的操作指南。5.1 导出 ONNX四行代码搞定但输入 shape 必须对齐export_onnx.py内容精简到极致import torch import torchvision.models as models # 1. 加载训练好的模型CPU 模式 model torch.load(best_model.pth, map_locationcpu) model.eval() # 2. 构造 dummy input必须与训练时的 transform 输出 shape 一致 dummy_input torch.randn(1, 3, 256, 256) # batch1, ch3, h256, w256 # 3. 导出 ONNX torch.onnx.export( model, dummy_input, fruit_recognition.onnx, export_paramsTrue, # 保存模型权重 opset_version11, # ONNX opset 版本11 兼容性最好 do_constant_foldingTrue, # 优化常量折叠 input_names[input], # 输入名供推理时引用 output_names[output], # 输出名 dynamic_axes{input: {0: batch_size}, output: {0: batch_size}} # 支持动态 batch ) print(ONNX 模型导出成功fruit_recognition.onnx)dummy_input的 shape(1,3,256,256)必须与train.py中Resize((256,256))一致否则 ONNX 推理时 tensor size mismatchopset_version11是关键低于 10 不支持AdaptiveAvgPool2dResNet 用的池化层高于 13 可能被旧版 ONNX Runtime 拒绝dynamic_axes允许推理时 batch_size 变化如一次传 1 张图或 4 张图但树莓派部署通常用batch_size1可删掉此参数简化。5.2 树莓派部署ONNX Runtime OpenCV三步启动实时识别树莓派端无需 PyTorch只需onnxruntime和opencv-python# 树莓派终端执行Python 3.9 pip install onnxruntime opencv-python wget https://your-server.com/fruit_recognition.onnx # 下载导出的 ONNX 模型raspi_inference.py推理脚本import onnxruntime as ort import cv2 import numpy as np # 加载 ONNX 模型 session ort.InferenceSession(fruit_recognition.onnx) # 读取并预处理图片同训练时逻辑 img cv2.imread(test.jpg) img cv2.resize(img, (256, 256)) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img img.astype(np.float32) / 255.0 img (img - [0.485, 0.456, 0.406]) / [0.229, 0.224, 0.225] # Normalize img np.transpose(img, (2, 0, 1)) # HWC → CHW img np.expand_dims(img, axis0) # add batch dim # ONNX 推理 inputs {session.get_inputs()[0].name: img} outputs session.run(None, inputs) prob outputs[0][0] # softmax 输出 pred_idx np.argmax(prob) confidence prob[pred_idx] print(f识别结果{class_names[pred_idx]}置信度{confidence:.1%})ort.InferenceSession比 PyTorchtorch.load内存占用低 60%树莓派 4B 上常驻内存 300MBnp.expand_dims(img, axis0)补 batch 维度ONNX 模型输入要求(1,3,256,256)class_names需与训练时完全一致建议在树莓派脚本中硬编码class_names [apple, banana, orange]。5.3 验证 ONNX 等效性用同一张图对比 PyTorch 与 ONNX 输出导出后必须验证 ONNX 结果与原.pth模型一致否则部署即翻车。verify_onnx.py提供一键比对import torch import onnxruntime as ort import numpy as np # 加载原模型 pt_model torch.load(best_model.pth, map_locationcpu) pt_model.eval() # 加载 ONNX 模型 ort_session ort.InferenceSession(fruit_recognition.onnx) # 构造相同输入 dummy_input torch.randn(1, 3, 256, 256) pt_input dummy_input.clone().detach() # PyTorch 推理 with torch.no_grad(): pt_output pt_model(pt_input) pt_prob torch.nn.functional.softmax(pt_output, dim1)[0].numpy() # ONNX 推理 ort_input pt_input.numpy() ort_output ort_session.run(None, {input: ort_input})[0][0] # 比较最大误差 max_diff np.max(np.abs(pt_prob - ort_output)) print(fPyTorch 与 ONNX 输出最大误差{max_diff:.6f}) assert max_diff 1e-4, ONNX 导出失败输出差异过大 print(✅ ONNX 模型验证通过)max_diff 1e-4是工业级容忍阈值超过说明导出时opset_version或dynamic_axes设置错误若报错AssertionError退回export_onnx.py将opset_version改为12或10重试。从那以后我每次导出 ONNX都强制走一遍verify_onnx.py哪怕只是改了个注释。因为答辩现场老师真会拿手机拍图让你现场 run而 ONNX 验证就是你的后悔药——它不保证模型更好但保证你交出去的东西和你本地跑出来的一模一样。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

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

03
WHY YAOTU

想打造同款高转化官网?

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

◈

场景化定制

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

◐

营销型架构

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

▲

全周期服务

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

免费获取你的建站方案

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