機器學習原來這麼有趣!第四章:用深度學習識別人臉

作者:Adam Geitgey

原文:medium.com/@ageitgey/ma

譯者:巡洋艦科技——趙95

校對:離線Offline——林沁

轉載請聯繫譯者。

你有沒有發現 Facebook 研發出了一種能夠在你的照片中識別出你朋友的神奇neng li? 之前,你需要手動點擊你朋友的照片,輸入他們的名字,然後加上標籤。現在,只要你一上傳照片,Facebook 就會神奇地標註出你的每一個朋友:

Facebook 會基於你之前的標註,自動標註出你照片中的人。我不知道該說它有用呢還是邪乎呢!

這種技術被稱為人臉識別。你的朋友被標記了幾次之後,Facebook 的演算法就能夠識別你朋友的臉。 這是一項非常驚人的黑科技——Facebook 的人臉識別準確率達到了 98%,幾乎與人類做得一樣好!

讓我們來了解一下現代人臉識別是如何工作的! 但是,識別你的朋友這太容易了。 我們可以最大化擴展這項技術,來解決一個更具挑戰性的問題——區分威爾·法瑞爾(Will Ferrell,著名演員)和查德·史密斯(Chad Smith,著名搖滾音樂家)!

其中一個人是威爾·法瑞爾,另一個是查德·史密斯。我保證他們不是同一個人!

如何用機器學習解決複雜問題?

到目前為止,在前三章,我們用機器學習來解決了一些一步就能完成的問題——估計房子的價格、基於現有數據生成新數據[1]、判別圖像當中是否包含某個物品。 所有這些問題都可以通過下列步驟解決:選擇一個機器學習演算法,輸入數據,然後獲得結果。

但是,人臉識別是由一系列的幾個相關問題組成的:

1. 首先,找到一張圖片中的所有人臉。

2. 第二,對於每一張臉來說,無論光線明暗或面朝別處,它依舊能夠識別出是同一個人的臉。

3. 第三,能夠在每一張臉上找出可用於與他人區分的獨特之處,比如說眼睛有多大,臉有多長等等。

4. 最後,將這張臉的特點與已知的所有人臉進行比較,以確定這個人的姓名。

作為人類,你的大腦總是在一瞬間自動做出了這些判斷。實際上,人類在識別人臉這方面做得太好了,以至於他們會在日常物品中同樣去「找臉」:

計算機還不具備這種高級泛化能力(至少現在還不行……),所以我們必須手把手一步一步教它們怎麼做。

我們需要構建一個流水線(pipeline)來識別人臉,它可以把上一個步驟的結果發送給下一個步驟。換句話說,我們會將好幾個機器學習演算法連接到一起:

一個基礎的人臉識別的流水線是怎樣工作的

人臉識別——分步講解

讓我們一步一步地解決這個問題。 對於每個步驟,我們將學習一個不同的機器學習演算法。 我並不會完全解釋每一個的演算法,否則這篇文章就變成了一本教科書。但你會學到每個步驟的精髓,以及如何在 Python 中使用 OpenFace 和 dlib 來構建一個你自己的面部識別系統。

第一步:找出所有的面孔

我們流水線的第一步是人臉檢測。顯然,在我們區分人臉之前,我們必須要照片中找到他們才行!

如果你在過去 10 年裡使用過相機,你可能已經見過正在運行中的人臉檢測功能:

人臉檢測是相機很好的一個功能。 當相機自動選中人臉時,它可以確保在拍攝時對焦到所有人臉。 不過,我們使用它另有其因——我們需要找到想要傳遞到流水線下一步的圖像區域。

2000 年初的時候, 當保羅·比奧拉(Paul Viola)和邁克爾·瓊斯(Michael Jones) 發明了一種能夠快速在廉價相機上運行的人臉檢測方法之後[2],人臉檢測在成為了主流。然而現在,更可靠的解決方案出現了。 我們將使用 2005 年發明的一種稱為方向梯度直方圖(Histogram of Oriented Gradients)的方法,簡稱 HOG

要在一張圖片中找到臉,我們首先將圖像轉換為黑白,因為我們並不需要顏色數據來找到臉:

然後,我們將查看圖片中的每一個像素。 對於單個像素,我們也要查看它周圍的其他像素:

我們的目標是找出並比較當前像素與直接圍繞它的像素的深度。 然後我們要畫一個箭頭來代表圖像變暗的方向:

看這個像素和它周圍的像素,圖像向右上方變暗。

如果你對圖片中的每一個像素重複這個過程,最終每個像素會被一個箭頭取代。這些箭頭被稱為梯度(gradients),它們能顯示出圖像上從明亮到黑暗的流動過程:

用梯度來代替像素這事看起來沒有明確目的,但其實背後的理由很充分。如果我們直接分析像素,同一個人明暗不同的兩張照片將具有完全不同的像素值。但是如果只考慮亮度變化方向(direction)的話,明暗圖像將會有同樣的結果。這使得問題變得更容易解決!

但是保存每個像素的梯度太過細節化了,我們最終很有可能「一葉障目不見泰山」。如果能從更高的角度上觀察基本的明暗流動,我們就可以看出圖像的基本規律,這會比之前更好。

為了做到這一點,我們將圖像分割成一些 16×16 像素的小方塊。在每個小方塊中,我們將計算出每個主方向上有多少個梯度(有多少指向上,指向右上,指向右等)。然後我們將用指向性最強那個方向的箭頭來代替原來的那個小方塊。

最終的結果是,我們把原始圖像轉換成了一個非常簡單的表達形式,這種表達形式可以用一種簡單的方式來捕獲面部的基本結構:

原始圖像被表示成了 HOG 形式,以捕獲圖像的主要特徵,無論圖像明暗度如何。

為了在這個 HOG 圖像中找到臉部,我們要所需要做的,就是找到我們的圖像中,與已知的一些 HOG 圖案中,看起來最相似的部分。這些 HOG 圖案都是從其他面部訓練數據中提取出來的:

使用這種技術,我們現在可以輕鬆地在任何圖片中找到臉部:

如果你想用 Python 和 dlib 親手試試看,這些代碼顯示了如何生成和查看 HOG 圖像。

第二步:臉部的不同姿勢

哇,我們把圖片中的臉部分離出來了。 但現在,我們要處理的問題就是,對於電腦來說,面朝不同方向的同一張臉,是不同的東西:

人類可以很輕鬆地識別出到兩個圖片都是威爾·法瑞爾,但電腦會認為這兩張圖片是兩個完全不同的人。

為了解決這一點,我們將試圖扭曲每個圖片,使得眼睛和嘴唇總是在圖像中的樣本位置(sample place)。 這將使我們在接下來的步驟中,更容易比較臉部之間的不同。

為此,我們將使用一種稱為面部特徵點估計(face landmark estimation)的演算法。 很多方法都可以做到這一點,但這次我們會使用由 瓦希德·卡奇米(Vahid Kazemi)和約瑟菲娜·沙利文(Josephine Sullivan)在 2014 年發明的方法。

這一演算法的基本思路是找到 68 個人臉上普遍存在的特定點(稱為特徵點, landmarks)——包括下巴的頂部、每隻眼睛的外部輪廓、每條眉毛的內部輪廓等。接下來我們訓練一個機器學習演算法,讓它能夠在任何臉部找到這 68 個特定的點:

我們將在每一張臉上定位的 68 個特徵點。這張圖片的作者是在OpenFace工作的卡內基梅隆大學 Ph.D. 布蘭東·阿莫斯Brandon Amos)。

這是在測試圖片上定位 68 個特徵點的結果:

友情提示:你也可以使用這一技術來實現自己的 Snapchat 實時 3D 臉部過濾器!

現在,我們知道了眼睛和嘴巴在哪兒,我們將圖像進行旋轉、縮放和錯切,使得眼睛和嘴巴儘可能靠近中心。我們不會做任何花哨的三維扭曲,因為這會讓圖像失真。我們只會使用那些能夠保持圖片相對平行的基本圖像變換,例如旋轉和縮放(稱為仿射變換):

現在無論人臉朝向哪邊,我們都能將眼睛和嘴巴向中間挪動到大致相同的位置。這將使我們的下一步更加準確。

如果你想用 Python 和 dlib 親手試試看這一步的話,這裡有一些代碼幫你尋找臉部特徵點並用這些特徵點完成圖像變形。

第三步:給臉部編碼

現在我們要面臨最核心的問題了——如何區分不同的人臉。這才是這件事的有趣之處!

最簡單的人臉識別方法,就是直接把我們在第二步中發現的未知人臉,與我們已經標註了的人臉圖片作比較。當我們發現未知的面孔與一個以前標註過的面孔看起來及其相似的時候,它肯定是同一個人。似乎這主意看起來不錯,對吧?

實際上這種方法有一個巨大的問題。像 Facebook 這種擁有數十億用戶和數萬億張照片的網站,是不可能去循環比較每張先前標記的臉的,這浪費的時間太長了。他們需要在毫秒內識別人臉,而不是幾個小時。

我們需要的方法是一種從每張人臉上提取一些基本的測量數值。然後,我們可以用同樣的方式測量未知的面孔,並找到最接近測量數值的那張已知的臉。例如,我們可以測量每個耳朵的大小、眼睛之間的間距、鼻子的長度等。如果你曾經看過像《犯罪現場調查》這樣的電視劇,你就知道我在說什麼了。

cdn-images-1.medium.com

就像電視劇一樣!非常真實!#科學

測量人臉的最可靠的方法

好的,那麼我們應該測量面部的哪些數值,來建立我們的已知臉部資料庫呢?耳朵的大小?鼻子的長度?眼睛的顏色?還有什麼?

事實證明,對於我們人類來說一些顯而易見的測量值(比如眼睛顏色),對計算機來說沒什麼意義。研究人員發現,最準確的方法是讓計算機自己找出它要收集的測量值。深度學習比人類更懂面部哪些部分的測量值比較重要。

所以,解決方案是訓練一個深度卷積神經網路(就像我們在第三章做的那樣)。但是,並不是讓它去識別圖片中的物體,這一次我們的訓練是要讓它為臉部生成 128 個測量值。

每次訓練要觀察三個不同的臉部圖像:

1. 載入一張已知的人的面部訓練圖像

2. 載入同一個人的另一張照片

3. 載入另外一個人的照片

然後,演算法查看它自己為這三個圖片生成的測量值。再然後,稍微調整神經網路,以確保第一張和第二張生成的測量值接近,而第二張和第三張生成的測量值略有不同。

在為幾千個人的數百萬圖像重複該步驟數百萬次之後,神經網路學習了如何可靠地為每個人生成 128 個測量值。對於同一個人的任何十張不同的照片,它都應該給出大致相同的測量值。

機器學習專業人士把每張臉的 128 個測量值稱為一個嵌入(embedding)。將複雜的原始數據(如圖片)縮減為可由計算機生成的一個數列的方法,在機器學習(特別是語言翻譯)中出現了很多次。我們正在使用的這種臉部提取方法是由 Google 的研究人員在 2015 年發明的,但也有許多類似方法存在。

給我們的臉部圖像編碼

這個通過訓練卷積神經網路來輸出臉部嵌入的過程,需要大量的數據和強大的計算能力。即使使用昂貴的 Nvidia Telsa 顯卡,你也需要大約 24 小時的連續訓練,才能獲得良好的準確性。

但一旦網路訓練完成,即使它從來沒有見過這些面孔,它也可以生成這張面孔的測量值!所以這種訓練只需一次即可。幸運的是,OpenFace 上面的大神已經做完了這些,並且他們發布了幾個訓練過可以直接使用的網路。謝謝布蘭東·阿莫斯和他的團隊!

所以我們需要做的,就是通過他們預訓練的網路來處理我們的臉部圖像,以獲得 128 個測量值。這是我們測試圖像的一些測量值:

那麼,這 128 個數字到底測量了臉部的哪些部分?我們當然不知道,但是這對我們並不重要。我們關心的是,當看到同一個人兩張不同的圖片時,我們的網路需要能得到幾乎相同的數值。

如果你想自己嘗試這個步驟,OpenFace 提供了一個 lua 腳本,它可以生成一個文件夾中所有圖像的嵌入,並將它們寫入 csv 文件。點此查看如何運行。

第四步:從編碼中找出人的名字

最後這一步實際上是整個過程中最簡單的一步。我們要做的就是找到資料庫中,與我們的測試圖像的測量值最接近的那個人。

你可以通過任何基本的機器學習分類演算法來達成這一目標。我們並不需要太花哨的深度學習技巧。我們將使用一個簡單的線性 SVM 分類器,但實際上還有很多其他的分類演算法可以使用。

我們需要做的是訓練一個分類器,它可以從一個新的測試圖像中獲取測量結果,並找出最匹配的那個人。分類器運行一次只需要幾毫秒,分類器的結果就是人的名字!

所以讓我們試一下我們的系統。首先,我使用威爾·法瑞爾、查德·史密斯和吉米·法倫(Jimmy Falon)三人每人 20 張照片的嵌入來訓練分類器:

……就是這些訓練數據!

接下來,我在這個分類器上運行了威爾·法瑞爾和查德·史密斯在吉米·法倫的節目上互相模仿的那個視頻的每一幀:

cdn-images-1.medium.com

結果成功了!不同角度的臉部,甚至是側臉,它都能捕捉到!

你自己做一遍

讓我們回顧一下我們的步驟:

1. 使用 HOG 演算法給圖片編碼,以創建圖片的簡化版本。使用這個簡化的圖像,找到其中看起來最像通用 HOG 面部編碼的部分。

2. 通過找到臉上的主要特徵點,找出臉部的姿勢。一旦我們找到這些特徵點,就利用它們把圖像扭曲,使眼睛和嘴巴居中。

3. 把上一步得到的面部圖像放入神經網路中,神經網路知道如何找到 128 個特徵測量值。保存這 128 個測量值。

4. 看看我們過去已經測量過的所有臉部,找出哪個人的測量值和我們要測量的面部最接近。這就是你要找的人!

現在你知道這一切都是如何運行的了,這裡是如何使用 OpenFace 在你自己的電腦上運行整個人臉識別系統的說明:

開始之前

確保你已經安裝了 python、OpenFace 和 dlib。你也可以在這裡手動安裝,或者使用一個已經設定好的 docker image:

docker pullbamos/openfacedocker run -p 9000:9000-p 8000:8000 -t -i bamos/openface /bin/bashcd /root/openface

友情提示:如果你正在 OSX 上使用 Docker,你可以這樣使你的 OSX /Users/

文件夾在 docker image 中可見:

docker run -v /Users:/host/Users -p 9000:9000 -p8000:8000 -t -i bamos/openface /bin/bashcd /root/openface

然後你就能訪問你在 docker image 中 /host/Users/...的 OSX 文件

ls /host/Users/

第一步

在 openface 文件中建立一個名為 ./training-images/ 的文件夾。

mkdir training-images

第二步

為你想識別的每個人建立一個子文件夾。例如:

mkdir ./training-images/will-ferrell/mkdir ./training-images/chad-smith/mkdir ./training-images/jimmy-fallon/

第三步

將每個人的所有圖像複製進對應的子文件夾。確保每張圖像上只出現一張臉。不需要裁剪臉部周圍的區域。OpenFace 會自己裁剪。

第四步

從 openface 的根目錄中運行這個

openface 腳本。

首先,進行姿勢檢測和校準:

./util/align-dlib.py./training-images/ align outerEyesAndNose ./aligned-images/ --size 96

這將創建一個名為./aligned-images/的子文件夾,裡面是每一個測試圖像裁剪過、並且對齊的版本。

其次,從對齊的圖像中生成特徵文件:

./batch-represent/main.lua-outDir ./generated-embeddings/ -data ./aligned-images/

運行完後,這個./generated-embeddings/子文件夾會包含一個帶有每張圖像嵌入的 csv 文件。

第三,訓練你的面部檢測模型:

./demos/classifier.pytrain ./generated-embeddings/

這將生成一個名為 ./generated-embeddings/classifier.pkl的新文件,其中包含了你用來識別新面孔的 SVM 模型。

到這一步為止,你應該有了一個可用的人臉識別器!

第五步:識別面孔!

獲取一個未知臉孔的新照片,然後像這樣把它傳遞入分類器腳本中:

./demos/classifier.pyinfer ./generated-embeddings/classifier.pkl your_test_image.jpg

你應該會得到像這樣的一個預測:

===/test-images/will-ferrel-1.jpg ===Predict will-ferrell with 0.73 confidence.

至此,你已經完成了一個預測了。你也可以修改./demos/classifier.py 這個 python 腳本,來讓它匹配其他人的臉。

重要提示:

  • 如果你得到的結果不夠理想,試著在第三步為每個人添加更多照片(特別是不同姿勢的照片)。

  • 即使完全不知道這個面孔是誰,現在這個腳本仍然會給出預測。在真實應用中,低可信度(low confidence)的預測可能會被直接捨棄,因為很有可能它們就是錯的。

1. 譯者註:根據已有的超級馬里奧的關卡生成新的關卡

2. 譯者註:這種演算法也可以用來訓練檢測其他的物品,但是最經常還是被用於人臉的檢測,其英文名稱為 Viola–Jones object detection framework


推薦閱讀:

BP演算法的矩陣操作問題?
BLAS簡介
學習機器學習時需要儘早知道的三件事
機器學習--感知機科普入門
Python · 神經網路(六)· 拓展

TAG:机器学习 | 深度学习DeepLearning | 人脸识别 |