Reference:Theano入門三部曲
http://deeplearning.net/tutorial/logreg.html (Softmax回歸)
http://deeplearning.net/tutorial/mlp.html (MLP多層感知器)
http://deeplearning.net/tutorial/lenet.html (簡易LeNet卷積神經網絡)
為什么要使用Theano?
深度學習最好使用一些庫,比如Theano。主要是因為反向傳播調整參數時,需要求導。鏈式求導本身沒有難處。
但是深度學習的神經網絡架構設計的比較復雜,層數又多(15層不是夢)。
在基本BP網絡的三層結構里,鏈式的長度已經到了5,推導公式已經不忍直視,人工求導顯然不是明智的。
Theano提供了grad梯度函數,自動根據表達式求一階導數,grad(cost,param),其中cost函數可以是一個超長超長的表達式。
param則可以是一個超大超大的數組或是矩陣。
顯然,有了grad函數,我們可以專心設計正向傳播的I/O,反向傳播只要一個grad函數即可。省去了復雜的公式推導。
Theano是深度學習較早的庫之一,由深度學習三大先驅(Geoffrey Hinton(Google)、Yann LeCun(Facebook))的Yoshua Bengio構建。
使用Python組織邏輯,C編譯執行,CUDA並行加速計算,是非常好的實驗平台。
它的庫源碼中包含大量注釋,並且提供深度學習的幾個基本模型的代碼實現文檔。
每篇文檔都采用paper的形式,集中了許多大牛的論文的精華、各種小trick,也給出了論文的具體引用,方便按圖索驥。
Theano的一般結構
Theano基於Python的面向對象,所以它的神經網絡也是基於面向對象的思路去寫的。
【對象】
它認為,淺層網絡的中分類器,深度網絡中的每個層,都是一個對象。
在這個對象里,你被指定了輸入格式,你只需要做兩件事:
根據格式,定義參數、定義輸出。
【數據讀入/處理】
從文件讀入數據,並且對數據進行全局分享處理(shared)
Theano中搞了一個奇怪的shared類型,Python的普通類型可以由theano.shared()方法轉換。
Shared區是供GPU、C代碼使用的內存區,與Python的內存區獨立,但是由Tensor變量聯系着。
這里就不得不提Theano的函數機制。theano.tensor中封裝的着大量的惰性函數。
這些惰性函數,在Python里是不會執行的。需要在theano.function()里執行。
theano.function()有四大區:
inputs=[], 如果只是一個普通的列表,就把輸入放在這個參數。如果輸入有很多,應該放在givens區里。inputs區不支持shared變量,所以也要挪到givens區。
inputs在寫function時基本是留空的,inputs=[],這個位置接受的是在線傳入的值,如果是離線值,應當放到givens區里。
outputs=普通函數or惰性函數,就是指定工作函數。
這里有個trick,就是如何print出Tensor表達式的量(因為該量的值只會在執行時確定,不能使用get_value)。以取出Softmax的預測值y_pred為例。
只要寫這樣一個function就行了,function(inputs=[],outputs=classifier.y_pred,givens={....自己指定范圍...})。
updates=參數更新的列表,格式[(原,新),(原,新)....],Shared區的變量只能在updates里更新,Python的中賦值只會讓變量留在Python的內存區。
但是在function的內存區和Python一點關系也每有。如果Python里設置一個Tensor關聯一些Shared變量的話,Shared區的updates會波及到Python區的值。
如CNN教程里的,params這個Tensor,明明在Python的全局內存區,但是每次update之后,都會被改變。
也就是說Shared區能影響Python區,但是Python區無法動Shread區一根汗毛。
givens={x:List1[:],y:List2[:],.....},其中x和y是outputs函數里使用的變量的名字,一定要對應,下面會講為什么。
theano.function()不是以Python的方式執行,而是迅速編譯成C代碼執行,相當於每個function都是一個獨立的子程序,所以這四大區是必要的。
由於是獨立子程序,Python中的普通變量顯然不能很好工作。所以一般都設成shared類型。
實際上,tensor的不少惰性函數都需要在Python狀態下的shared變量才能定義。原理未知。比如T.dot就不要求shared變量,但是grad的param一定要求是shared。
由於Theano的大部分計算都在function里,而function又是以C執行,所以Theano具有不輸於C的速度,同時兼具Python的靈活性。
【主過程:前向傳播構建&反向傳播迭代】
創建各個神經網絡層、分類器的實例對象,由I/O首尾相連,最后利用分類器構建cost函數,完成前向傳播。
利用各個層對象的參數、cost函數,構建梯度表達式,以及updates列表。
為訓練集、驗證集、測試集創建以theano.function()為殼的模型。
使用mini-batch梯度法分批訓練訓練集,測試驗證集&測試集。
【mini-batch梯度法與驗證集收斂】
深度學習中的梯度法應當使用mini-batch。
隨機梯度(Stochastic Gradient Descent)雖然快,但是不利於收斂。
批梯度(Batch Gradient Descent)太慢,但是收斂很好。
mini-batch做了個折中,它把數據集分成好多小batch,每個batch有統一的batchsize。
對小部分數據進行BGD,這樣兼顧了速度和收斂。
每個小batch即算一次iter迭代,做完全部batch,算一次epoch。
同時引入了驗證集,由原訓練集切割而成。驗證集在小數據集里不會出現。但是在大數據集里一定是要有的。
原因是大數據集的cost函數,你很難去評估什么值才算是勉強收斂。所以采用訓練、驗證交叉的方法替代傳統的看值收斂。
驗證集訓練法有幾個參數,patience(耐力)、patience_increase(耐力增長系數)、improvement_threshold(耐力增長(模型繼續迭代改善)閾值)
validation_frequency(驗證集評估頻率)、best_validation_loss(當前最低錯誤率)。
驗證集的算法:
while(epoch++)
訓練每個小batch
計算當前小batch的iter
滿足評估頻率?開始評估!
若評估loss<最好loss
若評估loss<最好loss*閾值:patience=max(patience,iter*增長系數)
更新最好loss
(選擇):評估測試集
iter >=patience: 總迭代結束
一旦長時間評估loss沒有刷新patience,很快iter就會超過patience而結束迭代。
否則,則一直訓練,不停創造更好的loss。