您的位置:首頁>正文

吳恩達深度學習中文版筆記:人臉識別和神經風格轉換

大資料文摘經授權轉載

作者:黃海廣

自2016年8月份, 吳恩達的初創公司deeplearning.ai通過Coursera提供深度學習的最新線上課程, 到今年2月份, 吳老師更新了課程的第五部分(點擊查看大資料文摘相關報導),

前後耗時半年時間。

本文將著重介紹吳恩達老師第四周課程的視頻內容和筆記, 展示一些重要的卷積神經網路的特殊應用, 我們將從人臉識別開始, 之後講神經風格遷移, 你將有機會在程式設計作業中實現這部分內容, 創造自己的藝術作品。

什麼是人臉識別?

讓我們先從人臉識別開始, 我這裡有一個有意思的演示。 我在領導百度AI團隊的時候, 其中一個小組由林元慶帶領的, 做過一個人臉識別系統, 這個系統非常棒, 讓我們來看一下。

以下內容為演示視頻內容:

視頻開始:

我想演示一個人臉識別系統, 我現在在百度的中國總部, 很多公司要求進入公司的時候要刷工卡, 但是在這裡我們並不需要它, 使用人臉識別, 看看我能做什麼。 當我走近的時候, 它會識別我的臉, 然後說歡迎我(Andrew NG), 不需要工卡, 我就能通過了。

讓我們看看另一種情況, 在旁邊的是林元慶, IDL(百度深度學習實驗室)的主管, 他領導開發了這個人臉識別系統,

我把我的工卡給他, 上面有我的頭像, 他會試著用我的頭像照片, 而不是真人來通過。

(林元慶語:我將嘗試用Andrew的工卡騙過機器, 看看發生什麼, 系統不會識別, 系統拒絕識別。 現在我要用我自己的臉, (系統語音:“歡迎您”)(林元慶順利通過))

類似於這樣的人臉識別系統在中國發展很快,

我希望這個技術也可以在其他的國家使用。

#視頻結束

挺厲害的吧, 你剛看到的這個視頻展示了人臉識別和活體檢測, 後一項技術確認你是一個活人。 事實上, 活體檢測可以使用監督學習來實現, 去預測是不是一個真人, 這個方面我就不多說了。 我主要想講的是, 如何構造這個系統中的人臉識別這一部分。

首先, 讓我們瞭解一下人臉識別的一些術語。

在人臉識別的相關文獻中, 人們經常提到人臉驗證(face verification)和人臉識別(face recognition)。

這是人臉驗證問題, 如果你有一張輸入圖片, 以及某人的ID或者是名字, 這個系統要做的是, 驗證輸入圖片是否是這個人。 有時候也被稱作1對1問題, 只需要弄明白這個人是否和他聲稱的身份相符。

而人臉識別問題比人臉驗證問題難很多(整理者注:1對多問題(1:K)), 為什麼呢?假設你有一個驗證系統, 準確率是99%, 還可以。 但是現在, 假設在識別系統中, K=100, 如果你把這個驗證系統應用在100個人身上, 人臉識別上, 你犯錯的機會就是100倍了。

如果每個人犯錯的概率是1%, 如果你有一個上百人的資料庫, 如果你想得到一個可接受的識別誤差, 你要構造一個驗證系統, 其準確率為99.9%或者更高, 然後才可以在100人的資料庫上運行, 而保證有很大幾率不出錯。 事實上, 如果我們有一個100人的資料庫, 正確率可能需要遠大於99%, 才能得到很好的效果。

在之後的幾個視頻中,我們主要講構造一個人臉驗證,作為基本模組,如果準確率夠高,你就可以把它用在識別系統上。

下一個視頻中,我們將開始討論如何構造人臉驗證系統,人臉驗證之所以難,原因之一在於要解決“一次學”(one-shot learning problem)問題。讓我們看下一個視頻,什麼是一次學習問題。

One-Shot學習

人臉識別所面臨的一個挑戰就是你需要解決一次學習問題,這意味著在大多數人臉識別應用中,你需要通過單單一張圖片或者單單一個人臉樣例就能去識別這個人。而歷史上,當深度學習只有一個訓練樣例時,它的表現並不好,讓我們看一個直觀的例子,並討論如何去解決這個問題。

假設你的資料庫裡有4張你們公司的員工照片,實際上他們確實是我們deeplearning.ai的員工,分別是Kian,Danielle,Younes和Tian。現在假設有個人(編號1所示)來到辦公室,並且她想通過帶有人臉識別系統的柵門,現在系統需要做的就是,僅僅通過一張已有的Danielle照片,來識別前面這個人確實是她。相反,如果機器看到一個不在資料庫裡的人(編號2所示),機器應該能分辨出她不是資料庫中四個人之一。

所以在一次學習問題中,只能通過一個樣本進行學習,以能夠認出同一個人。大多數人臉識別系統都需要解決這個問題,因為在你的資料庫中每個雇員或者組員可能都只有一張照片。

有一種辦法是,將人的照片放進卷積神經網路中,使用softmax單元來輸出4種,或者說5種標籤,分別對應這4個人,或者4個都不是,所以softmax裡我們會有5種輸出。但實際上這樣效果並不好,因為如此小的訓練集不足以去訓練一個穩健的神經網路。

而且,假如有新人加入你的團隊,你現在將會有5個組員需要識別,所以輸出就變成了6種,這時你要重新訓練你的神經網路嗎?這聽起來實在不像一個好辦法。

所以要讓人臉識別能夠做到一次學習,為了能有更好的效果,你現在要做的應該是學習Similarity函數。

詳細地說,你想要神經網路學習這樣一個用d表示的函數,d(img1,img2)=degree of difference between images,它以兩張圖片作為輸入,然後輸出這兩張圖片的差異值。如果你放進同一個人的兩張照片,你希望它能輸出一個很小的值,如果放進兩個長相差別很大的人的照片,它就輸出一個很大的值。

所以在識別過程中,如果這兩張圖片的差異值小於某個閾值τ,它是一個超參數,那麼這時就能預測這兩張圖片是同一個人,如果差異值大於τ,就能預測這是不同的兩個人,這就是解決人臉驗證問題的一個可行辦法。

要將它應用於識別任務,你要做的是拿這張新圖片(編號6),然後用d函數去比較這兩張圖片(編號1和編號6),這樣可能會輸出一個非常大的數字,在該例中,比如說這個數字是10。之後你再讓它和資料庫中第二張圖(編號2)片比較,因為這兩張照片是同一個人,所以我們希望會輸出一個很小的數。然後你再用它與資料庫中的其他圖片(編號3、4)進行比較,通過這樣的計算,最終你能夠知道,這個人確實是Danielle。

對應的,如果某個人(編號7)不在你的資料庫中,你通過函數d將他們的照片兩兩進行比較,最後我們希望d會對所有的比較都輸出一個很大的值,這就證明這個人並不是資料庫中4個人的其中一個。

要注意在這過程中你是如何解決一次學習問題的,只要你能學習這個函數d,通過輸入一對圖片,它將會告訴你這兩張圖片是否是同一個人。如果之後有新人加入了你的團隊(編號5),你只需將他的照片加入你的資料庫,系統依然能照常工作。

現在你已經知道函數d是如何工作的,通過輸入兩張照片,它將讓你能夠解決一次學習問題。那麼,下節視頻中,我們將會學習如何訓練你的神經網路學會這個函數d。

Siamese 網路

上個視頻中你學到的函數d的作用就是輸入兩張人臉,然後告訴你它們的相似度。實現這個功能的一個方式就是用Siamese網路,我們看一下。

你經常看到這樣的卷積網路,輸入圖片x(1),然後通過一些列卷積,池化和全連接層,最終得到這樣的特徵向量(編號1)。有時這個會被送進softmax單元來做分類,但在這個視頻裡我們不會這麼做。我們關注的重點是這個向量(編號1),加如它有128個數,它是由網路深層的全連接層計算出來的,我要給這128個數命個名字,把它叫做f(x(1))。你可以把f(x(1))看作是輸入圖像x(1)的編碼,取這個輸入圖像(編號2),在這裡是Kian的圖片,然後表示成128維的向量。

建立一個人臉識別系統的方法就是,如果你要比較兩個圖片的話,例如這裡的第一張(編號1)和第二張圖片(編號2),你要做的就是把第二張圖片喂給有同樣參數的同樣的神經網路,然後得到一個不同的128維的向量(編號3),這個向量代表或者編碼第二個圖片,我要把第二張圖片的編碼叫做f(x(2))。這裡我用x(1)和x(2)僅代表兩個輸入圖片,他們沒必要非是第一個和第二個訓練樣本,可以是任意兩個圖片。

最後如果你相信這些編碼很好地代表了這兩個圖片,你要做的就是定義d,將x(1)和x(2)的距離定義為這兩幅圖片的編碼之差的範數:

對於兩個不同的輸入,運行相同的卷積神經網路,然後比較它們,這一般叫做Siamese網路架構。這裡提到的很多觀點,都來自於Yaniv Taigman,Ming Yang,Marc’ Aurelio Ranzato,Lior Wolf的這篇論文,他們開發的系統叫做DeepFace。

怎麼訓練這個Siamese神經網路呢?不要忘了這兩個網路有相同的參數,所以你實際要做的就是訓練一個網路,它計算得到的編碼可以用於函數d,它可以告訴你兩張圖片是否是同一個人。更準確地說,神經網路的參數定義了一個編碼函數f(x(i)),如果給定輸入圖像x(i),這個網路會輸出x(i)的128維的編碼。你要做的就是學習參數,使得如果兩個圖片x(i)和x(j)是同一個人,那麼你得到的兩個編碼的距離就小。前面幾個幻燈片我都用的是x(1)和x(2)其實訓練集裡任意一對x(i)和x(j)都可以。相反,如果x(i)和x(j)是不同的人,那麼你會想讓它們之間的編碼距離大一點。

如果你改變這個網路所有層的參數,你會得到不同的編碼結果,你要做的就是用反向傳播來改變這些所有的參數,以確保滿足這些條件。

你已經瞭解了Siamese網路架構,並且知道你想要網路輸出什麼,即什麼是好的編碼。但是如何定義實際的目標函數,能夠讓你的神經網路學習並做到我們剛才討論的內容呢?在下一個視頻裡,我們會看到如何用三元組損失函數達到這個目的。

Triplet 損失

要想通過學習神經網路的參數來得到優質的人臉圖片編碼,方法之一就是定義三元組損失函數然後應用梯度下降。

我們看下這是什麼意思,為了應用三元組損失函數,你需要比較成對的圖像,比如這個圖片,為了學習網路的參數,你需要同時看幾幅圖片,比如這對圖片(編號1和編號2),你想要它們的編碼相似,因為這是同一個人。然而假如是這對圖片(編號3和編號4),你會想要它們的編碼差異大一些,因為這是不同的人。

用三元組損失的術語來說,你要做的通常是看一個 Anchor 圖片,你想讓Anchor圖片和Positive圖片(Positive意味著是同一個人)的距離很接近。然而,當Anchor圖片與Negative圖片(Negative意味著是非同一個人)對比時,你會想讓他們的距離離得更遠一點。

這就是為什麼叫做三元組損失,它代表你通常會同時看三張圖片,你需要看Anchor圖片、Postive圖片,還有Negative圖片,我要把Anchor圖片、Positive圖片和Negative圖片簡寫成A、P、N。

把這些寫成公式的話,你想要的是網路的參數或者編碼能夠滿足以下特性,也就是說你想要||f(A)-f(P)||2,你希望這個數值很小,準確地說,你想讓它小於等於f(A)和f(N)之間的距離,或者說是它們的范數的平方(即:||f(A)-f(P)||2≤||f(A)-f(N)||2)。(||f(A)-f(N)||2當然這就是d(A,P),你可以把d看作是距離(distance)函數,這也是為什麼我們把它命名為d。

現在如果我把方程右邊項移到左邊,最終就得到:||f(A)-f(P)||2≤||f(A)-f(N)||2現在我要對這個運算式做一些小的改變,有一種情況滿足這個運算式,但是沒有用處,就是把所有的東西都學成0,如果f總是輸出0,即0-0≤0,這就是0減去0還等於0,如果所有圖像的f都是一個零向量,那麼總能滿足這個方程。所以為了確保網路對於所有的編碼不會總是輸出0,也為了確保它不會把所有的編碼都設成互相相等的。另一種方法能讓網路得到這種沒用的輸出,就是如果每個圖片的編碼和其他圖片一樣,這種情況,你還是得到0-0。

為了阻止網路出現這種情況,我們需要修改這個目標,也就是,這個不能是剛好小於等於0,應該是比0還要小,所以這個應該小於一個-α值(即||f(A)-f(P)||2-||f(A)-f(N)||2≤-a)這裡的a是另一個超參數,這個就可以阻止網路輸出無用的結果。按照慣例,我們習慣寫+a(即||f(A)-f(P)||2-||f(A)-f(N)||2+a≤0,而不是把-a寫在後面,它也叫做間隔(margin),這個術語你會很熟悉,如果你看過關於支持向量機 (SVM) 的文獻,沒看過也不用擔心。我們可以把上面這個方程(||f(A)-f(P)||2-||f(A)-f(N)||2)也修改一下,加上這個間隔參數。

舉個例子,假如間隔設置成0.2,如果在這個例子中,d(A,P)=0.5,如果 Anchor和 Negative圖片的d,即d(A,N)只大一點,比如說0.51,條件就不能滿足。雖然0.51也是大於0.5的,但還是不夠好,我們想要d(A,N)比d(A,P)大很多,你會想讓這個值(d(A,N))至少是0.7或者更高,或者為了使這個間隔,或者間距至少達到0.2,你可以把這項調大或者這個調小,這樣這個間隔a,超參數a 至少是0.2,在d(A,P)和d(A,N)之間至少相差0.2,這就是間隔參數a的作用。

它拉大了Anchor和Positive 圖片對和Anchor與Negative 圖片對之間的差距。取下面的這個方框圈起來的方程式,在下個幻燈片裡,我們會更公式化表示,然後定義三元組損失函數。

三元組損失函數的定義基於三張圖片,假如三張圖片A、P、N,即Anchor樣本、Positive樣本和Negative樣本,其中Positive圖片和Anchor圖片是同一個人,但是Negative圖片和Anchor不是同一個人。

接下來我們定義損失函數,這個例子的損失函數,它的定義基於三元圖片組,我先從前一張幻燈片複製過來一些式子,就是||f(A)-f(P)||2-||f(A)-f(N)||2+a≤0。所以為了定義這個損失函數,我們取這個和0的最大值:

L(A,P,N)=max(||f(A)-f(P)||2-||f(A)-f(N)||2+a,0)這個max函數的作用就是,只要這個||f(A)-f(P)||2-||f(A)-f(N)||2+a≤0,那麼損失函數就是0。只要你能使畫綠色底線部分小於等於0,只要你能達到這個目標,那麼這個例子的損失就是0。

另一方面如果這個||f(A)-f(P)||2-||f(A)-f(N)||2+a≤0,然後你取它們的最大值,最終你會得到綠色底線部分(即||f(A)-f(P)||2-||f(A)-f(N)||2+a)是最大值,這樣你會得到一個正的損失值。通過最小化這個損失函數達到的效果就是使這部分||f(A)-f(P)||2-||f(A)-f(N)||2+a成為0,或者小於等於0。只要這個損失函數小於等於0,網路不會關心它負值有多大。

這是一個三元組定義的損失,整個網路的代價函數應該是訓練集中這些單個三元組損失的總和。假如你有一個10000個圖片的訓練集,裡面是1000個不同的人的照片,你要做的就是取這10000個圖片,然後生成這樣的三元組,然後訓練你的學習演算法,對這種代價函數用梯度下降,這個代價函數就是定義在你資料集裡的這樣的三元組圖片上。

注意,為了定義三元組的資料集你需要成對的A和P,即同一個人的成對的圖片,為了訓練你的系統你確實需要一個資料集,裡面有同一個人的多個照片。這是為什麼在這個例子中,我說假設你有1000個不同的人的10000張照片,也許是這1000個人平均每個人10張照片,組成了你整個資料集。如果你只有每個人一張照片,那麼根本沒法訓練這個系統。

當然,訓練完這個系統之後,你可以應用到你的一次學習問題上,對於你的人臉識別系統,可能你只有想要識別的某個人的一張照片。但對於訓練集,你需要確保有同一個人的多個圖片,至少是你訓練集裡的一部分人,這樣就有成對的Anchor和Positive圖片了。

現在我們來看,你如何選擇這些三元組來形成訓練集。一個問題是如果你從訓練集中,隨機地選擇A、P和N,遵守A和P是同一個人,而A和N是不同的人這一原則。有個問題就是,如果隨機的選擇它們,那麼這個約束條件(d(A,P)+a≤d(A,N))很容易達到,因為隨機選擇的圖片,A和N比A和P差別很大的概率很大。

我希望你還記得這個符號d(A,P)就是前幾個幻燈片裡寫的||f(A)-f(P)||2,d(A,N)就是||f(A)-f(N)||2,d(A,P)+a≤d(A,N)即||f(A)-f(P)||2+a≤||f(A)-f(N)||2。但是如果A和N是隨機選擇的不同的人,有很大的可能性||f(A)-f(N)||2會比左邊這項||f(A)-f(P)||2大,而且差距遠大於a,這樣網路並不能從中學到什麼。

所以為了構建一個資料集,你要做的就是盡可能選擇難訓練的三元組A、P和N。具體而言,你想要所有的三元組都滿足這個條件(d(A,P)+a≤d(A,N)),難訓練的三元組就是,你的A、P和N的選擇使得d(A,P)很接近d(A,N),即d(A,P)≈d(A,N),這樣你的學習演算法會竭盡全力使右邊這個式子變大(d(A,N)),或者使左邊這個式子(d(A,P))變小,這樣左右兩邊至少有一個a的間隔。

並且選擇這樣的三元組還可以增加你的學習演算法的計算效率,如果隨機的選擇這些三元組,其中有太多會很簡單,梯度演算法不會有什麼效果,因為網路總是很輕鬆就能得到正確的結果,只有選擇難的三元組梯度下降法才能發揮作用,使得這兩邊離得盡可能遠。

如果你對此感興趣的話,這篇論文中有更多細節,作者是Florian Schroff, Dmitry Kalenichenko, James Philbin,他們建立了這個叫做FaceNet的系統,我視頻裡許多的觀點都是來自於他們的工作。

Florian Schroff, Dmitry Kalenichenko, James Philbin (2015). FaceNet: A Unified Embedding forFace Recognition and Clustering

https://arxiv.org/pdf/1503.03832.pdf

順便說一下,這有一個有趣的事實,關於在深度學習領域,演算法是如何命名的。如果你研究一個特定的領域,假如說“某某”領域,通常會將系統命名為“某某”網路或者深度“某某”,我們一直討論人臉識別, 所以這篇論文叫做FaceNet(人臉網路),上個視頻裡你看到過DeepFace(深度人臉)。“某某”網路或者深度“某某”,是深度學習領域流行的命名演算法的方式,你可以看一下這篇論文,如果你想要瞭解更多的關於通過選擇最有用的三元組訓練來加速演算法的細節,這是一個很棒的論文。

總結一下,訓練這個三元組損失你需要取你的訓練集,然後把它做成很多三元組,這就是一個三元組(編號1),有一個Anchor圖片和Positive圖片,這兩個(Anchor和Positive)是同一個人,還有一張另一個人的Negative圖片。這是另一組(編號2),其中Anchor和Positive圖片是同一個人,但是Anchor和Negative不是同一個人,等等。

定義了這些包括A、P和N圖片的資料集之後,你還需要做的就是用梯度下降最小化我們之前定義的代價函數J,這樣做的效果就是反向傳播到網路中的所有參數來學習到一種編碼,使得如果兩個圖片是同一個人,那麼它們的d就會很小,如果兩個圖片不是同一個人,它們的d 就會很大。

這就是三元組損失,並且如何用它來訓練網路輸出一個好的編碼用於人臉識別。現在的人臉識別系統,尤其是大規模的商業人臉識別系統都是在很大的資料集上訓練,超過百萬圖片的資料集並不罕見,一些公司用千萬級的圖片,還有一些用上億的圖片來訓練這些系統。這些是很大的資料集,即使按照現在的標準,這些資料集並不容易獲得。

幸運的是,一些公司已經訓練了這些大型的網路並且上傳了模型參數。所以相比於從頭訓練這些網路,在這一領域,由於這些資料集太大,這一領域的一個實用操作就是下載別人的預訓練模型,而不是一切都要從頭開始。但是即使你下載了別人的預訓練模型,我認為瞭解怎麼訓練這些演算法也是有用的,以防針對一些應用你需要從頭實現這些想法。

這就是三元組損失,下個視頻中,我會給你展示Siamese網路的一些其他變體,以及如何訓練這些網路,讓我們進入下個視頻吧。

面部驗證與二分類

Triplet loss是一個學習人臉識別卷積網路參數的好方法,還有其他學習參數的方法,讓我們看看如何將人臉識別當成一個二分類問題。

另一個訓練神經網路的方法是選取一對神經網路,選取Siamese網路,使其同時計算這些嵌入,比如說128維的嵌入(編號1),或者更高維,然後將其輸入到邏輯回歸單元,然後進行預測,如果是相同的人,那麼輸出是1,若是不同的人,輸出是0。這就把人臉識別問題轉換為一個二分類問題,訓練這種系統時可以替換Triplet loss的方法。

最後的邏輯回歸單元是怎麼處理的?輸出y^會變成,比如說sigmoid函數應用到某些特徵上,相比起直接放入這些編碼(f(x(i)),f(x(j))),你可以利用編碼之間的不同。

我解釋一下,符號f(x(i))k代表圖片x(i)的編碼,下標k代表選擇這個向量中的第k個元素,|f(x(i))k- f(x(j))k|對這兩個編碼取元素差的絕對值。你可能想,把這128個元素當作特徵,然後把他們放入邏輯回歸中,最後的邏輯回歸可以增加參數wi和b,就像普通的邏輯回歸一樣。你將在這128個單元上訓練合適的權重,用來預測兩張圖片是否是一個人,這是一個很合理的方法來學習預測0或者1,即是否是同一個人。

還有其他不同的形式來計算綠色標記的這部分公式(|f(x(i))k-f(x(j))k|),比如說,公式可以是[f(x(i))k-f(x(j))k]2/[f(x(i))k+f(x(j))k],這個公式也被叫做x2公式,是一個希臘字母x,也被稱為x平方相似度。

Yaniv Taigman, Ming Yang, Marc'Aurelio Ranzato, Lior Wolf (2014). DeepFace:Closing the gap to human-level performance in face verification

https://research.fb.com/wp-content/uploads/2016/11/deepface-closing-the-gap-to-human-level-performance-in-face-verification.pdf

這些公式及其變形在這篇DeepFace論文中有討論,我之前也引用過。

但是在這個學習公式中,輸入是一對圖片,這是你的訓練輸入x(編號1、2),輸出y是0或者1,取決於你的輸入是相似圖片還是非相似圖片。與之前類似,你正在訓練一個Siamese網路,意味著上面這個神經網路擁有的參數和下面神經網路的相同(編號3和4所示的網路),兩組參數是綁定的,這樣的系統效果很好。

之前提到一個計算技巧可以幫你顯著提高部署效果,如果這是一張新圖片(編號1),當員工走進門時,希望門可以自動為他們打開,這個(編號2)是在資料庫中的圖片,不需要每次都計算這些特徵(編號6),不需要每次都計算這個嵌入,你可以提前計算好,那麼當一個新員工走近時,你可以使用上方的卷積網路來計算這些編碼(編號5),然後使用它,和預先計算好的編碼進行比較,然後輸出預測值y^。

因為不需要存儲原始圖像,如果你有一個很大的員工資料庫,你不需要為每個員工每次都計算這些編碼。這個預先計算的思想,可以節省大量的計算,這個預訓練的工作可以用在Siamese網路結構中,將人臉識別當作一個二分類問題,也可以用在學習和使用Triplet loss函數上,我在之前的視頻中描述過。

總結一下,把人臉驗證當作一個監督學習,創建一個只有成對圖片的訓練集,不是三個一組,而是成對的圖片,目標標籤是1表示一對圖片是一個人,目標標籤是0表示圖片中是不同的人。利用不同的成對圖片,使用反向傳播演算法去訓練神經網路,訓練Siamese神經網路。

這個你看到的版本,處理人臉驗證和人臉識別擴展為二分類問題,這樣的效果也很好。我希望你知道,在一次學習時,你需要什麼來訓練人臉驗證,或者人臉識別系統。

想要查看完整版課程筆記的同學請移步GitHub(附有課程作業代碼):

https://github.com/fengdu78/deeplearning_ai_books

在之後的幾個視頻中,我們主要講構造一個人臉驗證,作為基本模組,如果準確率夠高,你就可以把它用在識別系統上。

下一個視頻中,我們將開始討論如何構造人臉驗證系統,人臉驗證之所以難,原因之一在於要解決“一次學”(one-shot learning problem)問題。讓我們看下一個視頻,什麼是一次學習問題。

One-Shot學習

人臉識別所面臨的一個挑戰就是你需要解決一次學習問題,這意味著在大多數人臉識別應用中,你需要通過單單一張圖片或者單單一個人臉樣例就能去識別這個人。而歷史上,當深度學習只有一個訓練樣例時,它的表現並不好,讓我們看一個直觀的例子,並討論如何去解決這個問題。

假設你的資料庫裡有4張你們公司的員工照片,實際上他們確實是我們deeplearning.ai的員工,分別是Kian,Danielle,Younes和Tian。現在假設有個人(編號1所示)來到辦公室,並且她想通過帶有人臉識別系統的柵門,現在系統需要做的就是,僅僅通過一張已有的Danielle照片,來識別前面這個人確實是她。相反,如果機器看到一個不在資料庫裡的人(編號2所示),機器應該能分辨出她不是資料庫中四個人之一。

所以在一次學習問題中,只能通過一個樣本進行學習,以能夠認出同一個人。大多數人臉識別系統都需要解決這個問題,因為在你的資料庫中每個雇員或者組員可能都只有一張照片。

有一種辦法是,將人的照片放進卷積神經網路中,使用softmax單元來輸出4種,或者說5種標籤,分別對應這4個人,或者4個都不是,所以softmax裡我們會有5種輸出。但實際上這樣效果並不好,因為如此小的訓練集不足以去訓練一個穩健的神經網路。

而且,假如有新人加入你的團隊,你現在將會有5個組員需要識別,所以輸出就變成了6種,這時你要重新訓練你的神經網路嗎?這聽起來實在不像一個好辦法。

所以要讓人臉識別能夠做到一次學習,為了能有更好的效果,你現在要做的應該是學習Similarity函數。

詳細地說,你想要神經網路學習這樣一個用d表示的函數,d(img1,img2)=degree of difference between images,它以兩張圖片作為輸入,然後輸出這兩張圖片的差異值。如果你放進同一個人的兩張照片,你希望它能輸出一個很小的值,如果放進兩個長相差別很大的人的照片,它就輸出一個很大的值。

所以在識別過程中,如果這兩張圖片的差異值小於某個閾值τ,它是一個超參數,那麼這時就能預測這兩張圖片是同一個人,如果差異值大於τ,就能預測這是不同的兩個人,這就是解決人臉驗證問題的一個可行辦法。

要將它應用於識別任務,你要做的是拿這張新圖片(編號6),然後用d函數去比較這兩張圖片(編號1和編號6),這樣可能會輸出一個非常大的數字,在該例中,比如說這個數字是10。之後你再讓它和資料庫中第二張圖(編號2)片比較,因為這兩張照片是同一個人,所以我們希望會輸出一個很小的數。然後你再用它與資料庫中的其他圖片(編號3、4)進行比較,通過這樣的計算,最終你能夠知道,這個人確實是Danielle。

對應的,如果某個人(編號7)不在你的資料庫中,你通過函數d將他們的照片兩兩進行比較,最後我們希望d會對所有的比較都輸出一個很大的值,這就證明這個人並不是資料庫中4個人的其中一個。

要注意在這過程中你是如何解決一次學習問題的,只要你能學習這個函數d,通過輸入一對圖片,它將會告訴你這兩張圖片是否是同一個人。如果之後有新人加入了你的團隊(編號5),你只需將他的照片加入你的資料庫,系統依然能照常工作。

現在你已經知道函數d是如何工作的,通過輸入兩張照片,它將讓你能夠解決一次學習問題。那麼,下節視頻中,我們將會學習如何訓練你的神經網路學會這個函數d。

Siamese 網路

上個視頻中你學到的函數d的作用就是輸入兩張人臉,然後告訴你它們的相似度。實現這個功能的一個方式就是用Siamese網路,我們看一下。

你經常看到這樣的卷積網路,輸入圖片x(1),然後通過一些列卷積,池化和全連接層,最終得到這樣的特徵向量(編號1)。有時這個會被送進softmax單元來做分類,但在這個視頻裡我們不會這麼做。我們關注的重點是這個向量(編號1),加如它有128個數,它是由網路深層的全連接層計算出來的,我要給這128個數命個名字,把它叫做f(x(1))。你可以把f(x(1))看作是輸入圖像x(1)的編碼,取這個輸入圖像(編號2),在這裡是Kian的圖片,然後表示成128維的向量。

建立一個人臉識別系統的方法就是,如果你要比較兩個圖片的話,例如這裡的第一張(編號1)和第二張圖片(編號2),你要做的就是把第二張圖片喂給有同樣參數的同樣的神經網路,然後得到一個不同的128維的向量(編號3),這個向量代表或者編碼第二個圖片,我要把第二張圖片的編碼叫做f(x(2))。這裡我用x(1)和x(2)僅代表兩個輸入圖片,他們沒必要非是第一個和第二個訓練樣本,可以是任意兩個圖片。

最後如果你相信這些編碼很好地代表了這兩個圖片,你要做的就是定義d,將x(1)和x(2)的距離定義為這兩幅圖片的編碼之差的範數:

對於兩個不同的輸入,運行相同的卷積神經網路,然後比較它們,這一般叫做Siamese網路架構。這裡提到的很多觀點,都來自於Yaniv Taigman,Ming Yang,Marc’ Aurelio Ranzato,Lior Wolf的這篇論文,他們開發的系統叫做DeepFace。

怎麼訓練這個Siamese神經網路呢?不要忘了這兩個網路有相同的參數,所以你實際要做的就是訓練一個網路,它計算得到的編碼可以用於函數d,它可以告訴你兩張圖片是否是同一個人。更準確地說,神經網路的參數定義了一個編碼函數f(x(i)),如果給定輸入圖像x(i),這個網路會輸出x(i)的128維的編碼。你要做的就是學習參數,使得如果兩個圖片x(i)和x(j)是同一個人,那麼你得到的兩個編碼的距離就小。前面幾個幻燈片我都用的是x(1)和x(2)其實訓練集裡任意一對x(i)和x(j)都可以。相反,如果x(i)和x(j)是不同的人,那麼你會想讓它們之間的編碼距離大一點。

如果你改變這個網路所有層的參數,你會得到不同的編碼結果,你要做的就是用反向傳播來改變這些所有的參數,以確保滿足這些條件。

你已經瞭解了Siamese網路架構,並且知道你想要網路輸出什麼,即什麼是好的編碼。但是如何定義實際的目標函數,能夠讓你的神經網路學習並做到我們剛才討論的內容呢?在下一個視頻裡,我們會看到如何用三元組損失函數達到這個目的。

Triplet 損失

要想通過學習神經網路的參數來得到優質的人臉圖片編碼,方法之一就是定義三元組損失函數然後應用梯度下降。

我們看下這是什麼意思,為了應用三元組損失函數,你需要比較成對的圖像,比如這個圖片,為了學習網路的參數,你需要同時看幾幅圖片,比如這對圖片(編號1和編號2),你想要它們的編碼相似,因為這是同一個人。然而假如是這對圖片(編號3和編號4),你會想要它們的編碼差異大一些,因為這是不同的人。

用三元組損失的術語來說,你要做的通常是看一個 Anchor 圖片,你想讓Anchor圖片和Positive圖片(Positive意味著是同一個人)的距離很接近。然而,當Anchor圖片與Negative圖片(Negative意味著是非同一個人)對比時,你會想讓他們的距離離得更遠一點。

這就是為什麼叫做三元組損失,它代表你通常會同時看三張圖片,你需要看Anchor圖片、Postive圖片,還有Negative圖片,我要把Anchor圖片、Positive圖片和Negative圖片簡寫成A、P、N。

把這些寫成公式的話,你想要的是網路的參數或者編碼能夠滿足以下特性,也就是說你想要||f(A)-f(P)||2,你希望這個數值很小,準確地說,你想讓它小於等於f(A)和f(N)之間的距離,或者說是它們的范數的平方(即:||f(A)-f(P)||2≤||f(A)-f(N)||2)。(||f(A)-f(N)||2當然這就是d(A,P),你可以把d看作是距離(distance)函數,這也是為什麼我們把它命名為d。

現在如果我把方程右邊項移到左邊,最終就得到:||f(A)-f(P)||2≤||f(A)-f(N)||2現在我要對這個運算式做一些小的改變,有一種情況滿足這個運算式,但是沒有用處,就是把所有的東西都學成0,如果f總是輸出0,即0-0≤0,這就是0減去0還等於0,如果所有圖像的f都是一個零向量,那麼總能滿足這個方程。所以為了確保網路對於所有的編碼不會總是輸出0,也為了確保它不會把所有的編碼都設成互相相等的。另一種方法能讓網路得到這種沒用的輸出,就是如果每個圖片的編碼和其他圖片一樣,這種情況,你還是得到0-0。

為了阻止網路出現這種情況,我們需要修改這個目標,也就是,這個不能是剛好小於等於0,應該是比0還要小,所以這個應該小於一個-α值(即||f(A)-f(P)||2-||f(A)-f(N)||2≤-a)這裡的a是另一個超參數,這個就可以阻止網路輸出無用的結果。按照慣例,我們習慣寫+a(即||f(A)-f(P)||2-||f(A)-f(N)||2+a≤0,而不是把-a寫在後面,它也叫做間隔(margin),這個術語你會很熟悉,如果你看過關於支持向量機 (SVM) 的文獻,沒看過也不用擔心。我們可以把上面這個方程(||f(A)-f(P)||2-||f(A)-f(N)||2)也修改一下,加上這個間隔參數。

舉個例子,假如間隔設置成0.2,如果在這個例子中,d(A,P)=0.5,如果 Anchor和 Negative圖片的d,即d(A,N)只大一點,比如說0.51,條件就不能滿足。雖然0.51也是大於0.5的,但還是不夠好,我們想要d(A,N)比d(A,P)大很多,你會想讓這個值(d(A,N))至少是0.7或者更高,或者為了使這個間隔,或者間距至少達到0.2,你可以把這項調大或者這個調小,這樣這個間隔a,超參數a 至少是0.2,在d(A,P)和d(A,N)之間至少相差0.2,這就是間隔參數a的作用。

它拉大了Anchor和Positive 圖片對和Anchor與Negative 圖片對之間的差距。取下面的這個方框圈起來的方程式,在下個幻燈片裡,我們會更公式化表示,然後定義三元組損失函數。

三元組損失函數的定義基於三張圖片,假如三張圖片A、P、N,即Anchor樣本、Positive樣本和Negative樣本,其中Positive圖片和Anchor圖片是同一個人,但是Negative圖片和Anchor不是同一個人。

接下來我們定義損失函數,這個例子的損失函數,它的定義基於三元圖片組,我先從前一張幻燈片複製過來一些式子,就是||f(A)-f(P)||2-||f(A)-f(N)||2+a≤0。所以為了定義這個損失函數,我們取這個和0的最大值:

L(A,P,N)=max(||f(A)-f(P)||2-||f(A)-f(N)||2+a,0)這個max函數的作用就是,只要這個||f(A)-f(P)||2-||f(A)-f(N)||2+a≤0,那麼損失函數就是0。只要你能使畫綠色底線部分小於等於0,只要你能達到這個目標,那麼這個例子的損失就是0。

另一方面如果這個||f(A)-f(P)||2-||f(A)-f(N)||2+a≤0,然後你取它們的最大值,最終你會得到綠色底線部分(即||f(A)-f(P)||2-||f(A)-f(N)||2+a)是最大值,這樣你會得到一個正的損失值。通過最小化這個損失函數達到的效果就是使這部分||f(A)-f(P)||2-||f(A)-f(N)||2+a成為0,或者小於等於0。只要這個損失函數小於等於0,網路不會關心它負值有多大。

這是一個三元組定義的損失,整個網路的代價函數應該是訓練集中這些單個三元組損失的總和。假如你有一個10000個圖片的訓練集,裡面是1000個不同的人的照片,你要做的就是取這10000個圖片,然後生成這樣的三元組,然後訓練你的學習演算法,對這種代價函數用梯度下降,這個代價函數就是定義在你資料集裡的這樣的三元組圖片上。

注意,為了定義三元組的資料集你需要成對的A和P,即同一個人的成對的圖片,為了訓練你的系統你確實需要一個資料集,裡面有同一個人的多個照片。這是為什麼在這個例子中,我說假設你有1000個不同的人的10000張照片,也許是這1000個人平均每個人10張照片,組成了你整個資料集。如果你只有每個人一張照片,那麼根本沒法訓練這個系統。

當然,訓練完這個系統之後,你可以應用到你的一次學習問題上,對於你的人臉識別系統,可能你只有想要識別的某個人的一張照片。但對於訓練集,你需要確保有同一個人的多個圖片,至少是你訓練集裡的一部分人,這樣就有成對的Anchor和Positive圖片了。

現在我們來看,你如何選擇這些三元組來形成訓練集。一個問題是如果你從訓練集中,隨機地選擇A、P和N,遵守A和P是同一個人,而A和N是不同的人這一原則。有個問題就是,如果隨機的選擇它們,那麼這個約束條件(d(A,P)+a≤d(A,N))很容易達到,因為隨機選擇的圖片,A和N比A和P差別很大的概率很大。

我希望你還記得這個符號d(A,P)就是前幾個幻燈片裡寫的||f(A)-f(P)||2,d(A,N)就是||f(A)-f(N)||2,d(A,P)+a≤d(A,N)即||f(A)-f(P)||2+a≤||f(A)-f(N)||2。但是如果A和N是隨機選擇的不同的人,有很大的可能性||f(A)-f(N)||2會比左邊這項||f(A)-f(P)||2大,而且差距遠大於a,這樣網路並不能從中學到什麼。

所以為了構建一個資料集,你要做的就是盡可能選擇難訓練的三元組A、P和N。具體而言,你想要所有的三元組都滿足這個條件(d(A,P)+a≤d(A,N)),難訓練的三元組就是,你的A、P和N的選擇使得d(A,P)很接近d(A,N),即d(A,P)≈d(A,N),這樣你的學習演算法會竭盡全力使右邊這個式子變大(d(A,N)),或者使左邊這個式子(d(A,P))變小,這樣左右兩邊至少有一個a的間隔。

並且選擇這樣的三元組還可以增加你的學習演算法的計算效率,如果隨機的選擇這些三元組,其中有太多會很簡單,梯度演算法不會有什麼效果,因為網路總是很輕鬆就能得到正確的結果,只有選擇難的三元組梯度下降法才能發揮作用,使得這兩邊離得盡可能遠。

如果你對此感興趣的話,這篇論文中有更多細節,作者是Florian Schroff, Dmitry Kalenichenko, James Philbin,他們建立了這個叫做FaceNet的系統,我視頻裡許多的觀點都是來自於他們的工作。

Florian Schroff, Dmitry Kalenichenko, James Philbin (2015). FaceNet: A Unified Embedding forFace Recognition and Clustering

https://arxiv.org/pdf/1503.03832.pdf

順便說一下,這有一個有趣的事實,關於在深度學習領域,演算法是如何命名的。如果你研究一個特定的領域,假如說“某某”領域,通常會將系統命名為“某某”網路或者深度“某某”,我們一直討論人臉識別, 所以這篇論文叫做FaceNet(人臉網路),上個視頻裡你看到過DeepFace(深度人臉)。“某某”網路或者深度“某某”,是深度學習領域流行的命名演算法的方式,你可以看一下這篇論文,如果你想要瞭解更多的關於通過選擇最有用的三元組訓練來加速演算法的細節,這是一個很棒的論文。

總結一下,訓練這個三元組損失你需要取你的訓練集,然後把它做成很多三元組,這就是一個三元組(編號1),有一個Anchor圖片和Positive圖片,這兩個(Anchor和Positive)是同一個人,還有一張另一個人的Negative圖片。這是另一組(編號2),其中Anchor和Positive圖片是同一個人,但是Anchor和Negative不是同一個人,等等。

定義了這些包括A、P和N圖片的資料集之後,你還需要做的就是用梯度下降最小化我們之前定義的代價函數J,這樣做的效果就是反向傳播到網路中的所有參數來學習到一種編碼,使得如果兩個圖片是同一個人,那麼它們的d就會很小,如果兩個圖片不是同一個人,它們的d 就會很大。

這就是三元組損失,並且如何用它來訓練網路輸出一個好的編碼用於人臉識別。現在的人臉識別系統,尤其是大規模的商業人臉識別系統都是在很大的資料集上訓練,超過百萬圖片的資料集並不罕見,一些公司用千萬級的圖片,還有一些用上億的圖片來訓練這些系統。這些是很大的資料集,即使按照現在的標準,這些資料集並不容易獲得。

幸運的是,一些公司已經訓練了這些大型的網路並且上傳了模型參數。所以相比於從頭訓練這些網路,在這一領域,由於這些資料集太大,這一領域的一個實用操作就是下載別人的預訓練模型,而不是一切都要從頭開始。但是即使你下載了別人的預訓練模型,我認為瞭解怎麼訓練這些演算法也是有用的,以防針對一些應用你需要從頭實現這些想法。

這就是三元組損失,下個視頻中,我會給你展示Siamese網路的一些其他變體,以及如何訓練這些網路,讓我們進入下個視頻吧。

面部驗證與二分類

Triplet loss是一個學習人臉識別卷積網路參數的好方法,還有其他學習參數的方法,讓我們看看如何將人臉識別當成一個二分類問題。

另一個訓練神經網路的方法是選取一對神經網路,選取Siamese網路,使其同時計算這些嵌入,比如說128維的嵌入(編號1),或者更高維,然後將其輸入到邏輯回歸單元,然後進行預測,如果是相同的人,那麼輸出是1,若是不同的人,輸出是0。這就把人臉識別問題轉換為一個二分類問題,訓練這種系統時可以替換Triplet loss的方法。

最後的邏輯回歸單元是怎麼處理的?輸出y^會變成,比如說sigmoid函數應用到某些特徵上,相比起直接放入這些編碼(f(x(i)),f(x(j))),你可以利用編碼之間的不同。

我解釋一下,符號f(x(i))k代表圖片x(i)的編碼,下標k代表選擇這個向量中的第k個元素,|f(x(i))k- f(x(j))k|對這兩個編碼取元素差的絕對值。你可能想,把這128個元素當作特徵,然後把他們放入邏輯回歸中,最後的邏輯回歸可以增加參數wi和b,就像普通的邏輯回歸一樣。你將在這128個單元上訓練合適的權重,用來預測兩張圖片是否是一個人,這是一個很合理的方法來學習預測0或者1,即是否是同一個人。

還有其他不同的形式來計算綠色標記的這部分公式(|f(x(i))k-f(x(j))k|),比如說,公式可以是[f(x(i))k-f(x(j))k]2/[f(x(i))k+f(x(j))k],這個公式也被叫做x2公式,是一個希臘字母x,也被稱為x平方相似度。

Yaniv Taigman, Ming Yang, Marc'Aurelio Ranzato, Lior Wolf (2014). DeepFace:Closing the gap to human-level performance in face verification

https://research.fb.com/wp-content/uploads/2016/11/deepface-closing-the-gap-to-human-level-performance-in-face-verification.pdf

這些公式及其變形在這篇DeepFace論文中有討論,我之前也引用過。

但是在這個學習公式中,輸入是一對圖片,這是你的訓練輸入x(編號1、2),輸出y是0或者1,取決於你的輸入是相似圖片還是非相似圖片。與之前類似,你正在訓練一個Siamese網路,意味著上面這個神經網路擁有的參數和下面神經網路的相同(編號3和4所示的網路),兩組參數是綁定的,這樣的系統效果很好。

之前提到一個計算技巧可以幫你顯著提高部署效果,如果這是一張新圖片(編號1),當員工走進門時,希望門可以自動為他們打開,這個(編號2)是在資料庫中的圖片,不需要每次都計算這些特徵(編號6),不需要每次都計算這個嵌入,你可以提前計算好,那麼當一個新員工走近時,你可以使用上方的卷積網路來計算這些編碼(編號5),然後使用它,和預先計算好的編碼進行比較,然後輸出預測值y^。

因為不需要存儲原始圖像,如果你有一個很大的員工資料庫,你不需要為每個員工每次都計算這些編碼。這個預先計算的思想,可以節省大量的計算,這個預訓練的工作可以用在Siamese網路結構中,將人臉識別當作一個二分類問題,也可以用在學習和使用Triplet loss函數上,我在之前的視頻中描述過。

總結一下,把人臉驗證當作一個監督學習,創建一個只有成對圖片的訓練集,不是三個一組,而是成對的圖片,目標標籤是1表示一對圖片是一個人,目標標籤是0表示圖片中是不同的人。利用不同的成對圖片,使用反向傳播演算法去訓練神經網路,訓練Siamese神經網路。

這個你看到的版本,處理人臉驗證和人臉識別擴展為二分類問題,這樣的效果也很好。我希望你知道,在一次學習時,你需要什麼來訓練人臉驗證,或者人臉識別系統。

想要查看完整版課程筆記的同學請移步GitHub(附有課程作業代碼):

https://github.com/fengdu78/deeplearning_ai_books

同類文章
Next Article
喜欢就按个赞吧!!!
点击关闭提示