9.3 反向传播算法的直观理解
上一小节讲了神经网络的反向传播算法,晦涩难懂!那有没有办法直观地去理解它呢?
很多时候,因为我们是调用库或者包来实现神经网络的,所以大多时候把神经网络看成一个黑盒子、训练的事情交给库来实现就可以了!但是,在学习的时候我们还是要有个大概的思路的,知道它的内部实现机理才能更好的使用它。
相比于前面讲的线性回归、逻辑回归,神经网络没有那么容易理解。那有没有办法通过了解反向传播的一些步骤理解这个算法呢?我们看一下。
前向传播
假设有下图这样的网络,两个输入、一个输出,中间有两个隐藏层。
中间隐藏层的每一个单元,都是前面的输入求和后输入给激活函数的,依次往后传播计算,当然在输入上都会乘以一个系数。如下图:
反向传播算法做的是:
反过来由输出计算输入的过程类似,计算方向不同而已。
回顾一下总的代价函数 :
如果简化一下,只有一个输出并忽略掉正则化项呢?如下图:
上图中,可以看出这个代价函数其实就是反映神经网络的输出与实际值的接近程度,可以近似看成是方差函数。
再回过头来看看反向传播是干嘛的。
到了这里,具体到某一层的某个神经元,这个反向传播的项好像是反映了这个单元上激活项的误差。进一步的说,它是一种一层又一层求偏导的过程。
更具体地,就是我们为了让神经网络的输出更接近目标值,我们需要对这些权值做些怎样的变化。这个思想和前面的梯度下降法是类似的。
那这些参数怎样变化呢?我们就从最后面的那个输出的误差一步步反过来往前推算前面的误差,如下图这样一个反向传播的过程。
9.4 实现注意:展开参数
上几个小节,从理论上讲了怎样实现神经网络代价函数的计算。当然,我们最终还是要通过代码告诉计算机怎样去实现这样一个算法。
本节将快速的介绍代码实现神经网络的一个重要技巧,将参数从矩阵展开成向量,参数的向量展开在后续的最优化步骤中有重要应用。
9.5 梯度检验
前面的小节中提过在前向网络、后向网络中计算导数的算法,但是反向传播算法因为实现起来比较复杂,所以会出现一些意想不到的bugs。
具体表现是,我们的代码看上去运行的很正常,而且代价函数也在不断的减小,但是因为不知道的bug的存在,使得我们得到的神经网络并不是最优解。那有什么办法帮我们把这些不易觉察的bug给揪出来呢?一种思想就是本小节要讲的梯度检验。
基本思想
说来也简单,就是我们使用一种和反向网络不同的方法来计算(估算)梯度,然后通过这个梯度值用来验证我们神经网络计算值的代码是否正确。具体怎么做呢?
计算梯度的代码实现
先看一维的情况:这样的一个函数,我们怎么计算点上的导数呢?
我们知道,这个点上的导数就是 曲线在这个点上的切线的斜率,如下图蓝色线。而我们可以用一个较小的值 在 点处的曲线点连接线的斜率值,用这个值来近似计算这个点的导数,如下图所示:
Octave 中代码如下:
gradApprox = (J(theta + eps) – J(theta - eps)) / (2*eps)
当是一个向量时,我们则需要对偏导数进行检验。因为代价函数的偏导数检验只针对一个参数的改变进行检验,下面是一个只针对进行检验的示例:
最后我们还需要对通过反向传播方法计算出的偏导数进行检验。
根据上面的算法,计算出的偏导数存储在矩阵 中。检验时,我们要将该矩阵展开成为向量,同时我们也将 矩阵展开为向量,我们针对每一个 都计算一个近似的梯度值,将这些值存储于一个近似梯度矩阵中,最终将得出的这个矩阵同 进行比较。
9.6 随机初始化
任何优化算法都需要一些初始的参数。到目前为止我们都是初始所有参数为0,这样的初始方法对于逻辑回归来说是可行的,但是对于神经网络来说是不可行的。如果我们令所有的初始参数都为0,这将意味着我们第二层的所有激活单元都会有相同的值。同理,如果我们初始所有的参数都为一个非0的数,结果也是一样的。
我们通常初始参数为正负ε之间的随机值,假设我们要随机初始一个尺寸为10×11的参数矩阵,代码如下:
Theta1 = rand(10, 11) * (2*eps) – eps
9.7 综合起来
小结一下使用神经网络时的步骤:
网络结构:第一件要做的事是选择网络结构,即决定选择多少层以及决定每层分别有多少个单元。
第一层的单元数即我们训练集的特征数量。
最后一层的单元数是我们训练集的结果的类的数量。
如果隐藏层数大于1,确保每个隐藏层的单元个数相同,通常情况下隐藏层单元的个数越多越好。
我们真正要决定的是隐藏层的层数和每个中间层的单元数。
训练神经网络:
参数的随机初始化
利用正向传播方法计算所有的
编写计算代价函数 的代码
利用反向传播方法计算所有偏导数
利用数值检验方法检验这些偏导数
使用优化算法来最小化代价函数