赞
踩
之前介绍了TensorRT部署ONNX模型,发现我的模型比较特殊(实现的太烂了 ),使用TensorRT推理不能得到正确的结果,使用ORT部署成功了,因此分享一下ORT的部署过程。
先给一个官网地址: ORT官网,官网的C++安装方式好像是针对.Net项目的(反正我是没看懂 )。所以这里简单介绍一下怎么安装,有两种方法:
仓库编译好的ORT库有系统版本与CPU和GPU版本区分注意选择自己需要的,ORT下载地址。
同时注意ORT所支持的CUDA和cuDNN版本,见CUDA支持官方文档。
我的 CUDA Toolkit 是12.1,cuDNN是8.9.7,因此选用1.17版本ORT。
我需要使用GPU版本ORT,并且我的CUDA版本是12.1因此选择以下文件。
下载完成得到以下文件。
Note:
- CUDA版本只向下兼容大版本号,即12.2只支持12.x,而不支持11.x。
- ORT支持使用TensorRT作为后端来提高模型的推理性能。(
ORT真好用啊,薄纱TensorRT),若想使用TensorRT,需要使用源码自己编译ORT库,同时要注意ORT所支持的CUDA和TensorRT版本,见TensorRT支持官方文档。
这里分享一个中文编译教程:Windows下编译Onnxruntime
主要是配置包含路径和依赖库,类似OpenCV配置。
准备就绪,终于可以开始愉快的部署过程了。
// 初始化ONNX Runtime
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "sdp infer");
// 创建会话选项并添加CUDA
Ort::SessionOptions session_options;
uint32_t device_id = 0; // CUDA 设备 ID
// 启用CUDA
Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CUDA(session_options, device_id));
// 加载模型
const std::wstring model_path = L"model.onnx";
Ort::Session session(env, model_path.c_str(), session_options);
上面代码主要做了2件事,(1)创建ORT环境env
,其全局唯一;(2)创建会话session
,可以存在多个,一个会话对应一个ONNX模型,env
可以运行多个session
。
设置输入输出需要与ONNX模型的输入输出严格对应,这里推荐一个查看模型结构的工具:Netron
使用Netron可以看到我的模型的输入输出节点名称与数据类型和形状信息。
下面我们使用这些信息设置输入输出,在例子中我需要2输入input, s_id
,1输出logits
。并且输入图像需要先进行预处理,我们可以使用OpenCV辅助。基本上都是些公式化的书写,注意代码中的数据类型与模型对应。
图像预处理 / // 用于保存input张量值 std::vector<float> input_tensor_values; // float32 input // 创建输入张量 cv::Mat img = cv::imread("E:/myDataset/mya1/train/fake/1100.bmp"); cv::Size imgSize(224, 224); cv::Mat blob; // 用于存储预处理后的图像 if (img.size() != imgSize) { cv::resize(img, blob, imgSize, 0, 0, cv::INTER_AREA); // 图像缩放 } else { img.copyTo(blob); } blob = cv::dnn::blobFromImage(blob, 1.0 / 255, blob.size(), cv::Scalar(0, 0, 0), true, false, CV_32F); // 图像转为blob格式 float* data_ptr = reinterpret_cast<float*>(blob.data); // 获取blob数据指针 int num_elements = blob.total() * blob.channels(); // 获取blob数据元素个数 // 将blob数据拷贝到input张量中 for (int i = 0; i < num_elements; ++i) { /*std::cout << data_ptr[i] << " ";*/ input_tensor_values.push_back(std::move(data_ptr[i])); } 图像预处理 over / // 创建内存信息 auto memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); // 创建input张量 std::vector<int64_t> input_tensor_shape = { 1, 3,224,224 }; // input张量形状 Ort::Value input_tensor = Ort::Value::CreateTensor<float>(memory_info, input_tensor_values.data(), input_tensor_values.size(), input_tensor_shape.data(), input_tensor_shape.size()); // 创建s_id张量 std::vector<int64_t> sid_tensor_shape = { 1 }; // s_id张量形状 std::vector<int32_t> sid_tensor_values = { 0 }; // int32 s_id Ort::Value sid_tensor = Ort::Value::CreateTensor<int32_t>(memory_info, sid_tensor_values.data(), sid_tensor_values.size(), sid_tensor_shape.data(), sid_tensor_shape.size()); // 设置输入节点名称 std::vector<const char*> input_node_names = { "input","s_id" }; // 创建输入张量数组 std::vector<Ort::Value> input_tensors; // 多输入 input_tensors.push_back(std::move(input_tensor)); input_tensors.push_back(std::move(sid_tensor)); // 设置输出节点名称 std::vector<const char*> output_node_names = { "logits" };
Note:
Ort::Value
删除了赋值操作,因此要使用std::move
移动语义对其对象操作
// 进行推理
auto output_tensors = session.Run(Ort::RunOptions{ nullptr }, // 运行选项 为空即可
input_node_names.data(), // 输入节点名称
input_tensors.data(), // 输入张量
input_tensors.size(), // 输入张量数量
output_node_names.data(), // 输出节点名称
output_node_names.size() // 输出张量数量
);
// 获取输出张量
float* floatarr = output_tensors.front().GetTensorMutableData<float>();
for (int i = 0; i < 2; i++)
printf(" %f\n", floatarr[i]);
使用GetTensorMutableData
获取数据头指针。
这是由于Windows默认的System32下存在同名的onnxruntime.dll
,其优先级高于ORT库中的。
解决方法: 将ORT的lib文件夹里的onnxruntime.dll、onnxruntime_providers_shared.dll、onnxruntime_providers_cuda.dll
复制到工程生成的exe文件夹下。
这个错误有很多原因引起,CUDA错误、输入数据类型与模型不一致、env或session为空指针等等。
解决方法: 通过调用堆栈查看报错语句处理,或者使用try-catch捕捉Ort::Exception获取错误信息。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。