简介这是一份面向机器学习课程设计或入门实践者的Python垃圾分类系统完整源码包。项目基于监督学习思路结合图像预处理、特征提取与模型训练可帮助学习者掌握从数据清洗、特征工程到模型评估与部署的完整流程适合高校学生完成课程报告或毕业设计参考。压缩包共32个文件其中包含4个Python脚本如模型训练、窗口识别、模型测试、jpg/jpeg/png图片样本、xml标注及xls结果文件可用于训练、测试和效果展示整体仅2.26MB轻量易用。目前已有772人学习浏览。通过该资源读者可获得可运行的垃圾分类模型源码、训练与预测脚本、数据集样本和可视化结果图以及清楚的工程目录结构能够快速复现项目并在此基础上进行改进。无论是初步接触机器学习还是需要借鉴课程设计框架这份资料都能提供完整参考具有较强的实用价值。1. 从课程设计到真实落地Python垃圾分类系统源码在解决什么问题每年到了课程设计季总有同学抱着一个“Python垃圾分类系统源码”的压缩包却不知道怎么把它变成自己答辩台上的东西。这个标题指向的是一个典型的基于深度学习图像分类的机器学习课程设计用Python写训练脚本用卷积神经网络对生活垃圾图片做分类最后给出一个能上传图片、返回类别和处置建议的可运行系统。它既是课程设计的标配题材也是入门CV方向最直观的闭环项目之一。比起单纯铺概念这篇笔记更想帮你把源码包里的东西拆成“能复现的最小路径”模型为什么这样选、训练参数怎么定、部署起来要哪些关键代码、以及最容易让你在答辩前夜翻车的几个坑。适合正在做课程设计、准备复试项目或者想快速跑通一个完整机器学习应用流程的读者。我们直接进入正题。2. 认识系统模型、数据与工程结构2.1 垃圾分类的本质是图像分类为什么优先选迁移学习打开任何一个“Python垃圾分类系统源码”核心模块几乎都是图像分类器。输入一张照片模型判断它属于可回收物、有害垃圾、厨余垃圾还是其他垃圾——如果数据集更细还会细分到纸板、玻璃、金属、塑料这些子类别。课程设计里最常见的做法是在ResNet、MobileNet或VGG这些预训练模型上做迁移学习而不是从零搭一个卷积神经网络。原因很实际。课程设计通常不会给你上万张高质量标注图数据集规模普遍在每类几百到几千张。从零训练的CNN在这么少的数据上很难收敛表现出的现象就是训练集准确率很高、验证集惨不忍睹典型过拟合。而加载ImageNet上预训练好的权重相当于模型已经会识别通用的纹理、边缘和形状特征我们只需要替换最后一层全连接用垃圾分类数据做微调。训练时间短、准确率高代码量也少在答辩时还能清楚讲出“为什么冻结特征层”“为什么把全连接层改成四分类”这类关键问题。import torchvision.models as models # 加载在ImageNet上预训练过的ResNet18 model models.resnet18(pretrainedTrue) # 把最后一层全连接换成4分类 num_features model.fc.in_features model.fc torch.nn.Linear(num_features, 4)这段代码是整个训练脚本最核心的几行。pretrainedTrue会下载预训练权重初次运行时需要联网把model.fc替换成新的全连接层是因为原来的1000类输出不再适用。这里有个容易被忽略的点新全连接层的权重是随机初始化的训练时如果整个网络一起更新前几个epoch梯度会被随机层主导破坏掉预训练学好的特征。所以常见做法是先把特征层参数冻结只训练新的全连接层跑几个epoch后再解冻微调。后面训练章节会具体讲参数怎么设。2.2 数据准备公共数据集带来的标签不一致坑课程设计用的数据很多来自公开的华为垃圾分类数据集或者从Kaggle、GitHub仓库收集整理。这类数据集通常会预处理好图片尺寸多数统一缩放到224×224或256×256。但拿到手后不能直接开训第一步要确认label和文件夹名的对应关系。常见的数据目录结构是这样的dataset/ train/ glass/ # 玻璃 metal/ # 金属 paper/ # 纸类 plastic/ # 塑料 val/ glass/ metal/ paper/ plastic/按文件夹名分配标签是很自然的做法但很多源码包里的label_map写死成了[glass, paper, metal, plastic]而你的目录排序可能不同。PyTorch的ImageFolder默认按文件夹名的字母顺序分配标签如果代码里手工指定的映射和实际顺序不一致就会出现训练时“玻璃”的标签是0预测时却把0当成了“纸类”——模型权重没问题是标签映射错位了。我一般会先打印dataset.class_to_idx确认映射再和label_map对一遍这一步能省掉后面一整轮的排查时间。另一个常见问题是类别不平衡。垃圾图片数据里可回收物往往远多于有害垃圾这会让模型对少样本类别学习不充分。后面避坑章节会专门讲处理办法数据准备阶段先确认每类图片数量如果某类只有几十张就需要考虑数据增强或过采样。2.3 源码目录结构每个文件在管线里的职责打开源码包除了训练脚本通常还包含数据处理、web部署和预测几个模块。常见文件规划大致如下classify_garbage/ train.py # 训练脚本加载数据、定义模型、训练、保存模型 predict.py # 单张图片预测脚本用于验证 app.py # Flask服务提供web上传接口 templates/ index.html # 上传页面 utils/ dataset.py # 数据加载、标签映射、数据增强 trainer.py # 训练循环与验证逻辑 models/ # 训练好的权重文件保存位置 requirements.txt # 依赖库清单这套结构的好处是训练、预测、部署三者解耦。运行train.py产出权重文件predict.py做离线验证app.py把训练好的模型包装成web服务。很多源码包为了压缩体积不包含权重文件需要你自己跑训练生成这其实是个好事——逼你至少完整跑通一次训练流程答辩时被问“模型怎么来的”才答得上。要做的一件事是检查requirements.txt里的依赖版本。常见的是torch、torchvision、flask、pillow、numpy、matplotlib这几项但有些老源码会写torch1.4.0这种旧版本。建议装好Python并激活虚拟环境后用pip install -r requirements.txt安装如果安装报错大多是版本冲突给torch和torchvision去掉版本号重装通常能解决。3. 用PyTorch跑通最小训练流程3.1 环境准备与一键训练命令训练前先把环境确认好。建议用Python 3.8以上的版本独立创建虚拟环境避免把系统Python搞乱。在Linux或Windows下命令类似python -m venv venv source venv/bin/activate # Windows下是 venv\Scripts\activate pip install torch torchvision flask pillow matplotlib scikit-learntorch是深度学习框架torchvision提供预训练模型和图像处理工具flask用来做web演示scikit-learn用于评估指标。安装完成后先跑一下python -c import torch; print(torch.__version__)能打印版本就说明环境没问题。然后是训练命令。源码包里的train.py通常支持命令行参数最常见的形式是python train.py --data_dir dataset --epochs 50 --batch_size 32 --lr 0.001如果包里面没有封装命令行参数而是直接改脚本里的常量路径那就手动把data_dir改成你的数据目录。我习惯先不用50个epoch设置成10个快速跑一遍确认数据加载、模型前向传播、损失函数、保存路径全都没问题再正式跑长训练。这比自己盯着黑屏终端看半个小时后才发现报错要划算得多。3.2 训练主脚本核心逻辑与关键参数训练脚本看着很长核心循环其实就那么几件事加载数据、前向传播、算损失、反向传播、更新权重、定期保存模型。下面抽主干代码来拆解一下。import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms, models # 1. 定义数据预处理 train_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(), transforms.RandomRotation(15), 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]) ]) # 2. 加载数据集 train_dataset datasets.ImageFolder(rootdataset/train, transformtrain_transform) val_dataset datasets.ImageFolder(rootdataset/val, transformval_transform) train_loader torch.utils.data.DataLoader(train_dataset, batch_size32, shuffleTrue) val_loader torch.utils.data.DataLoader(val_dataset, batch_size32, shuffleFalse) # 3. 定义模型并冻结特征层 model models.resnet18(pretrainedTrue) for param in model.parameters(): param.requires_grad False num_features model.fc.in_features model.fc nn.Linear(num_features, len(train_dataset.classes)) # 4. 定义损失函数和优化器 criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.fc.parameters(), lr0.001)先说明一个关键点数据预处理中使用了Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225])这是ImageNet数据集的均值和标准差。因为预训练模型是在ImageNet上训练的输入图片的分布必须对齐到同样的统计量上否则迁移效果会打折。如果源码里没写这一步预测结果经常出现奇怪的高错误率——输入图片的数值分布和模型预期不一致模型相当于在“陌生语言环境”下工作。训练和验证时的数据增强分离也是容易踩的地方。训练集用了随机翻转和随机旋转验证集只用缩放和归一化目的是让训练数据多样化、评估结果稳定。如果把数据增强也用到验证集每轮验证的结果会因为图片被随机旋转而上下波动你很难判断模型是在变好还是变坏。上面的冻结策略是第一阶段的。训练时我只更新model.fc这一层optimizer里传的是model.fc.parameters()。等第一阶段收敛后常见做法是解冻部分或全部层降低学习率继续微调。这也是为什么有些源码里训练脚本会分两段写学习率先大后小防止随机初始化的新层破坏预训练权重。3.3 训练过程中的评估用混淆矩阵而不是只看acc课程设计答辩里老师几乎一定会问“模型效果怎么样”。只回答“准确率95%”非常单薄因为准确率掩盖了类别间的差异。有害垃圾样本少即使模型每次都预测成其他类整体准确率也可能很高。所以在训练脚本里加一段验证逻辑输出混淆矩阵能让你答辩时多一张有说服力的图。import numpy as np from sklearn.metrics import confusion_matrix model.eval() all_preds, all_labels [], [] with torch.no_grad(): for images, labels in val_loader: outputs model(images) _, predicted torch.max(outputs, 1) all_preds.extend(predicted.cpu().numpy()) all_labels.extend(labels.cpu().numpy()) cm confusion_matrix(all_labels, all_preds) print(cm)这段代码放在每个epoch的验证阶段跑完打印一个矩阵。矩阵的第i行第j列表示“真实类别是i、预测为j”的样本数对角线越醒目越好。一张好的混淆矩阵图配合精确率、召回率、F1值答辩的展示深度和只看acc完全不一样。源码包里如果没有现成的评估函数建议自己加上这段顺手用matplotlib把混淆矩阵画出来保存为图片import matplotlib.pyplot as plt plt.imshow(cm, interpolationnearest) plt.colorbar() plt.xlabel(Predicted) plt.ylabel(True) plt.savefig(confusion_matrix.png)最后是检查点保存。训练50个epoch如果中途断电或内存不够全部从头重来非常折磨。保存最好的那个权重而不是最后一个epoch的权重是血泪经验。常见做法是每个epoch验证完后如果验证准确率比历史最高还高就覆盖保存一次best_model.pth同时保留last_model.pth。这样即使后面过拟合了也还能回退到验证集上表现最好的那版。很多源码只保存最后一次训练结果一旦最后一个epoch恰好过拟合模型文件就是坏的。4. 用Flask把模型封装成可演示的Web系统4.1 模型加载与预测接口课程设计如果只交一个训练脚本通常是不够的。老师会要求“能看到上传一张图片系统自动返回分类结果”所以一个可交互的web界面几乎是这类项目的标配。实现上最轻量的方案是用Flask搭一个单页应用前端一个上传按钮后端收到图片后调用模型预测把结果和置信度渲染回页面。import torch from flask import Flask, request, jsonify, render_template from torchvision import transforms, models from PIL import Image import io app Flask(__name__) # 类别名称和训练时的class_to_idx保持一致 classes [glass, metal, paper, plastic] # 加载模型 model models.resnet18(pretrainedFalse) model.fc torch.nn.Linear(model.fc.in_features, len(classes)) model.load_state_dict(torch.load(best_model.pth)) model.eval() 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]) ]) app.route(/) def index(): return render_template(index.html) app.route(/predict, methods[POST]) def predict(): file request.files[image] img Image.open(io.BytesIO(file.read())).convert(RGB) img_tensor transform(img).unsqueeze(0) with torch.no_grad(): outputs model(img_tensor) _, predicted torch.max(outputs, 1) prob torch.softmax(outputs, dim1)[0] * 100 return jsonify({ label: classes[predicted.item()], confidence: round(prob[predicted.item()].item(), 2) })这个接口的逻辑很直接/predict路由接收前端POST上来的图片文件用PIL打开并转成RGB三通道经过和训练时完全一致的数据预处理后送入模型前向推理最后返回类别标签和置信度百分比。需要注意的是如果训练时数据预处理里用了RandomHorizontalFlip这里一定不能带——推理时一张图片希望得到确定的结果随机翻转会让结果不稳定。模型加载时pretrainedFalse是必须的。有人会在部署脚本里也写pretrainedTrue结果每次启动都要重新下载预训练权重速度慢还可能报网络错误。加载的是训练好的best_model.pth里面的权重已经包括预训练权重和微调结果不需要也不应该再加载ImageNet的预训练权重。4.2 前端页面与服务启动源码包里的templates/index.html通常是一个简单的上传页面。如果包里的前端样式比较简陋可以替换成下面这个小而完整版本!DOCTYPE html html head title垃圾分类系统/title /head body h2上传一张垃圾图片/h2 form action/predict methodpost enctypemultipart/form-data input typefile nameimage acceptimage/* required button typesubmit识别/button /form /body /html注意form里action/predict必须和后端路由一致enctypemultipart/form-data是文件上传必须的编码方式。有的同学把源码下来改了接口路径忘了同步改表单的action点提交按钮后返回404还以为是模型有问题。启动服务用Flask自带测试服务器就够课程设计用了python app.pyFlask默认监听5000端口浏览器打开http://127.0.0.1:5000就能访问。如果端口被占用在脚本最后改成app.run(host0.0.0.0, port5001)换一个。把host设为0.0.0.0还有一个不方便明说的用处同一局域网内用手机访问电脑IP加端口也能打开页面答辩现场演示时不用挤在一台电脑前看。5. 避坑垃圾分类系统开发中最常见的5个坑5.1 中文文件名导致的图片读取为None现象训练脚本执行到中途报错提示Failed to load image但报错的图片在文件夹里明明存在。原因很多垃圾分类数据集的图片文件名是中文的比如“废纸箱001.jpg”或者文件夹路径本身带中文。一些基于OpenCV实现的旧源码用cv2.imread()读取图片它不支持中文路径读出来是None后续转成张量时直接崩溃。解决把cv2.imread()替换成Image.open()PIL对中文路径兼容性更好。如果源码里多处用到OpenCV读图统一改成PIL或者写一个包装函数def load_image(path): from PIL import Image return Image.open(path).convert(RGB)5.2 训练和预测时的预处理不一致结果严重漂移现象训练准确率95%部署后随便测几张真实照片预测结果准确率直线下降甚至出现同一个模型对同一张图两次预测答案不同。原因训练脚本里用了RandomHorizontalFlip和RandomRotation做数据增强但推理脚本也原样复制了这些变换。推理时图片被随机翻转旋转模型规则不明结果自然不稳定。另一个高频错误是推理脚本漏了Normalize输入图片像素值在0到255之间而训练时输入是0到1之间归一化后的分布完全不同。解决把训练和预测的数据预处理彻底分开。训练用带增强的transform推理只保留缩放、转张量、归一化严禁使用任何Random*操作。这是遇到“模型像玄学一样上蹿下跳”时第一个要检查的点。5.3 验证时忘记切换model.eval()BN层和数据增强一起捣乱现象验证阶段的准确率忽高忽低甚至训练集准确率100%验证集只有60%怎么调参都没用。原因模型默认处于model.train()模式BatchNorm层的统计量会随着当前batch的数据实时更新。验证时每张图片经过的BN统计量都不同输出自然不稳定。尤其是用预训练模型时更明显BN层的running_mean和running_var在没有固定统计量的情况下波动很大。解决验证和推理前显式调用model.eval()告诉PyTorch切到推理模式BN层使用训练阶段累积的全局统计量。训练前再切回model.train()。很多课程设计源码只在train里写了model.train()却没在eval里写model.eval()这个坑特别隐蔽。5.4 未设置随机种子两次训练结果差异巨大现象同一份代码、同一个数据集跑两次训练得到的准确率和混淆矩阵相差很大。答辩前夜跑出的最佳结果第二天重开环境复现不出来。原因PyTorch初始化模型权重、DataLoader随机打乱数据时都会用到随机数默认每次启动都不同。没有固定随机种子模型每次训练的起点和轨迹都变了。解决在训练脚本最开头加一段固定种子的代码import random, numpy as np, torch random.seed(42) np.random.seed(42) torch.manual_seed(42)如果机器有GPU还可以加一行torch.cuda.manual_seed_all(42)。这样至少同一环境下的训练流程是可以复现的“玄学波动”会小很多。5.5 数据集划分不当验证集出现数据泄漏现象训练集和验证集准确率都很高但拿到教室现场拍的照片或网上找的新图片去测效果很差。原因有些源码包直接从一个文件夹里随机抽取20%作为验证集但原始数据集本身就是从少数几个源头采集的同一个来源的图片在训练集和验证集里都出现了本质是“背题”。模型在遇到真正没见过的拍照场景时跨域泛化能力不足。解决按来源目录划分数据而不是按文件随机划分保证同一个来源的图片不会同时出现在训练集和验证集。如果条件允许单独留一个“现场测试”文件夹收集一些与训练数据风格不同的照片比如从手机拍、不同光照下用来模拟真实使用场景。答辩时能主动说自己做了跨场景验证是加分项。6. 进阶提升准确率与验证系统可用性6.1 如果只有几百张图片怎么把效果再往上提一点课程设计的数据普遍不算大这时最有效的几步不是换更大的模型而是把现有数据和模型用透。首先检查数据增强强度RandomResizedCrop、ColorJitter、RandomHorizontalFlip是图像分类三件套能同时缓解过拟合和提升对真实环境下拍摄角度变化的鲁棒性。其次尝试二阶段微调策略第一轮用较低学习率只更新全连接层第二轮解冻特征层全部参数学习率降到原来的十分之一继续微调通常能有几个点的提升。另外一个容易被忽视的是类别不均衡。如果“有害垃圾”只有其他类别一半的图片量可以在WeightedRandomSampler里给少样本类别更高的采样权重或者简单地在CrossEntropyLoss里传入class_weight参数。后者改一行损失函数就能生效criterion nn.CrossEntropyLoss( weighttorch.tensor([1.0, 1.0, 1.0, 2.0]) )实际权重值要根据你的数据分布计算这里只是示意。答辩时能讲清楚“因为我发现有害垃圾样本偏少所以加了类别权重”比只背准确率数字更有记忆点。6.2 训练收敛后如何验证和展示训练结束不是终点建议多做两件事来支撑答辩。第一把训练过程中的损失曲线和准确率曲线保存成图片这能直观展示模型在哪个epoch开始过拟合微调阶段效果如何。源码包里如果没有曲线记录用matplotlib在训练循环里每N轮记录loss和acc最后画图。第二准备几张真实环境下的测试图片手机拍的垃圾桶照片、不同光线下的饮料瓶放到一个demo_images文件夹里通过predict.py批量预测并打印结果。这样答辩现场不用临时翻相册上传也不怕现场网络不好导致公共API调用失败。如果学有余力还可以把模型导出为ONNX或者TorchScript格式推理速度比原生PyTorch更快同时也吹响“我考虑了部署效率”的信号。导出ONNX很简单dummy_input torch.randn(1, 3, 224, 224) torch.onnx.export(model, dummy_input, model.onnx, input_names[input], output_names[output])6.3 一个关于训练心态的小建议最后说点实在话。课程设计如果时间紧张不要试图重新发明轮子更不要追求训练出超过论文的准确率。核心是把“数据加载、模型训练、评估、部署”这条完整的机器学习应用流程跑通每个环节能说清楚为什么这么设计。我见过的翻车案例里反而多数不是因为模型效果差而是部署时模型加载地址写错、上传接口报错、演示图片在教室电脑上打不开这类工程问题。所以系统做完把虚拟环境、权重文件、演示图片、启动命令写进README换一台干净电脑完整跑一遍。自己能顺利走通的流程才是真正属于你的东西。希望这篇笔记能帮你的垃圾分类课程设计少走几步弯路顺利跑完属于你自己的机器学习落地项目。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?