TensorFlow 中的正则化(Batch Normalization)详解和实现代码

        虽然在训练初期使用 He 初始化方法初始ELU(或者其他派生的ReLU)能够有效的防止梯度弥散、爆炸问题。但是这种方式无法保证梯度问题不会在训练过程中产生。

        2015年的一篇paper( “Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift,” S. Ioffe and C. Szegedy (2015).)中, Sergey Ioffe and Christian Szegedy提出了一个Batch Normaalization(BN)技术去处理梯度弥散、爆炸问题。一般来说,在训练过程中随着前几层参数的变化,每一层输入的分布都会发生改变(这杯称作Internal Covariate Shift 问题)。

        该技术在模型每一层的激活函数之前添加一个操作,以0为中心对输入作正则化,在每一层使用两个参数分别缩放与转化输出结果。换句话说,该操作在每一层上让模型自动学习最优的尺度与均值。

        为了零中心化与正则化输入,该算法需要估计输入的均值与标准差。它通过计算当前输入最小批次的均值与标准差来实现(所以称之为Batch Normalization).整个操作的描述见下列等式。

        在测试的时候,没有mini-batch去计算经验均值(empirical mean)和标准差,所以应该使用整个训练集的均值和标准差。典型有效地在训练时使用移动平均计算得到。因此,总体而言,每一个batch normalized 层都能学到四个参数:

作者证明了该技术明显的提高了所有的神经网络的性能。能有效地控制梯度消失的问题,因此可以使用饱和的激活函数比如tanh , 甚至是逻辑斯第激活函数。同时,网络对权值的初始化也不敏感。也可以使用比较大的学习率,极大地提高学习速度。此外,他们指出:“应用到公认较好的图像分类模型中,batch mormal 达到了同样的正确率,但只用了14此训练步数,明显打败了原始模型。使用集成的batch-normal 网络,我们达到了最好的公共成绩在ImageNet classification 上:4.9%的验证误差,(4.8%的测试误差)超过人类分类的正确率。”最终, 就像一个不断给与的礼物,batch normalization也像一个正则化,可以减少对其他正则化技术的需要(比如 dropout)

        然而,batch normalization 的确增加了模型的复杂程度(即使使用它可以免去对输入数据的规则化,因为第一层隐藏层会对数据提供batch normal处理).此外,会有一个运行时的罚项:由于在每一层需要额外的计算,会让神经网络在预测的时候变慢。因此,如果你需要预测变得轻快。你可能需要检查一下普通的ELU+He initialization在使用batch normalization之前的表现如何。

  • 你可能会发现,刚开始的时候训练是相当缓慢的, Gradient Descent正在为每个变量寻找最优的比例和偏移量在每一层中。但一旦找到了合理的合适的值,它就会加速运行。

使用TensorFlow 实现Batch Normalization

        Tensorflow 提供了 batch_normalization()方法方便地使输入数据中心化与正则化,但是需要你亲自计算均值和标准差(该值得计算基于训练时的mini-batch 或测测试时的全量数据,)作为该函数的参数。这样做是可行的,但不是最方便的方法,作为替代,你应该使用 batch_norm( )方法。这个函数为解决了这些计算。你可以直接或高数fully_connected()函数使用它,如下代码所示:

import tensorflow as tf

n_inputs = 28 * 28
n_hidden1 = 300
n_hidden2 = 100
n_outputs = 10

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")

training = tf.placeholder_with_default(False, shape=(), name='training')

hidden1 = tf.layers.dense(X, n_hidden1, name="hidden1")
bn1 = tf.layers.batch_normalization(hidden1, training=training, momentum=0.9)
bn1_act = tf.nn.elu(bn1)

hidden2 = tf.layers.dense(bn1_act, n_hidden2, name="hidden2")
bn2 = tf.layers.batch_normalization(hidden2, training=training, momentum=0.9)
bn2_act = tf.nn.elu(bn2)

logits_before_bn = tf.layers.dense(bn2_act, n_outputs, name="outputs")
logits = tf.layers.batch_normalization(logits_before_bn, training=training,
momentum=0.9)

为了避免一遍又一遍得重复写参数,我们可以使用python的 ‘partial()’函数:

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
training = tf.placeholder_with_default(False, shape=(), name='training')

from functools import partial

my_batch_norm_layer = partial(tf.layers.batch_normalization,
training=training, momentum=0.9)

hidden1 = tf.layers.dense(X, n_hidden1, name="hidden1")
bn1 = my_batch_norm_layer(hidden1)
bn1_act = tf.nn.elu(bn1)
hidden2 = tf.layers.dense(bn1_act, n_hidden2, name="hidden2")
bn2 = my_batch_norm_layer(hidden2)
bn2_act = tf.nn.elu(bn2)
logits_before_bn = tf.layers.dense(bn2_act, n_outputs, name="outputs")
logits = my_batch_norm_layer(logits_before_bn)

 

下面,我们使用神经网络识别MNIST 的例子,其中,激活函数使用ELU,并对每一层使用Batch Normalization:

batch_norm_momentum = 0.9

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int32, shape=(None), name="y")
training = tf.placeholder_with_default(False, shape=(), name='training')

with tf.name_scope("dnn"):
he_init = tf.variance_scaling_initializer()

my_batch_norm_layer = partial(
tf.layers.batch_normalization,
training=training,
momentum=batch_norm_momentum)

my_dense_layer = partial(
tf.layers.dense,
kernel_initializer=he_init)

hidden1 = my_dense_layer(X, n_hidden1, name="hidden1")
bn1 = tf.nn.elu(my_batch_norm_layer(hidden1))
hidden2 = my_dense_layer(bn1, n_hidden2, name="hidden2")
bn2 = tf.nn.elu(my_batch_norm_layer(hidden2))
logits_before_bn = my_dense_layer(bn2, n_outputs, name="outputs")
logits = my_batch_norm_layer(logits_before_bn)

with tf.name_scope("loss"):
xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
loss = tf.reduce_mean(xentropy, name="loss")

with tf.name_scope("train"):
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
training_op = optimizer.minimize(loss)

with tf.name_scope("eval"):
correct = tf.nn.in_top_k(logits, y, 1)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))
init = tf.global_variables_initializer()
saver = tf.train.Saver()
n_epochs = 20
batch_size = 20
extra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)

with tf.Session() as sess:
       init.run()
       for epoch in range(n_epochs):
            for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):
                  sess.run([training_op, extra_update_ops],
                        feed_dict={training: True, X: X_batch, y: y_batch})
            accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})
            print(epoch, "Validation accuracy:", accuracy_val)

       save_path = saver.save(sess, "./my_model_final.ckpt")

 

注意,你也能够让训练操作依赖于更新操作,如下所示:

with tf.name_scope("train"):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    extra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
    with tf.control_dependencies(extra_update_ops):
        training_op = optimizer.minimize(loss)

 

这样的话,你就只需要在训练的时候运行 ‘training_op’ Tensorflow 将会自动执行更新操作:

sess.run(training_op, feed_dict={training: True, X: X_batch, y: y_batch})

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值