RK3588 ArmNN CPU/GPU ResNet50 FP32/FP16/INT8 推理测试

news2025/5/21 6:06:17

RK3588 ArmNN CPU/GPU ResNet50 FP32/FP16/INT8 推理测试

      • **背景与目标**
    • 一.性能数据【INT8模型在CPU上推理的结果已经不对,暂未分析原因】
    • 二.操作步骤
      • 2.1 在`x86-Linux`上生成`onnx`模型,以及`tflite`量化模型(避免在RK3588上安装过多依赖)
        • 2.1.1 创建容器
        • 2.1.2 安装依赖
        • 2.1.3 下载推理图片
        • 2.1.4 生成`onnx`模型
        • 2.1.5 `onnx`转`tensorflow`,验证结果是否正确
        • 2.1.6 `tensorflow saved_model` 转`tflite`,验证结果是否正确
      • 2.2 `RK3588`上运行`ArmNN`推理测试
        • 2.2.1 下载`ArmNN SDK`,设置环境变量
        • 2.2.2 创建`MSE`计算脚本
        • 2.2.3 测试`CPU-FP32`的`MSE`
        • 2.2.4 测试`GPU-FP32`的`MSE`
        • 2.2.4 测试`CPU-FP16`的`MSE`
        • 2.2.5 测试`GPU-FP16`的`MSE`
        • 2.2.6 测试`CPU-INT8`的`MSE`
        • 2.2.7 测试`GPU-INT8`的`MSE`
        • 2.2.8 性能测试

背景与目标

本文在RK3588芯片上完成了以下任务:

  1. 将PyTorch的ResNet-50模型转换为ONNX格式
  2. 将ONNX模型转换为TFLite(FP16、INT8)
  3. 在RK3588平台使用ArmNN 测试CPU和GPU上,不同精度模型的推理性能与精度损失
  4. 通过MSE(均方误差)和执行时间两个指标评估各模型表现

一.性能数据【INT8模型在CPU上推理的结果已经不对,暂未分析原因】

  • 模型:resnet50 输入:[1,3.224,224 float32] 输出:[1,1000 float32]
精度类型CPU推理时间(ms)GPU推理时间(ms)结果正确性(MSE)
ONNX FP32175.3971.74MSE:0.00000
TFLite FP16117.1066.84MSE:0.00000
TFLite INT846.88 MSE:4.3317627.59 MSE:4.28963CPU上推理的MSE: 4.276822

二.操作步骤

2.1 在x86-Linux上生成onnx模型,以及tflite量化模型(避免在RK3588上安装过多依赖)

2.1.1 创建容器
docker run -it --privileged --net=host \
    -v $PWD:/home -w /home --rm  nvcr.io/nvidia/pytorch:24.03-py3 /bin/bash
2.1.2 安装依赖
pip install tensorflow==2.15 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install tensorflow_probability==0.22 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install onnx onnx-tf  -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install onnx-simplifier -i https://pypi.tuna.tsinghua.edu.cn/simple
2.1.3 下载推理图片
wget https://raw.githubusercontent.com/hi20240217/csdn_images/refs/heads/main/YellowLabradorLooking_new.jpg
2.1.4 生成onnx模型
cat> resnet50.py <<-'EOF'
import requests
from PIL import Image
from io import BytesIO
import torchvision.transforms as transforms
import torch
import numpy as np
import torchvision.models as models

# 读取图片
image = Image.open("YellowLabradorLooking_new.jpg")

# 定义预处理流程
preprocess = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])

# 应用预处理
img_t = preprocess(image)
input_tensor = torch.unsqueeze(img_t, 0).float()

input_np=input_tensor.numpy()

# 保存预处理好的输入,用于后面量化和精度对比
np.savetxt('resnet50_input.txt',input_np.reshape(-1), delimiter=' ', fmt='%.6f')
np.save('resnet50_input.npy',input_np)

# 加载预训练的ResNet50模型
model = models.resnet50(pretrained=True).float()
model.eval()  # 将模型设为评估模式

# 执行前向推理
with torch.no_grad():
    output = model(input_tensor).numpy()

# 保存推理的结果,用于后面对比精度
np.savetxt('resnet50_output.txt',output.reshape(-1), delimiter=' ', fmt='%.6f')

# 获取预测类别
predicted = np.argmax(output)
print("Index:",predicted)

input_names = ["input"]
output_names = ["output"]
torch.onnx.export(model, input_tensor, "resnet50.onnx", verbose=False, input_names=input_names, output_names=output_names)
EOF
python3 resnet50.py
onnxsim resnet50.onnx resnet50_opt.onnx
2.1.5 onnxtensorflow,验证结果是否正确
cat> onnx2tf.py<<-'EOF'  
import onnx
from onnx_tf.backend import prepare
onnx_model = onnx.load("resnet50_opt.onnx")   # 加载ONNX模型
tf_rep = prepare(onnx_model)                  # 转换为TensorFlow表示
tf_rep.export_graph("saved_model")            # 导出为SavedModel格式

import tensorflow as tf
import numpy as np
tf_model = tf.saved_model.load("saved_model")
tf_model = tf_model.signatures["serving_default"]  # 获取推理函数

# 验证结果是否符合预期
input_data = np.load('resnet50_input.npy')
tf_input = tf.constant(input_data)
tf_output = tf_model(tf_input)
print(tf_output.keys())
tf_output = tf_output["output"].numpy()
predicted = np.argmax(tf_output)
print("Index:",predicted)
EOF
rm saved_model -rf
python3 onnx2tf.py
2.1.6 tensorflow saved_modeltflite,验证结果是否正确
cat> tf2lite.py<<-'EOF'  
import numpy as np
import sys
import tensorflow as tf

def representative_dataset():
    for _ in range(1):
        data = tf.convert_to_tensor(np.load('resnet50_input.npy').astype(np.float32))
        yield [data]

converter = tf.lite.TFLiteConverter.from_saved_model("saved_model")
converter.optimizations = [tf.lite.Optimize.DEFAULT]          # 启用默认优化

quant_type=sys.argv[1]
output_path=sys.argv[2]

if quant_type=="int8":
    converter.representative_dataset = representative_dataset                    # 设置代表数据集
    converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]  # 全整数支持
else:
    converter.target_spec.supported_types = [tf.float16]

tflite_quant_model = converter.convert()
with open(output_path, "wb") as f:
    f.write(tflite_quant_model)

# 推理验证部分
def validate_inference():
    # 加载原始模型
    original_model = tf.saved_model.load("saved_model")
    infer = original_model.signatures["serving_default"]
    
    # 加载量化模型
    interpreter = tf.lite.Interpreter(model_content=tflite_quant_model)
    interpreter.allocate_tensors()
    
    # 获取模型IO详细信息
    input_details = interpreter.get_input_details()
    output_details = interpreter.get_output_details()
    
    # 加载测试数据
    test_data = np.load('resnet50_input.npy')
    
    # 原始模型推理
    original_input = tf.constant(test_data, dtype=tf.float32)
    original_output = infer(original_input)
    original_output = list(original_output.values())[0].numpy()
    
    # 量化模型输入预处理
    input_shape = input_details[0]['shape']
    input_dtype = input_details[0]['dtype']
    
    # 处理量化参数(如果存在)
    if input_details[0]['quantization'] != (0, 0):
        scale, zero_point = input_details[0]['quantization']
        quantized_input = np.round(test_data / scale + zero_point)
        quantized_input = np.clip(quantized_input, 
                                 np.iinfo(input_dtype).min,
                                 np.iinfo(input_dtype).max).astype(input_dtype)
    else:
        quantized_input = test_data.astype(input_dtype)
    
    # 设置输入并推理
    interpreter.set_tensor(input_details[0]['index'], quantized_input)
    interpreter.invoke()
    
    # 获取输出并反量化(如果需要)
    quant_output = interpreter.get_tensor(output_details[0]['index'])
    if output_details[0]['quantization'] != (0, 0):
        scale, zero_point = output_details[0]['quantization']
        quant_output = (quant_output.astype(np.float32) - zero_point) * scale
    
    # 计算误差指标
    print("Index:",np.argmax(original_output),np.argmax(quant_output))
    mse = np.mean((original_output - quant_output) ** 2)
    mae = np.mean(np.abs(original_output - quant_output))
    
    print("\n验证结果:")
    print(f"原始模型输出均值:{np.mean(original_output):.4f}")
    print(f"量化模型输出均值:{np.mean(quant_output):.4f}")
    print(f"均方误差 (MSE): {mse:.6f}")
    print(f"平均绝对误差 (MAE): {mae:.6f}")
    
# 执行验证
validate_inference()
EOF
python3 tf2lite.py int8 resnet50_int8.tflite
python3 tf2lite.py fp18 resnet50_fp16.tflite

输出

# int8 (已经存在误差)
Index: 208 904
验证结果:
原始模型输出均值:0.0000
量化模型输出均值:0.0006
均方误差 (MSE): 4.276822
平均绝对误差 (MAE): 1.536772

# fp16
Index: 208 208
验证结果:
原始模型输出均值:0.0000
量化模型输出均值:0.0000
均方误差 (MSE): 0.000003
平均绝对误差 (MAE): 0.001248

2.2 RK3588上运行ArmNN推理测试

2.2.1 下载ArmNN SDK,设置环境变量
wget https://github.com/ARM-software/armnn/releases/download/v25.02/ArmNN-linux-aarch64.tar.gz
tar -xf ArmNN-linux-aarch64.tar.gz
export LD_LIBRARY_PATH=$PWD:$PWD/delegate:$LD_LIBRARY_PATH
2.2.2 创建MSE计算脚本
cat> compute_mse.py<<-'EOF'  
import numpy as np
import sys
l = np.loadtxt(sys.argv[1], delimiter=' ').reshape(-1)
r = np.loadtxt(sys.argv[2], delimiter=' ').reshape(-1)
print(l.shape,r.shape)
print(l[:8])
print(r[:8])
mse=np.mean((l - r) ** 2)
print(f'MSE:{mse:.5f}')
EOF
2.2.3 测试CPU-FP32MSE
./ExecuteNetwork -c CpuAcc -m resnet50_opt.onnx -I 1 -d resnet50_input.txt -w temp.txt
cat temp.txt | awk -F, '{print $2}' | awk -F: '{print $2}' | sed 's/ /\n/g' > resnet50_fp32_cpu_pred.txt
python3 compute_mse.py resnet50_fp32_cpu_pred.txt resnet50_output.txt

输出

(1000,) (1000,)
[-0.964521  1.21882  -2.75427  -1.55273  -0.906159 -1.08674  -3.01336  0.647321]
[-0.964521  1.218818 -2.754273 -1.552727 -0.906162 -1.086741 -3.013363 0.647324]
MSE:0.00000
2.2.4 测试GPU-FP32MSE
./ExecuteNetwork -c GpuAcc -m resnet50_opt.onnx -I 1 -d resnet50_input.txt -w temp.txt
cat temp.txt | awk -F, '{print $2}' | awk -F: '{print $2}' | sed 's/ /\n/g' > resnet50_fp32_gpu_pred.txt
python3 compute_mse.py resnet50_fp32_gpu_pred.txt resnet50_output.txt

输出

(1000,) (1000,)
[-0.964525  1.21881  -2.75427  -1.55272  -0.906155 -1.08674  -3.01337  0.64732 ]
[-0.964521  1.218818 -2.754273 -1.552727 -0.906162 -1.086741 -3.013363 0.647324]
MSE:0.00000
2.2.4 测试CPU-FP16MSE
./ExecuteNetwork -c CpuAcc -m resnet50_fp16.tflite -I 1 -d resnet50_input.txt -w temp.txt
cat temp.txt | awk -F, '{print $2}' | awk -F: '{print $2}' | sed 's/ /\n/g' > resnet50_fp16_cpu_pred.txt
python3 compute_mse.py resnet50_fp16_cpu_pred.txt resnet50_output.txt

输出

(1000,) (1000,)
[-0.965583  1.21759  -2.75328  -1.55201  -0.907105 -1.08602  -3.01482   0.646945]
[-0.964521  1.218818 -2.754273 -1.552727 -0.906162 -1.086741 -3.013363  0.647324]
MSE:0.00000
2.2.5 测试GPU-FP16MSE
./ExecuteNetwork -c GpuAcc -m resnet50_fp16.tflite -I 1 -d resnet50_input.txt -w temp.txt
cat temp.txt | awk -F, '{print $2}' | awk -F: '{print $2}' | sed 's/ /\n/g' > resnet50_fp16_gpu_pred.txt
python3 compute_mse.py resnet50_fp16_gpu_pred.txt resnet50_output.txt

输出

(1000,) (1000,)
[-0.965581  1.21759  -2.75328  -1.55201  -0.907109 -1.08602  -3.01482   0.646946]
[-0.964521  1.218818 -2.754273 -1.552727 -0.906162 -1.086741 -3.013363  0.647324]
MSE:0.00000
2.2.6 测试CPU-INT8MSE
./ExecuteNetwork -c CpuAcc -m resnet50_int8.tflite -I 1 -d resnet50_input.txt -w temp.txt
cat temp.txt | awk -F, '{print $2}' | awk -F: '{print $2}' | sed 's/ /\n/g' > resnet50_int8_cpu_pred.txt
python3 compute_mse.py resnet50_int8_cpu_pred.txt resnet50_output.txt

输出

(1000,) (1000,)
[-1.57105   1.45883  -2.30047  -1.2344    0.168327  1.12218  -0.617199  -1.12218 ]
[-0.964521  1.218818 -2.754273 -1.552727 -0.906162 -1.086741 -3.013363  0.647324]
MSE:4.33176
2.2.7 测试GPU-INT8MSE
./ExecuteNetwork -c GpuAcc -m resnet50_int8.tflite -I 1 -d resnet50_input.txt -w temp.txt
cat temp.txt | awk -F, '{print $2}' | awk -F: '{print $2}' | sed 's/ /\n/g' > resnet50_int8_gpu_pred.txt
python3 compute_mse.py resnet50_int8_gpu_pred.txt resnet50_output.txt

输出

(1000,) (1000,)
[-1.40273   1.62716  -2.30047  -1.17829   0.336654  1.06607  -0.617199  -1.12218 ]
[-0.964521  1.218818 -2.754273 -1.552727 -0.906162 -1.086741 -3.013363  0.647324]
MSE:4.28963
2.2.8 性能测试
./ExecuteNetwork -c CpuAcc -m resnet50_opt.onnx -I 5 -N 
./ExecuteNetwork -c GpuAcc -m resnet50_opt.onnx -I 5 -N 

./ExecuteNetwork -c CpuAcc -m resnet50_fp16.tflite -I 5 -N 
./ExecuteNetwork -c GpuAcc -m resnet50_fp16.tflite -I 5 -N 

./ExecuteNetwork -c CpuAcc -m resnet50_int8.tflite -I 5 -N 
./ExecuteNetwork -c GpuAcc -m resnet50_int8.tflite -I 5 -N 

输出

Info: Execution time: 175.39 ms.
Info: Execution time: 71.74 ms.

Info: Execution time: 117.10 ms.
Info: Execution time: 66.84 ms.

Info: Execution time: 46.88 ms.
Info: Execution time: 27.59 ms.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2380501.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2025年渗透测试面试题总结-华顺信安[实习]安全服务工程师(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 华顺信安[实习]安全服务工程师 1. 自我介绍 2. 红蓝队经验 3. Shiro漏洞知识体系 4. APP渗透测试方法…

掌握Git:版本控制与高效协作指南

一、初始Git 提出问题&#xff1a;无论是在工作还是学习&#xff0c;我们在编写各种文档的时候&#xff0c;更改失误&#xff0c;失误后恢复到原来版本&#xff0c;不得不复制出一个副本。 每个版本由各自的内容&#xff0c;但最终只有一个报告需要被我们使用。 但在此之前的…

VsCode和AI的前端使用体验:分别使用了Copilot、通义灵码、iflyCode和Trae

1、前言 大杂烩~每次开发一行代码&#xff0c;各个AI争先恐后抢着提供帮助 备注&#xff1a;四款插件都需要先去官网注册账号&#xff0c;安装好之后有个账号验证。 2、插件详解 2.1、AI分析的答案 GitHub Copilot 定位&#xff1a;老牌 AI 代码补全工具&#xff0c;深度集成…

交叉熵损失函数,KL散度, Focal loss

目录 交叉熵损失函数&#xff08;Cross-Entropy Loss&#xff09; 二分类交叉熵 多分类交叉熵 KL散度&#xff08;Kullback-Leibler Divergence) 交叉熵损失函数和KL散度总结 Focal loss Focal loss 和 交叉熵损失函数 的区别 交叉熵损失函数&#xff08;Cross-Entropy…

【Part 3 Unity VR眼镜端播放器开发与优化】第一节|基于Unity的360°全景视频播放实现方案

《VR 360全景视频开发》专栏 将带你深入探索从全景视频制作到Unity眼镜端应用开发的全流程技术。专栏内容涵盖安卓原生VR播放器开发、Unity VR视频渲染与手势交互、360全景视频制作与优化&#xff0c;以及高分辨率视频性能优化等实战技巧。 &#x1f4dd; 希望通过这个专栏&am…

IDEA连接github(上传项目)

【前提&#xff1a;菜鸟学习的记录过程&#xff0c;如果有不足之处&#xff0c;还请各位大佬大神们指教&#xff08;感谢&#xff09;】 1.先配置好git环境。 没配置的小伙伴可以看上一篇文章教程。 安装git&#xff0c;2.49.0版本-CSDN博客 2.在idea设置git 打开IDEA设置-…

重构研发效能:项目管理引领软件工厂迈向智能化

1.项目管理智能化&#xff0c;激活软件工厂新引擎 在高速发展的软件开发时代&#xff0c;企业如何高效管理多个项目、协调团队合作、优化资源配置&#xff0c;已成为推动技术进步的关键。尤其是在多任务、多项目并行的复杂环境下&#xff0c;智能项目组合管理工具正成为软件工…

Vue3 中使用 provide/inject 实现跨层级组件传值失败的原因及解决方案

1、基础用法 父组件&#xff1a; <script setup> import { ref, provide } from vue; import ChildComponent from ./ChildComponent.vue; const parentData ref(初始数据); // 提供数据 provide(parentData, parentData); </script>子组件&#xff1a; <sc…

小白的进阶之路系列之二----人工智能从初步到精通pytorch中分类神经网络问题详解

什么是分类问题? 分类问题涉及到预测某物是一种还是另一种。 例如,你可能想要: 问题类型具体内容例子二元分类目标可以是两个选项之一,例如yes或no根据健康参数预测某人是否患有心脏病。多类分类目标可以是两个以上选项之一判断一张照片是食物、人还是狗。多标签分类目标…

Vue3——Pinia

目录 什么是 Pinia&#xff1f; 为什么选择 Pinia&#xff1f; 基本使用 安装pinia 配置pinia 定义store 使用 持久化插件 什么是 Pinia&#xff1f; Pinia 是一个轻量级的状态管理库&#xff0c;专为 Vue 3 设计。它提供了类似 Vuex 的功能&#xff0c;但 API 更加简…

02 基本介绍及Pod基础排错

01 yaml文件里的字段错误 # 多打了一个i导致的报错 [rootmaster01 yaml]# cat 01-pod.yaml apiVersion: v1 kind: Pod metadata:name: likexy spec:contaiiners:- name: aaaimage: registry.cn-hangzhou.aliyuncs.com/yinzhengjie-k8s/apps:v1 [rootmaster01 yaml]# kubectl …

⼆叉搜索树详解

1. ⼆叉搜索树的概念 ⼆叉搜索树⼜称⼆叉排序树&#xff0c;它或者是⼀棵空树&#xff0c;或者是具有以下性质的⼆叉树: • 若它的左⼦树不为空&#xff0c;则左⼦树上所有结点的值都⼩于等于根结点的值 • 若它的右⼦树不为空&#xff0c;则右⼦树上所有结点的值都⼤于等于根结…

如何使用通义灵码提高前端开发效率

工欲善其事&#xff0c;必先利其器。对于前端开发而言&#xff0c;使用VSCode已经能够极大地提高前端的开发效率了。但有了AI加持后&#xff0c;前端开发的效率又更上一层楼了&#xff01; 本文采用的AI是通义灵码插件提供的通义千问大模型&#xff0c;是目前AI性能榜第一梯队…

Android Studio Kotlin 中的方法添加灰色参数提示

在使用 Android Studio 时&#xff0c; 我发现使用 Java 编写方法后在调用方法时&#xff0c; 会自动显示灰色的参数。 但在 Kotlin 中没有显示&#xff0c; 于是找了各种方法最后找到了设置&#xff0c; 并且以本文章记录下来。 博主博客 https://blog.uso6.comhttps://blog.…

TCP协议简介

TCP 协议 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是互联网协议套件中的核心协议之一&#xff0c;位于传输层。它提供了一种可靠的、面向连接的、基于字节流的数据传输服务。TCP 的主要特点是确保数据在传输过程中不丢失、不重复&a…

Linux学习心得问题整理(二)

day05 Linux基础入门 Linux语法解析 如何理解ssh远程连接?如何使用ssh使用远程连接服务&#xff1f; ssh进也称远程服务终端&#xff0c;常见连接方式可以包括windows和Linux两种方式 首先咱们使用windows窗口进行连接&#xff0c;这里就采用xshell连接工具来给大家做演示吧…

SOC-ESP32S3部分:2-2-VSCode进行编译烧录

飞书文档https://x509p6c8to.feishu.cn/wiki/CTzVw8p4LiaetykurbTciA42nBf?fromScenespaceOverview 无论是使用Window搭建IDF开发环境&#xff0c;还是使用Linux Ubuntu搭建IDF开发环境&#xff0c;我们都建议使用VSCode进行代码编写和编译&#xff0c;VSCode界面友好&#x…

Python虚拟环境再PyCharm中自由切换使用方法

Python开发中的环境隔离是必不可少的步骤,通过使用虚拟环境可以有效地管理不同项目间的依赖,避免包冲突和环境污染。虚拟环境是Python官方提供的一种独立运行环境,每个项目可以拥有自己单独的环境,不同项目之间的环境互不影响。在日常开发中,结合PyCharm这样强大的IDE进行…

使用Mathematica绘制一类矩阵的特征值图像

学习过线性代数的&#xff0c;都知道&#xff1a;矩阵的特征值非常神秘&#xff0c;但却携带着矩阵的重要信息。 今天&#xff0c;我们将展示&#xff1a;一类矩阵&#xff0c;其特征值集体有着很好的分布特征。 modifiedroots[c_List] : Block[{a DiagonalMatrix[ConstantAr…

SpringBoot-6-在IDEA中配置SpringBoot的Web开发测试环境

文章目录 1 环境配置1.1 JDK1.2 Maven安装配置1.2.1 安装1.2.2 配置1.3 Tomcat1.4 IDEA项目配置1.4.1 配置maven1.4.2 配置File Encodings1.4.3 配置Java Compiler1.4.4 配置Tomcat插件2 Web开发环境2.1 项目的POM文件2.2 项目的主启动类2.3 打包为jar或war2.4 访问测试3 附录3…