1. 程式人生 > >BN(Batch Normalization)

BN(Batch Normalization)

ssi statistic fse 改變 分享圖片 turn hsi value beta

Batch Nornalization

Question?

  1.是什麽?

  2.有什麽用?

  3.怎麽用?

paper:《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》

  先來思考一個問題:我們知道在神經網絡訓練開始前,都要對輸入數據做一個歸一化處理,那麽具體為什麽需要歸一化呢?歸一化後有什麽好處呢?原因在於神經網絡學習過程本質就是為了學習數據分布,一旦訓練數據與測試數據的分布不同,那麽網絡的泛化能力也大大降低;另外一方面,一旦每批訓練數據的分布各不相同(batch 梯度下降),那麽網絡就要在每次叠代都去學習適應不同的分布,這樣將會大大降低網絡的訓練速度,這也正是為什麽我們需要對數據都要做一個歸一化預處理的原因。

  對於深度網絡的訓練是一個復雜的過程,只要網絡的前面幾層發生微小的改變,那麽後面幾層就會被累積放大下去。一旦網絡某一層的輸入數據的分布發生改變,那麽這一層網絡就需要去適應學習這個新的數據分布,所以如果訓練過程中,訓練數據的分布一直在發生變化,那麽將會影響網絡的訓練速度。

  我們知道網絡一旦train起來,那麽參數就要發生更新,除了輸入層的數據外(因為輸入層數據,我們已經人為的為每個樣本歸一化),後面網絡每一層的輸入數據分布是一直在發生變化的,因為在訓練的時候,前面層訓練參數的更新將導致後面層輸入數據分布的變化。以網絡第二層為例:網絡的第二層輸入,是由第一層的參數和input計算得到的,而第一層的參數在整個訓練過程中一直在變化,因此必然會引起後面每一層輸入數據分布的改變。我們把網絡中間層在訓練過程中,數據分布的改變稱之為:“Internal Covariate Shift”。Paper所提出的算法,就是要解決在訓練過程中,中間層數據分布發生改變的情況,於是就有了Batch Normalization,這個牛逼算法的誕生。

1.1 BN是什麽?

  就像激活函數層、卷積層、全連接層、池化層一樣,BN(Batch Normalization)也屬於網絡的一層。在前面我們提到網絡除了輸出層外,其它層因為低層網絡在訓練的時候更新了參數,而引起後面層輸入數據分布的變化。這個時候我們可能就會想,如果在每一層輸入的時候,再加個預處理操作那該有多好啊,比如網絡第三層輸入數據X3(X3表示網絡第三層的輸入數據)把它歸一化至:均值0、方差為1,然後再輸入第三層計算,這樣我們就可以解決前面所提到的“Internal Covariate Shift”的問題了。

  而事實上,paper的算法本質原理就是這樣:在網絡的每一層輸入的時候,又插入了一個歸一化層,也就是先做一個歸一化處理,然後再進入網絡的下一層。不過文獻歸一化層,可不像我們想象的那麽簡單,它是一個可學習、有參數的網絡層。既然說到數據預處理,下面就先來復習一下最強的預處理方法:白化。

  說到神經網絡輸入數據預處理,最好的算法莫過於白化預處理。然而白化計算量太大了,很不劃算,還有就是白化不是處處可微的,所以在深度學習中,其實很少用到白化。經過白化預處理後,數據滿足條件:a、特征之間的相關性降低,這個就相當於pca;b、數據均值、標準差歸一化,也就是使得每一維特征均值為0,標準差為1。如果數據特征維數比較大,要進行PCA,也就是實現白化的第1個要求,是需要計算特征向量,計算量非常大,於是為了簡化計算,作者忽略了第1個要求,僅僅使用了下面的公式進行預處理,也就是近似白化預處理:

           技術分享圖片

  公式簡單粗糙,但是依舊很牛逼。因此後面我們也將用這個公式,對某一個層網絡的輸入數據做一個歸一化處理。需要註意的是,我們訓練過程中采用batch 隨機梯度下降,上面的E(xk)指的是每一批訓練數據神經元xk的平均值;然後分母就是每一批數據神經元xk激活度的一個標準差了。

1.2 BN算法實現

其實如果是僅僅使用上面的歸一化公式,對網絡某一層A的輸出數據做歸一化,然後送入網絡下一層B,這樣是會影響到本層網絡A所學習到的特征的。打個比方,比如我網絡中間某一層學習到特征數據本身就分布在S型激活函數的兩側,你強制把它給我歸一化處理、標準差也限制在了1,把數據變換成分布於s函數的中間部分,這樣就相當於我這一層網絡所學習到的特征分布被你搞壞了,這可怎麽辦?於是文獻使出了一招驚天地泣鬼神的招式:變換重構,引入了可學習參數γ、β,這就是算法關鍵之處:
               技術分享圖片
  每一個神經元xk都會有一對這樣的參數γ、β。這樣其實當:
                技術分享圖片

  是可以恢復出原始的某一層所學到的特征的。因此我們引入了這個可學習重構參數γ、β,讓我們的網絡可以學習恢復出原始網絡所要學習的特征分布。最後Batch Normalization網絡層的前向傳導過程公式就是:

    技術分享圖片

  上面的公式中m指的是mini-batch size。

源碼實現:

m = K.mean(X, axis=-1, keepdims=True)#計算均值  
std = K.std(X, axis=-1, keepdims=True)#計算標準差  
X_normed = (X - m) / (std + self.epsilon)#歸一化  
out = self.gamma * X_normed + self.beta#重構變換 

2.BN有什麽用?

  隨機梯度下架成了訓練深度網絡的主流方法。盡管隨機梯度下降法對於訓練深度網絡簡單高效,但是它有個毛病,就是需要我們人為的去選擇參數,比如學習率、參數初始化、權重衰減系數、Drop out比例等。這些參數的選擇對訓練結果至關重要,以至於我們很多時間都浪費在這些的調參上。那麽學完這篇文獻之後,你可以不需要那麽刻意的慢慢調整參數。BN算法(Batch Normalization)其強大之處如下:

  (1)你可以選擇比較大的初始學習率,讓你的訓練速度飆漲。以前還需要慢慢調整學習率,甚至在網絡訓練到一半的時候,還需要想著學習率進一步調小的比例選擇多少比較合適,現在我們可以采用初始很大的學習率,然後學習率的衰減速度也很大,因為這個算法收斂很快。當然這個算法即使你選擇了較小的學習率,也比以前的收斂速度快,因為它具有快速訓練收斂的特性;

  (2)你再也不用去理會過擬合中drop out、L2正則項參數的選擇問題,采用BN算法後,你可以移除這兩項了參數,或者可以選擇更小的L2正則約束參數了,因為BN具有提高網絡泛化能力的特性;

  (3)再也不需要使用使用局部響應歸一化層了(局部響應歸一化是Alexnet網絡用到的方法,搞視覺的估計比較熟悉),因為BN本身就是一個歸一化網絡層

  (4)可以把訓練數據徹底打亂(防止每批訓練的時候,某一個樣本都經常被挑選到)。

  • 加大探索步長,加快收斂速度。
  • 更容易跳出局部極小。
  • 破壞原來的數據分布,一定程度上防止過擬合。
  • 解決收斂速度慢和梯度爆炸。

3.在實際tensorflow框架中怎麽用?

  tensorflow 在實現Batch Normalization (各個網絡層輸出的結果歸一化,以防止過擬合)時,主要用到一下兩個API。分別是

 3.1:tf.nn.moments

tf.nn.moments(x, axes, name=None, keep_dims=False) ? mean, variance

其中計算的得到的為統計矩,mean 是一階矩,variance 是二階中心矩 各參數的另一為

  • x 可以理解為我們輸出的數據,形如 [batchsize, height, width, kernels]
  • axes 表示在哪個維度上求解,是個list,例如 [0, 1, 2]
  • name 就是個名字,
  • keep_dims 是否保持維度

Example:

IN:
img = tf.Variable(tf.random_normal([2, 3]))
axis = list(range(len(img.get_shape()) - 1))
mean, variance = tf.nn.moments(img, axis)
OUT:
img = [[ 0.69495416  2.08983064 -1.08764684]
         [ 0.31431156 -0.98923939 -0.34656194]]
mean =  [ 0.50463283  0.55029559 -0.71710438]
variance =  [ 0.0362222   2.37016821  0.13730171]

3.2 tf.nn.batch_normalization

tf.nn.batch_normalization(x, mean, variance, offset, scale, variance_epsilon, name=None)
tf.nn.batch_norm_with_global_normalization(t, m, v, beta, gamma, variance_epsilon, scale_after_normalization, name=None)

由函數接口可知,tf.nn.moments 計算返回的 mean 和 variance 作為 tf.nn.batch_normalization 參數進一步調用;

在這一堆參數裏面,其中x,mean和variance這三個,已經知道了,就是通過moments計算得到的,另外菱格參數,offset和scale一般需要訓練,其中offset一般初始化為0,scale初始化為1,另外這兩個參數的offset,scale的維度和mean相同。
def batch_norm(x, name_scope, training, epsilon=1e-3, decay=0.99):
    """ Assume 2d [batch, values] tensor"""
    with tf.variable_scope(name_scope):
        size = x.get_shape().as_list()[1]
        scale = tf.get_variable(scale, [size], initializer=tf.constant_initializer(0.1))
        offset = tf.get_variable(offset, [size])
 
        pop_mean = tf.get_variable(pop_mean, [size], initializer=tf.zeros_initializer(), trainable=False)
        pop_var = tf.get_variable(pop_var, [size], initializer=tf.ones_initializer(), trainable=False)
        batch_mean, batch_var = tf.nn.moments(x, [0])
        train_mean_op = tf.assign(pop_mean, pop_mean*decay+batch_mean*(1-decay))
        train_var_op = tf.assign(pop_var, pop_var*decay + batch_var*(1-decay))
 
        def batch_statistics():
            with tf.control_dependencies([train_mean_op, train_var_op]):
                return tf.nn.batch_normalization(x, batch_mean, batch_var, offset, scale, epsilon)
 
        def population_statistics():
            return tf.nn.batch_normalization(x, pop_mean, pop_var, offset, scale, epsilon)
 
        return tf.cond(training, batch_statistics, population_statistics)

BN(Batch Normalization)