ml/ library Learn by doing
ALGORITHM 25 · PYTHON / JAVASCRIPT

Regression Tree

ถามทีละข้อ แล้วตอบด้วยตัวเลขเฉลี่ยของกลุ่ม

LEARNING LEVEL2
ต่อยอดโดยตรงควรรู้มาก่อน · Decision Tree และ Linear Regression
ลองใน Playground

01Overall Concept

OVERALL CONCEPT

ไอเดียหลัก — Regression Tree ใช้คำถามแบบใช่หรือไม่แบ่งตัวอย่างเป็นกลุ่มย่อย แล้วตอบด้วยค่าเฉลี่ยของเป้าหมายในกลุ่มนั้น เช่น บ้านพื้นที่น้อยกับบ้านพื้นที่มากอาจมีราคาเฉลี่ยต่างกัน วิธีนี้จับความสัมพันธ์ที่ไม่เป็นเส้นตรงโดยไม่ต้องกำหนดสมการโค้งล่วงหน้า

ลองนึกภาพว่า — เหมือนคู่มือประเมินราคาที่ถามว่าบ้านใหญ่กว่า 80 ตารางเมตรไหม อยู่ใกล้รถไฟฟ้าไหม เมื่อเดินตามคำถามไปถึงหน้าสุดท้าย จะพบราคาเฉลี่ยของบ้านกลุ่มนั้น

ตัวอย่างการนำไปใช้ — ประมาณราคาบ้าน ยอดขายรายสาขา เวลาส่งของ หรือใช้เป็นต้นไม้ฐานของ Random Forest และ Gradient Boosting สำหรับงาน regression

02อธิบายภาษาคนเข้าใจง่ายๆ

เล่าแบบไม่ใช้ศัพท์ยาก

Decision Tree ที่ทายคลาสจะจบด้วยคำตอบอย่างแมวหรือหมา แต่ Regression Tree จบด้วยตัวเลข เช่น ราคาบ้าน 3.2 ล้านบาท โครงสร้างคำถามยังเหมือนเดิม

สมมติมีบ้านเล็กที่ราคา 2 และ 4 ล้านบาท กับบ้านใหญ่ที่ราคา 8 และ 10 ล้านบาท ถ้ารวมทั้งหมดจะทาย 6 แต่เมื่อแบ่งตามขนาด กลุ่มบ้านเล็กทาย 3 และบ้านใหญ่ทาย 9 ซึ่งใกล้ราคาจริงมากขึ้น

คอมพิวเตอร์ลองจุดแบ่งหลายตำแหน่ง แล้วเลือกตำแหน่งที่ทำให้ราคาภายในแต่ละกลุ่มใกล้กันที่สุด จากนั้นถามต่อได้อีกจนถึงความลึกหรือจำนวนตัวอย่างที่กำหนด

บ้านใหม่จะเดินตามคำถามทีละข้อจนถึงใบ แล้วรับค่าเฉลี่ยในใบนั้นเป็นราคา บ้านสองหลังในใบเดียวกันจึงได้คำตอบเท่ากัน แม้ขนาดจะต่างกันเล็กน้อย

เมื่อวาดกราฟด้วย feature เดียว เราจะเห็นเส้นเป็นขั้นบันได ไม่ใช่เส้นโค้งเรียบ ต้นไม้ลึกทำให้ขั้นถี่ขึ้น แต่อาจกำลังจำความบังเอิญของข้อมูลเก่า

ภาพจำ: แบ่งตารางออกเป็นช่องเล็ก ๆ แล้วเขียนค่าเฉลี่ยไว้ในแต่ละช่อง

03How it works

ค่อยๆ ทำไปด้วยกัน ตั้งแต่ข้อมูลแถวแรกจนถึงการตรวจคำตอบ

  1. เตรียมตัวเลขคำตอบ

    ให้ X เป็น feature เช่น พื้นที่บ้าน และ y เป็นราคาต่อหลัง แบ่ง train/test ก่อนฝึก ต้นไม้ไม่ต้องปรับสเกลเพื่อเลือก threshold แต่ข้อมูลหมวดหมู่ยังต้องแปลงให้ estimator ใช้ได้

  2. ลองแบ่งแล้ววัดความกระจาย

    สำหรับทุกจุดตัดที่เป็นไปได้ แยกแถวซ้ายกับขวา คำนวณค่าเฉลี่ย y ของแต่ละฝั่ง แล้วรวม (y − ค่าเฉลี่ยของฝั่งนั้น)² เลือกจุดตัดที่ทำให้ผลรวมนี้ต่ำที่สุด

  3. เห็นตัวเลขจากตัวอย่างเล็ก

    y=[2,4,8,10] มีค่าเฉลี่ย 6 และ SSE=40 ถ้าแบ่งเป็น [2,4] กับ [8,10] ค่าเฉลี่ยกลายเป็น 3 กับ 9 และ SSE รวมเหลือ 4 การแบ่งนี้ลด error ลง 36

  4. ฝึกต้นไม้ด้วยเงื่อนไขหยุด

    ใช้ DecisionTreeRegressor(max_depth=3, min_samples_leaf=2, random_state=42) แล้ว fit(X_train, y_train) การทำนายเดินตาม threshold จนถึงใบและคืนค่าเฉลี่ย ไม่ได้ต่อเส้นระหว่างใบ

  5. เลือกความละเอียดที่ใช้กับข้อมูลใหม่ได้

    ดู RMSE บน validation แล้วปรับ max_depth หรือ min_samples_leaf ใน Playground ค่า Test RMSE ใช้จุดที่กันไว้และไม่ได้ร่วมเลือกจุดแบ่ง ลองเทียบต้นไม้ตื้นกับลึก และระวังการทำนาย x ที่อยู่นอกช่วงฝึกซึ่งจะได้ค่าจากใบริมสุด

เปิดตัวอย่างโค้ดเพื่อลองทำตาม →ตัวอย่างตัวเลขในเรื่องใช้ช่วยอธิบาย ส่วนโค้ดและ Playground ใช้ชุดข้อมูลสาธิตของแต่ละหน้าอ่านเอกสารอ้างอิงของเทคนิคนี้ ↗

ข้อมูล พารามิเตอร์ และภาพรวมการคำนวณ

DATA INX · featuresy · targety=[2,4,8,10] → ค่าเฉลี่ย 6 · SSE=40
CALCULATERegression Tree
max_depthmin_samples_leafcriterion
RESULTnumeric prediction[2,4] → 3 · [8,10] → 9 · SSE รวม=4
เส้นทางของข้อมูล: จากค่าที่รับเข้า ผ่านการคำนวณด้วยพารามิเตอร์ ไปเป็นผลลัพธ์
INPUT

ข้อมูลที่รับเข้ามา

X เป็นตาราง feature และ y เป็นค่าต่อเนื่อง ใช้ squared_error เป็นเกณฑ์แบ่ง

y=[2,4,8,10] → ค่าเฉลี่ย 6 · SSE=40
PARAMETERS

พารามิเตอร์ที่ใช้

max_depth
จำกัดจำนวนชั้นของคำถาม
min_samples_leaf
จำนวนแถวอย่างน้อยในแต่ละใบ
criterion
ตัวอย่างนี้ใช้ squared_error และค่าเฉลี่ยในใบ
OUTPUT

ผลลัพธ์ที่ได้

โครงสร้างต้นไม้และค่าตัวเลขจากใบที่ข้อมูลใหม่ตกอยู่

[2,4] → 3 · [8,10] → 9 · SSE รวม=4
CALCULATION

คำนวณทีละขั้น

  1. 01

    ลอง threshold ของ feature เพื่อแยกซ้าย/ขวา

  2. 02

    เลือก split ที่ลด Σ(y−ค่าเฉลี่ยใบ)² มากที่สุด

  3. 03

    ทำซ้ำแล้วเก็บค่าเฉลี่ยไว้ในใบสุดท้าย

ควรรู้อีกนิด — การลด error ในชุดฝึกไม่รับประกันว่า test error จะลดด้วย ถ้าต้องการเส้นต่อเนื่องหรือการ extrapolate ควรเปรียบเทียบกับโมเดลอื่นและตรวจช่วงข้อมูลที่ใช้งาน

04Make it yours

Python · scikit-learn
# ติดตั้ง: pip install numpy scikit-learn# ดาวน์โหลด sample.csv จากหน้านี้ แล้ววางไว้ข้างสคริปต์import numpy as np data = np.genfromtxt("regression-tree-sample.csv", delimiter=",", skip_header=1)X = data[:, :2] from sklearn.tree import DecisionTreeRegressorfrom sklearn.metrics import mean_squared_error X = data[:, :1]y = data[:, 1]# กันทุกแถวที่ 5 ไว้ทดสอบ ตามข้อมูลสาธิตใน Playground# งานจริงควรเลือก split ให้เหมาะกับเวลา/บุคคล/กลุ่มของข้อมูลis_test = np.arange(len(X)) % 5 == 0X_train, X_test = X[~is_test], X[is_test]y_train, y_test = y[~is_test], y[is_test]model = DecisionTreeRegressor(max_depth=3, min_samples_leaf=2, random_state=42)model.fit(X_train, y_train)pred = model.predict(X_test)print("Test RMSE:", mean_squared_error(y_test, pred) ** 0.5) 

โค้ดเปลี่ยนตามค่าที่เลือกใน Playground · แต่ละภาษาใช้ข้อมูลตัวอย่างตามที่ระบุในโค้ด

05Learn by playing

ปรับ ความลึกสูงสุด แล้วสังเกตรูปแบบคำทำนายและตัวชี้วัดที่คำนวณใหม่ทันที

Sample dataset / Curved signal● คำนวณใหม่เมื่อปรับค่า
เพิ่มความลึกเพื่อแบ่งช่วงละเอียดขึ้น โดยแต่ละใบมีอย่างน้อย 2 จุดฝึก
ข้อมูลฝึกและผลการทำนายFeature xค่าจริง • / ค่าทำนาย —
Test RMSE0.161
จำนวนใบ7
พารามิเตอร์ที่ลอง3

Playground เป็น implementation เพื่อการเรียนรู้ที่คำนวณในเบราว์เซอร์ ส่วนโค้ด Python ใช้ scikit-learn จึงอาจให้ค่าต่างกันเล็กน้อยจากรายละเอียดการ optimize และค่าเริ่มต้นของไลบรารี ผลนี้ไม่ใช่ benchmark บนข้อมูลจริง