
简介这是一份基于Python与深度学习的垃圾分类系统毕业设计源码适合计算机相关专业用于毕业设计、期末大作业或课程设计也适合有Python基础的小白进行实战练习。系统围绕垃圾分类图像识别展开涵盖数据预处理、模型训练、类别预测与Web端展示等完整环节能够帮助读者理解卷积神经网络在图像分类任务中的实际搭建与调参流程。压缩包共6319个文件整体约18.98MB主要包含py源码、pyc编译文件、html前端页面、js交互脚本、png及jpg图像素材、exe可执行工具以及少量说明性txt文档文件类型齐全、目录结构清晰便于按模块查阅和二次开发。目前已有338人学习适合需要快速构建完整项目或参考高分毕业设计写作思路的同学。资源内还附带了环境依赖与运行所需的基础配置可辅助快速复现系统效果。1. 为什么这份垃圾分类系统源码值得你动手复现先别急着被“基于python与深度学习”这种标题劝退。拆开看这其实就是一个典型的图像分类落地项目传一张垃圾照片模型告诉你它是可回收、厨余、有害还是其他垃圾。但它拿高分的原因不在于算法有多新而是它把深度学习从“调包跑通”推进到了“能演示、能交付”的完整链路——数据整理、模型训练、Web界面、结果展示缺一不可。如果你正在做毕业设计或课程设计恰好需要一个能把“我会深度学习”写在答辩PPT里的项目这套源码的完成度会省掉你大量从零拼装的时间。我对这类源码一直保留一个习惯先不看模型结构先看数据怎么喂、推理接口怎么出结果。模型结构谁都能从GitHub拷但数据清洗和前后端拼装里的细节才是决定答辩时能不能当场跑通的关键。这篇笔记就按我自己拆这套项目的顺序来写从数据集准备讲到训练脚本再讲到Web推理和踩坑记录最后给一个验证系统真实效果的小技巧全部步骤都可以直接照着改。往下看之前建议你把压缩包解压后先跑一遍pip install -r requirements.txt把环境问题前置。2. 数据准备先于模型训练类别映射与图像预处理2.1 这套源码采用的数据组织方式项目里默认使用按类别分文件夹的图像库目录结构一般是dataset/train/、dataset/val/和dataset/test/每一类垃圾一个子文件夹文件夹名就是类别名。这种结构最直观PyTorch 的ImageFolder可以直接读取不需要手写复杂的 Dataset 类。项目实际运行时模型会把每一张图映射成一个数字标签并输出置信度因此中文类别名和数字标签之间的对应关系是整条链路里最先需要确认的东西。我拿到源码后的第一步永远是写一个几行的快速统计脚本把训练集每个目录下的图片数量、图片尺寸分布打印出来。这一步能提前暴露两个问题一是类别严重不平衡某个文件夹上千张而另一个只有几十张二是图片尺寸不统一有的图是 500×500有的是 1280×720这直接影响后面 dataloader 的Resize参数是否需要统一处理。下面是这个统计脚本的常见写法import os from PIL import Image from collections import Counter train_root dataset/train categories os.listdir(train_root) counter Counter() size_set set() for cat in categories: cat_path os.path.join(train_root, cat) if not os.path.isdir(cat_path): continue images [f for f in os.listdir(cat_path) if f.lower().endswith((.jpg, .jpeg, .png))] counter[cat] len(images) for img_name in images[:200]: # 每类抽样200张统计尺寸 with Image.open(os.path.join(cat_path, img_name)) as img: size_set.add(img.size) print(类别数量统计:, dict(counter)) print(出现过的图片尺寸:, list(size_set))逻辑很简单先遍历所有子目录统计图片数量再从每类抽前 200 张读取尺寸信息。如果size_set里出现多种尺寸训练时transforms.Resize((224, 224))会统一拉伸或裁剪通常问题不大但如果比例差异太夸张比如一张横构图一张竖构图模型对物体形变的适应能力会下降常见的做法是先按短边等比缩放到 256再做中心裁剪到 224。参数上建议优先用Resize(256)加CenterCrop(224)的组合而不是直接Resize((224, 224))畸变更小。2.2 类别映射中文标签与数字索引的对齐模型输出的是索引Web 前端展示的是中文两者之间的映射通常写在源码的labels.txt或class_indices.json里。这份项目里常见的是 40 类细分垃圾比如“一次性快餐盒”“旧衣服”“报纸”等然后再映射到“可回收物、有害垃圾、厨余垃圾、其他垃圾”四类大桶。这里有一个高分开题报告里反复强调的取舍模型不要直接学四个大类而是学 40 个细分类别最后再做一层规则映射因为细分类的数据特征更集中类间差异更大准确率通常更高。import json # 常见的映射结构实际内容以项目内 json 为准 labels_40 json.load(open(class_indices.json, encodingutf-8)) index_to_40 {int(k): v for k, v in labels_40.items()} # 细分类 - 四分类映射规则 four_class_map { 塑料瓶: 可回收物, 旧衣服: 可回收物, 报纸: 可回收物, 剩饭: 厨余垃圾, 香蕉皮: 厨余垃圾, 电池: 有害垃圾, 过期药品: 有害垃圾, } def index_to_four_class(idx): fine_label index_to_40[idx] return four_class_map.get(fine_label, 其他垃圾)参数说明class_indices.json的 key 必须是从 0 开始的整数或可强转的字符串否则ImageFolder.class_to_idx排序会错位。这种映射层的意义在于答辩时你可以清楚地解释“为什么不用 4 分类而用 40 分类”细分类任务更简单且支持后续扩展垃圾桶类型。实际复现时先打印出class_to_idx对照 json 人工核对一遍这一步花 5 分钟能省掉后面所有结果错位导致的翻车时间。3. 训练脚本怎么改才对得起高分ResNet34 迁移学习3.1 为什么这份源码选 ResNet 而不是更花哨的网络项目里主模型是 ResNet34配合 ImageNet 预训练权重。选它不是因为它最强而是因为这个体量的垃圾分类数据集只有一两万张图从零训练一个深层网络很容易过拟合而 ResNet34 在 ImageNet 上已经学好了边缘、纹理、形状这些通用特征只需要在垃圾图像上微调高层语义特征。相比 ResNet5034 层的参数量更小显存压力和训练时间都更友好相比 MobileNetResNet 在中等规模数据上的分数更稳当不会出现为压缩体积牺牲精度的问题。PyTorch 调用这块的代码很固定核心是替换最后的全连接层import torch import torch.nn as nn from torchvision import models, transforms model models.resnet34(weightsmodels.ResNet34_Weights.IMAGENET1K_V1) num_ftrs model.fc.in_features model.fc nn.Linear(num_ftrs, 40) # 40类细分类 device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device)逻辑说明weightsmodels.ResNet34_Weights.IMAGENET1K_V1是 PyTorch 新版推荐写法旧版pretrainedTrue已被标记为废弃继续用会在运行时报 deprecation 警告。model.fc.in_features取出原全连接层的输入维度ResNet34 默认是 512替换成 40 输出。如果机器显存不够把 batch size 调小比换网络更直接。训练阶段有一个值得你重点检查的细节项目里是否冻结了前面的卷积层。常见做法是前几层requires_grad_(False)只训练后面的 Block 和 FC这样显存占用小、收敛快。但如果你的数据集和 ImageNet 场景差异很大比如垃圾图片光线极差、视角怪异全量微调反而效果更好。我在这类项目上习惯先冻结微调 5 个 epoch再解冻全部层用更小学习率跑 10 个 epoch效果比直接全量训练更快。3.2 数据增强和超参数设置的参考值数据预处理在实际代码里长这样建议保持训练和推理两套 transform 高度一致至少归一化的 mean 和 std 必须相同否则 Web 端预测结果会崩。train_transform transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.RandomHorizontalFlip(p0.5), transforms.RandomRotation(15), transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.2), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) val_transform transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ])参数说明Normalize的值不能乱改它们必须和预训练权重的统计值一致这是从 ImageNet 数据集上算出来的。RandomRotation(15)是角度在垃圾图片场景里旋转太大会让类别语义模糊15 度以内是安全值。ColorJitter的亮度对比度增强用于模拟不同拍摄环境这是垃圾分类项目比通用分类更依赖的增强手段因为真实场景里手机拍出来的图亮度差异很大。训练超参数建议优化器用 Adam初始学习率 3e-4batch size 32取决于显存我一般从 32 起步OOM 就降到 16损失函数用 CrossEntropyLoss。学习率衰减不用太花哨ReduceLROnPlateau 在验证集 loss 连续 3 个 epoch 不降时乘 0.1这个策略对大多数毕设场景够用。训练轮数可以参考 20 个 epoch每轮结束后记录验证集准确率保存最优模型而不是最后一轮模型。3.3 保存 checkpoint 时顺便把类别索引写进去这是一个常被忽略但对后续 Web 部署影响很大的细节。训练结束保存的权重文件里只有神经网络的参数和优化器状态并不包含“类别 17 对应哪一类垃圾”的信息。如果 Web 推理端没有同步加载类别映射预测出的数字标签就是黑匣子。checkpoint { model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), class_to_idx: train_dataset.class_to_idx, epoch: epoch, val_acc: best_acc, } torch.save(checkpoint, best_model.pth)保存时强制写入class_to_idx加载权重后一次性恢复映射关系。这样即使换了机器、换了环境只要拿到这个 pth 文件推理脚本就能自解释出分类含义不需要再找原始 json。这是我从一个线上事故里学到的教训模型文件拷给同学跑结果对方没法复现我的精度最后发现是两边class_to_idx顺序不一致导致的错位。4. 把模型接成可交互的垃圾分类系统Flask 推理接口与边界处理4.1 模型加载与推理接口的常规结构这份源码用 Flask 实现 Web 端前端是原生 HTML 加一个上传框后端接一个 POST 接口接收图片并返回预测结果。整个交互链路是浏览器上传图片 → Flask 接口读取 → 走预处理 → 模型推理 → 返回类别与置信度。流程不复杂但有几个容易翻车的点集中在图片读取和预处理上。先看模型加载的写法import torch from torchvision import models import torch.nn as nn def load_model(model_path, num_classes40, devicecuda): model models.resnet34(weightsNone) model.fc nn.Linear(model.fc.in_features, num_classes) checkpoint torch.load(model_path, map_locationdevice) model.load_state_dict(checkpoint[model_state_dict]) model.to(device) model.eval() return model逻辑说明加载时必须先用weightsNone构造一个空骨架再覆盖同结构的全连接层最后用load_state_dict加载训练好的参数。如果加载时又填了预训练权重然后再load_state_dict等于白白下载一遍重复参数。model.eval()是必须的不调用的话 BatchNorm 层还在用训练时的统计量推理结果会不稳定。map_locationdevice在无 GPU 机器上读取 GPU 训练的权重时能自动映射到 CPU避免报 CUDA 不可用的错误。推理接口的核心部分不复杂但预处理要和训练保持一致尤其要注意图片通道顺序from PIL import Image import io app.route(/predict, methods[POST]) def predict(): file request.files.get(image) img Image.open(io.BytesIO(file.read())).convert(RGB) tensor val_transform(img).unsqueeze(0).to(device) with torch.no_grad(): outputs model(tensor) probs torch.softmax(outputs, dim1) top5_prob, top5_idx torch.topk(probs, 5) top5_idx top5_idx.cpu().numpy()[0] top5_prob top5_prob.cpu().numpy()[0] results [] for idx, p in zip(top5_idx, top5_prob): results.append({class: index_to_40[int(idx)], prob: round(float(p), 4)}) return jsonify(results)代码逻辑先读取二进制流并用 PIL 打开convert(RGB)强制转成三通道这一步能兜住用户传灰度图或 PNG 带透明通道的报错。val_transform(img).unsqueeze(0)把单张图变成 batch 大小为 1 的四维张量再到 GPU 上。torch.no_grad()关闭梯度计算推理阶段不需要反向传播显存占用更小。返回前用torch.topk取置信度最高的前 5 个类别而不是只返回第 1 名这样前端可以展示“最可能是塑料瓶也可能是一次性塑料盒”给人留下模型确实在“思考”的印象。4.2 前端展示时容易忽略的置信度陷阱一个真实发生过的问题模型对一张模糊图片输出的最高类置信度只有 0.31但前端依然只显示“可回收物 31%”会给用户造成错误引导。我在这类项目里的处理方式是加一个置信度阈值低于阈值时返回“无法识别请重新拍摄光线充足的照片”。这是答辩中可以刻意讲的细节很多评委喜欢问“这个系统对模糊图片怎么处理”这一句话就能把评分拉开。# 在返回 json 前加一层判断 max_prob float(probs.max().cpu()) if max_prob 0.5: return jsonify({warning: 图像不清晰或主题不明确请重新上传})阈值参数 0.5 不是固定值取决于你的验证集表现。如果验证集样本本身得分普遍偏低可以调到 0.35如果项目里有大量相似类别的细分类阈值太高会频繁误报“无法识别”。实际复现时我建议单独挑 20 张背景复杂的失败图跑一遍观察置信度分布区间来定这个值不要照抄任何项目的配置。5. 垃圾自觉系统实操避坑四类高频故障与排查手段5.1 坑一训练时验证集准确率正常但 Web 端预测结果离谱现象训练过程打印的 val_acc 能到 90% 以上前端上传同一张训练集图片预测结果却和标签对不上。原因训练和推理的预处理不一致。常见的有三种一是训练用了RandomHorizontalFlip推理时也误用了随机翻转二是推理时忘了归一化直接把 0~255 的像素值输入网络三是推理时图片被前端压缩过通道顺序变成 BGR用 CV2 读图但模型是按 RGB 训练的。解决单独写一个验证脚本读取dataset/test里的 5 张图和对应标签走一遍和 Flask 完全相同的 transform打印预测结果。如果这 5 张对了说明模型加载没问题问题出在 Flask 和前端传输环节如果这 5 张也不对直接怀疑 transform 差异。从那以后我每次上线 Web 端之前都强制走一遍固定测试集的抽样推理再部署。5.2 坑二RuntimeError: CUDA out of memory现象batch size 设为 32训练第 1 个 epoch 中途崩掉报CUDA out of memory且地址指向显卡显存不足。原因最常见的是数据加载阶段每个线程预取图片时占用了额外显存PyTorch 的 DataLoader 默认num_workers2每个 worker 会拷贝一部分数据到显存。其次前一轮实验的显存没有被释放程序出错退出但进程还挂着nvidia-smi能看到多个残留进程占着显存。解决先nvidia-smi杀掉残留进程或者重启机器然后把 DataLoader 的num_workers调整为 0同时把 batch size 从 32 降到 16。如果还崩把pin_memoryTrue关掉。还有一个进阶方案给模型训练加torch.cuda.empty_cache()在每个 epoch 结束执行一次释放碎片显存。但最核心的思路是——先确认是残留进程还是真实容量不足不要一上来就换模型。5.3 坑三权重文件加载报size mismatch现象load_state_dict报错提示fc.weight的尺寸不一致比如 Expected 40 但 got 1000。原因加载时用了别人训练好的完整模型它最后输出的分类数是 1000ImageNet 的类别数而你的模型已经把 FC 层改成 40。或者反过来你保存的不是完整 checkpoint 而是裸 state_dict。解决问题出在“骨架构造顺序”。先创建 ResNet34 空模型改fc为 40 类再加载。如果你手头只有别人保存的裸字典但它的 FC 输出是 1000就不要强行 load 这个权重改从公开资料中找到对应输出的版本。这个问题的本质是结构化排查不要试图通过strictFalse跳过那样你的 FC 层仍然是随机初始化推理结果不可用。我做毕设那会在这上面耗过一晚上凌晨才意识到是私自改了num_classes却没有同步改模型结构。5.4 坑四中文类别名显示乱码或编码报错现象训练脚本在 Windows 命令行里收集类别名时打印出来的中文全部是锟斤拷Flask 返回的 JSON 里中文变成\uXXXX。原因Windows 控制台默认编码是 GBKPython 从os.listdir读取的中文目录名在打印时按 UTF-8 输出控制台无法正确解码。Flask 的jsonify默认会转义非 ASCII 字符这是规范行为不算错但前端没有用JSON.parse解码就显示原始字符串时会看到转义序列。解决在脚本第一行加import sys; sys.stdout.reconfigure(encodingutf-8)存放类别映射的 json 文件必须用 UTF-8 编码打开json.load时加encodingutf-8Python 3 里open需要显式指定。前端如果看到\uXXXX用JSON.parse后再渲染实际值就是中文。乱码这个坑的隐蔽性在于它不影响程序运行只影响结果展示很多同学会误以为模型有问题其实是编码链路没对齐。6. 进阶技巧用错误分类抽样验证系统真的能扛住答辩演示当训练完成、Web 端也能跑通之后先别急着截图。我建议在dataset/test目录里跑一遍全量推理把预测错误的图片按“真实类别 → 预测类别”分组挑出高频的混淆对比如“一次性快餐盒”经常被识别成“塑料碗”“果皮”被识别成“剩饭”。这一步对答辩价值极大评委一旦问“你的系统哪里有不足”你不能只说“数据不够多”而要说具体哪些类别容易混淆、原因是什么、后续如何调整。下面是抽样脚本的核心片段import os import torch from PIL import Image test_root dataset/test wrong_cases [] for cat in os.listdir(test_root): cat_path os.path.join(test_root, cat) if not os.path.isdir(cat_path): continue for img_name in os.listdir(cat_path): img Image.open(os.path.join(cat_path, img_name)).convert(RGB) tensor val_transform(img).unsqueeze(0).to(device) with torch.no_grad(): pred_idx model(tensor).argmax(dim1).item() pred_label index_to_40[pred_idx] if pred_label ! cat: wrong_cases.append((cat, pred_label, img_name)) print(错误样本总数:, len(wrong_cases)) for real, pred, name in wrong_cases[:20]: print(f真实类别: {real} - 预测类别: {pred} 文件名: {name})逻辑说明逐类遍历测试集用已经推理部署的val_transform处理每一张图预测出的索引通过index_to_40还原成中文。如果预测和真实类别不符就记录到wrong_cases列表。打印前 20 条就足够观察模式。参数上注意两个地方一是val_transform不能带任何随机增强二是要统计测试集而不是训练集否则得分虚高。拿到混淆列表后针对高频错误类别可以做一轮针对性数据增强。比如“一次性快餐盒”总被识别错我先去测试集里看这个类别图片是不是大量带食物残渣再用ColorJitter和RandomResizedCrop强化训练重新微调 5 个 epoch通常能把这类别的召回率提升 3 到 5 个百分点。如果你时间不够也可以把高频混淆对写进答辩 PPT作为“后续优化方向”展示比你只说“准确率 92%”要有说服力得多。这套项目真正的价值不在于模型本身因为模型结构在网上到处都是。它值钱的地方在于数据怎么组织、映射怎么对齐、预处理怎么保持训练和推理一致、Web 端怎么兜住异常输入这四件事全部串起来才是能现场稳定演示的高分毕业设计。我从前做类似系统时最常犯的错就是把注意力全放在训练精度上结果 Web 端第一次连模型就报了 size mismatch现场手忙脚乱改代码。从那以后我每次拆这种源码包都强制按“数据统计 → 类别映射核对 → 推理脚本验证 → Web 联调”的顺序走一遍宁可慢一点也要确认每个环节的输出都对得上。希望这篇笔记能帮你把踩坑时间省下来把这些细节补进你自己的项目里祝顺利。本文还有配套的精品资源点击获取