级联MobileNet-V2实现CelebA人脸关键点检测(转)

级联MobileNet-V2实现CelebA人脸关键点检测(附训练源码)

偶然看到一篇关于用MobileNet-V2做人脸关键点检测的文章,所以就转载了。
原作者链接:https://blog.csdn.net/u011995719/article/details/79435615
Github:https://github.com/TingsongYu/cascaded_mobilenet-v2


此博客详细介绍级联MobileNet-V2实现人脸关键点检测。
模型:MobileNet-V2
数据:CelebA( http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html )
框架:Caffe
系统:ubuntu16.04
GPU:GTX1080
实验结果: 单模型不到1M,仅956KB,GTX1080上仅6ms,简单部署即可在移动端进行实时检测。
本实验代码: https://github.com/tensor-yu/cascaded_mobilenet-v2
(仅想实现demo,请直接跳到2.0修改caffe,然后进入2.5级联展示)

一 、引言

1.1为什么是级联?

近几年人脸关键点检测的方法大都采用从粗到精(coarse-to-fine)的思想,在CNN上体现为级联。级联之所以好,是因为后一级的输入是经过上一级“筛选”后的区域,即达到一个取其精华去其糟粕的过程。比如,在一张大图里要看清一个人的左眼珠,第一步应该是在图中找到这个人,第二步看他的眼睛,第三步才是看到他的左眼珠.

不熟悉级联CNN的,可阅读几篇经典的级联CNN的论文

	1. Deep Convolutional Network Cascade for Facial Point Detection 
	2.Extensive Facial Landmark Localization with Coarse-to-Fine Convolutional Network Cascade
	3.Facial Landmark Detection by Deep Multi-task Learning
	4.Facial Landmark Detection with Tweaked Convolutional Neural Networks
	5. Joint Face Detection and Alignment Using Multitask Cascaded Convolutional Networks

也可查看博客了解:深度学习人脸关键点检测方法----综述

1.2为什么是MobileNet-V2?

为了获得更好的性能,近些年设计的CNN更深,更复杂,这样明显阻碍了模型的部署应用。而手机端的应用越来越广泛,因此,一种轻量化,高效率的网络——MobileNet,应运而生。MobileNets是Google针对移动端而设计的一种轻量化,高效率的网络模型。MobileNet-V1参考博客。据说,MobileNet-V1的工作很早就做了,当时小组里没人在意,等过了一段时间发现MobileNet-V1的坑还没有人占,就投了出来。这也是为什么在残差概念如此火爆的16,17年里,出现了一个没有使用残差概念的MobileNet-V1。
MobileNet-V2——紧跟时代的MobileNet
2018年1月底,Google团队将MobileNet-V1的升级版——MobileNet-V2挂到了arXiv上(https://arxiv.org/abs/1801.04381),主要改进是采用了残差思想,但由于采用depth-wise convolution,所以不能直接采用传统的残差结构,而是稍稍做了改变,用的是, Inverted residuals,并且在residual block的Eltwise sum之前的那个 1*1 Conv 不再采用Relu。MobileNet-V2博客

二、 级联MobileNet-V2之人脸关键点检测

2.0 修改caffe

进行实验前,请先配置支持MobileNet及多标签的caffe
本实验基于MobileNet-V2,因此需要给caffe添加新的layer,即depth-wise convolution,并且需要修改image_data_layer,使得其支持多标签输入
(感谢 hpp,cpp,cu,prototxt提供者:suzhenghang github地址)

具体步骤,进入caffe_need/文件夹下,

  1. 将image_data_layer.hpp 替换掉 caffe_path/include/caffe/layers 下的 image_data_layer.hpp
  2. 将conv_dw_layer.hpp 复制到 caffe_path/include/caffe/layers 下
  3. 将image_data_layer.cpp 替换掉 caffe_path/src/caffe/layers 下的image_data_layer.cpp
  4. 将conv_dw_layer.cu
    conv_dw_layer.cpp 复制到 caffe_path/src/caffe/layers 下

重新编译,并且配置python接口

2.1 整体框架及思路

本实验采用两级级联,level_1只是初步的“剔除”非人脸部分区域,保留人脸区域供level_2进行检测,从而获得更精确的检测结果。其实还可以继续做第三级,即类似DCNN(2013.sun)那样,把眼睛,鼻子,嘴巴都裁剪出来,单独用一个网络去检测,这样的效果肯定优于本实验,只不过级联的训练实在是太繁琐了!一个人真心搞不过来。
实验流程如下图所示:
这里写图片描述

第一行是level_1,输入为原始数据,level_1输出的landmark主要作用是用来剪裁,本实验采用的剪裁策略很“拙劣”,希望有人给出更好的裁剪策略。具体如何裁剪稍后讲解。

第二行是level_2,输入的输出为level_1裁剪出来之后的图像,可以看到,level_2的输入已经“剔除”了大量无关信息——背景,身体躯干等等。level_2输出的点是相对于level_1 crop这张图的,因此还需要记住level_1裁剪的信息,才可以返回原始数据进行关键点标注。

简单介绍训练代码结构。训练代码共计三部分,分别是0_raw_data, 1_level_1, 2_level_2
每一部分里面均包含三个文件夹——Code ,Data ,Result

2.2 原始数据处理 0_raw_data

训练数据采用CelebA,官网:http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html
百度网盘下载:https://pan.baidu.com/s/1eSNpdRG#list/path=%2F

第一步
将CelebA\Img\img_celeba 复制到 0_raw_data/Data/ 下面,
将CelebA\Anno\list_landmarks_celeba.txt复制到 0_raw_data/Data/ 并且重命名为celeba_label.txt
这里写图片描述

第二步
进入0_raw_data/, 运行divide_tr_te.py,将会划分好训练集,测试集,并且保存在0_raw_data/Result/ 下面
这里写图片描述

第三步
运行 draw_point.py,将会在 0_raw_data/Result/draw_img/下获得 打上关键点的图片,用来检查图片以及标签是否正确
这里写图片描述

来感受一下我们的训练数据都是什么样子的
列表内容
可以发现,虽然是人脸数据,但是图片并不想LFW,ALFW那么简单,这里有不同的角度,表情,场景,以及人脸所占图片区域大小也不尽相同(这点正是级联所要解决的问题)

划分好数据集,就可以进入level_1了

2.3 level_1训练

同样的,1_level_1文件夹下包含三个文件夹,分别是Code,Data,Result
进入Code里面看看:
这里写图片描述

又有5个文件夹,是不是要晕了? 没办法,级联就是那么繁琐!
现在一个文件夹一个文件夹的来,不着急。0_raw_data中我们只是划分好了数据,但是真正能够喂给level_1的数据还没有准备好,所以第一步就是生成能喂给level_1的数据。

进入0_gen_data,运行 gen_l1_data.py,则会在1_level_1/Data下面生成 四个东西:
这里写图片描述

由于强迫症,以及避免生成数据时候把数据搞错了,这里进行 1_draw_img,即用1_level_1/Data下面的数据将关键点打出来,看一下有没有弄错。

进入1_draw_img/ 运行draw_l1_point.py ,即可在1_level_1/Data/下获得一个文件夹draw_img ,进去看一看,没问题的话,那就可以进行训练了。

进入 2_train/,里面包含l1_mobilenet.prototxt, solver.prototxt,train.sh
请进入train.sh,更改caffe路径/your_caffe_path/build/tools/caffe ,确保你的caffe能用

部分参数设置:loss_weight 我设置为100,80000次迭代后就下降学习率,100000次再次下降学习率,经过多次实验发现第二次下降学习率之后,会出现过拟合问题,所以我的模型均采用 100000次迭代时得到的模型。(若发现有更好的solver,请贴出来分享,谢谢!)

MobileNet-V2 网络结构如下:

InputOperatortcns
48x48x3conv2d-1612
24x24x16bottleneck62422
12x12x24bottleneck63222
6x6x32bottleneck66422
3x3x64fc-256--
1x1x256fc-10--

lr=0.01, 8万次下降一次,10万次下降一次train和test的loss曲线分别为:
这里写图片描述
这里写图片描述

Train loss大概是在 0.005~0.006, Test loss是在 0.007~0.008。 过拟合不是很严重(PS,训练的时候loss_weighs设置为100,因此数量级上有些许出入 )
训练完毕,caffemode会保存在1_level_1/Result/solver_state/下面,接着可以做inference。

进入3_inference/,运行 打开inference.py,修改caffe的路径(并确保你的caffe配置了python接口)
运行 inference.py,即可获得level_1对所有图片的关键点预测值,并保存在Result/下,以供后面评估以及裁剪所需。

有了预测,就可以看看误差有多大,一开始采用双眼距离作为归一化因子,即: e r r = d e u c d i p d e r r = d e u c d i p d e r r = d i p d ​ d e u c ​ ​ err=deucdipderr=\frac{d_{euc}}{d_{ipd}}err=dipd​deuc​​ err=deucdipderr=dipddeucerr=dipddeuc

,其中deuc表示输出landmark与真实landmark的欧氏距离,dipd表示两眼之间的欧氏距离。可是在计算的时候,出现了inf!几经周折,才找到问题所在!之前采用两眼距离作为归一化因子的那些实验,他们的数据集一定一定是比较正的人脸,绝对有没有以下这种情况:
这里写图片描述

其实就是侧脸,两只眼睛在同一个位置,晕~

于是乎将归一化因子改为图片的对角线长度,开始计算吧~
进入4_evaluate/

在这里不仅计算误差,并且还将level_1预测的关键点打到了图片上,用来观察效果如何,这部分图片保存在1_level_1/Result/l1_out_draw/ 下面
并且设置了一个阈值(这里设置为10%),误差大于该阈值的那部分图片,统统舍弃,因为这些图片已经不能够为level_2所用。这部分图片保存在 1_level_1/Result/l1_drop/ 下面

分别运行 evaluate_test.py 和evaluate_train.py,
可获得5个关键点的平均误差,以及所有图片平均误差的分布图。以test为例,5个点的平均误差分别是:
这里写图片描述

所有图片的平均误差分布为:
这里写图片描述

可以看到,99%的误差是小于10%的,平均误差为1.17%
为什么要画这个分布图的?有了这个分布图,可以更好的选择一个更好的阈值,用来确定哪些图片需要裁剪,哪些图片需要丢弃。

至此,level_1的训练结束,接下来要为level_2准备数据,这里一并放在level_1里边做。
进入文件夹 5_crop_img/, 分别执行crop_train_img.py 和 crop_test_img.py即可。

这里要说一下,裁剪策略,由于CelebA的图片奇奇怪怪,不像LFW,ALFW那样正,所以没想出个什么好的裁剪策略,乱想了一个,就是以level_1预测到的鼻子为中心,裁剪出一个正方形,这个正方形的边长为四倍鼻子到左眼的距离。举个例子:
下图绿色点即为level_1的预测点,以鼻子为中心,裁剪出一个正方形, 红框所示。裁剪出来的图片作为level_2的输入。

这里写图片描述

运行完 5_crop_img/ level_1 就结束了。

这个solver跑出来的模型在这批数据集上,并不尽人意,来看了一下预测结果较差的图片,大概是这样的:

这里写图片描述

各种千奇百怪的人脸姿态,很多是横躺着的。。。

2.4 level_2训练

有了level_1/5_crop_img/得出来的数据, level_2不需要再进行生成数据这一步了。可依次执行: 0_train, 1_inference, 2_evaluate

这里,有必要看一下train loss和test loss曲线:
这里写图片描述
这里写图片描述

其实并没有收敛,但这不是重点,重点是train和test的曲线都出现了很大的浮动,有尖点存在。这是为什么呢? 其实是因为level_1剪裁出了问题,在裁剪时,将误差小于10%的图片保留,用以裁剪。其实这个阈值(10%)还是太大了,以至于裁剪出这一些“不合格”图片。示例:
这里写图片描述

2.5 级联展示[ 3_demo ]

有了level_1和level_2的caffemodel,就可以跑级联了,具体可参见 3_demo/Code/ inferencen.py,由于是demo,这里只测试几张图片,若想测试整个CelebA数据集,请修改
inferencen.py当中的两个参数: raw_txt = ‘…/Data/demo.txt’ 和 relative_path = ‘…/Data/img/’

demo结果:
这里写图片描述
这里写图片描述
这里写图片描述

其中,绿色点为真实landmark,红色点为level_1的预测,蓝色点为level_2的预测。可以看到蓝色点比红色点更靠近绿色点(好绕口。。。)

#三 总结
本实验是对级联的一个粗略实现,通过本实验可深刻了解到级联是多么的繁琐,虽然繁琐,但是级联的效果还是很明显的。实验初步验证了MobileNet-V2是短小精干的模型,并且可在移动端实现实时检测。

本实验还有一些不足之处,大家可以做相应的修改

  1. 模型训练不足,未完全收敛,可对solver进行修改,获得更好的模型。
  2. level_1的裁剪策略相对“拙劣”,可依据具体应用场景,提出不同的裁剪策略,确保输入到level_2的图片包含整个人脸。

其实本实验还有一个问题是,未采用人脸检测步骤。通常的人脸关键点检测,会涉及人脸检测这一步,将人脸检测得到的人脸图片作为输入,其实这也是一从粗到精的过程。通过人脸检测器,可以剔除不必要的信息。而在本实验中,省略掉了人脸检测这一步,而level_1的作用恰恰相当于人脸检测!因此,在特定场景下,是否可以考虑省略人脸检测这一步(毕竟检测需要耗时),而采用level_1进行人脸的初步定位?从而提升检测速度

这里要说的特定场景是指图片中仅有一个人脸的情况,否者此法失效。 有不少场景还是有且仅有一个人脸的,因此针对这种场景可以采用level_1作为人脸检测器。

针对级联还有很多idea想去实现,但限于数据(68点数据不足),人力(一个人)原因,搁置了。

转载请注明出处:
http://blog.csdn.net/u011995719/article/details/79435615

                                </div>
            <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-e9f16cbbc2.css" rel="stylesheet">
                </div>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值