神经网络Backpropagation算法入门

近期学习了 coursera 上 Andrew Ng 的机器学习课程[1]。总体而言,该课程是入门机器学习的绝佳教程,整体难度不大,但概括了机器学习中很多基础的概念。但弊端在于跳过了过多数学推导,导致有些问题讲解得不是很清楚。特别是在第5周课程 Neural Networks: Learning 中,虽讲解了 BP 算法的轮廓和执行过程,但没有计算图的辅助和数学公式的推导就很难理解,且出现了较多的讹误。因此写下该篇笔记,记录关于BP算法的重要学习路线和结论,以期给其他同样困惑的同学和未来的自己一些提示作用。

该笔记的写作过程中,同样参考了斋藤康毅的《深度学习入门》一书中计算图(computational graph)的叙述,和长躯鬼侠的知乎专栏矩阵求导术(上)中关于标量对矩阵导数计算的说明。

概述

BP 神经网络的实现过程大体上如下:

Step 1 随机产生网络参数的初始值,

Step 2 计算正向传播(Forward propagation)得到预测值,进而计算损失函数的值;

Step 3 通过 Backpropagation 算法计算偏导数,进而得到损失函数的梯度;

Step 4 利用优化算法对参数值进行更新;

Step 5 反复执行Step 2 ~ Step 4 至收敛为止。

Backpropagation 算法是计算神经网络梯度(Step 3)的重要算法,比起计算导数的差分算法,它大大提升了导数的计算速度,进而加快了神经网络的训练速度。

符号系统

本篇笔记的符号系统与 coursera 课程上的基本保持一致,略有修改。简述如下:

图1为神经网络的示例图(未画出偏置项),该网络用于一个多元分类问题,共分为 $K$ 类。

图1 神经网络示例

用 $L$ 表示神经网络总层数, $s_l$ 表示第 $l$ 层的神经元个数(不包括偏置项),共有 $M$ 个数据,数据记为

激活函数记为

$(h_\theta(x))_i$ 表示第 $i$ 个结果。

损失函数记为 $J(\Theta)$ ,表达式如下:

式中, $M$ 为样本个数, $K$ 为分类总数, $\Theta_{ji}^{(l)}$ 为第 $l$ 层后节点编号为 $j$ 、前节点编号为 $i$ 的参数值。

记激活函数 sigmoid 函数为 $g(x)$ ,即:

记 $\delta_j^{(l)}$ 为第 $l$ 层,第 $j$ 个节点的”误差”(coursera: “error”),下面的叙述会具体解释该参数的含义。

前向传播

以 one-hot 表示的单一数据 $(x,y)$ 为例,正向传播过程的表示如下:

本文重点是反向传播,前向传播推导从略。

反向传播

先给出算法,后进行解释,最后进行推导。

算法描述

coursera 课程上的算法描述如下:

Step 1 Training set ${\left\{(x^{(1)},y^{(1)}),(x^{(2)},y^{(2)}),\cdots,(x^{(M)},y^{(M)}) \right\}}$

Step 2 Set $\Delta_{ji}^{(l)}=0$ (for all $l,i,j$).

Step 3 For $m=1,M$

​ Set $a^{(1)}=x^{(m)}$

​ Perform forward propagation to compute $a^{(l)}$ for $l=2,3,\cdots,L$

​ Compute $\delta^{(L)},\delta^{(L-1)},\delta^{(L-2)},\cdots,\delta^{(2)}$

​ $\Delta_{ji}^{(l)}:=\Delta_{ji}^{(l)}+a_j^{(l)}\delta_{i}^{(l+1)}$

Step 4 Computer $D_{ji}^{(l)}$:

​ $D_{ji}^{(l)}=\frac{1}{M}(\Delta_{ji}^{(l)}+\lambda\Theta_{ji}^{(l)})$ if $i\neq0$

​ $D_{ji}^{(l)}=\frac{1}{M}\Delta_{ji}^{(l)}$ if $i=0$

其中 $\delta^{(l)}$ 的计算如下:

计算图及公式推导

要正确理解 BP 算法,有两种方法,一种是基于数学式,一种是基于计算图。前者是比较常见的方法,该方法严密简洁且合理,但如果一上来就围绕数学式进行探讨,会忽略一些根本的东西[2]。但笔者认为,只根据计算图进行理解,不利于后续的编程计算。因而本文将二者相结合进行算法的推导。

计算图按照图中的数据类型可分为两类:标量计算图和矩阵计算图。

标量计算图

对于标量计算图,根据链式法则可以得到下图所示的结果:

图2 标量计算图

在该图中,为使表示清晰,我们省略了反向传播的箭头。以箭头上方的数字表示正向传播的数值,箭头下方的为反向传播的数值。

矢量计算图1

对于矢量计算图,涉及到标量对矩阵的导数[3],输出层的计算图如图3。

图3 矩阵计算图1

计算推导过程如下:

若不考虑正则化,样本数量为 $M$ 时的总损失函数的矢量化形式为

其中, $\mathbf{1}$ 为元素全为1的与 $y$ 尺寸相同的矩阵, $tr(A)$ 表示矩阵 $A$ 的迹。这里的总损失函数 $J’=J\cdot M$ 。

故有

进而

式中,符号 $\odot$ 为逐元素乘法。

由此,得到如图3所示的计算图。

矢量计算图2

按照矢量计算图1的思路,继续反向传播,以正向传播中的 ${z^{(4)}=\Theta^{(3)} a^{(3)}}$ 为例,我们给出如下计算图:

图4 矩阵计算图2

计算推导过程如下:

故有

类似地,有

进而

以此类推,有

从以上的推导中可以发现, $\delta^{(l)}$ 的定义为总损失函数对 $z^{(l)}$ 的偏导数,即

由此自然可以得到

这就是算法描述中的 $\Delta_{ji}^{(l)}$ 表达的矢量化表达。

若考虑正则化和损失函数的平均值 $J=J’/M$ ,自然有算法描述中的

需要注意的是,由于前向传播时,每到下一层节点时,总是先添加偏置项($a_0^{(1)},a_0^{(2)},a_0^{(3)}$)再正向传播计算对应的($z^{(2)},z^{(3)},z^{(4)}$),所以在反向传播时得到($\delta^{(3)},\delta^{(2)}$)时应去掉第一个元素再进行反向传播。一般地,应去调($\delta^{(l-1)},\delta^{(l-2)},\cdots,\delta^{(l-1)}$)中的第一个元素再进行反向传播。这一做法按照计算图是十分易于理解的。

经过以上推导,我们对 BP 算法的反向传播过程有了更加深入的了解,也加深了对算法描述中的内容的理解。

注解

  1. 斋藤康毅《深度学习入门》一书中,输出层的激活函数为 softmax ,而损失函数采用了交叉熵误差函数,可以证明,这两个函数得到的效果与 Coursera 中输出层采用 sigmoid 函数和相应损失函数的效果是一致的,都是使 $\delta^{(4)}=a^{(4)}-y$ 成立,也就是将”误差“返回给上一层(过程见矩阵求导术(上)例6解2)。这样的”漂亮“结果是针对问题特意设计得到的。
  2. 关于算法描述的证明,可以用标量的形式,与矩阵形式的证明同理,过程稍显繁琐,具体过程可参考斋藤康毅《深度学习入门》一书第 5 章及附录。

参考文献

[1] Andrew Ng 的 Coursera 课程 Machine Learning

[2] 斋藤康毅.深度学习入门:基于Python
的理论与实现.北京:人民邮电出版社,2018.121~161

[3] 长驱鬼侠的知乎专栏.矩阵求导术(上)