Machine Learning › Tree และ boosting
Gradient Boosting
ประมาณ 35 นาที
เป้าหมายของบท
- อธิบายกลไกของ gradient boosting (ต้นไม้ตื้นทีละต้น แก้ความผิดพลาดต่อเนื่อง)
- ปรับ n_estimators / learning_rate / max_depth และดูเส้นโค้งด้วย staged_predict บน validation
- ใช้ early stopping ในตัวเพื่อเลือกจำนวนรอบ
Boosting: ต้นไม้ตื้นๆ ที่ช่วยกันแก้ความผิดพลาดของต้นก่อนหน้า
Random forest สร้างต้นไม้ลึกๆ พร้อมกัน แล้วเฉลี่ย ส่วน gradient boosting สร้างต้นไม้ตื้นๆ ทีละต้น โดยแต่ละต้นพยายามแก้ส่วนที่โมเดลรวมของต้นก่อนหน้ายังทำนายพลาด:
- เริ่มจากคำตอบง่ายๆ (เช่น สัดส่วนคลาสเฉลี่ย)
- คำนวณ “ความผิดพลาด” (gradient ของ loss) ของแต่ละแถว
- เทรนต้นไม้เล็กๆ ทำนายความผิดพลาดนั้น แล้วบวกเข้าโมเดลโดยย่อขนาดด้วย
learning_rate - ทำซ้ำ
n_estimatorsรอบ
from sklearn.ensemble import GradientBoostingClassifier
gb = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1, max_depth=3, random_state=42)
gb.fit(X_train, y_train)
ไฮเปอร์พารามิเตอร์ที่ต้องรู้
n_estimators— จำนวนรอบ (มากขึ้น = พอดีข้อมูลเทรนมากขึ้น)learning_rate— ก้าวของแต่ละรอบ; ค่าเล็กต้องใช้รอบมากขึ้นแต่มักนิ่งกว่า (เป็น trade-off คู่กับn_estimators)max_depth— ความลึกของต้นไม้ย่อย (boosting ใช้ตื้น 1–5)
ต่างจาก forest: ในบูสต์ จำนวนรอบที่มากเกินไปทำให้ overfit ได้ ผลจากข้อมูลของเรา (learning_rate=0.1, depth 3):
| จำนวนรอบ | test accuracy |
|---|---|
| 1 | 0.5901 |
| 5 | 0.6437 |
| 10 | 0.6493 |
| 25 | 0.6535 |
| 50 | 0.6451 |
| 100 | 0.6394 |
ผลบน test ขึ้นไปสูงสุดราว 25 รอบแล้วค่อยๆ ลง ขณะที่ train ยังเพิ่มขึ้น (100 รอบ: train 0.7172; 300 รอบ: train 0.8074 / test 0.6225)
staged_predict และ early stopping
gb.staged_predict(X) คืนคำทำนายหลังแต่ละรอบ ทำให้เห็นเส้นโค้งแบบนี้ได้ในการ fit ครั้งเดียว — แต่ให้ดูบน validation ไม่ใช่ test
ทางลัดคือ early stopping ในตัว: ตั้ง n_iter_no_change=10 และ validation_fraction=0.1 แล้วมันจะกันข้อมูลส่วนหนึ่งไว้ตรวจเอง และหยุดเมื่อผลไม่ดีขึ้น 10 รอบติด
gb = GradientBoostingClassifier(n_estimators=500, validation_fraction=0.1, n_iter_no_change=10, random_state=42)
gb.fit(X_train, y_train)
gb.n_estimators_ # 25 ในข้อมูลนี้ (ไม่ใช่ 500)
เมื่อเทียบกับ forest
boosting มักแม่นกว่าเมื่อมีสัญญาณซับซ้อนหลายชั้นพอ แต่จูนยากกว่า ไวต่อ noise/รอบมากเกินไป ในข้อมูลของเรา (CV 5-fold) GB ค่าเริ่มต้นได้ accuracy ≈ 0.6492 และ AUC ≈ 0.6526 ซึ่งสูงกว่า forest (0.6432 / 0.6420) เพียงเล็กน้อย — ยังอยู่ในระดับ “ต่างกันไม่กี่ร้อยส่วน” ไม่ใช่ชัยชนะเด็ดขาด
ตัวอย่างแก้โจทย์
กด "ขั้นต่อไป" ทีละขั้น ลองรันและแก้โค้ดในแต่ละขั้นได้
GB ค่าเริ่มต้นและจำนวนรอบ
ดูว่าผลเปลี่ยนตามจำนวนรอบอย่างไร
fit GB 100 รอบ (lr 0.1, depth 3): train 0.7172 test 0.6394
early stopping ในตัว
ให้ GB หยุดเองเมื่อ validation ไม่ดีขึ้น
ตั้ง n_estimators=500 แต่เปิด n_iter_no_change=10 — มันหยุดที่ 25 รอบ (n_estimators_) แทนที่จะครบ 500
เติมช่องว่าง
แทน ___ ด้วยโค้ดที่ถูกต้อง แล้วกดตรวจคำตอบ
fit GB ด้วย learning_rate=0.05 (random_state=42) แล้วเก็บ accuracy บนชุดทดสอบไว้ในตัวแปร lr_acc เติม ___ ให้ครบ
แบบฝึกท้ายบท
บทนี้จะนับว่าเสร็จเมื่อผ่านข้อพื้นฐานและประยุกต์ครบ ข้อท้าทายได้ดาวเพิ่ม
🟢 พื้นฐาน
fit GradientBoostingClassifier(random_state=42) (ค่าเริ่มต้นอื่นๆ) แล้วเก็บ accuracy บน train และ test (ปัด 4 ตำแหน่ง) ไว้ในตัวแปร gb_train_acc, gb_test_acc
GB ที่ตั้ง n_estimators=30 (random_state=42) มีกี่รอบ/ต้นไม้ย่อย (ดูจาก n_estimators_) เก็บไว้ในตัวแปร n_stages
GB ที่ learning_rate=0.05 (random_state=42): accuracy บนชุดทดสอบ ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร lr05_test_acc
GB ที่ใช้ต้นไม้ตอ (max_depth=1, random_state=42): accuracy บนชุดทดสอบ ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร stump_test_acc
🟡 ประยุกต์
fit GB ค่าเริ่มต้น (random_state=42) แล้วใช้ staged_predict บน X_test เก็บ accuracy หลังรอบที่ 1, 10 และ 50 (ปัด 4 ตำแหน่ง) ไว้ในตัวแปร acc_1, acc_10, acc_50
ใช้ชุด validation (X_tr/X_val/y_tr/y_val ที่แบ่งจาก train ไว้ให้แล้ว) เทรน GB 100 รอบ (random_state=42) แล้วหาจำนวนรอบที่ให้ accuracy บน X_val สูงสุด (ถ้าเสมอเอาน้อยสุด) เก็บไว้ใน best_stage และ accuracy ของมัน (ปัด 4 ตำแหน่ง) ไว้ใน best_val_acc
GB ที่เปิด early stopping (n_estimators=500, validation_fraction=0.1, n_iter_no_change=10, random_state=42): หยุดที่กี่รอบ เก็บไว้ใน stopped_at และ accuracy บนชุดทดสอบ (ปัด 4 ตำแหน่ง) ไว้ใน es_test_acc
🔴 ท้าทาย
CV 5-fold (skf ที่ให้ไว้) ของ ROC-AUC เฉลี่ย เทียบ GB ค่าเริ่มต้น (random_state=42) กับ random forest (100 ต้น, min_samples_leaf=10, random_state=42) ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร gb_cv_auc, rf_cv_auc
GB 100 รอบ (random_state=42) เทียบ learning_rate สูง (0.5) กับต่ำ (0.01): accuracy บน train และ test ของแต่ละแบบ (ปัด 4 ตำแหน่ง) เก็บไว้ใน fast_train, fast_test, slow_train, slow_test
Quiz
สรุป
- Gradient boosting เพิ่มต้นไม้ตื้นทีละต้นเพื่อแก้ความผิดพลาดของโมเดลรวม; learning_rate ย่อแต่ละก้าว
- รอบมากเกินไป overfit ได้ (ข้อมูลนี้ test สูงสุดที่ ~25 รอบ 0.6535 แล้วลดเหลือ 0.6394 ที่ 100 รอบ) เลือกรอบด้วย validation/early stopping ไม่ใช่ test
- GB ค่าเริ่มต้นได้ CV AUC 0.6526 เทียบ forest 0.6420 — ต่างกันเล็กน้อยเท่านั้น
คำถามสัมภาษณ์
อธิบาย gradient boosting ให้คนที่รู้จักแต่ decision tree
เริ่มจากคำตอบง่ายๆ แล้วสร้างต้นไม้เล็กๆ ทีละต้นเพื่อทำนาย "ส่วนที่ยังพลาด" (gradient ของ loss) ของโมเดลปัจจุบัน แล้วบวกเข้าไปโดยคูณ learning_rate ทำซ้ำหลายรอบ ผลรวมของต้นไม้ตื้นๆ เหล่านี้จึงสร้างขอบเขตการตัดสินใจที่ซับซ้อนได้
จะเลือก n_estimators และ learning_rate อย่างไรไม่ให้ overfit
ทั้งสองแลกกัน (ก้าวเล็ก = ต้องรอบมาก) ตั้ง learning_rate ค่อนข้างเล็กแล้วใช้ early stopping (n_iter_no_change + validation_fraction) หรือ validation set แยกเพื่อหยุดตอน validation หยุดดีขึ้น และยืนยันด้วย cross-validation แล้วแตะ test ครั้งเดียว