ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

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

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.txtYOLOv5 会判定为不同文件而忽略该样本。我解压后会先把图片统一改成小写扩展名确保命名一致。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 标注绘制矩形框和类别 IDimport 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(fimages/{split_name}, exist_okTrue) os.makedirs(flabels/{split_name}, exist_okTrue) for img in img_list: stem os.path.splitext(img)[0] src_img os.path.join(images/all, img) dst_img os.path.join(fimages/{split_name}, img) src_lbl os.path.join(labels/all, stem .txt) dst_lbl os.path.join(flabels/{split_name}, stem .txt) if os.path.exists(src_lbl): copyfile(src_img, dst_img) copyfile(src_lbl, dst_lbl) else: print(fmissing 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.yamlYOLOv5 通过--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.yamlnc修改后模型输出的矩阵维度也会随之变化。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。momentumSGD 动量默认 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 显存可跑 168GB 建议降到 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, recursiveTrue): 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 保存一个 checkpointpython 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获得每一类的 APpython 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 权重转为 ONNXpython 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 鱼类检测的路上少走几个弯。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进