Machine Learning › Tree และ boosting

จูน hyperparameter และ feature importance

ประมาณ 35 นาที

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

  • จูน hyperparameter ด้วย GridSearchCV บน train และอ่านผลจาก cv_results_
  • เข้าใจว่า impurity importance เอนเอียง และตรวจซ้ำด้วย permutation importance
  • เลือกโมเดลสุดท้ายอย่างซื่อตรงเมื่อผลต่างกันเพียงเล็กน้อย

Hyperparameter คือค่าที่เรา “ตั้ง” ไม่ใช่ที่โมเดล “เรียน”

max_depth, min_samples_leaf, n_estimators, learning_rate ไม่ได้ถูกเรียนรู้จากข้อมูล เราต้องเลือกเอง — วิธีมาตรฐานคือลองหลายชุดแล้ววัดด้วย cross-validation

GridSearchCV

from sklearn.model_selection import GridSearchCV, StratifiedKFold
from sklearn.tree import DecisionTreeClassifier

skf = StratifiedKFold(5, shuffle=True, random_state=42)
param_grid = {'max_depth': [2, 3, 4, 5], 'min_samples_leaf': [10, 30, 60]}

gs = GridSearchCV(DecisionTreeClassifier(random_state=42), param_grid, cv=skf, scoring='roc_auc')
gs.fit(X_train, y_train)          # 12 ชุด × 5 fold = 60 ครั้ง
gs.best_params_, gs.best_score_   # ชุดที่ดีที่สุด และคะแนน CV เฉลี่ย
  • ค่า grid ใช้ train เท่านั้น — cv แบ่ง train ออกเป็น fold เอง
  • หลังหาชุดที่ดีที่สุดเสร็จ refit=True (ค่าเริ่มต้น) จะเทรนใหม่บน train ทั้งหมด ใช้ gs.predict / gs.score(X_test, y_test) ได้เลย
  • gs.cv_results_ (แปลงเป็น DataFrame) ดูคะแนนทุกชุดได้ — สำคัญมากเพราะชุดที่ต่างกันเล็กน้อยมักเสมอกัน
  • ข้อมูลของเรา: ชุดที่ดีที่สุดคือ max_depth=4, min_samples_leaf=60 (CV AUC 0.6387 test accuracy 0.655) แต่ชุดอันดับ 2–4 ต่างกันแค่ 0.005 — อย่าตื่นเต้นกับ “อันดับ 1”

จำนวนการเทรนโตเป็น ผลคูณ ของทุกแกน (grid 5×5×5 คือ 125 ชุด × 5 fold) เมื่อเวลาแพงให้ลดขอบเขต หรือใช้ RandomizedSearchCV (สุ่มแค่ n ชุด)

Feature importance: อย่าเชื่อตัวแรกที่เจอ

Impurity importance (feature_importances_) คือผลรวมของการลด impurity ที่ feature นั้นสร้าง — คำนวณจากข้อมูลเทรนและ เอนเอียงไปหา feature ที่มีค่าละเอียดมาก (ตัวเลขต่อเนื่อง, ค่าหลากหลาย) ทดลองใส่คอลัมน์สุ่มล้วน noise เข้าไปในข้อมูล: forest ให้ noise อยู่ อันดับ 2 ด้วยความสำคัญ 0.1761!

Permutation importance: สลับค่าของ feature หนึ่งในชุดทดสอบแบบสุ่มแล้วดูว่าคะแนน (เช่น AUC) ตกลงเท่าไร — ถ้าตกมาก แปลว่าโมเดลพึ่ง feature นั้นจริง

from sklearn.inspection import permutation_importance
pi = permutation_importance(rf, X_test, y_test, n_repeats=5, random_state=42, scoring='roc_auc')
pi.importances_mean

ในการทดลองเดียวกัน noise ได้ permutation importance เพียง ≈ 0.002 ขณะที่ channel_promo ≈ 0.046

ข้อควรจำ: importance บอก “โมเดลพึ่ง feature ไหน” ไม่ได้บอก “สาเหตุ” และ feature ที่สัมพันธ์กันมากจะแบ่งความสำคัญกัน

เลือกโมเดลสุดท้ายอย่างซื่อตรง

ผลจากข้อมูลนี้ (grid บน train, CV AUC): tree ≈ 0.639, random forest ≈ 0.655, gradient boosting ≈ 0.646 — ต่างกันเพียงราว 0.01–0.02 ทั้งหมด การเลือกที่สมเหตุสมผลคือ โมเดลที่เรียบง่ายและอธิบายได้พอกัน ไม่ใช่ตัวที่ตัวเลขสูงกว่าเพราะโชค; รายงานช่วงความไม่แน่นอน (std ระหว่าง fold) และแตะชุด test ครั้งเดียวตอนท้าย

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

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

GridSearchCV บนต้นไม้

ค้นหา max_depth และ min_samples_leaf ด้วย CV

  1. grid 4×3 = 12 ชุด แต่ละชุด CV 5 fold: ได้ max_depth=4, min_samples_leaf=60 และ CV AUC ≈ 0.6387

    Python

impurity vs permutation importance

ทดลองใส่ feature สุ่มล้วนเข้าไปแล้วดูว่า importance วิธีไหนถูกหลอก

  1. เพิ่มคอลัมน์ noise แล้ว fit forest — impurity importance ให้ noise อันดับ 2 (ความสำคัญ 0.1761)

    Python

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

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

ยังไม่ได้ทำ

ตั้ง GridSearchCV ของต้นไม้ (max_depth ∈ [2, 3], min_samples_leaf ∈ [10, 30]) แล้วนับจำนวนชุดที่ลองใน n_candidates เติม ___ ให้ครบ

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

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

🟢 พื้นฐาน

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

grid ที่ให้ไว้ (param_grid) มีกี่ชุดพารามิเตอร์ และถ้า CV 5 fold ต้อง fit ทั้งหมดกี่ครั้ง เก็บไว้ในตัวแปร n_candidates, n_fits

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

ชุดพารามิเตอร์ที่ดีที่สุด (scoring="roc_auc") มี max_depth และ min_samples_leaf เท่าไร เก็บไว้ในตัวแปร best_depth, best_leaf

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

คะแนน CV (ROC-AUC เฉลี่ย) ของชุดที่ดีที่สุด ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร best_cv_auc

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

โมเดลที่ refit แล้ว (gs) วัด ROC-AUC บนชุดทดสอบ (gs.score ใช้ scoring เดียวกัน) ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร test_auc

🟡 ประยุกต์

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

จากผลของ gs.cv_results_ ชุดพารามิเตอร์ที่ได้คะแนนเฉลี่ยต่ำสุดมี max_depth และ min_samples_leaf เท่าไร เก็บไว้ในตัวแปร worst_depth, worst_leaf

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

GridSearchCV บน RandomForestClassifier(n_estimators=50, random_state=42) ด้วย grid max_depth ∈ [3, 5] × min_samples_leaf ∈ [10, 30] (cv=skf, scoring="roc_auc") เก็บ max_depth ที่ดีที่สุดไว้ใน rf_best_depth, min_samples_leaf ไว้ใน rf_best_leaf และคะแนน CV (ปัด 4 ตำแหน่ง) ไว้ใน rf_best_auc

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

ความ "มองโลกในแง่ดี" ของการจูน: คะแนน CV ที่ดีที่สุดลบด้วย AUC บนชุดทดสอบของ gs (ปัด 4 ตำแหน่ง) เก็บไว้ในตัวแปร optimism

🔴 ท้าทาย

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

fit forest (n_estimators=100, min_samples_leaf=10, random_state=42) บน train แล้วหา permutation importance บนชุดทดสอบ (n_repeats=5, random_state=42, scoring="roc_auc") — feature ที่สำคัญสุดคืออะไร (string) เก็บไว้ใน top_perm_feature และค่า (ปัด 3 ตำแหน่ง) ไว้ใน top_perm_value

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

ตั้งค่า Xn_train/Xn_test ที่มีคอลัมน์ noise สุ่มล้วนไว้ให้แล้ว fit forest (n_estimators=100, random_state=42) บน Xn_train: อันดับของ noise ตาม impurity importance (1 = สำคัญสุด) เก็บไว้ใน noise_rank และ permutation importance ของ noise (AUC, n_repeats=5, random_state=42, ปัด 3 ตำแหน่ง) ไว้ใน noise_perm

Quiz

1. hyperparameter ต่างจาก parameter ของโมเดลอย่างไร
2. ทำไม GridSearchCV จึงควรเรียกบนข้อมูล train เท่านั้น
3. ในการทดลองใส่คอลัมน์สุ่ม noise: forest จัดอันดับ noise ที่ 2 ด้วย impurity importance แต่ permutation importance ให้ ≈ 0.002 สรุปได้ว่า
4. ผล grid ของต้นไม้: อันดับ 1 (CV AUC 0.6387) กับอันดับ 2–4 ต่างกันแค่ ~0.005 ควรทำอย่างไร

สรุป

  • GridSearchCV ลองทุกชุดใน grid ด้วย CV บน train (จำนวน fit = ชุด × fold) แล้ว refit ชุดที่ดีที่สุด — แตะ test ครั้งเดียวตอนท้าย
  • impurity importance เอนเอียง: คอลัมน์สุ่ม noise ได้อันดับ 2 (0.1761) แต่ permutation importance ≈ 0.002 (promo ≈ 0.046)
  • tree/forest/GB ต่างกันราว 0.01 AUC ในข้อมูลนี้ เลือกตัวที่เรียบง่าย อธิบายได้ และรายงานความไม่แน่นอน

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

อธิบายขั้นตอนจูนโมเดลอย่างถูกต้องโดยไม่ให้ test รั่ว

แบ่ง train/test ก่อน จากนั้นใช้ cross-validation บนเฉพาะ train เพื่อเปรียบเทียบชุด hyperparameter (GridSearchCV/RandomizedSearchCV) เลือกชุดที่ดีที่สุด refit บน train ทั้งหมด แล้วประเมินบน test เพียงครั้งเดียวเพื่อรายงานผล ถ้าต้องการผลที่ไม่เอนเอียงมากขึ้นใช้ nested CV

feature importance ของ random forest เชื่อถือได้แค่ไหน

impurity importance คำนวณจากข้อมูลเทรนและเอนเอียงไปหา feature ต่อเนื่อง/มีหลายค่า (ทดลองใส่คอลัมน์สุ่มก็ยังได้อันดับสูง) permutation importance บน validation/test เชื่อถือกว่าเพราะวัดผลกระทบต่อคะแนนจริง แต่ก็ไม่ใช่สาเหตุ และ feature ที่สัมพันธ์กันจะแบ่งความสำคัญกัน