
简介面向目标检测开发者的捕鱼识别数据集包含1813张带标注图像支持YOLOv5、YOLOv7、YOLOv8、YOLOv9、YOLOv10及YOLO11等主流YOLO算法直接训练与验证。数据已预先划分好可直接用于模型训练和测试。标签同时提供两种格式YOLO格式的txt文件与VOC格式的xml文件txt中以类别索引与归一化的中心点坐标、宽高记录每个目标框xml则保留了更完整的对象信息便于在其他检测框架中转换使用。压缩包共2000个文件其中xml标注1584个、txt标注416个整体大小96.7MB目录结构清晰可快速定位到图像与对应标注包内已按用途分类无需额外整理即可开始使用。资源已有75人学习适合渔业监测、水产养殖等目标检测场景也可帮助初学者通过实际标注数据熟悉YOLO系列数据组织方式与标签格式差异从而更快完成模型迭代与效果验证。1. 捕鱼识别为什么绕不开 YOLO1813 张图能撑起一个检测任务做渔业监测、水产养殖或者非法捕捞监管的同行大概率都经历过这种尴尬翻遍公开数据集找不到几张带标注的渔船和渔网图像自己标数据一张图里几十条鱼挤在一起框到手抽筋。yolo算法-捕鱼识别数据集这个标题指向的正是一份已经切好标签的 1813 张图像资源直接喂给 YOLO 系列做训练省掉最耗时的标注环节。1813 张听起来不多但对检测任务来说它够不够用取决于你要识别什么、场景有多杂、以及你愿不愿意做迁移学习。适合读这篇的人不是刚装好 CUDA 的萌新而是手里有监控视频、有无人机航拍、或者有养殖池摄像头想快速出一个能用模型的从业者。更直白地说这篇文章会带你拆解这份数据集的结构、标注格式、训练参数怎么调、推理脚本怎么写以及最重要的——哪些坑会让你白跑十几个小时。后面每一节都按「先讲清楚为什么这么干再给你能抄的命令」来写。2. 拆开 zip 看数据集目录结构、标注格式与类别的门道2.1 1813 张图的常见组织方式images 与 labels 分家拿到一个 YOLO 系列的数据集压缩包第一件事不是解压就训练而是看目录结构。常见做法是解压后得到一个主目录内部包含images和labels两个文件夹各自再拆成train、val、test三个子集。如果压缩包是别人整理好的大概率长这样yolo-fishing-dataset/ ├── images/ │ ├── train/ # 约 1200 张 │ ├── val/ # 约 300 张 │ └── test/ # 约 200 张 ├── labels/ │ ├── train/ # 与 images/train 一一对应的 txt 文件 │ ├── val/ │ └── test/ ├── data.yaml # YOLO 训练入口配置文件 └── README.md # 类别说明、来源说明打开data.yaml你会看到最关键的三行信息train和val的路径、以及names类别列表。捕鱼识别数据集的类别通常不只一类常见的有fish、fishing_net、boat、fisherman等有的还会细分鱼种。每张图对应一个同名 txt 文件比如IMG_0001.jpg对应IMG_0001.txt文件名一模一样只是扩展名不同。如果解压后发现文件名对不上多半是标注工具导出时的命名规则不一致这是第一个要检查的点。2.2 txt 标签文件逐行解读class cx cy w h 到底代表什么YOLO 格式的标签文件用纯文本存储每行代表一个目标框格式是五个数字类别ID、中心点x坐标、中心点y坐标、框宽度、框高度。所有坐标都是归一化到 0~1 的相对值除以了图片宽高。举个例子某张 1920x1080 的图里有一条鱼框的左上角在 (480, 270)右下角在 (960, 810)那 txt 里存的就是0 0.375 0.5 0.25 0.5计算过程中心点 x (480 960) / 2 / 1920 0.375宽度 (960 - 480) / 1920 0.25高度同理。这个格式是 YOLOv5、YOLOv8、YOLOv9 乃至 YOLOv11 通用的所以这份数据集理论上可以直接喂给不同版本的 YOLO不需要转换脚本。唯一要注意的是类别 ID 必须从 0 开始连续编号如果发现 txt 里出现负数或大于类别总数的 ID那就是标注工具或转换脚本出错了训练时会直接报错或吞掉该目标。2.3 先做一次可视化核验别急着训练数据集的标注质量直接影响模型上限而 1813 张小数据集里哪怕只有 5% 的脏标签都足以让 mAP 掉好几个点。我一般会用 OpenCV 写个脚本把标注框画回原图挑几十张肉眼扫一遍import cv2 import os img_dir yolo-fishing-dataset/images/train label_dir yolo-fishing-dataset/labels/train class_names [fish, fishing_net, boat, fisherman] # 以 data.yaml 为准 for img_name in os.listdir(img_dir)[:50]: img_path os.path.join(img_dir, img_name) label_path os.path.join(label_dir, img_name.replace(.jpg, .txt)) img cv2.imread(img_path) h, w img.shape[:2] if not os.path.exists(label_path): print(f警告: {img_name} 缺少标签文件) continue with open(label_path, r) as f: for line in f.readlines(): parts line.strip().split() cls_id int(parts[0]) cx, cy, bw, bh map(float, parts[1:]) 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), (0, 255, 0), 2) cv2.putText(img, class_names[cls_id], (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) cv2.imshow(check, cv2.resize(img, (960, 540))) if cv2.waitKey(0) ord(q): break cv2.destroyAllWindows()这段代码做了三件事遍历前 50 张训练图、读取同名 txt 标签、把每个框画回图上并标注类别名。逻辑上要注意os.path.exists这个分支它能暴露「有图没标签」或「标签名不匹配」的问题这类问题在解压不完整或文件被中间环节重命名时非常常见。坐标换算的公式cx * w是核心如果不对画出来的框位置会明显偏移。参数里[:50]控制抽查数量实际我建议全量过一遍脚本找出所有缺标签的文件再补回去。3. 用 YOLOv8 训练捕鱼识别模型从 yaml 配置到权重产出3.1 为什么选 YOLOv8 而不是 YOLOv5 或 R-CNN这份数据集既然打着 YOLO 的名号训练框架优先在 YOLO 系列里选。YOLOv8 是目前最稳的版本文档全、报错少、默认超参在中小数据集上表现不错而且ultralytics库把训练、验证、导出封装成一条命令。YOLOv5 的优势是生态老、资料多但维护已进入收尾阶段YOLOv11 和 v12 虽然指标更好对小数据集反而容易过拟合默认增强策略也偏激进。至于 R-CNN 系列检测速度慢一个量级捕鱼场景往往是视频流实时分析推理帧率上就被否掉了。3.2 改 yaml 文件数据路径与类别名的三个必改项用 YOLOv8 训练自己的数据集第一步不是写代码而是改配置。把数据集里的data.yaml打开按下面的模板核对# 数据集配置文件 data.yaml path: /absolute/path/to/yolo-fishing-dataset # 改成你的绝对路径 train: images/train val: images/val test: images/test # 可省略验证时用不上 nc: 4 # 类别数量和 names 列表长度一致 names: 0: fish 1: fishing_net 2: boat 3: fisherman三个必改项第一是path必须写绝对路径YOLOv8 对相对路径的解析有时会因为工作目录不同而翻车第二是nc必须手动核对很多数据集的 yaml 里nc写错了多写或少写一个数字都会导致训练时类别索引越界第三是names的排列顺序必须和标签 txt 里的类别 ID 严格对应。如果数据集的 README 里说 fish 是 ID 0但 yaml 里names第一项是 boat训练不会报错但模型学的东西就是错的——你标注里 ID 0 代表鱼模型却当成船来学。3.3 跑通训练命令batch size、epochs 与预训练权重配置改完后直接用ultralytics库启动训练。命令如下yolo detect train \ data/absolute/path/to/yolo-fishing-dataset/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ patience20 \ projectfishing_train \ nameexp_yolov8s这条命令的核心参数拆开看modelyolov8s.pt表示加载 COCO 预训练权重做迁移学习这是小数据集训练的命根子——从零训练 1813 张图基本不可能收敛迁移学习能把收敛时间缩短一个量级。epochs100对这个小数据集来说偏多但有patience20兜底连续 20 轮验证集指标不涨就自动停。imgsz640是 YOLO 默认输入尺寸如果你的原图是 4K 航拍图建议先用 640 跑通流程后续再试 1280 看小目标召回有没有提升。batch16取决于显存12GB 显存跑 yolov8s 用 16 没问题8GB 就降到 8。训练结束后fishing_train/exp_yolov8s/weights/目录下会生成best.pt和last.pt前者是验证集指标最高的权重部署时认准它。另外每个 epoch 结束后还会打印mAP50、mAP50-95、precision、recall四个指标其中mAP50是捕鱼场景最该看的——检测框和真值框的 IoU 超过 0.5 就算命中这个阈值对瓢虫大小的鱼和缠成一团的渔网都比较宽容。3.4 小数据集训练轮数怎么定早停不是万能药patience20看起来稳妥但小数据集有一个隐藏问题验证集只有 300 张随机波动大mAP 可能第 30 轮冲到 0.85第 35 轮又掉到 0.80早停逻辑可能在第 50 轮就停了而实际上第 120 轮能到 0.88。我的经验是把patience设在 30 到 40 之间或者直接设成 0 关闭早停手动观察曲线决定何时中断。1813 张图跑 200 轮yolov8s 在单张 4090 上也就一两个小时不算贵。4. 推理落地把权重文件变成可用的检测脚本4.1 单张图片推理与置信度阈值调节训练产物best.pt可以直接被ultralytics库加载推理。先跑单张图验证效果再写批量或视频逻辑。from ultralytics import YOLO model YOLO(fishing_train/exp_yolov8s/weights/best.pt) results model.predict( sourcetest_images/IMG_0001.jpg, conf0.25, iou0.45, imgsz640, saveTrue, save_txtTrue, projectfishing_infer, nametest_run ) for r in results: for box in r.boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) xyxy box.xyxy[0].tolist() print(f类别 {cls_id} 置信度 {conf:.2f} 坐标 {xyxy})参数里conf0.25是最常见的默认值但对捕鱼场景要单独调水面反光、鱼群密集时真目标本身置信度就低conf设太高会把鱼漏掉设太低又会让船桨、泡沫浮球这些长得像目标的东西混进来。我一般先用 0.25 跑一遍看误检多还是漏检多再往 0.15 或 0.4 调。iou0.45是 NMS 阈值两个重叠框 IoU 大于它时会保留置信度高的那个目标密集的鱼群场景建议降到 0.35避免相邻鱼被合并成一个框。4.2 处理视频流抽帧检测还是逐帧检测捕鱼识别真正实用的场景是监控视频常见做法有两种。逐帧检测最直接但 1080p 视频在 640 输入下yolov8s 大约能跑 30 FPS遇到快速移动的渔船会产生运动模糊检测框抖动明显。抽帧检测更省算力比如每秒抽 5 帧检测到目标后再回补帧率插值适合做统计类应用。import cv2 from ultralytics import YOLO model YOLO(fishing_train/exp_yolov8s/weights/best.pt) cap cv2.VideoCapture(fish_pond_camera.mp4) fps int(cap.get(cv2.CAP_PROP_FPS)) frame_interval max(1, fps // 5) # 每 5 帧取 1 帧检测 frame_idx 0 while cap.isOpened(): ret, frame cap.read() if not ret: break if frame_idx % frame_interval 0: results model.predict(frame, conf0.3, imgsz640, verboseFalse) for r in results: for box in r.boxes: x1, y1, x2, y2 map(int, box.xyxy[0].tolist()) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, f{int(box.cls[0])} {float(box.conf[0]):.2f}, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) frame_idx 1 cv2.imshow(fishing_detect, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码的要点是frame_interval的计算——用视频本身帧率除以 5确保每秒固定检测 5 帧而不是每 5 帧检测一次后者在高帧率视频里会浪费算力。检测结果直接画在原图的frame上verboseFalse是为了避免每帧都在终端刷日志跑起来会清爽很多。4.3 导出成 ONNX丢掉 PyTorch 环境也能跑部署到监控主机或边缘设备时对方环境不一定有 Python 和 PyTorch。常见做法是把best.pt导出成 ONNX 格式然后用 ONNX Runtime 推理体积更小、启动更快。from ultralytics import YOLO model YOLO(fishing_train/exp_yolov8s/weights/best.pt) model.export(formatonnx, imgsz640, halfTrue)halfTrue表示把模型权重转成 FP16 半精度显存占用减半推理速度提升约 20%在 NVIDIA 显卡上效果明显但 CPU 部署时部分算子不支持 FP16那就不加这个参数。导出后同目录下会出现best.onnx用 ONNX Runtime 或 TensorRT 加载即可。5. 捕鱼识别数据集训练的常见坑标注噪声、类别失衡与小目标漏检5.1 坑一txt 标签里出现超出类别数的 ID训练中断现象启动训练后报IndexError: index 5 is out of bounds for axis 0 with size 5或者日志里出现WARNING: ignoring corrupt label。原因标注工具导出的类别 ID 从 1 开始编号而 YOLO 要求从 0 开始或者合并多个数据集时部分标签的类别映射没有统一。这份 1813 张的数据集如果是多人合作标注这类问题几乎必然存在。解决写脚本全量扫描所有 txt 文件找出最大类别 ID 和data.yaml里的nc比对。不一致时写一个映射字典批量替换import os label_root yolo-fishing-dataset/labels mapping {1: 0, 2: 1, 3: 2, 4: 3} # 旧ID - 新ID for split in [train, val, test]: split_dir os.path.join(label_root, split) if not os.path.exists(split_dir): continue for file in os.listdir(split_dir): if not file.endswith(.txt): continue path os.path.join(split_dir, file) new_lines [] with open(path, r) as f: for line in f.readlines(): parts line.strip().split() old_id int(parts[0]) if old_id in mapping: parts[0] str(mapping[old_id]) new_lines.append( .join(parts)) else: new_lines.append(line.strip()) with open(path, w) as f: f.write(\n.join(new_lines) \n)这段代码的核心是mapping字典它定义了旧类别 ID 到新 ID 的映射关系。你要根据数据集的 README 或你自己的检查结果来定不是我写的这个数字。替换前建议先备份原标签目录替换后抽取 10 张图重新可视化核对一遍确保类别语义没被搞反。5.2 坑二类别间样本数量悬殊模型只认鱼不认网现象训练完测试鱼的 mAP 有 0.9渔网只有 0.3船和人的指标中间徘徊。原因1813 张图里如果 60% 只有鱼10% 只有渔网模型天然偏向学样本多的类别。捕鱼场景还有个特殊问题渔网是半透明纹理目标不像鱼有清晰轮廓特征本身难学。解决先统计每个类别的目标框数量用labels目录里的 txt 统计。如果差异超过 3 倍优先用数据增强把少数类别补起来而不是删多数类别的图。YOLOv8 自带的增强参数里hsv_h、hsv_s、hsv_v调低一点可以减少颜色扰动让模型更关注纹理特征flipud0.5对水面场景有用因为倒影会让目标上下翻转。实在不行再考虑合成数据把渔网的透明纹理贴到无标签的养殖池图片上手动造一批标签。5.3 坑三远距离小鱼目标太小imgsz640 直接漏检现象航拍视频里能看见鱼群泛起的波纹但模型一个框都不出。原因一条鱼在 4K 航拍图里可能只占 20x20 像素缩放到 640 输入后只剩 7x7 像素特征几乎丢失。YOLO 的下采样倍数最高到 32小目标经过多层卷积后信息所剩无几。解决首选把imgsz提到 1280 或 1536验证集 mAP 通常立刻回升两到三个点。代价是显存和推理时间翻倍8GB 显存跑 1280 会比较吃力。如果显存不够用yolov8s-p2模型结构它在原有 P3、P4、P5 基础上加了 P2 层专门保留高分辨率特征图上的小目标细节。另外训练时把mosaic0.5调低因为 mosaic 增强会把目标缩得更小本来就是小目标的数据集不适合过度压缩。5.4 坑四验证集和训练集泄露指标虚高现象训练时 mAP 直逼 0.95部署到新视频上直接崩检测框乱飞。原因数据集切分时没有按场景隔离。同一个养殖池、同一时间段拍的图被随机分到了训练集和验证集——模型见过几乎一样的图验证集指标当然好看但一到陌生水域就原形毕露。解决拿到数据集先看images/train和images/val的文件名规律。如果命名带拍摄时间或摄像头编号按拍摄场景切分而不是随机切分。如果原数据集已经切好且存在泄露只能重新划分把所有图按场景归类用 8:1:1 比例抽取场景分到三个集合保证同一个钓鱼点、同一段视频的画面只出现在一个集合里。重新划分后要重新生成data.yaml的路径。5.5 坑五过拟合的典型信号loss 曲线告诉你该收手了现象训练日志里训练集 loss 持续下降但验证集 mAP 在某个轮数后原地踏步甚至下滑。原因1813 张图对小模型来说够用但对网络容量偏大的模型就是杯水车薪。尤其用yolov8x或开了强增强后模型直接把训练图的背景纹理背下来。解决先确认是不是模型选太大换回yolov8s或yolov8n对比一轮然后检查增强参数mosaic、mixup这类强增强在小数据集上不是越猛越好mosaic0.3往往比mosaic1.0更稳。最后看epochs——小数据集跑 150 轮以上基本就是后期过拟合早停的 patience 设短一点没毛病。6. 让捕鱼模型更皮实自建验证集、类别重映射与模型迭代的进阶习惯模型训练完只是起点真正投入使用的模型要经过三轮验证才敢放到生产环境。第一轮用test目录跑一遍记录每张图的置信度分布第二轮找 10 段没参与训练的真实监控视频按时间段切片检测重点看有没有跨场景掉点第三轮把检测结果导出成 CSV统计每个类别的检出数量和平均置信度和真实捕捞记录做交叉比对。这套流程走下来哪些场景是你的模型软肋基本就摆在明面上了。做过几次数据集项目后我养成了一个习惯**每个数据集压缩包解压后第一件事不是训练而是写一个数据体检脚本。**脚本做三件事——扫描缺失标签、统计类别分布、可视化 10% 的图。跑完这三步我对这份数据集的理解比直接看压缩包强十倍也能判断 1813 张图的价值到底在哪如果分布均衡、标注干净这套数据集足够训练出一个能用的渔业监测模型如果分布不均那就按 5.2 节的方法先人工干预一轮再进训练流程。另一个值得做的进阶操作是类别重映射。假设交付的模型要同时做非法捕捞监管和养殖存量统计而数据集里只有fish、fishing_net两类不需要重新标数据。写一个映射脚本把fish拆成small_fish、medium_fish、large_fish三个子类——按标注框的面积占比做启发式判断面积小于整图的 1% 算 small大于 5% 算 large。这个做法不完美但能把一个二分类检测器拓展成五分类省下重新标注的时间。实现逻辑不复杂遍历所有 txt按w * h计算相对面积写入新的类别 ID 即可。最后提一句训练参数上的玄学同等数据量下yolov8s配合imgsz960比yolov8m配合imgsz640更值得优先尝试。捕鱼目标往往是中等尺寸960 的输入分辨率对特征保留的意义比模型宽度更大。如果你手头数据还想继续扩把视频每隔 10 帧截一张图用已训练的模型做伪标签人工挑出置信度高的框补进训练集——这是小数据集做大的标准路径代价低、见效快。希望这些踩坑经验能帮你少跑几轮无效训练。本文还有配套的精品资源点击获取