上传图片的网站要怎么做广东seo加盟
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/27;最后编辑时间:2024/2/27。
这是本教程的第七篇,其余内容见上述专栏链接。
ONNX with Python
本教程的第一篇:介绍了ONNX的基本概念。
在本教程的第二篇,介绍了ONNX关于Python的API,具体涉及一个简单的线性回归例子和序列化。
本教程的第三篇,包括python API的三个部分:初始化器Initializer;属性Attributes;算子集和元数据Opset和Metadata
本教程的第四篇,包括子图的两个内容,使用If和Scan算子实现子图的选择和循环。
本教程的第五篇,包括函数,模型解析域形状推理。
本教程的第六篇:包括求值:即实现模型的计算与其他的一些实现细节。
在本教程,会简单的一览ONNX现有的API。
Python API一览
完整的的API描述见API Reference,后续的教程可能会进一步的进行介绍。
01 加载ONNX模型
import onnxonnx_model = onnx.load("path/to/the/model.onnx")
02 带有外部数据时加载ONNX模型
如果外部数据在模型的相同目录,则简单的使用onnx.load即可.
import onnxonnx_model = onnx.load("path/to/the/model.onnx")
如果外部数据在其他目录,使用load_external_data_for_model()
来指定目录路径,并在onnx.load
之后加载。
import onnx
from onnx.external_data_helper import load_external_data_for_modelonnx_model = onnx.load("path/to/the/model.onnx", load_external_data=False)
load_external_data_for_model(onnx_model, "data/directory/path/")
03 将ONNX模型转换为外部数据
from onnx.external_data_helper import convert_model_to_external_data# onnx_model是一个内存中的ModelProto
onnx_model = ...
convert_model_to_external_data(onnx_model, all_tensors_to_one_file=True, location="filename", size_threshold=1024, convert_attribute=False)
# 然后onnx_model已经将原始数据转换为外部数据;# 必须在后面保存模型
04 存储ONNX模型
import onnx# onnx_model是一个内存中的ModelProto
onnx_model = ...# 保存ONNX模型
onnx.save(onnx_model, "path/to/the/model.onnx")
05 转换并保存ONNX模型到一个外部数据
import onnx# onnx_model是一个内存中的ModelProto
onnx_model = ...
onnx.save_model(onnx_model, "path/to/save/the/model.onnx", save_as_external_data=True, all_tensors_to_one_file=True, location="filename", size_threshold=1024, convert_attribute=False)
# 然后onnx_model将原始数据转换为外部数据并保存到特定目录
06 操作TensorProto和Numpy数组
import numpy
import onnx
from onnx import numpy_helper# 预处理: 创建一个Numpy数组
numpy_array = numpy.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], dtype=float)
print(f"Original Numpy array:\n{numpy_array}\n")# 将Numpy数组转换成一个TensorProto
tensor = numpy_helper.from_array(numpy_array)
print(f"TensorProto:\n{tensor}")# 转换TensorProto成一个Numpy数组
new_array = numpy_helper.to_array(tensor)
print(f"After round trip, Numpy array:\n{new_array}\n")# 保存TensorProto
with open("tensor.pb", "wb") as f:f.write(tensor.SerializeToString())# 加载TensorProto
new_tensor = onnx.TensorProto()
with open("tensor.pb", "rb") as f:new_tensor.ParseFromString(f.read())
print(f"After saving and loading, new TensorProto:\n{new_tensor}")from onnx import TensorProto, helper# ONNX IR上映射属性的实用格式转换技巧;以下函数在ONNX 1.13之后可用
np_dtype = helper.tensor_dtype_to_np_dtype(TensorProto.FLOAT)
print(f"The converted numpy dtype for {helper.tensor_dtype_to_string(TensorProto.FLOAT)} is {np_dtype}.")
storage_dtype = helper.tensor_dtype_to_storage_tensor_dtype(TensorProto.FLOAT)
print(f"The storage dtype for {helper.tensor_dtype_to_string(TensorProto.FLOAT)} is {helper.tensor_dtype_to_string(storage_dtype)}.")
field_name = helper.tensor_dtype_to_field(TensorProto.FLOAT)
print(f"The field name for {helper.tensor_dtype_to_string(TensorProto.FLOAT)} is {field_name}.")
tensor_dtype = helper.np_dtype_to_tensor_dtype(np_dtype)
print(f"The tensor data type for numpy dtype: {np_dtype} is {helper.tensor_dtype_to_string(tensor_dtype)}.")for tensor_dtype in helper.get_all_tensor_dtypes():print(helper.tensor_dtype_to_string(tensor_dtype))
输出:
Original Numpy array:
[[1. 2. 3.][4. 5. 6.]]TensorProto:
dims: 2
dims: 3
data_type: 11
raw_data: "\000\000\000\000\000\000\360?\000\000\000\000\000\000\000@\000\000\000\000\000\000\010@\000\000\000\000\000\000\020@\000\000\000\000\000\000\024@\000\000\000\000\000\000\030@"After round trip, Numpy array:
[[1. 2. 3.][4. 5. 6.]]After saving and loading, new TensorProto:
dims: 2
dims: 3
data_type: 11
raw_data: "\000\000\000\000\000\000\360?\000\000\000\000\000\000\000@\000\000\000\000\000\000\010@\000\000\000\000\000\000\020@\000\000\000\000\000\000\024@\000\000\000\000\000\000\030@"The converted numpy dtype for TensorProto.FLOAT is float32.
The storage dtype for TensorProto.FLOAT is TensorProto.FLOAT.
The field name for TensorProto.FLOAT is float_data.
The tensor data type for numpy dtype: float32 is TensorProto.FLOAT.
TensorProto.FLOAT
TensorProto.UINT8
TensorProto.INT8
TensorProto.UINT16
TensorProto.INT16
TensorProto.INT32
TensorProto.INT64
TensorProto.BOOL
TensorProto.FLOAT16
TensorProto.BFLOAT16
TensorProto.DOUBLE
TensorProto.COMPLEX64
TensorProto.COMPLEX128
TensorProto.UINT32
TensorProto.UINT64
TensorProto.STRING
TensorProto.FLOAT8E4M3FN
TensorProto.FLOAT8E4M3FNUZ
TensorProto.FLOAT8E5M2
TensorProto.FLOAT8E5M2FNUZ
07 使用Helper函数创建ONNX模型
import onnx
from onnx import helper
from onnx import AttributeProto, TensorProto, GraphProto# 创建输入(ValueInfoProto)
X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [3, 2])
pads = helper.make_tensor_value_info("pads", TensorProto.FLOAT, [1, 4])
value = helper.make_tensor_value_info("value", AttributeProto.FLOAT, [1])# 创建输出(ValueInfoProto)
Y = helper.make_tensor_value_info("Y", TensorProto.FLOAT, [3, 4])# 创建节点(NodeProto)
node_def = helper.make_node("Pad", # name["X", "pads", "value"], # inputs["Y"], # outputsmode="constant", # attributes
)# 创建图(GraphProto)
graph_def = helper.make_graph([node_def], # nodes"test-model", # name[X, pads, value], # inputs[Y], # outputs
)# 创建模型(ModelProto)
model_def = helper.make_model(graph_def, producer_name="onnx-example")# 检查模型
onnx.checker.check_model(model_def)
print("The model is checked!")
08 ONNX IR上映射属性的转换实用技术
from onnx import TensorProto, helpernp_dtype = helper.tensor_dtype_to_np_dtype(TensorProto.FLOAT)
print(f"The converted numpy dtype for {helper.tensor_dtype_to_string(TensorProto.FLOAT)} is {np_dtype}.")field_name = helper.tensor_dtype_to_field(TensorProto.FLOAT)
print(f"The field name for {helper.tensor_dtype_to_string(TensorProto.FLOAT)} is {field_name}.")
# 在helper中还有其它转换函数
输出是:
The converted numpy dtype for TensorProto.FLOAT is float32.
The field name for TensorProto.FLOAT is float_data.
09 检查ONNX模型
Onnx提供了一个函数来检查模型是否有效。当它检测到不一致时,它会检查输入类型或形状。
import onnx# 预处理: 加载ONNX模型
model_path = "path/to/the/model.onnx"
onnx_model = onnx.load(model_path)print(f"The model is:\n{onnx_model}")# 检查模型
try:onnx.checker.check_model(onnx_model)
except onnx.checker.ValidationError as e:print(f"The model is invalid: {e}")
else:print("The model is valid!")
10 检查大于2GB的ONNX模型
当前检查器支持使用外部数据检查模型,但对于那些大于2GB的模型,请使用onnx的模型路径,而不是内存中的模型。Checker和外部数据需要在同一个目录下。
import onnxonnx.checker.check_model("path/to/the/model.onnx")
# onnx.checker.check_model(loaded_onnx_model)在模型大于2GB时会出错
11 在一个ONNX模型上执行形状推理
import onnx
from onnx import helper, shape_inference
from onnx import TensorProto# 预处理: 创建一个有两个节点的模型, Y的形状未知
node1 = helper.make_node("Transpose", ["X"], ["Y"], perm=[1, 0, 2])
node2 = helper.make_node("Transpose", ["Y"], ["Z"], perm=[1, 0, 2])graph = helper.make_graph([node1, node2],"two-transposes",[helper.make_tensor_value_info("X", TensorProto.FLOAT, (2, 3, 4))],[helper.make_tensor_value_info("Z", TensorProto.FLOAT, (2, 3, 4))],
)original_model = helper.make_model(graph, producer_name="onnx-examples")# 检查模型并打印出Y的形状信息
onnx.checker.check_model(original_model)
print(f"Before shape inference, the shape info of Y is:\n{original_model.graph.value_info}")# 在模型上应用形状推理
inferred_model = shape_inference.infer_shapes(original_model)# 检查模型并打印出Y的形状信息
onnx.checker.check_model(inferred_model)
print(f"After shape inference, the shape info of Y is:\n{inferred_model.graph.value_info}")
输出是:
Before shape inference, the shape info of Y is:
[]
After shape inference, the shape info of Y is:
[name: "Y"
type {tensor_type {elem_type: 1shape {dim {dim_value: 3}dim {dim_value: 2}dim {dim_value: 4}}}
}
]
12 对一个大于2GB模型进行形状推理
当前的shape_inference支持具有外部数据的模型,但对于那些大于2GB的模型,请使用onnx.shape_inference的模型路径。Infer_shapes_path和外部数据需要位于同一目录下。你可以指定保存推断出的模型的输出路径;否则,默认输出路径与原始模型路径相同。
import onnx# 将推断出的模型输出到原始模型路径
onnx.shape_inference.infer_shapes_path("path/to/the/model.onnx")# 将推断出的模型输出到指定的模型路径
onnx.shape_inference.infer_shapes_path("path/to/the/model.onnx", "output/inferred/model.onnx")# inferred_model = onnx.shape_inference.infer_shapes(loaded_onnx_model) # 在模型大于2GB会报错
13 在ONNX函数上执行类型推断
import onnx
import onnx.helper
import onnx.parser
import onnx.shape_inferencefunction_text = """<opset_import: [ "" : 18 ], domain: "local">CastTo <dtype> (x) => (y) {y = Cast <to : int = @dtype> (x)}
"""
function = onnx.parser.parse_function(function_text)# 上面的函数有一个输入参数x和一个属性参数dtype。
# 要对该函数应用“类型与形状推断”,必须提供输入参数的类型和属性参数的属性值,如下所示:
float_type_ = onnx.helper.make_tensor_type_proto(1, None)
dtype_6 = onnx.helper.make_attribute("dtype", 6)
result = onnx.shape_inference.infer_function_output_types(function, [float_type_], [dtype_6]
)
print(result) # a list containing the (single) output type
输出:
[tensor_type {elem_type: 6
}
]
14 转换默认域("“或"ai.onnx”)上ONNX模型的opset版本
import onnx
from onnx import version_converter, helper# 预处理: 加载待转换的模型
model_path = "path/to/the/model.onnx"
original_model = onnx.load(model_path)print(f"The model before conversion:\n{original_model}")# 进行版本转换
converted_model = version_converter.convert_version(original_model, <int target_version>)print(f"The model after conversion:\n{converted_model}")
15 实用函数:使用输入张量名称和输出张量名称提取子模型
函数extract_model()
可以从一个ONNX模型中提取子模型,可以使用输入张量名称和输出张量名称提取子模型。
import onnxinput_path = "path/to/the/original/model.onnx"
output_path = "path/to/save/the/extracted/model.onnx" # 提取模型保存到的路径
input_names = ["input_0", "input_1", "input_2"]
output_names = ["output_0", "output_1"]onnx.utils.extract_model(input_path, output_path, input_names, output_names)
注意:对于控制流操作,例如 If 和 Loop,子模型的边界,即由输入和输出张量定义的边界,不应穿过作为这些操作符属性连接到主图的子图。
16 实用函数:onnx.compose
onnx.compose
模块提供工具来创建结合的模型。
onnx.compose.merge_models
可以用于合并两个模型,方法是将第一个模型的某些输出连接到第二个模型的输入。默认情况下,io_map
参数中不存在的输入/输出将保留为合并模型的输入/输出。
在这个例子中,我们通过将第一个模型的每个输出连接到第二个模型中的一个输入来合并两个模型。得到的模型将具有与第一个模型相同的输入和与第二个模型相同的输出。
import onnxmodel1 = onnx.load("path/to/model1.onnx")
# agraph (float[N] A, float[N] B) => (float[N] C, float[N] D)
# {
# C = Add(A, B)
# D = Sub(A, B)
# }model2 = onnx.load("path/to/model2.onnx")
# agraph (float[N] X, float[N] Y) => (float[N] Z)
# {
# Z = Mul(X, Y)
# }combined_model = onnx.compose.merge_models(model1, model2,io_map=[("C", "X"), ("D", "Y")] # c->x; d ->y
)
此外,用户可以指定一个输入/输出列表,以包含在组合模型中,有效地舍弃了对组合模型输出没有贡献的图的部分。在下面的示例中,我们只将第一个模型中的两个输出中的一个连接到第二个模型中的两个输入。通过显式指定组合模型的输出,我们从第一个模型中丢弃了未使用的输出,以及图的相关部分。
import onnx# 默认例子. 在结合后的模型上包括所有输出
combined_model = onnx.compose.merge_models(model1, model2,io_map=[("C", "X"), ("C", "Y")],
) # outputs: "D", "Z"# 显式输出. “Y”输出和子节点在组合模型中不存在
combined_model = onnx.compose.merge_models(model1, model2,io_map=[("C", "X"), ("C", "Y")],outputs=["Z"],
) # outputs: "Z"
onnx.compose.add_prefix
允许您向模型中的名称添加前缀,以避免在合并它们时发生名称冲突。默认情况下,它会重命名图中的所有名称:输入、输出、边、节点、初始化器、稀疏初始化器和值信息。
import onnxmodel = onnx.load("path/to/the/model.onnx")
# model - outputs: ["out0", "out1"], inputs: ["in0", "in1"]new_model = onnx.compose.add_prefix(model, prefix="m1/")
# new_model - outputs: ["m1/out0", "m1/out1"], inputs: ["m1/in0", "m1/in1"]# 也可以就地运行
onnx.compose.add_prefix(model, prefix="m1/", inplace=True)
onnx.compose.expand_out_dim
可以用来连接那些期望不同维度数量的模型,通过插入范围为一的维度。当结合一个产生样本的模型与一个处理样本批次的模型时,这会很有用。
import onnx# outputs: "out0", shape=[200, 200, 3]
model1 = onnx.load("path/to/the/model1.onnx")# outputs: "in0", shape=[N, 200, 200, 3]
model2 = onnx.load("path/to/the/model2.onnx")# outputs: "out0", shape=[1, 200, 200, 3]
new_model1 = onnx.compose.expand_out_dims(model1, dim_idx=0)# 模型现在可以合并了
combined_model = onnx.compose.merge_models(new_model1, model2, io_map=[("out0", "in0")]
)# 也可以就地执行
onnx.compose.expand_out_dims(model1, dim_idx=0, inplace=True)
17 更行模型的输入/输出维度大小
函数 update_inputs_outputs_dims
更新模型的输入和输出的维度,将其更新为参数中提供的值。可以通过使用 dim_param
提供静态和动态维度大小。在更新输入/输出大小之后,该函数运行模型检查器。
import onnx
from onnx.tools import update_model_dimsmodel = onnx.load("path/to/the/model.onnx")
# Here both "seq", "batch" and -1 are dynamic using dim_param.
variable_length_model = update_model_dims.update_inputs_outputs_dims(model, {"input_name": ["seq", "batch", 3, -1]}, {"output_name": ["seq", "batch", 1, -1]})
18 ONNX解析器
函数 onnx.parser.parse_model
和 onnx.parser.parse_graph
可以用来从文本表示中创建一个 ONNX 模型或图,如下所示。有关语言语法的更多详细信息,请参阅Language Syntax。
input = """agraph (float[N, 128] X, float[128, 10] W, float[10] B) => (float[N, 10] C){T = MatMul(X, W)S = Add(T, B)C = Softmax(S)}
"""
graph = onnx.parser.parse_graph(input)input = """<ir_version: 7,opset_import: ["" : 10]>agraph (float[N, 128] X, float[128, 10] W, float[10] B) => (float[N, 10] C){T = MatMul(X, W)S = Add(T, B)C = Softmax(S)}
"""
model = onnx.parser.parse_model(input)
19 ONNX Inliner
函数 onnx.inliner.inline_local_functions
和 inline_selected_functions
可用于在 ONNX 模型中内联模型本地函数。特别地,inline_local_functions
可用于生成一个无函数的模型(适用于不处理或不支持函数的后端)。另一方面,inline_selected_functions
可用于内联选定的函数。目前尚不支持内联 ONNX 标准操作中的函数(也称为模式定义(schema-defined)的函数)。
import onnx
import onnx.inlinermodel = onnx.load("path/to/the/model.onnx")
inlined = onnx.inliner.inline_local_functions(model)
onnx.save("path/to/the/inlinedmodel.onnx")