在深度学习中,神经网络的权重初始化方法($weight$ $initialization$)对模型的收敛速度和性能有着至关重要的影响。说白了,神经网络其实就是对权重参数$w$的不停迭代更新,以达到更好的性能。因此,对权重$w$的初始化则显得至关重要,一个好的权重初始化虽然不能完全解决梯度消失和梯度爆炸的问题,但是对于处理这两个问题是有很大的帮助的,并且十分有利于模型性能和收敛速度。
本文将介绍以下五种常见的权重初始化的方法:
$Xavier$ $initialization$
如果将权重初始化全部为$0$的话,这样的操作等同于等价于一个线性模型,将所有权重设为$0$时,对于每一个$w$而言,损失函数的导数都是相同的,因此在随后的迭代过程中所有权重都具有相同的值,这会使得隐藏单元变得对称,并继续运行设置的$n$次的迭代,会导致网络中同一个神经元的不同权重都是一样的。下面代码为权重初始化为$0$的代码:
Copy 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$次)后,梯度逐渐消失,再训练迭代已经不起什么作用 了。
图 $1$ 权重初始化为$0$的$cost$ $function$变化图 权重随机初始化是比较常见的做法,即$W$随机初始化。随机初始化的代码如下:
上述代码中权重乘$0.01$是因为要把$W$随机初始化到一个相对较小的值,因为如果$X$很大的话,$W$又相对较大,会导致$Z$非常大,这样如果激活函数是$sigmoid$,就会导致$sigmoid$的输出值$1$或者$0$,然后会导致一系列问题(比如$cost$ $function$计算的时候,$log$里是$0$,这样会有点麻烦)。随机初始化后,$cost$ $function$随着迭代次数的变化示意图如下图$2$所示为:
图$2$ 权重随机初始化$cost$ $function$变化图 能够看出,$cost$ $function$的变化是比较正常的。但是随机初始化也有缺点,$np.random.randn()$其实是一个均值为$0$,方差为$1$的高斯分布中采样。当神经网络的层数增多时,会发现越往后面的层的激活函数(使用$tanH$)的输出值几乎都接近于$0$,极易出现梯度消失 。如下图$3$所示:
$Xavier$ $initialization$
在使用以上两种方法来初始化权重极易出现梯度消失的问题,而$Xavier$ $initialization$出现就解决了上面问题。其思想倒就是尽可能的让输入和输出服从相同的分布,这样就能够避免后面层的激活函数的输出值趋向于$0$。本文主要介绍$Pytorch$当中$Xavier$均匀分布和$Xavier$正态分布初始化这两种方式。
我们来简单推导一下$Xavier$初始化的原理:首先我们定义一层的卷积运算为如下公式,其中${n_i}$表示输入的个数。
y = w 1 x 1 + ⋅ ⋅ ⋅ + w n i x n i + b y = w_1x_1 + ··· + w_{ni}x_{ni}+ b y = w 1 x 1 + ⋅⋅⋅ + w ni x ni + b 根据我们学过的概率论统计知识可以得到如下的方差公式:
V a r ( w i x i ) = E [ w i ] 2 V a r ( x i ) + E [ x i ] 2 V a r ( w i ) + V a r ( w i ) V a r ( x i ) Var(w_ix_i)=E[w_i]^2Var(x_i) + E[x_i]^2Var(w_i) + Var(w_i)Var(x_i) Va r ( w i x i ) = E [ w i ] 2 Va r ( x i ) + E [ x i ] 2 Va r ( w i ) + Va r ( w i ) Va r ( x i ) 当我们假设输入和输入的权重的均值都是$0$(使用BN层以后很容易就可以满足)时,上式可以简化为:
V a r ( w i x i ) = V a r ( w i ) V a r ( x i ) Var(w_ix_i)=Var(w_i)Var(x_i) Va r ( w i x i ) = Va r ( w i ) Va r ( x i ) 进一步我们假设输入$x$和权重$w$都是独立同分布,则可以得到:
V a r ( y ) = n i V a r ( w i ) V a r ( x i ) Var(y) = n_iVar(w_i)Var(x_i) Va r ( y ) = n i Va r ( w i ) Va r ( x i ) 于是按照$Xavier$的要求保证输入和输出的方差要一致,则可以得到:
V a r ( w i ) = 1 n i Var(w_i) = \frac{1}{n_i} Va r ( w i ) = n i 1 对于一个多层的网络,某一层的方差可以用累积的形式表达:
V a r [ z i ] = V a r [ x ] ∏ i ‘ = 0 i − 1 n i ‘ V a r [ W i ‘ ] Var[z^i] = Var[x]\prod_{i^{`}=0}^{i-1}n_{i^`}Var[W^{i^`}] Va r [ z i ] = Va r [ x ] i ‘ = 0 ∏ i − 1 n i ‘ Va r [ W i ‘ ] 对于误差反向传播也有类似的表达形式,如下所示,其中$n_{i+1}$表示输出个数
V a r [ ∂ C o s t ∂ s i ] = V a r [ ∂ C o s t ∂ s d ] ∏ i ‘ = i d n i ‘ + 1 V a r [ W i ‘ ] Var[\frac{\partial Cost}{\partial s^i}] = Var[\frac{\partial Cost}{\partial s^d}]\prod_{i^{`}=i}^{d}n_{i^`+1}Var[W^{i^`}] Va r [ ∂ s i ∂ C os t ] = Va r [ ∂ s d ∂ C os t ] i ‘ = i ∏ d n i ‘ + 1 Va r [ W i ‘ ] 综上,为了保证前向传播和反向传播时每一层的方差一致,应满足:
∀ i , n i V a r [ W i ] = 1 \forall_i ,n_iVar[W^i]=1 ∀ i , n i Va r [ W i ] = 1 ∀ i , n i + 1 V a r [ W i ] = 1 \forall_i ,n_{i+1}Var[W^i]=1 ∀ i , n i + 1 Va r [ W i ] = 1 但是,实际当中输入与输出的个数往往不相等,于是为了均衡考量输出和输入,最终我们的权重的方差应满足如下要求:
∀ i , V a r [ W i ] = 2 n i + n i + 1 \forall_i ,Var[W^i]= \frac{2}{n_i + n_{i+1}} ∀ i , Va r [ W i ] = n i + n i + 1 2 1、$Xavier$均匀分布初始化
对于均匀分布来说,我们都知道区间$[a,b]$的方差为:
V a r = ( b − a ) 2 12 Var=\frac{(b-a)^2}{12} Va r = 12 ( b − a ) 2 那么就需要将均匀分布的方差等于我们在上面推导出来的$Xavier$的权重方差,即:
( b − a ) 2 12 = 2 n i + n i + 1 \frac{(b-a)^2}{12} = \frac{2}{n_i + n_{i+1}} 12 ( b − a ) 2 = n i + n i + 1 2 经过化解后$(a+b=0)$可以得到$Xavier$均匀初始化后权重的取值范围为:
W − U [ − 6 n i + n i + 1 , 6 n i + n i + 1 ] W - U[-\frac{\sqrt{6}}{\sqrt{n_i+n_{i+1}}}, \frac{\sqrt{6}}{\sqrt{n_i+n_{i+1}}}] W − U [ − n i + n i + 1 6 , n i + n i + 1 6 ] 原理我们讲完了,现在来看一下在$Pytorch$中是如何调用$Xavier$均匀分布初始化的:
2、$Xavier$正态分布初始化
我们都知道均值为$0$,标准差为$\sigma$的正态分布方差为
同样的,需要将正态分布的方差等于我们在上面推导出来的$Xavier$的权重方差,即:
σ 2 = 2 n i + n i + 1 \sigma^2 = \frac{2}{n_i + n_{i+1}} σ 2 = n i + n i + 1 2 经过化解后可以得到$Xavier$正态分布初始化后权重的标准差为:
σ = 2 n i + n i + 1 \sigma = \sqrt{\frac{2}{n_i + n_{i+1}}} σ = n i + n i + 1 2 那么我们再来看一下在$Pytorch$中是如何调用$Xavier$正态分布初始化的:
3、$Xavier$权重初始化表现效果
如下图$4$所示为采用$Xavier$ $initialization$后每层的激活函数输出值的分布,从图中我们可以看出,深层的激活函数输出值还是非常服从标准高斯分布 。
图$4$ $Xavier$权重初始化后$tanH$激活层输出分布 虽然$Xavier$ $initialization$能够很好的适用于 $tanH$ 激活函数,但对于目前神经网络中最常用的$ReLU$激活函数,还是无能能力,如下图$5$所示为采用$ReLU$激活函数后,$Xavier$ $initialization$初始化的每层激活函数输出值的分布,从图中可以看出当达到$5$、$6$层后几乎又开始趋向于$0$,更深层的话很明显又会趋向于$0$ 。
图$5$ $Xavier$权重初始化后$ReLU$激活层输出分布 由此可见,$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$,如下所示:
V a r ( y ) = 1 2 n i V a r ( w i ) V a r ( x i ) Var(y) = \frac{1}{2}n_iVar(w_i)Var(x_i) Va r ( y ) = 2 1 n i Va r ( w i ) Va r ( x i ) 为了保证输出和输入的方差一直,则可以得到权重的方差为:
V a r ( w i ) = 2 n i Var(w_i) = \frac{2}{n_i} Va r ( w i ) = n i 2 对于$Backward$来说和$Forward$思路是相似的,只不过需要考虑到链式求导法则,这里不予以推导,只给出最终的结果为:
V a r ( w i + 1 ) = 2 n i + 1 Var(w_{i+1}) = \frac{2}{n_{i+1}} Va r ( w i + 1 ) = n i + 1 2 1、$He$ $initialization$均匀分布初始化
和$Xavier$均匀分布初始化操作一样我们得到$He$ $initialization$的取值范围为:
W − U [ − 6 n i + ( a 2 + 1 ) , 6 n i + ( a 2 + 1 ) ] W - U[-\frac{\sqrt{6}}{\sqrt{n_i+(a^2+1)}}, \frac{\sqrt{6}}{\sqrt{n_i+(a^2+1)}}] W − U [ − n i + ( a 2 + 1 ) 6 , n i + ( a 2 + 1 ) 6 ] 在$Pytorch$中$He$ $initialization$也叫做$kaiming$,调用代码如下:
2、$He$ $initialization$正态分布初始化
和$Xavier$正态分布初始化操作一样我们得到$He$ $initialization$的标准差为:
σ = 2 n i + ( a 2 + 1 ) \sigma = \sqrt{\frac{2}{n_i + (a^2+1)}} σ = n i + ( a 2 + 1 ) 2 在$Pytorch$中$Xavier$正态分布初始化的调用代码如下:
3、$He$ $initialization$权重初始化表现效果
如下图$6$所示为采用$He$ $initialization$方式初始化权重后,隐藏层使用$ReLU$时,激活函数的输出值的分布情况,从图中可知,针对$ReLU$激活函数,$He$ $initialization$效果是比$Xavier$ $initialization$好很多。
图$6$ $He$ $initialization$权重初始化后$ReLU$激活层输出分布 由此可见,$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