Perceptron ตอนที่ 3 : Adaptive Linear Neuron (ADALINE)

Adaptive Linear Neuron (ADALINE) [1]  เสนอโดย Bernard Widrow [2] และ Marcian Hoff [3] ในปี ค.ศ. 1959 คล้อยหลังจากที่ Rosenblatt เสนอ Perceptron [4] ประมาณ 2 ปี ในวิดีโอข้างล่างนี้ Bernard Widrow กำลังอธิบายแนวคิดของ ADALINE 

 


จากวิดีโอจะเห็นว่า ADALINE มีโครงสร้างพื้นฐานที่เหมือนกับ Perceptron ของ Rosenblatt [5] สิ่งที่ต่างออกไป คือ 
1) learning algorithm  แทนที่จะใช้ความต่างระหว่างค่าสังเกตุ (expected, y) กับค่า \( \hat{y}\) (predicted ) ที่ได้จาก activated function แต่ใช้ค่า \( \hat{y}\) ที่ได้จาก linear function แทน  (สังเกตุได้จากแผนภาพที่วาดบนกระดานในวิดีโอแรก) ดูรูปที่ 1 และ 2 ประกอบ ข้อดีของการทำแบบนี้คือการปรับค่าของ weight จะเกิดขึ้นแบบต่อเนื่อง (adaptive) ทุกครั้งเมื่อมี input เข้ามาในตัวแบบ เทียบกับของ Rosenblatts ที่ผลลัพธ์ต้องผ่าน activation function ออกมาก่อน จึงจะเกิดการเรียนรู้ วิธีการนี้ทำให้การเรียนของตัวแบบเป็นอิสระออกจาก threshold function (หรือ activate function)

รูปที่ 1  ตัวแบบ Perceptron ของ Rosenblatt



รูปที่ 2  ตัวแบบของ ADALINE



2) ในวิดีโอจะมีการกล่าวถึงการใช้ least mean square (LMS) algorithm [6] ในการคำนวณค่า error หาได้จาก
\[ \begin{align*} error_i &= (y_i - \hat{y_i})^2 \\ MSE &= \frac{1}{n}\sum_{i=1}^{n}{error_i} \\ \end{align*} \]
Mean Square Error (MSE) คือค่าเฉลี่ยกำลังสองของ error และเนื่องจาก error มีความสัมพันธ์กับ \( \hat{y} \) ดังนั้นเราจึงสามารถเขียน MSE ในรูปแบบของ function ของ \(\hat{y} \)
\[ \begin{align*} E(\hat{y}) &= \frac{1}{n}\sum_{i=1}^{n}{error_i} \\ \end{align*} \]
ในขณะเดียวกัน \(\hat{y}\) ก็มีความสัมพันธ์กับ Input (x),และ Weight (W) ทำให้เราสามารถเขียนสมการในรูปแบบที่โยงกับทั้ง W,X
\[ \begin{align*} E(\hat{y}) &= \frac{1}{n}\sum_{i=1}^{n}{(y_i - \hat{y}_i)^2} \\ E(w,x) &= \frac{1}{n}\sum_{i=1}^{n}{(y_i - (b_i+\sum_{j=1}^{m}w_j x_j)_i)^2} \longrightarrow (1)\\ \end{align*} \]
เมื่อ n คือจำนวนรายการข้อมูลทั้งหมด และ m คือจำนวน features
การทำงานของ LMS algorithm คือความพยายามประมาณค่า parameters ที่ทำให้ค่า \( E(w,x) \) มีค่าน้อยที่สุด 

Widrow ได้กล่าวถึงวิธีการประมาณค่า \( E(w,x) \) โดยใช้ Gradient [7]  ใช้บอกทิศทางการเปลี่ยนแปลงค่าของ function เพื่อความสะดวกในการหา Gradient แทนที่จะมองไปที่ MSE โดยตรง แต่เราจะมองไปที่ค่า error จากข้อมูลลำดับที่ i ใด ๆ  จากสมการ (1) จะได้

\[ \begin{align*} e(w,x) &= (y - (b + wx))^2 \\ e(w,x) &= y^2 - 2y(b + wx) + (b+wx)^2 \\ e(w,x) &= y^2 - 2yb -2ywx + b^2+2bwx+w^2x^2 \longrightarrow (2) \\ \end{align*} \]

ในระหว่างทำการสอน (train) ตัวแบบ ข้อมูลตำแหน่งที่ i  ประกอบไปด้วย independent variable \(x_i\) และ dependence variable \(y_i \) จะถูกนำเข้าสู่ตัวแบบ จะมีแต่ w เท่านั้นที่จะถูกปรับแต่งค่าจากกระบวนการ พิจารณามุมนี้เราจึงอนุมานได้ว่า \(x_i\) และ \(y_i\) เป็นค่าคงที่ ดังนั้น สมการที่ (2) จะถูกจัดใหม่เป็น
\[ \begin{align*} e(w,x) &= (y^2 + b^2 - 2yb) - (2yxw + 2bxw) + (x^2w^2) \longrightarrow (3.1) \\ C &= y^2 + b^2 - 2yb \\ B &= 2yx + 2bx \\ A &= x^2 \\ e(w) &= C + Bw + Aw^2 \longrightarrow (3.2) \\ \end{align*} \]
จะเห็นได้ว่า สมการ (3.2) อยู่ในรูปแบบของสมการ Quadratic Equation [9] แสดงว่ากราฟความสัมพันธ์ระหว่าง error กับ \(w_i \) ในกรณีนี้จะได้กราฟออกมาเป็นเส้นโค้งหรือ parabolar หากข้อมูลที่นำมาสอนตัวแบบมี features มาก จำนวน \(w_i \) ก็จะมากตาม กราฟที่ได้ก็จะแสดงออกมาเหมือนกับรูปถ้วย (รูปที่ 3)

รูปที่ 3  error surface ที่แสดงความสัมพันธ์ระหว่าง error กับ weight parameters 

จากรูป 3 จุดที่ต่ำที่สุดของกราฟคือจุดที่ให้ค่า error (ในกรณีนี้วัดด้วย MSE) น้อยที่สุด การที่จะไปสู่จุดนั้นได้ต้องอาศัยความรู้เรื่อง derivative เพื่อใช้บอกอัตราการเปลี่ยนแปลงของ \(e(w) \) เทียบกับการเปลี่ยนแปลงค่าของ \( w_i \) (\( \frac{\partial e}{\partial w}\) )เปรียบได้กับการกลิ้งลงจากเขาของลูกบอลล์สีดำเมื่อความลาดชันของพื้นเปลี่ยนแปลงไป (รูปที่ 4)

รูปที่ 4 

จากสมการ (2) เมื่อพิจารณาให้ x เป็นค่าคงที่

\[ \begin{align*} \frac{\partial e}{\partial{w}} &= \frac{\partial y^2}{\partial{w}} - \frac{\partial 2yb}{\partial{w}} - \frac{\partial 2ywx}{\partial{w}} + \frac{\partial b^2}{\partial{w}}+ \frac{\partial 2bwx}{\partial{w}}+ \frac{\partial w^2x^2}{\partial{w}} \\ \frac{\partial e}{\partial{w}} &= -2yx + 2bx + 2x^2w \\ \frac{\partial e}{\partial{w}} &= 2x(b+xw - y) \\ \frac{\partial e}{\partial{w}} &= -2x(y-(b+wx)) \\ \nabla e(w) &= -2x(y-(b+wx)) \\\\ -\nabla &= 2x(y-(b+wx)) \longrightarrow (4)\\ \end{align*} \]

สมการที่ (4) บอกเราถึงอัตราการเปลี่ยนแปลงของค่า error (e) เทียบกับการเปลี่ยนแปลงของ weight (w)  เรียกว่า gradient 


3)  การปรับแต่ง parameter (ในวิดีโอ Widrow ใช้คำว่า adaptive algorithm เขียนไว้บนกระดาน) กำหนดให้เป็น
\[ W_{\text{new}} = W_{\text{old}} + \eta (-\nabla) \longrightarrow (5) \]

สัญญลักษณ์ \(\eta \) คืออัตราการเรียนรู้ (learning rate) เป็นค่าคงที่ โดยที่ \(\eta \in \Re^ + \) และมักกำหนดให้มีค่าน้อยๆ เช่น 0.0001 เป็นต้น ในวิดีโอ Widrow กล่าวเปรียบเทียบ learning rate ไว้กับขนาดของก้าว (step size) ค่าของ learning rate มีผลต่ออัตราการเปลี่ยนแปลงของค่า parameter weight ในแต่ละ episode ของการ train การกำหนดค่า learning rate มีค่ามากเกินไปอาจทำให้การกระโดดข้าม (over shoot) ค่า error ที่น้อยที่สุดไปได้ หากมากเกินไปก็จะทำให้การเรียนรู้นั้นช้า

เครื่องหมาย \( - \) ข้างหน้า \(\nabla\) หมายถึงทิศทางของ gradient ที่เป็นขาลง เพราะเป้าหมายของเราคือการหา error น้อยที่สุด
เมื่อเรานำค่าจากสมการ (4) มาแทนลงในสมการ (5) เราจะได้
\[ \begin{align*} W_{\text{new}} &= W_{\text{old}} + \eta (2x(y-(b+wx))) \\ W_{\text{new}} &= W_{\text{old}} + 2\eta x(y-(b+wx)) \longrightarrow (6) \\ \end{align*} \]
จากสมการ (6) ขอให้สังเกตุว่า \(y-(b+wx) \) ซึ่งก็คือ \(y - \hat{y} \) หรือ error ใน learning algorithm ที่เสนอโดย Rosenblatts
\[ \begin{align*} W_{\text{new}} &= W_{\text{old}} + 2\eta e x \longrightarrow \text{Widrow and Hoff} \\ W_{\text{new}} &= W_{\text{old}} + \eta e x \longrightarrow \text{Rosenblatts} \\ \end{align*} \]
จากความสัมพันธ์นี้ เป็นเรื่องน่าสนใจอย่างมาก ที่ค่าของ gradient ที่เสนอมีค่าเป็น 2 เท่าของ error ในตัวแบบของ Rosenblatts ทำให้แทบไม่ต้องเปลี่ยนแปลง Code ที่ได้ทำไว้ก่อนหน้านี้ในเรื่องตอนที่กล่าวถึงตัวแบบของ Rosenblatts [8]

ตัวอย่าง Python Code
1. สร้าง ADALINE class
 
   
   import numpy as np
   from sklearn.utils import shuffle
   import pandas as pd
   import matplotlib.pyplot as plt
   
   class ADALINE(object):
    def __init__(self, input_size):
        # plus one means bias parameter is included in W vector
        self.W = np.zeros(input_size+1)
        self.mse = []    
    
    def activation_fn(self, x):
        return 1 if x >= 0.0 else 0
    
 
    def predict(self, x):
        if x.shape[0] < self.W.shape[0]:
            x = np.insert(x, 0, 1)
            
        z = self.W.T.dot(x)
        a = self.activation_fn(z)
        return z,a
    
    def fit(self, X, d, lr=0.001, epochs=100):
        self.lr = lr
        n = X.shape[0]
        self.mse.clear()      
        
        for epoch in range(epochs):
            se = 0.0
            for i in range(n):
                x = X[i]      
                x = np.insert(x, 0, 1)
                linear_output = self.W.T.dot(x)
                error = d[i][0] - linear_output
                gradient = 2 * error
            
                # update parameters
                self.W = self.W + lr * gradient * x
                se += np.power(error,2)
            self.mse.append(se/n)

   
   
Code ชุดนี้ดัดแปลงมาจากข้อเขียนในตอน Perceptron ตอนที่ 2 [8] ดังนั้นจะขอกล่าวถึงเฉพาะส่วนที่เปลี่ยนแปลงไปคือ

     gradient = 2 * error
     self.W = self.W + lr * gradient * x
     
มีการคำนวณ gradient โดยอ้างถึงการคำนวณจากสมการ (6) และนำค่า gradient ที่ได้ไปปรับปรุงค่าของ parameters แทน error

2) เตรียมข้อมูล
  
data_file = "datasets_weight-height.csv"
ds = pd.read_csv(data_file)

ds.loc[ds['Gender']=='Male','Gender']=1
ds.loc[ds['Gender']=='Female','Gender']=0
ds = shuffle(ds)

# standardize data
ds["Height"]=(ds["Height"]-ds["Height"].mean())/ds["Height"].std()
ds["Weight"] = (ds["Weight"]-ds["Weight"].mean())/ds["Weight"].std()

#split data
X_train = ds.iloc[:8000,[1,2]]
y_train = ds.iloc[:8000,[0]]

X_test = ds.iloc[8000:,[1,2]]
y_test = ds.iloc[8000:,[0]]
  

3) learning
    
    # learning 
adaline = ADALINE(input_size=2)
adaline.fit(np.array(X_train), np.array(y_train),lr=0.00001, epochs=200)

# print what we want to know
print(adaline.W)

>>> [ 0.49774204 -0.12760273  0.51220155]  
    
    

4) test
    
correct = 0 
n = X_test.shape[0]

for i in range(n):
    z,pred = adaline.predict(np.array(X_test.iloc[i]))
    y = y_test.iloc[i][0]
    if y - pred == 0 :
        correct += 1
print(correct/n)

>>> 0.619
    
    
ได้ความถูกต้องจากตัวอย่างทดสอบที่ 0.619 หรือประมาณ 61.9 % ก็นับว่าใช้ได้อยู่แม้จะไม่สูงมากนัก ดู MSE กันว่าจะลดลงตามที่คาดหรือไม่

รูปที่  5 แสดงการลดลงของ MSE 


รูปที่ 5 แสดงให้เห็นว่า MSE มีการลดลงจริงตามที่ Widrow ได้เสนอไว้ คำถามต่อไปคือ การเปลี่ยนค่า learning rate จะมีผลต่อ MSE อย่างไร เพื่อตอบคำถามนี้ จะลองเปลี่ยนค่าของ learning rate ดู แล้วทำการ train ตัวแบบ ADALINE ด้วยข้อมูลชุดเดียวกัน ได้ผลดังรูปที่ 6

รูปที่ 6 ผลของ learning rate ต่อ MSE

ค่าของ learning rate ที่ดีจะต้องทำให้ค่าของ error น้อยที่สุด ค่าของ learning rate ส่งผลต่อการลดลงของ error ต่างกัน จากการทดลองเล็กนี้ ทำให้ทราบว่าค่า learning rate ที่ดีในกรณีนี้คือ 0.0001

สรุป
จากตอนที่ 1, 2  เริ่มต้นจาก McCulloch & Pitts สร้างตัวแบบทางคณิตศาสตร์ของ perceptron จากเซลล์ประสาทของสิ่งมีชีวิต จากนั้นก็มีการเพิ่ม learning algorithm โดย Rosenblatts มาตอนในตอนนี้กล่าวถึง ADALINE ได้มีการเพิ่มอีกสองเรื่องเข้าไปคือ gradient เพื่อใช้ในการหาทิศทางบน error surface ที่ทำให้ error ลดลงเร็วที่สุด นอกจากนี้ก็ได้มีการกล่าวถึง learning rate เทคนิคนี้ถูกเรียกว่า gradient descent learning 

ตัวแบบ ของ Widrow & Hoff แม้จะการพัฒนาการเรียนรู้ แต่ perceptron ก็ยังคงใช้ได้แต่กับปัญหา linear classification ไม่สามารถใช้กับ non-linear classification ในตอนต่อไปจะได้กล่าวถึงการพัฒนาต้วแบบเพื่อใช้กับ non-linear กัน

เอกสารอ้างอิง

ความคิดเห็น