Skip to content

RKNN 输入到底是 FP32、FP16 还是 INT8

“这个 RKNN 模型输入是 FP32、FP16 还是 INT8?”看似是一个类型问题,实际混在一起的至少有三层:模型如何量化、Runtime API 接受什么、NPU 最终怎样计算。

先拆开三个概念

1. 模型精度

模型转换时决定权重与算子的精度,例如非量化模型或 INT8 量化模型。它描述的是 .rknn 图的属性。

2. Runtime 输入 dtype

应用传给 rknn_inputs_set() 或 zero-copy buffer 的数据类型,例如 RKNN_TENSOR_FLOAT32FLOAT16INT8UINT8

3. NPU 内部计算精度

Runtime 可能在进入 NPU 前做类型和 layout 转换。应用传 FP32,不等于整个网络就在 NPU 上以 FP32 执行。

因此,不能只根据“模型是 FP16”就猜应用 buffer 一定是 FP16,也不能只看到 API 传 FP32 就认定模型未量化。

唯一可靠的入口:查询模型属性

初始化 context 后,查询输入 tensor:

cpp
rknn_input_output_num io_num{};
rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));

for (uint32_t i = 0; i < io_num.n_input; ++i) {
    rknn_tensor_attr attr{};
    attr.index = i;
    rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &attr, sizeof(attr));

    printf("input[%u] type=%d fmt=%d dims=%u size=%u stride=%u\n",
           i, attr.type, attr.fmt, attr.n_dims,
           attr.size, attr.size_with_stride);
}

重点检查:

  • attr.type:native dtype;
  • attr.fmt:NCHW / NHWC 等 layout;
  • attr.dims:维度顺序;
  • attr.size_with_stride:实际 buffer 容量;
  • 量化参数,如 scale 与 zero point。

pass_through=0 为什么能传 FP32

普通 rknn_inputs_set() 路径中,pass_through=0 允许 Runtime 根据描述做预处理或类型转换。应用可以传 FP32,Runtime 再转成模型需要的类型。

这让接口更易用,但转换会消耗 CPU 和内存带宽。如果前处理原本就在 RGA/OpenCL 上,随后又把数据交给 Runtime 做一次 layout/dtype 转换,zero-copy 的意义会被削弱。

高性能路径的原则

对于固定模型的板端部署,尽量让上游直接产出模型 native contract:

text
图像采集
  → resize / crop
  → color convert
  → normalize / quantize(按模型要求)
  → native dtype + native layout buffer
  → RKNN zero-copy input

但不要为了“少一次转换”凭经验硬编码 INT8 或 FP16。先查询 .rknn,再决定 OpenCL/RGA 输出格式和 buffer stride。

常见误区

  • “RK3588 NPU 固定吃 INT8”:错误,取决于模型与 runtime 合同;
  • “FP16 模型就必须从相机直接给 FP16”:不一定,API 可能允许 FP32 转换;
  • “UINT8 输入代表模型一定是 UINT8 量化”:还要结合量化参数和转换路径;
  • 只看 size 不看 size_with_stride:zero-copy buffer 容易越界或产生错位;
  • 把模型精度与内部算子精度画等号:Runtime 与硬件可能做混合精度处理。

建议的验收

  1. 打印每个输入/输出 tensor attr;
  2. 固定一组输入,比较普通输入与 zero-copy 结果;
  3. 测量 Runtime 转换前后的 CPU 与延迟;
  4. 验证 stride、layout 和量化参数;
  5. 把模型 hash 与 attr dump 一起纳入部署记录。

结论很简单:RKNN 输入类型不是平台常量,而是模型文件与调用路径共同定义的运行时合同。

参考:RKNN-Toolkit2 官方仓库