news 2026/8/28 8:09:15

将训练好的模型转换并部署在NPU上的详细步骤(工具链功能)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
将训练好的模型转换并部署在NPU上的详细步骤(工具链功能)

最近瑞芯微以及地瓜RDK上做NPU/BPU的神经网络部署,所以在这里总结一下
瑞芯微模型转化链接:https://github.com/airockchip/rknn-toolkit2

名词解释

模型转换(整体流程)

PyTorch / ONNX / TensorFlow 转成 硬件适配的格式
.pt / .onnx转化为.rknn(瑞芯微)/.bin(地瓜)

.rknn(瑞芯微)/.bin(地瓜)均为二进制文件

解析计算图
映射支持的算子
保存权重和网络结构
适配目标芯片的运行方式

计算图(Computational Graph)

节点(Nodes):代表运算操作(如卷积、加法、矩阵乘法)。
边(Edges):代表数据张量(Tensor)的流动方向。

图优化

对模型的计算图做重写和精简,让推理更高效。

常见优化包括:

  1. 删除无用节点
  2. 融合多个算子(比如:×3×2 - > ×6)
  3. 折叠常量
  4. 调整数据流顺序
  5. 减少内存拷贝
  6. transpose 消除

比如:Conv + BatchNorm + ReLU 优化成一个更高效的组合执行方式。

算子适配/算子映射

把模型里的每一种运算,映射到目标硬件或推理引擎支持的实现上。

深度学习算子 NPU 适配总结表

算子名称所属类别核心功能NPU 适配友好度主要适配问题 / 原因常见优化/处理方法
Conv计算类图像/特征提取的核心操作,进行滑窗加权求和。🟢 原生友好无。所有NPU都为卷积设计了专门的加速单元。作为融合优化的核心,常与BN、激活函数融合。
BatchNorm (BN)计算类推理时对特征图进行标准化(线性变换)。🟢 原生友好无。但在部署时为了性能,需要被处理掉。算子融合:在图优化阶段,将其参数“折叠”进相邻的Conv层权重中,从而被消除。
ReLU激活函数引入非线性:f(x) = max(0, x)🟢 原生友好无。硬件指令集几乎都原生支持。算子融合:被硬件直接吸收进Conv等算子的激活选项中,零开销。
SiLU (Swish)激活函数YOLOv5/v8主力激活:f(x) = x * sigmoid(x)🟢 良好较老的NPU可能无原生指令支持。融合/替换:新NPU支持融合;老NPU可能拆解为sigmoid + mul,也可考虑替换为ReLU
MaxPool池化/采样类下采样,取池化窗口内的最大值。🟢 原生友好无。NPU通常有专门指令支持。关注kernel_sizestridepadding等参数是否符合硬件限制。
AveragePool池化/采样类下采样,取池化窗口内的平均值。🟢 原生友好无。NPU通常有专门指令支持。关注kernel_sizestridepadding等参数是否符合硬件限制。
GlobalAveragePool池化/采样类对整个特征图取全局平均值,常用于分类头。🟢 原生友好无。通常可高效实现。常作为YOLO分类头或辅助头的输出层,可直接被NPU支持。
Add元素级运算类逐元素相加,常用于残差连接(Residual Connection)。🟢 原生友好无。这是最基础的逐元素操作。常与Conv融合,形成Conv + Add的优化模式。
Mul元素级运算类逐元素相乘,常用于注意力机制中的加权。🟢 原生友好无。NPU支持良好。注意Mul的第二个输入是常量还是动态张量,广播规则是否支持。
Concat张量操作类沿指定维度拼接多个张量,常用于特征融合(如FPN)。🟢 良好拼接的维度信息必须在编译时确定(静态)。图优化:在可能的情况下,与前后算子融合消除。
Reshape张量操作类改变张量形状,但总元素数不变。🟢 良好本身开销极小。问题在于其输入形状可能来自动态算子。图优化消除:编译器常可优化掉,不产生实际指令。
Squeeze张量操作类移除张量中所有尺寸为1的维度。🟢 良好无。编译时可确定。常被编译器自动优化或与Reshape合并。
Unsqueeze张量操作类在指定位置插入一个尺寸为1的维度。🟢 良好无。编译时可确定。常被编译器自动优化或与Reshape合并。
Cast张量操作类改变张量的数据类型(如float32 → int64)。🟢 良好需确认目标硬件是否支持该类型转换。通常在图优化阶段保留,注意类型转换是否有精度损失。
Flatten张量操作类将张量展平为一维。🟢 良好无。编译时可确定。常被编译器优化,或与Gemm等算子融合。
Split张量操作类沿指定维度将一个张量分割成多个子张量。🟢 良好分割的维度和数量必须静态确定。常与Concat配对出现,有时可被优化消除。
ConvWithBnAct融合优化类部署工具生成的融合算子,并非原始模型中的独立算子。🟢 优化产物无。这是所有NPU都追求的最终目标形态图优化器自动生成,代表了最佳的适配结果。
Resize池化/采样类上采样或下采样特征图,改变空间尺寸(如双线性插值、最近邻插值)。🟢 良好主要适配问题在于modescales是否被目标NPU支持。scales须为常量。首选mode='nearest'(硬件友好)。若必须用linear,确认NPU是否支持;不支持时可用TransposedConv替代或写Custom Op。
Softmax激活/归一化类将输入向量映射为概率分布,所有元素值在(0,1)之间且和为1。🟡 中等需确认NPU对axis(沿哪个维度做Softmax)参数的支持范围。某些NPU要求axis必须是最后一维。尽量确保axis=-1(最后一维)或硬件支持的其他静态轴;性能敏感时可考虑用LogSoftmax替代(如果后续接NLLLoss)。
Transpose张量操作类交换张量维度(如 NCHW <-> NHWC)。🟡 中等导致内存重排,增加耗时;且会阻断相邻算子的融合。消除/吸收:通过修改相邻算子的数据布局参数来吸收,或在图分析中被消除。
Slice张量操作类从张量中切取一部分子集(如startsends指定范围)。🟡 中等startsendssteps等参数必须为常量(静态),动态切片极难处理。尽量确保切片参数在编译时固定。若不可避免,考虑用StridedSlice替代或写Custom Op。
Gather选择/过滤类根据提供的索引,从张量中取出对应数据。🟡 中等本身功能简单。问题在于索引(indices)的来源。若索引来自固定常量,则无问题;若来自TopK等动态算子,则需整体替换为Custom Op
Upsample池化/采样类(注:与Resize功能重叠,ONNX中已统一为Resize)上采样特征图。🟡 中等与Resize相同,关注modescales同Resize。
MatMul矩阵运算类执行矩阵乘法,在注意力机制、全连接层中常见。🟡 中等权重形状需对齐;需确认NPU对批次矩阵乘(BMM)的支持。确保输入维度是静态的;考虑是否有Conv 1x1替代的可能。
ReduceSum规约运算类沿指定维度求和,降低张量维度。🟡 中等需确认axes(沿哪个轴)和keepdims(是否保留维度)的支持情况。确保axes参数是常量。
ReduceMean规约运算类沿指定维度求平均,降低张量维度。🟡 中等ReduceSum类似,关注axeskeepdims确保axes参数是常量。
Mish激活函数YOLOv4/v7使用的激活函数:x * tanh(ln(1+exp(x)))🔴 困难表达式复杂,老NPU无原生支持。拆解为多个基础算子后,计算量大且精度可能下降。算子替换:建议直接替换为SiLU并重新微调模型,是性价比最高的方案。
LayerNorm归一化类对特征图沿特征维度进行标准化,在Transformer结构中常见。🔴 困难无法像BN那样被融合消除,需要NPU原生支持;对精度敏感。确认NPU是否支持;若性能不佳,考虑混合精度或写Custom Op。
InstanceNorm归一化类对每个样本的每个通道独立做标准化,在风格迁移等任务中常见。🔴 困难与LayerNorm类似,无法简单融合,需要硬件原生支持。确认NPU是否支持;若支持不好,考虑修改网络结构替代。
Shape张量操作类获取张量各维度的大小(运行时动态值)。🔴 困难输出为动态值,会引入数据依赖,导致NPU编译器无法进行静态内存分配。消除:尽量在模型导出时固定输入尺寸,避免使用依赖Shape的动态逻辑。
TopK选择/过滤类在指定维度上选取数值最大(或最小)的前K个值及索引。🔴 困难涉及排序或部分排序逻辑,NPU的并行矩阵单元不擅长处理串行逻辑。替换/插件:常与Gather配合,整体替换为Custom Op(如NMS插件)。
Exp元素级运算类逐元素计算指数(e的x次方)。🔴 困难指数运算对NPU硬件支持要求较高,可能精度不足或速度慢。确认NPU是否支持;若精度不达标,考虑替换或使用查找表(LUT)近似。
Log元素级运算类逐元素计算自然对数。🔴 困难与Exp类似,对硬件支持要求高。确认NPU是否支持;若精度不达标,考虑替换或使用查找表(LUT)近似。
Pow元素级运算类逐元素计算幂运算(x的y次方)。🔴 困难指数部分须为常量,否则NPU难以处理。确保指数是常量(initializer);若指数是动态张量,基本无法适配。
NonZero选择/过滤类找出张量中所有非零元素的索引。💀 极难非零元素数量完全动态,输出形状不确定(变长),NPU基本无法处理。绝对避免:这是NPU的“杀手算子”,任何部署场景下都应极力避免。
Unique选择/过滤类对张量进行去重,返回唯一值。💀 极难去重后元素数量动态,输出形状不确定,与NonZero问题性质相同。绝对避免:几乎无法在NPU上直接部署,需在模型设计阶段规避。

适配时可能会遇到:
硬件支持某个算子,但参数有限制
某些算子需要拆分成多个更基础的操作
某些算子只能放到 CPU 上跑,不能放到 NPU 上
需要针对芯片特性做特殊实现

custom op 自定义算子处理

见官方文档:
https://github.com/airockchip/rknn-toolkit2/tree/master/rknn-toolkit2/examples/functions/custom_op/gen_custom_onnx_from_pytorch/register_pytorch_op

INT8 量化

把原本用 FP32(float point,浮点数) 计算的模型参数和激活值,压缩成 INT8(8 位整数) 表示。

目的:
更省内存
推理更快
更适合 NPU / 边缘设备
减少模型大小,加速深度学习推理的速度

需要:
一批代表性数据做校准
计算浮点到整数的映射关系
尽量减少精度损失

scale(缩放因子)和 zero_point(零点)估计方法

  1. Direct Min-Max(直接最大最小值法)
  2. Percentile / Histogram(百分位 / 直方图法)
  3. KL 散度(相对熵)

模型转换整体流程(以瑞芯微为例)

https://github.com/airockchip/rknn-toolkit2

训练好的模型(网络结构、权重参数、输入输出定义)+ 校准数据

模型解析

  1. 模型文件 → 计算图
  2. 权重文件 → 参数张量

    图优化

    算子映射

    量化

    生成 .rknn 文件

模型部署整体流程

设备端加载

输入预处理

rknn runtime 执行

输出张量

后处理—工作重点

最终结果

瑞芯微官方演示流程

https://github.com/airockchip/rknn-toolkit2/tree/master/rknpu2/examples

地瓜BPU模型部署

Model Zoo

https://github.com/D-Robotics/rdk_model_zoo/blob/rdk_x5/README_cn.md
一些训练好的模型示例
原始模型(PyTorch/ONNX)→ 定点量化转换 → 推理运行 → 结果解析 →

c/c++规范

samples/vision/yolov5/runtime/cpp

配置结构体
每个模型必须定义一个独立的配置结构体,用于存放模型初始化和运行时参数;
模型类

构造函数 - 模型对象创建
职责:

  1. 创建模型对象本身;
  2. 不执行任何重资源操作、不加载模型、不分配 Tensor 内存;
  3. 为后续显式调用 init() 做准备;

init - 模型资源初始化
职责

  1. 加载模型(如通过 Horizon DNN API);
  2. 查询模型元信息;
  3. 分配输入 / 输出 tensor 所需的内存;
    返回值
    返回错误码,0 表示成功,非 0 表示失败;
推理相关函数
pre_process – 预处理

职责:
将用户输入数据转换为 runtime 所需格式;
对不支持的 image_format 应及时报错,而非静默失败;
模型的input tensor必须以引用方式传递并写入,便于后续多线程/并行扩展;

infer – 前向推理执行

职责:
创建推理任务;
提交任务到 BPU 调度器;
等待任务完成;
释放任务句柄;
可通过参数配置底层硬件的调度参数,如sched_param,参数设置缺省值;
输入输出tensor需以形参的形式传入,便于后续多线程/并行扩展;
返回值:
当推理出现底层错误时返回错误码,0为无错误,非0为异常;

post_process – 后处理

职责:
将 raw 输出 tensor 转换为业务语义结果;
模型输出tensor需以形参的形式传入,便于后续多线程/并行扩展;

算法工具链

https://developer.d-robotics.cc/api/v1/fileData/x5_doc-v126cn/index.html
地平线BPU架构的计算平台使用的是int8的计算精度

训练后量化(PTQ)Post-Training Quantization 量化感知训练(QAT)Quantization-Aware Training

训练后量化PTQ:使用一批校准数据对训练好的模型进行校准,将训练过的FP32模型直接转换为定点计算的模型,过程中无需对原始模型进行任何训练,只需要对几个超参数进行调整就可以完成量化过程, 且过程简单快速,无需训练。

量化感知训练QAT:是将训练过的模型量化后又再进行重训练。由于定点数值无法用于反向梯度计算,实际操作过程是在某些OP前插入伪量化节点(fake quantization nodes), 用于在训练时获取流经该OP的数据的截断值,便于在部署量化模型时对节点进行量化时进行使用。我们需要在训练中通过不断优化精度来获取最佳的量化参数。

为什么定点数值无法用于反向梯度计算?

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

一条消息的旅程:RabbitMQ 学习与实践(四)

专栏&#xff1a;RabbitMQ 进阶之路 个人主页&#xff1a;手握风云 目录 一、SpringBoot 整合 RabbitMQ 1.1. 环境准备 二、四大常用模式 2.1. Work Queue 工作队列模式 2.2. Publish/Subscribe 发布‑订阅 2.3. Routing 路由模式 2.4. Topics 通配符模式 一、SpringBoo…

作者头像 李华
网站建设 2026/8/28 8:08:11

低光增强实战:从基线到NTIRE Twilight Cowboy挑战的完整工程链路

图像增强赛道在近年计算机视觉挑战中热度很高&#xff0c;低光增强则是其中最能体现“从坏输入到好输出”的一类任务。NTIRE 2026 Low-light Enhancement 赛道以 “Twilight Cowboy Challenge” 为名&#xff0c;直接指向黄昏低照度、高动态范围、复杂光源混合的真实拍摄场景。…

作者头像 李华
网站建设 2026/8/28 8:04:52

学术论文写作全流程指南:从IMRaD结构到语言规范与修改策略

1. 从“写出来”到“写得好”&#xff1a;论文书写的核心价值 写论文&#xff0c;大概是每个学生和研究者都绕不开的“必修课”。很多人觉得&#xff0c;论文嘛&#xff0c;不就是把研究结果整理一下&#xff0c;按照格式要求填进去就行了吗&#xff1f;我最初也是这么想的&…

作者头像 李华
网站建设 2026/8/28 8:04:42

STM32定时器深度解析:从时钟树到PWM与输入捕获实战

1. 项目概述&#xff1a;为什么STM32的定时器是嵌入式开发的“心脏”&#xff1f; 如果你刚开始接触STM32&#xff0c;可能会觉得定时器&#xff08;Timer&#xff09;只是一个用来计时的简单外设&#xff0c;设置一个时间&#xff0c;让它到点触发个中断就完事了。但当你真正深…

作者头像 李华
网站建设 2026/8/28 8:04:06

Java项目练习题

购买两款披萨父类package com.lqy.test01; //父类&#xff1a;披萨 public class Pizza {//属性private String name;//名称private int size;//尺寸大小private int price;//价格//方法,为这三个属性提供set和get方法public String getName() {return name;}public void setN…

作者头像 李华