利用onnx和onnxruntime实现pytorch深度框架使用C++推理进行服务器部署,模型推理的性能是比python快很多的
python:
pytorch == 1.6.0
onnx == 1.7.0
onnxruntime == 1.3.0
c++:
首先,利用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进行检测
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++对该模型的使用。
下面就是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倍
文章浏览阅读5.7k次,点赞3次,收藏4次。服务器上都配置完毕,jenkins启动成功后访问不到这个url检查一下你的端口是否开启firewall-cmd --list-ports如果发现没有jenkins的端口,否则加上firewall-cmd --permanent --zone=public --add-port=8080/tcp开启已经配置好的jenkins的端口,然后在重启防火墙,注意一定要重启防火墙!systemctl reload firewalld注意jenkins默认的是8080端口,修改的话要到指定目录下修改vi_搜索端口8080,内容包含jenkins的网站
文章浏览阅读5.4k次。简介AddSingleton:此方法创建一个 Singletion 服务。首次请求时创建 Singleton 服务。然后,所有后续的请求中都会使用相同的实例。因此,通常每个应用程序只创建一次 Singleton 服务,并且在整个应用程序生命周期中使用该单个实例 AddScoped:此方法创建一个 Scoped 服务。在范围内的每个请求中创建一个新的 Scoped 服务实例。例如在 We..._addsingleton
文章浏览阅读1.6w次,点赞33次,收藏208次。股吧数据抓取分析_股吧爬虫
文章浏览阅读3.5k次,点赞3次,收藏11次。oRTP 是一个 RTP (Real-time Transport Protocol (RFC 3550)) 协议的库实现,它完全以 C 语言来实现,因此方便应用于各种不同的平台。本文分享用 oRTP 发送,以 Android 的 MediaCodec 编码出来的原始 H.264 码流,又称裸流的方法。H.264 码流MediaCode 以 H.264 编码格式编码之后的视频,是由一个一个的NALU_ortp-warning-rtp_session_ts_to_t: use of unsupported payload type 96.
文章浏览阅读217次。目录1. 压缩包管理1>. 屌丝版:2>. 高富帅版:1). tar -- 不使用z/j参数 , 该命令只能对文件或目录打包2). rar -- 必须手动安装该软件3). zip --总结: 相同之处:2. 进程管理:1. 压缩包管理1>. 屌丝版:1). gzip – .gz格式的压缩包gzip *.txt //每个文件分别压缩 不会保留原始文件了 不能打包压缩gunzip *.txt//解压缩 还原文件2). bzip2 – .bz2格式的压缩包bzip2 *.txt /_linux useradd 用户名称大小写
文章浏览阅读103次。转自:http://www.pinlue.com/article/2020/12/1609/3211419596458.html_java最强学习目录
文章浏览阅读8.4k次,点赞5次,收藏7次。AUTO-INC LocksAn AUTO-INC lock is a special table-level lock taken by transactions inserting into tables with AUTO_INCREMENT columns. In the simplest case, if one transaction is inserting values i_autoinc_increment
文章浏览阅读266次。2K21能力值 今天,NBA 2K21公布了最新的球员能力值,勒布朗-詹姆斯以98的能力值高居第一。排在第2到第6的依次是:扬尼斯-阿德托昆博(97)、詹姆斯-哈登(96)、凯文-杜兰特(95)、斯蒂芬-库里(95)和达米安-利拉德(95)。 接下来的东契奇的能力值达到94,之后的依次是:吉米-巴特勒(93)、凯里-欧文(90)和杰森-塔图姆(90)。 90..._威少2k21能力值
文章浏览阅读822次。// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'Shader "Unity Shaders Book/Chapter 12/Bloom" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} ..._后处理效果
文章浏览阅读3.7k次。二、 Linux镜像2.1 下载安装镜像http://ftp.sjtu.edu.cn/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-1503-01.iso注:此镜像为示例,具体实际镜像版本以实际要求为准2.2 创建一个空镜像#qemu-img create -f qcow2 [filename] 10G2.3 安装linux镜像#qemu-syst..._ntp镜像
文章浏览阅读1.4w次,点赞4次,收藏42次。docker安装青龙面板可以实现很多我们想要的功能,哈哈, 废话不多说了, 开始说方法。一、购买云服务器现在的云服务器真的是很便宜,各大主机厂商对待新手是如此的慷慨,老用户不如狗,要买就好狠狠地买上它三年。腾讯云:【腾讯云】云产品限时秒杀,爆款1核2G云服务器,首年74元购买完云服务器后安装 CentOS 系统版本的话 CentOS 7.6,当然你要用8.0或8.2更高的版本应该也可以。二、Xshell软件SSH连接终端连接工具很多,我这里用的是“堡塔远程工具”,个人觉得..._宝塔面板和青龙面板哪个好
文章浏览阅读303次。HDU6092 Rikka with Subset-01背包dp-2017多校联盟5 第8题As we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Rikka some math tasks to practice. There is one of them:Yuta has npositive A1−Anand their sum is m. Then for each subset Sof