類神經網路跟Backpropagation一點筆記

自己摸索機器學習,總算是看到了類神經網路的部分,這一兩年一直很夯的深度學習最基本的基石部分可以說就是類神經網路了吧
為了確認自己的理解程度,今天把自己學習過程的一點心得跟裡面經典的演算法Backpropagation的實作用個人的方式記錄起來

類神經網路(neural network)、網路上很多資料了,簡單來說想要模擬人類大腦神經的方式來達到複雜的學習效果
一個簡單的神經網路模型如下

神經網路是由許許多多的神經元(neuron)所組成,也就是圖中那些圓形.每個神經元或多或少會更其他的神經元有所連結
在機器學習裡面會把神經元歸納在三個layer裡面

  • Input Layer:裡面每個神經元可以想像成某筆訓練資料的所有特徵,以途中例子我的資料會有x1跟x2兩個特徵
  • Output Layer:某筆訓練資料對應的輸出結果,若是二元分類問題或是回歸問題通常在Output層只會有一個神經元,而在做多元分類的時候就會有多個神經元當作輸出
  • Hidden Layer:非Input Layer跟Output Layer的中間層一律歸類在Hidden Layer

Input Layer跟Output Layer只會有一層,而Hidden Layer根據設計可以有很多層,為了接下來記錄方便起見我圖中先只放一層.而除了Output Layer以外,會在每一層加上一個bias的神經元當作一個常數,這樣一來每神經元的運算就能變成機器學習裡面常用的數學公式

至於為什麼需要神經網路,什麼樣的情況下適用神經網路,網路上查了一些資料,結論來說就是當訓練資料我們沒有辦法定義其特徵的時候,就可以試著使用神經網路,比如說Raw data像是圖片,音波等這些binary檔案.因為神經網路的Hidden Layer代表著一層又一層的特徵抽取過程,當Hidden Layer很多且設計的還不錯的時候,有可能就可以對於那些raw data萃取出更有物理意義的特徵來做學習.這也是一些特徵難以定義的應用像是圖形辨識,語音辨識,NLP等等喜歡用神經網路當作當作學習的模型

而神經元這東西的基本構造如下

每個神經元都會由一些input得到一個加權總和z

再透過activation function做非線性轉換輸出得到結果o,而有趣的是當我們採用sigmoid當作一個神經元的activation function的時候,神經元就會變成一個logistic分類器,這樣一來神經網路就會變成一堆logistic分類器的組合

這樣有什麼好處,logistic分類器是個線性分類器,當資料大致線性可分的時候可以做出還不錯的分類效果


但是當資料的分佈根本不是線性的時候,logistic分類器的表現就會很差
考慮下面這樣一個case

資料分佈是呈現一個對稱的方式,也就是這一個XOR的分類問題,如果硬是要做分類,透過多項式等方式的特徵轉換來讓分類器能處理更複雜的問題,如上圖第右邊所表示,但如此一來可能會遇到一些問題

  • 要選用什麼樣的特徵轉換,要多複雜
  • 特徵數量爆炸,轉換過後的特徵可能會變得非常複雜且難以理解,尤其是運算量會暴增

對於上面的問題有個簡單的想法,我能不能用兩條線或是好幾條線去切開資料做分類就好

也就是對於上面這個XOR的問題其實我多用幾個logistic分類器就能解的很漂亮了,不需要複雜的特徵轉換,我只需要組合多個線性分類器一樣能做到複雜的事情,這也就是神經網路強大的地方

  • 神經元越多,神經網路能做到越複雜的事情
  • 不需要事先用複雜的特徵轉換,用原始資料運算就能達到效果 => 適合binary格式等raw data

引用一下XOR的圖



套用到神經網路的概念上,其實就是多個神經元的組合,我只需要組合三個神經元可以做出XOR分類器
對於這個問題待會附上自己實作的程式碼

接下來的問題就是神經網路如何學習,上面提到當我們使用sigmoid當作activation function的時候一個神經元其實就可以看成一個logistic分類器,而有N個神經元就會有N個分類器,每個神經元(分類器)都各自有一組對應的資料的權重,如下圖神經元a1持有著w1,w2,w3跟個權重來對應著他的input,要如何針對各個神經元的權重做訓練就是接下來的問題

神經網路的學習裡面有個經典的眼算法Backpropagation,今天這篇文章試著記錄並實作
細節的部分可以參考:https://mattmazur.com/2015/03/17/a-step-by-step-backpropagation-example/
上面這篇文章很詳細的說明Backpropagation的運算方法

Backpropagation的基本流程如下

  1. 初始化神經網路所有權重
  2. 將資料由input layer往output layer向前傳遞運算(forward)計算出所有神經元的output
  3. 計算誤差由output layer往input layer向後傳遞運算(backward)算出每個神經元對誤差的影響
  4. 用誤差影響去更新權重
  5. 重複2,3,4直到誤差收斂到夠小

用第一張圖的神經網路舉個例子如何使用梯度下降的方式去更新神經網路的誤差,為了簡單起見這邊不考慮Regularization等問題,同時一次只看一筆資料做更新而不使用mini-batch等方法
這邊假設有一筆資料要被訓練 input features是(0.3, 0.5),期望output結果是(0, 1),bias全都為1
並且設定當做我們梯度下降的learning rate
梯度下降方法不懂的話可以參考logistic
並使用sigmoid function當作神經元的activation function
sigmoid function定義如下

初始化神經網路所有權重

先將所有神經元做隨機初始化如下圖,這邊要注意的是bias的權重在初始的時候會被同一層的神經元共享
像是a1跟a2的bias權重同為0.6,可以看出a1神經元所持有的權重是[0.5, 0.3, 0.6]來對應他的input

向前傳遞運算(forward)計算出所有神經元的output

我們先專心看a1這個神經元的計算

a1會先從input(0.3, 0.5)得到加權總和z=0.9

之後將得到的結果帶入sigmoid function算出a1的output大約會等於0.71

之後我們可以用同樣的方式算出a2得到加權總和0.975跟output0.726,a1,跟a2的output將會成為下一層的神經元的input
接下來看看神經元o1的計算

a1會先從input(0.71, 0.726)得到加權總和z=0.7525

之後將得到的結果帶入sigmoid function算出a1的output大約會等於0.679

之後我們可以用同樣的方式算出o2得到加權總和1.15跟output0.759

如此一來所有神經元的output都算出來了

計算誤差向後傳遞運算(backward)算出每個神經元對誤差的影響

接下來我們要計算真實output跟期望output的誤差,並計算每個神經元對於誤差的影響
而神經元對於誤差的影響通常用來表示

經過數學推導,若神經元並非屬於output layer,其造成的誤差影響可以由後面一層的誤差計算出
若神經元屬於output layer,其誤差影響正好可以直接看成真實output跟期望output的差
參考:https://en.wikipedia.org/wiki/Delta_rule

數學的推導這邊我就省略了,請參考
https://mattmazur.com/2015/03/17/a-step-by-step-backpropagation-example/

各層誤差影響的算法

  • Input Layer:不持有權重,不需要計算
  • Output Layer:
  • Hidden Layer:用後面一層的誤差跟activation function的微分算出,

W代表的後面一層跟自己有連結的神經元他的權重,之後在乘上該神經元的誤差影響
因為可能會有多個神經元,的運算可以看成一個加總後的結果

activation function因為我們採用的sigmoid function 其微分結果可以看成如下

z是該神經元的input,o是該神經元的output

o1是output layer的神經元,期望輸出是0,實際輸出是0.679, 計算方式如下


可以算出神經元o1的誤差影響約是0.1479
之後可以用同樣的方法算出o2的會等於-0.0438

有了o1跟o2的之後,我就可以用他們去算出a1跟a2的
我們先算a1的,跟a1跟o1, o2連結的權重分別是0.1跟0.2 分別乘上他們的加總後再乘上他自己的g'即可 公式如下


可以得到a1的是0.0012,用同樣方法可以算出a2的大概是0.0012
如此一來所有神經元的就算完了,這一步到此為止

用誤差影響去更新權重

之後,就能透過梯度下降的方式去更新神經元的權重
假設對應的input是 那權重更新方式如下

這邊要注意一點,bias的權重如果想要更新的話,公式有點不一樣,長相如下
直接減去learning rate乘做更新

實際走過一遍,假設我先更新神經元o1的w7權重也就是o1跟a1連結的那條線
且權重w7對應的input是神經元a1的output也就是0.71
w7更新方式如下

新的w7權重在這輪被更新成0.474(因為是用程式計算,可能帶有精確度的誤差)

之後就可以用同樣的方式去更新其他w1~w12的權重,全部權重更新完之後這一輪就做完了

重複2,3,4直到誤差收斂到夠小

做過一輪更新後,剩下的就只是重複學習直到誤差夠小

這邊附上一個用純python實作的版本
https://github.com/del680202/MachineLearning-memo/blob/master/src/nn/neuralnetwork.py

對於上面那一個範例
我建構了一個2-2-2的神經網路,另外建構了一個2-5-5-2的四層網路跟一個2-30-2多神經元網路來訓練
https://github.com/del680202/MachineLearning-memo/blob/master/src/nn/exmple1.py
結果如下

NeuralNetwork 2-2-2, Except output:[0, 1], Real output:[0.014522285039060256, 0.9855417739681581]
NeuralNetwork 2-5-5-2, Except output:[0, 1], Real output:[0.009779504706775488, 0.9907341760553725]
NeuralNetwork 2-30-2, Except output:[0, 1], Real output:[0.0006106291222799419, 0.9999945419840505]

對於同一筆資料當神經網路hidden layer增加的時候,降低誤差的能力也比較高
而對於2-30-2這個網路,這邊測試的時候發現它對於梯度下降的速度非常慢,他需要更大量的訓練次數來達到效果
這算是這次實驗一個比較沒想到的現象

而對於上面提到XOR對稱的網路,也建立了一個example
https://github.com/del680202/MachineLearning-memo/blob/master/src/nn/exmple2.py
用四個點做訓練,圈圈叉叉是對稱

訓練出來的圈叉分佈如下,看起來就像有兩條線將資料切開一樣

再試試看多元分類
https://github.com/del680202/MachineLearning-memo/blob/master/src/nn/exmple3.py
神經網路在做多元分類的時候很方便,依照類別數量做成一個向量
類別1就第一個位置是1其他位置是0 類別2第二個位置是1其他位置是0
依此類推
然後類別數量同時會等於output layer的神經元數量,每個神經元都代表一個類別

class_1 = [1, 0, 0 ,0]
class_2 = [0, 1, 0 ,0]
class_3 = [0, 0, 1 ,0]
class_4 = [0, 0, 0 ,1]

四個類別分佈在四個點上

輸出結果,因為是用比較硬性的方式去分所以有些類別分不出來
但大致的勢力範圍還是分出來了

這篇就先到這了

comments powered by Disqus