ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于YOLOv8深度学习的垃圾分类目标检测系统完整实战

基于YOLOv8深度学习的垃圾分类目标检测系统完整实战 简介这份资源是一套基于深度学习的垃圾分类目标检测系统完整源码面向毕业设计、期末大作业与课程设计等场景解决图像中垃圾类别识别与定位的问题。代码包含较详细的注释并整合Docker部署配置、前端展示页面、模型YAML配置与训练笔记即使新手也能较快理解网络结构、训练流程与推理逻辑。压缩包共117个文件核心包含27个Python程序文件、18个YAML配置、10个JSON数据文件以及小程序前端JS/WXML/WXSS、HTML页面、图片素材和Markdown文档另有Shell脚本、Dockerfile与Jupyter Notebook等部署和说明材料从数据读取、模型训练、推理检测到结果可视化的各个模块均有覆盖整体约7.33MB目录按功能划分便于检索和二次开发。系统是作者手打并获得98分的项目已有369人学习下载适合作为项目答辩、毕业设计演示的参考也可基于其工程结构扩展新的检测类别或对比不同模型。1. Python垃圾分类目标检测系统这门“高分大作业”到底在做什么拿到“基于Python深度学习的垃圾分类目标检测系统”这种题目很多人的第一反应是去GitHub上找个仓库跑通交差。但真正决定这门大作业能不能拿高分的不是模型选得多新而是你能不能把数据、训练、界面、报表这条链路完整串起来讲清楚。这套系统本质是一个目标检测工程化项目用深度学习模型在图片中找出可回收物、有害垃圾、厨余垃圾和其他垃圾四类目标输出位置框、置信度并配一个能演示的图形界面。它解决的是课程设计和毕业设计里最实际的需求——在算力一般的电脑上用两周到一个月时间做出一个“能演示、能讲清、能回答老师追问”的完整系统。适合没系统做过深度学习项目但需要交源码和报告的同学。2. 数据集是最大的坑YOLO标注格式、data.yaml配置与最小推理验证很多人下载数据集时只看“垃圾分类”四个字就往项目里塞这是整个大作业里最常见的翻车点。网上下载的“垃圾分类数据集”大多是按类别文件夹整理的分类数据集图片里有垃圾但没有目标框拿来训练目标检测训练日志里刷满“no labels found”。所以动手敲训练命令之前先把数据摸清楚这一步花半天后面能省三天。2.1 先分清分类数据集与检测数据集别拿分类目录直接开训公开能拿到的垃圾分类数据大概有三种第一种是比赛遗留的图片集标签就是类别名只有“这张图是什么垃圾”没有“垃圾在图的哪个位置”第二种是带COCO格式标注的检测数据集比如TACO这类垃圾分析数据集有框但类别特别多、场景杂直接训练会把问题复杂化第三种是已经被整理成YOLO格式的检测数据集图片和同名txt放在对应目录下这种最省事。我的建议是优先找第三种找不到就退一步用第二种然后写个COCO转YOLO的小脚本。如果只有分类图片也不要硬着头皮拿整张图当框去生成标签——那种“假检测框”训练出来的模型演示时框永远套住整张图老师一眼就能看穿。大作业周期短不值得从头标几千张自己用labelImg花半天画上两三百张真实场景图效果比伪造标签好得多。2.2 工程骨架与data.yaml让训练脚本一眼看懂数据集长什么样项目结构建议按“训练、推理、界面”三层拆开别把所有代码堆在一个文件里。这是从业者常用的组织方式也方便答辩时解释模块划分。garbage_detection/ ├── dataset/ │ ├── images/train/ # 训练图片 │ ├── images/val/ # 验证图片 │ ├── labels/train/ # 与图片同名的YOLO标签txt │ └── labels/val/ ├── configs/data.yaml # 数据集配置文件 ├── train.py # 训练入口 ├── predict.py # 命令行推理 ├── ui/ │ ├── worker.py # 推理线程 │ └── main_window.py # PyQt5主窗口 └── runs/ # ultralytics自动生成的训练输出dataset目录下图片和标签按images与labels两个子目录分开是YOLO系列通用的约定。图片文件名和标签txt文件名必须完全一致比如img_001.jpg对应img_001.txt。runs目录不需要手动创建ultralytics训练时自动生成。接着写数据配置文件configs/data.yaml这是训练时唯一需要改的数据入口# configs/data.yaml path: ../dataset # 数据集根目录相对本文件所在目录 train: images/train # 相对path的训练图片目录 val: images/val # 相对path的验证图片目录 names: 0: recyclable # 可回收物 1: harmful # 有害垃圾 2: kitchen # 厨余垃圾 3: other # 其他垃圾names里的id必须与标签txt里每行开头的数字严格对应。这里建议用英文标识因为中文标签在ultralytics的命令行输出和绘图里会出现乱码。界面显示中文时再做一次映射就行不要在yaml里直接写中文。path字段指向数据集根目录下面的train和val都相对它解析。数据准备好后训练前先跑一遍校验脚本把缺标签、标签格式不对的图片排查出来。下面这段是我每次训练前都会先跑的检查逻辑# check_labels.py from pathlib import Path for split in [train, val]: img_dir Path(fdataset/images/{split}) label_dir Path(fdataset/labels/{split}) for img_path in img_dir.glob(*.jpg): label_file label_dir / (img_path.stem .txt) if not label_file.exists(): print(f缺标签: {img_path}) continue for line in label_file.read_text(encodingutf-8).strip().splitlines(): parts line.split() if len(parts) ! 5: print(f格式错误: {label_file}: {line}) continue cls_id, cx, cy, w, h map(float, parts) # YOLO格式的四个坐标值都是归一化的必须在0到1之间 if not (0 cx 1 and 0 cy 1 and 0 w 1 and 0 h 1): print(f坐标越界: {label_file}: {line})这段脚本只扫描jpg后缀如果你的数据是png把glob(*.jpg)改成glob(*.png)或glob(*.jpeg)。坐标越界的标注会让训练时的损失值一开始就异常很多“loss爆炸”其实源头在数据。检查结果如果只有零星几条警告直接删掉对应图片即可如果大量缺标签说明数据集本身不是检测格式回到2.1节的结论重新选数据。2.3 最小推理验证还没训练先让预训练权重跑出一张带框的图环境装好之后别急着训练先用官方预训练权重跑一次推理验证输入输出链路通不通。这一步花不到一分钟却能把“环境问题”和“模型问题”分开排查。# quick_check.py from ultralytics import YOLO # 先用官方预训练权重走一遍完整流程 model YOLO(yolov8n.pt) results model.predict( sourcedemo.jpg, # 任意一张测试图 conf0.35, # 置信度阈值 iou0.5, # NMS的IoU阈值 saveTrue, # 结果自动保存到runs/detect/predict devicecpu, # 没有显卡就写cpu ) print(results[0].boxes.cls) # 打印预测的类别索引初次跑这个脚本ultralytics会自动把yolov8n.pt下载到当前目录文件只有几MB。如果下载失败一般是网络问题重试或手动下载后放到当前目录即可。conf0.35是垃圾检测比较常用的起始阈值垃圾目标外观差异极大弱光、遮挡、小物体都会让置信度偏低阈值设太高会把真框滤掉。saveTrue会把画好框的图片保存到runs/detect/predict目录免去自己写cv2.imshow的麻烦。这一步跑通说明Python环境、ultralytics包、图片读写链路都没问题可以正式进入训练了。3. 用YOLOv8把模型训出来训练入口、4个必调参数与判断收敛的曲线训练阶段是整个项目里最像“黑匣子”的部分。大作业答辩不需要你背YOLOv8的网络结构你需要能说清三件事为什么选这个模型、调了哪几个参数、怎么判断它训好了。评分人真正追问的也是这三件事。3.1 为什么选YOLOv8n而不是更大的模型网上各种项目喜欢堆大模型但大作业场景里“大”不是优势。公开垃圾分类检测数据集的规模通常在几百到几千张数据量小的时候大模型很容易过拟合训练时间还成倍增加。YOLOv8n是整个系列里最小的版本权重文件几MBCPU也能跑推理迁移学习在小型数据集上收敛更稳。从答辩角度讲轻量模型更好讲。你可以说“我选了YOLOv8n因为它参数量小、推理快适合部署在普通电脑上同时用预训练权重做迁移学习在有限数据下达到了够用的精度”。这套话术比“我用了最大的模型”更能体现你对工程边界的理解。如果你的显卡在4GB以上可以把模型换成yolov8s.pt精度略高训练时间也在可接受范围内。显存只有2GB甚至没有独显老老实实用n。3.2 train.py的写法ultralytics环境配置和4个必调参数训练入口我习惯单独建一个train.py理由只有一个训练失败时改参数重来不会污染推理和界面代码。# train.py —— 训练入口 from ultralytics import YOLO if __name__ __main__: # 迁移学习加载官方预训练权重在当前数据集上微调 model YOLO(yolov8n.pt) results model.train( dataconfigs/data.yaml, # 数据集配置 epochs100, # 总训练轮数 imgsz640, # 输入图片分辨率 batch16, # 每批图片数量 lr00.01, # 初始学习率 patience20, # 连续20轮无提升则早停 device0, # 0表示第一块显卡无显卡填cpu workers0, # Windows下先设0避免dataloader崩溃 verboseTrue, # 打印逐轮日志 )这4个参数是大作业里最值得调的也是答辩最可能被问到的参数建议值调整理由epochs50~150小数据集100轮够用再多提升有限还容易过拟合imgsz640默认值降到416可省显存但小目标精度明显下降batch8~32主要受显存限制不追求大16是均衡点lr00.01迁移学习常用值loss发散时降到0.005重试代码里的if __name__ __main__:不是形式Windows下ultralytics会启动多进程加载数据不写这层保护很容易触发dataloader崩溃。workers0同样是为了Windows兼容Linux和Mac可以改回4或8以加快数据读取。训练输出在runs/detect/train/目录里面有每次实验的权重和曲线图。best.pt是验证集表现最好的模型last.pt是最后一轮的模型后续推理和界面全部用best.pt。3.3 训练跑起来之后日志和曲线该看哪个训练时终端会逐轮打印类似下面的表格epoch、box_loss、cls_loss、dfl_loss、mAP50和mAP50-95数值只是示意格式不是某一轮的真实结果epoch box_loss cls_loss dfl_loss mAP50 mAP50-95 50 1.124 0.812 1.034 0.372 0.181看日志的规则很简单box_loss持续下降且mAP50在涨说明模型还在学继续等验证集损失掉头上涨而mAP50不再涨说明开始过拟合patience早停会自动帮你停下来如果loss一直降不到正常范围同时mAP50在0.1附近抖动大概率是标签问题回到第2章查数据。训练结束后打开runs/detect/train/results.png这张图把损失曲线和mAP曲线全部画在一起是答辩PPT里最适合放的一张图。你能指着它说“这是验证集上的精度变化趋势”比贴命令行日志直观得多。3.4 训练中断和误关终端的后悔药resume续训训练到第70轮时手滑关了终端这几乎是每个跑训练的人都经历过的场景。ultralytics留了官方后悔药用last.pt断点续训。# resume_train.py from ultralytics import YOLO # 从上次中断的位置继续训练无需重填任何训练参数 model YOLO(runs/detect/train/weights/last.pt) model.train(resumeTrue)last.pt会保存训练状态和超参数resumeTrue后自动接续最后一轮的epoch数往下训。注意只能用last.pt续训用best.pt的话相当于重新开始一个没有预训练状态的新训练。训完之后把runs/detect/train/weights/best.pt复制到项目根目录后续所有模块都从固定位置加载模型。4. 把模型包装成“系统源码”统一推理类、PyQt5界面线程和CSV导出报表模型训好只完成了一半“系统源码”意味着要有一套能用鼠标点、能看结果、能导出的产品外壳。界面不需要花哨但交互链路必须完整选图、识别、显示、导出。按“推理层→界面层→报表层”的顺序搭每一层各司其职。4.1 先写一个统一推理类命令行和界面共用同一套逻辑很多人会先在命令行里写一段推理脚本跑通然后复制粘贴到界面代码里结果改阈值时改一处漏一处。正确的做法是先抽一个Detector类命令行和PyQt5界面都调用它。# detector.py from ultralytics import YOLO class Detector: def __init__(self, weights_path, conf0.35): # 模型权重只加载一次常驻内存 self.model YOLO(weights_path) self.conf conf # 英文类别id在这里映射为中文显示名 self.names { 0: 可回收物, 1: 有害垃圾, 2: 厨余垃圾, 3: 其他垃圾, } def detect(self, image): 输入BGR图片返回检测框列表。image是numpy数组。 results self.model.predict(image, confself.conf, verboseFalse)[0] boxes [] for b in results.boxes: boxes.append({ cls: int(b.cls), cls_name: self.names[int(b.cls)], conf: float(b.conf), xyxy: list(map(int, b.xyxy[0].tolist())), }) return boxes推理类把三个变化点收敛在一起模型路径、置信度阈值、类别映射。后续界面里加滑条改阈值只需要改self.conf这一个属性中文类别显示统一从这里取不会出现“界面显示和报表不一致”的情况。verboseFalse避免推理时往终端刷大量日志界面程序会干净很多。4.2 PyQt5界面为什么推理必须放子线程PyQt5界面最常见的翻车方式点击识别按钮后窗口立刻变成“未响应”过几秒才缓过来。原因是模型推理把GUI主线程堵住了。Python的GIL加上深度学习推理耗时主线程被占住界面事件循环自然卡死。解决办法是把推理放进QThread通过信号把结果传回主线程。# ui/worker.py from PyQt5.QtCore import QThread, pyqtSignal class InferenceWorker(QThread): # 子线程推理完成后把结果通过信号交回主线程 result_ready pyqtSignal(list) def __init__(self, detector, image): super().__init__() self.detector detector self.image image def run(self): # QThread启动后自动执行run方法 boxes self.detector.detect(self.image) self.result_ready.emit(boxes)主窗口里的调用方式是固定的# ui/main_window.py 片段 self.worker InferenceWorker(self.detector, current_image) self.worker.result_ready.connect(self.update_ui) # 主线程更新界面 self.worker.start()注意两点。第一不要在子线程里直接改QLabel或QTableWidgetQt的控件只能在主线程操作跨线程改控件轻则刷新异常重则随机崩溃必须用信号槽把数据传回主线程再更新界面。第二每次点击识别都要新建一个InferenceWorker实例不要复用已运行完的worker这是PyQt5线程对象的生命周期约定。模型本身不重复加载Detector在窗口初始化时创建一次。4.3 导出CSV结果报表给答辩留数据支撑演示时截几张图不够老师更愿意看到量化的结果。一个很简单的做法是把每次识别的框导出成CSV一行一个目标字段包含文件名、类别、置信度、坐标。用utf-8-sig编码Excel打开不会乱码。# export_report.py import csv from pathlib import Path all_rows [] for img_path in Path(demo_photos).glob(*.jpg): boxes detector.detect(cv2.imread(str(img_path))) for b in boxes: all_rows.append([ img_path.name, b[cls_name], f{b[conf]:.2f}, # 置信度保留两位小数 *b[xyxy], # 四个坐标值 ]) with open(result_report.csv, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([文件名, 类别, 置信度, x1, y1, x2, y2]) writer.writerows(all_rows)这个报表有双重价值一是论文报告里可以直接当“实验数据”用二是答辩现场如果被问“有没有做过批量测试”把它调出来就是现成证据。建议再加上一个按类别统计数量的小表两行代码就能算出来但能让报表看起来完整很多。4.4 给界面加置信度滑条小改动但演示效果很直接很多大作业界面功能少得可怜选图、检测、结果三个按钮结束。我建议加一个置信度阈值滑条改动量不大但答辩演示时能派生出一个很加分的交互场景。# ui/main_window.py 片段 from PyQt5.QtWidgets import QSlider self.conf_slider QSlider(QtCore.Qt.Horizontal) self.conf_slider.setRange(10, 90) # 映射到0.10 ~ 0.90 self.conf_slider.valueChanged.connect(self.on_conf_change) def on_conf_change(self, value): # 只修改推理类的阈值不触发重新识别 self.detector.conf value / 100 self.conf_label.setText(f置信度阈值: {value / 100:.2f})滑条变化时不要自动重跑推理否则拖动过程中模型反复执行界面照样卡死。正确逻辑是滑动只改阈值点“重新识别”按钮后再跑一次。演示时可以当着老师的面把阈值从0.5拉到0.25屏幕上多出几个原本被滤掉的框顺势讲一句“阈值设定对结果影响很大”这是很有记忆点的互动。5. 大作业避坑清单跑这个项目必踩的5个真实翻车点这一章写五个我见过最多、也最典型的翻车现场。每一条都按“现象→原因→解决”的顺序来先知道是什么症状再谈怎么处理。5.1 ultralytics环境配置pip装完了import还是报错现象按教程执行pip install ultralytics后import ultralytics报错提示numpy版本冲突或者缺少某个依赖。原因系统Python版本太旧或之前装过其他深度学习包把环境搞乱了。ultralytics对Python版本有要求Python 3.7以下、3.12以上的环境都会出现隐性问题。解决先装一个干净的Python 3.10再用虚拟环境隔离项目依赖装包时换国内镜像源提高成功率python -m venv venv venv\Scripts\activate # Windows激活虚拟环境 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple ultralytics虚拟环境是这类项目最简单的后悔药环境坏了直接删掉venv目录重建不用跟系统Python较劲。镜像源地址不唯一选一个你网络环境下速度最快的即可。5.2 Windows下训练一开始就报Dataloader worker崩溃现象训练脚本运行没几秒终端报Dataloader worker (pid(s) X) exited unexpectedly有时直接整个进程退出。原因Windows的多进程启动方式和Linux不同ultralytics的数据加载进程会重新执行主模块。如果训练代码没放在if __name__ __main__:保护之下子进程会把整个脚本重跑一遍产生递归副作用。解决把训练入口完整放进if __name__ __main__:块内同时把workers0从根源上绕开多进程问题。如果不用Windows可以改回workers4以加快数据读取。5.3 训练日志刷屏“no labels found”现象训练一开始ultralytics打印大量警告提示某张图没有标签文件训练还能继续但mAP一直很低。原因图片和标签txt不完全同名或者数据集根本不是检测格式。最常见的场景是下载了只有类别文件夹、没有标注框的数据集训练脚本找不到与图片同名的txt只能跳过这些图。解决先用第2章的校验脚本排查缺标签的图片数量不多直接移出数据集目录如果一半以上的图都缺标签就是数据集选错了。强行用整图假框训练出来的系统演示时框永远覆盖整个画面这种数据造假式的项目经不起答辩追问。5.4 训练完mAP50看着不错演示照片上就是不出框现象验证集mAP50有0.7以上但把自己拍的照片丢进去界面上一个框都不出或者只出几个置信度很低的框。原因可能出在四个地方——用了last.pt而不是best.pt推理时conf阈值设太高演示照片和训练集分布差异太大或者是根本加载错了模型权重文件。解决先把推理入口统一改到best.pt然后把conf降到0.25重新识别。如果还是无框把推理时的置信度全部打印出来看看分布results model.predict(img, conf0.1, verboseFalse) for r in results: print(r.boxes.conf) # 看看最高置信度到底是多少如果最高置信度都在0.3以下说明模型对这类场景本来就没有把握光调阈值解决不了只能补充相似场景的数据重训。如果明明有高置信度的框但界面没显示那就是界面代码的阈值和打印用的阈值不一致检查滑条初始值。5.5 界面一点识别按钮就卡成未响应现象界面启动正常选图正常点击“识别”后窗口立刻白屏或变成“未响应”几秒后恢复但操作很顿。原因模型推理直接放进了主线程或者每次点击都重新执行YOLO(best.pt)加载模型。推理是CPU密集操作主线程被占住界面事件循环就停了。解决按第4章的写法把推理放进QThread模型在窗口初始化时加载一次。基本可以彻底消除卡顿。如果界面还慢检查是不是对超大分辨率图片直接推理可以先缩放再送进模型640像素的推理尺寸和4K原图的检测结果差距很小。6. 答辩前的最后一次验证把检测结果变成能讲清楚的数据答辩前两天别再调模型了再训也提升不了几个点。我做这类项目的习惯是把所有精力放在一次“盲测”上找10到20张训练集和验证集里都没出现过的照片跑一遍完整的推理脚本把结果逐条打印出来。# blind_test.py from pathlib import Path import cv2 detector Detector(best.pt, conf0.35) for img_path in Path(demo_photos).glob(*.jpg): boxes detector.detect(cv2.imread(str(img_path))) summary [(b[cls_name], round(b[conf], 2)) for b in boxes] print(f{img_path.name}: {summary})这组输出就是答辩时最实在的实验数据。老师问“系统效果怎么样”你直接说“我在训练集之外的20张照片上做了盲测其中18张能正确框出所有垃圾目标2张把快递纸箱认成了其他垃圾”这比任何曲线都更有说服力。在此基础上我会再加三个小改造改动量不大但很提升完成度。第一把yaml里的类别英文名和界面中文名的映射放到一个字典文件里推理、界面、报表三处统一读取避免答辩时改一处漏一处。第二把批量测试的结果同时导出CSV并按类别做一次数量汇总“可回收物检出12次、有害垃圾检出3次”这种统计口径老师很爱听。第三把盲测的带框图片保存到项目里的demo_results/目录做PPT时直接取图答辩现场断网也不慌。这套流程走下来你手里会同时有模型、界面、报表、盲测记录四样东西哪怕模型精度只算合格整个系统的完整度也足以支撑你讲满答辩时间。我当年第一次做这类大作业时最大的教训就是把所有时间花在反复重训模型上结果界面粗糙、没有测试数据老师问“你怎么证明它有效”时只能支支吾吾。后来养成习惯答辩前一晚绝不碰训练参数只做一件事把模型拿到新照片上跑一遍看看它在真实场景里到底见过什么、漏了什么然后把这些记录整理成能讲清楚的数据。希望帮到你。本文还有配套的精品资源点击获取
返回列表