TensorFlow学习 深度学习参数初始化(weights initializer)策略

前言:深度学习的初始化参数指的是在网络训练之前,对各个节点的权重和偏置进行初始化的过程,很多时候我们以为这个初始化是无关紧要的,不需要什么讲究,但是实际上,一个参数的初始化关系到网络能否训练出好的结果或者是以多快的速度收敛,这都是至关重要的,有时候因为参数初始化的缘故,甚至得不到好的训练结果。本文就来讨论一下参数初始化到底有什么讲究以及常见的参数初始化的一些策略方法。阅读本文需要神经网络相关背景,能够理解误差反向传播算法的实现过程。

目录

一、参数初始化的原则

1.1 一些基本的储备知识

1.2、参数初始化的几个基本条件

1.3、关于方差的三个客观事实

1.4、参数初始化的几点要求

二、常见的参数初始化方法

2.1、Glorot初始化方法

2.2、Kaiming初始化

2.3、lecun初始化

2.4、Batch Normalization

三、参数初始化方法的总结

四、参考文献

一、参数初始化的原则

1.1 一些基本的储备知识

在总结参数初始化的原则之前,先简单看一下网络运行的过程,参数初始化的目的是使网络能够更好的训练,现在大部分的网络训练依然是采用误差的反向传播算法,误差反向传播分为正反两个过程,这里就不再赘述了,先引入几个概念,看下面这幅图,这是一个神经网络的某一个层:

我们引入几个概念:

激活值h:因为它经过了激活函数,实质上就是每一个网络层的输出值;

状态值z:每一个层内部的组成如上所示,z是作为激活函数的一个输入,它在网络层的内部,所以称之为状态值。

数据在网络中流动的时候,会有下面的公式(这里默认没有偏置项):

然后在反向传播的过程中,由于是复合函数的求导,根据链式法则,会有两组倒数,一个是损失函数Cost对Z的导数,一个是损失函数对W的导数,(详细过程这里不推到),这里再引入两个概念:

(1)损失函数关于状态Z的梯度:即

(2)损失函数关于参数W的梯度:即

1.2、参数初始化的几个基本条件

什么样的初始化参数才是最好的呢?这里直接引入几个参数初始化的要求:

Glorot条件:优秀的初始化应该保证以下两个条件:

(1)各个层的激活值h(输出值)的方差要保持一致,即

(2)各个层对状态Z的梯度的方差要保持一致,即

这就是Glorot条件条件,后面会介绍到Glorot初始化。

1.3、关于方差的三个客观事实

既然要保持上面的两个方差在各个网络层中不改变,那也就是它实际上是会改变的,关于为什么会改变的公式推导,这里不详细说明了,直接引入三个基本的客观事实(两有关一无关):

(1)各个层激活值h(输出值)的方差与网络的层数有关

(2)关于状态Z的梯度的方差与网络的层数有关

(3)各个层参数W的梯度的方差与层数无关

关于上面几个基本结论的详情以及公式推导,我们可以参考原论文:

http://proceedings.mlr.press/v9/glorot10a/glorot10a.pdf

借用论文中的几幅图,也可以看出这几个结论。

(1)各层激活值h直方图如下:

可以看出,激活值的方差逐层递减。

(2)各层状态Z的梯度的直方图如下:

状态的梯度在反向传播过程中越往下梯度越小(因为方差越来越小)。

(3)各层参数W的梯度的直方图

参数梯度的方差和层数基本无关。

1.4、参数初始化的几点要求

(1)参数不能全部初始化为0,也不能全部初始化同一个值,为什么,请参见“对称失效”;

(2)最好保证参数初始化的均值为0,正负交错,正负参数大致上数量相等;

(3)初始化参数不能太大或者是太小,参数太小会导致特征在每层间逐渐缩小而难以产生作用,参数太大会导致数据在逐层间传递时逐渐放大而导致梯度消失发散,不能训练

(4)如果有可能满足Glorot条件也是不错的

上面的几点要求中,(1)(2)(3)基本上是硬性要求,这也就衍生出了一系列的参数初始化方法,什么正态标准化等诸如此类的标准化方法,关于各种参数初始化方法,会在后面继续说明。

二、常见的参数初始化方法

我们常见的几种初始化方法是按照“正态分布随机初始化——对应为normal”和按照“均匀分布随机初始化——对应为uniform”,这里就不再多说了,这里介绍几种遇见较少的初始化方法。

2.1、Glorot初始化方法

(1)正态化的Glorot初始化——glorot_normal

Glorot 正态分布初始化器,也称为 Xavier 正态分布初始化器。它从以 0 为中心,标准差为 stddev = sqrt(2 / (fan_in + fan_out)) 的截断正态分布中抽取样本, 其中 fan_in 是权值张量中的输入单位的数量, fan_out 是权值张量中的输出单位的数量。

在keras和tensorflow均有实现,以keras为例:

keras.initializers.glorot_normal(seed=None)

(2)标准化的Glorot初始化——glorot_uniform

Glorot 均匀分布初始化器,也称为 Xavier 均匀分布初始化器。

它从 [-limit,limit] 中的均匀分布中抽取样本, 其中 limit 是 sqrt(6 / (fan_in + fan_out)), fan_in 是权值张量中的输入单位的数量, fan_out 是权值张量中的输出单位的数量。

以keras为例:

keras.initializers.glorot_uniform(seed=None)

(3)Glorot初始化器的缺点

因为Xavier的推导过程是基于几个假设的,

其中一个是激活函数是线性的,这并不适用于ReLU,sigmoid等非线性激活函数;

另一个是激活值关于0对称,这个不适用于sigmoid函数和ReLU函数它们不是关于0对称的。

2.2、Kaiming初始化

Kaiming初始化,也称之为he初始化,也称之为msra初始化,出自大神 何凯明只手。即

Kaiming initializer=he initializer=msra initializer

因为前面讲了Glorot初始化不适合relu激活函数,所以

残差网络的作者何凯明在这篇论文中提出了ReLU网络的初始化方法:Kaming初始化。作者的推导过程针对的其实是卷积网络的前向和反向过程。而为了和Xavier初始化方法保持一致,这里我们还是讨论全连接网络结构。

关于期望、方差的性质,我们已经在Xavier初始化一节介绍过了,这里不再重复。

在Xavier论文中,作者给出的Glorot条件是:正向传播时,激活值的方差保持不变;反向传播时,关于状态值的梯度的方差保持不变。

这在本文中稍作变换:正向传播时,状态值的方差保持不变;反向传播时,关于激活值的梯度的方差保持不变。

(1)正态化的kaiming初始化——he_normal

He 正态分布初始化器。

它从以 0 为中心,标准差为 stddev = sqrt(2 / fan_in) 的截断正态分布中抽取样本, 其中 fan_in是权值张量中的输入单位的数量,在keras中的实现为

keras.initializers.he_normal(seed=None)

(2)标准化化的kaiming初始化——he_uniform

He 均匀方差缩放初始化器。

它从 [-limit,limit] 中的均匀分布中抽取样本, 其中 limit 是 sqrt(6 / fan_in), 其中 fan_in 是权值张量中的输入单位的数量。

keras.initializers.he_uniform(seed=None)

2.3、lecun初始化

出自大神Lecun之手。

(1)标准化化的kaiming初始化——lecun_uniform

LeCun 均匀初始化器。

它从 [-limit,limit] 中的均匀分布中抽取样本, 其中 limit 是 sqrt(3 / fan_in), fan_in 是权值张量中的输入单位的数量。

keras.initializers.lecun_uniform(seed=None)

(2)正态化的kaiming初始化——lecun_normal

LeCun 正态分布初始化器。

它从以 0 为中心,标准差为 stddev = sqrt(1 / fan_in) 的截断正态分布中抽取样本, 其中 fan_in是权值张量中的输入单位的数量。

keras.initializers.lecun_normal(seed=None)

2.4、Batch Normalization

BN是将输入的数据分布变成高斯分布,这样可以保证每一层神经网络的输入保持相同分布。

优点

随着网络层数的增加,分布逐渐发生偏移,之所以收敛慢,是因为整体分布往非线性函数取值区间的上下限靠近。这会导致反向传播时梯度消失。BN就是通过规范化的手段,把每层神经网络任意神经元这个输入值的分布强行拉回到均值0方差1的标准正态分布,使得激活输入值落入非线性函数中比较敏感的区域。可以让梯度变大,学习收敛速度快,能大大加快收敛速度。

Scale and Shift作用

γ和βγ和β是学习到的参数,他们可以让标准正态分布变得更高/更胖和向左右偏移。

三、参数初始化方法的总结

参考

一文详解深度学习参数初始化(weights initializer)策略

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个使用对抗网络深度学习的三维重建代码的基本框架,供参考: ```python import tensorflow as tf import numpy as np import matplotlib.pyplot as plt # 定义生成器模型 def generator_model(): # 定义输入 input_layer = tf.keras.layers.Input(shape=(100,)) # 添加全连接层 x = tf.keras.layers.Dense(256, activation='relu')(input_layer) # 添加全连接层 x = tf.keras.layers.Dense(512, activation='relu')(x) # 添加全连接层 x = tf.keras.layers.Dense(1024, activation='relu')(x) # 添加全连接层 x = tf.keras.layers.Dense(2048, activation='relu')(x) # 添加全连接层 x = tf.keras.layers.Dense(4096, activation='relu')(x) # 添加全连接层 x = tf.keras.layers.Dense(8192, activation='relu')(x) # 添加全连接层 x = tf.keras.layers.Dense(16384, activation='relu')(x) # 添加输出层 output_layer = tf.keras.layers.Reshape((32, 32, 16))(x) # 定义生成器模型 model = tf.keras.models.Model(inputs=input_layer, outputs=output_layer) return model # 定义判别器模型 def discriminator_model(): # 定义输入 input_layer = tf.keras.layers.Input(shape=(32, 32, 16)) # 添加卷积层 x = tf.keras.layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same')(input_layer) # 添加LeakyReLU激活层 x = tf.keras.layers.LeakyReLU()(x) # 添加卷积层 x = tf.keras.layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same')(x) # 添加LeakyReLU激活层 x = tf.keras.layers.LeakyReLU()(x) # 添加扁平层 x = tf.keras.layers.Flatten()(x) # 添加输出层 output_layer = tf.keras.layers.Dense(1, activation='sigmoid')(x) # 定义判别器模型 model = tf.keras.models.Model(inputs=input_layer, outputs=output_layer) return model # 定义对抗网络模型 def gan_model(generator, discriminator): # 冻结判别器 discriminator.trainable = False # 定义输入 input_layer = tf.keras.layers.Input(shape=(100,)) # 使用生成器生成三维模型 generated_model = generator(input_layer) # 判别生成的三维模型 y = discriminator(generated_model) # 定义对抗网络模型 model = tf.keras.models.Model(inputs=input_layer, outputs=y) return model # 定义训练函数 def train(generator, discriminator, gan, dataset, epochs=100, batch_size=32): # 定义优化器 optimizer = tf.keras.optimizers.Adam(lr=0.0002, beta_1=0.5) # 定义损失函数 loss_fn = tf.keras.losses.BinaryCrossentropy(from_logits=True) # 定义迭代器 iterator = dataset.make_initializable_iterator() # 定义迭代器的初始化操作 iterator_init_op = iterator.initializer for epoch in range(epochs): # 初始化迭代器 sess.run(iterator_init_op) while True: try: # 获取一个batch的数据 x = sess.run(iterator.get_next()) # 添加噪声 noise = np.random.normal(0, 1, (batch_size, 100)) # 使用生成器生成三维模型 generated_models = generator.predict(noise) # 训练判别器 real_labels = np.ones((batch_size, 1)) fake_labels = np.zeros((batch_size, 1)) real_loss = loss_fn(discriminator(x), real_labels) fake_loss = loss_fn(discriminator(generated_models), fake_labels) discriminator_loss = real_loss + fake_loss discriminator.trainable = True discriminator_optimizer = optimizer.minimize(discriminator_loss, var_list=discriminator.trainable_weights) # 训练生成器 noise = np.random.normal(0, 1, (batch_size, 100)) gan_loss = loss_fn(gan(noise), real_labels) generator_optimizer = optimizer.minimize(gan_loss, var_list=generator.trainable_weights) except tf.errors.OutOfRangeError: break # 输出日志 print('Epoch: {}, Discriminator loss: {}, Generator loss: {}'.format(epoch, discriminator_loss, gan_loss)) # 加载数据集 data = np.load('data.npy') # 创建数据集对象 dataset = tf.data.Dataset.from_tensor_slices(data) # 打乱数据集 dataset = dataset.shuffle(1000) # 批量化数据集 dataset = dataset.batch(32) # 创建生成器模型 generator = generator_model() # 创建判别器模型 discriminator = discriminator_model() # 创建对抗网络模型 gan = gan_model(generator, discriminator) # 创建会话 sess = tf.Session() # 初始化全局变量 sess.run(tf.global_variables_initializer()) # 训练模型 train(generator, discriminator, gan, dataset, epochs=100, batch_size=32) ``` 这个示例代码中使用了基于GAN的三维重建方法,其中生成器模型用于生成三维模型,判别器模型用于判别真实的三维模型和生成的三维模型,对抗网络模型将生成器和判别器组合在一起进行训练。在训练过程中,使用优化器来最小化损失函数,以提高模型的准确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值