You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyTorch实现PINN求解简单ODE拟合不佳问题咨询

PINN求解简单ODE拟合失败排查与改进方案

最高优先级根因:损失函数缺失边界约束

你当前训练流程仅优化PDE残差损失,完全没有将两个边界条件y(0)=0、y(2π)=0纳入损失计算。

针对你求解的方程dy/dx=cos(x),仅满足PDE的通解为y=sin(x)+C,其中C为任意常数。没有边界条件约束时,模型没有任何动力锁定C=0的特解,训练过程中常数项随机漂移,自然会出现结果完全偏离的问题。

PINN标准损失构成为三类损失的加权和:总损失 = PDE残差损失 + 边界/初值条件损失 + 观测数据损失(若有),缺失任何一类约束都会导致解空间不收敛到目标特解。

次优先级常见代码疏漏排查

按以下顺序逐行检查代码:

  • 自动微分配置错误:调用torch.autograd.grad计算一阶导时,必须传入create_graph=True参数,否则反向传播无法穿过微分节点回传梯度,PDE损失相当于无效损失;若训练过程中出现计算图释放报错,补充retain_graph=True参数。
  • 计算图断裂:检查输入的采样点张量是否设置requires_grad=True,网络输出在计算损失前有没有被.detach()、.cpu().numpy()这类截断计算图的操作。
  • 采样逻辑错误:内部PDE采样点取开区间(0, 2π)即可,不要把边界点混入PDE损失计算;边界点需要单独采样,单独计算边界损失。
  • 验证逻辑错误:先确认训练定义域[0,2π]内的拟合精度,再看扩展区间效果。PINN本身没有外推约束,超出训练区间的预测偏差属于正常现象,不能作为训练失败的判断依据。

训练流程优化建议

  • 迭代轮次调整:500轮Adam迭代对于4层50神经元的网络完全不足以收敛,建议先训练10000~20000轮观察损失下降趋势,Adam收敛到平台后可以切换L-BFGS优化器进一步提升精度。
  • 损失权重调整:训练过程中分别打印PDE损失、边界损失的数值,如果两类损失量级差超过1个数量级,需要手动给小量级损失加权重,保证两类损失对总损失的贡献相当,避免模型只优化某一类损失。
  • 采样密度调整:单周期内内部PDE采样点建议至少取100~200个,采样点过稀疏会导致残差约束不足,模型在采样点间隙出现不符合方程的波动。

修正后可运行核心代码

import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt

# 环境配置
torch.set_default_dtype(torch.float32)
torch.manual_seed(42)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# PINN网络定义
class PINN(nn.Module):
    def __init__(self, layer_dims):
        super().__init__()
        self.linears = nn.ModuleList([
            nn.Linear(layer_dims[i], layer_dims[i+1]) for i in range(len(layer_dims)-1)
        ])
        self.act = nn.Tanh()
        # 参数初始化
        for layer in self.linears:
            nn.init.xavier_normal_(layer.weight)
            nn.init.zeros_(layer.bias)
    
    def forward(self, x):
        for i in range(len(self.linears)-1):
            x = self.act(self.linears[i](x))
        return self.linears[-1](x)

# 超参配置
layer_dims = [1, 50, 50, 50, 50, 1]
model = PINN(layer_dims).to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
mse = nn.MSELoss()

# 数据集构建
## 内部PDE采样点(去掉首尾边界点)
x_pde = torch.linspace(0, 2*np.pi, 200, device=device)[1:-1].reshape(-1, 1)
x_pde.requires_grad = True
## 边界点
x_bc = torch.tensor([[0.0], [2*np.pi]], device=device)
y_bc = torch.tensor([[0.0], [0.0]], device=device)

# 训练循环
epochs = 15000
for epoch in range(epochs):
    optimizer.zero_grad()
    # 计算PDE残差损失
    y_pde_pred = model(x_pde)
    dy_dx = torch.autograd.grad(
        y_pde_pred, x_pde,
        grad_outputs=torch.ones_like(y_pde_pred),
        create_graph=True,
        retain_graph=True
    )[0]
    loss_pde = mse(dy_dx, torch.cos(x_pde))
    # 计算边界损失
    y_bc_pred = model(x_bc)
    loss_bc = mse(y_bc_pred, y_bc)
    # 总损失(两类损失量级接近,权重均取1即可)
    loss_total = loss_pde + loss_bc
    loss_total.backward()
    optimizer.step()

    # 每1000轮打印损失
    if epoch % 1000 == 0:
        print(
            f"Epoch:{epoch:5d} | Total Loss:{loss_total.item():.6f} "
            f"| PDE Loss:{loss_pde.item():.6f} | BC Loss:{loss_bc.item():.6f}"
        )

# 训练区间验证
x_test = torch.linspace(0, 2*np.pi, 1000, device=device).reshape(-1,1)
y_pred = model(x_test).detach().cpu().numpy()
y_true = np.sin(x_test.cpu().numpy())
# 自行添加绘图逻辑即可对比预测值与解析解

快速验证技巧

  • 做网络能力 sanity check:先用解析解生成一批带噪声的(x,y)样本,用纯数据驱动的MSE损失训练同一个网络,确认网络本身能拟合sin(x)曲线,排除网络结构、初始化、设备配置的低级错误。
  • 损失分拆监控:训练时不要只看总损失,必须单独打印PDE损失和边界损失的变化,如果某一类损失始终不下降,直接定位对应部分的梯度链路问题,不要盲目调参。

内容的提问来源于stack exchange,提问作者falamiw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 01:25:05