原文鏈接: https://www.zhihu.com/question/68730628/answer/607608890
BN和IN其實本質上是同一個東西,只是IN是作用於單張圖片,但是BN作用於一個batch。
一.BN和IN的對比
假如現有6張圖片x1,x2,x3,x4,x5,x6,每張圖片在CNN的某一卷積層有6個通道,也就是6個feature map。有關Batch Normalization與Instance Normalization的區別請看下圖:
上圖中,從C方向看過去是指一個個通道,從N看過去是一張張圖片。每6個豎着排列的小正方體組成的長方體代表一張圖片的一個feature map。藍色的方塊是一起進行Normalization的部分。
由此就可以很清楚的看出,Batch Normalization是指6張圖片中的每一張圖片的同一個通道一起進行Normalization操作。而Instance Normalization是指單張圖片的單個通道單獨進行Noramlization操作。
二.各自適用場景
BN適用於判別模型中,比如圖片分類模型。因為BN注重對每個batch進行歸一化,從而保證數據分布的一致性,而判別模型的結果正是取決於數據整體分布。但是BN對batchsize的大小比較敏感,由於每次計算均值和方差是在一個batch上,所以如果batchsize太小,則計算的均值、方差不足以代表整個數據分布;
IN適用於生成模型中,比如圖片風格遷移。因為圖片生成的結果主要依賴於某個圖像實例,所以對整個batch歸一化不適合圖像風格化中,在風格遷移中使用Instance Normalization不僅可以加速模型收斂,並且可以保持每個圖像實例之間的獨立。

四.算法的過程
4.1 BN
- 沿着通道計算每個batch的均值u
- 沿着通道計算每個batch的方差σ^2
- 對x做歸一化,x’=(x-u)/開根號(σ^2+ε)
- 加入縮放和平移變量γ和β ,歸一化后的值,y=γx’+β
4.2 IN
- 沿着通道計算每張圖的均值u
- 沿着通道計算每張圖的方差σ^2
- 對x做歸一化,x’=(x-u)/開根號(σ^2+ε)
- 加入縮放和平移變量γ和β ,歸一化后的值,y=γx’+β
算法的作用
BatchNorm就是在深度神經網絡訓練過程中使得每一層神經網絡的輸入保持相同分布的。 BN算法在網絡中的作用 BN算法像卷積層,池化層、激活層一樣也輸入一層。BN層添加在激活函數前,對輸入激活函數的輸入進行歸一化。這樣解決了輸入數據發生偏移和增大的影響。 優點: 1、加快訓練速度,能夠增大學習率,即使小的學習率也能夠有快速的學習速率; 2、不用理會擬合中的droupout、L2 正則化項的參數選擇,采用BN算法可以省去這兩項或者只需要小的L2正則化約束。
原因,BN算法后,參數進行了歸一化,原本經過激活函數沒有太大影響的神經元,分布變得明顯,經過一個激活函數以后,神經元會自動削弱或者去除一些神經元,就不用再對其進行dropout。
另外就是L2正則化,由於每次訓練都進行了歸一化,就很少發生由於數據分布不同導致的參數變動過大,帶來的參數不斷增大。 3、 可以把訓練數據集打亂,防止訓練發生偏移。
x:input
mean:樣本均值
variance:樣本方差
offset:樣本偏移(相加一個轉化值)
scale:縮放(默認為1)
variance_epsilon:為了避免分母為0,添加的一個極小值
class torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True)[source] 對小批量(mini-batch)3d數據組成的4d輸入進行批標准化(Batch Normalization)操作
在每一個小批量(mini-batch)數據中,計算輸入各個維度的均值和標准差(多張圖片的同一通道一起考慮)。gamma與beta是可學習的大小為C的參數向量(C為輸入通道數) 在訓練時,該層計算每次輸入的均值與方差,並進行移動平均。移動平均默認的動量值為0.1。 在驗證時,訓練求得的均值/方差將用於標准化驗證數據。 參數: num_features: 來自期望輸入的特征數,該期望輸入的大小為'batch_size x num_features x height x width' eps: 為保證數值穩定性(分母不能趨近或取0),給分母加上的值。默認為1e-5。 momentum: 動態均值和動態方差所使用的動量。默認為0.1。 affine: 一個布爾值,當設為true,給該層添加可學習的仿射變換參數。 Shape: - 輸入:(N, C,H, W) - 輸出:(N, C, H, W)(輸入輸出相同)
在使用pytorch的 nn.BatchNorm2d() 層的時候,經常地使用方式為在參數里面只加上待處理的數據的通道數(out_channel,特征數量),但是有時候會在后面再加入一個小數,比如這樣 nn.BatchNorm2d(64,0.8),這里面的0.8有什么作用呢? 我們知道在訓練過程中 nn.BatchNorm2d() 的作用是根據統計的mean 和var來對數據進行標准化,並且這個mena和var在每個batch中都會進行,為了使得數據更有統計意義,
使得整個訓練數據的特征都能夠被保存,則在每個batch過程中,都會對網絡的mean和var進行更新,這里就涉及到新的 batch的統計數據mean和var與網絡已經保存的這兩個統計數據之間的取舍問題了,而這個0.8就指定了保存的比例,
這個參數名為momentum.
參數更新是以差分的形式進行的,xt代表新一輪batch產生的數據,x^代表歷史數據,這個參數越大,代表當前batch產生的統計數據的重要性越強。
pytorch方法測試詳解——歸一化(BatchNorm2d)
import torch import torch.nn as nn m = nn.BatchNorm2d(2,affine=True) #權重w和偏重將被使用學習 input = torch.randn(2,2,2,3)#第二個2對應BatchNorm2d(2,affine=True)中的2,表示通道數目 output = m(input) print("輸入圖片:") print(input) print("歸一化權重:") print(m.weight.shape)#[2] print(m.weight) print("歸一化的偏重:") print(m.bias.shape)#[2] print(m.bias) print("歸一化的輸出:") print(output) print("輸出的尺度:") print(output.size()) print("輸入的第一個維度:") A = input[0][0] B = input[1][0] print(A) print(B) C =torch.cat([A,B],dim=0)#縱向拼接,BN按通道處理 print(C) firstDimenMean = torch.Tensor.mean(C) firstDimenVar= torch.Tensor.var(C,False) #Bessel's Correction貝塞爾校正不會被使用 print(m.eps) print("輸入的第一個維度平均值:") print(firstDimenMean) print("輸入的第一個維度方差:") print(firstDimenVar) bacthnormone = \ ((input[0][0][0][0] - firstDimenMean)/(torch.pow(firstDimenVar,0.5)+m.eps ))\ * m.weight[0] + m.bias[0] print(bacthnormone)#-0.3023對output[0][0][0][0]
輸入圖片: tensor([[[[ 0.0242, -0.4434, 0.0058], [-0.2674, 1.8737, 1.8069]], [[-0.3274, -0.7689, 0.3982], [ 0.7088, -0.4982, 1.3230]]], [[[-2.0022, 1.2179, 1.0144], [-0.0057, 0.3186, 0.4562]], [[ 0.3482, -0.0875, -1.1406], [ 1.5080, 0.8830, 0.0946]]]]) 歸一化權重: torch.Size([2]) Parameter containing: tensor([1., 1.], requires_grad=True) 歸一化的偏重: torch.Size([2]) Parameter containing: tensor([0., 0.], requires_grad=True) 歸一化的輸出: tensor([[[[-0.3023, -0.7597, -0.3203], [-0.5875, 1.5068, 1.4414]], [[-0.6781, -1.2420, 0.2489], [ 0.6456, -0.8962, 1.4301]]], [[[-2.2844, 0.8653, 0.6663], [-0.3316, -0.0144, 0.1203]], [[ 0.1849, -0.3717, -1.7169], [ 1.6665, 0.8681, -0.1391]]]], grad_fn=<NativeBatchNormBackward>) 輸出的尺度: torch.Size([2, 2, 2, 3]) 輸入的第一個維度: tensor([[ 0.0242, -0.4434, 0.0058], [-0.2674, 1.8737, 1.8069]]) tensor([[-2.0022, 1.2179, 1.0144], [-0.0057, 0.3186, 0.4562]]) tensor([[ 0.0242, -0.4434, 0.0058], [-0.2674, 1.8737, 1.8069], [-2.0022, 1.2179, 1.0144], [-0.0057, 0.3186, 0.4562]]) 1e-05 輸入的第一個維度平均值: tensor(0.3332) 輸入的第一個維度方差: tensor(1.0452) tensor(-0.3023, grad_fn=<AddBackward0>)