反向传播算法推导(三层神经网络为例)

符号说明

  • i、j和k分别代表输入层、隐含层和输出层的神经元序号
  • $net_x$表示神经元x未经过激活函数的输出
  • $y_x$表示神经元x经过激活函数后的输出
  • $f(\cdot)$表示激活函数
  • $t_k$表示输出层的真实值
  • 损失函数$L(\mathcal{W}) = \frac{1}{2}\sum_k (y_k - t_k)^2$(加个系数方便后面求导)

参考下面这个示意图($net_i$和$y_i$都和输入相等)

更新隐含层到输出层的权重

针对$W_{j,k}$,计算梯度 $$ \frac{\partial{L}}{\partial{W_{j,k}}}=\frac{\partial{L}}{\partial{net_k}}\cdot \frac{\partial{net_k}}{\partial{W_{j,k}}} $$

$$\frac{\partial{L}}{\partial{net_k}} = \frac{\partial{L}}{\partial{y_k}} \cdot \frac{\partial{y_k}}{\partial{net_k}}$$

容易得到$$\frac{\partial{L}}{\partial{y_k}} = y_k - t_k$$

$$\begin{eqnarray} \frac{\partial{L}}{\partial{net_k}} &=& (y_k - t_k) \frac{\partial{y_k}}{\partial{net_k}}\\ &=&(y_k - t_k)f^{'}(net_k) \end{eqnarray}$$

因为有$$net_k = \sum_x W_{x, k}\cdot y_x$$ 所以$$\frac{\partial{net_k}}{\partial{W_{j,k}}} = y_j$$

综上可以得到 $$\frac{\partial{L}}{\partial{W_{j,k}}} = (y_k - t_k)f^{'}(net_k)y_j$$

那么k号输出神经元隐含层到输出层权重参数的梯度为 $$\Delta{W_k} = (y_k - t_k)f^{'}(net_k)Y_{hidden}$$

注意 这里约定$W_k$和$Y_{hidden}$都是行向量。

这里有个问题,如果有多个输出怎么办?每个输出都会更新上一层的权重,难道取平均?不对,每个输出神经元对应的都是自己的权重,更新的也只是自己的。 那么隐含层到输出层所有的权重的梯度为(这里没办法编程矩阵运算,先放着吧)

更新输入层到隐含层的权重

要求的是损失函数相对于i号神经元(输入层)但j号神经元(隐含层)的权重 $$\frac{\partial{L}}{\partial{W_{i,j}}}=\frac{\partial{L}}{\partial{net_j}}\cdot \frac{\partial{net_j}}{\partial{W_{i,j}}}$$

同隐含层的关系有 $$\frac{\partial{net_j}}{\partial{W_{i,j}}} = y_i$$

$$\frac{\partial{L}}{\partial{net_j}} = \frac{\partial{L}}{\partial{y_j}}\cdot \frac{\partial{y_j}}{\partial{net_j}}$$

这里求导时注意把损失函数展开,因为一个隐含层的神经元对应着多个输出层的神经元。 $$\begin{eqnarray} \frac{\partial{L}}{\partial{y_j}} &=& \frac{\partial}{\partial{y_j}}\left[ \frac{1}{2}\sum_k (y_k - t_k)^2\right]\\ &=&\sum_k (y_k - t_k)\frac{\partial{y_k}}{\partial{y_j}}\\ &=&\sum_k (y_k - t_k)f^{'}(net_k)W_{j,k}\\ \end{eqnarray}$$

综上,可以得到$$\frac{\partial{L}}{\partial{W_{i,j}}} = f^{'}(net_j) y_i \sum_k (y_k - t_k)f^{'}(net_k)W_{j,k}$$

那么j号神经元参数的梯度为$$\Delta W_j = Y_{input}f^{'}(net_j) \sum_k (y_k - t_k)f^{'}(net_k)(W_{j,k})$$

简化计算

找出每一层公共计算的部分,尽量使用矩阵操作,以方便实现。

针对某个参数$W_{x, y}$,损失函数对它的梯度总是可以写成两部分 $$\begin{eqnarray} \frac{\partial{L}}{\partial{W_{x, y}}} &=& \frac{\partial{L}}{\partial{net_y}} \cdot \frac{\partial{net_y}}{\partial{W_{x,y }}}\\ &=&\frac{\partial{L}}{\partial{net_y}}y_x\\ &\rightarrow& \delta_y \cdot y_x \end{eqnarray}$$

对$\delta_y$进一步拆分,z是y的下一层神经元 $$\begin{eqnarray} \delta_y &=& \frac{\partial{L}}{\partial{net_y}}\\ &=& \frac{\partial{L}}{\partial{y_y}}\cdot \frac{\partial{y_y}}{\partial{net_y}}\\ &=& f^{'}(net_y)\frac{\partial{L}}{\partial{y_y}} \end{eqnarray}$$

而 $$\begin{eqnarray} \frac{\partial{L}}{\partial{y_y}} &=& \sum_z \frac{\partial{L}}{\partial{net_z}}\cdot \frac{\partial{net_z}}{\partial{y_y}}\\ &=& \sum_z \delta_z \cdot W_{y, z} \end{eqnarray}$$

那么有 $$\Delta W_{x, y} = \eta \cdot \delta_y \cdot y_x$$ 而 $$\delta_y = f^{'}(net_y)\sum_z \delta_z\cdot W_{y, z}$$ 这就将每层参数的更新形式定下来了,并且相邻层的关系也给出了。

最后一层网络的$\delta$记为$\delta_n$ $$\begin{eqnarray} \delta_n &=& \frac{\partial{L}}{\partial{net_n}}\\ &=& \frac{\partial{L}}{\partial{y_n}}\cdot \frac{\partial{y_n}}{\partial{net_n}}\\ &=& f^{'}(net_n)\cdot \frac{\partial{L}}{\partial{y_n}} \end{eqnarray}$$ 从$\delta_n$开始可以依次计算其他层的$\delta$。

矩阵化

这里的向量指行向量 $$\vec{\delta}_k = \vec{\delta}_{k+1} \times \mathcal{W}^T_{k\rightarrow k+1} \cdot f^{'}(\vec{net}_k)$$

$\mathcal{W}^T_{k\rightarrow k+1}$是第k层到k+1层的weights,行数对应k层神经元的个数,列数对应k+1层神经元的个数。注意这里的“$\times$“表示矩阵乘法,而“$\cdot$”计算两个向量对应元素相乘得到的新向量。

$$\Delta \mathcal{W}_{k-1 \rightarrow k} = \eta \cdot \vec{y}_{k-1}^T \times \vec{\delta}_k$$

注意,这里只使用了一个样本,当有多个样本同时训练时(batch),应该如何做?把每个样本计算的$\Delta$加起来作为最终的$\Delta$。因为有激活函数,所以从输出到$\Delta$的变换不是线性的,那么就不能先将结果加起来,在计算最终的$\Delta$,应分别计算每个样本的$\Delta$再求和,矩阵化见下面。

$$\large \mathbf{\delta_k} = \mathbf{\delta_{k+1}} \times \mathcal{W}^T_{k \rightarrow k+1} \cdot f^{'}(\mathbf{net_k})$$

这里的$\delta$和$net_k$都是矩阵,每一行对应一个样本。“$\times$”是矩阵乘法,“$\cdot$”是矩阵点乘。

$$\large \Delta \mathcal{W}_{k-1 \rightarrow k} = \eta \cdot \mathit{Y}_{k-1}^T \times \mathit{\delta_k}$$

实现

softmax loss

用神经网络做多分类时,一般用softmax作Loss函数。输出层神经元个数为C(类别个数),标签用one-hot编码(其实就对应着输出层神经元的每个输出)。那么softmax loss的计算公式如下: $$p_n = \frac{e^{y_n}}{\sum_x^C e^{y_x}}$$

因为$p_n$包含了指数函数,计算过程中可能会溢出,所以一般采用一些技巧来避免。看下面的变换,参考softmax: $$\begin{eqnarray} p_n &=& \frac{C\cdot e^{y_n}}{C \cdot \sum_x^C e^{y_x}}\\ &=& \frac{e^{y_n + \log C}}{\sum_x^C e^{y_x + \log C}} \end{eqnarray}$$

一般取$\log C = -\max y_n$

$$\begin{eqnarray} L(W) &=& -\sum_n t_n \log{p_n}\\ &=&-\sum_n t_n(\log y_n - \log \sum_x^C{e^{y_x}}) \end{eqnarray}$$

对网络的输出求导(参考Derivative of Softmax loss function) $$\frac{\partial{L}}{\partial{y_n}} = \sum_i \frac{\partial{L}}{\partial{p_i}} \cdot \frac{\partial{p_i}}{\partial{y_n}}$$

$$\frac{\partial{p_i}}{\partial{y_n}} = \begin{cases} p_n(1-p_n), i = n\\ -p_n p_i,i \ne n \end{cases}$$
$$\begin{eqnarray} \frac{\partial{L}}{\partial{y_n}} &=& t_n(p_n - 1) + \sum_{i\ne n} t_i p_n \\ &=& p_n\sum_i t_i - t_n\\ &=& p_n - t_n \end{eqnarray}$$

ReLU

数学意义上,ReLU在0处没有导数,但为了方便实现,一般0处的导数置为0。参考Neural network backpropagation with RELU。 $$ReLU(x) = max(0, x)\\ ReLU^{'}(x) = \begin{cases} 0, x \le 0\\ 1, x > 0 \end{cases}$$

momentum

借用物理上的动量(momentum)的概念,在更新weights时做一点改变: $$W(t+1) = W(t) + \Delta W(t) + \alpha \Delta W(t-1)$$

直观的理解:把寻找最优解想象成从loss平面往下冲坡的过程,中间可能会遇到翘起来的坎,借用进入这个坎之前的动量就很有希望冲过这个坎。

加入momentum可有效避免陷入局部最优解。$\alpha$取值一般在0.9左右。

Weight decay

是一种经验规则,用上面的方法求得新的weight后,再进行一道变换: $$W_{new} = (1-\epsilon)W_{old}$$ 形象一点的理解是开始有足够多的神经元,随着训练地推进,用不上的神经元就会慢慢退化掉。

检验算法是否正确实现

主要检验在反向传播时每个参数的梯度是否计算正确,梯度的计算方法,除了上面用到的理论计算方法,还有一种极限方式(参考Gradient checking)的定义: $$\frac{\partial{L}}{\partial{W}} \approx \frac{L(W + \epsilon) - L(W - \epsilon)}{2\epsilon}$$

注意W中的参数是分开检验的,检验一个参数时,其他参数保持不变。

结构

按layer来组织网络,输入层、隐含层、输出层、loss函数都是layer。需要注意的是对于由weight相连的layer,$layer_k$与$W_{k\rightarrow k+1}$放在一起,还包含了激活函数。那么forward时,forward($Y_{k-1}$, layer_k)得到的是$Y_k$,传入下一层。backward时,用上一层返回的$\delta_{\delta_{k+1}}$计算$\Delta W$和$\delta_k$,将$\delta_k$作为backward的输出。一层的输入输出入下图所示:

从感知机的角度组织网络

一层网络看成并列在一起的多个感知机(如下图)。对于第k层网络,求权重的导数。 $$\begin{eqnarray} \frac{\partial{L}}{\partial{W_{k}(i, j)}} &=& \frac{\partial{L}}{\partial{y_k(j)}} \cdot \frac{\partial{y_k(j)}}{\partial{W_{k}(i, j)}}\\ &=& \delta_k(j) \cdot \frac{\partial{y_k(j)}}{\partial{W_{k}(i, j)}} \end{eqnarray}$$

$$\begin{eqnarray} \frac{\partial{L}}{\partial{Bias_k(j)}} &=& \frac{\partial{L}}{\partial{y_k(j)}}\cdot \frac{\partial{y_k(j)}}{\partial{Bias_k(j)}}\\ &=& \delta_k(j) \end{eqnarray}$$
$$\begin{eqnarray} \frac{\partial{y_k(j)}}{\partial{W_k(i, j)}} &=& \frac{\partial{y_k(j)}}{\partial{net_k(j)}}\cdot \frac{\partial{net_k(j)}}{\partial{W_k(i, j)}}\\ &=& f_k^{'}(net_k(j)) \cdot y_{k-1}(i) \end{eqnarray}$$
$$\begin{eqnarray} \frac{\partial{L}}{\partial{y_k(j)}} &=& \sum_z \frac{\partial{L}}{\partial{y_{k+1}(z)}}\cdot \frac{\partial{y_{k+1}(z)}}{\partial{y_k(j)}}\\ &=& \sum_z \frac{\partial{L}}{\partial{y_{k+1}(z)}}\cdot \frac{\partial{y_{k+1}(z)}}{\partial{net_{k+1}(z)}} \cdot \frac{\partial{net_{k+1}(z)}}{\partial{y_k(j)}}\\ &=& \sum_z \delta_{k+1}(z) \cdot f_{k+1}^{'}(net_{k+1}(z)) \cdot W_{k+1}(j, z) \end{eqnarray}$$
$$\frac{\partial{L}}{\partial{W_k(i, j)}} = \delta_k(j) \cdot f_k^{'}(net_k(j)) \cdot y_{k-1}(i)$$
$$\delta_k(j) = \sum_z \delta_{k+1}(z) \cdot f_{k+1}^{'}(net_{k+1}(z)) \cdot W_{k+1}(j, z)$$
$$\frac{\partial{L}}{\partial{W_k}} = \frac{y_{k-1}^T \times \left[\delta_k \cdot f_k^{'}(net_k)\right]}{N}$$

注意 多个样本更新weight时取的是$\Delta$的平均值。

$$\frac{\partial{L}}{\partial{Bias_k}} = mean(\delta_k)$$

bias的更新也是取平均值。

$$\delta_k = \left[\delta_{k+1} \cdot f^{'}_{k+1}(net_{k+1})\right] \times W_{k+1}^T$$

In [ ]: