真锋
永远保持一颗学习和专注的心
嵌入式视觉笔记

神经网络参数初始化方法

本文属于学习笔记,综合了网上对权重初始化方法的资料总结而来,部分公式没有手写。

神经网络的训练过程中的参数学习是基于梯度下降法进行优化的。梯度下降法需要在开始训练时给每一个参数赋一个初始值。这个初始值的选取十分关键。一般我们希望数据和参数的均值都为 0,输入和输出数据的方差一致。在实际应用中,参数服从高斯分布或者均匀分布都是比较有效的初始化方式。 
所以理想的网络参数初始化是很重要的,但是现在框架都定义了很多参数初始化方式,可以直接调用,比如tensorflow的变量初始化方式如下: 
initializer:是变量初始化的方式,初始化的方式有以下几种:

  • tf.constant_initializer:常量初始化函数
  • tf.random_normal_initializer(mean=0.0, stddev=0.01):均值为 0 ,方差为 1 的正态分布
  • tf.truncated_normal_initializer(mean=0.0, stddev=0.01):带截断的正态分布
  • tf.random_uniform_initializer(-0.1, 0.1):均匀分布
  • tf.zeros_initializer:全部是0
  • tf.ones_initializer:全是1
  • tf.uniform_unit_scaling_initializer:满足均匀分布,但不影响输出数量级的随机值
with tf.variable_scope('layer1-conv1'):
    conv1_weights = tf.get_variable("weight",[11,11,3,96],initializer=tf.truncated_normal_initializer(stddev=0.01))
    conv1_biases = tf.get_variable("bias", [96], initializer=tf.constant_initializer(0.0)) # 定义偏置变量并初始化
    conv1 = tf.nn.conv2d(input_tensor, conv1_weights, strides=[1, 4, 4, 1], padding='SAME') # 'SAME'添加
    relu1 = tf.nn.relu(tf.nn.bias_add(conv1, conv1_biases))  

权重初始化方式分类

常用的网络参数初始化方式有全零初始化随机初始化Xaxier初始化He初始化随机初始化With BNPre-train初始化,特性如下图所示(来源于网络): 

来源网络

全零初始化

通过合理的数据预处理和规范化,当网络收敛到稳定状态时,参数(权值)在理想情况下应基本保持正负各半的状态(此时期望(平均值)为 0 )。全零初始化参数可使得初始化时参数得期望(expectation)与网络稳定时参数的期望一致为零。但是,实际上参数全为0时网络不同神经元的输出必然相同,输出相同则导致梯度更新完全一样,这样直接导致了网络神经元无法改变,也就无法训练。

随机初始化

实际应用中,参数随机初始化,包含均匀随机初始化和正态随机初始化,在 tensorflow 中对应的代码为:

  • 均匀随机:tf.initializers.random_uniform(-0.1, 0.1)
  • 正态分布:tf.initializaers.random_normal(0, 1),均值为0,方差为1的正态分布。
  • 正态分布带截断:tf.initializers.truncated_normal(0, 1),生成均值为0,方差为1的正态分布,若产生随机数落到2σ外,则重新生成

假设网络输入神经元个数为\( n_{in} \),输出神经元个数为\( n_{out}\),则服从高斯分布的参数随机化为: 

$$w = 0.001*randn(n_{in}, n_{out})$$

其中的高斯分布为均值为 0,方差为 1 的标准高斯分布(standard normal distribution)。式中的“0.001”为控制参数量纲的因子,这样可使得参数期望能保持在接近 0 的较小数值范围内。

Xavier初始化

权重参数随机初始化会带来一个问题,即网络输出数据分布的方差会随着输入神经元个数改变,为解决这一问题,会在初始化的同时加上对方差大小的规划化,Glorot提出了Xavier初始化方式,其中服从高斯分布的Xavier初始化公式如下:

$$w =  0.001*randn(n_{in}, n_{out})*stdev ,(stdev=sqrt(1/n))$$

服从高斯分布的Xavier初始化tensorflow代码如下 :

# 适用于普通激活函数(tanh,sigmoid):stdev 为高斯分布的标准差,均值设为 0
stdev = np.sqrt(1/n) 
W = tf.Variable(np.random.randn(n_in, n_out) * stdev)

服从均匀分布的Xavier初始化tensorflow代码如下:

# 适用于普通激活函数(tanh, sigmoid)
scale = np.sqrt(3/n)
W = tf.Variable(np.random.uniform(low=-scale, high=scale, size=[n_in, n_out]))

其中,n为输入神经元个数\(n_{in}\),有时也可指定为\( (n_{in}+n_{out})/2\)。Xavier这样初始化的原因在于维持了输入输出数据分布方差的一致性。公式分析如下图(来源于CNN解析卷积神经网络书籍,没时间写公式了): 

来源CNN解析神经网络-魏秀参

He初始化

Xavier方法未考虑非线性映射函数对输入 s(未经过激活函数的网络层输出结果) 的影响,使用如RELU等非线性映射函数后,输出的期望往往不再为 0 ,为解决这个问题,2015 年 He 等人提出改进-将非线性映射造成的影响考虑进参数初始化中,其中服从高斯分布的He初始化公式如下:

$$w = 0.001*randn(n_{in}, n_{out})*stdev ,(stdev=sqrt(2/n))$$

服从高斯分布的 He初始化 tensorflow代码如下 :

# 适用于 ReLU:stdev 为高斯分布的标准差,均值设为 0
stdev = np.sqrt(2/n)
W = tf.Variable(np.random.randn(n_in, n_out) * stdev) 

服从均匀分布的 He初始化 初始化tensorflow代码如下 :

# 适用于 ReLU
scale = np.sqrt(6/n)
W = tf.Variable(np.random.uniform(low=-scale, high=scale, size=[n_in, n_out]))

梯度弥散也叫梯度消失。概率论中用方差来度量随机变量和数学期望(即均值)之间的偏离程度。统计中的方差(样本方差)是每个样本值与全体样本值的平均数之差的平方值的平均数。方差符号为Var,方差公式如下: 
$$\sigma^{2} = \frac{\sum(X -\mu)^{2}}{N}$$
(\(\sigma^{2}\)为总体方差, X为变量, \(\mu\)为总体均值, N为总体例数。)

Keras网络参数初始化

上面内容将网络参数初始化都是用 tensorflow 代码,这里再给出 keras 如何使用初始化方法,这里说的初始化方法是网络权重参数初始化方法,包括全连接层和卷积层。

keras选定初始化方法

在 Keras 不同的层可能使用不同的关键字来传递初始化方法,但是,一般来说指定初始化方法的关键字是 kernel_initializer 和 bias_initializer。一个初始化器可以由字符串指定(必须是下面的预定义初始化器之一),或一个callable的函数,例如:

from keras import initializers
# callable函数指定初始化方法
model.add(Dense(64, kernel_initializer=initializers.random_normal(stddev=0.01)))
# 字符串指定初始化方法
# also works; will use the default parameters.
model.add(Dense(64, kernel_initializer='random_normal'))

Initializer 是所有初始化方法的父类,不能直接使用,如果想要定义自己的初始化方法,请继承此类。

同时记住,kears中卷积层比如conv2d的默认初始化方法是 ‘glorot_uniform’方法(也称作Xavier正态分布初始化),通过看keras源码可以知道:

@interfaces.legacy_conv2d_support
def __init__(self, filters,
                kernel_size,
                strides=(1, 1),
                padding='valid',
                data_format=None,
                dilation_rate=(1, 1),
                activation=None,
                use_bias=True,
                kernel_initializer='glorot_uniform',
                bias_initializer='zeros',
                kernel_regularizer=None,
                bias_regularizer=None,
                activity_regularizer=None,
                kernel_constraint=None,
                bias_constraint=None,
                **kwargs):

想要使用其他初始化方法比如 He初始化,可以自己指定,比如给conv2d定义He初始化方法:

conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv2)

keras自定义初始化器

Keras 支持常见的初始化器,如下:

初始器方法初始器函数
全零初始化 Zeros keras.initializers.Zeros()
全1初始化 Oneskeras.intiailizers.Ones()
正太分布初始化 RandomNormal: keras.initializers.RandomNormal(mean=0.0, stddev=0.05, seed=None)),mean:均值 , stddev:标准差,seed:随机数种子
均匀分布初始化 RandomUniform keras.initializers.RandomUniform(minval=-0.05, maxval=0.05, seed=None),minval:均匀分布下边界 maxval:均匀分布上边界, seed:随机数种子
截尾高斯分布初始化 TruncatedNormal keras.initializers.TruncatedNormal(mean=0.0, stddev=0.05, seed=None),mean:均值, stddev:标准差,seed:随机数种子。
Glorot正态分布初始化方法(Xavier正态分布初始化)glorot_normal glorot_normal(seed=None),seed:随机数种子
Glorot 均匀分布初始化方法 glorot_uniform glorot_uniform(seed=None),seed:随机数种子
He正太分布初始化方法 he_normalhe_normal(seed=None),seed:随机数种子
He均匀分布初始化方法 he_uniform he_uniform(seed=None),seed:随机数种子

更多初始化器,请参考官方文档

如果需要传递自定义的初始化器,则该初始化器必须是callable的,并且接收shape(将被初始化的张量shape)和dtype(数据类型)两个参数,并返回符合shapedtype的张量。

from keras import backend as K

def my_init(shape, dtype=None):
    return K.random_normal(shape, dtype=dtype)

model.add(Dense(64, init=my_init))

总结

  1. 使用 RELU(without BN) 激活函数时,最好选用 He 初始化方法,将参数初始化为服从高斯分布或者均匀分布的较小随机数。
  2. 使用 BN 时,减少了网络对参数初始值尺度的依赖,此时使用较小的标准差(eg:0.01)进行初始化即可。
  3. 借助预训练模型中参数作为新任务参数初始化的方式也是一种简便易行且十分有效的模型参数初始化方法。
  4. 当前的主流初始化方式 Xavier, 主要是为了保持每层的输入与输出方差相等, 而参数的分布采用均匀分布或高斯分布均可.

参考资料

神经网络之权重初始化 
深度学习中的参数初始化 
神经网络中的权重初始化
魏秀参-CNN解析神经网络

赞赏

发表评论

textsms
account_circle
email

嵌入式视觉笔记

神经网络参数初始化方法
本文属于学习笔记,综合了网上对权重初始化方法的资料总结而来,部分公式没有手写。 神经网络的训练过程中的参数学习是基于梯度下降法进行优化的。梯度下降法需要在开始训练时给每一…
扫描二维码继续阅读
2019-07-23