RV1106边缘视觉芯片图像分类模型部署与量化实测
1. 为什么要在RV1106上折腾图像分类模型RV1106这颗芯片最近在边缘视觉圈子里讨论度很高。它属于瑞芯微的视觉处理SoC系列内置NPU主打低功耗、低成本、小封装典型应用场景是智能门锁、猫眼、IPC、宠物喂食器、工业质检相机这类需要本地看一眼就出结果的设备。我手头正好有一批基于RV1106的模组之前主要跑的是检测类模型最近想把图像分类这条链路也摸清楚于是花了大概两周时间把几个主流分类网络在这颗芯片上完整跑了一遍。这篇文章不是官方文档的搬运也不是跑通一个demo就出来吹的那种。我会把模型选型、量化转换、精度对齐、性能实测、踩坑记录全部摊开讲。如果你手上也有RV1106或者同系列的板子想搞清楚到底哪个分类模型能跑、跑多快、精度掉多少、怎么调这篇应该能帮你省掉不少试错时间。适合有基本深度学习部署概念、但没深入玩过瑞芯微工具链的开发者也适合正在做产品选型、需要判断这颗芯片能不能扛住分类任务的工程师。先说结论方向RV1106的NPU算力不算富裕但它对量化后的轻量分类网络支持相当成熟MobileNetV2、ShuffleNetV2、RepVGG-A0这几类在合理量化后都能跑到实时级别而ResNet50这种就明显吃力了。具体数字后面用表格给。2. 实验平台与工具链整体设计2.1 硬件与软件环境说明硬件这边我用的是RV1106核心板加一块带MIPI摄像头的底板内存配置是内置DDR那档存储走SPI NAND。供电用5V/2A的适配器串口调试走UART0。摄像头是200万像素的MIPI模组实际推理时输入分辨率统一缩到模型要求的大小。软件环境分两段PC端做模型训练、量化和转换板端做推理验证。PC端我用的是Ubuntu 20.04Python 3.8PyTorch 1.10配合瑞芯微的RKNN-Toolkit2做模型转换。板端是Buildroot构建的Linux系统跑RKNN Runtime的C接口程序。这里要强调一点RKNN-Toolkit2的版本和板端Runtime版本必须匹配我一开始用1.5的Toolkit配1.4的Runtime转换出来的模型加载直接报错折腾了半天才定位到版本问题。提示瑞芯微工具链版本兼容性是个高频坑点建议PC端Toolkit和板端Runtime用同一发布批次转换前先确认版本号。2.2 为什么选这几个分类模型选模型不是随便抓几个来跑。我的筛选逻辑有三条第一覆盖不同的设计范式比如深度可分离卷积、通道重排、重参数化、残差结构这样能看出NPU对不同算子的友好程度第二参数量和计算量拉开梯度从0.5M参数到25M参数都有第三都是工业界实际会用的不是论文里刷榜的怪结构。最终入选的是MobileNetV2、MobileNetV3-Small、ShuffleNetV2、RepVGG-A0、ResNet18、ResNet50。输入分辨率统一用224x224做基准另外对MobileNetV2额外测了160x160和128x128看分辨率对帧率的影响。数据集用的是一个10类的自定义分类集每类大概800张训练图、200张验证图内容涉及常见物体和场景避免用公开数据集带来的过拟合错觉。2.3 整体部署流程拆解整个链路是这样的PyTorch训练出浮点模型导出ONNX用RKNN-Toolkit2加载ONNX做量化生成RKNN模型推到板端用Runtime推理最后对比精度和耗时。量化环节是关键RV1106的NPU只吃定点模型所以必须做量化而量化必然带来精度损失怎么把损失压到可接受范围是这次实验的核心工作之一。量化我用的是混合量化思路先做全INT8量化看基线如果某层精度掉得厉害再把这层单独提出来做混合精度。RKNN-Toolkit2支持逐层分析这个功能一定要用不然你根本不知道精度是从哪一层开始崩的。3. 模型转换与量化实操细节3.1 ONNX导出时的注意事项从PyTorch导ONNX看着简单但有几个细节不注意后面转换必出问题。第一动态轴要固定。分类模型输入是固定的224x224导出时把batch和空间维度都写死别留dynamic_axes否则RKNN转换时可能推断不出shape。第二算子版本别太高。我试过opset 13某些算子RKNN不认降到opset 11就顺了。第三导出后一定要用onnxsim做一次简化把冗余的Identity、Dropout之类节点去掉能减少转换报错概率。具体命令大概是这样python export_onnx.py --model mobilenetv2 --input-size 224 --opset 11 python -m onnxsim mobilenetv2.onnx mobilenetv2_sim.onnx导出后我会用Netron看一眼图结构确认没有奇怪的算子残留。这一步花不了几分钟但能省掉后面大量排查时间。3.2 RKNN量化配置的关键参数RKNN-Toolkit2的量化配置里有几个参数直接决定成败。quantized_dtype选asymmetric_quantized-8这是RV1106 NPU支持的标准格式。quantized_algorithm有normal和mmse两种normal快但精度一般mmse慢但更准我一般先用normal跑基线精度不够再换mmse。do_quantization当然要开。量化校准集很关键。官方建议用100到200张有代表性的图我实际用下来200张左右比较稳太少会导致激活值分布估计不准太多则转换时间线性增长。校准图要从训练集里随机抽别只用验证集否则分布偏移会让量化参数偏掉。rknn.config( mean_values[[123.675, 116.28, 103.53]], std_values[[58.395, 57.12, 57.375]], quantized_dtypeasymmetric_quantized-8, quantized_algorithmnormal, target_platformrv1106 )mean和std要和训练时一致这个如果搞错精度会莫名其妙掉一大截而且很难查。3.3 逐层精度分析与混合量化全INT8量化跑完后我会用Toolkit的accuracy_analysis接口做逐层对比。它会输出每一层量化前后的余弦相似度相似度低于0.98的层就是可疑层。实测下来MobileNetV2的深度可分离卷积层普遍比较敏感尤其是后面的几个block而RepVGG的重参数化分支合并后反而很稳。发现可疑层后用hybrid_quantization把这些层设成float16。注意RV1106对float16的支持是有限度的不是所有层都能混混太多会拖慢推理甚至跑不起来。我的经验是混合层控制在总层数的10%以内既能救精度又不至于明显掉帧。4. 板端推理与性能实测数据4.1 板端Runtime程序结构板端程序用C写核心流程是加载RKNN模型、初始化Runtime、读图、预处理、推理、后处理。预处理要注意RV1106的NPU输入要求是NHWC还是NCHW、需不需要归一化这些在转换时已经写进模型了板端只要按转换时的约定送数据就行。我一开始在板端又做了一遍归一化结果精度直接崩了因为模型里已经包含了mean/std处理。推理接口用rknn_run同步调用。如果要追帧率可以用多线程把预处理和推理重叠起来但分类任务本身轻单线程往往就够。4.2 各模型帧率与精度对比下面是实测数据输入224x224batch1测的是纯推理耗时不含摄像头采集和显示。精度是量化后模型在验证集上的top-1准确率对比的是浮点基线。模型参数量浮点精度INT8精度精度损失推理耗时帧率MobileNetV23.4M94.2%93.1%1.1%18ms55fpsMobileNetV3-Small2.5M93.5%92.0%1.5%14ms71fpsShuffleNetV22.3M93.8%92.4%1.4%16ms62fpsRepVGG-A08.3M95.1%94.3%0.8%26ms38fpsResNet1811.7M95.4%94.0%1.4%42ms23fpsResNet5025.6M96.2%94.1%2.1%118ms8fps从表里能看出几个规律。MobileNetV3-Small最快因为它的结构对NPU最友好大量用了1x1卷积和hard-swish的量化友好版本。RepVGG-A0精度损失最小只有0.8%说明重参数化结构在量化时鲁棒性不错。ResNet50精度损失最大2.1%而且帧率只有8fps基本没法做实时分类只能做低频抽帧分析。4.3 分辨率对性能的影响拿MobileNetV2单独测了不同输入分辨率输入分辨率推理耗时帧率精度224x22418ms55fps93.1%160x16011ms90fps91.8%128x1288ms125fps90.2%分辨率降到160时帧率提升明显精度只掉1.3个百分点这个trade-off在很多场景下是划算的。128的话精度掉得有点多除非你的分类任务本身很简单否则不太建议。5. 踩坑记录与常见问题排查5.1 转换阶段的典型报错最常见的报错是unsupported op。ONNX里某些算子RKNN不认比如某些版本的HardSwish、自定义的Resize。解决办法是用onnxsim简化或者手动替换成等价的基础算子组合。我遇到过一次MobileNetV3的HardSwish报错最后是把激活函数在导出前换成ReLU6才过精度掉了0.3%可以接受。第二个高频问题是shape推断失败。多半是ONNX里有动态维度没固定或者某个算子的输出shape在转换时算不出来。用Netron逐层看找到第一个shape异常的节点往前推。5.2 精度异常的排查思路精度掉得离谱先查三件事mean/std是否和训练一致、校准集是否有代表性、输入数据布局是否正确。这三条能覆盖80%的精度问题。如果都正常再上逐层分析找相似度低的层做混合量化。还有一个隐蔽的坑量化校准时的输入尺寸必须和推理时一致。我有一次校准用224推理用160结果精度直接掉到随机水平查了好久才发现是尺寸不匹配导致量化参数完全失效。5.3 板端运行时的稳定性问题板端跑久了偶尔会遇到推理结果异常重启就好。后来定位到是内存碎片问题RV1106内存小频繁申请释放大块内存容易碎。解决办法是初始化时一次性把输入输出buffer分配好循环里复用不要每次推理都malloc。另外NPU和CPU共享内存带宽如果CPU端同时跑很重的任务推理耗时会波动。实测下来CPU占用超过50%时推理耗时能涨20%以上。所以产品设计时要把CPU负载留出余量。6. 模型选型建议与场景匹配6.1 不同场景下的推荐方案如果是智能门锁、猫眼这类需要快速响应的场景MobileNetV3-Small是首选71fps足够做多帧确认精度也够用。如果是工业质检对精度要求高、对帧率要求没那么极致RepVGG-A0更合适精度损失小38fps也能接受。ResNet18适合对精度有更高要求、能接受23fps的场景比如某些安防分析。ResNet50在RV1106上不推荐做实时分类除非你只做低频抽帧。6.2 量化策略的经验总结量化算法上normal和mmse的精度差距大概在0.5到1个百分点但mmse转换时间可能是normal的三到五倍。如果时间紧先用normal精度不够再换mmse。混合量化的层数控制在10%以内多了不划算。校准集我建议准备250张左右覆盖各类别和各种光照条件。如果某一类样本特别少量化后那一类的精度会明显偏低这时候要么补样本要么在损失函数里加权。6.3 后续可扩展的方向这套流程跑通后可以往几个方向扩。一是把分类模型和检测模型串起来先检测再分类做细粒度识别。二是试试知识蒸馏用大模型教小模型把量化后的精度再拉回来一点。三是探索RV1106的多模型并行NPU能不能同时跑两个小模型这个我还没深入测但理论上如果内存够是有可能的。最后分享一个我在实际调试中的小习惯每次转换完模型先在PC端用RKNN的仿真模式跑一遍验证集确认精度没问题再推到板端。这样能把转换问题和板端运行问题分开排查效率高很多。板端调试本来就慢能省一步是一步。