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=(22)):  
  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=[510], 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.mzph.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…

数据结构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…

树莓派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…

数据结构05数组和广义表

第五章 数组 和 广义表 数组和广义表可以看成是线性表在下述含义上的扩展:表中的数据元素本身也是一个数据结构。 5.1 数组的定义 n维数组中每个元素都受着n个关系的约束,每个元素都有一个直接后继元素。 可以把二维数组看成是这样一个定长线性表&…

数据结构06树和二叉树

第六章 树和二叉树 6.1 树的定义和基本术语 树 Tree 是n个结点的有限集。 任意一棵非空树中: (1)有且仅有一个特定的称为根(root)的结点; (2)当n>1时,其余结点可…

CountDownLatch,CyclicBarrier和Semaphore

在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch,CyclicBarrier和Semaphore,今天我们就来学习一下这三个辅助类的用法。以下是本文目录大纲:一.CountDownLatch用法二.CyclicBarrie…

数据结构07排序

第十章内部排序 10.1 概述 排序就是把一组数据按关键字的大小有规律地排列。经过排序的数据更易于查找。 排序前KiKj,且Ki在前: 排序方法是稳定的,若排序后Ki在前; 排序方法是不稳定的,如排序后Kj在前。 分类: 内…

数据结构08查找

第九章 查找 另一种在实际应用中大量使用的数据结构--查找表。 所谓查找,即为在一个含有众多的数据元素的查找表中找出某个“特定的”数据元素。 查找表 search table 是由同一类型的数据元素构成的集合。集合中的数据元素之间存在着完全松散的关系,故…

下载Centos7 64位镜像

下载Centos7 64位镜像 1.打开Centos官网 打开Centos官方网站地址:https://www.centos.org/,点击Get CentOS Now 2.点击Minimal ISO镜像 Minimal ISO镜像,与DVD ISO镜像的差别有很多,这里只说两点 1.Minimal ISO类似于Windows的纯净…

Scala01入门

第1章 可伸展的语言 Scala应用范围广,从编写脚本,到建立大型系统。 运行在标准Java平台上,与Java库无缝交互。 更能发挥力量的地方:建立大型系统或可重用控件的架构。 将面向对象和函数式编程加入到静态类型语言。 在Scala中&a…

Java网络01基本网络概念

协议 Protocol:明确规则 (1)地址格式; (2)数据如何分包; ... TCP/IP四层模型: 应用层 HTTP SMTP POP IMAP 传输层 TCP UDP 网际层 IP 主机网络层 host to host layer 数模、…

Java网络02基本Web概念

URI Uniform Resource Identifier 同一资源标识符 以特定语法标识一个资源的字符串 绝对URI:URI模式模式特有部分 scheme:scheme-specific-part scheme分为: data file本地文件系统 ftp http telnet urn 统一资源名 scheme-specific-part为&am…

解决自建ca认证后浏览器警告

前一篇讲解了基本的建立证书的过程,但是建立后总是会在浏览器那里警告: 此链接不是私密链接 --谷歌浏览器 此证书颁发机构不可信 此证书不是这个网站的 --ie浏览器 总之证书是生成成功了,但是其中的内容填写错误了&a…