神经网络的反向传播
要理解梯度丢失(vanishing gradient)和梯度爆炸,首先需要理解神经网络的反向传播算法。
一般来说,训练一个神经网络需要很多个迭代。在每个迭代中,都包含两个步骤。
- 前馈(feed forward):它指的是从神经网络的输入开始,根据每一层的权重和偏置,逐层计算输出,直到得到神经网络的最终输出。这个输出值可以是对图片的分类,也可以是对数据走势的预测等等。
- 反向传播(back propagation):它指的是将神经网络的输出值和标准值进行比较,从而得到误差值。然后计算网络的每一层对这个误差值的“贡献”,并对每一层的权重和偏置进行调整的过程。
一个标准的神经网络训练迭代如下图所示:
链式求导法则
那么,我们如何根据误差值更新每一个权重和偏置呢?这就涉及到我们如何计算误差值对每一个权重和偏置的梯度(gradient)。有了这个梯度,我们就可以利用梯度下降法来更新权重和偏置了。在计算误差对每个权重和偏置的梯度的过程中,我们利用了链式求导法则。下面让我们用一个具体的例子来说明链式求导在反向传播中是如何工作的。
让我们考虑下面这个简单的深度神经网络,它的每一层都只包含一个神经元,一共有三个隐藏层:
这里的表示权重,表示偏置,表示网络的输出,每个神经元的激活函数(activation function)记为,表示第个神经元的输出,其中等于网络的输入,,。
在反向传播过程中,我们需要计算对所有权重和偏置的偏导数。下面我们以为例,说明反向传播是如何工作的。
根据链式求导法则,可以被写为:
由于,因此
反复利用上述求导方法,可以最终得到:
这个等式也反映了反向传播的工作模式:它从输出层开始,逐层计算偏导数,直到输入层为止。然后,利用这些计算出来的偏导数,更新对应的权重和偏置,从而达到反向传播的目的。
梯度不稳定
梯度丢失和梯度爆炸统称为梯度不稳定。它们产生的原因是类似的。让我们首先看看梯度丢失是如何产生的。
梯度丢失
在实际应用中,最常用到的激活函数是Sigmoid函数,它的图像如下:
Sigmoid函数的输出值在0到1之间,它的导函数最大值出现在的时候,最大值为0.25。它的导函数图像画如下所示:
由于我们初始化权重值的时候一般从标准正态分布中采样,所以的绝对值通常小于1,因此我们可以得到:
在深度网络中,为了计算初始层的梯度,我们会累乘多个项 ,最终计算结果将会呈指数级变小,这也就是梯度丢失产生的原因。(个人备注:所以这里说的梯度丢失实际说的是从靠近输出层一侧的隐藏层往输入层方向,计算出来的梯度呈指数级减少)
梯度爆炸
梯度爆炸产生的原因和梯度丢失正好相反。当我们选取的权重值较大时,将大于1。当累乘这些项的时候,计算结果将呈指数级增长。(个人备注:所以这里说的梯度丢失实际说的是从靠近输出层一侧的隐藏层往输入层方向,计算出来的梯度呈指数级增加)
解决方法
梯度不稳定会使得网络不收敛,最终导致我们的训练无法得到任何有意义的结果。因此,我们必须找到相应的解决方法。
梯度剪裁
梯度剪裁是用来解决梯度爆炸问题的。具体描述如下:
- 选取一个梯度剪裁的阈值clip_norm(一般选择1)
- 在计算完每个权重的梯度之后,我们并不像通常那样直接使用这些梯度进行权重更新,而是先求所有权重梯度的平方和global_norm
- 最后把每个梯度乘以缩放因子clip_norm / max(global_norm, clip_norm)。
这样就保证了在一次迭代更新中,所有权重的梯度的平方和在一个设定范围以内。在实现过程中我们可以使用Tensorflow提供的库函数tf.clip_by_global_norm。关于梯度丢失也有相应的解决方法,这里就不再赘述了。