BP 算法的训练过程,各种博客和参考书已经讲的很清楚了,但不管是周志华的《机器学习》,还是林轩田的《机器学习技法》,都只是对 3 层的神经网络的训练做了非向量化的推导。本文将结合 Andrew Ng 的最新视频,从向量和矩阵运算的角度推一遍任意层神经网络(全连接)的训练算法。
为什么要公式向量化
从工程的角度来讲,向量化可以极大的加快程序的运行速度。举一个例子,求两个向量(列向量)的内积,你可能会写出以下两个公式:
在实现的过程中,公式 (1) 使用了 for 循环,公式 (2) 使用了优化过的矩阵乘法,第一种的速度会远快于第二种。
符号约定
符号 | 意义 |
---|---|
l |
表示神经网络的层数 |
m |
batch_size |
C |
表示每一层的神经元的个数 |
J |
cost function |
a |
学习率 |
符号 | shape | 意义 | i 的取值 |
---|---|---|---|
Z[i] |
(Ci, ) |
表示第 i 层的输入(未经激活函数) | 2 - l |
A[i] |
(Ci, ) |
表示第 i 层的输入 | 1 - l |
w[i] |
(Ci, Ci-1) |
表示第 i 层神经元的权重矩阵 | 2 - l |
b[i] |
(Ci, ) |
表示第 i 层神经元的偏置(bias) | 2 - l |
正向传播过程
输入:
A[1]
,即 X
输出:A[l]
,即 y^
对于i = 2, 3, ... l
其中
式子中的 *
符号指的是智能乘法,表示矩阵的对应位置相乘。
误差反向传导过程
BP 算法的原理是利用链式法则,对于每一个路径只访问一次就能求顶点对所有下层节点的偏导值。
先来看看 J
对 w[l]
的导数
其中
再来看 J
对 w[l-1]
的导数
通过观察 dw[l]
和 dw[l-1]
,可以找到递推的规律:
BP 算法前向递推的规律:
起始条件
对于i = l - 1, l - 2, ... 2
,有
经过了一轮循环,求出了神经网络各层的 dw
,然后按照下式更新网络参数:
这就完成了一个 epoch 的训练。
技巧
上述算法的缺点是,由于在后向传播的过程中,需要求激活函数 φ
对 输入 Z[i]
的导数,所以我们在进行前向传播的过程中,需要保存一下每一层的 Z
的值,这无疑增大了内存的消耗,在这里我们有一个技巧:
当激活函数为 Sigomid、Tanh 和 ReLU 时,其导数可由其函数值表示,例如:Sigomid 函数的导数为
f(x) * (1 - f(x))
,Tanh 函数的导数为1 - f(x)^2
。
所以,在参与上式 dZ
的运算中,利用函数的化简可以约分掉与 Z[i]
相关的项(用 A[i]
替代了) 的值,这样我们就可以适当的变换一下迭代公式,使 Z[i]
不出现在公式中,这样就不用再额外存储 Z
了。
实现
具体的 Python 实现,请点击这里
参考资料
- Neural Networks and Deep Learning, Andrew Ng.
- 如何直观地解释 back propagation 算法? - 胡逸夫的回答 - 知乎