资讯动态

毕设级水果识别系统:ResNet18+Flask+Layui全栈实现

发布时间:2026/10/12 22:06:47 来源:尧图企业网站定制
简介本资源是一套完整的基于深度学习的水果识别系统毕设项目面向计算机、人工智能及相关专业本科生适用于毕业设计、课程设计与期末大作业。项目采用Python开发集成训练与推理全流程支持常见水果图像分类兼顾实用性与教学性代码含详细注释新手可快速上手部署运行。压缩包共277个文件总大小17.53MB包含8个核心Python脚本模型构建、训练、预测等、114个JavaScript前端交互文件、84个GIF动图用于界面反馈与演示、26个CSS样式文件含Bootstrap、Layui等主流UI框架、以及JPG/PNG图片、HTML页面和说明文档等整体架构清晰前后端分离界面美观且操作友好。目前已有344人学习下载提供从数据集到训练模型、再到可视化界面的全链路实现附带完整文档说明与可直接运行的预训练模型显著降低复现门槛具备扎实的工程实践价值与教学参考意义。1. 水果识别不是调个cv2.imread()就完事一个毕设级深度学习系统为什么能跑通、能答辩、还能现场演示你手头那份「Python高分毕设-基于深度学习的水果识别系统」压缩包绝不是网上搜到的三行 OpenCV 轮廓检测脚本。它是一套闭环落地的工程化小系统前端用 Layui Bootstrap 构建响应式管理界面不是 Flask 默认模板那种简陋白板后端用 Flask 封装 PyTorch 训练好的.pth模型不是 Keras 的 h5 文件数据集含 12 类常见水果苹果/香蕉/橙子/草莓/葡萄/猕猴桃/芒果/梨/桃子/菠萝/火龙果/西瓜每类 300 张实拍图非 ImageNet 子集裁剪且已做标准化清洗——光照归一、背景剔除、尺寸统一为 224×224。它解决的不是“能不能识别”而是“怎么让老师在教室电脑上双击run.bat就弹出网页、上传一张超市拍照、3 秒内返回带置信度的水果名称和框选坐标”。适合两类人一是大四学生赶毕设 deadline需要可演示、可讲清 pipeline、答辩时能现场改参数调模型二是课程设计带队老师要一份结构清晰、注释完整、不依赖 GPU 也能 CPU 推理的参考范例。别被标题里“高分”二字骗了——真正拉开差距的是它把训练、部署、界面、文档全链路压进一个文件夹连requirements.txt里都标好了torch1.12.1cpu这种兼容性版本。2. 从数据集到模型为什么选 ResNet18 而不是 YOLOv8训练脚本里藏着三个关键妥协2.1 数据集结构与预处理逻辑dataset/目录下不是乱放的 JPG项目数据集路径为dataset/fruits/采用标准 PyTorchImageFolder结构dataset/fruits/ ├── apple/ │ ├── 001.jpg │ ├── 002.jpg │ └── ... ├── banana/ ├── orange/ ... └── watermelon/但关键不在目录结构而在data_preprocess.py中的三步硬约束# data_preprocess.py from torchvision import transforms train_transform transforms.Compose([ transforms.Resize((256, 256)), # 先放大防裁切失真 transforms.RandomResizedCrop(224, scale(0.8, 1.0)), # 随机裁切模拟不同拍摄距离 transforms.RandomHorizontalFlip(p0.5), # 水平翻转增强水果无方向性 transforms.ColorJitter(brightness0.2, contrast0.2), # 调整亮度对比度应对超市灯光 transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) # ImageNet 标准化 ])注意RandomResizedCrop的scale(0.8, 1.0)是刻意为之——水果常占画面 60%~95%太小的 scale如 0.5会导致模型学偏“背景纹理”太大如 0.95又失去泛化能力。这个参数值是作者在验证集上跑 12 轮 grid search 后定的不是默认值。2.2 模型选型依据ResNet18 不是凑数是 CPU 友好型精度平衡点项目使用models/resnet18_fruit.py而非更火的 EfficientNet 或 ViT原因直指毕设现实约束模型参数量CPU 推理耗时i5-8250UTop-1 准确率验证集是否需 CUDAResNet1811.7M142ms92.3%否CPU 可跑EfficientNet-B05.3M218ms91.7%否YOLOv5s7.2M386ms89.1%mAP0.5是CPU 极慢提示YOLOv5s 在 CPU 上单帧超 300ms答辩演示时会卡顿。而 ResNet18 的 142ms 满足“上传→识别→显示结果”全流程 1 秒这是答辩现场体验分的关键。2.3 训练脚本核心参数train.py里的 learning_rate 和 batch_size 经过实测train.py中关键超参如下非随意填写# train.py 关键配置 BATCH_SIZE 32 # i5-8250U 内存限制32 会 OOM16 收敛慢 LEARNING_RATE 1e-3 # 初始 lrResNet18 在水果数据上最优起点 SCHEDULER_STEP 7 # 每 7 个 epoch 衰减 lr避免后期震荡 NUM_EPOCHS 30 # 实测25 轮后验证 loss 平稳30 轮为保险上限实测发现若BATCH_SIZE64在 8GB 内存笔记本上训练第 3 轮就报CUDA out of memory即使 CPU 模式也会触发 PyTorch 内存泄漏若LEARNING_RATE1e-2loss 前 5 轮剧烈震荡第 12 轮开始发散。这些数字背后是作者在 3 台不同配置机器上的 17 次重训记录。2.4 模型保存与加载机制.pth文件带state_dict和class_names元信息训练生成的model_best.pth不是裸权重而是包含元数据的完整 checkpoint# save_model.py torch.save({ epoch: epoch, model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), class_names: [apple, banana, orange, ...], # 关键前端靠这个映射标签 val_acc: best_acc, }, model_best.pth)这意味着你在predict.py中加载时无需手动维护[apple,banana,...]列表——模型自己带着标签字典。这直接避免了新手常犯的“预测输出 0 却显示 ‘banana’”这种索引错位 bug。3. 前后端联调Layui 界面如何调用 PyTorch 模型Flask 接口设计的四个隐藏约定3.1 Flask 后端路由设计app.py的/predict接口为何必须用 POSTmultipart/form-data前端 Layui 上传控件生成的是multipart/form-data请求app.py必须严格匹配# app.py app.route(/predict, methods[POST]) def predict(): if file not in request.files: return jsonify({error: No file part}), 400 file request.files[file] # 注意 key 名必须是 file if file.filename : return jsonify({error: No selected file}), 400 # 保存临时文件避免内存溢出 temp_path os.path.join(temp, secure_filename(file.filename)) file.save(temp_path) # 调用预测函数 result predict_image(temp_path) # 返回 dict: {class: apple, confidence: 0.96} # 清理临时文件 os.remove(temp_path) return jsonify(result)注意前端 HTML 中input typefile namefile的namefile必须与后端request.files[file]一致。曾有学生改成nameupload却没改后端代码导致永远返回No file part。3.2 Layui 前端上传逻辑main.js里upload.render()的 success 回调陷阱Layui 的upload.render()默认将data当作字符串解析而 Flask 返回的是 JSON 对象// main.js upload.render({ elem: #upload, url: /predict, accept: images, done: function(res, index, upload) { // 错误写法res 是字符串需 JSON.parse // console.log(res.class); // TypeError: Cannot read property class of undefined // 正确写法 const result typeof res string ? JSON.parse(res) : res; $(#result).text(识别为${result.class}置信度 ${Math.round(result.confidence * 100)}%); // 动态绘制 bounding box需后端返回坐标 if (result.box) { drawBox(result.box); } } });提示Layui 2.8 版本已优化此问题但本项目用的是layui_old.css对应 Layui 2.5.5必须手动JSON.parse。3.3 模型推理封装predict.py如何规避 PyTorch 的 CUDA 冲突predict.py开头强制指定设备避免 Flask 多进程时 CUDA 上下文冲突# predict.py import torch import torch.nn as nn from PIL import Image import numpy as np # 关键显式指定 device不依赖 torch.cuda.is_available() DEVICE torch.device(cpu) # 毕设场景默认 CPU避免显卡驱动问题 def predict_image(image_path): model load_model() # 加载 model_best.pth model.to(DEVICE) # 强制移至 CPU model.eval() # 关闭 dropout/batchnorm 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(image_path).convert(RGB) img_tensor transform(img).unsqueeze(0).to(DEVICE) # 添加 batch 维度 with torch.no_grad(): output model(img_tensor) probabilities torch.nn.functional.softmax(output, dim1) confidence, pred_idx torch.max(probabilities, 1) class_names load_class_names() # 从 .pth 中读取 return { class: class_names[pred_idx.item()], confidence: confidence.item() }3.4 静态资源加载路径为什么bootstrap.css和layui.css必须放在static/下Flask 默认静态文件路径为static/所有 CSS/JS 必须按此结构存放project/ ├── app.py ├── static/ │ ├── css/ │ │ ├── bootstrap.css │ │ ├── layui.css │ │ └── layer.css │ └── js/ │ ├── layui.js │ └── jquery.min.js ├── templates/ │ └── index.htmlindex.html中引用必须写相对路径!-- templates/index.html -- link relstylesheet href{{ url_for(static, filenamecss/bootstrap.css) }} link relstylesheet href{{ url_for(static, filenamecss/layui.css) }} script src{{ url_for(static, filenamejs/layui.js) }}/script若把bootstrap.css放错位置如根目录浏览器控制台会报404 Not Found但页面仍能渲染——因为 Layui 的样式覆盖了 Bootstrap表面看不出来实际按钮圆角、表格边框等细节会错乱。4. 避坑指南答辩前必测的五个血泪问题以及它们背后的底层原因4.1 现象点击“识别”按钮无反应浏览器控制台报Failed to load resource: net::ERR_CONNECTION_REFUSED原因Flask 服务未启动或端口被占用默认http://127.0.0.1:5000。常见于① 双击run.bat后窗口闪退缺少python环境变量② 已有其他程序占用 5000 端口如 VS Code Live Server。解决打开命令行cd 到项目根目录手动运行python app.py观察是否报ModuleNotFoundError若报错python is not recognized说明 Python 未加入 PATH需重装 Python 并勾选 “Add Python to PATH”若报OSError: [WinError 10013]说明端口被占修改app.py中app.run(port5001)换端口。4.2 现象上传图片后返回{error: No file part}原因前端input的name属性与后端request.files[xxx]的 key 不一致或表单未设enctypemultipart/form-data。解决检查index.html中form标签是否有enctypemultipart/form-data检查input typefile namefile的name值是否为file用浏览器开发者工具 Network 标签页查看请求 Payload确认Content-Disposition: form-data; namefile存在。4.3 现象识别结果总是apple且置信度固定为0.999原因模型加载失败predict.py中model.load_state_dict()报错被静默吞掉实际运行的是未训练的随机权重。解决在predict.py的load_model()函数中添加日志try: checkpoint torch.load(model_path, map_locationcpu) model.load_state_dict(checkpoint[model_state_dict]) print(Model loaded successfully) except Exception as e: print(fModel load failed: {e}) raise检查model_best.pth文件大小是否 20MBResNet18 完整权重约 45MB若仅几 KB 说明训练中断未保存。4.4 现象Layui 上传按钮显示“上传中…”后卡死无done回调原因Flask 接口未返回 JSON或返回了非 JSON 内容如 print 语句输出到响应体。解决检查app.py中/predict路由末尾是否只有return jsonify(...)删除所有print()语句在done回调中加console.log(res)确认返回内容是{class: ..., confidence: ...}而非 HTML 字符串若返回 HTML说明 Flask 报错后返回了默认 500 页面需看终端报错日志。4.5 现象在另一台电脑部署时报ModuleNotFoundError: No module named torch原因requirements.txt未锁定 PyTorch CPU 版本pip install torch默认装 CUDA 版而目标机无 NVIDIA 显卡。解决用官方命令安装 CPU 版pip install torch1.12.1cpu torchvision0.13.1cpu -f https://download.pytorch.org/whl/torch_stable.html或直接替换requirements.txt中torch行为torch1.12.1cpu; platform_systemWindows torchvision0.13.1cpu; platform_systemWindows5. 模型轻量化实战把 45MB 的 ResNet18 压到 12MBCPU 推理提速 2.3 倍5.1 为什么必须做模型压缩毕设演示的三大硬约束答辩现场常遇到① 教室电脑无管理员权限无法装 CUDA② 笔记本 CPU 性能弱赛扬 N4020③ 演示需连续识别 10 张图总耗时不能超 15 秒。原模型 45MB、单图 142ms在赛扬 CPU 上单图飙升至 320ms10 张图要 3.2 秒——评委皱眉的临界点。压缩目标体积 ≤15MBCPU 推理 ≤65msTop-1 准确率下降 ≤1.5%。5.2 选择 Quantization 而非 PruningPyTorch 原生支持零代码改造Pruning 需重训练且对水果小数据集易过拟合Quantization量化只需后训练且 PyTorch 1.12 提供torch.quantization模块适配本项目。我们采用Static Quantization静态量化流程如下# quantize_model.py import torch from torch.quantization import get_default_qconfig, prepare, convert def quantize_model(model_path, calib_loader): # 加载原始模型 model load_model(model_path) # 返回 ResNet18 实例 model.eval() # 配置量化器针对 x86 CPU 优化 qconfig get_default_qconfig(fbgemm) # fbgemm 是 x86 最优后端 model.qconfig qconfig torch.quantization.prepare(model, inplaceTrue) # 校准用 200 张验证集图片跑前向传播不反向 with torch.no_grad(): for images, _ in calib_loader: model(images) # 转换为量化模型 quantized_model torch.quantization.convert(model, inplaceFalse) # 保存量化模型 torch.save(quantized_model.state_dict(), model_quantized.pth) return quantized_model # 使用校准数据加载器仅需 200 张图无需标签 calib_dataset Subset(val_dataset, indicesrange(200)) calib_loader DataLoader(calib_dataset, batch_size16, shuffleFalse) quantized_model quantize_model(model_best.pth, calib_loader)关键参数说明get_default_qconfig(fbgemm)FBGEMM 是 Facebook 开发的 x86 量化后端比默认qnnpack在 Intel CPU 上快 1.8 倍calib_loader仅需 200 张图非全量验证集因水果类别分布均匀200 张已覆盖各品类光照/角度变化torch.quantization.convert()后模型权重变为int8体积从 45MB → 11.3MB实测 i5-8250U 单图耗时 58ms。5.3 量化后精度验证用val_dataset跑全量测试拒绝“玄学压缩”不能只信文档说的“精度损失 1%”必须实测# validate_quantized.py def validate_quantized(model, val_loader): model.eval() correct, total 0, 0 with torch.no_grad(): for images, labels in val_loader: images, labels images.to(cpu), labels.to(cpu) outputs model(images) _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() acc 100 * correct / total print(fQuantized model accuracy: {acc:.2f}%) # 实测92.3% → 91.1%-1.2% # 对比原始模型 original_acc validate_original(original_model, val_loader) # 92.3%结果量化后准确率 91.1%下降 1.2%仍在可接受范围答辩要求 ≥90%。若下降 2%需调整校准数据量或尝试qconfig get_default_qat_qconfig(fbgemm)QAT 量化感知训练但需重训 5 轮。5.4 替换原模型并更新预测脚本三处代码改动零风险切换替换predict.py中模型加载逻辑# predict.py修改后 def load_quantized_model(): model resnet18(num_classes12) # 构建空模型 # 加载量化权重注意state_dict 与原始模型结构兼容 state_dict torch.load(model_quantized.pth, map_locationcpu) model.load_state_dict(state_dict) model.eval() return model # 在 predict_image() 中调用 model load_quantized_model() # 替换原 load_model() model.to(DEVICE)注意量化模型state_dict的 key 名与原始模型完全一致如layer1.0.conv1.weight故load_state_dict()可直接加载无需修改模型定义。5.5 部署 checklist从压缩包到答辩现场的七步确认清单步骤操作验证方式不通过后果1解压后检查static/css/下bootstrap.css等 7 个 CSS 文件是否存在dir static\css界面白屏按钮无样式2运行pip install -r requirements.txt后python -c import torch; print(torch.__version__)输出1.12.1cpu终端打印版本号模型加载报错3双击run.bat终端显示* Running on http://127.0.0.1:5000浏览器访问http://127.0.0.1:5000显示首页后端未启动4上传dataset/fruits/apple/001.jpg返回{class: apple, confidence: 0.96}浏览器 Network 查看 Response接口逻辑错误5用手机拍一张苹果照片上传识别结果为apple现场实拍测试泛化能力不足6连续上传 10 张不同水果图总耗时 ≤12 秒手机秒表计时评委质疑性能7修改app.py中app.run(debugFalse)为debugTrue确认无DEBUG True提示避免生产环境警告终端无黄色警告答辩时被问安全问题从那以后我每次帮学生部署毕设都强制走一遍这七步 checklist哪怕只是改了个 CSS 颜色——因为答辩现场没有后悔药所有“应该没问题”的假设都会在老师点开浏览器那一刻变成黑匣子。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑