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

我的CSDN博客地址:紅色石頭的專欄

我的知乎主頁:紅色石頭

我的知乎專欄:紅色石頭的機器學習之路

歡迎大家關注我!共同學習,共同進步!

上節課我們主要介紹了向量化、矩陣計算的方法和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]} 寫成矩陣的形式:

 boldsymbol{a^{[1]}}= left[ begin{matrix} a_1^{[1]}  a_2^{[1]}  a_3^{[1]}  a_4^{[1]} end{matrix} right]

最後,相應的輸出層記為 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]}為:

 boldsymbol{a^{[1]}}= left[ begin{matrix} a_1^{[1]}  a_2^{[1]}  a_3^{[1]}  a_4^{[1]} end{matrix} right]

上述每個節點的計算都對應著一次邏輯運算的過程,分別由計算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^{[2](i)}=sigma(z^{[2](i)})

    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]})=Wx+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<0 1, & zgeq0 end{cases}

對於Leaky ReLU函數:

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

g(z)=begin{cases} 0.01, & z<0 1, & zgeq0 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(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]}都初始化為零,即:

W^{[1]}= left[ begin{matrix} 0 & 0  0 & 0 end{matrix} right]

W^{[2]}= left[ begin{matrix} 0 & 0 end{matrix} right]

這樣使得隱藏層第一個神經元的輸出等於第二個神經元的輸出,即 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.01nb_1 = np.zero((2,1))nW_2 = np.random.randn((1,2))*0.01nb_2 = 0n

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

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

12. Summary

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

我的CSDN博客地址:

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


推薦閱讀:

【神經網路】激活函數面面觀
求推薦人工智慧特別是神經網路方面的書?
人工神經網路——跨學科應用的大殺器
神經網路研究與應用這塊用python好還是matlab?
只有達到 state of the art精度的方法才能發文章嗎?

TAG:机器学习 | 深度学习DeepLearning | 神经网络 |