MTCNN论文

 
Joint Face Detection and Alignment using Multi-task Cascaded Convolutional Networks 
Kaipeng Zhang, Zhanpeng Zhang, Zhifeng Li, Senior Member, IEEE, and Yu Qiao, Senior Member, IEEE

联合 人脸 检测 与 对齐 使用 多任务 

discrete 英/dɪˈskriːt/ 美/dɪˈskriːt/  adj. 离散的;分离的;各别的;互不相连的
也可以理解为串联,所串的事物是不相关的

物体检测:类别,概率,框,
- 类别:有哪些种类的物体
- 概率:每类的概率多少
- 框:两个框的offset 

人脸对齐:眼睛,鼻子,嘴角 五点定位

二分类器解决多类别的问题

 
一个模型判断一个事物,有多少类别,就训练多少个模型

模型1,输入一张图像数据,判断是否含有事物A,
模型2,输入一张图像数据,判断是否含有事物B,
...
...
... 

机器学习要求很强的数学能力,
深度学习要求灵活解决问题的能力

重在思想,思想可以联接二者,思想可以联接一切...学科
    

物体检测多分类

 
MTCNN,或者说物体检测,是一次分出多个物体类别,并且还可以给物体加上框 
    

resize的目的

 
模型输入固定,图像resize成一系列比例的图片  
- 因为不知道图像上哪些地方有人脸
- 也不知道人脸的大小,是小脸还是大脸 
- 因此,将输入的图片resize成一系列大小的图片
- 如果整个图片都是人脸,那么它只有resize成小图片,即与框差不多的大小的图片时,才能被检测出来 
- 如果人脸是图片上的一小块,那么它只有resize成大图片,然后切片出的小图片,与框匹配才能检测出来 

小图检测大脸
大图检测小脸
    

切图之P-Net

 
P-Net对切图后的小图片进行判断
- 这些小图片从原始图片resize后的图片上切 

P-Net的判断非常简单
- 切图后生成了大量的图片,大部分是背景,背景与人脸的区别还是比较大的
- P-Net 就是用简单的办法,去除哪些不怎么像人脸的背景
- 处理后的结果,包含了人脸,也包含了有点像人脸的图片 
    
可以认为第1阶段排除了那些根本不可能的数据,且是用非常简单的方法进行的排除

 

    

 
P-Net 
切图+初筛 
先宁可错选也要多选,然后 初步筛选 

 
NMS:
非极大值抑制,抑制的是得分低的预测框,涉及到两个指标,
IOU(交并比:两个框的重叠区域面积与两个框并集面积的比值)和预测框得分,
若有多个预测框预测的是同一个物体,若两个框的IOU值大于阈值(一般阈值取0.5和0.7),
那么NMS算法将会把得分低的预测框丢弃。

参考 【深度学习】——物体检测细节处理(NMS、样本不均衡、遮挡物体)
  

 
R-Net 精修 
- 不同于PNet用使用卷积,RNet最后使用了全连接 

O-Net 精修+五点定位 
  

 

    

框的设计

 
模型设计一个固定大小的框,12*12 

图片按固定的比例,比如0.9开始缩放,至到最小的框接近12*12 
- 如果该图片上只有一个人脸,且人脸挤满整个图片的话,这张图片收缩至12*12后,刚好与模型框匹配
- 如果该图片上的人脸比较小,收缩到12*12后,人脸特征几乎消失,将被模型判断为非人脸
- 这就是小图匹配大脸 
- 然后是比12*12稍微大点的,按12*12的尺寸切图,必定能切中人脸,
- 因为该图片就比12*12大了一圈,可是切出了多个12*12尺寸的图片
- 再大点,再大点...一点点增大,但切图全是12*12,这样逐渐增大,必定能切中所有的人脸 

如此切图逻辑,所有的图片都在12*12的图片上,只是不同图片的缩放比例不同 

切图大小是12*12,那后面的24*24,48*48是怎么回事?

 
图片是经过缩放的,即所有数据计算的是比例,这意味着同样可以应用到原图上 

原始图片上的标注框对应的是原图的坐标位置,中间处理全部转化为比例
- 比如,x/宽度,y/高度

12*12大小的图片进入模型之后,输出概率与框的偏移率 
- 确定该图片为人脸,转换后比例后,
- 实际上确定的是图片上某个比例处,比如图片左上角宽30%,高40%的位置有个人脸 
- 24*24是以这个位置为中心,稍微扩大一点范围,取的图片 
- 48*48的图片也是如此,只是多了五个位置偏移率的计算  
  

 

  

 

  

 


MTCNN总体思路

 
任务分析
--------------------------------
1. 判断一张图片是否为人脸
2. 在图片人脸部分画上一个框
3. 标注眼睛,鼻子,嘴角


数据集采集与构建
--------------------------------
针对任务1:
将任务简化为判断一张图片是不是正面人脸,
故,数据集的正样本要采集人的正脸图片

针对任务2:
提前在人脸图片上,在人脸部位画个框

针对任务3:
在人脸图片上眼睛,鼻子,嘴角部分 标注个点

数据集采集
由以上条件,真实采集的数据集是这样的:
N张图片,每张图片都是包含人脸的,
并且是人的正脸,就是面向镜头的正脸,
可以是头像,或上半身图像,或全身图像,
关键是每张图片的脸都是正脸,正脸,正脸... 

为什么非要是正脸?
AI还不知道什么是人脸呢,你就拿一个侧脸让它判断,这不是为难它,是为难自己了... 
每张图片上没有画框,但都对应一个标注文件,
里面记录人的正脸部分的框的坐标,以及眼睛,鼻子,嘴角的坐标


由 模型需要 决定的数据处理及损失函数设计
--------------------------------
任务1:
判断是否为人脸,
根据标注文件中人正脸的坐标,
去原图片上把图片扣下来,
再抖一下,就有人脸的正负样本了

任务2:
由任务1得到一张人脸的图片,这实际上就是当初的标注框内的图片 
怎么让程序框学习这个框呢?
不做处理是没办法了,因为现在的人脸就是当初的框,100%的正脸

要处理就得将一个问题再细化一些 
当初的人脸标注框,实际上大多框的人的头像,把耳朵,头发,脖子也给框进去部分
除了这个外,有的框还把背景框进去一部分,
也就是说,得到的人脸图片,虽然一眼看过去是个人脸,但又不完全是人脸 

于是新的任务就出来了:找到这张图片上,真正人脸的位置,记为box1
将原来标注框按一定比例收缩一圈,这样得到的图片为新任务的正样本,

这基于数据采集的一个标框前提,框的中心,就是真实的人脸
再科一下,得到的图片为新任务的负样本box2
这里负样本计算的是抖动后的图片box2相对box1的偏移率,
偏移率 = (box2 - box1)/box1的最大边长

损失函数设计为让这个偏移率逼近0 
- 这就是一个学习过程,让AI认识到什么是正脸
- 同时,对于接近正脸的事物也进行学习,比如侧了10%的样子是什么,侧20%的样子又是什么
- 对于周边事物的认识也有利于AI判断真正的正脸

任务3:
由任务2得到了box2,
损失函数设计为让
原五点坐标相对box2的偏移率2 
逼近
原五坐标相对box1的偏移率1

 

  

原始数据集

 
原始图片上做人脸框标注,并记录框的坐标

数据转化

 
正负样本生成
- 取标注框,收缩10%为正样本,
- 抖动一下,重叠度低于一定阈值为负样本


框的偏移率
- 转化为框的四个点x,y坐标的偏移率 
- (x1-x2)/x1  
- 如此,生成的每个图片都带了一个偏移率
- 因为图片是按不同比例缩放的,偏移率也是使用比例计算,与切图在原图片上的偏移率一致 

新的数据集是标注框附近的切图+框的偏移率 

 


训练

 
训练是并行的,得同时准备一堆12×12,24×24,48×48的图像,
让模型学习什么是人脸 及 偏差多少以内仍算人脸 
并在此基础上进一步确定 眼睛,鼻子,嘴角 

这个觉得耳朵也是一个很好的边界,添加一下耳朵效果可能会更好 
  

预测

 
预测是串行的,先PNet,后Rnet,最后Onet,前一个网络的输出,是后一个网络的输入 
  

 

  

 


 


单图多分类

使用卷积网络进行单图多类任务

 
卷积提取图像特征
全连接分类 
最后一层全连接输出的维度是总类别个数,每个元素值为 输入图像对应类别的概率 

针对异常处理,比如你输入了一张从未训练过的图像类别进行预测,
这个类别不在已有的任何类别当中,
针对此类异常情况,可以在训练的时候在类别中加入一个others类别,
将无法识别的,异常的定位到others

切图技巧

框的形状与大小占比

 
模型框的形状与物体的形状越接近越好 

模型框的大小转化为模型框占图像的比例
- 一个思路是图像不变,调整模型框的占比
- 一个是模型框大小不变,对图像的大小进行resize
  - 如果一个图像收缩,那么框不变的情况下,可以对大物体分类
  - 如果图像不收缩,框不变的情况下,可以对小物体分类
    

模型输入图像大小与切图大小的转换

 
模型输入规定 24*24
    
实际切出的人脸为11*13,其中一种做法如下:
- 以11*13图像的中心点为中心
- 11两边各扩展1,形成13*13像素的图片,中心点不变
- 再resize为24*24的图像
- 如此,图像不会变形,同比扩大


 

    

模型输入图片大小固定且统一,故事物图像从原图上取下来后要resize

 
第一次:图片派生
----------------------------------------------------
将图像按比例缩小,在这个过程中取一系列图片
原来有N张图片,假如一个图片缩小10次,最后得到10N张图片
通常下一次是这一次的70%,直到图像缩小到特定的尺寸;
如果不怕速度慢,以速度换精度,收缩比例可以一次90% 

因为大图在缩小过程中,小物体消失了,所以 由缩小得到的小图 只能检测出有没有大物体


滑动取框

 
第二次:窗口切图 
---------------------------------------------------
窗口设计:
正方形,横/竖长方形,三个,每个设计多个尺寸,得到一系列窗口
当然了,也可以只用方框 ,这取决于要判断的事物的形状

切图:
使用设计的窗口得到一系列图片,划动切图,得到一系列图片

在原图上使用窗口划动,只要窗口设计合适,每次步长不大,必定能圈住小物体

经过这两次操作,大物体转移到小图片了,
小物体存在于一堆由窗口划动切割的小图片中,

所以最终不管是大物体还是小物体,
都存在于最后得到的一堆小图片中了

如果等差数列的方式切框的问题 
- 优点是框的数量少
- 问题是会出现框切不到完整物体的情况

卷积切框
- 优点是可以切中物体
- 缺点是切一个框移动一点点,最后出现大量的无用的框

切框的本质 
- 将连续的无限的信息转化为离散的有限的信息,
- 这是模拟信号转化为数字信息的基本方法,就是短时傅立叶变换的思想

 
切图得到一张张固定大小的图片,开始学习,学习的内容主要有
- 是不是人脸
- offset是多少
  - 一个人脸图片,由于切图原因的不完整(提前是拍摄角度是正脸且无遮挡...)
  - 不管什么原因,通过offset来衡量这张人脸的完整度 
  - offset越小,那么该人脸图像就越接近原图像,它就越完整
    

 
核心思想
-------------------------------- 
会切出很多的图,宁可错选1000,绝不遗漏1个;逐窗切图,暴力计算... 

MTCNN切图的框只有一种,所以切出来的图的大小,也只有一种12*12,
MTCNN没有设计矩形的框,即那些长条形的框,
因为MTCNN是针对人脸,人脸大部分是正方形,
这是由具体的任务-人脸识别决定的 

如果检测的物体不只一种,形状各异,那就得正方形,横竖矩形都有,
甚至你检测的物体是个类三角形,你就得设计一个三角形的框

虽然都叫MTCNN,但不同的业务调整过的MTCNN设计的细节是不一样的:
框的形状
速度与性能的平衡
    

 

    

 


 

  

 


P-Net

一张图片上有大量的背景,按前面的切图法,会切出大图的背景图片

 
Stage1 P-Net: 切图+粗选 

输入:12×12×3,实际可以输入任意大小的图片,因为P-Net没有要求输出的shape

第1层:3×3卷积+maxpool提取特征
第2层:3×3卷积
第3层:3×3卷积

图片分类:是否人脸
偏差计算:如果是人脸,则计算标签人脸的框 与 模型输入的切割后的小图片,在原图上位置的 偏移率

 
现在深度学习网络层数较多,三层卷积就能提特征的情况还是比较少
关键是效果要好才行 
    

 
输入:12*12的图像,大部分都不是人脸
输出:
- 是否人脸及偏移量
- 在是人脸的情况下,再加一个偏移量 
  - 一个12*12的图像是人脸,但可能不是100%与原人脸的图像重合
  - 需要在原图像上,上下左右移动一下,这就是偏移量
  - 如果完全重合,那么偏移量为0 
  - 输出的人脸图片+偏移处理,这样得到的图片,
  - 就去除了大部分非人脸图片,同样向着完全的人脸靠近了一些 
- 框+偏移量之后,得到的框就不再是正方形了,所以后面RNet处理时,重新resize 

 

    

 
import torch
from torch import nn


class PNet(nn.Module):
    def __init__(self):
        super(PNet, self).__init__()
        self.pre_layer = nn.Sequential(
            # 第1层卷积
            nn.Conv2d(in_channels=3,
                        out_channels=10,
                        kernel_size=3,
                        stride=1,
                        padding=0),
            nn.BatchNorm2d(num_features=10),
            nn.PReLU(num_parameters=10, init=0.25),

            # 最大池化
            nn.MaxPool2d(kernel_size=2, stride=2),

            # 第2层卷积
            nn.Conv2d(in_channels=10,
                        out_channels=16,
                        kernel_size=3,
                        stride=1,
                        padding=0),
            nn.BatchNorm2d(num_features=16),
            nn.PReLU(num_parameters=16, init=0.25),

            # 第3层卷积
            nn.Conv2d(in_channels=16,
                        out_channels=32,
                        kernel_size=3,
                        stride=1,
                        padding=0),
            nn.BatchNorm2d(num_features=32),
            nn.PReLU(num_parameters=32, init=0.25)
        )

        # 输出人脸的概率 bce
        self.conv4_1 = nn.Conv2d(in_channels=32,
                                    out_channels=1,
                                    kernel_size=1,
                                    stride=1,
                                    padding=0)

        # 输出人脸的定位框的偏移量(误差)
        self.conv4_2 = nn.Conv2d(in_channels=32,
                                    out_channels=4,
                                    kernel_size=1,
                                    stride=1,
                                    padding=0)

    def forward(self, x):
        x = self.pre_layer(x)
        cls = torch.sigmoid(self.conv4_1(x))
        offset = self.conv4_2(x)
        return cls, offset
    

 
输出有二:一个分类,一个回归,回归是一个数

face classification :2分类,1*1*2 ,使用sigmoid映射到[0,1]
bounding box offset regression :1*1*4,一个框4个顶点,4个回归值   

核心代码

 
# 输出人脸的概率 bce
self.conv4_1 = nn.Conv2d(in_channels=32,
                            out_channels=1,
                            kernel_size=1,
                            stride=1,
                            padding=0)

# 输出人脸的定位框的偏移量(误差)
self.conv4_2 = nn.Conv2d(in_channels=32,
                            out_channels=4,
                            kernel_size=1,
                            stride=1,
                            padding=0)

凭什么conv4_1就是概率,conv4_2是偏移率 ?

 
它们最初是随机初始化的,一开始的时候的确什么也不是 
- 计算之始,它们确定了一个输出的形式
- 概率输出一个数值 
- 偏移率是框四个点的偏移率,所以输出4个值 

它们将来会具有什么样的业务含义,完全是由损失函数设计决定的
- 使用损失函数衡量它们与标签之间的差异 
- 梯度下降法不断修改模型参数,使模型的输出不断向标签靠近 
- 如此,模型的输出才慢慢具有了业务含义 ,才是概率,才是偏移率...

 
kernel_size=1,
stride=1,
padding=0
每次计算一个元素,然后计算下一个元素,这就是全连接的功能 

但如果换成全连接,在API上就必须填写输入的维度,
卷积就不需要填写这个,只填写通道数就可以了,
这意味着最后一层的特征图的维度可以是随意的,
即意味着输出的图形的形状也不固定,是随意的

更具体地说,是可以随着输入的不同而变化的,
而不是像最后一层加全连接的网络那样,输出的格式是固定的

 


 

  

 


R-Net

 
Stage1 P-Net处理过的图片先按其最长边长 resize为正方形,
再resize为24*24*3的图片 

输入:24×24×3

第1层:3×3卷积+maxpool提取特征
第2层:3×3卷积+maxpool提取特征
第3层:2×2卷积
第4层:全连接 

图片分类:是否人脸
偏差计算:
- 如果是人脸,则计算标签人脸的框 与 模型输入的切割后的小图片,在原图上位置的 偏移率
- 不是人脸的,就不用计算偏移率了,节省了计算资源
- 前一步的误判,即不是人脸的图片被判定为人脸时,在偏移率计算时会被处理掉

O-Net

 
一系列卷积操作后

图片分类:是否人脸
偏差计算:
如果是人脸,则计算标签人脸的框 与 模型输入的切割后的小图片,在原图上位置的 偏移率

五点定位:
眼睛,鼻子,两个嘴角,在定位的过程中,也会消除一系列R-Net产生的错误框
计算五点相对模型框左上角的偏移率

 
对于一个人脸识别任务,只需要判定是否为人脸就可以了
但该算法有更高的追求,还要判断出眼睛鼻子,
虽然任务本身没要求,但却在这一过程中提高了人脸识别的精度

这也为精度的提高提供了一条思路
- 当某个层面上事情无法精进时,可以去比它更高的一层上探索
- 好像...很多事就是如此,这也算理论转算法的一个实践了... 

 

    

 
Stage1 R-Net处理过的图片先按其最长边长 resize为正方形,再resize为48*48*3的图片 

 


 


 

  

 


设计思路

 
1. 找正脸图片,
   因为人脸识别的时候都要求你正对镜头,所以数据集要收集正方向的人脸,
   要找的人脸图片是人的半身或全身图片,然后让程序去学习什么是人脸

2. 人脸图片坐标标注
   2.1 在原图上用正方形框 框住人脸,记录的是框左上角与右下角的坐标
   2.2 五点标注,两个眼睛,鼻子,两个嘴角,记录的是这五个点的坐标
   2.3 图像左上角的坐标是(x,y)=(0,0),横/宽方向是x轴,竖/高方向是y
   2.4 在图像收缩到12*12大小时,(x,y)坐标同比收缩,这样所有标记坐标就统一了尺度
   2.5 也就是说,最终的入模型的数据是百分比,范围正常情况下0-1

3. 分类任务 
   取人脸部位图像的小正方形,得到一堆小正形人脸图片,单个小正方形的主体内容就是一张人脸,
   让AI从整张图片上学习哪些特征是人脸,所以要先告诉AI哪些特征是人脸,
   AI学习之后就能分辨一个小图片是不是人脸,
   这一步就是让AI拥有识别人脸的能力
   这是人脸分类任务,判断一张图片是不是人脸
   这里只有正面人脸,即正样本,即只有一个类别,分类任务至少要两个类别

   我们对本任务进行分析:
   我们要的w人正脸图片,这是正样本,
   如果镜头偏了一点,或者人稍微偏了一点,那算不算人脸?
   这里我们定下一个标准,本任务是人脸识别,就定下重叠50%的算,其他的都不算 
   这个其他的也包括了背景图,即不是人的部分
   分类任务不涉及坐标计算,单纯的就是判断一张图是不是人脸

4. 框的误差 
   输入一张图片,是一个人的全身图片,我们期望它能把人脸的位置框出来,
   程序从原图取下来的一系列小图片,同时记录了它在原图的位置,
   然后对每个小图片进行是否人脸判断,
   如是人脸,就能根据位置记录,回溯到原图,然后画个框... 
   这个框就是告诉人们,看,我找到你了... 但有点偏... 
   于是就有了第二个任务,框的误差计算
   标注的时候记录过人脸框的左上右下坐标,后来转为了图像的百分比,即0-1之间的数,
   由人脸分类任务,判断出这是一张人脸,这张图片也记录了左上右下坐标,也转成了0-1的数,
   标注框与得到有人脸的框 坐标误差越小,框的越准,
   设计一个损失函数计算框的误差,随着不断迭代,框的准确度就会不断提高

5. 模型输入进一步分析 
   P-Net 输入的12*12的图片
   将切图后的图片全部转为12×12的图片,然后输入P-Net模型,
   12×12图片很小,比芝麻大点,比绿豆小点,差不多就这样 ,
   这些小图有是人脸,有不是的,有一部分是的,
   前面我们定了个重叠大于50%的就是人脸这么一个标准,
   所以P-Net判断出的人脸的图片,有一些图片只是人脸的一部分,
   另一部分超出了当前图片12×12的范围,
   这些小图片都是带着原图的坐标的,
   P-Net下一步是R-Net,它的输入是24×24 
   R-Net输入的24×24是根据P-Net在原图上的坐标,直接从原图上取的24*24
   P-Net只有三层卷积,12×12又是很小的图片,
   粗粒度的计算又加上图片的高度收缩,信息丢失很多,
   但由于定位是人脸的标准很低,重叠50%就算,宁可错选1000,也不遗漏1个,
   也就是说,P-Net把图片上看上去稍微像点人脸的区域坐标都给取下来了,
   而R-Net是从原图上取的坐标,这样取到的图片就弥补了P-Net信息丢失的缺陷,
   这么一来,信息就丢失就很少了, 
   R-Net对这些包含一些人脸的图片以稍微高点的标准判断一下是不是人脸,
   得到的图片就比较迫近标签标注的人脸图片了 

   根据R-Net得到的比较接近人脸的区域坐标,
   再扩大四倍范围,从原图上取48×48区域,就是O-Net的输入,
   O-Net不仅定位了人脸的框,还进行了更加细化的五点定位,

6. 坚持至简 
   我们知道有些问题本层面方法是无法很好解决的,怎么搞就只能解决90%的问题,
   但若你再上升一个层面,虽然更高层层面的方法解决更高层面的问题只能解决80%,
   但对来对付低一个层面的问题,却能解决其95%,
   并且是轻松解决,不是当初费了九牛二虎之力才提升了1%的精度,

   O-Net就有这样的一种思想,
   你看它的网络设计,都是非常简单的,没有很深的层数,
   也没有很高大尚的 类似图像金字塔 涉及傅立叶变换等 这样高深莫测的事物

   简单的组合之后,
   仅凭这三个网络就已经可以名扬天下了(即使没有最后的五点定位),
   但发现最后的精度不是很高,或者说没有达到让当时的人们感觉到震撼的程度,
   MTCNN的解决办法不是去复杂化一个简单的网络(这才是解决问题的首先想到的思路),
   而是在人脸分类还没搞好的情况下,直接去定位眼睛,鼻子.... 

   本层面简单方法解决不了的问题,就去找高一个层面的简单方法... 

   像不像古代有些人,自己温饱问题还没解决,房子媳妇还不知道在哪,脑子里天天想的却是天下大事... 
   实际上,若是你真能解决一个天下大事,前面的都不叫事了... 
   
   五点定位的计算方法也不并复杂,但直接提高了人脸分类的精度,
   即把那些本不是人脸区域 却误判为人脸的框 给消除了

划框理论

 
MTCNN要解决的两大问题:
1. 分类 
2. 定位

首先程序通过训练集已经学习过了,有了区分物体的能力

以框切图来分类:
   这个框要与检测的物体样子差不多,不能相差太远 
   滑动取框,切出一系统数据集,分正负两类样本,用于训练/预测 
   可以将这个过程看作一个实时动态分类的过程,
   每切出一个框就可以判断它是不是要检测的物体,
   同样的,用滑框理论检测信号波,识别语音也是可以的

框的合并:
   如果同样包含一个物体的两个框重叠度比较高,可以进行合并
   这样框的数量会大量减少,
   因为滑框的步长比较小,
   这就导致一个物体附近必定存在多个包含该物体的框 

损失函数设计

图像偏移率

 
通过图像增强生成一系列新的图像,记录这些新图像在原图上的坐标,这里记为新坐标

(原坐标 - 新坐标)/ 新的最大边长 = 偏移率 

所有点的 偏移率之和 就是 损失函数 

损失函数的目标是 让程序框到的图像 离 真实标签框的图像 的偏移率 越来越小

如此,程序框 就逐渐逼近 标签框 了

损失目标与数据集构建

 
MTCNN 是多任务,这里有三个损失函数
1. 图像分类,是否有人脸 
2. 框的损失,目标是程序框逼近标注框
3. 五点定位,目标是程序点逼近标注点

图像分类:
二分类问题,先从原图像上切出一系列固定大小的图像,然后确认其是不是人脸 
切图时,要生成了正样本,标注为1,表示是有脸,负样本标注为0表示不是人脸,
将这些数据扔进几个net训练一下,就有分类的能力 

框的损失:
标注框就是提前画好的框,目的在于让AI学习 图像上哪些位置是有人脸 
程序框与标注框的差,偏移率 这两个值都能反应损失的大小
但由于人脸在不同的照片上缩放程序不一样,就导致其视觉大小也不一样 
所以选择偏移率来作为损失函数,让程序框 偏移 标注框 的比例接近0 是任务目标 
那怎么制造/生成 一批 数据/图像 让AI学习这个偏移率呢?这是一个连续型数值 
标注图像记录下图像的名称,高与宽,框的坐标,这是原始的标注信息
使用图像增强,就是让标注框抖动一下,生成一批框,记录下新框的位置,相对标注框的偏移率
轻微抖动表示偏移率很小,是任务需要的,抖动过大的,就是负样本,舍弃不要,
因为我们的目标是学习什么是人脸,
就是给了大批量的偏移率小的场景让任务去学习,
偏移率越小,框的就越准确

五点定位:
眼睛,鼻子,两个嘴角,是在人脸的基础上标注的,是相对人脸坐标的一个位置
定下人脸,五点的位置就是一个相对位置,
同样的,具体的相对坐标大小,不如相对偏移率更合适
但以哪个位置为标准呢?可选的有,框的左上,右下,中心 
可能是因为图像的原点坐标就是左上,
所以,五点定位的起点就是框的左上角(x0,y0)
以鼻子为例(x1,y1),偏移率计算如下:
(x1-x0)/max_size为宽方向上的相对偏移率 
(y1-y0)/max_size为高方向上的相对偏移率 
各个单维度上计算,然后再组合回二维

标注框中五点偏移率是个比值,
就鼻子而言,
它学习到的可能就是人脸框 宽方向二分之一处,
高方向大概也是二分之一就是鼻子了吧

损失函数设计

 
分类任务损失函数可以选择交叉熵 
针对这里的二分类问题,可以选择BCE 

回归问题,MSE 

参考