卷积深度置信网络工具箱的使用---人脸识别

引言

本文主要以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,接触机器学习时间不是很长,文章有什么错误,欢迎留言指正,谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容