Tensorflow中BatchNormalization用法详解

目录

概要:

为什么引入BN

为什么如果gamma和beta默认1和0,最终输出等于原样不变?

关于为什么直接用mean和var做normalize就解决分布问题,还要经过scale和shift进行转换的问题

关于这个BN如何被训练到

根据说明文档可知,无论用哪种实现,training阶段的选择都要有:

重中之重:无论接口白盒黑盒,BN操作需要使用依赖关系来完成滑动平均的更新

BN更新和EMA的关系

工程实际效果对比:

歪用:

工程

测试代码:



概要:

矛盾体:既要归一化又要偏离中心,trade-off。

有两三种用法。

同时包含可训练参数和不可训练参数。

因为BN是带变量的,要注意变量的保存。

注意阶段的切换。

 

TF提供了一些自动化操作和防呆设计,如果简单跑例子或者抄别人代码的套路,可能遇不到问题,但是最好理解机制并明白TF为你省去了什么操作。

 

为什么引入BN


机器学习就是基于IID的,你现在SGD的分布乱变,不符合这种假设,网络模型学不到规律。BN去除了这个烦恼,全是同分布(虽然他也用gamma和beta搞偏移了。但是这组变量是共用的,所以分布应该还是相同的)
总之,把激活的输入分布固定住,避免Internal  Covariate Shift。

另外一方面就是导致梯度消失和收敛速度之类的,就不赘述了。

 

为什么如果gamma和beta默认1和0,最终输出等于原样不变?


“这里t层某个神经元的x(k)不是指原始输入,就是说不是t-1层每个神经元的输出,而是t层这个神经元的线性激活x=WU+B,这里的U才是t-1层神经元的输出。”

这句话,当前层t的“特征”x(k),不是前一层t-1的神经元的输出
疑问:x=WU+B能叫线性激活吗?不是线性变换,还没激活吗?
总之,BN要变的东西,是当前这一层乘以W并且加B以后,激活之前的数。所以更没局限在0附近了,测试数据不超纲,为什么得不到想要的结果?

 

关于为什么直接用mean和var做normalize就解决分布问题,还要经过scale和shift进行转换的问题


normalize变换之后,形成均值0,方差1的正态分布。(虽然目前在TF实现上还有点疑问)

先不说TF得不到想要结果,假设能得到,变换后,某个神经元的激活x(对应前边那句话的x)是均值0,方差1的正态分布。从训练和收敛的角度,到这就够了。

但是会导致网络表达能力下降,所以加了scale和shift,这两个参数也是通过训练学到的!但是这具体是怎么学的?怎么促进参数往这个方向上靠?因为normalize之后表达能力差,所以预测表现差,所以为了让预测表现更好,就自然缩放和偏移了,就训练了scale和shift参数!


关于表达能力下降的解释:
BN会让激活值落在非线性函数的线性區内,具体的说,sigmoid的斜率为1的那个位置,非线性。
深度网络的本质是非线性变换和拟合复杂曲线,BN相当于遏止了向这一目的发展。
所以才引入了scale和shift,也相信这个对应的gamma和beta不会太大,相对于原本偏的离谱的分布,最后应该整体还是比较偏向于中心的分布,但是又不极端靠近中心——也就是sigmoid斜率为1的0点。
先normalize,又scale和shift,好像确实这一矛盾操作也有些争议。但是完全抵消也是一个理论上的状态,实际还是有些效果的,姑且算是一个tradeoff吧。实际效果好,并且有很多其他优点,这是一个实践先于理论的行业。。。。
 

不同归一化方法优缺点:

BN的替代方案有GN和Weight Normalization。

BN额外产生噪声,对于噪声敏感的RL和GAN、VAE等不利。可以用Weight Normalization代替。

BN也不适合动态网络结构和RNN等。

GN和WN细节略

 

关于这个BN如何被训练到

四组参数,optimizer会自然训练到其中两组,另外两组是滑动平均的。

更多细节在笔记中。

 

根据说明文档可知,无论用哪种实现,training阶段的选择都要有:

无论手动初始化变量,还是用接口隐藏内部的变量,本质都一样:

Tensorflow中BatchNormalization用法详解_第1张图片

训练阶段和测试阶段不同,一定要设定

一个是当前batch的mean、variance,一个是滑动平均的mean、variance。

如果不切换阶段,等于什么都没做,还是拿当前batch做平均。

如果阶段选错:

如果测试时选了training=True,可能输出却“很不如意”,因为如果用batch自己的mean和variance,输出总是规范的在一个区间,他总是将自身先缩放到标准范围,然后缩放偏移,但是BN的目的就是消除batch自己的偏差,使用统一的滑动mean和variance!

 

 

重中之重:无论接口白盒黑盒,BN操作需要使用依赖关系来完成滑动平均的更新

            self.mean, self.variance = tf.nn.moments(x, axes=reduce_dims)#默认tf.nn.moments=False
            update_move_mean = moving_averages.assign_moving_average(self.moving_mean,
                                                    self.mean, decay=decay)
            update_move_variance = moving_averages.assign_moving_average(self.moving_variance,
                                                    self.variance, decay=decay)
            tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, update_move_mean)
            tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, update_move_variance)
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
    train_op = optimizer.minimize(loss)

What's the differences between
tf.GraphKeys.TRAINABLE_VARIABLES and 
tf.GraphKeys.UPDATE_OPS in tensorflow?

 

tf.GraphKeys中,TRAINABLE_VARIABLES就是被optimizer训练的variable子集。
黑盒的BN层,参数就被自动加到了GraphKeys.UPDATE_OPS
用tf.get_collection()能找到想要的tensor
When use tensorflow.contrib.layers.batch_norm(), the parameter updates_collections default value is GraphKeys.UPDATE_OPS.
How can we understand those collections, and difference in them.
Besides, we can find more in ops.py.


TRAINABLE_VARIABLES是variables的集合!
是minimizing loss时候训练的,如果不指定trainable=False,一般variable都被自动加进去了。
不可训练的使用场景两步训练,fine-tune

UPDATE_OPS是ops的集合!不是variables
维护了每个训练步骤之前的操作列表
怎么加进来的?
根据定义,更新操作发生在损失最小化的常规培训流之外,因此通常只有在特殊情况下才会将操作添加到此集合中。例如,在执行批处理规范化时,您希望在每个培训步骤之前重新计算批平均值和差异,这就是它的实现方式。本文更详细地描述了使用tf.contrib.layers.batch_norm的批处理规范化机制。
http://ruishu.io/2016/12/27/batchnorm/

 

BN更新和EMA的关系

Tensorflow中BatchNormalization用法详解_第2张图片

ema可以选tensor列表,比如所有trainable变量,这个是可以包括BN的变量的,不过都是和Adam相关的变量。

BN有独自维护的变量:moving_mean、moving_variance,这个是ema包括不到的,也不应该包括。

update_ops是必选操作,不选,moving_xx不会变。

ema是可选操作,取决于是否有变量decay的需求。

无论是哪种情况,最好自己提前打印调试好,以免遗漏!

 

 

    ema = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step,name='ema')
    print('ema:',ema)
    ema_op = ema.apply(tf.trainable_variables())
    print('ema_op:',ema_op)
    
    update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)#BN需要依赖操作
    print(tf.global_variables())
    print('update_ops:',update_ops)
    with tf.control_dependencies([train_step, ema_op]):
        train_op = tf.no_op(name='train')

 

Tensorflow中BatchNormalization用法详解_第3张图片

Tensorflow中BatchNormalization用法详解_第4张图片

 

 

EMA中包含BN的beta和gamma,根本的原因是beta和gamma本来就要被训练到,EMA要对他们进行操作,还有恢复模型时的替换工作。但是并没有包含moving_mean和moving_variance,其实EMA是不负责更新BN的滑动平均的,需要自己手动来。

如果实际训练和测试中,BN层设置training=False之后,预测效果反倒不好!这个就是没有使用update_ops的原因。通过tensorboard跟踪可知是否有更新滑动平均值。

如下,没使用update_ops时的滑动平均和使用update_ops时的滑动平均:

Tensorflow中BatchNormalization用法详解_第5张图片

https://blog.csdn.net/huqinweI987/article/details/87884341

工程实际效果对比:

实验对比(无LRN且无BN对比有BN):

使用BN前,可以看到三层卷积层的激活输出relu1、2、3,一个比一个萎缩,两层FC的输出也不太好。使用BN后(图二),conv层激活RELU3的输出分布明显好转,仔细看FC层y1、y2的数值分布,深色区域训练初期1.5+,训练后期1.5-,明显好于图一,而图一大多数深色区域小于1。

 

然后是变量的区别,使用BN前,训练过程中,变量weights范围变化大,biases有大范围的偏移,说明参数要迁就数据,使用BN后(图二),weights范围相对平稳了许多(biases为固定值0.1,因为在BN前取消掉了biases,闲置了,fc_b3对应最终分类输出,没有做BN)

测试BN的拟合效果(歪用):

如果是利用BN接口去强行拟合一个目标,比如让1,2,3,去拟合2,4,6,那么输入其他比如1,3,5,预测也会是2,4,6,因为BN就是干这个的,让输入经过BN层的转换,集中在某一个区间,而不是学习规律。

https://github.com/huqinwei/tensorflow_demo/batch_normalization_use_TF.ipynb

 

 

 

工程

https://blog.csdn.net/huqinweI987/article/details/87884341

 

测试代码:

手动、自动、保存、恢复的代码实现,相关参数的变化:

https://github.com/huqinwei/tensorflow_demo/batch_normalization_use_TF.ipynb

这里边有不同接口、不同暴露程度的BN层实现。

 

 

 

其实TensorFlow提供了两三种接口,透明程度和使用方法稍有不同,但是本质都一样,如果想学习,最好是先看纯python版本
https://blog.csdn.net/huqinweI987/article/details/103224054

 

 

你可能感兴趣的:(机器学习,tensorflow,python)