ONNX 简介

开放神经网络交换,Open Neural Network Exchange,是一套表示 网络模型 的开放格式,由微软和FaceBook在2017年推出;

通过几年的快速发展,大有一统整个 AI 模型(ml、dl)的交换标准;

 

ONNX 定义了一组与 环境和平台 无关的标准格式,使得 AI 模型可以在 跨平台、跨框架 的情况下使用;

目前,ONNX主要关注在模型预测方面(inferring),使用不同框架训练的模型,转化为ONNX格式后,可以很容易的部署在兼容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  安装

1.要选择 和 python 版本对应的 版本

2.如果 pip 不行,可从官网下载 安装包,https://www.cnpython.com/pypi/onnx#

pip install onnx-1.8.0-cp36-cp36m-win_amd64.whl

 

ONNX 使用

ml2onnx

机器学习的  模型训练、转换、推理 代码

import onnx
import numpy
import onnxruntime as rt
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType


iris = load_iris()
X, y = iris.data, iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y)
print(X_train.shape)
clr = LogisticRegression()
clr.fit(X_train, y_train)

# 使用skl2onnx把Scikit-learn模型序列化为ONNX格式
initial_type = [('float_input', FloatTensorType([None, 4]))]    # 定义了模型输入
onx = convert_sklearn(clr, initial_types=initial_type)
with open("logreg_iris.onnx", "wb") as f:
    f.write(onx.SerializeToString())

# 使用ONNX Python API查看和验证模型
model = onnx.load('logreg_iris.onnx')
print(model)

# 使用ONNX Runtime Python API预测该ONNX模型
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.astype(numpy.float32)})
print(pred_onx)

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

 

计算图

计算图 定义了 网络的计算过程,为了加速计算,一些框架会使用  先编译、后执行 的静态图来描述网络;

静态图的缺点是难以描述 控制流 如  if-else 或者 for 循环 的计算过程,比如 for 循环执行 n次 a=a+b,不同的 n,对应不同的计算图

 

 

故 ONNX 并不支持 所有计算图

 

dl2onnx

模型及参数转换成 onnx 

def init_torch_model(): 
    torch_model = SuperResolutionNet(upscale_factor=3) 
 
    state_dict = torch.load('srcnn.pth')['state_dict'] 
 
    # Adapt the checkpoint 
    for old_key in list(state_dict.keys()): 
        new_key = '.'.join(old_key.split('.')[1:]) 
        state_dict[new_key] = state_dict.pop(old_key) 
 
    torch_model.load_state_dict(state_dict) 
    torch_model.eval() 
    return torch_model 
 
model = init_torch_model() 
...

# Write in file as onnx file
x = torch.randn(1, 3, 256, 256) 
 
with torch.no_grad(): 
    torch.onnx.export( 
        model,              # 模型
        x,                  # 定义输入格式
        "srcnn.onnx", 
        opset_version=11,   # onnx算子集的版本
        input_names=['input'], 
        output_names=['output'])

参数解释

为什么需要为模型提供一组输入呢?这就涉及到 ONNX 转换的原理了。从 PyTorch 的模型到 ONNX 的模型,本质上是一种语言上的翻译。直觉上的想法是像编译器一样彻底解析原模型的代码,记录所有控制流。
但前面也讲到,我们通常只用 ONNX 记录不考虑控制流的静态图。
因此,PyTorch 提供了一种叫做追踪(trace)的模型转换方法:给定一组输入,再实际执行一遍模型,即把这组输入对应的计算图记录下来,保存为 ONNX 格式。export 函数用的就是追踪导出方法,需要给任意一组输入,让模型跑起来。
我们的测试图片是三通道,256x256大小的,这里也构造一个同样形状的随机张量。 opset_version 表示 ONNX 算子集的版本。深度学习的发展会不断诞生新算子,为了支持这些新增的算子,ONNX会经常发布新的算子集,
目前已经更新15个版本。我们令 opset_version
= 11,即使用第11个 ONNX 算子集,
是因为 SRCNN 中的 bicubic (双三次插值)在 opset11 中才得到支持。
剩下的两个参数 input_names, output_names 是输入、输出 tensor 的名称,我们稍后会用到这些名称。

 

onnx.load 函数用于读取一个 ONNX 模型。onnx.checker.check_model 用于检查模型格式是否正确,如果有错误的话该函数会直接报错。我们的模型是正确的,控制台中应该会打印出"Model correct"

#check model
import onnx 
 
onnx_model = onnx.load("srcnn.onnx") 
try: 
    onnx.checker.check_model(onnx_model) 
except Exception: 
    print("Model incorrect") 
else: 
    print("Model correct")

 

推理引擎 -ONNX Runtime

ONNX Runtime 是由微软维护的一个跨平台机器学习推理加速器,也就是我们前面提到的”推理引擎“。

ONNX Runtime 是直接对接 ONNX 的,即 ONNX Runtime 可以直接读取并运行 .onnx 文件, 而不需要再把 .onnx 格式的文件转换成其他格式的文件。

也就是说,对于 PyTorch - ONNX - ONNX Runtime 这条部署流水线,只要在目标设备中得到 .onnx 文件,并在 ONNX Runtime 上运行模型,模型部署就算大功告成了。

# test onnx model
import onnxruntime 
 
ort_session = onnxruntime.InferenceSession("srcnn.onnx", providers=['CPUExecutionProvider']) #notice
ort_inputs = {'input': input_img} 
ort_output = ort_session.run(['output'], ort_inputs)[0] 
 
ort_output = np.squeeze(ort_output, 0) 
ort_output = np.clip(ort_output, 0, 255) 
ort_output = np.transpose(ort_output, [1, 2, 0]).astype(np.uint8) 
cv2.imwrite("img/face_ort.png", ort_output)

 

以上只是模型 部署的 基本流程,在实际部署过程中,会遇到各种各样的困难与挑战

 

 

 

参考资料:

https://onnx.ai/ 

http://onnx.ai/sklearn-onnx/

https://zhuanlan.zhihu.com/p/477743341  模型部署入门教程(一):模型部署简介

https://zhuanlan.zhihu.com/p/86867138   使用ONNX部署深度学习和传统机器学习模型

https://www.bilibili.com/video/BV1mt4y1x7hP?spm_id_from=333.337.search-card.all.click&vd_source=f0fc90583fffcc40abb645ed9d20da32  b站 对 上面文章的讲解

https://github.com/hisrg/Onnx-python  实战代码,上面 b 站讲解员的代码

https://www.bilibili.com/video/BV1TY411T7vC?spm_id_from=333.337.search-card.all.click&vd_source=f0fc90583fffcc40abb645ed9d20da32  ONNX Runtime 架构进阶课程之介绍篇