mirror of
https://github.com/openmlsys/openmlsys-zh.git
synced 2026-04-24 10:34:34 +08:00
Update Automatic reference
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
## 计算图的基本构成
|
||||
|
||||
计算图是用来表示深度学习网络模型在训练与推理过程中计算逻辑与状态的工具。计算框架在后端会将前端语言构建的神经网络模型前向计算与反向梯度计算以计算图的形式来进行表示。计算图由基本数据结构张量(Tensor)和基本运算单元算子(Operator)构成。在计算图中通常使用节点来表示算子,节点间的有向线段来表示张量状态,同时也描述了计算间的依赖关系。如图3.2.1所示,将$\boldsymbol{Z}=relu(\boldsymbol{X}*\boldsymbol{Y})$转化为计算图表示,数据流将根据图中流向与算子进行前向计算和反向梯度计算来更新图中张量状态,以此达到训练模型的目的。
|
||||
计算图是用来表示深度学习网络模型在训练与推理过程中计算逻辑与状态的工具。计算框架在后端会将前端语言构建的神经网络模型前向计算与反向梯度计算以计算图的形式来进行表示。计算图由基本数据结构张量(Tensor)和基本运算单元算子(Operator)构成。在计算图中通常使用节点来表示算子,节点间的有向线段来表示张量状态,同时也描述了计算间的依赖关系。如 :numref:`simpledag`所示,将$\boldsymbol{Z}=relu(\boldsymbol{X}*\boldsymbol{Y})$转化为计算图表示,数据流将根据图中流向与算子进行前向计算和反向梯度计算来更新图中张量状态,以此达到训练模型的目的。
|
||||
|
||||

|
||||
:width:`300px`
|
||||
@@ -22,12 +22,12 @@
|
||||
|
||||
张量的形状是一个重要的属性,它记录了每个轴的长度,也就是张量每个维度的元素数量。秩则代表张量的轴数或者阶数。张量中通常可以保存布尔类型、浮点数、整型数以及复数和字符串数据。每一个张量都具有唯一的数据类型,在计算过程中会对所有参与运算的张量进行类型检查,当发现类型不匹配时就会报错。部分特殊的计算则必须使用指定的数据类型,比如逻辑运算应为布尔类型。在部分计算框架中张量的属性中包含可以指明张量存储的设备位置,比如存储于CPU、GPU等。张量数据的存储状态可以分为可变和不可变两种,不可变张量一般用于用户初始化的数据或者网络模型输入的数据;而可变张量则存储网络权重参数,根据梯度信息更新自身数据。
|
||||
|
||||
如图3.2.2所示,标量就是一个零阶张量,包含单个数值但没有轴信息。向量即为一阶张量,具有一个轴。二阶张量具有两个轴即秩为二。
|
||||
如 :numref:`tensor`,标量就是一个零阶张量,包含单个数值但没有轴信息。向量即为一阶张量,具有一个轴。二阶张量具有两个轴即秩为二。
|
||||
|
||||

|
||||
:width:`800px`
|
||||
:label:`tensor`
|
||||
通常我们使用的张量是"整齐"的,每个轴上的具有相同的元素个数,就像一个"矩形"或者"立方体"。在特定的环境中,也会使用特殊类型的张量,比如不规则张量和稀疏张量,如图3.2.3中所示。不规则张量在某个轴上可能具有不同的元素个数,它们支持存储和处理包含非均匀形状的数据,在自然语言处理领域,不规则张量可以存储不同长度文本的信息。稀疏张量则通常应用于图数据与图神经网络中,采用特殊的存储格式如坐标表格式(Coordinate
|
||||
通常我们使用的张量是"整齐"的,每个轴上的具有相同的元素个数,就像一个"矩形"或者"立方体"。在特定的环境中,也会使用特殊类型的张量,比如不规则张量和稀疏张量,如 :numref:`tensorclass`中所示。不规则张量在某个轴上可能具有不同的元素个数,它们支持存储和处理包含非均匀形状的数据,在自然语言处理领域,不规则张量可以存储不同长度文本的信息。稀疏张量则通常应用于图数据与图神经网络中,采用特殊的存储格式如坐标表格式(Coordinate
|
||||
List, COO),可以高效存储稀疏数据,节省存储空间。
|
||||
|
||||

|
||||
@@ -52,7 +52,7 @@ List, COO),可以高效存储稀疏数据,节省存储空间。
|
||||
:width:`400px`
|
||||
:label:`dependence`
|
||||
|
||||
如图3.2.4中所示,在此简单的计算图中,若将$\mathbf{Matmul1}$算子移除则该节点无输出,导致后续的激活函数无法得到输入,从而计算图中的数据流动中断,这表明计算图中的算子间具有依赖关系并且存在传递性。我们对依赖关系进行区分如下:
|
||||
如 :numref:`dependence`中所示,在此简单的计算图中,若将$\mathbf{Matmul1}$算子移除则该节点无输出,导致后续的激活函数无法得到输入,从而计算图中的数据流动中断,这表明计算图中的算子间具有依赖关系并且存在传递性。我们对依赖关系进行区分如下:
|
||||
|
||||
- **直接依赖**:节点$\mathbf{ReLU1}$直接依赖于节点$\mathbf{Matmul1}$,即如果节点$\mathbf{ReLU1}$要执行运算,必须接受直接来自节点$\mathbf{Matmul1}$的输出数据;
|
||||
|
||||
@@ -60,13 +60,13 @@ List, COO),可以高效存储稀疏数据,节省存储空间。
|
||||
|
||||
- **相互独立**:在计算图中节点节点$\mathbf{Matmul1}$与节点$\mathbf{Matmul2}$之间并无数据输入输出依赖关系,所以这两个节点间相互独立。
|
||||
|
||||
掌握依赖关系后,分析图3.2.5可以得出节点$\mathbf{Add}$间接依赖于节点$\mathbf{Matmul}$,而节点$\mathbf{Matmul}$直接依赖于节点$\mathbf{Add}$,此时两个节点互相等待对方计算完成输出数据,将无法执行计算任务。若我们手动同时给两个节点赋予输入,计算将持续不间断进行,模型训练将无法停止造成死循环。循环依赖产生正反馈数据流,被传递的数值可能在正方向上无限放大,导致数值上溢,或者负方向上放大导致数值下溢,也可能导致数值无限逼近于0,这些情况都会致使模型训练无法得到预期结果。在构建深度学习模型时,应避免算子间产生循环依赖。
|
||||
掌握依赖关系后,分析 :numref:`recurrent`可以得出节点$\mathbf{Add}$间接依赖于节点$\mathbf{Matmul}$,而节点$\mathbf{Matmul}$直接依赖于节点$\mathbf{Add}$,此时两个节点互相等待对方计算完成输出数据,将无法执行计算任务。若我们手动同时给两个节点赋予输入,计算将持续不间断进行,模型训练将无法停止造成死循环。循环依赖产生正反馈数据流,被传递的数值可能在正方向上无限放大,导致数值上溢,或者负方向上放大导致数值下溢,也可能导致数值无限逼近于0,这些情况都会致使模型训练无法得到预期结果。在构建深度学习模型时,应避免算子间产生循环依赖。
|
||||
|
||||

|
||||
:width:`300px`
|
||||
:label:`recurrent`
|
||||
|
||||
在深度学习计算框架中,表示循环关系通常是以**展开**机制(Unrolling)来实现。当需要实现循环关系时,循环体的计算子图按照迭代次数进行复制,将代表相邻迭代轮次的子图进行串联,相邻迭代轮次的计算子图之间就是直接依赖关系。循环三次的计算图进行展开如图3.2.6。在计算图中,每一个张量和运算符都具有独特的标识符,即使是相同的操作运算,在参与不同计算任务时都具有不同的标识符。区分循环关系和循环依赖的关键在于,是否两个独特标识符之间的运算互相具有直接依赖和相互依赖。循环关系在展开复制计算子图的时候会给复制的所有张量和运算符赋予新的标识符,区分被复制的原始子图,以避免形成循环依赖。
|
||||
在深度学习计算框架中,表示循环关系通常是以**展开**机制(Unrolling)来实现。当需要实现循环关系时,循环体的计算子图按照迭代次数进行复制,将代表相邻迭代轮次的子图进行串联,相邻迭代轮次的计算子图之间就是直接依赖关系。循环三次的计算图进行展开如 :numref:`unroll`。在计算图中,每一个张量和运算符都具有独特的标识符,即使是相同的操作运算,在参与不同计算任务时都具有不同的标识符。区分循环关系和循环依赖的关键在于,是否两个独特标识符之间的运算互相具有直接依赖和相互依赖。循环关系在展开复制计算子图的时候会给复制的所有张量和运算符赋予新的标识符,区分被复制的原始子图,以避免形成循环依赖。
|
||||
|
||||

|
||||
:width:`800px`
|
||||
@@ -101,7 +101,7 @@ def control(A, B, C, conditional = True):
|
||||
:width:`600px`
|
||||
:label:`if`
|
||||
|
||||
图3.2.7描述上述代码的前向计算图和反向计算图。对于具有if-条件的模型,梯度计算需要知道采用了条件的哪个分支,然后将梯度逻辑应用于该分支。在前向计算图中张量${C}$经过条件控制不参与计算,在反向计算时同样遵守控制流决策,不会计算关于张量$C$的梯度。
|
||||
:numref:`if`描述上述代码的前向计算图和反向计算图。对于具有if-条件的模型,梯度计算需要知道采用了条件的哪个分支,然后将梯度逻辑应用于该分支。在前向计算图中张量${C}$经过条件控制不参与计算,在反向计算时同样遵守控制流决策,不会计算关于张量$C$的梯度。
|
||||
|
||||
当模型中有循环控制时,循环中的操作可以执行零次或者多次。此时采用展开机制,对每一次操作都赋予独特的运算标识符,以此来区分相同运算操作的多次调用。每一次循环都直接依赖于前一次循环的计算结果,所以在循环控制中需要维护一个张量列表,将循环迭代的中间结果缓存起来,这些中间结果将参与前向计算和梯度计算。下面这段代码描述了简单的循环控制,将其展开得到等价代码后,可以清楚的理解需要维护张量$\boldsymbol{Y_i}$和$\boldsymbol{W_i}$的列表。
|
||||
```python
|
||||
@@ -116,7 +116,7 @@ def recurrent_control(X, W, cur_num = 3):
|
||||
Y = matmul(X2, W2)
|
||||
return Y
|
||||
```
|
||||
如图3.2.8描述了上述代码的前向计算图和反向计算图,循环控制的梯度同样也是一个循环,它与前向循环相迭代次数相同,执行循环体的梯度计算。循环体输出的梯度值作为下一次梯度计算的初始值,直至循环结束。
|
||||
如 :numref:`while`描述了上述代码的前向计算图和反向计算图,循环控制的梯度同样也是一个循环,它与前向循环相迭代次数相同,执行循环体的梯度计算。循环体输出的梯度值作为下一次梯度计算的初始值,直至循环结束。
|
||||
|
||||

|
||||
:width:`600px`
|
||||
@@ -169,7 +169,7 @@ grad_W1 = matmul(transpose(Y1), grad_Y2)
|
||||
grad_Y = matmul(grad_Y1, transpose(W))
|
||||
grad_W = matmul(transpose(Y), grad_Y1)
|
||||
```
|
||||
结合公式、代码以及图3.2.9我们可以看出,在反向传播过程中使用到前向传播的中间变量。因此保存网络中间层输出状态和中间变量,尽管占用了部分内存但能够复用计算结果,达到了提高反向传播计算效率的目的。
|
||||
结合公式、代码以及 :numref:`chain`我们可以看出,在反向传播过程中使用到前向传播的中间变量。因此保存网络中间层输出状态和中间变量,尽管占用了部分内存但能够复用计算结果,达到了提高反向传播计算效率的目的。
|
||||
|
||||

|
||||
:width:`600px`
|
||||
|
||||
Reference in New Issue
Block a user