写点什么

如何在 Keras 中使用 FaceNet 开发人脸识别系统

  • 2019-07-13
  • 本文字数:14963 字

    阅读完需:约 49 分钟

如何在 Keras 中使用 FaceNet 开发人脸识别系统


Keras 是一个用 Python 编写的高级神经网络 API,能够以 TensorFlow、CNTK 或 Theano 作为后端运行。FaceNet 是 Google 工程师 Florian Schroff、Dmitry Kalenichenko、James Philbin 等人于 2015 年开发的人脸识别系统,由于算法原理容易理解、应用方便,成了目前最为流行的人脸识别技术。今天,AI 前线带领大家跟随 Jason Brownlee 去学习如何在 Keras 中使用 FaceNet 开发人脸识别系统。


人脸识别是一项计算机视觉任务,根据人脸的照片来识别和验证某个人。


FaceNet 是 Google 研究人员于 2015 年开发的人脸识别系统,这个系统在一系列人脸识别基准数据集上取得了当时最好的成绩。由于该模型的多个第三方开源实现和预训练模型的可用性,FaceNet 系统得到了广泛的使用。


FaceNet 系统可从人脸中提取高质量的特征,称为人脸嵌入(face embeddings),可用于训练人脸识别系统。


在本教程中,你将了解如何使用 FaceNet 和 SVM 分类器开发人脸检测系统,来从照片中识别出身份。


完成本教程之后,你将了解:


关于 Google 开发的 FaceNet 人脸识别系统,以及开源实现和预训练模型。


如何准备人脸检测数据集,包括首先通过人脸检测系统提取人脸,然后通过人脸嵌入提取人脸特征。


如何拟合、评估和演示 SVM 模型从人脸嵌入的身份。


让我们开始吧。

教程概述

本教程分为五个部分,分别是:


  1. 人脸识别

  2. FaceNet 模型

  3. 如何在 Keras 中加载 FaceNet 模型

  4. 如何检测人脸进行人脸识别

  5. 如何开发人脸分类系统

人脸识别

人脸识别是从人脸照片中识别和验证人脸的一般任务。


2011 年出版的一本书 “Handbook of Face Recognition”(《人脸识别手册》)描述了人脸识别的两种主要模式:


  • 人脸验证:给定人脸与已知身份的一对一映射(例如,这幅人脸是此人吗?)

  • 人脸识别:给定人脸与已知人脸数据库的一对多映射(例如,此人是谁?)


人脸识别系统有望自动识别图像和视频中的人脸。它可以在两种模式中的一种或两种模式下进行操作:


(1)人脸验证(或身份验证);


(2)人脸识别(或身份识别)。

——第一页,Handbook of Face Recognition,2011 年。


在本教程中,我们将重点介绍人脸识别任务。

FaceNet 模型

FaceNet 是 Florian Schroff 等人在 Google 于 2015 年发表的论文 “FaceNet: A Unified Embedding for Face Recognition and Clustering”(《FaceNet:人脸识别与聚类的统一嵌入》)中描述的一种人脸识别系统。


这是一种系统,给定一张人脸的图片,系统就将从人脸中提取出高质量的特征,并预测 128 个元素向量表示这些特征,称为人脸嵌入。


FaceNet,它可以直接学习从人脸图像到紧凑的欧几里得空间的映射,其中距离直接对应于人脸相似性的度量。

——FaceNet: A Unified Embedding for Face Recognition and Clustering,2015 年。


该模型是通过三重损失函数训练的深度卷积神经网络,鼓励相同身份的向量变得更相似(距离更小),而不同身份的向量预期变得更不相似(距离更大)。将重点放在训练模型来直接创建嵌入(而不是从模型的中间层提取嵌入),是这项工作中的一个重要创新。


我们的方法使用经过训练的深度卷积网络直接优化嵌入本身,而不是像以前的深度学习方法那样使用中间的瓶颈层。

FaceNet: A Unified Embedding for Face Recognition and Clustering,2015 年。


然后,将这些人脸嵌入作为在标准人脸识别基准数据集上训练分类器系统的基础,从而得到当时最先进的结果。


与已公布的最好结果相比,我们的系统将错误率降低了 30%……

FaceNet: A Unified Embedding for Face Recognition and Clustering,2015 年。


这篇论文还探讨了嵌入的其他用途,如基于提取的特征聚类对相似人脸进行分组。


这是一种健壮而有效的人脸识别系统,提取的人脸嵌入的一般性质使得这种方法具有广泛的应用前景。

如何在 Keras 中加载 FaceNet 模型

有许多项目提供了用于训练基于 FaceNet 的模型和利用预训练模型的工具。


也许最著名的叫 OpenFace ,它提供了使用 PyTorch 深度学习框架构建和训练的 FaceNet 模型。有一个到 Keras 的 OpenFace 端口,叫做 Keras OpenFace,但在撰写本文时,这个模型似乎需要 Python 2,这就极大限制了它的应用。


另一个著名的项目是 David Sandberg 的 FaceNet,它提供了利用 TensorFlow 构建和训练的 FaceNet 模型。虽然在撰写本文时还没有提供基于库的安装,也没有提供干净的 API,但这个项目看起来已经成熟。有用的是,David 的项目提供了许多高性能的预训练 FaceNet 模型,并且有许多项目可以移植或转换这些模型,以便能够在 Keras 中使用。


一个值得注意的例子是 Hiroki Taniai 的 Keras FaceNet,他的项目提供了一个脚本,用于将 Inception ResNet v1 模型从 TensorFlow 转换为 Keras。他还提供了预训练 Keras 模型,可供随时使用。


在本教程中,我们将使用 Hiroki Taniai 提供的预训练 Keras FaceNet 模型,它是在 MS-Celeb-1M 数据集上训练的,并要求输入图像是彩色的,其像素值要进行白噪化(在所有三个通道中进行标准化),并且具有 160x160 像素的正方形。


Keras FaceNet 预训练模型 (88 MB) 下载网址如下:


https://drive.google.com/open?id=1pwQ3H4aJ8a6yyJHZkTwtjcL4wYWQb7bn


下载模型文件并将其放在当前工作目录中,文件名为 “facenet_keras.h5”。


我们可以使用 load_model()函数直接在 Keras 中加载模型,示例代码如下:


# example of loading the keras facenet modelfrom keras.models import load_model# load the modelmodel  =  load_model('facenet_keras.h5')# summarize input and output shapeprint(model.inputs)print(model.outputs)
复制代码


运行该示例代码将加载模型并打印输入和输出张量的形状。


我们可以看到,这个模型确实期望将正方形彩色图像作为具有形状为 160x160 的输入,并将输出一个包含 128 个元素向量的人脸嵌入。


# [<tf.Tensor 'input_1:0' shape=(?, 160, 160, 3) dtype=float32>]# [<tf.Tensor 'Bottleneck_BatchNorm/cond/Merge:0' shape=(?, 128) dtype=float32>]
复制代码


现在我们有了 FaceNet 模型,我们可以探索如何使用这个模型。

人脸识别中如何检测人脸

在进行人脸识别之前,需要对人脸进行检测。


人脸检测是自动定位照片中的人脸并通过在其范围内绘制边界框来定位这些人脸的过程。


在本教程中,我们还将使用多任务级联卷积神经网络(Multi-Task Cascaded Convolutional Neural Network,MTCNN)进行人脸检测,例如,从照片中查找并提取人脸。这是一种最先进的人脸检测深度学习模型,在 2016 年发表的论文《使用多任务级联卷积网络的联合人脸检测与对齐》(Joint Face Detection and Alignment Using Multitask Cascaded Convolutional Networks)有所描述。


我们将在 ipazc/mtcnn 项目中使用 Iván de Paz Centeno 提供的实施方案。这也可以通过 pip 进行安装,如下所示:


sudo pip install mtcnn
复制代码


我们可以通过导入库并打印版本来确认库是否已经正确安装,示例代码如下:


# confirm mtcnn was installed correctlyimport mtcnn# print versionprint(mtcnn.__version__)
复制代码


运行该示例代码将会打印库的当前版本。


0.0.8
复制代码


我们可以使用 mtcnn 库创建人脸检测器并提取人脸,以便在后续章节中与 FaceNet 人脸检测器模型一起使用。


第一步是以 NumPy 数组的形式加载图像,我们可以使用 PIL 库和 open() 函数来实现。我们还将图像转换为 RGB,以防图像出现 alpha 通道或变成黑白。


# load image from fileimage  =  Image.open(filename)# convert to RGB, if neededimage  =  image.convert('RGB')# convert to arraypixels  =  asarray(image)
复制代码


接下来,我们可以创建一个 MTCNN 人脸检测器类,并使用它来检测加载的照片中所有的人脸。


# create the detector, using default weightsdetector  =  MTCNN()# detect faces in the imageresults  =  detector.detect_faces(pixels)
复制代码


结果是一个边界框列表,其中每个边界框定义了边界框的左下角,以及宽度和高度。如果我们假设照片中只有一张人脸用于实验,我们可以确定边界框的像素坐标如下。有时候库会返回负像素索引,我认为这是一个 bug,可以通过取坐标的绝对值来解决这一问题。


# extract the bounding box from the first facex1,  y1,  width,  height  =  results[]('box')# bug fixx1,  y1  =  abs(x1),  abs(y1)x2,  y2  =  x1  +  width,  y1  +  height
复制代码


我们可以使用这些坐标来提取人脸。


# extract the faceface  =  pixels[y1:y2,  x1:x2]
复制代码


然后我们可以使用 PIL 库将这个人脸的小图像调整为所需的尺寸;具体而言,模型需要形状为 160x160 的正方形输入面。


# resize pixels to the model sizeimage  =  Image.fromarray(face)image  =  image.resize((160,  160))face_array  =  asarray(image)
复制代码


将所有这些结合在一起,函数 extract_face() 将从加载的文件名加载照片,并返回提取的人脸。它假定照片包含一张人脸,并将返回检测到的第一张人脸。


# function for face detection with mtcnnfrom PIL import Imagefrom numpy import asarrayfrom mtcnn.mtcnn import MTCNN
# extract a single face from a given photographdef extract_face(filename, required_size=(160, 160)): # load image from file image = Image.open(filename) # convert to RGB, if needed image = image.convert('RGB') # convert to array pixels = asarray(image) # create the detector, using default weights detector = MTCNN() # detect faces in the image results = detector.detect_faces(pixels) # extract the bounding box from the first face x1, y1, width, height = results[]('box') # bug fix x1, y1 = abs(x1), abs(y1) x2, y2 = x1 + width, y1 + height # extract the face face = pixels[y1:y2, x1:x2] # resize pixels to the model size image = Image.fromarray(face) image = image.resize(required_size) face_array = asarray(image) return face_array
# load the photo and extract the facepixels = extract_face('...')
复制代码


在下一节中,我们可以根据需要使用这个函数来提取人脸,这些人脸可以作为 FaceNet 模型的输入。

如何开发人脸分类系统

在本节中,我们将开发一个人脸检测系统来预测给定人脸的身份。


该模型将使用 5 Celebrity Faces Dataset 进行训练和测试,这个数据集包含五位不同名人的许多照片。


我们将使用 MTCNN 模型进行人脸检测,使用 FaceNet 模型为每个检测到的人脸创建人脸嵌入,然后我们将开发一个线性支持向量机(Linear Support Vector Machine (SVM),SVM)分类器模型来预测给定人脸的身份。

5 Celebrity Faces Dataset

The 5 Celebrity Faces Dataset 是一个包含名人照片的小型数据集。


这个数据集的名人照片包括: Ben AffleckElton JohnJerry SeinfeldMadonnaMindy Kaling


这个数据集是由 Dan Becker 收集整理并提供给 Kaggle,可免费下载。需要注意的是,下载这个数据集需要 Kaggle 账户。


5 Celebrity Faces Dataset, Kaggle.


下载数据集(可能需要 Kaggle 账户登陆),data.zip(3 MB),并将其解压到本地目录中,文件夹名称为“5-celebrity-faces-dataset”。


你现在应该有一个具有以下结构的目录(注意:某些目录名称中存在拼写错误,在本示例代码中这些拼写错误的名称保持原样):


5-celebrity-faces-dataset├── train│   ├── ben_afflek│   ├── elton_john│   ├── jerry_seinfeld│   ├── madonna│   └── mindy_kaling└── val    ├── ben_afflek    ├── elton_john    ├── jerry_seinfeld    ├── madonna    └── mindy_kaling
复制代码


我们可以看到一个训练数据集和验证或测试数据集。


查看目录中的一些照片,我们可以看到这些照片提供了各种方向、光照和各种大小的人脸。重要的是,每张照片都包含一张人脸。


我们将使用这个数据集作为分类器的基础,仅对 train 数据集进行训练,并对 val 数据集中的人脸进行分类。你可以使用相同的结构来开发使用你自己的照片的分类器。

检测人脸

第一步是检测每张照片中的人脸,并将数据集缩减为一系列人脸。


让我们测试一下前面小节中定义的人脸检测器函数,具体来说就是 extract_face()。


在 5-celebrity-faces-dataset/train/ben_afflek/文件夹中,我们可以看到在训练数据集中有 Ben Affleck 的 14 张照片。我们可以检测每张照片中的人脸,并创建每张包含 14 个人脸的图像,每张图像有两行,每行有七张照片。


完整的示例代码如下。


# demonstrate face detection on 5 Celebrity Faces Datasetfrom os import listdirfrom PIL import Imagefrom numpy import asarrayfrom matplotlib import pyplotfrom mtcnn.mtcnn import MTCNN
# extract a single face from a given photographdef extract_face(filename, required_size=(160, 160)): # load image from file image = Image.open(filename) # convert to RGB, if needed image = image.convert('RGB') # convert to array pixels = asarray(image) # create the detector, using default weights detector = MTCNN() # detect faces in the image results = detector.detect_faces(pixels) # extract the bounding box from the first face x1, y1, width, height = results[]('box') # bug fix x1, y1 = abs(x1), abs(y1) x2, y2 = x1 + width, y1 + height # extract the face face = pixels[y1:y2, x1:x2] # resize pixels to the model size image = Image.fromarray(face) image = image.resize(required_size) face_array = asarray(image) return face_array
# specify folder to plotfolder = '5-celebrity-faces-dataset/train/ben_afflek/'i = 1# enumerate filesfor filename in listdir(folder): # path path = folder + filename # get face face = extract_face(path) print(i, face.shape) # plot pyplot.subplot(2, 7, i) pyplot.axis('off') pyplot.imshow(face) i += 1pyplot.show()
复制代码


运行这段示例代码需要一点时间,并报告全程加载的每个照片的进度以及包含人脸像素数据的 NumPy 数据的形状。


1 (160, 160, 3)2 (160, 160, 3)3 (160, 160, 3)4 (160, 160, 3)5 (160, 160, 3)6 (160, 160, 3)7 (160, 160, 3)8 (160, 160, 3)9 (160, 160, 3)10 (160, 160, 3)11 (160, 160, 3)12 (160, 160, 3)13 (160, 160, 3)14 (160, 160, 3)
复制代码


将创建一个包含在 Ben Affleck 目录中检测到的人脸的图形。


我们可以看到,每张人脸都被正确地检测到了,并且我们在检测到的人脸中有不同的光照、肤色和方向。



从 the 5 Celebrity Faces Dataset 训练数据集中检测到的 Ben Affleck 的 14 张人脸的图


到目前为止,一切还顺利。


下一步,我们可以扩展这个示例代码,遍历给定数据集的每个子目录(例如“train”或“val”),提取出人脸,并为每个检测到的人脸准备一个以名称作为输出标签的数据集。


下面的 load_faces()函数将所有人脸加载到给定目录的列表中,例如“5-celebrity-faces-dataset/train/ben_afflek/*”。


# load images and extract faces for all images in a directorydef load_faces(directory):    faces  =  list()    # enumerate files    for  filename in  listdir(directory):        # path        path  =  directory  +  filename        # get face        face  =  extract_face(path)        # store        faces.append(face)    return  faces
复制代码


我们可以为“train”或“val” 文件夹中的每个子目录调用 load_faces()函数,每张人脸都有一个标签,至于名人的名字,我们可以从目录名中提取。


下面的 load_dataset()函数采用目录名称,如“5-celebrity-faces-dataset/train/”,并每个子


目录(名人)检测人脸,为每个检测到的人脸分配标签。


它以 NumPy 数据的形式返回数据集的 X 和 y 元素。


# load a dataset that contains one subdir for each class that in turn contains imagesdef load_dataset(directory):    X,  y  =  list(),  list()    # enumerate folders, on per class    for  subdir in  listdir(directory):        # path        path  =  directory  +  subdir  +  '/'        # skip any files that might be in the dir        if  not  isdir(path):            continue        # load all faces in the subdirectory        faces  =  load_faces(path)        # create labels        labels  =  [subdir for  _  in  range(len(faces))]        # summarize progress        print('>loaded %d examples for class: %s'  %  (len(faces),  subdir))        # store        X.extend(faces)        y.extend(labels)    return  asarray(X),  asarray(y)
复制代码


然后,我们可以为“train”和“val”文件夹调用这个函数来加载所有的数据,然后通过 savez_compressed() 函数将结果保存到一个压缩的 NupPy 数据文件中。


# load train datasettrainX,  trainy  =  load_dataset('5-celebrity-faces-dataset/train/')print(trainX.shape,  trainy.shape)# load test datasettestX,  testy  =  load_dataset('5-celebrity-faces-dataset/val/')print(testX.shape,  testy.shape)# save arrays to one file in compressed formatsavez_compressed('5-celebrity-faces-dataset.npz',  trainX,  trainy,  testX,  testy)
复制代码


将所有这些结合在一起,下面列出了检测 5 Celebrity Faces Dataset 数据集中所有人脸的完整示例代码。


# face detection for the 5 Celebrity Faces Datasetfrom os import listdirfrom os.path import isdirfrom PIL import Imagefrom matplotlib import pyplotfrom numpy import savez_compressedfrom numpy import asarrayfrom mtcnn.mtcnn import MTCNN
# extract a single face from a given photographdef extract_face(filename, required_size=(160, 160)): # load image from file image = Image.open(filename) # convert to RGB, if needed image = image.convert('RGB') # convert to array pixels = asarray(image) # create the detector, using default weights detector = MTCNN() # detect faces in the image results = detector.detect_faces(pixels) # extract the bounding box from the first face x1, y1, width, height = results[]('box') # bug fix x1, y1 = abs(x1), abs(y1) x2, y2 = x1 + width, y1 + height # extract the face face = pixels[y1:y2, x1:x2] # resize pixels to the model size image = Image.fromarray(face) image = image.resize(required_size) face_array = asarray(image) return face_array
# load images and extract faces for all images in a directorydef load_faces(directory): faces = list() # enumerate files for filename in listdir(directory): # path path = directory + filename # get face face = extract_face(path) # store faces.append(face) return faces
# load a dataset that contains one subdir for each class that in turn contains imagesdef load_dataset(directory): X, y = list(), list() # enumerate folders, on per class for subdir in listdir(directory): # path path = directory + subdir + '/' # skip any files that might be in the dir if not isdir(path): continue # load all faces in the subdirectory faces = load_faces(path) # create labels labels = [subdir for _ in range(len(faces))] # summarize progress print('>loaded %d examples for class: %s' % (len(faces), subdir)) # store X.extend(faces) y.extend(labels) return asarray(X), asarray(y)
# load train datasettrainX, trainy = load_dataset('5-celebrity-faces-dataset/train/')print(trainX.shape, trainy.shape)# load test datasettestX, testy = load_dataset('5-celebrity-faces-dataset/val/')# save arrays to one file in compressed formatsavez_compressed('5-celebrity-faces-dataset.npz', trainX, trainy, testX, testy)
复制代码


运行这段示例代码可能需要一些时间。


首先,加载“train”数据集中的所有照片,然后提取人脸,得到 93 个样本,其中正方形人脸输入和类标签字符串作为输出。然后加载“val ” 数据集,提供 25 个可用作测试数据集的样本。


然后,将这两个数据集保存到“5-celebrity-faces-dataset.npz”的压缩 NymPy 数组文件中,该文件大约 3MB,存在在当前的工作目录中。


>loaded 14 examples for class: ben_afflek>loaded 19 examples for class: madonna>loaded 17 examples for class: elton_john>loaded 22 examples for class: mindy_kaling>loaded 21 examples for class: jerry_seinfeld(93, 160, 160, 3) (93,)>loaded 5 examples for class: ben_afflek>loaded 5 examples for class: madonna>loaded 5 examples for class: elton_john>loaded 5 examples for class: mindy_kaling>loaded 5 examples for class: jerry_seinfeld(25, 160, 160, 3) (25,)
复制代码


该数据次已准备就绪,可提供给人脸检测模型。

创建人脸嵌入

下一步就是创建人脸嵌入。


人脸嵌入是一个向量,表示从人脸中提取的特征。然后,可以将其与为其他人脸生成的向量进行比较,例如,另一个距离较近的向量(通过某种标准)可能是同一个人,而距离较远的较量(通过某种标准)则可能是不同的人。


我们要开发的分类器模型将人脸嵌入作为输入,并预测该人脸的身份。FaceNet 模型将为给定的人脸图像生成此嵌入。


FaceNet 模型可以用作分类其本身的一部分,或者,我们可以用 FaceNet 模型对人脸进行预处理,创建可以存储并用作分类器模型输入的人脸嵌入。首选后一种方法,因为 FaceNet 模型既大又慢,不利于创建人脸嵌入。


因此,我们可以预先计算训练中所有人脸的人脸嵌入,并在我们的 5 Celebrity Faces Dataset 测试集(形式上为“val”)。


首先,我们可以使用 load() NumPy 函数加载检测到的人脸数据集。


# load the face datasetdata  =  load('5-celebrity-faces-dataset.npz')trainX,  trainy,  testX,  testy  =  data['arr_0'],  data['arr_1'],  data['arr_2'],  data['arr_3']print('Loaded: ',  trainX.shape,  trainy.shape,  testX.shape,  testy.shape)
复制代码


接下来,我们可以加载 FaceNet 模型,准备讲人脸转换为人脸嵌入。


# load the facenet modelmodel  =  load_model('facenet_keras.h5')print('Loaded Model')
复制代码


然后,我们可以枚举训练中的每一张人脸,并测试数据集来预测嵌入。


为了预测嵌入,首先需要适当地准备图像的像素值,以满足 FaceNet 模型的要求。FaceNet 模型的这种特性实现要求像素是标准化的。


# scale pixel valuesface_pixels  =  face_pixels.astype('float32')# standardize pixel values across channels (global)mean,  std  =  face_pixels.mean(),  face_pixels.std()face_pixels  =  (face_pixels  -  mean)  /  std
复制代码


为了对 Keras 中的每一个样本进行预测,我们必须扩展维数,使人脸数组成为一个样本。


# transform face into one samplesamples  =  expand_dims(face_pixels,  axis=0)
复制代码


然后,利用该模型进行预测,提取嵌入向量。


# make prediction to get embeddingyhat  =  model.predict(samples)# get embeddingembedding  =  yhat[0]
复制代码


下面定义的 get_embedding()函数实现了这些行为,并在给定一个人脸的图像和加载的 FaceNet 模型的情况下返回一个人脸嵌入。


# get the face embedding for one facedef get_embedding(model,  face_pixels):    # scale pixel values    face_pixels  =  face_pixels.astype('float32')    # standardize pixel values across channels (global)    mean,  std  =  face_pixels.mean(),  face_pixels.std()    face_pixels  =  (face_pixels  -  mean)  /  std    # transform face into one sample    samples  =  expand_dims(face_pixels,  axis=0)    # make prediction to get embedding    yhat  =  model.predict(samples)    return  yhat[0]
复制代码


将所有这些结合在一起,下面列出了将每个人脸转换成嵌入到训练和测试数据集中的人脸的完整示例代码。


# calculate a face embedding for each face in the dataset using facenetfrom numpy import loadfrom numpy import expand_dimsfrom numpy import asarrayfrom numpy import savez_compressedfrom keras.models import load_model
# get the face embedding for one facedef get_embedding(model, face_pixels): # scale pixel values face_pixels = face_pixels.astype('float32') # standardize pixel values across channels (global) mean, std = face_pixels.mean(), face_pixels.std() face_pixels = (face_pixels - mean) / std # transform face into one sample samples = expand_dims(face_pixels, axis=0) # make prediction to get embedding yhat = model.predict(samples) return yhat[0]
# load the face datasetdata = load('5-celebrity-faces-dataset.npz')trainX, trainy, testX, testy = data['arr_0'], data['arr_1'], data['arr_2'], data['arr_3']print('Loaded: ', trainX.shape, trainy.shape, testX.shape, testy.shape)# load the facenet modelmodel = load_model('facenet_keras.h5')print('Loaded Model')# convert each face in the train set to an embeddingnewTrainX = list()for face_pixels in trainX: embedding = get_embedding(model, face_pixels) newTrainX.append(embedding)newTrainX = asarray(newTrainX)print(newTrainX.shape)# convert each face in the test set to an embeddingnewTestX = list()for face_pixels in testX: embedding = get_embedding(model, face_pixels) newTestX.append(embedding)newTestX = asarray(newTestX)print(newTestX.shape)# save arrays to one file in compressed formatsavez_compressed('5-celebrity-faces-embeddings.npz', newTrainX, trainy, newTestX, testy)
复制代码


运行示例代码可以报告整个过程的进度。


我们可以看到,正确地加载了人脸数据集,模型也是如此。然后将训练数据集转换成 93 个人脸嵌入,每个嵌入由 128 个元素向量组成。测试数据集中的 25 个样本也被适当地转换为人脸嵌入。


然后将得到的数据集保存到压缩的 NumPy 数组中,该数组大约为 50KB,在当前的工作目录中,名为“5-celebrity-faces-embeddings.npz”


Loaded:  (93, 160, 160, 3) (93,) (25, 160, 160, 3) (25,)Loaded Model(93, 128)(25, 128)
复制代码


我们现在准备开发我们的人脸分类系统。

执行人脸分类

在本节中,我们将开发一个模型,将人脸嵌入分类为 5 Celebrity Faces Dataset 中已知的名人之一。


首先,我们必须加载人脸嵌入数据集。


# load datasetdata  =  load('5-celebrity-faces-embeddings.npz')trainX,  trainy,  testX,  testy  =  data['arr_0'],  data['arr_1'],  data['arr_2'],  data['arr_3']print('Dataset: train=%d, test=%d'  %  (trainX.shape[0],  testX.shape[0]))
复制代码


接下来,在建模之前,数据需要进行一些小小的准备。


首先,对人脸嵌入向量进行归一化是一种很好的做法。这是一个很好的实践,因为向量通常使用距离度量进行比较。


在这种情况下,向量归一化意味着对这些值进行缩放,直到向量的长度或幅值为 1 或单位长度。这可以通过 scikit-learn 的 Normalizer 类来实现。如果在前面步骤中创建人脸嵌入时,那么执行这一步骤可能会更方便。


# normalize input vectorsin_encoder  =  Normalizer(norm='l2')trainX  =  in_encoder.transform(trainX)testX  =  in_encoder.transform(testX)
复制代码


接下来,需要将每个名人姓名的字符串目标变量转换为整数。


这可以通过 scikit-learn 中的 LabelEncoder 类来实现。


# label encode targetsout_encoder  =  LabelEncoder()out_encoder.fit(trainy)trainy  =  out_encoder.transform(trainy)testy  =  out_encoder.transform(testy)
复制代码


接下来,我们可以拟合一个模型。


在处理归一化人脸嵌入输入时,通常使用线性支持向量机(SVM)。这是因为该方法在分离人脸嵌入向量方面非常有效。我们可以使用 scilit-learn 中的 SVC 类将线性 SVM 拟合到训练数据中,并将“kernel”属性设置为“linear”。我们可能还希望在以后进行预测时使用概率,可以通过将“probability”设置为“True”。


# fit modelmodel  =  SVC(kernel='linear')model.fit(trainX,  trainy)
复制代码


接下来,我们就可以对模型进行评估了。


这可以通过使用拟合模型对训练和测试数据集中的每个样本进行预测,然后计算分类正确率来实现。


# predictyhat_train  =  model.predict(trainX)yhat_test  =  model.predict(testX)# scorescore_train  =  accuracy_score(trainy,  yhat_train)score_test  =  accuracy_score(testy,  yhat_test)# summarizeprint('Accuracy: train=%.3f, test=%.3f'  %  (score_train*100,  score_test*100))
复制代码


将所有这些结合在一起,下面列出了在 5 Celebrity Faces Dataset 的人脸嵌入上拟合线性 SVM 的完整示例代码。


# develop a classifier for the 5 Celebrity Faces Datasetfrom numpy import loadfrom sklearn.metrics import accuracy_scorefrom sklearn.preprocessing import LabelEncoderfrom sklearn.preprocessing import Normalizerfrom sklearn.svm import SVC# load datasetdata  =  load('5-celebrity-faces-embeddings.npz')trainX,  trainy,  testX,  testy  =  data['arr_0'],  data['arr_1'],  data['arr_2'],  data['arr_3']print('Dataset: train=%d, test=%d'  %  (trainX.shape[0],  testX.shape[0]))# normalize input vectorsin_encoder  =  Normalizer(norm='l2')trainX  =  in_encoder.transform(trainX)testX  =  in_encoder.transform(testX)# label encode targetsout_encoder  =  LabelEncoder()out_encoder.fit(trainy)trainy  =  out_encoder.transform(trainy)testy  =  out_encoder.transform(testy)# fit modelmodel  =  SVC(kernel='linear',  probability=True)model.fit(trainX,  trainy)# predictyhat_train  =  model.predict(trainX)yhat_test  =  model.predict(testX)# scorescore_train  =  accuracy_score(trainy,  yhat_train)score_test  =  accuracy_score(testy,  yhat_test)# summarizeprint('Accuracy: train=%.3f, test=%.3f'  %  (score_train*100,  score_test*100))
复制代码


运行这段示例代码,确认训练和测试数据集中的样本数量正如我们所预期的那样。


然后,在训练和测试数据集上对模型进行了评估,结果表明,该模型具有较好的分类正确率。考虑到数据集的大小以及所使用的人脸检测和人脸识别模型的强大功能,因此能取得这样的结果并不奇怪。


Dataset: train=93, test=25Accuracy: train=100.000, test=100.000
复制代码


我们可以通过绘制原始人脸和预测,让它变得更有趣。


首先,我们需要加载人脸数据集,特别是测试数据集中的人脸。我们还可以加载原始照片,使它更有趣。


# load facesdata  =  load('5-celebrity-faces-dataset.npz')testX_faces  =  data['arr_2']
复制代码


在我们对模型进行拟合之前,示例代码的其余部分都是相同的。


首先,我们需要从测试集中随机选择一个样本,然后获取嵌入、人脸像素、期望的类预测以及类的相应名称。


# test model on a random example from the test datasetselection  =  choice([i  for  i  in  range(testX.shape[0])])random_face_pixels  =  testX_faces[selection]random_face_emb  =  testX[selection]random_face_class  =  testy[selection]random_face_name  =  out_encoder.inverse_transform([random_face_class])
复制代码


接下来,我们可以使用人脸嵌入作为输入,与拟合模型进行单个预测。


我们既可以预测类整数,也可以对预测的概率进行预测。


# prediction for the facesamples  =  expand_dims(random_face_emb,  axis=0)yhat_class  =  model.predict(samples)yhat_prob  =  model.predict_proba(samples)
复制代码


然后,我们可以得到预测数的名称,以及这个预测的概率。


# get nameclass_index  =  yhat_class[0]class_probability  =  yhat_prob[0,class_index]  *  100predict_names  =  out_encoder.inverse_transform(yhat_class)
复制代码


然后我们可以打印这些信息。


print('Predicted: %s (%.3f)'  %  (predict_names[0],  class_probability))print('Expected: %s'  %  random_face_name[0])
复制代码


我们还可以根据预测的名字和概率进行绘制人脸像素。


# plot for funpyplot.imshow(random_face_pixels)title  =  '%s (%.3f)'  %  (predict_names[0],  class_probability)pyplot.title(title)pyplot.show()
复制代码


将所有这些结合在一起,下面列出了用于预测测试数据集中给定未公开照片的身份的完整实例代码。


# develop a classifier for the 5 Celebrity Faces Datasetfrom random import choicefrom numpy import loadfrom numpy import expand_dimsfrom sklearn.preprocessing import LabelEncoderfrom sklearn.preprocessing import Normalizerfrom sklearn.svm import SVCfrom matplotlib import pyplot# load facesdata  =  load('5-celebrity-faces-dataset.npz')testX_faces  =  data['arr_2']# load face embeddingsdata  =  load('5-celebrity-faces-embeddings.npz')trainX,  trainy,  testX,  testy  =  data['arr_0'],  data['arr_1'],  data['arr_2'],  data['arr_3']# normalize input vectorsin_encoder  =  Normalizer(norm='l2')trainX  =  in_encoder.transform(trainX)testX  =  in_encoder.transform(testX)# label encode targetsout_encoder  =  LabelEncoder()out_encoder.fit(trainy)trainy  =  out_encoder.transform(trainy)testy  =  out_encoder.transform(testy)# fit modelmodel  =  SVC(kernel='linear',  probability=True)model.fit(trainX,  trainy)# test model on a random example from the test datasetselection  =  choice([i  for  i  in  range(testX.shape[0])])random_face_pixels  =  testX_faces[selection]random_face_emb  =  testX[selection]random_face_class  =  testy[selection]random_face_name  =  out_encoder.inverse_transform([random_face_class])# prediction for the facesamples  =  expand_dims(random_face_emb,  axis=0)yhat_class  =  model.predict(samples)yhat_prob  =  model.predict_proba(samples)# get nameclass_index  =  yhat_class[0]class_probability  =  yhat_prob[0,class_index]  *  100predict_names  =  out_encoder.inverse_transform(yhat_class)print('Predicted: %s (%.3f)'  %  (predict_names[0],  class_probability))print('Expected: %s'  %  random_face_name[0])# plot for funpyplot.imshow(random_face_pixels)title  =  '%s (%.3f)'  %  (predict_names[0],  class_probability)pyplot.title(title)pyplot.show()
复制代码


每次运行代码时,都会从测试数据集中选择一个不同的随机样本。


让我们试着运行几次。


在这种情况下,选择 Jerry Seinfeld 的一张照片并做出正确的预测。


Predicted: jerry_seinfeld (88.476)Expected: jerry_seinfeld
复制代码


此外,还创建了所选人脸的一幅图,在这幅图像的标题中显示了预测的名称和概率。



用支持向量机分类器正确识别 Jerry Seinfeld 的人脸

延伸阅读

如果你希望深入了解,本节将提供更多的参考资源。

论文

书籍

项目

API


作者介绍:


Jason Brownlee 博士,机器学习专家,他通过实践教程教授开发人员如何使用现代机器学习方法来获得结果。


原文链接:How to Develop a Face Recognition System Using FaceNet in Keras


公众号推荐:

跳进 AI 的奇妙世界,一起探索未来工作的新风貌!想要深入了解 AI 如何成为产业创新的新引擎?好奇哪些城市正成为 AI 人才的新磁场?《中国生成式 AI 开发者洞察 2024》由 InfoQ 研究中心精心打造,为你深度解锁生成式 AI 领域的最新开发者动态。无论你是资深研发者,还是对生成式 AI 充满好奇的新手,这份报告都是你不可错过的知识宝典。欢迎大家扫码关注「AI前线」公众号,回复「开发者洞察」领取。

2019-07-13 21:148514
用户头像

发布了 368 篇内容, 共 170.4 次阅读, 收获喜欢 939 次。

关注

评论

发布
暂无评论
发现更多内容

【资损】知名金融企业的资损与资损防控

小明Java问道之路

架构 安全 金融科技 10月月更 资损

【一Go到底】第十九天---init函数、匿名函数

指剑

Go golang 10月月更

日志管理与分析系统的基本功能

阿泽🧸

日志管理 10月月更

让Jenkins执行GitHub上的pipeline脚本

程序员欣宸

GitHub jenkins 10月月更

高效能敏捷交付团队反思:特性团队(FeatureTeam)+Scrum

laofo

DevOps 敏捷 研发效能 持续交付 敏捷研发

leetcode 380. Insert Delete GetRandom O(1) O(1) 时间插入、删除和获取随机元素 (中等)

okokabcd

LeetCode 数据结构与算法

前端关于面试你可能需要收集的面试题

loveX001

JavaScript

React组件之间的通信方式总结(下)

beifeng1996

React

C++中变化布局实现思路

中国好公民st

c++ 布局 10月月更

翟佳:StreamNative 组织构建之路丨声网开发者创业讲堂 • 第 5 期

声网

技术管理 人工智能’

抢滩东南亚,融云IM助力应用抓住经济转型红利

融云 RongCloud

互联网 数字化 IM

数据湖(四):Hudi与Spark整合

Lansonli

Hudi 10月月更

CorelDRAW 2019 软件应用项目(五)

张立梵

设计师 CorelDRAW 2022 10月月更

React源码分析2-深入理解fiber

goClient1992

React

java部分基础总结

Studying_swz

java 编程 10月月更

Java多线程 关闭线程池 shutdown() 、shutdownNow()、awaitTermination()

Yeats_Liao

后端 Java core 10月月更

Linux中的目录结构是什么样的?有人说像“树”,你觉得呢

wljslmz

Linux 10月月更 目录结构

改变线程状态的方法

急需上岸的小谢

10月月更

js异步编程面试题你能答上来几道

loveX001

JavaScript

All Eyes on Docs! 练就火眼金睛,就来StarRocks 极客营

StarRocks

数据库

ZooKeeper数据模型

穿过生命散发芬芳

zookeeper 10月月更

挑战 30 天学完 Python:Day6 数据类型 - 元组tuple

MegaQi

Python 挑战30天学完Python 10月月更

华为云从入门到实战 | 云速建站服务与企业主机安全服务

TiAmo

华为 华为云 云开发 10月月更

Go 语言入门很简单:Go 语言的错误处理

宇宙之一粟

异常处理 错误处理 Go 语言 10月月更

Java多线程 线程池的生命周期及运行状态

Yeats_Liao

后端 Java core 10月月更

画一个冰糖葫芦祝大家甜甜蜜蜜

急需上岸的小谢

10月月更

Java注解

急需上岸的小谢

10月月更

2022年ArchSummit全球架构师峰会杭州站感想

谙忆

ArchSummit 飞链云 架构师峰会

CorelDRAW 2019 软件应用项目(六)

张立梵

设计师 CorelDRAW 2022 10月月更

promise执行顺序面试题令我头秃,你能作对几道

loveX001

JavaScript

React源码分析1-jsx转换及React.createElement

goClient1992

React

如何在 Keras 中使用 FaceNet 开发人脸识别系统_AI&大模型_Jason Brownlee_InfoQ精选文章