尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python实现垃圾分类:CNN图像分类+决策树算法完整实战

Python实现垃圾分类:CNN图像分类+决策树算法完整实战 简介基于Python、卷积神经网络与决策树算法实现的垃圾分类系统项目包面向高校学生与毕业设计开发者覆盖图像分类、树模型和垃圾识别等算法实践代码已调试可运行评审得分95分以上难度适中适合作为课程设计、毕业设计或入门深度学习的参考工程。压缩包内共2000个文件其中包含1985张jpg图片、8个Python源码、4个docx文档、2个md说明及1个txt文件各类型相互配套图片用于训练和测试源码完成模型构建与推理说明文档包含设计报告、测试方案和需求规格说明书便于复现与二次开发。资源整体约53MB目录组织清晰已有196人学习浏览对于需要课程设计、项目源码或算法入门资料的学习者这份压缩包提供了从数据集、可运行代码到文档的完整闭环可直接参考并在此基础上扩展。1. 先看懂这套垃圾分类系统CNN负责看决策树负责判这套基于 Python 的垃圾分类系统把 CNN 卷积神经网络和决策树算法串在同一条流水线上先用 CNN 图像分类算法从垃圾照片里抽出视觉特征再用决策树对特征做最终归类。我拆完源码和数据集后最直观的感受是它不像网上那些只贴模型的 Demo而是把数据准备、两种算法实现、测试方案和文档评审真正揉成了一个能交付的完整项目。它适合两类人——正处于课程设计、毕业设计阶段的学生以及想快速上手图像分类加机器学习组合方案的从业者。你不需要有很深的算法基础只要会装环境、能跑通脚本就能顺着源码把整条链路复现出来。2. 把项目跑起来文件结构、环境配置与三阶段训练流程2.1 解压后先摸清目录文档、数据集、源码各自的位置拿到压缩包后先别急着运行花五分钟把目录结构看一遍。这套资源的文件组织是典型的课程设计风格源码集中在根目录或 src 目录下数据集按类别分目录存放文档单独放一份另外还有若干 paper 开头的 jpg 图片这些是论文插图写设计报告时可以对照引用也能作为参考文献来源的佐证。每个文件的存在都有目的不是随手丢进去的。我一般会先看两个文档需求规格说明书和测试方案。前者告诉你系统边界在哪里特别是垃圾分类的类别数——这直接决定 CNN 最后一层输出几个神经元、决策树要分几个分支后者告诉你跑通之后怎么验证比如准确率要过多少才算达标。这些信息比直接读代码更省时间尤其当你要把这个项目改造成自己的大作业时文档就是你的改造依据。下面这张表是我按这套资源的内容整理的。文件在评审和开发中的用途需求规格说明书定义系统功能边界、垃圾类别范围、性能指标设计报告说明 CNN 和决策树的选型理由、模块间接口、结构图测试方案给出验收标准、测试用例、评估指标项目进度计划和可行性研究课程评审必交材料说明时间规划和项目可行性paper 开头的图片论文插图写报告时作为参考文献来源佐证数据集部分我建议解压后先统计一下每个类别的图片数量。类别不均衡是这类资源最常见的问题——某个类别几百张另一个类别只有几十张直接影响训练效果。用一行 Python 就能扫出来import os from collections import Counter data_root data/train # 改成你本地的训练集路径 counter Counter() for class_name in os.listdir(data_root): path os.path.join(data_root, class_name) if os.path.isdir(path): counter[class_name] len(os.listdir(path)) print(counter)这段代码的逻辑很简单遍历数据根目录下每个子目录目录名就是类别名统计每个目录里的图片数量输出一个类别分布字典。如果发现某个类别数量特别少后面训练时要考虑类别权重或者数据增强补偿。参数上只需要改 data_root 这一个路径Windows 下注意路径分隔符用反斜杠时别漏了转义建议都用正斜杠。2.2 环境配置Python版本选择与依赖安装这个项目的运行环境并不特殊核心依赖是 TensorFlow、scikit-learn、NumPy、Pandas、Pillow 和 Matplotlib。用 Python 3.8 或 3.9 都行我个人习惯用 3.9和 TensorFlow 2.x 的兼容性比较好。安装时强烈建议先建一个虚拟环境避免依赖和系统 Python 纠缠在一起以后删了重装也干净python -m venv waste_env source waste_env/bin/activate # Windows 下用 waste_env\Scripts\activate pip install tensorflow scikit-learn pandas numpy matplotlib pillow这里有个细节TensorFlow 2.x 已经内置了 Keras不需要单独装。scikit-learn 提供决策树实现Pillow 负责读图片Matplotlib 用来画混淆矩阵和训练曲线。如果你的机器没有 NVIDIA GPUpip 会自动安装 CPU 版 TensorFlow能跑但训练会慢一些如果自己装了 GPU 版却跑不起来多半是 CUDA 和 cuDNN 版本没对上这个问题放到第 5 章避坑再展开。装完之后先验证一下环境再进入下一步。我一般会执行一段极短的检查脚本确认核心库都能正常导入且版本可用python -c import tensorflow as tf; print(tf.__version__) python -c import sklearn; print(sklearn.__version__)这两行命令会打印 TensorFlow 和 scikit-learn 的版本号。如果报错先检查是不是当前 shell 没有激活虚拟环境——这个错误初学者最容易犯往往在系统 Python 里重复装了好几遍才发现环境不对。2.3 按顺序跑通三个脚本预处理、CNN训练、决策树训练源码里通常有三个入口脚本分别对应数据预处理、CNN 训练、决策树训练。我第一次运行时走了弯路直接运行主训练脚本结果报错找不到数据后来才发现要先运行数据预处理脚本把原始图片转换成统一尺寸的张量并生成标签文件。这个依赖顺序在说明文档里有写但藏得比较深我建议你按下面这个顺序来python 01_preprocess.py # 生成训练集/验证集目录和标签映射 python 02_train_cnn.py # 训练CNN并保存模型、特征向量 python 03_train_tree.py # 用CNN特征训练决策树并输出评估结果三条命令的逻辑是递进的。第一步把数据集里的图片统一缩放按比例划分训练集和验证集同时生成一个类别名到数字标签的映射文件第二步用 CNN 在训练集上迭代若干轮训练完成后不仅保存模型权重还会把每张图片在卷积网络倒数第二层的输出抽出来存成特征向量文件第三步读取特征向量用决策树做分类训练并输出准确率、分类报告。如果你拿到的脚本命名不是这样比如叫 train.py 或 main.py就按数据准备→CNN→决策树的依赖顺序执行别跳步。执行完每个脚本后我习惯先看一眼控制台输出和中间文件是否生成再进入下一阶段。比如第一步结束后应该有 train/ 和 val/ 目录第二步结束后应该有 waste_cnn.h5 和 features.npz第三步结束会打印分类报告。哪一步缺了文件回头检查那一步的脚本更省时间。3. CNN图像分类模块模型结构、训练参数与特征向量输出3.1 为什么用CNN做图像分类卷积核、池化与参数共享垃圾分类图像分类的核心问题是怎么把一张 200×200 的彩色图片变成一个能区分类别的向量。如果用全连接网络输入层就有 200×200×3120000 个神经元第一层全连接的参数量会膨胀到不可接受训练既慢又容易过拟合。CNN 解决这个问题靠的是两个机制——局部感受野和参数共享。卷积核每次只看图片的一小块区域提取边缘、纹理这类局部特征同一个卷积核在所有位置上滑动权重是共享的参数量因此大幅下降。卷积核的尺寸和步长直接影响特征提取的粒度。3×3 卷积核是当前的主流选择因为它感受野足够、参数量小堆叠两层等价于一层 5×5 的感受野padding 一般用 same保证特征图尺寸不缩小边界信息不丢失。池化层的作用是下采样通常用最大池化取窗口内的最大值作为输出这对图像分类特别有效——它能保留最显著的激活响应同时把特征图尺寸减半相当于让模型关注有没有这个特征而不是特征精确在哪里。设计报告里对这个模块应该有结构图你对照着看每一层的输入输出维度会更清楚。但这里要注意一个关键点CNN 在这套系统里不只是做一个分类器它更重要的角色是特征提取器——后面的决策树还要消费它产出的特征向量所以倒数第二层的设计比最后一层的 Softmax 更重要。这句话是理解整套代码的钥匙。3.2 模型结构设计三个卷积模块的参数逻辑源码里的 CNN 模型结构常见做法是三段式卷积模块加一个分类头。每个模块包含一个 Conv2D、一个 BatchNormalization 和一个 MaxPooling2D卷积核数量从 32 逐级翻倍到 128。这是图像分类任务最稳定的配置套路既不会因为太浅学不到高级特征也不会因为太深在小数据集上过拟合。我把它整理成等价的形式方便你对照源码里的定义修改from tensorflow.keras import layers, models def build_cnn(input_shape(128, 128, 3), num_classes6): inputs layers.Input(shapeinput_shape) # 第一段提取低级边缘纹理特征 x layers.Conv2D(32, (3, 3), activationrelu, paddingsame)(inputs) x layers.BatchNormalization()(x) x layers.MaxPooling2D((2, 2))(x) # 第二段提取物体局部结构特征 x layers.Conv2D(64, (3, 3), activationrelu, paddingsame)(x) x layers.BatchNormalization()(x) x layers.MaxPooling2D((2, 2))(x) # 第三段提取更抽象的类别相关特征 x layers.Conv2D(128, (3, 3), activationrelu, paddingsame)(x) x layers.BatchNormalization()(x) x layers.GlobalAveragePooling2D()(x) # 特征向量在这一层输出决策树后续消费它 x layers.Dense(64, activationrelu, namefeature_vector)(x) outputs layers.Dense(num_classes, activationsoftmax, namepredictions)(x) model models.Model(inputsinputs, outputsoutputs) return model这个结构里有一个刻意设计在 GlobalAveragePooling2D 展平之后、Softmax 之前插入了一个 64 维的 Dense 层并命名为 feature_vector。训练完成后我们会单独取出这一层的输出作为每张图片的特征向量供第 4 章的决策树使用。所以这个模型的输出其实是两层的集合理解这一点你就知道为什么不能用 model.predict 直接拿最终分类结果。参数上的几个要点input_shape 必须和预处理脚本里的图像缩放尺寸完全一致如果数据集图片分辨率不高128×128 是性价比很好的选择num_classes 要等于你数据集的类别总数改成四分类就填 4六分类就填 6这个数字和预处理时生成的标签映射必须对应。BatchNormalization 放在卷积之后、池化之前作用是让每层输入分布稳定收敛速度会明显更快。GlobalAveragePooling2D 比起 Flatten 的优势是大大减少参数量而且它对空间位置不敏感物体在图片里稍微偏移也不影响特征表达。3.3 训练配置与调参学习率、BatchSize、数据增强训练 CNN 的调参是这门课最像玄学的地方。源码里默认的配置我建议先按原样跑一遍别一上来就改参数。常见配置是 batch size 取 32 或 64epochs 取 30 到 50优化器用 Adam初始学习率取 0.001。如果训练到一半发现损失明显波动或者准确率上不去再把学习率降到 0.0001。数据增强在源码里是开着的翻转、旋转、缩放这些操作能显著缓解过拟合但增强幅度别太大否则会让模型学到的特征不稳定。from tensorflow.keras.preprocessing.image import ImageDataGenerator train_datagen ImageDataGenerator( rescale1./255, rotation_range20, width_shift_range0.1, height_shift_range0.1, horizontal_flipTrue ) train_generator train_datagen.flow_from_directory( data/train, target_size(128, 128), batch_size32, class_modecategorical ) model.compile(optimizeradam, losscategorical_crossentropy, metrics[accuracy]) history model.fit( train_generator, steps_per_epochtrain_count // 32, epochs50, validation_dataval_generator, validation_stepsval_count // 32 ) model.save(waste_cnn.h5)这段代码里的 rescale1./255 是重中之重它把像素值从 0-255 缩放到 0-1不归一化直接训练loss 经常会掉不下去。rotation_range 旋转、shift 平移、flip 翻转这几个增强参数数值越大训练越慢但对小数据集效果越明显。flow_from_directory 要求图片按类别放在不同子目录下目录名就是类别名它会自动生成标签但标签的数字顺序是按目录名字母序排的这一点在后面决策树阶段要对上号。train_count // 32 是每个 epoch 的训练步数如果训练集有 1200 张图batch size 是 32那每个 epoch 约 37 步。我提醒一个容易翻车的点如果 train_count 和 val_count 不是 batch size 的整数倍整除后会丢最后一批准确率评估有微小偏差一般不需要额外处理但训练集太小的时候建议把 batch size 调小。4. 决策树分类模块特征衔接、剪枝参数与评估方法4.1 决策树在系统里的定位白盒分类与可解释性很多同学看到CNN 决策树会困惑CNN 不是已经能分类了吗为什么还要套一层决策树这是这套设计里比较亮眼的一笔。CNN 的 Softmax 层输出的是类别概率但它本质上是黑匣子说不清楚为什么觉得这张图是塑料瓶。而决策树是白盒模型每个分裂节点都是一个可解释的判断条件。这套系统的做法是把 CNN 倒数第二层输出的 64 维特征向量当作决策树的输入让决策树在高层特征上做一次可解释的分类决策。这样做的好处是双向的。CNN 负责把图像这种非结构化数据转成结构化的特征向量弥补了决策树不能直接处理原始像素的短板决策树负责输出一条条类似如果特征 32 0.5 且特征 57 0.3则判定为可回收垃圾的规则写设计报告时把这些规则贴上去评审老师一看就知道你是真懂这两种算法各自的能力边界。相比之下随机森林在这个任务上准确率通常会更高但可解释性会差不少如果你做对比实验时会发现这个差距到时候可以单独讨论。4.2 特征向量构建从CNN倒数第二层抽取特征提取特征向量这一步要把 CNN 模型的输出重新定义一下。因为前面保留了 feature_vector 层这里直接指定它的输出即可。训练集和验证集要分开提取避免决策树在训练时偷看验证集信息——这是数据泄漏的典型来源之一我见过不少人在这一步翻车。import numpy as np from tensorflow.keras import models from tensorflow.keras.models import load_model model load_model(waste_cnn.h5) extractor models.Model(inputsmodel.input, outputsmodel.get_layer(feature_vector).output) X, y [], [] for label, class_name in enumerate(class_names): for img_path in train_images[label]: img load_and_preprocess(img_path) # 必须与CNN训练时的预处理完全一致 vec extractor.predict(np.expand_dims(img, axis0))[0] X.append(vec) y.append(label) X np.array(X) y np.array(y) np.savez(features.npz, XX, yy)这段代码的逻辑是用 get_layer 拿到 feature_vector 层的输出构造一个输入图片→输出 64 维特征向量的新模型然后遍历每个类别下的所有图片逐张提取特征并打上数字标签。class_names 来自预处理阶段生成的标签映射文件train_images 是按类别整理的图片路径列表这两个变量在源码里是从预处理脚本阶段传递过来的。关键坑在于 load_and_preprocess 必须和 CNN 训练时用的预处理完全一致包括缩放尺寸、归一化方式、是否做了同样的裁剪。不一致会导致特征分布偏移决策树学了错误分布验证时表现会很差。如果你发现源码里这一步复用了同一个函数那就没问题如果是复制的代码务必比一下两个函数的参数。savez 保存的是 .npz 压缩格式后面决策树直接 np.load 就能读。4.3 决策树训练与评估max_depth、交叉验证与分类报告决策树这段用 scikit-learn 是最省事的。核心参数是 criterion、max_depth 和 min_samples_split。criterion 一般选 gini 或 entropy在高维特征上两者差别不大。max_depth 是防过拟合的王牌不限制深度的决策树会把训练集完美分类但在验证集上经常一塌糊涂源码里应该设置了深度上限我建议从 8 开始试。from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report data np.load(features.npz) X_train, X_val, y_train, y_val train_test_split( data[X], data[y], test_size0.2, random_state42, stratifydata[y] ) clf DecisionTreeClassifier(max_depth8, min_samples_split4, random_state42) clf.fit(X_train, y_train) y_pred clf.predict(X_val) print(classification_report(y_val, y_pred))这里我特别要强调 stratifydata[y] 这个参数。当各类别样本数不均衡时不做分层抽样会导致某些类别在验证集里一个样本都没有分类报告直接报错或者严重失真。加了 stratify 之后训练集和验证集里各类别比例保持一致评估结果才有参考价值。max_depth 和 min_samples_split 是一对组合拳。max_depth8 限制树的层数min_samples_split4 限制节点继续分裂所需的最小样本数两者联合控制模型复杂度。如果你不确定最优值用 GridSearchCV 在几个候选值上做交叉验证能省很多试错时间。训练完成后我还会把树导出成语义化的条件文本贴进设计报告from sklearn.tree import export_text print(export_text(clf, feature_names[ff{i} for i in range(64)], max_depth5))export_text 输出的就是树结构的可读文本每一行代表一个判断节点比如f12 0.32 - 可回收垃圾。它会直接变成你论文答辩时的白盒证据。feature_names 生成 f0 到 f63 的特征名max_depth5 限制导出深度避免文本过长。5. 避坑手册本地复现这个系统最常见的5个问题这套链路我完整跑过一遍踩过的坑比顺利的时候多。下面按现象→原因→解决的方式列出最容易遇到的五个问题复现时可以直接对照排查不用再走一遍弯路。5.1 现象flow_from_directory 报错或类别错乱原因数据集目录里存在中文路径、空格或者类别目录名和代码里写死的类别列表不一致。ImageDataGenerator 对路径编码和目录结构很敏感中文路径在 Windows 下尤其容易出问题。解决先检查数据集根目录到图片文件的每一级路径有中文或空格就改成英文比如data/train/可回收改成data/train/recyclable再打开预处理脚本确认 class_names 的顺序和数据集目录的实际排序一致——flow_from_directory 会按目录名的字母序自动映射标签如果你在代码里手写了一个类别列表顺序对不上就会张冠李戴。我每次都在预处理脚本里加一行 print(class_names) 打印映射表肉眼核对后再开始训练。5.2 现象训练 loss 不降准确率一直在低位徘徊原因常见的有三个——图片没有归一化、标签没有做 one-hot 编码、学习率太大导致 loss 震荡。最容易被忽略的是归一化很多人直接用 ImageDataGenerator 拿数据忘了加 rescale1./255。解决先检查 flow_from_directory 里有没有 rescale 参数没有就补上确认标签用 to_categorical 转成了 one-hot学习率从 0.001 降到 0.0001 再跑一轮。如果还是不行把训练集缩小到十分之一只跑 5 个 epoch看 loss 是不是能降下来。不能降说明是代码链路的问题而不是参数问题这时候要回头查数据加载和标签映射的代码。5.3 现象决策树在验证集上的准确率比 CNN 低了一大截原因这不是 bug而是正常的。CNN 用 Softmax 分类时隐式学到了适合分类的全局特征决策树接收的 64 维特征向量虽然信息充足但决策树的决策边界是轴对齐的矩形表达能力不如神经网络的最后一层。另外如果你没限制 max_depth决策树过拟合了训练集。解决把 max_depth 限制在 6 到 10 之间用 GridSearchCV 自动搜索 max_depth 和 min_samples_split 的组合。如果准确率还是不理想试着把决策树换成随机森林对比一下但这种改动会偏离决策树算法的题目要求建议把两者结果都列在讲座报告里做对比分析反而比单模型更有说服力。5.4 现象pip 安装 TensorFlow 后 import 报错或某些 API 不存在原因TensorFlow 2.x 版本迭代快Keras 接口在 2.4 之后统一从 tensorflow.keras 导入。网上老代码如果是单独安装的 Keras就会出现版本冲突。这个项目源码里如果同时出现 from keras 和 from tensorflow.keras那一定有一处会挂。解决统一用 from tensorflow.keras 导入所有层、模型、预处理工具如果源码里混用了两种导入方式把 from keras 全部改成 from tensorflow.keras。安装时锁定一个大版本比如 tensorflow2.10避免后续升级带来的接口变化。装完后执行 import tensorflow as tf 验证打印版本号确认没装错环境。5.5 现象训练时显存或内存不足电脑直接卡死原因图片尺寸太大、batch size 设太高或者数据增强在 CPU 上执行导致内存暴涨。解决把图像缩放尺寸从 224×224 降到 128×128 甚至 96×96这个项目里对精度的影响不大batch size 从 64 降到 32 或 16如果数据集图片原始分辨率很高预处理脚本里务必先压缩再进内存。另外训练时关掉浏览器和其他占用内存的程序数据增强阶段会同时维护多份图片副本内存占用比想象中高。实在不行把 epochs 减半分批跑完再续训模型保存后随时可以接着训练。6. 验证与进阶从分类报告到一键预测的收尾习惯跑通不等于能交付。课程设计评审最看重的是你怎么证明你的系统是有效的测试方案文档里要求的验证指标几乎都指向三个工具混淆矩阵、分类报告、以及一组测试样本的真实预测结果。我在复现时会额外生成一张混淆矩阵图对角线上的数字是每个类别的正确召回数非对角线上的数字就是典型的混淆错误比如厨余垃圾被误判成其他垃圾。把这张图放进设计报告评审老师一眼就能看出你是理解过模型行为的而不是盲目跑完交差的。一份好的验证自查表不需要复杂但每一项都要能对着勾验证项方法通过标准分类准确率决策树在验证集上的 accuracy与 CNN 验证集准确率差距在 5% 以内类别混淆查看混淆矩阵非对角元素无明显的高频错判对单张图片预测用一张新图片走完整条链路输出类别与人工判断一致进阶方向上我觉得潜力最大的做法是保留当前的CNN 特征 决策树作为 baseline再训练一个随机森林和一个逻辑回归分类器用同一份特征矩阵做对比。你会发现64 维特征上逻辑回归往往比决策树更稳随机森林更强但解释性差。把这个对比结果作为设计报告的一节项目的深度立刻上一个台阶——你不仅实现了要求还验证了选型合理性。最后分享一个我自己吃过的亏早期做实验时改一个参数跑一次结果记在脑子里两三天后写报告时什么都说不清楚甚至把两类模型的准确率记反了。从那以后我每次改参数都会把修改前、修改后的指标追加写进同一个表格文件包括改了什么、为什么改、结果差多少。这样任何一次回退都有据可查答辩时被问到你试过哪些配置也能对答如流。这套工作习惯在课程设计和毕业设计里比模型本身更值钱。希望帮到你。本文还有配套的精品资源点击获取
返回列表