前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ONNX调研

ONNX调研

原创
作者头像
Johns
修改2022-06-30 10:22:40
1.1K0
修改2022-06-30 10:22:40
举报
文章被收录于专栏:代码工具代码工具

一. ONNX生态简介

ONNX(英语:Open Neural Network Exchange)是一种针对机器学习所设计的开放式的文件格式,用于存储训练好的模型。它使得不同的人工智能框架(如Pytorch、MXNet)可以采用相同格式存储模型数据并交互。 ONNX的规范及代码主要由微软,亚马逊,Facebook和IBM等公司共同开发,以开放源代码的方式托管在Github上。13  目前官方支持加载ONNX模型并进行推理的深度学习框架有: Caffe2, PyTorch, MXNet,ML.NET,TensorRT 和 Microsoft CNTK,并且 TensorFlow 也非官方的支持ONNX。

ONNX Runtime(ORT) 是机器学习模型的加速器,具有多平台支持和与硬件特定库集成的灵活接口。ONNX Runtime 可与来自 PyTorch、Tensorflow/Keras、TFLite、scikit-learn 和其他框架的模型一起使用。主要是通过图优化技术来提高模型的性能, 同时也支持CUDA还有一些线程级别的优化。

image.png
image.png

ONNX Go Live ONNX的性能调优和可视化工具,可以帮助开发快速找出最佳的参数配置组合。

调研目的: 提高当前在线推断模型的性能, 最大限度地降低工程的机器成本。

二. ONNX Runtime(ORT) 使用

1. 使用ORT部署传统机器学习模型

第一步: Install ONNX

环境准备, 使用前请保证好了以下软件, 如果没有可以先自行安装:

  1. python并且版本>=python3.7
  2. docker
  3. 安装onnx
代码语言:shell
复制
pip install onnx
pip install onnxruntime

第二步: Train Model

训练一个传统的Logistic Regression模型,使用sklearn训练,训练集直接选择sklearn自带的鸢尾花数据集

代码语言:python
复制
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from onnx import onnx_ml_pb2

# 数据集内包含 3 类共 150 条记录,每类各 50 个数据,每条记录都有 4 项特征:花萼长度、花萼宽度、花瓣长度、花瓣宽度,可以通过这4个特征预测鸢尾花卉属于(iris-setosa, iris-versicolour, iris-virginica)中的哪一品种
iris = load_iris()
X, y = iris.data, iris.target
# 测试集和训练集打散拆分,这里一定要打散,因为原始数据是相对有序的。
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666, test_size=0.3)

# 训练出一个分类器
clr = LogisticRegression(solver='liblinear', multi_class='auto')
clr.fit(X_train, y_train)

output:

代码语言:text
复制
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
                   intercept_scaling=1, l1_ratio=None, max_iter=100,
                   multi_class='auto', n_jobs=None, penalty='l2',
                   random_state=None, solver='liblinear', tol=0.0001, verbose=0,
                   warm_start=False)

第三步: Convert To ONNX

使用 skl2onnx 把Scikit-learn模型序列化为ONNX格式,并检查模型文件是否生成正常

代码语言:python
复制
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType

initial_type = [('float_input', FloatTensorType([1, 4]))]
onx = convert_sklearn(clr, initial_types=initial_type)
with open("logreg_iris.onnx", "wb") as f:
    f.write(onx.SerializeToString())

import onnx
model = onnx.load('logreg_iris.onnx')
print(model)

output:

代码语言:text
复制
ir_version: 8
producer_name: "skl2onnx"
producer_version: "1.10.0"
domain: "ai.onnx"
model_version: 0
doc_string: ""
graph {
  node {
    input: "float_input"
    output: "label"
    output: "probability_tensor"
    name: "LinearClassifier"
    op_type: "LinearClassifier"
    attribute {
     此处内容过多已省略...
 }

第四步: Direct Predict

使用ONNX Runtime Python API预测该ONNX模型,当前仅使用了测试数据集中的第一条数据。

代码语言:python
复制
import onnxruntime as rt
import numpy

initial_type = [('float_input', FloatTensorType([None, 4]))]
sess = rt.InferenceSession("logreg_iris.onnx")
input_name = sess.get_inputs()[0].name
label_name = sess.get_outputs()[0].name
probability_name = sess.get_outputs()[1].name
pred_onx = sess.run([label_name, probability_name], {input_name: X_test[0:1].astype(numpy.float32)})

# print info
print('input_name: ' + input_name)
print('label_name: ' + label_name)
print('probability_name: ' + probability_name)
print(X_test[0])
print(pred_onx)

output:

代码语言:shell
复制
input_name: float_input
label_name: output_label
probability_name: output_probability
# 这里输入的是一个4维的向量,分别表示花萼长度、花萼宽度、花瓣长度、花瓣宽度
[5.  3.5 1.3 0.3]
#  品种为iris-setosa
[array([0], dtype=int64), [{0: 0.9112248420715332, 1: 0.08868706971406937, 2: 8.812700252747163e-05}]]

第五步: Serve Model using ONNX

以HTTP&GRPC 的方式对外提供服务, 使用mcr.microsoft.com/onnxruntime/server 镜像部署刚才的logreg_iris.onnx模型。

首先刚才生成好的模型文件logreg_iris.onnx拷贝到/data/www/ai/models 目录下, 然后进行/data/www/ai/models执行:

代码语言:shell
复制
cd /data/www/ai/models

docker run \
    -it \
    -v "$PWD:/models" \
    -p 9001:8001 \
    -p 50051:50051 \
    --name ort mcr.microsoft.com/onnxruntime/server \
    --model_path="/models/logreg_iris.onnx"

output:

代码语言:te x
复制
Version: local_build
Commit ID: default

[2021-10-23 13:28:53.657] [ServerApp] [info] Model path: /models/logreg_iris.onnx
[2021-10-23 13:28:53.660] [ServerApp] [info] [ onnxruntime inference_session.cc:545 Initialize]: Initializing session.

此处内容过多已省略...

SaveInitializedTensors]: Done saving initialized tensors
[2021-10-23 13:28:53.663] [ServerApp] [info] [ onnxruntime inference_session.cc:620 Initialize]: Session successfully initialized.
[2021-10-23 13:28:53.664] [ServerApp] [info] GRPC Listening at: 0.0.0.0:50051
[2021-10-23 13:28:53.664] [ServerApp] [info] Listening at: http://0.0.0.0:8001

结果分析: 同时启动了一个GRPC和HTTP服务, 其中Http通过一下路由访问

代码语言:shell
复制
http://<your_ip_address>:<port>/v1/models/<your-model-name>/versions/<your-version>:predict

也就是 http://127.0.0.1:9001/v1/models/default/versions/1:predict 访问。

注意这里的<your-model-name>其实是Commit ID你可以在上面的docker run命令后看到了。

2. 使用ORT部署算法推荐模型

前期准备工作:一个已经训练好的算法推荐模型

第一步: Convert To ONNX

代码语言:txt
复制
# 进入模型目录
cd /Users/guirong/Desktop/ai/models/

# 环境准备
pip install tensorflow-cpu==2.3.0
pip install tf2onnx

# 转换
python -m tf2onnx.convert \
        --saved-model output/1634309909/ \
        --output output/recommend.onnx \
        --opset 10 \
        --tag serve

第二步: Serve Model using ONNX

执行如下命令:

代码语言:txt
复制
docker run \
    -it \
    --rm \
    -v "$PWD:/models" \
    -p 9001:8001 \
    -p 50051:50051 \
    --name ort mcr.microsoft.com/onnxruntime/server \
    --model_path="/models/recommend.onnx"

需要注意⚠️的是当前目录下必须要有一个models目录并且放置了上一步生成的onnx模型文件。

第三步:Client Test

代码语言:python
复制
import onnxruntime as rt
import warnings
import numpy
import json
from array import array
warnings.filterwarnings('ignore')
sess = rt.InferenceSession("recommend.onnx")
input_name0 = sess.get_inputs()[0].name
input_name1 = sess.get_inputs()[1].name
input_name2 = sess.get_inputs()[2].name
input_name3 = sess.get_inputs()[3].name
print(input_name0,input_name1,input_name2,input_name3)

req = {
    'dense_input' : [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 
                                              0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 
                                              0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 
                                              0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 
                                              0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                                              0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                                              0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 
                                              0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                                              0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                                              0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
                                              0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 
                                              0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 
                                              0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 
                                              0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]],
    'seq_input': [[
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]],
     'sparse_ids_input': [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
                         26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
                         50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
                         74, 75, 76, 77, 78]],
    'sparse_wgt_input': [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]],
      
}

import datetime
start_time = datetime.datetime.now()
# 输出有9个值, 我们取最后一个pctv_pcvr
pred_onx = sess.run([sess.get_outputs()[8].name,], req,)

end_time = datetime.datetime.now()
print("耗时: {}秒".format(end_time - start_time))

print(sess.get_outputs()[8].name)
print(pred_onx)

output:

代码语言:txt
复制
tf_op_layer_pcvr_ctr
[array([[0.00265199, 0.04154155]], dtype=float32)]

备注

由于ONNX本身没有直接提供免费的服务化的方案, 故没有再进行ORT性能测试.

如果想使用ONNX模型到线上, 建议使用Triton服务化, 个人本地测试并没有获得非常明显的性能提升(和个人模型有关), 所以没再深度研究和使用ONNX, 其实工具/框架带来的提升其实非常有限, 建议先把注意力放到模型结构优化上去, 如果结构没法优化了再使用工具/框架来进一步优化.

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. ONNX生态简介
  • 二. ONNX Runtime(ORT) 使用
    • 1. 使用ORT部署传统机器学习模型
      • 第一步: Install ONNX
      • 第二步: Train Model
      • 第三步: Convert To ONNX
      • 第四步: Direct Predict
      • 第五步: Serve Model using ONNX
    • 2. 使用ORT部署算法推荐模型
      • 第一步: Convert To ONNX
      • 第二步: Serve Model using ONNX
      • 第三步:Client Test
  • 备注
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档