DeepLearning tutorial(5)CNN卷积神经网络应用于人脸识别(详细流程+代码实现)
DeepLearning tutorial(5)CNN卷積神經網絡應用于人臉識別(詳細流程+代碼實現)
@author:wepon
@blog:http://blog.csdn.net/u012162613/article/details/43277187
本文代碼下載地址:我的github
本文主要講解將CNN應用于人臉識別的流程,程序基于python+numpy+theano+PIL開發,采用類似LeNet5的CNN模型,應用于olivettifaces人臉數據庫,實現人臉識別的功能,模型的誤差降到了5%以下。本程序只是個人學習過程的一個toy implement,模型可能存在overfitting,因為樣本小,這一點也無從驗證。
但是,本文意在理清程序開發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 plaincopy
二、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 plaincopy
三、組建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 plaincopy
另外,值得一提的是,在訓練CNN階段,我們必須定時地保存模型的參數,這是在訓練機器學習算法時一個經常會做的事情,這一部分的詳細介紹我之前寫過一篇文章《DeepLearning tutorial(2)機器學習算法在訓練過程中保存參數》。簡單來說,我們要保存CNN模型中layer0、layer1、layer2、layer3的參數,所以在“訓練CNN階段”這部分下面,有一句代碼:
[python] view plaincopy
這個函數具體定義為:
[python] view plaincopy
如果在其他算法中,你要保存的參數有五個六個甚至更多,那么改一下這個函數的參數就行啦。
四、訓練結果以及參數設置的討論
ok,上面基本介紹完了CNN模型的構建,以及模型的訓練,我將它們的代碼都放在train_CNN_olivettifaces.py這個源文件中,將Olivetti Faces這張圖片跟這個代碼文件放在同個目錄下,運行這個文件,幾分鐘就可以訓練完模型,并且在同個目錄下得到一個params.pkl文件,這個文件保存的就是最后的模型的參數,方便你以后直接使用這個模型。
好了,現在討論一下怎么設置參數,具體來說,程序中可以設置的參數包括:學習速率learning_rate、batch_size、n_epochs、nkerns、poolsize。下面逐一討論調節它們時對模型的影響。
- 調節learning_rate
好,回到本文的模型,下面是我使用時的記錄,固定其他參數,調節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
回到本文的模型,首先因為我們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 plaincopy
params_W,params_b就是從params.pkl文件中讀取來的,讀取的函數:
[python] view plaincopy
ok,可以用參數初始化的CNN定義好了,那現在就將需要測試的人臉圖輸入該CNN,測試其類別。同樣的,需要寫一個讀圖像的函數load_data(),代碼就不貼了。將圖像數據輸入,CNN的輸出便是該圖像的類別,這一部分的代碼在use_CNN()函數中,代碼很容易看懂。
這一部分還涉及到theano.function的使用,我把一些筆記記在了use_CNN_olivettifaces.py代碼的最后,因為跟代碼相關,結合代碼來看會比較好,所以下面就不講這部分,有興趣的看看代碼。
最后說說測試的結果,我仍然以整副olivettifaces.gif作為輸入,得出其類別后,跟真正的label對比,程序輸出被錯分的那些圖像,運行結果如下:
錯了五張,我標了三張:
六、一些需要說明的
首先是本文的嚴謹性:在文章開頭我就說這只是一個toy implement,我從建立這個模型到開發實現,用的時間比寫這篇文章的時間還少,所以后來我覺得雖然錯分率比較低,但是很可能是模型overfitting了,為什么?樣本太少,可能這個模型就是在這400張圖片里面跑的效果比較好。你讓這40個人再拍一些照片來測這個模型,可能效果就不好了。這個是我最后要說明的,以免誤導。 當然我寫這篇文章,只是為了總結一下這個實現流程,這一點希望對讀者也有參考意義。
最后,我的代碼都放在這里:github地址,可以下載
備注:?【細思,這個模型很不合理,嚴重過擬合。但代碼框架還是可以用的。大牛請輕噴】
總結
以上是生活随笔為你收集整理的DeepLearning tutorial(5)CNN卷积神经网络应用于人脸识别(详细流程+代码实现)的全部內容,希望文章能夠幫你解決所遇到的問題。

- 上一篇: 【DeepLearning工具】Fedo
- 下一篇: 《Neural networks and