繼續回到神經網路章節,上次只對模型進行了簡要的介紹,以及做了一個Hello World的練習,這節主要是對當我們結果不好時具體該去做些什麼呢?本節就總結一些在深度學習中一些基本的解決問題的辦法。
為什麼說是“基本的辦法”?因為這一部分主要是比較基礎的內容,是一些常用的,比較容易理解的,不過多的去討論各式各樣的網路結構,只是介紹這些方法都做了些什麼。
對於深度學習的探索後面會再開專題,專門去學習和討論(突然發現要學的東西真的很多~)
深度學習技巧
0.不要總是讓“過擬合”背鍋
在機器學習中,當我們對我們最終的結果(testing data上的score)不滿意時,總是認為模型可能overfitting了,其實這樣是不對的,正確的步驟應該是下面這樣的:
當我們經過三步走①建立好神經網路模型②後,需要去查驗模型在訓練集上的誤差,如果這個誤差較大(準確率很差),則需要重新調整模型或模型引數,轉向③;
如果模型誤差較小(準確率很好),則轉向④,進一步在測試集上驗證正確率和誤差,當此時測試集上的誤差很大,那麼才能稱之為過擬合,需要我們回頭⑤重新設計模型或者模型引數;
如果在測試集上的誤差也很小,那麼恭喜你,一個好的模型就可以使用了。
所以,不能總怪過擬合的問題,因為針對不同原因產生效果差有不同的解決方法,我們要首先判斷是否屬於過擬合,才能進一步採取措施。
下面我們就針對上面兩個問題(①在訓練集上表現就不好,②在訓練集上表現還可以,在測試集上表現很差)進行鍼對性地解決。
首先是當在訓練集上的結果就不好的情況:
1.更換啟用函式(Activation function)
首先我們先在前面的手寫辨識的例子的基礎上舉個例子:
在上節中我們採用了中間有兩層的隱藏層的網路結構,得到的準確率為99.53%的準確率,如果現在我增加隱藏層的層數,增加到10層:
可以看到,結果完全爛掉了,準確率只有11%了。
因此,網路並非越深越好,下圖展示了不同的網路層數的訓練準確率的變化情況:
可以看出,並非隨著網路層數的增加,準確率並非一直上升的。這裡要提一句,在深度學習中,其實是很難將準確率提升至1.0的,這點與之前的模型是不同的,比如決策樹,只要樹的深度足夠,很輕易能夠達到1。
回到正題,為什麼會出現模型越deep,準確率反而下降的情況呢?回頭看一下網路的結構:
對於一個很深的網路結構,在前面網路層(靠近輸出層)由於具有較大的梯度,當每層網路的學習率都相同時,前面的引數學習會很快,而後面的(靠近輸入層)的引數由於梯度較小,導致學習速度很慢;
當前面的引數已經收斂時,前面的還沒有開始更新,依然是初始的隨機的引數,這種現象稱之為梯度消失,也叫梯度彌散。
為什麼會出現這種現象呢?
其實就是啟用函式Sigmoid的問題,當啟用函式為Sigmoid時,我們需要看一下為什麼後面的梯度很小。
這裡不需要具體算出具體的數值,只需要大概知道當前面的某一個引數w改變後,對Loss有多大的影響即可。如圖所示:
當前面的某一個引數增大Δw後,看下Loss的損失增大了多少,即ΔL,從圖中看出,當經過第一層網路時,要經過一個sigmoid函式,該函式會將輸入進行一次衰減,如右側sigmoid函式影像
即使引數有一個很大的改變時(橫軸),在經過sigmoid函式後,也會被縮放到一個很小的區間(縱軸),以此類推,每層如此,因此,當網路足夠深的話,這個ΔL→0。
又因為在引數更新的過程是反向傳播的,因此,在更新時,由於鏈式求導法則,需要不斷乘上σ'(z),也是一個不斷衰減的過程,因此到了後面,幾乎梯度極小,導致梯度消失。
那麼這個問題如何解決呢?
只需換掉啟用函式就行了,在前面的Hello World程式中,在add(Activation)時,該引數是可選的,下面就介紹一種啟用函式“RELU”。
RELU(Rectified Linear Unit)
RELU函式是一個很簡單的函式,如圖所示:
當輸入<0時,輸出為0,當輸入>0時,輸出為其本身。
為什麼要用RELU作為啟用函式呢,(1)由於其計算速度很快,相比於Sigmoid,不用計算exp了(2)生物學上有一定的道理(這個我也不太理解,但不重要)(3)最重要的是RELU可以解決梯度消失的問題。
那麼RELU代入到網路中是如何運作的呢?
把原網路中的sigmoid函式換成RELU,網路結構如下:
圖中將所有的sigmoid換成RELU,那麼當輸入經過RELU時只會有兩種輸出,一種是0,另一種是是其本身,如上圖所示,假設在第二層中後兩個為0,第三次1、3神經元為0,其他都是其本身(圓圈內已用線性表示)。
那麼最終我們的網路結構可以看作如下這樣子:
這樣一方面不會再出現反向傳播過程中導致的梯度消失問題,另一方面網路變成了一個更“瘦”的線性結構,提升了運算速度。
然而,當使用RELU時,對於上一層一部分輸出小於0的值會直接置為0,這樣對應的引數就不再更新了,這也會導致一部分神經“壞死”,如上面我們直接忽略掉的一些神經元,這可能有時造成特徵的提取不夠充分。
而當使用較大的學習率時,w更新過大,可能更新為負值,那麼輸出極可能為負值,再經過RELU時,會導致神經元處於壞死狀態,永遠都不會再被啟用。
為了解決這一問題,另一種RELU的變形的啟用函式Leakly ReLU就應運而生,其影像為:
當輸入小於0是,有一個極小的輸出,這時使該神經元的作用變得很弱,幾乎處於“昏死”狀態,但一旦有機會,這個神經依然可能被啟用。
以及另一種變形的ReLU,引數ReLU形式:
這裡的α可以是任意引數,同時,α也可以作為網路的引數一起被學習。
Maxout啟用函式
Maxout是一種自動學習啟用函式的啟用函式,就是能夠自動學習啟用函式的引數,具體的運作方式如圖所示:
假設一個輸入x是二維的,經過wx+b之後得到4個輸出;
然後對輸出進行分組,這個分組是事先定好的,如將前兩個元素分為一組,後兩個元素分為一組;
然後對每個組取出最大的值,作為輸出,接下來輸入到下一層網路中,以此類推。如圖所示
紅色框的部分即為一個神經元,這裡需要注意的是,原本我們的網路中間層應該只有兩個輸出,現在要經過一層maxout,多出了兩個輸出,那麼相當於多出了四個引數,
這四個引數就是靠學習出來的。因此說maxout是自己學習出來的啟用函式。
實際上,只要maxout願意,一樣可以變成ReLU啟用函式:如下圖:
前一張為ReLU結構,假設x是一個一維的(注意圖上的1是引數b,並非一個維度),那麼在ReLU中:z=wx+b,畫在圖上為:
那麼經過ReLU之後,大於0(x軸上方)的部分輸出是其本身,小於0(x軸下方)的部分輸出為0,那麼則有:
而在maxout中,後面那張圖,除了原先的w和b引數,還要有一組引數w'和b',現在我們令w'和b'都為0,那麼:z1=wx+b,z2=0*x+0=0,畫在圖上為:
然後經過maxout函式,maxout是取二者之間最大值,所以始終取位於上方的那一部分,即為:
可以看到最終的綠色的線就是ReLU函式。
所以說ReLU只是maxout的一種特例。
而在實際中,maxout是自身要去學習引數w'和b'的,如圖:
那麼z1和z2在圖中,畫出來就是這樣的:
圖中藍色線是z1,紅色線是z2,最終取max之後就是綠色的那條線。
當然,我們也可以取三個元素分為一組,那麼會得到如下的啟用函式形式:
再來回顧一下maxout的結構:
這裡會有一個問題,由於maxout函式是存在不可導點的,那是否可以用梯度下降的方法呢?
其實是可以的,對於每一個max的輸出,我們直接將所得到的那一部分放進去訓練即可,另一個就可以暫時忽略掉了,跟ReLU那個置為0的神經元一樣:
網路就變成了這樣的結構,然後去訓練就好了。
但這裡要注意的是,這裡並不是讓某個神經元“壞死”,因為maxout的結構內(即在經過max之前)有兩個輸出,刪掉一個之後這個神經元還是存在的(因為有一個輸出本來就是多出來的),只不過此時只更新與這個最大值連線的引數。
那問題就來了,這樣不是就不能更新那些刪掉的元素相連線的引數了嗎?
其實一樣是可以更新的,因為對於一個樣本而言,可能最大值是z1,那麼對於另一個輸入x而言,可能最大值就變成了z2,這時就會更新之前被刪掉的元素z2所連線的引數了。
2.自適應學習率
這個就是前面提到的,當學習率都相同時,伴隨著梯度消失(因為導致梯度消失的元凶是啟用函式,不知道怎麼描述,就寫“伴隨著”吧),有時使用動態的學習率確實能夠改善梯度消失的問題;
另一方面,前面梯度下降章節說到,學習率在學習中自適應能夠加快收斂,且更容易達到區域性最優,因此自適應的學習率是必要的。
至於自適應學習率有哪些方法和原理,參加梯度下降章節中有關內容https://www.cnblogs.com/501731wyb/p/15322391.html。這裡就不再說了。
上面就是對於在訓練集上都表現不好,所要採取的策略,當然還包括最基本的模型的結構調整。
下面就是當模型在訓練集上表現很好,但在測試集上反而下降了,這種情況就屬於過擬合,一般情況下采取的策略。
3.正則化
正則化可謂是伴隨著整個機器學習演算法,過擬合一般離不了正則化,正則化在前面正則化的章節中也已介紹完了,一般就包括L1正則和L2正則,在深度學習中道理是一樣的,在損失函式後面加上對應的正則項即可。
正則化有關內容詳見:https://www.cnblogs.com/501731wyb/p/15436330.html。
4.Early Stopping
在訓練過程中,模型從一開始啥都不知道,再到學習之後,開始逐漸表現越來愈好,到後面會更加傾向於細枝末節的學習,為了能夠在訓練集上表現的更加優秀,因此在測試集上的精度就會降低。
因此此時需要能夠及早停止訓練,防止過擬合,這種方法就稱之為early stopping。
那麼如何操作呢,這時就需要validation data了,當我們的模型在validation data上的準確率不再提高,就可以停止訓練了。
具體做法是,每個epoch訓練完成後,模型回到validation data上進行驗證,當accuracy不再提高時,就停止訓練。
這裡所謂的“不再提高”,不能因為一兩次的下降就停止,一般是記錄當前在validation data上最好的accuracy,當連續10次epoch都沒有達到這個accuracy時,則就可以停止了。這種方法也稱為“No-improvement-in-n”
5.dropout
dropout可謂是深度學習中的一個特色,但又有點像整合學習中隨機森林中隨機抽取部分特徵作為樹的訓練的味道,後面會進行一個說明。這裡先介紹dropout。
所謂dropout就是在訓練過程中隨機去掉原本網路結構中的一部分神經元,當然也可以包括輸入,再進行訓練,如圖所示:
神經元被丟棄後,類似於上面的ReLU,網路結構也就發生了變化:
那麼我們在進行引數更新之前,需要設定一個引數,這個引數決定了每個神經元被丟掉的機率p%,因此每個節點都有p%的概率被丟棄掉。
這個工作一般是在batch上做的,即對於每個mini_batch進行一次引數更新的時候,就會改變一次網路的結構。
要注意的是,當我們訓練好所有的引數之後,在對樣本進行預測時,此時是不再需要進行dropout的,即:
如果訓練時設定的dropout的概率為p%,那麼在進行測試時,需要對訓練好的引數統統乘以1-p%。
至於為什麼dropout能夠提升效果呢,一個直觀的解釋就是如圖所示:
在進行dropout時,當一些神經元被丟棄掉(擺爛)之後,那麼那些沒有被丟棄的神經元會好好表現,爭取做好。
而到了測試階段,所有的神經元都會參與,這時沒有會擺爛了,所以會得到比較好的結果。(略微牽強,但還是有一定的道理的)。
那麼至於為什麼要dropout在測試時要乘以補償係數1-p,下面就從bagging的角度來探討一下:
dropout可以說是一種bagging的整合學習方法,如下圖所示:
就相當於我們訓練出來了很多個模型,只不過這些模型的引數都是共享的,那麼當把模型應用於測試集時:
相當於我們拿一個測試資料,分別拿到每個模型中,然後根據每個模型的輸出,求出平均值就是最終的輸出。
然而這其實是不太可能的,因為網路結構太多了,假設有M個神經元,那個共有2M種可能的網路結構。
而dropout神奇的地方就是,當把訓練引數weights都乘以1-p%之後,不進行dropout的話,經過網路所得到的結果和average是很接近的。下面舉個例子說明一下:
假設一個網路結構只有輸入到輸出,dropout機率為50%,只能dropout輸入的維度了,那麼一共可能產生4中結構,如下:
通過綜合四個輸出,最終得到z=1/4(2*w1*x1+2*w2*x2)=0.5*w1*x1+0.5*w2*x2,如果對於最終訓練的權重直接乘(1-50%),則有:
可以看出,兩個是相等的。這只是作為一個例子說明,有時其實並非完全相等,只能說是近似相等。
好了,到這裡有關深度學習中一些常用的技巧就到差不多了,由於這裡僅是對深度學習有個初步的認知,知道其是什麼,以及在做什麼事,其他內容暫不深究。
後記:部落格內容僅是自己的回顧和學習,可能有的地方描述不是很深刻,路過的如果有疑問可以看李宏毅老師的機器學習課程,無論是講課還是PPT真的很良心~
有關深度學習的內容後面可能暫時就不更新了,等到後面基礎部分介紹完畢後,再開一個深度學習的專題(也有可能一邊更基礎,一邊學習深度學習,根據自己的學習進度吧~),下一更可能會介紹CNN,也可能到具體某一種演算法。