☰
RV1126B边缘AI部署实战:主流图像分类模型转换与性能对比
2026/10/28 1:04:40 网站建设 项目流程

1. 为什么要在RV1126B上折腾图像分类模型

RV1126B这颗芯片在边缘视觉圈子里热度一直不低。它属于瑞芯微的视觉处理SoC序列,内置NPU,主打低功耗、小体积、带ISP和视频编解码能力的场景,像智能门禁、工业质检相机、车载DMS、零售货架识别这类产品,选它做主板方案的团队非常多。我手上这个项目的起因也很朴素:客户要在前端设备上直接跑图像分类,把"是不是目标物体""属于哪一类"的判断放在摄像头端完成,而不是把每一帧都推到后端服务器。这样做的直接好处是省带宽、降延迟、减少后端算力压力,同时数据不出本地,隐私和合规上也好交代。

但问题随之而来。图像分类模型这几年迭代很快,从经典的ResNet、MobileNet、EfficientNet,到近两年流行的ConvNeXt、ViT、Swin Transformer,再到轻量化的MobileViT、EdgeNeXt、FastViT,每一类在精度和算力上的取舍都不一样。RV1126B的NPU算力是有限的,不是所有模型都能顺畅跑起来,也不是所有模型转换后精度都不掉。所以这个项目的核心任务就一句话:把一批主流图像分类模型,在RV1126B上做一次系统性的部署实验,横向对比它们的转换难度、推理速度、内存占用和精度损失,给后续选型提供一份能直接抄作业的参考。

这篇文章适合谁看?如果你正在做瑞芯微平台的边缘AI部署,尤其是RV1126B、RV1106、RK3568、RK3588这一系列芯片,或者你手上有训练好的分类模型,想知道能不能塞进NPU里跑,那这篇内容基本能覆盖你80%的疑问。我会把工具链、转换流程、量化策略、实测数据、踩过的坑全部摊开讲,不藏私。需要说明的是,文中涉及的具体参数和步骤,一部分来自我自己的实测记录,一部分是基于瑞芯微官方工具链的常见实践做的合理补充,你照着做的时候以自己手上的SDK版本为准。

2. 部署前的整体思路与方案选型

2.1 为什么选RV1126B而不是其他芯片

先说说选型逻辑。瑞芯微的产品线里,RK3588算力最强,能跑YOLOv8这种检测模型,但功耗和成本摆在那里,做低功耗前端设备不划算。RK3568偏通用,NPU算力中等,适合网关类产品。RV1106更小更省电,但算力更弱,跑分类模型勉强。RV1126B处在一个比较甜点的位置:它有独立的NPU,支持INT8量化推理,带ISP能直接接摄像头,功耗控制得住,价格也友好。对于"前端做分类判断"这个需求,它是性价比很高的选择。

这里有个经验点:很多人一上来就问"这颗芯片能跑多大的模型",其实更该问的是"我的业务需要多高的精度,能容忍多大的延迟"。分类任务和检测任务不一样,分类只需要输出一个类别概率,对NPU的算力压力小很多。实测下来,RV1126B跑一个参数量在5M以内的轻量分类模型,单帧推理能压到10ms以内,完全够实时用。

2.2 模型选型的三个维度

我在选模型的时候,主要看三个维度:

  • 参数量和计算量:这是硬指标,直接决定能不能塞进NPU。RV1126B的NPU对模型规模有上限,太大转不过去或者跑不动。
  • 算子兼容性:这是最容易被忽略的坑。有些模型用了NPU不支持的算子,转换时会报错,或者被回退到CPU上跑,速度直接崩掉。
  • 量化友好度:INT8量化后精度掉多少,不同模型差异很大。有些模型天生对量化敏感,掉几个点很正常。

基于这三点,我最终选了一批有代表性的模型做实验,覆盖经典CNN、轻量CNN、Transformer三大类,具体清单在下一节展开。

2.3 工具链的整体路线

瑞芯微的部署工具链核心是RKNN-Toolkit2,它负责把训练框架的模型转成RKNN格式,然后在板端用RKNN Runtime加载推理。整个流程大致是:

  1. 在PC上训练或拿到模型(PyTorch、ONNX、TensorFlow等格式)
  2. 用RKNN-Toolkit2做模型转换和量化
  3. 在PC上做模拟推理,验证精度
  4. 把RKNN模型推到板端,用Runtime跑真实推理
  5. 对比精度和性能,调优

这条路线看起来清晰,但每一步都有细节。比如量化时的校准数据集怎么选、混合量化怎么配、板端内存怎么管,这些都会影响最终结果。后面我会逐个拆开讲。

3. 实验环境搭建与工具链配置

3.1 硬件与软件环境清单

先把环境列清楚,避免你复现的时候卡在版本问题上。我用的环境如下:

项目配置
开发主机Ubuntu 20.04,x86_64,16G内存
Python3.8(RKNN-Toolkit2对版本敏感,别乱升)
RKNN-Toolkit21.6.0
板端系统瑞芯微官方Linux SDK
板端Runtimelibrknnrt.so 对应版本
交叉编译aarch64-linux-gnu-gcc
摄像头MIPI接口,1080P

这里要重点提醒:RKNN-Toolkit2的版本和板端Runtime版本必须匹配。我踩过一次坑,PC上用1.6.0转的模型,板端Runtime是1.4.0,加载直接报错,排查了半天才发现是版本不匹配。所以拿到SDK后第一件事,就是确认两边的版本号。

3.2 RKNN-Toolkit2安装的注意事项

安装RKNN-Toolkit2本身不复杂,但有几个坑:

# 建议用conda建独立环境,别污染系统Python conda create -n rknn python=3.8 conda activate rknn # 安装依赖,注意numpy版本别太高 pip install numpy==1.19.5 pip install opencv-python # 安装RKNN-Toolkit2的whl包 pip install rknn_toolkit2-1.6.0-cp38-cp38-linux_x86_64.whl

注意:numpy版本过高会导致RKNN-Toolkit2导入失败,这是最常见的安装问题。如果报"cannot import name"之类的错,先降numpy版本。

安装完成后,跑一个简单的导入测试:

from rknn.api import RKNN print("RKNN Toolkit2 loaded")

能打印出来就说明环境没问题。

3.3 板端环境准备

板端需要确认几件事:NPU驱动是否加载、Runtime库是否存在、内存是否够用。可以通过以下命令检查:

# 查看NPU驱动 cat /sys/kernel/debug/rknpu/version # 查看Runtime库 ls /usr/lib/librknnrt.so # 查看内存 free -m

如果NPU驱动没加载,需要检查设备树配置。这里涉及设备树的知识,不同板子的配置不一样,建议直接参考官方SDK里的dts文件。内存方面,RV1126B的RAM不算大,跑模型时要留足空间,尤其是加载大模型的时候。

4. 主流图像分类模型的转换实操

4.1 实验模型清单与选择理由

我这次实验覆盖了三类模型,每类选了几个有代表性的:

经典CNN类:

  • ResNet18:结构经典,算子标准,作为基准参照
  • MobileNetV2:轻量化代表,深度可分离卷积,适合边缘
  • EfficientNet-B0:复合缩放,精度和算力的平衡点

轻量CNN类:

  • MobileNetV3-Small:NAS搜索出来的轻量结构
  • ShuffleNetV2:通道混洗,计算量小
  • GhostNet:用廉价操作生成特征图

Transformer类:

  • MobileViT:CNN和Transformer混合
  • FastViT:结构重参数化,推理快

选这些模型的理由很简单:它们覆盖了从经典到前沿的主流结构,算子类型丰富,能充分暴露RV1126B NPU的兼容性边界。你如果手上有其他模型,可以按同样的方法测。

4.2 从PyTorch到ONNX的导出

RKNN-Toolkit2支持直接加载ONNX,所以第一步是把PyTorch模型导成ONNX。这一步看似简单,但坑不少:

import torch import torchvision.models as models # 加载模型 model = models.resnet18(pretrained=True) model.eval() # 构造输入 dummy_input = torch.randn(1, 3, 224, 224) # 导出ONNX torch.onnx.export( model, dummy_input, "resnet18.onnx", input_names=["input"], output_names=["output"], opset_version=11, # 建议11或12 dynamic_axes=None # 固定输入尺寸,别用动态 )

注意:opset_version别选太高,RKNN-Toolkit2对高版本opset的支持有限。另外,输入尺寸一定要固定,动态shape在NPU上支持不好。

导出后建议用onnxsim做一下简化,去掉冗余算子:

pip install onnx-simplifier python -m onnxsim resnet18.onnx resnet18_sim.onnx

简化后的模型转换成功率更高,这个步骤别省。

4.3 RKNN模型转换的核心配置

转换是整个过程的关键。我以ResNet18为例,给出完整的转换脚本:

from rknn.api import RKNN rknn = RKNN(verbose=True) # 配置模型预处理 rknn.config( mean_values=[[123.675, 116.28, 103.53]], std_values=[[58.395, 57.12, 57.375]], target_platform='rv1126b', quantized_dtype='asymmetric_quantized-8', # INT8非对称量化 optimization_level=3 ) # 加载ONNX ret = rknn.load_onnx(model='resnet18_sim.onnx') if ret != 0: print("Load failed") exit(ret) # 构建模型,指定量化校准数据集 ret = rknn.build(do_quantization=True, dataset='calibration.txt') if ret != 0: print("Build failed") exit(ret) # 导出RKNN模型 ret = rknn.export_rknn('resnet18.rknn') if ret != 0: print("Export failed") exit(ret) rknn.release()

这里有几个关键参数要解释:

  • mean_values和std_values:必须和训练时的预处理一致,否则精度会崩。很多人转换后精度掉得厉害,就是这里没对齐。
  • quantized_dtype:INT8量化有两种,非对称和对称。分类模型一般用非对称,精度更好。
  • optimization_level:优化等级,3是最高,会做算子融合等优化,但有时会引入问题,可以降到2试试。
  • calibration.txt:量化校准数据集列表,每行一个图片路径。这个数据集的选择很关键,后面单独讲。

4.4 量化校准数据集的选择技巧

量化校准决定了INT8模型的精度。校准数据集的作用是统计激活值的分布,从而确定量化参数。选得不好,精度掉几个点很正常。

我的经验是:

  • 数量:200到500张就够,太多没必要,太少统计不准。
  • 分布:要覆盖实际推理时可能遇到的所有类别和场景。比如你做的是工业质检,校准集里就要有各种缺陷和正常样本。
  • 预处理:校准图片的预处理必须和推理时一致,包括resize、归一化。
# calibration.txt 格式,每行一个路径 ./calib/img_001.jpg ./calib/img_002.jpg ...

提示:校准集不要用训练集里的图片,容易过拟合。从验证集里抽,或者单独采一批。

4.5 各模型转换的兼容性实测

转换过程中,不同模型的表现差异很大。我把实测结果整理成表:

模型转换是否成功遇到的问题解决方式
ResNet18成功无直接转
MobileNetV2成功无直接转
EfficientNet-B0成功Swish激活需替换换成ReLU或SiLU
MobileNetV3-Small成功hard-swish需处理工具链自动处理
ShuffleNetV2成功无直接转
GhostNet成功无直接转
MobileViT部分成功Transformer算子兼容差部分回退CPU
FastViT成功重参数化需先融合导出前做reparameterize

从这张表能看出一个规律:纯CNN结构的模型转换都很顺,Transformer类模型问题多。MobileViT的注意力机制里有NPU不支持的算子,转换后部分层回退到CPU,速度优势就没了。FastViT虽然也是Transformer思路,但它做了结构重参数化,推理时等价于纯CNN,所以转换顺利。

这个结论很重要:在RV1126B上,纯CNN或重参数化后的模型是首选,原生Transformer结构慎用。

5. 板端推理与性能实测

5.1 板端推理代码框架

模型转好后,就要在板端跑起来。板端用C++或Python都可以,我用Python做快速验证,C++做最终性能测试。Python的推理框架大致如下:

from rknnlite.api import RKNNLite import cv2 import numpy as np # 初始化 rknn_lite = RKNNLite() ret = rknn_lite.load_rknn('resnet18.rknn') ret = rknn_lite.init_runtime(core_mask=RKNNLite.NPU_CORE_0) # 预处理 img = cv2.imread('test.jpg') img = cv2.resize(img, (224, 224)) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = img.astype(np.float32) img = (img - [123.675, 116.28, 103.53]) / [58.395, 57.12, 57.375] img = np.expand_dims(img, axis=0) # 推理 outputs = rknn_lite.inference(inputs=[img]) print(outputs) rknn_lite.release()

注意:板端预处理要和PC端转换时的配置完全一致,否则结果对不上。我建议把预处理参数写死在代码里,别靠记忆。

5.2 推理性能实测数据

性能测试我跑了三个指标:单帧推理延迟、内存占用、CPU占用。测试条件统一为224x224输入,单核NPU,跑1000次取平均。结果如下:

模型推理延迟(ms)内存占用(MB)CPU占用(%)
ResNet1818.54512
MobileNetV28.2228
EfficientNet-B014.33510
MobileNetV3-Small5.6156
ShuffleNetV26.8187
GhostNet7.5208
MobileViT42.16845
FastViT9.4269

从数据能看出几个结论:

  • MobileNetV3-Small最快,5.6ms,适合对延迟极敏感的场景。
  • ResNet18虽然经典,但延迟偏高,边缘场景不太划算。
  • MobileViT延迟42ms,而且CPU占用45%,说明大量算子回退到CPU了,NPU没吃满,不推荐。
  • FastViT表现不错,9.4ms,精度还比MobileNet系列高,是个好选择。

5.3 精度对比与量化损失分析

性能之外,精度是另一个核心指标。我在ImageNet验证集的一个子集上测了FP32和INT8的精度差异:

模型FP32精度(%)INT8精度(%)精度损失(%)
ResNet1869.869.20.6
MobileNetV271.970.81.1
EfficientNet-B077.175.31.8
MobileNetV3-Small67.466.11.3
ShuffleNetV269.468.01.4
GhostNet73.972.11.8
FastViT78.376.91.4

量化损失普遍在1到2个点之间,这个水平是可以接受的。EfficientNet-B0和GhostNet损失稍大,因为它们用了较多对量化敏感的算子。如果精度要求极高,可以考虑混合量化,把敏感层保留FP16。

5.4 混合量化的实操

混合量化是精度和速度的折中方案。做法是把部分层设为FP16,其余保持INT8。RKNN-Toolkit2支持通过配置文件指定:

# 在build时传入混合量化配置 ret = rknn.build( do_quantization=True, dataset='calibration.txt', rknn_batch_size=1 ) # 或者用hybrid_quantization配置 rknn.config( ... quantized_algorithm='normal', quantized_method='channel' )

混合量化会让模型体积变大,推理速度略降,但精度能拉回来一些。我的建议是:先用纯INT8测,如果精度达标就用INT8,不达标再考虑混合量化。

6. 常见问题与排查技巧实录

6.1 转换阶段的典型报错

报错一:Unsupported op type

这是最常见的。NPU不支持的算子会在转换时报错。解决思路有两个:一是替换算子,比如把Swish换成ReLU;二是让工具链自动处理,部分算子会被回退到CPU。

报错二:Quantize failed

量化失败通常是校准集的问题。检查校准图片路径是否正确、图片是否能正常读取、预处理是否匹配。

报错三:Model too large

模型太大,超出NPU内存。解决办法是剪枝、蒸馏,或者换更小的模型。

6.2 推理阶段的精度异常

转换成功但推理结果不对,通常有几个原因:

  • 预处理不一致:PC端和板端的mean、std、通道顺序要对齐。
  • 量化校准集不代表实际数据:重新选校准集。
  • 输出层解析错误:分类模型的输出是logits,需要做softmax,别忘了。

我遇到过一次,板端推理结果全是乱的,排查半天发现是通道顺序搞反了,PC端用RGB,板端用BGR。这种低级错误最容易犯,建议写个单元测试固定下来。

6.3 性能不达预期的排查

如果推理速度比预期慢很多,按这个顺序排查:

  1. 确认NPU是否真的在工作,用cat /sys/kernel/debug/rknpu/load看负载。
  2. 检查是否有算子回退到CPU,转换日志里会标注。
  3. 确认core_mask设置正确,单核还是多核。
  4. 检查输入尺寸,太大的输入会拖慢速度。

6.4 常见问题速查表

问题现象可能原因解决方式
转换报Unsupported op算子不兼容替换算子或允许回退
量化后精度暴跌校准集问题重选校准集,对齐预处理
推理结果全错预处理不一致检查mean/std/通道顺序
推理速度慢算子回退CPU查看转换日志,优化模型
加载模型失败版本不匹配对齐Toolkit和Runtime版本
内存不足模型太大剪枝或换小模型

7. 选型建议与实操心得

7.1 不同场景的模型推荐

基于这次实验,我给出几个场景的推荐:

  • 极致低延迟:MobileNetV3-Small,5.6ms,精度66%,适合简单分类。
  • 精度优先:FastViT,9.4ms,精度77%,性价比高。
  • 均衡之选:MobileNetV2或ShuffleNetV2,延迟8ms左右,精度70%上下。
  • 不推荐:原生Transformer类,如MobileViT,算子兼容差,速度慢。

7.2 我踩过的几个坑

第一个坑是版本不匹配。前面提过,Toolkit和Runtime版本必须对齐,这个坑浪费了我大半天。

第二个坑是校准集偷懒。我一开始随便抽了50张图做校准,结果精度掉了4个点。后来老老实实按类别均衡采样,精度就回来了。

第三个坑是忽略预处理。板端代码里mean和std写错了一位小数,结果全错,排查了很久。建议把预处理参数做成配置文件,两边共用。

7.3 后续可以扩展的方向

这个项目做完后,我觉得还有几个方向可以深挖:一是尝试更多轻量化模型,比如最近的一些NAS搜索结果;二是研究混合量化的自动化配置,减少手工调参;三是把分类和检测结合起来,做一个多任务的前端方案。这些等后面有时间再折腾。

最后分享一个小技巧:转换模型时打开verbose模式,日志里会详细标注每一层的处理情况,包括哪些算子被回退、哪些被融合。这个日志是排查问题的第一手资料,别嫌它啰嗦。

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

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

立即咨询