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
- Bootstrap: แต่ละต้นเทรนบนตัวอย่างที่สุ่มแบบใส่คืนจากข้อมูล train (ราว 63% ของแถวไม่ซ้ำ)
- สุ่ม feature ต่อทุกจุดแยก: ที่แต่ละ split พิจารณาเพียงบางส่วนของ feature (
max_features='sqrt'เป็นค่าเริ่มต้นของ classifier) ทำให้ต้นไม้ไม่ซ้ำกัน - ทำนายโดย โหวตข้างมาก (
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 ช่วยลดการจำข้อมูลเทรนได้แค่ไหน
forest 100 ต้น: train 0.9995 แต่ test 0.6197 (เทียบกับต้นไม้เดี่ยวที่ test 0.5493)
OOB score และ CV
ประมาณการความแม่นโดยไม่ใช้ test
เปิด oob_score=True แล้วอ่าน oob_score_ (≈ 0.6534) — ประมาณการความแม่นจากแถวที่แต่ละต้นไม่เคยเห็น
เติมช่องว่าง
แทน ___ ด้วยโค้ดที่ถูกต้อง แล้วกดตรวจคำตอบ
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
สรุป
- 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 สำคัญกว่าตัวโมเดล