什么是人脸验证?这可以认为是一个分类问题,用匹配分数来验证人的身份。如果两幅图像属于同一个人,那么它们应该有很高的匹配度;如果两幅图像属于两个不同的人,则匹配度应该较低。
你可能想到的第一件事就是,为什么不把拍摄的图像和另一个像素一一对应起来呢?如果捕获图像的像素值和另一个图像的像素值之间的距离(均方或绝对值)很小,则它们应该对应于同一个人。但由于光线、位置或方向的轻微变化,图像中的像素值也会发生较大变化,所以这种方法的效果其实并不好。
那我们现在怎么办?这就是卷积神经网络(俗称CNN)发挥作用的地方。通过将每个图像嵌入到D维向量空间中,这种网络可以帮助我们更好地表示图像。然后评价图像嵌入的相似性。
解决问题的一些方法SVM:这里的想法是为训练集中的每个例子训练一个线性SVM分类器。深:这里把验证任务看成人脸识别的一个子问题(给每个人分配标签)。暹罗网:这是基于以下思路:个体内部的距离要比人与人之间的距离小得多。在研究暹罗网之前,我们先讨论一下暹罗网所基于的一个非常重要的概念。这是一次性学习。
“一击学习”是一个物体分类问题,主要出现在计算机视觉中。它试图从一个或几个训练样本中学习关于对象分类的信息。通常在深度学习中,我们需要大量的机器学习数据。我们获得的机器学习数据越多,机器学习模型就越好。但是在人脸验证的情况下,一个人是不可能得到上千张照片的。实际上,我们的大脑不需要几千个人的照片来识别一个人。
对于人脸验证任务,我们希望系统能够从一张/几张图像中判断一个人的身份。
如前所述,卷积神经网络(CNN)有助于图像的矢量化表示。但是卷积神经网络(CNN)确实需要用很多例子来训练。此外,机器学习数据集中每增加一个新用户的图像,都给机器学习模型的训练带来不便。那么,为什么不建立一个模型来学习两个不同人之间的距离呢?这正是暹罗网络要做的。
图像x(1)馈入卷积神经网络(CNN),由卷积层和全连接层组成。卷积层提供了一个特征空间,而附着于卷积层的全连通层在这个空间中学习一个函数(大多是非线性的)。我们最终得到的是一个特征向量(softmax激活没有添加到特征向量中,因为它在这个阶段不会用于分类)。图像x(2)被输入到卷积神经网络(CNN ),该网络与上述完全相同。在我们的例子中,我们将第三个图像x(3)提供给同一个CNN。
我们选择x(1)作为锚图像,x(2)作为正图像,x(3)作为负图像。主播和正面形象属于同一个人,负面形象是另一个人。因此,我们的目标是使正面形象与主播的距离最小化,同时使主播与负面形象的距离最大化。
这个目标可以写成:
我们可以添加一个超级参数。
这里,A=x(1),P=x(2),N=x(3)
现在,我们如何把它构造成损失函数。
这就是所谓的三重损失函数。我们可以看到,这个损失函数可以保证max函数中的第一项不超过0。
Python实现了创建数据
这是用于创建机器学习数据集的Python代码。它使用一种预先训练好的分类器,称为哈尔正面人脸分类器,以级联方式识别人脸。下面的代码存储了20张由网络摄像头拍摄的人脸图像,并将它们存储在一个文件夹中。
导入osimport cv2#此函数可在注册用户定义create_dataset()期间使用: #包含预训练分类器的文件Haar _ file=' Haar scade _ front face _ default。XML ' #所有人脸数据都将出现在此文件夹中
dataset = './dataset' if not os.path.exists(dataset): os.mkdir(dataset) sub_data = input("Enter your username: ") # Use the username as path name path = os.path.join(dataset, sub_data) # Add a verfication for this step if not os.path.exists(path): os.mkdir(path) # Image to be resized to this shape (width, height) = (224, 224) # Make the cascade classifier object face_cascade = cv2.CascadeClassifier(haar_file) webcam = cv2.VideoCapture(0) # The program loops until it has 30 images of the face. count = 0 while count < 20: # Read from the webcam (_, im) = webcam.read() # Convert to grayscale gray = cv2.cvtColor(im, cv2.COLOR_BGR2RGB) # Detect the face faces = face_cascade.detectMultiScale(gray, 1.3, 4) face_resize = None for (x, y, w, h) in faces: # The classifier seemed to scrap the chin and hair. Adjustments made to accomodate those. face = im创建模型并进行训练
创建数据集时,您可以看到我在现有图像上填充了一些值(此处宽度为22,高度为12)。之所以如此,是因为使用了在ImageNet数据集上预训练的VGG16模型,该神经网络模型期望输入图像的尺寸为(224、224、3),而我们使用的数据集的每个图像都为(200、180、3)。
对于每个人(由数据集文件夹内的文件夹标识的人),我们存储5个(A,P,N)的triplets。然后对它们进行训练,该模型本身包含三个VGG16模型,并由一个实现三重损失函数的Lambda层连接。
# Importing the required modulesfrom keras import backend as K, modelsfrom keras.models import *from keras.layers import *from keras.layers.normalization import BatchNormalizationfrom keras.applications import VGG16from keras.regularizers import l2from keras.activations import reluimport osimport os.path import join as join_import numpy as npfrom PIL import Image# Setting up the datasetSET_DIR = 'dataset'NUM_CLASSES = len(os.listdir('dataset'))# The shape which VGG19 accepts as input and thus each image is resized toimage_shape = (224, 224, 3)# NUM_EXAMPLES is the number of (A,P,N) triplets chosen for the same class (N belongs to a different class of course)NUM_EXAMPLES = 5# Triplets list will contain anchor(A), positive(P) and negative(N) triplets.triplets = <>A = P = N = <># creating anchor, positive, negative tripletsfor _ in range(NUM_EXAMPLES): for direc in os.listdir(SET_DIR): dir_path = SET_DIR + direc dir_contents = os.listdir(dir_path) length = len(dir_contents) anchor = np.asarray(Image.open(join_(dir_path, dir_contents
使用模型进行验证
接收图像img(使用网络摄像头捕获的人的图像)的detect_face函数查找面部并将其裁剪。用验证如下:
a)我们在数据集文件夹中找到命名的文件夹。我们从该文件夹中选择一个图像。
b)我们随机选择另外三个文件夹,并从每个文件夹中选择一个图像。这些将作为negative images。
c)我们找到每个图像的编码,由detect_face返回,一个来自步骤a),三个来自步骤b)。
d)我们求出步骤a)和步骤b中每个图像编码的均方误差。
e)如果从步骤b)获得的任何图像的误差小于从步骤a)获得的图像的误差,我们说该人已被授权,否则就没有授权。
import osfrom os.path import join as jimport numpy as npimport matplotlib.pyplot as pltdef detect_face(img): # The file containing the pretrained classifier haar_file = 'haarcascade_frontalface_default.xml' # Image to be resized to this shape (width, height) = (224, 224) # Make the cascade classifier object face_cascade = cv2.CascadeClassifier(haar_file) # Convert to grayscale gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Detect the face faces = face_cascade.detectMultiScale(gray, 1.3, 4) face_resize = None for (x, y, w, h) in faces: # The classifier seemed to scrap the chin and hair. Adjustments made to accomodate those. face = img