XOR 介紹
使用多層感知機(Multilayer Perceptron)實現 XOR 函數是一個非常經典的非線性函數例子,可以在圖中畫一條直線能夠完美分割紅點與藍點嗎?不行,
這就是非線性的問題。
這是一個單層神經網路的的結構,如果用單層神經網路的結構只能找出一條直線或是一個超平面,無論在圖中哪個位置放處一條直線,都無法完美切割,所以不適合解決 XOR 這種非線性的問題。
多層感知機
多層感知機的結構包含輸入層(Input layer)、隱藏層(Hidden layer)與輸出層(Output layer),如果只是將「資料 * 權重 + 偏差值」,無論向前傳遞幾層,都只是在做線性組合(Linear Combination),線性組合不管做幾次結果都還是線性,那麼多層感知機要怎麼解決非線性的問題呢?
激勵函數(Activation Function)
在資料每次經過一個節點時,都會通過一個激勵函數,激勵函數即是多層感知機能夠解決非線性問題的關鍵。通過激勵函數後,便是做了一次非線性轉換,假設隱藏層與輸出層共有 n 個節點,就是經過 n 次的非線性轉換。
接下來,我們進入實作,用最多人使用的深度學習框架 TensorFlow 實現 XOR。
引入 TensorFlow
與 Numpy
引入之前要事先確定環境中是否已經安裝 TensorFlow 與 Numpy,可以在 command-line 中使用 pip list
列出環境中已安裝完成的 package,從中尋找是否有 TensorFlow 與 Numpy,如果尚未安裝請參考 [Tensorflow實戰] TensorFlow 環境搭建。
輸入資料的 feature 與 label
我們要利用 TensorFlow 搭建一個 MLP 預測 XOR gate 的模型,看到下方圖表中的數值 a 和數值 b 是邏輯閘的輸入,也就是 feature。而經過 XOR gate 的數值作為是 label,因此每一組 feature 都會對應一個 label。
首先,利用 numpy array 建立 feature 與 label 的對應,我們會將 x_
與 y_
同時作為 training data 和 testing data。
現實中的問題並非像是 XOR gate,擁有固定的數值對應,通常遇到到的問題都是非線性問題,因此如果 training data 與 testing data 使用相同的數據,訓練出來的模型容易造成 overfitting,泛化能力較差,所以訓練模型時,必須將 training data 與 testing data 分開。
設計 MLP 模型的結構與運算
我們定義一個 2 x 4 x 1 的 MLP 模型
- Input layer 有 2 個節點,也就是輸入 XOR gate 的兩個值
- Hidden layer 有 4 個節點
- Output layer 有 1 個節點,輸出預測經過 XOR gate 後的數值。
定義 MLP 模型的結構與運算
在 TensorFlow 中主要會運用 placeholder
與Variable
定義神經網路,這時候你一定會想,為什麼要用 placeholder
與 Variable
?兩者有什麼區別?
placeholder
placeholder
是 TensorFlow 中一個很有趣的用法,可以預先在神經網路中 佔位,而不用事先定義好輸入的資料,這也是 TensorFlow 中計算圖(Computing Graph)的概念,之後再使用 Session 將資料傳入計算圖中。
在 placeholder
中定義節點的形狀 shape=(None, 2)
,None
的意思是二維矩陣的 row 事先尚未定義,也就是「不定義輸入資料的筆數」,隨著透過 Session 將資料傳入計算圖時,動態符合輸入資料的筆數。
往後在訓練神經網路時,可以透過設定超參數 batch_size
決定每次訓練資料的筆數,保持神經網路的彈性。
Variable
在我們談完 placeholder
後,接著談到 TensorFlow 另一個重要的基石–Variable
。placeholder
用於在計算圖中傳入 feature 與 label,Variable
則是在訓練神經網路時,儲存可更新參數的容器。
權重 (weight) 和偏差值 (bias) 皆是我們在訓練神經網路時需要優化的對象,經由優化權重和偏差值,讓神經網路可以擬合 feature 與 label 之間的對應。
以上是 Neural Network Playground 的範例,可以看到神經網路擬合資料的過程,該資料是呈現非線性分布狀態。接著,定義 4 x 8 x 3 的 MLP,藉由向前傳播 (forward propagation) 與向後傳播 (back propagation) 更新權重與偏差值,藉此讓神經網路擬合資料的分佈。
定義 MLP 模型的結構與運算
在先前我們只定義所需的參數,接著我們要將 placholder (資料) 與 Variable (參數) 組合起來,定義在計算圖中向前傳播 (forward propagation) 的運算流程。
此外,激活函數 (activation) 是神經網路中的重要元素,神經網路的隱藏層藉由激活函數對輸出結果做非線性轉換,而我們用使用其中一個常見的 S函數(sigmoid) 做為模型中的激活函數。
損失函數(Loss Function)
在訓練模型時,需要評估神經網路預測的結果與真實質的差異,均方誤差(Mean Squared Error, MSE) 經常被用在回歸問題。當然,也可以使用其他損失函數來評估模型,無論是何種損失函數都是在比較預測值與真實值之間的差異。
優化器(Optimizer)
優化器在類神經網路中擔任重要的其中一位角色,它被用來優化類神經網路中的權重與偏差值。目前主流的優化器都是基於梯度下降 (gradient descent) 所衍生的方法,梯度下降決定權重與偏差值怎麼被優化,藉此讓損失函數最小化
在這邊我們使用 Adam (Adaptive Moment Estimation) 作為優化神經網路的優化器,它是現今經常被使用的其中一個優化器,可以想像 Adam 是能夠動態調整 learning rate,並且在訓練時帶有動量的優化器。
初始化參數
在運行計算圖前,必須先初始化所有變數,可以選擇針對每一個變數單獨做初始化,我們則使用 tf.global_variables_initializer()
一次初始化所有的變數。
訓練類神經網路
在定義好 Variable、placeholder、loss function、opimizer 後,我們可以將資料丟進類神經網路中開始訓練模型。
在訓練 MLP 模型讓其符合 XOR gate 的結果是使用 BGD (Batch gradient descent),使用 BGD 的原因是因為 XOR 的輸入和輸出總共只有 4 筆資料,因為資料量很少,所以不用擔心訓練時,電腦運算量過大造成負擔的問題。
訓練時,透過 Session 傳遞資料至計算圖中,讓資料流至 output_layer
這個節點,接著透過 MSE 比較預測值與真實值的差異,然後再使用 Adam 這個優化器透過反向傳播優化模型中的權重與偏差值,以上就是訓練的過程。
模型將會不斷的重複經過上述的過程訓練,直到迭代次數到達設定的 epochs
為止。
每經過 10000 萬次 epoch 將 loss 儲存至 losses
,在訓練結束後將 losses
繪製成折現圖,觀察是否隨著訓練而讓模型越來越擬合 XOR gate 的結果,最後 MSE 等於 1.1172499e-11,是一個非常小的數字,也就顯示著訓練相當成功。
預測 XOR gate 的輸出值
最後,再次將資料傳入計算圖中的 output_layer
節點,看看模型的預測值是否符合 XOR gate 的輸出值。從結果看到預測值與真實值得誤差非常地小,誤差大約在小數點後 5~7 位,因此可以斷定此模型訓練得相當好。
當然,模型可以再透過更多種的方法優化,調整超參數 (hyperparameters)、調整隱藏層的節點數量或是層數,或者選擇其他的優化器,都有可能訓練出更好的 MLP 模型。
如果認為 MLP 不符合原本的期待,甚至可以選擇其他的類神經網路,例如: CNN、RNN 等,但其訓練得結果能否符合期待就讓各位自行去挑戰了。