数值稳定性
这里的
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的变体