简介面向人工智能毕设与海洋生态监测需求这套基于YOLO系列深度学习框架的海洋生物检测系统内置7464张标注图片的训练流程能够识别海胆、海参、扇贝、海星四类目标并支持图片、视频与实时摄像头检测。压缩包共2000个文件以标注数据文本、程序源码、界面配置和开发文档为主总大小约453.36MB包含PyQt5可视化界面、模型对比评估脚本及完整训练部署代码。已有66人学习下载系统具备目标计数、置信度显示和结果保存功能既可用于毕业设计参考也能为水产养殖与海洋资源管理提供实用的智能检测方案。从数据集准备到模型训练、界面封装均有工程化实现和说明文档支撑适合具备编程基础的开发者直接复用或按需改进。1. 拿到“基于深度学习的海洋生物检测系统 yolov8.zip”先别急着解压跑训练这个压缩包标题里的关键词实际指向一条完整的落地链路用 YOLOv8 检测水下图像里的海星、海参、扇贝、螃蟹等生物。它不是让你重新发明检测算法而是给你一套现成的数据、标注和训练流程。你需要的不是啃论文而是把训练跑通、把权重导出来、塞进自己手里的设备或演示系统里。这篇笔记会从数据标注、环境搭建、训练参数、踩坑排错一直讲到视频推理照着做就能复现出一套能交差的海洋生物检测 demo。适合正在做毕业设计、课程设计或想快速入门 YOLOv8 目标检测的开发者。2. 把海洋生物原始图像变成 YOLOv8 能吃的训练集从抽帧到标注平衡水下生物检测和普通街景检测最大的差别是数据极难找。公开数据集里能直接用的海洋生物检测集不多最常见的做法是自己从水下视频里抽帧、清洗、标注。别指望“解压 zip 里就有现成数据集”就能省事就算 zip 里带了样本新场景下你仍然要自己补数据。这一章就解决“怎么造出一份干净、类别平衡的 YOLOv8 训练集”。2.1 自建数据集的采集思路视频抽帧与难例收集水下视频一分钟有几十上百帧连续画面相邻帧高度相似直接全量抽帧会让训练集里出现大量重复样本模型会严重过拟合。推荐的做法是先按每秒 1 帧抽帧做完初步筛选后只保留清晰、目标可见的图片。好用的一条 ffmpeg 命令如下。ffmpeg -i under_water.mp4 -vf fps1 frame_%04d.jpg -loglevel errorfps1表示每秒抽一帧如果视频里目标多、运动快可以改成fps2如果目标基本静止fps0.5就够了。抽出来的帧建议人工快速扫一遍把所有模糊的、目标被遮挡超过一半的、被水草完全盖住的帧直接删掉。水下图像普遍偏绿偏蓝这个问题我建议留在训练阶段让模型自己学不要在数据阶段强行做白平衡否则会引入新的域差异。难例收集平时很容易忽略。真正到了测试阶段你才会发现模型对背景复杂的礁石区域特别容易误识别成海参对光线昏暗的沙地又容易漏检。所以第一批数据里除了正样本还应该专门收集一批只有礁石、水草、沙子、气泡、没有任何目标生物的“干净背景帧”。这些背景帧不标注框但在筛选划分时保留在训练目录里能让模型学会“这里什么都没有”。2.2 Labelme 标注转 YOLOv8 txt转换脚本与四个边界坑Labelme 是图形化标注工具里最常见的选择能画矩形、多边形和点每周我都会有项目用它标数据。Labelme 保存的 json 文件里记录的是点数坐标而 YOLOv8 需要的是归一化后的中心点坐标(x_center, y_center, width, height)写个转换脚本是绕不过去的一步。import json import os from glob import glob # classes 顺序要和 YOLOv8 的 data.yaml 里 names 顺序严格一致 classes [sea_star, sea_cucumber, echinus, scallop, water_weeds] def labelme_to_yolo(json_path, output_dir, img_width, img_height): with open(json_path, r, encodingutf-8) as f: data json.load(f) txt_name os.path.basename(json_path).replace(.json, .txt) txt_path os.path.join(output_dir, txt_name) with open(txt_path, w) as out: for shape in data[shapes]: cls_name shape[label] if cls_name not in classes: continue cls_id classes.index(cls_name) points shape[points] xs [p[0] for p in points] ys [p[1] for p in points] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) # 归一化到 0~1 x_center (x_min x_max) / 2 / img_width y_center (y_min y_max) / 2 / img_height w (x_max - x_min) / img_width h (y_max - y_min) / img_height # 防止越界和零宽高 x_center min(0.999, max(0.001, x_center)) y_center min(0.999, max(0.001, y_center)) w min(0.999, max(0.001, w)) h min(0.999, max(0.001, h)) out.write(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n) # 批量转换 for jf in glob(/data/labels/*.json): # 图片宽高从同名 json 里取也可以直接从图读 labelme_to_yolo(jf, /data/yolo_labels, 1920, 1080)这段脚本的逻辑很简单取所有标注点坐标的最大最小值构造外接矩形再除以图片宽高得到归一化值。写完后建议抽查几个 txt 文件第一列类别 ID 是否正确后四列数值是否都在 0 和 1 之间。这里踩过的坑有四个。第一个坑是类别名和data.yaml里names顺序不一致比如 YAML 里第二个是海参脚本里第二个是海星模型就会拿海参数据去学海星的标签训练时 loss 照样降验证时 AP 却全乱。第二个坑是 Labelme 画的框超出图像边界例如框从画面边缘延伸出去导致坐标大于 1必须像上面代码里那样钳位。第三个坑是某些 json 不包含imageWidth字段读不到宽高就除零所以我选择直接从图片文件读取真实尺寸。第四个坑是标注点全在一个像素位置比如误点两下外接矩形宽度变成 0YOLOv8 读到零宽目标会直接崩掉需要过滤掉这种 json。2.3 数据不平衡的采样策略别让某几类把模型带偏海洋生物数据不平衡是必然的海星在水下视频里大量出现扇贝可能只有零星几个螃蟹往往藏在石头缝里能作为训练样本的更少。如果直接按原始比例训练模型会为了整体 loss 最低而把所有目标都预测成海星少数类别的召回率难看是小事交项目时被老师当场用扇贝图片验证才是真尴尬。处理不平衡先统计每一类的样本数量代码不复杂。import glob from collections import Counter stats Counter() for txt in glob.glob(/data/yolo_labels/*.txt): with open(txt) as f: for line in f: cls_id int(line.split()[0]) stats[cls_id] 1 print(stats)如果发现某个类别样本量是其他类别的一半以下我一般不会简单重复复制那部分图片。复制图片只能让模型反复看同一张图很快陷入过拟合。更可靠的做法是对少数类别做离线增强把对应的原图以 90 度、180 度旋转上下翻转再叠加亮度扰动和轻微模糊生成新的图片和新的标注文件。注意旋转增强不适合直接用于有方向性特征的生物但对海星、扇贝这种无方向性目标基本安全。另外划分训练集和验证集时不要打乱后随机抽帧否则同一母视频的相邻帧可能被拆到两边验证 loss 会虚低。我习惯按视频来源划分一段视频的帧全部进训练集或验证集这样验证结果更接近真实泛化水平。3. 在 Ubuntu20.04 上把 YOLOv8 环境跑起来从 CPU 到 GPU 的训练命令很多卡在这个项目上的人问题不是模型结构不懂而是环境一天搭不起来。这一章讲我实际操作过的流程用 conda 建独立环境装 ultralytics先确认 torch 设备再跑出第一个训练命令。以 Ubuntu20.04 为例Windows 也是同样的 pip 流程差别只在 GPU 驱动。3.1 从零搭建可以复现的 YOLOv8 环境新机器上我从不直接在 base 环境装包先创建独立环境避免把系统里本来用的 torch 版本搅乱。conda create -n yolo python3.9 -y conda activate yolo pip install ultralytics python -c import torch; print(torch.cuda.is_available())第三行命令会同时把 YOLOv8 的依赖、torch、opencv 这些全部装齐。第四行输出True说明当前环境能用 GPU 训练输出False也不用慌这就是网上常说的 CPU 版 YOLOv8 环境速度慢一点但从搭建到跑通最省心。CPU 版训练几百张小图、几十个 epoch 也能出效果只是单 epoch 可能要几分钟到十几分钟。这里有个常见的翻车点很多人先用 conda 装了cudatoolkit装了cudnn然后发现 torch.gpu 还是 False。原因往往是 N 卡驱动没装或者 PyPI 的 torch 版本和驱动版本不匹配。我调试时会先跑nvidia-smi看驱动是否存在再确认pip list | grep torch里是不是下了 CPU 版。GPU 环境不用过度迷信 conda 装 CUDA系统装上正确驱动后直接用官方 pip 源装 torch 是更少出错的路。3.2 第一个训练命令用 GPU 训练自己的海洋生物数据数据准备好后需要写一个数据文件告诉 YOLOv8 图片路径和类别名这是大多数人看网上的 demo 容易漏掉的一步。我在项目里习惯把数据和这个 yaml 放在同一个项目根目录下。# ocean.yaml path: /data/ocean_dataset train: images/train val: images/val names: 0: sea_star 1: sea_cucumber 2: echinus 3: scallop 4: water_weeds训练命令本身很短真正决定项目成败的是里面的参数。yolo detect train \ modelyolov8s.pt \ dataocean.yaml \ epochs100 \ batch8 \ imgsz640 \ device0 \ patience20modelyolov8s.pt表示加载 YOLOv8s 预训练权重在此基础上微调比从头训练收敛快得多。batch8是单卡一次喂进去的图片数量如果显存只有 6G建议改成 4。patience20表示连续 20 个 epoch 验证集指标没提升就会自动停止训练。设备端device0是第 0 块 GPU没 GPU 就改成devicecpu。选模型时nano 不是不能跑但水下目标普遍偏小、背景又复杂用 s 或者 m 起步会明显踏实很多。GTX1660Ti 这类 6G 卡跑 yolov8s、640 分辨率完全够用只有 4G 显存建议上 yolov8n并把 imgsz 降到 480 避免 OOM。3.3 训练参数含义与判断依据epochs、imgsz、batch、optimizer 怎么调网上很多人问“yolov8模型训练参数含义”实际上你只需要理解四个关键量。epochs是训练轮数不是越多越好模型通常前 50 个 epoch 指标快速上升后面曲线变平甚至过拟合所以配合早停比单设一个大 epoch 更科学。imgsz是输入网络的分辨率640 是速度和精度的折中点小目标多的水下场景可以设 960 甚至 1280但显存占用会以平方上涨。batch影响梯度稳定性和显存batch 太小 loss 跳得厉害太大又装不下我通常按“显存余量 80%”来试。optimizer默认auto会自己挑我的观察平滑性最好的是 AdamW追求更强拟合但调参略麻烦时才用 SGD。只看这几个参数还不够训练过程中你一定会盯得住 loss 曲线。如果你想画自己项目的曲线而不是只看终端输出推荐在训练时加一行命令把结果记录到 CSV训练完用 matplotlib 直接读。yolo detect train \ modelyolov8s.pt \ dataocean.yaml \ epochs100 \ batch8 \ imgsz640 \ device0 \ projectruns/train/ocean \ nameexp1训练结束后runs/train/ocean/exp1/results.csv里每一列就是每个 epoch 的 train loss、val loss、mAP 等指标用 pandas 读出来画成两条 loss 曲线是判断模型有没有正常收敛最快的方式。如果 train loss 一直掉而 val loss 抬高基本可以断定过拟合再补数据比调参数更有效。4. 海洋生物检测在训练中翻车的四个高频坑现象、原因与解决办法这一章写的是我复现类似项目时真正花掉最多时间的部分。前三种情况靠调参能缓解第四种属于环境层面的玄学问题要按现象排查。4.1 小目标漏检小而远的扇贝检测不到现象训练完了测试图里拳头大的扇贝能框出来但视频远处成片的小扇贝漏成一片精度表里扇贝类的 mAP 明显低于海星类。原因YOLOv8 对 640 分辨率输入会把图片缩小到 32 倍一个 20 像素的小目标在深层特征图上只剩几个像素默认 anchor 分配机制会跳过很多小目标。这就是小目标漏检最常见的病灶。解决先把imgsz提高到 960 或 1280如果你用默认 640 而数据里很多目标比较小这项改动带来回报最大。显存不够时配合调小 batch 到 4。其次换更大的模型把yolov8s.pt换成yolov8m.ptm 模型对小目标的特征表达能力更强。最后可在导出测试时把conf0.25降到conf0.1代价是误检多一些但至少能先把目标找出来。4.2 礁石被当成海参正样本多负样本为零现象纯礁石画面没有海参模型却固执地给出 0.7 以上置信度的“海参”框且这些框的位置不固定毁掉整个检测演示效果。原因训练集里每一张图片都至少有一个目标模型从未见过“不含目标”的背景画面。推理时任何与海参颜色相似的纹理都会被当成目标这其实是典型的假阳性问题。解决数据阶段专门收集一批没有生物标签的纯背景图放进images/train这些图对应 txt 文件为空即可。模型在训练时会看到很多“图里没有目标”的样例从而学会压制误检。这种方法效果立竿见影比任何阈值调优都可靠。注意不用在ocean.yaml里加一个“背景类”空 txt 就是最好的背景表达。4.3 类别不平衡海星占比太多模型学不到螃蟹现象整体 mAP 在 50 附近细看每个类的 AP海星 80 多螃蟹只有 10 上下典型的“平均分很高但实际没法用”。原因训练时一个 batch 里出现海星的频率远高于螃蟹backward 时海星贡献的梯度盖住了螃蟹的梯度模型逐渐变成海星“专家”。解决除了上一章说的离线增强还可以在训练时按类别限制采样比例。ultralytics 库里没有开箱即用的类别采样器但可以先做一个“重采样文件列表”的技巧遍历训练图片的 txt统计每类的图片数量对少数类别所在的图片路径重复追加到临时文件列表中。然后再用yolo detect train ... dataocean.yaml搭配cacheTrue训练配合验证集就能看到螃蟹类明显回升。代价是训练 epoch 变长但对小项目完全能接受。4.4 显存 OOM 与 loss 不降两个看起来相近但解法不同的“卡住”现象 A训练第一个 epoch 还没跑完终端报CUDA out of memory。原因imgsz 提高后没有同步降低 batch一张 1280 分辨率的图片在 8G 卡上可能就占 3G 显存batch 8 直接炸。解决先把 batch 降到 2确认能跑完一个 epoch 后再逐步往上加。同时开ampTrue混合精度训练显存占用能降 30% 到 40%。如果降到 batch 2 还是 OOM基本是该换更大显存或者降 imgsz 了。现象 B训练跑了一百个 epochloss 一直高位震荡mAP 没有抬头的迹象。原因学习率不合适常见的是lr0太大导致 loss 反复震荡另一个常见因素是我经常提的数据量不够模型没有足够信息去学习有效特征。小数据集上把epochs加到 300 但没有做数据增强模型只是把训练集背下来了验证集上照样没有提升。解决把lr0从默认 0.01 调到 0.005lrf保持 0.01 让后期学习率降得更慢。如果调完 loss 依旧平直检查训练集图片数量是不是只有一百来张这个数量下任何参数都难救回头补数据才是最正确的做法。5. 把训练好的模型跑通全流程视频推理、mAP 评估与导出部署模型训练完下一步就是验证你在真实视频里的效果并把它用起来。这一章讲我怎么用官方脚本做视频推理、怎么看 mAP 指标才不会被平均数骗到以及怎么把权重复制到边缘设备。5.1 用训练好的权重对测试视频逐帧推理先找到训练结束时生成的best.pt它在runs/train/ocean/exp1/weights/目录下。prediction 脚本非常简单核心就三行。from ultralytics import YOLO model YOLO(runs/train/ocean/exp1/weights/best.pt) results model.predict( sourcedemo.mp4, conf0.5, saveTrue, projectruns/predict, nameocean_demo, )conf0.5是置信度阈值如果检测结果里误检很多调高到 0.6 或 0.7如果漏检多调低到 0.3。saveTrue会把带框的视频和图片存到runs/predict/ocean_demo/目录。如果你要跑的不是视频而是一张图片把source改成图片路径就行。这里要小心一个坑推理时的imgsz默认是 640。如果你训练用的imgsz960推理时先显式加上imgsz960否则模型输入尺寸不一致虽然 YOLOv8 的检测头是自适应输入但效果会比训练时差一截。5.2 验证集 mAP 别只盯着总分逐类看才靠谱只有“平均 mAP”不够海洋生物检测项目里个别类拉低整体是常态。用官方命令评估一次就能输出 Per Class 的指标。yolo detect val \ modelruns/train/ocean/exp1/weights/best.pt \ dataocean.yaml终端会输出一张表格每一行是一个类别的精确率、召回率、mAP50 和 mAP50-95。我评估时主要看两个数字mAP50反映框和类别对不对mAP50-95反映框的位置和尺度够不够精细。如果有单个类别 mAP50 低于 0.3参考第 4 章对症处理别被整表平均分蒙蔽。5.3 把 best.pt 导出成 ONNX方便部署到边缘盒子和嵌入式板训练好的.pt文件只能在 PyTorch 环境里跑交项目可以想塞进 RK3588、Jetson 或者树莓派这类设备就不方便。常见做法是先导出成中间格式再按照具体平台的工具链转换。yolo export \ modelruns/train/ocean/exp1/weights/best.pt \ formatonnx \ imgsz640 \ opset12导出成功后同目录会出现best.onnx。ONNX 是平台无关的交换格式你可以直接拿它在 CPU 上推理也可以再接 RKNN-Toolkit 转成 rknn 模型或者用 TensorRT 转成 engine。opset 参数如果目标平台比较老建议保持 12 而不是默认更高的版本否则边缘设备上的解析器可能报不识别算子的错误。6. 进一步把海洋生物检测做得更稳、更像可交付系统的三个小技巧模型只是中间产物最后能上台演示甚至交付的往往是在代码层面做了正确约束的系统。这里分享三个我每次做类似项目都会顺手加上的技巧。第一个技巧是固定 ROI 区域。水下监测的摄像头大多数时候是固定位置目标只会出现在画面某个范围。推理前用掩码把 ROI 以外的区域直接置灰不仅减少礁石区域的误检还能顺带省一点算力。实现时用 OpenCV 画一个多边形 mask保留区域为白色再对输入帧做按位与操作即可。第二个技巧是帧间结果平滑。视频逐帧检测会出现同一目标时有时无的闪烁观众一眼就能看出“系统不稳定”。我习惯维护一个历史检测结果队列同一个目标如果在连续三帧里至少出现两次就在当前帧输出中间某一帧漏检了用前一帧的坐标补上。这个简单逻辑能把演示视频的可信度提上一个档次。第三个技巧是留存难例。测试视频里凡是模型置信度在 0.3 到 0.5 之间、并不确定的目标我会把它们截图放进一个hard_examples目录。项目收尾前如果有时间就用这些难例再续训几十轮。这个习惯替我挡过很多次汇报现场翻车一个小 demo 往往不是败在平均精度差而是败在观众随手指出一个滑稽误检。希望今天的这套流程能帮你少踩我当年踩过的坑后面做同类项目时少熬几个夜。本文还有配套的精品资源点击获取
