目标:
实现三元组损失函数。
使用一个已经训练好了的模型来将人脸图像映射到一个128位数字的的向量。
使用这些编码来执行人脸验证和人脸识别。
from keras.models import Sequential
from keras.layers import Conv2D, ZeroPadding2D, Activation, Input, concatenate
from keras.models import Model
from keras.layers.normalization import BatchNormalization
from keras.layers.pooling import MaxPooling2D, AveragePooling2D
from keras.layers.merge import Concatenate
from keras.layers.core import Lambda, Flatten, Dense
from keras.initializers import glorot_uniform
from keras.engine.topology import Layer
from keras import backend as K
#------------用于绘制模型细节,可选--------------#
from IPython.display import SVG
from keras.utils.vis_utils import model_to_dot
from keras.utils import plot_model
#------------------------------------------------#
K.set_image_data_format('channels_first')
import time
import cv2
import os
import numpy as np
from numpy import genfromtxt
import pandas as pd
import tensorflow as tf
import fr_utils
from inception_blocks_v2 import *
%matplotlib inline
%load_ext autoreload
%autoreload 2
np.set_printoptions(threshold=np.nan)
初始模型使用由Szegedy et al.等人的初始模型,加载预训练权值,可以在inception_blocks.py文件来查看是如何实现的。
关键:
创建一个人脸识别的模型:
FRmodel = faceRecoModel(input_shape=(3,96,96))
print("参数数量:" + str(FRmodel.count_params()))
结果:
参数数量:3743280
使用128神经元全连接层作为最后一层,该模型确保输出是大小为128的编码向量,然后使用比较两个人脸图像的编码:
通过计算两个编码和阈值之间的误差,可以确定这两幅图是否代表同一个人。
三元组损失函数将上面的形式实现,它会试图将同一个人的两个图像(对于给定的图和正例)的编码“拉近”,同时将两个不同的人的图像(对于给定的图和负例)进一步“分离”。
满足:
同一个人的两个图像的编码非常相似。
两个不同人物的图像的编码非常不同。
什么是三元组损失函数:
使用三元组图像(A,P,N)进行训练:
这些三元组来自训练集,我们使用 ( A( i ), P( i ), N( i )) 来表示第i个训练样本。我们要保证图像A( i )与图像P( i )的差值至少比与图像 N ( i ) 的差值相差 α(阈值):
逐步实现:
计算"anchor" 与 "positive"之间编码的距离:
计算"anchor" 与 "negative"之间编码的距离:
根据公式计算每个样本的值:
def triplet_loss(y_true, y_pred, alpha = 0.2):
anchor, positive, negative = y_pred[0], y_pred[1], y_pred[2]
pos_dist = tf.reduce_sum(tf.square(tf.subtract(anchor,positive)),axis=-1)
neg_dist = tf.reduce_sum(tf.square(tf.subtract(anchor,negative)),axis=-1)
basic_loss = tf.add(tf.subtract(pos_dist,neg_dist),alpha)
loss = tf.reduce_sum(tf.maximum(basic_loss,0))
return loss
测试:
with tf.Session() as test:
tf.set_random_seed(1)
y_true = (None, None, None)
y_pred = (tf.random_normal([3, 128], mean=6, stddev=0.1, seed = 1),
tf.random_normal([3, 128], mean=1, stddev=1, seed = 1),
tf.random_normal([3, 128], mean=3, stddev=4, seed = 1))
loss = triplet_loss(y_true, y_pred)
print("loss = " + str(loss.eval()))
结果:
loss = 528.143
FaceNet是通过最小化三元组损失来训练的,但是由于训练需要大量的数据和时间,所以我们不会从头训练,相反,我们会加载一个已经训练好了的模型,运行下列代码来加载模型,可能会需要几分钟的时间。
FRmodel.compile(optimizer = 'adam', loss = triplet_loss, metrics = ['accuracy'])
fr_utils.load_weights_from_FaceNet(FRmodel)
使用这个模型进行人脸验证和人脸识别。
构建一个数据库,里面包含了允许进入的人员的编码向量,使用的是一个字典来表示,这个字典将每个人的名字映射到他们面部的128维编码上。
database = {}
database["danielle"] = fr_utils.img_to_encoding("images/danielle.png", FRmodel)
database["younes"] = fr_utils.img_to_encoding("images/younes.jpg", FRmodel)
database["tian"] = fr_utils.img_to_encoding("images/tian.jpg", FRmodel)
database["andrew"] = fr_utils.img_to_encoding("images/andrew.jpg", FRmodel)
database["kian"] = fr_utils.img_to_encoding("images/kian.jpg", FRmodel)
database["dan"] = fr_utils.img_to_encoding("images/dan.jpg", FRmodel)
database["sebastiano"] = fr_utils.img_to_encoding("images/sebastiano.jpg", FRmodel)
database["bertrand"] = fr_utils.img_to_encoding("images/bertrand.jpg", FRmodel)
database["kevin"] = fr_utils.img_to_encoding("images/kevin.jpg", FRmodel)
database["felix"] = fr_utils.img_to_encoding("images/felix.jpg", FRmodel)
database["benoit"] = fr_utils.img_to_encoding("images/benoit.jpg", FRmodel)
database["arnaud"] = fr_utils.img_to_encoding("images/arnaud.jpg", FRmodel)
当有人出现在你的门前刷他们的身份证的时候,你可以在数据库中查找他们的编码。
实现verify() 函数来验证摄像头的照片(image_path)是否与身份证上的名称匹配,这个部分可由以下步骤构成:
我们使用L2(np.linalg.norm)来计算差距。
def verify(image_path, identity, database, model):
encoding = fr_utils.img_to_encoding(image_path, model)
dist = np.linalg.norm(encoding - database[identity])
if dist < 0.7:
print("欢迎 " + str(identity) + "回家!")
is_door_open = True
else:
print("经验证,您与" + str(identity) + "不符!")
is_door_open = False
return dist, is_door_open
测试:
verify("images/camera_0.jpg","younes",database,FRmodel)
结果:
欢迎 younes回家!
(0.65939206, True)
实现一个人脸识别系统,该系统将图像作为输入,并确定它是否是授权人员之一(如果是,是谁),与之前的人脸验证系统不同,我们不再将一个人的名字作为输入的一部分。
现在我们要实现who_is_it()函数,实现它需要有以下步骤:
def who_is_it(image_path, database,model):
encoding = fr_utils.img_to_encoding(image_path, model)
min_dist = 100
for (name,db_enc) in database.items():
dist = np.linalg.norm(encoding - db_enc)
if dist < min_dist:
min_dist = dist
identity = name
if min_dist > 0.7:
print("抱歉,您的信息不在数据库中。")
else:
print("姓名" + str(identity) + " 差距:" + str(min_dist))
return min_dist, identity
测试:
who_is_it("images/camera_0.jpg", database, FRmodel)
结果:
姓名younes 差距:0.659392
(0.65939206, 'younes')
人脸验证解决了更容易的1:1匹配问题,人脸识别解决了更难的1∶k匹配问题。
三重损失是训练神经网络学习人脸图像编码的一种有效的损失函数。
相同的编码可用于验证和识别。测量两个图像编码之间的距离可以确定它们是否是同一个人的图片。