RV1103端侧图像分类模型选型与部署实战:从MobileNetV2到RKNN量化调优
发布时间:2026/10/7 1:01:38 锦皓数字建站

图像分类这件事放在服务器上跑早就不是什么新鲜事了但要把一个能用的分类模型塞进瑞芯微RV1103这种级别的芯片里并且让它稳定跑起来这里面的门道和坑远比在PC上跑通一个demo要多得多。RV1103这颗芯片定位很明确——低功耗、低成本、带NPU的视觉处理SoC经常出现在智能门锁、猫眼、小型IPC、玩具机器人这类产品里。它的算力有限内存也紧所以“什么模型能跑、怎么跑、跑多快、精度掉多少”这几个问题是每一个做端侧视觉的工程师都绕不开的。这篇内容就是把我自己在RV1103上折腾各种主流图像分类模型的完整过程、数据、踩过的坑和最后的取舍逻辑原原本本梳理出来。不管你是刚拿到RV1103开发板的新手还是已经在端侧部署上摸爬滚打过的老手应该都能从里面找到对自己有用的东西。我会从芯片本身的硬件约束讲起再到模型选型的逻辑、转换工具链的实操、量化过程中的精度损失分析最后给出不同场景下的推荐方案。1. RV1103的硬件底子决定了模型选型的边界在聊模型之前必须先搞清楚RV1103到底能给模型提供什么样的运行环境。很多人拿到芯片第一反应是“NPU算力多少TOPS”但实际上对于图像分类这种任务制约因素远不止算力一项。RV1103的NPU算力官方标称是0.5TOPS左右INT8这个数字放在今天看确实不大但真正卡脖子的往往是内存带宽和可用RAM容量。1.1 算力、内存与带宽的三重约束RV1103通常搭配的是内置的DDR容量根据具体型号和封装有所不同常见的是64MB或者128MB的片内DRAM。注意这个内存是要被系统、NPU、CPU、ISP等模块共享的。你不可能把全部内存都给模型用。在实际项目中留给模型推理的可用内存往往只有几十MB甚至更少。这意味着什么意味着一个动辄几十MB的浮点模型光是加载进去就把内存吃光了更别提推理过程中的中间张量。所以第一层筛选就来了模型参数量必须足够小同时中间激活值也不能太大。像ResNet-50这种25MB以上FP32的模型在RV1103上基本没有实用价值即使量化到INT8也有十几MB加上运行时开销内存直接爆掉。真正适合RV1103的是MobileNet系列、ShuffleNet系列、SqueezeNet、以及一些专门为端侧设计的轻量网络。带宽方面NPU访问DDR的频率和位宽是固定的模型越大、中间特征图越大带宽压力越大推理延迟就越高。我实测下来在RV1103上一个1MB左右的INT8模型单帧推理时间可以做到十几毫秒而一个5MB的模型推理时间可能直接飙到80ms以上而且帧率不稳定。这个非线性关系很关键它说明在RV1103上模型大小和推理延迟之间不是简单的线性关系超过某个阈值后会急剧恶化。1.2 NPU支持的操作类型与算子限制RV1103的NPU不是万能的它支持的算子集合是有限的。常见的卷积、深度可分离卷积、全连接、池化、BN、ReLU这些都没问题但一些特殊的激活函数比如Swish、GELU、特殊的注意力机制比如SE模块里的某些操作、以及动态shape的操作NPU可能不支持或者支持得不好会回退到CPU上执行。一旦回退到CPU速度会慢一个数量级整个推理时间就被拖垮了。这一点在选模型的时候特别重要。比如MobileNetV3里用到了hard-swish和SE模块hard-swish在NPU上通常是可以支持的但SE模块里的global pooling和excitation操作在某些工具链版本里会被拆解成多个算子甚至部分回退CPU。我在RV1103上跑MobileNetV3的时候就遇到过SE模块导致推理时间比预期多了30%的情况。后来换成MobileNetV2虽然精度略低一点但推理速度稳定了很多。所以选模型的第一原则是优先选算子结构简单、NPU原生支持度高的网络。不要只看论文里的精度和FLOPs要看工具链的实际支持情况。1.3 输入分辨率对推理开销的影响输入分辨率是另一个容易被忽视的因素。很多人习惯性地把输入设成224x224因为这是ImageNet的标准。但在RV1103上224x224的输入意味着第一层卷积的计算量和中间特征图的内存占用都会显著增加。我做过对比测试同一个MobileNetV2模型输入从224x224降到160x160推理时间减少了大约40%而Top-1精度在ImageNet上只掉了不到2个百分点。如果降到128x128推理时间进一步减少但精度掉得就比较明显了大概会掉5-8个百分点。这里的关键是你的实际应用场景需要多高的分辨率。如果是做人脸识别或者物体分类目标在画面中占比比较大128x128甚至96x96可能就够了。如果是做场景分类需要看整体画面那160x160或192x192是比较平衡的选择。不要盲目追求224x224那是在浪费RV1103宝贵的算力。2. 主流轻量分类模型的实测对比与选型逻辑搞清楚硬件边界之后接下来就是具体选哪些模型来测。我选了目前在端侧比较主流的几个轻量分类网络MobileNetV2、MobileNetV3-Small、ShuffleNetV2、SqueezeNetV1.1以及瑞芯微官方模型库里的几个预训练分类模型。测试平台就是一块标准的RV1103开发板工具链用的是瑞芯微官方的RKNN-Toolkit2具体版本后面会说系统跑的是官方Buildroot镜像。2.1 参数量、精度与推理延迟的三角权衡先给一个总览表格这是我在RV1103上实测的数据输入分辨率统一为160x160INT8量化单帧推理时间取100次平均模型参数量(INT8)ImageNet Top-1推理时间(ms)内存占用(MB)MobileNetV2~3.5MB71.8%18-22~12MobileNetV3-Small~2.5MB67.4%15-19~10ShuffleNetV2 1.0x~2.3MB69.4%16-20~10SqueezeNetV1.1~1.2MB58.2%10-13~7官方ResNet18~11MB69.8%65-80~35从这张表可以读出几个关键信息。第一MobileNetV2在精度和速度之间取得了最好的平衡71.8%的Top-1精度在RV1103上完全可用推理时间控制在20ms以内意味着理论上可以做到40FPS以上当然实际还要看前后处理。第二MobileNetV3-Small虽然参数量更小但精度掉了4个多百分点而且因为SE模块的存在推理时间并没有比V2快多少性价比反而不如V2。第三SqueezeNetV1.1速度最快但精度只有58.2%只适合对精度要求极低的场景。第四官方ResNet18虽然精度不错但推理时间直接飙到65ms以上内存占用也大在RV1103上实用性很差。2.2 为什么MobileNetV2成了我的默认选择经过多轮测试我最终把MobileNetV2作为RV1103上的默认分类模型。原因不只是上面表格里的数据还有一些实际部署中的细节。MobileNetV2的核心是倒残差结构Inverted Residual和线性瓶颈Linear Bottleneck。倒残差结构先用1x1卷积升维再做3x3深度可分离卷积最后再用1x1卷积降维。这个结构在NPU上的算子映射非常干净几乎全部可以原生执行没有算子回退的问题。线性瓶颈去掉了降维后的ReLU减少了信息损失对量化也更友好。相比之下MobileNetV3虽然引入了NAS搜索出来的结构理论上更优但它的SE模块和hard-swish激活在工具链里的支持不够完美。我试过用RKNN-Toolkit2转换MobileNetV3转换过程中就报了几个警告说某些算子会被拆解。实际跑起来推理时间波动比较大有时候18ms有时候25ms稳定性不如V2。ShuffleNetV2的channel shuffle操作在NPU上需要额外的数据重排虽然工具链支持但会引入额外的开销。实测下来ShuffleNetV2 1.0x的推理时间和MobileNetV2差不多但精度低了2个多百分点所以也被我排除了。2.3 量化前后的精度损失到底有多大这是很多人关心的问题。FP32模型量化到INT8精度肯定会掉但掉多少取决于模型结构和量化策略。我在RV1103上做了详细的对比测试。对于MobileNetV2FP32下ImageNet Top-1是72.0%官方数据INT8量化后掉到71.8%只掉了0.2个百分点。这个损失几乎可以忽略。但要注意这是用完整的ImageNet验证集测出来的如果你自己的数据集分布和ImageNet差异比较大量化后的精度损失可能会更大。MobileNetV3-Small的量化损失就明显一些FP32下是67.4%INT8后掉到66.1%左右掉了1.3个百分点。这主要是因为SE模块和hard-swish对量化的敏感度更高。SqueezeNetV1.1的量化损失最大FP32下60.0%左右INT8后只有58.2%掉了近2个百分点。SqueezeNet本身结构比较激进大量使用1x1卷积和fire模块量化时信息损失比较严重。这里有一个实操经验量化时一定要用真实场景的数据做校准。RKNN-Toolkit2默认用的是随机数据或者ImageNet的少量样本做量化校准如果你做的是特定场景的分类比如工业零件分类、农作物病害分类一定要用自己的数据集里的图片做校准否则量化后的精度可能掉得你怀疑人生。我做过一个工业零件分类的项目用默认校准数据量化后精度掉了15个百分点换成自己的数据校准后精度损失控制在2个百分点以内。3. RKNN工具链的完整转换流程与参数调优模型选好了接下来就是把它转换成RV1103能跑的格式。瑞芯微的NPU用的是RKNN格式需要通过RKNN-Toolkit2把ONNX或者TensorFlow模型转成RKNN。这个过程看起来简单但里面的参数和坑非常多。3.1 环境搭建与工具链版本选择RKNN-Toolkit2的版本选择很重要。不同版本的Toolkit对算子支持、量化策略、以及RV1103的适配程度都不一样。我建议用较新的稳定版本比如1.6.0或以上。太老的版本可能不支持某些算子太新的版本可能还有bug。环境搭建在Ubuntu 20.04或22.04上比较顺利。Python版本建议用3.8到3.10太新的Python版本可能会有依赖问题。安装命令大致如下pip install rknn-toolkit2 -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后可以用rknn.config和rknn.load_onnx来验证环境是否正常。注意RKNN-Toolkit2是跑在PC上的用来做模型转换和量化不是跑在RV1103上的。RV1103上跑的是RKNN Runtime是另一套东西。3.2 从ONNX到RKNN的转换步骤与关键参数假设你已经有了一个训练好的MobileNetV2 ONNX模型转换的核心代码如下from rknn.api import RKNN rknn RKNN(verboseTrue) # 配置参数 rknn.config( mean_values[[123.675, 116.28, 103.53]], std_values[[58.395, 57.12, 57.375]], target_platformrv1103, quantized_dtypeasymmetric_quantized-8, optimization_level3 ) # 加载ONNX模型 ret rknn.load_onnx(modelmobilenetv2.onnx) if ret ! 0: print(Load ONNX failed) exit(ret) # 构建RKNN模型 ret rknn.build(do_quantizationTrue, dataset./calibration_dataset.txt) if ret ! 0: print(Build RKNN failed) exit(ret) # 导出RKNN模型 ret rknn.export_rknn(mobilenetv2_rv1103.rknn) if ret ! 0: print(Export RKNN failed) exit(ret)这里面有几个关键参数需要解释。mean_values和std_values是预处理参数必须和你训练模型时用的预处理一致。很多人转换后精度不对就是因为这里没对齐。target_platform必须设成rv1103否则生成的模型可能不兼容。quantized_dtype一般用asymmetric_quantized-8这是RV1103 NPU支持的量化类型。optimization_level设成3会做更多的图优化通常能提升推理速度但偶尔也可能引入问题如果遇到精度异常可以降到2试试。dataset参数指向一个txt文件里面每一行是一张校准图片的路径。这个校准数据集非常关键前面已经强调过了。一般准备100到500张图片就够了要覆盖你实际场景中的各种情况。3.3 转换过程中最常见的报错与处理转换过程中最容易遇到的报错有这么几类。第一类是算子不支持报错信息里会明确说哪个算子不支持或者被回退。遇到这种情况要么换模型结构要么自己实现自定义算子比较麻烦要么接受CPU回退但速度会慢。第二类是shape不匹配通常是ONNX模型的输入shape和config里设置的不一致检查一下ONNX模型的输入定义。第三类是量化校准失败可能是校准图片路径不对或者图片格式有问题确保图片能被正常读取。还有一个坑是输入layout的问题。RKNN默认期望的输入是NHWC格式但很多ONNX模型是NCHW格式。RKNN-Toolkit2在加载ONNX时通常会自动处理但有时候会出问题。如果转换后推理结果完全不对可以检查一下是不是layout搞反了。4. 板端部署与推理性能的实测调优模型转换成RKNN之后就要放到RV1103板子上跑了。板端部署涉及Runtime的集成、输入输出的处理、以及性能调优。4.1 RKNN Runtime在Buildroot中的集成方式RV1103的官方Buildroot镜像里通常已经包含了RKNN Runtime的库librknnmrt.so。如果没有需要自己交叉编译或者从官方SDK里拷贝。集成方式一般是在你的应用程序里链接这个库然后调用C接口。板端推理的核心流程是初始化RKNN context加载模型设置输入运行推理获取输出。C接口的函数名大概是rknn_init、rknn_query、rknn_inputs_set、rknn_run、rknn_outputs_get这些。具体用法可以参考官方SDK里的示例代码。需要注意的是RV1103的内存比较紧张加载模型的时候要确保有足够的内存。如果同时跑多个模型或者有其他内存占用大的进程可能会加载失败。建议在系统启动后尽早加载模型并尽量复用context。4.2 输入预处理的优化别让CPU拖了后腿很多人只关注NPU的推理时间忽略了CPU做预处理的时间。在RV1103上CPU性能也不强如果预处理写得不好可能比NPU推理还慢。常见的预处理包括图像缩放、颜色空间转换比如YUV到RGB、归一化。这些操作如果全部用CPU做在160x160的输入下可能就要花5到10ms。优化方法有几个一是用RV1103的硬件加速模块比如RGARaster Graphic Acceleration来做缩放和格式转换速度比CPU快很多二是把归一化参数直接融合到模型里通过RKNN的mean_values和std_values配置这样板端就不需要再做归一化了三是尽量用定点运算代替浮点运算。我实测过用RGA做预处理加上模型内置归一化整个预处理时间可以压到2ms以内比纯CPU实现快了3到4倍。4.3 多模型切换与内存复用的实战经验在实际产品中有时候需要跑多个模型比如一个检测模型加一个分类模型。RV1103的内存有限同时加载多个模型可能会OOM。这时候可以考虑模型切换的方案先加载模型A跑完推理后释放再加载模型B。但频繁加载释放会有开销影响实时性。另一个方案是内存复用。RKNN Runtime支持在一定条件下复用内存但需要仔细管理。我的经验是如果两个模型不会同时运行可以在初始化时只加载一个另一个在需要时再加载。如果两个模型需要交替运行最好确保它们的内存占用总和不超过可用内存并且尽量使用相同的输入输出buffer。还有一个技巧是把多个小模型合并成一个大模型。比如把特征提取部分共享后面接不同的分类头。这样只需要加载一个模型减少了内存开销和切换开销。不过这需要重新训练和转换模型适合对精度要求不极致的场景。5. 不同应用场景下的模型选择建议前面讲的都是通用性的内容最后落到具体场景上选择会有所不同。我按几个典型的RV1103应用场景来给出建议。5.1 智能门锁/猫眼的人脸属性分类这类场景通常需要判断画面中是否有人、人的性别、年龄范围、是否戴眼镜等属性。输入分辨率不需要太高128x128或96x96就够了因为人脸在画面中占比通常比较大。模型选择上MobileNetV2输入128x128是首选精度足够速度可以做到10ms以内。如果对速度要求极高可以考虑SqueezeNetV1.1但精度会差一些需要在实际数据上微调。这类场景的一个关键是数据分布要和训练数据匹配。门锁摄像头拍出来的人脸角度、光照条件和ImageNet差异很大所以最好在自己的数据上做fine-tune然后再量化。量化校准也要用门锁场景的图片。5.2 工业零件缺陷分类工业场景对精度要求高而且往往需要区分非常细的类别。RV1103的算力有限如果类别数很多比如几十类MobileNetV2可能不够用。这时候可以考虑两个方案一是用更大的输入分辨率比如192x192或224x224但推理时间会增加二是用级联的方式先用一个粗分类模型缩小范围再用一个细分类模型做精确判断。工业场景的另一个特点是光照和背景相对固定这对模型来说是好消息因为可以减少数据增强的复杂度。但要注意工业相机的图像格式可能是灰度图或者Bayer格式预处理需要相应调整。5.3 智能玩具/机器人的场景分类这类场景对成本敏感对精度要求不高但要求实时性好。SqueezeNetV1.1或者MobileNetV3-Small如果工具链支持好的话可以用。输入分辨率可以降到96x96推理时间可以压到10ms以内。如果场景分类的类别很少比如只有室内、室外、草地、天空几类甚至可以用更小的自定义网络。这类场景的一个坑是运动模糊。玩具或机器人在移动时摄像头拍出来的画面容易模糊影响分类精度。解决方法一是提高快门速度二是训练时加入运动模糊的数据增强三是用多帧投票的方式平滑输出。5.4 模型更新与OTA升级的考虑产品出货后可能需要更新模型。RV1103的存储空间有限模型文件不能太大。RKNN模型通常比原始ONNX小很多因为量化了MobileNetV2的RKNN文件大概3到4MBOTA升级是可以接受的。但要注意升级模型时可能需要同时更新Runtime库如果Runtime版本不兼容新模型可能跑不起来。建议在设计OTA方案时把模型和Runtime的版本管理考虑进去。另外模型更新后预处理参数mean/std可能也会变板端代码要能适配。最好把预处理参数也做成可配置的而不是硬编码在代码里。6. 那些文档里不会写的踩坑记录最后这部分我整理了一些在实际项目中踩过的坑都是文档里不会写、但实际会遇到的。第一个坑是量化校准数据集的代表性。前面提过但值得再强调。我做过一个项目校准数据集用了ImageNet的图片结果在实际场景中精度掉了20多个百分点。后来换成实际场景的图片精度恢复到了正常水平。校准数据集一定要覆盖实际场景的各种光照、角度、背景。第二个坑是RKNN模型在不同Toolkit版本间的兼容性。用Toolkit 1.4转换的模型在Runtime 1.6上可能跑不起来或者结果不对。反过来也一样。所以模型转换和板端Runtime的版本要匹配最好用同一个SDK版本里的Toolkit和Runtime。第三个坑是输入图像的stride对齐。RV1103的NPU对输入图像的stride有要求如果图像的width不是某个值的倍数比如16可能会导致推理结果错位或者性能下降。解决方法是在预处理时把图像padding到对齐的尺寸或者在模型转换时设置合适的stride参数。第四个坑是多线程推理的线程安全问题。RKNN Runtime的context不是线程安全的如果多个线程同时用同一个context跑推理会出现各种奇怪的问题。解决方法是为每个线程创建独立的context或者加锁串行化。但创建多个context会消耗更多内存在RV1103上要谨慎。第五个坑是温度对推理稳定性的影响。RV1103在长时间高负载运行后芯片温度会升高可能导致NPU降频推理时间变长。在散热不好的产品里这个问题尤其明显。解决方法一是加散热片二是控制推理频率不要持续满负载跑三是监控温度在温度过高时降低帧率。第六个坑是模型输出的后处理。RKNN模型的输出通常是量化后的整数需要反量化才能得到实际的概率值。反量化的参数scale和zero_point在转换时确定板端代码要正确使用。如果后处理写错了分类结果会完全乱掉。建议在PC上用RKNN-Toolkit2的仿真功能先验证后处理逻辑再移植到板端。这些坑每一个都让我多花了不少时间。希望看到这里的你能少走一些弯路。RV1103是一颗很有性价比的芯片只要模型选得对、转换参数调得好、预处理优化到位跑一个实用的图像分类任务是完全没有问题的。关键是要理解它的边界在边界内做设计而不是硬塞一个不适合的模型进去。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。