尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

基于YOLOv8的鱼类疾病检测系统:从数据标注到模型部署全流程解析

发布时间:2026/9/14 18:27:34

资讯中心
01
ARTICLE

基于YOLOv8的鱼类疾病检测系统:从数据标注到模型部署全流程解析

基于YOLOv8的鱼类疾病检测系统:从数据标注到模型部署全流程解析
简介基于Python和YOLOv8的鱼类疾病检测系统面向水产养殖从业者、计算机视觉学习者和算法工程师借助深度学习实现鱼类疾病自动化识别解决人工巡检效率低、漏检率高的问题。系统覆盖22种鱼类及其常见病症如出血、眼部缺陷、鳍部缺陷、溃疡等支持图片、视频和摄像头实时检测检测结果可自动保存并导出为Excel同时提供Web前端界面展示便于日常监测与预警。压缩包内共24个文件含4个Python脚本分别对应训练、预测、验证及界面交互19张PNG图片为运行效果与检测示意图另有README说明文档整体仅2.26MB结构清晰易于上手。目前已有90人浏览学习。下载后既可快速复现完整检测流程也可参考包内提到的70余种YOLOv8创新改进点优化模型精度配合训练教程和数据集可训练自定义模型适合作为鱼类检测方向毕业设计或实际项目落地的参考方案。1. 鱼病检测的痛点正好是YOLOv8该干的活水产养殖里鱼到底生了什么病尤其早期病斑、烂鳍和寄生虫感染肉眼判断极不可靠。一个五万尾的池塘人工巡检一遍两小时起步等肉眼能确认发病往往已经传染开。基于Python和YOLOv8的鱼类疾病检测系统做的就是把这套人工巡检变成目标检测流程用标注好的鱼体图像训练YOLOv8得到能框出病灶位置的权重再对批量图像或实时视频帧输出类别与置信度。这套源码不是一条固定命令而是一条从数据标注到模型训练的完整链路适合正在做水产智能化、病害预警的开发者也适合想拿YOLOv8跑通目标检测全流程的人。下面按实际做这类项目的路径展开先搞定环境和数据再训练和调参然后评估与部署最后收在细粒度病斑检测的几个优化手段上。2. YOLOv8环境配置与鱼类图像数据集的组织方式2.1 Python环境搭建与ultralytics安装yolov8环境配置是第一个坎。常见做法是用conda建独立环境避免把系统Python搞乱。Python版本选3.10或3.11都行ultralytics从8.0.x到当前版本对这两个版本兼容性都不错。如果机器有NVIDIA显卡先确认CUDA版本再装对应PyTorch。# 创建独立环境避免污染系统python conda create -n fish-yolo python3.10 -y conda activate fish-yolo # 安装GPU版PyTorch按自己CUDA版本调整cu118/cu121 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装YOLOv8核心库 pip install ultralytics安装完验证一下python -c import ultralytics; print(ultralytics.__version__) yolo predict modelyolov8n.pt sourcehttps://ultralytics.com/images/bus.jpg第二条命令会自动下载yolov8n.pt权重并跑一张示例图能输出推理结果就说明环境通了。要注意PyTorch和CUDA版本必须匹配否则torch.cuda.is_available()返回False训练会掉回CPU速度慢几十倍。验证CUDA可用性用python -c import torch; print(torch.cuda.is_available())2.2 鱼类图像的采集与标注要点任何检测系统的天花板在数据质量不在模型结构。鱼类疾病检测的数据采集有几种来源养殖场实地拍摄的发病鱼、实验室培养皿内的病鱼特写、以及公开的鱼类图像数据集。每类病害至少准备500张带病图像才算勉强够训练。标注工具用labelImg最直接pip install labelimg labelimg标注时注意三点。第一病斑区域要框紧密不要把整条鱼都框进去YOLO学习的是病灶特征框得越精准模型越容易区分病种第二类别名统一用英文小写加下划线如white_spot、fin_rot、gill_rot避免中文路径和类名带来的编码问题第三每张图的标注信息是独立的txt文件文件名与图像名一致。2.3 从VOC格式到YOLO格式的转换脚本labelImg默认可以输出YOLO格式但很多公开数据集给的是VOC的XML标注。写个转换脚本是常见需求import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_path, class_names): 将VOC格式XML标注转为YOLO格式txt tree ET.parse(xml_path) root tree.getroot() # 图像宽高是归一化的基准 img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue cls_id class_names.index(name) box obj.find(bndbox) xmin int(box.find(xmin).text) ymin int(box.find(ymin).text) xmax int(box.find(xmax).text) ymax int(box.find(ymax).text) # YOLO格式要求中心点坐标和宽高全部归一化到0~1 cx ((xmin xmax) / 2) / img_w cy ((ymin ymax) / 2) / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(out_path, w) as f: f.write(\n.join(lines))核心是归一化的计算方式。YOLO不直接存像素坐标而是存相对图像宽高的比例这样不同分辨率图像可以统一训练。class_names列表的顺序就是类别ID的映射后续dataset.yaml里的names顺序必须与此完全一致这是新手最容易踩的坑。2.4 数据集目录结构与dataset.yaml配置yolov8训练自己的数据集目录结构有固定要求。常见布局如下fish-disease/ ├── images/ │ ├── train/ # 训练图像建议70%~80% │ └── val/ # 验证图像建议20%~30% ├── labels/ │ ├── train/ # 每张图像对应的txt标注 │ └── val/ └── fish_disease.yaml训练前的核心配置文件是yaml文件# fish_disease.yaml path: /home/fish/fish-disease # 数据集根目录绝对路径 train: images/train # 相对path的训练图像目录 val: images/val # 相对path的验证图像目录 names: 0: healthy 1: white_spot 2: fin_rot 3: gill_rotpath建议写绝对路径相对路径在某些版本的ultralytics里会解析出问题。names从0开始编号且顺序与标注txt中的类别ID一致。写完后做一次校验from ultralytics.data import YOLODataset ds YOLODataset(fish_disease.yaml, imgsz640) print(f训练集大小: {len(ds)})能正常打印数量就说明数据集没毛病。3. 用YOLOv8训练鱼类疾病检测模型从C2f结构到关键参数3.1 yolov8模型结构中C2f模块的作用YOLOv8的主干网络里C2f模块替代了YOLOv5的C3。C2f的输入先过一个卷积层然后拆成两条路径一条直接传递另一条经过n个Bottleneck的串行堆叠每经过一个Bottleneck都会在通道维度上拼接一次。这种结构让梯度回传路径变短且更丰富同时每个Bottleneck的输出都保留下来信息流不再是简单的串行压榨。具体到鱼病检测这个任务C2f带来的直接好处是鱼身上小尺寸病斑比如白点病初期只有几个像素的白色颗粒在浅层特征中更容易被保留不会因为层数加深而消失。想可视化YOLOv8网络结构在Python里画一下from ultralytics import YOLO import torch model YOLO(yolov8n.pt) x torch.randn(1, 3, 640, 640) y model.model(x) # 查看C2f结构 for name, module in model.model.named_modules(): if c2f in name: print(name, module)一般会看到三个C2f位于不同下采样阶段控制着特征图的通道数和分辨率。理解它有助于后面的调参C2f里的Bottleneck数量多模型容量大但训练慢、容易过拟合数量少则反之。自定义模型结构时改yaml文件里的depth_multiple就能统一缩放。3.2 训练脚本与超参数的选择逻辑确定网络规模后写训练脚本from ultralytics import YOLO # 加载预训练权重n/s/m/l/x按数据量和显存选 model YOLO(yolov8n.pt) model.train( datafish_disease.yaml, epochs150, imgsz640, batch16, patience20, optimizerSGD, lr00.01, lrf0.01, momentum0.937, weight_decay0.0005, augmentTrue, projectruns/train, namefish-v1, seed42 )参数选择背后是有逻辑的不是随手填参数推荐值说明imgsz640病斑太小时可提到1280但显存占用翻4倍batch16或32显存不够就减半别硬上会OOMepochs150鱼病数据量通常几百到几千张150轮足够收敛patience20连续20轮val指标不涨就早停省时间optimizerSGD数据量小时SGD比AdamW泛化好lr00.01预训练权重上的常用初始学习率关键理解patience的意义在于避免后50轮白跑。如果150轮训练在第60轮就停说明数据简单或者模型容量过大属于正常。imgsz提升到1280会显著提升小目标检测效果但训练时间大约翻3倍先小规模测试再决定。3.3 用results.csv绘制损失函数曲线训练过程中最要紧的监控就是损失曲线训练是否收敛、有没有过拟合都能一眼看出来。ultralytics每次训练都会在输出目录生成results.csv里面按epoch记录了box_loss、cls_loss、dfl_loss以及各指标。画损失曲线的脚本import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/train/fish-v1/results.csv) # 老版本列名带前导空格先strip掉 df.columns [c.strip() for c in df.columns] plt.figure(figsize(12, 5)) plt.subplot(1, 2, 1) plt.plot(df[epoch], df[train/box_loss], labeltrain_box) plt.plot(df[epoch], df[val/box_loss], labelval_box) plt.xlabel(epoch) plt.ylabel(box_loss) plt.title(Box Loss) plt.legend() plt.grid(True) plt.subplot(1, 2, 2) plt.plot(df[epoch], df[train/cls_loss], labeltrain_cls) plt.plot(df[epoch], df[val/cls_loss], labelval_cls) plt.xlabel(epoch) plt.ylabel(cls_loss) plt.title(Cls Loss) plt.legend() plt.grid(True) plt.tight_layout() plt.savefig(loss_curve.png, dpi200)看曲线的重点是train和val的间距。train_loss持续下降而val_loss在第40轮后开始反弹就是过拟合信号回调weight_decay或减小模型到yolov8n。两条线保持同步下降且最终平稳说明训练状态健康。3.4 数据不平衡与类别权重鱼病数据里不同病种的样本数往往差异很大常见的寄生虫病可能只有几十张图而健康鱼的标注成百上千。这种不平衡会让模型偏向多数类。常见做法是在yaml文件里用weight字段加类别权重或者在train参数里调cls损失权重model.train( ..., cls1.5, # 提高分类损失的权重让模型更重视类别区分 dropout0.1 # 加dropout缓解少量数据过拟合 )还有一个更实用的做法对样本少的类别做离线增强把每张图水平翻转、旋转15度、亮度抖动各生成一张副本把该类数量补齐到多数类的60%左右。这比单纯调损失权重稳定因为模型看到的是真实变化后的图像。4. 模型评估、置信度阈值调优与批量推理4.1 用val评估模型mAP、Precision、Recall怎么解读训练结束后best.pt保留的是验证集上表现最好的权重。评估命令yolo val modelruns/train/fish-v1/weights/best.pt datafish_disease.yaml输出结果里重点关注这几项指标含义鱼病场景判断标准mAP50IoU0.5时的平均精度目标明确的大病斑期望0.9以上mAP50-95IoU从0.5到0.95的平均精度0.55以上算及格Precision预测框中真正是病灶的比例误报高就调高置信度Recall真实病灶中被框出的比例漏检严重就调低置信度或检查标注只看mAP不够建议加一条混淆矩阵输出yolo val modelruns/train/fish-v1/weights/best.pt datafish_disease.yaml plotsTrueplotsTrue会在输出目录生成confusion_matrix.png一眼看出哪个类别之间互相误判。鱼病场景里最容易混淆的是white_spot和healthy因为健康鱼身上也有白色鳞片反光。如果这两类混在一起优先检查标注框是否把非病灶区域也框进去了。4.2 置信度阈值与NMS参数对漏检误检的影响模型输出的每个预测框都带一个置信度分数默认阈值是0.25。实际部署时这个值必须按场景调。漏检严重真实病鱼没框出来就降到0.1宁可多框几个误报严重健康鱼被标成病鱼就往0.4甚至0.5调。写推理脚本时把这几个参数暴露出来from ultralytics import YOLO # 加载训练好的最佳权重 model YOLO(runs/train/fish-v1/weights/best.pt) # 对整张图像批量推理 results model.predict( sourcetest_images/, conf0.35, # 置信度阈值低于此值的框直接丢弃 iou0.5, # NMS的IoU阈值控制重叠框的合并 saveTrue, save_txtTrue, line_width2 ) for r in results: boxes r.boxes print(f图像: {r.path}) for box in boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) xyxy box.xyxy[0].tolist() print(f 类别{cls_id} 置信度{conf:.2f} 坐标{xyxy})iou参数的语义要理解准确NMS阶段两个框的重叠程度超过0.5就合并成一个值调大比如0.7允许更多重叠框保留适合病斑密集的场景值调小比如0.3则只保留最精准的框。鱼病图片里如果多条鱼重叠在一起默认0.5通常表现已经不错。4.3 导出ONNX做部署推理训练好的PyTorch权重没法直接扔给生产环境导出成ONNX是常见第一步yolo export modelruns/train/fish-v1/weights/best.pt formatonnx opset12 imgsz640导出后使用onnxruntime推理import onnxruntime as ort import cv2 import numpy as np session ort.InferenceSession(best.onnx) input_name session.get_inputs()[0].name img cv2.imread(test.jpg) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img_resized cv2.resize(img, (640, 640)) # 归一化并转成NCHW格式 input_tensor img_resized.astype(np.float32) / 255.0 input_tensor np.transpose(input_tensor, (2, 0, 1)) input_tensor np.expand_dims(input_tensor, axis0) outputs session.run(None, {input_name: input_tensor}) # outputs[0]是(1, 84, 8400)的tensor需要后处理ONNX推理比PyTorch快30%到50%尤其在CPU环境下更明显。8400是640输入下三个检测头输出的锚点总数84对应4个坐标加80个COCO类别数。如果训练时自定义了类别这个数字会变为4N写后处理时注意别硬编码。5. 细粒度病斑检测的进阶优化切片推理与多尺度训练最后一个环节也是最值得花时间的地方鱼病检测里真正难的不是大白斑而是早期几个像素的小病灶。模型在640分辨率下能看到的细节有限很多白点病的初期症状就是被下采样抹掉的。第一个实用技巧是多尺度训练。在train参数里加scale0.5模型会在0.5到1.5倍之间随机缩放输入图像等于变相做了数据增强让模型见过不同尺寸的目标model.train( ..., imgsz640, scale0.5, # 随机缩放范围0.5x~1.5x mosaic1.0, # mosaic增强4图拼接 fliplr0.5, hsv_h0.015, # 色相抖动适应不同水质光照 hsv_s0.7, hsv_v0.4 )鱼生活在不同的水色里偏绿、偏黄、偏浑浊。hsv增强管的就是这个让模型不依赖颜色绝对值而依赖纹理和形状。但注意hsv_h别调太大否则锦鲤的红色斑纹会被抖动成别的颜色引入噪声。第二个技巧是切片推理处理超大图。养殖场拍的照片常常是3000x4000的全池图像直接缩放到640会丢失所有小鱼。常见做法是用SAHI这类切片推理库把大图切成一堆640x640的重叠块分别推理再合并结果from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction detection_model AutoDetectionModel.from_pretrained( model_typeyolov8, model_pathruns/train/fish-v1/weights/best.pt, confidence_threshold0.3, devicecuda:0 ) result get_sliced_prediction( big_pond.jpg, detection_model, slice_height640, slice_width640, overlap_height_ratio0.2, # 高度方向重叠20% overlap_width_ratio0.2 # 宽度方向重叠20% ) result.export_visuals(export_dirsliced_output/)切片重叠的意义目标刚好落在切片边界时不重叠会把它切断。20%的重叠比例在精度和计算量之间是合理折中。病斑被切开后两个切片各含半个目标NMS会把置信度低的一半去掉置信度高的另一半保留总体精度远好于整图缩放。第三个优化点是用TensorBoard监控训练过程比盯着终端输出直观得多tensorboard --logdir runs/train浏览器打开6006端口可以交叉查看box_loss、cls_loss、mAP的变化也能在Images选项卡里看每一轮训练后的预测结果直接观察病斑有没有被正确框出。数据量不足是鱼病检测最常见的限制。模型再花哨标注质量差也训练不出可靠结果。建议先拿50张最有代表性的图像做一轮快速实验确认整个链路没问题再大规模标注如果小样本上模型已经能把主要病种类别区分开再扩数据量往往效果更好。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。