入門深度學習,理解神經網路、反向傳播演算法是第一關
- 2019 年 10 月 28 日
- 筆記
1.學習或者回憶一些數學知識
因為電腦能做的就只是計算,所以人工智慧更多地來說還是數學問題[1]。我們的目標是訓練出一個模型,用這個模型去進行一系列的預測。於是,我們將訓練過程涉及的過程抽象成數學函數:首先,需要定義一個網路結構,相當於定義一種線性非線性函數;接著,設定一個優化目標,也就是定義一種損失函數(loss function)。
而訓練的過程,就是求解最優解及次優解的過程。在這個過程中,我們需要掌握基本的概率統計、高等數學、線性代數等知識,如果學過就最好,沒學過也沒關係,僅僅知道原理和過程即可,有興趣的讀者可以涉獵一些推導證明。
2.掌握經典機器學習理論與基本演算法
這些基本演算法包括支援向量機、邏輯回歸、決策樹、樸素貝葉斯分類器、隨機森林、聚類演算法、協同過濾、關聯性分析、人工神經網路和BP演算法、PCA、過擬合與正則化等。
在本書「實戰篇」的第8章到第13章的例子中也有貫穿這些演算法知識,保證讀者可以用它寫出一個小的TensorFlow程式。
3.掌握一種編程工具(語言)
Python語言是一種解釋型、面向對象、動態數據類型的高級程式設計語言。Python是很多新入門的程式設計師的入門程式語言,也是很多老程式設計師後來必須掌握的程式語言。我們需要重點掌握使用線性代數庫和矩陣的操作,尤其是Numpy、Pandas第三方庫,也要多試試機器學習的庫,如sklearn,做一些SVM及邏輯回歸的練習。這對直接上手寫TensorFlow程式大有裨益。
有些工業及學術領域的讀者還可能擅長MATLAB或R,其實現演算法的思想和Python也很類似。
同時考慮到許多讀者是使用C++、Java、Go語言的,TensorFlow還提供了和Python「平行語料庫」的介面。雖然本書是主要是基於Python講解的,對於其他語言的原理和應用API也都非常類似,讀者把基礎掌握後,只需要花很短的時間就能使用自己擅長的語言開發。另外對於Java語言的同學,本書第18章會講解TensorFlowOnSpark,第19章會講到TensorFlow的移動端開發。
4.研讀經典論文,關注最新動態和研究成果
一些經典論文是必讀的。例如,要做手寫數字識別,若採用LeNet,要先閱讀一下LeNet的學術論文;要做物體目標檢測的訓練,若選定MSCNN框架,可以先讀MSCNN相關的論文。那麼,論文從哪裡找呢?那麼多論文應該讀哪篇呢?
下面以GoogleNet的TensorFlow實現為例。在GitHub[3]上,一般在開頭的描述中就會說明這個模型所依據的論文,如圖所示。
順著這篇論文閱讀,可以大致了解這個網路的實現原理,對迅速上手應用有很大的作用。同時,我在第6章也會對LeNet、AlexNet、ResNet這幾個常見的網路進行講解,幫助讀者舉一反三。
很多做模式識別的工作者之所以厲害,是因為他們有過很多、很深的論文積累,對模型的設計有很獨到的見解,而他們可能甚至一行程式碼也不會寫,而工程(寫程式碼)能力在工作中很容易訓練。許多工程方向的軟體工程師,工作模式常常在實現業務邏輯和設計架構系統上,編碼能力很強,但卻缺少論文積累。同時具有這兩種能力的人,正是矽谷一些企業目前青睞的人才。
讀者平時還可以閱讀一些部落格、筆記,以及微信公眾號、微博新媒體資訊等,往往一些很流行的新訓練方法和模型會很快在這些媒體上發酵,其訓練神經網路採用的一些方法可能有很大的啟發性。
5.自己動手訓練神經網路
接著,就是要選擇一個開源的深度學習框架。選擇框架時主要考慮哪種框架用的人多。人氣旺後,遇到問題很容易找到答案;GitHub上關於這個框架的項目和演示會非常多;相關的論文也會層出不窮;在各個QQ群和微信群的活躍度會高;雜誌、公眾號、微博關注的人也會很多;行業交流和技術峰會討論的話題也多;也能享受到中國外研究資訊成果的同步。
目前這個階段,TensorFlow因為背靠Google公司這座靠山,再加上擁有龐大的開發者群體,而且採用了稱為「可執行的偽程式碼」的Python語言,更新和發版速度著實非常快。目前TensorFlow已經升級到1.0版,在性能方面也有大幅度提高,而且新出現的Debugger、Serving、XLA特性也是其他框架所不及的。此外,一些外圍的第三方庫(如Keras、TFLearn)也基於它實現了很多成果,並且Keras還得到TensorFlow官方的支援。TensorFlow支援的上層語言也在逐漸擴大,對於不同工程背景的人轉入的門檻正在降低。
在GitHub[4]上有一個關於各種框架的比較,從建模能力、介面、模型部署、性能、架構、生態系統、跨平台等7個方面進行比較,TensorFlow也很占綜合優勢。截至2017年1月,TensorFlow的star數已經超過了其他所有框架的總和,如圖1-8所示。
因此,從目前來看,投身TensorFlow是一個非常好的選擇,掌握TensorFlow在找工作時是一個非常大的加分項。
接下來就是找一個深度神經網路,目前的研究方向主要集中在視覺和語音兩個領域。初學者最好從電腦視覺入手,因為它不像語音等領域需要那麼多的基礎知識,結果也比較直觀。例如,用各種網路模型來訓練手寫數字(MNIST)及影像分類(CIFAR)的數據集。
6.深入感興趣或者工作相關領域
人工智慧目前的應用領域很多,主要是電腦視覺和自然語言處理,以及各種預測等。對於電腦視覺,可以做影像分類、目標檢測、影片中的目標檢測等;對於自然語言處理,可以做語音識別、語音合成、對話系統、機器翻譯、文章摘要、情感分析等,還可以結合影像、影片和語音,一起發揮價值。
更可以深入某一個行業領域。例如,深入醫學行業領域,做醫學影像的識別;深入淘寶的穿衣領域,做衣服搭配或衣服款型的識別;深入保險業、通訊業的客服領域,做對話機器人的智慧問答系統;深入智慧家居領域,做人機的自然語言交互;等等。
7.在工作中遇到問題,重複前六步
在訓練中,準確率、壞案例(bad case)、識別速度等都是可能遇到的瓶頸。訓練好的模型也不是一成不變的,需要不斷優化,也需要結合具體行業領域和業務進行創新,這時候就要結合最新的科研成果,調整模型,更改模型參數,一步步更好地貼近業務需求。 這裡,一些人擔心人工智慧超越人類還會產生哲學和倫理問題。我認為做這種討論還為時尚早,嚴謹的數據基礎是要突破的主要方向。
深度學習所需要的數學基礎並沒有想像中的那麼難,只需要知道導數和相關的函數概念即可。假如你高等數學也沒學過,很好,這篇文章其實是想讓文科生也能看懂,只需要學過初中數學。
不必有畏難的情緒,我比較推崇李書福的精神,在一次電視採訪中,李書福說:誰說中國人不能造汽車?造汽車有啥難的,不就是四個輪子加兩排沙發嘛。當然,他這個結論有失偏頗,不過精神可嘉。
導數是什麼?
無非就是變化率,比如:王小二今年賣了 100 頭豬,去年賣了 90 頭,前年賣了 80 頭。。。變化率或者增長率是什麼?每年增長 10 頭豬,多簡單。
這裡需要注意有個時間變數—年。王小二賣豬的增長率是 10頭/年,也就是說,導數是 10。
函數 y = f(x) = 10x + 30,這裡我們假設王小二第一年賣了 30 頭,以後每年增長 10 頭,x代表時間(年),y代表豬的頭數。
當然,這是增長率固定的情形,而現實生活中,很多時候,變化量也不是固定的,也就是說增長率不是恆定的。
比如,函數可能是這樣: y = f(x) = 5x² + 30,這裡 x 和 y 依然代表的是時間和頭數,不過增長率變了,怎麼算這個增長率,我們回頭再講。或者你乾脆記住幾個求導的公式也可以。
深度學習還有一個重要的數學概念:偏導數
偏導數的偏怎麼理解?偏頭疼的偏,還是我不讓你導,你偏要導?
都不是,我們還以王小二賣豬為例,剛才我們講到,x 變數是時間(年),可是賣出去的豬,不光跟時間有關啊,隨著業務的增長,王小二不僅擴大了養豬場,還雇了很多員工一起養豬。
所以方程式又變了:y = f(x) = 5x₁² + 8x₂ + 35x₃ + 30
這裡 x₂ 代表面積,x₃ 代表員工數,當然 x₁ 還是時間。
偏導數是什麼
偏導數無非就是多個變數的時候,針對某個變數的變化率。在上面的公式里,如果針對 x₃ 求偏導數,也就是說,員工對於豬的增長率貢獻有多大。
或者說,隨著(每個)員工的增長,豬增加了多少,這裡等於 35—每增加一個員工,就多賣出去 35 頭豬。
計算偏導數的時候,其他變數都可以看成常量,這點很重要,常量的變化率為 0,所以導數為 0,所以就剩對 35x₃ 求導數,等於 35。對於 x₂ 求偏導,也是類似的。
求偏導,我們用一個符號表示:比如 y / x₃ 就表示 y 對 x₃ 求偏導。
深度學習是採用神經網路,用於解決線性不可分的問題。
什麼是學習係數?
另一個是學習係數,為什麼叫學習係數?
剛才我們上面講到∆增量,到底每次增加多少合適?是不是等同於偏導數(變化率)?
經驗告訴我們,需要乘以一個百分比,這個就是學習係數,而且,隨著訓練的深入,這個係數是可以變的。
當然,還有一些很重要的基本知識,比如 SGD(隨機梯度下降),mini batch 和 epoch(用於訓練集的選擇)。
我們已經看到不少人提到如果想實現強AI,就必須讓機器學會觀察並總結規律的言論。具體地說,要讓機器觀察什麼是圓的,什麼是方的,區分各種顏色和形狀,然後根據這些特徵對某種事物進行分類或預測。其實這就是回歸問題。 如何解決回歸問題?我們用眼睛看到某樣東西,可以一下子看出它的一些基本特徵。可是電腦呢?它看到的只是一堆數字而已,因此要讓機器從事物的特徵中找到規律,其實是一個如何在數字中找規律的問題。

例:假如有一串數字,已知前六個是1、3、5、7,9,11,請問第七個是幾? 你一眼能看出來,是13。對,這串數字之間有明顯的數學規律,都是奇數,而且是按順序排列的。
那麼這個呢?前六個是0.14、0.57、1.29、2.29、3.57、5.14,請問第七個是幾? 這個就不那麼容易看出來了吧!我們把這幾個數字在坐標軸上標識一下,可以看到如下圖形:

用曲線連接這幾個點,延著曲線的走勢,可以推算出第七個數字——7。 由此可見,回歸問題其實是個曲線擬合(Curve Fitting)問題。那麼究竟該如何擬合?機器不可能像你一樣,憑感覺隨手畫一下就擬合了,它必須要通過某種演算法才行。
假設有一堆按一定規律分布的樣本點,下面我以擬合直線為例,說說這種演算法的原理。 其實很簡單,先隨意畫一條直線,然後不斷旋轉它。每轉一下,就分別計算一下每個樣本點和直線上對應點的距離(誤差),求出所有點的誤差之和。這樣不斷旋轉,當誤差之和達到最小時,停止旋轉。說得再複雜點,在旋轉的過程中,還要不斷平移這條直線,這樣不斷調整,直到誤差最小時為止。這種方法就是著名的梯度下降法(Gradient Descent)。為什麼是梯度下降呢?在旋轉的過程中,當誤差越來越小時,旋轉或移動的量也跟著逐漸變小,當誤差小於某個很小的數,例如0.0001時,我們就可以收工(收斂, Converge)了。啰嗦一句,如果隨便轉,轉過頭了再往迴轉,那就不是梯度下降法。

我們知道,直線的公式是y=kx+b,k代表斜率,b代表偏移值(y軸上的截距)。也就是說,k可以控制直線的旋轉角度,b可以控制直線的移動。強調一下,梯度下降法的實質是不斷的修改k、b這兩個參數值,使最終的誤差達到最小。
求誤差時使用 累加(直線點-樣本點)^2,這樣比直接求差距 累加(直線點-樣本點) 的效果要好。這種利用最小化誤差的平方和來解決回歸問題的方法叫最小二乘法(Least Square Method)。

問題到此使似乎就已經解決了,可是我們需要一種適應於各種曲線擬合的方法,所以還需要繼續深入研究。
我們根據擬合直線不斷旋轉的角度(斜率)和擬合的誤差畫一條函數曲線,如圖:

從圖中可以看出,誤差的函數曲線是個二次曲線,凸函數(下凸, Convex),像個碗的形狀,最小值位於碗的最下端。如果在曲線的最底端畫一條切線,那麼這條切線一定是水平的,在圖中可以把橫坐標軸看成是這條切線。如果能求出曲線上每個點的切線,就能得到切線位於水平狀態時,即切線斜率等於0時的坐標值,這個坐標值就是我們要求的誤差最小值和最終的擬合直線的最終斜率。
這樣,梯度下降的問題集中到了切線的旋轉上。切線旋轉至水平時,切線斜率=0,誤差降至最小值。

切線每次旋轉的幅度叫做學習率(Learning Rate),加大學習率會加快擬合速度,但是如果調得太大會導致切線旋轉過度而無法收斂。 注意:對於凹凸不平的誤差函數曲線,梯度下降時有可能陷入局部最優解。下圖的曲線中有兩個坑,切線有可能在第一個坑的最底部趨於水平。

微分就是專門求曲線切線的工具,求出的切線斜率叫做導數(Derivative),用dy/dx或f'(x)表示。擴展到多變數的應用,如果要同時求多個曲線的切線,那麼其中某個切線的斜率就叫偏導數(Partial Derivative),用∂y/∂x表示,∂讀「偏(partial)」。由於實際應用中,我們一般都是對多變數進行處理,我在後面提到的導數也都是指偏導數。
以上是線性回歸(Linear Regression)的基本內容,以此方法為基礎,把直線公式改為曲線公式,還可以擴展出二次回歸、三次回歸、多項式回歸等多種曲線回歸。

在多數情況下,曲線回歸會比直線回歸更精確,但它也增加了擬合的複雜程度。 直線方程y=kx+b改為二次曲線方程y=ax^2+bx+c時,參數(Parameter)由2個(分別是k、b)變為3個(分別是a、b、c),特徵(Feature)由1個(x)變為2個(x^2和x)。三次曲線和複雜的多項式回歸會增加更多的參數和特徵。

前面講的是總結一串數字的規律,現實生活中我們往往要根據多個特徵(多串數字)來分析一件事情,每個原始特徵我們都看作是一個維度(Dimension)。例如一個學生的學習成績好壞要根據語文、數學、英語等多門課程的分數來綜合判斷,這裡每門課程都是一個維度。當使用二次曲線和多變數(多維)擬合的情況下,特徵的數量會劇增,特徵數=維度^2/2 這個公式可以大概計算出特徵增加的情況,例如一個100維的數據,二次多項式擬合後,特徵會增加到100*100/2=5000個。 下面是一張50*50像素的灰度圖片,如果用二次多項式擬合的話,它有多少個特徵呢?——大約有3百萬!

它的維度是50*50=2500,特徵數=2500*2500/2=3,125,000。如果是彩色圖片,維度會增加到原來的3倍,那麼特徵數將增加到接近3千萬了!

這麼小的一張圖片,就有這麼巨大的特徵量,可以想像一下我們的數位相機拍下來的照片會有多大的特徵量!而我們要做的是從十萬乃至億萬張這樣的圖片中找規律,這可能嗎? 很顯然,前面的那些回歸方法已經不夠用了,我們急需找到一種數學模型,能夠在此基礎上不斷減少特徵,降低維度。 於是,「人工神經網路(ANN, Artificial Neural Network)」就在這樣苛刻的條件下粉墨登場了,神經科學的研究成果為機器學習領域開闢了廣闊的道路。
有一種假說:「智慧來源於單一的演算法(One Learning Algorithm)」。如果這一假說成立,那麼利用單一的演算法(神經網路)處理世界上千變萬化的問題就成為可能。我們不必對萬事萬物進行編程,只需採用以不變應萬變的策略即可。有越來越多的證據證明這種假說,例如人類大腦發育初期,每一部分的職責分工是不確定的,也就是說,人腦中負責處理聲音的部分其實也可以處理視覺影像。
神經元(Neuron),或者說一個腦細胞的生理結構:

下面是單個神經元的數學模型,可以看出它是生理結構的簡化版,模仿的還挺像:

解釋一下:+1代表偏移值(偏置項, Bias Units);X1,X2,X2代表初始特徵;w0,w1,w2,w3代表權重(Weight),即參數,是特徵的縮放倍數;特徵經過縮放和偏移後全部累加起來,此後還要經過一次激活運算然後再輸出。激活函數有很多種,後面將會詳細說明。 舉例說明:

X1*w1+X2*w2+…+Xn*wn這種計算方法稱為加權求和(Weighted Sum)法,此方法在線性代數里極為常用。加權求和的標準數學符號是,

不過為了簡化,我在教程里使用女巫布萊爾的符號

表示,剛好是一個加號和一個乘號的組合。
這個數學模型有什麼意義呢?下面我對照前面那個 y=kx+b 直線擬合的例子來說明一下。

這時我們把激活函數改為Purelin(45度直線),Purelin就是y=x,代表保持原來的值不變。 這樣輸出值就成了 Y直線點 = b + X直線點*k,即y=kx+b。看到了吧,只是換了個馬甲而已,還認的出來嗎?下一步,對於每個點都進行這種運算,利用Y直線點和Y樣本點計算誤差,把誤差累加起來,不斷地更新b、k的值,由此不斷地移動和旋轉直線,直到誤差變得很小時停住(收斂)。這個過程完全就是前面講過的梯度下降的線性回歸。 一般直線擬合的精確度要比曲線差很多,那麼使用神經網路我們將如何使用曲線擬合?答案是使用非線性的激活函數即可,最常見的激活函數是Sigmoid(S形曲線),Sigmoid有時也稱為邏輯回歸(Logistic Regression),簡稱logsig。logsig曲線的公式如下:

還有一種S形曲線也很常見到,叫雙曲正切函數(tanh),或稱tansig,可以替代logsig。

下面是它們的函數圖形,從圖中可以看出logsig的數值範圍是0~1,而tansig的數值範圍是-1~1。

下圖是幾種比較常見的網路形式:

– 左邊藍色的圓圈叫「輸入層」,中間橙色的不管有多少層都叫「隱藏層」,右邊綠色的是「輸出層」。 – 每個圓圈,都代表一個神經元,也叫節點(Node)。 – 輸出層可以有多個節點,多節點輸出常常用於分類問題。 – 理論證明,任何多層網路可以用三層網路近似地表示。 – 一般憑經驗來確定隱藏層到底應該有多少個節點,在測試的過程中也可以不斷調整節點數以取得最佳效果。 計算方法:

– 雖然圖中未標識,但必須注意每一個箭頭指向的連線上,都要有一個權重(縮放)值。 – 輸入層的每個節點,都要與的隱藏層每個節點做點對點的計算,計算的方法是加權求和+激活,前面已經介紹過了。(圖中的紅色箭頭指示出某個節點的運算關係) – 利用隱藏層計算出的每個值,再用相同的方法,和輸出層進行計算。 – 隱藏層用都是用Sigmoid作激活函數,而輸出層用的是Purelin。這是因為Purelin可以保持之前任意範圍的數值縮放,便於和樣本值作比較,而Sigmoid的數值範圍只能在0~1之間。 – 起初輸入層的數值通過網路計算分別傳播到隱藏層,再以相同的方式傳播到輸出層,最終的輸出值和樣本值作比較,計算出誤差,這個過程叫前向傳播(Forward Propagation)。
前面講過,使用梯度下降的方法,要不斷的修改k、b兩個參數值,使最終的誤差達到最小。神經網路可不只k、b兩個參數,事實上,網路的每條連接線上都有一個權重參數,如何有效的修改這些參數,使誤差最小化,成為一個很棘手的問題。從人工神經網路誕生的60年代,人們就一直在不斷嘗試各種方法來解決這個問題。直到80年代,誤差反向傳播演算法(BP演算法)的提出,才提供了真正有效的解決方案,使神經網路的研究絕處逢生。

BP演算法是一種計算偏導數的有效方法,它的基本原理是:利用前向傳播最後輸出的結果來計算誤差的偏導數,再用這個偏導數和前面的隱藏層進行加權求和,如此一層一層的向後傳下去,直到輸入層(不計算輸入層),最後利用每個節點求出的偏導數來更新權重。

為了便於理解,後面我一律用「殘差(error term)」這個詞來表示誤差的偏導數。
輸出層→隱藏層:殘差 = -(輸出值-樣本值) * 激活函數的導數 隱藏層→隱藏層:殘差 = (右層每個節點的殘差加權求和)* 激活函數的導數 如果輸出層用Purelin作激活函數,Purelin的導數是1,輸出層→隱藏層:殘差 = -(輸出值-樣本值) 如果用Sigmoid(logsig)作激活函數,那麼:Sigmoid導數 = Sigmoid*(1-Sigmoid) 輸出層→隱藏層:殘差 = -(Sigmoid輸出值-樣本值) * Sigmoid*(1-Sigmoid) = -(輸出值-樣本值)*輸出值*(1-輸出值) 隱藏層→隱藏層:殘差 = (右層每個節點的殘差加權求和)* 當前節點的Sigmoid*(1-當前節點的Sigmoid)

如果用tansig作激活函數,那麼:tansig導數 = 1 – tansig^2 殘差全部計算好後,就可以更新權重了:
輸入層:權重增加 = 輸入值 * 右層對應節點的殘差 * 學習率
隱藏層:權重增加 = 當前節點的Sigmoid * 右層對應節點的殘差 * 學習率 偏移值的權重增加 = 右層對應節點的殘差 * 學習率 學習率前面介紹過,學習率是一個預先設置好的參數,用於控制每次更新的幅度。 此後,對全部數據都反覆進行這樣的計算,直到輸出的誤差達到一個很小的值為止。
以上介紹的是目前最常見的神經網路類型,稱為前饋神經網路(FeedForward Neural Network),由於它一般是要向後傳遞誤差的,所以也叫BP神經網路(Back Propagation Neural Network)。
BP神經網路的特點和局限: – BP神經網路可以用作分類、聚類、預測等。需要有一定量的歷史數據,通過歷史數據的訓練,網路可以學習到數據中隱含的知識。在你的問題中,首先要找到某些問題的一些特徵,以及對應的評價數據,用這些數據來訓練神經網路。 – BP神經網路主要是在實踐的基礎上逐步完善起來的系統,並不完全是建立在仿生學上的。從這個角度講,實用性 > 生理相似性。 – BP神經網路中的某些演算法,例如如何選擇初始值、如何確定隱藏層的節點個數、使用何種激活函數等問題,並沒有確鑿的理論依據,只有一些根據實踐經驗總結出的有效方法或經驗公式。 – BP神經網路雖然是一種非常有效的計算方法,但它也以計算超複雜、計算速度超慢、容易陷入局部最優解等多項弱點著稱,因此人們提出了大量有效的改進方案,一些新的神經網路形式也層出不窮。
這裡介紹的是計算完一條記錄,就馬上更新權重,以後每計算完一條都即時更新權重。實際上批量更新的效果會更好,方法是在不更新權重的情況下,把記錄集的每條記錄都算過一遍,把要更新的增值全部累加起來求平均值,然後利用這個平均值來更新一次權重,然後利用更新後的權重進行下一輪的計算,這種方法叫批量梯度下降(Batch Gradient Descent)。
Andrew Ng的《機器學習》公開課: https://class.coursera.org/ml Coursera公開課筆記中文版(神經網路的表示): http://52opencourse.com/139/coursera公開課筆記-斯坦福大學機器學習第八課-神經網路的表示-neural-networks-representation Coursera公開課影片(神經網路的學習): http://52opencourse.com/289/coursera公開課影片-斯坦福大學機器學習第九課-神經網路的學習-neural-networks-learning 斯坦福深度學習中文版: http://deeplearning.stanford.edu/wiki/index.php/UFLDL教程