1. 案例1:物理约束神经网络求解一维稳态热传导

问题设定与物理约束

考虑一根长度 L = 1 m 的均质杆,导热系数 k = 1 W/(m·K),无内热源。稳态一维热传导方程为:

d²T/dx² = 0,  x ∈ (0, L)
T(0) = T_left = 100 °C
T(L) = T_right = 0 °C

解析解为线性分布 T(x) = 100(1 - x)。本例用 PINN 学习该温度场,网络输入为坐标 x,输出为温度 T。训练损失由两部分加权组成:

  • 物理残差损失 L_phys = mean( (d²T/dx²)² ),在内部配点上计算,约束方程成立;
  • 边界损失 L_bc = mean( (T(0)-100)² + (T(1)-0)² ),约束 Dirichlet 条件。

总损失 L = λ_phys·L_phys + λ_bc·L_bc。加权组合是关键:若 λ_phys 过大,边界约束被忽视,解整体漂移;若 λ_bc 过大,网络只拟合两端点而内部不满足方程。下面给出可运行的 PyTorch 实现。

PINN 求解一维稳态热传导:输入、网络、双损失与温度场 杆 L=1 m,k=1 x=0, T=100 °C x=1, T=0 °C 内部配点 x_i ∈ (0,1) MLP x → T(x) tanh 激活 物理残差 L_phys mean(d²T/dx²)² 边界损失 L_bc (T(0)-100)²+(T(1)-0)² L = λ_phys·L_phys + λ_bc·L_bc 预测温度场 T(x) x=0 x=1 100 解析解 T=100(1-x) Dirichlet Dirichlet

关键实现:自动微分求二阶导

物理残差需要对网络输出关于输入求二阶导。PyTorch 中用 torch.autograd.grad 并设置 create_graph=True,使一阶导仍可继续求导。注意输入 x 必须 requires_grad=True。

import torch
import torch.nn as nn

torch.manual_seed(0)

class PINN(nn.Module):
    def __init__(self, width=32):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(1, width), nn.Tanh(),
            nn.Linear(width, width), nn.Tanh(),
            nn.Linear(width, 1)
        )
    def forward(self, x):
        return self.net(x)

def physics_residual(model, x):
    x = x.clone().requires_grad_(True)
    T = model(x)
    dT = torch.autograd.grad(T, x, torch.ones_like(T), create_graph=True)[0]
    d2T = torch.autograd.grad(dT, x, torch.ones_like(dT), create_graph=True)[0]
    return d2T

model = PINN()
opt = torch.optim.Adam(model.parameters(), lr=1e-3)

# 内部配点与边界点
x_col = torch.rand(200, 1)                 # 内部配点
x_bc = torch.tensor([[0.0], [1.0]])        # 边界点
T_bc = torch.tensor([[100.0], [0.0]])

lam_phys, lam_bc = 1.0, 100.0              # 加权组合

for step in range(5000):
    opt.zero_grad()
    r = physics_residual(model, x_col)
    loss_phys = (r ** 2).mean()
    loss_bc = ((model(x_bc) - T_bc) ** 2).mean()
    loss = lam_phys * loss_phys + lam_bc * loss_bc
    loss.backward()
    opt.step()
    if step % 1000 == 0:
        print(f"step {step:5d}  L_phys={loss_phys.item():.3e}  L_bc={loss_bc.item():.3e}")

加权组合的调参逻辑

边界损失量级通常远小于物理残差(残差是二阶导,数值小),因此 λ_bc 需要放大才能让边界条件被有效约束。经验做法:先固定 λ_phys=1,把 λ_bc 从 10 试到 1000,观察两项损失是否同步下降。若 L_bc 长期停在较大值,说明边界权重不足;若 L_phys 不降而 L_bc 迅速趋零,说明网络只记住了端点、内部方程未被满足。

验证预测误差

训练后在均匀网格上对比解析解,计算最大绝对误差与均方根误差:

x_test = torch.linspace(0, 1, 101).unsqueeze(1)
with torch.no_grad():
    T_pred = model(x_test)
T_exact = 100.0 * (1 - x_test)
err = (T_pred - T_exact).abs()
print("最大绝对误差:", err.max().item())
print("RMSE:", torch.sqrt(((T_pred - T_exact) ** 2).mean()).item())

对线性解,合理训练后最大绝对误差通常可降到 1 °C 量级以下(示例预期,实际数值随随机种子与训练轮数变化)。若误差偏大,优先检查:配点是否覆盖整个区间、λ_bc 是否足够、训练轮数是否充分。

一维稳态问题的解析解是直线,可作为 PINN 流程的“最小可验证样例”:先确认损失下降、边界满足、误差收敛,再迁移到含非线性项或变系数的方程。