海洋垃圾检测数据集:1000张图、三种标签格式与YOLO11一键训练落地指南
发布时间:2026/9/30 4:16:58来源:尧图网络
简介这份资源面向从事海洋环境监测、水下目标检测的算法工程师与深度学习学习者提供一套真实拍摄的海洋海底垃圾检测数据集可用于海底监控场景下的垃圾识别项目也可作为通用水下检测任务的数据补充。数据集共1000张高质量图像覆盖海底塑料、铁罐、纸张、木头、橡胶手套等垃圾以及海洋生物、水下探测器、时间条等同框场景标注包含plastic、bio、rov、metal、paper、wood、rubber、timestamp、unknown九个类别。资源包为1个PDF文件大小约2.77MB内附数据集基本情况介绍与获取方式标注采用labelimg完成同时提供VOC、COCO、YOLO三种格式标签可直接用于YOLO等算法训练。此外还附赠YOLO11一键训练脚本支持GPU、CPU及Mac多平台方案并给出训练结果日志供参考。目前已有540人学习适合希望快速上手水下垃圾检测实战的读者。1. 海洋垃圾检测数据集1000 张图、三种标签格式与 YOLO11 一键训练到底怎么落地海面漂浮物检测这件事真正卡住大多数团队的从来不是模型结构而是数据。你搜「目标检测」能翻出一堆论文和开源仓库但真到要训一个能识别塑料瓶、渔网、泡沫块的模型时手里没有标注干净的图一切白搭。这个标题讲的是一套已经整理好的海洋垃圾检测数据集1000 张真实海面场景图同时提供 VOC、COCO、YOLO 三种标签格式配一份能在 GPU、CPU、Mac 三平台跑起来的 YOLO11 一键训练脚本。它解决的是「从零标注到能训出第一个可用模型」之间那段最耗人力的路。适合两类人一类是想快速验证海洋垃圾检测可行性的算法工程师一类是手里有算力但缺数据、想直接拿现成数据集跑通全流程的学生和独立开发者。下面我按「数据长什么样 → 三种格式怎么选怎么转 → 脚本怎么跑 → 坑在哪」的顺序把这条链路拆开讲清楚。2. 先看清数据1000 张图、类别分布与三种标签格式的取舍拿到一个数据集第一件事不是急着训而是先搞清楚它到底装了什么。1000 张图在目标检测里属于小数据集能不能训出东西取决于类别是否均衡、标注是否干净、场景是否单一。海洋垃圾检测的难点在于目标尺度差异极大——一个远处的塑料瓶可能只有十几个像素一张漂浮的渔网可能占半张图这种尺度分布直接决定你后面 anchor 和输入分辨率怎么设。2.1 海洋垃圾的典型类别与标注边界海洋垃圾检测常见的类别划分有这么几类塑料瓶plastic bottle、塑料袋/薄膜plastic bag、渔网与绳索fishing net / rope、泡沫塑料styrofoam、金属罐metal can、玻璃瓶glass bottle有些数据集还会单列「其他漂浮物」。类别数一般落在 5 到 8 之间。类别越多1000 张图摊到每个类上的样本就越少这是小数据集最现实的约束。标注边界是另一个容易翻车的地方。海面反光、浪花、半沉入水的垃圾标注员很容易把边界框画大或画小。我一般会先抽 30 到 50 张图肉眼过一遍重点看三类问题框是否把水面反光也框进去了、密集小目标有没有漏标、被遮挡的目标框是否合理。这一步花二十分钟能省掉后面训练时 loss 不收敛的几小时排查。提示小数据集里标注质量对最终 mAP 的影响往往比换模型结构更大。先信数据再信模型。2.2 VOC、COCO、YOLO 三种格式各自适合什么场景同一个数据集给三种格式不是让你随便挑而是对应不同的下游工具链。选错了格式后面要么写转换脚本要么在训练框架里报一堆找不到标签的错。格式目录/文件结构坐标表示典型用途VOCAnnotations/*.xml JPEGImages/左上角 xmin,ymin 右下角 xmax,ymax 绝对像素老牌检测框架、部分标注工具导入导出COCOannotations/*.json 单文件[x,y,width,height] 绝对像素按 image_id 组织MMDetection、Detectron2、COCO 评测YOLOlabels/*.txt images/归一化中心点 cx,cy 宽高 w,h0~1Ultralytics YOLO 系列直接训练如果你就是要跑 YOLO11直接用 YOLO 格式最省事不用转。VOC 和 COCO 的价值在于兼容性和可迁移性——比如你想拿这份数据去对比 MMDetection 里的某个模型COCO 格式就是刚需。我的习惯是以 YOLO 格式为主训练同时保留 VOC 原始标注作为「后悔药」因为 XML 里保留了图片尺寸和原始文件名回溯问题时最方便。2.3 用脚本核对图片与标签是否一一对应数据集最常见的隐性问题是图片和标签对不上有图没标签、有标签没图、文件名大小写不一致。训练时框架通常不会报错只是默默跳过最后你发现实际参与训练的图远少于 1000 张。跑之前先核对一遍。import os img_dir images lbl_dir labels imgs {os.path.splitext(f)[0] for f in os.listdir(img_dir)} lbls {os.path.splitext(f)[0] for f in os.listdir(lbl_dir)} only_img imgs - lbls # 有图无标签 only_lbl lbls - imgs # 有标签无图 print(f图片总数: {len(imgs)}, 标签总数: {len(lbls)}) print(f有图无标签: {len(only_img)} - {list(only_img)[:5]}) print(f有标签无图: {len(only_lbl)} - {list(only_lbl)[:5]})这段脚本用集合差集找出不匹配的文件。img_dir和lbl_dir换成你实际的目录名。如果only_img数量超过总数的 5%说明数据集本身有问题要么补标要么剔除。only_lbl一般较少但出现就说明有标签文件是孤儿直接删掉即可。这一步不涉及任何模型纯文件系统操作几秒钟跑完却能避免后面训练日志里「图片数对不上」的玄学问题。3. 三种格式互转VOC 转 YOLO、COCO 转 YOLO 的脚本与参数实际项目里你拿到的往往是 VOC 或 COCO但训练要用 YOLO。转换脚本网上一搜一大把但真正能一次跑通、不丢类别、不搞错坐标的没几个。这一章把两个最常用的转换方向写清楚重点是坐标归一化和类别映射这两个最容易出错的环节。3.1 VOC 转 YOLO坐标归一化与类别映射VOC 的 XML 里存的是绝对像素坐标YOLO 要的是归一化后的中心点加宽高。转换的核心就一个公式cx (xmin xmax) / 2 / Wcy (ymin ymax) / 2 / Hw (xmax - xmin) / Wh (ymax - ymin) / H。W 和 H 是图片的实际宽高必须从 XML 的size节点里读不能想当然用固定值。import os import xml.etree.ElementTree as ET # 类别名到 id 的映射顺序必须和训练时的 data.yaml 一致 classes [plastic_bottle, plastic_bag, fishing_net, styrofoam, metal_can, glass_bottle] class_to_id {c: i for i, c in enumerate(classes)} def voc_to_yolo(xml_path, out_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) W int(size.find(width).text) H int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in class_to_id: continue # 跳过未定义类别避免 id 错位 cid class_to_id[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) cx (xmin xmax) / 2.0 / W cy (ymin ymax) / 2.0 / H w (xmax - xmin) / W h (ymax - ymin) / H lines.append(f{cid} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(out_path, w) as f: f.write(\n.join(lines))classes列表的顺序就是最终标签里的类别 id必须和data.yaml里的names完全一致否则模型学到的类别会整体错位。class_to_id用字典做映射遇到不在列表里的类别直接continue跳过而不是报错中断——真实数据里偶尔会有标注员写错的类别名跳过比崩溃更实用。坐标保留 6 位小数足够YOLO 官方也是这个精度。转换完记得抽查几个 txt确认数值都在 0 到 1 之间出现大于 1 的值说明 XML 里的坐标本身就越界了。3.2 COCO 转 YOLO处理 image_id 与类别 id 的错位COCO 的 JSON 结构比 VOC 复杂坑也更多。它的categories里 id 往往不是从 0 开始的连续整数比如从 1 开始或者中间有跳号而 YOLO 要求类别 id 从 0 开始连续。直接拿 COCO 的 category_id 当 YOLO 的 class id是新手最常踩的坑训出来的模型类别全乱。import json import os coco_json annotations/instances.json out_dir labels os.makedirs(out_dir, exist_okTrue) with open(coco_json) as f: data json.load(f) # 建立 COCO category_id - 连续 YOLO id 的映射 cat_ids sorted(c[id] for c in data[categories]) coco2yolo {cid: i for i, cid in enumerate(cat_ids)} # image_id - (file_name, width, height) img_info {img[id]: img for img in data[images]} # 按 image_id 聚合标注 from collections import defaultdict anns defaultdict(list) for ann in data[annotations]: anns[ann[image_id]].append(ann) for img_id, info in img_info.items(): W, H info[width], info[height] stem os.path.splitext(info[file_name])[0] lines [] for ann in anns.get(img_id, []): cid coco2yolo[ann[category_id]] x, y, w, h ann[bbox] # COCO 是左上角 宽高 cx (x w / 2) / W cy (y h / 2) / H lines.append(f{cid} {cx:.6f} {cy:.6f} {w/W:.6f} {h/H:.6f}) with open(os.path.join(out_dir, stem .txt), w) as f: f.write(\n.join(lines))关键在coco2yolo这个映射先把所有 category_id 排序再重新编号成 0、1、2……这样无论原始 id 怎么跳号输出都是连续的。ann[bbox]在 COCO 里是[x, y, width, height]注意是左上角坐标加宽高不是右下角这点和 VOC 不同转换时别搞混。defaultdict(list)按 image_id 聚合标注避免对每张图都遍历一遍全部标注1000 张图的规模下性能差异不明显但习惯要养好。转换后同样抽查重点看有没有类别 id 超出len(classes)-1的情况。3.3 转换后必须做的三项校验转换脚本跑完不代表就对了。我一般固定做三项校验缺一不可。第一项数量校验YOLO 标签文件数应该等于图片数每个 txt 的行数之和应该等于原始标注框总数。第二项范围校验遍历所有 txt确认每行的 cx、cy、w、h 都在 0 到 1 之间出现越界值说明原标注有问题。第三项可视化校验随机抽 10 张图把 YOLO 标签画回图上肉眼看框是否贴合目标。第三项最费时间但最有效很多坐标错位的问题肉眼一看就出来了。import os def check_range(lbl_dir): bad [] for fn in os.listdir(lbl_dir): with open(os.path.join(lbl_dir, fn)) as f: for i, line in enumerate(f): parts line.strip().split() if len(parts) ! 5: bad.append((fn, i, 字段数不对)) continue vals list(map(float, parts[1:])) if any(v 0 or v 1 for v in vals): bad.append((fn, i, 坐标越界)) return bad issues check_range(labels) print(f问题条目数: {len(issues)}) for item in issues[:10]: print(item)check_range同时检查字段数和坐标范围字段数不是 5 说明这行标签格式坏了坐标越界说明原标注或转换公式有问题。返回的issues列表前 10 条打印出来定位通常问题集中在少数几个文件上单独修就行。4. YOLO11 一键训练脚本GPU、CPU、Mac 三平台的参数差异数据集准备好了接下来是训练。标题里说的「一键训练脚本」核心价值在于抹平平台差异——同一份代码在带 NVIDIA 显卡的机器上走 CUDA在 Mac 上走 MPS在没有独显的机器上退回 CPU。这一章把脚本结构、关键参数和三平台的配置差异讲透。4.1 脚本骨架与 data.yaml 的写法Ultralytics 的 YOLO11 训练入口很简洁但data.yaml写错一个路径就白跑。先看数据配置文件的标准写法。# data.yaml path: /abs/path/to/dataset # 数据集根目录建议用绝对路径 train: images/train val: images/val test: images/test names: 0: plastic_bottle 1: plastic_bag 2: fishing_net 3: styrofoam 4: metal_can 5: glass_bottlepath用绝对路径能避免「在哪个目录下运行脚本」导致的相对路径歧义这是最常见的翻车点之一。train、val、test是相对path的子路径YOLO 会自动把images替换成labels去找标签所以目录结构必须是images/train配labels/train。names的 id 必须和前面转换脚本里的类别顺序严格一致这是整条链路里最容易断的一环。4.2 三平台设备选择cuda、mps、cpu 怎么自动判断一键脚本的关键是自动选设备而不是让用户手动改。判断逻辑很简单先看有没有 CUDA再看是不是 Apple Silicon最后退回 CPU。import torch from ultralytics import YOLO def pick_device(): if torch.cuda.is_available(): return cuda:0 # NVIDIA GPU if torch.backends.mps.is_available(): return mps # Apple Silicon (M 系列) return cpu # 无独显退回 CPU device pick_device() print(f使用设备: {device}) model YOLO(yolo11n.pt) # 用预训练权重起步小数据集必备 results model.train( datadata.yaml, epochs100, imgsz640, batch16 if device ! cpu else 4, devicedevice, workers8 if device ! cpu else 2, patience20, projectruns/ocean, nameyolo11n_baseline, )pick_device的顺序不能反Mac 上如果先判断 MPS某些装了 CUDA 兼容层的环境会误判。batch在 CPU 上必须调小16 的 batch 在 CPU 上大概率直接爆内存4 是保守值。workers是数据加载线程数CPU 上开太多反而拖慢2 到 4 足够。patience20表示 20 个 epoch 验证指标不提升就早停小数据集上这个参数能省不少时间。yolo11n.pt是 nano 版本的预训练权重1000 张图这种规模从预训练起步比从头训收敛快得多这是小数据集的常识。4.3 关键训练参数imgsz、batch、epochs 在小数据集上怎么设1000 张图属于小数据集参数设置和大数据集有明显区别照搬 COCO 那套配置基本会过拟合。imgsz默认 640海洋垃圾里小目标多理论上调大到 960 或 1280 能提升小目标召回但显存占用和训练时间会成倍增加。我的建议是先跑 640 拿到 baseline如果小目标漏检严重再考虑 960。batch在 GPU 上 16 是稳妥起点显存够可以上 32不够就降到 8配合梯度累积也能达到类似效果。epochs设 100 到 300小数据集收敛快但太少会欠拟合配合patience早停实际不会跑满。学习率方面YOLO11 默认的lr00.01对预训练微调来说偏大小数据集上我一般降到 0.001 到 0.005减少把预训练权重「冲掉」的风险。数据增强默认开着 mosaic 和 mixup小数据集上这些增强是防止过拟合的主力不建议关。如果发现训练 loss 震荡厉害先降学习率再考虑关掉 mixup。注意小数据集上验证集 mAP 波动大是正常的别看到某个 epoch 掉点就急着调参看整体趋势。4.4 训练过程监控与断点续训训练启动后runs/ocean/yolo11n_baseline/下会生成results.csv、权重文件和几张可视化图。results.csv每行一个 epoch记录了 box_loss、cls_loss、mAP50、mAP50-95 等指标用 pandas 读出来画个曲线比盯着终端刷屏直观得多。import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/ocean/yolo11n_baseline/results.csv) df.columns df.columns.str.strip() # 列名可能带空格先清理 fig, ax plt.subplots(1, 2, figsize(12, 4)) ax[0].plot(df[epoch], df[train/box_loss], labelbox_loss) ax[0].plot(df[epoch], df[train/cls_loss], labelcls_loss) ax[0].legend(); ax[0].set_title(train loss) ax[1].plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) ax[1].plot(df[epoch], df[metrics/mAP50-95(B)], labelmAP50-95) ax[1].legend(); ax[1].set_title(val mAP) plt.savefig(train_curve.png, dpi120)df.columns.str.strip()这行别省Ultralytics 导出的 CSV 列名有时带前导空格不清理会 KeyError。看曲线主要看两点train loss 是否稳定下降、val mAP 是否在某个点后不再提升。如果 train loss 一直降但 val mAP 早早走平甚至下降就是过拟合该早停了。断点续训用model.train(resumeTrue)前提是上次训练的last.pt还在且name指向同一个目录。5. 避坑与排查小数据集训海洋垃圾检测最容易翻的五个地方这一章是我自己踩过和帮别人排查过的真实问题按「现象 → 原因 → 解决」写。小数据集训练的问题往往不在模型本身而在数据和配置的细节里。现象一训练启动就报「No labels found」。原因通常是data.yaml里的path写成了相对路径而脚本运行目录和数据集目录不在同一层YOLO 找不到labels文件夹。解决把path改成绝对路径并确认images/train旁边确实有labels/train两个目录名必须严格对应。现象二训练正常但 mAP 一直是 0 或极低。最常见的原因是类别 id 错位——data.yaml里names的顺序和标签文件里的 class id 对不上。比如标签里 0 是塑料瓶但 yaml 里 0 写成了渔网模型学到的全是错的。解决打印几个标签文件的前几行对照names逐个核对转换脚本里的classes列表和 yaml 必须完全一致。现象三CPU 上训练直接卡死或内存爆掉。原因是batch和workers沿用了 GPU 的配置。CPU 上 batch16 加上多线程数据加载内存瞬间打满。解决CPU 平台把batch降到 4 甚至 2workers降到 2imgsz也可以从 640 降到 416 先跑通流程。现象四Mac 上 MPS 报错或训练极慢。部分 PyTorch 版本对 MPS 的某些算子支持不完整会回退到 CPU 或者直接报错。解决先确认 PyTorch 版本支持 MPS训练时如果报算子不支持把device显式设成cpu先跑通或者升级 PyTorch。MPS 本身速度介于 CPU 和入门独显之间别期待太高。现象五验证集 mAP 忽高忽低曲线像心电图。1000 张图分到验证集可能只有一两百张样本量小导致指标波动大这是统计上的必然不是模型坏了。解决看平滑后的趋势而不是单点适当增大验证集比例比如 8:2 而不是 9:1或者用 k 折交叉验证取平均代价是训练时间成倍增加。6. 从能跑到好用提升海洋垃圾检测精度的一个具体技巧跑通训练只是起点真正决定这个方案值不值得投入的是能不能把 mAP 从「能看」提到「能用」。小数据集上提升精度与其折腾模型结构不如把力气花在数据侧。我试过最有效的一招是「针对性补数据 难例挖掘」的组合具体做法是先用 baseline 模型在验证集和一批未标注的测试图上推理把置信度在 0.1 到 0.5 之间的预测框挑出来这些就是模型「拿不准」的难例。人工复核这些难例把漏标的补上、错标的修正再混进训练集重新训一轮。这个流程的价值在于它让有限的标注精力花在模型真正需要的地方而不是均匀地标所有图。1000 张图里可能只有 200 张包含密集小目标或严重遮挡这 200 张的标注质量提升对 mAP 的贡献远大于再标 500 张简单场景。from ultralytics import YOLO model YOLO(runs/ocean/yolo11n_baseline/weights/best.pt) results model.predict( sourceunlabeled_images, conf0.1, # 低阈值尽量多召回候选 iou0.5, save_txtTrue, # 保存预测结果为 txt便于筛选 save_confTrue, # 带上置信度方便按区间挑难例 projectruns/hard_examples, nameround1, )conf0.1是关键故意设低是为了把模型不确定的框也捞出来这些正是难例的来源。save_confTrue让每个预测框带上置信度之后按 0.1 到 0.5 区间筛选即可。save_txtTrue输出的格式和 YOLO 标签一致复核修正后可以直接当训练标签用。这个流程跑一轮大概增加半天到一天的人工但通常能带来几个点的 mAP 提升比盲目加 epoch 划算得多。另一个值得养成的习惯是固定验证集。小数据集上每次调参都换验证集指标就没有可比性你会陷入「这次涨了是调参有效还是运气好」的玄学里。我一般会从 1000 张里划出固定的 150 张作为「从不参与训练、只用于最终对比」的 holdout 集所有实验都在同一套验证集上比结论才站得住。最后说个我自己的教训早期做这类项目我总想一步到位把模型调到最好结果在数据没理清的情况下反复调参浪费了大量时间。后来固定成「先核对数据 → 再跑 baseline → 再针对性补难例」的节奏反而更快出结果。海洋垃圾检测这个方向数据质量的天花板远低于模型结构的天花板把数据这关守住YOLO11 这种成熟模型自然能给你一个能用的结果。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网