ตอนที่ 1 ได้กล่าวถึงองค์ประกอบหลักและlearning algorithm ของ perceptron ไปแล้ว ในตอนนี้จะนำเอาความรู้ที่ได้มาเขียน Python code เพื่อใช้แยกกลุ่มข้อมูลจริงดู จะได้เห็นภาพชัดขึ้นว่า perceptron ทำงานอย่างไร
ตัวอย่างที่ 1 เรียนรู้เพื่อแยกเพศจากส่วนสูงแลน้ำหนัก
ตัวอย่างนี้จะทำให้ perceptron ทำการเรียนรู้จากข้อมูลเพื่อให้ได้ \( \vec{W} \) ที่นำมาใช้แยกเพศของตัวอย่างได้เมื่อทราบส่วนสูงและน้ำหนัก
1. download ข้อมูลจาก https://www.kaggle.com/mustafaali96/weight-height ท่านจำเป็นต้องทำการลงทะเบียนก่อนจึงจะสามารถ download ได้ การลงทะเบียนไม่เสียเงิน ข้อมูลบันทึกในรูปแบบของ CSV file บรรจุข้อมูลความสูง (inches) และ น้ำหนัก (bounds) ของตัวอย่าง 10,000 คน เป็นชายและหญิงกลุ่มละ 5,000 คน
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.utils import shuffle
# prepare data
ds = pd.read_csv("datasets_weight-height.csv")
ds = shuffle(ds)
#males
ds.loc[ds['Gender']=='Male','Gender'] = -1
#females
ds.loc[ds['Gender']=='Female','Gender'] = 1
การทำงานเริ่มจากการอ่านข้อมูล CSV file เข้ามาแล้วทำการแยกออกเป็นสองส่วนของชาย หญิงแยกกันเก็บไว้ใน males_ds และ femals_ds ตามลำดับ
นำข้อมูลมา plot แบบ scatter โดยให้แกนตั้งเป็นส่วนสูง แกนนอนเป็นน้ำหนัก พบว่าข้อมูลมีการแบ่งกลุ่มตามเพศค่อนข้างชัดเจน กลุ่มสีเขียวแทนกลุ่มเพศชายและกลุ่มสีเหลืองแทนเพศหญิง จุดสีแดงแสดงตำแหน่งของค่ากลางของแต่ละกลุ่ม ดูรูปที่ 1
รูปที่ 1 |
ขั้นตอนต่อไปคือการสร้าง dataset 2 dataset จากข้อมูลต้นฉบับ dataset แรกเอาไว้สอน perceptron จำนวน 80 % ของทั้งหมด ที่เหลือเอามาสร้างเอาไว้ทดสอบ perceptron ว่าสามารถใช้แยกข้อมูลได้จริงหรือไม่ เหตุที่ต้องทำการแยกข้อมูลออกจากกันเพราะเราไม่ต้องการให้ perceptron ได้รู้จักข้อมูลในชุดที่ใช้ทดสอบนั้นเอง
#create training dataset
size = len(ds)
training_size = round(size * 0.8)
X_train = ds.loc[:training_size,['Heigh','Weight']]
l_train = ds.loc[:training_size,'Gender']
สร้าง perceptron class จุดที่ต้องขยายความคือ
- input_size คือ dimension หรือจำนวน features ของ input data เช่น input ที่ประกอบด้วยค่าของ ส่วนสูงและน้ำหนัก ก็จะมี input_size เป็น 2 เป็นต้น
- dimension ของ self.W ต้องมากกว่า input_size อยู่ 1 เพราะใช้แทนตำแหน่งของ bias (ดูเรื่อง linear classifier)
class Perceptron(object):
def __init__(self, input_size, lr=1, epochs=100):
self.W = np.zeros(input_size+1)
self.epochs = epochs
self.lr = lr
def activation_fn(self, x):
return 1 if x >= 0 else -1
def predict(self, x):
z = self.W.T.dot(x)
a = self.activation_fn(z)
return a
def fit(self, X, d):
for epoch in range(self.epochs):
i = np.random.choice(X.shape[0])
x = X[i]
e = 0
x = np.insert(x, 0, 1)
y = self.predict(x)
e = d[i] - y
self.W = self.W + self.lr * e * x
เริ่มต้นการเรียนรู้
# learning
# input_size parameter is dimension of input
perceptron = Perceptron(input_size=2,lr=1.0, epochs=80000)
perceptron.fit(np.array(X_train), np.array(l_train))
# print what we want to know
print(perceptron.W)
[ 188. 8804.98328662 -3648.86955358]
ค่า parameter ที่ได้ทำให้เราทราบว่าสมการที่ช่วยแยกเพศคือ
\[
\begin{align*}
g &= 188 + 8804.98h - 3648.87w \\\\
\hat{y} &=
\begin{cases}
1 & \quad \text{ if g} \geq 0 \\
-1 & \quad \text{ otherwise}
\end{cases}\\\\
gender &= \begin{cases}
\text{female} & \quad \text{ if }\hat{y} = 1 \\
\text{male} & \quad \text{ if }\hat{y} = -1
\end{cases}
\end{align*}
\]
เมื่อ h = ความสูง (inches) และ w = น้ำหนัก (bound) ยกตัวอย่างข้อมูล {'gender':'Male','Height': 73.847017,'Weight': 241.893563}
\[
\begin{align*}
g &= 188 + 8804.98 \times 73.847017 - 3648.87 \times 241.893563 \\
\hat{y} &= sign(-232228.6574791501) = -1 \\
\therefore gender &= \text{male}
\end{align*}
\]
นำสมการที่ได้ไป plot ดูจะได้ตามรูปที่ 2 คือสมการเส้นตรงดูแล้วน่าจะเหมาะสมกับการแบ่งกลุ่มข้อมูล นำมาทดสอบกับข้อมูลที่เตรียมไว้แล้วดูผล
X_test = np.array(ds.loc[testing_size:,['Height','Weight']])
l_test = np.array(ds.loc[testing_size:,'Gender'])
corect = 0
for i in range(X_test.shape[0]):
x = np.insert(X_test[i],0,1) # insert 1 to dimension 0
pred = perceptron.predict(x)
if pred == l_test[i] :
corect+=1
print(corect/test_size)
0.9195
ตัวเลข 0.9195 ตีความได้ว่าตัวแบบสามารถแยกเพศจากข้อมูลความสูงและน้ำหน้กได้ถูกต้องประมาณ 91 % ของตัวอย่างที่นำมาทดสอบจำนวน 2000 ตัวอย่าง
รูปที่ 2 |
ตัวอย่างที่ 2 : เรียนรู้เพื่อช่วยวินิจฉัย Breast cancer
ข้อมูลนี้เป็นของ University of Wisconsin Hospitals, Madison โดย Dr. William H. Wolberg จากเว็บ https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/ ในเว็บจะมีไฟล์หลายไฟล์ เลือกดาวน์โหลด 2 ไฟล์คือ
- breast-cancer-wisconsin.data เป็นไฟล์เก็บข้อมูลดิบ
- breast-cancer-wisconsin.names คำอธิบายประกอบข้อมูล
หมายเหตุ
หลังจาก download file มาแล้ว ผมเปลี่ยนนามสกุลไฟล์จาก .data ไปเป็น .csv เพราะว่าได้นำมาเติม column header เข้าไปเพื่อความสะดวกในการทำเป็น pandas.dataFrame
Variable name | Full Name | Domain |
CT | Clump Thickness | 1-10 |
UCSi | Uniformity of Cell Size | 1-10 |
UCSh | Uniformity of Cell Shap | 1-10 |
MAd | Marginal Adhesion | 1-10 |
SECS | Single Epithelial Cell Size | 1-10 |
BNu | Bare Nuclei | 1-10 |
BCh | Bland Chromatin | 1-10 |
NNu | Normal Nucleoli | 1-10 |
NNu | Mitoses | 1-10 |
Variable name | Full Name | Domain |
CT | Clump Thickness | 1-10 |
UCSi | Uniformity of Cell Size | 1-10 |
UCSh | Uniformity of Cell Shap | 1-10 |
MAd | Marginal Adhesion | 1-10 |
SECS | Single Epithelial Cell Size | 1-10 |
BNu | Bare Nuclei | 1-10 |
BCh | Bland Chromatin | 1-10 |
NNu | Normal Nucleoli | 1-10 |
NNu | Mitoses | 1-10 |
bc_ds = pd.read_csv("breast-cancer-wisconsin.csv")
# ID is not needed
bc_ds = bc_ds.drop(columns=['ID'])
# shuffer dataframe
bc_ds = shuffle(bc_ds)
# show sample data
bc_ds.head()
จะเห็นว่าข้อมูลมี 9 features ซึ่งไม่สามารถนำไปทำ scatter plot ได้ในคราวเดียวอย่างตัวอย่างแรก (ต้องแยกไป plot ทีละคู่) ใน column สุดท้าย เป็นข้อมูลระบุ class ของแต่ละรายการ มีเพียงสอง class คือ 4 = malignant case และ 2 = benign case จึงสามารถนำเอา perceptron มาใช้ได้ โดยจะทำการเปลี่ยนตัวเลขให้สอดคล้องกับตัวแบบคือ class 2 เปลี่ยนเป็น -1 และ class 4 เป็น 1 แล้วทำการแยกส่วนข้อมูลเป็น training dataset และ testing dataset
bc_ds.loc[bc_ds['Classes']==2,'Classes'] = -1
bc_ds.loc[bc_ds['Classes']==4,'Classes'] = 1
cols = ['CT','UCSi','UCSh','MAd','SECS','BNu','BCh','NNu','Mitoses']
train_size = round(len(bc_ds) * 0.8)
X_train = np.array(bc_ds.loc[:train_size,cols])
l_train = np.array(bc_ds.loc[:train_size,'Classes'])
X_test = np.array(bc_ds.loc[train_size:,cols])
l_test = np.array(bc_ds.loc[train_size:,'Classes'])
Training กำหนดให้ทำการ train 80000 รอบ
# learning
bc_perceptron = Perceptron(input_size=9,lr=1.0, epochs=80000)
bc_perceptron.fit(X_train, l_train)
# print what we want to know
print(bc_perceptron.W)
[-774. 36. -4. 8. 46. -18. 42. 40. 34. 72.]
ค่า parameter ที่ได้ทำให้เราทราบว่าสมการที่ใช้แยกข้อมูลระหว่าง malignant กับ benign คือ
\[
\begin{align*}
c &= 36CT-4UCSi+8UCSh+46MAd-18SECS+42BNu +40BCh+34NNu+72NNu - 774 \\\\
\hat{y} &=
\begin{cases}
1 & \quad \text{if c} \geq 0 \\
-1 & \quad \text{otherwise}
\end{cases} \\\\
class &=
\begin{cases}
\text{benign} & \quad \text{if }\hat{y} = -1 \\
\text{malignant} & \quad \text{if }\hat{y} = 1
\end{cases}
\end{align*}
\]
ยกตัวอย่าง ถ้าผู้ป่วยรายหนึ่งมีข้อมูลดังนี้
Variable name | Full Name | Value |
CT | Clump Thickness | 8 |
UCSi | Uniformity of Cell Size | 7 |
UCSh | Uniformity of Cell Shap | 4 |
MAd | Marginal Adhesion | 4 |
SECS | Single Epithelial Cell Size | 5 |
BNu | Bare Nuclei | 3 |
BCh | Bland Chromatin | 5 |
NNu | Normal Nucleoli | 10 |
NNu | Mitoses | 1 |
\[
\begin{align*}
c &= sign(
(36\times 8)-
(4 \times 7) +
(8 \times 4) +
(46 \times 4)-
(18 \times 5) +
(42 \times 3) +
(40\times 5)+
(34 \times 10)+
(72 \times 1) - 774) \\
c &= sign(350) = 1 \\
\therefore class &= \text{malignant}
\end{align*}
\]
ทดสอบตัวแบบกับ testing dataset
# testing
bc_test = np.array(bc_ds.loc[train_size:,cols])
bc_label = np.array(bc_ds.loc[train_size:,'Classes'])
bc_test = np.insert(bc_test,0,1,axis=1)
correct = 0
for i in range(bc_test.shape[0]):
pre = bc_perceptron.predict(bc_test[i])
if bc_label[i] == pre :
correct += 1
print(correct/bc_test.shape[0])
0.975609756097561
ได้ความแม่นยำประมาณ 97 % ถือว่าดีทีเดียว
สรุป
ในตอนนี้เป็นการนำเอาความรู้ในเรื่อง perceptron มาใช้ทำ binary classification ของข้อมูลจริงในสองรูปแบบคือแบบที่มี feature เพียงสอง คือ ส่วนสูงกับน้ำหนัก และอีกแบบคือข้อมูลที่มี feature มากกว่า 2 จะเห็นได้ว่า perceptron ทำงานได้ดีในรูปแบบปัญหา binary classification แต่ในรูปแบบปัญหาที่ซับซ้อนกว่านี้ลำพังตัวแบบ perceptron จะไม่สามารถใช้งานได้ จึงต้องมีการกล่าวถึงในตอนต่อไปคือ multiplayer perceptron
ความคิดเห็น
แสดงความคิดเห็น