
简介面向YOLO目标检测学习者和渔业病害研究人员这份数据集围绕鱼体表疾病图像目标检测场景整理而成按YOLOv5标准格式划分了训练集约720张、验证集约100张、测试集约100张覆盖细菌性疾病、真菌性疾病、健康鱼、寄生虫病、白尾病五类目标适合作为课程设计、竞赛练习或模型迭代验证的输入数据。图片与TXT标注一一对应类别class文件同步提供拿到后无需额外转换即可直接开始模型训练。资源包共1831个文件主体为914张JPG图片与915个TXT标注另附一个可视化Python脚本无需修改参数即可直接运行随机传入一张图片即可绘制预测框并保存在当前目录方便快速检查标注质量压缩包为7z格式整体仅28.65MB便于携带共享。目前已有750人学习浏览可作为鱼类疾病检测入门练习或YOLOv5改进实验的基础数据。1. 鱼病检测数据集为什么我建议你直接拿它跑 YOLO做水产养殖病害检测的同行应该都有体会鱼体表面的白点、烂尾、赤皮这些症状靠人工肉眼去看既慢又容易漏拍回来的图片堆在硬盘里真正能用来训练目标检测模型的却少得可怜。这份“YOLO 数据集鱼身上疾病图像目标检测”资源核心就是一套已经标注并划分好的目标检测数据集附带类别 class 文件和配套的数据可视化脚本。也就是说你拿到的不是一堆零散原图而是可以直接丢进 YOLO 训练流程的成品数据包省掉最磨人的整理和划分环节。它的价值在于解决两个痛点一是数据集已经按训练、验证、测试划分完毕目录结构符合 YOLO 的读取习惯不用自己写脚本重排二是每个类别都有对应的 class 文件类别 ID 和名称的对应关系是确定的不会出现训练时标签错位这种坑。适合谁用刚接触 YOLO、手头没有合适数据集的初学者以及做鱼类疾病检测相关项目、想快速跑通 baseline 的从业者。这篇笔记我会把数据集结构、训练配置、可视化脚本用法以及实际踩过的坑逐一说清楚。2. 数据集内部结构目录划分逻辑与类别文件配对2.1 目录结构images 和 labels 怎么对应拿到资源后第一步不是急着训练而是先把目录结构看清楚。我解压后看到的组织方式是标准的 YOLO 风格根目录下分images和labels两个大目录各自下面再按train、val、test三个子目录分开。图片是.jpg标签是.txt文件名一一对应。这种组织方式的优点在于Ultralytics YOLO 训练时只需要在 YAML 配置里指定train、val、test的路径它会自动去对应目录找同名图片和标签文件不需要额外写数据加载逻辑。dataset/ ├── images/ │ ├── train/ # 训练图片 │ ├── val/ # 验证图片 │ └── test/ # 测试图片 ├── labels/ │ ├── train/ # 训练标签txt格式 │ ├── val/ # 验证标签 │ └── test/ # 测试标签 └── class.txt # 类别名称文件提示检查标签文件是否缺失最直接的方式是对比两个目录下的文件名我一般用diff (ls images/train) (ls labels/train)快速筛查如果输出为空说明配对完整。这个步骤虽然简单但能提前暴露很多后面会炸雷的问题。2.2 class 文件与标签 ID 的对齐逻辑class.txt或者classes.txt这个文件决定了模型输出层的类别数量每一行是一个类别名称行号从 0 开始就是该类别的 ID。YOLO 标签文件里每一行是“类别ID 中心点x 中心点y 宽度 高度”前四列归一化到 0~1类别 ID 必须是整数且不能超过 class 文件行数减一。这份资源的类别是鱼身上的不同疾病症状class 文件里已经排好了顺序训练时 YAML 里names列表的顺序必须和 class 文件行序保持一致否则训练出来的模型输出的类别就是乱的推理时会出现“白点病显示成烂尾”这种张冠李戴的情况。验证标注是否合法我通常直接读一个标签文件看数值范围# 检查标签文件格式是否正确 with open(labels/train/某个文件名.txt, r) as f: for line in f.readlines(): parts line.strip().split() assert len(parts) 5, f列数不对: {line} cls_id int(parts[0]) assert 0 cls_id 类别总数, f类别ID越界: {cls_id} coords [float(x) for x in parts[1:]] assert all(0 x 1 for x in coords), f坐标未归一化: {coords} print(格式检查通过)这段脚本的逻辑是逐行解析标签文件先确认每行有 5 个字段再检查类别 ID 是否在合法范围最后确保四个坐标值都落在 0 到 1 之间。三个检查分别对应三类典型错误列数不对通常是标注工具导出设置问题类别 ID 越界是 class 文件没配对坐标越界则说明归一化出了问题需要回头检查标注工具的设置。2.3 划分比例与数据量评估数据集的划分情况直接决定了训练效果的可靠性。训练集和验证集大概是 8:1 或者 7:2 的体量测试集单独留了一部分。我自己的习惯是拿到数据集后先统计每个类别的样本数重点看有没有类别严重不均衡。如果某个疾病的样本量只有别的类别的十分之一模型大概率会偏向样本多的类别推理时容易漏检少样本的类别。# 统计每个类别的样本数量 cat labels/train/*.txt | awk {print $1} | sort | uniq -c | sort -rn # 结果示例类别ID - 样本数 # 450 0 # 280 1 # 120 2 # 35 3这个命令把训练集所有标签文件的第一个字段类别 ID取出来排序后统计每个 ID 出现的次数。如果发现某个罕见疾病的样本数量很少训练时可以在损失函数里对对应类别提高权重或者对包含该类别的图片做离线增强。数据量本身不算特别大作为小型目标检测任务的起始数据是够用的但如果要达到生产级精度建议后续自己补充数据再微调。3. 训练配置与 class 文件把 YAML 配齐再动手3.1 环境准备Ultralytics YOLO 的安装与验证训练这套数据集最常见的做法是用 Ultralytics YOLO 框架。安装完成后先跑一段代码确认环境可用能省去后续半天的排错时间。CUDA 版本和 PyTorch 版本不匹配是最常见的问题建议直接装官方预编译的 torch 版本。# 创建虚拟环境并安装依赖 python -m venv yolo_env source yolo_env/bin/activate # Windows 下是 yolo_env\\Scripts\\activate pip install ultralytics torch torchvision # 验证安装和 GPU 可用性 python -c import torch; print(torch.cuda.is_available()); print(torch.__version__)如果torch.cuda.is_available()返回False大概率是 PyTorch 安装成了 CPU 版本需要去 PyTorch 官网找对应 CUDA 版本的安装命令重新装。数据集本身没有环境要求但训练速度和显存占用和 GPU 强相关没有 GPU 的话只能用小 batch size 硬跑时间会拉长很多。3.2 数据集 YAML 配置路径与类别名配对训练前需要写一个 YAML 文件告诉 YOLO 数据在哪里、类别是什么。这个文件是训练流程的“总入口”路径写错或者类别名对不上训练会直接报错或者训练出一个无法使用的模型。我把资源配置好后按下面的模板改路径和类别名# fish_disease.yaml path: /绝对路径/fish_disease_dataset # 数据集根目录 train: images/train # 训练图片相对路径 val: images/val # 验证图片相对路径 test: images/test # 测试图片相对路径 nc: 4 # 类别数量必须与class.txt行数一致 names: [white_spot, fin_rot, red_skin, gill_rot] # 类别名称顺序必须与class.txt一致注意path字段建议写绝对路径因为相对路径在换机器训练时容易出问题。nc和names的顺序一旦写错模型训练时标签解析就会错位这是新手最容易翻车的地方。路径这块Ultralytics 对相对路径的解析规则是相对当前工作目录如果你在项目根目录下启动训练train: images/train尚可但建议还是用绝对路径直接把path指向数据集根目录后面train和val写相对路径这样最不容易出错。类别名称建议看 class 文件里实际是怎么写的不要凭猜测填因为不同标注工具导出的 class 文件可能是中文名、英文名或者带下划线的缩写。3.3 开始训练关键参数的选择依据配置完成后执行训练命令核心参数包括model、data、epochs、imgsz、batch和device。我第一次跑的时候直接把epochs设成了 300结果发现前 50 个 epoch 验证集指标基本就不动了纯属浪费算力。后来学乖了先跑 100 个 epoch 看趋势确认 loss 下降和 mAP 变化情况再决定是否拉长。yolo train \ modelyolov8n.pt \ datafish_disease.yaml \ epochs100 \ imgsz640 \ batch16 \ device0 \ projectfish_disease_runs \ nameexp_default参数逻辑说明modelyolov8n.pt是官方预训练权重用它的 COCO 预训练参数做初始化迁移学习下收敛速度比从零训练快得多imgsz640是 YOLOv8 的默认输入尺寸如果鱼体目标在图片里占比很小可以试 960 甚至 1280但显存占用会翻倍batch16要根据显存调整16GB 显存跑 16 没问题8GB 建议降到 8device0明确指定第一块 GPU避免多卡环境踩到不该用的设备project和name控制训练结果的输出目录我习惯每次实验用不同的name区分方便后面对比模型。训练结束后输出目录下会有weights/best.pt和weights/last.pt。best.pt是验证集上指标最高的权重做推理和部署永远用best.pt这个习惯要养成不要图省事用last.pt。4. 训练验证与可视化脚本混淆矩阵和热力图怎么读4.1 召回率与精确率的平衡点训练完成后results.png里能看到精确率、召回率和 mAP50 的曲线。鱼病检测场景里我更关注召回率因为漏检一条病鱼意味着它可能继续传染整塘鱼损失比误报更大。模型训练停止的时机不是看训练集 loss而是看验证集 mAP 是否还有上升趋势。如果验证集 loss 连续 20 个 epoch 不降反升说明开始过拟合了这时候就该停下来用best.pt。4.2 混淆矩阵可视化发现类别混淆的利器数据可视化脚本在这个资源里的作用是帮你看清楚模型在哪些类别上容易出错。脚本会读取验证集的预测结果和真实标签生成混淆矩阵热力图。读混淆矩阵有个关键技巧看非对角线上的数值大的格子那才是真正要解决的问题。# 使用 ultralytics 自带的混淆矩阵可视化基于训练产生的 results from ultralytics.utils.metrics import ConfusionMatrix import torch # 加载训练好的模型并在验证集上推理 model torch.load(fish_disease_runs/exp_default/weights/best.pt) # 实际项目中通常直接用 model.predict 收集预测结果后交给混淆矩阵工具处理如果发现“white_spot”和“gill_rot”两个类别互相混淆也就是大量 gill_rot 样本被预测成 white_spot原因通常是图片拍摄角度相似或标注框覆盖范围重叠。解决办法有两个方向一是检查标注框边界是否贴合目标如果框太大把周围健康组织也包进去了模型学到的特征就容易被带偏二是考虑是否需要合并类别如果两个病在视觉上确实难以区分合并成一个“混合感染”类别往往比强行让模型区分更实际。4.3 可视化脚本的定制化修改资源自带的可视化脚本我一般会做两处定制改类别名称映射和调整热力图配色。默认的 matplotlib 配色在深色背景下对比度不够改成YlOrRd色系后混淆程度高的格子一眼就能看出来。这些脚本的核心价值在于它们把训练结果转成了人可以直接理解的图表不用自己现写解析逻辑。5. 常见问题排查五个训练翻车的真实记录5.1 标签错位模型训练不收敛现象训练 loss 一直在高位震荡验证集 mAP 始终在 0.1 以下推理时预测框位置全乱。原因数据集 YAML 中的names顺序和class.txt不一致导致同一个标签 ID 被解析成不同类别模型学到的特征根本对不上。解决逐行比对 YAML 里names列表和 class 文件内容确保顺序完全一致。我后来写了一个脚本自动按行读取 class 文件生成 YAML 的names字段从源头杜绝手写出错。5.2 图片与标签失配训练时报错找不到标签现象训练过程中频繁报 “Assertion failed: labels not found” 或者直接跳过一批数据。原因部分图片在images/目录下存在但labels/目录下没有对应的 txt 文件或者图片和标签文件名前缀不一致比如图片叫fish_001.jpg标签叫fish_01.txt。解决写一个脚本遍历两边的文件名做差集比对把缺失的文件列表打印出来再用脚本批量补齐或移除问题图片。这个问题在从网上下载数据集时特别常见因为有些资源打包时把标签文件漏了。5.3 类别不均衡模型对少样本类别几乎不检现象验证集上常见类别精确率和召回率都还不错但罕见类别几乎全漏混淆矩阵里那行全是 0。原因数据集中不同疾病样本数差距过大模型在训练时对多数类别的梯度贡献远大于少数类别导致决策边界完全偏向多数类。解决最简单的方式是给少样本类别做离线增强比如对包含该类别的图片做随机旋转 90 度、水平翻转、亮度抖动先把样本数补齐到多数类别的 40% 以上再训练。另一个办法是在损失函数里给对应类别加权重但实操中调权重参数比较玄学不如直接扩数据稳妥。5.4 BN 崩溃训练中出现 NaN 损失现象训练在第几百个 iteration 时 loss 突然变成 NaN之后所有指标全部消失。原因batch size 太小导致 BatchNorm 层的统计量不稳定或者学习率设置过高导致梯度爆炸。尤其在数据集中某些小目标样本的梯度波动特别大时更容易触发。解决先把学习率从默认的 0.01 降到 0.001再把 batch size 从 4 提升到 8 以上。如果显存不够可以在 YAML 里把imgsz从 640 降到 512这样 batch size 还能再往上加。BN 崩溃这个问题在处理小数据集小目标时特别容易触发不要硬扛直接调参最快。5.5 模型只学背景不学目标预测框围绕全图乱转现象推理时预测框要么覆盖整个画面要么在背景区域乱飘看起来完全没学到目标语义。原因最常见的两个来源——标签坐标没有归一化数值范围是像素坐标而非 0~1或者图片尺寸和标注尺寸不一致。模型读到的“目标”位置全是错的自然学不到真正的位置特征。解决用前面提到的标签检查脚本跑一遍全部训练集把坐标值范围异常的标签全找出来。如果发现坐标是像素值需要把整个标签文件重新归一化一遍按每张图片的实际宽高做除法后再写回文件。6. 一个进阶技巧用快速验证脚本在十分钟内判断数据集质量拿到一个数据集最怕的是投入大量时间训练完后发现数据本身有问题。我后来强制自己每次训练前先跑一遍快速验证脚本加载预训练模型在数据集上做小规模验证推理把检测框画出来看是否贴合目标。这个动作能把 5.2 和 5.5 里提到的标签错位、坐标异常这类问题在训练前暴露出来。# quick_check.py用预训练模型快速验证数据集标注质量 from ultralytics import YOLO import cv2 import os # 加载一个已经训练好的鱼病模型或任意可用的检测模型 model YOLO(fish_disease_runs/exp_default/weights/best.pt) # 从验证集中任选若干图片做推理 val_images dataset/images/val for img_name in os.listdir(val_images)[:5]: img_path os.path.join(val_images, img_name) results model.predict(img_path, conf0.25, imgsz640) # 画框并保存可视化结果 annotated results[0].plot() save_path fquick_check_{img_name} cv2.imwrite(save_path, annotated) print(f检测到 {len(results[0].boxes)} 个目标结果保存为 {save_path})这段脚本的逻辑是从验证集里取前 5 张图片用已训练好的模型做推理把预测框画在原图上保存。检查时重点看两类情况一是框里是否真的包住了鱼身上的病灶如果框的位置和病灶完全不搭问题出在标注坐标二是类别标签是否和视觉表现一致比如框住的是白点区域却标成了“烂尾”说明类别 ID 错位。整个过程跑下来不到十分钟但能省下后面几个小时的无效训练。脚本里的参数说明conf0.25是置信度阈值过滤掉低置信度的预测框如果图片上目标很多可以调低到 0.1 看到更多候选imgsz640要和训练时保持一致否则推理精度会受影响results[0].plot()返回的是画完框的 BGR 图像数组cv2.imwrite直接保存为图片文件。从那以后我每次拿到新数据集都强制自己先跑一遍这个快速验证流程再开始训练。这个习惯帮我筛掉过两次有问题的数据包一次是标签坐标全是像素值没归一化另一次是 class 文件里类别顺序和标注工具导出顺序不一致。数据这关把住了后面的训练才有意义。这份资源本身数据组织是规范的但你换上自己的数据集时这套验证流程一样用得上的希望帮到你。本文还有配套的精品资源点击获取