3个CNN,胜过1个诸葛亮

作者简介

CW,广东深圳人,毕业于中山大学(SYSU)数据科学与计算机学院,毕业后就业于腾讯计算机系统有限公司技术工程与事业群(TEG)从事Devops工作,期间在AI LAB实习过,实操过道路交通元素与医疗病例图像分割、视频实时人脸检测与表情识别、OCR等项目。

目前也有在一些自媒体平台上参与外包项目的研发工作,项目专注于CV领域(传统图像处理与深度学习方向均有)。

前言

MTCNN(Multi-Task Cascaded Convolutional Networks)是2016年提出的,如其名,算法采用了级联CNN的结构实现了多任务学习——人脸检测 和 人脸对齐,最终能够预测出人脸框(bounding box)和关键点(landmarks)的位置。

MTCNN在 FDDB、WIDER FACE 和 AFLW 数据集上均取得了当时(2016年4月)最好的效果,而且速度也快(在当时来说),被广泛用于人脸识别流程中的前端部分。

总地来说,MTCNN的效果主要得益于以下几点:

  1. 级联的CNN架构是一个coarse-to-fine(由粗到细)的过程;

  2. 在训练过程中使用了在线困难样本挖掘(OHEM,Online Hard Example Mining)的策略;

  3. 加入了人脸对齐的联合学习(Joint Face Alignment Learning)

下面,请诸位客官进入MTCNN的世界里进行参观。

MTCNN的“宣传海报”


一网络结构

MTCNN由3个网络组成,共同构成级联的CNN架构。这3个网络依次为 P-Net、R-Net 和 O-Net,前者的输出会作为后者的输入。

P-Net(Proposal Network)

P-Net

顾名思义,这个网络主要用于输出包含的人脸候选区域。输出包含3部分:分类结果(二分类),代表是否人脸、检测到的人脸框(bbox)位置 以及 定位到的关键点(landmarks)位置。

需要注意的是,这里回归输出的bbox和landmarks并非是坐标值本身,而是归一化的offset,具体在后文会讲解,这里先埋个坑~

另外,P-Net是一个FCN(Fully Convolutonal Network),输出的是一个map,channel为2+4+10=16,2对应二分类的score,4对应bbox左上角和右下角横纵坐标的offset,而10则对应5个关键点(左、右眼、鼻、左、右嘴角)横纵坐标的offset,后续R-Net和O-Net的输出意义也与此相同。

或许你会疑惑,为何P-Net要设计成FCN呢?从上图可以看到,输入图像的size是12×12,最终输出的size变为1×1,完全没有必要嘛~其实仔细看下本文前言里展示的那幅图的最上面部分,应该就知道答案了。

R-Net(Refine Network)

R-Net

R-Net不再是FCN,最后使用全连接层,相比于P-Net,其输入size和每层的通道数也比较大,可以认为,其拟合的结果会更精确,主要作用是对P-Net的结果进行校正,消除其中的误判(FP,False-Positive)。

O-Net(Output Network)

O-Net

O-Net的结构与P-Net较为类似,只不过输入size、宽度以及深度都更大,因此可使最终输出结果更加精确。

从上述展示的结构中我们可以feel到,P-Net->R-Net->O-Net 是一个coarse-to-fine的过程。


推理过程

推理过程的pipeline依次是:对输入图像进行处理生成图像金字塔 -> 输入到P-Net -> 对P-Net的输出进行后处理 -> 输入到R-Nnet -> 对R-Net的输出进行后处理 -> 输入到O-Net -> 对O-Net的输出进行后处理得到最终预测结果。

另外,推理时P-Net和R-Net可以不必输出landmarks分量,因为此时预测的人脸框位置还不准,根据offset计算出对应的landmarks位置没有太大意义,而且也没有必要,最终的输出还是取决于O-Net。

图像金字塔

由上一节展示的P-Net网络架构可知,其是在单尺度(12×12 size)上做训练的,为了检测到不同尺寸的人脸,所以在推理时需要先生成图像金字塔,而P-Net为何设计成FCN的原因也在于此。

首先,设置一个想要检测的最小人脸尺寸min_size,比如20×20,其次,设定一个缩放因子fator,通常为0.707。

然后,将图像的最短边乘以 12 / 20,进行一次缩放,接着不断对前面缩放的尺寸乘以fator进行缩放,比如最短边初始为s,接下来就会缩放得到:s x 12 / 20、s x 12/ 20 x factor、s x 12 / 20 x factor ^2、…,直至s不大于12时停止。

记录下以上缩放过程中(最短边还大于12时)的各个scale:12 / 20、12 / 20 x factor、12 / 20 x factor^2、…,最后依次按照每个scale对图像采用双线性插值的方式进行缩放从而得到图像金字塔。提问时间到!为何缩放因子是0.707?

这个数字不觉得有点眼熟吗?是的,它约等于

,那么这又代表什么呢?通常,按照我们凡夫俗子的思维,缩放系数都习惯于取0.5,如果边长都缩放0.5倍的话,那么矩形框面积就缩小了1/4,跨度未免有点大,可能会漏掉许多要检测的人脸。要知道,按照P-Net的意思,每个尺度对应的就是一张人脸。于是我们可以让面积缩放1/2,减小这之间的跨度,相应地,边长的缩放系数就为

十万个为什么系列之第二个灵魂拷问——min_size和factor对推理过程会产生什么样的影响?

由小学数学知识可知,min_size越大、fator越小,图像最短边就越快缩放到接近12,从而生成图像金字塔的耗时就越短,同时各尺度的跨度也更大。因此,加大min_size、减小factor能加速图像金字塔的生成,但同时也更易造成漏检。

好了,不问了,真要问十万个估计我的手要码到残废了..剩下的诸位自行yy。

由上述可知,图像金字塔的缺点就是慢!慢在需要不断resize生成各个尺寸的图像,还慢在这些图像是一张张输入到P-Net中进行检测的(每张的输出结果代表一张人脸候选区域),相当于做了多次推断过程,你说可不可怕..

P-Net获取人脸候选区域

将图像金字塔中每个尺寸的图像依次输入到P-Net,对于每个尺度的图像都会对应输出一个map,输出map的每点都会有分类score和回归的bbox offset。先保留置信度较高的一批位置点,然后将每点还原出对应的人脸区域,接着用NMS剔除重复的区域,得到所有尺寸图像保留下来的结果后,再用一次NMS进行去重(CW做过实验,不同scale下输出的图像之间NMS没有效果,说明不同scale下没有重复的,也符合不同scale检测不同尺寸人脸的意思),最后使用回归得到的offset对人脸候选区域进行校正得到bbox位置。十万个为什么系列再次上线——如何还原出输出map每点对应的人脸区域?

根据P-Net的结构设计可知,输出feature map的一点实际对应的是输入中12×12的区域,而P-Net中有一个池化层进行了2倍的下采样(对卷积层带来的尺寸损失不计),于是对于输出feature map的一点(x, y),我们可以这么做来进行还原:

i). 先还原出其在输入图像的位置:(x2, y2);

ii). 将其作为人脸区域的左上角,对应地,右下角为:(x2 + 12, y2 + 12);

iii). 由于在生成图像金字塔时进行了缩放,因此还要除以当时缩放的scale:(x1 = x2 / scale, y1 = y2 / scale)、(x2 = (x2 + 12) / scale, y2 = (y2 + 12) / scale)

以上(x1, y1, x2, y2)便是还原出来在原图上对应的人脸区域。

你们应该还没有吃饱,继续问——如何用回归得到的offset计算出人脸bbox位置?

x1_true = x1 + tx1*w

y1_true = y1 + ty1*h

x2_true = x2 + tx2*w

y2_true = y2 + ty2*h

使用以上公式就能计算得到结果,其中x*_true和y*_true代表人脸bbox的位置,tx*、ty*代表回归得到的offset,w和h就是人脸区域(x1, y1, x2, y2)的宽和高。

至于为什么能这么计算,这是由训练前制作label的时候决定的,在下一节讲训练过程时会说明,这里先hold着。

P-Net输出的bbox不够精准,仅作为包含人脸的候选区域,接下来交由R-Net进行校正。

R-Net基于P-Net的结果进一步校正

R-Net并不是直接将P-Net的输出结果拿来用,他还是个细心boy,先对这些输出“做了一番洗剪吹”,成为这条街上最靓的仔后再拿来用,处理过程如下:

i). 为了尽可能涵盖到人脸,将P-Net输出的候选区域都按其各自的长边resize方形;

ii). 检查这些区域的坐标是否在原图范围内,在原图内的像素值设置为原图对应位置的像素值,其它则置为0;

iii). 将以上处理得到的图片区域使用双线性插值resize到24×24大小

处理完后,所有输入都是24×24大小,网络输出的后处理与P-Net类似,先筛选置信度较高的一批结果,然后用NMS剔除重复的,最后使用offset计算出bbox位置。此处使用offset计算时用到的(x1,y1,x2,y2)就是P-Net输出的bbox。

又要问了——为何不直接对原图有效范围内的区域进行resize?

可以这么想,如果有效范围内的区域刚好正是人脸区域,丝毫不差,那么resize后必定会有像素损失,从而丢失细节。

O-Net检测出人脸和关键点位置

看到R-Net是个细心boy,O-Net当然也是很自觉地对R-Net的输出结果做了处理,处理方式与R-Net类似,只不过最终resize到的大小是48×48。

由于O-Net会输出landmarks的offset,因此还需要计算出landmarks的位置:

x_true = x_box + tx*w

y_true = y_box + ty*h

(x_true, y_true)代表一个关键点的位置,tx、ty代表对应的offset,(x_box, y_box)代表R-Net输出bbox的左上角位置,w、h代表bbox的宽、高。至于bbox位置的计算、后处理等这些也都和之前的类似,这里不再阐述了。

哦,另外,在此处NMS计算IoU时,分母可以不取并集,而是取当前置信度最高的bbox和其余bbox面积的较小者,这样分母会变大,从而导致IoU计算结果偏高,相当于变向降低NMS阀值,对重复检测的要求更为苛刻。

三训练过程

MTCNN的三个网络是依次分别训练的,先训练P-Net,待其训练好后,将其检测结果用于R-Net的训练数据,然后对R-Net进行训练。同样地,R-Net训练完后,将其检测结果用于O-Net的训练数据,最后训练O-Net。

数据样本分类

MTCNN是集成了人脸检测和人脸对齐的多任务架构,通常使用两个数据集来训练,其中一个训练人脸检测,另一个训练人脸对齐(这部分其实也能训练人脸检测)。在处理训练数据时,会对图像进行随机裁剪,具体方法后文会讲解,这里再埋个坑。可以将训练样本分为4类:

i). Positive:裁剪后图片与人脸标注框 IoU >= 0.65,类别标签为1;

ii). Part:裁剪后图片与人脸标注框 IoU

[0.4, 0.65),类别标签为-1;

iii). Negative:裁剪后图片与人脸标注框 IoU < 0.3,类别标签为0;

iv). Landmarks:来自训练人脸对齐的数据集,类别标签为-2

使用Positive和Negative样本计算分类损失,使用Positive和Part样本计算bbox回归损失,而仅使用Landmarks样本计算landmarks回归损失。

十万个为什么时间到——为何只使用Postive和Negative计算分类损失?

应该是考虑到这些正负样本与标注框的IoU差别明显,区分度大,使模型更易收敛。

标签制作

填坑时间到!之前埋下的2个坑会在这里填上,打起精神咯!

P-Net、R-Net和O-Net的标签处理过程有些许差别,下面依次介绍。

P-Net

负样本Negative

  1. 对图像进行随机裁剪,裁剪后为方形,边长s=(12, min(H, W) ),H、W分别为图像的高和宽。将图像左上角向右下方进行移动,移动后横、纵坐标分别为

(0, W – s)、

(0, H – s),于是裁剪后图像右下角坐标为(x2=x1 + s, y2=y1 + s)。

  1. 计算裁剪后图像与原图中所有标注框的IoU,若最大IoU<0.3,则将这张裁剪后的图像作为负样本Negative,resize至12×12大小,对应的类别标签记为0,保存下来待训练时用。

  2. 重复1、2直至生成了50个负样本图像。

  3. 对图像中的每个标注框重复5次随机裁剪,也是用于生成负样本。裁剪后边长s与1中的相同,移动bbox的左上角(x1, y1),移动后(nx1=x1 + delta_x, ny1=y1 + delta_y),其中delta_x

[max(-s, -x1), w),delta_y

[max(-s, -y1), h),也就是对标注框左上角随机进行上下左右移动,w、h分别bbox的宽和高。对应地,bbox右下角坐标变为(nx2=nx1 + s, ny2=ny1 + s)。若右下角坐标超出原图范围,则舍弃该裁剪图片,进行下一次裁剪。

正样本Positive和Part

  1. 紧接着4,在一个标注框随机裁剪5次后,再重复随机裁剪20次,用于生成正样本Postive和Part。裁剪后图像边长s

即bbox短边的0.8倍至长边的1.25倍。对bbox的中心点随机进行左右上下移动,移动范围delta_

[-0.2w, 0.2w),delta_

[-0.2h, 0.2h),于是移动后中心点坐标为

,

相应地,左上角和右下角坐标为

(

)若右下角坐标超出原图范围,则舍弃此次裁剪的结果,继续执行下一次随机裁剪。

  1. 若裁剪后图像与该bbox的IoU>=0.65,则作为正样本Positive,resize到12×12大小,类别标签记为1;否则若IoU>=0.4,则作为Part,同样也resize成12×12,类别标签记为-1;

  2. 对于Postive和Part样本,计算bbox左上角和右下角的offset标签:

其中,(x, y)为标注框左上角/右下角的横、纵坐标,(nx, ny)为裁剪图片的左上角/右下角坐标,s为其边长。也就是标注框两个角相对于裁剪图片对应两角的归一化位移。最后将Postive和Part样本对应的裁剪图像、类别标签、offset标签保存下来待训练时用。

关键点样本Landmarks

  1. Landmarks样本在另一个数据集中单独处理,每张图片是一个单独的人脸,对每张图重复10次随机裁剪,裁剪方式与生成Positive和Part样本时相同,若裁剪后图像右下角坐标不在原图范围内,或者裁剪后图像与标注框IoU<0.65,则舍弃此次裁剪结果,进行下一次随机裁剪。

  2. 计算bbox和landmarks的offset标签,其中bbox的计算方式与Postive以及Part的相同,这里不再阐述,而landmarks的如下:

其中,(x, y)为标注的关键点坐标,

为裁剪后图像的左上角坐标,s为裁剪后图像的边长,即标注的关键点相对于裁剪图像左上角的归一化位移。

R-Net

在R-Net的标签制作前需要使用训练好的P-Net的数据集进行检测,后续的数据处理过程与P-Net类似,下面进行介绍。

  1. 使用训练好的P-Net对数据集的图片进行检测,将原图、标注框以及检测出的bbox位置对应保存下来。

  2. 将原图、标注以及图中检测出的bbox依次取出,对每个bbox依次处理,若尺寸小于20(min_size,代表想要检测的最小人脸size)或者 坐标不在原图范围内,则舍弃该检测出的bbox,继续处理下一个bbox;否则,将bbox对应的区域从原图中裁出并resize到24×24备用。

负样本Negative

  1. 计算bbox与所有标注框的IoU,若最大IoU<0.3且当前生成的负样本数量小于60个,则将裁剪出的图像作为负样本,类别标签记为0,保存下来待训练时用。

正样本Positive和Part

  1. 否则,若最大IoU>=0.65或0.4<=最大IoU<0.65,则对应地将裁剪出的图像记为正样本Positive或Part,类别标签记为1或-1。同时,取出最大IoU对应的标注框,用于计算offset,计算方式与P-Net中提到的相同,最后将裁剪出的图像、类别标签、offset标签保存好待训练时使用。

O-Net

在制作O-Net的数据标签前需要依次使用训练好的P-Net和R-Net对数据集做检测,然后将R-Net预测的bbox和对应的原图以及标注保存下来供O-Net使用。

标签制作过程和R-Net的几乎相同,只不过没有最小负样本数量的限制(P-Net有50个、R-Net有60个)。另外,若landmarks数据集的标注框最长边小于40或坐标不在原图范围内,则舍弃这个样本。

Loss函数

loss由三部分组成:分类损失、bbox回归损失、landmarks回归损失。

每个网络的loss都由这3种loss加权而成,其中分类损失使用交叉熵损失,回归损失均使用L2距离损失。

上式中,j代表不同的任务(分类、bbox回归、landmarks回归),

即不同任务对应的loss权重,对于P-Net和R-Net,它们的分类loss、bbox回归loss、landmarks回归loss的权重比为1:0.5:0.5,而O-Net中为1:0.5:1。

是第i个样本在任务j中是否需要贡献loss,需要则为1,否则为0。例如,对于Negative样本,它在回归任务中这个β值就为0。

OHEM(Online Hard Example Mining)

在线困难样本挖掘的策略仅在计算分类loss时使用。直白来说,就是挑选损失最大的前70%作为困难样本,在反向传播时仅使用这70%困难样本产生的损失。作者在paper中的实验表明,这样做在FDDB数据集上能带来1.5个点的性能提升。

四总结

最后,总结下MTCNN的整个pipeline:

首先,对待检测图片生成图像金字塔,输入P-Net,得到输出,通过分类置信度和NMS筛选出人脸候选区域;

然后,基于这些候选区域去原图crop出对应的图像,输入R-Net,得到输出后同样是使用分类置信度和NMS,过滤掉false-positive,得到更精准的一批候选;

最后,基于得到的这批候选去原图crop出对应的图像,输入O-Net,输出最终预测的bbox和landmark位置。