人人都能懂的機器學(xué)習(xí)——訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)——批標(biāo)準(zhǔn)化

批標(biāo)準(zhǔn)化

盡管使用He初始化和ELU(或者其他ReLU的變體)可以極大地減少在訓(xùn)練之初梯度不穩(wěn)的問題逼庞,但是無法保證隨著訓(xùn)練的進(jìn)行赞庶,梯度不穩(wěn)的問題不會再次發(fā)生车吹。

Sergey Ioffe和Christian Szegedy在2015年發(fā)表了文章当辐,提出了批標(biāo)準(zhǔn)化的技術(shù)(BN)黎棠,該技術(shù)解決了梯度消失和爆炸問題晋渺。這個技術(shù)是在模型每層的激活函數(shù)之前或之后增加一個操作,將每一個輸入歸零并歸一化脓斩,并且每一層都使用兩個參數(shù)向量對結(jié)果進(jìn)行縮放和移動木西。換句話說,這個操作使得模型學(xué)習(xí)每一層輸入的最優(yōu)范圍和均值俭厚。在很多情況下户魏,如果你在神經(jīng)網(wǎng)絡(luò)的第一層添加了一層批標(biāo)準(zhǔn)化層,那么就不需要再將訓(xùn)練集進(jìn)行標(biāo)準(zhǔn)化了(也就是用StandardScaler)挪挤,批標(biāo)準(zhǔn)化會處理好標(biāo)準(zhǔn)化的問題(不過批標(biāo)準(zhǔn)化層一次將一批的輸入進(jìn)行歸零和縮放叼丑,并且還可以對每個輸入特征進(jìn)行縮放移動)。

為了將輸入數(shù)據(jù)標(biāo)準(zhǔn)化扛门,算法通過計算當(dāng)前批次的平均值和標(biāo)準(zhǔn)差鸠信,從而評估每個輸入的平均值和標(biāo)準(zhǔn)差。整個操作過程見下式:

\mu_{B}=\frac{1}{m_{B}}\sum\limits_{i=1}^{m_{B}}x_{i} \\ \sigma_{B}^{2}=\frac{1}{m_{B}}\sum\limits_{i=1}^{m_{B}}(x_{i}-\mu_{B})^{2} \\ \vec x_{i}=\frac{x_{i}-\mu_{B}}{\sqrt{\sigma_{B}^{2}+\epsilon}} \\ z_{i}=\gamma \bigotimes \vec x_{i}+\beta=BN_{\gamma,\beta(x_{i})}

上式中:

  • μ_B 為輸入的平均向量论寨, 通過對整個批次B計算得出的(每個輸入都包含一個平均值)
  • σ_B 為輸入的標(biāo)準(zhǔn)差向量星立,同樣是通過整個批次B計算得出的(每個輸入都包含一個標(biāo)準(zhǔn)差)
  • m_B 是該批次中的實例數(shù)量
  • x_i 是實例i以0點居中并標(biāo)準(zhǔn)化后的輸入向量
  • γ 是神經(jīng)層的輸出縮放參數(shù)(對每個輸入都有一個縮放參數(shù))
  • 圈乘 表示同位矩陣元素對應(yīng)相乘(每個輸入與它對應(yīng)的輸出縮放參數(shù)相乘)
  • β是神經(jīng)層的輸出移動(偏移)參數(shù)向量(對應(yīng)每個輸入都有一個偏移參數(shù))。每個輸入都通過對應(yīng)的移動參數(shù)進(jìn)行偏移葬凳。
  • epsilon 是一個很小的數(shù)绰垂,用來避免被0除(一般是10 -5)。它被稱為平滑項火焰。
  • z_i 是批標(biāo)準(zhǔn)化的輸出劲装,是將輸入縮放和移動后的結(jié)果

那么在訓(xùn)練過程中,批標(biāo)準(zhǔn)化操作將其輸入標(biāo)準(zhǔn)化,并重新縮放和移動它們占业。很好绒怨!但到了測試過程呢?事情就不是那么簡單了谦疾。實際上南蹂,在測試時我們可能需要對單個實例進(jìn)行預(yù)測,而不是批次預(yù)測:在這種情況下念恍,我們將無法計算每個輸入的平均值和標(biāo)準(zhǔn)差六剥。此外,即使我們有一批實例樊诺,該批次的數(shù)量也可能不夠仗考,或者實例之間可能不是獨立、同分布的(IID)词爬,那么計算該批次的統(tǒng)計信息是不可靠的秃嗜。一種解決方法是等到訓(xùn)練結(jié)束,然后通過神經(jīng)網(wǎng)絡(luò)運行整個訓(xùn)練集顿膨,計算批標(biāo)準(zhǔn)化層每個輸入的均值和標(biāo)準(zhǔn)差锅锨。在進(jìn)行預(yù)測時,可以使用這些“最終”輸入的平均值和標(biāo)準(zhǔn)偏差恋沃,而不是每批的輸入平均值和標(biāo)準(zhǔn)偏差必搞。然而,大多數(shù)成熟深度學(xué)習(xí)庫中的批標(biāo)準(zhǔn)化算法會在訓(xùn)練期間囊咏,通過層的輸入均值和標(biāo)準(zhǔn)差的移動平均來估計這些”最終“數(shù)據(jù)恕洲。這也是在使用BatchNormalization層時,Keras自動幫你執(zhí)行的操作梅割。綜上所述霜第,在每一個批標(biāo)準(zhǔn)化層中,神經(jīng)網(wǎng)絡(luò)會學(xué)習(xí)四個參數(shù)向量:通過正常的反向傳播算法户辞,神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)γ(輸出的縮放向量)和β(輸出偏移向量)泌类;使用指數(shù)移動平均,估計出μ(最終輸入平均向量)和σ(最終輸入標(biāo)準(zhǔn)差向量)底燎。請注意刃榨,μ和σ是在訓(xùn)練期間計算出的,但在訓(xùn)練神經(jīng)網(wǎng)絡(luò)期間 根本不使用它們双仍,只在訓(xùn)練完成之后才使用(用以替換上面公式中的批輸入平均值和標(biāo)準(zhǔn)差)枢希。

Ioffe和Szegedy證明了批標(biāo)準(zhǔn)化顯著地改善了所有他們測試的深度神經(jīng)網(wǎng)絡(luò),并對ImageNet分類任務(wù)中的表現(xiàn)有了極大的提高(ImageNet是一個巨大的已分類的圖片數(shù)據(jù)庫朱沃,在評估計算機視覺系統(tǒng)中有著廣泛的應(yīng)用)晴玖。梯度消失問題幾乎沒有了,甚至可以使用一些飽和激活函數(shù)比如tanh甚至sigmoid函數(shù)。并且神經(jīng)網(wǎng)絡(luò)對權(quán)重初始化也不再敏感呕屎。作者還嘗試使用了較大的學(xué)習(xí)率,極大地加速了訓(xùn)練過程敬察。特別地秀睛,他們指出:

應(yīng)用于最新的圖片分類模型,批標(biāo)準(zhǔn)化少用了14次的訓(xùn)練次數(shù)并達(dá)到了相同的準(zhǔn)確度莲祸,并遠(yuǎn)遠(yuǎn)超過了原模型的表現(xiàn)蹂安。使用批標(biāo)準(zhǔn)化的組合神經(jīng)網(wǎng)絡(luò),我們提高了最佳的ImageNet分類結(jié)果:達(dá)到4.9% top-5 驗證誤差(4.8%測試誤差)锐帜,超過了人類分類的準(zhǔn)確度田盈。

最后,批標(biāo)準(zhǔn)化甚至能夠發(fā)揮正則化器的作用缴阎,較少了對其他正則化技術(shù)的需求(比如dropout技術(shù)允瞧,我們在未來的文章中會提到)。

不過批標(biāo)準(zhǔn)化技術(shù)確實增加了模型的復(fù)雜度(盡管它不再需要將輸入函數(shù)標(biāo)準(zhǔn)化了)蛮拔。另外還有一個運行時間的缺點:在進(jìn)行預(yù)測時述暂,由于在每一層都需要進(jìn)行額外的計算量,所以預(yù)測的速度會變慢建炫。當(dāng)然畦韭,我們是可以在訓(xùn)練完成之后,去掉批標(biāo)準(zhǔn)化層肛跌,并且更新前一層的權(quán)重和偏差艺配,使得上一層能夠直接輸出合適的縮放和偏移結(jié)果。換句話說衍慎,我們可以將批標(biāo)準(zhǔn)化層直接與前一層結(jié)合起來转唉。比如說,如果前一層計算了:

XW+b

然后批標(biāo)準(zhǔn)化層計算了:

\gamma \bigotimes (XW+b-\mu)/\sigma+\beta

上式暫時忽略了分母中的平滑項ipsilong西饵。那么我們可以設(shè):

W' = \gamma \bigotimes W/\sigma \\ b′ = \gamma \bigotimes (b - \mu)/\sigma + \beta

那么方程就可以簡化為:

XW'+b'

那么酝掩,我們將前一層的權(quán)重和偏差替換成新的權(quán)重和偏差,那么我們就可以丟掉批標(biāo)準(zhǔn)化層了(TFLite的優(yōu)化器會自動完成這個步驟眷柔,我們會在未來的文章中展示)期虾。

你可能會發(fā)現(xiàn),使用批標(biāo)準(zhǔn)化后驯嘱,訓(xùn)練的速度變慢了镶苞,因為每過一遍訓(xùn)練集都需要更大的計算量。但使用批標(biāo)準(zhǔn)化后鞠评,模型的收斂速度大大加快茂蚓,所以模型訓(xùn)練達(dá)到一定性能所需的訓(xùn)練周期次數(shù)減少了。總的來說聋涨,wall time一般是減少的(所謂wall time就是用墻上的鐘測量的時間)晾浴。

用Keras實現(xiàn)批標(biāo)準(zhǔn)化

跟Keras其他的功能實現(xiàn)一樣,實現(xiàn)批標(biāo)準(zhǔn)化的操作非常簡單牍白。只需要在每個隱藏層的激活函數(shù)前面或者后面增加一個BatchNormalization層就可以了脊凰。甚至還可以選擇在模型的第一層后加入批標(biāo)準(zhǔn)化層。比如茂腥,下面這個模型在每個隱藏層后增加了批標(biāo)準(zhǔn)化層狸涌,并在第一層之后也加入了批標(biāo)準(zhǔn)化層(在將輸入圖像展平之后):

model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(300, activation="elu", kernel_initializer="he_normal"),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(100, activation="elu", kernel_initializer="he_normal"),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(10, activation="softmax")
])

這就是所有我們需要做的事情了!在這個例子里最岗,模型只有兩個隱藏層帕胆。所以批標(biāo)準(zhǔn)化應(yīng)該不會有很大的作用,但是在更深的神經(jīng)網(wǎng)絡(luò)里般渡,它會發(fā)揮巨大的作用懒豹。

讓我們來看一下模型的總結(jié):

>>> model.summary()
Model: "sequential_3"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
flatten_3 (Flatten) (None, 784) 0
_________________________________________________________________
batch_normalization_v2 (Batc (None, 784) 3136
_________________________________________________________________
dense_50 (Dense) (None, 300) 235500
_________________________________________________________________
batch_normalization_v2_1 (Ba (None, 300) 1200
_________________________________________________________________
dense_51 (Dense) (None, 100) 30100
_________________________________________________________________
batch_normalization_v2_2 (Ba (None, 100) 400
_________________________________________________________________
dense_52 (Dense) (None, 10) 1010
=================================================================
Total params: 271,346
Trainable params: 268,978
Non-trainable params: 2,368

可以看到,每個BN層對每個輸入神經(jīng)元加入了四個參數(shù):γ诊杆,β歼捐,μ和σ(比如第一個BN層加入了3136個參數(shù),即4X784)晨汹。后兩個參數(shù)裙戏,即μ和σ奶赔,為移動平均值;它們不隨著反向傳播而變化,所以Keras稱之為“Non-trainable”狈惫。但是實際上它們是根據(jù)訓(xùn)練數(shù)據(jù)估計出來的平均值憔古,所以其實是可以訓(xùn)練的皂甘,但是這里Keras中的不可訓(xùn)練僅僅是指不受反向傳播的影響(如果你將BN層參數(shù)的總數(shù)相加恋追,3136+1200+400,然后除以2曙聂,就會得到2368晦炊,即模型中的Non-trainable參數(shù)的總數(shù))。

我們再來詳細(xì)看一下第一層BN層的參數(shù)宁脊,兩個是可訓(xùn)練的断国,兩個是不可訓(xùn)練的:

>>> [(var.name, var.trainable) for var in model.layers[1].variables]
[('batch_normalization_v2/gamma:0', True),
('batch_normalization_v2/beta:0', True),
('batch_normalization_v2/moving_mean:0', False),
('batch_normalization_v2/moving_variance:0', False)]

當(dāng)我們在Keras中創(chuàng)建BN層時,它還會創(chuàng)建兩個操作榆苞,Keras將在訓(xùn)練期間每次迭代中調(diào)用它們稳衬。這兩個操作會更新移動平均值。既然我們的Keras使用的是TensorFlow后端坐漏,那么這兩個操作為TensorFlow操作:

>>> model.layers[1].updates
[<tf.Operation 'cond_2/Identity' type=Identity>,
<tf.Operation 'cond_3/Identity' type=Identity>]

批標(biāo)準(zhǔn)化的論文作者主張應(yīng)在激活函數(shù)之前添加BN層薄疚,而不是在激活函數(shù)之后(跟我們上面的模型結(jié)構(gòu)一樣)碧信。但是研究人員對此有一些爭論,因為這似乎取決于具體的任務(wù)街夭。那么這就是在實際操作過程中可以試驗的部分砰碴,看哪種模型架構(gòu)在你的數(shù)據(jù)集中表現(xiàn)更好。但需要在激活函數(shù)之前加入BN層莱坎,就必須將激活函數(shù)從隱藏層中剝離出來衣式,然后在BN層之后單獨加一個激活層。另外檐什,由于BN層為每個輸入都增加了一個偏移參數(shù),那么就可以將上一層的偏離項刪除(只要在創(chuàng)建隱藏層時輸入use_bias=False即可):

model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(300, kernel_initializer="he_normal", use_bias=False),
    keras.layers.BatchNormalization(),
    keras.layers.Activation("elu"),
    keras.layers.Dense(100, kernel_initializer="he_normal", use_bias=False),
    keras.layers.BatchNormalization(),
    keras.layers.Activation("elu"),
    keras.layers.Dense(10, activation="softmax")
])

BatchNormalization層有一些可調(diào)的參數(shù)弱卡。BN層的默認(rèn)參數(shù)通常沒有什么問題乃正,但是有些情況下可能需要調(diào)整一下動量(momentum)。這個超參是使BN層更新指數(shù)移動平均:當(dāng)輸入一個新的值v婶博,則BN使用下方的公式更新移動平均值:

\overline{v}=\overline{v}\times{momentum}+v\times{(1-momentum)}

一個合適的momentum值通常接近于1瓮具,比如0.9,0.99凡人,或者0.999(當(dāng)數(shù)據(jù)量較大且單批量較小時名党,可以加更多的9)

另一個重要的超參數(shù)是axis:它會確定標(biāo)準(zhǔn)化哪個軸。這個參數(shù)的默認(rèn)值為-1挠轴,也就是默認(rèn)情況下標(biāo)準(zhǔn)化最后一個軸(使用在其他軸上計算的平均值和標(biāo)準(zhǔn)差)传睹。舉個例子,當(dāng)輸入值為2D時(也就是岸晦,批數(shù)據(jù)的形狀為[批數(shù)量欧啤,特征數(shù)]),那就意味著每個輸入特征將會基于批次中所有的實例計算出來的平均值和標(biāo)準(zhǔn)差進(jìn)行標(biāo)準(zhǔn)化處理启上。例如上述代碼示例中邢隧,第一個BN層將784個輸入特性各自獨立地標(biāo)準(zhǔn)化(縮放和移動)。如果我們將第一個BN層移動到Flatten層之前冈在,那么該BN層的輸入數(shù)據(jù)為3D結(jié)構(gòu)倒慧,數(shù)據(jù)形狀為[批數(shù)量,高包券,寬]纫谅。鑒于輸入的結(jié)構(gòu)為[28,28]兴使,那么BN層就會計算28個平均值和28個標(biāo)準(zhǔn)差(針對數(shù)據(jù)的最后一個軸系宜,寬,也就是針對圖片中的每一列像素單元发魄,基于該批次中的所有實例的該列中的每一行像素值)盹牧,并對處于該列的所有像素值使用相同的平均值和標(biāo)準(zhǔn)差進(jìn)行標(biāo)準(zhǔn)化俩垃。那么也就會計算出28個縮放參數(shù)和28個移動參數(shù)。但是如果你想對784個像素獨立進(jìn)行標(biāo)準(zhǔn)化處理汰寓,那么就可以將超參數(shù)axis設(shè)置為:axis=[1,2]口柳。

之前已經(jīng)提到過,BN層在訓(xùn)練中和訓(xùn)練完成后所進(jìn)行的計算是不同的:在訓(xùn)練中有滑,它計算并使用該批次的統(tǒng)計信息跃闹;在訓(xùn)練后,它使用“最終”統(tǒng)計信息(比如說移動平均值的最終值)毛好。我們來簡單看一下源碼示例望艺,來看看Keras是如何做到這一功能的:

class BatchNormalization(keras.layers.Layer):
    [...]
    def call(self, inputs, training=None):
        [...]

call()方法中有一個training參數(shù),這個參數(shù)默認(rèn)為None肌访,但是fit()方法在訓(xùn)練過程中找默,會將該training參數(shù)設(shè)為1。我們在建立自定義層時也可以使用類似的方法吼驶,如果我們希望自定義層在訓(xùn)練和測試時實現(xiàn)不同的功能惩激,就需要在call()方法中增加一個training參數(shù),然后用它來決定計算規(guī)則蟹演。

批標(biāo)準(zhǔn)化已經(jīng)成為了深度神經(jīng)網(wǎng)絡(luò)中最常用的層之一风钻,常常默認(rèn)在每個層之后都加一個批標(biāo)準(zhǔn)化層,所以一般會在神經(jīng)網(wǎng)絡(luò)架構(gòu)圖中將其忽略酒请。但是Hongyi Zhang等人發(fā)表的《固定更新初始化:不帶標(biāo)準(zhǔn)化的殘差學(xué)習(xí)》一文可能會改變這一想法:作者表示骡技,通過一種新的固定更新權(quán)值初始化技術(shù),可以成功訓(xùn)練一個不帶批標(biāo)準(zhǔn)化的非常深的神經(jīng)網(wǎng)絡(luò)(10000層0龈浮)哮兰,并在復(fù)雜的圖像分類任務(wù)中表現(xiàn)出最出色的性能。但由于這是前沿研究苟弛,我們可能需要更多的研究論證才可以徹底放棄批標(biāo)準(zhǔn)化技術(shù)喝滞。

本篇文章向大家介紹了解決梯度問題的一個非常常用的技術(shù)——批標(biāo)準(zhǔn)化。之后的文章將再向大家介紹一種解決梯度爆炸問題的方法膏秫,梯度裁剪右遭。之后,我們將一起學(xué)習(xí)預(yù)訓(xùn)練模型與遷移學(xué)習(xí)缤削。

敬請期待吧窘哈!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市亭敢,隨后出現(xiàn)的幾起案子滚婉,更是在濱河造成了極大的恐慌,老刑警劉巖帅刀,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件让腹,死亡現(xiàn)場離奇詭異远剩,居然都是意外死亡,警方通過查閱死者的電腦和手機骇窍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進(jìn)店門瓜晤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人腹纳,你說我怎么就攤上這事痢掠。” “怎么了嘲恍?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵足画,是天一觀的道長。 經(jīng)常有香客問我佃牛,道長锌云,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任吁脱,我火速辦了婚禮,結(jié)果婚禮上彬向,老公的妹妹穿的比我還像新娘兼贡。我一直安慰自己,他們只是感情好娃胆,可當(dāng)我...
    茶點故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布遍希。 她就那樣靜靜地躺著,像睡著了一般里烦。 火紅的嫁衣襯著肌膚如雪凿蒜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天胁黑,我揣著相機與錄音废封,去河邊找鬼。 笑死丧蘸,一個胖子當(dāng)著我的面吹牛漂洋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播力喷,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼刽漂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了弟孟?” 一聲冷哼從身側(cè)響起贝咙,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拂募,沒想到半個月后庭猩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體窟她,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年眯娱,在試婚紗的時候發(fā)現(xiàn)自己被綠了礁苗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,444評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡徙缴,死狀恐怖试伙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情于样,我是刑警寧澤疏叨,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站穿剖,受9級特大地震影響蚤蔓,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜糊余,卻給世界環(huán)境...
    茶點故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一秀又、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧贬芥,春花似錦吐辙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至威沫,卻和暖如春贤惯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背棒掠。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工孵构, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人句柠。 一個月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓浦译,卻偏偏與公主長得像,于是被迫代替她去往敵國和親溯职。 傳聞我的和親對象是個殘疾皇子精盅,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,455評論 2 359