YOLO海洋目标检测数据集:三种标注格式与训练避坑指南
简介YOLO海洋目标检测数据集面向计算机视觉学习者与从业者聚焦海洋生态监测、渔业资源调查等真实场景涵盖5000张高质量图片及对应标注解决海洋领域公开数据集稀缺、格式转换繁琐的痛点。压缩包共2000个文件核心为1986个xml标签文件并配套yolo格式txt、coco格式json标签另有6个html教程、3个python划分脚本和5个txt列表文件整体体积75.39MB。目前已有264人关注学习适用于需要海洋数据快速开展YOLO系列模型训练的实验与课程项目。配套教程覆盖Windows和Linux双平台的环境搭建指导如何根据案例修改配置以训练自定义数据集三个划分脚本可自由切分训练集、验证集与测试集免去手动标注与格式整理的重复劳动帮助读者从数据准备到模型部署形成完整闭环。1. 为什么这份 YOLO 海洋目标检测数据集值得你花时间三种格式一次配齐做目标检测的人都有同一个体会模型架构迭代再快真正卡住进度的永远是数据。YOLO 海洋目标检测数据集这份资源就是冲着这个痛点来的——5000 张真实海洋环境图片已经用 labelimg 逐张标注完并且同时给了 VOC(xml)、COCO(json)、YOLO(txt) 三种格式的标签分别存在不同文件夹下拿到手不需要再折腾格式转换直接能喂给 YOLO 系列训练。对刚入门目标检测、想把流程整个跑通的学生或者要做海洋、近海场景检测的从业者来说它省掉的是最枯燥也最容易出错的那几步。更难得的是还打包了环境搭建教程、训练案例教程和划分脚本从零到出权重一条龙我第一次拿到时最大的感受是这资料包把从数据到模型的每个坑都提前替你踩了一遍。2. 数据集内部结构5000 张图与 voc/coco/yolo 标签的真实分布2.1 图片质量与标注方式为什么标得干净比数量多更重要先看图片本身。这 5000 张是真实场景采集的海洋环境图像不是合成图场景覆盖了不同光照、不同海况、不同远近尺度下的目标。用 labelimg 标注标注框质量高——这一点在实际训练里的权重比很多人想象的大。我见过不少公开数据集数量是够的但框画得潦草目标边缘留白过多或者把小目标漏标了一半训练出来的模型 mAP 就是上不去怎么调参数都没用最后查数据才发现是标注问题。这份资源的标注框属于比较规矩的那种框紧贴目标轮廓类别标签也统一。真实场景图 框质量高这两个条件对海洋目标这种背景复杂、目标尺度差异大的检测任务来说基本决定了模型收敛的上限。另外图片和标签是严格同名的这一点虽然基础但在后续划分脚本阶段会省很多事。2.2 三种标注格式对不上的问题一份数据为何要出三份标签很多初学者不理解为什么要同时给 VOC、COCO、YOLO 三种格式直接用 YOLO 的 txt 不就行了原因是不同训练框架的输入格式要求不一样。YOLO 系列原生吃的是 txt 格式每个框一行内容是类别 中心点x 中心点y 宽度 高度四个坐标值全部归一化到 0~1VOC 系模型比如 Faster R-CNN 的某些实现习惯读 xml里面记录的是左上角和右下角的绝对像素坐标而 COCO 格式的 json 是很多检测框架的通用输入MMDetection、Detectron2 都用它。你手里只有一种格式就意味着被某一个框架绑死换个框架就得重新写转换脚本。同一张图在三种格式里的样子完全不同我举个实际例子。假设图片宽度是 1920、高度是 1080图中有一个目标标注框左上角在 (500, 300)右下角在 (800, 700)!-- VOC 格式xml 文件内容片段 -- annotation size width1920/width height1080/height depth3/depth /size object nameship/name bndbox xmin500/xmin ymin300/ymin xmax800/xmax ymax700/ymax /bndbox /object /annotation// COCO 格式json 中的 annotation 片段 { image_id: 1001, category_id: 1, bbox: [500, 300, 300, 400], area: 120000 }YOLO 格式txt 文件内容注意坐标是归一化后的相对值 0 0.3385 0.4630 0.1563 0.3704VOC 和 COCO 这里都是像素绝对值但 COCO 的 bbox 存的不是右下角坐标而是x, y, width, height四元组。YOLO 的 txt 则是把中心点坐标和宽高都除以图片尺寸做了归一化。这份资源把三种格式的标签按文件夹分好你训练时只需要在配置里指定对应路径不用写任何转换代码这是它最实在的地方。2.3 目录组织方式拿到压缩包后先核对这个结构解压后建议先按下面的结构核对一遍确认图片和标签对应关系没乱dataset/ ├── images/ # 所有图片jpg/png 混存 ├── annotations/ # VOC 格式 xml 标签 ├── coco/ # COCO 格式 json 标签 ├── labels/ # YOLO 格式 txt 标签 ├── 训练集、验证集、测试集划分脚本图片标签划分写入新文件夹.py ├── 训练集、验证集划分脚本图片标签划分写入新文件夹.py └── split_train_val生成ImageSets下txt文件划分脚本.py提示三个脚本在压缩包根目录使用时最好先复制到 dataset 同级的目录再跑避免脚本里的相对路径把数据目录搞乱。3. 划分脚本实操train/val/test 按你的比例重切3.1 三个脚本的分工先想清楚你要哪种划分方式资源里给了三个划分脚本很多新手一上来就懵不知道跑哪个。我先帮你捋清楚第一个是三分脚本把全部数据按你设定的比例切成训练集、验证集、测试集三份每份都包含图片和对应的标签且会复制到新文件夹里第二个是二分脚本只切训练集和验证集适合你暂时不需要独立测试集、打算用验证集代替测试集快速迭代的场景第三个不太一样它不复制文件而是生成 ImageSets 下的 txt 索引文件也就是 train_list.txt 这类文件这是 VOC 风格训练里用来告诉框架哪些图片属于哪个集合的列表文件。实际使用中我的建议是先跑三分脚本把测试集真正独立出来。原因后面避坑章细说这里先说你最关心的——脚本怎么改参数。3.2 三分脚本怎么改比例、路径、文件名前缀一个都不能错训练集、验证集、测试集划分脚本图片标签划分写入新文件夹.py这类脚本的核心逻辑是遍历所有图片文件名按比例随机分配然后用 shutil 把图片和同名标签复制到目标子目录。下面是我按常见做法整理的一个等价实现方便你对照理解原脚本里每个变量是干嘛的import os import random import shutil # 原图与标签目录改成你自己的实际路径 images_dir dataset/images labels_dir dataset/labels # 如果标签在 annotations改成对应路径 output_dir dataset/split # 输出根目录 # 划分比例和必须等于 1 train_ratio 0.8 val_ratio 0.1 test_ratio 0.1 # 收集所有图片文件名不带后缀 all_images [f for f in os.listdir(images_dir) if f.endswith((.jpg, .png, .jpeg))] random.shuffle(all_images) # 打乱顺序避免同类场景连续出现 # 按比例切分 train_cnt int(len(all_images) * train_ratio) val_cnt int(len(all_images) * val_ratio) splits { train: all_images[:train_cnt], val: all_images[train_cnt:train_cnt val_cnt], test: all_images[train_cnt val_cnt:], } # 逐个复制图片与同名标签 for split_name, file_list in splits.items(): img_out os.path.join(output_dir, images, split_name) lbl_out os.path.join(output_dir, labels, split_name) os.makedirs(img_out, exist_okTrue) os.makedirs(lbl_out, exist_okTrue) for img_name in file_list: stem os.path.splitext(img_name)[0] src_img os.path.join(images_dir, img_name) src_lbl os.path.join(labels_dir, stem .txt) if os.path.exists(src_lbl): # 标签不存在直接跳过防止训练时报错 shutil.copy(src_img, os.path.join(img_out, img_name)) shutil.copy(src_lbl, os.path.join(lbl_out, stem .txt)) print(ftrain: {len(splits[train])} val: {len(splits[val])} test: {len(splits[test])})逻辑说明脚本先读取所有图片文件名并打乱随机打乱这一步很关键——海洋数据里相邻图片往往是同一个时间段、同一片海域采的不打乱的话训练集和验证集会存在场景重叠导致验证集指标虚高。然后按比例切三个集合最后用 shutil.copy 把图片和同名标签复制进新目录名字一致才配得上对。参数说明train_ratio0.8, val_ratio0.1, test_ratio0.1是通用默认值数据量小、样本不均时我一般改成 0.7/0.15/0.15如果你的场景类别极少、每类只有几十张那应该先做数据增强而不是调整比例。注意脚本只复制不移动原目录不会被破坏这点设计得比较稳妥跑错了也有后悔药。3.3 生成 ImageSets txt 文件为什么要给框架一份名单split_train_val生成ImageSets下txt文件划分脚本.py要做的事是不复制文件只在 dataset 下生成 ImageSets/Main/train.txt、val.txt、test.txt文件里每行是一个图片路径通常是不带后缀的文件名配合 train_list.txt 使用。Darknet 框架训练时通过 train_list.txt 告诉程序去哪读图片、对应的标签自动按同名规则去找。我一般这样用先跑完三分脚本再跑这个脚本生成 train_list.txt一份数据两种消费方式都能满足。跑之前检查两件事一是脚本里的root_path是否指向你的图片目录二是确认生成的是相对路径还是绝对路径——换机器训练时绝对路径直接失效相对路径只要保持目录结构一致就没问题。4. 环境搭建选型Linux 和 Windows 各走一遍关键差异4.1 先定 GPU驱动、CUDA、cuDNN、PyTorch 的版本咬合关系资料包里有两份环境搭建教程分别对应 Linux 和 Windows还附带了一份 Ubuntu 安装教程。我的建议是如果你的机器能用 Linux优先走 Linux 路线Windows 也能跑但在驱动和编译环节更容易翻车。不管哪条路线第一关都是确定 GPU 驱动、CUDA、cuDNN、PyTorch 四者版本匹配。常见做法是先装好显卡驱动然后nvidia-smi看驱动支持的 CUDA 版本上限再往下选对应版本的 PyTorch# 查看显卡驱动和驱动支持的 CUDA 版本 nvidia-smi # 环境里确认 PyTorch 实际使用的 CUDA 版本 python -c import torch; print(torch.__version__); print(torch.cuda.is_available())逻辑说明nvidia-smi输出的右上角是驱动支持的最高 CUDA 版本PyTorch 编译时带的 CUDA 必须不高于这个值。很多人踩的坑是驱动版本太老装完最新版 PyTorch 一跑就报CUDA driver version is insufficient——这不是代码问题是版本错位。参数说明torch.cuda.is_available()必须返回 True 再继续这里有一步没验证就往后跑训练时才会暴露问题排查成本成倍增加。如果你用 conda推荐建独立环境别动 base。conda create -n yolo python3.9 -y conda activate yolo4.2 Linux 路线Ubuntu 下最稳的顺序Linux 这边Ubuntu 是社区支持最好的发行版资料包里的 Ubuntu 安装教程就是从系统装起。硬件驱动装完后环境搭建顺序一般是装 Anaconda → 建虚拟环境 → 用 pip 装 PyTorch → 拉取 YOLO 项目代码 → 安装依赖。这里有个小坑pip 源在国内建议直接换成镜像源否则下载大文件时网络波动容易中断# 临时用镜像源安装 PyTorch避免默认源过慢 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118逻辑说明repo 安装依赖时requirements.txt里的包版本是配合开发环境锁的按它装一般没问题如果遇到个别包编译报错常见做法是单独升级该包或先装系统级的libgl1、libglib2.0-0这类基础库。4.3 Windows 路线哪些坑只有 Windows 才有Windows 路线整体流程一致但有几个 Linux 上不会遇到的特有问题。第一路径不能含中文和空格别把数据集放在新建文件夹 (2)这种路径下YOLO 读取标签时有概率直接崩第二Windows 默认的shutil行为在某些情况下会锁文件跑划分脚本如果提示PermissionError关掉所有占用该目录的窗口再试第三nvidia-smi在 Windows 上同样可用驱动和 CUDA 的判断逻辑不变。另外一个容易被忽略的点Windows 上装 PyTorch 时CPU 版和 GPU 版的包名不同装错了torch.cuda.is_available()会返回 False很多人误以为显卡坏了其实只是装成了 cpu 版# Windows 下 GPU 版 PyTorch 安装示例 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118逻辑说明Python 的包管理机制决定了同一环境下只能装一个 torch 变体之前装过 CPU 版的话要先pip uninstall torch torchvision再换 GPU 版否则会提示冲突。5. 训练避坑标签、显存、过拟合三个方向的高频翻车现场5.1 标签越界和空文件训练直接中断的元凶现象训练刚起步几十个 iteration就报AssertionError: labels require no nan values或者 xml 解析错误程序退出。原因YOLO 格式要求坐标是归一化后的 0~1 数值但有少数目标靠近图片边缘时标注框可能出现 xmax 超出图片宽、ymax 超出高的像素值导致归一化后出现大于 1 的异常值。还有一种情况是图片里有目标没标生成了空标签文件或者 labelimg 标完漏存了同名 txt。解决训练前写一个清洗脚本把所有标签读一遍剔除异常框和空文件同时把越界框裁剪回 0~1 范围内import os labels_dir dataset/labels for f in os.listdir(labels_dir): if not f.endswith(.txt): continue path os.path.join(labels_dir, f) lines [] with open(path, r, encodingutf-8) as fp: for line in fp: parts line.strip().split() if len(parts) ! 5: continue # 跳过格式不对的行 cls, cx, cy, w, h int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) # 修正越界值 cx min(max(cx, 0.0), 1.0) cy min(max(cy, 0.0), 1.0) w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) lines.append(f{cls} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) if lines: with open(path, w, encodingutf-8) as fp: fp.write(\n.join(lines) \n) else: os.remove(path) # 空标签文件直接删掉训练时省得报错逻辑说明这段脚本做两件事一是把坐标值强制裁剪到合法区间二是把空标签文件删掉。训练之前的清洗环节我非常建议保留在流程里尤其换数据集时更要做一次数据集本身标签质量高但经过人为复制移动难保不会出现个别文件被改动的情况。5.2 类别编号对不上训练能跑但 AP 全为零现象训练正常跑完了验证集 mAP 显示 0损失也迟迟降不下来损失曲线发散。原因VOC 格式里的类别是字符串(ship、person)而 YOLO 的 txt 里存的是类别编号(0、1)。如果你手动改过标签文件或者用脚本转换时没有维护好类别到编号的映射表就会造成标签里的编号和训练配置文件里的class_names顺序不一致。比如数据里ship在 VOC 中是第一个对象编号应为 0但你的模型配置里 class 0 对应的是person模型学到的东西全是错的。解决训练前输出一份类别分布统计确认编号类别对应关系。写一行命令就能看# 统计 labels 目录下每个类别编号的出现次数 awk {print $1} dataset/labels/*.txt | sort | uniq -c逻辑说明输出的每一行是数量 编号对照训练配置里的类别名列表从 0 开始按顺序确认。我在实际项目中遇到过最隐蔽的翻车是labels目录里混进了一个从别的数据集复制来的标签文件类别编号跟当前项目完全不是一套体系训练时这个样本对损失的贡献异常大最后花了一整天才定位到。5.3 显存溢出OOM 之后不是调 batch size 就完事现象训练到一半报CUDA out of memory显存直接被打满然后程序退出。原因batch size 设置过大或者输入图片尺寸img-size设得太高。YOLO 训练时的显存消耗跟batch_size和img_size几乎成正比但很多新手只改 batch size无视了 img_size 的影响。解决两步降显存先降 batch size 到原来的一半再降 img_size一般就能跑起来# 用一半的 batch size 和较低分辨率先验证显存是否够 model.train( datadata.yaml, batch16, # 原来是 32 就从 16 开始 imgsz640, # 原来是 1280 就先降到 640 workers8 )逻辑说明workers是数据加载线程数Windows 上设太高容易报DataLoader worker相关错误建议 4~8 之间。另外即使显存能放下batch size 过小会导致 BN 层的统计量不准训练不稳定这时候优先考虑的是梯度累积而不是硬扛。5.4 划分后图片和标签数量对不上训练集里出现无标签图片现象训练时日志里反复出现WARNING: no labels found in ...或者最终训练完的类别数是 0。原因划分脚本运行时有些标签文件缺失或者没有和图片同名。比如你在 Windows 上跑脚本.jpg和.JPG后缀大小写不一致导致匹配失败又或者复制时标签文件被占用没拷过去。解决划分完成后立刻做一次数量比对# 分别统计两个目录下的文件数量应该严格一致 ls dataset/split/train/images/ | wc -l ls dataset/split/train/labels/ | wc -l逻辑说明两边数量相等是基本前提。数量不一致时优先检查是否有大小写混用的情况其次看看是否有图片文件本身后缀特殊比如.jpeg和.jpg并存文件名取 stem 的时候也会受影响。这个检查是我每次划分完数据集后固定跑的一步省下来的排错时间比脚本运行时间长得多。5.5 验证集和训练集场景重叠指标虚高一上线上就崩现象训练时验证集 mAP 曲线很漂亮85% 往上但把模型拿去对实测视频测试漏检率明显偏高跟在验证集上的表现完全不是一回事。原因原始数据是按时间序列采集的同一时间段、同一片海域的图片在打乱前是连在一起的。如果划分前没有随机打乱或者分成训练集和验证集时太随意验证集会包含跟训练集极相近的同类图片等于开卷考试指标自然好看。解决回到 3.2 节的脚本确认random.shuffle生效并且划分完之后人工抽查验证集图片和训练集图片是否有明显的时间连续性。极端情况下如果单类数据极少按场景分组再划分才是可靠的但这份数据集场景丰富度足够正常随机划分问题不大。提示如果你同时用了三分脚本和 ImageSets 脚本要保证两次划分的随机种子一致否则可能出现 train.txt 里的图片和实际训练目录里的图片对不上的情况。跑训练前把 train_list.txt 打开随便看几行核对第一张图确实存在于训练文件夹中。6. 一套我常跑的验证流训练完如何快速证明模型没白练训练完不是看到mAP数字就收工我有一套固定流程用训练好的模型对验证集批量推理把预测框和对应的真值框做 IoU 比对统计不同 IoU 阈值下的命中率同时把结果图片导出来人工扫一眼。这套流程能帮我快速判断模型是真正学到了目标特征还是仅仅靠数据集重叠刷高了指标。import cv2 import torch import numpy as np model torch.hub.load(ultralytics/yolov5, custom, pathruns/val/exp/weights/best.pt) # 挑一张验证集图片推理并输出 img_path dataset/images/test/00321.jpg results model(img_path, size640) boxes results.xyxy[0].cpu().numpy() # [x1, y1, x2, y2, conf, cls] # 读对应的 VOC 真值框做 IoU 比对 import xml.etree.ElementTree as ET xml_path dataset/annotations/00321.xml tree ET.parse(xml_path) gt_box None for obj in tree.findall(object): bnd obj.find(bndbox) gt_box [int(bnd.find(t).text) for t in [xmin, ymin, xmax, ymax]] def iou(box1, box2): x1 max(box1[0], box2[0]); y1 max(box1[1], box2[1]) x2 min(box1[2], box2[2]); y2 min(box1[3], box2[3]) inter max(0, x2 - x1) * max(0, y2 - y1) area1 (box1[2] - box1[0]) * (box1[3] - box1[1]) area2 (box2[2] - box2[0]) * (box2[3] - box2[1]) return inter / (area1 area2 - inter 1e-6) if (area1 area2 - inter) 0 else 0 if gt_box is not None and len(boxes) 0: for box in boxes: if box[4] 0.5: # 置信度阈值 score iou(box[:4].tolist(), gt_box) print(fconf{box[4]:.2f} IoU{score:.2f}) # 可视化推理结果肉眼确认框的位置 results.show()逻辑说明先用 YOLO 官方接口加载训练好的权重对单张图推理再解析同名 xml 拿到真值框计算 IoU。IoU 大于 0.5 视为命中这一步能快速量化模型的框精度。conf是置信度实际项目里要根据漏检和误检的代价调阈值不是越高越好。参数说明size640是推理时缩放的输入尺寸要和训练时设置保持一致否则小目标检测率会明显波动。conf0.5是通用起点如果业务上更在意召回比如漏检一个目标代价极高降低到 0.3 再看效果代价是误检增多这个权衡没有标准答案只能按业务目标定。从那以后我每次训练完都强制自己走一遍这套验证流程而不是只盯 mAP 数字至少能筛掉一半的假收敛。这份资料包里还有环境搭建和训练的完整教程照着顺序操作基本能通数据集的详情展示和更多说明可以去博客文章页看。希望帮到你。本文还有配套的精品资源点击获取