反向传导算法

From Ufldl

Jump to: navigation, search
Line 4: Line 4:
Wiki上传者:王方,email:fangkey@gmail.com,新浪微博:@GuitarFang
Wiki上传者:王方,email:fangkey@gmail.com,新浪微博:@GuitarFang
 +
 +
 +
假设我们有一个固定样本集 <math>\textstyle \{ (x^{(1)}, y^{(1)}), \ldots, (x^{(m)}, y^{(m)}) \}</math>,它包含 <math>\textstyle m</math> 个样例。我们可以用批量梯度下降法求解神经网络。具体来讲,对于单个样例 <math>\textstyle (x,y)</math> ,其代价函数为:
 +
 +
 +
:<math>
 +
\begin{align}
 +
J(W,b; x,y) = \frac{1}{2} \left\| h_{W,b}(x) - y \right\|^2.
 +
\end{align}
 +
</math>
 +
 +
 +
这是一个(二分之一的)方差代价函数。给定一个包含 <math>\textstyle m</math> 个样例的数据集,我们可以定义整体代价函数为:
 +
 +
:<math>
 +
\begin{align}
 +
J(W,b)
 +
&= \left[ \frac{1}{m} \sum_{i=1}^m J(W,b;x^{(i)},y^{(i)}) \right]
 +
                      + \frac{\lambda}{2} \sum_{l=1}^{n_l-1} \; \sum_{i=1}^{s_l} \; \sum_{j=1}^{s_{l+1}} \left( W^{(l)}_{ji} \right)^2
 +
\\
 +
&= \left[ \frac{1}{m} \sum_{i=1}^m \left( \frac{1}{2} \left\| h_{W,b}(x^{(i)}) - y^{(i)} \right\|^2 \right) \right]
 +
                      + \frac{\lambda}{2} \sum_{l=1}^{n_l-1} \; \sum_{i=1}^{s_l} \; \sum_{j=1}^{s_{l+1}} \left( W^{(l)}_{ji} \right)^2
 +
\end{align}
 +
</math>
 +
 +
以上公式中的第一项 <math>\textstyle J(W,b)</math> 是一个均方差项。第二项是一个规则化项(也叫'''权重衰减项'''),其目的是减小权重的幅度,防止过度拟合。
 +
 +
 +
[注:通常权重衰减的计算并不使用偏置项 <math>\textstyle b^{(l)}_i</math>,比如我们在 <math>\textstyle J(W, b)</math> 的定义中就没有使用。将偏置项包含在权重衰减项中通常只会对最终的神经网络产生很小的影响。如果你在斯坦福选修过CS229(机器学习)课程,或者在YouTube上看过课程视频,你会发现这个权重衰减实际上是课上提到的贝叶斯规则化方法的变种,在贝叶斯规则化方法中,我们引入高斯先验概率到参数中计算MAP(极大后验假设)估计(而不是极大似然估计)。]
 +
 +
 +
'''权重衰减参数''' <math>\textstyle \lambda</math> 用于控制公式中两项的相对重要性。在此再次重申一下这两个复杂函数的含义:<math>\textstyle J(W,b;x,y)</math> 是针对单个样例计算得到的方差代价函数; <math>\textstyle J(W,b)</math> 是整体样本代价函数,它包含权重衰减项。
 +
 +
 +
以上的代价函数经常被用于分类和回归问题。在分类问题中,我们用 <math>\textstyle y = 0</math> 或 <math>\textstyle 1</math> ,来代表两种类型的标签(回忆一下,这是因为 sigmoid激活函数的值域为 <math>\textstyle [0,1]</math> ;如果我们使用双曲正切型激活函数,那么应该选用-1和+1作为标签)。对于回归问题,我们首先要变换输出值域(译者注:也就是 <math>\textstyle y</math>),以保证其范围为 <math>\textstyle [0,1]</math> (同样地,如果我们使用双曲正切型激活函数,使其值域范围为 <math>\textstyle [-1,1]</math>)。
 +
 +
 +
我们的目标是针对参数 <math>\textstyle W</math> 和 <math>\textstyle b</math> 来求其函数 <math>\textstyle J(W,b)</math> 的最小值。为了求解神经网络,我们需要将每一个参数 <math>\textstyle W^{(l)}_{ij}</math> 和 <math>\textstyle b^{(l)}_i</math> 初始化为一个很小的、接近零的随机值(比如说,使用正态分布 <math>\textstyle {Normal}(0,\epsilon^2)</math> 生成的随机值,其中 <math>\textstyle \epsilon</math> 设置为 <math>\textstyle 0.01</math> ),之后对目标函数使用诸如批量梯度下降法的最优化算法。因为 <math>\textstyle J(W, b)</math> 是一个非凸函数,梯度下降法很可能收敛到局部最优解;但是在实际应用中,梯度下降法通常能得到令人满意的结果。最后,需要再次强调的是,要将参数进行随机初始化,而不是全部置为0。如果所有参数都用相同的值作为初始值,那么所有隐藏层单元最终会得到与输入值有关的、相同的函数(说得明白点,就是对于所有 <math>\textstyle i</math>,<math>\textstyle W^{(1)}_{ij}</math>都会取相同的值,于是对于任何输入 <math>\textstyle x</math> 都会有:<math>\textstyle a^{(2)}_1 = a^{(2)}_2 = a^{(2)}_3 = \ldots</math> )。随机初始化的目的是使'''对称失效'''。
 +
 +
 +
梯度下降法中每一次迭代都按照如下公式,对参数W和b进行更新:
 +
 +
:<math>
 +
\begin{align}
 +
W_{ij}^{(l)} &= W_{ij}^{(l)} - \alpha \frac{\partial}{\partial W_{ij}^{(l)}} J(W,b) \\
 +
b_{i}^{(l)} &= b_{i}^{(l)} - \alpha \frac{\partial}{\partial b_{i}^{(l)}} J(W,b)
 +
\end{align}
 +
</math>
 +
 +
其中 <math>\textstyle \alpha</math> 是学习速率。其中关键步骤是计算偏导数。我们现在来讲一下'''反向传播'''算法,它是计算偏导数的一种有效方法。
 +
 +
 +
我们首先来讲一下如何使用反向传播算法来计算 <math>\textstyle \frac{\partial}{\partial W_{ij}^{(l)}} J(W,b; x, y)</math> 和 <math>\textstyle \frac{\partial}{\partial b_{i}^{(l)}} J(W,b; x, y)</math>,这两项是针对单个样例 <math>\textstyle (x,y)</math> 的代价函数 <math>\textstyle J(W,b;x,y)</math> 的偏导数。一旦我们求出该偏导数,我们就可以推导出整体代价函数 <math>\textstyle J(W,b)</math> 的偏导数:
 +
 +
 +
:<math>
 +
\begin{align}
 +
\frac{\partial}{\partial W_{ij}^{(l)}} J(W,b) &=
 +
\left[ \frac{1}{m} \sum_{i=1}^m \frac{\partial}{\partial W_{ij}^{(l)}} J(W,b; x^{(i)}, y^{(i)}) \right] + \lambda W_{ij}^{(l)} \\
 +
\frac{\partial}{\partial b_{i}^{(l)}} J(W,b) &=
 +
\frac{1}{m}\sum_{i=1}^m \frac{\partial}{\partial b_{i}^{(l)}} J(W,b; x^{(i)}, y^{(i)})
 +
\end{align}
 +
</math>
 +
 +
以上两行公式稍有不同,第一行比第二行多出一项,是因为权重衰减是作用于 <math>\textstyle W</math> 而不是 <math>\textstyle b</math> 。
 +
 +
 +
反向传播算法的思路如下:给定一个样例 <math>\textstyle (x,y)</math>,我们首先进行“前向传导”运算,计算出网络中所有的激活值,包括 <math>\textstyle h_{W,b}(x)</math> 的输出值。之后,针对第 <math>\textstyle l</math> 层的每一个节点 <math>\textstyle i</math> ,我们计算出其“残差” <math>\textstyle \delta^{(l)}_i</math> ,该残差表明了该节点对最终输出值的残差产生了多少影响。对于最终的输出节点,我们可以直接算出网络产生的激活值与实际值之间的差距,我们将这个差距定义为 <math>\textstyle \delta^{(n_l)}_i</math>  (这里的第 <math>\textstyle n_l</math> 层表示输出层)。对于隐藏单元我们是如何处理的呢?我们将基于节点(译者注:第 <math>\textstyle l+1</math> 层节点)残差的加权平均值计算 <math>\textstyle \delta^{(l)}_i</math>,这些节点以 <math>\textstyle a^{(l)}_i</math> 作为输入。下面将给出反向传导算法的细节:
 +
 +
 +
<ol>
 +
<li>进行前向传导计算,得到 <math>\textstyle L_2</math> 、 <math>\textstyle L_3</math> …直到输出层 <math>\textstyle L_{n_l}</math> 的激活值。
 +
<li>对于第 <math>\textstyle n_l</math> 层(输出层)的每个输出单元 <math>\textstyle i</math> ,我们根据以下公式计算残差:
 +
 +
:<math>
 +
\delta^{(n_l)}_i = \frac{\partial}{\partial z^{n_l}_i}J(W,b;x,y)
 +
= \frac{\partial}{\partial z^{n_l}_i}\frac{1}{2} \left\|y - h_{W,b}(x)\right\|^2
 +
= - (y_i - a^{(n_l)}_i) \cdot f'(z^{(n_l)}_i)
 +
</math>
 +
 +
 +
<li>对 <math>\textstyle l = n_l-1, n_l-2, n_l-3, \ldots, 2</math> 的各个层,第 <math>\textstyle l</math> 层的第 <math>\textstyle i</math> 个节点的残差计算方法如下:
 +
:: <math>\textstyle
 +
                \delta^{(l)}_i = \left( \sum_{j=1}^{s_{l+1}} W^{(l)}_{ji} \delta^{(l+1)}_j \right) f'(z^{(l)}_i)
 +
                </math>
 +
 +
[译者注:完整推导过程如下:
 +
 +
:<math>
 +
\delta^{(n_{l-1})}_i = \frac{\partial}{\partial z^{n_l-1}_i}J(W,b;x,y) = \frac{\partial}{\partial z^{n_l}_i}J(W,b;x,y)\cdot\frac{\partial z^{n_l}_i}{\partial z^{n_{l-1}}_i}
 +
</math>
 +
 +
:<math>
 +
= \delta^{(n_l)}_i\cdot\frac{\partial z^{n_l}_i}{\partial z^{n_{l-1}}_i} = \delta^{(n_l)}_i\cdot\frac{\partial}{\partial z^{n_{l-1}}_i}\sum_{j=1}^{s_{l-1}} W^{n_l-1}_{ji} f(z^{n_l-1}_i)
 +
= \left( \sum_{j=1}^{s_{l-1}} W^{n_l-1}_{ji} \delta^{(n_l)}_i \right) f'(z^{n_l-1}_i)
 +
</math>
 +
 +
根据递推过程,将 <math>\textstyle n_l-1</math> 与 <math>\textstyle n_l</math> 的关系替换为 <math>\textstyle l</math> 与 <math>\textstyle l+1</math> 的关系,可以得到上面的结果:
 +
 +
::<math>
 +
                \delta^{(l)}_i = \left( \sum_{j=1}^{s_{l+1}} W^{(l)}_{ji} \delta^{(l+1)}_j \right) f'(z^{(l)}_i)
 +
                </math>
 +
以上的逐步反向递推求导的过程就是“反向传播”算法的本意所在。]
 +
 +
<li>计算我们需要的偏导数,计算方法如下:
 +
:<math>
 +
\begin{align}
 +
\frac{\partial}{\partial W_{ij}^{(l)}} J(W,b; x, y) &= a^{(l)}_j \delta_i^{(l+1)} \\
 +
\frac{\partial}{\partial b_{i}^{(l)}} J(W,b; x, y) &= \delta_i^{(l+1)}.
 +
\end{align}
 +
</math>
 +
</ol>
 +
 +
最后,我们用矩阵-向量表示法重写以上算法。我们使用“<math>\textstyle \bullet</math>” 表示向量乘积运算符(在Matlab或Octave里用“<tt>.*</tt>”表示,也称作阿达马乘积),因此若 <math>\textstyle a = b \bullet c</math>,则 <math>\textstyle a_i = b_ic_i</math> 。在上一个教程中我们扩展了 <math>\textstyle f(\cdot)</math> 的定义,使其包含向量运算,这里我们对偏导数 <math>\textstyle f'(\cdot)</math> 也做了同样的处理(于是又有 <math> f'([z_1, z_2, z_3]) =
 +
[f'(z_1), f'(z_2), f'(z_3)]</math> )。
 +
 +
于是反向传播算法可表示为以下几个步骤:
 +
 +
<ol>
 +
<li>进行前向传导计算,利用前向传导公式,得到 <math>\textstyle L_2</math>、 <math>\textstyle L_3</math> …直到输出层 <math>\textstyle L_{n_l}</math> 的激活值。
 +
 +
<li>对输出层(第 <math>\textstyle n_l</math> 层),计算:
 +
 +
:<math> \begin{align}
 +
\delta^{(n_l)}
 +
= - (y - a^{(n_l)}) \bullet f'(z^{(n_l)})
 +
\end{align}</math>
 +
 +
<li>对于 <math>\textstyle l = n_l-1, n_l-2, n_l-3, \ldots, 2</math> 的各层,计算:
 +
::<math> \begin{align}
 +
                \delta^{(l)} = \left((W^{(l)})^T \delta^{(l+1)}\right) \bullet f'(z^{(l)})
 +
                \end{align}</math>
 +
 +
<li>计算最终需要的偏导数值:
 +
:<math> \begin{align}
 +
\nabla_{W^{(l)}} J(W,b;x,y) &= \delta^{(l+1)} (a^{(l)})^T, \\
 +
\nabla_{b^{(l)}} J(W,b;x,y) &= \delta^{(l+1)}.
 +
\end{align}</math>
 +
</ol>
 +
 +
 +
'''实现中应注意:'''在以上的第2步和第3步中,我们需要为每一个 <math>\textstyle i</math> 值计算 <math>\textstyle f'(z^{(l)}_i)</math> 。假设 <math>\textstyle f(z)</math> 是sigmoid函数,并且我们已经在神经网络的前向传导运算中得到了 <math>\textstyle a^{(l)}_i</math>。于是,使用我们早先推导出的 <math>\textstyle f'(z)</math> 的表达式,我们可以计算得到 <math>\textstyle f'(z^{(l)}_i) = a^{(l)}_i (1- a^{(l)}_i)</math> 。
 +
 +
 +
最后,我们将对梯度下降算法做个全面总结。在下面的伪代码中,<math>\textstyle \Delta W^{(l)}</math> 是一个与矩阵 <math>\textstyle W^{(l)}</math> 维度相同的矩阵, <math>\textstyle \Delta b^{(l)}</math> 是一个与 <math>\textstyle b^{(l)}</math> 维度相同的向量。注意这里“<math>\textstyle \Delta W^{(l)}</math>”是一个矩阵,而不是“<math>\textstyle \Delta</math> 与 <math>\textstyle W^{(l)}</math> 相乘”。下面,我们实现批量梯度下降法的一步迭代:
 +
 +
 +
<ol>
 +
<li>对于所有 <math>\textstyle l</math>,令 <math>\textstyle \Delta W^{(l)} := 0</math> ,  <math>\textstyle \Delta b^{(l)} := 0</math> ,(设置为全零矩阵或全零向量)
 +
 +
<li>对于 <math>\textstyle i = 1</math> 到 <math>\textstyle m</math>,
 +
 +
<ol type="a">
 +
<li>使用反向传播算法计算 <math>\textstyle \nabla_{W^{(l)}} J(W,b;x,y)</math> 和 <math>\textstyle \nabla_{b^{(l)}} J(W,b;x,y)</math> 。
 +
<li>计算 <math>\textstyle \Delta W^{(l)} := \Delta W^{(l)} + \nabla_{W^{(l)}} J(W,b;x,y)</math> 。
 +
<li>计算 <math>\textstyle \Delta b^{(l)} := \Delta b^{(l)} + \nabla_{b^{(l)}} J(W,b;x,y)</math> 。
 +
</ol>
 +
 +
<li>更新权重参数:
 +
:<math> \begin{align}
 +
W^{(l)} &= W^{(l)} - \alpha \left[ \left(\frac{1}{m} \Delta W^{(l)} \right) + \lambda W^{(l)}\right] \\
 +
b^{(l)} &= b^{(l)} - \alpha \left[\frac{1}{m} \Delta b^{(l)}\right]
 +
\end{align}</math>
 +
</ol>
 +
 +
现在我们可以重复梯度下降法的迭代步骤来减小代价函数 <math>\textstyle J(W,b)</math> 的值,从而求解我们的神经网络。
 +
 +
==中英文对照==
 +
:反向传播算法 Backpropagation Algorithm
 +
:(批量)梯度下降法 (batch) gradient descent
 +
:(整体)代价函数 (overall) cost function
 +
:方差 squared-error
 +
:均方差 average sum-of-squares error
 +
:规则化项 regularization term
 +
:权重衰减 weight decay
 +
:偏置项 bias terms
 +
:贝叶斯规则化方法 Bayesian regularization method
 +
:高斯先验概率 Gaussian prior
 +
:极大后验假设 MAP
 +
:极大似然估计 maximum likelihood estimation
 +
:激活函数 activation function
 +
:双曲正切函数 tanh function
 +
:非凸函数 non-convex function
 +
:隐藏层单元 hidden (layer) units
 +
:对称失效 symmetry breaking
 +
:学习速率 learning rate
 +
:前向传递 forward pass
 +
:假设值 hypothesis
 +
:残差 error term
 +
:加权平均值 weighted average
 +
:前馈传导 feedforward pass
 +
:阿达马乘积 Hadamard product
 +
:前向传播 forward propagation
 +
 +
{{Sparse_Autoencoder}}
 +
 +
 +
:【原文】:
:【原文】:

Revision as of 14:13, 14 March 2013

Personal tools