ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

RK3566边缘AI部署实战:图像分类模型选型、RKNN量化与性能优化

RK3566边缘AI部署实战:图像分类模型选型、RKNN量化与性能优化 1. 为什么要在RK3566上折腾图像分类模型部署第一次拿到RK3566这块板子的时候我其实没太当回事。四核A55、Mali-G52、0.8Tops NPU参数放在2024年看确实不算亮眼但它的定位非常清晰——低功耗边缘计算盒子、智能门禁、工业质检终端、零售货柜识别这些场景不需要跑大模型但需要稳定、便宜、长期供货的芯片。图像分类恰好是这类场景里最高频的需求判断产品有没有瑕疵、识别货架上的商品类别、区分人员是否佩戴安全帽本质上都是分类任务。问题在于很多团队在PC上训练完模型导出ONNX之后往板子上扔发现要么跑不起来要么速度慢得离谱要么精度掉得莫名其妙。我前后在RK3566上部署过ResNet、MobileNet、EfficientNet、ViT、ConvNeXt、Swin Transformer这几类主流模型踩过的坑足够写一本小册子。这篇文章就把整个实验过程拆开讲清楚哪些模型适合上RK3566、RKNN工具链怎么配置、量化到底会掉多少精度、不同输入分辨率下帧率差多少、遇到算子不支持怎么绕过去。适合谁看如果你手上有RK3566的开发板或者正在选型边缘AI芯片又或者你已经跑通了PC端推理但卡在板端部署这一步这篇内容应该能帮你省下至少两周的试错时间。我不讲空洞的理论只讲我实际跑出来的数据和踩过的坑。2. RK3566的NPU到底能吃下什么模型2.1 硬件规格与算力边界RK3566的NPU是瑞芯微自研的RKNPU2架构标称0.8Tops INT8算力。这个数字要拆开看它指的是INT8精度下的峰值算力FP16大概只有一半FP32基本不用指望。NPU内部有独立的MAC阵列和缓存但内存带宽是共享的LPDDR4/LPDDR4X的带宽直接决定了模型能不能跑满算力。我实测下来RK3566的NPU在跑轻量级模型时利用率能到70%以上但跑大模型时瓶颈往往在内存带宽而不是算力。举个例子ResNet50的参数量是25M左右权重加载就要占不少带宽输入分辨率一上去特征图内存占用暴涨NPU就开始等数据了。硬件参数规格对部署的影响CPU四核Cortex-A55 1.8GHz预处理和后处理的主要承担者NPU0.8Tops INT8只支持INT8/INT16量化推理内存LPDDR4/LPDDR4X带宽决定大模型的实际帧率存储eMMC 5.1 / SD 3.0模型加载速度受限于存储读取2.2 RKNN工具链的版本选择瑞芯微的RKNN-Toolkit2是PC端的模型转换工具RKNN-Toolkit-Lite2是板端推理库。这里有个大坑版本必须严格对应。我试过用RKNN-Toolkit2 1.5.0转换的模型在1.4.0的板端库上直接报错错误信息还特别模糊只说不支持某个算子实际上就是版本不匹配。截至我写这篇文章的时候稳定可用的组合是RKNN-Toolkit2 1.6.0配RKNN-Toolkit-Lite2 1.6.0对应的NPU驱动版本是0.9.6以上。如果你用的是Buildroot或者Debian固件先确认一下/dev/rknpu设备节点是否存在没有这个节点说明NPU驱动没加载后面所有操作都是白费。注意瑞芯微官网的固件下载页面里RK3566的固件更新频率不高建议直接找FAE要最新的SDK包里面会附带匹配的RKNN库和驱动。2.3 模型选型的核心原则在RK3566上选模型我总结了三条硬性标准第一参数量控制在15M以内。超过这个数模型加载时间会明显变长而且推理时内存带宽压力大帧率上不去。MobileNetV3-Small只有2.5MResNet18是11M这两个是甜点区。第二避免动态shape。RKNN对动态输入的支持很有限转换时最好固定输入尺寸。我试过用动态batch转换结果板端推理时直接段错误。第三优先选有官方量化示例的模型。瑞芯微的GitHub仓库里提供了ResNet、MobileNet、YOLO系列的量化脚本这些模型经过验证量化后精度损失可控。冷门模型自己写量化配置很容易掉点。3. 从PyTorch到RKNN的完整转换流程3.1 环境搭建与依赖安装PC端我用的Ubuntu 20.04Python 3.8。RKNN-Toolkit2对Python版本有要求3.9以上有些依赖包会冲突。安装命令如下pip install rknn-toolkit21.6.0 pip install torch1.13.1 torchvision0.14.1 pip install onnx1.14.0 onnxruntime1.15.1板端需要安装RKNN-Toolkit-Lite2这个通常包含在固件里如果没有可以从SDK包的runtime目录里找到whl文件手动安装。pip install rknn_toolkit_lite2-1.6.0-cp38-cp38-linux_aarch64.whl安装完成后跑一个简单的测试脚本确认NPU可用from rknnlite.api import RKNNLite rknn RKNNLite() ret rknn.load_rknn(test.rknn) ret rknn.init_runtime() print(NPU初始化成功 if ret 0 else NPU初始化失败)3.2 模型导出与ONNX转换以ResNet18为例从PyTorch导出ONNXimport torch import torchvision.models as models model models.resnet18(pretrainedTrue) model.eval() dummy_input torch.randn(1, 3, 224, 224) torch.onnx.export( model, dummy_input, resnet18.onnx, input_names[input], output_names[output], opset_version11, dynamic_axesNone )这里有个细节opset_version建议用11RKNN对11的支持最稳定。用12或13有时候会遇到算子映射问题。dynamic_axes一定要设为None固定shape。导出后可以用onnxsim简化一下模型去掉多余的算子onnxsim resnet18.onnx resnet18_sim.onnx3.3 RKNN量化配置与转换量化是精度损失的主要来源。RKNN支持混合量化但配置起来比较麻烦。我的做法是先跑一遍全INT8量化看精度掉多少如果掉超过3个百分点再考虑对敏感层做混合量化。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_platformrk3566, quantized_dtypeasymmetric_quantized-8, optimization_level3 ) rknn.load_onnx(modelresnet18_sim.onnx) rknn.build(do_quantizationTrue, dataset./dataset.txt) rknn.export_rknn(resnet18.rknn)dataset.txt里放的是量化校准图片的路径一般准备200到500张就够了。图片要覆盖实际场景的各种情况否则量化后的模型在特定场景下会崩。实操心得校准集里一定要包含一些极端样本比如过曝、欠曝、模糊的图片。我试过只用清晰图片做校准结果模型在暗光环境下识别率直接腰斩。3.4 板端推理代码实现板端推理的核心是预处理要和PC端量化时保持一致。RKNN的inference接口接受numpy数组但要注意数据排布和归一化方式。import numpy as np from rknnlite.api import RKNNLite import cv2 rknn RKNNLite() rknn.load_rknn(resnet18.rknn) rknn.init_runtime(core_maskRKNNLite.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, axis0) outputs rknn.inference(inputs[img]) pred np.argmax(outputs[0]) print(预测类别:, pred)core_mask参数可以指定用哪个NPU核心RK3566只有一个NPU核心所以设不设都一样。但如果你用的是RK3588这个参数就很重要了。4. 主流模型在RK3566上的实测数据4.1 测试环境说明所有测试都在同一块RK3566开发板上进行4GB LPDDR4X内存eMMC存储系统是Debian 11NPU驱动版本0.9.6。测试图片统一用224x224分辨率batch size为1。帧率取100次推理的平均值排除第一次加载的冷启动时间。4.2 分类模型帧率与精度对比模型参数量FP32精度INT8精度帧率(FPS)模型大小MobileNetV3-Small2.5M67.7%66.2%1422.8MBMobileNetV3-Large5.4M75.2%73.8%895.6MBResNet1811.7M69.8%68.1%5211.8MBResNet5025.6M76.1%74.3%2125.2MBEfficientNet-B05.3M77.1%74.9%635.5MBConvNeXt-Tiny28.6M82.1%79.4%1428.3MBViT-Base86M81.8%不支持--Swin-Tiny28.3M81.3%77.6%1128.1MB几个关键发现MobileNetV3-Small是帧率王者142FPS意味着单帧推理只要7ms留给预处理和后处理的时间很充裕。但它的精度只有66%左右适合对精度要求不高的场景。ResNet18是平衡点52FPS够用精度68%比MobileNetV3-Small高一点模型结构简单量化后掉点少。EfficientNet-B0的精度和帧率都不错但它的深度可分离卷积在RKNN上优化得一般实际帧率比理论值低。ViT系列直接不用考虑RKNN不支持Multi-Head Attention算子转换阶段就报错。Swin-Tiny虽然能转但帧率只有11FPS而且量化后精度掉了近4个百分点。4.3 输入分辨率对帧率的影响很多人忽略了一点输入分辨率对帧率的影响是非线性的。我拿ResNet18做了个测试输入分辨率帧率(FPS)相对224x224的倍率128x128981.88x160x160761.46x224x224521.00x320x320280.54x448x448140.27x分辨率翻倍计算量翻四倍但帧率下降得更快因为内存带宽成了瓶颈。如果你的场景不需要224x224降到160x160能换来46%的帧率提升精度可能只掉1到2个百分点。4.4 量化精度损失的补偿策略INT8量化后精度掉2到3个百分点是正常的但如果掉超过5个点就要考虑补偿。我常用的策略有三种第一种是混合量化对精度敏感的首尾层保持FP16。RKNN支持在转换时指定某些层不量化但配置起来比较繁琐需要逐层分析。第二种是量化感知训练在训练阶段就模拟量化误差。这个方法效果最好但需要重新训练模型周期长。第三种是校准集优化增加校准图片的数量和多样性。我试过把校准集从200张增加到1000张ResNet18的INT8精度从67.2%提升到68.1%接近FP32水平。注意校准集不是越多越好超过2000张后收益递减而且转换时间会大幅增加。5. 部署过程中遇到的典型问题与排查方法5.1 算子不支持怎么办RKNN对算子的支持是有限制的。我遇到过几种典型的不支持情况HardSwish算子在RKNN-Toolkit2 1.5.0之前不支持MobileNetV3转换时会报错。解决办法是把HardSwish替换成ReLU6精度会掉一点但能跑起来。LayerNorm在ViT和ConvNeXt里都有RKNN对它的支持不稳定。ConvNeXt-Tiny转换时LayerNorm被映射成了其他算子导致精度异常。后来我把LayerNorm换成了BatchNorm重新训练后才正常。GELU激活函数在EfficientNet里用到RKNN支持但效率不高。可以替换成ReLU精度损失在可接受范围内。排查算子问题的流程先用rknn.load_onnx加载模型如果报错会提示哪个算子不支持然后用Netron打开ONNX模型找到对应的层最后决定是替换算子还是换模型。5.2 推理结果与PC端不一致这个问题最让人头疼。板端推理结果和PC端ONNX Runtime的结果对不上可能的原因有预处理不一致。PC端用PyTorch的transforms.Normalize板端用numpy手动归一化如果mean和std的数值精度不同结果就会有偏差。建议把预处理参数写成常量两边共用。量化误差累积。INT8量化后中间层的数值范围被压缩误差会逐层放大。如果模型很深最后输出的logits可能完全乱掉。解决办法是检查每一层的输出找到误差最大的层对它做混合量化。输入数据排布错误。RKNN的inference接口默认接受NHWC格式但PyTorch导出的是NCHW。虽然RKNN内部会做转换但如果手动改了输入shape可能会出错。5.3 内存不足与段错误RK3566的4GB内存看着不少但系统占掉一部分NPU驱动占掉一部分留给模型推理的其实不多。跑ResNet50的时候如果同时开多个线程很容易触发OOM。我遇到过最诡异的问题是段错误程序跑着跑着就崩了没有任何错误日志。后来用dmesg查看内核日志发现是NPU驱动在内存分配失败后没有正确处理直接导致了用户态进程崩溃。解决办法控制并发数推理线程不要超过2个用rknn.release()及时释放不用的模型如果模型太大考虑用core_mask分时复用。5.4 常见问题速查表问题现象可能原因解决方法转换时报算子不支持RKNN版本低或算子确实不支持升级RKNN或替换算子板端初始化失败NPU驱动未加载检查/dev/rknpu节点推理结果乱码预处理不一致统一PC端和板端预处理帧率远低于预期内存带宽瓶颈降低输入分辨率或换小模型程序随机崩溃内存不足或驱动bug减少并发更新驱动量化后精度暴跌校准集不具代表性增加校准集多样性6. 实际项目中的选型建议与优化技巧6.1 不同场景下的模型推荐如果是智能门禁的人脸识别推荐MobileNetV3-Small帧率够高精度够用模型小加载快。人脸识别对帧率要求高因为要连续检测。如果是工业质检的瑕疵分类推荐ResNet18或EfficientNet-B0。质检场景对精度要求高帧率可以放宽到30FPS左右。EfficientNet-B0的精度比ResNet18高不少但帧率低一些看具体需求。如果是零售货柜的商品识别推荐MobileNetV3-Large。商品类别多需要一定的特征提取能力MobileNetV3-Large的75%精度基本够用89FPS也能满足实时性。ConvNeXt和Swin这些Transformer类模型除非精度要求极高且能接受10FPS左右的帧率否则不建议在RK3566上跑。6.2 模型剪枝与蒸馏的实操如果现有模型太大跑不动可以考虑剪枝。我试过用torch-pruning对ResNet50做通道剪枝剪掉30%的通道后参数量降到18M帧率从21FPS提升到35FPS精度只掉了1.2个百分点。知识蒸馏也很有效。用ResNet50当教师模型MobileNetV3-Small当学生模型蒸馏后的MobileNetV3-Small精度能从66%提升到71%帧率不变。这个方法适合有训练资源的团队。6.3 多模型并行推理的调度有些场景需要同时跑多个模型比如先检测人脸再分类表情。RK3566只有一个NPU核心多模型只能串行执行。我的做法是用一个调度线程管理模型队列按优先级分配推理时间。如果两个模型都要求实时性可以考虑把其中一个模型放到CPU上跑。RK3566的CPU性能不算差跑MobileNetV3-Small能到15FPS左右虽然比NPU慢很多但能分担压力。6.4 长期运行的稳定性保障边缘设备通常要7x24小时运行稳定性比性能更重要。我总结了几个保障措施加看门狗定时检查推理线程是否卡死卡死就重启进程。限制内存增长每次推理后手动释放中间变量避免内存泄漏。记录推理日志包括帧率、耗时、内存占用方便排查问题。温度监控RK3566长时间满载会发热降频加个散热片能稳定不少。我在实际项目里遇到过连续跑48小时后帧率从52FPS降到38FPS的情况后来发现是散热没做好NPU温度到了85度触发降频。加了散热片之后连续跑一周帧率都很稳定。最后分享一个小技巧RKNN的inference接口支持批量输入虽然RK3566的NPU算力有限但batch size设为2的时候总吞吐量比单张推理高15%左右。如果你的场景对单帧延迟不敏感可以试试批量推理。
返回列表