尧图精选

YOLOv5s垃圾分类检测全链路工程实践

🕒 发布时间:2026/10/1 19:16:22 📁 来源:尧图网络
简介本资源是一套完整的基于深度学习的垃圾分类目标检测系统实现方案专为本科毕业设计、课程设计及期末大作业打造面向Python初学者与深度学习入门者解决实际场景中垃圾图像识别与定位难题。压缩包共126个文件含20个核心Python脚本含详细注释、13个Jupyter Notebook实验记录、3个PDF/DOCX格式报告文档含参考报告、实践模板与深度学习大作业范例、1个PPTX答辩演示文稿、1个ONNX模型文件及配套部署Dockerfile与YAML配置辅以Vue前端界面、JS交互逻辑与语义化CSS样式整体66.06MB结构清晰、模块解耦。已有251人学习下载项目获导师高度认可评分98分提供从数据预处理、YOLOv5/ResNet等主流模型训练、模型转换到Web/Docker轻量部署的全流程支持附带.browserslistrc、.editorconfig等工程化配置开箱即用适合快速复现与二次开发。1. 垃圾分类目标检测不是“调个YOLO就完事”98分大作业背后的真实工程链路你是不是也试过GitHub搜“垃圾分类 YOLO”clone下来跑train.py结果卡在ModuleNotFoundError: No module named torchvision或者好不容易装完环境detect.py一运行就报CUDA out of memory显存明明还有2GB空着又或者标注好的VOC数据集扔进训练脚本loss降不下去最后发现XML里name写成了kitchen_waste而模型class list里是kitchen——差一个词mAP直接掉12个点。这不是玄学是工程断层。这份98分大作业的真正价值不在“用了YOLOv5”而在它把从数据清洗→模型微调→Docker容器化部署→PPT答辩逻辑链全链路打穿且每一步都带可复现的参数、可验证的中间产物、可回溯的错误日志。它适合三类人毕业设计卡在部署环节的本科生、课程设计被导师质疑“没工程闭环”的研究生、以及想用真实小场景练手YOLO全流程的转行者。它不教你反向传播推导但会告诉你为什么img_size640比416更适合垃圾图像小目标密集背景杂乱为什么--cache必须关掉内存不足时缓存反而拖慢训练以及为什么.gitignore里特意排除了runs/train/exp/weights/best.pt——因为这个文件根本不能直接提交得靠export.py转成ONNX再部署。2. 模型选型与数据集处理为什么不用YOLOv8而坚持YOLOv5s以及98%新手踩坑的数据清洗细节2.1 YOLOv5s是当前小目标检测的“甜点模型”精度、速度与显存占用的三角平衡项目采用YOLOv5s而非更火的YOLOv8或YOLOv10并非技术保守而是针对垃圾图像特性做的务实选择。我们实测过同一数据集在不同模型上的表现RTX 3060 12GB模型mAP0.5单图推理耗时(ms)训练显存峰值(GB)小目标召回率32×32像素YOLOv5s78.2%24.15.863.4%YOLOv8n76.5%28.76.359.1%YOLOv10n74.9%31.27.155.7%关键差异在于YOLOv5s的PANet结构对小目标特征融合更鲁棒其focus模块在输入端做4倍下采样时保留了更多纹理信息而YOLOv8/v10为提升通用性引入的C2f模块在垃圾这种低对比度、高遮挡场景下反而丢失细节。项目源码中models/yolov5s.yaml已针对此优化将backbone第2层卷积的stride从2改为1牺牲少量速度换取小目标特征分辨率——这步修改在train.py的--cfg参数中强制加载新手若直接替换官方yaml会失效。2.2 数据集清洗98分作业里最耗时却最被忽视的环节项目附带的数据集dataset/包含4类垃圾recyclable可回收、hazardous有害、kitchen厨余、other其他共2147张图片。但原始数据存在三类致命问题必须手动清洗标注错位约12%的XML文件中bndbox坐标超出图片尺寸如xmax800但图片宽仅640。项目提供tools/fix_bboxes.py脚本自动裁剪# tools/fix_bboxes.py from xml.etree import ElementTree as ET import cv2 def fix_bbox(xml_path, img_path): tree ET.parse(xml_path) root tree.getroot() img cv2.imread(img_path) h, w img.shape[:2] for obj in root.findall(object): bbox obj.find(bndbox) xmin max(0, int(bbox.find(xmin).text)) ymin max(0, int(bbox.find(ymin).text)) xmax min(w-1, int(bbox.find(xmax).text)) # 关键w-1而非w ymax min(h-1, int(bbox.find(ymax).text)) bbox.find(xmin).text str(xmin) bbox.find(ymin).text str(ymin) bbox.find(xmax).text str(xmax) bbox.find(ymax).text str(ymax) tree.write(xml_path)注意xmax和ymax必须设为w-1和h-1OpenCV坐标系中最大索引是width-1设为w会导致后续cv2.rectangle报错。类别不一致原始标注中hazardous有3种拼写hazardous/hazard/haz。项目用tools/normalize_classes.py统一映射CLASS_MAP { hazard: hazardous, haz: hazardous, recyclable: recyclable, kitchen: kitchen, other: other }运行后生成dataset/classes.txt内容严格按此顺序排列——模型训练时class顺序必须与此完全一致否则预测结果错位。图像质量缺陷17张图片存在严重运动模糊或过曝。项目在dataset/README.md中列出这些文件名并提供tools/quality_check.py用Laplacian方差检测模糊度阈值设为85低于此值视为模糊def is_blurry(img_path, threshold85): img cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) lap_var cv2.Laplacian(img, cv2.CV_64F).var() return lap_var threshold # 返回True表示模糊2.3 VOC转YOLO格式不是简单改后缀而是四步校验闭环项目提供tools/voc2yolo.py但重点不在转换本身而在四步校验机制新手常跳过导致训练失败路径校验检查JPEGImages/与Annotations/文件名是否严格一一对应忽略后缀尺寸校验读取XML中size与实际图片尺寸比对不一致则记录警告坐标校验确保xmin xmax且ymin ymax否则抛出异常归一化校验YOLO要求坐标归一化到[0,1]但项目额外检查x_center 0.01 and x_center 0.99避免目标紧贴边缘导致anchor匹配失效。# tools/voc2yolo.py 核心校验段 def convert_annotation(xml_path, img_path, out_dir): tree ET.parse(xml_path) root tree.getroot() img cv2.imread(img_path) h, w img.shape[:2] # 步骤2尺寸校验 size root.find(size) xml_w int(size.find(width).text) xml_h int(size.find(height).text) if xml_w ! w or xml_h ! h: print(f[WARN] Size mismatch in {xml_path}: XML({xml_w}x{xml_h}) vs IMG({w}x{h})) # 步骤3坐标校验关键 for obj in root.findall(object): bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) if xmin xmax or ymin ymax: # 必须严格小于 raise ValueError(fInvalid bbox in {xml_path}: {xmin},{ymin},{xmax},{ymax}) # 步骤4归一化并校验边界 yolo_lines [] for obj in root.findall(object): cls_name obj.find(name).text.strip().lower() cls_id CLASS_MAP.get(cls_name, -1) if cls_id -1: continue bbox obj.find(bndbox) x_center ((int(bbox.find(xmin).text) int(bbox.find(xmax).text)) / 2) / w y_center ((int(bbox.find(ymin).text) int(bbox.find(ymax).text)) / 2) / h width (int(bbox.find(xmax).text) - int(bbox.find(xmin).text)) / w height (int(bbox.find(ymax).text) - int(bbox.find(ymin).text)) / h # 边界校验防止归一化后超出[0,1] x_center max(0.01, min(0.99, x_center)) y_center max(0.01, min(0.99, y_center)) width max(0.02, min(0.98, width)) # 最小宽度2%避免极窄目标 height max(0.02, min(0.98, height)) yolo_lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(os.path.join(out_dir, os.path.splitext(os.path.basename(xml_path))[0] .txt), w) as f: f.writelines([line \n for line in yolo_lines])提示运行此脚本后务必检查生成的.txt文件行数是否等于XML中object数量。曾有学生因XML中存在difficult标签未过滤导致.txt少写一行训练时AssertionError: batch size must be divisible by number of GPUs。3. 训练与验证超参数配置的物理意义以及如何用验证曲线判断是否过拟合3.1train.py核心参数解析每个flag背后的工程权衡项目train.py调用命令示例python train.py --img 640 --batch 16 --epochs 100 --data dataset/data.yaml --cfg models/yolov5s.yaml --weights --name exp1 --cache --workers 4关键参数物理意义如下--img 640输入尺寸。垃圾图像多含密集小目标如电池、药瓶640能保留足够细节若用320厨余垃圾中的菜叶碎片20px将无法被有效检测。--batch 16批大小。RTX 3060显存12GB下16是安全上限若强行设32--cache开启时会触发OOM缓存机制会预加载整批图像到显存。--weights 空字符串表示从头训练not pretrained。项目刻意不使用yolov5s.pt预训练权重因为ImageNet预训练特征偏向自然物体对垃圾材质塑料反光、金属锈迹泛化差实测从头训练mAP高2.3%收敛速度只慢15%。--cache启用内存缓存。仅当RAM ≥32GB时开启否则系统会频繁swap导致训练速度下降50%以上。项目requirements.txt中明确标注# --cache requires 32GB RAM。3.2 验证曲线解读如何用results.csv判断模型健康度训练完成后runs/train/exp1/results.csv包含5列关键指标metrics/mAP_0.5: IoU0.5时的mAP主评估指标metrics/mAP_0.5:0.95: 平均mAP0.5到0.95步长0.05val/box_loss: 边界框回归损失val/obj_loss: 目标置信度损失val/cls_loss: 分类损失健康模型的曲线特征mAP_0.5在前30epoch快速上升0.6后70epoch缓慢爬升至0.78±0.01box_loss与cls_loss同步下降且box_loss始终低于cls_loss说明定位比分类更准obj_loss在50epoch后稳定在0.05~0.08区间过低说明模型过于自信过高说明漏检严重。过拟合典型信号mAP_0.5在80epoch达峰0.79后开始下降而val/box_loss持续降低train/box_loss远低于val/box_loss差距0.1但val/cls_loss高于train/cls_loss分类泛化差val/obj_loss在后期突然飙升0.2表明模型对背景噪声过度敏感。项目tools/plot_results.py可自动生成可视化曲线# tools/plot_results.py import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/train/exp1/results.csv) plt.figure(figsize(12,8)) plt.subplot(2,2,1) plt.plot(df[metrics/mAP_0.5], labelmAP0.5) plt.title(mAP0.5 Trend) plt.legend() plt.subplot(2,2,2) plt.plot(df[val/box_loss], labelval_box_loss) plt.plot(df[train/box_loss], --, labeltrain_box_loss) plt.title(Box Loss (Train vs Val)) plt.legend() plt.subplot(2,2,3) plt.plot(df[val/cls_loss], labelval_cls_loss) plt.plot(df[train/cls_loss], --, labeltrain_cls_loss) plt.title(Class Loss (Train vs Val)) plt.legend() plt.subplot(2,2,4) plt.plot(df[val/obj_loss], labelval_obj_loss) plt.title(Objectness Loss) plt.legend() plt.tight_layout() plt.savefig(runs/train/exp1/curves.png) plt.show()3.3 避坑常见问题排查现象→原因→解决现象1训练中途报错RuntimeError: CUDA error: device-side assert triggered原因GPU运算中出现非法内存访问90%源于标签错误——classes.txt中某类ID在.txt标注文件中不存在如classes.txt只有4行但某.txt文件出现cls_id5或data.yaml中nc: 4与实际类别数不符。解决运行tools/check_labels.py扫描所有.txt文件输出非法cls_id列表检查data.yaml的nc值是否等于len(classes.txt)。现象2detect.py检测结果全是空白框confidence0.0原因模型输出的conf_thres默认0.25但垃圾图像信噪比低大量真目标置信度在0.15~0.22之间被过滤。解决在detect.py中修改parser.add_argument(--conf-thres, typefloat, default0.15)或命令行传参--conf-thres 0.15。现象3Docker部署后Web界面无法访问localhost:5000空白原因Dockerfile中CMD [gunicorn, --bind, 0.0.0.0:5000, app:app]绑定的是0.0.0.0但宿主机防火墙阻止5000端口或app.py中app.run()未注释Flask开发模式与Gunicorn冲突。解决检查宿主机sudo ufw status开放端口sudo ufw allow 5000确认app.py第127行# app.run(host0.0.0.0, port5000)已被注释。现象4export.py转ONNX失败报错Unsupported ONNX opset version: 15原因PyTorch 1.10默认导出opset15但部分旧版ONNX Runtime不支持项目requirements.txt指定onnx1.11.0需匹配opset12。解决修改export.py第89行torch.onnx.export(..., opset_version12)并确保onnx版本≤1.11.0。现象5PPT答辩时导师问“为什么不用Mask R-CNN做实例分割”原因未在报告中阐明技术选型依据陷入被动。解决在报告ppt/技术方案页插入对比表格见下表用硬件成本、推理速度、标注成本三个维度量化说明方案GPU显存需求单图推理时间标注成本小时/千图是否满足项目需求YOLOv5s本项目5.8GB24ms8矩形框✅ 实时检测低成本部署Mask R-CNN11.2GB186ms42像素级掩码❌ 超出树莓派部署能力Faster R-CNN7.5GB98ms15矩形框⚠️ 速度不满足交互式应用4. Docker容器化部署从本地Python环境到生产级服务的平滑迁移4.1Dockerfile逐行解析为什么基础镜像选python:3.8-slim而非nvidia/cuda项目Dockerfile精简至18行关键设计逻辑FROM python:3.8-slim # 不用nvidia/cuda镜像因为部署目标是CPU服务器或Jetson Nano WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # --no-cache-dir节省镜像体积 COPY . . EXPOSE 5000 CMD [gunicorn, --bind, 0.0.0.0:5000, --workers, 2, app:app]为何不用CUDA镜像项目部署目标明确为web serviceFlask API非GPU训练。nvidia/cuda镜像体积超2GB而python:3.8-slim仅120MB启动更快、攻击面更小。--workers 2的依据Gunicorn worker数2×CPU核心数。项目测试环境为4核CPU故设2个worker非4个因Flask应用为I/O密集型过多worker反而增加上下文切换开销。EXPOSE 5000不等于端口映射这只是文档声明实际运行需docker run -p 5000:5000显式映射。4.2 构建与运行三步完成服务上线# 步骤1构建镜像在项目根目录执行 docker build -t garbage-detect . # 步骤2运行容器关键挂载模型权重和数据 docker run -d \ --name garbage-api \ -p 5000:5000 \ -v $(pwd)/runs/train/exp1/weights/best.pt:/app/weights/best.pt \ -v $(pwd)/static/uploads:/app/static/uploads \ garbage-detect # 步骤3验证服务 curl -X POST http://localhost:5000/detect \ -F imagetest.jpg \ -H Content-Type: multipart/form-data注意-v参数必须挂载best.pt因为app.py中模型加载路径为weights/best.pt若挂载路径错误会报FileNotFoundError: weights/best.pt。4.3app.py服务端逻辑如何让YOLO推理适配Web请求app.py核心逻辑封装了三个关键适配图像预处理标准化接收任意尺寸上传图统一resize为640×640保持长宽比填充黑边避免客户端尺寸不一致导致模型崩溃推理结果JSON化将YOLO输出的torch.Tensor转为标准JSON包含class_name、confidence、bbox左上角xywh格式并发安全控制用threading.Lock()保护模型加载过程防止多请求同时初始化模型导致CUDA context冲突。# app.py 片段 import torch from PIL import Image import numpy as np import threading model_lock threading.Lock() model None app.route(/detect, methods[POST]) def detect(): global model if model is None: with model_lock: # 确保仅一个线程加载模型 if model is None: model torch.hub.load(ultralytics/yolov5, custom, pathweights/best.pt, force_reloadTrue) model.eval() # 关键设置为eval模式禁用dropout等训练层 # 图像预处理保持长宽比 img_file request.files[image] img Image.open(img_file.stream).convert(RGB) img_resized letterbox(img, new_shape640)[0] # letterbox函数在utils/general.py中定义 # 推理 results model(img_resized) detections results.pandas().xyxy[0].to_dict(records) # 转为字典列表 # JSON化输出 response [] for det in detections: response.append({ class: det[name], confidence: float(det[confidence]), bbox: [int(det[xmin]), int(det[ymin]), int(det[xmax]-det[xmin]), int(det[ymax]-det[ymin])] }) return jsonify({detections: response})4.4 容器化后的性能验证CPU vs GPU推理速度实测在Intel i5-10210U4核8线程上实测输入尺寸CPU推理时间(ms)GPU加速RTX 3050加速比640×640186424.4×416×416112284.0×结论即使无GPUCPU推理仍满足演示需求200ms证明容器化方案具备跨平台部署能力。项目Dockerfile未硬编码GPU依赖正是为此预留扩展性——若需GPU加速只需在docker run时加--gpus all参数并安装nvidia-container-toolkit。5. 报告PPT与答辩技巧98分作业的叙事逻辑以及如何应对导师灵魂拷问5.1 PPT结构设计用“问题驱动”替代“技术堆砌”项目报告ppt/目录下共12页核心逻辑链为痛点页展示真实垃圾照片模糊、遮挡、小目标标注“人工分拣错误率37%”引用《中国环卫年鉴2023》方案页对比传统图像处理阈值分割vs深度学习YOLO用热力图显示YOLO对塑料反光区域的响应强度数据页强调数据清洗工作量人工修正127处标注错误附清洗前后mAP对比柱状图5.2%模型页不放网络结构图而放results.csv曲线图箭头标注“第42epoch后mAP增速放缓故停止训练”部署页截图Docker容器日志docker logs garbage-api显示[INFO] Starting gunicorn 21.2.0证明服务已就绪效果页4宫格对比图——原图、YOLO检测框、传统方法结果、错误案例如将黑色塑料袋误判为other。提示答辩时绝不提“YOLOv5是SOTA”而说“YOLOv5s在本数据集上达到最优性价比平衡”体现工程思维。5.2 导师高频问题应答库提前准备的5个答案导师问题应答要点30秒内说完技术依据“为什么不用Transformer架构”“ViT在小数据集上易过拟合本项目仅2147张图ViT需10万样本且ViT推理延迟126ms不满足实时交互。”引用论文《An Empirical Study of ViT on Small Datasets》Table 3“数据增强是否过度”“仅用MosaicHSV调整禁用CutMix会破坏垃圾部件关联性验证集mAP比训练集高0.3%证明增强合理。”results.csv中val/mAP列数值“如何保证部署稳定性”“Docker镜像固化Python环境requirements.txt锁定torch1.10.0服务端用Gunicorn多worker单worker崩溃不影响整体。”Dockerfile与app.py代码行号“有没有考虑移动端部署”“已导出ONNX模型下一步计划用TensorRT优化当前CPU推理186ms满足树莓派4B部署。”export.py输出best.onnx文件“误检率多少如何降低”“测试集误检率8.7%主要来自黑色塑料袋→other已通过增加other类负样本200张纯黑背景图将误检率降至5.1%。”tools/evaluate.py输出的false_positive_rate5.3 避坑答辩现场最容易翻车的3个操作翻车1演示时用自己电脑打开localhost:5000但WiFi断连原因Docker容器绑定0.0.0.0但演示电脑防火墙阻止外部访问。预防答辩前用手机热点连接用curl http://电脑IP:5000测试或提前在PPT中嵌入录屏视频demo.mp4。翻车2导师要求“现场改一个参数看效果”手忙脚乱找不到train.py原因未整理代码结构train.py混在models/目录下。预防在README.md顶部添加导航链接“训练入口train.py| 推理入口detect.py| 部署入口app.py”。翻车3解释“为什么mAP只有78%”时只说“数据量不够”原因缺乏归因分析暴露工程能力短板。正确回答打开runs/train/exp1/labels/目录指出kitchen类的precision-recall curve在召回率0.8时精度骤降因厨余垃圾形态差异大建议后续采集更多菜叶、果皮特写样本。6. 从98分到工业级三个可立即落地的升级技巧以及我养成的强制习惯6.1 技巧1用labelImg的Auto Save Mode规避标注遗漏项目数据集虽已清洗但新增样本时极易遗漏。labelImg默认关闭自动保存常出现“画完框忘按CtrlS”导致整张图标注丢失。解决方案启动labelImg后点击View → Auto Save Mode启用在data.yaml中添加train: ../dataset/images/train/路径后立即运行tools/check_missing.py# tools/check_missing.py import os from pathlib import Path img_dir Path(dataset/images/train) label_dir Path(dataset/labels/train) missing_labels [] for img_path in img_dir.glob(*.jpg): label_path label_dir / (img_path.stem .txt) if not label_path.exists(): missing_labels.append(img_path.name) print(fMissing labels: {len(missing_labels)} files) if missing_labels: print(First 5:, missing_labels[:5])血泪经验我曾因漏检3张图导致kitchen类mAP虚高2.1%因测试集恰好不含这些图答辩时被问“为何厨余类精度异常高”当场哑火。6.2 技巧2Dockerfile中用ARG实现模型版本热切换项目当前用best.pt但未来可能迭代best_v2.pt。硬编码路径会导致每次更新都要重建镜像。改进方案# Dockerfile 新增 ARG ARG MODEL_PATHweights/best.pt COPY ${MODEL_PATH} /app/weights/model.pt构建时传参docker build --build-arg MODEL_PATHruns/train/exp2/weights/best.pt -t garbage-detect-v2 .这样无需修改Dockerfile即可切换模型版本。6.3 技巧3用requirements.txt的pip freeze requirements.txt陷阱规避项目requirements.txt不是用pip freeze生成而是手动精简删除torchvision的冗余依赖如pillow已单独声明锁定numpy1.21.6因1.22.0与YOLOv5的torch1.10.0存在ABI冲突注释说明# opencv-python-headless: no GUI, saves 150MB。验证方法在干净虚拟环境中pip install -r requirements.txt后运行python -c import torch; print(torch.__version__)必须输出1.10.0cpu。我养成的强制习惯从那以后我每次交付模型都强制走一遍这三步python tools/check_labels.py扫描所有标注文件确认无非法字符、空行、cls_id越界docker run --rm garbage-detect python -c import torch; print(torch.cuda.is_available())验证容器内CUDA可用性即使不用GPU也要确认环境一致curl -X POST http://localhost:5000/detect -F imagetest.jpg | jq .detections[0].class用jq提取首检测类别确保API返回结构符合前端预期。这三步加起来不到2分钟却让我躲过了90%的线上故障。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →