吴恩达深度神经网络笔记—人脸识别

人脸识别

目标:
实现三元组损失函数。
使用一个已经训练好了的模型来将人脸图像映射到一个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)

将人脸图像编码为128位的向量

初始模型使用由Szegedy et al.等人的初始模型,加载预训练权值,可以在inception_blocks.py文件来查看是如何实现的。
关键:

  1. 该网络使用了 96 × 96的RGB图像作为输入数据,图像数量为 m,输入的数据维度为 ( m , nc , nh , nw ) = ( m , 3 , 96 , 96 )。
  2. 输出为 ( m , 128 ) (m,128) (m,128)的已经编码的 m m m个 128 128 128位的向量。

创建一个人脸识别的模型:

FRmodel = faceRecoModel(input_shape=(3,96,96))
print("参数数量:" + str(FRmodel.count_params()))

结果:

参数数量:3743280

使用128神经元全连接层作为最后一层,该模型确保输出是大小为128的编码向量,然后使用比较两个人脸图像的编码:
在这里插入图片描述

通过计算两个编码和阈值之间的误差,可以确定这两幅图是否代表同一个人。

三元组损失函数将上面的形式实现,它会试图将同一个人的两个图像(对于给定的图和正例)的编码“拉近”,同时将两个不同的人的图像(对于给定的图和负例)进一步“分离”。
满足:

同一个人的两个图像的编码非常相似。
两个不同人物的图像的编码非常不同。

在这里插入图片描述

三元组损失函数

什么是三元组损失函数:
使用三元组图像(A,P,N)进行训练:

  1. A是“Anchor”,是一个人的图像。
  2. P是“Positive”,是相对于“Anchor”的同一个人的另外一张图像。
  3. N是“Negative”,是相对于“Anchor”的不同的人的另外一张图像。

这些三元组来自训练集,我们使用 ( A( i ), P( i ), N( i )) 来表示第i个训练样本。我们要保证图像A( i )与图像P( i )的差值至少比与图像 N ( i ) 的差值相差 α(阈值):

在这里插入图片描述

将上面式子化为损失函数求最小:
在这里插入图片描述
注意点:

  1. 公式(1)是给定三元组A与正例P之间的距离的平方,我们要让它变小。
  2. 公式(2)是给定三元组A与负例N之间的距离的平方,我们要让它变大。
  3. α是间距,是超参数,这里我们使用 α = 0.2。

逐步实现:

  1. 计算"anchor" 与 "positive"之间编码的距离:在这里插入图片描述

  2. 计算"anchor" 与 "negative"之间编码的距离:
    在这里插入图片描述

  3. 根据公式计算每个样本的值:
    在这里插入图片描述

  4. 通过取带零的最大值和对训练样本的求和来计算整个公式:
    在这里插入图片描述

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)是否与身份证上的名称匹配,这个部分可由以下步骤构成:

  1. 根据image_path来计算编码。
  2. 计算与存储在数据库中的身份图像的编码的差距。
  3. 如果差距小于0.7,那么就打开门,否则就不开门。

我们使用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()函数,实现它需要有以下步骤:

  1. 根据image_path计算图像的编码。
  2. 从数据库中找出与目标编码具有最小差距的编码。
    初始化min_dist变量为足够大的数字(100),它将找到与输入的编码最接近的编码。
    遍历数据库中的名字与编码,可以使用for (name, db_enc) in database.items()语句。
    计算目标编码与当前数据库编码之间的L2差距。
    如果差距小于min_dist,那么就更新名字与编码到identity与min_dist中。
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匹配问题。

三重损失是训练神经网络学习人脸图像编码的一种有效的损失函数。

相同的编码可用于验证和识别。测量两个图像编码之间的距离可以确定它们是否是同一个人的图片。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值