简介:本资源为面向目标检测学习者的YOLO海洋目标检测数据集,适用于需要真实场景海洋目标样本的算法训练与课程实践,可解决自建数据集标注成本高、格式转换繁琐的问题。压缩包共2000个文件,约23.38MB,包含1000张真实场景图片及对应标注,其中xml文件1000个、txt文件990个,另有yaml配置、py脚本与html说明文档,分别提供voc、coco和yolo三种格式标签,可直接接入YOLO系列模型训练。资源同时附赠数据集划分脚本与训练教程,支持按需生成训练集、验证集、测试集,并覆盖Windows与Linux环境搭建及训练案例,便于快速复现与迁移到自有数据。目前已有413人学习,适合具备一定深度学习基础、希望快速开展海洋目标检测实验的学生与开发者参考使用。
1. 拆开这个海洋目标检测数据集:1000 张图、三套标签、四个划分脚本
做海洋场景目标检测的同行大概都有过这种体验:模型在 COCO 上跑得挺欢,一换到海面、码头、养殖区就集体翻车,水面反光、目标尺度跨度大、背景纹理单一,全是玄学。这个资源包就是冲着这类场景来的——1000 张真实海洋场景图片,用 labelImg 标注,同时给了 VOC(xml)、COCO(json)、YOLO(txt)三种格式标签,分文件夹存放,拿到手不用自己转格式就能直接喂给 YOLO 系列。包里还塞了环境搭建、训练教程和四个划分脚本,覆盖 Windows 和 Linux 两套流程。适合谁?手上有海洋、海事、水面目标检测需求,又不想从零标数据、写转换脚本的从业者;新手可以照着教程走通全流程,熟手可以直接拿标签和划分脚本接进自己的训练管线。
2. 三种标签格式怎么选:VOC、COCO、YOLO 的坐标逻辑与转换关系
拿到一个数据集,第一件事不是急着训练,而是搞清楚三种标签格式各自在算什么。很多人训练报错、框画歪、mAP 上不去,根子就在坐标没对齐。这一章把三种格式的坐标定义、文件组织、适用场景讲透,再给出验证标签是否正确的可执行方法。
2.1 三种格式的坐标定义与文件组织
VOC 格式是每张图对应一个 xml 文件,核心节点是<bndbox>,里面存xmin、ymin、xmax、ymax,这四个值是绝对像素坐标,原点在图片左上角。xml 里还有<size>节点记录宽高,方便校验。它的好处是可读性强,labelImg 默认就存这个格式,缺点是文件多、解析慢。
COCO 格式把所有标注塞进一个 json 文件,images数组存图片信息,annotations数组存标注,categories存类别。标注里的bbox是[x, y, width, height],注意这里是左上角坐标加宽高,不是右下角坐标,而且坐标是绝对像素值。COCO 还带area、iscrowd字段,做评估时用得上。
YOLO 格式是每张图一个 txt,每行一个目标,格式是class_id x_center y_center width height,后四个值全部是归一化到 0~1 的相对值,相对于图片宽高。class_id 从 0 开始,对应classes.txt或data.yaml里的类别顺序。这是 YOLO 训练直接吃的格式,也是三种里最容易写错的一种——忘了归一化、把中心点写成左上角,都是高频翻车点。
| 格式 | 文件形态 | 坐标类型 | 关键字段 | 典型用途 |
|---|---|---|---|---|
| VOC | 每图一个 xml | 绝对像素,左上右下 | xmin/ymin/xmax/ymax | labelImg 标注、传统检测框架 |
| COCO | 单个 json | 绝对像素,左上+宽高 | bbox=[x,y,w,h] | 评估、多任务、COCO 预训练对齐 |
| YOLO | 每图一个 txt | 归一化,中心+宽高 | cx/cy/w/h ∈ [0,1] | YOLO 系列直接训练 |
2.2 用脚本验证标签是否对得上图片
在动手训练前,我一般会先跑一段校验脚本,确认三件事:图片和标签文件名能一一对应、坐标没越界、归一化值都在 0~1 之间。下面这段针对 YOLO 格式,VOC 和 COCO 同理改解析逻辑即可。
import os import cv2 img_dir = "images" lbl_dir = "labels" bad = [] for name in os.listdir(img_dir): if not name.lower().endswith((".jpg", ".png", ".jpeg")): continue stem = os.path.splitext(name)[0] lbl_path = os.path.join(lbl_dir, stem + ".txt") if not os.path.exists(lbl_path): bad.append((name, "缺标签")) continue img = cv2.imread(os.path.join(img_dir, name)) h, w = img.shape[:2] with open(lbl_path) as f: for i, line in enumerate(f): parts = line.strip().split() if len(parts) != 5: bad.append((name, f"第{i}行字段数={len(parts)}")) continue cls, cx, cy, bw, bh = parts vals = list(map(float, [cx, cy, bw, bh])) if any(v < 0 or v > 1 for v in vals): bad.append((name, f"第{i}行归一化越界 {vals}")) # 还原像素框,检查是否超出图片边界 x1 = (float(cx) - float(bw) / 2) * w y1 = (float(cy) - float(bh) / 2) * h x2 = (float(cx) + float(bw) / 2) * w y2 = (float(cy) + float(bh) / 2) * h if x1 < -1 or y1 < -1 or x2 > w + 1 or y2 > h + 1: bad.append((name, f"第{i}行框越界 ({x1:.1f},{y1:.1f},{x2:.1f},{y2:.1f})")) print(f"共检查 {len(os.listdir(img_dir))} 张图,异常 {len(bad)} 条") for b in bad[:20]: print(b)逻辑说明:先按文件名 stem 匹配图片和标签,缺标签直接记异常;再逐行解析 YOLO 标签,字段数不对、归一化值越界、还原成像素框后超出图片边界,都算问题。参数上img_dir和lbl_dir按你解压后的实际目录改,bad列表只打印前 20 条避免刷屏。跑完如果异常为 0,说明标签基本干净,可以进入划分环节;如果有越界框,多半是标注时框拖出了边界,需要回 labelImg 修。
2.3 三种格式互转时最容易错的地方
VOC 转 YOLO 时,归一化要除以图片宽高,而宽高要从 xml 的<size>里读,不能想当然用固定值。COCO 转 YOLO 时,bbox是左上角加宽高,要先算中心点cx = x + w/2,再归一化,顺序反了框就整体偏移。YOLO 转 VOC 时,要把归一化值乘回图片宽高,再算xmin = cx*w - bw*w/2,注意这里bw已经是归一化宽度,乘w才是像素宽。这三种转换我见过太多人栽在「中心点 vs 左上角」和「归一化 vs 绝对像素」这两组概念上,写转换脚本时建议每转完一批就抽几张可视化看一眼。
3. 划分脚本怎么用:train/val/test 与 ImageSets 两条路线
数据集划分看着简单,实际是个容易埋雷的环节。划分比例不对、随机种子没固定、图片和标签没同步搬移,都会导致训练时找不到标签或者验证集泄漏。这个包里给了四个脚本,覆盖了两种主流划分思路,这一章把每个脚本的用法、参数和适用场景讲清楚。
3.1 三个划分脚本的分工
包里的划分脚本大致分三类:训练集、验证集、测试集划分脚本(图片标签划分写入新文件夹).py是把图片和对应标签一起复制到新的 train/val/test 文件夹,适合直接生成 YOLO 训练目录结构;训练集、验证集划分脚本(图片标签划分写入新文件夹).py是它的两分版本,只分 train 和 val;split_train_val生成ImageSets下txt文件划分脚本.py走的是 VOC 那套 ImageSets 路线,只在ImageSets/Main/下生成train.txt、val.txt、test.txt,里面存的是文件名列表,不搬移原文件。train_list.txt则是已经生成好的一个示例列表,可以直接参考它的格式。
选哪条路线取决于你的训练框架。YOLOv5/v8 官方推荐的是images/train、images/val这种目录结构,用前两个脚本;如果你用的是早期 darknet 或者需要 ImageSets 的框架,用第三个脚本。
3.2 跑通三分划分脚本并检查结果
以三分脚本为例,典型用法是改脚本顶部的路径和比例参数,然后直接运行。下面是我一般会改的几个位置:
# 划分脚本关键参数(按实际解压路径修改) image_dir = "./images" # 原图目录 label_dir = "./labels" # YOLO 标签目录 output_dir = "./dataset_split" # 划分后输出根目录 train_ratio = 0.7 # 训练集比例 val_ratio = 0.2 # 验证集比例 test_ratio = 0.1 # 测试集比例 seed = 42 # 随机种子,固定后可复现逻辑说明:脚本会先列出所有图片,按seed打乱,再按比例切分,然后把图片和同名标签一起复制到output_dir/train/images、output_dir/train/labels这样的子目录。参数上三个比例加起来必须等于 1,否则脚本可能报错或漏掉部分文件;seed固定是为了让每次划分结果一致,方便复现实验。跑完后建议用一条命令核对数量:
# 统计各子集图片数,确认比例符合预期 for d in train val test; do echo -n "$d images: "; ls dataset_split/$d/images | wc -l echo -n "$d labels: "; ls dataset_split/$d/labels | wc -l done图片数和标签数必须相等,如果不等,说明有图片没有对应标签,或者标签文件名和图片名不一致(比如图片是.jpg标签是.JPG.txt),这类问题在 Windows 上尤其常见,因为文件名大小写不敏感但脚本里字符串比较是敏感的。
3.3 ImageSets 路线与 train_list.txt 的格式
如果你走 ImageSets 路线,脚本会在ImageSets/Main/下生成三个 txt,每行一个文件名(不带扩展名)。train_list.txt是包里的示例,格式通常是每行一个图片路径或文件名,具体取决于你的训练框架怎么读。用这条路线时要注意:ImageSets 只生成列表,不搬移文件,所以你的训练配置里要正确指向原始图片目录和标签目录,否则框架按列表找图会找不到。另外,ImageSets 路线下验证集和测试集的划分同样要固定 seed,不然每次跑出来的列表都不一样,实验没法对比。
提示:划分前先备份原始数据,脚本是复制不是移动,但万一路径写错覆盖了原目录就麻烦了。
4. 环境搭建与训练:Windows 和 Linux 两条线的关键差异
包里的教程分了 Windows 和 Linux 两个版本,还单独给了 Ubuntu 环境安装教程。这一章不逐字复述教程,而是把两条线里最容易卡住的地方拎出来——CUDA 版本匹配、依赖安装顺序、路径写法差异,这些才是决定你能不能一次跑通的关键。
4.1 Windows 线:CUDA、cuDNN 与 PyTorch 版本对齐
Windows 上搭 YOLO 环境,翻车最多的就是 CUDA 和 PyTorch 版本对不上。常见做法是先确认显卡驱动支持的 CUDA 最高版本(nvidia-smi右上角那个),然后去 PyTorch 官网找对应 CUDA 版本的安装命令。比如驱动支持 CUDA 11.8,就装cu118版本的 PyTorch,不要装成cu121,否则torch.cuda.is_available()会返回 False。
# 先看驱动支持的 CUDA 版本 nvidia-smi # 按官网给的命令装对应版本,例如 CUDA 11.8 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 验证 python -c "import torch; print(torch.cuda.is_available(), torch.version.cuda)"逻辑说明:nvidia-smi输出的 CUDA Version 是驱动能支持的最高版本,不是已安装版本;PyTorch 安装命令里的cu118要和它匹配或更低。验证时两个值都要看,is_available()为 True 且torch.version.cuda和你装的一致,才算成功。如果为 False,先别急着重装,检查是不是装了 CPU 版 PyTorch,或者环境变量里有多余的 CUDA 路径。
4.2 Linux 线:Ubuntu 依赖与虚拟环境隔离
Linux 线的教程从 Ubuntu 环境安装讲起,核心是依赖装全、环境隔离。我一般用 conda 建独立环境,避免和系统 Python 打架。关键依赖除了 PyTorch,还有opencv-python、pyyaml、tqdm、matplotlib,YOLOv5/v8 还需要requirements.txt里那一串。
# 建独立环境,指定 Python 版本 conda create -n yolo_sea python=3.9 -y conda activate yolo_sea # 装 PyTorch(按 CUDA 版本选) pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 装 YOLO 依赖 pip install -r requirements.txt # 验证 GPU 可用 python -c "import torch; print(torch.cuda.is_available())"逻辑说明:Python 版本建议 3.8~3.10,太新可能有些依赖没轮子。requirements.txt在 YOLO 仓库根目录,装之前先cd进去。如果pip install卡在编译某个包,多半是缺系统库,Ubuntu 上常见的是libgl1、libglib2.0-0,用apt install补上。
4.3 用案例配置改自己的数据集
教程里给的是案例配置,改成自己的海洋数据集要动几个地方:data.yaml里的train、val、test路径指向划分后的目录,nc改成类别数,names改成类别名列表。nc和names长度必须一致,否则训练启动就报错。类别名要和标签里的 class_id 顺序对应,0 对应 names 第一个,1 对应第二个,错一个整个类别就乱了。
# data.yaml 示例 train: ./dataset_split/train/images val: ./dataset_split/val/images test: ./dataset_split/test/images nc: 3 names: ['ship', 'buoy', 'person']逻辑说明:路径可以是相对路径也可以是绝对路径,相对路径是相对于训练脚本运行目录,容易搞错,建议先用绝对路径跑通再改相对。nc是类别数,names是类别名,两者必须对齐。改完可以先跑一个 epoch 看 loss 有没有正常下降,如果 loss 是 nan 或者不降,先回去查标签格式和类别对应。
5. 避坑与排查:训练前必须过的五道检查
这一章是我自己踩过的坑,也是带人时反复强调的检查项。每条按「现象 → 原因 → 解决」写,训练前过一遍能省下大量重跑时间。
5.1 现象:训练启动报「No labels found」
原因:YOLO 按图片路径推导标签路径,默认是把images替换成labels,如果你的目录结构不是这个约定,或者标签扩展名不是.txt,就找不到。解决:确认划分后的目录是images/train和labels/train这种平行结构,标签必须是.txt,文件名和图片 stem 完全一致。用 3.2 里的统计命令核对图片数和标签数是否相等。
5.2 现象:loss 一直不降或变成 nan
原因:常见是标签归一化值越界、类别 id 超出nc范围、学习率太大。解决:先跑 2.2 的校验脚本确认标签干净,再检查data.yaml的nc是否大于等于标签里出现的最大 class_id,最后把学习率调小一个量级试跑几个 epoch。海洋场景里如果目标特别小,还要检查输入分辨率是不是太小导致小目标特征丢失。
5.3 现象:验证集 mAP 高得离谱或低得离谱
原因:高得离谱多半是验证集和训练集有重叠(划分时没去重或 seed 没固定导致重复),低得离谱可能是验证集标签格式和训练集不一致。解决:划分时固定 seed,划分后比对 train 和 val 的文件名列表,确认没有交集;再抽查 val 的标签格式和 train 是否一致。海洋数据里同一段视频抽帧的图片很像,如果按帧划分,相邻帧会同时进 train 和 val,造成泄漏,建议按场景或时间段划分。
5.4 现象:Windows 上路径报错或中文乱码
原因:Windows 路径用反斜杠,Python 字符串里\是转义符;中文路径在某些库下编码不一致。解决:路径统一用正斜杠/或原始字符串r"...",数据集目录尽量用英文,避免中文和空格。如果必须用中文路径,确认脚本文件本身是 UTF-8 编码。
5.5 现象:训练中途显存爆了
原因:batch size 太大、输入分辨率太高、模型太大。解决:先降 batch size 到 4 或 2 试跑,能跑通再往上加;输入分辨率从 640 起步,小目标多可以试 1280 但显存翻倍;模型从 yolov8n/s 这种小模型起步,跑通再换大的。海洋场景如果图片本身分辨率很高,预处理阶段先缩放到统一尺寸,别直接喂原图。
6. 进阶技巧:用混淆矩阵和可视化反查标注质量
训练跑通只是开始,真正决定模型上限的是标注质量。这一章讲两个我常用的反查手段:混淆矩阵定位类别混淆,可视化预测框定位漏标和错标。这两个手段配合用,能把数据集里的问题挖出来。
6.1 混淆矩阵怎么读才不误导
YOLO 训练完会在runs/下生成混淆矩阵图,横轴是预测类别,纵轴是真实类别,对角线是分对的,非对角线是分错的。但要注意,混淆矩阵的「总和」在不同版本里统计口径可能不一样,有的按检测框数,有的按图片数,所以别只看绝对值,看比例和分布。如果ship大量被预测成buoy,说明这两类在特征上太像,要么合并类别,要么补充区分性样本。如果某一类整行都是 0,说明这一类在验证集里根本没有样本,划分时漏了。
# 从验证结果里统计每类样本数,确认没有空类 import yaml from collections import Counter with open("data.yaml") as f: cfg = yaml.safe_load(f) names = cfg["names"] cnt = Counter() with open("dataset_split/val/labels/" + "".join([]) ) if False else None: pass import os for fn in os.listdir("dataset_split/val/labels"): with open(os.path.join("dataset_split/val/labels", fn)) as f: for line in f: cnt[int(line.split()[0])] += 1 for i, n in enumerate(names): print(f"{n}: {cnt.get(i, 0)} 个框")逻辑说明:遍历验证集所有标签文件,统计每个 class_id 出现的次数,再映射回类别名。如果某类为 0,说明验证集里没有这个类,混淆矩阵那一行自然全空,评估结果不可信。参数上路径按实际划分目录改,names从data.yaml读,保证和训练时一致。
6.2 可视化预测框反查漏标
混淆矩阵看的是整体,定位具体问题要靠可视化。我一般会挑验证集里 loss 最高的几张图,把真实框和预测框画在一起对比。真实框有、预测框没有,可能是漏标;预测框有、真实框没有,可能是标错了或者模型误检。海洋场景里水面反光、浪花容易被误检成目标,这类图要重点看。
import cv2 def draw_boxes(img_path, label_path, color=(0, 255, 0)): img = cv2.imread(img_path) h, w = img.shape[:2] with open(label_path) as f: for line in f: cls, cx, cy, bw, bh = map(float, line.split()) x1 = int((cx - bw / 2) * w) y1 = int((cy - bh / 2) * h) x2 = int((cx + bw / 2) * w) y2 = int((cy + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) return img # 用法:挑几张图,画真实框后保存,和预测结果对比 out = draw_boxes("dataset_split/val/images/xxx.jpg", "dataset_split/val/labels/xxx.txt") cv2.imwrite("check_xxx.jpg", out)逻辑说明:把 YOLO 归一化标签还原成像素框画在图上,绿色是真实框。再拿模型预测结果画一遍(用不同颜色),两张图叠着看,就能定位漏标和误检。参数上color区分真实和预测,线宽 2 够看清。这个手段不依赖任何评估指标,纯靠眼睛,但对付标注问题最直接。
6.3 我自己的习惯
从那以后我每次拿到新数据集,都强制走一遍「校验脚本 → 划分 → 统计各类框数 → 可视化抽查」这四步,一步不省。海洋数据尤其如此,水面场景的标注一致性比陆地差很多,同一艘船不同标注员画的框可能差出一大截,不抽查根本发现不了。这套流程跑下来,训练翻车的概率能降一大半。希望帮到你。
本文还有配套的精品资源,点击获取