第4章:RKNN转换初探——用Python API搞定模型转换
好,咱们进入正题。上一章我们把环境搭好了,这一章就来点实际的——用Python API把模型转成RKNN格式。说实话,我第一次接触RKNN转换时,心里也没底。但跑通一次之后,你会发现这东西其实挺直白的。
4.1 模型转换的基本流程
RKNN转换,说白了就是把训练好的模型(比如PyTorch、TensorFlow、ONNX)变成NPU能吃的格式。整个过程就三步:
- 初始化RKNN对象——创建一个转换器实例
- 配置参数——告诉转换器你的目标平台、量化方式等
- 加载模型并转换——喂入原始模型,吐出RKNN文件
嗯,就这么简单。但每一步都有坑,我一个个说。
4.2 初始化与rknn.config()参数详解
先看一段最基础的代码:
from rknn.api import RKNN
# 创建RKNN对象
rknn = RKNN()
# 配置参数
rknn.config(
target_platform='rk3588',
mean_values=[[123.675, 116.28, 103.53]],
std_values=[[58.395, 57.12, 57.375]],
quant_img_RGB2BGR=True,
quantized_dtype='asymmetric_quantized-8',
optimization_level=3
)
这里每个参数我都踩过坑,咱们逐个拆解。
target_platform
指定你的NPU芯片型号。我习惯写成'rk3588',但如果你用的是3568、3576,记得改。有一次我帮同事调试,他死活转不过去,结果发现target_platform写成了'RK3588'——大小写敏感,坑不坑?
mean_values和std_values
这两个参数做的是数据归一化。公式是:input = (pixel - mean) / std。注意顺序——如果你训练时用的是RGB,这里也得是RGB。我曾经在项目里把RGB和BGR搞反了,推理结果全乱套,排查了一下午才发现。
quant_img_RGB2BGR
这个参数控制是否在量化时做RGB到BGR的转换。为什么需要这个?因为很多摄像头输出的是BGR格式,但模型训练时用的是RGB。我个人建议:如果模型训练用的是RGB,这里设成True,让NPU帮你做转换,省得预处理时再折腾。
quantized_dtype
量化数据类型。默认是'asymmetric_quantized-8',也就是非对称8位量化。如果你追求精度,可以试试'int8'或'int16'。但说实话,8位量化在大多数场景下够用了,我做过一个车牌识别项目,8位量化后精度只掉了0.3%。
optimization_level
优化等级,0到3。等级越高,优化越激进,但转换时间也越长。我一般设成3,除非遇到转换失败,才会降到2试试。
4.3 模型加载与转换
配置完参数,接下来就是加载模型。不同框架的加载方式略有不同,但大同小异。
加载ONNX模型
# 加载ONNX模型
ret = rknn.load_onnx(model='./model.onnx')
if ret != 0:
print('模型加载失败!')
exit(ret)
加载PyTorch模型
# 加载PyTorch模型(需要先导出为torchscript)
ret = rknn.load_pytorch(model='./model.pt', input_size_list=[[1, 3, 224, 224]])
if ret != 0:
print('模型加载失败!')
exit(ret)
这里要注意:PyTorch模型必须先转成TorchScript格式。怎么转?用torch.jit.trace或者torch.jit.script。我个人更推荐trace,因为它更稳定。script有时候会碰到动态控制流的问题,trace基本不会。
执行转换
# 开始转换
ret = rknn.build(do_quantization=True, dataset='./dataset.txt')
if ret != 0:
print('模型转换失败!')
exit(ret)
# 保存RKNN模型
ret = rknn.export_rknn('./model.rknn')
if ret != 0:
print('模型保存失败!')
exit(ret)
rknn.build()里的dataset参数指向一个文本文件,里面是量化用的图片路径。每行一张图,比如:
./images/img001.jpg
./images/img002.jpg
./images/img003.jpg
量化图片的数量,我建议至少50张,最好100张以上。图片太少,量化后的精度会明显下降。我曾经偷懒只用了10张,结果模型精度从98%掉到了85%,教训深刻。
4.4 推理验证
模型转完了,怎么知道它对不对?得跑一下推理验证。
# 初始化运行时环境
rknn.init_runtime()
# 加载图片并推理
import cv2
img = cv2.imread('./test.jpg')
img = cv2.resize(img, (224, 224))
# 执行推理
outputs = rknn.inference(inputs=[img])
# 查看输出
print(outputs[0].shape)
print(outputs[0])
这里有个细节:rknn.inference()的输入是列表,即使只有一张图也得包在列表里。我第一次用的时候忘了,直接传了个numpy数组,结果报错说shape不匹配。
推理完成后,对比一下输出结果和原始模型的输出。如果数值接近(比如误差在1%以内),说明转换成功。如果差得离谱,大概率是量化参数或者预处理方式不对。
- rknn.config()的参数直接影响转换结果,尤其是mean/std和量化方式
- 量化图片至少50张,且分布要与训练数据一致
- 推理验证时,输入预处理要和训练时保持一致
- 转换失败时,先降低optimization_level试试
4.5 完整示例代码
最后,给一个完整的转换脚本,你直接复制就能用:
from rknn.api import RKNN
import cv2
import numpy as np
# 1. 创建RKNN对象
rknn = RKNN()
# 2. 配置参数
rknn.config(
target_platform='rk3588',
mean_values=[[123.675, 116.28, 103.53]],
std_values=[[58.395, 57.12, 57.375]],
quant_img_RGB2BGR=True,
quantized_dtype='asymmetric_quantized-8',
optimization_level=3
)
# 3. 加载模型
ret = rknn.load_onnx('./model.onnx')
if ret != 0:
print('加载模型失败')
exit(ret)
# 4. 执行转换
ret = rknn.build(do_quantization=True, dataset='./dataset.txt')
if ret != 0:
print('转换失败')
exit(ret)
# 5. 保存模型
rknn.export_rknn('./model.rknn')
# 6. 推理验证
rknn.init_runtime()
img = cv2.imread('./test.jpg')
img = cv2.resize(img, (224, 224))
outputs = rknn.inference(inputs=[img])
print('推理结果:', outputs[0])
# 7. 释放资源
rknn.release()
这个脚本我用了很多项目,基本没出过问题。你只要把模型路径和量化图片路径改一下就行。
好了,这一章的内容就到这儿。下一章我们会深入RKNN的量化原理,聊聊为什么量化后模型会变小、变快,但精度却可能下降。到时候我会分享一个我踩过的量化精度坑,保证让你少走弯路。