ARTICLE DETAIL

资讯详情

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

基于Python与深度学习的垃圾分类系统源码实战:从训练到部署

基于Python与深度学习的垃圾分类系统源码实战:从训练到部署 简介这是一套面向高校学生与初学者的垃圾分类深度学习实战项目源码基于Python与主流深度学习框架实现可直接用于毕业设计、期末大作业或课程设计场景。项目已通过教师指导与验收属于高分完整方案对零基础读者也较为友好能帮助快速理解图像分类任务的完整落地流程。压缩包共收录6319个文件约18.98MB其中以py源码与pyc编译文件为主体辅以mo、po多语言资源、html与js前端页面、png与svg图像素材以及txt说明、css样式和少量exe工具整体结构接近可直接运行的工程形态。目前已有338人学习下载说明该方案在同类选题中具备一定参考热度。读者可从中获取完整的垃圾分类识别代码、模型训练与推理脚本、界面展示页面及配套资源文件便于对照复现实验、梳理项目目录组织方式并在此基础上进行功能扩展或二次开发。1. 从一份能跑通的垃圾分类源码说起它到底解决了什么如果你正在为毕业设计发愁或者想找一个能真正跑起来、有完整训练流程的深度学习项目练手这份基于 Python 与深度学习的垃圾分类系统源码值得先看一眼。它不是那种只丢几个.py文件、连数据集路径都写死的半成品而是一套从数据预处理、模型训练、评估到推理预测都能串起来的完整工程。垃圾分类本身是个典型的图像分类任务类别通常包括可回收物、厨余垃圾、有害垃圾和其他垃圾四类正好覆盖了 CNN 从入门到落地的全部关键环节。你拿到手之后最直接的用途就是改一改数据集路径、调一调超参数就能跑出自己的分类模型往深了说这套代码的结构足够清晰方便你在上面加注意力机制、换 backbone、做消融实验支撑起一篇像样的毕业论文。适合谁适合已经装好 Python 环境、知道pip install怎么用、但还没完整走过一遍深度学习项目流程的人。下面我就按实际拆包复现的顺序把这份资源从里到外讲一遍。2. 拆开压缩包先看什么目录结构与技术栈选型2.1 拿到源码后的第一轮文件排查解压之后别急着python train.py先花五分钟把目录结构过一遍。这类毕业设计项目通常包含以下几个核心部分data/或dataset/放原始图片按类别分文件夹models/存网络定义utils/放数据加载和预处理脚本根目录下有train.py、predict.py、evaluate.py这几个入口文件可能还有一个config.py或args.py统一管理超参数。我一般会先执行下面这段命令把文件树和文件大小摸清楚# 查看目录结构排除缓存文件 find . -type f -not -path ./.git/* -not -name *.pyc | head -50 # 统计各类图片数量确认数据集是否完整 for dir in data/*/; do echo -n $dir: find $dir -type f \( -name *.jpg -o -name *.png -o -name *.jpeg \) | wc -l done第一段命令帮你快速定位入口脚本和配置文件的位置第二段命令按类别统计图片数量。如果某个类别只有几十张图那训练时大概率会严重过拟合需要先做数据增强或者补充样本。这一步看着简单但我见过太多人直接开训跑了半天才发现某个类别文件夹是空的血泪经验。2.2 技术栈选型为什么是 PyTorch CNN这份源码大概率用的是 PyTorch 而不是 TensorFlow原因很实际PyTorch 的动态图机制对调试更友好print一下 tensor 的 shape 就能定位问题而静态图框架报错信息往往让人摸不着头脑。网络结构方面垃圾分类这种四分类任务用 ResNet18 或 MobileNetV2 做迁移学习是最稳妥的选择。ResNet18 参数量约 1100 万在 ImageNet 上预训练过的权重拿来微调通常 20 到 30 个 epoch 就能收敛到一个不错的精度。MobileNetV2 更轻量适合你想部署到边缘设备或者对推理速度有要求的场景。选型理由可以归结为三点第一数据量通常不大从零训练一个深层网络必然过拟合迁移学习是正解第二垃圾分类的类间差异比较明显不需要特别复杂的网络结构第三ResNet 的残差连接能有效缓解梯度消失训练过程更稳定。如果你在论文里需要对比实验可以再加一个 VGG16 或者自己搭一个简单的四层 CNN 作为 baseline这样论文的实验部分会更充实。2.3 环境配置与依赖安装的实操步骤环境配置是新手翻车最多的地方。我建议用 conda 建一个独立环境避免和系统 Python 打架# 创建虚拟环境指定 Python 3.8 或 3.9 conda create -n garbage_cls python3.9 -y conda activate garbage_cls # 安装 PyTorch根据你的 CUDA 版本选择对应命令 # 如果没有 GPU用 CPU 版本即可 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装其他依赖 pip install numpy pandas matplotlib opencv-python pillow tqdm scikit-learn这里有几个参数需要留意python3.9是我实测兼容性最好的版本3.10 以上有时会遇到某些库还没适配的问题PyTorch 的安装命令要根据你的显卡驱动来选cu118对应 CUDA 11.8如果你不确定直接去 PyTorch 官网用它的选择器生成命令最保险。安装完之后跑一句python -c import torch; print(torch.cuda.is_available())返回True说明 GPU 可用返回False就是 CPU 模式训练速度会慢很多但代码本身不用改。提示如果你用的是 VSCode记得在右下角切换 Python 解释器到刚创建的 conda 环境否则终端里装好了、编辑器里还是找不到包。3. 数据管道与模型训练从图片到可用的分类器3.1 数据预处理与增强策略垃圾分类数据集通常存在两个问题类别不平衡和样本量不足。源码里一般会用torchvision.transforms做在线增强常见组合是随机裁剪、水平翻转、颜色抖动。我一般会在这个基础上再加一个随机旋转因为垃圾图片的拍摄角度往往很随意。下面是一段典型的数据加载代码import torch from torchvision import transforms, datasets from torch.utils.data import DataLoader, WeightedRandomSampler # 训练集增强随机裁剪 翻转 旋转 颜色抖动 train_transform transforms.Compose([ transforms.RandomResizedCrop(224, scale(0.7, 1.0)), # 随机裁剪并缩放到224 transforms.RandomHorizontalFlip(p0.5), # 50%概率水平翻转 transforms.RandomRotation(15), # 随机旋转±15度 transforms.ColorJitter(brightness0.2, contrast0.2), # 亮度对比度扰动 transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], # ImageNet标准均值 std[0.229, 0.224, 0.225]) # ImageNet标准方差 ]) # 验证集只做缩放和归一化不做增强 val_transform 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]) ]) # 加载数据集ImageFolder要求按类别分文件夹 train_dataset datasets.ImageFolder(data/train, transformtrain_transform) val_dataset datasets.ImageFolder(data/val, transformval_transform) # 处理类别不平衡给少数类更高采样权重 targets [s[1] for s in train_dataset.samples] class_counts torch.bincount(torch.tensor(targets)) weights 1.0 / class_counts.float() sample_weights weights[targets] sampler WeightedRandomSampler(sample_weights, num_sampleslen(sample_weights), replacementTrue) train_loader DataLoader(train_dataset, batch_size32, samplersampler, num_workers4) val_loader DataLoader(val_dataset, batch_size32, shuffleFalse, num_workers4)这段代码的关键点有三个RandomResizedCrop的scale参数控制裁剪区域占原图的比例设太小会把垃圾物体裁掉设太大增强效果不明显0.7 到 1.0 是比较安全的范围Normalize用的均值和方差必须和预训练模型一致否则迁移学习的效果会打折扣WeightedRandomSampler是解决类别不平衡的利器它让少数类样本在每批中被抽到的概率更高比简单的过采样更不容易过拟合。num_workers设成 4 还是 8 取决于你机器的 CPU 核心数设太大反而会因为进程切换拖慢速度。3.2 模型构建与迁移学习微调模型部分的核心思路是加载预训练权重替换最后的全连接层然后分阶段微调。源码里可能直接写死了 ResNet18但你可以根据自己需求换成 ResNet50 或 EfficientNet。下面是我常用的写法import torch.nn as nn from torchvision import models def build_model(num_classes4, backboneresnet18, pretrainedTrue): if backbone resnet18: model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1 if pretrained else None) in_features model.fc.in_features # 替换最后的全连接层输出类别数改为4 model.fc nn.Sequential( nn.Dropout(0.3), # 防止过拟合 nn.Linear(in_features, num_classes) ) elif backbone mobilenet_v2: model models.mobilenet_v2(weightsmodels.MobileNet_V2_Weights.IMAGENET1K_V1 if pretrained else None) in_features model.classifier[1].in_features model.classifier nn.Sequential( nn.Dropout(0.3), nn.Linear(in_features, num_classes) ) return model # 分阶段微调先冻结骨干网络只训练分类头 model build_model(num_classes4, backboneresnet18) for param in model.parameters(): param.requires_grad False for param in model.fc.parameters(): param.requires_grad True # 第一阶段优化器只更新分类头参数 optimizer torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr1e-3)这里的分阶段策略值得展开说第一阶段冻结骨干网络只训练新加的全连接层学习率可以设大一点1e-3因为随机初始化的分类头需要快速收敛训练 5 个 epoch 之后解冻骨干网络的后几层用更小的学习率1e-4 或 1e-5做微调避免把预训练学到的通用特征破坏掉。Dropout(0.3)是防止过拟合的常规操作如果你的数据集特别小每类不到 200 张可以加到 0.5。损失函数用CrossEntropyLoss就够了如果类别不平衡特别严重可以给它传weight参数和上面的采样器二选一即可不用同时上。3.3 训练循环与关键参数设置训练循环的代码看起来都差不多但魔鬼在细节里。下面这段是我会实际用的版本import torch from tqdm import tqdm device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) criterion nn.CrossEntropyLoss() scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max30) best_acc 0.0 for epoch in range(30): model.train() running_loss 0.0 for images, labels in tqdm(train_loader, descfEpoch {epoch1}): 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, total 0, 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(fBest model saved with acc: {best_acc:.4f})几个参数需要根据实际情况调整batch_size设 32 是 8GB 显存下的安全值显存够大可以上 64学习率调度器用余弦退火比固定学习率效果好T_max设成总 epoch 数保存模型时只存state_dict而不是整个模型这样加载时更灵活。如果你发现训练 loss 一直在降但验证 acc 不涨大概率是过拟合了可以加数据增强、加 Dropout、或者提前停止。反过来如果训练 loss 都不降先检查学习率是不是太大或者数据标签有没有搞错。4. 推理部署与效果验证模型训完之后怎么用4.1 单张图片推理与批量预测训练完拿到best_model.pth之后下一步就是写推理脚本。很多人训完模型就不知道怎么办了其实推理代码比训练简单得多from PIL import Image import torch from torchvision import transforms # 加载模型结构再加载权重 model build_model(num_classes4, backboneresnet18, pretrainedFalse) model.load_state_dict(torch.load(best_model.pth, map_locationcpu)) model.eval() # 推理预处理和验证集保持一致 infer_transform 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]) ]) class_names [可回收物, 厨余垃圾, 有害垃圾, 其他垃圾] def predict(image_path): image Image.open(image_path).convert(RGB) tensor infer_transform(image).unsqueeze(0) # 增加batch维度 with torch.no_grad(): outputs model(tensor) probabilities torch.softmax(outputs, dim1) confidence, predicted torch.max(probabilities, 1) return class_names[predicted.item()], confidence.item() # 测试 label, conf predict(test.jpg) print(f预测类别: {label}, 置信度: {conf:.4f})这里有个容易忽略的点load_state_dict之前必须先实例化模型结构而且pretrained要设成False否则会去下载预训练权重浪费时间。map_locationcpu是防止在没有 GPU 的机器上加载时报错。推理时的预处理必须和验证集完全一致包括Resize的大小和Normalize的参数差一点都会导致精度下降。torch.softmax把输出转成概率分布置信度低于 0.6 的时候我一般会提示“结果不确定”这在演示系统里很实用。4.2 评估指标与混淆矩阵只看准确率是不够的尤其是类别不平衡的时候。我习惯在验证集上跑一遍完整的评估输出分类报告和混淆矩阵from sklearn.metrics import classification_report, confusion_matrix import seaborn as sns import matplotlib.pyplot as plt model.eval() all_preds, all_labels [], [] with torch.no_grad(): for images, labels in val_loader: images images.to(device) outputs model(images) _, predicted torch.max(outputs, 1) all_preds.extend(predicted.cpu().numpy()) all_labels.extend(labels.numpy()) # 打印每个类别的精确率、召回率、F1 print(classification_report(all_labels, all_preds, target_namesclass_names)) # 画混淆矩阵 cm confusion_matrix(all_labels, all_preds) sns.heatmap(cm, annotTrue, fmtd, xticklabelsclass_names, yticklabelsclass_names) plt.xlabel(预测) plt.ylabel(真实) plt.savefig(confusion_matrix.png, dpi150, bbox_inchestight)classification_report会给出每个类别的 precision、recall 和 f1-score如果某个类别的 recall 特别低说明模型把很多该类样本漏判了可能是样本太少或者特征不明显。混淆矩阵能直观看出模型把哪两类搞混了比如“厨余垃圾”和“其他垃圾”经常互相误判这时候可以考虑针对这两类补充训练数据或者加一个二分类器做后处理。这些分析写进论文里比单纯报一个准确率有说服力得多。4.3 用 Grad-CAM 做可视化解释答辩的时候老师经常会问“模型到底学到了什么”这时候 Grad-CAM 热力图就是你的后悔药。它能把模型关注区域高亮出来让你看到模型是根据哪些像素做判断的from pytorch_grad_cam import GradCAM from pytorch_grad_cam.utils.image import show_cam_on_image import numpy as np import cv2 # 选择目标层ResNet18的最后一个卷积层 target_layers [model.layer4[-1]] cam GradCAM(modelmodel, target_layerstarget_layers) # 准备输入 rgb_img cv2.imread(test.jpg) rgb_img cv2.resize(rgb_img, (224, 224)) input_tensor infer_transform(Image.fromarray(cv2.cvtColor(rgb_img, cv2.COLOR_BGR2RGB))).unsqueeze(0) # 生成热力图 grayscale_cam cam(input_tensorinput_tensor)[0, :] visualization show_cam_on_image(rgb_img / 255.0, grayscale_cam, use_rgbTrue) cv2.imwrite(gradcam_result.jpg, cv2.cvtColor(visualization, cv2.COLOR_RGB2BGR))target_layers选最后一层卷积是因为它保留了最多的空间信息选太浅的层热力图会很散。生成的热力图叠加在原图上红色区域就是模型最关注的地方。如果模型关注的是垃圾物体本身说明学对了如果关注的是背景或者水印那就要检查数据集里有没有引入偏差。这个工具在论文里放一张图比写一堆文字解释都管用。5. 避坑与常见问题排查5.1 训练 loss 不下降或震荡严重现象训练几个 epoch 后 loss 一直在 2.0 附近徘徊或者上下剧烈跳动。原因通常是学习率设太大了或者数据标签有问题。先检查学习率迁移学习第一阶段用 1e-3 是上限如果 loss 炸了降到 1e-4 试试。然后抽查一批数据把图片和标签打印出来看看是否对应我遇到过有人把类别文件夹名字写错导致标签全乱的。解决用torch.optim.lr_scheduler.ReduceLROnPlateau做自适应降学习率同时写一个check_dataset.py脚本随机可视化 16 张图确认标签。5.2 验证集准确率远低于训练集现象训练集准确率 95%验证集只有 60%。这是典型的过拟合。原因可能是数据增强不够、模型参数量太大、或者训练集和验证集分布不一致。解决先加数据增强RandomResizedCrop、ColorJitter、RandomRotation都加上然后加 Dropout 和权重衰减weight_decay1e-4如果还不行换更小的模型比如从 ResNet50 换回 ResNet18。另外检查一下验证集的图片是不是从训练集里漏过来的虽然听起来离谱但我确实见过。5.3 GPU 显存不足报 CUDA out of memory现象训练到一半突然报RuntimeError: CUDA out of memory。原因可能是 batch_size 太大或者没有释放中间变量。解决先把batch_size减半从 32 降到 16然后在训练循环里加torch.cuda.empty_cache()虽然它不能完全解决问题但能缓解碎片化如果还不行用torch.cuda.amp做混合精度训练显存占用能降差不多一半。代码改动很小在loss.backward()前加一个scaler.scale(loss).backward()就行。5.4 推理时预测结果全是同一类现象不管输入什么图片模型都输出“其他垃圾”。原因通常是加载权重时没有正确匹配或者推理预处理和训练时不一致。解决先确认load_state_dict没有报错如果有unexpected keys说明模型结构对不上然后检查推理的Normalize参数是不是和训练时一样最后用训练集里的一张图做推理如果训练集图片都预测错那基本是权重加载的问题。我一般会在推理脚本开头加一句print(model.fc)确认分类头结构。5.5 数据加载速度慢导致 GPU 利用率低现象nvidia-smi显示 GPU 利用率只有 20% 到 30%训练一个 epoch 要很久。原因通常是num_workers设太小或者图片尺寸太大。解决把num_workers设成 CPU 核心数的一半左右比如 8 核 CPU 设 4如果图片原始分辨率是 4000x3000先在预处理阶段统一缩放到 256x256 再存一份能大幅减少 IO 时间。另外把数据放在 SSD 上而不是机械硬盘这个提升立竿见影。6. 把项目改出花从及格到优秀的三个进阶技巧第一个技巧是换 backbone 做对比实验。毕业设计如果只用一个 ResNet18实验部分会显得单薄。你可以把build_model函数扩展一下支持efficientnet_b0、resnet50、mobilenet_v2三种然后在同一份数据上跑对比用表格呈现准确率、参数量、推理时间三个指标。这样论文的第四章就有了扎实的内容。具体做法是在torchvision.models里找对应的类替换分类头的代码逻辑是一样的注意efficientnet的分类头叫classifierresnet叫fc改的时候别搞混。第二个技巧是加一个简单的 Web 演示界面。用 Gradio 或者 Streamlit 十几行代码就能搭起来答辩的时候现场上传图片、实时显示分类结果和置信度比放 PPT 截图直观得多。Gradio 的代码大概长这样import gradio as gr def classify_image(image): label, conf predict(image) return f{label} (置信度: {conf:.2%}) interface gr.Interface( fnclassify_image, inputsgr.Image(typefilepath), outputstext, title垃圾分类识别系统 ) interface.launch(shareFalse)shareFalse表示只在本地局域网访问答辩时用自己电脑跑就行。这个界面不需要任何前端知识gr.Image自动处理上传和格式转换fn接收文件路径返回字符串。第三个技巧是导出 ONNX 模型做推理加速。PyTorch 模型在 CPU 上推理有时候比较慢转成 ONNX 之后可以用 ONNX Runtime 加速在同样的硬件上通常能快 1.5 到 2 倍。导出代码import torch.onnx dummy_input torch.randn(1, 3, 224, 224).to(device) torch.onnx.export( model, dummy_input, garbage_cls.onnx, input_names[input], output_names[output], dynamic_axes{input: {0: batch_size}, output: {0: batch_size}}, opset_version11 )dynamic_axes让导出的模型支持变长 batchopset_version11兼容性最好。导出之后用onnxruntime加载推理速度提升明显而且部署到没有 PyTorch 环境的机器上也能跑。这三个技巧我每次做图像分类项目都会走一遍尤其是对比实验和可视化属于投入产出比最高的部分。从那以后我每次拿到一个新的分类项目都强制自己先把 baseline 跑通、再逐步加改进绝不一上来就堆模块。希望帮到你。本文还有配套的精品资源点击获取
返回列表