人脸检测与识别:基于MTCNN与insightface的人脸打卡系统

注:篇幅较长,持续更新状态

2019.4.10

 

阶段一:基于MTCNN的人脸检测

前期已有实现,遂不再重复。github代码持续更新,现更新到version 0.2,博客由于代码更新幅度较大且仅提供入门参考遂不再更新,如有更新那就是我有时间和节操了~。

version 0.1博客地址:人脸检测与识别:MTCNN人脸检测

github地址:https://github.com/friedhelm739/MTCNN-tensorflow

 

阶段二:基于insightface的人脸识别

项目环境及配置:ubuntu16.04+2*GTX 1080ti+Python3.6+Anaconda5.2.0+Tensorflow1.7-gpu

本阶段是对《ArcFace: Additive Angular Margin Loss for Deep Face Recognition》论文的复现,网上解读文章很多,大家可以择优选读,关于代码解读有一系列比较好的解读,对入门理解源码有一定的帮助。

博客地址:人脸检测与识别:基于MTCNN与insightface的人脸打卡系统

github地址:https://github.com/friedhelm739/Insightface-tensorflow

 

本阶段代码参考:

  • https://github.com/deepinsight/insightface
  • https://github.com/luckycallor/InsightFace-tensorflow (非常感谢)
  • https://github.com/auroua/InsightFace_TF
  • https://github.com/tensorflow/models

在此对其表示衷心的感谢。

 

1、数据获取与处理

本文数据可以很轻松的从源代码的Dataset Zoo内获取,本文使用CASIA数据集。

下载后解压成如图1形式:

图1

其中训练用的是train.idx和train.rec,其他的bin文件都是验证用的。

解压后需要使用/data/gen_tfrecord_mxdata.py将原MXNet训练数据格式转换为tensorflow的TFRecord格式,代码直接抄袭,如下所示:


 
 
   
   
   
   
  1. # -*- coding: utf-8 -*-
  2. """
  3. @author: friedhelm
  4. """
  5. import tensorflow as tf
  6. import mxnet as mx
  7. import os
  8. import io
  9. import numpy as np
  10. import cv2
  11. import time
  12. from scipy import misc
  13. import argparse
  14. from core import config
  15. def arg_parse():
  16. parser=argparse.ArgumentParser()
  17. parser.add_argument( "--read_dir",default=config.mxdata_dir,type=str, help= 'directory to read data')
  18. parser.add_argument( "--save_dir",default=config.tfrecord_dir,type=str, help= 'path to save TFRecord file')
  19. return parser
  20. def main():
  21. with tf.python_io.TFRecordwriter(save_dir) as writer:
  22. idx_path = os.path.join(read_dir, 'train.idx')
  23. bin_path = os.path.join(read_dir, 'train.rec')
  24. imgrec = mx.recordio.MXIndexedRecordIO(idx_path, bin_path, 'r')
  25. s = imgrec.read_idx( 0)
  26. header, _ = mx.recordio.unpack(s)
  27. imgidx = list(range( 1, int(header.label[ 0])))
  28. labels = []
  29. for i in imgidx:
  30. img_info = imgrec.read_idx(i)
  31. header, img = mx.recordio.unpack(img_info)
  32. label = int(header.label)
  33. labels.append(label)
  34. img = io.BytesIO(img)
  35. img = misc.imread(img).astype(np.uint8)
  36. img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
  37. #img = cv2.resize(img, (112,112))
  38. img_raw = img.tobytes()
  39. example=tf.train.Example(features=tf.train.Features(feature={
  40. "img" : tf.train.Feature(bytes_list=tf.train.BytesList(value=[img_raw])),
  41. "label" : tf.train.Feature(int64_list=tf.train.Int64List(value=[label])),
  42. }))
  43. writer.write(example.SerializeToString())
  44. if i % 10000 == 0:
  45. print( '%d pics processed' % i, "time: ", time.time()-begin)
  46. if __name__ == "__main__":
  47. parser=arg_parse()
  48. save_dir=parser.save_dir
  49. read_dir=parser.read_dir
  50. begin=time.time()
  51. main()

测试数据直接使用,如想转换请参考上述代码。测试数据参照源码,是使用MTCNN检测人脸得到的,下面以lfw为例,代码路径:/data/gen_lfw_data.py。代码基本为搬运源码,由于数据集的特性,MTCNN会检测出多张未标注人脸,或者同一人脸多检测框的情况,这时源码使用以图片中心为准的思路,只取与图片中心点偏差最小的人脸框,。


 
 
   
   
   
   
  1. # -*- coding: utf-8 -*-
  2. """
  3. @author: friedhelm
  4. """
  5. import sys
  6. sys.path.append( "../")
  7. from core.MTCNN.mtcnn_detector import MTCNN_Detector
  8. from core.MTCNN.MTCNN_model import Pnet_model,Rnet_model,Onet_model
  9. import numpy as np
  10. import os
  11. from collections import namedtuple
  12. from easydict import EasyDict as edict
  13. from scipy import misc
  14. import cv2
  15. from collections import namedtuple
  16. from core import config
  17. import argparse
  18. from core.tool import preprocess
  19. def arg_parse():
  20. parser=argparse.ArgumentParser()
  21. parser.add_argument( "--input_dir",default=config.lfw_dir,type=str, help= 'directory to read lfw data')
  22. parser.add_argument( "--output_dir",default=config.lfw_save_dir,type=str, help= 'path to save lfw_face data')
  23. parser.add_argument( "--image_size",default= "112,112",type=str, help= 'image size')
  24. return parser
  25. def get_DataSet(input_dir, min_images=1):
  26. ret = []
  27. label = 0
  28. person_names = []
  29. for person_name in os.listdir(input_dir):
  30. person_names.append(person_name)
  31. person_names = sorted(person_names)
  32. for person_name in person_names:
  33. _subdir = os.path.join(input_dir, person_name)
  34. if not os.path.isdir(_subdir):
  35. continue
  36. _ret = []
  37. for img in os.listdir(_subdir):
  38. fimage = edict()
  39. fimage.id = os.path.join(person_name, img)
  40. fimage.classname = str(label)
  41. fimage.image_path = os.path.join(_subdir, img)
  42. fimage.bbox = None
  43. fimage.landmark = None
  44. _ret.append(fimage)
  45. if len(_ret)>=min_images:
  46. ret += _ret
  47. label+= 1
  48. return ret
  49. def main(args):
  50. dataset = get_DataSet(args.input_dir)
  51. print( 'dataset size', 'lfw', len(dataset))
  52. print( 'Creating networks and loading parameters')
  53. if(model_name in [ "Pnet", "Rnet", "Onet"]):
  54. model[ 0]=Pnet_model
  55. if(model_name in [ "Rnet", "Onet"]):
  56. model[ 1]=Rnet_model
  57. if(model_name== "Onet"):
  58. model[ 2]=Onet_model
  59. detector=MTCNN_Detector(model,model_path,batch_size,factor,min_face_size,threshold)
  60. if not os.path.exists(args.output_dir):
  61. os.makedirs(args.output_dir)
  62. output_filename = os.path.join(args.output_dir, 'lfw_list')
  63. print( 'begin to generate')
  64. with open(output_filename, "w") as text_file:
  65. nrof_images_total = 0
  66. nrof = np.zeros( ( 2,), dtype=np.int32)
  67. for fimage in dataset:
  68. if nrof_images_total% 100== 0:
  69. print( "Processing %d, (%s)" % (nrof_images_total, nrof))
  70. nrof_images_total += 1
  71. image_path = fimage.image_path
  72. if not os.path.exists(image_path):
  73. print( 'image not found (%s)'%image_path)
  74. continue
  75. try:
  76. img = cv2.imread(image_path)
  77. except (IOError, ValueError, IndexError) as e:
  78. errorMessage = '{}: {}'.format(image_path, e)
  79. print(errorMessage)
  80. else:
  81. _paths = fimage.image_path.split( '/')
  82. a,b = _paths[ -2], _paths[ -1]
  83. target_dir = os.path.join(args.output_dir, a)
  84. if not os.path.exists(target_dir):
  85. os.makedirs(target_dir)
  86. target_file = os.path.join(target_dir, b)
  87. _bbox = None
  88. _landmark = None
  89. bounding_boxes, points = detector.detect_single_face(img, False)
  90. nrof_faces = np.shape(bounding_boxes)[ 0]
  91. if nrof_faces> 0:
  92. det = bounding_boxes[:, 0: 4]
  93. img_size = np.asarray(img.shape)[ 0: 2]
  94. bindex = 0
  95. if nrof_faces> 1:
  96. #select the center face according to the characterize of lfw
  97. bounding_box_size = (det[:, 2]-det[:, 0])*(det[:, 3]-det[:, 1])
  98. img_center = img_size / 2
  99. offsets = np.vstack([ (det[:, 0]+det[:, 2])/ 2-img_center[ 1], (det[:, 1]+det[:, 3])/ 2-img_center[ 0] ])
  100. offset_dist_squared = np.sum(np.power(offsets, 2.0), 0)
  101. bindex = np.argmax(bounding_box_size-offset_dist_squared* 2.0) # some extra weight on the centering
  102. _bbox = bounding_boxes[bindex, 0: 4]
  103. _landmark = points[bindex, :]
  104. nrof[ 0]+= 1
  105. else:
  106. nrof[ 1]+= 1
  107. warped = preprocess(img, bbox=_bbox, landmark = _landmark, image_size=args.image_size)
  108. cv2.imwrite(target_file, warped)
  109. oline = '%d\t%s\t%d\n' % ( 1,target_file, int(fimage.classname))
  110. text_file.write(oline)
  111. if __name__== "__main__":
  112. model=[ None, None, None]
  113. #原文参数
  114. factor= 0.79
  115. threshold=[ 0.8, 0.8, 0.6]
  116. min_face_size= 20
  117. #原文参数
  118. batch_size= 1
  119. model_name= "Onet"
  120. base_dir= "."
  121. model_path=[os.path.join(base_dir, "model/MTCNN_model/Pnet_model/Pnet_model.ckpt-20000"),
  122. os.path.join(base_dir, "model/MTCNN_model/Rnet_model/Rnet_model.ckpt-40000"),
  123. os.path.join(base_dir, "model/MTCNN_model/Onet_model/Onet_model.ckpt-40000")]
  124. args=arg_parse()
  125. #User = namedtuple('User', ['input_dir', 'output_dir', 'image_size'])
  126. #args = User(input_dir='./data/lfw', output_dir='./data/lfw_face', image_size="112,112")
  127. main(args)

在检测出人脸框后会进行人脸对齐操作,这一步使用的是preprocessing函数,采用skimage的SimilarityTransform()仿射函数针对人脸关键点进行人脸对齐,其中cv2的仿射变换函数我也使用过,效果不如skimage的好。/core/preprocessing.py代码如下所示:


 
 
   
   
   
   
  1. # -*- coding: utf-8 -*-
  2. """
  3. @author: friedhelm
  4. """
  5. import numpy as np
  6. from skimage import transform as trans
  7. import cv2
  8. def preprocess(img, bbox=None, landmark=None, **kwargs):
  9. M = None
  10. image_size = []
  11. str_image_size = kwargs.get( 'image_size', '')
  12. if len(str_image_size)> 0:
  13. image_size = [int(x) for x in str_image_size.split( ',')]
  14. if len(image_size)== 1:
  15. image_size = [image_size[ 0], image_size[ 0]]
  16. assert len(image_size)== 2
  17. assert image_size[ 0]== 112
  18. assert image_size[ 0]== 112 or image_size[ 1]== 96
  19. # define desire position of landmarks
  20. src = np.array([
  21. [ 30.2946, 51.6963],
  22. [ 65.5318, 51.5014],
  23. [ 48.0252, 71.7366],
  24. [ 33.5493, 92.3655],
  25. [ 62.7299, 92.2041] ], dtype=np.float32 )
  26. if image_size[ 1]== 112:
  27. src[:, 0] += 8.0
  28. if ((landmark is not None)&(kwargs.get( 'align', True))):
  29. assert len(image_size)== 2
  30. dst = landmark.astype(np.float32)
  31. #skimage affine
  32. tform = trans.SimilarityTransform()
  33. tform.estimate(dst, src)
  34. M = tform.params[ 0: 2,:]
  35. # #cv2 affine , worse than skimage
  36. # src = src[0:3,:]
  37. # dst = dst[0:3,:]
  38. # M = cv2.getAffineTransform(dst,src)
  39. if M is None:
  40. if bbox is None: #use center crop
  41. det = np.zeros( 4, dtype=np.int32)
  42. det[ 0] = int(img.shape[ 1]* 0.0625)
  43. det[ 1] = int(img.shape[ 0]* 0.0625)
  44. det[ 2] = img.shape[ 1] - det[ 0]
  45. det[ 3] = img.shape[ 0] - det[ 1]
  46. else:
  47. det = bbox
  48. margin = kwargs.get( 'margin', 44)
  49. bb = np.zeros( 4, dtype=np.int32)
  50. bb[ 0] = np.maximum(det[ 0]-margin/ 2, 0)
  51. bb[ 1] = np.maximum(det[ 1]-margin/ 2, 0)
  52. bb[ 2] = np.minimum(det[ 2]+margin/ 2, img.shape[ 1])
  53. bb[ 3] = np.minimum(det[ 3]+margin/ 2, img.shape[ 0])
  54. ret = img[bb[ 1]:bb[ 3],bb[ 0]:bb[ 2],:]
  55. if len(image_size)> 0:
  56. ret = cv2.resize(ret, (image_size[ 1], image_size[ 0]))
  57. return ret
  58. else: #do align using landmark
  59. assert len(image_size)== 2
  60. warped = cv2.warpAffine(img,M,(image_size[ 1],image_size[ 0]), borderValue = 0.0)
  61. return warped

在人脸检测与对齐完成后,制作lfw_pair文件,代码依旧抄袭源码,根据lfw官网下载的pairs.txt文件进行测试集数据制作,格式为5000个样本,2500个同一人正样本,和2500个负样本,人脸识别的验证就是相当于人脸验证。/data/gen_eval_pickle_data.py如下:


 
 
   
   
   
   
  1. # -*- coding: utf-8 -*-
  2. """
  3. @author: friedhelm
  4. """
  5. import argparse
  6. import pickle
  7. import os
  8. import numpy as np
  9. from collections import namedtuple
  10. from core import config
  11. def get_paths(lfw_dir, pairs, file_ext):
  12. nrof_skipped_pairs = 0
  13. path_list = []
  14. issame_list = []
  15. for pair in pairs:
  16. if len(pair) == 3:
  17. path0 = os.path.join(lfw_dir, pair[ 0], pair[ 0] + '_' + '%04d' % int(pair[ 1])+ '.'+file_ext)
  18. path1 = os.path.join(lfw_dir, pair[ 0], pair[ 0] + '_' + '%04d' % int(pair[ 2])+ '.'+file_ext)
  19. issame = True
  20. elif len(pair) == 4:
  21. path0 = os.path.join(lfw_dir, pair[ 0], pair[ 0] + '_' + '%04d' % int(pair[ 1])+ '.'+file_ext)
  22. path1 = os.path.join(lfw_dir, pair[ 2], pair[ 2] + '_' + '%04d' % int(pair[ 3])+ '.'+file_ext)
  23. issame = False
  24. if os.path.exists(path0) and os.path.exists(path1): # Only add the pair if both paths exist
  25. path_list += (path0,path1)
  26. issame_list.append(issame)
  27. else:
  28. print( 'not exists', path0, path1)
  29. nrof_skipped_pairs += 1
  30. if nrof_skipped_pairs> 0:
  31. print( 'Skipped %d image pairs' % nrof_skipped_pairs)
  32. return path_list, issame_list
  33. def read_pairs(pairs_filename):
  34. pairs = []
  35. with open(pairs_filename, 'r') as f:
  36. for line in f.readlines()[ 1:]:
  37. pair = line.strip().split()
  38. pairs.append(pair)
  39. return np.array(pairs)
  40. def arg_parse():
  41. parser = argparse.ArgumentParser(description= 'Package LFW images')
  42. parser.add_argument( '--input_dir', default=config.mxdata_dir, help= 'path to load')
  43. parser.add_argument( '--output_dir', default=config.eval_dir, help= 'path to save.')
  44. return parser
  45. if __name__== "__main__":
  46. args = arg_parse()
  47. # User = namedtuple('User', ['input_dir', 'output_dir'])
  48. # args = User(input_dir='./data', output_dir='./data/lfw_face.db')
  49. lfw_dir = args.input_dir
  50. lfw_pairs = read_pairs(os.path.join(lfw_dir, 'pairs.txt'))
  51. lfw_dir = os.path.join(lfw_dir, 'lfw_face')
  52. lfw_paths, issame_list = get_paths(lfw_dir, lfw_pairs, 'jpg')
  53. lfw_bins = []
  54. i = 0
  55. for path in lfw_paths:
  56. with open(path, 'rb') as fin:
  57. _bin = fin.read()
  58. lfw_bins.append(_bin)
  59. i+= 1
  60. if i% 1000== 0:
  61. print( 'loading lfw', i)
  62. with open(args.output_dir, 'wb') as f:
  63. pickle.dump((lfw_bins, issame_list), f, protocol=pickle.HIGHEST_PROTOCOL)

至此数据制作完成。

2、人脸识别模型与损失函数

人脸识别的重点在于其损失函数而不在于模型,insightface的论文(下统称论文)中仅对Resnet模型进行了小修改,模型代码参考官方slim_model与参考代码2。论文修改如下所示:

  • 将残差模块的stride=2的卷积模块提前
  • 激活函数改用prelu
  • 取消第一层max_pool与最后的全局average_pool

修改后模型代码在/core目录下,请自行参阅,博客不再附代码。

损失函数为Arcloss,具体原理请参考其他博客。代码如下所示:


 
 
   
   
   
   
  1. # -*- coding: utf-8 -*-
  2. """
  3. @author: friedhelm
  4. """
  5. import sys
  6. sys.path.append( "../")
  7. import tensorflow as tf
  8. import tensorflow.contrib.slim as slim
  9. import math
  10. from core import config
  11. def arcface_loss(inputs,labels,s,m):
  12. with tf.name_scope( "arcface_loss"):
  13. weight = tf.get_variable( "loss_wight",[inputs.get_shape().as_list()[ -1], config.class_num],
  14. initializer = tf.contrib.layers.xavier_initializer(),
  15. regularizer=slim.l2_regularizer(config.model_params[ "weight_decay"]))
  16. inputs = tf.nn.l2_normalize(inputs, axis= 1)
  17. weight = tf.nn.l2_normalize(weight, axis= 0)
  18. sin_m = math.sin(m)
  19. cos_m = math.cos(m)
  20. mm = sin_m * m
  21. threshold = math.cos(math.pi - m)
  22. cos_theta = tf.matmul(inputs,weight,name= "cos_theta")
  23. sin_theta = tf.sqrt(tf.subtract( 1. , tf.square(cos_theta)))
  24. cos_theta_m = s * tf.subtract(tf.multiply(cos_theta , cos_m) , tf.multiply(sin_theta , sin_m))
  25. keep_val = s * (cos_theta - mm)
  26. cond_v = cos_theta - threshold
  27. cond= tf.cast(tf.nn.relu(cond_v),dtype=tf.bool)
  28. cos_theta_m_keep = tf.where(cond , cos_theta_m , keep_val)
  29. mask = tf.one_hot(labels , config.class_num)
  30. inv_mask = tf.subtract( 1., mask)
  31. output = tf.add(tf.multiply(mask , cos_theta_m_keep) , tf.multiply(inv_mask , s * cos_theta) , name= "arcface_loss")
  32. return output

代码中其他的都好理解,就是阈值这块有点费劲,源码中设定了cos(pi-m)的阈值,一旦cos(/theta )越界就将cos(/theta +m)设为cos(/theta )--m *sin(m)。

3、模型训练

本文代码选用双GPU运行,单gpu版可在/train/train.py中找到。/train/train_multi_gpus.py代码如下所示:


 
 
   
   
   
   
  1. import tensorflow as tf
  2. from train.train_tool import arcface_loss,read_single_tfrecord,average_gradients
  3. from core import Arcface_model,config
  4. import time
  5. import os
  6. from evaluate.evaluate import evaluation,load_bin
  7. def train(image,label,train_phase_dropout,train_phase_bn, images_batch, images_f_batch, issame_list_batch):
  8. train_images_split = tf.split(image, config.gpu_num)
  9. train_labels_split = tf.split(label, config.gpu_num)
  10. global_step = tf.Variable(name= 'global_step', initial_value= 0, trainable= False)
  11. inc_op = tf.assign_add(global_step, 1, name= 'increment_global_step')
  12. scale = int( 512.0/batch_size)
  13. lr_steps = [scale*s for s in config.lr_steps]
  14. lr_values = [v/scale for v in config.lr_values]
  15. lr = tf.train.piecewise_constant(global_step, boundaries=lr_steps, values=lr_values, name= 'lr_schedule')
  16. opt = tf.train.MomentumOptimizer(learning_rate=lr, momentum=config.momentum)
  17. embds = []
  18. logits = []
  19. inference_loss = []
  20. wd_loss = []
  21. total_train_loss = []
  22. pred = []
  23. tower_grads = []
  24. update_ops = []
  25. for i in range(config.gpu_num):
  26. sub_train_images = train_images_split[i]
  27. sub_train_labels = train_labels_split[i]
  28. with tf.device( "/gpu:%d"%(i)):
  29. with tf.variable_scope(tf.get_variable_scope(),reuse=(i> 0)):
  30. net, end_points = Arcface_model.get_embd(sub_train_images, train_phase_dropout, train_phase_bn,config.model_params)
  31. logit = arcface_loss(net,sub_train_labels,config.s,config.m)
  32. arc_loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits = logit , labels = sub_train_labels))
  33. L2_loss = tf.reduce_sum(tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES))
  34. train_loss = arc_loss + L2_loss
  35. pred.append(tf.to_int32(tf.argmax(tf.nn.softmax(logit),axis= 1)))
  36. tower_grads.append(opt.compute_gradients(train_loss))
  37. update_ops.append(tf.get_collection(tf.GraphKeys.UPDATE_OPS))
  38. embds.append(net)
  39. logits.append(logit)
  40. inference_loss.append(arc_loss)
  41. wd_loss.append(L2_loss)
  42. total_train_loss.append(train_loss)
  43. embds = tf.concat(embds, axis= 0)
  44. logits = tf.concat(logits, axis= 0)
  45. pred = tf.concat(pred, axis= 0)
  46. wd_loss = tf.add_n(wd_loss)/config.gpu_num
  47. inference_loss = tf.add_n(inference_loss)/config.gpu_num
  48. train_ops = [opt.apply_gradients(average_gradients(tower_grads))]
  49. train_ops.extend(update_ops)
  50. train_op = tf.group(*train_ops)
  51. with tf.name_scope( 'loss'):
  52. train_loss = tf.add_n(total_train_loss)/config.gpu_num
  53. tf.summary.scalar( 'train_loss',train_loss)
  54. with tf.name_scope( 'accuracy'):
  55. train_accuracy = tf.reduce_mean(tf.cast(tf.equal(pred, label), tf.float32))
  56. tf.summary.scalar( 'train_accuracy',train_accuracy)
  57. saver=tf.train.Saver(max_to_keep= 20)
  58. merged=tf.summary.merge_all()
  59. train_images,train_labels=read_single_tfrecord(addr,batch_size,img_size)
  60. tf_config = tf.ConfigProto(allow_soft_placement= True)
  61. tf_config.gpu_options.allow_growth = True
  62. with tf.Session(config=tf_config) as sess:
  63. sess.run((tf.global_variables_initializer(),
  64. tf.local_variables_initializer()))
  65. coord = tf.train.Coordinator()
  66. threads = tf.train.start_queue_runners(sess=sess,coord=coord)
  67. writer_train=tf.summary.FileWriter(model_path,sess.graph)
  68. print( "start")
  69. try:
  70. for i in range( 1,train_step):
  71. image_batch,label_batch=sess.run([train_images,train_labels])
  72. sess.run([train_op,inc_op],feed_dict={image:image_batch,label:label_batch,train_phase_dropout: True,train_phase_bn: True})
  73. if(i% 100== 0):
  74. summary=sess.run(merged,feed_dict={image:image_batch,label:label_batch,train_phase_dropout: True,train_phase_bn: True})
  75. writer_train.add_summary(summary,i)
  76. if(i% 1000== 0):
  77. print( 'times: ',i)
  78. # print('train_accuracy: ',sess.run(train_accuracy,feed_dict={image:image_batch,label:label_batch,train_phase_dropout:True,train_phase_bn:True}))
  79. # print('train_loss: ',sess.run(train_loss,{image:image_batch,label:label_batch,train_phase_dropout:True,train_phase_bn:True}))
  80. print( 'time: ',time.time()-begin)
  81. if(i% 5000== 0):
  82. f.write( "itrations: %d"%(i)+ '\n')
  83. for idx in range(len(eval_datasets)):
  84. tpr, fpr, accuracy, best_thresholds = evaluation(sess, images_batch[idx], images_f_batch[idx], issame_list_batch[idx], batch_size, img_size, dropout_flag=config.eval_dropout_flag, bn_flag=config.eval_bn_flag, embd=embds, image=image, train_phase_dropout=train_phase_dropout, train_phase_bn=train_phase_bn)
  85. print( "%s datasets get %.3f acc"%(eval_datasets[idx].split( "/")[ -1].split( ".")[ 0],accuracy))
  86. f.write( "\t %s \t %.3f \t \t "%(eval_datasets[idx].split( "/")[ -1].split( ".")[ 0],accuracy)+str(best_thresholds)+ '\n')
  87. f.write( '\n')
  88. if((i> 150000)&(i%config.model_save_gap== 0)):
  89. saver.save(sess,os.path.join(model_path,model_name),global_step=i)
  90. except tf.errors.OutOfRangeError:
  91. print( "finished")
  92. finally:
  93. coord.request_stop()
  94. writer_train.close()
  95. coord.join(threads)
  96. f.close()
  97. def main():
  98. with tf.name_scope( 'input'):
  99. image = tf.placeholder(tf.float32,[batch_size,img_size,img_size, 3],name= 'image')
  100. label = tf.placeholder(tf.int32,[batch_size],name= 'label')
  101. train_phase_dropout = tf.placeholder(dtype=tf.bool, shape= None, name= 'train_phase_dropout')
  102. train_phase_bn = tf.placeholder(dtype=tf.bool, shape= None, name= 'train_phase_bn')
  103. images_batch = []
  104. images_f_batch = []
  105. issame_list_batch = []
  106. for dataset_path in eval_datasets:
  107. images, images_f, issame_list = load_bin(dataset_path, img_size)
  108. images_batch.append(images)
  109. images_f_batch.append(images_f)
  110. issame_list_batch.append(issame_list)
  111. train(image,label, train_phase_dropout, train_phase_bn, images_batch, images_f_batch, issame_list_batch)
  112. if __name__ == "__main__":
  113. img_size = config.img_size
  114. batch_size = config.batch_size
  115. addr = config.addrt
  116. model_name = config.model_name
  117. train_step = config.train_step
  118. model_path = config.model_patht
  119. eval_datasets = config.eval_datasets
  120. begin=time.time()
  121. f = open( "./eval_record.txt", 'w')
  122. f.write( "\t dataset \t accuracy \t best_thresholds \t"+ '\n')
  123. main()
  124. # tensorboard --logdir=/home/dell/Desktop/insightface/model/Arcface_model/

4、结果

训练过程曲线就不贴了,在github了,里有相应的event文件。验证结果如图2所示,其中lfw_face为自己生成的lfw验证集:

图2

5、总结

  • 原本中使用prelu进行训练,而我使用prelu激活函数验证的结果并不好,我又使用参考代码2的leaky_relu模型验证,验证结果比prelu的高出很多。
  • 人脸识别受到人脸检测和对齐的极大影响。

 

阶段三:基于MTCNN与insightface的人脸打卡系统

项目环境及配置:ubuntu16.04+2*GTX 1080ti+Python3.6+Anaconda5.2.0+Tensorflow1.7-gpu+Mysql5.7.25

 

博客地址:人脸检测与识别:基于MTCNN与insightface的人脸打卡系统

github地址:https://github.com/friedhelm739/Insightface-tensorflow

 

 

 

 

你可能感兴趣的:(python,人脸识别)