在实践中,权重是否合理的进行了初始化,决定着模型的很多走向,比如模型算法离最优解的距离远近或方向是否准确、是否会出现梯度爆炸或梯度消失从而导致训练无法收敛、同等效果下需要花多长时间来训练等。
合理的权重初始化会让模型算法梯度更加正常且更加容易到达全局最优解。同样反过来,不合理权重初始化很容易让模型算法出现梯度问题,让模型算法陷入局部最优解导致训练失败等。
炼丹知识点:模型评估里的陷阱
炼丹知识点:那些决定模型上限的操作
炼丹知识点:模型的燃料,数据采样的秘密
神经网络的权重初始化策略有非常多,不同的初始化策略影响也是非常巨大的,比如:
- tf.keras.initializers.RandomNormal(mean=0., stddev=1.)
- tf.keras.initializers.RandomUniform(minval=-0.05, maxval=0.05, seed=None)
- ...
除了工具包中自带的一些初始化函数,还可以使用非常多的预训练好的embedding进行权重初始化,例如:
- Word2Vec的词向量;
- Glove的词向量;
- ...
例如,在DOTA的《微信视大数据挑战赛决赛方案》中,就使用了多种预训练方法多Embedding层进行初始化,从而得到了良好的线上成绩。具体如下:
因为在设计时,DOTA没有使用传统意义上的特征工程,所以DOTA对3个ID使用了两种方式得到embedding层的初始化权重。
而在这3个ID中,作者又是其中较为特殊的一种存在,特殊的点有两个,
- 从数据逻辑层面,他可以理解为是对底层信息的聚合
- 从产品层面,作者可能是自然人账号、主题账号、甚至搬运工账号等。
因此,对于3ID中间的相互表示,使用了两种不同原理的方法。
有兴趣了解具体方案的同学,可以直接点击《微信视大数据挑战赛决赛方案,如何3ID上721?》阅读。
在很多问题中,我们发现Dense特征都是非常重要的组成部分,而如果我们一拿到Dense特征就将其输入到NN网络中,则往往效果一般,但如果我们对其进行一些输入的预处理,则往往能带来巨大的提升,所以当我们拿到Dense特征的时候第一时间需要特别注意的就是Dense特征的预处理。
一般常用的对Dense特征的预处理策略有下面几个非常不错的选择:
- sklearn中的standardscaler策略;
- Gaussian Normalization策略;
- MinMax标准化策略;
- 图像中的除以最大像素值的策略;
而实践中,我们也发现1和2的效果一般是最好的。但是不同的问题可能会有些许差别。
在你从零开始进行深度学习算法炼丹,自我实现一个神经网络时,往往会出现模型不收敛的情况,那么为什么会出现这种情况呢?大部分情况是用0进行了初始化,因为如果神经元的权重被初始化为0, 在第一次进行网络更新时,所有的中间层的节点的值都为0。一般NN拥有对称的结构,那么在进行第一次误差反向传播时,更新后的网络参数将会相同,在下一次更新时,相同的网络参数学习提取不到有用的特征。这就是你的模型不Work的原因。
拿一个简单的网络举例,如下图所示,该网络包含一个隐层,该隐层有6个神经元,都是Relu的激活函数,我们称之为网络A。我们知道当输入都是正数,weights是以0为中心的,神经元是否激活就完全依赖weights,因为所有输入是正的,所以我们期望50%的情况下神经元是被激活的.我们做个模拟,让Xi~N(10,1),让Wi~N(0,1),我们会得到如下分布,我们发现很多时候,要么对于h0神经元要么不激活,要么就都激活:
再看看另一个极端的例子,数据是以0为中心的,但是weights全正,会得到如下分布,对于H0神经元而言,确实会有50%的概率被激活了,但是对所有神经元而言,要么所有神经元被激活,要么没有一个被激活:
这两种情况下模型就无法收敛了,从另一个角度可以从梯度上进行分析。
当所有输入数据是正的,权重以0为中心时:
梯度的方向正的权重的方向是正的,负的权重方向都是负的,这限制了网络的表达能力,因为一开始网络初始化后就限定了网络的走向。
当数据以0为中心时,权重都是正的:
梯度的方向完全取决于数据本身,这同样限制了网络的表达。
说到现在看起来只要把权重和数据都归一化到0附近,问题就解决了?当然没那么轻松。为了更逼近我们真实训练情况,我们再考虑bias,让Xi, wi, b~N(0, 1),如下图所示:
我们发现网络变的更“灵活”了,从上图左图可以看到一个指定的神经元被激活的数据比例分布在0.0~1.0了,但是我们如果shift上图的bias,网络就会失去“活性”。
如果初始化时数据和权重的方差(范围)太大,梯度可能太大,网络可能超出minimal cost。这类似于在渐变下降过程中选择过高的步长。
如果偏差的方差(范围)太大,我们会看到神经元完全打开或关闭。基于50%的数据,神经元很少被激活。
到目前为止都在讨论relu,常用的激活函数有很多,如果我们网络变成了多层,sigmoid和ReLU输出不是以零为中心且始终为正。因此,不建议在非常深怼网络的隐藏层中使用sigmoid或ReLU激活,因为它可能使得网络深层的神经元不激活。
其实对于不同的激活函数应该使用不同的初始化方法的,如tanh使用Xavier initialization,而Relu应该使用He initialization,至于为什么,感兴趣可以自己模拟下画出以上的分布图。
- 即使数据以零为中心,如果出现以下情况,网络仍可能冻结:
- 权重和偏差不是以零为中心的。
- 数据、权重或偏差的方差(范围)太大。
- 非零中心激活函数用于网络的深隐藏层。
- 增加一个偏差项,以随机化并尽可能减少网络中的重复工作。
- 在隐藏层中使用以零为中心的激活,以帮助抵消先前层的非规范化,或在整个过程中保持规范化。
- 数据、权重和偏差都是相互关联的,必须在初始化时一起仔细检查。
- 使用多个隐藏层时使用Batch Normalization。
至于,为什么Batch Normalization有这么神奇的作用,敬请关注下一期的 炼丹笔记 炼丹知识点。