訓練神經網絡是一個很複雜的過程,在前面提到了深度學習中經常使用的激活函數,例如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,不過這都是後話,看我的喜愛吧。函數