
简介这份资源是面向高校学生与机器学习初学者的Python垃圾分类系统课程设计源码适合作为大作业、课程实践或入门级图像分类项目的参考方案。项目围绕垃圾图像识别与分类任务展开涵盖数据整理、模型训练与测试等环节可帮助读者理解从数据到模型落地的完整流程。压缩包共32个文件包含4个py脚本、4个xml配置、1个xls表格以及jpg、jpeg、png等图片素材和少量工程配置文件整体约2.27MB体量轻便便于快速下载与本地运行。目录中可见训练脚本、矩阵统计与测试模块配合结果图表能直观呈现模型训练效果与评估过程。目前已有1884人学习下载说明该方案在同类课程设计中具有一定参考价值。对于需要完成机器学习课程设计、想借鉴垃圾分类项目实现思路的读者这份源码可作为可运行的起点帮助节省环境搭建与代码调试时间。1. 从一份课程设计源码说起Python 垃圾分类系统到底能跑出什么课程设计选题里垃圾分类识别是出现频率极高的一类。原因很直接它有真实场景、有公开数据集、有明确的评价指标还能把 Python、机器学习、计算机视觉这几样东西串成一条完整的链路。但真正动手做过的人都知道从一份「机器学习课程设计Python垃圾分类系统源码」到能在自己机器上跑出结果中间隔着的不是几行代码而是一堆环境、路径、依赖版本和参数配置的坑。这份源码类项目通常包含几个部分数据集加载与预处理、模型定义与训练脚本、推理预测接口有的还会带一个简单的可视化界面。它适合两类人一类是正在做课程设计、需要一份能跑通的参考实现另一类是刚入门机器学习、想找一个完整项目把「数据到模型到预测」走一遍。但要注意源码能跑通不等于你能跑通环境差异、数据集路径、依赖版本这三样东西是翻车最集中的地方。这篇文章不假设你手上已经有一份完美的源码而是按「一个 Python 垃圾分类系统从零到能跑」的完整路径来讲。你会看到数据怎么组织、模型怎么选、训练脚本怎么写、参数怎么调、报错怎么排查。中间会给出可以直接抄的代码块和参数表也会把那些血泪经验摊开说清楚。如果你正在做课程设计或者想拿一个完整项目练手下面的内容可以照着复现。2. 垃圾分类系统的数据准备与模型选型为什么不是随便拿个 CNN 就完事2.1 数据集从哪来、怎么组织目录结构垃圾分类识别最常用的公开数据集是 TrashNet也有在此基础上扩充的版本。TrashNet 原始数据大约 2500 张左右分 6 类glass、paper、cardboard、plastic、metal、trash。图片尺寸不统一背景也比较杂。课程设计里如果直接用这个数据集第一件事不是写模型而是把目录结构定下来。常见的做法是按类别分文件夹训练集和验证集在代码里用splitfolders或者torchvision.datasets.ImageFolder配合random_split来切。我一般会先把原始数据整理成下面这种结构dataset/ glass/ paper/ cardboard/ plastic/ metal/ trash/然后写一个脚本做训练/验证/测试的划分。用splitfolders是最省事的import splitfolders # 输入原始目录输出划分后的目录 # ratio(0.7, 0.15, 0.15) 表示训练/验证/测试比例 splitfolders.ratio( dataset, outputdataset_split, seed42, ratio(0.7, 0.15, 0.15), group_prefixNone )这段代码的逻辑很简单把每个类别文件夹里的图片按 7:1.5:1.5 的比例复制到dataset_split/train、dataset_split/val、dataset_split/test三个目录下。seed42保证每次划分结果一致方便复现。group_prefixNone表示不按文件名前缀分组如果你的数据里有同一物体的多角度照片建议按前缀分组避免训练集和验证集出现同一物体的不同角度导致验证指标虚高。参数上唯一需要根据实际情况调的是ratio。如果数据量少于 2000 张验证集和测试集各留 10% 就够了训练集尽量大。如果数据量超过 1 万张可以按 8:1:1 来分。注意划分之前先检查每个类别下的图片数量。如果某一类少于 100 张训练时大概率会欠拟合需要做数据增强或者找补充数据。2.2 模型选型ResNet、MobileNet 还是自己搭一个小 CNN课程设计里常见的模型选择有三种自己搭一个 3 到 5 层的 CNN、用预训练的 ResNet18/50、用 MobileNetV2。三者的区别不在「哪个更高级」而在你的数据量、算力和课程设计的要求。自己搭 CNN 的优点是结构透明答辩时能讲清楚每一层在干什么。缺点是数据量小的时候容易过拟合准确率通常比预训练模型低 10 到 20 个百分点。ResNet18 预训练模型在 TrashNet 上微调通常能到 85% 到 92% 的准确率训练时间在单张 GPU 上大约 10 到 20 分钟。MobileNetV2 更轻适合部署到边缘设备或者用 CPU 推理准确率比 ResNet18 低 2 到 3 个百分点但推理速度快 3 倍以上。我一般会这样选如果课程设计只要求跑通并展示结果用 ResNet18 预训练加微调性价比最高。如果要求模型能部署到树莓派或者手机端选 MobileNetV2。如果老师明确要求「自己设计网络结构」那就搭一个带 BatchNorm 和 Dropout 的 4 层 CNN但要在报告里说明数据增强和正则化策略。下面是一个用torchvision加载预训练 ResNet18 并替换最后一层的代码import torch import torch.nn as nn from torchvision import models def build_model(num_classes6, pretrainedTrue): # 加载 ResNet18pretrainedTrue 表示使用 ImageNet 预训练权重 model models.resnet18(pretrainedpretrained) # 替换最后的全连接层输入特征数 512输出为类别数 model.fc nn.Linear(512, num_classes) return model # 如果要用 MobileNetV2把上面两行换成 # model models.mobilenet_v2(pretrainedpretrained) # model.classifier[1] nn.Linear(model.last_channel, num_classes)pretrainedTrue会下载 ImageNet 预训练权重第一次运行需要联网。如果网络不通可以提前下载权重文件放到~/.cache/torch/hub/checkpoints/下。num_classes6对应 TrashNet 的 6 个类别如果你的数据集类别数不同改这个参数就行。提示替换全连接层之后建议先冻结前面的卷积层训练几轮再解冻全部层做微调。这样能避免随机初始化的全连接层在初期产生大梯度破坏预训练权重。3. 训练脚本怎么写从数据加载到模型保存的完整链路3.1 DataLoader 与数据增强的参数配置数据加载这部分课程设计里最容易出问题的是图片尺寸不统一和增强过度。TrashNet 的图片尺寸从 200 多到 500 多不等直接 resize 到 224×224 是标准做法。增强策略上训练集用随机水平翻转、随机旋转 10 度、颜色抖动验证集和测试集只做 resize 和归一化。from torchvision import transforms from torch.utils.data import DataLoader from torchvision.datasets import ImageFolder # 训练集增强随机翻转、旋转、颜色抖动 train_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p0.5), transforms.RandomRotation(10), transforms.ColorJitter(brightness0.2, contrast0.2, saturation0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) # 验证集和测试集只做 resize 和归一化 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 ImageFolder(dataset_split/train, transformtrain_transform) val_dataset ImageFolder(dataset_split/val, transformval_transform) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers4) val_loader DataLoader(val_dataset, batch_size32, shuffleFalse, num_workers4)Normalize里的 mean 和 std 是 ImageNet 的统计值用预训练模型时必须保持一致。batch_size32是 8GB 显存下的安全值如果显存不够降到 16如果显存充足可以升到 64。num_workers4在 Windows 上有时会报错改成 0 或者 2 试试。shuffleTrue只对训练集开验证集不能开否则验证指标会波动。数据增强的度要控制住。RandomRotation(10)表示正负 10 度再大就可能把垃圾物体的特征转没了。ColorJitter的四个参数都设 0.2 是保守值如果数据集本身光照差异大可以加到 0.3 到 0.4。3.2 训练循环、学习率与保存策略训练循环的骨架很固定前向传播、算损失、反向传播、更新参数。但有几个参数直接决定你能不能跑出好结果。import torch.optim as optim from torch.optim.lr_scheduler import StepLR device torch.device(cuda if torch.cuda.is_available() else cpu) model build_model(num_classes6).to(device) # 交叉熵损失适用于单标签多分类 criterion nn.CrossEntropyLoss() # 初始学习率 0.001动量 0.9 optimizer optim.SGD(model.parameters(), lr0.001, momentum0.9, weight_decay1e-4) # 每 7 个 epoch 学习率乘以 0.1 scheduler StepLR(optimizer, step_size7, gamma0.1) best_acc 0.0 for epoch in range(30): model.train() running_loss 0.0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() running_loss loss.item() # 验证阶段 model.eval() correct 0 total 0 with torch.no_grad(): for images, labels in val_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, predicted torch.max(outputs, 1) total labels.size(0) correct (predicted labels).sum().item() val_acc correct / total scheduler.step() # 保存验证准确率最高的模型 if val_acc best_acc: best_acc val_acc torch.save(model.state_dict(), best_model.pth) print(fEpoch {epoch1}, Loss: {running_loss/len(train_loader):.4f}, Val Acc: {val_acc:.4f})学习率设 0.001 是微调预训练模型的常用起点。如果从头训练自己的 CNN可以设 0.01。weight_decay1e-4是 L2 正则化防止过拟合。StepLR每 7 个 epoch 把学习率降为原来的 0.1这个策略在 30 个 epoch 的训练里比较稳。保存策略上只保存验证准确率最高的模型而不是最后一个 epoch 的模型。课程设计里经常有人直接保存最后一个 epoch结果过拟合了还在用。torch.save(model.state_dict(), ...)只保存参数加载时需要先实例化模型再load_state_dict。注意如果训练 loss 一直在降但验证 acc 不升说明过拟合了。优先加数据增强、加 Dropout、减小模型复杂度而不是继续加 epoch。4. 推理与部署把训练好的模型跑成能用的预测接口4.1 单张图片预测的完整代码训练完之后课程设计通常要求演示预测效果。单张图片预测的代码要处理好图片预处理和类别映射。from PIL import Image import torch.nn.functional as F # 类别名称顺序必须和 ImageFolder 的类别索引一致 class_names [cardboard, glass, metal, paper, plastic, trash] def predict(image_path, model, device): # 加载图片并转为 RGB防止灰度图或 RGBA 图报错 image Image.open(image_path).convert(RGB) # 使用和验证集一致的预处理 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]) ]) input_tensor transform(image).unsqueeze(0).to(device) model.eval() with torch.no_grad(): output model(input_tensor) # 计算 softmax 得到概率 prob F.softmax(output, dim1) # 取概率最大的类别 conf, pred torch.max(prob, 1) return class_names[pred.item()], conf.item() # 使用示例 model build_model(num_classes6) model.load_state_dict(torch.load(best_model.pth, map_locationdevice)) model model.to(device) label, confidence predict(test.jpg, model, device) print(f预测类别: {label}, 置信度: {confidence:.4f})convert(RGB)这一步不能省TrashNet 里有少量灰度图不转的话ToTensor之后通道数对不上。unsqueeze(0)是加一个 batch 维度因为模型输入要求是[batch, channel, height, width]。map_locationdevice在加载时指定设备避免在 CPU 上训练的模型加载到 GPU 时报错。类别名称的顺序必须和ImageFolder的class_to_idx一致。ImageFolder默认按文件夹名称的字母顺序排索引所以class_names要按字母序写。如果不确定可以打印train_dataset.class_to_idx确认。4.2 批量测试与混淆矩阵课程设计答辩时老师通常会问「哪几类容易混」。这时候需要跑一遍测试集输出混淆矩阵。from sklearn.metrics import confusion_matrix, classification_report import numpy as np model.eval() all_preds [] all_labels [] with torch.no_grad(): for images, labels in test_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, predicted torch.max(outputs, 1) all_preds.extend(predicted.cpu().numpy()) all_labels.extend(labels.cpu().numpy()) # 输出分类报告 print(classification_report(all_labels, all_preds, target_namesclass_names)) # 输出混淆矩阵 cm confusion_matrix(all_labels, all_preds) print(cm)classification_report会给出每一类的 precision、recall、f1-score。TrashNet 上常见的混淆是 glass 和 plastic 互混因为透明塑料瓶和玻璃瓶在低分辨率下确实难分。如果这两类的 f1 明显低于其他类可以在报告里说明并给出改进方向比如增加这两类的数据或者用更强的 backbone。混淆矩阵用sklearn的confusion_matrix输出行是真实标签列是预测标签。对角线上的数字越大越好。如果某一列整体偏大说明模型倾向于预测那一类可能是类别不平衡导致的。5. 避坑与排查课程设计里最容易翻车的 5 个地方5.1 路径问题相对路径和绝对路径混用现象训练脚本在 PyCharm 里能跑换到命令行就报FileNotFoundError。原因PyCharm 的工作目录默认是项目根目录命令行的当前目录可能是别的。代码里用了相对路径dataset_split/train工作目录一变就找不到。解决统一用绝对路径或者在脚本开头用os.chdir切到脚本所在目录。我一般会在脚本最上面加import os os.chdir(os.path.dirname(os.path.abspath(__file__)))这样不管从哪运行工作目录都是脚本所在目录。5.2 显存不足CUDA out of memory现象训练到一半报RuntimeError: CUDA out of memory。原因batch_size太大或者没有释放中间变量。验证阶段没有用torch.no_grad()也会累积显存。解决先把batch_size降到 16 或 8。验证和测试阶段必须包在with torch.no_grad():里。如果还不行在训练循环里加torch.cuda.empty_cache()但不要每步都加会拖慢速度。5.3 类别不平衡某一类准确率特别低现象整体准确率 85%但 trash 类的 recall 只有 0.3。原因trash 类的样本数远少于其他类模型倾向于预测多数类。解决在CrossEntropyLoss里加weight参数按类别样本数的倒数给权重。或者对少数类做过采样。代码示例# 假设各类样本数为 [500, 480, 450, 520, 400, 100] class_counts [500, 480, 450, 520, 400, 100] weights [1.0 / c for c in class_counts] weights torch.tensor(weights, dtypetorch.float).to(device) criterion nn.CrossEntropyLoss(weightweights)5.4 模型加载报错Missing key(s) in state_dict现象load_state_dict时报Missing key(s) in state_dict: fc.weight, fc.bias。原因保存模型时用了torch.save(model, ...)保存整个模型加载时又先实例化了模型导致 key 对不上。或者替换全连接层之后保存的 key 和加载时的模型结构不一致。解决统一用torch.save(model.state_dict(), ...)保存参数加载时先build_model()再load_state_dict。如果 key 对不上打印model.state_dict().keys()和torch.load(...).keys()对比。5.5 验证集准确率波动大现象每个 epoch 的验证准确率上下跳动超过 5 个百分点。原因验证集太小或者batch_size太小导致 BatchNorm 统计量不稳定。解决增大验证集比例或者用model.eval()确保 BatchNorm 用移动平均而不是当前 batch 的统计量。如果验证集确实小可以跑多次取平均或者在报告里说明波动范围。6. 把准确率再往上推一推两个我常用的技巧第一个技巧是分层学习率。预训练模型的卷积层已经学到了通用特征不需要大改而新替换的全连接层是随机初始化的需要更大的学习率。用参数组给不同层设不同学习率# 卷积层用小学习率全连接层用大学习率 optimizer optim.SGD([ {params: model.conv1.parameters(), lr: 1e-4}, {params: model.layer1.parameters(), lr: 1e-4}, {params: model.layer2.parameters(), lr: 1e-4}, {params: model.layer3.parameters(), lr: 1e-4}, {params: model.layer4.parameters(), lr: 1e-4}, {params: model.fc.parameters(), lr: 1e-3} ], momentum0.9, weight_decay1e-4)这样全连接层学得快卷积层只做微调。在 TrashNet 上这个改动通常能带来 2 到 4 个百分点的提升。第二个技巧是测试时增强TTA。对同一张测试图片做多次增强把预测概率平均。代码不复杂但推理时间会翻几倍def predict_tta(image_path, model, device, n5): image Image.open(image_path).convert(RGB) 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]) ]) probs torch.zeros(1, 6).to(device) model.eval() with torch.no_grad(): for _ in range(n): # 每次做随机水平翻转 aug_image transforms.RandomHorizontalFlip(p0.5)(image) input_tensor transform(aug_image).unsqueeze(0).to(device) output model(input_tensor) probs F.softmax(output, dim1) prob probs / n conf, pred torch.max(prob, 1) return class_names[pred.item()], conf.item()TTA 在验证集上通常能再涨 1 到 2 个百分点但代价是推理时间乘以 n。如果课程设计只要求演示用 n3 就够了。最后说一个我自己的习惯每次跑完训练不管结果好坏都把best_model.pth的验证准确率、混淆矩阵和分类报告存到一个文本文件里。课程设计报告里要放这些数据临时跑一遍容易忘参数。另外随机种子一定要固定torch.manual_seed(42)、np.random.seed(42)、random.seed(42)三行都写上不然复现结果时对不上答辩时说不清楚。希望帮到你。本文还有配套的精品资源点击获取