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

引言

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

卷積深度置信網絡理論知識

參考以下學習資料

CDBN工具箱簡介

據筆者了解,目前,比較流行的深度學習框架,如TensorFlow、DeepLearning4j等不支持CDBN。GitHub上有基于Matlab的CDBN工具箱:CDBN工具箱下載鏈接
下面簡要介紹該工具箱。
從GitHub上下載的壓縮包解壓后再打開,文件目錄如下:

CDBN工具箱的文件目錄

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

toolbox下的三個lib

需要注意的是,由于這個工具箱不是官方版的,因此可能存在某些bug,后面會涉及到筆者使用工具箱過程中的一些經驗。

神經網絡結構

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

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

  • 第一個CRBM:

第一個CRBM參數
  • 第二個CRBM:
第二個CRBM參數
  • Softmax層
    神經元個數40個,最大迭代次數maxIter=1000,代價函數為交叉熵代價函數(Cross-Entropy Error)

  • 其他參數
    其他諸如學習速率等的參數使用CDBN-master\toolbox\CDBNLIB\default_layer2D.m中的默認值。

編程

以下講解編程步驟。

  • 步驟一:安裝工具箱
    只需運行setup_toolbox.m即可。
    安裝工具箱其實只是把用到的一些函數添加到matlab的搜索路徑,因此你完全可以把工具箱內所有的文件都復制到你當前的路徑下,不過肯定麻煩啦!

  • 步驟二:加載和矩陣化數據

%load data
dataFortrain=load('ORL_64x64\StTrainFile1.txt');%注意修改路徑
train_data=dataFortrain(:,1:end-1)';%訓練樣本
train_data=reshape(train_data,[64,64,1,360]);%矩陣化訓練樣本
trainL=dataFortrain(:,end);%訓練樣本標簽
dataFortest=load('ORL_64x64\StTestFile1.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(樣本數)。倒數第二行同理。

  • 步驟三:定義層參數
    工具箱把一層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()前面,則會出現“使用未定義字段”的錯誤。
補充:要注意根據自己使用的數據集的情況設定層的輸入類型,對[0,1]之間的數據集,應該使用二值神經網絡,設定 layer{i}.type_input = 'Binary'(程序默認);其他數據集,應該設為layer{i}.type_input = 'Gaussian';至于二者的區別,自行百度,這里不展開了。

  • 步驟四:訓練CDBN網絡
    這個過程是無監督學習,只需調用cdbn2D方法即可。

在調用cdbn2D方法之前,CDBN-master\toolbox\CDBNLIB\mex中的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

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

卷積神經網絡圖示
  • 步驟五:將測試樣本輸入訓練好的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的大小比較
  • 步驟六:訓練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;

完整代碼

FaceRecognitionDemo.m

clear;
%load data
dataFortrain=load('ORL_64x64\StTrainFile1.txt');
train_data=dataFortrain(:,1:end-1)';
train_data=reshape(train_data,[64,64,1,360]);
trainL=dataFortrain(:,end);
dataFortest=load('ORL_64x64\StTestFile1.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;

運行截圖及準確率

運行截圖1
運行截圖2
運行截圖3

97.5%的識別率,還是可以接受的,一方面是數據集好,另一方面是搭建得網絡好。
讀者可以試一試調整CDBN網絡的參數,比如增大epoch(本文取1),看能否獲得更高的識別率。
為了方便讀者研究,附上所有文件。

本Demo文件匯總下載鏈接(原鏈接失效,此為新版連接),提取碼:7f6i

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

  • 原始工具箱只在LINUX系統測試過,由于LINUX系統和WINDOWS系統的文件分隔符不同,
    因此DemoCDBN_Binary_2D.m的第83行、
    cdbn2D.m的第15、24行、 setup_toolbox.m的文件分隔符要修改。
  • 源程序存在bug,即若樣本個數不是batchsize的整數倍的話,會出錯,因此在此bug排除前,應將batchsize設置為樣本數目的因數
  • 類別標簽不要用負數或0,比如進行二分類,標簽不要設為-1和1,可以設為1和2,這是因為softmaxCost.m文件中的第18行建立稀疏矩陣時會以標簽作為矩陣的索引,如果設為0或負數,肯定會報錯:矩陣索引必須為正數

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容