c++基于Resnet(Dlib库)+opencv3的高精度人脸识别

Resnet

产生原因

介绍残差网络Resnet之前,先介绍一下卷积神经网络过程中会遇到的问题,分别有:

  1. 计算资源的消耗
  2. 模型容易过拟合
  3. 梯度消失/梯度爆炸问题的产生

问题1可以通过GPU集群来解决,对于一个企业资源并不是很大的问题;问题2的过拟合通过采集海量数据,并配合Dropout正则化等方法也可以有效避免;问题3通过Batch Normalization也可以避免。貌似我们只要无脑的增加网络的层数,我们就能从此获益,但实验数据给了我们当头一棒。事实上,随着网络层数的增加,网络发生了退化(degradation)的现象:随着网络层数的增多,训练集loss逐渐下降,然后趋于饱和,当你再增加网络深度的话,训练集loss反而会增大。注意这并不是过拟合,因为在过拟合中训练loss是一直减小的。

从信息论的角度讲,由于DPI(数据处理不等式)的存在,在前向传输的过程中,随着层数的加深,Feature Map包含的图像信息会逐层减少,而ResNet的直接映射的加入,保证了下一层的网络一定比上层包含更多的图像信息。基于这种使用直接映射来连接网络不同层直接的思想,残差网络应运而生。

理论介绍

本文以学习和应用为主,具体的公式推导不做过多的赘述,只是大概了解一下残差网络的原理即可。

残差网络是由一系列残差块组成的(图1)。一个残差块可以用表示为:

c++基于Resnet(Dlib库)+opencv3的高精度人脸识别_第1张图片

残差块分成两部分直接映射部分和残差部分。 xl 是直接映射,反应在图1中是左边的曲线;F(xl,Wl) 是残差部分,一般由两个或者三个卷积操作构成,即图1中右侧包含卷积的部分。

简单来说,就是将上一层的输出,即本层的输入直接和通过本层网络后的网络参数值做一个叠加,从而保留了上层网络的信息,网络就不会出现退化的现象,也就是说随着层数的增加,网络一定会越来越优化。

但是实际上来说,当层数增加到一定程度上也会出现退化的现象,具体原因暂时还不理解。

详细网络推导参考:详解残差网络 - 知乎

前期准备

框架思路

整个过程大致分为四步,分别是:人脸检测——人脸截取——人脸矫正——人脸识别,用ResNet实现对一个人脸的编码,生成一个128维向量,然后通过计算两个向量之间的距离来实现识别。

环境搭建

需要用到的有:

  • opencv(最好是用2.x.x版本,可以直接用参考文章的代码,但是我的是3.4.6导致原来的一些函数不能正常使用,出现了很多麻烦。)
  • dlib深度学习库   安装教程链接:使用vs2017 + cmake 编译 dlib库的步骤以及遇到的问题_sihaiyinan的博客-CSDN博客
  • VS2017
  • Cmake

这里还得重点说明一下,由于opencv3以后的版本缺少contrib函数,所以不能用里面的Directory函数,这个函数本来是来得到文件夹目录中的所有特定类型的文件名,所以导致原来那个代码不能使用。为此,我去网上找了很多contrib库的安装教程,这个过程遇到的问题越来越多,最后成功了,但是因为文件地址的问题发现还是不能正常使用,于是找到了opencv3以后版本中的glob函数写了一个类,最后成功了。总之简单来说,如果要用参考里面的代码,就直接装opencv2,要是用我的代码,就不用管3以上的版本。

此外,还必须在dlib模型库中,下载链接为:http://dlib.net/files/   ,下载两个文件并解压,分别是为:

shape_predictor_68_face_landmarks.dat

dlib_face_recognition_resnet_model_v1.dat

第一个是人脸特征点识别模型,可以识别68个特征点,第二个基于Resnet的人脸识别模型。

代码实现

代码基本都给出了非常详细的说明,我是在图片进行人脸识别之后修改的,然后调用摄像头进行动态是识别,FPS不高,但是整体效果还不错,建议大家先从图片识别开始入手,读取路径那些代码基本没动,只是注释掉了,最好还是参考一下这篇文章里面的代码。

文章链接:ResNet:用dlib实现人脸识别—C++,包括人脸检测和人脸矫正(附代码)_意疏的博客-CSDN博客_dlib resnet

完整代码(输入图片识别)

/*-------------------------------------------------------------------------------------
这是一个例子,说明使用DLIB C++的深度学习工具库。在这里,我们将展示如何进行人脸识别。此示例使用预先培训过的
DLib_人脸识别_resnet_model_v1模型,可从DLIB网站下载。该模型在标准LFW面上的精度为99.38%。识别基准,与其他
最先进的面部识别方法相比截至2017年2月的认可。
on_images_ex.cpp示例。
------------------------------------------------------------------------------------*/
#include 
#include 
#include
#include

#include 
#include 
#include 
#include 
#include 
#include 

#include "opencv\cv.h"
#include "opencv2\core.hpp"
#include "opencv2\highgui\highgui.hpp"
#include "opencv2\imgproc\imgproc.hpp"
#include "opencv2\videoio.hpp"
//#include "contrib.hpp"


#include 
#include 
#include 
#include 
#include 
#include 
#include 


using namespace dlib;
using namespace std;
using namespace cv;

/*-----------------------------------------------------------------------------------*/

//下一位代码定义Resnet网络。基本上是复制的

//并从dnn_imagenet_ex.cpp示例粘贴,但我们替换了损失

//使用损耗度量进行分层,使网络变得更小。去读导论吧

//dlib dnn示例了解所有这些内容的含义。

//另外,dnn_metric_learning_on_images_ex.cpp示例显示了如何训练此网络。

//本例使用的dlib_face_recognition_resnet_model_v1模型是使用

//基本上是dnn_metric_learning_on_images_ex.cpp中显示的代码,除了

//小批量大(35x15而不是5x5),迭代没有进展

//设置为10000,训练数据集由大约300万个图像组成,而不是

//55。此外,输入层被锁定为150大小的图像。
/*------------------------------------------------------------------------------------*/


/*------------------------------------------------------------------------------------*/
template