简介:面向具备一定Python基础的研究人员与环保监测技术人员,这份资料完整呈现基于YOLOv11的水面垃圾检测系统从环境配置、数据集准备到模型训练、ONNX导出、性能评估与GUI界面搭建的全流程方案。文档以单份docx形式提供,压缩包整体41KB,虽仅含1个文件,但涵盖项目介绍、特点说明、参考资料、未来改进方向、注意事项及分步实施代码,浓缩了项目核心知识。目前已有441人学习,适合希望快速复现水面漂浮物自动识别与定位、理解检测评估指标可视化方法,并在此基础上扩展多类别识别与实时监测功能的读者。内容突出精准检测与多类别支持,结合YOLOv11与OpenCV等工具,可直接作为环保监控领域项目开发或课题研究的参考。
1. 水面垃圾检测为什么选 YOLOv11:从人工巡河到自动识别的完整落地路径
水面垃圾检测这事,听起来像个“小任务”,真做起来却比想象中更依赖工程细节。人工巡河拍照再回来筛图,效率低、漏检率高,而且不同水域的垃圾形态差别很大——塑料瓶、食品包装、漂浮废弃物,光照和水流条件一变,模型表现就可能翻车。这套基于 YOLOv11 的水面垃圾检测系统,把“数据准备 → 模型训练 → 指标评估 → ONNX 导出 → GUI 交互”整条链路都串好了,拿到的不是一段孤立的推理脚本,而是一个能直接跑起来的完整项目。适合有 Python 基础、想快速在自己数据集上复现检测流程的研究人员或环保行业技术人员。我拆完这份项目后的直接感受是:它的价值不在模型本身多先进,而在把 YOLOv11 训练到部署的每一步都给你铺好了路,连 GUI 和评估可视化都写了,属于拿来就能改的工程模板。
2. 数据集与标注格式:水面垃圾检测的成败其实在训练之前就定了
2.1 数据集目录结构:train / val / test 分离为什么不能省
很多初学者拿到 YOLOv11 第一件事就是跑 train.py,结果训练集和验证集混在一起,评估指标虚高,模型一上真实场景就现原形。这个项目在数据准备上给了明确的目录规范,我先完整贴出来再解释逻辑:
water_surface_garbage/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/train / val / test 三个子集必须物理隔离,不能有交集。训练集负责让模型学特征,验证集用于每个 epoch 结束后评估泛化能力并保存最佳权重,测试集是最后模拟真实场景的“闭卷考试”。有些资料会把 val 和 test 混用,省事但会污染结论。
我一般会在标注完成后用脚本按比例随机划分,比如 7 : 2 : 1,同时保证 images 和 labels 两边文件名一一对应。图片是 .jpg,标注文件必须是同名的 .txt,这个对应关系一旦错位,YOLOv11 训练时会直接报 no labels found,排查起来相当折腾。
2.2 YOLO 标注格式的细节:归一化坐标与类别 ID 的对应关系
每个 .txt 标注文件里,每一行对应一个目标物体,格式是:
class_id x_center y_center width height注意这里所有坐标都是相对图片宽高的归一化值,不是像素值。也就是说,x_center = 目标的中心点 x 像素坐标 ÷ 图片宽度,取值在 0 到 1 之间。很多标注工具导出的格式是左上角和右下角坐标,需要先转换成中心点加宽高的形式,再除以图片尺寸。
类别 ID 从 0 开始编号,对应 yaml 配置文件里的 names 列表顺序。比如这个项目里配置的顺序是:
nc: 3 names: ["waste", "plastic_bottle", "food_packaging"]那么标注文件里 class_id 为 0 的是 waste,为 1 的是 plastic_bottle,为 2 的是 food_packaging。如果标注工具导出的类别顺序和 yaml 文件不一致,模型训练时 loss 直接不收敛,检测结果张冠李戴,这是水面垃圾这类多类别项目里最容易犯的低级错误。
提示:标注完成后建议写一个小脚本统计每个类别的目标数量,如果某个类别只有几十个样本,后续训练时要给这个类别单独调高 loss 权重,否则模型会倾向于把所有目标都预测为数量多的那个类别。
3. 模型训练:从环境准备到 train.py 关键参数调优
3.1 环境准备与依赖安装的版本陷阱
项目第一步是安装依赖,原文给的命令是:
pip install torch torchvision torchaudio onnx onnxruntime opencv-python matplotlib pandas tkinter这里有个隐蔽的坑:tkinter往往无法通过 pip 直接安装成功,它是 Python 自带的 GUI 库,在 Linux 上需要sudo apt-get install python3-tk,在 Windows 上安装 Python 时勾选 tcl/tk 组件。如果检测 GUI 界面时发现 import tkinter 报错,问题基本都出在这。
克隆 YOLOv11 代码库并安装 requirements:
git clone https://github.com/YourGitHubYOLOv11.git cd YourGitHubYOLOv11 pip install -r requirements.txt我建议用 conda 创建独立虚拟环境,Python 版本选 3.9 或 3.10。YOLOv11 对 PyTorch 版本有最低要求,建议装 2.0 以上版本,CPU 机器也能跑训练,只是速度会慢不少。有条件用 GPU 的话,CUDA 版本和 PyTorch 的 cu118 / cu121 后缀必须对应,否则 torch.cuda.is_available() 永远返回 False,训练时模型静默跑在 CPU 上,速度和 GPU 完全两个量级。
3.2 train.py 参数逐项拆解:img / batch / epochs 的含义与选择逻辑
训练命令是 YOLOv11 全系列统一入口:
python train.py --img 640 --batch 16 --epochs 100 --data water_surface_garbage.yaml --weights yolov11.pt参数含义拆解:
--img 640:输入图片统一缩放到 640×640。这个值影响检测精度和小目标召回率,水面垃圾里的塑料瓶、食品包装往往是小目标,建议先按 640 跑通流程,后续图省事可以换 832 看小目标召回率是否提升。--batch 16:单次迭代送入模型的图片数量。显存不够时优先降低这个值,8GB 显存跑 640 分辨率建议 batch 取 8。batch 太小会导致梯度震荡,loss 曲线毛刺很多。--epochs 100:训练轮数。水面垃圾数据量通常不大,100 轮足够收敛,继续加大轮数容易过拟合,模型在训练集上 loss 很低,验证集 mAP 反而下降。--data water_surface_garbage.yaml:指向数据集配置文件。--weights yolov11.pt:预训练权重路径。用 COCO 预训练权重做迁移学习初始化,比从零训练收敛速度快得多。
从零训练和迁移学习的主要差别在收敛速度。COCO 预训练模型已经学好了通用的边缘、纹理、形状特征,水面垃圾的塑料瓶和 COCO 里的瓶子类有相似性,所以微调后 50 轮左右 mAP 就能上到可观水平。如果数据集很小——每个类别只有几百张——可以冻结 backbone 层只训练检测头,防止过拟合。
3.3 训练过程中的日志监控:results.csv 是排查过拟合的第一手依据
训练启动后,runs/train/exp/ 目录会实时生成 results.csv,里面每个 epoch 记录了 train_loss、val_loss、precision、recall、mAP50、mAP50-95 等指标。我建议每训练 10 个 epoch 就看一眼文件末尾几行:
tail -5 runs/train/exp/results.csv重点关注两组关系:train_loss 和 val_loss 的走向,precision 和 recall 的相对变化。val_loss 在第 60 轮开始回升但 train_loss 还在降,就是典型的过拟合信号,可以提前停掉训练,用第 60 轮的权重重新评估。训练正常时 final epoch 训练损失大概降到一个较低水平,精度和召回率维持在一定合理区间内,具体数值取决于数据集规模和标注质量,不做硬性参考。
另外一个容易忽略的细节:每次训练前把 runs/train 目录清空,或者用--name参数指定新的实验名称,否则新实验的结果会写到 exp2、exp3,找权重时容易拿错文件。
4. 模型评估与指标可视化:别只盯 mAP,要看 precision / recall 的曲线走向
4.1 val.py 评估输出字段的完整解读
训练完成后执行:
python val.py --weights runs/train/exp/weights/best.pt --data water_surface_garbage.yaml --img 640评估结果会打印出各类别的 precision、recall、mAP50、mAP50-95。水面垃圾检测场景里,mAP50 能到 80% 以上基本可用;mAP50-95 是更严格的标准,计算了不同 IoU 阈值下的平均精度,这个值通常比 mAP50 低 15 到 25 个百分点,不必因为数字难看就怀疑模型错了,这是正常现象。
4.2 用 matplotlib 绘制评估曲线:一次把四张图都画全的脚本
项目给出了用 results.csv 绘制训练曲线的代码,我整理成可以直接替换路径的版本:
import matplotlib.pyplot as plt import pandas as pd data = pd.read_csv('runs/train/exp/results.csv') plt.figure(figsize=(12, 8)) plt.subplot(2, 2, 1) plt.plot(data['epoch'], data['train/box_loss'], label='Box Loss', color='blue') plt.plot(data['epoch'], data['val/box_loss'], label='Val Box Loss', color='cyan') plt.title('Loss over Epochs') plt.xlabel('Epoch') plt.ylabel('Loss') plt.grid(True) plt.legend() plt.subplot(2, 2, 2) plt.plot(data['epoch'], data['metrics/precision'], label='Precision', color='green') plt.title('Precision over Epochs') plt.xlabel('Epoch') plt.ylabel('Precision') plt.grid(True) plt.legend() plt.subplot(2, 2, 3) plt.plot(data['epoch'], data['metrics/recall'], label='Recall', color='red') plt.title('Recall over Epochs') plt.xlabel('Epoch') plt.ylabel('Recall') plt.grid(True) plt.legend() plt.subplot(2, 2, 4) plt.plot(data['epoch'], data['metrics/mAP50'], label='mAP50', color='orange') plt.plot(data['epoch'], data['metrics/mAP50-95'], label='mAP50-95', color='purple') plt.title('mAP over Epochs') plt.xlabel('Epoch') plt.ylabel('mAP') plt.grid(True) plt.legend() plt.tight_layout() plt.show()这段代码有四个子图,分别对应 box loss、precision、recall、mAP 的逐 epoch 曲线。box loss 曲线是判断训练是否收敛的最直观信号,如果它一直走平甚至回升,说明学习率设置不当或数据有问题。precision 和 recall 永远存在权衡关系:precision 高说明检测框基本都对了,漏检可能比较多;recall 高说明目标基本都找到了,误检可能比较多。实际部署时看具体场景需要,比如环保巡检宁可多报几个假阳性,也不能漏掉真实污染源,那就倾向提高 recall。
4.3 评估阶段最容易误导人的三个指标认知
第一,mAP50 高不代表模型在真实场景里好用。它是单张图片上的静态指标,真实水面视频里会有运动模糊、反光、遮挡,这些在评估集里未必覆盖。第二,results.csv 里的metrics/precision是所有类别的宏观平均,单个类别表现可能差异很大——塑料瓶可能在各个指标上都高,食品包装由于标注样本少,recall 可能低到没法看。第三,评估时的 img size 必须和训练时一致,否则同一模型在 640 和 1280 下输出指标差异巨大,不是模型变好了,是输入分辨率改变了目标的有效尺寸。
5. 训练与部署避坑:这几个问题我赌你会遇到
5.1 标注文件里出现越界坐标
现象:训练到一半报错,提示 box coordinate 超出图像范围,或者 loss 出现 NaN。
原因:标注工具导出的坐标没有严格限制在 0 到 1 之间,或者图片 resize 后标注没有同步归一化更新。
解决:写一个 Python 脚本遍历所有 labels 文件,把小于 0 的坐标强制截断为 0,大于 1 的截断为 1。同时检查 width 和 height 是否大于 0,小于等于 0 的标注行直接删除。这类脏数据在训练前清理干净,能省下好几个小时的排查时间。
5.2 GPU 显存不足导致训练中断
现象:train.py 启动后几秒钟,报 CUDA out of memory,或者进程直接被杀。
原因:batch size 设置过大,输入图片分辨率 640,加上 YOLOv11 本身的中间特征图,显存占用比想象中大很多。
解决:先把--batch降到 4 跑通流程,再逐步往上加。如果显存始终不够,把--img降到 480 或 512,小目标检测精度会有损失,但至少让你先拿到完整流程。另外 train.py 里可以开启--cache参数把图片缓存到内存,减少数据加载对显存的临时占用。
5.3 GUI 界面 import tkinter 失败
现象:运行 GUI 脚本报 ModuleNotFoundError: No module named 'tkinter'。
原因:pip 安装的 Python 在 Linux 上默认不带 tcl/tk 支持,Ubuntu 上还需要额外安装系统包。
解决:Ubuntu/Debian 执行sudo apt-get install python3-tk,Windows 用户重新运行 Python 安装程序勾选 tcl/tk 组件。装完后在 Python 里执行import tkinter验证。
5.4 torch.hub.load 加载本地模型时路径错误
现象:GUI 脚本执行时提示模型文件找不到,或者报错说无法从 GitHub 下载。
原因:原项目示例代码用了torch.hub.load,它的第一个参数如果是 GitHub 仓库名,会默认去线上拉取代码,网络环境受限时就会卡住。
解决:改成从本地路径加载:
model = torch.hub.load('你的YOLOv11代码库路径', 'custom', path='runs/train/exp/weights/best.pt', source='local')注意source='local'必须显式写出,否则还是会去找 GitHub。这是一个很值得记住的细节——很多从 GitHub 仓库部署 YOLOv11 到内网或本地服务器的情况,都是栽在 torch.hub 的远程依赖上。
5.5 评估指标和实际检测效果严重不符
现象:val.py 显示 mAP50 很高,但 GUI 里上传真实照片后漏检一大堆。
原因:验证集和训练集来自同一分布,可能连拍摄设备都相同,模型相当于做了“开卷考试”;真实场景里光照、角度、水面反光都变了,模型没见过类似特征。
解决:训练完成后额外收集一批完全没有参与训练和验证的真实照片做独立测试集,只看这批数据上的检测效果。如果漏检严重,说明训练数据多样性不足,需要补充不同光线、水质条件的数据。这是所有视觉检测项目的共性,水面垃圾这种目标形态多变的任务尤其敏感。
6. 部署到 ONNX 并脱离训练环境推理:模型导出与验证的完整闭环
训练和评估只是第一步,真正让这套水面垃圾检测系统发挥价值的是导出 ONNX 并完成跨平台部署。ONNX 格式的好处在于不依赖 PyTorch 运行时,可以用 onnxruntime 在任何环境的 CPU 上推理,也能转到 TensorRT 后端优化,往 Jetson Nano 这类边缘设备上迁移时这是必经之路。
导出命令:
python export.py --weights runs/train/exp/weights/best.pt --img 640 --batch-size 1 --include onnx导出完成后,runs/train/exp/weights/ 目录下会生成 best.onnx。用 onnxruntime 加载并完成一次推理验证:
import cv2 import onnxruntime as ort import numpy as np session = ort.InferenceSession('runs/train/exp/weights/best.onnx') input_name = session.get_inputs()[0].name image = cv2.imread('test_image.jpg') img_resized = cv2.resize(image, (640, 640)) img_blob = img_resized[:, :, ::-1].transpose(2, 0, 1) / 255.0 img_blob = np.expand_dims(img_blob, axis=0).astype(np.float32) outputs = session.run(None, {input_name: img_blob})这段代码要注意三点。第一,输入张量的通道顺序是 CHW,所以需要 transpose(2, 0, 1);第二,输入需要做归一化到 0 到 1;第三,推理输出的原始张量格式和 YOLOv11 的推理脚本输出格式不完全一致,直接拿 outputs 里的数组画框会得到奇怪的坐标,因为需要做解码和 NMS 后处理。ONNX 导出的模型只是推理引擎,完整部署还需要配套后处理逻辑。
用 ONNX 模型做一次推理验证后,可以拿同一张图分别用 PyTorch 权重和 ONNX 权重跑检测,对比两者的检测框坐标差异。两端坐标偏差如果在 1% 以内,说明导出没有问题;偏差很大,就需要确认预处理是否一致——很多部署翻车都发生在图像缩放和归一化参数与训练时不一致。
评估模型是否适合部署到边缘设备,核心指标是推理耗时和模型体积。ONNX 格式相比 PyTorch 权重体积有压缩,但更关键的是在 CPU 上单张图片推理耗时。桌面 CPU 一般在几百毫秒量级,Jetson Nano 上用 TensorRT 优化后能跑实时或准实时。导出完成后,别忘了把输入尺寸是否支持动态 shape 这件事也确认掉——固定尺寸导出在边缘设备上通常更稳定。
这套项目从数据整理到 GUI 部署都齐了,但真正把它用起来的关键还是验证闭环。从那以后我每次跑完 YOLOv11,都会强制走一遍导出 ONNX、脱离训练环境独立推理的流程,确认部署端和训练端行为一致,才敢把模型放出去。环境监测这种场景里漏检一个漂浮物可能意味着一次污染事件没被发现,这个验证习惯值得养成。希望帮到你。
本文还有配套的精品资源,点击获取