CNN框架的搭建及各個參數的調節

本文代碼下載地址:我的github


本文主要講解將CNN應用于人臉識別的流程,程序基于Python+numpy+theano+PIL開發,采用類似LeNet5的CNN模型,應用于olivettifaces人臉數據庫,實現人臉識別的功能,模型的誤差降到了5%以下。本程序只是個人學習過程的一個toy implement,樣本很小,模型隨時都會過擬合。

但是,本文意在理清程序開發CNN模型的具體步驟,特別是針對圖像識別,從拿到圖像數據庫,到實現一個針對這個圖像數據庫的CNN模型,我覺得本文對這些流程的實現具有參考意義。


《本文目錄》

一、olivettifaces人臉數據庫介紹

二、CNN的基本“構件”(LogisticRegression、HiddenLayer、LeNetConvPoolLayer)

三、組建CNN模型,設置優化算法,應用于Olivetti Faces進行人臉識別

四、訓練結果以及參數設置的討論

五、利用訓練好的參數初始化模型

六、一些需要說明的


一、olivettifaces人臉數據庫介紹


Olivetti Faces是紐約大學的一個比較小的人臉庫,由40個人的400張圖片構成,即每個人的人臉圖片為10張。每張圖片的灰度級為8位,每個像素的灰度大小位于0-255之間,每張圖片大小為64×64。如下圖,這個圖片大小是1190*942,一共有20*20張人臉,故每張人臉大小是(1190/20)*(942/20)即57*47=2679:



本文所用的訓練數據就是這張圖片,400個樣本,40個類別,乍一看樣本好像比較小,用CNN效果會好嗎?先別下結論,請往下看。

要運行CNN算法,這張圖片必須先轉化為數組(或者說矩陣),這個用到python的圖像庫PIL,幾行代碼就可以搞定,具體的方法我之前剛好寫過一篇文章,也是用這張圖,考慮到文章冗長,就不復制過來了,鏈接在此:《利用Python PIL、cPickle讀取和保存圖像數據庫》。


訓練機器學習算法,我們一般將原始數據分成訓練數據(training_set)、驗證數據(validation_set)、測試數據(testing_set)。本程序將training_set、validation_set、testing_set分別設置為320、40、40個樣本。它們的label為0~39,對應40個不同的人。這部分的代碼如下:

[python]?view plain?copy
  1. """?
  2. 加載圖像數據的函數,dataset_path即圖像olivettifaces的路徑?
  3. 加載olivettifaces后,劃分為train_data,valid_data,test_data三個數據集?
  4. 函數返回train_data,valid_data,test_data以及對應的label?
  5. """??
  6. def?load_data(dataset_path):??
  7. ????img?=?Image.open(dataset_path)??
  8. ????img_ndarray?=?numpy.asarray(img,?dtype='float64')/256??
  9. ????faces=numpy.empty((400,2679))??
  10. ????for?row?in?range(20):??
  11. ???????for?column?in?range(20):??
  12. ????????faces[row*20+column]=numpy.ndarray.flatten(img_ndarray?[row*57:(row+1)*57,column*47:(column+1)*47])??
  13. ??
  14. ????label=numpy.empty(400)??
  15. ????for?i?in?range(40):??
  16. ????label[i*10:i*10+10]=i??
  17. ????label=label.astype(numpy.int)??
  18. ??
  19. ????#分成訓練集、驗證集、測試集,大小如下??
  20. ????train_data=numpy.empty((320,2679))??
  21. ????train_label=numpy.empty(320)??
  22. ????valid_data=numpy.empty((40,2679))??
  23. ????valid_label=numpy.empty(40)??
  24. ????test_data=numpy.empty((40,2679))??
  25. ????test_label=numpy.empty(40)??
  26. ??
  27. ????for?i?in?range(40):??
  28. ????train_data[i*8:i*8+8]=faces[i*10:i*10+8]??
  29. ????train_label[i*8:i*8+8]=label[i*10:i*10+8]??
  30. ????valid_data[i]=faces[i*10+8]??
  31. ????valid_label[i]=label[i*10+8]??
  32. ????test_data[i]=faces[i*10+9]??
  33. ????test_label[i]=label[i*10+9]??
  34. ??
  35. ????#將數據集定義成shared類型,才能將數據復制進GPU,利用GPU加速程序。??
  36. ????def?shared_dataset(data_x,?data_y,?borrow=True):??
  37. ????????shared_x?=?theano.shared(numpy.asarray(data_x,??
  38. ???????????????????????????????????????????????dtype=theano.config.floatX),??
  39. ?????????????????????????????????borrow=borrow)??
  40. ????????shared_y?=?theano.shared(numpy.asarray(data_y,??
  41. ???????????????????????????????????????????????dtype=theano.config.floatX),??
  42. ?????????????????????????????????borrow=borrow)??
  43. ????????return?shared_x,?T.cast(shared_y,?'int32')??
  44. ??
  45. ??
  46. ??
  47. ????train_set_x,?train_set_y?=?shared_dataset(train_data,train_label)??
  48. ????valid_set_x,?valid_set_y?=?shared_dataset(valid_data,valid_label)??
  49. ????test_set_x,?test_set_y?=?shared_dataset(test_data,test_label)??
  50. ????rval?=?[(train_set_x,?train_set_y),?(valid_set_x,?valid_set_y),??
  51. ????????????(test_set_x,?test_set_y)]??
  52. ????return?rval??

二、CNN的基本“構件”(LogisticRegression、HiddenLayer、LeNetConvPoolLayer)


卷積神經網絡(CNN)的基本結構就是輸入層、卷積層(conv)、子采樣層(pooling)、全連接層、輸出層(分類器)。 ?卷積層+子采樣層一般都會有若干個,本程序實現的CNN模型參考LeNet5,有兩個“卷積+子采樣層”LeNetConvPoolLayer。全連接層相當于MLP(多層感知機)中的隱含層HiddenLayer。輸出層即分類器,一般采用softmax回歸(也有人直接叫邏輯回歸,其實就是多類別的logistics regression),本程序也直接用LogisticRegression表示。

總結起來,要組建CNN模型,必須先定義LeNetConvPoolLayer、HiddenLayer、LogisticRegression這三種layer,這一點在我上一篇文章介紹CNN算法時講得很詳細,包括代碼注解,因為太冗長,這里給出鏈接:《DeepLearning tutorial(4)CNN卷積神經網絡原理簡介+代碼詳解》。

代碼太長,就不貼具體的了,只給出框架,具體可以下載我的代碼看看:

[python]?view plain?copy
  1. #分類器,即CNN最后一層,采用邏輯回歸(softmax)??
  2. class?LogisticRegression(object):??
  3. ????def?__init__(self,?input,?n_in,?n_out):??
  4. ????????self.W?=?....??
  5. ????????self.b?=?....??
  6. ????????self.p_y_given_x?=?...??
  7. ????????self.y_pred?=?...??
  8. ????????self.params?=?...??
  9. ????def?negative_log_likelihood(self,?y):??
  10. ????def?errors(self,?y):??
  11. ??
  12. #全連接層,分類器前一層??
  13. class?HiddenLayer(object):??
  14. ????def?__init__(self,?rng,?input,?n_in,?n_out,?W=None,?b=None,activation=T.tanh):??
  15. ????????self.input?=?input??
  16. ????????self.W?=?...??
  17. ????????self.b?=?...??
  18. ????????lin_output?=?...??
  19. ????????self.params?=?[self.W,?self.b]??
  20. ??
  21. #卷積+采樣層(conv+maxpooling)??
  22. class?LeNetConvPoolLayer(object):??
  23. ????def?__init__(self,?rng,?input,?filter_shape,?image_shape,?poolsize=(2,?2)):??
  24. ????????self.input?=?input??
  25. ????????self.W?=?...??
  26. ????????self.b?=?...??
  27. ????????#?卷積??
  28. ????????conv_out?=?...??
  29. ????????#?子采樣??
  30. ????????pooled_out?=...??
  31. ????????self.output?=?...??
  32. ????????self.params?=?[self.W,?self.b]??



三、組建CNN模型,設置優化算法,應用于Olivetti Faces進行人臉識別


上面定義好了CNN的幾個基本“構件”,現在我們使用這些構件來組建CNN模型,本程序的CNN模型參考LeNet5,具體為:input+layer0(LeNetConvPoolLayer)+layer1(LeNetConvPoolLayer)+layer2(HiddenLayer)+layer3(LogisticRegression)


這是一個串聯結構,代碼也很好寫,直接用第二部分定義好的各種layer去組建就行了,上一layer的輸出接下一layer的輸入,具體可以看看代碼evaluate_olivettifaces函數中的“建立CNN模型”部分。


CNN模型組建好了,就剩下用優化算法求解了,優化算法采用批量隨機梯度下降算法(MSGD),所以要先定義MSGD的一些要素,主要包括:代價函數,訓練、驗證、測試model、參數更新規則(即梯度下降)。這部分的代碼在evaluate_olivettifaces函數中的“定義優化算法的一些基本要素”部分。


優化算法的基本要素也定義好了,接下來就要運用人臉圖像數據集來訓練這個模型了,訓練過程有訓練步數(n_epoch)的設置,每個epoch會遍歷所有的訓練數據(training_set),本程序中也就是320個人臉圖。還有迭代次數iter,一次迭代遍歷一個batch里的所有樣本,具體為多少要看所設置的batch_size。關于參數的設定我在下面會討論。這一部分的代碼在evaluate_olivettifaces函數中的“訓練CNN階段”部分。

代碼很長,只貼框架,具體可以下載我的代碼看看:

[python]?view plain?copy
  1. def?evaluate_olivettifaces(learning_rate=0.05,?n_epochs=200,??
  2. ????????????????????dataset='olivettifaces.gif',??
  3. ????????????????????nkerns=[5,?10],?batch_size=40):?????
  4. ??
  5. ????#隨機數生成器,用于初始化參數....??
  6. ????#加載數據.....??
  7. ????#計算各數據集的batch個數....??
  8. ????#定義幾個變量,x代表人臉數據,作為layer0的輸入......??
  9. ??
  10. ????######################??
  11. ????#建立CNN模型:??
  12. ????#input+layer0(LeNetConvPoolLayer)+layer1(LeNetConvPoolLayer)+layer2(HiddenLayer)+layer3(LogisticRegression)??
  13. ????######################??
  14. ????...??
  15. ????....??
  16. ????......??
  17. ??
  18. ????#########################??
  19. ????#?定義優化算法的一些基本要素:代價函數,訓練、驗證、測試model、參數更新規則(即梯度下降)??
  20. ????#########################??
  21. ????...??
  22. ????....??
  23. ????......??
  24. ??
  25. ????#########################??
  26. ????#?訓練CNN階段,尋找最優的參數。??
  27. ????########################??
  28. ????...??
  29. ????.....??
  30. ????.......??


另外,值得一提的是,在訓練CNN階段,我們必須定時地保存模型的參數,這是在訓練機器學習算法時一個經常會做的事情,這一部分的詳細介紹我之前寫過一篇文章《DeepLearning tutorial(2)機器學習算法在訓練過程中保存參數》。簡單來說,我們要保存CNN模型中layer0、layer1、layer2、layer3的參數,所以在“訓練CNN階段”這部分下面,有一句代碼:

[python]?view plain?copy
  1. save_params(layer0.params,layer1.params,layer2.params,layer3.params)??


這個函數具體定義為:


[python]?view plain?copy
  1. #保存訓練參數的函數??
  2. def?save_params(param1,param2,param3,param4):????
  3. ????????import?cPickle????
  4. ????????write_file?=?open('params.pkl',?'wb')?????
  5. ????????cPickle.dump(param1,?write_file,?-1)??
  6. ????????cPickle.dump(param2,?write_file,?-1)??
  7. ????????cPickle.dump(param3,?write_file,?-1)??
  8. ????????cPickle.dump(param4,?write_file,?-1)??
  9. ????????write_file.close()????


如果在其他算法中,你要保存的參數有五個六個甚至更多,那么改一下這個函數的參數就行啦。



四、訓練結果以及參數設置的討論


ok,上面基本介紹完了CNN模型的構建,以及模型的訓練,我將它們的代碼都放在train_CNN_olivettifaces.py這個源文件中,將Olivetti Faces這張圖片跟這個代碼文件放在同個目錄下,運行這個文件,幾分鐘就可以訓練完模型,并且在同個目錄下得到一個params.pkl文件,這個文件保存的就是最后的模型的參數,方便你以后直接使用這個模型。

好了,現在討論一下怎么設置參數,具體來說,程序中可以設置的參數包括:學習速率learning_rate、batch_size、n_epochs、nkerns、poolsize。下面逐一討論調節它們時對模型的影響。

  • 調節learning_rate
學習速率learning_rate就是運用SGD算法時梯度前面的系數,非常重要,設得太大的話算法可能永遠都優化不了,設得太小會使算法優化得太慢,而且可能還會掉入局部最優。可以形象地將learning_rate比喻成走路時步子的大小,想象一下要從一個U形的山谷的一邊走到山谷最低點,如果步子特別大,像巨人那么大,那會直接從一邊跨到另一邊,然后又跨回這邊,如此往復。如果太小了,可能你走著走著就掉入了某些小坑,因為山路總是凹凸不平的(局部最優),掉入這些小坑后,如果步子還是不變,就永遠走不出那個坑。

好,回到本文的模型,下面是我使用時的記錄,固定其他參數,調節learning_rate:
(1)kerns=[20, 50], batch_size=40,poolsize=(2,2),learning_rate=0.1時,validation-error一直是97.5%,沒降下來,分析了一下,覺得應該是學習速率太大,跳過了最優。

(2)nkerns=[20, 50], batch_size=40,poolsize=(2,2),learning_rate=0.01時,訓練到epoch 60多時,validation-error降到5%,test-error降到15%

(3)nkerns=[20, 50], batch_size=40,poolsize=(2,2),learning_rate=0.05時,訓練到epoch 36時,validation-error降到2.5%,test-error降到5%

注意,驗證集和測試集都只有40張圖片,也就是說只有一兩張識別錯了,還是不錯的,數據集再大點,誤差率可以降到更小。最后我將learning_rate設置為0.05。

PS:學習速率應該自適應地減小,是有專門的一些算法的,本程序沒有實現這個功能,有時間再研究一下。


  • 調節batch_size
因為我們采用minibatch SGD算法來優化,所以是一個batch一個batch地將數據輸入CNN模型中,然后計算這個batch的所有樣本的平均損失,即代價函數是所有樣本的平均。而batch_size就是一個batch的所包含的樣本數,顯然batch_size將影響到模型的優化程度和速度。


回到本文的模型,首先因為我們train_dataset是320,valid_dataset和test_dataset都是40,所以batch_size最好都是40的因子,也就是能讓40整除,比如40、20、10、5、2、1,否則會浪費一些樣本,比如設置為30,則320/30=10,余數時20,這20個樣本是沒被利用的。并且,如果batch_size設置為30,則得出的validation-error和test-error只是30個樣本的錯誤率,并不是全部40個樣本的錯誤率。這是設置batch_size要注意的。特別是樣本比較少的時候。


下面是我實驗時的記錄,固定其他參數,改變batch_size:
batch_size=1、2、5、10、20時,validation-error一直是97.5%,沒降下來。我覺得可能是樣本類別覆蓋率過小,因為我們的數據是按類別排的,每個類別10個樣本是連續排在一起的,batch_size等于20時其實只包含了兩個類別,這樣優化會很慢。

因此最后我將batch_size設為40,也就是valid_dataset和test_dataset的大小了,沒辦法,原始數據集樣本太少了。一般我們都不會讓batch_size達到valid_dataset和test_dataset的大小的。


  • 關于n_epochs

n_epochs也就是最大的訓練步數,比如設為200,那訓練過程最多遍歷你的數據集200遍,當遍歷了200遍你的dataset時,程序會停止。n_epochs就相當于一個停止程序的控制參數,并不會影響CNN模型的優化程度和速度,只是一個控制程序結束的參數。


  • nkerns=[20, 50]

20表示第一個卷積層的卷積核的個數,50表示第二個卷積層的卷積核的個數。這個我也是瞎調的,暫時沒什么經驗可以總結。
不過從理論上來說,卷積核的個數其實就代表了特征的個數,你提取的特征越多,可能最后分類就越準。但是,特征太多(卷積核太多),會增加參數的規模,加大了計算復雜度,而且有時候卷積核也不是越多越好,應根據具體的應用對象來確定。所以我覺得,CNN雖號稱自動提取特征,免去復雜的特征工程,但是很多參數比如這里的nkerns還是需要去調節的,還是需要一些“人工”的。


下面是我的實驗記錄,固定batch_size=40,learning_rate=0.05,poolsize=(2,2):

(1)nkerns=[20, 50]時,訓練到epoch 36時,validation-error降到2.5%,test-error降到5%

(2)nkerns=[10, 30]時,訓練到epoch 46時,validation-error降到5%,test-error降到5%

(3)nkerns=[5, 10]時,訓練到epoch 38時,validation-error降到5%,test-error降到7.5%


  • poolsize=(2, 2)


poolzize在本程序中是設置為(2,2),即從一個2*2的區域里maxpooling出1個像素,說白了就算4和像素保留成1個像素。本例程中人臉圖像大小是57*47,對這種小圖像來說,(2,2)時比較合理的。如果你用的圖像比較大,可以把poolsize設的大一點。




+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++分割線+++++++++++++++++++++++++++++++++++++++++++



上面部分介紹完了CNN模型構建以及模型訓練的過程,代碼都在train_CNN_olivettifaces.py里面,訓練完可以得到一個params.pkl文件,這個文件保存的就是最后的模型的參數,方便你以后直接使用這個模型。以后只需利用這些保存下來的參數來初始化CNN模型,就得到一個可以使用的CNN系統,將人臉圖輸入這個CNN系統,預測人臉圖的類別。

接下來就介紹怎么使用訓練好的參數的方法,這部分的代碼放在use_CNN_olivettifaces.py文件中。




五、利用訓練好的參數初始化模型


在train_CNN_olivettifaces.py中的LeNetConvPoolLayer、HiddenLayer、LogisticRegression是用隨機數生成器去隨機初始化的,我們將它們定義為可以用參數來初始化的版本,其實很簡單,代碼只需要做稍微的改動,只需要在LogisticRegression、HiddenLayer、LeNetConvPoolLayer這三個class的__init__()函數中加兩個參數params_W,params_b,然后將params_W,params_b賦值給這三個class里的W和b:

[python]?view plain?copy
  1. self.W?=?params_W??
  2. self.b?=?params_b??

params_W,params_b就是從params.pkl文件中讀取來的,讀取的函數:
[python]?view plain?copy
  1. #讀取之前保存的訓練參數??
  2. #layer0_params~layer3_params都是包含W和b的,layer*_params[0]是W,layer*_params[1]是b??
  3. def?load_params(params_file):??
  4. ????f=open(params_file,'rb')??
  5. ????layer0_params=cPickle.load(f)??
  6. ????layer1_params=cPickle.load(f)??
  7. ????layer2_params=cPickle.load(f)??
  8. ????layer3_params=cPickle.load(f)??
  9. ????f.close()??
  10. ????return?layer0_params,layer1_params,layer2_params,layer3_params??


ok,可以用參數初始化的CNN定義好了,那現在就將需要測試的人臉圖輸入該CNN,測試其類別。同樣的,需要寫一個讀圖像的函數load_data(),代碼就不貼了。將圖像數據輸入,CNN的輸出便是該圖像的類別,這一部分的代碼在use_CNN()函數中,代碼很容易看懂。

這一部分還涉及到theano.function的使用,我把一些筆記記在了use_CNN_olivettifaces.py代碼的最后,因為跟代碼相關,結合代碼來看會比較好,所以下面就不講這部分,有興趣的看看代碼。

最后說說測試的結果,我仍然以整副olivettifaces.gif作為輸入,得出其類別后,跟真正的label對比,程序輸出被錯分的那些圖像,運行結果如下:



錯了五張,我標了三張:





六、一些需要說明的


首先是本文的嚴謹性:在文章開頭我就說這只是一個toy implement,400張圖片根本不適合用DL來做。
當然我寫這篇文章,只是為了總結一下這個實現流程,這一點希望對讀者也有參考意義。

最后,我的代碼都放在這里:github地址,可以下載

@author:wepon

@blog:http://blog.csdn.net/u012162613/article/details/43277187


本文代碼下載地址:我的github


本文主要講解將CNN應用于人臉識別的流程,程序基于Python+numpy+theano+PIL開發,采用類似LeNet5的CNN模型,應用于olivettifaces人臉數據庫,實現人臉識別的功能,模型的誤差降到了5%以下。本程序只是個人學習過程的一個toy implement,樣本很小,模型隨時都會過擬合。

但是,本文意在理清程序開發CNN模型的具體步驟,特別是針對圖像識別,從拿到圖像數據庫,到實現一個針對這個圖像數據庫的CNN模型,我覺得本文對這些流程的實現具有參考意義。


《本文目錄》

一、olivettifaces人臉數據庫介紹

二、CNN的基本“構件”(LogisticRegression、HiddenLayer、LeNetConvPoolLayer)

三、組建CNN模型,設置優化算法,應用于Olivetti Faces進行人臉識別

四、訓練結果以及參數設置的討論

五、利用訓練好的參數初始化模型

六、一些需要說明的


一、olivettifaces人臉數據庫介紹


Olivetti Faces是紐約大學的一個比較小的人臉庫,由40個人的400張圖片構成,即每個人的人臉圖片為10張。每張圖片的灰度級為8位,每個像素的灰度大小位于0-255之間,每張圖片大小為64×64。如下圖,這個圖片大小是1190*942,一共有20*20張人臉,故每張人臉大小是(1190/20)*(942/20)即57*47=2679:



本文所用的訓練數據就是這張圖片,400個樣本,40個類別,乍一看樣本好像比較小,用CNN效果會好嗎?先別下結論,請往下看。

要運行CNN算法,這張圖片必須先轉化為數組(或者說矩陣),這個用到python的圖像庫PIL,幾行代碼就可以搞定,具體的方法我之前剛好寫過一篇文章,也是用這張圖,考慮到文章冗長,就不復制過來了,鏈接在此:《利用Python PIL、cPickle讀取和保存圖像數據庫》。


訓練機器學習算法,我們一般將原始數據分成訓練數據(training_set)、驗證數據(validation_set)、測試數據(testing_set)。本程序將training_set、validation_set、testing_set分別設置為320、40、40個樣本。它們的label為0~39,對應40個不同的人。這部分的代碼如下:

  1. """?
  2. 加載圖像數據的函數,dataset_path即圖像olivettifaces的路徑?
  3. 加載olivettifaces后,劃分為train_data,valid_data,test_data三個數據集?
  4. 函數返回train_data,valid_data,test_data以及對應的label?
  5. """??
  6. def?load_data(dataset_path):??
  7. ????img?=?Image.open(dataset_path)??
  8. ????img_ndarray?=?numpy.asarray(img,?dtype='float64')/256??
  9. ????faces=numpy.empty((400,2679))??
  10. ????for?row?in?range(20):??
  11. ???????for?column?in?range(20):??
  12. ????????faces[row*20+column]=numpy.ndarray.flatten(img_ndarray?[row*57:(row+1)*57,column*47:(column+1)*47])??
  13. ??
  14. ????label=numpy.empty(400)??
  15. ????for?i?in?range(40):??
  16. ????label[i*10:i*10+10]=i??
  17. ????label=label.astype(numpy.int)??
  18. ??
  19. ????#分成訓練集、驗證集、測試集,大小如下??
  20. ????train_data=numpy.empty((320,2679))??
  21. ????train_label=numpy.empty(320)??
  22. ????valid_data=numpy.empty((40,2679))??
  23. ????valid_label=numpy.empty(40)??
  24. ????test_data=numpy.empty((40,2679))??
  25. ????test_label=numpy.empty(40)??
  26. ??
  27. ????for?i?in?range(40):??
  28. ????train_data[i*8:i*8+8]=faces[i*10:i*10+8]??
  29. ????train_label[i*8:i*8+8]=label[i*10:i*10+8]??
  30. ????valid_data[i]=faces[i*10+8]??
  31. ????valid_label[i]=label[i*10+8]??
  32. ????test_data[i]=faces[i*10+9]??
  33. ????test_label[i]=label[i*10+9]??
  34. ??
  35. ????#將數據集定義成shared類型,才能將數據復制進GPU,利用GPU加速程序。??
  36. ????def?shared_dataset(data_x,?data_y,?borrow=True):??
  37. ????????shared_x?=?theano.shared(numpy.asarray(data_x,??
  38. ???????????????????????????????????????????????dtype=theano.config.floatX),??
  39. ?????????????????????????????????borrow=borrow)??
  40. ????????shared_y?=?theano.shared(numpy.asarray(data_y,??
  41. ???????????????????????????????????????????????dtype=theano.config.floatX),??
  42. ?????????????????????????????????borrow=borrow)??
  43. ????????return?shared_x,?T.cast(shared_y,?'int32')??
  44. ??
  45. ??
  46. ??
  47. ????train_set_x,?train_set_y?=?shared_dataset(train_data,train_label)??
  48. ????valid_set_x,?valid_set_y?=?shared_dataset(valid_data,valid_label)??
  49. ????test_set_x,?test_set_y?=?shared_dataset(test_data,test_label)??
  50. ????rval?=?[(train_set_x,?train_set_y),?(valid_set_x,?valid_set_y),??
  51. ????????????(test_set_x,?test_set_y)]??
  52. ????return?rval??

二、CNN的基本“構件”(LogisticRegression、HiddenLayer、LeNetConvPoolLayer)




代碼太長,就不貼具體的了,只給出框架,具體可以下載我的代碼看看:



三、組建CNN模型,設置優化算法,應用于Olivetti Faces進行人臉識別


上面定義好了CNN的幾個基本“構件”,現在我們使用這些構件來組建CNN模型,本程序的CNN模型參考LeNet5,具體為:input+layer0(LeNetConvPoolLayer)+layer1(LeNetConvPoolLayer)+layer2(HiddenLayer)+layer3(LogisticRegression)


這是一個串聯結構,代碼也很好寫,直接用第二部分定義好的各種layer去組建就行了,上一layer的輸出接下一layer的輸入,具體可以看看代碼evaluate_olivettifaces函數中的“建立CNN模型”部分。


CNN模型組建好了,就剩下用優化算法求解了,優化算法采用批量隨機梯度下降算法(MSGD),所以要先定義MSGD的一些要素,主要包括:代價函數,訓練、驗證、測試model、參數更新規則(即梯度下降)。這部分的代碼在evaluate_olivettifaces函數中的“定義優化算法的一些基本要素”部分。


優化算法的基本要素也定義好了,接下來就要運用人臉圖像數據集來訓練這個模型了,訓練過程有訓練步數(n_epoch)的設置,每個epoch會遍歷所有的訓練數據(training_set),本程序中也就是320個人臉圖。還有迭代次數iter,一次迭代遍歷一個batch里的所有樣本,具體為多少要看所設置的batch_size。關于參數的設定我在下面會討論。這一部分的代碼在evaluate_olivettifaces函數中的“訓練CNN階段”部分。

代碼很長,只貼框架,具體可以下載我的代碼看看:



另外,值得一提的是,在訓練CNN階段,我們必須定時地保存模型的參數,這是在訓練機器學習算法時一個經常會做的事情,這一部分的詳細介紹我之前寫過一篇文章《DeepLearning tutorial(2)機器學習算法在訓練過程中保存參數》。簡單來說,我們要保存CNN模型中layer0、layer1、layer2、layer3的參數,所以在“訓練CNN階段”這部分下面,有一句代碼:

[python]?view plain?copy
  1. save_params(layer0.params,layer1.params,layer2.params,layer3.params)??


這個函數具體定義為:


[python]?view plain?copy
  1. #保存訓練參數的函數??
  2. def?save_params(param1,param2,param3,param4):????
  3. ????????import?cPickle????
  4. ????????write_file?=?open('params.pkl',?'wb')?????
  5. ????????cPickle.dump(param1,?write_file,?-1)??
  6. ????????cPickle.dump(param2,?write_file,?-1)??
  7. ????????cPickle.dump(param3,?write_file,?-1)??
  8. ????????cPickle.dump(param4,?write_file,?-1)??
  9. ????????write_file.close()????


如果在其他算法中,你要保存的參數有五個六個甚至更多,那么改一下這個函數的參數就行啦。



四、訓練結果以及參數設置的討論







  • 調節batch_size
因為我們采用minibatch SGD算法來優化,所以是一個batch一個batch地將數據輸入CNN模型中,然后計算這個batch的所有樣本的平均損失,即代價函數是所有樣本的平均。而batch_size就是一個batch的所包含的樣本數,顯然batch_size將影響到模型的優化程度和速度。


回到本文的模型,首先因為我們train_dataset是320,valid_dataset和test_dataset都是40,所以batch_size最好都是40的因子,也就是能讓40整除,比如40、20、10、5、2、1,否則會浪費一些樣本,比如設置為30,則320/30=10,余數時20,這20個樣本是沒被利用的。并且,如果batch_size設置為30,則得出的validation-error和test-error只是30個樣本的錯誤率,并不是全部40個樣本的錯誤率。這是設置batch_size要注意的。特別是樣本比較少的時候。


下面是我實驗時的記錄,固定其他參數,改變batch_size:
batch_size=1、2、5、10、20時,validation-error一直是97.5%,沒降下來。我覺得可能是樣本類別覆蓋率過小,因為我們的數據是按類別排的,每個類別10個樣本是連續排在一起的,batch_size等于20時其實只包含了兩個類別,這樣優化會很慢。

因此最后我將batch_size設為40,也就是valid_dataset和test_dataset的大小了,沒辦法,原始數據集樣本太少了。一般我們都不會讓batch_size達到valid_dataset和test_dataset的大小的。


  • 關于n_epochs

n_epochs也就是最大的訓練步數,比如設為200,那訓練過程最多遍歷你的數據集200遍,當遍歷了200遍你的dataset時,程序會停止。n_epochs就相當于一個停止程序的控制參數,并不會影響CNN模型的優化程度和速度,只是一個控制程序結束的參數。


  • nkerns=[20, 50]

20表示第一個卷積層的卷積核的個數,50表示第二個卷積層的卷積核的個數。這個我也是瞎調的,暫時沒什么經驗可以總結。
不過從理論上來說,卷積核的個數其實就代表了特征的個數,你提取的特征越多,可能最后分類就越準。但是,特征太多(卷積核太多),會增加參數的規模,加大了計算復雜度,而且有時候卷積核也不是越多越好,應根據具體的應用對象來確定。所以我覺得,CNN雖號稱自動提取特征,免去復雜的特征工程,但是很多參數比如這里的nkerns還是需要去調節的,還是需要一些“人工”的。


下面是我的實驗記錄,固定batch_size=40,learning_rate=0.05,poolsize=(2,2):

(1)nkerns=[20, 50]時,訓練到epoch 36時,validation-error降到2.5%,test-error降到5%

(2)nkerns=[10, 30]時,訓練到epoch 46時,validation-error降到5%,test-error降到5%

(3)nkerns=[5, 10]時,訓練到epoch 38時,validation-error降到5%,test-error降到7.5%


  • poolsize=(2, 2)


poolzize在本程序中是設置為(2,2),即從一個2*2的區域里maxpooling出1個像素,說白了就算4和像素保留成1個像素。本例程中人臉圖像大小是57*47,對這種小圖像來說,(2,2)時比較合理的。如果你用的圖像比較大,可以把poolsize設的大一點。




+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++分割線+++++++++++++++++++++++++++++++++++++++++++



上面部分介紹完了CNN模型構建以及模型訓練的過程,代碼都在train_CNN_olivettifaces.py里面,訓練完可以得到一個params.pkl文件,這個文件保存的就是最后的模型的參數,方便你以后直接使用這個模型。以后只需利用這些保存下來的參數來初始化CNN模型,就得到一個可以使用的CNN系統,將人臉圖輸入這個CNN系統,預測人臉圖的類別。

接下來就介紹怎么使用訓練好的參數的方法,這部分的代碼放在use_CNN_olivettifaces.py文件中。




五、利用訓練好的參數初始化模型













六、一些需要說明的




本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/news/387223.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/387223.shtml
英文地址,請注明出處:http://en.pswp.cn/news/387223.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

操作系統05死鎖

進程管理4--Deadlock and Starvation Concurrency: Deadlock and Starvation 內容提要 >產生死鎖與饑餓的原因 >解決死鎖的方法 >死鎖/同步的經典問題:哲學家進餐問題 Deadlock 系統的一種隨機性錯誤 Permanent blocking of a set of processes that eith…

CNN tensorflow 人臉識別

數據材料這是一個小型的人臉數據庫,一共有40個人,每個人有10張照片作為樣本數據。這些圖片都是黑白照片,意味著這些圖片都只有灰度0-255,沒有rgb三通道。于是我們需要對這張大圖片切分成一個個的小臉。整張圖片大小是1190 942&am…

數據結構01緒論

第一章緒論 1.1 什么是數據結構 數據結構是一門研究非數值計算的程序設計問題中,計算機的操作對象以及他們之間的關系和操作的學科。 面向過程程序數據結構算法 數據結構是介于數學、計算機硬件、計算機軟件三者之間的一門核心課程。 數據結構是程序設計、編譯…

css3動畫、2D與3D效果

1.兼容性 css3針對同一樣式在不同瀏覽器的兼容 需要在樣式屬性前加上內核前綴; 谷歌(chrome) -webkit-transition: Opera(歐鵬) -o-transition: Firefox(火狐) -moz-transition Ie -ms-tr…

ES6學習筆記(六)數組的擴展

1.擴展運算符 1.1含義 擴展運算符(spread)是三個點(...)。它好比 rest 參數的逆運算,將一個數組轉為用逗號分隔的參數序列。 console.log(...[1, 2, 3]) // 1 2 3console.log(1, ...[2, 3, 4], 5) // 1 2 3 4 5[...doc…

數據結構02線性表

第二章 線性表 C中STL順序表:vector http://blog.csdn.net/weixin_37289816/article/details/54710677鏈表:list http://blog.csdn.net/weixin_37289816/article/details/54773406在數據元素的非空有限集中: (1)存在唯一一個被稱作“第…

訓練一個神經網絡 能讓她認得我

寫個神經網絡,讓她認得我(?????)(Tensorflow,opencv,dlib,cnn,人臉識別) 這段時間正在學習tensorflow的卷積神經網絡部分,為了對卷積神經網絡能夠有一個更深的了解,自己動手實現一個例程是比較好的方式,所以就選了一個這樣比…

數據結構03棧和隊列

第三章棧和隊列 STL棧:stack http://blog.csdn.net/weixin_37289816/article/details/54773495隊列:queue http://blog.csdn.net/weixin_37289816/article/details/54773581priority_queue http://blog.csdn.net/weixin_37289816/article/details/5477…

Java動態編譯執行

在某些情況下,我們需要動態生成java代碼,通過動態編譯,然后執行代碼。JAVA API提供了相應的工具(JavaCompiler)來實現動態編譯。下面我們通過一個簡單的例子介紹,如何通過JavaCompiler實現java代碼動態編譯…

樹莓派pwm驅動好盈電調及伺服電機

本文講述如何通過樹莓派的硬件PWM控制好盈電調來驅動RC車子的前進后退,以及如何驅動伺服電機來控制車子轉向。 1. 好盈電調簡介 車子上的電調型號為:WP-10BLS-A-RTR,在好盈官網并沒有搜到對應手冊,但找到一份通用RC競速車的電調使…

數據結構04串

第四章 串 STL:string http://blog.csdn.net/weixin_37289816/article/details/54716009計算機上非數值處理的對象基本上是字符串數據。 在不同類型的應用中,字符串具有不同的特點,要有效的實現字符串的處理,必須選用合適的存儲…

CAS單點登錄原理解析

CAS單點登錄原理解析 SSO英文全稱Single Sign On,單點登錄。SSO是在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統。CAS是一種基于http協議的B/S應用系統單點登錄實現方案,認識CAS之前首先要熟悉http協議、Session與Co…

JDK1.6版添加了新的ScriptEngine類,允許用戶直接執行js代碼。

JDK1.6版添加了新的ScriptEngine類,允許用戶直接執行js代碼。在Java中直接調用js代碼 不能調用瀏覽器中定義的js函數,會拋出異常提示ReferenceError: “alert” is not defined。[java] view plaincopypackage com.sinaapp.manjushri; import javax.sc…

數據結構05數組和廣義表

第五章 數組 和 廣義表 數組和廣義表可以看成是線性表在下述含義上的擴展:表中的數據元素本身也是一個數據結構。 5.1 數組的定義 n維數組中每個元素都受著n個關系的約束,每個元素都有一個直接后繼元素。 可以把二維數組看成是這樣一個定長線性表&…

k8s的ingress使用

ingress 可以配置一個入口來提供k8s上service從外部來訪問的url、負載平衡流量、終止SSL和提供基于名稱的虛擬主機。 配置ingress的yaml: 要求域名解析無誤 要求service對應的pod正常 一、test1.domain.com --> service1:8080 apiVersion: extensions/v1beta1…

JDK1.8中如何用ScriptEngine動態執行JS

JDK1.8中如何用ScriptEngine動態執行JS jdk1.6開始就提供了動態腳本語言諸如JavaScript動態的支持。這無疑是一個很好的功能,畢竟Java的語法不是適合成為動態語言。而JDK通過執行JavaScript腳本可以彌補這一不足。這也符合“Java虛擬機不僅僅是Java一種語言的虛擬機…

數據結構06樹和二叉樹

第六章 樹和二叉樹 6.1 樹的定義和基本術語 樹 Tree 是n個結點的有限集。 任意一棵非空樹中: (1)有且僅有一個特定的稱為根(root)的結點; (2)當n>1時,其余結點可…

2019.03.20 mvt,Django分頁

MVT模式 MVT各部分的功能: M全拼為Model,與MVC中的M功能相同,負責和數據庫交互,進行數據處理。 V全拼為View,與MVC中的C功能相同,接收請求,進行業務處理,返回響應。 T全拼為Tem…

CountDownLatch,CyclicBarrier和Semaphore

在java 1.5中,提供了一些非常有用的輔助類來幫助我們進行并發編程,比如CountDownLatch,CyclicBarrier和Semaphore,今天我們就來學習一下這三個輔助類的用法。以下是本文目錄大綱:一.CountDownLatch用法二.CyclicBarrie…

數據結構07排序

第十章內部排序 10.1 概述 排序就是把一組數據按關鍵字的大小有規律地排列。經過排序的數據更易于查找。 排序前KiKj,且Ki在前: 排序方法是穩定的,若排序后Ki在前; 排序方法是不穩定的,如排序后Kj在前。 分類: 內…