การเขียน K-NN ด้วย Python
สวัสดีผู้อ่านทุกท่านนะครับ ในบทความนี้ผมจะพาทุกท่านมาทำความรู้จักกับ K-NN หรือ K-Nearest Neighbors ว่ามันคืออะไร ทำงานยังไง เหมาะกับงานประเภทไหน แล้วถ้าเราอยากจะใช้งานมัน จะเขียนขึ้นมาใช้เองยังไง
1. K-NN คืออะไร
K-NN ย่อมาจาก K-Nearest Neighbors เป็น Machine Learning ประเภทหนึ่ง (แล้ว Machine Learning คืออะไรละ ถ้าใครสงสัยเข้าไปอ่านได้ที่นี่นะครับ “ทำความรู้จักกับ Machine Learning”) ที่เอาไว้ใช้กับงานประเภท Classification หรือจำแนกประเภทของข้อมูล โดยดูจากข้อมูลที่อยู่ใกล้ตัวมันมากที่สุด K ตัว (ตัว K คือตัวเลขจำนวนเต็มที่เราต้องกำหนดเอง เช่น 1, 2, 5, 10) ตัวอย่างเช่น
ในรูปที่ 1 เรามีข้อมูลอยู่ 2 กลุ่มได้แค่ Class A (รูปดาว 5 แฉกสีแดง) และ Class B (รูป 3 เหลี่ยมสีเขียว) แล้วบังเอิญว่าเรามีข้อมูลเข้ามาใหม่ 1 ตัว ซึ่งก็คือรูป 4 เหลี่ยมสีเหลือง โจทย์ปัญหาก็คือ เราควรจะจัดว่ามันอยู่กลุ่มไหนดี
วิธีการจำแนกแบบ K-NN ก็คือ นับจำนวนข้อมูลที่อยู่ใกล้มันที่สุด K ตัว แล้วดูว่าในข้อมูลพวกนั้น เป็นประเภทไหนมากที่สุด เช่น
ถ้าเรากำหนดให้ K=3 คือจะดูข้อมูลที่ใกล้มันมากที่สุดแค่ 3 ตัวเท่านั้น ผลลัพธ์ที่ได้ก็คือ วงกลมที่เป็นเส้นประด้านในของรูปที่ 1 ซึ่งจะเห็นว่าในวงกลมนั้นมี Class A อยู่ 1 ตัวและ Class B อยู่ 2 ตัว ดังนั้น ข้อมูลใหม่ที่เป็น 4 เหลี่ยมสีเหลืองนั้น คือ Class B
แต่ถ้าเรากำหนดให้ K=7 คือจะดูข้อมูลใกล้มันที่สุด 7 ตัว ผลลัพธ์ที่ได้ก็คือวงกลมเส้นประด้านนอก ซึ่งจะเห็นว่ามี Class A อยู่ในวงกลม 4 ตัว ส่วน Class B มีอยู่แค่ 3 ตัว ดังนั้น ข้อมูลชุดใหม่นี้ก็จะถูกจัดให้เป็นประเภท Class A
พออ่านถึงตรงนี้ หลายคนก็น่าจะเริ่มมีคำถามแล้วว่า แล้วเราควรจะกำหนด K ให้เป็นเท่าไรดีละ ดังนั้นต่อไปเรามาดูวิธีการหาค่า K ที่ดีที่สุดกันครับ
2. การหาค่า K ที่ดีที่สุด
แน่นอนว่ามันมีฟังก์ชันสำหรับการคำนวณหาค่า K ที่ดีที่สุด อย่างเช่นการใช้ GridSearCV แต่ผมไม่แนะนำหรอกครับ เพราะผมคิดว่ามันยากกว่าที่เราจะคำนวณหาเองตรงๆ ซะอีก และวิธีการคำนวณหาค่า K ที่ดีที่สุด ก็ไม่ได้ยากอะไรเลย คุณก็แค่ลองเปลี่ยนค่า K ไปเรื่อยๆ แล้วก็ดูว่าค่า K เท่าไรที่ให้ผลลัพธ์ถูกต้องที่สุด แค่นั้นเอง
บางคนอาจจะคิดว่า ถ้า K มีเป็น 100 หรือเป็น 1000 ค่าแล้วจะทำไง จะไม่ต้องมานั่งเปลี่ยนค่า K จนเมื่อยมือเหรอ? แน่นอนว่าคุณจะเจอปัญหานี้ ถ้าคุณไม่รู้จักการ “วนลูป” ซึ่งมันมีอยู่ในโปรแกรมทุกภาษา เอาละลองมาดูตัวอย่างกันครับ
โดยหลักการก็คือ สมมุติว่าเรามีข้อมูล Class A และ Class B อย่างละ 100 ตัว ก่อนจะเทรนเราก็ต้องแบ่งข้อมูล ออกเป็น 2 ชุด คือ ชุดที่ใช้เทรน และชุดที่ใช้ทดสอบ ซึ่งเราจะใช้ข้อมูลทดสอบนี่แหละครับ มาคำนวณหาค่า K ที่ดีที่สุด เพราะว่า….
เรารู้ว่าข้อมูลทดสอบทุกตัวคือ Class อะไร แต่ตัว K-NN ไม่รู้
ดังนั้นเราก็ลองกำหนด K=1 ดูก่อน แล้วให้ K-NN จำแนกประเภทข้อมูลทดสอบ แล้วเราก็ดูผลลัพธ์ความแม่นยำ จากนั้นก็วนลูปเปลี่ยนค่า K ไปเรื่อยๆ แล้วก็เก็บค่า K ที่มีความแม่นยำสูงที่สุดเอาไว้ แค่นี้เราก็จะรู้แล้วครับว่าควรจะใช้ K เท่าไรดี
เอาละครับ ตอนนี้เรารู้จักหลักการของ K-NN กันแล้ว ลำดับต่อไป เราก็จะมาเริ่มเขียน K-NN กันเลย
3. เริ่มต้นเขียน K-NN
จริงๆ แล้วใน Python มันก็มีตัวอย่างข้อมูลเอาไว้ให้เราได้ลองทดสอบดูแหละครับ แต่ในบทความนี้ ผมจะไม่ใช้ข้อมูลพวกนั้นหรอก เพราะถ้าใช้ข้อมูลสำเร็จรูปพวกนั้น คุณก็จะเตรียมข้อมูลเองไม่เป็น พอเตรียมข้อมูลเองไม่เป็น คุณก็เอา K-NN ไปประยุกต้ใช้ไม่ได้ ดังนั้นในตัวอย่างนี้ ผมจะพาคุณเริ่มเตรียมข้อมูลตั้งแต่ต้นกันเลย ซึ่งสิ่งที่คุณต้องทำมีดังนี้
- กำหนดโจทย์ปัญหา
- เก็บข้อมูลตัวอย่าง
- นำข้อมูลเข้ามาในโปรแกรม
- เขียนโปรแกรมเทรน K-NN
- เขียนโปรแกรมทดสอบ K-NN
เอ๊ะ คุณพูดว่าอะไรนะ….
- ไม่มีเวลาทำ
- เขียนโปรแกรมไม่เก่ง
- อยากได้โปรแกรมที่พร้อมใช้งานเลยทันที
ไม่มีปัญหาครับ ลองติดต่อผมมาสิ ได้งานรวดเร็วทันใจ และคิดราคาตามความยากง่ายของงาน รับรองว่าถูกกว่าจ้างบริษัทใหญ่ๆ ทำแน่นอน สนใจใช่ไหม คลิกที่นี่เลยครับ ติดต่อว่าจ้างเขียนโปรแกรม
เอาละ ถ้าคุณไม่ติดปัญหาข้างต้น งั้นลำดับต่อไป เราก็มาเริ่มเขียนโปรแกรมกันเลยครับ
ขั้นตอนที่ 1 กำหนดโจทย์ปัญหา
โจทย์ปัญหาที่ผมจะใช้ทำเป็นตัวอย่างในบทความนี้ เป็นโจทย์ที่ผมคิดขึ้นมาเองนะครับ นั่นก็คือ “โพสต์ขายของออนไลน์อย่างไรให้ขายดี” โดยข้อมูลในตารางด้านล่างนี้เป็นข้อมูลจริงที่ผมไปเก็บมาจากเว็บ Aliexpress.com โดยใช้คำค้นหาว่า “เสื้อกันหนาว” (สมมุติว่าเราอยากจะขายเสื้อกันหนาว) เราควรจะโพสต์อย่างไร เพื่อให้ขายดี เราก็ต้องไปดูโพสต์คนที่เขาขายดี และขายไม่ดีนั่นแหละครับ เราถึงจะพอเห็นแนวทาง
ซึ่งผมได้ลองกำหนดคุณสมบัติของโพสต์ออกมา 4 อย่าง คือ จำนวนภาพประกอบ, จำนวนวีดีโอ, คำอธิบายสินค้า, ราคา (ซึ่งจริงๆ ต้องมีมากกว่านี้) แต่ผมจะลองใช้ข้อมูลทั้ง 4 อย่างนี้ เพื่อคาดการณ์ยอดขาย โดยแบ่งยอดขายออกเป็น 3 กลุ่มได้แก่
กลุ่มที่ 1 แทนด้วยเลข 1 คือ กลุ่มที่มียอดขายน้อย
กลุ่มที่ 2 แทนด้วยเลข 2 คือ กลุ่มที่มียอดขายปานกลาง
กลุ่มที่ 3 แทนด้วยเลข 3 คือ กลุ่มที่มียอดขายสูง
ขั้นตอนที่ 2 เก็บข้อมูลตัวอย่าง
ขั้นตอนนี้เป็นขั้นตอนที่ค่อนข้างเสียเวลาในการทำพอสมควรครับ ยิ่งถ้าเราอยากให้โปรแกรมมีความแม่นยำ ก็ยิ่งต้องเก็บข้อมูลจำนวนมาก แต่ในตัวอย่างนี้ผมใช้ข้อมูลแค่ 20 ชุดเท่านั้น เพราะต้องการแค่ข้อมูลที่จะนำมาใช้เป็นตัวอย่างในการเขียนโปรแกรมเท่านั้น
หมายเหตุ จริงๆ แล้วข้อมูลที่ใช้เทรน ควรจะมีจำนวนเยอะมาก อย่างน้อยก็หลักร้อย หรือหลักพัน แต่ในตัวอย่างนี้ผมใช้ข้อมูลแค่ 20 ชุดเท่านั้นครับ เพราะต้องการเอาข้อมูลมาใช้เป็นตัวอย่างในการเขียนโปรแกรมเท่านั้น ดังนั้นผลที่ออกมากก็อาจจะไม่ค่อยแม่นยำเท่าไร ถ้าอยากให้ความแม่นยำเพิ่มขึ้น ก็ต้องใช้จำนวนข้อมูลเยอะขึ้นครับ
ขั้นตอนที่ 3 นำข้อมูลเข้าโปรแกรม
ในการอ่านข้อมูลเข้าในในโปรแกรม เราจะใช้โมดูลที่ชื่อว่า pandas ครับ และเพื่อความง่ายในการเลือกไฟล์ เราจะใช้โมดูล tkinter เพื่อสร้างหน้าต่างเลือกไฟล์ เราจะได้ไม่ต้องมาแก้ไขชื่อไฟล์ทุกครั้งที่ต้องรันโปรแกรม
อันดับแรก import module ที่ต้องใช้
import pandas as pd #use for read excel
import tkinter as tk #use for draw and close file dialog
from tkinter import filedialog #use for open file dialog
import sys # use for exit program
import pickle #use for save k-nn after train
จากนั้นอ่านไฟล์ excel เข้ามาในโปรแกรมครับ (ไฟล์ excel ของเราเก็บข้อมูลเหมือนในตารางด้านบนนะครับ)
root = tk.Tk()
root.withdraw()
file_path = filedialog.askopenfilename()
if not file_path:
sys.exit("End program...")
# Read excel file
df = pd.read_excel(file_path)
print(df)
เมื่อกดรันโปรแกรมจะได้ผลลัพธ์ประมาณนี้
ตอนนี้เราได้ข้อมูลเข้ามาในโปรแกรมแล้วนะครับ แต่ว่าข้อมูลเหล่านี้ ยังไม่สามารถนำไปใช้ได้โดยตรง เพราะ…
- รูปแบบข้อมูลยังไม่ยังไม่ถูกจัดให้ถูกต้อง สำหรับการเทรน K-NN
- ประเภทของข้อมูลยังไม่ตรงกับที่ฟังก์ชัน K-NN ต้องการ
ดังนั้นก่อนอื่น เรามาทำความรู้จักกับโครงสร้างข้อมูลที่ใช้เทรน K-NN กันก่อนเลยครับ
ฟังก์ชันเทรน K-NN นั้นจะรับข้อมูล input ประเภท list หรือ numpy แต่ตอนนี้ข้อมูลในตัวแปร df (ตัวแปรที่เก็บค่าจากการอ่านไฟล์ excel) มี data type เป็น DataFrame ซึ่งไม่ตรงกับประเภทข้อมูลที่ K-NN ต้องการ
ส่วนข้อมูลที่ใช้เทรนนั้นจะมี 2 ชุด คือ X กับ Y โดยที่ X เก็บค่าคุณลักษณะ ในรูปแถวแนวนอน ส่วน Y เก็บประเภทของข้อมูล ตัวอย่างเช่น
X Y
=================================================================
d1, d2, d3, d4, d5 L1
d6, d7, d8, d9, d10 L2
โดยที่ d1-d5 คือคุณลักษณะของข้อมูลของชุดที่ 1 และ d6-d10 คือคุณลักษณะของข้อมูลชุดที่ 2 ส่วน L1 และ L2 คือประเภทของข้อมูลชุดที่ 1 และ 2 ตามลำดับ
เอาละครับ ตอนนี้เราทราบความหมาย และรูปแบบของข้อมูลแล้ว งั้นเราก็มาเริ่มจัดการกับข้อมูลในตัวแปร df ให้ตรงกับที่เราต้องการกันเลย
X = []
Y = []r,c = df.shape
for m in range(0,r):
tmp = []
n = 0
for cl in df:
a = df[cl]
if n<4:
tmp.append(a[m])
if n==c-1:
Y.append(a[m])
n+=1
X.append(tmp)
เราก็จะได้ X และ Y แบบนี้ครับ ซึ่งพร้อมที่จะเอาไปเทรน K-NN ในลำดับต่อไป
ในตัวอย่างนี้ผมเตรียมข้อมูล โดยกำหนดประเภทให้เป็น list นะครับ ซึ่งเป็น data type ของข้อมูลประเภทอาเรย์ใน python
ขั้นตอนที่ 4 เขียนโปรแกรมเทรน K-NN
เมื่อเราเตรียมข้อมูลเสร็จแล้ว ลำดับต่อไปก็คือการแบ่งประเภทข้อมูลครับ โดยเราจะแบ่งเป็น 2 ชุดคือ ชุดแรกเป็นข้อมูลสำหรับเทรน K-NN ส่วนชุดที่ 2 เป็นข้อมูลสำหรับทดสอบ K-NN ที่เราเทรนเสร็จแล้ว ดังนี้
from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state=42, stratify=Y)
จากนั้นเราก็เทรน K-NN โดยใช้คำสั่งต่อไปนี้
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors = 1)
knn.fit(X_train,Y_train)
answer = knn.predict(X_test)
และก็ตรวจสอบความแม่นยำของ K-NN โดยใช้คำสั่งต่อไปนี้
from sklearn.metrics import classification_report
rpt = classification_report(Y_test, answer)
print(rpt)
เราก็จะได้ผลลัพธ์ประมาณนี้ครับ
จริงๆ แล้วเราจะเห็นค่าความแม่นยำจากตัวแปร rpt ได้เลยครับ โดยดูในส่วนของ f-score ของ micro avg แต่ว่าตัวแปร rpt เป็นชนิด str หรือก็คือ ข้อมูลที่แสดงให้เราเห็นทั้งหมดนั้นเป็นสตริง (ข้อความตัวอักษร) ดังนั้นเราจึงไม่สามารถอ่านค่าความแม่นยำออกมาใช้ได้โดยตรง จึงจำเป็นต้องใช้คำสั่งต่อไปนี้ เพื่ออ่านค่าความแม่นยำออกมาครับ
from sklearn.metrics import accuracy_score
acc = accuracy_score(Y_test, answer)
print(acc)
ซึ่งจะเห็นว่าความแม่นยำยังไม่ดีเท่าไร งั้นต่อไปเรามาลองปรับค่า K กันดูนะครับ เพราะตอนนี้เราใช้ K=1 ถ้าปรับค่า K เพิ่มขึ้น ผลอาจจะแม่นยำขึ้นก็ได้ ดังนั้น มาลองดูกัน
# search best K
bestScore = 0
bestK = 0
for m in range(1,10):
knn = KNeighborsClassifier(n_neighbors = m)
knn.fit(X_train,Y_train)
answer = knn.predict(X_test)
acc = accuracy_score(Y_test, answer)
if acc>bestScore:
bestScore = acc
bestK = m
print(bestK)
จากตัวอย่างนี้จะได้ผลลัพธ์เป็น bestK = 3 ครับ
จากนั้นเพื่อความชัวร์ ก่อนจะบันทึก K-NN เพื่อเอาไว้ใช้งาน เราก็เทรนอีกรอบ โดยใช้ bestK ที่เราหาได้ครับ
# recalculate best result again and show result
knn = KNeighborsClassifier(n_neighbors = bestK)
knn.fit(X_train,Y_train)
answer = knn.predict(X_test)
rpt = classification_report(Y_test, answer)
print(rpt)
acc = accuracy_score(Y_test, answer)
print(acc)
ซึ่งจะได้ผลลัพธ์ประมาณนี้
จากนั้นเราก็สามารถบันทึก object K-NN ที่เราเทรนเสร็จแล้วเอาไว้ใช้งานได้เลยครับ โดยใช้คำสั่งต่อไปนี้
# save K-NN to pickle file
fn = open("knnSample.pickle", "wb")
pickle.dump(knn, fn)
fn.close()
เพียงเท่านี้เราก็จะได้ไฟล์ K-NN ที่พร้อมใช้งานแล้วครับ ไม่ต้องกลับมาเทรนใหม่ทุกครั้งที่ต้องการใช้งาน เราสามารถโหลดจากไฟล์ knnSample.pickle ไปใช้งานได้เลย
ขั้นตอนที่ 5 เขียนโปรแกรมทดสอบ K-NN
ในขั้นตอนนี้เราจะลองโหลดไฟล์ K-NN model ที่เราบันทึกเอาไว้มาใช้งานดูนะครับ โดยใช้คำสั่งดังนี้
# load K-NN pickle file and use
fn = open("knnSample.pickle", "rb")
knnModel = pickle.load(fn)
fn.close()# Test K-NN model
Test = [[18, 0, 130, 19.5]]
reSult = knnModel.predict(Test)
print(reSult)
โดยข้อมูลในตัวแปร Test เป็นข้อมูลที่ผมสุ่มขึ้นมา เพื่อใช้ทดสอบโมเดล K-NN ครับ โดยผลลัพธ์ที่ได้ก็คือ 2 ซึ่งหมายความว่า….
ถ้าหากผมจะโพสต์ขายเสื้อกันหนาว โดยโพสต์รูปทั้งหมด 18 รูป ไม่มีวีดีโอในโพสต์เลย และเขียนบรรยายสินค้าด้วยความยาว 130 ตัวอักษร และขายสินค้านี้ในราคา $19.5 สินค้าตัวนี้จะขายได้ในระดับ “ปานกลาง” หรือขายได้ประมาณ 200–800 ตัว
สรุป
ถึงแม้ว่าบทความนี้จะค่อนข้างยาว แต่จริงๆ แล้ว K-NN เป็นวิธีการ จำแนกประเภทข้อมูลที่ค่อนข้างง่าย และรวดเร็ววิธีหนึ่งเลยนะครับ ซึ่งถ้าคุณได้ไปอ่านจากบทความอื่นๆ หรือที่เขาสอนกันในคอร์สออนไลน์ มันก็จะมีวิธีการปรับสเกล เพื่อให้ความแม่นยำของ K-NN เพิ่มขึ้น แต่ผมไม่แนะนำให้ทำหรอกครับ เพราะไม่ใช่ว่าปรับสเกลแล้วผลลัพธ์จะต้องดีขึ้นเสมอไป และจริงๆ แล้ว ผมคิดว่ามันก็ไม่ใช่ส่วนสำคัญของ K-NN ด้วย กล่าวคือ ถ้าหากคุณเทรน K-NN ออกมาแล้วได้ผลไม่ดี คุณก็ไม่ต้องไปปรับอะไรมันหรอก เพราะโปรแกรมมันบอกคุณว่า….
ข้อมูลที่คุณใช้เทรนนั้น ไม่มีความสัมพันธ์กับ output หรือ target ที่คุณกำหนด ดังนั้นฝืนปรับไปก็ไม่ได้ประโยชน์อะไรหรอก คุณควรจะไปหาข้อมูลใหม่มาลองเทรนดูดีกว่า หรืออาจจะลองเพิ่มข้อมูลอื่นๆ เข้ามา หรือตัดข้อมูลบางตัวออกไปก็ได้
ถ้าคุณเข้าใจทั้งหมดนี้ คุณก็พร้อมจะนำ K-NN ไปใช้ได้จริงแล้วครับ สุดท้าย โค้ดเต็มๆ ของบทความนี้ ดูได้จากด้านล่างนี้เลยครับ