Lesson 03 · คาบเช้า 28 ต.ค. · ~25 นาที
K-means clustering และ Silhouette score
1อัลกอริทึม
Centroid คือ "ตัวแทนกลุ่ม" รอบแรกเราสุ่มมามั่ว ๆ จึงยังไม่อยู่กลางกลุ่ม แล้วทำสองขั้นสลับกัน คือ อัปเดตสมาชิก และ อัปเดต centroid (ช่วงเช้า ไม่มีไทม์สแตมป์)
- ผู้ใช้กำหนด k (จำนวนกลุ่ม)
- สุ่ม centroid k จุด
- จัดสมาชิกแต่ละจุดเข้ากลุ่มของ centroid ที่ ใกล้ที่สุด (วัดด้วยระยะทาง จึงเรียกว่า distance-based)
- อัปเดต centroid ให้เป็น ค่าเฉลี่ย ของสมาชิกในกลุ่ม (เฉลี่ย x, เฉลี่ย y ... ตัวแทนอยู่กลางกลุ่ม)
- กลับไปข้อ 3 ทำซ้ำ
ลองกดทีละขั้น
กด ขั้นถัดไป สลับกัน: ขั้นคี่จัดสมาชิก (จุดเปลี่ยนสี) ขั้นคู่ย้าย centroid (กากบาทขยับไปกลางกลุ่ม เส้นประคือทางที่ขยับ) ลอง สุ่ม centroid ใหม่ ดูว่าจุดเริ่มต้นต่างกันใช้กี่รอบ
ถ้า centroid เริ่มต้นสุ่มได้ไม่ดี K-means อาจ converge ไปที่การแบ่งกลุ่มที่ไม่สวย (กดสุ่มใหม่หลายครั้งจะเห็น) นี่คือเหตุผลที่ผลขึ้นกับจุดเริ่มต้น
ลองเปลี่ยน k ใน config เป็น 4 หรือ 5 กับข้อมูลชุดเดิม (มีกลุ่มจริง 3 กลุ่ม) จะเห็นว่าแบ่งเกินก็ยัง "ทำงาน" แต่กลุ่มอธิบายยากขึ้น
หยุดเมื่อ converge (สมาชิกไม่เปลี่ยนกลุ่มอีก) หรือครบจำนวนรอบที่กำหนด (max iter) ชื่อ "K-means" มาจากการใช้ค่าเฉลี่ยเป็นตัวแทนกลุ่ม
อัปเดต centroid
กลุ่มสีแดงมีสมาชิก (อายุ, รายได้ในหน่วยพัน) = (20, 50), (30, 70), (25, 60) centroid ใหม่ = ( (20+30+25)/3 , (50+70+60)/3 ) = (25, 60)
หลัง centroid ขยับ สมาชิกบางจุดต้องเปลี่ยนกลุ่มไหม
อาจต้อง เพราะจุดนั้นอาจใกล้ centroid ของกลุ่มอื่นมากกว่าแล้ว จึงต้องทำซ้ำ (เมื่อสมาชิกไม่เปลี่ยนจึงหยุด)
2ก่อนทำ K-means ต้องเตรียมข้อมูล
ใช้ระยะทางจึงต้อง:
- ตัวแปรทุกตัวเป็น ตัวเลข (ถ้าเป็น category ต้อง One-hot encode ก่อน)
- ตัวแปรอยู่ ช่วงเดียวกัน ใช้
StandardScalerเพราะรายได้ระดับหมื่นจะกลืนอายุระดับสิบ
ทำไมต้องสเกล
คน A อายุ 30 รายได้ 50,000 กับคน B อายุ 40 รายได้ 50,000 → ระยะ = \( \sqrt{10^2+0^2}=10 \) คน A กับคน C อายุ 30 รายได้ 50,020 → ระยะ = \( \sqrt{0^2+20^2}=20 \) อายุต่างกัน 10 ปี (ต่างมาก) กลับ "ใกล้" กว่ารายได้ที่ต่างกันแค่ 20 บาท
อาจารย์ย้ำว่า ถ้าสั่ง GenAI สร้างโมเดลให้ มันอาจไม่เตรียมข้อมูลให้ถูก (ไม่สเกล) ผลจึงผิดได้ ต้องตรวจเอง
3ใช้ใน sklearn
from sklearn.cluster import KMeans
km = KMeans(n_clusters=4) # default n_clusters=8, max_iter=300
km.fit(X_scaled) # ใส่เฉพาะตัวแปรต้น ไม่ใส่ y
km.cluster_centers_ # centroid (ยังอยู่ในสเปซที่สเกลแล้ว)
km.labels_ # กลุ่มของสมาชิกแต่ละตัว
km.predict(X_new) # ลูกค้าใหม่อยู่กลุ่มไหน
fit ใส่แต่ตัวแปรต้น ไม่ใส่ตัวแปรตาม เพราะเป็น unsupervised พอ fit เสร็จจะมี centroid และมีกลุ่มของสมาชิกแต่ละตัว แล้ว predict ข้อมูลใหม่ได้ว่าอยู่กลุ่มไหน (ช่วงเช้า ไม่มีไทม์สแตมป์)
ถ้า centroid อยู่ในสเปซที่สเกลแล้ว (ค่าติดลบ/บวกแปลก ๆ) ต้อง inverse_transform กลับสเปซเดิมก่อนถึงอ่านรู้เรื่อง เช่น กลุ่มที่อายุ −1.2 ไม่ได้แปลว่าอายุติดลบ
4ผลลัพธ์ที่ต้องอธิบายให้ได้
อาจารย์เล่นตัวอย่างโรงแรม: สามกลุ่มจากอายุ+รายได้ ถ้าจะเสนอ กระโดดร่มราคา 2 หมื่น ต้องเสนอกลุ่ม อายุน้อย รายได้สูง ส่วนกลุ่มอายุมากไม่เหมาะ (อาจารย์ล้อเล่นเรื่องตายก่อน) แต่หลักคือ
ผลลัพธ์ของ unsupervised คือ insight เราต้อง อธิบายแต่ละกลุ่มได้ แล้วเอาไปใช้ ถ้าอธิบายไม่ได้ก็ไม่มีประโยชน์
5เลือก k: Silhouette score
ไม่มีกฎตายตัวว่า k ควรเป็นเท่าไร (ถ้าเป็นนักเรียนในห้อง จะแบ่ง 3 หรือ 5 กลุ่มก็แล้วแต่ว่าดูแลได้เหมาะไหม) มีตัวช่วยวัด "คุณภาพกลุ่ม" คือ silhouette
$$ s_i = \frac{b_i - a_i}{\max(a_i, b_i)} $$
- \( a_i \) = ระยะเฉลี่ยจากจุด i ถึงสมาชิกอื่นใน กลุ่มเดียวกัน (intra) ควร ต่ำ
- \( b_i \) = ระยะเฉลี่ยจากจุด i ถึงสมาชิกใน กลุ่มใกล้ที่สุดอีกกลุ่ม (inter) ควร สูง
- ค่าอยู่ใน \( [-1, 1] \) ยิ่งใกล้ 1 ยิ่งดี
silhouette_score= ค่าเฉลี่ยของ \( s_i \) ทุกจุด
คำนวณ silhouette
จุดหนึ่งมี a = 1 และ b = 8.5 → \( s=(8.5-1)/8.5\approx 0.88 \) (เกาะกลุ่มดี แยกจากกลุ่มอื่นชัด) ถ้า a = 6 และ b = 2 → \( s=(2-6)/6\approx -0.67 \) (น่าจะอยู่ผิดกลุ่ม)
ลองเลื่อน k เอง
เลื่อน \( k \) จาก 2 ถึง 6 บนข้อมูลชุดเดียวกัน ดูภาพกลุ่มพร้อมคะแนน silhouette ของแต่ละ k
อย่าเลือก k จาก silhouette สูงสุดอย่างเดียว
อาจารย์ทดลอง k = 2, 3, 4, 5, 6 พบว่า k = 2 ได้ silhouette สูงสุด แต่ถ้าพล็อตจะเห็นว่า หยาบเกินใช้ประโยชน์ไม่ได้ อาจารย์จึงเลือก k = 4 ซึ่ง silhouette ต่ำกว่านิดหน่อยแต่ใช้งานได้
ประโยคนี้ผิด: "ต้องจัด 2 กลุ่มเท่านั้นเพราะ silhouette สูงสุด" Silhouette เป็นแค่ guideline ว่าควรลอง k ช่วงไหน แล้วเลือกที่ใช้ประโยชน์ได้ อาจารย์บอกว่านี่อาจออกสอบทฤษฎี (ช่วงเช้า ไม่มีไทม์สแตมป์)
6การใช้ clustering ช่วยโมเดลทำนาย
โมเดลเดียวทำนายราคาที่ดินทั้งประเทศอาจไม่แม่น เพราะกรุงเทพมีรถไฟฟ้าแต่เชียงใหม่ไม่มี แต่ถ้า แบ่งกลุ่มก่อน (หรือแบ่งตามจังหวัด) แล้วทำโมเดลแยกกลุ่ม จะแม่นขึ้น
7ลองทำ
- scikit-learn: KMeans พารามิเตอร์และ attribute ทั้งหมด
- scikit-learn: silhouette_score สูตรและช่วงค่า −1 ถึง 1
ติดตรงไหน ถาม Claude ได้เลย บอกเลขบทกับเลขข้อมาด้วย