K-NN กับ Sklearn : Machine Learning 101

Mr.P L
Mr.P L
Sep 7, 2018 · 5 min read

มาหาเพื่อนบ้านใกล้เคียงของดอกไม้ชนิดต่างๆกัน

จากบล็อกก่อนหน้านี้ ที่มี Intro เกี่ยวกับ Machine Learning เรียบร้อย

โดย K-NN เป็น Machine Learning ประเภท Supervised แบบ Classification นั้นคือ จำเป็นจะต้องมี Data set ที่มีเฉลย(Label)ให้ด้วย

อ่อ ไม่เกี่ยวกับ knn อันนี้นะ

K-Nearest Neighbour (K-NN)

แปลตรงๆก็คือ เพื่อนบ้านที่ใกล้เคียงที่สุด K ตัว หมายความว่า เวลาทำนาย มันจะดึงข้อมูลที่ใกล้เคียงตัวมันเองที่สุดมา K ตัว แล้วมานับจำนวนกันว่าเพื่อนบ้านที่ใกล้เคียงของมันทั้งหมด K ตัว เป็น Class อะไรมากที่สุดนั้นเอง

หาเพื่อนบ้านใกล้เคียงนั้นเอง

โดยหลักการเบื้องต้นง่ายๆ

โดยขั้นตอนแรกให้เทรนโมเดล K-NN เมื่อเทรนเสร็จตัวโมเดลจะนำเอาข้อมูลที่เรา train ไปเก็บไว้ในหน่วยความจำก่อนเป็นอย่างแรก จากนั้นก็จะเข้าสู่วิธีการ predict

https://www.kdnuggets.com/2016/01/implementing-your-own-knn-using-python.html

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)
คำอธิบาย dataset ชุดนี้

ทีนี้มาดู 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))
Train 80% และ Test 20%

ทีนี้ได้เวลาสร้างโมเดล 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 นั้นใกล้กันเกินไป ทำให้ทำนายผิดพลาด

ภาพรวมอยู่ที่ 0.97 ดีมากๆ แต่ยังดีได้มากกว่านี้

แต่…. เราสามารถทำให้มันแม่นยำ 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 ที่ดีที่สุด

ค่าเพื่อนบ้านที่ดีที่สุดคือ “4” นั้นเองไม่ใช่ 1

ทีนี้ลองทดสอบโมเดลกับโมเดลที่ดีที่สุดของเรากัน

answer = knn_best.predict(X_test)
print(classification_report(y_test, answer))

จะได้ว่าค่าทุกอย่างเป็น 100% แล้วนั้นเอง

ได้ 100% ทั้งหมดเลย

ข้อดี K-NN

  • ทำง่ายมาก ไม่ต้องมีสมการคณิตศาสตร์เข้ามายุ่งมาก
  • เหมาะกับข้อมูลง่ายๆ ไม่ซับซ้อน

ข้อเสีย K-NN

  • ไม่ใช่ statistical model จริงๆ เพราะตอน train แค่นำข้อมูลไปเก็บไว้ ไม่ได้เรียนรู้จากข้อมูลเลย ทำให้ !! หากนำมาใช้กับรูป หากเรามี data set รูปแมว แต่แมวหันหน้าไปด้านซ้ายทั้งหมด แต่ตอนทำนาย แมวหันหน้าไปด้านขวา K-NN ของเราจะแยกไม่ออกทันทีว่าเป็นแมว
  • ช้ามาก ถ้าเทียบกับโมเดลอื่นๆ (Logistic,Decision)

สรุปผลการทดลอง

K-NN เป็นโมเดลที่เหมาะสำหรับผู้เริ่มต้น เพราะไม่ต้องปรับค่า hyper parameter อะไรมากนัก ไม่มีโมเดลคณิตศาสตร์เท่าไร แต่ช้าเพราะต้องนำข้อมูลไปเก็บไว้ในหน่วยความจำ

บทความถัดไป :

บทความก่อนหน้า เริ่มเรียน 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/

บทความนี้เป็นส่วนหนึ่งของบทความ

mmp-li

Life style of Programmer, Machine Learning, Deep Learning and new trend of technology

Mr.P L

Written by

Mr.P L

Life Style of Programmer & IoT (Node-RED|Blynk) & Data Science (ML,DL) and What ever I want to do | cat can coding too | PhD -> VISTEC -> IST

mmp-li

mmp-li

Life style of Programmer, Machine Learning, Deep Learning and new trend of technology

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade