☰
RK3568 NPU 部署实战:主流图像分类模型转换、量化与性能评测
2026/10/14 1:08:05 网站建设 项目流程

把一堆常见图像分类模型挨个部署到 RK3568 上跑一遍,这个想法其实源于一次项目预研:需要一台无风扇、功耗控制在 10W 以内、预算又不高的边缘盒子,用来做实时图像分类。当时备选方案不少,但真正把“价格、算力、生态”都凑齐的,RK3568 确实是绕不开的一个选择。瑞芯微这颗 SoC 不算旗舰,但它的 NPU 在中低端边缘设备里非常有代表性,很多工业相机、闸机、边缘网关都在用它。这篇文章就把我这段时间的部署实验记录整理出来,围绕 RK3568 的 RKNN 工具链,把主流图像分类模型的转换流程、量化方法、实际性能数据、踩坑经验一次性说全,给后面想做类似端侧推理项目的朋友做个参考。

我先说结论:RK3568 的 NPU 官方标称是 1 TOPS 算力,INT8 精度,实际跑起来和“够用”之间还有不少距离需要靠优化填补。MobileNet 系列、ShuffleNet 系列这种轻量模型,跑满 60 到 70 FPS 是可能的;ResNet50 这种重量级选手,INT8 量化后在 NPU 上也能跑进 80ms 以内。如果是做视频流抽帧分类、工业质检、智能相册这类场景,这个水平完全可以落地。

1. RK3568 部署图像分类的可行性判断:硬件和理论分析

在真正动手写 RKNN 转换脚本之前,我建议先花十分钟把 RK3568 这颗芯片的底子看明白。SoC 选型阶段如果方向错了,后面性能怎么调都是白费功夫。

1.1 先把 RK3568 的规格拆开看

RK3568 是一颗四核 Cortex-A55 的处理器,最高主频 2.0GHz,集成 Mali-G52 GPU,以及一个官方标称 1 TOPS 的 NPU。内存支持 LPDDR4/LPDDR4X,最高可以配置到 8GB。这颗 NPU 不支持 FP16,但支持 INT8 和 INT4 量化推理,这也是 RKNN 工具链最关键的一环。

单看 CPU 部分,A55 在 ARM 公版核心里属于能效优先的定位,单核性能并不强。之前在树莓派同类板卡上用 NCNN 跑 MobileNetV2,单帧 224x224 推理耗时大约在 70ms 到 90ms 浮动,到了 RK3568 上也不会有质的飞跃。但 NPU 负责推理后,同样的模型在 RKNN INT8 下能做到 12ms 左右,差距非常直观。卷积、全连接这些算子在 NPU 上是硬件加速的,CPU 只负责图像预处理、数据搬运和分类结果后处理。

内存带宽是一个容易被忽略的指标。RK3568 的内存接口是 32bit 的 LPDDR4,实测带宽比 RK3588 低不少。模型在 NPU 上算得再快,如果频繁进行大 tensor 读写,内存带宽就会变成瓶颈。后面实测 EfficientNet 系列耗时明显偏高,很大程度上就是吃了内存带宽的亏。

1.2 为什么图像分类这种任务适合丢给 NPU

图像分类网络由大量卷积层、BatchNorm 层、激活层组成,这些算子都是典型的密集计算,非常适合 NPU 这种固定流水线硬件结构。RK3568 的 NPU 内部是硬件卷积引擎,对 INT8 数据做乘加运算的速度远高于 CPU 指令。更重要的一点是,NPU 在执行推理时 CPU 可以同步处理图像采集、缩放、编码等任务,这对于视频流多路并发场景非常关键。

不过这不代表所有网络结构都能直接“喂”给 NPU。RKNN 工具链会把模型切分成若干子图,能放进 NPU 的算子加速,放不进去的算子会自动落到 CPU 跑。如果网络里出现过多 CPU 才能执行的算子,比如某些复杂注意力机制,那整条推理链路就会在 NPU 和 CPU 之间反复切换,性能直接崩塌。

1.3 RK3568 在同系列里的真实定位

很多人把 RK3566 和 RK3568 混为一谈。RK3566 的 NPU 算力是 0.8 TOPS,主要面向低端平板和电子书;RK3568 是 1 TOPS,多了一些接口和显示能力,更偏向 AIOT、边缘计算。和更高端的 RK3588(三核 NPU,6 TOPS)相比,RK3568 的单核 NPU 决定了它不适合跑大模型和复杂检测网络,但做轻量化图像分类、简单目标识别,性价比很高。

我当时做方案选型时对比过几个方向:用 RK3588 太贵,用纯 CPU 盒子性能不够,用独立 AI 加速卡又需要考虑功耗和成本。RK3568 刚好卡在“够用且便宜”的点位上。如果你要做的是工业残次品分类、垃圾分类、车牌分类这类固定类别场景,一个 500 张图片以内的轻量分类模型在 RK3568 上跑起来非常舒服。

2. 模型怎么选才稳:RKNN 对网络结构的约束

RKNN-Toolkit2 不是万能的。它支持的算子列表看着挺全,但实际转换时会遇到各种兼容性问题。在挑模型之前,先了解工具链的脾气,能省掉很多排查时间。

2.1 主流分类模型候选清单

我这次实验挑了七个有代表性又足够“主流”的模型,覆盖不同设计风格:

模型参数量理论 MACs设计特点RKNN 转换预期
SqueezeNet1.11.24M约 0.7G大量 1x1 卷积,模型极小转换简单,偏向 CPU 算子
ShuffleNetV2 x1.02.28M约 0.6GChannel Shuffle 操作多Shuffle 切片可能部分落到 CPU
MobileNetV2 1.03.40M约 0.3G倒残差结构,Depthwise 卷积多转换顺畅,NPU 友好
MobileNetV3-Large 1.05.48M约 0.2G带 SE 模块,H-Swish 激活SE 算子需要重点验证
EfficientNet-Lite04.65M约 0.4G复合缩放,结构复杂卷积核组合可能拖慢速度
ResNet1811.69M约 1.8G经典残差结构转换顺利,精度有保障
ResNet5025.56M约 4.1G深度残差结构模型大,但 RKNN 支持完善

原本我还想加一个 Vision Transformer 小模型做对照,但实测后发现 RKNN-Toolkit2 对 Attention 相关算子的支持还不成熟,转换虽然能过,推理速度却非常难看。ViT 类模型在 RK3568 上暂时不具备实用价值,除非后续官方工具链有大的算子更新。

2.2 RKNN 的算子支持边界

RKNN-Toolkit2 对常见 CNN 算子支持得比较完善,例如 Conv、BatchNorm、ReLU、MaxPool、GlobalAveragePool、Softmax 都能直接放在 NPU 上执行。但有几类算子容易出问题:

第一个是 Depthwise 卷积。理论上 RKNN 支持,但 Depthwise 卷积在部分硬件层面实现效率一般。MobileNetV2 之所以实际速度没有比 ResNet18 快出特别多,就是因为在 NPU 上 Depthwise 卷积的利用率不如普通卷积。

第二个是 Channel Shuffle。ShuffleNetV2 用大量的通道重排来增强特征交互,但通道重排在 NPU 内部往往要被拆成若干个 reshape 和 gather 操作,很容易出现一部分算子落到 CPU 执行的现象。从转换日志里看,ShuffleNetV2 的 NPU 算子占比比其他模型都要低。

第三个是 SE 模块里的 Sigmoid 和多维全局池化。全局池化本身还好,但 SE 计算会引入额外的乘法,造成输入输出通道不连续,尤其在 INT8 量化后,精度波动比普通结构更明显。

2.3 输入尺寸和输出形式的设计建议

模型转换前先确定输入分辨率。RKNN 并不强制要求输入必须是 224x224,但输入尺寸会直接影响 NPU 计算的耗时和内存占用。我在实验中统一使用 224x224,因为大部分预训练模型的默认输入就是这么大,方便做公平对比。

输出端也有讲究。图像分类模型通常把输出接一个 Softmax 或者直接线性输出。RKNN 转换后,NPU 计算完成后可以把输出通过后端模式配置成浮点数据,方便直接比对。这里有个细节:如果你想要最终的分类置信度,最好把 Softmax 层放到代码里手动计算,或者把输出层直接设置为“不做 Softmax 融合”,这样在 RKNN 层面操作更可控。

3. 完整部署流程实操:ONNX、RKNN 转换与量化

RKNN-Toolkit2 的部署链路可以归纳为三步:先把训练好的 PyTorch 或 TensorFlow 模型导出成 ONNX,再用 rknn-toolkit2 将 ONNX 转换成 RKNN 格式并做 INT8 量化,最后把 RKNN 模型文件放到板子上,用 RKNN-Lite 或者原生 C 接口进行推理。

3.1 模型导出 ONNX 的坑与正确姿势

这一步看起来简单,但导出参数直接影响后续 RKNN 转换。我在 PyTorch 里导出用的是 opset 12,原因是 RKNN-Toolkit2 对 ONNX opset 12 的算子支持最稳定,用更高的 opset 版本时某些新算子反而无法识别。

import torch # 假设 model 是一个已加载权重的分类模型 model.eval() dummy_input = torch.randn(1, 3, 224, 224) torch.onnx.export( model, dummy_input, "model.onnx", input_names=["input"], output_names=["output"], opset_version=12, do_constant_folding=True, dynamic_axes=None, # 固定输入尺寸,部署优先 )

我个人的建议是导出时把 dynamic_axes 参数关掉。固定输入尺寸可以让 RKNN 编译器做更多静态优化,比如算子融合和内存复用。如果确实需要动态分辨率,就必须在转换时配置 max_dynamic_range 之类参数,不但麻烦,推理性能还会下降。

导出后用 netron 看一眼 ONNX 图的整体结构,重点检查是否有奇怪的 Resize 组合、Shape/Gather 操作。RKNN 不怕卷积,就怕形状推导类的算子。看到这些节点后,尽量在模型结构里避免,或者提前在脚本里把图优化掉。

3.2 RKNN 转换和 INT8 量化实操

RKNN-Toolkit2 有独立的 Python 包,我用的宿主机是 Ubuntu 20.04,Python 3.8。需要提前装好对应版本的 rknn-toolkit2 wheel 包,以及依赖的 torch、opencv 这些基础库。转换脚本核心代码如下:

from rknn.api import RKNN rknn = RKNN() rknn.config( mean_values=[[123.675, 116.28, 103.53]], std_values=[[58.395, 57.12, 57.375]], target_platform="rk3568" ) ret = rknn.load_onnx(model="model.onnx") if ret != 0: print("load_onnx failed") ret = rknn.build(do_quantization=True, dataset="dataset.txt") if ret != 0: print("build failed") rknn.export_rknn("model_int8.rknn") rknn.release()

这段代码里最容易错的是 config 里的 mean_values 和 std_values。PyTorch 的 ImageNet 预训练模型通常使用归一化参数 mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225],换算成 RGB 顺序的 8bit 整数形式就是上面代码里的三组值。如果填成 Caffe 模型的 BGR 顺序,量化精度会掉得非常快,而且这种错法往往看起来不是很大,排查起来很坑。

do_quantization=True 是关键。RK3568 的 NPU 最大优势在于 INT8,不量化就浪费了硬件。dataset.txt 的内容是校准图片的路径列表,量化时工具链会根据这些图片统计每一层激活值的分布区间,再选择对应的量化参数。

# dataset.txt 示例 ./calib_images/001.jpg ./calib_images/002.jpg ./calib_images/003.jpg

校准图片数量我建议 200 到 500 张。数量太少会导致量化后精度损失普遍偏大;数量太大则转化时间增加,收益却边际递减。校准图片最好从验证集里随机抽取,并且覆盖不同光照、不同背景的样本。这里有个实际经验:不要把整批训练集图片直接丢进去,RNN 工具会过度拟合训练集的激活分布,反而影响泛化。

3.3 板端推理:Python 快速验证与 C 部署

板端推理我一般先用 Python 接口 RKNN-Lite 做验证,比如确认输出 shape、测试单帧耗时、核对分类结果。RKNN-Lite 是 RKNN-Toolkit2 的精简版 Python 包,专门跑在 ARM 端 Linux 系统上,不需要复杂的编译依赖。

from rknnlite.api import RKNNLite rknn_lite = RKNNLite() rknn_lite.load_rknn("model_int8.rknn") rknn_lite.init_runtime() img = cv2.imread("test.jpg") img = cv2.resize(img, (224, 224)) outputs = rknn_lite.inference(inputs=[img]) print(outputs)

注意推理时的输入图片默认是 BGR 格式,uint8,和 OpenCV 的读图结果直接对齐。不要再额外做一次 RGB 转换,否则预处理就和转换时设置的 mean/std 对不上了。

等 Python 验证通过,产品落地阶段我会把推理换成 C 接口。C 接口的好处是省去 Python 解释器开销,还能把预处理、推理、后处理完全放在一个进程内。此时需要用 rknn_init 加载模型,再通过 rknn_run 执行推理,开发流程相对简单但需要仔细管理内存缓冲区。

3.4 推理性能提升的几个有效手段

RK3568 的 NPU 只有一个核,没有 RK3588 那种多核并行模式,所以“单模型多核并行”这条路走不通。能做的优化集中在三点。

第一是预处理加速。用 CPU 做 resize 和归一化虽然简单,但很耗时。RK3568 内置 RGA 图形加速引擎,可以把图像缩放、颜色转换这类操作交给硬件执行,CPU 只负责从摄像头取帧。

第二是避免不必要的拷贝。RKNN-Lite 的 inference 接口会把输入输出数据复制进复制出,如果每帧都新建数组,内存分配和销毁会成为不可忽略的开销。正确做法是复用同一块输入输出 buffer,循环调用。

第三是通过设置 NPU 核心频率和工作模式来保证稳定性能。板端跑长时间任务时,CPU 和 NPU 都有降频的风险,需要把系统 CPU 调成 performance 模式,同时检查 NPU 频率是否因为发热被限制在某个低档位。

4. 多模型实测:速度、精度、内存的取舍

实验对比不是纸上谈兵,数据是最重要的收获。以下数据来自我手头的一台 4GB 内存的 RK3568 开发板,系统刷的是官方 Linux 固件,板端主频锁定在最高档。推理延迟是连续跑 100 次后取的平均值,计算时包含 CPU 图像 resize 但排除摄像头取流时间。

4.1 实验环境配置

宿主机负责模型转换,模型统一用 ImageNet 预训练权重,导出为 ONNX 后再转 RKNN。板端用同一个测试图片集,保持输入分辨率 224x224。量化数据集统一抽 300 张验证集图片,所有模型用同一套校准图片,保证对比公平。

整个测试期间板子保持无外壳裸板状态,环境温度约 25 度,没有明显降频问题。为了排除其他进程干扰,测试程序跑在单独的进程里,系统后台只保留基础服务。

4.2 七个模型的性能和内存对比

模型RKNN 推理耗时(ms)实际吞吐(FPS)峰值内存(MB)RKNN 文件大小(MB)
MobileNetV2 1.011.97022013.8
MobileNetV3-Large13.86223815.2
ShuffleNetV2 x1.015.4572056.2
SqueezeNet1.120.6431985.1
ResNet1831.22831544.7
EfficientNet-Lite037.42428611.5
ResNet5070.813450102.0

MobileNetV2 是跑得最快的,单帧推理 11.9ms,实际吞吐量约 70 FPS,这个成绩做实时视频流分类完全可用。MobileNetV3 虽然理论计算量比 V2 小,但因为引入了 SE 模块和 H-Swish,实际速度反而比 V2 慢,这正好对应前面说的算子兼容性问题。

ResNet50 是常规认知里的“经典模型”,但它的参数量和计算量放在 RK3568 上确实偏重,70.8ms 的单帧耗时只适合做低频抽帧分类,不适合实时视频流。EfficientNet-Lite0 的耗时比预想中高很多,复合缩放带来的结构复杂度和频繁的深度可分离卷积,在 NPU 上并没有体现出它理论低 FLOPs 的优势。

4.3 量化前后的精度变化

为了验证 INT8 量化对精度的影响,我在 ImageNet 验证集里随机抽了 1000 张图片作为子集,分别测出模型在浮点状态下的 Top-1 精度,以及转成 RKNN INT8 后的 Top-1 精度。

模型FP32 Top-1 精度RKNN INT8 Top-1 精度精度损失
MobileNetV2 1.071.8%70.5%-1.3%
MobileNetV3-Large73.5%72.8%-0.7%
ShuffleNetV2 x1.069.4%68.4%-1.0%
SqueezeNet1.156.8%55.8%-1.0%
ResNet1869.8%69.2%-0.6%
EfficientNet-Lite074.3%73.1%-1.2%
ResNet5076.5%75.5%-1.0%

这个数据是在校准图片固定 300 张的前提下测的。可以看到,只要校准图片选得合理,INT8 量化带来的精度损失基本控制在 1.5% 以内。对于很多实际项目来说,这个差距甚至不需要做特殊处理。

有一点必须提醒:如果只拿 20 张图片做校准,精度损失很容易飙到 3% 到 5%,特别是用 MobileNetV2 这种 Depthwise 卷积较多的模型时。万一出现这种情况,别急着怀疑工具链,先把量化图片数量加上去再试。

4.4 从结果反推模型选型

综合速度、精度、内存三个维度,我可以给出几个方向性的结论。

需要极致速度就选 MobileNetV2,70 FPS 的吞吐非常香,精度也足够覆盖大多数场景。需要更高精度但又要保持实时,MobileNetV3-Large 是正确的折中。ResNet18 在 RK3568 上跑得不算快,但它结构简单、算子兼容性最好,适合做二次开发和模型剪枝。ResNet50 只有在精度要求很高且不需要实时处理时才建议使用,例如定时拍照质检。

SqueezeNet 在 RK3568 上的表现其实很尴尬,文件最小但不代表速度最快,它的 1x1 卷积占比太高,理论吃亏。ShuffleNetV2 在移动端 CPU 上的优势很明显,但在 NPU 上由于 Channel Shuffle 算子的落地问题,优势被磨平了一大半。

5. 部署过程中的典型坑与排查经验

从模型转换到板端推理,我前前后后踩了不少坑。下面这几个是最常遇到的,也可能是你马上就会遇到的。

5.1 转换阶段的高频报错和对应处理

RKNN-Toolkit2 转换时会打印非常详细的日志。看到“Unknown layer”报错时,不要慌,先定位是哪个节点出了问题。这个方法最实用:把 ONNX 里对应的输出层名字从报错日志里找出来,再到模型结构里对照,基本能确定是哪些算子不支持。

有个典型例子是 Resize 算子。某些 PyTorch 版本导出 ONNX 后会生成双线性插值的 Resize,而 RKNN 在处理这种带 aligned_corners 参数的 Resize 时要看版本支持情况。如果报错,常见解法要么改导出方式,要么在模型里把上采样改成参数固定的 ConvTranspose。

另外还有一类报错是 “Shape not match”或 “error in init runtime”。这种问题九成出现在输入维度搞错。模型转换时输入 shape 是 1x3x224x224,板端推理时读入的图片如果是 224x224x3 且没做 transpose,就会在运行时出现维度不匹配。RKNN 内部默认输入是 NHWC,但很多模型导出时用的是 NCHW,工具链会做转换,千万不要自己在代码里二次转换。

5.2 精度对不上的排查思路

量化后精度下降是正常现象,但是出现两位数级别的精度暴跌就需要认真排查了。第一个检查项是 mean/std 是否和训练时一致。很多预训练模型下载下来并没有说明归一化参数,如果代码里直接拿来用,量化校准阶段统计出的激活范围就是错的。建议先把模型在浮点模式下导出并测试一遍,得到正常精度后,再开启量化。

第二个检查项是校准图片的分布。我有一次做工业零件分类任务,预训练模型的校准图片用了网上找的风景图,结果目标工件在量化后识别率惨不忍睹。换成同场景的工件图后,精度立刻恢复到正常水平。量化校准图片必须来自目标应用场景。

第三个检查项是预处理里的缩放方式。RKNN 转换时 mean/std 作用于整个输入区间,而 cv2.resize、PIL.Image.resize 这些不同库的插值算法有差异。特别要注意的是如果模型训练时是先等比缩放再做 center crop,部署时千万不能简化成直接拉伸到 224x224,否则同样的模型表现会差很多。

5.3 性能远远低于预期的排查顺序

如果模型转换成功,但推理速度比网上大家在 RK3588、或者 RK3568 得到的数据慢很多,按下面的顺序排查。

先看 NPU 有没有真正参与计算。转换日志里有一项会显示 NPU 算子和 CPU 算子的分配比例,如果一大半算子在 CPU 上执行,性能自然上不去。修复方向是替换算子,或者调整模型结构,例如把 SE 模块简化。

再看主频和降频。用 cpufreq 工具把 CPU 调到 performance 模式,同时检查 /sys/class/devfreq/ 下面的 NPU 频率。不检查这个就测性能,结果很可能会被系统动态调频机制干扰。

然后看是否被内存带宽锁死。同样一个 ResNet50,在 8GB 双通道 LPDDR4 板卡上的速度一定比 4GB 单通道板卡快,这个不是代码问题。如果预算允许,部署这类中等模型优先选择内存带宽更高的板卡版本。

最后检查是否每一帧都在做重复的 infer 前置操作。比如把 cv2.resize 的 dst 缓存、量化转换、归一化重复计算,部分实现里这些操作的开销甚至大于模型推理本身。

收尾:我的选型和部署习惯

整个项目做下来,最大的经验就是“别高估 NPU 的通用性”。RK3568 的 1 TOPS 更适合做定制化、结构规整的分类模型,而不是把一个大模型随便丢进去跑。现在我接任何 RKNN 部署任务,都会先跑一个轻量级模型做性能摸底,再决定是加大模型、加输入分辨率,还是裁剪网络结构。

如果让我再给一个具体的建议,那就是尽量保留一个 INT8 精度的 MobileNetV2 模型作为系统兜底。它转换稳定、速度兜得住、量化精度损失也好控制,非常适合做边缘端的“默认配置”。等业务要求更高精度时,再按需替换为 ResNet18 或 MobileNetV3-Large,避免一上来就把系统压满。多跑几次量化、多留几组校准图,这套流程跑顺之后,后续换个网络到 RK3568 上基本上就是半天的事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询