通过创建一个单Conv节点的onnx模型来熟悉onnx库的使用和相关的概念。
make_graph 原型
def make_graph(
nodes: Sequence[NodeProto],
name: str,
inputs: Sequence[ValueInfoProto],
outputs: Sequence[ValueInfoProto],
initializer: Optional[Sequence[TensorProto]] = None,
doc_string: Optional[str] = None,
value_info: Optional[Sequence[ValueInfoProto]] = None,
sparse_initializer: Optional[Sequence[SparseTensorProto]] = None,
) -> GraphProto:
"""Construct a GraphProto
Args:
nodes: list of NodeProto
name (string): graph name
inputs: list of ValueInfoProto
outputs: list of ValueInfoProto
initializer: list of TensorProto
doc_string (string): graph documentation
value_info: list of ValueInfoProto
sparse_initializer: list of SparseTensorProto
Returns:
GraphProto
"""
从函数原型中,可以看出一个onnx模型的网络拓扑图需要包含以下几种类型的数据:
- nodes: Sequence[NodeProto],即节点列表。
- name: str,即模型的名称。
- inputs: Sequence[ValueInfoProto],即输入节点列表,主要包含输入节点的名称,形状和数据类型信息。
- outputs: Sequence[ValueInfoProto],即输出节点列表,主要包含输出节点的名称,形状和数据类型信息。
- initializer: Optional[Sequence[TensorProto]],有些类型的节点除了变化的input tensor之外,还有一些固定的输入,这些固定的输入就是initializer。
- doc_string: Optional[str],模型的文档字符串(不甚了解)。
- value_info: Optional[Sequence[ValueInfoProto]],节点间流动的tensor的信息,和输入输出类似,主要包括tensor的名称,形状和数据类型。
- sparse_initializer: Optional[Sequence[SparseTensorProto]],稀疏张量的初始化信息(不甚了解)。
创建一个单Conv节点的模型
import torch
import numpy as np
import onnx
from onnx import helper
from onnx import AttributeProto, TensorProto, GraphProto, ValueInfoProto
# 创建i/o ValueInfoProto
input_name = 'input'
input_tensor = helper.make_tensor_value_info(
input_name, # 输入的名称
TensorProto.FLOAT, # 数据类型
[1, 3, 224, 224] # 数据的形状
)
output_name = 'output'
output_tensor = helper.make_tensor_value_info(
output_name, # 输出的名称
TensorProto.FLOAT, # 数据类型
[1, 12, 112, 112] # 数据的形状
)
# 从现有的权重张量创建初始化器节点
weight_name = 'weight'
weight_data = np.random.randn(12, 3, 2, 2).astype(np.float32)
weight_initializer = onnx.numpy_helper.from_array(weight_data, name=weight_name)
# 创建全零偏置的初始化器节点
bias_name = 'bias'
bias_data = np.zeros((12,), dtype=np.float32)
bias_initializer = onnx.numpy_helper.from_array(bias_data, name=bias_name)
# 创建(12,3,2,2)卷积节点的属性
kernel_shape = [2, 2] # 卷积核的形状
strides = [2, 2] # 步幅
pads = [0, 0, 0, 0] # 填充
dilations = [1, 1] # 膨胀率
group = 1 # 常规卷积,不分组
auto_pad = 'NOTSET' # 自动填充方式,默认为NOTSET
# 创建卷积节点
conv_node = helper.make_node(
'Conv', # 节点类型
inputs=[input_name, weight_name, bias_name], # 输入的名称,需要根据实际情况修改
outputs=[output_name], # 输出的名称,需要根据实际情况修改
name='conv', # 节点名称
kernel_shape=kernel_shape,
strides=strides,
pads=pads,
dilations=dilations,
group=group,
auto_pad=auto_pad
)
graph = helper.make_graph(nodes=[conv_node], # 节点
name='conv', # 图的名称
inputs=[input_tensor], # 输入
outputs=[output_tensor], # 输出
initializer=[weight_initializer, bias_initializer] # 初始化器
)
# 创建ONNX模型
model = helper.make_model(graph, producer_name='onnx-example')
onnx.checker.check_model(model)
# 保存ONNX模型
onnx.save_model(model, 'conv_model.onnx')
ps: Conv节点的输入输出只需要指定 ValueInfoProto 或 TensorProto 的名称即可,不需要指定具体的数据。
最终拓扑结构为:
input
|
| weight bias
| | |
(----Conv---)
|
output
可以通过netron可视化保存的onnx模型文件查看模型的拓扑结构。