มาหาเพื่อนบ้านใกล้เคียงของดอกไม้ชนิดต่างๆกัน
จากบล็อกก่อนหน้านี้ ที่มี Intro เกี่ยวกับ Machine Learning เรียบร้อย
โดย K-NN เป็น Machine Learning ประเภท Supervised แบบ Classification นั้นคือ จำเป็นจะต้องมี Data set ที่มีเฉลย(Label)ให้ด้วย
K-Nearest Neighbour (K-NN)
แปลตรงๆก็คือ เพื่อนบ้านที่ใกล้เคียงที่สุด K ตัว หมายความว่า เวลาทำนาย มันจะดึงข้อมูลที่ใกล้เคียงตัวมันเองที่สุดมา K ตัว แล้วมานับจำนวนกันว่าเพื่อนบ้านที่ใกล้เคียงของมันทั้งหมด K ตัว เป็น Class อะไรมากที่สุดนั้นเอง
โดยหลักการเบื้องต้นง่ายๆ
โดยขั้นตอนแรกให้เทรนโมเดล K-NN เมื่อเทรนเสร็จตัวโมเดลจะนำเอาข้อมูลที่เรา train ไปเก็บไว้ในหน่วยความจำก่อนเป็นอย่างแรก จากนั้นก็จะเข้าสู่วิธีการ predict
1. สังเกตุข้อมูล
นำเอาข้อมูลที่เราต้องการ predict มาวางไว้บนกราฟ (ข้อมูลที่ฝึกฝนก็เช่นกัน) โดยเราจะเห็นว่าข้อมูลฝึกฝนจะเป็นวงกลมสีเทา ในกราฟก็จะมีข้อมูลชนิดอื่นๆอยู่ด้วย
2. หาระยะห่างระหว่างข้อมูล
หาระยะห่างระหว่างวงกลมสีเทากับวงกลมอื่นๆนั้นเอง โดยการใช้สมการหาระยะทาง
โดยแต่ละวงกลมก็จะมีระยะห่างระหว่างวงเป็นของตัวเอง
3. หาเพื่อนบ้านใกล้เคียง
เมื่อมีระยะระหว่างวงกลมแล้วก็เอามาเรียงว่าอันไหนใกล้ที่สุดและอันไหนไกลที่สุดโดยเราจะให้น้ำหนักของคลาสที่ใกล้ที่สุดมาก่อนนั้นเอง
4. นับจำนวนเพื่อนบ้าน
ให้เรากำหนดว่าต้องการเพื่อนบ้านกี่คน (ค่า K) หากต้องการ 3 คนเราก็จะมาดูว่า 3 อันที่ใกล้ที่สุดเนี่ยเป็นคลาสอะไรมากสุด ซึ่งจากตัวอย่างคลาสสีเหลืองก็จะชนะไป ซึ่งหมายความว่าจุดสีเทานั้นเป็นคลาสสีเหลืองนั้นเอง
หากในกรณีที่เพื่อนบ้านเท่ากันทุกคลาสก็จะเลือกเพื่อนบ้านที่ใกล้ที่สุดนั้นเอง
เริ่มต้นทดลอง
ในส่วนของโค้ดนั้น เราไม่จำเป็นต้องเขียนโค้ดที่ให้ข้อมูลลงหน่วยความจำเอง หรือ เขียนสมการหาระยะทางเอง เพราะ Sklearn นั้นเตรียมมาให้หมดแล้ว
โดยโค้ดทั้งหมดในบล็อกนี้อยู่ใน Github หมดแล้วสามารถโหลดมาดูประกอบได้ โดยเป็นไฟล์ Jupyter หรือจะทำไปพร้อมๆกันก็ได้
โดย Dataset ที่เลือกมาทดสอบรอบนี้คือ IRIS Data set เป็น Data set ยอดนิยมอีกตัวหนึ่ง โดยในตัว Data set นั้นจะประกอบไปด้วยข้อมูลของดอกไม้ 3 ชนิดคือ Iris Versicolor , Iris Setosa , Iris Virginica
โดยทั้ง 3 ถ้ามองด้วยตาแทบจะแยกไม่ออก แต่การจะแยกพวกมันออกมาได้นั้น จะใช้ข้อมูลอยู่ 2 อย่างคือ Petal และ Sepal โดยเราจะแยกดอกไม้ด้วยการนำ feature ที่เด่นชัดได้แก่ Petal lenght และ Pental width นั้นเอง
โดย dataset ชุดนี้อยู่ภายใน sklearn อยู่แล้วสามารถเรียกมาใช้จาก sklearn ได้เลย
%config IPCompleter.greedy = True
from sklearn import datasets
data = datasets.load_iris()
#อธิบาย dataset
print(data.DESCR)
ทีนี้มาดู Label ข้อมูลของเรากันตามโค้ดด้านล่าง
data.target #0 = Setosa 1 = Versicolour 2 = Virginica
ทีนี้เราจะทำการตรวจสอบข้อมูลของเราก่อนว่าข้อมูลแต่ละ Label เป็นข้อมูลที่ดีพอจะใช้งาน โดยเราจะพล็อตข้อมูลของเราบนกราฟ โดยถ้าข้อมูลของเราดีพอที่จะใช้งาน ข้อมูลแต่ละ Label/Class จะแยกออกจากกันอย่างชัดเจนนั้นเอง
โดยเริ่มจากข้อมูลของ petal width กันก่อน โดยให้แกน X เป็นจำนวนข้อมูลต่อคลาส ส่วนแกน Y เป็นข้อมูลของเรานั้นเอง
import matplotlib.pyplot as plt
%matplotlib inline
x = range(50)
plt.scatter(x,data.data[:50,3],color=’red’) #petal width of Setosa
plt.scatter(x,data.data[50:100,3],color=’blue’) #petal width of Versicolour
plt.scatter(x,data.data[100:,3],color=’green’) #petal width of Virginica
จากนั้นเช็ค pental length
plt.scatter(x,data.data[:50,2],color=’red’) #petal length of Setosa
plt.scatter(x,data.data[50:100,2],color=’blue’) #petal length of Versicolour
plt.scatter(x,data.data[100:,2],color=’green’) #petal length of Virginica
จะพบว่าข้อมูลของเราถูกแบ่งอย่างชัดเจน ไม่ผิดเพี้ยนอาจจะมีบางจุดที่ทับกันบางแต่ก็ไม่เป็นไร เรามีทางแก้
ขั้นตอนต่อไป คือการแบ่งข้อมูลเพื่อกันไม่ให้โมเดลเรียนรู้มากเกินไป (Overfitting) โดยมีอธิบายไว้ละเอียดแล้วในบทความนี้
โดยทำการแบ่งข้อมูลด้วยฟังก์ชั่น train_test_split แยกข้อมูลออกเป็น 2 ส่วน
- ส่วนสำหรับการฝึกฝน เป็นจำนวน 80% ของข้อมูลทั้งหมด (X_train,y_train)
- ส่วนสำหรับการทดสอบ เป็นจำนวน 20% ของข้อมูลทั้งหมด (X_test,y_test)
โดยการแบ่งข้อมูลทั้ง 2 ชุดจะไม่มีการซ้ำกันเกิดขึ้นแน่นอน
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(data.data[:,2:4], data.target, test_size=0.2, random_state=42,stratify=data.target)
print(‘Train Shape X: {} Y : {}’.format(X_train.shape,y_train.shape))
print(‘Test Shape X: {} Y : {}’.format(X_test.shape,y_test.shape))
ทีนี้ได้เวลาสร้างโมเดล K-NN แล้วด้วยการเรียก Library KNeighborsClassifier
โดยเราจะกำหนดเพื่อนบ้านไว้ที่ 1 คนก่อนเพื่อความเข้าใจตรงกันว่า
เราจะเลือกเพื่อนบ้านที่ใกล้เคียงกับข้อมูลของเราเพียง 1 คนเท่านั้น
โดยการกำหนด n_neighbors นั้นเอง
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors = 1)
จากนั้นฝึกฝนโมเดลด้วยคำสั่ง fit(X,y) โดยให้ใส่ข้อมูลฝึกฝนที่ X และเฉลยที่ y
knn.fit(X_train,y_train)
ทีนี้ทำการทดสอบโมเดลโดยการนำชุดข้อมูลทดสอบเข้าไปทำนายด้วยคำสั่ง predict() โดยรอบนี้เราไม่ต้องใส่เฉลยเข้าไป ให้ใส่แค่ข้อมูลที่ต้องการทดสอบ
answer = knn.predict(X_test)
ทีนี้ได้เวลาทำ Evaluate-Test นั้นคือการตรวจสอบว่าโมเดลของเราแม่นยำขนาดไหนและดีจะใช้งานหรือไม่ โดยสามารถอ่านเพิ่มเติมได้ที่บทความนี้
โดยเราจะใช้ classification_report ที่สามารถดึงค่า precision , recall , f1-score จากการเทียบระหว่างสิ่งที่โมเดลทำนายกับเฉลยได้เลย
from sklearn.metrics import classification_report
print(classification_report(y_test, answer))
ทีนี้เมื่อเรา print() ออกมาก็จะได้ precision , recall , f1-score ของแต่ละคลาสออกมาเลย โดยจะสังเกตุได้ว่าคลาส 0 เราไม่มีปัญหาเลยเพราะถ้าย้อนไปดูกราฟจะพบว่ามันแยกออกมาจาก 1,2 อย่างชัดเจน แต่ข้อมูล 1,2 นั้นใกล้กันเกินไป ทำให้ทำนายผิดพลาด
แต่…. เราสามารถทำให้มันแม่นยำ 100% ได้ !
ถ้าเราสังเกตุกราฟที่เราเคยพล็อตจะพบว่าสีเขียวกับสีฟ้าข้อมูลมันจะทับกัน เราจะทำให้ข้อมูลมันออกห่างกันยังไง ?
คำตอบคือการ Scale
การ Scale คือการปรับ Variance และ mean ของข้อมูลให้มีค่ากลางมากขึ้นอ่านเพิ่มเติมได้ที่บทความนี้
ทำด้วยการเรียก StandardScaler จาก sklearn จากนั้นก็แปลงค่าด้วยคำสั่ง fit_transform() นั้นเอง
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X = scaler.fit_transform(data.data[:,2:4])
จากนั้นให้ทำการแบ่งข้อมูลอีกครั้ง
X_train, X_test, y_train, y_test = train_test_split(X, data.target, test_size=0.2, random_state=42,stratify=data.target)
แล้วทดสอบกับโมเดลเดิมและประเมินโมเดลอีกรอบ
knn = KNeighborsClassifier(n_neighbors = 1)
knn.fit(X_train,y_train)#ฝึกโมเดล#ทดสอบโมเดล
answer = knn.predict(X_test)
print(classification_report(y_test, answer))
สาเหตุที่มันยังไม่ 100% ก็เพราะว่าจำนวนเพื่อนบ้านของเรามันยังไม่ถูกต้อง เราจะปรับค่าเพื่อนบ้านให้ถูกต้องก่อน แล้วจะทำยังไงละ ? ปรับค่าจาก 1–100 ทีละค่าและประเมินโมเดลทีละครั้งก็คงไม่ไหว
เราสามารถหาค่าเพื่อนบ้านที่ถูกต้อง (เราเรียกค่าพวกนี้ว่า Hyper parameter) ก่อนโดยใช้ GridSearchCV โดย GrideSearchCV จะทำการหาตั้งแต่ n จนถึง m นั้นเอง เราเรียกกระบวนการปรับค่า Hyper parameter แบบนี้ว่า Tuning Machine Learning Model / Tune Model / Tune Hyper parameter (แล้วแต่จะสดวกเรียก)
โดยจากตัวอย่างเราให้หาเพื่อนบ้าน (n_neighbors) ตั้งแต่ 1 จนถึง 10 โดยค่าข้างใน GridesearchCV ประกอบด้วย
- Machine Learning Model ที่เราต้องการปรับ (knn)
- Hyper parameter ที่ต้องการปรับและ range ที่ต้องการทดสอบ (parameters)
- จำนวนรอบการทดสอบต่อ 1 parameter (cv)
from sklearn.model_selection import GridSearchCV
parameters = {‘n_neighbors’: range(1,11)}
knn_best = GridSearchCV(knn, parameters, cv=5)
knn_best.fit(X_train,y_train)
knn_best.best_estimator_
โดยเมื่อรันโค้ดนี้แล้ว เราจะได้ Best parameter สำหรับข้อมูลชุดนี้ออกมา หากเราใช้ข้อมูลอย่างอื่น เราก็ต้องทำ GrideSearchCV ใหม่อีกครั้งเพื่อหา parameter ที่ดีที่สุด
ทีนี้ลองทดสอบโมเดลกับโมเดลที่ดีที่สุดของเรากัน
answer = knn_best.predict(X_test)
print(classification_report(y_test, answer))
จะได้ว่าค่าทุกอย่างเป็น 100% แล้วนั้นเอง
ข้อดี K-NN
- ทำง่ายมาก ไม่ต้องมีสมการคณิตศาสตร์เข้ามายุ่งมาก
- เหมาะกับข้อมูลง่ายๆ ไม่ซับซ้อน
ข้อเสีย K-NN
- ไม่ใช่ statistical model จริงๆ เพราะตอน train แค่นำข้อมูลไปเก็บไว้ ไม่ได้เรียนรู้จากข้อมูลเลย ทำให้ !! หากนำมาใช้กับรูป หากเรามี data set รูปแมว แต่แมวหันหน้าไปด้านซ้ายทั้งหมด แต่ตอนทำนาย แมวหันหน้าไปด้านขวา K-NN ของเราจะแยกไม่ออกทันทีว่าเป็นแมว
- ช้ามาก ถ้าเทียบกับโมเดลอื่นๆ (Logistic,Decision)
สรุปผลการทดลอง
K-NN เป็นโมเดลที่เหมาะสำหรับผู้เริ่มต้น เพราะไม่ต้องปรับค่า hyper parameter อะไรมากนัก ไม่มีโมเดลคณิตศาสตร์เท่าไร แต่ช้าเพราะต้องนำข้อมูลไปเก็บไว้ในหน่วยความจำ
บทความถัดไป :
Under Fitting / Over Fitting ปัญหาที่มองไม่เห็นแต่สัมผัสได้ว่ามี…..
บทความก่อนหน้า เริ่มเรียน Machine Learning 0–100 (Introduction)
Github : https://github.com/peeratpop/Machine_Learning_101
Medium : https://medium.com/@pingloaf
Linkedin : https://www.linkedin.com/in/peerat-limkonchotiwat/