ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Python和YOLOv8的鱼类疾病检测系统:从环境配置到部署全流程

基于Python和YOLOv8的鱼类疾病检测系统:从环境配置到部署全流程 简介面向水产养殖智能化与深度学习开发者这份基于Python和YOLOv8的鱼类疾病检测系统源码可解决鱼类出血、眼部缺陷、鳍部缺陷、溃疡等多种常见病症的自动识别与实时预警问题。压缩包共24个文件包括4个Python脚本、19张示例图片及1份说明文档总大小2.26MB结构紧凑便于快速查阅。该资源已有90人学习/下载适合有一定Python基础、希望搭建鱼类疾病检测原型的初学者与研究者。读者可获得完整训练、验证、预测及界面展示代码支持图片、视频和摄像头实时检测结果自动保存并支持导出电子表格系统覆盖22类鱼类疾病并附有70余种改进思路。此外资源还提供完整训练教程与数据集用户可按需训练自定义模型以提高检测精度与速度助力水产病害防控。1. 用 Python 和 YOLOv8 做鱼类疾病检测到底解决什么问题养殖场里最怕的不是鱼价波动而是鱼病在池子里悄悄传开。白点病早期只有针尖大小的白点水霉病在鱼身上是一层灰白絮状物人工巡检时既靠经验又靠眼力一个养鱼池成千上万条鱼根本看不过来。于是这套“基于 Python 和 YOLOv8 的鱼类疾病检测系统”就指向一个很具体的场景用摄像头采集鱼缸或养殖池画面Python 做数据管理、接口和结果输出YOLOv8 负责对病灶区域进行实时检测框出鱼身上的疾病区域并给出类别和置信度。它能解决的并不是“包治百病”而是把肉眼巡检变为端到端的自动化检测。这套方案适合水产养殖技术人员、做毕业设计的本科生或研究生以及想搞懂 YOLOv8 从标注到部署全流程的新手工程师。下面讲的是能跑通、能复现的路径不含论文式推导。2. 拿到源码包先看什么目录结构、依赖文件与数据集组织方式不管源码包叫什么名字鱼病检测系统的核心资产永远是三样模型代码、权重文件、数据集标注。你把这个 zip 解开看到的无非是fish_data/、runs/、train.py、detect.py和环境文件requirements.txt。先不要急着训练按下面顺序检查这三块能省掉后面一大半排错时间。2.1 YOLOv8 环境配置其实就三步Python、ultralytics、PyTorch关于 yolov8 环境配置的求助比模型怎么训练多得多尤其是“ubuntu20.04 搭建 yolov8 环境 cpu 版本”这种很具体的问题。这一类源码包里的依赖文件通常只写着 ultralytics、torch、torchvision、pandas、opencv-python环境搭建并没有想象中复杂。先创建干净虚拟环境再装包是避免把系统 Python 搞乱的唯一稳做法。# 1. 创建 python3.10 虚拟环境并激活 conda create -n fish python3.10 -y conda activate fish # 2. 安装 PyTorchGPU 机器请按 PyTorch 官网给出的 CUDA 版本命令安装 pip install torch torchvision # 3. 安装 ultralytics 包装完会多出 yolo 命令 pip install ultralytics # 4. 验证安装 python -c from ultralytics import YOLO; print(ok)逻辑说明第一步的虚拟环境隔离很关键鱼病检测项目经常和别的 Python 项目共用一台机器不隔离的话依赖冲突会让你连import torch都报错。第二步注意千万别用pip install yolov8PyPI 上这个名字对应的是另一个老项目正确包名是ultralytics。第三步验证只看一件事能不能成功导入 YOLO 类。参数说明如果你是纯 CPU 机器第二步不要加任何 CUDA 参数直接pip install torch torchvision装的就是 CPU 版如果后续换了带 NVIDIA 显卡的机器再按 PyTorch 官方命令重装 GPU 版。CPU 版不是不能跑而是训练一个 epoch 可能比 GPU 慢十到二十倍用来验证脚本通路可以指望它训练完整模型会等到怀疑人生。提示验证安装成功的标准不是没有报错而是执行yolo predict modelyolov8n.pt source你的测试图片能正常出一张带框的图。如果这步报ImportError根因一般是 Python 版本低于 3.8或 torch 与 torchvision 版本不匹配。2.2 数据集目录images 和 labels 谁放哪以及鱼病样本从哪来很多 YOLOv8 新手的第一个坑不是模型而是目录。检测系统的标注文件按图像尺寸归一化后的坐标存在 txt 里训练程序按图像路径找 labels 路径如果文件名对不上训练时这张图的标注就被忽略模型相当于白看这张图。常见的数据集目录长这样fish_data/ ├── images/ │ ├── train/ │ │ ├── img_0001.jpg │ │ └── img_0002.jpg │ └── val/ │ ├── img_0100.jpg │ └── img_0101.jpg └── labels/ ├── train/ │ ├── img_0001.txt │ └── img_0002.txt └── val/ ├── img_0100.txt └── img_0101.txt逻辑说明images/train/img_0001.jpg必须对应labels/train/img_0001.txt文件名一字不差。txt 里每行是一个目标格式是class x_center y_center width height所有坐标归一化到 0 到 1 之间。训练时 YOLOv8 会自己根据图片路径找同名 txt找不到就直接跳过这张图的监督信号。鱼病样本从哪来是个现实问题。常见做法是找养殖场或水族箱摄像头抓帧一条鱼拍多个角度只挑病灶清晰的画面如果只有几十张原始图可以靠翻转、旋转、亮度变化扩到几百张但要注意类别平衡。比较稳的初始类别是三种常见病加一个健康对照白点病、水霉病、烂鳍病、健康。类别超过六个每类样本量会迅速稀释模型就不太容易收敛了。注意没有病灶的负样本也要留一部分。鱼缸里的气泡、水草、玻璃划痕在模型眼里很像白点病只有让模型见过大量“没有病”的图片它才不会把背景误检成病灶。2.3 data.yaml 与类别定义names 顺序决定了输出顺序YOLOv8 训练的第一步是读一个 data.yaml里面定义了数据集路径和类别名。类别名的顺序不能乱写因为它直接映射到模型输出的整数序号。比如你在 yaml 里把 healthy 写在 0训练结束后模型输出一张白点病照片时返回 class1如果推理脚本把 names 顺序改了class1 就代表别的病这个坑非常隐蔽。常规的鱼病 data.yaml 长这样path: ./fish_data # 数据集根目录 train: images/train val: images/val names: 0: healthy 1: white_spot 2: saprolegnia 3: fin_rot逻辑说明path建议用相对路径指向 yaml 文件所在位置的相对关系。很多源码包习惯写绝对路径换台电脑就全部失效这是最容易踩的迁移坑。names的每个 key 必须从 0 开始连续编号不能跳号。类别定义决定了模型能输出什么我给一个小型鱼病项目做时初期类别就四类ID标签病名典型症状0healthy健康对照无病灶1white_spot白点病体表和鳍上白色小点2saprolegnia水霉病灰白棉絮状覆盖物3fin_rot烂鳍病鳍边缘溃烂缺损这四类覆盖了淡水养殖里最常暴发的三种病视觉特征差异大模型学起来比较容易。如果一上来就上十几种病很多病的表现互相重叠标注一致性又会崩模型效果必然翻车。3. 从标注到训练的完整流程每条命令的参数都是为生产服务的看懂源码包是别人的结果真正落地是自己把全流程跑一遍。从 LabelMe 标注到数据集转换再到训练命令和损失曲线每一步都有参数值得细看。下面这条链路我基本每次做鱼病项目都会走一遍。3.1 LabelMe 标注转 YOLOv8 格式转换脚本与四个边界坑LabelMe 是标注鱼病最顺手的工具之一但它默认保存的是 JSON 多边形YOLOv8 要的是 txt 归一化框。所以源码包里通常都有一个转换脚本核心逻辑是把多边形的外接矩形转成 YOLO 格式。我自己惯用的转换脚本如下import json import os class_dict { healthy: 0, white_spot: 1, saprolegnia: 2, fin_rot: 3, } def labelme_json_to_yolo(json_path, out_dir): with open(json_path, r, encodingutf-8) as fp: data json.load(fp) w, h data[imageWidth], data[imageHeight] lines [] for shape in data[shapes]: label shape[label] if label not in class_dict: continue # LabelMe 的 points 是多边形顶点取外接矩形作为检测框 xs [p[0] for p in shape[points]] ys [p[1] for p in shape[points]] x_min, x_max, y_min, y_max min(xs), max(xs), min(ys), max(ys) bw, bh x_max - x_min, y_max - y_min if bw 8 or bh 8: # 小于 8 像素的框在 640 输入下没意义 continue x_center (x_min bw / 2) / w y_center (y_min bh / 2) / h width bw / w height bh / h lines.append(f{class_dict[label]} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) out_txt os.path.join(out_dir, os.path.basename(json_path).replace(.json, .txt)) with open(out_txt, w, encodingutf-8) as fp: fp.write(\n.join(lines))逻辑说明脚本先读 JSON 里的图像宽高然后对每个 shape 取多边形的外接矩形再按图像宽高归一化。关键点是循环维度是data[shapes]不是按文件处理一次就完事因为一张鱼病图上可能同时框了多条鱼或多个病灶。参数说明bw 8这个阈值我按 imgsz640 来设病灶在图上连 8 个像素都不到时模型基本学不到有效特征留着反而会把标签噪声带进训练。如果你把训练尺寸改成 1280这个阈值可以放宽到 12。转换脚本有四个常见边界坑血泪经验在这里第一个坑是中文文件名。LabelMe 默认按图片名存 JSON如果原图叫“白点病_001.jpg”OpenCV 在部分 Linux 环境下读取会乱码。建议先统一重命名为img_0001.jpg再标注。第二个坑是标注时用了折线或圆形工具。LabelMe 的 shape_type 可以是 polygon、rectangle、circlecircle 的 points 只有两个点外接矩形会算出一条直径做对角线框不贴病灶。标注时尽量全部使用 polygon 或 rectangle。第三个坑是类别映射不一致。标注里的“白点病”和 yaml 里的white_spot必须映射到同一个 ID脚本里的 class_dict 一旦错了训练全程都在错而且 mAP 看起来还挺高属于特别迷惑人的坑。第四个坑是太小的框没过滤。鱼身上早期病灶经常只有几个像素不设下限过滤转换出来的 txt 会出现极端高宽比训练时 anchor 匹配不到损失曲线会异常抖动。3.2 开始训练先搞懂几个参数的含义再碰键盘数据集整理好之后训练命令其实不复杂YOLOv8 把大量细节都收敛到了参数里。问题在于很多人直接拿默认值跑跑崩了也不知道该调谁。我给一张 GTX 1660Ti 6GB 显卡做鱼病检测时常用的训练命令是yolo train \ modelyolov8n.pt \ datafish_data/data.yaml \ epochs120 \ imgsz640 \ batch16 \ patience20 \ device0 \ workers2 \ project./runs \ namefish_disease逻辑说明modelyolov8n.pt用的是 YOLOv8 的 nano 版预训练权重。鱼病检测的目标是小病灶、少样本场景nano 版在速度和精度上的平衡最好没必要一上来就上 yolov8s 或 yolov8mCOCO 上比 nano 高的那几个点在鱼病这种小数据集上根本体现不出来只会把训练时间翻倍。参数说明整理成表方便对照自己的机器参数含义我的经验值epochs全量数据迭代次数100 到 150imgsz输入图像尺寸病灶极小就 1280常规 640batch一次喂给模型的图片数6GB 显存用 168GB 用 32patience验证集不再提升时早停的 epoch 数15 到 20device0 表示第一块 GPUcpu 表示用 CPU有 GPU 就指定 0workers数据加载进程数Windows 建议 0 或 1参数里最容易踩坑的是workers。Windows 下如果设 8训练开始时会直接报 DataLoader worker 进程相关错误这不是代码问题是 Windows 多进程读取的兼容性问题改成workers0立刻就好。 Linux 服务器则可以设成 4 或 8能明显加快数据读取。还有一点训练时如果不想让模型下载权重提前把 yolov8n.pt 下载好放到项目根目录然后model./yolov8n.pt。离线环境训练时这是最常见的卡住点不是你的代码有问题是模型在联网下载。3.3 yolov8 画损失函数曲线图box、cls、dfl 三条曲线到底在看什么训练结束之后源码包里会多出runs/fish_disease/results.png这是 ultralytics 自动生成的指标图。但新手只看 mAP 不看损失会漏掉很多问题。我习惯自己把三条损失曲线单独拉出来看训练日志在runs/fish_disease/results.csv里画图脚本如下import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/fish_disease/results.csv) df[epoch] df[epoch].astype(int) fig, axes plt.subplots(1, 3, figsize(15, 4)) for i, col in enumerate([train/box_loss, train/cls_loss, train/dfl_loss]): axes[i].plot(df[epoch], df[col], labeltrain) axes[i].plot(df[epoch], df[val/ col.split(/, 1)[1]], labelval) axes[i].set_title(col.split(/)[1]) axes[i].legend() plt.tight_layout() plt.savefig(fish_loss_curve.png, dpi200)逻辑说明代码读取训练时自动记录的 CSV把 train 和 val 的 box_loss、cls_loss、dfl_loss 分别画在三张子图里。col.split(/, 1)[1]是把train/box_loss截成box_loss再拼出val/box_loss这一列这样画出来的图能直接对比训练集和验证集。这三条损失的含义不一样box_loss 管的是框得准不准cls_loss 管的是类别分得对不对dfl_loss 管的是边框分布的质量。鱼病检测里如果 box_loss 一直降不下来先别调学习率回去看标注框是不是只框了鱼的一段身体如果 cls_loss 高多半是类别不平衡白点病样本特别多、烂鳍病只有几十张模型的分类头会被带偏。判断收敛的规律也很简单三条 train_loss 持续下降且 val_loss 同步下降说明模型在正常学train_loss 下降但 val_loss 拉平甚至翘尾就是过拟合了该早停三条损失在某个 epoch 突然跳高通常不是学习率问题而是数据里有脏标签或空 txt。先看损失曲线再决定要不要继续训练这比盲调参数省时间得多。4. 避坑与排查鱼病检测里反复翻车的五个地方鱼病检测场景数据量小、标注标准不统一、负样本稀缺这三个问题叠加起来会让训练过程出现各种看似玄学的现象。下面五条按出现频率排每条都按现象、原因、解决三部分展开都是实际能复现的排查路径。4.1 现象训练 loss 前面正常跑到中途变 NaN训练二十个 epoch 后 loss 突然变成nan然后训练中断或者 loss 打印出来是一个-nan(ind)。这种情况在小数据集上特别多见。原因绝大多数是标注数据里有越界框或空标签。LabelMe 转换成 YOLO 格式时如果坐标没有做边界裁剪物体贴近图像边缘时归一化后的 x_center 或 width 会超过 1。YOLOv8 的损失函数遇到这种越界值梯度直接发散。另一种情况是某个 txt 文件是空的模型读到空标签时某些增强模式下也会不稳定。解决训练前先扫一遍标签文件把空标签和越界框清理掉。我一般会跑下面这个检查脚本cd fish_data/labels/train python - EOF from pathlib import Path import numpy as np for p in Path(.).glob(*.txt): try: arr np.loadtxt(p, ndmin2) except ValueError: print(empty:, p) continue if arr.size 0: print(empty:, p) elif (arr[:, 1:] 1).any() or (arr[:, 1:] 0).any(): print(out of range:, p) EOF这个脚本会列出所有空标签和坐标越界的文件找到后回到标注软件里重新修正或者直接删掉这几张问题图。越界框比空标签更隐蔽因为只有少数图出错loss 要到几十个 epoch 之后才爆排查起来很费时间。4.2 现象train_loss 正常下降val_loss 后期突然翘尾训练到一半train_loss 稳中有降但 val_loss 降到某个值后开始反弹mAP0.5 也跟着往下掉。这是典型的过拟合鱼病小数据集上非常常见。原因数据量太少模型把训练集里的鱼个体背下来了而不是学到“病灶长什么样”。尤其是我见过有人把同一批图片既做增强前的训练又做增强后的验证数据泄漏会让 val_loss 彻底失真。解决先按鱼个体切分数据集同一条鱼的不同角度照片只能进 train 或只能进 val不能两头都占。训练时把数据增强打开常见的组合是mosaic1.0 fliplr0.5 hsv_v0.4让模型看到更多变化。最后把 patience 设到 15 到 20不要让模型硬跑完所有 epochval_loss 翘尾就该停。4.3 现象同一份数据训练两次mAP 差 0.1 以上下午跑一次 mAP0.5 是 0.82第二天同样代码同样的数据跑出来只有 0.71很多人第一反应以为是源码有问题或者显卡坏了。原因数据量小的时候随机划分训练集和验证集会产生很大的波动。鱼病数据可能只有几百张某一次划分把白点病的样本大部分分到了验证集训练集里这类病只剩几十张模型学不好mAP 自然掉。解决不要在源码包里找玄学固定随机种子并做多次划分取平均 mAP。YOLOv8 训练时加seed0然后用 Stratified K-Fold 按类别比例切分数据分别训练三到五次最终指标取平均值。单独一次的 mAP 在鱼病这种小数据集上没有可信度这个教训我吃了很多次。4.4 现象验证集 mAP 挺高实拍视频把水草和气泡框成白点病这是鱼病检测系统走到真实场景时最典型的翻车。验证集图片都是你亲手挑的干净特写模型表现得很好拿到养殖池真实画面水草边缘、气泡反光、玻璃上的一圈水渍全被框了出来。原因训练集负样本太少背景特异性不足。模型学到的可能不是“白点病的白点”而是“画面里有一小撮浅色圆形区域就报警”。鱼缸里的气泡和水草在像素特征上和白点病早期病灶高度相似。解决专门采集负样本空鱼缸、水草浓密、气泡很多、水面反光的画面都要拍放在images/train下labels 对应建空 txt 文件。YOLOv8 支持空标签图片作为背景样本参与训练。另外推理时把conf阈值从默认 0.25 提到 0.35 以上宁愿漏检几条鱼也不要让系统每隔几秒误报一次否则工人很快就放弃看告警了。4.5 现象用官方预训练权重微调loss 下降非常慢甚至不动很多人拿到鱼病数据直接modelyolov8s.pt开始训跑了五十个 epochbox_loss 几乎不变cls_loss 忽上忽下。这时候最容易怀疑数据集有问题其实问题在迁移策略上。原因COCO 预训练权重学的是人、车、猫狗这些通用物体鱼病病灶只有针尖大小高层语义特征完全不通用。再加上默认学习率 lr00.01 对迁移场景偏大模型在最开始几个 epoch 会震荡看起来就像没在学。解决先用更小的模型和冻结层做冷启动。以yolov8n.pt为基础冻结前 10 层卷积只训练检测头让模型先认病灶位置和类别命令如下yolo train modelyolov8n.pt datafish_data/data.yaml freeze10 epochs30 lr00.005 imgsz640 batch16等这 30 个 epoch 跑完loss 已经降到一个稳定平台再去掉freeze10用lr00.01完整训练 80 到 100 个 epoch。这个两步走的方法基本上能解决小样本鱼病训练的收敛问题。5. 推理、部署与验证让模型在产线里真正干活训练完成后runs/fish_disease/weights/best.pt就是整个源码包最值钱的产物。但检测系统能不能用不取决于训练指标取决于推理脚本稳不稳、部署路径顺不顺。5.1 用 best.pt 写自己的推理脚本源码包里如果只给了一个 CLI 式推理示例落地时往往不够用。我习惯封装成一个 Python 脚本方便后面接管理后端from ultralytics import YOLO model YOLO(runs/fish_disease/weights/best.pt) results model.predict( source./img/, conf0.30, iou0.45, imgsz640, saveTrue, save_txtTrue, save_confTrue, ) for res in results: boxes res.boxes if boxes is not None: print(res.path, boxes.cls.tolist(), boxes.conf.tolist())逻辑说明预测支持单张图、整个目录、视频流source指定输入路径。saveTrue会输出带框的可视化结果图save_txtTrue输出检测框坐标save_confTrue会在文本里附带置信度方便后面判读。参数说明conf0.30比默认的 0.25 略高这是生产场景的设置鱼病误报会消耗管理员的耐心宁可漏检不要刷屏。imgsz必须和训练时的尺寸一致否则框的位置会偏移这是新手最容易忽略的部署坑。5.2 验证模型的三个硬指标和一句实话跑完推理后要回到验证集用yolo val看正式指标yolo val modelruns/fish_disease/weights/best.pt datafish_data/data.yaml输出会给出 precision、recall、mAP0.5、mAP0.5:0.95。鱼病场景我的参考线如下指标参考线说明precision0.85 以上误报率低recall0.80 以上漏检率低mAP0.50.75 以上综合检测能力mAP0.5:0.950.45 以上框质量要求高说句实话这些指标在人工挑过的验证集上只有参考意义真正要验收拿手机或摄像头到鱼缸边走一圈统计二十张真实拍摄画面的召回率。我见过太多项目 mAP 很好看一到现场就露馅原因就是验证集和真实场景分布不一致。如果要把模型接到 RK3588 这类边缘设备上同样的 best.pt 先导出 ONNXmodel.export(formatonnx, imgsz640)再用 ONNX Runtime 做推理帧率和内存占用会比直接跑 PyTorch 好很多。整个项目做下来我养成的唯一习惯就是先跑最小训练、看损失曲线再去调数据集。鱼病检测不是黑匣子能把两三百张鱼病图跑通并部署到现场就已经比人工巡检快一个数量级了。希望帮到你。本文还有配套的精品资源点击获取
返回列表