YOLO水下垃圾检测数据集构建与训练实战:从标注到部署避坑指南
发布时间:2026/9/28 17:21:07来源:尧图网络
简介这份资源面向计算机视觉学习者、水下目标检测研究者及需要真实场景数据训练YOLO模型的开发者提供一套已标注完成的海洋水下垃圾检测数据集可用于算法训练、模型对比与课程实验。压缩包共约2000个文件包含7667张jpg真实场景图像、7667个xml格式VOC标签与7668个txt格式YOLO标签两种标注格式分文件夹存放整体约174.28MB方便直接接入YOLO系列训练流程或转换为其他框架所需格式。数据覆盖metal、wood、plastic、rubber、cloth等多个海洋垃圾类别场景丰富、目标多样适合做多类别检测与泛化能力验证。目前已有2081人学习下载读者可据此快速搭建训练与评估环境省去从零采集和标注的成本把精力集中在模型结构、数据增强与调参策略上也便于复现和对比不同检测方案的效果。1. 水下垃圾检测为什么总在浑浊场景翻车从 YOLO 数据集说起做海洋水下垃圾检测的团队十有八九都经历过同一个场景在实验室用公开数据集跑出 0.85 的 mAP信心满满地丢进真实水域结果模型把海草认成塑料袋、把鱼群认成漂浮瓶召回率直接腰斩。问题往往不在 YOLO 算法本身而在数据集——水下成像的色偏、悬浮颗粒、光照衰减和陆地上那套 COCO、VOC 的分布差得太远。YOLO算法海洋水下垃圾检测数据集本质上是为这类场景准备的一套「图像 标注 训练配置」的组合拳让 YOLO 系列模型v5/v8/v11 都行能在水下这个特殊域里真正收敛。它解决的核心问题是把散落在各个水下机器人、ROV 拍摄素材、公开海洋监测影像里的垃圾目标整理成 YOLO 能直接吃的格式并给出针对水下成像特点的增强和调参策略。适合谁做海洋环保监测的算法工程师、水下机器人视觉方向的在校研究生、以及想用 YOLO 快速验证水下检测可行性的开发者。如果你手上只有一堆没标注的潜水视频或者拿 COCO 预训练权重直接下水这篇内容能帮你少走至少两周弯路。2. 水下垃圾数据集到底长什么样类别、标注与格式拆解2.1 水下垃圾的类别体系怎么定才不返工陆地垃圾检测常用「可回收/有害/其他」这种粗分类但水下场景完全不能照搬。我见过最惨的一次团队按陆标定义了 6 类结果标注到一半发现「塑料袋」和「塑料碎片」在水下根本分不清返工重标了 3000 张。比较稳妥的做法是按材质 形态双维度定类常见的水下垃圾检测类别体系如下类别名典型目标水下识别难点plastic_bag塑料袋、保鲜膜半透明与水体颜色接近plastic_bottle塑料瓶、饮料瓶反光易与气泡混淆metal_can易拉罐、金属罐锈蚀后颜色与礁石接近fishing_net废弃渔网、绳索细长结构YOLO 小目标易漏glass_bottle玻璃瓶折射导致边缘模糊tire轮胎、橡胶制品体积大但常被泥沙半掩other_trash无法归类的杂物类内差异极大类别数控制在 68 类是比较舒服的区间。太少区分度不够太多单类样本不足YOLO 的分类头会训崩。如果只做二分类「垃圾/非垃圾」那另说但实际落地项目基本都需要细分。2.2 YOLO 格式标注从原始标注到 labels 目录YOLO 要求每张图对应一个同名.txt每行格式是class_id x_center y_center width height全部归一化到 01。如果你用 LabelImg 或 CVAT 标出来的是 VOC 的 XML需要转一道。下面这个脚本是我常用的转换逻辑处理了边界裁剪和空标注两个坑import os import xml.etree.ElementTree as ET # 类别映射顺序必须和 data.yaml 里的 names 一致 CLASS_MAP { plastic_bag: 0, plastic_bottle: 1, metal_can: 2, fishing_net: 3, glass_bottle: 4, tire: 5, other_trash: 6 } def voc_to_yolo(xml_path, img_w, img_h, out_dir): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in CLASS_MAP: continue # 未定义类别直接跳过避免训练时报错 cls_id CLASS_MAP[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) if xmax xmin or ymax ymin: continue # 裁剪后无效的框丢弃 x_c (xmin xmax) / 2.0 / img_w y_c (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_c:.6f} {y_c:.6f} {w:.6f} {h:.6f}) if not lines: return # 空标注不生成文件否则 YOLO 会当负样本 out_path os.path.join(out_dir, os.path.basename(xml_path).replace(.xml, .txt)) with open(out_path, w) as f: f.write(\n.join(lines))逻辑说明先做类别映射未定义类别直接跳过而不是报错这在多人协作标注时很关键。边界裁剪那两行是血泪经验——水下目标经常贴着画面边缘标注员手一抖框就出界了不裁剪的话归一化坐标会大于 1YOLO 训练时直接报normalized coordinates outside [0,1]。空标注不生成文件是因为 YOLO 把没有对应 txt 的图片当纯背景负样本处理如果你确实想要负样本应该生成一个空 txt 文件而不是不生成。参数上CLASS_MAP的 value 必须从 0 连续编号中间断了 YOLO 会报类别数不匹配。img_w和img_h要读原图真实尺寸别用标注工具里显示的缩放后尺寸。2.3 data.yaml 与目录结构一次配对终身受用YOLOv8 之后统一用data.yaml描述数据集目录结构建议这样组织underwater_trash/ ├── images/ │ ├── train/ # 约 70% │ ├── val/ # 约 20% │ └── test/ # 约 10% ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml对应的data.yamlpath: /data/underwater_trash train: images/train val: images/val test: images/test nc: 7 names: [plastic_bag, plastic_bottle, metal_can, fishing_net, glass_bottle, tire, other_trash]注意names的顺序必须和转换脚本里的CLASS_MAP完全一致错一位整个训练结果的混淆矩阵就全乱了。划分比例上水下数据如果总量少于 3000 张建议 train:val:test 用 8:1:1验证集太小评估波动会很大。3. 用 YOLOv8 在水下垃圾数据集上跑通第一个基线3.1 环境与预训练权重别从零训水下数据集通常不大从零初始化训练基本没戏。常见做法是加载 COCO 预训练权重做迁移学习。YOLOv8 的加载方式很直接pip install ultralytics # 训练时指定预训练权重ultralytics 会自动下载 yolo detect train modelyolov8s.pt datadata.yaml epochs100 imgsz640 batch16yolov8s.pt是 small 版本参数量约 11M在水下这种中等难度任务上性价比最高。n 版太浅对细长渔网这种小目标欠拟合m/l 版在数据量不足 5000 张时容易过拟合。imgsz640是默认值但如果你的原始图像是 4K 水下相机拍的建议先缩到 1280 再训直接 640 会丢太多小目标细节。3.2 水下专属的数据增强配置YOLO 默认的 HSV 增强是为陆地设计的水下图像本身色偏严重直接套用会加剧颜色失真。我一般会自定义一个增强配置重点调整色调和饱和度扰动范围from ultralytics import YOLO model YOLO(yolov8s.pt) model.train( datadata.yaml, epochs100, imgsz640, batch16, hsv_h0.010, # 默认 0.015水下降低色调扰动 hsv_s0.500, # 默认 0.7饱和度扰动也收窄 hsv_v0.400, # 亮度保持默认附近水下亮度变化本就大 degrees10.0, # 旋转角度水下目标朝向随机 translate0.1, scale0.5, fliplr0.5, # 水平翻转水下左右对称场景可用 mosaic1.0, # 马赛克增强小目标友好 mixup0.1, # 混合增强别开太高否则水下目标更糊 patience20, # 20 轮无提升就早停 device0 )参数说明hsv_h调到 0.01 是因为水下图像整体偏蓝绿色调扰动太大会让模型学到错误的颜色先验。hsv_s降到 0.5 同理水下饱和度本身就被水体吸收削弱了。mosaic1.0保持开启它对小目标检测提升明显废弃渔网这种细长目标受益最大。mixup只开 0.1开高了水下图像叠加后更浑浊模型反而学不到清晰特征。3.3 训练过程看什么loss 曲线与 mAP 的读法训练启动后runs/detect/train/下会生成results.csv和一堆曲线图。重点盯三个信号第一box_loss和cls_loss是否同步下降。如果 cls_loss 震荡不降多半是类别不平衡某个类别样本太少。第二mAP50在前 10 轮应该快速爬升到 0.3 以上如果 10 轮还在 0.1 徘徊检查标注格式或 data.yaml 路径。第三验证集 loss 如果连续 15 轮上升而训练 loss 还在降就是过拟合了该早停或加增强。有个玄学现象水下数据集的 mAP 曲线经常在 6080 轮之间突然跳一下这是因为 mosaic 增强在最后 10 轮默认关闭模型开始适应真实分布。所以别看到中间平台期就手动停让它跑完。4. 水下检测的坑从标注到部署的 5 个翻车现场4.1 坑一把气泡标成塑料瓶现象模型在验证集上 plastic_bottle 的精确率很高但一放到有大量气泡的视频里就疯狂误检。原因标注阶段标注员把成串气泡框成了塑料瓶因为水下气泡和半透明瓶体在低分辨率下确实像。解决标注规范里明确写「气泡不标」并在标注工具里放大到 200% 再判断已经标错的用置信度阈值 0.5 以上的预测结果反向筛查人工复核高置信度误检样本。4.2 坑二类别编号不连续导致训练直接崩现象训练启动几秒后报AssertionError: nc mismatch或IndexError: index out of range。原因data.yaml里nc: 7但标注文件里出现了 class_id 7 甚至 8通常是转换脚本的 CLASS_MAP 和 yaml 的 names 顺序不一致。解决写一个校验脚本遍历所有 txt 文件统计 class_id 的最大值和 nc 对比import glob max_id -1 for txt in glob.glob(labels/**/*.txt, recursiveTrue): with open(txt) as f: for line in f: if line.strip(): max_id max(max_id, int(line.split()[0])) print(fmax class_id {max_id}, 请确保 nc {max_id 1})4.3 坑三验证集 mAP 虚高测试集一塌糊涂现象val mAP50 到 0.82test 只有 0.51。原因划分数据时按视频帧随机切分同一段视频的相邻帧同时进了 train 和 val模型其实在「背」场景。解决按视频源或拍摄批次划分同一段视频的帧只能进同一个集合。如果数据来自多个 ROV 任务按任务 ID 分组划分最稳。4.4 坑四小目标渔网漏检严重现象plastic_bag 和 metal_can 召回率 0.8fishing_net 只有 0.3。原因渔网是细长结构在 640 分辨率下经过多次下采样后特征几乎消失。解决把imgsz提到 960 或 1280同时在data.yaml同级加一个anchors自定义配置YOLOv8 已改为自适应锚框但可以通过增大imgsz间接改善另一个办法是在训练时对含 fishing_net 的样本做 2 倍过采样。4.5 坑五BN 层在水下小批量训练时崩溃现象训练几十轮后 loss 突然变 NaN报BN momentum相关警告。原因batch size 太小比如 4 或 8水下图像分布又和预训练分布差异大BN 统计量估计不准。解决把 batch 提到 16 以上显存不够就降 imgsz 到 512或者改用yolov8s.pt时冻结前 10 层 BN只训检测头。这个坑在 V100 上跑大模型时反而少见因为 batch 能开大。5. 把 mAP 再往上推 5 个点三个我反复验证过的技巧5.1 用测试时增强TTA榨干最后一点精度YOLOv8 内置了 TTA推理时加augmentTrue即可。它会对每张图做多尺度 翻转推理再融合结果代价是推理速度慢 23 倍但 mAP 通常能涨 13 个点。水下场景尤其适合因为水体折射导致目标在不同尺度下特征差异大多尺度融合能互补。from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict( sourcetest_images/, augmentTrue, # 开启 TTA conf0.25, iou0.45, imgsz960, saveTrue )conf0.25是水下场景比较稳的阈值低于 0.2 误检会明显增多。iou0.45用于 NMS渔网这种重叠目标多的情况可以调到 0.5 减少漏检。5.2 类别不平衡用 copy-paste 增强补样本如果某个类别比如 glass_bottle只有 200 张别硬训。用 copy-paste 把该类别目标抠出来随机贴到其他水下背景图上能快速扩充到 8001000 张。注意粘贴时要匹配水下光照直接贴会显得突兀。我一般会做一次高斯模糊 颜色迁移让粘贴区域和背景色调一致。这个技巧在 YOLO 官方文档里没写但在水下这种长尾分布场景里效果拔群。5.3 验证时别只看 mAP混淆矩阵才是排错利器YOLO 训练完会在runs/detect/train/下生成confusion_matrix.png。水下检测最常出现的混淆是 plastic_bag ↔ fishing_net 和 metal_can ↔ tire。前者是因为都是半透明或细长后者是因为锈蚀后颜色接近。看到混淆矩阵里某两个类互相误判严重优先补这两类的区分性样本比盲目加数据有效得多。最后说个我自己的习惯每次训完模型我都会挑 20 张验证集里预测错的图逐张看是标注错了、目标太小、还是真的长得像。十次里有六次是标注问题。水下数据集的质量比换什么模型、调什么参数都重要。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网