深度解析Droupout與Batch Normalization


Droupout與Batch Normalization都是深度學習常用且基礎的訓練技巧了。本文將從理論和實踐兩個角度分布其特點和細節。

Droupout

2012年,Hinton在其論文中提出Dropout。當一個復雜的前饋神經網絡被訓練在小的數據集時,容易造成過擬合。為了防止過擬合,可以通過阻止特征檢測器的共同作用來提高神經網絡的性能。
Droupout是一種針對深度學習廣泛應用的正則化技術。在每次迭代時隨機關閉一些神經單元,隨着迭代的進行,由於其他神經元可能在任何時候都被關閉,因此神經元對其他特定神經元的激活變得不那么敏感。

Droupout

從數學上來說,神經網絡層訓練過程中使用的標准 Dropout 的行為可以被寫作:

\[y = f(\mathbf W \mathbf x) \circ \mathbf m, m_i \rightarrow Bernoulli(p) \]

其中 \(f(·)\)為激活函數,\(x\) 是該層的輸入,\(W\) 是該層的權值矩陣,\(y\)為該層的輸出,而 \(m\) 則為該層的 Dropout 掩膜(mask),mask 中每個元素為 1 的概率為 \(p\)。在測試階段,該層的輸出可以被寫作:

\[y = p f(\mathbf W \mathbf x) \]

Q&A

為什么說Dropout可以解決過擬合?

(1)取平均的作用。dropout掉不同的隱藏神經元就類似在訓練不同的網絡,隨機刪掉一半隱藏神經元導致網絡結構已經不同,整個dropout過程就相當於對很多個不同的神經網絡取平均。而不同的網絡產生不同的過擬合,一些互為“反向”的擬合相互抵消就可以達到整體上減少過擬合。

(2)減少神經元之間復雜的共適應關系: 因為dropout程序導致兩個神經元不一定每次都在一個dropout網絡中出現。這樣權值的更新不再依賴於有固定關系的隱含節點的共同作用,阻止了某些特征僅僅在其它特定特征下才有效果的情況 。迫使網絡去學習更加魯棒的特征 ,這些特征在其它的神經元的隨機子集中也存在。換句話說假如我們的神經網絡是在做出某種預測,它不應該對一些特定的線索片段太過敏感,即使丟失特定的線索,它也應該可以從眾多其它線索中學習一些共同的特征。從這個角度看dropout就有點像L1,L2正則,減少權重使得網絡對丟失特定神經元連接的魯棒性提高。

(3)Dropout類似於性別在生物進化中的角色:物種為了生存往往會傾向於適應這種環境,環境突變則會導致物種難以做出及時反應,性別的出現可以繁衍出適應新環境的變種,有效的阻止過擬合,即避免環境改變時物種可能面臨的滅絕。

類比於Bagging方法,Dropout可被認為是一種實用的大規模深度神經網絡的模型集成算法。這是由於傳統意義上的Bagging涉及多個模型的同時訓練與測試評估,當網絡與參數規模龐大時,這種集成方式需要消耗大量的運算時間與空間。Dropout在小批量級別上的操作,提供了一種輕量級的Bagging集成近似,能夠實現指數級數量神經網絡的訓練與評測。

Dropout缺點

明確定義的損失函數每一次迭代都會下降,而dropout每一次都會隨機刪除節點,也就是說每一次訓練的網絡都是不同的,損失函數不再被明確地定義,在某種程度上很難計算,我們失去了調試工具。

如何使用 Dropout

Dropout存在兩個版本:Vanilla Dropout 和 Inverted Dropout。(這里只對Inverted Dropout進行說明)
對於inverted dropout,在訓練階段期間對激活值進行縮放,而測試階段保持不變。這是因為當模型使用了dropout layer,訓練的時候只有占比為\(p\)的隱藏層單元參與訓練,那么在預測的時候,如果所有的隱藏層單元都需要參與進來,則得到的結果是訓練時的\(\frac{1}{p}\)倍 ,為了避免這種情況,就需要測試的時候將輸出結果乘以\(p\) 使下一層的輸入規模保持不變。而利用inverted dropout,我們可以在訓練的時候直接將dropout后留下的權重擴大\(\frac{1}{p}\)倍,這樣就可以使結果的scale保持不變,而在預測的時候也不用做額外的操作了。

ResNet為什么不用Dropout?

Dropout與BN不兼容;同時,BN在訓練過程對每個單個樣本的forward均引入多個樣本(Batch個)的統計信息,相當於自帶一定噪音,起到正則效果,所以也就基本消除了Dropout的必要。

Dropout實現

這里使用numpy實現了一個普通的神經網絡如何在訓練中,利用Dropout的keep_out進行前向和反向傳播。

import numpy as np
def forward_propagation_with_dropout(X, parameters, keep_prob=0.5):
    """
    Implements the forward propagation: LINEAR -> RELU + DROPOUT -> LINEAR -> RELU + DROPOUT -> LINEAR -> SIGMOID.
    """
    np.random.seed(1)

    # retrieve parameters
    W1 = parameters["W1"]
    b1 = parameters["b1"]
    W2 = parameters["W2"]
    b2 = parameters["b2"]
    W3 = parameters["W3"]
    b3 = parameters["b3"]

    Z1 = np.dot(W1, X) + b1
    A1 = relu(Z1)

    D1 = np.random.rand(A1.shape[0], A1.shape[1])
    D1 = (D1 < keep_prob)
    A1 = A1 * D1
    A1 = A1 / keep_prob

    Z2 = np.dot(W2, A1) + b2
    A2 = relu(Z2)
    D2 = np.random.rand(A2.shape[0], A1.shape[1])
    D2 = (D2 < keep_prob)
    A2 = A2 * D2
    A2 = A2 / keep_prob
    Z3 = np.dot(W3, A2) + b3
    A3 = sigmoid(Z3)

    cache = (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3)

    return A3, cache


# GRADED FUNCTION: backward_propagation_with_dropout
def backward_propagation_with_dropout(X, Y, cache, keep_prob):
    """
    Implements the backward propagation of our baseline model to which we added dropout.
    """
    m = X.shape[1]
    (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3) = cache

    dZ3 = A3 - Y
    dW3 = 1. / m * np.dot(dZ3, A2.T)
    db3 = 1. / m * np.sum(dZ3, axis=1, keepdims=True)
    dA2 = np.dot(W3.T, dZ3)

    dA2 *= D2
    dA2 = dA2 / keep_prob

    dZ2 = np.multiply(dA2, np.int64(A2 > 0))
    dW2 = 1. / m * np.dot(dZ2, A1.T)
    db2 = 1. / m * np.sum(dZ2, axis=1, keepdims=True)

    dA1 = np.dot(W2.T, dZ2)
    dA1 *= D1
    dA1 /= keep_prob

    dZ1 = np.multiply(dA1, np.int64(A1 > 0))
    dW1 = 1. / m * np.dot(dZ1, X.T)
    db1 = 1. / m * np.sum(dZ1, axis=1, keepdims=True)

    gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3, "dA2": dA2,
                 "dZ2": dZ2, "dW2": dW2, "db2": db2, "dA1": dA1,
                 "dZ1": dZ1, "dW1": dW1, "db1": db1}

    return gradients

Batch Normalization

Batch Normalization, 批標准化, 和普通的數據標准化類似, 是將分散的數據統一的一種做法, 也是優化神經網絡的一種方法.
在深度學習中,由於問題的復雜性,尤其是對深層神經網絡的訓練調參是困難且復雜的。在這個過程中,我們需要去嘗試不同的學習率、初始化參數方法(例如Xavier初始化)等方式來幫助我們的模型加速收斂。深度神經網絡之所以如此難訓練,其中一個重要原因就是網絡中層與層之間存在高度的關聯性與耦合性。在訓練中,網絡中層與層之間的關聯性會導致如下的狀況:隨着訓練的進行,網絡中的參數也隨着梯度下降在不停更新。一方面,當底層網絡中參數發生微弱變化時,由於每一層中的線性變換與非線性激活映射,這些微弱變化隨着網絡層數的加深而被放大(類似蝴蝶效應);另一方面,參數的變化導致每一層的輸入分布會發生改變,進而上層的網絡需要不停地去適應這些分布變化,使得我們的模型訓練變得困難。上述這一現象叫做Internal Covariate Shift

Batch Normalization的原論文作者給了Internal Covariate Shift一個較規范的定義:在深層網絡訓練的過程中,由於網絡中參數變化而引起內部結點數據分布發生變化的這一過程被稱作Internal Covariate Shift。

Internal Covariate Shift會帶來什么問題?
(1)上層網絡需要不停調整來適應輸入數據分布的變化,導致網絡學習速度的降低
(2)網絡的訓練過程容易陷入梯度飽和區,減緩網絡收斂速度
當我們在神經網絡中采用飽和激活函數(saturated activation function)時,例如sigmoid,tanh激活函數,很容易使得模型訓練陷入梯度飽和區(saturated regime)。對於激活函數梯度飽和問題,有兩種解決思路。第一種就是更為非飽和性激活函數,例如線性整流函數ReLU可以在一定程度上解決訓練進入梯度飽和區的問題。另一種思路是,我們可以讓激活函數的輸入分布保持在一個穩定狀態來盡可能避免它們陷入梯度飽和區,這也就是Normalization的思路。

那我們如何減緩Internal Covariate Shift?ICS產生的原因是由於參數更新帶來的網絡中每一層輸入值分布的改變,並且隨着網絡層數的加深而變得更加嚴重,因此我們可以通過固定每一層網絡輸入值的分布來對減緩ICS問題。
(1)白化(Whitening)。白化是對輸入數據分布進行變換,進而達到以下兩個目的:
第一,使得輸入特征分布具有相同的均值與方差。其中PCA白化保證了所有特征分布均值為0,方差為1;而ZCA白化則保證了所有特征分布均值為0,方差相同;
第二,去除特征之間的相關性。通過白化操作,我們可以減緩ICS的問題,進而固定了每一層網絡輸入分布,加速網絡訓練過程的收斂。
(2)Batch Normalization。白化過程計算成本太高,並且由於改變了網絡每一層的分布,因而改變了網絡層中本身數據的表達能力。底層網絡學習到的參數信息會被白化操作丟失掉。

在深度學習中,由於采用full batch的訓練方式對內存要求較大,且每一輪訓練時間過長;我們一般都會采用對數據做划分,用mini-batch對網絡進行訓練。因此,Batch Normalization也就在mini-batch的基礎上進行計算。
通過對每一個神經元輸出值減去一個batch的均值,除以方差,我們可以用更加簡化的方式來對數據進行規范化,即:

\[\mu_j = \frac{1}{m} \sum_{i=1}^m Z_j^{(i)} \\ \sigma_j^2 = \frac{1}{m} \sum_{i=1}^m (Z_j^{(i)}-\mu_j)^2 \\ \hat{Z_j}=\frac{Z_j - \mu_j}{ \sqrt{\sigma_j^2 + \epsilon}} \]

其中\(\epsilon\)是為了防止方差為0產生無效計算。

使得每一層的輸入每個特征的分布均值為0,方差為1。但這樣但卻導致了數據表達能力的缺失。也就是我們通過變換操作改變了原有數據的信息表達(representation ability of the network),使得底層網絡學習到的參數信息丟失。另一方面,通過讓每一層的輸入分布均值為0,方差為1,會使得輸入在經過sigmoid或tanh激活函數時,容易陷入非線性激活函數的線性區域。
因此,BN又引入了兩個可學習(learnable)的參數\(\gamma\)\(\beta\) 。這兩個參數的引入是為了恢復數據本身的表達能力,對規范化后的數據進行線性變換,即:

\[\check{Z_j}=\gamma_j \hat{Z_j} + \beta_j \]

特別地,當\(\gamma^2=\sigma^2,\beta=\mu\)時,可以實現等價變換(identity transform)並且保留了原始輸入特征的分布信息。
通過上面的步驟,我們就在一定程度上保證了輸入數據的表達能力。
以上就是整個Batch Normalization在模型訓練中的算法和思路。

補充: 在進行normalization的過程中,由於我們的規范化操作會對減去均值,因此,偏置項 \(b\) 可以被忽略掉或可以被置為0

總結一下,BN的作用與問題:
BN的作用:
(1)允許較大的學習率;
(2)減弱對初始化的強依賴性
(3)保持隱藏層中數值的均值、方差不變,讓數值更穩定,為后面網絡提供堅實的基礎;
(4)有輕微的正則化作用(相當於給隱藏層加入噪聲,類似Dropout)
BN存在的問題:
(1)每次是在一個batch上計算均值、方差,如果batch size太小,則計算的均值、方差不足以代表整個數據分布。
(2)batch size太大:會超過內存容量;需要跑更多的epoch,導致總訓練時間變長;會直接固定梯度下降的方向,導致很難更新。

Q&A

測試階段如何使用Batch Normalization?

利用BN訓練好模型后,我們保留了每組mini-batch訓練數據在網絡中每一層的 \(\mu_{batch}\)\(\sigma_{batch}^2\)
此時我們使用整個樣本的統計量來對Test數據進行歸一化,具體來說使用均值與方差的無偏估計:

\[\mu_{test} = E(\mu_{btach}) \\ \sigma_{test}^2 = \frac{m}{m-1}E(\sigma_{btach}^2) \\ \]

得到每個特征的均值與方差的無偏估計后,我們對test數據采用同樣的normalization方法:

\[BN(X_{test}) = \gamma \cdot \frac{X_{test}-\mu_{test}}{\sqrt{\sigma_{test}^2 + \epsilon}} + \beta \]

另外,除了采用整體樣本的無偏估計外。吳恩達在Coursera上的Deep Learning課程指出可以對train階段每個batch計算的mean/variance采用指數加權平均來得到test階段mean/variance的估計。

BN訓練時為什么不用全量訓練集的均值和方差呢?

因為用全量訓練集的均值和方差容易過擬合,對於BN,其實就是對每一批數據進行歸一化到一個相同的分布,而每一批數據的均值和方差會有一定的差別,而不是用固定的值,這個差別實際上能夠增加模型的魯棒性,也會在一定程度上減少過擬合。
也正是因此,BN一般要求將訓練集完全打亂,並用一個較大的batch值,否則,一個batch的數據無法較好得代表訓練集的分布,會影響模型訓練的效果。

Batch Normalization的優勢

Batch Normalization在實際工程中被證明了能夠緩解神經網絡難以訓練的問題,BN具有的優勢可以總結為以下幾點:
(1)BN使得網絡中每層輸入數據的分布相對穩定,加速模型學習速度
BN通過規范化與線性變換使得每一層網絡的輸入數據的均值與方差都在一定范圍內,使得后一層網絡不必不斷去適應底層網絡中輸入的變化,從而實現了網絡中層與層之間的解耦,允許每一層進行獨立學習,有利於提高整個神經網絡的學習速度。
(2)BN使得模型對網絡中的參數不那么敏感,簡化調參過程,使得網絡學習更加穩定
在神經網絡中,我們經常會謹慎地采用一些權重初始化方法(例如Xavier)或者合適的學習率來保證網絡穩定訓練。
當學習率設置太高時,會使得參數更新步伐過大,容易出現震盪和不收斂。但是使用BN的網絡將不會受到參數數值大小的影響。在使用Batch Normalization之后,抑制了參數微小變化隨着網絡層數加深被放大的問題,使得網絡對參數大小的適應能力更強,此時我們可以設置較大的學習率而不用過於擔心模型divergence的風險。
(3)BN允許網絡使用飽和性激活函數(例如sigmoid,tanh等),緩解梯度消失問題
在不使用BN層的時候,由於網絡的深度與復雜性,很容易使得底層網絡變化累積到上層網絡中,導致模型的訓練很容易進入到激活函數的梯度飽和區;通過normalize操作可以讓激活函數的輸入數據落在梯度非飽和區,緩解梯度消失的問題;另外通過自適應學習\(\gamma\)\(\beta\)又讓數據保留更多的原始信息。
(4)BN具有一定的正則化效果
在Batch Normalization中,由於我們使用mini-batch的均值與方差作為對整體訓練樣本均值與方差的估計,盡管每一個batch中的數據都是從總體樣本中抽樣得到,但不同mini-batch的均值與方差會有所不同,這就為網絡的學習過程中增加了隨機噪音,與Dropout通過關閉神經元給網絡訓練帶來噪音類似,在一定程度上對模型起到了正則化的效果。

Batch Normalization訓練注意事項

tf.layers.batch_normalization接口中training參數非常重要。當我們訓練時,要設置為True,保證在訓練過程中使用的是mini-batch的統計量進行normalization;在Inference階段,使用False,也就是使用總體樣本的無偏估計。另外,當self.use_batch_norm為True時,要使用tf.control_dependencies保證模型正常訓練。

Batch-normalized 應該放在非線性激活層的前面還是后面?

在BN的原始論文中,BN是放在非線性激活層前面的。但目前在實踐上,傾向於把BN放在ReLU后面。 Batch-Normalization可以視作對傳給隱藏層的輸入的normalization。BN層的作用機制也許是通過平滑隱藏層輸入的分布,幫助隨機梯度下降的進行,緩解隨機梯度下降權重更新對后續層的負面影響。因此,實際上,無論是放非線性激活之前,還是之后,也許都能發揮這個作用。只不過,取決於具體激活函數的不同,效果也許有一點差別(比如,對sigmoid和tanh而言,放非線性激活之前,也許順便還能緩解sigmoid/tanh的梯度衰減問題,而對ReLU而言,這個平滑作用經ReLU“扭曲”之后也許有所衰弱)。

BN和Dropout共同使用時會出現的問題

BN和Dropout單獨使用都能減少過擬合並加速訓練速度,但如果一起使用的話並不會產生1+1>2的效果,相反可能會得到比單獨使用更差的效果。
理解 Dropout 與 BN 之間沖突的關鍵是網絡狀態切換過程中存在神經方差的(neural variance)不一致行為。試想若有圖一中的神經響應 X,當網絡從訓練轉為測試時,Dropout 可以通過其隨機失活保留率(即 p)來縮放響應,並在學習中改變神經元的方差,而 BN 仍然維持 X 的統計滑動方差。這種方差不匹配可能導致數值不穩定(見下圖中的紅色曲線)。而隨着網絡越來越深,最終預測的數值偏差可能會累計,從而降低系統的性能。簡單起見,作者們將這一現象命名為「方差偏移」。事實上,如果沒有 Dropout,那么實際前饋中的神經元方差將與 BN 所累計的滑動方差非常接近(見下圖中的藍色曲線),這也保證了其較高的測試准確率。

BN和Dropout共同使用時會出現的問題

作者采用了兩種策略來探索如何打破這種局限。一個是在所有 BN 層后使用 Dropout,另一個就是修改 Dropout 的公式讓它對方差並不那么敏感,就是高斯Dropout。
第一個方案比較簡單,把Dropout放在所有BN層的后面就可以了,這樣就不會產生方差偏移的問題,但實則有逃避問題的感覺。
第二個方案來自Dropout原文里提到的一種高斯Dropout,是對Dropout形式的一種拓展。作者進一步拓展了高斯Dropout,提出了一個均勻分布Dropout,這樣做帶來了一個好處就是這個形式的Dropout(又稱為“Uout”)對方差的偏移的敏感度降低了,總得來說就是整體方差偏地沒有那么厲害了。

論文:Improving neural networks by preventing co-adaptation of feature detectors
論文:Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift
論文:Understanding the Disharmony between Dropout and Batch Normalization by Variance Shift
深度學習中Dropout原理解析
Batch Normalization原理與實戰
Batch-normalized 應該放在非線性激活層的前面還是后面?
BN和Dropout在訓練和測試時的差別


免責聲明!

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



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