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

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动态库:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Uniapp判断设备是安卓还是 iOS,并调用不同的方法

在 UniApp 中&#xff0c;可以通过 uni.getSystemInfoSync() 方法来获取设备信息&#xff0c;然后根据系统类型判断当前设备是安卓还是 iOS&#xff0c;并调用不同的方法。 示例代码 export default {onLoad() {this.checkPlatform();},methods: {checkPlatform() {// 获取系…

VMWare虚拟机+Ubuntu24.04+ROS2Jazzy版本安装——踩坑及爬坑过程

VMWare安装 VMWare安装参考VMWare安装&#xff0c;WMWare workstation从17版本以后就面向个人用户免费开放了&#xff0c;所以在安装的最后只要勾选“用于个人”这个选项&#xff0c;就无需再输入激活码等&#xff0c;非常方便。 WMWare workstation17的获取地址&#xff1a;通…

【Golang 面试题】每日 3 题(三十一)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/UWz06 &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏…

分布式数据存储基础与HDFS操作实践(副本)

以下为作者本人撰写的报告&#xff0c;步骤略有繁琐&#xff0c;不建议作为参考内容&#xff0c;可以适当浏览&#xff0c;进一步理解。 一、实验目的 1、理解分布式文件系统的基本概念和工作原理。 2、掌握Hadoop分布式文件系统&#xff08;HDFS&#xff09;的基本操作。 …

《OpenCV》——模版匹配

文章目录 OpenCV——模版匹配简介模版匹配使用场景OpenCV 中模板匹配的函数参数 OpenCV——模版匹配实例导入所需库读取图片并处理图片对模版图片进行处理进行模版匹配显示模版匹配的结果注意事项 OpenCV——模版匹配简介 OpenCV 是一个非常强大的计算机视觉库&#xff0c;其中…

迅翼SwiftWing | ROS 固定翼开源仿真平台正式发布!

经过前期内测调试&#xff0c;ROS固定翼开源仿真平台今日正式上线&#xff01;现平台除适配PX4ROS环境外&#xff0c;也已实现APROS环境下的单机飞行控制仿真适配。欢迎大家通过文末链接查看项目地址以及具体使用手册。 1 平台简介 ROS固定翼仿真平台旨在实现固定翼无人机决策…

基于深度学习的视觉检测小项目(十二) 使用线条边框和渐变颜色美化界面

到目前为止&#xff0c;已经建立起了基本的项目架构&#xff0c;样式表体系也初步具备&#xff0c;但是与成品的界面相比&#xff0c;还是差点什么。 我的界面效果图&#xff1a; 优秀demo的界面截图&#xff1a; 是的&#xff0c;我的界面太“平” 了&#xff0c;没有立体感&…

MySQL(高级特性篇) 06 章——索引的数据结构

一、为什么使用索引 索引是存储引擎用于快速找到数据记录的一种数据结构&#xff0c;就好比一本教科书的目录部分&#xff0c;通过目录找到对应文章的页码&#xff0c;便可快速定位到需要的文章。MySQL中也是一样的道理&#xff0c;进行数据查找时&#xff0c;首先查看查询条件…

Springboot + vue 图书管理系统

&#x1f942;(❁◡❁)您的点赞&#x1f44d;➕评论&#x1f4dd;➕收藏⭐是作者创作的最大动力&#x1f91e; &#x1f496;&#x1f4d5;&#x1f389;&#x1f525; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;欢迎留言讨论 &#x1f525;&#x1f525;&…

2025年01月15日Github流行趋势

1. 项目名称&#xff1a;tabby - 项目地址url&#xff1a;https://github.com/TabbyML/tabby - 项目语言&#xff1a;Rust - 历史star数&#xff1a;25764 - 今日star数&#xff1a;1032 - 项目维护者&#xff1a;wsxiaoys, apps/autofix-ci, icycodes, liangfung, boxbeam - 项…

详解数据增强中的平移shft操作

Shift 平移是指在数据增强&#xff08;data augmentation&#xff09;过程中&#xff0c;通过对输入图像或目标进行位置偏移&#xff08;平移&#xff09;&#xff0c;让目标在图像中呈现出不同的位置。Shift 平移的目的是增加训练数据的多样性&#xff0c;从而提高模型对目标在…

Linux:地址空间(续)与进程控制

hello&#xff0c;各位小伙伴&#xff0c;本篇文章跟大家一起学习《Linux&#xff1a;地址空间与进程控制》&#xff0c;感谢大家对我上一篇的支持&#xff0c;如有什么问题&#xff0c;还请多多指教 &#xff01; 如果本篇文章对你有帮助&#xff0c;还请各位点点赞&#xff0…

RabbitMQ(三)

RabbitMQ中的各模式及其用法 工作队列模式一、生产者代码1、封装工具类2、编写代码3、发送消息效果 二、消费者代码1、编写代码2、运行效果 发布订阅模式一、生产者代码二、消费者代码1、消费者1号2、消费者2号 三、运行效果四、小结 路由模式一、生产者代码二、消费者代码1、消…

ssh,samba,tftp,nfs服务安装和配置

前提准备 sudo ufw disable sudo ufw status sudo apt update ssh服务 sudo apt-get install openssh-server sudo apt-get install openssh-client sudo apt-get install ssh echo "PasswordAuthentication yes" >> /etc/ssh/ssh_config //配置ssh客户…

.NetCore 使用 NPOI 读取带有图片的excel数据

在.NetCore使用NPOI插件进行批量导入时&#xff0c;获取Excel中的所有的图片数据&#xff0c;存到集合中。 1.定义类PictureData 代码如下&#xff1a; public class PictureData { public byte[] Data { get; set; } } 2.数据集引用 using NPOI.XSSF.UserModel; usin…

MAC上安装Octave

1. 当前最新版Octave是9.3版本&#xff0c;需要把mac os系统升级到14版本&#xff08;本人之前的版本是10版本&#xff09; https://wiki.octave.org/Octave_for_macOS octave的历史版本参考此文档&#xff1a;Octave for macOS (outdated) - Octavehttps://wiki.octave.org/Oc…

文档智能:OCR+Rocketqa+layoutxlm <Rocketqa>

此次梳理Rocketqa&#xff0c;个人认为该篇文件讲述的是段落搜索的改进点&#xff0c;关于其框架&#xff1a;粗检索 重排序----&#xff08;dual-encoder architecture&#xff09;&#xff0c;讲诉不多&#xff0c;那是另外的文章&#xff1b; 之前根据文档智能功能&#x…

算法每日双题精讲 —— 二分查找(二分查找,在排序数组中查找元素的第一个和最后一个位置)

&#x1f31f;快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。 &#x1f31f; 别再犹豫了&#xff01;快来订阅我们的算法每日双题精讲专栏&#xff0c;一起踏上算法学习的精彩之旅吧&#xff01;&#x1f4aa…

【RDMA学习笔记】1:RDMA(Remote Direct Memory Access)介绍

从帝国理工的PPT学习。 什么是RDMA Remote Direct Memory Access&#xff0c;也就是Remote的DMA&#xff0c;是一种硬件机制&#xff0c;能直接访问远端结点的内存&#xff0c;而不需要处理器介入。 其中&#xff1a; Remote&#xff1a;跨node进行数据传输Direct&#xff…

「实战应用」如何为DHTMLX JavaScript 甘特图添加进度线

DHTMLX Gantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表。可满足项目管理应用程序的所有需求&#xff0c;是最完善的甘特图图表库。 今天&#xff0c;您将学习如何使用进度线补充JavaScript 甘特图&#xff0c;以便于监控项目进度。 DHTMLX Gantt 最新试用版下载 …