引言

物理信息神经网络(Physics-Informed Neural Networks, PINNs)是由 Raissi 等人于 2019 年提出的一种将物理定律嵌入神经网络训练过程的框架。1 传统数值方法(如有限元法、有限差分法)需要将连续问题离散化,而 PINNs 通过将偏微分方程(Partial Differential Equations, PDEs)的约束直接编码为神经网络的损失函数,利用自动微分(Automatic Differentiation, AD)技术实现对物理规律的端到端学习。

为什么需要 PINNs

传统 PDE 求解方法面临以下挑战:

方法优点缺点
有限元法(FEM)理论完善,精度高网格生成复杂,高维问题维度灾难
有限差分法(FDM)实现简单网格依赖,复杂几何形状处理困难
谱方法高精度仅适合规则区域,边界条件处理复杂

PINNs 的核心优势在于:

  1. 无网格方法:避免离散化,天然适合高维问题和复杂几何
  2. 数据驱动:可融合稀疏观测数据与物理约束
  3. 端到端学习:损失函数同时包含物理先验和数据拟合项
  4. 逆问题求解:可从少量观测数据中反推未知参数

核心思想

将 PDE 约束编码为损失函数

考虑一般形式的 PDE:

其中 是未知解, 是物理参数, 是微分算子。

PINNs 通过神经网络 近似解,并将 PDE 残差作为损失项:

自动微分求导

PINNs 利用自动微分技术精确计算神经网络的任意阶偏导数。给定网络 ,其导数可通过反向传播计算:

import torch
 
class PINN(torch.nn.Module):
    def __init__(self, layers):
        super().__init__()
        self.fc = torch.nn.ModuleList([
            torch.nn.Linear(layers[i], layers[i+1]) 
            for i in range(len(layers)-1)
        ])
        self.activation = torch.nn.Tanh()
    
    def forward(self, x, t):
        X = torch.cat([x, t], dim=1)
        for i in range(len(self.fc)-1):
            X = self.activation(self.fc[i](X))
        return self.fc[-1](X)
    
    def pde_residual(self, x, t, nu):
        """计算 Burgers 方程残差: u_t + u*u_x - nu*u_xx = 0"""
        x.requires_grad_(True)
        t.requires_grad_(True)
        
        u = self.forward(x, t)
        
        # 一阶导数
        u_t = torch.autograd.grad(u, t, grad_outputs=torch.ones_like(u),
                                   create_graph=True)[0]
        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]
        
        # Burgers 方程残差
        residual = u_t + u * u_x - nu * u_xx
        return residual

硬约束 vs 软约束

软约束(Soft Constraint):将边界条件和初始条件作为损失项加权加入:

硬约束(Hard Constraint):通过特殊网络架构强制满足边界条件:

  • Bas sin 网络:将满足边界条件的解析基函数嵌入网络输出2
  • 罚函数法:使用高权重强制边界条件
  • 参数化变换:设计输出层结构使边界值自动为零
class HardConstraintPINN(torch.nn.Module):
    """硬约束示例:边界条件自动满足"""
    def __init__(self, base_net):
        super().__init__()
        self.base_net = base_net
    
    def forward(self, x, t):
        """
        对区间 [0, 1] × [0, T],若边界条件为 u(0,t)=u(1,t)=0
        使用 x*(1-x) 乘子自动满足
        """
        u_base = self.base_net(x, t)
        # x*(1-x) 确保 x=0 和 x=1 处为零
        return x * (1 - x) * u_base

损失函数设计

完整损失函数

PINNs 的总损失函数通常包含以下组成部分:

PDE 残差项

对于一般非线性 PDE:

常见 PDE 的残差形式

方程形式残差
Poisson
热传导
Burgers
Navier-Stokes见下方代码
def navier_stokes_residual(u_net, x, y, t, rho=1.0, nu=0.01):
    """2D 不可压 Navier-Stokes 方程"""
    u = u_net(x, y, t)[:, 0:1]  # u 速度
    v = u_net(x, y, t)[:, 1:2]  # v 速度
    p = u_net(x, y, t)[:, 2:3]  # 压力
    
    # 开启梯度计算
    u_x, u_y, u_t = compute_derivatives(u, [x, y, t])
    v_x, v_y, v_t = compute_derivatives(v, [x, y, t])
    _, _, p_x = compute_derivatives(p, [x, y, t])
    _, _, p_y = compute_derivatives(p, [x, y, t])
    
    u_xx = compute_2nd_derivative(u, x, x)
    u_yy = compute_2nd_derivative(u, y, y)
    v_xx = compute_2nd_derivative(v, x, x)
    v_yy = compute_2nd_derivative(v, y, y)
    
    # 连续性方程(不可压条件)
    continuity = u_x + v_y
    
    # 动量方程
    momentum_x = u_t + u*u_x + v*u_y + p_x/rho - nu*(u_xx + u_yy)
    momentum_y = v_t + u*v_x + v*v_y + p_y/rho - nu*(v_xx + v_yy)
    
    return momentum_x, momentum_y, continuity

边界条件(BC)项

Dirichlet 边界条件

Neumann 边界条件

周期性边界条件

初始条件(IC)项

自适应权重策略

固定权重难以平衡不同损失项的梯度量级。自适应权重方法动态调整权重:

GradNorm 算法3

其中 是平均梯度范数。

动态加权平均(DWA)

class AdaptiveWeightPINN:
    def __init__(self, pinn, method='gradnorm', alpha=0.5):
        self.pinn = pinn
        self.method = method
        self.alpha = alpha
        self.weights = {'residual': 1.0, 'bc': 1.0, 'ic': 1.0, 'data': 1.0}
        self.loss_history = {'residual': [], 'bc': [], 'ic': [], 'data': []}
    
    def update_weights_gradnorm(self, losses, grads):
        """GradNorm 自适应权重更新"""
        N = len(losses)
        G_i = [torch.norm(g) for g in grads.values()]
        G_avg = sum(G_i) / N
        
        for key, G_i_val in zip(self.weights.keys(), G_i):
            self.weights[key] = self.weights[key] * (G_avg / G_i_val) ** self.alpha
        
        # 归一化
        total = sum(self.weights.values())
        for key in self.weights:
            self.weights[key] /= total
    
    def compute_weighted_loss(self, x, t, x_bc, t_bc, u_bc, x_ic, u_ic):
        """计算加权总损失"""
        # 各分量损失
        L_res = self.pinn.pde_residual(x, t)
        L_bc = self.pinn.boundary_loss(x_bc, t_bc, u_bc)
        L_ic = self.pinn.initial_loss(x_ic, u_ic)
        
        # 返回加权损失和未加权损失(用于梯度计算)
        weighted_loss = (self.weights['residual'] * L_res + 
                        self.weights['bc'] * L_bc + 
                        self.weights['ic'] * L_ic)
        
        return weighted_loss, {'residual': L_res, 'bc': L_bc, 'ic': L_ic}

网络架构

全连接网络(标准 PINNs)

标准 PINNs 使用多层全连接网络(Feedforward Neural Network, FNN):

其中

class StandardPINN(torch.nn.Module):
    def __init__(self, input_dim, output_dim, hidden_layers=[64, 64, 64, 64], 
                 activation='tanh'):
        super().__init__()
        
        activations = {
            'tanh': torch.nn.Tanh(),
            'relu': torch.nn.ReLU(),
            'gelu': torch.nn.GELU(),
            'sin': lambda x: torch.sin(x),
            'siren': lambda x: torch.sin(30 * x)
        }
        act = activations.get(activation, torch.nn.Tanh())
        
        layers = []
        prev_dim = input_dim
        for h_dim in hidden_layers:
            layers.extend([
                torch.nn.Linear(prev_dim, h_dim),
                act
            ])
            prev_dim = h_dim
        layers.append(torch.nn.Linear(prev_dim, output_dim))
        
        self.network = torch.nn.Sequential(*layers)
    
    def forward(self, x, t):
        return self.network(torch.cat([x, t], dim=1))

残差连接(ResNet 型 PINNs)

深层网络训练困难,残差连接缓解梯度消失:

class ResidualBlock(torch.nn.Module):
    def __init__(self, dim):
        super().__init__()
        self.fc1 = torch.nn.Linear(dim, dim)
        self.fc2 = torch.nn.Linear(dim, dim)
        self.activation = torch.nn.Tanh()
    
    def forward(self, x):
        residual = x
        out = self.activation(self.fc1(x))
        out = self.fc2(out)
        return out + residual  # 残差连接
 
class ResPINN(torch.nn.Module):
    def __init__(self, input_dim, output_dim, hidden_dim=64, num_blocks=4):
        super().__init__()
        self.input_layer = torch.nn.Sequential(
            torch.nn.Linear(input_dim, hidden_dim),
            torch.nn.Tanh()
        )
        self.res_blocks = torch.nn.ModuleList([
            ResidualBlock(hidden_dim) for _ in range(num_blocks)
        ])
        self.output_layer = torch.nn.Linear(hidden_dim, output_dim)
    
    def forward(self, x, t):
        h = self.input_layer(torch.cat([x, t], dim=1))
        for block in self.res_blocks:
            h = block(h)
        return self.output_layer(h)

激活函数选择

激活函数表达式适用场景
Tanh默认选择,光滑逼近
ReLU简单问题,计算高效
GELUTransformer 风格架构
SIREN周期性/振荡问题

SIREN(正弦网络)4 对拉普拉斯算子具有归纳偏置:

class SIRENLayer(torch.nn.Module):
    def __init__(self, in_dim, out_dim, omega=30):
        super().__init__()
        self.linear = torch.nn.Linear(in_dim, out_dim)
        self.omega = omega
    
    def forward(self, x):
        return torch.sin(self.omega * self.linear(x))

自适应采样策略

残差加权采样

高 PDE 残差区域需要更多采样点以降低逼近误差:

def residual_based_sampling(pinn, domain_bounds, N_samples, N_iterations=5):
    """
    基于残差的自适应采样
    1. 均匀采样计算残差
    2. 按残差大小加权采样
    3. 迭代细化
    """
    x_min, x_max = domain_bounds['x']
    t_min, t_max = domain_bounds['t']
    
    all_points = []
    
    for _ in range(N_iterations):
        # 均匀采样
        x = torch.rand(N_samples, 1) * (x_max - x_min) + x_min
        t = torch.rand(N_samples, 1) * (t_max - t_min) + t_min
        
        # 计算残差
        residual = pinn.pde_residual(x, t)
        residuals = torch.abs(residual).detach()
        
        # 按残差加权采样(保留高残差点)
        probs = residuals / residuals.sum()
        indices = torch.multinomial(probs.view(-1), N_samples, replacement=True)
        
        x_sampled = x[indices]
        t_sampled = t[indices]
        all_points.append((x_sampled, t_sampled))
    
    return all_points

分布平行采样(DPS)

DPS(Distribution Parallel Sampling)结合残差信息和边界层检测:

class DPSampler:
    def __init__(self, domain, N_physics=1000, N_boundary=100, N_initial=100):
        self.domain = domain
        self.N_physics = N_physics
        self.N_boundary = N_boundary
        self.N_initial = N_initial
    
    def sample(self, pinn, weights=None):
        if weights is None:
            weights = {'residual': 1.0, 'boundary': 1.0, 'initial': 1.0}
        
        # 内部残差点
        x_r, t_r = self.domain.sample_collocation(self.N_physics)
        
        # 边界点(4个边界)
        x_bc, t_bc, u_bc = self.domain.sample_boundary(self.N_boundary)
        
        # 初始条件点
        x_ic, t_ic, u_ic = self.domain.sample_initial(self.N_initial)
        
        # 计算各区域残差并调整权重
        if self.use_adaptive:
            res_physics = pinn.pde_residual(x_r, t_r)
            res_boundary = pinn.boundary_loss(x_bc, t_bc, u_bc)
            res_initial = pinn.initial_loss(x_ic, u_ic)
            
            # 调整采样分布
            # ...
        
        return {
            'residual': (x_r, t_r),
            'boundary': (x_bc, t_bc, u_bc),
            'initial': (x_ic, t_ic, u_ic)
        }

动态权重调整

class DynamicSamplingPINN:
    def __init__(self, pinn, domain, strategy='curriculum'):
        self.pinn = pinn
        self.domain = domain
        self.strategy = strategy
        self.residual_history = []
    
    def compute_adaptive_loss(self, epoch, total_epochs):
        # 学习率调度式采样:初期宽松,后期严格
        if self.strategy == 'curriculum':
            # 余弦退火式难度增加
            progress = epoch / total_epochs
            alpha = 0.5 * (1 + np.cos(np.pi * progress))
        else:
            alpha = 1.0
        
        # 采样
        x, t = self.domain.sample(self.N_points)
        
        # 计算残差
        residual = self.pinn.pde_residual(x, t)
        
        # 残差加权损失
        weights = torch.abs(residual) ** alpha
        weighted_loss = (weights * residual ** 2).mean()
        
        return weighted_loss

挑战与局限

高维问题

PINNs 在高维问题()面临维度灾难

  • 均匀采样密度: 个点填满 维空间,每维仅有 个网格点
  • 精度随维度指数衰减

解决方向

  • 神经算子(如 FNO、DeepONet)替代逐点逼近
  • 稀疏数据驱动
  • 低维流形假设

奇异性

PDE 解的奇异性(如裂缝、激波)导致网络难以精确拟合:

问题表现应对策略
导数奇异性梯度爆炸,训练不稳定软化奇异点、损失重加权
边界层薄区域内梯度变化剧烈网格加密、边界层检测
不连续解连续网络无法精确表示接触线检测、水平集方法

多尺度问题

物理系统常包含多个特征尺度:

时,出现边界层,PINNs 难以同时捕捉宏观和微观行为。

收敛性保证

PINNs 缺乏严格的收敛理论保证:

  • 损失景观非凸,存在多个局部最优
  • 自适应采样可能导致训练不稳定
  • 梯度消失/爆炸问题

2025 年新进展

PINN 与神经算子结合

神经算子(Neural Operators)学习解算子映射 ,结合 PINNs 的物理约束:

Physics-Informed Neural Operator (PINO)5

其中 是学习到的神经算子。

Physics-Informed Laplace Neural Operator (PI-LNO)6

将拉普拉斯算子嵌入神经算子架构,利用傅里叶空间的谱特性提高计算效率。

收敛性理论进展

2024-2025 年理论工作取得重要进展:

  1. 过度参数化分析:证明足够宽的网络可收敛到 PDE 真解7
  2. 损失景观分析:揭示了物理约束项对优化动态的影响
  3. PAC-Bayes 框架:为 PINNs 提供泛化界

推理时间缩放

Physics-Informed Inference Time Scaling (PI-ITS)8 探索推理时计算资源与精度的关系:

  • 训练阶段:标准 PINNs 训练
  • 推理阶段:通过额外前向传播迭代精化预测
  • 类似于 LLM 的推理时计算扩展范式

应用场景

流体力学

问题PDE典型应用
Burgers 方程交通流、激波建模
Navier-Stokes不可压/可压 NS 方程翼型绕流、湍流模拟
浅水方程波面方程河流、海洋建模
# 2D Navier-Stokes 流体 PINN
class NavierStokesPINN(torch.nn.Module):
    def __init__(self, nu=0.01, rho=1.0):
        super().__init__()
        self.net = StandardPINN(3, 3)  # (x, y, t) -> (u, v, p)
        self.nu = nu
        self.rho = rho
    
    def forward(self, x, y, t):
        return self.net(x, y, t)
    
    def loss(self, x, y, t, x_bc, y_bc, t_bc, u_bc, v_bc, 
             x_ic, y_ic, t_ic, u_ic, v_ic):
        # PDE 残差
        res_x, res_y, res_cont = navier_stokes_residual(self.net, x, y, t, 
                                                         self.nu, self.rho)
        L_pde = torch.mean(res_x**2 + res_y**2 + res_cont**2)
        
        # 边界条件
        u_pred, v_pred, _ = self.net(x_bc, y_bc, t_bc)
        L_bc = torch.mean((u_pred - u_bc)**2 + (v_pred - v_bc)**2)
        
        # 初始条件
        u_ic_pred, v_ic_pred, _ = self.net(x_ic, y_ic, t_ic)
        L_ic = torch.mean((u_ic_pred - u_ic)**2 + (v_ic_pred - v_ic)**2)
        
        return L_pde + L_bc + L_ic

热传导

热方程

  • 温度场预测
  • 热传导反问题(从观测推断热导率

量子力学

薛定谔方程

PINNs 可用于求解量子系统的基态和动力学演化。

生物医学

  • 药物扩散建模
  • 血流仿真
  • 神经网络与生物物理模型结合

材料科学

  • 相变建模
  • 断裂力学
  • 多物理场耦合

参考资料

Footnotes

  1. Raissi, M., Perdikaris, P., & Karniadakis, G. E. (2019). Physics-informed neural networks: A deep learning framework for solving forward and inverse problems involving nonlinear partial differential equations. Journal of Computational Physics, 378, 686-707. https://doi.org/10.1016/j.jcp.2018.10.045

  2. Jagtap, A. D., Kawaguchi, K., & Karniadakis, G. E. (2020). Adaptive activation functions accelerate convergence in deep and physics-informed neural networks. Journal of Computational Physics, 404, 109136. https://doi.org/10.1016/j.jcp.2019.109136

  3. Chen, Z., Liu, Y., & Sun, H. (2021). Physics-informed learning of governing equations from scarce data. Nature Communications, 12(1), 6136. https://doi.org/10.1038/s41467-021-26407-4

  4. Sitzmann, V., Martel, J., Bergman, A., Lindell, D., & Wetzstein, G. (2020). Implicit neural representations with periodic activation functions. NeurIPS. https://proceedings.neurips.cc/paper/2020/hash/53c04118df206c0e8cc5dbd05942b0f4-Abstract.html

  5. Li, Z., Kovachki, N., Azizzadenesheli, K., et al. (2021). Physics-informed neural operator for learning partial differential equations. arXiv preprint. https://arxiv.org/abs/2111.03794

  6. Physics-Informed Laplace Neural Operator (2025). arXiv:2602.12706. https://arxiv.org/abs/2602.12706

  7. Shin, Y., Darbon, J., & Karniadakis, G. E. (2023). On the convergence of physics-informed neural networks for linear second-order elliptic and parabolic PDEs. Communications in Computational Physics. https://doi.org/10.4208/cicp.OA-2023-0289

  8. Physics-Informed Inference Time Scaling (2025). arXiv:2504.16172. https://arxiv.org/abs/2504.16172