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

一個高爾夫球手練習高爾夫球時會花絕大多數時間練習基本的揮杆動做。在基本的揮杆動做的基礎上,逐漸的纔會練習其餘動做。類似的,目前爲止咱們一直專一在理解BP算法, 它是咱們的基礎」揮杆」動做,學習神經網絡的基礎。這章中我會解釋一些用來提高BP算法的技術,以提升神經網絡的學習。python

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

固然,咱們沒有覆蓋了全部用於神經網絡的訓練技術,事實上咱們只覆蓋的不多的一些。這裏的思想是,在學習不少技術的時候,最好的方法是深刻的學習一些最重要的方法。掌握了這些重要技術的不止技術自己有用處,也會加深你對實際中出現問題的理解。這樣在必要的時候,你也會迅速的掌握其餘是技術。網絡

##Cross-entropy 損失函數 大多數人以爲犯錯誤是很讓人不開心的。學習鋼琴後的不久,我有過一次演出的經驗。那次我很是緊張,而後把一個八度音演奏的特別慢。我很困惑,不能繼續演奏,直到有我的指出了個人錯誤。我感到很尷尬。雖然不開心,可是當咱們錯的很厲害的時候,咱們也會學習的很快。我能保證下次演出的時候,我不會再錯了。相反,錯誤不是很明顯的時候,咱們學習的會比較慢。app

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

咱們將訓練這個神經元作一件異常簡單的事:輸入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.六、偏移爲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(以及它的一些變體)。視圖瞭解她的原理,侷限在哪。沿着咱們討論的過的直覺行的方法。每個直覺不只僅是一個解釋。並且是一個對研究和理解的挑戰。

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

相關文章
相關標籤/搜索