ONNX 简介
开放神经网络交换,Open Neural Network Exchange,是一套表示 网络模型 的开放格式,由微软和FaceBook在2017年推出;
通过几年的快速发展,大有一统整个 AI 模型(ml、dl)的交换标准;
ONNX 定义了一组与 环境和平台 无关的标准格式,使得 AI 模型可以在 跨平台、跨框架 的情况下使用;
目前,ONNX主要关注在模型预测方面(inferring),使用不同框架训练的模型,转化为ONNX格式后,可以很容易的部署在兼容ONNX的运行环境中;
简历理解,ONNX 就是一套 基于 训练和推理 间的 标准协议,起到 桥梁 作用,不同平台、不同框架之间 通过 标准协议 进行 转换 和 交互;
ONNX 标准介绍
ONNX规范由以下几个部分组成:
- 一个可扩展的计算图模型:定义了通用的计算图中间表示法(Intermediate Representation)。
- 内置操作符集:
ai.onnx
和ai.onnx.ml
,ai.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 架构进阶课程之介绍篇