资讯动态

RK3566图像分类模型部署实测:精度与性能对比

发布时间:2026/10/20 16:41:41 来源:尧图企业网站定制
做边缘端图像分类部署瑞芯微RK3566是个绕不开的芯片。它带了一个0.8TOPS算力的NPU定位偏入门级边缘AI最近我把几类主流的图像分类模型逐个在RK3566上部署了一遍从ResNet系列到MobileNet系列再到EfficientNet-Lite完整走了一遍“训练权重→ONNX→RKNN→板端推理”的流程记录了各模型在精度、单帧耗时、内存占用上的真实差异。这篇文章把整个部署实验的过程和经验整理出来包括选型思路、转换工具链的坑、量化掉点的排查方法给正在做模型部署选型的开发者一个可以直接参考的对照记录。1. 为什么选RK3566做图像分类模型部署1.1 芯片资源的真实上限RK3566是一颗四核Cortex-A55芯片最高主频1.8GHz左右搭配Mali-G52 GPU最关键的是带了一个0.8TOPS算力INT8的NPU。说实话这个算力放在手机SoC面前不够看但在几百元的开发板方案里已经足够跑轻量级图像分类模型。我做实验前先梳理了这颗NPU的能力边界不搞清楚算力上限直接拿大模型硬上后面一定浪费时间。0.8TOPS的INT8算力对应的是每秒大概0.8万亿次定点运算。以一次224x224分辨率的MobileNetV2推理为例它大约需要3亿次乘加运算理论上折算下来在NPU上最快可以做到几个毫秒但实际受内存带宽、算子实现效率、驱动开销影响最终跑出来的单帧耗时会在二三十毫秒的量级。我把这个理论值列出来主要是想说明一个判断依据如果模型单帧计算量超过5亿次乘加在RK3566上就很难达到实时效果所以在选模型时我自己划了一条线优先考虑一亿到三亿次乘加区间的轻量分类网络。内存方面RK3566通常搭配LPDDR4/LPDDR3带宽相对有限做推理时如果模型权重文件和中间特征图太大内存搬运本身就会成为瓶颈。我实测一个现象ResNet50跑出来的耗时比ResNet18高的倍数远不止参数量的倍数原因就在于ResNet50各层通道数大中间特征图体积大NPU算得动但内外存搬来搬去的时间下不来。1.2 和带GPU的嵌入式平台对比很多人会问RK3566和带GPU的板卡比到底差在哪。简单说GPU平台擅长跑FP32大batch运算但功耗高、价格贵而RK3566这类NPU方案强在低功耗和低成本适合固定输入、单帧推理、持续运行的场景。拿图像分类这个任务来说单图输入、batch永远是1这种场景恰恰是NPU最擅长的CPU或GPU反而要花额外精力处理调度和散热。我做选型时的判断很简单项目如果只有分类需求RK3566够用如果后面要做目标检测加实例分割且对帧率有硬性要求那就得考虑更高算力的芯片比如RK3588或者带更强GPU的方案。分类模型相对规整卷积、激活、池化、全连接这些算子NPU支持度高很少出现算子不支持导致模型跑不出来的情况这也是我选分类模型做部署实验的原因——先把工具链吃透再去做检测类模型会顺很多。2. 模型选型与数据预处理的关键考量2.1 挑选有代表性的主流模型图像分类模型这个池子非常大逐一出精度和性能数据意义不大我挑了六个结构差异明显的模型作为实验对象ResNet18、ResNet50、MobileNetV1、MobileNetV2、MobileNetV3-Large、ShuffleNetV2外加EfficientNet-Lite0作为NAS结构参考。选这些模型的逻辑很简单ResNet系列代表传统堆叠结构MobileNet系列代表深度可分离卷积路线ShuffleNetV2代表通道混洗和均衡设计EfficientNet则代表带复合缩放的NAS结构。覆盖这些结构差异才能看出RK3566的NPU对哪种算子组合更友好。每个模型的参数量和计算量差异非常大。ResNet18算轻量但也有1.8亿次乘加ResNet50直接跳到3.8亿次MobileNetV2只有3亿次MobileNetV3-Large更少一点2.2亿次左右ShuffleNetV2大约1.5亿次。从算术量上看ShuffleNetV2最占优势但实际部署时效果还要看NPU硬件的算子优化情况。我后面跑出来发现理论算力较小的模型在NPU上不一定更快因为深度可分离卷积在某些NPU上实现效率偏低所以实测数据永远比理论推算更靠谱。2.2 输入尺寸与归一化参数不必盲从ImageNet分类模型在ImageNet上训练时通常默认输入224x224或240x240归一化用mean[0.485,0.456,0.406]、std[0.229,0.224,0.225]。这个参数在PC上用PyTorch直接推理没问题但转到RKNN时需要把归一化逻辑转换为mean_values和std_values的格式这里的细节相当容易出错。RKNN配置里mean_values和std_values填的是“像素域”的数值不是训练时的百分比数值。训练代码里如果是先用除以255再减mean除std那转RKNN时应该填mean255和std255。以ImageNet标准为例mean_values应该是[123.675, 116.28, 103.53]std_values是[58.395, 57.12, 57.375]。如果直接把0.485填进去模型出来的结果基本是乱猜。我在这个坑上卡了半个下午后来用单张图和RKNN模拟器输出逐元素对比才定位到问题。输入尺寸方面不建议为了追求精度把分辨率提到256或更高RK3566的NPU算力就那么多分辨率一上去耗时线性上涨。我实测224到240耗时大概增加12%到18%所以除非你的数据集本身对细节特别敏感否则保持224是一个均衡选择。另外RKNN转换时input_size_list里的NCHW排布要与导出ONNX一致我统一用[1,3,224,224]后面板端输入就直接用一个224x224的BGR图像避免在板端再做额外resize。2.3 训练技巧对量化精度的影响这个点容易被忽略。做部署时我们通常直接拿训练好的模型做INT8量化但训练时用的trick会直接影响量化后的精度。比如label smoothing它会让模型输出的置信度更平滑不会出现极端接近0或1的预测分数这对量化是有好处的因为量化时要把浮点数值域映射到0到255的整数域输出分布太平滑反而不容易压缩出有区分度的量化表。反而是有些模型用了很激进的dropout或focal loss导致特征图数值分布不均匀量化后掉点严重。我这次实验里对比了一个小细节训练时是否在BN层之后加权重衰减以及是否在最后一个全连接层做了bias修正这些都会影响输出logits的数值范围。如果logits范围超过[-4,4]INT8量化后精度明显变差。实践做法是在训练时对最后一层做coupled到softmax的温度缩放或者导出模型时干脆把全连接层后面的缩放操作固化进去。这些听起来对部署无关但实际上决定了你后面量化调优要花多少时间。3. 从ONNX到RKNN的完整转换流程3.1 工具链版本与运行环境瑞芯微的部署工具链是RKNN-Toolkit2对应板端运行时是RKNNLite。我在实验里用的版本是rknn-toolkit2 1.6.0运行在x86 Ubuntu环境里做模型转换转换完的rknn文件拷贝到RK3566板端加载推理。有一点必须提醒rknn-toolkit2的转换环境建议用Python 3.8或3.10先装好onnx、onnxruntime、numpy这些依赖再pip安装rknn-toolkit2避免依赖冲突。板端跑推理不在PC上跑需要另外安装rknnlite包并把librknnrt.so动态库推到板子上放在/usr/lib或程序同目录。整个结构是这样的PC端负责训练模型、导出ONNX、转RKNNRK3566板端只负责加载rknn文件、执行推理、做后处理。理解这点后就不会把PC端的rknn-toolkit2和板端的rknnlite搞混。3.2 从PyTorch导出ONNX的注意事项模型导出看着简单实际上有几个决定后续转换成败的细节。我用的是PyTorch导出时用torch.onnx.export关键参数如下import torch import torchvision.models as models model models.mobilenet_v2(pretrainedTrue) model.eval() dummy_input torch.randn(1, 3, 224, 224) torch.onnx.export( model, dummy_input, mobilenet_v2.onnx, opset_version11, input_names[input], output_names[output], do_constant_foldingTrue, dynamic_axesNone )opset_version这里我用了11不建议盲目用更高的17或18因为高opset可能引入新的算子表示RKNN工具链不一定完全支持。dynamic_axes设为None固定batch为1分类模型本来就是单张推理动态输入只会让转换和量化复杂化。导出之后先别急着转RKNN用onnxruntime做一次推理验证对比onnx输出和PyTorch原模型的输出。如果差异大于1e-4说明导出的图结构有问题先排查再往下走。尤其是BatchNorm被folding进卷积后输出会有细微变化需要把onnx的预处理逻辑和PyTorch保持一致。3.3 模型转换与INT8量化的核心配置RKNN转换核心代码不长但参数含义要搞清楚from rknn.api import RKNN rknn RKNN() rknn.config( target_platformrk3566, mean_values[[123.675, 116.28, 103.53]], std_values[[58.395, 57.12, 57.375]], reorder_channel0 1 2 ) ret rknn.load_onnx( modelmobilenet_v2.onnx, input_size_list[[1, 3, 224, 224]] ) dataset [data/calib_001.jpg, data/calib_002.jpg, ...] ret rknn.build( do_quantizationTrue, datasetdataset.txt ) rknn.export_rknn(mobilenet_v2.rknn)target_platformrk3566必须显式指定否则工具链可能按默认的rk3588或rv1126优化策略转换生成的rknn文件在板端加载时会出现精度下降或算子匹配问题。do_quantizationTrue是INT8量化的开关。dataset.txt里写的是校准图片的路径列表每行一张图我用的是200张从验证集抽出来的图覆盖了各个类别。校准集不是越多越好关键在分布要接近真实数据。数量太少会导致量化参数计算不准数量太多转换时间成倍增加实测下来200张足够。校准图片在送入RKNN做校准前会先经过rknn.config里设置的mean/std做归一化所以dataset里的图片直接放原始jpg就行不需要提前做减均值处理——这个坑我见过太多人踩提前减了一遍均值结果量化后的模型预测结果离谱。3.4 模拟器验证与板端推理转换完成第一步先在PC端用RKNN模拟器跑一遍用它对比onnxruntime的输出看精度是否在预期范围。但模拟器结果只能作为参考板端NPU实际运算时量化算子的实现和模拟器可能有细微差异最终精度要以板端运行为准。板端推理代码要短小直接from rknnlite.api import RKNNLite rknn_lite RKNNLite() rknn_lite.load_rknn(mobilenet_v2.rknn) rknn_lite.init_runtime() img preprocess(load_image(test.jpg)) # BGR, uint8, 224x224 outputs rknn_lite.inference(inputs[img]) print(outputs[0].shape)这里有个容易出错的小细节板端输入的numpy数组必须是连续内存的如果图片经过了裁剪、翻转、拼接操作要先调用np.ascontiguousarray(img)再做inference否则NPU读到的数据是乱序的推理结果会完全错误。另外输入图像的通道顺序默认是BGR还是RGB取决于rknn.config里reorder_channel的设置。我训练时用的是RGB但RKNN工具链默认对某些模型执行BGR通道重排所以需要用0 1 2保持RGB顺序或者根据实际训练预处理来设置。4. 部署后的精度与性能对比4.1 六个模型的精度表现模型转换和量化做完后我在自己的测试集上跑了完整的分类精度验证。这里的测试集和ImageNet分布接近不过数量不算大各模型的绝对值只能做相对参考。量化前后对比表格如下模型输入尺寸量化方式Top-1精度原始FP32Top-1精度INT8量化精度差值ResNet18224x224INT8 per-layer69.8%68.4%-1.4%ResNet50224x224INT8 per-layer76.1%73.9%-2.2%MobileNetV1224x224INT8 per-layer71.0%69.6%-1.4%MobileNetV2224x224INT8 per-layer71.6%70.1%-1.5%MobileNetV3-Large224x224INT8 per-layer75.2%73.2%-2.0%ShuffleNetV2224x224INT8 per-layer69.4%67.8%-1.6%EfficientNet-Lite0224x224INT8 per-layer74.9%72.6%-2.3%从精度数据能看出一个规律结构越复杂、通道数越多的模型量化后掉点越明显。ResNet50掉了2.2个百分点EfficientNet-Lite0掉了2.3个百分点而MobileNetV1只掉了1.4个百分点。深层原因在于复杂模型的特征图数值对长尾分布更敏感INT8量化把动态范围压到256个离散值那些偶发的大数值激活值会拉大量化步长导致常见的中等数值精度下降。MobileNetV3-Large的原始精度很高但量化后掉点也大我分析是它用了h-swish激活和SE模块这些算子的数值范围比ReLU更宽量化时更难校准确。如果你的项目对精度要求比较苛刻建议对MobileNetV3-Large做逐通道量化或者搭配混合量化把SE模块里的层保持FP16。4.2 单帧推理耗时与内存占用耗时测试在RK3566评估板上进行NPU默认频率单线程预处理和后处理不算在推理耗时里。得到的相对数据如下模型单帧推理耗时运行内存占用估算权重大小INT8ResNet1858ms约45MB11.2MBResNet50196ms约140MB25.6MBMobileNetV141ms约32MB4.2MBMobileNetV229ms约30MB3.4MBMobileNetV3-Large24ms约36MB5.1MBShuffleNetV240ms约28MB3.2MBEfficientNet-Lite026ms约38MB5.6MBResNet50在RK3566上跑到196ms这个数字在实时视频流场景里基本不可用但在单张图片离线分类场景下还能接受。MobileNetV3-Large是速度和精度平衡最好的选择24ms的单帧耗时加上73.2%的Top-1精度非常适合摄像头抓拍后逐帧分析。ShuffleNetV2理论上算力最小但实测耗时40ms比MobileNetV2还要慢。原因是RK3566的NPU对通道混洗算子的实现效率不高这种算子需要频繁做数据重排NPU内部的访存模式被打乱效率反而不如规整的深度可分离卷积。这说明不同硬件对算子支持偏好差异极大理论计算量只能作为参考不能替代实测。内存占用数据来自rknn运行时申请的权重和中间缓存总和实际数值还与板端固件、驱动版本有关。我在实验中发现如果把NPU频率调低内存占用会略微下降但耗时明显增加所以不建议靠降频省内存而是应该从模型层面控制权重和中间特征图大小。4.3 精度与速度的权衡曲线把精度和耗时放到一张图上看最直观的结论是MobileNetV3-Large落在“高精度、低耗时”的区间EfficientNet-Lite0紧随其后MobileNetV2则是在中低精度区间保证低耗时。ResNet18就比较尴尬精度不高速度也不快唯一的优势是结构经典、训练资料多、调参容易。ResNet50只有当项目里必须复用已有训练权重或需要和其他检测模型共用backbone时才有意义。如果你的应用场景是固定输入尺寸的单分类任务我建议优先试MobileNetV3-Large其次是EfficientNet-Lite0。这两个模型在RK3566上的推理耗时都低于30ms配合板端CPU做前后处理整体一帧也能控制在40ms以内基本满足实时响应需求。如果需要处理视频流且对精度要求不那么敏感MobileNetV2是更稳的兜底选择因为它在不同数据集上的泛化表现比较稳定量化掉点也小。5. 踩坑实录模型转换与推理环节的常见问题5.1 量化掉点严重时的排查顺序遇到INT8量化后精度掉得特别多的情况不要第一时间怀疑NPU有问题按照顺序排查基本能定位。第一步检查校准集和真实数据的分布差异校准集图片如果全是同类目标量化参数就会被某一类数据带偏。我用ImageNet验证集抽了200张均衡覆盖不同类别的图片做校准掉点立刻从5%以上回落到2%以内。第二步检查模型中是否有大数值动态范围的中间层比如某些模型的SE模块输出范围能到20以上而普通卷积层输出范围只有2到3这种情况下全局量化会把步长调大精度损失集中出现在敏感层。解决办法是在rknn.build中加入量化层白名单把这些特定层设为不量化保留浮点运算代价是推理耗时略有上升。第三步检查最后输出层。很多模型在导出ONNX时自带SoftmaxSoftmax输出值域是0到1量化步长非常小看起来没毛病但Softmax前的logits值域可能是负10到正10如果量化前没有把logits范围适配好量化误差会在最后一步集中爆发。我通常导出模型时就把Softmax去掉把argmax逻辑放到板端后处理直接对logits操作。5.2 算子不支持的替代写法分类模型大多数算子NPU都支持但偶尔会遇到某些版本导出的Resize或Gather算子不能被RKNN转换。Resize常见于输入图从任意尺寸缩放为224时如果你把resize放进了模型里ONNX导出时opset_version不同插值参数表示方式不同。opset11时Resize的坐标变换模式是半像素对齐opset16则变了转换时可能会导致输出尺寸差1像素。我的做法是尽量不在模型里带resize层而是在板端用CPU或RGA硬件先缩放到网络输入尺寸模型只接受固定224x224输入。Gather算子常见于一些从TensorFlow迁移过来的模型做permute操作ONNX转换后会产生GatherTranspose组合。RKNN-Toolkit2部分版本对低维Gather支持不全我的绕行方案是在PyTorch里用view、permute、reshape重写这段操作尽量让导出的图结构以Conv和Pool为主。5.3 板端预处理导致性能瓶颈只盯着NPU耗时是新手常犯的错误。RK3566板端CPU算力并不强如果每帧图像都要在CPU上做BGR转换、减均值、标准化、resize到224这些预处理时间可能比NPU推理时间还长。我实测过一组数据用纯Python加numpy对一张1080p图像做resize加归一化耗时能到35ms以上加上NPU的24ms总耗时直接翻倍。优化思路有三个方向。第一固定输入尺寸让摄像头或上游直接输出接近224x224的图像减少resize开销。第二如果用OpenCV读取摄像头帧先用cv2.resize把图像缩到256x256再居中裁剪到224x224这样比直接裁完再resize更省时间。第三把resize和减均值操作放到NPU能执行的层里前提是RKNN工具链支持把预处理层折叠进模型但分类模型里通常没这个必要CPU优化就够了。我最终采用的方案是先把1080p缩放到256再做中心裁剪到224预处理总耗时能压到12ms左右。5.4 模拟器与板端结果不一致同一个模型在PC端模拟器跑出的推理结果和板端NPU跑出的结果有时候会在个别类别上出现不一致。我一开始以为模型转换出了问题反复转换了很多次后来才发现这是量化后不同平台对数值舍入策略不同导致的。模拟器用的是x86指令集模拟定点运算板端NPU有自己的一套乘加累加逻辑某些层累加顺序不同舍入误差会被放大。这个现象在INT8量化模型上基本无法完全消除只能确保两者趋势一致。真正验证部署是否正确的标准是板端输出top-5和模拟器输出top-5高度重合并且top-1分数差距在可接受范围内。如果在某些输入图上板端top-1和模拟器完全相反优先检查输入图像是否在预处理阶段被被压成了错误的通道顺序或缩放系数这类问题造成的错误往往是全局性的不会只影响个别图片。5.5 后处理细节对终端体验的影响最后想提醒一个容易被忽略的点分类模型的后处理看着简单但对输出分数做温度缩放会对实际使用体验产生较大影响。有些模型的logits分布比较平最高分和次高分只差零点几如果直接取argmax很容易出现连续帧分类结果跳变。我的做法是对logits除以一个温度常数T比如T0.1让分数拉开差距再取top-1。这个操作带来几乎为零的额外耗时但会让整个系统输出稳定很多。6. 我实测之后的选型建议如果让我现在直接给结论在RK3566上做图像分类项目默认首选MobileNetV3-Large输入224x224INT8量化精度和速度的平衡最理想。对精度要求高且不追求实时的场景用ResNet50量化版但要做好单帧推理接近200ms的心理准备。对算力资源比较紧张、需要同时跑多个模型的场景用MobileNetV2它的量化稳定性和较小权重是最稳妥的选择。做这个实验给我最大的感受是模型部署不能只看理论计算量和参数量同一个模型在不同芯片上的表现差异非常大。RK3566的NPU对规整的卷积结构优化很好但对通道混洗、动态resize这类操作支持一般所以最后选型一定要做完整的小规模实测拿自己的模型、自己的数据、自己的板子说话。踩过这些坑之后我再做类似项目就会先跑一个最小闭环单模型单图转换、板端推理、精度对比确定这条路走得通再扩展数据集和场景。这也算是我这次实验下来最值得分享的经验。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑