2019年7月16日 星期二

Face Recognition with FaceNet



Google在2015年提出透過深度學習的方式處理人臉辨識的系統FaceNet

該論文的連結可參考此處

很快的這個方法就席捲了人臉辨識的應用

許多過去傳統的人臉辨識演算法都不敵FaceNet

在論文中提到,其採用LFW人臉資料庫進行benchmark

並以99.63%的準確率,打破了過去的紀錄

而在YouTubeFaces人臉資料庫也進行了測試

依舊創下高達95.12%的準確率

若是想要研究人臉辨識,一定要認識FaceNet


關於原文版本如果不好閱讀,可參考下面這篇翻譯版

中文翻譯的易讀版

核心的部份這邊就不多談了

簡單說一下FaceNet的運作流程

一般認為深度學習的方式就是透過訓練資料進行學習

訓練好的Model能夠辨識各種不同種類的物件

因此當你input一個圖檔,他能output一個類別名稱給你

例如傳入一張貓的圖片,就會輸出"貓"這個類別出來,如下圖:


那人臉辨識要怎麼運作??

傳入一張周杰倫的照片,就會輸出"周杰倫"這個類別出來嗎?

但深度學習的訓練需要大量的資料以及時間

人臉辨識有辦法做嗎?

每次要增加一個人,是不是要準備大量的照片? 然後還要很長的訓練時間?

上述這樣實在太不合乎使用邏輯了

因此FaceNet的做法並非輸出分類結果

而是輸出量化特徵值,如下圖:



FaceNet的產出就是Embeddings(128或512個特徵值)
(舊版的Pre-Trained Model產生128個特徵值、新版的產生512特徵值)

當你傳入一張周杰倫的照片,FaceNet輸出128個特徵值

當你傳入一張郭富城的照片,FaceNet輸出128個特徵值

只要將兩個結果做歐式距離(Euclidean Distance)的計算

值越小代表這兩張臉越相似,值越大代表差異越大

因此這樣的作法是不是很簡單呢!!

基本上的流程會如下圖:

1. 先進行人臉偵測,取出人臉的影像

2. 人臉影像前置處理(resize、prewhiten)

3. 把處理後的影像給FaceNet進行運作

4. 取得特徵值並計算歐式距離

5. 計算的distance小於閥值則認為是同一個人
(正常計算出來的dinstance介於0~2之間)


因此當你開始要研究如何實作的時候

google上只能找到python...
(廢話...原作者就已經提供python的程式了...)

或是頂多找到c++的程式

因此這大概又是全世界第一篇用C#來實作的介紹...

要用C#跑TensorFlow,前面的文章有介紹過幾個

大致上就是TensorFlowSharp以及EmguTF

這裡以EmguTF作為範例

首先先下載FaceNet已經處理好的Pre-Trained Models

可直接在GitHub的頁面下載


下載下來後

只要取其中的.pb檔即可,如下圖:
依照原有既定的邏輯

要先處理人臉偵測,而人臉偵測可以參考上一篇介紹的方法


整個範例採用EmguTF套件與EmguCV套件來實作


這裡實作了,如何讀入剛剛所下載的Pre-Trained Model

並建立TFSession 與 TFGraph 

接著透過EmguCV把人臉的影像讀進來

偵測出人臉後,把人臉的影像節取出來,並進行Resize

FaceNet需Resize至160x160的大小

resize後的image需轉換成Tensor物件

轉換程式可以參考EmguTF的範例程式TensorConvert.cs

這裡使用的版本如下:


將影像的Mat傳入,取得該影像的Tensor物件:tensorImage


以上都算是比較簡單的部分

再來的處理就比較麻煩

第一個是Session Run的實作

來看一下原始Python的Source Code:compare.py


在sess.run裡面會帶入一個參數feed_dict,包含images跟一個False

但是在EmguTF的Tensor物件並沒有帶入bool的建構式

所以必須自行在EmguTF的Source Code加入一個新的建構式

才能建立一個new Tensor(false)物件

接著透過以下的程式進行處理

Session Run的方式如下,並取得特徵值boxes


boxes就是一個一維陣列(128或512個值)

利用這個值,進行歐式距離的計算

即可求出每個人臉的差異結果

Distance的求法很簡單,如下:


用上述的方法就可以求出兩個相同長度的一維陣列的歐式距離

以上的做法可以順利把人臉的影像傳進去給FaceNet

並取得一個一為陣列boxes的值

於是開始著手進行準備資料

收集了150個人的人臉照片,平均一個人1~6張不等作為訓練照片


藝人的照片占多數為主

接著再準備上述150個人,另外500張人臉照片作為測試

500張測試照,角度不同,甚至還有黑白照片

但依據這樣的實作會發現

計算出來的distance值,好像不太準耶??

無論Threshold的值如何訂,準確率都很低

大概只有不到20%... 整個傻眼...


在仔細研究原專案的Source Code:facenet.py

會發現該專案把影像傳給FaceNet時

會先進行一些前處理,包含一個叫做prewhiten的方法

這個看似簡單的方法,卻至關重要!!

裡面使用了叫做Numpy的python套件

是一個方便處理矩陣運算的工具套件

np.mean、np.std、np.maximum、np.multiply...一連四個矩陣運算

如果今天要改實作到別的語言...

只能去爬Numpy中這幾個方法的邏輯

然後...自己實作

也可以參考C++的source code,裡面有OpenCV的語法可以參考


實作完成後,將人臉影像進行上述的影像處理運算

再傳入FaceNet取得特徵值進行比對

實測了目前GitLab提供的兩個新的Model

20180408-102900.pb 以及 20180402-114759.pb
(這兩個Model會產出512個特徵值)

以及一個舊的Model

20170512-110547.pb
(產出128個特徵值)

一樣的測試資料,閥值訂在0.8

20180408-102900.pb  ==>  70.3%

20180402-114759.pb  ==>  84.5%

20170512-110547.pb  ==>  93.8%

舊版的Model實測出:93.8%的準確率!!!

AMAZING!!


雖然不太理解為什麼舊版的測試效果反而比較好 XDD

而且版本之間的準確率落差還蠻巨大

由於藝人的照片是上google隨便亂抓的

有奇怪的光源、角度、甚至不同年代的妝容

因此藝人的照片算是拉低了很多辨識率

但就以20170512-110547.pb 的實測結果

辨識率還是相當驚人呢!!!


而後續提出的新的Model

也提出了新的計算距離的算法

可嘗試使用cosine similarity來計算兩個一維陣列的距離

或許還可以得到更好的辨識效果呢!!


2 則留言: