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 ไว้ในวัตถุเดียว

  1. fit pipeline บน train แล้ววัด accuracy และ AUC บนชุดทดสอบ

    Python

cross-validation ของทั้ง pipeline

วัดผลที่เชื่อถือได้โดยไม่รั่วจากการเตรียมข้อมูล

  1. 5-fold stratified CV วัด ROC-AUC ของทั้ง pipeline — ค่าเฉลี่ยประมาณ 0.64 มี std ประมาณ 0.02

    Python

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

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

ยังไม่ได้ทำ

สร้าง 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

1. ข้อดีหลักของ Pipeline คืออะไร
2. ColumnTransformer ใช้ทำอะไร
3. ส่ง Pipeline เข้า cross_val_score ต่างจากส่งข้อมูลที่ scale ไปแล้วอย่างไร
4. ทำไม pipeline ในบทนี้ทำนายลูกค้าจากเมืองใหม่ได้โดยไม่ error

สรุป

  • 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 เดียวช่วยให้ทำนายจากข้อมูลดิบได้ตรงกับตอนเทรนเสมอ และเวอร์ชันของตัวแปลงกับโมเดลอยู่ด้วยกัน