BP神經網絡算法推導

從知乎上看到的一篇超好的文章,貨很乾,含着淚也要讀完!

一 科普:

生物上的神經元就是接收四面八方的刺激(輸入),然後做出反應(輸出),給它一點就燦爛。仿生嘛,於是喜歡放飛自我的 某些人 就提出了人工神經網絡。一切的基礎-->人工神經單元,看圖:

 

▌二. 通往沙漠的入口: 神經元是什麼,有什麼用:

開始前,需要搞清楚一個很重要的問題:人工神經網絡裏的神經元是什麼,有什麼用。只有弄清楚這個問題,你才知道你在哪裏,在做什麼,要往哪裏去。

首先,回顧一下神經元的結構,看下圖, 我們先忽略激活函數不管:

 

沒錯,開始曬公式了!我們的數據都是離散的,爲了看得更清楚點,所以換個表達方式,把離散的數據寫成向量。該不會忘了向量是啥吧?回頭致電問候一下當年的體育老師!

 

現在回答問題剛纔的問題:

一個神經元是什麼:參照式(3.6),從函數圖像角度看,這就是一根直線。一個神經元有什麼用:要說明用途就要給出一個應用場景:分類。一個神經元就是一條直線,相當於楚河漢界,可以把紅棋綠棋分隔開,此時它就是個分類器。所以,在線性場景下,單個神經元能達到分類的作用,它總能學習到一條合適的直線,將兩類元素區分出來。

先睹爲快,看效果圖,自己可以去玩:傳送門

http://t.cn/RBCoWof

 

對上面的圖簡單說明一下:

(x1,x2) 對於神經元的輸入都是 x, 而對我們而言,這數據就是意義上的點的座標,我們習慣寫成 (x,y)。

又要劃重點了:

 

我們需要對神經元的輸出做判定,那麼就需要有判定規則,通過判定規則後我們才能拿到我們想要的結果,這個規則就是:

假設,0代表紅點,1代表藍點(這些數據都是事先標定好的,在監督學習下,神經元會知道點是什麼顏色並以這個已知結果作爲標杆進行學習)當神經元輸出小於等於 0 時,最終結果輸出爲 0,這是個紅點當神經元輸出大於 1 時,最終結果輸出爲 1,這是個藍點

上面提到的規則讓我聞到了激活函數的味道!(這裏只是線性場景,雖然不合適,但是簡單起見,使用了單位階躍函數來描述激活函數的功能)當 x<=0 時,y = 0; 當 x > 0 時,y = 1

這是階躍函數的長相:

 

此時神經元的長相:

 

三. 茫茫大漠第一步: 激活函數是什麼,有什麼用

從上面的例子,其實已經說明了激活函數的作用;但是,我們通常面臨的問題,不是簡單的線性問題,不能用單位階躍函數作爲激活函數,原因是:

階躍函數在x=0時不連續,即不可導,在非0處導數爲0。用人話說就是它具備輸出限定在[0-1],但是它不具備絲滑的特性,這個特性很重要。並且在非0處導數爲0,也就是硬飽和,壓根兒就沒梯度可言,梯度也很重要,梯度意味着在神經元傳播間是有反應的,而不是“死”了的。

接下來說明下,激活函數所具備的特性有什麼,只挑重要的幾點特性講:

非線性: 即導數不是常數,不然就退化成直線。對於一些畫一條直線仍然無法分開的問題,非線性可以把直線掰彎,自從變彎以後,就能包羅萬象了。

幾乎處處可導:也就是具備“絲滑的特性”,不要應激過度,要做正常人。數學上,處處可導爲後面降到的後向傳播算法(BP算法)提供了核心條件

輸出範圍有限:一般是限定在[0,1],有限的輸出範圍使得神經元對於一些比較大的輸入也會比較穩定。

非飽和性:飽和就是指,當輸入比較大的時候,輸出幾乎沒變化了,那麼會導致梯度消失!什麼是梯度消失:就是你天天給女生送花,一開始妹紙還驚喜,到後來直接麻木沒反應了。梯度消失帶來的負面影響就是會限制了神經網絡表達能力,詞窮的感覺你有過麼。sigmod,tanh函數都是軟飽和的,階躍函數是硬飽和。軟是指輸入趨於無窮大的時候輸出無限接近上線,硬是指像階躍函數那樣,輸入非0輸出就已經始終都是上限值。數學表示我就懶得寫了,傳送門在此(https://www.cnblogs.com/rgvb178/p/6055213.html),裏面有寫到。如果激活函數是飽和的,帶來的缺陷就是系統迭代更新變慢,系統收斂就慢,當然這是可以有辦法彌補的,一種方法是使用交叉熵函數作爲損失函數,這裏不多說。ReLU是非飽和的,親測效果挺不錯,所以這貨最近挺火的。

單調性:即導數符號不變。導出要麼一直大於0,要麼一直小於0,不要上躥下跳。導數符號不變,讓神經網絡訓練容易收斂。

這裏只說我們用到的激活函數:

 

求一下它的導數把,因爲後面講bp算法會直接套用它:

先祭出大殺器,高中數學之複合函數求導法則:

 

它的導數圖像:

 

四. 沙漠中心的風暴:BP(Back Propagation)算法

1. 神經網絡的結構

經過上面的介紹,單個神經元不足以讓人心動,唯有組成網絡。神經網絡是一種分層結構,一般由輸入曾,隱藏層,輸出層組成。所以神經網絡至少有3層,隱藏層多於1,總層數大於3的就是我們所說的深度學習了。

輸入層:就是接收原始數據,然後往隱層送輸出層:神經網絡的決策輸出隱藏層:該層可以說是神經網絡的關鍵,相當於對數據做一次特徵提取。隱藏層的意義,是把前一層的向量變成新的向量。就是座標變換,說人話就是把數據做平移,旋轉,伸縮,扭曲,讓數據變得線性可分。可能這個不那麼好理解,舉個栗子:

下面的圖左側是原始數據,中間很多綠點,外圍是很多紅點,如果你是神經網絡,你會怎麼做呢?

一種做法:把左圖的平面看成一塊布,把它縫合成一個閉合的包包(相當於數據變換到了一個3維座標空間),然後把有綠色點的部分擼到頂部(伸縮和扭曲),然後外圍的紅色點自然在另一端了,要是姿勢還不夠帥,就挪挪位置(平移)。這時候乾脆利落的砍一刀,綠點紅點就徹底區分開了。

重要的東西再說一遍:神經網絡換着座標空間玩數據,根據需要,可降維,可升維,可大,可小,可圓可扁,就是這麼“無敵”

這個也可以自己去玩玩,直觀的感受一下:傳送門

https://cs.stanford.edu/people/karpathy/convnetjs//demo/classify2d.html

 

2.正反向傳播過程

看圖,這是一個典型的三層神經網絡結構,第一層是輸入層,第二層是隱藏層,第三層是輸出層。PS:不同的應用場景,神經網絡的結構要有針對性的設計,這裏僅僅是爲了推導算法和計算方便才採用這個簡單的結構

 

 

我們以戰士打靶,目標是訓練戰士能命中靶心成爲神槍手作爲場景:

那麼我們手裏有這樣一些數據:一堆槍擺放的位置(x,y),以及射擊結果,命中靶心和不命中靶心。

我們的目標是:訓練出一個神經網絡模型,輸入一個點的座標(射擊姿勢),它就告訴你這個點是什麼結果(是否命中)。

我們的方法是:訓練一個能根據誤差不斷自我調整的模型,訓練模型的步驟是:

正向傳播:把點的座標數據輸入神經網絡,然後開始一層一層的傳播下去,直到輸出層輸出結果。反向傳播(BP):就好比戰士去靶場打靶,槍的擺放位置(輸入),和靶心(期望的輸出)是已知。戰士(神經網絡)一開始的時候是這樣做的,隨便開一槍(w,b參數初始化稱隨機值),觀察結果(這時候相當於進行了一次正向傳播)。然後發現,偏離靶心左邊,應該往右點兒打。所以戰士開始根據偏離靶心的距離(誤差,也稱損失)調整了射擊方向往右一點(這時,完成了一次反向傳播)當完成了一次正反向傳播,也就完成了一次神經網絡的訓練迭代,反覆調整射擊角度(反覆迭代),誤差越來越小,戰士打得越來越準,神槍手模型也就誕生了。

3.BP算法推導和計算

參數初始化:

 

 

正向傳播:

2.隱層-->輸出層:

 

正向傳播結束,我們看看輸出層的輸出結果:[0.7987314002, 0.8374488853],但是我們希望它能輸出[0.01, 0.99],所以明顯的差太遠了,這個時候我們就需要利用反向傳播,更新權值w,然後重新計算輸出.

反向傳播:1.計算輸出誤差:

 

 

PS: 這裏我要說的是,用這個作爲誤差的計算,因爲它簡單,實際上用的時候效果不咋滴。如果激活函數是飽和的,帶來的缺陷就是系統迭代更新變慢,系統收斂就慢,當然這是可以有辦法彌補的,一種方法是使用交叉熵函數作爲損失函數

交叉熵做爲代價函數能達到上面說的優化系統收斂下歐工,是因爲它在計算誤差對輸入的梯度時,抵消掉了激活函數的導數項,從而避免了因爲激活函數的“飽和性”給系統帶來的負面影響。如果項瞭解更詳細的證明可以點 --> 傳送門(https://blog.csdn.net/lanchunhui/article/details/50086025)

對輸出的偏導數:

2.隱層-->輸出層的權值及偏置b的更新:

先放出鏈式求導法則

以更新w5舉例:

我們知道,權重w的大小能直接影響輸出,w不合適那麼會使得輸出誤差。要想直到某一個w值對誤差影響的程度,可以用誤差對該w的變化率來表達。如果w的一點點變動,就會導致誤差增大很多,說明這個w對誤差影響的程度就更大,也就是說,誤差對該w的變化率越高。而誤差對w的變化率就是誤差對w的偏導。

所以,看下圖,總誤差的大小首先受輸出層神經元O1的輸出影響,繼續反推,O1的輸出受它自己的輸入的影響,而它自己的輸入會受到w5的影響。這就是連鎖反應,從結果找根因。

 

 

那麼,根據鏈式法則則有:

現在挨個計算:

 

 

有個學習率的東西,學習率取個0.5。關於學習率,不能過高也不能過低。因爲訓練神經網絡系統的過程,就是通過不斷的迭代,找到讓系統輸出誤差最小的參數的過程。每一次迭代都經過反向傳播進行梯度下降,然而誤差空間不是一個滑梯,一降到底,常規情況下就像坑窪的山地。學習率太小,那就很容易陷入局部最優,就是你認爲的最低點並不是整個空間的最低點。如果學習率太高,那系統可能難以收斂,會在一個地方上串下跳,無法對準目標(目標是指誤差空間的最低點),可以看圖:

xy軸是權值w平面,z軸是輸出總誤差。整個誤差曲面可以看到兩個明顯的低點,顯然右邊最低,屬於全局最優。而左邊的是次低,從局部範圍看,屬於局部最優。而圖中,在給定初始點的情況下,標出的兩條抵達低點的路線,已經是很理想情況的梯度下降路徑。

 

 

 

3.輸入層-->隱層的權值及偏置b更新:

以更新w1爲例:仔細觀察,我們在求w5的更新,誤差反向傳遞路徑輸出層-->隱層,即out(O1)-->in(O1)-->w5,總誤差只有一根線能傳回來。但是求w1時,誤差反向傳遞路徑是隱藏層-->輸入層,但是隱藏層的神經元是有2根線的,所以總誤差沿着2個路徑回來,也就是說,計算偏導時,要分開來算。看圖:

 

那麼,現在開始算總誤差對w1的偏導:

 

4.結論:

我們通過親力親爲的計算,走過了正向傳播,也體會了反向傳播,完成了一次訓練(迭代)。隨着迭代加深,輸出層的誤差會越來越小,專業點說就是系統趨於收斂。來一張系統誤差隨迭代次數變化的圖來表明我剛纔說描述:

 

五. 沙漠的綠洲:代碼實現

1. 代碼代碼!

其實已經有很多機器學習的框架可以很簡單的實現神經網絡。但是我們的目標是:在看懂算法之後,我們是否能照着算法的整個過程,去實現一遍,可以加深對算法原理的理解,以及對算法實現思路的的理解。順便說打個call,numpy這個庫,你值得擁有!

代碼實現如下。代碼裏已經做了儘量囉嗦的註釋,關鍵實現的地方對標了公式的編號,如果看的不明白的地方多回來啃一下算法推導。對應代碼也傳到了github上。代碼能自己定義神經網絡的結構,支持深度網絡。代碼實現了對紅藍顏色的點做分類的模型訓練,通過3層網絡結構,改變隱藏層的神經元個數,通過圖形顯示隱藏層神經元數量對問題的解釋能力。代碼中還實現了不同激活函數。隱藏層可以根據需要換着激活函數玩,輸出層一般就用sigmoid,當然想換也隨你喜歡~

代碼後續整理好上傳

2. 曬圖曬圖!

關於誤差曲線(這裏只舉其中一個栗子):

通過看誤差曲線,可以從一定程度上判定網絡的效果,模型訓練是否能收斂,收斂程度如何,都可以從誤差曲線對梯度下降的過程能見一二。

 

3層網絡的結構下,隱藏層只有一層,看圖說明一下隱藏層神經元個數變化對神經網絡表達能力的影響:

當隱藏層只有1個神經元:就像文章剛開始說的,一個神經元,就是個線性分類器,表達能力就一條直線而已,見式(3.6)2個神經元:線開始有點彎曲了,但是這次結果一點都不明顯,尷尬。但從原理上神經網絡開始具備了非線性表達能力隨着隱藏層神經元個數不斷增加,神經網絡表達能力越來越強,分類的效果越來越好。當然也不是神經元越多越好,可以開始考慮深度網絡是不是效果更好一些。

六. 沒有結局

記住一點,bp神經網絡是其他各種神經網絡中最簡單的一種。只有學會了它,才能以此爲基礎展開對其他更復雜的神經網絡的學習。

雖然推導了並實現了算法,但是仍然是有很多疑問,這裏就作爲拋磚引玉吧:

神經網絡的結構,即幾層網絡,輸入輸出怎麼設計才最有效?

數學理論證明,三層的神經網絡就能夠以任意精度逼近任何非線性連續函數。那麼爲什麼還需要有深度網絡?

在不同應用場合下,激活函數怎麼選擇?

學習率怎麼怎麼選擇?

訓練次數設定多少訓練出的模型效果更好?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章