• 基于FaceNet的人脸识别


    ⚠申明: 未经许可,禁止以任何形式转载,若要引用,请标注链接地址。 全文共计10077字,阅读大概需要10分钟
    🌈更多学习内容, 欢迎👏关注👀【文末】我的个人微信公众号:不懂开发的程序猿
    个人网站:https://jerry-jy.co/

    基于FaceNet的人脸识别


    一、任务需求

    人脸识别是一项计算机视觉任务,它根据人脸照片识别和验证人。FaceNet 是谷歌研究人员于 2015 年开发的人脸识别系统,该系统在一系列人脸识别基准数据集上取得了当时最先进的结果。由于模型的多个第三方开源实现和预训练模型的可用性,FaceNet 系统可以被广泛使用。FaceNet 系统可用于从人脸中提取高质量特征,称为人脸嵌入,然后可用于训练人脸识别系统。通过本实验,您将了解如何使用 FaceNet和SVM分类器开发人脸检测系统,以从照片中识别人物。

    二、任务目标

    1、了解人脸识别
    2、了解FaceNet 模型
    3、了解tensorflow.Keras中加载FaceNet模型
    4、了解检测人脸以进行人脸识别
    5、了解人脸识别的分类过程

    三、任务环境

    1、jupyter开发环境
    2、python3.6
    3、tensorflow2.4

    四、任务实施过程

    (一)、人脸检测

    FaceNet 是一种人脸识别系统,由Florian Schroff等人描述。在 Google 的 2015 年论文中,题为“ FaceNet:人脸识别和聚类的统一嵌入”。
    这是一个系统,给定一张人脸图片,将从人脸中提取高质量特征并预测这些特征的 128 个元素向量表示,称为人脸嵌入。
    本实验中,我们将使用Hiroki Taniai提供的预训练Keras FaceNet 模型。它在MS-Celeb-1M 数据集上进行了训练,具有 160×160 像素的正方形。
    与 IMDB 和 MNIST 类似,路透社数据集也内置为 tf.Keras 的一部分。我们查看训练集和测试集数据。下载模型文件并将其放置在./data目录中,文件名为“ facenet_keras.h5 ”

    1、使用load_model()函数直接在Keras 中加载模型

    import tensorflow as tf
    # example of loading the keras facenet model
    from tensorflow.keras.models import load_model
    tf.compat.v1.logging.set_verbosity('ERROR')
    # load the model
    model = load_model('./data/facenet_keras.h5')
    # summarize input and output shape
    print(model.inputs)
    print(model.outputs)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    [<KerasTensor: shape=(None, 160, 160, 3) dtype=float32 (created by layer 'input_1')>]
    [<KerasTensor: shape=(None, 128) dtype=float32 (created by layer 'Bottleneck_BatchNorm')>]
    
    • 1
    • 2

    运行示例加载模型并打印输入和输出张量的形状。我们可以看到,该模型确实期望正方形彩色图像作为形状为 160×160 的输入,并将输出人脸嵌入作为 128 个元素的向量。现在我们有了 FaceNet 模型,我们可以探索使用它。

    在进行人脸识别之前,我们需要检测人脸。人脸检测是自动定位照片中的人脸并通过在其范围周围绘制边界框来定位它们的过程。

    2、我们还将使用多任务级联卷积神经网络 (MTCNN) 进行人脸检测,例如从照片中查找和提取人脸。这是用于人脸检测的最先进的深度学习模型

    # demonstrate face detection on 5 Celebrity Faces Dataset
    from os import listdir
    from PIL import Image
    from numpy import asarray
    from matplotlib import pyplot
    from mtcnn.mtcnn import MTCNN
    # extract a single face from a given photograph
    def 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[0]['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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    第一步是将图像加载为 NumPy 数组,我们可以使用 PIL 库和open()函数来实现。我们还将图像转换为 RGB

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

    结果是一个边界框列表,其中每个边界框定义了边界框的左下角,以及宽度和高度。我们可以使用这些坐标来提取人脸。

    然后我们可以使用 PIL 库将这张人脸的小图像调整为所需的大小;具体来说,该模型需要形状为 160×160 的方形输入。

    3、执行人脸检测

    数据集是一个包含名人照片的小型数据集。

    它包括以下照片:本·阿弗莱克、埃尔顿·约翰、杰瑞·宋飞、麦当娜和明迪·卡灵。

    该数据集目录结果如下

    在这里插入图片描述

    # specify folder to plot
    folder = './data/train/ben_afflek/'
    i = 1
    # enumerate files
    for 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 += 1
    pyplot.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述
    运行该示例需要一点时间,并报告每张加载照片的进度以及包含面部像素数据的 NumPy 数组的形状。

    我们可以看到每张脸都被正确检测到,并且在检测到的脸中我们有一系列的光照、肤色和方向。

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

    4、下面的load_faces()函数会将所有人脸加载到给定目录的列表中

    # load images and extract faces for all images in a directory
    def 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

    5、下面的load_dataset()函数采用目录名称,并为每个子目录(名人)检测人脸,为每个检测到的人脸分配标签。

    # load a dataset that contains one subdir for each class that in turn contains images
    def 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    6、然后,我们可以为 ‘train’ 和 ‘val’ 文件夹调用此函数以加载所有数据,然后通过savez_compressed() 函数将结果保存在单个压缩的 NumPy 数组文件中。

    # face detection for the 5 Celebrity Faces Dataset
    from os import listdir
    from os.path import isdir
    from PIL import Image
    from matplotlib import pyplot
    from numpy import savez_compressed
    from numpy import asarray
    from mtcnn.mtcnn import MTCNN
    # load train dataset
    trainX, trainy = load_dataset('./data/train/')
    print(trainX.shape, trainy.shape)
    # load test dataset
    testX, testy = load_dataset('./data/val/')
    # save arrays to one file in compressed format
    savez_compressed('5-celebrity-faces-dataset.npz', trainX, trainy, testX, testy)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    >loaded 17 examples for class: elton_john
    >loaded 14 examples for class: ben_afflek
    >loaded 0 examples for class: .ipynb_checkpoints
    >loaded 22 examples for class: mindy_kaling
    >loaded 19 examples for class: madonna
    >loaded 21 examples for class: jerry_seinfeld
    (93, 160, 160, 3) (93,)
    >loaded 5 examples for class: elton_john
    >loaded 5 examples for class: ben_afflek
    >loaded 0 examples for class: .ipynb_checkpoints
    >loaded 5 examples for class: mindy_kaling
    >loaded 5 examples for class: madonna
    >loaded 5 examples for class: jerry_seinfeld
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    运行示例可能需要一些时间。

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

    然后将两个数据集保存到一个名为“ 5-celebrity-faces-dataset.npz ”的压缩 NumPy 数组文件中,该文件大小约为 3 兆字节,并存储在当前工作目录中。

    (二)、人脸嵌入

    面部嵌入是表示从面部提取的特征的向量。然后可以将其与为其他人脸生成的向量进行比较。

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

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

    # calculate a face embedding for each face in the dataset using facenet
    from numpy import load
    from numpy import expand_dims
    from numpy import asarray
    from numpy import savez_compressed
    from tensorflow.keras.models import load_model
    # load the face dataset
    data = 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    Loaded: (93, 160, 160, 3) (93,) (25, 160, 160, 3) (25,)

    2、接下来,我们可以加载我们的 FaceNet 模型,准备将人脸转换为人脸嵌入

    # load the facenet model
    model = load_model('./data/facenet_keras.h5')
    print('Loaded Model')
    
    • 1
    • 2
    • 3

    Loaded Model

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

    3、要预测嵌入,首先需要适当准备图像的像素值以满足 FaceNet 模型的期望。FaceNet 模型的这种特定实现期望像素值是标准化的。

    # get the face embedding for one face
    def 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]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

    然后我们可以使用该模型进行预测并提取嵌入向量。

    并在给定人脸的单个图像和加载的 FaceNet 模型的情况下返回人脸嵌入。

    4、执行人脸嵌入

    # convert each face in the train set to an embedding
    newTrainX = 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 embedding
    newTestX = 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 format
    savez_compressed('5-celebrity-faces-embeddings.npz', newTrainX, trainy, newTestX, testy)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    (93, 128)
    (25, 128)

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

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

    (三)、人脸识别

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

    # load dataset
    data = 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
    • 2
    • 3
    • 4

    Dataset: train=93, test=25

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

    首先,将人脸嵌入向量归一化是一个很好的做法,因为通常使用距离度量将向量相互比较。

    在这种情况下,向量归一化意味着缩放值,直到向量的长度或大小为 1 或单位长度。这可以使用scikit-learn 中的Normalizer 类来实现。如果在上一步中创建了人脸嵌入,则执行此步骤可能会更方便。

    2、数据归一化处理

    # develop a classifier for the 5 Celebrity Faces Dataset
    from numpy import load
    from sklearn.metrics import accuracy_score
    from sklearn.preprocessing import LabelEncoder
    from sklearn.preprocessing import Normalizer
    from sklearn.svm import SVC
    # normalize input vectors
    in_encoder = Normalizer(norm='l2')
    trainX = in_encoder.transform(trainX)
    testX = in_encoder.transform(testX)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3、接下来,需要将每个名人姓名的字符串目标变量转换为整数。这可以通过scikit-learn 中的LabelEncoder 类来实现。

    # label encode targets
    out_encoder = LabelEncoder()
    out_encoder.fit(trainy)
    trainy = out_encoder.transform(trainy)
    testy = out_encoder.transform(testy)
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

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

    4、拟合模型

    # fit model
    model = SVC(kernel='linear', probability=True)
    model.fit(trainX, trainy)
    
    • 1
    • 2
    • 3

    SVC(kernel=‘linear’, probability=True)

    5、接下来,我们可以评估模型。这可以通过使用拟合模型对训练和测试数据集中的每个示例进行预测,然后计算分类精度来实现。

    # predict
    yhat_train = model.predict(trainX)
    yhat_test = model.predict(testX)
    # score
    score_train = accuracy_score(trainy, yhat_train)
    score_test = accuracy_score(testy, yhat_test)
    # summarize
    print('Accuracy: train=%.3f, test=%.3f' % (score_train*100, score_test*100))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Accuracy: train=100.000, test=100.000

    我们可以通过绘制原始人脸来进行人脸预测。

    6、我们需要加载人脸数据集,特别是测试数据集中的人脸。

    # load faces
    data = load('5-celebrity-faces-dataset.npz')
    testX_faces = data['arr_2']
    
    • 1
    • 2
    • 3

    7、我们需要从测试集中随机选择一个示例,然后获取嵌入、人脸像素、预期类别预测以及对应的类别名称。

    from random import choice
    # test model on a random example from the test dataset
    selection = 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])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    8、我们可以预测类别整数和预测概率。

    # prediction for the face
    samples = expand_dims(random_face_emb, axis=0)
    yhat_class = model.predict(samples)
    yhat_prob = model.predict_proba(samples)
    
    • 1
    • 2
    • 3
    • 4

    9、然后我们可以获得预测类整数的名称,以及这个预测的概率

    # get name
    class_index = yhat_class[0]
    class_probability = yhat_prob[0,class_index] * 100
    predict_names = out_encoder.inverse_transform(yhat_class)
    
    • 1
    • 2
    • 3
    • 4

    10、然后打印这些信息

    print('Predicted: %s (%.3f)' % (predict_names[0], class_probability))
    print('Expected: %s' % random_face_name[0])
    
    • 1
    • 2

    Predicted: mindy_kaling (84.453)
    Expected: mindy_kaling

    11、我们还可以绘制面部像素以及预测的名称和概率。

    # plot for fun
    pyplot.imshow(random_face_pixels)
    title = '%s (%.3f)' % (predict_names[0], class_probability)
    pyplot.title(title)
    pyplot.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

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

    您的结果可能会因算法或评估程序的随机性或数值精度的差异而有所不同。

    五、任务小结

    本实验中,使用FaceNet和SVM 分类器开发人脸检测系统,以从照片中识别人物。在此过程中,我们使用了人脸检测、人脸嵌入和人脸识别技术和算法。通过
    本实验,您将了解:

    • 关于谷歌开发的 FaceNet 人脸识别系统以及实现和预训练模型。
    • 如何准备人脸检测数据集,包括人脸检测系统提取人脸,人脸嵌入提取人脸特征。
    • 如何拟合、评估和演示SVM模型以从人脸嵌入中预测身份。

    –end–

  • 相关阅读:
    Prometheus持久化安装
    基于CSP的运动想象EEG分类任务实战
    <Linux系统复习>共享内存
    强大的项目管理软件:OmniPlan Pro 4 mac中文版
    力扣日记11.7-【二叉树篇】二叉树的层序遍历
    了解被测系统(二)接入链路--包括域名解析和Nginx代理
    常见位运算公式使用场景
    史上最简单清晰的ThreadLocal讲解,呕心沥血,你确定不看吗?
    mongodb固定集合(Capped Collections) | MongoDB 自动删除集合中过期的数据——TTL索引
    用户案例 | 珍岛集团基于 Apache DolphinScheduler 打造智能营销云平台
  • 原文地址:https://blog.csdn.net/qq_44807756/article/details/138214421