ml/ library Learn by doing
ALGORITHM 27 · PYTHON / JAVASCRIPT

Bernoulli Naive Bayes

ดูทั้งคำที่มี และคำที่ไม่มี

LEARNING LEVEL2
ต่อยอดโดยตรงควรรู้มาก่อน · Gaussian Naive Bayes และข้อมูล binary
ลองใน Playground

01Overall Concept

OVERALL 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

ค่อยๆ ทำไปด้วยกัน ตั้งแต่ข้อมูลแถวแรกจนถึงการตรวจคำตอบ

  1. กำหนดว่าหนึ่งหมายถึงอะไร

    ให้ X_j=1 เมื่อพบคำ j และเป็น 0 เมื่อไม่พบ แบ่ง train/test ก่อนสร้าง vocabulary ถ้าใช้ CountVectorizer(binary=True) จะได้รูปแบบนี้โดยตรง โค้ดตัวอย่างแปลงตาราง count ด้วย (X>0).astype(int)

  2. เรียนรู้สัดส่วนการปรากฏ

    ในแต่ละคลาสนับจำนวนเอกสาร N_c และจำนวนที่พบ feature N_cj จากนั้น p_cj=(N_cj+alpha)/(N_c+2×alpha) เลข 2 มาจากสองสถานะของแต่ละ feature ไม่ใช่จำนวนคำใน vocabulary

  3. ใช้ทั้งมีและไม่มี

    สำหรับแต่ละคลาสรวม log P(c)+Σ[x_j log p_cj+(1−x_j) log(1−p_cj)] เมื่อ x_j=0 เทอมด้านหลังยังทำงานอยู่ จึงต่างจาก Multinomial NB ที่ count ศูนย์ไม่มีส่วนในผลรวมคำนั้น

  4. กำหนด binarize ให้ตรงข้อมูล

    เมื่อ X เป็น 0/1 อยู่แล้ว ใช้ BernoulliNB(alpha=1.0,binarize=None) แล้ว fit/predict ถ้ารับ count โดยตรงใช้ binarize=0.0 เพื่อถือว่าค่าที่มากกว่าศูนย์คือพบ ใน Playground แกนแสดง 0/1 และจุดที่ทับกันถูกขยับเล็กน้อยเฉพาะตอนวาด

  5. ตรวจความเหมาะสมของ representation

    ประเมิน macro F1 และ confusion matrix บนชุดทดสอบ ลองเทียบกับ Multinomial NB โดยใช้การแบ่งชุดเดียวกัน ปรับ alpha จาก validation เท่านั้นและอย่าตีความคะแนนสูงบนข้อมูล binary สังเคราะห์ว่าใช้กับข้อความจริงได้ทันที

เปิดตัวอย่างโค้ดเพื่อลองทำตาม →ตัวอย่างตัวเลขในเรื่องใช้ช่วยอธิบาย ส่วนโค้ดและ Playground ใช้ชุดข้อมูลสาธิตของแต่ละหน้าอ่านเอกสารอ้างอิงของเทคนิคนี้ ↗

ข้อมูล พารามิเตอร์ และภาพรวมการคำนวณ

DATA INX · featuresy · target4 เอกสาร · พบคำ 3 ฉบับ · alpha=1
CALCULATEBernoulli Naive Bayes
alphabinarizefit_prior
RESULT0.800.150.05class / probabilityP(พบคำ|คลาส)=4/6 · P(ไม่พบ)=2/6
เส้นทางของข้อมูล: จากค่าที่รับเข้า ผ่านการคำนวณด้วยพารามิเตอร์ ไปเป็นผลลัพธ์
INPUT

ข้อมูลที่รับเข้ามา

X เป็น 0/1 ต่อ feature และ y เป็นคลาสของตัวอย่าง

4 เอกสาร · พบคำ 3 ฉบับ · alpha=1
PARAMETERS

พารามิเตอร์ที่ใช้

alpha
smoothing สำหรับทั้งการพบและไม่พบ
binarize
threshold แปลงเป็น 0/1; None เมื่อแปลงมาแล้ว
fit_prior
ใช้ความถี่คลาสเป็น prior หรือ prior เท่ากัน
OUTPUT

ผลลัพธ์ที่ได้

คลาสและความน่าจะเป็นตามแบบจำลอง binary

P(พบคำ|คลาส)=4/6 · P(ไม่พบ)=2/6
CALCULATION

คำนวณทีละขั้น

  1. 01

    นับเอกสารที่พบ feature ในแต่ละคลาส

  2. 02

    คำนวณ p=(N_cj+alpha)/(N_c+2alpha)

  3. 03

    รวม x log p + (1−x) log(1−p) พร้อม log prior

ควรรู้อีกนิด — ถ้า binarize=None ต้องมั่นใจว่า X เป็น binary อยู่แล้ว คำที่ไม่ปรากฏหลายพันคำยังรวมอยู่ใน score จึงควรตรวจ vocabulary และเปรียบเทียบผลกับ count-based model

04Make it yours

Python · scikit-learn
# ติดตั้ง: 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 แล้วสังเกตรูปแบบคำทำนายและตัวชี้วัดที่คำนวณใหม่ทันที

Sample dataset / Word counts · Three classes● คำนวณใหม่เมื่อปรับค่า
เติมจำนวนสมมติทั้งสถานะพบและไม่พบคำ ทำให้ความน่าจะเป็นไม่สุดโต่ง
พื้นที่สีคือคำทำนาย จุดคือคำตอบจริงword_1 ∈ {0,1} · word_2 ∈ {0,1} · jitter เพื่อให้เห็นจุดซ้อน
คำตอบของทั้ง 4 รูปแบบ · ปรับ alpha แล้วดูความน่าจะเป็นเปลี่ยน
word_1word_2คลาสที่ทายP(คลาสที่ทาย)
00149.9%
01192.9%
10097.7%
11060.5%

ค่าจากแบบจำลอง ยังไม่ผ่าน calibration · สีจุด: คลาส 0 เขียว / 1 ม่วง / 2 ส้ม

Test accuracy83.3%
ชุดฝึกต่อคลาส72 / 36 / 12
พารามิเตอร์ที่ลอง1.0

Playground เป็น implementation เพื่อการเรียนรู้ที่คำนวณในเบราว์เซอร์ ส่วนโค้ด Python ใช้ scikit-learn จึงอาจให้ค่าต่างกันเล็กน้อยจากรายละเอียดการ optimize และค่าเริ่มต้นของไลบรารี ผลนี้ไม่ใช่ benchmark บนข้อมูลจริง