基于Tensorflow框架的人脸匹配总结附代码(持续更新)

   基于Tensorflow框架的人脸检测总结附代码(持续更新)
   基于Tensorflow框架的人脸对齐、人脸关键点检测总结附代码(持续更新)
   基于Tensorflow框架的人脸活体检测、人脸属性总结附代码(持续更新)

  最近利用人脸开源数据库WIDER FACE、64_CASIA-FaceV5、CelebA、300W_LP以及自己做的一个近200张照片的私人数据集复现了人脸检测、人脸匹配、人脸对齐、人脸关键点检测、活体检测、人脸属性等功能,并且将其集成在微信小程序中,以上所有内容将用大概5篇博客来总结。所有数据集下载链接附在下方,所有完整代码附在GitHub链接

WIDER FACE 数据集,包括32203个图像和393703个人脸图像,其中在尺度、姿势、遮挡、表情、装扮、光照等均有所不同。

LFW数据集,是目前人脸识别的常用测试集,其中提供的人脸图片均来源于生活中的自然场景,因此识别难度会增大,尤其由于多姿态、光照、表情、年龄、遮挡等因素影响导致即使同一人的照片差别也很大。并且有些照片中可能不止一个人脸出现,对这些多人脸图像仅选择中心坐标的人脸作为目标,其他区域的视为背景干扰。LFW数据集共有13233张人脸图像,每张图像均给出对应的人名,共有5749人,且绝大部分人仅有一张图片。每张图片的尺寸为250X250,绝大部分为彩色图像,但也存在少许黑白人脸图片。

64_CASIA-FaceV5 该数据集包含了来自500个人的2500张亚洲人脸图片。

CelebA 包含10,177个名人身份的202,599张人脸图片,每张图片都做好了特征标记,包含人脸bbox标注框、5个人脸特征点坐标以及40个属性标记,广泛用于人脸相关的计算机视觉训练任务,可用于人脸属性标识训练、人脸检测训练以及landmark标记等。

300W_LP 该数据集是3DDFA团队基于现有的AFW,IBUG,HEPEP,FLWP等2D人脸对齐数据集,通过3DMM拟合得到的3DMM标注,并对姿态,光照,色彩等进行变化以及对原始图像进行flip(镜像)得到的一个大姿态3D人脸对齐数据集。

1. 人脸匹配业务介绍

  首先人脸匹配是判断任意给出的两张人脸图像中的人脸是否属于同一个人。而人脸识别是基于人的脸部特征信息进行身份识别的一种生物识别技术,通过任意给出一张人脸图像,与数据库人脸比对,最后给出判断结果。人脸匹配可用于人脸验证、人脸识别、人脸检索三种常用模式。

  人脸验证: 其辨别模式为1 :1模式,即判断 ”你是不是×××?“ 的问题。常用于身份核实,手机、设备验证等情况,由用户直接提供需要比对的两个人脸。

  人脸识别:其辨别模式为1 :N模式,即判断“你是不是***的×××?”的问题。常用于数据比对,如公司门禁等情况,由数据采集设备获取人脸图像之后与已有数据库做比对,从而做出判断。

  人脸检索:其辨别模式为M :N(1 :N),即判断 “ 你们(你)是不是属于×××的?” 最终结果取排名第一的结果,设置阈值来判断两幅人脸图像中人脸特征相似度最高的。通常使用检索的数据库人脸数据多,个体差异大,识别难度较大。

人脸匹配算法性能评价指标

  人脸验证:ROC曲线、PR曲线

  ROC 曲线:常用于二分类问题中的模型比较,主要表现为一种真正例率 (TPR) 和假正例率 (FPR) 的权衡。具体方法是在不同的分类阈值 (threshold) 设定下分别以TPR和FPR为纵、横轴作图。

  PR 曲线:展示的是Precision vs Recall的曲线,PR曲线与ROC曲线的相同点是都采用了TPR (Recall),都可以用AUC来衡量分类器的效果。不同点是ROC曲线使用了FPR,而PR曲线使用了Precision,因此PR曲线的两个指标都聚焦于正例。类别不平衡问题中由于主要关心正例,所以在此情况下PR曲线被广泛认为优于ROC曲线。

  机器学习之类别不平衡问题 (2) —— ROC和PR曲线

  人脸识别:CMC曲线

  CMC曲线综合反映了分类器的性能,利用Top1 err以及Top5 err评价指标,其中 Rank1识别率=1-Top1 err,Rank5识别率=1-top5 err
在这里插入图片描述

  模式识别分类器评价指标之CMC曲线

在这里插入图片描述

2. 人脸匹配方法介绍

  特征表示:是寻求图像中有效信息的方法,而有效信息称为特征,特征包括低层次特征(纹理、颜色、形状)、中层次特征(融入监督信息、类别学习等)以及高层次(抽象)特征;

  纹理:简单可以理解为直观感受下的一张图片,此外也存在(1D,3D的纹理),LBP(Local Binary Patterns,局部二值模式)是提取局部特征作为判别依据的,一种有效的纹理描述算子,度量和提取图像局部的纹理信息,它具有旋转不变性和灰度不变性等显著的优点,对光照具有不变性。用于纹理特征提取。而且,提取的特征是图像的局部的纹理特征。有多种改进型,LBP结合BP神经网络已经用于人脸识别等领域,详解见链接

  纹理那些事儿

   特征提取算法(1)——纹理特征提取算法LBP

  颜色:图像中存在很多像素点,这些像素点构成了一个数字矩阵也称为颜色矩阵,描述了当前图片中的颜色信息,彩色图像中有R、G、B三个颜色通道,以此每一个像素点可抽象为一个三维向量,进一步可提取出图像的颜色直方图等颜色特征。通过对颜色矩阵做差分运算,可得到其对应的纹理特征。

  形状:形状是对纹理特征进行深一步的分析,是对图像中的结构信息进行分析,不同种类的个体结构信息差异大,同一种类下的不同个体结构信息差异小,利用此种特征信息可以区别要素特征与背景,以及多分类特征问题。

  如何进行特征表示的优化学习?

  PCA:属于无监督方式,也称为主成分分析,常用于高维数据的降维操作,也用来做降噪处理。PCA详解见链接  【机器学习】降维——PCA(非常详细)

  LDA:属于监督方式,其思路是将数据投影到低维空间,使得同一类数据尽可能紧凑,不同类数据尽可能分散,其详解解释推导见链接 机器学习-LDA(线性判别降维算法)

  迁移学习:迁移学习主要是将某个领域或任务上学习到的知识或者模式应用到不同但相关的邻域问题中,具体详解见链接 迁移学习概述(Transfer Learning)

  稀疏表示:用较少的基本信号的线性组合来表达大部分或者全部的原始信号。其中,这些基本信号被称作原子,是从过完备字典中选出来的;而过完备字典则是由个数超过信号维数的原子聚集而来的。可见,任一信号在不同的原子组下有不同的稀疏表示, 稀疏表示(Sparse Representations)

  低秩学习:矩阵表达的是结构性信息,例如图像、用户-商品推荐表等等,那么矩阵各行之间存在这一定的相关性,那这个矩阵一般就是低秩的,更加详细推导见链接: 机器学习——低秩矩阵分解中低秩的意义、矩阵填补、交叉验证

  哈希学习:哈希学习(learning to hash)是通过机器学习机制将数据映射成二进制串的形式,能显著减少数据的存储和通信开销,从而有效提高学习系统的效率,详解链接为: 机器学习中的哈希学习总结(learning to hash)

  深度学习:深度学习中通过构建网络结构 以及在之后添加 FC层用以表征特征向量。

  相似性度量

  在做分类任务的时候,经常需要估算不同样本之间的相似性度量(Similarity Measurement),这时通常采用的方法就是计算样本间的“距离”(Distance)。在面对上述类似1 :1情况时候通常采用 分类模型或者度量模型,而处理1 :N情况时候通常使用度量模型。

  欧式距离:源自N维欧氏空间中两点间的距离,推导见下述链接,常见于Softmax、SiameseNet、TripletNet等等。

  余弦距离:严格来说余弦距离是指相似性,其他距离直接测量两个高维空间上的点的距离,如果距离为0则两个点“相同”;余弦的结果为在[0,1]之中,如果为 1,只能确定两者完全相关、完全相似。常用于Sphereface、Normface、AM-Softmax、CosFace、ArcFace等等。

  马氏距离:马氏距离又称为数据的协方差距离,它是一种有效的计算两个未知样本集的相似度的方法。马氏距离的结果也是将数据投影到N(0,1)区间并求其欧式距离,与标准化欧氏距离不同的是它认为各个维度之间不是独立分布的,所以马氏距离考虑到各种特性之间的联系。

[机器学习-概念] 什么是欧式距离、标准化欧式距离、马氏距离、余弦距离

  度量优化问题Metric Learning——度量学习

  深度学习方法:分类问题、相似性(Contrastive Loss, Triplet Loss)、重排序(Rerank)

在这里插入图片描述

3. 人脸匹配问题挑战以及解决思路

  人脸匹配面临的问题有人脸表情复杂、人脸随年龄变化、人脸特征被部分掩盖、人脸有易变化的附加物等问题,具体解决思路出发点应该首先对数据进行特异化处理,扩充数据,如增添同张人脸的不同表情图像,识别年龄无变化的人脸用分类即可,而对年龄变化的人脸识别方法是通过年龄模拟,将测试图像和查询库中的图像变换到某一共同的年年龄,从而去除年龄不同的影响,使识别在年龄相同的人脸图像进行。此外,将人脸数据针对需求进行分类整理,选择合理化的模型,以及对算法进行一定程度的优化,譬如可以对人脸数据进行分块处理,三维构建找寻关键点等方式。

  人脸识别长篇研究
  目前人脸识别技术的挑战是什么?
  人脸识别人脸验证
  【概念介绍】人脸匹配
  人脸识别技术介绍和表情识别最新研究

4. TensorFlow + FaceNet模型以及优化

  FaceNet算法 = MTCNN模型 + FaceNet模型,其算法理论为MTCNN人脸检测与对其模型以及FaceNet人脸比对模型,FaceNet论文实验结果:主要是利用相同人脸在不同角度等姿态的照片下有高内聚性、不同人脸有低耦合性,提出的一种人脸识别方法,在LFW数据集上准确度达到99.63%,在 youtube人脸数据集上准确度 95.12%,比以往准确度提升了将近30%。

  具体过程为:

  1.通过MTCNN人脸检测模型,从照片中提取人脸图像。
  2.把人脸图像输入到FaceNet,计算Embedding的特征向量。
  3.比较特征向量间的欧式距离,判断是否为同一人,例如当特征距离小于1的时候认为是同一个人,特征距离大于1的时候认为是不同人。
  4.使用深度卷积神经网络,然后采用L2归一化操作,得到人脸图像的特征表示,最后为三元组(Triplet Loss)的损失函数。

FaceNet模型
Triplet Loss

Triplet Loss是一个三分枝的loss,Anchor为一个参照,判别使同张人脸距离小于不同人脸距离。在计算loss时候使用OHEM难例挖掘策略,也常用于目标检测中,尽可能减少网络崩塌的情况发生。

谷歌人脸识别系统FaceNet解析
算法—FaceNet理论学习篇

人脸匹配数据准备

本例使用LFW,CASIA-faceV5数据集,首先利用下述的代码流程对其进行处理。

方法一:使用 dlib 库用来进行人脸检测 + 关键点定位,之后根据关键点或者人脸检测结果提取人脸图像。
其关键代码如下:

import numpy as np
import cv2
import dlib
import glob
import os

im_floder = "E:/Python/facenet-master/300w-lp-dataset/64_CASIA-FaceV5"
crop_im_path = "E:/Python/facenet-master/300w-lp-dataset/64_CASIA-FaceV5/crop_image_160"

#读取大文件夹下面所有500个小文件夹
im_floder_list = glob.glob(im_floder + "/*")

#通过dlib定义一个检测器
detector = dlib.get_frontal_face_detector()

idx = 0
#遍历每个小文件夹下所有图片
for idx_floder in im_floder_list:
    im_items_list = glob.glob(idx_floder  + "/*")

    #针对不同人创建不同的文件夹
    if not os.path.exists("{}/{}".format(crop_im_path, idx)):
        os.mkdir("{}/{}".format(crop_im_path, idx))

    idx_im = 0
    #遍历每张图片,预测人脸框的位置
    for im_path in im_items_list:
       # print(im_path)
        i_data = cv2.imread(im_path)
        im_data = cv2.cvtColor(i_data, cv2.COLOR_BGR2GRAY)

        dets = detector(im_data, 1)
        #打印人脸框
        print(dets)
        #判断是否检测到人脸框
        if dets.__len__() == 0:
            continue
        #如果取到人脸框,获取人脸框坐标
        d = dets[0]
        x1 = d.left()
        y1 = d.top()
        x2 = d.right()
        y2 = d.bottom()

        #为了取到完整人脸,进行区域扩充,整体面积变大
        y1 = int(y1 - (y2 - y1) * 0.3)
        x1 = int(x1 - (x2 - x1) * 0.05)
        x2 = int(x2 + (x2 - x1) * 0.05)
        y2 = y2

        #裁剪
        im_crop_data = im_data[y1:y2, x1:x2]
        im_data = cv2.resize(im_crop_data, (160,160))
        im_save_path = "{}/{}/{}_{}.jpg".format(crop_im_path, idx, idx, "%04d" % idx_im)
        cv2.imwrite(im_save_path, im_data)
        idx_im += 1
    idx += 1

方法二:通过MTCNN人脸检测模型,从照片中提取人脸图像。
使用align_dataset_mtcnn.py文件利用MTCNN做人脸检测关键代码如下:

    with open(bounding_boxes_filename, "w") as text_file:
        nrof_images_total = 0
        nrof_successfully_aligned = 0
        if args.random_order:
            random.shuffle(dataset)
        for cls in dataset:
            output_class_dir = os.path.join(output_dir, cls.name)
            if not os.path.exists(output_class_dir):
                os.makedirs(output_class_dir)
                if args.random_order:
                    random.shuffle(cls.image_paths)
            for image_path in cls.image_paths:
                nrof_images_total += 1
                filename = os.path.splitext(os.path.split(image_path)[1])[0]
                output_filename = os.path.join(output_class_dir, filename+'.png')
                print(image_path)
                if not os.path.exists(output_filename):
                    try:
                        img = imageio.imread(image_path)
                    except (IOError, ValueError, IndexError) as e:
                        errorMessage = '{}: {}'.format(image_path, e)
                        print(errorMessage)
                    else:
                        if img.ndim<2:
                            print('Unable to align "%s"' % image_path)
                            text_file.write('%s\n' % (output_filename))
                            continue
                        if img.ndim == 2:
                            img = facenet.to_rgb(img)
                        img = img[:,:,0:3]
    
                       ...
                       ...
                       ...
                                if args.detect_multiple_faces:
                                    output_filename_n = "{}_{}{}".format(filename_base, i, file_extension)
                                else:
                                    output_filename_n = "{}{}".format(filename_base, file_extension)
                                imageio.imsave(output_filename_n, scaled)
                                text_file.write('%s %d %d %d %d\n' % (output_filename_n, bb[0], bb[1], bb[2], bb[3]))
                        else:
                            print('Unable to align "%s"' % image_path)
                            text_file.write('%s\n' % (output_filename))
                            
    print('Total number of images: %d' % nrof_images_total)
    print('Number of successfully aligned images: %d' % nrof_successfully_aligned)

经过上述代码处理后的人脸数据为以下类型:

人脸匹配模型训练、调试与测试

  在做人脸匹配模型训练时,使用源码当中的train_tripletloss.py文件以及facenet.py文件,其中训练样本train_tripletloss.py中关键代码为:

	image_batch, labels_batch = tf.train.batch_join(
    images_and_labels, batch_size=batch_size_placeholder, 
        shapes=[(args.image_size, args.image_size, 3), ()], enqueue_many=True,
        capacity=4 * nrof_preprocess_threads * args.batch_size,
        allow_smaller_final_batch=True)
    image_batch = tf.identity(image_batch, 'image_batch')
    image_batch = tf.identity(image_batch, 'input')
    labels_batch = tf.identity(labels_batch, 'label_batch')

     # Build the inference graph  使用Inception-resnet_v1
    prelogits, _ = network.inference(image_batch, args.keep_probability, 
         phase_train=phase_train_placeholder, bottleneck_layer_size=args.embedding_size,
         weight_decay=args.weight_decay)
        #特征向量,l2归一化,N*128 reshape M*3*128
     embeddings = tf.nn.l2_normalize(prelogits, 1, 1e-10, name='embeddings')
     # Split embeddings into anchor, positive and negative and calculate triplet loss
     anchor, positive, negative = tf.unstack(tf.reshape(embeddings, [-1,3,args.embedding_size]), 3, 1)

     triplet_loss = facenet.triplet_loss(anchor, positive, negative, args.alpha)
        
     learning_rate = tf.train.exponential_decay(learning_rate_placeholder, global_step,
          args.learning_rate_decay_epochs*args.epoch_size,args.learning_rate_decay_factor, staircase=True)

     learning_rate = tf.to_int32(learning_rate)
     tf.summary.scalar('learning_rate', learning_rate)

     # Calculate the total losses定义正则化
     regularization_losses = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.REGULARIZATION_LOSSES)
     total_loss = tf.add_n([triplet_loss] + regularization_losses, name='total_loss')

     # Build a Graph that trains the model with one batch of examples and updates the model parameters
     train_op = facenet.train(total_loss, global_step, args.optimizer, 
        learning_rate, args.moving_average_decay, tf.compat.v1.global_variables())   
    ...
	with tf.Graph().as_default():
        tf.set_random_seed(args.seed)
        global_step = tf.Variable(0, trainable=False)

        # Placeholder for the learning rate
        learning_rate_placeholder = tf.placeholder(tf.float32, name='learning_rate')
        
        batch_size_placeholder = tf.placeholder(tf.int32, name='batch_size')
        
        phase_train_placeholder =tf.placeholder(tf.bool, name='phase_train')
        
        image_paths_placeholder = tf.placeholder(tf.string, shape=(None,3), name='image_paths')
        labels_placeholder = tf.compat.v1.placeholder(tf.int64, shape=(None,3), name='labels')
        
        input_queue = data_flow_ops.FIFOQueue(capacity=100000,
                                    dtypes=[tf.string, tf.int64],
                                    shapes=[(3,), (3,)],
                                    shared_name=None, name=None)
        enqueue_op = input_queue.enqueue_many([image_paths_placeholder, labels_placeholder])
        
        nrof_preprocess_threads = 4
        images_and_labels = []
        for _ in range(nrof_preprocess_threads):
            filenames, label = input_queue.dequeue()
            images = []
            for filename in tf.unstack(filenames):
                file_contents = tf.read_file(filename)

                image = tf.image.decode_image(file_contents, channels=3)
                image = tf.compat.v1.to_float(image)  #int ~ float
                #数据增强
                if args.random_crop:
                    image = tf.random_crop(image, [args.image_size, args.image_size, 3])
                else:
                    image = tf.image.resize_image_with_crop_or_pad(image, args.image_size, args.image_size)
                if args.random_flip:
                    image = tf.image.random_flip_left_right(image)
    
                #pylint: disable=no-member
                image.set_shape((args.image_size, args.image_size, 3))
                images.append(tf.image.per_image_standardization(image))
            images_and_labels.append([images, label])
    
      ...
      ...
      ...

        with sess.as_default():

            if args.pretrained_model:
                print('Restoring pretrained model: %s' % args.pretrained_model)
                saver.restore(sess, os.path.expanduser(args.pretrained_model))

            # Training and validation loop
            epoch = 0
            while epoch < args.max_nrof_epochs:
                step = sess.run(global_step, feed_dict=None)
                epoch = step // args.epoch_size
                # Train for one epoch
                train(args, sess, train_set, epoch, image_paths_placeholder, labels_placeholder, labels_batch,
                    batch_size_placeholder, learning_rate_placeholder, phase_train_placeholder, enqueue_op, input_queue, global_step, 
                    embeddings, total_loss, train_op, summary_op, summary_writer, args.learning_rate_schedule_file,
                    args.embedding_size, anchor, positive, negative, triplet_loss)

                # Save variables and the metagraph if it doesn't exist already
                save_variables_and_metagraph(sess, saver, summary_writer, model_dir, subdir, step)

                # Evaluate on LFW
                if args.lfw_dir:
                    evaluate(sess, lfw_paths, embeddings, labels_batch, image_paths_placeholder, labels_placeholder, 
                            batch_size_placeholder, learning_rate_placeholder, phase_train_placeholder, enqueue_op, actual_issame, args.batch_size, 
                            args.lfw_nrof_folds, log_dir, step, summary_writer, args.embedding_size)

    return model_dir

  在facenet.py文件中的关键代码为:

def triplet_loss(anchor, positive, negative, alpha):
    """Calculate the triplet loss according to the FaceNet paper
    
    Args:
      anchor: the embeddings for the anchor images.
      positive: the embeddings for the positive images.
      negative: the embeddings for the negative images.
  
    Returns:
      the triplet loss according to the FaceNet paper as a float tensor.
    """
    with tf.variable_scope('triplet_loss'):
        pos_dist = tf.reduce_sum(tf.square(tf.subtract(anchor, positive)), 1)
        neg_dist = tf.reduce_sum(tf.square(tf.subtract(anchor, negative)), 1)
        
        basic_loss = tf.add(tf.subtract(pos_dist,neg_dist), alpha)
        loss = tf.reduce_mean(tf.maximum(basic_loss, 0.0), 0)
      
    return loss
    
def get_image_paths_and_labels(dataset):
    image_paths_flat = []
    labels_flat = []
    for i in range(len(dataset)):
        image_paths_flat += dataset[i].image_paths
        labels_flat += [i] * len(dataset[i].image_paths)
    return image_paths_flat, labels_flat

def shuffle_examples(image_paths, labels):
    shuffle_list = list(zip(image_paths, labels))
    random.shuffle(shuffle_list)
    image_paths_shuff, labels_shuff = zip(*shuffle_list)
    return image_paths_shuff, labels_shuff

def random_rotate_image(image):
    angle = np.random.uniform(low=-10.0, high=10.0)
    return misc.imrotate(image, angle, 'bicubic')

  测试代码使用源码当中的validate_on_lfw.py文件,其关键部分代码为:

def evaluate(sess, enqueue_op, image_paths_placeholder, labels_placeholder, phase_train_placeholder, batch_size_placeholder, control_placeholder,
        embeddings, labels, image_paths, actual_issame, batch_size, nrof_folds, distance_metric, subtract_mean, use_flipped_images, use_fixed_image_standardization):
    # Run forward pass to calculate embeddings
    print('Runnning forward pass on LFW images')
    
    # Enqueue one epoch of image paths and labels
    nrof_embeddings = len(actual_issame)*2  # nrof_pairs * nrof_images_per_pair
    nrof_flips = 2 if use_flipped_images else 1
    nrof_images = nrof_embeddings * nrof_flips
    labels_array = np.expand_dims(np.arange(0,nrof_images),1)
    image_paths_array = np.expand_dims(np.repeat(np.array(image_paths),nrof_flips),1)
    control_array = np.zeros_like(labels_array, np.int32)
    if use_fixed_image_standardization:
        control_array += np.ones_like(labels_array)*facenet.FIXED_STANDARDIZATION
    if use_flipped_images:
        # Flip every second image
        control_array += (labels_array % 2)*facenet.FLIP
    sess.run(enqueue_op, {image_paths_placeholder: image_paths_array, labels_placeholder: labels_array, control_placeholder: control_array})
    
    embedding_size = int(embeddings.get_shape()[1])
    assert nrof_images % batch_size == 0, 'The number of LFW images must be an integer multiple of the LFW batch size'
    nrof_batches = nrof_images // batch_size
    emb_array = np.zeros((nrof_images, embedding_size))
    lab_array = np.zeros((nrof_images,))
    for i in range(nrof_batches):
        feed_dict = {phase_train_placeholder:False, batch_size_placeholder:batch_size}
        emb, lab = sess.run([embeddings, labels], feed_dict=feed_dict)
        lab_array[lab] = lab
        emb_array[lab, :] = emb
        if i % 10 == 9:
            print('.', end='')
            sys.stdout.flush()
    print('')
    embeddings = np.zeros((nrof_embeddings, embedding_size*nrof_flips))
    if use_flipped_images:
        # Concatenate embeddings for flipped and non flipped version of the images
        embeddings[:,:embedding_size] = emb_array[0::2,:]
        embeddings[:,embedding_size:] = emb_array[1::2,:]
    else:
        embeddings = emb_array

    assert np.array_equal(lab_array, np.arange(nrof_images))==True, 'Wrong labels used for evaluation, possibly caused by training examples left in the input pipeline'
    tpr, fpr, accuracy, val, val_std, far = lfw.evaluate(embeddings, actual_issame, nrof_folds=nrof_folds, distance_metric=distance_metric, subtract_mean=subtract_mean)
    
    print('Accuracy: %2.5f+-%2.5f' % (np.mean(accuracy), np.std(accuracy)))
    print('Validation rate: %2.5f+-%2.5f @ FAR=%2.5f' % (val, val_std, far))
    
    auc = metrics.auc(fpr, tpr)
    print('Area Under Curve (AUC): %1.3f' % auc)
    eer = brentq(lambda x: 1. - x - interpolate.interp1d(fpr, tpr)(x), 0., 1.)
    print('Equal Error Rate (EER): %1.3f' % eer)
    

  之后便是模型固化以及保存pb文件,使用源码中的freeze_graph.py文件,其关键代码为:

def main(args):
    with tf.Graph().as_default():
        with tf.Session() as sess:
            # Load the model metagraph and checkpoint
            print('Model directory: %s' % args.model_dir)
            meta_file, ckpt_file = facenet.get_model_filenames(os.path.expanduser(args.model_dir))
            
            print('Metagraph file: %s' % meta_file)
            print('Checkpoint file: %s' % ckpt_file)

            model_dir_exp = os.path.expanduser(args.model_dir)
            saver = tf.train.import_meta_graph(os.path.join(model_dir_exp, meta_file), clear_devices=True)
            tf.get_default_session().run(tf.global_variables_initializer())
            tf.get_default_session().run(tf.local_variables_initializer())
            saver.restore(tf.get_default_session(), os.path.join(model_dir_exp, ckpt_file))
            
            # Retrieve the protobuf graph definition and fix the batch norm nodes
            input_graph_def = sess.graph.as_graph_def()
            
            # Freeze the graph def
            output_graph_def = freeze_graph_def(sess, input_graph_def, 'embeddings,label_batch')

        # Serialize and dump the output graph to the filesystem
        with tf.gfile.GFile(args.output_file, 'wb') as f:
            f.write(output_graph_def.SerializeToString())
        print("%d ops in the final graph: %s" % (len(output_graph_def.node), args.output_file))
        
def freeze_graph_def(sess, input_graph_def, output_node_names):
    for node in input_graph_def.node:
        if node.op == 'RefSwitch':
            node.op = 'Switch'
            for index in xrange(len(node.input)):
                if 'moving_' in node.input[index]:
                    node.input[index] = node.input[index] + '/read'
        elif node.op == 'AssignSub':
            node.op = 'Sub'
            if 'use_locking' in node.attr: del node.attr['use_locking']
        elif node.op == 'AssignAdd':
            node.op = 'Add'
            if 'use_locking' in node.attr: del node.attr['use_locking']
    
    # Get the list of important nodes
    whitelist_names = []
    for node in input_graph_def.node:
        if (node.name.startswith('InceptionResnet') or node.name.startswith('embeddings') or 
                node.name.startswith('image_batch') or node.name.startswith('label_batch') or
                node.name.startswith('phase_train') or node.name.startswith('Logits')):
            whitelist_names.append(node.name)

    # Replace all the variables in the graph with constants of the same values
    output_graph_def = graph_util.convert_variables_to_constants(
        sess, input_graph_def, output_node_names.split(","),
        variable_names_whitelist=whitelist_names)
    return output_graph_def

保存后的模型以及pb文件保存为相似类型:
在这里插入图片描述
 人脸匹配模型优化改进策略

 在人脸匹配模型中,应从以下几个方面进行优化:

  1. 首先是数据的扩充与处理,如考虑清晰度、光照环境、人脸遮挡程度、采集程度以及后续使用的数据增强方法等等。
  2. 其次从深度学习算法入手,使用更加适合人脸分析的损失函数,使用合适的深度学习模型等等。
  3. 结合其他目标检测算法,优化网络卷积层,实验尝试多种卷积方式以及池化方式等。

  图像处理 人脸识别的三种经典算法与简单的CNN 【附Python实现】
  人脸识别原理与模型方法综述

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐