Machine Learning › Feature engineering

Scaling และ missing values

ประมาณ 30 นาที

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

  • ใช้ StandardScaler โดย fit บน train เท่านั้น และรู้ว่าโมเดลแบบไหนไวต่อสเกล
  • ไม่เชื่อว่า scaling ช่วยเสมอไป — วัดผลก่อนตัดสินใจ
  • เติม missing values ด้วย SimpleImputer โดยใช้ค่าจาก train

scaling: ปรับตัวเลขให้อยู่สเกลใกล้กัน

feature ต่างๆ มีสเกลต่างกันมาก (first_discount_pct 0–20, days_since_first_order 3–728) โมเดลบางแบบไวต่อสเกล — โดยเฉพาะที่ใช้ ระยะทาง (KNN, SVM) หรือ regularization/gradient (logistic regression, neural network) ส่วน decision tree ไม่สนใจสเกล

StandardScaler ปรับให้แต่ละคอลัมน์มี mean 0 และ std 1: z = (x − mean) / std

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler().fit(X_train[num])   # เรียนรู้ mean/std จาก train เท่านั้น
X_train_s = scaler.transform(X_train[num])
X_test_s = scaler.transform(X_test[num])      # ใช้ mean/std เดิมจาก train

กฎสำคัญ: fit บน train เท่านั้น แล้ว transform ทั้ง train และ test — ถ้า fit บนข้อมูลทั้งชุด ค่า mean/std จะมีข้อมูล test ปนอยู่ (leakage แบบละเอียด) ผลคือ train จะมี mean = 0 พอดี ส่วน test จะเบี่ยงเล็กน้อย ซึ่งเป็นเรื่องปกติและถูกต้อง

scaling ช่วยเสมอไหม? ไม่เสมอไป

scaling เปลี่ยน “ใครมีอิทธิพลต่อระยะทาง” — ตัวอย่างในบทนี้ KNN ที่ไม่ scale ถูกครอบงำด้วย days_since_first_order (ค่าเป็นร้อย) ซึ่งบังเอิญเป็น feature ที่มีประโยชน์ พอ scale แล้วทุก feature (รวม feature 0/1 ที่มีแต่สัญญาณรบกวน) มีน้ำหนักเท่ากัน ผลกลับแย่ลงเล็กน้อย — ต้องวัดด้วยข้อมูลจริงเสมอ ไม่ใช่เชื่อกฎลอยๆ

missing values

โมเดลส่วนใหญ่ใน scikit-learn (เช่น LogisticRegression) error ถ้าเจอ NaN จึงต้องจัดการก่อน วิธีพื้นฐานคือเติมค่า (impute) ด้วย SimpleImputer:

from sklearn.impute import SimpleImputer

imputer = SimpleImputer(strategy='median').fit(X_train[['days_to_first_order']])
imputer.transform(X_test[['days_to_first_order']])
  • median ทนต่อค่าสุดโต่งกว่า mean — เหมาะกับข้อมูลเบ้
  • เช่นเดียวกับ scaler: fit บน train เท่านั้น แล้วเติมค่าให้ทั้ง train และ test ด้วยค่าจาก train

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

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

StandardScaler ที่ fit บน train

ปรับสเกล feature ตัวเลขโดยไม่ให้ข้อมูล test ปนเข้ามา

  1. fit scaler บน train — ดู mean และ std ของ days_to_first_order ที่เรียนรู้ได้

    Python

scaling กับ KNN

KNN ใช้ระยะทาง — scale แล้วดีขึ้นไหม

  1. KNN (k=15) บน feature ตัวเลขทั้ง 6 ตัวโดยไม่ scale

    Python

เติมค่าที่หายไปด้วย SimpleImputer

บางแถวไม่มีค่า days_to_first_order — เติมยังไงให้ไม่รั่ว

  1. จำลองข้อมูลหาย: ตั้ง days_to_first_order เป็น NaN ให้ลูกค้า customer_id ที่หาร 10 ลงตัว

    Python

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

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

ยังไม่ได้ทำ

fit SimpleImputer แบบ median บน train แล้วเก็บค่ากลางที่เรียนรู้ไว้ในตัวแปร train_median เติม ___ ให้ครบ

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

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

🟢 พื้นฐาน

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

mean ของ days_to_first_order ที่ StandardScaler เรียนรู้จาก train ปัด 3 ตำแหน่ง เก็บไว้ในตัวแปร train_mean

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

ส่วนเบี่ยงเบนมาตรฐาน (scale_) ของ days_to_first_order ที่เรียนรู้จาก train ปัด 3 ตำแหน่ง เก็บไว้ในตัวแปร train_scale

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

หลัง scale ชุดทดสอบด้วย scaler จาก train: mean ของ days_to_first_order ในชุดทดสอบ ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร test_mean_scaled

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

หลังตั้ง NaN ตามที่จำลองไว้ มีแถว days_to_first_order ที่หายกี่แถว เก็บไว้ในตัวแปร n_missing

🟡 ประยุกต์

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

ค่ามัธยฐานของ days_to_first_order ที่ SimpleImputer(strategy="median") เรียนรู้จากชุด train เก็บไว้ในตัวแปร train_median

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

ในชุดทดสอบมี days_to_first_order ที่หายกี่แถว เก็บไว้ในตัวแปร n_test_missing

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

KNN (n_neighbors=15) บน feature ตัวเลข 6 ตัวโดยไม่ scale (ใช้ข้อมูล FEATS ที่ยังไม่มี NaN): accuracy บนชุดทดสอบ ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร knn_acc_unscaled

🔴 ท้าทาย

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

KNN แบบเดียวกันแต่ scale ด้วย StandardScaler ที่ fit บน train ก่อน: accuracy บนชุดทดสอบ ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร knn_acc_scaled

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

เติมค่าที่หายในชุดทดสอบด้วยค่ามัธยฐานจาก train แล้วหาค่าเฉลี่ยของ days_to_first_order ในชุดทดสอบหลังเติม ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร filled_test_mean

Quiz

1. ทำไมต้อง fit StandardScaler บนชุด train เท่านั้น
2. โมเดลแบบไหนได้ประโยชน์จากการ scale มากที่สุด
3. ในบทนี้ KNN ที่ scale แล้วได้ accuracy สูงกว่าแบบไม่ scale หรือไม่
4. เมื่อข้อมูลเบ้ขวา ควรเติมค่าที่หายด้วยอะไร

สรุป

  • StandardScaler และ SimpleImputer ต้อง fit บน train เท่านั้น แล้ว transform ทั้ง train และ test
  • scaling เปลี่ยนว่า feature ไหนมีอิทธิพลต่อระยะทาง — ในข้อมูลนี้ KNN ที่ scale แล้วได้ accuracy ต่ำกว่า จึงต้องวัดเสมอ
  • โมเดลส่วนใหญ่ error เมื่อมี NaN; เติมด้วย median จะทนค่าสุดโต่งกว่า mean

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

อธิบายว่าทำไมการ fit scaler บนข้อมูลทั้งชุดก่อนแบ่ง train/test จึงเป็น leakage

เพราะ mean/std ที่ใช้ปรับสเกลถูกคำนวณจากข้อมูลที่รวมชุด test — ข้อมูล test จึงมีผลต่อการแปลง feature ที่โมเดลเห็นตอนเทรน ทำให้ผลทดสอบดีเกินจริงเล็กน้อยและไม่ตรงกับสถานการณ์จริงที่ข้อมูลใหม่ยังไม่เคยมีอยู่ตอนเทรน วิธีที่ถูกคือ fit บน train แล้ว transform test ด้วยค่าเดิม (หรือใส่ไว้ใน Pipeline)

ถ้าข้อมูลมี missing 5% ควรเลือกวิธีจัดการอย่างไร

ดูก่อนว่าหายแบบสุ่มหรือมีแบบแผน (เช่น หายเฉพาะกลุ่มลูกค้าบางกลุ่ม) แล้วเลือกจากลบแถว เติม median/mean/ค่าคงที่ หรือเพิ่มคอลัมน์ flag ว่าค่านี้เคยหาย และวัดผลด้วย cross-validation โดยต้องคำนวณค่าที่เติมจาก train เท่านั้น