ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Python的垃圾分类系统毕业设计:从CNN模型训练到Flask部署全流程

基于Python的垃圾分类系统毕业设计:从CNN模型训练到Flask部署全流程 简介这是一套面向计算机、人工智能、自动化等专业学生与教师的Python垃圾分类系统毕业设计资源包含完整源码与部署指南适合作为期末课程设计、大作业或毕设参考也便于基础较好的学习者在此基础上二次开发。资源包共126个文件约66.06MB涵盖20个Python脚本、13个Jupyter Notebook、12个Vue前端组件、18张运行截图及若干JSON、HTML、Markdown、PDF、Word文档与ONNX模型文件从算法训练、前后端实现到部署配置均有覆盖并附有参考报告与课程实践模板目录结构清晰便于按模块查阅。目前已有104人学习下载。整体项目经过调试测试答辩评审分达98分读者可据此快速理解垃圾分类识别流程、模型调用方式与系统部署思路减少从零搭建的时间成本对撰写论文与准备答辩具有较高的借鉴价值。1. 从一张宿舍楼下的垃圾桶照片说起垃圾分类系统到底在做什么去年帮学弟看毕业设计他选题是基于 Python 的垃圾分类系统开题报告写了八页代码却只有两百行——一个 Flask 页面加一个调模型接口的函数连数据集长什么样都没见过。答辩前一周他来找我说老师问你的模型在哪些类别上翻车最多他答不上来。这不是个例每年计算机毕业设计里垃圾分类系统是被选得最多、也最容易被做浅的题目之一。这个标题背后其实是一条完整的工程链路图像采集、数据清洗、模型训练、推理服务、前端交互、部署上线。它解决的核心问题是——把一张随手拍的垃圾照片映射到可回收物 / 厨余垃圾 / 有害垃圾 / 其他垃圾四分类中的一类并且给出足够快的响应。适合谁做适合想拿一个能跑通、能演示、能讲清楚技术细节的计算机毕业设计的学生也适合想练一遍从数据集到 Web 服务完整流程的 Python 入门者。下面我按自己带过几届毕设的经验把这条链路拆开讲源码结构和部署步骤都会落到能直接抄的程度。2. 垃圾分类系统的技术选型为什么是 CNN 而不是规则匹配2.1 图像分类任务的本质与模型选型逻辑垃圾分类识别在算法层面就是一个标准的图像分类问题。输入是 RGB 图像输出是四类概率分布。有人会想用颜色直方图或者边缘检测做规则匹配比如绿色的是厨余、蓝色的是可回收——这条路在实验室里能跑出 60% 左右的准确率但一换光照、一换拍摄角度就崩。原因很简单垃圾的视觉特征不是颜色决定的塑料瓶可以是透明的纸盒可以是彩色的规则匹配无法覆盖这种类内差异。所以主流做法是卷积神经网络。选型上有三个梯队ResNet50 精度稳但参数量 25MMobileNetV3 参数量只有 5M 左右、推理快EfficientNet-B0 在精度和速度之间平衡得不错。毕业设计场景我一般推荐 MobileNetV3-Small 或 ResNet18理由是训练在单张消费级显卡甚至 CPU 上能跑完推理延迟在 50ms 以内答辩演示不会卡。如果你追求更高的精度且实验室有卡可以上 ResNet50 做对比实验这本身就是论文里的一节。数据集方面公开可用的有 TrashNet2527 张6 类和华为云垃圾分类数据集约 1.5 万张40 类。TrashNet 类别少、图片干净适合快速跑通华为云那个类别细、噪声大适合做数据增强和难例挖掘的章节。我的建议是先用 TrashNet 跑通全流程再换大数据集做提升这样论文有对比、有故事。2.2 从零搭一个可训练的项目骨架项目目录结构决定了你后期改代码会不会痛苦。我见过太多毕设把所有代码塞在一个main.py里改一个参数要翻三百行。下面是我常用的骨架garbage-classify/ ├── configs/ │ └── default.yaml # 超参数、路径配置 ├── data/ │ ├── raw/ # 原始图片按类别分文件夹 │ └── split/ # 划分后的 train/val/test ├── src/ │ ├── dataset.py # Dataset 与 DataLoader │ ├── model.py # 模型定义 │ ├── train.py # 训练循环 │ ├── evaluate.py # 评估与混淆矩阵 │ └── predict.py # 单图推理 ├── app/ │ └── server.py # Flask 接口 ├── requirements.txt └── README.md配置文件用 YAML 而不是硬编码是为了让答辩老师一眼看到你的超参数是可管理的。configs/default.yaml内容大致如下data: root: data/split num_classes: 4 image_size: 224 batch_size: 32 num_workers: 4 model: name: mobilenet_v3_small pretrained: true train: epochs: 30 lr: 0.001 weight_decay: 0.0001 device: cuda参数说明image_size设 224 是因为 ImageNet 预训练权重默认输入尺寸改了要同步改模型第一层batch_size32 是 8G 显存下的安全值显存小就降到 16num_workers在 Windows 上设 0 更稳Linux 上设 4 能加速数据加载。pretrained: true是关键——从 ImageNet 权重微调比从零训练收敛快得多小数据集上精度能差 15 个点以上。2.3 数据划分与增强别让测试集泄漏进训练集数据划分是最容易翻车的地方。很多人直接把所有图片按 8:2 随机分结果同一张图的不同角度版本同时出现在训练集和测试集里测试准确率虚高到 98%一上真实场景就掉到 60%。正确做法是按原始来源分组划分同一来源的图片只能进一个集合。import os import random import shutil from pathlib import Path def split_dataset(raw_dir, out_dir, ratios(0.7, 0.15, 0.15), seed42): random.seed(seed) raw Path(raw_dir) out Path(out_dir) for cls_dir in raw.iterdir(): if not cls_dir.is_dir(): continue imgs list(cls_dir.glob(*.jpg)) list(cls_dir.glob(*.png)) random.shuffle(imgs) n len(imgs) n_train int(n * ratios[0]) n_val int(n * ratios[1]) splits { train: imgs[:n_train], val: imgs[n_train:n_train n_val], test: imgs[n_train n_val:], } for split_name, files in splits.items(): target out / split_name / cls_dir.name target.mkdir(parentsTrue, exist_okTrue) for f in files: shutil.copy(f, target / f.name) print(f{cls_dir.name}: train{len(splits[train])}, fval{len(splits[val])}, test{len(splits[test])}) if __name__ __main__: split_dataset(data/raw, data/split)这段脚本按类别分别划分保证每个类别的训练/验证/测试比例一致避免某类样本全进了训练集导致验证时该类无法评估。seed42固定随机种子保证你论文里的数据可复现——答辩老师如果让你重跑结果要能对上。数据增强用 torchvision 的 transforms 组合随机裁剪、水平翻转、颜色抖动。注意垃圾分类场景下不要用垂直翻转因为垃圾不会倒着放垂直翻转会引入不真实的样本。颜色抖动幅度也别太大否则透明塑料瓶和玻璃瓶的区分特征会被破坏。3. 训练、评估与推理把模型跑出能写进论文的数字3.1 训练循环的关键参数与早停策略训练脚本的核心是损失函数、优化器和学习率调度。垃圾分类四分类用交叉熵损失就够类别不均衡时加weight参数。优化器选 AdamW学习率 1e-3 配合余弦退火比固定学习率收敛更平滑。import torch import torch.nn as nn from torch.optim import AdamW from torch.optim.lr_scheduler import CosineAnnealingLR from torchvision import models def build_model(num_classes4, pretrainedTrue): weights models.MobileNet_V3_Small_Weights.DEFAULT if pretrained else None model models.mobilenet_v3_small(weightsweights) in_features model.classifier[3].in_features model.classifier[3] nn.Linear(in_features, num_classes) return model def train_one_epoch(model, loader, optimizer, criterion, device): model.train() total_loss, correct, total 0.0, 0, 0 for imgs, labels in loader: imgs, labels imgs.to(device), labels.to(device) optimizer.zero_grad() outputs model(imgs) loss criterion(outputs, labels) loss.backward() optimizer.step() total_loss loss.item() * imgs.size(0) correct (outputs.argmax(1) labels).sum().item() total imgs.size(0) return total_loss / total, correct / total # 早停验证集 loss 连续 5 轮不降就停 best_val_loss float(inf) patience, wait 5, 0 for epoch in range(cfg[train][epochs]): train_loss, train_acc train_one_epoch(model, train_loader, optimizer, criterion, device) val_loss, val_acc evaluate(model, val_loader, criterion, device) scheduler.step() if val_loss best_val_loss: best_val_loss val_loss torch.save(model.state_dict(), best.pth) wait 0 else: wait 1 if wait patience: print(fEarly stop at epoch {epoch}) break print(fEpoch {epoch}: train_loss{train_loss:.4f} train_acc{train_acc:.4f} fval_loss{val_loss:.4f} val_acc{val_acc:.4f})逻辑说明每个 epoch 先训练再验证验证 loss 创新低就保存权重连续 5 轮不降就提前停。这样既防止过拟合又省训练时间。torch.save只存state_dict而不是整个模型文件小、加载灵活。注意model.train()和model.eval()必须成对出现忘了切 eval 模式BatchNorm 和 Dropout 会在验证时继续更新验证指标会飘。3.2 混淆矩阵找出模型到底在哪两类之间犯迷糊准确率是个笼统指标答辩时老师更想看混淆矩阵。四分类的混淆矩阵能直接告诉你厨余和其他垃圾是不是经常混可回收和有害垃圾有没有分不清。import numpy as np import matplotlib.pyplot as plt from sklearn.metrics import confusion_matrix, classification_report def plot_confusion(model, loader, device, class_names): model.eval() preds, trues [], [] with torch.no_grad(): for imgs, labels in loader: imgs imgs.to(device) out model(imgs).argmax(1).cpu().numpy() preds.extend(out) trues.extend(labels.numpy()) cm confusion_matrix(trues, preds) print(classification_report(trues, preds, target_namesclass_names)) fig, ax plt.subplots(figsize(6, 5)) im ax.imshow(cm, cmapBlues) ax.set_xticks(range(len(class_names))) ax.set_yticks(range(len(class_names))) ax.set_xticklabels(class_names, rotation45) ax.set_yticklabels(class_names) for i in range(len(class_names)): for j in range(len(class_names)): ax.text(j, i, cm[i, j], hacenter, vacenter) plt.colorbar(im) plt.tight_layout() plt.savefig(confusion_matrix.png, dpi150)classification_report会输出每个类别的 precision、recall、f1-score。如果某一类 recall 特别低说明该类样本被大量误判要么加样本要么调类别权重。我做过的一个项目里有害垃圾的 recall 只有 0.62查下来是电池和药品包装的图片太少补了 300 张后涨到 0.81。这个排查过程写进论文比单纯报一个准确率有说服力得多。3.3 Flask 推理接口把模型变成能演示的服务模型训练完得有个界面能演示。Flask 是最轻的选择一个上传接口加一个预测接口前端用最简单的 HTML 表单就行。from flask import Flask, request, jsonify, render_template from PIL import Image import torch import io from torchvision import transforms app Flask(__name__) device torch.device(cuda if torch.cuda.is_available() else cpu) model build_model(num_classes4, pretrainedFalse) model.load_state_dict(torch.load(best.pth, map_locationdevice)) model.to(device).eval() CLASS_NAMES [可回收物, 厨余垃圾, 有害垃圾, 其他垃圾] preprocess transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) app.route(/) def index(): return render_template(index.html) app.route(/predict, methods[POST]) def predict(): file request.files.get(image) if not file: return jsonify({error: no image}), 400 img Image.open(io.BytesIO(file.read())).convert(RGB) tensor preprocess(img).unsqueeze(0).to(device) with torch.no_grad(): probs torch.softmax(model(tensor), dim1)[0] idx probs.argmax().item() return jsonify({ class: CLASS_NAMES[idx], confidence: round(probs[idx].item(), 4), all: {CLASS_NAMES[i]: round(probs[i].item(), 4) for i in range(4)} }) if __name__ __main__: app.run(host0.0.0.0, port5000)参数说明Normalize的均值和标准差必须和训练时一致这是新手最常犯的错——训练用了 ImageNet 统计量推理忘了加准确率直接掉一截。unsqueeze(0)是给单张图补上 batch 维度。host0.0.0.0让局域网内其他设备也能访问答辩时用手机拍一张就能演示。返回all字段是为了前端能画概率条比只显示一个类别更有说服力。4. 部署上线从本地 Flask 到能长期跑的服务4.1 环境依赖与 requirements 的坑部署第一步是把依赖锁死。pip freeze requirements.txt会把当前环境所有包都导出包括无关的建议手写核心依赖torch2.1.0 torchvision0.16.0 flask3.0.0 pillow10.1.0 numpy1.26.0 scikit-learn1.3.2 pyyaml6.0.1 gunicorn21.2.0版本号要写死否则换台机器pip install可能装到不兼容的新版本。torch 和 torchvision 版本必须匹配2.1.0 配 0.16.0装错了会报undefined symbol之类的玄学错误。如果目标机器没有 GPU装 CPU 版 torch 用pip install torch --index-url https://download.pytorch.org/whl/cpu体积小很多。4.2 用 Gunicorn 替代 Flask 自带服务器app.run()是开发服务器单线程、性能差、不能上生产。部署用 Gunicorngunicorn -w 4 -b 0.0.0.0:5000 app.server:app --timeout 120-w 4是 4 个 worker 进程一般设为 CPU 核数乘 2 加 1。--timeout 120是请求超时模型首次加载慢设小了会 502。注意 Gunicorn 在 Windows 上不支持Windows 部署要么用 Waitress要么直接上 Linux。我一般建议毕设部署用一台便宜的 Linux 云主机把模型文件和代码传上去用 systemd 托管 Gunicorn开机自启。4.3 模型加载慢与内存占用的优化Flask 每个 worker 会各自加载一份模型4 个 worker 就是 4 份内存。MobileNetV3 小模型还好ResNet50 一份就 100M 左右4 份 400M小内存机器扛不住。解决办法有两个一是减少 worker 数到 2二是用torch.jit或 ONNX 导出模型加载更快、内存更省。# 导出 ONNX推理时用 onnxruntime dummy torch.randn(1, 3, 224, 224).to(device) torch.onnx.export( model, dummy, model.onnx, input_names[input], output_names[output], dynamic_axes{input: {0: batch}, output: {0: batch}}, opset_version11, )导出后推理用onnxruntime.InferenceSession不依赖 torch内存占用能降一半以上CPU 推理速度也更快。这一步在毕设里算加分项能体现你考虑过部署成本。5. 避坑与排查那些答辩前夜才暴露的问题5.1 训练准确率 99% 但演示时全错现象训练日志里验证准确率 0.99答辩现场拍一张矿泉水瓶模型判成厨余垃圾。 原因训练集和测试集来自同一批网图风格高度一致模型学到的是背景和拍摄风格不是物体本身。真实手机拍摄的光照、角度、背景完全不同分布偏移导致崩溃。 解决训练时加入强增强随机旋转、透视变换、背景替换并且留一部分自己手机拍的图作为真实测试集不参与训练。论文里报告这个真实测试集的准确率比报告网图测试集更有价值。5.2 推理接口第一次请求超时现象服务启动后第一个请求要等十几秒才返回后面就快了。 原因模型权重是懒加载的第一次请求才真正把权重读进内存并初始化计算图。 解决在 Flask 启动时预热一次用一张全零图跑一遍model(tensor)把初始化开销提前到服务启动阶段。Gunicorn 的--preload参数也能让 master 进程先加载再 fork避免每个 worker 重复加载。5.3 中文类别名在接口里变成乱码现象返回的 JSON 里可回收物显示成\u53ef\u56de\u6536\u7269。 原因Flask 的jsonify默认ensure_asciiTrue把非 ASCII 字符转义了。 解决app.config[JSON_AS_ASCII] False或者用json.dumps(..., ensure_asciiFalse)手动构造响应。前端fetch拿到后正常解析即可不影响功能但演示时看着别扭。5.4 换机器后 torch 报 CUDA 版本不匹配现象本地训练好的模型拷到服务器torch.load报RuntimeError: CUDA error。 原因本地 torch 是 CUDA 11.8 编译的服务器驱动只支持到 11.6或者服务器根本没 GPU。 解决保存权重时用torch.save(model.state_dict())只存参数加载时map_locationcpu先加载再决定放哪。部署前用nvidia-smi确认驱动版本按官方对照表装对应 CUDA 版本的 torch。没 GPU 就装 CPU 版代码里device自动判断。5.5 图片上传后方向不对现象手机拍的照片上传后模型识别错下载下来看是横着的。 原因手机照片带 EXIF 方向信息PIL 默认不读取导致图像被旋转。 解决用PIL.ImageOps.exif_transpose(img)自动纠正方向再送进预处理。这一行不加竖拍的照片全变横的模型自然认不出。6. 把毕设做成能讲二十分钟的项目几个提分技巧答辩时最怕的不是模型精度不够而是讲不出技术细节。我见过一个学生准确率只有 82%但他把数据清洗、增强策略、混淆矩阵分析、部署优化讲得清清楚楚最后拿了优秀。另一个学生准确率 95%问他为什么选 MobileNet 不选 ResNet答网上说 MobileNet 快直接被打断。第一个技巧是准备一张技术决策表把每个选型的原因写清楚。比如决策点选择备选理由模型MobileNetV3-SmallResNet50参数量 5M vs 25MCPU 推理 30ms vs 120ms精度差 2 个点可接受优化器AdamWSGD小数据集上收敛更快无需精细调 lr部署Gunicorn ONNXFlask 原生并发能力提升内存占用降一半增强随机裁剪翻转仅翻转提升泛化真实测试集准确率 6 个点这张表往 PPT 一放老师的问题基本都能接住。第二个技巧是留一个失败案例页。挑几张模型判错的图分析为什么错——是光照太暗、物体被遮挡还是类别本身边界模糊比如用过的纸巾算厨余还是其他。这体现你有反思能力比只报喜不报忧强得多。第三个技巧是把部署过程录成一段两分钟的视频。从git clone到pip install到启动服务到上传图片出结果全程录屏。答辩现场网络不稳、环境可能出问题视频是后悔药。而且这段视频本身就是你部署指南的实证比文字描述可信。最后一个习惯所有实验数据当天记录别攒到最后补。我一般让学生建一个experiments.md每次跑完训练就记一行——日期、改动、验证准确率、备注。到写论文时这些记录直接就是实验章节的素材不用回忆当时那个 0.87 是哪次跑出来的。这个习惯我保持了六年帮我在无数次答辩和评审里省下了翻日志的时间。希望帮到你。本文还有配套的精品资源点击获取
返回列表