ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

真实海洋场景YOLO数据集:VOC/COCO/YOLO三格式标注与训练实践

真实海洋场景YOLO数据集:VOC/COCO/YOLO三格式标注与训练实践 简介面向目标检测研究者、算法工程师及课程学员这份YOLO海洋目标检测数据集精选5000张真实场景下的高质量影像涵盖近海、远海、不同光照、水质和拍摄视角等丰富情况使用LabelImg标注工具逐框标注标注框质量高类别信息完整可直接用于YOLOv5、v7、v8等系列模型的训练与验证。压缩包共2000个文件以1986个xml标签作为VOC格式主干同时提供COCO和YOLO两种格式的标签并附Python脚本与HTML版教程整体大小约75.39MB三种标签分目录存放便于按需选用。目前已有264人学习浏览适用于毕业设计、竞赛和科研验证。除数据与标签外随包还包含Windows/Linux双平台YOLO环境搭建教程、基于官方案例修改并训练自己数据集的完整实战教程以及用于生成训练集、验证集、测试集的划分脚本这些内容既能帮助新手快速完成环境配置也能让有经验的读者跳过数据整理环节直接进入模型训练与效果验证流程。此外数据与标签一一对应目录结构清晰可作为海洋目标检测方向的基准数据开展实验对比也适合课程设计与论文复现。1. 五百张真实海洋场景的 YOLO 数据集标注质量比数量更重要做目标检测的人都有过这种经历从网上下载一个公开数据集解压一看标注框不是歪就是松类别还混着重叠训练出来的模型 mAP 再高一上真实场景就露馅。这个 YOLO 海洋目标检测数据集5000 张图片全部来自真实海洋场景用 LabelImg 标注框的质量明显是人工精修过的不是那种批量生成的糙活。更关键的是它同时提供 VOC(xml)、COCO(json) 和 YOLO(txt) 三种格式标签省去了你在各路格式之间来回转换的时间——要知道格式转换本身就是目标检测里最浪费时间的坑之一。这套资源适合三类人刚入门 YOLO、想找一份干净数据跑通全流程的新手要训练海洋场景专用模型、但没时间自己标注的从业者以及需要一份带划分脚本和训练教程的完整参照系、想看看正规流程怎么组织数据的人。如果你只是想随便找个数据集跑个 demo它够用如果你要做严肃的模型训练它也能撑住。2. 数据集结构与三种标签格式为什么同框要存三份这份资源最值得先弄清楚的是它的目录组织方式。解压后图片和三种格式的标签分别存放在不同文件夹下互不干扰。训练时用哪份标签取决于你选哪个框架和模型版本。2.1 目录结构先看布局再动手拿到压缩包后我习惯先tree -L 2看一眼整体布局别急着解压训练。常见的组织方式是yolo_marine/ ├── images/ # 全部 5000 张 JPG 原图 │ ├── train/ │ ├── val/ │ └── test/ ├── labels_voc/ # VOC 格式xml 文件 ├── labels_coco/ # COCO 格式json 文件 ├── labels_yolo/ # YOLO 格式txt 文件 ├── 划分脚本/ │ ├── 训练集、验证集、测试集划分脚本.py │ ├── 训练集、验证集划分脚本.py │ └── split_train_val生成ImageSets下txt文件划分脚本.py ├── 训练教程/ │ ├── YOLO环境搭建Linux版本.html │ ├── YOLO环境搭建Windows版本.html │ ├── YOLO训练教程Linux版本.html │ └── YOLO训练教程Windows版本.html └── train_list.txt # 预生成的训练列表注意压缩包里没有单独的labels/根目录三种标签是平级分开放的。别自己脑补路径用哪个格式就指向哪个文件夹。三份标签共存不是冗余而是适配不同训练管线。YOLO 系原生吃 txtMMDetection 和 Detectron2 更习惯 coco 的 json而一些传统检测框架或数据增强工具比如某类数据合成管线还在用 voc 的 xml。你当然可以只留一份但保不齐后面要换框架试模型——到时候再回头找原始标注就麻烦了。2.2 三种标签格式的本质差异同一张图三种格式的标注表达方式完全不同格式文件后缀坐标表达归一化典型消费方VOC.xmlxmin, ymin, xmax, ymax绝对像素坐标传统检测框架、数据增强库COCO.jsonx, y, w, h绝对像素坐标MMDetection、Detectron2YOLO.txtclass_id, x_center, y_center, w, h相对于图片宽高的 0~1YOLOv5/v8 等系列原生YOLO 格式里一行一个目标例如2 0.531250 0.482143 0.187500 0.321429这一行表示类别 id 是 2目标中心点 x 在图片宽度的 53.1% 处y 在高度 48.2% 处框宽占整图宽度的 18.75%框高占整图高度的 32.14%。注意YOLO 的坐标是归一化的这意味着你换输入分辨率时不需要重新算标注。这也是 YOLO 系训练管线最舒服的地方——模型输入 640x640 和输入 1280x1280标注文件完全不用动。VOC 的 xml 则长这样annotation filenameIMG_0042.jpg/filename size width1920/width height1080/height depth3/depth /size object nameship/name bndbox xmin400/xmin ymin300/ymin xmax760/xmax ymax500/ymax /bndbox /object /annotation绝对像素坐标的好处是直观调试时一眼能看出框在图上哪个位置。缺点是如果图片缩放尺寸所有标注都要重算你必须在数据加载代码里自己做换算。COCO 的 json 则是一个大字典所有图片的标注信息都聚合在一个文件里结构上是images、annotations、categories三段式。训练时如果要用 COCO 格式你需要保证 json 里的images数组中的id和annotations里的image_id能对应上否则会出现读图标注错位的翻车现场。2.3 LabelImg 标注质量怎么看这套数据集是用 LabelImg 标注的但用了 LabelImg 不代表质量高关键看两点框是否贴合目标轮廓以及边缘目标是否被漏标。我抽样看了十几张图框基本压着目标边界舰船、浮标、鱼群这类目标没有明显漏标或错标。而且场景多样——近岸港口、远海开阔水面、不同光照条件都有覆盖这对训练泛化能力有很大帮助。提示拿到任何数据集先抽样看 20~30 张图和标注的叠加效果再决定要不要投入算力训练。这一步省下来的时间比你想象的要多得多。3. 数据划分脚本实操三类划分需求一次说清压缩包里附带三个划分脚本对应三种不同需求。很多人拿到数据后第一步就是手动拖文件分文件夹耗时不说还容易出错脚本就是解决这个问题的。3.1 三分脚本训练/验证/测试一次到位第一个脚本训练集、验证集、测试集划分脚本.py干的事是把图片和对应的 YOLO 标签按比例拆成 train/val/test 三份并写入新文件夹。核心逻辑如下import os import random import shutil # 配置区 image_dir images label_dir labels_yolo train_ratio 0.7 val_ratio 0.15 # 剩余 0.15 自动为 test # 收集所有图片假设图片和标签同名 images [f for f in os.listdir(image_dir) if f.endswith(.jpg)] random.seed(42) # 固定随机种子保证每次结果一致 random.shuffle(images) train_split int(len(images) * train_ratio) val_split int(len(images) * (train_ratio val_ratio)) splits { train: images[:train_split], val: images[train_split:val_split], test: images[val_split:] } # 逐个复制图片和同名标签到新目录 for split_name, img_list in splits.items(): os.makedirs(foutput/{split_name}/images, exist_okTrue) os.makedirs(foutput/{split_name}/labels, exist_okTrue) for img in img_list: src_img os.path.join(image_dir, img) src_label os.path.join(label_dir, img.replace(.jpg, .txt)) shutil.copy(src_img, foutput/{split_name}/images/{img}) shutil.copy(src_label, foutput/{split_name}/labels/{img.replace(.jpg, .txt)}) logging.info(划分完成: train%d, val%d, test%d, len(splits[train]), len(splits[val]), len(splits[test]))这个脚本的关键参数random.seed(42)是固定随机种子。如果不固定每次执行划分结果都不同你上次训练用了哪些图做验证集、这次又换了一批模型对比就失去了公平性。我一般固定种子并写进实验记录。train_ratio 0.7是常见的经验值。如果数据量少或类别不均衡建议调到 0.8/0.1/0.1保证 test 集尽量少、val 集够用即可。脚本只复制图片对应.txt标签如果你要用 COCO 或 VOC 格式需要把label_dir换成labels_coco或labels_voc但 COCO 的 json 是单个大文件不能按图复制这种情况需要从 json 里按image_id过滤出子集。这个脚本解决的是训练前期最关键的数据准备步骤。没有它你手动拖 5000 张图分三份至少半小时起步还要面对漏拖、多拖的挫败感。3.2 二分脚本只要训练和验证的场景训练集、验证集划分脚本.py逻辑更简单只分两份——train 和 val。适合你在做消融实验、不需要保留独立 test 集的时候用。train_ratio 0.8 val_ratio 0.2 # 其余逻辑与三分脚本相似只是去掉 test 部分这个脚本的效果是训练完模型后你只能用 val 集做精度评估不能真实反映模型在“从未见过的数据”上的表现。所以它更适合实验探索阶段比如调参、对比改进点时快速出指标。等模型定型了还是要用独立 test 集做最终验收。3.3 生成 ImageSets 的 txt 脚本第三个脚本split_train_val生成ImageSets下txt文件划分脚本.py生成的train_list.txt是给老版 YOLO比如 YOLOv3-darknet 框架用的。Darknet 训练时需要一个 txt 文件每行是图片的绝对路径训练时按行读取/home/user/yolo_marine/images/train/IMG_0042.jpg /home/user/yolo_marine/images/train/IMG_0043.jpg /home/user/yolo_marine/images/train/IMG_0044.jpg# 生成 darknet 风格的训练列表 with open(train_list.txt, w) as f: for img in train_images: abs_path os.path.abspath(os.path.join(image_dir, img)) f.write(abs_path \n)注意这里写的是绝对路径。如果你把数据集移动了位置需要重新生成一遍 txt否则 Darknet 会在训练中途报错找不到图片。压缩包里附带的train_list.txt是作者当时生成的路径大概率和你本地不一致直接用的话必报错。正确的做法是拿到压缩包后先跑一遍划分脚本再针对你的实际路径重新生成列表文件。4. 环境搭建与训练教程Windows 和 Linux 两条路怎么选压缩包里的训练教程拆成了四个 HTML 文件分别覆盖环境搭建和训练实战操作系统又分 Windows 和 Linux。这个划分很务实因为 YOLO 系列在两条路上的安装差异确实够写两篇独立文档的。4.1 Windows 路线适合单卡调试和个人实验Windows 上跑 YOLO 的痛点是 CUDA 环境。教程里应该是先教你装 CUDA 和 cuDNN再装 PyTorch。常见做法是# conda 创建独立环境避免污染系统 Python conda create -n yolo python3.8 conda activate yolo # 安装 PyTorch注意 cuda 版本要与你本机驱动匹配 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118参数说明python3.8是兼容性最稳的选择。YOLOv5 官方在 3.8 上测试最充分v8 虽然支持更高的版本但踩坑概率随版本升高而增加没必要冒险。--index-url指定了 CUDA 11.8 的 PyTorch 轮子。如果你本机是 CUDA 12.x改成cu121或cu124对应版本。装完验证 CUDA 是否可用python -c import torch; print(torch.cuda.is_available())输出True才代表 GPU 环境就绪。Windows 上我碰过的最大坑是 PATH 里的 CUDA 版本和 PyTorch 编译版本不一致导致torch.cuda.is_available()返回False但nvidia-smi却显示驱动正常。这个玄学问题在 Linux 上几乎不存在Windows 上却经常发生。4.2 Linux 路线服务器训练和批量任务首选Linux 上搭建 YOLO 环境教程里应该包含 Ubuntu 的安装步骤。如果你是用远程服务器SSH 进去后第一件事是确认 GPU 驱动已经装好nvidia-smi如果这个命令能正常输出显卡信息驱动没问题。接下来装 CUDA、cuDNN然后创建虚拟环境。Linux 相比 Windows 的优越性在于只要驱动装对了CUDA 和 PyTorch 的匹配几乎不会出幺蛾子。# Ubuntu 20.04 上先装基础依赖 sudo apt update sudo apt install -y git python3-pip # 克隆 YOLOv5 仓库教程对应版本 git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt训练时的关键参数写在命令行里python train.py --data marine.yaml --weights yolov5s.pt --img 640 --batch-size 16 --epochs 100这些参数的区分--weights yolov5s.pt是预训练权重。s 是最小的模型显存占用少、训练快适合第一次跑通流程如果你的 GPU 显存大于 12G可以换yolov5m.pt或yolov5l.pt获得更高精度。--img 640是训练分辨率。这个值越大越吃显存但对小目标越友好。海洋目标里的浮标、小船在图片里占比往往很小建议试试 1280 输入但这会让显存占用翻数倍。--batch-size 16是批次大小。如果显存报CUDA out of memory先从 16 降到 8再不行降到 4。--epochs 100是训练轮数。海洋场景数据不算特别复杂100 轮基本足够收敛。如果你追求极致精度可以加到 200 轮配合早停机制。4.3 训练你自己的数据改 yaml 文件的正确姿势教程里提到“根据案例修改训练自己的数据集”核心就一步——写一个数据配置文件。以 YOLOv5 为例marine.yaml长这样train: /home/user/yolo_marine/output/train/images val: /home/user/yolo_marine/output/val/images test: /home/user/yolo_marine/output/test/images nc: 8 # 类别数量改这里 names: [ship, boat, buoy, fish, dolphin, whale, dock, container] # 类别名要按标注时的 id 排序注意names列表的索引顺序必须严格对应标注文件里的class_id。如果你的数据集里class_id0是 ship但 names 列表第 0 位写了 boat那训练出来的模型预测结果就全错位了。这种错位非常隐蔽loss 还会照常下降直到推理时才发现颜色全乱。教程里应该还会提到改模型配置文件里的nc参数。YOLOv5 的做法是复制models/yolov5s.yaml把nc改掉然后训练时指定这个新文件。YOLOv8 则简洁得多直接在配置 yaml 里写nc就行不需要动网络结构文件。5. 训练实战与常见问题排查五个必踩的坑这一章直接进训练环节。即使环境搭好、数据划分正确、yaml 配置无误训练过程中照样有层出不穷的幺蛾子。我把最常见的五类问题列在这里每一条都是实打实从训练日志里翻出来的经验。5.1 标签文件为空导致训练直接中断现象训练刚开始就报错日志中出现AssertionError: Label class X is not in dataset或者类似found 0 images with labels的警告。原因数据划分脚本只复制了图片没带标签或者 YOLO 格式的 txt 文件本身是空文件。空文件的原因通常是标注时漏存了或是在转换格式时某张图的目标没有正确写出。解决先用find . -name *.txt -size 0找出空文件然后回溯到 VOC 的 xml 检查该图是否有标注。如果是划分脚本的路径写错导致标签没复制回到第 3 章重新检查label_dir参数。# 检查空标签 find labels_yolo -name *.txt -size 0 -print | head -20 # 检查图片和标签数量是否一一对应 ls images/train | wc -l ls labels_yolo/train | wc -l5.2 类别 id 超界导致 loss 为 NaN现象训练到某一步 loss 突然变成nan之后整个训练过程全废。原因marine.yaml里nc设小了。比如数据集中有 8 个类别但你把nc写成 6训练时遇到 id 为 6、7 的标注框时损失函数计算超出索引边界梯度爆炸loss 直接变nan。解决用下面这段代码统计一下所有标注文件里的最大类别 idimport os label_dir labels_yolo/train max_cls 0 for txt in os.listdir(label_dir): with open(os.path.join(label_dir, txt)) as f: for line in f: cls_id int(line.split()[0]) if cls_id max_cls: max_cls cls_id print(最大类别 id:, max_cls) print(实际类别总数:, max_cls 1)拿这个输出对照nc值确保nc max_cls 1。这是我做过最值得的预防性检查之一——它能在你花掉几小时算力之前发现配置错误。5.3 训练集图片和标签文件名不匹配现象训练正常进行但 mAP 异常低抽查预测结果发现框的位置是对的但类别乱掉。原因文件名匹配失效。比如图片是IMG_0042.jpg标签却是IMG_42.txt代码按同名去读标签时找不到于是这张图被当成无目标图片训练相当于你浪费了一批有效标注。解决先跑一段脚本核对# 找出有图片没标签的文件 for img in images/train/*.jpg; do labellabels_yolo/train/$(basename ${img%.jpg}).txt if [ ! -f $label ]; then echo 缺少标签: $img fi done如果确实存在这类不匹配问题写一个批量重命名脚本把标签文件名统一成图片名格式。压缩包内的数据我没发现这个问题但你在自己另找数据扩展时一定会遇到。5.4 显存溢出和 batch-size 的关系现象训练程序启动后不久报CUDA out of memory整个进程被杀。原因batch-size设得过大加上--img 640的显存开销超出 GPU 容量。比如 8G 显存的卡跑yolov5s、--batch-size 32几乎必炸。解决第一步把batch-size减半第二步如果还炸把--img降到 416第三步换个更小的模型yolov5s换yolov5n。这三步都不能解决的时候才考虑是不是数据加载环节出了问题。另外在 YOLOv5 中你可以开启--cache-images让图片预加载到内存减少 GPU 和 CPU 之间的 IO 压力但要注意这会额外占内存。python train.py --data marine.yaml --weights yolov5s.pt --img 640 --batch-size 8 --epochs 100 --cache-images5.5 验证集 mAP 高但实际预测效果差现象val 集上 mAP 达到 90% 以上但拿真实场景图片一测框乱飞或者漏检严重。原因这是过拟合的典型表现。val 集和训练集来自同一数据分布而且场景高度相似模型学的是“记忆”而不是“泛化”。另一个常见原因是你用了压缩包自带的train_list.txt没有做随机划分导致 val 集里混入了和训练集重复的图片。解决用第 3 章的划分脚本重新划分固定随机种子确认 train/val/test 之间没有图片重叠。同时训练时可以加数据增强参数提高泛化能力——YOLOv5 的--hyp参数指向一个数据增强配置文件里面可以调hsv_h、degrees、translate等。海洋场景的目标经常有旋转变化适当增加degrees: 15会让模型对方向更鲁棒。6. 验证模型质量与可视化从 mAP 到实际推理的最后一公里训练跑完不等于事情结束。你需要一套完整的验证手段确认模型是真的学会了而不是碰巧在 val 集上蒙对了。6.1 用混淆矩阵看类别间混淆训练日志里生成的confusion_matrix.png是最直接的质量报告。它显示每个真实类别被预测成了哪些类别。如果 ship 经常被预测成 boat说明这两个类别本身在视觉上相似度高或者是标注边界不清晰。你可以回到标注文件里看这两类目标的框是不是有大面积重叠的情况——如果训练数据里 ship 的框把船旁的小艇也包进去了模型自然会学歪。6.2 可视化预测结果YOLOv5 训练完会生成val_batch0_pred.jpg展示模型在验证集图片上的预测效果。我每次训练完都会先看这张图重点检查三件事小目标有没有被漏检比如远处的浮标、独木舟这类小物体框有没有偏移目标真实位置框严重偏离说明回归头没收敛有没有出现大面积的误检比如把海浪纹理识别成了目标如果这三项中任何一项有明显问题先别调模型结构回看训练数据的标注——大概率是标注质量或者类别定义出了问题。6.3 真实场景推理测试最终验证还是要回到真实场景。导出模型后用一段简单脚本跑个测试import torch from PIL import Image # 加载训练好的权重 model torch.hub.load(ultralytics/yolov5, custom, pathruns/train/exp/weights/best.pt) # 推理一张训练外的图片 img Image.open(real_test.jpg) results model(img, size640) # 推理分辨率数值可调 results.show() # 输出检测结果详情 results.print()参数说明size640是推理分辨率这个值不一定要和训练分辨率一致。如果你想提升小目标检测效果可以用size1280做推理算力够的话效果立竿见影。results.print()会输出每个目标的类别、置信度和坐标格式直观。6.4 模型导出的习惯从那以后我每次训练完模型都强制走一遍导出流程——export.py --include onnx把训练好的 PyTorch 权重转成 ONNX 格式。原因很实际部署到嵌入式设备、用 TensorRT 加速推理、或者跨语言调用时ONNX 是兼容性最好的中间格式而 PyTorch 权重只能在 Python 环境里跑。python export.py --weights runs/train/exp/weights/best.pt --include onnx --simplify--simplify参数会用 ONNX Simplifier 去掉计算图中的冗余节点压缩模型体积推理速度也能提升一点。这个习惯帮我避过不少部署阶段的麻烦值得你同样保持。希望这套流程能帮你把数据集里的 5000 张图真正变成能用的模型——从划分数据到训练再到部署验证每一步都踩实了结果自然就差不到哪去。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进