การเขียน K-NN ด้วย Python

Kritthanit Malathong
5 min readDec 5, 2020

--

สวัสดีผู้อ่านทุกท่านนะครับ ในบทความนี้ผมจะพาทุกท่านมาทำความรู้จักกับ 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 ตัวอย่างกลุ่มข้อมูล

ในรูปที่ 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)

เมื่อกดรันโปรแกรมจะได้ผลลัพธ์ประมาณนี้

ตัวอย่างข้อมูลที่อ่านจากไฟล์ excel

ตอนนี้เราได้ข้อมูลเข้ามาในโปรแกรมแล้วนะครับ แต่ว่าข้อมูลเหล่านี้ ยังไม่สามารถนำไปใช้ได้โดยตรง เพราะ…

  1. รูปแบบข้อมูลยังไม่ยังไม่ถูกจัดให้ถูกต้อง สำหรับการเทรน K-NN
  2. ประเภทของข้อมูลยังไม่ตรงกับที่ฟังก์ชัน 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 ในลำดับต่อไป

X-Y data สำหรับเทรน 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)

เราก็จะได้ผลลัพธ์ประมาณนี้ครับ

ผลลัพธ์การเทรน K-NN

จริงๆ แล้วเราจะเห็นค่าความแม่นยำจากตัวแปร 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)

ซึ่งจะได้ผลลัพธ์ประมาณนี้

ประสิทธิภาพของ K-NN หลังปรับค่า K

จากนั้นเราก็สามารถบันทึก 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 ไปใช้ได้จริงแล้วครับ สุดท้าย โค้ดเต็มๆ ของบทความนี้ ดูได้จากด้านล่างนี้เลยครับ

--

--

No responses yet