在深度学习出现后,人脸识别技术才真正有了可用性。这是因为之前的机器学习技术中,难以从图片中取出合适的特征值。轮廓?颜色?眼睛?如此多的面孔,且随着年纪、光线、拍摄角度、气色、表情、化妆、佩饰挂件等等的不同,同一个人的面孔照片在照片象素层面上差别很大,凭借专家们的经验与试错难以取出准确率较高的特征值,自然也没法对这些特征值进一步分类。深度学习的最大优势在于由训练算法自行调整参数权重,构造出一个准确率较高的f(x)函数,给定一张照片则可以获取到特征值,进而再归类。本文中笔者试图用通俗的语言探讨人脸识别技术,首先概述人脸识别技术,接着探讨深度学习有效的原因以及梯度下降为什么可以训练出合适的权重参数,最后描述基于CNN卷积神经网络的人脸识别。
一、人脸识别技术概述
人脸识别技术大致由人脸检测和人脸识别两个环节组成。
之所以要有人脸检测,不光是为了检测出照片上是否有人脸,更重要的是把照片中人脸无关的部分删掉,否则整张照片的像素都传给f(x)识别函数肯定就不可用了。人脸检测不一定会使用深度学习技术,因为这里的技术要求相对低一些,只需要知道有没有人脸以及人脸在照片中的大致位置即可。一般我们考虑使用OpenCV、dlib等开源库的人脸检测功能(基于专家经验的传统特征值方法计算量少从而速度更快),也可以使用基于深度学习实现的技术如MTCNN(在神经网络较深较宽时运算量大从而慢一些)。
在人脸检测环节中,我们主要关注检测率、漏检率、误检率三个指标,其中:
检测率:存在人脸并且被检测出的图像在所有存在人脸图像中的比例;
漏检率:存在人脸但是没有检测出的图像在所有存在人脸图像中的比例;
误检率:不存在人脸但是检测出存在人脸的图像在所有不存在人脸图像中的比例。
当然,检测速度也很重要。本文不对人脸检测做进一步描述。
二、深度学习技术的原理
由清晰的人脸照转化出的像素值矩阵,应当设计出什么样的函数f(x)转化为特征值呢?这个问题的答案依赖于分类问题。即,先不谈特征值,首先如何把照片集合按人正确地分类?这里就要先谈谈机器学习。机器学习认为可以从有限的训练集样本中把算法很好地泛化。所以,我们先找到有限的训练集,设计好初始函数f(x;w),并已经量化好了训练集中x->y。如果数据x是低维的、简单的,例如只有二维,那么分类很简单,如下图所示:
上图中的二维数据x只有方形和圆形两个类别y,很好分,我们需要学习的分类函数用最简单的f(x,y)=ax+by+c就能表示出分类直线。例如f(x,y)大于0时表示圆形,小于0时表示方形。
给定随机数作为a,c,b的初始值,我们通过训练数据不断的优化参数a,b,c,把不合适的L1、L3等分类函数逐渐训练成L2,这样的L2去面对泛化的测试数据就可能获得更好的效果。然而如果有多个类别,就需要多条分类直线才能分出,如下图所示:
这其实相当于多条分类函数执行与&&、或||操作后的结果。这个时候还可能用f1>0 && f20这样的分类函数,但如果更复杂的话,例如本身的特征不明显也没有汇聚在一起,这种找特征的方式就玩不转了,如下图所示,不同的颜色表示不同的分类,此时的训练数据完全是非线性可分的状态:
从上图示例可见,虽然输入照片是猫,但得分上属于狗的得分值437.9最高,但究竟比猫和船高多少呢?很难衡量!如果我们把得分值转化为0-100的百分比概率,这就方便度量了。这里我们可以使用sigmoid函数,如下图所示:
从上图公式及图形可知,sigmoid可以把任意实数转换为0-1之间的某个数作为概率。但sigmoid概率不具有归一性,也就是说我们需要保证输入照片在所有类别的概率之和为1,这样我们还需要对得分值按softmax方式做以下处理:
这样给定x后可以得到x在各个类别下的概率。假定三个类别的得分值分别为3、1、-3,则按照上面的公式运算后可得概率分别为[0.88、0.12、0],计算过程如下图所示:
然而实际上x对应的概率其实是第一类,比如[1,0,0],现在拿到的概率(或者可称为似然)是[0.88、0.12、0]。那么它们之间究竟有多大的差距呢?这个差距就是损失值loss。如何获取到损失值呢?在softmax里我们用互熵损失函数计算量最小(方便求导),如下所示:
其中i就是正确的分类,例如上面的例子中其loss值就是-ln0.88。这样我们有了损失函数f(x)后,怎么调整x才能够使得函数的loss值最小呢?这涉及到微分导数。
可以形象的看出,当斜率的值为正数时,把x向左移动变小一些,f(x)的值就会小一些;当斜率的值为负数时,把x向右移动变大一些,f(x)的值也会小一些,如下图所示:
这样,斜率为0时我们其实就得到了函数f在该点可以得到最小值。那么,把x向左或者向右移一点,到底移多少呢?如果移多了,可能移过了,如果移得很少,则可能要移很久才能找到最小点。还有一个问题,如果f(x)操作函数有多个局部最小点、全局最小点时,如果x移的非常小,则可能导致通过导数只能找到某个并不足够小的局部最小点。如下图所示:
以上我们是以一维数据来看梯度下降,但我们的照片是多维数据,此时如何求导数?又如何梯度下降呢?此时我们需要用到偏导数的概念。其实它与导数很相似,因为x是多维向量,那么我们假定计算Xi的导数时,x上的其他数值不变,这就是Xi的偏导数。此时应用梯度下降法就如下图所示,θ是二维的,我们分别求θ0和θ1的导数,就可以同时从θ0和θ1两个方向移动相应的步长,寻找最低点,如下图所示:
四、基于CNN卷积神经网络进行人脸识别
CNN认为可以只对整张图片的一个矩形窗口做全连接运算(可称为卷积核),滑动这个窗口以相同的权重参数w遍历整张图片后,可以得到下一层的输入,如下图所示:
CNN中认为同一层中的权重参数可以共享,因为同一张图片的各个不同区域具有一定的相似性。这样原本的全连接计算量过大问题就解决了,如下图所示:
结合着之前的函数前向运算与矩阵,我们以一个动态图片直观的看一下前向运算过程:
这里卷积核大小与移动的步长stride、输出深度决定了下一层网络的大小。同时,核大小与stride步长在导致上一层矩阵不够大时,需要用padding来补0(如上图灰色的0)。以上就叫做卷积运算,这样的一层神经元称为卷积层。上图中W0和W1表示深度为2。
CNN卷积网络通常在每一层卷积层后加一个激励层,激励层就是一个函数,它把卷积层输出的数值以非线性的方式转换为另一个值,在保持大小关系的同时约束住值范围,使得整个网络能够训练下去。在人脸识别中,通常都使用Relu函数作为激励层,Relu函数就是max(0,x),如下所示:
可见 Relu的计算量其实非常小!
CNN中还有一个池化层,当某一层输出的数据量过大时,通过池化层可以对数据降维,在保持住特征的情况下减少数据量,例如下面的4*4矩阵通过取最大值降维到2*2矩阵:
上图中通过对每个颜色块筛选出最大数字进行池化,以减小计算数据量。
通常网络的最后一层为全连接层,这样一般的CNN网络结构如下所示:
CONV就是卷积层,每个CONV后会携带RELU层。这只是一个示意图,实际的网络要复杂许多。目前开源的Google FaceNet是采用resnet v1网络进行人脸识别的,关于resnet网络请参考论文https://arxiv.org/abs/1602.07261,其完整的网络较为复杂,这里不再列出,也可以查看基于TensorFlow实现的Python代码https://github.com/davidsandberg/facenet/blob/master/src/models/inception_resnet_v1.py,注意slim.conv2d含有Relu激励层。
以上只是通用的CNN网络,由于人脸识别应用中不是直接分类,而是有一个注册阶段,需要把照片的特征值取出来。如果直接拿softmax分类前的数据作为特征值效果很不好,例如下图是直接将全连接层的输出转化为二维向量,在二维平面上通过颜色表示分类的可视化表示:
可见效果并不好,中间的样本距离太近了。通过centor loss方法处理后,可以把特征值间的距离扩大,如下图所示:
这样取出的特征值效果就会好很多。
公众号推荐:
公众号:VOA英语每日一听
领取专属 10元无门槛券
私享最新 技术干货