首页 / 资讯中心 / 文章详情

RDK X5部署YOLOv5全流程详解:从模型转换到板上推理避坑

RDK X5部署YOLOv5全流程详解:从模型转换到板上推理避坑 ★ FEATURED ARTICLE
写这篇教程之前我先说句大实话我在 RDK X5 上第一次部署 YOLOv5 时光卡在从 .pt 到 .bin 这一步就折腾了整整两天。模型在电脑上跑得好好的一到转换环节就报算子不支持、动态 shape 警告、量化精度崩……网上的资料要么太零散要么直接省略了关键步骤照着做完全对不上号。后来把整条链路从模型导出、量化校准、工具链编译到板上推理全部跑通之后回头看发现大部分坑都有规律只是缺少一篇把细节讲透的教程。这篇文章就是干这个用的不管你是刚接触边缘设备部署的新手还是手里正好有一个训练好的 YOLOv5 模型打算上板都可以照着我这套流程走一遍从环境准备一直跟到推理结果打印出来。1. 开跑之前RDK X5 环境搭建与工具链版本避坑1.1 开发板访问与系统准备RDK X5 的部署工作至少涉及两台设备一台开发机用来跑模型转换工具链一块开发板负责实际推理。开发板第一次用供电和网络连接是两件最容易出问题的事。供电建议直接用包装里配套的电源适配器不要拿普通手机充电头凑合。部分手机头虽然电压标称对得上但电流输出不稳模型推理时 BPU 满负载会让瞬时功耗飙升劣质电源直接重启。网络连接我推荐两种方式按你的习惯选一种就行网线直连路由器开发板开机后自动获取 IP。开发机装好网络调试工具在同一局域网内通过 SSH 访问板卡。开发板接 HDMI 显示器和键鼠直接在板上操作。这种方式调试系统时方便但后续改代码、传文件效率低。我自己的习惯是双保险先用 HDMI 进系统确认 IP然后立刻转到 SSH 上操作。毕竟后面要反复往板上拷贝 .bin 文件和推理脚本SSH 配合文件传输工具效率高得多。确认能连上之后建议先跑一个简单的系统更新命令把板上已有的底层依赖刷到最新。很多报错其实是系统镜像里的驱动和工具链版本不匹配造成的更新完能省掉不少莫名其妙的坑。1.2 工具链安装Docker 容器和依赖版本匹配开发机上装模型转换工具链时我的建议是优先用官方提供的 Docker 镜像而不是直接在宿主机上 pip install 一整套依赖。直接装到系统里不是不行但工具链涉及的 Python 版本、ONNX 版本、protobuf 版本之间耦合很强一不小心就把系统里原有环境搞乱。Docker 的好处是隔离干净、复制方便、搞坏了随时删掉容器重新起一个不用跟系统环境较劲。有一点需要特别提醒工具链版本和板卡端 SDK 版本必须配套。我见过有人用新版本工具链转换出的模型拷贝到旧版本 Runtime 的板子上加载失败报错信息里只说模型版本不匹配排查半天才发现是工具链和板上 Runtime 小版本不一致导致的。所以装工具链之前先去查一下你的 SDK 版本对应的工具链版本是多少把这一条锁死后面会省很多事。启动 Docker 容器的时候记得挂载一个本地目录进去用于存放模型文件和配置。转换过程中生成的文件比较多都放在容器里的话一旦容器被删就全没了。我习惯把整个项目的目录集体挂载进去模型、校准数据、输出目录都能在宿主机上直接访问。1.3 先确认三件事输入尺寸、输入通道顺序与目标类别数开始转模型之前先花五分钟确认你训练时的三个关键参数。如果不先搞清楚这三点后面每一步都可能踩雷。第一模型的输入尺寸。YOLOv5 训练时的默认尺寸通常是 640x640但你训练时改成 512、416 也不少见。需要把这个值记下来后面导出 ONNX 和填写转换配置时都要用同一个尺寸而且必须是 32 的倍数这是 YOLO 系列下采样结构决定的。我用过 640 和 512 两种只要和训练一致性能差异不大但部署时选尺寸小些推理速度会快不少。第二颜色通道顺序。这是一个非常阴险的坑。PyTorch 训练时数据集加载器读进来的一般是 RGB 顺序但很多部署工具的预处理默认 BGR如果两边对不上模型在训练集上表现正常、一到板子上识别结果就完全错乱。后面准备校准数据和推理代码时都需要统一通道顺序。具体你的场景是 RGB 还是 BGR决定了校准脚本和预处理代码怎么写。第三类别数。如果你的模型不是 COCO 的 80 类而是自己数据集的 2 类、5 类、10 类那导出的 ONNX 输出层维度会随之变化。工具链本身不知道你的类别数它只按模型结构来解析但你自己写后处理解码脚本时必须清楚类别数是多少不然输出解析全乱套。这三项信息用一张表记下来贴在工位旁边或者写到项目的 README 里都行反正别靠脑子记。后面写预处理、看量化报告、写后处理代码时都会反复用到。2. 模型导出从 .pt 到 ONNX固定住 YOLOv5 模型的输入输出2.1 官方仓库导出命令的正确打开方式YOLOv5 模型的导出优先用官方仓库自带的导出脚本。不要自己去写 torch.onnx.export 的调用代码官方脚本已经把模型结构、前向传播、动态轴这些琐碎逻辑都处理好了直接调用是最省事的。导出命令大概长这样python export.py --weights yolov5s.pt --include onnx --opset 11 --img-size 640不同版本仓库的参数名会有些差异但核心要理解的是几个关键参数在干什么--weights指定你的训练权重文件也就是 .pt 文件所在路径。--include onnx告诉脚本只需要导出 ONNX 格式不用同时导出其他格式。--opset 11是 ONNX 算子集的版本号。这个值不是越大越好工具链对算子集的支持范围有限太高了容易产生新算子导致编译失败太低了可能某些算子不支持。11 是一个相对稳妥的起点。--img-size 640与训练尺寸保持一致并且这个参数决定导出的模型输入是固定尺寸不是动态尺寸。导出成功后会生成一个 .onnx 文件和 .pt 文件在同一目录下。此刻先别急着走下一步先确认一下文件大小和生成时间如果文件只有几百 KB说明导出过程可能出问题了正常模型的大小应该在几十 MB 量级。2.2 导出后必做的 ONNX 结构检查拿到 .onnx 文件后我不是很建议直接丢进工具链而是先用可视化工具检查一遍模型结构确认输入输出符合预期。用可视化工具打开导出的 ONNX 模型你会看到模型的计算图。YOLOv5 导出后输入节点一般是固定的 1x3x640x640 形状含义是 batch 为 1、3 通道、宽高 640。输出节点通常是三个分别对应三个尺度的特征图例如 1x255x80x80、1x255x40x40、1x255x20x20其中 255 的计算方式是 3 乘5 加上类别数3 表示每个位置的三个锚框5 表示中心点坐标加宽高再加置信度共 5 个值加上类别数就是 255。检查时可以关注三点输入形状是不是静态的。如果显示 dynamic_axes说明导出的模型支持动态输入尺寸这在 BPU 编译时往往会引发额外问题需要重新固定尺寸导出。输出节点是不是三个分支且每个分支的维度符合你的类别数计算。整个计算图里有没有奇怪的算子节点比如某些自定义的后处理操作被一起导出了。检查完结构和预期一致后继续下一步。如果不一致先用可视化工具判断是哪个环节的问题多数情况是导出参数设置不对重跑一遍即可。2.3 后处理拆不拆直接决定你后面省多少麻烦这一步是整个部署流程中最容易被忽视的决策点也是很多人转换失败的根本原因。PyTorch 模型里的 YOLOv5 是完整结构包含检测头中的锚框解码、置信度筛选、NMS 非极大值抑制等后处理逻辑。这些逻辑如果在导出 ONNX 时原封不动地被包含进去转换工具链就会尝试把这些后处理算子映射成硬件支持的指令。可工具链的算子支持范围是有限的复杂的后处理器在编译时要么直接报错要么被放到 CPU 上执行导致推理性能大幅下降。正确的做法是导出时尽量让模型只保留主干网络和检测头输出的原始特征图把锚框解码、NMS 这些后处理从模型里拆出去放到推理代码里用 CPU 自己实现。这样模型结构干净工具链编译时容易通过推理速度也更快。如何拆后处理通常有两种方式如果官方仓库的导出脚本带了类似--no-nms之类的参数直接加上。如果脚本没有这个参数就需要手动修改模型代码里的检测头前向函数让导出时只输出三个尺度的原始预测张量而不执行后续解码操作。我第一次部署的时候没有拆后处理结果进入工具链编译环节就连续报错翻文档才发现问题出在后处理算子太多。后来把模型里只保留特征图输出后处理完全改用 CPU 代码实现编译一次通过推理帧率也稳了。这一步拆得越干净后面越省心。3. 量化校准数据集的准备直接影响 .bin 识别精度的隐形关卡3.1 选图原则数量、分布与场景匹配模型从浮点转换到 int8 量化模型时工具链需要一批样本数据来估算每一层激活值的分布范围这批数据就是校准数据集。校准数据集的质量直接决定量化后的模型精度损失程度但很多教程对这一块只字不提导致不少人随便找几张图凑数换来一个精度崩塌的 .bin 文件。校准数据的数量官方推荐一般在 20 到 50 张之间。我的经验是以 35 张左右为基准图片太少统计出的激活分布不够稳定图片太多也不会带来额外提升反而拖长转换时间。关键是保证图片能覆盖实际使用场景中的多样性比如目标的大小变化、光照明暗、遮挡程度、背景复杂度。如果拿办公室环境的图片去校准一个要识别户外车辆的模型量化误差一定会偏大。选图时还要注意一个细节校准图片不要包含太多同类极端情况。比如某个类别在 30 张里出现 28 次模型对这个类别过拟合其他类别的激活范围估计就失准了。尽量让图片内容在类别、场景、目标尺寸上分散一些。3.2 预处理脚本与颜色通道的统一校准数据集准备好之后需要写一个预处理脚本把这些图片处理成工具链要求的输入格式。工具链的预处理逻辑必须和 YOLOv5 训练时保持一致通常包含以下几项图像缩放保持宽高比的前提下 pad 到指定输入尺寸这一步对应训练时的 letterbox 操作。颜色通道顺序统一。前面提到过确认你的模型训练时是 RGB 还是 BGR预处理脚本中对应进行通道转换。归一化方式。YOLOv5 训练时通常是将像素值除以 255映射到 0 到 1 范围。校准脚本也要做同样处理归一化方式不一致会严重破坏激活值分布估计导致量化精度大幅下降。处理完成后把预处理好的数据按工具链要求的格式存放。具体格式会在工具链文档中说明常见的是要求把所有校准图片放在同一个目录下或者提供一个文本文件列出图片路径列表。这步出现过的一个高频问题是校准脚本里通道顺序写错工具链转换命令也跑通了模型部署到板子上推理时才发现输出异常。到时候既要查预处理、又要查量化报告排查链路拉得特别长。所以校准之前先输出一张处理后的图片在你本机上用同样的预处理流程喂给 ONNX 模型确认输出正常再用来校准。3.3 用校准数据先跑一次 onnxruntime 验证在正式投入工具链转换之前我强烈建议先做一次 ONNX 推理验证。这一步能帮你提前发现模型结构和预处理逻辑的问题避免把错误带到转换环节。具体做法是安装 onnxruntime在 Python 脚本里加载导出的 ONNX 模型拿一张预处理后的图片跑一次推理确认输出的形状和数值范围符合预期。比如三个输出头的 shape 是不是 1x255x80x80 这种预期结构输出的数值是不是正常的浮点值而不是 NaN。如果这一步发现输出形状异常那就先回到模型导出环节排查问题别急着往下走。因为哪怕你跳过这一步直接进入转换流程工具链检查也会报错但错误信息可能更晦涩不如自己先查清楚。验证通过后这一张图片的结果也可以作为你后续部署推理时的基准结果。板子上跑完推理之后和这个基准结果对比一下可以快速判断部署链路是否正确。4. 核心转换ONNX 到 .bin 的配置、编译与分析4.1 配置文件的几个关键字段在准备把 ONNX 转换到 .bin 文件之前工具链会要求你提供一个配置文件。这个配置文件通常以 yaml 格式编写里面声明了模型转换的各种参数是整个转换过程的核心。配置文件里必须关注的关键字段有这几项配置字段含义填写建议model_type模型输入格式填写 onnxmodel_pathONNX 文件路径指向你导出的 .onnx 文件input_shape模型输入形状必须与导出时固定尺寸一致例如 1x3x640x640input_layout输入数据排列方式根据工具链要求选择 NCHW 或 NHWCcalibration_dataset校准数据路径指向预处理后的校准图片目录normalization归一化参数与训练时保持一致例如 mean 0、std 255配置里还有一个需要注意的选项是 batch 的设置。BPU 推理通常只支持 batch 为 1 的输入所以配置里最好将输入 batch 固定为 1。我见过有人把 batch 配成 8转换过程报错改回 1 之后就顺利通过。batch 大于 1 的模型在部署阶段还要额外处理数据拆分完全没有必要。input_layout 这个字段也需要特别留意。PyTorch 模型导出到 ONNX 一般是 NCHW 格式也就是通道、高度、宽度依次排列但有些工具链默认 NCHW有些默认 NHWC。填反了会直接导致推理结果错误而且这种错误不容易通过报错信息发现。配置前务必确认工具链默认的 layout 格式。4.2 转换命令与编译输出解析配置写好之后就可以执行转换命令了。工具链提供命令行方式和 Python API 方式实际操作中用命令行更直观。命令通常形如hb_mapper makertbin --model-type onnx --model-path yolov5s.onnx --config yolov5s_config.yaml如果你的工具链版本不同具体命令名可能略有差异。不确定时可以在工具链的 docs 目录里查找模型转换使用说明一般在docs或example目录下能看到完整的命令示例。转换过程会输出一系列日志信息大致会经历以下几个阶段模型解析与结构检查检查 ONNX 模型是否能被工具链完整识别。算子映射把 ONNX 算子映射到量化编译器支持的指令集。量化校准基于校准数据统计激活值分布生成量化参数。编译生成最终生成 .bin 文件。转换完成后输出目录里会有几个重要文件一个 .bin 文件这就是最终部署到 RDK X5 上的模型文件。一些包含模型信息和转换日志的文件记录量化细节与性能估算。可能还有一个精度评估报告里面包含每一层的量化前后误差分析。如果转换过程中出现错误日志里会标出是哪个算子、哪一层出问题。大多数情况下错误信息会具体到算子名称和层序号拿着这个信息去查工具链文档就能知道这个算子是被阻止还是被降级到 CPU 执行。4.3 量化报告怎么看、哪一层最可疑转换完成后生成的量化报告是评估 .bin 模型质量的重要依据。很多人拿到报告根本不看直接复制到板子上跑结果发现精度崩了才开始排查效率太低。量化报告里通常包含每一层在量化前后的余弦相似度、均方误差等指标。我的建议是重点关注余弦相似度这个指标直观地反映了量化前后该层输出的相似程度数值越接近 1 越好。经验上如果某层的余弦相似度低于 0.9这一层很可能就是精度损失的来源。看到相似度偏低的层之后可以选择对这几层做混合量化处理也就是让这些敏感层保持浮点计算其余层正常量化。混合配置的方式在工具链文档里都有详细说明实际操作为了效果调优会花掉比转换本身更多的时间。量化报告还能间接暴露校准数据的问题。如果整份报告都显示相似度偏低而不是个别层偏低那大概率不是模型的错而是你的校准数据准备错了。重新检查预处理的归一化方式、通道顺序、图片分布修正后重新转换往往能解决大规模精度崩坏的问题。5. 部署上板在 RDK X5 上加载 .bin 并跑通推理5.1 拷贝文件与运行环境准备拿到 .bin 文件之后接下来就是把它部署到 RDK X5 上跑起来。这一阶段的第一个动作就是把模型文件和相关依赖库拷贝到板上。需要拷贝到板上的文件至少包括.bin 模型文件这是推理的核心。推理脚本、后处理脚本、标签文件等。测试图片或测试视频用于验证推理效果。拷贝完成后在板上创建一个项目目录把所有文件归置整齐。我习惯在板上建一个deploy_test目录模型文件放model子目录代码放src子目录测试素材放data子目录这样后续调试时路径清晰不会把板上的目录弄得一团乱。板上运行推理还需要安装 Runtime 库不同 SDK 版本的库名和安装方式会有区别。拿到板子之后优先看板卡系统自带的 SDK 文档确认推理开发库是否已经预装。通常系统镜像会预装一套如果你用的是 Docker 工具链转换出的模型需要注意模型版本与 Runtime 库的兼容性版本不匹配时可在模型加载阶段直接报错。5.2 推理脚本骨架与预处理/后处理要点推理脚本的核心思路是加载 .bin 模型 - 读取图像并预处理 - 调用推理接口 - 解析输出 - 做后处理 - 得到最终检测框。完整的推理脚本骨架大致如下import numpy as np # 导入地平线 Runtime 库不同 SDK 版本模块名不同以官方文档为准 from horizon_runtime import Model # 加载模型 model Model(yolov5s.bin) # 读取图像 img load_image(data/test.jpg) # 预处理letterbox 通道转换 归一化 input_data preprocess(img) # 推理 outputs model.run(input_data) # 后处理锚框解码 置信度过滤 NMS boxes, scores, class_ids postprocess(outputs) # 绘制结果 draw_result(img, boxes, scores, class_ids)推理脚本中最关键也最耗时间的部分是后处理。由于导出模型时我们把后处理拆出去了这一步就得自己补回来。YOLOv5 的后处理逻辑包括从三个尺度的特征图中解码出预测框的位置和类别置信度、根据置信度阈值过滤低分框、执行 NMS 合并重叠框。这段逻辑写起来有一定代码量建议从 YOLOv5 官方仓库中把原来的后处理代码改写成 numpy 版本移植过来比完全自己造轮子靠谱得多。这里有一个实操上的建议:推理接口返回的输出通常是三维或四维数组每个尺度一个数组。写代码之前先打印一下每个输出数组的 shape确认和你预期的一致。如果返回的 shape 和模型导出的 shape 对不上多半是模型加载时工具链做了某种裁剪需要回头检查模型转换阶段的配置。预处理部分的代码和校准数据预处理几乎一模一样letterbox 的填充值、归一化的参数、通道顺序都要严格保持一致。我遇到过一个问题训练时 letterbox 用灰色填充部署时预处理忘了设置填充值结果填充默认成了黑色整个推理结果的精度和训练时差距很大。这种低级错误一旦发生排查起来很耗时间所以建议把预处理函数抽取出来反复检查和训练代码的一致性。5.3 首帧推理验证与性能指标确认第一次跑通推理之后不要急着测速度先验证结果的正确性。拿一张你跑过 onnxruntime 推理的同一张图片在板上再跑一次把两边的检测框结果对比一下。由于浮点量化和 int8 量化的差异两边的检测框不会完全一样但关键的检测目标应该保持一致置信度可能有小幅波动。如果板上的结果和基准结果明显不一致比如检测目标完全不同那说明部署链路里某个环节出了错。结果验证通过之后再来评估速度指标。在推理脚本里用系统时间函数包住模型推理部分统计单帧推理耗时换算成 FPS。同时也可以观察板上的系统负载情况确认模型是主要在 BPU 上运行而不是被降级到了 CPU。如果发现有明显的 CPU 占用飙升检查模型转换日志看看是不是存在算子被降级到 CPU 执行的情况。6. 避坑合集从导出到推理最常踩的四个坑与排查路径6.1 算子不支持SiLU 与 Focus 的处理思路YOLOv5 网络结构中有 Focus 层激活函数通常使用 SiLU。这些结构在 PyTorch 里跑得很顺但到了工具链里可能会变成绊脚石。不同工具链版本对这两种算子的支持程度不一样。新版工具链基本都能直接编译 Focus 层但如果你用旧版本工具链看到报错里出现 Focus、SiLU、Slice 之类的字样基本就是算子不受支持。排查思路先去工具链文档的算子支持列表里查一下当前版本支持哪些算子。如果 SiLU 不受支持要么升级工具链版本到支持 SiLU 的版本要么在导出 ONNX 模型时手动把 SiLU 替换成等价的计算组合。如果 Focus 层有兼容问题还可以尝试把 Focus 层的切片操作拆分成多个卷积操作这样工具链更容易识别。我在早期版本工具链上确实碰到过 Focus 层编译不通过的情况当时把模型里的 Focus 层替换成等价的卷积组合转换就顺利过了。处理这类问题原则就是尽量让模型结构变得常规化减少使用工具链可能不认识的复杂操作。6.2 精度崩量化报告与通道顺序联合排查部署后模型精度大幅下降是量化部署里最令人头疼的问题。排查时会发现多种可能原因我建议按下面的顺序一步步缩小范围第一步看量化报告。如果报告显示所有层普遍相似度偏低优先考虑校准数据的预处理是否和训练一致尤其是通道顺序和归一化参数。这一步是最大概率出问题的地方值得优先排查。第二步看是否是少数几层的相似度特别低。如果是个别敏感层导致的精度损失可以做混合量化把那些层排除在量化范围之外。工具链配置中会有相关开关具体字段名在文档里查询。第三步检查推理阶段的预处理。有些时候 .bin 文件本身没问题而是板上推理脚本的预处理写错了。建议拿同一个基准图片分别在 onnxruntime 和板卡 Runtime 上跑一遍对比输出结果能快速定位是模型转换的问题还是推理代码的问题。上面三步做完大部分精度问题都能定位到根因。如果依然找不到方向那就回到校准数据本身看看图片是否覆盖了实际使用场景这是最后一个需要检查的环节。6.3 shape 不匹配先查预处理再查输出头推理时报出输入 shape 不匹配的错属于高频问题。这类错误信息通常直接提示你期望的输入形状和实际传入的形状不一致。排查时先检查预处理代码里的 resize 或 letterbox 逻辑确认最终输入尺寸和模型输入的尺寸完全一致。这个过程中最容易忽略的是 batch 维度。预处理代码如果漏掉了扩展 batch 维度的操作传入的数据是 3x640x640而模型期望的是 1x3x640x640自然会报错。接着检查输入通道数。传入数据的通道数如果不是 3也会报 shape 错尤其是某些库读取图像时可能会输出 4 通道的 RGBA 数据。检查这一步时直接在预处理代码里打印一下数组的 shape一目了然。如果预处理没有问题那就再回头检查 ONNX 导出时的固定尺寸是否正确以及转换配置里 input_shape 是否写错。这三层检查下来shape 不匹配基本都能找到原因。6.4 性能不达标看日志确认 BPU 利用率模型部署好了功能完全正常但帧率远低于预期这种情况通常和算子降级有关。工具链在转换过程中不一定所有算子都能放到 BPU 上执行某些不支持的算子会被静默地切到 CPU 上跑。BPU 是专门为神经网络计算设计的CPU 处理算子的效率天差地别哪怕只有几个算子降级整体推理速度都会大幅下跌。排查性能问题时可以通过工具链生成的日志查看每一步的算子分配情况。日志里如果出现类似 CPU 算子字样就说明存在算子降级。常见的降级原因包括模型里存在工具链不支持的特殊算子、部分算子的输入布局不满足硬件要求、模型结构过于复杂导致切分开销过大。解决性能问题的手段有限修改模型结构让算子更常规化、升级工具链版本获得更好的计算图优化、或者适当调整输入尺寸减少计算量。其中输入尺寸是性价比最高的手段从 640 降到 512计算量直接降到原来的六成左右对精度的影响通常可控。我在实际使用中还有一个习惯转换完成后先看日志里估算的性能数据如果和预期差距太大就别急着上板先在模型结构上想办法优化。这比等部署完再回来查性能问题高效得多。整个流程踩下来的经验就一条每一步都要留证据模型导出后先验证结构校准数据准备后先跑基准转换完成后先看报告。把排查工作前置后面真正上板时就会顺很多。
阅读完成 · 觉得有帮助?
咨询建站