K-NN กับ Sklearn : Machine Learning 101

Mr.P L
mmp-li
Published in
5 min readSep 7, 2018

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

จากบล็อกก่อนหน้านี้ ที่มี 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 อะไรมากนัก ไม่มีโมเดลคณิตศาสตร์เท่าไร แต่ช้าเพราะต้องนำข้อมูลไปเก็บไว้ในหน่วยความจำ

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

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/

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

เริ่มเรียน Machine/Deep Learning 0–100 (Introduction)

--

--

Mr.P L
mmp-li
Editor for

Lifestyle of Programmer & IoT (Node-RED|Blynk) & Data Science (ML,DL,NLP) and Whatever I want to do | cat can coding too | Ph.D. -> VISTEC -> IST