深度学习模型部署(五)onnx模型以及相应工具

news2024/11/17 19:34:25

在这里插入图片描述

ONNX概念

onnx不仅仅一种模型参数存储的格式,还是一套完整的用于描述计算函数的编程语言,它的作用就是定义计算图,他本身无法进行。

这个概念和Verilog有点像,一个是硬件描述语言,一个是模型描述语言。

onnx模型或者说计算图,是由这几部分组成:

  • input,output:输入输出
  • node:节点,即算子,算子的固定参数保存在attribute中
  • initializer:初始化器,用于加载函数

此外onnx还允许在模型中添加一些元数据,用于记录作者,模型版本等信息,类似于注释一样。onnx模型中的元数据有:

  • doc_string:人类可读的文档,可以用markdown
  • domain:不知道干啥用的,反正存个模型名字
  • metadata_props:是个字典类型的,不知道干啥使的
  • model_author:模型的作者
  • model_license:模型的版权协议
  • model_version:模型版本
  • producer_name:训练模型的框架
  • producer_version:训练框架的版本
  • training_info:训练信息

Onnx存储方式是使用protobuf来存储,protobuf是Protocol Buffers的简称,它是Google公司开发的一种数据描述语言,很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。它的定位类似于xml和json这种。

Onnx支持的operator

Onnx将支持的operator存储到两个domain中,一个domain是ai.onnx,存储了大量深度学习常用operator,另一个是ai.onnx.ml,存储了树模型以及一些机器学习中的operator。

Onnx支持的数据类型

基本上主流的数据类型都支持,例如fp32,fp16,int4,int8,int16等.Onnx中支持的数据类型:

 0: onnx.TensorProto.UNDEFINED
 1: onnx.TensorProto.FLOAT
 2: onnx.TensorProto.UINT8
 3: onnx.TensorProto.INT8
 4: onnx.TensorProto.UINT16
 5: onnx.TensorProto.INT16
 6: onnx.TensorProto.INT32
 7: onnx.TensorProto.INT64
 8: onnx.TensorProto.STRING
 9: onnx.TensorProto.BOOL
10: onnx.TensorProto.FLOAT16
11: onnx.TensorProto.DOUBLE
12: onnx.TensorProto.UINT32
13: onnx.TensorProto.UINT64
14: onnx.TensorProto.COMPLEX64
15: onnx.TensorProto.COMPLEX128
16: onnx.TensorProto.BFLOAT16
17: onnx.TensorProto.FLOAT8E4M3FN
18: onnx.TensorProto.FLOAT8E4M3FNUZ
19: onnx.TensorProto.FLOAT8E5M2
20: onnx.TensorProto.FLOAT8E5M2FNUZ
21: onnx.TensorProto.UINT4
22: onnx.TensorProto.INT4

ONNX是强类型的语言,不支持数据类型之间的隐式转换,想要转换必须添加显式转换。
ONNX支持2维的稀疏张量,类型为SparseTensorProto

稀疏向量是指0比较多的向量,可以用特殊的方式来存储以减少空间占用。

opset version

ONNX的算子库是不断更新的,opset version就是算子库版本,onnx版本和opset的版本是对应的,每次算子库版本更新都会引入新的算子。模型本身也要指定一个算子版本来表示模型所依赖的算子的版本。例如6,7,13,14版本的算子库对Add算子进行了更新,如果模型指定的算子库版本是15,那么Add算子将使用14版本的实现。

Onnx控制流

ONNX支持控制流算子例如if,但是这样会降低性能,最好避免控制流算子。

if算子

根据条件决定执行哪个子图,但是子图的输出shape和num必须是一样的,子图的输出将作为if算子的输出。
下面是一个简单的模型的搭建过程,手搓模型就是这么搓的。

import onnx
import numpy as np
# Given a bool scalar input cond.
# return constant tensor x if cond is True, otherwise return constant tensor y.

cond = onnx.helper.make_tensor_value_info( # 创建输入
    "cond", onnx.TensorProto.BOOL, []
)

then_out = onnx.helper.make_tensor_value_info( # 创建then输出
    "then_out", onnx.TensorProto.FLOAT, [5]
)
else_out = onnx.helper.make_tensor_value_info( # 创建else输出
    "else_out", onnx.TensorProto.FLOAT, [5]
)

x = np.array([1, 2, 3, 4, 5]).astype(np.float32) # 创建then输出的值
y = np.array([5, 4, 3, 2, 1]).astype(np.float32) # 创建else输出的值

then_const_node = onnx.helper.make_node( # 创建then输出的节点
    "Constant",
    inputs=[],
    outputs=["then_out"],
    value=onnx.numpy_helper.from_array(x),
)

else_const_node = onnx.helper.make_node( # 创建else输出的节点
    "Constant",
    inputs=[],
    outputs=["else_out"],
    value=onnx.numpy_helper.from_array(y),
)

then_body = onnx.helper.make_graph( # 创建then的子图
    [then_const_node], "then_body", [], [then_out]
)

else_body = onnx.helper.make_graph( # 创建else的子图
    [else_const_node], "else_body", [], [else_out]
)

if_node = onnx.helper.make_node( # 创建if节点
    "If",
    inputs=["cond"],
    outputs=["res"],
    then_branch=then_body,
    else_branch=else_body,
)

res = onnx.helper.make_tensor_value_info("res", onnx.TensorProto.FLOAT, [5]) # 创建输出,这个输出是if节点的输出
graph = onnx.helper.make_graph( # 创建主图
    [if_node], "test_if", [cond], [res]
)
onnx.save_model( # 保存模型
    onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid("", 11)]),
    "if.onnx",
)

在这里插入图片描述
模型结构图

Scan循环算子

scan算子有一个全局变量,只需要开始时输入进行初始化,还有一个输入,每次循环都输入,这两个是并在一个输入里面的,scan的输出也是,一个输出是全局变量最后的值,另一个输出是每次循环的输出结合到一起,比如:全局变量是N维度,每次输入是M维,每次输出也是M维,循环k次,那么scan的输入就是:[N,kM],输出是:[N,kM]。

# Given an input sequence [x1, ..., xN], sum up its elements using a scan
# returning the final state (x1+x2+...+xN) as well the scan_output
# [x1, x1+x2, ..., x1+x2+...+xN]
#
# create graph to represent scan body
import numpy as np
import onnx

sum_in = onnx.helper.make_tensor_value_info(
    "sum_in", onnx.TensorProto.FLOAT, [2]
)
next = onnx.helper.make_tensor_value_info("next", onnx.TensorProto.FLOAT, [2])
sum_out = onnx.helper.make_tensor_value_info(
    "sum_out", onnx.TensorProto.FLOAT, [2]
)
scan_out = onnx.helper.make_tensor_value_info(
    "scan_out", onnx.TensorProto.FLOAT, [2]
)
add_node = onnx.helper.make_node(
    "Add", inputs=["sum_in", "next"], outputs=["sum_out"]
)
id_node = onnx.helper.make_node(
    "Identity", inputs=["sum_out"], outputs=["scan_out"]
)
scan_body = onnx.helper.make_graph(
    [add_node, id_node], "scan_body", [sum_in, next], [sum_out, scan_out]
)
# create scan op node
node = onnx.helper.make_node(
    "Scan",
    inputs=["initial", "x"],
    outputs=["y", "z"],
    num_scan_inputs=1,
    body=scan_body,
)
# create inputs for sequence-length 3, inner dimension 2
initial = np.array([0, 0]).astype(np.float32).reshape((2,))
x = np.array([1, 2, 3, 4, 5, 6]).astype(np.float32).reshape((3, 2))
# final state computed = [1 + 3 + 5, 2 + 4 + 6]
y = np.array([9, 12]).astype(np.float32).reshape((2,))
# scan-output computed
z = np.array([1, 2, 4, 6, 9, 12]).astype(np.float32).reshape((3, 2))

# create graph
initial_info = onnx.helper.make_tensor_value_info(
    "initial", onnx.TensorProto.FLOAT, initial.shape
)
x_info = onnx.helper.make_tensor_value_info("x", onnx.TensorProto.FLOAT, x.shape)
y_info = onnx.helper.make_tensor_value_info("y", onnx.TensorProto.FLOAT, y.shape)
z_info = onnx.helper.make_tensor_value_info("z", onnx.TensorProto.FLOAT, z.shape)
graph = onnx.helper.make_graph(
    [node], "test_scan", [initial_info, x_info], [y_info, z_info]
)
# create model
model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid("", 11)])
# save model
onnx.save_model(model, "scan.onnx")

#inference
import onnxruntime as rt
sess = rt.InferenceSession("scan.onnx")
res = sess.run(None, {"initial": initial, "x": x})
print(res)

LOOP算子

就常见的循环,跟scan差不多,不过没有全局变量,输入也是每次循环输入一个,输出是每个循环的输出结合到一起,结合方式有两种:一种是结合成一个大的tensor,另一种是多个tensor结合成一个sequence,前者要求每个循环的输出的shape必须可以相互兼容才能结合。

扩展算子

ONNX允许自定义算子,这部分内容比较多,后面再写blog专门讲

Function

我的理解是:有的模型中的层是多个算子结合到一起形成的,比如yolo中的C3,这种不需要再自定义算子,可以把几个需要的算子结合到一起形成一个Function。官方说这样做的好处是:可以减小代码量,可以给推理引擎额外信息,推理引擎可以用这些信息做优化,比如为一些Function进行底层实现。

官方文档原文:Functions are one way to extend ONNX specifications. Some model requires the same combination of operators. This can be avoided by creating a function itself defined with existing ONNX operators. Once defined, a function behaves like any other operators. It has inputs, outputs and attributes.
There are two advantages of using functions. The first one is to have a shorter code and easier to read. The second one is that any onnxruntime can leverage that information to run predictions faster. The runtime could have a specific implementation for a function not relying on the implementation of the existing operators

类型推理

Onnx可以推理出模型的输出的数据类型以及大小,但是对于有自定义算子的模型不可以。

工具

  • netron:可视化工具,自行百度下载
  • onnx2py.py:根据onnx模型反生成一个py文件,这个py脚本可以生成这个模型,用于让用户修改模型,例如想要修改一个模型,可以先生成“可以生成这个模型的脚本”,然后再修改这个脚本,再用这个修改过的脚本生成模型。
  • onnx-graphsurgeon:TensorRT做的一个工具,可以用于修改onnx模型,名字翻译过来就是图手术刀

如果觉得有帮助,点赞收藏+关注!thanks!

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

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

相关文章

Learn OpenGL 10 Assimp+网格+模型

Assimp 一个非常流行的模型导入库是Assimp,它是Open Asset Import Library(开放的资产导入库)的缩写。Assimp能够导入很多种不同的模型文件格式(并也能够导出部分的格式),它会将所有的模型数据加载至Assim…

git基础命令(一)

目录 基础概念git statusgit addgit diffgit loggit commit文件可以处于以下三种状态之一远程存储库与本地存储库参考 用于知识记录。后续有新的的内容,例子,将持续更新本文档。 基础概念 工作树:git add 之前,变动内容的文件列表…

经典数组和指针笔试题解析——C语言

【本节内容】 1. 数组和指针笔试题解析 2. 指针运算笔试题解析 1. 数组和指针笔试题解析 1.1 一维数组 #include <stdio.h> int main() {int a[] { 1,2,3,4 };printf("%zd\n", sizeof(a));printf("%zd\n", sizeof(a 0));printf("%zd\n&qu…

【金三银四】掌趣科技24.3.7 H项目 服务端开发笔试题

考试题型&#xff1a; 不定项选择题 10 道 &#xff0c; 填空题 10 道 &#xff0c; 问答题 2 道 &#xff0c; 编程题 4 道 目录 不定项选择题 10 道填空题 10 道问答题 2 道编程题 4 道 不定项选择题 10 道 在TCP协议中&#xff0c;发送方的窗口大小是由两个关键因素共同决定…

三个表联合查询的场景分析-场景1:a表关联了b表和c表

本场景对应情景如下&#xff1a; 三个数据表&#xff0c;一个表的两个字段分别关联了另外两个表各自的id数据&#xff0c;可能包含多个id&#xff08;两个1对多关联&#xff09;。 目录 数据表准备 需求1、查询c表的列表数据&#xff0c;要求获得关联的b表中的name&#xf…

工业界真实的推荐系统(小红书)-涨指标的方法:召回、排序、多样性、特殊人群、利用交互行为

课程特点&#xff1a;系统、清晰、实用&#xff0c;原理和落地经验兼具 b站&#xff1a;https://www.bilibili.com/video/BV1HZ421U77y/?spm_id_from333.337.search-card.all.click&vd_sourceb60d8ab7e659b10ea6ea743ede0c5b48 讲义&#xff1a;https://github.com/wangsh…

CSDN 编辑器设置图片缩放和居中

CSDN 编辑器设置图片缩放和居中 文章目录 CSDN 编辑器设置图片缩放和居中对齐方式比例缩放 对齐方式 Markdown 编辑器插入图片的代码格式为 ![图片描述](图片路径)CSDN 的 Markdown 编辑器中插入图片&#xff0c;默认都是左对齐&#xff0c;需要设置居中对齐的话&#xff0c;…

项目性能优化—性能优化的指标、目标

项目性能优化—性能优化的指标、目标 性能优化的终极目标是什么 性能优化的目标实际上是为了更好的用户体验&#xff1a; 一般我们认为用户体验是下面的公式&#xff1a; 用户体验 产品设计&#xff08;非技术&#xff09; 系统性能 ≈ 系统性能 快 那什么样的体验叫快呢…

STM32第九节(中级篇):RCC——时钟树讲解(第一节)

目录 前言 STM32第九节&#xff08;中级篇&#xff09;&#xff1a;RCC——时钟树讲解 时钟树主系统时钟讲解 HSE时钟 HSI时钟 锁相环时钟 系统时钟 SW位控制 HCLK时钟 PCLKI时钟 PCLK2时钟 RTC时钟 MCO时钟输出 6.2.7时钟安全系统(CSS&#xff09; 小结 前言 从…

基于HarmonyOS ArkTS中秋国庆祝福程序、以代码之名,写阖家团圆祝福

中秋、国庆双节将至&#xff0c;作为程序员&#xff0c;以代码之名&#xff0c;表达对于阖家团圆的祝福。本节将演示如何在基于HarmonyOS ArkUI的SwiperController、Image、Swiper等组件来实现节日祝福轮播程序。 规则要求具体要求如下&#xff1a; 1、根据主题&#xff0c;用…

遗嘱消息(Will Message)介绍与示例 _ MQTT 5.0 特性详解

什么是 MQTT 遗嘱消息&#xff1f; 在现实世界中&#xff0c;一个人可以制定一份遗嘱&#xff0c;声明在他去世后应该如何分配他的财产以及应该采取什么行动。在他去世后&#xff0c;遗嘱执行人会将这份遗嘱公开&#xff0c;并执行遗嘱中的指示。 在 MQTT 中&#xff0c;客户端…

honle电源维修UV电源控制器维修EVG EPS60

好乐UV电源控制器维修&#xff1b;honle控制器维修&#xff1b;UV电源维修MUC-Steuermodul 2 LΛmpen D-82166 主要维修型号&#xff1a; EVG EPS 60/120、EVG EPS 100、EVG EPS200、EVG EPS 220、EVG EPS 340、EVG EPS40C-HMI、EVG EPS60 HONLE好乐uv电源维修故障包括&#…

Python实时追踪关键点组成人体模型

项目背景 最近遇到这样一个需求&#xff1a; 1&#xff1a;实时追踪关键点组成人体模型&#xff08;手臂包括三个点&#xff1a;手腕&#xff0c;肘关节&#xff0c;双肩&#xff1b;腿部包括胯骨&#xff0c;膝盖&#xff0c;脚踝&#xff09; 2&#xff1a;运用追踪到的关键…

【c++】string类的使用及模拟实现

1.我们为什么要学习string类&#xff1f; 1.1 c语言中的字符串 我们先了解一下什么是OOP思想 OOP思想&#xff0c;即面向对象编程&#xff08;Object-Oriented Programming&#xff09;的核心思想&#xff0c;主要包括“抽象”、“封装”、“继承”和“多态”四个方面。 抽象…

Day16 面向对象进阶——接Day15

Day16 面向对象进阶——接Day15 文章目录 Day16 面向对象进阶——接Day15一、抽象类及抽象方法二、接口三、多态四、对象转型五、内部类 一、抽象类及抽象方法 //抽象类 public abstract class 类名{//抽象方法public abstract void method(); }1、抽象方法交给非抽象的子类去…

轻松驾驭时间流:MYSQL日期与时间函数的实用技巧

​&#x1f308; 个人主页&#xff1a;danci_&#x1f525; 系列专栏&#xff1a;《MYSQL应用》&#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 轻松驾驭时间流&#xff1a;MYSQL日期与时间函数的实用技巧 MYSQL日期时间函数是数据库操作中不可…

第五十八回 吴用赚金铃吊挂 宋江闹西岳华山-飞桨图像分割套件PaddleSeg初探

鲁智深被贺太守抓住&#xff0c;押入死牢。武松得信后&#xff0c;正想回梁山报信&#xff0c;正好戴宗来了&#xff0c;就请戴宗赶快回梁山搬救兵。宋江说兄弟有难&#xff0c;怎能不救&#xff1f; 于是带了十六个头领来到少华山。 因为华州城池厚壮&#xff0c;宋江等无计可…

Selenium 自动化 —— 入门和 Hello World 实例

Selenium 是什么 Selenium 是一个用于自动化网页浏览器操作的工具&#xff0c;它支持多种浏览器和多种操作系统。主要用于测试 web 应用程序的功能&#xff0c;也可用于执行一些基本的浏览器操作任务&#xff0c;例如自动化表单填写、网页导航等。 Selenium 是一个开源项目&a…

扒带和扒谱的区别 FL Studio怎么扒带 扒带编曲制作 扒带简单歌曲

在许多业余音乐爱好者们的眼里&#xff0c;扒带和扒谱是同一种东西。诚然&#xff0c;扒带和扒谱的确非常相似&#xff0c;但是从严格的意义上来说&#xff0c;这二者还是有一定的区别。今天我们就来说一说扒带和扒谱的区别&#xff0c;FL Studio怎么扒带。 FL Studio21中文官网…

.Net使用ElasticSearch

文章目录 前言主体内容一.Kibana中ElasticSearch的基础操作1.GET&#xff08;查询&#xff09;1.POST&#xff08;新增&#xff09;1.PUT&#xff08;修改&#xff09;1.DELET&#xff08;删除&#xff09; 二.在.Net中&#xff0c;对ElasticSearch进行基础操作1.DotNet连接Ela…