物理資訊神經網路 PINN
前面的神經網路模型完全靠資料學習,對物理一無所知:資料沒涵蓋的地方就只能亂猜。但工程上我們常常知道系統大致遵守的方程式,只是不知道某些參數,或量測點很少。物理資訊神經網路(physics-informed neural network,PINN;Raissi、Perdikaris 與 Karniadakis,2019)把已知的微分方程式放進訓練的損失函數,讓網路同時符合資料與物理。
做法 Formulation
用網路 \(h_\theta(t)\) 直接表示未知的解。若物理定律是 \(\mathcal N[h; c] = 0\)(例如一條微分方程式,\(c\) 是未知參數),損失函數有兩部分:
$$L(\theta, c) = \underbrace{\frac{1}{N_d}\sum_{i=1}^{N_d} \big(h_\theta(t_i) - h_i\big)^2}_{\text{資料誤差}} + \lambda\, \underbrace{\frac{1}{N_c}\sum_{j=1}^{N_c} \mathcal N[h_\theta; c](\tau_j)^2}_{\text{物理誤差}}$$- 資料誤差只在量測時刻 \(t_i\) 計算,量測可以很少、有雜訊。
- 物理誤差在一組自己選的配置點(collocation points)\(\tau_j\) 上計算,不需要量測,可以撒滿整個要預測的範圍。方程式裡的微分 \(dh_\theta/dt\) 用自動微分對網路的輸入求出,這是 PINN 能實作的關鍵。
- 未知的物理參數 \(c\) 當成和網路權值一起最佳化的變數,順便就估出來了(反問題,inverse problem)。
範例:水槽排水 Example
同一個水槽,閥關閉、從 2 m 開始排水,水位遵守
$$\frac{dh}{dt} = -c\sqrt{2gh}$$出水係數 \(c\) 當作未知(真值 0.05)。只在前 4 秒量到 10 個水位,量測雜訊的標準差 2 cm。目標是預測 0~10 s 的水位,並估計 \(c\)。比較兩個相同架構(1→32→32→1,tanh)、相同訓練步數的網路:一般網路只用資料誤差,PINN 再加上 0~10 s 間 101 個配置點的物理誤差:
# 物理資訊神經網路(PINN):水槽關閥排水,只量到前 4 秒的 10 個(有雜訊的)水位,
# 要預測到 10 秒,並同時估計未知的出水係數 c。已知的物理:dh/dt = −c√(2gh)
import json
import math
from pathlib import Path
import numpy as np
import torch
from torch import nn
torch.set_default_dtype(torch.float64)
G, C_TRUE, H0 = 9.8, 0.05, 2.0
truth = lambda t: np.maximum(math.sqrt(H0) - C_TRUE * math.sqrt(G / 2) * t, 0.0) ** 2 # 解析解(只用來產生資料與評分)
rng = np.random.default_rng(1)
t_d = np.linspace(0, 4, 10)
h_d = truth(t_d) + rng.normal(0, 0.02, len(t_d)) # 量測:標準差 2 cm 的雜訊
td, hd = torch.tensor(t_d)[:, None], torch.tensor(h_d)[:, None]
tc = torch.linspace(0, 10, 101)[:, None].requires_grad_(True) # 檢查物理的「配置點」:0~10 s
t_all = np.linspace(0, 10, 101)
def make_net(seed):
torch.manual_seed(seed)
return nn.Sequential(nn.Linear(1, 32), nn.Tanh(), nn.Linear(32, 32), nn.Tanh(), nn.Linear(32, 1))
def train(physics, iters=6000, lr=2e-3, snap=200):
net = make_net(0)
log_c = torch.tensor(math.log(0.1), requires_grad=True) # 未知參數 c 也當成要學的量(初猜 0.1)
opt = torch.optim.Adam(list(net.parameters()) + ([log_c] if physics else []), lr=lr)
h = lambda t: net(t / 10) # 時間先縮放到 0~1
snaps = []
for it in range(iters + 1):
loss = torch.mean((h(td) - hd) ** 2) # 資料誤差
if physics:
hc = h(tc)
dh = torch.autograd.grad(hc.sum(), tc, create_graph=True)[0] # 自動微分求 dh/dt
res = dh + log_c.exp() * torch.sqrt(2 * G * hc.clamp(min=0) + 1e-12)
loss = loss + torch.mean(res ** 2) # 物理誤差:方程式在配置點上要成立
if it % snap == 0:
with torch.no_grad():
snaps.append({"it": it, "h": h(torch.tensor(t_all)[:, None])[:, 0].tolist(), "c": float(log_c.exp())})
if it < iters:
opt.zero_grad(); loss.backward(); opt.step()
return snaps
nn_s = train(False)
pinn_s = train(True)
far = t_all > 4
rm = lambda h, sel: float(np.sqrt(np.mean((h[sel] - truth(t_all[sel])) ** 2))) * 100
for name, s in (("一般神經網路(只用資料)", nn_s), ("PINN(資料+物理)", pinn_s)):
hh = np.array(s[-1]["h"])
print(f"{name}:0~4 s 的 RMSE {rm(hh, ~far):.2f} cm,4~10 s(沒有資料)的 RMSE {rm(hh, far):.2f} cm")
print(f"PINN 估計的出水係數 c = {pinn_s[-1]['c']:.4f}(真值 {C_TRUE})")
# 對照:這個例子剛好有解析解,√h 是時間的直線,直接對 √h 做直線擬合也能估 c
a, b = np.polyfit(t_d, np.sqrt(np.maximum(h_d, 0)), 1)
print(f"對照(解析解+直線擬合):c = {-a / math.sqrt(G / 2):.4f}")
Path(__file__).with_name("pinn_tank.json").write_text(json.dumps({
"t": t_all.tolist(), "truth": truth(t_all).tolist(), "td": t_d.tolist(), "hd": h_d.tolist(),
"nn": nn_s, "pinn": pinn_s}), encoding="utf-8")⬇ 下載 pinn_tank.py(Python 3+PyTorch)
實際執行的輸出:
一般神經網路(只用資料):0~4 s 的 RMSE 0.61 cm,4~10 s(沒有資料)的 RMSE 22.50 cm
PINN(資料+物理):0~4 s 的 RMSE 0.76 cm,4~10 s(沒有資料)的 RMSE 0.60 cm
PINN 估計的出水係數 c = 0.0501(真值 0.05)
對照(解析解+直線擬合):c = 0.0499
- 在有資料的前 4 秒,兩者都通過量測點。一般網路的誤差略小(0.61 cm 對 0.76 cm),因為它可以順著雜訊彎曲;PINN 被方程式約束,不會去配合雜訊。
- 在沒有資料的 4~10 s,一般網路只是把曲線往外延伸,誤差 22.5 cm,最後還出現負的水位。PINN 依方程式外插,誤差只有 0.6 cm。
- PINN 估計的 \(c = 0.0501\),誤差 0.2%。
下方動畫播放兩個網路的訓練過程(範例每 200 步存下的預測),PINN 估計的 \(c\) 從初猜 0.1 逐步下降,沒有資料那一段的曲線跟著修正:第 600 步時 \(c = 0.064\),4~10 s 的誤差還有 4.5 cm;約 2400 步後 \(c\) 收斂到 0.05 附近,誤差降到 0.3 cm,之後在 0.15~0.6 cm 之間小幅擺動(Adam 的步長固定,接近最小值時會來回跳動,範例最後一步剛好落在 0.6 cm):
什麼時候該用 PINN When to Use
這個例子其實有解析解:\(\sqrt h = \sqrt{h_0} - c\sqrt{g/2}\,t\),也就是 \(\sqrt h\) 是時間的直線。直接對量測值的平方根做直線擬合,就得到 \(c = 0.0499\),又快又準。對於模型已知、低維、方程式好解的問題,傳統的參數估計(最小平方、卡爾曼濾波)通常比 PINN 簡單可靠;用 PINN 求解已知參數的方程式,也常比傳統數值方法(如 Runge–Kutta、有限元素)慢而且不準。
PINN 的價值在傳統方法不好處理的情況:
- 偏微分方程式與不規則的資料:例如由少數感測器重建整個溫度場或流場,同時估計熱傳或黏滯係數。
- 不完整的物理:只知道部分方程式,其餘用資料補足。
- 需要可微分的快速模型:訓練好的網路計算很快、可以對輸入微分,適合當作數位分身(digital twin)或 MPC 的預測模型。
PINN 也有已知的困難:資料誤差與物理誤差的權重 \(\lambda\) 不好選;解變化很快(剛性)或時間範圍很長時常常訓練不起來;收斂慢,常需搭配 L-BFGS 等最佳化器。
相關的做法:灰箱模型 Gray-Box Models
另一種結合物理與網路的方式,是保留已知的方程式,只用網路取代未知的部分。例如水槽的進水量確定是 \(0.5u\),但出水口的特性不清楚,就寫成
$$\frac{dh}{dt} = 0.5u - \text{NN}(h)$$再用數值積分模擬並對資料訓練(神經常微分方程式 neural ODE、universal differential equations 都屬於這類)。和純資料的 NARX 相比,這種模型自動滿足「質量守恆」等物理結構,外插也較可靠;和 PINN 相比,它直接得到一個可以模擬、可以拿去設計控制器的動態模型。