Pytorch导出onnx模型并在C++环境中调用(含python和C++工程)

news2025/1/13 11:09:45

Pytorch导出onnx模型并在C++环境中调用(含python和C++工程)

工程下载链接:Pytorch导出onnx模型并在C++环境中调用(python和C++工程)

机器学习多层感知机MLP的Pytorch实现-以表格数据为例-含数据集和PyCharm工程中简单介绍了在python中使用pytorch搭建神经网络模型的步骤和代码工程,此处介绍AI模型的跨平台调用问题,即使用跨平台的ONNX框架,在C++代码中进行模型调用

参考:pytorch导出模型并使用onnxruntime C++部署加载模型推理

目录

  • Pytorch导出onnx模型并在C++环境中调用(含python和C++工程)
    • 1、pkl权重文件转换为onnx模型
      • 1.1、加载保存的模型
      • 1.2、pytorch调用加载的模型进行测试
      • 1.3、导出onnx模型
      • 1.4、在python中调用onnx模型测试
      • 1.5、全部代码
    • 2、C++调用onnx模型
      • 2.1、库的下载安装和官方手册
      • 2.2、C++调用代码实现
      • 2.3、注意,模型文件和onnx的dll要在exe同一级目录
    • 3、运行时遇到的一些问题
      • 编译报错---error: ‘_Frees_ptr_opt_‘ has not been declared
      • 运行报错---The given version [14] is not supported, only version 1 to 10 is supported in this build.

1、pkl权重文件转换为onnx模型

在机器学习多层感知机MLP的Pytorch实现-以表格数据为例-含数据集和PyCharm工程中,我们对训练完成的模型进行了模型的保存:

torch.save(model.state_dict(),
           'weights/mlp_weights-epoch%d-Total_loss%.4f-val_loss%.4f.pkl' % (
               (epoch + 1), train_loss, val_loss / (iteration + 1)))

此处我们需要先加载保存的模型,如何再将其导出为onnx格式。

1.1、加载保存的模型

这一步主要是要把保存的模型恢复出来:

import numpy as np
import onnxruntime
import torch
from torch import nn
import torch.nn.functional as F


# 定义多层感知机(MLP)类,继承自nn.Module
class MLP(nn.Module):
    # 类的初始化方法
    def __init__(self):
        # 调用父类nn.Module的初始化方法
        super(MLP, self).__init__()
        self.hidden1 = nn.Linear(in_features=8, out_features=50, bias=True)
        self.hidden2 = nn.Linear(50, 50)
        self.hidden3 = nn.Linear(50, 50)
        self.hidden4 = nn.Linear(50, 50)
        self.predict = nn.Linear(50, 1)

    # 定义前向传播方法
    def forward(self, x):
        # x是输入数据,通过第一个隐藏层并应用ReLU激活函数
        x = F.relu(self.hidden1(x))
        x = F.relu(self.hidden2(x))
        x = F.relu(self.hidden3(x))
        x = F.relu(self.hidden4(x))
        output = self.predict(x)
        # 将输出展平为一维张量
        # out = output.view(-1)
        return output


# 检查是否有可用的GPU
# device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device = "cpu"
# 定义模型并将其转移到GPU
model = MLP().to(device)
model.load_state_dict(torch.load('weights/mlp_weights-epoch1000-Total_loss0.0756-val_loss0.0105.pkl',
                                 weights_only=True))
model.eval()

1.2、pytorch调用加载的模型进行测试

先简单测试一下这个模型,定义一个输入全为0的数组作为输入,打印输出的结果:

# 初始化一个输入全为0的数组进行测试
x = (torch.from_numpy(np.array([0, 0, 0, 0, 0, 0, 0, 0]).astype(np.float32)).to(device))
y = model(x).cpu().detach().numpy()[0]
print(f"pytorch直接测试结果为: {y}")

在这里插入图片描述

1.3、导出onnx模型

使用下面的代码将原来的模型model导出为onnx的模型,其中x是上面定义的案例输入

batch_size = 1  # 批处理大小
export_onnx_file = "test.onnx"  # 目的ONNX文件名
torch.onnx.export(model,
                  (x),
                  export_onnx_file,
                  opset_version=10,
                  do_constant_folding=True,  # 是否执行常量折叠优化
                  input_names=["input"],  # 输入名
                  output_names=["output"],  # 输出名
                  dynamic_axes={"input": {0: "batch_size"},  # 批处理变量
                                "output": {0: "batch_size"}})

这个函数的具体定义可以参考:从pytorch转换到onnx

1.4、在python中调用onnx模型测试

使用下面代码加载onnx模型并进行测试:

resnet_session = onnxruntime.InferenceSession(export_onnx_file)
inputs = {resnet_session.get_inputs()[0].name: x.cpu().detach().numpy()}
outs = resnet_session.run(None, inputs)[0][0]
print(f"py onnx直接测试结果为: {outs}")

可以看到pytorch的结果和onnx的结果是基本一致的
在这里插入图片描述

1.5、全部代码

import numpy as np
import onnxruntime
import torch
from torch import nn
import torch.nn.functional as F


# 定义多层感知机(MLP)类,继承自nn.Module
class MLP(nn.Module):
    # 类的初始化方法
    def __init__(self):
        # 调用父类nn.Module的初始化方法
        super(MLP, self).__init__()
        self.hidden1 = nn.Linear(in_features=8, out_features=50, bias=True)
        self.hidden2 = nn.Linear(50, 50)
        self.hidden3 = nn.Linear(50, 50)
        self.hidden4 = nn.Linear(50, 50)
        self.predict = nn.Linear(50, 1)

    # 定义前向传播方法
    def forward(self, x):
        # x是输入数据,通过第一个隐藏层并应用ReLU激活函数
        x = F.relu(self.hidden1(x))
        x = F.relu(self.hidden2(x))
        x = F.relu(self.hidden3(x))
        x = F.relu(self.hidden4(x))
        output = self.predict(x)
        # 将输出展平为一维张量
        # out = output.view(-1)
        return output


# 检查是否有可用的GPU
# device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device = "cpu"
# 定义模型并将其转移到GPU
model = MLP().to(device)
model.load_state_dict(torch.load('weights/mlp_weights-epoch1000-Total_loss0.0756-val_loss0.0105.pkl',
                                 weights_only=True))
model.eval()

# 初始化一个输入全为0的数组进行测试
x = (torch.from_numpy(np.array([0, 0, 0, 0, 0, 0, 0, 0]).astype(np.float32)).to(device))
y = model(x).cpu().detach().numpy()[0]
print(f"pytorch直接测试结果为: {y}")



batch_size = 1  # 批处理大小
export_onnx_file = "test.onnx"  # 目的ONNX文件名
torch.onnx.export(model,
                  (x),
                  export_onnx_file,
                  opset_version=10,
                  do_constant_folding=True,  # 是否执行常量折叠优化
                  input_names=["input"],  # 输入名
                  output_names=["output"],  # 输出名
                  dynamic_axes={"input": {0: "batch_size"},  # 批处理变量
                                "output": {0: "batch_size"}})

resnet_session = onnxruntime.InferenceSession(export_onnx_file)
inputs = {resnet_session.get_inputs()[0].name: x.cpu().detach().numpy()}
outs = resnet_session.run(None, inputs)[0][0]
print(f"py onnx直接测试结果为: {outs}")

2、C++调用onnx模型

2.1、库的下载安装和官方手册

这个整个库的下载还是要到官方的github仓库去:microsoft/onnxruntime
具体的使用方式参考英文的手册:https://onnxruntime.ai/docs/】

此处下载的window版本的,下载下来可以得到头文件和库文件:
在这里插入图片描述在这里插入图片描述
因此在实际编程的时候我使用的Cmakelist来链接到相关的库,我是使用VS code + gcc构成的C++编译环境

if (WIN32)
    include_directories(${CMAKE_CURRENT_SOURCE_DIR}/onnxruntime-1.14.0/onnxruntime-win-x64-1.14.0/include)
else()
    include_directories(${CMAKE_CURRENT_SOURCE_DIR}/onnxruntime-1.14.0/onnxruntime-linux-x64-1.14.0/include)
endif()

if (WIN32)
    link_directories(
        ${CMAKE_CURRENT_SOURCE_DIR}/onnxruntime-1.14.0/onnxruntime-win-x64-1.14.0/lib
    )
else()
    link_directories(
        ${CMAKE_CURRENT_SOURCE_DIR}/onnxruntime-1.14.0/onnxruntime-linux-x64-1.14.0/lib
    )
endif()

实际的工程目录结构如下所示:
在这里插入图片描述

2.2、C++调用代码实现

下面代码实现和1.4、在python中调用onnx模型测试相同的效果,输入是全0的数组,进行计算并返回相关结果:

#include <iostream>
#include <array>
#include <algorithm>
#include "onnxruntime_cxx_api.h"

#define ONNX_IN_OUT_SIZE_MAX 20

int main(int argc, char* argv[])
{
    //print hello
    printf("hello");
    std::vector<float> input_matrix_vector={0, 0, 0, 0, 0, 0, 0, 0};
    int onnx_input_shape = 8;
    int onnx_output_shape = 1;




    // --- define model path
    #if _WIN32
        const wchar_t* model_path = L"./model.onnx"; // you can use string to wchar_t* function to convert
    #else
        const char* model_path = "./model.onnx";
    #endif

    // --- init onnxruntime env
    Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "Default");

    auto memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);

    // set options
    Ort::SessionOptions session_option;
    session_option.SetIntraOpNumThreads(1); // extend the number to do parallel
    session_option.SetGraphOptimizationLevel(ORT_ENABLE_ALL);

    // --- prepare data
    const char* input_names[] = { "input" }; // must keep the same as model export
    const char* output_names[] = { "output" };

    std::array<float, ONNX_IN_OUT_SIZE_MAX> input_matrix;
    std::array<float, ONNX_IN_OUT_SIZE_MAX> output_matrix;
    if(input_matrix_vector.size()>ONNX_IN_OUT_SIZE_MAX)
    {
        throw std::runtime_error("input_matrix_vector.size()<ONNX_IN_OUT_SIZE_MAX");
    }

    std::copy(input_matrix_vector.begin(),input_matrix_vector.end(),input_matrix.begin());

    // must use int64_t type to match args
    std::array<int64_t, 1> input_shape{ onnx_input_shape };
    std::array<int64_t, 1> output_shape{ onnx_output_shape };

    

    Ort::Value input_tensor = Ort::Value::CreateTensor<float>(memory_info, input_matrix.data(), input_matrix.size(), input_shape.data(), input_shape.size());
    Ort::Value output_tensor = Ort::Value::CreateTensor<float>(memory_info, output_matrix.data(), output_matrix.size(), output_shape.data(), output_shape.size());

    // --- predict
    Ort::Session session(env, model_path, session_option); // FIXME: must check if model file exist or valid, otherwise this will cause crash
    session.Run(Ort::RunOptions{ nullptr }, input_names, &input_tensor, 1, output_names, &output_tensor, 1); // here only use one input output channel
    
    std::vector<float> outputVector(onnx_output_shape);
    std::copy(output_matrix.begin(),output_matrix.begin()+onnx_output_shape,outputVector.begin());

    std::cout << "--- predict result ---" << std::endl;
    // matrix output
    std::cout << "ouput matrix: ";
    for (int i = 0; i < outputVector.size(); i++)
        std::cout << outputVector[i] << " ";
    std::cout << std::endl;
    // argmax value
    // int argmax_value = std::distance(output_matrix.begin(), std::max_element(output_matrix.begin(), output_matrix.end()));
    // std::cout << "output argmax value: " << argmax_value << std::endl;

    // getchar();

    return 0;
}

可以看到最终的返回结果为:
在这里插入图片描述
和之前在python中的结果是一致的!!!

2.3、注意,模型文件和onnx的dll要在exe同一级目录

在这里插入图片描述

3、运行时遇到的一些问题

编译报错—error: ‘Frees_ptr_opt‘ has not been declared

在编译器命令行或者代码中定义这些宏,使其在非MSVC环境中被忽略。在代码的开头( onnxruntime_c_api.h 文件中)添加以下代码:

#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
//add code here
#ifndef _Frees_ptr_opt_ 
#define _Frees_ptr_opt_ 
#endif 
#ifndef _In_ 
#define _In_ 
#endif 
#ifndef _Out_ 
#define _Out_ 
#endif 
#ifndef _Inout_ 
#define _Inout_
#endif

运行报错—The given version [14] is not supported, only version 1 to 10 is supported in this build.

将onnxruntime-1.14.0\onnxruntime-win-x64-1.14.0\lib的onnxruntime.dll复制一份到exe的目录下面,这是因为路径默认索引的是System32中的老版本库文件:
在这里插入图片描述
System32中存在老版本的onnx动态库:
在这里插入图片描述

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

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

相关文章

git打补丁

1、应用场景 跨仓库升级 开发项目B使用的是开源项目A。开源项目A发现漏洞&#xff0c;作者进行了修复&#xff0c;我们可以通过使用git补丁的方式&#xff0c;将作者修改的内容复制到我 们的项目B中。 2、TortoiseGit方式 源仓库 格式化补丁 根据提交数量&#xff0c;生成…

计算机网络 (34)可靠传输的工作原理

前言 计算机网络可靠传输的工作原理主要依赖于一系列协议和机制&#xff0c;以确保数据在传输过程中能够准确无误地到达目的地。 一、基本概念 可靠传输指的是数据链路层的发送端发送什么&#xff0c;在接收端就收到什么&#xff0c;即保证数据的完整性、正确性和顺序性。由于网…

基于ADAS 与关键点特征金字塔网络融合的3D LiDAR目标检测原理与算法实现

一、概述 3D LiDAR目标检测是一种在三维空间中识别和定位感兴趣目标的技术。在自动驾驶系统和先进的空间分析中&#xff0c;目标检测方法的不断演进至关重要。3D LiDAR目标检测作为一种变革性的技术&#xff0c;在环境感知方面提供了前所未有的准确性和深度信息. 在这里&…

Vue3初学之常用的指令

v-bind&#xff1a;动态绑定属性 v-bind 用于动态绑定一个或多个属性&#xff0c;或一个组件 prop 到表达式的值。 v-model&#xff1a;双向数据绑定 见上篇 https://editor.csdn.net/md/?articleId145022994 v-if、v-else-if、v-else&#xff1a;条件渲染 v-show&…

docker中jenkins流水线式部署GitLab中springboot项目

本质就是将java项目拉取下来&#xff0c;并自动打包成docker镜像&#xff0c;运行 首先启动一个docker的jenkins 如果没有镜像使用我的镜像 通过网盘分享的文件&#xff1a;jenkins.tar 链接: https://pan.baidu.com/s/1VJOMf6RSIQbvW_V1zFD7eQ?pwd6666 提取码: 6666 放入服…

在ubuntu下对NFS做性能测试

安装NFS 首先&#xff0c;安装服务 sudo apt update sudo apt install nfs-kernel-server然后创建共享文件夹 # 请自定义你自己的共享目录 sudo mkdir -p /exports/nfs4/homes sudo chmod -R 777 /exports/nfs4/homes# 这个可以根据no_root_squash标致选择设置。 # 如果不设…

Open FPV VTX开源之默认MAVLink设置

Open FPV VTX开源之默认MAVLink设置 1. 源由2. 准备3. 连接4. 安装5. 配置6. 测试6.1 启动wfb-ng服务6.2 启动wfb-ng监测6.3 启动QGroundControl6.4 观察测试结果 7. 总结8. 参考资料9. 补充9.1 telemetry_tx异常9.2 DEBUG串口部分乱码9.3 PixelPilot软件问题 1. 源由 飞控图传…

26个开源Agent开发框架调研总结(2)

根据Markets & Markets的预测&#xff0c;到2030年&#xff0c;AI Agent的市场规模将从2024年的50亿美元激增至470亿美元&#xff0c;年均复合增长率为44.8%。 Gartner预计到2028年&#xff0c;至少15%的日常工作决策将由AI Agent自主完成&#xff0c;AI Agent在企业应用中…

mark 一下conductor github

Netflix 关闭conductor 后&#xff0c;后续https://orkes.io/content/ 继续在维护&#xff0c;github地址如下 https://github.com/conductor-oss/conductor 最新release为3.21.11

PyCharm文档管理

背景&#xff1a;使用PyCharmgit做文档管理 需求&#xff1a;需要PyCharm自动识别docx/xslx/vsdx等文件类型&#xff0c;并在PyCharm内点击文档时唤起系统内关联应用(如word、excel、visio) 设置步骤&#xff1a; 1、file -》 settings -》file types 2、在Files opened i…

嘉立创画原理图和PCB

一、环境 进入立创EDA官网 注册登录的环节就不介绍了。 登录账号后&#xff0c;选择专业版 二、原理图 工程中&#xff0c;有原理图和PCB&#xff0c;这里选择原理图 那么接下来就是进行绘制 元器件在如下区域搜索使用。 双击进行放置&#xff0c;也可以左键提前预览。 网…

科创驱动 | 华望系统科技荣膺西湖区年度前沿创新新锐企业

2025年1月3日&#xff0c;由中共西湖区党委、西湖区人民政府主办的“新年第一会”—西湖区科技创新大会在杭州隆重举行。大会现场揭晓了西湖区年度科技创新团队与项目&#xff0c;并发布了“2024西湖区科技十大事件”与“西湖区五大年度科技榜单”。杭州华望系统科技有限公司榜…

Monorepo设置:新手指南

Monorepo是一种项目代码管理方法&#xff0c;指在单个代码仓库中管理多个项目&#xff0c;有助于简化代码共享、版本控制、构建和部署的复杂性&#xff0c;并提供更好的可重用性和协作性。 简单理解&#xff1a;所有项目都在一个代码仓库中 &#x1f4e6;&#xff0c;但这并不意…

[Python学习日记-75] 计算机基础与网络

[Python学习日记-75] 计算机基础与网络 简介 计算机基础 什么是网络编程 计算机网络 简介 本篇主要介绍的计算机基础是浓缩的&#xff0c;这是因为我们主要学习的是 Python&#xff0c;而 Python 主要是为了开发应用程序的&#xff0c;并不会用它来开发操作系统和嵌入式程序…

1. Doris分布式环境搭建

一. 环境准备 本次测试集群采用3台机器hadoop1、hadoop2、hadoop3, Frontend和Backend部署在同一台机器上&#xff0c;Frontend部署3台组成高可用&#xff0c;Backend部署3个节点&#xff0c;组成3副本存储。 主机IP操作系统FrontendBackendhadoop1192.168.47.128Centos7Foll…

【Java】-- 利用 jar 命令将配置文件添加到 jar 中

目录 1、准备 2、目标 3、步骤 3.1、安装 jdk 3.2、添加配置文件 3.3、校验 1、准备 java 环境hadoop-core-1.2.1.jar 和 core-site.xml 2、目标 将 core-site.xml 添加到 hadoop-core-1.2.1.jar 中。 3、步骤 3.1、安装 jdk 3.2、添加配置文件 jar -cvf hadoop-core-…

day14-Linux系统基础权限知识精讲

1. 给文件加特殊属性 1.1 chattr a:只能追加内容&#xff0c;不能删除 i:不能修改&#xff0c;不能删除;保护关键文件&#xff0c;防止非法写入 [rootoldboy ~]# chattr a test.txt [rootoldboy ~]# chattr i test.txt [rootoldboy ~]# echo 123 >> test.txt -bash: t…

Android使用系统消息与定时器实现霓虹灯效果

演示效果: 界面设计: 在帧布局FrameLayout中添加6个TextView 依次设置这6个TextView的宽&#xff0c;高&#xff0c;权重 也可在XML中直接设置 添加自定义颜色 关联自定义颜色到数组变量 关联6个TextView控件到数组变量 处理自定义系统消息 Handler _sysHandler new Han…

数据结构大作业——家谱管理系统(超详细!完整代码!)

目录 设计思路&#xff1a; 一、项目背景 二、功能分析 查询功能流程图&#xff1a; 管理功能流程图&#xff1a; 三、设计 四、实现 代码实现&#xff1a; 头文件 结构体 函数声明及定义 创建家谱树头结点 绘制家谱树&#xff08;打印&#xff09; 建立右兄弟…

vue3+elementPlus之后台管理系统(从0到1)(day1)

vue3官方文档&#xff1a;https://cn.vuejs.org/guide/introduction.html 1、项目创建 确保电脑已安装node 查看命令&#xff1a; node -v进入项目目录&#xff0c;创建项目 npm init vuelatest Need to install the following packages: create-vue3.13.0 Ok to procee…