☰
YOLOv5水下垃圾检测实战:从数据标注到模型训练与推理
2026/9/28 16:43:58 网站建设 项目流程

简介:这份资源面向计算机视觉学习者与水下垃圾检测方向的开发者,提供一套可直接复现的YOLOv5水下垃圾检测方案,解决从数据准备到模型训练、评估的完整流程问题。压缩包共147个文件,约213.98MB,包含31个Python脚本、42个yaml配置、3个pt权重文件,以及jpg图片、xml标注、png曲线图、md说明文档等,覆盖训练、推理与结果展示各环节。资源内含训练好的水下垃圾检测权重,并附PR曲线、loss曲线等评估结果,便于直接验证效果;同时提供数千张真实场景水下垃圾图像,经labelimg标注,含VOC与YOLO两种格式,类别涵盖metal、wood、plastic、rubber、cloth等,场景丰富。目前已有1929人学习下载,适合希望快速上手水下目标检测、开展课程设计或科研实验的读者参考使用。

1. 水下垃圾检测为什么值得单独拆一个 YOLOv5 工程

做水下机器人、海洋环保监测或者水产养殖巡检的团队,几乎都会碰到同一个问题:通用目标检测模型在水下图像上直接翻车。原因不复杂——水体对红光的吸收、悬浮颗粒的散射、人工光源造成的亮度不均,让水下图像的色彩分布和陆上数据集完全不是一回事。拿 COCO 预训练权重直接推理,塑料瓶和塑料袋经常被识别成"运动器材"或干脆漏检。

这份资源解决的就是这个断层:它提供了一套已经训练好的 YOLOv5 水下垃圾检测权重,配套几千张用 labelImg 标注的真实场景图片,标签同时给了 VOC 和 YOLO 两种格式,类别覆盖 metal、wood、plastic、rubber、cloth 等常见海洋垃圾类型。仓库里还带了训练过程的 events 日志、results.csv、PR 曲线和 loss 曲线,等于把"数据—训练—评估"整条链路都摊开了。

适合两类人:一类是想快速验证水下检测可行性、不想从零标数据的工程团队;另一类是正在学 YOLOv5 训练自己数据集、需要一个真实非理想场景练手的开发者。下面按"资源结构 → 环境与数据 → 训练与推理 → 避坑 → 进阶验证"的顺序拆开讲。

2. 资源结构与 YOLOv5 数据管线:先搞清楚手里有什么

2.1 仓库文件逐个说明

拿到压缩包先别急着跑训练,花五分钟把目录结构过一遍,能省掉后面大量"文件找不到"的报错。这份资源的核心文件大致分四类:

文件/目录作用是否必须保留
events.out.tfevents.*TensorBoard 训练日志,记录 loss、mAP 等标量建议保留,用于复现曲线
results.csv每个 epoch 的指标表格,含 box_loss、obj_loss、mAP_0.5必须保留,评估基线
train_batch1.jpg/train_batch2.jpg训练时数据增强后的批次可视化可选,用于检查增强是否合理
Dockerfile/.dockerignore容器化环境定义可选,本地有环境可忽略
yolov5-6.0.imlIDE 模块配置,非代码文件可删
.gitattributes/.gitignore版本控制配置保留,避免误提交大文件

results.csv是最容易被忽略但最有价值的文件。它记录了每个 epoch 的train/box_loss、train/obj_loss、metrics/mAP_0.5、metrics/mAP_0.5:0.95,你可以直接用它判断这份权重是不是过拟合、训练了多少轮收敛。常见做法是用 pandas 读进来画一条曲线,比看 TensorBoard 还快。

2.2 VOC 与 YOLO 两种标签格式的差异

资源里标签分两个文件夹存放,这是很贴心的设计,但很多人搞不清什么时候用哪个。VOC 格式是每张图对应一个 XML,里面用<bndbox>记录xmin/ymin/xmax/ymax绝对像素坐标;YOLO 格式是每张图对应一个 txt,每行class_id x_center y_center width height,全部归一化到 0~1。

YOLOv5 训练只认 YOLO 格式,所以 VOC 那套主要是给你做数据审查、转换或者喂给其他框架用的。转换逻辑不复杂,但有两个坑:一是坐标越界,标注时框超出图像边界,归一化后会出现大于 1 的值;二是类别名和data.yaml里的names顺序必须严格对应,否则训练出来的模型会把塑料识别成木头。

import xml.etree.ElementTree as ET import os # VOC 类别顺序,必须和 data.yaml 的 names 完全一致 classes = ["metal", "wood", "plastic", "rubber", "cloth"] def voc_to_yolo(xml_path, img_w, img_h, out_path): tree = ET.parse(xml_path) root = tree.getroot() lines = [] for obj in root.iter("object"): cls_name = obj.find("name").text if cls_name not in classes: continue # 跳过未定义类别,避免索引错位 cls_id = classes.index(cls_name) bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 裁剪到图像边界,防止归一化后越界 xmin, xmax = max(0, xmin), min(img_w, xmax) ymin, ymax = max(0, ymin), min(img_h, ymax) xc = (xmin + xmax) / 2.0 / img_w yc = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{cls_id} {xc:.6f} {yc:.6f} {w:.6f} {h:.6f}") with open(out_path, "w") as f: f.write("\n".join(lines))

这段脚本的关键点在max(0, xmin)和min(img_w, xmax)这两行裁剪。水下图像标注时,因为能见度低,标注员经常把框画到画面边缘外,不裁剪的话 YOLOv5 在数据加载阶段会直接抛non-normalized coordinates警告,严重时该图被跳过。classes列表的顺序就是最终模型输出的类别索引,改顺序等于改标签含义,务必和data.yaml对齐。

2.3 data.yaml 的写法与路径陷阱

YOLOv5 靠一个 yaml 文件告诉训练器去哪找图、类别有几个。这份资源常见做法是写成下面这样:

# data/underwater.yaml train: ../datasets/underwater/images/train val: ../datasets/underwater/images/val nc: 5 names: ["metal", "wood", "plastic", "rubber", "cloth"]

nc是类别数,必须等于names长度,写错了训练不报错但 mAP 会莫名其妙很低。train和val指向的是 images 目录,YOLOv5 会自动把路径里的images替换成labels去找同名 txt。所以你的目录必须是images/train/xxx.jpg配labels/train/xxx.txt,文件名(不含扩展名)一一对应。很多人把标签和图片放同一目录,结果训练时提示找不到标签,就是踩了这个约定。

3. 环境配置与训练复现:从 conda 到第一个 epoch

3.1 环境搭建与依赖版本

YOLOv5 6.0 这个版本对 PyTorch 和 CUDA 比较敏感,用太新的 torch 会出现torch.load权重不兼容或者amp报错。稳妥的组合是 Python 3.8 + PyTorch 1.10~1.12 + CUDA 11.3。低显存机器(比如 6G 显存的笔记本)也能跑,但要调 batch size 和图像尺寸。

# 创建独立环境,避免和系统里的 torch 冲突 conda create -n underwater python=3.8 -y conda activate underwater # 安装匹配 CUDA 11.3 的 torch,具体版本按自己驱动选 pip install torch==1.12.1+cu113 torchvision==0.13.1+cu113 \ --extra-index-url https://download.pytorch.org/whl/cu113 # 安装 yolov5 依赖 cd yolov5-6.0 pip install -r requirements.txt

--extra-index-url那行是告诉 pip 去 PyTorch 官方源找带 CUDA 的包,不加的话默认装 CPU 版,训练速度会慢到怀疑人生。装完用python -c "import torch; print(torch.cuda.is_available())"验证,返回 True 才算环境通了。requirements.txt 里会装matplotlib、opencv-python、tqdm这些,如果公司内网 pip 慢,提前配好镜像源。

3.2 用预训练权重启动训练

资源里已经带了训练好的权重,但你要在自己的数据上微调或者复现,还是得走一遍训练命令。YOLOv5 6.0 的入口是train.py,最简命令如下:

python train.py \ --data data/underwater.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --device 0 \ --project runs/train \ --name underwater_exp

逐个参数说清楚:--weights yolov5s.pt是官方在 COCO 上预训练的骨干,用它初始化比从零训练收敛快得多,水下场景也建议这么干;--img 640是输入分辨率,显存不够就降到 416 或 320,但小目标(比如远处的塑料碎片)会掉点;--batch 16在 8G 显存上比较稳,6G 显存建议降到 8 并配合--img 512;--device 0指定第一块 GPU,多卡用0,1;--name决定输出目录,训练日志、权重、曲线都会存到runs/train/underwater_exp/下。

训练启动后重点盯三个输出:train/box_loss应该稳定下降,如果震荡剧烈多半是学习率太大或标注噪声多;metrics/mAP_0.5在前 10 个 epoch 涨得慢是正常的,水下数据收敛本来就比 COCO 慢;val/obj_loss如果开始上升而 train loss 还在降,说明过拟合了,该早停或者加数据增强。

3.3 推理与结果验证

训练完或者直接用资源里的权重,推理命令是detect.py:

python detect.py \ --weights runs/train/underwater_exp/weights/best.pt \ --source data/samples \ --img 640 \ --conf-thres 0.25 \ --iou-thres 0.45 \ --save-txt \ --project runs/detect \ --name underwater_test

--conf-thres 0.25是置信度阈值,水下图像噪声大,调太高会漏检,调太低会满屏误检,0.25 是个常用起点;--iou-thres 0.45控制 NMS 合并重叠框的力度,同一堆垃圾挨得近时可以适当调高到 0.5 避免误合并;--save-txt会把检测结果按 YOLO 格式存下来,方便你后续做统计或者二次处理。结果图默认存到runs/detect/underwater_test/,先拿几张典型场景(浑浊水、强反光、密集垃圾)肉眼过一遍,比只看 mAP 数字靠谱。

4. 水下场景训练避坑:五条血泪经验

4.1 现象:mAP 卡在 0.3 上不去,loss 也不降

原因通常是类别不平衡。水下垃圾里 metal 和 plastic 样本多,cloth 和 rubber 可能只有几十张,模型倾向于预测多数类。解决方法是检查results.csv里每个类别的 AP,如果某一类特别低,要么补数据,要么在data.yaml里给这类加过采样,或者用--hyp指定带类别权重的超参文件。YOLOv5 默认的hyp.scratch-low.yaml对不平衡数据不友好,可以手动调cls损失权重。

4.2 现象:训练报 "No labels found" 但标签明明存在

九成是路径问题。YOLOv5 找标签的规则是把图片路径里的/images/替换成/labels/,再把扩展名换成.txt。如果你的目录是images/train和labels/train,没问题;但如果是train/images和train/labels,替换逻辑就对不上。解决方法是严格按datasets/underwater/images/train和datasets/underwater/labels/train组织,或者用--data里写绝对路径排查。

4.3 现象:显存溢出 CUDA out of memory

水下图像分辨率普遍偏高,原始图可能 1920×1080,直接喂进去显存瞬间爆。YOLOv5 会在 dataloader 里 resize 到--img指定尺寸,但如果你的--img设成 1280 又用--batch 16,8G 卡必炸。解决方法是降--img到 640 或 512,降--batch到 8,或者开--multi-scale让尺寸动态变化但整体更省。实在不够就上--device cpu先跑通流程,再换机器。

4.4 现象:检测框位置偏移,框不住目标

多半是标注格式转换时坐标系搞混了。VOC 用的是左上角和右下角绝对坐标,YOLO 用的是中心点加宽高归一化。转换脚本里如果忘了除以图像宽高,或者把xmax-xmin写成了xmax-xmin+1,框就会系统性偏移。解决方法是转换后随机抽 10 张图,用labelImg或自己写脚本把 YOLO 标签画回图上,肉眼比对一遍再开训。

4.5 现象:验证集 mAP 高但实际推理效果差

这是典型的验证集和真实场景分布不一致。如果验证集图片和训练集来自同一批拍摄、同样的光照条件,mAP 会虚高。解决方法是手动划出一批不同水域、不同深度的图片做测试集,或者用detect.py跑真实视频抽帧,看漏检和误检集中在哪类场景。水下检测里,浑浊水和清澈水的表现能差 20 个点,别被单一验证集骗了。

5. 进阶:用 results.csv 反推训练质量与权重选型

拿到一份训练好的权重,怎么判断它值不值得用?别只看最终 mAP,results.csv里藏着更多信息。用下面这段脚本把关键曲线画出来,比 TensorBoard 更灵活:

import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("runs/train/underwater_exp/results.csv") df.columns = df.columns.str.strip() # 列名可能带空格,先清理 fig, axes = plt.subplots(1, 3, figsize=(15, 4)) axes[0].plot(df["epoch"], df["train/box_loss"], label="box_loss") axes[0].plot(df["epoch"], df["val/box_loss"], label="val_box_loss") axes[0].legend(); axes[0].set_title("Box Loss") axes[1].plot(df["epoch"], df["metrics/mAP_0.5"], label="mAP@0.5") axes[1].plot(df["epoch"], df["metrics/mAP_0.5:0.95"], label="mAP@0.5:0.95") axes[1].legend(); axes[1].set_title("mAP") axes[2].plot(df["epoch"], df["x/lr0"], label="lr0") axes[2].legend(); axes[2].set_title("Learning Rate") plt.savefig("training_curves.png", dpi=150)

看三条线就能判断权重质量。第一,train/box_loss和val/box_loss的差距:如果验证 loss 在某个 epoch 后持续上升,说明过拟合,应该取上升前的那个 epoch 的权重,而不是最后一个。第二,mAP_0.5的收敛点:如果 50 个 epoch 就平了,后面 50 个 epoch 基本是浪费,说明数据量或学习率已经到瓶颈。第三,学习率曲线:YOLOv5 默认用余弦退火,lr0应该平滑下降,如果出现跳变,可能是--cos-lr没开或者中断续训导致。

选权重时我一般会同时导出best.pt和last.pt各跑一遍测试集,best.pt是验证集 mAP 最高的,但有时last.pt在真实场景反而更稳,因为验证集本身可能有偏。资源里带的 PR 曲线也值得看,如果某一类的曲线下面积明显小,说明这类样本太少或者特征和别的类混淆,比如 rubber 和 plastic 在浑浊水里颜色接近,容易互相误判。

还有一个容易被忽略的点:events.out.tfevents文件可以用tensorboard --logdir runs/train直接打开,里面除了 loss 还有每层的梯度分布和权重直方图。如果发现某些层梯度长期接近零,说明这些层没学到东西,可能是冻结策略不对或者学习率太小。我习惯在正式部署前,把best.pt在至少三个不同来源的测试集上跑一遍,只有三个都稳定才敢上线。从那以后我每次拿到新权重,都强制先画一遍 results.csv 曲线再决定用哪个,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询