解决|onnxruntime gpu 推理比 torch 慢,session 初始化

用 pytorch 几秒就能跑完的,用 onnxruntime 反而慢了10 倍不止,下图中 ‘CUDAExecutionProvider’ 也说明 onnxruntime 确实是用上了 GPU。
在这里插入图片描述

onnxruntime 部分代码如下

opts = onnxruntime.SessionOptions()
ort_session = onnxruntime.InferenceSession('/path/to/yourmodel.onnx', opts, providers=['CUDAExecutionProvider'])

# double check is using GPU?
print(ort_session.get_providers())

# onnx 的输入是 numpy array 而非 tensor!    
ort_inputs = {'input': numpy_input}

ort_output = ort_session.run(['output'], ort_inputs)[0]
# tensor 转 numpy
ort_output = torch.from_numpy(ort_output)

排查方法

在 onnxruntime.InferenceSession 前可以加入以下代码,

opts.enable_profiling = True 
# 可以在当前目录下保存一个 onnxruntime_profile_xxx.json

onnxruntime.set_default_logger_severity(1) 
# 可以在 bash 中获得详细信息

打开 onnxruntime_profile_xxx.json ,关键看 dur 部分代表了持续时间。在这里插入图片描述

从上图可见 session_initialization 部分耗时占了非常多,有 45335065 微秒,也就是 40 多秒,说明就是 session 初始化占据了大部分时间。

解决方案

将 onnxruntime session 的初始化和实际推理进行分离,即统一进行 session 初始化,而每次用模型时只采用 .run 即可。示例代码如下:

# 全局初始化ONNX Runtime会话
def initialize_session():
    session_options = onnxruntime.SessionOptions()
    session_options.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_ENABLE_EXTENDED
    ort_session = onnxruntime.InferenceSession('/swinir-trt/swinir_real_sr_large_model_dynamic.onnx',
                                               session_options=session_options,
                                               providers=['CUDAExecutionProvider'])
    return ort_session

##### 分割线 #####

# 具体需要用 onnx 模型推理的位置只用 .run
ort_inputs = {'input': numpy_input}
ort_output = ort_session.run(['output'], ort_inputs)[0]
# tensor 转 numpy
ort_output = torch.from_numpy(ort_output)

分离两部分后,每次模型的推理时间便正常了,仅需短短几秒就能完成!

在这里插入图片描述

那么问题来了,为什么 session 初始化要搞怎么久?有了解的朋友欢迎留言讨论呀~

补充:减缓 session 初始化太久的问题

pip install onnx-simplifier
pip3 install -U pip && pip3 install onnxsim
onnxsim input_onnx_model.onnx output_onnx_model.onnx

将原始的 input_onnx_model.onnx 简化为 output_onnx_model.onnx

模型比较复杂的话,会等一段时间,如果成功运行会出现类似如下的提示:

Simplifying...
Finish! Here is the difference:
┏━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┓
┃                    ┃ Original Model ┃ Simplified Model ┃
┡━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━┩
│ Add                │ 1955547              │
│ Cast               │ 27600                │
│ Concat             │ 1913404              │
│ Constant           │ 25917834              │
│ ConstantOfShape    │ 248518               │
│ Conv               │ 3636               │
│ Div                │ 381231              │
│ Equal              │ 205219               │
│ Erf                │ 5454               │
│ Expand             │ 243085               │
│ Gather             │ 2877601              │
│ LayerNormalization │ 110110              │
│ LeakyRelu          │ 2424               │
│ MatMul             │ 324324              │
│ Mod                │ 44                │
│ Mul                │ 2110165              │
│ Not                │ 542                │
│ Pad                │ 11                │
│ Range              │ 194464               │
│ Reshape            │ 2776605              │
│ Resize             │ 22                │
│ ScatterND          │ 48617               │
│ Shape              │ 6613236              │
│ Slice              │ 2335271              │
│ Softmax            │ 5454               │
│ Sub                │ 585                │
│ Transpose          │ 345293              │
│ Unsqueeze          │ 4194472              │
│ Where              │ 205221               │
│ Model Size         │ 125.4MiB       │ 114.7MiB         │
└────────────────────┴────────────────┴──────────────────┘

官方文档:https://pypi.org/project/onnx-simplifier/

参考博客:
https://zhuanlan.zhihu.com/p/686755347

  • 11
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
首先,您需要将Yolov5模型转换为ONNX格式。您可以使用PyTorch将模型转换为ONNX格式,然后使用ONNX Runtime C++ API加载和运行模型。 以下是一些步骤: 1. 安装PyTorchONNX Runtime 2. 使用PyTorchYolov5模型转换为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是每个边界框的属性数(左上角和右下角坐标,置信度和类别)。 请注意,这只是一个简单的例子,您需要根据您的模型和数据进行适当的修改。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值