神經網絡與深度學習(三):如何提升神經網絡學習效果


一個高爾夫球手練習高爾夫球時會花絕大多數時間練習基本的揮桿動作。在基本的揮桿動作的基礎上,逐漸的才會練習其他動作。相似的,目前為止我們一直專注在理解BP算法, 它是我們的基礎”揮桿”動作,學習神經網絡的基礎。這章中我會解釋一些用來提升BP算法的技術,以提高神經網絡的學習。

本章介紹的技術包括:1,新的cost函數,cross-enropy cost函數;2,regularization方法(L1 regularization, L2 regularization, drop out, 手動擴展訓練集),提升神經網絡的在非訓練集上的泛化;3,更優的神經網絡的初始化方法;4,選擇更好的超參數的一些探索。我也會簡單過一遍其他的技術,但不會深入討論。這些技術的討論都是互不依賴的,所以你可以直接跳到各節閱讀。我們也代碼實現了一些技術,用來提升第一章中學習的數字識別問題。

當然,我們沒有覆蓋了所有用於神經網絡的訓練技術,事實上我們只覆蓋的很少的一些。這里的思想是,在學習很多技術的時候,最好的方法是深入的學習一些最重要的方法。掌握了這些重要技術的不止技術本身有用處,也會加深你對實際中出現問題的理解。這樣在必要的時候,你也會迅速的掌握其他是技術。

Cross-entropy 損失函數

大多數人覺得犯錯誤是很讓人不開心的。學習鋼琴后的不久,我有過一次演出的經驗。那次我非常緊張,然后把一個八度音演奏的特別慢。我很困惑,不能繼續演奏,直到有個人指出了我的錯誤。我感到很尷尬。雖然不開心,但是當我們錯的很厲害的時候,我們也會學習的很快。我能保證下次演出的時候,我不會再錯了。相反,錯誤不是很明顯的時候,我們學習的會比較慢。

我們希望我們的神經網絡能快速的從錯誤中學習。實際中這個會發生嗎?我們首先來看一個特別簡單的例子,例子中之一個神經元和一個輸入:

我們將訓練這個神經元做一件異常簡單的事:輸入1輸出0。當然,對於這個問題,我們完全可以人工手動的配置權值和偏移值。但是應用梯度下降去學習權重和偏移的過程具有一定的啟發性,所以我們來看下這個神經元是怎么學習的。

首先,我們設置初始的權重是0.6,初始的偏移值是0.9,我並沒用什么特別的方法來選擇這兩個數字,而是隨意選取的。這樣,初始的輸出值是0.82。離我們想要的輸出0相差比較遠。我們設置學習速率\(\eta=0.15\),損失函數\(C\)用二次損失函數.

你可以看到,神經元不斷的學到新的權重和偏移,損失函數不斷減小。最后給出的輸出是0.09,已經很接近我們想要的輸出0了。現在,我們設置初始的權值和偏移值都是2,這樣初始的輸出值是0.98,比上面的0.82要錯的更遠,我們來看下運行的結果:

這個例子中,我們使用的學習速率\(\eta\)也是0.15。可以看到,開始的時候學習的很慢,權重和偏移幾乎不怎么變化,損失函數減小的很少。學習大概150次了之后,學習才開始加快.

與人類的學習過程相比較,神經元的學習行為很奇怪。因為本節的開始部分我說過,當我們錯誤比較大的時候,我們往往學習的比較塊。上面的兩個例子看來,神經元的行為卻相反。並且這種現象不止發生在這個單神經元的網絡中,更一般的網絡中也同樣存在。學習為什么會慢呢?有什么辦法可以避免嗎?

為了理解問題的根源,細想一下我們神經元學習的方法是,通過損失函數的梯度來不斷的改變權重和偏移值,學習的慢的意思,其實就是梯度比較小的意思。所以問題的關鍵是理解,梯度值為什么小。為了理解這個問題,我們來考慮梯度,也就是偏導數的計算方法。二次損失函數的定義如下:

\[C=\frac{(y-1)^2}{2}\quad(54) \]

其中,\(a\)是當輸入為1時網絡的實際輸出,\(y=0\)。通過求導法則,我們知道偏導數的計算方式如下:

\[\frac{\partial C}{\partial w}=(a-y)\sigma'(z)x=a\sigma'(z)\quad(55) \]

\[\frac{\partial C}{\partial b}=(a-y)\sigma'(z)=a\sigma('z)\quad(56) \]

回一下一下\(\sigma\)函數的圖像如下:

可以看出,當函數的值接近1的時候,圖像變的非常平坦,也就是\(\sigma'(z)\)的值接近0,比較小。結合公式55,56可以看出,這導致偏導數\(\partial C/\partial w,\partial C/\partial b\)也會很小。這就是學習緩慢的根源。后面我們會看到,學習緩慢的問題是個比較普遍的問題,不止存在於我們的例子網絡中。

交叉墒損失函數(cross-entroy損失函數)

怎么解決學習緩慢的問題呢?我們可以通過使用一個叫做交叉墒的損失函數來解決。為了理解交叉墒損失函數,來看一下這個例子。假設神經元有多個輸出\(x_1,x_2,...\),對應的權重分別是\(w_1,w_2,...\),偏移是b:

神經元的輸出是\(a=\sigma(z)\),其中\(z=\sum_{j}w_jx_j+b\).交叉墒損失函數的定義如下:

\[C=-\frac{1}{n}\sum_{x}[ylna+(1-y)ln(1-a)]\quad(57) \]

其中n表示訓練數據的數量。求和的過程是在所有的訓練數據上進行的,\(x,y\)表示輸入和對應的輸出。

公式57能解決學習緩慢的問題,這點很不明顯。甚至,它能作為損失函數這點,也是很不明顯。我們首先來看下它為什么能作為一個損失函數。

它的兩點性質使它可以作為一個損失函數:首先,它的取值非負。我們注意到(a),公式57中的所有項都是負的。(b),前面有一個負號。

其次,輸出越接近目標輸出值,函數值越小。

總結來說,就是交叉墒函數是正數,而且網絡輸出越接近目標值,函數值越接近0.這正是損失函數需要的兩個特征。更好的消息是,交叉墒函數沒有學習緩慢的問題,為了理解為什么,我們來計算一下交叉墒函數的偏導數:

\[\frac{\partial C}{\partial w_j}=-\frac{1}{n}\sum_{x}(\frac{y}{\sigma(z)}-\frac{(1-y)}{1-\sigma(z)})\frac{\partial \sigma}{\partial w_j}\quad(58) \]

\[=-\frac{1}{n}\sum_{x}(\frac{y}{\sigma(z)}-\frac{(1-y)}{1-\sigma(z)})\sigma'(z)x_j\quad(59) \]

簡化后可以寫成:

\[\frac{\partial C}{\partial w_j}=\frac{1}{n}\sum_{x}\frac{\sigma'(z)x_j}{\sigma(z)(1-\sigma(z))}(\sigma(z)-y)\quad(60) \]

其中\(\sigma(z)=1/(1+e^{-z})\),不難推導出等式\(\sigma'(z)=\sigma(z)(1-\sigma(z))\).所以進一步簡化公式60得出:

\[\frac{\partial C}{\partial w_j}=\frac{1}{n}\sum_{x}x_j(\sigma(z)-y)\quad(61) \]

這個公式告訴我們,權值的學習的速率是由\(\sigma(z)-y\)來控制的,也就是輸出的錯誤程度。錯誤越大,學習的越快。這正是我們想要的特征。

類似的可以計算關於偏移的偏導數:

\[\frac{\partial C}{\partial b}=\frac{1}{n}\sum_{x}(\sigma(z)-y)\quad(62) \]

同樣,也避免了學習緩慢的問題。

回到前面的例子中,我們用交叉墒損失函數代替二次損失函數,看看會發生什么;我們首先從權值為0.6、偏移為0.9開始:

不出所料,神經元學習的很快。現在我們來看一下權值和偏移值都是2.0的情況:

我們看到,正如我們所希望的,神經元也學習的很快。

本例子中我並沒有說我用的學習速率\(\eta\)是多少。前面的例子里,我設置\(\eta=0.15\)。我們應該用相同的學習速率嗎?事實上,損失函數改變后,不太可能准確的找到"相同的"學習速率,這是蘋果和香蕉之間的比較。這里兩個例子里,我簡單通過實驗找到了一個學習效率,能夠使我們看清楚所發生的事情。如果你特別好奇,我使用的\(\eta=0.005\)

你可能會反對,學習速率不一樣,上面的比較就沒有意義。不同的學習速率下,誰會關心學習的速度啊?其實這個反對的點不對。上圖中的學習的速度其實不重要,重要的是速度的變化。具體來說就是,使用二次損失函數的情況下,當神經元錯誤比較大的時候,學習會緩慢,但是使用交叉墒損失函數的時候,沒有這個問題。這個結論是不依賴與具體采用的學習速率\(\eta\)的。

我們學習了當個神經元的交叉墒損失函數。其實,很容易推廣到多神經元多層的網絡中去。具體說,假設\(y=y_1,y_2,...\)是目標輸出值,\(a_1^L,a_2^L,...\)是網絡是設計輸出值,則交叉墒的定義為:

\[C=-\frac{1}{n}\sum_{x}\sum_{j}[y_jlna_j^L+(1-y_j)ln(1-a_j^L)]\quad(63) \]

這個跟公式57類似,差別在於需要在所有的輸出上求和\(\sum_{j}\).我不准備推導63的偏導數了,如果有興趣你可以推到一下。但是它確實可以避免學習緩慢的問題的。

那么,我們如何選擇二次損失函數和交叉墒損失函數呢?事實上,激活函數是\(\sigma\)函數的時候,交叉墒損失函數都是較好的選擇。原因是,我們的權重和偏移都是通過某種隨機方式初始話的,可能會導致初始的錯誤非常大,應該輸出0的可能輸出了1,或則正好相反。二次函數就會出現學習緩慢的問題。而交叉墒損失函數就沒有這個問題。

使用交叉墒分類MNIST數字

在使用梯度下降和BP算法的網絡中,交叉墒函數很容易實現,我們稍后會在network.py的改進版本network2.py中實現它和本章中將要討論的其他技術。現在讓我們的來看一下交叉墒函數在MINIST問題上的表現。我們使用上面的網絡結構--隱藏層有30個神經元,mini-batch大小為10,學習速率\(\eta=0.5\),學習次數為30次。network2.py的接口跟network.py少有不同,可以通過help命令(network2.Network.SGD)來查看network2.py的接口文檔,我們來看下怎么使用:

>>> import mnist_loader
>>> training_data, validation_data, test_data = \
... mnist_loader.load_data_wrapper()
>>> import network2
>>> net = network2.Network([784, 30, 10], cost=network2.CrossEntropyCost)
>>> net.large_weight_initializer()
>>> net.SGD(training_data, 30, 10, 0.5, evaluation_data=test_data,
... monitor_evaluation_accuracy=True)

注意,net.large_weight_initilizer()的作用是初始化權重和偏移,跟第一章中作用一樣。需要運行這樣的原因是,本章的后面,我們會修改權重和偏移的默認初始化方法。上面代碼運行的結果是准確率達到95.49%。這個結果更使用二次損失函數達到的結果95.42%很接近。

再來看一下在100個隱藏神經元的網絡的情況。使用交叉墒函數,其他的參數保持不變。我們的准確率達到96.83%。跟第一章使用二次損失函數的結果96.59相比,有了一定的提高。准確率看起來差別不大,但是考慮到錯誤率,從3.41下降到3.18,我們大概降低了1/14之一。這個進步還是不小的。

交叉墒損失函數的結果跟二次損失函數結果,相似甚至跟好一些,這很不錯,但是,並不能證明交叉墒比較好。原因是我花了很少的時間來選擇其他的超參。要想證明交叉上函數確實好一些,我們需要花些時間去調優一下各種超參。你會發現,結果確實證明了交叉墒損失函數要比二次損失函數好。

以上的模式會經常出現在本章和本書的后面內容里:我們討論一個新技術,嘗試后得到改進后的結果,然后我們花費很大的努力去優化其他的超參,使得改進的結果更加可信。這需要很大的工作量和計算量。我們不准備做這樣的透徹耗時的研究。想法,我們會繼續像上面一樣做一些簡單的測試。所以,需要記住的是,首先這些測試沒有絕對的證明,其次,解釋得出的結論可能不成立。

那為什么花費這么多篇章來討論交叉墒呢?畢竟它對MNIST結果的提升很有限。本章的后面我們會討論其他的一些技術--尤其是規則化,對結果會有很大的提升。所以,為什么重點討論交叉墒呢?一部分是因為交叉墒使用的很廣泛,值得深入學習。更大的原因是,飽和問題是神經網絡中的重要的問題,本書中會經常的回到這個問題上。所以,交叉墒的討論,是理解神經元飽和問題和解決辦法的很好的實驗。

交叉墒是什么意思?從哪來的?

目前為止,我們都在討論交叉墒函數的代數形式和實現形式。留下一些更廣泛的概念性的問題沒有去回答。例如:交叉墒是什么意思?如何直觀的思考交叉墒?交叉墒最早是怎么被構造出來的?

我們從最后一個問題開始討論:最早是什么激發了我們提出交叉墒的呢?假設我們已經發現了學習緩慢的問題,而且知道了問題的根源是公式55,56中的\(\sigma'(z)\)項。開始,我們可能會想:能不能通過選擇一個損失函數,去掉公式中的\(\sigma'(z)\)項。也就是損失函數滿足下面的公式:

\[\frac{\partial C}{\partial w_j}=x_j(a-y)\quad(71) \]

\[\frac{\partial C}{\partial b}=(a-y)\quad(72) \]

如果能找到一個損失函數使上面的公式71,72成立,則可以消除學習緩慢的問題。事實上,請過上面的公式可以推導出交叉墒的形式:

\[\frac{\partial C}{\partial b}=\frac{\partial C}{\partial a}\sigma'(z).\quad(73) \]

帶入\(\sigma'(z)=\sigma(z)(1-\sigma(z))=a(1-a)\),得到:

\[\frac{\partial C}{\partial b}=\frac{\partial C}{\partial a}a(1-a)\quad(74) \]

比較公式72得到:

\[\frac{\partial C}{\partial a}=\frac{a-y}{a(1-a)}.\quad(75) \]

由此得出:

\[C=-[ylna+(1-y)ln(1-a)]+constant\quad(76) \]

這是計算單個訓練數據的損失,對於所以訓練數據來說,需要球均值:

\[C=-\frac{1}{n}\sum_{x}[ylna+(1-y)ln(1-a)]+constant\quad(77) \]

公式77中的constant是公式76中的constant的均值。所以交叉墒不是什么憑空創造出來的,而是通過這種自然簡單的方式發現的。

那怎么直觀的思考交叉墒函數呢?深入的解釋需要進入一些這里我不想進入的領域。但是,值得一提的是,交叉墒可以在信息論領域找到一個標准的解釋。簡單來說就是,交叉墒是對意外的一個衡量。具體的,我們的神經元目標是計算函數\(x\rightarrow y=y(x)\),但是實際是在計算的函數\(x\rightarrow a=a(x)\)。假定\(a\)表示\(y=1\)的概率,那么\(1-a\)就表示\(y=0\)的概率。那么交叉上就表示,平均來說我們計算出\(y\)的真實值的意外程度。事實上,意外程度在信息論學科里面有一個准確系統的表達方式。如果你感興趣的話,可以參考這里這里.

Softmax

本章中,我們會很多次的使用交叉墒函數來解決學習緩慢的問題。這里,我想介紹解決此問題的另一個方法:softmax層。本章的余下部分並不會使用softmax,所有如果你比較着急,可以跳過這部分。但是softmax還是值得學習的,部分是因為它比較有趣,還一部分是因為在本書的第六章中,我們討論深度學習的時候會使用到它。

softmax是一種定義輸出層的方式。開始的部分跟sigmoid層類似,定義加權輸入為\(z_j^L=\sum_{k}w_{jk}^La_k^{L-1}+b_j^L\).但是不直接調用sigmoid函數得到輸出,而是調用一個所謂的softmax函數。根據次函數,\(j^{th}\)神經元的輸出\(a_j^L\)為:

\[a_j^L=\frac{e^{z_j^L}}{\sum_{k}}e^{z_k^L}\quad(78) \]

其中,分母是在所有的輸出神經元中求和。

如果你不是很熟悉softmax函數,公式78看起來會特別晦澀。我們使用它的原因很不明顯,它能解決學習緩慢問題的原因也不明顯。為了理解這個問題,假設我們有四個輸出神經元,對應的四個加權輸入\(z_1^L,z_2^L,z_3^L,z_4^L\)。下圖中顯示了變量之間的關系,可以通過增加\(z_4^L\)看看會發什么什么變化:

增加\(z_4^L\)值,得到:

當你增加\(a_4^L\)的時候,對應的輸出\(a_4^L\)也會增加,其他輸出會減少。類似的,當你減小\(z_4^L\)時,\(a_4^L\)也減小,其他的輸出會增加。事實上,如果你仔細的看,會發現輸出\(a_4^L\)的改變量正好等於其他所有輸出的改變量,符號相反。願意是,softmax函數保證了所有輸出的和為1:

\[\sum_{j}a_j^L=\frac{\sum_{j}e^{z_j^L}}{\sum_{k}e^{z_k^L}}=1\quad(79) \]

公式78還說明,所有的輸出都是非負數。因為指數函數是非負的。綜合來講,就是softmax層的使出是一些和為1的非負數。也就是說,softmax層是輸出可以看錯一個概率分布。

softmax的輸出是一個概率分布的事實,非常好。很多問題上,可以很方便的模擬計算輸出為\(j\)的概率。所以,在MNIST問題上,我們可以將\(a_j^L\)看做數字為\(j\)的概率。

相比而言,sigoid輸出層的輸出,不能看做一個概率分布。

我們開始建立一些softmax函數的理解。公式78告訴我們softmax層的輸出之和為1,且是非負數。所以公式78的具體形式不再神秘,而是一種將輸出轉化為概率分布的自然方法。你可以把softmax看過一種重新調節\(z_j^L\)並將他們組成概率分布的一種方法。

學習緩慢的問題:我們已經比較熟悉softmax函數了,但是對於它為什么能解決學習緩慢的問題,所知甚少。為了理解這個問題,我們定義一個對數似然損失函數。我們使用\(x\)表示任意一個輸出,\(y\)表示對應的目標輸出。那么,訓練輸出對應的對數似然損失就是:

\[C\equiv - lna_y^L\quad(80) \]

所以,比如,我們使用MNIST圖片,輸入是一張7的圖片,則對數似然損失是\(-lna_7^L\)。加入網絡的表現比較好,則它比較確認輸入是7。所以\(a_7^L\)的輸出接近1,導致\(-lna_7^L\)接近0.相反,如果網絡的表現很糟糕,則\(a_7^L\)的輸出會接近0,呆滯\(-lna_7^L\)的取值比較大。所以對數似然函數的特征符合損失函數的特征。

學習緩慢的問題怎么樣了呢?回憶學習緩慢的根源在於梯度變小,對數似然損失函數的梯度計算公式為:

\[\frac{\partial C}{\partial b_j^L}=a_j^L-y_j\quad(81) \]

\[\frac{\partial C}{\partial w_{jk}^L}=a_{k}^{L-1}(a_j^L-y_j)\quad(82) \]

這些公式保證了我們不會遇到學習緩慢的問題。事實上,softmax輸出層與對數似然損失函數的組合特別類似與sigmoid輸出層與交叉墒損失函數的組合。

那么具體問題中如何選擇的?事實上,很多情況下二套組合的表現都很不錯。本章的后面,我們會使用sigmoid輸出層和交叉墒函數。稍后的第六章中,我們有時會使用softmax輸出層和對數似然函數,目的是讓我們后面的網絡跟目前一些權威的學術論文中的網絡更接近一些。大多數情況下,如果你將輸出看錯概率分布的話,softmax加對數似然函數的組合都是比較值得使用的組合。這個結論不是絕對的,但是在一些離散的分類問題(MNIST問題)上很有用。

過擬合與正規化

諾貝爾獎獲得者物理學家Enrico Fermi有一次接受提問:學生為了解決一個未知物理問題的,提出了一個模型;這個模型的結果與實驗的結果吻合的特別好,但是Fermi卻表示懷疑。他問到模型有幾個可以設置的參數,學生回答說4個。Fermi說:"我記得我的朋友Johnny von Neumann曾經說過,通過4個參數我可以裝下一頭大象,5個參數我可以讓大象扭動自己的鼻子"。

這句話表達的思想就是,具有很多參數的模型可以描述的現象非常多。即使模型的數據跟一有數據非常的吻合,也不能說明模型就是一個很好的模型。或許這只是說明了模型可以描述一致數據中的所有現象,但是並沒有領悟到現象背后的本質。如果是這樣的話,模型在以后數據上會表現會非常好,但是在新的數據上就回出問題。對於模型的真正的測試實在它之前沒見多的數據上進行的。

Fermi和von Neumann對於有四的參數的模型表示懷疑,而我們的有30個隱藏神經元的MNISTS分類網絡擁有24,000個參數。100個隱藏神經元就有80,000個參數。最先進的深度神經網絡有時擁有百萬甚至數十億的參數,我們能夠相信網絡的輸出嗎?

我們通過一個例子來理解下這個問題,例子中我們呢的模型在新的數據上變現的很糟糕。我們使用30個隱藏神經元,擁有23,860個參數的網絡,但是我們不用50,000個MNIST的訓練圖片,而是用1,000個圖片。這樣能使泛化問題變的更明顯。我們用類似的方式訓練,學習速率\(\eta=0.5\),mini-batch大小為10.這次我們訓練了400次,比之前多了很多次,因為我們的訓練數據不如之前的多:


>>> import mnist_loader 
>>> training_data, validation_data, test_data = \
... mnist_loader.load_data_wrapper()
>>> import network2 
>>> net = network2.Network([784, 30, 10], cost=network2.CrossEntropyCost) 
>>> net.large_weight_initializer()
>>> net.SGD(training_data[:1000], 400, 10, 0.5, evaluation_data=test_data,
... monitor_evaluation_accuracy=True, monitor_training_cost=True)

圖形化實驗的結果得到:

結果令人鼓舞,損失值一直在下降。

我們再看一下在測試集上的准確率:

在訓練的前面的200次中,准確率一路上升,達到82%。然后上升變慢,在差不多280次左右停止上升了。之后一次在這個水平上線波動。相比直線,損失值還是下一致下降。也就是280次學習之后,網絡學到的東西不再能泛化到測試數據上。所以不是有用的學習。我們稱280之后為過擬合或則過度訓練。

也許你會懷疑原因是我們在比較訓練集上的損失值和測試記上的准確率,這也許是一個蘋果和香蕉的比較。我們來看下測試集上的損失:

我們看到損失值在15次學習之前一致下降,之后反而開始上升。這事過擬合的另一個信號。但是這個留下了一個難題,過擬合的點是15次呢還是280次呢?從實用的角度來書,我們真正關心的是測試集上的准確率,而測試集上的損失只不過是准曲率的代理。所以將280次作為過擬合點更合理一些。

過擬合的另一個信號如下,訓練集上的准確率:

看到測試集上的准曲率達到了100%,而測試集上只達到了82.27%。所以我們的網絡真的試紙在學習訓練集數據的細節,而沒有領悟背后原理。

過擬合是神經網絡的主要問題之一。特別是在一些現代的網絡中,因為權重和偏移數量十分巨大。我們需要檢測過你的方法,同事也需要能降低過擬合的方法。

檢測過擬合的最直接的辦法就是像上面例子一樣,跟蹤測試集上的准確率,當發現准確率不再上升的時候,停止訓練。當然,嚴格來說,這並不一定說明過擬合了,也許訓垃集和測試集上的准曲率同事停止上升了。但是這個策略仍然可以預防過擬合。

事實上,使用本策略,我們將啟用驗證集。回憶一下,前面我們加載MNIST數據的時候,分成了三個結合:

>>> import mnist_loader 
>>> training_data, validation_data, test_data = \
... mnist_loader.load_data_wrapper()

我們一直在使用training_data和test_data,而忽略了驗證集validation_data。驗證集包含了單獨1,000張圖片。我們將適應validation_data防止過擬合。使用方式與上面的使用測試集方式類似,一旦在驗證集上的准確率停停止上升,則停止訓練。這個策略稱為早停策略。當然,實際中,我們不可能立即知道過擬合發生了,我們將繼續訓練一段時間后才能確信過擬合了。

為了使用驗證集而不是測試集防止過擬合呢?事實上,這是一個通用的策略,用驗證集測試比如訓練次數,學習速率,網絡結構等超參,為超參選擇合適是取值。

當然,這並沒有回答為什么用驗證集而不用測試集防止過擬合。卻提出了一個更一般的問題:為什么用驗證集而不是測試集是設置超參呢?應為在設置超參的時候,我們會嘗試各種超參的組合。如果用測試集去設置這些超參的話,結果可能是超參過擬合了測試集。通過使用驗證集選擇超參,然后用測試測試最終表現,可以是我們確信網絡的表現是真實有效的。換句話說,你可以把驗證集當做一個學習超參的訓練集,讓我們學習到超參的取值。這種尋找超參的方式被稱為隔離法。因為驗證集和測試集一直是被隔離的。

實驗中我們最終反復在測試集上測試我們的網絡,驗證我們的想法--網絡結果啊,超參取值啊,等等--這種做法會不會使最后的結果在測試集上過擬合呢?這是一個很深很困難的問題。目前來說,對於我們的這個實際問題,我們不准備擔心這個問題。我們依然使用上面的三個集合的方式。

目前我們看的是1,000個訓練集的時候過擬合的問題。如果訓練集有50,000個呢?我們來看下(其他參數不變)情況:

我們看到,相比1000個訓練集的情況,這次訓練集和測試集上的准確率比較接近:訓練集上的97.86和測試集上的95.33,相差2.53。這個比上面的差值17.73小了很多。過擬合任然存在,但是明顯減小了。一般來說,減小過擬合的方法之一就是增大訓練集規模。在足夠大的訓練數據上,即使是大型的神經網絡也很難過擬合。不幸的是,數據獲取很昂貴或很難,所以這個辦法實際中不一定行的通。

正規化(Regularization)

增加訓練數據是降低過擬合的辦法之一。有沒有其他的辦法來降低過擬合的發生呢?一個可能的方法就是降低網絡的規模。但是,大規模網絡比小規模網絡有更大的潛力,所以降低規模是我們不願意采用的辦法。

慶幸的是,即使對於指定的網絡和指定的訓練集,還是有其他辦法降低過擬合的。即所謂的正規化技術。這節中,我介紹一個叫權值衰減,也稱為則L2正規化的技術。L2正規化的思想,是在損失函數中添加一項正規化項,L2規則化之后的cross-entropy損失函數如下:

\[C=-\frac{1}{n}\sum_{xj}[y_j\ln a_j^L + (1-y_j)\ln (1-a_j^L)]+\frac{\lambda}{2n}\sum_{w}w^2. (85) \]

第一項是常規的cross-entropy損失函數,在這之后再添加了一項,是網絡中所有節點的權重平方和,乘以\(\lambda/2n\),其中\(\lambda>0\),稱為規則化參數,\(n\)是訓練數據量。稍后會討論下如何選擇\(\lambda\)的值。林外值得一提的是,規則化項中沒有包含神經元的biases,稍后我們也會討論這點。

當然,其他的損失函數也是可以規則化的,例如quadratic損失函數,規則化之后的形式如下:

\[C=\frac{1}{2n}\sum_{x}||y-a^L||^2+\frac{\lambda}{2n}\sum_{w}w^2. (86) \]

規則化后的損失函數,可以統一寫成如下形式:

\[C=C_0+\frac{\lambda}{2n}\sum_{w}w^2. (87) \]

其中\(C_0\)表示原始的未規則化的損失函數。

直覺上,規則化的效果是,在同樣的情況下使網絡更願意學習到小權重。只有在大權重顯著降低損失函數第一項的情況下,才會被學習到。換句話說,規則化可以看作是減小損失函數\(C_0\)與學習小權重值之間的一個權衡。兩邊的重要性通過\(\lambda\)控制:\(\lambda\)較小是我們傾向於最小化\(C_0\)\(\lambda\)較大時,我們傾向於小權重值。

為什么規則化的這個權衡能降低過擬合呢?原因很不明顯,但是確實很有效果。我們將在下一節中討論為什么有效果。首先我們通過一個例子來說明規則化確實降低了過擬合。

構造這樣一個例子,首先需要搞清楚怎么把我們的SGD(隨機梯度下降)算法應用到一個規則化的神經網絡中。具體來說,就是我們需要知道在規則話的神經網絡中,怎么去計算偏導數\(\partial C/\partial w\)\(\partial C/\partial b\). 從(87)公式可以推導出如下公式:

\[\frac{\partial C}{\partial w}=\frac{\partial C_0}{\partial w}+\frac{\lambda}{n}\quad(88) \]

\[\frac{\partial C}{\partial b}=\frac{\partial C_0}{\partial b}\quad(89) \]

其中\(\partial C_0/\partial w\)\(\partial C_0/\partial b\)兩項可以用上一章中介紹的BP算法計算。所以總體來說,計算規則化后的損失函數的梯度方式是:bp算法的計算結果偏導數后,權重的偏導數基礎加上\(\frac{\lambda}{n}w\),偏移的偏導數保持不變。所以梯度學習規則如下,偏移值的規則不變:

\[b\rightarrow b-\eta\frac{\partial C_0}{\partial b} \]

權重的規則變為:

\[w\rightarrow w - \eta\frac{\partial C_0}{\partial w}-\frac{\eta\lambda}{n}w\quad(91) \]

\[=(1-\frac{\eta\lambda}{n})w-\eta\frac{\partial C_0}{\partial w}\quad(92) \]

除了w乘上了個系數\(1-\frac{\eta\lambda}{n}\),沒有發生任何其他的變化。這個系數有時候被稱為權值衰減,因為它使權重變小了。初看,以為這個改變會使權值不斷逼近零。但這個想法是不是對的,因為如果會能使非規則化損失函數減小的話,其他項可能會使權值增加。

這些是梯度下降算法的情況。對於隨機梯度下降算法,情況又是怎么樣的呢?在非規則化隨機梯度下降算法中,我們用含有m個訓練數據的mini訓練集的梯度的平均值估計梯度\(\partial C_0/\partial w\),與此類似,規則化的隨機梯度下降算法變成如下:

\[w\rightarrow(1-\frac{\eta\lambda}{n})w - \frac{\eta}{m}\sum_{x}\frac{\partial C_x}{\partial w} \]

其中x是mini訓練集中的每個訓練數據,\(C_x\)表示么個訓練樣本的非規則化的損失值\(C_0\)。除權重衰減系數之外,這跟常規的SGD算法完全一致。最后,說明一下在規則化學習中,偏移值的學習規則,這個跟非規則化是完全一致的:

\[b\rightarrow b-\frac{\eta}{m}\sum_{x}\frac{\partial C_x}{\partial b} \]

其中x表示mini訓練集中的每個訓練數據。

我們來看下規則化對我們網絡性能的長生什么影響。我們將使用的網絡含有30個隱藏神經元,mini-batch大小為10,學習速率為0.5,損失函數使用cross-entropy函數。不同的是,這個我們采用了規則化,並且設置規則化參數\(\lambda=0.1\)。需要注意的是,程序中我們使用了變量名是lmbda,是因為python中,lambda是保留字。再次使用test_data而不是validation_data。嚴格來說我們需要使用validation_data,原因前面有說過。但是為了使結果能跟之前的很好對比,我還是決定使用test_data.你可以很容用替換成validation_data,你會發現結果很相似。

>>>import minist_loader
>>>training_data, validation_data, test_data =   
mini_loader.load_data_wrapper()
>>>import networks
>>>net=network2.Network([784, 30, 10], cost=network2.CrossEntropyCost)
>>>net.large_weight_initializer()
>>>net.SGD(training_data[:1000], 400, 10, 0.5,
evaluation_data=test_data,
lmbda=0.1,
monitor_evaluation_cost=True,
monitor_evaluation_accuracy=True,
monitor_training_cost=True,
monitor_training_accuracy=True)

跟非規則化網絡相同,訓練集上的損失值在不斷的減小:

不同是,這次測試集上的准確度持續增大,一直到前400次的學習:

很明顯,規則化降低了過擬合。並且提高了准確度,最高值達到87.1%,相比之前的最高值82.27%。400次的訓練之后,我們還能達到更高的准確率。看起來,規則化是我們的網絡泛化的更好,很大程度的降低了過擬合。

我們回到有50,000個訓練數據的訓練集呢?我們之前已經看到了,在這個大訓練集上的過擬合明顯下降了。那么規則化還會降低過擬合嗎?我們把訓練次數,學習效率,mini訓練集個數等超參保持不變。但是,我們的規則化參數\(\lambda\)需要改變一下,因為我們的訓練集數量從1000增加到了50,000,\(1-\frac{\eta\lambda}{n}\)會發生變化。如果\(\lambda\)保持不變,這個衰減系數的影響會很小。所以這次我們設置\(\lambda=5.0\).

然后我們開始訓練我們的網絡:

>>>net.large_weight_initializer()
>>>net.SGD(training_data, 30, 10, 0.5,
evaluation_date=test_data, lmbda=5.0,
monitor_evaluation_accuracy=True,
monitor_training_accuracy=True)

結果如下:

有很多好消息。首先,測試集上我們的准確率從95.49%(未規則化)提高到96.49%,這是個很大的提升。還有,我們也看到訓練集和測試集之間的准確率相差也顯著減少了,低於1%,雖然還是有差值還是很明顯,但是不可否認,我們很顯著的降低了過擬合。

最后,我們試試使用100個隱藏神經元,設置規則化參數\(\lambda=5.0\),准確率會怎么樣。這次我不會仔細的分析過擬合的細節,只是為了看一下,用我們學到的新技巧:cross-entropy算是函數以及L2規則化,我們最高能把准確率提高到多少。

>>>net=network2.Network([784,100,10], cost=network2.CrossEntropyCost)
>>>net.large_weight_initializer()
>>>net.SGD(training_data, 30, 10, 0.5, lmbda=5.0,
evaluation_data=validation_data,
monitor_evaluation_accuracy=True)

最后在驗證集上得到的准確率是97.92%。相比30個隱藏神經元,這事個很大的提升了。事實上,再次調試了一下,在運行60次訓練,\(\eta=0.1\),\(\lambda=5.0\)的情況下,我們突破了98%的大關,在驗證集上的准確率達到了98.04%。對於只有152行的代碼來說,這個成績很不錯了。

綜上所述,我們知道規則化可以降低過擬合以及提高准確率。事實上,規則化的作用不止這些。從實驗的經驗看來,我們多次訓練我們的MINIST網絡,使用隨機初始化的不同的權重,我們會發現非規則化的網絡容易陷入局部最小值,多次的實驗或得到差別較大的結果。相比之下,規則化的網絡,多次訓練的結果差不是很大。

到底發生了什么?直覺上來想,如果損失函數沒有規則化,那么在其他條件都一致的情況下,權重向量的長度極有可能會不斷增長,產生大權重值。隨着訓練的進行,最終可能會導致權重向量非常大。當權重向量長度比較長的情況下,由於梯度下降帶來的變化只會對權重向量產生微小的改變,這樣最后導致權重向量卡在一個方向上。我認為這種現象會使我們的學習算法很難探索權重向量空間,最終導致很難去發現損失函數的全局的最小值。

為什么規則化會降低過擬合

通過實驗經驗,我們看到規則化確實能降低過擬合。很令人振奮,但是不幸的是,原因很不明了。人們常用來解釋的故事是這樣的:某種意義下,小權重值復雜度低,所以對數據給予的解釋也就更簡單和有力。這是一個非常簡要的故事,然而包含了一些看起來很奇妙和神秘的色彩。我來仔細分析一下這個故事。假設我們有一批數據需要建模:

毫無疑問,我們在研究現實世界的現象,\(x,y\)代表現實世界的數據。我們的目標是建立一個模型,通過\(x\)預測\(y\)。我們可以使用神經網絡來建模,但是我准備使用功能簡單的模型:多項式模型,使用x的多項式建模y。這么做的原因是,多項式模型跟透明。當我們理解了多項式的例子,我們在去討論神經網絡。圖中有十個點,我們能找到唯一的9次多項式精確的建模,\(y=a_0x^9+a_1x^8+a_2x^7+a_3x^6+a_4x^5+a_5x^4+a_6x^3+a_7x^2+a_8x^1+a_9\),多項式的圖如下所示:

這個模型可以精確的表示這些點。但是,我們也有是用線性的模型大致的切合一下這些點,\(y=2x\):

哪個模型更好呢?哪個更接近事實呢?對於顯示世界的其他數據,哪個會泛化的更好呢?

這些都是非常難的問題。事實上,沒有更多的數據的前題下,以上的三個問題都無法回答。我們考慮下兩種可能性:(1)9次多項式真的描述了現實世界的現象,會很完美的泛化到其他是數據,(2)線性的模型是對的,模型沒有完美描述數據點的原因是一些噪音造成的,比如測量誤差等等。

沒辦法判斷哪種可能性是對的。(或則,存在第三種可能性)。邏輯上說,兩種都可能是對的。並且兩個模型存在不可忽視的差別。雖然在我們 的這個小的例子里,兩個模型的差別不是很大。但是,假設x的值非常大呢,比例子中出現的x的值都大很多呢。兩個模型預測的y值就回相差很大。因為9次冪多項式的值會主要由\(x^9\)項決定。更線性模型預測的值,相差會非常大。

存在這么一個觀點,在科學上我們應該傾向於簡單的解釋,除非迫不得已。當我們找到能解釋我們很多數據的簡單模型的時候,我們會大喊"找到了"。畢竟,簡單的模型不太可能僅僅是巧合。並且,我們猜測,這個模型肯定是描述了現象中的一些隱藏的事實。比如這個例子,\(y=2x+noise\)看起來比\(y=a_0x^9+a_1x^8+...\)更簡單。所以我們猜測\(y=2x+noise\)描述了一些影藏的事實。從這個角度出發,9次冪的模型只是學習到了一下誤差而已,泛化到其他的數據點的時候,會出錯的,而線性模型會有更好的泛化性。

我們來看一下這個觀點對神經網絡意味着什么。假設我們的網路大部分權重值比較小,比如在規則化的網絡中比較常見的一樣。小權重值意味着,我們的網絡的行為不會因為小部分的輸入的改變而改變太大。這就使得我們規則化的網絡很難被數據中的噪音影響到。也就是單個數據現象很難影響到網絡的輸出,規則化的網絡會更在意數據中普遍存在的現象。於此對比,權重值比較大的網絡中,輸入的微小改變會導致網路輸出的很大變化。啥意義非規則化的網絡很用大的權重學習出一個復雜的模型,模型中包含了很多訓練數據中的噪音。簡單說就是,規則化的網絡會學習出相對簡單的模型,基於訓練數據中的普遍模式,而拒絕學習數據中的噪音。這就迫使網絡去真正學習數據,從而更好的泛化。

這種對簡單模型的傾向性應該讓你感到焦慮。人們通常把這個思想叫做"Occam's Razor",而且把它當做一個科學的原則狂熱的應用它。但是,它當然不是一個通用的科學原則。傾向於簡單模型沒有任何邏輯原因。事實上,有時候復雜模型結果是對的。

我來介紹兩個這樣的例子,例子中比較復雜的模型是正確的。1940年物理學家Marcel Schein宣布發現了一種新的自然粒子。他當時就職的公司General Electric,非常高興並對外公布了這個想發現。但是物理學家Hans Bethe表示懷疑。Bethe拜訪了Schein,觀察了Schine的新粒子的照片。每一張照片中Bethe都發現了一些問題,並且建議丟棄照片。但是有一張照片沒發現任何問題。但是Bethe認為這個可能是個統計上的僥幸。Schein說:"但是這個僥幸的概率也是1/5啊。"最后Schein說:但是在我所有的照片里,每一個好的照片,你都用了一個不同的解釋,但是我用了單獨的一個解釋了所有的現象。他們是新粒子。Bethe回應說:『是的,但是你的一個解釋是錯誤的,我的五個解釋是對的』。后續的工作確認了Bethe是對的,Schein的新粒子根本不是新的粒子。

第二個例子是,1859年,天文學家Urbain Le Verrier發現水星的軌道跟牛頓萬有引力理論預測的少有偏差。變差非常非常小,當時出現各種解釋,最后大家認為牛頓定律大致上是對的,但是需要一些小的修正。1916年,愛因斯坦宣布她的相對論理論,能很好的解釋這個偏差,這個相對論理論更牛頓定律有着根本上的不同,而且基於更復雜的數學理論。盡管相對論理論的復雜性,在今愛因斯坦的相對論理論的正確性是唄普遍接受的,牛頓重力理論,甚至它的修正后的理論,是錯的。部分是因為愛伊斯坦理論解釋了很多牛頓理論無法解釋的現象。而且,愛伊斯坦理論預測了很多牛頓重力理論沒有預測到的現象。但是在早先的時期,這些都沒有被注意到。如果只用理論的簡單新來判斷,修正的牛頓理論相比愛伊斯坦的相對論很明顯是更簡單的。

兩個故事給到我們三個啟示,首先,決定兩種模型哪個更簡單,可能是個很微妙的更業務邏輯有關的事情。其次,即使我們能決定了哪個更簡單,這個原則也得非常小心的使用。再次,模型的測試不是一個簡單的事,比如它在預測新現象時候的表現,在新環境下的表現,等等。

也就是說,我們要保持謹慎,要知道規則化網絡比非規則化網絡能更好的泛化只是一個經驗性的事實。所以本書的后續中,我們有經常是用規則化技術。我引用上面的例子是想說明為什么沒有人能總結出一套令人信服的理論去解釋為什么規則化會有利於網絡的泛化。事實上,學者們一直致力於使用新的規則化方法,比較他們的差別,並且試圖理解他們為什么會表現好或則壞。你可以把規則化當做一個組裝機。雖然它會經常有幫助,但是對於到底規則化發生了什么,我們沒有一個完整的令人滿意的系統的理解,只是一些不完整的啟發式的經驗法則。

還有一些更深層次的事情。關於泛化到底怎么發生的。規則化給了我們一個魔法棒,幫助我們網絡更好的泛化,到那時沒給我我們一個關於泛化如何工作的原則上的解釋,更別提最好的泛化方式了。

這個問題尤其讓人惱火的是,每天的生活中,我們人類泛化的能力非常好。給小孩子幾張大象的照片,他們很快就能認識大象。當然,他們也會偶爾犯錯誤,把犀牛當做大象。但是總的來說,這個處理的過程還是非常准確的。我們擁有一個系統--大腦--含有巨大數量的自由參數。看多一張或則極少的訓練數據圖片后,就能很好的識別其他的圖片。我們的大腦可以說是規則化的非常好。到底怎么做到的呢。目前我們不得而知。我預期在不遠的將來,我們會開發出強有力的規則化技術,是得神經網絡能在很小的數據集泛化的非常好。

事實上,我們都網絡泛化的比期待的要好。一個有100個隱藏神經元的網絡包含80,000個參數。但是我們的訓練集只有50,000個圖片。這就相當於我們用一個80,000次冪的多項式去擬合50,000個數據點。無論如何,我們的網絡應該會過擬合的非常嚴重。但是實際上,如前面所看到的那樣,我們的網絡泛化的很不錯。為什么會這樣呢?具體原因我們還不太理解。有一種猜測:多層網絡結構中的梯度下降算法有一種自我規則化的效果。這很幸運,同時令人不安的是我們不理解為什么會這樣。單同時,在允許的情況下,我們還是會采用了實用的規則化。我們神經網絡還是不錯的選擇。

本節的最后,我來解釋一下一個遺留的問題:為什么L2規則化沒有限制偏移值。毫無以為,通過簡單的修改就可以是規則化程序起規則化偏移值。歷史經驗看來,這么做對結果改變不是很明顯,所以沒有規則化偏移值的慣例。值得一提的是,大的偏移值,並不會像大的權重值那樣,使網絡對輸入敏感。所以我們不必擔心大的偏移值會使網絡學習到數據中噪音。相反,大偏移值會帶來一些好處,比如需要的時候,大偏移值會讓網絡更容易飽和。總得來說,我沒一般不會在規則化中對偏移值進行限制。

規則化的其他技術

L2規則化之外還有很多規則化的技術,多到難以一一舉例,本節中我主要的介紹三種其他的降低過擬合的技術:L1規則化,dropout,人工增大訓練數據集。我們不會讓之前那樣深入的學習這些技術,相反,我們的目標是熟悉主要的思想,了解歸一化技術的多樣性。

L1規則化:我們在非歸一化損失函數后,加上所有權重的絕對值之和:

\[C=C_0+\frac{\lambda}{n}\sum_{w}|w|\quad(95) \]

直觀上,這個跟L2規則化很類似,懲罰大權重,是網絡傾向於小權重。顯然,L1規則化項跟L2規則化項不一樣,所以它的效果也跟L2不太一樣。我們來理解一下L1跟L2的網絡行為有哪些不同之處。

我們來看下(95)的偏導數:

\[\frac{\partial C}{\partial w}=\frac{\partial C_0}{\partial w}+\frac{\lambda}{n}sgn(w)\quad(96) \]

其中\(sgn(w)\)表示\(w\)的符號,也就是,\(w\)為正則取值為\(+1\)\(w\)為負則取值為\(-1\)。有了這個公式我么很容易推出權重的更新規則:

\[w\rightarrow w=w-\frac{\eta\lambda}{n}sgn(w)-\eta\frac{\partial C_0}{\partial w}'\quad(97) \]

其中,我們可以通過mini-batch的估計出\(\partial C_0/\partial w\).我們來跟L2規則化的權重更新公式比較一下:

\[w\rightarrow w'=w(1-\frac{\eta\lambda}{n})-\eta\frac{\partial C_0}{\partial w}\quad(98) \]

兩個公式可以看出,兩種規則化方法都使權值減小。跟我們的直觀感覺是一致的。但是兩者對權值縮小的方式是不一樣的。L1中,權值以一定的幅度減小到零。L2中,權值減小的幅度是跟權值大小成比例的。所以,當一個特定的權值絕對值\(|w|\)比較大,則L1縮減權值的幅度要遠小於L2.相反,\(|w|\)比較小時,L1縮減的幅度又遠大於L2。最后的效果就是,L1使網絡集中在小范圍的比較重要的權重和連接上,而使其他的權重趨向於零。

上面的討論中,我忽略了一點,那就是偏導數\(\partial C/\partial w\)\(w=0\)的時候未定義。原因是函數\(|w|\)\(w=0\)處不可倒。不過沒關系,我在\(w=0\)的時候,使用非規則化的梯度更新規則。這事沒問題的,直觀上來看,規則化的效果是減小權值,顯然潛質為零的時候沒辦法再減小了。具體點說,就是公式(96)和(97)中,我們約定\(sgn(0)=0\).如此,則L1規則化后網絡的更新規則就變得很簡單完整。

Dropout:Dropout是一個根本上不太一樣的規則化技術。與L1和L2不同,Dropout並不會修改損失函數。相反,droupout修改網絡本身。在研究dropout的結果和原理之前,我我們來介紹一下它的工作機制。

假設我們要訓練一個網絡:

具體來說,假設我們有一個訓練輸入\(x\)以及一個需要的輸出\(y\)。正常情況下,我們通過正向傳入\(x\),以及逆向傳到的梯度來學習。在加入了dropout之后,訓練過程略有不同。開始訓練前,我們隨機的挑選一些隱藏的神經元,零時的刪除這些神經元,輸入輸出保持不變。我們得到如下的網絡。值得一提的是,臨時刪除的神經元還在,只是唄臨時刪除:

我們正向傳輸輸入\(x\),然后逆向傳輸結果,通過整個修改過的網絡。mini-batch中所有數據都如此處理完之后,我們更新下權值和偏移值。然后我們不斷重復這樣一個過程:恢復之前被零時刪除的神經元,然后選擇一批行的神經元刪除,然后在訓練,更新權值和偏移,如此不斷。

這樣一遍一遍的訓練后,網絡學習到了一組權值和偏移。當然,這些權值是在網絡的一般隱藏神經元唄臨時除去的情況下學習到的。當我們運行完整網絡的時候,會有一倍多的神經元被激活。所以我們吧隱藏神經元的權值減半。

這種dropout的過程看起來很奇怪。這個過程怎么能起到規則化的左右呢?為了解釋這個,我們暫時先不想dropout過程,想象一下常規的訓練過程。具體的,比如我們用同一批數據訓練多個網絡。當然,每個網絡的初始化不一樣,這樣訓練的結果是每個網絡可能給出不一樣的結果。我們可以加上一個例如投票的機制,去決定最終的結果。比如我們有5個網絡,其中3個輸出的結果是"3",則結果極有可能是"3",另外兩個網絡極有可能是錯誤的。這種去平均的方案很多時候被證明是非常有效的,對防止過擬合。原因是可冷不同的網絡以不同的方式過擬合了,平均一下輸出的結果可以減少這種過擬合。

這個跟dropout有什么關系呢?直覺上來說,每次漏掉不同的神經元,很類似每次訓練不同的神經網絡。所以dropout的過程,很像是大量的不同網絡的結果的平均值。不同的網絡會有不同的過擬合,所以,dropout網絡極有可能降低過擬合。

在早期的一個用到dropout的論文了有一個啟發是的解釋:dropout降低了神經元之間的復雜的相互依賴,因為每個神經元不能依賴特定的其他神經元。因此,迫使網絡學習到更魯棒的特征。換句話說,如果我們把網絡用在預測上,dropout會使網絡對部分特征的缺失表現得更健壯。在這點上,dropout有點類似L1和L2,L1,l2通過減小權值,使得網絡對單個網絡的單個鏈接的確實表現的更健壯。

當然,對dropout的真正的評價是,它已經成功的提升了很多神經網絡的表現。這篇論文介紹了這個應用在很多不同任務上的技術。對於我們來說,有人把這技術應用到的MINIST的數字分類任務上。論文指出,最高達到的准確率是98.4%,通過用L2規則化的改進,他們把准確率提高到了98.7%.類似的,圖片和語音處理等任務上,也取得了可觀的成績。在大數據量,深度學習領域,過擬合問題比較突出,dropout技術很有用處。

人工擴展訓練集:我們看到,用1000個訓練圖片時,我們的准確性降到了80%多。一點也不奇怪,因為少的訓練集表示網絡接觸到的手寫體樣本就很少。我們用不同數量的訓練集來訓練我們有30個隱藏神經元的網絡,看看結果如何。設定mini-batch大小為10,學習速率\(\eta=0.5\),規則化參數\(\lambda=5.0\).訓練集全量的時候,設定訓練次數為30,隨着訓練集的減小,我們適當增大訓練次數。為了保持權重衰減系數一樣,全量數據我們設定\(\lambda=5.0\),隨着數據量的減小,適當減小\(\lambda\).

可以看到,分類的准確度隨着訓練集的增大而顯著提高。也許,這個趨勢還會繼續。當然,上圖顯示我們准確率好像有點飽和了。看下圖:

很明顯,曲線還是在上升的。也許說明,我們如果用百萬或則千萬這樣大很多的訓練集來訓練的話,即使我們的小型網絡也會表現好很多。

使用更多的訓練集是非常好的主意。但實際上,數據是很昂貴的,甚至是不可能的。然后,有另一種方式來擴展數據集。比如,在minit圖片識別的例子里,5這張圖片:

我們把它旋轉一定的角度,比如說,15度:

任然還可以看出是同一個數字,但是在像素級別上,已經完全不同於minit訓練集中的數據。可以相信的是,添加這個數據后可以提升網絡的學習效果。而且,顯然我們不止可以添加一張圖片,我們可以吧現有訓練集中的圖片全部旋轉后再添加回來,用這個擴展之后的訓練集來提升網絡效果。

這個方法非常有用並且應用廣泛。我們來看下幾篇論文,文中吧這個方法的幾個變種方法應用在了minist問題上。其中一篇設計的網絡了結構跟我們用過的網絡很類似,前向傳播網絡,800個隱藏神經元,使用的cross-entropy損失函數。在標准的minist訓練集上訓練之后,在測試集上的分類准確率達到了98.4%。隨后他們擴展了訓練集,不僅僅使用了旋轉,還用到了拉伸,位移等操作,使准確率提升到了98.9%。他們還通過所謂的"彈性變形"處理--一種模擬手部肌肉的隨機波動的圖片處理方法,使准確率提升到更高,99.3%。他們使用這種在真是手寫中出現的變化,擴展了網絡的學習經驗。

這個思路下的很多方法可以用來提高不僅僅是手寫體識別的很多機器學習任務。總體是原則是使用模擬顯示世界的各種變化因素來擴展訓練集。這些方法不難想象。比如我們要訓練一個網絡識別語音。即使有各種例如背景噪音的干擾的情況下,人類也能識別語音。所以我們可以使用添加背景噪音的方式擴展訓練集。也可以用加快或減慢語音的方式擴展訓練集。這些方式不總是有用的--比如,相比較添加噪音的方式,通過去添加降噪過濾器的方式,可能更有效果。當時,擴展訓練集的方法,依然是值得嘗試的方法。

大數據和比較分類准確率的意義:我們看一下神經網絡的准確率是如何隨着訓練集的規模變化的:

假設我們不用神經網絡,還是用其他機器學習方式分類圖片,如果第一章中提到的SVM模型。不用擔心自己不了解SVM,跟第一章的例子類似,我們不必理解SVM的細節。相反,我們只要會用scikit-learn庫提供的SVM就行了。線面對比一下SVM跟神經網絡的差別:

圖中最令人震驚的事,可能是無論訓練集多大,神經網絡模型的表現都優於SVM。但是我們不用過多的解讀這個,因為我們使用的是scikit-learn的SVM的默認設置,而神經網絡這邊我們已經做了很多的優化。一個更不明顯但是有趣的現象是,50,000個圖片訓練出來的SVM模型(94.48%)比5,000個圖片訓練出來的神經網絡(93.24%)准確率更高。換句話說,更多的訓練數據可以彌補所采用的模型產生的區別。

無論是在做開發還是在讀別人的論文,腦中要謹記着這一點。許多論文集中精力提高標准測試數據集上的表現。一種標准的研究成果聲明形式是:"我們的技術在標准的測試Y上提升了x%".這些聲明的確很有趣,但是必須知道的是,這些是否實在只用特定訓練集的情況下取得的。想象這樣一個例子,人們穿件測試數據集,設定研究獎金。很有可能這些提升是因為新的數據集而不是算法的提升。在實際的應用中,好的數據和好的算法都是我們想要的。尋找好的算法很好,需要警惕的是,在尋找好的算法過程中滑到了實際是在尋找好的數據的行為里。

總結:我們已經完成了對過擬合和規則化的話題,當然,后面我們會再次回到這個話題。像我多次提到的一樣,過擬合多神經網絡是一個主要的問題之一,尤其是我們的計算機的計算能力越來越強,網絡的規模越來越大。所以對強大的規則化技術的需求非常巨大,現在這事一個非常活躍的研究領域。

權值初始化

當我們創建神經網絡的時候,我們不得不選擇一個方式去初始化權值和偏移值。目前為止,我們用的初始化的方式,都是第一章中簡要說明的一種方式,使用的是獨立高斯分布的,平均值為0,標准差為1的方法產生的。這個方式表現還不錯。我們回顧一下這個過程,看看能否找到更好的方式初始化,以幫助網絡的學習。

結論是,我們能比標磚高斯方式做的好很多。為什么呢?我假設我們的網絡有大量的輸入,比如1000。再假設我們用標准高斯分布來初始化輸入層與第一隱藏層的鏈接。從現在開始,我將主要精力集中在輸入層與第一隱藏層的第一個神經元之間的鏈接上,而忽略其他鏈接:

簡單起見,我們假設輸入神經元的輸入中,一般是0一般是1。更通用的情況后面再討論,這里先從簡單的例子里理解一下精髓。輸入值的加權和為\(z=\sum_{j}w_jx_j+b\).因為有一般的輸入為0另一半為1,所以\(z\)的取值符合均值為0標准差為\(\sqrt{510}\approx22.4\)的高斯分布。也就是,\(z\)與一個非常寬的高斯分布:

從圖中可以看出,\(|z|\)有很大的概率取值大於1,也就是\(z\geq1\)或則\(z\leq1\).如此,則輸出\(\sigma(z)\)的值則會非常接近1或則0。這樣就意味着這個影藏層的神經元飽和了。然后會導致什么呢,正如我們所知道的,會導致神經元學習的特別慢。這個問題類似前面章節學習到的輸出層的神經元飽和問題,但是我們通過換損失函數解決了這個問題,但是不幸的是,換損失函數的方式不能解決這個問題了。

我們討論的是第一層影藏層的神經元會遇到的問題,類似的,其他影藏層的神經元也會有類似問題,如果權重用高斯分布去初始化的話。

有沒有好的初始化方法能避免這種飽和的問題呢?假設我們有一個神經元,有\(n_{in}\)輸入權值。我們應該用均值為0標准差為\(1/\sqrt{n_{in}}\)的高斯分布去初始化它們。就是我們把高斯分布壓扁,降低神經元飽和的概率。我們繼續用均值為0標准差為1的高斯分布初始化偏移值,原因后面再說。這種方式初始化之后呢,加權和\(z=\sum_jw_jx_j+b\)的值還是符合一個高斯分布,但跟之前相比較但是圖像會很尖。假設還是上面的那個例子,500個輸入為0,另外500個輸出為1。很容易算出,\(z\)符合均值為0標准差為\(\sqrt{\frac{3}{2}}=1.22\)的高斯分布。這個圖像比之前的尖銳很多:

這樣的一個神經元飽和的概率打打減小,也就不會有學習太慢的問題。

上面說過,用均值為0標准差為1的高斯分布隨機數初始化偏移值。事實上,對於飽和的問題來說,怎么初始化偏移值關系不大,有些人會把所有的偏移值初始化為0,依賴梯度下降去學習偏移值。因為關系不大,所以我們繼續用之前的方法初始化偏移值。

我們來用MNIST的例子比較一下新舊權值初始化方式的差異。跟以前一樣,我們使用30個隱藏神經元,mini-batch大小為10,規則化參數\(\lambda=5.0\),損失函數使用cross-entroy函數。為了讓比較結果明顯些,我們吧學習步長從\(\eta=0.5\)降低到\(\eta=0.1\).首先使用老的方式初始化權值:

>>>import minist_loader
>>>training_data,validation_data,test_data=
minist_Loader.load_data_wrapper()
>>>import network2
>>>net=network2.Network([784,30,10], cost=network2.CrossEntropyCost)
>>>net.large_weight_initializer()
>>>net.SGD(training_data,30,10,0.1,lmbda=5.0,
evaluation_data=validation_data,
monitor_evaluation_accuracy=True)

然后使用新的方式初始化權值,方法更加簡單,因為network2默認的初始化方式就是使用新方法。忽略net.large_weight_initializer()就可以:

>>>net=network2.Network([784,30,10],cost=network2.CrossEntropyCost)
>>>net.SGD(training_data,30,10,0.1,lmbda=5.0,
evaluation_data=validation_data,
monitor_evaluation_accuracy=True)

實驗結果如下:

兩種方式,最后達到的分類准確率幾乎一樣,都超過96%。但是新的方式,很快達到了這個水平。並且第一批訓練完后的准確率就達到了93%,而老的方式才到87%。看起來,新的方式讓我們從一個更好的出發點,更快的得到好的結果。使用100個隱藏神經元也有類似的結果發生:

這個例子里,最后兩個曲線並沒有完全重合。但是我得實驗表明,多訓練一段事件后,連個曲線還是會重合的。基於這些實驗的結果看來,新的方式只加速了訓練過程,並沒有提升最后的訓練效果。然而在第四章中,我們會看到一些例子,不但速度得到提升,最后的訓練效果也得到了提升。

\(1/\sqrt{n_{in}}\)初始化方式改進了網絡的學習。基於這個基本的思路,很多權值初始化的方式被提出啦,我們就不一一列舉了,因為這個\(1/\sqrt{n_{in}}\)方式對我們的例子來說效果已經很好了。感興趣的話,可以看一下Yoshua Bengio的2012的論文的14,15頁。

手寫數字識別:代碼部分

基於本章討論的思想,我們來實現一個新版本的神經網絡網絡:networkd2.py.

如何選擇神經網絡的超參

目前為止,我都沒有解釋怎么去選擇侏儒學習速率\(\eta\),規則化系數\(\lambda\)等超參的值。我只是用了幾個值,效果還不錯。實際中,在應用神經網絡解決實際問題的時候,可能很難去找到好的超參的取值。想象一下,我們第一接觸MINIST問題,並開始着手解決,對使用什么超參一無所知。假設,我們的第一次實驗中,使用了30個隱藏神經元,mini-batch大小為10,損失函數用cross-entroy,訓練了30批次。但是學習效率\(\eta=10.0\),規則化參數\(\lambda=1000.0\).如下:

>>> import mnist_loader
>>> training_data, validation_data, test_data = \
... mnist_loader.load_data_wrapper()
>>> import network2
>>> net = network2.Network([784, 30, 10])
>>> net.SGD(training_data, 30, 10, 10.0, lmbda = 1000.0,
... evaluation_data=validation_data, monitor_evaluation_accuracy=True)
Epoch 0 training complete
Accuracy on evaluation data: 1030 / 10000

Epoch 1 training complete
Accuracy on evaluation data: 990 / 10000

Epoch 2 training complete
Accuracy on evaluation data: 1009 / 10000

...

Epoch 27 training complete
Accuracy on evaluation data: 1009 / 10000

Epoch 28 training complete
Accuracy on evaluation data: 983 / 10000

Epoch 29 training complete
Accuracy on evaluation data: 967 / 10000

我們網絡的准確率比瞎猜好不了多少。

你可能會說:"這個簡單,只要降低學習速率和規則化參數"。但是不幸的是,你並不先驗的知道需要調整這兩個超參。也許不管怎么選超參,30個隱藏神經元的網絡都不行呢?也許我們需要的是100個神經元?也許300個?也許多個隱藏層?也許也別當時定義輸出?也許網絡確實在學習,只不過需要更多的訓練?也許mini-batch太小?也許需要換回平方差損失函數?也許需要不同的權值初始化方式?等等等等。很容易迷失在超參空間里。特別是,你的網絡規模很大,訓練數據很多。需要幾個小時幾天甚至幾周才能出結果。它會打擊你的自信心。也許神經網絡不適合這個問題?也許你改辭職去養蜂?

本節里,我會介紹一些設置超參的方法。當然,不會覆蓋所有的超參優化的方式。這個是個很大的話題,並且也沒有被王全解決的話題。而且在實踐者當中還沒有達成一致。總是有別方式去更好的初始化你的網絡的超參。讓我門從下面的接個方式開始吧。

一般策略:應用神經網絡解決問題的時候,第一挑戰就是得到非平凡的學習。也就是准確率高於隨機猜測。我們來看下一些策略。

例如,你第一次拿到MINIST問題。你自信滿滿,熱情高漲,但是卻像上面的例子一樣徹底失敗了,你感到一點沮喪。接下來要做的是降低問題的規模。挑出訓練集和驗證集中的所以0和1的圖片,然后試着訓練一個網絡去區分這兩個數字。不僅僅是因為這個問題天然的比區分是個數字簡單,還因為使訓練集的規模降低了80%,使訓練速率加快了5倍。使你可以更頻繁的實驗,試圖去理解怎么建立一個好的網絡。

你還可以通過降低網路的規模去加快網絡的學習。比如[784,10]比[784,30,10]訓練起來會快很多,稍后還可以把隱藏層加回來。

你還可以通過加快監視的頻率來加速實驗的過程。在network2.py中我們在沒個訓練批次結束后查看訓練效果。在我的電腦上,50,000個圖片一側批次,網絡結構為[784,30,10]的時候,大概需要十秒鍾左右。當然,十秒鍾不是很久。但是如果你想是實驗十幾個超參的取值,這就會變得很煩。實驗上百個超參就回更加繁瑣了。我們可以更快的獲取反饋結果,比如沒1,000個圖片。甚至,不用整個10,000個驗證集去驗證訓練效果,用更小的驗證集去驗證。關鍵點是網絡燕郊看到了足夠多的圖片,拿到一個不錯的大概的效果的估計。當然,我們的程序network2.py現在沒有做這種監視。為了說明的需要,我們吧我們的訓練集降低到1000.

>>> net = network2.Network([784, 10])
>>> net.SGD(training_data[:1000], 30, 10, 10.0, lmbda = 1000.0, \
... evaluation_data=validation_data[:100], \
... monitor_evaluation_accuracy=True)
Epoch 0 training complete
Accuracy on evaluation data: 10 / 100

Epoch 1 training complete
Accuracy on evaluation data: 10 / 100

Epoch 2 training complete
Accuracy on evaluation data: 10 / 100
...

准確率還是很低。但是有一個勝利就是,我們現在沒一秒能拿到一個反饋。這樣就可以更快的實驗各種超參的取值了。升值同步的實驗各種超參。

上面的例子里,我們的\(\lambda=1000.0\),因為我們降低了訓練集的規模,我們確實需要減小\(\lambda\)來保持權重衰減值一致。也就是\(\lambda=20.0\).這么做之后得到下面的地址:

>>> net = network2.Network([784, 10])
>>> net.SGD(training_data[:1000], 30, 10, 10.0, lmbda = 20.0, \
... evaluation_data=validation_data[:100], \
... monitor_evaluation_accuracy=True)
Epoch 0 training complete
Accuracy on evaluation data: 12 / 100

Epoch 1 training complete
Accuracy on evaluation data: 14 / 100

Epoch 2 training complete
Accuracy on evaluation data: 25 / 100

Epoch 3 training complete
Accuracy on evaluation data: 18 / 100
...

啊哈,我們得到一個信號。雖然不是一個非常好的信號,但是是一個信號。我們可以修改超參去達到更好的效果。也許我們會猜我們需要提高學習效率。(你可能看出來了,這是個很傻的猜測,原因稍后討論,暫時先更隨我的思路.)所以我們先實驗\(\eta=100.0\)

>>> net = network2.Network([784, 10])
>>> net.SGD(training_data[:1000], 30, 10, 100.0, lmbda = 20.0, \
... evaluation_data=validation_data[:100], \
... monitor_evaluation_accuracy=True)
Epoch 0 training complete
Accuracy on evaluation data: 10 / 100

Epoch 1 training complete
Accuracy on evaluation data: 10 / 100

Epoch 2 training complete
Accuracy on evaluation data: 10 / 100

Epoch 3 training complete
Accuracy on evaluation data: 10 / 100

...

效果不好。表明我們的猜測是錯誤的,問題不是出在學習速率太低上。所以我們實驗\(\eta=1.0\)

>>> net = network2.Network([784, 10])
>>> net.SGD(training_data[:1000], 30, 10, 1.0, lmbda = 20.0, \
... evaluation_data=validation_data[:100], \
... monitor_evaluation_accuracy=True)
Epoch 0 training complete
Accuracy on evaluation data: 62 / 100

Epoch 1 training complete
Accuracy on evaluation data: 42 / 100

Epoch 2 training complete
Accuracy on evaluation data: 43 / 100

Epoch 3 training complete
Accuracy on evaluation data: 61 / 100

...

好多了!我恩繼續調整各個超參,逐漸的提高效果。一旦我們發現了一個更好的\(\eta\),我們接着去找更好的\(\lambda\).然后去實驗更復雜的網絡,比如加上10個隱藏神經元。然后再繼續調整\(\eta\)\(\lambda\).然后增加20個隱藏神經元,然后再繼續跳轉\(\eta\)\(\lambda\).如此不斷的繼續,在每個階段,用我們的削減了的驗證集,用這種不斷進化的方式不斷提高效果。隨着過程不斷繼續,等待結果的事件越來越長,我們可以逐漸的降低監視的頻率。

這個一般的策略看起來很可靠。但是我想在說一下超參調整的初始狀態,就是找到這些超參讓網絡學習到任何有用的東西。事實上,上面的討論已經很樂觀了。處理什么都沒學到的網絡的過程會非常的令人沮喪。你可能好幾天去調整超參,卻得不到一點有意義的回應。所以我想再次強調的是,在調參的初幾階段,你應該保證你能快速的得到反饋。直覺上看起來簡化問題和網絡結構會拖慢我們的進度。事實上,它會加快我們的進度,因為你們更快的發現有用信號的網絡。一旦你得到了這樣的信號,你就可以通過調參更快的取得進展。正如許多事情一樣,萬事開頭難。

以上是一個一般策略。下面我們來看一下設置超參的具體建議。我將重點討論學習速率\(\eta\),L2規則化參數\(\lambda\),以及mini-batch的大小。事實上,很多規則同樣適合於其他的超參,包括網絡結構,其他的規則化,以及本書中后面將提到的其他超參,比如co-efficient.

學習速率:假定我們用三種學習速率來學習MINIST問題,分別是\(\eta=0.025\),\(\eta=0.25\)\(\eta=2.5\).其他的超參不變:20批次,mini-batch大小為10,\(\lambda=5.0\),50,000圖片的訓練集。結果如下:

\(\eta=0.025\)的時候,損失一直下降,直到最后一批次的訓練。\(\eta=0.25\)的時候,損失開始的時候下降,但是在20批次之后,就接近飽和了,變化很小,看起來是一些震盪。最后,\(\eta=2.5\)的時候,從開始就震盪,而且震盪更大了。為了理解震盪的原因,我們來回憶一下隨機梯度算法,這個算法的目標是逐步的使我們下降到損失函數的谷底。

如果\(\eta\)太大,則每一步的歩長比較大,很有肯能越過最低值,導致損失值的上升。這個很有可能就是當\(\eta=2.5\)的時候損失值震盪的原因。\(\eta=0.25\)的時候,開始的時候我們走向谷底,接近谷底之后,震盪開始出現了。\(\eta=0.025\)的時候,前30批次的訓練中都不存在震盪的問題。當然,\(\eta\)值太小了又會有另一個問題,就是損失下降太慢的問題。一個更好的方法就是前20次設置\(\eta=0.25\),之后設置\(\eta=0.025\).這個可變學習效率的方法稍后討論,暫時我們先關注一下如果找到更好的單一的學習速率值。

記住這張圖,我們來這樣設置\(\eta\)。首先,我們估計\(\eta\)的一個閾值,使訓練集上的損失立即開始下降,而不是震盪或則增加。這個估計不需要很准確。你可以從\(\eta=0.01\)開始。如果開始幾次訓練中損失下降,你依次嘗試\(\eta=0.1,1.0\)。直到找個一個\(\eta\)值使算是震盪或則增加。相反,如果開始幾次訓練中震盪或則上升,則一次嘗試\(\eta=0.001,0.0001\),知道找到一個\(\eta\)值使得損失在開始幾次訓練中下降。這個步驟會給我們一個閾值的大概猜測。可以優化你的選擇,比如選一個最大的使損失開始幾次就下降的\(\eta\),比如\(\eta=0.5\)或則\(\eta=0.2\)(這個值不需要特別的准確)。大概估計出\(\eta\)的閾值。

顯然,實際使用的\(\eta\)值不應該大於這個閾值。事實上,這個值應該比閾值小2倍左右。這樣的學習速率可以訓練很多批次,而且不會導致學習緩慢的問題。MINIST的數據中,按照這個原則找到一個估計的閾值是0.1,優化之后,我們估計閾值\(\eta=0.5\).按照上面的方法,建議使用\(\eta=0.25\).事實上,我們發現前30批次的先練中,\(\eta=0.5\)沒有帶來問題。

這些看起來都很直接。但是,使用訓練損失去選擇\(\eta\)貌似跟我之前說的一些內容沖突了:使用削減的驗證集去演化超參。事實上,我們會用驗證集准確率去選擇規則化參數,mini-batch大小,網絡結構比如層數和神經元數量。為啥學習效率跟其他的不一樣呢?坦白說,這事我本人的喜好。原因是其他的超參的目標都是去改進驗證集上的最后的分類准確率,所以使用驗證集去選擇這些超參就很合理。然而學習速率只是順帶的影響最后的准確率,它的主要目的是控制梯度下降的步長。所以監視訓練損失是檢測步長是否太大的最好方法。這是個個人的偏好。實際上你使用哪個標准不會有太大的區別。

使用早停方式決定訓練次數:正如本章前面討論的,早停的意思是在沒次訓練完成后,我們計算驗證集上的准確率。不再提升的時候停止訓練。所以設置訓練次數非常簡單。這樣就意味着我們不用搞清楚訓練次數怎么依賴其他的超參的。並且,早停的機制可以自動的防止過擬合問題。而且在訓練的早期,我們可以關閉早停的機制,這樣就可以看到任何過擬合的信號,以便你采用規則化方法。

為了實現早停的機制,我們需要更明確的說明一下,什么叫做准確率不再提升了。正如我們看到的,准確可能會有升有降,即使整體趨勢是在下降。如果第一次碰到准確率下降就停止的話,可能沒有等到后面的提升。一個更好的規則就是,沒有提升后再等待一段時間。比如在MINIST例子中,我們可以在最后最后十次的訓練中都沒有提升后再停止。這樣既不會錯誤后面的提升,也不回永遠等待。

這個"十次不再提升"的規則對MINIST問題的早期探索很好。但是,網絡在達到一個准確率后可能會穩定很長一段事件后,再次有所提升。如果你想得到很好的效果,"十次不再提升"規則就太激進了。我建議可以是這個規則開始你的實驗,隨着你對網絡的理解越來越好,逐漸的采用更加靈活的規則:"二十次不再提升","五十次不再提升",等等。當然,這個又引進了一個新的超參需要優化。實際中,這個超參通常很容易優化而得到不錯的結果。類似的,MINIST之外的其他問題,"十次不再提升"規則或多或少的有些激進,具體問題具體分析。通常一些實驗之后,都很容易找到不錯的早停策略。

學習速率規划:一直以來我們的\(\eta\)是個常量。然而如果\(\eta\)是可變的話,會帶來很多好處。在學習過程的初期,權值可能錯的很離譜,所以最好使用大的學習速率快速學習,之后,我么可以降低學習速率更好的精調整權值。

那么我們如果規划我們的學習速率值呢?很多辦法都可以。一個很自然的辦法就是仿照早停機制的思想。保持學習速率不變,直到測試正確率開始變差。然后降低學習速率,不如除以2或則10。重復這個過程,直到學習速率是原來的1024或則(1000)分之一。

可變的學習速率,可以提升效果,但是也打開了一扇大門。這扇門也可能是個頭疼的事。你可能不斷的嘗試各種學習速率規划。對於第一次實驗來說,我建議使用常量學習速率。這樣可以拿到第一個不錯的近似值。然后如果你想得到最好的效果,上面提到的學習效率規划的方法,值得嘗試。

規則化參數,\(\lambda\)我建議從不規則化(\(\lambda=0.0\))開始,為\(\eta\)設置一個值。使用這個\(\eta\)值和訓練集圖選擇一個\(\lambda\)值。從\(\lambda=1.0\)開始,乘10或則除以10,不斷嘗試,只要能改進測試集的效果。當你找到一系列的\(\lambda\)值,你就可以精調你的\(\lambda\)了。之后,你應該回過頭來,再次去優化\(\eta\)值了。

前面我是怎么選擇超參的:如果你使用本節推薦的方式調超參,你會發現,你得到的\(\eta\)\(\lambda\)不總是跟我之前用的用的值一致。原因是為了本書的敘述的,有些時候去最后話超參不太實際。想想之前我們做的那么多比較,新舊方式,帶不帶規則化,等等。為了讓必考有意義,我盡量保持超參一致(或則按比例縮放)。當然,同一組超參沒有理由對任何方法都是最優的,所以有時候我所使用的超參不是最優化的,而是一種妥協。

除了妥協的方法,我本可以對於每一次方法逗優化出最優的超參值。原則上那是個更好的更公平的方法。但是我們已經做了數十種的比較。實際上我發現這個很費時間。所以我使用了這個妥協的辦法。

Mini-batch大小:我們該如何設置mini-batch大小呢?為了回答這個問題,首先我們來假設我們在做在線學習,也就是mini-batch大小為1.

對於在線學習最明顯的擔憂是只含有單個訓練數據的mini-batch會導致梯度估計出現很大的錯誤。事實上,這些錯誤不是很大的問題。原因是單個梯度的估計不需要特別的准確。我們需要的是一個估計值,精確度足夠是我們的損失函數值下降就可以了。有點類似與我們看着一個左右搖擺大概\(10-20\)度的指南針去南極一樣。只要你不時的停下來查看指南針,指南針指向平均值是對的,你就可以到達南極。

基於這個討論,聽起來我們應該采用在線學習。實際的情況會比較復雜。上一章中我指出,可以使用矩陣運算一次同時計算mini-batch中的所有數據的梯度更新,而不是依次循環計算。依賴與你的硬件和所使用的線代庫,這個優化可以節省很多時間。也就是一次計算100個訓練數據的梯度比依次計算100個訓練數據的梯度要快很多。

起初這個看起來對我們不會有太大的幫助。比如一個大小為100的mini-batch,它的權值更新規則如下:

\[w\rightarrow w'=w-\eta\frac{1}{100}\sum_{x}\nabla C_x\quad(100) \]

其中求和表示在mini-batch中的所有訓練數據上求和。相對的,在線學習的更新規則為:

\[w\rightarrow w'=w-\eta\nabla C_x\quad(101) \]

即使100只花費50倍的更新事件,看起來還是在線學習比較好,我們可以更快的更新權值。假設,我們把學習速率提到100倍,則規則100變成:

\[w\rightarrow w'=w-\eta\sum_{x}\nabla C_x\quad(102) \]

這個跟規則100特別相似。只是花費了50倍的時間。當然,跟100次的在線學習並不一樣,因為mini-batch的\(\nabla C_x\)是用同樣一組權值計算得到的,然在線學習並不是這樣的。看起來使用大些的mini-batch會提升速度的。

考慮以上的這些因素,選擇最優的mini-batch大小幾一個折中的過程。太小了,則不能發揮硬件和矩陣運算的優勢。太大了,則權值更新的速率太慢。你需要的是選擇一個折中的值,最大化你的學習速度。幸運的是,mini-batch大小的選擇更其他的超參的值比較獨立。所以比不需要最優的其他超參來調優mini-batch的大小。所以可行的方法是用一些可以接受的(不用是最優的)值來設置其他的超參。然后實驗一些不同的mini-batch大小,想之前那樣適當縮放\(\eta\)值。打印出准曲率更時間的關系,從中挑選出最優的mini-batch大小,使效果的提升最快速。然后用這個mini-batch大小去接着優化其他的超參。

當然,毫無以為你已經發現了,我並沒有在我們的例子中優化mini-batch的大小。事實上,我們並沒有用最快的方式去更新權值。我只是簡單的直接用了大小為10的mini-batch,並沒有學習如果降低mini-batch的大小。部分是因為我想介紹除了大小為1的mini-batch,部分還是因為我准備的實驗中,速度的提升比較有限。然而,在實際的實現中,我們多數情況下是會實現最快的mini-batch大小,達到最大話整體速度的目的。

自動化技術:我一直介紹的方法都是手動調超參的方法。手動調試對於理解網絡行為是個很好的方法。然后,不奇怪的,現在有很多自動調整的方法。一個比較普遍的方式就是網格搜索。具體可以看James Bergstra和Yoshua Bengio在2012年發表的論文。這里就不詳細說明了。

總結:按照上面的規則不會讓你的網絡達到最好的效果。但是它給你未來的進步提供一個好的開始和基礎。特別的,我在上面是分開獨立討論每一個超參的。實際上,他們之間存在着相互關系。你在實驗\(\eta\)的時候,感覺你已經拿到了最后的值,然后開始優化\(\lambda\),結果發現這個完全搞亂了\(\eta\)的值。綜上所述,需要記住的是,上面討論點都是經驗性的,不是定死不變的。你應該尋找出問題的信號,並且願意去實驗。具體來說,這個意味着小心的觀察網絡的行為,特別是測試集的准確率。

選擇超參的取值的困難,還在於怎么選擇超參的知識在各種研究論文和軟件程序中廣泛傳播,並且經常只在單個實踐者的腦子中。有很多論文推薦了很多建議操作的方法(有時甚至是互相沖突的)。但是很少有論文系統話的提煉這些知識。Yoshua Bengio有一篇2012年的論文中給出了一些使用BP和梯度下降算法訓練的建議,包括深度學習。Bengio討論了很多細節,包括如何系統話的搜索超參空間。另一篇不錯的論文是1998年Yann LeCun, Léon Bottou, Genevieve Orr 和 Klaus-Robert Müller發表的。這些論文逗在2012年的一本介紹很多神經網絡訓練技巧的書里。這書很貴,但是里面的許多文章都單獨的被作者發表過,或許可以通過搜索引擎檢索到。

在你看這些文章的同時,特別是你做實驗的同時,有件事變的很明朗,那就是超參的優化並不是一個被完全解決了的問題。總有另一種方法可以提高你的效果。作家中流傳着一句話叫做:書永遠不會寫完,只是放棄寫了。神經網絡的優化也是如此:超參的空間如此巨大,沒辦法完成優化過程,只有放棄優化,留給后來人繼續優化。所以,你的目標應該是開發出一套可以讓你快速做好優化工作的流程。這很重要。

超參調參的難度是一些人開始抱怨相比其他的機器學習技術,神經網絡需要大量的工作。我聽過很多這樣的抱怨:"一個精調參的神經網絡是很得到最好的效果,這個沒錯,但是,我可以使用隨機森林或則SVM或則其他的自己喜歡的的技術,同樣可以。我沒有時間來找到對的神經網絡"。當然,從實際出發,有一個容易應用的技術很不錯,並且並不知道機器學習是否能解決這個問題。另一方面來說,如果獲取最好結果很重要,你也許應該嘗試其他的需要專業知識的方法。雖然機器學習如果能簡單最好,但是沒有理由她必須簡單。

其他技術

本章中說到的每一個技術都值得學習,原因不止是我解釋的那些。更大的一點原因是,讓你門熟悉神經網絡中會出現哪些問題,該如何分析,最后如何去克服。某種意義上,我們已經學習了如何去思考神經網絡。本章剩下的部分,我大致過一下其他的技術。這些技術的討論不會讓之前的討論的那么深度。

隨機梯度下降的變化

BP隨機梯度下降在minit問題上表現非常好。然而還有很多其他的優化損失函數的方法。並且有時這些方法的表現優於mini-batch隨機梯度下降。本節中我們主要介紹兩種方法,Hessian技術和動量技術。

Hessian技術:我們先把神經網絡放一邊。考慮一下以下的這個抽象問題,損失函數\(C\)是一個關於許多變量的函數,也就是\(C=C(w),w=w_1,w_2,w_3,...\),根據泰勒公式,在w點附近的函數值可以用一下方式計算:

\[C(w+\Delta w)=C(w)+\sum_{j}\frac{\partial C}{\partial w_j} + \frac{1}{2}\sum_{jk}\Delta w_j\frac{\partial^2 C}{\partial w_j\partial w_k}\Delta w_k + ... \qquad(103) \]

公式可以改寫成以下比較簡潔的形式:

\[C(w+\Delta w)=C(w)+\nabla C\cdot\Delta w+\frac{1}{2}\Delta w^TH\Delta w+...\quad(104) \]

其中\(\nabla C\)是常規的梯度向量,\(H\)被稱為Hessian矩陣,第\(jk\)個元素是\(\partial^2/\partial w_j\partial w_k\).假設我們丟棄后面的一些項來近似計算C,則得到下面的近似計算公式:

\[C(w+\Delta w)\approx C(w) + \nabla C\cdot\Delta w+\frac{1}{2}\Delta w^TH\Delta w.\quad(105) \]

更具微積分的知識,我們知道,當

\[\Delta w=-H^{-1}\nabla C.\quad(106) \]

的時候,公式105右邊的值可以取得最小值。(嚴格來說,這只是個極值。如果要保證這極值是最小值而不是一個最大值,我們得假設Hessian矩陣為正。直覺上來將,就是意味着損失函數C看起來是一個山谷,而不是一個山峰)。

105是損失函數的近似計算,所以當我們移動點\(w\)\(w+\Delta w=w-H^{-1}\nabla C\)的時候,能減小損失函數的值。這就提供了一個最小化損失函數的算法:

  • 選擇一個開始的位置點,\(w\)
  • 更新點的位置\(w'=w-H^{-1}\nabla C\),其中Hessian矩陣和\(\nabla C\)\(w\)的位置計算。
  • 更新點的位置\(w''=w'-H^{-1}\nabla C\),其中Hessian矩陣和\(\nabla C\)\(w'\)的位置計算。
  • ...

實際上105只是個近似的計算,所以最好學習的步長小一點。我們設置\(\Delta w=-\eta H^{-1}\nabla C\),其中\(\eta\)被稱為學習速率。

這種最小化損失函數的算法叫做Hessian優化。有理論和經驗表明,Hessian優化比梯度下降方法在更短的步驟里收斂。特別的,通過引入二階導數的信息,Hessian能避免梯度下降算法的許多問題。而且,Hessian算法有相應的BP計算方法。

如果Hessian方法這么好,為什么我們不再我們的神經網絡里使用呢?不幸的是,雖然他有很多有點,但是卻有一個非常大的缺點:應用起來非常困難。部分原因來基於Hessian矩陣的計算規模問題。假設你的網絡里有\(10^7\)個權值和偏移值,相應的Hessian矩陣就包含\(10^7\times10^7=10^{14}\)項。這已經很大了。實際上,有很多梯度下降的變種方法是受到了Hessian優化方法的啟發的,但是避免了Hessian方法的超大規模矩陣的計算問題。我們來看一下其中的一個例子,基於動量的梯度下降算法。

基於動量的梯度下降:直觀上來說,Hessian的優點是它不僅包含了梯度的的信息,而且包含了梯度如果變化的信息。動量梯度下降算法基於同樣的思想,但是避免了二階倒數的大矩陣問題。為了理解動量算法,我們來回憶一下梯度下降的算法,當時我們想象的是一個球從山上滾向山谷。當時我們只是粗略的模擬了球滾下山谷的情況。動量算法在梯度下降算法上修改了兩點,更像這個物理過程了。首先,引入了"速度"參數,這是我們優化的目標。梯度改變速度,而不是直接改變位置,就像物理過程中力量改變速度,間接的改變位置一樣。其次,動量算法引入摩擦力的概念,摩擦力會不斷的降低速度。

我們來更數學化描述一下這個過程。我們為每一個權值\(w_j\)分別引入一個速度變量\(v=v_1,v_2,v_3...\)。然后把梯度下降的更新規則\(w\rightarrow w=w-\eta\nabla C\)改成:

\[v\rightarrow v'=\mu v-\eta\nabla C.\quad(107) \]

\[w\rightarrow w'=w+v'.\quad(108) \]

兩個方程式中,\(\mu\)是一個超參,控制系統的阻尼力度,或稱摩擦力。我了理解方程式的意義,首先我們來考慮\(\mu=1\)的情況,也就是沒有摩擦力。觀察方程式你發現,梯度\(\nabla C\)"力"改變速度\(v\),速度\(v\)控制着權值\(w\)的改變速率。直觀上,我們不斷的把梯度力量加到速度上。這就意味着如果在幾輪的學習中如果梯度的方向是(大致)一致的。我們在那個方向上會構建出一個客觀的動力。舉例來說,比如,如果我們從一個斜坡往下動,會發生什么呢:

沒一步之后我們速度會增大一些,所以我么會越來越快的移動到山谷。這個就是為什么動量算法會比普通梯度算法快速的原因。當然帶來的一個問題就是,當我們到達谷底時候,我們會越過谷底。或則,如果梯度平凡改變的話,我們會發現我們在錯誤的方向上移動。這就是超參\(\mu\)存在的意義。前面我說過,\(\mu\)控制系統的摩擦力;具體來說就是,你應該把\(1-\mu\)當做系統的摩擦力。當\(\mu=1\)的時候,系統沒有摩擦力,速度完全有梯度改變。相反,\(\mu=0\)的時候,系統存在很大的摩擦力,速度完全提不起來,方程107和108退化成普通的梯度下降方程\(w\rightarrow w=w-\eta\nabla C\).實際中,選擇一個0到1之間的\(\mu\)值,可以是我們構建出一定的速度,並且不至於越過谷底。我們可以想選擇\(\eta\)\(\lambda\)類似,用削減后的驗證集去為\(\mu\)選擇一個值。

我一直沒有提\(\mu\)的名字,那是因為這個名字很令人困惑:動量協效率(momentum co-efficient).因為\(\mu\)跟動量沒什么關系,跟類似與摩擦力。然后這個名字已經廣泛使用了,我們也沿用這個名稱吧。

動量算法的好處之一就是幾乎不用修改梯度下降算法。我們還依然使用BP算法計算梯度,依然使用隨機采樣的mini-batch。實踐中動量算法運用的很多,經常會加速學習的過程。

其他最小化損失函數的方法:很多其他的最小化方法被開發出來,哪種方法最好沒有統一的普遍適用的結論。隨着深入的學習神經網絡,很值得去深入理解這些技術,理解 他們的工作原理,有點和缺點,以及怎么在實踐中應用。我之前提到的一片論文對這幾種方法做了一個比較,包括結合梯度算法,BFGS,L-BFGS方法。Nesterov的加速梯度算法,在動量算法的基礎上進行了改進,最近表現的很多錯。但是,我么后續的書中任然使用隨機梯度下降算法,因為朴素的隨機梯度下降算法非常好用,尤其是在應用了動量算法之后。

人工神經元的其他模型

目前為止,我們構建的神經網絡都是采用了\(sigmoid\)神經元。原理上,采用\(sigmoid\)構建的網絡可以計算任何函數。實踐中,采用其他神經元的網絡的表現有時會超過\(sigmoid\)神經元。根據具體應用的不同,其他神經元的網絡可能會學習更快,在測試集上泛化更好,或則都有。我們來介紹一個其他類型的神經元模型。

tanh可能是最簡單的一個變種模型:把\(sigmoid\)函數替換成雙曲線tangent函數。輸入為\(x\),權值為\(w\),便宜為\(b\)的tanh神經元輸出為:

\[tanh(wx+b)\quad (109) \]

其中tanh是雙曲線tangent函數。次函數其實跟sigmoid函數有非常密切的關系。我們來回憶一下tanh函數的定義:

\[tanh(z)=\frac{e^z-e^{-z}}{e^z+e^{-z}}\quad(110) \]

很容易證明下面等式:

\[\sigma(z)=\frac{1+tanh(z/2)}{2}\quad(111) \]

tanh實際就是sigmoid函數的縮放移位。從圖像上也可以看出來:

有一個區別就是tanh的取值范圍是-1到1,人不是0到1。這意味着如果你用tanh去構建網絡,你輸出的標准化會跟sigmoid有所不同。

類似與sigmoid神經元,tanh網絡可以計算任何輸出為-1到1之間的函數。並且,BP算法和隨機梯度下降算法同樣適用。

應該用哪種網絡呢,tanh還是sigmoid?這個問題沒有先驗的回答。然后一些理論和經驗表明tanh有時表現的好一些。
我們來舉一個理論上的爭論的例子。我們來考慮l+1層上的第j個神經元的權值\(w_{jk}^{l+1}\).BP方程得知,相應的梯度值為\(a_k^l\delta_j^{l+1}\).由於所有的輸出都是正數,所有這項的符號取決於\(\delta_j^{l+1}\)的符號。這就意味着如果\(\delta_j^{l+1}\)是正的,則所有權值\(w_{jk}^{l+1}\)在梯度下降算法中都將減小。如果\(\delta_j^{l+1}\)為負的話,怎所有權值\(w_{jk}^{l+1}\)在梯度下降中都將增大。也就是,同一個神經元的權重要么同事增大要么同時減小。這是個問題,因為一些權值需要增大而另一些需要減小。而這個只有在一些神經元正激活一些負激活才有可能發生。這個討論建議替換sigmoid函數為一些允許正負激活的函數,比如tanh函數。確實,tanh函數是關於零點對稱的。就是tanh(-z)=-tanh(z),我們可能期望這樣的特性,簡單說就是隱藏層的激活能偶正負平衡。

應該怎么看待這個論點呢?應該要知道,這個是提議性質的,也是啟發和探索式的,並非嚴格邏輯證明了tanh神經元要比sigmoid神經元要好。或許sigmoid的一些特點抵消了這個問題呢?確實,對於許多問題上tanh唄發現會比simoid神經元好一點,有時並沒有好。不幸的是,對於任何特定的應用,我們沒有一個准確快速的規則去判斷哪種神經元會學習的更快,泛化的更好。

另一種sigmoid神經元的變種被稱為矯正線性神經元或則矯正線性單元。輸入為\(x\),權值為\(w\),偏移為\(b\)的矯正線性單元的輸出為

\[max(0, w\cdot x+b)\quad (112) \]

函數\(max(0,z)\)的圖像如下:

此函數更sigmoid和tanch函數顯然不同。然后類似於sigmoid和tanh神經元,矯正線性神經元也可以用來計算任何函數,也可以用BP算法和隨機梯度下降算法訓練。

什么時候適合使用矯正線性神經元呢?最近一些圖像識別上的工作發現矯正線性神經元的很多有點。但是,跟tanh類似,我們還沒有深入理解什么時候矯正線性神經元比較合適,更別提理解為什么了。為了給你一些感覺,我們回顧一下sigmoid神經元飽和后停止學習的情況,比如輸出接近0或者1的時候。像本章反復看到的一樣,問題出在\(\sigma'\)在這時梯度或下降,從而降低了學習的速率。tanh函數也有類似的問題。相比之下,矯正線性神經元卻不存在這個問題。另一方面來說,如果輸入為負的話,梯度消失了,神經元完全停止學習。這些是為什么矯正線性神經元表現比sigmoid和tanh好不好理解的原因之二。

我說了很多的不確定性,說明了我們還沒有完整可靠的理論來判斷哪一種激活函數更好。這個問題事實上比我描述的還要難,因為有幾乎無數中可能的激活函數。給定一個問題,哪個激活函數最好呢?哪個會學習的更快呢?哪個測試准確率會最高呢?我很驚訝的是我們對此了解太少了。理想情況下,我們應該有套理論告訴我們,怎么去選擇激活函數。另一方面,我們有不能讓理論的卻讓阻止我們。我們已經有了許多有力的工具。可以做很多事。雖然本書的剩余章節我們會繼續是用sigmoid神經元,原因他們足夠強大並且能給神經網絡的核心思想提供具體的解釋,但是要記住,這些思想也同樣可以使用與其他類型的神經元,並且有事會帶來很大的好處。

神經網絡的幾個故事

有一次,我去參加一個基礎量子力學的會議,我注意到了一個特別奇怪的口頭習慣:當演講結束后,觀眾席上的問題開始了,開頭都是"我非常同意你的觀點,但是...",量子基礎不是我的專業領域,我注意到這類問題,是因為在其他科學會議上我極少或則沒有提問者對演講者的觀點表示贊同。當時,我覺得問題的出現表示量子力學領域取得了一些本質的進展,人們轉動了一些輪子。后來我發現我的評價過早。演講者在更人類大腦遇到的最難的一些問題在斗爭。當然進展會很慢。當時聽一聽人們是如何思考的也是很有價值的。即使他們沒有無可辯駁的新進展做匯報。

我可能在一些現在的書上發現類似的句子"我非常同意...".為了解釋我們所看到的東西,我們經常會說:"直覺上","簡單來說",后面跟上一個故事來說明一些現象或什么的。這些故事貌似有理,但是提供的證明非常的單薄。如果你查看一下演講報告你會發現很多類似的證據不足的故事出現在各種研究論文中。我們應該如何看待哲學故事呢?

在科學的許多領域,特別是處理簡單現象的領域,為一個理論獲取可靠、可信任的證明是可能的。但是在神經網絡領域,有太多的參數和超參,以及他們之間極端復雜的相互聯系。在這樣的一個極端復雜的系統里,得出一個可靠通用的觀點是非常難的。理解數據網絡的全部就像是量子力學一樣,挑戰人類大腦的極限。相反,我們經常贊成或則反對一些普遍的觀點。結果這些通用的觀點或被修改或被放棄。

一種看待這些直覺故事的觀點就是這些故事都帶着隱含的挑戰。比如之前我關於dropout的一個觀點:這個技術降低餓了神經元之間的相互依賴,因為神經元不能依賴一些 特定的神經元的輸出,這就迫使她從各種隨機的其他神經元的子集中學習更魯棒的特征。這是一段豐富的觀點。人們可以圍繞這個觀點開展一些豐富的研究,搞清楚這個的哪些是真的,哪些是假的,哪些需要修改和優化。實際上,現在有一小部分研究者在研究dropout(以及它的一些變體)。視圖了解她的原理,局限在哪。沿着我們討論的過的直覺行的方法。每一個直覺不僅僅是一個解釋。而且是一個對研究和理解的挑戰。

當然,一個人不可能研究所有的探索式的解釋。需要整個神經網絡的研究則花費幾十年的時候去簡歷強大的基於證據的神經網絡理論。難道這幾意味着我們應該拒絕啟發式的解釋嗎?不!事實上我們需要這些解釋去啟發和知道我們思考。就像探險時代:早期的探險者依賴着錯誤的理論。然后這些錯誤唄改正過來。當你度食物理解很少的時候-就像是探險者對地理理解很少,勇敢探索比可可要求每一步都嚴格正確更重要。所以,你應該把這些故事去指引你的思考,同事記住這些故事的局限性。換句話說,就是我們需要這些故事去激勵和其他我們,邏輯縝密的深入理論來解開事情的真相。


免責聲明!

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



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