4. 模型转换基础

在RK3588边缘AI交付链路中,模型转换是连接训练框架与NPU运行时的关键一环。训练侧常见的ONNX、TensorFlow、PyTorch模型并不能被RKNPU直接执行,必须经过RKNN-Toolkit2转换为.rknn格式,才能由RKNN Runtime加载并调度NPU。理解转换流程、量化配置与常见报错,是保证部署成功率的第一道门槛。

一、核心原理:为什么需要RKNN转换

RK3588内置NPU,其指令集、算子实现、内存布局与GPU/CPU不同。RKNN-Toolkit2在PC端完成三件事:图解析、图优化与量化、序列化为RKNN模型。图解析阶段把不同框架的计算图统一为内部IR;图优化阶段做算子融合、常量折叠、布局转换;量化阶段把FP32权重与激活值映射到INT8/INT16,以换取NPU上的高吞吐与低功耗。最终产物包含权重、量化参数、算子描述与内存规划信息。

转换不是“格式另存为”,而是“图重写 + 量化校准 + 硬件适配”。同一份ONNX,不同target_platform、不同量化方式,得到的精度与性能可能差异很大。

二、方法流程:从训练框架到RKNN

推荐统一先转ONNX,再转RKNN。PyTorch可用torch.onnx.export导出;TensorFlow可用tf2onnx或SavedModel路径;ONNX作为中间表示,算子覆盖较全,便于排查。整体流程如下:

  1. 导出中间模型:固定输入shape,去除训练专用节点(Dropout、训练标志等)。
  2. 准备校准集:100~300张有代表性的图片/张量,预处理必须与推理时一致。
  3. 配置RKNN:设置target_platform、mean_values、std_values、量化算法、优化等级。
  4. 执行build:加载模型、build、export_rknn,得到.rknn文件。
  5. 精度验证:在PC端用RKNN模拟器对比原模型输出,确认余弦相似度与最大误差。
  6. 板端联调:用RKNN Runtime加载,检查初始化日志、输入输出属性与耗时。
RKNN 模型转换主链路与配置要点 PyTorch torch.onnx.export TensorFlow tf2onnx / SavedModel ONNX 统一中间表示 RKNN-Toolkit2 load_onnx / load_tflite config + build export_rknn 关键配置参数 target_platform: rk3588 mean_values: [[123.675,...]] std_values: [[58.395,...]] quantized_dtype: w8a8 quantized_algorithm: normal optimization_level: 3 do_quantization: True 校准数据集 100~300 张,预处理一致 dataset.txt 路径列表 model.rknn 权重 + 量化参数 算子描述 + 内存规划 RKNN Runtime 加载 常见失败点:输入shape未固定 · 校准集与推理预处理不一致 · 算子不支持 · 量化溢出 · target_platform 写错

三、配置参数:决定精度与性能的开关

RKNN-Toolkit2的配置集中在rknn.config()与rknn.build()。以下参数最常影响结果:

参数作用建议值/注意点
target_platform指定目标芯片RK3588必须写rk3588,写错会导致算子不支持
mean_values / std_values归一化参数必须与训练预处理一致,顺序为RGB或BGR要对齐
do_quantization是否量化True为INT8,False为FP16,精度优先可先关量化验证
quantized_dtype量化数据类型w8a8常用;w16a16精度更高但速度下降
quantized_algorithm量化算法normal/mmse;mmse对激活分布更友好
optimization_level图优化等级3为默认,遇到算子融合异常可降到0排查
batch_size批大小边缘端通常为1,多batch需确认NPU内存

一个典型的转换脚本如下:

from rknn.api import RKNN

rknn = RKNN(verbose=True)
rknn.config(
    target_platform='rk3588',
    mean_values=[[123.675, 116.28, 103.53]],
    std_values=[[58.395, 57.12, 57.375]],
    quantized_dtype='w8a8',
    quantized_algorithm='mmse',
    optimization_level=3
)
ret = rknn.load_onnx(model='yolov5s.onnx')
ret = rknn.build(do_quantization=True, dataset='dataset.txt')
ret = rknn.export_rknn('yolov5s.rknn')
rknn.release()

四、案例场景:YOLOv5s 转换与精度对齐

以YOLOv5s为例,PyTorch导出ONNX时需固定输入为1x3x640x640,并去掉后处理中的动态shape。转换后若发现检测框偏移,优先检查三点:一是mean/std是否与训练一致;二是ONNX输入是否为RGB、NCHW;三是校准集是否覆盖了夜间、逆光等场景。若mAP下降超过2%,可改用mmse量化或对敏感层保留FP16。板端运行时,用rknn.query(RKNN_QUERY_IN_OUT_NUM)确认输入输出数量,避免把后处理节点也塞进模型导致NPU无法执行。

建议先关闭量化(do_quantization=False)跑通全链路,确认模型结构与输入输出正确后,再开启INT8量化调优。这样能把“结构错误”和“量化误差”分开定位。

五、误区与复盘

  • 误区一:直接转PyTorch的.pth。RKNN-Toolkit2不直接吃.pth,必须先导出ONNX或TorchScript。
  • 误区二:校准集随便找几张图。校准集分布决定量化scale,分布偏差会直接拉低精度。
  • 误区三:忽略算子支持列表。遇到Unsupported op时,需在ONNX阶段替换为等价算子,或把该段回退到CPU。
  • 误区四:只看转换成功,不做精度对比。转换成功不等于结果正确,必须用同一输入对比原模型与RKNN模拟器输出。
  • 误区五:target_platform写成rk3568。平台不匹配会导致NPU无法加载或性能异常。
若build阶段报“E build: The input shape is not fixed”,说明ONNX仍含动态维度,需在导出时指定input_names与dynamic_axes,或使用onnxsim固定shape后再转换。