文章目錄
幾種常見的normalization方法
基本知識
數學原理
Batch Normalization (BN)
Layer Normalization (LN)
pytorch中的LN
Instance Normalization (IN)
Group Normalization (GN)
PyTorch中的使用
總結
這篇筆記主要來自餘庭嵩的講解。
幾種常見的normalization方法
基本知識
爲什麼要normalization?因爲如果初始值分佈不好時,數據尺度會隨着網絡層數加深而變化異常,詳情可參考之前這篇筆記:梯度消失或梯度爆炸原理。而normalization不僅可以解決這樣的問題,還可以給神經網絡訓練的過程帶來更多其他方便,所以就出現了不同的normalization方法。幾種常見的normalization方法有:batch normalization,layer normalization,instance normalization以及group normalization等等。建議先看BN部分的內容之後再看其他部分。
數學原理
如果一批數據(數據集)含有m個樣本輸入x1,x2,…,xm
x1,x2,…,xm。
這批數據的均值爲
μℬ←1m∑mi=1xiμB←m1i=1∑mxi
這批數據的方差爲
σ2ℬ←1m∑mi=1(xi−μℬ)2σB2←m1i=1∑m(xi−μB)2
那麼如何將這一批數據的方差轉變成1,也就是如何標準化?對每個xixi進行如下操作(下面的ϵϵ是防止分母是0)
xˆi←xi−μℬσ2ℬ+ϵ√x
i←σB2+ϵ
xi−μB
那如何將這一批數據的均值變爲0?或者如何對數據進行平移和縮放?
yi←γxˆi+β≡BNγ,β(xi)yi←γx
i+β≡BNγ,β(xi)
這一步驟就叫affine transform。這裏的兩個參數γγ,ββ是可學習的參數,讓模型自己選擇是否對數據進行變換操作。這裏還有一個重要的點,如果γγ,β
β分別學習爲是方差和均值,那麼標準化操作將被逆操作回來,由此就可視爲沒有標準化這一步。這就使得模型更加靈活了。
這裏的BN,LN,IN和GN相同的地方是:對於每一個樣本的操作都是一樣的,都是按照上面最後兩個公式進行操作
不同的地方是:上面開頭兩個公式,也就是均值和方差的求取方式不一樣。BN是在batch中尋找均值和方差,LN是在網絡層裏面尋找均值和方差,IN是依據實例求取均值方差(圖像生成),GN是分組求取均值和方差。
Batch Normalization (BN)
由於batch normalization篇幅較大,實驗較多,所以單寫了一篇筆記:動手理解Batch Normalization,裏面對batch normalization從理論到實驗都進行了探討說明。
Layer Normalization (LN)
LN主要出自文章《Layer Normalization》,如果想知道更多細節和實驗對比,可以看原文。
起因:BN不適用於變長的網絡,如RNN
思路:逐層計算均值和方差,每個網絡層的輸出都有其均值方差。
注意事項:
不再有running_mean和running_var,因爲給定特徵圖的情況下都是固定的標量了,是根據該層內所有元素的均值和方差計算的。
兩個參數γ
γ和ββ是逐元素的,也就是每個神經元都有其參數γγ和β
β。
pytorch中的LN
主要參數有
normalized_shape:該層的特徵形狀
eps:分母修正項,默認1e-5
elementwise_affine:是否需要affine transform,默認true
代碼實例如下,需要給nn.LayerNorm傳遞的參數是特徵的shape(除去batch維),需要從後往前設置維度,例如數據是AxBxCxD的,可以輸入D,[C, D],但是不可以輸入[B, C]:
batch_size = 8
num_features = 6
features_shape = (3, 4)
feature_map = torch.ones(features_shape)
feature_maps = torch.stack([feature_map * (i + 1) for i in range(num_features)], dim=0)
feature_maps_bs = torch.stack([feature_maps for i in range(batch_size)], dim=0)
ln = nn.LayerNorm(feature_maps_bs.size()[1:], elementwise_affine=True)
output = ln(feature_maps_bs)
print("Layer Normalization")
print(ln.weight.shape)
print(feature_maps_bs[0, ...])
print(output[0, ...])
輸出結果:
Layer Normalization
torch.Size([6, 3, 4])
tensor([[[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]],
[[2., 2., 2., 2.],
[2., 2., 2., 2.],
[2., 2., 2., 2.]],
[[3., 3., 3., 3.],
[3., 3., 3., 3.],
[3., 3., 3., 3.]],
[[4., 4., 4., 4.],
[4., 4., 4., 4.],
[4., 4., 4., 4.]],
[[5., 5., 5., 5.],
[5., 5., 5., 5.],
[5., 5., 5., 5.]],
[[6., 6., 6., 6.],
[6., 6., 6., 6.],
[6., 6., 6., 6.]]])
tensor([[[-1.4638, -1.4638, -1.4638, -1.4638],
[-1.4638, -1.4638, -1.4638, -1.4638],
[-1.4638, -1.4638, -1.4638, -1.4638]],
[[-0.8783, -0.8783, -0.8783, -0.8783],
[-0.8783, -0.8783, -0.8783, -0.8783],
[-0.8783, -0.8783, -0.8783, -0.8783]],
[[-0.2928, -0.2928, -0.2928, -0.2928],
[-0.2928, -0.2928, -0.2928, -0.2928],
[-0.2928, -0.2928, -0.2928, -0.2928]],
[[ 0.2928, 0.2928, 0.2928, 0.2928],
[ 0.2928, 0.2928, 0.2928, 0.2928],
[ 0.2928, 0.2928, 0.2928, 0.2928]],
[[ 0.8783, 0.8783, 0.8783, 0.8783],
[ 0.8783, 0.8783, 0.8783, 0.8783],
[ 0.8783, 0.8783, 0.8783, 0.8783]],
[[ 1.4638, 1.4638, 1.4638, 1.4638],
[ 1.4638, 1.4638, 1.4638, 1.4638],
[ 1.4638, 1.4638, 1.4638, 1.4638]]], grad_fn=<SelectBackward>)
Instance Normalization (IN)
起因:BN在圖像生成中不適用,每個batch中的圖像有不同的風格,模式和style,所以不能把一個batch中的數據拿到一起計算。
思路:逐instance(channel)計算均值和方差
例如,AxBxCxD的圖,A是batchsize,B是channel數,C和D代表一張圖的所有像素點。那麼是不可以使用nn.batchnorm2d那樣直接把各個batch的對應相同通道的特徵圖拿在一起計算均值和方差的。
所以instance normalization就是不僅逐樣本,還逐通道地計算每一個特徵圖(CxD)的均值和方差。
Group Normalization (GN)
可參考論文《Group Normalization》
起因:在小batch樣本中,BN估計不準確。樣本越多,估計的均值和方差就比較準,但如果batchsize是1或者2的時候,估計的樣本就很少,這樣估計值就不準確。那麼這個情況下再計算所有batch在該特徵下的平均值和方差就不可行了,就需要把多個特徵合併一下,併爲一組(group),來求均值和方差。
注意:和layernorm一樣,不再有running_mean和running_var,兩個參數γ
γ和β
β是逐通道的。
應用場景:大模型,特徵數非常多,此時的batchsize只能設置比較小。
PyTorch中的使用
主要參數:
num_groups:分組數,一般是2的n次冪,因爲其要能被特徵數整除
num_channels:通道數
代碼實例
batch_size = 2
num_features = 4
num_groups = 2
features_shape = (2, 2)
feature_map = torch.ones(features_shape) # 2D
feature_maps = torch.stack([feature_map * (i + 1) for i in range(num_features)], dim=0)
feature_maps_bs = torch.stack([feature_maps * (i + 1) for i in range(batch_size)], dim=0) # 4D
gn = nn.GroupNorm(num_groups, num_features)
outputs = gn(feature_maps_bs)
print("Group Normalization")
print(gn.weight.shape)
print(outputs[0])
輸出結果爲
Group Normalization
torch.Size([4])
tensor([[[-1.0000, -1.0000],
[-1.0000, -1.0000]],
[[ 1.0000, 1.0000],
[ 1.0000, 1.0000]],
[[-1.0000, -1.0000],
[-1.0000, -1.0000]],
[[ 1.0000, 1.0000],
[ 1.0000, 1.0000]]], grad_fn=<SelectBackward>)
總結