1. 背景
本篇博文主要講解2015年深度學(xué)習(xí)領(lǐng)域孔厉,非常值得學(xué)習(xí)的一篇文獻(xiàn):《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》搀菩,這個算法目前已經(jīng)被大量的應(yīng)用坡氯,最新的文獻(xiàn)算法很多都會引用這個算法,進(jìn)行網(wǎng)絡(luò)訓(xùn)練,可見其強大之處非同一般啊。
近年來深度學(xué)習(xí)捷報連連、聲名鵲起撕氧,隨機梯度下架成了訓(xùn)練深度網(wǎng)絡(luò)的主流方法。盡管隨機梯度下降法對于訓(xùn)練深度網(wǎng)絡(luò)簡單高效喇完,但是它有個毛病伦泥,就是需要我們?nèi)藶榈娜ミx擇參數(shù),比如學(xué)習(xí)率锦溪、參數(shù)初始化不脯、權(quán)重衰減系數(shù)、Drop out比例等刻诊。這些參數(shù)的選擇對訓(xùn)練結(jié)果至關(guān)重要防楷,以至于我們很多時間都浪費在這些的調(diào)參上。那么學(xué)完這篇文獻(xiàn)之后则涯,你可以不需要那么刻意的慢慢調(diào)整參數(shù)复局。BN算法(Batch Normalization)其強大之處如下:
- 實際上深度網(wǎng)絡(luò)中每一層的學(xué)習(xí)率是不一樣的,一般為了網(wǎng)絡(luò)能夠正確的收斂粟判、損失函數(shù)的值能夠有效的下降亿昏,常常將學(xué)習(xí)率設(shè)為所有層中學(xué)習(xí)率最小的那個值。但是 Batch Normalization 對每層數(shù)據(jù)規(guī)范化后档礁,這個最低學(xué)習(xí)率就會被提高角钩,所以網(wǎng)絡(luò)訓(xùn)練時可以設(shè)置較高的初始學(xué)習(xí)率,加快收斂。
- 你再也不用去理會過擬合中drop out彤断、L2正則項參數(shù)的選擇問題野舶,采用BN算法后易迹,可以選擇更小的L2正則約束參數(shù)了宰衙,因為BN具有提高網(wǎng)絡(luò)泛化能力的特性;
- 再也不需要使用使用局部響應(yīng)歸一化層了(局部響應(yīng)歸一化是Alexnet網(wǎng)絡(luò)用到的方法睹欲,搞視覺的估計比較熟悉)供炼,因為BN本身就是一個歸一化網(wǎng)絡(luò)層;
- 可以把訓(xùn)練數(shù)據(jù)徹底打亂(防止每批訓(xùn)練的時候窘疮,某一個樣本都經(jīng)常被挑選到袋哼,文獻(xiàn)說這個可以提高1%的精度,這句話我也是百思不得其解罢⑸馈)涛贯。
開始講解算法前,先來思考一個問題:我們知道在神經(jīng)網(wǎng)絡(luò)訓(xùn)練開始前蔚出,都要對輸入數(shù)據(jù)做一個歸一化處理弟翘,那么具體為什么需要歸一化呢?歸一化后有什么好處呢骄酗?原因在于神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)過程本質(zhì)就是為了學(xué)習(xí)數(shù)據(jù)分布稀余,一旦訓(xùn)練數(shù)據(jù)與測試數(shù)據(jù)的分布不同,那么網(wǎng)絡(luò)的泛化能力也大大降低趋翻;另外一方面睛琳,一旦每批訓(xùn)練數(shù)據(jù)的分布各不相同(batch 梯度下降),那么網(wǎng)絡(luò)就要在每次迭代都去學(xué)習(xí)適應(yīng)不同的分布踏烙,這樣將會大大降低網(wǎng)絡(luò)的訓(xùn)練速度师骗,這也正是為什么我們需要對數(shù)據(jù)都要做一個歸一化預(yù)處理的原因。
對于深度網(wǎng)絡(luò)的訓(xùn)練是一個復(fù)雜的過程讨惩,只要網(wǎng)絡(luò)的前面幾層發(fā)生微小的改變丧凤,那么后面幾層就會被累積放大下去。一旦網(wǎng)絡(luò)某一層的輸入數(shù)據(jù)的分布發(fā)生改變步脓,那么這一層網(wǎng)絡(luò)就需要去適應(yīng)學(xué)習(xí)這個新的數(shù)據(jù)分布愿待,所以如果訓(xùn)練過程中,訓(xùn)練數(shù)據(jù)的分布一直在發(fā)生變化靴患,那么將會影響網(wǎng)絡(luò)的訓(xùn)練速度仍侥。
我們知道網(wǎng)絡(luò)一旦train起來,那么參數(shù)就要發(fā)生更新鸳君,除了輸入層的數(shù)據(jù)外(因為輸入層數(shù)據(jù)农渊,我們已經(jīng)人為的為每個樣本歸一化),后面網(wǎng)絡(luò)每一層的輸入數(shù)據(jù)分布是一直在發(fā)生變化的或颊,因為在訓(xùn)練的時候砸紊,前面層訓(xùn)練參數(shù)的更新將導(dǎo)致后面層輸入數(shù)據(jù)分布的變化传于。以網(wǎng)絡(luò)第二層為例:網(wǎng)絡(luò)的第二層輸入,是由第一層的參數(shù)和input計算得到的醉顽,而第一層的參數(shù)在整個訓(xùn)練過程中一直在變化沼溜,因此必然會引起后面每一層輸入數(shù)據(jù)分布的改變。我們把網(wǎng)絡(luò)中間層在訓(xùn)練過程中游添,數(shù)據(jù)分布的改變稱之為:“Internal Covariate Shift”系草。Paper所提出的算法,就是要解決在訓(xùn)練過程中唆涝,中間層數(shù)據(jù)分布發(fā)生改變的情況找都,于是就有了Batch Normalization,這個牛逼算法的誕生廊酣。
2. BN(Batch Normalization)算法原理
2.1 BN概述
就像激活函數(shù)層能耻、卷積層、全連接層亡驰、池化層一樣晓猛,BN(Batch Normalization)也屬于網(wǎng)絡(luò)的一層。在前面我們提到網(wǎng)絡(luò)除了輸出層外隐解,其它層因為低層網(wǎng)絡(luò)在訓(xùn)練的時候更新了參數(shù)鞍帝,而引起后面層輸入數(shù)據(jù)分布的變化。這個時候我們可能就會想煞茫,如果在每一層輸入的時候帕涌,再加個預(yù)處理操作那該有多好啊,比如網(wǎng)絡(luò)第三層輸入數(shù)據(jù)X3(X3表示網(wǎng)絡(luò)第三層的輸入數(shù)據(jù))把它歸一化至:均值0续徽、方差為1蚓曼,然后再輸入第三層計算,這樣我們就可以解決前面所提到的“Internal Covariate Shift”的問題了钦扭。
而事實上纫版,paper的算法本質(zhì)原理就是這樣:在網(wǎng)絡(luò)的每一層輸入的時候,又插入了一個歸一化層客情,也就是先做一個歸一化處理其弊,然后再進(jìn)入網(wǎng)絡(luò)的下一層。 不過文獻(xiàn)歸一化層膀斋,可不像我們想象的那么簡單梭伐,它是一個可學(xué)習(xí)、有參數(shù)的網(wǎng)絡(luò)層仰担。既然說到數(shù)據(jù)預(yù)處理糊识,下面就先來復(fù)習(xí)一下最強的預(yù)處理方法:白化。
2.2 預(yù)處理操作選擇
說到神經(jīng)網(wǎng)絡(luò)輸入數(shù)據(jù)預(yù)處理,最好的算法莫過于白化預(yù)處理赂苗。然而白化計算量太大了愉耙,很不劃算,還有就是白化不是處處可微的拌滋,所以在深度學(xué)習(xí)中朴沿,其實很少用到白化。經(jīng)過白化預(yù)處理后鸠真,數(shù)據(jù)滿足條件:a悯仙、特征之間的相關(guān)性降低龄毡,這個就相當(dāng)于pca吠卷;b、數(shù)據(jù)均值沦零、標(biāo)準(zhǔn)差歸一化祭隔,也就是使得每一維特征均值為0,標(biāo)準(zhǔn)差為1路操。 如果數(shù)據(jù)特征維數(shù)比較大疾渴,要進(jìn)行PCA,也就是實現(xiàn)白化的第1個要求屯仗,是需要計算特征向量搞坝,計算量非常大,于是為了簡化計算魁袜,作者忽略了第1個要求桩撮,僅僅使用了下面的公式進(jìn)行預(yù)處理,也就是近似白化預(yù)處理:
公式簡單粗糙峰弹,但是依舊很牛逼店量。因此后面我們也將用這個公式,對某一個層網(wǎng)絡(luò)的輸入數(shù)據(jù)做一個歸一化處理鞠呈。需要注意的是融师,我們訓(xùn)練過程中采用batch 隨機梯度下降,上面的E(xk)指的是每一批訓(xùn)練數(shù)據(jù)神經(jīng)元xk的平均值蚁吝;然后分母就是每一批數(shù)據(jù)神經(jīng)元xk激活度的一個標(biāo)準(zhǔn)差了旱爆。
3. BN算法實現(xiàn)
3.1 算法流程
經(jīng)過前面簡單介紹,這個時候可能我們會想當(dāng)然的以為:好像很簡單的樣子窘茁,不就是在網(wǎng)絡(luò)中間層數(shù)據(jù)做一個歸一化處理嘛怀伦,這么簡單的想法,為什么之前沒人用呢庙曙?然而其實實現(xiàn)起來并不是那么簡單的空镜。其實如果是僅僅使用上面的歸一化公式,對網(wǎng)絡(luò)某一層A的輸出數(shù)據(jù)做歸一化,然后送入網(wǎng)絡(luò)下一層B吴攒,這樣是會影響到本層網(wǎng)絡(luò)A所學(xué)習(xí)到的特征的张抄。打個比方,比如我網(wǎng)絡(luò)中間某一層學(xué)習(xí)到特征數(shù)據(jù)本身就分布在S型激活函數(shù)的兩側(cè)洼怔,你強制把它給我歸一化處理署惯、標(biāo)準(zhǔn)差也限制在了1,把數(shù)據(jù)變換成分布于s函數(shù)的中間部分镣隶,這樣就相當(dāng)于我這一層網(wǎng)絡(luò)所學(xué)習(xí)到的特征分布被你搞壞了极谊,這可怎么辦?于是文獻(xiàn)使出了一招驚天地泣鬼神的招式:變換重構(gòu)安岂,引入了可學(xué)習(xí)參數(shù)γ轻猖、β,這就是算法關(guān)鍵之處:
每一個神經(jīng)元xk都會有一對這樣的參數(shù)γ域那、β咙边。這樣其實當(dāng):
是可以恢復(fù)出原始的某一層所學(xué)到的特征的。因此我們引入了這個可學(xué)習(xí)重構(gòu)參數(shù)γ次员、β败许,讓我們的網(wǎng)絡(luò)可以學(xué)習(xí)恢復(fù)出原始網(wǎng)絡(luò)所要學(xué)習(xí)的特征分布。最后Batch Normalization網(wǎng)絡(luò)層的前向傳導(dǎo)過程公式就是:
上面的公式中m指的是mini-batch size淑蔚。
3.2 源碼實現(xiàn)
m = K.mean(X, axis=-1, keepdims=True)#計算均值
std = K.std(X, axis=-1, keepdims=True)#計算標(biāo)準(zhǔn)差
X_normed = (X - m) / (std + self.epsilon)#歸一化
out = self.gamma * X_normed + self.beta#重構(gòu)變換
上面的x是一個二維矩陣市殷,對于源碼的實現(xiàn)就幾行代碼而已,輕輕松松刹衫。
3.3 實戰(zhàn)使用
-
(1) 可能學(xué)完了上面的算法醋寝,你只是知道它的一個訓(xùn)練過程,一個網(wǎng)絡(luò)一旦訓(xùn)練完了绪妹,就沒有了min-batch這個概念了甥桂。測試階段我們一般只輸入一個測試樣本,看看結(jié)果而已邮旷。因此測試樣本黄选,前向傳導(dǎo)的時候,上面的均值u婶肩、標(biāo)準(zhǔn)差σ要哪里來办陷?其實網(wǎng)絡(luò)一旦訓(xùn)練完畢,參數(shù)都是固定的律歼,這個時候即使是每批訓(xùn)練樣本進(jìn)入網(wǎng)絡(luò)民镜,那么BN層計算的均值u、和標(biāo)準(zhǔn)差都是固定不變的险毁。我們可以采用這些數(shù)值來作為測試樣本所需要的均值制圈、標(biāo)準(zhǔn)差们童,于是最后測試階段的u和σ計算公式如下:
上面簡單理解就是:對于均值來說直接計算所有batch u值的平均值;然后對于標(biāo)準(zhǔn)偏差采用每個batch σB的無偏估計鲸鹦。最后測試階段慧库,BN的使用公式就是:
-
(2) 根據(jù)文獻(xiàn)說,BN可以應(yīng)用于一個神經(jīng)網(wǎng)絡(luò)的任何神經(jīng)元上馋嗜。文獻(xiàn)主要是把BN變換齐板,置于網(wǎng)絡(luò)激活函數(shù)層的前面。在沒有采用BN的時候葛菇,激活函數(shù)層是這樣的:
z=g(Wu+b)也就是我們希望一個激活函數(shù)甘磨,比如s型函數(shù)s(x)的自變量x是經(jīng)過BN處理后的結(jié)果。因此前向傳導(dǎo)的計算公式就應(yīng)該是:
z=g(BN(Wu+b))
其實因為偏置參數(shù)b經(jīng)過BN層后其實是沒有用的眯停,最后也會被均值歸一化济舆,當(dāng)然BN層后面還有個β參數(shù)作為偏置項,所以b這個參數(shù)就可以不用了庵朝。因此最后把BN層+激活函數(shù)層就變成了:
z=g(BN(Wu))
4. Batch Normalization在CNN中的使用
通過上面的學(xué)習(xí)吗冤,我們知道BN層是對于每個神經(jīng)元做歸一化處理又厉,甚至只需要對某一個神經(jīng)元進(jìn)行歸一化九府,而不是對一整層網(wǎng)絡(luò)的神經(jīng)元進(jìn)行歸一化。既然BN是對單個神經(jīng)元的運算覆致,那么在CNN中卷積層上要怎么搞侄旬?假如某一層卷積層有6個特征圖,每個特征圖的大小是100100煌妈,這樣就相當(dāng)于這一層網(wǎng)絡(luò)有6100100個神經(jīng)元儡羔,如果采用BN,就會有6100*100個參數(shù)γ璧诵、β汰蜘,這樣豈不是太恐怖了。因此卷積層上的BN使用之宿,其實也是使用了類似權(quán)值共享的策略族操,把一整張?zhí)卣鲌D當(dāng)做一個神經(jīng)元進(jìn)行處理。
卷積神經(jīng)網(wǎng)絡(luò)經(jīng)過卷積后得到的是一系列的特征圖比被,如果min-batch sizes為m色难,那么網(wǎng)絡(luò)某一層輸入數(shù)據(jù)可以表示為四維矩陣(m,f,p,q),m為min-batch sizes等缀,f為特征圖個數(shù)枷莉,p、q分別為特征圖的寬高尺迂。在cnn中我們可以把每個特征圖看成是一個特征處理(一個神經(jīng)元)笤妙,因此在使用Batch Normalization冒掌,mini-batch size 的大小就是:mpq,于是對于每個特征圖都只有一對可學(xué)習(xí)參數(shù):γ蹲盘、β宋渔。說白了吧,這就是相當(dāng)于求取所有樣本所對應(yīng)的一個特征圖的所有神經(jīng)元的平均值辜限、方差皇拣,然后對這個特征圖神經(jīng)元做歸一化。下面是來自于keras卷積層的BN實現(xiàn)一小段主要源碼:
input_shape = self.input_shape
reduction_axes = list(range(len(input_shape)))
del reduction_axes[self.axis]
broadcast_shape = [1] * len(input_shape)
broadcast_shape[self.axis] = input_shape[self.axis]
if train:
m = K.mean(X, axis=reduction_axes)
brodcast_m = K.reshape(m, broadcast_shape)
std = K.mean(K.square(X - brodcast_m) + self.epsilon, axis=reduction_axes)
std = K.sqrt(std)
brodcast_std = K.reshape(std, broadcast_shape)
mean_update = self.momentum * self.running_mean + (1-self.momentum) * m
std_update = self.momentum * self.running_std + (1-self.momentum) * std
self.updates = [(self.running_mean, mean_update),
(self.running_std, std_update)]
X_normed = (X - brodcast_m) / (brodcast_std + self.epsilon)
else:
brodcast_m = K.reshape(self.running_mean, broadcast_shape)
brodcast_std = K.reshape(self.running_std, broadcast_shape)
X_normed = ((X - brodcast_m) /
(brodcast_std + self.epsilon))
out = K.reshape(self.gamma, broadcast_shape) * X_normed + K.reshape(self.beta, broadcast_shape)