百度PaddlePaddle入門-19(卷積神經網絡入門B)


批歸一化(Batch Normalization)

批歸一化方法方法(Batch Normalization,BatchNorm)是由Ioffe和Szegedy於2015年提出的,已被廣泛應用在深度學習中,其目的是對神經網絡中間層的輸出進行標准化處理,使得中間層的輸出更加穩定。

通常我們會對神經網絡的數據進行標准化處理,處理后的樣本數據集滿足均值為0,方差為1的統計分布,這是因為當輸入數據的分布比較固定時,有利於算法的穩定和收斂。對於深度神經網絡來說,由於參數是不斷更新的,即使輸入數據已經做過標准化處理,但是對於比較靠后的那些層,其接收到的輸入仍然是劇烈變化的,通常會導致數值不穩定,模型很難收斂。BatchNorm能夠使神經網絡中間層的輸出變得更加穩定,並有如下三個優點:

  • 使學習快速進行(能夠使用較大的學習率)

  • 降低模型對初始值的敏感性

  • 從一定程度上抑制過擬合

BatchNorm主要思路是在訓練時按mini-batch為單位,對神經元的數值進行歸一化,使數據的分布滿足均值為0,方差為1。具體計算過程如下:

1. 計算mini-batch內樣本的均值

uB <- 1/m * Sigma ( x(i))  (i=1->m)

其中x(i)表示mini-batch中的第i個樣本。

例如輸入mini-batch包含3個樣本,每個樣本有2個特征,分別是:

x(1)=(1,2),  x(2)=(3,6),  x(3)=(5,10)

對每個特征分別計算mini-batch內樣本的均值:

μB0=(1+3+5)/3=3,   μB1=(2+6+10)/3=6

則樣本均值是:

μB=(μB0,μB1)=(3,6)

 

2. 計算mini-batch內樣本的方差

σB2←1/m*∑(i=1,m) (x(i)−μB)^2

上面的計算公式先計算一個批次內樣本的均值μB和方差σB2,然后再對輸入數據做歸一化,將其調整成均值為0,方差為1的分布。

對於上述給定的輸入數據x(1),x(2),x(3),可以計算出每個特征對應的方差:

σB0 ^2=1/3⋅((1−3)^2+(3−3)^2+(5−3)^2)=8/3

σB1 ^2=1/3⋅((2−6)^2+(6−6)^2+(10−6)^2)=32/3

則樣本方差是:

σB ^2=(σB0 ^2,σB1 ^2)=(8/3,32/3)

 

3. 計算標准化之后的輸出

 

x^(i)← (x(i)−μB) / sqrt(σB^2+ϵ)

其中ϵ是一個微小值(例如1e−7),其主要作用是為了防止分母為0。

對於上述給定的輸入數據x(1),x(2),x(3),可以計算出標准化之后的輸出:

 

x^(1)=((1−3)/sqrt(8/3),  (2−6)/sqrt(32/3))=(−sqrt(3/2),  −swrt(3/2))

x^(2)=(3−3)/sqrt(8/3), ( 6−6)/sqrt(32/3))=(0,  0)                

x^(3)=(5−3)/sqrt(8/3),  (10−6)/sqrt(32/3))=(sqrt(3/2),  sqrt(3/2))   

  • 讀者可以自行驗證由x^(1),x^(2),x^(3)構成的mini-batch,是否滿足均值為0,方差為1的分布。

如果強行限制輸出層的分布是標准化的,可能會導致某些特征模式的丟失,所以在標准化之后,BatchNorm會緊接着對數據做縮放和平移。

 

yi←γ x^i+β

其中γβ是可學習的參數,可以賦初始值γ=1,β=0,在訓練過程中不斷學習調整。

上面列出的是BatchNorm方法的計算邏輯,下面針對兩種類型的輸入數據格式分別進行舉例。飛槳支持輸入數據的維度大小為2、3、4、5四種情況,這里給出的是維度大小為2和4的示例。

 

  • 示例一: 當輸入數據形狀是[N,K]時,一般對應全連接層的輸出,示例代碼如下所示。

這種情況下會分別對K的每一個分量計算N個樣本的均值和方差,數據和參數對應如下:

  • 輸入 x, [N, K]
  • 輸出 y, [N, K]
  • 均值 μB,[K, ]
  • 方差 σB^2, [K, ]
  • 縮放參數γ, [K, ]
  • 平移參數β, [K, ]

 “BatchNorm里面不是還要對標准化之后的結果做仿射變換嗎,怎么使用Numpy計算的結果與BatchNorm算子一致?

” 這是因為BatchNorm算子里面自動設置初始值γ=1,β=0,這時候仿射變換相當於是恆等變換。在訓練過程中這兩個參數會不斷的學習,這時仿射變換就會起作用。

 

 1 # 輸入數據形狀是[N, C, H, W]時的batchnorm示例
 2 import numpy as np
 3 
 4 import paddle
 5 import paddle.fluid as fluid
 6 from paddle.fluid.dygraph.nn import BatchNorm
 7 
 8 # 設置隨機數種子,這樣可以保證每次運行結果一致
 9 np.random.seed(100)
10 # 創建數據
11 data = np.random.rand(2,3,3,3).astype('float32')
12 # 使用BatchNorm計算歸一化的輸出
13 with fluid.dygraph.guard():
14     # 輸入數據維度[N, C, H, W],num_channels等於C
15     bn = BatchNorm('bn', num_channels=3)
16     x = fluid.dygraph.to_variable(data)
17     y = bn(x)
18     print('input of BatchNorm Layer: \n {}'.format(x.numpy()))
19     print('output of BatchNorm Layer: \n {}'.format(y.numpy()))
20 
21 # 取出data中第0通道的數據,
22 # 使用numpy計算均值、方差及歸一化的輸出
23 a = data[:, 0, :, :]
24 a_mean = a.mean()
25 a_std = a.std()
26 b = (a - a_mean) / a_std
27 print('channel 0 of input data: \n {}'.format(a))
28 print('std {}, mean {}, \n output: \n {}'.format(a_mean, a_std, b))
29 
30 # 提示:這里通過numpy計算出來的輸出
31 # 與BatchNorm算子的結果略有差別,
32 # 因為在BatchNorm算子為了保證數值的穩定性,
33 # 在分母里面加上了一個比較小的浮點數epsilon=1e-05

輸出結果:

input of BatchNorm Layer: 
 [[[[0.54340494 0.2783694  0.4245176 ]
   [0.84477615 0.00471886 0.12156912]
   [0.67074907 0.82585275 0.13670659]]

  [[0.5750933  0.89132196 0.20920213]
   [0.18532822 0.10837689 0.21969749]
   [0.9786238  0.8116832  0.17194101]]

  [[0.81622475 0.27407375 0.4317042 ]
   [0.9400298  0.81764936 0.33611196]
   [0.17541045 0.37283206 0.00568851]]]


 [[[0.25242636 0.7956625  0.01525497]
   [0.5988434  0.6038045  0.10514768]
   [0.38194343 0.03647606 0.89041156]]

  [[0.98092085 0.05994199 0.89054596]
   [0.5769015  0.7424797  0.63018394]
   [0.5818422  0.02043913 0.21002658]]

  [[0.5446849  0.76911515 0.25069523]
   [0.2858957  0.8523951  0.9750065 ]
   [0.8848533  0.35950786 0.59885895]]]]
output of BatchNorm Layer: 
 [[[[ 0.4126078  -0.46198368  0.02029109]
   [ 1.4071034  -1.3650038  -0.97940934]
   [ 0.832831    1.344658   -0.9294571 ]]

  [[ 0.2520175   1.2038351  -0.84927964]
   [-0.9211378  -1.1527538  -0.8176896 ]
   [ 1.4666051   0.96413004 -0.961432  ]]

  [[ 0.9541142  -0.9075856  -0.36629617]
   [ 1.37925     0.9590063  -0.6945517 ]
   [-1.2463869  -0.5684581  -1.8291974 ]]]


 [[[-0.5475932   1.2450331  -1.3302356 ]
   [ 0.5955492   0.6119205  -1.0335984 ]
   [-0.12019944 -1.2602081   1.5576957 ]]

  [[ 1.473519   -1.2985382   1.2014993 ]
   [ 0.25745988  0.7558342   0.41783488]
   [ 0.27233088 -1.4174379  -0.8467981 ]]

  [[ 0.02166975  0.79234385 -0.98786545]
   [-0.86699003  1.0783203   1.4993572 ]
   [ 1.1897788  -0.6142123   0.20769882]]]]
channel 0 of input data: 
 [[[0.54340494 0.2783694  0.4245176 ]
  [0.84477615 0.00471886 0.12156912]
  [0.67074907 0.82585275 0.13670659]]

 [[0.25242636 0.7956625  0.01525497]
  [0.5988434  0.6038045  0.10514768]
  [0.38194343 0.03647606 0.89041156]]]
std 0.418368607759, mean 0.303022772074, 
 output: 
 [[[ 0.41263014 -0.46200886  0.02029219]
  [ 1.4071798  -1.3650781  -0.9794626 ]
  [ 0.8328762   1.3447311  -0.92950773]]

 [[-0.54762304  1.2451009  -1.3303081 ]
  [ 0.5955816   0.61195374 -1.0336547 ]
  [-0.12020606 -1.2602768   1.5577804 ]]]
View Code

 


預測時使用BatchNorm

上面介紹了在訓練過程中使用BatchNorm對一批樣本進行歸一化的方法,但如果使用同樣的方法對需要預測的一批樣本進行歸一化,則預測結果會出現不確定性。

例如樣本A、樣本B作為一批樣本計算均值和方差,與樣本A、樣本C和樣本D作為一批樣本計算均值和方差,得到的結果一般來說是不同的。那么樣本A的預測結果就會變得不確定,這對預測過程來說是不合理的。解決方法是在訓練過程中將大量樣本的均值和方差保存下來,預測時直接使用保存好的值而不再重新計算。實際上,在BatchNorm的具體實現中,訓練時會計算均值和方差的移動平均值。在飛槳中,默認是采用如下方式計算:

saved_uB <- saved_uB * 0.9 + uB * (1-0.9)

saved_thedaB^2 <- savedthedaB^2 * 0.9 + thedaB^2 * (1-0.9)

在訓練過程的最開始將saved_uB,saved_thedaB^2 設置為0,每次輸入一批新的樣本,計算出uB,thedaB^2,然后通過上面的公式更新saved_uB,saved_thedaB^2,在訓練的過程中不斷的更新它們的值,並作為BatchNorm層的參數保存下來。預測的時候將會加載參數saved_uB,saved_thedaB^2,用他們來代替uB,thedaB^2(average,variance)

 


 

丟棄法(Dropout)

 

丟棄法(Dropout)是深度學習中一種常用的抑制過擬合的方法,其做法是在神經網絡學習過程中,隨機刪除一部分神經元。訓練時,隨機選出一部分神經元,將其輸出設置為0,這些神經元將不對外傳遞信號。

下圖是Dropout示意圖,左邊是完整的神經網絡,右邊是應用了Dropout之后的網絡結構。應用Dropout之后,會將標了×\times×的神經元從網絡中刪除,讓它們不向后面的層傳遞信號。在學習過程中,丟棄哪些神經元是隨機決定,因此模型不會過度依賴某些神經元,能一定程度上抑制過擬合。

 

在預測場景時,會向前傳遞所有神經元的信號,可能會引出一個新的問題:訓練時由於部分神經元被隨機丟棄了,輸出數據的總大小會變小了。比如:計算其L1范數會比不使用Dropout時變小,但是預測時卻沒有丟棄神經元,這將導致訓練和預測時數據的分布不一樣。為了解決這個問題,飛槳支持如下兩種方法:

  • 1 downgrade_in_infer

訓練時以比例rrr隨機丟棄一部分神經元,不向后傳遞它們的信號;預測時向后傳遞所有神經元的信號,但是將每個神經元上的數值乘以 (1−r)

  • 2 upscale_in_train

訓練時以比例rrr隨機丟棄一部分神經元,不向后傳遞它們的信號,但是將那些被保留的神經元上的數值除以 (1−r);預測時向后傳遞所有神經元的信號,不做任何處理。

在飛槳dropout API中,paddle.fluid.layers.dropout通過dropout_implementation參數來指定用哪種方式對神經元進行操作,dropout_implementation參數的可選值是'downgrade_in_infer'或'upscale_in_train',缺省值是'downgrade_in_infer'。

下面這段程序展示了經過dropout之后輸出數據的形式。

 

 1 # dropout操作
 2 import numpy as np
 3 
 4 import paddle
 5 import paddle.fluid as fluid
 6 
 7 # 設置隨機數種子,這樣可以保證每次運行結果一致
 8 np.random.seed(100)
 9 # 創建數據[N, C, H, W],一般對應卷積層的輸出
10 data1 = np.random.rand(2,3,3,3).astype('float32')
11 # 創建數據[N, K],一般對應全連接層的輸出
12 #reshape(-1,3)數組新的shape屬性應該要與原來的配套,如果等於-1的話,那么Numpy會根據剩下的維度計算出數組的另外一個shape屬性值。 
13 data2 = np.arange(1,13).reshape([-1, 3]).astype('float32')
14 # 使用dropout作用在輸入數據上
15 with fluid.dygraph.guard():
16     x1 = fluid.dygraph.to_variable(data1)
17     out1_1 = fluid.layers.dropout(x1, dropout_prob=0.5, is_test=False)
18     out1_2 = fluid.layers.dropout(x1, dropout_prob=0.5, is_test=True)
19 
20     x2 = fluid.dygraph.to_variable(data2)
21     out2_1 = fluid.layers.dropout(x2, dropout_prob=0.5, \
22                     dropout_implementation='upscale_in_train')
23     out2_2 = fluid.layers.dropout(x2, dropout_prob=0.5, \
24                     dropout_implementation='upscale_in_train', is_test=True)
25     
26     print('x1 {}, \n out1_1 \n {}, \n out1_2 \n {}'.format(data1, out1_1.numpy(),  out1_2.numpy()))
27     #print('x2 {}, \n out2_1 \n {}, \n out2_2 \n {}'.format(data2, out2_1.numpy(),  out2_2.numpy()))

結果輸出:

 

x1 [[[[0.54340494 0.2783694  0.4245176 ]
   [0.84477615 0.00471886 0.12156912]
   [0.67074907 0.82585275 0.13670659]]

  [[0.5750933  0.89132196 0.20920213]
   [0.18532822 0.10837689 0.21969749]
   [0.9786238  0.8116832  0.17194101]]

  [[0.81622475 0.27407375 0.4317042 ]
   [0.9400298  0.81764936 0.33611196]
   [0.17541045 0.37283206 0.00568851]]]


 [[[0.25242636 0.7956625  0.01525497]
   [0.5988434  0.6038045  0.10514768]
   [0.38194343 0.03647606 0.89041156]]

  [[0.98092085 0.05994199 0.89054596]
   [0.5769015  0.7424797  0.63018394]
   [0.5818422  0.02043913 0.21002658]]

  [[0.5446849  0.76911515 0.25069523]
   [0.2858957  0.8523951  0.9750065 ]
   [0.8848533  0.35950786 0.59885895]]]], 
 out1_1 
 [[[[0.         0.         0.        ]
   [0.84477615 0.00471886 0.12156912]
   [0.         0.         0.13670659]]

  [[0.5750933  0.89132196 0.20920213]
   [0.18532822 0.         0.21969749]
   [0.9786238  0.8116832  0.        ]]

  [[0.81622475 0.         0.        ]
   [0.9400298  0.         0.33611196]
   [0.17541045 0.37283206 0.00568851]]]


 [[[0.25242636 0.         0.01525497]
   [0.         0.         0.        ]
   [0.38194343 0.         0.        ]]

  [[0.         0.         0.        ]
   [0.         0.         0.        ]
   [0.         0.         0.        ]]

  [[0.5446849  0.76911515 0.25069523]
   [0.2858957  0.8523951  0.9750065 ]
   [0.8848533  0.35950786 0.        ]]]], 
 out1_2 
 [[[[0.27170247 0.1391847  0.2122588 ]
   [0.42238808 0.00235943 0.06078456]
   [0.33537453 0.41292638 0.0683533 ]]

  [[0.28754666 0.44566098 0.10460106]
   [0.09266411 0.05418845 0.10984875]
   [0.4893119  0.4058416  0.08597051]]

  [[0.40811238 0.13703687 0.2158521 ]
   [0.4700149  0.40882468 0.16805598]
   [0.08770522 0.18641603 0.00284425]]]


 [[[0.12621318 0.39783126 0.00762749]
   [0.2994217  0.30190226 0.05257384]
   [0.19097172 0.01823803 0.44520578]]

  [[0.49046043 0.02997099 0.44527298]
   [0.28845075 0.37123984 0.31509197]
   [0.2909211  0.01021957 0.10501329]]

  [[0.27234244 0.38455757 0.12534761]
   [0.14294785 0.42619756 0.48750326]
   [0.44242665 0.17975393 0.29942948]]]]
View Code

 


免責聲明!

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



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