用深度学习进行人脸识别
有趣的机器学习 前三章已更新
你有没有发现Facebook研发出了一种能够在你的照片中识别出你朋友的神之魔力? 之前,Facebook让你在照片里点击你的朋友,并输入他们的名字来标注出你的朋友。现在,只要你上传了一张照片,Facebook就魔力般的为你标注出你的每一个朋友:
Facebook基于你之前的标注,自动标注出你照片中的人。我不确定这到底是有帮助性的还是非常阴险恐怖的。
这种技术被称为人脸识别。你的朋友被标记了几次之后,Facebook的算法就能够识别你朋友的脸。 这是一项非常惊人的黑科技——Facebook的人脸识别准确率达到了98%,几乎与人类做的一样好!让我们来了解一下现代人脸识别是如何工作的! 但是,识别你的朋友这太容易了。 我们可以最大化扩展这项技术,来解决一个更具挑战性的问题——区分Will Ferrell(著名演员)和Chad Smith(著名摇滚音乐家)!
一个是Will Ferrell,另一个是Chad Smith。我保证他们不是同一个人!
如何用机器学习解决复杂问题?
到目前为止,在前三章,我们已经使用机器学习来解决了,一些只用一个步骤就可以解决的孤立问题——第一章:估计房子的价格、第二章:基于现有数据生成新数据(译者注:根据已有的超级马里奥的关卡生成新的关卡)、以及第三章:判别图像当中是否包含某个物品。 所有这些问题都可以通过下列步骤解决:选择一个机器学习算法,输入数据,并获得结果。
但是,人脸识别是由一系列的几个相关问题组成的:
首先,找到一张图片中的所有人脸
第二,对于每一张脸来说,无论光线明暗或面朝别处,它依旧能够识别出是同一个人的脸。
第三,能够在每一张脸上找出可用于与他人区分的独特之处,比如说眼睛有多大,脸有多长等等。
最后,将这张脸的特点与已知的所有人脸进行比较,以确定这个人的姓名。
作为人类,你的大脑总是在一瞬间自动做出了这些判断。实际上,人类在识别人脸这方面做得太好了,以至于他们会在日常物品中同样去“找脸”:
计算机不能进行这种高级普遍化(generalization)(至少现在还不行…),所以我们必须分别教给他们,这个过程中的每一步是怎么做到的。
我们需要构建一个流水线(pipeline),一个单独完成每个步骤并把结果发送给下一个步骤的流水线。换句话说,我们会将好几个机器学习算法连接到一起:
一个基础的探测人脸的流水线是怎样工作的
人脸识别——分步讲解
让我们一步一步地解决这个问题。 对于每个步骤,我们将学习一个不同的机器学习算法。 我并不会完全解释每一个的算法,否则这篇文章就变成了一本教科书。但你会学到每个步骤的精髓,以及如何在Python中使用OpenFace和dlib来构建一个你自己的面部识别系统。
第一步:找出所有的面孔
我们流水线的第一步是面部检测。显然,在我们区分人脸之前,我们必须要照片中找到他们才行!
如果你在过去10年里使用过相机,你可能已经见过正在运行中的面部检测功能:
面部检测是相机很好的一个功能。 当相机可以自动拾取人脸时,它可以确保相机在拍摄时对焦到所有人脸。 不过,我们使用它另有其因——我们需要找到想要传递到流水线下一步的图像区域。
2000年初的时候, 当Paul Viola和Michael Jones 发明了一种能够快速在廉价相机上运行的一种脸部检测方法之后(译者注:这种算法也可以用来训练检测其他的物品,但是最经常还是被用于人脸的检测,其英文名称为Viola–Jones object detection framework),面部检测在成为了主流。然而现在,更可靠的解决方案出现了。 我们将使用2005年发明的一种称为“方向梯度直方图(Histogram of Oriented Gradients)”的方法,或简称HOG。
要在一张图片中找到脸,我们首先将图像转换为黑白,因为我们并不需要颜色数据来找到脸:
然后,我们将查看图片中的每一个像素。 对于单个像素,我们要查看直接围绕着它的像素:
我们的目标是找出并比较当前像素与直接围绕它的像素的深度。 然后我们要画一个箭头来代表图像变暗的方向:
看这个像素和它周围的像素,图像向右上方变得越来越暗。
如果你对图片中的每一个像素重复这个过程,最终每个像素会被一个箭头取代。这些箭头被称为梯度gradients,它们能显示出图像上从明亮到黑暗的流动过程:
这可能看起来非常随机,但其实我们有非常好的理由用梯度来代替像素。如果我们直接分析像素,同一个人明暗不同的两张照片将具有完全不同的像素值。但是如果只考虑亮度变化方向direction**的话,明暗图像将会有同样的结果。这使得问题变得更容易解决!
但是保存每个像素的梯度太过细节化了,我们最终很有可能“一叶障目不见泰山missing the forest for the trees”。如果我们能从更高的角度上观察基本的明暗流动,我们就可以看出图像的基本规律,这会比之前更好。
为了做到这一点,我们将图像分割成一些16x16像素的小方块。在每个小方块中,我们将计算出每个主方向上有多少个梯度(有多少指向上,指向右上,指向右等)。然后我们将用指向性最强那个方向的箭头来代替原来的那个小方块。
最终的结果是,我们把原始图像变成了一个非常简单的表达形式,这种表达形式可以用一种简单的方式来捕获面部的基本结构:
原始图像被表示成了HOG形式,以捕获图像的主要特征,无论图像明暗度如何。
为了在这个HOG图像中找到脸部,我们要所需要做的,就是找到我们的图像中,与已知的一些HOG样式中,看起来最相似的部分。这些HOG样式都是从其他面部训练数据中提取出来的:
使用这种技术,我们现在可以轻松地在任何图片中找到脸部:
如果你想使用Python和dlib尝试这一步,这些代码显示了如何生成和查看HOG图像的表示。
第二步:脸部的不同姿势
哇,我们把图片中的脸部孤立出来了。 但现在,我们要处理的问题就是,对于电脑来说,面朝不同方向的同一张脸,是不同的东西:
人类可以很轻松地识别出到两个图片都是Will Ferrell,但电脑会认为这两张图片是两个完全不同的人。
为了解决这一点,我们将试图扭曲每个图片,使得眼睛和嘴唇总是在图像中的样本位置(Sample Place)。 这将使我们在接下来的步骤中,更容易比较脸部之间的不同。
为此,我们将使用一种称为**脸部标志点估计(Face Landmark Estimation)**的算法。 很多方法都可以做到这一点,但这次我们会使用由Vahid Kazemi和Josephine Sullivan在2014年发明的方法。
这一算法的基本思想是,我们找到人脸上普遍存在的68个特定点(称为Landmarks)——下巴的顶部,每只眼睛的外部轮廓,每条眉毛的内部轮廓等。接下来我们训练一个机器学习算法,能够在任何脸部找到这68个特定点:
我们将在每一张脸上定位的68个Landmarks。这张照片是由在OpenFace工作的CMU的Brandon Amos创造的。
这是在测试图片上定位68个标志点的结果:
友情提示:你也可以使用这一技术来实现自己的Snapchat实时3D脸部过滤器!
现在,我们知道了眼睛和嘴巴在哪儿,我们将图像进行旋转,缩放和切变,使得眼睛和嘴巴尽可能靠近中心。我们不会做任何花哨的三维扭曲,因为这会让图像失真。我们只会使用那些能够保持图片相对平行的基本图像变换,例如旋转和缩放,(称为仿射变换):
现在无论脸部怎样扭曲变形,我们都能将眼睛和嘴巴向中间挪动到大致相同的位置。这将使我们的下一步更加准确。
如果你想自己使用Python和dlib来尝试完成这一步的话,这里有一些代码帮你寻找脸部标志点和图像变形。
步骤3:给脸部编码
现在我们要面临最核心的问题了——如何区分脸部。这才是事情变得非常有趣的地方!
最简单的人脸识别方法,就是直接把我们在步骤2中发现的未知人脸,与我们已经标注了人脸图片作比较。当我们发现未知的面孔与一个以前标注过的面孔看起来及其相似的时候,它必须是同一个人。这似乎是一个很好的主意,对吗?
实际上这种方法有一个巨大的问题。像Facebook这种拥有数十亿用户和数万亿张照片的网站,是不可能去循环比较每张先前标记的脸的,这浪费的时间太长了。他们需要在毫秒内识别人脸,而不是几个小时。
我们需要的是一种从每张人脸上都可以提取一些基本特性的方法。然后,我们可以用同样的方式测量未知的面孔,并找到与已知的脸最相近的测量。例如,我们可以测量每个耳朵的大小,眼睛之间的间距,鼻子的长度等。如果你曾经看过像CSI这样的犯罪类型的电视剧,那么你就知道我在说什么了。
测量人脸的最可靠的方法
好的,那么我们应该测量面部的哪些数值,来建立我们的已知脸部数据库呢?耳朵的大小?鼻子的长度?眼睛的颜色?还有什么?
事实证明,对于我们人类来说一些显而易见的测量值(比如眼睛颜色),并没有对计算机产生太大的影响。研究人员发现,最准确的方法是让计算机自己找出测量值并自己收集。深度学习比人类更好地了解脸部的哪些部分是重要的测量值。
所以,解决方案是训练一个深度卷积神经网络(就像我们在第3章做的那样)。但是,并不是像上次那样训练我们的网络来识别图片对象,我们将训练它为脸部生成128个测量值。
每次训练要观察3个不同的脸部图像:
1.加载一张已知的人的面部训练图像
2.加载同一个人的另一张照片
3.加载另外一个人的照片
然后,算法查看它自己为这三个图片生成的测量值。再然后,它稍微调整神经网络,以确保它为第1张和第2章生成的测量值接近,而第2张和第3张生成的测量值略有不同。
在为几千个不同的人的数百万图像重复该步骤数百万次之后,神经网络学习了如何可靠地为每个人生成128个测量值。同一个人的任何十张不同的照片应该给出大致相同的测量值。
机器学习专业人士把每个面孔上的128个测量值称为“嵌入(Embedding)”。将复杂的原始数据(如图片)缩减为可由计算机生成的一个数列的方法在机器学习(特别是语言翻译)中出现了很多次。我们正在使用的这种方法是由Google的研究人员在2015年发明的,但其实这是许多类似的方法之一。
给我们的脸部图像编码
这个通过训练卷积神经网络来输出脸部嵌入的过程,需要大量的数据和计算机应用。即使使用昂贵的Nvidia Telsa显卡,它也需要大约24小时的连续训练,才能获得良好的准确性。
但一旦网络训练完成,它可以生成任何面孔的测量值,即使它从来没有见过这些面孔!所以这种训练只需一次即可。幸运的是,OpenFace上面的大神已经做完了这些,并且他们发布了几个训练过可以直接使用的网络,。谢谢Brandon Amos和他的团队!
所以我们需要做的,就是通过他们的预训练网络来处理我们的脸部图像,以获得128个测量值。这是我们测试图像的一些测量值:
那么,这128个数字到底测量了脸部的哪些部分?我们当然不知道,但是这对我们并不重要。我们关心的是,当看到同一个人的两张不同的图片时,我们的网络需要能得到几乎相同的数值。
如果你想自己尝试这个步骤,OpenFace提供了一个lua脚本,它可以生成一个文件夹中所有图像的嵌入,并将它们写入csv文件。点此查看如何运行。
第4步:从编码中找出人的名字
最后这一步实际上是整个过程中最简单的一步。我们要做的就是找到数据库中,与我们的测试图像的测量值最接近的那个人。
你可以通过任何基本的机器学习分类算法来达成这一目标。我们并不需要太花哨的深度学习技巧。我们将使用一个简单的线性SVM分类器,但实际上还有很多其他的分类算法可以使用。
我们需要做的是训练一个分类器,它可以从一个新的测试图像中获取测量结果,并找出最匹配的是哪个人。分类器运行一次只需要几毫秒,分类器的结果就是人的名字!
所以让我们试一下我们的系统。首先,我使用Will Ferrell, Chad Smith 和 Jimmy Falon每人20张照片的嵌入来训练分类器:
嗯…就是这些训练数据!
接下来,我在YouTube著名的Will Ferrell和Chad Smith的视频的每一帧上运行这个分类器:
它真的有效!它在不同的姿势的脸部依然有效- 甚至是侧脸!
你自己做一遍
让我们回顾一下我们的步骤:
1.使用HOG算法给图片编码,以创建图片的简化版本。使用这个简化的图像,找到图像中看起来最像通用HOG面部编码的部分。
2.通过找到脸上的主要标志点,找出脸部的姿态。一旦我们找到这些标志点,就利用它们把图像扭曲,使眼睛和嘴巴居中。
3. 把上一步得到的面部图像放入到神经网络中,神经网络知道如何找到128个特征点测量值。保存这128个测量值。
4.看看我们过去测量过得的所有脸部,找出哪个人的测量值和我们的脸部测量值最接近。这就是你要找的匹配的人!
现在你知道这一切都是如何运行的了,这里是如何使用OpenFace在你自己的电脑上运行整个人脸识别系统的说明.
可2016-12-27 19:07:32
文章里提到“Facebook的人脸识别准确率达到了98%,几乎与人类做的一样好!”,其实人类的面部识别准确率还没到98%呢,大概在85%-90%之间,所以说机器已经超越人类了
可2016-12-27 19:19:59
今天的留言难道都被我包场了吗?
可2016-12-27 19:18:40
还有文章里那128个数值作者说不知道,其实就是特定标志点之间的距离的比值,如内间眼角比外间眼角,眉间距比额唇中距等等
可2016-12-27 19:12:53
两年前看计算机颅面复原的资料时里面的面部标志点只有22个,现在居然都68个标志点了,技术越来越精准了
维崩2017-07-19 08:39:02
第一章应该是向我奶奶解释机器学习吧
作者
是的
Yutong Wu2017-01-09 06:39:02
所有超链接的代码都点击不了啊好心塞
作者
知乎上也有的,那个可以的