Skip to content

Commit

Permalink
remove space (d2l-ai#898)
Browse files Browse the repository at this point in the history
* remove space

* remove space

* remove space

* remove space

* remove space

* retrigger

* retrigger

* retrigger

* retrigger

* retrigger

* retrigger
  • Loading branch information
goldmermaid authored Jul 7, 2021
1 parent bbf7503 commit d7decdd
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 145 deletions.
30 changes: 15 additions & 15 deletions chapter_preliminaries/autograd.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# 自动求导
:label:`sec_autograd`

正如我们在 :numref:`sec_calculus` 中所说的那样,求导是几乎所有深度学习优化算法的关键步骤。虽然求导的计算很简单,只需要一些基本的微积分,但对于复杂的模型,手工进行更新是一件很痛苦的事情(而且经常容易出错)。
正如我们在 :numref:`sec_calculus`中所说的那样,求导是几乎所有深度学习优化算法的关键步骤。虽然求导的计算很简单,只需要一些基本的微积分,但对于复杂的模型,手工进行更新是一件很痛苦的事情(而且经常容易出错)。

深度学习框架通过自动计算导数,即 *自动求导* (automatic differentiation),来加快这项工作。实际中,根据我们设计的模型,系统会构建一个 *计算图* (computational graph),来跟踪计算是哪些数据通过哪些操作组合起来产生输出。自动求导使系统能够随后反向传播梯度。
深度学习框架通过自动计算导数,即*自动求导*(automatic differentiation),来加快这项工作。实际中,根据我们设计的模型,系统会构建一个*计算图*(computational graph),来跟踪计算是哪些数据通过哪些操作组合起来产生输出。自动求导使系统能够随后反向传播梯度。
这里,*反向传播*(backpropagate)只是意味着跟踪整个计算图,填充关于每个参数的偏导数。


## 一个简单的例子

作为一个演示例子,(**假设我们想对函数 $y = 2\mathbf{x}^{\top}\mathbf{x}$关于列向量 $\mathbf{x}$求导**)。首先,我们创建变量 `x` 并为其分配一个初始值。
作为一个演示例子,(**假设我们想对函数$y=2\mathbf{x}^{\top}\mathbf{x}$关于列向量$\mathbf{x}$求导**)。首先,我们创建变量`x`并为其分配一个初始值。

```{.python .input}
from mxnet import autograd, np, npx
Expand Down Expand Up @@ -57,7 +57,7 @@ x.grad # 默认值是None
x = tf.Variable(x)
```

(**现在让我们计算 $y$。**)
(**现在让我们计算$y$。**)

```{.python .input}
# 把代码放到`autograd.record`内,以建立计算图
Expand All @@ -80,7 +80,7 @@ with tf.GradientTape() as t:
y
```

`x` 是一个长度为 4 的向量,计算 `x``x` 的内积,得到了我们赋值给 `y` 的标量输出。接下来,我们可以[**通过调用反向传播函数来自动计算`y`关于`x` 每个分量的梯度**],并打印这些梯度。
`x`是一个长度为4的向量,计算`x``x`的内积,得到了我们赋值给`y`的标量输出。接下来,我们可以[**通过调用反向传播函数来自动计算`y`关于`x`每个分量的梯度**],并打印这些梯度。

```{.python .input}
y.backward()
Expand All @@ -99,7 +99,7 @@ x_grad = t.gradient(y, x)
x_grad
```

函数 $y = 2\mathbf{x}^{\top}\mathbf{x}$ 关于$\mathbf{x}$ 的梯度应为$4\mathbf{x}$。让我们快速验证我们想要的梯度是否正确计算。
函数$y=2\mathbf{x}^{\top}\mathbf{x}$关于$\mathbf{x}$的梯度应为$4\mathbf{x}$。让我们快速验证我们想要的梯度是否正确计算。

```{.python .input}
x.grad == 4 * x
Expand All @@ -115,7 +115,7 @@ x.grad == 4 * x
x_grad == 4 * x
```

[**现在让我们计算 `x` 的另一个函数。**]
[**现在让我们计算`x`的另一个函数。**]

```{.python .input}
with autograd.record():
Expand All @@ -142,7 +142,7 @@ t.gradient(y, x) # 被新计算的梯度覆盖

## 非标量变量的反向传播

`y` 不是标量时,向量`y`关于向量`x`的导数的最自然解释是一个矩阵。对于高阶和高维的 `y``x`,求导的结果可以是一个高阶张量。
`y`不是标量时,向量`y`关于向量`x`的导数的最自然解释是一个矩阵。对于高阶和高维的`y``x`,求导的结果可以是一个高阶张量。

然而,虽然这些更奇特的对象确实出现在高级机器学习中(包括[**深度学习中**]),但当我们调用向量的反向计算时,我们通常会试图计算一批训练样本中每个组成部分的损失函数的导数。这里(**,我们的目的不是计算微分矩阵,而是批量中每个样本单独计算的偏导数之和。**)

Expand Down Expand Up @@ -176,9 +176,9 @@ t.gradient(y, x) # 等价于 `y = tf.reduce_sum(x * x)`

有时,我们希望[**将某些计算移动到记录的计算图之外**]
例如,假设`y`是作为`x`的函数计算的,而`z`则是作为`y``x`的函数计算的。
现在,想象一下,我们想计算 `z` 关于 `x` 的梯度,但由于某种原因,我们希望将 `y` 视为一个常数,并且只考虑到 `x` `y`被计算后发挥的作用。
现在,想象一下,我们想计算`z`关于`x`的梯度,但由于某种原因,我们希望将`y`视为一个常数,并且只考虑到`x``y`被计算后发挥的作用。

在这里,我们可以分离 `y` 来返回一个新变量 `u`,该变量与 `y` 具有相同的值,但丢弃计算图中如何计算 `y` 的任何信息。换句话说,梯度不会向后流经 `u``x`。因此,下面的反向传播函数计算 `z = u * x` 关于 `x` 的偏导数,同时将 `u` 作为常数处理,而不是`z = x * x * x`关于 `x` 的偏导数。
在这里,我们可以分离`y`来返回一个新变量`u`,该变量与`y`具有相同的值,但丢弃计算图中如何计算`y`的任何信息。换句话说,梯度不会向后流经`u``x`。因此,下面的反向传播函数计算`z=u*x`关于`x`的偏导数,同时将`u`作为常数处理,而不是`z=x*x*x`关于`x`的偏导数。

```{.python .input}
with autograd.record():
Expand Down Expand Up @@ -212,7 +212,7 @@ x_grad = t.gradient(z, x)
x_grad == u
```

由于记录了 `y` 的计算结果,我们可以随后在 `y` 上调用反向传播,得到 `y = x * x` 关于的`x`的导数,这里是 `2 * x`
由于记录了`y`的计算结果,我们可以随后在`y`上调用反向传播,得到`y=x*x`关于的`x`的导数,这里是`2*x`

```{.python .input}
y.backward()
Expand All @@ -233,7 +233,7 @@ t.gradient(y, x) == 2 * x

## Python控制流的梯度计算

使用自动求导的一个好处是,[**即使构建函数的计算图需要通过 Python控制流(例如,条件、循环或任意函数调用),我们仍然可以计算得到的变量的梯度**]。在下面的代码中,`while` 循环的迭代次数和 `if` 语句的结果都取决于输入 `a` 的值。
使用自动求导的一个好处是,[**即使构建函数的计算图需要通过Python控制流(例如,条件、循环或任意函数调用),我们仍然可以计算得到的变量的梯度**]。在下面的代码中,`while`循环的迭代次数和`if`语句的结果都取决于输入`a`的值。

```{.python .input}
def f(a):
Expand Down Expand Up @@ -299,7 +299,7 @@ d_grad = t.gradient(d, a)
d_grad
```

我们现在可以分析上面定义的 `f` 函数。请注意,它在其输入 `a` 中是分段线性的。换言之,对于任何 `a`,存在某个常量标量 `k`,使得 `f(a) = k * a`,其中 `k` 的值取决于输入 `a`。因此,`d / a` 允许我们验证梯度是否正确。
我们现在可以分析上面定义的`f`函数。请注意,它在其输入`a`中是分段线性的。换言之,对于任何`a`,存在某个常量标量`k`,使得`f(a)=k*a`,其中`k`的值取决于输入`a`。因此,`d/a`允许我们验证梯度是否正确。

```{.python .input}
a.grad == d / a
Expand All @@ -323,9 +323,9 @@ d_grad == d / a

1. 为什么计算二阶导数比一阶导数的开销要更大?
1. 在运行反向传播函数之后,立即再次运行它,看看会发生什么。
1. 在控制流的例子中,我们计算 `d` 关于 `a` 的导数,如果我们将变量 `a` 更改为随机向量或矩阵,会发生什么?此时,计算结果 `f(a)` 不再是标量。结果会发生什么?我们如何分析这个结果?
1. 在控制流的例子中,我们计算`d`关于`a`的导数,如果我们将变量`a`更改为随机向量或矩阵,会发生什么?此时,计算结果`f(a)`不再是标量。结果会发生什么?我们如何分析这个结果?
1. 重新设计一个求控制流梯度的例子。运行并分析结果。
1. 使$f(x) = \sin(x)$,绘制$f(x)$ 和$\frac{df(x)}{dx}$的图像,其中后者不使用$f'(x) = \cos(x)$。
1. 使$f(x)=\sin(x)$,绘制$f(x)$和$\frac{df(x)}{dx}$的图像,其中后者不使用$f'(x)=\cos(x)$。

:begin_tab:`mxnet`
[Discussions](https://discuss.d2l.ai/t/1758)
Expand Down
Loading

0 comments on commit d7decdd

Please sign in to comment.