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_FLOAT32、FLOAT16、INT8、UINT8。
3. NPU 内部计算精度
Runtime 可能在进入 NPU 前做类型和 layout 转换。应用传 FP32,不等于整个网络就在 NPU 上以 FP32 执行。
因此,不能只根据“模型是 FP16”就猜应用 buffer 一定是 FP16,也不能只看到 API 传 FP32 就认定模型未量化。
唯一可靠的入口:查询模型属性
初始化 context 后,查询输入 tensor:
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:
图像采集
→ 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 与硬件可能做混合精度处理。
建议的验收
- 打印每个输入/输出 tensor attr;
- 固定一组输入,比较普通输入与 zero-copy 结果;
- 测量 Runtime 转换前后的 CPU 与延迟;
- 验证 stride、layout 和量化参数;
- 把模型 hash 与 attr dump 一起纳入部署记录。
结论很简单:RKNN 输入类型不是平台常量,而是模型文件与调用路径共同定义的运行时合同。