(科普)簡單的人臉識別

這是之前寫的一份期末報告,也放在了博客上:yangminz.github.io/post 和yangminz Blog - tensorface 都行,國內的話第二個速度快一點,而且對mathjax支持比較好。這個只能當初入門科普的東西來用吧,給沒做過的人看一看,行家就pass了

歷史的行程

人臉識別對於人類而言並不困難。在我們的計算機視覺課上,陳老師曾經拿過幾個名人的照片給我們識別,陳曉旭、布希……知道他們的人都可以認出照片對應的人,即使不認識照片對應的人,也能看出兩張照片描述的是不是同一個人。科學研究發現,甚至只出生了一天到三天的嬰兒,也能夠識別不同的人臉,即便嬰兒誰都不認識。人臉識別的機制與人類大腦的梭狀回面孔區有密切關係,我們有時候會在電視節目的奇聞異事中看到患有「臉盲症」的人,他們大腦的相應區域都出現了一些障礙,所以人臉識別能力比較低。但是對於計算機而言,人臉識別並不是一件簡單的事,尤其是在人腦的人臉識別機制尚未探清的情況下,我們主要依賴統計方法來幫助計算機進行人臉識別。

在歷史上,第一個真正意義上的人臉識別是在1965年完成的,Woody Bledsoe, Helen Chan Wolf, Charles Bisson都成為了人臉識別的先驅。他們運用幾何方法,人工標註面部特徵,計算出兩眼之間的距離比例、眼與鼻的角度等幾何特徵,在他們的數據集上獲得了不錯的效果。但是幾何方法由於其局限性太大,並沒有走到21世紀。

到了1991年,基於主成分分析的特徵臉方法被MIT的Matthew A. Turk和 Alex P. Pentland提出了。這對於人臉識別而言是一個里程碑式的進步,這樣基於統計的方法要優於基於幾何結構的方法,一時間成為了人臉識別演算法的主流,一直持續到現在,依然作為人臉識別中非常重要的方法。

在21世紀,隨著人工神經網路擺脫過去的陰霾,開始走向深度學習,基於卷積神經網路的一系列計算機視覺領域的突破給人臉識別帶來了新的方法。Krizhevsky在ImageNet上用卷積網路大殺四方,前後時間內人臉識別也運用了卷積網路的技術。到現在,深度學習已經成為計算機視覺領域內的一股有力的支流,像Face++這樣的公司展露頭角。

卷積神經網路

人工神經網路

人工神經網路的數學基礎是Kolmogorov和Arnold在50年代發展起來的函數逼近定理:

Phi (y)是單調有界連續增函數,其中y = f(x_1, x_2, ldots, x_n)只是一個有界閉子集上的連續函數,則:forall epsilon > 0,存在正整數Hc_j, 	heta_j,有w_{ij},i,j in { 1,2,ldots,n}

g(x_1,x_2,ldots,x_n) = sum_{j=1}^H c_j Phi left( sum_{i=1}^n w_{ij} cdot x_i - 	heta_j 
ight)

 s.t. max |f(x_1,x_2,ldots,x_n) - g(x_1,x_2,ldots,x_n)| < epsilon

其中Phi(cdot)在實際運用中一般為tanh, sigmoid這些常見的非線性函數。

在Kolmogorov定理的基礎上,複雜函數逼近的方法得以建立。到60年代時,在梯度下降演算法的幫助下,感知機模型被提出。到了80年代,人工神經網路被建立起來,而1986年Hinton提出的Back Propagation演算法更是通過求導的鏈式法則解決了高層網路難以反饋求導的問題。到了21世紀,硬體的發展使得人工神經網路的運算速度大為提升,逐漸走向深度學習。

歷史與發展

在21世紀,20世紀末發展的神經網路模型的變種——卷積神經網路開始隨著機器性能的提升而展現威力。1998年,Yann LeCun在他的驚世傳奇Gradient-based learning applied to document recognition中提出了第一個真正意義上的卷積神經網路模型——LeNet-5,Yann LeCun利用這個模型在Mnist手寫數據集識別上取得了突破性的進展,而這個數據集至今仍作為機器學習入門的數據集在使用。

在2006年,Hinton則提出了他的驚世駭俗之神作Reducing the dimensionality of data with neural networks,點明了深層網路對於特徵學習能力的優越性,這為後來如火如荼的深度學習奠定了基礎。

到了2012年,Hinton的親傳弟子Krizhevsky提出了他的CNN模型AlexNet,在ImageNet上一鳴驚人,以極大的差距擊敗第二名摘得桂冠。同時,他的論文Imagenet classification with deep convolutional neural networks也將永遠被人們銘記,因為這標誌著人工智慧的研究真正開始進入深度學習階段。

但是,我們也可以看到,實際上真正的理論創新在20世紀就已經止步不前了,如今深度學習能夠如此繁花似錦、烈火烹油,完全得益於硬體的加速。回顧歷史,我們可以發現,人工智慧這一領域,基本上始終是圍繞著神經網路為中心展開的,雖然其中有SVM異軍突起,但終究扭轉不了歷史的行程。

卷積神經網路的原理

數學上的離散卷積公式是這樣的:

xieta是獨立的隨機變數,且都取非負整數值,概率分布分別是{a_k}b_k,則隨機變數zeta = xi + eta的概率分布為:

 P{zeta = r} = sum_{k = 0}^r P{ xi=k, eta=r-k } = sum_{k = 0}^r a_k b_{r - k}

而實際上神經網路的原理和卷積公式的關聯並不密切。以1維的卷積網路為例:

可以看到它實際上只是一個窗口上的加權平均。但是這樣的網路,卻在圖像處理方面有著巨大的優勢。

眾所周知,傳統網路對於複雜特徵的學習非常具有優勢,但是在人臉識別這樣複雜的工程中,如果使用傳統的神經網路會帶來一個非常嚴重的後果——模型參數過多。由於灰度圖片作為輸入時,每個像素上的值都是一個輸入,而圖片上的像素點又是何其多,給每個輸入分配一個權重,這樣的訓練未免太強機器所難。因此,解決這個問題的方法是減小網路參數,在每層網路上使用全局參數,這就是LeCunn的論文的想法。

上面這張圖出自Stanford University的課程cs231n。這張圖描述了傳統的神經網路與卷積網路的差異。左圖是一張3層的全連接網路,可以看到即便輸入只在mathbb{R}^3,但到輸出層的時候保存的參數已經不少了,如果輸入達到3 	imes 1920 	imes 1080這樣的級別,那麼參數更是爆炸性的多,訓練根本無從下手。

但是卷積網路則可以避免這個弊端。可以看到左圖的這個網路,對於一個RGB三色通道的mathbb{R}^{3 	imes H 	imes W}的圖像輸入,卷積網路通過卷積濾波器(卷積核)提取同一張圖像的不同變換下的特徵,需要保存的權重矩陣的數量只有濾波器深度那麼多,這樣大大減小了模型的參數量。

卷積操作

卷積網路的關鍵之一自然是卷積操作。我們依然借用cs231n的圖來做如下說明:

上圖是一個RGB三色通道mathbb{R}^{3 	imes H 	imes W}的圖像輸入。左邊三張藍色矩陣是圖像的三色通道,中間有兩列粉紅色的mathbb{R}^{3 	imes 3 	imes 3}的卷積濾波器矩陣mathbf{W_0}, mathbf{W_1},分別對應著右邊兩張綠色的輸出矩陣。在這裡要注意,卷積濾波器的深度必須要和圖像輸入的深度一致,這是顯而易見的,在上圖中為3。卷積濾波器的個數可以任意設定,這裡是2。

進行卷積操作時,mathbf{W_0}的3個深度的矩陣分別遍歷3個深度上的圖像輸入,並且做向量數量積形式的加權平均,然後將三個數值加到一起,作為卷積層的一個數值。在上圖中,對應關係已經標註得很清晰了。

卷積操作的實驗

可見通過這樣的模型,確實能夠有效地減少模型的參數。我們可以通過傳統網路背後的Kolmogorov定理來理解卷積網路,認為這是一種特殊的參數設定。但是問題來了,雖然我們知道卷積網路能夠減少網路參數,而且也是Kolmogorov定理的一種變形,但是它為什麼能夠工作,這一問題回答得還不足夠清楚。

一種通俗而且形象的解釋是類比於人腦中的神經元。人腦中的神經元的結構實際上更類似於卷積網路而非傳統的全連接網路:

人腦的神經元也是局部連接的,並不是一個神經元與所有其他神經元相連,因此卷積網路和人腦更相似,所以特徵學習能力更強。實際上這也是可以理解的,表現在圖像上就是卷積網路更能抓住局部特徵,無視圖像遠方的干擾信息。也就是說,當一個卷積操作在眼睛的局部提取眼睛的局部信息時,並不會受到遠方鼻子、嘴巴信息的干擾,因此卷積網路具有更強的局部特徵學習能力。

我在實驗中利用Keras寫了這樣一個簡單的卷積網路

model = Sequential()# Building: conv1 - tanh - maxpoolingmodel.add(Convolution2D(filter1, conv_side, conv_side, border_mode=same, subsample = (2, 2), dim_ordering=tf, input_shape=(h, w, 1)))# print model.output_shapemodel.add(Activation(tanh))model.add(MaxPooling2D(pool_size=(pool_side, pool_side)))# Building: conv2 - tanh - maxpoolingmodel.add(Convolution2D(filter2, conv_side, conv_side)) model.add(Activation(tanh)) model.add(MaxPooling2D(pool_size=(pool_side, pool_side))) # model.add(Dropout(0.25)) # Building: flat - dense - tanh - dense - softmaxmodel.add(Flatten()) model.add(Dense(1000)) #Full connection model.add(Activation(tanh)) # model.add(Dropout(0.5)) model.add(Dense(SUBJECT_NUM)) model.add(Activation(softmax)) sgd = SGD(lr=sgd_lr,decay=sgd_decay,momentum=sgd_momentum,nesterov=True)model.compile(loss=categorical_crossentropy, optimizer=sgd)return model

訓練好以後保存了模型以及參數。然後將圖像輸入這個訓練好的模型中,將第一次卷積的中間結果取出,經過歸一化到[0,255]處理,還原出圖像:

上下兩行4張圖分別是某個人的臉經過卷積濾波器,在卷積層的前4深度上的圖像。可以看到,不同深度的濾波器實際上在抓取不同的特徵,比如卷積濾波器mathbf{W_0}抓到的特徵(0,0)這張臉在圖像上表現為藍色,而mathbf{W_2}抓到的特徵(0,2)則表現為紅色。

池化及實驗

除了卷積操作以外,總是成對出現的操作有池化(pooling):

我理解池化是進一步丟棄數據的過程。上圖是一個最大池化的例子,在每個池化窗口中,選出最大值作為池化層的數據。這樣的丟棄數據實際上是具有某種「隨意性」的,這給反向求導帶來了不可捉摸的困難,但是現在大家寫卷積網路基本都用機器學習的框架,所以也就可以不考慮這個問題了,把精力集中在前向傳播上。總之,這種「隨意性」以及無法精確的反向求導,能夠比較好地對抗過擬合問題。

可以看到第一層池化層的結果如上圖所示,這個結果其實我自己也沒怎麼搞明白為什麼會丟掉這麼多的數據,但是到第二層卷積與池化之後就變得正常了。

這是第二層卷積。可以看到,經過一次卷積與池化以後,到第二層卷積層時,卷積層輸入已經被大大削減,而且基本保留了原圖像的重要特徵:眼睛、鼻子、嘴巴。可見,卷積網路的學習能力是很強的。

可以看到,到第二層池化以後,特徵更加明顯,並且輸入更小。也就是說,採用卷積與池化,實際上是一個在儘可能保留特徵的前提下,不斷減小圖像規模的過程。從結果來看,訓練好的卷積網路將這兩點結合得很好,到第二次池化時,也就是上面這張圖,圖像的抽象層次基本上只保留了眼睛、鼻子、嘴巴這樣的特徵了。到這一步,接下來再用一個傳統網路作為分類器,代價就並不高了。所以我在模型中經過兩層卷積以後就進行了全連接分類。

其實我覺得可以從類比積分中值定理的角度來理解卷積和池化。

fg都在區域Omega上可積,且fOmega上連續,gOmega上不變號,則:

exists xi in Omega, s.t.

int_{Omega} f cdot g dV = f(xi) int_{Omega} g dV

可以看到,積分中值定理實際上是在函數連續的條件下選取了一個代表f(xi)來代表整個區域Omega。而對於一張合理的圖像而言,也應該具有某種連續性與局部性。所以,對於每一個局部區域,卷積實際上是找了一個代表來進行降維。卷積操作也可以這樣粗糙地理解,它是用不同的代表(特徵)來表徵一個局部區域。由於圖像語義的局部性,所以局部卷積和池化能夠排除其他局部的、全局的干擾;由於連續性,所以能夠在保持局部特徵的前提下不斷壓縮圖像規模。

需要注意的是,到這裡我們可以看到,不論是卷積操作亦或是池化操作,都基於輸入在局部區域上保持著「連續性」與「局部性」。這個特點應該是一張合乎情理的圖像所與生俱來的,也是卷積網路之所以能在計算機視覺領域大展神威的原因之一。但是,如果我們做一個簡單的思想實驗,對於圖像進行外延:如果我們的技術能夠超越離散進化到連續的程度,那麼就能找到這樣一種圖像,它「處處不連續、處處不可微」,也就是類似於Dirichlet函數,並且在取值上更加混沌的圖像,那麼卷積網路應該是無能為力的。

影響網路的超參數

影響卷積網路的超參數有很多,隱藏層的大小、卷積層的個數、卷積窗口的大小、步長、是否做padding(0或1)……在一篇合格的文獻中基本上都能夠找到相關超參數的性質分析,而且我也沒做改變超參數的實驗,所以就沒必要一一說明了。

在這裡我只想簡單說一下網路層數的影響。因為卷積與池化都是局部操作,所以每一層只獲得了前一層的局部信息,但是顯然層數累加的時候這樣的局部信息也在累加。也就是說,層數越高,每一層獲取的圖片信息「密度」就越高,它越能從「局部感知」走向「全局感知」。

主成分分析

相比於與人工智慧始終緊密聯繫的神經網路,主成分更像是傳統的數理統計方法。因此它並不像神經網路那樣充滿了不可知的「玄學」(甚至於有人將其比喻為「黑箱」),主成分分析從構想,到數學推導,再到結果,都一氣呵成,一俟現世就已經成熟,充滿了優雅。如果打一個不恰當的比喻,主成分像是Alexis de Tocqueville筆下的舊貴族,浴沂風雩詠而歸,而神經網路則像是他筆下的民主人,終日乾乾,夕惕若厲。

主成分分析的歷史

主成分分析(PCA)由Karl Pearson在1901年首創,距今已經有超過100年的歷史了,當時大清還沒亡。它是為了解決統計力學問題應運而生的,後來被廣泛地運用在信號處理等領域。到現在,PCA在複雜統計數據的處理上享有赫赫威名,人臉識別就是其應用之一,這部分的歷史在第一節就已經講過了。

PCA的本質

PCA在本質上是一個特徵分析多元統計分布的方法,它要尋找高維的數據空間上對方差影響最大的方向。從幾何上講,樣本點mathbf{x_1}, ldots, mathbf{x_n}mathbb{R}^d上形成了橢圓狀的雲團,PCA要找到就是這個雲團的主軸,也就是散布最大的方向,從而實現對空間的降維。

在前一節卷積網路中,我們有提到過,實際上CNN也實現了降維,不過它並不直白,而且保留了圖像的特徵。在PCA中,降維是直觀的,目的是找到正確的方向,投影樣本數據以後依然能夠有效地分類。

數學推導

假定我們有人臉數據集mathbf{X} = {mathbf{x_1}, mathbf{x_2}, cdots, mathbf{x_n} } in mathbb{R}^{n 	imes d}

降到mathbb{R}^0的場合

mathbb{R}^0場合是指,我們希望找到一個mathbf{x_0} in mathbb{R}^{d}點就能代表所有的人臉樣本點。理論上講是所有的人臉樣本點都投影到這個點,但從實際效果上講就是找到mathbf{x_0}能最好地代表人臉樣本就行。或者說:與所有樣本之間的距離的平方和越小越好:

J_0(mathbf{x_0}) = sum_{k = 0}^n || mathbf{x_0} - mathbf{x_k} ||^2

拆開目標函數:

J_0(mathbf{x_0}) = sum_{k = 1}^n || (mathbf{x_0} - mu ) - ( mathbf{x_k} - mu ) ||^2= n cdot || mathbf{x_0} - mathbf{mu} ||^2 + sum_{k =1}^n || mathbf{x_k} - mathbf{mu} ||^2

右邊一項並不依賴於mathbf{x_0}是常量,所以左邊一項取均值mu時目標函數取極小值。

由此可以知道,在mathbb{R}^0場合下,最能代表所有人臉樣本的就是均值點。但是mathbb{R}^0顯然是降維過頭了,到這個程度已經沒有方向可言了,分開數據更無從談起,所以下面要考慮到投影到mathbb{R}^1的場合。

mathbb{R}^1的場合

現在考慮降到一維的情況,也就是將樣本點投影到一條經過均值點的直線上,讓這根直線上的投影點來代表全體數據點:

mathbf{x} = mathbf{mu} + a cdot mathbf{e}

在這裡,標量a反應了投影點對均值的偏離程度,mathbf{e}是單位方向向量。

現在,我們希望最小化投影點和實際點之間的差異:

||(mathbf{mu} + a_k cdot mathbf{e}) - mathbf{x_k}||

所以構造平方誤差函數:

J_1(a_1, cdots, a_n, mathbf{e}) = sum_{k=1}^n ||(mathbf{mu} + a_k cdot mathbf{e}) - mathbf{x_k}||^2

展開處理:

J_1 = sum_{k=1}^n a_k^2 - 2sum_{k=1}^n a_k mathbf{e^T}(mathbf{x_k - mu}) + sum_{k=1}^n ||mathbf{x_k - mu}||^2

這樣就可以對a_k求偏導:

frac{partial J_1}{partial a_k} = 2a_k - 2mathbf{e^T}(mathbf{x_k - mu}) = 0 Rightarrow a_k = mathbf{e^T}(mathbf{x_k - mu})

從幾何上講,這告訴我們向量mathbf{x_k}只要向直線mathbf{e}做垂直投影,就能得到最小方差。接下來的問題是:如何找到直線mathbf{e}的最優方向?這就需要引入散布矩陣的概念:

mathbf{S} = sum_{k=1}^n (mathbf{x_k - mu})(mathbf{x_k - mu})^T

散布矩陣實際上是協方差矩陣的n?1倍。用散布矩陣來描述目標函數:

J_1(mathbf{e}) = -mathbf{e^TSe} + sum_{k=1}^n ||mathbf{x_k - mu}||^2

用Lagrange乘子法求||mathbf{e}||=1的條件極值:

u = -mathbf{e^TSe} + lambda left( mathbf{e^Te} - 1 
ight)

mathbf{e}求偏導:

 frac{partial u}{partial mathbf{e}} = 2mathbf{Se} - 2lambdamathbf{e} = 0 Rightarrow  mathbf{Se} = lambdamathbf{e}

所以,單位方向向量一定和特徵向量平行!要讓mathbf{e^TSe} = lambdamathbf{e^Te} = lambda最大,就要選取散布矩陣最大的特徵值所對應的特徵向量作為方向向量。

高維場合

如果要提取到高維空間d(更多)的特徵向量呢?

mathbf{x = mu} + sum_{i=1}^{d} a_i mathbf{e_i}

取散布矩陣前d個最大的特徵值所對應的特徵向量就行了。

此外,由於散布矩陣實對稱,所以這些特徵向量相互正交,相互獨立。

numpy實驗

為了更直觀地展現PCA的效果,我用numpy寫PCA時,也像CNN一樣提取了兩個中間結果:

第一張圖是將選出來的特徵向量都還原為圖片所得的結果,可以看到每一個特徵向量都抓取到了不同的人臉樣本的面部特徵,這些特徵和CNN一樣主要集中在眼睛、鼻子、嘴巴等五官上。

第二張圖則對比不同特徵向量的數量的影響。在實驗中選擇了不同數量的特徵向量,也就是投影的正交坐標的個數,然後將它們還原成人臉。可以看到,特徵向量越多,人臉就越清晰。這是當然的,投影越多越接近原圖,當取全部特徵向量做投影時,實際上也就恢復成原來的人臉了。實際上這方面和矩陣的秩有關,因為特徵值的個數與矩陣的秩相同,深究下去的話,可能會發現灰度圖像與它的矩陣的秩之間有深刻的聯繫。

人臉識別:既成熟又不成熟的技術

上面兩個模型,在AT&T與Cambridge大學聯合做出來的AT&T數據集The Database of Faces上都有近乎100%的識別率,難道我們就可以說人臉識別到現在是一項成熟的技術了嗎?非也,實際上在更複雜的課題中,模型的表現並不總是令人滿意。

更複雜的應用場景

就單拿靜態照片的識別而言,主要有兩個重大的問題:光照變化和姿態變化。

不同光照條件下的照片會給圖像帶來巨大的灰度相對分布,這樣引起的光照變化帶來的同一張人臉之間的差異甚至可能超過其他的人臉之間的差異,也就是類內差異大於類間差異。下圖是經過剪裁的Yale的人臉數據集vision.ucsd.edu/~iskwak,可以看到光照變化的影響:

對比上面三張圖,我們甚至難以認出第一張和第二張是同一個人!對於這樣的光照變化,計算機科學家們尋求的解決方法有改進現有演算法、合成全面的人臉模型。

另一個問題是姿態變化,當一個樣本在數據集中有時戴眼鏡,有時戴口罩,有時候把頭轉過去,這樣的姿態變化也會使識別變得非常困難。解決這個問題的構想是使用微分流形的思想用2維投影線性組合重構3維對象。

這兩個問題都是走出實驗室,在人臉識別的實際應用中非常關鍵的問題,雖然都有進展,但是距離完全解決都有一段距離。因此,在複雜應用場景下的人臉識別,還是一個不夠成熟的技術。

光照變化實驗

針對上面的光照變化問題,我又補充了一個光照變化的實驗。這次用的數據集是Yale的擴展數據集,就如上圖所示,它具有充分的光照變化。我依然使用之前寫好的CNN和PCA,重新訓練以後進行預測,發現:卷積網路的識別率依然維持在近乎100%的水平,而PCA降到了66%左右。

上面這張圖抽取了PCA中的前16個特徵向量並且還原成人臉,可以看到很多特徵向量收到光照的污染非常嚴重。因此,在PCA中,將受污染的特徵向量丟棄,會有助於提高識別率。如何找到這些受污染的特徵向量就是另一個話題了。

而對於卷積網路,由於它對於人臉特徵的抽象層次較高,能夠過濾掉一些光照干擾,所以識別率依然接近100%。但是,需要說明的是,也存在著CNN過擬合的風險。

人臉識別系統

人臉識別系統是一個非常複雜的工程。如果能在實際中應用,需要結合攝像技術、計算機視覺、機器學習等諸多領域的內容。就以公安系統的人臉識別為例,需要通過攝像頭抓取圖像,然後能夠對圖像剪裁,將不同的人臉分辨出來。然後通過與資料庫中的儲存的犯罪嫌疑人的照片進行學習、識別。每一個過程,考慮到數據量和實際應用場景,都是很複雜的,不同於上面兩個模型試驗,需要計算機科學家與工程師們的不懈努力。

實驗代碼tensorface簡介

略,這部分可以直接去我博客上看,放在這裡太瑣碎了

參考資料

Stanford, CS231n, Computer Vision

github.com/bytefish/fac guide

Keras, Keras Documentation

AT&T數據集

Yale數據集


推薦閱讀:

讀Focal Loss
Caffe2 教程--5. A Toy Regression
Learning to Segment Every Thing論文導讀
brox近期論文
【小林的OpenCV基礎課 番外】卷積與濾波

TAG:人臉識別 | 深度學習DeepLearning | 計算機視覺 |