Tensorflow中BatchNormalization用法詳解

因爲用了ipynb格式,所以代碼暫時不挪過來了。

手動、自動、保存、恢復的代碼實現,相關參數的變化:

https://github.com/huqinwei/tensorflow_demo/batch_normalization_use_TF.ipynb

 

概要:

矛盾體:既要歸一化又要偏離中心,trade-off。

有兩三種用法。

同時包含可訓練參數和不可訓練參數。

因爲BN是帶變量的,要注意變量的保存。

注意階段的切換。

 

TF提供了一些自動化操作和防呆設計,如果簡單跑例子或者抄別人代碼的套路,可能遇不到問題,但是最好理解機制並明白TF爲你省去了什麼操作。

 

下面是筆記內容,部分整理:

文化課:

爲什麼引入BN


機器學習就是基於IID的,你現在SGD的分佈亂變,不符合這種假設,網絡模型學不到規律。BN去除了這個煩惱,全是同分布(雖然他也用gamma和beta搞偏移了。但是這組變量是共用的,所以分佈應該還是相同的)
總之,把激活的輸入分佈固定住,避免Internal  Covariate Shift。

另外一方面就是導致梯度消失和收斂速度之類的,就不贅述了。

 

爲什麼如果gamma和beta默認1和0,最終輸出等於原樣不變?


“這裏t層某個神經元的x(k)不是指原始輸入,就是說不是t-1層每個神經元的輸出,而是t層這個神經元的線性激活x=WU+B,這裏的U纔是t-1層神經元的輸出。”

這句話,當前層t的“特徵”x(k),不是前一層t-1的神經元的輸出
疑問:x=WU+B能叫線性激活嗎?不是線性變換,還沒激活嗎?
總之,BN要變的東西,是當前這一層乘以W並且加B以後,激活之前的數。所以更沒侷限在0附近了,測試數據不超綱,爲什麼得不到想要的結果?

 

關於爲什麼直接用mean和var做normalize就解決分佈問題,還要經過scale和shift進行轉換的問題


normalize變換之後,形成均值0,方差1的正態分佈。(雖然目前在TF實現上還有點疑問)

先不說TF得不到想要結果,假設能得到,變換後,某個神經元的激活x(對應前邊那句話的x)是均值0,方差1的正態分佈。從訓練和收斂的角度,到這就夠了。

但是會導致網絡表達能力下降,所以加了scale和shift,這兩個參數也是通過訓練學到的!但是這具體是怎麼學的?怎麼促進參數往這個方向上靠?因爲normalize之後表達能力差,所以預測表現差,所以爲了讓預測表現更好,就自然縮放he訓練和偏移了,就訓練了scale和shift參數!


關於表達能力下降的解釋:
BN會讓激活值落在非線性函數的線性區內,具體的說,sigmoid的斜率爲1的那個位置,非線性。
深度網絡的本質是非線性變換和擬合複雜曲線,BN相當於遏止了向這一目的發展。
所以才引入了scale和shift,也相信這個對應的gamma和beta不會太大,相對於原本偏的離譜的分佈,最後應該整體還是比較偏向於中心的分佈,但是又不極端靠近中心——也就是sigmoid斜率爲1的0點。
先normalize,又scale和shift,好像確實這一矛盾操作也有些爭議。但是完全抵消也是一個理論上的狀態,實際還是有些效果的,姑且算是一個tradeoff吧。實際效果好,並且有很多其他優點,這是一個實踐先於理論的行業。。。。
 

關於這個BN如何被訓練到

四組參數,optimizer會自然訓練到其中兩組,另外兩組是滑動平均的。

更多細節在筆記中。

 

 

重中之重:無論接口白盒黑盒,BN操作需要使用依賴關係來完成滑動平均的更新

            self.mean, self.variance = tf.nn.moments(x, axes=reduce_dims)#默認tf.nn.moments=False
            update_move_mean = moving_averages.assign_moving_average(self.moving_mean,
                                                    self.mean, decay=decay)
            update_move_variance = moving_averages.assign_moving_average(self.moving_variance,
                                                    self.variance, decay=decay)
            tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, update_move_mean)
            tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, update_move_variance)
update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
    train_op = optimizer.minimize(loss)

What's the differences between
tf.GraphKeys.TRAINABLE_VARIABLES and 
tf.GraphKeys.UPDATE_OPS in tensorflow?

 

tf.GraphKeys中,TRAINABLE_VARIABLES就是被optimizer訓練的variable子集。
黑盒的BN層,參數就被自動加到了GraphKeys.UPDATE_OPS
用tf.get_collection()能找到想要的tensor
When use tensorflow.contrib.layers.batch_norm(), the parameter updates_collections default value is GraphKeys.UPDATE_OPS.
How can we understand those collections, and difference in them.
Besides, we can find more in ops.py.


TRAINABLE_VARIABLES是variables的集合!
是minimizing loss時候訓練的,如果不指定trainable=False,一般variable都被自動加進去了。
不可訓練的使用場景兩步訓練,fine-tune

UPDATE_OPS是ops的集合!不是variables
維護了每個訓練步驟之前的操作列表
怎麼加進來的?
根據定義,更新操作發生在損失最小化的常規培訓流之外,因此通常只有在特殊情況下才會將操作添加到此集合中。例如,在執行批處理規範化時,您希望在每個培訓步驟之前重新計算批平均值和差異,這就是它的實現方式。本文更詳細地描述了使用tf.contrib.layers.batch_norm的批處理規範化機制。
http://ruishu.io/2016/12/27/batchnorm/

 

例外:EMA

同樣接口tf.layers.batch_normalization,兩個工程,一個簡單工程,update_ops有賦值操作;一個完整模型,更新操作歸到ema了,哪怕不聲明ema操作,update_ops也是空的,有點蒙得原地轉圈!!排除了globa_step等!

這樣的話,ema成了必選操作,因爲update_ops不帶滑動平均更新操作。

所以,無論是哪種情況,最好自己提前打印調試好,以免遺漏!

 

 

    ema = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step,name='ema')
    print('ema:',ema)
    ema_op = ema.apply(tf.trainable_variables())
    print('ema_op:',ema_op)
    
    update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)#BN需要依賴操作
    print(tf.global_variables())
    print('update_ops:',update_ops)
    with tf.control_dependencies([train_step, ema_op]):
        train_op = tf.no_op(name='train')

 

 

 

無論哪個接口,training階段的選擇都要有:

訓練階段和測試階段不同,一定要設定

一個是當前batch的mean、variance,一個是滑動平均的mean、variance。

如果不切換階段,等於什麼都沒做,還是拿當前batch自己做平均。

 

單獨看BN接口,如果選了training=False,可能輸出卻“很不如意”,因爲如果用batch自己的mean和variance,輸出總是規範的在一個區間,他總是將自身先縮放到標準範圍,然後縮放偏移,但是BN的目的就是消除batch自己的偏差,使用統一的mean和variance!

 

但是實際的工程https://blog.csdn.net/huqinweI987/article/details/87884341,如果training=False,準確率也是會大大下降,所以需要調試找下原因,可能是訓練不充分造成,也可能是ema的替代式更新改衝突了?或者batch-size還是太小?也或者是ema的更新沒起到作用?目前沒明白怎麼拆開bn的更新操作和ema的更新操作?adam optimizer?

是因爲Optimizer,沒有Optimizer時需要手動關聯

 

沒定義Optimizer的minimize也沒定義依賴操作時&定義了minimize操作,但是沒依賴時,update_ops包括:

 

實際訓練和測試中,使用training=False之後,數據被轉到比較偏的位置,預測效果反倒不好?

如果是training=True,數據根據自己的mean和variance得到一個結果,位置相對更“正”,應該和結果比較接近。

現在效果不好,按理說不是BN自身的問題吧。BN本來就是要這個效果的!

 

換個角度想,訓練的時候,利用自身mean和variance處理之後都差不多,縮放和平移也就差不多。所以gamma和beta也算是針對比較常規分佈的數據。現在用了不一樣的mean和variance,可能有偏差,所以處理不好了?從宏觀角度,應該沒問題的。

會不會EMA慣性太大了?工程中EMA是0.99,tf接口給的默認也是0.99,所以,很可能是,訓練還不夠多吧?0.99屬於一個很難改變的慣性,實測,更低的慣性可能好一些(至少是前期,實在跑不了那麼多的訓練,暫時不測)。

還可以嘗試:只對BN做ema,其他的都不做。

dropout和BN有干擾嗎?宏觀看應該沒問題,實測不是主要因素。

 

 

 

代碼

 

手動、自動、保存、恢復的代碼實現,相關參數的變化:

https://github.com/huqinwei/tensorflow_demo/batch_normalization_use_TF.ipynb

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章