This repository has been archived by the owner on Aug 9, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #81 from rickllyxu/dev
校对了Chapter16,翻译了附录CD
- Loading branch information
Showing
18 changed files
with
267 additions
and
89 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# Appendix C. SVM 对偶问题 | ||
|
||
为了理解对偶性,你首先得理解拉格朗日乘子法。它基本思想是将一个有约束优化问题转化为一个无约束优化问题,其方法是将约束条件移动到目标函数中去。让我们看一个简单的例子,例如要找到合适的 $x$ 和 $y$ 使得函数 $f(x, y) = x^2 + 2y$ 最小化,且其约束条件是一个等式约束:$3x + 2y + 1 = 0$。 使用拉格朗日乘子法,我们首先定义一个函数,称为**拉格朗日函数**:$g(x, y, \alpha) = f(x, y) - \alpha(3x + 2y + 1)$. 每个约束条件(在这个例子中只有一个)与新的变量(称为拉格朗日乘数)相乘,作为原目标函数的减数。 | ||
|
||
Joseph-Louis Lagrange 大牛证明了如果 $(\bar{x}, \bar{y})$ 是原约束优化问题的解,那么一定存在一个 $\bar{\alpha}$ ,使得 $(\bar{x}, \bar{y}, \bar{\alpha})$ 是拉格朗日函数的不动点(不动点指的是,在该点处,该函数所有的偏导数均为0)。换句话说,我们可以计算拉格朗日函数 $g(x, y, \alpha) $ 关于 $x, y$ 以及 $\alpha$ 的偏导数;然后我们可以找到那些偏导数均为0的不动点;最后原约束优化问题的解(如果存在)一定在这些不动点里面。 | ||
|
||
在上述例子里,偏导数为 | ||
|
||
$\begin{align}\frac{\partial}{\partial x}g(x, y, \alpha) = 2x - 3\alpha \\ \frac{\partial}{\partial y}g(x, y, \alpha) = 2 - 2\alpha \\ \frac{\partial}{\partial \alpha}g(x, y, \alpha) = -3x - 2y - 1 \end{align}$ | ||
|
||
当这些偏导数均为0时,即 2x − 3α = 2 − 2α = − 3x − 2y − 1 = 0,即可得 $x = \frac{3}{2}, y=-\frac{11}{4}, \alpha=1$. 这是唯一一个不动点,那它一定是原约束优化问题的解。然而,上述方法仅应用于等式约束,幸运的是,在某些正则性条件下,这种方法也可被一般化应用于不等式约束条件(例如不等式约束,$3x + 2y + 1 \geq 0$)。如下公式 C-1 ,给了SVM硬间隔问题时的一般化拉格朗日函数。在该公式中,$\alpha^{(i)}$ 是 KKT 乘子,它必须大于或等于0. (译者注:$\alpha^{(i)}$ 是 $\geq0$ 抑或 $\leq0$ 取决于拉格朗日函数的写法,以及原目标函数函数最大化抑或最小化) | ||
|
||
![公式C-1](../images/Appendix/E_C-1.png) | ||
|
||
就像拉格朗日乘子法,我们可以计算上述式子的偏导数、定位不动点。如果该原问题存在一个解,那它一定在不动点$(\bar{w}, \bar{b}, \bar{\alpha})$之中,且遵循 KKT 条件: | ||
|
||
- 遵循原问题的约束: $t^{(i)}((\bar{w})^T x^{(i)} +\bar{b}) \geq 1$, 对于 $i = 1, 2, ..., m$ | ||
- 遵循现问题里的约束,即 $\bar{\alpha}^{(i)} \geq 0$ | ||
- $\bar{\alpha}^{(i)} = 0$ 或者第`i`个约束条件是积极约束,意味着该等式成立:$t^{(i)}((\bar{w})^T x^{(i)} +\bar{b}) = 1$. 这个条件叫做 互补松弛 条件。它暗示了 $\bar{\alpha}^{(i)} = 0$ 和第`i`个样本位于SVM间隔的边界上(该样本是支撑向量)。 | ||
|
||
注意 KKT 条件是确定不动点是否为原问题解的必要条件。在某些条件下,KKT 条件也是充分条件。幸运的是,SVM 优化问题碰巧满足这些条件,所以任何满足 KKT 条件的不动点保证是原问题的解。 | ||
|
||
我们可以计算上述一般化拉格朗日函数关于`w`和`b`的偏导数,如 公式C-2. | ||
|
||
![公式C-2](../images/Appendix/E_C-2.png) | ||
|
||
令上述偏导数为0, 可得到公式C-3. | ||
|
||
![公式C-3](../images/Appendix/E_C-3.png) | ||
|
||
如果我们把上述式子代入到一般化拉格朗日函数(公式C-1)中,某些项会消失,从而得到公式C-4,并称之为原问题的对偶形式。 | ||
|
||
![公式C-4](../images/Appendix/E_C-4.png) | ||
|
||
现在该对偶形式的目标是找到合适的向量 $\bar{\alpha}$ , 使得该函数 $L(w, b, \alpha)$ 最小化,且 $\bar{\alpha}^{(i)} \geq 0$. 现在这个有约束优化问题正是我们苦苦追寻的对偶问题。 | ||
|
||
一旦你找到了最优的 $\bar{\alpha}$, 你可以利用公式C-3第一行计算 $\bar{w}$。为了计算 $\bar{b}$, 你可以使用支撑向量的已知条件 $t^{(i)}((\bar{w})^T x^{(i)} +\bar{b}) = 1$, 当第 k 个样本是支撑向量时(即它对应的 $\alpha_k > 0$,此时使用它计算 $\bar{b} =1-t^{(k)}((\bar{w})^T . x^{(k)}) $. 对了,我们更喜欢利用所有支撑向量计算一个平均值,以获得更稳定和更准确的结果,如公式C-5. | ||
|
||
![公式C-5](../images/Appendix/E_C-5.png) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
# Appendix D 自动微分 | ||
|
||
这个附录解释了 TensorFlow 的自动微分功能是如何工作的,以及它与其他解决方案的对比。 | ||
|
||
假定你定义了函数 $f(x, y) = x^2y + y + 2$, 需要得到它的偏导数 $\frac{\partial f}{\partial x}$和 $\frac{\partial f}{\partial y}$,以用于梯度下降或者其他优化算法。你的可选方案有手动微分法,符号微分法,数值微分法,前向自动微分,和反向自动微分。TensorFlow实现的反向自动微分法。我们来看看每种方案。 | ||
|
||
## 手动微分法 | ||
|
||
第一个方法是拿起一直笔和一张纸,使用你的代数知识去手动的求偏导数。对于已定义的函数,求它的偏导并不太困难。你需要使用如下5条规则: | ||
|
||
- 常数的导数为0. | ||
- $\lambda x$ 的导数为 $\lambda$ , $\lambda$为常数。 | ||
- $x^{\lambda}$ 的导数是 $\lambda x^{\lambda - 1}$ | ||
- 函数的和的导数,等于函数的导数的和 | ||
- $\lambda$ 乘以函数,再求导,等于 $\lambda$ 乘以函数的导数 | ||
|
||
从上述这些规则,可得到公式D-1. | ||
|
||
![公式D-1](../images/Appendix/E_D-1.png) | ||
|
||
这个种方法应用于更复杂函数时将变得非常罗嗦,并且有可能出错。好消息是,像刚才我们做的求数学式子的偏导数可以被自动化,通过一个称为符号微分的过程。 | ||
|
||
## 符号微分 | ||
|
||
图D-1展示了符号微分是如何运行在相当简单的函数上的,$g(x,y) = 5 + xy$. 该函数的计算图如图的左边所示。通过符号微分,我们可得到图的右部分,它代表了 $\frac{\partial g}{\partial x} = 0 + (0 \times x + y \times 1) = y$, 相似地也可得到关于`y`的导数。 | ||
|
||
![D-1](../images/Appendix/D-1.png) | ||
|
||
概算法先获得叶子结点的偏导数。常数5返回常数0, 因为常数的导数总是0。变量`x`返回常数1,变量`y`返回常数0因为 $\frac{\partial y}{\partial x} = 0$(如果我们找关于`y`的偏导数,那它将反过来)。 | ||
|
||
现在我们移动到计算图的相乘结点处,代数告诉我们,`u`和`v`相乘后的导数为 $\frac{\partial (u \times v)}{\partial x} = \frac{\partial v}{\partial x} \times u + \frac{\partial u}{\partial x} \times v $. 因此我们可以构造有图中大的部分,代表0 × x + y × 1. | ||
|
||
最后我们往上走到计算图的相加结点处,正如5条规则里提到的,和的导数等于导数的和。所以我们只需要创建一个相加结点,连接我们已经计算出来的部分。我们可以得到正确的偏导数,即:$\frac{\partial g}{\partial x} = 0 + (0 \times x + y \times 1) $. | ||
|
||
然而,这个过程可简化。对该图应用一些微不足道的剪枝步骤,可以去掉所有不必要的操作,然后我们可以得到一个小得多的只有一个结点的偏导计算图:$\frac{\partial g}{\partial x} = y$. | ||
|
||
在这个例子里,简化操作是相当简单的,但对更复杂的函数来说,符号微分会产生一个巨大的计算图,该图可能很难去简化,以导致次优的性能。更重要的是,符号微分不能处理由任意代码定义的函数,例如,如下已在第9章讨论过的函数: | ||
|
||
```python | ||
def my_func(a, b): | ||
z = 0 | ||
for i in range(100): | ||
z = a * np.cos(z + i) + z * np.sin(b - i) | ||
return z | ||
``` | ||
|
||
## 数值微分 | ||
|
||
从数值上说,最简单的方案是去计算导数的近似值。回忆`h(x)`在 $x_0$ 的导数 $h^{'}(x_0)$,是该函数在该点处的斜率,或者更准确如公式D-2所示。 | ||
|
||
![E_D-2](../images/Appendix/E_D-2.png) | ||
|
||
因此如果我们想要计算 $f(x,y)$ 关于`x`,在 $x=3, y=4$ 处的导数,我们可以简单计算 $f(3+\epsilon, 4) - f(3, 4)$ 的值,将这个结果除以 $\epsilon$, 且 $\epsilon$ 去很小的值。这个过程正是如下的代码所要干的。 | ||
|
||
```python | ||
def f(x, y): | ||
return x**2*y + y + 2 | ||
|
||
def derivative(f, x, y, x_eps, y_eps): | ||
return (f(x + x_eps, y + y_eps) - f(x, y)) / (x_eps + y_eps) | ||
|
||
df_dx = derivative(f, 3, 4, 0.00001, 0) | ||
df_dy = derivative(f, 3, 4, 0, 0.00001) | ||
``` | ||
|
||
不幸的是,偏导的结果并不准确(并且可能在求解复杂函数时更糟糕)。上述正确答案分别是 24 和 10 ,但我们得到的是: | ||
|
||
```python | ||
>>> print(df_dx) | ||
24.000039999805264 | ||
>>> print(df_dy) | ||
10.000000000331966 | ||
``` | ||
|
||
注意到为了计算两个偏导数, 我们不得不调用`f()`至少三次(在上述代码里我们调用了四次,但可以优化)。如果存在 1000 个参数,我们将会调用`f()`至少 1001 次。当处理大的神经网络时,这样的操作很没有效率。 | ||
|
||
然而,数值微分实现起来如此简单,以至于它是检查其他方法正确性的优秀工具。例如,如果它的结果与您手动计算的导数不同,那么你的导数可能包含错误。 | ||
|
||
## 前向自动微分 | ||
|
||
前向自动微分既不是数值微分,也不是符号微分,但在某些方面,它是他们爱的小孩儿。它依赖对偶数。对偶数是奇怪但迷人的,是 $a + b\epsilon$ 形式的数,这里 `a` 和 `b` 是实数,$\epsilon$ 是无穷小的数,满足 $\epsilon ^ 2 = 0$, 但 $\epsilon \ne 0$. 你可以认为对偶数 $42 + 24\epsilon$ 类似于有着无穷个0的42.0000⋯000024(但当然这是简化后的,仅仅给你对偶数什么的想法)。一个对偶数在内存中表示为一个浮点数对,例如,$42 + 24\epsilon$ 表示为 (42.0, 24.0)。 | ||
|
||
对偶数可相加、相乘、等等操作,正如公式D-3所示。 | ||
|
||
![E_D-3](../images/Appendix/E_D-3.png) | ||
|
||
最重要的,可证明 h(a + bϵ) = h(a) + b × h′(a)ϵ,所以计算一次 h(a + ϵ) 就得到了两个值 h(a) 和 h′(a) 。图D-2展示了前向自动微分如何计算 $f(x,y)=x^2y + y + 2$ 关于`x`,在 $x=3, y=4$ 处的导数的。我们所要做的一切只是计算 $f(3+\epsilon, 4)$; 它将输出一个对偶数,其第一部分等于 $f(3, 4)$, 第二部分等于 $f^{'}(3, 4) = \frac{\partial f}{\partial x} (3,4)$. | ||
|
||
![D-2](../images/Appendix/D-2.png) | ||
|
||
为了计算 $\frac{\partial f}{\partial y} (3,4)$ 我们不得不再历经一遍计算图,但这次前馈的值为 $x=3, y = 4 + \epsilon$. | ||
|
||
所以前向自动微分比数值微分准确得多,但它遭受同样的缺陷:如果有1000个参数,那为了计算所有的偏导数,得历经计算图 1000 次。这正是反向自动微分耀眼的地方:计算所有的偏导数,它只需要经历计算图2次。 | ||
|
||
## 反向自动微分 | ||
|
||
反向自动微分是 TensorFlow 采取的方案。它首先前馈经历计算图(即,从输入到输出),计算出每个结点的值。然后进行第二次经历,这次是反向经历(即,从输出到输入),计算出所有的偏导数。图D-3展示了第二次经历的过程。在第一次经历过程中,所有结点值已被计算,输入是 $x=3, y=4$。你可以在每个结点底部右方看到这些值(例如,$x \times x = 9$)。结点已被标号,从 $n_1$ 到 $n_7$。输出结点是 $n_7: f(3, 4) = n_7 = 42$. | ||
|
||
![D-3](../images/Appendix/D-3.png) | ||
|
||
这个计算关于每个连续结点的偏导数的思想逐渐地从上到下遍历图,直到到达变量结点。为实现这个,反向自动微分强烈依赖于链式法则,如公式D-4所示。 | ||
|
||
![E_D-4](../images/Appendix/E_D-4.png) | ||
|
||
由于 $n_7$ 是输出结点,即 $f= n_7$, 所以 $\frac{\partial f}{\partial n_7} = 1$. | ||
|
||
接着到了图的 $n_5$ 结点:当 $n_5$ 变化时,$f$ 会变化多少?答案是 $\frac{\partial f}{\partial n_5} = \frac{\partial f}{\partial n_7} \times \frac{\partial n_7}{\partial n_5}$. 我们已经知道 $\frac{\partial f}{\partial n_7} = 1$, 因此我们只需要知道 $\frac{\partial n_7}{\partial n_5}$ 就行。因为 $n_7$ 是 $n_5 + n_6$ 的和,因此可得到 $\frac{\partial n_7}{\partial n_5} = 1$, 因此 $\frac{\partial f}{\partial n_5}=1 \times 1 = 1$. | ||
|
||
现在前进到 $n_4$: 当 $n_4$ 变化时,$f$ 会变化多少?答案是 $\frac{\partial f}{\partial n_4} = \frac{\partial f}{\partial n_5} \times \frac{\partial n_5}{\partial n_4}$. 由于 $n_5 = n_4 \times n_2$, 我们可得到 $\frac{\partial n_5}{\partial n_4} = n_2$, 所以 $\frac{\partial f}{\partial n_4}= 1 \times n_2 = 4$. | ||
|
||
这个遍历过程一直持续,此时我们达到图的底部。这时我们已经得到了所有偏导数在点 $x=3, y=4$ 处的值。在这个例子里,我们得到 $\frac{\partial f}{\partial x} = 24, \frac{\partial f}{\partial y} = 10$. 听起来很美妙! | ||
|
||
反向自动微分是非常强大且准确的技术,尤其是当有很多输入参数和极少输出时,因为它只要求一次前馈传递加上一次反向传递,就可计算所有输出关于所有输入的偏导数。最重要的是,它可以处理任意代码定义的函数。它也可以处理那些不完全可微的函数,只要 你要求他计算的偏导数在该点处是可微的。 | ||
|
||
如果你在 TensorFlow 中实现了新算子,你想使它与现有的自动微分相兼容,那你需要提供函数,该函数用于构建一个子图,来计算关于新算子输入的偏导数。例如,假设你实现了一个计算其输入的平方的函数,平方算子 $f(x)= x ^2$,在这个例子中你需要提供相应的导函数 $f^{'}(x)= 2x $. 注意这个导函数不计算一个数值结果,而是用于构建子图,该子图后续将计算偏导结果。这是非常有用的,因为这意味着你可以计算梯度的梯度(为了计算二阶导数,或者甚至更高维的导数)。 |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.