Machine Learning › Feature engineering
Pipeline และ ColumnTransformer
ประมาณ 30 นาที
เป้าหมายของบท
- รวมขั้นตอนเตรียมข้อมูลกับโมเดลด้วย Pipeline และ ColumnTransformer
- วัดผลทั้ง pipeline ด้วย cross-validation โดยไม่มี leakage
- ส่งข้อมูลดิบเข้า pipeline ที่เทรนแล้วเพื่อทำนายได้เลย
ปัญหาของการทำทีละขั้นด้วยมือ
ตอนนี้มีหลายขั้นตอน: scale ตัวเลข, one-hot ตัวหมวด, แล้วค่อยเทรนโมเดล — ถ้าทำแยกด้วยมือ ต้องจำว่าขั้นไหน fit บนอะไร และพลาดง่าย (เช่น fit scaler ทั้งชุดก่อนแบ่ง หรือใช้ตัวแปลงคนละชุดกับตอนเทรน) Pipeline รวมทุกขั้นตอนเป็นวัตถุเดียว
ColumnTransformer: ทำคนละอย่างกับแต่ละกลุ่มคอลัมน์
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.linear_model import LogisticRegression
pre = ColumnTransformer([
('num', StandardScaler(), num), # ตัวเลข → scale
('cat', OneHotEncoder(handle_unknown='ignore'), cat), # หมวดหมู่ → one-hot
])
pipe = Pipeline([('pre', pre), ('model', LogisticRegression(max_iter=2000))])
pipe.fit(X_train, y_train) # fit ตัวแปลงบน train แล้วเทรนโมเดล ในคำสั่งเดียว
pipe.predict_proba(X_new) # ส่งข้อมูลดิบเข้าไปได้เลย
ตอน fit มันจะ fit ตัวแปลงบน train เท่านั้น และตอน predict จะใช้ตัวแปลงเดิมกับข้อมูลใหม่โดยอัตโนมัติ — ไม่มีทางลืมหรือ fit ผิดชุด
cross-validation ทั้ง pipeline
ส่ง pipeline เข้า cross_val_score ได้ตรงๆ — ในแต่ละ fold มันจะ fit ตัวแปลงใหม่บนส่วน train ของ fold นั้นเท่านั้น จึงไม่มี leakage จากการเตรียมข้อมูล
(ถ้า scale ทั้งชุดก่อนแล้วค่อย CV ข้อมูลของ fold ทดสอบจะปนอยู่ใน mean/std แล้ว)
from sklearn.model_selection import StratifiedKFold, cross_val_score
skf = StratifiedKFold(5, shuffle=True, random_state=42)
cross_val_score(pipe, X_all, y, cv=skf, scoring='roc_auc')
ได้อะไรอีก
- ใช้กับข้อมูลใหม่ที่มีหมวดไม่เคยเห็นได้ (จาก
handle_unknown='ignore') โดยไม่ error - ส่งออก/นำไปใช้งานจริงเป็นก้อนเดียว ไม่ต้องพกตัวแปลงหลายตัว
pipe.named_steps['pre'].get_feature_names_out()ดูชื่อ feature หลังแปลง
ตัวอย่างแก้โจทย์
กด "ขั้นต่อไป" ทีละขั้น ลองรันและแก้โค้ดในแต่ละขั้นได้
สร้างและเทรน Pipeline
รวม scale + one-hot + LogisticRegression ไว้ในวัตถุเดียว
fit pipeline บน train แล้ววัด accuracy และ AUC บนชุดทดสอบ
cross-validation ของทั้ง pipeline
วัดผลที่เชื่อถือได้โดยไม่รั่วจากการเตรียมข้อมูล
5-fold stratified CV วัด ROC-AUC ของทั้ง pipeline — ค่าเฉลี่ยประมาณ 0.64 มี std ประมาณ 0.02
เติมช่องว่าง
แทน ___ ด้วยโค้ดที่ถูกต้อง แล้วกดตรวจคำตอบ
สร้าง ColumnTransformer ที่ one-hot คอลัมน์หมวดหมู่ แล้วนับจำนวน feature หลัง fit บน train เก็บไว้ในตัวแปร n_features เติม ___ ให้ครบ
แบบฝึกท้ายบท
บทนี้จะนับว่าเสร็จเมื่อผ่านข้อพื้นฐานและประยุกต์ครบ ข้อท้าทายได้ดาวเพิ่ม
🟢 พื้นฐาน
หลัง fit pipeline บน train มี feature หลังแปลงกี่คอลัมน์ เก็บไว้ในตัวแปร n_features_out
accuracy ของ pipeline บนชุดทดสอบ ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร test_acc
ROC-AUC ของ pipeline บนชุดทดสอบ ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร test_auc
pipeline นี้มีกี่ขั้นตอน (steps) เก็บไว้ในตัวแปร n_steps
🟡 ประยุกต์
ค่าเฉลี่ย ROC-AUC ของ 5-fold StratifiedKFold(shuffle=True, random_state=42) ทั้ง pipeline (ใช้ df[num + cat] และ df["repeated"]) ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร cv_auc_mean
ส่วนเบี่ยงเบนมาตรฐานของคะแนน AUC ทั้ง 5 fold จากข้อ e5 ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร cv_auc_std
ค่าเฉลี่ย accuracy ของ CV เดียวกัน ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร cv_acc_mean
🔴 ท้าทาย
fit pipeline บน train แล้วทำนายความน่าจะเป็นที่จะซื้อซ้ำของลูกค้าใหม่ 2 คน (ข้อมูลดิบจากฟังก์ชัน new_customer ที่ให้ไว้): ช่องทาง promo และช่องทาง organic (ปัด 4 ตำแหน่ง) เก็บไว้ในตัวแปร proba_promo, proba_organic
ลูกค้าจากเมืองที่ไม่มีในข้อมูลเทรน (city="Mars", ช่องทาง organic) ยังทำนายได้ไหม — เก็บความน่าจะเป็นที่ได้ (ปัด 4 ตำแหน่ง) ไว้ในตัวแปร proba_unknown_city
Quiz
สรุป
- Pipeline + ColumnTransformer รวม scale/one-hot/โมเดลเป็นก้อนเดียวและ fit ตัวแปลงบน train เท่านั้น
- ส่ง pipeline ทั้งก้อนเข้า cross_val_score เพื่อไม่ให้การเตรียมข้อมูลรั่วข้าม fold (ในข้อมูลนี้ AUC เฉลี่ย ~0.64, std ~0.02)
- handle_unknown="ignore" ทำให้ pipeline รับหมวดใหม่ (เช่น เมืองที่ไม่เคยเห็น) ตอนใช้งานจริงได้โดยไม่ error
คำถามสัมภาษณ์
อธิบายว่า Pipeline ช่วยป้องกัน data leakage อย่างไร
Pipeline ห่อขั้นตอนเตรียมข้อมูลและโมเดลไว้ด้วยกัน ตอน fit จะเรียนรู้ตัวแปลง (mean/std ของ scaler, หมวดของ encoder) จากข้อมูล train เท่านั้น และตอน predict/transform จะใช้ค่าที่เรียนรู้ไว้แล้วกับข้อมูลใหม่ เมื่อใช้ร่วมกับ cross-validation แต่ละ fold จะ fit ตัวแปลงใหม่บนส่วน train ของ fold นั้น ข้อมูลทดสอบจึงไม่มีผลต่อการเตรียมข้อมูลเลย
ถ้าต้องส่งโมเดลไปใช้งานจริง ทำไมควรส่งเป็น pipeline ทั้งก้อน
เพราะข้อมูลดิบที่เข้ามาต้องผ่านการแปลงเหมือนตอนเทรนทุกขั้นตอน ถ้าแยกตัวแปลงกับโมเดลออกจากกัน เสี่ยงที่จะใช้ตัวแปลงคนละชุดหรือลืมขั้นตอน pipeline เดียวช่วยให้ทำนายจากข้อมูลดิบได้ตรงกับตอนเทรนเสมอ และเวอร์ชันของตัวแปลงกับโมเดลอยู่ด้วยกัน