简介:基于Python与YOLOv8开发的鱼类疾病检测系统源码,面向水产养殖从业者、计算机视觉学习者和算法工程师,用于对多种鱼类常见病症(如出血、眼部缺陷、鳍部缺陷、溃疡)进行自动化识别与实时监测。系统支持图片、视频及摄像头多种输入方式,检测结果可自动保存并导出为Excel,同时内置Web前端展示页面,便于现场或远程查看。项目还提供了模型训练相关说明,并集成70余种YOLOv8改进点,方便用户按需优化识别精度与速度。
压缩包共24个文件,主体为19张PNG界面与效果图、4个Python脚本(分别承担训练、验证、预测和UI交互)以及1份Markdown说明文档,整体大小约2.26MB,结构轻量但覆盖完整流程。目前已有90人学习浏览,适合希望快速上手目标检测在水产场景应用的开发者。通过阅读源码与说明,可掌握数据准备、模型训练、结果导出和界面定制等环节的具体实现,为后续自建鱼类病害检测系统提供可直接参考的工程范式。
1. 鱼类疾病检测的坑,这套源码包怎么替你填
水产养殖里鱼病发现晚了,损失按整池算。传统靠老师傅肉眼巡检,等看到体表出血、烂鳍,往往已经传染开。基于 Python 和 YOLOv8 的鱼类疾病检测系统,做的是"机器替人盯":摄像头对着鱼池,模型实时框出病鱼病灶,区分出血、眼部缺陷、鳍部缺陷、溃疡等症状,自动存图、导出 Excel、Web 界面展示。源码包把训练、验证、预测、展示四条链路都备齐了:train.py 训练、val.py 出指标、predict.py 推断、ui.py 拉界面,支持 22 类鱼类疾病识别,还带 70+ 种 YOLOv8 改进模块。适合养殖场技术员、做目标检测毕设的学生、想在 YOLOv8 上做垂直场景落地的开发者。它不是原理讲义,是能直接改、直接训的工程包。
2. 先把环境踩平:Python 与 YOLOv8 的安装配置
环境装不对,后面跑训练全是玄学报错。这一章把 Python 版本、PyTorch、CUDA 和 ultralytics 的搭配一次讲清楚,照着抄能少折腾半天。
2.1 版本选型:Python 3.8 与 YOLOv8 的搭配逻辑
YOLOv8 的核心是 ultralytics 库,底层计算靠 PyTorch。版本搭配上我习惯 Python 优先 3.8,不是越高越好,而是 3.8 的编译型依赖兼容性最稳。之前我在 Python 3.11 上装 pycocotools 遇到过没有预编译 wheel 的情况,现场编译折腾了半小时,换了 3.8 一次过。如果你手头有 GPU,先确认显卡驱动和它支持的 CUDA 版本,再决定 PyTorch 装哪个版本,这步顺序反了会很麻烦。
| 场景 | Python | PyTorch | CUDA | 说明 |
|---|---|---|---|---|
| NVIDIA 显卡训练 | 3.8 | 2.x | 11.8 或 12.1 | 日常训练推理最稳 |
| 老显卡 / 驱动旧 | 3.8 | 1.13 | 11.6 | 兼容老驱动 |
| 纯 CPU 跑 | 3.8 | 2.x | 无 | 跑 demo 够用,训练非常慢 |
选型逻辑很简单:显卡驱动装好之后,nvidia-smi 能看到一个 CUDA 版本号,PyTorch 的 CUDA 版本必须小于等于它,否则会直接报驱动不兼容。GTX 1660 Ti 这种 6G 显存的卡也能用,但模型尺寸和 batch 要克制,后面 2.3 节单独讲。
2.2 从零装到能跑通:依赖安装与权重准备
用 conda 建独立虚拟环境最省心,不会污染系统 Python,出了问题删掉重建就行。三行命令搞定:
conda create -n fish python=3.8 -y conda activate fish pip install ultralytics -i https://pypi.tuna.tsinghua.edu.cn/simple第一行创建名为 fish 的 Python 3.8 环境,第二行激活它,第三行安装 ultralytics。装 ultralytics 时会自动拉取 PyTorch、opencv-python、pandas 这些依赖,国内网络环境下加清华源能把下载时间从半小时压到几分钟。装完别急着跑训练,先验证环境:
import torch import ultralytics print("torch:", torch.__version__) print("cuda available:", torch.cuda.is_available()) print("ultralytics:", ultralytics.__version__)torch.cuda.is_available() 返回 True 说明 CUDA 版 PyTorch 可用,返回 False 就回到 2.1 的表格换 PyTorch 版本。ultralytics 的版本号看一眼确认是 8.x 就行,不同小版本 API 会有细微差异,但 train、predict 这两个入口基本稳定。再跑一下 nvidia-smi 确认显卡驱动状态:
nvidia-smi能正常显示显卡型号和驱动版本就说明驱动没问题。注意这里显示的 CUDA 版本是驱动支持的版本,和 PyTorch 内部用的 CUDA 不是一回事,很多人把这两个搞混,导致装错版本浪费一晚上。
权重方面,YOLOv8 的预训练权重 yolov8n.pt 第一次使用时会自动下载,但国内网络下经常卡住。常见做法是手动下载对应文件放到项目根目录或 weights/ 文件夹,训练时 model 参数直接指定路径,省去下载等待。
2.3 中低端显卡的显存规划:GTX 1660 Ti 跑 YOLOv8 的取舍
6G 显存跑目标检测训练是够用的,关键是把超参数控制在显存安全线内。我之前用 GTX 1660 Ti 跑鱼类疾病数据集,batch=16、imgsz=640、yolov8n 模型,训练过程很稳。但如果你用 yolov8s 或更大模型,batch 就得降到 8 甚至 4。训练前先跑一段显存检查脚本,心里有底再开训:
import torch free, total = torch.cuda.mem_get_info() print(f"显存 {total / 1024**3:.1f} GB, 剩余 {free / 1024**3:.1f} GB") if free / 1024**3 < 2: print("剩余显存不足 2GB, 建议调小 batch 或 imgsz")torch.cuda.mem_get_info() 返回当前显存剩余和总量,单位是字节,除以 1024 的立方转成 GB。剩余不足 2GB 时训练大概率 OOM,这时候优先降 batch 而不是降 imgsz,因为 imgsz 影响模型感受野,降太多小病灶会直接学不到。另外 ultralytics 默认开启 AMP 混合精度训练,显存占用能省约一半,不要手动关掉,除非遇到 loss 震荡等兼容性问题。
3. 读懂源码包:train.py、val.py、predict.py 三条主线
拿到源码包先别急着跑,花十分钟理清文件结构,后面操作会顺很多。这套包的核心是三个 Python 脚本加一个 Web 界面脚本,图片素材主要是检测效果示例。
3.1 文件结构与数据流向
| 文件 | 作用 |
|---|---|
| train.py | 训练入口,加载数据集和模型权重,产出 best.pt / last.pt |
| val.py | 验证入口,在验证集上计算 mAP、精确率、召回率 |
| predict.py | 预测入口,支持单张图片、视频、摄像头实时检测 |
| ui.py | Web 界面,封装检测逻辑做可视化展示 |
| README.md | 环境安装与训练步骤说明 |
| *.png(19 张) | 示例图片,既当效果展示也当测试素材 |
数据流向是:训练阶段 train.py 读数据集产出权重,val.py 在验证集上评估权重质量,预测阶段 predict.py 加载 best.pt 输出标注框,ui.py 把同样的检测逻辑包装成 Web 服务。训练和预测共用同一套 YOLO 模型定义,所以权重文件可以互通,不会有格式问题。
3.2 先跑通预测:predict.py 的参数与输入输出
拿到源码包第一件事,建议先跑预测而不是先训练。预测链路短,能最快确认模型、环境、图片输入输出整个闭环是通的。源码里的 predict.py 核心结构是这个模式:
import argparse from ultralytics import YOLO parser = argparse.ArgumentParser(description="鱼类疾病检测预测脚本") parser.add_argument("--weights", type=str, default="best.pt", help="模型权重路径") parser.add_argument("--source", type=str, default="1.png", help="图片路径/视频路径/摄像头ID") parser.add_argument("--conf", type=float, default=0.25, help="置信度阈值") parser.add_argument("--iou", type=float, default=0.45, help="NMS的IoU阈值") parser.add_argument("--device", type=str, default="0", help="推理设备, cpu或0") args = parser.parse_args() model = YOLO(args.weights) results = model.predict( source=args.source, conf=args.conf, iou=args.iou, device=args.device, save=True, save_txt=True, ) for box in results[0].boxes: cls = int(box.cls[0]) score = float(box.conf[0]) print(f"类别ID {cls}, 置信度 {score:.2f}")逻辑说明:ultralytics 的 YOLO 对象封装了推理全流程,predict 返回 Results 列表,每个元素对应一帧。save=True 会把标注框画到原图并存到 runs/detect 目录,save_txt=True 额外导出标签文件,后面做批量分析和 Excel 统计时直接读 txt 就行。
参数说明:conf 是置信度阈值,调低会放过更多低置信度框但误报变多,调高漏检变多。鱼类体表病灶对比度低,我一般会调到 0.2 而不是默认 0.25。iou 是 NMS 的去重阈值,0.45 是通用值,如果出现一条鱼被重复框住的情况,可以调到 0.5 加强合并。source 传图片路径就是单张检测,传视频路径就是逐帧检测,传 0 则打开默认摄像头实时检测,多摄像头环境传 1、2 切换。
3.3 val.py 与 mAP 指标怎么读
训练完模型好不好不能凭肉眼感觉,要看 val.py 出的指标。常见做法是:
python val.py --weights best.pt --data fish.yaml --batch 16val.py 的逻辑是让模型在验证集上做一次完整推理,把所有预测框和标注框做对比,统计出 precision、recall、mAP50、mAP50-95 四个核心指标。
| 指标 | 含义 | 鱼病场景注意 |
|---|---|---|
| precision | 预测框中真正目标的比例 | 误报多时偏低 |
| recall | 真实目标被找到的比例 | 漏检多时偏低 |
| mAP50 | IoU=0.5 时的平均精度 | 主要参考指标 |
| mAP50-95 | 严格的多阈值平均 | 病灶边缘不规则时偏低 |
鱼类疾病里眼部缺陷、出血点都是小目标,边缘还模糊,mAP50-95 比 mAP50 低一截是正常的,不用慌。如果 mAP50 都不到 0.5,那要先查数据集和标注。还有个实用的排查技巧:val.py 结束后会在 runs/val 目录生成混淆矩阵图,打开看哪些类别互相混。鳍部缺陷和溃疡形态接近,模型容易把两者搞混,这是数据分布问题,解决办法是给混淆多的类别补样本,而不是盲目调超参。
4. 训练自己的鱼类疾病模型:数据集格式与超参数选型
源码包本身带训练脚本,但真正要训出自己的模型,数据集格式和超参数选型是关键。这一章讲的是从数据集准备到训练完成的全流程。
4.1 数据从哪来:YOLO 格式数据集与 YAML 配置
YOLOv8 训练吃的是标准化格式:每张图片对应一个同名 .txt 标注文件,每行是一个目标,五个数字分别表示类别ID、中心点x坐标、中心点y坐标、框宽、框高,坐标全部归一化到 0~1。源码包里只有 19 张示例图,想训自己的模型,得先准备一批带标注的鱼病图片。
目录结构按这个组织:
dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── fish.yaml标注文件写法和内容示例:
# 001.png 对应 001.txt,每行一个目标 3 0.512 0.431 0.142 0.098 7 0.233 0.612 0.108 0.121第一列 3 和 7 是类别ID,必须从 0 开始连续编号。后面四列是目标框的中心点相对坐标和宽高比例。标注工具选 labelImg 或 labelme 都行,导出时直接选 YOLO 格式。如果原始标注是 COCO 或 VOC 格式,需要先转成这种 txt,转换逻辑后面避坑章节会讲。
训练配置写在 YAML 文件里:
train: dataset/images/train val: dataset/images/val nc: 22 names: 0: fin-rot 1: eye-disease 2: ulcer # 其余类别按标注顺序补齐train 和 val 路径写相对路径时,是基于当前工作目录解析的,所以执行训练命令前先 cd 到项目根目录。nc 必须和 names 列表长度一致,写少一个都会在训练启动时报错,而且报错信息不够直观,容易让人以为数据集路径配错了。
4.2 训练命令与超参数:epochs、batch、imgsz 怎么调
数据集就绪后,训练命令一行就跑起来了:
yolo train data=fish.yaml model=yolov8n.pt epochs=150 batch=16 imgsz=640 device=0model=yolov8n.pt 表示加载预训练权重做迁移学习,比从零训练收敛快很多,鱼病数据集一般几百到几千张,从零训容易欠拟合。epochs=150 是训练轮数,batch=16 是每轮喂给模型的图片数,imgsz=640 是输入分辨率,device=0 指定用第一张 GPU。
超参数怎么选,直接看这张表:
| 参数 | 含义 | 建议值 |
|---|---|---|
| model | 模型骨架 | 6G 显存用 n 或 s,显存大用 m/l |
| epochs | 训练轮数 | 100~300,看数据集大小 |
| batch | 批次大小 | 显存不够就降到 4/8 |
| imgsz | 输入分辨率 | 640 默认,病灶小可试 960 |
| device | 训练设备 | 0 是第一张卡,cpu 是纯 CPU |
模型尺寸这块重点说下:YOLOv8 有 n/s/m/l/x 五档,n 最小最快但精度最低,x 反之。鱼病检测里病灶是小目标,理论上大模型精度更高,但工程落地得看卡。GTX 1660 Ti 这类 6G 卡老老实实用 n 或 s,一晚上能训完;有 3090 或 A100 再上 m 或 l。注意模型变大的同时 imgsz 也可以跟着提,比如从 640 提到 960,小病灶在特征图上占的像素更多,但同样要接受显存占用翻倍。
训练完成后,权重在 runs/detect/train/weights/ 目录下,best.pt 是验证集上 mAP 最优的模型,last.pt 是最后一轮的。预测时用 best.pt,不用 last.pt,因为最后一轮往往已经过拟合,best.pt 才是泛化能力最好的。
4.3 训练曲线怎么看:results.png 的读图逻辑
训练完先别急着测,打开训练日志和 results.png 看看过程是否健康。ultralytics 每轮训练会自动把损失和指标画成曲线,存到 runs/detect/train/results.png,包含 train/loss、val/loss、metrics/mAP50 这些子图。
判断训练是否正常的经验法则:train/loss 和 val/loss 一起下降,说明模型在正常收敛;train/loss 降但 val/loss 掉头向上,就是过拟合了,需要加增广或换小模型;mAP50 曲线先升后平,说明学得差不多了,继续加 epochs 提升有限。训练过程中想看实时数据,直接看 CSV:
tail -f runs/detect/train/results.csvresults.csv 是 results.png 的数据源,每行一轮。想自己重新画损失曲线图,用 pandas 读 CSV 再 matplotlib 画就行:
import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("runs/detect/train/results.csv") epochs = range(1, len(df) + 1) plt.plot(epochs, df["train/box_loss"], label="train box_loss") plt.plot(epochs, df["val/box_loss"], label="val box_loss") plt.legend() plt.show()注意 results.csv 的列名以你本机 ultralytics 版本实际输出为准,如果 KeyError,先打印 df.columns 看真实列名再改。这套画法同样适用于想单独看 mAP50 曲线,把列名换成 metrics/mAP50(B) 就行。
5. 避坑指南:鱼类疾病检测里最容易翻车的五个地方
这套源码包我拆过之后自己也拿鱼病数据训过,踩了不少坑。下面五条按出现频率排,每条都按现象、原因、解决的顺序写,给你省点调试时间。
5.1 数据侧的坑:标注、增广与类别不平衡
坑1:loss 降得很低,mAP 却一直上不去
现象:训练 100 轮,train/loss 降到 0.05,但 val.py 一跑,mAP50 只有 0.3 左右。原因:数据量太少,模型把训练集背下来了。鱼病数据集通常只有几百张图,病灶又小,极易过拟合。解决:加强数据增广。ultralytics 的增广参数默认开了一部分,还不够就把 mosaic=1.0 打开,让小目标在拼图里反复出现;还有一个土办法是把 imgsz 从 640 提到 960,病灶占的像素变多,模型更容易学到有效特征。
坑2:标注文件里的类别 ID 和 names 对不上
现象:训练能跑,但预测时输出的类别名全乱了,眼部缺陷显示成鳍部缺陷。原因:标注工具的类别顺序和 YAML 里 names 顺序不一致,最常见的是漏了第 0 类,所有 ID 整体平移一位。解决:训练前写个脚本扫一遍 labels 目录,把所有出现过的类别 ID 打印出来和 names 对齐:
import os label_dir = "dataset/labels/train" ids = set() for f in os.listdir(label_dir): if not f.endswith(".txt"): continue with open(os.path.join(label_dir, f)) as fp: for line in fp: ids.add(int(line.split()[0])) print("出现过的类别ID:", sorted(ids))这个脚本遍历 labels 目录下所有 txt,把每行第一个数字存入集合并排序输出。如果输出的最大 ID 是 21 而 names 有 22 项,没问题;如果输出的是 0~20 共 21 个 ID,说明有类别没出现或者 ID 错位,需要回标注工具里检查。
5.2 训练与推理侧的坑:显存、卡顿与重复框
坑3:训练中段 CUDA out of memory
现象:前 20 轮正常,第 30 轮突然 OOM 退出,重启训练又在前 20 轮附近挂掉。原因:mosaic 增广把 4 张图拼成一张,尺寸超出预期,加上验证阶段 batch 太大,验证集图片分辨率又高。解决:训练参数里加 val_batch 单独控制验证批次,或者把 batch 降到 4。另外确保 AMP 混合精度开着:
from ultralytics import YOLO model = YOLO("yolov8n.pt") model.train(data="fish.yaml", epochs=150, batch=8, imgsz=640, amp=True)amp=True 在 ultralytics 8.x 里默认开启,但显式写出来能确认没被全局配置或老版本覆盖。AMP 能省接近一半显存,6G 卡跑 yolov8n 全靠它。
坑4:摄像头实时检测卡成 PPT
现象:predict.py 里 source=0,画面一帧一帧跳,延迟明显。原因:鱼病画面上目标多时 NMS 计算量大,每帧还重新画框、写日志、存图,CPU 被拖垮;conf 阈值太低会让候选框数量爆炸。解决:推理设备强制指定 GPU,存图改成间隔帧,conf 从 0.25 提到 0.35。具体做法是每 5 帧保存一次检测结果,而不是每帧都存,实时画面为主、存图留痕为辅。
坑5:一条鱼被框了好几个重叠框
现象:模型对同一病灶输出多个 IoU 很高的重叠框,没有被合并。原因:NMS 的 IoU 阈值设太低,默认 0.45,而病灶区域特征响应强,容易产生多个高置信度框。解决:把 iou 参数调到 0.5 或 0.55,重叠框会被更激进地合并。注意别调太高,超过 0.7 会把两条紧贴的鱼错误合成一个框,漏检更麻烦。
6. 进阶玩法:创新点改进、Web 界面与边缘部署
基础链路跑通之后,这套源码包的进阶价值在三个方向:改进模型结构、Web 化展示、边缘设备部署。
6.1 70+ 创新点怎么接:从注意力机制到 Head 改进
源码包里带了 70 多种 YOLOv8 创新点改进,本质是把模型结构模块化,通过改 YAML 实现替换。以给 Backbone 加注意力机制为例,复制一份 yolov8n.yaml,把某层 C2f 换成改进模块:
backbone: - [-1, 1, Conv, [64, 3, 2]] - [-1, 1, C2f_Attention, [128, True]]改之前先确认对应模块在源码 modules 目录里有定义,没有就得自己补注册。我自己的习惯:先跑通原版做 baseline,再逐个替换模块,每换一个跑一次 val.py 对比 mAP,别一次性全换,否则模型涨了或跌了都说不清是哪个模块的贡献。
6.2 从 .pt 到 rknn:RK3588 部署流程
训完的 best.pt 要上 RK3588 这类边缘设备,得经历 .pt → ONNX → RKNN 的转换。导出 ONNX 用 ultralytics 自带命令:
yolo export model=best.pt format=onnx imgsz=640导出时 imgsz 必须和训练时一致,否则特征图尺寸对不上,转 RKNN 后输出会偏移。再用 RKNN-Toolkit2 把 ONNX 转成 rknn 格式,量化校准图片用 val 集里抽几张就行,不需要全量。部署后在板子上跑一张测试图,对比 RKNN 输出和 PyTorch 输出的框坐标,误差在几个像素内说明转换成功。
有一次我图省事,训练用 960 导出却写 640,部署完 mAP 掉了一截,查了半天才发现是 imgsz 不一致。从那以后我每次改模型尺寸,都强制把训练、导出、部署三处的 imgsz 写进 README,训完先导出再验证再上板,一步都不跳过。希望帮到你。
本文还有配套的精品资源,点击获取