【caffe】基于CNN的年龄和性别检测

转载 2017年04月16日 17:18:46

自2012年深度学习火起来后,AlexNet,vgg16,vgg19,gooGleNet,caffeNet,faster RCNN等,各种模型层出不群,颇有文艺复兴时的形态。

在各种顶会论文中,对年龄和性别的检测的论文还是比较少的。而本文将要讲解的是2015年的一篇cvpr,Age and Gender Classification using Convolutional Neural Networks。官方链接为http://www.openu.ac.il/home/hassner/projects/cnn_agegender/

 

理论基础:

其整体结构如下图所示,输入一幅图片,然后经过3个卷基层,2个全链接层,最后经过svm分类器分类,输出相应的结果。

【caffe】基于CNN的年龄和性别检测

【caffe】基于CNN的年龄和性别检测

详细结构如下图所示,其中norm层为了提高模型的泛化能力,drop层是为了防止过拟合。

其中左中右分别为第一二三个卷基层,下面的为第六七八个全连接层。

【caffe】基于CNN的年龄和性别检测

【caffe】基于CNN的年龄和性别检测

程序运行:

可以写一个简单的shell脚本来进行运行测试,touch一个.sh文件,随便起个名字,输入如下的脚本,然后命令行运行就可以输出结果。其中路径名换成自己电脑的实际路径名。

[plain] view plain copy

  1. #!/bin/bash  
  2. #directed by watersink([email protected])  
  3.   
  4. echo "Begin gender....."  
  5.   
  6. ./build/examples/cpp_classification/classification.bin \  
  7.   models/cnn_age_gender/deploy_gender.prototxt \  
  8.   models/cnn_age_gender/gender_net.caffemodel \  
  9.   data/cnn_age_gender/mean.binaryproto \  
  10.   data/cnn_age_gender/genderlabels.txt \  
  11.   data/cnn_age_gender/example_image.jpg  
  12.   
  13. echo "Begin age....."  
  14.   
  15. ./build/examples/cpp_classification/classification.bin \  
  16.   models/cnn_age_gender/deploy_age.prototxt \  
  17.   models/cnn_age_gender/age_net.caffemodel \  
  18.   data/cnn_age_gender/mean.binaryproto \  
  19.   data/cnn_age_gender/agelabels.txt \  
  20.   data/cnn_age_gender/example_image.jpg  
  21.  echo "Done.  
[plain] view plain copy
  1. #!/bin/bash  
  2. #directed by watersink([email protected])  
  3.   
  4. echo "Begin gender....."  
  5.   
  6. ./build/examples/cpp_classification/classification.bin \  
  7.   models/cnn_age_gender/deploy_gender.prototxt \  
  8.   models/cnn_age_gender/gender_net.caffemodel \  
  9.   data/cnn_age_gender/mean.binaryproto \  
  10.   data/cnn_age_gender/genderlabels.txt \  
  11.   data/cnn_age_gender/example_image.jpg  
  12.   
  13. echo "Begin age....."  
  14.   
  15. ./build/examples/cpp_classification/classification.bin \  
  16.   models/cnn_age_gender/deploy_age.prototxt \  
  17.   models/cnn_age_gender/age_net.caffemodel \  
  18.   data/cnn_age_gender/mean.binaryproto \  
  19.   data/cnn_age_gender/agelabels.txt \  
  20.   data/cnn_age_gender/example_image.jpg  
  21.  echo "Done.  

classification.bin为caffe自带的二进制文件,用于实现分类。

deploy_gender.prototxt,deploy_age.prototxt分别为性别,年龄部署或者测试时候的模型文件

gender_net.caffemodel,age_net.caffemodel分别为性别,年龄训练好的权值文件

mean.binaryproto,为训练的样本的均值文件,减均值时使用,可以达到更好的检测效果

genderlabels.txt,agelabels.txt为性别,年龄的标签文件,具体内容如下

genderlabels.txt中的内容:

[plain] view plain copy

  1. male 1  
  2. female 0  
[plain] view plain copy
  1. male 1  
  2. female 0  

agelabels.txt中的内容:

[plain] view plain copy

  1. 0-2 0  
  2. 4-6 1  
  3. 8-13 2  
  4. 15-20 3  
  5. 25-32 4  
  6. 38-43 5  
  7. 48-53 6  
  8. 60- 7  
[plain] view plain copy
  1. 0-2 0  
  2. 4-6 1  
  3. 8-13 2  
  4. 15-20 3  
  5. 25-32 4  
  6. 38-43 5  
  7. 48-53 6  
  8. 60- 7  

example_image.jpg,为随便一张想要测试的图片

实验结果:

【caffe】基于CNN的年龄和性别检测

性别识别率:

【caffe】基于CNN的年龄和性别检测

年龄识别率:

【caffe】基于CNN的年龄和性别检测

训练自己的模型:

论文的原作者给出了自己的5-folder训练文件,首先我们对原作者的训练数据进行分析。
【caffe】基于CNN的年龄和性别检测

通过上面的数据可以看出,性别数据反面基本是1:1比例进行的。而年龄方面并没有执行大概的1:1:1:1:1:1:1:1比例,而是3:3:3:3:6:3:1:1,对于这样的训练比例,我个人认为是很不科学和合理的,所谓的mini-batch,应该是每个batch中的属性和整体的基本是一样的才可以。简单的说,任意给一张图像,这张图像中人物属于哪一个年龄段是都有可能的,也就是是平均概率的。(个人见解,如有错误,希望大家指正)

 

下面通过一个简单的c++程序来获得和作者一样的txt文件。

  1. int main()  
  2. {  
  3.       
  4.     vector<string>file_folder;  
  5.     file_folder.push_back("fold_0_data.txt");  
  6.     file_folder.push_back("fold_1_data.txt");  
  7.     file_folder.push_back("fold_2_data.txt");  
  8.     file_folder.push_back("fold_3_data.txt");  
  9.     file_folder.push_back("fold_4_data.txt");  
  10.   
  11.     ofstream file_stream_gender_out("gender.txt", ios::app);  
  12.     ofstream file_stream_age_out("age.txt", ios::app);  
  13.   
  14.     char buffer[500];  
  15.     char str1[50], str2[50], str3[50], str4[50], str5[50], str6[50];  
  16.     for (int i = 0; i < file_folder.size();i++)  
  17.     {  
  18.         ifstream file_stream_in(file_folder[i]);  
  19.         file_stream_in.getline(buffer, 400);//提取标题  
  20.   
  21.   
  22.         while (!file_stream_in.eof())  
  23.         {  
  24.             file_stream_in.getline(buffer,400);  
  25.             sscanf(buffer, "%s %s %s %s %s %s", &str1, &str2, &str3, &str4, &str5, &str6);  
  26.               
  27.                 string tmp1 = str3, tmp2 = str2;  
  28.                 string address = "F:\\MydataSet\\Adience\\aligned\\landmark_aligned_face." + tmp1 + "." + tmp2;  
  29.                 Mat image = imread(address);  
  30.                 if (image.data)  
  31.                 {  
  32.                     if (str4[0] == '(')  
  33.                         file_stream_age_out << "Adience/" << "landmark_aligned_face." << str3 << "." << str2 << " " << str4 << " " << str5 << endl;  
  34.                     if (strcmp(str6, "f") == 0 || strcmp(str6, "m") == 0 )  
  35.                         file_stream_gender_out << "Adience/" << "landmark_aligned_face." << str3 << "." << str2 << " " << str6<<endl;  
  36.                         else  
  37.                         {  
  38.                         if(strcmp(str5, "f") == 0 || strcmp(str5, "m") == 0)  
  39.                         file_stream_gender_out << "Adience/" << "landmark_aligned_face." << str3 << "." << str2 << " " << str5 << endl;  
  40.                         }  
  41.   
  42.                 }  
  43.   
  44.                   
  45.   
  46.         }  
  47.         file_stream_in.close();  
  48.   
  49.     }  
  50.     file_stream_gender_out.close();  
  51.     file_stream_age_out.close();  
  52.     return 0;  
  53. }  
  1. int main()  
  2. {  
  3.       
  4.     vector<string>file_folder;  
  5.     file_folder.push_back("fold_0_data.txt");  
  6.     file_folder.push_back("fold_1_data.txt");  
  7.     file_folder.push_back("fold_2_data.txt");  
  8.     file_folder.push_back("fold_3_data.txt");  
  9.     file_folder.push_back("fold_4_data.txt");  
  10.   
  11.     ofstream file_stream_gender_out("gender.txt", ios::app);  
  12.     ofstream file_stream_age_out("age.txt", ios::app);  
  13.   
  14.     char buffer[500];  
  15.     char str1[50], str2[50], str3[50], str4[50], str5[50], str6[50];  
  16.     for (int i = 0; i < file_folder.size();i++)  
  17.     {  
  18.         ifstream file_stream_in(file_folder[i]);  
  19.         file_stream_in.getline(buffer, 400);//提取标题  
  20.   
  21.   
  22.         while (!file_stream_in.eof())  
  23.         {  
  24.             file_stream_in.getline(buffer,400);  
  25.             sscanf(buffer, "%s %s %s %s %s %s", &str1, &str2, &str3, &str4, &str5, &str6);  
  26.               
  27.                 string tmp1 = str3, tmp2 = str2;  
  28.                 string address = "F:\\MydataSet\\Adience\\aligned\\landmark_aligned_face." + tmp1 + "." + tmp2;  
  29.                 Mat image = imread(address);  
  30.                 if (image.data)  
  31.                 {  
  32.                     if (str4[0] == '(')  
  33.                         file_stream_age_out << "Adience/" << "landmark_aligned_face." << str3 << "." << str2 << " " << str4 << " " << str5 << endl;  
  34.                     if (strcmp(str6, "f") == 0 || strcmp(str6, "m") == 0 )  
  35.                         file_stream_gender_out << "Adience/" << "landmark_aligned_face." << str3 << "." << str2 << " " << str6<<endl;  
  36.                         else  
  37.                         {  
  38.                         if(strcmp(str5, "f") == 0 || strcmp(str5, "m") == 0)  
  39.                         file_stream_gender_out << "Adience/" << "landmark_aligned_face." << str3 << "." << str2 << " " << str5 << endl;  
  40.                         }  
  41.   
  42.                 }  
  43.   
  44.                   
  45.   
  46.         }  
  47.         file_stream_in.close();  
  48.   
  49.     }  
  50.     file_stream_gender_out.close();  
  51.     file_stream_age_out.close();  
  52.     return 0;  
  53. }  

程序运行结束后,将会生成age.txt和gender.txt

【caffe】基于CNN的年龄和性别检测

有了这2个文件就可以进行相应的训练工作了。当然为了得到更好的效果,可以考虑以下3点建议。

(1)对txt中的数据平均分配,不要好多相同的标签在一起,如果不这样做的话,需要在输入层设置shuffle,同样可以达到相同的效果。

(2)增加一定的样本,使得年龄的每一个阶段数量都大致相同

(3)将图像缩放到256*256,并做aligement

本人处理后数据如下图所示,大小256*256,做了aligement

【caffe】基于CNN的年龄和性别检测

cafferoot/examples/imagenet/ (cafferoot为自己的caffe的根目录)下的create_imagenet.sh

make_imagenet_mean.shtrain_caffenet.sh复制到自己的工程下,分别改名为,create_age.shmake_age_mean.shtrain_age.sh再复制一份,改名为,create_gender.shmake_gender_mean.shtrain_gender.sh并对其中的参数做相应的修改。(如果这一步有疑问,请留言)

年龄的训练:

执行create_age.sh

【caffe】基于CNN的年龄和性别检测

执行,make_age_mean.sh

【caffe】基于CNN的年龄和性别检测

执行,train_age.sh,可以看到得到了68.95%的识别率

【caffe】基于CNN的年龄和性别检测

性别的训练:

执行create_gender.sh

【caffe】基于CNN的年龄和性别检测

执行,make_gender_mean.sh

【caffe】基于CNN的年龄和性别检测

执行,train_gender.sh,可以看到得到了95.75%的识别率

【caffe】基于CNN的年龄和性别检测

本人实际在ubuntu14.04+corei7cpu+TitanX上跑的cpu进行年龄或者性别判断的时间为150ms左右,gpu的时间为2-3ms左右。

在windows7+XeonE3上测试,cpu+debug为120ms左右,cpu+release为30ms左右。

 

欢迎大家指证与交流……