tensorflow中關于BN(Batch Normalization)的函數主要有兩個,分別是:
- tf.nn.moments
- tf.nn.batch_normalization
關于這兩個函數,官方API中有詳細的說明,具體的細節(jié)可以點鏈接查看,關于BN的介紹可以參考這篇論文,我來說說自己的理解。
不得不吐槽一下,tensorflow的官方API很少給例子,太不人性化了,人家numpy做的就比tensorflow強。
對了,moments函數的計算結果一般作為batch_normalization的部分輸入!這就是兩個函數的關系,下面展開介紹!
一、tf.nn.moments函數
官方的輸入定義如下:
def moments(x, axes, name=None, keep_dims=False)
解釋如下:
- x 可以理解為我們輸出的數據,形如 [batchsize, height, width, kernels]
- axes 表示在哪個維度上求解,是個list,例如 [0, 1, 2]
- name 就是個名字,不多解釋
- keep_dims 是否保持維度,不多解釋
這個函數的輸出有兩個,用官方的話說就是:
Two
Tensor
objects:mean
andvariance
.
解釋如下:
- mean 就是均值啦
- variance 就是方差啦
關于這個函數的最基本的知識就介紹完了,但依然沒明白這函數到底是干啥的,下面通過幾個例子來說明:
- 1、計算2×3維向量的mean和variance,程序節(jié)選如下:
img = tf.Variable(tf.random_normal([2, 3]))
axis = list(range(len(img.get_shape()) - 1))
mean, variance = tf.nn.moments(img, axis)
輸出的結果如下:
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]
有了例子和結果,就很好理解了,moments函數就是在 [0] 維度上求了個均值和方差,對于axis這個參數的理解,可以參考這里。
另外,針對2×3大小的矩陣,axis還可以這么理解,若axis = [0],那么我們2×3的小矩陣可以理解成是一個包含了2個長度為3的一維向量,然后就是求這兩個向量的均值和方差啦!多個向量的均值、方差計算請自行腦補。
當然了,這個例子只是一個最簡單的例子,如果換做求形如“[batchsize, height, width, kernels]”數據的mean和variance呢?接下來來簡單分析一下。
- 2、計算卷積神經網絡某層的的mean和variance
假定我們需要計算數據的形狀是 [batchsize, height, width, kernels],熟悉CNN的都知道,這個在tensorflow中太常見了,例程序如下:
img = tf.Variable(tf.random_normal([128, 32, 32, 64]))
axis = list(range(len(img.get_shape()) - 1))
mean, variance = tf.nn.moments(img, axis)
形如[128, 32, 32, 64]的數據在CNN的中間層非常常見,那么,為了給出一個直觀的認識,這個函數的輸出結果如下,可能輸出的數字比較多。。。
mean = [ -1.58071518e-03 9.46253538e-04 9.92774963e-04 -2.57909298e-04
4.31227684e-03 2.85443664e-03 -3.51431966e-03 -2.95847654e-04
-1.57856941e-03 -7.36653805e-04 -3.81006300e-03 1.95848942e-03
-2.19231844e-03 1.88898295e-04 3.09050083e-03 1.28045678e-04
-5.45501709e-04 -7.49588013e-04 3.41436267e-03 4.55856323e-04
1.21808052e-03 1.71916187e-03 2.33578682e-03 -9.98377800e-04
1.01172924e-03 -3.25803459e-03 1.98090076e-03 -9.53197479e-04
3.37207317e-03 6.27857447e-03 -2.22939253e-03 -1.75476074e-04
1.82938576e-03 2.28643417e-03 -2.59208679e-03 -1.05714798e-03
-1.82652473e-03 4.51803207e-05 -1.38700008e-03 1.88308954e-03
-3.67999077e-03 -4.22883034e-03 8.54551792e-04 -1.30176544e-04
-1.02388859e-03 3.15248966e-03 -1.00244582e-03 -3.58343124e-04
9.68813896e-04 -3.17507982e-03 -2.61783600e-03 -5.57708740e-03
-3.49491835e-04 7.54106045e-03 -9.98616219e-04 5.13806939e-04
1.08468533e-03 1.58560276e-03 -2.76589394e-03 -1.18827820e-03
-4.92024422e-03 3.14301252e-03 9.12249088e-04 -1.98567938e-03]
variance = [ 1.00330877 1.00071466 1.00299144 1.00269675 0.99600208 0.99615276
0.9968518 1.00154674 0.99785519 0.99120021 1.00565553 0.99633628
0.99637395 0.99959981 0.99702841 0.99686354 1.00210547 1.00151515
1.00124979 1.00289011 1.0019592 0.99810153 1.00296855 1.0040164
1.00397885 0.99348587 0.99743217 0.99921477 1.00718474 1.00182319
1.00461221 1.00222814 1.00570309 0.99897575 1.00203466 1.0002507
1.00139284 1.0015136 1.00439298 0.99371535 1.00209546 1.00239146
0.99446201 1.00200033 1.00330424 0.99965429 0.99676734 0.99974728
0.99562836 1.00447667 0.9969337 1.0026046 0.99110448 1.00229466
1.00264072 0.99483615 1.00260413 1.0050714 1.00082493 1.00062656
1.0020628 1.00507069 1.00343442 0.99490905]
然后我解釋一下這些數字到底是怎么來的,可能對于2×3這么大的矩陣,理解起來比較容易,但是對于 [128, 32, 32, 64] 這樣的4維矩陣,理解就有點困難了。
其實很簡單,可以這么理解,一個batch里的128個圖,經過一個64 kernels卷積層處理,得到了128×64個圖,再針對每一個kernel所對應的128個圖,求它們所有像素的mean和variance,因為總共有64個kernels,輸出的結果就是一個一維長度64的數組啦!
手畫示意圖太丑了,我重新畫了一個!
二、tf.nn.batch_normalization函數
官方對函數輸入的定義是:
def batch_normalization(x, mean, variance, offset, scale, variance_epsilon, name=None):
關于這幾個參數,可以參考這篇論文和這個博客,我這里就直接給出一個公式的截圖了,如下
官方對參數的解釋如下
這一堆參數里面,我們已經知道x、mean、variance這三個,那offset和scale呢??答案是:這兩個參數貌似是需要訓練的,其中offset一般初始化為0,scale初始化為1,另外offset、scale的shape與mean相同。
variance_epsilon這個參數設為一個很小的數就行,比如0.001。
但是,我這里要但是一下!BN在神經網絡進行training和testing的時候,所用的mean、variance是不一樣的!這個博客里已經說明了,但具體怎么操作的呢?我們看下面的代碼
update_moving_mean = moving_averages.assign_moving_average(moving_mean, mean, BN_DECAY)
update_moving_variance = moving_averages.assign_moving_average(moving_variance, variance, BN_DECAY)
tf.add_to_collection(UPDATE_OPS_COLLECTION, update_moving_mean)
tf.add_to_collection(UPDATE_OPS_COLLECTION, update_moving_variance)
mean, variance = control_flow_ops.cond(['is_training'], lambda: (mean, variance), lambda: (moving_mean, moving_variance))
看不懂沒關系,這段代碼的意思就是計算moving mean(滑動平均)、moving variance(滑動方差),然后利用 (moving_mean, moving_variance) 進行網絡測試。
關于BN的完整實現,在Ryan Dahl的repository里有,名字叫做tensorflow-resnet,可以自行查看。