原文连接:https://www.leiphone.com/news/201703/3qMp45aQtbxTdzmK.htmlhtml
原文是谷歌大神工程师写的一篇文章,看到以后以为很不错,可以直观地让你深刻理解权重初始化方式以及激活函数对模型训练的影响。node
本文是对原文的解读,并附上了本身的理解以及代码实现。网络
首先,一个好的权重初始化方法可以帮助神经网络更快的找到最优解决方案。app
初始化权重的必要条件1:各网络层激活值不会落在激活函数的饱和区域;dom
初始化权重的必要条件2:各网络层激活值不会都很是接近0,也不会都远离0,最好是均值为0(以0为中心分布)iphone
一、初始化为0的可行性:函数
不可行,若是将全部的权重都初始化为0,那么全部神经元的输出数值都是同样的,那么反向传播时,同一层的全部的梯度都是同样的,权重更新也是同样的,这样的训练是没有意义的。学习
二、可行的几种初始化方式:优化
pre-training:spa
便是利用训练好的模型的参数进行初始化,而后再作fine-tuning。
Random initialization:
10层网络,采用随机初始化权重,每层输出数据分布
import tensorflow as tf import numpy as np import matplotlib.pyplot as plt data = tf.constant(np.random.randn(2000, 800),dtype=tf.float32) layer_sizes = [800 - 50 * i for i in range(0,10)]#10层网络,输入和 num_layers = len(layer_sizes) fcs = [] # To store fully connected layers' output for i in range(0, num_layers-1): X = data if i == 0 else fcs[i - 1] node_in = layer_sizes[i] node_out = layer_sizes[i + 1] # W = tf.Variable(np.random.randn(node_in, node_out))# * 0.01 W = tf.Variable(np.random.randn(node_in, node_out),dtype=tf.float32)*0.01 fc = tf.matmul(X, W) fc = tf.nn.tanh(fc) fcs.append(fc) plt.figure() with tf.Session() as sess: sess.run(tf.global_variables_initializer()) for i in range(0,num_layers-1): plt.subplot(1,num_layers,i+1) x=fcs[i] x=np.array(x.eval()) x=x.flatten() plt.hist(x=x,bins=20,range=(-1,1)) plt.show()
建立10层神经网络,激活函数为tanh,每层权重都采用随机正态分布,均值0,标准差为0.01,如下为输出分布:
由上图能够看出,随着网络层数的增长,输出数值分布逐渐往0靠近,后面几层输出都很是接近0。根据f=W.X+b可知反向传播时对权重W求偏导时,当前层输出的数值X便是反向传播时计算的梯度中的乘积因子,致使梯度很是小,使得参数更新困难。
将以上正态分布初始化参数值调大,标准差变为1:
W = tf.Variable(np.random.randn(node_in, node_out))
再看看输出分布:
能够看出输出数值都集中在1和-1附近,激活函数用的tanh函数,可见已经落入激活函数的饱和区域,tanh函数1和-1附近的梯度为0,参数难以被更新。
Xavier initialization 能够解决上面的问题,Xavier初始化是保持输入和输出的方差一致,这样能够避免全部输出值都趋向于0:
W = tf.Variable(np.random.randn(node_in, node_out)) / np.sqrt(node_in)
下面就是采用Xavier初始化以后的每层的数据分布直方图:
哇,输出不少层以后依然保持良好的分布,很是有利于咱们优化神经网络!
xavier initialization是在线性函数上推导出来,这说明它对非线性函数并不具备广泛适用性,这里仅仅是讨论了tanh激活函数,下面讨论ReLu激活函数的试验
由上图能够看出,因为relu函数特性致使各层网络数据输出都偏向0-1,输出分布不是zero-centred,且到后面几层,数据都在0附近 。
看来Xavier初始化对于Relu激活函数不是很适用。下面看看He initialization是否能解决relu初始化问题,He initialization的思想:在relu 网络中,假定每一层有一半的神经元被激活,另外一半为0,因此要保持方差不变,只需在Xavier的基础上除以2。
W = tf.Variable(np.random.randn(node_in, node_out)) / np.sqrt(node_in/2)
......
fc = tf.nn.relu(fc)
下面看看输出分布,虽然不是zero-centred(以0为中心即0均值)可是起码输出数值分布很稳定都在0-1之间,再也不像以前用xavier那样在0附近,效果不错,推荐在relu网络中使用。
Batch Normalization Layer:
Batch Normalization是一种巧妙粗暴的方法来削弱很差的initialization带来的影响,想要在非线性激活以前,输出值应该有比较好的分布(例如高斯分布),以便于反向传播计算梯度,更新权重。Batch Normalization将输出值强行作了一次Gaussian Normalization和线性变换。
Batch Normalization中全部的操做都是平滑可导,这使得反向传播时候能够有效学习到相应的参数Υβ,Bach Normalization 在train 和test时行为有所差异。训练时的
μβ和σβ由当前batch计算得出;在testing时μβ和σβ应该使用训练时保存的均值或相似的通过处理的值,而不是当前batch计算
Batch Normalization 试验:
Relu激活,随机初始化,无Batch Normal:
随机初始化,有batchNormalization:
fc=tf.contrib.layers.batch_norm(fc,center=True,scale=True,is_training=True)
#这里注意须要将以前的输入数据,输出数据所有转换成dtype=float32,batchnorm要求数据类型为float32,数据不一致会报错哦
由图上能够看出,加了batch Normalization以后,随着网络的加深,后面几层的输出数据分布仍然保持的很好,没有趋向于0,效果不错。
初始化推荐
·在ReLU activation function中推荐使用Xavier Initialization的变种,称之为He Initialization:
总结:良好的权重初始化和激活函数可让数据在网络中正常流动,权重正常更新,以达到学习的目的,经过以上实验能够直观的理解权重初始化和激活函数对输出分布的影响。