尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

基于卷积神经网络的垃圾分类系统:从数据预处理到预测部署的完整实践

发布时间:2026/9/28 20:12:45

资讯中心
01
ARTICLE

基于卷积神经网络的垃圾分类系统:从数据预处理到预测部署的完整实践

基于卷积神经网络的垃圾分类系统:从数据预处理到预测部署的完整实践
简介这是一份面向计算机专业毕业设计及项目实战学习的Python垃圾分类系统资源基于卷积神经网络实现图像识别与分类适合正在做毕设、课程设计或期末大作业的学生也适合需要完整项目练手的学习者内容经助教老师审定难度适中。压缩包为zip格式、大小约5.1MB主要包含可运行的Python源码和论文PDF源码经过本地编译与严格调试下载后可直接运行论文部分可用于撰写设计说明、梳理模型结构与实验过程。资源目前已有156人学习下载。项目覆盖垃圾分类识别的完整流程评审分为98分属于导师认可的高分设计可帮助读者对照完整方案理解CNN在图像分类任务中的实际应用同时减少从零搭建模型和排错的时间。1. 基于卷积神经网络的垃圾分类系统毕设选题不踩雷的一套完整源码每年毕业季都能看到一批人抱着计算机视觉方向冲进垃圾分类的选题但真正能从头到尾跑通、又符合毕设要求深度的并不多。这套基于卷积神经网络的垃圾分类系统源码加论文PDF是我拆过比较顺手的资源之一PyTorch或者Keras的代码结构都给你理好了数据预处理、模型训练、预测展示一条线走完评审分98分的底子在哪跑一遍就能有体感。如果你是计算机相关专业的应届生或者想拿项目实战练手、做课程设计和期末大作业这套资源的适合度比较高——难度中等偏上一点点但代码全部本地编译可运行几乎不用二次返工。这篇笔记就把我的复现过程和参数细节拆给你看尤其是那些一跑就翻车的坑我都会单独点名。2. CNN为什么是垃圾分类的标配先看懂卷积神经网络的设计逻辑2.1 从全连接到卷积图像分类为什么要换网络结构刚接触图像分类的人最容易有个疑问用普通的全连接神经网络直接吃像素不行吗当然可以但代价很大。一张 224×224 的三通道彩图展平之后是 150528 个输入特征如果第一层全连接用 1024 个神经元光这一层就有超过 1.5 亿个参数。训练这样的网络不仅算力吃紧还非常容易过拟合——因为全连接层对图像的每个像素一视同仁根本不会关注「垃圾图像里的塑料瓶轮廓」这种局部特征。卷积神经网络解决的就是这个问题。它的核心思路来自对图像局部性的观察一个物体在图像里往往由局部的边缘、纹理、色块组合而成而这些局部特征在图像的不同位置可能重复出现。于是 CNN 做了两件关键的事局部连接和权值共享。局部连接让每个神经元只感知一个小区域比如 3×3 或 5×5 的卷积核权值共享让同一个卷积核在整张图上滑动计算这样参数数量大幅下降同时网络能提取到位置无关的特征——这在垃圾分类里非常实用因为同一个易拉罐可能出现在图像左上角也可能出现在右下角。实际的 CNN 结构通常在卷积层之后接池化层MaxPooling 或 AveragePooling用来下采样、压缩特征图尺寸只保留最显著的特征堆叠若干组卷积池化之后再接 Flatten 扁平化和全连接层最后由 Softmax 输出每个类别的概率。这套组合拳就是绝大多数图像分类模型的底层逻辑也是这份源码里模型设计的出发点。2.2 数据目录怎么组织ImageDataGenerator 的拼图规则拿到源码之后第一步不是急着跑模型而是先把数据目录对齐。这套项目里的数据组织形式走的是 Keras 经典的flow_from_directory约定根目录下分 train、validation、test 三个文件夹每个文件夹里再按类别建子文件夹子文件夹名就是类别名。dataset/ train/ recyclable/ # 可回收纸箱、塑料瓶、玻璃瓶 kitchen/ # 厨余剩饭、果皮、菜叶 harmful/ # 有害电池、过期药品 other/ # 其他烟头、陶瓷碎片 validation/ recyclable/ kitchen/ harmful/ other/ test/ recyclable/ kitchen/ harmful/ other/这个目录结构决定了后续的数据加载方式。flow_from_directory会自动扫描子文件夹名称并生成类别标签索引所以子文件夹的命名必须和类别一一对应不要用中文也不要用带空格的文件夹名否则在 Windows 上很容易出现编码问题。我一般会额外检查一遍每个类别下的图片数量确保没有空文件夹——空的类别文件夹不会直接报错但训练时会出现样本不足导致那一类的准确率拉胯。2.3 预处理与数据增强训练之前让模型少走弯路图像输入的预处理直接决定模型能不能收敛。这份源码里用的预处理方式可以总结为三件事缩放、归一化、数据增强。缩放是把所有图片统一到target_size源码里默认是 224×224这个尺寸和很多预训练模型的输入尺寸一致后续如果想换成 VGG16 或 ResNet50 做迁移学习不需要改数据管道。归一化是把像素值从 0~255 压缩到 0~1做法是 rescale1.0/255。数据增强是训练阶段最有用的技巧它通过对已有图片做小幅度变换让模型看到更多样的输入从而降低过拟合风险。from tensorflow.keras.preprocessing.image import ImageDataGenerator train_datagen ImageDataGenerator( rescale1.0 / 255, rotation_range20, width_shift_range0.2, height_shift_range0.2, shear_range0.2, zoom_range0.2, horizontal_flipTrue, fill_modenearest ) val_datagen ImageDataGenerator(rescale1.0 / 255) train_generator train_datagen.flow_from_directory( dataset/train, target_size(224, 224), batch_size32, class_modecategorical ) val_generator val_datagen.flow_from_directory( dataset/validation, target_size(224, 224), batch_size32, class_modecategorical )这里的关键参数是rotation_range20最多旋转 20 度、width_shift_range0.2水平偏移 20%、zoom_range0.2缩放范围 20%。需要注意的是验证集和测试集不要做数据增强只做归一化否则验证集的指标会被增强后的图片干扰不能真实反映模型泛化能力。训练集增大图片多样性验证集保持原图这是一个很容易被忽视、又直接影响答辩时评委问题的细节。3. 模型搭建与训练调参让卷积神经网络收敛且不欠拟合3.1 网络结构怎么选这个源码里的 CNN 分层设计垃圾分类虽然属于图像分类任务但各类别之间的视觉差异有时很小比如「其他垃圾」里的陶瓷碎片和「可回收」里的玻璃瓶颜色和纹理高度相似。因此模型不能做得太浅源码里给出的网络结构是 3 组卷积块加 2 层全连接每一组卷积块包含卷积层、BatchNormalization 和最大池化层。from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, BatchNormalization model Sequential([ Conv2D(32, (3, 3), activationrelu, input_shape(224, 224, 3)), BatchNormalization(), MaxPooling2D((2, 2)), Conv2D(64, (3, 3), activationrelu), BatchNormalization(), MaxPooling2D((2, 2)), Conv2D(128, (3, 3), activationrelu), BatchNormalization(), MaxPooling2D((2, 2)), Flatten(), Dense(256, activationrelu), Dropout(0.5), Dense(4, activationsoftmax) ]) model.summary()这个结构的选型逻辑值得展开说。第一层卷积核数量 32第二层 64第三层 128采用翻倍策略因为随着网络加深特征图尺寸变小需要更多通道数来承载抽象的语义特征。每个卷积块里都加了 BatchNormalization它的作用是在每批数据进入激活函数之前做标准化让梯度分布更稳定——训练时你会发现加了 BN 之后模型对学习率的敏感度下降不需要反复调学习率也能收敛。最后的 Dropout(0.5) 是防过拟合的主要手段训练时随机丢弃一半神经元推理时全部保留。3.2 训练参数设定学习率、batch size 与 epochs 的搭配关系训练参数看起来是几个数字实际上每一组都像在走钢丝。先说 batch size源码里给的是 32这个值在 224×224 输入下对显存的压力适中。如果显存不够可以降到 16但要明白 batch size 变小会导致梯度估计的噪声变大训练震荡会更明显所以学习率也要相应调低。如果显存充裕并且想加速收敛可以尝试 64但前提是数据集的类别分布够均衡。学习率是所有参数里最玄学的一个没有标准答案只有经验区间。用 Adam 优化器时我一般把初始学习率设在 1e-4 到 3e-3 之间数据集小、类别少可以激进一点用 3e-3数据集大、类别多保守一点用 1e-4。这份源码里直接用的 default 学习率Adam 默认约 1e-3在多数场景下能正常收敛但如果训练过程中 loss 震荡剧烈建议加一个 LearningRateScheduler每 5 个 epochs 把学习率乘 0.6你会发现 loss 曲线明显变得平滑。epochs 的设定我习惯配合 EarlyStopping 一起用而不是傻跑固定轮数。EarlyStopping 的参数设置如from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint early_stop EarlyStopping( monitorval_loss, patience8, restore_best_weightsTrue ) checkpoint ModelCheckpoint( best_model.h5, monitorval_accuracy, save_best_onlyTrue, modemax ) history model.fit( train_generator, steps_per_epochtrain_generator.samples // 32, validation_dataval_generator, validation_stepsval_generator.samples // 32, epochs50, callbacks[early_stop, checkpoint] )patience8 表示验证集 loss 连续 8 个 epochs 不下降就停止训练restore_best_weightsTrue 是真正的后悔药——训练结束后自动回滚到验证集表现最好的那一轮权重而不是最后一轮的权重。ModelCheckpoint 则保证训练过程中任何一次的 val_accuracy 新高都会被保存。3.3 训练输出怎么读loss 曲线与准确率背后的信息量训练跑起来之后每过一个 epoch 都会打印一行训练 loss、训练 acc、验证 loss、验证 acc。很多人只看 acc这是不够的。正确做法是同时盯住验证 loss 和训练 loss 的差值如果训练 acc 一直在涨验证 acc 涨到某个点之后开始掉头同时验证 loss 开始回升说明模型已经开始过拟合了——这时候 EarlyStopping 如果还没触发就需要手动检查是不是 Dropout 率太低、数据增强不够或者模型容量太大。如果你的训练输出里 val_loss 始终不降甚至越跑越高而训练 loss 却能正常下降先别急着改模型结构检查一下训练集和验证集的数据分布是否一致。我见过最典型的情况是训练集做了数据增强验证集没有做归一化之外的任何操作但这本身没问题问题出在验证集图片里有大量训练集完全没见过的拍摄角度和光线条件导致验证集难度远高于训练集。复现这套项目时建议把验证集图片数量控制在每个类别 3050 张之间保证和训练集有重叠的场景分布这样才能准确判断模型的学习进度。4. 从训练到预测把卷积神经网络权重跑通到实际图片4.1 模型保存与加载h5 格式和 SavedModel 选哪个训练完成之后的第一件事就是把模型权重存下来。Keras 里模型保存有两种主流格式.h5和 SavedModel 目录。这套源码里用的是.h5单文件管理方便尤其适合毕设答辩时拷贝演示。需要注意的一点是如果你用model.save(model.h5)保存完整模型那么加载时用的是load_model它会反向构建整个网络结构不需要你再手动重写一遍模型定义。但如果你的模型里有自定义层或者自定义损失函数load_model在加载时会报错这时候要么改成model.save_weights()只存权重然后手动构建模型结构再load_weights要么老老实实把自定义层定义好再保存。我一般会把最终模型用两种方式各存一份h5 用来演示和存档SavedModel 用来部署。因为 SavedModel 格式对后续转 TensorFlow Serving 或者移动端部署更友好如果你预计这个毕设项目后续要做成绩展示之外的拓展多存一份没坏处。4.2 单张图片预测预处理必须和训练时保持一致性训练结束、模型加载成功接下来就是最关键的一步拿一张没见过的垃圾图片来预测。这里最容易踩的坑是预处理不一致——训练时图片被 ImageDataGenerator 缩放到 224×224 并除以 255预测时如果直接用原始尺寸原像素值喂给模型结果会离谱到完全不可信。from tensorflow.keras.models import load_model from tensorflow.keras.preprocessing.image import load_img, img_to_array import numpy as np model load_model(best_model.h5) img load_img( test/plastic_bottle.jpg, target_size(224, 224) ) x img_to_array(img) # 形状: (224, 224, 3), 取值 0~255 x x / 255.0 # 与训练时保持一致, 归一化到 0~1 x np.expand_dims(x, axis0) # 加上 batch 维度, 形状: (1, 224, 224, 3) pred model.predict(x) class_index np.argmax(pred[0]) confidence pred[0][class_index] class_names [recyclable, kitchen, harmful, other] print(f分类结果: {class_names[class_index]}, 置信度: {confidence:.2%})这段代码里最容易被忽略的是expand_dims这一步。模型训练时的输入是四维张量(batch_size, 224, 224, 3)单张图片只有三维必须手动加一个 batch 维度。另外x / 255.0的归一化操作看似简单但如果你用的是 Float32 类型需要注意内存中的数值精度一般默认没问题不需要额外处理。4.3 做一个可交互的预测展示Flask 接口让毕设答辩有底气单个脚本里跑通预测之后建议再花半小时套一个 Flask 接口这样答辩演示时可以直接打开浏览器上传图片比在黑框框里敲命令直观得多。这个项目的源码里已经带了 Web 展示部分核心逻辑可以拆成三步接收上传图片、调用模型预测、返回 JSON 结果。from flask import Flask, request, jsonify from tensorflow.keras.models import load_model from tensorflow.keras.preprocessing.image import load_img, img_to_array import numpy as np app Flask(__name__) model load_model(best_model.h5) class_names [recyclable, kitchen, harmful, other] app.route(/predict, methods[POST]) def predict(): file request.files[image] img load_img(file, target_size(224, 224)) x img_to_array(img) / 255.0 x np.expand_dims(x, axis0) pred model.predict(x) idx int(np.argmax(pred[0])) conf float(pred[0][idx]) return jsonify({ class: class_names[idx], confidence: conf }) if __name__ __main__: app.run(host0.0.0.0, port5000)这里说明一点load_img传入的文件对象可以直接处理 Flask 上传的 FileStorage 对象不需要先保存到本地再加载这能省掉一大段临时文件管理代码。接口返回的confidence建议保留几位小数再传给前端避免 JSON 序列化时出现过长浮点数。另外host0.0.0.0可以让局域网内其他设备访问到这个接口答辩时如果评委想用自己的手机试一张图片直接访问http://你的IP:5000就能演示体验会好很多。5. 避坑与排查复现这套垃圾分类源码最容易翻车的五个场景5.1 运行时报错keras 和 tf.keras 混用导致的 ImportError现象按照 README 的说明运行脚本启动阶段直接报ImportError: cannot import name to_categorical from keras或者是module keras has no attribute preprocessing。原因电脑上同时安装了独立的 Keras 包和 TensorFlow 自带的tf.keras而代码里from keras...和from tensorflow.keras...混着写解释器随机拉到的是旧版 Keras 的接口兼容性直接炸掉。解决全部统一成from tensorflow.keras...包括models、layers、preprocessing、callbacks等模块。更彻底的做法是卸载独立的 Keras 包只保留 TensorFlow 全家桶。我复现任何 Keras 项目的第一步永远是pip show keras看一眼版本如果版本号里带2.x且不是以tf-keras开头直接卸载重来。5.2 OpenCV 与图片路径中文名导致的读图失败现象训练数据准备阶段flow_from_directory能正常统计图片数量但训练中途报cv2.error: ... filename is not recognized损失直接停住恢复不了。原因数据集的某个子文件夹或者图片文件名包含中文字符。在 Windows 下OpenCV 的imread默认不处理非 ASCII 路径文件读取失败后会返回空矩阵Keras 在预处理时对空矩阵做 resize 就会抛异常。解决把数据集的所有路径改成纯英文文件夹和文件名都不要出现中文、空格和特殊符号。如果你已经拍了很多中文命名的图片用一个批量重命名循环跑一遍就搞定不要在代码里写编码转换——那是给自己挖坑。5.3 显存爆炸和 CUDA out of memory现象训练刚开始几个 batch 就报CUDA out of memory或者运行到中途突然 OOM整个进程被杀掉。原因224×224 的输入加上默认 32 的 batch size对 4GB 显存的显卡已经逼近极限如果再开几个 TensorBoard 或视频解码程序显存很容易不够。解决把 batch size 降到 16 或 8或者把图片输入尺寸降到 160×160模型重新训练时的参数会急剧减少但准确率下降通常可以接受。还有一个常用技巧是在代码开头加一行import tensorflow as tf gpus tf.config.experimental.list_physical_devices(GPU) for gpu in gpus: tf.config.experimental.set_memory_growth(gpu, True)set_memory_growth(True)的作用是让 TensorFlow 按需分配显存而不是启动时把所有显存都占满。加上之后即使显存不充裕也能在小显存的机器上完成训练只是速度会稍微慢一点。5.4 训练 loss 变成 NaN 的排查思路现象训练到第几个 epoch 之后loss 突然变成 NaN准确率也跟着变成 0之后再也回不来。原因NaN 出现的常见源头有三个——学习率过高导致梯度爆炸、数据里有 NaN 像素值或全黑图片、模型权重初始化异常导致前向传播输出 Inf。检查顺序也是按这个优先级来。解决先在compile里给优化器加梯度裁剪约束Adam 优化器支持clipnorm1.0这是最常见的止损手段然后检查读取图片的管道打印几张图片的像素最大值和最小值确认没有异常值最后把数据集的异常图片比如损坏的 JPEG、0 字节文件批量清理掉。我一般会在数据加载后加一个断言检查np.isnan(images).sum()是否为 0不满足就自动跳过该文件。5.5 预测结果永远指向同一个类别现象模型训练完准确率看着还行比如 85%但一到预测阶段无论输入什么图片输出永远是同一个类别置信度还接近 100%。原因数据集类别严重不平衡。如果「其他垃圾」类别的图片数量是其他两类的三倍以上模型学到的最优策略就是「一律输出其他垃圾」因为这样总体的准确率也能刷到 60% 以上。训练时 model.fit 输出的 acc 会骗人但验证集的单类别回传矩阵会暴露问题。解决重新统计每个类别的图片数量把最多的类别降采样到接近最少类别的数量或者用class_weight参数给样本少的类别加上权重让模型在反向传播时更关注那些「稀有类别」。6. 进阶用法用 sklearn 的混淆矩阵定位模型短板再决定是否换迁移学习训练结束后别急着写论文先把测试集上的分类细节拉出来看一眼。classification_report能告诉你每个类别的精确率、召回率和 F1-score而confusion_matrix能直观展示模型具体把哪两类垃圾搞混了。from sklearn.metrics import classification_report, confusion_matrix y_true [] y_pred [] for filename, label in test_pairs: img load_img(filename, target_size(224, 224)) x img_to_array(img) / 255.0 x np.expand_dims(x, axis0) pred model.predict(x) y_true.append(label) y_pred.append(np.argmax(pred[0])) print(classification_report(y_true, y_pred, target_namesclass_names)) print(confusion_matrix(y_true, y_pred))如果发现「陶瓷碎片」经常被识别成「玻璃瓶」说明这两个类别在纹理和颜色上高度重叠人工肉眼也容易混淆。这时候与其盲目加网络层数不如直接换迁移学习把这个分类问题从头训练改成特征迁移去掉你自己搭建的全连接头换成 ImageNet 上预训练的 ResNet50 主干做特征提取器只训练最后几个全连接层。这样做的好处是模型在训练集较小的情况下也能借到 ImageNet 学到的底层纹理和边缘特征准确率往往比你从零训练的 CNN 高出一截。具体做法和参数讲起来又是一篇文章但核心就一句话当你的目标任务和自己的 CNN 结构在视觉上不匹配时用轻量级微调往往更省力。我后来每次做毕设类的图像识别项目都会强制走一遍这个流程先看分类报告里每一类的召回率再对比混淆矩阵里的高混淆对最后决定是调自己模型的深度还是直接上迁移学习。这套垃圾分类源码也一样它不是完美的银弹但只要把数据和训练管线吃透论文里的实验分析部分反而比算法本身更好写。希望这份复现笔记能帮到你少走一段我当年走过的最坑的弯路。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。