☰
RV1106边缘AI实战:图像分类模型选型与INT8量化部署指南
2026/10/19 6:42:36 网站建设 项目流程

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

边缘端跑视觉模型这件事,这两年从"能不能跑"变成了"怎么跑得划算"。瑞芯微RV1106这颗芯片在低功耗IPC、智能门锁、猫眼、宠物喂食器这类产品里出镜率很高,它自带NPU,官方标称算力在0.5TOPS这个量级,配合内置的ISP和轻量级CPU,整颗芯片的定位就是"用最低的物料成本完成一路视频的采集、编码和基础AI推理"。很多做硬件的朋友拿到开发板第一反应是:既然有NPU,那我直接把MobileNet、ResNet搬上去不就行了?实测下来,事情没这么简单。

我这段时间陆陆续续在RV1106上跑了几个主流的图像分类模型,包括MobileNetV2、ResNet18、EfficientNet-Lite、ShuffleNetV2,还有几个专门为边缘设计的轻量网络。整个过程踩了不少坑,也积累了一些在官方文档里不太容易找到的经验。这篇文章不打算写成一份"官方文档复读机",而是想把每个模型在RV1106上的真实表现、转换过程中的取舍、量化带来的精度损失、以及最终怎么选型这些事讲清楚。如果你正在做边缘视觉产品选型,或者手头正好有一块RV1106的开发板想跑点东西,这篇内容应该能帮你省下不少试错时间。

需要先说明的是,RV1106的NPU对算子支持是有边界的,它不是那种"什么模型都能塞进去"的通用加速器。它的强项在于卷积、池化、全连接这些常规算子,对某些特殊结构(比如某些注意力机制、动态shape操作)支持有限。所以选模型的第一原则不是"哪个精度高",而是"哪个能被完整转换且量化后精度掉得少"。这个思路会贯穿全文。

2. RV1106的NPU到底能吃下什么

2.1 算力账要算清楚,别被标称值忽悠

RV1106的NPU算力标称0.5TOPS,这个数字是INT8精度下的理论峰值。什么意思呢?就是说你的模型必须量化成INT8,才能跑到这个算力。如果你拿一个FP32的模型直接往里塞,要么转换失败,要么回退到CPU上跑,那速度就是另一个故事了。我实测过,一个FP32的MobileNetV2在RV1106的CPU上跑一张224x224的图,单帧推理时间在300ms以上,基本没有实用价值。而量化成INT8走NPU,同样的模型能压到20ms以内,差距是十几倍。

所以第一件事:上RV1106的模型,必须走INT8量化路线。这不是可选项,是必选项。量化工具链官方提供的是RKNN-Toolkit2,它能把ONNX、TensorFlow、PyTorch等格式的模型转成RKNN格式,并在转换过程中完成量化。量化需要一份校准数据集,通常准备100到500张和实际场景分布接近的图片就够了。校准集的质量直接决定量化后的精度,这一点后面会展开讲。

2.2 算子支持清单决定了你的模型能不能转

RKNN-Toolkit2在转换时会逐层检查算子支持情况。如果遇到不支持的算子,它会报错或者把这一层回退到CPU。回退到CPU的层会成为整个推理流程的瓶颈,因为NPU和CPU之间的数据搬运开销很大。我遇到过最典型的情况是某些模型里的HardSwish激活函数,在早期版本的Toolkit里不支持,导致整个MobileNetV3转出来有一大半层跑在CPU上,速度比纯CPU还慢。

解决办法有两个:一是升级Toolkit版本,新版本通常会补充算子支持;二是在导出ONNX之前就把不支持的算子替换成等价的受支持算子组合。比如HardSwish可以用ReLU6加一些逐元素运算来近似,虽然会损失一点精度,但能保证全部跑在NPU上。这个取舍在边缘部署里非常常见,能全跑NPU的次优模型,往往比部分跑CPU的最优模型更实用。

2.3 内存带宽是隐藏的瓶颈

RV1106的NPU算力虽然不高,但对于轻量模型来说够用。真正容易出问题的是内存带宽。NPU在推理时需要频繁读写特征图,如果模型的特征图通道数很大、分辨率很高,内存带宽就会成为瓶颈,表现为算力利用率上不去。我实测发现,当输入分辨率从224x224提升到320x320时,某些模型的推理时间增长并不是线性的,而是超线性的,原因就是特征图变大后带宽压力陡增。

所以在RV1106上,输入分辨率的选择要克制。224x224对于大多数分类任务已经够用,如果非要上320x320,建议先测一下带宽是否扛得住。另外,模型的通道数也要控制,像ResNet那种动辄512、1024通道的结构,在RV1106上跑起来带宽压力会很明显。

3. 四个主流模型的实测数据与踩坑记录

3.1 MobileNetV2:最稳的选择,但量化有讲究

MobileNetV2是我在RV1106上跑得最顺的模型。它的深度可分离卷积结构对NPU友好,算子全部受支持,转换过程基本没有障碍。我用ImageNet预训练权重,输入224x224,校准集用了200张来自实际场景的图片,量化后Top-1精度从FP32的71.8%掉到70.2%,损失1.6个百分点,完全可以接受。推理时间稳定在18到22ms之间,具体取决于CPU负载。

但这里有个坑:MobileNetV2的量化对校准集非常敏感。我第一次用随机从网上下的图片做校准,精度掉了将近5个百分点。后来换成和实际部署场景一致的图片(比如都是室内场景、光照条件接近),精度损失就控制在了2个点以内。原因是量化需要统计激活值的动态范围,如果校准集和实际输入分布差异大,量化参数就会偏,导致精度崩塌。所以校准集一定要用真实场景的数据,哪怕只有100张,也比1000张不相关的图强。

另一个经验是,MobileNetV2的最后一层全连接之前有个Dropout,导出ONNX时记得把它去掉,否则转换会报错。这个在训练时有用,推理时是多余的。

3.2 ResNet18:精度高但带宽吃紧

ResNet18的精度比MobileNetV2高不少,FP32下Top-1能到69.8%(这里用的是简化版训练,不是完整ImageNet),但它在RV1106上的表现就没那么理想了。转换本身没问题,残差结构里的Add算子NPU是支持的,但推理时间明显更长,224x224输入下要35到40ms。原因就是前面说的带宽问题,ResNet18的特征图通道数最大到512,中间层的特征图尺寸也不小,NPU在读写这些数据时带宽压力很大。

我试过把ResNet18的输入降到160x160,推理时间能压到25ms左右,但精度也相应下降。所以ResNet18在RV1106上的定位比较尴尬:精度比轻量模型高不了太多,速度却慢一截。除非你的任务对精度要求特别苛刻,否则不太推荐。如果非要用,建议做通道剪枝,把每层的通道数砍掉30%到40%,再重新微调,这样能在精度损失可控的前提下显著降低带宽压力。

3.3 EfficientNet-Lite:结构优雅但转换有暗坑

EfficientNet-Lite是专门为边缘设备设计的,去掉了SE模块和Swish激活,理论上应该很适合RV1106。实际转换时确实比原版EfficientNet顺利,但还是在MBConv结构里的某些逐元素乘法上遇到了问题。具体来说,EfficientNet-Lite的MBConv里有一个Multiply操作,用于把两个分支的特征图相乘,这个操作在早期Toolkit版本里不支持,导致转换失败。

解决办法是升级到较新的RKNN-Toolkit2版本,新版本已经支持了这个算子。升级后转换通过,量化后精度损失在2.5个百分点左右,推理时间224x224下约28ms。这个成绩介于MobileNetV2和ResNet18之间,精度也介于两者之间。EfficientNet-Lite的优点是参数量小,模型文件只有几MB,适合存储空间紧张的场景。但它的推理时间对输入分辨率更敏感,分辨率一上去速度掉得很快,所以建议固定在224x224使用。

3.4 ShuffleNetV2:速度最快但精度是短板

ShuffleNetV2的设计目标就是极致轻量,它在RV1106上的推理时间是最短的,224x224下只要12到15ms。通道混洗操作NPU是支持的,转换很顺利。但代价是精度,FP32下Top-1只有65%左右,量化后掉到63%上下。这个精度对于简单的二分类或者粗粒度分类任务够用,比如区分"有人"和"没人"、"猫"和"狗",但要做细粒度分类就力不从心了。

我的建议是,如果你的任务类别数少于10类,且类间差异明显,ShuffleNetV2是性价比最高的选择。但如果类别数超过50类,或者类间差异细微(比如不同品种的花),还是老老实实用MobileNetV2或者EfficientNet-Lite。

3.5 四个模型的横向对比

模型FP32 Top-1INT8 Top-1推理时间(224x224)模型大小转换难度
MobileNetV271.8%70.2%18-22ms14MB低
ResNet1869.8%68.5%35-40ms45MB低
EfficientNet-Lite72.5%70.0%26-30ms18MB中
ShuffleNetV265.0%63.2%12-15ms8MB低

这张表里的数据是我在同一个开发板上反复测出来的,测试条件统一:单帧推理、CPU空闲、室温环境。实际产品中如果CPU还要跑其他任务,推理时间会有所波动。从表里能看出来,MobileNetV2在精度和速度之间取得了最好的平衡,这也是为什么它成了我在RV1106上的默认选择。

4. 从训练到部署的完整链路拆解

4.1 模型导出ONNX时的三个关键设置

不管你用什么框架训练,最终都要导出成ONNX再喂给RKNN-Toolkit2。导出时有三个设置必须注意,否则后面转换会出各种幺蛾子。

第一,固定输入shape。RV1106的NPU不支持动态shape,所以导出ONNX时要把输入的batch size和分辨率都固定死。比如input_shape = [1, 3, 224, 224],不要用None。如果训练时用了动态shape,导出前记得改成固定值。

第二,去掉训练专用的层。Dropout、BatchNorm的训练模式、各种数据增强算子,这些在推理时都不需要,导出前要切换到eval模式并移除。PyTorch里用model.eval(),TensorFlow里用training=False。BatchNorm在eval模式下会使用训练时统计的均值和方差,这个行为要确认清楚,否则量化时会有偏差。

第三,算子版本要匹配。ONNX有不同的opset版本,RKNN-Toolkit2对opset版本有要求,太高或太低都可能出问题。我一般用opset 11或12,这两个版本兼容性最好。如果导出时报错说某个算子不支持,先试试降opset版本。

4.2 校准集制作:100张图怎么选

校准集的作用是让量化工具统计每一层激活值的分布,从而确定量化的缩放因子和零点。校准集选得好,量化精度损失就小;选得不好,精度可能直接崩掉。我的经验是,校准集不需要多,100到200张就够,但必须满足两个条件:分布覆盖和场景一致。

分布覆盖的意思是,校准集里的图片要涵盖实际部署中可能出现的各种情况。比如你的产品是智能门锁,那校准集里就要有白天、夜晚、逆光、侧光、有人、没人、不同肤色、不同角度的人脸图片。如果只放白天的图片,晚上部署时精度就会掉。场景一致的意思是,校准集不能拿网上的通用图片凑数,要用实际设备采集的图片。因为不同摄像头的色彩响应、噪声特性都不一样,用A摄像头的数据校准B摄像头的模型,效果会打折扣。

具体操作上,我会从实际场景里采集300张左右的图片,然后人工筛选出150张覆盖各种情况的,作为校准集。筛选标准是:不重复、不极端、有代表性。极端图片(比如全黑、全白、严重过曝)反而会拉偏量化参数,要剔除。

4.3 量化精度损失的定位方法

量化后精度掉了,怎么知道是哪一层的问题?RKNN-Toolkit2提供了逐层量化的分析工具,可以输出每一层的量化误差。我一般会先看整体精度损失,如果损失在2个百分点以内,基本可以接受;如果超过3个点,就要逐层排查。

排查方法是:把量化后的模型和FP32模型在同一个验证集上跑,对比每一层的输出差异。差异大的层就是问题层。常见的问题层有两类:一是激活值动态范围特别大的层,比如某些ReLU之后的输出,最大值和最小值差距悬殊,量化时分辨率不够;二是通道数特别少的层,比如深度可分离卷积里的逐通道卷积,通道数少导致量化统计不充分。

针对第一类问题,可以在训练时加入Clipped ReLU,把激活值限制在一个合理范围内,减小动态范围。针对第二类问题,可以考虑把这层保持FP32不量化,虽然会牺牲一点速度,但能保住精度。RKNN-Toolkit2支持混合量化,可以指定某些层不量化。

4.4 板端部署的完整流程

模型转成RKNN格式后,部署到板子上分几步。首先是把RKNN文件推到开发板的文件系统里,通常放在/userdata或者/oem分区。然后写一个C或者Python的推理程序,调用RKNN的运行时API加载模型、输入数据、执行推理、取回结果。

C版本的推理程序性能最好,适合最终产品。Python版本适合快速验证,但因为有解释器开销,速度会慢一些。我一般先用Python版本验证模型能跑通、精度对得上,然后再移植到C版本做性能优化。

推理程序里有个细节要注意:输入数据的预处理要和训练时完全一致。包括归一化的均值方差、通道顺序(RGB还是BGR)、resize的插值方式。这些细节如果对不上,精度会莫名其妙地掉。我踩过一次坑,训练时用的是RGB,部署时摄像头输出的是BGR,忘了转换,结果精度掉了十几个点,排查了半天才发现是通道顺序的问题。

5. 实测中那些文档不会告诉你的坑

5.1 开发板供电不足导致NPU降频

这个坑很隐蔽。RV1106的NPU在满载运行时功耗会上升,如果开发板的供电能力不足,电压会被拉低,芯片为了保护自己会自动降频,表现为推理时间忽快忽慢。我一开始以为是模型的问题,后来换了一个供电能力更强的USB口才解决。所以如果你发现推理时间不稳定,先检查供电,别急着怀疑模型。

5.2 温度对推理时间的影响

RV1106在长时间连续推理后芯片温度会上升,温度高了之后NPU也会降频。我做过一个连续跑一小时的测试,前十分钟推理时间稳定在20ms,半小时后升到25ms,一小时后稳定在28ms左右。所以如果你的产品需要7x24小时运行,散热设计要跟上,至少加个散热片或者留出足够的空气流通空间。

5.3 多线程推理的陷阱

RV1106的CPU核心数不多,NPU是单核的。如果你开多个线程同时调用NPU推理,不但不会加速,反而会因为资源竞争导致整体吞吐下降。正确的做法是单线程串行推理,或者用一个生产者-消费者队列,让NPU推理和前后处理流水线化。我试过开两个线程同时推理,结果总吞吐比单线程还低15%左右。

5.4 模型文件加载时间不可忽略

RKNN模型在第一次加载时需要初始化NPU,这个过程大概需要几百毫秒。如果你的应用是间歇性推理(比如门锁只在有人靠近时才推理),这个加载时间会影响响应速度。解决办法是在程序启动时就加载好模型,保持NPU处于就绪状态,而不是每次推理都重新加载。

6. 选型建议与后续优化方向

6.1 什么场景选什么模型

经过这一轮实测,我的选型建议是这样的:如果任务类别少、类间差异大、对速度要求极高,选ShuffleNetV2;如果追求精度和速度的平衡,选MobileNetV2,这也是最稳妥的选择;如果存储空间紧张且能接受稍慢的速度,选EfficientNet-Lite;ResNet18在RV1106上性价比不高,除非你有特殊的精度需求,否则不推荐。

还有一个维度是输入分辨率。224x224是甜点分辨率,再往上速度掉得快,再往下精度掉得快。如果实在需要更高分辨率,建议先做感兴趣区域裁剪,把目标区域裁出来再送进模型,而不是直接提高整图分辨率。

6.2 量化感知训练能救回多少精度

如果你的任务对精度要求很高,量化后损失两三个点都不能接受,可以考虑量化感知训练(QAT)。QAT是在训练阶段就模拟量化的效果,让模型提前适应量化带来的误差。我试过在MobileNetV2上做QAT,量化后精度损失从1.6个点降到了0.5个点,效果很明显。但QAT的训练成本更高,需要修改训练代码,而且不是所有框架都支持得好。如果时间充裕且精度要求苛刻,值得一试。

6.3 模型剪枝与知识蒸馏的取舍

模型剪枝能直接减少参数量和计算量,对降低带宽压力很有帮助。但剪枝后需要重新微调,否则精度会掉。知识蒸馏是用一个大模型教一个小模型,让小模型学到更好的特征表示。这两种方法可以结合使用,先剪枝再蒸馏,能在保持精度的同时显著压缩模型。不过这些操作都需要额外的训练资源和时间,适合有充足开发周期的项目。

6.4 后续可以尝试的方向

RV1106的NPU还在迭代,新版本的Toolkit会支持更多算子。我建议定期关注官方的更新日志,看看有没有新增的算子支持或者性能优化。另外,混合量化(部分层INT8、部分层FP16)也是一个值得尝试的方向,能在精度和速度之间找到更好的平衡点。还有就是输入管线的优化,比如用硬件ISP直接输出模型需要的格式,减少CPU的预处理开销,这个对整体帧率的提升可能比换模型还明显。

我在实际项目里最后落地的是MobileNetV2加224x224输入加INT8量化这套组合,在RV1106上跑到了稳定20ms左右的单帧推理,精度满足业务需求,功耗和散热也都在可控范围内。这套方案不一定适合所有人,但作为一个经过验证的基线,至少能让你少走一些弯路。

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

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

立即咨询