机器学习之在线模型简单部署,基于ONNX技术

文章目录

  • 一,onnx
  • 二,onnx的作用
    • 1. 提高模型的互操作性:
    • 2. 加速推理引擎的开发:
    • 3. 改进模型优化和调试:
  • 三,onnx的应用场景
  • 四,onnx模型部署涉及的主要函数
    • 1. onnxruntime.InferenceSession:
    • 2. session.`run`:
    • 3. cv2.VideoCapture:
    • 4. Flask:
    • 5. Requests:
    • 6. NumPy:
    • 7. JSON:
  • 五,onnx机器学习部署的简单示例
    • 1. svm进行模型部署
      • 保存模型
      • 加载模型
      • 模型web部署 增加版本控制
    • 2. 增加并发处理

一,onnx

ONNX是一个开放的神经网络交换格式,它允许机器学习框架之间进行模型的互操作性。通过使用ONNX,您可以将训练好的模型从一个深度学习框架(如TensorFlow或PyTorch)转换为另一个框架(如CNTK或Caffe2),使得模型在不同平台设备上能够运行,同时保留其精度和性能。 ONNX已被广泛应用于推理引擎、运行时优化、自动化代码生成等领域,为机器学习社区提供了一种通用的模型表示方式。

很好的一个功能不受开发框架限制,可以实现不同框架之间的跨越.

二,onnx的作用

1. 提高模型的互操作性:

ONNX允许不同深度学习框架之间进行模型的转换和共享。这意味着您可以将从一个框架中训练的模型转换为另一个框架,并在不同平台或设备上运行,同时保留其精度和性能。通过提供通用的模型表示方式,ONNX提高了模型的互操作性,使得深度学习社区更容易共享和利用先进的模型。

2. 加速推理引擎的开发:

ONNX提供了一种标准化的模型表示方式,使得编写支持ONNX格式的推理引擎变得更加容易。此外,ONNX还提供了各种语言平台的API,以便快速开发和部署推理引擎。通过降低推理引擎的开发门槛,ONNX加速了推理引擎的开发,并促进了推理引擎的创新和发展。

3. 改进模型优化和调试:

ONNX允许用户查看模型的结构参数,并提供了可视化工具来帮助用户分析优化模型。这使得模型的调试和优化更加容易和直观,可以帮助用户进一步提高模型的精度和性能。

三,onnx的应用场景

  1. 计算机视觉: ONNX模型被广泛应用于计算机视觉领域,例如目标检测、图像分类、语义分割等。通过使用ONNX,用户可以轻松地将从一个深度学习框架中训练的模型转换为其他框架,并在不同平台上运行。

  2. 自然语言处理: ONNX模型也被广泛应用于自然语言处理领域,例如文本分类、情感分析、自动问答等。通过使用ONNX,用户可以轻松地将不同框架中训练的模型转换为其他框架,并在不同平台上运行。

  3. 数据分析和预测: ONNX模型还被应用于数据分析和预测领域,例如时间序列分析、异常检测、推荐系统等。通过使用ONNX,用户可以将训练好的模型部署到生产环境中,并实时进行预测和分析。

  4. 机器人技术: ONNX模型也被应用于机器人技术领域,例如机器人视觉、控制和导航等。通过使用ONNX,机器人可以快速识别和感知周围环境,从而更加智能地进行操作和交互。

四,onnx模型部署涉及的主要函数

1. onnxruntime.InferenceSession:

这个函数用于创建一个会话,它可以加载ONNX模型并在Web服务器上进行推理操作。

onnxruntime.InferenceSession函数用于创建一个会话对象,该对象可以加载ONNX模型并在Web服务器上进行推理操作。以下是该函数的主要参数详解:

model_path:指定加载的ONNX模型文件路径。
sess_options:配置会话的选项,例如使用CPU或GPU进行推理,设置推理过程中使用的线程数等。
providers:指定硬件加速器提供程序列表,例如OpenVINO、TensorRT等。
custom_ops_library_path:指定包含自定义算子实现的动态库路径。
overwrite_initializers:设置是否允许覆盖模型中的初始值。
debug_options:调试选项,例如启用内存分配跟踪等。
下面是一个简单的示例,展示如何使用InferenceSession函数加载和运行ONNX模型:

import onnxruntime as ort
import numpy as np

# 加载ONNX模型
session = ort.InferenceSession("model.onnx")

# 准备输入数据
input_data = np.random.rand(1, 3, 224, 224).astype(np.float32)

# 运行模型
outputs = session.run(None, {"input": input_data})

在此示例中,我们首先使用InferenceSession函数加载了名为“model.onnx”的ONNX模型。然后,我们准备了一个随机生成的输入数据,并将其传递给模型进行推理。最后,我们通过调用session.run函数来运行模型,并将输出结果存储在outputs变量中。

2. session.run

这里有对tensorflow中的run()函数详细介绍
这两者在使用上类似。
这个函数用于运行ONNX模型,它可以输入数据并返回模型的输出结果。

使用sess.run()方法运行模型并进行推理。下面是该方法的详细参数介绍:

def run(self,
        output_names: Optional[List[str]] = None,
        input_feed: Optional[Dict[str, np.ndarray]] = None,
        run_options: Optional[RunOptions] = None) -> Optional[List[np.ndarray]]:

output_names: 一个字符串列表,用于指定要返回的输出张量的名称。如果不指定,则返回所有输出张量。

input_feed: 一个字典,用于指定输入张量的值。字典的键是输入张量的名称,而值是一个包含输入数据的NumPy数组。如果没有指定某个输入张量的值,则将使用默认值。

run_options: 运行时选项,用于控制如何执行模型和处理结果。例如,您可以设置run_options.log_severity_level参数来设置日志级别。

这些参数中,最常用的就是output_namesinput_feedoutput_names允许您指定要提取哪些输出张量。而input_feed允许您指定输入张量的值,以便模型能够对其进行推理。

需要注意的是,input_feed中指定的NumPy数组必须与在转换为ONNX格式时定义的输入类型匹配。否则,将会抛出一条错误消息。

另外,sess.run()方法将返回一个包含输出NumPy数组的列表。每个NumPy数组都是一个张量,其形状和类型在转换为ONNX格式时定义的输出类型相匹配。如果只有一个输出张量,则返回该张量的NumPy数组;否则,将返回包含所有输出张量的列表。

3. cv2.VideoCapture:

如果您想要将图像或视频流输入到ONNX模型中进行处理,就需要使用OpenCV库中的这个函数。

4. Flask:

这个Python Web框架可以帮助您轻松地构建和托管Web服务器,并提供网页接口来调用ONNX模型。

5. Requests:

这个库用于向Web服务器发送HTTP请求,并获取响应。如果您要使用Web服务API来调用ONNX模型,则需要使用此库。

6. NumPy:

这个库用于处理各种类型的数值数据,在ONNX模型推理过程中经常使用。

7. JSON:

您可以使用JSON格式来编写参数或配置文件,并将其用作与Web服务器交互的输入或输出格式。

五,onnx机器学习部署的简单示例

1. svm进行模型部署

保存模型

from sklearn import svm,datasets

import onnxmltools
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
import onnxruntime as rt
import numpy as np


iris = datasets.load_iris()
x = iris.data[:,:2]
y = iris.target

print('x:',x)
print('y:',y)

model = svm.SVC(kernel='linear',C=1,decision_function_shape='ovr')

model.fit(x,y)


initial_type = [('float_input',FloatTensorType([None,2]))]
onx = convert_sklearn(model,initial_types=initial_type)
onnx_path = 'model.onnx'
onnxmltools.utils.save_model(onx, onnx_path)


加载模型

import onnxruntime as rt
import numpy as np
from flask import Flask, jsonify, request

sess = rt.InferenceSession('model.onnx')

# input_name = sess.get_inputs()
# print('input_name:',input_name[0].name)

input_name = sess.get_inputs()[0].name
# out_name = sess.get_outputs()[1].name
# print('out_name:',out_name)

out_name = sess.get_outputs()[0].name

data =  [[6.2,3.4]]

data = np.array(data,dtype=np.float32)
test = sess.run([out_name],{input_name:data})[0]
print(test)

模型web部署 增加版本控制

import joblib
import onnxmltools
import onnxruntime as rt
import numpy as np
from sklearn import svm, datasets
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
from datetime import datetime

# 将模型保存到本地文件中
def save_model(model, model_path, meta_data):
    joblib.dump(model, model_path)
    meta_path = model_path + '.meta'
    with open(meta_path, 'w') as f:
        f.write(str(meta_data))

# 从本地文件中加载模型
def load_model(model_path):
    model = joblib.load(model_path)
    meta_path = model_path + '.meta'
    with open(meta_path, 'r') as f:
        meta_data = eval(f.read())
    return model, meta_data

# 创建和训练模型
iris = datasets.load_iris()
X = iris.data[:, :2]
y = iris.target
model = svm.SVC(kernel='linear', C=1, decision_function_shape='ovr').fit(X, y)

# 定义输入张量类型
initial_type = [('float_input', FloatTensorType([None, 2]))]

# 使用convert_sklearn函数将Scikit-learn模型转换为ONNX格式
onx = convert_sklearn(model, initial_types=initial_type)

# 将ONNX模型保存到本地文件中,并记录元数据
model_name = 'iris_svc'
version = '1.0'
model_path = f'{model_name}_{version}.pkl'
onnx_path = f'{model_name}_{version}.onnx'
save_model(model, model_path, {'created_at': datetime.now(), 'version': version})
onnxmltools.utils.save_model(onx, onnx_path)

# 加载模型和元数据
loaded_model, meta_data = load_model(model_path)
print('Loaded Model:', loaded_model)
print('Meta Data:', meta_data)

# 加载ONNX模型并执行推理
sess = rt.InferenceSession(onnx_path)
input_name = sess.get_inputs()[0].name
output_name = sess.get_outputs()[0].name

def predict(data):
    data = np.array(data, dtype=np.float32)
    return sess.run([output_name], {input_name: data})[0]

# 在Web应用程序中使用模型进行推理
from flask import Flask, jsonify, request

app = Flask(__name__)

@app.route('/predict', methods=['POST'])
def make_prediction():
    data = request.get_json()
    prediction = predict(data['features'])
    return jsonify({'prediction': prediction.tolist()})

if __name__ == '__main__':
    app.run(debug=True)

2. 增加并发处理

import joblib
import onnxmltools
import onnxruntime as rt
import numpy as np
from sklearn import svm, datasets
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
from datetime import datetime
import threading

# 创建和训练模型
iris = datasets.load_iris()
X = iris.data[:, :2]
y = iris.target
model = svm.SVC(kernel='linear', C=1, decision_function_shape='ovr').fit(X, y)

# 定义输入张量类型
initial_type = [('float_input', FloatTensorType([None, 2]))]

# 使用convert_sklearn函数将Scikit-learn模型转换为ONNX格式
onx = convert_sklearn(model, initial_types=initial_type)

# 将ONNX模型保存到本地文件中,并记录元数据
model_name = 'iris_svc'
version = '1.0'
model_path = f'{model_name}_{version}.pkl'
onnx_path = f'{model_name}_{version}.onnx'
joblib.dump(model, model_path)
onnxmltools.utils.save_model(onx, onnx_path)

# 加载模型和元数据
loaded_model = joblib.load(model_path)
print('Loaded Model:', loaded_model)

# 创建TLS以缓存ONNX Runtime会话对象
tls = threading.local()


# 加载ONNX模型并执行推理
def predict(data):
    # 从TLS中获取或创建ONNX Runtime会话对象
    if not hasattr(tls, 'sess'):
        tls.sess = rt.InferenceSession(onnx_path)
    sess = tls.sess

    # 获取模型输入和输出名称
    input_name = sess.get_inputs()[0].name
    output_name = sess.get_outputs()[0].name

    # 执行模型推理
    data = np.array(data, dtype=np.float32)
    return sess.run([output_name], {input_name: data})[0]


# 在Web应用程序中使用模型进行推理
from flask import Flask, jsonify, request

app = Flask(__name__)


@app.route('/predict', methods=['POST'])
def make_prediction():
    data = request.get_json()
    prediction = predict(data['features'])
    return jsonify({'prediction': prediction.tolist()})


if __name__ == '__main__':
    # 启动Flask应用程序,并配置多线程处理请求
    app.run(threaded=True, debug=True)

'''
如果您的Web应用程序需要支持并发请求,那么您需要确保您的代码是
线程安全的,并且可以处理多个并发请求。以下是一些可帮助您实现
这一目标的提示:


在启动应用程序时,使用threaded=True参数将Flask应用程序配置为使用多个线程处理请求,例如:app.run(threaded=True)。


如果您使用的是基于进程的服务器(如Gunicorn或uWSGI),则需要正确地调整工作进程数和线程数,以便最大化系统资源的利用率,并防止超过系统的限制。
这通常需要进行一些性能测试和基准测试,以找到最佳的设置。


对于模型推理,在每个请求中创建一个新的ONNX Runtime会话对象可能会影响性能,因为每个对象都需要占用一定的系统资源。为了避免这种情况,您可以使
用以下两种方法之一:


1. 使用线程本地存储TLS(Thread-Local Storage)来缓存已经创建的ONNX Runtime会话对象,以避免在每个请求中创建新的会话对象。这样可以提高性能,并减少内存消耗。

2. 使用基于进程的服务器,则可以使用进程池来缓存ONNX Runtime会话对象。这样,每个进程只需要创建一次会话对象,并在需要时重复使用该对象。这
种方法可以大大减少内存消耗,但也可能会对性能产生一些影响。

对于模型推理,您还可以考虑使用异步编程模型来处理并发请求。例如,可以使用asyncio库和aiohttp库来创建异步Web应用程序,并在每个请求中使用异步ONNX Runtime客户端来执行模型推理。

可以在上面的基础上添加并发控制。该代码使用Python的threading.local()方法创建了一个TLS(Thread-Local Storage),以缓存ONNX Runtime会话对
象,并避免在每个请求中创建新的会话对象。同时,该代码还通过设置Flask应用程序的线程数来配置多线程处理请求。

在这个例子中,我们创建了一个名为tls的TLS(Thread-Local Storage)对象,以缓存ONNX Runtime会话对象。

这样可以避免每个请求都创建新的会话对象,从而提高性能和可扩展性。在predict()函数中,
我们首先检查TLS对象是否包含ONNX Runtime会话对象。如果没有,则创建一个新的会话对象,
并将其绑定到TLS对象上。然后,我们使用该会话对象执行模型推理。

最后,我们在启动Flask应用程序时,使用threaded=True参数来配置多线程处理请求。
这样可以利用系统的多核CPU资源,并提高应用程序的并发性能。

需要注意的是,虽然TLS可以避免在每个请求中创建新的会话对象,但它仍然可能导致竞态条件或死锁等问题。
'''

你可能感兴趣的:(机器学习,机器学习,人工智能,深度学习)