🎨
Bible-DeepLearning
  • Introduction
  • 第6章 深度前馈网络
    • 6.1 例子:学习XOR
    • 6.2 基于梯度的学习
      • 6.2.1 代价函数
        • 6.2.1.1 使用最大似然学习条件分布
        • 6.2.1.2 学习条件统计量
      • 6.2.2 输出单元
        • 6.2.2.1 用于高斯输出分布的线性神单元
        • 6.2.2.2 用于Bernoulli输出分布的sigmoid单元
        • 6.2.2.3 用于Multinoulli输出分布的softmax单元
    • 6.3 隐藏单元
      • 6.3.1 ReLU及其扩展
      • 6.3.2 logistic sigmoid与双曲正切函数
      • 6.3.3 其他隐藏单元
      • 李宏毅补充 SELU
    • 6.4 架构设计
    • 6.5 反向传播和其他的微分算法
      • 6.5.1 计算图
      • 6.5.2 微积分中的链式法则
      • 6.5.3 递归地使用链式法则来实现反向传播
      • 6.5.4 全连接MLP中的反向传播计算
      • 6.5.5 符号到符号的导数
      • 6.5.6 一般化的反向传播
      • 6.5.7 实例:用于MLP 训练的反向传播
      • 6.5.8 复杂化
  • 第7章 深度学习中的正则化
    • 7.1 参数范数惩罚
      • 7.1.1 L2参数正则化
      • 7.1.2 L1参数正则化
    • 7.2 作为约束的范数惩罚
    • 7.3 正则化和欠约束问题
    • 7.4 数据集增强
    • 7.5 噪声鲁棒性
    • 7.6 半监督学习
    • 7.7 多任务学习
    • 7.8 提前终止
    • 7.9 参数绑定和参数共享
    • 7.10 稀疏表示
    • 7.11 Bagging 和其他集成方法
    • 7.12 Dropout
    • 7.13 对抗训练
    • 7.14 切面距离、正切传播和流形正切分类器
    • Ag补充 一些能用于提升比赛成绩的方法
  • 第8章 深度模型中的优化
    • 8.1 学习和纯优化有什么不同
      • 8.1.1 经验风险最小化
      • 8.1.2 代理损失函数和提前终止
      • 8.1.3 批量算法和小批量算法
    • 8.2 神经网络优化中的挑战
      • 8.2.1 病态
      • 8.2.2 局部极小值
      • 8.2.3 8.2.3 高原、鞍点和其他平坦区域
      • 8.2.4 悬崖和梯度爆炸
      • 8.2.5 长期依赖
      • 8.2.6 非精确梯度
    • 8.3 基本算法
      • 8.3.1 随机梯度下降
      • 8.3.2 动量
      • 8.3.3 Nesterov 动量
    • 8.4 参数初始化策略
    • 8.5 自适应学习率算法
      • 8.5.1 AdaGrad
      • 8.5.2 RMSProp
      • 8.5.3 Adam
      • 8.5.4 选择正确的优化算法
    • 8.6 二阶近似方法
      • 8.6.1 牛顿法
      • 8.6.2 共轭梯度
      • 8.6.3 BFGS
    • 8.7 优化策略和元算法
      • 8.7.1 批标准化
      • 8.7.2 坐标下降
      • 8.7.3 Polyak 平均
      • 8.7.4 监督预训练
      • 8.7.5 设计有助于优化的模型
  • 第9章 卷积网络
    • 9.1 卷积运算
    • 9.2 动机
    • 9.3 池化
    • 9.4 卷积与池化作为一种无限强的先验
    • 9.5 基本卷积函数的变体
    • 9.6 结构化输出
    • 9.7 数据类型
  • 第10章 序列建模:循环和递归网络
    • 10.1 展开计算图
    • 10.2 循环神经网络
      • 10.2.1 导师驱动过程和输出循环网络
      • 10.2.2 计算循环神经网络的梯度
      • 10.2.3 作为有向图模型的循环网络
      • 10.2.4 基于上下文的RNN序列建模
    • 10.3 双向RNN
    • 10.4 基于编码 - 解码的序列到序列架构
    • 10.5 深度循环网络
    • 10.6 递归神经网络
    • 10.7 长期依赖的挑战
    • 10.9 渗漏单元和其他多时间尺度的策略
    • 10.10 长短期记忆和其他门控RNN
      • 10.10.1 LSTM
      • 10.10.2 其他门控RNN
    • 10.11 优化长期依赖
      • 10.11.1 梯度截断
      • 10.11.2 引导信息流的正则化
    • 10.12 外显记忆
  • 第11章 实践方法论
    • 11.1 性能度量
    • 11.2 默认的基准模型
    • 11.3 决定是否收集更多数据
    • 11.4 选择超参数
      • 11.4.1 手动选择超参数
      • 11.4.3 网络搜索
      • 11.4.4 随机搜索
    • 11.5 调试策略
Powered by GitBook
On this page

Was this helpful?

  1. 第6章 深度前馈网络
  2. 6.5 反向传播和其他的微分算法

6.5.6 一般化的反向传播

Previous6.5.5 符号到符号的导数Next6.5.7 实例:用于MLP 训练的反向传播

Last updated 5 years ago

Was this helpful?

反向传播算法非常简单。 为了计算某个标量$z$关于图中它的一个祖先$x$的梯度,我们首先观察到它关于$z$的梯度由$\frac{dz}{dz}=1$给出。 然后,我们可以计算对图中$z$的每个父节点的梯度,通过现有的梯度乘以产生$z$的操作的Jacobian。 我们继续乘以Jacobian,以这种方式向后穿过图,直到我们到达$x$。 对于从$z$出发可以经过两个或更多路径向后行进而到达的任意节点,我们简单地对该节点来自不同路径上的梯度进行求和。

[success]

更正式地,图$G$中的每个节点对应着一个变量。 为了实现最大的一般化,我们将这个变量描述为一个张量$V$。 张量通常可以具有任意维度,并且包含标量、向量和矩阵。

我们假设每个变量$V$与下列子程序相关联:

  • get_operation($V$):它返回用于计算$V$的操作,代表了在计算图中流入$V$的边。 例如,可能有一个Python或者C++的类表示矩阵乘法操作,以及get_operation函数。 假设我们的一个变量是由矩阵乘法产生的,$C=AB$。 那么,get_operation($V$)返回一个指向相应C++类的实例的指针。

    [success] 返回get_operation函数的指针

  • get_consumers($V, G$):它返回一组变量,是计算图$G$中$V$的子节点。

  • get_inputs($V, G$):它返回一组变量,是计算图$G$中$V$的父节点。

每个操作op也与bprop操作相关联。

[warning] bprop操作?

该bprop操作可以计算如式6.47所描述的Jacobian向量积。

[info] 公式6.47: $\nabla_X z = \sum_j (\nabla_X Y_j)\frac{\partial z}{\partial Y_j}$

这是反向传播算法能够实现很大通用性的原因。

[warning] [?] [?] 这一段看不懂

每个操作负责了解如何通过它参与的图中的边来反向传播。 例如,我们可以使用矩阵乘法操作来产生变量$C=AB$。 假设标量$z$关于$C$的梯度是$G$。 矩阵乘法操作负责定义两个反向传播规则,每个规则对应于一个输入变量。 如果我们调用bprop方法来请求关于$A$的梯度,那么在给定输出的梯度为$G$的情况下,矩阵乘法操作的bprop方法必须说明关于$A$的梯度是$GB^\top$。

[success] $\frac{\partial z}{\partial A} = \frac{\partial z}{\partial C} = \frac{\partial C}{\partial A} = GB^\top$

类似的,如果我们调用bprop方法来请求关于$B$的梯度,那么矩阵操作负责实现bprop方法并指定希望的梯度是$A^\top G$。 反向传播算法本身并不需要知道任何微分法则。 它只需要使用正确的参数调用每个操作的bprop方法即可。 正式地,op.bprop(inputs, X, G)必须返回

[success] op.bprop(inputs, X, G)用于计算$\frac{\partial z}{\partial X}$ $\frac{\partial z}{\partial X} = \sumi \frac{\partial f}{\partial X}\frac{\partial z}{\partial f}$ 第一项是$(\nabla{X} \text{op.f(inputs)}_i)$ 第二项是${G}_i$

这只是如公式6.47所表达的链式法则的实现。 这里,inputs是提供给操作的一组输入,op.f是操作实现的数学函数,$X$是输入,我们想要计算关于它的梯度,$G$是操作对于输出的梯度。

op.bprop方法应该总是假装它的所有输入彼此不同,即使它们不是。 例如,如果mul操作传递两个$x$来计算$x^2$,op.bprop方法应该仍然返回$x$作为对于两个输入的导数。 反向传播算法后面会将这些变量加起来获得$2x$,这是$x$上总的正确的导数。

反向传播算法的软件实现通常提供操作和其bprop方法,所以深度学习软件库的用户能够对使用诸如矩阵乘法、指数运算、对数运算等等常用操作构建的图进行反向传播。 构建反向传播新实现的软件工程师或者需要向现有库添加自己的操作的高级用户通常必须手动为新操作推导op.bprop方法。

反向传播算法的正式描述参考算法6.5。

在第6.5.2节中,我们使用反向传播作为一种策略来避免多次计算链式法则中的相同子表达式。 由于这些重复子表达式的存在,简单的算法可能具有指数运行时间。 现在我们已经详细说明了反向传播算法,我们可以去理解它的计算成本。 如果我们假设每个操作的执行都有大致相同的开销,那么我们可以依据执行操作的数量来分析计算成本。 注意这里我们将一个操作记为计算图的基本单位,它实际可能包含许多算术运算(例如,我们可能将矩阵乘法视为单个操作)。 在具有$n$个节点的图中计算梯度,将永远不会执行超过$O(n^2)$个操作,或者存储超过$O(n^2)$个操作的输出。 这里我们是对计算图中的操作进行计数,而不是由底层硬件执行的单独操作,所以重要的是要记住每个操作的运行时间可能是高度可变的。 例如,两个矩阵相乘可能对应着图中的一个单独的操作,但这两个矩阵可能每个都包含数百万个元素。 我们可以看到,计算梯度至多需要$O(n^2)$的操作,因为在最坏的情况下,前向传播的步骤将在原始图的全部$n$个节点上运行(取决于我们想要计算的值,我们可能不需要执行整个图)。 反向传播算法在原始图的每条边添加一个Jacobian向量积,可以用$O(1)$个节点来表达。 因为计算图是有向无环图,它至多有$O(n^2)$条边。对于实践中常用图的类型,情况会更好。 大多数神经网络的代价函数大致是链式结构的,使得反向传播只有$O(n)$的成本。 这远远胜过简单的方法,简单方法可能需要在指数级的节点上运算。 这种潜在的指数级代价可以通过非递归地扩展和重写递归链式法则公式6.53来看出:

由于节点$j$到节点$n$的路径数目可以关于这些路径的长度上指数地增长,所以上述求和符号中的项数(这些路径的数目),可能以前向传播图的深度的指数级增长。 会产生如此大的成本是因为对于$\frac{\partial u^{(i)}}{\partial u^{(j)}}$,相同的计算会重复进行很多次。 为了避免这种重新计算,我们可以将反向传播看作一种表填充算法,利用存储的中间结果$\frac{\partial u^{(n)}}{\partial u^{(i)}}$来对表进行填充。 图中的每个节点对应着表中的一个位置,这个位置存储对该节点的梯度。 通过顺序填充这些表的条目,反向传播算法避免了重复计算许多公共子表达式。 这种表填充策略有时被称为动态规划。

∑i(∇Xop.f(inputs)i)Gi,\begin{aligned} \sum_i (\nabla_{X} \text{op.f(inputs)}_i) \textsf{G}_i, \end{aligned}i∑​(∇X​op.f(inputs)i​)Gi​,​
∂u(n)∂u(j)=∑path(u(π1),u(π2),…,u(πt),from π1=j to πt=n∏k=2t∂u(πk)∂u(πk−1)(6.55)\begin{aligned} \frac{\partial u^{(n)}}{\partial u^{(j)}} = \sum_{\text{path}(u^{(\pi_1)}, u^{(\pi_2)}, \ldots, u^{(\pi_t)}, \text{from } \pi_1=j \text{ to }\pi_t = n} \prod_{k=2}^t \frac{\partial u^{(\pi_k)}}{\partial u^{(\pi_{k-1})}} && (6.55) \end{aligned}∂u(j)∂u(n)​=path(u(π1​),u(π2​),…,u(πt​),from π1​=j to πt​=n∑​k=2∏t​∂u(πk−1​)∂u(πk​)​​​(6.55)​
6.5.3中的例子