onnx作为一个通用格式,很少有中文教程,因此开一篇文章对onnx 1.16文档进行翻译与进一步解释,
onnx 1.16官方文档:https://onnx.ai/onnx/intro/index.html](https://onnx.ai/onnx/intro/index.html),
如果觉得有收获,麻烦点赞收藏关注,目前仅在CSDN发布,本博客会分为多个章节,目前尚在连载中,详见专栏链接:
https://blog.csdn.net/qq_33345365/category_12581965.html
开始编辑时间:2024/2/21;最后编辑时间:2024/2/21
这是本教程的第三篇,其余内容见上述专栏链接。
ONNX with Python
本教程的第一篇:介绍了ONNX的基本概念。
在本教程的第二篇,介绍了ONNX关于Python的API,具体涉及一个简单的线性回归例子和序列化,在本篇将继续对更多的内容进行介绍。包括以下三个部分:
- 初始化器Initializer
- 属性Attributes
- 算子集和元数据Opset和Metadata
初始化器,默认值Initializer, default value
之前模型假设线性回归的系数(常数A,C)也是模型的输入,这并不方便。它们应该作为模型本身的一部分,以常量或初服务器托管网始化器的方式存在,以遵循 ONNX 的语义。下一个例子修改了前一个例子,将输入 A 和 B 改为初始化器。该包实现了两个函数,用于将 NumPy 转换为 ONNX,反之亦然。
-
onnx.numpy_helper.to_array
: onnx -> numpy -
onnx.numpy_helper.from_array
: numpy -> onnx
import numpy
from onnx import numpy_helper, TensorProto
from onnx.helper import (
make_model, make_node, make_graph,
make_tensor_value_info)
from onnx.checker import check_model
# initializers
value = numpy.array([0.5, -0.6], dtype=numpy.float32)
A = numpy_helper.from_array(value, name='A') # numpy -> onnx
value = numpy.array([0.4], dtype=numpy.float32)
C = numpy_helper.from_array(value, name='C') # numpy -> onnx
# the part which does not change
X = make_tensor_value_info('X', TensorProto.FLOAT, [None, None])
Y = make_tensor_value_info('Y', TensorProto.FLOAT, [None])
node1 = make_node('MatMul', ['X', 'A'], ['AX'])
node2 = make_node('Add', ['AX', 'C'], ['Y'])
graph = make_graph([node1, node2], 'lr', [X], [Y], [A, C])
onnx_model = make_model(graph)
check_model(onnx_model)
print(onnx_model)
输出如下:
ir_version: 10
graph {
node {
input: "X"
input: "A"
output: "AX"
op_type: "Ma服务器托管网tMul"
}
node {
input: "AX"
input: "C"
output: "Y"
op_type: "Add"
}
name: "lr"
initializer {
dims: 2
data_type: 1
name: "A"
raw_data: "000000?23223131277"
}
initializer {
dims: 1
data_type: 1
name: "C"
raw_data: "315314314>"
}
input {
name: "X"
type {
tensor_type {
elem_type: 1
shape {
dim {
}
dim {
}
}
}
}
}
output {
name: "Y"
type {
tensor_type {
elem_type: 1
shape {
dim {
}
}
}
}
}
}
opset_import {
version: 21
}
与之前一样,也可以通过ONNX结构来查看初始化器的内容:
print('** initializer **')
for init in onnx_model.graph.initializer:
print(init)
输出如下所示:
** initializer **
dims: 2
data_type: 1
name: "A"
raw_data: "000000?23223131277"
dims: 1
data_type: 1
name: "C"
raw_data: "315314314>"
属性Attibutes
一些算子需要属性,例如转置算子Transpose。下面来构建一个图:
y
=
X
A
′
+
B
y=XA’+B
y=XA′+B,也可以表示成y=Add(MatMul(x,Transpose(A))+B)
。转置需要一个定义轴排列的属性:perm=[1,0]
。它作为命名属性(named attibutes)添加到make_node函数中。构建图的代码如下:
from onnx import TensorProto
from onnx.helper import (
make_model, make_node, make_graph,
make_tensor_value_info)
from onnx.checker import check_model
# unchanged
X = make_tensor_value_info('X', TensorProto.FLOAT, [None, None])
A = make_tensor_value_info('A', TensorProto.FLOAT, [None, None])
B = make_tensor_value_info('B', TensorProto.FLOAT, [None, None])
Y = make_tensor_value_info('Y', TensorProto.FLOAT, [None])
# added
node_transpose = make_node('Transpose', ['A'], ['tA'], perm=[1, 0])
# unchanged except A is replaced by tA
node1 = make_node('MatMul', ['X', 'tA'], ['XA'])
node2 = make_node('Add', ['XA', 'B'], ['Y'])
# node_transpose is added to the list
graph = make_graph([node_transpose, node1, node2],
'lr', [X, A, B], [Y])
onnx_model = make_model(graph)
check_model(onnx_model)
# the work is done, let's display it...
print(onnx_model)
转置是第一个节点,make_node的最后一个参数添加属性,输出如下所示:
ir_version: 10
graph {
node {
input: "A"
output: "tA"
op_type: "Transpose"
attribute {
name: "perm"
ints: 1
ints: 0
type: INTS
}
}
node {
input: "X"
input: "tA"
output: "XA"
op_type: "MatMul"
}
node {
input: "XA"
input: "B"
output: "Y"
op_type: "Add"
}
name: "lr"
input {
name: "X"
type {
tensor_type {
elem_type: 1
shape {
dim {
}
dim {
}
}
}
}
}
input {
name: "A"
type {
tensor_type {
elem_type: 1
shape {
dim {
}
dim {
}
}
}
}
}
input {
name: "B"
type {
tensor_type {
elem_type: 1
shape {
dim {
}
dim {
}
}
}
}
}
output {
name: "Y"
type {
tensor_type {
elem_type: 1
shape {
dim {
}
}
}
}
}
}
opset_import {
version: 21
}
make函数的种类可以用下述方法进行展示:
import onnx
import pprint
pprint.pprint([k for k in dir(onnx.helper)
if k.startswith('make')])
输出如下:
['make_attribute',
'make_attribute_ref',
'make_empty_tensor_value_info',
'make_function',
'make_graph',
'make_map',
'make_map_type_proto',
'make_model',
'make_model_gen_version',
'make_node',
'make_operatorsetid',
'make_opsetid',
'make_optional',
'make_optional_type_proto',
'make_sequence',
'make_sequence_type_proto',
'make_sparse_tensor',
'make_sparse_tensor_type_proto',
'make_sparse_tensor_value_info',
'make_tensor',
'make_tensor_sequence_value_info',
'make_tensor_type_proto',
'make_tensor_value_info',
'make_training_info',
'make_value_info']
算子集与元数据 Opset and Metadata
加载之前存下的ONNX文件来看看它有什么元数据:
from onnx import load
with open("linear_regression.onnx", "rb") as f:
onnx_model = load(f)
for field in ['doc_string', 'domain', 'functions',
'ir_version', 'metadata_props', 'model_version',
'opset_import', 'producer_name', 'producer_version',
'training_info']:
print(field, getattr(onnx_model, field))
输出如下所示:
doc_string
domain
functions []
ir_version 10
metadata_props []
model_version 0
opset_import [version: 21
]
producer_name
producer_version
training_info []
其中大部分都是空的,原因是他们在ONNX图创建时是空的。其中只有两个是有值的:
from onnx import load
with open("linear_regression.onnx", "rb") as f:
onnx_model = load(f)
print("ir_version:", onnx_model.ir_version)
for opset in onnx_model.opset_import:
print("opset domain=%r version=%r" % (opset.domain, opset.version))
输出是:
ir_version: 10
opset domain='' version=21
ir_version是ONNX语言的版本。算子集定义了使用算子的版本。在没有明确时,ONNX会使用安装包对应的最新版本。如果需要修改,方式如下所示:
from onnx import load
with open("linear_regression.onnx", "rb") as f:
onnx_model = load(f)
del onnx_model.opset_import[:] # delete(删除)opset_import的属性
opset = onnx_model.opset_import.add() # 添加新属性
opset.domain = '' # 修改新属性的值
opset.version = 14
for opset in onnx_model.opset_import:
print("opset domain=%r version=%r" % (opset.domain, opset.version))
输出如下:
opset domain='' version=14
只要所有操作符都按照 ONNX 的规范定义,任何 opset 都可以使用。Reshape 操作符的版本 5 将形状定义为输入,而在版本 1 中为属性。opset 用于描述图时指定遵循哪些规范。
其他元数据(例如模型版本model_version等)可以用于存储任何信息,例如有关模型生成方式的信息,以及使用版本号将模型与其他模型区分开来的方法。例如:
from onnx import load, helper
with open("linear_regression.onnx", "rb") as f:
onnx_model = load(f)
onnx_model.model_version = 15
onnx_model.producer_name = "something"
onnx_model.producer_version = "some other thing"
onnx_model.doc_string = "documentation about this model"
# prop = onnx_model.metadata_props
data = dict(key1="value1", key2="value2")
helper.set_model_props(onnx_model, data)
print(onnx_model)
在开头和结尾添加了一些元数据,输出如下所示:
ir_version: 10
producer_name: "something"
producer_version: "some other thing"
model_version: 15
doc_string: "documentation about this model"
graph {
node {
input: "X"
input: "A"
output: "XA"
op_type: "MatMul"
}
node {
input: "XA"
input: "B"
output: "Y"
op_type: "Add"
}
name: "lr"
input {
name: "X"
type {
tensor_type {
elem_type: 1
shape {
dim {
}
dim {
}
}
}
}
}
input {
name: "A"
type {
tensor_type {
elem_type: 1
shape {
dim {
}
dim {
}
}
}
}
}
input {
name: "B"
type {
tensor_type {
elem_type: 1
shape {
dim {
}
dim {
}
}
}
}
}
output {
name: "Y"
type {
tensor_type {
elem_type: 1
shape {
dim {
}
}
}
}
}
}
opset_import {
version: 21
}
metadata_props {
key: "key1"
value: "value1"
}
metadata_props {
key: "key2"
value: "value2"
}
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
直接上代码,复制即可使用 public class SnowflakeIdGenerator { private static final long START_TIMESTAMP = 1624000000000L; // 设置起始时间戳,2021-06-18…