恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
基于深度学习的垃圾分类项目实战:从解压到部署全流程
首页
资讯中心
/
基于深度学习的垃圾分类项目实战:从解压到部署全流程
基于深度学习的垃圾分类项目实战:从解压到部署全流程
发布时间:2026/10/5 12:31:03
简介一份基于深度学习的垃圾分类项目压缩包面向毕业设计、期末大作业及机器学习入门者提供从数据处理、模型定义到应用部署的完整参考方案。项目以 Python 为主要开发语言可配合 TensorFlow 或 PyTorch 等框架复现训练与推理流程。压缩包内含 43 个文件主要由 Python 脚本、PyCharm 配置、Jupyter Notebook 和说明文档组成其中模型脚本覆盖 SqueezeNet、ResNet 等常用网络结构预处理模块负责图像缩放与归一化工具函数则用于日志、评估和 JSON 解析便于二次开发与调试。包体仅 55KB轻量精炼适合快速查阅代码结构。目前已有 45 人学习下载。通过该资源可以了解垃圾分类数据集的标注格式、模型训练流程、界面交互逻辑及推理输出方式同时也能参考 .gitignore、readme 等文件学习项目规范化管理。对于准备课程设计或入门深度学习图像分类的读者是一份实用且易上手的参考资料。1. 基于深度学习的垃圾分类先拆开这个zip看它到底值不值拿到“基于深度学习的垃圾分类.zip”这类压缩包第一反应别急着双击解压。这个标题背后是一个典型的图像分类落地项目训练脚本、数据集组织、模型文件和推理入口全塞在一个压缩包里。它的核心价值在“分类”两个字——输入一张垃圾照片输出它属于可回收、厨余、有害还是其他垃圾。适合有 PyTorch 基础、想把深度学习模型跑通并部署到边缘设备的工程师和学生。前提是你能把 zip 里的环境依赖、数据路径和类别映射理顺否则大概率会在训练环节翻车。2. 解压与项目结构梳理先复现demo再碰训练2.1 两步解压先给文件体检拿到 zip 后我一般会先在 Linux 或 macOS 下用unzip解压而不是直接双击。因为项目里常带 shell 脚本和 Python 代码文件名可能包含中文或特殊字符图形化解压工具容易把编码弄坏导致后续 import 路径报错。mkdir -p garbage_classify cd garbage_classify unzip ../基于深度学习的垃圾分类.zip -d . find . -maxdepth 2 -type d | head -20逻辑说明先把 zip 解压到独立的项目目录再用find看两层以内的目录结构。这样能快速确认压缩包是不是套了一层同名文件夹。如果是“垃圾分类/垃圾分类/...”这种嵌套训练脚本里写死的相对路径会全部失效。参数说明-d .指定解压目标目录maxdepth 2限制find的递归深度避免把模型权重文件全列出来刷屏。解压后如果看到.py文件和data/目录平级说明包结构正常。解压后先看依赖文件。常见做法是项目根目录放一个requirements.txt或environment.yml里面锁定了 numpy、torch、torchvision、opencv-python 这些版本。我建议用虚拟环境安装不要直接装进系统 Python避免和别的项目互相污染。python -m venv venv source venv/bin/activate pip install --upgrade pip pip install -r requirements.txt逻辑说明创建虚拟环境后所有依赖都装进venv/。后续即使项目里某个包版本号和本机已有包冲突也不会影响其他工作目录。参数说明venv是虚拟环境目录名可改成.venv以便在 shell 提示符中隐藏--upgrade pip先把 pip 升到较新版本因为老版本 pip 解析复杂依赖时容易报怪错。Windows 上source venv/bin/activate要换成venv\Scripts\activate。2.2 项目目录里每类文件是干什么的一个规范的垃圾分类项目解压后大致是这种结构路径作用data/train/训练集图片按类别分子目录data/val/验证集图片目录名与训练集一致train.py训练入口包含模型构建、loss、优化器model.py模型定义通常是 resnet/mobilenet 的封装infer.py推理脚本输入单张图片输出类别config.py超参数和路径配置集中管理models/训练产出的权重文件目录这里最值得先看的是config.py和train.py头部。很多项目把类别列表、图片尺寸、epoch 数、学习率都散落在各个文件里集中配置的话改起来省事。如果压缩包里直接带着.pth权重先跑python infer.py --image 测试图.jpg把 demo 复现出来再决定要不要重新训练。# config.py 典型内容 NUM_CLASSES 4 IMG_SIZE 224 BATCH_SIZE 32 EPOCHS 30 LR 1e-4 CLASS_NAMES [可回收, 厨余, 有害, 其他] DEVICE cuda if torch.cuda.is_available() else cpu逻辑说明把类别名、图片尺寸、批大小、学习率集中定义训练和推理两个脚本同时引用这份配置避免出现训练用 224、推理用 256 这种隐蔽的不一致。参数说明IMG_SIZE224是 ImageNet 预训练模型惯用输入尺寸BATCH_SIZE32在 8GB 显存下跑 mobilenet 比较稳resnet50 的话可能要降到 16NUM_CLASSES必须和data/train/下的子目录数量一致这是新手最容易栽的地方。3. 数据组织与标签映射垃圾分类数据集的坑比模型更大3.1 从“可回收/厨余/有害/其他”到 one-hot 标签垃圾分类本质是图像分类任务模型输出的是一个 4 维概率向量。但实际拿到的数据往往是“某张图片对应某个中文类别名”中间需要一次映射。常见做法是手写一个字典# label_map.py label_map { 可回收: 0, 厨余: 1, 有害: 2, 其他: 3, } id_to_label {v: k for k, v in label_map.items()}逻辑说明训练时图片目录名是中文或英文标签通过label_map转成 0/1/2/3 的整数推理时把模型输出概率最大的下标通过id_to_label再翻译回中文。参数说明这个映射必须和config.py里的CLASS_NAMES顺序完全一致。如果训练脚本里写的是[其他, 有害, 可回收, 厨余]而label_map里是另一套顺序模型会一直学错准确率还看不出明显异常——这类错位是垃圾分类项目里最难受的隐性 bug。我见过不少项目直接拿 ImageFolder 按字母序生成标签。中文目录名在 Windows 上按 Unicode 排序在 Linux 上按字节排序两套顺序可能都不一样。所以不要依赖自动排序必须显式指定映射。垃圾分类数据集另一个特点是“图片质量参差”。用手机拍的厨余垃圾往往有大量背景干扰、光线不均有的类别样本只有几百张有的上万张。数据层面的坑比模型选型的坑更容易决定最终效果。3.2 用 PyTorch Dataset 加载本地图片路径和缓存都别偷懒如果项目没有直接给 Dataset 实现我习惯自己写一份。核心要做两件事把图片路径和标签配对再在__getitem__里做解码和增广。import torch from torch.utils.data import Dataset from PIL import Image import os class GarbageDataset(Dataset): def __init__(self, root_dir, transformNone): self.samples [] for label_name, label_id in label_map.items(): class_dir os.path.join(root_dir, label_name) for fname in os.listdir(class_dir): if fname.lower().endswith((.jpg, .jpeg, .png)): self.samples.append((os.path.join(class_dir, fname), label_id)) self.transform transform def __len__(self): return len(self.samples) def __getitem__(self, idx): path, label self.samples[idx] image Image.open(path).convert(RGB) if self.transform: image self.transform(image) return image, label逻辑说明__init__阶段扫描整个目录把每张图片的绝对路径和整数标签存进列表一次扫完训练时不再访问文件系统目录。__getitem__按索引读取图片、转成 RGB、做增广变换。参数说明convert(RGB)强制统一成三通道避免出现灰度图或带透明通道的 PNG 导致 tensor 形状不一致endswith过滤只保留常见图片格式避免.txt或系统隐藏文件混进来。transform参数由训练脚本构造一般包含 Resize、RandomCrop、RandomHorizontalFlip。随机增广要谨慎。垃圾分类图片里有些类别方向敏感度很低比如电池有些类别其实敏感比如按形状辨认的玻璃瓶倒过来后语义没有变化但训练分布变了。我一般只用RandomHorizontalFlip和ColorJitter不做RandomRotation避免把“有害”标志的朝向搞乱导致模型特征学习偏移。4. 训练脚本拆解mobilenet 还是 resnet参数怎么设4.1 模型选型理由先看算力再看准确率如果 zip 里的项目默认用 resnet通常是因为在 GPU 上跑很顺手。resnet50 在 ImageNet 上预训练充分作为迁移学习起点非常稳。但如果目标是落地到 Jetson 或树莓派这种边缘设备mobilenet v3 或 mobilenet v2 才是合理选择。参数量差距很直观模型参数量单张 224×224 推理相对耗时适合场景resnet50约 25.6M中服务器训练追求准确率mobilenet v2约 3.5M快边缘设备部署mobilenet v3 small约 2.5M更快低功耗设备选型不是越大的模型越好。垃圾分类总共 4 类或几十类类别数是固定的输入图是日常照片不需要像 ImageNet 1000 类那样大的容量。我做实际项目时先用 mobilenet 做 baseline如果验证集准确率不达标再换 resnet 看增益。反过来先跑 resnet 再压缩往往费时费力。4.2 训练主循环与 5 个必调参数# train.py 核心片段 from torchvision import models from torch.utils.data import DataLoader model models.mobilenet_v2(pretrainedTrue) model.classifier[1] torch.nn.Linear(model.last_channel, NUM_CLASSES) model model.to(DEVICE) criterion torch.nn.CrossEntropyLoss() optimizer torch.optim.AdamW(model.parameters(), lrLR, weight_decay1e-4) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_maxEPOCHS) for epoch in range(EPOCHS): model.train() for images, labels in train_loader: images, labels images.to(DEVICE), labels.to(DEVICE) outputs model(images) loss criterion(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() scheduler.step()逻辑说明用 torchvision 加载在 ImageNet 上预训练过的 mobilenet_v2把最后分类层替换成自己类别数的输出。训练循环是标准模式前向、算损失、清空梯度、反向、更新参数。scheduler 每轮调整学习率让后期训练更稳。参数说明pretrainedTrue用 ImageNet 权重做初始化收敛速度远快于随机初始化。model.last_channel是 mobilenet_v2 的倒数第二层输出维度替换分类头时必须用它不能写成 1280 这种魔法数字否则换模型时忘记同步改就报维度错。AdamW比 Adam 多了正确的权重衰减实现配合weight_decay1e-4能减少过拟合。CosineAnnealingLR在训练后期自动降低学习率比固定学习率更容易收到一个平缓收敛的尾巴。BATCH_SIZE设为 32 在 8GB 显存下跑 mobilenet 比较稳如果报 CUDA out of memory降到 16 或 8。训练时我习惯每跑完一个 epoch 打印一次 train loss 和 val acc不要等到全部训完才看。loss 一直在降但验证集准确率不动十有八九是过拟合早停比换模型更有效。4.3 类别不均衡loss 里加一个权重就够了真实垃圾分类数据集中“其他垃圾”往往样本特别多“有害垃圾”特别少。如果不处理模型会学成“全都预测其他垃圾”整体准确率还挺高但有害垃圾的实际召回率几乎为 0。解决手段很简单统计每类样本数给少数类更大的 loss 权重。from sklearn.utils.class_weight import compute_class_weight import numpy as np labels [label for _, label in train_dataset.samples] class_weights compute_class_weight( class_weightbalanced, classesnp.unique(labels), ynp.array(labels) ) class_weights torch.tensor(class_weights, dtypetorch.float32, deviceDEVICE) criterion torch.nn.CrossEntropyLoss(weightclass_weights)逻辑说明compute_class_weight按样本数反比计算出每类权重样本少的类别权重高样本多的权重低。CrossEntropyLoss 在算梯度时乘以该权重相当于把少数类的每个样本重要性放大。参数说明balanced模式的计算公式是总样本数除以类别数乘以该类样本数不需要手写classes参数必须传唯一的标签数组不能传原始列表。权重最终要搬到与模型相同的设备上否则 loss 计算时 tensor 跨设备会报错。加了类别权重之后整体准确率可能略有下降但有害垃圾的召回率会明显上来。做垃圾分类项目要看的核心指标不是总 acc而是每个类别的精确率和召回率尤其是有害垃圾。只看总分容易被数据分布骗了。5. 垃圾分类项目落地的常见坑与排查解压到推理全流程5.1 zip 解压后中文目录名导致 ImageFolder 读取失败现象Windows 上解压后data/train/可回收/下面图片一读取就报FileNotFoundError或者图片找到了但路径显示乱码。原因zip 压缩包里的中文文件名编码通常由打包机器的系统编码决定常见 GBK 或 UTF-8Windows 自带解压工具和 Python 的zipfile模块对非 UTF-8 文件名处理不一致导致解压出来的目录名看着正常实际字节已经损坏。解决在 Python 里重新解压并显式指定编码或者干脆把数据集目录全部改成拼音或英文名只保留label_map里的中文显示名。我一般选后者一劳永逸cd data/train mv 可回收 recyclable mv 厨余 kitchen_waste mv 有害 hazardous mv 其他 other这样目录名全是 ASCII无论在哪台机器解压都不会再出现编码问题。对应地把label_map和CLASS_NAMES同步改成英文 key推理展示时再转回中文。5.2 类别目录名与代码硬编码不一致报错 KeyError现象train.py一跑起来就KeyError: 可回收或者label_map定义的键不在目录列表里。原因数据目录是后来补的图片目录名和代码里写死的类别名有一个字对不上比如“厨余”和“厨余垃圾”或者打包时把类别目录嵌套了一层os.listdir拿到的是子文件夹名而不是图片文件。解决优先让数据迁就代码。写一段校验脚本打印出label_map和实际目录之间的差集import os for label_name in label_map.keys(): path os.path.join(data/train, label_name) if not os.path.isdir(path): print(缺失目录:, label_name)逻辑说明脚本遍历映射字典中的每个类别名检查训练目录下是否存在同名子目录。如果少了某个类别说明数据目录和代码不一致。参数说明os.path.isdir只能判断目录不能用文件路径替代。检查完目录后还要确认每个类别目录里至少有一张图片否则DataLoader在某个 epoch 采样时仍会报空。5.3 训练时内存/显存不足batch_size、workers、混合精度现象训练开始没几个 step 就报CUDA out of memory或者 CPU 直接卡死、内存被吃满。原因显存不足通常是 batch_size 太大或输入图片尺寸超出了IMG_SIZE设定内存吃满多半是DataLoader的num_workers开太高或者有缓存机制一次性把全部图片读进内存。解决先减小 batch_size 到 8 或 16把IMG_SIZE从 224 降到 192 实测一次确认能跑通再加回去。num_workers一般从 4 开始调显存和内存都不宽裕的机器设成 2。train_loader DataLoader( train_dataset, batch_sizeBATCH_SIZE, shuffleTrue, num_workers2, pin_memoryTrue, persistent_workersTrue, )逻辑说明pin_memoryTrue让数据加载到锁页内存能加快 GPU 拷贝persistent_workersTrue避免每个 epoch 重复创建 worker 进程但该参数在 Windows 上偶尔会引发多进程报错遇到就删掉。参数说明num_workers2在双核机器和主流服务器上都是比较稳的起点开太高会让数据加载变成瓶颈反而不快。shuffleTrue只能用于训练集验证集必须设False。如果换了 mobilenet 还爆显存检查代码里是不是把验证集也放进了训练流程或者模型输出了所有中间层激活比如为了可视化 Grad-CAM 把 hook 一直挂着这类代码问题比硬件问题更常见。5.4 部署到低算力设备时模型转换失败现象用torch.jit.trace或onnx.export导出模型时报Unsupported operator或推理结果全错。原因训练脚本里用了动态控制流比如if batch_size 1分支trace 阶段没法正确跟踪或者模型里有 Resize、插值操作与导出格式不兼容。解决导出前把模型切到 eval 模式用固定尺寸的 dummy input 做 trace。更稳妥的做法是先导出 ONNX再用 ONNX Runtime 推理整体流程更清晰import torch.onnx model.eval() dummy torch.randn(1, 3, 224, 224).to(DEVICE) torch.onnx.export( model, dummy, garbage_classify.onnx, input_names[input], output_names[output], opset_version11, dynamic_axes{input: {0: batch}, output: {0: batch}}, )逻辑说明model.eval()关闭 dropout 和 batch norm 的训练行为保证导出的图是推理时真正执行的路径。dynamic_axes允许推理时 batch 维不固定方便单张图片按 1 传入。参数说明opset_version11兼容性较好动态 batch 设置后推理端要按[N, 3, 224, 224]的输入形状组装 tensor。如果目标设备只支持静态输入就删掉dynamic_axes固定成[1, 3, 224, 224]。6. 进阶把模型导出成 ONNX 再量化让垃圾分拣跑在边缘盒子上训练完权重只是第一步。如果最终要部署到 Jetson Nano、RK3588 或树莓派这类边缘设备我一般会把模型先转成 ONNX再做 INT8 量化体积能压到原来的四分之一推理速度提升明显。这里有一个特别实用的技巧不是导出之后才量化而是先做 Post-Training Quantization 校准。校准需要一小批有代表性的真实图片不能随便拿几十张高斯噪声凑数。垃圾分类场景下校准集最好覆盖四个类别每类挑 5 到 10 张确保缩放因子覆盖到各种亮度。用 onnxruntime 的量化工具包时特别留意opset_version和量化算子是否被目标设备的推理引擎支持。我踩过的最深一个坑是把量化后的模型发到设备上发现输出概率全是同一个值排查半天才发现是校准图片没有做和训练时一致的归一化亮度分布偏移导致激活值全部落入量化的死区。现在我做这套项目会先把transform里的Normalize(mean, std)参数原封不动带到推理前处理脚本再设计一个“端到端一致性检查”拿同一张图原始 PyTorch 模型的 top-1 类别和 ONNX 量化模型的 top-1 类别必须一致不一致就回退到 FP16 或找算子的兼容性问题不强行压 INT8。按这个套路从 zip 解压到部署我基本能在半天内把一个垃圾分类 demo 跑起来。早期我也犯过只盯着训练集准确率、拿到压缩包不先看数据分布就开训的毛病后来被验证集准确率狠狠教育了一回。实践下来这类项目的成败关键七成在数据组织三成在模型参数。希望这份拆解能帮你少走几趟弯路把时间花在真正影响结果的地方。本文还有配套的精品资源点击获取