翻譯自 http://neuralnetworksanddeeplearning.com/chap3.html ,《neural networks and deep learning》的第三章內(nèi)容。原作者寫的非常淺顯易懂,英文好的朋友推薦直接讀原作,文章也有別人翻譯過,但是想加深自己的理解,就自己也嘗試翻譯了,水平有限,難免有疏漏,謹(jǐn)慎閱讀。
當(dāng)一個(gè)高爾夫球手在初次學(xué)習(xí)打高爾夫時(shí),他通常會花費(fèi)大量的時(shí)間學(xué)習(xí)基礎(chǔ)的揮桿動(dòng)作。他們會逐漸的學(xué)習(xí)其他的擊球方式,學(xué)習(xí)怎么樣切球,怎么樣打出曲線球,并在學(xué)習(xí)訓(xùn)練過程中不斷的修正他們的基礎(chǔ)揮桿動(dòng)作。我們到目前為止在研究的“反向傳播”算法就跟高爾夫球?qū)W習(xí)中的揮桿動(dòng)作一樣,它是神經(jīng)網(wǎng)絡(luò)訓(xùn)練中的大部分基礎(chǔ)工作。在這一章中,我將會介紹一些能夠提升“反向傳播”算法效率的技術(shù),通過這些技術(shù)可以優(yōu)化神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)效率。
我們在這一章中提出的優(yōu)化技術(shù)包括:“交叉熵”損失函數(shù);四種能夠提高模型泛化能力的正則化方法(L1和L2正則化,dropout and artificial expansion of the training data));一種更好的初始化神經(jīng)網(wǎng)絡(luò)中各節(jié)點(diǎn)權(quán)重的方法;神經(jīng)網(wǎng)絡(luò)中的超參數(shù)設(shè)置方法。我也會稍微提到一些其他的方法。這四個(gè)部分之間內(nèi)容都是相互獨(dú)立的,所以你也可以直接挑選你感興趣的部分,跳過那些你已經(jīng)很了解或者不感興趣的部分。我們也會給出一些技術(shù)實(shí)現(xiàn)的代碼,并使用這些技術(shù)實(shí)現(xiàn)方法改善我們在第一章中提到的手寫識別問題。
由于神經(jīng)網(wǎng)絡(luò)的中涉及的技術(shù)非常多,我們這一章只會研究其中的一些常用方法。我們希望通過深入研究少數(shù)幾個(gè)最重要的方法,來加強(qiáng)對神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)理論的認(rèn)識。掌握這幾個(gè)最重要的技術(shù)理論及方法,不僅僅能夠優(yōu)化神經(jīng)網(wǎng)絡(luò)的表現(xiàn),并且有助于幫你解決你在學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)中可能遇到的其他問題。這些技術(shù)會有助于你快速學(xué)習(xí)其他相關(guān)技術(shù),為你奠定理論基礎(chǔ)。
交叉熵
無論做什么,當(dāng)我們發(fā)現(xiàn)自己做得不好,總是會令人感到沮喪的。很久之前,我第一次給一位聽眾演奏鋼琴的時(shí)候,我非常緊張,在演奏時(shí),我比琴譜低了一個(gè)八度。我感到很困惑,甚至無法完成整個(gè)演奏,直到別人指出了我的錯(cuò)誤,那真是非常尷尬的經(jīng)歷。相比這個(gè)不愉快的經(jīng)歷,我們發(fā)現(xiàn)當(dāng)我們被別人明確的指出錯(cuò)誤根源時(shí),我們反而能獲得更大的進(jìn)步。你能想象,我在下一次給別人演奏時(shí),就能正確的演奏出琴譜了!相反,如果沒有人指出我們的錯(cuò)誤,我們學(xué)習(xí)的效率往往就比較低。
在神經(jīng)網(wǎng)絡(luò)訓(xùn)練中,我們也期望神經(jīng)網(wǎng)絡(luò)也能夠從錯(cuò)誤中獲得進(jìn)步。在神經(jīng)網(wǎng)絡(luò)訓(xùn)練過程中,這能實(shí)現(xiàn)嗎?為了回答這個(gè)問題,我們來看一個(gè)簡單的例子。我們用只有一個(gè)輸入的單個(gè)神經(jīng)元作為示例:
我們假定這個(gè)神經(jīng)元訓(xùn)練完成后,具備的功能是:當(dāng)輸入為1時(shí),它的輸出是0。這個(gè)假定只是為了方便我們演示神經(jīng)元的訓(xùn)練過程,有助于清晰的表述怎樣以梯度下降的方式來獲取權(quán)重w和偏移b的過程。下面我們將具體的討論這個(gè)神經(jīng)元是怎樣學(xué)習(xí)的。
首先,我們初始化權(quán)重w=0.6,b=0.9。通常情況下,有一些常用的方法來初始化這些變量的取值,在這里我并沒有采用這些方法,只是簡單的指定了變量的初始值。當(dāng)我指定了初始值之后,這個(gè)神經(jīng)元的輸出是0.82(激活函數(shù)采用sigmoid函數(shù))。我們期望的輸出是0,當(dāng)前的輸出0.82與期望值明顯有較大偏差,因此這個(gè)神經(jīng)元還需要進(jìn)一步訓(xùn)練,獲取更好的W和b。點(diǎn)擊下圖中右下方的“Run”按鈕,你可以看到這個(gè)神經(jīng)元是如何完成訓(xùn)練,逐漸的接近期望值0。(注意,這并不是一個(gè)預(yù)先處理過的動(dòng)畫圖片,是通過瀏覽器計(jì)算梯度之后,js程序使用這個(gè)梯度更新權(quán)重w和偏移b,最終展示給你了結(jié)果。)這個(gè)示例中的設(shè)置學(xué)習(xí)率η=0.15,確保訓(xùn)練的過程比較慢,這樣我們可以清晰的可視化該過程。我們也可以更改設(shè)置加快訓(xùn)練過程,使訓(xùn)練在數(shù)秒內(nèi)完成。在這個(gè)訓(xùn)練過程中,我們使用的損失函數(shù)是我們在第一章介紹過的平方損失函數(shù)。我們會在后面簡短的介紹下這個(gè)損失函數(shù),如果你還是對該函數(shù)有疑問,則可以重新閱讀第一章中的該內(nèi)容。(注意,你可以重復(fù)點(diǎn)擊“Run”按鈕,重復(fù)觀看該訓(xùn)練過程)。
補(bǔ)圖,js效果(文章頭部有原文鏈接,可至原文觀看)
正像你所看到的這樣,通過快速的更改權(quán)重w和偏移b,神經(jīng)元輸出的誤差漸漸變小,最終該神經(jīng)元給出的輸出值是0.09.它并不是完全等于我們的期望輸出值0,但是0.09也已經(jīng)是非常接近這個(gè)期望了。假設(shè)我們初始化權(quán)重w=b=2.0,那么神經(jīng)元初始輸出值是0.98,這是一個(gè)非常糟糕的輸出。你可以點(diǎn)擊下面示例中的“Run”按鈕,來觀察一下這個(gè)神經(jīng)元是如何學(xué)習(xí)的。
補(bǔ)圖,js效果(文章頭部有原文鏈接,可至原文觀看)
雖然我們在這兩個(gè)例子中使用了同樣的學(xué)習(xí)率(η=0.15),但是我們發(fā)現(xiàn)第二例子的學(xué)習(xí)速度要更慢一些。事實(shí)上,第一個(gè)示例在差不多150次迭代的時(shí)候,權(quán)重w和偏移b基本上就不再改變了,神經(jīng)元的輸出也更快的接近期望值0.
這個(gè)神經(jīng)元的學(xué)習(xí)過程和人類的學(xué)習(xí)過程并不相同。我在本章內(nèi)容的開端曾說過,我們經(jīng)常能夠從嚴(yán)重的錯(cuò)誤中獲得更高的學(xué)習(xí)效率。但是神經(jīng)元從嚴(yán)重錯(cuò)誤中學(xué)習(xí)進(jìn)步卻比較困難,相比簡單的錯(cuò)誤,神經(jīng)元反而能學(xué)習(xí)的更快。經(jīng)過人們的研究發(fā)現(xiàn),這種現(xiàn)象并不是僅僅在我們這個(gè)簡單神經(jīng)元訓(xùn)練中出現(xiàn),在其他神經(jīng)網(wǎng)絡(luò)的訓(xùn)練中也經(jīng)常出現(xiàn)。那么為什么神經(jīng)元不能從錯(cuò)誤中獲得更高的學(xué)習(xí)效率呢?是否能發(fā)掘一種方法來避免這種現(xiàn)象出現(xiàn)?
更進(jìn)一步的探尋這個(gè)問題,我們發(fā)現(xiàn)這個(gè)神經(jīng)元在學(xué)習(xí)訓(xùn)練過程中,是以損失函數(shù)對權(quán)重W和偏移b的偏導(dǎo)數(shù)決定的方向不斷更改W和b的取值,即?C/?w和?C/?b。所以我們說“學(xué)習(xí)效率低”實(shí)際上與說偏導(dǎo)數(shù)太小是等價(jià)的。那么問題的根源就是為什么這兩個(gè)偏導(dǎo)數(shù)比較小,那我們先來計(jì)算一下這兩個(gè)偏導(dǎo)數(shù)看看真實(shí)的情況是什么樣子的。我們在這個(gè)示例中使用的是平方損失函數(shù),定義如下所示
在該表達(dá)式中,a是神經(jīng)元在輸入x=1時(shí)的輸出值,y=0是我們的期望輸出值。但實(shí)際上,這個(gè)表達(dá)式是W和b的函數(shù),其中a=σ(z),而z=wx+b。(σ是sigmoid激活函數(shù)。)應(yīng)用導(dǎo)數(shù)鏈?zhǔn)揭?guī)則可得到兩個(gè)偏導(dǎo)數(shù)如下所示:
在上式中,我們最后將y=0和x=1分別代入該表達(dá)式。我們再接下來看看上式中的σ′(z)。σ函數(shù)如下圖所示
通過上面這張函數(shù)圖像,可知當(dāng)神經(jīng)元的輸出取值接近1的時(shí)候,曲線變的非常平,σ′(z)取值也就非常小。上面的等式(55)和(56)這時(shí)的取值就是非常小。這就是學(xué)習(xí)速度變慢的根本原因所在。隨后我們將看到在一般的神經(jīng)網(wǎng)絡(luò)中,學(xué)習(xí)速度變慢也是由這個(gè)原因引起的,我們的這個(gè)簡單示例并不是特例。
交叉熵?fù)p失函數(shù)簡介
我們應(yīng)該如何應(yīng)對學(xué)習(xí)速度慢的問題?經(jīng)過上面的分析證明,我們可以通過將平方損失函數(shù)替換成一個(gè)其他類型的損失函數(shù)來避免這個(gè)問題。為了能更明白的解釋交叉熵函數(shù),我們會對上面那個(gè)單神經(jīng)元的示例進(jìn)行一些擴(kuò)展。我們假定我們要訓(xùn)練的神經(jīng)元具備多個(gè)變量輸入,x1,x2,….,每個(gè)變量對應(yīng)的權(quán)重為w1,w2,…,該神經(jīng)元偏移為b
這個(gè)神經(jīng)元的輸出值是a=σ(z),其中z=∑jwjxj+b.我們將這個(gè)神經(jīng)元的交叉熵函數(shù)定義如下
在上式中的n代表訓(xùn)練樣本數(shù),對所有的樣本輸入x,神經(jīng)元輸出a和標(biāo)簽y按上式進(jìn)行累加求和。
我們很難從直觀上觀察出這個(gè)函數(shù)表達(dá)式對解決學(xué)習(xí)速度降低問題是否有效。實(shí)際上,我們甚至不確定這個(gè)函數(shù)表達(dá)的含義跟我們理解的“損失”含義是否一致。因此,讓我們先明確一下這個(gè)函數(shù)能被用來做損失函數(shù)的原因。
交叉熵函數(shù)能夠被用來做損失函數(shù)的原因有兩個(gè)。第一,函數(shù)是非負(fù)的,也就是說該函數(shù)取值C>0。(注意:由于a=σ(z)是sigmoid函數(shù)的取值,所以00。)第二,當(dāng)神經(jīng)元的輸出跟我們期望輸出越接近,交叉熵函數(shù)的取值越接近0。為什么呢?由于訓(xùn)練樣本中y=0,如果神經(jīng)元輸出值a≈0,此時(shí)神經(jīng)元的輸出與期望輸出非常接近,而交叉熵函數(shù)中的ylna=0,并且(1-y)ln(1-a)≈0,累加所有項(xiàng)之后則有C≈0,即可說明當(dāng)神經(jīng)元的輸出跟我們期望輸出越接近,交叉熵函數(shù)的取值越接近0。同理,如果y=1,神經(jīng)元的輸出a≈1,交叉熵函數(shù)取值也接近0。
綜上可知,交叉熵函數(shù)是一個(gè)取值大于0,并且當(dāng)神經(jīng)元的輸出與y越接近,其取值就越接近0,這恰恰就是我們選擇損失函數(shù)的標(biāo)準(zhǔn)(注:平方損失函數(shù)也可以簡單的推導(dǎo)出這兩條特性),所以理論上交叉熵函數(shù)是可以用作損失函數(shù)的。
下一個(gè)問題,交叉熵函數(shù)能避免學(xué)習(xí)速度降低的問題嗎?我們先計(jì)算交叉熵函數(shù)的對W和b的偏導(dǎo)數(shù),令a=σ(z),同時(shí)利用鏈?zhǔn)角髮?dǎo)規(guī)則,可以得到下面的表達(dá)式
對上面的表達(dá)式做一些基礎(chǔ)運(yùn)算,就可以得到下面的表達(dá)式
其中σ(z)=1/(1+e-z),對其求導(dǎo)可知σ′(z)=σ(z)(1-σ(z))。我希望你能在下面的練習(xí)章節(jié)中自己推到這個(gè)等式,現(xiàn)在我們就直接使用該等式。當(dāng)我們把σ′(z)代入等式(60),就可以將其簡化成下面的表達(dá)式
這是一個(gè)非常漂亮的表達(dá)式。通過該表達(dá)式可知,學(xué)習(xí)速度依賴于(σ(z)-y)的取值。(σ(z)-y)在一定程度上反映了錯(cuò)誤的程度,也就是說錯(cuò)誤越大,神經(jīng)元每次改變w和b的幅度越大,學(xué)習(xí)速度就越快,這似乎與我們的直覺更加接近(對比作者鋼琴演奏的例子)。同時(shí),這個(gè)函數(shù)也可以避免像表達(dá)式(55)那樣出現(xiàn)由于σ′(z)平滑而帶來的學(xué)習(xí)速度降低的問題。當(dāng)我們使用交叉熵函數(shù)作為損失函數(shù)時(shí),我們不再需要計(jì)算σ′(z),也就不再需要擔(dān)心它的取值太小帶來的問題。正是由于我們使用了交叉熵函數(shù),它使我們完全避免了這個(gè)問題,你也許會認(rèn)為這是一個(gè)很神奇的事情。其實(shí)這并不是一個(gè)很神奇的事情,我們一會就會讓你了解到,選擇交叉熵函數(shù)確實(shí)是由于它具備這一特性。
我們以同樣的方式計(jì)算偏移b的偏導(dǎo)數(shù),我不會再給出詳細(xì)的證明步驟,你可以自己驗(yàn)證下面的表達(dá)式
同樣,對比表達(dá)式(56),這再次避免了σ′(z)帶來的學(xué)習(xí)速度降低的問題
練習(xí)
證明表達(dá)式:σ′(z)= σ(z)(1-σ(z))
讓我們回到最開始的單神經(jīng)元簡單示例中,看看當(dāng)我們使用交叉熵函數(shù)替換平方損失函數(shù)后都發(fā)生了些什么。我們首先看一下,在平方函數(shù)表現(xiàn)較好的情況下,交叉熵函數(shù)的表現(xiàn)。在下圖中,我們使用交叉熵函數(shù)替換了平方損失函數(shù),初始化權(quán)重w=0.6,權(quán)重b=0.9,點(diǎn)擊下方的“Run”按鈕,可以觀察訓(xùn)練過程:
補(bǔ)圖,js效果(文章頭部有原文鏈接,可至原文觀看)
不出意外,采用交叉熵函數(shù)的神經(jīng)元學(xué)習(xí)的過程表現(xiàn)良好,與采用平方損失函數(shù)的神經(jīng)元學(xué)習(xí)過程類似。接下來我們將權(quán)重w和偏移b都設(shè)置成2.0,采用平方損失函數(shù)的神經(jīng)元訓(xùn)練過程并不理想(可觀察上面示例),我們看一下采用交叉熵函數(shù)的神經(jīng)元訓(xùn)練效果如何,如下圖所示:
補(bǔ)圖,js效果(文章頭部有原文鏈接,可至原文觀看)
通過上圖可知,采用交叉熵函數(shù)的神經(jīng)元訓(xùn)練過程非常迅速,并且正向我們預(yù)期那樣得到了很好的效果。如果你仔細(xì)觀察的話,你會發(fā)現(xiàn)采用交叉熵函數(shù)的訓(xùn)練的曲線在初始階段要比平方損失函數(shù)曲線更加陡峭。曲線的坡度比較大,說明了交叉熵函數(shù)正像我們預(yù)期的那樣從錯(cuò)誤中學(xué)習(xí)的更快。
在這兩個(gè)例子中,我并沒有像平方損失函數(shù)示例中那樣指明學(xué)習(xí)率的取值。在平方損失函數(shù)中,我們將η設(shè)置為0.15。我們也應(yīng)該在交叉熵?fù)p失函數(shù)中采用相同的值嗎?事實(shí)上,損失函數(shù)不同,很難準(zhǔn)確的定義相同的學(xué)習(xí)率,就像蘋果和橘子的對比一樣。在這幾個(gè)例子中,我們設(shè)置學(xué)習(xí)率的標(biāo)準(zhǔn)是可以讓我們更容易的看清楚神經(jīng)元的學(xué)習(xí)過程。假如你仍舊好奇,我們可以公布我們在交叉熵?fù)p失函數(shù)中采用的學(xué)習(xí)率:η=0.005。
你可能會認(rèn)為學(xué)習(xí)率的改變會使上述幾個(gè)示例失去了對比意義。當(dāng)你在隨機(jī)初始化設(shè)置學(xué)習(xí)率參數(shù)時(shí),誰知道神經(jīng)元學(xué)習(xí)速度會有多快呢?這種想法忽略了問題的重點(diǎn)。這幾張圖的重點(diǎn)不是神經(jīng)元訓(xùn)練的絕對速度對比,而是神經(jīng)元的學(xué)習(xí)速度是怎樣變化的。在對比示例中,采用平方損失函數(shù)要比采用交叉熵?fù)p失函數(shù),隨著神經(jīng)元在逐漸接近正確值的過程中,神經(jīng)元從錯(cuò)誤中學(xué)習(xí)的速度要慢很多。這是不依賴于學(xué)習(xí)率參數(shù)的設(shè)置的。
我們已經(jīng)研究了交叉熵函數(shù)對單個(gè)神經(jīng)元的作用。我們可以輕易的將交叉熵函數(shù)應(yīng)用推廣到多層神經(jīng)網(wǎng)絡(luò)的多個(gè)神經(jīng)元中。我們假設(shè)y=y1,y2,…是神經(jīng)元的期望輸出值,這些神經(jīng)元位于最終的輸出層,而aL1,aL2,..是輸出層的實(shí)際輸出。我們可以定義交叉熵函數(shù)如下
這個(gè)表達(dá)式跟我們表達(dá)式(57)非常相似,不同的地方是我們對所有的輸出神經(jīng)元進(jìn)行了累加求和。我不會通過求導(dǎo)再證明這個(gè)表達(dá)式在避免學(xué)習(xí)速度下降方面的作用,從表面上看它在多層神經(jīng)網(wǎng)絡(luò)上應(yīng)該是有效的。假如你對這個(gè)感興趣,你可以自己通過求導(dǎo)驗(yàn)證。
我們應(yīng)該在什么時(shí)候采用交叉熵函數(shù)替換平方損失函數(shù)呢?事實(shí)上,在采用sigmoid函數(shù)作為激活函數(shù)的神經(jīng)網(wǎng)絡(luò)中,交叉熵函數(shù)基本上總是更好的選擇。讓我們看看原因是什么,當(dāng)我們在建立神經(jīng)網(wǎng)絡(luò)時(shí),我們會隨機(jī)初始化權(quán)重和偏移。這些隨機(jī)選擇的初始值會對某些輸入給出錯(cuò)誤的輸出,也就是說神經(jīng)元在應(yīng)該輸出0的時(shí)候,錯(cuò)誤的輸出了1,或者相反的情況也有可能,這種情況基本上不可避免的總會出現(xiàn)。如果我們采用平方損失函數(shù),神經(jīng)元會學(xué)習(xí)速度比較慢。由于權(quán)重會持續(xù)從訓(xùn)練樣本中學(xué)習(xí),因此甚至可能會出現(xiàn)無法最終收斂,這顯然不是我們想要的。
練習(xí)
l一個(gè)關(guān)于交叉熵函數(shù)可能會混淆的點(diǎn)是y和a在函數(shù)中的位置。你有可能會將?[ylna+(1?y)ln(1?a)]記成?[alny+(1?a)ln(1?y)]。想想第二個(gè)表達(dá)式當(dāng)y=0或者y=1會發(fā)生什么?這些情況是否會影響第一個(gè)表達(dá)式?想想為什么。
l在本章節(jié)開始的示例中,我們假設(shè)交叉熵函數(shù)輸出值很小,并且對于所有的訓(xùn)練樣本都有σ(z)≈y。這個(gè)假設(shè)依賴于y只能取值0或者1。在分類問題時(shí),這種建設(shè)是沒有問題的,但是對其他問題(如回歸問題)y可能取值位于0和1之間。事實(shí)是當(dāng)σ(z)=y時(shí),交叉熵函數(shù)會取到最小值(注:對表達(dá)式63中的變量a求偏導(dǎo),可知最小值點(diǎn)為a=y,即σ(z)=y,將a=y代入表達(dá)式63,即可得到下面的表達(dá)式),該最小值為:
表達(dá)式?[ylny+(1?y)ln(1?y)]有時(shí)候也被稱為二元熵
問題
l在多層多神經(jīng)元網(wǎng)絡(luò)中,交叉熵函數(shù)是否有效性。
按照我們提到的計(jì)算方法,平方損失函數(shù)對輸出層權(quán)重的偏導(dǎo)數(shù)是:
當(dāng)神經(jīng)元處于最后的收斂階段時(shí),σ′(zLj)會讓學(xué)習(xí)的速度快速下降。對于交叉熵函數(shù)來說,對于單個(gè)輸入x,其輸出誤差δL是
因此,在采用交叉熵函數(shù)的神經(jīng)網(wǎng)絡(luò)的輸出層,損失函數(shù)對輸出層權(quán)重的偏導(dǎo)數(shù)是
σ′(zLj)也消失了,所以交叉熵函數(shù)能夠避免學(xué)習(xí)速度下降的問題。交叉熵函數(shù)并不僅僅在單個(gè)神經(jīng)元中生效,在更復(fù)雜的多層多個(gè)神經(jīng)元網(wǎng)絡(luò)中也能起到效果。對于偏差b的證明也是類似的過程,如果你對這一證明過程抱有疑問,你可以自己利用公式推導(dǎo)出這一結(jié)果。
l如果神經(jīng)網(wǎng)絡(luò)的輸出層采用線性激活函數(shù),則可以使用平方損失函數(shù)。
假定在多層神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)的輸出層采用線性激活函數(shù),也就是說不采用sigmoid函數(shù)激活,輸出層激活規(guī)則aLj=zLj。在這種情況下,采用平方損失函數(shù)的輸出誤差δL為
跟前面的處理過程類似,計(jì)算平方損失函數(shù)對輸出層權(quán)重w和偏移b的偏導(dǎo)數(shù)如下
這說明如果激活函數(shù)是線性函數(shù),平方損失函數(shù)并不會造成學(xué)習(xí)速度的下降。事實(shí)上,在采用線性激活函數(shù)的情況下,平方損失函數(shù)是一個(gè)很合適的選擇。
將交叉熵函數(shù)應(yīng)用到手寫數(shù)字識別
未完....