ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

ONNX使用pytorch导出的模型进行推理

2021-06-30 14:30:31  阅读:1484  来源: 互联网

标签:node tensor ONNX 导出 pytorch session input output size


官方例程

https://github.com/microsoft/onnxruntime/blob/master/csharp/test/Microsoft.ML.OnnxRuntime.EndToEndTests.Capi/CXX_Api_Sample.cpp

在VisualStudio使用NuGet安装Onnx-Runtime.GPU

  1. 点击项目,管理NuGet程序包
  2. 点击预览搜索Microsoft.ML.OnnxRuntime.Gpu
  3. 安装对应版本的Runtime
    NuGet安装

修改后的代码

使用pytorch导出的人体关键点模型kp.onnx,输出是16个关键点的heat map

#include <iostream>
#include <assert.h>
#include <vector>
// 引入runtime库
#include <onnxruntime_cxx_api.h>
// 引入cuda库
#include "cuda_provider_factory.h"
#include <ctime>
int main(int argc, char* argv[])
{
	//设置为VERBOSE,方便控制台输出时看到是使用了cpu还是gpu执行
    Ort::Env env(ORT_LOGGING_LEVEL_VERBOSE, "test");
    Ort::SessionOptions session_options;
    // 使用五个线程执行op,提升速度
    session_options.SetIntraOpNumThreads(5);
    // 第二个参数代表GPU device_id = 0,注释这行就是cpu执行
    OrtSessionOptionsAppendExecutionProvider_CUDA(session_options, 0);
    // ORT_ENABLE_ALL: To Enable All possible opitmizations
    session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);
#ifdef _WIN32
    const wchar_t* model_path = L"kp.onnx";
#else
    const char* model_path = "squeezenet.onnx";
#endif

    printf("Using Onnxruntime C++ API\n");
    Ort::Session session(env, model_path, session_options);

    Ort::AllocatorWithDefaultOptions allocator;
	// 获得模型又多少个输入和输出,一般是指对应网络层的数目
	// 一般输入只有图像的话input_nodes为1
    size_t num_input_nodes = session.GetInputCount();
    // 如果是多输出网络,就会是对应输出的数目
    size_t num_output_nodes = session.GetOutputCount();
    std::vector<const char*> input_node_names(num_input_nodes);
    std::vector<const char*> output_node_names(num_output_nodes);
    std::vector<int64_t> input_node_dims;
    std::vector<int64_t> output_node_dims;
    printf("Number of inputs = %zu\n", num_input_nodes);
    printf("Number of output = %zu\n", num_output_nodes);
	// 迭代所有输出层信息
    for (int i = 0; i < num_output_nodes; i++) {
        char* output_name = session.GetOutputName(i, allocator);
        printf("Output %d : name=%s\n", i, output_name);
        // 将输出层的名称添加到output_node_names这个vector
        output_node_names[i] = output_name;

        Ort::TypeInfo type_info = session.GetOutputTypeInfo(i);
        auto tensor_info = type_info.GetTensorTypeAndShapeInfo();

        ONNXTensorElementDataType type = tensor_info.GetElementType();
        printf("Output %d : type=%d\n", i, type);

        output_node_dims = tensor_info.GetShape();
        printf("Output %d : num_dims=%zu\n", i, output_node_dims.size());
        for (int j = 0; j < output_node_dims.size(); j++)
            printf("Output %d : dim %d=%jd\n", i, j, output_node_dims[j]);
    }
	// 获取所有输入层信息
    for (int i = 0; i < num_input_nodes; i++) {
        char* input_name = session.GetInputName(i, allocator);
        printf("Input %d : name=%s\n", i, input_name);
        input_node_names[i] = input_name;

        Ort::TypeInfo type_info = session.GetInputTypeInfo(i);
        auto tensor_info = type_info.GetTensorTypeAndShapeInfo();

        ONNXTensorElementDataType type = tensor_info.GetElementType();
        printf("Input %d : type=%d\n", i, type);

        input_node_dims = tensor_info.GetShape();
        printf("Input %d : num_dims=%zu\n", i, input_node_dims.size());
        for (int j = 0; j < input_node_dims.size(); j++)
            printf("Input %d : dim %d=%jd\n", i, j, input_node_dims[j]);
    }
	// 输入的总大小(所有像素)
    size_t input_tensor_size = 256 * 256 * 3;
	// 生成假的输入
    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);

    auto memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
    clock_t startTime, endTime;
    // 创建输入tensor
    Ort::Value input_tensor = Ort::Value::CreateTensor<float>(memory_info, input_tensor_values.data(), input_tensor_size, input_node_dims.data(), 4);
    assert(input_tensor.IsTensor());
    startTime = clock();
    // 第二个参数代表输入节点的名称集合
    // 第四个参数1代表输入层的数目
    // 第五个参数代表输出节点的名称集合
    // 最后一个参数代表输出节点的数目
    // 除了第一个节点外,其他参数与原网络对应不上程序就会无法执行
    auto output_tensors = session.Run(Ort::RunOptions{ nullptr }, input_node_names.data(), &input_tensor, 1, output_node_names.data(), num_output_nodes);
    endTime = clock();
    assert(output_tensors.size() == 1 && output_tensors.front().IsTensor());
    // 获取输出输出
    float* floatarr = output_tensors.front().GetTensorMutableData<float>();
	// TODO 因为这里我的输出是个heat map,暂时还没完成这部分功能
	// 计算运行时间
    std::cout << "The run time is:" << (double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << std::endl;
    printf("Done!\n");
    system("pause");
    return 0;
}

cpu与gpu执行速度对比

设置op现成为1,对两者进行比较

session_options.SetIntraOpNumThreads(5);

GPU

gpu

CPU

CPU

总结

结果竟然cpu速度比gpu快…根据上方红色框可以看到,cpu执行会提示for cpu字眼,gpu执行会提示for cuda字眼。这个问题可能是因为部分op官方还没有用CUDA实现,在cpu上面是做了很多优化,当前网络在cpu上最快能达到30fps。即使使用官方例程还是cpu比gpu快…在pc平台还是用TensorRT吧。

一个官方issue的原话,关键点是最后一句话。

If you set the session log severity level to VERBOSE, it’ll print which nodes were assigned to GPU and which ones to CPU. Let us know what this prints. It might explain the behavior. We don’t have CUDA implementations for all ops.

标签:node,tensor,ONNX,导出,pytorch,session,input,output,size
来源: https://blog.csdn.net/a2824256/article/details/118359382

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有