简介:一份基于深度学习的垃圾分类项目压缩包,面向毕业设计、期末大作业及机器学习入门者,提供从数据处理、模型定义到应用部署的完整参考方案。项目以 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_SIZE=224是 ImageNet 预训练模型惯用输入尺寸;BATCH_SIZE=32在 8GB 显存下跑 mobilenet 比较稳,resnet50 的话可能要降到 16;NUM_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, transform=None): 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(pretrained=True) 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(), lr=LR, weight_decay=1e-4) scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=EPOCHS) 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 每轮调整学习率,让后期训练更稳。
参数说明:
pretrained=True用 ImageNet 权重做初始化,收敛速度远快于随机初始化。model.last_channel是 mobilenet_v2 的倒数第二层输出维度,替换分类头时必须用它,不能写成 1280 这种魔法数字,否则换模型时忘记同步改就报维度错。AdamW比 Adam 多了正确的权重衰减实现,配合weight_decay=1e-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_weight="balanced", classes=np.unique(labels), y=np.array(labels) ) class_weights = torch.tensor(class_weights, dtype=torch.float32, device=DEVICE) criterion = torch.nn.CrossEntropyLoss(weight=class_weights)逻辑说明:compute_class_weight按样本数反比计算出每类权重,样本少的类别权重高,样本多的权重低。CrossEntropyLoss 在算梯度时乘以该权重,相当于把少数类的每个样本重要性放大。
参数说明:balanced模式的计算公式是总样本数除以(类别数乘以该类样本数),不需要手写;classes参数必须传唯一的标签数组,不能传原始列表。权重最终要搬到与模型相同的设备上,否则 loss 计算时 tensor 跨设备会报错。
加了类别权重之后,整体准确率可能略有下降,但有害垃圾的召回率会明显上来。做垃圾分类项目要看的核心指标不是总 acc,而是每个类别的精确率和召回率,尤其是有害垃圾。只看总分,容易被数据分布骗了。
5. 垃圾分类项目落地的常见坑与排查:解压到推理全流程
5.1 zip 解压后中文目录名导致 ImageFolder 读取失败
现象:Windows 上解压后,data/train/可回收/下面图片一读取就报FileNotFoundError,或者图片找到了但路径显示乱码。
原因:zip 压缩包里的中文文件名编码通常由打包机器的系统编码决定(常见 GBK 或 UTF-8),Windows 自带解压工具和 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_size=BATCH_SIZE, shuffle=True, num_workers=2, pin_memory=True, persistent_workers=True, )逻辑说明:pin_memory=True让数据加载到锁页内存,能加快 GPU 拷贝;persistent_workers=True避免每个 epoch 重复创建 worker 进程,但该参数在 Windows 上偶尔会引发多进程报错,遇到就删掉。
参数说明:num_workers=2在双核机器和主流服务器上都是比较稳的起点,开太高会让数据加载变成瓶颈反而不快。shuffle=True只能用于训练集,验证集必须设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_version=11, dynamic_axes={"input": {0: "batch"}, "output": {0: "batch"}}, )逻辑说明:model.eval()关闭 dropout 和 batch norm 的训练行为,保证导出的图是推理时真正执行的路径。dynamic_axes允许推理时 batch 维不固定,方便单张图片按 1 传入。
参数说明:opset_version=11兼容性较好;动态 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 跑起来。早期我也犯过只盯着训练集准确率、拿到压缩包不先看数据分布就开训的毛病,后来被验证集准确率狠狠教育了一回。实践下来,这类项目的成败关键,七成在数据组织,三成在模型参数。希望这份拆解能帮你少走几趟弯路,把时间花在真正影响结果的地方。
本文还有配套的精品资源,点击获取