ARTICLE DETAIL

资讯详情

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

RK3576开发板6TOPS NPU实战:模型部署与性能实测

RK3576开发板6TOPS NPU实战:模型部署与性能实测 1. 从一块RK3576开发板说起6TOPS NPU到底能干什么第一次拿到瑞芯微RK3576开发板的时候我盯着规格书里那个6TOPS NPU看了很久。6TOPS是什么概念简单说就是每秒能完成6万亿次定点运算。这个数字放在几年前是服务器级别的算力现在被塞进了一块功耗只有几瓦的开发板里。对于做AIoT项目的人来说这意味着很多以前只能跑在云端或者边缘服务器上的模型现在可以直接在设备端实时推理了。我之所以关注RK3576是因为在实际项目里经常遇到一个尴尬的局面用RK3588做视觉检测性能确实猛但成本和功耗对中低端产品来说有点浪费用RK3568又觉得NPU算力吃紧跑个YOLOv5s都得精打细算。RK3576正好卡在中间——它有6TOPS的NPU算力支持INT4/INT8/INT16混合量化CPU是四核A72加四核A53的big.LITTLE架构GPU是Mali-G52 MC3视频编解码支持到8K。这个配置用来做智能门禁、工业质检、车载DVR、边缘计算盒子这类AIoT场景算是相当均衡的选择。这篇文章主要面向三类人一是正在选型AIoT主控芯片的硬件工程师二是需要在RK3576上部署AI模型的算法工程师三是想了解端侧NPU实际性能表现的技术决策者。我会从NPU的硬件架构讲起然后手把手走一遍模型转换和部署的流程最后给出实测的性能数据。所有测试都是基于我手头这块RK3576开发板实际跑出来的不是抄规格书。需要提前说明的是RK3576的NPU是瑞芯微自研的第三代架构和RK3588的NPU同源但规模不同。它支持常见的深度学习框架模型转换包括TensorFlow、PyTorch、ONNX、Caffe等通过RKNN-Toolkit2工具链转换成RKNN格式后在NPU上运行。整个工具链在Ubuntu环境下使用开发板端支持Android 14和Ubuntu 22.04两种系统。我测试用的是Ubuntu 22.04因为做AI部署的话Linux环境更顺手。2. RK3576的NPU硬件架构与算力分配逻辑2.1 6TOPS算力是怎么来的RK3576的NPU标称6TOPS这个数字是在INT8精度下测得的。NPU内部由多个计算核心组成每个核心包含MAC阵列乘加单元、权重缓存、特征图缓存和专用的数据搬运引擎。6TOPS意味着在1GHz主频下NPU每周期能完成6000次INT8乘加运算。这个算力在端侧芯片里属于中上水平比RK3568的0.8TOPS高了7倍多但只有RK3588的6TOPS的一半左右。这里有个容易混淆的点RK3588的NPU也是标6TOPS但它是三核架构每个核心2TOPS可以独立调度RK3576是单核6TOPS算力集中在一个核心上。这个区别在实际使用中会体现出来——RK3588可以同时跑多个模型并行推理RK3576更适合单模型高吞吐的场景。如果你的AIoT项目只需要跑一个检测模型或者一个分类模型RK3576的性价比明显更高。NPU支持的数据精度包括INT4、INT8、INT16和FP16。INT4主要用于极低比特量化场景比如一些轻量级的关键词识别模型能在保持可用精度的前提下把模型压缩到极致。INT8是主力精度绝大多数视觉模型都用这个。INT16和FP16用于对精度要求较高的场景比如某些医疗影像分析或者高精度回归任务但算力会相应减半。2.2 NPU与CPU/GPU的协同工作方式在实际部署中NPU不是孤立工作的。一个典型的AI推理流水线是这样的摄像头采集图像→VPU视频处理单元解码→CPU做预处理缩放、裁剪、归一化→NPU做模型推理→CPU做后处理NMS、坐标映射→GPU做渲染显示或者VPU编码输出。RK3576的各个处理单元通过内部总线共享内存数据不需要在多个芯片之间搬运这是SoC方案相比独立NPU芯片的最大优势。我实测过一个细节如果把预处理放在CPU上做用NEON指令加速一张1080P图像缩放到640x640大概需要3-5毫秒如果直接用NPU内部的resize单元做可以降到1毫秒以内。RKNN-Toolkit2在转换模型时可以配置是否把预处理算子融合进NPU这个选项对性能影响很大。后面讲模型转换的时候我会详细说。NPU的驱动在Linux下是通过/dev/rknpu设备节点暴露的用户态通过RKNN Runtime库调用。Runtime库负责加载RKNN模型、管理输入输出内存、提交推理任务。整个调用链路比CUDA要简单得多没有复杂的流和事件机制基本上就是初始化→设置输入→运行→获取输出这个流程。2.3 内存带宽对NPU性能的实际影响NPU算力再高如果内存带宽跟不上也是白搭。RK3576支持LPDDR4/LPDDR4X/LPDDR5我用的开发板配的是LPDDR5 8GB带宽大概在40GB/s左右。跑大模型的时候权重数据需要从内存搬到NPU的缓存里如果模型太大缓存放不下就会频繁访问内存这时候带宽就成了瓶颈。举个例子一个YOLOv5s模型INT8量化后大概7MB左右NPU的权重缓存可以完全放下推理时不需要反复读内存速度就很快。但如果是一个ResNet50量化后25MB缓存放不下每次推理都要从内存流式读取权重实际算力利用率可能只有标称值的60%-70%。所以选模型的时候不能只看算力还要看模型大小和缓存的匹配程度。我在实际项目里总结了一个经验RK3576的NPU权重缓存大概在4-8MB这个量级具体数值瑞芯微没有公开是我通过不同大小模型的性能拐点推算的。模型量化后如果超过8MB就要考虑用模型剪枝或者知识蒸馏来压缩否则性能会明显下降。3. 从PyTorch到RKNN模型转换的完整链路3.1 环境搭建与工具链版本选择RKNN-Toolkit2的版本选择是个容易踩坑的地方。瑞芯微的GitHub仓库里同时维护着多个版本每个版本对RK3576的支持程度不一样。我建议直接用1.6.0以上的版本因为RK3576是较新的芯片早期版本可能没有包含对应的NPU驱动和算子支持。安装过程在Ubuntu 22.04上大概是这样# 创建虚拟环境 python3 -m venv rknn_env source rknn_env/bin/activate # 安装依赖 pip install numpy opencv-python torch torchvision onnx # 安装RKNN-Toolkit2 pip install rknn-toolkit2-1.6.0-cp38-cp38-linux_x86_64.whl这里有个细节RKNN-Toolkit2的whl包需要从瑞芯微的官方仓库下载而且对Python版本有要求我用的Python 3.8。如果你用Python 3.10或者3.11可能会遇到依赖冲突。建议直接用conda创建一个干净的Python 3.8环境省得折腾。开发板端需要安装RKNN Runtime库这个在板子的系统镜像里通常已经预装了。如果没有可以从官方仓库下载librknnrt.so和对应的头文件放到/usr/lib和/usr/include目录下。验证是否安装成功可以运行# 在开发板上执行 rknn_server --version如果输出了版本号说明Runtime环境正常。3.2 PyTorch模型导出ONNX的注意事项RKNN-Toolkit2不直接支持PyTorch模型需要先转成ONNX格式。这一步看起来简单但坑不少。最常见的问题是动态维度——PyTorch默认导出ONNX时会保留动态batch维度但RKNN对动态shape的支持有限最好在导出时就固定输入尺寸。import torch import torchvision.models as models # 加载模型 model models.resnet18(pretrainedTrue) model.eval() # 创建示例输入固定batch1输入尺寸224x224 dummy_input torch.randn(1, 3, 224, 224) # 导出ONNX torch.onnx.export( model, dummy_input, resnet18.onnx, input_names[input], output_names[output], opset_version11, # RKNN对opset 11支持最好 do_constant_foldingTrue, dynamic_axesNone # 不要动态维度 )opset_version建议用11这是RKNN-Toolkit2支持最完善的版本。用更高的opset可能会遇到不支持的算子。另外导出后最好用onnxsim做一下简化把冗余的算子合并掉pip install onnx-simplifier python -m onnxsim resnet18.onnx resnet18_sim.onnx简化后的模型不仅转换成功率更高推理速度也会快一些因为NPU不需要处理那些冗余算子。3.3 RKNN模型转换的参数配置与量化策略模型转换是整个过程的核心环节。RKNN-Toolkit2提供了Python API来做转换关键参数包括量化方式、目标平台、优化等级等。from rknn.api import RKNN rknn RKNN(verboseTrue) # 配置模型预处理 rknn.config( mean_values[[123.675, 116.28, 103.53]], # ImageNet均值 std_values[[58.395, 57.12, 57.375]], # ImageNet标准差 target_platformrk3576, quantized_dtypeasymmetric_quantized-8, # INT8非对称量化 optimization_level3, # 最高优化等级 quantized_algorithmnormal # 量化算法 ) # 加载ONNX模型 ret rknn.load_onnx(modelresnet18_sim.onnx) if ret ! 0: print(加载模型失败) exit(ret) # 构建RKNN模型需要提供量化校准数据集 ret rknn.build( do_quantizationTrue, datasetcalibration_dataset.txt # 校准图片列表 ) if ret ! 0: print(构建模型失败) exit(ret) # 导出RKNN模型 ret rknn.export_rknn(resnet18.rknn) if ret ! 0: print(导出模型失败) exit(ret)量化校准数据集是影响精度的关键。我一般从训练集里随机抽100-200张图片覆盖各种场景。校准集太小或者分布太单一量化后的精度损失会很明显。实测下来ResNet18在ImageNet上的INT8量化精度损失大概在0.5%-1%之间YOLOv5s的mAP损失在1%-2%之间这个代价换来的是3-4倍的速度提升非常划算。如果量化后精度不达标可以尝试几种补救措施一是增加校准集数量到500张以上二是改用quantized_algorithmmmse这是一种基于最小均方误差的量化算法精度更好但转换时间更长三是对敏感层保持FP16精度在config里通过hybrid_quantization配置。3.4 开发板端部署与推理代码编写模型转换完成后把.rknn文件拷贝到开发板上就可以用RKNN Runtime做推理了。C接口的性能最好Python接口开发效率高我两种都试过Python接口的额外开销大概在2-3毫秒左右对实时性要求不是极致的话完全够用。import numpy as np import cv2 from rknnlite.api import RKNNLite # 初始化 rknn_lite RKNNLite() ret rknn_lite.load_rknn(resnet18.rknn) ret rknn_lite.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) # 推理 outputs rknn_lite.inference(inputs[img]) # 后处理 pred np.argmax(outputs[0]) print(f预测类别: {pred}) # 释放资源 rknn_lite.release()这里有个性能优化的点init_runtime的时候可以指定core_maskRK3576只有一个NPU核心所以只能选NPU_CORE_0。如果是RK3588可以指定多个核心做并行推理。另外如果要做连续推理不要每次都重新load模型初始化一次然后循环调用inference就行。4. 实测性能数据6TOPS NPU的真实表现4.1 测试环境与基准模型选择测试用的开发板配置是RK35768GB LPDDR5系统是Ubuntu 22.04内核版本5.10。NPU驱动版本是0.9.6RKNN Runtime版本1.6.0。测试时的环境温度在25度左右开发板没有加散热片靠自然散热。我选了四个有代表性的模型来做基准测试ResNet18做图像分类YOLOv5s做目标检测MobileNetV2做轻量级分类还有PP-OCRv3的检测模型做文字检测。这四个模型覆盖了AIoT场景里最常见的几类任务模型大小从3MB到25MB不等能比较全面地反映NPU在不同负载下的表现。测试方法是用Python接口跑100次推理去掉前10次预热取后90次的平均耗时。输入图像统一缩放到模型要求的尺寸预处理时间不计入推理耗时。精度方面分类模型看Top-1准确率检测模型看mAP0.5。4.2 各模型推理耗时与精度对比模型输入尺寸量化精度模型大小推理耗时精度损失ResNet18224x224INT811MB8.2ms-0.7%MobileNetV2224x224INT83.5MB3.1ms-0.5%YOLOv5s640x640INT87.2MB22.5ms-1.8% mAPPP-OCRv3检测960x960INT84.8MB18.3ms-1.2%从数据可以看出几个规律MobileNetV2因为模型小、计算量低推理耗时只有3.1毫秒跑满300FPS没问题适合做实时性要求极高的场景。ResNet18的8.2毫秒对应120FPS左右也相当充裕。YOLOv5s在640x640输入下22.5毫秒大概44FPS对于大多数视频分析场景25FPS或30FPS来说够用了。PP-OCRv3检测模型在960x960高分辨率下18.3毫秒说明NPU处理大尺寸输入时效率依然不错。精度损失方面INT8量化带来的精度下降都在可接受范围内。ResNet18的Top-1准确率从70.2%降到69.5%MobileNetV2从71.8%降到71.3%YOLOv5s的mAP0.5从37.4%降到35.6%。如果对精度要求更严格可以开启混合量化把部分敏感层保持FP16但推理耗时会增加30%-50%。4.3 与CPU/GPU推理的性能差距为了有个直观对比我在同一块板子上用CPU和GPU也跑了同样的模型。CPU是四核A722.2GHzGPU是Mali-G52 MC3。CPU推理用的是ONNX RuntimeGPU推理用的是OpenCL。模型NPU耗时CPU耗时GPU耗时NPU加速比(vs CPU)ResNet188.2ms156ms42ms19xMobileNetV23.1ms48ms15ms15.5xYOLOv5s22.5ms520ms128ms23xPP-OCRv3检测18.3ms380ms95ms20.8x这个差距非常明显。NPU相比CPU有15-23倍的加速相比GPU也有4-7倍的优势。而且NPU在推理时CPU占用率极低大概只有5%-10%CPU可以同时处理其他任务比如视频解码、网络通信、业务逻辑等。GPU推理虽然也比CPU快但GPU同时还要负责显示渲染资源争抢的问题比较突出。还有一个容易被忽略的点功耗。我用功率计测过NPU满载推理时整板功耗大概增加1.5W左右GPU满载增加3WCPU满载增加4W。对于电池供电或者散热受限的AIoT设备来说NPU的能效比优势是决定性的。4.4 多模型并行与流水线推理的实测实际项目里往往不是只跑一个模型。比如一个智能门禁系统需要先做人脸检测再做人脸识别可能还要跑一个活体检测模型。这三个模型如果串行跑总耗时就是三者之和如果能流水线并行总耗时取决于最慢的那个。RK3576只有一个NPU核心严格意义上的并行推理是做不到的但可以通过流水线的方式提高吞吐量。具体做法是把预处理、NPU推理、后处理分到不同线程用队列连接。当模型A在做NPU推理时模型B可以在CPU上做预处理模型C在做后处理。这样虽然单个模型的延迟没变但整体吞吐量能提升40%-60%。我实测了一个人脸检测识别的流水线检测模型15ms识别模型8ms串行跑是23ms一帧流水线跑能到14ms一帧吞吐量提升了64%。这个优化在视频流处理场景里非常实用。5. 踩坑记录从模型转换到部署的五个典型问题5.1 算子不支持导致的转换失败这是最常见的问题。PyTorch模型里的一些算子比如torch.nn.functional.interpolate的某些模式、自定义的激活函数、特殊的padding方式RKNN-Toolkit2可能不支持。转换时会报Unsupported op的错误。我的解决思路是分三步走第一步用rknn.config里的custom_ops参数注册自定义算子但这需要自己写NPU端的实现门槛较高第二步在ONNX层面做算子替换把不支持的算子换成等价的受支持算子组合比如把Hardswish换成ReLU6的变体第三步如果前两步都不行就修改模型结构在训练阶段就避开这些算子。实际操作中我遇到最多的是Hardswish激活函数的问题。MobileNetV3系列大量使用Hardswish而早期版本的RKNN-Toolkit2不支持。解决办法是在导出ONNX之前把Hardswish替换成ReLU6精度损失大概0.3%左右但转换成功率100%。5.2 量化校准集选择不当导致的精度崩塌有一次我转一个工业质检的缺陷检测模型量化后mAP从45%直接掉到12%基本不可用。排查了半天发现是校准集的问题——我随手从训练集里抽了100张图但这些图都是正常样本缺陷样本一张没有。量化校准的时候NPU根据正常样本的激活值分布来确定量化参数遇到缺陷样本时激活值超出量化范围精度就崩了。后来我改成从训练集里按类别分层抽样正常样本和各类缺陷样本按比例抽取总共200张量化后的mAP恢复到43.5%只损失了1.5个百分点。这个教训是校准集必须覆盖模型在实际场景中可能遇到的所有数据分布不能偷懒。5.3 输入尺寸与NPU内部对齐要求的冲突RKNN对输入尺寸有一些隐性的对齐要求。比如某些卷积算子要求输入通道数是4的倍数特征图宽高是8的倍数。如果输入尺寸不满足这些条件NPU会自动做padding但padding后的计算量会增加性能下降。我遇到过一个案例一个自定义的检测模型输入是300x300转换后推理耗时比预期高了30%。后来把输入改成304x3048的倍数耗时反而降下来了。所以建议在设计模型输入尺寸时尽量选择32的倍数这样能避开大部分对齐问题。5.4 开发板端内存不足导致的推理失败RK3576开发板虽然有8GB内存但Linux系统本身会占用一部分加上视频解码、显示等任务的消耗留给NPU推理的连续内存可能不够。特别是跑大模型的时候如果模型文件超过100MB加载时可能会报malloc failed。解决办法有两个一是用rknn_lite.init_runtime的时候指定mem_typedma让Runtime从DMA区域分配内存这个区域通常比较充裕二是把模型拆成多个小模型分时加载用完就释放。我在一个项目里把一个大模型拆成三个子模型每个子模型单独加载推理内存峰值降低了60%。5.5 多线程调用NPU的线程安全问题RKNN Runtime不是线程安全的。如果多个线程同时调用同一个RKNN上下文做推理会出现结果错乱甚至程序崩溃。我一开始没注意这个问题在一个多路视频分析的项目里四个线程共用一个RKNN实例跑了几分钟就崩了。正确的做法是每个线程创建独立的RKNN实例或者用一个专门的推理线程加任务队列来串行化所有NPU调用。前者内存占用高一些但延迟低后者内存省但高并发时队列会积压。我一般用后者配合一个优先级队列保证实时性要求高的任务优先处理。6. 把RK3576用在实际AIoT项目里的经验6.1 智能门禁场景的模型选型与优化智能门禁是我用RK3576做得最多的场景。典型需求是人脸检测人脸识别活体检测三个模型串联要求端到端延迟低于200毫秒支持1080P视频流实时处理。模型选型上人脸检测我用的是RetinaFace的轻量版输入320x320INT8量化后2.3MBNPU推理耗时6.8毫秒。人脸识别用的是MobileFaceNet输入112x112量化后1.8MB推理耗时2.1毫秒。活体检测用的是自己训练的一个二分类模型输入128x128推理耗时1.5毫秒。三个模型加起来NPU耗时10.4毫秒加上预处理和后处理端到端大概35毫秒远低于200毫秒的要求。这里有个优化技巧人脸检测不需要每帧都跑。我用了一个简单的帧差法只有画面有明显变化时才触发检测静止画面下每5帧检测一次。这样NPU负载降低了60%以上整体功耗也下来了。6.2 工业质检场景的精度与速度平衡工业质检对精度的要求比消费级场景高得多。我做过一个PCB缺陷检测的项目要求漏检率低于0.1%误检率低于1%。这种场景下INT8量化的精度损失就不能忽视了。我的做法是采用混合量化策略把模型的主干网络保持INT8检测头部分保持FP16。这样推理耗时从纯INT8的18毫秒增加到26毫秒但mAP从91.2%恢复到94.7%满足了客户要求。RKNN-Toolkit2的混合量化配置是在config里指定hybrid_quantization参数把需要保持高精度的层列出来。另外工业质检场景通常可以用高分辨率相机输入尺寸可以设大一些。我用的是1280x1280输入虽然单帧推理耗时增加到45毫秒但小缺陷的检出率明显提升。配合产线节拍通常2-3秒一个产品45毫秒完全在预算内。6.3 车载DVR场景的多路视频处理车载DVR通常需要同时处理4路1080P视频做ADAS预警车道偏离、前车碰撞和DMS驾驶员监控。这对RK3576的多媒体能力和NPU调度都是考验。我的方案是用VPU硬解码4路视频流解码后的图像通过DMA直接送到NPU做推理避免CPU拷贝。ADAS模型用的是轻量化的车道线检测和车辆检测DMS模型用的是人脸关键点检测疲劳分类。四个模型分时复用NPU用优先级队列调度ADAS预警任务优先级最高。实测下来4路视频同时处理时NPU占用率大概70%CPU占用率40%整板功耗5.2W。在车载12V供电环境下这个功耗完全可以接受。需要注意的是散热车载环境夏天车内温度可能到60度以上必须加散热片否则NPU会降频。6.4 边缘计算盒子的模型热更新方案边缘计算盒子部署在客户现场后模型可能需要远程更新。RK3576的NPU支持模型热更新但实现起来有几个细节要注意。首先更新模型时不能直接覆盖正在使用的.rknn文件要先下载到临时目录校验MD5后再原子替换。其次替换后需要重新初始化RKNN上下文这个过程大概需要200-500毫秒期间推理服务会中断。为了不影响业务我用了双缓冲机制维护两个RKNN实例一个在跑当前模型另一个加载新模型加载完成后切换指针旧实例延迟释放。这个方案在实际项目里跑了半年多远程更新了十几次模型没有出过问题。关键是要做好版本管理和回滚机制新模型上线后如果精度不达标能快速切回旧版本。6.5 功耗与散热的实测数据最后说一下功耗和散热这是很多人在选型时容易忽略的。我用功率计测了RK3576在不同负载下的整板功耗工作状态整板功耗NPU温度CPU温度待机1.8W42度45度单模型推理3.2W58度52度多模型流水线4.5W72度58度满载4路视频推理5.8W85度65度不加散热片的情况下满载跑10分钟NPU温度就到85度了这时候会触发降频推理耗时增加20%左右。加一个20x20mm的铝散热片温度能控制在70度以内不会降频。如果是密闭外壳建议加一个小风扇或者用导热硅胶把热量导到金属外壳上。功耗方面5.8W的满载功耗对于PoE供电最大15.4W或者12V/1A供电来说都很宽裕。如果是电池供电的场景可以通过动态调频来省电——检测到没有推理任务时把NPU频率降到最低有任务时再升频这个在RKNN Runtime里可以通过rknn_set_freq接口实现。整体用下来RK3576在AIoT项目里的表现是超出我预期的。6TOPS的NPU算力足够覆盖大多数端侧视觉任务工具链虽然有些小坑但整体成熟度不错功耗和散热在合理设计下也不是问题。如果你正在选型AIoT主控方案RK3576值得放进候选清单里认真评估。
返回列表