跑通FaceNet人脸识别
原创:杨其泓
一、前言
FaceNet是一个十分经典的人脸识别模型,并且具有较好的性能,但要实现使用自己的数据进行人脸识别,还需要对模型进行重新训练。本文将介绍跑通一个简单FaceNet的全部流程,以及踩坑记录。
二、方案技术路线
1. 人脸检测:使用 Dlib 中预先训练的模型检测面部;
2. 人脸校准:使用 Dlib 的实时姿势估计与 OpenCV 的仿射变换来尝试使眼睛和下唇在每个图像上出现在相同位置;
3. 卷积网络:使用深度神经网络把人脸图片映射为 128 维单位超球面上的一个点;
4. 分类:使用三元损失函数对每张图片对应的超球面的点(128维向量)比较相似度以此进行分类。
图片来源:https://cmusatyalab.github.io/openface/
三、参考
3.1 论文
FaceNet: A Unified Embedding for Face Recognition and Clustering.
论文链接:https://arxiv.org/abs/1503.03832
3.2 代码
Github代码链接:https://github.com/foamliu/FaceNet
3.3 数据
训练集
CelebFaces Attributes Dataset (CelebA) 是一个大型的人脸数据集,有10,177个身份和202,599张人脸图像。
数据链接:
http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html
Celeba数据集是香港中文大学的公开数据集,所以支持百度网盘下载!选择Baidu Drive即可将数据集转存至自己的网盘,Celeba数据集大约有22个G(解压后24左右),所以最好要弄个百度网盘SVIP账号再下。
TODO:数据集详细介绍
测试集
LFW (Labled Faces in the Wild)人脸数据集:是目前人脸识别的常用测试集,共有13233张图像,每张图像均给出对应的人名,共有5749人,尺寸为250X250。
数据官网链接:
http://vis-www.cs.umass.edu/lfw/
(官网链接有详细的数据说明及介绍,看不懂数据是啥的可以来看这个)
数据直接下载链接:
http://vis-www.cs.umass.edu/lfw/lfw-funneled.tgz
(直接下载链接是下下来就能用来跑的)
Linux中直接下载:
$ wget http://vis-www.cs.umass.edu/lfw/lfw-funneled.tgz
$ tar -xvf lfw-funneled.tgz
$ wget http://vis-www.cs.umass.edu/lfw/pairs.txt
$ wget http://vis-www.cs.umass.edu/lfw/people.txt
其实执行前两行就行,后两个文件在Github的文件夹中有了已经。
如果是在自己电脑本地下载验证数据集,可以看到它里面分了好几个文件,里面不光有图片,还有好几个label的txt,但是label文件其实在Github上的data里有了,所以其实用到的就是那个图片。
TODO:数据集详细介绍
3.4 模型权重
Dlib人脸校准模型
shape_predictor_5_face_landmarks.dat.bz2链接:
http://dlib.net/files/shape_predictor_5_face_landmarks.dat.bz2
本项目的人脸检测部分直接使用DLIB来实现,并且通过DLIB的关键点来进行人脸平面对齐,所以需要下载相关模型权重。
FaceNet 人脸识别模型
model.10-0.0156.hdf5链接:
https://github.com/foamliu/FaceNet/releases/download/v1.0/model.10-0.0156.hdf5
这个是作者训练好的一个模型,可以直接用来预测。(模型保存的名称是epoch数-损失,但在后续训练过程中,大家可能会发现在第10个epoch不可能训练到这样的精度,我认为可能是训练中断后重新训练,但是没有调整初始计数造成的)
3.5 配置环境
硬件环境:
系统:Linux
4个RTX2080ti GPU,显存12G(训练使用了俩,只用一个的话会OOM内存溢出)
主要依赖项:
TensorFlow-gpu==1.13.1
Keras==2.2.4
OpenCV-python==4.2.0.32
Dlib==1.19.0
四、代码运行
4.1 前期准备
数据:
将两套数据文件解压后放在data文件夹中,data文件夹如下图所示:
模型:
将两个模型权重放在models文件夹中(因为训练的时候存了很多权重这里就不截图了)。
4.2 数据预处理
进入FaceNet-master/目录,运行:
$ python pre-process.py
这里主要是通过DLIB对Celeba数据进行预处理,包括人脸检测、人脸5特征点检测、人脸对齐等。总共 202,599张人脸图像中,5600张无法被 dlib 标定。因此 202599 - 5600 = 196999 张被用于训练。大约耗时15-20分钟左右。
4.3 模型训练
运行:
$ python train.py
4.4 模型评估
运行:
$ python lfw_eval.py
使用LFW数据集对模型进行验证,输出结果为ROC曲线面积。
五、踩坑记录
5.1 DLIB安装
DLIB可能需要如下前置配置(根据系统差异配置可能有所不同):
X11(xquartz)
Cmake
前置配置安装完成后,一般可用pip install dlib直接安装。
5.2 Keras预训练权重下载
此套代码的核心模型是keras自带的inception_resnet_v2,:
这个代码在执行的时候,会自动去下载这个模型的预训练权重(keras自己公布的几个重点模型的预训练权重),文件大小大约在209M,但是下载会很卡,解决方法是重新下,不停的重新下,或者尝试科学上网,这个在我的mac上极其有效,如果还是下不下来,可以在百度找这个模型权重的文件手动下载,百度上说下载完成后保存在.models文件中(mac或linux中.开头的文件应该是隐藏文件),但是我找了好久都没有找到这个文件夹,后来才发现,他是在我的根目录里!而不是site-packges下面的keras里:
找到位置,下对文件,放进来就ok了。
5.3 get_random_triplets函数
data_generator.py文件中使用了函数get_random_triplets():
但是,在utils.py文件中定义的这个函数并没有输入项,
所以那个括号里的‘train’是多余的,需要删除,要不会报错。
5.4 np.array大计算量报错
我的环境里,最开始keras的版本是2.1.5(好像是,反正是2.1开头的),在进行训练时,报:
StopIteration: ufunc 'true_divide' output (typecode 'd') could not be coerced to provided output parameter (typecode 'B') according to the casting rule ''same_kind''
这个错误的本质是:在对numpy的dnarray数组使用类似+=的运算符时候(比如y+=c),如果数组复杂到一定程度,这样的自加运算就会报如上的错。解决方案就是不使用+=等写法(不使用数学符号的写法),转而使用add等api,但是程序报这个错的具体位置是在keras里,有一行使用了/=,所以这里使用上述修改方法是不稳妥的,毕竟是要改keras源码,一是有大量的此类计算符的话难以处理,二是还要做备份,所以这条路暂时走不通,继而我想到,提高keras版本是否能解决此问题,高本版的keras是否对此进行了优化,结果是,是的,提高版本即可(但要注意keras和TensorFlow的版本匹配关系,不能过高)。
5.5 OOM显存溢出
这个Facenet使用的是keras内部自带的inception_resnet_v2网络,共计5000多万个可训练参数,输入图片大小为139*139,默认batchsize为128,因此训练这个模型的显存占用是极大的,我的显卡是12G显存,实际能用的是10个g多一点,只使用一块卡的话会直接报显存溢出OOM,我还尝试过两块卡,batchsize为256,依然溢出,所以应该视情况对batchsize进行控制,或者考虑换用其他模型(这个其实我一上来就想换过,毕竟是直接走keras调用的,但是由于调用的地方很多以及需要模型指定层的输出,所以还不能随便替换,需要提前去了解一下其他模型的及具体结构)。
5.6 数据缺失
我一开始下载了Celeba数据,就开始尝试进行模型训练了,但是后来报了‘nonetype object has no attribute’,经过判断我发现是image为空,也就是没有读到数据,后来发现:这套github的代码使用了Celeba数据及LFW数据,前者用来训练,候着用来验证及出精确度,但是并不是按常用的方法将训练集拆分成一定比例进行训练、验证,用单独的数据及测试,而是直接调用训练集进行训练,在每个epoch的出验证集损失的时候直接调用LFW数据(可以通过看fit_generator的参数来证明这点),并在进行模型评估的时候只使用LFW数据,这也就意味着,必须同时下好这两套数据才能顺利进行训练。
5.7 Accuracy计算
模型评估代码的最后,会自动打印模型accuracy,我第一反应是常规上理解的accuracy,也就是PP=TP/(TP+FP),然而,这里虽然print写的是accuracy,但实际上计算的是ROC曲线下的面积,也就是AUC。计算关键部位代码如下:
这几个评价指标的区别到底是什么呢?
图片来源:https://www.deeplearn.me/1522.html
如图所示,正确率的计算公式就是TP/(TP+FP);
而ROC曲线是由tpr=TP/(TP+FN)和fpr=FN/(TP+N)计算而来的。
ROC曲线的特性在于:当测试集中的正负样本的分布变化的时候,ROC 曲线能够保持不变。