
简介面向海洋生态监测、水产养殖智能化与生物多样性研究的海洋鱼类目标检测数据集共含921张真实海洋环境拍摄的鱼类图片及配套YOLO格式标注覆盖大西洋鲳鱼、石首鱼、篮子鱼和刺尾鱼四个常见物种适合相关领域学生、研究者与算法工程师直接用于目标检测模型训练和数据验证。数据包共1844个文件含921个jpg图片、921个txt标注文件并附带yaml配置与说明文档整体约62.15MB图片与标注一一对应目录清晰加载即可训练。目前已有186人学习使用。所有标注采用边界框加类别标签形式原始图片还包含同帧多目标样本有助于提升模型在复杂水下与近岸场景中的泛化能力。该数据集既能支撑科研论文实验也可作为海洋生物识别教学案例实用价值突出。1. 海洋鱼类目标检测数据集921张真实海图能把你从“无数据焦虑”里捞出来做目标检测的人最怕的不是模型调参而是手里没有一张带标注的真图。这个海洋鱼类目标检测数据集总共921张图片覆盖训练集644张、验证集185张、测试集92张类别定死为四种大西洋鲳鱼Lobotes surinamensis、石首鱼Pomadasys incisus、篮子鱼Siganus luridus、刺尾鱼Stephanolepis diaspros。它不是那种实验室摆拍图全部来自真实海洋环境很多样本是地中海水域拍摄的野生个体带多目标、遮挡、光照不均和背景干扰。它的标注是YOLO格式的txt文件每个文件对应一张图边界框用归一化坐标写死直接扔进YOLOv5/v8或者自己写的检测训练脚本都能跑。对做海洋生态监测、水产养殖视觉方案、或者计算机视觉交叉方向毕业设计的人来说这个数据集的价值在于“场景真实”而非“数量巨大”它把最难搞的水下光照和鱼体形变问题直接摆在你面前省掉你满世界找数据集的功夫。下面从数据构成、格式核对、训练实测、避坑到扩展一层层拆开讲。2. 数据集解剖921张图的构成规律与YOLO标注的真实脾性2.1 文件结构从zip解压到目录树梳理下载下来是一个zip包解压后里面是一堆jpg图片和同名txt标注文件这种结构是Roboflow导出的典型布局。常见做法是每个图片名对应一个txt例如Lobotes-surinamensis-pt1-203_jpg.rf.c867c6b00671a4abc48dc48a7ececcf1.jpg对应的标注文件就是Lobotes-surinamensis-pt1-203_jpg.rf.c867c6b00671a4abc48dc48a7ececcf1.txt。命名里的rf是Roboflow的痕迹pt1可能是拍摄批次或来源相册不用管它模型只认图片和txt的配对关系。我一般拿到这种包第一件事不是急着训练而是写一个脚本核对三件事图片数量与txt数量是否一致、txt里有没有空文件或损坏行、类别编号是否越界。下面是核对脚本的常见写法import os from collections import Counter img_dir images # 图片目录 label_dir labels # 标注目录 imgs [f for f in os.listdir(img_dir) if f.endswith(.jpg) or f.endswith(.png)] txts [f for f in os.listdir(label_dir) if f.endswith(.txt)] img_stems set(os.path.splitext(f)[0] for f in imgs) txt_stems set(os.path.splitext(f)[0] for f in txts) print(图片数量:, len(imgs)) print(标注数量:, len(txts)) print(缺标注的图片:, img_stems - txt_stems) print(无图片的标注:, txt_stems - img_stems) class_counter Counter() empty_txt [] for txt in txts: path os.path.join(label_dir, txt) with open(path, r) as f: lines [line.strip() for line in f.readlines() if line.strip()] if not lines: empty_txt.append(txt) for line in lines: class_id int(line.split()[0]) class_counter[class_id] 1 print(类别分布:, class_counter) print(空标注文件:, empty_txt[:10])这段脚本做的事情就是配对检查和类别分布统计。img_stems - txt_stems找出那些有图但没标注的样本这类样本如果直接喂进训练流程轻则警告重则报错class_counter统计每个类别的实例数量用来判断类别均衡性。这一步不花多长时间但能避免训练到一半才发现数据问题的尴尬。2.2 标注格式细节归一化坐标与类别编号YOLO格式的txt每一行是class_id x_center y_center width height五个数字都是相对于图片宽高的归一化值取值在0到1之间。比如某个txt里有2 0.5432 0.6105 0.3128 0.2841代表类别是2篮子鱼边界框中心点在图片横向54.32%、纵向61.05%的位置框宽占图片31.28%、高占28.41%。这种归一化格式的好处是跟图片分辨率解耦训练时无论输入是640还是416标注都不用改。但有个坑如果标注里的坐标出现0或负数说明目标紧贴边缘被截断很多标注工具会生成越界框训练时容易导致loss异常。常见做法是在数据预处理里加一个过滤或裁剪逻辑把坐标钳制到0-1范围。这个数据集的标注质量从文件名里的内容看不少图片配有拍摄地点和摄影师信息比如某张图写着31st-October-2022-Mgarr-harbour-Gozo-Rodney-Camilleri-fb可以推断部分来源是马耳他Gozo港口的野外记录这类真实拍摄数据比合成图更考验模型的泛化能力。四类鱼的实例分布并不是完全均匀的从文件命名粗略估算大西洋鲳鱼的样本量明显多于其他三类这种不均衡会直接影响训练效果后面避坑章节会专门讲。2.3 两种目录组织方式Roboflow风格与手动整理解压后常见的目录结构有两种一种是train/images、valid/images、test/images加对应labels兄弟目录另一种是Roboflow导出的train、valid、test三个文件夹每个文件夹下再分images和labels子目录。这个数据集采用的是后者。用YOLOv8训练时data.yaml文件路径要指到这三个目录的父级写法如下path: /data/fish_dataset # 数据集根目录 train: train/images val: valid/images test: test/images names: 0: Lobotes surinamensis 1: Pomadasys incisus 2: Siganus luridus 3: Stephanolepis diaspros注意names的顺序必须和标注txt里的class_id一致否则模型会把大西洋鲳鱼识别成刺尾鱼这种错位问题训练时不会报错但验证集mAP会异常低。我一般会在训练前先跑一遍model.predict在test集上出几张图人工看一眼类别颜色对不对这一步能省掉后面好几个小时的排错时间。3. 选型分析这个数据集适合什么任务与场景适配边界3.1 四类鱼种的识别难度差异这四种鱼在视觉上有明显区分度。大西洋鲳鱼体侧偏扁轮廓接近菱形背鳍和腹鳍对称展开时非常有辨识度石首鱼体型更修长体色银灰带暗斑篮子鱼体表有细密花纹背鳍刺较长刺尾鱼体侧有竖向条纹。但这只是“理想状态”实际拍摄中鱼体会弯曲、侧转、被遮挡或与背景融为一体导致同一物种的形态变异极大。在实际训练中大西洋鲳鱼因为样本多且形态特征强通常会获得较高的AP值。而刺尾鱼样本少、体色随环境变化大有些个体偏黄、有些偏褐可能是四类里最难收敛的类别。如果你做的是粗分类鱼 vs 非鱼这个数据集四类合并也能用如果要做细粒度识别建议额外补充刺尾鱼的样本。3.2 数据规模对模型选型的约束921张图对YOLOv8n这种轻量模型来说勉强够用但对YOLOv8x或更重的Transformer模型来说偏少。以我的经验这个规模最适合的模型是YOLOv8s输入分辨率640x640开启mosaic和mixup增强训练100个epoch左右能收敛到不错的效果。如果上YOLOv8l没过拟合是运气过拟合是常态。数据规模还有一个隐性问题测试集只有92张按每张图上平均1.5个目标估算测试评估的实例总数不超过150个。这意味着mAP的波动会很大单次评估的分数不可全信。常见做法是用K折交叉验证把训练集拆成5折每轮换一块验证把5轮的mAP取平均得到更稳定的评估结果。这个数据集的总量不大交叉验证的计算成本完全可以接受。3.3 数据增强策略的倾向性选择水下图像的通病是偏蓝、低对比度、光照不均匀这个数据集的部分图片有明显色偏。训练时我一般不开太多色彩抖动而是优先用空间增强旋转15度以内、水平翻转、随机缩放10%、mosaic。原因很简单鱼的朝向在图片里不固定模型需要学习的是“形状纹理”特征而不是颜色恒等性。色彩增强开太猛会导致模型学到错误的颜色关联。另一个有效的增强是Cutout或Random Erasing模拟鱼被海草或珊瑚遮挡的情况这对提升模型在复杂场景下的鲁棒性帮助明显。YOLOv8自带这些增强在训练配置里调整hsv_h、degrees、scale这几个参数就能控制强度。我的习惯是degrees15, scale0.3, hsv_h0.01幅度克制但有效。4. 实操复现用YOLOv8从数据集到训练完成的完整记录4.1 数据准备与YOLOv8环境安装整个流程我以YOLOv8为例因为它的API简洁、配置灵活且对这个量级的数据集有很好的适配性。安装没什么花活pip install ultralytics装完先验证一下能不能正常导入。然后整理数据目录把训练、验证、测试的图片和标注按前面 data.yaml 里的结构摆好。这个数据集的zip解压后目录可能带有Roboflow的嵌套层级常见做法是先写一条shell命令把图片和标注文件平铺到正确位置unzip fish_dataset.zip -d fish_dataset cd fish_dataset mkdir -p train/images train/labels valid/images valid/labels test/images test/labels # 将对应子目录中的图片和txt拷入上述目录具体路径按解压后的结构调整拷贝时注意图片和txt要成对拷入别只拷图片忘了标注。拷完用前面那个核对脚本再走一遍确认目录干净没有多余文件。这一步虽然机械但能防止训练时数据集加载器due到奇怪的文件名编码问题。4.2 训练启动与关键参数调整训练命令用ultralytics的标准接口参数我按这个数据集的实际情况做了微调yolo detect train \ modelyolov8s.pt \ data/data/fish_dataset/data.yaml \ epochs100 \ imgsz640 \ batch16 \ patience15 \ augmentTrue \ degrees15 \ scale0.3 \ hsv_h0.01 \ project/output/fish_yolov8 \ namerun1modelyolov8s.pt用COCO预训练权重做迁移学习初始化对中小规模数据集极其关键冷启动在921张图上很难收敛。patience15表示验证集mAP连续15轮不提升就早停防止无效训练浪费时间。batch16是考虑到显存和BN层统计稳定性的折中如果你的显卡只有6G显存调小到8并配合梯度累积。训练过程中重点观察两个指标训练loss下降曲线是否平滑验证集mAP50和mAP50-95是否同步上升。如果训练loss下降但mAP不动大概率是过拟合或数据分布问题如果两者都不动先检查data.yaml路径和类别映射。训练结束后ultralytics会自动保存best.pt和last.pt到项目目录。4.3 模型评估与可视化验证训练完别急着部署先用验证集和测试集分别出评估结果yolo detect val \ model/output/fish_yolov8/run1/weights/best.pt \ data/data/fish_dataset/data.yaml \ splitval yolo predict \ model/output/fish_yolov8/run1/weights/best.pt \ source/data/fish_dataset/test/images \ saveTrue评估结果会打印每个类别的AP、整体mAP等信息。predict跑完去保存目录翻图片重点看三件事有没有把鱼漏检成背景、有没有把背景误检成鱼、类别有没有标错。很多时候mAP数字还行但可视化一看就露馅——比如把石首鱼全部识别成篮子鱼这种情况看混淆矩阵就能发现。混淆矩阵的调取方式是在val结果的输出目录里找到confusion_matrix.png直接看对角线数值和非对角线的分布。如果类别之间混淆严重优先怀疑标注质量问题或类别特征相近这时候补样本比调模型有用。5. 避坑与排查这个数据集最常见的五个坑位5.1 坑一训练时提示图片数量与标注数量不匹配现象启动训练后ultralytics在数据加载阶段直接报错或警告部分图片被跳过。原因Roboflow导出的zip包里偶尔会混入无标注的图片或者某张图片的txt文件损坏打不开。这个数据集里如果存在某些图片是纯背景记录标注工具没有给框就会产生这种孤儿图片。解决用第一节的核对脚本先跑一遍把缺标注的图片移到单独的文件夹或直接删除。务必在训练前处理别依赖训练器自动跳过——自动跳过会破坏batch构成影响BN统计。5.2 坑二小目标鱼体漏检严重现象训练完在验证集上mAP50还可以但mAP50-95偏低测试图上远处的小鱼目标几乎全漏。原因这四种鱼的野外照片里不少目标在画面中占比不足5%也就是典型的“小目标”。YOLOv8默认的P3特征层对中小目标有响应但在这个数据量下小目标样本天然稀少模型学不够。再加上测试集本身只有92张小目标占比不高评估指标容易被大目标“平均”掉。解决把输入分辨率从640提到960或1280小目标像素数多了模型才看得清。如果显存不够用SAHI做切片推理大图上切成小块再检测对小目标召回率提升明显。另外可以尝试在训练时开启close_mosaic10让最后10个epoch关掉mosaic避免小目标被拼图增强稀释掉。5.3 坑三类别不均衡导致刺尾鱼AP异常低现象训练曲线显示大西洋鲳鱼的AP在0.8以上刺尾鱼的AP只有0.3左右。原因这个数据集中四类鱼的数量不是1:1:1:1。从文件名粗略统计大西洋鲳鱼样本占比可能接近一半刺尾鱼占比不到15%。模型在样本量充足的类别上收敛良好在稀缺类别上欠拟合。解决两条路。第一是数据层面把刺尾鱼的图片做离线增强复制一份上下翻转、旋转、调亮度的副本把类别比例拉到接近1:1。第二是算法层面YOLOv8不支持class weight参数直接传入但可以按类别比例对loss加权或者在训练时对每一类的采样频率做调整。实际操作中我倾向于先做离线增强简单粗暴且对mAP的稳定提升比调loss更直接。5.4 坑四data.yaml路径写错导致训练“看似正常”但全部fallback现象训练能启动loss在降但验证集mAP始终在0.1以下且predict输出全是一片空白或重复同一类别。原因常见错误有四类——path字段写相对路径但当前工作目录不对names顺序与标注class_id不一致test路径写成了val路径目录名大小写与Linux文件系统不匹配。ultralytics对绝对路径最友好但很多人习惯写相对路径。解决训练前先单独执行yolo val modelyolov8s.pt dataxxx.yaml splitval用预训练模型跑一遍看看能不能正常加载数据。如果能出正常结果说明数据链路没问题如果报找不到文件或类别索引错逐步排查路径和names。5.5 坑五过拟合在50个epoch左右提前到来现象训练loss持续下降验证loss在某个epoch后反转上升mAP50也停止增长甚至回落。原因921张的总图量太少模型在30-50个epoch后就开始“背题”。尤其是开启了mosaic增强后前中期数据多样性被放大但到了后期模型反复见同一批真实样本记忆效应压过泛化。解决提前设置patience早停并限制epochs不要超过120。同时把weight_decay从默认0.0005调高到0.001给权重加上更强的正则约束。还有一个技巧是固定住backbone的前几层——用freeze10冻结前10层让模型主要在head部分做适配能显著延缓过拟合。6. 把YOLOv8模型迁移到其他框架ONNX导出与mmrotate扩展实战6.1 ONNX导出与推理加速训练完的模型要落地通常第一步是导出为ONNX格式这样C、Java、Python各路都能调也能用TensorRT做GPU加速from ultralytics import YOLO model YOLO(/output/fish_yolov8/run1/weights/best.pt) model.export(formatonnx, imgsz640, opset12)导出后的ONNX模型可以直接用onnxruntime推理。需要注意opset的兼容性opset太新在旧版onnxruntime上会报不支持的算子错误。推理时可以用session.get_inputs()查看输入张量的shape通常是[1, 3, 640, 640]NCHW布局。这部分的价值在于脱离ultralytics环境部署不受Python版本和torch依赖的绑架。6.2 格式转换从YOLO到DOTA与mmrotate接轨如果你是做遥感图像目标检测的手头有一些带角度的旋转框需求YOLO的轴对齐框就不够用了。mmrotate是遥感目标检测的主流工具箱它需要的标注格式是x1 y1 x2 y2 x3 y3 x4 y4 class_id is_hard这样的四点旋转框。这个数据集标注是轴对齐框没法直接转成旋转框但可以用一个常见技巧把轴对齐框转成四点坐标然后让标注工具的自动分割功能生成贴合鱼体的旋转框。def yolo_to_dota(txt_path, img_w, img_h, out_path): with open(txt_path, r) as f: lines f.readlines() with open(out_path, w) as f: for line in lines: parts line.strip().split() cls int(parts[0]) xc, yc, bw, bh map(float, parts[1:]) x1 (xc - bw / 2) * img_w y1 (yc - bh / 2) * img_h x2 (xc bw / 2) * img_w y2 (yc - bh / 2) * img_h x3 (xc bw / 2) * img_w y3 (yc bh / 2) * img_h x4 (xc - bw / 2) * img_w y4 (yc bh / 2) * img_h f.write(f{int(x1)} {int(y1)} {int(x2)} {int(y2)} {int(x3)} {int(y3)} {int(x4)} {int(y4)} {cls} 0\n)这段代码把YOLO归一化框还原成像素坐标下的四点矩形框写入DOTA格式。转换后的旋转框依旧是个矩形但胜在能和mmrotate的数据加载器直接对接后续做任意角度检测时可以在这个基础上用roLabelImg手动修正旋转角度。别指望自动转换能直接产出高质量的旋转框它只是给你省去手工重标注的重复劳动。6.3 交叉验证的性价比用test集做统一基准数据集自带的test集只有92张但它的用途不是给你训完顺手看一眼mAP就完事。我的习惯是把这个test集作为“出海基线”只在最终版本模型上跑一次中间所有调参都只用val集。原因是多次在test集上评估会产生隐式过拟合模型会在不知情的情况下“记住”test集。实际操作中我用K折交叉验证做完模型选型确认最优超参组合后再在固定test集上做最终报告。四类鱼的mAP表格横着放类别、竖着放实验组一对比就能看出每个类别的增益和短板。这个习惯是从一次惨痛教训总结出来的——当时反复用同一个test集调参最后去现场测试效果惨不忍睹。从那以后我每次训练任何数据集都强制走一遍“固定test集交叉验证选参”的流程这个数据集的体量正好适合这样操作成本低收获大。希望帮到你。本文还有配套的精品资源点击获取