【自制C++深度学习推理框架】计算图的设计思路

news2024/11/26 10:01:05

计算图的设计思路

什么是计算图

在深度学习推理框架中,计算图是一种数据结构,它由算子节点数据节点组成,在该图中前向传播时数据从输入节点开始流动,经过一层层的计算后输出到输出节点,表示深度学习模型的计算过程。

与神经网络架构图类似,计算图也是一种有向图,使用节点来表示操作变量,并使用边来表示它们之间的依赖关系。不同之处在于,神经网络架构图通常只描述了神经元之间的连接方式,而计算图可以精确地表示深度学习模型的计算逻辑。

计算图和神经网络架构图的区别在于,神经网络架构图仅仅是一张有连接关系的图,它主要用来可视化模型的结构展示,包括每层神经元的数目、连接方式等;而计算图则更关注深度学习模型的计算过程,包括每个节点的具体操作定义、输入输出张量的尺寸大小等。

此外,计算图还支持对图上的操作进行修改,如节点之间的添加、删除、重组等。

为什么要封装PNNX作为本项目的计算图

PyTorch Neural Network eXchange(PNNX)是一个旨在提高深度学习模型部署效率的开源工具,由PyTorch社区推出。

PNNX充分利用了PyTorch的动态图机制和ONNX的静态图机制,实现了模型的转换和优化,并提供了一些功能强大的应用程序接口,可帮助用户快速地将训练好的PyTorch模型部署到生产环境中。

PNNX帮我做了很多的图优化、算子融合的工作,所以底层的用它PNNX的话,我们可以吸收图优化的结果,后面推理更快。

PNNX的格式定义

Operator(操作符)

  • Inputs: std::vector<operand*>,输入操作数
  • Outputs: std::vector<operand*>,输出操作数
  • Type: std::string,运算符的类型
  • Name: std::string,运算符的名称
  • Params: std::map,存放运算符的所有参数,例如卷积运算的stride, padding, kernel size
  • Attrs: std::map,存放运算符所需的具体权重属性,例如卷积的权重w和偏移量b

Operand(操作数)

  • Producer: operator,产生这个操作数的运算符,表示运算符的输出,只能有一个生产者
  • Customer: operator,下一个操作需要该操作数作为输入的运算符,表示运算符的输入,可以有多个消费者
  • Name: std::string,操作数的名称
  • shape: std::vector,操作数的维度

算子节点(计算节点)的定义

在本项目中,我们参考PNNX::operator和PNNX::operand,定义了RuntimeOperator和RuntimeOperand。

RuntimeOperand

  • std::string name; /// 操作数的名称
  • std::vector<int32_t> shapes; /// 操作数的形状
  • std::vector<std::shared_ptr<Tensor>> datas; /// 存储操作数
  • RuntimeDataType type = RuntimeDataType::kTypeUnknown; /// 操作数的类型,一般是float

RuntimeOperator

  • int32_t meet_num = 0; /// 计算节点被相连接节点访问到的次数

  • std::string name; /// 计算节点的名称

  • std::string type; /// 计算节点的类型

  • std::shared_ptr layer; /// 节点对应的计算Layer

  • std::map<std::string, RuntimeParameter*> params; /// 算子的参数信息

  • std::map<std::string, std::shared_ptr> attribute; /// 算子的属性信息,内含权重信息

  • std::vectorstd::string output_names; /// 后继节点名称

  • std::map<std::string, std::shared_ptr> output_operators; /// 后继节点

  • std::map<std::string, std::shared_ptr> input_operands; /// 节点的输入操作数

  • std::vector<std::shared_ptr> input_operands_seq; /// 节点的输入操作数,顺序排列

  • std::shared_ptr output_operands; /// 节点的输出操作数

值得注意的是,我们把算子节点拆分成输入操作数输出操作数算子的属性参数以及算子的实现

算子参数RuntimeParameter拷贝pnnx::Parameter,表示具体的权重数值、偏移数值等;

算子属性RuntimeAttribute拷贝pnnx::Attribute,表示具体的权重维度尺寸、偏移维度尺寸等;

在深度学习框架中,算子的实现抽象为layer,也就是神经网络架构中的层,

使用算子参数和算子属性来实例化layer,然后在layerforward前向传播函数中,以输入操作数和输出操作数作为输入,把计算结果保存到输出操作数中。

此外,RuntimeOperator存储了后继节点的名称和指针,方便我们构建计算图。

如何构建计算图

RuntimeGraph的定义

RuntimeGraph中有一个存放RuntimeOperator指针的vector,而RuntimeOperator中有一个存储后继节点的成员变量,因此只需要记录输入和输出节点,就可以构成一个计算图了。

  • GraphState:一个枚举类,代表计算图的状态。具体来说,枚举值为NeedInit表示计算图需要初始化,NeedBuild表示计算图需要构建,Complete表示计算图已完成。
  • graph_state_:一个GraphState类型的变量,用于保存计算图的状态。
  • input_name_:一个字符串类型的变量,表示计算图输入节点的名称。
  • output_name_:一个字符串类型的变量,表示计算图输出节点的名称。
  • param_path_:一个字符串类型的变量,表示计算图的结构文件路径。
  • bin_path_:一个字符串类型的变量,表示计算图的权重文件路径。
  • input_operators_maps_:一个映射类型的变量,保存计算图的输入节点和对应的RuntimeOperator对象。
  • output_operators_maps_:一个映射类型的变量,保存计算图的输出节点和对应的RuntimeOperator对象。
  • operators_:一个vector类型的变量,保存计算图的中间计算节点和对应的RuntimeOperator对象。
  • graph_:一个pnnx::Graph类型的智能指针,用于保存完整的计算图。

RuntimeGraph初始化过程

  1. 首先判断计算图的结构文件和权重文件路径是否为空。如果其中有任何一个为空,就输出错误信息并返回false,表示初始化失败。

  2. 使用pnnx库中的load()函数从计算图的结构文件和权重文件中读取完整的计算图,保存在类的私有成员变量graph_中。如果读取出错,则输出错误信息并返回false,表示初始化失败。

  3. 从graph_中获取所有的Operator对象,保存在一个vector类型的变量operators中。如果operators为空,说明读取图层定义失败,返回false,表示初始化失败。

  4. 遍历operators,对于每一个Operator对象,做以下几个操作:

  • 创建一个RuntimeOperator对象,并初始化其名称、类型等基本属性。

  • 获取Operator的输入Operand,调用InitGraphOperatorsInput()函数将其保存在RuntimeOperator对象中。

  • 获取Operator的输出Operand,调用InitGraphOperatorsOutput()函数将其保存在RuntimeOperator对象中。

  • 获取Operator的Attribute(权重),调用InitGraphAttrs()函数将其保存在RuntimeOperator对象中。

  • 获取Operator的Parameter,调用InitGraphParams()函数将其保存在RuntimeOperator对象中。

  • 将处理完毕的RuntimeOperator对象保存在一个vector类型的变量operators_中。

  1. 将计算图的状态设置为NeedBuild,表示准备好了计算图的推理。

  2. 返回true,表示初始化成功。

构建计算图中各个节点之间的关系

  1. 首先检查计算图的状态。如果其状态是NeedInit,则调用Init()函数初始化计算图;如果状态小于NeedBuild,则输出错误信息并返回。

  2. 检查计算图中是否存在节点。如果不存在,说明计算图没有成功初始化,输出错误信息并返回。

  3. 如果计算图状态已经是Complete,说明计算图已经构建完成,直接返回即可。

  4. 对计算图中的每一个节点进行遍历,分别获取其所有的输出节点名称,并寻找其余节点中是否存在名称符合这些输出节点名称的节点。如果存在则将其余节点保存到当前节点的后继节点列表中。

  5. 构建完毕后,清空输入节点和输出节点列表,对于每一个节点,根据类型创建相应的Layer对象,并将其保存在节点的layer成员变量中。

  6. 初始化各个节点的输入和输出数据格式。

  7. 将计算图状态设置为Complete。

  8. 保存输入和输出节点名称。

  9. 释放graph_智能指针,以便释放计算图的内存占用。

计算图的前向传播过程

计算图的前向传播采用了图的广度优先搜索算法来执行。

广度优先搜索执行的实现是使用一个队列,将一个节点的入度为0的后继节点放入到队列中,并在下一轮循环中按照先进先出的顺序对队列中的节点进行执行。

图的前向传播

  1. 检查计算图状态是否为Complete。如果状态小于Complete,则输出错误信息并中止推理。

  2. 在计算图中找到输入节点和输出节点。

  3. 创建一个待执行队列,将输入节点添加到队列的末尾。

  4. 如果开启debug模式,输出一些调试信息。

  5. 开始遍历待执行队列,每次从队列头取出一个节点。如果当前节点是输出节点,则说明前向传播结束;否则根据当前节点类型选择相应的运行流程。

  • 如果当前节点是输入节点,直接将输入数据拷贝到当前节点后继节点中,向队列尾部添加后继节点。

  • 如果当前节点是其他待执行节点,则判断是否就绪。

    • 如果就绪,就准备输入的数据,调用其layer对象的Forward函数进行前向推理,得到节点的输出,调用ProbeNextLayers()函数寻找其后继节点,把节点的输出保存在其后继节点中,并将这些节点添加到待执行队列尾部。
    • 如果未就绪,就需要重新被放入到待执行队列当中。
  1. 记录所有节点的运行时间。

  2. 最终返回输出节点的输出。

阅读的代码

  • include
    • runtime
      • ir.h
      • runtime_ir.hpp
      • runtime_parameter.hpp
      • runtime_attr.hpp
      • runtime_datatype.hpp
      • runtime_operand.hpp
      • runtime_op.hpp
      • store_zip.hpp
  • source
    • runtime
      • ir.cpp
      • runtime_ir.cpp
      • runtime_op.cpp
      • store_zip.cpp

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

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

相关文章

Unity Shader variants (shader 变体)

官方地址 https://docs.unity3d.com/cn/2022.2/Manual/SL-MultipleProgramVariants.html 教程可以看这里 https://www.jianshu.com/p/48ad75f0b4b9 https://www.jianshu.com/p/3e6b84317097 变种用我自己的理解就是 能用程序控制的shader 举个例子 这里声明了 a b c d 四个变…

数据结构与算法03:栈

目录 什么是栈&#xff1f; 栈在函数调用中的应用 栈的应用&#xff1a;如何实现浏览器的前进和后退功能&#xff1f; 每日一练&#xff1a;左右括号匹配 什么是栈&#xff1f; 简单地说&#xff0c;先进后出&#xff0c;后进先出的数据结构就是栈&#xff0c;可以理解为一…

面试题:什么是 TCP/IP?

目录标题 什么是 TCP/IP?1) 网络接口层:2) 网络层:3) 传输层:4) 应用层: 2.数据包3.网络接口层4.网络层1) IP:2)地址解析协议 ARP3)子网 5 传输层1&#xff09;UDP&#xff1a;2&#xff09;TCP&#xff1a; 6 应用层运行在TCP协议上的协议&#xff1a;运行在UDP协议上的协议&…

大模型即将改变世界,百度先上牌桌

“未来&#xff0c;所有的应用都将基于大模型来开发&#xff0c;每一个行业都应该有属于自己的大模型&#xff0c;大模型会深度融合到实体经济当中去。” 作者|思杭 斗斗 编辑|皮爷 出品|产业家 “大模型即将改变世界。”5月26日&#xff0c;李彦宏在中关村论坛说道。 而…

ESP32CAM开发板记录

忘记过去&#xff0c;超越自己 ❤️ 博客主页 单片机菜鸟哥&#xff0c;一个野生非专业硬件IOT爱好者 ❤️❤️ 本篇创建记录 2023-05-26 ❤️❤️ 本篇更新记录 2023-05-26 ❤️&#x1f389; 欢迎关注 &#x1f50e;点赞 &#x1f44d;收藏 ⭐️留言&#x1f4dd;&#x1f64…

【问题小记】解决Linux下php-fpm进程过多耗尽内存问题

最近一段时间&#xff0c;发现经常性的服务器内存耗尽&#xff0c;导致mysql服务down掉&#xff0c;一开始以为是mysql跑的太久占用较多内存&#xff0c;后来认真排查了一下原来是是PHP-FPM进程过多导致的。 今天一看内存又达到了82%&#xff0c;预计不会太久服务又会挂掉&…

深入探索: 对象构造的隐藏功能与技巧

&#x1f9d1;‍&#x1f4bb;CSDN主页&#xff1a;夏志121的主页 &#x1f4cb;专栏地址&#xff1a;Java基础进阶核心技术专栏 目录 &#x1f383; 一、重载 &#x1f384; 二、默认字段初始化 &#x1f386; 三、无参数的构造器 ✨ 四、显式字段初始化 &#x1f38a; 五…

Vue(路由插件)

一、介绍路由 1. 路由就是一组key-value的对关系&#xff0c;多个路由需要经过路由器进行管理 2. 主要应用在SPA&#xff08;单页面应用&#xff09; 在一个页面展示功能之间的跳转 特点&#xff1a; 当跳转时候不进行页面刷新路径随着变化展示区变化但是不开启新的页签 …

总结丨SGAT单基因关联分析工具,一文上手使用

SGAT是一个免费开源的单基因分析工具&#xff0c;基于Linux系统实现自动化批量处理&#xff0c;能够快速准确的完成单基因和表型的关联分析&#xff0c;只需要输入基因型和表型原始数据&#xff0c;即可计算出显著关联的SNP位点&#xff0c;并自动生成结果报告。 前段时间陆续的…

YOLOv5白皮书-第Y4周:common.py文件解读

目录 0.导入需要的包和基本配置1.基本组件1.1 autopad1.2 Conv1.3 Focus1.4 Bottleneck1.5 BottleneckCSP1.6 C31.7 SPP1.8 Concat1.9 Contract、Expand 2.重要类2.1 非极大值抑制&#xff08;NMS&#xff09;2.2 AutoShape2.3 Detections2.4 Classify &#x1f368; 本文为&am…

【头歌实训】【基于 Logisim 的 RISC-V 处理器设计 · 终】

真的恶心&#xff0c;我哭死 目录 前言 一、说明 1、参考 2、建议 二、处理器设计 三、Control器件设计 1、加速经常性事件&#xff0c;提高效率 2、控制信号设置 1.RegWEn 2.IMMSel 3.BSel 4.ALUSel & WBSel 5.MemWEn 6.PCSel & ASel 7.ALUB 总结…

【C语言】标准库(头文件、静态库、动态库),windows与Linux平台下的常用C语言标准库

一、Introduction1.1 C语言标准库1.2 历代C语言标准1.3 主流C语言编译器 二、C语言标准库2.1 常用标准头文件2.2 常用标准静态库 三、windows平台四、Linux平台五、常用头文件功能速览5.1 通用常用头文件01. stdio.h——标准输入输出02. stdlib.h——内存管理与分配、随机数、字…

Git常用命令reset和revert

Git常用命令reset和revert 1、reset 用于回退版本&#xff0c;可以指定退回某一次提交的版本。 checkout 可以撤销工作区的文件&#xff0c;reset 可以撤销工作区/暂存区的文件。 reset 和 checkout 可以作用于 commit 或者文件&#xff0c;revert 只能作用于 commit。 命…

为什么 String#equals 方法在做比较时没有使用 hashCode

一个疑问的引入 我之前出于优化常数项时间的考虑&#xff0c;想当然的认为 String#equals 会事先使用 hashCode 进行过滤 我想像中的算法是这样的 当两个 hashCode 不等时&#xff0c;直接返回 false&#xff08;对 hash 而言&#xff0c;相同的输入会得到相同的输出&#x…

数据安全复合治理框架和模型解读(0)

数据治理,数据安全治理行业在发展,在实践,所以很多东西是实践出来的,哪有什么神仙理论指导,即使有也是一家之说,但为了提高企业投产比,必要的认知是必须的,当前和未来更需要专业和创新。数据安全治理要充分考虑现实数据场景,强化业务安全与数据安全治理,统一来治理,…

学会了程序替换,我决定手写一个简易版shell玩一玩...

文章目录 &#x1f490;专栏导读&#x1f490;文章导读&#x1f427;程序进程替换&#x1f426;替换原理&#x1f426;替换函数&#x1f414;观察与结论&#x1f414;函数命名理解 &#x1f427;myshell编写&#x1f514;代码展示&#x1f514;效果展示 &#x1f427;myshell_p…

Vue电商项目--分页器制作

分页器静态组件 分页这个组件&#xff0c;不单单是一个页面用到了。多个页面同时用它,因此我们可以封装成一个全局组件 需要将这个分页结构拆分到components 通用的分页组件Pagination <template><div class"pagination"><button>1</butto…

【C语言】函数规则及入门知识

&#x1f6a9;纸上得来终觉浅&#xff0c; 绝知此事要躬行。 &#x1f31f;主页&#xff1a;June-Frost &#x1f680;专栏&#xff1a;C语言 ⚡注&#xff1a;此篇文章的 部分内容 将根据《高质量 C/C 编程指南》 —— 林锐 进行说明。该部分将用橙色表示。 &#x1f525;该篇…

新手建站:使用腾讯云轻量服务器宝塔面板搭建WP博客教程

腾讯云轻量应用服务器怎么搭建网站&#xff1f;太简单了&#xff0c;轻量服务器选择宝塔Linux镜像&#xff0c;然后在宝塔面板上添加站点&#xff0c;以WordPress建站为例&#xff0c;腾讯云服务器网来详细说下腾讯云轻量应用服务器搭建网站全流程&#xff0c;包括轻量服务器配…

html5视频播放器代码实例(含倍速、清晰度切换、续播)

本文将对视频播放相关的功能进行说明&#xff08;基于云平台&#xff09;&#xff0c;包括初始化播放器、播放器尺寸设置、视频切换、倍速切换、视频预览、自定义视频播放的开始/结束时间、禁止拖拽进度、播放器皮肤、控件按钮以及播放控制等。 图 / html5视频播放器调用效果&a…