简介:这份资源面向从事水下视觉与目标检测的开发者、研究生及工程团队,提供一套真实拍摄的海洋海底垃圾检测数据集,可用于海底监控场景下的垃圾识别项目,也可作为通用水下垃圾检测数据的补充。数据集共1000张高质量图像,覆盖海底塑料、铁罐、纸张、海洋生物与垃圾同框、水下探测器与垃圾同框、打光拍摄等多种场景,标注包含plastic、bio、rov、metal、paper、wood、rubber、timestamp、unknown九个类别,采用labelimg标注,质量较高。资源以PDF形式交付,文件总数1个,大小约2.77MB,内附数据集基本情况介绍与获取方式,并同步提供VOC、COCO、YOLO三种主流格式标签,可直接接入YOLO等算法训练。此外还附赠YOLO11一键训练脚本,支持GPU、CPU及Mac(M芯片)多平台方案,并给出博主训练结果日志供参考。目前已有540人学习,适合希望快速验证模型、复现训练流程并积累水下检测经验的读者。
1. 海洋垃圾检测数据集:1000 张图、三种标签格式与三平台训练脚本到底怎么用
手里有一份标注好的海洋垃圾检测数据集,1000 张图,同时给了 VOC、COCO、YOLO 三种格式标签,还配了支持 GPU、CPU、Mac 三平台的 YOLO11 一键训练脚本——这个组合对做目标检测落地的人来说,省掉的不是一点半点时间。海洋垃圾检测本身是个很实际的方向:海面漂浮物、岸边塑料瓶、渔网碎片,这些目标尺度差异大、背景杂乱、反光干扰强,用通用数据集训出来的模型往往直接翻车。1000 张图不算多,但胜在场景聚焦、标签齐全,适合做小目标检测的快速验证和迁移学习起点。这篇文章面向的是想拿这份数据直接跑通训练、又不想在格式转换和环境配置上反复踩坑的从业者,从数据长什么样、三种格式怎么选、脚本怎么改参数,一路讲到训练崩了怎么排查。
2. 三种标签格式的取舍:VOC、COCO、YOLO 各自适合什么场景
拿到一份同时带 VOC、COCO、YOLO 三种格式标签的数据集,第一反应不应该是"全都要",而是先搞清楚每种格式在训练链路里扮演什么角色。选错了格式,轻则多写一堆转换脚本,重则标签解析出错、训练时 loss 直接不降。
2.1 VOC 格式的结构与适用边界
VOC 格式的核心是每张图对应一个 XML 文件,里面用<object>节点记录每个目标的类别名和边界框坐标,坐标是绝对像素值,原点在左上角。它的典型目录结构是Annotations/放 XML、JPEGImages/放原图、ImageSets/Main/放训练验证划分的 txt。
<annotation> <filename>ocean_0001.jpg</filename> <size> <width>1920</width> <height>1080</height> </size> <object> <name>plastic</name> <!-- 类别名,注意大小写要和类别表一致 --> <bndbox> <xmin>312</xmin> <!-- 绝对像素坐标,不是归一化值 --> <ymin>540</ymin> <xmax>398</xmax> <ymax>612</ymax> </bndbox> </object> </annotation>VOC 的好处是可读性强,用 LabelImg 打标直接产出这个格式,人工检查标签时一眼能看出框对不对。但它的短板也明显:坐标是绝对像素,换分辨率就要重算;一个 XML 一个目标文件,1000 张图就是 1000 个文件,批量处理时 IO 开销不小。我一般把 VOC 当作"原始标注存档",真正训练前一定转成 YOLO 格式。
2.2 COCO 格式的 JSON 组织方式
COCO 格式把所有标注塞进一个 JSON 文件,用images、annotations、categories三个数组互相通过 id 关联。边界框是[x, y, width, height],同样是绝对像素,但原点在左上角、宽高而非右下角坐标。
{ "images": [ {"id": 1, "file_name": "ocean_0001.jpg", "width": 1920, "height": 1080} ], "annotations": [ {"id": 1, "image_id": 1, "category_id": 1, "bbox": [312, 540, 86, 72], // x, y, w, h 绝对像素 "area": 6192, "iscrowd": 0} ], "categories": [ {"id": 1, "name": "plastic"} ] }COCO 格式的优势是生态广,很多评估脚本、可视化工具、预训练模型都吃这个格式,做 mAP 评测时尤其方便。缺点是单文件体积大,1000 张图的标注 JSON 动辄几 MB,手改容易出错,必须靠脚本生成。如果你的下游要做标准 COCO 评测,保留这份格式;如果只是训 YOLO,它更多是个"中转站"。
2.3 YOLO 格式为什么是训练首选
YOLO 格式每张图对应一个 txt,每行一个目标:类别索引 中心x 中心y 宽 高,后四个值全部归一化到 0~1。这是它和 VOC/COCO 最本质的区别——归一化坐标让模型对输入分辨率不敏感,换 640 还是 1280 训练都不用改标签。
# ocean_0001.txt 0 0.1849 0.5333 0.0448 0.0667 # 类别索引 中心x 中心y 宽 高,均为归一化值对应的data.yaml负责把类别索引映射回名字:
path: ./ocean_dataset train: images/train val: images/val nc: 4 # 类别数,必须和实际类别数一致 names: # 索引顺序必须和 txt 里的数字对应 0: plastic 1: fishing_net 2: bottle 3: foam注意:YOLO 格式最容易翻车的地方是类别索引和 names 顺序对不上。txt 里写 0,names 里第 0 个却是别的类,训练照样跑,但模型学到的全是错的,mAP 低到怀疑人生还找不到原因。
三种格式的定位可以这样记:VOC 是标注存档,COCO 是评测通用货币,YOLO 是训练直用格式。1000 张图的数据集,我一般保留 VOC 原始档、生成 COCO 备用、训练只用 YOLO。
3. 从 VOC 到 YOLO 的转换脚本与四个边界坑
数据集虽然号称三种格式齐全,但实际拿到手经常发现 YOLO 标签缺几张、或者类别名不统一。自己写一遍转换脚本,比盲目信任现成标签靠谱得多。这一章把 VOC 转 YOLO 的完整脚本拆开讲,顺带把四个最容易踩的边界坑说透。
3.1 转换脚本的完整实现
import os import xml.etree.ElementTree as ET from pathlib import Path # 类别名到索引的映射,顺序必须和 data.yaml 的 names 完全一致 CLASS_MAP = {"plastic": 0, "fishing_net": 1, "bottle": 2, "foam": 3} def voc_to_yolo(xml_dir, img_dir, out_dir): xml_dir, img_dir, out_dir = Path(xml_dir), Path(img_dir), Path(out_dir) out_dir.mkdir(parents=True, exist_ok=True) skipped = [] for xml_path in xml_dir.glob("*.xml"): 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_MAP: # 坑1:类别名不在映射表里 skipped.append((xml_path.name, name)) continue cls_id = CLASS_MAP[name] box = obj.find("bndbox") xmin = float(box.find("xmin").text) ymin = float(box.find("ymin").text) xmax = float(box.find("xmax").text) ymax = float(box.find("ymax").text) # 坑2:坐标越界,裁剪到图像范围内 xmin, ymin = max(0, xmin), max(0, ymin) xmax, ymax = min(w, xmax), min(h, ymax) # 坑3:宽高为 0 的退化框,直接丢弃 bw, bh = xmax - xmin, ymax - ymin if bw <= 1 or bh <= 1: continue # 归一化:中心点 + 宽高,全部除以图像尺寸 cx = (xmin + xmax) / 2 / w cy = (ymin + ymax) / 2 / h lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {bw/w:.6f} {bh/h:.6f}") # 坑4:没有有效目标的图,生成空 txt 还是跳过? if not lines: continue out_file = out_dir / (xml_path.stem + ".txt") out_file.write_text("\n".join(lines)) if skipped: print(f"跳过的类别: {set(s for _, s in skipped)}") return len(list(out_dir.glob("*.txt"))) if __name__ == "__main__": n = voc_to_yolo("./Annotations", "./JPEGImages", "./labels") print(f"转换完成,共生成 {n} 个标签文件")脚本逻辑分四步:解析 XML 拿到图像尺寸、遍历每个 object 取类别和框、裁剪越界坐标并丢弃退化框、归一化后写入 txt。CLASS_MAP是唯一需要你手动对齐的地方,类别名一个字母都不能差。
3.2 四个边界坑的具体表现
坑一:类别名大小写和空格不一致。现象是转换后某些类别的目标凭空消失,脚本打印出跳过的类别名。原因是标注时有人写Plastic、有人写plastic(带尾空格)。解决方式是在CLASS_MAP查找前先strip()并统一小写,或者干脆先跑一遍统计所有出现过的类别名。
坑二:坐标越界。现象是训练时 YOLO 报non-normalized coordinates或坐标大于 1 的警告。原因是标注框超出了图像边界,LabelImg 有时允许拖到画布外。解决方式是像脚本里那样用max(0, ...)和min(w, ...)硬裁剪。
坑三:宽高为 0 的退化框。现象是训练 loss 出现 NaN。原因是标注时误点产生了一个点状框,宽高算出来是 0,归一化后除零。解决方式是丢弃bw <= 1 or bh <= 1的框。
坑四:空标签图的处理。现象是训练时提示某张图没有标签。这其实不算错误——YOLO 允许背景图存在,但要求有对应的空 txt 文件。如果你的数据集里混了纯背景图,要么生成空 txt,要么从训练集里剔除,别让图片和标签对不上号。
3.3 转换后的自检清单
转完不要直接开训,先跑一遍自检:
# 检查图片和标签是否一一对应 python -c " from pathlib import Path imgs = {p.stem for p in Path('./images').glob('*.jpg')} lbls = {p.stem for p in Path('./labels').glob('*.txt')} print('有图无标签:', imgs - lbls) print('有标签无图:', lbls - imgs) "再抽查几个 txt,确认类别索引在0 ~ nc-1范围内、四个数值都在 0~1 之间。这一步花两分钟,能省掉后面几小时的排查。
4. YOLO11 一键训练脚本:三平台参数怎么改
一键脚本的价值在于把环境差异封装掉,但"一键"不等于"不用改"。GPU、CPU、Mac 三种平台的差异主要体现在设备参数、批大小和精度设置上,改错一个参数,要么跑不起来,要么慢到无法接受。
4.1 脚本的核心结构与设备参数
一个典型的一键训练脚本,核心就是加载模型、指定数据配置、设置训练超参、启动训练四步:
from ultralytics import YOLO def train(device="0", batch=16, epochs=100, imgsz=640, amp=True): model = YOLO("yolo11n.pt") # 从预训练权重起步,小数据集强烈建议 results = model.train( data="./ocean_dataset/data.yaml", epochs=epochs, imgsz=imgsz, batch=batch, device=device, # "0" 表示第一块 GPU,"cpu" 表示纯 CPU,"mps" 表示 Mac amp=amp, # 混合精度,CPU 和部分 Mac 上要关掉 workers=8, # 数据加载线程数,Windows 上建议降到 4 以下 project="./runs", name="ocean_yolo11", patience=30, # 30 轮无提升就早停,小数据集防过拟合 ) return results if __name__ == "__main__": train()device是最关键的参数:NVIDIA GPU 填"0"或多卡"0,1";纯 CPU 填"cpu";Apple Silicon 填"mps"。填错会直接报设备不可用。
4.2 GPU、CPU、Mac 三平台的参数对照
| 平台 | device | batch 建议 | amp | workers | 备注 |
|---|---|---|---|---|---|
| NVIDIA GPU | "0" | 16~32 | True | 8 | 显存不足就降 batch |
| 纯 CPU | "cpu" | 4~8 | False | 4 | 1000 张图训 100 轮可能要数小时 |
| Apple Silicon | "mps" | 8~16 | False | 4 | 部分算子 MPS 不支持会回退 CPU |
GPU 上amp=True能省显存、提速,但如果你用的是很新的卡或很旧的驱动,混合精度偶尔会出 NaN,这时先关掉 amp 验证是不是精度问题。CPU 上开 amp 没意义,反而可能因为算子不支持报错。Mac 的 MPS 后端对某些算子支持不全,遇到NotImplementedError就临时切回 CPU 跑通流程。
4.3 小数据集的关键超参调整
1000 张图属于小数据集,默认超参直接套容易过拟合。我一般会动这几个:
model.train( data="./ocean_dataset/data.yaml", epochs=150, # 小数据集多训几轮,配合早停 imgsz=640, # 海洋垃圾目标偏小,可试 960 提升小目标召回 batch=16, lr0=0.001, # 初始学习率,小数据集比默认 0.01 更稳 lrf=0.01, # 最终学习率系数 warmup_epochs=5, # 预热轮数,防止初期梯度爆炸 mosaic=1.0, # 马赛克增强,小数据集靠它扩样本 mixup=0.1, # 混合增强,别开太大否则小目标被淹没 degrees=10.0, # 旋转增强,海面目标方向随机,适度旋转有用 fliplr=0.5, # 水平翻转 )imgsz从 640 提到 960 对小目标检测帮助明显,代价是显存和耗时上升,GPU 显存不够就退回 640。mosaic和mixup是小数据集扩样本的主力,但mixup开太大(比如 0.5)会让小目标在叠加图里变得模糊,反而伤召回。
提示:改超参一次只改一个,跑完对比 mAP 再决定留不留。一次改五个参数,涨了跌了都不知道是谁的功劳。
4.4 训练启动与日志观察
脚本跑起来后,重点盯三个指标:box_loss是否稳定下降、mAP50是否在涨、cls_loss有没有异常波动。前 10 轮 loss 不降,先查学习率是不是太大;mAP 一直卡在很低的值,回去查标签格式和类别映射;loss 出现 NaN,查退化框和 amp 设置。训练完的权重默认存在runs/ocean_yolo11/weights/best.pt,拿它做推理验证。
5. 训练不收敛、显存爆、mAP 上不去:排查清单
训练跑不起来或者效果差,八成不是模型的问题,而是数据或配置的锅。这一章按"现象 → 原因 → 解决"整理五条最常见的踩坑记录,都是我在实际项目里真金白银换来的。
现象一:训练一开始 loss 就是 NaN。原因通常是标签里有退化框(宽高为 0)导致归一化除零,或者学习率设得过大。解决方式是先跑第 3 章的自检脚本确认没有零宽高框,再把lr0从默认值降到 0.001 试一轮。
现象二:显存爆掉,报 CUDA out of memory。原因是 batch 太大或 imgsz 太高。解决方式是先把batch减半,还爆就把imgsz从 960 降到 640,再不行开amp=True。GPU 显存是硬约束,别硬扛。
现象三:mAP50 一直卡在 0.1 以下。这种"学了但没完全学"的情况,九成是类别索引和 names 对不上,或者标签根本没被正确读取。解决方式是随便挑一张训练图,用训练好的模型推理,看输出的类别名是不是你期望的;再打开对应的 txt 确认类别索引范围。
现象四:CPU 上训练慢到无法接受。原因是 CPU 训练本身就是慢,1000 张图 100 轮可能要跑一整天。解决方式是先用epochs=10跑通流程验证代码没问题,再决定要不要上 GPU;如果只有 CPU,把imgsz降到 416、batch降到 4,牺牲精度换速度。
现象五:Mac 上 MPS 报算子不支持。现象是训练中途抛NotImplementedError。原因是 MPS 后端对某些算子还没实现。解决方式是临时把device="cpu"跑通,或者升级 PyTorch 到较新版本,很多算子支持是逐步补上的。
注意:排查顺序永远是"先数据、后配置、最后模型"。数据错了,换多大的模型都白搭。
6. 用验证集反查标注质量:一个被低估的进阶技巧
训练跑通只是开始,真正决定模型上限的是标注质量。1000 张图的数据集,人工标注难免有漏标、错标、框不准的情况,而这些错误在 loss 曲线上往往看不出来。我习惯在训练后做一件事:用best.pt在验证集上推理,把预测框和真实标签叠在同一张图上对比,专门找"模型预测对了但标签没有"和"标签有但模型死活不预测"的图。
from ultralytics import YOLO import cv2 model = YOLO("runs/ocean_yolo11/weights/best.pt") results = model.predict("./ocean_dataset/images/val", conf=0.25, save=True) # 重点看两类图: # 1. 模型预测出目标但原标签没有 -> 可能漏标 # 2. 原标签有目标但模型置信度极低 -> 可能错标或框不准 for r in results: if len(r.boxes) == 0: print(f"无预测: {r.path}") # 这些图优先人工复查conf=0.25是推理置信度门限,调低(如 0.1)能捞出更多疑似漏标,调高(如 0.5)只看高置信预测。这个技巧的本质是把模型当成一个"标注质检员"——模型在大量数据上学到的共性,能反过来暴露单张图上的标注异常。
我一般会挑出 20~30 张可疑图人工复查,改完标签再训一轮,mAP 往往能涨几个点。这个收益比调超参来得实在,因为超参调的是"怎么学",标注质量决定的是"学什么"。血泪经验是:别一上来就换更大的模型,先把这 1000 张图的标签抠干净,小模型也能跑出能用的效果。数据质量这件事没有后悔药,越早查越省事。
希望帮到你。
本文还有配套的精品资源,点击获取