ONNX学习笔记

ONNX

一种表示深度学习模型的开放格式,一套独立于环境和平台的标准格式。

ONNX文件存储了神经网络模型的权重和模型的结构信息、网络中各层的输入输出等一些信息。

ONNX官方文档

使用Netron查看ONNX文件

Netron网址,除onnx文件之外,还能查看.tflite .pb .h5 等文件

Protobuf

Google提出的一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化。ONNX使用Protobuf去存储神经网络的权重信息。

安装Protobuf Compiler

编译abseil
1. git clone https://github.com/abseil/abseil-cpp.git
2. cd ./abseil-cpp
3. mkdir build && cd ./build
4. cmake -DABSL_BUILD_TESTING=ON -DABSL_USE_GOOGLETEST_HEAD=ON -DCMAKE_CXX_STANDARD=14 ..
要求C++ 标准最低是 C++ 14
5. cmake --build . --target all
6. sudo make install
编译Protobuf Compiler
1. git clone https://github.com/protocolbuffers/protobuf.git
2. cd ./protobuf
3. git submodule update --init --recursive
4. cmake -S . -B ./build
5. cmake --build ./build --parallel 10
6. cd ./build
7. sudo make install
ubuntu 使用 sudo cmake install:
    /usr/local/bin: 该目录用于存放编译安装的可执行文件
    /usr/local/lib: 该目录用于存放编译安装的库文件
    /usr/local/include: 该目录用于存放头文件
    /usr/local/cmake/: 该目录存放xxConfig.cmake等文件,用于find_package使用
使用Protobuf
1. 编写.proto文件
2. 使用protoc ./addressbook.proto --cpp_out .
得到一个.pb.h 和 一个.pb.cc文件
3. 编写C++文件,头文件中 #include ".pb.h"
// .proto example
syntax = "proto3"; 	// 声明了protobuf的版本

package fixbug; 	// 声明了代码所在的包(对于C++来说是namespace)

// 登录消息类型
message LoginRequest {
    string name = 1;    // 1表示第一个字段
    string pwd = 2;
}

// 定义登录响应消息类型
message LoginResponse {
    int32 errcode = 1;
    string errmsg = 2;
    bool success = 3;
}

Onnx文件结构

// https://github.com/onnx/onnx/blob/main/onnx/onnx.proto
enum Vesion;

message AttributeProto {
	enum AttributeType {
		// 属性
	}
	...
}

message ValueInfoProto;
message NodeProto {
	repeated string input = 1;
	repeated string output = 2;
	optional string name = 3;
	optional string op_type = 4;
	optional string domain = 7;
	repeated AttributeProto attribute = 5;
  	optional string doc_string = 6; 
}
message TrainingInfoProto;
message ModelProto;
message GraphProto;
message TensorProto;
message SparseTensorProto;
message TensorShapeProto;
message TypeProto;
message OperatorSetIdProto;
message FunctionProto;

拓扑结构

Proto详细介绍

一个Onnx文件即是一个Modelproto, 每个Modelproto包含一些版本信息,生产者信息和一个GraphProto.

GraphProto定义了模型的计算逻辑,由参数化的节点列表组成,这些节点根据其输入和输出形成有向无环图。

其中包含了NodeProto(node)、TensorProto(initializer)、SparseTensorProto(sparse_initializer)、 ValueInfoProto(input)、 ValueInfoProto(output)、 ValueInfoProto(value_info)、TensorAnnotation(quantization_annotation), 其中node中存放了模型中所有的计算节点,input存放了模型的输入节点,output存放了模型中所有的输出节点,initializer存放了模型的所有权重参数。

每个NodeProto包括input 和 output 用于构建网络的拓扑关系。

AttributeProto数组,用来描述该节点的属性。

API

ONNX提供了python用于创建、修改和测试.onnx文件,可以很方便的对onnx文件进行操作。

// 将一个yolo.onnx模型分为不包含后处理和仅包含后处理两个部分
import onnx
input_path = "./yolo.onnx"

# 不包含后处理
# output_path = "./yolo_sub_module.onnx"
# input_names = ["input"]

# output_names = [
#     "1105", "1106", "1107", "1132", "1133", "1134",
#     "1159", "1160", "1161", "1186", "1187", "1188",
#     "1051", "1052", "1053", "1078", "1079", "1080",
# ]

# onnx.utils.extract_model(input_path, output_path, input_names, output_names)

# 仅包含后处理
output_path = "./yolo_sub_post.onnx"
input_names = [
    "1105", "1106", "1107", "1132", "1133", "1134",
    "1159", "1160", "1161", "1186", "1187", "1188",
    "1051", "1052", "1053", "1078", "1079", "1080",
]

output_names = [
    "dets", "scores", "labels",
]
onnx.utils.extract_model(input_path, output_path, input_names, output_names)

也可以使用API来搭建一个模型

// 官方示例,更多参见https://onnx.ai/onnx/intro/python.html
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')

value = numpy.array([0.4], dtype=numpy.float32)
C = numpy_helper.from_array(value, name='C')

# 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)

同样,除了使用可视化查看onnx模型的信息外,也可以使用api来打印模型信息

# the list of inputs
print('** inputs **')
print(onnx_model.graph.input)

# in a more nicely format
print('** inputs **')
for obj in onnx_model.graph.input:
    print("name=%r dtype=%r shape=%r" % (
        obj.name, obj.type.tensor_type.elem_type,
        shape2tuple(obj.type.tensor_type.shape)))

# the list of outputs
print('** outputs **')
print(onnx_model.graph.output)

# in a more nicely format
print('** outputs **')
for obj in onnx_model.graph.output:
    print("name=%r dtype=%r shape=%r" % (
        obj.name, obj.type.tensor_type.elem_type,
        shape2tuple(obj.type.tensor_type.shape)))

# the list of nodes
print('** nodes **')
print(onnx_model.graph.node)

# in a more nicely format
print('** nodes **')
for node in onnx_model.graph.node:
    print("name=%r type=%r input=%r output=%r" % (
        node.name, node.op_type, node.input, node.output))

验证

因为OnnxRuntime的python api接口比较简单,一般使用OnnxRuntime来进行验证结果,但ONNX本身也提供了验证,并且可以针对节点,中间层输出,自定义算子等进行验证。

import numpy
from onnx import numpy_helper, TensorProto
from onnx.helper import (
    make_model, make_node, set_model_props, make_tensor,
    make_graph, make_tensor_value_info)
from onnx.checker import check_model
from onnx.reference import ReferenceEvaluator

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])
node1 = make_node('MatMul', ['X', 'A'], ['XA'])
node2 = make_node('Add', ['XA', 'B'], ['Y'])
graph = make_graph([node1, node2], 'lr', [X, A, B], [Y])
onnx_model = make_model(graph)
check_model(onnx_model)

sess = ReferenceEvaluator(onnx_model)

x = numpy.random.randn(4, 2).astype(numpy.float32)
a = numpy.random.randn(2, 1).astype(numpy.float32)
b = numpy.random.randn(1, 1).astype(numpy.float32)
feeds = {'X': x, 'A': a, 'B': b}

print(sess.run(None, feeds))

导出

不同的框架有不同导出ONNX文件的方法,以PyTorch为例:

def export_model_from_pytorch_to_onnx(pytorch_model, onnx_model_name):
    batch_size = 1
    # input to the model
    x = torch.randn(batch_size, 1, 32, 32)
    out = pytorch_model(x)
    #print("out:", out)
 
    # export the model
    torch.onnx.export(pytorch_model,             # model being run
                      x,                         # model input (or a tuple for multiple inputs)
                      onnx_model_name,           # where to save the model (can be a file or file-like object)
                      export_params=True,        # store the trained parameter weights inside the model file
                      opset_version=9,           # the ONNX version to export the model to
                      do_constant_folding=True,  # whether to execute constant folding for optimization
                      input_names = ['input'],   # the model's input names
                      output_names = ['output'], # the model's output names
                      dynamic_axes={'input' : {0 : 'batch_size'},    # variable length axes
                                    'output' : {0 : 'batch_size'}})

onnx-simplifier

ONNX Simplifer Github

用于简化onnx文件,得到更精简的表示,对pytorch、tf或者paddle转换得到的onnx模型做优化,去除很多胶水节点,以及做一些图优化,得到一个简洁明了的模型图

参考

知乎
Onnx-Simplifier
ONNX 官方文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值