Batch Normalization在TensorFlow中有三個接口調用 (不包括slim、Keras模塊中的),分別是:
通過觀察這三個接口的參數列表可以得到一個初步的結論,tf.layers.batch_normalization
和tf.contrib.layers.batch_norm
可以用來構建待訓練的神經網絡模型,而tf.nn.batch_normalization
一般只用來構建推理模型。簡潔起見,本文把神經網絡模型分為訓練模式和推理模式(包括推理、測試和評估等)。由於tf.contrib
包的不穩定性,本文將主要介紹如何使用tf.layers.batch_normalization
這個方法在模型中添加BN layer
。
tf.layers.batch_normalization()方法
方法接口如下:
tf.layers.batch_normalization( inputs, axis=-1, momentum=0.99, epsilon=0.001, center=True, scale=True, beta_initializer=tf.zeros_initializer(), gamma_initializer=tf.ones_initializer(), moving_mean_initializer=tf.zeros_initializer(), moving_variance_initializer=tf.ones_initializer(), beta_regularizer=None, gamma_regularizer=None, beta_constraint=None, gamma_constraint=None, training=False, trainable=True, name=None, reuse=None, renorm=False, renorm_clipping=None, renorm_momentum=0.99, fused=None, virtual_batch_size=None, adjustment=None )
這里有幾個重要參數需要注意:
axis
的值取決於按照input
的哪一個維度進行BN,例如輸入為channel_last
format,即[batch_size, height, width, channel]
,則axis
應該設定為4,如果為channel_first
format,則axis
應該設定為1.momentum
的值用在訓練時,滑動平均的方式計算滑動平均值moving_mean
和滑動方差moving_variance
。 后面做更詳細的說明。center
為True
時,添加位移因子beta
到該BN層,否則不添加。添加beta
是對BN層的變換加入位移操作。注意,beta
一般設定為可訓練參數,即trainable=True
。scale
為True
是,添加縮放因子gamma
到該BN層,否則不添加。添加gamma
是對BN層的變化加入縮放操作。注意,gamma
一般設定為可訓練參數,即trainable=True
。training
表示模型當前的模式,如果為True
,則模型在訓練模式,否則為推理模式。要非常注意這個模式的設定,這個參數默認值為False
。如果在訓練時采用了默認值False
,則滑動均值moving_mean
和滑動方差moving_variance
都不會根據當前batch的數據更新,這就意味着在推理模式下,均值和方差都是其初始值,因為這兩個值並沒有在訓練迭代過程中滑動更新。
TensorFlow的api文檔中對該方法標星出了Note警示如下:

train_op
並沒有依賴滑動均值
moving_mean
和滑動方差
moving_variance
,則
moving_mean
和
moving_variance
不會自動更新,所以必須加入負責更新這些參數的
update_ops
到依賴中,且應該在執行前向計算結束后、后向計算開始前執行
update_ops
,所以添加依賴的位置不能出錯。實際中,
只需要在構建模型代碼中,添加完所有BN層之后獲取update_ops
就不會出錯,切記!切記!這是TensorFlow的圖計算模式造成的編程影響,在其他深度學習框架中可能會有差別。
Batch Normalization的作用
- 效果
該方法出自Sergey Ioffe和Christian Szegedy的《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》一文,BN方法到底能否減少Internal Covariate Shift可能還無法下最終結論 (參見《How Does Batch Normalization Help Optimization?》),但的確可以加速訓練平穩收斂!所以,BatchNorm成為了居家旅行、訓練模型之必備layer。 - 計算
BatchNorm的具體計算/變換如圖 (見論文):Batch Normalizing Transform, applied to activation x over a mini-batch.mean
,計算出當前batch的每個channel的方差variance
,令輸入減去均值再除以標准差delta
,得到normalized輸出x-hat
,最后乘以scale參數gamma
,加上shift參數beta
,得到最終變換后的輸出y
。 - BN層在train與inference時的差別
在訓練時,我們可以計算出batch的均值和方差,迭代訓練過程中,均值和方差一直在發生變化。但是在推理時,均值和方差是固定的,它們在訓練過程中就被確定下來。《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》中給出的確定方式和TensorFlow中存在不同,這里我們介紹TensorFlow中的方式,即采用滑動平均MovingAverage的方法,公式為:moving_average_value * momentum + value * (1 - momentum)
,其中value
為當前batch的平均值或方差,moving_average_value
為滑動均值或滑動方差。
最終,模型訓練完畢后,在推理時使用滑動平均值moving_mean
和滑動方差moving_variance
對feature maps進行變換。 - 在模型中的哪些位置插入BN層
推薦在Conv層或FC層之后,非線性變換激活層之前插入BN層。原因見論文:where to insert BN layers
TensorFlow中的BatchNorm實現鏈
tf.layers.batch_normalization -->
tf.layers.BatchNormalization -->
tf.python.keras.layers.BatchNormalization -->
keras.backend.normalize_batch_in_training -->
keras.backend._fused_normalize_batch_in_training -->
tf.python.ops.nn.fused_batch_norm
tf.python.keras.layers.BatchNormalization -->
keras.backend.moving_average_update -->
tf.python.training import moving_averages.moving_averages
前半部分主要是對batch_norm的變換,后半部分是對滑動均值的更新。
后記
寫這篇文章是為了介紹BatchNorm在TensorFlow中的正確使用方法。作者曾因為沒有正確傳遞training
參數給tf.layers.batch_normalization
,導致模型在訓練完成后所有moving_mean都是0,所有moving_variance都是1.0,在讀取並量化模型進行batch norm fold時,無法讀入這兩個參數,提示NoneType
錯誤。經過研究一番才發現是調用tf.layers.batch_normalization
方法是必須pass正確的模式。
鏈接:https://www.jianshu.com/p/437fb1a5823e