PINN Poisson 1d
📌
一、问题定义
我们要求解的微分方程是
d 2 u d x 2 = f ( x ) \begin{equation} \frac{d^2 u}{d x^2} = f(x) \end{equation} dx2d2u=f(x)
其中:
- f ( x ) = − 0.49 s i n ( 0.7 x ) − 2.25 c o s ( 1.5 x ) f(x) = -0.49sin(0.7x) - 2.25cos(1.5x) f(x)=−0.49sin(0.7x)−2.25cos(1.5x)
- 定义域: x ∈ [ − 10 , 10 ] x \in [-10, 10] x∈[−10,10]
- 边界条件: u ( − 10 ) = u l e f t u(-10) = u_{left} u(−10)=uleft , , , u ( 10 ) = u r i g h t u(10)=u_{right} u(10)=uright
真解为:
u ( x ) = s i n ( 0.7 x ) + c o s ( 1.5 x ) − 0.1 x u(x) = sin(0.7x) + cos(1.5x) - 0.1x u(x)=sin(0.7x)+cos(1.5x)−0.1x
📌二、PINN 思路
我们用一个神经网络 u θ ( x ) u_{\theta}(x) uθ(x)拟合方程的解, PINN 的核心是通过自动微分 (autograd) 求导,把屋里方程残差纳入损失函数。
- 方程残差:
r ( x ) = d 2 u θ d x 2 − f ( x ) \begin{equation} r(x) = \frac{d^2u_{\theta}}{d x^2} - f(x) \end{equation} r(x)=dx2d2uθ−f(x)希望在训练过程中让残差趋近于0。 - 边界条件残差:
r b c = ( u θ ( − 10 ) − u a ) 2 + ( u θ ( 10 ) − u b ) 2 \begin{equation} r_{bc} = (u_{\theta}(-10) - u_{a})^2 + (u_{\theta}(10) - u_{b})^2 \end{equation} rbc=(uθ(−10)−ua)2+(uθ(10)−ub)2
📌三、损失函数构建
最终的损失函数是由两个部分组成:
L ( θ ) = L f + L b c \begin{equation} \mathcal{L}(\theta) = \mathcal{L}_f + \mathcal{L}_{bc} \end{equation} L(θ)=Lf+Lbc
其中方程的残差为
L f = 1 N f ∑ i = 1 N f ( d 2 u θ ( x f ( i ) ) d x 2 − f ( x f ( i ) ) ) 2 \begin{equation} \mathcal{L}_f = \frac{1}{N_f} \sum_{i=1}^{N_f} \left( \frac{d^2 u_\theta(x_f^{(i)})}{dx^2} - f(x_f^{(i)}) \right)^2 \end{equation} Lf=Nf1i=1∑Nf(dx2d2uθ(xf(i))−f(xf(i)))2
边界条件损失为:
L b c = ( u θ ( x a ) − u a ) 2 + ( u θ ( x b ) − u b ) 2 \begin{equation} \mathcal{L}_{bc} = \left(u_\theta(x_a) - u_a\right)^2 + \left(u_\theta(x_b) - u_b\right)^2 \end{equation} Lbc=(uθ(xa)−ua)2+(uθ(xb)−ub)2
其中:
- N f N_f Nf方程内部采样点的数量
📌四、梯度计算(自动微分)
利用 Pytorch Autograd:
-
求 u θ ( x ) u_{\theta}(x) uθ(x)对 x x x 求一阶导数
u x = ∂ u θ ∂ x \begin{equation} u_x \;=\; \frac{\partial u_{\theta}}{\partial x} \end{equation} ux=∂x∂uθ -
求 u θ ( x ) u_{\theta}(x) uθ(x) 对 x x x 求二阶导数
u x x = ∂ 2 u θ ∂ x 2 \begin{equation} u_{xx} \;=\; \frac{\partial^2 u_{\theta}}{\partial x^2} \end{equation} uxx=∂x2∂2uθ
📌 五、优化过程
通过 Adam + L-BFGS 联合优化:
- Adam 进行粗调,快速收敛到低损失区域
- L-BFGS 精调,寻找更优点,尤其适合无噪声的 PINN 问题
目标是最小化总损失 L ( θ ) \mathcal{L}(\theta) L(θ)
📌 六、代码实现
import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt# 设置随机种子 保证复现
torch.manual_seed(42)
np.random.seed(42)# 定义 PINN 网络结构
class PINN(nn.Module):def __init__(self):super(PINN, self).__init__()self.net = nn.Sequential(nn.Linear(1, 64),nn.Tanh(),nn.Linear(64, 64),nn.Tanh(),nn.Linear(64, 1))def forward(self, x):return self.net(x)# 定义方程残差
def compute_residual(x):x.requires_grad = Trueu = model(x)u_x = torch.autograd.grad(u, x, grad_outputs=torch.ones_like(u), create_graph=True)[0]u_xx = torch.autograd.grad(u_x, x, grad_outputs=torch.ones_like(u_x), create_graph=True)[0]f = -0.49 * torch.sin(0.7 * x) - 2.25 * torch.cos(1.5 * x)residual = u_xx - freturn residual# 定义损失函数
def loss_function():# 方程残差损失 (collocation points)x_f = torch.linspace(-10, 10, 100).view(-1, 1)residual = compute_residual(x_f)loss_residual = torch.mean(residual**2)# 边界条件损失u_left = model(torch.tensor([[-10.0]]))u_right = model(torch.tensor([[10.0]]))u_left_true = -np.sin(7) + np.cos(15) + 1u_right_true = np.sin(7) + np.cos(15) - 1loss_bc = (u_left - u_left_true)**2 + (u_right - u_right_true)**2return loss_residual + loss_bc# 实例化模型
model = PINN()# 定义优化器
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)# 训练
epochs = 5000
loss_history = []for epoch in range(epochs):optimizer.zero_grad()loss = loss_function()loss.backward()optimizer.step()loss_history.append(loss.item())if epoch % 500 == 0:print(f'Epoch {epoch}, Loss: {loss.item():.5f}')# (可选)用 L-BFGS 精调
lbfgs_optimizer = torch.optim.LBFGS(model.parameters(), lr=1.0, max_iter=500)def closure():lbfgs_optimizer.zero_grad()loss = loss_function()loss.backward()return losslbfgs_optimizer.step(closure)# 预测与对比
x_test = torch.linspace(-10, 10, 200).view(-1, 1)
u_pinn = model(x_test).detach().numpy()x_np = x_test.numpy()
u_true = np.sin(0.7 * x_np) + np.cos(1.5 * x_np) - 0.1 * x_np# 一行两列子图
fig, axes = plt.subplots(1, 2, figsize=(14, 5))# 左图:PINN预测 vs 解析解
axes[0].plot(x_np, u_pinn, label="PINN Prediction", linewidth=2)
axes[0].plot(x_np, u_true, label="Analytical Solution", linestyle='dashed', linewidth=2)
axes[0].set_xlabel("x")
axes[0].set_ylabel("u(x)")
axes[0].set_title("PINN vs Analytical Solution")
axes[0].legend()
axes[0].grid(True)# 右图:Loss曲线
axes[1].plot(loss_history)
axes[1].set_yscale('log')
axes[1].set_xlabel("Epoch")
axes[1].set_ylabel("Loss")
axes[1].set_title("Training Loss History")
axes[1].grid(True)# 调整子图间距
plt.tight_layout()# 显示
plt.show()