paddle2.0高层API实现人脸关键点检测(人脸关键点检测综述_自定义网络_paddleHub_趣味ps)
[toc]
本文包含了:
1 | - 人脸关键点检测综述 |
『深度学习 7 日打卡营·day3』
零基础解锁深度学习神器飞桨框架高层 API,七天时间助你掌握 CV、NLP 领域最火模型及应用。
- 课程地址
传送门:https://aistudio.baidu.com/aistudio/course/introduce/6771
- 目标
- 掌握深度学习常用模型基础知识
- 熟练掌握一种国产开源深度学习框架
- 具备独立完成相关深度学习任务的能力
- 能用所学为 AI 加一份年味
一、问题定义
人脸关键点检测,是输入一张人脸图片,模型会返回人脸关键点的一系列坐标,从而定位到人脸的关键信息。
人脸关键点检测是人脸识别和分析领域中的关键一步,它是诸如自动人脸识别、表情分析、三维人脸重建及三维动画等其它人脸相关问题的前提和突破口。近些年来,深度学习方法由于其自动学习及持续学习能力,已被成功应用到了图像识别与分析、语音识别和自然语言处理等很多领域,且在这些方面都带来了很显著的改善。因此,本文针对深度学习方法进行了人脸关键点检测的研究。
人脸关键点检测深度学习方法综述
Deep Convolutional Network Cascade for Facial Point Detection
2013 年,Sun 等人首次将 CNN 应用到人脸关键点检测,提出一种级联的 CNN(拥有三个层级)——DCNN(Deep Convolutional Network),此种方法属于级联回归方法。作者通过精心设计拥有三个层级的级联卷积神经网络,不仅改善初始不当导致陷入局部最优的问题,而且借助于 CNN 强大的特征提取能力,获得更为精准的关键点检测。
如图所示,DCNN 由三个 Level 构成。Level-1 由 3 个 CNN 组成;Level-2 由 10 个 CNN 组成(每个关键点采用两个 CNN);Level-3 同样由 10 个 CNN 组成。
DCNN 采用级联回归的思想,从粗到精的逐步得到精确的关键点位置,不仅设计了三级级联的卷积神经网络,还引入局部权值共享机制,从而提升网络的定位性能。最终在数据集 BioID 和 LFPW 上均获得当时最优结果。速度方面,采用 3.3GHz 的 CPU,每 0.12 秒检测一张图片的 5 个关键点。
Extensive Facial Landmark Localization with Coarse-to-fine Convolutional Network Cascade
2013 年,Face++在 DCNN 模型上进行改进,提出从粗到精的人脸关键点检测算法,实现了 68 个人脸关键点的高精度定位。该算法将人脸关键点分为内部关键点和轮廓关键点,内部关键点包含眉毛、眼睛、鼻子、嘴巴共计 51 个关键点,轮廓关键点包含 17 个关键点。
针对内部关键点和外部关键点,该算法并行的采用两个级联的 CNN 进行关键点检测,网络结构如图所示。
算法主要创新点由以下三点:
- 把人脸的关键点定位问题,划分为内部关键点和轮廓关键点分开预测,有效的避免了 loss 不均衡问题
- 在内部关键点检测部分,并未像 DCNN 那样每个关键点采用两个 CNN 进行预测,而是每个器官采用一个 CNN 进行预测,从而减少计算量
- 相比于 DCNN,没有直接采用人脸检测器返回的结果作为输入,而是增加一个边界框检测层(Level-1),可以大大提高关键点粗定位网络的精度。
Face++版 DCNN 首次利用卷积神经网络进行 68 个人脸关键点检测,针对以往人脸关键点检测受人脸检测器影响的问题,作者设计 Level-1 卷积神经网络进一步提取人脸边界框,为人脸关键点检测获得更为准确的人脸位置信息,最终在当年 300-W 挑战赛上获得领先成绩。
TCDCN-Facial Landmark Detection by Deep Multi-task Learning
优点是快和多任务,不仅使用简单的端到端的人脸关键点检测方法,而且能够做到去分辨人脸的喜悦、悲伤、愤怒等分类标签属性,这样跟文章的标题或者说是文章的主题贴合——多任务。
Joint Face Detection and Alignment using Multi-task Cascaded Convolutional Networks
2016 年,Zhang 等人提出一种多任务级联卷积神经网络(MTCNN, Multi-task Cascaded Convolutional Networks)用以同时处理人脸检测和人脸关键点定位问题。作者认为人脸检测和人脸关键点检测两个任务之间往往存在着潜在的联系,然而大多数方法都未将两个任务有效的结合起来,本文为了充分利用两任务之间潜在的联系,提出一种多任务级联的人脸检测框架,将人脸检测和人脸关键点检测同时进行。
MTCNN 包含三个级联的多任务卷积神经网络,分别是 Proposal Network (P-Net)、Refine Network (R-Net)、Output Network (O-Net),每个多任务卷积神经网络均有三个学习任务,分别是人脸分类、边框回归和关键点定位。网络结构如图所示:
MTCNN 实现人脸检测和关键点定位分为三个阶段。首先由 P-Net 获得了人脸区域的候选窗口和边界框的回归向量,并用该边界框做回归,对候选窗口进行校准,然后通过非极大值抑制(NMS)来合并高度重叠的候选框。然后将 P-Net 得出的候选框作为输入,输入到 R-Net,R-Net 同样通过边界框回归和 NMS 来去掉那些 false-positive 区域,得到更为准确的候选框;最后,利用 O-Net 输出 5 个关键点的位置。
DAN(Deep Alignment Networks)
2017 年,Kowalski 等人提出一种新的级联深度神经网络——DAN(Deep Alignment Network),以往级联神经网络输入的是图像的某一部分,与以往不同,DAN 各阶段网络的输入均为整张图片。当网络均采用整张图片作为输入时,DAN 可以有效的克服头部姿态以及初始化带来的问题,从而得到更好的检测效果。之所以 DAN 能将整张图片作为输入,是因为其加入了关键点热图(Landmark Heatmaps),关键点热图的使用是本文的主要创新点。DAN 基本框架如图所示:
DAN 包含多个阶段,每一个阶段含三个输入和一个输出,输入分别是被矫正过的图片、关键点热图和由全连接层生成的特征图,输出是面部形状(Face Shape)。其中,CONNECTION LAYER 的作用是将本阶段得输出进行一系列变换,生成下一阶段所需要的三个输入
PFLD: A Practical Facial Landmark Detector
这个人脸检测算法 PFLD,全文名称为《PFLD: A Practical Facial Landmark Detector》。作者分别来自天津大学、武汉大学、腾讯 AI 实验室、美国天普大学。该算法对嵌入式设备非常优化,在骁龙 845 的芯片中效率可达 140fps;另外模型大小较小,仅 2.1MB;此外在许多关键点检测的 benchmark 中也取得了相当好的结果。综上,该算法在实际的应用场景中(如低算力的端上设备)有很大的应用空间。
PFLD 模型设计
在模型设计上,PFLD 的模型设计上骨干网络没有采用 VGG16、ResNet 等大模型,但是为了增加模型的表达能力,对 Mobilenet 的输出特征进行了结构上的修改。
PFLD 的模型训练策略
一开始我们设计的那个简单的网络,采用的损失函数为 MSE,所以为了平衡各种情况的训练数据,我们只能通过增加极端情况下的训练数据、平衡各类情况下的训练数据的比例、控制数据数据的采样形式(非完全随机采样)等方式进行性能调优。
-
损失函数设计
PFLD 采用了一种很优雅的方式来处理上述各情况样本不均衡的问题,我们先看看其损失函数的设计:
上式中 wn 为可调控的权值函数(针对不同的情况选取不同的权值,如正常情况、遮挡情况、暗光情况等等),theta 为人脸姿态的三维欧拉角(K=3),d 为回归的 landmark 和 groundtrue 的度量(一般情况下为 MSE,也可以选 L1 度量)。该损失函数设计的目的是,对于样本量比较大的数据(如正脸,即欧拉角都相对较小的情况),给予一个小的权值,在进行梯度的反向传播的时候,对模型训练的贡献小一些;对于样本量比较少的数据(侧脸、低头、抬头、表情极端),给予一个较大的权值,从而使在进行梯度的反向传播的时候,对模型训练的贡献大一些。该模型的损失函数的设计,非常巧妙的解决了平衡各类情况训练样本不均衡的问题。
-
配合训练的子网络
PFLD 的训练过程中引入了一个子网络,用以监督 PFLD 网络模型的训练。该子网络仅在训练的阶段起作用,在 inference 的时候不参与;该子网络的用处,是对于每一个输入的人脸样本,对该样本进行三维欧拉角的估计,其 groundtruth 由训练数据中的关键点信息进行估计,虽然估计的不够精确,但是作为区分数据分布的依据已经足够了,毕竟还该网络的目的是监督和辅助训练收敛,主要是为了服务关键点检测网络。有一个地方挺有意思的是,该子网络的输入不是训练数据,而是 PFLD 主网络的中间输出,如下图:
主网络和姿态估计子网络的详细配置如下表:
1 | # 环境导入 |
1 | paddle.__version__ |
1 | '2.0.0' |
二、数据准备
传统人脸关键点检测数据库为室内环境下采集的数据库,比如Multi-pie
、Feret、Frgc
、AR、BioID
等人脸数据库。而现阶段人脸关键点检测数据库通常为复杂环境下采集的数据库.LFPW 人脸数据库有 1132 幅训练人脸图像和 300 幅测试人脸图像,大部分为正面人脸图像,每个人脸标定 29 个关键点。AFLW
人脸数据库包含 25993 幅从 Flickr
采集的人脸图像,每个人脸标定 21 个关键点。COFW
人脸数据库包含 LFPW
人脸数据库训练集中的 845 幅人脸图像以及其他 500 幅遮挡人脸图像,而测试集为 507 幅严重遮挡(同时包含姿态和表情的变化)的人脸图像,每个人脸标定 29 个关键点。MVFW
人脸数据库为多视角人脸数据集,包括 2050 幅训练人脸图像和 450 幅测试人脸图像,每个人脸标定 68 个关键点。OCFW
人脸数据库包含 2951 幅训练人脸图像(均为未遮挡人脸)和 1246 幅测试人脸图像(均为遮挡人脸),每个人脸标定 68 个关键点。
2.1 下载数据集
本次实验所采用的数据集来源为 github 的开源项目
目前该数据集已上传到 AI Studio 人脸关键点识别,加载后可以直接使用下面的命令解压。
1 | # 覆盖且不显示 |
解压后的数据集结构为
1 | data/ |
其中,training
和 test
文件夹分别存放训练集和测试集。training_frames_keypoints.csv
和 test_frames_keypoints.csv
存放着训练集和测试集的标签。接下来,我们先来观察一下 training_frames_keypoints.csv
文件,看一下训练集的标签是如何定义的。
1 | key_pts_frame = pd.read_csv('data/training_frames_keypoints.csv') # 读取数据集 |
1 | Number of images: 3462 |
Unnamed: 0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ... | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | Luis_Fonsi_21.jpg | 45.0 | 98.0 | 47.0 | 106.0 | 49.0 | 110.0 | 53.0 | 119.0 | 56.0 | ... | 83.0 | 119.0 | 90.0 | 117.0 | 83.0 | 119.0 | 81.0 | 122.0 | 77.0 | 122.0 |
1 | Lincoln_Chafee_52.jpg | 41.0 | 83.0 | 43.0 | 91.0 | 45.0 | 100.0 | 47.0 | 108.0 | 51.0 | ... | 85.0 | 122.0 | 94.0 | 120.0 | 85.0 | 122.0 | 83.0 | 122.0 | 79.0 | 122.0 |
2 | Valerie_Harper_30.jpg | 56.0 | 69.0 | 56.0 | 77.0 | 56.0 | 86.0 | 56.0 | 94.0 | 58.0 | ... | 79.0 | 105.0 | 86.0 | 108.0 | 77.0 | 105.0 | 75.0 | 105.0 | 73.0 | 105.0 |
3 | Angelo_Reyes_22.jpg | 61.0 | 80.0 | 58.0 | 95.0 | 58.0 | 108.0 | 58.0 | 120.0 | 58.0 | ... | 98.0 | 136.0 | 107.0 | 139.0 | 95.0 | 139.0 | 91.0 | 139.0 | 85.0 | 136.0 |
4 | Kristen_Breitweiser_11.jpg | 58.0 | 94.0 | 58.0 | 104.0 | 60.0 | 113.0 | 62.0 | 121.0 | 67.0 | ... | 92.0 | 117.0 | 103.0 | 118.0 | 92.0 | 120.0 | 88.0 | 122.0 | 84.0 | 122.0 |
5 rows × 137 columns
上表中每一行都代表一条数据,其中,第一列是图片的文件名,之后从第 0 列到第 135 列,就是该图的关键点信息。因为每个关键点可以用两个坐标表示,所以 136/2 = 68,就可以看出这个数据集为 68 点人脸关键点数据集。
Tips1: 目前常用的人脸关键点标注,有如下点数的标注
- 5 点
- 21 点
- 68 点
- 98 点
Tips2:本次所采用的 68 标注,标注顺序如下:
1 | # 计算标签的均值和标准差,用于标签的归一化 |
1 | 标签的均值为: 104.4724870017331 |
2.2 查看图像
1 | def show_keypoints(image, key_pts): |
1 | # 展示单条数据 |
1 | (136,) |
2.3 数据集定义
使用飞桨框架高层 API 的 paddle.io.Dataset
自定义数据集类,具体可以参考官网文档 自定义数据集。
作业 1:自定义 Dataset,完成人脸关键点数据集定义
按照 __init__
中的定义,实现 __getitem__
和 __len__
.
1 | # 按照Dataset的使用规范,构建人脸关键点数据集 |
2.4 训练集可视化
实例化数据集并显示一些图像。
1 | # 构建一个数据集类 |
1 | 数据集大小为: 3462 |
上述代码虽然完成了数据集的定义,但是还有一些问题,如:
- 每张图像的大小不一样,图像大小需要统一以适配网络输入要求
- 图像格式需要适配模型的格式输入要求
- 数据量比较小,没有进行数据增强
这些问题都会影响模型最终的性能,所以需要对数据进行预处理。
2.5 Transforms
对图像进行预处理,包括灰度化、归一化、重新设置尺寸、随机裁剪,修改通道格式等等,以满足数据要求;每一类的功能如下:
- 灰度化:丢弃颜色信息,保留图像边缘信息;识别算法对于颜色的依赖性不强,加上颜色后鲁棒性会下降,而且灰度化图像维度下降(3->1),保留梯度的同时会加快计算。
- 归一化:加快收敛
- 重新设置尺寸:数据增强
- 随机裁剪:数据增强
- 修改通道格式:改为模型需要的结构
作业 2:实现自定义 ToCHW
实现数据预处理方法 ToCHW
1 | # 标准化自定义 transform 方法 |
1 | import paddle.vision.transforms.functional as F |
看一下每种图像预处理方法的的效果。
1 | import paddle.vision.transforms as T |
2.6 使用数据预处理的方式完成数据定义
让我们将 Resize、RandomCrop、GrayNormalize、ToCHW
应用于新的数据集
1 | from paddle.vision.transforms import Compose |
1 | Number of train dataset images: 3462 |
3、模型组建
3.1 组网可以很简单
根据前文的分析可知,人脸关键点检测和分类,可以使用同样的网络结构,如 LeNet、Resnet50 等完成特征的提取,只是在原来的基础上,需要修改模型的最后部分,将输出调整为 人脸关键点的数量*2,即每个人脸关键点的横坐标与纵坐标,就可以完成人脸关键点检测任务了,具体可以见下面的代码,也可以参考官网案例:人脸关键点检测
网络结构如下:
作业 3:根据上图,实现网络结构
1 | import paddle.nn as nn |
3.2 网络结构可视化
使用model.summary
可视化网络结构。
1 | model = paddle.Model(SimpleNet(key_pts=68)) |
1 | ------------------------------------------------------------------------------- |
四、模型训练
4.1 模型配置
训练模型前,需要设置训练模型所需的优化器,损失函数和评估指标。
- 优化器:Adam 优化器,快速收敛。
- 损失函数:SmoothL1Loss
- 评估指标:NME
4.2 自定义评估指标
特定任务的 Metric 计算方式在框架既有的 Metric 接口中不存在,或算法不符合自己的需求,那么需要我们自己来进行 Metric 的自定义。这里介绍如何进行 Metric 的自定义操作,更多信息可以参考官网文档自定义 Metric;首先来看下面的代码。
1 |
|
作业 4:实现模型的配置和训练
1 | # 使用 paddle.Model 封装模型 |
1 | The loss value printed in the log is the current step, and the metric is the average value of previous step. |
损失函数的选择:L1Loss、L2Loss、SmoothL1Loss 的对比
- L1Loss: 在训练后期,预测值与 ground-truth 差异较小时, 损失对预测值的导数的绝对值仍然为 1,此时如果学习率不变,损失函数将在稳定值附近波动,难以继续收敛达到更高精度。
- L2Loss: 在训练初期,预测值与 ground-truth 差异较大时,损失函数对预测值的梯度十分大,导致训练不稳定。
- SmoothL1Loss: 在 x 较小时,对 x 梯度也会变小,而在 x 很大时,对 x 的梯度的绝对值达到上限 1,也不会太大以至于破坏网络参数。
模型保存
1 | checkpoints_path = './output/models' |
五、模型预测
1 | # 定义功能函数 |
1 | # 读取图像 |
1 | (847, 700, 3) |
使用 PaddleHub 进行测试
便捷地获取 PaddlePaddle 生态下的预训练模型,完成模型的管理和一键预测。配合使用 Fine-tune API,可以基于大规模预训练模型快速完成迁移学习,让预训练模型能更好地服务于用户特定场景的应用
图像 - 关键点检测
->face_landmark_localization
1 | !hub install face_landmark_localization==1.0.2 |
1 | /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/pandas/core/tools/datetimes.py:3: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working |
1 | import paddlehub as hub |
1 | [2021-02-06 01:30:35,589] [ INFO] - Installing face_landmark_localization module |
1 | image = cv2.imread('./test.jpeg') |
1 | (136, 1) |
1 | # 设置画幅 |
六、趣味应用
当我们得到关键点的信息后,就可以进行一些趣味的应用。
装饰预览
1 | star_image = cv2.imread('ps.jpeg') |
1 | (524, 650, 3) |
1 | # 定义功能函数 |
作业 6:实现趣味 PS
根据人脸检测的结果,实现趣味 PS。
1 | # 读取图像 |
1 | # paddleHub 效果可视化 |