简介:面向机器学习课程设计或毕业设计的完整Python垃圾分类系统源码包,适合掌握Python基础、希望将理论用于图像分类实战的高校学生和开发者,能够解决从数据集处理、模型训练到结果展示全流程的参考需求。压缩包共32个文件、约2.26MB,包含4个Python脚本,分别对应模型训练、测试评估与矩阵分析;另有9张jpg与8张jpeg原始图像、4个xml标注文件、4张png结果图以及一份Excel评估表,同时兼顾代码、数据与可视化输出。目前已有772人学习,在同类课程设计资源中具有较高参考热度。项目基于TensorFlow 2.3与MobileNet迁移学习,覆盖图像预处理、特征提取、监督学习模型训练、召回率与F1分数评估、混淆矩阵可视化等关键步骤,并附带可运行的图形界面演示,方便直接体验分类结果。通过源码可直观掌握sklearn主成分分析、交叉验证与超参数调整的落地方式,为完成课程设计报告、答辩演示或进一步扩展至深度学习提供直接支撑。
1. 开箱Python机器学习课设包:垃圾分类系统的构成与复现前提
拿到《机器学习课程设计,Python垃圾分类系统源码》这份压缩包时,我习惯先列一遍文件再解压,因为这类课设包最大的变数往往不在算法,而在能不能一次跑通。它是一套基于 TensorFlow 2.3 和 MobileNet 的图像分类工程:训练脚本、评估脚本、桌面窗口三条线都在,非常适合作为机器学习课程设计的对标项目或入门练手。但想真正复现,得先把迁移学习选型、数据目录约定、混淆矩阵验收这三件事串起来。下面按我拆包时的顺序,从文件结构一路讲到参数、踩坑和界面接入。
2. 看清源码包结构:从文件命名反推完整流水线
2.1 顶层文件命名:先拆出训练、评估、界面三条主线
解压后通常能看到两个目录,一个是machine-learning-master,另一个是trash_classification_tf2.3-master。前者是常见仓库下载时的外层壳,后者才是真正的工程目录,名字里的tf2.3直接点明了框架版本。我一般先把后者完整列一遍:
trash_classification_tf2.3-master ├── train_mobilenet.py # 训练入口:MobileNet 迁移学习,50轮 ├── test_model.py # 单张图片预测脚本 ├── window_trash.py # 桌面窗口程序(PyQt5) ├── matrix.py # 混淆矩阵生成 + matrix.xls 导出 ├── results_epoch50.png # 训练50轮的结果图(loss/acc曲线) ├── matrix.xls # 混淆矩阵表格 ├── images/ # 图片数据集或测试图目录 ├── .keep # git空目录占位文件从这几个文件名就能反推完整流程:train_mobilenet.py负责训练并在最后输出模型,test_model.py负责拿单张图片验证模型,matrix.py对全部测试图片做批量预测并生成混淆矩阵,window_trash.py把模型封装成桌面窗口。results_epoch50.png和matrix.xls都是训练与评估的产物,说明这个包默认跑完过 50 轮,并且保留了一份可视化结果。
提示:这类压缩包通常不附带
requirements.txt,环境要自己补。建议先建虚拟环境再锁版本,具体版本写在第 5 章避坑清单里。
2.2 images 目录:垃圾分类数据集的摆放约定
图像分类项目里,数据目录按类别分子文件夹是最常见的约定。训练代码通过子目录名得到标签,所以images/下一般是这样组织的:
images/ paper/ # 纸张 plastic/ # 塑料 glass/ # 玻璃 metal/ # 金属 kitchen/ # 厨余垃圾 other/ # 其他train_mobilenet.py里通常用flow_from_directory或image_dataset_from_directory读取这个目录,类名由子文件夹名自动生成。也就是说,你的images/放错层级,类别映射就会整个错位。
我看这个包的时候发现它带有.keep占位文件,说明images/原本很可能只有目录骨架或少量样板图,真实数据集需要自己按上面的结构补进去。这一点不处理好,后面所有精度数字都是假的,后面避坑章会详细讲。
2.3 两个 master 目录怎么取舍:版本差异与占位文件
碰到双层目录时,先看哪一层有train_mobilenet.py,真正能跑的是这一层。machine-learning-master如果只有 README 或空壳,就把它当作下载残留,直接忽略即可。还有一点容易忽略:.keep是 git 里的空目录占位文件,说明原工程上images/或某个输出目录在仓库里是空的,模型保存目录通常也要自己建。
把这份源码包迁移到自己课程设计里时,我一般这样做:把trash_classification_tf2.3-master整体复制到新工程根目录,建一个干净的虚拟环境,然后把代码里写死的图片路径和输出路径统一改成相对路径。注意检查一个坑:matrix.py里如果写死了images/test/这种路径,而你只放了images/,运行时会直接找不到文件。
3. 训练MobileNet分类器:数据摆放、关键参数与50轮调参思路
3.1 为什么选 MobileNet 而不是从零训练 CNN
课程设计的数据量通常不大,垃圾图片可能每类只有几百张。从零训练一个 CNN 在这种情况下非常容易过拟合,训练集准确率很快冲到 0.98,验证集却在 0.7 附近震荡。MobileNet 是 ImageNet 预训练的特征提取器,它在通用图像特征上已经有了很强的先验,垃圾的纹理、边缘、颜色这些中低层特征可以直接复用,我们只需要在它后面接一个小的分类头,用少量数据和较少轮数就能拿到可接受的精度。
TF2.3 中直接调用tensorflow.keras.applications.MobileNet即可,include_top=False表示去掉原有 1000 类分类头,只保留卷积特征部分。相比 SVM、KNN 这类传统方法,迁移学习对图像这类高维非结构化数据的拟合能力更强;而相比自己写 ResNet,MobileNet 参数少,CPU 也能勉强跑完 50 轮训练。
3.2 train_mobilenet.py 的代码走读与参数表
这个脚本的核心逻辑可以归纳成下面这段典型写法:
from tensorflow.keras.applications import MobileNet from tensorflow.keras import layers, models from tensorflow.keras.preprocessing.image import ImageDataGenerator IMG_SIZE = (224, 224) BATCH_SIZE = 32 train_datagen = ImageDataGenerator( rescale=1.0 / 255.0, rotation_range=20, width_shift_range=0.2, height_shift_range=0.2, horizontal_flip=True, validation_split=0.2, ) train_gen = train_datagen.flow_from_directory( "images/", target_size=IMG_SIZE, batch_size=BATCH_SIZE, subset="training", shuffle=True, ) base_model = MobileNet( input_shape=(IMG_SIZE[0], IMG_SIZE[1], 3), include_top=False, weights="imagenet", ) base_model.trainable = False model = models.Sequential([ base_model, layers.GlobalAveragePooling2D(), layers.Dropout(0.5), layers.Dense(len(train_gen.class_indices), activation="softmax"), ]) model.compile(optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"])这个片段里几个参数值得注意。rescale=1.0/255.0把像素归一化到 0~1,MobileNet 的预训练权重就是在这个量级下学的;rotation_range、width_shift_range、horizontal_flip是数据增强,目的是减少过拟合。validation_split=0.2表示从数据里切出 20% 做验证集,两个subset="training"和验证集的 generator 要配对使用,漏掉subset="validation"就会报ValueError。
最关键的一行是base_model.trainable = False。迁移学习第一阶段只训练后面新增的Dense分类头,卷积部分权重冻结不动。如果训练完精度不满意,再把base_model.trainable = True解冻最后几十层做微调,学习率要降到 1e-4 左右。
参数约定可以整理成下面这张表:
| 参数 | 值 | 影响 |
|---|---|---|
| IMG_SIZE | (224, 224) | MobileNet 标准输入尺寸,太大显存吃紧,太小丢纹理 |
| BATCH_SIZE | 32 | 常规值,数据量小时建议降到 16 |
| epochs | 50 | 迁移学习第一阶段默认轮数 |
| optimizer | adam | 默认 lr=1e-3,微调阶段改 1e-4/1e-5 |
| Dropout | 0.5 | 分类头防过拟合 |
| validation_split | 0.2 | 没有独立 val 目录时的切分比例 |
results_epoch50.png的存在说明这份源码按默认参数跑过完整 50 轮。如果你机器比较慢,先用 10~15 轮确认流程全通,再续跑 50 轮,别一上来就全量训练。
3.3 训练过程监控:从结果图里看三点
跑完 50 轮后,results_epoch50.png会包含训练/验证的 loss 和 accuracy 两组曲线。看这个图我一般只盯三点:验证精度是否还在上升、验证 loss 有没有从某个 epoch 开始掉头向上、训练精度是不是已经贴到 1.0。前两点是过拟合信号,最后一点说明模型容量已经足够大,再训也不会有实质提升。
常见做法是给训练脚本补两个回调:
from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau early = EarlyStopping(monitor="val_loss", patience=5, restore_best_weights=True) lr_sched = ReduceLROnPlateau(monitor="val_loss", factor=0.5, patience=3, min_lr=1e-6) model.fit( train_gen, validation_data=valid_gen, epochs=50, callbacks=[early, lr_sched], )EarlyStopping的patience=5表示验证 loss 连续 5 轮不改善就停,restore_best_weights=True会自动回滚到验证集上最好的那版权重,相当于买了后悔药。ReduceLROnPlateau在 loss 停滞时把学习率减半,避免后期在原地打转。加上这两个回调,50 轮可能实际 30 轮就停了,效果通常比硬跑满 50 轮更好。
4. 评估与验收:混淆矩阵读出四类典型误判
4.1 matrix.py 在做什么:批量预测并生成 matrix.xls
matrix.py做的事情可以理解为遍历测试图片,对每张图调用训练好的模型做预测,收集真实标签和预测标签,然后用sklearn.metrics.confusion_matrix生成混淆矩阵。核心逻辑是这样的:
import numpy as np import pandas as pd from sklearn.metrics import confusion_matrix, classification_report cm = confusion_matrix(y_true, y_pred, labels=class_names) report = classification_report(y_true, y_pred, labels=class_names, output_dict=True) pd.DataFrame(cm, index=class_names, columns=class_names).to_excel("matrix.xls") pd.DataFrame(report).T.to_excel("matrix_report.xls")这里有两个容易被忽略的参数。labels=class_names必须显式传入,否则confusion_matrix会按字母顺序排类,和训练时的类别顺序对不上;output_dict=True则让分类报告变成结构化字典,方便直接写进 Excel。为什么要把结果落到matrix.xls里?课程设计答辩时,给老师看一张彩色混淆矩阵图加一张 Excel 表,比只报一个准确率数字有说服力得多,出问题时也更容易定位到具体类别。
4.2 混淆矩阵里最常出现的四类误判
垃圾图像分类的混淆矩阵里,误判通常集中在视觉相似的类型上。第一类是塑料瓶和玻璃瓶,两者都是透明或半透明圆柱体,颜色直方图和形状特征高度重合,模型经常把玻璃瓶判成塑料瓶。第二类是纸类内部的细分,比如报纸和纸板,纹理和颜色都接近,边界基本靠揉搓后的褶皱形态区分,二维图像里很难抓住。第三类是厨余垃圾和沾油污的一次性餐具,真实场景中塑料餐盒混着剩菜,标签本身都可能标错。第四类是高反光的金属罐与银色包装袋,反光区域让卷积特征出现类似的强边缘。
看混淆矩阵时不要只看整体准确率,而要看矩阵的off-diagonal值,也就是预测错的格子集中在哪个类别。如果某两类互相混淆严重,先回去检查images/下这两类图片的标签质量,而不是急着改网络结构。标签噪声在爬虫数据里非常常见,这个问题比模型容量更隐蔽。
4.3 三个指标:准确率、召回率、F1 怎么对照验收
课程设计评估阶段常用的三个指标各有用途。准确率是全部预测里正确的占比,适合整体概览;召回率是某类真实样本里被正确找出来的比例,数据不均衡时靠它发现问题;F1 是精确率和召回率的调和平均,对某类综合表现给一个分数。整理成表更直观:
| 指标 | 含义 | 在垃圾分类里的意义 |
|---|---|---|
| 准确率 | 全部预测中正确的比例 | 整体是否可用 |
| 召回率 | 该类实际样本中被正确识别的比例 | 某类是否被系统性漏掉 |
| F1 | 精确率与召回率的调和平均 | 对低召回率类给出综合评价 |
我拿到matrix.xls后习惯先按行找召回率最低的类别。如果某一类召回率比其他类低 15 个百分点以上,第一件事是回去数一下该类的样本量。样本少于 50 张的类别,训练时很难学到稳定特征,这时候不是调模型,而是补数据。另一个常见做法是把classification_report输出到另一张表,每类一行,方便写进课程设计报告。
5. 避坑排查:TensorFlow 2.3 环境与界面调用的五个高频问题
这个包要完整复现,我的建议顺序是:环境 ->train_mobilenet.py->test_model.py->matrix.py->window_trash.py。五个环节最容易翻车,每条都按现象、原因、解决来写。
5.1 现象1:TensorFlow 装好后 import 直接报错
现象:按代码里的import tensorflow.keras运行,提示module 'tensorflow' has no attribute 'keras'或类似错误。
原因:pip默认装的是比 2.3 新得多的版本,新版 TensorFlow 把 Keras 接口拆到了独立的tf_keras包里,老代码不再直接兼容。
解决:锁版本安装,固定成 TF2.3:
python -m venv venv_trash source venv_trash/bin/activate # Windows: venv_trash\Scripts\activate pip install tensorflow==2.3.0 numpy pandas matplotlib opencv-python scikit-learn xlwt PyQt5这个包基于 tf2.3 写的,锁版本最省事。注意一点:如果你机器没有 NVIDIA 显卡或不想配 CUDA,就装tensorflow-cpu==2.3.0,否则 TF2.3 的 GPU 版会对 CUDA 10.1 和 cuDNN 7.6 严格匹配,版本不对会在训练第一行就报底层库错误。
5.2 现象2:训练时报 labels 维度不匹配
现象:model.fit刚开始,报错内容是labels的形状与categorical_crossentropy不匹配,或者在Dense层输出维度上直接对不上。
原因:最常见的是Dense(num_classes)里的num_classes写死了,而实际images/下的子目录数量和类别数与写死的值不一致。
解决:训练脚本里用代码动态获取类别数,并在编译前打出来确认:
num_classes = len(train_gen.class_indices) print("classes:", train_gen.class_indices) assert num_classes > 1, "images/ 下至少需要两个类别子目录"class_indices这个字典就是训练时类别名到数字编号的映射,务必在训练时打印并保存下来,后面测试和界面都要复用它。
5.3 现象3:验证精度挺高,但window_trash.py一张图都测不对
现象:训练阶段明明显示验证精度 0.9,但把同一张图丢进窗口程序,预测结果完全不对,而且是固定的某个类。
原因:类别映射不一致。训练时class_indices是按flow_from_directory遍历目录顺序生成的,而测试脚本里往往硬编码了一个class_names = ['glass', 'metal', ...],两者顺序一旦错位,所有预测结果都偏到别的类上。
解决:训练完把类别顺序落盘成一个 JSON,训练、评估、界面通用同一个文件:
import json with open("classes.json", "w", encoding="utf-8") as f: json.dump(list(train_gen.class_indices.keys()), f, ensure_ascii=False)测试脚本里读取这个文件,而不是手写列表。这一步能避免大量错位问题,属于课设工程里最容易忽略的隐性问题。
5.4 现象4:中文路径和中文标签导致读图失败
现象:images/下用了中文子目录名,OpenCV的imread返回None,或者matplotlib画混淆矩阵时中文标签显示成方块。
原因:OpenCV 在 Windows 下对中文路径支持差,cv2.imread拿到中文路径直接失败;matplotlib 默认字体不含中文字符。
解决:一是目录和标签统一用英文;二是界面代码里绕开原生imread,改用numpy读字节再解码:
import cv2 import numpy as np data = np.fromfile(img_path, dtype=np.uint8) img = cv2.imdecode(data, cv2.IMREAD_COLOR)np.fromfile按二进制读入文件,cv2.imdecode再从内存解码,中文路径问题就绕过去了。这个技巧在 PyQt5 界面里几乎必用。
5.5 现象5:点击“识别”按钮后界面卡死或白屏
现象:窗口能打开,但点按钮后整个窗口无响应,过一会儿弹“未响应”,或者图片不显示。
原因:PyQt5 的主线程里直接跑了model.predict(),推理阻塞了事件循环;还有一种情况是按钮回调里load_model路径写错,模型没加载成功但异常被吞了。
解决:先用test_model.py单独验证模型路径和推理结果,确认没问题再接界面。界面侧把推理放到子线程:
from PyQt5.QtCore import QThread, pyqtSignal class PredictThread(QThread): done = pyqtSignal(str) def __init__(self, model, img_path): super().__init__() self.model = model self.img_path = img_path def run(self): prob = predict(self.model, self.img_path) self.done.emit(f"类别:{prob['label']},置信度:{prob['score']:.2f}")注意一个细节:模型实例在创建线程时传进去,不要在run()里再load_model,加载模型本身也很耗时,放在子线程里会让“点按钮”和“出结果”之间出现很长时间的假死感。
6. 进阶自测:把模型接进 PyQt5 界面做端到端验证
6.1 先从一条命令行开始:test_model.py 改出 top3 输出
拿到这份源码,我建议第一件事不是打开窗口程序,而是先把test_model.py改成能输出 Top3 置信度的版本。这样能从命令行快速确认模型是否真的可用:
def predict_top3(model, img_path, class_names, top_k=3): img = load_img(img_path, target_size=(224, 224)) arr = img_to_array(img) / 255.0 arr = np.expand_dims(arr, axis=0) pred = model.predict(arr, verbose=0)[0] idx = pred.argsort()[::-1][:top_k] for i in idx: print(f"{class_names[i]:12s} {pred[i]:.4f}")看输出时重点观察最高置信度。如果某张图的最高分只有 0.35,模型实际上是在“猜”,这时候就算界面显示了一个类别,结果也不可信。我在课设里习惯把 0.5 作为阈值,低于这个值就显示“无法判断”,这个细节在答辩时很加分。
6.2 界面接入的三个固定动作
window_trash.py的改造就三步:第一步,按钮回调里用QFileDialog.getOpenFileName拿到图片路径;第二步,把路径交给predict_top3函数;第三步,用 QLabel 显示结果文本,用小一点的 QLabel 显示原图缩略图。注意 OpenCV 读出来是 BGR 通道顺序,显示到 QLabel 前要先转成 RGB 再转成 QImage,否则颜色会整体偏蓝。这个通道顺序问题不解决,界面显示出来的垃圾图片颜色会失真,影响判断。
6.3 留一个冒烟测试脚本
最后我建议在这个包里补一个smoke_test.py,把“加载模型 - 读测试图 - top3 输出 - 断言最高置信度大于阈值”串起来跑一遍。每次改完代码后执行一次就能确认流水线还通着,不用反复开窗口点按钮。这套源码包做课程设计足够扎实,训练、评估、界面三条线完整,需要时直接下载后按上面的顺序对照复现。从那以后我每次拿到课设包,都强制走一遍“单测脚本 -> 界面回调 -> 真实图片验证”的顺序,再回去调参数,白屏和错标签的返工确实少了很多。希望帮到你。
本文还有配套的精品资源,点击获取