1. 损失曲线不下降的初步定位
先分清“优化不动”还是“权重失衡”
PINN 的总损失通常是 L = L_data + λ·L_pde(有时再加边界项)。当曲线长期平坦时,最常见的两类原因截然不同:一类是优化本身失效(学习率过大导致震荡、过小导致停滞、梯度爆炸/消失),另一类是两项损失量级相差几个数量级,导致梯度被其中一项主导,另一项几乎不参与更新。定位的关键不是盯着总损失,而是把两项损失、两项梯度范数和学习率放在同一时间轴上对比。
用一段可运行的诊断代码把三项观测打出来
下面是一个最小可复现的诊断脚本:一个两层 tanh 网络拟合 u(x)=sin(πx),PDE 残差用 u''+π²u(真解使其为 0)。脚本在每个记录点分别计算数据损失、物理残差损失、两者对参数的梯度范数,并打印学习率与参数更新量,用于判断属于哪一类问题。
import torch, math
torch.manual_seed(0)
dtype = torch.float64
# 网络
net = torch.nn.Sequential(
torch.nn.Linear(1, 32), torch.nn.Tanh(),
torch.nn.Linear(32, 32), torch.nn.Tanh(),
torch.nn.Linear(32, 1)
).to(dtype)
# 数据点(监督项)
x_data = torch.linspace(0, 1, 20, dtype=dtype).unsqueeze(1)
y_data = torch.sin(math.pi * x_data)
# 配点(物理残差项)
x_pde = torch.linspace(0, 1, 200, dtype=dtype).unsqueeze(1).requires_grad_(True)
lam = 1.0 # 物理项权重
lr = 1e-3
opt = torch.optim.Adam(net.parameters(), lr=lr)
def pde_residual(x):
u = net(x)
du = torch.autograd.grad(u, x, torch.ones_like(u), create_graph=True)[0]
d2u = torch.autograd.grad(du, x, torch.ones_like(du), create_graph=True)[0]
return d2u + (math.pi ** 2) * u
def grad_norm(loss):
gs = torch.autograd.grad(loss, [p for p in net.parameters() if p.requires_grad],
retain_graph=True, allow_unused=True)
return torch.sqrt(sum((g.pow(2).sum() for g in gs if g is not None)) + 1e-30)
for step in range(1, 2001):
opt.zero_grad()
L_data = ((net(x_data) - y_data) ** 2).mean()
r = pde_residual(x_pde)
L_pde = (r ** 2).mean()
L = L_data + lam * L_pde
g_data = grad_norm(L_data)
g_pde = grad_norm(lam * L_pde)
L.backward()
opt.step()
if step % 200 == 0:
ratio = (L_data / (lam * L_pde + 1e-30)).item()
print(f"step {step:5d} | L_data={L_data.item():.3e} "
f"| lam*L_pde={(lam*L_pde).item():.3e} | 比值={ratio:.2e} "
f"| g_data={g_data.item():.3e} | g_pde={g_pde.item():.3e} "
f"| lr={lr:.1e}")
如何读这些数字
- 看比值:若
L_data / (λ·L_pde)长期大于 100 或小于 0.01,说明两项量级严重失衡,总损失曲线基本只反映大项,小项“看起来没降”其实是没被优化。 - 看梯度范数:若
g_data与g_pde相差 2 个数量级以上,即使损失比值尚可,参数更新也主要被大梯度项主导,属于权重失衡的另一种表现。 - 看学习率:若两项梯度范数同量级但都极小(如 1e-8),且参数更新量接近 0,则是学习率过小或已进入平坦区;若损失出现明显震荡或 NaN,则是学习率过大。
- 看更新量:记录
‖Δθ‖/‖θ‖,若长期低于 1e-6,说明优化几乎停滞,应优先排查学习率而非权重。
判断顺序建议:先看学习率与更新量排除“完全不动”,再看两项梯度范数判断谁在主导,最后用损失比值确认量级差。三者一致时结论最可靠。
可验证的调整方案
若判定为优化问题:做一次学习率扫描(如 1e-4、1e-3、1e-2 各跑 500 步),观察是否出现损失下降段;若大 lr 震荡,加入梯度裁剪 torch.nn.utils.clip_grad_norm_(net.parameters(), 1.0) 后复测。
若判定为权重失衡:按量级比调整 λ,例如初始 L_data=1e-2、L_pde=1e2,则令 λ ← λ·L_data/L_pde = 1e-4,使两项量级接近;调整后复测两项梯度范数是否落在同一数量级。也可改用自适应权重(如 GradNorm 思路:按梯度范数比动态更新 λ)。
| 观测现象 | 归因 | 验证动作 | 通过判据 |
|---|---|---|---|
| 两项梯度范数同量级但均 < 1e-7,更新量≈0 | 学习率过小/平坦区 | lr 放大 10 倍重跑 | 损失出现持续下降段 |
| 损失震荡或 NaN | 学习率过大 | lr 缩小 10 倍 + 梯度裁剪 | 曲线平滑下降 |
| 损失比值 > 100 或 < 0.01 | 权重失衡 | 按量级比调 λ 后复测 | 两项梯度范数同量级 |
| g_pde 比 g_data 小 2 个数量级以上 | 物理项被淹没 | 增大 λ 或改用自适应权重 | g_pde 提升至同量级 |
调整 λ 后必须重新观察两项梯度范数,而不是只看总损失是否下降——总损失下降可能只是大项继续优化,小项仍未参与训练。