Demo : 人脸5个关键点检测
资料
算法构建
人脸关键点检测,需要使用回归算法,因此一开始的想法就是前面使用多层卷积,适当添加残差网络作为基础模块,最后进行线性全连接层,直接预测5个坐标点的值。经过在网上查找资料,发现了PFLD框架,使用MoblieNet作为主干网络,同时将网络后三层进行拼接(cat),再进行全链接层的预测。
网络结构
backbone主干网络
PFLD框架的基础网络是基于MoblieNet V2进行修改的,在主干网络中使用了Inverted Residual Block基础模块和深度可分离卷积:
-
Inverted Residual Block基础模块是由1x1,3x3,1x1三个卷积构成的残差网络。
-
深度可分离卷积是将输入的通道分组进行卷积(以下简称DW)。
这样可以保持网络性能的同事,减少网络的参数、运算量和计算时间。PFLD基础网络如下:
Imput | Operator | channel | number | stride |
---|---|---|---|---|
112x112x3 | Conv3x3 | 64 | 1 | 2 |
56x56x64 | DW Conv3x3 | 64 | 1 | 1 |
56x56x64 | Inverted Residual Block | 64 | 5 | 2 |
28x28x64 | Inverted Residual Block | 128 | 1 | 2 |
14x14x128 | Inverted Residual Block | 128 | 6 | 1 |
14x14x128 | Inverted Residual Block | 16 | 1 | 1 |
(S1) 14x14x16 (S2) 7x7x32 (S3) 1x1x128 | Conv3x3 Conv7x7 - | 32 128 128 | 1 1 1 | 2 1 - |
S1,S2,S3 | Full Connection | 136 | 1 | - |
注:代码中已经不再使用此网络结构,在后文有说明代码优化了网络结构,但是依然留有此结构代码,可随意转换。
loss损失函数
损失函数没有使用PFLD论文中的结合人脸姿态角信息的损失函数,实际应用了wing loss而不加辅助信息对网络进行训练:
w
i
n
g
l
o
s
s
(
x
)
=
{
w
l
n
(
1
+
∣
x
∣
/
ϵ
)
,
if
∣
x
∣
<
w
∣
x
∣
−
C
,
otherwise
wingloss(x) = \begin{cases} wln(1+|x|/\epsilon), & \text{if }|x|<w\text{ } \\ |x| - C, & \text{otherwise }\text{} \end{cases}
wingloss(x)={wln(1+∣x∣/ϵ),∣x∣−C,if ∣x∣<w otherwise
其他
为了减小计算量,实际训练过程中没有使用PFLD框架的辅助子网络,也就是没有添加姿态角信息进行训练。
运行环境
-
Ubuntu 18.04
-
Python 3.6
-
Pytorch 1.9
-
CUDA 11.4
-
额外需要tqdm、tensoeboard,可以使用pip install 安装
-
数据集:放入在data文件夹中,需自行分配训练集(train)、验证集(validation)和测试集(predict),并将图片数据和标签数据分别放入在Images文件夹和Annotations文件夹内,如下图:
data
├── predict
│ ├── Annotations
│ └── Images
├── train
│ ├── Annotations
│ └── Images
└── validation
├── Annotations
└── Images注:predict下的Annotations文件夹内可以不存放标签数据,即对测试的数据仅仅进行预测,而不进行预测与真实之间的对比,后面有说明。
Train 训练
如果你将数据集存放在其他路径下,你需要修改程序根路径下的config.py文件,将其中的cfg.ROOT_TRAIN_PATH等进行修改。
如需要有其他修改,可以修改config.py 文件内的epoch、lr等等信息。
训练过程只需要运行train.py 文件,即在程序根路径下运行
python train.py
训练过程中,会自动将训练集和验证集进行训练,其中验证集并不对权重有所影响。训练结束后,训练过程中的信息会存储在./checkpoint/log/train.log文件中,可查看train.log文件,会记录每次epoch所产生的训练集loss、验证集loss和验证集RMSE。同时,训练过程中的这些信息会使用tensorboard工具将数据存放在./checkpoint/log/ 文件夹下,即 使用如下命令(在程序根路径下):
tensorboard --logdir=checkpoint/log
就可以在网页上查看数据图表。
训练后的权重文件存放在./checkpoint/weight/pfld_ultralight_final.pth
Test 测试
测试过程如训练过程,只需要运行test.py 文件,即在程序根路径下运行:
python test.py
测试数据集需要将图片文件存放在./data/predict/Images/ 文件夹中,而标签文件可选择是否存放在./data/predict/Annotations 文件夹中,下面进行说明:
测试过程会产生过程信息:time(每张图片使用的时间)、RMSE_112(计算图片resize成112大小之后的RMSE)和RMSE_basic(计算原始图片大小时的RMSE),存放在./checkpoint/predict_log/ 文件夹下,使用tensorboard工具即可查看,即在程序根路径下运行:(若没有标签文件,则只会产生time信息)
tensorboard --logdir=checkpoint/predict_log
测试结果存放在**./result/** 文件夹下:
图片信息存放在Images文件夹下,文件名字按照训练顺序排列,对应了在tensorboard工具显示下的Loss和RMSE的横轴。每张图片中,红点代表模型预测点,绿点代表模型真实点 。(若没有标签文件,则没有Loss和RMSE的信息,图片上也没有绿点显示真实点)
标签信息存放在Landmark_basic 文件夹下,分别以txt为后缀的文件存储,文件名字是进行预测的图片的名字,即每个标签文件与每张图片名字对应 。
运行完test.py文件后,会在终端打印出每张图片平均的运行时间,RMSE_112和RMSE_basic的平均值。(若没有标签文件,则只会显示平均时间)
注:test.py文件自动选择./checkpoint/weight/pfld_ultralight_final.pth 的权重文件。
算法优化与对比分析
在运行算法的初期,经过多次修改参数,获得一个相对良好的输出,参数如下:
seed = 10 #随机种子,使得每次权重初始化的值一样
train_batch_size = 32 # batch_size 的选择,分别是训练集和验证集两个
val_batch_size = 8
epoches = 100 # 100次迭代
lr = 0.01 # 学习率0.01
# 其他参数没有多加改动,因此不予列出
注:
- seed随机种子,是在训练多次的情况下,选择的相对最优解
- 在训练过程中,一般在70多次迭代的时候,Loss值已经趋近于稳定,但是为了控制变量,设置在100次迭代没有改变。
LR学习率优化
在学习率上,进行动态调整,使得网络更好的稳定:


将学习率动态调整后,即MILESTONES = [20, 50, 75],在迭代20次,50次,75次之后,分别将学习率降低0.1倍,使得Loss会变得更加稳定,且RMSE_112最终会稳定在2.4左右。


网络结构优化
在网上找到对PFLD网络结构的优化方案,即将Inverted Residual Block替换成Ghost Bottleneck,并细调网络结构。Ghost Bottleneck是以GhostModule为基础模块的,并使用后5层特征图进行拼接。网络结构如下:
Imput | Operator | channel | number | stride |
---|---|---|---|---|
112x112x3 | Conv3x3 | 64 | 1 | 2 |
56x56x64 | DW Conv3x3 | 64 | 1 | 1 |
56x56x64 | Ghost Bottleneck | 80 | 3 | 2 |
28x28x80 | Ghost Bottleneck | 96 | 3 | 2 |
14x14x96 | Ghost Bottleneck | 144 | 4 | 2 |
7x7x144 | Ghost Bottleneck | 16 | 1 | 1 |
7x7x16 | Conv3x3 | 32 | 1 | 1 |
7x7x32 | Conv3x3 | 128 | 1 | 1 |
(S1) 56x56x64 (S2) 28x28x80 (S3) 14x14x96 (S4) 7x7x144 (S5) 1x1x128 | AvgPool AvgPool AvgPool AvgPool - | 64 80 96 144 128 | 1 1 1 1 1 | - - - - - |
S1,S2,S3,S4,S5 | Full Connection | 136 | 1 | - |
使用如上的结构后,对网络有更好的性能:


可看到,相比图2,网络结构优化后使得Loss更低,并且RMSE_112能够达到1.98。
数据增强
数据增强是在数据集的基础上对样本图片进行一定的干扰,使得在预测时能够对干扰有一定的鲁棒性。
在增加了0.05概率的光度干扰后,模型的表现反而更加不稳定,我认为主要由于样本标签正确率不高,样本较少造成的。


因此最后仅仅使用网络结构优化后的模型。