比如:自己的数据集要识别6个人,最后一个全连接层的节点个数为6。训练的数据集路径下改成自己的数据集。
代码修改及解释有如下几处:
经过上面的修改后即可在自己的数据集上进行迁移学习的训练,保存最终训练的模型,其他地方的参数可以自行调整优化。
FaceNet源码使用方法主要参照转载自:
https://blog.csdn.net/u013044310/article/details/79556099
https://blog.csdn.net/u013044310/article/details/80481642
前提条件:已安装并配置好Tensorflow的运行环境。
第一步:准备facenet程序:
一、下载FaceNet源码。
下载地址:facenet源码
二、精简代码。作者在代码里实现了很多功能,但是对于初学者来说,很多代码是没有必要的,反倒找不到学习这个程序的入口。建议先精简一下代码,便于读懂代码:新建一个文件夹,取名:facenet,把源码中的src文件夹直接拷贝过来。
注:便于大家能够看懂代码,以上两步我已经完成,同时,自己运行程序之后,还对里边的代码做了详细的注释,如果想先了解facenet的源码,强烈建议下载这个,下载地址:facenet精简版
第二步:下载预训练模型。
facenet提供了两个预训练模型,分别是基于CASIA-WebFace和MS-Celeb-1M人脸库训练的,不过需要去谷歌网盘下载,这里给其中一个模型的百度网盘的链接:链接: 预训练模型百度网盘地址 密码: 12mh
下载完成后,把预训练模型的文件夹拷贝在刚才的文件夹里。用pycharm打开这个工程文件如图:
第三步:运行人脸比对程序(compare.py)。
facnet可以直接比对两个人脸经过它的网络映射之后的欧氏距离。
-1、在compare.py所在目录下放入要比对的文件1.png和2.png。
-2、运行compare.py文件,但是会报错如下:
这是因为这个程序需要输入参数,在上方的工具栏里选择Run>EditConfigurations ,
在Parameters中配置参数:20170512-110547 1.png 2.png。再次运行程序
可以看到,程序会算出这两个图片的差值矩阵
第四步:图片预处理——运行人脸对齐程序(align\align_dataset_mtcnn.py)。
我们可以下载LFW数据集用来测试这个程序,也为后边的训练函数做好数据准备。
下载链接:http://vis-www.cs.umass.edu/lfw/。下载后解压在data文件夹中。
因为程序中神经网络使用的是谷歌的“inception resnet v1”网络模型,这个模型的输入时160*160的图像,而我们下载的LFW数据集是250*250限像素的图像,所以需要进行图片的预处理。
在运行时需要输入的参数:
input_dir:输入图像的文件夹(E:\facenet\data\lfw E:\facenet\data\lfw)
output_dir:输出图像的文件夹(E:\facenet\data\lfw E:\facenet\data\lfw_160)
指定裁剪后图片的大小:--image_size 160 --margin 32 --random_order(如果不指定,默认的裁剪结果是182*182像素的)
比如我的是:E:\facenet\data\lfw E:\facenet\data\lfw_160 --image_size 160 --margin 32 --random_order
如果在pycharm中运行,按照同样的方法配置以上参数如下:
第五步:运行训练程序:(train_tripletloss.py)。
前边已经下载并处理好了LFW的数据集,现在,可以进行训练了。
运行之前,要在train_tripletloss.py中把加载数据的路径改成自己的数据集所在路径,如下图:
注:train_tripletloss.py和train_softmax.py的区别:这是作者对论文做出的一个延伸,除了使用facenet里提到的train_tripletloss三元组损失函数来训练,还实现了用softmax的训练方法来训练。当然,在样本量很小的情况下,用softmax训练会更容易收敛。但是,当训练集中包含大量的不同个体(超过10万)时,最后一层的softmax输出数量就会变得非常大,但是使用train_tripletloss的训练仍然可以正常工作。
最后,附上原来的文件中各py文件的作用(持续更新):
一、主要函数
align/ :用于人脸检测与人脸对齐的神经网络
facenet :用于人脸映射的神经网络
util/plot_learning_curves.m:这是用来在训练softmax模型的时候用matlab显示训练过程的程序
二、facenet/contributed/相关函数:
1、基于mtcnn与facenet的人脸聚类
代码:facenet/contributed/cluster.py(facenet/contributed/clustering.py实现了相似的功能,只是没有mtcnn进行检测这一步)
主要功能:
① 使用mtcnn进行人脸检测并对齐与裁剪
② 对裁剪的人脸使用facenet进行embedding
③ 对embedding的特征向量使用欧式距离进行聚类
2、基于mtcnn与facenet的人脸识别(输入单张图片判断这人是谁)
代码:facenet/contributed/predict.py
主要功能:
① 使用mtcnn进行人脸检测并对齐与裁剪
② 对裁剪的人脸使用facenet进行embedding
③ 执行predict.py进行人脸识别(需要训练好的svm模型)
3、以numpy数组的形式输出人脸聚类和图像标签
代码:facenet/contributed/export_embeddings.py
主要功能:
① 需要对数据进行对齐与裁剪做为输入数据
② 输出embeddings.npy;labels.npy;label_strings.npy
一、对模型进行测试:
用到的函数:validate_on_lfw.py
在pycharm中配置的参数如下:
数据集所在路径 模型所在路径
示例:
20170512-110547 1.png 2.png
这将执行以下四个操作:
a)加载模型。
b)加载和解析文本文件与图像对。
c)计算所有图像(以及它们的水平翻转版本)在测试集中的向量。
d)计算精度,验证率(@ FAR = -10e-3),曲线下面积(AUC)和等误差率(EER)等性能指标。
典型的输出如下:
Model directory: /home/david/models/20180402-114759/
Metagraph file: model-20180402-114759.meta
Checkpoint file: model-20180402-114759.ckpt-275
Runnning forward pass on LFW images
........................
Accuracy: 0.99650+-0.00252
Validation rate: 0.98367+-0.00948 @ FAR=0.00100
Area Under Curve (AUC): 1.000
Equal Error Rate (EER): 0.004
二、对预训练模型重新进行训练
有时候,我们需要用自己的数据集对预训练好的模型进行重新训练,或者之前训练了一个模型之后,觉得训练轮数不够,又不想从头开始训练,这样,在训练之前就要把之前训练的模型重新加载进去,方式如下:
第一步:添加预训练模型的参数:
在中train_tripletloss.py
找到这样一个语句:
改成这样:
parser.add_argument('--pretrained_model', type=str,
help='Load a pretrained model before training starts.',default='模型所在路径')
第二步:解决程序中的一个小bug
如果只是完成了第一步,运行程序会报错。经过调试,是因为程序有一个小的bug需要修复:
找到这一行程序:
可以看出,这一处函数的作用是:如果预训练模型这个参数非空,那么用tensorflow的saver.restore()函数重新加载模型参数,但是此处会报错,
那么我们模仿compare.py
函数中的加载模型方法,将这个函数改为:
facenet.load_model(args.pretrained_model)
然后运行程序,发现程序已经可以正常执行了。
如果不放心,可以取一个已经训练好的模型,加载之后训练一轮,会发现初始的损失函数非常小,同时,训练一轮之后模型的准确率已经和加载的预训练模型准确率差不多了,说明模型加载成功。
三、用自己的数据集结合SVM训练一个人脸识别系统
用到的代码:calssifier.py
,这个程序的基本原理是:通过用图像算出来的向量数据来训练一个SVM分类器,从而对人的身份进行一个判断,同时在.pkl格式的文件中存储每一个分类。这也是作者对于FaceNet程序应用的一个探索。
这个函数有两个模式,一个模式用来训练,另一个模式用来测试。具体功能如下:
模式= TRAIN:
模式= CLASSIFY:
执行本代码需要添加的参数以及各参数的含义:
配置参数示例:
TRAIN 图片数据所在文件夹 模型文件夹 标签文件.pkl
运行结果:
Number of classes: 6
Number of images: 48
Loading feature extraction model
Model directory: 20180606
Metagraph file: model-20180606-232113.meta
Checkpoint file: model-20180606-232113.ckpt-120120
Calculating features for images
Training classifier
Saved classifier model to file "E:/facenet/pick/classifier.pkl"
测试:
CLASSIFY 图片数据所在文件夹 模型文件夹 标签文件保存地址.pkl
运行结果:
Number of classes: 6
Number of images: 48
Loading feature extraction model
Model directory: 20180606
Metagraph file: model-20180606-232113.meta
Checkpoint file: model-20180606-232113.ckpt-120120
Calculating features for images
Testing classifier
Loaded classifier model from file "E:/facenet/pick/classifier.pkl"
0 F1: 0.471
1 F1: 0.672
2 F1: 0.685
3 F1: 0.700
4 F3: 0.633
5 F1: 0.556
6 F1: 0.555
7 F1: 0.696
8 F2: 0.827
9 F2: 0.775
…… ……
如果不需要在每次执行的过程中都配置这几个参数,可以对程序进行微调,找到原程序中的这几行代码:
改动如下(即将初始值配置在程序中,避免每次执行程序时都要输入对应的参数。如果参数有改动,只需要更改程序对应部位即可。):
parser.add_argument('--mode', type=str, choices=['TRAIN', 'CLASSIFY'],
help='Indicates if a new classifier should be trained or a classification ' +
'model should be used for classification', default='CLASSIFY')#这里更改模式
parser.add_argument('--data_dir', type=str,
help='Path to the data directory containing aligned LFW face patches.',default='TF1_classify')#添加自己的数据文件夹
parser.add_argument('--model', type=str,
help='Could be either a directory containing the meta_file and ckpt_file or a model protobuf (.pb) file',default='20180606')#预训练模型
parser.add_argument('--classifier_filename',
help='Classifier model file name as a pickle (.pkl) file. ' +
'For training this is the output and for classification this is an input.',default='pick/classifier.pkl')#.pkl文件存储的位置
附录:程序中的一些小改进:
一)用GPU训练模型。
原程序默认使用CPU训练,但是这样训练的速度太慢,如果你电脑恰好有一块不错的GPU,或者实验室里有GPU服务器,那么配置好GPU环境之后(包括cuda,TensorFlow-gpu等),可以在程序中添加代码如下:
import OS
os.environ["CUDA_VISIBLE_DEVICES"] = '0'#如果有多块显卡,可以指定第几块显卡,0即为第一块显卡。
这样,程序在执行过程中就优先调用GPU训练模型了。
二)微调损失函数
在《In Defense of the Triplet Loss for Person Re-Identification》这篇论文中提到:损失函数中去掉平方后效果还会更好一些,如下图:
如果有需要的话,可以改成开方的形式,在facenet.py
下的triplet_loss
函数中,找到如下两句代码:
改成:
pos_dist = tf.sqrt(tf.reduce_sum(tf.square(tf.subtract(anchor, positive)), 1)) # tf.square:平方。tf.subtract::减法
neg_dist = tf.sqrt(tf.reduce_sum(tf.square(tf.subtract(anchor, negative)), 1))
即加上一个开方运算。(目前正在测试效果,以后补充……)