人臉辨識模型 Google Facenet 介紹與使用

作者:CH.Tseng

Google 於 2015 年所提出的人臉辨識系統 Facenet,由於演算原理容易理解且應用方便,成為目前最流行的臉部識別技術。本篇文章介紹 Facenet 的辨識模型和架構,並在 Keras 環境下實際測試。

近年來透過深度學習以及CNN,不但開啟了人臉辨識領域的新紀元,也讓傳統的LBP、HOG搭配支援向量機SVM的辨識方法顯得落伍。這些複雜的深度學習模型不但辨識率極高,而且能識別的人數級別與相片總量也都以萬級來計算,若侷限於學術的實驗情境下與人眼來比較,這成績已經遠遠抛開我們肉眼的能力了。

2015年由Google所提出的Facenet,在LFW人臉資料庫以99.63%的最佳成績刷新了記錄,由於其易於理解的演算原理以及應用方便,使得Facenet在眾多競爭者中(如DeepFace、DeepID、Face++…等)異軍突起,成為目前最流行的臉部識別技術。下面,我們大致來瞭解一下Facenet,並且學習如何來使用它。

Ps. LFW資料集中收錄了5749位公眾人物的人臉影像,總共有超過一萬三千多張影像檔案。但大部份公眾人物的影像都只有一張,只有1680位有超過一張照片,而極少數有超過10張照片(台大計算中心網站連結)。

輸出量化特徵值,而非輸出分類結果

下圖是標準的臉部辨識作業流程,首先1.針對輸入的影片或相片進行臉孔偵測Face Detection,接著2.進行臉部校正對齊Face Alignment,然後開始3.依據不同的算法來取得臉部的特徵Feature Extraction,有了特徵,傳統的作法是4.據此來計算與匹配不同的臉孔圖片,最後5.透過softmax輸出至各分類結果,但Facenet則不然,模型所輸出的是該臉孔特徵的歐式距離總和。

臉部辨識作業流程

由於Facenet模型輸出的是量化的數值,因此我們就能利用此數值來比對多張臉孔的差異度,並應用於如下的人臉辨識領域,顯示出Facenet相較其它的技術更為通用。

  1. Face Verification 驗証是否為同一人
  2. Face Identification 辨識身份或姓名
  3. Face Cluster 相似的人分類在一起
  4. Face Search 搜索相似的人
  5. Face Tracking  跟蹤特定的人臉

Triplet Loss:模型的核心

模型需要取得圖片不同的特徵並映射到歐幾里德的空間中(即計算特徵間的歐氏距離),所採用的作法是Triplet loss,如下圖原論文中的圖示說明。

Triplet loss的運作模式

Triplet Loss想法在於,如果我們選取與該樣本最相像(差異最小)的錯誤臉孔,與最不像(差異度大)的正確臉孔,同時進行特徵訓練,迭代訓練至誤差降至最小,用以改進模型中的臉孔特徵定義,如此一來,便能得到最佳的辨識結果。

Facenet架構

Facenet主要由以下步驟所組成:

Facenet的組成步驟

  1. Batch → Batch input layer 將臉孔圖片輸入模型,這些圖片需已經Facial alignment及resize處理。原開發者使用MTCNN來進行臉孔偵測及校準
  2. Deep Architecture → 即用於特徵學習的CNN架構,具彈性可以採用不同的網路。剛推出時使用Zeiler&Fergus架構和Google的Inception v1,今年最新版改為Inception ResNet-v2。不同架構對於辨識結果有顯著的影響,如下圖為使用不同的network model的辨識成績差異。

使用不同的network model的辨識成績差異

  1. L2 → L2 normalization 歸一化,讓資料能對應到一個超平面。
  2. Embedding → 經過CNN模型以及L2歸一化後生成的特徵向量
  3. ​Triplet Loss → 從向量中取得一個embedding函數f(x),讓相同孔之間的特徵距離要盡可能的小,而不同孔之間的特徵距離要盡可能的大。

在Keras環境使用Facenet

Facenet使用Tensorflow開發,不過已有很多有心人士另外撰寫為Cafffe或Keras等版本,如果我們想要在Keras環境中使用,推薦可以採用keras-facenet。作者提供了一個預訓練好的Keras model可直接下載使用。當然也可以下載其它的預訓練models,再透過作者提供的轉檔程式tf_to_keras.ipynb轉為.h5 model來使用。

下載預訓練模型

作者提供的預訓練模型是使用MS-Celeb-1M dataset訓練,用於一般環境的辨識來說效果已相當不錯,並不一定要自行搜集臉孔資料來訓練。MS-Celeb-1M是微軟於2016年6月所公開發佈的人臉資料庫,包含100萬個名人總共約800多萬張人臉影像。

下面說明如何在使用Keras版本的Facenet:

  • Step1. git clone (載點
  • Step2. 下載(載點)預訓練模型,將該檔facenet_keras.h5放置於model/keras/目錄下

程式說明

我改編了作者用於示範的.pynb程式,可方便驗證compares列表中相片的人與valid相片中的人,其差異度為多少。

程式碼

import numpy as np

import os, time

import cv2

from skimage.transform import resize

from scipy.spatial import distance

from keras.models import load_model

#驗證compares列表中相片的人與valid相片中的人

valid = “200127/200127_1.jpg”

compares = [“200002/200002_1.jpg”, “200127/200127_1″, “200127/o.jpg” ]

#用OpenCV的Cascade classifier來偵測臉部,不一定跟Facenet一樣要用MTCNN。

cascade_path = ‘haarcascade_frontalface_default.xml

#我們的人像相片都放置於validPicPath

validPicPath = ‘members/’

#此版Facenet model需要的相片尺寸為160×160

image_size = 160

#使用MS-Celeb-1M dataset pretrained好的Keras model

model_path = ‘model/facenet_keras.h5

model = load_model(model_path)

#————————————————————

#圖像白化(whitening)可用於對過度曝光或低曝光的圖片進行處理,處理的方式就是改變圖像的平均像素值為 0 ,改變圖像的方差為單位方差 1。

def prewhiten(x):

if x.ndim == 4:

axis = (1, 2, 3)

size = x[0].size

elif x.ndim == 3:

axis = (0, 1, 2)

size = x.size

else:

raise ValueError(‘Dimension should be 3 or 4’)

mean = np.mean(x, axis=axis, keepdims=True)

std = np.std(x, axis=axis, keepdims=True)

std_adj = np.maximum(std, 1.0/np.sqrt(size))

y = (x – mean) / std_adj

return y

#使用L1或L2標準化圖像,可強化其特徵。

def l2_normalize(x, axis=-1, epsilon=1e-10):

output = x / np.sqrt(np.maximum(np.sum(np.square(x), axis=axis, keepdims=True), epsilon))

return output

#偵測並取得臉孔area,接著再resize為模型要求的尺寸(下方例子並未作alignment)

def align_image(img, margin):

cascade = cv2.CascadeClassifier(cascade_path)

faces = cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors=3)

if(len(faces)>0):

(x, y, w, h) = faces[0]

face = img[y:y+h, x:x+w]

faceMargin = np.zeros((h+margin*2, w+margin*2, 3), dtype = “uint8″)

faceMargin[margin:margin+h, margin:margin+w] = face

cv2.imwrite(str(time.time())+”.jpgceMargin)

aligned = resize(faceMargin, (image_size, image_size), mode=’reflect’)

cv2.imwrite(str(time.time())+”_aligned.jpgigned)

return aligned

else:

return None

#圖像的預處理(即前述的幾項步驟)

def preProcess(img):

whitenImg = prewhiten(img)

whitenImg = whitenImg[np.newaxis, :]

return whitenImg

#————————————————-

imgValid = validPicPath + valid

aligned = align_image(cv2.imread(imgValid), 6)

if(aligned is None):

print(“Cannot find any face in image: {}”.format(imgValid))

else:

faceImg = preProcess(aligned)

#–> model會輸出128維度的臉孔特徵向量,接著我們將它們合併並進行L2正規化。Z

embs_valid = l2_normalize(np.concatenate(model.predict(faceImg)))

#同上方的valid圖片,依序取得各圖片人臉的臉孔特徵向量,再與valid進行歐氏距離計算。

for member in compares:

img_file = validPicPath + member

aligned = align_image(cv2.imread(img_file), 6)

if(aligned is not None):

faceImg = preProcess(aligned)

embs = l2_normalize(np.concatenate(model.predict(faceImg)))

distanceNum = distance.euclidean(embs_valid, embs)

print(“Diff with {} is {}”.format(member, distanceNum))

程式測試

我們將一些不同人的相片放置於members目錄下,目錄名稱為其人名或代表工號,接著來測試看看。

正面臉孔:

1.082138
0.493925

效果看來不錯,只憑左邊一張相片,Facenet就能判斷出右下方臉孔與左圖較為相似,雖然其拍攝角度的差異更大。

這次測試下方往上的正面臉孔:

1.0013418 1.0399750 0.7001376

毫無懸念的Facenet又猜對了,雖然我們肉眼感覺下圖中間的人臉其構圖上更為近似。

側面的臉孔:

0.36971601843833923 0.9557983875274658
0.7647050619125366 0.6167847514152527

        使用一張側面的臉孔來預測其它角度的臉孔,果然差異值最小的那兩張與上方的確是同一人沒錯。

本文簡單的介紹了Facenet,並且示範如何於Keras framework使用。dataset並非自行訓練而是使用微軟MS-Celeb-1M dataset,雖然如此,單張相片的verify能力已相當令人驚豔,如果希望應用在公司內部並提昇到更高的辨識率,建議應考慮加入自行搜集的相片重新訓練以製作更符合公司人員的model。

另外,如果在一般的PC上執行,您會發現Facenet速度不是很快速,此時可以考慮改為在GPU上執行,在迅速的辨識速度下,可作出各種有趣且實用的人臉辨識專案。

(本文經作者同意轉載自CH.TSENG部落格、原文連結;責任編輯:葉于甄)

曾 成訓

曾 成訓

人到中年就像沒對準的描圖紙,一點一點的錯開,我只能當個Maker來使它復位。
曾 成訓

Author: 曾 成訓

人到中年就像沒對準的描圖紙,一點一點的錯開,我只能當個Maker來使它復位。

Share This Post On
468 ad

Submit a Comment

你的電子郵件位址並不會被公開。 必要欄位標記為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料