深度學習中的規范化


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

1. 作用

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

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)\),然后利用期望求出方差。

\(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的尺度變大,這樣就消除了梯度彌散而導致參數更新慢的影響。

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

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會使計算的期望與方差不能代表整體分布,網絡性能就會大大折扣。

為了消除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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM