01Overall Concept
ไอเดียหลัก — Complement Naive Bayes เป็นญาติของ Multinomial NB ที่เปลี่ยนไปเรียนรู้คำจากเอกสารนอกคลาสเป้าหมาย เช่น ตอนพิจารณาคลาสกีฬา จะใช้เอกสารทุกหมวดที่ไม่ใช่กีฬาเพื่อสร้างน้ำหนัก วิธีนี้ถูกเสนอมาเพื่อช่วยกับปัญหาการจัดหมวดข้อความที่ข้อมูลไม่สมดุล
ลองนึกภาพว่า — เหมือนแยกของให้เข้ากล่องโดยเรียนรู้ว่าของที่ไม่ควรอยู่กล่องนี้หน้าตาเป็นอย่างไร แล้วเลือกกล่องที่ของชิ้นใหม่ขัดกับลักษณะของของนอกกล่องมากที่สุด
ตัวอย่างการนำไปใช้ — จัดหมวดข่าวหรือ ticket ที่บางหมวดมีตัวอย่างน้อย ใช้เป็น baseline เปรียบเทียบกับ Multinomial NB บน count หรือ TF-IDF ที่ไม่ติดลบ
02อธิบายภาษาคนเข้าใจง่ายๆ
สมมติเรามีข่าวกีฬาเยอะมาก แต่มีข่าวเทคโนโลยีนิดเดียว ถ้าเรียนจากตัวอย่างในหมวดเล็กอย่างเดียว สถิติของคำอาจไม่นิ่ง
Complement NB ลองกลับมุมมอง สำหรับแต่ละหมวด มันรวบข่าวทุกหมวดที่เหลือเข้าด้วยกัน เรียนรู้ว่าข่าวที่ไม่ใช่หมวดนี้มักมีคำอะไร
ตอนรับข่าวใหม่ มันดูว่าคำในข่าวเข้ากับกลุ่มนอกแต่ละหมวดมากแค่ไหน ถ้าข่าวใหม่ไม่เหมือนข่าวที่ไม่ใช่กีฬามากที่สุด ก็เป็นหลักฐานให้เลือกกีฬา
ขั้นตอนนี้ทำแยกสำหรับทุกคลาส ไม่ใช่รวมหมวดเล็กเข้าด้วยกัน และไม่ได้สร้างข่าวใหม่เพื่อเพิ่มจำนวนให้เท่ากัน
บางงานจะดีขึ้นโดยเฉพาะข้อมูลข้อความที่ไม่สมดุล แต่ไม่ใช่คำรับประกัน เราต้องดูว่าคลาสเล็กถูกหาเจอจริงไหม เพราะ accuracy รวมอาจดูสูงเพียงจากการทายคลาสใหญ่
ภาพจำ: แยกหมวดจากร่องรอยของทุกอย่างที่อยู่นอกหมวดนั้น แล้วตรวจผลของแต่ละหมวดแยกกัน
03How it works
ค่อยๆ ทำไปด้วยกัน ตั้งแต่ข้อมูลแถวแรกจนถึงการตรวจคำตอบ
แบ่งข้อความให้ยังมีคลาสเล็ก
สร้าง train/test แบบ stratify และแปลงเป็น count หรือ TF-IDF ที่ไม่ติดลบโดย fit vocabulary เฉพาะชุดฝึก ในข้อมูลจริงอาจต้องแยกตามเวลาและป้องกันข้อความซ้ำข้ามชุดด้วย
นับทุกคลาสยกเว้นคลาสที่กำลังดู
สำหรับคลาส c รวมจำนวนคำจากแถวที่ y≠c เป็น N_¬c,j เติม alpha แล้วหารด้วยจำนวนคำทั้งหมดใน complement บวก alpha×V จะได้ θ_¬c,j
เปลี่ยนสัดส่วนเป็นน้ำหนัก
เมื่อ norm=False ใช้ w_cj=−log θ_¬c,j แล้วเลือกคลาสที่ Σx_jw_cj สูงที่สุด ซึ่งเทียบเท่าการเลือก log likelihood ของ complement ต่ำที่สุด ในกรณีหลายคลาส scikit-learn ไม่บวก class prior แบบ Multinomial NB เข้า score นี้
ลองตัวเลือก normalization อย่างชัดเจน
โค้ดใช้ ComplementNB(alpha=1.0,norm=False) ตามค่าเริ่มต้น ถ้า norm=True จะหารน้ำหนักของแต่ละคลาสด้วยผลรวมขนาดสัมบูรณ์ของน้ำหนักนั้น เลือกจาก validation ไม่ควรสลับโดยไม่บันทึกค่าที่ใช้
ตรวจคลาสเล็กพร้อมคะแนนรวม
รายงาน macro F1, recall ต่อคลาส และ confusion matrix เทียบกับ Multinomial NB และการทายคลาสส่วนใหญ่ ข้อมูลสังเคราะห์ของ Playground แสดงเพียงสอง feature และใช้อัตราคลาสไม่สมดุล จึงเอาไว้ตรวจกลไก ไม่ใช่ยืนยันว่าโมเดลไหนดีที่สุด
ข้อมูล พารามิเตอร์ และภาพรวมการคำนวณ
alphanormforce_alphaข้อมูลที่รับเข้ามา
X เป็น count หรือ TF-IDF ไม่ติดลบ และ y เป็นคลาส
พิจารณากีฬา → รวมคำจากเทคโนโลยีและธุรกิจพารามิเตอร์ที่ใช้
- alpha
- smoothing ของสถิติ complement
- norm
- normalize น้ำหนักซ้ำหรือไม่; ตัวอย่างใช้ False
- force_alpha
- ควบคุมการใช้ alpha ที่เล็กมาก; ตัวอย่างเลือกค่าบวกอย่างน้อย 0.1
ผลลัพธ์ที่ได้
คลาสที่ได้คะแนน complement สูงสุดตามนิยามน้ำหนักข้างต้น
ไม่เหมือนข้อมูลนอกคลาสกีฬา → สนับสนุนคลาสกีฬาคำนวณทีละขั้น
- 01
รวม N_¬c,j จากทุกคลาสที่ไม่ใช่ c
- 02
θ_¬c,j=(N_¬c,j+alpha)/(ΣN_¬c,j+alpha×V)
- 03
เมื่อ norm=False เลือก argmax_c Σx_j(−log θ_¬c,j)
ควรรู้อีกนิด — การใช้ complement ไม่ได้แปลว่าปรับจำนวนคลาสให้เท่ากัน และ predict_proba ยังควรตรวจ calibration หากจะนำไปตัดสินใจด้วย threshold ความมั่นใจ
04Make it yours
# ติดตั้ง: pip install numpy scikit-learn# ดาวน์โหลด sample.csv จากหน้านี้ แล้ววางไว้ข้างสคริปต์import numpy as np data = np.genfromtxt("complement-naive-bayes-sample.csv", delimiter=",", skip_header=1)X = data[:, :2] from sklearn.naive_bayes import ComplementNBfrom sklearn.metrics import accuracy_score, classification_report y = data[:, 2].astype(int)# กันทุกแถวที่ 5 ไว้ทดสอบ ตามข้อมูลสาธิตใน Playground# งานจริงควรเลือก split ให้เหมาะกับเวลา/บุคคล/กลุ่มของข้อมูลis_test = np.arange(len(X)) % 5 == 0X_train, X_test = X[~is_test], X[is_test]y_train, y_test = y[~is_test], y[is_test]model = ComplementNB(alpha=1, norm=False)model.fit(X_train, y_train)pred = model.predict(X_test)print("Test accuracy:", accuracy_score(y_test, pred))print(classification_report(y_test, pred, labels=[0, 1, 2], zero_division=0)) โค้ดเปลี่ยนตามค่าที่เลือกใน Playground · แต่ละภาษาใช้ข้อมูลตัวอย่างตามที่ระบุในโค้ด
05Learn by playing
ปรับ alpha · smoothing แล้วสังเกตรูปแบบคำทำนายและตัวชี้วัดที่คำนวณใหม่ทันที
Playground เป็น implementation เพื่อการเรียนรู้ที่คำนวณในเบราว์เซอร์ ส่วนโค้ด Python ใช้ scikit-learn จึงอาจให้ค่าต่างกันเล็กน้อยจากรายละเอียดการ optimize และค่าเริ่มต้นของไลบรารี ผลนี้ไม่ใช่ benchmark บนข้อมูลจริง