01Overall Concept
ไอเดียหลัก — Bernoulli Naive Bayes ใช้ feature ที่มีสองสถานะ เช่น พบคำหรือไม่พบคำ แต่ละคำมีผลได้ทั้งตอนปรากฏและตอนหายไป เหมาะกับคำถามแบบเช็กรายการมากกว่าการนับว่าคำหนึ่งถูกใช้ซ้ำกี่ครั้ง
ลองนึกภาพว่า — เหมือนเช็กอุปกรณ์ก่อนออกเดินทาง มีร่มไหม มีเสื้อกันฝนไหม สิ่งที่ไม่ได้พกก็เป็นข้อมูล โดยไม่สนใจว่าพกร่มหนึ่งคันหรือสามคัน
ตัวอย่างการนำไปใช้ — แยกข้อความสั้นจากคำสำคัญ จำแนกสินค้าจากคุณสมบัติที่มี/ไม่มี และใช้เป็น baseline สำหรับตาราง binary
02อธิบายภาษาคนเข้าใจง่ายๆ
ถ้าเราเปลี่ยนข้อความเป็นรายการเช็กช่องว่าเจอแต่ละคำไหม ก็จะได้ข้อมูล 0 กับ 1 Bernoulli NB ถูกออกแบบมาสำหรับข้อมูลแบบนี้
มีคำว่า free หนึ่งครั้งหรือสิบครั้งก็ได้ค่า 1 เหมือนกัน เรากำลังถามถึงการปรากฏ ไม่ได้ถามจำนวน จึงลดอิทธิพลของการพูดคำเดิมซ้ำ ๆ
จุดต่างสำคัญคือคำที่ไม่พบก็มีผล สมมติข้อความโปรโมชั่นแทบทุกข้อความมีคำว่า free ถ้าข้อความใหม่ไม่มีคำนี้เลย โมเดลจะใช้การหายไปนั้นเป็นหลักฐานประกอบ
ถ้าเอกสารคลาสหนึ่ง 4 ฉบับมีคำนี้ 3 ฉบับ เมื่อเติม alpha=1 โอกาสพบคำจะเป็น (3+1)/(4+2)=2/3 ส่วนโอกาสไม่พบเป็น 1/3
วิธีนี้เร็วและเข้าใจง่าย แต่เสียข้อมูลเรื่องจำนวนครั้ง ถ้างานต้องแยกว่าพูดถึงหัวข้อไหนมากกว่ากันในบทความยาว Multinomial NB อาจเหมาะกว่า ต้องลองกับข้อมูลจริง
ภาพจำ: เช็กช่องทั้งที่ติ๊กและที่ว่าง แล้วใช้รูปแบบของรายการช่วยเดาหมวด
03How it works
ค่อยๆ ทำไปด้วยกัน ตั้งแต่ข้อมูลแถวแรกจนถึงการตรวจคำตอบ
กำหนดว่าหนึ่งหมายถึงอะไร
ให้ X_j=1 เมื่อพบคำ j และเป็น 0 เมื่อไม่พบ แบ่ง train/test ก่อนสร้าง vocabulary ถ้าใช้ CountVectorizer(binary=True) จะได้รูปแบบนี้โดยตรง โค้ดตัวอย่างแปลงตาราง count ด้วย (X>0).astype(int)
เรียนรู้สัดส่วนการปรากฏ
ในแต่ละคลาสนับจำนวนเอกสาร N_c และจำนวนที่พบ feature N_cj จากนั้น p_cj=(N_cj+alpha)/(N_c+2×alpha) เลข 2 มาจากสองสถานะของแต่ละ feature ไม่ใช่จำนวนคำใน vocabulary
ใช้ทั้งมีและไม่มี
สำหรับแต่ละคลาสรวม log P(c)+Σ[x_j log p_cj+(1−x_j) log(1−p_cj)] เมื่อ x_j=0 เทอมด้านหลังยังทำงานอยู่ จึงต่างจาก Multinomial NB ที่ count ศูนย์ไม่มีส่วนในผลรวมคำนั้น
กำหนด binarize ให้ตรงข้อมูล
เมื่อ X เป็น 0/1 อยู่แล้ว ใช้ BernoulliNB(alpha=1.0,binarize=None) แล้ว fit/predict ถ้ารับ count โดยตรงใช้ binarize=0.0 เพื่อถือว่าค่าที่มากกว่าศูนย์คือพบ ใน Playground แกนแสดง 0/1 และจุดที่ทับกันถูกขยับเล็กน้อยเฉพาะตอนวาด
ตรวจความเหมาะสมของ representation
ประเมิน macro F1 และ confusion matrix บนชุดทดสอบ ลองเทียบกับ Multinomial NB โดยใช้การแบ่งชุดเดียวกัน ปรับ alpha จาก validation เท่านั้นและอย่าตีความคะแนนสูงบนข้อมูล binary สังเคราะห์ว่าใช้กับข้อความจริงได้ทันที
ข้อมูล พารามิเตอร์ และภาพรวมการคำนวณ
alphabinarizefit_priorข้อมูลที่รับเข้ามา
X เป็น 0/1 ต่อ feature และ y เป็นคลาสของตัวอย่าง
4 เอกสาร · พบคำ 3 ฉบับ · alpha=1พารามิเตอร์ที่ใช้
- alpha
- smoothing สำหรับทั้งการพบและไม่พบ
- binarize
- threshold แปลงเป็น 0/1; None เมื่อแปลงมาแล้ว
- fit_prior
- ใช้ความถี่คลาสเป็น prior หรือ prior เท่ากัน
ผลลัพธ์ที่ได้
คลาสและความน่าจะเป็นตามแบบจำลอง binary
P(พบคำ|คลาส)=4/6 · P(ไม่พบ)=2/6คำนวณทีละขั้น
- 01
นับเอกสารที่พบ feature ในแต่ละคลาส
- 02
คำนวณ p=(N_cj+alpha)/(N_c+2alpha)
- 03
รวม x log p + (1−x) log(1−p) พร้อม log prior
ควรรู้อีกนิด — ถ้า binarize=None ต้องมั่นใจว่า X เป็น binary อยู่แล้ว คำที่ไม่ปรากฏหลายพันคำยังรวมอยู่ใน score จึงควรตรวจ vocabulary และเปรียบเทียบผลกับ count-based model
04Make it yours
# ติดตั้ง: pip install numpy scikit-learn# ดาวน์โหลด sample.csv จากหน้านี้ แล้ววางไว้ข้างสคริปต์import numpy as np data = np.genfromtxt("bernoulli-naive-bayes-sample.csv", delimiter=",", skip_header=1)X = data[:, :2] from sklearn.naive_bayes import BernoulliNBfrom 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 = BernoulliNB(alpha=1, binarize=None)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 แล้วสังเกตรูปแบบคำทำนายและตัวชี้วัดที่คำนวณใหม่ทันที
| word_1 | word_2 | คลาสที่ทาย | P(คลาสที่ทาย) |
|---|---|---|---|
| 0 | 0 | 1 | 49.9% |
| 0 | 1 | 1 | 92.9% |
| 1 | 0 | 0 | 97.7% |
| 1 | 1 | 0 | 60.5% |
ค่าจากแบบจำลอง ยังไม่ผ่าน calibration · สีจุด: คลาส 0 เขียว / 1 ม่วง / 2 ส้ม
Playground เป็น implementation เพื่อการเรียนรู้ที่คำนวณในเบราว์เซอร์ ส่วนโค้ด Python ใช้ scikit-learn จึงอาจให้ค่าต่างกันเล็กน้อยจากรายละเอียดการ optimize และค่าเริ่มต้นของไลบรารี ผลนี้ไม่ใช่ benchmark บนข้อมูลจริง