吳恩達《神經網路與深度學習》課程筆記(4)– 淺層神經網路

红色石头發表於2018-07-30

上節課我們主要介紹了向量化、矩陣計算的方法和python程式設計的相關技巧。並以邏輯迴歸為例,將其演算法流程包括梯度下降轉換為向量化的形式,從而大大提高了程式運算速度。本節課我們將從淺層神經網路入手,開始真正的神經網路模型的學習。

1. Neural Networks Overview

首先,我們從整體結構上來大致看一下神經網路模型。

前面的課程中,我們已經使用計算圖的方式介紹了邏輯迴歸梯度下降演算法的正向傳播和反向傳播兩個過程。如下圖所示。神經網路的結構與邏輯迴歸類似,只是神經網路的層數比邏輯迴歸多一層,多出來的中間那層稱為隱藏層或中間層。這樣從計算上來說,神經網路的正向傳播和反向傳播過程只是比邏輯迴歸多了一次重複的計算。正向傳播過程分成兩層,第一層是輸入層到隱藏層,用上標[1]來表示:

z^{[1]}=W^{[1]}x+b^{[1]}

a^{[1]}=\sigma(z^{[1]})

第二層是隱藏層到輸出層,用上標[2]來表示:

z^{[2]}=W^{[2]}a^{[1]}+b^{[2]}

a^{[2]}=\sigma(z^{[2]})

在寫法上值得注意的是,方括號上標[i]表示當前所處的層數;圓括號上標(i)表示第i個樣本。

同樣,反向傳播過程也分成兩層。第一層是輸出層到隱藏層,第二層是隱藏層到輸入層。其細節部分我們之後再來討論。

2. Neural Network Representation

下面我們以圖示的方式來介紹單隱藏層的神經網路結構。如下圖所示,單隱藏層神經網路就是典型的淺層(shallow)神經網路。

結構上,從左到右,可以分成三層:輸入層(Input layer),隱藏層(Hidden layer)和輸出層(Output layer)。輸入層和輸出層,顧名思義,對應著訓練樣本的輸入和輸出,很好理解。隱藏層是抽象的非線性的中間層,這也是其被命名為隱藏層的原因。

在寫法上,我們通常把輸入矩陣X記為a^{[0]},把隱藏層輸出記為a^{[1]},上標從0開始。用下標表示第幾個神經元,注意下標從1開始。例如a_1^{[1]}表示隱藏層第1個神經元,a_2^{[1]}表示隱藏層第2個神經元,,等等。這樣,隱藏層有4個神經元就可以將其輸出a^{[1]}寫成矩陣的形式:

最後,相應的輸出層記為a^{[2]},即\hat y。這種單隱藏層神經網路也被稱為兩層神經網路(2 layer NN)。之所以叫兩層神經網路是因為,通常我們只會計算隱藏層輸出和輸出層的輸出,輸入層是不用計算的。這也是我們把輸入層層數上標記為0的原因(a^{[0]})。

關於隱藏層對應的權重W^{[1]}和常數項b^{[1]}W^{[1]}的維度是(4,3)。這裡的4對應著隱藏層神經元個數,3對應著輸入層x特徵向量包含元素個數。常數項b^{[1]}的維度是(4,1),這裡的4同樣對應著隱藏層神經元個數。關於輸出層對應的權重W^{[2]}和常數項b^{[2]}W^{[2]}的維度是(1,4),這裡的1對應著輸出層神經元個數,4對應著輸出層神經元個數。常數項b^{[2]}的維度是(1,1),因為輸出只有一個神經元。總結一下,第i層的權重W^{[i]}維度的行等於i層神經元的個數,列等於i-1層神經元的個數;第i層常數項b^{[i]}維度的行等於i層神經元的個數,列始終為1。

3. Computing a Neural Network’s Output

接下來我們開始詳細推導神經網路的計算過程。回顧一下,我們前面講過兩層神經網路可以看成是邏輯迴歸再重複計算一次。如下圖所示,邏輯迴歸的正向計算可以分解成計算z和a的兩部分:

z=w^Tx+b

a=\sigma(z)

對於兩層神經網路,從輸入層到隱藏層對應一次邏輯迴歸運算;從隱藏層到輸出層對應一次邏輯迴歸運算。每層計算時,要注意對應的上標和下標,一般我們記上標方括號表示layer,下標表示第幾個神經元。例如a_i^{[l]}表示第l層的第i個神經元。注意,i從1開始,l從0開始。

下面,我們將從輸入層到輸出層的計算公式列出來:

z_1^{[1]}=w_1^{[1]T}x+b_1^{[1]},\ a_1^{[1]}=\sigma(z_1^{[1]})

z_2^{[1]}=w_2^{[1]T}x+b_2^{[1]},\ a_2^{[1]}=\sigma(z_2^{[1]})

z_3^{[1]}=w_3^{[1]T}x+b_3^{[1]},\ a_3^{[1]}=\sigma(z_3^{[1]})

z_4^{[1]}=w_4^{[1]T}x+b_4^{[1]},\ a_4^{[1]}=\sigma(z_4^{[1]})

然後,從隱藏層到輸出層的計算公式為:

z_1^{[2]}=w_1^{[2]T}a^{[1]}+b_1^{[2]},\ a_1^{[2]}=\sigma(z_1^{[2]})

其中a^{[1]}為:

上述每個節點的計算都對應著一次邏輯運算的過程,分別由計算z和a兩部分組成。

為了提高程式運算速度,我們引入向量化和矩陣運算的思想,將上述表示式轉換成矩陣運算的形式:

z^{[1]}=W^{[1]}x+b^{[1]}

a^{[1]}=\sigma(z^{[1]})

z^{[2]}=W^{[2]}a^{[1]}+b^{[2]}

a^{[2]}=\sigma(z^{[2]})

之前也介紹過,這裡順便提一下,W^{[1]}的維度是(4,3),b^{[1]}的維度是(4,1),W^{[2]}的維度是(1,4),b^{[2]}的維度是(1,1)。這點需要特別注意。

4. Vectorizing across multiple examples

上一部分我們只是介紹了單個樣本的神經網路正向傳播矩陣運算過程。而對於m個訓練樣本,我們也可以使用矩陣相乘的形式來提高計算效率。而且它的形式與上一部分單個樣本的矩陣運算十分相似,比較簡單。

之前我們也介紹過,在書寫標記上用上標(i)表示第i個樣本,例如x^{(i)}z^{(i)}a^{[2](i)}。對於每個樣本i,可以使用for迴圈來求解其正向輸出:

for i = 1 to m:
\ \ \ \ z^{[1](i)}=W^{[1]}x^{(i)}+b^{[1]}
\ \ \ \ a^{[1](i)}=\sigma(z^{[1](i)})
\ \ \ \ z^{[2](i)}=W^{[2]}a^{[1](i)}+b^{[2]}
\ \ \ \ a^{[2](i)}=\sigma(z^{[2](i)})

不使用for迴圈,利用矩陣運算的思想,輸入矩陣X的維度為(n_x,m)。這樣,我們可以把上面的for迴圈寫成矩陣運算的形式:

Z^{[1]}=W^{[1]}X+b^{[1]}

A^{[1]}=\sigma(Z^{[1]})

Z^{[2]}=W^{[2]}A^{[1]}+b^{[2]}

A^{[2]}=\sigma(Z^{[2]})

其中,Z^{[1]}的維度是(4,m),4是隱藏層神經元的個數;A^{[1]}的維度與Z^{[1]}相同;Z^{[2]}A^{[2]}的維度均為(1,m)。對上面這四個矩陣來說,均可以這樣來理解:行表示神經元個數,列表示樣本數目m。

5. Explanation for Vectorized Implementation

這部分Andrew用圖示的方式解釋了m個樣本的神經網路矩陣運算過程。其實內容比較簡單,只要記住上述四個矩陣的行表示神經元個數,列表示樣本數目m就行了。

值得注意的是輸入矩陣X也可以寫成A^{[0]}

6. Activation functions

神經網路隱藏層和輸出層都需要啟用函式(activation function),在之前的課程中我們都預設使用Sigmoid函式\sigma(x)作為啟用函式。其實,還有其它啟用函式可供使用,不同的啟用函式有各自的優點。下面我們就來介紹幾個不同的啟用函式g(x)

  • sigmoid函式

  • tanh函式

  • ReLU函式

  • Leaky ReLU函式

如上圖所示,不同啟用函式形狀不同,a的取值範圍也有差異。

如何選擇合適的啟用函式呢?首先我們來比較sigmoid函式和tanh函式。對於隱藏層的啟用函式,一般來說,tanh函式要比sigmoid函式表現更好一些。因為tanh函式的取值範圍在[-1,+1]之間,隱藏層的輸出被限定在[-1,+1]之間,可以看成是在0值附近分佈,均值為0。這樣從隱藏層到輸出層,資料起到了歸一化(均值為0)的效果。因此,隱藏層的啟用函式,tanh比sigmoid更好一些。而對於輸出層的啟用函式,因為二分類問題的輸出取值為{0,+1},所以一般會選擇sigmoid作為啟用函式。

觀察sigmoid函式和tanh函式,我們發現有這樣一個問題,就是當|z|很大的時候,啟用函式的斜率(梯度)很小。因此,在這個區域內,梯度下降演算法會執行得比較慢。在實際應用中,應儘量避免使z落在這個區域,使|z|儘可能限定在零值附近,從而提高梯度下降演算法運算速度。

為了彌補sigmoid函式和tanh函式的這個缺陷,就出現了ReLU啟用函式。ReLU啟用函式在z大於零時梯度始終為1;在z小於零時梯度始終為0;z等於零時的梯度可以當成1也可以當成0,實際應用中並不影響。對於隱藏層,選擇ReLU作為啟用函式能夠保證z大於零時梯度始終為1,從而提高神經網路梯度下降演算法運算速度。但當z小於零時,存在梯度為0的缺點,實際應用中,這個缺點影響不是很大。為了彌補這個缺點,出現了Leaky ReLU啟用函式,能夠保證z小於零是梯度不為0。

最後總結一下,如果是分類問題,輸出層的啟用函式一般會選擇sigmoid函式。但是隱藏層的啟用函式通常不會選擇sigmoid函式,tanh函式的表現會比sigmoid函式好一些。實際應用中,通常會會選擇使用ReLU或者Leaky ReLU函式,保證梯度下降速度不會太小。其實,具體選擇哪個函式作為啟用函式沒有一個固定的準確的答案,應該要根據具體實際問題進行驗證(validation)。

7. Why do you need non-linear activation functions

我們知道上一部分講的四種啟用函式都是非線性(non-linear)的。那是否可以使用線性啟用函式呢?答案是不行!下面我們就來進行簡要的解釋和說明。

假設所有的啟用函式都是線性的,為了簡化計算,我們直接令啟用函式g(z)=z,即a=z。那麼,淺層神經網路的各層輸出為:

z^{[1]}=W^{[1]}x+b^{[1]}

a^{[1]}=z^{[1]}

z^{[2]}=W^{[2]}a^{[1]}+b^{[2]}

a^{[2]}=z^{[2]}

我們對上式中a^{[2]}進行化簡計算:

a^{[2]}=z^{[2]}=W^{[2]}a^{[1]}+b^{[2]}=W^{[2]}(W^{[1]}x+b^{[1]})+b^{[2]}=(W^{[2]}W^{[1]})x+(W^{[2]}b^{[1]}+b^{[2]})=W’x+b’

經過推導我們發現a^{[2]}仍是輸入變數x的線性組合。這表明,使用神經網路與直接使用線性模型的效果並沒有什麼兩樣。即便是包含多層隱藏層的神經網路,如果使用線性函式作為啟用函式,最終的輸出仍然是輸入x的線性模型。這樣的話神經網路就沒有任何作用了。因此,隱藏層的啟用函式必須要是非線性的。

另外,如果所有的隱藏層全部使用線性啟用函式,只有輸出層使用非線性啟用函式,那麼整個神經網路的結構就類似於一個簡單的邏輯迴歸模型,而失去了神經網路模型本身的優勢和價值。

值得一提的是,如果是預測問題而不是分類問題,輸出y是連續的情況下,輸出層的啟用函式可以使用線性函式。如果輸出y恆為正值,則也可以使用ReLU啟用函式,具體情況,具體分析。

8. Derivatives of activation functions

在梯度下降反向計算過程中少不了計算啟用函式的導數即梯度。

我們先來看一下sigmoid函式的導數:

g(z)=\frac{1}{1+e^{(-z)}}

g'(z)=\frac{d}{dz}g(z)=g(z)(1-g(z))=a(1-a)

對於tanh函式的導數:

g(z)=\frac{e^{(z)}-e^{(-z)}}{e^{(z)}+e^{(-z)}}

g'(z)=\frac{d}{dz}g(z)=1-(g(z))^2=1-a^2

對於ReLU函式的導數:

g(z)=max(0,z)

g'(z)=\begin{cases} 0, & z<\ 1, & z\geq0 \end{cases}

對於Leaky ReLU函式:

g(z)=max(0.01z,z)

g'(z)=\begin{cases} 0.01, & z<\ 1, & z\geq0 \end{cases}

9. Gradient descent for neural networks

接下來看一下在神經網路中如何進行梯度計算。

仍然是淺層神經網路,包含的引數為W^{[1]}b^{[1]}W^{[2]}b^{[2]}。令輸入層的特徵向量個數n_x=n^{[0]},隱藏層神經元個數為n^{[1]},輸出層神經元個數為n^{[2]}=1。則W^{[1]}的維度為(n^{[1]},n^{[0]}),b^{[1]}的維度為(n^{[1]},1),W^{[2]}的維度為(n^{[2]},n^{[1]}),b^{[2]}的維度為(n^{[2]},1)。

該神經網路正向傳播過程為:

Z^{[1]}=W^{[1]}X+b^{[1]}

A^{[1]}=g(Z^{[1]})

Z^{[2]}=W^{[2]}A^{[1]}+b^{[2]}

A^{[2]}=g(Z^{[2]})

其中,g(\cdot)表示啟用函式。

反向傳播是計算導數(梯度)的過程,這裡先列出來Cost function對各個引數的梯度:

dZ^{[2]}=A^{[2]}-Y

dW^{[2]}=\frac1mdZ^{[2]}A^{[1]T}

db^{[2]}=\frac1mnp.sum(dZ^{[2]},axis=1,keepdim=True)

dZ^{[1]}=W^{[2]T}dZ^{[2]}\ast g'(Z^{[1]})

dW^{[1]}=\frac1mdZ^{[1]}X^T

db^{[1]}=\frac1mnp.sum(dZ^{[1]},axis=1,keepdim=True)

反向傳播的具體推導過程我們下一部分再進行詳細說明。

10. Backpropagation intuition(optional)

我們仍然使用計算圖的方式來推導神經網路反向傳播過程。記得之前介紹邏輯迴歸時,我們就引入了計算圖來推導正向傳播和反向傳播,其過程如下圖所示:

由於多了一個隱藏層,神經網路的計算圖要比邏輯迴歸的複雜一些,如下圖所示。對於單個訓練樣本,正向過程很容易,反向過程可以根據梯度計算方法逐一推導。

dz^{[2]}=a^{[2]}-y

dW^{[2]}=dz^{[2]}\cdot \frac{\partial z^{[2]}}{\partial W^{[2]}}=dz^{[2]}a^{[1]T}

db^{[2]}=dz^{[2]}\cdot \frac{\partial z^{[2]}}{\partial b^{[2]}}=dz^{[2]}\cdot 1=dz^{[2]}

dz^{[1]}=dz^{[2]}\cdot \frac{\partial z^{[2]}}{\partial a^{[1]}}\cdot \frac{\partial a^{[1]}}{\partial z^{[1]}}=W^{[2]T}dz^{[2]}\ast g^{[1]’}(z^{[1]})

dW^{[1]}=dz^{[1]}\cdot \frac{\partial z^{[1]}}{\partial W^{[1]}}=dz^{[1]}x^T

db^{[1]}=dz^{[1]}\cdot \frac{\partial z^{[1]}}{\partial b^{[1]}}=dz^{[1]}\cdot 1=dz^{[1]}

總結一下,淺層神經網路(包含一個隱藏層),m個訓練樣本的正向傳播過程和反向傳播過程分別包含了6個表示式,其向量化矩陣形式如下圖所示:

11. Random Initialization

神經網路模型中的引數權重W是不能全部初始化為零的,接下來我們分析一下原因。

舉個簡單的例子,一個淺層神經網路包含兩個輸入,隱藏層包含兩個神經元。如果權重W^{[1]}W^{[2]}都初始化為零,即:

這樣使得隱藏層第一個神經元的輸出等於第二個神經元的輸出,即a_1^{[1]}=a_2^{[1]}。經過推導得到dz_1^{[1]}=dz_2^{[1]},以及dW_1^{[1]}=dW_2^{[1]}。因此,這樣的結果是隱藏層兩個神經元對應的權重行向量W_1^{[1]}W_2^{[1]}每次迭代更新都會得到完全相同的結果,W_1^{[1]}始終等於W_2^{[1]},完全對稱。這樣隱藏層設定多個神經元就沒有任何意義了。值得一提的是,引數b可以全部初始化為零,並不會影響神經網路訓練效果。

我們把這種權重W全部初始化為零帶來的問題稱為symmetry breaking problem。解決方法也很簡單,就是將W進行隨機初始化(b可初始化為零)。python裡可以使用如下語句進行W和b的初始化:

W_1 = np.random.randn((2,2))*0.01
b_1 = np.zero((2,1))
W_2 = np.random.randn((1,2))*0.01
b_2 = 0

這裡我們將W_1^{[1]}W_2^{[1]}乘以0.01的目的是儘量使得權重W初始化比較小的值。之所以讓W比較小,是因為如果使用sigmoid函式或者tanh函式作為啟用函式的話,W比較小,得到的|z|也比較小(靠近零點),而零點區域的梯度比較大,這樣能大大提高梯度下降演算法的更新速度,儘快找到全域性最優解。如果W較大,得到的|z|也比較大,附近曲線平緩,梯度較小,訓練過程會慢很多。

當然,如果啟用函式是ReLU或者Leaky ReLU函式,則不需要考慮這個問題。但是,如果輸出層是sigmoid函式,則對應的權重W最好初始化到比較小的值。

12. Summary

本節課主要介紹了淺層神經網路。首先,我們簡單概述了神經網路的結構:包括輸入層,隱藏層和輸出層。然後,我們以計算圖的方式推導了神經網路的正向輸出,並以向量化的形式歸納出來。接著,介紹了不同的啟用函式並做了比較,實際應用中根據不同需要選擇合適的啟用函式。啟用函式必須是非線性的,不然神經網路模型起不了任何作用。然後,我們重點介紹了神經網路的反向傳播過程以及各個引數的導數推導,並以矩陣形式表示出來。最後,介紹了權重隨機初始化的重要性,必須對權重W進行隨機初始化操作。

更多AI資源請關注公眾號:AI有道(ID:redstonewill)

相關文章