Machine Learning › Tree และ boosting

Random Forest

ประมาณ 30 นาที

เป้าหมายของบท

  • อธิบายได้ว่า random forest ใช้ bootstrap + สุ่ม feature + โหวตเพื่อลดความไม่นิ่งของต้นไม้เดี่ยว
  • ปรับ n_estimators, min_samples_leaf, max_features และอ่าน OOB score
  • เปรียบเทียบ forest กับต้นไม้เดี่ยวและ baseline ด้วย cross-validation อย่างตรงไปตรงมา

ปัญหา: ต้นไม้เดี่ยว “ไม่นิ่ง”

ต้นไม้ตัวเดียวที่โตเต็มที่ overfit ง่าย และเปลี่ยนโครงสร้างมากเมื่อข้อมูลเปลี่ยนนิดเดียว (variance สูง) ไอเดียของ ensemble: สร้างต้นไม้หลายต้นที่ “ต่างกัน” แล้วให้ โหวต — ความผิดพลาดที่ไม่สม่ำเสมอของแต่ละต้นจะหักล้างกัน

Random forest = bagging + สุ่ม feature

  1. Bootstrap: แต่ละต้นเทรนบนตัวอย่างที่สุ่มแบบใส่คืนจากข้อมูล train (ราว 63% ของแถวไม่ซ้ำ)
  2. สุ่ม feature ต่อทุกจุดแยก: ที่แต่ละ split พิจารณาเพียงบางส่วนของ feature (max_features='sqrt' เป็นค่าเริ่มต้นของ classifier) ทำให้ต้นไม้ไม่ซ้ำกัน
  3. ทำนายโดย โหวตข้างมาก (predict_proba = ค่าเฉลี่ยของสัดส่วนในแต่ละต้น)
from sklearn.ensemble import RandomForestClassifier

rf = RandomForestClassifier(n_estimators=100, min_samples_leaf=10, oob_score=True, random_state=42)
rf.fit(X_train, y_train)
rf.score(X_test, y_test), rf.oob_score_

ค่าเริ่มต้นยังคง overfit ได้

ต้นไม้แต่ละต้นในค่าเริ่มต้นไม่ถูกจำกัดความลึก ดังนั้น forest 100 ต้นในข้อมูลของเรา train = 0.9995 แต่ test = 0.6197 — ดีกว่าต้นไม้เดี่ยว (0.5493) มาก แต่ยังไม่ดีเท่าการจำกัดขนาด เมื่อใส่ min_samples_leaf=10 ได้ test ≈ 0.6451

OOB score: validation ฟรี

แต่ละต้นไม่ได้เห็นแถวราว 37% (out-of-bag) — นำแถวเหล่านั้นมาทดสอบต้นนั้นแล้วเฉลี่ยรวม จะได้ประมาณการความแม่นโดยไม่ต้องแบ่ง validation (oob_score=True → rf.oob_score_) ในข้อมูลนี้ OOB ≈ 0.6534 ใกล้กับ test 0.6451

เปรียบเทียบตอนท้าย (5-fold CV)

โมเดล accuracy AUC
เดาคลาสส่วนใหญ่ 0.5903 0.5000
logistic regression 0.6442 0.6364
ต้นไม้เดี่ยว depth 3 0.6562 0.6444
ต้นไม้ไม่จำกัด 0.5696 0.5581
random forest (leaf 10) 0.6432 0.6420

ข้อสังเกตที่ตรงไปตรงมา: ข้อมูลนี้มีสัญญาณอ่อน (แทบมีแค่ promo) forest ไม่ได้ชนะ ต้นไม้ตื้นๆ หรือ logistic regression — ทุกตัวอยู่ในช่วง 0.01 กัน Ensemble ช่วยเรื่อง ความนิ่ง ไม่ได้สร้างสัญญาณที่ไม่มีอยู่ในข้อมูล

ตัวอย่างแก้โจทย์

กด "ขั้นต่อไป" ทีละขั้น ลองรันและแก้โค้ดในแต่ละขั้นได้

forest ค่าเริ่มต้น vs ต้นไม้เดี่ยว

ดูว่า ensemble ช่วยลดการจำข้อมูลเทรนได้แค่ไหน

  1. forest 100 ต้น: train 0.9995 แต่ test 0.6197 (เทียบกับต้นไม้เดี่ยวที่ test 0.5493)

    Python

OOB score และ CV

ประมาณการความแม่นโดยไม่ใช้ test

  1. เปิด oob_score=True แล้วอ่าน oob_score_ (≈ 0.6534) — ประมาณการความแม่นจากแถวที่แต่ละต้นไม่เคยเห็น

    Python

เติมช่องว่าง

แทน ___ ด้วยโค้ดที่ถูกต้อง แล้วกดตรวจคำตอบ

ยังไม่ได้ทำ

fit forest 50 ต้น (random_state=42) แล้วเก็บ accuracy บนชุดทดสอบไว้ในตัวแปร rf_acc เติม ___ ให้ครบ

แบบฝึกท้ายบท

บทนี้จะนับว่าเสร็จเมื่อผ่านข้อพื้นฐานและประยุกต์ครบ ข้อท้าทายได้ดาวเพิ่ม

🟢 พื้นฐาน

🟢 พื้นฐานยังไม่ได้ทำ

fit RandomForestClassifier(n_estimators=100, random_state=42) บน train แล้วเก็บ accuracy บน train และ test (ปัด 4 ตำแหน่ง) ไว้ในตัวแปร rf_train_acc, rf_test_acc

🟢 พื้นฐานยังไม่ได้ทำ

forest ที่ตั้ง n_estimators=50 มีต้นไม้ย่อยกี่ต้นใน rf.estimators_ เก็บไว้ในตัวแปร n_trees

🟢 พื้นฐานยังไม่ได้ทำ

forest 100 ต้นที่ตั้ง min_samples_leaf=10 (random_state=42): accuracy บนชุดทดสอบ ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร leaf10_test_acc

🟢 พื้นฐานยังไม่ได้ทำ

forest เดียวกัน (100 ต้น, min_samples_leaf=10, random_state=42) เปิด oob_score=True แล้วเก็บ OOB score (ปัด 4 ตำแหน่ง) ไว้ในตัวแปร oob

🟡 ประยุกต์

🟡 ประยุกต์ยังไม่ได้ทำ

เปรียบเทียบ accuracy บนชุดทดสอบของ forest ที่มี n_estimators = 5, 20 และ 100 (random_state=42 ค่าอื่นเป็นค่าเริ่มต้น ปัด 4 ตำแหน่ง) เก็บไว้ในตัวแปร acc_5, acc_20, acc_100

🟡 ประยุกต์ยังไม่ได้ทำ

เทียบ max_features="sqrt" (ค่าเริ่มต้น) กับ max_features=None (ใช้ทุก feature ทุก split) ที่ n_estimators=100, random_state=42: accuracy บนชุดทดสอบ (ปัด 4 ตำแหน่ง) เก็บไว้ในตัวแปร sqrt_acc, all_acc

🟡 ประยุกต์ยังไม่ได้ทำ

CV 5-fold (skf ที่ให้ไว้) ของ accuracy เฉลี่ย: forest (100 ต้น, min_samples_leaf=10, random_state=42) และต้นไม้เดี่ยวไม่จำกัดความลึก (random_state=42) ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร rf_cv, tree_cv

🔴 ท้าทาย

🔴 ท้าทายยังไม่ได้ทำ

forest (100 ต้น, min_samples_leaf=10, random_state=42): ชื่อ feature 3 อันดับแรกตาม feature_importances_ เรียงจากมากไปน้อย เก็บเป็น list ของ string ไว้ในตัวแปร top3

🔴 ท้าทายยังไม่ได้ทำ

forest (100 ต้น, min_samples_leaf=10, random_state=42): ความน่าจะเป็นที่ทำนายได้ (predict_proba) บนชุดทดสอบ — เก็บค่าเฉลี่ยของความน่าจะเป็นคลาส 1 ไว้ใน mean_proba และสัดส่วนแถวที่ proba >= 0.5 ไว้ใน share_pos (ปัด 4 ตำแหน่ง)

Quiz

1. Random forest ลด overfitting ของต้นไม้เดี่ยวได้อย่างไร
2. OOB score คืออะไร
3. ในบทนี้ forest (leaf 10) กับต้นไม้ depth 3 ให้ CV accuracy ใกล้กัน (0.6432 กับ 0.6562) บอกอะไร
4. จะลด overfitting ของ forest ที่ train 0.9995 / test 0.6197 ควรปรับอะไร

สรุป

  • Random forest = ต้นไม้หลายต้นจาก bootstrap + สุ่ม feature ต่อ split แล้วโหวตเฉลี่ย ช่วยลด variance
  • ค่าเริ่มต้นยังจำข้อมูลเทรนได้ (train 0.9995 / test 0.6197) จำกัดขนาดต้นไม้ย่อยด้วย min_samples_leaf/max_depth
  • OOB score = validation ฟรี; ในข้อมูลนี้ forest ไม่ชนะต้นไม้ตื้นหรือ logistic regression (ต่างกันราว 0.01) เพราะสัญญาณอ่อน

คำถามสัมภาษณ์

Random forest ต่างจาก bagging ของ decision tree ธรรมดาอย่างไร

Bagging สุ่มแถวแบบ bootstrap ให้แต่ละต้น แล้วเฉลี่ย Random forest เพิ่มการสุ่ม feature ที่แต่ละ split (max_features) เพื่อให้ต้นไม้ไม่สัมพันธ์กันมากนัก ทำให้เฉลี่ยแล้วลด variance ได้มากกว่า

เมื่อไรที่ random forest ไม่ช่วย

เมื่อสัญญาณในข้อมูลอ่อนหรือ noise สูง ต้นไม้ทุกต้นเรียนรู้ได้ไม่ต่างกันมาก ensemble จึงได้ผลใกล้กับโมเดลง่ายๆ (เช่นในข้อมูล repeat ของเรา) หรือเมื่อ feature engineering สำคัญกว่าตัวโมเดล