Batch Normalization

這是我參與8月更文挑戰的第8天,活動詳情查看: 8月更文挑戰html

Batch Normalization(批量歸一化)是深度學習中常常用到的一種歸一化方法python

咱們知道Sigmoid函數在定義域爲 ( , 4 ) ( 4 , ) (-\infty,-4) \cup (4,\infty) 內導數趨於0,因爲容易出現梯度消失的現象,所以ReLU函數使用的較多 markdown

但在某些場合不可避免的要去使用Sigmoid函數,所以咱們但願能把輸入的 x x 值控制在有效的區間內,進行一個等效變化,使這些值均勻的分佈在0附近,這樣輸入到Sigmoid函數後就能大機率避免梯度消失網絡

以下面左側的圖所示, x 1 x_1 的值處於一個比較小的區間 x 2 x_2 的值處於一個比較大的區間,所以對於 w 2 w_2 來講,少許的變化就會對Loss產生急劇的變化,而對 w 1 w_1 來講,變化就會相對小一點,能夠看下面的「等高線」圖,沿着縱軸方向變化的話,等高線會急劇的變化,沿着橫軸方向變化,等高線的變化就稍平緩一點app

但若是如右圖所示,輸入的值區間都很接近,這樣 w 1 w_1 w 2 w_2 對Loss的影響就比較接近,就會造成一種「圓形」路徑,在這種狀況進行搜索的時候,無論從哪一個點出發,梯度的方向都是指向全局最小解的方向,這種搜索過程會比較快一些,並且更穩定函數

Batch Normalization較多的應用於兩個方面post

  • Image Normalization,例如對RGB三通道進行Normalization,將數據進行統一縮放
normalize = transforms.Normalize(mean=ean[0.485, 0.456, 0.406],
                                 std=[0.229, 0.224, 0.225])
複製代碼

mean有三個值,分別對應RGB三個通道的均值,具體的Normalize過程就是學習

\begin{align*} x_R&= \frac{x_R-0.485}{0.229} \\ x_G&= \frac{x_G-0.456}{0.224} \\ x_B&= \frac{x_B-0.406}{0.225} \\ \end{align*}

常見的Normalization

目前常見的Normalization有四種,將輸入的圖像shape記爲[N,C,H,W],這幾個方法主要區別是: 優化

  • BatchNorm:batch方向作歸一化,計算NHW的均值,對小batchsize效果很差;(BN主要缺點是對batchsize的大小比較敏感,因爲每次計算均值和方差是在一個batch上,因此若是batchsize過小,則計算的均值、方差不足以表明整個數據分佈)
  • LayerNorm:channel方向作歸一化,計算CHW的均值;(對RNN做用明顯)
  • InstanceNorm:一個batch,一個channel內作歸一化。計算HW的均值,用在風格化遷移;(由於在圖像風格化中,生成結果主要依賴於某個圖像實例,因此對整個batch歸一化不適合圖像風格化中,於是對HW作歸一化。能夠加速模型收斂,而且保持每一個圖像實例之間的獨立)
  • GroupNorm:將channel方向分group,而後每一個group內作歸一化,算(C//G)HW的均值;這樣與batchsize無關,不受其約束

詳述Batch Normalization

以下圖,輸入數據是6張3通道784個像素點的數據,將其分到三個通道上,在每一個通道上也就是[6,784]的數據,而後分別獲得和通道數同樣多的統計數據均值 μ \mu 和標準差 σ \sigma 。將每一個像素值減去 μ \mu ,除以 σ \sigma 就變換成了近似於 N ( 0 , 1 ) N(0,1) 分佈的數據,以後再用參數 γ \gamma β \beta 將其變化到近似 N ( β , γ ) N(\beta, \gamma) 的分佈ui

μ \mu σ \sigma 只是樣本中的統計數據,是沒有梯度信息的,不過會保存在運行參數裏。而 γ \gamma β \beta 屬於訓練的參數,是有梯度信息的

Batch Normalize的規範化寫法爲 前三步是batch normalization的工序,通過這三步之後數據就近似於標準正態分佈 N ( 0 , 1 ) N(0,1) ,可是後面的公式還有一個反向操做,將normalize後的數據再平移和擴展,讓數據近似於 N ( β , γ ) N(\beta, \gamma) ,這位爲了讓神經網絡本身去學着使用和修改這兩個擴展參數,這樣神經網絡就能本身慢慢琢磨出前面的normalization操做到底有沒有起到優化的作喲個,若是沒有起到優化的做用,就用 γ \gamma β \beta 來抵消一些normalization的操做

下面具體看一下在PyTorch中如何實現Batch Normalize

import torch
import torch.nn as nn
import torch.nn.functional as F

# 隨機生成一個Batch的模擬,100張16通道784像素點的數據
# 均勻分佈U(0~1)
x = torch.rand(128, 16, 784)
# 將28*28變爲打平爲一維的784
layer = nn.BatchNorm1d(16)
# Batch Normalization層,由於輸入是將高度H和寬度W合成了一個維度,因此這裏用1d
# 由於Batch Norm的參數直接是由channel數量得來的,所以這裏直接給定了channel的數量爲16,後續會輸出16個channel的統計信息
out = layer(x) # f orward

print(out.shape)
print(layer.running_mean) # 全局的均值
print(layer.running_var) # 全局的方差
複製代碼

運行結果

tensor([0.0500, 0.0498, 0.0499, 0.0501, 0.0500, 0.0502, 0.0500, 0.0500, 0.0500,
        0.0500, 0.0499, 0.0501, 0.0500, 0.0499, 0.0499, 0.0502])
tensor([0.9084, 0.9084, 0.9083, 0.9084, 0.9084, 0.9083, 0.9083, 0.9083, 0.9083,
        0.9083, 0.9084, 0.9084, 0.9083, 0.9083, 0.9083, 0.9083])
複製代碼

注意layer.running_meanlayer.running_var獲得的是全局的均值和方差,不是當前Batch上的,只不過這裏只跑了一個Batch而已因此它就是這個Batch上的。如今尚未辦法直接查看某個Batch上的這兩個統計量的值

x = torch.randn(1, 16, 7, 7)  # 1張16通道的7乘7的圖像

# Batch Normalization層,由於輸入是有高度H和寬度W的,因此這裏用2d
layer = nn.BatchNorm2d(16)  # 傳入通道數
out = layer(x)

print(out.shape)
print(layer.running_mean)  # 全局的均值
print(layer.running_var)  # 全局的方差
print(layer.weight)  # weight也就是前面公式裏的gamma
print(layer.bias)  # bias也就是前面公式裏的beta
複製代碼

運行結果

torch.Size([1, 16, 7, 7])
tensor([ 0.0187,  0.0125, -0.0032,  0.0032,  0.0034,  0.0031,  0.0231, -0.0024,
         0.0002,  0.0194, -0.0097,  0.0177,  0.0324, -0.0013,  0.0128, -0.0086])
tensor([0.9825, 0.9799, 0.9984, 0.9895, 0.9992, 0.9809, 0.9919, 0.9769, 0.9928,
        0.9949, 1.0055, 1.0368, 0.9867, 0.9904, 1.0097, 0.9910])
Parameter containing:
tensor([0.5925, 0.5662, 0.1066, 0.0073, 0.9517, 0.0476, 0.0416, 0.2041, 0.8666,
        0.6467, 0.7665, 0.0300, 0.9050, 0.8024, 0.2816, 0.1745],
       requires_grad=True)
Parameter containing:
tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       requires_grad=True)
複製代碼

注意這裏的layer.weightlayer.bias是當前batch上的

若是在定義層時使用了參數affine=False,那麼就是固定 γ = 1 \gamma = 1 β = 0 \beta=0 不自動學習,這時參數layer.weightlayer.bias將是None

總結

使用了 γ \gamma β \beta 以後,最後獲得的分佈是往 N ( β , γ ) N(\beta, \gamma) 上靠的,而不是往 N ( 0 , 1 ) N(0, 1) 上靠的

使用了Batch Normalization讓Converge(收斂)的速度加快了,這個能夠直觀理解,使用了靠近0的部分的Sigmoid激活,其梯度信息更大了。而且可以獲得一個更好的解

提高了Robust(魯棒性),這使得網絡更加穩定,這能夠從最前面第二張圖所示來直觀理解,若是參數有大有小,解空間像左邊同樣,那麼稍微調整學習率可能就發生抖動(如圖中左側橢圓解空間上下方向走,且學習率太大時)或者訓練速度太慢(如圖中右側橢圓解空間左右方向走,且學習率過小時)。這讓超參數的調整沒有那麼敏感

相關文章
相關標籤/搜索