ONNX系列: ONNX模型结构解析

news2024/11/16 22:48:09

1. ONNX 背景        

ONNX 全称为 Open Neural Network Exchange,是微软提出并推广的一种机器学习模型的开放格式表示。ONNX定义了一组通用的算子集、一系列用于构建深度学习模型的模块以及一种通用的文件格式,使得人工智能开发人员能够将模型与各种框架、工具、运行时和编译器一起使用。ONNX可以理解为是 AI 算法框架与硬件平台之间的桥梁,AI算法研究人员可以使用任意的深度学习框架来设计并训练模型,训练完成后将模型转换成 ONNX 格式来进行存储,模型部署工程师可以针对 ONNX 这一中间格式来针对不同的硬件平台进行运行时设计和优化,从而实现AI模型设计和模型部署的解耦。

2. ONNX 结构分析

想要部署ONNX模型,我们首先需要了解 ONNX 模型的结构。神经网络模型是由 计算图 + 权重 组成的,计算图是一个有向无环的计算流程图,权重则是网络训练好的参数集合。关于 ONNX 更详尽的介绍,可以参考 ONNX 官方文档 onnx/docs at main · onnx/onnx · GitHub。

ONNX 模型是利用 ProtoBuf 这一数据结构存储协议来将模型序列化到硬盘上的。一个存储到本地的 .onnx 模型可以以结构化的方式解析成下图所示的各个部分,其中比较重要的部分加粗表示。加粗部分上方是当前结构的名称,下方是当前结构的类型,各个Proto类型的定义可以参考onnx/onnx/onnx.proto at main · onnx/onnx · GitHub。

2.1 查看onnx

  • 可以使用 Netron 来可视化查看 ONNX 模型
  • 可以使用 protoc 工具来解析 .onnx 模型文件。命令中 onnx.proto 是 ONNX 官方 repo 中的 Proto 定义。这条命令的含义是将super-resolution-10.onnx作为输入,按照 onnx.proto 定义从中提取 onnx.ModelProto 对象,并将结果重定向到 model.txt。

$ protoc --decode=onnx.ModelProto -I D:\Python\workspace\onnx_learn\onnx\onnx onnx.proto < D:\Python\workspace\onnx_learn\super-resolution-10.onnx > model.txt

2.2 使用 Python 来获取到 ONNX 模型结构

1) onnx model 结构

onnx model 是 ModelProto 类型,是 ONNX 模型最顶层的结构。其所包含的各个成员如下:

属性名

示例值

描述

ir_version

int64

模型的onnx IR版本。

opset_import

OperatorSetId

可用于模型的算子集标识符集合。一个onnx实现中必须包含这个集合中的所有算子,否则将拒接模型。

producer_name

string

生成这个onnx的生产者工具名称。

producer_version

string

这个生产者工具的版本。

domain

string

onnx模型的命名空间,用反向域名命名,和java一样。

model_version

int64

模型本身的版本。

doc_string

string

文档注释,可以是Markdown。

graph

Graph

模型计算图。

metadata_props

map<string,string>

元数据的键值对属性。

training_info

TrainingInfoProto[]

包含训练信息的可选扩展。

functions

FunctionProto[]

模型本地函数的可选列表。

使用Python API获取onnx model各成员

import onnx

model = onnx.load('super-resolution-10.onnx')

print(f"model.ir_version ---> {model.ir_version}")

print(f"model.opset_import ---> {model.opset_import}")

print(f"model.producer_name ---> {model.producer_name}")

print(f"model.producer_version ---> {model.producer_version}")

print(f"model.domain ---> {model.domain}")

print(f"model.model_version ---> {model.model_version}")

print(f"model.doc_string ---> {model.doc_string}")

print(f"model.metadata_props ---> {model.metadata_props}")

print(f"model.training_info ---> {model.training_info}")

print(f"model.functions ---> {model.functions}")

print(f"model.graph ---> {model.graph}")

2) model.graph 结构

model 中最重要的是 graph,类型是 GraphProto。 其所包含的各个成员如下:

属性名

示例值

描述

name

string

模型计算图的名字

node

Node[]

计算图中的算子集合(有向无环图的节点集),按照拓扑排序排列

initializer

Tensor[]

计算图中的initializer,是一个tensor列表,通常存放模型的权重,可以理解为一个常量池。

doc_string

string

文档注释

input

ValueInfo[]

模型计算图的输入tensor列表。

output

ValueInfo[]

模型计算图的输出tensor列表。

value_info

ValueInfo[]

模型计算图除输入输出外中间tensor列表,当使用shape_inference时,推理出来的shape存储到这里,即 Netron 中看到的中间tensor的维度。

metadata_props

map<string,string>

模型计算图的元数据(IR version >= 10)

使用Python API获取 model.graph 各成员

print("-------------------- model.graph.name --------------------")

print(model.graph.name)

print("-------------------- model.graph.node --------------------")

print(model.graph.node)

print("-------------------- model.graph.initializer --------------------")

print(model.graph.initializer)

print("-------------------- model.graph.doc_string --------------------")

print(model.graph.doc_string)

print("-------------------- model.graph.input --------------------")

print(model.graph.input)

print("-------------------- model.graph.output --------------------")

print(model.graph.output)

print("-------------------- model.graph.value_info --------------------")

print(model.graph.value_info)

print("-------------------- model.graph.metadata_props (IR version >= 10) --------------------")

print(model.graph.metadata_props)

3) model.graph.node 结构

model.graph中的 node 是一个节点集列表,其中的每个元素节点均为 NodeProto 类型,所包含的成员如下:

属性名

示例值

描述

name

string

节点的名字

input

string[]

节点的输入列表,相当于计算图的输入边集。

output

string[]

节点的输出列表,相当于计算图的输出边集。

op_type

string

节点的类型,表明该节点的计算逻辑。

domain

string

ONNX中定义的节点集的域。由于 ONNX 是支持第三方拓展内置的算子集的,这个域唯一的指明节点的op_type,类似Java的包管理一样,用域名倒置表示。

attribute

Attribute[]

节点的属性列表。例如Conv节点的kernel shape和padding等。

doc_string

string

文档注释。

overload

string

函数的唯一ID。(added in IR version 10)

metadata_props

map<string,string>

节点的元数据。(IR version >= 10)

使用Python API获取 model.graph 各成员

# 打印 node 各个属性值

print("----------------------- model.graph.node[0].name -----------------------")

print(model.graph.node[0].name)

print("----------------------- model.graph.node[0].input -----------------------")

print(model.graph.node[0].input)

print("----------------------- model.graph.node[0].output -----------------------")

print(model.graph.node[0].output)

print("----------------------- model.graph.node[0].op_type -----------------------")

print(model.graph.node[0].op_type)

print("----------------------- model.graph.node[0].domain -----------------------")

print(model.graph.node[0].domain)

print("----------------------- model.graph.node[0].attribute -----------------------")

print(model.graph.node[0].attribute)

print("----------------------- model.graph.node[0].doc_string -----------------------")

print(model.graph.node[0].doc_string)

print("----------------------- model.graph.node[0].overload -----------------------")

print(model.graph.node[0].overload)

print("----------------------- model.graph.node[0].metadata_props -----------------------")

print(model.graph.node[0].metadata_props)

4) model.graph.initializer 结构

model.graph.initializer 是一个tensor列表,其中的元素类型为TensorProto。Initializer通常保存模型的权重参数,一些输入默认值也可以保存在这里,可以将其理解为一个tensor常量池。initializer每个元素的成员如下:

属性名

示例值

描述

name

string

该tensor的名字

dims

int[]

该tensor的维度

data_type

int

该tensor的数据类型,不同的数值代表不同个的数据类型

raw_data

bytes

该tensor保存的具体数据,二进制形式

doc_string

string

文档注释

使用Python API获取 model.graph 各成员

print("----------------------- model.graph.initializer[0].name -----------------------")

print(model.graph.initializer[0].name)

print("----------------------- model.graph.initializer[0].dims -----------------------")

print(model.graph.initializer[0].dims)

print("----------------------- model.graph.initializer[0].data_type -----------------------")

print(model.graph.initializer[0].data_type)

print("----------------------- model.graph.initializer[0].raw_data -----------------------")

# 二进制表示,打印出来可能会很长

print(model.graph.initializer[0].raw_data)

5)model.graph.input & output & value_info 结构

graph 中的 input、output 和 value_info 均为一个列表,可以使用index进行索引。Input为计算图的所有输入,output是计算图的所有输出,value_info则为计算图中所有中间计算结果tensor的信息。当使用

onnx.shape_inference.infer_shapes()推理所有中间tensor的维度时,这些信息均会保存在value_info中。input、output 和 value_info的每个元素类型为ValueInfoProto,其包含的成员如下

属性名

示例值

描述

name

string

当前值的名字

type

TypeProto

当前值的类型,这其中包含当前值的数据类型和维度

使用Python API获取 model.graph 各成员

# 以第一个input为例

print("----------------------- model.graph.input[0].name -----------------------")

print(model.graph.input[0].name)

print("----------------------- model.graph.input[0].type -----------------------")

print(model.graph.input[0].type)

3. 总结

本文重点解析了 ONNX 模型结构,并演示了如何使用Python定位到ONNX模型各个层面的元素。在得到不同元素之后,我们可以对ONNX模型进行适当的修改,使其更加适配我们的后端运行时,进一步提高推理性能。我们在之后的文章会介绍如何使用 ONNX 官方的 API 来修改ONNX模型。

作者:高通工程师,阮慧源(Huiyuan Ruan)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1548673.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaIO流与字节输出流OutputStream

1 概述 1.1 什么是IO流 IO流是存储和读取数据的解决方案&#xff0c;用于读写文件中的数据&#xff08;包括本地文件、网络等&#xff09; IO流的参照是程序或内存&#xff0c;即使程序在读&#xff0c;程序在写。 1.2 IO的分类 根据流的方向分为&#xff1a;输入流和输出流…

九州金榜|面对校园霸凌,家长应该如何教育?

近期关于校园霸凌事件接连发生&#xff0c;前有邯郸时间&#xff0c;后有福建晋江一中学生因不忍被霸凌&#xff0c;选择跳楼轻生&#xff0c;面对此类事件&#xff0c;接连发生&#xff0c;孩子为什么会成为被霸凌的对象&#xff1f;家长应该如何教育孩子敢于对霸凌时说不。下…

2024年第八届制造、材料与冶金工程国际会议(ICMMME 2024)即将召开!

2024年第八届制造、材料与冶金工程国际会议&#xff08;ICMMME 2024&#xff09;将于2024年7月12-14日在日本横滨举行。本次会议的目的是促进与会者之间的互动&#xff0c;汇聚对相关研究领域感兴趣的研究人员&#xff0c;工程师和从业人员。以了解这些领域的最新发展。为分享理…

PCB绘制时增加多层及生成GND的一些总结

1. 如何增加多层板? 【说明】由于GND大面积铺铜,常采用负片输出。因此选择“PLANE”,并勾选“negative”。 值得注意的是, 1)通常是每层之间会加一层介质,即绿色部分那个。 2)GND层通常与主器件层相邻。 2. 如何自动生成GND层? 首先使用Line命令,对不同GND进行分割…

电脑如何设置个性便签 电脑个性便签分享

每次坐在电脑前&#xff0c;我都仿佛置身于一片信息的海洋。工作、生活、学习&#xff0c;方方面面的事情都需要我用心去记录。在这样一个快节奏的时代&#xff0c;电脑无疑成了我最得力的助手。但记事的时候&#xff0c;我总希望有一个既方便又有个性的工具&#xff0c;能让我…

何恺明重提十年之争——模型表现好是源于能力提升还是捕获数据集偏置?

想象一下&#xff0c;如果把世界上所有的图片都找来&#xff0c;给它们放到一块巨大的空地上&#xff0c;其中内容相似的图片放得近一些&#xff0c;内容不相似的图片放得远一些&#xff08;类比向量嵌入&#xff09;。然后&#xff0c;我随机地向这片空地撒一把豆子&#xff0…

OpenHarmony之媒体组件模块简介

源码 本文基于OpenAtom OpenHarmony&#xff08;以下简称“OpenHarmony”&#xff09;3.2 Release源码foundation目录下的player_framework&#xff0c;在OpenHarmony 2.0 Release版本当中&#xff0c;这个模块的名字叫媒体组件模块&#xff0c;为了方便理解我们在本文中仍旧延…

【面试经典 | 150】单词拆分

文章目录 Tag题目来源解题思路方法一&#xff1a;动态规划 写在最后 Tag 【动态规划】【字符串】 题目来源 139. 单词拆分 解题思路 方法一&#xff1a;动态规划 定义状态 定义 dp[i] 表示字符串 s 前 i 个字符组成的字符串&#xff08;s[0, ..., i-1]&#xff09;是否能被…

0.96寸OLED屏调试 ----(二)

所需设备&#xff1a; 1、USB 转 SPI I2C 适配器&#xff1b;内附链接 2、0.96寸OLED显示模块&#xff1b; 备注&#xff1a;专业版、升级版都适用&#xff1b; 读写控制 SSD1306通过 D/C# 和 R/W# 两位来确定&#xff1a;读/写数据&#xff0c;写命令和读状态四种通信行为。…

AI智能分析网关V4数字农场智能监控方案

随着大数据时代的到来&#xff0c;数据成为国家基础性战略资源&#xff0c;加快数字化转型、以数字化谋求国际竞争新优势已成为全球普遍共识&#xff0c;利用大数据推动经济发展、优化社会治理、改善公共服务成为了世界各国的必然选择。农村为实现产业转型升级和治理创新&#…

安达发|磁性材料智能化转型必经之路之APS高级排产软件

在当今的制造业中&#xff0c;磁性材料的应用非常广泛&#xff0c;它们在电子、电力、通信、汽车等多个行业中扮演着重要角色。随着工业4.0和智能制造的推进&#xff0c;磁性材料行业也面临着智能化转型的挑战。在这一转型过程中&#xff0c;APS&#xff08;高级计划排程系统&a…

寻找旋转排序数组中的最小值

题目链接 寻找旋转排序数组中的最小值 题目描述 注意点 1 < n < 5000-5000 < nums[i] < 5000nums中的所有整数 互不相同nums原来是一个升序排序的数组&#xff0c;并进行了 1 至 n 次旋转找出并返回数组中的最小元素设计一个时间复杂度为 O(log n) 的算法解决此…

QT 二维坐标系显示坐标点及点与点的连线-通过定时器自动添加随机数据点

QT 二维坐标系显示坐标点及点与点的连线-通过定时器自动添加随机数据点 功能介绍头文件C文件运行过程 功能介绍 上面的代码实现了一个简单的 Qt 应用程序&#xff0c;其功能包括&#xff1a; 创建一个 MainWindow 类&#xff0c;继承自 QMainWindow&#xff0c;作为应用程序的…

YB5156是一款完整的单节锂离子电池采用恒定电流/恒定电压线性充电器

概述: YB5156是一款完整的单节锂离子电池采用恒定电流/恒定电压线性充电器。其底部带有散热片的SOP8封装与较少的外部元件数目使得YB5156成为便携式应用的理想选择。YB5156可以适合USB电源和适配器电源工作。由于采用了内部PMOSFET架构&#xff0c;加上防倒充电路&#xff0c;…

flutter Got socket error trying to find package nested at

flutter Got socket error trying to find package nested at xxx 报错信息&#xff1a;“Got socket error trying to find package nested at” 通常出现在Flutter尝试从pub.dev获取依赖包时&#xff0c;由于网络问题导致无法连接到pub.dev或者无法正确解析包的路径。 例如&…

linux进程间通信IPC方式

前言 Linux环境下&#xff0c;进程地址空间相互独立、彼此隔离&#xff0c;因此进程间的数据不能直接访问。如果要交换数据&#xff0c;必须要通过内核&#xff0c;在内核中开辟一块缓冲区&#xff0c;进程A把数据从用户空间拷贝到内核缓冲区&#xff0c;进程B再把数据从内核缓…

【leetcode】双“指针”

标题&#xff1a;【leetcode】双指针 水墨不写bug 我认为 讲清楚为什么要用双指针 比讲怎么用双指针更重要&#xff01; &#xff08;一&#xff09;快乐数 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数…

centos7.9安装mysql

1. 概述 官网&#xff1a;https://www.mysql.com/ MySQL是一个关系型数据库管理系统&#xff0c;由瑞典 MySQL AB 公司开发&#xff0c;MySQL是最流行的关系型数据库管理系统之一&#xff0c;在 WEB 应用方面&#xff0c;MySQL是最好的RDBMS (Relational Database Management S…

Linux下线程池详解与实现:提升多任务处理效率的关键

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;マイノリティ脈絡—ずっと真夜中でいいのに。 0:24━━━━━━️&#x1f49f;──────── 4:02 &#x1f504; ◀…

【教程】谈一谈 IPA 上传到 App Store Connect 的几种方法

引言 在应用开发过程中&#xff0c;将应用程序上传到 App Store Connect 是一个关键的环节。本文将探讨几种常见的 IPA 文件上传方法&#xff0c;包括 Xcode、Application Loader、altool、Appuploader以及Transporter。通过本文的介绍和指导&#xff0c;读者将能够了解不同的…