news 2026/10/11 8:52:50

YOLOv5鱼类检测实战:从数据集训练到RK3568与树莓派部署

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv5鱼类检测实战:从数据集训练到RK3568与树莓派部署

简介:面向野生鱼类识别与YOLOv5模型训练任务的专用数据集,适合计算机视觉入门者、算法工程师及渔业监测与水下生态研究人员使用。压缩包内共2321个文件,包括1156张jpg格式的真实场景图像、585个txt标注文件、578个xml标签文件,以及2个mp4视频文件,总大小约518MB。txt与xml两种标注格式可分别对应YOLO训练和VOC格式转换,便于直接接入主流检测流程;视频则有助于观察原始拍摄环境与鱼类活动状态。数据图像覆盖多种野生鱼类在不同光照、角度、遮挡条件下的自然栖息画面,可有效帮助模型学习更丰富的特征并提升泛化能力。已有817人浏览学习,对希望快速获得一套可训练数据集的开发者来说具备不错的参考价值。借助这份数据,用户可以跳过繁琐的采集与标注环节,直接开展数据预处理、边界框检查、类别分布分析和YOLOv5训练调优,从而将更多精力放在模型优化与实际应用部署上。

1. 野生鱼类检测:为什么选择 FISHES-IN-THE-WILD 数据集

很多人做鱼类目标检测时,习惯先下载一批网上杂图,自己再用标注工具框几个类别就开始训练,结果模型一到真实水域就翻车——背景波纹、光线折射、水草遮挡,甚至水面的反光都会导致检测框乱跳。FISHES-IN-THE-WILD-YOLOv5 这个数据集的价值在于,它把野生环境下不同视角、光照、水质条件的鱼类图片统一整理成了 YOLOv5 可直接使用的格式,图片和标注文件分开存放,类别映射清晰,省去了从零清洗标注的繁琐过程。对于正在做渔业资源调查、水下机器人视觉、或者水域监控的开发者,可以直接拿它验证模型效果,也可以作为基础数据做迁移学习。这篇笔记会从数据集目录结构开始讲,覆盖训练配置、超参数调整、常见报错,最后落到 RK3568 和树莓派上的部署量化要点,全部是我实际跑通过的流程。

2. 数据集结构与标注格式:先搞清楚目录再动手

许多训练失败的真正原因并不是模型结构多难,而是数据集内部组织不规范。YOLOv5 对数据集目录有固定预期,一旦不一致,就会触发各类看似莫名其妙的报错。因此拿到 FISHES-IN-THE-WILD-YOLOv5 之后,第一件事不是急着设参数,而是花 10 分钟把目录结构、标注格式、类别映射这三件事核对清楚。下面三个小节就是我的标准检查流程。

2.1 目录结构与文件分布

YOLOv5 标准数据集目录一般长这样:

FISHES-IN-THE-WILD-YOLOv5/ ├── images/ │ ├── train/ │ │ ├── img_001.jpg │ │ └── ... │ └── val/ │ ├── img_001.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── img_001.txt │ │ └── ... │ └── val/ │ ├── img_001.txt │ └── ... ├── data.yaml └── README.md

其中images存放图片,labels存放标注文件。两者通过相同的文件名(不含扩展名)一一对应。data.yaml是训练时的数据入口,包含图片路径、类别数量和类别名称。README.md记录版本信息、类别定义和使用说明。

拿到压缩包后,我习惯先执行两条命令检查图片和标注是否成对:

find images/train -name "*.jpg" -o -name "*.png" | wc -l find labels/train -name "*.txt" | wc -l

如果两个数字不一致,说明部分图片没有标注,或者标注文件缺少对应图片。YOLOv5 会静默跳过没有标注的图片,结果就是实际参与训练的样本数少于你的预期,模型收敛变慢。更隐蔽的问题是图片存在但标注文件为空,这类样本也会被跳过,而且训练日志中不会出现任何警告。

这个数据集在不同发布版本中打包方式略有不同:有的已经分好train和val,有的则将所有素材放在all目录下,需要自行划分。如果压缩包里只有images_all和labels_all,请参考 2.3 节处理。另外,文件扩展名也是坑。图片可能是.jpg、.JPG、.jpeg、.png,而标注一定是.txt。在 Linux 下文件名区分大小写,如果图片是Fish01.JPG而标注是fish01.txt,YOLOv5 会判定为不同文件而忽略该样本。我解压后会先把图片统一改成小写扩展名,确保命名一致。

2.2 labels 标注内容与类别映射

YOLOv5 的标注是纯文本格式,每行表示一个目标:

class x_center y_center width height

其中坐标和宽高都是相对图片宽度和高度的归一化值,取值在 0 到 1 之间。举例:

0 0.5218 0.3421 0.1244 0.0876

这行表示类别 ID 为 0 的目标,中心点位于图片横向 52.18%、纵向 34.21% 的位置,目标宽度占图片总宽的 12.44%,高度占图片总高的 8.76%。这种表示的优点是标注与图片分辨率无关,不需要在训练时再按坐标进行像素级换算。

类别映射关系在data.yaml的names字段中定义:

names: 0: fish

如果数据集包含多种鱼,则类似:

names: 0: carp 1: trout 2: salmon

类别 ID 是从 0 开始递增的,顺序就是names列表的顺序。这里最容易犯的错误是:不同版本的data.yaml顺序不同,如果你将训练用的 YAML 和验证时的 YAML 混用,模型输出的类别 ID 会对不上实际鱼种,最后连可视化结果都看不出错在哪。

为了直观确认标注质量,我建议在训练前把标注框画到图片上检查。以下脚本读取一张图片和其 YOLO 标注,绘制矩形框和类别 ID:

import cv2 img_path = 'images/train/img_001.jpg' label_path = 'labels/train/img_001.txt' img = cv2.imread(img_path) h, w = img.shape[:2] with open(label_path) as f: for line in f: parts = line.strip().split() cls, x, y, bw, bh = int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) x1 = int((x - bw / 2) * w) y1 = int((y - bh / 2) * h) x2 = int((x + bw / 2) * w) y2 = int((y + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(cls), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) cv2.imwrite('label_check.jpg', img)

代码中x1, y1是左上角像素坐标,x2, y2是右下角坐标,计算逻辑是中心点坐标减去或加上宽高的一半,然后乘以图片的实际宽高。画完后再对照原图,如果框明显偏离鱼类主体,那就要检查标注工具的导出设置,不要直接训练。

2.3 数据集划分与建议的 train/val 比例

如果数据集已经分好 train 和 val,这节可以跳过。但如果你拿到的是统一目录,或者希望重新划分得到更均衡的分布,就需要写脚本。我习惯按 8:2 划分训练集与验证集,也就是 80% 图片用于训练,20% 用于验证。如果数据集规模非常大,比如超过 10 万张,可以降到 19:1,但至少要保证每个类别在验证集中出现。

划分的核心原则是随机且类别均衡。不能简单按文件名顺序截断,否则某些类可能全部落入训练集,验证集缺了那类样本,mAP 会虚高。下面是我常用的划分脚本:

import os import random from shutil import copyfile random.seed(2024) all_images = [f for f in os.listdir('images/all') if f.endswith(('.jpg', '.JPG', '.png'))] random.shuffle(all_images) split_idx = int(0.8 * len(all_images)) train_list = all_images[:split_idx] val_list = all_images[split_idx:] for split_name, img_list in [('train', train_list), ('val', val_list)]: os.makedirs(f'images/{split_name}', exist_ok=True) os.makedirs(f'labels/{split_name}', exist_ok=True) for img in img_list: stem = os.path.splitext(img)[0] src_img = os.path.join('images/all', img) dst_img = os.path.join(f'images/{split_name}', img) src_lbl = os.path.join('labels/all', stem + '.txt') dst_lbl = os.path.join(f'labels/{split_name}', stem + '.txt') if os.path.exists(src_lbl): copyfile(src_img, dst_img) copyfile(src_lbl, dst_lbl) else: print(f'missing label for {img}')

random.seed(2024)固定随机数种子,保证每次运行结果一致,便于复现实验。如果某张图片没有标注文件,脚本会打印missing label并跳过该图片。划分完成后,检查验证集的类别分布:

for f in labels/val/*.txt; do cat $f; done | awk '{print $1}' | sort | uniq -c

输出结果中,每个类别 ID 后面的数字表示该类别在验证集中的框数。如果有类别计数为 0,需要从all目录中手动补充该类图片到验证集。这一步很机械,但能避免后期训练出现“某一类 AP 为 0”的假象。

3. 训练 YOLOv5 检测模型:从配置到跑通第一步

数据集检查通过后,下一步就是把 YOLOv5 训练跑通。这一章是实操核心,我会按环境准备、数据配置、模型结构、启动训练四个步骤展开,每一步都给出可直接照抄的命令和参数说明。

3.1 环境准备与依赖安装

YOLOv5 官方仓库要求 Python 3.8 以上和 PyTorch 1.8 以上。如果你的显卡是近几年的架构,建议直接装 PyTorch 2.x,推理和训练速度都会有所提升。CUDA 版本必须与 PyTorch 对应,否则torch.cuda.is_available()返回False,模型会退到 CPU 上训练。

完整的安装命令如下:

git clone https://github.com/ultralytics/yolov5 cd yolov5 python -m venv venv source venv/bin/activate pip install --upgrade pip pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install -r requirements.txt

虚拟环境必须创建,因为 YOLOv5 依赖的opencv-python、seaborn、pandas等包版本比较敏感,如果直接混入系统 Python,很容易与已有项目发生冲突。环境建立好后,先跑一遍官方自带示例,确认基础链路通:

python detect.py --weights yolov5s.pt --source data/images/bus.jpg

这条命令会自动下载yolov5s.pt权重,然后对一张公交车图片做检测。成功后会生成runs/detect/exp/bus.jpg。如果这一步报错,八成是 PyTorch 和 CUDA 不匹配,而不是 YOLOv5 本身的问题。此时在 Python 中执行:

import torch print(torch.cuda.is_available())

返回False就要重新安装 PyTorch。另外,自动下载权重经常因网络原因失败,我一般手动把权重文件放进weights/目录,再用--weights weights/yolov5s.pt指定本地路径,这样训练时不会被下载流程卡住。

3.2 编写数据配置文件 data.yaml

YOLOv5 通过--data参数指定数据配置文件。这个 YAML 文件告诉训练器图片从哪里读取、有几类目标、类别名称是什么。针对 FISHES-IN-THE-WILD 数据集,我会新建一个fishes.yaml,内容如下:

train: /absolute/path/to/FISHES-IN-THE-WILD-YOLOv5/images/train val: /absolute/path/to/FISHES-IN-THE-WILD-YOLOv5/images/val nc: 1 names: ['fish']

如果是多类别数据集,需要把nc改成实际类别数,names按类别 ID 顺序列出。路径建议写绝对路径。相对路径容易出错,因为 YOLOv5 的解析基准是仓库根目录,而不是 YAML 文件所在目录,新手经常把路径写成相对于 YAML 文件的层级,导致AssertionError: train: No images in ...。

写好后用 Python 快速验证 YAML 能否被正确加载:

import yaml with open('fishes.yaml') as f: cfg = yaml.safe_load(f) print(cfg['nc'], cfg['names']) print(cfg['train'])

如果报错yaml.YAMLError,多数是文件缩进不正确或引号混用了中文符号。从 Windows 复制文件时尤其常见。还有一个细节:数据集自带的data.yaml有时包含download字段,训练时不影响,但没必要保留。我坚持新建配置文件,不直接用原始文件,这样还能避免误改原始数据。

3.3 修改模型结构与超参数

模型结构定义在models/目录下的 YAML 文件中,比如models/yolov5s.yaml定义了 YOLOv5s 的宽度、深度和 anchor。这些文件最关键的一行是:

nc: 80

这是 COCO 数据集的类别数。训练鱼类数据集时,必须把它改成自己的类别数。如果忘了改,可能的报错是维度不匹配;如果改成错误数值,模型能训练但分类头输出与实际类别对不上,mAP 失去参考意义。我习惯复制一份结构文件再改,保留原始文件不动:

cp models/yolov5s.yaml models/yolov5s_fish.yaml sed -i 's/^nc: 80/nc: 1/' models/yolov5s_fish.yaml

nc修改后,模型输出的矩阵维度也会随之变化。YOLOv5 会根据新的nc自动调整检测头,不需要手动改代码。

除此之外,anchors参数用于确定初始锚框的大小。COCO 的 anchor 是基于通用物体统计数据,鱼类目标的尺寸分布与 COCO 不同,但 YOLOv5 在训练中会自适应学习 anchor,不需要我们手工设置。如果你的图片尺寸比较特殊,比如全部是 1920×1080 的宽屏视频帧,我建议先运行:

python utils/autoanchor.py --cfg models/yolov5s_fish.yaml --data fishes.yaml

这个脚本会根据你的数据集重新计算 anchor 并自动更新到配置文件中。运行时间通常几分钟,对检测画面中远距离小鱼有可感知的提升。

超参数方面,YOLOv5 提供了两个预设文件:data/hyps/hyp.scratch-low.yaml和data/hyps/hyp.scratch-high.yaml。前者学习率低、增强强度低,适合中小数据集;后者增强更强、学习率更高,适合大数据集。我训练鱼类数据时,先用低增强预设。几个常调整的参数含义:

  • lr0:初始学习率,默认 0.01。如果前 20 轮损失不降,可降到 0.005。
  • momentum:SGD 动量,默认 0.937,通常不改。
  • weight_decay:权重衰减系数,默认 0.0005,防止过拟合。
  • hsv_h、hsv_s、hsv_v:色彩增强范围,过大的话会导致鱼体颜色失真,建议保持默认或调低。

这些参数通过--hyp指定文件路径,不要直接改预设文件,否则容易混淆实验记录。

3.4 启动训练与日志解读

准备好数据 YAML 和模型结构 YAML 后,执行训练命令:

python train.py --data fishes.yaml --cfg models/yolov5s_fish.yaml --weights yolov5s.pt --epochs 100 --batch-size 16 --img 640 --workers 8

参数含义:

  • --epochs:训练轮数,通常 100 轮足够。如果验证集 mAP 还在上升,可以续训。
  • --batch-size:批大小,受显存限制。16GB 显存可跑 16,8GB 建议降到 8。
  • --img:输入图片边长,默认 640。如果鱼类目标小,可考虑 1280,但显存占用会大幅增加。
  • --workers:数据加载线程数,一般设为 CPU 核心数的一半。
  • --cache:将数据集提前加载到内存,加速训练。内存不足时不要用。

训练过程中终端会打印box_loss、obj_loss、cls_loss。box_loss是定位损失,obj_loss是置信度损失,cls_loss是分类损失。单类别数据集里cls_loss很低是正常的,不用惊慌。

训练日志保存在runs/train/exp/results.csv中,可以用pandas查看损失趋势。如果前 20 轮box_loss和obj_loss基本不动,可能是学习率太高或者标注误差太大,此时先停止训练回去查数据,而不是盲目增加轮数。

训练完成后,runs/train/exp/weights/下会有best.pt和last.pt。best.pt是验证集表现最好的权重,last.pt是最后一轮权重。部署时只使用best.pt。我曾在一次实验中图省事直接用了last.pt,结果部署后 mAP 比best.pt低了近 8 个点,原因是最后几轮过拟合导致验证指标下降。这个教训比较深刻。

4. 避坑/常见问题:鱼类数据集训练中的五个典型陷阱

水下环境导致鱼类检测的坑比普通目标检测更多。我在 FISHES-IN-THE-WILD 数据集上反复训练后,总结了五个出现频率最高的坑,每一条按“现象 → 原因 → 解决”的顺序描述,读者可以对照自己的日志排查。

4.1 图片尺寸与标注框越界

现象:训练到某个 epoch 时,终端突然输出ValueError: All bounding boxes should have positive height and width,训练直接终止;有时训练没终止,但损失变成nan,后续模型全部不可用。

原因:部分标注文件坐标值超出 0~1 范围。标注工具允许在图片边缘画出部分越界框,导出的坐标可能出现1.02或-0.01。YOLOv5 在数据增强阶段会对图片随机缩放,越界坐标可能被变换成负宽高或空框,导致损失计算异常。

解决:训练前用脚本清洗所有标注文件。基本策略是裁剪越界坐标、过滤非法尺寸:

import glob def clean_label(txt_path): with open(txt_path) as f: lines = f.readlines() cleaned = [] for line in lines: parts = line.strip().split() if len(parts) != 5: continue cls, x, y, w, h = parts[0], float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) if w <= 0 or h <= 0: continue x = max(0, min(1, x)) y = max(0, min(1, y)) w = max(0, min(1, w)) h = max(0, min(1, h)) cleaned.append(f"{cls} {x:.6f} {y:.6f} {w:.6f} {h:.6f}\n") with open(txt_path, 'w') as f: f.writelines(cleaned) for lbl in glob.glob('labels/**/*.txt', recursive=True): clean_label(lbl)

脚本将坐标裁剪到[0,1]区间,过滤掉宽高不为正的框。执行后再次检查空标注文件,如果某张图片的所有框都被过滤掉了,建议删除对应图片或重新标注。

4.2 类别不平衡导致漏检

现象:训练完成后,验证集整体 mAP 看着不错,比如 0.85,但某个稀有鱼类的 AP 只有 0.1 左右。部署时,模型对该类几乎全漏。

原因:FISHES-IN-THE-WILD 若包含多类别,样本数往往差距很大。常见鱼类的图片可能有几千张,稀有鱼类可能只有几十张。模型在训练中会把绝大部分容量用于学习常见类,稀有类的特征学习不足,分类边界也无法收敛。

解决:先统计类别分布:

cat labels/train/*.txt | awk '{print $1}' | sort | uniq -c

如果某个类占比低于总框数的 5%,优先使用 YOLOv5 自带的--image-weights参数:

python train.py --data fishes.yaml --cfg models/yolov5s_fish.yaml --weights yolov5s.pt --image-weights

--image-weights会根据图片真实类别动态提高稀有类样本的采样概率。这个方案不改变数据集,成本最低。如果效果不明显,再将稀有类图片单独提取出来,用高增强超参数做 20 轮微调。

4.3 数据增强引起的标注错位

现象:训练过程很平稳,损失在下降,但验证集 mAP 一直不高。把训练示例图打开看,发现图像中鱼的位置和标注框完全错位,比如鱼在左上角,框却在右下角。

原因:YOLOv5 默认启用 Mosaic 增强,训练时会把 4 张图拼接。如果原始标注文件格式不统一,比如某一行缺少了坐标、或行内字段顺序混乱,Mosaic 模块在解析标注时就会拿到错误数据,最终生成的增强图像与标注不符。

解决:先用无增强方式训练几个 epoch 做对比:

python train.py --data fishes.yaml --cfg models/yolov5s_fish.yaml --weights yolov5s.pt --no-augment --epochs 10

如果关闭增强后验证 mAP 显著提高,说明是增强与标注的配合问题。接下来检查所有标注文件,确保每行都是五列且字段之间用单个空格分隔。快速定位问题行:

awk 'NF != 5 {print FILENAME, NR, $0}' labels/train/*.txt

修复这些行后,再开启增强训练。不要直接删除增强策略,因为 Mosaic 对最终精度有明显贡献,问题多半出在标注格式本身。

4.4 小目标检测效果差

现象:对于图片中只占几十像素的小鱼或远处鱼群,模型几乎不输出检测框,但大目标的检测效果正常。PR 曲线上小目标区间召回率很低。

原因:YOLOv5s 默认输入尺寸 640,小目标经过多次下采样后,在特征图上可能只有 1~2 个像素,卷积核无法提取有效语义信息。野生环境下相机距离远、鱼体小,这个现象更突出。

解决:按性价比从高到低尝试三种手段。

第一种是提高输入分辨率。训练时把--img从640改为1280,小目标在放大后特征更明显。代价是显存占用成倍增加,batch size 可能需要从 16 降到 4。

第二种是换更大的模型,比如yolov5m或yolov5l。模型网络更深,特征图分辨率更精细,但训练时间也会成倍增加。

第三种是使用 SAHI 切片推理库,推理时把图片切成多个带重叠区域的小块分别检测,再合并结果。这只影响推理阶段,不改变训练效果,适合部署在算力较强的机器上。

我实际测试中,--img 1280配合yolov5m对小目标 mAP 提升最明显,但从训练到部署的整体耗时增加了近 5 倍,需要根据自己的算力取舍。

4.5 训练中断后如何恢复

现象:训练到第 80 轮时断电,或者显卡驱动崩溃,重启后训练日志和损失曲线全部丢失,只能从头跑 100 轮。

原因:YOLOv5 默认每个 epoch 结束后保存last.pt和best.pt,但如果你没有周期保存中间 checkpoint,中断点之前最近的完整权重可能已经过时。更严重的是,如果进程被硬杀,当前 epoch 的优化器状态没有落盘,无法准确恢复。

解决:训练时加上--save-period 5,每 5 个 epoch 保存一个 checkpoint:

python train.py --data fishes.yaml --cfg models/yolov5s_fish.yaml --weights yolov5s.pt --epochs 100 --save-period 5

这样权重目录下会生成checkpoint_epoch5.pt、checkpoint_epoch10.pt等文件。中断后恢复:

python train.py --resume runs/train/exp/weights/checkpoint_epoch80.pt

--resume会读取权重中保存的训练轮数、学习率、优化器状态等,不需要再手动指定--data和--cfg。如果没有设置--save-period,使用last.pt也能恢复,但轮数会滞后于实际中断点,多跑几轮问题不大。

5. 提升检测效果:迁移学习与后处理调优

默认参数训练出来的模型往往只是“能跑”而不是“好用”。在真实水域监控中,需要通过迁移学习、推理参数调节和误检分析进一步提高精度和可靠性。这一章的三节内容是我在鱼类项目上反复验证过的优化手段。

5.1 使用预训练权重做迁移学习

YOLOv5 官方发布的各种*.pt权重是在 COCO 数据集上预训练的。COCO 中虽然没有鱼类专用类别,但骨干网络已经学会提取边缘、纹理、形状等通用特征。这些底层特征与水下鱼类识别所需特征高度重叠,所以用预训练权重比随机初始化收敛更快,最终精度通常也更高。

迁移学习的启用方式就是在训练命令中加--weights yolov5s.pt。当预训练权重的分类层神经元数与你的nc不一致时,YOLOv5 会自动丢弃最后一层,只加载骨干和颈部参数,这个过程完全自动。

对于几千张规模的小数据集,我还会使用--freeze冻结部分层。冻结前几层的作用是保留预训练权重中通用的特征提取能力,只让模型更新后面的检测头。经验上,数据集越少,冻结层数可以越多。命令示例:

python train.py --data fishes.yaml --cfg models/yolov5s_fish.yaml --weights yolov5s.pt --freeze 10 --epochs 50 --batch-size 16

--freeze 10表示冻结前 10 层。训练日志会打印被冻结的层名。如果冻结后损失下降缓慢,就减少冻结层数,或者干脆不冻结。我在 FISHES-IN-THE-WILD 上观察到,数据集约 2 万张时,冻结 10 层和完全不冻结的最终精度相差不大,但冻结时前 30 轮损失更稳定,不容易发散。

另外,预训练权重的选择也有讲究。yolov5s.pt速度和精度均衡,适合大多数场景。如果部署设备性能强,可以用yolov5m.pt或yolov5l.pt微调;如果设备算力很弱,用yolov5n.pt或yolov5s.pt更合理。不要盲目上大模型,因为边缘部署时的帧率往往比 mAP 更关键。

5.2 推理时的置信度与 NMS 调整

很多人不区分训练超参数和推理超参数,以为训练完成后用默认设置在detect.py上跑就是最优解。实际上,推理时的置信度阈值conf-thres和 NMS 的 IoU 阈值iou-thres需要根据业务需求单独调。

默认参数是--conf-thres 0.25 --iou-thres 0.45。鱼类监控场景中,如果漏检率太高,可以把conf-thres降到0.2甚至0.15,代价是误检框增多;如果误检太多,比如模型经常把水草或岩石当鱼,就把阈值提高到0.35。NMS 阈值影响重叠框的合并策略。鱼群密集时,相邻的鱼框重叠度高,默认 0.45 会把两条鱼合并成一个,所以我建议调高到0.6;对独立游动的稀疏场景,保持 0.45 即可。

示例命令:

python detect.py --weights runs/train/exp/weights/best.pt --source underwater_video.mp4 --conf-thres 0.2 --iou-thres 0.6

每换一个场景,我都会在验证集上跑一遍不同阈值,看看 PR 曲线的拐点。下面表格是我常用的推荐起始值:

场景conf-thresiou-thres说明
密集鱼群0.20.6减少漏检,容忍重叠
稀疏独立0.40.45减少误检,保持定位
远距离小目标0.150.5低置信度保留,防止漏掉

参数没有万能值,必须通过视频预览来主观判断。我通常在detect.py输出目录下多生成几组结果,逐个播放对比,再把最优参数写入部署代码。

5.3 验证模型:用混淆矩阵定位漏检

训练生成的runs/train/exp/目录里有一张confusion_matrix.png,这张图比单纯的 mAP 数字更能反映模型错误模式。混淆矩阵对角线表示类别间正确分类比例,非对角元素则表示某类被误判成了另一类。对于多类别数据集,它能直接告诉你哪两个类容易混,这在鱼类上很典型,因为不同鱼种体型接近、纹理相似,模型可能把青鱼认成草鱼。

如果数据集是单类,混淆矩阵价值有限,需要使用val.py获得每一类的 AP:

python val.py --data fishes.yaml --weights runs/train/exp/weights/best.pt --conf-thres 0.1 --iou-thres 0.5 --verbose

--verbose会在终端逐类打印 AP。如果某个类 AP 很低,打开对应的验证集图片,查看是目标太小、遮挡严重,还是标注本身就漏标了。我遇到最多的情况是:数据集里的某些标注框只框住了鱼身体的一部分,尾巴被标出框外,模型学到的目标范围就缩小了,后续检测自然也会框错过。

定位到问题后,回到标注文件修补,比如把框扩大以包含完整尾巴,然后重新训练。很多调参解决不了的问题,最后都发现是标注问题。所以我把标注质量检查作为每次训练的固定动作。

6. 把模型部署到边缘设备:RK3568 和树莓派上的量化要点

模型验证通过后,最终要落到边缘设备上。热搜里常看到 yolov5 量化 rk3568 和树莓派 4b 部署 yolov5,这里我总结在两类设备上部署的通用流程和关键参数,重点放在量化前后精度对比,避免模型在板上“没头没脑”。

6.1 导出与转换

训练完成后,先用官方导出脚本将 PyTorch 权重转为 ONNX:

python export.py --weights runs/train/exp/weights/best.pt --include onnx --opset 12

生成best.onnx后,不同设备走不同转换链路:

  • RK3568:使用瑞芯微的rknn-toolkit将 ONNX 转为 RKNN 格式,转换时需要指定输入尺寸和量化模式。
  • 树莓派 4B:可以继续使用 ONNX Runtime,也可以先转成 TFLite 再由 TFLite Runtime 加载。

转换时保持输入尺寸与训练一致,通常为 640。不要为了提速擅自改成 416,这会造成小目标漏检率成倍上升。

6.2 量化与验证

量化是边缘设备提速的关键,但必须做量化后验证。取验证集中 200~500 张代表性图片作为校准集,生成量化模型。转换完成后对比量化前后的 mAP:

  • 精度损失在 1% 以内,可以放心部署。
  • 损失在 3%~5% 之间,尝试增加校准图数量。
  • 损失超过 5%,检查是否有敏感层被量化,使用混合精度,对检测头保留 float16。

在树莓派上使用 ONNX Runtime 量化时,要避免量化第一层卷积和最后的输出层,这样可以保住定位精度。部署后观察内存占用和帧率,如果帧率不足,优先把输入尺寸降到 512,或者将模型从 yolov5s 换成 yolov5n,不要铤而走险调低 NMS 阈值。

最后说一个我的固守习惯:每次拿到训练好的鱼类模型,我都会把导出、量化、部署前后的验证数据单独存一份,量化前后 mAP 对比曲线放进项目仓库。从那以后,我每次部署到 RK3568 或树莓派前,都强制走一遍“导出 → 量化校准 → mAP 对比 → 板上实拍”的流程,很少再遇到精度莫名掉点的问题。希望这篇笔记能帮你在 YOLOv5 鱼类检测的路上少走几个弯。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/11 8:52:14

系统架构设计师:角色定位、职业素质与协作关系解析

在复杂的软件与信息系统开发中,系统架构设计师扮演着至关重要的角色。根据最新的知识体系定义,系统架构设计师不仅是技术的领军人物,更是连接业务需求与最终实现的核心枢纽。本文将基于系统架构设计师的基础知识点、图表配合关系,以及其与产品经理、项目经理、系统分析师的…

作者头像 李华
网站建设 2026/10/11 8:49:03

2026面试软件测试,软件测试常见的面试题(附带答案)

1、综合素质 1、自我介绍 面试官您好&#xff0c;我叫XXX&#xff0c;一直从事车载软件测试 &#xff0c;负责最多的是中控方面。 以下是我的一些优势&#xff1a; 车载的测试流程我是熟练掌握的&#xff0c;且能够独立编写测试用例 。 平时BUG 提交会使用到Jira&#xff…

作者头像 李华
网站建设 2026/10/11 8:44:10

本地AI绘图全家桶从零部署与调优实战指南

如果你跟我一样&#xff0c;对“本地AI绘图全家桶”这个组合有好奇&#xff0c;那这篇文章正好是给你写的。它不是某个软件的名字&#xff0c;而是目前开源社区里最主流的一套本地部署绘图方案&#xff1a;一个跑在你自己电脑上的浏览器界面工具&#xff0c;加上一堆模型文件、…

作者头像 李华
网站建设 2026/10/11 8:41:20

Python列表从入门到精通:底层原理与性能优化实战

很多朋友刚开始学 Python 时&#xff0c;第一个被劝退的地方往往不是语法本身&#xff0c;而是搞不懂列表&#xff08;List&#xff09;这种“什么都能装”的容器到底该怎么用。其实列表恰恰是整个 Python 里最实在、最好用的内置类型&#xff0c;没有之一。无论你是用 Pycharm…

作者头像 李华
网站建设 2026/10/11 8:39:14

高校机房 / 企业办公全适配:vDisk 融合云桌面,IDV+VDI 双架构 + 算力共享,降本又提效

不管是高校的公共机房、专业实训室、标准化考点&#xff0c;还是企业的办公终端、研发工位、分支机构&#xff0c;云桌面的核心需求早已从 “能用” 变成 “好用、划算、好管”。微碟云 vDisk 融合云管理平台 5.0全面升级 IDVVDI 双架构能力&#xff0c;打通终端本地算力与集中…

作者头像 李华
网站建设 2026/10/11 8:38:16

正则表达式调试工具 Rea:用可视化解析与回溯回放定位性能问题

最近在排查一段线上日志匹配慢的问题&#xff0c;我又双叒叕被正则表达式坑了一把。表达式在测试工具上看完全正常&#xff0c;样例数据也都是绿的&#xff0c;可一放到真实日志里就偶尔卡住好几个小时。查来查去&#xff0c;问题出在一个嵌套量词的灾难性回溯上。坦白说&#…

作者头像 李华