使用ONNX部署深度学习和传统机器学习模型

news/2024/5/9 23:34:21

ONNX简介

开放神经网络交换ONNX(Open Neural Network Exchange)是一套表示深度神经网络模型的开放格式,由微软和Facebook于2017推出,然后迅速得到了各大厂商和框架的支持。通过短短几年的发展,已经成为表示深度学习模型的实际标准,并且通过ONNX-ML,可以支持传统非神经网络机器学习模型,大有一统整个AI模型交换标准。

ONNX定义了一组与环境和平台无关的标准格式,为AI模型的互操作性提供了基础,使AI模型可以在不同框架和环境下交互使用。硬件和软件厂商可以基于ONNX标准优化模型性能,让所有兼容ONNX标准的框架受益。目前,ONNX主要关注在模型预测方面(inferring),使用不同框架训练的模型,转化为ONNX格式后,可以很容易的部署在兼容ONNX的运行环境中。

ONNX标准介绍

ONNX规范由以下几个部分组成:

  • 一个可扩展的计算图模型:定义了通用的计算图中间表示法(Intermediate Representation)。
  • 内置操作符集:ai.onnxai.onnx.mlai.onnx是默认的操作符集,主要针对神经网络模型,ai.onnx.ml主要适用于传统非神经网络机器学习模型。
  • 标准数据类型。包括张量(tensors)、序列(sequences)和映射(maps)。

目前,ONNX规范有两个官方变体,主要区别在与支持的类型和默认的操作符集。ONNX神经网络变体只使用张量作为输入和输出;而作为支持传统机器学习模型的ONNX-ML,还可以识别序列和映射,ONNX-ML为支持非神经网络算法扩展了ONNX操作符集。

ONNX使用protobuf序列化AI模型,顶层是一个模型(Model)结构,主要由关联的元数据和一个图(Graph)组成;图由元数据、模型参数、输入输出、和计算节点(Node)序列组成,这些节点构成了一个计算无环图,每一个计算节点代表了一次操作符的调用,主要由节点名称、操作符、输入列表、输出列表和属性列表组成,属性列表主要记录了一些运行时常量,比如模型训练时生成的系数值。

为了更直观的了解ONNX格式内容,下面,我们训练一个简单的Logistic Regression模型,然后导出ONNX。仍然使用常用的分类数据集iris

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegressioniris = load_iris()
X, y = iris.data, iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y)clr = LogisticRegression()
clr.fit(X_train, y_train)

使用skl2onnx把Scikit-learn模型序列化为ONNX格式:

from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorTypeinitial_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())

使用ONNX Python API查看和验证模型:

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

输出模型信息如下:

我们可以看到顶层字段记录了一些模型的元数据信息,代表的含义都比较直观,字段详细解释可以参考文档 Open Neural Network Exchange - ONNX。opset_import记录了该模型引入的操作符集。空的domain操作符集表示引入ONNX默认的操作符集ai.onnxai.onnx.ml代表支持传统非神经网络模型操作符集,比如以上模型中的LinearClassifierNormalizerZipMap。图(graph)中定义了以下元素:

  • 四个计算节点(node)。
  • 一个输入变量float_input,类型为1*4的张量,elem_type是一个DataType枚举型变量,1代表FLOAT。
  • 两个输出变量output_labeloutput_probabilityoutput_label类型为维数为1的INT64(elem_type: 7)张量,代表预测目标分类; output_probability类型是映射的序列,映射的键是INT64(key_type: 7),值为维数为1的FLOAT,代表每一个目标分类的概率。

可以使用netron,图像化显示ONNX模型的计算拓扑图,以上模型如下图:

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

import onnxruntime as rt
import numpy
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].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)
ir_version: 5
producer_name: "skl2onnx"
producer_version: "1.5.1"
domain: "ai.onnx"
model_version: 0
doc_string: ""
graph {node {input: "float_input"output: "label"output: "probability_tensor"name: "LinearClassifier"op_type: "LinearClassifier"attribute {name: "classlabels_ints"ints: 0ints: 1ints: 2type: INTS}attribute {name: "coefficients"floats: 0.375753253698349floats: 1.3907358646392822floats: -2.127762794494629floats: -0.9207873344421387floats: 0.47902926802635193floats: -1.5524250268936157floats: 0.46959221363067627floats: -1.2708674669265747floats: -1.5656673908233643floats: -1.256540060043335floats: 2.18996000289917floats: 2.2694246768951416type: FLOATS}attribute {name: "intercepts"floats: 0.24828049540519714floats: 0.8415762782096863floats: -1.0461325645446777type: FLOATS}attribute {name: "multi_class"i: 1type: INT}attribute {name: "post_transform"s: "LOGISTIC"type: STRING}domain: "ai.onnx.ml"}node {input: "probability_tensor"output: "probabilities"name: "Normalizer"op_type: "Normalizer"attribute {name: "norm"s: "L1"type: STRING}domain: "ai.onnx.ml"}node {input: "label"output: "output_label"name: "Cast"op_type: "Cast"attribute {name: "to"i: 7type: INT}domain: ""}node {input: "probabilities"output: "output_probability"name: "ZipMap"op_type: "ZipMap"attribute {name: "classlabels_int64s"ints: 0ints: 1ints: 2type: INTS}domain: "ai.onnx.ml"}name: "deedadd605a34d41ac95746c4feeec1f"input {name: "float_input"type {tensor_type {elem_type: 1shape {dim {dim_value: 1}dim {dim_value: 4}}}}}output {name: "output_label"type {tensor_type {elem_type: 7shape {dim {dim_value: 1}}}}}output {name: "output_probability"type {sequence_type {elem_type {map_type {key_type: 7value_type {tensor_type {elem_type: 1}}}}}}}
}
opset_import {domain: ""version: 9
}
opset_import {domain: "ai.onnx.ml"version: 1
}

我们可以看到顶层字段记录了一些模型的元数据信息,代表的含义都比较直观,字段详细解释可以参考文档 Open Neural Network Exchange - ONNX。opset_import记录了该模型引入的操作符集。空的domain操作符集表示引入ONNX默认的操作符集ai.onnxai.onnx.ml代表支持传统非神经网络模型操作符集,比如以上模型中的LinearClassifierNormalizerZipMap。图(graph)中定义了以下元素:

  • 四个计算节点(node)。
  • 一个输入变量float_input,类型为1*4的张量,elem_type是一个DataType枚举型变量,1代表FLOAT。
  • 两个输出变量output_labeloutput_probabilityoutput_label类型为维数为1的INT64(elem_type: 7)张量,代表预测目标分类; output_probability类型是映射的序列,映射的键是INT64(key_type: 7),值为维数为1的FLOAT,代表每一个目标分类的概率。

可以使用netron,图像化显示ONNX模型的计算拓扑图,以上模型如下图:

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

import onnxruntime as rt
import numpy
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].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)

打印的模型信息和预测值如下:

input_name: float_input
label_name: output_label
probability_name: output_probability
[5.5 2.6 4.4 1.2]
[array([1], dtype=int64), [{0: 0.012208569794893265, 1: 0.5704444646835327, 2: 0.4173469841480255}]]

完整的程序,可以参考以下Notebook:onnx.ipynb

ONNX与PMML

ONNX和PMML都是与平台和环境无关的模型表示标准,可以让模型部署脱离模型训练环境,简化了部署流程,加速模型快速上线到生产环境中。这两个标准都得到了各大厂商和框架的支持,具有广泛的应用。

  • PMML是一个比较成熟的标准,在ONNX诞生之前,可以说是模型表示的实际标准,对传统数据挖掘模型有丰富的支持,最新 PMML4.4 可以支持多达19种模型类型。但是,目前PMML缺乏对深度学习模型的支持,下一版本5.0有可能会添加对深度神经网络的支持,但是因为PMML是基于老式的XML格式,使用文本格式来存储深度神经网络模型结构和参数会带来模型大小和性能的问题,目前该问题还没有一个完美的解决方案。关于PMML的详细介绍,可以参考文章《使用PMML部署机器学习模型》。
  • ONNX作为一个新的标准,刚开始主要提供对深度神经网络模型的支持,解决模型在不同框架下互操作和交换的问题。目前通过ONNX-ML,ONNX已经可以支持传统非神经网络机器学习模型,但是目前模型类型还不够丰富。ONNX使用protobuf二进制格式来序列化模型,可以提供更好的传输性能。

ONNX和PMML这两种格式都有成熟的开源类库和框架支持,PMML有JPMML,PMML4S,PyPMML等。ONNX有微软的ONNX runtime,NVIDIA TensorRT等。用户可以根据自己的实际情况选择合适的跨平台格式来部署AI模型。

DaaS简介

DaaS(Deployment-as-a-Service)是AutoDeployAI公司推出的AI模型自动部署系统,支持多种模型类型的上线部署,以下我们介绍如何在DaaS中使用ONNX格式来部署传统机器学习模型和深度神经网络学习模型,DaaS使用ONNX Runtime作为ONNX模型的执行引擎,ONNX Runtime是微软开源的ONNX预测类库,提供高性能预测服务功能。首先,登陆DaaS系统后,创建一个新的工程ONNX,下面的操作都在该工程下进行。关于DaaS的详细信息,可以参考文章《自动部署PMML模型生成REST API》。

使用ONNX部署传统机器学习模型

1. 导入模型。选择上面训练的Logistic Regression模型logreg_iris.onnx

导入成功后,页面转到模型主页面。可以看到模型有一个输入字段float_input,类型是tensor(float),维数(1,4)。两个输出字段:output_labeloutput_probability

2. 测试模型。点击标签页测试,输入预测数据[[5.5, 2.6, 4.4, 1.2]],然后点击提交命令,输出页面显示预测测试结果:

3. 创建默认实时预测Web服务。点击标签页部署,然后点击添加服务命令,输入服务名称,其他使用默认值:

4. 测试Web服务。服务创建成功后,页面转到服务部署主页,当服务副本状态为运行中时,代表Web服务已经成功上线,可以接受外部请求。有两种方式测试该服务:

  • 在DaaS系统中通过测试页面。点击标签页测试,输入JSON格式的请求正文,点击提交命令:

  • 通过任意的RSET客户端,使用标准的REST API来测试。这里我们使用curl命令行程序来调用Web服务,点击生成代码命令,弹出显示使用curl命令调用REST API的对话框:


复制该curl命令,打开shell页面,执行命令:

使用ONNX部署深度神经网络模型

我们尝试部署 ONNX Model Zoo 中已经训练好的模型,这里我们选择 MNIST-手写数字识别CNN模型,下载基于ONNX1.3的模型最新版本:mnist.tar.gz。

1. 导入模型。选择已下载模型mnist.tar.gz

导入成功后,页面转到模型主页面。可以看到模型有一个输入字段Input3,类型是tensor(float),维数(1,1,28,28)。一个输出字段:Plus214_Output_0,类型同样是tensor(float),维数(1,10)

2. 测试模型。点击标签页测试,然后点击JSON命令,DaaS系统会自动创建符合输入数据格式的随机数据,以方便测试。点击提交命令,输出页面显示预测测试结果:

3. 创建自定义实施预测脚本。为了能支持输入图像,并且直接输出预测值,我们需要创建自定义预测脚本。点击标签页实时预测,然后点击生成自定义实时预测脚本命令,

脚本生成后,点击命令作为API测试,进入脚本测试页面,我们可以自由添加自定义预处理和后处理功能。添加以下函数预处理图像:

def rgb2gray(rgb):"""Convert the input image into grayscale"""import numpy as npreturn np.dot(rgb[...,:3], [0.299, 0.587, 0.114])def resize_img(img_to_resize):"""Resize image to MNIST model input dimensions"""import cv2r_img = cv2.resize(img_to_resize, dsize=(28, 28), interpolation=cv2.INTER_AREA)r_img.resize((1, 1, 28, 28))return r_imgdef preprocess_image(img_to_preprocess):"""Resize input images and convert them to grayscale."""if img_to_preprocess.shape == (28, 28):img_to_preprocess.resize((1, 1, 28, 28))return img_to_preprocessgrayscale = rgb2gray(img_to_preprocess)processed_img = resize_img(grayscale)return processed_img

在已有preprocess_files函数中调用preprocess_image,代码如下:

import matplotlib.image as mpimg
for key, file in files.items():img = mpimg.imread(file)record[key] = preprocess_image(img)

在已有postprocess函数中添加如下代码后处理预测结果以获取最终的预测值:

def postprocess(result):"""postprocess the predicted results"""import numpy as npreturn [int(np.argmax(np.array(result).squeeze(), axis=0))]

点击命令保存,然后在请求页面中输入函数名为predict,选择请求正文基于表单,输入表单名称为模型唯一的输入字段名Input3,类型选择文件,点击上传,选择测试图像2.png,最后点击提交命令,测试该脚本是否按照我们的期望工作:

4. 创建正式部署Web服务。当脚本测试成功后,点击部署标签页,然后点击添加网络服务命令,输入服务名称,其他使用默认值:

5. 测试Web服务。服务创建成功后,页面转到服务部署主页,当服务副本状态为运行中时,代表Web服务已经成功上线,可以接受外部请求。有两种方式测试该服务:

  • 在DaaS系统中通过测试页面。点击标签页测试,选择请求正文基于表单,选择输入测试图像5.jpg,点击提交命令:

  • 通过任意的RSET客户端,使用标准的REST API来测试。这里我们使用curl命令行程序来调用Web服务,点击生成代码命令,弹出显示使用curl命令调用REST API的对话框:


复制该curl命令,打开shell页面,切换到图像目录下,执行命令:

总结

本文中我们介绍了ONNX这种跨平台AI模型表示标准,以及与PMML的区别,最后演示了如何在DaaS系统中通过ONNX部署传统机器学习模型和深度神经网络模型,可以看到ONNX让模型部署脱离了模型训练环境,极大简化了整个部署流程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.cpky.cn/p/10596.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

macOS Monterey 12.7.4 (21H1123) Boot ISO 原版可引导镜像下载

macOS Monterey 12.7.4 (21H1123) Boot ISO 原版可引导镜像下载 3 月 8 日凌晨,macOS Sonoma 14.4 发布,同时带来了 macOS Ventru 13.6.5 和 macOS Monterey 12.7.4 安全更新。 本站下载的 macOS 软件包,既可以拖拽到 Applications&#xf…

Tomcat Web 开发项目构建教程

1下载Tomcat安装包,下载链接:Apache Tomcat - Welcome!,我电脑环境为JDK8,所以下载Tomcat9.0 2、下载完压缩包后,解压到指定位置 3.在intelij中新建一个项目 4.选中创建的项目,双击shift,输入add frame...然…

【c++】leetcode34 在排序数组中查找元素的第一个和最后一个位置

1. 题目 需求 2. 代码实现 /*** Definition for singly-linked list.*/ #include<iostream> #include<vector> using namespace std;class Solution { public:vector<int> searchRange(vector<int>& nums, int target) {return vector<int&g…

java Day7 正则表达式|异常

文章目录 1、正则表达式1.1 常用1.2 字符串匹配&#xff0c;提取&#xff0c;分割 2、异常2.1 运行时异常2.2 编译时异常2.3 自定义异常2.3.1 自定义编译时异常2.3.2 自定义运行时异常 1、正则表达式 就是由一些特定的字符组成&#xff0c;完成一个特定的规则 可以用来校验数据…

DataFunSummit 2023因果推断在线峰会:解码数据与因果,引领智能决策新篇章(附大会核心PPT下载)

在数据驱动的时代&#xff0c;因果推断作为数据科学领域的重要分支&#xff0c;正日益受到业界的广泛关注。DataFunSummit 2023年因果推断在线峰会&#xff0c;汇聚了国内外顶尖的因果推断领域专家、学者及业界精英&#xff0c;共同探讨因果推断的最新进展、应用与挑战。本文将…

Lambda表达式 --Java学习笔记

Lambda表达式是jdk8开始新增的一种语法形式作用&#xff1a;用于简化匿名内部类的代码写法格式&#xff1a; 注意&#xff1a;Lambda表达式只能简化函数式接口的匿名内部类 函数式接口指有且仅有一个抽象方法的接口 将来我们见到的大部分函数式接口&#xff0c;上面都可能会…