一文教你在Python中打造你自己專屬的面部識別系統
人臉識別是使用者身份驗證的最新趨勢。蘋果推出的新一代iPhone X使用面部識別技術來驗證使用者身份。百度也在使“刷臉”的方式允許員工進入辦公室。對於很多人來說,這些應用程式有一種魔力。但在這篇文章中,我們的目的是通過教你如何在Python中製作你自己的面部識別系統的簡化版本來揭開這個主題的神祕性。
- Github庫程式碼:https://github.com/Skuldur/facenet-face-recognition
背景
在討論實現的細節之前,我想討論FaceNet的細節,它是我們將在我們的系統中使用的網路。
FaceNet FaceNet是一個神經網路,它可以學習從臉部影象到緊湊的歐幾里得空間(Euclidean space)的對映,在這個空間裡,距離對應的是人臉的相似性。也就是說,兩張面部影象越相似,它們之間的距離就越小。
Triplet Loss FaceNet使用了一種名為Triplet Loss的獨特的損失方法來計算損失。Triplet Loss最小化了anchor與正數之間的距離,這些影象包含相同的身份,並最大化了anchor與負數之間的距離,這些影象包含不同的身份。
圖1: Triplet Loss等式
- f(a)指的是anchor的輸出編碼
- f(p)指的是正的輸出編碼
- f(n)指的是負的輸出編碼
- α是一個常量,用來確保網路不會對f(a)-f(p)=f(a)-f(n)=0進行優化
- […]+等於最大值(0,總和)
Siamese網路
圖2:一個Siamese網路的例子,它使用面部影象作為輸入,輸出一個128位數字編碼的影象。
FaceNet是一個Siamese網路。Siamese網路是一種神經網路體系結構,它學習如何區分兩個輸入。這使他們能夠了解哪些影象是相似的,哪些不是。這些影象可以包含面部影象。
Siamese網路由兩個完全相同的神經網路組成,每個神經網路都有相同的權重。首先,每個網路將兩個輸入影象中的一個作為輸入。然後,每個網路的最後一層的輸出被髮送到一個函式,該函式決定這些影象是否包含相同的身份。
在FaceNet中,這是通過計算兩個輸出之間的距離來完成的。
實現
既然我們已經闡明瞭這個理論,我們就可以直接去實現這個過程。在我們的實現中,我們將使用Keras和Tensorflow。此外,我們還使用了從deeplearning.ai的repo中得到的兩個實用程式檔案來為所有與FaceNet網路的互動做了個摘要:
- fr_utils.py包含了向網路提供影象的函式,並獲取影象的編碼
- inception_blocks_v2.py包含了準備和編譯FaceNet網路的函式
Kera地址:https://keras.io/
Tensorflow地址:https://www.tensorflow.org/
deeplearning.ai的repo地址:https://github.com/shahariarrabby/deeplearning.ai/tree/master/COURSE%204%20Convolutional%20Neural%20Networks/Week%2004/Face%20Recognition
編譯FaceNet網路 我們要做的第一件事就是編譯FaceNet網路,這樣我們就可以在面部識別系統中使用它。
import os
import glob
import numpy as np
import cv2
import tensorflow as tf
from fr_utils import *
from inception_blocks_v2 import *
from keras import backend as K
K.set_image_data_format('channels_first')
FRmodel = faceRecoModel(input_shape=(3, 96, 96))
def triplet_loss(y_true, y_pred, alpha = 0.3):
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.0))
return loss
FRmodel.compile(optimizer = 'adam', loss = triplet_loss, metrics = ['accuracy'])
load_weights_from_FaceNet(FRmodel)
我們將以一個(3,96,96)的輸入形狀開始初始化我們的網路。這意味著紅-綠-藍(RGB)通道是向網路饋送的影象卷(image volume)的第一個維度。所有被饋送網路的影象必須是96×96畫素的影象。
接下來,我們將定義Triplet Loss函式。上面的程式碼片段中的函式遵循我們在上一節中定義的Triplet Loss方程的定義。
一旦我們有了損失函式,我們就可以使用Keras來編譯我們的面部識別模型。我們將使用Adam優化器來最小化由Triplet Loss函式計算的損失。
- Adam優化器:https://keras.io/optimizers/#adam
準備一個數據庫 現在我們已經編譯了FaceNet,我們準備一個我們希望我們的系統能夠識別的個人資料庫。我們將使用影象目錄中包含的所有影象,以供我們的個人資料庫使用。
注意:我們將只在實現中使用每個單獨的影象。原因是FaceNet網路強大到只需要一個單獨的影象就能識別它們!
def prepare_database():
database = {}
for file in glob.glob("images/*"):
identity = os.path.splitext(os.path.basename(file))[0]
database[identity] = img_path_to_encoding(file, FRmodel)
return database
對於每個影象,我們將把影象資料轉換為128個浮點數的編碼。我們通過呼叫函式img_path_to_encoding來實現這一點。該函式接受一個影象的路徑,並將影象輸入到我們的面部識別網路中。然後,它返回來自網路的輸出,這恰好是影象的編碼。
一旦我們將每個影象的編碼新增到我們的資料庫,我們的系統就可以開始識別人臉了!
識別人臉 正如在背景部分所討論的,FaceNet被訓練來儘可能地最小化同一個體的影象之間的距離,並使不同個體之間的影象之間的距離最大化。我們的實現使用這些資訊來確定為我們的系統饋送的新影象最有可能是哪一個個體。
def who_is_it(image, database, model):
encoding = img_to_encoding(image, model)
min_dist = 100
identity = None
# Loop over the database dictionary's names and encodings.
for (name, db_enc) in database.items():
dist = np.linalg.norm(db_enc - encoding)
print('distance for %s is %s' %(name, dist))
if dist < min_dist:
min_dist = dist
identity = name
if min_dist > 0.52:
return None
else:
return identity
上面的函式將新影象輸入到一個名為img_to_encoding的效用函式中。該函式使用FaceNet處理影象,並返回影象的編碼。既然我們有了編碼,我們就能找到最可能屬於這個影象的那個個體。
為了找到個體,我們通過資料庫,計算新影象和資料庫中的每個個體之間的距離。在新影象中距離最小的個體被選為最有可能的候選。
最後,我們必須確定候選影象和新影象是否包含相同的物件。因為在我們的迴圈結束時,我們只確定了最有可能的個體。這就是下面的程式碼片段所發揮的作用。
if min_dist > 0.52:
return None
else:
return identity
- 如果距離大於0.52,那麼我們將確定新影象中的個體不存在於我們的資料庫中。
- 但是,如果距離等於或小於0.52,那麼我們確定它們是相同的個體!
這裡比較棘手的部分是,值0.52是通過在我的特定資料集上反覆實驗來實現的。最好的值可能要低得多或稍微高一些,這取決於你的實現和資料。我建議嘗試不同的值,看看哪個值最適合你的系統!
使用面部識別建立一個系統 在這篇文章的開頭,我連結到的Github庫中的程式碼是一個演示,它使用膝上型電腦的攝像頭來為我們的面部識別演算法饋送視訊幀。一旦演算法識別出框架中的一個人,演示就會播放一個音訊資訊,它允許使用者在資料庫中使用它們的影象名稱。圖3顯示了演示示例。
圖3:當網路在圖片中識別出個體時,圖片即時被捕捉。資料庫中影象的名稱是“skuli.jpg”,因此播出的音訊資訊是“Welcome skuli, have a nice day!”
結論
現在,你應該熟悉了面部識別系統的工作方式,以及如何使用python中的FaceNet網路的預先訓練版本來建立你自己的簡化的面部識別系統。如果你想在Github庫中進行演示,並新增你認識的人的影象,那麼就可以繼續使用這個庫來進行你的下一次實驗。