Batch Normalization || 3. 在tensorflow 中bn的坑 —— Is_training、momentum的设置

前言:

Batch Normalization || 1. 原理介绍
Batch Normalization || 2. 在tensorflow 中的API
Batch Normalization || 3. 在tensorflow 中bn的坑 —— Is_training、momentum的设置

1 问题概述

在神经网络中使用 Batch Normalization,已经是一个基本必用的正则手段。

现象:
当训练好神经网络,信心满满的进行预测,却发现结果一塌糊涂。

分析:
训练和测试时,bn中的均值和方差的计算方法。要明确:训练时使用batch内数据的mean和var。测试时会使用更新后的滑动均值和滑动方差)

问题:
那么可能出现bug的点就显而易见了:

  1. 训练时,bn 的 momentum、Is_training 参数设置的是否合理
  2. 训练时,moving_mean 和 moving_var 在每次迭代中是否得到有效的更新
  3. 模型保存时,moving_mean 和 moving_var 是否被保存在图结构中

1.1 momentum 的设置

在tensorflow 中,bn的常用的api:tf.layers.batch_normalization


tf.layers.batch_normalization( inputs,    axis=-1,   momentum=0.99,    training=False )
   

首先我们要理解滑动均值的意义:

滑动平均(exponential moving average),或者叫做指数加权平均(exponentially weighted moving average),可以用来估计变量的局部均值,使得变量的更新与一段时间内的历史取值有关。【Andrew Ng】在【Course 2 Improving Deep Neural Networks】中讲到, t t t 时刻变量 m o v i n g _ m e a n moving\_mean moving_mean 的滑动平均值大致等于过去的 1 / ( 1 − m o m e n t u m ) 1/(1−momentum) 1/(1momentum) 个时刻 m e a n mean mean 值的平均

  • 好处:占内存少,不需要保存过去的10个或者100个历史值的 θ 值,就能估计其均值。
  • 劣处:滑动平均不如将历史值全部保存下来计算均值准确,但后者占用更多的内存和计算成本更高。
  • 公式: m o m e n t u m ∈ [ 0 , 1 ) momentum \in[0,1) momentum[0,1)。当 m o m e n t u m = 0 momentum=0 momentum=0 相当于没有使用滑动平均 m o v i n g _ m e a n = m o m e n t u m ∗ m o v i n g _ m e a n + ( 1 − m o m e n t u m ) ∗ m e a n moving\_mean = momentum * moving\_mean + (1-momentum) * mean moving_mean=momentummoving_mean+(1momentum)mean
  • 例子:
    momentum = 0.9 ,mean 的 t 滑动平均值为 前10个时刻的mean的平均;
    momentum = 0.98 ,mean 的 t 滑动平均值为 前50个时刻的mean的平均;
    momentum = 0.99 ,mean 的 t 滑动平均值为 前100个时刻的mean的平均;

在神经网络bn中的 momentum 值的设置:

  • 对于滑动平均,我们需要深刻理解,它就是在估计 某一时间段的均值。那对于神经网络中的bn中的滑动均值和方差,我们取过去的时间段的均值,那么这个时间段应该取多长?个人经验:

    • 假设有10w数据量,batch为10,一轮的训练有1w次迭代,此时设置 momentum = 0.99,记录过去的100次迭代的平均值,这样是可以的;
    • 当batch=1000(不考虑显存的限制),一轮的训练有100次迭代,这时,我们依然记录过去的100此迭代的平均值,就显得不合理了。
      在前期和中期时,一轮次中,神经网络更新的梯度变化相对较大,batch=1000,将这变化划分了100等分,前第100次迭代 和 前第一次迭代的 变量变化已经相距较大,这样我们应该 缩短统计平均值的区间。此时选择 momentum = 0.95,记录过去的20次迭代的平均值,会更加合理些。

    所以 momentum的设置,需要结合 数据集的大小、batch的大小 进行设置。个人习惯设置的区间 [0.9,0.99],momentum 设置数值少量的偏差不会有决定性的影响,但不考虑应用情况,一律默认是完全不可取的。

    • 有人说了,以这个思路,反正训练到后期,网络变量的梯度变化已经不大,统计过去的100次迭代,都会得到较好的结果,所以不管什么情况都设置 momentum=0.99。但这样肯定是不可行的。我们要考虑到,如果为了得到bn的更好的滑动平均,而做更多的迭代,网络可训练的变量就会发生过拟合,从而导致网络性能变得更差

个人实际应用:

  • 项目中有数据1600个,
    当momentum=0.99时,验证集运行时设置 Is_training=False,在前10轮的验证loss以及accuracy非常差;当momentum=0.9 时,验证集运行时设置 Is_training=False,在训练开始时,验证集的loss和accuracy就会有较好的收敛。在训练15轮左右网络已经可以得到相对较好的结果。

1.2 Is_training 的设置

当 api 中的参数 training = True 时,表示bn是处于训练阶段,均值方差的是 batch 内计算而来
当 api 中的参数 training = False 时,表示 bn 是处于测试阶段,均值和方法 使用的是训练阶段统计的滑动均值和滑动方差。 所以一定要保证训练阶段的滑动均值方差,正确的得到更新和保存。

1.3 bn 的滑动mean、滑动var的更新

bn的前向传播公式如下图,其中
mean、var 不是训练参数。训练时是batch内计算而来;测试时是统计而来的(计算滑动平均)
scale、shift 是可训练参数。在神经网络训练时训练而来。
在这里插入图片描述


x_norm = tf.layers.batch_normalization(x, training=Is_training)
# ...
train_op = optimizer.minimize(loss)

上面的例子,我们使用sess.run(train_op)时,网络会自动更新所有可训练的参数,但是滑动平均这种统计的参数,是无法直接得到更新的。所以需要额外添加限制,使得网络在反向传播之前,先进行bn中统计参数的更新。如下:

x_norm = tf.layers.batch_normalization(x, training=Is_training)
# ...

update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
  train_op = optimizer.minimize(loss)

batch_normalization中更新mean和variance的操作,在tensorflow的内部实现中自动被加入tf.GraphKeys.UPDATE_OPS这个集合的,with tf.control_dependencies(update_ops) 表示我们执行 train_op时候,代码会自动先执行 update_ops,然后再执行train_op。

1.4 bn 的滑动mean、滑动var的保存

直接说最方便也是最建议的方式,将所有的参数都进行保存,包括可训练、不可训练的参数。这样也方便神经网络被中断后的继续训练。


var_list = tf.global_variables()
saver = tf.train.Saver(var_list=var_list, max_to_keep=5)


注意:千万不能设置成 var_list = tf.trainable_variables()

  • 9
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值