1. 程式人生 > >[tensorflow 應用之路]Batch Normalization 原理詳解及應用方法

[tensorflow 應用之路]Batch Normalization 原理詳解及應用方法

批正則化(Batch Normalize,BN)是2015年由Sergey Ioffe提出的方法,用於消除神經網路上一層不同分佈的輸入導致本層引數更新困難。由於各個層的卷積核引數不同,根據反向傳播法則我們知道,W||W||及結果h||h||越大對梯度的影響也就越大,而同一學習率在不同層造成的影響不一樣。這時,就需要BN層來消除這種差異。

原理簡析

原文 BN的實現其實非常簡單,主要通對過特徵scale和shift來消除特徵間的差異帶來的引數更新的影響。具體的介紹可以參照這個視訊(youtube),裡面有非常詳細的介紹。以下是原論文中的BN虛擬碼: 訓練 在訓練中使用移動平均(moving average)法更新均值和方差,在預測中,使用訓練的均值E

[x]E[x]和方差Var[x]Var[x]作為引數,配合γ\gammaβ\beta進行正則化。 這篇文章用各個實驗對比了BN層的在不同網路中的作用差異,有興趣的同學可以參考一下。

已知問題

1.如果使用tensorflow,不要忘記在optimizer前增加BN引數的更新操作。 2.不要將BN用在少量資料中。一旦資料總量變小,那麼整體均值標準差與單個數據的均值和方法偏差過大(下面會舉例子說明),導致預測和訓練資料相差過大。

Tensorflow中的BN層

我們首先分析tf.layers.batch_normalization中重要的引數。

  • axis:在此維度上做歸一化(Normalize)。一般選擇channel維度,NHWC時為-1,NCHW時為1。
  • momentum:滑動平均時的動量。假設平均值為vv,當前值為aa,公式如下:原始碼參見此處 v=v(1momentum)(vu)v=v-(1-momentum) * (v - u)
  • epsilon: 防止分母為0的數
  • center/scale: 當為True時,β\beta$\gamma$會生效(詳見上圖程式碼第11行)
  • training: 一定要在訓練時設定為True,在預測時設定為False。這個引數決定了bn層的歸一是使用一個batch內的均值和標準差,還是使用訓練的均值和標準差(下面有詳解)
  • fused: 能加速bn過程的引數,建議設定為True

BN在訓練時

訓練時的BN層會直接使用batch內的mean和var進行歸一化,公式如下: y=xmean(x))var(x) y=\frac{x-mean(x))}{var(x)} 我們用以下程式碼段驗證一下:

a = tf.Variable(tf.random_uniform((3, 4),seed=1))
bn = tf.layers.batch_normalization(a, -1, 1, 1e-6, False, False,training=True)
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    while True:
        temp1, temp2 = sess.run([a, bn])

在BN之前,原始資料如下: 在這裡插入圖片描述 在0維度下 均值為

[0.6144663 , 0.5615021 , 0.30789164, 0.42147708]

方差為

[0.07121453, 0.14869653, 0.11407541, 0.00624432]

我們用公式算一下,BN後的值應該是:

[[-1.4068358 , 0.93072194, -0.76203936, 0.9398434 ], [ 0.82835776, -1.3874875 , -0.6507127 , 0.44523987], [ 0.578478 , 0.45676556, 1.4127523 , -1.3850833 ]]

而在使用tensorflow的批正則後,數值如下: 在這裡插入圖片描述 和我們之前推算的差不多。這說明在訓練時,BN層會直接使用輸入資料的均值與標準差,來作正則化處理。

訓練時更新引數

在預測時,BN層需要直接用訓練時統計的均值與標準差,所以在訓練時需要更新moving_average和moving_variance(兩者在使用BN後就會自動建立)。 具體方法如下: 1.使用tf.control_dependencies:

...
 optimizer = tf.train.AdamOptimizer(learning_rate)       #可以是任意的optimizer
 update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
 with tf.control_dependencies(update_ops):
 		train_op = optimizer.minimize(loss)
...

這裡tf.control_dependencies是表示在做操作前(這裡是反向傳播),先進行括號裡的操作(這裡是更新moving_average和moving_variance)。如果直接使用optimizer的話,均值和標準差不更新,預測結果會全錯

2.使用slim的方式:

optimizer = tf.train.AdamOptimizer(learning_rate)
train_op = slim.learning.create_train_op(total_loss, optimizer)

在create_train_op時,會自動呼叫上面的tf.control_dependencies,原始碼位置見此處

BN在預測時

在預測時,BN層中會使用訓練時無偏統計的均值和標準差(注意,不使用輸入資料的均值和標準差),這會導致訓練時和預測時的結果有"偏差",資料量越大,這種“偏差”越不明顯。所以我們說,在資料量小的時候,不要使用batch normalize。 更新均值和標準差的方式在上文提過(momentum引數),我們看一下之前的例子。 更新前的均值和標準差: 在這裡插入圖片描述 在這裡插入圖片描述

更新後的均值和標準差(使用momentum=0): 在這裡插入圖片描述 在這裡插入圖片描述 和之前我們計算的結果一致,說明mean和variance就是用每個mini-batch的資料的均值和方差進行更新的。

總結

使用BN層可以歸一化層的輸入和輸出,使不同分佈的輸入差異的影響最小,讓學習率調整得更加便捷,減少過擬合風險,加快訓練速度。使用BN後,會造成訓練和預測的輸出差異,這種差異在小資料量時尤為明顯。

最後,祝您身體健康再見!