พื้นฐานการเรียนรู้ของเครื่อง
UAT 306 ปัญญาประดิษฐ์สำหรับระบบอากาศยานไร้คนขับ
บทเรียน
เมื่อเรียนจบโมดูลนี้ ผู้เรียนจะสามารถ
- เขียนการถดถอยโลจิสติกและฝึกด้วย gradient descent จากศูนย์
- อธิบายฟังก์ชันสูญเสียแบบ cross-entropy และบทบาทของอัตราการเรียนรู้
- ใช้ k-fold cross-validation เลือกความซับซ้อนของแบบจำลอง
- แยก underfitting กับ overfitting จากความคลาดเคลื่อนของชุดฝึกและชุดตรวจสอบ
ทำไมต้องรู้
ไลบรารีอย่าง scikit-learn และ PyTorch ทำให้ฝึกโมเดลได้ในไม่กี่บรรทัด แต่ถ้าไม่เข้าใจว่าข้างในทำอะไร จะแก้ปัญหาไม่ได้เมื่อโมเดลไม่เรียนรู้ หรือเรียนรู้เกินจนใช้งานจริงไม่ได้ หน่วยความรู้เรื่องพื้นฐานการเรียนรู้ของเครื่องของคลังความรู้โดรนครอบคลุม supervised และ unsupervised learning การแบ่งข้อมูล overfitting และการประเมินโมเดล ตำราของ Géron และ Prince อธิบายเรื่องนี้ละเอียด โมดูลนี้เขียนทุกอย่างเองด้วย NumPy เพื่อเห็นกลไกจริง
การถดถอยโลจิสติก
การถดถอยโลจิสติก ทำนายความน่าจะเป็นของคลาสด้วยฟังก์ชันซิกมอยด์ของผลรวมถ่วงน้ำหนักของลักษณะเด่น (feature) การฝึกคือหาน้ำหนัก ที่ทำให้ cross-entropy ต่ำสุด ซึ่งลงโทษการทำนายที่มั่นใจแต่ผิดอย่างหนัก gradient descent ขยับน้ำหนักไปทางตรงข้ามกับเกรเดียนต์ทีละก้าว ขนาดก้าวคือ อัตราการเรียนรู้ ถ้าเล็กเกินไปจะเรียนช้า ถ้าใหญ่เกินไปจะกระโดดข้ามจุดต่ำสุด
ตัวอย่างที่ 1 แยกรอยร้าวจากรอยเปื้อนด้วยสองลักษณะเด่น
ลักษณะเด่นสมมติสองตัวจากภาพย่อย: ความยาวต่อความกว้างของรอย และความเข้มของขอบ (ข้อมูลจำลอง 200 ตัวอย่าง)
import numpy as np
rng = np.random.default_rng(5)
n = 200
elong = np.r_[rng.normal(2, 0.8, n // 2), rng.normal(4, 0.8, n // 2)] # ความยาวต่อความกว้าง
edge = np.r_[rng.normal(1, 0.6, n // 2), rng.normal(2.5, 0.6, n // 2)] # ความเข้มของขอบ
y = np.r_[np.zeros(n // 2), np.ones(n // 2)] # 1 = รอยร้าว
X = np.c_[np.ones(n), (elong - elong.mean()) / elong.std(), (edge - edge.mean()) / edge.std()]
w, lr = np.zeros(3), 0.5
for it in range(1, 501):
p = 1 / (1 + np.exp(-X @ w))
w -= lr * X.T @ (p - y) / n
if it in (1, 10, 100, 500):
loss = -np.mean(y * np.log(p + 1e-12) + (1 - y) * np.log(1 - p + 1e-12))
print(f"iteration {it:>3}: loss {loss:.3f}, accuracy {((p > 0.5) == y).mean():.3f}")
print("weights (bias, elongation, edge):", np.round(w, 2))
iteration 1: loss 0.693, accuracy 0.500
iteration 10: loss 0.241, accuracy 0.980
iteration 100: loss 0.102, accuracy 0.980
iteration 500: loss 0.083, accuracy 0.980
weights (bias, elongation, edge): [0.09 4.25 3.11]
ความสูญเสียลดเร็วในช่วงแรกแล้วค่อย ๆ ช้าลง ความแม่นยำนิ่งหลังไม่กี่สิบรอบ น้ำหนักของทั้งสองลักษณะเด่นเป็นบวก แปลว่ารอยที่ยาวแคบและขอบคมมีแนวโน้มเป็นรอยร้าว การปรับมาตรฐานลักษณะเด่น (ลบค่าเฉลี่ยหารส่วนเบี่ยงเบนมาตรฐาน) ก่อนฝึกทำให้ gradient descent ลู่เข้าเร็วขึ้น
Overfitting และ cross-validation
แบบจำลองที่ซับซ้อนเกินไปจำสัญญาณรบกวนในชุดฝึกได้ ความคลาดเคลื่อนบนชุดฝึกจึงต่ำ แต่กับข้อมูลใหม่กลับสูง เรียกว่า overfitting ส่วนแบบจำลองที่ง่ายเกินไปผิดทั้งสองชุด เรียกว่า underfitting k-fold cross-validation แบ่งข้อมูลเป็น ส่วน ฝึก ครั้งโดยเว้นทีละส่วนไว้ตรวจสอบ แล้วเฉลี่ยผล ใช้เลือกความซับซ้อนได้โดยไม่แตะชุดทดสอบ (เอกสาร scikit-learn เรื่อง cross-validation)
ตัวอย่างที่ 2 เลือกดีกรีของพหุนามด้วย 5-fold
ความสัมพันธ์สมมติระหว่างความลึกของรอยร้าวกับระยะจากขอบคาน 30 จุด (ข้อมูลจำลอง)
import numpy as np
rng = np.random.default_rng(2)
x = np.linspace(0, 1, 30)
y = 5 * x + 2 * np.sin(5 * x) + rng.normal(0, 0.6, 30)
folds = np.array_split(rng.permutation(30), 5)
for degree in (1, 3, 9):
tr_err, va_err = [], []
for f in folds:
tr = np.setdiff1d(np.arange(30), f)
c = np.polyfit(x[tr], y[tr], degree)
tr_err.append(np.sqrt(np.mean((np.polyval(c, x[tr]) - y[tr]) ** 2)))
va_err.append(np.sqrt(np.mean((np.polyval(c, x[f]) - y[f]) ** 2)))
print(f"degree {degree}: train RMSE {np.mean(tr_err):.2f}, validation RMSE {np.mean(va_err):.2f}")
degree 1: train RMSE 1.09, validation RMSE 1.22
degree 3: train RMSE 0.55, validation RMSE 0.61
degree 9: train RMSE 0.46, validation RMSE 0.74
ดีกรี 1 ผิดทั้งชุดฝึกและชุดตรวจสอบ คือ underfitting ดีกรี 9 ผิดน้อยที่สุดบนชุดฝึกแต่ผิดมากขึ้นบนชุดตรวจสอบ คือ overfitting ดีกรีกลาง ๆ ให้ความคลาดเคลื่อนบนชุดตรวจสอบต่ำสุด หลักเดียวกันใช้กับโครงข่ายประสาทเทียม: ดูความสูญเสียของชุดตรวจสอบ ไม่ใช่ของชุดฝึก
ปฏิบัติการประจำโมดูล
ปฏิบัติการ: ฝึกโมเดลด้วยมือแล้วเทียบกับไลบรารี
- รันตัวอย่างที่ 1 เปลี่ยนอัตราการเรียนรู้เป็น 0.01, 0.5 และ 5 สังเกตความสูญเสีย
- ฝึกข้อมูลเดียวกันด้วย
LogisticRegressionของ scikit-learn เทียบน้ำหนักและความแม่นยำ - รันตัวอย่างที่ 2 ด้วยดีกรี 1 ถึง 12 วาดกราฟความคลาดเคลื่อนทั้งสองชุด
- ลองลดข้อมูลเหลือ 15 จุด แล้วดูว่าดีกรีที่ดีที่สุดเปลี่ยนหรือไม่
- เขียนสรุปว่าจะรู้ได้อย่างไรว่าโมเดลของตน underfit หรือ overfit
ข้อผิดพลาดที่พบบ่อย
ระวัง
- ไม่ปรับมาตรฐานลักษณะเด่น ทำให้ลู่เข้าช้าหรือไม่ลู่เข้า
- ตั้งอัตราการเรียนรู้สูงเกินไป จนความสูญเสียแกว่งหรือพุ่ง
- ดูแต่ความคลาดเคลื่อนของชุดฝึก
- ใช้ชุดทดสอบเลือกความซับซ้อน
- เพิ่มความซับซ้อนเมื่อข้อมูลน้อย
สรุป
- การถดถอยโลจิสติกทำนายความน่าจะเป็นด้วยซิกมอยด์ และฝึกโดยลด cross-entropy ด้วย gradient descent
- อัตราการเรียนรู้กำหนดขนาดก้าว ปรับมาตรฐานลักษณะเด่นช่วยให้ลู่เข้าเร็ว
- underfitting ผิดทั้งสองชุด overfitting ผิดน้อยบนชุดฝึกแต่ผิดมากบนชุดตรวจสอบ
- k-fold cross-validation ใช้เลือกความซับซ้อนโดยไม่แตะชุดทดสอบ
แบบฝึกตรวจความเข้าใจ
- ความน่าจะเป็นที่ทำนายเท่าใด
- ถ้าทำนาย แต่คำตอบคือ 0 ความสูญเสียของตัวอย่างนั้นเท่าใด
- ชุดฝึกผิดน้อยมาก ชุดตรวจสอบผิดมาก เป็นปัญหาแบบใด
- 5-fold cross-validation ฝึกโมเดลกี่ครั้ง
- ทำไมจึงไม่ควรใช้ชุดทดสอบเลือกดีกรี
เฉลย
- 0.5
- overfitting
- 5 ครั้ง
- ผลบนชุดทดสอบจะดีเกินจริง เพราะใช้ข้อมูลนั้นตัดสินใจไปแล้ว
สรุปสูตรสำคัญ
| การถดถอยโลจิสติก | |
| Cross-entropy และเกรเดียนต์ | |
| ปรับน้ำหนัก |
แหล่งอ้างอิงหลัก
อ่านเพิ่มเติม
ศึกษาหน่วยความรู้ที่กำหนดล่วงหน้า ดูสื่อประกอบ และทำ quiz ประจำโมดูล
ในชั้นเรียน / ภาคสนาม
ปฏิบัติการในห้องแล็บหรือภาคสนามตามใบงาน พร้อม checklist ความปลอดภัย
หลักฐานการเรียนรู้: ใบงานที่ผ่านการตรวจและผล quiz