简介基于深度学习的垃圾分类目标检测毕业设计项目完整可运行源码与答辩演示文稿一并打包。面向计算机相关专业正在准备毕业设计、课程设计或期末大作业的学生以及需要项目实战练习的开发者覆盖深度学习模型、前端界面与后端服务可直接用于学习和二次开发。压缩包共一百二十六个文件约六十六兆字节类型涵盖二十份Python脚本、十三份Jupyter Notebook、十二份Vue页面、六份JSON配置、六份HTML、五份Markdown、三份Word文档、三份PDF及答辩演示文稿等兼顾后端逻辑、前端界面、模型文件与设计文档。项目内附参考报告、课程实践模板与深度学习实践大作业文档结构清晰从数据到部署路径完整。目前已有两百人学习使用对希望快速上手深度学习目标检测项目的人有较高参考价值。1. 这套系统到底在做什么从一张垃圾照片到识别结果的全流程毕业设计拿到“基于深度学习的垃圾分类目标检测系统”这个题目时很多人的第一反应是“这不就是图像分类吗”——恰恰是这里最容易跑偏。图像分类只回答“图里是什么”而目标检测还要回答“东西在哪儿”输出的是类别标签加边界框坐标。真实场景里垃圾不会整整齐齐摆在画面中央等人拍可能一个塑料袋里混着瓶子、纸巾和果核这时分类模型整图给一个标签几乎没有实用价值检测模型则能把每个目标单独框出来。所以这个题目虽然挂着“垃圾分类”的名字本质是目标检测模型的训练、部署与系统集成数据标注、YOLO选型、训练调参、推理接口、可视化界面再加一份能撑住答辩追问的PPT。对0基础的纯小白来说最大的风险不是“不会”而是不知道每一步该用什么工具、踩坑时看哪里。这篇笔记就按我实际做项目的顺序把这套流程拆开讲透。2. 数据与环境准备类别体系、标注格式与深度学习环境配置2.1 先定类别体系分四分类还是细分物品决定了整个项目的标注成本垃圾分类检测的第一步不是装环境而是定“检什么”。常见做法是两种一种按回收场景分四大类可回收、有害、厨余、其他另一种按具体物品分细类塑料瓶、易拉罐、废纸、玻璃瓶、电池、果皮、菜叶等。取决于你的毕设定位——如果是系统演示四分类的标注门槛低模型容易收敛如果想拿高分细分类更能体现检测目标数量的对比效果但标注工作量会翻几倍。我的建议是取中间值选 610 个常见物品类既能体现多类别检测能力又不会让标注把人拖垮。类别确定后立刻写进一个 yaml 文件因为 YOLO 系列训练时所有类别顺序以这个文件为准。顺序一旦定下来后续标注和训练都不能再乱动否则模型会悄悄把“瓶子”当“电池”学。下面是一个典型的类别配置文件# garbage.yaml # 训练集和验证集的图片路径建议写绝对路径避免相对路径引发“找不到图片”的坑 path: E:/garbage_detection/dataset train: images/train val: images/val # nc 必须与下面 names 列表的长度完全一致 # names 的索引从 0 开始标注文件里的第一个数字就对应这里的下标 nc: 6 names: 0: bottle # 塑料瓶 1: can # 易拉罐 2: paper # 废纸 3: glass # 玻璃瓶 4: battery # 电池 5: leftover # 果皮菜叶这段配置里的nc和names重要性容易被忽略。训练时模型不读“类名”只读索引标注文件的第一列是数字 05对应 names 里的顺序。如果后来想在 names 中间插一个新类别前面所有标注文件的索引就全错位推理结果也会乱套。所以第一次写这个文件时就要想清楚后面只追加在末尾不要插入。2.2 标注格式转换把 VOC 的 XML 转成 YOLO 的 TXT四个字段一个都不能错公开的垃圾检测数据集大多给的是 VOC 格式每张图一个 XML 文件而 YOLO 训练要求每张图配一个同名 TXT每行是class x_center y_center width height坐标都归一化到 [0,1]。第一次做转换时最容易犯的错就是忘了归一化直接把像素坐标写进去结果训练 loss 狂降但检测框全是歪的。下面这段脚本读取 XML 并输出 YOLO 格式的 TXT是我每次做新数据集都会改一改就用的模板# voc2yolo.py import os import xml.etree.ElementTree as ET def convert_one_xml(xml_path, out_dir, class_map): tree ET.parse(xml_path) root tree.getroot() # 图片宽高必须从 XML 的 size 节点读取不能自己猜 img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_map: # 不在类别表里的目标直接跳过不要报错中断 continue # 左上角和右下角坐标 x1 float(obj.find(bndbox/xmin).text) y1 float(obj.find(bndbox/ymin).text) x2 float(obj.find(bndbox/xmax).text) y2 float(obj.find(bndbox/ymax).text) # 转成中心点 宽高的归一化形式 cx (x1 x2) / 2 / img_w cy (y1 y2) / 2 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h # 注意推理和训练对坐标的解释都依赖这 4 个数任何一项越界都要警惕 w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) lines.append(f{class_map[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) # TXT 文件名必须和图片文件名一致否则训练时无法找到对应标注 base os.path.basename(xml_path).replace(.xml, .txt) with open(os.path.join(out_dir, base), w) as f: f.write(\n.join(lines))这段脚本的逻辑说明核心是把xmin/ymin/xmax/ymax换算成中心点坐标和宽高再除以图片宽高完成归一化。参数上最关键的是img_w和img_h它们必须来自 XML 里记录的原始尺寸而不是代码里指定的某个固定值因为数据集中图片大小通常不一致。class_map是类别名到索引号的字典比如{bottle: 0, can: 1}。我在脚本里加了w min(max(w, 0.0), 1.0)这一行是因为个别标注框会轻微越界超出边界会导致训练时计算 loss 出现 NaN 或异常框。如果转换后某张图片没有任何标注行训练时会当作背景图跳过建议顺手打印一个统计确认没有大批量空文件。2.3 环境配置适合0基础纯小白的 conda 创建到 ultralytics 安装环境配置是很多新手第一个劝退点实际上按固定顺序操作十分钟就能跑通。我自己惯用的是 conda 建独立环境避免后面做其他项目时依赖打架。下面是干净的环境准备命令# 1. 创建独立环境指定 Python 版本不要混用系统自带 Python conda create -n garbage_det python3.9 -y # 2. 激活环境 conda activate garbage_det # 3. 安装 PyTorch CPU/GPU 版本 # 有 Nvidia 显卡就先装 CUDA 版没有就装 CPU 版也能跑通全流程 conda install pytorch torchvision torchaudio pytorch-cuda11.8 -c pytorch -c nvidia -y # 4. 安装目标检测训练库 pip install ultralytics # 5. 安装后续要用的辅助库 pip install flask opencv-python这里的参数说明Python 3.9 是目前和 PyTorch、ultralytics 兼容最稳的版本太新反而容易遇到某些算子不支持pytorch-cuda11.8对应的是 CUDA 11.8 驱动如果你的显卡驱动较新装 CUDA 12.x 也行但训练效果没有本质差别。装完后立刻在 Python 里 import 验证python -c import torch, ultralytics; print(torch.__version__)如果这行能打印出版本号说明环境没有缺胳膊少腿。装库卡住时很多人反复重装 PyTorch其实大多数问题是 conda 环境没激活或者 pip 和 conda 混用导致的。统一用 conda 装 PyTorch、用 pip 装其余库依赖冲突的概率最低。3. 模型选型与训练为什么用 YOLO 而不是 Faster R-CNN超参数怎么调3.1 选型对比从 YOLOv8n 到 YOLOv11先看你手上有多少显存和时间目标检测模型里毕业设计最常用的是 YOLO 系列原因很实际ultralytics 库封装好了训练、验证、导出、推理全链路且一直保持活跃更新。相比 Faster R-CNN 的两阶段结构YOLO 是单阶段检测速度和部署复杂度都占优势相比 DETR 这类基于 Transformer 的模型YOLO 对训练数据量和调参经验的要求低很多。也就是说在有限的算力和有限的毕设时间内YOLO 是最不容易翻车的主力方案。至于具体版本YOLOv8 生态最成熟教程多、报错少YOLOv11 在 ultralytics 里同样一行代码就能训练检测头做了优化但新手遇到异常报错时能查到的资料还相对少。我一般给纯小白的建议是先用 YOLOv8n 跑通全流程再根据效果换 v8s 或者升到 v11不要一上来就堆大模型。下面是几个版本在垃圾分类场景下的粗略选择参考具体以你的显卡显存为准模型版本参数量量级适合显存适用场景YOLOv8n / YOLO11n最小4GB 可跑快速验证流程、CPU 也能勉强推理YOLOv8s / YOLO11s较小6GB 以上精度和速度平衡毕设首选YOLOv8m / YOLO11m中等8GB 以上追求更高精度但训练时间长YOLOv8l / x较大12GB 以上数据量大时再考虑否则容易过拟合注意参数量的单位很容易被误解模型文件里写的“MB”是存储体积而不是参数量YOLOv8n 的参数量大约在 3M 上下训练产物best.pt有十几 MB 是因为还保存了优化器状态和结构信息。答辩时如果被问“模型多大”直接说参数量而不是文件大小会显得更专业。3.2 训练脚本数据集划分、模型加载与关键超参数的设置数据准备好之后先把训练集和验证集分开。YOLO 的习惯是图片放images/train和images/val标注放labels/train和labels/val名称一一对应。划分脚本很简单但要保证按文件列表移动而不是直接复制整个文件夹否则验证集里混进训练集图片mAP 会虚高答辩现场演示时露馅。以下是划分逻辑# split_dataset.py import os import random import shutil source_images dataset/images_all source_labels dataset/labels_all # 先收集所有图片名不含扩展名 all_files [f.split(.)[0] for f in os.listdir(source_images)] # 固定随机种子保证每次运行划分结果一致 random.seed(42) random.shuffle(all_files) # 前 85% 作为训练集后 15% 作为验证集 split_idx int(len(all_files) * 0.85) train_files all_files[:split_idx] val_files all_files[split_idx:] def move_files(file_list, split): for name in file_list: # 图片可能是 .jpg/.jpeg/.png需要找到实际存在的后缀 for ext in [.jpg, .jpeg, .png]: img_src os.path.join(source_images, name ext) if os.path.exists(img_src): shutil.move(img_src, fdataset/images/{split}/) break # 标注统一是 .txt lbl_src os.path.join(source_labels, name .txt) if os.path.exists(lbl_src): shutil.move(lbl_src, fdataset/labels/{split}/) move_files(train_files, train) move_files(val_files, val) print(f训练集 {len(train_files)} 张验证集 {len(val_files)} 张)这段脚本的逻辑说明random.seed(42)是为了复现论文或答辩里如果你想说“实验可复现”固定随机种子就是一个证据移动文件而不是复制既省空间也避免了同一张图同时出现在训练和验证集里。脚本里对图片后缀做了三种常见格式的尝试因为数据集中 jpg 和 png 混用很常见直接按固定后缀拼接会漏文件。划分比例上 85% / 15% 是我常用的值数据量少于 2000 张时可以用 90% / 10%给训练尽量多看几张图。接下来是训练脚本本身。用 ultralytics 的 Python API 比命令行更容易写注释和记录实验配置# train.py from ultralytics import YOLO # 加载预训练权重 # 首次运行会自动下载 coco 预训练权重网络不好时可以手动下载后放到当前目录 model YOLO(yolov8n.pt) # 开始训练 results model.train( datagarbage.yaml, # 数据配置文件路径 epochs100, # 训练轮数垃圾检测 100 轮基本够用 imgsz640, # 输入图片缩放尺寸越大越吃显存 batch8, # 批大小显存不足时优先调小这个值 lr00.01, # 初始学习率数据少时建议降低到 0.005 patience15, # 连续 15 轮验证指标不提升就早停 device0, # 使用第一块 GPUCPU 训练则改成 devicecpu workers2, # 数据加载线程数Windows 下建议不要设超过 4 )参数说明要分优先级batch是第一个要调的参数8GB 显存跑 YOLOv8nbatch8没问题换到 v8s 可能就要降到 4imgsz影响精度和显存的平衡640 是 YOLO 系列的默认训练尺寸不要为了省显存随便降到 320框较小的垃圾会直接特征丢失patience15是防过拟合的保险丝后期 loss 不再下降时自动停止避免浪费时间也避免在训练集上死磕导致泛化变差。Windows 下如果workers大于 4有时会报 DataLoader worker 错误这是多线程在 Windows 的兼容性问题降到 2 就行。3.3 训练结果判读val loss、mAP50 和过拟合的三种表现训练结束后ultralytics 会在runs/detect/train/下生成一堆结果文件新手最常见的误区是只看results.csv里的 loss而不看验证集指标。判断模型好坏按优先级看三件事第一val/box_loss和val/cls_loss是否持续下降并趋于平稳。训练集 loss 降得再低都不代表泛化能力验证集 loss 才是真实水平。第二训练结束跑一遍内置验证输出每类的 mAP50 和 mAP50-95在分类别表格里一眼能看到哪一类拖后腿。第三打开val_batch0_pred.jpg这样的可视化图片直接看检测框贴得准不准。loss 数字好看但框全偏的时代不少见可视化检查是最快的方式。下图就是每类指标的典型输出片段来自results.csv的摘要信息Class Images Instances Box(P) mAP50 mAP50-95 all 150 612 0.913 0.887 0.652 bottle 150 98 0.941 0.923 0.701 can 150 76 0.902 0.871 0.624 paper 150 134 0.886 0.854 0.603 glass 150 71 0.901 0.868 0.611 battery 150 87 0.832 0.794 0.532 leftover 150 146 0.915 0.902 0.674表中的Box(P)是精确率mAP50是 IoU 阈值 0.5 下的平均精度。battery明显低于其他类说明电池这类样本要么数量不够要么外观变化太大。对应做法不是盲目调学习率而是回数据集里看电池的标注图——如果有大量小电池目标做数据增强或补充电池的近景图比调参更有效。看到验证集 mAP50 超过 0.85对毕设演示来说就是及格线以上了不用再无限追高。4. 把模型变成一套能演示的系统Flask 推理接口、可视化界面与答辩PPT素材4.1 用 Flask 封装检测接口单张图片上传与结果 JSON 返回训练完的模型还只是 .pt 文件答辩时总不能每次都在命令行里跑推断。常见做法是用 Flask 写一个轻量 HTTP 服务前端传图、后端调 YOLO 推理、返回标注框坐标和类别。这是整个系统最显眼的交付物也是答辩评委“能现场演示吗”那一步的关键。下面是一个可直接用的接口代码# app.py import cv2 import numpy as np from flask import Flask, request, jsonify from ultralytics import YOLO # 加载训练好的权重 model YOLO(runs/detect/train/weights/best.pt) app Flask(__name__) app.route(/detect, methods[POST]) def detect(): # 接收前端上传的图片文件 file request.files[image] # 读成 OpenCV 格式 image_bytes np.frombuffer(file.read(), np.uint8) img cv2.imdecode(image_bytes, cv2.IMREAD_COLOR) # 推理置信度阈值设 0.45低于这个值的结果不返回 results model.predict(img, conf0.45, imgsz640, verboseFalse) boxes [] for r in results: for box in r.boxes: # 提取坐标、置信度、类别索引 x1, y1, x2, y2 box.xyxy[0].tolist() conf float(box.conf[0]) cls_id int(box.cls[0]) boxes.append({ bbox: [round(x1, 2), round(y1, 2), round(x2, 2), round(y2, 2)], confidence: round(conf, 3), class_id: cls_id, class_name: r.names[cls_id] }) # 返回 JSON 给前端绘制 return jsonify({count: len(boxes), boxes: boxes}) if __name__ __main__: # 监听所有网卡方便局域网内演示端口 5000 被占用时改成 5001 app.run(host0.0.0.0, port5000)这段代码的逻辑说明核心是model.predict(img, conf0.45)conf参数决定检测框的输出门槛保洁场景里垃圾不能漏0.45 是平衡误检和漏检的常用值如果发现一堆无关框就往上调到 0.5 或 0.55。r.boxes里xyxy是左上和右下角像素坐标前端画框时直接用不用再做归一化换算。verboseFalse是关掉 ultralytics 推理时的控制台刷屏不然每来一张图就打印一堆日志调试时干扰很大。host0.0.0.0是为了答辩时能用手机或另一台电脑访问如果只在本机演示改成127.0.0.1更安全。启动服务后用 curl 简单测一发curl -X POST -F imagetest.jpg http://127.0.0.1:5000/detect如果返回的 JSON 里有count和boxes说明接口链路是通的。这一步验证完成后再去写前端否则前端画完框发现接口没通排查范围会扩大两倍。4.2 可视化界面在图片上画检测框附带每帧类别统计纯 JSON 不够直观答辩演示需要“看得见”的结果。有两种主流做法一种是写一个 HTML 页面上传图片后调用上面的接口用 Canvas 或 SVG 画框另一种是直接用 OpenCV 做实时视频窗口每帧检测并画框。前者更适合现场演示——稳定、不回受摄像头光线影响后者更适合展示“实时性”这个加分项。我先说视频方案因为它的代码量更少也更抓眼球# webcam_demo.py import cv2 from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) # 打开摄像头0 代表默认摄像头外接摄像头可能是 1 cap cv2.VideoCapture(0) if not cap.isOpened(): print(摄像头打开失败检查设备索引) exit() while True: ret, frame cap.read() if not ret: break # 每帧做一次推理 results model.predict(frame, conf0.45, imgsz640, verboseFalse) annotated results[0].plot() # 直接画出标注后的画面 cv2.imshow(Garbage Detection, annotated) # 按 q 键退出循环 key cv2.waitKey(1) 0xFF if key ord(q): break cap.release() cv2.destroyAllWindows()这里的核心是results[0].plot()它会自动在原始帧上画好检测框、类别名和置信度不用自己用cv2.rectangle逐条画。这个做法省事但也意味着框的样式是内置的如果想定制颜色或字体就得自己遍历boxes画。实时演示时最容易翻车的点不是代码而是 OpenCV 打开摄像头失败——笔记本摄像头通常为 0外接摄像头是 1 或 2多准备一个cap.get(CAP_PROP_FRAME_WIDTH)打印来确认分辨率也是一个习惯。4.3 答辩PPT的素材组织一张架构图胜过十页文字很多人的评分差距不在项目本身而在 PPT 有没有把工作量讲清楚。标题里的“源码答辩PPT”说明这套交付很重要但 PPT 的目的是辅助你讲逻辑不是把代码贴上去。按我搭过多次毕设 PPT 的经验5 类素材必须提前准备好系统整体架构图数据标注 → 模型训练 → 服务部署 → 前端展示 的四层结构数据集的构成说明类别数、图片数、训练/验证划分模型训练的关键曲线截图val loss 曲线和 mAP 曲线效果对比表不同模型版本在验证集上的 mAP现场演示路径摄像头或上传图片的入口和预期结果。架构图建议用 Visio 或 draw.io 画不要贴论文里的示意图让别人猜。PPT 的顺序建议按“痛点 → 方案 → 实验 → 演示 → 总结”推每页只讲一个点。导师问“为什么用 YOLO”时你要答的是“因为单阶段检测在保持精度的同时推理更快适合部署到演示环境”而不是“因为源码多”。另外把训练过程中试错的记录留几页作为备选材料比如“最初用 CPU 训练单轮耗时过长改 GPU 后训练时间缩短 60%”这种过程性描述在答辩里通常会被认为是真实的工程经验。5. 避坑指南垃圾检测实战里最常踩的五个坑与排查路径5.1 类别文件索引错位损失函数一路下降预测结果却乱七八糟现象训练过程 loss 曲线几乎完美下降但推理时模型把塑料瓶认成果皮、把易拉罐框到完全无关的位置置信度还特别高。原因这是所有训练流程里最隐蔽的错误。标注时用的class_map顺序和训练 yaml 里的names顺序不一致。比如标注时把bottle记为 0训练 yaml 里 0 却是can模型实际学到的是“第 0 类长成瓶子那样”推理输出第 0 类时前端按 names 显示成易拉罐。由于 YOLO 训练本身完全依赖索引它不会报错甚至因为标签内部一致性而正常收敛。解决办法很简单但需要从头确认检查一张标注 TXT 的第一列数字再对照garbage.yaml的 names 字符串确保含义一致。解决写一个小脚本把数据集里每个类别的标注数量按索引汇总和names列表逐项核对。如果发现某个索引的样本数和预想不符说明哪里乱了这时宁愿重新生成标注也不要手动改索引。5.2 训练时报 CUDA Out of Memory不要先换大模型先调 batch现象输入model.train(...)后大约几秒控制台报CUDA out of memory进程直接退出。原因显存被模型权重、中间特征图和反向传播梯度共同消耗。YOLOv8s 在imgsz640、batch16下大约需要 8GB 以上显存很多笔记本显卡只有 4GB 或 6GB。但新手往往第一个念头是换更小的模型其实换模型只解决了一半问题调 batch 就能立刻缓解。解决按优先级依次处理batch2或batch4重开训练imgsz640降为 480前提是目标框不过小注册机即关闭训练时的 AMP 混合精度试试最后才考虑换 YOLOv8n。另外 Windows 下注意关闭其他占用显存的程序浏览器开一堆视频页面也能吃几百 MB 显存。如果以上都试了还是 OOM把devicecpu跑了速度慢但毕设绝对能出结果。5.3 验证集 mAP 不涨损失函数在最后 20 轮反复震荡现象前 60 轮 val loss 持续下降之后不再下降甚至轻微上升mAP50 在 0.7 附近反复横跳精确率上不去。原因三种情况按频率排——学习率过大导致参数在最优点附近震荡训练数据里有大量错标或漏标数据集本身太小模型开始过拟合。是哪种情况需要打开results.csv看验证集的类别 loss如果只有个别类在涨大概率是数据问题如果所有类一起涨大概率是学习率或过拟合。解决先把lr0从默认 0.01 降到 0.005 重新训练观察前 15 轮曲线是否更平滑如果还震荡检查patience触发的早停轮次早停也有助于防止过拟合。数据问题则回到 LabelImg 或脚本里确认是否存在框的中心点落在图片外的情况这是 YOLO 训练时的致命缺陷之一。如果数据量少于 800 张优先加数据增强而不是加模型深度。5.4 带玻璃反光的目标普遍漏检不是模型不行是训练样本覆盖不够现象验证集里其他类 mAP50 都在 0.85 以上唯独玻璃瓶只有 0.55 左右打开预测图发现反光强烈、背景复杂的瓶子全部没框出来。原因玻璃瓶的特征高度依赖背景透过来的颜色和光线训练集里如果大多是白墙背景的瓶子验证集里换成深色桌面就全崩。这与模型结构无关是典型的训练集与真实场景分布不一致也就是俗话说的“黑匣子现象——输入一变输出就玄学”。解决给玻璃类单独补充 100200 张不同光线、不同背景的样本这是最直接有效的办法。不想花时间拍照就用数据增强里的hsv_h和hsv_s随机改变色相与饱和度模拟不同光照。但它不是万能药如果增强幅度过大会把瓶子的形状特征也改坏所以增强幅度要控制在 0.015 以内。5.5 conda 环境反复崩pip 和 conda 混装同一个包导致依赖冲突现象import torch报错或者 training 时找不到torch.cuda而 conda list 里明明有 torch。原因最常见的是先用 conda 装了 PyTorch后来又用 pip 装一个依赖库时pip 把 torch 版本覆盖成另一个编译版本。OpenCV 的某个旧版本、numpy 的新版本都可能触发这类连锁冲突环境一旦进入这种状态再补装任何包都可能是拆东墙补西墙。解决新建一个全新环境从头按顺序装先 conda 装 PyTorch再 pip 装 ultralytics最后装 opencv-python 和 flask。装完一条命令验证python -c import torch; print(torch.cuda.is_available())。另外养成一个习惯每次训练跑出新结果后用pip freeze requirements.txt导出环境依赖换机器时照着装就能复现不再吃环境翻车的苦。6. 从能跑通到答辩拿高分迁移学习微调、模型量化与验证习惯先别急着把项目收尾这里还有三个相对容易实现但很能体现工程深度的动作。第一个是用你的垃圾数据集在更大的预训练模型上做迁移学习微调。前面训练直接用了yolov8n.pt在 COCO 上的预训练权重这已经是迁移学习了但更进一步的做法是先用 yaml 里的老模型权重做初始化再在专用数据集上继续训 30 轮冻结前面的骨干层只训练检测头。代码上用model YOLO(runs/detect/train/weights/best.pt)继续训练即可关键是学习率要降通常lr00.001否则前面的损失会瞬间冲高。这个动作在答辩里对应“如何利用预训练模型降低对标注数据量的依赖”是标准加分回答。第二个是模型量化。训练完的模型转成 ONNX 或 TensorRT 后推理速度会有肉眼可见的提升。以下是把模型导出成 ONNX 的命令答辩演示时如果现场机器没有 GPU 也能跑得更快# export_onnx.py from ultralytics import YOLO # 加载训练好的最佳权重 model YOLO(runs/detect/train/weights/best.pt) # 导出 ONNX 格式imgsz 必须和推理时一致 model.export(formatonnx, imgsz640, halfTrue)导出后得到的best.onnx可以用 onnxruntime 加载在 CPU 上的推理速度一般比原始 PyTorch 模型快一倍左右。在答辩 PPT 里放一张“原始模型 vs ONNX 模型推理耗时”的柱状对比这页的含金量比任何口头解释都高。第三个是我自己吃亏后养成的验证习惯训练完成后不要只看验证集 mAP要专门挑 1020 张不在训练集里、甚至是从网上找的“刁钻角度”垃圾图片做盲测。比如从侧面拍的、光线很暗的、多个垃圾互相遮挡的。把这些图做成一个tough_test文件夹每次模型迭代后用前面 Flask 接口批量跑一遍记录检测数和错检数。模型在标准验证集上分数高但盲测效果差说明过拟合了反过来盲测效果好才是真的泛化。这个习惯帮我避开过好几次“答辩前一晚才发现模型换了一组实验参数就废了”的险境。答辩前一周再检查一遍这几个点摄像头检测流程是否顺畅Flask 服务的端口是否被别的进程占用PPT 里的架构图和代码实际逻辑是否一致。我自己就犯过 PPT 里画了三层架构、代码里却是单文件的低级错误被评委直接指出来场面相当难熬。把验证流程固定下来别临时抱佛脚。希望帮到你。本文还有配套的精品资源点击获取