數學 · 神經網路(二)· BP(反向傳播)

這一章要講的就是可能最讓我們頭疼的 BP(反向傳播)演算法了。事實上,如果不是要做理論研究而只是想快速應用神經網路來幹活的話,了解如何使用 tensorflow 等幫你處理梯度的成熟的框架可能會比了解演算法細節要更好一些。但即使如此,了解神經網路背後的原理總是不虧的,在某種意義上它也能告訴我們應該選擇怎樣的神經網路結構來進行具體的訓練

要想知道 BP 演算法的推導,我們需要且僅需要知道兩點知識:求導及其鏈式法則;如果想知道原理的話,還需要知道梯度相關的一些理論。同時我們可以從名字上知道,BP 演算法和前向傳導演算法的「方向」剛好相反:前向傳導是由後往前一路傳導,反向傳播則是由前往後一路傳播

先從直觀上理解一下 BP 的原理。總體上來說,BP 的目的是利用梯度來更新結構中的變數以使得損失函數最小化。這裡面就涉及兩個問題:

  • 如何獲得梯度?
  • 如何更新?

這一章我們會講第一個問題如何解決、並說一種第二個問題的解決方案,對第二個問題的詳細討論會放在 Optimizers 章節中

在講如何獲得梯度之前,我們先說說梯度是個什麼東西。雖然梯度可以牽扯出許多理論性的東西,不過對於 BP 演算法而言,我們完全可以先把梯度直觀地理解為:

  • 梯度是函數f在某點x_0上升最快的方向

換言之,當自變數x沿著梯度方向走時,函數的增長最快

其數學定義則是

它是向量函數 f 對 n 個變數的偏導組成的向量(順帶一提,我個人的習慣是在推導向量函數的梯度時,先把它分拆成單個的函數進行普通函數的求偏導計算、最後再把它們合成梯度)

在開始進行 BP 的推導前,我們還需要知道這麼兩件事:

  • BP 演算法的輸入是真實的類別向量Y
  • 我們的目標是讓模型的輸出儘可能擬合Y。為此我們會定義:
    • 預測函數f(X),它是一個向量函數、會根據輸入矩陣X輸出預測向量Y_{pred}
    • 損失函數C^{*}(X) = C(Y, Y_{pred}) = C(Y, f(X)),它是一個標量函數、其函數值能反映Y_{pred}Y的差異;差異越大、C^{*}(X) = C(Y, Y_{pred})的值越大

接下來我們就可以開始進行 BP 的推導了。如前所述,我們會把求解梯度的過程化為若干個求解偏導數的問題,為此我們需要將我們的神經網路進行拆分:

其中:

代表著第 k 層第 j 個神經元的接收的輸入;

代表著對應的激活值

(話說這個人圖畫得真的好醜)(捂臉

接下來先來看 BP 的思想、再來看具體的步驟

正如前面提到的,BP 是在前向傳導之後進行的、從後往前傳播的演算法,所以我們需要時刻記住這麼一個要求:每個 Layer 的梯度除了利用它自身的數據外、僅會利用到:

  • 上一層傳過來的激活值和下一層傳回來的梯度

  • 該層與下一層之間的線性變換矩陣w

從而我們需要定義一個僅由當前層數據和下一層傳回的梯度決定局部梯度。先給定義:

我們接下來證明它符合要求。由鏈式法則可知:

這就是我們想要的梯度。可以看到只要局部梯度符合要求、則梯度也符合要求,從而問題化為了如何求局部梯度delta _{j}^{(k)}。這裡會有兩種情況(不妨設我們的神經網路一共有 n 層)

  • 當前 Layer 是 CostLayer、也就是說最後一層。此時的「下一層」就是Y,從而:

    相當長的一個式子,裡面涉及的定義也挺多,不過其本質只是鏈式法則而已 ( σ"ω")σ
  • 當前 Layer 不是最後一層,此時由:

    再由鏈式法則可知(註:該層每個神經元對下一層所有神經元都會有影響):

    其中

    一項就是下一層傳播回來的局部梯度,且:

    從而可見,局部梯度delta _{j}^{(k)}確實符合要求

這些就是求梯度的推導了。雖然有點多,但一步一步看下來的話、相信聰明的觀眾老爺們理解起來不難 ( σ"ω")σ(然而碼公式快把我碼死了 冷漠)

在得到梯度後該怎麼做呢?如我提到過的,梯度當自變數x沿著梯度方向走時,函數的增長最快。現在我們的目的是減少我們的損失函數C*,所以就讓我們的自變數w_{ij}^{(k)}梯度方向相反的方向走就行了:

其中eta叫做學習速率,直觀上來說其大小反映的是w_{ij}^{(k)}往梯度反方向走的距離

(感謝評論區 @熊亮 的建議,下補上 BP 演算法的矩陣形式)

事實上,BP 演算法的優點之一正在於它能利用矩陣運算這個被高度優化的計算過程。由於矩陣運算表達式的推導只是把上面的東西合起來,所以這裡就只給出最終結果。為簡潔,我們只考慮中間 Layer 的情況,CostLayer 的情況是幾乎同理的:

其中	imes代表矩陣乘法,v^{(k-1)}{w^{(k+1)}{代表矩陣的轉置,cdot代表 element wise 操作。相當簡潔不是嗎?如果所用的編程語言直接支持矩陣操作的話,BP 演算法就可以用一行實現 ( σ"ω")σ

以上就是 BP 演算法的全過程。由於 BP 演算法確實有些繁複、所以我上面的過程可能會有疏漏,觀眾老爺們如果有什麼意見的話還請多多指教 ( σ"ω")σ

此外值得一提的是,很多常用的激活函數的導函數使用函數值來定義會比使用自變數來定義要更好(所謂更好是指形式上更簡單、從而計算開銷會更小),具體細節可以參見這篇文章中間偏後的部分

下一章我們會介紹一些常用的損失函數,這些損失函數不僅在神經網路中有應用、在其它的一些機器學習演算法中也會用到

希望觀眾老爺們能夠喜歡~

(猛戳我進入下一章!( σ"ω")σ )


推薦閱讀:

Python · 神經網路(三*)· 網路
【啄米日常】3:一個不負責任的Keras介紹(下)
CTR預估[四]: Algorithm-LR Bias和Q分布
深度學習(Deep Learning)基礎概念7:搭建多層神經網路的python實現

TAG:数学 | 机器学习 | 神经网络 |