训练神经网络是一个很复杂的过程,在前面提到了深度学习中经常使用的激活函数,例如ELU或者Relu的变体可以在开始训练的时候很大程度上减小梯度消失或者爆炸问题,可是却不能保证在训练过程当中不出现该问题,例如在训练过程当中每一层输入数据分布
发生了改变了,那么咱们就须要使用更小的learning rate去训练,这一现象被称为internal covariate shift
,Batch Normalization可以很好的解决这一问题。目前该算法已经被普遍应用在深度学习模型中,该算法的强大至于在于:html
咱们在将数据输入到神经网络中每每须要对数据进行归一化,缘由在于模型的目的就是为了学习模型的数据的分布,若是训练集的数据分布和测试集的不同那么模型的泛化能力就会不好,另外一方面若是模型的每一 batch的数据分布都不同,那么模型就须要去学习不一样的分布,这样模型的训练速度会大大下降。
BN是一个独立的步骤,被应用在激活函数以前,它简单地对输入进行零中心(zero-center)和归一化(normalize),而后使用两个新参数来缩放和移动结果(一个用于缩放,另外一个用于缩放转移)。 换句话说,BN让模型学习最佳的尺度和 每层的输入的平均值。
为了零中心和归一化数据的分布,BN须要去估算输入的mean和standard deviation,算法的计算过程以下:
其中:python
scale
和shift
,是咱们须要学习的参数接下来咱们就使用TensorFlow来实现带有BN的神经网络,步骤和前面讲到的不少同样,只是在输入激活函数以前多处理了一部而已,在TF中咱们使用的实现是tf.layers.batch_normalization
。git
import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets("./") #自动下载数据到这个目录 tf.reset_default_graph() n_inputs = 28 * 28 n_hidden1 = 300 n_hidden2 = 100 n_outputs = 10 X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X") y = tf.placeholder(tf.int64, shape=(None), name="y") 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) with tf.name_scope("loss"): xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y,logits=logits)#labels容许的数据类型有int32, int64 loss = tf.reduce_mean(xentropy,name="loss") learning_rate = 0.01 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)) #结果boolean转为0,1 init = tf.global_variables_initializer() saver = tf.train.Saver() extra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) n_epochs = 20 batch_size = 200 with tf.Session() as sess: init.run() for epoch in range(n_epochs): for iteration in range(mnist.train.num_examples // batch_size): X_batch, y_batch = mnist.train.next_batch(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: mnist.test.images, y: mnist.test.labels}) print(epoch, "Test accuracy:", accuracy_val)
在上面代码中有一句须要解释一下算法
extra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
这是由于在计算BN中须要计算moving_mean
和moving_variance
而且更新,因此在执行run
的时候须要将其添加到执行列表中。咱们还能够这样写网络
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)
在训练的时候就只须要更新一个参数模块化
sess.run(training_op, feed_dict={training: True, X: X_batch, y: y_batch})
此外,咱们会发如今编写神经网络代码中,不少代码都是重复的能够将其模块化,例如将构建每一层神经网络的代码封装成一个function,不过这都是后话,看我的喜爱吧。函数