深度学习之人脸识别

Hits: 157

本文介绍Coursera-Deep Learning第四课卷积神经网络最后一节内容:人脸识别。

基础概念

  • 人脸检测(Face Detection)
    人脸检测(Face Detection)指的是给定一张图片,判断其是否是一张人脸。它仅仅具备识别功能,并不能进行身份验证。

  • 人脸识别(Face Recognition)
    所谓的人脸识别(Face Recognition),指的是给定一个人脸图像,从一个包含k个人脸信息的数据库中,查找出这张脸是否属于其中某个人,这就是人脸识别,它在人脸检测的基础上增加了身份验证功能。

特征脸算法(Eigenfaces)

在深度学习起飞之前,基于纯粹的图像处理的人脸识别方法已经存在了,第一个有效的人脸识别方法叫做特征脸方法(Eigenfaces)。该方法首先由Sirovich and Kirby (1987)提出,并由Matthew Turk和Alex Pentland用于人脸分类。它大致的原理是先对图像进行预处理,比如做直方图均值化(Histogram equalization);再通过PCA对图像进行降维,然后计算出数据库中K个人脸图像的平均脸和特征脸。于是,任意一张人脸图像都可以被认为是这些特征脸和平均脸的线性组合。当我们要识别一张新的人脸图像时,只需要按照这种方法计算出它的值,然后再从数据库中寻找与其最接近(比如欧氏距离)的即可。
那么在深度学习时代,我们又是如何做的呢?

深度学习方法

有一个思路是这样的:把人脸识别当成一个多任务分类问题,即把每个员工当成是一个类别。比如现在数据库里有K个员工,我们训练一个神经网络,输入一张新图片,通过softmax函数计算出它属于哪个员工。这就是所谓的One-shot Learning。这个方法很明显是不可行的,因为每个人能提供的图片数据是很少的,最多一两张,因此是无法训练的。那么该怎么办呢?
很简单,另一个很简单的思路是,我们训练出一个神经网络模型,这个模型的作用不是识别,而是计算两张图片的相似度。这样训练样本数量不足的问题就解决了,因为我们有足够多的相似的人脸图片去训练,只要两张脸是相似的就能作为样本,而不用管它是谁的脸。

Siamese Network

那么基于上面的思路,我们可以构建出如下所示的神经网络:
Imgur
上面这个图有两个神经网络,其实在代码层面只有一个神经网络,这里只是为了方便说明而已,他们的参数是一模一样的。Siamese Network的思路是:对两张不同的图片通过同一个神经网络进行编码(autoencoder),如果这个编码可靠的话,我们就可以使用编码来代替原始图片了。那么比较两个图片的相似度的问题就转化成比较两个编码的相似度问题了,那就很直观了。比如上图中,图像\(x^{(1)}\)和\(x^{(2)}\)经过autoencoder之后,它们的编码分别是\(f(x^{(1)})\)和\(f(x^{(2)})\)(上图中它们是一个128维的特征向量)于是,我们定义两者的相似度函数(距离函数):

$$
d(x^{(1)},x^{(2)}) = ||f(x^{(1)}) - f(x^{(2)})||^2
$$

那么我们学习的目标就是:

  • 如果两张照片是同一个人,那么\(d(x^{(1)},x^{(2)})\)要尽可能的小;
  • 如果两张照片不是同一个人,那么\(d(x^{(1)},x^{(2)})\)要尽可能的大;

我们知道,机器学习的目标是要最优化它的目标函数,那么我们怎么把上面所说的学习目标转化为一个统一的目标函数呢?

三元损失函数(Triplet Loss)

三元损失函数的使用是基于三组样例而言的,即每一组训练样本都有三组图片,分别是:Anchor,Positive和Negative。Anchor本意是锚点,这里可以理解为基准图片;Positive表示正例,也就是和Anchor是同一个人的人像图片;Negative表示反例,也就是和Anchor不是同一个人的人像图片。那么他们两两之间的距离可以定义为:

$$
d(A,P) = ||f(A) - f(P)||^2 \\
d(A,N) = ||f(A) - f(N)||^2
$$

有了这两个定义之后,我们就可以把他们的三元损失函数如下:

$$
||f(A) - f(P)||^2 + \alpha < ||f(A) - f(N)||^2
$$
这个公式很好理解,\(d(A,P)\)肯定是小于\(d(A,N)\)的,他们之间的差异值由一个常量\(\alpha\)控制的,比如设定\(\alpha=0.2\)。进一步变形,我们得到最终的损失函数:
$$
\ell(A,P,N) = \max(||f(A) - f(P)||^2 + \alpha - ||f(A) - f(N)||^2,0)
$$

在使用三元损失函数进行训练时,对样本的选择要特别注意。如果是仅仅满足A和P是同一个人,A和N不是同一个人这个简单条件,那么约束\(d(A,P)+\alpha < d(A,N)\)是很容易被满足的,也就是说如果我们随机选取A,P,N,那么有很大概率\(d(A,P) < d(A,N)\),这样的话神经网络并不能从中学到什么。所以,我们需要构建“很难”的三元组数据集去训练。那么什么是“很难”的三元组呢?我们选取图片时,要使得正例和反例与基准图片的差异很接近(\(d(A,P) \approx d(A,N)\)),也就是说正例和反例看起来都像基准图片,但只有正例和基准图片是同一个人。
三元损失函数这个想法来自于2015年Google发表在CVPR上的一篇论文:FaceNet: A Unified Embedding for Face Recognition and Clustering,该论文指出这个方法在LFW(Labeled Faces in the Wild)数据集上取得了创纪录的99.63%的准确率,在YouTube Faces DB数据集上的准确率则是95.12%,可以说是state-of-art的人脸识别算法了。

二元分类问题

除了使用上面提及到的三元损失函数外,我们还可以把人脸识别当成是一个二元分类问题,即给定两张人脸图片,判断是否是同一个人。如果是,输出1;否则输出0。这个想法仍然采用了Siamese网络,最大的不同是它的相似度函数(输出层的激活函数)采用了sigmoid而不是Triplet。对于一组输入\(x^{(i)}\)和\(x^{(j)}\),经过autoencoder之后的编码是\(f(x^{(i)})\)和\(f(x^{(j)})\)。那么它的相似度函数如下:

$$
\hat{y} = \sigma(\sum_{k=1}^{128}w_i|f(x^{(i)})_k -f(x^{(j)})_k| + b)
$$

其中,\(\sigma\)是sigmoid函数;\(|f(x^{(i)})_k -f(x^{(j)})_k|\)是两个向量距离的绝对值;这个距离也可以换成以下的函数:

$$
\frac{(f(x^{(i)})_k -f(x^{(j)})_k)^2}{f(x^{(i)})_k + f(x^{(j)})_k}
$$
它也被称为\(\chi^2\)相似度。
这个算法由Facebook团队发表在CVPR2014上:DeepFace: Closing the Gap to Human-Level Performance in Face Verification,时间比Triplet略早一年,所以准确率自然而然也会稍微低一点,但在LFW数据集上的准确率也达到了很高的97.35%,比之前最好的方法提高了27%,非常接近人类水平了。总而言之,这也是个非常不错的想法。


发表评论

电子邮件地址不会被公开。 必填项已用*标注