Lesson 1 Neural Network and Deep Learning
這篇文章其實是 Coursera 上吳恩達老師的深度學習專業課程的第一門課程的課程筆記。
參考了其他人的筆記繼續歸納的。
邏輯回歸 (Logistic Regression)
邏輯回歸的定義
神經網絡的訓練過程可以分為前向傳播(forward propagation) 和反向傳播 (backward propagation) 的 過程。我們通過邏輯回歸的例子進行說明。
邏輯回歸是一個用於二分類 (binary clasification) 的算法。比如說,我們有一張圖片作為輸入,比如下圖中的貓,如果識別這張圖片為貓,則輸出標簽1作為結果;如果識別出不是貓,那么輸出標簽0作為結果。而我們把輸出結果用 \(y\) 表示就如下圖所示。
圖片在計算機中保存的話,我們需要保存三個矩陣,它們分別對應圖片中的紅、綠、藍三種顏色通道。如果圖片是 \(64\times64\) 像素的,那么這三個矩陣的大小都是 \(64\times64\)。
為了把這張圖片的像素值轉換為特征向量 \(x\),我們需要把三個矩陣展開為一個向量,而這個向量的總維度用 \(n_x\) 表示的話,就是 \(n_x=3\times64\times64=12,288\)。
符號定義:
\(x\):表示一個 \(n_x\) 維數據,為輸入數據,維度為 \((n_x,1)\);
\(y\):表示輸出結果,取值為 \((0,1)\);
\((x^{(i)},y^{(i)})\):表示第 \(i\) 組數據,可能是訓練數據,也可能是測試數據;
\(X=[x^{(1)},x^{(2)},\dots,x^{(m)}]\):表示所有的訓練數據集的輸入值,放在一個 \(n_x\times m\) 的矩陣中,每一列為一組數據,其中 \(m\) 表示樣本數目;
\(Y=[y^{(1)},y^{(2)},\dots,y^{(m)}]\):對應表示所有訓練數據集的輸出值,維度為 \(1\times m\)。
其實對於邏輯回歸來說,我們想要的輸出結果是預測,稱為 \(\hat{y}\),也就是對實際值 \(y\) 的估計。\(\hat{y}\) 表示 \(y=1\)
的一種可能性,用上面的例子來說,就是讓 \(\hat{y}\) 告訴我們這是一只貓的圖片的幾率有多大。
邏輯回歸使用的參數有兩個: \(w\) (表示特征權重,維度與特征向量相同)和 \(b\) (表示偏差)。這時我們不能使用 \(\hat{y}=w^Tx+b\) 進行預測,這實際是一個線性回歸,而我們需要讓 \(\hat{y}\) 在 0 到 1 之間來表示對結果的預測。因此,我們使用 sigmoid 函數,這個非線性函數,即 \(\hat{y}=\sigma(w^Tx+b)\)。
sigmoid 函數
函數的定義為:$ f(x) = \frac{1}{1 + e^{-x}} $,其值域為 $ (0,1) $。
函數圖像如下所示
邏輯回歸的代價函數
為了訓練邏輯回歸模型的參數,我們需要一個代價函數 (cost function),有時也翻譯為成本函數。我們通過訓練代價函數來得到我們需要的參數 \(w\) 和 \(b\)。
損失函數 (loss function),又稱為誤差函數,用來衡量算法的運行情況。一般定義為:\(L(\hat{y},y)\)。
通過損失函數,我們可以衡量預測輸出值和實際值有多接近。一般我們用預測值和實際值的平方差或者它們平方差的一半,但是通常在邏輯回歸中不這么做。因為學習邏輯回歸參數的時候,我們的優化目標不是凸優化,只能找到多個局部最優值,梯度下降法很可能找不到全局最優值。
所以,在邏輯回歸中使用的損失函數是
當 \(y=1\) 時,損失函數 \(L=-log(\hat{y})\),如果想要損失函數盡可能的小,那么 \(\hat{y}\) 就要盡可能大,因為 sigmoid 函數值域是 \((0,1)\),所以 \(\hat{y}\) 會無限接近 1。
當 \(y=0\) 時,損失函數 \(L=-log(1-\hat{y})\),如果想要損失函數盡可能的小,那么 \(\hat{y}\) 就要盡可能小,因為 sigmoid 函數值域是 \((0,1)\),所以 \(\hat{y}\) 會無限接近 0。
當然,損失函數只是對於單個訓練樣本定義的,它衡量的是算法在單個訓練樣本中表現如何。為了衡量算法在全部訓練樣本上的表現如何,我們需要定義一個算法的代價函數,也就是對 \(m\) 個樣本的損失函數求均值:
所以在訓練邏輯回歸模型時候,我們需要找到合適的參數來讓代價函數的總代價降到最低。邏輯回歸可以看作一個非常小的神經網絡。
梯度下降法 (Gradient Descent)
梯度下降法可以在測試集上,通過最小化代價函數 \(J(w,b)\) 來訓練參數 \(w\) 和 \(b\)。
形象化地來表示梯度下降法如下圖所示。
在上圖中,橫軸表示參數 \(w\) 和 \(b\),在實際操作中,\(w\) 可以是更高維度的。這里僅為了繪圖需要,定義 \(w\) 和 \(b\) 為單一實數,代價函數就是圖中的曲面,因此曲面的高度就是代價函數 \(J(w,b)\) 在某一點的函數值。
如上圖,代價函數是一個凸函數 (convex function)。
而上圖,就不太一樣了。它是非凸的,而且有很多個不同的局部最小值。由於邏輯回歸的代價函數的特性,我們必須定義代價函數 \(J(w,b)\) 為凸函數。初始化參數 \(w\) 和 \(b\) 可以采用隨機初始化的方法,對於邏輯回歸幾乎所有的初始化方法都有效,因為函數是凸函數,無論在哪里初始化,應該達到同一點或大致相同的點。
比如說下圖,從最開始的小紅點開始初始化,朝最陡的下坡方向走,不斷地迭代,直到走到全局最優解或者接近全局最優解的地方。
細節化說明梯度下降法
假定代價函數 \(J(w)\) 只有一個參數 \(w\),即用一維曲線代替多維曲線。如下圖所示。
迭代就是不斷地重復下圖的公式:
其中,
\(:=\) 表示更新參數,
\(\alpha\) 表示學習率 (learning rate),用來控制步長 (step),即向下走一步的長度 \(\frac{dJ(w)}{dw}\) 就是函數 \(J(w)\) 對 \(w\) 求導 (derivative),在代碼中我們會使用 \(dw\) 表示這個結果。
對於導數更加形象化的理解就是斜率 (slope),如圖該點的導數就是這個點相切於 \(J(w)\) 的小三角形的高除寬。假設我們以如圖點為初始化點,該點處的斜率的符號是正的,即 \(\frac{dJ(w)}{dw}>0\),所以接下來會向左走一步。整個梯度下降法的迭代過程就是不斷地向左走,直至逼近最小值點。
那么現在把代價函數的參數重新設定為兩個,\(w\) 和 \(b\)。迭代的公式則為:
其中,\(\partial\) 表示求偏導符號,\(\frac{\partial{J(w,b)}}{\partial {w}}\) 就是函數 \(J(w,b)\) 對 \(w\) 求偏導。
邏輯回歸中的梯度下降
假設樣本只有兩個特征 \(x_1\) 和 \(x_2\),為了計算 \(z\),我們需要輸入參數 \(w_1\)、\(w2\) 和 \(b\),除此之外還有特征值 \(x_1\) 和 \(x_2\)。因此 \(z\) 的計算公式為:
回想一下邏輯回歸的公式:
其中 \(z=w^Tx+b\), \(\sigma(z)=\frac{1}{1+e^{-z}}\)。
損失函數:
代價函數:
現在先只考慮單個樣本的情況,單個樣本的代價函數(也就是損失函數)為:
其中,\(a\) 是邏輯回歸的輸出,\(y\) 是樣本的標簽值。
前面我們已經說了如何在單個訓練樣本上計算代價函數的前向步驟,現在我們來通過反向計算出導數。
而對於參數 \(w\) 和 \(b\) 來說:
而對於我們剛剛說的單樣本情況:
所以,總得來說,單樣本的梯度下降算法更新一次步驟如下:
- 計算 \(dz\);
- 計算 \(dw_1,dw_2,db\);
- 更新 \(w_1,w_2,b\)
擴展到 m 個樣本的梯度下降是類似的。給相應的值,添加上標 \(^{(i)}\) 就行了。
偽代碼如下圖所示。
向量化 (Vectorization)
向量化是非常基礎的去除代碼中 for 循環的藝術。for 循環非常沒有效率而且也沒美觀。
向量化后的公式如下:
前向傳播
后向傳播
當然,我們希望多次迭代進行梯度下降,仍然還會需要使用到 for 循環。
淺層神經網絡 (Shallow Neural Network)
神經網絡看起來是下圖這個樣子。我們可以把許多個 sigmoid 單元堆疊起來形成一個神經網絡。
在這個神經網絡對應的 3 個節點,首先計算第一層網絡中的各個節點相關的數 \(z^{[1]}\),接着計算 \(a^{[1]}\)。同理再計算下一層的網絡。注意這里,我們使用了上標 \(^{[m]}\) 表示第 m 層網絡中節點相關的數,這些節點的集合被稱為第 m 層網絡。
整個計算過程,公式如下:
此時 \(a^{[2]}\) 就是整個神經網絡最終的輸出,用 \(\hat{y}\) 表示網絡的輸出。
與邏輯回歸類似,神經網絡我們也需要反向計算。
神經網絡的表示
以下圖的神經網絡例子來說明一下。
其中輸入特征 \(x_1,x_2,x_3\),被稱為神經網絡的輸入層 (input layer);接着第二層的四個節點,我們稱之為隱藏層 (hide layer);最后只有一個結點構成的層被稱為輸出層 (output layer),它負責產生預測值。在論文里,也有人把這個神經網絡稱為一個兩層的神經網絡,因為輸入層不算作一個標准的層。
引入符號標記
在這里,我們用符號 \(a^{[0]}\) 表示輸入特征,從而替代了向量 \(x\)。\(a\) 表示激活的意思,它以為着網絡中不同層的值會傳遞到它們后面的層中,輸入層將 \(x\) 傳遞給隱藏層,所以我們將輸入層的激活值稱為 \(a^{[0]}\)。同理,下一層即隱藏層也會產生激活值,我們記為 \(a^{[1]}\)。它們是向量,具體地說,隱藏層的第一個單元我們將表示為 \(a^{[1]}_1\),以此類推。
隱藏層以及最后的輸出層是帶有參數的,這里的隱藏層將有兩個參數 \(W\) 和 \(b\)。因為隱藏層算第一層,所以我們給它們加上上標 \(^{[1]}\),即 \((W^{[1]},b^{[1]})\)。在這個例子里,參數 \(W\) 是一個 \(4\times3\) 的矩陣,而參數 \(b\) 是一個 \(4\times1\) 的向量。其中,4 源自於隱藏層有 4 個節點(隱藏層單元),3 源自於輸入層有 3 個輸入特征。相似地,輸出層也有參數 \(W^{[2]}\) 和 \(b^{[2]}\),它們的維數分別是 \(1\times4\) 和 \(1\times1\)。
神經網絡的計算
依舊以上面的兩層的神經網絡為例。我們從隱藏層的第一個神經元開始計算,與邏輯回歸相似,這個神經元的計算同樣也分為兩步:
第一步,計算 \(z_1^{[1]}\),\(z_1^{[1]}=w_1^{[1]T}x+b_1^{[1]}\);
第二步,通過激活函數計算 \(a_1^{[1]}\),\(a_1^{[1]}=\sigma(z_1^{[1]})\)。
隱藏層的余下幾個神經元的計算過程一樣,只是符號表示不同,最終分別可以得到 \(a_2^{[1]},a_3^{[1]},a_4^{[1]}\)。
向量化計算
轉換成向量化之后,公式如下
具體到第一層隱藏層的計算則如下
也就是對於我們簡單的兩層神經網絡來說,只需要四個公式就能計算完
而對於 m 個樣本來說,只需要對每個樣本計算這個四個公式就行了,使用 for 循環就能實現。當然,可以向量化的話,我們還是要使用向量化。
按列把變量都拼成矩陣,類似如下
那么計算就可以變形為如下所示
其中上標 \(^{(i)}\) 代表的是第 i 個樣本。
激活函數 (Activation functions)
使用一個神經網絡時,需要決定使用哪種激活函數用在隱藏層上,哪種用在輸出節點上。之前,我們都一直在用 sigmoid 函數,但是,有時其他的激活函數效果會更好。
常用的激活函數
(1)sigmoid 激活函數
函數的定義為:$ f(x) = \frac{1}{1 + e^{-x}} $,其值域為 $ (0,1) $。
函數圖像如下:
(2)tanh 激活函數
函數的定義為:$ f(x) = tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}} $,值域為 $ (-1,1) $。
函數圖像如下:
(3)Relu 激活函數
函數的定義為:$ f(x) = max(0, x) $ ,值域為 $ [0,+∞) $;
函數圖像如下:
(4)Leak Relu 激活函數
函數定義為:
,值域為 $ (-∞,+∞) $。
圖像如下($ a = 0.5 $):
選擇激活函數的經驗法則
如果輸出是 0、1 值(二分類問題),則輸出層選擇 sigmoid 函數,然后其它的所有單元都選擇 Relu 函數。
這是很多激活函數的默認選擇,如果在隱藏層上不確定使用哪個激活函數,那么通常會使用 Relu 函數。
有時,也會使用 tanh 函數,但 Relu 的一個優點是:當 \(z\) 值為負時,導數等於 0。在 \(z\) 的區間變動很大的情況下,激活函數的導數或者激活函數的斜率都會遠大於 0,在實踐中,使用 Relu 激活函數神經網絡通常會比使用 sigmoid 或者 tanh 激活函數學習的更快。而且,sigmoid 和 tanh 函數的導數在正負飽和區的梯度都會接近於0,這會造成梯度彌散,Relu 和 Leaky Relu 函數大於 0 部分都為常數,不會產生梯度彌散現象(但是,Relu 進入負半區的時候,梯度為 0,神經元此時不會訓練,產生所謂的稀疏性,而 Leaky Relu 不會有這問題)。
所以,總得來說:
sigmoid 函數:除了輸出層是一個二分類問題,基本不會用它;
tanh 函數:幾乎適合所有場合;
Relu 函數:最常用的默認函數,如果不確定用哪個激活函數,就使用 Relu 或者 Leaky Relu。其中,Leaky Relu 的參數 \(a\) 一般設為 0.01。
激活函數的導數
對常見激活函數,導數計算如下:
原函數 | 函數表達式 | 導數 | 備注 |
---|---|---|---|
Sigmoid 激活函數 | \(f(x)=\frac{1}{1+e^{-x}}\) | \(f^{'}(x)=\frac{1}{1+e^{-x}}\left( 1- \frac{1}{1+e^{-x}} \right)=f(x)(1-f(x))\) | 當 \(x=10\),或 \(x=-10\),\(f^{'}(x) \approx0\),當 \(x=0\)\(f^{'}(x) =0.25\) |
Tanh 激活函數 | \(f(x)=tanh(x)=\frac{e^x-e^{-x}}{e^x+e^{-x}}\) | \(f^{'}(x)=-(tanh(x))^2\) | 當 \(x=10\),或 \(x=-10\),\(f^{'}(x) \approx0\),當 \(x=0\)\(f^{`}(x) =1\) |
Relu 激活函數 | \(f(x)=max(0,x)\) | \(f^{'}(x)=\begin{cases} 0,x<0 \\ 1,x>0 \\ undefined,x=0\end{cases}\) | 通常 \(x=0\) 時,給定其導數為 1 和 0 |
Leaky Relu 激活函數 | \(f(x)=max(0.01x,x)\) | \(f^{'}(x)=\begin{cases} 0.01,x<0 \\ 1,x>0 \\ undefined,x=0\end{cases}\) | 通常 \(x=0\) 時,給定其導數為 1 和 0.01 |
隨機初始化 (Random Initialization)
訓練神經網絡時,權重隨機初始化是很重要的。對於邏輯回歸,把權重初始化為 0 當然也是可以的,但是對於一個神經網絡,如果把權重或者參數都初始化為 0,那么梯度下降將不會起作用。也就是說,我們把權重都初始化為 0,由於所有的隱含單元都是對稱的,都會開始計算同一個函數,所以無論運行梯度下降多久,他們都一直計算同樣的函數。
所以,我們需要進行隨機初始化參數。具體做法為,把 \(W^{[1]}\) 設為 np.random.randn(4,4)
,這樣生成了一個高斯分布的隨機數矩陣,通常再乘上一個小的數,比如 0.01,這樣把它初始化為很小的隨機數。然后 \(b\) 沒有這個對稱的問題 (symmetry breaking problem),所以可以把 \(b\) 初始化為 0。類似地,\(W^{[2]}\) 和 \(b^{[2]}\) 也進行這樣的初始化。
對於為什么要將參數初始化為比較小的隨機數,原因是,如果我們使用 tanh 或者 sigmoid 激活函數,如果數值波動太大,\(z\) 就會很大或者很小,這種時候就很可能停在 tanh 或者 sigmoid 函數的平坦的地方,這些地方梯度很小,也就意味着梯度下降會很慢,學習也就很慢。
其實有時有比 0.01 更好的常數,當我們訓練一個只有一層隱藏層的網絡時,設為 0.01 可能可以。但是當訓練一個非常非常深的神經網絡,可能就需要試試 0.01 以外的常數了。
深層神經網絡 (Deep Neural Networks)
神經網絡的層數是這么定義的:從左到右,由 0 開始定義。如下圖所示。
有一個隱藏層的神經網絡,就是一個兩層神經網絡。當我們算神經網絡的層數時,我們不算輸入層,我們只算隱藏層和輸出層。
前向傳播和反向傳播 (Forward and Backward Propagation)
前向傳播
輸入 \(a^{[l-1]}\),輸出是 \(a^{[l]}\),緩存為 \(z^{[l]}\);從實踐中來看,我們還可以緩存下 \(w^{[l]}\) 和 \(b^{[l]}\),這樣更容易在不同的環節中調用函數。
那么,前向傳播的步驟為
向量化的版本為
前向傳播需要喂入 \({A}^{[0]}\) 也就是 \(X\),來初始化;初始化的是第一層的輸入值。\({a}^{[0]}\) 對應於一個訓練樣本的輸入特征,而 \({{A}^{[0]}}\) 對應於一整個訓練樣本的輸入特征,所以這就是這條鏈的第一個前向函數的輸入,重復這個步驟就可以從左到右計算前向傳播。
反向傳播
輸入為 \({{da}^{[l]}}\),輸出為 \({{da}^{[l-1]}},{{dw}^{[l]}}, {{db}^{[l]}}\)。
所以反向傳播的步驟可以寫成
向量化的版本為
核對矩陣的維數
當實現深度神經網絡的時候,可以拿一張紙過一遍算法中矩陣的維數。
\(w\) 的維度是 (下一層的維數,前一層的維數),即 \(w^{[l]}:(n^{[l]},n^{[l-1]})\);
\(b\) 的維度是 (下一層的維數,1),即 \(b^{[l]}:(n^{[l]},1)\);
類似地,\(z^{[l]},a^{[l]}:(n^{[l]},1)\)。
\({{dw}^{[l]}}\) 和 \({{w}^{[l]}}\) 維度相同,\({{db}^{[l]}}\) 和 \({{b}^{[l]}}\) 維度相同,且 \(w\) 和\(b\) 向量化維度不變,但 \(z\), \(a\) 以及 \(x\) 的維度會向量化后發生變化。
向量化后:
\({Z}^{[l]}\) 可以看成由每一個單獨的 \({z}^{[l]}\) 疊加而得到,\({Z}^{[l]}=({{z}^{[l][1]}},{{z}^{[l][2]}},{{z}^{[l][3]}},…,{{z}^{[l][m]}})\),
\(m\) 為訓練集大小,所以 \({Z}^{[l]}\) 的維度不再是 \(({{n}^{[l]}},1)\),而是 \(({{n}^{[l]}},m)\)。
\({A}^{[l]}\):\(({n}^{[l]},m)\),\({A}^{[0]} = X =({n}^{[l]},m)\)
參數與超參數 (Parameters vs Hyperparameters)
什么是超參數?
比如算法中的learning rate \(\alpha\)(學習率)、iterations(梯度下降法循環的數量)、\(L\)(隱藏層數目)、\({{n}^{[l]}}\)(隱藏層單元數目)、choice of activation function(激活函數的選擇)都需要你來設置,這些數字實際上控制了最后的參數 \(W\) 和 \(b\) 的值,所以它們被稱作超參數。
如何尋找超參數的最優值?
走 Idea—Code—Experiment—Idea 這個循環,嘗試各種不同的參數,實現模型並觀察是否成功,然后再迭代。
References
[2] 深度學習 500 問