onnxruntime c++ inference

1、导出模型

首先,利用pytorch自带的torch.onnx模块导出 .onnx模型文件,具体查看该部分pytorch官方文档,主要流程如下:

import torch
checkpoint = torch.load(model_path)
model = ModelNet(params)
model.load_state_dict(checkpoint['model'])
model.eval()

input_x_1 = torch.randn(10,20)
input_x_2 = torch.randn(1,20,5)
output, mask = model(input_x_1, input_x_2)

torch.onnx.export(model,
                 (input_x_1, input_x_2),
                 'model.onnx',
                 input_names = ['input','input_mask'],
                 output_names = ['output','output_mask'],
                 opset_version=11,
                 verbose = True,
                 dynamic_axes={'input':{1,'seqlen'}, 'input_mask':{1:'seqlen',2:'time'},'output_mask':{0:'time'}})

torch.onnx.export参数在文档里面都有,opset_version对应的版本很重要,dynamic_axes是对输入和输出对应维度可以进行动态设置,不设置的话输入和输出的Tensor 的 shape是不能改变的,如果输入固定就不需要加。

导出的模型可否顺利使用可以先使用python进行检测

2、python推理

import onnxruntime as ort
import numpy as np
ort_session = ort.InferenceSession('model.onnx')
outputs = ort_session.run(None,{'input':np.random.randn(10,20),'input_mask':np.random.randn(1,20,5)})
# 由于设置了dynamic_axes,支持对应维度的变化
outputs = ort_session.run(None,{'input':np.random.randn(10,5),'input_mask':np.random.randn(1,26,2)})
# outputs 为 包含'output'和'output_mask'的list

import onnx
model = onnx.load('model.onnx')
onnx.checker.check_model(model)

如果没有异常代表导出的模型没有问题,目前torch.onnx.export只能对部分支持的Tensor操作进行识别,详情参考Supported operators,对于包括transformer等基本的模型都是没有问题的,如果出现ATen等问题,你就需要对模型不支持的Tensor操作进行改进,以免影响C++对该模型的使用。

3、 c++ inference

下面就是C++通过onnxruntime对.onnx模型的使用,参考官方样例常见问题写的模型多输入多输出的情况,部分参数可以参考样例或者查官方API文档:

#include <assert.h>
#include <vector>
#include <onnxruntime_cxx_api.h>

int main(int argc, char* argv[]) {
  Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "test");
  Ort::SessionOptions session_options;
  session_options.SetIntraOpNumThreads(1);
  
  session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);

#ifdef _WIN32
  const wchar_t* model_path = L"model.onnx";
#else
  const char* model_path = "model.onnx";
#endif

  Ort::Session session(env, model_path, session_options);
  // print model input layer (node names, types, shape etc.)
  Ort::AllocatorWithDefaultOptions allocator;

  // print number of model input nodes
  size_t num_input_nodes = session.GetInputCount();
  std::vector<const char*> input_node_names = {"input","input_mask"};
  std::vector<const char*> output_node_names = {"output","output_mask"};
    
  std::vector<int64_t> input_node_dims = {10, 20};
  size_t input_tensor_size = 10 * 20; 
  std::vector<float> input_tensor_values(input_tensor_size);
  for (unsigned int i = 0; i < input_tensor_size; i++)
    input_tensor_values[i] = (float)i / (input_tensor_size + 1);
  // create input tensor object from data values
  auto memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
  Ort::Value input_tensor = Ort::Value::CreateTensor<float>(memory_info, input_tensor_values.data(), input_tensor_size, input_node_dims.data(), 2);
  assert(input_tensor.IsTensor());

  std::vector<int64_t> input_mask_node_dims = {1, 20, 4};
  size_t input_mask_tensor_size = 1 * 20 * 4; 
  std::vector<float> input_mask_tensor_values(input_mask_tensor_size);
  for (unsigned int i = 0; i < input_mask_tensor_size; i++)
    input_mask_tensor_values[i] = (float)i / (input_mask_tensor_size + 1);
  // create input tensor object from data values
  auto mask_memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
  Ort::Value input_mask_tensor = Ort::Value::CreateTensor<float>(mask_memory_info, input_mask_tensor_values.data(), input_mask_tensor_size, input_mask_node_dims.data(), 3);
  assert(input_mask_tensor.IsTensor());
    
  std::vector<Ort::Value> ort_inputs;
  ort_inputs.push_back(std::move(input_tensor));
  ort_inputs.push_back(std::move(input_mask_tensor));
  // score model & input tensor, get back output tensor
  auto output_tensors = session.Run(Ort::RunOptions{nullptr}, input_node_names.data(), ort_inputs.data(), ort_inputs.size(), output_node_names.data(), 2);
  
  // Get pointer to output tensor float values
  float* floatarr = output_tensors[0].GetTensorMutableData<float>();
  float* floatarr_mask = output_tensors[1].GetTensorMutableData<float>();
  
  printf("Done!\n");
  return 0;
}

编译命令:

 g++ infer.cpp -o infer onnxruntime-linux-x64-1.4.0/lib/libonnxruntime.so.1.4.0 -Ionnxruntime-linux-x64-1.4.0/include/ -std=c++11

onnxruntime中Tensor支持的数据类型包括:

typedef enum ONNXTensorElementDataType {
  ONNX_TENSOR_ELEMENT_DATA_TYPE_UNDEFINED,
  ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT,   // maps to c type float
  ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8,   // maps to c type uint8_t
  ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8,    // maps to c type int8_t
  ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT16,  // maps to c type uint16_t
  ONNX_TENSOR_ELEMENT_DATA_TYPE_INT16,   // maps to c type int16_t
  ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32,   // maps to c type int32_t
  ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64,   // maps to c type int64_t
  ONNX_TENSOR_ELEMENT_DATA_TYPE_STRING,  // maps to c++ type std::string
  ONNX_TENSOR_ELEMENT_DATA_TYPE_BOOL,
  ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16,
  ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE,      // maps to c type double
  ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32,      // maps to c type uint32_t
  ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64,      // maps to c type uint64_t
  ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX64,   // complex with float32 real and imaginary components
  ONNX_TENSOR_ELEMENT_DATA_TYPE_COMPLEX128,  // complex with float64 real and imaginary components
  ONNX_TENSOR_ELEMENT_DATA_TYPE_BFLOAT16     // Non-IEEE floating-point format based on IEEE754 single-precision
} ONNXTensorElementDataType;

其中需要注意的是使用bool型,需要从uint_8的vector转为bool型:

std::vector<uint8_t> mask_tensor_values;
for(int i = 0; i < mask_tensor_size; i++){
	mask_tensor_values.push_back((uint8_t)(true));
}
auto mask_memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
Ort::Value mask_tensor = Ort::Value::CreateTensor<bool>(mask_memory_info, reinterpret_cast<bool *>(mask_tensor_values.data()),mask_tensor_size, mask_node_dims.data(), 3);

性能测试
实际情况粗略统计,以transformer为例,onnxruntime-c++上的运行效率要比pytorch-python快2-5倍

参考:onnxruntime的c++使用
参考:
参考:
参考:

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: 首先,您需要将Yolov5模型转换为ONNX格式。您可以使用PyTorch将模型转换为ONNX格式,然后使用ONNX Runtime C++ API加载和运行模型。 以下是一些步骤: 1. 安装PyTorch和ONNX Runtime 2. 使用PyTorch将Yolov5模型转换为ONNX格式。您可以使用以下代码: ``` import torch import torchvision # Load the model model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True) # Export the model to ONNX format input_shape = (1, 3, 640, 640) torch.onnx.export(model, torch.randn(*input_shape), "yolov5s.onnx", opset_version=11) ``` 3. 在C++中加载和运行模型。您可以使用以下代码: ``` #include <iostream> #include <vector> #include <chrono> #include <opencv2/opencv.hpp> #include "onnxruntime_cxx_api.h" using namespace std; using namespace cv; using namespace std::chrono; using namespace onnxruntime; int main() { // Load the model Ort::SessionOptions session_options; session_options.SetIntraOpNumThreads(1); session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL); Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "test"); Ort::Session session(env, "yolov5s.onnx", session_options); // Get input and output names auto input_names = session.GetInputNames(); auto output_names = session.GetOutputNames(); // Create input tensor Ort::AllocatorWithDefaultOptions allocator; Ort::Value input_tensor(nullptr); Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU); vector<int64_t> input_shape = {1, 3, 640, 640}; input_tensor = Ort::Value::CreateTensor<float>(memory_info, reinterpret_cast<float*>(new float[input_shape[0] * input_shape[1] * input_shape[2] * input_shape[3]]), input_shape.data(), input_shape.size()); // Load image Mat image = imread("test.jpg"); cvtColor(image, image, COLOR_BGR2RGB); resize(image, image, Size(640, 640)); float* input_data = input_tensor.GetTensorMutableData<float>(); for (int i = 0; i < 640 * 640 * 3; i++) { input_data[i] = image.data[i] / 255.0; } // Run inference auto start = high_resolution_clock::now(); vector<Ort::Value> output_tensors = session.Run(output_names, &input_names[0], &input_tensor, 1); auto end = high_resolution_clock::now(); auto duration = duration_cast<milliseconds>(end - start); cout << "Inference time: " << duration.count() << " ms" << endl; // Get output tensor Ort::Value& output_tensor = output_tensors[0]; float* output_data = output_tensor.GetTensorMutableData<float>(); // Process output for (int i = 0; i < 25200; i++) { if (output_data[i * 6 + 4] > 0.5) { int x1 = output_data[i * 6 + 0] * 640; int y1 = output_data[i * 6 + 1] * 640; int x2 = output_data[i * 6 + 2] * 640; int y2 = output_data[i * 6 + 3] * 640; cout << "Object detected: " << output_data[i * 6 + 5] << " (" << x1 << ", " << y1 << ") (" << x2 << ", " << y2 << ")" << endl; } } return 0; } ``` 这个例子假设您有一张名为“test.jpg”的图像,它将被用作模型的输入。它还假设您的模型输出是一个大小为[1, 25200, 6]的张量,其中25200是预测的边界框数,6是每个边界框的属性数(左上角和右下角坐标,置信度和类别)。 请注意,这只是一个简单的例子,您需要根据您的模型和数据进行适当的修改。 ### 回答2: 在使用ONNXRuntime C部署Yolov5之前,首先需要明确的是Yolov5是目标检测算法,而ONNXRuntime C则是一个高性能的推理框架,可以用来推理基于ONNX格式的深度学习模型,这包括Yolov5。 以下是ONNXRuntime C部署Yolov5的步骤参考: 1. 转换模型:由于Yolov5模型原先是以PyTorch格式存储,因此需要将其转化为ONNX格式。具体的转换方法可以参考ONNX官方文档,或者使用现成的转换脚本,如https://github.com/jkjung-avt/yolov5_onnx/blob/main/yolov5_onnx.py。 2. 编写C程序:根据ONNXRuntime C的API,编写C语言程序实现Yolov5模型的加载和推理。可以参考ONNXRuntime官方示例中的代码模板,进行修改和调整,完成模型的加载和推理功能。关键代码如下: ```c // 运行初始化,加载模型 OrtEnv* env; OrtCreateEnv(ORT_LOGGING_LEVEL_WARNING, "test", &env); OrtSessionOptions* session_options = OrtCreateSessionOptions(); OrtSession* session; OrtCreateSession(env, model_path, session_options, &session); // 获取模型输入输出信息 OrtStatus* status; OrtTensorTypeAndShapeInfo* input_info; OrtSessionGetInputTypeInfo(session, 0, &input_info); OrtAllocator* allocator; OrtCreateDefaultAllocator(&allocator); size_t num_inputs; OrtStatus* get_num_input = OrtSessionGetInputCount(session, &num_inputs); OrtValue** input_tensor = (OrtValue**)malloc(num_inputs * sizeof(OrtValue*)); OrtStatus* input_status = OrtCreateTensorAsOrtValue(allocator, input_info, &input_tensor[0]); OrtTypeInfo* type_info; OrtStatus* output_status = OrtSessionGetOutputTypeInfo(session, 0, &type_info); // 给输入tensor赋值 float* input = input_tensor[0]->GetTensorMutableData<float>(); for (int i = 0; i < input_size; i++) { input[i] = input_data[i]; } // 运行模型,获取结果 OrtValue* output_tensor = NULL; const char* output_names[] = { output_name }; OrtStatus* run_status = OrtRun(session, NULL, input_names, &input_tensor[0], num_inputs, output_names, 1, &output_tensor); float* output = output_tensor->GetTensorMutableData<float>(); ``` 3. 编译C程序:使用C编译器,如gcc,编译C程序,并将ONNXRuntime C的库文件链接到程序中,如: ```bash gcc main.c -lonnxruntime ``` 4. 运行C程序:运行编译后的程序,并输入Yolov5需要检测的图片或视频数据,程序将输出检测结果,包括检测框、置信度和类别等信息。 需要注意的几个点: 1. ONNXRuntime C需要先安装ONNXRuntime库,并将其包含到系统路径中。 2. 在程序中需要指定Yolov5的输入尺寸和类别数等信息。 3. 在使用Yolov5推理时,需要先对输入数据进行预处理,如尺寸缩放、通道变换和数据类型转换等。 4. 在编程时,需要对ONNXRuntime C的API进行深入学习,以保证程序的正确性和稳定性。同时,还需要对Yolov5的算法和原理有一定的了解,以便进行模型的参数调整和优化。 ### 回答3: 随着深度学习的广泛应用,越来越多的框架和工具被开发出来,但由于它们之间的差异,将模型从一个框架转换到另一个框架是一项具有挑战性和耗费时间的工作。ONNX(Runtime)是一种广泛接受的中间表示,它可以使不同的框架之间的模型转换变得容易。这篇文章将介绍如何使用ONNXRuntime C++ API来部署一个YOLOv5的模型。 首先,我们需要下载YOLOv5模型的权重和cfg文件,可以从Github上的YOLOv5仓库中下载。在下载完这两个文件后,我们需要用Python中的train.py将它们转换成ONNX文件,具体地,可以使用如下命令: ``` python3 train.py --weights yolov5s.pt --cfg models/yolov5s.yaml --img 640 --batch 1 --no-autoanchor --fuse python3 models/yolo.py --cfg models/yolov5s.yaml --weights yolov5s.pt --names models/coco.names ``` 这将生成名为“yolov5s.onnx”的模型文件。现在,我们可以使用ONNXRuntime C++ API加载和运行它。 首先,我们需要在C++中安装ONNXRuntime的API,可以从官方网站(https://www.onnxruntime.ai/)下载ONNXRuntime C++ API安装文件。安装完成后,我们可以开始编写C++代码来加载和运行YOLOv5模型。 我们需要使用以下头文件: ``` #include "onnxruntime_cxx_api.h" #include <opencv2/opencv.hpp> ``` 接下来,我们需要定义一些变量来存储模型信息。我们可以使用onnxruntime::Env类初始化ONNXRuntime。 ``` std::string model_path = "yolov5s.onnx"; std::shared_ptr<onnxruntime::Environment> env = onnxruntime::Environment::Create(); ``` 然后,我们需要创建一个会话,该会话将包含模型。通过onnxruntime::Session类,我们可以加载模型并返回一个会话句柄。 ``` std::shared_ptr<onnxruntime::Session> session = std::make_shared<onnxruntime::Session>(*env, model_path, nullptr); ``` 我们还需要创建一个输入模型。我们需要使用onnxruntime::Tensor类,它将用于加载输入数据。 ``` std::vector<int64_t> input_node_dims = { 1, 3, 640, 640 }; auto memory_info = onnxruntime::MemoryInfo::CreateCpu(ONNXRUNTIME_CPU, onnxruntime::DeviceAllocatorRegistrationFlags::None); auto input_tensor = onnxruntime::make_unique<onnxruntime::Tensor>(onnxruntime::DataType::Float, onnxruntime::TensorShape(input_node_dims), memory_info); ``` 现在,我们有了将图像转换为ONNX模型所需的一切。我们需要加载图像,将其尺寸调整为模型的输入大小,并使用OpenCV库将图像像素值转换为浮点数。然后我们可以将数据复制到输入_tensor中。 ``` cv::Mat img = cv::imread("test.jpg"); cv::resize(img, img, cv::Size(640, 640), cv::INTER_AREA); img.convertTo(img, CV_32FC3, 1 / 255.0); memcpy(input_tensor->MutableData<float>(), img.data, img.total() * img.elemSize()); ``` 现在,我们可以将输入_tensor传递给模型并运行。 ``` std::vector<const char*> input_node_names = { "input" }; std::vector<const char*> output_node_names = { "output" }; std::vector<OrtValue> output_tensors; OrtTensorScales scales = { nullptr, 0 }; session->Run(Ort::RunOptions{ nullptr }, input_node_names.data(), &input_tensor, input_node_dims.size(), output_node_names.data(), output_node_names.size(), output_tensors, scales); ``` 最后,我们可以使用输出张量中的数据进行后处理,以获得目标框位置和类别预测结果等信息。 这些就是使用ONNXRuntime C++ API来部署YOLOv5模型的基本步骤。当然,如果我们要获得更高的精度和更快的速度,还可以使用各种技术来优化模型和代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落花逐流水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值