# 人工神經網路 Artificial Neural Network ###### tags: `Machine Learning` 包含以下概念: 1. 神經元 The neuron 2. 激勵函數 Actication function * 梯度消失、梯度爆炸 * Step function * Sigmoid function * Tan h function * ReLU function * Leaky ReLU function 3. 神經網路如何運作 4. 神經網路如何訓練 * 隨機初始化 Random initialization * 正向傳播 Forward propogation * 計算誤差 Loss calculation * 反向傳播 Backward propogation 5. 最佳化方法 * 梯度下降 Gradient descent * 隨機梯度下降 Stochastic gradient descent 可以參考的文章有: * [Deep Learning A-Z™:人工神经网络实践-筆記2-Section3(ANN)](https://zhuanlan.zhihu.com/p/95984057) * ## 神經元 The Neuron 以下內容主要來自於[類神經網路(一)](https://ithelp.ithome.com.tw/articles/10254509)以及[腦力通訊:神經元的訊息傳遞](https://www.brainworksrnd.com/mws/brainworks/BrainWorks_5_201109_Web.pdf)。 神經元,生物體內的一部份,又名神經原或神經細胞,是神經系統的結構與功能單位之一。神經元能感知環境的變化,再將信息傳遞給其他的神經元,並指令集體做出反應。神經元佔了神經系統約10%,其他大部分由膠狀細胞所構成。基本構造由樹突、軸突、髓鞘、細胞核組成。傳遞形成電流,在其尾端為受體,藉由化學物質(化學遞質)傳導(多巴胺、乙醯膽鹼),在適當的量傳遞後在兩個突觸間形成電流傳導。 ![](https://i.imgur.com/dv4dSlg.png) 樹突負責接收資訊,接著由核心運算、判斷此訊息是否有必要傳遞(此處不多說明),若有,則由軸突傳遞出去,藉由突觸碰觸到另一個神經元的樹突,就完成一個完整的傳遞訊息流程,並稱兩個神經元之間「有連結」。 可以將樹突視為 input,軸突視為 output,核心是中間的隱藏層(運算層)。 ![](https://i.imgur.com/s7iDjAt.png) * 懶人包 收資料→判斷是否超過 threshold→是則發送出去,否則當沒事 * 人工神經網路就是基於神經元而建立 可以看看[我們為什麼需要 Deep Learning](https://yuehhua.github.io/2018/07/21/why-deep-learning/)。 過往提及的迴歸、分類、分群等演算法的使用,需要工程師非常的聰明,因為他必需要知道在 input 與 output 之間的所有規則,然後把這些規則化成可以執行的程式,這些實作的過程其實需要花非常大量的人力跟腦力。(如下圖) ![](https://i.imgur.com/exrClqs.png) 當神經元的概念被引入,讓機器學習模仿人類思考、學習、成長的行為,進而演變成深度學習。神經元可以幫我們將中間的**過程**連接起來,我們不需要去手刻或是事先知道這些過程(更何況自然界很多過程都是人類沒辦法理解的或是還不知道的)。 ![](https://i.imgur.com/ZvATpm3.png) ## 激勵函數 Actication function 參考資料為[深度學習激勵函數介紹](https://cvfiasd.pixnet.net/blog/post/275774124-%E6%B7%B1%E5%BA%A6%E5%AD%B8%E7%BF%92%E6%BF%80%E5%8B%B5%E5%87%BD%E6%95%B8%E4%BB%8B%E7%B4%B9)、[溫故知新——激活函數及其各自的優缺點](https://zhuanlan.zhihu.com/p/71882757)。 前面神經元的介紹中,有提到: > 收到的資料要先判斷是否有超過一特定之 threshold 才會決定是否向下傳遞。 此處的激勵函數,就是 ANN 中,判斷是否超過 threshold 的關鍵。 在類神經網路中使用激勵函數,**必須**是<font color='red'>利用非線性方程式,解決非線性問題</font>;若以線性的方式組合運算,因為隱藏層以及輸出層皆是直接將上層之結果運算,作為這一層的輸出,將使得輸出與輸入只存在著線性關係。而現實中,所有問題皆屬於非線性問題,因此,若無使用非線性之激勵函數,則類神經網路訓練出之模型便失去意義。(這部分可以參考[這篇](https://www.cnblogs.com/jokerjason/p/9436117.html)) 使用非線性激勵函數是為了**增加神經網絡模型的非線性因素**,以便使網絡更加強大,增加它的能力,使它可以學習複雜的事物,複雜的表單數據,以及表示輸入輸出之間非線性的複雜的任意函數映射。 * 輸出層可能會使用線性激活函數,但在隱藏層都使用非線性激活函數。 下圖中間綠色的部分,就是激勵函數。 ![](https://i.imgur.com/aAbnYI3.png) * 激勵函數注意事項 * 激勵函數需選擇可微分之函數,因為在誤差反向傳遞(Back Propagation)運算時,需要進行一次微分計算。 * 在深度學習中,當隱藏層之層數過多時,激勵函數不可隨意選擇,因為會造成梯度消失(Vanishing Gradient)以及梯度爆炸(Exploding gradients)等問題。 ### 梯度消失 梯度下降算法是非常廣泛的優化算法,一般監督式之類神經網路使用誤差反向傳遞(Back Propagation)進行神經網路權重更新,先計算輸出層對應的loss,然後將loss以導數的形式不斷向上一層網絡進行傳遞,並修正相應的權重參數,達到降低loss之目的。 而某些激勵函數在深度網絡的權重更新中,常會因為層數過多,導致導數逐漸變為0,使得前幾層之權重參數無法順利更新,造成神經網絡無法被優化,而無法找出最佳結果。 ### 梯度爆炸 深層類神經網路透過誤差反向傳遞(Back Propagation)更新權重,若在權重更新中,梯度累積得到非常大的值,使得每一層會大幅度的更新權重參數,將造成網路相當不穩定。 若權重參數變得相當大,並超出可計算之臨界值,則輸出結果會產生溢位的狀況(無窮大、非數值)。 ### 各種激勵函數 * 激勵函數有許多種類,適用於不同的狀況,下面將會舉幾個說明。 * wiki 列舉如下: * 單輸入 比較常見的是 Sigmoid function、Hyperbolic tangent function (tan h) 以及 ReLU function;其中又以 ReLU 最常使用,而 ReLU 又有許多變形(如Leaky ReLU)。 ![](https://i.imgur.com/5OpUaZo.png) * 多輸入 ![](https://i.imgur.com/jwpcwj9.png) ### Step Function(步階函數) $f(x)= \left\{ \begin{aligned} 0,\ if\ x<0 \\ 1,\ if\ x\ge0 \\ \end{aligned} \right.$ 屬於激勵函數中最簡單的作法-- * 當函數收到的值: * 非負,則回傳 1 * 否則,都回傳 0 ![](https://i.imgur.com/fMr73Ix.png) 雖然方法很簡單(只單純的回傳 0 或 1 ),但這應用在機器學習上會造成一些問題--因為它無法微分而無法進行gradient descent等運算,所以訓練 NN 的時候其實不常用,比較常用下面那些。 ### Sigmoid Function $f(x)=\sigma (x)=\dfrac{1}{1+e^{-x}}$ $f'(x)=\sigma (x)[1-\sigma (x)]$ ![](https://i.imgur.com/WfQydsw.png) * 適用時機 1. 可以用來做二分類 2. 在特徵相差比較複雜或是相差不是特別大時效果比較好 * 相對於 step function 的優點: 1. 傳回值是連續且可微分的 2. 輸出在 (0,1) 之間,輸出範圍有限,優化穩定,可以用作輸出層 * 缺點: 1. 不是 zero-centered 2. 正因頭尾兩端是漸近值,所以會導致輸出過度飽和,使得梯度計算不方便,且會對輸入的微小改變不敏感 > 在**反向傳播**時,當梯度接近於0,權重基本不會更新,很容易就會出現**梯度消失**的情況 3. 因為是指數函數,所以計算複雜度高,耗時長 因為仍有缺點,所以出現 tan h funtion。 ### tan h Function $f(x)=tanh(x)=\dfrac{sinh(x)}{cosh(x)}=\dfrac{e^x-e^{-x}}{e^x+e^{-x}}$ $f'(x)=1-[f(x)]^2$ ![](https://i.imgur.com/yOie0QD.png) * zero-centered,改善了 sigmoid function 的缺點一,大大增加可用性。 * 但仍未改善剩下兩個缺點,所以出現了 ReLU function。 ### ReLU Function $f(x)=max(0,x)$ ![](https://i.imgur.com/5FgmP7z.png) ReLU,全名 Rectified Linear Unit,整流線性單元。 ReLU 函數並不是全區間皆可微分,但是不可微分的部分可以使用 Sub-gradient 進行取代。ReLU 是近年來最頻繁被使用的激勵函數,因其存在以下特點:解決梯度爆炸問題、計算速度相當快、收斂速度快等特性。 * 優點 1. 收斂速度比前面的都快 2. $x>0$的狀況下,不會出現梯度爆炸、梯度消失的問題 3. 不是指數函數,所以計算複雜度低、速度快 * 缺點 1. 不是 zero-centered > 但可以用 sub-gradient 解決。 2. <font color='red'>Dead ReLU problem</font>(神經元壞死現象):ReLU在負數區域被 kill 的現象稱為 dead relu。ReLU 在訓練時,若 $x<0$,梯度為 0,則該神經元及之後的神經元梯度永遠為 0,不再對任何數據有所響應,導致相應參數永遠不會被更新。 可能原因:初始權重設定不恰當、learning rate 過高導致參數更新太大。 > 有趣的是,**若 $x<0$,梯度為 0** 這個特性並不是一定會造成 dead ReLU,反而有時候還是個益處--其可以讓網路變得更加多樣性,如同 dropout 的概念,可以緩解 over fitting 之問題。 ### Leaky ReLU Function $f(x)=max($<font color='red'>$\alpha$</font>$x, x)$ ![](https://i.imgur.com/0jlcSXD.png) 這是 ReLU 的變形,屬於較常見的,其他還有 PReLU、Bounded ReLU,可以參考[學習筆記:ReLU的各種變形函數](https://cloud.tencent.com/developer/article/1597479)。 Leaky ReLU 是為了防止 dead ReLU problem 而生,可以注意到它的函數有一個 $\alpha$,這個 $\alpha$ **通常為 0.01**,但也可以是 0.99 或任意數,總之能防止 $x<0$ 時永遠無法被激活之問題。 > 理論上,leaky ReLU 保留了 ReLU 的優點,並避免了 dead ReLU problem,看起來應該比 ReLU 更好;但實務上並未證實 leaky ReLU 絕對優於 ReLU 這件事。 > **推測**:應是因為 $x<0$,梯度為 0 如同 dropout 的概念。 ## 神經網路如何運作 ### 隱藏層(待補充) input 與 output 之間,可以有一層或多層神經元。 * 只有一層,或是多層但是彼此間為線性關係時,整體稱為**淺層神經網路** * 二層(含)以上且為非線性時,就可以稱為**深度神經網路** 中間這些神經元的運算並不是透明的,無法被得知,所以又稱為「隱藏層」;他們就像自然界的奧妙一樣,具有自己的規律與道理且有用,卻無法被參透。這也造成一個缺點--隱藏層又被稱為「黑盒子」,因為你不知道裡面在幹嘛。 ### 人工神經網路速覽 模擬神經元的運作,又稱作感知器(Perceptron)。 * 左邊黃色 X 表示 input,中間綠色 neuron 表示隱藏層(模擬神經元的核心),右邊紅色 y 表示 output,之間連接的藍線表示權重(模擬突觸) :::info ![](https://i.imgur.com/79pHDOm.png) ::: * 輸入到神經網路之前通常會對數據集作標準化(Standardize)或正規化(Normalize),可加速神經網路收斂。 :::info ![](https://i.imgur.com/ut17QLF.png) ::: * ANN 的輸出可以拿來預測連續資料、二元資料、類別資料,換言之,可以做到回歸、分類。 :::info ![](https://i.imgur.com/tsAPai7.png) ::: ### 小結 1. 對神經網路的輸入做標準化或正規化,可以加速神經網路收斂,且也能使輸出為標準化/正規化的結果 2. 藉由調整不同的權重(甚至刪除某些權重)來讓輸入處於最精準、沒有冗員的狀況,以獲得最佳結果 3. 透過隱藏層(激勵函數)的運算,決定輸入傳來的資料是否要通過並輸出,是決定這個神經網路是否具有「深度」的關鍵部分 ### 舉例-房價預測 * 使用多元線性回歸 :::info ![](https://i.imgur.com/ZD43zAx.png) ::: * 使用 ANN :exclamation:注意:ANN 常常會做「特徵擴增」,以本例子來講,只有四個輸入(input dim),但隱藏層有五個神經元(units),每個都會處理不同的特徵,因為對不同的神經元而言,不是所有的特徵都是有必要的。 :::info ![](https://i.imgur.com/MFaNSyP.png) ::: ## 神經網路如何訓練 神經網路的運作是: ``` 輸入*權重,進入隱藏層運算,然後輸出 ``` <font color='red'>**權重**</font>是一個很重要的關鍵,他決定了每個特徵的重要性,所以一個好的神經網路,必須要有完善的「計算權重」的環節。 權重的計算無法一步到位,要反覆訓練、檢驗誤差並且修正,才能得到一個好的網路,因此,計算權重的流程如下: :::info 1. 隨機初始化權重,使得權重接近 0 但不等於 0 2. 將第一個觀察數據輸入"輸入曾",每個自變量特徵輸入一個神經元 3. 正向傳播:神經網路從左至右進行計算,每個神經元得到來自上一層神經元的輸入與權重的運算 4. 計算誤差:計算預測值和實際值的差異 5. 反向傳播:神經網路從右至左進行計算,依據誤差函數相對於權重的梯度,對每個權重進行更新。學習速率與梯度將決定更新的速度。 6. 對每一組新的觀察數據,重複 step 1-5 (batch learning) 7. 當所有數據都輸入神經網路後,稱之為一期 (epoch) 的訓練 ::: 執行若干個 epoch(次數自己設定)直到 loss 小到可以接受或是 accuracy 大到滿意。 ### 隨機初始化 Random Initialization 為什麼隨機初始化?看看[每天五分鐘深度學習:神經網絡權重初始化問題](https://kknews.cc/code/mno68b9.html)的介紹。 模型第一次要訓練前,要先對輸入的每個特徵的權重做「隨機初始化」,才可以成為一個有效的深度神經網路。 如果沒有隨機初始化,而是將每個權重參數設定成一樣的數字,又因為每層隱藏層的激勵函數都會是一樣的,等於每個單元的行為都一樣,再多的層數也相當於只有一個單元在運作,就失去了「深度」「神經」網路的用意了。 常見的隨機初始化方法可以參考[深度學習權重初始化常用方法](https://zhuanlan.zhihu.com/p/133190518)。 ### 正向傳播 Forward Propogation 詳細的內容可以參考[3.14. 正向傳播、反向傳播和計算圖](https://zh.d2l.ai/chapter_deep-learning-basics/backprop.html)。 正向傳播(forward propagation)是指對神經網絡沿著從輸入層到輸出層的順序,依次計算並存儲模型的中間變量(包括輸出)。 簡單來說,就是順著所設計的模型的次序執行一遍,並記錄其中所有的變數以及最終的結果(預測值)。 最後這個結果(記作 $\hat{y}$)會跟 ground truth(記作 $y$),一起作誤差計算,以便調整模型優劣。 ### 計算誤差 Loss Calculation 本段內容參考於[Google ML - Lesson 9 - 加速ML模型訓練的兩大方法(如何設定batch/檢查loss頻率)、batch size, iteration, epoch的概念和比較](https://ithelp.ithome.com.tw/articles/10219945)以及[ML-误差函数](https://menh.github.io/2020/03/02/ML-%E6%8D%9F%E5%A4%B1%E5%87%BD%E6%95%B0/)。 #### 批次運算 神經網路的重點就在於「好像人類會思考」,那人類經常也是反思、三思後才會改變,所以神經網路的訓練也不會只有一次,所以這裡要引入`batch`、`iteration`、`epoch`的概念。 * batch:每「坨」資料中含有<font color='red'>幾筆資料</font>。 * iteration:總共有<font color='red'>幾堆</font>。 * epoch:看完一個資料的次數。(一輪訓練) 舉例: > 假設有 400 筆資料要做分堆 我決定<font color='blue'>一堆的大小 (batch size) </font>要有40筆資料, 這樣一共會有 10 堆(通常稱為number of batches,batch number), 也就是說每一輪我要<font color='blue'>學 10 堆資料,也就是學 10 個迭代 (iteration)</font>。 學完「10 個迭代 (iteration)」後,等於我把資料集全部都看過一次了。 這樣就是<font color='blue'>一輪 (epoch) </font>訓練的結束。 #### 誤差函數 根據前面「批次運算」的內容,可以注意到 batch、iteration、epoch 等名詞。訓練時,每個 epoch 都會有自己的 accuracy 跟 loss,而這些 loss 就是在學習每個 iteration 時的誤差計算出來的。 但在每次訓練中,我們有非常多組資料,就會得到一堆誤差 (error),我們需要去思考該怎麼組合這些數據。 * 最簡單的方法,就是直接加總,例如使用sum。 > 然而,如果直接加總的話,正值與負值會被抵銷,例如誤差組合:(+100, -100) 與 (0,0),sum 值相同,但它們代表的意義可是天差地遠。 > 顯然,這方法存在問題。 * 那「誤差(error)」的絕對值之和呢?也就是 MAE(Mean absolute error)平均絕對值誤差 > 這方法是合理的,但會有「在等於 0 時」不可微分的問題,這樣就不能做反向傳播修正模型了! > 但完全不能使用嗎?倒也不完全是不能用,他有它的長處,可以參考[機器/深度學習: 基礎介紹-損失函數(loss function)](https://chih-sheng-huang821.medium.com/%E6%A9%9F%E5%99%A8-%E6%B7%B1%E5%BA%A6%E5%AD%B8%E7%BF%92-%E5%9F%BA%E7%A4%8E%E4%BB%8B%E7%B4%B9-%E6%90%8D%E5%A4%B1%E5%87%BD%E6%95%B8-loss-function-2dcac5ebb6cb)。 因應以上的問題,推出了各種誤差函數,如下圖: ![](https://i.imgur.com/Izi6ruM.png) 以下簡單列出常見的誤差函數,詳細的可以直接到[ML-误差函数](https://menh.github.io/2020/03/02/ML-%E6%8D%9F%E5%A4%B1%E5%87%BD%E6%95%B0/)看。 * 迴歸常用 1. MSE,均方誤差 2. L2損失,平方損失 3. L1損失,平均絕對誤差 4. Huber損失,平滑平均絕對誤差 5. log-cosh損失 6. 分位數損失 * 分類常用 1. 0-1 損失 2. Hinge損失(SVM) 3. 對數損失(logarithmic loss, LR) 4. 交叉商損失函數(Cross-Entropy) 5. 指數損失函數(AdaBoost) 損失函數的選取依賴於參數的數量、異常值、機器學習算法、梯度下降的效率、導數求取的難易和預測的置信度等若干方面。 ### 反向傳播 Backward Propogation 細節的部份可以參考[為什麼需要反向傳播 ? Why Backpropagation ?](https://allen108108.github.io/blog/2020/06/01/%E7%82%BA%E4%BB%80%E9%BA%BC%E9%9C%80%E8%A6%81%E5%8F%8D%E5%90%91%E5%82%B3%E6%92%AD%20_%20Why%20Backpropagation%20_/),這裡只介紹反向傳播的用途。 反向傳播(縮寫為BP)是「誤差反向傳播」的簡稱,通常用來與最佳化方法(如梯度下降法)結合使用,用來訓練人工神經網絡。該方法對網絡中所有權重計算損失函數的梯度。這個梯度會反饋給最佳化方法,用來更新權值以最小化損失函數。 ## 最佳化方法 ### 梯度下降 Gradient descent 推薦你看看這篇:[機器/深度學習-基礎數學(二):梯度下降法(gradient descent)](https://chih-sheng-huang821.medium.com/%E6%A9%9F%E5%99%A8%E5%AD%B8%E7%BF%92-%E5%9F%BA%E7%A4%8E%E6%95%B8%E5%AD%B8-%E4%BA%8C-%E6%A2%AF%E5%BA%A6%E4%B8%8B%E9%99%8D%E6%B3%95-gradient-descent-406e1fd001f) 梯度下降法(GD)是一個**一階最佳化算法**,通常也稱為「最速下降法」。主要是希望用梯度下降法找到函數的**局部最小值**,因為梯度的方向是走向局部最大的方向,所以在梯度下降法中是往梯度的反方向走,也就是會對反方向的<font color='blue'>規定步長距離</font>點進行迭代搜索。 梯度下降法是一種不斷去更新參數(這邊參數用 x 表示)找「解」的方法,所以一定要先隨機產生一組初始參數的「解」,然後根據這組隨機產生的「解」開始算此「解」的梯度方向大小,然後將這個「解」去減去梯度方向,公式如下: $x^{(t+1)}=x^{(t)}-\gamma \nabla f(x^{(t)}$ 這邊的t是第幾次更新參數,γ是學習率(Learning rate)。 梯度的方向我們知道了,但找「解」的時候公式是往梯度的方向更新,一次要更新多少,就是由學習率來控制的。 ### 隨機梯度下降 Stochastic gradient descent 推薦你看看這篇:[機器/深度學習-基礎數學(三):梯度最佳解相關算法(gradient descent optimization algorithms)](https://chih-sheng-huang821.medium.com/%E6%A9%9F%E5%99%A8%E5%AD%B8%E7%BF%92-%E5%9F%BA%E7%A4%8E%E6%95%B8%E5%AD%B8-%E4%B8%89-%E6%A2%AF%E5%BA%A6%E6%9C%80%E4%BD%B3%E8%A7%A3%E7%9B%B8%E9%97%9C%E7%AE%97%E6%B3%95-gradient-descent-optimization-algorithms-b61ed1478bd7) * 在更新參數的時候 * GD 我們是一次用全部訓練集的數據去計算損失函數的梯度就更新一次參數 * SGD 就是一次跑一個樣本或是小批次(mini-batch)樣本然後算出一次梯度或是小批次梯度的平均後就更新一次,那這個樣本或是小批次的樣本是隨機抽取的,所以才會稱為隨機梯度下降法。 SGD 求出的解不一定是全域最佳解,但一定會近似於全域最佳解,而且方法執行速度較快,也更快收斂,所以即便不是全域最佳解也是足以被接受的。 更多的比較可以參考[如何理解隨機梯度下降(stochastic gradient descent,SGD)?](https://www.zhihu.com/question/264189719) ## 簡單總結 根據不同需求,用不同的激勵函數以及神經層來建構模型,然後用正向傳播求中間變量以及預測結果,透過誤差函數計算誤差之後,配合反向傳播來讓最佳化方法可以計算出最佳參數,用以調整模型使誤差達到最小。