Deep Learning สำหรับแก้สมการเชิงอนุพันธ์
คู่มือตั้งแต่พื้นฐานของ model ไปจนถึงการเขียน PyTorch เพื่อแก้ ODE และการนำ model ที่ train แล้วกลับมาใช้ใหม่ด้วย Transfer Learning ทุกหัวข้อมีตัวอย่างให้ลองปรับค่าได้
Input → Model → Output
ทุก model ทำงานเหมือนฟังก์ชันตัวหนึ่ง คือรับ Input เข้าไป คำนวณด้วยค่า parameter ที่อยู่ข้างใน แล้วให้ Output ออกมา งานแต่ละแบบต่างกันแค่รูปแบบของ Input, ชนิดของ Model และความหมายของ Output
Shape ของข้อมูลสำคัญมาก ใน PyTorch มิติแรกจะเป็นจำนวนตัวอย่าง (batch) เสมอ เช่น [N, 1] คือมี N จุด แต่ละจุดมี 1 ค่า ถ้า shape ของ input ไม่ตรงกับที่ model รับ โค้ดจะ error ทันที
Objective Function สำหรับแต่ละงาน
Objective function หรือที่นิยมเรียกว่า Loss function คือตัวเลขตัวเดียวที่วัดว่า output ของ model ห่างจากสิ่งที่ต้องการแค่ไหน การ train คือการปรับ parameter \(\theta\) ให้ค่านี้ต่ำที่สุด
งานต่างกันต้องใช้ loss ต่างกัน เลือกแท็บด้านล่างเพื่อดูสูตร ตัวอย่างโค้ด และลองปรับค่าเพื่อดูว่า loss เปลี่ยนอย่างไร
ใช้เมื่อ output เป็นตัวเลขต่อเนื่อง เช่น ราคา อุณหภูมิ หรือค่า u(t) ที่มี data จริงให้เทียบ
เส้นประแนวตั้งในกราฟซ้ายคือความผิดพลาดของแต่ละจุด MSE คือค่าเฉลี่ยของความยาวเส้นเหล่านี้ยกกำลังสอง ลองเลื่อนให้ MSE ต่ำที่สุด
ใช้เมื่อ output เป็นกลุ่ม เช่น จำแนกรูปภาพว่าเป็นตัวเลขอะไร model ให้ความน่าจะเป็นของแต่ละกลุ่ม \(p_c\)
ถ้า model มั่นใจแต่ตอบผิด (p ใกล้ 0) cross-entropy จะสูงขึ้นมาก ในขณะที่ MSE เพิ่มขึ้นแค่นิดเดียว นี่คือเหตุผลที่งานจำแนกกลุ่มนิยมใช้ cross-entropy
ใช้เมื่อ รู้สมการ และอยากได้คำตอบของสมการโดยไม่ต้องมี data เลย ตัวอย่าง \(u'' + u = 0,\; u(0)=1,\; u'(0)=0\) ที่มีคำตอบจริงคือ \(\cos t\)
ลองสมมติว่า model ตอบ \(u(t)=\cos(kt)\) แล้วดูว่า residual ของสมการเป็นเท่าไร ถ้า \(k=1\) จะได้คำตอบที่ถูก residual จะเป็น 0 ทุกจุด
คำตอบรูปแบบ cos(kt) ทำให้ L_IC เป็น 0 เสมอ เพราะ u(0)=1 และ u'(0)=0 ทุกค่า k ส่วน L_PDE จะเป็น 0 ได้เมื่อ k = 1 เท่านั้น model จริงเรียนรู้แบบเดียวกันแต่ปรับ parameter หลายร้อยตัวแทน k ตัวเดียว
ใช้เมื่อ ไม่รู้สมการ แต่มี data ที่วัดได้ตามเวลา เช่น ตำแหน่งของวัตถุ หรือสัญญาณจาก sensor ให้ neural network ทำหน้าที่เป็นสมการ \(\frac{dy}{dt}=f_\theta(y)\) แล้วใช้ ODE solver คำนวณ trajectory ออกมาเทียบกับ data
ใช้เมื่อ ต้องแก้สมการเดิมซ้ำหลายครั้งโดยเปลี่ยนเงื่อนไข เช่น เปลี่ยน initial condition model เรียนรู้การ map จากฟังก์ชัน input \(a\) ไปเป็นคำตอบ \(u\) ทั้งฟังก์ชัน โดยใช้ data จาก numerical solver
สรุป: งานแบบไหนใช้ loss อะไร
| งาน | Output | Loss | PyTorch |
|---|---|---|---|
| Regression | ตัวเลขต่อเนื่อง | MSE | nn.MSELoss() |
| Regression ที่ data มี outlier | ตัวเลขต่อเนื่อง | MAE / Huber | nn.L1Loss(), nn.HuberLoss() |
| Classification 2 กลุ่ม | ความน่าจะเป็น 1 ค่า | Binary cross-entropy | nn.BCEWithLogitsLoss() |
| Classification หลายกลุ่ม | ความน่าจะเป็นของแต่ละกลุ่ม | Cross-entropy | nn.CrossEntropyLoss() |
| PINN (รู้สมการ) | u(t) หรือ u(x, t) | Residual + IC/BC (+ data) | เขียนเองด้วย torch.autograd.grad |
| Neural ODE (มี data ตามเวลา) | Trajectory y(t) | MSE ของ trajectory | odeint + nn.MSELoss() |
| Neural Operator | คำตอบทั้งฟังก์ชัน | Relative L2 | เขียนเอง หรือ neuralop.losses.LpLoss |
การเลือก Model: จาก ML ถึง DL
Model มีตั้งแต่แบบง่ายที่อธิบายได้ชัดเจนและใช้ data น้อย ไปจนถึงแบบซับซ้อนที่เรียนรู้ได้ละเอียดแต่ต้องใช้ data และเวลา train มากกว่า การเลือกให้ดูที่ ชนิดของข้อมูล, ปริมาณข้อมูล และ สิ่งที่รู้อยู่แล้วเกี่ยวกับปัญหา เช่น รู้สมการหรือไม่
กดที่การ์ดเพื่อดูรายละเอียดเพิ่ม
ลำดับของ Model และ Layer
4.1 ลำดับการลอง Model: เริ่มจากง่ายไปยาก
ควรเริ่มจาก model ที่ง่ายที่สุดก่อนเสมอ แล้วค่อยเพิ่มความซับซ้อน เหตุผลคือ
- มีตัวเทียบ ถ้า model ใหญ่ทำได้ไม่ดีกว่า baseline แปลว่ามีอะไรผิด เช่น data มีปัญหา หรือ loss เขียนผิด
- หาจุดผิดง่าย model เล็ก train เร็ว ทดลองได้หลายรอบ
- ประหยัดเวลาและทรัพยากร บางงาน Random Forest ก็ได้ผลดีพอแล้ว ไม่ต้องใช้ GPU
4.2 ลำดับของ Layer ภายใน Model
ใน nn.Sequential ข้อมูลจะไหลผ่าน layer ตามลำดับจากบนลงล่าง ถ้าเรียงผิด model อาจยังรันได้แต่เรียนรู้ได้ไม่ดี หลักที่ใช้บ่อยคือ Linear → Activation → Linear → Activation → … → Linear ลองเรียง layer ของ PINN ด้านล่าง แล้วดูผลการตรวจทางขวา
Perceptron: สูตรของ Neuron และการหา Parameter
Neuron หนึ่งตัวรับ input หลายค่า คูณแต่ละค่าด้วย weight \(w\) รวมกันแล้วบวก bias \(b\) จากนั้นส่งผ่าน activation function \(\sigma\) เพื่อให้ model เรียนรู้ความสัมพันธ์ที่ไม่ใช่เส้นตรงได้
Objective: หาค่า w และ b ที่ดีที่สุด
ตอนเริ่มต้น weight และ bias เป็นค่าสุ่ม การ train คือการหาค่าที่ทำให้ loss ต่ำที่สุด โดยใช้ Gradient Descent คือคำนวณว่าถ้าขยับ parameter แต่ละตัวเล็กน้อย loss จะเปลี่ยนไปทางไหน (gradient) แล้วขยับไปทางตรงข้ามเพื่อให้ loss ลดลง
loss.backward()ลองตั้ง learning rate ให้สูงกว่า 0.8 แล้วกดเริ่ม loss จะไม่ลดลงแต่จะเพิ่มขึ้นเรื่อย ๆ เพราะขยับครั้งละมากเกินจนข้ามจุดต่ำสุดไป
จาก 1 neuron สู่ Deep Learning ถ้าเอา neuron หลายตัวมาวางเป็นชั้นเดียวกันจะได้ nn.Linear และถ้าเอาหลายชั้นมาต่อกันโดยมี activation คั่นจะได้ neural network แบบลึก (Deep) หลักการหา parameter ยังเหมือนเดิมทุกอย่าง แค่จำนวน parameter มากขึ้น
สร้าง Model ใน Deep Learning
1. Data
เตรียม input และ target ให้อยู่ในรูป torch.Tensor พร้อม shape ที่ถูกต้อง
2. Model
สร้าง class ที่สืบทอดจาก nn.Module กำหนด layer ใน __init__ และการคำนวณใน forward
3. Loss
เลือก objective function ให้ตรงกับงาน (ดูหัวข้อ 2)
4. Optimizer
ตัวปรับ parameter เช่น Adam, SGD หรือ LBFGS
5. Training loop
วนทำ forward → คำนวณ loss → backward → update ซ้ำหลายรอบ
6. Evaluate
ทดสอบกับข้อมูลที่ไม่เคยเห็น แล้วบันทึก model ด้วย torch.save
จำนวน parameter ของ nn.Linear(a, b) คือ \((a+1)\times b\) มาจาก weight \(a\times b\) ตัว บวก bias \(b\) ตัว
Training loop มาตรฐาน
โครงนี้ใช้ได้กับเกือบทุกงาน สิ่งที่เปลี่ยนคือ model, loss และวิธีเตรียมข้อมูล
ตัวอย่าง Model สำหรับแก้สมการเชิงอนุพันธ์
- Input
- จุดเวลา t (หรือ x, t)
- Output
- u(t)
- Loss
- Residual ของสมการ + IC/BC
- เหมาะกับ
- แก้สมการครั้งเดียว ไม่มี data หรือมีน้อย
- Input
- สถานะเริ่มต้น y₀ และเวลา
- Output
- Trajectory y(t)
- Loss
- MSE เทียบกับ data
- เหมาะกับ
- ไม่รู้สมการ อยากเรียนรู้ dynamics จาก data
- Input
- ฟังก์ชัน input (branch) + ตำแหน่ง (trunk)
- Output
- ค่าของคำตอบที่ตำแหน่งนั้น
- Loss
- MSE เทียบกับ solver
- เหมาะกับ
- เปลี่ยน parameter หรือเงื่อนไขบ่อย
- Input
- ฟังก์ชันบน grid เช่น u₀(x)
- Output
- คำตอบบน grid เดียวกัน
- Loss
- Relative L2
- เหมาะกับ
- PDE บน grid เช่น การไหลของของไหล
โจทย์ตัวอย่าง: การสั่นแบบมีแรงหน่วง
ใช้สมการของสปริงที่มีแรงเสียดทาน (damped harmonic oscillator) โดย \(\zeta\) คืออัตราส่วนแรงหน่วง และ \(\omega\) คือความถี่ธรรมชาติ
ดูการ train PINN จริงทีละช่วง
ข้อมูลด้านล่างมาจากการ train PINN ด้วย PyTorch จริงที่ \(\zeta=0.2,\ \omega=3\) จำนวน 8,000 step (MLP 3 hidden layer, 32 neuron, Tanh, Adam lr = 0.001) บันทึกผลทุก 100 step ลองเลื่อนหรือกดเล่นเพื่อดูว่า model ค่อย ๆ เรียนรู้คำตอบอย่างไร
สังเกตว่า model เรียนรู้ช่วงต้นของโดเมน (t ใกล้ 0) ได้ก่อน เพราะ initial condition อยู่ตรงนั้น แล้วค่อยขยายความถูกต้องไปทางขวา
โค้ด PyTorch: Model อยู่ตรงไหน
กดปุ่มด้านล่างเพื่อไฮไลต์แต่ละส่วนของโค้ด ปุ่ม เรียกใช้ Model จะแสดงทุกบรรทัดที่ส่งข้อมูลเข้า model (forward pass)
สรุปสั้น ๆ model คือ object model = PINN() ที่เก็บ parameter ทั้งหมด ส่วนที่เหลือของโค้ดคือการเตรียมจุด การคำนวณ loss และการสั่งให้ optimizer ปรับ parameter ของ model นั้น ดู parameter ทั้งหมดได้ด้วย model.parameters() หรือ model.state_dict()
Transfer Learning
Transfer Learning คือการนำ parameter ของ model ที่ train กับโจทย์หนึ่งแล้ว มาเป็นจุดเริ่มต้นของโจทย์ใหม่ที่คล้ายกัน แทนที่จะเริ่มจากค่าสุ่ม ทำให้ train เร็วขึ้นและมักได้ผลดีกว่า โดยเฉพาะเมื่อโจทย์ใหม่มี data น้อยหรือเรียนรู้ยาก
ผลการทดลองจริง: จาก ω = 3 ไป ω = 4
นำ PINN ที่ train กับ \(\omega=3\) (หัวข้อ 7) มาใช้กับโจทย์ใหม่ \(\omega=4\) แล้วเทียบ 3 แบบ โดย train เท่ากันแบบละ 3,000 step
กดที่ชื่อใน legend เพื่อซ่อนหรือแสดงแต่ละเส้น
| วิธี | ทำอย่างไร | Max error ที่ step 1,000 | Max error ที่ step 3,000 |
|---|
วิธีทำ Transfer Learning ใน PyTorch
วิธีที่ง่ายที่สุด โหลด parameter ทั้งหมดแล้ว train ต่อด้วย loss ของโจทย์ใหม่ ใช้เมื่อโจทย์ใหม่คล้ายของเดิมมาก
ล็อก parameter ของ layer ช่วงต้นไว้ ไม่ให้เปลี่ยน แล้ว train เฉพาะ layer ช่วงท้าย ช่วยลดจำนวน parameter ที่ต้อง train และป้องกันไม่ให้ลืมสิ่งที่เรียนมา เหมาะเมื่อ data ของโจทย์ใหม่มีน้อย
ให้ layer ช่วงต้นเปลี่ยนช้า ๆ ด้วย learning rate ต่ำ และ layer ช่วงท้ายเปลี่ยนเร็วกว่า เป็นทางสายกลางระหว่าง fine-tune ทั้งหมดกับ freeze
ใช้เมื่อ output ของโจทย์ใหม่มีจำนวนไม่เท่าเดิม เช่น เดิมทำนาย u(t) ค่าเดียว แต่โจทย์ใหม่เป็นระบบสมการที่ต้องทำนาย u(t) และ v(t) ให้โหลด parameter เฉพาะส่วนที่ขนาดตรงกัน
หา Model และ Data ที่ train แล้วได้จากที่ไหน
บน Kaggle Models และ Kaggle Datasets มีทั้ง model และ data ที่คนอื่นอัปโหลดไว้ ลองค้นด้วยคำว่า PDE, Navier-Stokes, Burgers หรือ PINN ใช้ library kagglehub ดาวน์โหลดได้โดยตรง รูปแบบชื่อของ model คือ owner/model/framework/variation
ก่อนโหลด weight ต้องสร้าง model ให้มีโครงสร้างเหมือนต้นฉบับทุกอย่าง (จำนวน layer, จำนวน neuron, activation) ให้อ่านคำอธิบายหรือ notebook ของเจ้าของ model ก่อน และโหลดไฟล์จากแหล่งที่เชื่อถือได้เท่านั้น เพราะไฟล์ .pt อาจมีโค้ดแฝงได้ ควรใช้ weights_only=True เสมอ
บน Hugging Face Hub มี model ด้าน scientific ML อยู่หลายตัว ดาวน์โหลดไฟล์ weight ได้ด้วย huggingface_hub
Poseidon เป็น foundation model สำหรับ PDE จาก ETH Zürich (NeurIPS 2024) ที่ pretrain กับข้อมูล PDE จำนวนมาก แล้วนำไป fine-tune กับโจทย์ใหม่ที่มี data น้อยได้ มี 3 ขนาดบน Hugging Face คือ Poseidon-T, Poseidon-B และ Poseidon-L
PDEBench เป็นชุด benchmark ที่มีทั้ง dataset ของ PDE หลายแบบ และ model ที่ train แล้ว (FNO, U-Net, PINN) โหลดได้จาก DaRUS ของ University of Stuttgart ส่วน FNO สร้างได้ง่ายด้วย library neuraloperator
เทคนิค Transfer Learning อื่น ๆ ที่ควรรู้
Curriculum learning
เริ่มจากโจทย์ง่ายแล้วค่อยยากขึ้น เช่น ω = 1 → 2 → 4 → 8 แต่ละรอบใช้ model จากรอบก่อนเป็นจุดเริ่ม ช่วยได้มากกับโจทย์ที่สั่นเร็วหรือโดเมนยาว ซึ่ง PINN มักเรียนรู้ยาก
Pretrain ด้วย data แล้ว fine-tune ด้วยสมการ
train ด้วย data จาก numerical solver ก่อน (supervised) แล้วค่อย fine-tune ด้วย physics loss ทำให้เริ่มจากคำตอบที่ใกล้เคียงแล้ว
Feature extraction
ใช้ model ที่ pretrain แล้วเป็นตัวแปลง input เป็น feature โดยไม่ train ต่อเลย แล้ว train แค่ส่วนหัวเล็ก ๆ ที่ต่อท้าย เหมาะเมื่อ data น้อยมาก
Foundation model
model ขนาดใหญ่ที่ pretrain กับ PDE หลายชนิด เช่น Poseidon นำมา fine-tune กับโจทย์เฉพาะได้โดยใช้ data น้อยกว่าการเริ่มจากศูนย์มาก
ข้อควรระวัง การ normalize input ต้องเหมือนตอน pretrain เช่น ถ้าตอนแรกหาร t ด้วย 5 ตอน fine-tune ก็ต้องหารด้วย 5 เหมือนเดิม และถ้าโจทย์ใหม่ต่างจากเดิมมาก (เช่น สมการคนละแบบ) transfer learning อาจไม่ช่วย หรือแย่กว่าเริ่มจากศูนย์ ควรเทียบผลกับการ train จากศูนย์เสมอ
แหล่งอ้างอิง
- Raissi, Perdikaris, Karniadakis (2019). Physics-informed neural networks. J. Comput. Phys. — arXiv:1711.10561
- Chen et al. (2018). Neural Ordinary Differential Equations. NeurIPS — arXiv:1806.07366
- Lu et al. (2021). Learning nonlinear operators via DeepONet. Nature Machine Intelligence — arXiv:1910.03193
- Li et al. (2021). Fourier Neural Operator for Parametric PDEs. ICLR — arXiv:2010.08895
- Herde et al. (2024). Poseidon: Efficient Foundation Models for PDEs. NeurIPS — arXiv:2405.19101
- Takamoto et al. (2022). PDEBench: An Extensive Benchmark for Scientific Machine Learning. NeurIPS — GitHub
- Library: torchdiffeq · DeepXDE · neuraloperator · kagglehub