1. 程式人生 > >全連線神經網路的反向傳播演算法(BP)

全連線神經網路的反向傳播演算法(BP)

一、預熱篇

參考連結:http://colah.github.io/posts/2015-08-Backprop/

要理解的主要點:路徑上所有邊相乘,所有路徑相加

反向傳播演算法(Backpropagation)已經是神經網路模型進行學習的標配。但是有很多問題值得思考一下:

反向傳播演算法的作用是什麼? 神經網路模型的學習演算法一般是SGD。SGD需要用到損失函式C關於各個權重引數w_{jk},b_j的偏導數\frac{ \partial C }{ \partial w_{jk} } , \frac{ \partial C }{ \partial b_j}。一個模型的引數w,b是非常多的,故而需要反向傳播演算法快速計算\frac{ \partial C }{ \partial w_{jk} } , \frac{ \partial C }{ \partial b_j}。也就是說反向傳播演算法是一種計算偏導數的方法。

為什麼要提出反向傳播演算法? 在反向傳播演算法提出之前人們應該想到了使用SGD學習模型,也想到了一些辦法求解網路模型的偏導數,但這些演算法求解效率比較低,所以提出反向傳播演算法來更高效的計算偏導數。(那時的網路模型還比較淺只有2-3層,引數少。估計即便不適用反向傳播這種高效的演算法也能很好的學習。一旦有人想使用更深的網路自然會遇到這個偏導數無法高效計算的問題,提出反向傳播也就勢在必行了)

反向傳播怎麼樣實現高效計算偏導數的? 請先回顧一下當初我們學習微積分時是如何計算偏導數的? (鏈式法則,具體看下面)

1 用計算圖來解釋幾種求導方法:

1.1 計算圖

式子 e=(a+b)*(b+1) 可以用如下計算圖表達:

preview

令a=2,b=1則有:

preview

如何在計算圖上表達“求導”呢? 導數的含義是 因變數隨自變數的變化率,例如 \frac{\partial y }{\partial x} = 3 表示當x變化1個單位,y會變化3個單位。 微積分中已經學過:加法求導法則是 \frac{\partial}{\partial a}(a+b) = \frac{\partial a}{\partial a} + \frac{\partial b}{\partial a} = 1 乘法求導法則是 \frac{\partial}{\partial u}uv = u\frac{\partial v}{\partial u} + v\frac{\partial u}{\partial u} = v 。 我們在計算圖的邊上表示導數或偏導數:\frac{ \partial e }{ \partial c } , \frac{ \partial e }{ \partial d }, \frac{ \partial c }{ \partial a }, \frac{ \partial c }{ \partial b }, \frac{ \partial d }{ \partial b } 如下圖:

preview

那麼 \frac{ \partial e  }{ \partial b } 如何求呢? \frac{\partial c }{ \partial b} = 1告訴我們1個單位的b變化會引起1個單位的c變換,\frac{\partial e }{ \partial c} = 2告訴我們 1 個單位的c變化會引起2個單位的e變化。所以 \frac{ \partial e  }{ \partial b } =   \frac{ \partial c }{ \partial b } * \frac{ \partial e  }{ \partial c }   = 1*2 =2

嗎? 答案必然是錯誤。因為這樣做只考慮到了下圖橙色的路徑,所有的路徑都要考慮: \frac{ \partial e  }{ \partial b } =   \frac{ \partial c }{ \partial b } * \frac{ \partial e  }{ \partial c }  +  \frac{ \partial d  }{ \partial b }  *  \frac{ \partial e  }{ \partial d }  =1*2 + 1 * 3 = 5

preview

所以上面的求導方法總結為一句話就是: 路徑上所有邊相乘,所有路徑相加。不過這裡需要補充一條很有用的合併策略:

例如:下面的計算圖若要計算\frac{\partial Z}{\partial X}就會有9條路徑:\frac{\partial Z}{\partial X} = \alpha\delta + \alpha\epsilon + \alpha\zeta + \beta\delta + \beta\epsilon + \beta\zeta + \gamma\delta + \gamma\epsilon + \gamma\zeta

 

preview

如果計算圖再複雜一些,層數再多一些,路徑數量就會呈指數爆炸性增長。但是如果採用合併策略:\frac{\partial Z}{\partial X} = (\alpha + \beta + \gamma)(\delta + \epsilon + \zeta) 就不會出現這種問題。這種策略不是 對每一條路徑都求和,而是 “合併同類路徑”,“分階段求解”。先求X對Y的總影響 (\alpha + \beta + \gamma) 再求Y對Z的總影響 (\delta + \epsilon + \zeta) 最後綜合在一起。

1.2 兩種求導模式:前向模式求導( forward-mode differentiation) 反向模式求導(reverse-mode differentiation)

上面提到的求導方法都是前向模式求導( forward-mode differentiation) :從前向後。先求X對Y的總影響 (\alpha + \beta + \gamma) 再乘以Y對Z的總影響 (\delta + \epsilon + \zeta)

preview

另一種,反向模式求導(reverse-mode differentiation) 則是從後向前。先求Y對Z的影響再乘以X對Y的影響。

1.3 反向求導模式(反向傳播演算法)的重要性

讓我們再次考慮前面的例子:

如果用前向求導模式:關於b向前求導一次

preview

如果用反向求導模式:向後求導

preview

前向求導模式只得到了關於輸入b的偏導\frac{\partial e}{\partial b} ,還需要再次求解關於輸入a的偏導\frac{\partial e}{\partial a} (運算2遍)。而反向求導一次運算就得到了e對兩個輸入a,b的偏導\frac{\partial e}{\partial a}, \frac{\partial e}{\partial b} (運算1遍)。上面的比較只看到了2倍的加速。但如果有1億個輸入1個輸出,意味著前向求導需要操作1億遍才得到所有關於輸入的偏導,而反向求導則只需一次運算,1億倍的加速。

 

 

當我們訓練神經網路時,把“損失“ 看作 ”權重引數“ 的函式,需要計算”損失“關於每一個”權重引數“的偏導數(然後用梯度下降法學習)。 神經網路的權重引數可以是百萬甚至過億級別。因此 反向求導模式(反向傳播演算法)可以極大的加速學習。

二、理論推導

轉載文章:https://zhuanlan.zhihu.com/p/25416673

神經網路結構圖:

示例網路圖

preview

其中C是損失函式,例如C可以取:

梯度下降(SGD)進行學習時,核心問題是求解損失函式C關於所有網路引數w_{jk},b_j的偏導數\frac{\partial C}{\partial w_{jk}} ,\frac{\partial C}{\partial b_j}。 我們已經知道用反向傳播演算法可以“一次反向計算”得到損失函式C關於網路中所有引數的偏導數。我們首先畫出上面網路圖的詳細計算圖:再看看具體怎麼樣反向傳播求偏導數。

神經網路計算圖

對應計算圖如下:(只展開了最後兩層的計算圖):

preview

綠色代表權重引數w_{jk},橙色代表基底引數b_j。可見雖然網路圖上只是簡單幾條線,計算圖還是蠻複雜的。

現在我們在計算圖箭頭上標出對應的偏導數(只標出了一部分)。

preview

反向傳播四公式

變數定義:

上圖是一個三層人工神經網路,layer1至layer3分別是輸入層、隱藏層和輸出層。如圖,先定義一些變數:

表示第層的第個神經元連線到第層的第個神經元的權重

表示第層的第個神經元的偏置;

表示第層的第個神經元的輸入,即

 表示第層的第個神經元的輸出,即

 

其中表示啟用函式

上面計算圖上每一個節點關於前一個節點的偏導數都可以求得,根據求導的鏈式法則,想要求損失函式C關於某一節點的偏導數,只需要“把該節點每條反向路徑上的偏導數做乘積,再求和”即可。(w_{jk},b_j分別對應綠色和橙色的節點)

現在我們已經可以在計算圖上求得損失函式C關於模型引數的偏導數\frac{\partial C}{\partial w_{jk}} ,\frac{\partial C}{\partial b_j}。但是還不夠優雅,反向傳播演算法要優雅的很多,它通過定義一個損失(\delta_j^l),先逐層向後傳播得到每一層節點的損失(\delta_j^l),再通過每一個節點的損失(\delta_j^l)來求解該節點的\frac{\partial C}{\partial w_{jk}} ,\frac{\partial C}{\partial b_j}

 

首先記損失函式C關於l層的第j個元素的偏導為:\delta_j^l \equiv \frac{\partial C}{\partial z_j^l}

最後一層

對於最後一層(L層)的元素j會有:

\delta_j^L = \frac{\partial C}{\partial z_j^L}=\frac{\partial C}{\partial a_j^L} \cdot \frac{\partial a_j^L}{\partial z_j^L} = \frac{\partial C}{\partial a_j^L} \cdot \sigma^{'}(z_j^L)

 

向量化為:

\bm \delta^L = \begin{pmatrix} \delta_1^L \\\vdots \\ \delta_j^L \\   \vdots\\  \delta_n^L \end{pmatrix}= \begin{pmatrix} \frac{\partial C}{\partial a_1^L} \cdot \sigma^{'}(z_1^L) \\\vdots \\ \frac{\partial C}{\partial a_j^L} \cdot \sigma^{'}(z_j^L) \\   \vdots\\  \frac{\partial C}{\partial a_n^L} \cdot \sigma^{'}(z_n^L) \end{pmatrix}= \begin{pmatrix} \frac{\partial C}{\partial a_1^L} \\\vdots \\ \frac{\partial C}{\partial a_j^L} \\   \vdots\\  \frac{\partial C}{\partial a_n^L} \end{pmatrix}\odot  \begin{pmatrix} \sigma^{'}(z_1^L) \\\vdots \\ \sigma^{'}(z_j^L) \\   \vdots\\  \sigma^{'}(z_n^L) \end{pmatrix} =  \bm \nabla_aC \odot  \sigma^{'}(\bm z^L)

                                                                                   (BP1)

其中\odot的操作是把兩個向量對應元素相乘組成新的元素。

後一層傳播到前一層

由前面計算圖中L和L-1層所標註的偏導數,可得到倒數第一層(L-1)元素j的損失為:(請仔細對照前面的計算圖)

\delta_j^{L-1} = (\sum_{j=1}^n{\frac{\partial z_j^L}{\partial a_{k}^{L-1}}  \delta_j^L }) \cdot \sigma_{'}(z_j^{L-1}) = (\sum_{j=1}^n{w_{jk}^L \delta_j^L } ) \cdot  \sigma_{'}(z_j^{L-1})  = \begin{pmatrix}  w_{1k}^L \cdots w_{jk}^L \cdots  w_{nk}^L\\   \end{pmatrix}  \begin{pmatrix}  \delta_1^L \\   \vdots \\   \delta_j^L\\ \vdots\\ \delta_n^L  \end{pmatrix} \cdot \sigma^{'}(z_j^{L-1})

向量化:\delta^{L-1} = ((w^{L})^T\delta^{L} \odot \sigma^{'}(z^{L-1}) )

這啟發我們後一層(l+1層)的損失\delta^{l+1} 如何傳播到前一層(l層)得到\delta^l。(只需要把L用l+1替換,L-1l替換)就得到了逐層傳播損失的公式

\bm \delta^{l} = ((\bm w^{l+1})^T \bm \delta^{l+1} \odot \sigma^{'}(\bm z^{l}) )

                                                                                              (BP2)

 

關於b_j^l的偏導數

\frac{\partial C}{\partial b_j^l} =\frac{ \partial C}{ \partial z_j^l} \frac{\partial z_j^l}{\partial b_j^l} = \delta_j^l \cdot 1 = \delta_j^l

                                                                                              (BP3)

向量化:\frac{\partial C}{\partial b^l} =\bm \delta^l


關於w_{jk}^l的偏導數

\frac{\partial C}{\partial w_{jk}^l} =\frac{ \partial C}{ \partial z_j^l} \frac{\partial z_j^l}{\partial w_{jk}^l} = \delta_j^l \cdot a_k^{l-1}

                                                                                           (BP4)

向量化:\frac{\partial C}{\partial w_{j\cdot }^l} = \begin{pmatrix} \delta_j^l  a_1^{l-1}  \\   \vdots \\   \delta_j^l  a_k^{l-1} \\ \vdots\\ \delta_j^l  a_n^{l-1}   \end{pmatrix} = \delta_j^l \cdot \begin{pmatrix}   a_1^{l-1}  \\   \vdots \\     a_k^{l-1} \\ \vdots\\   a_n^{l-1}   \end{pmatrix} = \delta_j^l \cdot \bm a^{l-1}\Rightarrow

\frac{\partial C}{\partial w^l} =  \begin{pmatrix} \delta^l_1 \cdot \bm a^{l-1} \\ \vdots \\ \delta^l_j \cdot \bm a^{l-1}\\ \vdots\\ \delta^l_n \cdot \bm a^{l-1}\\ \end{pmatrix} =  \begin{pmatrix} \delta^l_1   \\ \vdots \\ \delta^l_j \\ \vdots\\ \delta^l_n \\ \end{pmatrix} \cdot  \begin{pmatrix}  a^{l-1}_1  \cdots a^{l-1}_k \cdots a^{l-1}_n   \end{pmatrix} = \bm \delta^l \cdot (\bm a^{l-1})^T
 

至此就得到了反向傳播的4個公式:

 

preview

三 理論推導(向量化推導)

轉載:https://blog.csdn.net/u014313009/article/details/51039334

公式1(計算最後一層神經網路產生的錯誤):

 
       其中,表示Hadamard乘積,用於矩陣或向量之間點對點的乘法運算。公式1的推導過程如下:

公式2(由後往前,計算每一層神經網路產生的錯誤):

 
       推導過程:

公式3(計算權重的梯度):

 
       推導過程:

公式4(計算偏置的梯度):

 
       推導過程:

4. 反向傳播演算法虛擬碼


輸入訓練集


對於訓練集中的每個樣本x,設定輸入層(Input layer)對應的啟用值:
前向傳播:

 ,

計算輸出層產生的錯誤:


反向傳播錯誤:

使用梯度下降(gradient descent),訓練引數:
 

(對於每一個樣本x)
---------------------