简介这套目标检测数据集面向计算机视觉学习者与水下生物识别项目开发者整理为YOLOV5目录结构图像为1920×1080分辨率含海参、海胆、扇贝、海星、海草5个类别训练集6080张、验证集1520张均配对应txt标签另有1200张无标签测试图可直接接入模型训练流程。压缩包共2000个文件其中1999个为txt标签与类别字典文件、1个为可视化脚本整体约469.75MB无需额外转换。所有图片与标签均按YOLOV5规范命名类别字典txt文件便于映射类别ID可视化show.py可直接运行随机指定一张图片即可输出带边界框的标注效果方便核验数据质量。图像来源于水下环境分辨率高能覆盖不同姿态与遮挡情况有利于提升检测模型鲁棒性训练/验证/测试目录划分明确适合用于YOLOv5系列模型训练、验证与评测。目前已有597人学习下载可作为水下目标检测实战的可靠数据基础。1. 水下检测项目为什么绕不开数据集这块压舱石做目标检测的人看标题第一眼盯的不是“海鲜”也不是“5个类别”而是“YOLOV5目录格式”这六个字。水下海鲜动植物的检测任务和路面交通、工业质检最大的区别在于公开能用的、带高质量标注的、分辨率还够用的数据集非常少。大多数现实项目跑起来第一步不是调模型而是先整理一套能让 YOLOV5 直接读的数据集。这个标题说的就是解决这类问题的一套标准做法——把大分辨率水下生物图像按 YOLOV5 约定的目录结构组织好完成 5 类目标的标注与训练准备。适合谁看给刚开始做水下机器人抓取、养殖巡检、渔业资源调查的工程师也适合手里有一堆水下视频但不知道怎么转成可用训练集的团队。我可以直接说结论这类任务能不能顺利落地八成功夫在数据准备。YOLOV5 目录格式本身很简单但大分辨率带来的小目标问题、水下图像偏色问题、类别不平衡问题都会在标注和训练环节连环引爆。下面按我实际做过的一套水下 5 类目标检测数据方案来拆包含目录结构、标注格式、训练配置和排错经验。2. 拆开 YOLOV5 目录从图像文件到标注 txt 的映射规则2.1 labels 和 images 必须同名训练加载时到底怎么找文件YOLOV5 对数据集的目录要求并不复杂但它有一个硬性约定每张图片对应的标注 txt 必须和图片同名只是扩展名不同。图片放在 images 子目录标注放在 labels 子目录。训练时YOLOV5 会从 labels 目录里找和 image 同名的 txt找不到就跳过这张图并且会在终端里警告 “unused image”。常见做法是维护这样的目录骨架dataset/ ├── data.yaml ├── images/ │ ├── train/ │ │ ├── frame_0001.jpg │ │ └── frame_0002.jpg │ └── val/ │ ├── frame_0101.jpg │ └── frame_0102.jpg └── labels/ ├── train/ │ ├── frame_0001.txt │ └── frame_0002.txt └── val/ ├── frame_0101.txt └── frame_0102.txt这里 train 和 val 在 images 和 labels 下要严格一一对应。稍微省事一点的做法是 images 和 labels 平级都用 train/test 子目录来对称组织而不要出现 images/train 和 labels/val 这种错位结构。YOLOV5 源码里是拿 image 的路径去 replace 出 label 路径的目录层级一旦不对称就会全部加载失败。大分辨率水下图像通常不会整张直接扔给 YOLOv5 训练因为显存撑不住但我们仍然按原图存放。原始图像保持原始分辨率框架在训练时会自己缩放到模型输入尺寸比如 640x640。不要提前把大图压成小图再训练那样等于永久丢弃了小目标信息。正确姿势是存储用原图大分辨率训练时靠 YOLOv5 的 letterbox 缩放来喂给模型。2.2 标注 txt 的五列数字归一化坐标系里的真相打开一个 YOLO 格式的 txt你会看到每行五个数字依次是类别 id、归一化中心点 x、归一化中心点 y、归一化宽度 w、归一化高度 h。所有坐标值都除以了图像宽高范围在 0 到 1 之间。下面是一行真实标注样例# 类别0海参中心点(0.42, 0.38)宽高分别为全图的0.51和0.23 0 0.42 0.38 0.51 0.23你可能想问为什么不用绝对像素坐标原因是 YOLOv5 训练时会对图像做 letterbox 填充也就是先缩放再补边绝对像素坐标在缩放后就全乱了。归一化坐标天然免疫缩放只要 letterbox 用得一致标注就不用二次处理。这个设计是 YOLO 格式最需要理解的一点。从像素坐标转成 YOLO 格式的计算方式如下# 假设bbox为[x1, y1, x2, y2]图像宽高为img_w, img_h x_center (x1 x2) / 2 / img_w y_center (y1 y2) / 2 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h我一般会在标注脚本里直接做这一步而不是手工去改 txt。需要特别注意的是如果是用 LabelImg 这类工具导出的 Pascal VOC XML转 YOLO 格式时别忘了它是左上角和右下角坐标而 YOLO 需要的是中心点加宽高。这中间一旦算错训练出来的模型预测框全是偏移的。2.3 data.yaml三分钟写好训练入口文件数据集目录准备齐之后data.yaml 是唯一需要手动写一遍的配置文件。它告诉 YOLOv5 训练脚本训练集、验证集图片路径在哪类别名字有几个。下面是针对本数据集的 YAML 内容。# data.yaml train: dataset/images/train val: dataset/images/val nc: 5 names: [sea_cucumber, sea_urchin, scallop, starfish, jellyfish]这个文件里的 path 路径写的是 images 目录而不是根目录YOLOv5 会自动映射到同级 labels 目录。如果你把 train 写成整个 dataset 根目录就会报找不到标签。还有一类常见错误是 names 数量和 nc 不一致nc 写成 5names 却列了 6 个名字YOLOv5 不报错但类别 id 会错位最后训练出的 mAP 表格和实际类别对应不上。这种玄学错误最耗时间原因就是 data.yaml 里 nc 和 names 数量不匹配。3. 大分辨率水下 5 类目标从标注到训练都要换一套打法3.1 五个类别的视觉差异海参、海胆、扇贝、海星和水母水下目标检测的数据集里5 个类别如果选得视觉差异大模型学起来就轻松如果选了外观接近的类别就会无谓增加标注成本。比较好的类别搭配是海参、海胆、扇贝、海星和水母。海参是长条深色椭圆海胆是近圆形带棘刺扇贝有明显的扇形沟纹海星呈五角放射状水母则半透明且形状不规则。这五类在实际拍摄中相互遮挡频繁标注时要注意边界框尽量贴合目标外轮廓不要为了省事把两个靠在一起的个体框成一个大框。标注规范会影响模型上限。我给一个简单有效的标注纪律每个目标独立一个框哪怕两个海鲜挨在一起、边缘接触也必须分两个框遮挡超过一半的个体可以不标但要保持全局一致。这个纪律看起来笨但在训练时不至于让模型学到“两个海参合并成一个学习目标”这种错误映射。3.2 大分辨率是小目标问题的放大器水下相机拍摄的图像往往在 1920x1080 以上而一只成年海参在画面里可能只占 80x200 像素。YOLOv5 默认输入是 640x640原图缩放到这个尺寸后海参的宽度只剩 27 个像素左右属于典型小目标。小目标检测的第一道坎不在模型在标注质量如果标注框稍微偏了几个像素归一化后误差占比就很大。针对这种场景常见的做法是先把大图切块再训练也叫 tile 切分。比如把 1920x1080 的图像切分成 6 个 640x640 的小图每个小图单独训练。切分时需要处理边界框跨块问题要么跨块目标分配给 IoU 最大的一块要么丢弃。我一般会在切图脚本里用 IoU 大于 0.5 就保留给该块的规则这样切图后训练集涨了每个目标在模型输入尺寸中的像素占比也提上来了。# tile切分示例将1920x1080按640x640切块 # 对每个块计算目标框与块的IoU0.5则保留 def tile_image_and_filter_boxes(img, boxes, tile_size640): tiles [] new_boxes [] for y in range(0, img.shape[0], tile_size): for x in range(0, img.shape[1], tile_size): tile img[y:ytile_size, x:xtile_size] kept [] for b in boxes: iou compute_iou(b, [x, y, xtile_size, ytile_size]) if iou 0.5: # 将框坐标转换到当前块内 nb shift_box(b, -x, -y) kept.append(nb) if len(kept) 0: tiles.append(tile) new_boxes.append(kept) return tiles, new_boxes这个方案能缓解小目标问题但代价是训练时间变长、标注工作量增加。如果你的显存能开到 1280 输入分辨率也可以不切图直接训但 batch size 要降下来。我的建议是项目 deadline 紧张就先用 640 输入直接训一版看效果如果 AP 太低再上 tile 切图。3.3 类别不平衡是水下数据的隐藏雷区水下拍摄场景里海星往往成群出现水母则可能整段视频都没有。统计一下标注框数量很可能是海星占了 40%水母只占 3%。类别不平衡会让模型偏向样本多的类别在推理时把不确定目标全部预测成海星。处理不平衡有一条朴素路径先统计各类别目标框数量对少数类做重复采样。例如把水母样本复制两到三份放进 dataset 目录而不是简单地把整张图重复。也可以借助 YOLOv5 自带的 mosaic 增强它会让少数类目标在拼接图中反复出现变相缓解不平衡。最省事的办法是训练时给每个类别单独计算损失权重但 YOLOv5 默认不支持按类别设置 loss 权重改起来工作量大不如从数据本身入手来得快。4. 从零跑通 YOLOv5 训练命令、参数和验证一条线4.1 先用预训练权重做三分钟冒烟测试拿到一套新数据集不要直接从头开始训练。我习惯先随便拿一个已有权重做冒烟测试目的不是追求精度而是验证数据链路有没有断图片能否加载、标注能否对应、loss 有没有降。这个环节能筛掉一半以上的低级错误。# 假设已经clone了YOLOv5仓库并安装依赖 python train.py --data dataset/data.yaml \ --weights yolov5s.pt \ --epochs 5 \ --batch-size 8 \ --img 640如果前 5 个 epoch 的 loss 在下降说明数据链路基本正常。如果 loss 直接是 NaN或者训练完的 val 结果全为 0那大概率是标注格式问题。这里提醒一个细节--weights yolov5s.pt会下载 COCO 预训练权重它会包含 COCO 的 80 类输出头但迁移到 5 类数据集时 YOLOv5 会自动把最后一层改成 5 类输出无需手动处理。--img 640表示输入分辨率。上一条说过大分辨率原图会在预处理阶段被压缩。如果跑完一轮后发现小目标漏检多可以把 img 提到 960 或 1280。代价是显存占用翻倍batch size 要减半。我的排查顺序永远是先确认 loss 能降再调输入尺寸最后才动结构。4.2 训练核心参数怎么设epoch、batch、anchor 和 patience水下目标检测我在 5 类数据上常用的训练参数组合是epoch 100 到 150batch size 8 到 16取决于是单卡还是双卡img 640 起步patience 20。patience控制的是早停如果验证集 mAP 连续 20 个 epoch 没提升训练自动停止防止过拟合。数据集如果只有几千张图epoch 没必要拉满 300跑 100 轮左右就够收敛。YOLOv5 有个容易被忽略的还魂丹自动 anchor。它会根据你数据集中所有标注框的宽高分布用 k-means 重新计算 anchor 尺寸以符合目标实际大小。水下目标往往比 COCO 的目标更小更扁长自动 anchor 算出来的结果和默认值差很多。打开训练日志如果看到AutoAnchor相关的提示说明它在正常工作如果提示 anchor 比值超限可以考虑用--noautoanchor关闭并按提示手动修改模型 yaml。# 以更高分辨率训练batch相应调小 python train.py --data dataset/data.yaml \ --weights yolov5s.pt \ --epochs 150 \ --batch-size 4 \ --img 1280 \ --patience 20训练完成后模型权重保存在runs/train/exp/weights/best.pt。注意要用 best.pt 而不是 last.pt 做推理前者是验证集上最优的权重后者只是最后一个 epoch 的存储。在实际部署时我会额外跑一次val.py把各类别的 AP 单独打印出来才能看到具体哪个类拖了后腿。4.3 推理前的最后一道关letterbox 一致性这个问题坑过不少人。训练时 YOLOv5 用 letterbox 把图缩放到 640x640并在两侧补灰边。推理时如果不用同一个 letterbox 逻辑而是直接把图拉伸到 640x640预测框位置会全部偏移。YOLOv5 的detect.py已经内置了统一的 letterbox但如果你自己写部署脚本很容易踩这个坑。# 自定义推理时必须使用和训练一致的letterbox from utils.augmentations import letterbox img0 cv2.imread(test.jpg) img letterbox(img0, 640, stride32, autoTrue)[0] img img.transpose((2, 0, 1))[::-1] # BGR to RGB img np.ascontiguousarray(img)stride32很关键它保证缩放后的尺寸是 32 的倍数避免边缘残留不对齐。如果你不传 stride某些情况下缩放出来的尺寸不是 32 的倍数特征图尺寸和训练时不一致推理结果就会出偏差。做部署时一定要把训练代码里的 letterbox 参数原封不动搬到推理端一个字符都不要改。5. 数据集避坑指南四条真实踩坑记录5.1 现象训练正常但 val 的 mAP 为 0loss 也不降原因labels 里的类别 id 超出了 data.yaml 中 nc 的范围。比如标注文件里写了 5而 names 只有 5 个类id 从 0 到 4 是合法的但如果你从 1 开始编号id5 就超界了。YOLOv5 不一定会崩但模型输出层的维度对不上结果就是 loss 不降。解决检查所有 txt 里的第一个数字确保最大值为 nc-1。通常写一个全局扫描脚本遍历所有标注文件打印出出现过的类别 id 和分布。# 快速核查标注类别id范围 import os ids set() for root, _, files in os.walk(dataset/labels): for f in files: if f.endswith(.txt): with open(os.path.join(root, f)) as fp: for line in fp: ids.add(int(line.split()[0])) print(出现的类别id:, sorted(ids))5.2 现象训练速度快但 map 上不去验证集里漏检严重原因train 和 val 划分时不严谨同一段视频的连续帧同时进了两个集合。水下视频帧之间相似度高模型在训练时见过这些画面验证时自然分数较高但真实场景表现差得多。这个叫数据泄漏是视频抽帧做数据集最容易犯的错误。解决按视频文件划分而不是按帧随机划分。比如视频 A 的帧全进 train视频 B 的帧全进 val。这样验证集才具备真正的泛化意义。我在做水下数据集时就要求脚本按视频目录抽帧不做全局 shuffle 再切分。5.3 现象大分辨率下小目标检测几乎全部丢失原因直接 640 输入训练一只海参缩到 20 几个像素特征在下采样四层之后基本没了。模型不是不会检测而是输入分辨率限制了目标有效信息量。解决优先换用 1280 分辨率训练。如果显存撑不住改用 tile 切图方案。两种方式的对比我这边实测是1280 输入比 640 输入的 mAP 提升 5 到 8 个点tile 切图也能提升 3 到 5 个点。tile 的缺点在于推理时要先切图再拼结果部署复杂度上升。项目周期允许的前提下优先 1280。5.4 现象loss 先降后反弹验证集 AP 波动剧烈原因不是数据集问题是数据增强过猛。水下图本身颜色偏蓝绿YOLOv5 默认开启 HSV 增强色相变化会把本来就偏色的图像变得离谱模型学到一堆不存在的颜色关系导致过拟合到增强分布上。解决在训练时调低或者关闭 hsv 相关的增强参数。数据增强不是越强越好要贴合场景。水下场景 hue 变化范围可以压缩比如 hsv_h 从默认 0.015 降到 0.003或者用--hsv-h 0.0直接关闭色相增强保留亮度和饱和度增强。6. 把 5 类数据集用到极致的三个进阶技巧第一善用自动 anchor 分析的结果做人工干预。YOLOv5 每次训练在终端会打印 anchor 均值仔细看它对每类的宽高统计。如果发现某个类比如水母的框普遍偏大偏圆可以考虑在模型 yaml 里手动为它分配相近的 anchor减少 k-means 的随机性。这个方法我实际用过对提升水母这类形状不规则目标的召回率有明显效果。第二把验证集做成分层抽样按类别比例保持样本均衡。最简单的方法是统计目标框类别分布然后按视频分组抽样确保不同视频源均进入。如果你只有五六个视频片段最好每个视频都切一段进验证集防止某一类目标只在训练数据里出现。第三训练完成后对整个验证集做一次错误分析把假阳性框打印到图上。水下场景最常见的假阳性是海胆的棘刺被识别成海星、水母透明边缘被识别成扇贝。看错图比看 mAP 数字有用得多因为它直接告诉你下一步该补什么数据或者调什么参数。这套流程我反复跑过很多次每次拿到新的水下目标检测数据第一件事永远不是调模型而是先花时间把 YOLOV5 目录结构、同名对应、类别 id 核查这三件事做扎实。很多后期训练的“玄学问题”追根溯源都是数据准备期埋下的雷。如果能把数据组织这一步做干净模型训练的成功率会高很多希望这些经验能帮到你。本文还有配套的精品资源点击获取