有一次,我和Vito(我的合夥人)聊起了當下熱門的幾種技術趨勢。當談及它們在未來可能的發展前景的時候,Vito說了下面的一段話:
人工智慧是個資訊革命到蒸汽機規模之間的機會,相比之下虛擬現實應該是移動網際網路級別的,而使用者個性化服務應該是伴生規模的。
如果人工智慧技術帶來的變革確實能夠比擬工業革命的話,那麼它勢必會成就一代人,同時也淘汰掉一代人。而且,仔細想想,其實人工智慧離我們並不遙遠,甚至可以說已經開始深入到我們的日常生活中了。從iPhone裡的Siri,到各大網站的內容推薦系統,再到影像識別和人臉識別技術的廣泛應用,這些場景的背後都有這項技術在發揮作用。
作為程式設計師,以機器學習和深度學習為代表的人工智慧技術,與我們的關係則更加緊密。現在凡是有些規模的網際網路公司,基本都有專門研究演算法的團隊。在資料探勘、Antispam、推薦系統和廣告系統,以及其它一些領域,我們都多多少少會涉及到一些機器學習的技術。即使我們不親自負責開發和維護這些技術,在工作中也難免會與之產生交集。
說了這麼多,我其實想強調的一點是:任何人都應該瞭解一點跟人工智慧有關的技術,因為這是無法阻擋的大潮,是不可避免的未來趨勢。
而對於一名沒有涉及到任何這方面技術的工程師來說,這項技術本身的獨特性也絕對值得你花時間去了解。你一旦瞭解就會發現,這是一種全然不同的程式設計方式。
本文就是這樣的一篇科普文章,目的是向所有沒有接觸過人工智慧技術的程式設計師(甚至非技術人員),介紹人工智慧領域最前沿的神經網路和深度學習方面的知識。也許,你看完之後,會像我第一次接觸它們的時候一樣,驚奇地感嘆:這種程式設計方式簡直是造物主留下的一個後門!竟然用如此簡單的演算法就能實現出遠遠超越原本設計的智慧!
好了,蓄勢完畢,相信現在你對於是否願意花時間讀完剩下的內容,已經做出自己的決定了。實際上,這項科普的工作並不輕鬆,因為這項技術涉及到不少數學知識。為了避免閱讀障礙,我會嘗試在描述的過程中儘量不引入複雜的數學公式,同時讓講解儘量有趣。
感知器
要想理解深度學習,我們就必須先理解人工神經網路,因為神經網路是深度學習的基礎。而要理解神經網路,我們就必須先理解它的基本組成單元——神經元(neuron)。
感知器(perceptron)是一種早期的神經元結構,在上個世紀五六十年代就被提出來了[1]。現在它在神經網路中已很少被使用,但理解它有助於理清其它型別神經元的基本結構和設計思路。
如上圖所示,一個感知器的定義元素包括:
- 有多個輸入:x1, x2, x3, …, 它們只能是0或1。
- 有一個輸出:output. 只能是0或1。
- 每個輸入對應一個權重值:w1, w2, w3, …, 它們可以是任意實數。
- 有一個閾值:threshold. 可以是任意實數。
- 輸出output取決於各個輸入的加權求和與閾值threshold的大小,即:如果w1x1 + w2x2 + w3x3 + … > threshold,則輸出output=1,否則輸出output=0。
直觀上理解,感知器相當於一個決策模型。輸入表示進行決策時需要考慮的外在因素或條件,權重表示你對某個外在因素的重視程度,而閾值則表示你對於這個決策事件本身的喜好程度或接受程度。
舉一個例子:假設週末有一個同學聚會,現在你正在決策要不要去參加。你考慮的因素如下:
- 如果那天天氣好,那麼你就更有意願去參加。用x1=1表示天氣好,x1=0表示天氣不好。你對於天氣這個因素的重視程度為w1=3。
- 如果某個你討厭的人也去參加聚會,那麼你就興趣索然,不太樂意去了。用x2=1表示你討厭的那個人去參加聚會,x2=0表示那個人不參加聚會。對應權重w2=-5,負值表示這個因素的出現會降低你去參加聚會的意願。
- 但如果你暗戀的一個女孩去參加聚會,那麼你無論如何也是想去的。用x3=1表示那個女孩去參加聚會,x3=0表示她不參加聚會。這個女孩對於你太重要了,所以有一個很大的權重:w3=10。
現在假設閾值threshold=2。我們根據前面的規則去計算output,這個計算過程就相當於決策過程。如果output算出來等於1,那麼你就去參加聚會,否則就不去。
決策結果無非是下面幾種:
- 如果你暗戀的女孩去參加聚會,那麼不管其它因素,你肯定就去了。因為權重w3實在太大了,不管另外的輸入是多少,都會導致加權求和後超過threshold=2。
- 你暗戀的那個女孩不去參加聚會,而你討厭的那個人去參加聚會。這時不管天氣如何,你都不會去了。
- 你暗戀的那個女孩和你討厭的那個人都不去參加聚會。那麼你去不去最終取決於天氣怎麼樣。
對於一個給定的感知器來說,它的權重和閾值也是給定的,代表一種決策策略。因此,我們可以通過調整權重和閾值來改變這個策略。
關於閾值threshold,這裡需要指出的一點是,為了表達更方便,一般用它的相反數來表達:b=-threshold,這裡的b被稱為偏置(bias)。這樣,前面計算輸出的規則就修改為:如果w1x1 + w2x2 + w3x3 + … + b > 0,則輸出output=1,否則輸出output=0。
很明顯,只有當x1=x2=1的時候,output=0,因為(−2)*1+(−2)*1+3=−1,小於0。而其它輸入的情況下,都是output=1。這其實是一個“與非門”!
在電腦科學中,與非門是所有門部件中比較特殊的一個,它可以通過組合的方式表達任何其它的門部件。這被稱為與非門的普適性(Gate Universality)[2]。
既然感知器能夠通過設定恰當的權重和偏置引數,來表達一個與非門,那麼理論上它也就能表達任意其它的門部件。因此,只要建立足夠多的感知器,那麼它們便能夠通過彼此連線從而組成一個計算機系統。但這似乎沒有什麼值得驚喜的,我們已經有現成的計算機了,這只不過是讓事情複雜化了而已。
訓練和學習
單個感知器能做的事情很有限。要做複雜的決策,我們可能需要將多個感知器連線起來。就像下面這個一樣:
這個由感知器組成的網路,包含5個輸入,8個感知器。權重引數的數量,我們可以算一下:5*3+3*4+4*1=31。再加上8個偏置引數,這個網路總共有39個引數。
這個圖有一點需要說明的是:左邊第一層的每個感知器看起來似乎有4個輸出,而不是1個。但這是個錯覺。實際情況是每個感知器的那唯一的一個輸出分別連線到了下一層的各個感知器的輸入上了。這種表示法是為了方便。輸出端的多條連線只是表示連線關係,而不表示輸出的個數。
這個感知器網路還算是一個簡單的網路,就已經有多達39個引數了。而實際中的網路可能會有上萬個,甚至數十萬個引數。如果手工一個一個地去配置這些引數,恐怕這項任務永遠也完成不了了。
而神經網路最有特色的地方就在於這裡。我們不是為網路指定所有引數,而是提供訓練資料,讓網路自己在訓練中去學習,在學習過程中為所有引數找到最恰當的值。
如何訓練呢?大體思路是這樣:我們告訴網路當輸入是某個值的時候,我們期望的輸出是什麼。這樣的每一份訓練資料,稱為訓練樣本(training example)。這個過程相當於老師在教學生某個抽象的知識的時候,舉一個具體例子。一般來說,我們舉的例子越多,就越能表達那個抽象的知識。這在神經網路的訓練中同樣成立。我們可以向網路灌入成千上萬個訓練樣本,然後網路就自動從這些樣本中總結出那份隱藏在背後的抽象的知識。這份知識的體現,就在於網路的所有權重和偏置引數的取值。
假設各個引數有一個初始值。當我們輸入一個訓練樣本的時候,它會根據當前引數值計算出唯一的一個實際輸出值。這個值可能跟我們期望的輸出值不一樣。想象一下,這時候,我們可以試著調整某些引數的值,讓實際輸出值和期望輸出值儘量接近。當所有的訓練樣本輸入完畢之後,網路引數也調整到了最佳值,這時每一次的實際輸出值和期望輸出值已經無限接近。這樣訓練過程就結束了。
假設在訓練過程中,網路已經對數萬個樣本能夠給出正確(或接近正確)的反應了,那麼再給它輸入一個它沒見過的資料,它也應該有很大概率給出我們預期的決策。這就是一個神經網路工作的原理。
但這裡還有一個問題。在訓練過程中,當實際輸出值和期望輸出值產生差異的時候,我們如何去調整各個引數呢?當然,在思考怎麼做之前,我們應該先弄清楚:通過調整引數的方式獲得期望的輸出,這個方法可行嗎?
實際上,對於感知器網路來說,這個方法基本不可行。比如在上圖有39個引數的感知器網路中,如果維持輸入不變,我們改變某個引數的值,那麼最終的輸出基本完全不可預測。它或者從0變到1(或從1變到0),當然也可能維持不變。這個問題的關鍵在於:輸入和輸出都是二進位制的,只能是0或者1。如果把整個網路看成一個函式(有輸入,有輸出),那麼這個函式不是連續的。
因此,為了讓訓練成為可能,我們需要一個輸入和輸出能夠在實數上保持連續的神經網路。於是,這就出現了sigmoid神經元。
sigmoid神經元
sigmoid神經元(sigmoid neuron)是現代神經網路經常使用的基本結構(當然不是唯一的結構)。它與感知器的結構類似,但有兩個重要的區別。
第一,它的輸入不再限制為0和1,而可以是任意0~1之間的實數。
第二,它的輸出也不再限制為0和1,而是將各個輸入的加權求和再加上偏置引數,經過一個稱為sigmoid函式的計算作為輸出。
具體來說,假設z=w1x1+w2x2+w3x3+…+b,那麼輸出output=σ(z),其中:
σ(z) = 1/(1+e-z)
可見,σ(z)是一個平滑、連續的函式。而且,它的輸出也是0~1之間的實數,這個輸出值可以直接作為下一層神經元的輸入,保持在0~1之間。
可以想象,在採用sigmoid神經元組裝神經網路之後,網路的輸入和輸出都變為連續的了。也就是說,當我們對某個引數的值進行微小的改變的時候,它的輸出也只是產生微小的改變。這樣就使得逐步調整引數值的訓練成為可能。這個思想如下圖所示:
一個經典的應用案例
為了說明神經網路如何具體應用。這裡我們引入一個經典的案例。這個例子來自Michael Nielsen的書《Neural Networks and Deep Learning》[3],是利用神經網路對於手寫體數字進行識別。當然,這個例子在歷史上,很多研究人員也都做過嘗試。
這裡順便說一句,Michael Nielsen 的這本書真的很贊,沒見過哪一份資料能把神經網路和深度學習講解得這麼透徹。這本書簡直稱得上是神經網路的科普聖經,感興趣的初學者一定要讀一讀。
這個問題就是對類似下面這樣的手寫體數字進行識別,區分出它們具體是0到9哪一個數字:
這份手寫體資料其實來源於一個公開的資料集,稱為MNIST[4]。其中每個數字,是一張28畫素×28畫素的黑白圖片,每個畫素用一個灰度值表示。
左側第一列圓圈表示網路的784個輸入(注意圖中沒有畫出全部),對應一張圖片的28×28=784個畫素點。每個畫素的灰度值,在經過歸一化處理之後,可以表達為0~1之間的數值,作為這裡的輸入。注意:這一列圓圈並不是神經元(雖然看起來像),只是輸入而已。
中間一列稱為隱藏層(hidden layer),圖中畫出的是15個神經元節點。隱藏層上的每一個節點都與每個輸入連線,也就是說輸入層和隱藏層之間是全連線。
這個神經網路只有一層隱藏層,屬於淺層的神經網路(shallow neural networks)。而真正的深度神經網路(deep nerual networks),則會有多層隱藏層。
最右側一列是輸出層(output layer),有10個神經元節點,分別代表識別結果是0,1,2,…,9。當然,受sigmoid函式σ(z)的限制,每個輸出也肯定是0~1之間的數。那我們得到一組輸出值之後,我們到底認為識別結果是哪個數字呢?我們可以根據哪個輸出的值最大,我們就認為識別結果就取那個數字。而在訓練的時候,我們期望的輸出形式是:正確的那個數字輸出為1,其它輸出為0。隱藏層和輸出層之間也是全連線。
我們可以算一下這個神經網路共有多少個引數。權重引數有784*15+15*10=11910個,偏置引數有15+10=25個,總共引數個數為:11910+25=11935個。
對於這個神經網路的訓練過程,就是要確定這11935個引數。訓練的目標可以粗略概括為:對於每一個訓練樣本,我們期望的那個正確數字,對應的輸出無限接近於1,而其它輸出無限接近於0。
先不說具體的學習方法(下一節會介紹),我們先說一下神經網路這種程式設計方式在這一具體問題上取得的結果。根據Michael Nielsen給出的實驗結果,以上述網路結構為基礎,在未經過調優的情況下,可以輕鬆達到95%的正確識別率。而核心程式碼只有74行!
在採用了深度學習的思路和卷積網路(convolutional networks)之後,最終達到了99.67%的正確識別率。而針對MNIST資料集達到的歷史最佳成績是99.79%的識別率,是由Li Wan, Matthew Zeiler, Sixin Zhang, Yann LeCun, 和 Rob Fergus在2013年做出的。
考慮到這個資料集裡還有一些類似如下這樣難以辨認的數字,這個結果是相當驚人的!它已經超越了真正人眼的識別了。
在本文前面一節,我們已經對神經網路的訓練過程進行了描述,但其中關鍵的一步還沒有介紹,就是如何在這個過程中一步步調整權重和偏置引數的值呢?要講清楚這個問題,我們就必須引入梯度下降演算法(gradient descent)。
隨機梯度下降
在訓練的過程中,我們的神經網路需要有一個實際可行的學習演算法,來逐步調整引數。要設計這樣一個學習演算法,我們首先要明確訓練的目標。
我們訓練的最終目的,是讓網路的實際輸出與期望輸出能夠儘量接近。我們需要找到一個表示式來對這種接近程度進行表徵。這個表示式被稱為代價函式(cost function)。
這是本文出現的最複雜的一個公式了。但不用恐懼,我們對它分析一下,只要能理解它的主旨就好:
- x表示一個訓練樣本,即網路的輸入。其實一個x代表784個輸入。
- y(x)表示當輸入為x的時候,期望的輸出值;而a表示當輸入為x的時候,實際的輸出值。y(x)和a都分別代表10個輸出值(以數學上的向量來表示)。而它們的差的平方,就表徵了實際輸出值和期望輸出值的接近程度。越接近,這個差值就越小。
- n是訓練樣本的數量。假設有5萬個訓練樣本,那麼n就是5萬。因為是多次訓練,所以要除以n對所有訓練樣本求平均值。
- C(w,b)的表示法,是把cost function看成是網路中所有權重w和偏置b的函式。為什麼這樣看呢?進行訓練的時候,輸入x是固定的(訓練樣本),不會變。在認為輸入不變的情況下,這個式子就可以看成是w和b的函式。那麼,式子右邊的w和b在哪呢?實際上,在a裡面。y(x)也是固定值,但a是w和b的函式。
總結來說,C(w,b)表徵了網路的實際輸出值和期望輸出值的接近程度。越接近,C(w,b)的值就越小。因此,學習的過程就是想辦法降低C(w,b)的過程。而不管C(w,b)的表達形式如何,它是w和b的函式,這就變成了一個求函式最小值的最優化問題。
由於C(w,b)的形式比較複雜,引數也非常多,所以直接進行數學上的求解,非常困難。為了利用計算機演算法解決這一問題,電腦科學家們提出了梯度下降演算法(gradient descent)。這個演算法本質上是在多維空間中沿著各個維度的切線貢獻的方向,每次向下邁出微小的一步,從而最終抵達最小值。
由於多維空間在視覺上無法體現,所以人們通常會退到三維空間進行類比。當C(w,b)只有兩個引數的時候,它的函式影像可以在三維空間裡呈現。如下所示:
就好像一個小球在山谷的斜坡上向下不停地滾動,最終就有可能到達谷底。這個理解重新推廣到多維空間內也基本成立。
而由於訓練樣本的數量很大(上萬,幾十萬,甚至更多),直接根據前面的C(w,b)進行計算,計算量會很大,導致學習過程很慢。於是就出現了隨機梯度下降(stochastic gradient descent)演算法,是對於梯度下降的一個近似。在這個演算法中,每次學習不再針對所有的訓練集,而是從訓練集中隨機選擇一部分來計算C(w,b),下一次學習再從剩下的訓練集中隨機選擇一部分來計算,直到把整個訓練集用光。然後再不斷重複這一過程。
深度學習
深度神經網路(具有多個hidden layer)比淺層神經網路有更多結構上的優勢,它有能力從多個層次上進行抽象。
上圖表達了在一個基於深度學習的影像識別過程中,逐層抽象的過程:
- 最下面的視覺輸入層,接受圖片的各個畫素。
- 第一層hidden layer,通過比較相鄰元素的不同亮度,識別出影像的邊界。
- 第二層hidden layer,將邊界組合,識別出影像的角和輪廓。
- 第三層hidden layer,進一步抽象,將角和輪廓進行組合,識別出物體的組成部分。
- 最終,輸出層識別出具體的物體(是汽車、人,還是動物)。
從上個世紀八九十年代開始,研究人員們不斷嘗試將隨機梯度下降演算法應用於深度神經網路的訓練,但卻碰到了梯度消失(vanishing gradient)或梯度爆發(exploding gradient)的問題,導致學習過程異常緩慢,深度神經網路基本不可用。
然而,從2006年開始,人們開始使用一些新的技術來訓練深度網路,不斷取得了突破。這些技術包括但不限於:
- 採用卷積網路(convolutional networks);
- Regularization (dropout);
- Rectified linear units;
- 利用GPU獲得更強的計算能力;
- 使用更好的cost function;
- ……
限於篇幅原因,我們有機會下次再討論這些技術細節。
深度學習的優缺點
根據本文前面的介紹,深度學習的優點顯而易見:這是一種全新的程式設計方式,它不需要我們直接為要解決的問題設計演算法和程式設計,而是針對訓練過程程式設計。網路在訓練過程中就能自己學習到解決問題的正確方法,這使得我們可以用簡單的演算法來解決複雜的問題,而且在很多領域勝過了傳統方法。而訓練資料在這個過程發揮了更重要的作用:簡單的演算法加上覆雜的資料,可能遠勝於複雜的演算法加上簡單的資料。
但這項技術的一些缺點我們也不得不警惕:
- 深度網路往往包含大量的引數,這從哲學原則上不符合奧卡姆剃刀原則。通常人們要在調整這些引數上面花費巨大的精力;
- 訓練深度網路需要大量的計算力和計算時間;
- 過擬合(Overfitting)問題始終伴隨著神經網路的訓練過程,學習過慢的問題始終困擾著人們;
- 我們很難理解神經網路的工作方式,這容易讓人們產生一種失控的恐懼,同時也對這項技術在一些重要場合的進一步應用製造了障礙。
記得前一段時間在朋友圈流傳甚廣BetaCat的故事,講的就是一個人工智慧程式,通過自我學習,最終逐漸統治世界的故事。
那麼,現在的人工智慧技術的發展,會導致這種情況發生嗎?會導致強人工智慧的出現嗎?
恐怕還不太可能。個人感覺,大概有兩個重要因素:
- 第一,現在的人工智慧,它的自我學習還是限定在人們指定的方式,只能學習解決特定的問題,仍然不是通用的智慧。
- 第二,現在對於人工智慧的訓練過程,需要人們為其輸入規整化的訓練資料,系統的輸入輸出仍然對於資料的格式要求很嚴格。這也意味著,即使把人工智慧程式連到網上,它也不能像BetaCat那樣對於網際網路上海量的非結構化資料進行學習。
但是,本著實用的角度,這仍然是一種非常吸引人,而且很有前景的技術。
前段時間,朋友圈裡流傳著另外一個故事:一個日本小夥(一位工程師)利用深度學習技術,嘗試為他母親的農場設計了分選黃瓜的機器,大大減輕了他母親在農忙時節的工作量。
那麼,同樣作為工程師的你,是否也想利用平生所學,為媽媽做一點事呢?
(完)
注:本文圖片素材來源[3][5]。
參考文獻:
- [1] Frank Rosenblatt, http://books.google.ca/books/about/Principles_of_neurodynamics.html?id=7FhRAAAAMAAJ
- [2] Gate Universality. http://www.allaboutcircuits.com/textbook/digital/chpt-3/gate-universality/
- [3] Michael A. Nielsen, “Neural Networks and Deep Learning”, Determination Press, 2015. http://neuralnetworksanddeeplearning.com/
- [4] MNIST data set. http://yann.lecun.com/exdb/mnist/
- [5] Ian Goodfellow, Yoshua Bengio, Aaron Courville, “Deep Learning”, http://www.deeplearningbook.org/