1. 程式人生 > 其它 >人臉識別 | 卷積深度置信網路工具箱的使用

人臉識別 | 卷積深度置信網路工具箱的使用

本文主要以ORL_64x64人臉資料庫識別為例,介紹如何使用基於matlab的CDBN工具箱。至於卷積深度置信網路(CDBN,Convolutional Deep Belief Network)的理論知識,只給出筆者整理的一些學習資源。

01 卷積深度置信網路理論知識

參考資料 datas 1、CSDN部落格---受限玻爾茲曼機(RBM)學習筆記 (http://blog.csdn.net/itplus/article/details/19168937) 2、CSDN部落格---深度信念網路 (Deep Belief Network)(http://blog.csdn.net/losteng/article/details/51001247) 3、知乎---卷積神經網路工作原理直觀的解釋 (https://www.zhihu.com/question/39022858) 4、量子位公眾號---一文了解各種卷積結構原理及優劣 (https://mp.weixin.qq.com/s/47G744L87geyBIjxeSeoqQ) 5、中國知網博碩論文---基於卷積深度置信網路的歌手識別 (http://kns.cnki.net/KCMS/detail/detail.aspx?dbcode=CMFD&dbname=CMFD201601&filename=1015987604.nh&v=MTgzODg3RGgxVDNxVHJXTTFGckNVUkwyZmIrZG1GQ3ZrVUwvTFZGMjZHN3F3R2RmTXE1RWJQSVI4ZVgxTHV4WVM=) 6、CDBN paper(全英) (http://ai.stanford.edu/~ang/papers/icml09-ConvolutionalDeepBeliefNetworks.pdf)

02

CDBN工具箱簡介

據筆者瞭解,目前,比較流行的深度學習框架,如TensorFlow、DeepLearning4j等不支援CDBN。GitHub上有基於Matlab的CDBN工具箱:CDBN工具箱下載連結(https://github.com/lonl/CDBN)

下面簡要介紹該工具箱。

從GitHub上下載的壓縮包解壓後再開啟,檔案目錄如下:

CDBN工具箱的檔案目錄

其中,最為重要的肯定是toolbox。toolbox裡面有三個lib,分別是CDBN,DBN,Softmax庫。本文將用到CDBN和Softmax兩個庫。

toolbox下的三個lib

需要注意的是,由於這個工具箱不是官方版的,因此可能存在某些bug

,後面會涉及到筆者使用工具箱過程中的一些經驗。

03

神經網路結構

介紹一下本文搭建的進行人臉識別的卷積深度置信網路的結構。

  • 主體結構:兩個卷積受限玻爾茲曼機(CRBM,Convolutional Restricted Boltzmann Machine)堆疊(每個CRBM後都接有池化層),頂層採用Softmax,實現分類。
  • 第一個CRBM:

第一個CRBM引數

  • 第二個CRBM:

第二個CRBM引數

  • Softmax層

神經元個數40個,最大迭代次數maxIter=1000,代價函式為 交叉熵代價函式(Cross-Entropy Error)

  • 其他引數 其他諸如學習速率等的引數使用CDBN-mastertoolboxCDBNLIBdefault_layer2D.m中的預設值。

04

程式設計

以下講解程式設計步驟。

步驟1、安裝工具箱

只需執行setup_toolbox.m即可。 安裝工具箱其實只是把用到的一些函式新增到matlab的搜尋路徑,因 此你完全可以把工具箱內所有的檔案都複製到你當前的路徑下,不過肯定麻煩啦!

步驟2、載入和矩陣化資料

%load data dataFortrain=load('ORL_64x64StTrainFile1.txt');%注意修改路徑 train_data=dataFortrain(:,1:end-1)';%訓練樣本 train_data=reshape(train_data,[64,64,1,360]);%矩陣化訓練樣本 trainL=dataFortrain(:,end);%訓練樣本標籤 dataFortest=load('ORL_64x64StTestFile1.txt');%注意修改路徑 test_data=dataFortest(:,1:end-1)';%測試樣本 test_data=reshape(test_data,[64,64,1,40]);%注意修改路徑 testL=dataFortest(:,end);%測試樣本標籤

重點講一下第四行。

StTrainFile1.txt中有360行,4097列。每一行是一幅人臉影象(畫素為64X64=4096)的4096個灰度值,最後一列是該幅人臉影象的標籤(1-40),表明其屬於哪個人的(共40人,即分類數目為40)。由此可見,一幅二維影象(矩陣)被拉成了向量進行儲存,因此在資料輸入CDBN前,我們要對向量進行矩陣化,呼叫matlab的reshape方法,最終生成一個4維的矩陣,四個維度分別是64,64,1,360(樣本數)。倒數第二行同理。

步驟3、定義層引數

工具箱把一層layer定義為一個struct物件。

%INITIALIZE THE PARAMETERS OF THE NETWORK 
%first layer setting
layer{1} = default_layer2D();
layer{1}.inputdata=train_data;%輸入訓練樣本
layer{1}.n_map_v=1;
layer{1}.n_map_h=9;
layer{1}.s_filter=[7 7];
layer{1}.stride=[1 1];
layer{1}.s_pool=[2 2];
 layer{1}.batchsize=90;
layer{1}.n_epoch=1;
%second layer setting
layer{2} = default_layer2D();
layer{2}.n_map_v=9;
layer{2}.n_map_h=16;
layer{2}.s_filter=[5 5];
layer{2}.stride=[1 1];
layer{2}.s_pool=[2 2];
layer{2}.batchsize=10;
layer{2}.n_epoch=1;

需要注意的是,layer{i}=default_layer2D()這條語句是必須的,且必須位於所有層引數定義語句的最前面。原因:如果layer{i}=default_layer2D()這條語句不位於最前面的話,在這條語句前面的引數賦值語句實質不起作用,這些引數還是取預設值。特別是對於第一層,因為default_layer2D()方法中是沒有定義inputdata欄位的,如果layer{1}.inputdata=train_data這條語句位於layer{1}=default_layer2D()前面,則會出現“使用未定義欄位”的錯誤。

步驟4、訓練CDBN網路

這個過程是無監督學習,只需呼叫cdbn2D方法即可。

在呼叫cdbn2D方法之前,CDBN-mastertoolboxCDBNLIBmex中的crbm_forward2D_batch_mex.c要先用mex命令編譯生成crbm_forward2D_batch_mex.mexw64檔案才能供matlab呼叫

mex crbm_forward2D_batch_mex.c

在編譯前,crbm_forward2D_batch_mex.c要先修改:128行的out_id要改成在最開始的位置定義,否則編譯時會出現“缺少:在型別前面’”的報錯資訊(PS:第一次遇到這麼奇葩的報錯,當時懷疑C語言是不是白學了),原因:VS2010的C編譯器只支援C89標準,對C99標準支援不完全,而在C89標準中,變數需要放到函式體的前面宣告,先宣告再使用。

%% ----------- GO TO 2D CONVOLUTIONAL DEEP BELIEF NETWORKS ------------------%% tic; [model,layer] = cdbn2D(layer); save('model_parameter','model','layer'); toc; trainD = model{1}.output;%訓練樣本的第一個CRBM的輸出,是一個4維矩陣 trainD1 = model{2}.output;%訓練樣本的第二個CRBM的輸出,是一個4維矩陣

我們來比較一下train_data、trainD、trainD1的大小

train_data、trainD、trainD1

現在再看看卷積神經網路的圖示,是不是很好理解了呢?

卷積神經網路圖示

步驟5、將測試樣本輸入訓練好的CDBN網路,提取高維特徵

這段程式碼可以直接copy,修改好變數名即可!

%% ------------ TESTDATA FORWARD MODEL WITH THE PARAMETERS ------------------ %% % FORWARD MODEL OF NETWORKS H = length(layer); layer{1}.inputdata = test_data; fprintf('output the testdata features:>>...n'); tic; if H >= 2 % PREPROCESSS INPUTDATA TO BE SUITABLE FOR TRAIN layer{1} = preprocess_train_data2D(layer{1}); model{1}.output = crbm_forward2D_batch_mex(model{1},layer{1},layer{1}.inputdata); for i = 2:H layer{i}.inputdata = model{i-1}.output; layer{i} = preprocess_train_data2D(layer{i}); model{i}.output = crbm_forward2D_batch_mex(model{i},layer{i},layer{i}.inputdata); end else layer{1} = preprocess_train_data2D(layer{1}); model{1}.output = crbm_forward2D_batch_mex(model{1},layer{1},layer{1}.inputdata); end testD = model{1}.output;%訓練樣本的第一個CRBM的輸出,是一個4維矩陣 testD1 = model{2}.output;%訓練樣本的第二個CRBM的輸出,是一個4維矩陣 toc;

同樣的,我們來看一下test_data、testD、testD1的大小:

test_data、testD、testD1的大小比較

步驟6、訓練Softmax分類器,同時進行識別

這裡我們用到 softmaxExercise(inputData,labels,inputData_t,labels_t)這個函式

引數說明:

- inputdata:訓練樣本的CDBN輸出,要求是二維矩陣 -labels:訓練樣本的標籤 -inputData_t:測試樣本的CDBN輸出,要求是二維矩陣 -labels_t:測試樣本的標籤

由於CDBN的輸出是4維矩陣,因此在訓練Softmax分類器前,需要把矩陣拉成向量(和之前的過程相反)。程式碼如下,可直接copy,修改變數名即可!

%% ------------------------------- Softmax ---------------------------------- %%

fprintf('train the softmax:>>...n');

tic;

% TRANSLATE THE OUTPUT TO ONE VECTOR
trainDa = [];
trainLa=trainL;
for i= 1:size(trainD,4)
a1 = [];
a2 = [];
a3 = [];
for j = 1:size(trainD,3)
    a1 = [a1;reshape(trainD(:,:,j,i),size(trainD,2)*size(trainD,1),1)];
end

for j = 1:size(trainD1,3)
    a2 = [a2;reshape(trainD1(:,:,j,i),size(trainD1,2)*size(trainD1,1),1)];
end
a3 = [a3;a1;a2];
trainDa = [trainDa,a3];
end

testDa = [];
testLa=testL;
for i= 1:size(testD,4)
b1 = [];
b2 = [];
b3 = [];
for j = 1:size(testD,3)
    b1 = [b1;reshape(testD(:,:,j,i),size(testD,2)*size(testD,1),1)];
end

for j =1:size(testD1,3)
    b2 = [b2;reshape(testD1(:,:,j,i),size(testD1,2)*size(testD1,1),1)];
end
b3 = [b3;b1;b2];
testDa = [testDa,b3];
end

我們來看一下拉成向量後的trainDa以及testDa的大小

拉成向量後的trainDa以及testDa的大小

對比一下,train_data和test_data在矩陣化之前的大小:

train_data和test_data在矩陣化之前的大小

可見,CDBN作為特徵提取器,將4096維特徵對映到了9873維特徵,提高了Softmax的分類能力!

softmaxExercise.m中有這樣一段註釋:

softmaxExercise.m中的註釋

因此在呼叫softmaxExercise方法前,要做以下4個工作:

  • 修改softmaxExercise.m第22行的numClasses,如本文改為40
  • 修改softmaxExercise.m第96行的maxIter,本文取1000

PS:個人覺得softmaxExercise方法應該增加兩個入口引數,即numClasses和maxIter,如此才能更好體現封裝的思想。

  • softmaxCost.m中定義需要的損失函式,只需要改第90行

cost = -(1. / numCases) * sum(sum(groundTruth .* log(p))) + (lambda / 2.) * sum(sum(theta.^2));

這條語句即可,原檔案使用的是交叉熵代價函式。

  • 有必要的話可以修改 softmaxPredict.m中內容,個人覺得完全沒必要,保留即可。

最後呼叫softmaxExercise方法

softmaxExercise(trainDa,trainLa,testDa,testLa); toc;

05

完整程式碼

FaceRecognitionDemo.m

clear;
%load data
dataFortrain=load('ORL_64x64StTrainFile1.txt');
train_data=dataFortrain(:,1:end-1)';
train_data=reshape(train_data,[64,64,1,360]);
trainL=dataFortrain(:,end);
dataFortest=load('ORL_64x64StTestFile1.txt');
test_data=dataFortest(:,1:end-1)';
test_data=reshape(test_data,[64,64,1,40]);
testL=dataFortest(:,end);
%INITIALIZE THE PARAMETERS OF THE NETWORK 
%first layer setting
layer{1} = default_layer2D();
layer{1}.inputdata=train_data;
layer{1}.n_map_v=1;
layer{1}.n_map_h=9;
layer{1}.s_filter=[7 7];
layer{1}.stride=[1 1];
layer{1}.s_pool=[2 2];
layer{1}.batchsize=90;
layer{1}.n_epoch=1;
%second layer setting
layer{2} = default_layer2D();
layer{2}.n_map_v=9;
layer{2}.n_map_h=16;
 layer{2}.s_filter=[5 5];
layer{2}.stride=[1 1];
layer{2}.s_pool=[2 2];
layer{2}.batchsize=10;
layer{2}.n_epoch=1;
%% ----------- GO TO 2D CONVOLUTIONAL DEEP BELIEF NETWORKS ------------------     %% 
tic;

[model,layer] = cdbn2D(layer);
save('model_parameter','model','layer');

toc;

trainD  = model{1}.output;
trainD1 = model{2}.output;
%% ------------ TESTDATA FORWARD MODEL WITH THE PARAMETERS ------------------ %%
% FORWARD MODEL OF NETWORKS
H = length(layer);
layer{1}.inputdata = test_data;
fprintf('output the testdata features:>>...n');

tic;
if H >= 2

 % PREPROCESSS INPUTDATA TO BE SUITABLE FOR TRAIN 
layer{1} = preprocess_train_data2D(layer{1});
model{1}.output = crbm_forward2D_batch_mex(model{1},layer{1},layer{1}.inputdata);

for i = 2:H
    layer{i}.inputdata = model{i-1}.output;
    layer{i} = preprocess_train_data2D(layer{i});
    model{i}.output = crbm_forward2D_batch_mex(model{i},layer{i},layer{i}.inputdata);
end

else

layer{1} = preprocess_train_data2D(layer{1});
model{1}.output = crbm_forward2D_batch_mex(model{1},layer{1},layer{1}.inputdata);
end

testD  = model{1}.output;
testD1 = model{2}.output;
toc;
%% ------------------------------- Softmax ---------------------------------- %%

fprintf('train the softmax:>>...n');

tic;

% TRANSLATE THE OUTPUT TO ONE VECTOR
trainDa = [];
trainLa=trainL;
for i= 1:size(trainD,4)
a1 = [];
a2 = [];
a3 = [];
for j = 1:size(trainD,3)
    a1 = [a1;reshape(trainD(:,:,j,i),size(trainD,2)*size(trainD,1),1)];
end

for j = 1:size(trainD1,3)
    a2 = [a2;reshape(trainD1(:,:,j,i),size(trainD1,2)*size(trainD1,1),1)];
end
a3 = [a3;a1;a2];
trainDa = [trainDa,a3];
end

testDa = [];
testLa=testL;
for i= 1:size(testD,4)
b1 = [];
b2 = [];
b3 = [];
for j = 1:size(testD,3)
    b1 = [b1;reshape(testD(:,:,j,i),size(testD,2)*size(testD,1),1)];
end

for j =1:size(testD1,3)
    b2 = [b2;reshape(testD1(:,:,j,i),size(testD1,2)*size(testD1,1),1)];
end
b3 = [b3;b1;b2];
testDa = [testDa,b3];
end
softmaxExercise(trainDa,trainLa,testDa,testLa);
toc;

06

執行截圖及準確率

執行截圖1

執行截圖2

執行截圖3

97.5%的識別率,還是可以接受的,一方面是資料集好,另一方面是搭建得網路好。

讀者可以試一試調整CDBN網路的引數,比如增大epoch(本文取1),看能否獲得更高的識別率。

為了方便讀者研究,附上所有檔案。

本Demo檔案彙總下載連結,提取碼:drms(https://pan.baidu.com/s/1i53Q5hF)

以下是使用此工具箱的幾點提示:

  • 原始工具箱只在LINUX系統測試過,由於LINUX系統和WINDOWS系統的檔案分隔符不同, 因此DemoCDBN_Binary_2D.m的第83行、 cdbn2D.m的第15、24行、 setup_toolbox.m的檔案分隔符要修改。
  • 源程式存在bug,即若樣本個數不是batchsize的整數倍的話,會出錯,因此在此bug排除前,應將batchsize設定為樣本數目的因數

over,接觸機器學習時間不是很長,文章有什麼錯誤,歡迎留言指正,謝謝!