深度學習中的規範化

這篇文章介紹深度學習四種主流的規範化, 分別是Batch Normalization(BN[9]), Layer Normalization(LN[7]), Instance Normalization(IN[8])以及Group Normalization(GN[2])。html

1. 做用

爲啥用Normalization? 這是由於訓練深度神經網絡會收斂很慢,頗有可能發生梯度彌散或者梯度爆炸。用了Normalization能夠訓練得很快,學習更好。python

2. 作法

給定輸入\(x\),Normalization的處理方式能夠由下面這個公式形容,不過各類Normalization對x的指望以及方差的求法不一樣。這個公式能夠分兩個部分,第一個部分是\(\frac{x-\mathrm{E}[x]}{\sqrt{\operatorname{Var}[x]+\epsilon}}\)是對activation進行規範化操做,將activation變爲均值爲0,方差爲1的正態分佈,而最後的「scale and shift」\((\gamma,\beta)\)操做則是爲了讓因訓練所需而「刻意」加入的規範化可以有可能還原最初的輸入, 保證模型的能力。[1]
\[y=\frac{x-\mathrm{E}[x]}{\sqrt{\operatorname{Var}[x]+\epsilon}} * \gamma+\beta\]
下面這張圖[2]很直觀地解釋了各類Normalization處理張量的不一樣之處。
1.jpg-54kB
給定一個四維張量\(x\),四維依次表明\([batchsize, channel, height, width]\), 簡單起見,表示爲\((N, C, H, W)\),上圖中,張量的第三個維度以及第四個維度合起來組成了一個維度,方便展現。網絡

\(BN\):能夠看到BN是以C爲滑動軸,對BHW三個維度求和取平均,因此指望的維度是\((1, C, 1, 1)\),而後利用指望求出方差。dom

\(LN\): LN與BN恰好是垂直的位置,是以B爲滑動軸,對CHW三個維度求和取平均,指望的維度是\((N, 1, 1, 1)\)性能

\(IN\): IN則是LN和BN的交匯,以B和C雙軸滑動,對HW兩個維度求和取平均,指望的維度是\((N, C, 1, 1)\)學習

\(GN\): GN則是IN和LN的一種折中考慮,對C維度進行了分組,上圖中是分紅了兩組,因此最後指望的維度是\((N, 2, 1, 1)\)測試

3. 原理與使用

深度神經網絡中的Normalization最早是出如今AlexNet網絡中的LRN(local response normalization), 而LRN計算的是像素局部的統計量,對加速收斂沒有什麼做用。開山加速收斂的Normalization方法是BN,那麼它是怎麼加速收斂的呢?首先要弄清楚爲何沒有BN,收斂會慢,對於一個深層網絡來講,會發生梯度彌散, 這樣在反向傳播更新梯度時,會更新得很是慢,收斂也會變得慢,而BN將原來要變小的activation經過規範化操做,使activation的尺度變大,這樣就消除了梯度彌散而致使參數更新慢的影響。spa

BN訓練階段與測試階段:訓練階段的指望和方差經過當前批數據進行計算,\(\gamma\)\(\beta\)則是BN層的可學習參數,因爲BN層會減去指望,因此前一層是不必加上偏置的。在測試階段,通常是單例forward,對單例求指望和方差是無心義的,因此BN的測試階段的指望和方差是訓練時每一個批次的指望和方差的累計移動平均或者指數移動平均求得的[3][4][6],找了一個簡單的BN訓練實現,詳細見[6]。.net

import numpy as np

def Batchnorm(x, gamma, beta, bn_param, momentum=0.1):

    # x_shape:[B, C, H, W]
    running_mean = bn_param['running_mean']
    running_var = bn_param['running_var']
    results = 0.
    eps = 1e-5

    x_mean = np.mean(x, axis=(0, 2, 3), keepdims=True)
    x_var = np.var(x, axis=(0, 2, 3), keepdims=True)
    x_normalized = (x - x_mean) / np.sqrt(x_var + eps)
    results = gamma * x_normalized + beta

    # 由於在測試時是單個圖片測試,這裏保留訓練時的均值和方差,用在後面測試時用
    running_mean = momentum * running_mean + (1 - momentum) * x_mean
    running_var = momentum * running_var + (1 - momentum) * x_var

    bn_param['running_mean'] = running_mean
    bn_param['running_var'] = running_var

    return results, bn_param

從BN的訓練階段中知道,BN嚴重依賴批數據,經過批數據的統計信息來近似估計全局的統計信息,而在測試階段,沒有進行統計信息的計算,而是經過訓練階段的統計信息來估計新數據,當新數據來自未知的domain(風格遷移將每張圖片看成一個domain,圖像的生成結果主要依賴於某個圖像實例,BN統計的近似全局信息並不會給任務帶來收益,反而會弱化實例之間的特殊性[5]),訓練的統計信息就用處不那麼大了,另外大網絡的大batchsize很佔用GPU顯存,對於缺乏多GPU的人來講,這是很差辦的,而減少batchsize會使計算的指望與方差不能表明總體分佈,網絡性能就會大大折扣。code

爲了消除batch的影響,LN,IN,GN就出現了。這三個規範化操做均對於batch都是不敏感的。

  • BN是針對不一樣神經元層計算指望和方差,同一個batch有相同的指望和方差。
  • LN是針對同層神經元計算指望和方差,不一樣樣本有不一樣的指望和方差。
  • IN是不一樣樣本的不一樣神經元層有不一樣的指望和方差。
  • GN是不一樣樣本不一樣分組有不一樣的指望和方差。

這也致使了它們的用途不一樣。BN統計的是數據的總體分佈,判別模型的結果主要取決於數據的總體分佈,因此BN常常用於固定深度的DNN,CNN中。

對於RNN來講,序列的長度是不一致的,也就是深度不固定,不一樣時間保存的統計信息不一樣,這對於固定批次的BN是計算很麻煩的。而LN與輸入序列的長度是沒有關係的,所以LN在RNN中效果明顯,但在cnn中不如BN。以下圖所示[7],LSTM+LN更快收斂,學習得更好。
image_1d85h60r7m6ommj1k8ql4m15fq2e.png-73.6kB
在圖像風格化任務中,生成結果主要依賴於單個圖像實例,因此這類任務用BN並不合適,但能夠對HW作規範化,能夠加速模型收斂[6][8]。

GN根據傳統的特徵提取器組合特徵的思路(例如HOG根據orientation分組),對channel進行分組,每一層的都有不少卷積核,被核學習到的特徵不是徹底獨立的,有的特徵可能屬於頻率,還有的屬於形狀,亮度等等,所以對特徵進行分組處理是天然的思路,最後的結果也很好,與BN的效果相差無幾,但對batch是無依賴的,適合小批量任務[2]。下圖是BN與GN的對比效果。
image_1d852df6j11hu1t0atf18ab1n1t11.png-54.8kB

具體使用參考見pytorch官方文檔

參考

[1] 魏秀參的回答
[2] GNpaper
[3] YJango的BN文章
[4] BatchNorm源碼
[5] Naiyang Wang的回答
[6] liuxiao的博客
[7] LNpaper
[8] INpaper
[9] BNpaper

相關文章
相關標籤/搜索