数值稳定性
这里的 t t t表示层,假设 h t − 1 h^{t-1} ht−1是第 t − 1 t-1 t−1层隐藏层的输出,经过一个 f t f_{t} ft得到第 t t t层隐藏层的输出 h t h^{t} ht。
y y y表示 x x x进来,第一层一直到第 d d d层,最后到一个损失函数,就是我们预测的,要优化的目标函数。( y y y这里不是预测, y y y还包括了损失函数。)
如果我们计算损失 l l l关于我们某一个层权重 W t W^{t} Wt的梯度的话,损失 l l l自顶向下求导,一直求到第 t t t层的输出 h t h^{t} ht,再乘以第 t t t层的输出 h t h^{t} ht关于第 t t t层的权重 W t W^{t} Wt的导数。
注意到说,这里的所有的 h h h都是一些向量,向量关于向量的导数是一个矩阵,所以黄色括号这里是一个 d − t d-t d−t次的矩阵乘法。
我们的主要问题就在这里,因为我们做了太多的矩阵乘法!!!
梯度爆炸:假设我的梯度都是一些比 1 1 1大一点的数字,然后我就是对 1.5 1.5 1.5作 100 100 100次,假设我有 100 100 100层的话,作 100 100 100次就会得到一个 4 × 1 0 17 4 \times 10^{17} 4×1017的数,当然这个数浮点数是能表示的,但是这个数很容易带来浮点数上限的问题。
梯度消失:假设我的梯度是一个小于 1 1 1的数,就算不是太小,但是作 100 100 100层的话,那么 0.8 0.8 0.8的 100 100 100次方,也是 2 × 1 0 − 10 2 \times 10^{-10} 2×10−10次方,也是个非常非常小的数。慢慢地,梯度就很快不见了。
第 t t t层的输入 h t − 1 h^{t-1} ht−1,也就是第 t − 1 t-1 t−1层的输出,第 t t t层的权重 W t W_{t} Wt乘以我的第 t t t层的输入 h t − 1 h^{t-1} ht−1,然后我们假设省略掉偏移,我们直接在输出上作激活函数。
Relu的导数,如果 x > 0 x > 0 x>0,导数为 1 1 1,否则为 0 0 0。
我们是用GPU的时候,通常会使用 16 16 16位浮点数。 16 16 16浮点数的缺点是,它的数值范围很小。如果你的值超出了我这个区间,那我就变成无穷大了。
学习率不好调,大一点点就炸掉了,小一点点就不动,学习率只能在一个很小的范围内比较合适,对模型训练的调参很麻烦。
蓝色是值的函数,黄色是梯度的函数,当值很大的时候,梯度很小,对于激活函数,当输入稍微大一点点的时候,它的导数就会变成 0 0 0。
让训练更加稳定
我们的核心问题是说,如何让我们的训练更加稳定,也就是让梯度不要太大也不要太小。
归一化:
- 把梯度变成均值为 0 0 0,方差为 1 1 1的数,不管有多大,都拉回来
- 梯度剪裁:比如,如果梯度 > 5 >5 >5,就把它变成 5 5 5, < − 5 <-5 <−5,就把它变成 − 5 -5 −5,就是强行把梯度剪裁到一个范围里面
t t t是我第 t t t层的输出, i i i是我的第 i i i个元素,所以 h i t h_{i}^{t} hit是个标量,我把它当作随机变量。
正向:我的输出的期望为 0 0 0,方差假设为 a a a。
反向:损失函数关于第 t t t层输出的第 i i i个元素,我一样希望它的期望为 0 0 0,方差为常数 b b b。
不管哪个层,不管哪一层的哪个输出,不管我作多深,都可以保证数值在一个合理的范围内。这是我们希望的假设,我们要设计神经网络,使得它满足这个性质。
权重初始化
越陡的地方梯度越大,因为梯度指向最陡的方向。
使用 N ( 0 , 0.01 ) N(0,0.01) N(0,0.01)有可能太小,有可能太大,不能保证深度神经网络。
假设权重是独立同分布,那可以说均值等于0,方差等于 γ t \gamma_{t} γt, t t t就是层数。
我的这一层的输入 h i t − 1 h_{i}^{t-1} hit−1也是独立于我当前层的权重,这两个事件是独立的事件。
假设没有激活函数,这样可以求出均值为 0 0 0。(因为独立同分布)
我们需要满足两个条件
第一个条件是要满足每次我的前项的输出方差是一致的,
第二个条件是要使得梯度是一样的。
这两个条件很难同时满足。
除非输入刚好等于输出,那不然的话,无法同时满足这两个条件。
Xavier初始化,取个折中,作个权衡。
给定我的神经网络的当前层的输入和输出的大小,那我就能确定我的权重需要满足的方差的大小。
Xavier初始化,是我们常用的模型初始化的方法,意思是我的初始化权重的方差是根据我的输入和输出维度来定的。
当你的输入和输出长得不那么一样的时候,或者每个网络变化比较大的时候,可以根据输入和输出来适配我的权重形状,使得我希望我的梯度和输出的方差都在一个恒定的范围里。
激活函数
正向反向都意味着,你这个激活函数,必须是 f ( x ) = x f(x)=x f(x)=x。
对于tanh和relu来讲,在零点附近,确实是近似到 f ( x ) = x f(x)=x f(x)=x。
sigmod不过原点,但是可以把sigmod调整一下。
- 合理的权重初始值:Xavier初始化
- 激活函数,尽量用relu和tanh,实在不行用sigmod的变体