01Overall Concept
ไอเดียหลัก — Generalized Additive Model หรือ GAM ให้ตัวแปรแต่ละตัววาดเส้นโค้งของตัวเอง แล้วนำผลของทุกเส้นมาบวกกัน จึงจับความสัมพันธ์ที่ไม่เป็นเส้นตรงได้ แต่เรายังเปิดดูได้ว่าตัวแปรแต่ละตัวส่งผลอย่างไร
ลองนึกภาพว่า — เหมือนเครื่องผสมน้ำผลไม้ที่มีปุ่มหลายปุ่ม ปุ่มอุณหภูมิ ปุ่มวันในสัปดาห์ และปุ่มราคาอาจมีรูปแบบการเพิ่มลดต่างกัน เมื่อรวมผลจากทุกปุ่ม เราจึงได้รสชาติหรือคำตอบสุดท้าย
ตัวอย่างการนำไปใช้ — พยากรณ์ยอดขายจากราคาและฤดูกาล ดูความสัมพันธ์ของอายุกับสุขภาพ หรืออธิบายการใช้พลังงานที่เปลี่ยนแบบโค้งตามอุณหภูมิ
02อธิบายภาษาคนเข้าใจง่ายๆ
GAM อยู่ตรงกลางระหว่างโมเดลที่ง่ายมากกับโมเดลที่ซับซ้อนมาก
มันยอมรับว่า
“โลกไม่ได้เป็นเส้นตรง แต่เรายังอยากเข้าใจว่าปัจจัยแต่ละตัวมีผลยังไง”
ตัวอย่างเช่น เราต้องการดูว่ายอดขายขึ้นอยู่กับอุณหภูมิอย่างไร
อาจพบว่า
ตอนอากาศ 20°C ยอดขายต่ำ 25°C เริ่มดี 30°C ดีมาก 35°C เริ่มตก 40°C ตกหนัก
ถ้าใช้ Linear Regression จะจับรูปแบบแบบนี้ไม่ค่อยดี เพราะมันคิดว่าอุณหภูมิเพิ่ม → ยอดขายควรเพิ่มหรือลดในทิศทางเดียว
GAM ยอมให้ความสัมพันธ์เป็นเส้นโค้งได้
และที่ดีคือเรายังสามารถเปิดดูได้ว่า
อายุส่งผลแบบไหน รายได้ส่งผลแบบไหน ฤดูกาลส่งผลแบบไหน
มันจึงเป็นตัวเลือกที่น่าสนใจเมื่อเราต้องการ โมเดลที่ยืดหยุ่น แต่ยังอธิบายมนุษย์ได้
ภาพจำ: ให้แต่ละตัวแปรวาดเส้นโค้งของตัวเอง แล้วเอาเส้นเหล่านั้นมาประกอบกัน
03How it works
ค่อยๆ ทำไปด้วยกัน ตั้งแต่ข้อมูลแถวแรกจนถึงการตรวจคำตอบ
ต่อเรื่องอุณหภูมิกับยอดขาย
เตรียม X เป็นอุณหภูมิ ราคา และปัจจัยที่รู้ล่วงหน้า ส่วน y คือยอดขาย แยกวันหลังไว้ทดสอบ GAM จะให้แต่ละปัจจัยมีเส้นของตัวเอง เช่น เส้นอุณหภูมิอาจขึ้นแล้วลง ส่วนเส้นราคาอาจค่อยๆ ลด โดยยังอ่านผลแยกคอลัมน์ได้
ต่อเส้นโค้งจากชิ้นส่วนเล็กๆ
ตัวอย่าง Python ในหน้านี้ใช้ SplineTransformer ตามด้วย Ridge เพื่อสร้างโมเดล additive แบบ spline สำหรับ regression ตัว transformer เปลี่ยนแต่ละคอลัมน์เป็นชุดเส้นพื้นฐานที่เรียกว่า spline basis แล้ว Ridge หาน้ำหนักของชิ้นส่วนเหล่านั้น จึงไม่ต้องวาดเส้นความสัมพันธ์เองทีละจุด
รวมเรื่องของแต่ละคอลัมน์เป็นคำตอบเดียว
แนวคิดคือยอดขาย = ค่าตั้งต้น + f(อุณหภูมิ) + g(ราคา) แต่ละฟังก์ชันเป็นผลรวม spline ที่เรียนรู้ หากอยากให้ผลของราคาขึ้นกับอุณหภูมิด้วย ต้องเพิ่ม interaction โดยตั้งใจ เพราะรูปแบบบวกแยกนี้ไม่ได้เรียนความสัมพันธ์ข้ามคอลัมน์ทั้งหมดอัตโนมัติ
เลือกจำนวนจุดต่อและแรงควบคุม
เริ่ม make_pipeline(SplineTransformer(n_knots=5, degree=3, include_bias=False), Ridge(alpha=1.0)) แล้ว fit(X_train, y_train) n_knots มากขึ้นเปิดพื้นที่ให้เส้นเปลี่ยนรูปละเอียดขึ้น degree กำหนดกำลังของ spline ส่วน alpha เป็นค่าปรับน้ำหนักของ Ridge ตัวอย่างนี้ไม่เหมือนการลงโทษความโค้งโดยตรงของ GAM ทุกไลบรารี
ดูทั้งเส้นและการทายจริง
predict(X_test) ให้ยอดขาย ตรวจ MAE และลอง n_knots หลายค่าร่วมกับ alpha ผ่านการแบ่งตามเวลา หากอยากดูผลอุณหภูมิ ให้สร้างตารางใหม่ที่ไล่อุณหภูมิแต่คงคอลัมน์อื่นไว้ แล้ววาดค่าทำนายเป็นเส้น ใช้ช่วงที่มีข้อมูลรองรับและอย่าอ่านเส้นนั้นเป็นเหตุและผลโดยอัตโนมัติ
ข้อมูล พารามิเตอร์ และภาพรวมการคำนวณ
n_knotsdegreealphaข้อมูลที่รับเข้ามา
ตัวอย่างนี้รับ X ตัวเลขและ y ยอดขายแบบต่อเนื่อง ใช้ SplineTransformer + Ridge เพื่อทำ additive spline regression หากมีหมวดหมู่ต้องจัดการการเข้ารหัสแยกให้เหมาะ
X = [อุณหภูมิ, ราคา] · y = ยอดขายพารามิเตอร์ที่ใช้
- n_knots
- จำนวนหลักที่ใช้ต่อเส้นโค้งของแต่ละ feature
- degree
- ระดับของ spline ที่กำหนดความเรียบของช่วงต่อ
- alpha
- ค่าปรับน้ำหนักของ Ridge ในตัวอย่างนี้ ลองร่วมกับ n_knots บน validation
ผลลัพธ์ที่ได้
ได้คำทำนายและเส้นผลกระทบของแต่ละ feature ซึ่งเปิดดูแยกกันเพื่ออธิบายโมเดลได้
ยอดขาย = b + f(อุณหภูมิ) + g(ราคา)คำนวณทีละขั้น
- 01
แปลง feature แต่ละตัวเป็น spline basis หรือชิ้นส่วนเส้นโค้งหลายคอลัมน์
- 02
Ridge หาน้ำหนักของ spline basis พร้อมค่าปรับขนาดน้ำหนัก เพื่อประกอบเป็นฟังก์ชัน f₁, f₂, …
- 03
รวมเป็น ŷ = b + f₁(x₁) + f₂(x₂) + … ตัวอย่างนี้ให้ค่าตัวเลขและไม่รวม interaction อัตโนมัติ
ควรรู้อีกนิด — basis มากเกินไปอาจ overfit ต้องมี smoothing penalty และ interaction ระหว่าง feature ต้องเพิ่มอย่างตั้งใจ
04Make it yours
# ติดตั้ง: pip install numpy scikit-learn matplotlibfrom sklearn.pipeline import make_pipelinefrom sklearn.preprocessing import SplineTransformerfrom sklearn.linear_model import Ridgefrom sklearn.metrics import mean_squared_error import numpy as npfrom sklearn.model_selection import train_test_split rng = np.random.default_rng(42)X = np.linspace(-1, 1, 60).reshape(-1, 1)y = 0.1 + 0.55*X[:, 0] + 0.55*np.sin(3.4*X[:, 0])y += rng.normal(0, 0.14, len(y))X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42) model = make_pipeline(SplineTransformer(n_knots=6), Ridge(alpha=0.35))model.fit(X_train, y_train)pred = model.predict(X_test)print("RMSE:", mean_squared_error(y_test, pred) ** 0.5)โค้ดเปลี่ยนตามค่าที่เลือกใน Playground · แต่ละภาษาใช้ข้อมูลตัวอย่างตามที่ระบุในโค้ด
05Learn by playing
ปรับ จำนวน spline แล้วสังเกตรูปแบบคำทำนายและตัวชี้วัดที่คำนวณใหม่ทันที
Playground เป็น implementation เพื่อการเรียนรู้ที่คำนวณในเบราว์เซอร์ ส่วนโค้ด Python ใช้ scikit-learn จึงอาจให้ค่าต่างกันเล็กน้อยจากรายละเอียดการ optimize และค่าเริ่มต้นของไลบรารี ผลนี้ไม่ใช่ benchmark บนข้อมูลจริง