使用TensorFlow中的Batch Normalization

問題

訓練神經網絡是一個很複雜的過程,在前面提到了深度學習中經常使用的激活函數,例如ELU或者Relu的變體可以在開始訓練的時候很大程度上減小梯度消失或者爆炸問題,可是卻不能保證在訓練過程當中不出現該問題,例如在訓練過程當中每一層輸入數據分佈發生了改變了,那麼咱們就須要使用更小的learning rate去訓練,這一現象被稱爲internal covariate shiftBatch Normalization可以很好的解決這一問題。目前該算法已經被普遍應用在深度學習模型中,該算法的強大至於在於:html

  • 能夠選擇一個較大的學習率,可以達到快速收斂的效果。
  • 可以起到Regularizer的效果,在一些狀況下能夠不使用Dropout,由於BN提升了模型的泛化能力

介紹

咱們在將數據輸入到神經網絡中每每須要對數據進行歸一化,緣由在於模型的目的就是爲了學習模型的數據的分佈,若是訓練集的數據分佈和測試集的不同那麼模型的泛化能力就會不好,另外一方面若是模型的每一 batch的數據分佈都不同,那麼模型就須要去學習不一樣的分佈,這樣模型的訓練速度會大大下降。
BN是一個獨立的步驟,被應用在激活函數以前,它簡單地對輸入進行零中心(zero-center)和歸一化(normalize),而後使用兩個新參數來縮放和移動結果(一個用於縮放,另外一個用於縮放轉移)。 換句話說,BN讓模型學習最佳的尺度和 每層的輸入的平均值。
爲了零中心和歸一化數據的分佈,BN須要去估算輸入的mean和standard deviation,算法的計算過程以下:

其中:python

  • \(u_B\)是mini-btach \(B\)的均值,\(\sigma\)是mini-btach的標準差
  • \(m_B\)是mini-batch中的樣本
  • \(\hat{x}^{(i)}\) 是zero-center和normalize後的輸入
  • 公式4是一個線性變換,是對數據分佈的重構,\(z^{(i)}\)是算法對數據重構的output,\(\gamma\)\(\beta\)分別表明的是對數據的scaleshift,是咱們須要學習的參數

應用

接下來咱們就使用TensorFlow來實現帶有BN的神經網絡,步驟和前面講到的不少同樣,只是在輸入激活函數以前多處理了一部而已,在TF中咱們使用的實現是tf.layers.batch_normalizationgit

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_meanmoving_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,不過這都是後話,看我的喜愛吧。函數

相關文章
相關標籤/搜索