clean commit messages.

This commit is contained in:
Luo Mai
2022-03-01 15:22:19 +00:00
parent 9ba4715573
commit 86661feadd
115 changed files with 6994 additions and 67 deletions

3
.gitignore vendored
View File

@@ -10,4 +10,5 @@ dist*
_build/
test*.md
run.sh
.idea
.idea
env

12
INFO.md
View File

@@ -1,21 +1,17 @@
## 环境安装
机器学习系统书籍部署在Github是依赖于d2lbook工具实现的。因此我们首先要安装d2lbook。
```
pip install git+https://github.com/d2l-ai/d2l-book
```
使用pip如果不能安装成功可以通过git clone下载代码安装
```
```bash
git clone git@github.com:d2l-ai/d2l-book.git
cd d2l-book
python setup.py install
```
使用d2lbook构建HTML需要安装pandoc,可以使用pip install pandoc
构建PDF时如果有SVG图片需要安装LibRsvg来转换SVG图片安装librsvg可以通过pip install librsvg。
使用d2lbook构建HTML需要安装`pandoc`, 可以使用`apt-get install pandoc`如果是MacOS可以用Homebrew
构建PDF时如果有SVG图片需要安装LibRsvg来转换SVG图片安装`librsvg`可以通过`apt-get install librsvg`如果是MacOS可以用Homebrew
当然构建PDF必须要有LaTeX如安装[Tex Live](https://www.tug.org/texlive/).
## 编译HTML版本
在编译前先下载[openmlsys-zh](https://github.com/openmlsys/openmlsys-zh) 所有的编译命令都在改文件目录内执行。
```
```bash
git clone git@github.com:openmlsys/openmlsys-zh.git
cd openmlsys-zh
```

View File

@@ -1,2 +1,60 @@
# 机器学习系统:设计和实现
本开源项目试图给读者讲解现代机器学习系统的设计原理和实现经验。
## 适用读者
本书的常见读者包括:
- **学生:**
随着大量机器学习课程在大学中的普及,学生已经开始掌握大量机器学习的基础理论和神经网络的实现。然而,需要训练出可以实际应用的机器学习模型,需要对现代机器学习系统有充分的认识。
- **科研人员:**
研发新型的机器学习模型不仅仅需要会使用基础的机器学习系统接口。同时新型的模型往往需要给系统提供新的自定义算子Custom
Operators又或者是会利用高级的分布式执行算子来实现大模型的开发。这一系列需求都需要对底层系统具有充分认识。
- **开发人员:**
大量的数据和AI驱动的公司都部署了机器学习基础设施。这一设施的核心就是机器学习系统。因此了解机器学习系统有助于开发人员对于系统性能调优以定位问题并且根据业务需求对机器学习系统进行深度定制。
## 构建指南
请参考[构建指南](INFO.md)来了解如何构建本书的网页版本和PDF版本。
## 写作指南
我们欢迎大家来一起贡献和更新本书的内容。常见的贡献方式是提交PR来更新和添加Markdown文件。写作的风格和图片要求请参考[风格指南](STYLE_GUIDE.md)。同时,机器学习领域涉及到大量的中英文翻译,相关的翻译要求请参考[术语指南](TERMINOLOGY.md)。
## 内容介绍
一个现代的机器学习框架往往具有如下图所示的基本架构。
<!-- ![机器学习框架基本构成](img/ch01/framework_architecture.svg) -->
- **编程接口:** 一个机器学习框架面向用户的编程接口Programming
interface需要特殊设计。编程接口提供简单易用的编程函数往往是PyThon从而让用户定义出各式各样的神经网络和相关的训练过程。同时编程接口要兼顾性能神经网络的执行可以调用硬件相关C和C++函数许多加速器和操作系统的编程接口。该部分的内容将在第2章展开。
- **计算图:**
用户定义的机器学习程序往往会表达成一个计算图Computational
graph。这个计算图使得用户并行计算和异步执行得以实现。该部分内容将在第3章展开。
- **计算加速器:**
现代计算加速器提供了丰富的编程接口让应用来优化其相关性能。而如何高效使用计算加速器是许多机器学习框架的核心。我们将在第4章中讨论加速器的加速原理和相关使用技巧。
- **编译器前端:**
在将计算图发送到加速器执行之前机器学习框架往往会对计算图做一系列硬件无关的一系列优化这一过程被称之为编译器前端。其中核心步骤是对用户定义的神经网络训练实现自动微分。在此期间计算图会被表示为中间表达Intermediate
Representation并同时应用类型系统和静态分析等一系列技术。我们将在第5章中讨论相关内容。
- **编译器后端:**
编译器前端生成的中间表达可以进一步针对硬件的特性例如说L2/L3大小指令流水线长度进行性能优化硬件算子选择内存分配。这一以硬件为核心的编译过程被称为编译器后端该部分内容将在第6章中讨论。
- **数据处理:**
机器学习框架会集成多种数据管理模块。其中包括数据预处理模块模型参数checkpoint模型可视化和训练结果可视化等。该部分内容将在第7章中讨论。
- **模型部署:**
在模型完成训练后用乎需要对模型进行部署。该过程中我们会根据部署硬件的特点进行模型格式的转换针对硬件特性进行推理性能优化。同时移动硬件往往具有小内存的特点。因此大量的模型压缩技术也在部署中得到应用。这些相关内容将在第8章中讨论。
- **分布式训练:**
当模型的训练需要大量内存和算力的时候机器学习框架会提供原生的分布式执行编程接口。分布式机器学习系统已经在工业界得到大量的部署。相关内容会在第9章讨论。
除了上述核心组件以外机器学习系统作为一个依然高速发展的前沿学科还有大量的问题正在被密集研究相关的前沿问题将在本书的第10章中展开讨论。另外机器学习算法相关的理论知识是本书的预备知识本书不做深入讨论基础的机器学习理论知识可以在附录中找到。

View File

@@ -17,12 +17,19 @@
* 图片需要手动改
* 公式部分可能会有不正确,需要注意
* 代码部分需要手动改,样式如下:
```python
```python
import os
import argparse
```markdown
``` python
import os
import argparse
```
```
```
* 转换得到的md中如果使用了"-"语法,则不能出现以下形式的内容:
```markdown
- title
content content content content content con...
```
即"-"之后空了一行且内容行首空了4格否则d2lbook会编译失败:
## 图片
@@ -68,6 +75,10 @@
机器学习系统工作流如 :numref:`img_workflow` 。必须注意的是在引用时冒号前要空有一个字符距离。
```
* 表格或者章节引用和图片引用类似,流程依旧是打上标签,然后用 :numref:‘引用的标签’
* 其他转换方式
* 如果图中有很多公式,使用工具导入可能会有大量公式乱码,此时可以将图保存为.png格式。
* 使用[在线图片去底工具](https://www.aigei.com/bgremover/) 将图片中的白底去除。

View File

@@ -0,0 +1,68 @@
## 经典机器学习方法
大量经典机器学习算法,如 支持向量机(Support Vector MachineSVM),
K最近邻(K-Nearest Neighbor, KNN)分类算法 和K均值聚类算法(K-Means
Clustering Algorithm)等,
虽然它们有的有网络参数,有的没有网络参数,有的是监督学习算法,有的是无监督学习算法,
训练过程也不一样,但是从系统的角度,它们都是以矩阵运算为基础的。下面,我们来简要介绍一下这些算法。
### 支持向量机
**支持向量机**(Support Vector
MachineSVM),是一种经典的机器学习分类算法,其核心思想在于最大化决策边界到数据点的距离。在这里,我们以线性可分数据为例;对于非线性可分的数据,运用**核方法**(Kernel
Method)即可类似处理。
如果训练数据是线性可分的SVM的目标则是最大化**间隔**(Margin)。首先,我们先来定义最大化间隔的分类器,如下:
$$\min_{{w},b} ~~~\frac{1}{2} ||{w}||^2$$
$$s.t. ~~~y_i ({w}^T {x_i} + b) \geq 1, ~~~\forall 1 \leq i \leq n$$
其拉格朗日乘子为
$$L({w},b,{\lambda}) = \frac{1}{2} ||{w}||^2 + \sum_{i=1}^n \lambda_i (1-y_i({w}^T {x_i} + b))$$
由于$\frac{1}{2} ||{w}||^2$是凸的,并且$\lambda_i (1-y_i({w}^T {x_i} + b))$是线性的(也是凸的),所以优化问题的解为
$$\max_{\lambda>0} \min_{{w},b} L({w},b, {\lambda})$$
求$L$关于${w},b$的导数有
$$\nabla_{{w}} L= {w} - \sum_{i=1}^n \lambda_i y_i {x_i}$$
$$\nabla_b L = - \sum_{i=1}^n \lambda_i y_i$$
令$L$关于${w},b$的导数均为0得到${w}^* = \sum_{i=1}^n \lambda_i y_i {x_i}$以及$\sum_{i=1}^n \lambda_i y_i = 0$。
由于当$\lambda$固定的时候,$b$的值对目标函数无贡献,所以可以令$b^* = 0$。
这时由对偶性理论和KTT条件我们得到
$$y_i ({w}^{*T} {x_i} + b^*) > 1 \Rightarrow \lambda_i^* = 0$$
$$\lambda_i^* > 0 \Rightarrow y_i ({w}^{*T} {x_i} + b^*) = 1$$
$${w}^* = \sum_{i=1}^n \lambda_i^* y_i {x_i}$$
如果$y_i ({w}^{*T} {x_i} + b^*) = 1$,那么${x_i}$就是离超平面$({w}^*,b^*)$最近的点之一,否则就不是。因此,${w}^*$就是离超平面$({w}^*,b^*)$最近的点${x_i}$的线性组合。
如此通过SVM算法我们实现了数据的分类并且能够最大化了决策边界到最近点的距离。
我们定义满足$y_i ({w}^{*T} {x_i} + b^*) = 1$的${x_i}$为**支持向量**(Support
Vectors),同时把分类器$\hat{y}=sgn({w}^{*T} {x_i} + b^*)$称为支持向量机。
### K最近邻算法
**K最近邻算法**(K-Nearest
NeighborKNN)也是一种传统的机器学习算法可用于分类、回归等基本的机器学习任务。和上面介绍的SVM算法不同K最近邻算法的核心思想并不是用一个决策边界把属于不同类的数据分开而是依靠每个数据点周围几个距离最近的数据的性质来预测数据点本身的性质。
KNN用于分类时为了预测某个样本点的类别会进行一次投票。投票的对象为离这个观测样本点最近的K个样本点每个要投票的样本点可能会被赋予不同的权重而投票的"内容"则是样本点的类别。处理投票结果的时候,采用的是少数服从多数的决策方法(Majority
Vote)。也就是说若一个样本点最近的K个样本点中大多数属于某个类别那么该样本点也属于这个类别。
KNN算法的具体描述如下1计算待分类点到各已知类别点的距离2将这些点按照距离排序并按照距离挑选出最近的K个点3按照每个点的权重进行"统票"票面内容为点所处的类别4返回得票最高的类别并作为待分类点的预测类别。
KNN算法有几个需要注意的关键问题包括超参数K的选择距离的度量方式还有分类决策规则。对于超参数K不宜过大否则会导致很大的近似误差反之亦不宜过小否则会导致很大的估计误差。距离的度量则可以选择曼哈顿距离、欧式距离和闵可夫斯基距离等等。为了降低K值对于预测结果产生的误差和影响我们通常可以对分类决策规则做一定的规定比如在投票决策时让距离小的点有更大的权重距离较大的点权重较小。在编程实现KNN算法的时候权重等参数都会以矩阵的形式进行运算以提高运算效率。
### K均值聚类算法
**K均值聚类算法**(K-Means Clustering
Algorithm)是机器学习中一种常见的无监督聚类算法。在这里,我们首先定义聚类问题:给定数据点${x_1},\cdots, {x_n} \in \mathbb{R}^d$和$K\in \mathbb{N}$,需要划分为$K$个簇${C_1}, \cdots, {C_K} \in \mathbb{R}^d$以及每个数据点所对应的分类中心点${ C_{(1)}}, \cdots, {C_{(n)}}$,以最小化距离和$\sum_i ||{x_i} - {C_{(i)}}||^2$。
K均值聚类算法是一种解决聚类问题的算法算法过程如下
- 随机选择${C_1}, \cdots, {C_K}$
- 把${x_i}$所对应的分类置为距离其最近的聚类中心点的分类
- 计算并赋值${C_K} = \frac{\sum_{{C_{(i)}}={C_K}} {x_i}}{\sum_{{C_{(i)}}={C_K}} 1}$
- 重复以上步骤直到算法收敛
可以证明K均值聚类算法会使得距离和$\sum_i ||{x_i} - {C_{(i)}}||^2$不断地单调减小,并且最终能够收敛。不过,算法可能收敛到局部最小值。
本章结束语:
在系统角度,机器学习的算法无论是什么算法,涉及到高维数据任务的现都是矩阵运算实现的。

View File

@@ -0,0 +1,94 @@
## 梯度下降与反向传播
上面大体上介绍了经典神经网络的内容,那么现在有一个问题,这些网络中的参数是如何确定的呢?如果要解决的问题是一个小感知器就能解决的话,参数可以人为地去确定。但是如果是一个深度网络的话,参数的确定需要自动化,也就是所谓的网络训练,而这个过程需要我们设定一个**损失函数**Loss
Function来作为训练优化的一个方向。
常见的损失函数有1用来衡量向量之间距离的均方误差(Mean Squared
ErrorMSE)
$\mathcal{L} = \frac{1}{N}\|{y}-\hat{{y}}\|^{2}_{2} = \frac{1}{N}\sum_{i=1}^N(y_{i}-\hat{y}_{i})^{2}$
和 平均绝对误差(Mean Absolute ErrorMAE)
$\mathcal{L} = \frac{1}{N}\sum_{i=1}^{N}|y_{i}-\hat{y}_{i}|$
,其中$N$代表数据样本的数量,用以求平均用,而$y$代表真实标签Ground
Truth、$\hat{y}$代表网络输出的预测标签。
2分类任务可以用的交叉熵损失Cross Entropy
$\mathcal{L} = - \frac{1}{N} \sum_{i=1}^N \bigg(y_{i}\log\hat{y}_{i} + (1 - y_{i})\log(1 - \hat{y}_{i})\bigg)$来作为损失数,当且仅当输出标签和预测标签一样的时候损失值才为零。
有了损失值之后,我们就可以利用大量真实标签的数据和优化方法来更新模型参数了,其中最常用的方法是**梯度下降**Gradient
Descent。如 :numref:`gradient_descent2`所示,
开始的时候,模型的参数${w}$是随机选取的,然后求出损失值对参数的偏导数$\frac{\partial \mathcal{L}}{\partial {w}}$,通过反复迭代
${w}:={w}-\alpha\frac{\partial \mathcal{L}}{\partial {w}}$完成优化。这个优化的过程其实就可以降低损失值以达到任务目标,其中$\alpha$是控制优化幅度的**学习率**Learning
Rate
在实践中,梯度下降最终得到的最小值很大可能是一个局部最小值,而不是全局最小值。不过由于深度神经网络能提供一个很强的数据表达能力,所以局部最小值可以很接近全局最小值,损失值可以足够小。
![梯度下降介绍。(左图)只有一个可以训练的参数$w$;(右图)有两个可以训练的参数${w}=[w_1,w_2]$。在不断更新迭代参数后,损失值$\mathcal{L}$会逐渐地减小。但是由于存在很多局部最优解,我们往往不能更新到全局最优解。](../img/ch_basic/gradient_descent2.png)
:width:`600px`
:label:`gradient_descent2`
那么接下来,在深度神经网络中如何实现梯度下降呢,这需要计算出网络中每层参数的偏导数$\frac{\partial \mathcal{L}}{\partial {w}}$,我们可以用**反向传播**Back-Propagation[@rumelhart1986learning; @lecun2015deep]来实现。
接下来,
我们引入一个中间量${\delta}=\frac{\partial \mathcal{L}}{\partial {z}}$来表示损失函数$\mathcal{L}$
对于神经网络输出${z}$(未经过激活函数,不是$a$)的偏导数,
并最终得到$\frac{\partial \mathcal{L}}{\partial {w}}$。
我们下面用一个例子来介绍反向传播算法,
我们设层序号为$l=1, 2, \ldots L$(输出层(最后一层)序号为$L$)。
对于每个网络层,我们有输出${z}^l$,中间值${\delta}^l=\frac{\partial \mathcal{L}}{\partial {z}^l}$和一个激活值输出${a}^l=f({z}^l)$
(其中$f$为激活函数)。
我们假设模型是使用Sigmoid激活函数的多层感知器损失函数是均方误差MSE。也就是说我们设定
- 网络结构${z}^{l}={W}^{l}{a}^{l-1}+{b}^{l}$
- 激活函数${a}^l=f({z}^l)=\frac{1}{1+{\rm e}^{-{z}^l}}$
- 损失函数$\mathcal{L}=\frac{1}{2}\|{y}-{a}^{L}\|^2_2$
我们可以直接算出激活输出对于原输出的偏导数:
- $\frac{\partial {a}^l}{\partial {z}^l}=f'({z}^l)=f({z}^l)(1-f({z}^l))={a}^l(1-{a}^l)$
和损失函数对于激活输出的偏导数:
- $\frac{\partial \mathcal{L}}{\partial {a}^{L}}=({a}^{L}-{y})$
有了这些后,为了进一步得到损失函数对于每一个参数的偏导数,可以使用**链式法则**Chain
Rule细节如下
首先,从输出层($l=L$,最后一层)开始向后方传播误差,根据链式法则,我们先计算输出层的中间量:
- ${\delta}^{L}
=\frac{\partial \mathcal{L}}{\partial {z}^{L}}
=\frac{\partial \mathcal{L}}{\partial {a}^{L}}\frac{\partial {a}^L}{\partial {z}^{L}}=({a}^L-{y})\odot({a}^L(1-{a}^L))$
除了输出层($l=L$)的中间值${\delta}^{L}$,其他层($l=1, 2, \ldots , L-1$)的中间值${\delta}^{l}$如何计算呢?
- 已知模型结构${z}^{l+1}={W}^{l+1}{a}^{l}+{b}^{l+1}$,我们可以直接得到$\frac{\partial {z}^{l+1}}{\partial {a}^{l}}={W}^{l+1}$;而且我们已知$\frac{\partial {a}^l}{\partial {z}^l}={a}^l(1-{a}^l)$
- 那么根据链式法则,我们可以得到 ${\delta}^{l}
=\frac{\partial \mathcal{L}}{\partial {z}^{l}}
=\frac{\partial \mathcal{L}}{\partial {z}^{l+1}}\frac{\partial {z}^{l+1}}{\partial {a}^{l}}\frac{\partial {a}^{l}}{\partial {z}^{l}}
=({W}^{l+1})^\top{\delta}^{l+1}\odot({a}^l(1-{a}^l))$
根据上面的计算有所有层的中间值${\delta}^l, l=1, 2, \ldots , L$后,我们就可以在此基础上求出损失函数对于每层参数的偏导数:$\frac{\partial \mathcal{L}}{\partial {W}^l}$和$\frac{\partial \mathcal{L}}{\partial {b}^l}$,以此来根据梯度下降的方法来更新每一层的参数。
- 已知模型结构${z}^l={W}^l{a}^{l-1}+{b}^l$,我们可以求出
$\frac{\partial {z}^{l}}{\partial {W}^l}={a}^{l-1}$ 和
$\frac{\partial {z}^{l}}{\partial {b}^l}=1$
- 那么根据链式法则,我们可以得到$\frac{\partial \mathcal{L}}{\partial {W}^l}=\frac{\partial \mathcal{L}}{\partial {z}^l}\frac{\partial {z}^l}{\partial {W}^l}={\delta}^l({a}^{l-1})^\top$
,
$\frac{\partial \mathcal{L}}{\partial {b}^l}=\frac{\partial \mathcal{L}}{\partial {z}^l}\frac{\partial {z}^l}{\partial {b}^l}={\delta}^l$
求得所有偏导数$\frac{\partial \mathcal{L}}{\partial {W}^l}$ 和
$\frac{\partial \mathcal{L}}{\partial {b}^l}$后,我们就可以用梯度下降更新所有参数${W}^l$
和 ${b}^l$
- ${W}^l:={W}^l-\alpha\frac{\partial \mathcal{L}}{\partial {W}^l}$,
${b}^l:={b}^l-\alpha\frac{\partial \mathcal{L}}{\partial {b}^l}$
但是还有一个问题需要解决,那就是梯度下降的时候每更新一次参数,都需要计算一次当前参数下的损失值。然而,当训练数据集很大时($N$很大),若每次更新都用整个训练集来计算损失值的话,计算量会非常巨大。
为了减少计算量,我们使用**随机梯度下降**Stochastic Gradient
DescentSGD来计算损失值。具体来说我们计算损失值不用全部训练数据而是从训练集中随机选取一些数据样本来计算损失值比如选取16、32、64或者128个数据样本样本的数量被称为**批大小**Batch
Size
此外,学习率的设定也非常重要。如果学习率太大,可能无法接近最小值的山谷,如果太小,训练又太慢。
自适应学习率例如Adam [@KingmaAdam2014]、RMSProp [@tieleman2012rmsprop]
Adagrad [@duchi2011adagrad] 等,在训练的过程中通过自动的方法来修改学习率,实现训练的快速收敛,到达最小值点。

View File

@@ -1,10 +1,12 @@
# 附录:机器学习介绍
本书假设读者有一定的机器学习算法基础,因此本章只会简略地介绍一下机器学习,其中的梯度下降方法对本书机器学习系统来说尤为重要,是必须掌握的内容。
```toc
:maxdepth: 2
:numbered:
neural_network
gradient_descent
classic_machine_learning
```

View File

@@ -0,0 +1,179 @@
## 神经网络
### 感知器
![有三个输入和单一输出的神经元](../img/ch_basic/single_neuron2.png)
:width:`600px`
:label:`single_neuron`
:numref:`single_neuron`是一个神经元的例子,输入数据$x$根据连线上的权重$w$做加权求和得到输出$z$,我们把这样的模型叫作**感知器**Perceptron
因为输入和输出之间只有一层神经连接,这个模型也叫做单层感知器。 :numref:`single_neuron`的模型计算可以写为:$z = w_{1}x_{1}+ w_{2}x_{2} + w_{3}x_{3}$。
当输入数据用列向量${x}=[x_1,x_2,x_3]^T$表示,模型权重用行向量${w}=[w_1,w_2,w_3]$表示,那么输出的标量$z$可以写为:
$$z =
\begin{bmatrix}
w_1,w_2,w_3\\
\end{bmatrix}
\begin{bmatrix}
x_1\\
x_2\\
x_3
\end{bmatrix}
={w}{x}$$
我们可以利用输出标量$z$为输入的加权组合来实现特定任务。
比如,可以对"好苹果"和"坏苹果"进行分类,输入的$x_1,x_2,x_3$分别代表三种不同的特征1红色的程度2有没有洞3大小。如果苹果的大小对这个判断没有影响那么对应的权重就为零。
这个神经网络的训练,其实就是选择合适的权重,来实现我们的任务。比如我们可以选择合适的权重,使得当$z$小于等于$0$时代表"坏苹果",而当$z$大于$0$时则是"好苹果"。
则最终的分类输出标签$y$如下,为$1$时代表好,$0$代表坏。这个神经元的输入和输出之间只有一层,所以可以成为单层神经网络。
$$
y =
\begin{cases}
1 & z>0 \\
0 & z \leq 0 \\
\end{cases}$$
### 决策边界vs.偏置
通过选择合适的权重以$z$大于或小于$0$来对输入数据做分类的话,可以在数据空间上获得一个**决策边界**
Decision
Boundary。如 :numref:`single_neuron_decision_boundary2`所示,以神经元输出$z=0$作为输出标签$y$的决策边界,没有偏置时决策边界必然经过坐标原点,如果数据样本点不以原点来分开,会导致分类错误。
为了解决这个问题,可以在神经元上加入一个**偏置**Bias。 :numref:`single_neuron_bias2`
是一个有偏置$b$的神经元模型,可以用 :eqref:`singleneuron_bias`表达:
$$z = w_{1}x_{1}+ w_{2}x_{2}+ w_{3}x_{3} + b$$
:eqlabel:`singleneuron_bias`
![两个输入(左)和三个输入(右)时的决策边界。不同形状的点代表不同类别的数据,需要找到$z=0$作为决策边界来把不同数据点分开。两个输入时决策边界是一直线,三个输入时决策边界是一个平面,高维度输入时决策边界称为**超平面**Hyperplane
左: $z=w_{1}x_{1}+w_{2}x_{2}+b$。右:$z=w_{1}x_{1}+w_{2}x_{2}+w_{3}x_{3}+b$。没有偏置时,决策边界必然经过原点,所以不能分开不同类别的数据样本。](../img/ch_basic/single_neuron_decision_boundary2.png)
:width:`600px`
:label:`single_neuron_decision_boundary2`
![一个有偏置的单层神经网络](../img/ch_basic/single_neuron_bias2.png)
:width:`600px`
:label:`single_neuron_bias2`
有了偏置以后,决策边界(直线、平面或超平面)可以不经过坐标原点,因此能更好地分类样本。
准确来说,决策边界把这些样本数据分成两个不同的类别,这个边界是
$\{x_1, x_2, x_3 | w_{1}x_{1}+ w_{2}x_{2}+ w_{3}x_{3} + b = 0\}$。
### 逻辑回归
上述神经元的输入和输出是线性关系,为了提供非线性的数据表达能力,可以在神经元输出上加上**激活函数**Activation
Function最常见的激活函数有Sigmoid、Tanh、ReLU和Softmax等。
比如,上述神经元以$z=0$为分界来做分类任务,那么我们可不可以让神经元输出一个概率呢?比如输出$0~1$$1$代表输入数据$100\%$为某一类。
为了让神经元输出$0~1$,可以在$z$上加一个逻辑函数**Sigmoid**
如 :eqref:`sigmoid`所示Sigmoid把数值限制在0和1之中通过一个简单的临界值0.5)来决定最终输出的标签是否属于某个类别。这个方法叫做**逻辑回归**Logistic
Regression
$$a = f({z}) = \frac{1}{1+{\rm e}^{-{z}}}$$
:eqlabel:`sigmoid`
### 多个神经元
![多个神经元](../img/ch_basic/two_neurons2.png)
:width:`600px`
:label:`two_neurons2`
上述网络只有一个输出,若多个神经元在一起就可以有多个输出。 :numref:`two_neurons2`是有两个输出的网络,每个输出都和所有输入相连,所以也被称**全连接层**Fully-Connected(FC) Layer
可由下述式子 :eqref:`fullyconnected`表示X。
$$
z_{1} &= w_{11}x_{1} + w_{12}x_{2} + w_{13}x_{3} + b_1 \notag \\
z_{2} &= w_{21}x_{1} + w_{22}x_{2} + w_{23}x_{3} + b_2$$
:eqlabel:`fullyconnected`
如下式子表示了矩阵方法的实现:
$$
{z} =
\begin{bmatrix}
z_1 \\
z_2
\end{bmatrix}
=
\begin{bmatrix}
w_{11} & w_{12} & w_{13}\\
w_{21} & w_{22} & w_{23}\\
\end{bmatrix}
\begin{bmatrix}
x_1\\
x_2\\
x_3
\end{bmatrix}
+
\begin{bmatrix}
b_1 \\ b_2
\end{bmatrix}
= {W}{x} + {b}$$
多输出的网络可以实现多分类问题比如有10个数值输出每个数值分别代表一类物品的概率每个输出在$0$到$1$之间10个输出之和为$1$。
可用 :eqref:`e_softmax`的**Softmax** 函数来实现,$K$为输出的个数:
$$f({z})_{i} = \frac{{\rm e}^{z_{i}}}{\sum_{k=1}^{K}{\rm e}^{z_{k}}}$$
:eqlabel:`e_softmax`
### 多层感知器
![多层感知器例子。$a^l_i$表示神经元输出$z$经过激活函数后的值,其中$l$代表层的序号($L$代表输出层),$i$代表输出的序号](../img/ch_basic/mlp2.png)
**多层感知器**Multi-Layer
PerceptronMLP[@rosenblatt1958perceptron]通过叠加多层全连接层来提升网络的表达能力。相比单层网络,多层感知器有很多中间层的输出并不暴露给最终输出,这些层被称为**隐含层**Hidden
Layers。这个例子中的网络可以通过下方的串联式矩阵运算实现其中$W^l$和$b^l$代表不同层的权重矩阵和偏置,$l$代表层号,$L$代表输出层。
$${z} = f({W^L}f({W^3}f({W^2}f({W^1}{x} + {b^1}) + {b^2}) + {b^3}) + {b^L})$$
在深度学习时代,网络模型基本都是多层的神经网络层连接起来的,输入数据经过多层的特征提取,可以学到不同抽象层级的**特征向量**Feature
Vector。下面我们介绍一下其他常用的神经网络层。
### 卷积网络
![卷积运算例子。 输入一个三通道的数据,其大小为$4 \times 4 \times 3$(高
$\times$ 宽 $\times$
通道数),为了对各个通道做卷积,卷积核也必须有三个通道,一个卷积核的大小为$3 \times 3 \times 3 \times 1$(高
$\times$
宽$\times$输入通道数$\times$输出通道数(卷积核的个数))。有多少个卷积核就有多少个输出的**特征图**Feature
Map在这个例子中因为只有一个卷积核所以输出的通道数为1高宽为2。与此同时我们把这种高维度的输入数据称为**张量**Tensor比如RGB图像、视频、前一层卷积层的输出等等。](../img/ch_basic/conv_computation_v4.png)
:width:`600px`
:label:`conv_computation_v4`
**卷积神经网络** Convolutional Neural
NetworkCNN[@lecun1989backpropagation]由多层**卷积层**Convolutional
Layer组成常用于计算机视觉任务 [@krizhevsky2012imagenet; @he2016deep]。
:numref:`conv_computation_v4`描述了一个卷积运算的例子。
根据卷积的特点我们可以知道两个事实1一个卷积核的通道数等于输入的通道数2输出的通道数等于卷积核的数量。
:numref:`conv_computation_v4`例子中,卷积核每次滑动一个数值的范围来进行卷积操作,我们称它的**步长**Stride为1。此外如果希望输入的边缘数值也能被考虑在内的话则需要对边缘做**填零**Zero
Padding操作。 :numref:`conv_computation_v4`例子中,如果输入的每个通道上下左右都填充一圈零,那么输出的大小则为$4\times 4\times 1$。填零的圈数取决于卷积核的大小,卷积核越大则填零圈数越大。
为了对输入的图像数据做特征提取,卷积核数量往往比输入数据的通道数据要多,这样的话输出数据的数值会很多,计算量变大。然而图像数据中相邻像素的特征往往相似,所以我们可以对相邻的输出特征进行聚合操作。**池化层**就是为了实现这个目的我们通常有两种池化方法最大值池化Max
Pooling和平均值池化Mean
Pooling。如 :numref:`pooling_v3`所示,假设池化的卷积核高宽为$2\times2$,输入$4\times4$的数据步长为2步长为1时则输出等于输入则输出为$2\times2$。
![$2 \times 2$
最大值池化和平均值池化的例子它们的步长为2输入大小是$4 \times 4$](../img/ch_basic/pooling_v3.png)
:width:`600px`
:label:`pooling_v3`
卷积层和全连接层都是很常用的,但是卷积层在输入是高维度的图像时,需要的参数量远远小于全连接层。卷积层的运算和全连接层是类似的,前者基于高维度张量运算,后者基于二维矩阵运算。
### 时序模型
现实生活中除了图像还有大量时间序列数据,例如视频、股票价格等等。**循环神经网络**Recurrent
Neural NetworksRNN[@rumelhart1986learning]
是一种处理序列数据的深度学习模型结构。序列数据是一串连续的数据$\{x_1, x_2, \dots, x_n\}$,比如每个$x$代表一个句子中的单词。
为了可以接收一连串的输入序列,如 :numref:`rnn_simple_cell2`所示朴素循环神经网络使用了循环单元Cell作为计算单元用隐状态Hidden
State来存储过去输入的信息。具体来说对输入模型的每个数据$x$,根据公式 :eqref:`aligned`,循环单元会反复计算新的隐状态,用于记录当前和过去输入的信息。而新的隐状态会被用到下一单元的计算中。
$${h}_t = {W}[{x}_t; {h}_{t-1}] + {b}$$
:eqlabel:`aligned`
![朴素循环神经网络。 在每一步的计算中,循环单元通过过去时刻的隐状态${h}_{t-1}$和当前的输入${x}_t$,求得当前的隐状态${h}_t$。](../img/ch_basic/rnn_simple_cell2.png)
:width:`600px`
:label:`rnn_simple_cell2`
然而这种简单的朴素循环神经网络有严重的信息遗忘问题。比如说我们的输入是"我是中国人,我的母语是\_\_\_",隐状态记住了"中国人"的信息,使得网络最后可以预测出"中文"一词;但是如果句子很长的时候,隐状态可能记不住太久之前的信息了,比如说"我是中国人,我去英国读书,后来在法国工作,我的母语是\_\_\_",这时候在最后的隐状态中关于"中国人"的信息可能会被因为多次的更新而遗忘了。
为了解决这个问题后面有人提出了各种各样的改进方法其中最有名的是长短期记忆Long
Short-Term
MemoryLSTM[@Hochreiter1997lstm]。关于时序的模型还有很多很多比如近年来出现的Transformer [@vaswani2017attention]等等。

2
build_and_transform.sh Normal file
View File

@@ -0,0 +1,2 @@
d2lbook build html
python3 tools/format_tables.py

View File

@@ -29,7 +29,7 @@
上述脚本将所有的计算逻辑定义在Computation类的construct方法中由于在脚本开头的context中预先设置了单算子执行模式construct中的计算将被Python的运行时逐行调用执行同时可以在代码中的任意位置添加print命令以便打印中间的计算结果。
单算子执行的调用链路如图:numref:`single_op_exec`所示算子在Python侧被触发执行后会经过AI框架初始化其中需要确定包括算子的精度输入与输出的类型和大小以及对应的硬件设备等信息接着框架会为该算子分配计算所需的内存最后交给具体的硬件计算设备完成计算的执行。
单算子执行的调用链路如图 :numref:`single_op_exec`所示算子在Python侧被触发执行后会经过AI框架初始化其中需要确定包括算子的精度输入与输出的类型和大小以及对应的硬件设备等信息接着框架会为该算子分配计算所需的内存最后交给具体的硬件计算设备完成计算的执行。
![单算子执行](../img/ch05/single_op_exec.PNG)
:width:`800px`
@@ -39,13 +39,13 @@
### 计算图调度
虽然单算子调度具有如上所述的优点,其缺点也很明显。一方面是难于进行计算性能的优化,原因是由于缺乏计算图的全局信息,单算子执行时无法根据上下文完成算子融合,代数化简等优化;另一方面由于缺乏计算的拓扑关系,整个计算只能串行调度执行,即无法通过运行时完成并行计算。例如上述示例代码的计算逻辑可以表达为图:numref:`graph_exec`所示。由该计算图可以看出,其中乘法和减法之间并没有依赖关系,因此这两个计算可以并行执行,而这样的并行执行信息只有将计算表达为计算图后才能完成分析,这也是计算图调度相对于单算子调度的优势之一。
虽然单算子调度具有如上所述的优点,其缺点也很明显。一方面是难于进行计算性能的优化,原因是由于缺乏计算图的全局信息,单算子执行时无法根据上下文完成算子融合,代数化简等优化;另一方面由于缺乏计算的拓扑关系,整个计算只能串行调度执行,即无法通过运行时完成并行计算。例如上述示例代码的计算逻辑可以表达为图 :numref:`graph_exec`所示。由该计算图可以看出,其中乘法和减法之间并没有依赖关系,因此这两个计算可以并行执行,而这样的并行执行信息只有将计算表达为计算图后才能完成分析,这也是计算图调度相对于单算子调度的优势之一。
![计算图](../img/ch05/graph_exec.png)
:width:`800px`
:label:`graph_exec`
下面我们开始介绍计算图的调度方式在一个典型的异构计算环境中主要存在CPU、GPU以及NPU等多种计算设备因此一张计算图可以由运行在不同设备上的算子组成为异构计算图。图:numref:`computation_graph`展示了一个典型的由异构硬件共同参与的计算图。
下面我们开始介绍计算图的调度方式在一个典型的异构计算环境中主要存在CPU、GPU以及NPU等多种计算设备因此一张计算图可以由运行在不同设备上的算子组成为异构计算图。图 :numref:`computation_graph`展示了一个典型的由异构硬件共同参与的计算图。
![异构硬件计算图](../img/ch05/computation_graph.png)
:width:`800px`
@@ -63,7 +63,7 @@
- **Python算子**在执行模式上与CPU算子类似都是由主机上的CPU执行计算区别在于计算逻辑是由Python语言的运行时通过Python解释器解释执行。
异构计算图能够被正确表达的首要条件是准确标识算子执行所在的设备,例如异构计算图:numref:`computation_graph`中所标识的CPU、GPU和Ascend
异构计算图能够被正确表达的首要条件是准确标识算子执行所在的设备,例如异构计算图 :numref:`computation_graph`中所标识的CPU、GPU和Ascend
Kernel以及被标记为被Python语言运行时执行的Python
Kernel。主流框架均提供了指定算子所在运行设备的能力以MindSpore为例一段简单的异构计算代码如下所示
@@ -138,7 +138,7 @@ z的计算逻辑其中Add算子被设置为在CPU上执行Sub算子被设
:width:`800px`
:label:`side_effect_1`
代码中所示三行计算之间并没有依赖关系,因此这三个算子在计算图的逻辑上可以被并发执行,并发关系如图:numref:`side_effect_1`所示,然而根据代码的语义,显而易见是需要确保程序能够被顺序执行,这里引入的问题被称为副作用,副作用是指函数修改了在函数外部定义的状态变量的行为。由于副作用的引入而导致了错误并发关系的发生,一种解决方案是在计算图编译阶段通过添加算子间的依赖,将并发执行逻辑转换为顺序执行逻辑,转换后的计算图如图:numref:`side_effect_2`所示:
代码中所示三行计算之间并没有依赖关系,因此这三个算子在计算图的逻辑上可以被并发执行,并发关系如图 :numref:`side_effect_1`所示,然而根据代码的语义,显而易见是需要确保程序能够被顺序执行,这里引入的问题被称为副作用,副作用是指函数修改了在函数外部定义的状态变量的行为。由于副作用的引入而导致了错误并发关系的发生,一种解决方案是在计算图编译阶段通过添加算子间的依赖,将并发执行逻辑转换为顺序执行逻辑,转换后的计算图如图 :numref:`side_effect_2`所示:
![消除副作用](../img/ch05/side_effect_2.png)
:width:`800px`
@@ -157,7 +157,7 @@ z的计算逻辑其中Add算子被设置为在CPU上执行Sub算子被设
:width:`800px`
:label:`graph_exec_1`
如图:numref:`graph_exec_1`是一张非异构计算图计算图上全部Kernel均为GPU算子执行方式一般分为串行执行和并行执行
如图 :numref:`graph_exec_1`是一张非异构计算图计算图上全部Kernel均为GPU算子执行方式一般分为串行执行和并行执行
![串行执行](../img/ch05/graph_exec_2.png)
:width:`800px`
@@ -167,9 +167,9 @@ z的计算逻辑其中Add算子被设置为在CPU上执行Sub算子被设
:width:`800px`
:label:`graph_exec_3`
- **串行执行**:将计算图展开为执行序列,按照执行序逐个串行执行,如图:numref:`graph_exec_2`所示。其特点为执行顺序固定,单线程执行,对系统资源要求相对较低。
- **串行执行**:将计算图展开为执行序列,按照执行序逐个串行执行,如图 :numref:`graph_exec_2`所示。其特点为执行顺序固定,单线程执行,对系统资源要求相对较低。
- **并行执行**:将计算图按照算子之间的依赖关系展开,有依赖关系的算子通过输入依赖保证执行顺序,没有依赖关系的算子则可以并行执行,如图:numref:`graph_exec_3`所示Kernel_1和Kernel_2没有依赖可以并行执行Kernel_3和Kernel_4没有依赖可以并行执行。其特点为执行顺序不固定每轮执行的算子顺序大概率不一样多线程执行对系统资源要求相关较高。
- **并行执行**:将计算图按照算子之间的依赖关系展开,有依赖关系的算子通过输入依赖保证执行顺序,没有依赖关系的算子则可以并行执行,如图 :numref:`graph_exec_3`所示Kernel_1和Kernel_2没有依赖可以并行执行Kernel_3和Kernel_4没有依赖可以并行执行。其特点为执行顺序不固定每轮执行的算子顺序大概率不一样多线程执行对系统资源要求相关较高。
串行执行和并行执行各有优点和缺点总结对比见表5.1。
@@ -186,8 +186,8 @@ z的计算逻辑其中Add算子被设置为在CPU上执行Sub算子被设
:width:`800px`
:label:`graph_exec_4`
如图:numref:`graph_exec_4`是一张异构计算图其中Kernel_1、Kernel_2、Kernel_5、Kernel_9为CPU算子Kernel_6为python算子执行也是在CPU上Kernel_3和Kernel_4为GPU算子Kernel_7和Kernel_8为GPU算子。
一般来说计算图的优化都是基于非异构计算图来实现的,要求计算图中的算子为同一设备上的,方便算子间的融合替换等优化操作,因此需要将一张异构计算图切分为多个非异构计算图,这里切分就比较灵活了,可以定义各种切分规则,一般按照产生尽量少的子图的切分规则来切分,尽量将多的同一设备上的算子放在一张子图中,如图:numref:`graph_exec_5`所示最后产生5张子图Graph_1\_CPU、Graph_2\_GPU、Graph_3\_CPU、Graph_4\_Ascend、Graph_5\_CPU。
如图 :numref:`graph_exec_4`是一张异构计算图其中Kernel_1、Kernel_2、Kernel_5、Kernel_9为CPU算子Kernel_6为python算子执行也是在CPU上Kernel_3和Kernel_4为GPU算子Kernel_7和Kernel_8为GPU算子。
一般来说计算图的优化都是基于非异构计算图来实现的,要求计算图中的算子为同一设备上的,方便算子间的融合替换等优化操作,因此需要将一张异构计算图切分为多个非异构计算图,这里切分就比较灵活了,可以定义各种切分规则,一般按照产生尽量少的子图的切分规则来切分,尽量将多的同一设备上的算子放在一张子图中,如图 :numref:`graph_exec_5`所示最后产生5张子图Graph_1\_CPU、Graph_2\_GPU、Graph_3\_CPU、Graph_4\_Ascend、Graph_5\_CPU。
![异构计算图切分](../img/ch05/graph_exec_5.png)
:width:`800px`
@@ -195,9 +195,9 @@ z的计算逻辑其中Add算子被设置为在CPU上执行Sub算子被设
将一张异构计算图切分为多个子计算图后,执行方式一般分为子图拆分执行和子图合并执行:
- **子图拆分执行**:将切分后的多个子图分开执行,即一个子图执行完再执行另一个子图,如图:numref:`graph_exec_6`所示上一个子图的输出数据会传输给下一个子图的输入数据并且下一个子图需要对输入数据拷贝为本图的device数据如Graph_2\_GPU需要将Graph_1\_CPU的输出数据从CPU拷贝到GPU反过来Graph_3\_CPU需要将Graph2GPU的输出数据从GPU拷贝到CPU子图之间互相切换执行有一定的开销。
- **子图拆分执行**:将切分后的多个子图分开执行,即一个子图执行完再执行另一个子图,如图 :numref:`graph_exec_6`所示上一个子图的输出数据会传输给下一个子图的输入数据并且下一个子图需要对输入数据拷贝为本图的device数据如Graph_2\_GPU需要将Graph_1\_CPU的输出数据从CPU拷贝到GPU反过来Graph_3\_CPU需要将Graph2GPU的输出数据从GPU拷贝到CPU子图之间互相切换执行有一定的开销。
- **子图合并执行**将切分后的多个子图进行合并合并为一个整体大的DAG执行如图:numref:`graph_exec_7`所示,通过算子的设备属性来插入拷贝算子以实现不同设备上的算子数据传输,并且拷贝算子也是进入整图中的,从而形成一个大的整图执行,减少子图之间的切换执行开销。
- **子图合并执行**将切分后的多个子图进行合并合并为一个整体大的DAG执行如图 :numref:`graph_exec_7`所示,通过算子的设备属性来插入拷贝算子以实现不同设备上的算子数据传输,并且拷贝算子也是进入整图中的,从而形成一个大的整图执行,减少子图之间的切换执行开销。
![子图拆分](../img/ch05/graph_exec_6.png)
:width:`800px`
@@ -218,7 +218,7 @@ z的计算逻辑其中Add算子被设置为在CPU上执行Sub算子被设
3、异构计算图的执行加速
前面讲述了非异构计算图的两种执行方式和异构计算图的两种执行方式其中异构计算图又是在非异构计算图的基础之上因此异构计算图按照两两组合共有四种执行方式以MindSpore为例采用的是子图合并并行执行示例图如图:numref:`graph_exec_5`所示,首先是作为一张整图来执行可以避免子图切换的执行开销,然后在整图内并行执行,可以最大粒度的发挥并发执行优势,达到最优的执行性能。
前面讲述了非异构计算图的两种执行方式和异构计算图的两种执行方式其中异构计算图又是在非异构计算图的基础之上因此异构计算图按照两两组合共有四种执行方式以MindSpore为例采用的是子图合并并行执行示例图如图 :numref:`graph_exec_5`所示,首先是作为一张整图来执行可以避免子图切换的执行开销,然后在整图内并行执行,可以最大粒度的发挥并发执行优势,达到最优的执行性能。
![异构硬件加速](../img/ch05/graph_exec_8.png)
:width:`800px`
@@ -226,6 +226,6 @@ z的计算逻辑其中Add算子被设置为在CPU上执行Sub算子被设
### 下沉式执行
下沉式执行是通过专用芯片的SoC架构将整个或部分计算图一次性调度到芯片上以完成全量数据的计算。例如对于Ascend芯片多个Ascend算子组成的计算图可以在执行前被编译成为一个Task通过Ascend驱动程序提供的接口将包含多个算子的Task一次性下发到硬件上调度执行。因此上例中可以将Ascend的算子Kernel_7和Kernel_8优化为一个子图Graph_4\_Ascend再将该子图编译成为一个Task并下沉到Ascend上执行如图:numref:`graph_exec_8`所示。
下沉式执行是通过专用芯片的SoC架构将整个或部分计算图一次性调度到芯片上以完成全量数据的计算。例如对于Ascend芯片多个Ascend算子组成的计算图可以在执行前被编译成为一个Task通过Ascend驱动程序提供的接口将包含多个算子的Task一次性下发到硬件上调度执行。因此上例中可以将Ascend的算子Kernel_7和Kernel_8优化为一个子图Graph_4\_Ascend再将该子图编译成为一个Task并下沉到Ascend上执行如图 :numref:`graph_exec_8`所示。
下沉式执行由于避免了在计算过程中主机侧和设备侧的交互因此可以获得更好的整体计算性能。然而下沉式执行也存在一些局限例如在动态shape算子复杂控制流等场景下会面临较大的技术挑战。

View File

@@ -14,7 +14,7 @@ ReLUElement-Wise Sum等。
ReLU"。Conv卷积算子是计算密集型ReLU算子是访存密集型算子ReLU算子可以直接取Conv算子的计算结果进行计算因此我们可以将二者融合成一个算子来进行计算从而减少内存访问延时和带宽压力提高执行效率。
例如:"Conv + Conv + Sum +
ReLU"的融合,从图:numref:`conv_sum_relu`中我们可以看到融合后的算子减少了两个内存的读和写的操作优化了Conv的输出和Sum的输出的读和写的操作。
ReLU"的融合,从图 :numref:`conv_sum_relu`中我们可以看到融合后的算子减少了两个内存的读和写的操作优化了Conv的输出和Sum的输出的读和写的操作。
![Elementwise算子融合](../img/ch05/conv_sum_relu.png)
:width:`800px`
@@ -28,7 +28,7 @@ MindSpore
:width:`800px`
:label:`graph_kernel`
图:numref:`graph_kernel`算子拆解阶段Expander将计算图中一些复杂算子composite
:numref:`graph_kernel`算子拆解阶段Expander将计算图中一些复杂算子composite
op, 图中Op1、Op3、Op4展开为计算等价的基本算子组合
图中虚线正方形框包围着的部分在算子聚合阶段Aggregation将计算图中将基本算子basic
op, 如图中Op2、拆解后的算子(expanded
@@ -45,7 +45,7 @@ Op2。图算融合通过对计算图结构的拆解和聚合可以实现
1、硬件指令限制
在一些特定的硬件上IR中计算节点没有直接对应的硬件算子,只能通过子图的变换来达到子图中所有算子在对应的硬件上的存在。例如在MindSpore中,昇腾芯片上的Concat算子,只支持有限的输入个数63个,因此当前端IR上的输入个数大于限制输入的时候,需要将该计算节点拆分成等价的多个Concat节点,如图:numref:`concat`所示:
在一些特定的硬件上IR中计算节点没有直接对应的硬件算子,只能通过子图的变换来达到子图中所有算子在对应的硬件上的存在。例如在MindSpore中,昇腾芯片上的Concat算子,只支持有限的输入个数63个,因此当前端IR上的输入个数大于限制输入的时候,需要将该计算节点拆分成等价的多个Concat节点,如图 :numref:`concat`所示:
当Concat有100个输入时单个算子只支持最多63个输入此时会将该计算节点拆分成两个Concat节点分别为63个输入和37个输入的两个算子。
![Concat算子拆分](../img/ch05/concat.png)
@@ -54,7 +54,7 @@ Op2。图算融合通过对计算图结构的拆解和聚合可以实现
2、数据排布格式的限制
针对不同特点的计算平台和不同的算子,为了追求最好的性能,一般都需要选择不同的数据排布格式(Format),而这些排布格式可能跟框架缺省的排布格式是不一样的。在这种情况下,一般的做法是算子在执行完成后对输出插入一个格式转换操作,把排布格式转换回框架的缺省排布格式,这就引入了额外的内存操作。以下图:numref:`transdata`为例在昇腾平台上Conv算子在输入和输出的内存排布为5HD时是性能最优的所以可以看到Conv算子输出结果的格式是5HD然后通过一个转换操作转回了框架缺省的NCHW紧接着后面又是一个Conv算子它需要5HD的输入所以又做了一个NCHW到5HD的转换。我们很容易看出虚线框内的两个转换操作互为逆操作可以相互抵消。通过对计算图的模式匹配,可以将该类型的操作消除。
针对不同特点的计算平台和不同的算子,为了追求最好的性能,一般都需要选择不同的数据排布格式(Format),而这些排布格式可能跟框架缺省的排布格式是不一样的。在这种情况下,一般的做法是算子在执行完成后对输出插入一个格式转换操作,把排布格式转换回框架的缺省排布格式,这就引入了额外的内存操作。以下图 :numref:`transdata`为例在昇腾平台上Conv算子在输入和输出的内存排布为5HD时是性能最优的所以可以看到Conv算子输出结果的格式是5HD然后通过一个转换操作转回了框架缺省的NCHW紧接着后面又是一个Conv算子它需要5HD的输入所以又做了一个NCHW到5HD的转换。我们很容易看出虚线框内的两个转换操作互为逆操作可以相互抵消。通过对计算图的模式匹配,可以将该类型的操作消除。
![数据排布格式转换消除](../img/ch05/transdata.png)
:width:`800px`

View File

@@ -13,13 +13,13 @@
**数据排布格式**
机器学习系统中很多运算都会转换成为矩阵的乘法,例如卷积运算。我们知道矩阵乘法$A\times B = C$
,是以A的一行乘以B的一列求和后得到C的一个元素。以图:numref:`matmuldatalayout`为例,在图:numref:`matmuldatalayout`的上方矩阵数据的存储是按照行优先来进行存储虽然B在存储时是按照行存储但是读取数据时却按照列进行读取假如我们能把B的格式进行转换转换为列存储例如图:numref:`matmuldatalayout`下方所示,这样就可以通过访问连续内存的方式加快数据访问速度进而提升运算速度。由此可见不同的数据排布方式对性能有很大影响。
,是以A的一行乘以B的一列求和后得到C的一个元素。以图 :numref:`matmuldatalayout`为例,在图 :numref:`matmuldatalayout`的上方矩阵数据的存储是按照行优先来进行存储虽然B在存储时是按照行存储但是读取数据时却按照列进行读取假如我们能把B的格式进行转换转换为列存储例如图 :numref:`matmuldatalayout`下方所示,这样就可以通过访问连续内存的方式加快数据访问速度进而提升运算速度。由此可见不同的数据排布方式对性能有很大影响。
![矩阵乘法数据排布示意图](../img/ch05/matmuldatalayout.png)
:width:`800px`
:label:`matmuldatalayout`
在机器学习系统中我们常见的数据格式一般有两种分别为NCHW类型和NHWC类型。其中N代表了数据输入的BatchSize大小C代表了图像的通道H和W分别代表图像输入的长和宽。图:numref:`data_format`展示了BatchSize为2通道数16和长度为5\*4大小的数据逻辑示意图。
在机器学习系统中我们常见的数据格式一般有两种分别为NCHW类型和NHWC类型。其中N代表了数据输入的BatchSize大小C代表了图像的通道H和W分别代表图像输入的长和宽。图 :numref:`data_format`展示了BatchSize为2通道数16和长度为5\*4大小的数据逻辑示意图。
![常见数据格式](../img/ch05/data_format.png)
:width:`800px`
@@ -29,7 +29,7 @@
对于NCHW的数据是先取W轴方向数据再取H轴方向数据再取C轴方向最后取N轴方向。其中物理存储与逻辑存储的之间的映射关系为
$$offsetnchw(n,c,h,w) = n*CHW + c*HW + h*W +w$$
如图:numref:`nchw`所示这种格式中是按照最低维度W轴方向进行展开W轴相邻的元素在内存排布中同样是相邻的。如果需要取下一个图片上的相同位置的元素,就必须跳过整个图像的尺寸($C*H*W$。比如我有8张32\*32的RGB图像此时$N=8C=3,H=32,W=32$。在内存中存储们需要先按照w轴方向进行展开然后按照h轴排列这样之后便完成了一个通道的处理之后按照同样的方式处理下一个通道。处理完全部通道后处理下一张图片。PyTorch和MindSpore框架默认使用NCHW格式。
如图 :numref:`nchw`所示这种格式中是按照最低维度W轴方向进行展开W轴相邻的元素在内存排布中同样是相邻的。如果需要取下一个图片上的相同位置的元素,就必须跳过整个图像的尺寸($C*H*W$。比如我有8张32\*32的RGB图像此时$N=8C=3,H=32,W=32$。在内存中存储们需要先按照w轴方向进行展开然后按照h轴排列这样之后便完成了一个通道的处理之后按照同样的方式处理下一个通道。处理完全部通道后处理下一张图片。PyTorch和MindSpore框架默认使用NCHW格式。
![RGB图片下的NHWC数据格式](../img/ch05/nchw.png)
:width:`800px`
@@ -37,7 +37,7 @@ $$offsetnchw(n,c,h,w) = n*CHW + c*HW + h*W +w$$
类似的NHWC数据格式是先取C方向数据再取W方向然后是H方向最后取N方向。NHWC是Tensorflow默认的数据格式。这种格式在PyTorch中称为Chanel-Last。
$$offsetnchw(n,c,h,w) = n*HWC + h*HW + w*C +c$$
图:numref:`nchwandnhwc`展示了不同数据格式下逻辑排布到内存物理侧数据排布的映射。\[x:1\]代表从最内侧维度到最下一维度的索引变换。比如\[a:1\]表示当前行W轴结束后下一个H轴排布。\[b:1\]表示最内侧C轴排布完成后进行按照W轴进行排列。
:numref:`nchwandnhwc`展示了不同数据格式下逻辑排布到内存物理侧数据排布的映射。\[x:1\]代表从最内侧维度到最下一维度的索引变换。比如\[a:1\]表示当前行W轴结束后下一个H轴排布。\[b:1\]表示最内侧C轴排布完成后进行按照W轴进行排列。
![NHWC与NHWC数据存储格式](../img/ch05/nchwandnhwc.png)
:width:`800px`
@@ -56,7 +56,7 @@ Precision浮点表示。这种数据类型占用32位内存。还有一种精
:width:`800px`
:label:`floatdtype`
如图:numref:`floatdtype`其中sign代表符号位占1位表示了机器数的正负exponent表示指数位Mantissa为尾数位。其数据计算采用二进制的科学计数法转换为十进制的计算方式如下
如图 :numref:`floatdtype`其中sign代表符号位占1位表示了机器数的正负exponent表示指数位Mantissa为尾数位。其数据计算采用二进制的科学计数法转换为十进制的计算方式如下
$$(-1)^{sign}\times 2^{exponent-15}\times (\frac{mantissa}{1024}+1)$$
其中如果指数位全为0时且尾数位全为0时表示数字0。
如果指数位全为0尾数位不全为0则表示一个非常小的数值。

View File

@@ -1,4 +1,5 @@
## 内存分配 {#sec:ch06/ch06-memory-pool}
## 内存分配
:label:`ch05-sec-memory_pool`
内存在传统计算机存储器层次结构中有着重要的地位,它是连接高速缓存和磁盘之间的桥
有着比高速缓存更大的空间比磁盘更快的访问速度。随着深度学习的发展深度神经网络的模型越来越复杂AI芯片上的内存很可能无法容纳一个大型网络模型。因此对内存进行复用是一个重要的优化手段。此外通过连续内存分配和
@@ -6,7 +7,7 @@ In-Place内存分配还可以提高某些算子的执行效率。
### Device内存概念
在深度学习体系结构中我们通常将与硬件加速器如GPUAI芯片等相邻的内存称之为设备(Device)内存而与CPU相邻的内存称之为主机(Host)内存。如图:numref:`host-device-memory`所示CPU可以合法地访问主机上的内存而无法直接访问设备上的内存同理AI芯片可以访问设备上的内存却无法访问主机上的内存。因此在网络训练过程中我们往往需要从磁盘加载数据到主机内存中然后在主机内存中做数据处理再从主机内存拷贝到设备内存中最后设备才能合法地访问数据。算子全部计算完成后用户要获取训练结果又需要把数据从设备内存拷贝到主机内存中。
在深度学习体系结构中我们通常将与硬件加速器如GPUAI芯片等相邻的内存称之为设备(Device)内存而与CPU相邻的内存称之为主机(Host)内存。如图 :numref:`host-device-memory`所示CPU可以合法地访问主机上的内存而无法直接访问设备上的内存同理AI芯片可以访问设备上的内存却无法访问主机上的内存。因此在网络训练过程中我们往往需要从磁盘加载数据到主机内存中然后在主机内存中做数据处理再从主机内存拷贝到设备内存中最后设备才能合法地访问数据。算子全部计算完成后用户要获取训练结果又需要把数据从设备内存拷贝到主机内存中。
![主机内存和设备内存](../img/ch05/host-device-memory.png)
:width:`800px`
@@ -23,11 +24,11 @@ $$size=\prod_{i=0}^{dimention}shape_i * sizeof\left ( data type \right )$$
:width:`800px`
:label:`memory_allocate`
下面以图:numref:`memory_allocate`为例介绍内存分配的大致流程。首先我们会给Input
下面以图 :numref:`memory_allocate`为例介绍内存分配的大致流程。首先我们会给Input
Tensor、Conv2D的权重和Conv2D的输出分配内存地址。然后为BatchNorm的输入分配地址时我们发现BatchNorm的输入就是Conv2D算子的输出而该Tensor的地址已经在之前分配过了因此只需要将Conv2D算子的输出地址共享给BatchNorm的输入就可以避免内存的重复申请以及内存的冗余拷贝。以此类推可以发现整个过程中可以将待分配的内存分成三种类型一是整张图的输入Tensor二是算子的权重或者属性三是算子的输出Tensor三种Tensor在训练过程中的生命周期有所不同。
在CPU上我们常常使用malloc函数直接申请内存这种方式申请内存好处是随时申请随时释放简单易用。然而在许多对性能要求严苛的计算场景中由于所申请内存块的大小不定频繁申请释放会降低性能。通常我们会使用内存池的方式去管理内存先申请一定数量和大小的内存块留作备用当程序有内存申请需求时直接从内存池中的内存块中申请。当程序释放该内存块时内存池会进行回收并用作后续程序内存申请时使用。
在深度学习框架中Device内存的申请也是非常频繁的往往也是通过内存池的方式去管理Device内存并让Device内存的生命周期与Tensor的生命周期保持一致。不同的深度学习框架在内存池的设计上大同小异我们以图:numref:`device_malloc`的MindSpore框架内存申请为例进程会从Device上申请足够大的内存然后通过双游标从两端偏移为Tensor分配内存。首先从申请的首地址开始进行偏移为算子权重的Tensor分配内存这部分Tensor生命周期较长往往持续整个训练过程。然后从申请Device地址的末尾开始偏移为算子的输出Tensor分配内存这部分内存的生命周期较短往往在该算子计算结束并且后续计算过程中无需使用再次使用该算子的输出的情况下其生命周期就可以结束。通过这种方式我们只需要从Device上申请一次足够大的内存后续算子的内存分配都是通过指针偏移进行分配减少了直接从设备申请内存的耗时。
在深度学习框架中Device内存的申请也是非常频繁的往往也是通过内存池的方式去管理Device内存并让Device内存的生命周期与Tensor的生命周期保持一致。不同的深度学习框架在内存池的设计上大同小异我们以图 :numref:`device_malloc`的MindSpore框架内存申请为例进程会从Device上申请足够大的内存然后通过双游标从两端偏移为Tensor分配内存。首先从申请的首地址开始进行偏移为算子权重的Tensor分配内存这部分Tensor生命周期较长往往持续整个训练过程。然后从申请Device地址的末尾开始偏移为算子的输出Tensor分配内存这部分内存的生命周期较短往往在该算子计算结束并且后续计算过程中无需使用再次使用该算子的输出的情况下其生命周期就可以结束。通过这种方式我们只需要从Device上申请一次足够大的内存后续算子的内存分配都是通过指针偏移进行分配减少了直接从设备申请内存的耗时。
![双游标法分配内存](../img/ch05/device_malloc.png)
:width:`800px`
@@ -36,19 +37,19 @@ Tensor、Conv2D的权重和Conv2D的输出分配内存地址。然后为BatchNor
### 内存复用
在机器学习系统中内存复用是指分析Tensor的生命周期将生命周期结束的Tensor的Device内存释放回内存池并用于后续Tensor的内存分配。内存复用的目的是提高内存的利用率让有限的设备内存容纳更大的模型。
以图:numref:`memory_allocate`为例当BatchNorm算子计算结束后output1不再被任何算子使用则该Tensor的Device内存可以被回收并且如果output1的内存尺寸大于等于output3的内存尺寸则从output1回收的地址可以用于output3的内存分配从而达到复用output1地址的目的。
以图 :numref:`memory_allocate`为例当BatchNorm算子计算结束后output1不再被任何算子使用则该Tensor的Device内存可以被回收并且如果output1的内存尺寸大于等于output3的内存尺寸则从output1回收的地址可以用于output3的内存分配从而达到复用output1地址的目的。
![内存生命周期图](../img/ch05/combine_memory_reuse_and_no_reuse.png)
:width:`800px`
:label:`combine_memory_reuse_and_no_reuse`
为了更好地描述内存复用问题,我们通过内存生命周期图来辅助理解。如图:numref:`combine_memory_reuse_and_no_reuse`所示图中横坐标表示Tensor的生命周期图中纵坐标表示内存大小。在生命周期内某一个Tensor将一直占用某块Device内存直至生命周期结束才会释放相应内存块。通过Tensor生命周期和内存大小可以构造出矩形块而内存分配要求解的目标是在内存生命周期图中容纳更多的矩形块问题的约束是矩形块之间无碰撞。图:numref:`combine_memory_reuse_and_no_reuse`左边是在未使用任何内存复用策略的情况下的内存生命周期图此时内存同时只能容纳T0、T1、T2、T3四个Tensor。
为了更好地描述内存复用问题,我们通过内存生命周期图来辅助理解。如图 :numref:`combine_memory_reuse_and_no_reuse`所示图中横坐标表示Tensor的生命周期图中纵坐标表示内存大小。在生命周期内某一个Tensor将一直占用某块Device内存直至生命周期结束才会释放相应内存块。通过Tensor生命周期和内存大小可以构造出矩形块而内存分配要求解的目标是在内存生命周期图中容纳更多的矩形块问题的约束是矩形块之间无碰撞。图 :numref:`combine_memory_reuse_and_no_reuse`左边是在未使用任何内存复用策略的情况下的内存生命周期图此时内存同时只能容纳T0、T1、T2、T3四个Tensor。
内存复用策略的求解是一个NP完全的问题。许多深度学习框架通常采用贪心的策略去分配内存例如采用BestFit算法每次直接从内存池中选取可以满足条件的最小内存块然而这种贪心的策略往往会陷入局部最优解而无法求得全局最优解。为了更好地逼近内存分配策略全局最优解MindSpore框架提出了一种新的内存分配算法
SOMAS(Safe Optimized Memory Allocation
Solver)。SOMAS将计算图并行流与数据依赖进行聚合分析得到算子间祖先关系构建张量全局生命周期互斥约束使用多种启发式算法求解最优的内存静态规划实现逼近理论极限的内存复用从而提升支持的内存大小。
由图:numref:`combine_memory_reuse_and_no_reuse`右边可知经过SOMAS求解之后同样的内存大小可支持的Tensor数量达到了7个。
由图 :numref:`combine_memory_reuse_and_no_reuse`右边可知经过SOMAS求解之后同样的内存大小可支持的Tensor数量达到了7个。
### 常见的内存分配优化手段
@@ -64,10 +65,10 @@ Solver)。SOMAS将计算图并行流与数据依赖进行聚合分析得到
#### In-Place算子
在前面的内存分配流程中,我们会为每个算子的输入和输出都分配不同的内存。然而对很多算子而言,为其分配不同的输入和输出地址,会浪费内存并且影响计算性能。例如优化器算子,其计算的目的就是更新神经网络的权重;例如Python语法中的'+='和'\*='操作符,将计算结果更新到符号左边的变量中;例如'a\[0\]=b'语法,将'a\[0\]'的值更新为'b'。诸如此类计算有一个特点都是为了更新输入的值。下面以Tensor的'a\[0\]=b'操作为例介绍In-Place的优点。
图:numref:`inplace-op`左边是非In-Place操作的实现step1将Tensor
:numref:`inplace-op`左边是非In-Place操作的实现step1将Tensor
a拷贝到Tensor a'step2将Tensor b赋值给Tensor a'step3将Tensor
a'拷贝到Tensor
a。图:numref:`inplace-op`右边是算子In-Place操作的实现仅用一个步骤将Tensor
a。图 :numref:`inplace-op`右边是算子In-Place操作的实现仅用一个步骤将Tensor
b拷贝到Tensor
a对于的位置上。对比两种实现可以发现In-Place操作节省了两次拷贝的耗时并且省去了Tensor
a'内存的申请。

View File

@@ -0,0 +1,252 @@
自动微分
--------
上一节,我们介绍了机器学习框架的中间表示,设计这些中间表示的最核心的目的之一便是服务于自动微分变换。那么什么是自动微分?我们在这一节来详细介绍。
### 自动微分的基本概念
自动微分Automatic
DifferentiationAD是一种对计算机程序进行高效且准确求导的技术在上个世纪六七十年代就已经被广泛应用于流体力学、天文学、数学金融等领域[@10.5555/1455489])。时至今日,自动微分的实现及其理论仍然是一个活跃的研究领域。随着近些年深度学习在越来越多的机器学习任务上取得领先成果([@lecun2015deep]自动微分被广泛的应用于机器学习领域。许多机器学习模型使用的优化算法都需要获取模型的导数因此自动微分技术成为了一些热门的机器学习框架例如TensorFlow和PyTorch的核心特性。
常见的计算机程序求导的方法可以归纳为以下四种([@2015Automatic]手工微分Manual
Differentiation、数值微分Numerical
Differentiation、符号微分Symbolic
Differentiation和自动微分Automatic Differentiation
1手工微分需手工求解函数导数的表达式并在程序运行时根据输入的数值直接计算结果。手工微分需根据函数的变化重新推导表达式工作量大且容易出错。
2数值微分[@2015Numerical]):数值微分通过差分近似方法完成,其本质是根据导数的定义推导而来。
$$f^{'}(x)=\lim_{h \to 0}\frac{f(x+h)-f(x)}{h}$$
当$h$充分小时,可以用差分$\frac{f(x+h)-f(x)}{h}$来近似导数结果。而近似的一部分误差称为截断误差Truncation
error。理论上数值微分中的截断误差与步长$h$有关,$h$越小则截断误差越小,近似程度越高。但实际情况下数值微分的精确度并不会随着$h$的减小而一直减小。这是因为计算机系统对于浮点数运算的精度有限导致另外一种误差的存在这种误差称为舍入误差Round-off
Error。舍入误差会随着$h$变小而逐渐增大。当h较大时截断误差占主导。而当h较小时舍入误差占主导。
在截断误差和舍入误差的共同作用下,数值微分的精度将会在某一个$h$值处达到最小值,并不会无限的减小。因此,虽然数值微分容易实现,但是存在精度误差问题。
3符号微分[@2003Computer]):利用计算机程序自动地通过如下的数学规则对函数表达式进行递归变换来完成求导。
$$\frac{d}{dx}(f(x)+g(x))\rightsquigarrow\frac{d}{dx}f(x)+\frac{d}{dx}g(x)$$
$$\frac{d}{dx}(f(x)g(x))\rightsquigarrow(\frac{d}{dx}f(x))g(x)+f(x)(\frac{d}{dx}g(x))$$
符号微分常被应用于现代代数系统工具中例如Mathematica、Maxima和Maple以及机器学习框架如Theano。符号微分虽然消除了手工微分硬编码的缺陷。但因为对表达式进行严格的递归变换和展开不复用产生的变换结果很容易产生表达式膨胀expression
swell[@10.5555/60181.60188]))问题。如图:numref:`symbolic_differentiation`所示,用符号微分计算递归表达式$l_{n+1}=4l_n(1-l_n)$$l_1=x$的导数表达式,其结果随着迭代次数增加快速膨胀。
![符号微分的表达式膨胀问题](../img/ch04/符号微分的表达式膨胀问题.png)
:width:`800px`
:label:`symbolic_differentiation`
并且符号微分需要表达式被定义成闭合式的closed-form不能带有或者严格限制控制流的语句表达使用符号微分会很大程度上地限制了机器学习框架网络的设计与表达。
4自动微分[@2000An]):自动微分的思想是将计算机程序中的运算操作分解为一个有限的基本操作集合,且集合中基本操作的求导规则均为已知,在完成每一个基本操作的求导后,使用链式法则将结果组合得到整体程序的求导结果。自动微分是一种介于数值微分和符号微分之间的求导方法,结合了数值微分和符号微分的思想。相比于数值微分,自动微分可以精确地计算函数的导数;相比符号微分,自动微分将程序分解为基本表达式的组合,仅对基本表达式应用符号微分规则,并复用每一个基本表达式的求导结果,从而避免了符号微分中的表达式膨胀问题。而且自动微分可以处理分支、循环和递归等控制流语句。目前的深度学习框架基本都采用自动微分机制进行求导运算,下面我们将重点介绍自动微分机制以及自动微分的实现。
### 前向与反向自动微分
自动微分根据链式法则的不同组合顺序可以分为前向模式Forward
Mode和反向模式Reverse
Mode。对于一个复合函数$y=a(b(c(x)))$,其梯度值$\frac{dy}{dx}$的计算公式为:
$$\frac{dy}{dx}=\frac{dy}{da}\frac{da}{db}\frac{db}{dc}\frac{dc}{dx}$$
前向模式的自动微分是从输入方向开始计算梯度值的,其计算公式为:
$$\frac{dy}{dx}=(\frac{dy}{da}(\frac{da}{db}(\frac{db}{dc}\frac{dc}{dx})))$$
反向模式的自动微分是从输出方向开始计算梯度值的,其计算公式为:
$$\frac{dy}{dx}=(((\frac{dy}{da}\frac{da}{db})\frac{db}{dc})\frac{dc}{dx})$$
我们以下面的函数为例介绍两种模式的计算方式,我们希望计算函数在$(x_1, x_2)=(2,5)$处的导数$\frac{\partial y}{\partial x_1}$
$$y=f(x_1,x_2)=ln(x_1)+{x_1}{x_2}-sin(x_2)$$
该函数对应的计算图如:numref:`example_compute_graph`
![示例计算图](../img/ch04/自动微分-示例计算图.svg)
:width:`800px`
:label:`example_compute_graph`
1前向模式
![前向模式自动微分示例](../img/ch04/自动微分-前向模式自动微分示例.png)
:width:`800px`
:label:`forward_AD`
前向模式的计算过程如图:numref:`forward_AD`所示,左侧是源程序分解后得到的基本操作集合,右侧展示了运用链式法则和已知的求导规则,从上至下计算每一个中间变量${\dot{v}_i}=\frac{\partial v_i}{\partial x_1}$,从而计算出最后的变量${\dot{v}_5}=\frac{\partial y}{\partial x_1}$。
当我们想要对一个函数求导时,我们想要得到的是该函数的任意一个输出对任意一个输入的偏微分的集合。对于一个带有$n$个独立输入$x_i$和$m$个独立输出$y_i$的函数$f:{\mathbf{R}^n}\to \mathbf{R}^m$该函数的求导结果可以构成如下的雅克比矩阵Jacobian
Matrix $$\mathbf{J}_{f}=
\begin{bmatrix}
\frac{\partial y_1}{\partial x_1} & \cdots & \frac{\partial y_1}{\partial x_n} \\
\vdots & \ddots & \vdots \\
\frac{\partial y_m}{\partial x_1} & \cdots & \frac{\partial y_m}{\partial x_n}
\end{bmatrix}$$
前向模式中每次计算函数$f$的所有输出对某一个输入的偏微分也就是雅克比矩阵的某一列如下面的向量所示。因此通过n次前向模式的自动微分就可以得到整个雅克比矩阵。
$$\begin{bmatrix}
\frac{\partial y_1}{\partial x_i} \\
\vdots \\
\frac{\partial y_m}{\partial x_i}
\end{bmatrix}$$
前向模式通过计算雅克比向量积Jacobian-vector
products的方式来计算这一列的结果。我们初始化$\dot{\mathbf{x}}=\mathbf{r}$。基本操作的求导规则是已经定义好的,代表着基本操作的雅可比矩阵是已知量。在此基础上,我们应用链式法则从$f$的输入到输出传播求导结果,从而得到输入网络的雅克比矩阵中的一列。
$$\mathbf{J}_{f}\mathbf{r}=
\begin{bmatrix}
\frac{\partial y_1}{\partial x_1} & \cdots & \frac{\partial y_1}{\partial x_n} \\
\vdots & \ddots & \vdots \\
\frac{\partial y_m}{\partial x_1} & \cdots & \frac{\partial y_m}{\partial x_n}
\end{bmatrix}
\begin{bmatrix}
r_1 \\
\vdots \\
r_n
\end{bmatrix}$$
2反向模式
![反向模式自动微分示例](../img/ch04/自动微分-反向模式自动微分示例.png)
:width:`800px`
:label:`backward_AD`
反向模式的计算过程如上图:numref:`backward_AD`所示,左侧是源程序分解后得到的基本操作集合,右侧展示了运用链式法则和已知的求导规则,从$\bar{v}_5=\bar{y}=\frac{\partial y}{\partial y}=1$开始,
由下至上地计算每一个中间变量${\bar{v}_i}=\frac{\partial y_j}{\partial v_i}$,从而计算出最后的变量${\bar{x}_1}=\frac{\partial y}{\partial x_1}$和${\bar{x}_2}=\frac{\partial y}{\partial x_2}$。
反向模式每次计算的是函数$f$的某一个输出对任一输入的偏微分也就是雅克比矩阵的某一行如下面的向量所示。因此通过运行m次反向模式自动微分我们就可以得到整个雅克比矩阵。
$$\begin{bmatrix}
\frac{\partial y_j}{\partial x_1} & \cdots & \frac{\partial y_j}{\partial x_n}
\end{bmatrix}$$
类似地我们可以通过计算向量雅克比积Vector-jacobian
products的方式来计算雅克比矩阵的一行。我们初始化$\bar{\mathbf{y}}=\mathbf{r}$,在已知基本操作的求导规则的前提下,应用链式法则从$f$的输出到输入传播求导结果,从而最后得到雅克比矩阵中的一行。
$$\mathbf{r}^{T}\mathbf{J}_{f}=
\begin{bmatrix}
r_1 & \cdots & r_m
\end{bmatrix}
\begin{bmatrix}
\frac{\partial y_1}{\partial x_1} & \cdots & \frac{\partial y_1}{\partial x_n} \\
\vdots & \ddots & \vdots \\
\frac{\partial y_m}{\partial x_1} & \cdots & \frac{\partial y_m}{\partial x_n}
\end{bmatrix}$$
在求解函数$f$的雅克比矩阵时,前向模式的迭代次数与雅克比矩阵的列数相关,而反向模式的迭代次数则与雅克比矩阵的行数相关。因此,在函数输出个数远远大于输入个数时$(f:{\mathbf{R}^n}\to \mathbf{R}^m, n << m)$,前向模式效率更高;反之,在函数输入个数远远大于输出个数时$(f:{\mathbf{R}^n}\to \mathbf{R}^m, n >> m)$,反向模式效率更高。在极端情况下的函数$f:{\mathbf{R}^n}\to \mathbf{R}$,只需要应用一次反向模式就已经能够把所有输出对输入的导数$(\frac{\partial y}{\partial x_1},\cdots,\frac{\partial y}{\partial n})$都计算出来而前向模式则需要执行n次。这种计算一个标量值的输出关于大量参数输入的梯度的场景恰好是机器学习实践中最常见的一种计算场景这使得反向模式的自动微分成为反向传播算法使用的核心技术之一。
但是反向模式也存在一定的缺陷。在源程序分解为一系列基本操作后前向模式由于求导顺序与基本操作的执行顺序一致输入值可以在执行基本操作的过程中同步获得。而在反向模式中由于求导顺序与源程序的执行顺序是相反的计算过程需要分为两个阶段第一个阶段先执行源程序且将源程序的中间结果保存起来在第二阶段才把中间结果取出来去计算导数。因此反向模式会有额外的内存消耗。业界也一直在研究反向模式的内存占用优化方法例如检查点策略checkpointing
strategies和数据流分析data-flow
analysis[@2006The];[@2017Divide])。
### 自动微分的实现
上一节我们介绍了自动微分的基本概念,可以总结为将程序分解为一系列微分规则已知的基本操作,然后运用链式法则将它们的微分结果组合起来得到程序的微分结果。而在机器学习的应用中,因为输入的数量远远大于输出的数量,所以反向模式的自动微分更受青睐。虽然自动微分的基本思想是明确的,但是具体的实现方法也分为几类([@2015Automatic]大体可以划分为基本表达式法Elemental
Libraries、操作符重载法Operator
OverloadingOO和代码变换法Source Code TransformationST
1基本表达式法封装大多数的基本表达式及对应的微分表达式通过库函数的方式提供给用户用户在写代码时需要手工分解程序为一系列的基本表达式然后使用这些库函数去替换这些基本表达式。以程序$a=(x+y)/z$为例,用户需要手工地把这个程序分解为:
```python
t = x + y
a = t / z
```
然后使用自动微分的库函数去替换分解出来的基本表达式:
```python
// 参数为变量x, y, t和对应的导数变量dx, dy, dt
call ADAdd(x, dx, y, dy, t, dt)
// 参数为变量t, z, a和对应的导数变量dt, dz, da
call ADDiv(t, dt, z, dz, a, da)
```
库函数ADAdd和ADDiv运用链式法则分别定义了Add和Div的微分表达式。
```python
def ADAdd(x, dx, y, dy, z, dz):
z = x + y
dz = dy + dx
def ADDiv(x, dx, y, dy, z, dz):
z = x / y
dz = dx / y + (x / (y * y)) * dy
```
基本表达式法的优缺点显而易见,优点是实现简单直接,可为任意语言快速实现微分的库函数;而缺点是增加了用户的工作量,用户必须先手工分解程序为一些基本表达式,才能使用这些库函数进行编程,无法方便地使用语言原生的表达式。
2操作符重载法Operator Overlading,
OO依赖于现代编程语言的多态特性使用操作符重载对编程语言中的基本操作语义进行重定义封装其微分规则。每个基本操作类型及其输入关系在程序运行时会被记录在一个所谓的"tape"的数据结构里面,最后,这些"tape"会形成一个跟踪轨迹(trace)我们就可以使用链式法则沿着轨迹正向或者反向地将基本操作组成起来进行微分。以自动微分库AutoDiff为例对编程语言的基本运算操作符进行了重载
```C++
namespace AutoDiff
{
public abstract class Term
{
// 重载操作符 `+``*` 和 `/`,调用这些操作符时,会通过其中的
// TermBuilder 将操作的类型、输入输出信息等记录至 tape 中
public static Term operator+(Term left, Term right)
{
return TermBuilder.Sum(left, right);
}
public static Term operator*(Term left, Term right)
{
return TermBuilder.Product(left, right);
}
public static Term operator/(Term numerator, Term denominator)
{
return TermBuilder.Product(numerator, TermBuilder.Power(denominator, -1));
}
}
// Tape 数据结构中的基本元素,主要包含:
// 1) 操作的运算结果
// 2) 操作的运算结果对应的导数结果
// 3) 操作的输入
// 除此外还通过函数 Eval 和 Diff 定义了该运算操作的计算规则和微分规则
internal abstract class TapeElement
{
public double Value;
public double Adjoint;
public InputEdges Inputs;
public abstract void Eval();
public abstract void Diff();
}
}
```
OO对程序的运行跟踪经过了函数调用和控制流因此实现起来也是简单直接。而缺点是需要在程序运行时进行跟踪特别在反向模式上还需要沿着轨迹反向地执行微分所以会造成性能上的损耗尤其对于本来运行就很快的基本操作。并且因为其运行时跟踪程序的特性该方法不允许在运行前做编译时刻的图优化控制流也需要根据运行时的信息来展开。Pytorch的自动微分框架使用了该方法。
3代码变换法Source
TransformationST提供对编程语言的扩展分析程序的源码或抽象语法树AST将程序自动地分解为一系列可微分的基本操作而这些基本操作的微分规则已预定义好最后使用链式法则对基本操作的微分表达式进行组合生成新的程序表达来完成微分。TensorFlowMindSpore等机器学习框架都采用了该方式。
不同于OO在编程语言内部操作ST需要语法分析器parser和操作中间表示的工具。除此以外ST需要定义对函数调用和控制流语句如循环和条件等的转换规则。其优势在于对每一个程序自动微分的转换只做一次因此不会造成运行时的额外性能损耗。而且因为整个微分程序在编译时就能获得编译器可以对微分程序进行进一步的编译优化。但ST实现起来更加复杂需要扩展语言的预处理器、编译器或解释器且需要支持更多的数据类型和操作需要更强的类型检查系统。另外虽然ST不需要在运行时做自动微分的转换但是对于反向模式在反向部分执行时仍然需要确保前向执行的一部分中间变量可以被获取到有两种方式可以解决该问题[@van2018Automatic]
1基于Tape的方式。该方式使用一个全局的"tape"去确保中间变量可以被获取到。原始函数被扩展为在前向部分执行时把中间变量写入到tape中的函数在程序执行反向部分时会从tape中读取这些中间变量。除了存储中间变量外OO中的tape还会存储执行的操作类型。然而因为tape是一个在运行时构造的数据结构所以需要添加一些定制化的编译器优化方法。且为了支持高阶微分对于tape的读写都需要是可微分的。而大多数基于tape的工具都没有实现对tape的读写操作的微分因此它们都不支持多次嵌套执行反向模式的自动微分reverse-over-reverse。机器学习框架Tangent采用了该方式。
2基于闭包closure的方式。基于闭包的方式可以解决基于tape方式的缺陷。在函数式编程里闭包可以捕获到语句的执行环境并识别到中间变量的非局部使用。因为这些它们是闭包里的自由变量所以不需要再去定制化编译器优化方法。
MindSpore是使用基于闭包的代码变换法来实现的自动微分的。这需要一个定制的中间表示。MindIR的具体设计在上一节中已经介绍过这里不再赘述。
MindSpore的自动微分使用基于闭包的代码变换法实现转换程序根据正向部分的计算构造了一个闭包的调用链。这些闭包包含了计算导数的代码以及从正向部分拿到的中间变量。程序中的每个函数调用都会得到转换并且额外返回一个叫做"bprop"的函数,$bprop$根据给定的关于输出的导数,计算出关于输入的导数。由于每个基本操作的$bprop$是已知的,我们可以容易地反向构造出用户定义的整个函数的$bprop$。为了支持reverse-over-reverse调用去计算高阶导数我们需要确保可以在已转换好的程序中再进行转换这需要有处理函数自由变量函数外定义的变量的能力。为了达到这个目的每个$bprop$除了关于原始函数输入的偏导数以外,还会返回一系列关于自由变量的偏导数,闭包里面的$bprop$负责把每个偏导数解开将其分别累加贡献到各自的自由变量上。且闭包也是一种函数可以作为其他闭包的输入。因此MindSpore自动微分的算法设计可以总结为
1应用链式求导法则对每个函数算子或子图定义一个反向传播函数$bprop: dout->(df, dinputs)$,这里$df$表示函数对自由变量的导数,$dinputs$表示函数对输入的导数。
2应用全微分法则将($df$, $dinputs$)累加到对应的变量上。
涉及控制流语句时因为MindIR实现了分支、循环和闭包等操作的函数式表达我们对这些操作应用上述法则进行组合即可完成微分。定义运算符K求解导数MindSpore的自动微分算法可以简单表达如下
```C++
// func和inputs分别表示函数及其输入dout为关于输出的梯度
v = (func, inputs)
F(v): {
(result, bprop) = K(func)(inputs)
df, dinputs = bprop(dout)
v.df += df
v.dinputs += dinputs
}
```
MindSpore解析器模块首先根据Python的AST生成MindIR再经过特化模块使得中间表示中的算子可识别然后调用自动微分模块。自动微分模块的入口函数如下所示
```C++
function Grad {
Init();
MapObject(); // 实现Parameter/Primitive/FuncGraph/FreeVariable对象的映射
MapMorphism(); // 实现CNode的映射
Finish();
Return GetKGraph(); // 获取梯度函数计算图
}
```
Grad函数先通过MapObject实现图上自由变量、Parameter和ValueNodePrimitive或FuncGraph等节点到$fprop$的映射。$fprop$是$(forward\_result, bprop)$形式的梯度函数对象。$forward\_result$是前向计算图的输出节点,$bprop$是以$fprop$的闭包对象形式生成的梯度函数,它只有$dout$一个入参,其余的输入则是引用的$fprop$的输入和输出。其中对于ValueNode\<Primitive\>类型的$bprop$通过解析Python层预先注册的$get\_bprop$函数的得到如下所示。对于ValueNode\<FuncGraph\>类型的节点,则递归求出它的梯度函数对象。
```python
@bprop_getters.register(P.ReLU)
def get_bprop_relu(self):
"""Grad definition for `ReLU` operation."""
input_grad = G.ReluGrad()
def bprop(x, out, dout):
dx = input_grad(dout, out)
return (dx,)
return bprop
```
随后MapMorphism函数从原函数的输出节点开始实现对CNode的映射并建立起节点间的反向传播连接实现梯度累加最后返回原函数的梯度函数计算图。

View File

@@ -0,0 +1,44 @@
常见前端编译优化方法
--------------------
和传统编译器相同,机器学习编译器也会进行编译优化。编译优化意在解决编译生成的中间表示的低效性,使得代码的长度变短,编译与运行的时间减少,执行期间处理器的能耗变低。编译优化可以分为与硬件无关的优化和与硬件相关的编译优化。因为前端是不感知具体后端硬件的,因此前端执行的全部都是与硬件无关的编译优化。
### 前端编译优化简介
大多数编译优化器会由一系列的"趟"Pass来组成。每个"趟"以中间表示为输入,又以新生成的中间表示为输出。一个"趟"还可以由几个小的"趟"所组成。一个"趟"可以运行一次,也可以运行多次。
在编译优化中,优化操作的选择以及顺序对于编译的整体具有非常关键的作用。优化操作的选择决定了优化器能够感知中间表示中的哪些低效性,也决定了编译器将要如何去重写中间表示以消除这种低效性。优化操作的顺序决定了各趟操作的执行顺序。编译器可以根据具体需要运行不同的编译优化操作。也可以根据编译优化级别来调整优化的次数,种类以及顺序。
![编译优化的"趟"结构](../img/ch04/编译优化-pass结构.svg)
:width:`800px`
:label:`pass_structure`
### 常见编译优化方法介绍及实现
前端编译优化的方法有很多,机器学习框架也有很多不同于传统编译器的优化方式。在本小节当中,我们会介绍三种常见且通用的前端编译优化方法。
1\. 无用与不可达代码消除
如图 :numref:`pass_useless_code_elimination`所示。无用代码是指输出结果没有被任何其他代码所使用的代码。不可达代码是指没有有效的控制流路径包含该代码。删除无用或不可达的代码可以使得中间表示更小,提高程序的编译与执行速度。无用与不可达代码一方面有可能来自于程序编写者的编写失误,也有可能是其他编译优化所产生的结果。
![无用代码消除](../img/ch04/编译优化-无用代码消除.svg)
:width:`600px`
:label:`pass_useless_code_elimination`
2\. 常量传播、常量折叠
常量传播:如图 :numref:`pass_constant_broadcast`所示,如果某些量为已知值的常量,那么可以在编译时刻将使用这些量的地方进行替换。
常量折叠:如图 :numref:`pass_constant_broadcast`所示,多个量进行计算时,如果能够在编译时刻直接计算出其结果,那么变量将由常量替换。
![常量传播与常量折叠](../img/ch04/编译优化-常量传播与常量折叠.svg)
:width:`600px`
:label:`pass_constant_broadcast`
3\. 公共子表达式消除
如图 :numref:`pass_CSE`所示如果一个表达式E已经计算过了并且从先前的计算到现在E中所有变量的值都没有发生变化那么E就成为了公共子表达式。对于这种表达式没有必要花时间再对它进行计算只需要直接用前面计算过的表达式结果代替E就可以了。
![公共子表达式消除](../img/ch04/编译优化-公共子表达式消除.svg)
:width:`600px`
:label:`pass_CSE`

View File

@@ -1,10 +1,31 @@
# 编译器前端和IR
# 编译器前端
在上一章节中我们详细讨论了计算图的生成和调度在进阶部分的介绍中简单介绍了深度学习编译器的作用。定义深度学习模型、计算图使用系统为用户提供的高级编程API我们将用户使用高级编程API编写的程序称为源程序将与硬件相关的程序称为目标程序深度学习编译器需要理解输入的源程序并将其映射到目标机。为了实现这两项任务编译器的设计被分解为两个主要部分前端和后端。传统编译器的前端专注于理解源程序后端则专注于将功能映射到目标机。为了将前后端相连接我们需要一种结构来表示转换后的源代码这就是中间表示Intermediate
Representation, IR
图 :numref:`compiler_frontend_structure`展示了机器学习编译器的前端的流程。其中,对源程序的解析过程与传统编译器是大致相同的,本章节不对这部分进行更细致的讨论。机器学习框架的编译器前端的独特之处主要在于自动微分功能的支持。为了满足自动微分功能带来的新需求,机器学习框架需要在传统中间表示的基础上设计新的中间表示结构。因此,本章节的介绍重点会放在中间表示以及自动微分这两个部分。最后,我们会简要探讨类型系统,静态分析和前端优化等编译器基础概念。
![编译器前端基础结构](../img/ch04/编译器前端基础架构.svg)
:width:`1000px`
:label:`compiler_frontend_structure`
本章的学习目标包括:
- 理解中间表示的基础概念,特点和实现方法
- 理解自动微分的基础概念,特点和实现方法
- 了解类型系统和静态推导的基本原理
- 了解编译器优化的主要手段和常见优化方法
```toc
:maxdepth: 2
:numbered:
```
overview_of_frontend
intermediate_representation
ad
type_system_and_static_analysis
common_frontend_optimization_pass
summary
```

View File

@@ -0,0 +1,187 @@
中间表示
--------
中间表示作为编译器的核心数据结构之一,无论是在传统编译器中,还是在机器学习框架中,
都有着极其重要的地位。本章节我们会先介绍中间表示的基本概念以及传统编译器的中间表示类型。在此基础上,我们会探讨针对机器学习框架,中间表示的设计所面临的新的需求和挑战。最后,我们会介绍现有机器学习框架的中间表示的种类及其实现。
### 中间表示的基本概念
中间表示(IR),是编译器用于表示源代码的数据结构或代码,是程序编译过程中介于源语言和目标语言之间的程序表示。几乎所有的编译器都需要某种形式的中间表示,来对被分析、转换和优化的代码进行建模。在编译过程中,中间表示必须具备足够的表达力,在不丢失信息的情况下准确表达源代码,并且充分考虑从源代码到目标代码编译的完备性、编译优化的易用性和性能。
引入中间表示后,中间表示既能面向多个前端,表达多种源程序语言,又能对接多个后端,连接不同目标机器,如图 :numref:`intermediate_representation`所示。在此基础上编译流程就可以在前后端直接增加更多的优化流程这些优化流程以现有IR为输入又以新生成的IR为输出被称为优化器。优化器负责分析并改进中间表示极大程度的提高了编译流程的可拓展性也降低了优化流程对前端和后端的破坏。
![中间表示](../img/ch04/中间表示-中间表示结构.svg)
:width:`800px`
:label:`intermediate_representation`
随着编译器技术的不断演进,中间表示主要经历了三个发展阶段。在早期阶段,中间表示是封闭在编译器内部的,供编译器编写者使用。在中期阶段,随着编译器的开源,中间表示逐步开源公开,主要供编译器设计者、分析工具设计者使用。现阶段,中间表示朝着软件生态构建的方向发展,旨在构建统一的中间表示。
### 中间表示的种类
上一节介绍了中间表示的基本概念,初步阐述了中间表示的重要作用和发展历程。接下来从组织结构的角度出发,介绍通用编译器的中间表示的类型以及各自特点([@2007Engineering]),如下表所示。中间表示组织结构的设计,对编译阶段的分析优化、代码生成等有着重要影响。编译器的设计需求不同,采用的中间表示组织结构也有所不同。
::: {#tab:ch04/ch04-categorize}
组织结构 特点 举例
-------------- ---------------------- ----------------------------------
Linear IR 基于线性代码 堆栈机代码、三地址代码
Graphical IR 基于图 抽象语法树、有向无环图、控制流图
Hybrid IR 基于图与线性代码混合 LLVM IR
: 中间表示的分类
:::
1\) 线性中间表示
线性中间表示类似抽象机的汇编代码,将被编译代码表示为操作的有序序列,对操作序列规定了一种清晰且实用的顺序。由于大多数处理器采用线性的汇编语言,线性中间表示广泛应用于编译器设计。
常用线性中间表示有堆栈机代码(Stack-Machine Code)和三地址代码(Three
Address Code) [@2007Compilers]
。堆栈机代码是一种单地址代码提供了简单紧凑的表示。堆栈机代码的指令通常只有一个操作码其操作数存在一个栈中。大多数操作指令从栈获得操作数并将其结果推入栈中。三地址代码简称为3AC模拟了现代RISC机器的指令格式。它通过一组四元组实现每个四元组包括一个运算符和三个地址(两个操作数、一个目标)。对于表达式a-b\*5堆栈机代码和三地址代码如图 :numref:`linear_ir`所示。
![堆栈机代码和三地址代码](../img/ch04/中间表示-线性中间表示.svg)
:width:`800px`
:label:`linear_ir`
2、图中间表示
图中间表示将编译过程的信息保存在图中,算法通过图中的对象如节点、边、列表、树等来表述。虽然所有的图中间表示都包含节点和边,但在抽象层次、图结构等方面各有不同。常见的图中间表示包括抽象语法树(Abstract
Syntax TreeAST)、有向无环图(Directed Acyclic
GraphDAG)、控制流图(Control-Flow GraphCFG)等。
AST抽象语法树采用树型中间表示的形式是一种接近源代码层次的表示。对于表达式$a*5+a*5*b$其AST表示如图 :numref:`AST_DAG`所示。可以看到AST形式包含$a*5$的两个不同副本存在冗余。在AST的基础上DAG提供了简化的表达形式一个节点可以有多个父节点相同子树可以重用。如果编译器能够证明$a$的值没有改变则DAG可以重用子树降低求值过程的代价。
![AST图和DAG图](../img/ch04/中间表示-ASTDAG.svg)
:width:`600px`
:label:`AST_DAG`
3、混合中间表示
混合中间表示是线性中间表示和图中间表示的结合这里以LLVM IR
[@2004LLVM] 为例进行说明。LLVM(Low Level Virtual
Machine)是2000年提出的开源编译器框架项目旨在为不同的前端后端提供统一的中间表示。LLVM
IR使用线性中间表示表示基本块使用图中间表示表示这些块之间的控制流如图 :numref:`LLVM_IR`所示。基本块中,每条指令以静态单赋值(Static
Single Assignment SSA) [@Richard1995A]
形式呈现这些指令构成一个指令线性列表。SSA形式要求每个变量只赋值一次并且每个变量在使用之前定义。控制流图中每个节点为一个基本块基本块之间通过边实现控制转移。
![LLVM IR](../img/ch04/中间表示-LLVMIR.svg)
:width:`600px`
:label:`LLVM_IR`
### 机器学习框架的中间表示
上一节介绍了中间表示的类型并举例说明了常见的中间表示形式。传统中间表示如LLVM
IR能够很好地满足通用编译器的基本功能需求包括类型系统、控制流和数据流分析等。然而它们偏向机器语言难以满足机器学习框架编译器的中间表示的需求。
在设计机器学习框架的中间表示时,需要充分考虑以下因素:
1\)
张量表达。机器学习框架主要处理张量数据,因此正确处理张量数据类型是机器学习框架中间表示的基本要求。
2\)
自动微分。自动微分是指对网络模型的自动求导,通过梯度指导对网络权重的优化。主流机器学习框架都提供了自动微分的功能,在设计中间表示时需要考虑自动微分实现的简洁性、性能以及高阶微分的扩展能力。
3\)
计算图模式。主流机器学习框架如TensorFlow、PyTorch、MindSpore等都提供了静态图和动态图两种计算图模式静态计算图模式先创建定义计算图再显式执行有利于对计算图进行优化高效但不灵活。动态计算图模式则是每使用一个算子后该算子会在计算图中立即执行得到结果使用灵活、便于调试但运行速度较低。机器学习框架的中间表示设计同时支持静态图和动态图可以针对待解决的任务需求选择合适的模式构建算法模型。
4\)
支持高阶函数和闭包([@2010C])。高阶函数和闭包是函数式编程的重要特性,高阶函数是指使用其它函数作为参数、或者返回一个函数作为结果的函数,闭包是指代码块和作用域环境的结合,可以在另一个作用域中调用一个函数的内部函数,并访问到该函数作用域中的成员。支持高阶函数和闭包,可以抽象通用问题、减少重复代码、提升框架表达的灵活性和简洁性。
5\)
编译优化。机器学习框架的编译优化主要包括硬件无关的优化、硬件相关的优化、部署推理相关的优化等,这些优化都依赖于中间表示的实现。
6\) JIT(Just In
Time)能力。机器学习框架进行编译执行加速时经常用到JIT即时编译。JIT编译优化将会对中间表示中的数据流图的可优化部分实施优化包括循环展开、融合、内联等。中间表示设计是否合理将会影响机器学习框架的JIT编译性能和程序的运行能力。
针对上述需求,机器学习框架的开发者在传统中间表示的设计基础上不断扩展,提出了很多适用于机器学习框架的中间表示。接下来介绍一些主流机器学习框架的中间表示。
1、PyTorch
PyTorch框架是一个基于动态计算图机制的机器学习框架以Python优先具有很强的易用性和灵活性方便用户编写和调试网络代码。为了保存和加载网络模型PyTorch框架提供了TorchScript方法用于创建可序列化和可优化模型。TorchScript
IR作为PyTorch模型的中间表示通过JIT即时编译的形式将Python代码转换成目标模型文件。任何TorchScript程序都可以在Python进程中保存并加载到没有Python依赖的进程中。
PyTorch框架采用命令式编程方式其TorchScript
IR以基于SSA的线性IR为基本组成形式并通过JIT即时编译的Tracing和Scripting两种方法将Python代码转换成TorchScript
IR。图 :numref:`TorchScript_IR`给出了Python示例代码及其TorchScript
IR。
![Python代码及输出的TorchScript IR](../img/ch04/中间表示-torchscript.png)
:width:`800px`
:label:`TorchScript_IR`
TorchScript是PyTorch的JIT实现支持使用Python训练模型然后通过JIT转换为语言无关的模块从而提升模型部署能力提高编译性能。同时TorchScript
IR显著改善了Pytorch框架的模型可视化效果。
2、TensorFlow
与PyTorch框架的动态图机制不同TensorFlow机器学习框架因为其静态图机制而被人熟知。关于静态图和动态图的介绍请参考第3.3章节。
TensorFlow框架同时支持静态图和动态图是一个基于数据流编程的机器学习框架使用数据流图作为数据结构进行各种数值计算。为了适配不同的硬件平台基于静态计算图TensorFlow采用了多种IR设计其编译生态系统如图 :numref:`MLIR`所示。蓝色部分是基于图的中间表示绿色部分是基于SSA的中间表示各层级在结构和抽象层级上存在较大的差距转换开销大而且同一层级的中间表示优化是相互独立的不利于协同优化。
![TensorFlow MLIR](../img/ch04/中间表示-MLIR.svg)
:width:`600px`
:label:`MLIR`
针对这个问题TensorFlow团队提出了MLIR(Multi-Level Intermediate
Represent多级中间表示)
([@2020MLIR]允许使用TensorFlow和其它机器学习库的项目编译更有效的代码从而最大程度地利用基础硬件。MLIR是用于现代优化编译器的灵活基础架构旨在定义一个通用的中间表示在统一的基础架构中支持多种不同的需求。MLIR采用混合中间表示允许在同一编译单元中结合多个层级的抽象来表示、分析和转换计算图利用其模块化、可扩展的特点解决了各种中间表示之间转换效率和可迁移性不高的问题从而适配多种硬件平台。
3、Jax
Jax机器学习框架同时支持静态图和动态图其中间表示采用Jaxpr(JAX Program
Representation) IR。Jaxpr
IR是一种强类型、纯函数的中间表示其输入、输出都带有类型信息函数输出只依赖输入不依赖全局变量。
![ANF文法与Jaxpr IR](../img/ch04/中间表示-Jaxpr.png)
:width:`800px`
:label:`Jaxpr`
Jaxpr IR的表达采用ANF(A-norm
Form)函数式表达形式,如图 :numref:`Jaxpr`所示。ANF形式将表达式划分为两类原子表达式(aexp)和复合表达式(cexp)。原子表达式用于表示常数、变量、原语、匿名函数,复合表达式由多个原子表达式组成,可看作一个匿名函数或原语函数调用,组合的第一个输入是调用的函数,其余输入是调用的参数。
Jax框架结合了Autograd 和 JIT基于Jaxpr
IR支持循环、分支、递归、闭包函数求导以及三阶求导并且支持自动微分的反向传播和前向传播。
4、MindSpore
与PyTorch、TensorFlow、Jax框架相同MindSpore机器学习框架同时支持静态图和动态图。MindSpore框架采用的是一种基于图表示的函数式中间表示即MindIR全称MindSpore
IR。MindIR通过统一的中间表示定义了网络的逻辑结构和算子的属性能够消除不同后端的模型差异连接不同的目标机器。
MindIR最核心的目的是服务于自动微分变换而自动微分采用的是基于函数式编程框架的变换方法因此MindIR采用了接近于ANF函数式的语义。MindIR具有以下特点
1基于图的Graph
based。与TensorFlow类似程序使用图来表示使其容易去做优化。但跟TensorFlow不一样的是在MindSpore中函数是"一等公民"。函数可以被递归调用也可以被当做参数传到其他的函数中或者从其他函数中返回使得MindSpore可以表达一系列的控制流结构。
2纯函数的Purely functional
纯函数是指函数的结果只依赖函数的参数。若函数依赖或影响外部的状态,比如,函数会修改外部全局变量,或者函数的结果依赖全局变量的值,则称函数具有副作用([@spuler1994compiler])。若使用了带有副作用的函数,代码的执行顺序必须得到严格的保证,否则可能会得到错误的结果,比如对全局变量的先写后读变成了先读后写。同时,副作用的存在也会影响自动微分,因为反向部分需要从前向部分获取中间变量,需要确保该中间变量的正确。因此需要保证自动微分的函数是纯函数。
由于Python语言具有高度动态性的特点纯函数式编程对用户使用上有一些编程限制。有些机器学习框架的自动微分功能只支持对纯函数求导且要求用户自行保证这一点。如果用户代码中写了带有副作用的函数那么求导的结果可能会不符合预期。MindIR支持副作用的表达能够将副作用的表达转换为纯函数的表达从而在保持ANF函数式语义不变的同时确保执行顺序的正确性从而实现自由度更高的自动微分。
3支持闭包表示的Closure
representation。反向模式的自动微分需要存储基本操作的中间结果到闭包中然后再去进行组合连接。所以有一个自然的闭包表示尤为重要。闭包是指代码块和作用域环境的结合在MindIR中代码块是以函数图呈现的而作用域环境可以理解为该函数被调用时的上下文环境。
4强类型的Strongly
typed。每个节点需要有一个具体的类型这个对于性能最大化很重要。在机器学习应用中因为算子可能很耗费时间所以越早捕获错误越好。因为需要支持函数调用和高阶函数相比于TensorFlow的数据流图MindIR的类型和形状推导更加复杂且强大。
在结合MindSpore框架的自身特点后MindIR的定义如图 :numref:`MindIR`所示。
![MindIR文法。MindIR中的ANode对应于ANF的原子表达式ValueNode用于表示常数值ParameterNode用于表示函数的形参CNode则对应于ANF的复合表达式表示函数调用](../img/ch04/中间表示-MindIR.svg)
:width:`800px`
:label:`MindIR`
接下来我们通过图 :numref:`MindIR_example`中的一段程序作为示例来进一步分析MindIR。
![MindIR的ANF表达](../img/ch04/中间表示-MindIR示例.PNG)
:width:`600px`
:label:`MindIR_example`
在ANF中每个表达式都用let表达式绑定为一个变量通过对变量的引用来表示对表达式输出的依赖而在MindIR中每个表达式都绑定为一个节点通过节点与节点之间的有向边表示依赖关系。其函数图表示如图 :numref:`MindIR_graph`所示。
![MindIR的函数图表示](../img/ch04/中间表示-MindIR图.png)
:width:`800px`
:label:`MindIR_graph`
MindIR同时支持静态计算图和动态计算图的构建方式更好地兼顾了灵活性与高性能。相比传统计算图MindIR不仅可以表达算子之间的数据依赖还可以表达丰富的函数式语义具备更自然的自动微分实现方式。MindIR原生支持闭包并且支持高阶函数的表达。在处理控制流时MindIR将控制流转换为高阶函数的数据流不仅支持数据流的自动微分还支持条件跳转、循环和递归等控制流的自动微分从而提升MindSpore的自动微分能力。
在JIT即时编译方面MindIR采用了基于图表示的形式将控制流和数据流合一支持更高效的JIT优化。在编译优化方面MindIR引入优化器对计算图进行优化采用前端-优化器-后端的三段式表达形式,支持硬件无关的优化(如类型推导、表达式化简等)、硬件相关的优化如自动并行、内存优化、图算融合、流水线执行等以及部署推理相关的优化如量化、剪枝等显著提升了MindSpore的编译执行能力。

View File

@@ -0,0 +1,23 @@
概述
----
本章节将讨论重点放在中间表示与自动微分章节。自动微分作为机器学习框架的编译器的独有功能,其实现需要满足其需求的中间表示的支持。在讨论完这两部分后,我们会简要介绍类型系统系统,静态分析和前端编译优化等编译器基础概念。
### 中间表示
中间表示是编译器用于表示源代码的数据结构或代码,是程序编译过程中介于源语言和目标语言之间的程序表示。传统机器学习框架的中间表示分为三大类,分别是线性中间表示,图中间表示以及混合中间表示。然而,传统编译器的中间表示难以完全满足机器学习框架对于中间表示的一系列需求。因此,机器学习框架的开发者在传统中间表示的设计基础上不断扩展,提出了很多适用于机器学习框架的中间表示。
### 自动微分
自动微分Automatic Differentiation
AD是一种介于符号微分和数值微分之间的针对计算图进行符号解析的求导方法用于计算函数梯度值。深度学习等现代AI算法通过使用大量数据来学习拟合出一个优化后带参模型其中使用的学习算法多是基于现实数据在模型中的经验误差通过梯度下降的方法来更新模型的参数。因此自动微分在深度学习中处于非常重要的地位是整个训练算法的核心组件之一。自动微分通常在编译器前端优化中实现通过对中间表示的符号解析来生成带有梯度函数的中间表示。
### 类型系统与静态分析
为了有效减少程序在运行时可能出现的错误编译器的前端引入了类型系统Type
System和静态分析Static
Analysis系统。类型系统可以防止程序在运行时发生类型错误而静态分析能够为编译优化提供线索和信息有效减少代码中存在的结构性错误、安全漏洞等问题。
### 前端编译优化
编译优化意在解决代码的低效性,无论是在传统编译器还是在机器学习框架中都起着很重要的作用。前端的编译优化与硬件无关。

View File

@@ -0,0 +1,22 @@
总结
----
- 中间表示是编译器的核心数据结构之一,是程序编译过程中介于源语言和目标语言之间的程序表示。
- 传统编译器的中间表示从组织结构出发,可以分为线性中间表示,图中间表示以及混合中间表示。
- 机器学习框架的中间对中间表示有一系列新的需求,这些新的需求是传统中间表示所不能完美支持的。因此需要在传统中间表示的基础上扩展新的,更适用于机器学习框架的中间表示。
- 自动微分的基本思想是将将计算机程序中的运算操作分解为一个有限的基本操作集合,且集合中基本操作的求导规则均为已知,在完成每一个基本操作的求导后,使用链式法则将结果组合得到整体程序的求导结果。
- 自动微分根据链式法则的组合顺序,可以分为前向自动微分与反向自动微分。
- 前向自动微分更适用于对输入维度小于输出维度的网络求导,反向自动微分则更适用于对输出维度小于输入维度的网络求导。
- 自动微分的实现方法大体上可以划分为基本表达式法,操作符重载法以及代码变化法。
- 类型系统是指类型的集合以及使用类型来规定程序行为的规则,用于定义不同的类型,指定类型的操作和类型之间的相互作用,广泛应用于编译器、解释器和静态检查工具中。
- 静态分析,是指在不实际运行程序的情况下,通过词法分析、语法分析、控制流、数据流分析等技术对代码进行分析验证的技术
- 编译优化意在解决编译生成的中间表示的低效性,前端执行的均为与硬件无关的编译优化。

View File

@@ -0,0 +1,41 @@
类型系统和静态分析
------------------
上一章节介绍了自动微分的基本概念和实现方法,自动微分是机器学习框架中不可或缺的核心功能。在编译器前端的设计中,为了提高编译器的抽象能力和程序运行的正确性,有效减少程序在运行时可能出现的错误,编译器引入了类型系统和静态分析系统,接下来将对它们的基本概念、主要功能、常见系统进行介绍。
### 类型系统概述
程序设计语言中,类型是指数值、表达式、函数等属性内容。类型系统是指类型的集合以及使用类型来规定程序行为的规则。类型系统用于定义不同的类型,指定类型的操作和类型之间的相互作用,广泛应用于编译器、解释器和静态检查工具中。类型系统提供的主要功能有:
1正确性。编译器的类型系统引入了类型检查技术用于检测和避免运行时错误确保程序运行时的安全性。通过类型推导与检查编译器能够捕获大多数类型相关的异常报错避免执行病态程序导致运行时错误保证内存安全避免类型间的无效计算和语义上的逻辑错误。
2优化。静态类型检查可以提供有用的信息给编译器从而使得编译器可以应用更有效的指令节省运行时的时间。
3抽象。在安全的前提下一个强大的类型系统的标准是抽象能力。通过合理设计抽象开发者可以更关注更高层次的设计。
4可读性。阅读代码时明确的类型声明有助于理解程序代码。
机器学习框架一般使用Python语言作为描述网络模型结构的前端语言。Python语言是一门动态弱类型的语言入门简单易学习开发代码简洁高效但由于其解释执行的方式运行速度往往较慢。Python前端语言给用户带来了动态灵活的语义和高效的开发效率但是若想要生成运行高效的后端代码后端框架需要优化友好的静态强类型中间表示。因此需要一种高效可靠的静态分析方法作为桥梁将Python前端表示转换成等价的静态强类型中间表示以此给用户同时带来高效的开发效率和运行效率例如Hindley--MilnerHM类型系统。这是一种具有参数多态性的简单类型lambda演算的类型系统。它最初由J.
Roger Hindley 提出([@1969The]并由Robin Milner
进行扩展和验证([@1978A]。后来路易斯·达马斯Luis
Damas对HM类型推导方法进行了详尽的分析和证明[@1982Principal]并将其扩展到支持具有多态引用的系统。Hindley--Milner类型系统的目标是在没有给定类型注解的情况下自动推导出任意表达式的类型。其算法具有抽象性和通用性采用简洁的符号表示能够根据表达式形式推导出明确直观的定义常用于类型推导和类型检查。因此Hindley--Milner类型系统广泛应用于编程语言设计中比如Haskell和Ocaml。
### 静态分析概述
在编译器前端中,除了类型系统的设计,还需要对程序代码进行检查分析,经常用到静态分析技术。静态分析,是指在不实际运行程序的情况下,通过词法分析、语法分析、控制流、数据流分析等技术对代码进行分析验证的技术。
词法分析:将字符流变换为输入语言的单流,每个单词都必须归类到某个语法范畴中。常用的词法分析器包括表驱动词法分析器、直接编码词法分析器和手工编码的词法分析器。
语法分析:判断单词流表示的输入程序在程序设计语言中是否是一个有效的句子。语法分析分为自顶向下语法分析和自底向上语法分析。自顶向下语法分析器从语法分析树的根开始,系统化地向下扩展树,直至树的叶节点与语法分析器返回的已归类单词相匹配。而自底向上语法分析器从叶结点开始构建语法分析树,自叶结点向根节点的方向前景。
控制流分析:控制流分析是分析程序控制流图、获得程序静态属性的静态分析方法。常见的控制流分析方法包括抽象释义、约束补偿和类型系统等。
数据流分析:数据流分析用于确定可进行优化的机会,并证明变换的安全性。
通过以上几种分析,静态分析对程序代码进行扫描,验证代码是否满足规范性、安全性、可靠性、可维护性等指标。
接下来以MindSpore框架为例简要介绍一下静态分析模块的具体实现。MindSpore采用抽象释义的方法对抽象值做不确定的抽象语义的解释执行函数图中每个节点的抽象值是所期望得到的程序静态信息。基本的抽象释义方法流程可以理解为从MindIR的顶层函数图入口开始解释执行将函数图中所有节点进行拓扑排序根据节点的语义递归推导各节点的抽象值。当遇到函数子图时递归进入函数子图进行解释执行最后返回顶层函数输出节点的抽象值。根据抽象释义方法流程MindSpore的静态分析模块主要分为抽象域模块、缓存模块、语义推导模块和控制流处理模块。
![静态分析模块](../img/ch04/静态分析-静态分析模块.png)
:width:`850px`
:label:`static_analysis_module`

View File

@@ -1,4 +1,4 @@
# 计算图的设计背景和作用
# 计算图
在上一章节中,我们展示了用户的利用机器学习框架所编写的程序。这些用户程序包含了对于训练数据,模型和训练过程的定义。然而为了运行这些程序,机器学习系统依然需要解决诸多问题,包括:如何高效执行一个复杂的机器学习模型?如何识别出机器学习模型中需要训练的参数?如何自动计算更新模型所需的梯度?为了解决这些问题,现代机器学习框架实现了*计算图*(Computational
graph)这一技术。在本章中,我们详细讨论计算图的基本组成,生成和执行等关键设计。本章的学习目标包括:

View File

@@ -0,0 +1,21 @@
## 保序性设计
和常规数据并行计算任务不同的是,机器学习场景下的数据并行处理为了确保实验的可复现性需要维护保序的性质。在具体实现中,我们需要保证并行数据预处理后的数据输出顺序与输入顺序保持相同(即下图中的SeqB和SeqA相同)。这确保了每一次的数据模块的结果输出顺序由数据混洗模块输出顺序唯一确定有助于用户在不同的实验之间进行比较和调试。不同的机器学习系统采用了不同的方案来确保保序性我们以MindSpore的实现为例子进行介绍以加深读者对这部分内容的理解。
![数据的保序性——确保SeqB与SeqA相同](../img/ch07/7.4/data_ordering.png)
:width:`800px`
:label:`data_order_definition`
MindSpore通过约束算子线程组间的通信行为来确保对当前算子的下游算子的输入顺序与自己的输入顺序相同基于这种递归的约束确保了整个并行数据处理最后一个算子的输出顺序与第一个算子的输入顺序相同。具体实现中MindSpore以Connector为算子线程组间的通信组件对Connector的核心操作为上游算子的Push操作以及下游算子的Pop操作我们重点关注MindSpore对这两个行为的约束。
Connector的使用有如下两个要求:
- Connector两端的数据生产线程组和数据消费线程组中的线程分别从0开始编号。
- 确保数据生产者的输入数据顺序是在各个生产者线程间为按顺序轮询分布(Round-Robin distribution), 即当生产者线程组大小为M时生产者线程0拥有第(0 + M \* k)个数据生产者线程1拥有第(1 + M \* k)生产者线程2拥有第(2 + M \* k)个数据等(其中k=0123\...)。
Connector中维护与生产者线程数目相同的队列并确保向Connector中放入数据时每个生产者线程生产的数据只放到对应编号的队列中这样可以确保Connector中的数据在不同的队列间的分布与在不同生产者线程组之间的分布相同(代码片段中的Push函数)。接着当Connector的消费者线程组从Connector中获取数据时我们需要确保最终数据在不同的消费者线程间依然为按顺序轮询分布即当消费者线程组大小为N时消费者线程0拥有第(0 + N \* k)个数据消费者线程1拥有第(1 + N \* k)消费者线程2拥有第(2 + N \* k)个数据等(其中k=0123\...)。为此当有消费者线程从Connector中请求数据时Connector在确保当前请求消费者线程编号i与待消费数据标号j符合$i=j\%N$的关系下(其中N为消费者线程数目)按照轮循的方式从各个队列中获取数据如果二者标号不符合上述关系则该请求阻塞等待。通过这种通信的约束方式MindSpore实现了保序功能。
![MindSpore保序性实现](../img/ch07/7.4/mindspore_data_order.jpeg)
:width:`800px`
:label:`mindspore_data_order_implementation`

View File

@@ -0,0 +1,97 @@
## 单机数据处理性能的扩展
上文我们介绍了通过并行架构发挥多核CPU算力来加速数据预处理以满足芯片上模型计算对于数据消费的吞吐率需求这在大部分情况下都能解决用户的问题。然而数据消费性能随着AI芯片的发展在逐年快速增长即模型计算速率在变快而主要借助CPU算力的数据模块却由于摩尔定律的逐渐终结无法享受到芯片性能提升带来的硬件红利使得数据生产的性能很难像模型计算性能一样逐年突破。不仅如此近几年AI服务器上AI芯片数量的增长速度远超CPU数量的增长速度进一步加剧了芯片的数据消费需求与数据模块的数据生产性能之间的矛盾。我们以英伟达(NVIDIA)公司生产的NVIDIA DGX系列服务器为例子DGX-1服务器中配置有40个CPU核和8个GPU芯片而到了下一代的NVIDIA DGX-2服务器时GPU芯片的数目增长了到了16个而CPU核的数目仅从40个增加到了48个。由于所有的GPU芯片在训练时共享CPU的算力故平均而言每个GPU芯片(数据消费者)能够使用的算力从NVIDIA DGX-1时的5CPU核/GPU下降到了 NVIDIA DGX-2的3CPU核/GPUCPU的算力瓶颈会导致用户使用多卡训练时无法达到预期的扩展性能。针对单机上的CPU算力不足的问题我们给出两种目前常见的两种解决方案即基于CPU+AI芯片的异构数据处理的加速方案和基于分布式数据预处理的扩展方案。
### 基于异构计算的数据预处理
由于AI芯片相比于CPU拥有更丰富的算力资源故在CPU算力成为数据预处理瓶颈时通过借助AI加速芯片来做数据预处理是一个行之有效的方案。虽然AI芯片不具备通用的数据预处理能力但是由于大部分高耗时的数据预处理都是Tensor相关的计算如语音中的快速傅立叶变换(Fast Fourier Transform, FFT)图像中的去噪等使得部分操作可以被卸载到AI芯片上来加速。如华为昇腾Ascend310芯片上的Dvpp模块为芯片内置的硬件解码器相较于CPU拥有对图形处理更强劲的性能Dvpp支持JPEG图片的解码缩放等图像处理基础操作用户实际数据预处理中可以指定部分图像处理在昇腾Ascend310芯片上完成以提升数据模块性能。
```python
namespace ms = mindspore;
namespace ds = mindspore::dataset;
// 初始化操作
//...
// 构建数据处理算子
// 1. 解码
std::shared_ptr<ds::TensorTransform> decode(new ds::vision::Decode());
// 2. 缩放
std::shared_ptr<ds::TensorTransform> resize(new ds::vision::Resize({256}));
// 3. 归一化
std::shared_ptr<ds::TensorTransform> normalize(new ds::vision::Normalize(
{0.485 * 255, 0.456 * 255, 0.406 * 255}, {0.229 * 255, 0.224 * 255, 0.225 * 255}));
// 4. 剪裁
std::shared_ptr<ds::TensorTransform> center_crop(new ds::vision::CenterCrop({224, 224}));
// 构建流水并指定使用昇腾Ascend进行计算
ds::Execute preprocessor({decode, resize, center_crop, normalize}, MapTargetDevice::kAscend310, 0);
// 执行数据处理流水
ret = preprocessor(image, &image);
```
相比较Dvpp只支持图像的部分预处理操作英伟达公司研发的DALI\[8\]是一个更加通用的基于GPU的数据预处理加速框架。DALI中包含如下三个核心概念
- DataNode:表示一组Tensor的集合
- Operator:对DataNode进行变换处理的算子一个Operator的输入和输出均为DataNode。比较特殊的是DALI中的算子可以被设置为包括cpugpumixed三种不同执行模式其中cpu模式下算子的输入输出均为cpu上的DataNodegpu模式下算子的输入输出均为gpu上的DataNode而mixed模式下的算子的输入为cpu的DataNode而输出为gpu的DataNode。
- Pipeline:用户通过Operator描述DataNode的处理变换过程而构建的数据处理流水
实际使用中用户通过设置算子的运行模式(mode)来配置算子的计算是用CPU还是GPU完成计算同时DALI中有如下限制当一个算子为mixed模式或者gpu模式时其所有的下游算子强制要求必须为gpu模式执行。
![NVIDIA DALI概览](../img/ch07/7.5/dali_overview.png)
:width:`800px`
:label:`dali_overview`
下面展示一段使用DALI构建数据处理流水线的示例代码我们从文件中读取图片数据经过混合模式的解码再经过运算在GPU上的旋转和缩放算子处理后返回给用户处理
结果。由于其展示出的优异性能,
DALI被广泛的用于高性能推理服务和多卡训练性能的优化上。
```python
import nvidia.dali as dali
pipe = dali.pipeline.Pipeline(batch_size = 3, num_threads = 2, device_id = 0)
with pipe:
files, labels = dali.fn.readers.file(file_root = "./my_file_root")
images = dali.fn.decoders.image(files, device = "mixed")
images = dali.fn.rotate(images, angle = dali.fn.random.uniform(range=(-45,45)))
images = dali.fn.resize(images, resize_x = 300, resize_y = 300)
pipe.set_outputs(images, labels)
pipe.build()
outputs = pipe.run()
```
### 基于分布式的数据预处理
分布式数据预处理是另一种解决CPU算力性能不足的可选方案。一种常见的做法是借助Spark、Dask等现有大数据计算框架进行数据预处理并将结果写入分布式文件系统而训练的机器只需要读取预处理的结果数据并进行训练即可。
![基于第三方分布式计算框架的分布式数据预处理](../img/ch07/7.5/distribute.png)
:width:`800px`
:label:`distributed_data_preprocess_based_on_3rd_party_software`
该方案虽然再业内被广泛使用,却面临着三个问题:
- 由于数据处理和数据训练采用不同的框架,使得用户为此常常需要在两个不同的框架中编写不同语言的程序,增加了用户的使用负担。
- 由于数据处理系统和机器学习两个系统间无法做零拷贝的数据共享,使得数据的序列化和反序列化常常成为不可忽视的额外开销。
- 由于大数据计算框架并不是完全针对机器学习场景,使得某些分布式预处理操作如全局的数据混洗无法被高效的实现。
为了更适配机器学习场景的数据预处理分布式机器学习框架Ray借助其自身的任务调度能力实现了简单的分布式的数据预处理------
Ray Dataset\[10\],由于数据预处理和训练处在同一个框架内,在降低了用户的编程负担的同时也通过数据的零拷贝共享消除了序列化/反序列化带来的额外开销。Ray Dataset支持如map、batch、map、filter等简单并行数据集变换算子、以及如mean等一些基础的聚合操作算子。同时Ray
Dataset也支持排序、随机打乱、GroupBy等全局混洗操作该方案目前处在研究开发中还未被广泛的采用感兴趣的读者可以翻阅相关资料进一步的了解。
```python
ray.data.read_parquet("foo.parquet") \
.filter(lambda x: x < 0) \
.map(lambda x: x**2) \
.random_shuffle() \
.write_parquet("bar.parquet")
```

View File

@@ -1,10 +1,31 @@
# 数据处理框架
在前两个章节中,我们介绍了编译器前后端的相关内容,详细地阐述了源程序到目标程序的转换优化过程。除了让芯片在训练/推理过程中高性能地运行我们还需要将数据高效地发送给芯片以实现全流程的性能最优。机器学习模型训练和推理需要从存储设备如本地磁盘和内存、远端的存储系统等中加载数据集对数据集进行一系列处理变换将处理结果发送到到GPU或者华为昇腾Ascend等加速器中完成模型计算该流程的任何一个步骤出现性能问题都会对训练和推理的吞吐率造成负面影响。本章我们将核心介绍如何设计、并实现一个面向机器学习场景的数据系统以帮助用户轻松构建各种复杂的数据处理流水线(Data
Pipeline),同时我们的数据系统要有足够高的执行性能,以确保数据预处理步骤不会成为模型训练和推理的性能瓶颈。
本章主要从易用性、高效性和保序性三个维度展开介绍机器学习系统中的数据模块。在前两个小节中我们首先讨论如何构建一个易用的数据模块。包括如何设计编程抽象使得用户通过短短几行代码便可以描述一个复杂的预处理过程以及如何做到既内置丰富算子提升易用性又可以灵活支持用户使用自定义算子覆盖长尾需求。用户构建好数据处理流程后数据模块需要负责高效的调度执行数据流水线以达到最优的数据处理吞吐率。高效的执行数据流水线是一个具有挑战性的任务我们既要面临数据读取部分的I/O性能问题又要解决数据处理部分的计算性能问题。针对上述挑战我们将分别介绍面向高吞吐率读取性能的数据文件格式设计以及能够充分发挥多核CPU算力的并行架构设计。不仅如此和常规数据并行计算任务不同的是大部分机器学习场景对于数据的输入输出顺序有着特殊的`保序性`的要求,我们将会使用一节的内容来介绍什么是保序性,以及如何在数据模块的并行架构中设计相应组件计来满足该特性需求。学习了上述的内容后,读者将会对如何构建一个面向机器学习场景高效易用的数据模块有深刻的理解。最后,作为拓展内容,我们将以目前学术界和业界的一些实践经验来介绍当单机处理性能达不到要求时,该如何去扩展我们的数据处理模块以满足训练性能需求。本章学习目标包括:
- 了解机器学习数据模块架构中的关键组件及其功能
- 了解不同数据模块用户编程接口的设计
- 掌握面向高性能数据读取的数据文件格式设计
- 掌握机器学习系统数据模块并行架构
- 掌握机器学习系统数据模块数据保序性含义及其解决方案
- 了解两种单机数据处理性能扩展方案
```toc
:maxdepth: 2
:numbered:
requirements
program_model
performance
data_order
extension
summary
reference
```

View File

@@ -0,0 +1,183 @@
## 高效性设计
在上一节中我们重点介绍了数据模块的编程抽象以及编程接口设计确保用户可以方便的基于我们提供的API描述数据处理流程而不需要过多关注实现和执行细节。那么本节我们将进一步探究数据加载以及流水线调度执行等数据模块关键部分设计细节以确保用户能够拥有最优的数据处理性能。同时在本节内容中我们也会贯穿现有主要机器学习系统的实践经验以帮助读者加深对这些关键设计方案的理解。
如 :numref:`async_data_process` 所示深度学习模型训练需要借助数据模块首先从存储设备中加载数据集在内存中进行一系列的预处理变换最终将处理好的数据集发送到加速器芯片上执行模型的计算目前有大量的工作都着重于研究如何通过设计新的硬件或者应用算子编译等技术加速芯片上的模型计算而在数据梳理流水的性能问题上鲜有涉及。但事实上很多情况下数据预处理的执行时间往往在整个训练任务中占据着相当大的比例导致GPU/华为昇腾Ascend等加速器无法被充分利用。研究数据表明企业内数据中心的计算任务大约有30%的计算时间花费在数据预处理步骤\[5\]也有研究发现在一些公开数据集上的模型训练任务有65%的时间都花费在了数据预处理上\[6\],由此可以看出数据模块的性能对于整体训练吞吐率有着决定性的影响。
![数据加载、预处理、模型计算异步并行执行](../img/ch07/7.3/async_data_process.png)
:width:`800px`
:label:`async_data_process`
为了追求最高的训练吞吐率现有系统一般选择将数据读取、数据预处理计算、以及芯片上的模型计算三个步骤异步并行执行。这三步构成了典型的数据生产者和数据消费者的上下游关系我们将数据从存储设备中的读取速率用F表示数据预处理速率用P表示芯片上的数据消费速率用G表示。理想情况下我们希望G < min(F, P)此时加速芯片不会因为等待数据而阻塞。然而现实情况下我们常常要么因为数据加载速率F过低(称为I/O Bound)要么因为数据预处理速率P过低(称为CPU Bound)导致G>min(F, P)而使得芯片无法被充分利用。针对上述关键性能问题,我们将在本节重点探究两个内容:
- 如何针对机器学习场景的特定I/O需求来设计相应文件格式及加载方式以优化数据读取速率F。
- 如何设计并行架构来充分发挥现代多核CPU的计算能力以提升数据处理速率P。
在本节的最后我们还会研究一个具有挑战性的问题,即如何利用我们在前几章学到的计算图的编译技术来优化用户的数据处理计算流图,以进一步达到最优的数据处理吞吐率性能。那么接下来,请读者和我们一起开启本节的头脑风暴旅程。
### 数据读取的高效性
首先我们来研究如何解决数据读取的性能挑战。我们面临的第一个问题是数据类型繁多存储格式不统一带来的I/O差异如文本数据可能存储成txt数据格式图像数据可能存储成原始格式或者如JPEG等压缩格式。我们显然无法去针对每一种存储情况都设计其最优的数据读取方案。但是我们可以通过提出一种统一的存储格式(我们称之为Unirecord格式)以屏蔽不同数据类型的I/O差异并基于这种数据格式进行数据加载方案的设计与优化而实际使用中用户只需要将其原始数据集转换存储为我们的统一数据格式便可以享受到高效的读取效率。
![统一数据格式](../img/ch07/7.3/uni_record.png)
:width:`800px`
:label:`unified_record_format`
那么我们的Unirecord除了统一用户存储格式之外还需要具备哪些特性呢机器学习模型训练中对数据的访问具有如下特点
- 每一个Epoch内以一种随机顺序遍历所有的数据且每个数据只被遍历一次
- 所有Epoch需要以不同的随机顺序遍历访问所有数据
上述的访问特性要求我们的Unirecord存储格式能够支持高效的随机读取。当我们的数据集能够全部存储在RAM中时对Unirecord的随机读取并不会成为大的问题。但是当数据集大到必须存储在本地磁盘或者分布式文件系统中时我们就需要设计特定的方案。一个直观的想法是将一个Unirecord文件分为索引块和数据块索引块中记录每个数据在文件中的大小、偏移以及一些校验值等元信息数据块存储每个数据的主体数据。当我们需要对一个Unirecord格式的文件进行随机读取时我们首先在内存中加载该文件的索引块(通常远远小于整个文件大小)并在内存中建立文件内数据的索引表接着当我们需要随机读取数据时我们首先在索引表中查询该数据在文件中的偏移、大小等信息并基于该信息从磁盘上进行读取。这样的读取方式可以满足我们在磁盘上的随机读取需求。接下来我们以MindSpore提出的MindRecord的实践经验为例子介绍统一文件格式的设计以帮助大家加深对这部分内容的理解
![支持随机读取的文件格式设计](../img/ch07/7.3/file_indexing.png)
:width:`800px`
:label:`file_random_access`
#### MindRecord介绍
MindRecord是MindSpore推出的统一数据格式目标是归一化用户的数据集优化训练数据的读取过程。该文件格式具备如下特征
- 实现多变的用户数据统一存储、访问,训练数据读取更加简便。
- 数据聚合存储,高效读取,且方便管理、移动。
- 高效的数据编解码操作,对用户透明、无感知。
- 可以灵活控制分区的大小,实现分布式训练。
和我们前文设计的Unirecord思路相似一个MindRecord文件也由数据文件和索引文件组成数据文件包含文件头、标量数据页、块数据页用于存储用户归一化后的训练数据索引文件包含基于标量数据如图像Label、图像文件名等生成的索引信息用于方便的检索、统计数据集信息。为确保对一个MindRecord文件的随机读取性能MindSpore建议单个MindRecord文件小于20G若数据集超过20G用户可在MindRecord数据集生成时指定相应参数将原始数据集分片存储为多个MindRecord文件。
![MindRecord文件格式组成](../img/ch07/7.3/MindRecord_format.png)
:width:`800px`
:label:`mindrecord_format`
一个MindRecord文件中的数据文件部分具体的关键部分的详细信息如下
- **文件头**
文件头主要用来存储文件头大小、标量数据页大小、块数据页大小、Schema信息、索引字段、统计信息、文件分区信息、标量数据与块数据对应关系等是MindRecord文件的元信息。
- **标量数据页**
标量数据页主要用来存储整型、字符串、浮点型数据如图像的Label、图像的文件名、图像的长宽等信息即适合用标量来存储的信息会保存在这里。
- **块数据页**
块数据页主要用来存储二进制串、Numpy数组等数据如二进制图像文件本身、文本转换成的字典等。
用户训练时MindRecord的读取器能基于索引文件快速的定位找到数据所在的位置并将其读取解码出来。另外MindRecord具备一定的检索能力用户可以通过指定查询条件筛选获取符合期望的数据样本。
对于分布式训练场景MindRecord会基于数据文件中Header及索引文件进行元数据的加载得到所有样本的ID及样本在数据文件中的偏移信息然后根据用户输入的num_shards训练节点数和shard_id当前节点号进行数据的partition得到当前节点的num_shards分之一的数据分布式训练时多个节点只读取数据集的num_shards分之一借由计算侧的AllReduce实现整个数据集训练的效果。进一步如果用户开启shuffle操作那么每epoch保证所有节点shuffle seed保持一致那么对所有样本的ID shuffle结果是一致的那么数据partition的结果就是正确的。
![MindRecord Partition策略](../img/ch07/7.3/partition.png)
:width:`800px`
:label:`mindrecord_partition`
### 数据计算的高效性
解决了数据读取性能问题后,我们继续来研究数据计算的性能提升(即最大化上文中的数据处理速率P)。我们以上文提及的数据预处理流水为例子、来研究如何设计数据模块对用户计算图的调度执行以达到最优的性能。
![数据预处理流程串行顺序执行示意图](../img/ch07/7.3/single_pipeline.png)
:width:`800px`
:label:`serialized_data_process`
由于深度学习芯片如GPU/华为昇腾Ascend等并不具备通用数据处理的能力
我们目前还是主要依赖CPU来完成预处理计算。主流的AI服务器大多具备多个多核CPU数据模块需要设计合理的并行架构充分发挥多核算力以提升数据预处理性能达到尽可能减少加速器由于等待数据而阻塞的目的。本节中我们将介绍流水线粒度并行以及算子粒度并行两种常见的并行架构。流水线并行的方式结构清晰易于理解和实现主要被Pytorch这样基于Python实现数据模块的机器学习系统所采用。受到经典数据并行系统调度执行架构设计的影响其他如Google的TensorFlow以及华为的MindSpore等系统主要采用算子粒度并行做精细CPU算力分配以达到充分利用多核算力的目的。然而精细的分配意味着我们需要对所有数据处理流程中涉及的算子设置合理的并行参数这对用户而言是一个较大的挑战。于是MindSpore等框架又提供数据流图中关键参数自动调优的功能通过运行时的动态分析自动搜索得到最优的算子并行度等参数极大的减少了用户的编程负担。接下来我们一一展开讨论。
#### 流水线并行
第一种常见的并行方案为流水线粒度的并行,即我们把用户构建的计算流水在一个线程/进程内顺序串行执行,同时启动多个线程/进程并行执行多个流水线。假设用户总共需要处理N个数据样本那么当流水线并行度为M时每个进程/线程只需要执行处理(N/M)个样本。流水线并行架构结构简单,易于实现。整个并行架构中各个执行进程/线程只需要在数据执行的开始和结束进行跨进程/线程的通信即可,数据模块将待处理的数据任务分配给各个流水线进程/线程并在最终进行结果汇总发送到芯片上进行模型计算。从用户的角度而言使用也相对方便只需要指定关键的并行度参数即可。接下来我们以Pytorch为例子进行详细展开。
![流水线级别并行执行示意图](../img/ch07/7.3/pipeline_parallisim.png)
:width:`800px`
:label:`pipeline_parallisim`
在Pytorch中用户只需要实现一个Dataset的Python类编写数据处理过程Dataloader通过用户指定的并行度参数num_workers来启动相应个数的Python进程调用用户自定义的Dataset类进行数据预处理。Dataloader中的进程有两类角色worker进程以及主进程以及两类进程间通信队列index_queue以及worker_result_queue。训练过程中主进程负责将待处理数据任务列表通过index_queue发送给各个worker进程每个woker进程执行用户编写的Dataset类的数据预处理逻辑并将处理后的结果通过worker_result_queue返回给主进程。
![Pytorch Dataloader并行执行架构](../img/ch07/7.3/pytorch_dataloader.png)
:width:`800px`
:label:`pytorch_dataloader`
接下来我们展示一段用户使用Pytorch的Dataloader进行并行数据预处理的代码片段可以发现我们只需要实现Dataset类描述数据预处理逻辑并指定num_workers即可实现流水线粒度的并行数据预处理。
```python
# 描述数据预处理流程
class TensorDataset:
def __init__(self, inps):
sef.inps = inps
def __getitem__(self, idx):
data = self.inps[idx]
data = data + 1
return data
def __len__(self):
return self.inps.shape[0]
inps = torch.arange(10 * 5, dtype=torch.float32).view(10, 5)
dataset = TensorDataset(inps)
# 指定并行度为3
loader = DataLoader(dataset, batch_size=2, num_workers=3)
for batch_idx, sample in enumerate(loader):
print(sample)
```
最后需要指出的是, Pytorch Dataloader的执行过程中涉及大量进程间通信虽然为了加速这一步骤Pytorch对Tensor类数据实现了基于共享内存的进程间通信机制。然而当通信数据量较大时跨进程通信仍然会较大地影响端到端的数据预处理吞吐率性能。当然这不是流水线并行自身的架构问题而是由于CPython的全局解释器锁(Global Interpreter Lock, GIL)导致在Python层面实现流水线并行时只能采用进程并行。为了解决这个问题目前Pytorch团队也在尝试通过移除CPython中的GIL来达到基于多线程实现流水线并行以提升通信效率的目的\[7\],感兴趣的读者可以选择继续深入了解。
#### 算子并行
流水线并行中算力(CPU核心)的分配以流水线为粒度相对而言以算子为计算资源分配粒度的算子并行是一种追求更精细算力分配的并行方案。我们期望对计算耗时高的算子分配更高的并行度计算耗时低的算子分配更低的并行度以达到更加高效合理的CPU算力使用。算子并行想法和经典的数据并行计算系统的并行方式一脉相承我们以经典的MapReduce计算执行为例子我们发现这也可以认为是一种算子并行(map算子和reduce算子),其中map算子的并行度和reduce算子的并行度根据各个算子阶段的计算耗时而决定。
![MapReduce经典并行执行架构](../img/ch07/7.3/map_reduce.png)
:width:`800px`
:label:`mapreduce`
下图中我们给出本节开头数据预处理流程的算子并行架构示意图我们根据各个算子的计算耗时设置图片解码算子并行度为3图片缩放并行度为2图片随机旋转算子并行度为4图片归一化算子并行度为3以及图像通道转置算子并行度为1。我们期望通过给不同耗时的算子精准的分配算力以达到算力高效充分的利用。具体实现中算子并行一般采用线程级并行所有的算子使用线程间队列等方法进行共享内存通信。
![算子并行执行架构](../img/ch07/7.3/operator_parallisim.png)
:width:`800px`
:label:`operator_parallisim`
现有机器学习系统的数据模块中tf.data以及MindData均采用了算子并行的方案。由于对算力的利用更加充分、以及基于C++的高效数据流调度实现算子并行的方案往往展示出更好的性能tf.data的性能评测表明其相比较Pytorch的Dataloader有近两倍的性能优势\[5\]。
接下来我们以一段基于MindSpore实现本节开篇的数据预处理流程来展示如何在一个算子并行的数据流水线中设置各个算子的并行度。
```python
import mindspore.dataset as ds
import mindspore.dataset.transforms.c_transforms as c_transforms
import mindspore.dataset.transforms.vision.c_transforms as vision
# 读取数据
dataset_dir = "path/to/imagefolder_directory"
dataset = ds.ImageFolderDatasetV2(dataset_dir, num_parallel_workers=8)
transforms_list = [vision.Decode(),
vision.Resize((256, 256)),
vision.RandomRotation((0, 15)),
vision.Normalize((100, 115.0, 121.0), (71.0, 68.0, 70.0)),
vision.HWC2CHW()]
onehot_op = c_transforms.OneHot(num_classes)
# 解码算子并行度为3
dataset = dataset.map(input_columns="image", operations=vision.Decode(), num_parallel_workers=3)
# 缩放算子并行度为2
dataset = dataset.map(input_columns="image", operations=vision.Resize((256, 256)), num_parallel_workers=2)
# 随机旋转算子并行度为4
dataset = dataset.map(input_columns="image", operations=vision.RandomRotation((0, 15)), num_parallel_workers=4)
# 正规化算子并行度为3
dataset = dataset.map(input_columns="image", operations=vision.Normalize((100, 115.0, 121.0), (71.0, 68.0, 70.0)), num_parallel_workers=3)
# 通道转置算子并行度为1
dataset = dataset.map(input_columns="image", operations=vision.HWC2CHW(), num_parallel_workers=1)
dataset = dataset.map(input_columns="label", operations=onehot_op)
```
我们发现虽然算子并行具备更高的性能潜力但却需要我们对每一个算子设置合理并行参数。这不仅对用户提出了较高的要求同时也增加了由于不合理的并行参数设置导致性能反而下降的风险。为了让用户更加轻松的使用算子并行tf.data和MindData都增加了流水线关键参数动态调优功能基于对流水线执行时的性能监控计算得到合理的参数以尽可能达到最优的数据预处理吞吐率\[5\]。
#### 数据处理计算图优化
在前文中,我们专注于通过并行架构来高效执行用户构建的数据预处理计算图。但我们可以思考如下问题:用户给定的计算图是否是一个高效的计算图?
如果不高效我们是否能够在保证等价变换的前提下将用户的数据计算图进行优化重写得到执行性能预期更好的计算图没错这和我们在前几章中学习的模型计算图编译优化有着相同的思想即通过分析变换计算图IR得到更优的IR表示来达到更好的执行性能。常用的数据图优化策略有算子融合以及map操作向量化两种。算子融合将map+map、map+batch、map+filter、filter+filter等算子组合融合成一个等价复合算子将原先需要在两个线程组中执行的计算融合为在一个线程组中执行的复合计算减少线程间的同步通信开销达到了更优的性能。而map操作向量化则将常见的dataset.map(f).batch(b)操作组合变换调整为dataset.batch(b).map(parallel_for(f))借助现代CPU的对并行操作更友好的SIMD指令集来加速数据预处理。

View File

@@ -0,0 +1,122 @@
## 易用性设计
本节我们主要介绍如何设计一个易用的机器学习系统数据模块。正如前文所言,易用性既要求数据模块提供好的编程抽象和接口使得用户可以方便的构建一个数据处理流水,同时还要支持用户灵活地在数据流水中注册使用自定义算子以满足丰富多变的特殊需求,接下来我们将从编程接口抽象和自定义算子注册机制两个方面来展开探讨。
### 编程抽象与接口
:numref:`image_process_pipeline` 我们展示的是一个训练图片分类模型的经典数据预处理流水线。我们从存储设备中加载数据集后对数据集中的图片数据进行解码、缩放、旋转、正规化、通道变换等一系列操作对数据集的标签也进行特定的预处理操作最终将处理好的数据发送到芯片上进行模型的计算。我们希望数据模块提供的编程抽象具备足够高的层次以使得用户可以通过短短几行代码就能描述清楚数据处理的逻辑不需要陷入过度的、重复的数据处理实现细节当中。同时又要确保这一套高层次的抽象具备足够通用性以满足多样的数据预处理需求。在我们得到一个好的编程抽象后我们将会以基于MindSpore的数据模块提供的编程接口实现下图所描述的数据预处理流水线的代码片段为例子来展示一个优秀的编程抽象对用户编程负担的减轻是有多么大的作用。
![数据预处理示例](../img/ch07/7.2/image_process_pipeline.png)
:width:`800px`
:label:`image_process_pipeline`
事实上,面向数据计算的编程抽象早已在通用数据并行计算系统领域中被广泛的研究并取得了相对统一的共识------那就是提供类LINQ式\[1\]的编程抽象其最大的特点是让用户专注于描述基于数据集的生成与变换而将这些操作的高效实现与调度执行交由数据系统的运行时负责。一些优秀的系统如Naiad\[2\],
Spark\[3\], DryadLINQ\[4\]等都采用了这种编程模型。我们以Spark为例子进行简要介绍。
Spark向用户提供了基于弹性分布式数据集(Resilient Distributed Dataset, RDD)概念的编程模型。一个RDD是一个只读的分布式数据集合用户通过Spark的编程接口来主要描述RDD的创建及变换过程我们以一个Spark示例进行展开讨论。下面展示了一段在一个日志文件中统计包含ERROR字段的行数的Spark代码我们首先通过文件读取创建一个分布式的数据集file前文提到RDD表示数据的集合这里的file实际上是日志行的数据集合
我们对这个file数据集进行filter(过滤)运算得到新的只保留包含ERROR字段的日志行的数据集errs接着我们对errs中的每一个数据进行map(映射)操作得到数据集ones最后我们对ones数据集进行reduce操作得到了我们最终想要的统计结果即file数据集中包含ERROR字段的日志行数。
```java
val file = spark.textFile("hdfs://...")
val errs = file.filter(_.contains("ERROR"))
val ones = errs.map(_ => 1)
val count = ones.reduce(_+_)
```
我们发现用户只需要四行代码就完成了在这样一个分布式的数据集中统计特定字段行数的复杂任务这得益于Spark核心的RDD编程抽象从 :numref:`rdd_transformation_example`的计算流程可视化中我们也可以清晰的看到用户在创建数据集后,只需要描述在数据集上的作用算子即可,至于算子的执行和实现则由系统的运行时负责。
![Spark编程核心------RDD变换](../img//ch07/7.2/RDD.png)
:width:`800px`
:label:`rdd_transformation_example`
主流机器学习系统中的数据模块同样也采用了类似的编程抽象如TensorFlow的数据模块tf.data\[5\],
以及MindSpore的数据模块MindData等。接下来我们以MindData的接口设计为例子来介绍如何面向机器学习这个场景设计好的编程抽象来帮助用户方便的构建模型训练中多种多样的数据处理流水线。
MindData是机器学习系统MindSpore的数据模块主要负责完成机器学习模型训练中的数据预处理任务MindData的向用户提供的核心编程抽象为基于Dataset数据集的变换处理。这里的Dataset是一个数据帧的概念(Data
Frame)即一个Dataset为一个多行多列且每一列都有列名的关系数据表。
![MindSpore
Dataset示例](../img/ch07/7.2/dataset_table.png)
:width:`800px`
:label:`mindspore dataset example`
基于这样一个编程模型结合我们在第一节中介绍的机器学习数据流程中的关键处理流程MindData为用户提供了对数据集进行shuffle、map、batch等变换操作的数据集操作算子这些算子接收一个Dataset作为输入并以一个新处理生成的Dataset作为结果输出我们列举典型的数据集变换接口如下
:MindSpore支持的数据集操作接口
| 数据集操作 | 含义解释 |
| -------------------- | -------------------------------------------------------- |
| batch | 将数据集中的多行数据项组成一个mini-batch |
| map | 对数据集中的每行数据进行变换操作 |
| shuffle | 随机打乱数据集中的数据行的顺序 |
| filter | 对数据集的数据行进行过滤操作,只保留通过过滤条件的数据行 |
| prefetch | 从存储介质中预取数据集 |
| project | 从Dataset数据表中选择一些列用于接下来的处理 |
| zip | 将多个数据集合并为一个数据集 |
| repeat | 多轮次训练中,重复整个数据流水多次。 |
| create_dict_iterator | 对数据集创建一个返回字典类型数据的迭代器。 |
| ... | ... |
上述描述了数据集的接口抽象而对数据集的具体操作实际上是由具体的数据算子函数定义。为了方便用户使用MindData对机器学习领域常见的数据类型及其常见数据处理需求都内置实现了丰富的数据算子库。针对视觉领域MindData提供了常见的如Decode(解码)、Resize缩放、RandomRotation随机旋转、Normalize(正规化)以及HWC2CHW通道转置等算子针对文本领域MindData提供了Ngram、NormalizeUTF8、BertTokenizer等算子针对语音领域MindData提供了TimeMasking时域掩盖、LowpassBiquad双二阶滤波器、ComplexNorm归一化等算子这些常用算子能覆盖用户的绝大部分需求。
除了支持灵活的Dataset变换针对数据集种类繁多、格式与组织各异的难题MindData还提供了灵活的Dataset创建主要分为如下三类
- 通过内置数据集直接创建MindData内置丰富的经典数据集如CelebADataset、Cifar10Dataset、CocoDataset、ImageFolderDataset、MnistDataset、VOCDataset等。如果用户需要使用这些常用数据集可通过一行代码即可实现数据集的开箱使用。同时MindData对这些数据集的加载进行了高效的实现以确保用户能够享受到最好的读取性能。
- 从MindRecord中加载创建MindRecord为MindData设计的一种高性能通用数据存储文件格式用户可将数据集转换为MindRecord后借助MindSpore的相关API进行高效的读取。
- 从Python类创建如果用户已经有自己数据集的Python读取类那么可以通过MindData的GeneratorDataset接口调用该Python类实现Dataset的创建这给用户提供了极大的自由度。
![MindSpore
Dataset多种生成方式](../img/ch07/7.2/dataset.png)
最后我们以一个基于MindData实现我们本节开篇所描述的数据处理流水线为例子来展示以Dataset为核心概念的数据编程抽象是多么的用户友好。我们只需要短短10余行代码即可完成我们所期望的复杂数据处理同时在整个过程中我们只专注于逻辑的描述而将算子的实现和算子执行流程交由数据模块负责这极大的减轻了用户的编程负担。
```python
import mindspore.dataset as ds
import mindspore.dataset.transforms.c_transforms as c_transforms
import mindspore.dataset.transforms.vision.c_transforms as vision
dataset_dir = "path/to/imagefolder_directory"
# create a dataset that reads all files in dataset_dir with 8 threads
dataset = ds.ImageFolderDatasetV2(dataset_dir, num_parallel_workers=8)
#create a list of transformations to be applied to the image data
transforms_list = [vision.Decode(),
vision.Resize((256, 256)),
vision.RandomRotation((0, 15)),
vision.Normalize((100, 115.0, 121.0), (71.0, 68.0, 70.0)),
vision.HWC2CHW()]
onehot_op = c_transforms.OneHot(num_classes)
# apply the transform to the dataset through dataset.map()
dataset = dataset.map(input_columns="image", operations=transforms_list)
dataset = dataset.map(input_columns="label", operations=onehot_op)
```
### 自定义算子支持
有了基于数据集变换的编程抽象、以及针对机器学习各种数据类型的丰富变换算子支持,我们可以覆盖用户绝大部分的数据处理需求。然而由于机器学习领域本身进展快速,新的数据处理需求不断涌现,可能会有用户想要使用的数据变换算子没有被数据模块覆盖支持到的情况发生。为此我们需要设计良好的用户自定义算子注册机制,使得用户可以方便在构建数据处理流水线时使用自定义的算子。
机器学习场景中用户的开发编程语言以Python为主所以我们可以认为用户的自定义算子更多情况下实际上是一个Python函数或者Python类。数据模块支持自定义算子的难度主要由数据模块对计算的调度实现方式有关系比如Pytorch的dataloader的计算调度主要在Python层面实现得益于Python语言的灵活性在dataloader的数据流水中插入自定义的算子相对来说比较容易而像TensorFlow的tf.data以及MindSpore的MindData的计算调度主要在C++层面实现这使得数据模块想要灵活的在数据流中插入用户定义的Python算子变得较为有挑战性。接下来我们以MindData中的算子自定义算子注册使用实现为例子展开讨论这部分内容。
![MindData的C层算子和Python层算子](../img/ch07/7.2/operation.png)
:width:`800px`
:label:`mindspore operator example`
MindData中的数据预处理算子可以分为C层算子以及Python层算子C层算子能提供较高的执行性能而Python层算子可以很方便借助丰富的第三方Python包进行开发。为了灵活地覆盖更多场景MindData支持用户使用Python开发自定义算子如果用户追求更高的性能MindData也支持用户将开发的C层算子编译后以插件的形式注册到MindSpore的数据处理中进行调用。
对于用户传入map、filter等数据集变换算子中的自定义数据处理算子MindData的Pipeline启动后会通过创建的Python运行时来执行。需要指出的是自定义的Python算子需要保证需要保一个或多个输入、输出均是numpy.ndarray类型。具体执行过程中当MindData的Pipeline的数据集变换中执行用户自定义的PyFunc算子时会将输入数据以numpy.ndarray的类型传递给用户的PyFunc自定义算子执行完毕后再以numpy.ndarray返回给MindData在此期间正在执行的数据集变换算子如map、filter等负责该PyFunc的运行时生命周期及异常判断。如果用户追求更高的性能MindData也支持用户自定义C算子。dataset-plugin仓插件仓\[10\]为MindData的算子插件仓囊括了为特定领域遥感医疗气象等量身制作的算子该仓承载MindData的插件能力扩展为用户编写MindData的新算子提供了便捷易用的入口用户通过编写算子、编译、安装插件步骤然后就可以在MindData
Pipeline的map操作中使用新开发的算子。
![MindSpore自定义算子注册](../img/ch07/7.2/dataset-plugin.png)
:width:`800px`
:label:`mindspore_user_defined_operator`

View File

@@ -0,0 +1,34 @@
## 引用
\[1\] Meijer, E., Beckman, B., & Bierman, G. (2006, June). Linq:
reconciling object, relations and xml in the. net framework. In
Proceedings of the 2006 ACM SIGMOD international conference on
Management of data (pp. 706-706).
\[2\] Murray, D. G., McSherry, F., Isaacs, R., Isard, M., Barham, P., &
Abadi, M. (2013, November). Naiad: a timely dataflow system. In
Proceedings of the Twenty-Fourth ACM Symposium on Operating Systems
Principles (pp. 439-455).
\[3\] Zaharia, M., Chowdhury, M., Franklin, M. J., Shenker, S., & Stoica,
I. (2010). Spark: Cluster computing with working sets. HotCloud,
10(10-10), 95.
\[4\] Fetterly, Y. Y. M. I. D., Budiu, M., Erlingsson, Ú., & Currey, P.
K. G. J. (2009). DryadLINQ: A system for general-purpose distributed
data-parallel computing using a high-level language. Proc. LSDS-IR, 8.
\[5\] Murray, D. G., Simsa, J., Klimovic, A., & Indyk, I. (2021). tf.
data: A Machine Learning Data Processing Framework. arXiv preprint
arXiv:2101.12127.
\[6\] Mohan, J., Phanishayee, A., Raniwala, A., & Chidambaram, V. (2020).
Analyzing and mitigating data stalls in DNN training. arXiv preprint
arXiv:2007.06775.
\[7\] https://docs.google.com/document/d/18CXhDb1ygxg-YXNBJNzfzZsDFosB5e6BfnXLlejd9l0/edit#.
\[8\] https://github.com/NVIDIA/DALI.
\[9\] https://docs.ray.io/en/latest/data/dataset.html.
\[10\] https://gitee.com/mindspore/dataset-plugin.

View File

@@ -0,0 +1,33 @@
## 概述
机器学习场景中的数据处理是一个典型的ETLExtract, Transform,
Load过程第一个阶段Extract需要从存储设备中加载数据集第二个阶段Transform完成对数据集的变换处理。虽然不同的机器学习系统在构建数据模块时采用了不同的技术方案但其核心都会包含数据加载、数据混洗、数据变换、数据mini-batch组装以及数据发送等关键组件。其中每个组件的功能介绍如下所示
- **数据加载组件(Load)**:负责从存储设备中加载读取数据集,需要同时考虑存储设备的多样性(如本地磁盘/内存远端磁盘和内存等和数据集格式的多样性如csv格式txt格式等。根据机器学习任务的特点AI框架也提出了统一的数据存储格式如谷歌TFRecord,
华为MindRecord等以提供更高性能的数据读取。
- **数据混洗组件(Shuffle)**:负责将输入数据的顺序按照用户指定方式随机打乱,以提升模型的鲁棒性。
- **数据变换组件(Map)**:负责完成数据的变换处理,内置面向各种数据类型的常见预处理算子,如图像中的尺寸缩放和翻转,音频中的随机加噪和变调、文本处理中的停词去除和随机遮盖(Mask)等。
- **数据组装组件(Batch)**:负责组装构造一个批次(mini-batch)的数据发送给训练/推理。
- **数据发送组件(Send)**负责将处理后的数据发送到GPU/华为昇腾Ascend等加速器中以进行后续的模型计算和更新。高性能的数据模块往往选择将数据向设备的搬运与加速器中的计算异步执行以提升整个训练的吞吐率。
![数据模块的核心组件](../img/ch07/7.1/pipeline.png)
:width:`800px`
:label:`pipeline`
实现上述的组件只是数据模块的基础,我们还要对如下方面进行重点设计:
#### 易用性
AI模型训练/推理过程中涉及到的数据处理非常灵活一方面不同的应用场景中数据集类型千差万别特点各异在加载数据集时数据模块要支持图像、文本、音频、视频等多种类型的特定存储格式还要支持内存、本地磁盘、分布式文件系统以及对象存储系统等多种存储设备类型模块需要对上述复杂情况下数据加载中的IO差异进行抽象统一减少用户的学习成本。另一方面不同的数据类型往往也有着不同的数据处理需求。现有常见机器学习任务中图像任务常常对图像进行缩放、翻转、模糊化等处理文本任务需要对文本进行切分、向量化等操作而语音任务需要对语音进行快速傅立叶变换、混响增强、变频等预处理。为帮助用户解决绝大部分场景下的数据处理需求数据模块需要支持足够丰富的面向各种类型的数据预处理算子。然而新的算法和数据处理需求在不断快速涌现我们需要支持用户在数据模块中方便的使用自定义处理算子以应对数据模块未覆盖到的场景达到灵活性和高效性的最佳平衡。
#### 高效性
由于GPU/华为昇腾Ascend等常见AI加速器主要面向Tensor数据类型计算并不具备通用的数据处理能力现有主流机器学习系统数据模块通常选择使用CPU进行数据流水线的执行。理想情况下在每个训练迭代步开始之前数据模块都需要将数据准备好、以减少加速器因为等待数据而阻塞的时间消耗。然而数据流水线中的数据加载和数据预处理常常面临着具有挑战性的I/O性能性能和CPU计算性能问题数据模块需要设计具备支持随机读取且具备高读取吞吐率的文件格式来解决数据读取瓶颈问题同时还需要设计合理的并行架构来高效的执行数据流水线以解决计算性能问题。为达到高性能的训练吞吐率主流机器学习系统均采用数据处理与模型计算进行异步执行以掩盖数据预处理的延迟。
#### 保序性
和常规的数据并行计算任务所不同的是,机器学习模型训练对数据输入顺序敏感。使用随机梯度下降算法训练模型时,通常在每一轮需要按照一种伪随机顺序向模型输入数据,并且在多轮训练(Epoch)中每一轮按照不同的随机顺序向模型输入数据。由于模型最终的参数对输入数据的顺序敏感,为了帮助用户更好的调试和确保不同次实验的可复现性,我们需要在系统中设计相应机制使得数据最终送入模型的顺序由数据混洗组件的数据输出顺序唯一确定,不会由于并行数据变换而带来最终数据模块的数据输出顺序不确定。我们将在后文中对于保序性的要求和具体实现细节展开探讨。

View File

@@ -0,0 +1,3 @@
## 章节总结
本章我们围绕着易用性、高效性和保序性三个维度展开研究如何设计实现机器学习系统中的数据预处理模块。在易用性维度我们重点探讨了数据模块的编程模型通过借鉴历史上优秀的并行数据处理系统的设计经验我们认为基于描述数据集变换的编程抽象较为适合作为数据模块的编程模型在具体的系统实现中我们不仅要在上述的编程模型的基础上提供足够多内置算子方便的用户的数据预处理编程同时还要考虑如何支持用户方便的使用自定义算子。在高效性方面我们从数据读取和计算、两个分别介绍了特殊文件格式设计和计算并行架构设计。我们也使用我们在前几章中学习到的模型计算图编译优化技术来优化用户的数据预处理计算图以进一步的达到更高的数据处理吞吐率。机器学习场景中模型对数据输入顺序敏感于是衍生出来保序性这一特殊性质我们在本章中对此进行了分析并通过MindSpore中的Connector的特殊约束实现来展示真实系统实现中如何确保保序性。最后我们也针对部分情况下单机CPU数据预处理性能的问题介绍了当前基于异构处理加速的纵向扩展方案和基于分布式数据预处理的横向扩展方案我们相信读者学习了本章后能够对机器学习系统中的数据模块有深刻的认知也对数据模块未来面临的挑战有所了解。

View File

@@ -1,10 +1,331 @@
# 分布式训练系统
---
bibliography:
- references.bib
---
# 分布式训练系统 {#ch:distributed}
随着机器学习的进一步发展科学家们设计出更大型更多功能的机器学习模型例如说GPT-3。这种模型含有大量参数需要复杂的计算以及处理海量的数据。单个机器上有限的资源无法满足训练大型机器学习模型的需求。因此我们需要设计分布式训练系统从而将一个机器学习模型任务拆分成多个子任务并将子任务分发给多个计算节点解决资源瓶颈。
```toc
:maxdepth: 2
:numbered:
在本章节中我们会引入分布式机器学习系统的相关概念设计挑战系统实现和实例研究。我们会首先讨论分布式训练系统的定义设计动机和好处。进一步我们会讨论常见的分布式训练方法数据并行模型并行和流水线并行。在实际中这些分布式训练方法会被参数服务器Parameter
Servers或者是集合通讯库Collective Communication
Libraries实现。不同的系统实现具有各自的优势和劣势。我们会用大型预训练模型和大型深度学习推荐系统作为实例来探讨不同系统实现的利与弊。
本章的学习目标包括:
```
- 掌握分布式训练相关系统组件的定义,设计动机和好处
- 掌握常见的分布式训练方法:数据并行,模型并行和流水线并行
- 掌握常见的分布式训练框架实现:参数服务器和集合通讯
- 理解常见分布式训练的实例,和采用不同实现方法的利弊。
## 系统概述
### 设计动机
接下来,我们详细讨论分布式训练系统的设计动机
![对比机器学习模型参数量增长和计算硬件的算力增长](figs/ch10/ch10-computation-increase.pdf){#fig:ch010/ch10-computation-increase}
##### 算力不足
单处理器的算力不足是促使人们设计分布式训练系统的一个主要原因。一个处理器的算力可以用**每秒钟浮点数操作**Floating
Point Operations Per SecondFLOPS来衡量。
如图[1.1](#fig:ch010/ch10-computation-increase){reference-type="ref"
reference="fig:ch010/ch10-computation-increase"}所示根据摩尔定律Moore's
Law中央处理器的算力每18个月增长2倍。虽然计算加速卡如GPU和Tensor
Processing
UnitTPU针对机器学习计算如矩阵相乘提供了大量的算力。这些加速卡的发展最终也受限于摩尔定律增长速度也停留在每18个月2倍。而与此同时机器学习模型正在快速发展。短短数年我们从仅能识别有限物体的AlexNet模型[@krizhevsky2012alexnet]一路发展到在复杂任务中打败人类的AlphaStar[@alphastar]。这期间模型对于算力需求每18个月增长了35倍。解决处理器性能和算力需求之间的鸿沟
的关键就在于利用分布式计算。通过大型数据中心和云计算设施,我们可以快速获取大量的处理器。通过分布式训练系统有效管理这些处理器,我们可以实现算力的快速增长,从而持续满足模型的需求。
##### 内存不足
在训练机器学习模型的过程中训练系统需要在内存中存储大量数据。这些数据包括模型参数Parameters以及训练和更新这些参数所产生的中间数据如特征图Feature
Map和梯度Gradients。假设一个深度神经网络模型具有10亿的参数所有特征图共有20亿参数每个参数都由一个32位浮点数表达而更新这些参数至少还需要产生与特征图和参数等量的梯度。由于一个32位浮点数需要4个字节Byte的内存来存储那么训练这个10亿规模的模型就需要至少24GB$24 \times 10^9$
Byte的内存。现在随着大型预训练模型的崛起一个深度神经网络如GPT-3会拥有超过千亿的参数。假设我们依然使用32位浮点数来存储参数激活值和梯度那么训练这个模型就至少需要1.2TB的内存。而如今的训练加速卡如NVIDIA
A100仅能提供最高80GB的内存。单卡内存空间的增长受到硬件规格散热和成本等诸多因素难以进一步快速增长。因此我们需要分布式训练系统来同时使用数百个训练加速卡从而为千亿级别的模型提供所需的TB级别的内存。
### 分布式训练架构
\[概述本章核心系统组件,定义本章的技术用语\]
受限于单节点的有限算力,内存和存储资源,人们把关注投向了日益成熟的云计算数据中心。一个数据中心管理着数十万个计算服务器。随着数据中心的全球部署,人们可以很方便地获得数百个服务器。这些服务器可以通过分布式训练系统来协调和管理,解决训练大型机器学习模型过程遇到的算力,内存和存储不足,从而完成训练过程的加速。
![单节点计算和多节点分布式计算](figs/ch10/ch10-single-vs-multi.pdf){#fig:ch010/ch10-single-vs-multi}
在设计分布式训练系统的过程中,我们需要找出有资源瓶颈的计算任务,根据计算任务的特点,将其拆分成多个子任务,然后将子任务分发给多个节点(可以是服务器,机器,或者是加速卡)并行完成。
图 [1.2](#fig:ch010/ch10-single-vs-multi){reference-type="ref"
reference="fig:ch010/ch10-single-vs-multi"}描述了如何将单节点执行转换为分布式执行的一般过程。在机器学习系统中(如图 [1.2](#fig:ch010/ch10-single-vs-multi){reference-type="ref"
reference="fig:ch010/ch10-single-vs-multi"}所示一个计算任务往往会有一组数据例如训练样本或者任务例如算子作为输入利用一个计算节点例如GPU生成一组输出例如梯度。假如单节点成为瓶颈我们可以利用分布式计算进行加速。如图 [1.2](#fig:ch010/ch10-single-vs-multi){reference-type="ref"
reference="fig:ch010/ch10-single-vs-multi"}所示,分布式执行一般具有三个步骤:第一步,我们需要将输入进行**切分**。第二步,每个输入部分会分发给不同的计算节点,实现**并行**计算。第三步,每个计算节点的输出,进一步**合并**,最终得到和单节点等价的计算结果。这种切分-并行-合并的模式本质上实现了分而治之算法Divide-and-Conquer
Algorithm的设计思想由于每个计算节点只需要负责更小的子任务因此其可以更快速的完成计算最终形成对整个计算过程的加速。
### 用户益处
\[总结系统对于用户的好处\]
通过使用分布式训练系统,我们往往可以获得以下几个关键好处:
- **提升系统性能**使用分布式训练往往可以带来训练性能的巨大提升。一个分布式训练系统往往用以下这个指标来衡量性能到达目标精度所需的时间time-to-accuracy。这个指标由两个参数决定:
一个数据周期所需的完成时间,以及一个数据周期模型所提升的精度。通过持续增加并行处理节点,我们可以将数据周期的完成时间不断变短,最终显著减少到达目标精度所需的时间。
- **经济性Economy**:使用分布式训练,我们也可以进一步减少训练及其模型所需的成本。受限于单节点散热和半导体制程的限制,在一个节点上不断增加算力和内存的成本一般会指数增加\[引用\]。因此,高性能的深度学习计算节点往往具有极高的硬件成本。而提供同等的算力下,通过组合多个计算节点往往比使用单个节点显著的更低。举例,在亚马逊\[\...\...\]。因此通过构建分布式训练系统,我们可以提升训练集群的经济性,降低模型的训练成本。
- **抵御硬件故障**分布式训练系统同时能有效提升抵御硬件故障的能力。机器学习训练集群往往由商用硬件Commodity
Hardware组成这类硬件例如说磁盘和网卡运行一定周期就会产生故障。而仅使用单个硬件进行训练的话那么一个硬件的故障就会造成整个训练的任务的失败。通过将这个训练任务又多个硬件共同完成即使一个硬件故障了我们也可以通过将这个硬件上相应的计算子任务转移给其余硬件继续完成训练从而避免训练任务的失败。
## 分布式训练方法
我们会讨论分布式训练系统实现的常用并行方法。我们首先给出并行方法的设计目标以及分类。然后,我们会详细描述各个并行方法。
### 概述
![单节点训练系统](figs/ch10/ch10-single-node.pdf){#fig:ch010/ch10-single-node}
分布式训练系统的设计目标是:将单节点训练系统转化成**等价的**并行训练系统,从而在不影响模型精度的条件下完成训练过程的加速。一个单节点训练系统往往如图[1.3](#fig:ch010/ch10-single-node){reference-type="ref"
reference="fig:ch010/ch10-single-node"}所示。一个训练过程会由多个数据小批次mini-batch完成。在图中一个数据小批次被标示为**数据**。训练系统会利用数据小批次来生成梯度,提升模型精度。这个过程由一个训练**程序**实现。在实际中,这个程序往往实现了一个多层神经网络的执行过程。
该神经网络的执行由一个计算图Computational
Graph表达。这个图有多个相互连接的算子Operator每个算子会拥有计算参数。每个算子往往会实现一个神经网络层Neural
Network Layer而参数则代表了这个层在训练中所更新的的权重Weights
为了更新参数,计算图的执行会分为**前向**传播和**反向**传播两个阶段。前向传播的第一步会将数据读入第一个算子该算子会根据当前的参数计算出传播给下一个算子的数据。算子依次重复这个前向传播的过程算子1
-\> 算子2 -\>
算子3直到最后一个算子结束。最后的算子随之马上开始反向传播。反向传播中每个算子依次计算出梯度梯度3
-\> 梯度2 -\>
梯度1并利用梯度更新本地的参数。反向传播最终在第一个算子结束。反向传播的结束也标志本次数据小批次的结束系统随之读取下一个小批次继续更新模型。
::: {#tab:ch010/ch10-parallel-methods}
单数据 多数据
-------- ------------------------ ------------------------
单程序 单程序单数据:单点执行 单程序多数据:数据并行
多程序 多程序单数据:模型并行 多程序多数据:混合并行
: 分布式训练方法分类
:::
给定一个单节点训练系统,人们会对**数据**和**程序**分区Partition从而完成并行加速。表[1.1](#tab:ch010/ch10-parallel-methods){reference-type="ref"
reference="tab:ch010/ch10-parallel-methods"}总结了不同的切分方法。单节点训练系统可以被归类于
单程序单数据模式。而假如用户希望使用更多的设备来实现并行计算,他们首先可以选择对数据进行分区,并将同一个程序复制到多个设备上并行执行。这种方式是单程序多数据模式,常被称为**数据并行**Data
Parallelism。另一种并行方式是对程序进行分区程序的算子会被分发给多个设备按照依次完成。这种模式是
多程序单数据模式,常被称为**模型并行**Model
Parallelism。当训练超大型智能模型时开发人们往往要同时对数据和程序进行切分从而实现最高程度的并行。这种模式是多程序多数据模式常被称为**混合并行**Hybrid
Parallelism
接下来,我们详细讲解各种并行方法的执行过程。
### 数据并行
![数据并行训练系统](figs/ch10/ch10-data-parallel.pdf){#fig:ch010/ch10-data-parallel}
数据并行往往可以解决单节点的算力不足。这种并行方式在人工智能框架中最为常见具体实现包括TensorFlow
DistributedStrategy [@tensorflow_distributed])PyTorch
Distributed [@pytorch_distributed]Horovod
DistributedOptimizer [@horovod_distributed]等。在一个数据并行系统中,假设用户给定一个训练批大小$N$,并且希望使用$M$个并行设备来加速训练。那么,该训练批大小会被分为$M$个分区,每个设备会分配到$N/M$个训练样本。这些设备共享一个训练程序的副本,在不同数据分区上独立执行,计算梯度。不同的设备(假设设备编号为$i$)会根据本地的训练样本估计出梯度$G_i$。为了确保训练程序参数的一致性,本地梯度$G_i$需要聚合,计算出平均梯度$(\sum_{i=1}^{N} G_i) / N$。最终,训练程序利用平均梯度修正模型参数,完成小批量的训练。
图[1.4](#fig:ch010/ch10-data-parallel){reference-type="ref"
reference="fig:ch010/ch10-data-parallel"}展示了2个设备构成的数据并行例子。假设用户给定的批大小Batch
Size是64那么每个设备会分配到32个训练样本并且具有相同的神经网络参数程序副本。本地的训练样本会依次通过这个程序副本中的算子完成前向传播和反向传播。在反向传播的过程中程序副本会生成局部梯度。不同设备上对应的局部梯度如设备1和设备2上各自的梯度1会进行聚合从而计算平均梯度。这个聚合的过程往往由集合通讯库Collective
Communication的Allreduce操作来完成。
### 模型并行
![模型并行系统:算子内并行](figs/ch10/ch10-model-parallel-intra-op.pdf){#fig:ch010/ch10-model-parallel-intra-op}
模型并行往往用于解决单节点的内存不足问题。一个常见的内存不足场景是模型中含有大型算子例如说深度神经网络中需要计算大量分类的全连接层Fully
Connected
Layer。完成这种大型算子计算所需的内存可能超过单设备的内存容量。那么我们需要对这个大型算子进行切分。假设这个算子具有$P$个参数,而我们拥有$N$个设备,那么我们可以将$P$个参数平均分配给$N$个设备(每个设备分配$P/N$个参数),从而让每个设备负责更少的计算量,能够在内存容量的限制下完成前向传播和反向传播中所需的计算。这种切分方式是模型并行的应用,被称为**算子内并行**Intra-operator
Parallelism
图[1.5](#fig:ch010/ch10-model-parallel-intra-op){reference-type="ref"
reference="fig:ch010/ch10-model-parallel-intra-op"}给出了一个由2个设备实现的算子内并行的例子。在这个例子中假设一个神经网络具有2个算子算子1的计算包含正向和反向传播需要预留16G的内存算子2的计算需要预留1G的内存。而本例中的设备最多可以提供10G的内存。为了完成这个神经网络的训练我们需要对算子1实现并行。具体做法是将算子1的参数平均分区设备1和设备2各负责其中部分算子1的参数。由于设备1和设备2的参数不同因此它们各自负责程序分区1和程序分区2。在训练这个神经网络的过程中数据小批量会首先传给算子1。由于算子1的参数分别由2个设备负责因此数据会被广播给这2个设备。不同设备根据本地的参数分区完成前向计算生成的本地计算结果需要进一步合并Combine发送给下游的算子2。在反向传播中算子2的数据会被广播给设备1和设备2这些设备根据本地的算子1分区各自完成局部的反向计算。计算结果进一步合并传播回数据最终完成反向传播。
另一种内存不足的场景是:模型的总内存需求超过了单设备的内存容量。在这种场景下,假如我们总共有$N$个算子和$M$个设备,我们可以将算子平摊给这$M$个设备,让每个设备仅需负责$N/M$个算子的前向和反向计算,降低设备的内存开销。这种并行方式是模型并行的另一种应用,被称为**算子间并行**Inter-operator
Parallelism
![模型并行系统:算子间并行](figs/ch10/ch10-model-parallel-inter-op.pdf){#fig:ch010/ch10-model-parallel-inter-op}
图[1.6](#fig:ch010/ch10-model-parallel-inter-op){reference-type="ref"
reference="fig:ch010/ch10-model-parallel-inter-op"}给出了一个由2个设备实现的算子间并行的例子。在这个例子中假设一个神经网络具有2个算子算子1和算子2各自需要10G的内存完成计算则模型总共需要20G的内存。而每个设备仅能提供10G内存。在这个例子中用户可以把算子1放置在设备1上算子2放置在设备2上。在前向传播中算子1的输出会被发送Send给下游的设备2。设备2接收Receive来自上游的数据完成算子2的前向计算。在反向传播中设备2将算子2的反向计算结果发送给设备1。设备1完成算子1的反向计算完成本次训练。
### 混合并行
![混合并行系统](figs/ch10/ch10-hybrid-parallel.pdf){#fig:ch010/ch10-hybrid-parallel}
在训练大型人工智能模型中,我们往往会同时面对算力不足和内存不足。因此,我们需要混合使用数据并行和模型并行,这种方法被称为混合并行。图[1.7](#fig:ch010/ch10-hybrid-parallel){reference-type="ref"
reference="fig:ch010/ch10-hybrid-parallel"}提供了一个由4个设备实现的混合并行的例子。在这个例子中我们首先实现算子间并行来解决训练程序内存开销过大的问题该训练程序的算子1和算子2被分摊到了设备1和设备2上。进一步我们通过数据并行来添加3和设备4提升系统算力。为了达到这一点我们对训练数据进行分区数据分区1和数据分区2并将模型算子1和算子2分配复制到设备3和设备4上生成可以并行执行的程序副本。在前向计算的过程中设备1和设备3上的算子1副本同时开始计算结果分别发送Send给设备2和设备4完成算子2副本的计算。在反向计算中设备2和设备4同时开始计算梯度本地梯度通过Allreduce进行平均。反向计算传递到设备1和设备3上的算子1副本结束。
## 流水线并行
在数据并行和模型并行以外,流水线并行是另一种常用的并行加速方法。
流水线并行往往被应用在大型模型并行系统中。这种系统通过算子内并行和算子间并行解决单设备内存不足的问题。
然而,当这类系统的运行中(如图[1.5](#fig:ch010/ch10-model-parallel-intra-op){reference-type="ref"
reference="fig:ch010/ch10-model-parallel-intra-op"}和图[1.6](#fig:ch010/ch10-model-parallel-inter-op){reference-type="ref"
reference="fig:ch010/ch10-model-parallel-inter-op"}所示计算图中的下游设备需要长期持续处于空闲状态等待上游设备的计算完成才可以开始计算这极大降低了设备的平均使用率。这种现象被称为模型并行空洞Model
Parallelism Bubble
![流水线并行系统。**注意图的F和B任务的编号需要更新**](figs/ch10/ch10-pipeline-parallel.pdf){#fig:ch010/ch10-pipeline-parallel
width="\\linewidth"}
为了减少空洞提升设备使用率我们可以在模型并行系统中构建流水线。这种做法的核心想法是将一个数据小批量Data
Mini-batch划分为多个微批量Micro-batch。假设一个数据小批量有$D$个训练数据,这个小批量可以被划分为$M$个微批量,那么微批量的大小就是$D/M$。每个微批量相应进入训练系统完成前向传播Forwards
propagation和反向传播Backwards
propagation计算出梯度。每个微批量对应的梯度将会缓存等到全部微批量完成缓存的梯度会被加和算出平均梯度更新模型参数。
图[1.8](#fig:ch010/ch10-pipeline-parallel){reference-type="ref"
reference="fig:ch010/ch10-pipeline-parallel"}进一步给出了一个流水线并行的执行例子。在本例中模型参数需要切分给4个设备存储。为了充分利用起来这4个设备我们将小批量切分为2个微批量。当设备1完成第一个微批量的前向传播后表示为$F_{0,0}$他会将中间结果发送给设备2触发响应的前向传播任务表示为$F_{1,0}$。与此同时设备1也可以开始第二个微批量的前向传播任务表示为$F_{0,1}$)。前向传播会在流水线的最后一个设备--设备3--完成。系统于是开始反向传播。设备4开始第1个微批量的反向传播任务表示为$B_{3,0}$。该任务完成后的中间结果会被发送给设备3触发响应的反向传播任务表示为$B_{2,0}$。与此同时设备4会缓存好对应第1个微批量的梯度接下来开始第2个微批量计算表示为$B_{3,1}$。当设备4完成了全部的反向传播计算后他会将本地缓存的梯度进行相加并且除以微批量数量计算出平均梯度该梯度用于更新模型参数。
流水线并行的关键因素是流水线泡沫Bubble。当设备完成前向传播后必须等到全部反向传播开发在此期间设备会处于空闲状态。在图[1.8](#fig:ch010/ch10-pipeline-parallel){reference-type="ref"
reference="fig:ch010/ch10-pipeline-parallel"}中我们可以看到设备1在完成2个前向传播任务后要等很多时间才能开始2个传向传播任务。这其中的等待时间即被称为泡沫。为了减少设备的等待时间一种常见的做法是尽可能的增加微批量的数量从而让反向传播尽可能早的开始。然而使用非常小的微批量大小可能会造成加速器无法被充分利用。因此最优的微批量大小是多种因素的折中。其中最核心的因素是流水线泡沫的大小和加速器的计算能力。
## 集合通讯的高效实现
接下来,我们会讲解常见的大型深度模型训练的系统实现。
这一类系统往往部署在商用的数据中心Data
Centers以及如何在数据中心中高效实现集合通讯从而让分布式训练系统免于网络瓶颈。
### 梯度计算和数据中心网络
图[1.9](#fig:ch010/ch10-datacentre){reference-type="ref"
reference="fig:ch010/ch10-datacentre"}描述了一个典型的用于深度学习模型训练的数据中心。数据中心中的训练服务器一般会有多个设备。如需增加服务器我们会将多个训练服务器放置在一个机柜Rack同时接入一个架顶交换机Top
of Rack
Switch来连接多个服务器。当一个机柜满的时候我们可以通过在架顶交换机之间增加骨干交换机Spine
Switch接入新的机柜。通过这种方式我们可以在数据中心内不断增加服务器从而为神经网络的训练提供海量的算力和内存。目前的商用数据中心可能拥有超过一百万台服务器。
![数据中心](figs/ch10/ch10-datacentre.pdf){#fig:ch010/ch10-datacentre}
在数据中心中训练大型神经网络的首要挑战是如何高效计算大量的平均梯度。假设给定一个千亿级别参数的神经网络GPT-3模型含有1750亿参数如果用32位浮点数来表达每一个参数那么每一步训练中一个数据并行模式下的模型副本Model
replica就需要生成700GB的本地梯度数据即 175G $\times$ 4 bytes =
700GB。假如我们有3个模型副本那么至少需要传输1.4TB700GB
$\times$
$(3-1)$)的本地梯度(这是因为$N$个副本中,我们只需要传送$N-1$梯度来完成平均梯度计算。当平均梯度计算完成后我们需要进一步将平均梯度广播到全部的模型副本即1.4TB的数据更新本地参数从而确保模型副本不会偏离Diverge
当前的数据中心往往使用以太网Ethernet构建网络。主流的商用以太网链路带宽一般是10Gbps和25Gbps。利用以太网传输海量梯度会产生严重的传输延迟从而降低模型训练的速度。新型深度学习训练集群如英伟达的DGX系列机器往往配置有更快的Inifiband。单个InfiniBand链路可以提供100Gbps和200Gbps的带宽。即使拥有这种高速网络传输TB级别的本地梯度依然需要大量延迟1TB的数据需要在200Gbps的链路上传输25秒
为了避免通过网络传输数据现代深度学习服务器一般都会配备多个计算设备例如说DGX-3机器会被配备8个A100
GPU而在一个服务器内的多个设备可以通过高速机内网络互联如NVLink。这种高速机内网络可以提供高达400GB/s的带宽从而让传输TB级别数成为可能。然而受限于单个服务器的散热成本和硬件故障等需求在一个服务器内我们无法无限制的持续增加设备大型深度学习模型的训练最终还是需要多个服务器共同完成。因此计算平均梯度需要同时借助以太网或者是InfiniBand以及服务器内部的NVLink等机内网络。
### 高效梯度计算Allreduce算法
为了在数据中心中高效完成梯度平均的操作,我们往往会实现
Allreduce算法。这个算法诞生的背景是传统计算平均梯度的方法往往是在集群中找出一个设备来收集本地梯度计算平均梯度然后再将平均梯度广播到全部的设备。这种做法易于实现但是其引入了两个问题。首先多设备共同给这个聚合设备发送数据的时候在聚合设备上往往会产生严重的带宽不足和网络拥塞。其次单设备需要负担大量的梯度平均的计算而受限于单设备上的有限算力这种平均计算会受限于算力瓶颈。
![Allreduce初始状态和终止状态](figs/ch10/ch10-allreduce-state.pdf){#fig:ch010/ch10-allreduce-state
width="\\linewidth"}
为了解决上述问题人们设计了Allreduce算法。该算法的核心设计思路是让全部的节点参与进来平均梯度的网络通信和平均计算中从而将巨大的网络和算力开销均摊给全部节点从而解决使用单个梯度聚合节点的问题。假设我们有$M$个设备,每个设备有一个模型副本,该模型由$N$个参数构成。那么按照Allreduce算法要求我们需要首先将全部的参数按照设备数量切分成$M$个分区Partition每个分区具有$N/M$个参数。
为了讲解Allreduce的过程我们首先给出这个算法的初始和终止状态。如图[1.10](#fig:ch010/ch10-allreduce-state){reference-type="ref"
reference="fig:ch010/ch10-allreduce-state"}
所示该例子含有3个设备每个设备有一个模型副本这个副本有3个参数。那么按照Allreduce的分区方法参数会被划分成3个分区3个设备而每一个分区有1个参数$N/M$N代表3个参数M代表3个设备。在这个例子中假定设备1拥有参数2,4,6设备2拥有参数1,2,3设备3拥有参数4,8,12那么Allreduce结束后全部的设备都拥有梯度相加后的结果7,14,21其中分区1的结果7是由3个设备中分区1的初始结果相加而成7
= 1 + 2 +
4。为了计算平均梯度每个设备只需要在最后将梯度之和除以设备数量即可分区1的最终结果为7除以3
Allreduce算法会把梯度的加和计算拆分成$M-1$个Reduce步骤和$M-1$个Broadcast步骤其中$M$是节点的数量。Reduce步骤是为了计算出梯度的和SummationBroadcast步骤是为了把梯度之和广播给全部的节点。为了说明这些步骤的执行过程我们利用
图[1.11](#fig:ch010/ch10-allreduce-process){reference-type="ref"
reference="fig:ch010/ch10-allreduce-process"}。Allreduce算法由Reduce步骤开始在第一个Reduce步骤中Allreduce算法会对全部节点进行配对Pairing让他们共同完成梯度相加的操作。在图[1.11](#fig:ch010/ch10-allreduce-process){reference-type="ref"
reference="fig:ch010/ch10-allreduce-process"}的第一个Reduce步骤中设备1和设备2进行了配对共同对分区1的数据相加。其中设备2把本地的梯度数据1发送给设备1设备将接收到1和本地的分区1内的梯度数据2进行相加计算出中间intermediate梯度相加的结果3。于此同时设备1和设备3进行配对共同完成对分区3的数据相加。而设备3和设备2进行配对共同完成对于分区2的数据相加。
在上述Reduce的步骤中梯度的计算实现了以下几个特性:
- **网络优化:**
全部设备都同时在接收和发送数据利用起了每个设备的入口Ingress和出口Egress带宽。因此Allreduce过程中可利用的带宽是$M \times B$,其中$M$是节点数量,
$B$是节点带宽,从而让系统实现网络带宽上的可扩展性。
- **算力优化:**
全部设备的处理器都参与了梯度相加的计算。。因此Allreduce过程中可利用的处理器是$M \times P$,其中$M$是节点数量,
$P$是处理器数量,从而让系统实现计算上的可扩展性。
- **负载均衡:**
由于数据分区是平均划分的,因此每次设备分摊到的通讯和计算开销是相等的。
在接下来的Reduce步骤中Allreduce算法会对不同数据分区选择另外的配对方法。例如说在图[1.11](#fig:ch010/ch10-allreduce-process){reference-type="ref"
reference="fig:ch010/ch10-allreduce-process"}的第二个Reduce步骤中Allreduce算法会将设备1和设备3进行配对负责分区1的数据相加。将设备1和设备2进行配对负责分区2。将设备2和设备3进行配对负责分区3。在一个3个节点的Allreduce集群里在2个Reduce步骤完成后我们就计算出了每个分区的数据相加结果分区1的结果7此时在设备3上分区2的结果14此时在设备1上分区3的结果21此时在设备2上
![Allreduce算法的过程](figs/ch10/ch10-allreduce-process.pdf){#fig:ch010/ch10-allreduce-process
width="\\linewidth"}
接下来Allreduce算法将进入Broadcast阶段。这一阶段的过程和Reduce步骤类似核心区别是节点进行配对后他们不再进行数据相加而是将Reduce的计算结果进行广播。在图[1.11](#fig:ch010/ch10-allreduce-process){reference-type="ref"
reference="fig:ch010/ch10-allreduce-process"}中的第一个Broadcast步骤中设备1会将分区2的结果14直接写入设备3的分区2中。设备2会讲分区3的结果21直接写入设备1中。设备3会将分区1的结果直接写入设备2中。在一个3个节点的Allreduce集群中我们会重复2次Broadcast步骤来将每个分区的Reduce结果告知全部的节点。
Allreduce算法已经被常见的分布式训练框架包括Horovod, KungFu, TensorFlow
distributed, PyTorch
distributed等支持。当用户选择使用数据并行模式的过程其底层会默认触发。
## 参数服务器
接下来我们介绍另一种常见的分布式训练系统实现参数服务器。TensorFlow原生提供了参数服务器的实现。而其他框架例如PyTorch和MindSpore则需要用户使用第三方的参数服务器实现例如PS-Lite。
### 计算和存储分离
利用参数服务器的其中一个核心需求是实现计算和存储的分离。在训练模型中计算可以被理解为计算更新模型参数所需要的计算例如说计算本地梯度和计算平均梯度而存储可以被理解为将模型参数存储在内存设备中例如说主机内存加速卡内存和SSD设备。传统的神经网络训练中计算往往是核心瓶颈因此我们只需要配置有合适数量的带有加速卡的服务器常被称为训练服务器Training
servers
随着机器学习的发展新型的稀疏模型被开发出来。相比于传统的神经网络训练稀疏模型的训练往往不需要大量昂贵的计算加速卡GPU而需要海量的内存来存储嵌入表Embedding
table。例如说一个大型深度学习推荐系统中它们往往使用小型的深度神经网络如Multi-layer
Perception训练这种神经网络只需要几个GPU即可。而另一方面推荐系统中往往需要存储PB级别的嵌入表。嵌入表往往由推荐系统的用户特征User
feature和产品特征Item
feature构成。这些特征往往是大型向量Vector。现代推荐系统需要服务数亿的用户推荐数以千万的商品。假设用户的特征是1MB而系统需要服务10亿的用户那么用户的嵌入表就会有1PB的大小。而这个大小远远超过了一个深度学习服务器所具有的内存。假如我们部署大量的昂贵的深度学习服务器来存储海量嵌入表那么这些服务器上的加速卡的使用率将会极低无法实现对于硬件的高效利用。
![参数服务器](figs/ch10/ch10-parameter-servers.pdf){#fig:ch010/ch10-parameter-servers
width="0.63\\linewidth"}
为了解决上述问题,人们往往会在稀疏模型集群中混合部署:训练服务器和参数服务器,从而实现对于计算需求和内存需求分别满足。图[1.12](#fig:ch010/ch10-parameter-servers){reference-type="ref"
reference="fig:ch010/ch10-parameter-servers"}描述了带有参数服务器的机器学习集群。这个集群中含有2个训练服务器和2个参数服务器训练服务器一般是拥有加速卡的计算优化服务器Compute-optimised
server。而参数服务器一般是内存优化服务器Memory-optimised
server其的内存大小一般远远大于计算优化服务器。在一个稀疏模型中往往拥有神经网络参数和嵌入表参数。神经网络较小其可以存储在训练服务器内存中。而嵌入表很大因此需要存储在额外的参数服务器中。参数服务器一般会按照键-值对Key-value
pairs的方式来存储参数。常用的键包括用户名User ID产品名Item
ID或者是参数名Parameter
Key。常用的值是以多维度向量Multi-dimensional
tensors表达的模型参数。假如存在多个参数服务器参数服务器会用数据分区函数例如哈希函数和区域划分将健-值映射到不同参数服务器上。
为了完成对于模型的训练在每一步训练中训练服务器会根据当前的小批量训练数据找到本批量中需要用到的参数。例如说本小批量数据只会训练部分用户的特征那么这些用户的特征才会需要。根据参数服务器的数据分区函数训练服务器可以知道参数当前在哪个参数服务器上它们因此会用参数的键Key向对应的参数服务器发起拉取请求Pull
request。参数服务器响应并返回对应的值Value。训练服务器将拉取的参数往往是嵌入表和本地内存中的模型参数往往是神经网络进行合并从而对合并的模型进行训练计算梯度。假如训练服务器实现了数据并行那么训练服务器计算出的本地梯度需要利用Allreduce计算出平均梯度。对于训练服务器本地内存中的参数训练服务器可以马上利用平均梯度进行修改。对于在参数服务器中存储的参数训练服务器发起推送请求Push
request将平均梯度发送到参数服务器参数服务器更新本地存储的参数。
在以上的参数服务器架构中机器学习集群拥有者可以灵活的根据梯度计算所需要算力配置合理数量的训练服务器。他们也可以根据参数的数量配置大部分的稀疏参数Sparse
parameters在参数服务器中仅留下小部分的密集参数Dense
parameters在训练服务器中。密集参数和稀疏参数的核心区别是稀疏参数在每一步训练不一定都会被用到他们需要根据当前训练小批量来决定。而密集参数每一步训练都需要用到。因此为了频繁从参数服务器中拉取密集参数往往会存储在训练服务器中。
### 数据副本
在参数服务器的实际部署中人们往往需要解决数据热点问题。互联网数据往往符合幂律概率Power-law
distribution这会导致部分稀疏参数在训练过程中被访问的次数会显著高于其他参数。例如说热门商品的特征向量被训练服务器拉取的次数就会远远高于非热门商品。因此存储了热门数据的参数服务器所承受的数据拉取和推送请求会远远高于其他参数服务器因此形成数据热点伤害了系统的可扩展性。
解决数据热点问题的关键是利用在没有副本的情况下通用的做法是每隔一段时间将所有参数在外存中保存一份检查点checkpoint。当出现机器故障时首先所有的训练必须停止等待故障的机器恢复上线然后从外存中重新加载检查点。这就会导致从上一次保存检查点到故障发生时的数据全部丢失。保存一次检查点的开销随模型大小而增加训练大模型时通常每隔1-2小时保存一次。因此无副本的参数服务器如果发生故障会丢失最多1-2小时的数据。
解决参数服务器故障和数据热点问题的常用技术是构建模型主从副本。Master-slave
replication。一份参数在多个机器上拥有副本并指定其中一个副本作为主副本。训练服务器的所有更新操作都向主副本写入并同步至从副本上。如何取得共识确定哪一个副本是主副本是分布式系统领域一个经典问题已经有了相当多的成熟的算法例如Paxos[@lamport2001paxos]RAFT[@184040]ZooKeeper[@hunt2010zookeeper]等。此外主副本上的更新如何复制到从副本上也同样是分布式系统领域的经典共识问题。通常系统设计者需要在可用性Availability和一致性Consistency之间做出取舍。如果参数服务器副本间采用强一致性的复制协议例如链式复制[@li2014scaling])则可能导致训练服务器的推送请求失败,即参数服务器不可用。反之,如果参数服务器采用弱一致性的复制协议([@yu2020weips]),则可能导致副本间存储的参数不一致。
### 掉队者问题
参数服务器的另一大核心作用是可以让用户方便解决掉队者问题。在之前的讨论中在每一步训练结束后训练服务器都需要计算平均梯度来对每一个模型副本进行更新从而保证下一步训练开始前全部模型副本的参数的一致性这种对于参数一致性的确保一般被称为同步训练Synchronous
training。同步训练一般会有助于训练系统达到更好的模型精度但是当系统规模变大我们往往会在系统中引入掉队者Straggler。掉队者出现的原因很多。常见的原因包括掉队者设备可能和其他设备不在同一个机柜中因此掉队者的通讯带宽显著小于其他设备。另外掉队者设备也可能和其他进程共享本地的服务器计算和通讯资源形成资源竞争从而降低了性能。
掉队者对于基于Allreduce的同步训练系统的性能有显著影响这是因为Allreduce让全部节点参与到平均梯度的计算和通讯中而每个节点负责等量的数据。因此任何一个掉队者的出现都会让整个Allreduce操作延迟完成。为了解决这个问题人们也会使用参数服务器来计算平均梯度。一种常见的设计是训练服务器训练出本地梯度后会把本地梯度全部推送到参数服务器。参数服务器在等到一定数据训练服务器例如说90%的训练服务器)的本地梯度后,就开始计算平均梯度。这样可以确保平均梯度的计算不会被落后者的出现延误。计算好的平均梯度马上推送给全部训练服务器,开始下一轮训练。
0
解决掉队者的另外一种常见做法是利用参数服务器实现**异步训练**(Asynchronous
training)。在一个异步训练系统中,每个训练服务器在训练开始时,有相同的模型参数副本。在训练中,他们计算出本地梯度后会马上将本地梯度推送到参数服务器,参数服务器将推送的梯度立刻用于更新参数,并把更新好的参数马上推送回对应的训练服务器。在这个过程中,不同的训练服务器很可能会使用不同版本的模型参数进行本地梯度的计算,这种做法有可能会伤害模型的精度,但它同时让不同训练服务器可以按照各自的运算速度来推送和拉取参数,而无需等待同伴,因此避免了掉队者对于整个集群性能的影响。
## 总结
- 大型机器学习模型的出现带来了对于算力和内存需求的快速增长,催生了分布式训练系统的出现。
- 分布式训练系统的设计往往遵循"分而治之"的设计思路。
- 利用分布式训练系统,人们可以显著提升性能性能,经济性,并且帮助抵御硬件故障。
- 分布式训练系统可以通过数据并行增加设备来提升算力。
- 当单节点内存不足时,我们可以通过模型并行来解决单设备内存不足。模型并行有两种实现方式:算子内并行和算子间并行。
- 大型模型并行系统容易出现设备使用空洞,而这种空洞可以通过流水线并行解决。
- 分布式训练系统往往运行在商用数据中心之中,数据中心网络无法提供充足的网络带宽来传输大量训练中生成的梯度。
- 为了提供海量的带宽机器学习集群拥有异构的网络以太网机内网络NVLink和InfiniBand。
- 为了解决单节点瓶颈,我们可以使用
Allreduce来分摊梯度聚合过程中的计算和通讯开销。
- 参数服务器可以帮助机器学习集群实现计算-存储的分离,从而更好的支持大型稀疏模型。
- 参数服务器常用数据副本技术解决数据热点问题,同时它们也可以被用来解决同步训练系统中常见的掉队者问题。

View File

@@ -1,10 +1,27 @@
# 模型部署
在前面的章节中,我们讲述了机器学习模型训练系统的基本组成,这一章节我们将讲述模型部署的相关知识。模型部署是将训练好的模型部署到运行环境中进行推理的过程,模型部署的过程中需要解决训练模型到推理模型的转换,硬件资源对模型的限制,模型推理的时延、功耗、内存占用等指标对整个系统的影响以及模型的安全等一系列的问题。
本章将主要介绍机器学习模型部署的主要流程,包括训练模型到推理模型的转换、适应硬件限制的模型压缩技术、模型推理及性能优化以及模型的安全保护,最后我们会给出一个模型部署端到端的实践用例。
本章的学习目标包括:
- 了解训练模型到推理模型转换及优化
- 掌握模型压缩的常用方法:量化、稀疏和知识蒸馏
- 掌握模型推理的流程及常用的性能优化的技术
- 了解模型安全保护的常用方法
```toc
:maxdepth: 2
:numbered:
model_deployment_introduction
model_converter_and_optimizer
model_compression
model_inference
model_security
summary
```

View File

@@ -0,0 +1,122 @@
## 模型压缩
:label:`ch08-sec-model_compression`
在上一小节中,我们简要介绍了模型转换的目的,并重点讲述了模型部署时的一些常用的模型优化手段。考虑到不同场景的硬件对模型的要求不同,比如部署在手机上,对于模型的大小比较敏感,一般在兆级别。因此,对于一些较大的模型,我们往往需要通过一些模型压缩的技术,使其能满足不同计算硬件的要求。
### 量化
模型量化是指以较低的推理精度损失将连续取值通常为FP32或者大量可能的离散值的浮点型权重或者通过各个算子的数据定点近似通常为INT8为有限多个离散值的过程如 :numref:`ch08-fig-quant_minmax`T是量化前的数据范围。通过以更少的位数表示浮点数据模型量化可以减少模型尺寸进而减少在推理时的内存消耗并且在一些低精度运算较快的处理器上可以增加推理速度。
![量化原理](../img/ch08/quant-minmax.png)
:width:`300px`
:label:`ch08-fig-quant_minmax`
计算机中不同数据类型的占用比特数及其表示的数据范围各不相同。可以根据实际业务需求将原模型量化成不同比特数的模型一般深度神经网络的模型用单精度浮点数表示如果能用有符号整数来近似原模型的参数那么被量化的权重参数存储大小就可以降到原先的四分之一用来量化的比特数越少量化后的模型压缩率越高。工业界目前最常用的量化位数是8比特低于8比特的量化被称为低比特量化。1比特是模型压缩的极限可以将模型压缩为1/32在推理时也可以使用高效的XNOR和BitCount位运算来提升推理速度。
另外,根据量化数据表示的原始数据范围是否均匀,还可以将量化方法分为线性量化和非线性量化。实际的深度神经网络的权重和激活值通常是不均匀的,因此理论上使用非线性量化能够达到更高的精度,但在实际推理中非线性量化的计算复杂度较高,通常使用线性量化。下面着重介绍线性量化的原理。
假设r表示量化前的浮点数量化后的整数q可以表示为
$$q=clip(round(\frac{r}{s}+z),q_{min},q_{max})$$
$round(\cdot)$和$clip(\cdot)$分别表示取整和截断操作,$q_{min}$和$q_{max}$是量化后的最小值和最大值。$s$是数据量化的间隔,$z$是表示数据偏移的偏置,$z$为0的量化被称为对称Symmetric量化不为0的量化称为非对称Asymmetric量化。对称量化可以避免量化算子在推理中计算z相关的部分降低推理时的计算复杂度非对称量化可以根据实际数据的分布确定最小值和最小值可以更加充分的利用量化数据信息使得计算精度更高。
根据量化参数$s$和$z$的共享范围,量化方法可以分为逐层量化和逐通道量化。逐层量化以一层网络为量化单位,每层网络的一组量化参数;逐通道量化以一层网络的每个量化通道为单位,每个通道单独使用一组量化参数。逐通道量化由于量化粒度更细,能获得更高的量化精度,但计算也更复杂。
根据量化过程中是否需要训练可以将模型量化分为量化感知训练Quantization Aware Training, QAT和训练后量化Post Training Quantization, PTQ两种,其中感知量化训练是指在模型训练过程中加入伪量化算子,通过训练时统计输入输出的数据范围可以提升量化后模型的精度,适用于对模型精度要求较高的场景;训练后量化指对训练后的模型直接量化,只需要少量校准数据,适用于追求高易用性和缺乏训练资源的场景。
#### 量化感知训练
量化感知训练是在训练过程中模拟量化,利用伪量化节点将量化带来的精度变化计入训练误差,使得优化器能在训练过程中尽量减少量化误差,得到更高的模型精度。量化感知训练的具体流程如下:
- 初始化:设置权重和激活值的范围$q_{min}$和$q_{max}$的初始值;
- 构建模拟量化网络:在需要量化的权重和激活值后插入伪量化节点;
- 量化训练:重复执行以下步骤直到网络收敛,计算量化网络层的权重和激活值的范围$q_{min}$和$q_{max}$,前向计算反向传播更新网络权重参数;
- 导出量化网络:获取$q_{min}$和$q_{max}$,并计算量化参数$s$和$z$
根据公式计算权重的量化整数值,并替换对应网络层的参数和数据类型;
删除伪量化节点,在量化网络层前后分别插入量化和反量化算子。
#### 训练后量化
训练后量化也可以分成两种权重量化和全量化。权重量化仅量化模型的权重以压缩模型的大小在推理时将权重反量化为原始的FP32数据后续推理流程与普通的FP32模型一致。权重量化的好处是不需要校准数据集不需要实现量化算子且模型的精度误差较小由于实际推理使用的仍然是FP32算子所以推理性能不会提高。全量化不仅会量化模型的权重还会量化模型的激活值在模型推理时执行量化算子来加快模型的推理速度、为了量化激活值需要用户提供一定数量的校准数据集用于统计每一层激活值的分布并对量化后的算子做校准。校准数据集可以来自训练数据集或者真实场景的输入数据需要数量通常非常小。在做训练后量化时会以校准数据集为输入执行推理流程然后统计每层激活值的数据分布并得到相应的量化参数具体的操作流程如下
- 使用直方图统计的方式得到原始FP32数据的统计分布$P_f$
- 在给定的搜索空间中选取若干个$q_{min}$和$q_{max}$分别对激活值量化,得到量化后的数据$Q_q$
- 使用直方图统计得到$Q_q$的统计分布;
- 计算每个$Q_q$与$P_f$的统计分布差异,并找到差异性最低的一个对应的$q_{min}$和$q_{max}$来计算相应的量化参数常见的用于度量分布差异的指标包括KL散度(Kullback-Leibler Divergence)、对称KL散度(Symmetric Kullback-Leibler Divergence)和JS散度(Jenson-Shannon Divergence)。
除此之外,由于量化存在固有误差,还需要校正量化误差。以矩阵乘为例,$a=\sum_{i=1}^Nw_ix_i+b$w表示权重x表示激活值b表示偏置。首先需要对量化的均值做校正对fp32算子和量化算子输出的每个通道求平均假设某个通道i的fp32算子输出均值为$a_i$,量化算子反量化输出均值为$a_{qi}$,将这个通道两个均值的差$a_i-a_q$加到对应的通道上即可使得最终的输出均值和fp32一致。另外还需要保证量化后的分布和量化前是一致的设某个通道权重数据的均值、方差为$E(w_c)$、$||w_c-E(w_c)||$,量化后的均值和方差为$E(\hat{w_c})$、$||\hat{w_c}-E(\hat{w_c})||$,对权重做如下校正:
$$\hat{w_c}\leftarrow\zeta_c(\hat{w_c}+u_c)$$
$$u_c=E(w_c)-E(\hat{w_c})$$
$$\zeta_c=\frac{||w_c-E(w_c)||}{||\hat{w_c}-E(\hat{w_c})||}$$
量化方法作为一种通用的模型压缩方法,可以大幅提升神经网络存储和压缩的效率,已经取得了广泛的应用。
### 模型稀疏
模型稀疏是通过去除神经网络中部分组件(如权重、特征图、卷积核)降低网络的存储和计算代价,它和模型权重量化、权重共享、池化等方法一样,属于一种为达到降低模型计算复杂度的目标而引入的一种强归纳偏置。
#### 模型稀疏的动机
因为卷积神经网络中的卷积计算可以被看作输入数据和卷积核中权重的加权线性组合,所以细小的权重对输出数据就具有相对较小的影响。对模型进行稀疏操作的合理性主要来源于两方面的假设:
- 其一针对权重参数来说当前许多神经网络模型存在过参数化Over-parameterized的现象动辄具有几千万甚至数亿规模的参数量。
- 其二,针对模型推理过程中生成的激活值特征图,对于许多检测、分类、分割等视觉任务来说激活值特征图中能利用的有效信息相对于整张图仅占较小的比例。
根据以上描述按照模型稀疏性来源的不同主要分为权重稀疏和激活值稀疏它们的目的都是为了减少模型当中的冗余成分来达到降低计算量和模型存储的需求。具体来说对模型进行稀疏就是根据模型的连接强弱程度一般根据权重或激活的绝对值大小对一些强度较弱的连接进行剪枝将权重参数或激活值置为0来达到模型稀疏并提高模型推理性能的目的。特别地我们将模型权重或激活值张量中0值所占的比例称为模型稀疏度。一般而言模型稀疏度越高带来的模型准确率下降越大因此我们的目标是尽可能在提高模型稀疏度的同时保证模型准确率下降较小。
实际上,如同神经网络本身的发明受到了神经生物学启发一样,神经网络模型稀疏方法同样受到了神经生物学的启发。在一些神经生物学的发现中,人类以及大多数哺乳动物的大脑都会出现一种叫做突触修剪的活动。突触修剪即神经元的轴突和树突发生衰退和完全死亡,这一活动发生在哺乳动物的婴幼儿时期,然后一直持续到成年以后。这种突触修剪机制不断简化和重构哺乳动物大脑的神经元连接,使得哺乳动物的大脑能以更低的能量获得更高效的工作方式。
#### 结构与非结构化稀疏
首先我们考虑权重稀疏对于权重稀疏来说按照稀疏模式的不同主要分为结构化和非结构化稀疏。简单来讲结构化稀疏就是在通道或者卷积核层面对模型进行剪枝。这种稀疏方式能够得到规则且规模更小的权重矩阵因此比较适合CPU和GPU进行加速计算。但与此同时结构化稀疏是一种粗粒度的稀疏方式将会对模型的推理准确率造成较大的下降。
而非结构化稀疏,可以对权重张量中任意位置的权重进行裁剪,因此这种稀疏方式属于细粒度的稀疏。这种稀疏方式相对于结构化稀疏,造成的模型准确率下降较小。但是也正是因为这种不规则的稀疏方式,导致稀疏后的模型难以利用硬件获得较高的加速比。其背后原因主要有以下几点:
- 不规则排布的模型权重矩阵会带来大量的控制流指令比如由于大量0值的存在我们会不可避免地引入大量if-else分支判断指令因此会降低指令层面的并行度。
- 权重矩阵的不规则内存排布会造成线程发散和负载不均衡,而不同卷积核往往是利用多线程进行计算的,因此这也影响了线程层面的并行度。
- 权重矩阵的不规则内存排布造成了较低的访存效率,因为它降低了数据的局部性以及缓存命中率。
为了解决以上非结构化稀疏带来的种种问题,近期出现的研究当中通过引入特定稀疏模式将结构化稀疏和非结构化稀疏结合了起来,从而一定程度上兼具结构化和非结构化稀疏的优点并克服了两者的缺点。
#### 稀疏策略
明确了模型稀疏的对象之后,我们需要确定模型稀疏的具体策略,具体来说我们需要决定何时对模型进行稀疏以及如何对模型进行稀疏。目前最常见模型稀疏的一般流程为:预训练、剪枝、微调。具体而言,我们首先需要训练得到一个收敛的稠密模型,然后在此基础上进行稀疏和微调。选择在预训练之后进行稀疏动作的原因基于这样一个共识,即预训练模型的参数蕴含了学习到的知识,继承这些知识然后进行稀疏得到的模型效果要比从头开始训练好。除了基于预训练模型进行进行一步修剪之外,训练和剪枝交替进行也是一种常用的策略。相比于一步修剪的方法,这种逐步的修剪方式,使得训练和剪枝紧密结合,可以更有效地发现冗余的卷积核,被广泛采用于现代神经网络剪枝方法中。
以下通过一个具体实例(Deep Compression([@han2015deep])
来说明如何进行网络修剪:如 :numref:`ch08-fig-deepcomp`所示在去掉大部分的权值之后深度卷积神经网络的精度将会低于其原始的精度。对剪枝后稀疏的神经网络进行微调可以进一步提升压缩后网络的精度。剪枝后的模型可以进一步进行量化使用更低比特的数据来表示权值此外结合霍夫曼Huffman编码可以进一步地降低深度神经网络的存储。
![Deep Compression([@han2015deep])](../img/ch08/deepcomp.png)
:width:`700px`
:label:`ch08-fig-deepcomp`
除了直接去除冗余的神经元之外,基于字典学习的方法也可以用来去掉深度卷积神经网络中无用的权值([@bagherinezhad2017lcnn])。通过学习一系列卷积核的基可以把原始卷积核变换到系数域上并且它们稀疏。比如Bagherinezhad等人([@bagherinezhad2017lcnn])将原始卷积核分解成卷积核的基和稀疏系数的加权线性组合。
### 知识蒸馏
知识蒸馏,也被称为教师-学生神经网络学习算法,已经受到业界越来越多的关注。大型深度模型在实践中往往会获得良好的性能,因为当考虑新数据时,过度参数化会提高泛化性能。在知识蒸馏中,小模型(学生模型)通常是由一个大模型(教师模型)监督,算法的关键问题是如何从老师模型转换的知识传授给学生模型。通过把一个全新的更深的更窄结构的深度神经网络当作学生神经网络,然后把一个预先训练好的神经网络模型当作教师神经网络。利用这个教师神经网络模型来帮助学生神经网络模型的算法是当下的一个研究热点。
Hinton等人([@Distill])首先提出了教师神经网络-学生神经网络学习框架,通过最小化两个神经网络之间的差异来学习一个更窄更深的神经网络。记教师神经网络为$\mathcal{N}_{T}$,它的参数为$\theta_T$,同时记学生神经网络为$\mathcal{N}_{S}$,相应的参数为$\theta_S$。一般而言,学生神经网络相较于教师神经网络具有更少的参数。
文献([@Distill])提出的知识蒸馏knowledge distillationKD方法同时令学生神经网络的分类结果接近真实标签并且令学生神经网络的分类结果接近于教师神经网络的分类结果
$$\mathcal{L}_{KD}(\theta_S) = \mathcal{H}(o_S,\mathbf{y}) +\lambda\mathcal{H}(\tau(o_S),\tau(o_T))$$
:label:`ch08-equ-c2Fcn_distill`
其中,$\mathcal{H}(\cdot,\cdot)$是交叉熵函数,$o_S$和$o_T$分别是学生网络和教师网络的输出,$\mathbf{y}$是标签。公式 :numref:`ch08-equ-c2Fcn_distill`中的第一项使得学生神经网络的分类结果接近预期的真实标签,而第二项的目的是提取教师神经网络中的有用信息并传递给学生神经网络,$\lambda$是一个权值参数用来平衡两个目标函数。$\tau(\cdot)$是一个软化soften函数将网络输出变得更加平滑。
公式 :numref:`ch08-equ-c2Fcn_distill`仅仅从教师神经网络分类器输出的数据中提取有价值的信息并没有从其它中间层去将教师神经网络的信息进行挖掘。因此Romero等人[@FitNet]进一步地开发了一种学习轻型学生神经网络的方法该算法可以从教师神经网络中任意的一层来传递有用的信息给学生神经网络。此外事实上并不是所有的输入数据对卷积神经网络的计算和完成后续的任务都是有用的。例如在一张包含一个动物的图像中对分类和识别结果比较重要的是动物所在的区域而不是那些无用的背景信息。所以有选择性地从教师神经网络的特征图中提取信息是一个更高效的方式。于是Zagoruyko和Komodakis[@attentionTS]提出了一种基于感知attention损失函数的学习方法来提升学生神经网络的性能如 :numref:`ch08-fig-AttentionTS`所示。该算法在学习学生神经网络的过程中引入了Attention模块选择性地将教师神经网络中的信息传递给学生神经网络并帮助其进行训练。
![文献([@attentionTS])所提出的教师神经网络-学生神经网络学习算法该算法在学习学生神经网络的过程中引入了感知模块Attention选择性地将教师神经网络中的信息传递给学生神经网络并帮助其进行训练。感知图可以识别输入图像不同位置对最终分类结果的重要性并从教师网络传递到学生网络。](../img/ch08/AttentionTS.png)
:width:`800px`
:label:`ch08-fig-AttentionTS`
知识蒸馏是一种有效的帮助小网络优化的方法,能够进一步和剪枝、量化等其他压缩方法结合,训练得到精度高、计算量小的高效模型。

View File

@@ -0,0 +1,93 @@
## 训练模型到推理模型的转换及优化
### 模型转换
前面我们提到过不同的训练框架Tensorflow、PyTorch、MindSpore、MXNet、CNTK等都定义了自己的模型的数据结构推理系统需要将它们转换到统一的一种数据结构上。Open Neural Network Exchange(ONNX正是为此目的而设计的。ONNX支持广泛的机器学习运算符集合并提供了不同训练框架的转换器例如TensorFlow模型到ONNX模型的转换器、PyTorch模型到ONNX模型的转换器等。
模型转换本质上是将模型这种结构化的数据从一种数据结构转换为另一种数据结构的过程。进行模型转换首先要分析两种数据结构的异同点然后针对结构相同的数据做搬运对于结构相似的数据做一一映射对于结构差异较大的数据则需要根据其语义做合理的数据转换更进一步如果两种数据结构上存在不兼容则模型转换无法进行。ONNX的一个优势就在于其强大的表达能力从而大多数业界框架的模型都能够转换到ONNX的模型上来而不存在不兼容的情况.
模型可以抽象为是一种图,从而模型的数据结构可以解构为以下两个要点:
- 模型拓扑表达从图的角度来说就是图的边从模型的角度来说就是模型中的数据流和控制流等模型数据流和控制流的定义又可以引申出子图的表达形式、模型输入输出的表达形式、控制流结构的表达形式等。比如Tensorflow1.x中的控制流表达为一种有环图通过Enter、Exit、Switch、LoopCond、NextIteration等算子来解决成环而ONNX通过LoopIf等算子来表达控制流从而避免引入了有环所以在将Tensorflow1.x的控制流模型转化为ONNX模型时需要将Tensorflow模型中的控制流图结构融合成ONNX的While或者If算子。
- 算子原型定义从图的角度来说就是图的顶点从模型角度来说就是模型中的数据处理节点或者控制流节点。算子原型包括但不限于算子类型、算子输入输出的定义、算子属性的定义等。比如Caffe的slice算子和ONNX的slice算子的语义其实是不一致的Caffe的slice算子应该映射到ONNX的Split算子所以在将Caffe模型转换成ONNX模型时需要将Caffe的Slice算子映射到ONNX的Split算子。比如Tensorflow中的中的FusedBatchNorm算子在Caffe中找不到相同语义的算子需要将Caffe的BatchNorm算子和Scale算子组合起来才能表达相同的语义。
通常模型转换的过程也就是转换模型中的拓扑关系和映射模型中的算子原型。
在完成模型转换之后,通常地,我们会将一些不依赖于输入的工作提前去完成。这些工作包括了如常量折叠、算子融合、算子替换、算子重排等一些优化手段。这些优化手段的概念在前面的章节其实已经提及到,比如在编译器前端阶段,通常也会做常量折叠;在编译器后端阶段,通常会根据后端的硬件支持程度,对算子进行融合和拆分。但是有些优化工作只有在部署阶段才能进行或者彻底进行。
### 算子融合
:label:`ch08-sec-fusion`
算子融合,就是将深度神经网络模型中的多个算子,按照一定的规则,合并成一个新的算子。通过算子融合,可以减少模型在线推理时的计算量、访存开销,从而降低推理时的时延和功耗。
![计算机分层存储架构](../img/ch08/storage.png)
:width:`150px`
:label:`ch08-fig-storage`
算子融合带来的性能上的收益主要来自两个方面一是通过融合充分利用寄存器和缓存避免多个算子运算时数据在CPU和内存之间的存储和读取的耗时。如 :numref:`ch08-fig-storage`可以看到计算机的储存系统从最靠近cpu的寄存器L1、L2等多级缓存到内存、硬盘其存储的容量越来越大但读取数据的耗时也越来越大。融合后前一次计算的结果可以先暂存在CPU的寄存器(Register)或者缓存Cache下一次计算直接从寄存器或者缓存中读取减少了内存读写的IO次数。二是通过融合可以将一些计算量提前完成避免了前向推理时的冗余计算或者循环冗余计算。
![Convolution + Batchnorm算子融合](../img/ch08/conv-bn-fusion.png)
:width:`500px`
:label:`ch08-fig-conv_bn_fusion`
如 :numref:`ch08-fig-conv_bn_fusion`我们以Convolution算子和Batchnorm算子的融合为例阐述算子融合的基本原理图中蓝色框表示算子黄色框表示融合后新增或者改变的算子白色框表示算子中的权重或者常数张量。其融合的过程是一个计算表达式简化的过程Convolution算子的计算过程可以等效为一个矩阵乘其公式可以表达为 :numref:`equ:conv-equation`
$$\pmb{Y_{conv}}=\pmb{W_{conv}}*\pmb{X_{conv}}+\pmb{B_{conv}}$$
:label:`ch08-equ-conv_equation`
这里我们不需要理解公式 :numref:`ch08-equ-conv_equation`中每个变量的含义,只需要注意到一点,该公式是$\pmb{Y_{conv}}$关于$\pmb{X_{conv}}$的,其他符号均表示常量。
Batchnorm算子的计算过程如公式 :numref:`equ:bn-equation`所示。
$$\pmb{Y_{bn}}=\gamma\frac{\pmb{X_{bn}}-\mu_{\mathcal{B}}}{\sqrt{{\sigma_{\mathcal{B}}}^{2}+\epsilon}}+\beta$$
:label:`ch08-equ-bn_equation`
同样这里我们不需要理解batchnorm中的所有参数的含义只需要了解公式 :numref:`ch08-equ-bn_equation`是$\pmb{Y_{bn}}$关于$\pmb{X_{bn}}$的,其他符号均表示常量。
如 :numref:`ch08-fig-conv_bn_fusion`当Convlution算子的输出作为Batchnorm输入时最终Batchnorm算子的计算公式也就是要求$\pmb{Y_{bn}}$关于$\pmb{X_{conv}}$的计算公式,我们将$\pmb{Y_{conv}}$代入到$\pmb{X_{bn}}$,然后将常数项合并提取后,可以得到公式 :numref:`equ:conv-bn-equation-3`
$$\pmb{Y_{bn}}=\pmb{A}*\pmb{X_{conv}}+\pmb{B}$$
:label:`ch08-equ-conv_bn_equation_3`
其中$\pmb{A}$和$\pmb{B}$为两个矩阵。可以看到,公式 :numref:`ch08-equ-conv_bn_equation_3`其实就是一个Convolution的计算公式。这个结果表明在模型部署时我们可以将Convolution和Batchnorm两个算子的计算等价为一个Convolution算子。我们将上述以计算公式的合并和简化为基础的算子融合称为计算公式融合。
在Convolution算子和Batchnorm算子融合的前后网络结构相当于减少了一个Batchnorm算子相应的网络中的参数量和网络所需的计算量都减少了同时由于算子数量的减少访存次数也相应地减少了。综合来看该融合Pattern优化了模型部署时的功耗、性能同时对于模型的体积大小也有少许收益。
在融合过程中Convolution计算公式和Batchnorm计算公式中被认为是常量的符号在训练时均为参数并不是常量。训练阶段如果进行该融合会导致模型参数的缺失。从该融合Pattern的结果来看融合后网络中减少了一个Batchnorm算子减少了一个Batchnorm算子的参数量其实就是改变了深度神经网络的算法会影响到网络的准确率这是不可接受的。所以Convolution算子与Batchnorm算子的融合一般是在部署阶段特有的一种优化手段其优化效果我们以MinsSpore Lite为例构造了包含一个Convolution和一个Batchnorm的sample网络分别以样例网络和mobilenet-v2网络为例在华为Mate30手机上以两线程运行模型推理取3000轮推理的平均时耗作为模型推理性能的指标对比融合前后该指标的变化。从表 :numref:`ch08-tab-conv_bn_fusion`可以看到对于sample网络和mobilenet-v2网络融合后分别获得了8.5%和11.7%的推理性能提升,这个性能提升非常可观。并且这个性能提升没有带来任何的副作用,也没有对于硬件或算子库的提出额外要求。
::: {#tab:ch08-tab-conv_bn_fusion}
网络 sample mobilenet-v2
------- ------------- -----------------
融合前 0.035 15.415
融合后 0.031 13.606
: Convolution + Batchnorm融合前后推理性能单位ms
:::
:label:`ch08-tab-conv_bn_fusion`
### 算子替换
算子替换,即将模型中某些算子替换计算逻辑一致但对于在线部署更友好的算子。算子替换的原理是通过合并同类项、提取公因式等数学方法,将算子的计算公式加以简化,并将简化后的计算公式映射到某类算子上。算子替换可以达到降低计算量、降低模型大小的效果。
![Batchnorm算子替换](../img/ch08/bn-replace.png)
:width:`500px`
:label:`ch08-fig-bn_replace`
如 :numref:`ch08-fig-bn_replace`我们以Batchnorm算子替换成Scale算子为例阐述算子替换的原理。我们直接将Batchnorm的计算公式 :numref:`ch08-equ-replace_scale`进行分解并将常量合并简化Batchnorm的计算公式可以写成
$$\pmb{Y_{bn}}=scale*\pmb{X_{bn}}+offset$$
:label:`ch08-equ-replace_scale`
其中scale和offset为两个标量。可以看到计算公式简化后我们可以将其映射到一个Scale算子。
在Batchnorm算子被替换为Scale算子的前后网络中的参数量、计算量都减少了该算子替换策略可以优化模型部署时的功耗和性能。同理该算子替换优化策略只能在部署阶段才能进行因为一方面在部署阶段Batchnorm计算公式中被认为是常量的符号在训练时是参数并非常量。另一方面该优化策略会降低模型的参数量改变模型的结构降低模型的表达能力影响训练收敛时模型的准确率。
### 算子重排
算子重排是指将模型中算子的拓扑序按照某些规则进行重新排布在不降低模型的推理精度的前提下降低模型推理的计算量。常用的算子重排技术有针对于Slice算子、StrideSlice算子、Crop算子等裁切类算子的前移、Reshape算子和Transpose算子的重排、BinaryOp算子的重排等。
![Crop算子重排](../img/ch08/crop-reorder.png)
:width:`500px`
:label:`ch08-fig-crop_reorder`
如 :numref:`ch08-fig-crop_reorder`Crop算子是从输入的feature map中裁取一部分作为输出经过Crop算子后feature map的size就降低了。如果我们将这个裁切的过程前移提前对feature map进行裁切那么后续算子的计算量也会相应地减少从而提高模型部署时的推理性能。Crop算子前移带来的性能提升跟Crop算子的参数有关。但是Crop算子一般只能沿着的element wise类算子前移。
通过前面的实验数据我们可以看到,通过推理前的模型优化,可以为推理的时延、功耗、内存占用带来极大的收益。

View File

@@ -0,0 +1,29 @@
## 概述
模型完成训练后,需要将模型及参数持久化成文件,不同的训练框架导出的模型文件中存储的数据结构不同,这给模型的推理系统带来了不便。推理系统为了支持不同的训练框架的模型,需要将模型文件中的数据转换成统一的数据结构。此外,在训练模型转换成推理模型的过程中,需要进行一些如算子融合、常量折叠等模型的优化以提升推理的性能。
推理模型部署到不同的场景需要满足不同的硬件设备的限制例如在具有强大算力的计算中心或数据中心的服务器上可以部署大规模的模型而在边缘侧服务器、个人电脑以及智能手机上算力和内存则相对有限部署的模型的规模就相应地要降低。在超低功耗的微控制器上则只能部署非常简单的机器学习模型。此外不同硬件对于不同数据类型如FP32、FP16、BF16、INT8等的支持程度也不相同。为了满足这些硬件的限制在有些场景下需要对训练好的模型进行压缩降低模型的复杂度或者数据的精度减少模型的参数以适应硬件的限制。
模型部署到运行环境中执行推理推理的时延、内存占用、功耗等是影响用户使用的关键因素优化模型推理的方式有两种一是设计专有的机器学习的芯片相对于通用的计算芯片这些专有芯片一般在能效比上具有很大的优势。二是通过软硬协同最大程度地发挥硬件的能力。对于第二种方式以CPU为例如何切分数据块以满足cache大小如何对数据进行重排以便计算时可以连续访问如何减少计算时的数据依赖以提升硬件流水线的并行如何使用扩展指令集以提升计算性能这些都需要针对不同的CPU架构进行设计和优化。
对于一个企业来讲,模型是属于重要的资产,因此,在模型部署到运行环境以后,保护模型的安全至关重要。本章节会介绍如模型混淆等一些常见的机器学习模型的安全保护手段。
- **模型压缩**
通过量化、剪枝等手段减小模型体积以及计算复杂度的技术,可以分为需要重训的压缩技术和不需要重训的压缩技术两类。
- **算子融合**
通过表达式简化、属性融合等方式将多个算子合并为一个算子的技术,融合可以降低模型的计算复杂度及模型的体积。
- **常量折叠**
将符合条件的算子在离线阶段提前完成前向计算,从而降低模型的计算复杂度和模型的体积。常量折叠的条件是算子的所有输入在离线阶段均为常量。
- **数据排布**
根据后端算子库支持程度和硬件限制,搜索网络中每层的最优数据排布格式,并进行数据重排或者插入数据重排算子,从而降低部署时的推理时延。
- **模型混淆**
xxx。

View File

@@ -0,0 +1,186 @@
## 模型推理
训练模型经过前面的转换、压缩等流程后,需要部署在计算硬件上进行推理。执行推理主要包含以下步骤:
- 前处理:将原始数据处理成适合网络输入的数据。
- 执行推理:将离线转换得到的模型部署到设备上执行推理流程,根据输入数据计算得到输出数据。
- 后处理:模型的输出结果做进一步的加工处理,如筛选阈值。
### 前处理与后处理
#### 前处理
前处理主要完成数据预处理在现实问题中我们得到的原始数据往往非常混乱机器学习模型无法识别并从中提取信息。数据预处理的目的是将原始数据例如图片、语音、文本等处理成适合网络输入的tensor数据并消除其中无关的信息恢复有用的真实信息增强有关信息的可检测性最大限度地简化数据从而改进模型的特征抽取、图像分割、匹配和识别等可靠性。
常见的数据预处理手段有:
- 特征编码将描述特征的原始数据编码成数字输入给机器学习模型因为它们只能处理数字数据。常见的编码方法有离散化、序号编码、One-hot编码二进制编码等
- 数据归一化修改数据的值使其达到共同的标度但不改变它们之间的相关性消除数据指标之间的量纲影响。常用的技术有Min-Max归一化将数据缩放到给定范围Z-score归一化使数据符合正态分布
- 处理离群值: 离群值是与数据中的其他值保持一定距离的数据点,适当地排除离群值可以提升模型的准确性。
针对特定的原始数据往往存在特定的数据处理手段。在前述8.2"机器学习数据基本类型及常见数据变换方式"章节中,分别详细介绍了图像、音频、文本等数据的预处理方法。
#### 后处理
通常,模型推理结束后,需要把推理的输出数据传递给用户完成后处理,常见的数据后处理手段有:
- 连续数据离散化:模型实际用于预测离散数据,例如商品数量时,用回归模型预测得到的是连续值,需要四取五入、取上下限阈值等得到实际结果;
- 数据可视化:将数据图形化、表格化,便于找到数据之间的关系,来决定下一步的分析策略;
- 手动拉宽预测范围:回归模型往往预测不出很大或很小的值,结果都集中在中部区域。例如医院的化验数据,通常是要根据异常值诊断疾病。手动拉宽预测范围,将偏离正常范围的值乘一个系数,可以放大两侧的数据,得到更准确的预测结果。
### 并行计算
:label:`ch08-sec-parallel_inference`
为提升推理的性能,需要重复利用多核的能力,所以一般推理框架会引入多线程机制。主要的思路是将算子的输入数据进行切分,通过多线程去执行不同数据切片,实现算子并行计算,从而成倍提升算子计算性能。
![矩阵乘数据切分](../img/ch08/parallel.png)
:width:`800px`
:label:`ch08-fig-parallel`
如图所示对于矩阵乘可以按左矩阵的行进行切分可以利用三个线程分别计算A1\*BA2\*BA3\*B实现矩阵乘多线程并行计算。
为方便算子并行计算,同时避免频繁创建销毁线程的开销,推理框架一般会使用线程池机制。业界有两种较为通用的做法:
- 使用OpenMp编程接口OpenMPOpen Multi-Processing是一套支持跨平台共享内存方式的多线程并发的编程API如算子并行最常用的接口\"parallel for\"实现for循环体的代码被多线程并行执行。
- 推理框架实现针对算子并行计算的线程池相对OpenMp提供的接口会更有针对性性能会更高且更轻量。
### 算子优化
:label:`ch08-sec-kernel_optimization`
在部署AI模型时我们期望模型执行训练或推理的时间尽可能地短以获得更优越的性能。对于一个固定的深度学习网络框架调度的时间占比往往很小性能的瓶颈就在算子的执行。下面从硬件指令和算法角度介绍一些算子优化的方法。
#### 硬件指令优化
绝大多数的设备上都有CPU因此算子在CPU上的时间尤为重要下面介绍一下在ARM
CPU硬件指令优化的方法。
1\. 汇编语言
开发者使用的C++、Java等高级编程语言会通过编译器输出为机器指令码序列而高级编程语言能做的事通常受编译器所限汇编语言是靠近机器的语言可以一对一实现任何指令码序列编写的程序存储空间占用少、执行速度快、效率优于高级编程语言。
在实际应用中,最好是程序的大部分用高级语言编写,运行性能要求很高的部分用汇编语言来编写,通过混合编程实现优势互补。深度学习的卷积、矩阵乘等算子涉及大量的计算,使用汇编语言能够给模型训练和推理性能带来数十到数百倍量级的提升。
下面以ARMv8系列处理器为例介绍和硬件指令相关的优化。
2\. 寄存器与NEON指令
ARMv8系列的CPU上有32个NEON寄存器v0-v31如 :numref:`ch08-fig-register`所示NEON寄存器v0可存放128bit的数据即4个float328个float1616个int8等。
![ARMv8处理器NEON寄存器v0的结构](../img/ch08/register.png)
:width:`500px`
:label:`ch08-fig-register`
针对该处理器可以采用SIMD(Single InstructionMultiple Data单指令、多数据)提升数据存取计算的速度。相比于单数据操作指令NEON指令可以一次性操作NEON寄存器的多个数据。例如对于浮点数的fmla指令用法为fmla v0.4s, v1.4s, v2.4s,如 :numref:`ch08-fig-fmla`所示用于将v1和v2两个寄存器中相对应的float值相乘累加到v0的值上。
![fmla指令计算功能](../img/ch08/fmla.png)
:width:`600px`
:label:`ch08-fig-fmla`
3\. 汇编语言优化
对于已知功能的汇编语言程序来说,计算类指令通常是固定的,性能的瓶颈就在非计算指令上。如 :numref:`ch08-fig-storage`所示计算机各存储设备类似于一个金字塔结构最顶层空间最小但是速度最快最底层速度最慢但是空间最大。L1-L3统称为cache(高速缓冲存储器)CPU访问数据时会首先访问位于CPU内部的cache没找到再访问CPU之外的主存此时引入了缓存命中率的概念来描述在cache中完成数据存取的占比。要想提升程序的性能缓存命中率要尽可能的高。
下面简单列举一些提升缓存命中率、优化汇编性能的手段:
1循环展开尽可能使用更多的寄存器以代码体积换性能
2指令重排打乱不同执行单元的指令以提高流水线的利用率提前有延迟的指令以减轻延迟减少指令前后的数据依赖等
3寄存器分块合理分块NEON寄存器减少寄存器空闲增加寄存器复用
4计算数据重排尽量保证读写指令内存连续提高缓存命中率
5使用预取指令将要使用到的数据从主存提前载入缓存减少访问延迟。
#### 算法优化
多数AI模型的推理时间主要耗费在卷积、矩阵乘算子的计算上占到了整网百分之九十甚至更多的时间。本小节主要介绍卷积算子算法方面的优化手段可以应用到各种硬件设备上。
卷积的计算可以转换为两个矩阵相乘在前述5.3.3小节中已经详细介绍了矩阵乘GEMM运算的优化。对于不同的硬件确定合适的矩阵分块优化数据访存与指令并行可以最大限度的发挥硬件的算力提升推理性能。
1.Img2col
将卷积的计算转换为矩阵乘一般采用Img2col的方法实现。在常见的神经网络中卷积的输入通常都是4维的默认采用的数据排布方式为NHWC如 :numref:`ch08-fig-conv_nhwc`所示是一个卷积示意图。输入维度为1,IH,IW,IC卷积核维度为OC,KH,KW,IC输出维度为1,OH,OW,OC
![通用卷积示意图](../img/ch08/conv_nhwc.png)
:width:`800px`
:label:`ch08-fig-conv_nhwc`
对卷积的Img2col规则如下。如 :numref:`ch08-fig-img2col_input`所示对该输入做重排得到的矩阵见右侧行数对应输出的OH\*OW的个数每个行向量里先排列计算一个输出点所需要输入上第一个通道的KH\*KW个数据再按次序排列之后的通道直到通道IC。
![输入Img2col的矩阵](../img/ch08/img2col_input.png)
:width:`800px`
:label:`ch08-fig-img2col_input`
如 :numref:`ch08-fig-img2col_weight`所示对权重数据做重排。将1个卷积核展开为权重矩阵的一列因此共有OC列每个列向量上先排列第一个输入通道上KH\*KW的数据再依次排列后面的通道直到IC。通过重排卷积的计算就可以转换为两个矩阵相乘的求解。在实际实现时Img2col和GEMM的数据重排会同时进行以节省运行时间。
![卷积核Img2col的矩阵](../img/ch08/img2col_weight.png)
:width:`600px`
:label:`ch08-fig-img2col_weight`
2.Winograd算法
卷积计算归根到底是矩阵乘法,两个二维矩阵相乘的时间复杂度是$O(n^3)$。我们可以使用Winograd来降低矩阵乘法的复杂度。
以一维卷积运算为例记为F(mr)其中m代表输出的个数r为卷积核的个数。输入为$d=[d_0 \ d_0 \ d_2 \ d_3]$,卷积核为$g=[g_0 \ g_0 \ g_2]^T$,该卷积计算可以写成矩阵形式如公式 :numref:`ch08-equ-conv_matmul_one_dimension`所示需要6次乘法和4次加法。
$$F(2, 3)=
\left[ \begin{matrix} d_0 & d_0 & d_2 \\ d_1 & d_2 & d_3 \end{matrix} \right] \left[ \begin{matrix} g_0 \\ g_1 \\ g_2 \end{matrix} \right]=
\left[ \begin{matrix} y_0 \\ y_1 \end{matrix} \right]$$
:label:`ch08-equ-conv_matmul_one_dimension`
可以观察到,卷积运算转换为矩阵乘法时输入矩阵中存在着重复元素$d_1$和$d_2$,因此,卷积转换的矩阵乘法相对一般的矩阵乘有了优化空间。可以通过计算中间变量$m_0-m_3$得到矩阵乘的结果,见公式 :numref:`ch08-equ-conv-2-winograd`
$$F(2, 3)=
\left[ \begin{matrix} d_0 & d_0 & d_2 \\ d_1 & d_2 & d_3 \end{matrix} \right] \left[ \begin{matrix} g_0 \\ g_1 \\ g_2 \end{matrix} \right]=
\left[ \begin{matrix} m_0+m_1+m_2 \\ m_1-m_2+m_3 \end{matrix} \right]$$
:label:`ch08-equ-conv-2-winograd`
其中,$m_0-m_3$的分别见公式 :numref:`ch08-equ-winograd-param`
$$\begin{aligned}
m_0=(d_0-d_2)*g_0 \\
m_1=(d_1+d_2)*(\frac{g_0+g_1+g_2}{2}) \\
m_2=(d_0-d_2)*(\frac{g_0-g_1+g_2}{2}) \\
m_2=(d_1-d_3)*g_2
\end{aligned}$$
:label:`ch08-equ-winograd-param`
通过$m_0-m_3$间接计算r1r2需要的运算次数包括输入d的4次加法输出m的4次乘法和4次加法。在推理阶段权重的数值是常量因此卷积核上的运算可以在图编译阶段计算不计入在线的run时间。所以总的运算次数为4次乘法和8次加法与直接运算的6次乘法和4次加法相比乘法次数减少加法次数增加。在计算机中乘法一般比加法慢通过减少乘法次数增加少量加法可以实现加速。
计算过程写成矩阵形式如公式 :numref:`ch08-equ-winograd-matrix`所示其中⊙为对应位置相乘A、B、G都是常量矩阵。这里写成矩阵计算是为了表达清晰实际使用时按照公式 :numref:`ch08-equ-winograd-param`手写展开的计算速度更快。
$$\mathbf{Y}=\mathbf{A^T}(\mathbf{G}g)*(\mathbf{B^T}d)$$
:label:`ch08-equ-winograd-matrix`
$$\mathbf{B^T}=
\left[ \begin{matrix} 1 & 0 & -1 & 0 \\ 0 & 1 & 1 & 0 \\ 0 & -1 & 1 & 0 \\ 0 & 1 & 0 & -1 \end{matrix} \right]$$
:label:`ch08-equ-winograd-matrix-bt`
$$\mathbf{G}=
\left[ \begin{matrix} 1 & 0 & 0 \\ 0.5 & 0.5 & 0.5 \\ 0.5 & -0.5 & 0.5 \\ 0 & 0 & 1 \end{matrix} \right]$$
:label:`ch08-equ-winograd-matrix-g`
$$\mathbf{A^T}=
\left[ \begin{matrix} 1 & 1 & -1 & 0 \\ 0 & 1 & -1 & -1 \end{matrix} \right] \\$$
:label:`ch08-equ-winograd-matrix-at`
通常深度学习领域通常使用的都是2D卷积将F(23)扩展到F(2x23x3),可以写成矩阵形式,如公式 :numref:`ch08-equ-winograd-two-dimension-matrix`所示。此时Winograd算法的乘法次数为16而直接卷积的乘法次数为36降低了2.25倍的乘法计算复杂度。
$$\mathbf{Y}=\mathbf{A^T}(\mathbf{G}g\mathbf{G^T})*(\mathbf{B^T}d\mathbf{B})\mathbf{A}$$
:label:`ch08-equ-winograd-two-dimension-matrix`
Winograd算法的整个计算过程在逻辑上可以分为4步如 :numref:`ch08-fig-winograd`所示:
![winograd步骤示意图](../img/ch08/winograd.png)
:width:`500px`
:label:`ch08-fig-winograd`
针对任意的输出大小要使用F(2x23x3)的Winograd算法需要将输出切分成2x2的块找到对应的输入按照上述的四个步骤就可以求出对应的输出值。当然Winograd算法并不局限于求解F(2x23x3)针对任意的F(m\*mr\*r)都可以找到适当的常量矩阵A、B、G通过间接计算的方式减少乘法次数。但是随着m、r的增大输入、输出涉及的加法以及常量权重的乘法次数都在增加那么乘法次数带来的计算量下降会被加法和常量乘法所抵消。因此在实际使用场景中还需要根据Winograd的实际收益来选择。
本小节主要介绍了模型推理时的数据处理和性能优化手段。选择合适的数据处理方法,可以更好地提取输入特征,处理输出结果。并行计算以及算子级别的硬件指令与算法优化可以最大限度的发挥硬件的算力。除此之后,内存的占用及访问速率也是影响推理性能的重要因素,因此推理时需要设计合理的内存复用策略,关于内存复用的策略我们在编译器后端章节已经做了阐述。

View File

@@ -0,0 +1,7 @@
## 模型的安全保护
### 概述
### 模型混淆
### 模型加密

View File

@@ -0,0 +1,13 @@
## 总结
- 不同的模型部署场景下,通常对于模型大小、运行时内存占用、推理时延和推理功耗等指标有限制。
- 针对模型大小指标,通常在离线阶段通过模型压缩技术来优化,比如量化技术、剪枝技术、知识蒸馏技术等,除此之外,一部分模型优化技术,比如融合技术 :numref:`ch08-sec-fusion`等,也有助于模型轻量化,不过其效果比较微弱。
- 针对运行时内存指标,主要有三方面的优化:优化模型大小、优化部署框架包大小以及优化运行时临时内存。模型大小的优化手段在上一点中已经说明;部署框架包大小主要通过精简框架代码、框架代码模块化等方式来优化。运行时临时内存主要通过内存池实现内存之间的复用来优化,这部分可以参见 :numref:`ch05-sec-memory_pool`
- 针对模型的推理时延指标,主要有两方面的优化,一方面是离线时通过模型优化技术 :numref:`ch08-sec-fusion`和模型压缩技术 :numref:`ch08-sec-model_compression`尽可能降低模型推理所需的计算量;另一方面是通过加大推理的并行力度 :numref:`ch08-sec-parallel_inference`和优化算子实现 :numref:`ch08-sec-kernel_optimization`来充分挖掘硬件的计算潜力。值得注意的是,除了考虑计算量和算力,推理时的访存开销也是一个重要的影响因素,这一点在 :numref:`ch08-sec-fusion`小节和 :numref:`ch08-sec-kernel_optimization`小节中进行了相关优化。
- 针对模型的推理功耗,主要的优化思路是降低模型的计算量,这与针对模型推理时延的优化手段有重合之处,可以参考离线的模型优化技术 :numref:`ch08-sec-fusion`和模型压缩技术 :numref:`ch08-sec-model_compression`
- 本章除了介绍优化模型部署的各方面指标的优化技术以外,还介绍了安全部署相关的技术,如模型混淆、模型加密等。部署安全一方面可以保护企业的重要资产,另一方面可以防止黑客通过篡改模型从而入侵攻击部署环境。

View File

@@ -26,7 +26,7 @@
### 环境配置
在构建机器学习工作流程前MindSpore需要通过context.set_context来配置运行需要的信息如运行模式、后端信息、硬件等信息。
导入context模块配置运行需要的信息。
导入context模块配置运行需要的信息。以下代码运行环境为Ubuntu16.04CUDA10.1MindSpore1.5.2。
```python
import os
@@ -94,7 +94,7 @@ def create_dataset(data_path, batch_size=32, repeat_size=1,
# 导入需要用到的模块
import mindspore.nn as nn
# 定义线性模型
class MLPNet(nn.Module):
class MLPNet(nn.Cell):
def __init__(self):
super(MLPNet, self).__init__()
self.flatten = nn.Flatten()
@@ -175,20 +175,32 @@ train_net(args, model, train_epoch, mnist_path, dataset_size, ckpoint, False)
测试是模型运行测试数据集得到的结果通常在训练过程中每训练一定的数据量后就会测试一次以验证模型的泛化能力。MindSpore使用model.eval接口读入测试数据集。
```python
def test_net(network, model, data_path):
def test_net(model, data_path):
"""定义验证的方法"""
ds_eval = create_dataset(os.path.join(data_path, "test"))
acc = model.eval(ds_eval, dataset_sink_mode=False)
print("{}".format(acc))
# 验证模型精度
test_net(model, mnist_path)
```
在训练完毕后参数保存在checkpoint中可以将训练好的参数加载到模型中进行验证。
```python
import numpy as np
from mindspore import Tensor
from mindspore import load_checkpoint, load_param_into_net
# 定义测试数据集batch_size设置为1则取出一张图片
ds_test = create_dataset(os.path.join(mnist_path, "test"), batch_size=1).create_dict_iterator()
data = next(ds_test)
# images为测试图片labels为测试图片的实际分类
images = data["image"].asnumpy()
labels = data["label"].asnumpy()
# 加载已经保存的用于测试的模型
param_dict = load_checkpoint("checkpoint_lenet-1_1875.ckpt")
# 加载参数到网络中
load_param_into_net(net, param_dict)
# 使用函数model.predict预测image对应分类
output = model.predict(Tensor(data['image']))
# 输出预测分类与实际分类
print(f'Predicted: "{predicted[0]}", Actual: "{labels[0]}"')
```

View File

@@ -0,0 +1,47 @@
# 第二部分:进阶篇
第一部分基础篇介绍了机器学习系统的基础,首先把机器学习系统作为一个黑盒系统,介绍机器学习系统的使用场景和主要需求,机器学习系统提供给用户的编程接口。以及一个典型的机器学习系统中的计算图构建、生成和调度的过程和主要技术。第二部分进阶篇,将站在系统设计的角度,思考在设计现代机器学习系统中需要考虑的问题和解决方案,详细的技术方案介绍在后面的章节会具体展开,本节重点介绍机器学习系统的总体架构以及进阶篇各章节之间的关系。
机器学习系统面临的挑战可以从如下四个方面的变化趋势来看如图所示横向表示的是模型算法研究生产和部署的过程纵向表示的是AI系统支撑的模型算法应用以及支持AI系统的芯片硬件
1. 面向算法开发者怎样提高算法开发的效率并兼顾运行性能怎样让算法开发者减少学习成本随心所欲的表达算法思想等一系列问题是机器学习系统需要考虑的问题。按照构建计算图的方式AI框架分为了静态图和动态图两种类型静态图在执行前先进行构图和编译优化需要使用系统提供的构图API进行构图其表达受限于API的灵活和丰富程度但由于静态图先编译后执行执行期性能较高动态图则是边执行边构图符合算法开发人员的使用习惯由于直接使用Python生态可以做到会Python就会写算法动态图逐语句执行调试方便算法开发效率很高但动态图的执行效率一般情况下不如静态图。
2. 面向部署怎样将AI模型算法部署到每个设备、每个应用、每个行业由于各个设备的算力和资源属性差别巨大尤其是端侧和边缘侧的某些设备硬件资源极其受限机器学习系统本身和模型大小和能效比有严苛的要求以智能手表为例其要求框架严格限制在100KB以内能效/功耗比要能支持较长的待机时间要求。而另一方面AI模型越来越大即使部署在端侧的模型也动辄几十MB占用大量的内存、计算和通信资源。因此机器学习系统需要解决跨系统、跨设备、极致轻量化部署的问题。
3. 面向算法和数据从计算规模看AI模型规模呈现指数级增长2021年模型规模已经达到十万亿参数预计很快会增长到百万亿参数怎么应对模型越来越大的挑战从计算范式看如何处理不断涌现的新的计算范式。机器学习系统过去是由机器学习、深度学习为主的未来会不会支持其他的负载比如科学计算、数据分析、图形计算等这样可以最大化的利用资源和提高开发易用性。
4. 面向硬件如果把AI加速器按照GPU、多核/众核、DataFlow架构来进行分类的话DataFlow的架构占比一半左右Dataflow架构虽然在能效比上有很大的优势但是加大了可编程性和性能优化的难度如何充分发挥芯片的性能进行极致的性能优化
![影响机器学习系统架构的因素](../img/Advanced/preface3_1.png)
上述四个方面的不同需求在设计AI框架系统的时候需要基于场景充分的予以考虑另外还需要考虑
通用性:也可以成为泛化性,是不是所有的模型算法同一套代码,没有针对某个网络的特殊定制代码? 是不是所有硬件同一套机制,在机器学习系统中针对特定硬件版本的定制只存在于硬件相关层?
易用性:对新用户而言,易用性关注更多的是入门的门槛,能不能一键式的安装、升级和运行常见的模型;对深度用户,如:算法研究人员而言,是不是能够轻松的表达算法、调试算法和部署算法模型是易用性的重点。另外,生态兼容性是易用性的一个重要考量,方便的使用常用的工具、第三方库兼容和对接,支持更多的硬件进行训练和部署是重要的因素。
性能追求性价比、性能功耗比、大规模集群的性能加速比是机器学习系统性能的主要衡量指标MLPerf、AIPerf榜单主要比拼的是典型网络在不同系统上的综合性能指标机器学习系统中提供的系统优化机制对性能起到了重要作用。
在机器学习系统的架构设计中,采用三层架构来应对这些挑战和需要考虑的问题。 下图是MindSpore的总体架构图就是以AI编译器为核心的三层架构设计。第一层MindExpression表示层提供灵活的模型/算法表示考到到生态兼容性采用Python作为主要的编程语言后续也可以提供其他编程语言的扩展如Julia、仓颉等编程语言。支持常见的第三库如Numpy、Scipy等数据处理支持常用的数据处理库opencv的API等第二层MindCompilerAI编译器负责机器学习系统的编译优化包括自动微分、硬件无关的优化硬件相关的优化分布式系统相关的优化等都在AI编译器层完成经过这一层后用户表达的算法和模型转换成特定硬件上的高效执行的机器代码前端负责静态分析、类型推导以及自动微分、分布式并行子图拆分等PASS优化后端负责硬件相关的优化内存优化、图算融合等。在第二层可以看到有MindData数据处理框架主要是因为在系统中所处的层次和MindCompiler相同关系也比较密切。数据处理框架主要是提供数据处理格式、数据处理加速和数据处理相关的保序、分布式缓存等操作。由于数据处理和AI训练是在同一套硬件集群系统中需要根据训练和数据处理的负载消耗情况来进行资源分配有时候也会把数据处理相关操作在AI计算中构成一张DAG图进行调度。第三层MindRT运行时提供不同部署环境下通用的分布式异构并行调度机制、内存分配等对于不同的硬件使用不同的backend的设计方式。详细的介绍再后面的章节中会展开讨论在本部分重点介绍设计的思想和章节之间的关系。
![preface3_arc](../img/Advanced/preface3_arc.png "MindSpore系统架构")
既要对上承接模型算法的变化,满足算法开发者研究不断探索的诉求, 又要在最终的二进制输出上满足多样性硬件的诉求,满足不同部署环境的资源要求。既要满足系统的通用,也要满足易用性的灵活性要求,还要满足性能的不断优化诉求,这里引入编译器的概念再合适不过了。 编译器概念可以很好抽象上面提到的挑战和问题编译器的架构和机器学习系统非常吻合。编译器输入的是用户编程代码输出的是机器执行的高效代码编译器的作用主要是转换和优化这和机器学习系统的输入输出机器学习的目标是完全一致的。所以在进阶篇我们将用两个章节详细介绍AI编译器里面的很多概念是和通用编译器中的概念是相同的比如AOTAhead of Time提前编译、JITJust in time、IR中间表示、PASS优化、ASTAbstract Struct Trees、副作用、闭包等概念对编译器相关概念需要了解的读者可以翻阅相关的编译原理教材了解。
AI编译器是一种相对较新的概念和工具一个强大的AI编译器将让算法科学家和开发人员享受其带来的益处包括表达的便捷和执行的高性能是AI框架设计的核心。为了更好的理解AI编译器架构先从如下图传统编译器LLVM架构说起随着越来越多的编程语言和多种不同架构的算力硬件的支持 LLVM IR为不同的前端编程语言和硬件架构提供了一个桥梁先把前端不同的编程语言映射成统一的LLVM IR再把LLVM IR映射到不同的后端这样可以大大简化编译器的开发不同的前端不同的硬件都可以尽可能的共享LLVM IR和其上的基础设施和编译优化。在这里可以把LLVM编译器分成三个部分前端、IR和后端。前端将高级语言转换成IR后端将IR转换成目标硬件上的机器指令IR作为桥梁在前后端之间进行基于IR的各种优化。这样无论是新增硬件的支持还是新的前端支持都可以尽可能的复用IR相关的部分。IR可以是单层的IR也可以是多层的IR LLVM IR是典型的单层的IR前后端优化都基于相同的LLVM IR进行。
![影响机器学习系统架构的因素](../img/Advanced/preface3_2.png "LLVM IR的架构")
在AI编译器领域往往采用多层级逐步优化的方式对应的IR也是多层级IR的。TensorFlow等AI框架就是采用多层级IR来进行设计的MindSpore早期版本也是使用多层次IR的下图就是MLIR官方材料中给出的TensorFlow框架当前的IR现状中间至少有三个层次的IRTensorFlow Graph IR XLA Accelerated Linear AlgebraHLO、 以及特定硬件的LLVM IR 或者TPU IR下面就不同的层级IR和其上的编译优化做一个简要介绍。在前一章节计算图部分提到的图优化也称为图编译优化主要实现整图级别的优化和操作 图优化、图切分往往是基于Graph IR这个层次进行的比较适合静态图的高效执行模式由于整图级别的IR缺少相应的硬件信息难以进行硬件相关的优化所以在中间层次就出现了相关的硬件通用编译优化XLA、TensorRT、MindSpore的图算融合等都属于这一类编译器来针对不同的硬件进行算子融合提升网络在特定硬件上的执行性能。在本书的第五章节编译器后端中硬件通用优化中有一个章节介绍图算融合编译器的相关设计。最后一个层次的IR是特定硬件加速器专有的IR一般由硬件厂商自带的编译器提供Ascend硬件自带的TBE编译器就是基于TVM IR生成高效的执行算子的在本书的第6章加速器章节也会有相应章节介绍算子编译器。
![影响机器学习系统架构的因素](../img/Advanced/preface3_3.png "图. MLIR架构")
多层级IR的优势是IR表达上更加地灵活可以在不同层级的IR上进行合适的PASS优化更加方便优化算法也更加地高效但是多层级IR也有一些劣势。首先多层级IR必然需要进行IR之间的转换IR转换要做到兼容非常困难工程工作量很大IR的转换除了工程的工作量比较大以外可能会带来信息的损失 由于上一层IR的某些信息缺失给下一层优化带来困难由于会造成信息丢失对优化的影响因此对优化执行的顺序也有更强的约束。其次多层级IR有些优化既可以在上一层IR进行也可以在下一层IR进行让系统开发者很难选择。最后不同层级IR定义的算子粒度大小不同可能给给精度带来一定的影响。所以在MindSpore的架构设计中采用了统一的MindIR设计如下图所示详细给出了AI编译器内部的运行流程和分工所以本书中编译器前端主要是指图编译和硬件无关的优化编译器后端主要是指硬件相关优化、算子选择等。
![影响机器学习系统架构的因素](../img/Advanced/preface3_4.png "图. MindSpore AI编译器分层架构")
性能除了在AI领域有一个很重要的需求就是要尽可能和AI硬件结合起来发挥AI硬件的算力性能。当前AI硬件除了常见的CPU、GPU外各种DSA架构的加速器硬件如华为推出的Ascend昇腾AI加速器是不可忽视的AI硬件的主打硬件。在第6章加速器部分首先对加速器硬件的架构进行介绍理解加速器的编程手段和编程接口在机器学习系统中就可以更好的使用加速器。
数据是机器学习的生命线在训练计算中有30%左右的计算时间用于数据处理如果算上一个完整的机器学习模型训练包括从公开的数据集中预处理数据等占比时间比例更高。ML模型数据处理需要提供灵活的数据处理能力包括接口和自定义算子的能力也需要高性能的处理包括数据处理加速和分布式数据处理等在本书第7章节数据处理框架部分进行了介绍。
训练后的模型需要部署到各种形态的终端设备中将智慧赋能到各行各业。当前AI部署到企业的比例还不到20%,模型部署存在着两大方面的挑战一方面资源受限设备的CPU处理能力不强内存和硬盘空间都很小同时对模型执行的功耗、精度和时延开销又有严苛的指标要求在此场景下模型部署遇到了性能、功耗和大小的挑战另一方面超大模型部署对算力消耗较大需要进行分布式部署。而很多大企业往往采用统一的模型部署的云服务需要一套服务接口提供对外服务机器学习系统需要能适配不同的模型不同的数据处理方法对接不同的硬件后端提升云端部署的效率、成本和吞吐率。本书第8章节模型部署将从模型部署的一般流程出发详细的介绍在模型部署到手机等端侧设备上需要进行的模型转换、模型优化和模型压缩的相关技术。对于分布式场景下模型部署相关的技术进行介绍。模型部署也可以转换到和模型训练相同的IR上进行算子融合、代数化简以及硬件相关的优化共享AI编译器相关的技术设施采用和训练相同的IR可以保持训练和推理的精度一致性。
机器学习系统中单机系统包括编译优化、加速器加速、模型部署和数据处理执行流程如下图所示。随着模型的越来越大AI训练单机系统已经不能满足需求大型模型的训练需要使用到AI集群来进行训练2021年盘古大模型的发布就提到使用了2048个训练节点的超大规模的昇腾计算集群。在超大模型训练中如果Scale out模型训练到集群上需要机器学习系统支持数据并行、模型并行、流水线并行、优化器并行的能力同时允许多种策略灵活的组合使用。由于大规模集群的系统配置的复杂度很高无论对于算法人员还是系统人员配置这些策略的门槛都比较高需要机器学习系统提供半自动和自动的策略支持适配不同模型、不同规模的集群的灵活的分布式并行策略这些技术将在第9章节分布式训练进行介绍。
![影响机器学习系统架构的因素](../img/Advanced/preface3_5.png "图. MindSpore AI编译器分层架构")

BIN
img/Advanced/preface3_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
img/Advanced/preface3_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
img/Advanced/preface3_3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
img/Advanced/preface3_4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
img/Advanced/preface3_5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@@ -0,0 +1,342 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
version="1.1"
id="svg1165"
xml:space="preserve"
width="357.36581"
height="184.40012"
viewBox="0 0 357.36581 184.40012"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs1169"><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1179"><path
d="M 0,1.2207e-4 H 960 V 540.00012 H 0 Z"
clip-rule="evenodd"
id="path1177" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1191"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1189" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1205"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1203" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1219"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1217" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1231"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1229" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1243"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1241" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1257"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1255" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1269"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1267" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1281"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1279" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1293"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1291" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1313"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1311" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1327"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1325" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1339"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1337" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1351"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1349" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1363"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1361" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1375"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1373" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1397"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1395" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1409"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1407" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1421"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1419" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1433"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1431" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1445"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1443" /></clipPath></defs><g
id="g1171"
transform="matrix(1.3333333,0,0,-1.3333333,-434.15916,476.41082)"><path
d="m 373.24,346.57 -13.71,-12.39 0.67,-0.74 13.71,12.39 z m -14.64,-9.87 -2.44,-6.25 6.46,1.8 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1183" /><g
id="g1185"><g
id="g1187"
clip-path="url(#clipPath1191)"><text
transform="matrix(1,0,0,-1,374.54,350.23)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1195"><tspan
x="0"
y="0"
id="tspan1193"></tspan></text></g></g><path
d="M 345.17,316.41 334.89,305 l 0.75,-0.67 10.27,11.42 z m -11.47,-8.99 -1.78,-6.47 6.24,2.46 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1197" /><g
id="g1199"><g
id="g1201"
clip-path="url(#clipPath1205)"><text
transform="matrix(1,0,0,-1,344.88,318.31)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1209"><tspan
x="0"
y="0"
id="tspan1207">×</tspan></text></g></g><path
d="m 385.54,346.69 13.71,-12.52 -0.68,-0.74 -13.71,12.52 z m 14.66,-10 2.4,-6.26 -6.45,1.83 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1211" /><g
id="g1213"><g
id="g1215"
clip-path="url(#clipPath1219)"><text
transform="matrix(1,0,0,-1,399.48,318.72)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1223"><tspan
x="0"
y="0"
id="tspan1221">×</tspan></text></g></g><g
id="g1225"><g
id="g1227"
clip-path="url(#clipPath1231)"><text
transform="matrix(1,0,0,-1,325.01,286.78)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1235"><tspan
x="0"
y="0"
id="tspan1233">a</tspan></text></g></g><g
id="g1237"><g
id="g1239"
clip-path="url(#clipPath1243)"><text
transform="matrix(1,0,0,-1,364.97,286.78)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1247"><tspan
x="0"
y="0"
id="tspan1245">5</tspan></text></g></g><path
d="m 353.18,315.93 9.73,-11.06 -0.75,-0.66 -9.74,11.06 z m 10.94,-8.66 1.72,-6.48 -6.22,2.52 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1249" /><g
id="g1251"><g
id="g1253"
clip-path="url(#clipPath1257)"><text
transform="matrix(1,0,0,-1,384.1,287.06)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1261"><tspan
x="0"
y="0"
id="tspan1259">×</tspan></text></g></g><g
id="g1263"><g
id="g1265"
clip-path="url(#clipPath1269)"><text
transform="matrix(1,0,0,-1,418.46,288.46)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1273"><tspan
x="0"
y="0"
id="tspan1271">b</tspan></text></g></g><g
id="g1275"><g
id="g1277"
clip-path="url(#clipPath1281)"><text
transform="matrix(1,0,0,-1,366.65,253.85)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1285"><tspan
x="0"
y="0"
id="tspan1283">a</tspan></text></g></g><g
id="g1287"><g
id="g1289"
clip-path="url(#clipPath1293)"><text
transform="matrix(1,0,0,-1,403.94,253.85)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1297"><tspan
x="0"
y="0"
id="tspan1295">5</tspan></text></g></g><path
d="m 403.13,315.93 -10.28,-11.41 0.75,-0.67 10.27,11.42 z m -11.47,-8.99 -1.78,-6.47 6.24,2.46 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1299" /><path
d="m 410.66,315.93 9.73,-11.06 -0.75,-0.66 -9.74,11.06 z m 10.94,-8.66 1.72,-6.48 -6.22,2.52 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1301" /><path
d="m 385.73,282.45 -10.28,-11.41 0.75,-0.67 10.27,11.42 z m -11.47,-8.99 -1.78,-6.47 6.24,2.46 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1303" /><path
d="m 393.74,282.09 9.73,-11.06 -0.75,-0.66 -9.74,11.06 z m 10.94,-8.66 1.71,-6.48 -6.21,2.52 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1305" /><g
id="g1307"><g
id="g1309"
clip-path="url(#clipPath1313)"><text
transform="matrix(1,0,0,-1,538.87,350.23)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1317"><tspan
x="0"
y="0"
id="tspan1315"></tspan></text></g></g><path
d="m 553.9,346.69 13.71,-12.52 -0.68,-0.74 -13.71,12.52 z m 14.66,-10 2.4,-6.26 -6.45,1.83 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1319" /><g
id="g1321"><g
id="g1323"
clip-path="url(#clipPath1327)"><text
transform="matrix(1,0,0,-1,570,317.95)"
style="font-variant:normal;font-weight:normal;font-size:12.024px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1331"><tspan
x="0"
y="0"
id="tspan1329">×</tspan></text></g></g><g
id="g1333"><g
id="g1335"
clip-path="url(#clipPath1339)"><text
transform="matrix(1,0,0,-1,540.02,288.82)"
style="font-variant:normal;font-weight:normal;font-size:12.024px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1343"><tspan
x="0"
y="0"
id="tspan1341">×</tspan></text></g></g><g
id="g1345"><g
id="g1347"
clip-path="url(#clipPath1351)"><text
transform="matrix(1,0,0,-1,586.8,288.46)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1355"><tspan
x="0"
y="0"
id="tspan1353">b</tspan></text></g></g><g
id="g1357"><g
id="g1359"
clip-path="url(#clipPath1363)"><text
transform="matrix(1,0,0,-1,524.54,253.9)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1367"><tspan
x="0"
y="0"
id="tspan1365">a</tspan></text></g></g><g
id="g1369"><g
id="g1371"
clip-path="url(#clipPath1375)"><text
transform="matrix(1,0,0,-1,561.84,253.9)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1379"><tspan
x="0"
y="0"
id="tspan1377">5</tspan></text></g></g><path
d="m 570.89,316.77 -10.28,-11.41 0.75,-0.67 10.27,11.42 z m -11.47,-8.99 -1.78,-6.47 6.24,2.46 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1381" /><path
d="m 579.02,315.93 9.73,-11.06 -0.75,-0.66 -9.74,11.06 z m 10.94,-8.66 1.71,-6.48 -6.21,2.52 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1383" /><path
d="m 543.53,282.57 -10.28,-11.41 0.75,-0.67 10.27,11.42 z m -11.47,-8.99 -1.78,-6.47 6.24,2.46 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1385" /><path
d="m 551.66,282.21 9.73,-11.06 -0.75,-0.66 -9.74,11.06 z m 10.94,-8.66 1.71,-6.48 -6.21,2.52 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1387" /><path
d="m 544.46,343.57 0.51,-34.6 -1,-0.02 -0.51,34.6 z m 2.99,-33.57 -2.91,-6.04 -3.08,5.96 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1389" /><g
id="g1391"><g
id="g1393"
clip-path="url(#clipPath1397)"><text
transform="matrix(1,0,0,-1,351.26,220.25)"
style="font-variant:normal;font-weight:normal;font-size:11.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1401"><tspan
x="0 6.3599601 11.39512"
y="0"
id="tspan1399">AST</tspan></text></g></g><g
id="g1403"><g
id="g1405"
clip-path="url(#clipPath1409)"><text
transform="matrix(1,0,0,-1,368.06,220.25)"
style="font-variant:normal;font-weight:normal;font-size:11.04px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1413"><tspan
x="0 11.04 22.08 33.119999 44.16"
y="0"
id="tspan1411">抽象语法树</tspan></text></g></g><g
id="g1415"><g
id="g1417"
clip-path="url(#clipPath1421)"><text
transform="matrix(1,0,0,-1,506.88,219.84)"
style="font-variant:normal;font-weight:normal;font-size:11.064px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1425"><tspan
x="0 6.826488 13.188288"
y="0"
id="tspan1423">DAG</tspan></text></g></g><g
id="g1427"><g
id="g1429"
clip-path="url(#clipPath1433)"><text
transform="matrix(1,0,0,-1,527.04,219.84)"
style="font-variant:normal;font-weight:normal;font-size:11.064px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1437"><tspan
x="0"
y="0"
id="tspan1435"></tspan></text></g></g><g
id="g1439"><g
id="g1441"
clip-path="url(#clipPath1445)"><text
transform="matrix(1,0,0,-1,538.1,219.84)"
style="font-variant:normal;font-weight:normal;font-size:11.064px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1449"><tspan
x="0 11.04 22.08 33.119999"
y="0"
id="tspan1447">向无环图</tspan></text></g></g></g></svg>

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -0,0 +1,321 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
version="1.1"
id="svg765"
xml:space="preserve"
width="573.44"
height="259.83206"
viewBox="0 0 573.44 259.83206"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs769"><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath779"><path
d="M 0,1.2207e-4 H 960 V 540.00012 H 0 Z"
clip-rule="evenodd"
id="path777" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath793"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path791" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath805"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path803" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath821"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path819" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath837"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path835" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath853"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path851" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath869"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path867" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath885"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path883" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath897"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path895" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath913"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path911" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath929"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path927" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath941"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path939" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath957"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path955" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath969"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path967" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath981"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path979" /></clipPath></defs><g
id="g771"
transform="matrix(1.3333333,0,0,-1.3333333,-279.35999,533.43999)"><g
id="g773"><g
id="g775"
clip-path="url(#clipPath779)" /></g><path
d="m 249.72,333.76 c 0,8.59 6.97,15.56 15.56,15.56 h 62.24 c 8.59,0 15.56,-6.97 15.56,-15.56 V 271.4 c 0,-8.59 -6.97,-15.56 -15.56,-15.56 h -62.24 c -8.59,0 -15.56,6.97 -15.56,15.56 z"
style="fill:#a5c3f4;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path783" /><path
d="m 249.72,333.76 c 0,8.59 6.97,15.56 15.56,15.56 h 62.24 c 8.59,0 15.56,-6.97 15.56,-15.56 V 271.4 c 0,-8.59 -6.97,-15.56 -15.56,-15.56 h -62.24 c -8.59,0 -15.56,6.97 -15.56,15.56 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path785" /><g
id="g787"><g
id="g789"
clip-path="url(#clipPath793)"><text
transform="matrix(1,0,0,-1,268.61,305.66)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text797"><tspan
x="0 4.8000002 10.776 17.148001 21.84 28.164 32.375999 37.883999 40.644001 47.004002"
y="0"
id="tspan795">TensorFlow</tspan></text></g></g><g
id="g799"><g
id="g801"
clip-path="url(#clipPath805)"><text
transform="matrix(1,0,0,-1,281.45,291.26)"
style="font-variant:normal;font-weight:normal;font-size:12.024px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text809"><tspan
x="0 7.5871439 11.518992 17.278488 23.627159"
y="0"
id="tspan807">Graph</tspan></text></g></g><path
d="m 419.04,377.82 c 0,2.15 1.75,3.9 3.9,3.9 h 67.44 c 2.15,0 3.9,-1.75 3.9,-3.9 v -15.6 c 0,-2.15 -1.75,-3.9 -3.9,-3.9 h -67.44 c -2.15,0 -3.9,1.75 -3.9,3.9 z"
style="fill:#a5c3f4;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path811" /><path
d="m 419.04,377.82 c 0,2.15 1.75,3.9 3.9,3.9 h 67.44 c 2.15,0 3.9,-1.75 3.9,-3.9 v -15.6 c 0,-2.15 -1.75,-3.9 -3.9,-3.9 h -67.44 c -2.15,0 -3.9,1.75 -3.9,3.9 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path813" /><g
id="g815"><g
id="g817"
clip-path="url(#clipPath821)"><text
transform="matrix(1,0,0,-1,436.1,365.93)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text825"><tspan
x="0 6.2280002 11.268 18.216 20.988001 28.427999 33.228001"
y="0"
id="tspan823">XLA HLO</tspan></text></g></g><path
d="m 419.04,344.1 c 0,2.15 1.75,3.9 3.9,3.9 h 67.44 c 2.15,0 3.9,-1.75 3.9,-3.9 v -15.6 c 0,-2.15 -1.75,-3.9 -3.9,-3.9 h -67.44 c -2.15,0 -3.9,1.75 -3.9,3.9 z"
style="fill:#a5c3f4;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path827" /><path
d="m 419.04,344.1 c 0,2.15 1.75,3.9 3.9,3.9 h 67.44 c 2.15,0 3.9,-1.75 3.9,-3.9 v -15.6 c 0,-2.15 -1.75,-3.9 -3.9,-3.9 h -67.44 c -2.15,0 -3.9,1.75 -3.9,3.9 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path829" /><g
id="g831"><g
id="g833"
clip-path="url(#clipPath837)"><text
transform="matrix(1,0,0,-1,433.1,332.18)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text841"><tspan
x="0 4.8000002 10.776 17.148001 21.84 28.164 32.375999 35.015999 41.375999"
y="0"
id="tspan839">Tensor RT</tspan></text></g></g><path
d="m 419.64,310.38 c 0,2.15 1.75,3.9 3.9,3.9 h 67.44 c 2.15,0 3.9,-1.75 3.9,-3.9 v -15.6 c 0,-2.15 -1.75,-3.9 -3.9,-3.9 h -67.44 c -2.15,0 -3.9,1.75 -3.9,3.9 z"
style="fill:#a5c3f4;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path843" /><path
d="m 419.64,310.38 c 0,2.15 1.75,3.9 3.9,3.9 h 67.44 c 2.15,0 3.9,-1.75 3.9,-3.9 v -15.6 c 0,-2.15 -1.75,-3.9 -3.9,-3.9 h -67.44 c -2.15,0 -3.9,1.75 -3.9,3.9 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path845" /><g
id="g847"><g
id="g849"
clip-path="url(#clipPath853)"><text
transform="matrix(1,0,0,-1,439.15,298.46)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text857"><tspan
x="0 6.348 13.92 17.868 23.615999 29.976"
y="0"
id="tspan855">nGraph</tspan></text></g></g><path
d="m 419.64,276.66 c 0,2.15 1.75,3.9 3.9,3.9 h 67.44 c 2.15,0 3.9,-1.75 3.9,-3.9 v -15.6 c 0,-2.15 -1.75,-3.9 -3.9,-3.9 h -67.44 c -2.15,0 -3.9,1.75 -3.9,3.9 z"
style="fill:#bfbfbf;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path859" /><path
d="m 419.64,276.66 c 0,2.15 1.75,3.9 3.9,3.9 h 67.44 c 2.15,0 3.9,-1.75 3.9,-3.9 v -15.6 c 0,-2.15 -1.75,-3.9 -3.9,-3.9 h -67.44 c -2.15,0 -3.9,1.75 -3.9,3.9 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path861" /><g
id="g863"><g
id="g865"
clip-path="url(#clipPath869)"><text
transform="matrix(1,0,0,-1,436.99,264.72)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text873"><tspan
x="0 6.3600001 12.684 16.799999 22.775999 25.440001 35.748001"
y="0"
id="tspan871">Core ML</tspan></text></g></g><path
d="m 407.04,242.94 c 0,2.15 1.75,3.9 3.9,3.9 h 92.76 c 2.15,0 3.9,-1.75 3.9,-3.9 v -15.6 c 0,-2.15 -1.75,-3.9 -3.9,-3.9 h -92.76 c -2.15,0 -3.9,1.75 -3.9,3.9 z"
style="fill:#a5c3f4;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path875" /><path
d="m 407.04,242.94 c 0,2.15 1.75,3.9 3.9,3.9 h 92.76 c 2.15,0 3.9,-1.75 3.9,-3.9 v -15.6 c 0,-2.15 -1.75,-3.9 -3.9,-3.9 h -92.76 c -2.15,0 -3.9,1.75 -3.9,3.9 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path877" /><g
id="g879"><g
id="g881"
clip-path="url(#clipPath885)"><text
transform="matrix(1,0,0,-1,419.33,230.98)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text889"><tspan
x="0 4.8000002 10.776 17.148001 21.84 28.164 32.375999 37.883999 40.644001 47.004002"
y="0"
id="tspan887">TensorFlow</tspan></text></g></g><g
id="g891"><g
id="g893"
clip-path="url(#clipPath897)"><text
transform="matrix(1,0,0,-1,477.65,230.98)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text901"><tspan
x="0 5.04 7.8000002 11.76"
y="0"
id="tspan899">Lite</tspan></text></g></g><path
d="m 563.88,395.7 c 0,2.15 1.75,3.9 3.9,3.9 h 67.44 c 2.15,0 3.9,-1.75 3.9,-3.9 v -15.6 c 0,-2.15 -1.75,-3.9 -3.9,-3.9 h -67.44 c -2.15,0 -3.9,1.75 -3.9,3.9 z"
style="fill:#94c57e;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path903" /><path
d="m 563.88,395.7 c 0,2.15 1.75,3.9 3.9,3.9 h 67.44 c 2.15,0 3.9,-1.75 3.9,-3.9 v -15.6 c 0,-2.15 -1.75,-3.9 -3.9,-3.9 h -67.44 c -2.15,0 -3.9,1.75 -3.9,3.9 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path905" /><g
id="g907"><g
id="g909"
clip-path="url(#clipPath913)"><text
transform="matrix(1,0,0,-1,582.36,383.78)"
style="font-variant:normal;font-weight:normal;font-size:12.024px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text917"><tspan
x="0 5.0500798 9.2344322 16.05204 26.380655 28.905697 31.935743"
y="0"
id="tspan915">LLVM IR</tspan></text></g></g><path
d="m 563.88,364.52 c 0,2.14 1.74,3.88 3.88,3.88 h 67.48 c 2.14,0 3.88,-1.74 3.88,-3.88 V 349 c 0,-2.14 -1.74,-3.88 -3.88,-3.88 h -67.48 c -2.14,0 -3.88,1.74 -3.88,3.88 z"
style="fill:#94c57e;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path919" /><path
d="m 563.88,364.52 c 0,2.14 1.74,3.88 3.88,3.88 h 67.48 c 2.14,0 3.88,-1.74 3.88,-3.88 V 349 c 0,-2.14 -1.74,-3.88 -3.88,-3.88 h -67.48 c -2.14,0 -3.88,1.74 -3.88,3.88 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path921" /><g
id="g923"><g
id="g925"
clip-path="url(#clipPath929)"><text
transform="matrix(1,0,0,-1,585.48,352.63)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text933"><tspan
x="0 5.8439999 12.108 19.812 22.656 25.68"
y="0"
id="tspan931">TPU IR</tspan></text></g></g><g
id="g935"><g
id="g937"
clip-path="url(#clipPath941)"><text
transform="matrix(1,0,0,-1,566.95,325.94)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text945"><tspan
x="0 5.5079999 11.508 16.788 22.764 26.747999 32.495998 35.256001 37.787998 44.112 48.216 54.563999 60.540001 64.524002"
y="0"
id="tspan943">Several others</tspan></text></g></g><path
d="m 563.88,247.5 c 0,2.15 1.75,3.9 3.9,3.9 h 67.44 c 2.15,0 3.9,-1.75 3.9,-3.9 v -15.6 c 0,-2.15 -1.75,-3.9 -3.9,-3.9 h -67.44 c -2.15,0 -3.9,1.75 -3.9,3.9 z"
style="fill:#bfbfbf;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path947" /><path
d="m 563.88,247.5 c 0,2.15 1.75,3.9 3.9,3.9 h 67.44 c 2.15,0 3.9,-1.75 3.9,-3.9 v -15.6 c 0,-2.15 -1.75,-3.9 -3.9,-3.9 h -67.44 c -2.15,0 -3.9,1.75 -3.9,3.9 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path949" /><g
id="g951"><g
id="g953"
clip-path="url(#clipPath957)"><text
transform="matrix(1,0,0,-1,585.6,235.51)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text961"><tspan
x="0 7.7880001 15.576 22.524 28.764"
y="0"
id="tspan959">NNAPI</tspan></text></g></g><g
id="g963"><g
id="g965"
clip-path="url(#clipPath969)"><text
transform="matrix(1,0,0,-1,575.88,207.62)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text973"><tspan
x="0 10.308 16.056 22.188 27.624001 30.108 36.431999 40.535999 46.883999 52.860001 56.844002"
y="0"
id="tspan971">Many others</tspan></text></g></g><g
id="g975"><g
id="g977"
clip-path="url(#clipPath981)"><text
transform="matrix(1,0,0,-1,278.52,367.87)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text985"><tspan
x="0 7.572 11.52 17.268 23.628 29.976 32.736 38.712002"
y="0"
id="tspan983">Grappler</tspan></text></g></g><path
d="m 344.29,326.84 70.66,40.33 -0.5,0.87 -70.66,-40.34 z m 71.03,37.66 3.72,5.58 -6.7,-0.37 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path987" /><path
d="m 343.22,313.83 71.09,20.65 -0.28,0.96 -71.09,-20.65 z m 70.83,17.97 4.92,4.56 -6.6,1.21 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path989" /><path
d="m 343.08,303.14 h 71.54 v -1 h -71.54 z m 70.54,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path991" /><path
d="m 342.14,291.36 72.86,-20.69 -0.27,-0.96 -72.87,20.69 z m 72.58,-18.01 4.96,-4.52 -6.6,-1.25 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path993" /><path
d="m 343.37,280.25 59.79,-41.89 -0.57,-0.82 -59.8,41.89 z m 60.41,-39.27 3.19,-5.9 -6.63,0.99 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path995" /><path
d="m 209.52,321.26 h 35.22 v -1 h -35.22 z m 34.22,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path997" /><path
d="m 209.52,303.14 h 35.22 v -1 h -35.22 z m 34.22,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path999" /><path
d="m 209.52,285.62 h 35.22 v -1 h -35.22 z m 34.22,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1001" /><path
d="m 306.81,349.92 -2.67,2.43 h 1.22 c -0.85,4.29 -3.8,7.29 -7.18,7.29 h 2.43 c 3.38,0 6.33,-3 7.18,-7.29 H 309 Z"
style="fill:#5b9bd5;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1003" /><path
d="m 299.39,359.51 c -3.58,-0.78 -6.2,-4.84 -6.2,-9.59 h -2.43 c 0,5.37 3.32,9.72 7.42,9.72 0.4,0 0.81,-0.04 1.21,-0.13 z"
style="fill:#497dab;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1005" /><path
d="m 299.39,359.51 c -3.58,-0.78 -6.2,-4.84 -6.2,-9.59 h -2.43 c 0,5.37 3.32,9.72 7.42,9.72 h 2.43 c 3.38,0 6.33,-3 7.18,-7.29 H 309 l -2.19,-2.43 -2.67,2.43 h 1.22 c -0.85,4.29 -3.8,7.29 -7.18,7.29"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1007" /><path
d="m 494.4,369.52 64.73,16.59 -0.25,0.97 -64.72,-16.59 z m 64.38,13.92 5.07,4.4 -6.56,1.42 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1009" /><path
d="m 494.37,370.57 64.66,-12.36 -0.19,-0.98 -64.65,12.36 z m 64.15,-9.71 5.33,-4.08 -6.46,-1.82 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1011" /><path
d="m 494.51,370.52 65.14,-34.16 -0.46,-0.89 -65.14,34.17 z m 65.42,-31.48 3.92,-5.45 -6.71,0.13 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1013" /><path
d="m 507.64,234.6 51.29,4.14 -0.08,1 -51.29,-4.14 z m 50.49,1.57 5.74,3.47 -6.22,2.51 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1015" /><path
d="m 507.79,235.54 51.65,-21.43 -0.38,-0.93 -51.65,21.44 z m 51.69,-18.74 4.39,-5.07 -6.69,-0.47 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1017" /></g></svg>

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1,380 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
version="1.1"
id="svg25"
xml:space="preserve"
width="902.39996"
height="387.03998"
viewBox="0 0 902.39996 387.03998"
sodipodi:docname="MindIR.svg"
inkscape:version="1.1.1 (c3084ef, 2021-09-22)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs29"><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath39"><path
d="M 0,1.2207e-4 H 960 V 540.00012 H 0 Z"
clip-rule="evenodd"
id="path37" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath53"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path51" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath69"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path67" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath85"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path83" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath101"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path99" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath117"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path115" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath133"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path131" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath149"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path147" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath165"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path163" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath181"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path179" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath197"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path195" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath213"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path211" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath229"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path227" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath245"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path243" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath261"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path259" /></clipPath></defs><sodipodi:namedview
id="namedview27"
pagecolor="#ffffff"
bordercolor="#cccccc"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="1"
inkscape:pagecheckerboard="0"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="0.8375"
inkscape:cx="458.50746"
inkscape:cy="275.8209"
inkscape:window-width="1792"
inkscape:window-height="1120"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="g31" /><g
id="g31"
inkscape:groupmode="layer"
inkscape:label="9"
transform="matrix(1.3333333,0,0,-1.3333333,-181.92,635.83998)"><path
d="m 422.16,472.08 c 0,2.39 1.93,4.32 4.32,4.32 h 66.6 c 2.39,0 4.32,-1.93 4.32,-4.32 V 454.8 c 0,-2.39 -1.93,-4.32 -4.32,-4.32 h -66.6 c -2.39,0 -4.32,1.93 -4.32,4.32 z"
style="fill:#a5c3f4;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path43" /><path
d="m 422.16,472.08 c 0,2.39 1.93,4.32 4.32,4.32 h 66.6 c 2.39,0 4.32,-1.93 4.32,-4.32 V 454.8 c 0,-2.39 -1.93,-4.32 -4.32,-4.32 h -66.6 c -2.39,0 -4.32,1.93 -4.32,4.32 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path45" /><g
id="g47"><g
id="g49"
clip-path="url(#clipPath53)"><text
transform="matrix(1,0,0,-1,437.98,459.36)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text57"><tspan
x="0 6.948 13.2 16.908001 24.695999 31.02 37.403999"
y="0"
sodipodi:role="line"
id="tspan55">AnfNode</tspan></text></g></g><path
d="m 323.16,386.74 c 0,2.6 2.1,4.7 4.7,4.7 h 65.84 c 2.6,0 4.7,-2.1 4.7,-4.7 v -18.8 c 0,-2.6 -2.1,-4.7 -4.7,-4.7 h -65.84 c -2.6,0 -4.7,2.1 -4.7,4.7 z"
style="fill:#a5c3f4;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path59" /><path
d="m 323.16,386.74 c 0,2.6 2.1,4.7 4.7,4.7 h 65.84 c 2.6,0 4.7,-2.1 4.7,-4.7 v -18.8 c 0,-2.6 -2.1,-4.7 -4.7,-4.7 h -65.84 c -2.6,0 -4.7,2.1 -4.7,4.7 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path61" /><g
id="g63"><g
id="g65"
clip-path="url(#clipPath69)"><text
transform="matrix(1,0,0,-1,344.09,373.22)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text73"><tspan
x="0 6.9720001 14.784 21.156 27.528"
y="0"
sodipodi:role="line"
id="tspan71">ANode</tspan></text></g></g><path
d="m 512.28,386.68 c 0,2.63 2.13,4.76 4.76,4.76 h 65.72 c 2.63,0 4.76,-2.13 4.76,-4.76 v -19.04 c 0,-2.63 -2.13,-4.76 -4.76,-4.76 h -65.72 c -2.63,0 -4.76,2.13 -4.76,4.76 z"
style="fill:#a5c3f4;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path75" /><path
d="m 512.28,386.68 c 0,2.63 2.13,4.76 4.76,4.76 h 65.72 c 2.63,0 4.76,-2.13 4.76,-4.76 v -19.04 c 0,-2.63 -2.13,-4.76 -4.76,-4.76 h -65.72 c -2.63,0 -4.76,2.13 -4.76,4.76 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path77" /><g
id="g79"><g
id="g81"
clip-path="url(#clipPath85)"><text
transform="matrix(1,0,0,-1,533.45,373.08)"
style="font-variant:normal;font-weight:normal;font-size:12.024px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text89"><tspan
x="0 6.3606958 14.1282 20.512943 26.861616"
y="0"
sodipodi:role="line"
id="tspan87">CNode</tspan></text></g></g><path
d="m 136.92,302.44 c 0,2.56 2.08,4.64 4.64,4.64 h 86 c 2.56,0 4.64,-2.08 4.64,-4.64 v -18.56 c 0,-2.56 -2.08,-4.64 -4.64,-4.64 h -86 c -2.56,0 -4.64,2.08 -4.64,4.64 z"
style="fill:#a5c3f4;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path91" /><path
d="m 136.92,302.44 c 0,2.56 2.08,4.64 4.64,4.64 h 86 c 2.56,0 4.64,-2.08 4.64,-4.64 v -18.56 c 0,-2.56 -2.08,-4.64 -4.64,-4.64 h -86 c -2.56,0 -4.64,2.08 -4.64,4.64 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path93" /><g
id="g95"><g
id="g97"
clip-path="url(#clipPath101)"><text
transform="matrix(1,0,0,-1,145.75,289.06)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text105"><tspan
x="0 6 11.748 15.72 21.468 31.056 36.959999 40.919998 46.896 51.108002 58.896 65.220001 71.603996"
y="0"
sodipodi:role="line"
id="tspan103">ParameterNode</tspan></text></g></g><path
d="m 422.16,302.66 c 0,2.44 1.98,4.42 4.42,4.42 h 66.4 c 2.44,0 4.42,-1.98 4.42,-4.42 v -17.68 c 0,-2.44 -1.98,-4.42 -4.42,-4.42 h -66.4 c -2.44,0 -4.42,1.98 -4.42,4.42 z"
style="fill:#a5c3f4;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path107" /><path
d="m 422.16,302.66 c 0,2.44 1.98,4.42 4.42,4.42 h 66.4 c 2.44,0 4.42,-1.98 4.42,-4.42 v -17.68 c 0,-2.44 -1.98,-4.42 -4.42,-4.42 h -66.4 c -2.44,0 -4.42,1.98 -4.42,4.42 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path109" /><g
id="g111"><g
id="g113"
clip-path="url(#clipPath117)"><text
transform="matrix(1,0,0,-1,433.01,289.7)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text121"><tspan
x="0 6.1199999 11.868 14.628 20.988001 26.964001 34.776001 41.099998 47.484001"
y="0"
sodipodi:role="line"
id="tspan119">ValueNode</tspan></text></g></g><path
d="m 136.92,187.08 h 95.28 v 22.08 h -95.28 z"
style="fill:#7cc2f2;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path123" /><path
d="m 136.92,187.08 h 95.28 v 22.08 h -95.28 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path125" /><g
id="g127"><g
id="g129"
clip-path="url(#clipPath133)"><text
transform="matrix(1,0,0,-1,159,193.99)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text137"><tspan
x="0 6 11.748 15.72 21.468 31.056 36.959999 40.919998 46.896"
y="0"
sodipodi:role="line"
id="tspan135">Parameter</tspan></text></g></g><path
d="m 269.64,187.68 h 54.24 v 21.96 h -54.24 z"
style="fill:#7cc2f2;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path139" /><path
d="m 269.64,187.68 h 54.24 v 21.96 h -54.24 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path141" /><g
id="g143"><g
id="g145"
clip-path="url(#clipPath149)"><text
transform="matrix(1,0,0,-1,282.31,194.5)"
style="font-variant:normal;font-weight:normal;font-size:12.024px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text153"><tspan
x="0 5.5190158 10.436832 16.196327 18.961847 24.721344"
y="0"
sodipodi:role="line"
id="tspan151">Scalar</tspan></text></g></g><path
d="m 330.36,187.68 h 54.12 v 21.96 h -54.12 z"
style="fill:#7cc2f2;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path155" /><path
d="m 330.36,187.68 h 54.12 v 21.96 h -54.12 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path157" /><g
id="g159"><g
id="g161"
clip-path="url(#clipPath165)"><text
transform="matrix(1,0,0,-1,339.62,194.5)"
style="font-variant:normal;font-weight:normal;font-size:12.024px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text169"><tspan
x="0 7.7675042 13.527 23.134176 29.146175"
y="0"
sodipodi:role="line"
id="tspan167">Named</tspan></text></g></g><path
d="m 390.96,187.08 h 54.12 v 23.04 h -54.12 z"
style="fill:#7cc2f2;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path171" /><path
d="m 390.96,187.08 h 54.12 v 23.04 h -54.12 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path173" /><g
id="g175"><g
id="g177"
clip-path="url(#clipPath181)"><text
transform="matrix(1,0,0,-1,401.83,194.5)"
style="font-variant:normal;font-weight:normal;font-size:12.024px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text185"><tspan
x="0 4.797576 10.785528 17.146223 21.847609 28.184256"
y="0"
sodipodi:role="line"
id="tspan183">Tensor</tspan></text></g></g><path
d="m 451.68,187.8 h 54.12 v 23.04 h -54.12 z"
style="fill:#7cc2f2;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path187" /><path
d="m 451.68,187.8 h 54.12 v 23.04 h -54.12 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path189" /><g
id="g191"><g
id="g193"
clip-path="url(#clipPath197)"><text
transform="matrix(1,0,0,-1,467.18,195.14)"
style="font-variant:normal;font-weight:normal;font-size:12.024px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text201"><tspan
x="0 5.2785358 10.677312 17.025984"
y="0"
sodipodi:role="line"
id="tspan199">Type</tspan></text></g></g><path
d="m 512.28,188.16 h 54.12 v 22.92 h -54.12 z"
style="fill:#7cc2f2;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path203" /><path
d="m 512.28,188.16 h 54.12 v 22.92 h -54.12 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path205" /><g
id="g207"><g
id="g209"
clip-path="url(#clipPath213)"><text
transform="matrix(1,0,0,-1,524.35,195.46)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text217"><tspan
x="0 5.5079999 11.868 17.615999 23.976"
y="0"
sodipodi:role="line"
id="tspan215">Shape</tspan></text></g></g><path
d="m 572.88,188.16 h 60.84 v 22.92 h -60.84 z"
style="fill:#7cc2f2;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path219" /><path
d="m 572.88,188.16 h 60.84 v 22.92 h -60.84 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path221" /><g
id="g223"><g
id="g225"
clip-path="url(#clipPath229)"><text
transform="matrix(1,0,0,-1,581.52,195.46)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text233"><tspan
x="0 6.204 10.428 13.188 22.775999 25.535999 29.615999 32.375999 37.655998"
y="0"
sodipodi:role="line"
id="tspan231">Primitive</tspan></text></g></g><path
d="m 640.2,188.16 h 94.08 v 22.92 H 640.2 Z"
style="fill:#7cc2f2;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path235" /><path
d="m 640.2,188.16 h 94.08 v 22.92 H 640.2 Z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path237" /><g
id="g239"><g
id="g241"
clip-path="url(#clipPath245)"><text
transform="matrix(1,0,0,-1,647.74,195.46)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text249"><tspan
x="0 10.308 16.188 20.148001 25.896 31.403999 37.776001 44.124001 49.164001 56.736 60.683998 66.431999 72.683998"
y="0"
sodipodi:role="line"
id="tspan247">MetaFuncGraph</tspan></text></g></g><path
d="m 740.76,188.16 h 72 v 22.92 h -72 z"
style="fill:#7cc2f2;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path251" /><path
d="m 740.76,188.16 h 72 v 22.92 h -72 z"
style="fill:none;stroke:#41719c;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path253" /><g
id="g255"><g
id="g257"
clip-path="url(#clipPath261)"><text
transform="matrix(1,0,0,-1,750.12,195.46)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text265"><tspan
x="0 5.5079999 11.868 18.216 23.256001 30.827999 34.776001 40.523998 46.883999"
y="0"
sodipodi:role="line"
id="tspan263">FuncGraph</tspan></text></g></g><path
d="m 459.5,450.91 -94.62,-56.47 0.51,-0.85 94.62,56.46 z m -95.04,-53.81 -3.62,-5.65 6.69,0.5 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path267" /><path
d="m 459.99,450.9 85.98,-56.29 -0.54,-0.84 -85.98,56.29 z m 86.52,-53.65 3.37,-5.8 -6.66,0.78 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path269" /><path
d="m 360.68,363.72 -171.51,-54.6 0.31,-0.95 171.5,54.59 z m -171.31,-51.91 -4.81,-4.68 6.63,-1.04 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path271" /><path
d="m 361.09,363.67 94.57,-53.64 -0.5,-0.87 -94.57,53.65 z m 94.93,-50.97 3.74,-5.57 -6.7,0.35 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path273" /><path
d="m 185.06,279.24 v -65.05 h -1 v 65.05 z m 2.5,-64.05 -3,-6 -3,6 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path275" /><path
d="m 459.53,281.02 -158.38,-68.87 0.39,-0.92 158.39,68.87 z m -158.46,-66.18 -4.31,-5.14 6.7,-0.36 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path277" /><path
d="m 459.4,280.97 -98.21,-68.02 0.56,-0.82 98.22,68.02 z m -98.82,-65.39 -3.22,-5.88 6.64,0.95 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path279" /><path
d="m 460.2,280.69 17.66,-64.9 -0.96,-0.27 -17.66,64.91 z m 19.81,-63.28 -1.32,-6.58 -4.47,5 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path281" /><path
d="m 460.05,280.94 75.85,-66.12 -0.66,-0.76 -75.85,66.12 z m 76.73,-63.58 2.56,-6.21 -6.5,1.69 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path283" /><path
d="m 459.94,281.01 139.08,-67.23 -0.43,-0.9 -139.09,67.23 z m 139.27,-64.54 4.09,-5.31 -6.7,-0.09 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path285" /><path
d="m 459.87,281.04 222.71,-67.95 -0.29,-0.95 -222.72,67.94 z m 222.49,-65.26 4.86,-4.62 -6.61,-1.12 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path287" /><path
d="m 459.83,281.05 312.13,-68.34 -0.22,-0.97 -312.13,68.33 z m 311.68,-65.68 5.22,-4.21 -6.5,-1.65 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path289" /><path
d="m 459.32,280.81 -39.12,-66.07 0.86,-0.51 39.12,66.08 z m -40.76,-63.94 -0.48,-6.69 5.64,3.64 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path291" /></g></svg>

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

View File

@@ -0,0 +1,364 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
version="1.1"
id="svg915"
xml:space="preserve"
width="993.91998"
height="272.47998"
viewBox="0 0 993.91998 272.47998"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs919"><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath929"><path
d="M 0,1.2207e-4 H 960 V 540.00012 H 0 Z"
clip-rule="evenodd"
id="path927" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath943"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path941" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath955"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path953" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath971"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path969" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath983"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path981" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath999"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path997" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1011"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1009" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1023"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1021" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1039"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1037" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1055"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1053" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1071"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1069" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1087"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1085" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1099"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1097" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1115"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1113" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1127"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1125" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1143"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1141" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1155"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1153" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1167"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1165" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1195"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1193" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1207"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1205" /></clipPath></defs><g
id="g921"
transform="matrix(1.3333333,0,0,-1.3333333,-127.84,518.39999)"><path
d="m 96.36,356.4 h 102.96 v 31.92 H 96.36 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path933" /><path
d="m 96.36,356.4 h 102.96 v 31.92 H 96.36 Z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path935" /><g
id="g937"><g
id="g939"
clip-path="url(#clipPath943)"><text
transform="matrix(1,0,0,-1,109.3,367.39)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text947"><tspan
x="0 14.04 28.08 42.119999 56.16"
y="0"
id="tspan945">源程序语言</tspan></text></g></g><g
id="g949"><g
id="g951"
clip-path="url(#clipPath955)"><text
transform="matrix(1,0,0,-1,179.52,367.39)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text959"><tspan
x="0"
y="0"
id="tspan957">1</tspan></text></g></g><path
d="m 96.36,283.92 h 102.96 v 31.8 H 96.36 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path961" /><path
d="m 96.36,283.92 h 102.96 v 31.8 H 96.36 Z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path963" /><g
id="g965"><g
id="g967"
clip-path="url(#clipPath971)"><text
transform="matrix(1,0,0,-1,109.3,294.86)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text975"><tspan
x="0 14.04 28.08 42.119999 56.16"
y="0"
id="tspan973">源程序语言</tspan></text></g></g><g
id="g977"><g
id="g979"
clip-path="url(#clipPath983)"><text
transform="matrix(1,0,0,-1,179.52,294.86)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text987"><tspan
x="0"
y="0"
id="tspan985">2</tspan></text></g></g><path
d="m 96.36,184.92 h 102.96 v 31.8 H 96.36 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path989" /><path
d="m 96.36,184.92 h 102.96 v 31.8 H 96.36 Z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path991" /><g
id="g993"><g
id="g995"
clip-path="url(#clipPath999)"><text
transform="matrix(1,0,0,-1,106.78,195.82)"
style="font-variant:normal;font-weight:normal;font-size:14.064px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1003"><tspan
x="0 14.04 28.08 42.119999 56.16"
y="0"
id="tspan1001">源程序语言</tspan></text></g></g><g
id="g1005"><g
id="g1007"
clip-path="url(#clipPath1011)"><text
transform="matrix(1,0,0,-1,176.98,195.82)"
style="font-variant:normal;font-weight:normal;font-size:14.064px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1015"><tspan
x="0"
y="0"
id="tspan1013">M</tspan></text></g></g><g
id="g1017"><g
id="g1019"
clip-path="url(#clipPath1023)"><text
transform="matrix(1,0,0,-1,143.04,246.96)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1027"><tspan
x="0"
y="0"
id="tspan1025"></tspan></text></g></g><path
d="m 277.92,252.12 h 96.24 v 31.8 h -96.24 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1029" /><path
d="m 277.92,252.12 h 96.24 v 31.8 h -96.24 z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1031" /><g
id="g1033"><g
id="g1035"
clip-path="url(#clipPath1039)"><text
transform="matrix(1,0,0,-1,312.05,263.04)"
style="font-variant:normal;font-weight:normal;font-size:14.064px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1043"><tspan
x="0 14.04"
y="0"
id="tspan1041">前端</tspan></text></g></g><path
d="m 417.12,251.16 h 96.24 v 31.8 h -96.24 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1045" /><path
d="m 417.12,251.16 h 96.24 v 31.8 h -96.24 z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1047" /><g
id="g1049"><g
id="g1051"
clip-path="url(#clipPath1055)"><text
transform="matrix(1,0,0,-1,444.19,262.08)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1059"><tspan
x="0 14.04 28.08"
y="0"
id="tspan1057">优化器</tspan></text></g></g><path
d="m 557.16,251.16 h 96.24 v 31.8 h -96.24 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1061" /><path
d="m 557.16,251.16 h 96.24 v 31.8 h -96.24 z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1063" /><g
id="g1065"><g
id="g1067"
clip-path="url(#clipPath1071)"><text
transform="matrix(1,0,0,-1,591.26,262.08)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1075"><tspan
x="0 14.04"
y="0"
id="tspan1073">后端</tspan></text></g></g><path
d="m 737.88,356.4 h 102.96 v 31.92 H 737.88 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1077" /><path
d="m 737.88,356.4 h 102.96 v 31.92 H 737.88 Z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1079" /><g
id="g1081"><g
id="g1083"
clip-path="url(#clipPath1087)"><text
transform="matrix(1,0,0,-1,757.78,367.39)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1091"><tspan
x="0 14.04 28.08 42.119999"
y="0"
id="tspan1089">目标机器</tspan></text></g></g><g
id="g1093"><g
id="g1095"
clip-path="url(#clipPath1099)"><text
transform="matrix(1,0,0,-1,813.94,367.39)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1103"><tspan
x="0"
y="0"
id="tspan1101">1</tspan></text></g></g><path
d="m 737.88,284.16 h 102.96 v 31.92 H 737.88 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1105" /><path
d="m 737.88,284.16 h 102.96 v 31.92 H 737.88 Z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1107" /><g
id="g1109"><g
id="g1111"
clip-path="url(#clipPath1115)"><text
transform="matrix(1,0,0,-1,757.78,295.13)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1119"><tspan
x="0 14.04 28.08 42.119999"
y="0"
id="tspan1117">目标机器</tspan></text></g></g><g
id="g1121"><g
id="g1123"
clip-path="url(#clipPath1127)"><text
transform="matrix(1,0,0,-1,813.94,295.13)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1131"><tspan
x="0"
y="0"
id="tspan1129">2</tspan></text></g></g><path
d="m 737.88,184.92 h 102.96 v 31.8 H 737.88 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1133" /><path
d="m 737.88,184.92 h 102.96 v 31.8 H 737.88 Z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1135" /><g
id="g1137"><g
id="g1139"
clip-path="url(#clipPath1143)"><text
transform="matrix(1,0,0,-1,756.82,195.82)"
style="font-variant:normal;font-weight:normal;font-size:14.064px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1147"><tspan
x="0 14.04 28.08 42.119999"
y="0"
id="tspan1145">目标机器</tspan></text></g></g><g
id="g1149"><g
id="g1151"
clip-path="url(#clipPath1155)"><text
transform="matrix(1,0,0,-1,812.98,195.82)"
style="font-variant:normal;font-weight:normal;font-size:14.064px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1159"><tspan
x="0"
y="0"
id="tspan1157">N</tspan></text></g></g><g
id="g1161"><g
id="g1163"
clip-path="url(#clipPath1167)"><text
transform="matrix(1,0,0,-1,784.56,241.99)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1171"><tspan
x="0"
y="0"
id="tspan1169"></tspan></text></g></g><path
d="m 199.32,300.38 h 39.76 v -32.34 l -0.5,0.5 h 34.27 v -1 h -34.77 v 32.34 l 0.5,-0.5 h -39.26 z m 72.53,-29.34 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1173" /><path
d="m 374.17,268.46 37.93,-0.84 -0.02,-1 -37.93,0.84 z m 36.98,1.68 5.94,-3.13 -6.07,-2.87 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1175" /><path
d="m 513.36,267.62 h 38.69 v -1 h -38.69 z m 37.69,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1177" /><path
d="m 653.4,266.58 h 37.63 v 33.54 l -0.5,-0.5 h 42.31 v 1 h -42.81 v -33.54 l 0.5,0.5 H 653.4 Z m 78.44,30.54 6,3 -6,3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1179" /><path
d="m 653.4,259.34 h 23.83 v -58.5 l -0.5,0.5 h 56.16 v -1 h -56.66 v 58.5 l 0.5,-0.5 H 653.4 Z m 78.49,-55.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1181" /><path
d="m 199.32,372.86 h 52.15 v -95.13 l -0.5,0.5 h 21.92 v -1 h -22.42 v 95.13 l 0.5,-0.5 h -51.65 z m 72.57,-92.13 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1183" /><path
d="m 199.32,200.31 h 52.85 v 59.49 l -0.5,-0.5 h 21.18 v 1 h -21.68 v -59.49 l 0.5,0.5 h -52.35 z m 72.53,56.49 6,3 -6,3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1185" /><path
d="m 653.4,275.91 h 21.37 v 92.49 l -0.5,-0.5 h 58.06 v 1 h -58.56 v -92.49 l 0.5,0.5 H 653.4 Z m 77.93,89.49 6,3 -6,3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1187" /><g
id="g1189"><g
id="g1191"
clip-path="url(#clipPath1195)"><text
transform="matrix(1,0,0,-1,390.12,275.09)"
style="font-variant:normal;font-weight:normal;font-size:14.064px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1199"><tspan
x="0 3.4800279"
y="0"
id="tspan1197">IR</tspan></text></g></g><g
id="g1201"><g
id="g1203"
clip-path="url(#clipPath1207)"><text
transform="matrix(1,0,0,-1,529.73,273.79)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1211"><tspan
x="0 3.47998"
y="0"
id="tspan1209">IR</tspan></text></g></g></g></svg>

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1,744 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
version="1.1"
id="svg25"
xml:space="preserve"
width="871.03998"
height="274.07776"
viewBox="0 0 871.03998 274.07776"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs29"><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath39"><path
d="M 0,1.2207e-4 H 960 V 540.00012 H 0 Z"
clip-rule="evenodd"
id="path37" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath53"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path51" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath65"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path63" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath77"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path75" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath89"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path87" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath101"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path99" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath121"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path119" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath133"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path131" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath145"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path143" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath157"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path155" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath169"><path
d="m 562.56,350.52 h 65.64 v 117.6 h -65.64 z"
clip-rule="evenodd"
id="path167" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath181"><path
d="m 562.56,350.52 h 65.64 v 117.6 h -65.64 z"
clip-rule="evenodd"
id="path179" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath193"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path191" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath205"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path203" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath217"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path215" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath229"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path227" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath241"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path239" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath253"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path251" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath265"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path263" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath277"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path275" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath289"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path287" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath301"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path299" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath313"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path311" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath325"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path323" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath337"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path335" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath349"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path347" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath361"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path359" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath373"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path371" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath385"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path383" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath401"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path399" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath413"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path411" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath425"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path423" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath437"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path435" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath449"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path447" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath461"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path459" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath473"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path471" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath485"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path483" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath497"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path495" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath509"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path507" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath521"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path519" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath533"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path531" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath545"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path543" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath557"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path555" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath569"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path567" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath581"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path579" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath593"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path591" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath605"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path603" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath617"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path615" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath629"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path627" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath641"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path639" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath653"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path651" /></clipPath></defs><g
id="g31"
transform="matrix(1.3333333,0,0,-1.3333333,-182.08,565.27999)"><path
d="m 137.04,401.08 c 0,12.3 9.98,22.28 22.28,22.28 h 89.12 c 12.3,0 22.28,-9.98 22.28,-22.28 V 297.32 c 0,-12.3 -9.98,-22.28 -22.28,-22.28 h -89.12 c -12.3,0 -22.28,9.98 -22.28,22.28 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path43" /><path
d="m 137.04,401.08 c 0,12.3 9.98,22.28 22.28,22.28 h 89.12 c 12.3,0 22.28,-9.98 22.28,-22.28 V 297.32 c 0,-12.3 -9.98,-22.28 -22.28,-22.28 h -89.12 c -12.3,0 -22.28,9.98 -22.28,22.28 z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path45" /><g
id="g47"><g
id="g49"
clip-path="url(#clipPath53)"><text
transform="matrix(1,0,0,-1,167.28,386.38)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text57"><tspan
x="0 9.4499998 18.936001 25.974001 35.478001 39.653999"
y="0"
id="tspan55">push 5</tspan></text></g></g><g
id="g59"><g
id="g61"
clip-path="url(#clipPath65)"><text
transform="matrix(1,0,0,-1,167.28,364.78)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text69"><tspan
x="0 9.4499998 18.936001 25.974001 35.478001 39.653999"
y="0"
id="tspan67">push b</tspan></text></g></g><g
id="g71"><g
id="g73"
clip-path="url(#clipPath77)"><text
transform="matrix(1,0,0,-1,167.28,343.18)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text81"><tspan
x="0 14.382 23.832001 27.972 34.001999 38.034 47.484001 51.624001"
y="0"
id="tspan79">multiply</tspan></text></g></g><g
id="g83"><g
id="g85"
clip-path="url(#clipPath89)"><text
transform="matrix(1,0,0,-1,167.28,321.58)"
style="font-variant:normal;font-weight:normal;font-size:18.024px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text93"><tspan
x="0 9.4625998 18.9252 26.026655 35.489258 39.688847"
y="0"
id="tspan91">push a</tspan></text></g></g><g
id="g95"><g
id="g97"
clip-path="url(#clipPath101)"><text
transform="matrix(1,0,0,-1,167.28,299.95)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text105"><tspan
x="0 7.0380001 16.542 25.902 32.742001 38.771999 44.622002 53.243999 60.858002"
y="0"
id="tspan103">substract</tspan></text></g></g><path
d="M 388.54,421.84 V 274.53"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path107" /><path
d="M 628.19,421.84 V 274.53"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path109" /><path
d="M 388.04,421.34 H 628.69"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path111" /><path
d="M 388.04,275.03 H 628.69"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path113" /><g
id="g115"><g
id="g117"
clip-path="url(#clipPath121)"><text
transform="matrix(1,0,0,-1,398.69,404.4)"
style="font-variant:normal;font-weight:normal;font-size:14.064px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text125"><tspan
x="0 14.04"
y="0"
id="tspan123">目标</tspan></text></g></g><g
id="g127"><g
id="g129"
clip-path="url(#clipPath133)"><text
transform="matrix(1,0,0,-1,445.73,404.4)"
style="font-variant:normal;font-weight:normal;font-size:14.064px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text137"><tspan
x="0 14.04 28.08"
y="0"
id="tspan135">运算符</tspan></text></g></g><g
id="g139"><g
id="g141"
clip-path="url(#clipPath145)"><text
transform="matrix(1,0,0,-1,505.01,404.4)"
style="font-variant:normal;font-weight:normal;font-size:14.064px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text149"><tspan
x="0 14.04 28.08"
y="0"
id="tspan147">操作数</tspan></text></g></g><g
id="g151"><g
id="g153"
clip-path="url(#clipPath157)"><text
transform="matrix(1,0,0,-1,547.13,404.4)"
style="font-variant:normal;font-weight:normal;font-size:14.064px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text161"><tspan
x="0"
y="0"
id="tspan159">1</tspan></text></g></g><g
id="g163"><g
id="g165"
clip-path="url(#clipPath169)"><text
transform="matrix(1,0,0,-1,570.86,404.4)"
style="font-variant:normal;font-weight:normal;font-size:14.064px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text173"><tspan
x="0 14.04 28.08"
y="0"
id="tspan171">操作数</tspan></text></g></g><g
id="g175"><g
id="g177"
clip-path="url(#clipPath181)"><text
transform="matrix(1,0,0,-1,613.01,404.4)"
style="font-variant:normal;font-weight:normal;font-size:14.064px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text185"><tspan
x="0"
y="0"
id="tspan183">2</tspan></text></g></g><g
id="g187"><g
id="g189"
clip-path="url(#clipPath193)"><text
transform="matrix(1,0,0,-1,406.85,380.23)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text197"><tspan
x="0 4.6799998"
y="0"
id="tspan195">t1</tspan></text></g></g><g
id="g199"><g
id="g201"
clip-path="url(#clipPath205)"><text
transform="matrix(1,0,0,-1,460.37,380.23)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text209"><tspan
x="0"
y="0"
id="tspan207"></tspan></text></g></g><g
id="g211"><g
id="g213"
clip-path="url(#clipPath217)"><text
transform="matrix(1,0,0,-1,526.01,380.23)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text221"><tspan
x="0"
y="0"
id="tspan219">5</tspan></text></g></g><g
id="g223"><g
id="g225"
clip-path="url(#clipPath229)"><text
transform="matrix(1,0,0,-1,406.85,355.85)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text233"><tspan
x="0 4.6799998"
y="0"
id="tspan231">t2</tspan></text></g></g><g
id="g235"><g
id="g237"
clip-path="url(#clipPath241)"><text
transform="matrix(1,0,0,-1,460.37,355.85)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text245"><tspan
x="0"
y="0"
id="tspan243"></tspan></text></g></g><g
id="g247"><g
id="g249"
clip-path="url(#clipPath253)"><text
transform="matrix(1,0,0,-1,525.89,355.85)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text257"><tspan
x="0"
y="0"
id="tspan255">b</tspan></text></g></g><g
id="g259"><g
id="g261"
clip-path="url(#clipPath265)"><text
transform="matrix(1,0,0,-1,406.85,331.46)"
style="font-variant:normal;font-weight:normal;font-size:14.064px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text269"><tspan
x="0 4.6800399"
y="0"
id="tspan267">t3</tspan></text></g></g><g
id="g271"><g
id="g273"
clip-path="url(#clipPath277)"><text
transform="matrix(1,0,0,-1,459.77,331.22)"
style="font-variant:normal;font-weight:normal;font-size:14.064px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text281"><tspan
x="0"
y="0"
id="tspan279">×</tspan></text></g></g><g
id="g283"><g
id="g285"
clip-path="url(#clipPath289)"><text
transform="matrix(1,0,0,-1,523.73,331.46)"
style="font-variant:normal;font-weight:normal;font-size:14.064px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text293"><tspan
x="0 4.6800399"
y="0"
id="tspan291">t1</tspan></text></g></g><g
id="g295"><g
id="g297"
clip-path="url(#clipPath301)"><text
transform="matrix(1,0,0,-1,589.61,331.46)"
style="font-variant:normal;font-weight:normal;font-size:14.064px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text305"><tspan
x="0 4.6800399"
y="0"
id="tspan303">t2</tspan></text></g></g><g
id="g307"><g
id="g309"
clip-path="url(#clipPath313)"><text
transform="matrix(1,0,0,-1,406.85,307.06)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text317"><tspan
x="0 4.6799998"
y="0"
id="tspan315">t4</tspan></text></g></g><g
id="g319"><g
id="g321"
clip-path="url(#clipPath325)"><text
transform="matrix(1,0,0,-1,460.37,307.06)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text329"><tspan
x="0"
y="0"
id="tspan327"></tspan></text></g></g><g
id="g331"><g
id="g333"
clip-path="url(#clipPath337)"><text
transform="matrix(1,0,0,-1,526.25,307.06)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text341"><tspan
x="0"
y="0"
id="tspan339">a</tspan></text></g></g><g
id="g343"><g
id="g345"
clip-path="url(#clipPath349)"><text
transform="matrix(1,0,0,-1,406.85,282.67)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text353"><tspan
x="0 4.6799998"
y="0"
id="tspan351">t5</tspan></text></g></g><g
id="g355"><g
id="g357"
clip-path="url(#clipPath361)"><text
transform="matrix(1,0,0,-1,464.57,282.67)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text365"><tspan
x="0"
y="0"
id="tspan363">-</tspan></text></g></g><g
id="g367"><g
id="g369"
clip-path="url(#clipPath373)"><text
transform="matrix(1,0,0,-1,523.73,282.67)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text377"><tspan
x="0 4.6799998"
y="0"
id="tspan375">t4</tspan></text></g></g><g
id="g379"><g
id="g381"
clip-path="url(#clipPath385)"><text
transform="matrix(1,0,0,-1,589.61,282.67)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text389"><tspan
x="0 4.6799998"
y="0"
id="tspan387">t3</tspan></text></g></g><path
d="m 650.52,400.34 c 0,12.78 10.36,23.14 23.14,23.14 h 92.56 c 12.78,0 23.14,-10.36 23.14,-23.14 V 298.18 c 0,-12.78 -10.36,-23.14 -23.14,-23.14 h -92.56 c -12.78,0 -23.14,10.36 -23.14,23.14 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path391" /><path
d="m 650.52,400.34 c 0,12.78 10.36,23.14 23.14,23.14 h 92.56 c 12.78,0 23.14,-10.36 23.14,-23.14 V 298.18 c 0,-12.78 -10.36,-23.14 -23.14,-23.14 h -92.56 c -12.78,0 -23.14,10.36 -23.14,23.14 z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:8;stroke-dasharray:none;stroke-opacity:1"
id="path393" /><g
id="g395"><g
id="g397"
clip-path="url(#clipPath401)"><text
transform="matrix(1,0,0,-1,676.94,386.04)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text405"><tspan
x="0 6.0300002 15.156"
y="0"
id="tspan403">t1 </tspan></text></g></g><g
id="g407"><g
id="g409"
clip-path="url(#clipPath413)"><text
transform="matrix(1,0,0,-1,696.26,386.04)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text417"><tspan
x="0"
y="0"
id="tspan415"></tspan></text></g></g><g
id="g419"><g
id="g421"
clip-path="url(#clipPath425)"><text
transform="matrix(1,0,0,-1,716.54,386.04)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text429"><tspan
x="0"
y="0"
id="tspan427">5</tspan></text></g></g><g
id="g431"><g
id="g433"
clip-path="url(#clipPath437)"><text
transform="matrix(1,0,0,-1,676.94,364.8)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text441"><tspan
x="0 6.0300002 15.156"
y="0"
id="tspan439">t2 </tspan></text></g></g><g
id="g443"><g
id="g445"
clip-path="url(#clipPath449)"><text
transform="matrix(1,0,0,-1,696.26,364.8)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text453"><tspan
x="0"
y="0"
id="tspan451"></tspan></text></g></g><g
id="g455"><g
id="g457"
clip-path="url(#clipPath461)"><text
transform="matrix(1,0,0,-1,716.54,364.8)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text465"><tspan
x="0"
y="0"
id="tspan463">b</tspan></text></g></g><g
id="g467"><g
id="g469"
clip-path="url(#clipPath473)"><text
transform="matrix(1,0,0,-1,676.94,342.84)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text477"><tspan
x="0 6.0300002 15.156"
y="0"
id="tspan475">t3 </tspan></text></g></g><g
id="g479"><g
id="g481"
clip-path="url(#clipPath485)"><text
transform="matrix(1,0,0,-1,696.26,342.84)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text489"><tspan
x="0"
y="0"
id="tspan487"></tspan></text></g></g><g
id="g491"><g
id="g493"
clip-path="url(#clipPath497)"><text
transform="matrix(1,0,0,-1,716.54,342.84)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text501"><tspan
x="0 6"
y="0"
id="tspan499">t1</tspan></text></g></g><g
id="g503"><g
id="g505"
clip-path="url(#clipPath509)"><text
transform="matrix(1,0,0,-1,731.66,342.84)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text513"><tspan
x="0"
y="0"
id="tspan511">×</tspan></text></g></g><g
id="g515"><g
id="g517"
clip-path="url(#clipPath521)"><text
transform="matrix(1,0,0,-1,749.66,342.84)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text525"><tspan
x="0 6"
y="0"
id="tspan523">t2</tspan></text></g></g><g
id="g527"><g
id="g529"
clip-path="url(#clipPath533)"><text
transform="matrix(1,0,0,-1,676.94,321.6)"
style="font-variant:normal;font-weight:normal;font-size:18.024px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text537"><tspan
x="0 6.0380402 15.122136"
y="0"
id="tspan535">t4 </tspan></text></g></g><g
id="g539"><g
id="g541"
clip-path="url(#clipPath545)"><text
transform="matrix(1,0,0,-1,696.26,321.6)"
style="font-variant:normal;font-weight:normal;font-size:18.024px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text549"><tspan
x="0"
y="0"
id="tspan547"></tspan></text></g></g><g
id="g551"><g
id="g553"
clip-path="url(#clipPath557)"><text
transform="matrix(1,0,0,-1,716.54,321.6)"
style="font-variant:normal;font-weight:normal;font-size:18.024px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text561"><tspan
x="0"
y="0"
id="tspan559">a</tspan></text></g></g><g
id="g563"><g
id="g565"
clip-path="url(#clipPath569)"><text
transform="matrix(1,0,0,-1,676.94,299.98)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text573"><tspan
x="0 6.0300002 15.156"
y="0"
id="tspan571">t5 </tspan></text></g></g><g
id="g575"><g
id="g577"
clip-path="url(#clipPath581)"><text
transform="matrix(1,0,0,-1,696.26,299.98)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text585"><tspan
x="0"
y="0"
id="tspan583"></tspan></text></g></g><g
id="g587"><g
id="g589"
clip-path="url(#clipPath593)"><text
transform="matrix(1,0,0,-1,716.54,299.98)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text597"><tspan
x="0 6.0300002 15.156"
y="0"
id="tspan595">t4 </tspan></text></g></g><g
id="g599"><g
id="g601"
clip-path="url(#clipPath605)"><text
transform="matrix(1,0,0,-1,735.86,299.98)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text609"><tspan
x="0"
y="0"
id="tspan607"></tspan></text></g></g><g
id="g611"><g
id="g613"
clip-path="url(#clipPath617)"><text
transform="matrix(1,0,0,-1,748.94,299.98)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text621"><tspan
x="0 6"
y="0"
id="tspan619">t3</tspan></text></g></g><g
id="g623"><g
id="g625"
clip-path="url(#clipPath629)"><text
transform="matrix(1,0,0,-1,171.38,219.43)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text633"><tspan
x="0 14.04"
y="0"
id="tspan631">堆栈</tspan></text></g></g><g
id="g635"><g
id="g637"
clip-path="url(#clipPath641)"><text
transform="matrix(1,0,0,-1,199.46,219.43)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text645"><tspan
x="0 14.04 28.08"
y="0"
id="tspan643">机代码</tspan></text></g></g><g
id="g647"><g
id="g649"
clip-path="url(#clipPath653)"><text
transform="matrix(1,0,0,-1,570.7,219.43)"
style="font-variant:normal;font-weight:normal;font-size:14.04px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text657"><tspan
x="0 14.04 28.08 42.119999 56.16"
y="0"
id="tspan655">三地址代码</tspan></text></g></g></g></svg>

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

@@ -0,0 +1,581 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
version="1.1"
id="svg1251"
xml:space="preserve"
width="845.71997"
height="453.86667"
viewBox="0 0 845.71997 453.86667"
sodipodi:docname="编译优化-pass结构.svg"
inkscape:version="1.1.1 (c3084ef, 2021-09-22)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs1255"><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1265"><path
d="M 0,1.2207e-4 H 960 V 540.00012 H 0 Z"
clip-rule="evenodd"
id="path1263" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1279"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1277" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1291"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1289" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1307"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1305" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1319"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1317" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1335"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1333" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1347"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1345" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1367"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1365" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1379"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1377" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1391"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1389" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1403"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1401" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1423"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1421" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1435"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1433" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1451"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1449" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1463"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1461" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1479"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1477" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1491"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1489" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1517"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1515" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1529"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1527" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1545"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1543" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1557"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1555" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1573"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1571" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1585"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1583" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1609"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1607" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1621"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1619" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1637"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1635" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1649"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1647" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1665"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1663" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1677"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path1675" /></clipPath></defs><sodipodi:namedview
id="namedview1253"
pagecolor="#ffffff"
bordercolor="#cccccc"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="1"
inkscape:pagecheckerboard="0"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="0.95859375"
inkscape:cx="402.67319"
inkscape:cy="226.37327"
inkscape:window-width="1792"
inkscape:window-height="1092"
inkscape:window-x="0"
inkscape:window-y="28"
inkscape:window-maximized="0"
inkscape:current-layer="g1257" /><g
id="g1257"
inkscape:groupmode="layer"
inkscape:label="编译优化-pass结构"
transform="matrix(1.3333333,0,0,-1.3333333,-43.999999,636.26665)"><g
id="g1259"><g
id="g1261"
clip-path="url(#clipPath1265)" /></g><path
d="m 134.6,466.07 c 0,5.81 4.72,10.53 10.53,10.53 h 42.14 c 5.81,0 10.53,-4.72 10.53,-10.53 V 311.13 c 0,-5.81 -4.72,-10.53 -10.53,-10.53 h -42.14 c -5.81,0 -10.53,4.72 -10.53,10.53 z"
style="fill:#f8cbad;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1269" /><path
d="m 134.6,466.07 c 0,5.81 4.72,10.53 10.53,10.53 h 42.14 c 5.81,0 10.53,-4.72 10.53,-10.53 V 311.13 c 0,-5.81 -4.72,-10.53 -10.53,-10.53 h -42.14 c -5.81,0 -10.53,4.72 -10.53,10.53 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1271" /><g
id="g1273"><g
id="g1275"
clip-path="url(#clipPath1279)"><text
transform="matrix(1,0,0,-1,149.95,393.5)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1283"><tspan
x="0 8.7961998 17.574375 24.748325 31.922276"
y="0"
sodipodi:role="line"
id="tspan1281">Pass </tspan></text></g></g><g
id="g1285"><g
id="g1287"
clip-path="url(#clipPath1291)"><text
transform="matrix(1,0,0,-1,160.75,371.88)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1295"><tspan
x="0"
y="0"
id="tspan1293">A</tspan></text></g></g><path
d="m 318.6,466.07 c 0,5.81 4.72,10.53 10.53,10.53 h 42.14 c 5.81,0 10.53,-4.72 10.53,-10.53 V 311.13 c 0,-5.81 -4.72,-10.53 -10.53,-10.53 h -42.14 c -5.81,0 -10.53,4.72 -10.53,10.53 z"
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1297" /><path
d="m 318.6,466.07 c 0,5.81 4.72,10.53 10.53,10.53 h 42.14 c 5.81,0 10.53,-4.72 10.53,-10.53 V 311.13 c 0,-5.81 -4.72,-10.53 -10.53,-10.53 h -42.14 c -5.81,0 -10.53,4.72 -10.53,10.53 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1299" /><g
id="g1301"><g
id="g1303"
clip-path="url(#clipPath1307)"><text
transform="matrix(1,0,0,-1,334.15,393.5)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1311"><tspan
x="0 8.7961998 17.574375 24.748325 31.922276"
y="0"
sodipodi:role="line"
id="tspan1309">Pass </tspan></text></g></g><g
id="g1313"><g
id="g1315"
clip-path="url(#clipPath1319)"><text
transform="matrix(1,0,0,-1,345.35,371.88)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1323"><tspan
x="0"
y="0"
id="tspan1321">B</tspan></text></g></g><path
d="m 503,466.13 c 0,5.78 4.69,10.47 10.47,10.47 h 41.86 c 5.78,0 10.47,-4.69 10.47,-10.47 V 311.07 c 0,-5.78 -4.69,-10.47 -10.47,-10.47 h -41.86 c -5.78,0 -10.47,4.69 -10.47,10.47 z"
style="fill:#9dc3e6;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1325" /><path
d="m 503,466.13 c 0,5.78 4.69,10.47 10.47,10.47 h 41.86 c 5.78,0 10.47,-4.69 10.47,-10.47 V 311.07 c 0,-5.78 -4.69,-10.47 -10.47,-10.47 h -41.86 c -5.78,0 -10.47,4.69 -10.47,10.47 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1327" /><g
id="g1329"><g
id="g1331"
clip-path="url(#clipPath1335)"><text
transform="matrix(1,0,0,-1,518.35,393.5)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1339"><tspan
x="0 8.7961998 17.574375 24.748325 31.922276"
y="0"
sodipodi:role="line"
id="tspan1337">Pass </tspan></text></g></g><g
id="g1341"><g
id="g1343"
clip-path="url(#clipPath1347)"><text
transform="matrix(1,0,0,-1,529.55,371.88)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1351"><tspan
x="0"
y="0"
id="tspan1349">C</tspan></text></g></g><path
d="m 197.8,388.1 h 116.03 v 1 H 197.8 Z m 115.03,-2.5 6,3 -6,3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1353" /><path
d="m 381.8,389.1 h 116.03 v -1 H 381.8 Z m 115.03,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1355" /><path
d="m 33,389.1 h 96.49 v -1 H 33 Z m 95.49,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1357" /><path
d="m 565.8,389.1 h 96.49 v -1 H 565.8 Z m 95.49,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1359" /><g
id="g1361"><g
id="g1363"
clip-path="url(#clipPath1367)"><text
transform="matrix(1,0,0,-1,69.225,397.38)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1371"><tspan
x="0 4.4003"
y="0"
sodipodi:role="line"
id="tspan1369">IR</tspan></text></g></g><g
id="g1373"><g
id="g1375"
clip-path="url(#clipPath1379)"><text
transform="matrix(1,0,0,-1,250.85,397.38)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1383"><tspan
x="0 4.4003"
y="0"
sodipodi:role="line"
id="tspan1381">IR</tspan></text></g></g><g
id="g1385"><g
id="g1387"
clip-path="url(#clipPath1391)"><text
transform="matrix(1,0,0,-1,435.03,397.38)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1395"><tspan
x="0 4.4003"
y="0"
sodipodi:role="line"
id="tspan1393">IR</tspan></text></g></g><g
id="g1397"><g
id="g1399"
clip-path="url(#clipPath1403)"><text
transform="matrix(1,0,0,-1,602.17,397.38)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1407"><tspan
x="0 4.4003"
y="0"
sodipodi:role="line"
id="tspan1405">IR</tspan></text></g></g><path
d="m 515.4,252.8 h 9.5 v 30.6 h 19 v -30.6 h 9.5 l -19,-19 z"
style="fill:#9dc3e6;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1409" /><path
d="m 515.4,252.8 h 9.5 v 30.6 h 19 v -30.6 h 9.5 l -19,-19 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1411" /><path
d="m 460.6,212.13 c 0,2.69 2.18,4.87 4.87,4.87 h 19.46 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 h -19.46 c -2.69,0 -4.87,2.18 -4.87,4.87 z"
style="fill:#9dc3e6;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1413" /><path
d="m 460.6,212.13 c 0,2.69 2.18,4.87 4.87,4.87 h 19.46 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 h -19.46 c -2.69,0 -4.87,2.18 -4.87,4.87 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1415" /><g
id="g1417"><g
id="g1419"
clip-path="url(#clipPath1423)"><text
transform="matrix(1,0,0,-1,470.5,181.78)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1427"><tspan
x="0"
y="0"
id="tspan1425">C</tspan></text></g></g><g
id="g1429"><g
id="g1431"
clip-path="url(#clipPath1435)"><text
transform="matrix(1,0,0,-1,470.5,160.17)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1439"><tspan
x="0"
y="0"
id="tspan1437">1</tspan></text></g></g><path
d="m 523,212.13 c 0,2.69 2.18,4.87 4.87,4.87 h 19.46 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 h -19.46 c -2.69,0 -4.87,2.18 -4.87,4.87 z"
style="fill:#9dc3e6;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1441" /><path
d="m 523,212.13 c 0,2.69 2.18,4.87 4.87,4.87 h 19.46 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 h -19.46 c -2.69,0 -4.87,2.18 -4.87,4.87 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1443" /><g
id="g1445"><g
id="g1447"
clip-path="url(#clipPath1451)"><text
transform="matrix(1,0,0,-1,532.72,181.78)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1455"><tspan
x="0"
y="0"
id="tspan1453">C</tspan></text></g></g><g
id="g1457"><g
id="g1459"
clip-path="url(#clipPath1463)"><text
transform="matrix(1,0,0,-1,532.72,160.17)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1467"><tspan
x="0"
y="0"
id="tspan1465">2</tspan></text></g></g><path
d="m 584.6,212.13 c 0,2.69 2.18,4.87 4.87,4.87 h 19.46 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 h -19.46 c -2.69,0 -4.87,2.18 -4.87,4.87 z"
style="fill:#9dc3e6;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1469" /><path
d="m 584.6,212.13 c 0,2.69 2.18,4.87 4.87,4.87 h 19.46 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 h -19.46 c -2.69,0 -4.87,2.18 -4.87,4.87 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1471" /><g
id="g1473"><g
id="g1475"
clip-path="url(#clipPath1479)"><text
transform="matrix(1,0,0,-1,594.63,181.78)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1483"><tspan
x="0"
y="0"
id="tspan1481">C</tspan></text></g></g><g
id="g1485"><g
id="g1487"
clip-path="url(#clipPath1491)"><text
transform="matrix(1,0,0,-1,594.63,160.17)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1495"><tspan
x="0"
y="0"
id="tspan1493">3</tspan></text></g></g><path
d="m 489.8,177.5 h 28 v -1 h -28 z m 27,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1497" /><path
d="m 552.2,176.5 h 27.72 v 1 H 552.2 Z m 26.72,-2.5 6,3 -6,3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1499" /><path
d="m 613.8,177.5 h 18.14 v -1 H 613.8 Z m 17.14,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1501" /><path
d="m 331.4,252.8 h 9.5 v 30.6 h 19 v -30.6 h 9.5 l -19,-19 z"
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1503" /><path
d="m 331.4,252.8 h 9.5 v 30.6 h 19 v -30.6 h 9.5 l -19,-19 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1505" /><path
d="m 273.8,212.13 c 0,2.69 2.18,4.87 4.87,4.87 h 19.46 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 h -19.46 c -2.69,0 -4.87,2.18 -4.87,4.87 z"
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1507" /><path
d="m 273.8,212.13 c 0,2.69 2.18,4.87 4.87,4.87 h 19.46 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 h -19.46 c -2.69,0 -4.87,2.18 -4.87,4.87 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1509" /><g
id="g1511"><g
id="g1513"
clip-path="url(#clipPath1517)"><text
transform="matrix(1,0,0,-1,283.6,181.78)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1521"><tspan
x="0"
y="0"
id="tspan1519">B</tspan></text></g></g><g
id="g1523"><g
id="g1525"
clip-path="url(#clipPath1529)"><text
transform="matrix(1,0,0,-1,283.6,160.17)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1533"><tspan
x="0"
y="0"
id="tspan1531">1</tspan></text></g></g><path
d="m 335.8,212.13 c 0,2.69 2.18,4.87 4.87,4.87 h 19.46 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 h -19.46 c -2.69,0 -4.87,2.18 -4.87,4.87 z"
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1535" /><path
d="m 335.8,212.13 c 0,2.69 2.18,4.87 4.87,4.87 h 19.46 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 h -19.46 c -2.69,0 -4.87,2.18 -4.87,4.87 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1537" /><g
id="g1539"><g
id="g1541"
clip-path="url(#clipPath1545)"><text
transform="matrix(1,0,0,-1,345.67,181.78)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1549"><tspan
x="0"
y="0"
id="tspan1547">B</tspan></text></g></g><g
id="g1551"><g
id="g1553"
clip-path="url(#clipPath1557)"><text
transform="matrix(1,0,0,-1,345.67,160.17)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1561"><tspan
x="0"
y="0"
id="tspan1559">2</tspan></text></g></g><path
d="m 397.8,212.13 c 0,2.69 2.18,4.87 4.87,4.87 h 19.46 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 h -19.46 c -2.69,0 -4.87,2.18 -4.87,4.87 z"
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1563" /><path
d="m 397.8,212.13 c 0,2.69 2.18,4.87 4.87,4.87 h 19.46 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 h -19.46 c -2.69,0 -4.87,2.18 -4.87,4.87 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1565" /><g
id="g1567"><g
id="g1569"
clip-path="url(#clipPath1573)"><text
transform="matrix(1,0,0,-1,407.72,181.78)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1577"><tspan
x="0"
y="0"
id="tspan1575">B</tspan></text></g></g><g
id="g1579"><g
id="g1581"
clip-path="url(#clipPath1585)"><text
transform="matrix(1,0,0,-1,407.72,160.17)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1589"><tspan
x="0"
y="0"
id="tspan1587">3</tspan></text></g></g><path
d="m 303,177.5 h 27.86 v -1 H 303 Z m 26.86,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1591" /><path
d="m 365,176.5 h 27.86 v 1 H 365 Z m 26.86,-2.5 6,3 -6,3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1593" /><path
d="m 147,253 h 9.6 v 30.4 h 19.2 V 253 h 9.6 l -19.2,-19.2 z"
style="fill:#f8cbad;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1595" /><path
d="m 147,253 h 9.6 v 30.4 h 19.2 V 253 h 9.6 l -19.2,-19.2 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1597" /><path
d="m 89.4,212.13 c 0,2.69 2.179,4.87 4.867,4.87 h 19.463 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 H 94.267 c -2.688,0 -4.867,2.18 -4.867,4.87 z"
style="fill:#f8cbad;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1599" /><path
d="m 89.4,212.13 c 0,2.69 2.179,4.87 4.867,4.87 h 19.463 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 H 94.267 c -2.688,0 -4.867,2.18 -4.867,4.87 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1601" /><g
id="g1603"><g
id="g1605"
clip-path="url(#clipPath1609)"><text
transform="matrix(1,0,0,-1,98.875,181.78)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1613"><tspan
x="0"
y="0"
id="tspan1611">A</tspan></text></g></g><g
id="g1615"><g
id="g1617"
clip-path="url(#clipPath1621)"><text
transform="matrix(1,0,0,-1,99.275,160.17)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1625"><tspan
x="0"
y="0"
id="tspan1623">1</tspan></text></g></g><path
d="m 151,212.13 c 0,2.69 2.18,4.87 4.87,4.87 h 19.46 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 h -19.46 c -2.69,0 -4.87,2.18 -4.87,4.87 z"
style="fill:#f8cbad;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1627" /><path
d="m 151,212.13 c 0,2.69 2.18,4.87 4.87,4.87 h 19.46 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 h -19.46 c -2.69,0 -4.87,2.18 -4.87,4.87 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1629" /><g
id="g1631"><g
id="g1633"
clip-path="url(#clipPath1637)"><text
transform="matrix(1,0,0,-1,160.47,181.78)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1641"><tspan
x="0"
y="0"
id="tspan1639">A</tspan></text></g></g><g
id="g1643"><g
id="g1645"
clip-path="url(#clipPath1649)"><text
transform="matrix(1,0,0,-1,160.88,160.17)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1653"><tspan
x="0"
y="0"
id="tspan1651">2</tspan></text></g></g><path
d="m 212.6,212.13 c 0,2.69 2.18,4.87 4.87,4.87 h 19.46 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 h -19.46 c -2.69,0 -4.87,2.18 -4.87,4.87 z"
style="fill:#f8cbad;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path1655" /><path
d="m 212.6,212.13 c 0,2.69 2.18,4.87 4.87,4.87 h 19.46 c 2.69,0 4.87,-2.18 4.87,-4.87 v -69.86 c 0,-2.69 -2.18,-4.87 -4.87,-4.87 h -19.46 c -2.69,0 -4.87,2.18 -4.87,4.87 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path1657" /><g
id="g1659"><g
id="g1661"
clip-path="url(#clipPath1665)"><text
transform="matrix(1,0,0,-1,222.08,181.78)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1669"><tspan
x="0"
y="0"
id="tspan1667">A</tspan></text></g></g><g
id="g1671"><g
id="g1673"
clip-path="url(#clipPath1677)"><text
transform="matrix(1,0,0,-1,222.48,160.17)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text1681"><tspan
x="0"
y="0"
id="tspan1679">3</tspan></text></g></g><path
d="M 118.6,177.5 H 146 v -1 h -27.4 z m 26.4,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1683" /><path
d="m 180.2,177.5 h 27.4 v -1 h -27.4 z m 26.4,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1685" /><path
d="m 66.6,177.5 h 18.137 v -1 H 66.6 Z m 17.137,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1687" /><path
d="m 241.8,176.5 h 26.93 v 1 H 241.8 Z m 25.93,-2.5 6,3 -6,3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1689" /><path
d="m 427,176.5 h 28.59 v 1 H 427 Z m 27.59,-2.5 6,3 -6,3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path1691" /></g></svg>

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
version="1.1"
id="svg512"
xml:space="preserve"
width="641.59998"
height="356.26666"
viewBox="0 0 641.59998 356.26666"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs516"><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath526"><path
d="M 0,1.2207e-4 H 960 V 540.00012 H 0 Z"
clip-rule="evenodd"
id="path524" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath540"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path538" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath560"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path558" /></clipPath></defs><g
id="g518"
transform="matrix(1.3333333,0,0,-1.3333333,-25.066666,641.06665)"><path
d="m 19.4,466.6 c 0,7.51 6.089,13.6 13.601,13.6 H 485.8 c 7.51,0 13.6,-6.09 13.6,-13.6 v -54.4 c 0,-7.51 -6.09,-13.6 -13.6,-13.6 H 33.001 c -7.512,0 -13.601,6.09 -13.601,13.6 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path530" /><path
d="m 19.4,466.6 c 0,7.51 6.089,13.6 13.601,13.6 H 485.8 c 7.51,0 13.6,-6.09 13.6,-13.6 v -54.4 c 0,-7.51 -6.09,-13.6 -13.6,-13.6 H 33.001 c -7.512,0 -13.601,6.09 -13.601,13.6 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path532" /><g
id="g534"><g
id="g536"
clip-path="url(#clipPath540)"><text
transform="matrix(1,0,0,-1,30.4,431.05)"
style="font-variant:normal;font-weight:normal;font-size:24px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text544"><tspan
x="0 13.200001 26.400002 39.600002 52.800003 66.000008 79.200005 92.400009 105.60001 118.80001 132.00002 145.20001 158.78401 171.98401 185.18401 198.38402 211.58401 224.78401 237.98402 251.18402 264.76801 277.96802 291.16803 304.36801 317.56802 330.76804 343.96802 357.16803 370.75201 383.95203 397.53604 410.73602 423.93604 437.13602 450.33603"
y="0"
id="tspan542">d = (a + b*c)*2 + (b*c)/5 + (c*c*b)</tspan></text></g></g><path
d="m 240.2,339 h 9.6 v 35.6 H 269 V 339 h 9.6 l -19.2,-19.2 z"
style="fill:#5b9bd5;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path546" /><path
d="m 240.2,339 h 9.6 v 35.6 H 269 V 339 h 9.6 l -19.2,-19.2 z"
style="fill:none;stroke:#41719c;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path548" /><path
d="m 19.4,282.2 c 0,7.51 6.089,13.6 13.601,13.6 H 485.8 c 7.51,0 13.6,-6.09 13.6,-13.6 v -54.4 c 0,-7.51 -6.09,-13.6 -13.6,-13.6 H 33.001 c -7.512,0 -13.601,6.09 -13.601,13.6 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path550" /><path
d="m 19.4,282.2 c 0,7.51 6.089,13.6 13.601,13.6 H 485.8 c 7.51,0 13.6,-6.09 13.6,-13.6 v -54.4 c 0,-7.51 -6.09,-13.6 -13.6,-13.6 H 33.001 c -7.512,0 -13.601,6.09 -13.601,13.6 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path552" /><g
id="g554"><g
id="g556"
clip-path="url(#clipPath560)"><text
transform="matrix(1,0,0,-1,30.4,246.48)"
style="font-variant:normal;font-weight:normal;font-size:24.025px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text564"><tspan
x="0 13.19995 26.3999 39.59985 52.799801 65.999748 79.199699 92.399651 105.5996 118.79955 131.9995 145.19945 158.7838 171.98375 185.1837 198.38365 211.5836 224.78355 237.98351 251.18346 264.76779 277.96774 291.16769 304.36765 317.5676"
y="0"
id="tspan562">d = (a + E)*2 + E/5 + c*E</tspan></text></g></g></g></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
version="1.1"
id="svg25"
xml:space="preserve"
width="972.79999"
height="243.2"
viewBox="0 0 972.79999 243.2"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs29"><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath39"><path
d="M 0,1.2207e-4 H 960 V 540.00012 H 0 Z"
clip-rule="evenodd"
id="path37" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath53"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path51" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath65"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path63" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath85"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path83" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath97"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path95" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath113"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path111" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath125"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path123" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath141"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path139" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath153"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path151" /></clipPath></defs><g
id="g31"
transform="matrix(1.3333333,0,0,-1.3333333,-25.066666,641.06665)"><path
d="m 19.4,452.87 c 0,15.09 12.238,27.33 27.334,27.33 H 156.07 c 15.09,0 27.33,-12.24 27.33,-27.33 V 326.33 C 183.4,311.24 171.16,299 156.07,299 H 46.734 C 31.638,299 19.4,311.24 19.4,326.33 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path43" /><path
d="m 19.4,452.87 c 0,15.09 12.238,27.33 27.334,27.33 H 156.07 c 15.09,0 27.33,-12.24 27.33,-27.33 V 326.33 C 183.4,311.24 171.16,299 156.07,299 H 46.734 C 31.638,299 19.4,311.24 19.4,326.33 Z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path45" /><g
id="g47"><g
id="g49"
clip-path="url(#clipPath53)"><text
transform="matrix(1,0,0,-1,34.425,395.58)"
style="font-variant:normal;font-weight:normal;font-size:24.025px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text57"><tspan
x="0 13.19995 26.3999 39.59985 52.799801"
y="0"
id="tspan55">a = 2</tspan></text></g></g><g
id="g59"><g
id="g61"
clip-path="url(#clipPath65)"><text
transform="matrix(1,0,0,-1,34.425,366.75)"
style="font-variant:normal;font-weight:normal;font-size:24.025px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text69"><tspan
x="0 13.19995 26.3999 39.59985 52.799801 65.999748 79.199699 92.399651 105.5996"
y="0"
id="tspan67">b = a + 2</tspan></text></g></g><path
d="m 205.4,400 h 52.8 v 10.6 l 21.2,-21.2 -21.2,-21.2 v 10.6 h -52.8 z"
style="fill:#5b9bd5;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path71" /><path
d="m 205.4,400 h 52.8 v 10.6 l 21.2,-21.2 -21.2,-21.2 v 10.6 h -52.8 z"
style="fill:none;stroke:#41719c;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path73" /><path
d="m 301.4,452.87 c 0,15.09 12.24,27.33 27.33,27.33 h 109.34 c 15.09,0 27.33,-12.24 27.33,-27.33 V 326.33 C 465.4,311.24 453.16,299 438.07,299 H 328.73 c -15.09,0 -27.33,12.24 -27.33,27.33 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path75" /><path
d="m 301.4,452.87 c 0,15.09 12.24,27.33 27.33,27.33 h 109.34 c 15.09,0 27.33,-12.24 27.33,-27.33 V 326.33 C 465.4,311.24 453.16,299 438.07,299 H 328.73 c -15.09,0 -27.33,12.24 -27.33,27.33 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path77" /><g
id="g79"><g
id="g81"
clip-path="url(#clipPath85)"><text
transform="matrix(1,0,0,-1,316.7,395.58)"
style="font-variant:normal;font-weight:normal;font-size:24.025px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text89"><tspan
x="0 13.22495 26.4499 39.67485 52.899799"
y="0"
id="tspan87">a = 2</tspan></text></g></g><g
id="g91"><g
id="g93"
clip-path="url(#clipPath97)"><text
transform="matrix(1,0,0,-1,316.7,366.75)"
style="font-variant:normal;font-weight:normal;font-size:24.025px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text101"><tspan
x="0 13.22495 26.4499 39.67485 52.899799 66.124748 79.349701 92.574654 105.7996"
y="0"
id="tspan99">b = 2 + 2</tspan></text></g></g><path
d="m 583.8,452.87 c 0,15.09 12.24,27.33 27.33,27.33 h 109.34 c 15.09,0 27.33,-12.24 27.33,-27.33 V 326.33 C 747.8,311.24 735.56,299 720.47,299 H 611.13 c -15.09,0 -27.33,12.24 -27.33,27.33 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path103" /><path
d="m 583.8,452.87 c 0,15.09 12.24,27.33 27.33,27.33 h 109.34 c 15.09,0 27.33,-12.24 27.33,-27.33 V 326.33 C 747.8,311.24 735.56,299 720.47,299 H 611.13 c -15.09,0 -27.33,12.24 -27.33,27.33 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path105" /><g
id="g107"><g
id="g109"
clip-path="url(#clipPath113)"><text
transform="matrix(1,0,0,-1,599,395.58)"
style="font-variant:normal;font-weight:normal;font-size:24.025px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text117"><tspan
x="0 13.19995 26.3999 39.59985 52.799801"
y="0"
id="tspan115">a = 2</tspan></text></g></g><g
id="g119"><g
id="g121"
clip-path="url(#clipPath125)"><text
transform="matrix(1,0,0,-1,599,366.75)"
style="font-variant:normal;font-weight:normal;font-size:24.025px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text129"><tspan
x="0 13.19995 26.3999 39.59985 52.799801"
y="0"
id="tspan127">b = 4</tspan></text></g></g><path
d="m 487.4,400 h 53.2 v 10.6 l 21.2,-21.2 -21.2,-21.2 v 10.6 h -53.2 z"
style="fill:#5b9bd5;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path131" /><path
d="m 487.4,400 h 53.2 v 10.6 l 21.2,-21.2 -21.2,-21.2 v 10.6 h -53.2 z"
style="fill:none;stroke:#41719c;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path133" /><g
id="g135"><g
id="g137"
clip-path="url(#clipPath141)"><text
transform="matrix(1,0,0,-1,205.9,419.1)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:SimSun;-inkscape-font-specification:SimSun;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text145"><tspan
x="0 18 36 54"
y="0"
id="tspan143">常量传播</tspan></text></g></g><g
id="g147"><g
id="g149"
clip-path="url(#clipPath153)"><text
transform="matrix(1,0,0,-1,488.17,417)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:SimSun;-inkscape-font-specification:SimSun;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text157"><tspan
x="0 18.025 36.049999 54.075001"
y="0"
id="tspan155">常量折叠</tspan></text></g></g></g></svg>

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@@ -0,0 +1,234 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
version="1.1"
id="svg2124"
xml:space="preserve"
width="1214.4"
height="243.2"
viewBox="0 0 1214.4 243.2"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs2128"><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2138"><path
d="M 0,1.2207e-4 H 960 V 540.00012 H 0 Z"
clip-rule="evenodd"
id="path2136" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2152"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path2150" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2164"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path2162" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2176"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path2174" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2188"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path2186" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2204"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path2202" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2216"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path2214" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2228"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path2226" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2240"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path2238" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2252"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path2250" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2264"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path2262" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2276"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path2274" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2288"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path2286" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2300"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path2298" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2312"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path2310" /></clipPath></defs><g
id="g2130"
transform="matrix(1.3333333,0,0,-1.3333333,-34.133332,483.73332)"><path
d="m 26.2,332 c 0,16.68 13.521,30.2 30.2,30.2 h 327.2 c 16.68,0 30.2,-13.52 30.2,-30.2 V 211.2 C 413.8,194.52 400.28,181 383.6,181 H 56.4 c -16.679,0 -30.2,13.52 -30.2,30.2 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path2142" /><path
d="m 26.2,332 c 0,16.68 13.521,30.2 30.2,30.2 h 327.2 c 16.68,0 30.2,-13.52 30.2,-30.2 V 211.2 C 413.8,194.52 400.28,181 383.6,181 H 56.4 c -16.679,0 -30.2,13.52 -30.2,30.2 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path2144" /><g
id="g2146"><g
id="g2148"
clip-path="url(#clipPath2152)"><text
transform="matrix(1,0,0,-1,42.125,306.38)"
style="font-variant:normal;font-weight:normal;font-size:24.025px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text2156"><tspan
x="0 13.19995 26.3999 39.59985 52.799801 65.999748 79.199699 92.399651 105.5996 118.79955"
y="0"
id="tspan2154">x = Net1()</tspan></text></g></g><g
id="g2158"><g
id="g2160"
clip-path="url(#clipPath2164)"><text
transform="matrix(1,0,0,-1,42.125,277.55)"
style="font-variant:normal;font-weight:normal;font-size:24.025px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text2168"><tspan
x="0 13.19995 26.3999 39.59985 52.799801 65.999748 79.199699 92.399651 105.5996 118.79955"
y="0"
id="tspan2166">y = Net2()</tspan></text></g></g><g
id="g2170"><g
id="g2172"
clip-path="url(#clipPath2176)"><text
transform="matrix(1,0,0,-1,42.125,248.73)"
style="font-variant:normal;font-weight:normal;font-size:24.025px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text2180"><tspan
x="0 13.19995 26.3999 39.59985 52.799801 65.999748 79.199699 92.399651 105.5996 118.79955 131.9995 145.19945 158.7838 171.98375 185.1837 198.38365 211.5836 224.78355 237.98351 251.18346 264.76779 277.96774 291.16769 304.36765 317.5676 330.76755"
y="0"
id="tspan2178">z = Net3() # z is not used</tspan></text></g></g><g
id="g2182"><g
id="g2184"
clip-path="url(#clipPath2188)"><text
transform="matrix(1,0,0,-1,42.125,219.9)"
style="font-variant:normal;font-weight:normal;font-size:24.025px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text2192"><tspan
x="0 13.19995 26.3999 39.59985 52.799801 65.999748 79.199699 92.399651 105.5996 118.79955 131.9995 145.19945 158.7838 171.98375"
y="0"
id="tspan2190">d = ops1(x, y)</tspan></text></g></g><path
d="m 560.2,332 c 0,16.68 13.52,30.2 30.2,30.2 h 315.2 c 16.68,0 30.2,-13.52 30.2,-30.2 V 211.2 C 935.8,194.52 922.28,181 905.6,181 H 590.4 c -16.68,0 -30.2,13.52 -30.2,30.2 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path2194" /><path
d="m 560.2,332 c 0,16.68 13.52,30.2 30.2,30.2 h 315.2 c 16.68,0 30.2,-13.52 30.2,-30.2 V 211.2 C 935.8,194.52 922.28,181 905.6,181 H 590.4 c -16.68,0 -30.2,13.52 -30.2,30.2 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path2196" /><g
id="g2198"><g
id="g2200"
clip-path="url(#clipPath2204)"><text
transform="matrix(1,0,0,-1,576.23,291.95)"
style="font-variant:normal;font-weight:normal;font-size:24.025px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text2208"><tspan
x="0"
y="0"
id="tspan2206">x</tspan></text></g></g><g
id="g2210"><g
id="g2212"
clip-path="url(#clipPath2216)"><text
transform="matrix(1,0,0,-1,602.65,291.95)"
style="font-variant:normal;font-weight:normal;font-size:24.025px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text2220"><tspan
x="0"
y="0"
id="tspan2218">=</tspan></text></g></g><g
id="g2222"><g
id="g2224"
clip-path="url(#clipPath2228)"><text
transform="matrix(1,0,0,-1,629.05,291.95)"
style="font-variant:normal;font-weight:normal;font-size:24.025px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text2232"><tspan
x="0 13.19995 26.3999 39.59985 52.799801 65.999748"
y="0"
id="tspan2230">Net1()</tspan></text></g></g><g
id="g2234"><g
id="g2236"
clip-path="url(#clipPath2240)"><text
transform="matrix(1,0,0,-1,576.23,263.13)"
style="font-variant:normal;font-weight:normal;font-size:24px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text2244"><tspan
x="0"
y="0"
id="tspan2242">y</tspan></text></g></g><g
id="g2246"><g
id="g2248"
clip-path="url(#clipPath2252)"><text
transform="matrix(1,0,0,-1,602.65,263.13)"
style="font-variant:normal;font-weight:normal;font-size:24px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text2256"><tspan
x="0"
y="0"
id="tspan2254">=</tspan></text></g></g><g
id="g2258"><g
id="g2260"
clip-path="url(#clipPath2264)"><text
transform="matrix(1,0,0,-1,629.05,263.13)"
style="font-variant:normal;font-weight:normal;font-size:24px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text2268"><tspan
x="0 13.200001 26.400002 39.600002 52.800003 66.000008"
y="0"
id="tspan2266">Net2()</tspan></text></g></g><g
id="g2270"><g
id="g2272"
clip-path="url(#clipPath2276)"><text
transform="matrix(1,0,0,-1,576.23,234.3)"
style="font-variant:normal;font-weight:normal;font-size:24px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text2280"><tspan
x="0"
y="0"
id="tspan2278">d</tspan></text></g></g><g
id="g2282"><g
id="g2284"
clip-path="url(#clipPath2288)"><text
transform="matrix(1,0,0,-1,602.65,234.3)"
style="font-variant:normal;font-weight:normal;font-size:24px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text2292"><tspan
x="0"
y="0"
id="tspan2290">=</tspan></text></g></g><g
id="g2294"><g
id="g2296"
clip-path="url(#clipPath2300)"><text
transform="matrix(1,0,0,-1,629.05,234.3)"
style="font-variant:normal;font-weight:normal;font-size:24px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text2304"><tspan
x="0 13.200001 26.400002 39.600002 52.800003 66.000008 79.200005"
y="0"
id="tspan2302">ops1(x,</tspan></text></g></g><g
id="g2306"><g
id="g2308"
clip-path="url(#clipPath2312)"><text
transform="matrix(1,0,0,-1,735.1,234.3)"
style="font-variant:normal;font-weight:normal;font-size:24px;font-family:Consolas;-inkscape-font-specification:Consolas;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text2316"><tspan
x="0 13.200001"
y="0"
id="tspan2314">y)</tspan></text></g></g><path
d="M 427.8,289 H 511 v 17.6 L 546.2,271.4 511,236.2 v 17.6 h -83.2 z"
style="fill:#5b9bd5;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path2318" /><path
d="M 427.8,289 H 511 v 17.6 L 546.2,271.4 511,236.2 v 17.6 h -83.2 z"
style="fill:none;stroke:#41719c;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path2320" /></g></svg>

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,326 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
version="1.1"
id="svg25"
xml:space="preserve"
width="1030.9867"
height="91.972893"
viewBox="0 0 1030.9867 91.972893"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs29"><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath39"><path
d="M 0,1.2207e-4 H 960 V 540.00012 H 0 Z"
clip-rule="evenodd"
id="path37" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath53"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path51" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath69"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path67" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath85"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path83" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath101"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path99" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath117"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path115" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath133"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path131" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath145"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path143" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath169"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path167" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath181"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path179" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath195"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path193" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath207"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path205" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath219"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path217" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath231"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path229" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath243"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path241" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath255"><path
d="M -6.1035e-5,540 H 960 V 1.2207e-4 H 0"
clip-rule="evenodd"
id="path253" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath265"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path263" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath277"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path275" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath289"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path287" /></clipPath></defs><g
id="g31"
transform="matrix(1.3333333,0,0,-1.3333333,-103.68,437.01289)"><path
d="m 130.56,292.2 h 60.96 v 27.96 h -60.96 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path43" /><path
d="m 130.56,292.2 h 60.96 v 27.96 h -60.96 z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path45" /><g
id="g47"><g
id="g49"
clip-path="url(#clipPath53)"><text
transform="matrix(1,0,0,-1,151.13,302.5)"
style="font-variant:normal;font-weight:normal;font-size:9.96px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text57"><tspan
x="0 9.96"
y="0"
id="tspan55">解析</tspan></text></g></g><path
d="m 243.6,292.2 h 60.96 v 27.96 H 243.6 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path59" /><path
d="m 243.6,292.2 h 60.96 v 27.96 H 243.6 Z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path61" /><g
id="g63"><g
id="g65"
clip-path="url(#clipPath69)"><text
transform="matrix(1,0,0,-1,254.21,302.5)"
style="font-variant:normal;font-weight:normal;font-size:9.96px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text73"><tspan
x="0 9.96 19.92 29.879999"
y="0"
id="tspan71">静态分析</tspan></text></g></g><path
d="m 356.64,292.2 h 60.96 v 27.96 h -60.96 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path75" /><path
d="m 356.64,292.2 h 60.96 v 27.96 h -60.96 z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path77" /><g
id="g79"><g
id="g81"
clip-path="url(#clipPath85)"><text
transform="matrix(1,0,0,-1,367.25,302.5)"
style="font-variant:normal;font-weight:normal;font-size:9.96px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text89"><tspan
x="0 9.96 19.92 29.879999"
y="0"
id="tspan87">前端优化</tspan></text></g></g><path
d="m 469.68,292.2 h 60.96 v 27.96 h -60.96 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path91" /><path
d="m 469.68,292.2 h 60.96 v 27.96 h -60.96 z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path93" /><g
id="g95"><g
id="g97"
clip-path="url(#clipPath101)"><text
transform="matrix(1,0,0,-1,480.29,302.5)"
style="font-variant:normal;font-weight:normal;font-size:9.96px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text105"><tspan
x="0 9.96 19.92 29.879999"
y="0"
id="tspan103">自动微分</tspan></text></g></g><path
d="m 582.6,292.2 h 61.08 v 27.96 H 582.6 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path107" /><path
d="m 582.6,292.2 h 61.08 v 27.96 H 582.6 Z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path109" /><g
id="g111"><g
id="g113"
clip-path="url(#clipPath117)"><text
transform="matrix(1,0,0,-1,593.33,302.5)"
style="font-variant:normal;font-weight:normal;font-size:9.96px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text121"><tspan
x="0 9.96 19.92 29.879999"
y="0"
id="tspan119">前端优化</tspan></text></g></g><path
d="m 695.64,306.18 50.76,21.06 50.76,-21.06 -50.76,-21.06 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path123" /><path
d="m 695.64,306.18 50.76,21.06 50.76,-21.06 -50.76,-21.06 z"
style="fill:none;stroke:#000000;stroke-width:0.96;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path125" /><g
id="g127"><g
id="g129"
clip-path="url(#clipPath133)"><text
transform="matrix(1,0,0,-1,728.52,307.66)"
style="font-variant:normal;font-weight:normal;font-size:9px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text137"><tspan
x="0 9 18 27"
y="0"
id="tspan135">是否继续</tspan></text></g></g><g
id="g139"><g
id="g141"
clip-path="url(#clipPath145)"><text
transform="matrix(1,0,0,-1,737.52,297.46)"
style="font-variant:normal;font-weight:normal;font-size:9px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text149"><tspan
x="0 9"
y="0"
id="tspan147">优化</tspan></text></g></g><path
d="m 191.52,306.62 h 47.05 v -1 h -47.05 z m 46.05,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path151" /><path
d="m 304.56,305.62 h 47.05 v 1 h -47.05 z m 46.05,-2.5 6,3 -6,3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path153" /><path
d="m 417.6,306.62 h 47.05 v -1 H 417.6 Z m 46.05,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path155" /><path
d="m 530.64,306.62 h 47.05 v -1 h -47.05 z m 46.05,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path157" /><path
d="m 643.68,305.62 h 47.05 v 1 h -47.05 z m 46.05,-2.5 6,3 -6,3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path159" /><path
d="m 797.16,305.67 48.84,0.06 v 1 l -48.84,-0.06 z m 47.84,-2.44 6,3.01 -6.01,2.99 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path161" /><g
id="g163"><g
id="g165"
clip-path="url(#clipPath169)"><text
transform="matrix(1,0,0,-1,197.21,308.81)"
style="font-variant:normal;font-weight:normal;font-size:9.984px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text173"><tspan
x="0 9.96 19.92 29.879999"
y="0"
id="tspan171">中间表示</tspan></text></g></g><g
id="g175"><g
id="g177"
clip-path="url(#clipPath181)"><text
transform="matrix(1,0,0,-1,86.352,308.81)"
style="font-variant:normal;font-weight:normal;font-size:9.984px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text185"><tspan
x="0 9.96 19.92"
y="0"
id="tspan183">源程序</tspan></text></g></g><path
d="m 77.76,306.74 h 47.05 v -1 H 77.76 Z m 46.05,2.5 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path187" /><g
id="g189"><g
id="g191"
clip-path="url(#clipPath195)"><text
transform="matrix(1,0,0,-1,310.37,308.81)"
style="font-variant:normal;font-weight:normal;font-size:9.984px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text199"><tspan
x="0 9.96 19.92 29.879999"
y="0"
id="tspan197">中间表示</tspan></text></g></g><g
id="g201"><g
id="g203"
clip-path="url(#clipPath207)"><text
transform="matrix(1,0,0,-1,423.72,308.81)"
style="font-variant:normal;font-weight:normal;font-size:9.96px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text211"><tspan
x="0 9.96 19.92 29.879999"
y="0"
id="tspan209">中间表示</tspan></text></g></g><g
id="g213"><g
id="g215"
clip-path="url(#clipPath219)"><text
transform="matrix(1,0,0,-1,536.23,308.81)"
style="font-variant:normal;font-weight:normal;font-size:9.96px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text223"><tspan
x="0 9.96 19.92 29.879999"
y="0"
id="tspan221">中间表示</tspan></text></g></g><g
id="g225"><g
id="g227"
clip-path="url(#clipPath231)"><text
transform="matrix(1,0,0,-1,648.05,308.81)"
style="font-variant:normal;font-weight:normal;font-size:9.96px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text235"><tspan
x="0 9.96 19.92 29.879999"
y="0"
id="tspan233">中间表示</tspan></text></g></g><g
id="g237"><g
id="g239"
clip-path="url(#clipPath243)"><text
transform="matrix(1,0,0,-1,801.41,308.81)"
style="font-variant:normal;font-weight:normal;font-size:9.96px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text247"><tspan
x="0 9.96 19.92 29.879999"
y="0"
id="tspan245">中间表示</tspan></text></g></g><g
id="g249"><g
id="g251"
clip-path="url(#clipPath255)"><path
d="m 745.96,285.07 v -25.79 l 0.5,0.5 H 274.08 l 0.5,-0.5 v 27.92 h -1 v -28.42 h 473.38 v 26.29 z m -468.88,1.13 -3,6 -3,-6 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path257" /></g></g><g
id="g259"><g
id="g261"
clip-path="url(#clipPath265)"><text
transform="matrix(1,0,0,-1,284.21,263.06)"
style="font-variant:normal;font-weight:normal;font-size:9.96px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text269"><tspan
x="0 9.96 19.92 29.879999"
y="0"
id="tspan267">中间表示</tspan></text></g></g><g
id="g271"><g
id="g273"
clip-path="url(#clipPath277)"><text
transform="matrix(1,0,0,-1,791.69,289.46)"
style="font-variant:normal;font-weight:normal;font-size:9.96px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text281"><tspan
x="0"
y="0"
id="tspan279"></tspan></text></g></g><g
id="g283"><g
id="g285"
clip-path="url(#clipPath289)"><text
transform="matrix(1,0,0,-1,730.54,273.14)"
style="font-variant:normal;font-weight:normal;font-size:9.96px;font-family:'宋体';-inkscape-font-specification:'宋体';writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text293"><tspan
x="0"
y="0"
id="tspan291"></tspan></text></g></g></g></svg>

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -0,0 +1,810 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
version="1.1"
id="svg25"
xml:space="preserve"
width="1031.3199"
height="340.83304"
viewBox="0 0 1031.3199 340.83304"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs29"><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath39"><path
d="M 0,1.2207e-4 H 960 V 540.00012 H 0 Z"
clip-rule="evenodd"
id="path37" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath53"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path51" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath65"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path63" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath77"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path75" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath93"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path91" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath105"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path103" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath121"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path119" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath133"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path131" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath149"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path147" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath161"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path159" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath177"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path175" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath189"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path187" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath205"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path203" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath217"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path215" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath233"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path231" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath245"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path243" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath279"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path277" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath291"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path289" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath303"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path301" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath315"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path313" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath327"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path325" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath339"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path337" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath351"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path349" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath363"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path361" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath375"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path373" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath387"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path385" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath399"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path397" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath411"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path409" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath423"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path421" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath435"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path433" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath447"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path445" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath459"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path457" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath471"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path469" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath483"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path481" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath495"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path493" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath507"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path505" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath519"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path517" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath531"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path529" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath543"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path541" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath555"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path553" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath567"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path565" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath579"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path577" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath591"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path589" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath603"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path601" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath615"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path613" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath627"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path625" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath639"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path637" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath651"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path649" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath663"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path661" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath675"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path673" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath687"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path685" /></clipPath><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath699"><path
d="M 1.4305e-5,0 H 960.00001 V 540 H 1.4305e-5 Z"
clip-rule="evenodd"
id="path697" /></clipPath></defs><g
id="g31"
transform="matrix(1.3333333,0,0,-1.3333333,-63.199998,614.43302)"><g
id="g33" /><path
d="m 138.6,406.4 c 0,14.25 11.73,25.8 26.2,25.8 14.47,0 26.2,-11.55 26.2,-25.8 0,-14.25 -11.73,-25.8 -26.2,-25.8 -14.47,0 -26.2,11.55 -26.2,25.8 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path43" /><path
d="m 138.6,406.4 c 0,14.25 11.73,25.8 26.2,25.8 14.47,0 26.2,-11.55 26.2,-25.8 0,-14.25 -11.73,-25.8 -26.2,-25.8 -14.47,0 -26.2,11.55 -26.2,25.8 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path45" /><g
id="g47"><g
id="g49"
clip-path="url(#clipPath53)"><text
transform="matrix(1,0,0,-1,157.02,401.33)"
style="font-variant:normal;font-weight:normal;font-size:16.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text57"><tspan
x="0"
y="0"
id="tspan55">V</tspan></text></g></g><g
id="g59"><g
id="g61"
clip-path="url(#clipPath65)"><text
transform="matrix(1,0,0,-1,166.23,397.73)"
style="font-variant:normal;font-weight:normal;font-size:10.825px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text69"><tspan
x="0"
y="0"
id="tspan67">-</tspan></text></g></g><g
id="g71"><g
id="g73"
clip-path="url(#clipPath77)"><text
transform="matrix(1,0,0,-1,169.43,397.73)"
style="font-variant:normal;font-weight:normal;font-size:10.825px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text81"><tspan
x="0"
y="0"
id="tspan79">1</tspan></text></g></g><path
d="m 138.6,231.6 c 0,14.25 11.73,25.8 26.2,25.8 14.47,0 26.2,-11.55 26.2,-25.8 0,-14.25 -11.73,-25.8 -26.2,-25.8 -14.47,0 -26.2,11.55 -26.2,25.8 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path83" /><path
d="m 138.6,231.6 c 0,14.25 11.73,25.8 26.2,25.8 14.47,0 26.2,-11.55 26.2,-25.8 0,-14.25 -11.73,-25.8 -26.2,-25.8 -14.47,0 -26.2,11.55 -26.2,25.8 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path85" /><g
id="g87"><g
id="g89"
clip-path="url(#clipPath93)"><text
transform="matrix(1,0,0,-1,157.08,226.32)"
style="font-variant:normal;font-weight:normal;font-size:16px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text97"><tspan
x="0"
y="0"
id="tspan95">V</tspan></text></g></g><g
id="g99"><g
id="g101"
clip-path="url(#clipPath105)"><text
transform="matrix(1,0,0,-1,166.28,222.73)"
style="font-variant:normal;font-weight:normal;font-size:10.8px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text109"><tspan
x="0"
y="0"
id="tspan107">0</tspan></text></g></g><path
d="m 327.8,406.4 c 0,14.25 11.64,25.8 26,25.8 14.36,0 26,-11.55 26,-25.8 0,-14.25 -11.64,-25.8 -26,-25.8 -14.36,0 -26,11.55 -26,25.8 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path111" /><path
d="m 327.8,406.4 c 0,14.25 11.64,25.8 26,25.8 14.36,0 26,-11.55 26,-25.8 0,-14.25 -11.64,-25.8 -26,-25.8 -14.36,0 -26,11.55 -26,25.8 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path113" /><g
id="g115"><g
id="g117"
clip-path="url(#clipPath121)"><text
transform="matrix(1,0,0,-1,346,401.33)"
style="font-variant:normal;font-weight:normal;font-size:16.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text125"><tspan
x="0"
y="0"
id="tspan123">V</tspan></text></g></g><g
id="g127"><g
id="g129"
clip-path="url(#clipPath133)"><text
transform="matrix(1,0,0,-1,355.2,397.73)"
style="font-variant:normal;font-weight:normal;font-size:10.825px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text137"><tspan
x="0"
y="0"
id="tspan135">1</tspan></text></g></g><path
d="m 328.2,310.4 c 0,14.25 11.73,25.8 26.2,25.8 14.47,0 26.2,-11.55 26.2,-25.8 0,-14.25 -11.73,-25.8 -26.2,-25.8 -14.47,0 -26.2,11.55 -26.2,25.8 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path139" /><path
d="m 328.2,310.4 c 0,14.25 11.73,25.8 26.2,25.8 14.47,0 26.2,-11.55 26.2,-25.8 0,-14.25 -11.73,-25.8 -26.2,-25.8 -14.47,0 -26.2,11.55 -26.2,25.8 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path141" /><g
id="g143"><g
id="g145"
clip-path="url(#clipPath149)"><text
transform="matrix(1,0,0,-1,346.7,305.17)"
style="font-variant:normal;font-weight:normal;font-size:16.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text153"><tspan
x="0"
y="0"
id="tspan151">V</tspan></text></g></g><g
id="g155"><g
id="g157"
clip-path="url(#clipPath161)"><text
transform="matrix(1,0,0,-1,355.9,301.58)"
style="font-variant:normal;font-weight:normal;font-size:10.825px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text165"><tspan
x="0"
y="0"
id="tspan163">2</tspan></text></g></g><path
d="m 516.6,406.4 c 0,14.25 11.64,25.8 26,25.8 14.36,0 26,-11.55 26,-25.8 0,-14.25 -11.64,-25.8 -26,-25.8 -14.36,0 -26,11.55 -26,25.8 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path167" /><path
d="m 516.6,406.4 c 0,14.25 11.64,25.8 26,25.8 14.36,0 26,-11.55 26,-25.8 0,-14.25 -11.64,-25.8 -26,-25.8 -14.36,0 -26,11.55 -26,25.8 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path169" /><g
id="g171"><g
id="g173"
clip-path="url(#clipPath177)"><text
transform="matrix(1,0,0,-1,535,401.33)"
style="font-variant:normal;font-weight:normal;font-size:16.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text181"><tspan
x="0"
y="0"
id="tspan179">V</tspan></text></g></g><g
id="g183"><g
id="g185"
clip-path="url(#clipPath189)"><text
transform="matrix(1,0,0,-1,544.2,397.73)"
style="font-variant:normal;font-weight:normal;font-size:10.825px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text193"><tspan
x="0"
y="0"
id="tspan191">4</tspan></text></g></g><path
d="m 517.8,231.6 c 0,14.25 11.64,25.8 26,25.8 14.36,0 26,-11.55 26,-25.8 0,-14.25 -11.64,-25.8 -26,-25.8 -14.36,0 -26,11.55 -26,25.8 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path195" /><path
d="m 517.8,231.6 c 0,14.25 11.64,25.8 26,25.8 14.36,0 26,-11.55 26,-25.8 0,-14.25 -11.64,-25.8 -26,-25.8 -14.36,0 -26,11.55 -26,25.8 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path197" /><g
id="g199"><g
id="g201"
clip-path="url(#clipPath205)"><text
transform="matrix(1,0,0,-1,536.35,226.32)"
style="font-variant:normal;font-weight:normal;font-size:16px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text209"><tspan
x="0"
y="0"
id="tspan207">V</tspan></text></g></g><g
id="g211"><g
id="g213"
clip-path="url(#clipPath217)"><text
transform="matrix(1,0,0,-1,545.55,222.73)"
style="font-variant:normal;font-weight:normal;font-size:10.8px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text221"><tspan
x="0"
y="0"
id="tspan219">3</tspan></text></g></g><path
d="m 674.6,310.4 c 0,14.25 11.64,25.8 26,25.8 14.36,0 26,-11.55 26,-25.8 0,-14.25 -11.64,-25.8 -26,-25.8 -14.36,0 -26,11.55 -26,25.8 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path223" /><path
d="m 674.6,310.4 c 0,14.25 11.64,25.8 26,25.8 14.36,0 26,-11.55 26,-25.8 0,-14.25 -11.64,-25.8 -26,-25.8 -14.36,0 -26,11.55 -26,25.8 z"
style="fill:none;stroke:#000000;stroke-width:1.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
id="path225" /><g
id="g227"><g
id="g229"
clip-path="url(#clipPath233)"><text
transform="matrix(1,0,0,-1,693.13,305.17)"
style="font-variant:normal;font-weight:normal;font-size:16.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text237"><tspan
x="0"
y="0"
id="tspan235">V</tspan></text></g></g><g
id="g239"><g
id="g241"
clip-path="url(#clipPath245)"><text
transform="matrix(1,0,0,-1,702.33,301.58)"
style="font-variant:normal;font-weight:normal;font-size:10.825px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text249"><tspan
x="0"
y="0"
id="tspan247">5</tspan></text></g></g><path
d="m 191,406 h 131.8 v 1.2 H 191 Z m 130.8,-2.4 6,3 -6,3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path251" /><path
d="M 191.28,407.13 332,331.6 l -0.56,-1.06 -140.72,75.53 z m 140.98,-72.94 3.87,-5.48 -6.71,0.19 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path253" /><path
d="m 191.23,231.05 140.46,58.67 -0.46,1.11 -140.46,-58.67 z m 140.46,56.07 4.38,5.08 -6.69,0.46 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path255" /><path
d="m 379.8,406 h 131.8 v 1.2 H 379.8 Z m 130.8,-2.4 6,3 -6,3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path257" /><path
d="m 380.89,309.78 139.34,75.51 -0.57,1.06 -139.35,-75.52 z m 139.6,72.92 3.85,5.5 -6.71,-0.22 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path259" /><path
d="M 191,230.8 H 513 V 232 H 191 Z m 321,-2.4 6,3 -6,3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path261" /><path
d="m 570.08,231.07 107.84,58.23 -0.57,1.05 -107.84,-58.22 z m 108.1,55.64 3.86,5.49 -6.71,-0.21 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path263" /><path
d="m 568.94,407.09 109.45,-75.06 -0.68,-0.99 -109.45,75.07 z m 109.98,-72.52 3.25,-5.86 -6.64,0.92 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path265" /><path
d="m 726.6,309.6 h 89.29 v 1.2 H 726.6 Z m 88.29,-2.4 6,3 -6,3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path267" /><path
d="m 47.4,407.2 h 86.2 V 406 H 47.4 Z m 85.2,2.4 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path269" /><path
d="m 47.4,232 h 86.2 v -1.2 H 47.4 Z m 85.2,2.4 6,-3 -6,-3 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path271" /><g
id="g273"><g
id="g275"
clip-path="url(#clipPath279)"><text
transform="matrix(1,0,0,-1,57.7,418.02)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text283"><tspan
x="0"
y="0"
id="tspan281">X</tspan></text></g></g><g
id="g285"><g
id="g287"
clip-path="url(#clipPath291)"><text
transform="matrix(1,0,0,-1,66.9,414.02)"
style="font-variant:normal;font-weight:normal;font-size:12.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text295"><tspan
x="0"
y="0"
id="tspan293">1</tspan></text></g></g><g
id="g297"><g
id="g299"
clip-path="url(#clipPath303)"><text
transform="matrix(1,0,0,-1,59.35,243.2)"
style="font-variant:normal;font-weight:normal;font-size:18px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text307"><tspan
x="0"
y="0"
id="tspan305">X</tspan></text></g></g><g
id="g309"><g
id="g311"
clip-path="url(#clipPath315)"><text
transform="matrix(1,0,0,-1,68.55,239.2)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text319"><tspan
x="0"
y="0"
id="tspan317">2</tspan></text></g></g><g
id="g321"><g
id="g323"
clip-path="url(#clipPath327)"><text
transform="matrix(1,0,0,-1,328.6,447.13)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text331"><tspan
x="0"
y="0"
id="tspan329">V</tspan></text></g></g><g
id="g333"><g
id="g335"
clip-path="url(#clipPath339)"><text
transform="matrix(1,0,0,-1,339,443.13)"
style="font-variant:normal;font-weight:normal;font-size:12.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text343"><tspan
x="0"
y="0"
id="tspan341">1</tspan></text></g></g><g
id="g345"><g
id="g347"
clip-path="url(#clipPath351)"><text
transform="matrix(1,0,0,-1,349.4,447.13)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text355"><tspan
x="0"
y="0"
id="tspan353">=</tspan></text></g></g><g
id="g357"><g
id="g359"
clip-path="url(#clipPath363)"><text
transform="matrix(1,0,0,-1,362.2,447.13)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text367"><tspan
x="0 4.0195751 13.6269 19.232676"
y="0"
id="tspan365">ln(V</tspan></text></g></g><g
id="g369"><g
id="g371"
clip-path="url(#clipPath375)"><text
transform="matrix(1,0,0,-1,391.82,443.13)"
style="font-variant:normal;font-weight:normal;font-size:12.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text379"><tspan
x="0"
y="0"
id="tspan377">-</tspan></text></g></g><g
id="g381"><g
id="g383"
clip-path="url(#clipPath387)"><text
transform="matrix(1,0,0,-1,395.42,443.13)"
style="font-variant:normal;font-weight:normal;font-size:12.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text391"><tspan
x="0"
y="0"
id="tspan389">1</tspan></text></g></g><g
id="g393"><g
id="g395"
clip-path="url(#clipPath399)"><text
transform="matrix(1,0,0,-1,401.43,447.13)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text403"><tspan
x="0"
y="0"
id="tspan401">)</tspan></text></g></g><g
id="g405"><g
id="g407"
clip-path="url(#clipPath411)"><text
transform="matrix(1,0,0,-1,502.43,279.73)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text415"><tspan
x="0"
y="0"
id="tspan413">V</tspan></text></g></g><g
id="g417"><g
id="g419"
clip-path="url(#clipPath423)"><text
transform="matrix(1,0,0,-1,512.83,275.7)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text427"><tspan
x="0"
y="0"
id="tspan425">3</tspan></text></g></g><g
id="g429"><g
id="g431"
clip-path="url(#clipPath435)"><text
transform="matrix(1,0,0,-1,522.85,279.73)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text439"><tspan
x="0 8.7961998 13.176275 20.350225 24.351774 33.941074 39.528824"
y="0"
id="tspan437">= sin(V</tspan></text></g></g><g
id="g441"><g
id="g443"
clip-path="url(#clipPath447)"><text
transform="matrix(1,0,0,-1,572.85,275.7)"
style="font-variant:normal;font-weight:normal;font-size:12px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text451"><tspan
x="0"
y="0"
id="tspan449">0</tspan></text></g></g><g
id="g453"><g
id="g455"
clip-path="url(#clipPath459)"><text
transform="matrix(1,0,0,-1,578.88,279.73)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text463"><tspan
x="0"
y="0"
id="tspan461">)</tspan></text></g></g><g
id="g465"><g
id="g467"
clip-path="url(#clipPath471)"><text
transform="matrix(1,0,0,-1,330.02,348.83)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text475"><tspan
x="0"
y="0"
id="tspan473">V</tspan></text></g></g><g
id="g477"><g
id="g479"
clip-path="url(#clipPath483)"><text
transform="matrix(1,0,0,-1,340.43,344.83)"
style="font-variant:normal;font-weight:normal;font-size:12.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text487"><tspan
x="0"
y="0"
id="tspan485">2</tspan></text></g></g><g
id="g489"><g
id="g491"
clip-path="url(#clipPath495)"><text
transform="matrix(1,0,0,-1,350.42,348.83)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text499"><tspan
x="0 8.8322496 13.212325"
y="0"
id="tspan497">= V</tspan></text></g></g><g
id="g501"><g
id="g503"
clip-path="url(#clipPath507)"><text
transform="matrix(1,0,0,-1,374.05,344.83)"
style="font-variant:normal;font-weight:normal;font-size:12.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text511"><tspan
x="0"
y="0"
id="tspan509">-</tspan></text></g></g><g
id="g513"><g
id="g515"
clip-path="url(#clipPath519)"><text
transform="matrix(1,0,0,-1,377.65,344.83)"
style="font-variant:normal;font-weight:normal;font-size:12.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text523"><tspan
x="0"
y="0"
id="tspan521">1</tspan></text></g></g><g
id="g525"><g
id="g527"
clip-path="url(#clipPath531)"><text
transform="matrix(1,0,0,-1,383.65,348.83)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text535"><tspan
x="0"
y="0"
id="tspan533">V</tspan></text></g></g><g
id="g537"><g
id="g539"
clip-path="url(#clipPath543)"><text
transform="matrix(1,0,0,-1,394.05,344.83)"
style="font-variant:normal;font-weight:normal;font-size:12.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text547"><tspan
x="0"
y="0"
id="tspan545">0</tspan></text></g></g><g
id="g549"><g
id="g551"
clip-path="url(#clipPath555)"><text
transform="matrix(1,0,0,-1,502.43,447.13)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text559"><tspan
x="0"
y="0"
id="tspan557">V</tspan></text></g></g><g
id="g561"><g
id="g563"
clip-path="url(#clipPath567)"><text
transform="matrix(1,0,0,-1,512.83,443.13)"
style="font-variant:normal;font-weight:normal;font-size:12.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text571"><tspan
x="0"
y="0"
id="tspan569">4</tspan></text></g></g><g
id="g573"><g
id="g575"
clip-path="url(#clipPath579)"><text
transform="matrix(1,0,0,-1,522.85,447.13)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text583"><tspan
x="0 8.7961998 13.176275"
y="0"
id="tspan581">= V</tspan></text></g></g><g
id="g585"><g
id="g587"
clip-path="url(#clipPath591)"><text
transform="matrix(1,0,0,-1,546.45,443.13)"
style="font-variant:normal;font-weight:normal;font-size:12.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text595"><tspan
x="0"
y="0"
id="tspan593">1</tspan></text></g></g><g
id="g597"><g
id="g599"
clip-path="url(#clipPath603)"><text
transform="matrix(1,0,0,-1,556.45,447.13)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text607"><tspan
x="0 8.7961998 13.176275"
y="0"
id="tspan605">+ V</tspan></text></g></g><g
id="g609"><g
id="g611"
clip-path="url(#clipPath615)"><text
transform="matrix(1,0,0,-1,580.07,443.13)"
style="font-variant:normal;font-weight:normal;font-size:12.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text619"><tspan
x="0"
y="0"
id="tspan617">2</tspan></text></g></g><g
id="g621"><g
id="g623"
clip-path="url(#clipPath627)"><text
transform="matrix(1,0,0,-1,681.9,348.83)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text631"><tspan
x="0"
y="0"
id="tspan629">V</tspan></text></g></g><g
id="g633"><g
id="g635"
clip-path="url(#clipPath639)"><text
transform="matrix(1,0,0,-1,692.3,344.83)"
style="font-variant:normal;font-weight:normal;font-size:12.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text643"><tspan
x="0"
y="0"
id="tspan641">5</tspan></text></g></g><g
id="g645"><g
id="g647"
clip-path="url(#clipPath651)"><text
transform="matrix(1,0,0,-1,702.3,348.83)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text655"><tspan
x="0 8.7961998 13.176275"
y="0"
id="tspan653">= V</tspan></text></g></g><g
id="g657"><g
id="g659"
clip-path="url(#clipPath663)"><text
transform="matrix(1,0,0,-1,725.9,344.83)"
style="font-variant:normal;font-weight:normal;font-size:12.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text667"><tspan
x="0"
y="0"
id="tspan665">4</tspan></text></g></g><g
id="g669"><g
id="g671"
clip-path="url(#clipPath675)"><text
transform="matrix(1,0,0,-1,735.9,348.83)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text679"><tspan
x="0"
y="0"
id="tspan677">-</tspan></text></g></g><g
id="g681"><g
id="g683"
clip-path="url(#clipPath687)"><text
transform="matrix(1,0,0,-1,745.53,348.83)"
style="font-variant:normal;font-weight:normal;font-size:18.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text691"><tspan
x="0"
y="0"
id="tspan689">V</tspan></text></g></g><g
id="g693"><g
id="g695"
clip-path="url(#clipPath699)"><text
transform="matrix(1,0,0,-1,755.93,344.83)"
style="font-variant:normal;font-weight:normal;font-size:12.025px;font-family:Calibri;-inkscape-font-specification:Calibri;writing-mode:lr-tb;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="text703"><tspan
x="0"
y="0"
id="tspan701">3</tspan></text></g></g></g></svg>

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
img/ch07/7.1/pipeline.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
img/ch07/7.2/RDD.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
img/ch07/7.2/dataset.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
img/ch07/7.2/operation.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
img/ch07/7.3/map_reduce.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
img/ch07/7.3/partition.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
img/ch07/7.3/uni_record.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
img/ch08/AttentionTS.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 KiB

BIN
img/ch08/bn-replace.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
img/ch08/conv-bn-fusion.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
img/ch08/conv_2d.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
img/ch08/conv_nhwc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
img/ch08/crop-reorder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
img/ch08/deepcomp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

BIN
img/ch08/flow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
img/ch08/fmla.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
img/ch08/gemm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
img/ch08/img2col_input.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
img/ch08/img2col_weight.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
img/ch08/parallel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
img/ch08/quant-minmax.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Some files were not shown because too many files have changed in this diff Show More