ONNX是开放式神经网络(Open Neural Network Exchange)的简称,主要由微软和合作伙伴社区创建和维护。很多深度学习训练框架(如Tensorflow, PyTorch, Scikit-learn, MXNet等)的模型都可以导出或转换为标准的ONNX格式,采用ONNX格式作为统一的界面,各种嵌入式平台就可以只需要解析ONNX格式的模型而不用支持多种多样的训练框架,本文主要介绍如何通过代码或JSON文件的形式来构造一个ONNX单算子模型或者整个graph,以及使用ONNX Runtime进行推理得到算子或模型的计算结果。
一. ONNX文件格式
ONNX文件是基于Protobuf进行序列化。了解Protobuf协议的同学应该知道,Protobuf都会有一个*.proto的文件定义协议,ONNX的该协议定义在
https://github.com/onnx/onnx/blob/master/onnx/onnx.proto3
文件中。
从onnx.proto3协议中我们需要重点知道的数据结构如下:
-
ModelProto:模型的定义,包含版本信息,生产者和GraphProto。
-
GraphProto: 包含很多重复的NodeProto, initializer, ValueInfoProto等,这些元素共同构成一个计算图,在GraphProto中,这些元素都是以列表的方式存储,连接关系是通过Node之间的输入输出进行表达的。
-
NodeProto: onnx的计算图是一个有向无环图(DAG),NodeProto定义算子类型,节点的输入输出,还包含属性。
-
ValueInforProto: 定义输入输出这类变量的类型。
-
TensorProto: 序列化的权重数据,包含数据的数据类型,shape等。
-
AttributeProto: 具有名字的属性,可以存储基本的数据类型(int, float, string, vector等)也可以存储onnx定义的数据结构(TENSOR, GRAPH等)。
二. Python API
2.1 搭建ONNX模型
ONNX是用DAG来描述网络结构的,也就是一个网络(Graph)由节点(Node)和边(Tensor)组成,ONNX提供的helper类中有很多API可以用来构建一个ONNX网络模型,比如make_node, make_graph, make_tensor等,下面是一个单个Conv2d的网络构造示例:
import onnx
from onnx import helper
from onnx import TensorProto
import numpy as np
weight = np.random.randn(36)
X = helper.make_tensor_value_info('X', TensorProto.FLOAT, [1, 2, 4, 4])
W = helper.make_tensor('W', TensorProto.FLOAT, [2, 2, 3, 3], weight)
B = helper.make_tensor('B', TensorProto.FLOAT, [2], [1.0, 2.0])
Y = helper.make_tensor_value_info('Y', TensorProto.FLOAT, [1, 2, 2, 2])
node_def = helper.make_node(
'Conv', # node name
['X', 'W', 'B'],
['Y'], # outputs
# attributes
strides=[2,2],
graph_def = helper.make_graph(
[node_def],
'test_conv_mode',
[X], # graph inputs
[Y], # graph outputs
initializer=[W, B],
mode_def = helper.make_model(graph_def, producer_name='onnx-example')
onnx.checker.check_model(mode_def)
onnx.save(mode_def, "./Conv.onnx")
搭建的这个Conv算子模型使用netron可视化如下图所示:
这个示例演示了如何使用helper的make_tensor_value_info, make_mode, make_graph, make_model等方法来搭建一个onnx模型。
相比于PyTorch或其它框架,这些API看起来仍然显得比较繁琐,一般我们也不会用ONNX来搭建一个大型的网络模型,而是通过其它框架转换得到一个ONNX模型。
2.2 Shape Inference
很多时候我们从pytorch, tensorflow或其他框架转换过来的onnx模型中间节点并没有shape信息,如下图所示:
我们经常希望能直接看到网络中某些node的shape信息,shape_inference模块可以推导出所有node的shape信息,这样可视化模型时将会更友好:
import onnx
from onnx import shape_inference
onnx_model = onnx.load("./test_data/mobilenetv2-1.0.onnx")
onnx_model = shape_inference.infer_shapes(onnx_model)
onnx.save(onnx_model, "./test_data/mobilenetv2-1.0_shaped.onnx")
可视化经过shape_inference之后的模型如下图:
2.3 ONNX Optimizer
ONNX的optimizer模块提供部分图优化的功能,例如最常用的:fuse_bn_into_conv,fuse_pad_into_conv等等。
查看onnx支持的优化方法:
from onnx import optimizer
all_passes = optimizer.get_available_passes()
print("Available optimization passes:")
for p in all_passes:
print(p)
print()
应用图优化到onnx模型上进行变换:
passes = ['fuse_bn_into_conv']
# Apply the optimization on the original model
optimized_model = optimizer.optimize(onnx_model, passes)
将mobile net v2应用fuse_bn_into_conv之后,BatchNormalization的参数合并到了Conv的weight和bias参数中,如下图所示:
三. ONNX Runtime计算ONNX模型
onnx本身只是一个协议,定义算子与模型结构等,不涉及具体的计算。onnx runtime是类似JVM一样将ONNX格式的模型运行起来的解释器,包括对模型的解析、图优化、后端运行等。
安装onnx runtime:
python3 -m pip install onnxruntime
import onnx
import onnxruntime as ort
import numpy as np
import cv2
def preprocess(img_data):
mean_vec = np.array([0.485, 0.456, 0.406])
stddev_vec = np.array([0.229, 0.224, 0.225])
norm_img_data = np.zeros(img_data.shape).astype('float32')
for i in range(img_data.shape[0]):
# for each pixel in each channel, divide the value by 255 to get value between [0, 1] and then normalize
norm_img_data[i,:,:] = (img_data[i,:,:]/255 - mean_vec[i]) / stddev_vec[i]
return norm_img_data
img = cv2.imread("test_data/dog.jpeg")
img = cv2.resize(img, (224,224), interpolation=cv2.INTER_AREA)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
input_data = np.transpose(img, (2, 0, 1))
input_data = preprocess(input_data)
input_data = input_data.reshape([1, 3, 224, 224])
sess = ort.InferenceSession("test_data/mobilenetv2-1.0.onnx")
input_name = sess.get_inputs()[0].name
result = sess.run([], {input_name: input_data})
result = np.reshape(result, [1, -1])
index = np.argmax(result)
print("max index:", index)
使用
ONNX
导出和
运行
模型
本教程系列将涵盖txtai的主要用例,这是一个 AI 驱动的语义搜索平台。该系列的每章都有相关代码,可也可以在colab 中使用。
colab 地址
https://dev.to/neuml/tutorial-series-on-txtai-ibg
ONNX
运行
时为机器学习
模型
提供了一种通用的序列化
格式
。
ONNX
支持多种不同的平台/语言,并具有有助于减少推理时间的内置功能。PyTorch 对将 Torch
模型
导出到
ONNX
具有强大的支持。这使得可以将 Hugging Face Transformer 和/或其他下游
模型
直接导出到
ONNX
。
ONNX
开辟了一条使用多种语言和平台进行直接推理的途径。例如,
模型
可以直接在 Android 上
运行
以限制发送到第三方服务的数据。
ONNX
是一个令人兴奋的发展,充满希望。
使用 C++ 的 OpenCV 接口调用
ONNX
格式
的 PyTorch 深度学习
模型
进行预测(Windows, C++, PyTorch,
ONNX
, Visual Studio, OpenCV)
onnx
的基本操作一、
onnx
的配置环境二、获取
onnx
模型
的输出层三、获取中节点输出数据四、
onnx
前向InferenceSession的使用1. 创建实例,源码分析2.
模型
推理run,源码分析五、遇到的一些问题
最近在对
模型
进行量化时候,
模型
格式
转变为
onnx
模型
了,因此需要对
onnx
进行加载、
运行
以及量化(权重/输入/输出)。故,对
onnx
模型
的相关操作进行简单的学习,并写下了这边博客,若有错误请指出,谢谢。
一、
onnx
的配置环境
onnx
的环境主要包含两个包
onnx
和
onnx
runtime,我们
ONNX
提供了一个C ++库,可用于在
ONNX
模型
上执行任意优化,以及越来越多的预打包优化过程列表。
主要动机是在许多
ONNX
后端实现之间共享工作。 并非所有可能的优化都可以直接在
ONNX
图上实现-有些需要附加的特定于后端的信息-但是很多可以,我们的目标是与
ONNX
一起提供所有此类传递,以便可以通过单个函数调用重复使用。
您可能有兴趣调用提供的通行证,或实施新的通行证(或两者都有)。
您可以从PyPI安装
onnx
optimizer:
pip3 install
onnx
optimizer
请注意,如果遇到问题,您可能需要先升级点子:
pip3 install -U pip
如果要从源代码
构建
:
git clone --recursive https://github.com/
onnx
/optimizer
onnx
optimizer
cd
onnx
optim
文章目录
ONNX
介绍
ONNX
与Protobuf
ONNX
数据结构
ONNX
模型
解析
ONNX
模型
构建
及推理
ONNX
模型
修改节点删除:节点修改:
ONNX
介绍
ONNX
是一种针对机器学习所设计的开放式的文件
格式
,用于存储训练好的
模型
。它使得不同的人工智能框架(如Pytorch, MXNet)可以采用相同
格式
存储
模型
数据。简而言之,
ONNX
是一种便于在各个主流深度学习框架中迁移
模型
的中间表达
格式
。
ONNX
与Protobuf
ONNX
采用序列化数据结构协议protobuf来存储
模型
信息。我们可以通过protobuf
为什么要说
ONNX
,
ONNX
又是个什么东西,经常要部署神经网络应用的童鞋们可能会
ONNX
会比较熟悉,我们可能会在某一任务中将Pytorch或者TensorFlow
模型
转化为
ONNX
模型
(
ONNX
模型
一般用于中间部署阶段),然后再拿转化后的
ONNX
模型
进而转化为我们使用不同框架部署需要的类型。
典型的几个线路:
Pytorch ->
ONNX
-> TensorRT
Pytorch ->
ONNX
-> TVM
TF –
onnx
– ncnn
模型
部署入门系列教程持续更新啦,在前两期教程中,我们学习了PyTorch
模型
转
ONNX
模型
的方法,了解了如何在原生算子表达能力不足时,为 PyTorch 或
ONNX
自定义算子。一直以来,我们都是通过 PyTorch 来导出
ONNX
模型
的,基本没有单独探究过
ONNX
模型
的构造知识。
不知道大家会不会有这样一些疑问:
ONNX
模型
在底层是用什么
格式
存储的?如何不依赖深度学习框架,只用
ONNX
的 API 来构造一个
ONNX
模型
?如果没有源代码,只有一个
ONNX
模型
,该如何对这个
模型
.