這是我參與8月更文挑戰的第8天,活動詳情查看: 8月更文挑戰html
Batch Normalization(批量歸一化)是深度學習中常常用到的一種歸一化方法python
咱們知道Sigmoid函數在定義域爲 內導數趨於0,因爲容易出現梯度消失的現象,所以ReLU函數使用的較多 markdown
但在某些場合不可避免的要去使用Sigmoid函數,所以咱們但願能把輸入的 值控制在有效的區間內,進行一個等效變化,使這些值均勻的分佈在0附近,這樣輸入到Sigmoid函數後就能大機率避免梯度消失網絡
以下面左側的圖所示, 的值處於一個比較小的區間 的值處於一個比較大的區間,所以對於 來講,少許的變化就會對Loss產生急劇的變化,而對 來講,變化就會相對小一點,能夠看下面的「等高線」圖,沿着縱軸方向變化的話,等高線會急劇的變化,沿着橫軸方向變化,等高線的變化就稍平緩一點app
但若是如右圖所示,輸入的值區間都很接近,這樣 和 對Loss的影響就比較接近,就會造成一種「圓形」路徑,在這種狀況進行搜索的時候,無論從哪一個點出發,梯度的方向都是指向全局最小解的方向,這種搜索過程會比較快一些,並且更穩定函數
Batch Normalization較多的應用於兩個方面post
normalize = transforms.Normalize(mean=ean[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
複製代碼
mean有三個值,分別對應RGB三個通道的均值,具體的Normalize過程就是學習
目前常見的Normalization有四種,將輸入的圖像shape記爲[N,C,H,W],這幾個方法主要區別是: 優化
以下圖,輸入數據是6張3通道784個像素點的數據,將其分到三個通道上,在每一個通道上也就是[6,784]的數據,而後分別獲得和通道數同樣多的統計數據均值 和標準差 。將每一個像素值減去 ,除以 就變換成了近似於 分佈的數據,以後再用參數 和 將其變化到近似 的分佈ui
和 只是樣本中的統計數據,是沒有梯度信息的,不過會保存在運行參數裏。而 和 屬於訓練的參數,是有梯度信息的
Batch Normalize的規範化寫法爲 前三步是batch normalization的工序,通過這三步之後數據就近似於標準正態分佈 ,可是後面的公式還有一個反向操做,將normalize後的數據再平移和擴展,讓數據近似於 ,這位爲了讓神經網絡本身去學着使用和修改這兩個擴展參數,這樣神經網絡就能本身慢慢琢磨出前面的normalization操做到底有沒有起到優化的作喲個,若是沒有起到優化的做用,就用 和 來抵消一些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_mean
和layer.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.weight
和layer.bias
是當前batch上的
若是在定義層時使用了參數affine=False
,那麼就是固定
和
不自動學習,這時參數layer.weight
和layer.bias
將是None
使用了 和 以後,最後獲得的分佈是往 上靠的,而不是往 上靠的
使用了Batch Normalization讓Converge(收斂)的速度加快了,這個能夠直觀理解,使用了靠近0的部分的Sigmoid激活,其梯度信息更大了。而且可以獲得一個更好的解
提高了Robust(魯棒性),這使得網絡更加穩定,這能夠從最前面第二張圖所示來直觀理解,若是參數有大有小,解空間像左邊同樣,那麼稍微調整學習率可能就發生抖動(如圖中左側橢圓解空間上下方向走,且學習率太大時)或者訓練速度太慢(如圖中右側橢圓解空間左右方向走,且學習率過小時)。這讓超參數的調整沒有那麼敏感