談談Tensorflow的Batch Normalization

tensorflow中關(guān)于BN(Batch Normalization)的函數(shù)主要有兩個,分別是:

  • tf.nn.moments
  • tf.nn.batch_normalization

關(guān)于這兩個函數(shù),官方API中有詳細的說明,具體的細節(jié)可以點鏈接查看,關(guān)于BN的介紹可以參考這篇論文,我來說說自己的理解。
不得不吐槽一下,tensorflow的官方API很少給例子,太不人性化了,人家numpy做的就比tensorflow強。
對了,moments函數(shù)的計算結(jié)果一般作為batch_normalization的部分輸入!這就是兩個函數(shù)的關(guān)系,下面展開介紹!

一、tf.nn.moments函數(shù)

官方的輸入定義如下:

def moments(x, axes, name=None, keep_dims=False)

解釋如下:

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

這個函數(shù)的輸出有兩個,用官方的話說就是:

Two Tensor objects: mean and variance.

解釋如下:

  • mean 就是均值啦
  • variance 就是方差啦

關(guān)于這個函數(shù)的最基本的知識就介紹完了,但依然沒明白這函數(shù)到底是干啥的,下面通過幾個例子來說明:

  • 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)

輸出的結(jié)果如下:

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]

有了例子和結(jié)果,就很好理解了,moments函數(shù)就是在 [0] 維度上求了個均值和方差,對于axis這個參數(shù)的理解,可以參考這里。
另外,針對2×3大小的矩陣,axis還可以這么理解,若axis = [0],那么我們2×3的小矩陣可以理解成是一個包含了2個長度為3的一維向量,然后就是求這兩個向量均值方差啦!多個向量的均值、方差計算請自行腦補。
當然了,這個例子只是一個最簡單的例子,如果換做求形如“[batchsize, height, width, kernels]”數(shù)據(jù)的mean和variance呢?接下來來簡單分析一下。

  • 2、計算卷積神經(jīng)網(wǎng)絡某層的的mean和variance
    假定我們需要計算數(shù)據(jù)的形狀是 [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]的數(shù)據(jù)在CNN的中間層非常常見,那么,為了給出一個直觀的認識,這個函數(shù)的輸出結(jié)果如下,可能輸出的數(shù)字比較多。。。

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]

然后我解釋一下這些數(shù)字到底是怎么來的,可能對于2×3這么大的矩陣,理解起來比較容易,但是對于 [128, 32, 32, 64] 這樣的4維矩陣,理解就有點困難了。
其實很簡單,可以這么理解,一個batch里的128個圖,經(jīng)過一個64 kernels卷積層處理,得到了128×64個圖,再針對每一個kernel所對應的128個圖,求它們所有像素的mean和variance,因為總共有64個kernels,輸出的結(jié)果就是一個一維長度64的數(shù)組啦!
手畫示意圖太丑了,我重新畫了一個!


計算mean和variance

二、tf.nn.batch_normalization函數(shù)

官方對函數(shù)輸入的定義是:

def batch_normalization(x, mean, variance, offset, scale, variance_epsilon, name=None):

關(guān)于這幾個參數(shù),可以參考這篇論文和這個博客,我這里就直接給出一個公式的截圖了,如下

晦澀難懂的公式

官方對參數(shù)的解釋如下


官方的解釋

這一堆參數(shù)里面,我們已經(jīng)知道x、mean、variance這三個,那offset和scale呢??答案是:這兩個參數(shù)貌似是需要訓練的,其中offset一般初始化為0,scale初始化為1,另外offset、scale的shape與mean相同。

variance_epsilon這個參數(shù)設為一個很小的數(shù)就行,比如0.001。

但是,我這里要但是一下!BN在神經(jīng)網(wǎng)絡進行training和testing的時候,所用的mean、variance是不一樣的!這個博客里已經(jīng)說明了,但具體怎么操作的呢?我們看下面的代碼

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))

看不懂沒關(guān)系,這段代碼的意思就是計算moving mean(滑動平均)、moving variance(滑動方差),然后利用 (moving_mean, moving_variance) 進行網(wǎng)絡測試。

關(guān)于BN的完整實現(xiàn),在Ryan Dahl的repository里有,名字叫做tensorflow-resnet,可以自行查看。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容