背景
在全连接网络中,参数初始化应该满足以下两个条件:
1、各层激活值不会出现饱和现象;
2、各层激活值不位0.
一般再训练SLP/逻辑回归模型时才使用0初始化所有参数,深度模型都不会使用0初始化所有参数
1、随机正态分布
一般而言,参数的初始值不能太小,因为较小的参数再反向传播时会导致过小的梯度产生,对于深度网络而言,会产生梯度弥散的问题,降低参数的收敛速度。
np.random.randn(node_in, node_out)
设均值为0,标准差为1。
初始化参数为正太分布在神经网络中应用的最多,可以初始化为标准正太分布和截断正太分布。
tf中使用 tf.random_normal_initializer() 类来生成一组符合标准正太分布的tensor。
tf中使用 tf.truncated_normal_initializer() 类来生成一组符合截断正太分布的tensor。
tf.random_normal_initializer 类和 tf.truncated_normal_initializer 的构造函数定义:
def __init__(self, mean=0.0, stddev=1.0, seed=None, dtype=dtypes.float32):
self.mean = mean
self.stddev = stddev
self.seed = seed
self.dtype = _assert_float_dtype(dtypes.as_dtype(dtype))
mean: 正太分布的均值,默认值0
stddev: 正太分布的标准差,默认值1
seed: 随机数种子,指定seed的值可以每次都生成同样的数据
dtype: 数据类型
import tensorflow as tf
init_random = tf.random_normal_initializer(mean=0.0, stddev=1.0, seed=None, dtype=tf.float32)
init_truncated = tf.truncated_normal_initializer(mean=0.0, stddev=1.0, seed=None, dtype=tf.float32)
with tf.Session() as sess:
x = tf.get_variable('x', shape=[10], initializer=init_random)
y = tf.get_variable('y', shape=[10], initializer=init_truncated)
x.initializer.run()
y.initializer.run()
print(x.eval())
print(y.eval())
#output:
# [-0.40236568 -0.35864913 -0.94253045 -0.40153521 0.1552504 1.16989613
# 0.43091929 -0.31410623 0.70080078 -0.9620409 ]
# [ 0.18356581 -0.06860946 -0.55245203 1.08850253 -1.13627422 -0.1006074
# 0.65564936 0.03948414 0.86558545 -0.4964745 ]
2、 标准初始化,权重参数服从确定区间内均匀分布
权重参数服从确定区间内均匀分布,从(-1/sqrt(d),1/sqrt(d))均匀分布中生成神经元的权重,d为每个神经元的输入数量,除以d可以确保神经元的输出有相同的分布。
可以看出标准初始化方法得到一个非常好的特性:隐层的状态的均值为0,方差为常量1/3,和网络的层数无关,这意味着对于sigmoid函数来说,自变量落在有梯度的范围内
。但是因为sigmoid激活值都是大于0的,会导致下一层的输入不满足E(⋅)=0。其实标准初始化也只适用于满足下面将要提到的Glorot假设的激活函数,比如tanh
。
tf中使用 tf.random_uniform_initializer 类来生成一组符合均匀分布的tensor。
tf.random_uniform_initializer类构造函数定义:
def __init__(self, minval=0, maxval=None, seed=None, dtype=dtypes.float32):
self.minval = minval
self.maxval = maxval
self.seed = seed
self.dtype = dtypes.as_dtype(dtype)
minval: 最小值
maxval: 最大值
seed:随机数种子
dtype: 数据类型
import tensorflow as tf
init_uniform = tf.random_uniform_initializer(minval=0, maxval=10, seed=None, dtype=tf.float32)
with tf.Session() as sess:
x = tf.get_variable('x', shape=[10], initializer=init_uniform)
x.initializer.run()
print(x.eval())
# output:
# [ 6.93343639 9.41196823 5.54009819 1.38017178 1.78720832 5.38881063
# 3.39674473 8.12443542 0.62157512 8.36026382]
从输出可以看到,均匀分布生成的随机数并不是从小到大或者从大到小均匀分布的,这里均匀分布的意义是每次从一组服从均匀分布的数里边随机抽取一个数。
tf中另一个生成均匀分布的类是 tf.uniform_unit_scaling_initializer(),构造函数是:
def __init__(self, factor=1.0, seed=None, dtype=dtypes.float32):
self.factor = factor
self.seed = seed
self.dtype = _assert_float_dtype(dtypes.as_dtype(dtype))
`同样都是生成均匀分布,tf.uniform_unit_scaling_initializer 跟 tf.random_uniform_initializer 不同的地方是前者不需要指定最大最小值,是通过公式计算出来的`:
max_val = math.sqrt(3 / input_size) * factor
min_val = -max_val
input_size是生成数据的维度,factor是系数。
import tensorflow as tf
init_uniform_unit = tf.uniform_unit_scaling_initializer(factor=1.0, seed=None, dtype=tf.float32)
with tf.Session() as sess:
x = tf.get_variable('x', shape=[10], initializer=init_uniform_unit)
x.initializer.run()
print(x.eval())
# output:
# [-1.65964031 0.59797513 -0.97036457 -0.68957627 1.69274557 1.2614969
# 1.55491126 0.12639415 0.54466736 -1.56159735]
二、Xavier初始化
self.params['w1'] = np.random.randn(self.input_size, self.num1)/np.sqrt(self.input_size)#服从随机正太分布
# self.params['b1'] = np.zeros(self.num1,)
self.params['b1'] = 0.001*np.ones(self.num1,)
self.params['w2'] = np.random.randn(self.num1, self.cls_num)/np.sqrt(self.num1)
# self.params['b2'] = np.zeros(self.cls_num,)
self.params['b2'] = 0.001*np.ones(self.cls_num,)
Xavier initialization 发音/‘zeɪvɪr/可以解决上面的问题!其初始化方式也并不复杂。Xavier初始化的基本思想是保持输入和输出的方差一致,这样就避免了所有输出值都趋向于0,使得网络中信息可以更好的流动。
它有个缺陷:只适用于关于0对称、呈线性的激活函数,比如sigmoid、tanh、softsign。对于ReLU网络可采用Kaming初始化
tensorflow实现:
tf.contrib.layers.variance_scaling_initializer()
variance_scaling_initializer(
factor=2.0,
mode='FAN_IN', ## Count only number of input connections.
uniform=False,
seed=None,
dtype=tf.float32
)
MSRA初始化