ONNX-开放式神经网络交换格式

摘要:
ONNX的规范和代码主要由微软、亚马逊、Facebook和IBM联合开发,并以开源方式托管在Github上。

以下内容根据个人理解整理而成,如有错误,欢迎指出,不胜感激。

1. ONNX简介

ONNX是一种针对机器学习所设计的开放式的文件格式,用于存储训练好的模型。它使得不同的人工智能框架(如Pytorch, MXNet)可以采用相同格式存储模型数据并交互。 ONNX的规范及代码主要由微软,亚马逊 ,Facebook 和 IBM 等公司共同开发,以开放源代码的方式托管在Github上。目前官方支持加载ONNX模型并进行推理的深度学习框架有: Caffe2, PyTorch, MXNet,ML.NET,TensorRT 和 Microsoft CNTK,并且 TensorFlow 也非官方的支持ONNX。---维基百科

ONNX全称是Open Neural Network Exchange,不同深度学习框架可以将模型保存为ONNX格式,从而实现模型在不同框架之间的转换。
ONNX中,每一个计算流图都定义为由节点组成的列表,每个节点是一个OP,可能有一个或多个输入与输出,并由这些节点构建有向无环图。
目前,ONNX已支持当前主要的各种深度学习框架,有些框架如PyTorch是官方集成了ONNX,有些需要第三方支持,即便像darknet这种小众的框架,也可以手动构建ONNX图来将模型转为ONNX格式。

我们可以使用pip或在conda环境中使用conda来获取ONNX,具体参见ONNX的github

2. ONNX使用

ONNX是一个开放式规范,由以下组件组成:

  • 可扩展计算图模型
  • 一系列内置运算单元(OP)
  • 标准数据类型

将一个模型转为ONNX格式,主要是构造计算流图,在官方github的example目录下有很多使用示例,这里列举出最常用的几个方法。

在ONNX中,数据的存储使用的是Google的Protobuf序列化框架,数据结构主要有以下六种,定义在onnx/onnx.in.proto文件内:

TensorProto
ValueInfoProto
AttributeProto

NodeProto
ModelProto
GraphProto

构造上述数据类型的方法定义在onnx/helper.py文件内,具体如下:

helper.make_tensor()            
helper.make_tensor_value_info()  
helper.make_attribute()         

helper.make_node()  
helper.make_model()   
helper.make_graph()

另外两个比较常用的方法是对构造完的模型的检查和保存,分别定义在onnx/checker.pyonnx/__init__.py中:

onnx.checker.check_model()
onnx.save()

下面给出上述主要方法的定义,具体使用示例可参考官方example
helper.make_tensor()

def make_tensor(
        name,       # tensor名称(string)
        data_type,  # tensor内数据类型(TensorProto.dataType)
        dims,       # tensor的shape(list of int)
        vals,       # tensor的值
        raw=False   # 当为false时,该方法会根据data_type类型来存储vals,当为true时,该方法会使用raw_data来存储vals(在该方法中指的是bytes类型) -> 此处理解存疑(proto field ?)
):  # type: (...) -> TensorProto

helper.make_tensor_value_info()

# 通常和make_tensor()一起使用,用于创建一个tensor的信息
def make_tensor_value_info(
        name,                   # tensor名称(string)
        elem_type,              # tensor中元素的类型(TensorProto.dataType)
        shape,                  # tensor的shape(list of int)
        doc_string="",          # 可选参数:对该tensor的描述
        shape_denotation=None,  # 可选参数:对shape中每个维度的描述(list of string)
):  # type: (...) -> ValueInfoProto

helper.make_attribute()

# 该方法是make_node()的内部调用方法,当我们通过**kwargs传入一系列op的属性时,最终都会调用该方法转化为键值对,因此可以直接调用该方法先构造键值对,然后将构造结果传给**kwargss
def make_attribute(
        key,             
        value,           
        doc_string=None  
):  # type: (...) -> AttributeProto

helper.make_node()

def make_node(
        op_type,          # 要构造的op的名字(string) -> 相当于是将一个op封装为一个节点
        inputs,           # 输入当前节点的节点名称(list of string)  
        outputs,          # 当前节点输出的名称(list of string)  -> 当节点只有一个输出时,节点名称就相当于outputs
        name=None,        # 可选参数:当前节点的名称,作为索引该节点的唯一标识(string)
        doc_string=None,  # 可选参数:为当前节点添加描述(string)
        domain=None,      # 可选参数:为当前节点添加一个领域?(string)
        **kwargs          # 当前节点的属性(dict or 类似普通参数的传入),不同类型的op具有不同的属性,具体参数需要看构造的op类型,然后参考官方op文档所给的属性选项
):  # type: (...) -> NodeProto

helper.make_graph()

def make_graph(
    nodes,             # 节点的列表(list of node)
    name,              # graph的名称(string)
    inputs,            # 输入网络的tensor的相关信息(list of ValueInfoProto),包括网络每层的权重等信息 -> 由make_tensor_value_info()构造
    outputs,           # 网络输出tensor的相关信息(list of ValueInfoProto) -> 由make_tensor_value_info()构造
    initializer=None,  # 可选参数:图中每个节点的初始化权值(list of TensorProto) -> 由make_tensor()构造
    doc_string=None,   # 可选参数:对graph的描述
    value_info=[],     # 可选参数:存放中间层产生的输出数据的信息(list of ValueInfoProto) -> 由make_tensor_value_info()构造
):  # type: (...) -> GraphProto

helper.make_model()

def make_model(
        graph, 
        **kwargs
):  # type: (GraphProto, **Any) -> ModelProto

3. 总结

总结一下构造一个onnx模型的具体流程:

  1. 根据自己的网络结构调用make_node()来创建相关节点,节点的inputs和outputs参数决定了后续graph的连接情况,节点的权值和信息通过调用make_tensor()make_tensor_value_info()来创建,它们和节点的联系在于节点的name
  2. 上述三个方法构造的结构分别对应make_graph()中的三个参数,具体如下所示:
    • nodes: make_node()
    • inputs: make_tensor_value_info()
    • initializer: make_tensor()
  3. 最后检查和保存模型即可

Reference

开源一年多的模型交换格式ONNX,已经一统框架江湖了?
ONNX github
ONNX example
ONNX op

免责声明:文章转载自《ONNX-开放式神经网络交换格式》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇智能对话机器人实战开发案例剖析(1)- 体系结构和分类Python 中的垃圾回收机制下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

java在图片上添加文字

业务需求要在图片上添加水印。下面粘出代码供自己和大家分享 packagecom.pro.drawTextOnImg; importjava.awt.Color; importjava.awt.Font; importjava.awt.FontMetrics; importjava.awt.Graphics2D; importjava.awt.Im...

C#中IntPtr

C#中IntPtr System.Object System.ValueType System.IntPtr 1.C#中的IntPtr类型被称之为“平台特定的整数类型”,用于本机资源,例如窗口句柄。 2.资源的大小取决于使用的硬件和操作系统,即此类型的实例在32位硬件和操作系统中将是32位,在64位硬件和操作系统中将是64位;但其大小总是足以包含系统的...

Linux命令之basename 命令

用途 返回一个字符串参数的基本文件名称。 语法 basename String [ Suffix ] 描述 basename 命令读取 String 参数,删除以 /(斜杠) 结尾的前缀以及任何指定的 Suffix 参数,并将剩余的基本文件名称写至标准输出。basename 命令应用以下创建基本文件名称的规则: 如果 String 参数是 //(双斜杠)...

跟着我一步一步的搭建一个基于springcloud的微服务实例

Table of Contentsgenerated with DocToc microservicecloud 插件推荐 建立父工程Microservicecloud 搭建Employ员工服务创建数据库 创建消费者服务microservicecloud-employconsummer Eureka注册中心搭建 单机模式创建microserv...

【Gstreamer开发】TI嵌入式处理器GStreamer pipeline

Example GStreamer Pipelines From Texas Instruments Embedded Processors Wiki Jump to: navigation, search Translate this page to Translate Example GStreamer Pipelines MAINTENANCE...

【原创】Kafka接受发送消息对象Object基础版

首先感谢 kafka 中国社区 王扬庭例子的帮助和指导~~~~~(kafka_2.9.2-0.8.1.1) kafka常用的发送消息的方法如下: Properties props = new Properties(); props.put("zookeeper.connect", "slaves2:2181,slaves3:2181,slaves4:218...