一文搞定BP神經網絡——從原理到應用(原理篇)

  本文着重講述經典BP神經網絡的數學推導過程,並輔助一個小例子。本文不會介紹機器學習庫(好比sklearn, TensorFlow等)的使用。 欲瞭解卷積神經網絡的內容,請參見個人另外一篇博客一文搞定卷積神經網絡——從原理到應用html

  本文不免會有敘述不合理的地方,但願讀者能夠在評論區反饋。我會及時吸納你們的意見,並在以後的chat裏進行說明。python

本文參考了一些資料,在此一併列出。git

0. 什麼是人工神經網絡?

  首先給出一個經典的定義:「神經網絡是由具備適應性的簡單單元組成的普遍並行互連網絡,它的組織可以模擬生物神經系統對真實世界物體所做出的交互反應」[Kohonen, 1988]。github

  這種說法雖然很經典,可是對於初學者並非很友好。好比我在剛開始學習的時候就把人工神經網絡想象地很高端,以致於很長一段時間都不能理解爲何神經網絡可以起做用。類比最小二乘法線性迴歸問題,在求解數據擬合直線的時候,咱們是採用某種方法讓預測值和實際值的「誤差」儘量小。同理,BP神經網絡也作了相似的事情——即經過讓「誤差」儘量小,使得神經網絡模型儘量好地擬合數據集。web

1. 神經網絡初探

1.1 神經元模型

  神經元模型是模擬生物神經元結構而被設計出來的。典型的神經元結構以下圖1所示:
在這裏插入圖片描述算法

【圖1 典型神經元結構 (圖片來自維基百科)】網絡

  神經元大體能夠分爲樹突、突觸、細胞體和軸突。樹突爲神經元的輸入通道,其功能是將其它神經元的動做電位傳遞至細胞體。其它神經元的動做電位藉由位於樹突分支上的多個突觸傳遞至樹突上。神經細胞能夠視爲有兩種狀態的機器,激活時爲「是」,不激活時爲「否」。神經細胞的狀態取決於從其餘神經細胞接收到的信號量,以及突觸的性質(抑制或增強)。當信號量超過某個閾值時,細胞體就會被激活,產生電脈衝。電脈衝沿着軸突並經過突觸傳遞到其它神經元。(內容來自維基百科「感知機」)app

  同理,咱們的神經元模型就是爲了模擬上述過程,典型的神經元模型以下:dom

在這裏插入圖片描述

【圖2 典型神經元模型結構 (摘自周志華老師《機器學習》第97頁)】機器學習

  這個模型中,每一個神經元都接受來自其它神經元的輸入信號,每一個信號都經過一個帶有權重的鏈接傳遞,神經元把這些信號加起來獲得一個總輸入值,而後將總輸入值與神經元的閾值進行對比(模擬閾值電位),而後經過一個「激活函數」處理獲得最終的輸出(模擬細胞的激活),這個輸出又會做爲以後神經元的輸入一層一層傳遞下去。

1.2 神經元激活函數

  本文主要介紹2種激活函數,分別是 s i g m o i d sigmoid r e l u relu 函數,函數公式以下:
s i g m o i d ( z ) = 1 1 + e z sigmoid(z)=\frac{1}{1+e^{-z}}
r e l u ( z ) = { z z > 0 0 z 0 relu(z)= \left\{ \begin{array}{rcl} z & z>0\\ 0&z\leq0\end{array} \right.
  作函數圖以下:

在這裏插入圖片描述
s i g m o i d ( z ) sigmoid(z)
在這裏插入圖片描述
r e l u ( z ) relu(z)
【圖3 激活函數】

補充說明
【補充說明的內容建議在看完後文的反向傳播部分以後再回來閱讀,我只是爲了文章結構的統一把這部份內容添加在了這裏】

  引入激活函數的目的是在模型中引入非線性。若是沒有激活函數,那麼不管你的神經網絡有多少層,最終都是一個線性映射,單純的線性映射沒法解決線性不可分問題。引入非線性可讓模型解決線性不可分問題。

  通常來講,在神經網絡的中間層更加建議使用 r e l u relu 函數,兩個緣由:

  • r e l u relu 函數計算簡單,能夠加快模型速度;
  • 因爲反向傳播過程當中須要計算偏導數,經過求導能夠獲得 s i g m o i d sigmoid 函數導數的最大值爲0.25,若是使用 s i g m o i d sigmoid 函數的話,每一層的反向傳播都會使梯度最少變爲原來的四分之一,當層數比較多的時候可能會形成梯度消失,從而模型沒法收斂。

1.3 神經網絡結構

  咱們使用以下神經網絡結構來進行介紹,第0層是輸入層(3個神經元), 第1層是隱含層(2個神經元),第2層是輸出層:

enter image description here
【圖4 神經網絡結構(手繪)】

  咱們使用如下符號約定 w j k [ l ] w_{jk}^{[l]} 表示從網絡第 ( l 1 ) t h (l-1)^{th} k t h k^{th} 個神經元指向第 l t h l^{th} 中第 j t h j^{th} 個神經元的鏈接權重,好比上圖中 w 21 [ 1 ] w^{[1]}_{21} 即從第0層第1個神經元指向第1層第2個神經元的權重。同理,咱們使用 b j [ l ] b^{[l]}_j 來表示第 l t h l^{th} 層中第 j t h j^{th} 神經元的誤差,用 z j [ l ] z^{[l]}_j 來表示第 l t h l^{th} 層中第 j t h j^{th} 神經元的線性結果,用 a j [ l ] a^{[l]}_j 來表示第 l t h l^{th} 層中第 j t h j^{th} 神經元的激活函數輸出。

  激活函數使用符號 σ \sigma 表示,所以,第 l t h l^{th} 層中第 j t h j^{th} 神經元的激活爲:
a j [ l ] = σ ( k w j k [ l ] a k [ l 1 ] + b j [ l ] ) a^{[l]}_j=\sigma(\sum_kw^{[l]}_{jk}a^{[l-1]}_k+b^{[l]}_j)

  如今,咱們使用矩陣形式重寫這個公式:

  定義 w [ l ] w^{[l]} 表示權重矩陣,它的每個元素表示一個權重,即每一行都是鏈接第 l l 層的權重,用上圖舉個例子就是:

w [ 1 ] = [ w 11 [ 1 ] w 12 [ 1 ] w 13 [ 1 ] w 21 [ 1 ] w 22 [ 1 ] w 23 [ 1 ] ] w^{[1]}=\left[ \begin{array}{cc} w_{11}^{[1]} & w_{12}^{[1]} & w_{13}^{[1]} \\ w_{21}^{[1]}& w_{22}^{[1]} & w_{23}^{[1]}\end{array}\right]
  同理,
b [ 1 ] = [ b 1 [ 1 ] b 2 [ 1 ] ] b^{[1]}=\left[ \begin{array}{cc}b^{[1]}_1 \\ b^{[1]}_2 \end{array}\right]
z [ 1 ] = [ w 11 [ 1 ] w 12 [ 1 ] w 13 [ 1 ] w 21 [ 1 ] w 22 [ 1 ] w 23 [ 1 ] ] [ a 1 [ 0 ] a 2 [ 0 ] a 3 [ 0 ] ] + [ b 1 [ 1 ] b 2 [ 1 ] ] = [ w 11 [ 1 ] a 1 [ 0 ] + w 12 [ 1 ] a 2 [ 0 ] + w 13 [ 1 ] a 3 [ 0 ] + b 1 [ 1 ] w 21 [ 1 ] a 1 [ 0 ] + w 22 [ 1 ] a 2 [ 0 ] + w 23 [ 1 ] a 3 [ 0 ] + b 2 [ 1 ] ] z^{[1]}=\left[ \begin{array}{cc} w_{11}^{[1]} & w_{12}^{[1]} & w_{13}^{[1]} \\ w_{21}^{[1]}& w_{22}^{[1]} & w_{23}^{[1]}\end{array}\right]\cdot \left[ \begin{array}{cc} a^{[0]}_1 \\ a^{[0]}_2 \\ a^{[0]}_3 \end{array}\right] +\left[ \begin{array}{cc}b^{[1]}_1 \\ b^{[1]}_2 \end{array}\right]=\left[ \begin{array}{cc} w_{11}^{[1]}a^{[0]}_1+w_{12}^{[1]}a^{[0]}_2+w_{13}^{[1]}a^{[0]}_3+b^{[1]}_1 \\ w^{[1]}_{21}a^{[0]}_1+w_{22}^{[1]}a^{[0]}_2+w_{23}^{[1]}a^{[0]}_3+b^{[1]}_2\end{array}\right]

  更通常地,咱們能夠把前向傳播過程表示:
a [ l ] = σ ( w [ l ] a [ l 1 ] + b [ l ] ) a^{[l]}=\sigma(w^{[l]}a^{[l-1]}+b^{[l]})

  到這裏,咱們已經講完了前向傳播的過程,值得注意的是,這裏咱們只有一個輸入樣本,對於多個樣本同時輸入的狀況是同樣的,只不過咱們的輸入向量再也不是一列,而是m列,每個都表示一個輸入樣本。

  多樣本輸入狀況下的表示爲:
Z [ l ] = w [ l ] A [ l 1 ] + b [ l ] Z^{[l]}=w^{[l]}\cdot A^{[l-1]}+b^{[l]}
A [ l ] = σ ( Z [ l ] ) A^{[l]}=\sigma(Z^{[l]})
其中,此時 A [ l 1 ] = [ a [ l 1 ] ( 1 ) a [ l 1 ] ( 2 ) a [ l 1 ] ( m ) ] A^{[l-1]}=\left[ \begin{array}{cc} |&|&\ldots&| \\a^{[l-1](1)}&a^{[l-1](2)}&\ldots&a^{[l-1](m)} \\ |&|&\ldots&|\end{array}\right]
每一列都表示一個樣本,從樣本1到m

   w [ l ] w^{[l]} 的含義和原來徹底同樣, Z [ l ] Z^{[l]} 也會變成m列,每一列表示一個樣本的計算結果。

以後咱們的敘述都是先討論單個樣本的狀況,再擴展到多個樣本同時計算。

2. 損失函數和代價函數

  說實話,**損失函數(Loss Function)代價函數(Cost Function)**並無一個公認的區分標準,不少論文和教材彷佛把兩者當成了差很少的東西。

  爲了後面描述的方便,咱們把兩者稍微作一下區分(這裏的區分僅僅對本文適用,對於其它的文章或教程須要根據上下文自行判斷含義):

  損失函數主要指的是對於單個樣本的損失或偏差;代價函數表示多樣本同時輸入模型的時候整體的偏差——每一個樣本偏差的和而後取平均值。

  舉個例子,若是咱們把單個樣本的損失函數定義爲:
L ( a , y ) = [ y l o g ( a ) + ( 1 y ) l o g ( 1 a ) ] L(a,y)=-[y \cdot log(a)+(1-y)\cdot log(1-a)]
  那麼對於m個樣本,代價函數則是:
C = 1 m i = 0 m ( y ( i ) l o g ( a ( i ) ) + ( 1 y ( i ) ) l o g ( 1 a ( i ) ) ) C=-\frac{1}{m}\sum_{i=0}^m(y^{(i)}\cdot log(a^{(i)})+(1-y^{(i)})\cdot log(1-a^{(i)}))

3. 反向傳播

  反向傳播的基本思想就是經過計算輸出層與指望值之間的偏差來調整網絡參數,從而使得偏差變小。

  反向傳播的思想很簡單,然而人們認識到它的重要做用卻通過了很長的時間。後向傳播算法產生於1970年,但它的重要性一直到David Rumelhart,Geoffrey Hinton和Ronald Williams於1986年合著的論文發表才被重視。

  事實上,人工神經網絡的強大力量幾乎就是創建在反向傳播算法基礎之上的。反向傳播基於四個基礎等式,數學是優美的,僅僅四個等式就能夠歸納神經網絡的反向傳播過程,然而理解這種優美可能須要付出一些腦力。事實上,反向傳播如此之難,以致於至關一部分初學者很難進行獨立推導。因此若是讀者是初學者,但願讀者能夠耐心地研讀本節。對於初學者,我以爲拿出1-3個小時來學習本小節是比較合適的,固然,對於熟練掌握反向傳播原理的讀者,你能夠在十幾分鍾甚至幾分鐘以內快速瀏覽本節的內容。

3.1 矩陣補充知識

  對於大部分理工科的研究生,以及學習過矩陣論或者工程矩陣理論相關課程的讀者來講,能夠跳過本節。

  本節主要面向只學習過本科線性代數課程或者已經忘記矩陣論有關知識的讀者。

  總之,具有了本科線性代數知識的讀者閱讀這一小節應該不會有太大問題。本節主要在線性代數的基礎上作一些擴展。(不排除少數本科線性代數課程也涉及到這些內容,若是感受講的簡單的話,勿噴)

3.1.1 求梯度矩陣

  假設函數 f : R m × n R f:R^{m\times n}\rightarrow R 能夠把輸入矩陣(shape: m × n m\times n )映射爲一個實數。那麼,函數 f f 的梯度定義爲:

A f ( A ) = [ f ( A ) A 11 f ( A ) A 12 f ( A ) A 1 n f ( A ) A 21 f ( A ) A 22 f ( A ) A 2 n f ( A ) A m 1 f ( A ) A m 2 f ( A ) A m n ] \nabla_Af(A)=\left[ \begin{array}{cc} \frac{\partial f(A)}{\partial A_{11}}&\frac{\partial f(A)}{\partial A_{12}}&\ldots&\frac{\partial f(A)}{\partial A_{1n}} \\ \frac{\partial f(A)}{\partial A_{21}}&\frac{\partial f(A)}{\partial A_{22}}&\ldots&\frac{\partial f(A)}{\partial A_{2n}} \\\vdots &\vdots &\ddots&\vdots\\ \frac{\partial f(A)}{\partial A_{m1}}&\frac{\partial f(A)}{\partial A_{m2}}&\ldots&\frac{\partial f(A)}{\partial A_{mn}}\end{array}\right]
  即 ( A f ( A ) ) i j = f ( A ) A i j (\nabla_Af(A))_{ij}=\frac{\partial f(A)}{\partial A_{ij}}

  同理,一個輸入是向量(向量通常指列向量,本文在沒有特殊聲明的狀況下默認指的是列向量)的函數 f : R n × 1 R f:R^{n\times 1}\rightarrow R ,則有:

x f ( x ) = [ f ( x ) x 1 f ( x ) x 2 f ( x ) x n ] \nabla_xf(x)=\left[ \begin{array}{cc}\frac{\partial f(x)}{\partial x_1}\\ \frac{\partial f(x)}{\partial x_2}\\ \vdots \\ \frac{\partial f(x)}{\partial x_n} \end{array}\right]

  注意:這裏涉及到的梯度求解的前提是函數 f f 返回的是一個實數若是函數返回的是一個矩陣或者向量,那麼咱們是沒有辦法求梯度的。好比,對函數 f ( A ) = i = 0 m j = 0 n A i j 2 f(A)=\sum_{i=0}^m\sum_{j=0}^nA_{ij}^2 ,因爲返回一個實數,咱們能夠求解梯度矩陣。若是 f ( x ) = A x ( A R m × n , x R n × 1 ) f(x)=Ax (A\in R^{m\times n}, x\in R^{n\times 1}) ,因爲函數返回一個 m m 行1列的向量,所以不能對 f f 求梯度矩陣。

  根據定義,很容易獲得如下性質:

   x ( f ( x ) + g ( x ) ) = x f ( x ) + x g ( x ) \nabla_x(f(x)+g(x))=\nabla_xf(x)+\nabla_xg(x)
   ( t f ( x ) ) = t f ( x ) , t R \nabla(tf(x))=t\nabla f(x), t\in R

  有了上述知識,咱們來舉個例子:

  定義函數 f : R m R , f ( z ) = z T z f:R^m\rightarrow R, f(z)=z^Tz ,那麼很容易獲得 z f ( z ) = 2 z \nabla_zf(z)=2z ,具體請讀者本身證實。

3.1.2 海塞矩陣

  定義一個輸入爲 n n 維向量,輸出爲實數的函數 f : R n R f:R^n\rightarrow R ,那麼海塞矩陣(Hessian Matrix)定義爲多元函數 f f 的二階偏導數構成的方陣:

x 2 f ( x ) = [ 2 f ( x ) x 1 2 2 f ( x ) x 1 x 2 2 f ( x ) x 1 x n 2 f ( x ) x 2 x 1 2 f ( x ) x 2 2 2 f ( x ) x 2 x n 2 f ( x ) x n x 1 2 f ( x ) x n x 2 2 f ( x ) x n 2 ] \nabla^2_xf(x)=\left[ \begin{array}{cc} \frac{\partial^2f(x)}{\partial x_1^2}&\frac{\partial^2f(x)}{\partial x_1\partial x_2}&\ldots &\frac{\partial^2f(x)}{\partial x_1\partial x_n}\\ \frac{\partial^2f(x)}{\partial x_2\partial x_1}&\frac{\partial^2f(x)}{\partial x_2^2}&\ldots&\frac{\partial^2f(x)}{\partial x_2\partial x_n}\\ \vdots&\vdots&\ddots&\vdots\\\frac{\partial^2f(x)}{\partial x_n\partial x_1}&\frac{\partial^2f(x)}{\partial x_n\partial x_2}&\ldots&\frac{\partial^2f(x)}{\partial x_n^2}\end{array}\right]

  由上式能夠看出,海塞矩陣老是對稱陣

  注意:不少人把海塞矩陣當作 x f ( x ) \nabla _xf(x) 的導數,這是不對的。只能說,海塞矩陣的每一個元素都是函數 f f 二階偏導數。那麼,有什麼區別呢?

  首先,來看正確的解釋。**海塞矩陣的每一個元素是函數 f f 的二階偏導數。**拿 2 f ( x ) x 1 x 2 \frac{\partial^2f(x)}{\partial x_1\partial x_2} 舉個例子,函數 f f x 1 x_1 求偏導獲得的是一個實數,好比 2 f ( x ) x 1 = x 2 3 x 1 \frac{\partial^2f(x)}{\partial x_1}=x_2^3x_1 ,所以繼續求偏導是有意義的,繼續對 x 2 x_2 求偏導能夠獲得 3 x 1 x 2 2 3x_1x_2^2

  而後,來看一下錯誤的理解。把海塞矩陣當作 x f ( x ) \nabla _xf(x) 的導數,也就是說錯誤地覺得 x 2 f ( x ) = x ( x f ( x ) ) \nabla^2_xf(x)=\nabla_x(\nabla_xf(x)) ,要知道, x f ( x ) \nabla_xf(x) 是一個向量,而在上一小節咱們已經重點強調過,在咱們的定義裏對向量求偏導是沒有定義的

  可是 x f ( x ) x i \nabla_x\frac{\partial f(x)}{\partial x_i} 是有意義的,由於 f ( x ) x i \frac{\partial f(x)}{\partial x_i} 是一個實數,具體地:

x f ( x ) x i = [ 2 f ( x ) x i x 1 2 f ( x ) x i x 2 2 f ( x ) x i x n ] \nabla_x\frac{\partial f(x)}{\partial x_i}=\left[ \begin{array}{cc} \frac{\partial^2f(x)}{\partial x_i\partial x_1}\\\frac{\partial^2f(x)}{\partial x_i\partial x_2}\\\vdots\\\frac{\partial^2f(x)}{\partial x_i\partial x_n}\end{array}\right]

  即海塞矩陣的第i行(或列)。

  但願讀者能夠好好區分。

3.1.3 總結

  根據3.1.1和3.1.2小節的內容很容易獲得如下等式:

   b R n , x R n , A R n × n A b\in R^{n}, x\in R^n, A\in R^{n\times n}而且A 是對稱矩陣
   b , x b,x 均爲列向量
  那麼,
   x b T x = b \nabla_xb^Tx=b
   x x T A x = 2 A x ( A ) \nabla_xx^TAx=2Ax(A是對稱陣)
   x 2 x T A x = 2 A ( A ) \nabla^2_xx^TAx=2A(A是對稱陣)

  這些公式能夠根據前述定義自行推導,有興趣的讀者能夠本身推導一下。
####3.2 矩陣乘積和對應元素相乘
  在下一節講解反向傳播原理的時候,尤爲是把公式以矩陣形式表示的時候,須要你們時刻區分何時須要矩陣相乘,何時須要對應元素相乘。

  好比對於矩陣 A = [ 1 2 3 4 ] B = [ 1 2 3 4 ] A=\left[ \begin{array}{cc} 1&2\\3&4\end{array}\right],矩陣B=\left[ \begin{array}{cc} -1&-2\\-3&-4\end{array}\right]
  矩陣相乘

A B = [ 1 × 1 + 2 × 3 1 × 2 + 2 × 4 3 × 1 + 4 × 3 3 × 2 + 4 × 4 ] = [ 7 10 15 22 ] AB=\left[\begin{array}{cc}1\times -1+2\times -3&1\times -2+2\times -4\\3\times -1+4\times -3&3\times -2+4\times -4\end{array}\right]=\left[\begin{array}{cc}-7&-10\\-15&-22\end{array}\right]

  對應元素相乘使用符號 \odot 表示:

A B = [ 1 × 1 2 × 2 3 × 3 4 × 4 ] = [ 1 4 9 16 ] A\odot B=\left[\begin{array}{cc}1\times -1&2\times -2 \\ 3\times -3&4\times -4\end{array}\right]=\left[\begin{array}{cc}-1&-4 \\ -9&-16\end{array}\right]

3.3 梯度降低法原理

  經過以前的介紹,相信你們均可以本身求解梯度矩陣(向量)了。

  那麼梯度矩陣(向量)求出來的意義是什麼?從幾何意義講,梯度矩陣表明了函數增長最快的方向,所以,沿着與之相反的方向就能夠更快找到最小值。如圖5所示:

在這裏插入圖片描述

【圖5 梯度降低法 圖片來自百度】

  反向傳播的過程就是利用梯度降低法原理,慢慢的找到代價函數的最小值,從而獲得最終的模型參數。梯度降低法在反向傳播中的具體應用見下一小節。

3.4 反向傳播原理(四個基礎等式)

  反向傳播可以知道如何更改網絡中的權重 w w 和誤差 b b 來改變代價函數值。最終這意味着它可以計算偏導數 L ( a [ l ] , y ) w j k [ l ] \frac{\partial L(a^{[l]},y)} {\partial w^{[l]}_{jk}} L ( a [ l ] , y ) b j [ l ] \frac{\partial L(a^{[l]},y)}{\partial b^{[l]}_j}
  爲了計算這些偏導數,咱們首先引入一箇中間變量 δ j [ l ] \delta^{[l]}_j ,咱們把它叫作網絡中第 l t h l^{th} 層第 j t h j^{th} 個神經元的偏差。後向傳播可以計算出偏差 δ j [ l ] \delta^{[l]}_j ,而後再將其對應回 L ( a [ l ] , y ) w j k [ l ] \frac{\partial L(a^{[l]},y)}{\partial w^{[l]}_{jk}} L ( a [ l ] , y ) b j [ l ] \frac{\partial L(a^{[l]},y)}{\partial b^{[l]}_j}

  那麼,如何定義每一層的偏差呢?若是爲第 l l 層第 j j 個神經元添加一個擾動 Δ z j [ l ] \Delta z^{[l]}_j ,使得損失函數或者代價函數變小,那麼這就是一個好的擾動。經過選擇 Δ z j [ l ] \Delta z^{[l]}_j L ( a [ l ] , y ) z j [ l ] \frac{\partial L(a^{[l]}, y)}{\partial z^{[l]}_j} 符號相反(梯度降低法原理),就能夠每次都添加一個好的擾動最終達到最優。

  受此啓發,咱們定義網絡層第 l l 層中第 j j 個神經元的偏差爲 δ j [ l ] \delta^{[l]}_j :

δ j [ l ] = L ( a [ L ] , y ) z j [ l ] \delta^{[l]}_j=\frac{\partial L(a^{[L], y})}{\partial z^{[l]}_j}

  因而,每一層的偏差向量能夠表示爲:

δ [ l ] = [ δ 1 [ l ] δ 2 [ l ] δ n [ l ] ] \delta ^{[l]}=\left[\begin{array}{cc}\delta ^{[l]}_1\\\delta ^{[l]}_2\\ \vdots \\ \delta ^{[l]}_n\end{array} \right]

  下面開始正式介紹四個基礎等式【確切的說是四組等式】

  **注意:**這裏咱們的輸入爲單個樣本(因此咱們在下面的公式中使用的是損失函數而不是代價函數)。多個樣本輸入的公式會在介紹完單個樣本後再介紹。

  • 等式1 :輸出層偏差

δ j [ L ] = L a j [ L ] σ ( z j [ L ] ) \delta^{[L]}_j=\frac{\partial L}{\partial a^{[L]}_j}\sigma^{'}(z^{[L]}_j)
  其中, L L 表示輸出層層數。如下用 L \partial L 表示 L ( a [ L ] , y ) \partial L(a^{[L]}, y)

  寫成矩陣形式是:

δ [ L ] = a L σ ( z [ L ] ) \delta^{[L]}=\nabla _aL\odot \sigma^{'}(z^{[L]})
  【注意是對應元素相乘,想一想爲何?】

  說明

  根據本小節開始時的敘述,咱們指望找到 L   / z j [ l ] \partial L \ /\partial z^{[l]}_j ,而後朝着方向相反的方向更新網絡參數,並定義偏差爲:

δ j [ L ] = L z j [ L ] \delta^{[L]}_j=\frac{\partial L}{\partial z^{[L]}_j}

  根據鏈式法則,
δ j [ L ] = k L a k [ L ] a k [ L ] z j [ L ] \delta^{[L]}_j = \sum_k \frac{\partial L}{\partial a^{[L]}_k} \frac{\partial a^{[L]}_k}{\partial z^{[L]}_j}
  當 k j k\neq j 時, a k [ L ] / z j [ L ] \partial a^{[L]}_k / \partial z^{[L]}_j 就爲零。結果咱們能夠簡化以前的等式爲
δ j [ L ] = L a j [ L ] a j [ L ] z j [ L ] \delta^{[L]}_j = \frac{\partial L}{\partial a^{[L]}_j} \frac{\partial a^{[L]}_j}{\partial z^{[L]}_j}
  從新拿出定義: a j [ L ] = σ ( z j [ L ] ) a^{[L]}_j = \sigma(z^{[L]}_j) ,就能夠獲得:
δ j [ L ] = L a j [ L ] σ ( z j [ L ] ) \delta^{[L]}_j = \frac{\partial L}{\partial a^{[L]}_j} \sigma'(z^{[L]}_j)
  再"堆砌"成向量形式就獲得了咱們的矩陣表示式(這也是爲何使用矩陣形式表示須要 對應元素相乘 的緣由)。

  • 等式2: 隱含層偏差
    δ j [ l ] = k w k j [ l + 1 ] δ k [ l + 1 ] σ ( z j [ l ] ) \delta^{[l]}_j = \sum_k w^{[l+1]}_{kj} \delta^{[l+1]}_k \sigma'(z^{[l]}_j)

  寫成矩陣形式:

δ [ l ] = [ w [ l + 1 ] T δ [ l + 1 ] ] σ ( z [ l ] ) \delta^{[l]}=[w^{[l+1]T}\delta^{[l+1]}]\odot \sigma ^{'}(z^{[l]})

  說明:

z k [ l + 1 ] = j w k j [ l + 1 ] a j [ l ] + b k [ l + 1 ] = j w k j [ l + 1 ] σ ( z j [ l ] ) + b k [ l + 1 ] z^{[l+1]}_k=\sum_jw^{[l+1]}_{kj}a^{[l]}_j+b^{[l+1]}_k=\sum_jw^{[l+1]}_{kj}\sigma(z^{[l]}_j)+b^{[l+1]}_k
  進行偏導能夠得到:
z k [ l + 1 ] z j [ l ] = w k j [ l + 1 ] σ ( z j [ l ] ) \frac{\partial z^{[l+1]}_k}{\partial z^{[l]}_j} = w^{[l+1]}_{kj} \sigma'(z^{[l]}_j)
  代入獲得:
δ j [ l ] = k w k j [ l + 1 ] δ k [ l + 1 ] σ ( z j [ l ] ) \delta^{[l]}_j = \sum_k w^{[l+1]}_{kj} \delta^{[l+1]}_k \sigma'(z^{[l]}_j)

  • 等式3:參數變化率

L b j [ l ] = δ j [ l ] \frac{\partial L}{\partial b^{[l]}_j}=\delta^{[l]}_j

L w j k [ l ] = a k [ l 1 ] δ j [ l ] \frac{\partial L}{\partial w^{[l]}_{jk}}=a^{[l-1]}_k\delta^{[l]}_j

  寫成矩陣形式:
L b [ l ] = δ [ l ] \frac{\partial L}{\partial b^{[l]}}=\delta^{[l]} L w [ l ] = δ [ l ] a [ l 1 ] T \frac{\partial L}{\partial w^{[l]}}=\delta^{[l]}a^{[l-1]T}

  說明:

  根據鏈式法則推導。
  因爲
z j [ l ] = k w j k [ l ] a k [ l ] + b k [ l ] z^{[l]}_j=\sum_kw^{[l]}_{jk}a^{[l]}_k+b^{[l]}_k
  對 b j [ l ] b^{[l]}_j 求偏導獲得:
L b j [ l ] = L z j [ l ] z j [ l ] b j [ l ] = δ j [ l ] \frac{\partial L}{\partial b^{[l]}_j}=\frac{\partial L}{\partial z^{[l]}_j}\frac{\partial z^{[l]}_j}{b^{[l]}_j}=\delta^{[l]}_j
  對 w j k [ l ] w^{[l]}_{jk} 求偏導獲得:
L w j k [ l ] = L z j [ l ] z j [ l ] w j k [ l ] = a k [ l 1 ] δ j [ l ] \frac{\partial L}{\partial w^{[l]}_{jk}}=\frac{\partial L}{\partial z^{[l]}_j}\frac{\partial z^{[l]}_j}{w^{[l]}_{jk}}=a^{[l-1]}_k\delta^{[l]}_j
  最後再變成矩陣形式就行了。

  對矩陣形式來講,須要特別注意維度的匹配。強烈建議讀者在本身編寫程序以前,先列出這些等式,而後仔細檢查維度是否匹配。

  很容易看出 L w [ l ] \frac{\partial L}{\partial w^{[l]}} 是一個 d i m ( δ [ l ] ) dim(\delta^{[l]}) d i m ( a [ l 1 ] ) dim(a^{[l-1]}) 列的矩陣,和 w [ l ] w^{[l]} 的維度一致; L b [ l ] \frac{\partial L}{\partial b^{[l]}} 是一個維度爲 d i m ( δ [ l ] ) dim(\delta^{[l]}) 的列向量

  • 等式4:參數更新規則

  這應該是這四組公式裏最簡單的一組了,根據梯度降低法原理,朝着梯度的反方向更新參數:

b j [ l ] b j [ l ] α L b j [ l ] b^{[l]}_j\leftarrow b^{[l]}_j-\alpha \frac{\partial L}{\partial b^{[l]}_j}
w j k [ l ] w j k [ l ] α L w j k [ l ] w^{[l]}_{jk}\leftarrow w^{[l]}_{jk}-\alpha\frac{\partial L}{\partial w^{[l]}_{jk}}
  寫成矩陣形式:

b [ l ] b [ l ] α L b [ l ] b^{[l]}\leftarrow b^{[l]}-\alpha\frac{\partial L}{\partial b^{[l]}}

w [ l ] w [ l ] α L w [ l ] w^{[l]}\leftarrow w^{[l]}-\alpha\frac{\partial L}{\partial w^{[l]}}

  這裏的 α \alpha 指的是學習率。學習率指定了反向傳播過程當中梯度降低的步長。

3.5 反向傳播總結

  咱們能夠獲得以下最終公式:

3.5.1 單樣本輸入公式表
說明 公式 備註
輸出層偏差 δ [ L ] = a L σ ( z [ L ] ) \delta^{[L]}=\nabla _aL\odot \sigma^{'}(z^{[L]})
隱含層偏差 δ [ l ] = [ w [ l + 1 ] T δ [ l + 1 ] ] σ ( z [ l ] ) \delta^{[l]}=[w^{[l+1]T}\delta^{[l+1]}]\odot \sigma ^{'}(z^{[l]})
參數變化率 L b [ l ] = δ [ l ] \frac{\partial L}{\partial b^{[l]}}=\delta^{[l]} L w [ l ] = δ [ l ] a [ l 1 ] T \frac{\partial L}{\partial w^{[l]}}=\delta^{[l]}a^{[l-1]T} 注意維度匹配
參數更新 b [ l ] b [ l ] α L b [ l ] b^{[l]}\leftarrow b^{[l]}-\alpha\frac{\partial L}{\partial b^{[l]}} w [ l ] w [ l ] α L w [ l ] w^{[l]}\leftarrow w^{[l]}-\alpha\frac{\partial L}{\partial w^{[l]}} α \alpha 是學習率
3.5.2 多樣本輸入公式表

  多樣本:須要使用代價函數,若是有m個樣本,那麼因爲代價函數有一個 1 m \frac{1}{m} 的常數項,所以全部的參數更新規則都須要有一個 1 m \frac{1}{m} 的前綴。

  多樣本同時輸入的時候須要格外注意維度匹配,一開始可能以爲有點混亂,可是不斷加深理解就會豁然開朗。

說明 公式 備註
輸出層偏差 d Z [ L ] = A C σ ( Z [ L ] ) dZ^{[L]}=\nabla _AC\odot \sigma^{'}(Z^{[L]}) 此時 d Z [ l ] dZ^{[l]} 再也不是一個列向量,變成了一個 m m 列的矩陣,每一列都對應一個樣本的向量
隱含層偏差 d Z [ l ] = [ w [ l + 1 ] T d Z [ l + 1 ] ] σ ( Z [ l ] ) dZ^{[l]}=[w^{[l+1]T}dZ^{[l+1]}]\odot \sigma ^{'}(Z^{[l]}) 此時 d Z [ l ] dZ^{[l]} 的維度是 n × m n\times m n n 表示第l層神經元的個數,m表示樣本數
參數變化率 d b [ l ] = C b [ l ] = 1 m m e a n O f E a c h R o w ( d Z [ l ] ) d w [ l ] = C w [ l ] = 1 m d Z [ l ] A [ l 1 ] T db^{[l]}=\frac{\partial C}{\partial b^{[l]}}=\frac{1}{m}meanOfEachRow(dZ^{[l]})\\dw^{[l]}=\frac{\partial C}{\partial w^{[l]}}=\frac{1}{m}dZ^{[l]}A^{[l-1]T} 更新 b [ l ] b^{[l]} 的時候須要對每行求均值; 注意維度匹配; m m 是樣本個數
參數更新 b [ l ] b [ l ] α C b [ l ] b^{[l]}\leftarrow b^{[l]}-\alpha\frac{\partial C}{\partial b^{[l]}} w [ l ] w [ l ] α C w [ l ] w^{[l]}\leftarrow w^{[l]}-\alpha\frac{\partial C}{\partial w^{[l]}} α \alpha 是學習率
3.5.3 關於超參數

  經過前面的介紹,相信讀者能夠發現BP神經網絡模型有一些參數是須要設計者給出的,也有一些參數是模型本身求解的。

  那麼,哪些參數是須要模型設計者肯定的呢?

  好比,學習率 α \alpha ,隱含層的層數,每一個隱含層的神經元個數,激活函數的選取,損失函數(代價函數)的選取等等,這些參數被稱之爲超參數

  其它的參數,好比權重矩陣 w w 和偏置係數 b b 在肯定了超參數以後是能夠經過模型的計算來獲得的,這些參數稱之爲普通參數,簡稱參數

  超參數的肯定實際上是很困難的。由於你很難知道什麼樣的超參數會讓模型表現得更好。好比,學習率過小可能形成模型收斂速度過慢,學習率太大又可能形成模型不收斂;再好比,損失函數的設計,若是損失函數設計很差的話,可能會形成模型沒法收斂;再好比,層數過多的時候,如何設計網絡結構以免梯度消失和梯度爆炸……

  神經網絡的程序比通常程序的調試難度大得多,由於它並不會顯式報錯,它只是沒法獲得你指望的結果,做爲新手也很難肯定到底哪裏出了問題(對於本身設計的網絡,這種現象尤甚,我目前也基本是新手,因此這些問題也在困擾着我)。固然,使用別人訓練好的模型來微調看起來是一個捷徑……

  總之,神經網絡至少在目前來看感受仍是黑箱的成分居多,但願經過你們的努力慢慢探索吧。

4. 是否是貓?

  本小節主要使用上述公式來完成一個小例子,這個小小的神經網絡能夠告訴咱們一張圖片是否是貓。本例程參考了coursera的做業,有改動。

  在實現代碼以前,先把用到的公式列一個表格吧,這樣對照着看你們更清晰一點(若是你沒有2個顯示器建議先把這些公式抄寫到紙上,以便和代碼對照):

編號 公式 備註
1 Z [ l ] = w [ l ] A [ l 1 ] + b [ l ] Z^{[l]}=w^{[l]}A^{[l-1]}+b^{[l]}
2 A [ l ] = σ ( Z [ l ] ) A^{[l]}=\sigma(Z^{[l]})
3 d Z [ L ] = A C σ ( Z [ L ] ) dZ^{[L]}=\nabla_AC\odot\sigma^{'}(Z^{[L]})
4 d Z [ l ] = [ w [ l + 1 ] T d Z [ l + 1 ] ] σ ( Z [ l ] ) dZ^{[l]}=[w^{[l+1]T}dZ^{[l+1]}]\odot \sigma ^{'}(Z^{[l]})
5 d b [ l ] = C b [ l ] = 1 m m e a n O f E a c h R o w ( d Z [ l ] ) db^{[l]}=\frac{\partial C}{\partial b^{[l]}}=\frac{1}{m}meanOfEachRow(dZ^{[l]})
6 d w [ l ] = C w [ l ] = 1 m d Z [ l ] A [ l 1 ] T dw^{[l]}=\frac{\partial C}{\partial w^{[l]}}=\frac{1}{m}dZ^{[l]}A^{[l-1]T}
7 b [ l ] b [ l ] α d b [ l ] b^{[l]}\leftarrow b^{[l]}-\alpha \cdot db^{[l]}
8 w [ l ] w [ l ] α d w [ l ] w^{[l]}\leftarrow w^{[l]}-\alpha\cdot dw^{[l]}
9 d A [ l ] = w [ l ] T d Z [ l ] dA^{[l]}=w^{[l]T}\odot dZ^{[l]}

  準備工做作的差很少了,讓咱們開始吧?等等,好像咱們尚未定義代價函數是什麼?OMG!好吧,看來咱們得先把這個作好再繼續了。

  那先看結果吧,咱們的代價函數是:
C = 1 m i = 1 m ( y ( i ) l o g ( a [ L ] ( i ) ) + ( 1 y ( i ) ) l o g ( 1 a [ L ] ( i ) ) ) C =-\frac{1}{m} \sum^{m}_{i=1}(y^{(i)}log(a^{[L](i)})+(1-y^{(i)})log(1-a^{[L](i)}))
  其中, m m 是樣本數量;

  下面簡單介紹一下這個代價函數是怎麼來的(做者非數學專業,不嚴謹的地方望海涵)。
.
  代價函數的肯定用到了統計學中的**「極大似然法」**,既然這樣,那就不可避免地要介紹一下「極大似然法」了。極大似然法簡單來講就是「在模型已定,參數未知的狀況下,根據結果估計模型中參數的一種方法",換句話說,極大似然法提供了一種給定觀察數據來評估模型參數的方法。

  舉個例子(本例參考了知乎相關回答),一個不透明的罐子裏有黑白兩種球(球僅僅顏色不一樣,大小重量等參數都同樣)。有放回地隨機拿出一個小球,記錄顏色。重複10次以後發現7次是黑球,3次是白球。問你罐子裏白球的比例?

  相信不少人能夠一口回答「30%」,那麼,爲何呢?背後的原理是什麼呢?

  這裏咱們把每次取出一個球叫作一次抽樣,把「抽樣10次,7次黑球,3次白球」這個事件發生的機率記爲 P ( M o d e l ) P(事件結果|Model) ,咱們的Model須要一個參數 p p 表示白球的比例。那麼 P ( M o d e l ) = p 3 ( 1 p ) 7 P(事件結果|Model)=p^3(1-p)^7

  好了,如今咱們已經有事件結果的機率公式了,接下來求解模型參數 p p ,根據極大似然法的思想,既然這個事件發生了,那麼爲何不讓這個事件(抽樣10次,7次黑球,3次白球)發生的機率最大呢?由於顯然機率大的事件發生纔是合理的。因而就變成了求解 p 3 ( 1 p ) 7 p^3(1-p)^7 取最大值的 p p ,即導數爲0,通過求導:
d ( p 3 ( 1 p ) 7 ) = 3 p 2 ( 1 p ) 7 7 p 3 ( 1 p ) 6 = p 2 ( 1 p ) 6 ( 3 10 p ) = 0 d(p^3(1-p)^7)=3p^2(1-p)^7-7p^3(1-p)^6=p^2(1-p)^6(3-10p)=0
  求解可得 p = 0.3 p=0.3

  極大似然法有一個重要的假設:

假設全部樣本獨立同分布!!!

  好了,如今來看看咱們的神經網絡模型。

  最後一層咱們用sigmoid函數求出一個激活輸出a,若是a大於0.5,就表示這個圖片是貓( y = 1 y=1 ),不然就不是貓( y = 0 y=0 )。所以:
P ( y = 1 x ; θ ) = a P(y=1|x;\theta)=a
P ( y = 0 x ; θ ) = 1 a P(y=0|x;\theta)=1-a

公式解釋:
上述第一個公式表示,給定模型參數 θ \theta 和輸入 x x ,是貓的機率是 P ( y = 1 x ; θ ) = a P(y=1|x;\theta)=a

  把兩個公式合併成一個公式,即
p ( y x ; θ ) = a y ( 1 a ) ( 1 y ) p(y|x;\theta)=a^y(1-a)^{(1-y)}

這裏的 θ \theta 指的就是咱們神經網絡的權值參數和偏置參數。

  那麼似然函數
L ( θ ) = p ( Y X ; θ ) = i = 1 m p ( y ( i ) x ( i ) ; θ ) = i = 1 m ( a [ L ] ( i ) ) y ( i ) ( 1 a [ L ] ( i ) ) ( 1 y ( i ) ) L(\theta)=p(Y|X;\theta)=\prod^m_{i=1}p(y^{(i)}|x^{(i)};\theta)=\prod^m_{i=1}(a^{[L](i)})^{y^{(i)}}(1-a^{[L](i)})^{(1-y^{(i)})}
  變成對數形式:
l o g ( L ( θ ) ) = i = 1 m ( y ( i ) l o g ( a [ L ] ( i ) ) + ( 1 y ( i ) ) l o g ( 1 a [ L ] ( i ) ) ) log(L(\theta))=\sum^m_{i=1}(y^{(i)}log(a^{[L](i)})+(1-y^{(i)})log(1-a^{[L](i)}))
  因此咱們的目標就是最大化這個對數似然函數,也就是最小化咱們的代價函數:
C = 1 m i = 1 m ( y ( i ) l o g ( a [ L ] ( i ) ) + ( 1 y ( i ) ) l o g ( 1 a [ L ] ( i ) ) ) C =-\frac{1}{m} \sum^{m}_{i=1}(y^{(i)}log(a^{[L](i)})+(1-y^{(i)})log(1-a^{[L](i)}))
  其中, m m 是樣本數量;

  好了,終於能夠開始寫代碼了,碼字手都有點酸了,不得不說公式真的好難打。

因爲代碼比較簡單就沒有上傳github。本文代碼和數據文件能夠在這裏下載: https://pan.baidu.com/s/1q_PzaCSXOhRLOJVF5-vy2Q,密碼: d7vx

其餘下載源:
https://drive.google.com/file/d/0B6exrzrSxlh3TmhSV0ZNeHhYUmM/view?usp=sharing

4.1 輔助函數

  輔助函數主要包括激活函數以及激活函數的反向傳播過程函數:
其中,激活函數反向傳播代碼對應公式4和9.

def sigmoid(z):
    """ 使用numpy實現sigmoid函數 參數: Z numpy array 輸出: A 激活值(維數和Z徹底相同) """
    return 1/(1 + np.exp(-z))

def relu(z):
    """ 線性修正函數relu 參數: z numpy array 輸出: A 激活值(維數和Z徹底相同) """
    return np.array(z>0)*z

def sigmoidBackward(dA, cacheA):
    """ sigmoid的反向傳播 參數: dA 同層激活值 cacheA 同層線性輸出 輸出: dZ 梯度 """
    s = sigmoid(cacheA)
    diff = s*(1 - s)
    dZ = dA * diff
    return dZ

def reluBackward(dA, cacheA):
    """ relu的反向傳播 參數: dA 同層激活值 cacheA 同層線性輸出 輸出: dZ 梯度 """
    Z = cacheA
    dZ = np.array(dA, copy=True) 
    dZ[Z <= 0] = 0
    return dZ

  另一個重要的輔助函數是數據讀取函數和參數初始化函數:

def loadData(dataDir):
    """ 導入數據 參數: dataDir 數據集路徑 輸出: 訓練集,測試集以及標籤 """
    train_dataset = h5py.File(dataDir+'/train.h5', "r")
    train_set_x_orig = np.array(train_dataset["train_set_x"][:]) # your train set features
    train_set_y_orig = np.array(train_dataset["train_set_y"][:]) # your train set labels

    test_dataset = h5py.File(dataDir+'/test.h5', "r")
    test_set_x_orig = np.array(test_dataset["test_set_x"][:]) # your test set features
    test_set_y_orig = np.array(test_dataset["test_set_y"][:]) # your test set labels

    classes = np.array(test_dataset["list_classes"][:]) # the list of classes
    
    train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0]))
    test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0]))
    
    return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes

def iniPara(laydims):
    """ 隨機初始化網絡參數 參數: laydims 一個python list 輸出: parameters 隨機初始化的參數字典(」W1「,」b1「,」W2「,」b2「, ...) """
    np.random.seed(1)
    parameters = {}
    for i in range(1, len(laydims)):
        parameters['W'+str(i)] = np.random.randn(laydims[i], laydims[i-1])/ np.sqrt(laydims[i-1])
        parameters['b'+str(i)] = np.zeros((laydims[i], 1))
    return parameters

4.2 前向傳播過程

對應公式1和2.

def forwardLinear(W, b, A_prev):
    """ 前向傳播 """
    Z = np.dot(W, A_prev) + b
    cache = (W, A_prev, b)
    return Z, cache

def forwardLinearActivation(W, b, A_prev, activation):
    """ 帶激活函數的前向傳播 """
    Z, cacheL = forwardLinear(W, b, A_prev)
    cacheA = Z
    if activation == 'sigmoid':
        A = sigmoid(Z)
    if activation == 'relu':
        A = relu(Z)
    cache = (cacheL, cacheA)
    return A, cache

def forwardModel(X, parameters):
    """ 完整的前向傳播過程 """
    layerdim = len(parameters)//2
    caches = []
    A_prev = X
    for i in range(1, layerdim):
        A_prev, cache = forwardLinearActivation(parameters['W'+str(i)], parameters['b'+str(i)], A_prev, 'relu')
        caches.append(cache)
        
    AL, cache = forwardLinearActivation(parameters['W'+str(layerdim)], parameters['b'+str(layerdim)], A_prev, 'sigmoid')
    caches.append(cache)
    
    return AL, caches

4.3 反向傳播過程

線性部分反向傳播對應公式5和6。

def linearBackward(dZ, cache):
    """ 線性部分的反向傳播 參數: dZ 當前層偏差 cache (W, A_prev, b)元組 輸出: dA_prev 上一層激活的梯度 dW 當前層W的梯度 db 當前層b的梯度 """
    W, A_prev, b = cache
    m = A_prev.shape[1]
    
    dW = 1/m*np.dot(dZ, A_prev.T)
    db = 1/m*np.sum(dZ, axis = 1, keepdims=True)
    dA_prev = np.dot(W.T, dZ)
    
    return dA_prev, dW, db

非線性部分對應公式三、四、5和6 。

def linearActivationBackward(dA, cache, activation):
    """ 非線性部分的反向傳播 參數: dA 當前層激活輸出的梯度 cache (W, A_prev, b)元組 activation 激活函數類型 輸出: dA_prev 上一層激活的梯度 dW 當前層W的梯度 db 當前層b的梯度 """
    cacheL, cacheA = cache
    
    if activation == 'relu':
        dZ = reluBackward(dA, cacheA)
        dA_prev, dW, db = linearBackward(dZ, cacheL)
    elif activation == 'sigmoid':
        dZ = sigmoidBackward(dA, cacheA)
        dA_prev, dW, db = linearBackward(dZ, cacheL)
    
    return dA_prev, dW, db

完整反向傳播模型:

def backwardModel(AL, Y, caches):
    """ 完整的反向傳播過程 參數: AL 輸出層結果 Y 標籤值 caches 【cacheL, cacheA】 輸出: diffs 梯度字典 """
    layerdim = len(caches)
    Y = Y.reshape(AL.shape)
    L = layerdim
    
    diffs = {}
    
    dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))
    
    currentCache = caches[L-1]
    dA_prev, dW, db =  linearActivationBackward(dAL, currentCache, 'sigmoid')
    diffs['dA' + str(L)], diffs['dW'+str(L)], diffs['db'+str(L)] = dA_prev, dW, db
    
    for l in reversed(range(L-1)):
        currentCache = caches[l]
        dA_prev, dW, db =  linearActivationBackward(dA_prev, currentCache, 'relu')
        diffs['dA' + str(l+1)], diffs['dW'+str(l+1)], diffs['db'+str(l+1)] = dA_prev, dW, db
        
    return diffs

4.4 測試結果

  打開你的jupyter notebook,運行咱們的BP.ipynb文件,首先導入依賴庫和數據集,而後使用一個循環來肯定最佳的迭代次數大約爲2000:

在這裏插入圖片描述
【圖6】

  最後用一個例子來看一下模型的效果——判斷一張圖片是否是貓:

在這裏插入圖片描述
【圖7】

好了,測試到此結束。你也能夠本身嘗試其它的神經網絡結構和測試其它圖片。

5. 本文小結

  本文主要敘述了經典的全鏈接神經網絡結構以及前向傳播和反向傳播的過程。經過本文的學習,讀者應該能夠獨立推導全鏈接神經網絡的傳播過程,對算法的細節爛熟於心。另外,因爲本文裏的公式大部分是我本身推導的,瑕疵之處,但願讀者不吝賜教。

  雖然這篇文章實現的例子並無什麼實際應用場景,可是本身推導一下這些數學公式並用代碼實現對理解神經網絡內部的原理頗有幫助,繼這篇博客以後,我還計劃寫一個如何本身推導並實現卷積神經網絡的教程,若是有人感興趣,請繼續關注我!

  本次內容就到這裏,謝謝你們。

訂正與答疑:

前向傳播過程比較簡單,我就再也不贅述了。

這裏主要針對反向傳播過程當中可能會出現的問題作一個總結:

1. 具體解釋一下公式1裏面的「堆砌」是什麼意思?

δ j [ L ] = k L a k [ L ] a k [ L ] z j [ L ] \delta^{[L]}_j = \sum_k \frac{\partial L}{\partial a^{[L]}_k} \frac{\partial a^{[L]}_k}{\partial z^{[L]}_j}

有讀者對這裏不太理解,這實際上是由於,咱們的輸出層不必定是隻有一個神經元,可能有好多個神經元,所以損失函數是每一個輸出神經元「偏差」之和,所以纔會出現這種 \sum 的形式,而後每一個輸出神經元的偏差函數與其它神經元沒有關係,因此只有 k = j k=j 的時候值不是0.

另外,這裏說的「堆砌」指的就是:

δ [ l ] = [ L a 1 [ L ] L a 2 [ L ] ] [ σ ( z 1 [ L ] ) σ ( z 2 [ L ] ) ] \delta^{[l]}=\left[ \begin{array}{cc} \frac{\partial L}{\partial a^{[L]}_1} \\ \frac{\partial L}{\partial a^{[L]}_2} \\\vdots \end{array}\right]\odot\left[\begin{array}{cc}\sigma^{&#x27;}(z^{[L]}_1) \\\sigma^{&#x27;}(z^{[L]}_2)\\\vdots\end{array}\right]

2. 公式2寫成矩陣形式爲何係數矩陣會有轉置?本身沒搞懂。

這裏可能有一點繞,有的讀者感受個人推導不是很明白,因此有必要詳細說明一下。

不少讀者不明白,寫成矩陣形式的時候

δ [ l ] = [ w [ l + 1 ] T δ [ l + 1 ] ] σ ( z [ l ] ) \delta^{[l]}=[w^{[l+1]T}\delta^{[l+1]}]\odot \sigma ^{&#x27;}(z^{[l]})

裏面的「係數矩陣轉置」是怎麼來的。這裏就主要說明一下:

相信你們都已經理解了下面這個前向傳播公式:

z k [ l + 1 ] = j w k j [ l + 1 ] a j [ l ] + b k [ l + 1 ] = j w k j [ l + 1 ] σ ( z j [ l ] ) + b k [ l + 1 ] z^{[l+1]}_k=\sum_jw^{[l+1]}_{kj}a^{[l]}_j+b^{[l+1]}_k=\sum_jw^{[l+1]}_{kj}\sigma(z^{[l]}_j)+b^{[l+1]}_k

求偏導這裏在原文中有一點錯誤,應該是:

z k [ l + 1 ] z j [ l ] = k w k j [ l + 1 ] σ ( z j [ l ] ) \frac{\partial z^{[l+1]}_k}{\partial z^{[l]}_j} =\sum_k w^{[l+1]}_{kj} \sigma&#x27;(z^{[l]}_j)

爲了你們有一個直觀的感覺,來一個具體的例子:

第 1 層的係數矩陣比方是:

w [ 2 = [ w 11 [ 2 ] w 12 [ 2 ] w 13 [ 2 ] w 21 [ 2 ] w 22 [ 2 ] w 23 [ 2 ] ] w^{[2}=\left[ \begin{array}{cc} w_{11}^{[2]} &amp; w_{12}^{[2]} &amp; w_{13}^{[2]} \\ w_{21}^{[2]}&amp; w_{22}^{[2]} &amp; w_{23}^{[2]}\end{array}\right]

b [ 2 ] = [ b 1 [ 2 ] b 2 [ 2 ] b^{[2]}=\left[ \begin{array}{cc}b^{[2]}_1 \\ b^{[2}_2 \end{array}\right]

z [ 2 ] = [ w 11 [ 2 ] w 12 [ 2 ] w 13 [ 2 ] w 21 [ 2 ] w 22 [ 2 ] w 23 [ 2 ] ] [ a 1 [ 1 ] a 2 [ 1 ] a 3 [ 1 ] ] + [ b 1 [ 2 ] b 2 [ 2 ] ] = [ w 11 [ 2 ] a 1 [ 1 ] + w 12 [ 2 ] a 2 [ 1 ] + w 13 [ 2 ] a 3 [ 1 ] + b 1 [ 2 ] w 21 [ 2 ] a 1 [ 1 ] + w 22 [ 2 ] a 2 [ 1 ] + w 23 [ 2 ] a 3 [ 1 ] + b 2 [ 2 ] ] z^{[2]}=\left[ \begin{array}{cc} w_{11}^{[2]} &amp; w_{12}^{[2]} &amp; w_{13}^{[2]} \\ w_{21}^{[2]}&amp; w_{22}^{[2]} &amp; w_{23}^{[2]}\end{array}\right]\cdot \left[ \begin{array}{cc} a^{[1]}_1 \\ a^{[1]}_2 \\ a^{[1]}_3 \end{array}\right] +\left[ \begin{array}{cc}b^{[2]}_1 \\ b^{[2]}_2 \end{array}\right]=\left[ \begin{array}{cc} w_{11}^{[2]}a^{[1]}_1+w_{12}^{[2]}a^{[1]}_2+w_{13}^{[2]}a^{[1]}_3+b^{[2]}_1 \\ w^{[2]}_{21}a^{[1]}_1+w_{22}^{[2]}a^{[1]}_2+w_{23}^{[2]}a^{[1]}_3+b^{[2]}_2\end{array}\right]

那麼,

z 1 [ 2 ] a 1 [ 1 ] = ( w 11 [ 2 ] a 1 [ 1 ] + w 12 [ 2 ] a 2 [ 1 ] + w 13 [ 2 ] a 3 [ 1 ] + b 1 [ 2 ] ) a 1 [ 1 ] = w 11 [ 2 ] \frac{\partial z^{[2]}_1}{\partial a^{[1]}_1}=\frac{\partial(w_{11}^{[2]}a^{[1]}_1+w_{12}^{[2]}a^{[1]}_2+w_{13}^{[2]}a^{[1]}_3+b^{[2]}_1)}{\partial a^{[1]}_1}=w^{[2]}_{11}

那麼,根據以前介紹的求解梯度向量的定義:

z 1 [ 2 ] a [ 1 ] = [ z 1

相關文章
相關標籤/搜索