尧图精选

基于Python与深度学习的垃圾分类系统:迁移学习、数据处理与Web部署全解析

🕒 发布时间:2026/10/1 19:10:11 📁 来源:尧图网络
简介这份资源是基于Python与深度学习的垃圾分类系统设计与实现项目定位为高分毕业设计、期末大作业或课程设计适合具有一定Python基础、希望做图像分类实战项目的计算机专业学生。压缩包共12个文件包含6个Python脚本覆盖数据生成、模型训练、推理演示等环节2个docx文档提供理论介绍与训练记录另有json配置文件、jpg示意图等辅助材料整体大小为4.21MB。代码结构清晰主模型采用ResNet50可帮助读者掌握深度学习图像分类项目的完整开发流程。已有373人学习项目可直接运行适合通过实际代码理解数据处理、模型搭建与训练调优的关键步骤也可作为毕业设计答辩的参考资料。1. 为什么一个“垃圾分类系统”会让编程新手卡在90%的进度上先说结论拿到“基于python与深度学习的垃圾分类系统设计与实现”这类高分项目最大的难点不是模型怎么训练而是别人看不见的那几步——数据集怎么组织、路径怎么处理、模型文件在解压zip之后还认不认、以及Web界面到底怎么跟推理代码串起来。很多照着教程瞎忙一周的人最后不是倒在CNN结构上而是倒在“能跑起来”四个字上。这个题目的本质是用深度学习做一个图像分类系统输入一张垃圾图片模型输出它是可回收、有害、厨余还是其他垃圾。听起来简单但它把python安装、深度学习环境配置、图像预处理、模型训练、后端接口、前端页面全部串了一遍恰好覆盖了课设和毕设评分里爱看的内容。适合谁做适合要把简历写成本科项目、或者需要交一份完整可演示系统的人。下面这套路线我自己带项目时反复用照着一个模块一个模块过三天能跑到Web界面出结果。2. 系统拆解与技术选型CNN不是唯一选择但迁移学习是最优解2.1 先想清楚这个系统由哪几块组成一个能演示的垃圾分类系统至少包含四个模块数据集、训练脚本、推理服务、Web前端。很多教程只讲训练把Web端一笔带过导致项目根本没法对外演示。实际评分和答辩不会只看模型精度他们更在意“完整链路能不能转起来”。我一般把整个项目拆成三条流水线数据流水线负责把图片变成模型能吃的张量训练流水线负责加载预训练模型、替换分类头、在GPU或CPU上跑若干epoch推理流水线负责接收前端上传的图片走一次前向传播返回分类结果和置信度。三者之间用模型文件.pth或.pt衔接Web端只依赖推理服务不碰训练代码。这样拆的好处是每个模块都能单独调试。数据流水线出问题时你不至于跑去改模型结构。2.2 为什么选迁移学习而不是从零训练CNN“基于深度学习”不等于要自己搭一个VGG或ResNet。垃圾分类公开数据集一般在几千到几万张之间从零训练一个深层网络在CPU上跑一个epoch可能就四五十分钟而且精度大概率不如微调后的预训练模型。这里用迁移学习是标准做法加载ImageNet上训好的权重冻住前面若干层只训练后面的分类层。选型上我最常用ResNet18或MobileNetV3。选ResNet18是因为它对新手友好结构直观参数量不大CPU也能勉强推理选MobileNetV3是因为如果后续要换个安卓端或者边缘设备做展示这个主干能直接搬。两个模型在百度网盘、Model Zoo之类的地方都能下载到预训练权重zip包里一般也会内置一份但要注意版本匹配gpytorch和torchvision的模型列表是在不同版本里才能对上。注意不要拿ImageNet的1000类分类头直接跑垃圾图片分类必须把最后一层全连接替换成“你的类别数”否则训练时loss会一直降不下去。2.3 zip包解压后先确认这几样东西拿到基于python与深度学习的垃圾分类系统设计与实现高分项目.zip之后别急着看代码先解压并确认目录结构里是否有这几类资源模型权重文件通常是以.pth、.pt、.ckpt结尾的文件大小在几十到两百MB之间。训练脚本train.py、data_loader.py这类文件缺少时要能自己写得出来。Web相关文件app.py、templates文件夹、static文件夹。如果没有只能用Flask或FastAPI自己补。数据集文件夹或数据准备说明不是所有zip都会附带全量数据很多项目只给样例和下载链接这时候需要自己准备。发现缺文件不必慌。上面这就是个典型的高分课设结构缺Model就补Model缺Web就补Web。最怕的是解压时出现zip伪加密报错Windows自带的解压工具会直接提示“文件损坏”。这种zip包在Linux下用7z或unar能正常解开后面避坑章节会专门讲。3. 数据准备与预处理把图片喂进模型前先解决“目录结构”和“中文路径”两个坑3.1 数据集目录结构torchvision.ImageFolder能直接吃什么样的文件夹训练一个图像分类器最省事的方式是用torchvision.datasets.ImageFolder它对目录格式有约定根目录下按分类建子文件夹每个子文件夹放同一类别的图片。例如dataset/ train/ recyclable/ img1.jpg img2.jpg kitchen/ img3.jpg img4.jpg val/ recyclable/ kitchen/对应的加载代码是# data_loader.py from torchvision import datasets, transforms from torch.utils.data import DataLoader # 训练数据增强与归一化 train_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p0.5), transforms.RandomRotation(degrees15), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) # 验证集只做缩放和归一化不做增强 val_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]) ]) train_dataset datasets.ImageFolder(rootdataset/train, transformtrain_transform) val_dataset datasets.ImageFolder(rootdataset/val, transformval_transform) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers0, pin_memoryTrue) val_loader DataLoader(val_dataset, batch_size32, shuffleFalse, num_workers0, pin_memoryTrue)这里有三个需要注意的参数。num_workers在Windows上建议设为0否则经常报BrokenPipeError这是因为Windows下多进程DataLoader的兼容性比较玄学pin_memoryTrue在CPU训练时没有明显收益但在GPU训练时可以加快host到device的传输shuffle在训练集必须打开否则模型会学到样本顺序里的虚假模式。归一化的mean和std是三通道RGB的标准ImageNet统计值不是你想当然的[0.5, 0.5, 0.5]写错的话模型收敛会很慢。3.2 样本不均衡有的垃圾类别图片多有的少怎么办垃圾分类数据集里可回收物和有害垃圾的数量往往差得很远。一个极端不平衡的训练集会让模型偏向样本多的类别导致有害垃圾的recall低得可怜。处理的办法有三个对少数类做过采样、对多数类做欠采样、在loss里加类别权重。一个直接可用的方案是在交叉熵loss中传递class_weight参数权重按类别样本数的倒数计算# train_utils.py import torch from torch.nn import CrossEntropyLoss def make_class_weight(labels): # labels: 长度为训练集样本总数的类别序号List cnt {} for y in labels: cnt[y] cnt.get(y, 0) 1 total len(labels) # 权重 总样本数 / 类别数量 / 该类样本数 weight [total / (len(cnt) * cnt[i]) for i in range(len(cnt))] return torch.tensor(weight, dtypetorch.float32) criterion CrossEntropyLoss(weightmake_class_weight(all_train_labels))这个weight列表会放进PyTorch的CrossEntropyLoss中损失函数会把少数类的梯度放大。这是性价比最高的处理方式因为不需要改动数据读入代码也不需要对图片做额外变换。数据增强方面我的建议是翻转、旋转、随机裁剪这三个操作是安全牌不需要上CutMix、MixUp这类高级技法。垃圾分类图片背景相对干净旋转角度不要超过20度否则瓶子倒了、盒子歪了语义信息反而被破坏。3.3 公开数据集的下载与目录整理脚本网上能找到的垃圾分类公开数据集有华为云垃圾分类数据集、垃圾图片分类数据集Garbage Classification等zip包项目里一般会给下载链接。如果你拿到的数据是分散的、没有按ImageFolder格式组织写一个脚本自动归位# organize_data.py import os import shutil import random def split_dataset(src_root, train_ratio0.8): # src_root: 原始数据集根目录子文件夹名为类别名 # 目标结构dataset/train/某个类/xxx.jpg, dataset/val/某个类/xxx.jpg dst_root dataset os.makedirs(dst_root, exist_okTrue) for class_name in os.listdir(src_root): class_dir os.path.join(src_root, class_name) if not os.path.isdir(class_dir): continue files os.listdir(class_dir) random.shuffle(files) split_idx int(len(files) * train_ratio) train_files files[:split_idx] val_files files[split_idx:] # 拷贝到新目录目录名保留中文 for split_name, split_files in [(train, train_files), (val, val_files)]: dst_dir os.path.join(dst_root, split_name, class_name) os.makedirs(dst_dir, exist_okTrue) for f in split_files: src os.path.join(class_dir, f) dst os.path.join(dst_dir, f) if not os.path.exists(dst): shutil.copy(src, dst) if __name__ __main__: split_dataset(raw_data)运行前先在src_root下放一个test.jpg做全流程验证防止写路径时大意导致全盘复制失败。这脚本里唯一需要你改的是train_ratio如果数据量少于2000张建议设成0.85保留多一点训练样本。4. 模型训练与评估迁移学习的三行关键代码和一套监控指标4.1 用torchvision加载ResNet18并替换分类头既然前面选了迁移学习这里直接给出最小训练配置。核心动作是三个加载预训练模型、冻结特征层、替换全连接层。# train.py import torch import torch.nn as nn from torchvision import models # 1. 加载预训练权重最好指定weights参数而不是从旧版写法里扣 model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) # 2. 冻结全部参数 for param in model.parameters(): param.requires_grad False # 3. 替换最后一层全连接num_classes要换成你自己的类别数 num_classes 4 # 比如可回收、有害、厨余、其他 model.fc nn.Linear(model.fc.in_features, num_classes) # 4. 只把分类头参数交给优化器 optimizer torch.optim.Adam(model.fc.parameters(), lr1e-3)逻辑说明requires_gradFalse意味着反向传播跳过这些层训练时只更新最后一层这既能大幅减少计算量也能防止数据量小时把预训练特征破坏掉。Adam的lr从1e-3起步是保守选择如果loss下降太慢可以提到5e-3但超过1e-2容易震荡。训练主循环我不放完整代码太长。你只需要知道每个epoch遍历train_loader把图片和标签放到同一个device上前向传播计算lossbackward之后optimizer.step()把梯度更新到模型参数。验证集每轮结束后跑一次准确率。4.2 如果精度不够这两行参数比换模型更有效一组值得试的调参顺序先确认分类头训练到收敛再解冻最后一两个block微调。# 解冻layer4配合更低的学习率 for name, param in model.named_parameters(): if layer4 in name or fc in name: param.requires_grad True # 分组学习率分类头1e-3解冻部分1e-4 optimizer torch.optim.Adam([ {params: model.fc.parameters(), lr: 1e-3}, {params: model.layer4.parameters(), lr: 1e-4} ], weight_decay1e-4)这个操作在业界叫“微调最后一层”是精度的主要来源。原因很简单预训练模型已经在ImageNet上见过大量纹理、轮廓、色彩特征垃圾图片的语义信息集中在局部纹理上因此只需微调最高层特征不必动底层。若解冻全部层在几千张图片上训练过拟合几乎是必然的。4.3 训练循环里的三个监控指标训练时不要只盯着loss打印应该额外保存三个东西每个epoch的平均loss、验证集准确率、每个类别的准确率。# evaluate.py def evaluate(model, val_loader, device, class_names): model.eval() correct 0 total 0 class_correct [0] * len(class_names) class_total [0] * len(class_names) with torch.no_grad(): for images, labels in val_loader: images, labels images.to(device), labels.to(device) outputs model(images) preds outputs.argmax(dim1) for i in range(len(labels)): cls labels[i].item() class_total[cls] 1 if preds[i].item() cls: correct 1 class_correct[cls] 1 total labels.size(0) overall_acc correct / total print(fOverall Acc: {overall_acc:.4f}) for i, name in enumerate(class_names): cls_acc class_correct[i] / max(class_total[i], 1) print(f{name}: {cls_acc:.4f}) return overall_acc类别准确率比总准确率重要得多。总准确率会被样本多的类别拉高而有害垃圾分错在答辩时是硬伤。如果某个类别的准确率低于70%优先怀疑样本量不够或该类图片背景和另一个类相似而不是急着换网络结构。训练中途可以用early stopping连续5个epoch验证集准确率不再提升就终止并保存当前最优模型。模型权重用torch.save(model.state_dict(), best_model.pth)保存不要保存整个模型对象前者的文件更小且在不同机器上加载兼容性更好。5. 常见问题排查与避坑指南我被这些坑各卡过一次5.1 解压zip一直提示文件损坏现象Windows 11自带的资源管理器解压项目zip时报“压缩文件已损坏”重下两遍都一样。原因这类项目zip为了压缩效率或防盗加了伪加密标志位。文件本身完整但Windows内置解压工具不认这一位Linux下的7z和macOS下的The Unarchiver能正常识别。还有一个原因是文件下载时网络中断导致zip包不完整但这种情况校验大小就能发现。解决先试试用7-Zip打开能打开就直接解压如果7-Zip也提示头部损坏在Linux下执行7z x 基于python与深度学习的垃圾分类系统设计与实现高分项目.zip若7z无法处理直接unzip命令加-O参数指定编码处理中文文件名乱码unzip -O gbk 基于python与深度学习的垃圾分类系统设计与实现高分项目.zip提示Windows下解压后的中文文件名乱码多半是zip内部文件名编码用了GBK而不是UTF-8这种情况不要在Windows里改名否则模型训练脚本里写死的路径会失效。5.2 num_workers报BrokenPipeError现象Windows环境下一跑DataLoader就崩错误日志一大堆BrokenPipeError。原因Windows没有Linux的fork机制PyTorch DataLoader的多进程在主程序被if __name__ __main__保护不当时经常出这个问题。此外num_workers不为0时Windows下每次启动训练都要重新拉起子进程。解决训练脚本的入口必须写成if __name__ __main__:包裹把DataLoader的num_workers设为0或者减小batch_size。如果一定要用多进程把DataLoader放到if块内部创建。5.3 模型训练loss不降准确率一直在20%左右现象分类问题有4个类训练了10个epochloss从2.0降到1.8之后几乎不动验证集准确率25%上下。原因分类头的输出维度不对或者标签从0开始编号但数据集类别文件夹排序和期望不一致。20%这个数值几乎就是“模型在瞎猜”的信号。解决先检查num_classes是否等于数据集里的类别文件夹数量。再打印train_dataset.class_to_idx确认类别名到索引的映射是否符合预期。常见坑是ImageFolder按文件夹名字典序分配索引如果你的文件夹叫“可回收物、有害垃圾”这种中文名排序不一定和你的预期对应。5.4 Linux服务器上提示python环境不对torch导入失败现象把项目传到Linux服务器上一运行import torch就报ModuleNotFoundError或者Illegal instruction。原因服务器上可能装的是系统自带python3而项目是在Windows的Anaconda里写的。torch不同版本对应不同CUDA版本CPU版和GPU版的whl包不能混用。在Linux离线环境下很多人用pip install在线失败转而下zip包离线安装反而容易装错版本。解决在项目根目录建一个requirements.txt固定版本号torch2.1.2 torchvision0.16.2 flask3.0.2 pillow10.2.0 numpy1.26.4然后用以下命令创建独立环境conda create -n waste python3.10 conda activate waste pip install -r requirements.txt如果目标机器完全离线copy一个torch的whl或zip包过去离线安装但要先确认python版本兼容。5.5 Web端上传图片后一直转圈后端没有任何反应现象前端选了图片点击识别按钮页面一直loading后端控制台什么都没打印。原因最常见是前端请求的URL和后端Flask路由不一致比如前端请求/predict后端实际注册的是/predict/多了个结尾斜杠。另一个可能是后端代码里图片读取时用cv2.imread()读的是中文字段名路径OpenCV不支持中文路径静默失败。解决先在后端加一行日志打印app.route(/predict, methods[POST]) def predict(): file request.files.get(image) print(received file:, file.filename) # 用PIL代替cv2读取PIL对中文路径的支持好得多 from PIL import Image img Image.open(file.stream).convert(RGB)关键点是图片传进来后不要走临时文件再读取直接用file.stream喂给PIL能避开文件名字符编码问题。如果还不行打开浏览器开发者工具看Network面板确认请求有没有发出去、返回的状态码是什么。6. 进阶玩法画一张混淆矩阵热力图让答辩老师一眼看懂你的模型前面所有步骤跑通后模型在验证集上准确率可能到了85%以上但答辩时老师问“哪些类别容易混”你如果只报一个总准确率就太浪费了。我习惯在每个项目收尾时加一个混淆矩阵可视化脚本既验证模型短板又是答辩加分项。混淆矩阵的行是真实类别列是预测类别。对角线越亮说明该类别识别越好非对角线亮点则是指向“易混淆对”。# confusion_matrix.py import torch import matplotlib.pyplot as plt import numpy as np from sklearn.metrics import confusion_matrix def plot_confusion_matrix(model, val_loader, device, class_names, save_pathconfusion.png): model.eval() y_true, y_pred [], [] with torch.no_grad(): for images, labels in val_loader: images images.to(device) outputs model(images) preds outputs.argmax(dim1).cpu().numpy() y_pred.extend(preds) y_true.extend(labels.numpy()) cm confusion_matrix(y_true, y_pred) # 归一化方便比较类别间的混淆程度 cm_norm cm.astype(float) / cm.sum(axis1, keepdimsTrue).clip(min1e-9) fig, ax plt.subplots(figsize(8, 6)) im ax.imshow(cm_norm, cmapBlues) ax.set_xticks(range(len(class_names))) ax.set_yticks(range(len(class_names))) ax.set_xticklabels(class_names, rotation45, haright) ax.set_yticklabels(class_names) for i in range(cm.shape[0]): for j in range(cm.shape[1]): text f{cm_norm[i, j]:.2f} ax.text(j, i, text, hacenter, vacenter, colorwhite if cm_norm[i, j] 0.5 else black) ax.set_xlabel(Predicted) ax.set_ylabel(True) fig.colorbar(im, axax) fig.tight_layout() fig.savefig(save_path, dpi150) print(Confusion matrix saved to, save_path)这段代码的三个要点normalize按行做得到的是recall视角的混淆比例比看原始计数直观cm.sum(axis1, keepdimsTrue).clip(min1e-9)防止某类在验证集上为0时除零报错color切换条件用的0.5阈值让色块文字始终清晰可读。跑这个脚本的时机很有讲究。我通常是训练结束后先跑一次如果发现“可回收物”和“有害垃圾”混淆严重就去翻几张三张典型的错误样本看看是背景颜色接近还是某些图片根本就是近义词标签下错了。这种错误分析比再调一轮learning rate更能说明你理解了模型。看到混淆矩阵里某两类交叉高亮最稳妥的补救办法是检查数据集里这两类的图片质量——模糊图、缩略图、带水印的图它们才是准确率上不去的“隐藏杀手”而不是网络不够深。从我自己的经验看一次完整的垃圾分类项目数据处理占一半时间模型训练只占两成剩下的时间全花在“让系统稳定跑完一个演示流程”上。做这个项目前我也总以为调参是最难的部分后来翻了五次车才明白把数据整理干净、处理好中文路径和zip伪加密这类边角料问题整个系统其实已经成功了大半。希望这篇踩坑记录能帮你跳过那些不值得再踩的坑。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →