深度学习之权重初始化
在深度学习中,神经网络的权重初始化方法($weight$ $initialization$)对模型的收敛速度和性能有着至关重要的影响。说白了,神经网络其实就是对权重参数$w$的不停迭代更新,以达到更好的性能。因此,对权重$w$的初始化则显得至关重要,一个好的权重初始化虽然不能完全解决梯度消失和梯度爆炸的问题,但是对于处理这两个问题是有很大的帮助的,并且十分有利于模型性能和收敛速度。
本文将介绍以下五种常见的权重初始化的方法:
权重初始化为$0$
权重随机初始化
$Xavier$ $initialization$
$He$ $initialization$
预训练权重
权重初始化为$0$
如果将权重初始化全部为$0$的话,这样的操作等同于等价于一个线性模型,将所有权重设为$0$时,对于每一个$w$而言,损失函数的导数都是相同的,因此在随后的迭代过程中所有权重都具有相同的值,这会使得隐藏单元变得对称,并继续运行设置的$n$次的迭代,会导致网络中同一个神经元的不同权重都是一样的。下面代码为权重初始化为$0$的代码:
def initialize_parameters_zeros(layers_dims):
"""
Arguments:
layer_dims -- python array (list) containing the size of each layer.
Returns:
parameters -- python dictionary containing your parameters "W1", "b1", ..., "WL", "bL":
W1 -- weight matrix of shape (layers_dims[1], layers_dims[0])
b1 -- bias vector of shape (layers_dims[1], 1)
...
WL -- weight matrix of shape (layers_dims[L], layers_dims[L-1])
bL -- bias vector of shape (layers_dims[L], 1)
"""
parameters = {}
np.random.seed(3)
L = len(layers_dims) # number of layers in the network
for l in range(1, L):
parameters['W' + str(l)] = np.zeros((layers_dims[l], layers_dims[l - 1]))
parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))
return parameters让我们来看看权重初始化为$0$之后其$cost$ $function$是如何变化的,从图中可以看出,当代价函数降到$0.64$(迭代$1000$次)后,梯度逐渐消失,再训练迭代已经不起什么作用了。

权重随机初始化
权重随机初始化是比较常见的做法,即$W$随机初始化。随机初始化的代码如下:
def initialize_parameters_random(layers_dims):
"""
Arguments:
layer_dims -- python array (list) containing the size of each layer.
Returns:
parameters -- python dictionary containing your parameters "W1", "b1", ..., "WL", "bL":
W1 -- weight matrix of shape (layers_dims[1], layers_dims[0])
b1 -- bias vector of shape (layers_dims[1], 1)
...
WL -- weight matrix of shape (layers_dims[L], layers_dims[L-1])
bL -- bias vector of shape (layers_dims[L], 1)
"""
np.random.seed(3) # This seed makes sure your "random" numbers will be the as ours
parameters = {}
L = len(layers_dims) # integer representing the number of layers
for l in range(1, L):
parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1])*0.01
parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))
return parameters上述代码中权重乘$0.01$是因为要把$W$随机初始化到一个相对较小的值,因为如果$X$很大的话,$W$又相对较大,会导致$Z$非常大,这样如果激活函数是$sigmoid$,就会导致$sigmoid$的输出值$1$或者$0$,然后会导致一系列问题(比如$cost$ $function$计算的时候,$log$里是$0$,这样会有点麻烦)。随机初始化后,$cost$ $function$随着迭代次数的变化示意图如下图$2$所示为:

能够看出,$cost$ $function$的变化是比较正常的。但是随机初始化也有缺点,$np.random.randn()$其实是一个均值为$0$,方差为$1$的高斯分布中采样。当神经网络的层数增多时,会发现越往后面的层的激活函数(使用$tanH$)的输出值几乎都接近于$0$,极易出现梯度消失。如下图$3$所示:

$Xavier$ $initialization$
在使用以上两种方法来初始化权重极易出现梯度消失的问题,而$Xavier$ $initialization$出现就解决了上面问题。其思想倒就是尽可能的让输入和输出服从相同的分布,这样就能够避免后面层的激活函数的输出值趋向于$0$。本文主要介绍$Pytorch$当中$Xavier$均匀分布和$Xavier$正态分布初始化这两种方式。
我们来简单推导一下$Xavier$初始化的原理:首先我们定义一层的卷积运算为如下公式,其中${n_i}$表示输入的个数。
根据我们学过的概率论统计知识可以得到如下的方差公式:
当我们假设输入和输入的权重的均值都是$0$(使用BN层以后很容易就可以满足)时,上式可以简化为:
进一步我们假设输入$x$和权重$w$都是独立同分布,则可以得到:
于是按照$Xavier$的要求保证输入和输出的方差要一致,则可以得到:
对于一个多层的网络,某一层的方差可以用累积的形式表达:
对于误差反向传播也有类似的表达形式,如下所示,其中$n_{i+1}$表示输出个数
综上,为了保证前向传播和反向传播时每一层的方差一致,应满足:
但是,实际当中输入与输出的个数往往不相等,于是为了均衡考量输出和输入,最终我们的权重的方差应满足如下要求:
1、$Xavier$均匀分布初始化
对于均匀分布来说,我们都知道区间$[a,b]$的方差为:
那么就需要将均匀分布的方差等于我们在上面推导出来的$Xavier$的权重方差,即:
经过化解后$(a+b=0)$可以得到$Xavier$均匀初始化后权重的取值范围为:
原理我们讲完了,现在来看一下在$Pytorch$中是如何调用$Xavier$均匀分布初始化的:
# tensor表示要初始化的张量,gain表示缩放因子
torch.nn.init.xavier_uniform(tensor, gain=1)
# 举例说明:
w = torch.Tensor(3, 5)
nn.init.xavier_uniform(w, gain=math.sqrt(2))2、$Xavier$正态分布初始化
我们都知道均值为$0$,标准差为$\sigma$的正态分布方差为
同样的,需要将正态分布的方差等于我们在上面推导出来的$Xavier$的权重方差,即:
经过化解后可以得到$Xavier$正态分布初始化后权重的标准差为:
那么我们再来看一下在$Pytorch$中是如何调用$Xavier$正态分布初始化的:
# tensor表示要初始化的张量,gain表示缩放因子
torch.nn.init.xavier_normal(tensor, gain=1)
# 举例说明:
w = torch.Tensor(3, 5)
nn.init.xavier_normal(w)3、$Xavier$权重初始化表现效果
如下图$4$所示为采用$Xavier$ $initialization$后每层的激活函数输出值的分布,从图中我们可以看出,深层的激活函数输出值还是非常服从标准高斯分布。

虽然$Xavier$ $initialization$能够很好的适用于 $tanH$ 激活函数,但对于目前神经网络中最常用的$ReLU$激活函数,还是无能能力,如下图$5$所示为采用$ReLU$激活函数后,$Xavier$ $initialization$初始化的每层激活函数输出值的分布,从图中可以看出当达到$5$、$6$层后几乎又开始趋向于$0$,更深层的话很明显又会趋向于$0$。

由此可见,$Xavier$权重初始化方式比较适用于$tanH$和$Sigmoid$激活函数,而对于$ReLU$这种非对称性的激活函数还是容易出现梯度消失的现象。
$He$ $initialization$
$He$ $initialization$是由何凯明大神提出的一种针对$ReLU$激活函数的初始化方法。$He$ $initialization$的思想是:和$Xavier$初始化方式一样,都希望初始化使得正向传播时,状态值的方差保持不变,反向传播时,关于激活值的梯度的方差保持不变。由于小于$0$的值经过$ReLU$激活函数都会变成$0$,而大于$0$的值则保持原值。因此在$ReLU$网络中,假定每一层有一半的神经元被激活,另一半为$0$,所以,要保持$variance$不变,只需要在$Xavier$的基础上再除以2即可。本文主要介绍$Pytorch$当中$He$ $initialization$均匀分布和$He$ $initialization$正态分布初始化这两种方式。
对于$He$ $initialization$的推导来说前面和$Xavier$ $initialization$是相似的,但在方差推到过程中,需要将式子左侧除以$2$,如下所示:
为了保证输出和输入的方差一直,则可以得到权重的方差为:
对于$Backward$来说和$Forward$思路是相似的,只不过需要考虑到链式求导法则,这里不予以推导,只给出最终的结果为:
1、$He$ $initialization$均匀分布初始化
和$Xavier$均匀分布初始化操作一样我们得到$He$ $initialization$的取值范围为:
在$Pytorch$中$He$ $initialization$也叫做$kaiming$,调用代码如下:
# tensor表示要初始化的张量
# a表示这层之后使用的rectifier的斜率系数(ReLU的默认值为0)
# mode可以为“fan_in”(默认)或“fan_out”。
# “fan_in”保留前向传播时权值方差的量级,“fan_out”保留反向传播时的量级。
torch.nn.init.kaiming_uniform(tensor, a=0, mode='fan_in')
# 举例说明:
w = torch.Tensor(3, 5)
nn.init.kaiming_uniform(w, mode='fan_in')2、$He$ $initialization$正态分布初始化
和$Xavier$正态分布初始化操作一样我们得到$He$ $initialization$的标准差为:
在$Pytorch$中$Xavier$正态分布初始化的调用代码如下:
# tensor表示要初始化的张量
# a表示这层之后使用的rectifier的斜率系数(ReLU的默认值为0)
# mode可以为“fan_in”(默认)或“fan_out”。
# “fan_in”保留前向传播时权值方差的量级,“fan_out”保留反向传播时的量级。
torch.nn.init.kaiming_normal(tensor, a=0, mode='fan_in')
# 举例说明:
w = torch.Tensor(3, 5)
nn.init.kaiming_normal(w, mode='fan_out')3、$He$ $initialization$权重初始化表现效果
如下图$6$所示为采用$He$ $initialization$方式初始化权重后,隐藏层使用$ReLU$时,激活函数的输出值的分布情况,从图中可知,针对$ReLU$激活函数,$He$ $initialization$效果是比$Xavier$ $initialization$好很多。

由此可见,$He$ $initialization$权重初始化方式是非常适用于$ReLU$激活函数。
预训练模型
目前更多的使用已经针对相似任务已经训练好的模型,称之为预训练模型。在训练开始时就已经有了非常好的初始化参数,只需要将最后的全连接层进行冻结,训练其他部分即可。
总结
1、权重采用初始化为$0$和随机初始化都比较容易出现梯度消失的问题,因此不常用。
2、$Xavier$权重初始化方式主要针对于$tanH$和$sigmoid$激活函数。
3、$He$ $initialization$权重初始化方式主要针对于$ReLU$激活函数。
4、如果有相似任务已经训练好的模型,也可以考虑采用预训练模型来作权重初始化。
引用
https://zhuanlan.zhihu.com/p/25110150
https://www.jianshu.com/p/cf2dcc624f47
https://zhuanlan.zhihu.com/p/103825243
https://pytorch.org/docs/stable/nn.html#torch-nn-init
https://pytorch-cn.readthedocs.io/zh/latest/package_references/nn_init/
https://blog.csdn.net/dss_dssssd/article/details/83959474?locationNum=16&fps=1
http://machinelearning.wustl.edu/mlpapers/paper_files/AISTATS2010_GlorotB10.pdf
https://blog.csdn.net/u011534057/article/details/53930199?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-0&spm=1001.2101.3001.4242
Last updated