从零实现PINN:一维泊松方程PyTorch实战

发布时间:2026/10/20 8:56:57

从零实现PINN:一维泊松方程PyTorch实战 大概每个刚接触物理信息神经网络PINN的人都会经历同一个尴尬阶段论文里的公式全都认识示意图也看得懂——“让神经网络的输出满足偏微分方程”听上去比训练一个图像分类器还简单——可一旦打开编辑器却不知道第一行该写什么。我当初第一次复现 PINN 的时候就是这个状态前前后后折腾了将近一周才把一维例子从方程到代码完整跑通。期间踩的坑包括自动微分忘了开 create_graph、激活函数用了 ReLU 导致损失卡死在一个降不动的平台、边界权重太小导致端点“飘”起来……每一个问题单独看都不难但串在一起足以劝退一个新手。这篇文章就用一个具体的例子——一维泊松方程 u(x) -π²sin(πx)配上最简单却可解析验证的边界条件 u(0)u(1)0——把 PINN 的完整链路走一遍。内容包括四块PINN 的核心思想和损失函数从哪来、数学上如何把方程改写成残差优化、完整可运行的 PyTorch 代码、以及训练结果的分析方法和常见坑。目标读者是读过一些 PINN 介绍、但还没亲手写过一个能跑通程序的人读完这篇文章你应该能独立复现出自己的第一版实现并且知道每一步选择背后的理由。1. 先想明白PINN 为什么能用方程当监督信号1.1 普通神经网络和物理信息神经网络的区别普通监督学习的标准流程是准备好数据集、定义网络、用标签算损失、反向传播更新参数。这套流程在图像、文本、语音这类场景非常成熟可一落到物理问题就尴尬了——高质量的标签往往很贵。做实验测量要花钱花时间用高精度求解器生成数据集也得先有一套可靠的传统数值算法。而另一方面我们手里明明握着远比数据更硬的信息物理规律本身。方程并不是一堆离散样本的统计规律它是连续时空上必须满足的强约束。PINN 的核心想法就是把这层约束塞进训练过程。设神经网络的输出为 u(x; θ)把它当作偏微分方程解的候选函数。把 u 代入控制方程得到方程残差 R(x) u_xx(x; θ) π² sin(πx)。如果 u 恰好是精确解那么每个点上的 R 都等于 0。于是训练目标变得非常干净调整网络参数 θ让所有采样点上的残差平方和尽量小——物理规律就这样变成了损失函数而且是免费的、可以无限取样的监督信号。需要注意这里的“监督”不是来自任何标签而是来自方程本身。神经网络在其中扮演的角色也从“从数据里挖规律”变成了“在允许的解函数空间里做优化”。理解这个角色转换比记住任何代码都重要同样一个网络用数据标签驱动它是传统的拟合器用方程残差驱动它就是求解器。1.2 损失函数里通常装着哪几项虽然方程残差是 PINN 的招牌但完整的问题还需要边界条件来约束网络否则满足方程的解有无穷多个。拿这个例子来说任意 u(x) sin(πx) C₁x C₂ 都有相同的二阶导数 -π²sin(πx)只有加上两端为零的边界条件才唯一确定出 C₁C₂0也就是 u(x) sin(πx)。所以损失函数通常是几项残差的加权和残差项数学形式需要的数据作用方程残差(1/N_pde) Σ R(xᵢ)²只需配点坐标强制解满足控制方程边界残差(1/N_bc) Σ (u(x_b;θ)-g_b)²边界取值强制满足边界条件初值残差(1/N₀) Σ (u(x,t₀;θ)-u₀)²初始分布含时间的问题必须加数据残差(1/N_data) Σ (u(x_d;θ)-u_obs)²观测数据可选用来同化实测信息需要说明的是数据残差项在纯正问题forward problem里可加可不加。如果你在训练之外拿到了少量观测值把它加进来能明显改善解的局部精度如果没有就只靠方程和边界这两项。这也是 PINN 最有魅力的地方——它可以在没有任何标记数据的情况下直接求解偏微分方程这也是“物理信息”四个字的真正含义。1.3 为什么拿一维泊松方程当第一个例子选例子不是越复杂越好。我第一次上手时贪心直接去复现带激波的 Burgers 方程结果连续一周都在跟不收敛斗争连 PINN 的基本框架都没看清。后来退回到一维泊松方程两天就把所有流程走通了。这个选择背后有四个理由值得你现在就记住第一它有解析解 u(x) sin(πx)可以严格量化误差而不是靠“看起来差不多”主观判断。第二方程简单到可以手推二阶导方便你在早期验证自动微分算出来的结果是否正确。第三解是光滑、低频的不会触发神经网络常见的谱偏置问题——高频成分才是 PINN 真正头疼的地方入门阶段没必要主动撞上去。第四一维问题只需要几百到上千个配点普通笔记本电脑的 CPU 几分钟就能跑完容错空间大可以放心做实验。物理图景也直观一根两端固定的弦受到横向分布力 f(x) π²sin(πx) 的作用平衡形态就是 sin(πx)。后面分析结果时你可以随时用这根弦的直觉来判断网络输出是不是物理上合理。2. 数学准备把泊松方程变成最小二乘问题2.1 方程与解析解先用手算一遍正式写代码前先把方程和解析解完整算一遍。模型方程为u(x) -π² sin(πx)x ∈ [0, 1]u(0) 0u(1) 0对右侧积分一次得到 u(x) π cos(πx) C₁再积分一次得到 u(x) sin(πx) C₁x C₂。代入边界条件u(0) 0 推出 C₂ 0u(1) 0 推出 sin(π) C₁ 0也就是 C₁ 0。最终解析解就是 u(x) sin(πx)。别小看这一步手算。它除了给出误差对比的基准还有一个实际用途后面网络训练完你可以把网络输出 u 的数值解和 sin(πx) 直接对照也可以把网络输出的二阶导数值和 -π²sin(πx) 对照——如果自动微分的梯度计算有问题这个对照能帮你很快定位而不是对着莫名奇妙的结果瞎猜。2.2 残差形式与损失函数写法对任意候选解 û定义残差R(x) û(x) π² sin(πx)精确解代入R 恒等于 0。PINN 做的事情就是找到参数 θ让 R 在尽可能多的点上逼近 0同时让网络在边界上满足约束。把网络记为 u(x; θ)整体损失写成L(θ) (1/N_pde) Σᵢ [u_xx(xᵢ; θ) π² sin(πxᵢ)]² λ_bc · (1/N_bc) Σⱼ [u(x_j; θ) - 0]²其中第一项是方程残差的最小二乘第二项是边界残差λ_bc 是边界权重。如果懂一点传统数值方法你会认出这就是配点法collocation method的最小二乘形式。有限元把解空间设成分片多项式、在网格节点上离散PINN 把解空间换成神经网络参数族、在随机采样点上离散。理解这个联系很重要——看文献时遇到 weighted residual、collocation 这些词你就知道它们说的是同一件事的不同表达。PINN 的创新不在“加权残值”这一数学框架而在“用神经网络做解空间 用自动微分求导”这套工程实现。2.3 二阶导数怎么算自动微分的关键细节神经网络的前向传播本身是一条计算图PyTorch 的 autograd 可以沿图反向求导。一阶导数对任意网络都能直接算但 PINN 的方程残差通常要二阶导数于是需要连续两次调用求导接口u model(x_pde) u_x torch.autograd.grad( u, x_pde, grad_outputstorch.ones_like(u), create_graphTrue, retain_graphTrue )[0] u_xx torch.autograd.grad( u_x, x_pde, grad_outputstorch.ones_like(u_x), create_graphTrue )[0]这里两个参数是整个 PINN 工程里最容易被忽略的坑。create_graphTrue 表示在求导的同时把这次求导的过程也记进计算图——只有这样u_xx 才能继续对网络参数 θ 求梯度否则你会拿到一个“数值正确但无法反向传播”的张量训练直接报错。第一个 grad 调用还要加 retain_graphTrue因为接下来还要在同一条计算图上再做一次求导默认情况下图会在第一次求导后被释放。提示如果你在训练中看到类似 “RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn” 的报错优先检查的就是 create_graph 有没有开。这是我见过最高频的 PINN 新手报错没有之一。3. 配点采样与损失权重决定训练成败的两个隐藏细节3.1 内部配点随机采样、拉丁超立方与配点数量方程残差是在一组离散点上计算的这些点叫配点collocation points。一维问题最简单的方式是均匀网格但 PINN 的定位是“无网格方法”随机采样更符合它的精神而且在高维问题里网格点会快速爆炸随机采样则几乎不随维度增加而增加复杂度。最朴素的做法是直接用 torch.rand() 在 [0, 1] 里均匀随机采样。对一维问题完全够用代码也最干净。如果想稍微讲究一点可以用拉丁超立方采样LHS它在低维下能保证覆盖更均匀from scipy.stats import qmc sampler qmc.LatinHypercube(d1) x_pde sampler.random(n1000) # 形状 (1000, 1)取值 [0, 1)配点数量怎么定我自己的经验是一维问题 1000 个起步就够二维问题 5000 到 10000 个更高维另说。数量太多会让每次迭代变慢而且边界约束更难被满足数量太少则残差采样不足解会出现局部波浪。有一个直觉值得记住配点就像测量弦有没有弯曲的探针探针太少弯得再离谱你也测不出来。还需要注意输入尺度。方程里所有变量都定义在 [0, 1]网络训练得很顺。如果你的物理问题定义在比如 [0, 10⁶] 这种大尺度范围务必先做归一化把输入缩放到 [-1, 1] 或 [0, 1]。神经网络对输入尺度极其敏感大数值会让权重初始化失效、激活函数饱和训练一上来就崩。这个步骤和图像处理里归一化像素不是一个性质它是 PINN 能否收敛的前提。3.2 边界条件的两条路软惩罚与硬乘子处理边界条件有两条主流路线。第一条是软约束soft constraint把边界残差作为损失项加进总损失我们前面写的损失函数就是这种。它的优点是通用任何复杂的边界都能写成 loss 项缺点是引入了 λ_bc 这个需要手工调的权重而且边界只能被“逼近”不能被严格满足。第二条是硬约束hard constraint通过构造网络输出结构让边界条件在数学上恒成立。对于本例这种零边界可以令u(x) x(1-x) · N(x; θ)不管 N 输出什么x(1-x) 这个乘子都保证 u(0)u(1)0。这样一来损失函数里完全没有边界项只剩方程残差少调一个参数边界误差也直接归零。我的建议是入门阶段先跑通软约束因为实际项目的边界往往是不规则曲面很难写出这种直观的乘子函数但硬约束这个技巧一定要了解它在你被 λ_bc 折磨时是一条很有效的退路。真实项目里也常混用容易参数化的边界用硬乘子复杂区域用软惩罚。3.3 权重 λ_bc 为什么不能随便取 1这是 PINN 调参里最玄学、但也最有规律的一个问题。想象训练开始时内部 1000 个配点的方程残差都在“努力”把解塑造成 sin(πx) 的形状而边界只有 2 个点负责把端点摁在 0 上。两边虽然各自的 loss 都是平均后的均方误差但梯度尺度完全不同1000 个内部点的合力远比 2 个边界点的提拉力大。如果 λ_bc 取 1边界很快就会被内部方程“带跑”端点慢慢离开 0形成一个整体偏置的“假解”。经验上λ_bc 从 10 到 100 起步跑完一轮训练后检查一下端点的预测值如果偏离 0 超过 1e-3就把权重往上调。也不要盲目加到 1e6——权重过大时网络会优先保边界、牺牲内部方程结果边界极其精确但内部残差下不去。我通常在 10 到 100 之间就能找到平衡点。更科学的做法是自适应权重每轮训练时统计各项残差的梯度范数然后动态调整权重让梯度量级接近。这类方法在文献里叫 self-adaptive PINN实现也不复杂但入门阶段没必要一上来就上固定权重足够跑通第一个例子。4. 从零写代码PyTorch 实现逐段拆解4.1 网络结构层数、激活函数、初始化网络结构我用最简单的多层感知机MLP层数是 [1, 32, 32, 32, 1]输入一个坐标 x输出一个解值 u。三层的宽度 32 对一维光滑解来说容量完全够用。盲目加宽加深收益很小反而会让训练变慢、收敛变难——解函数只有 sin(πx) 这一个模态用不着大炮打蚊子。激活函数选 tanh这是 PINN 里最稳妥的选择原因值得强调方程残差里需要二阶导数而 ReLU 的二阶导数几乎处处为零。用 ReLU 做激活的网络输出是分片线性函数u 恒等于 0于是方程残差 R(x) 永远等于 π²sin(πx)损失函数会卡死在一个下不去的平台——这个平台值你甚至能提前算出来大约 48.7。如果看到损失停在几十这个量级死活不动第一反应就应该是激活函数用错了。初始化我用 Xavier 正态初始化它对 tanh 这类激活函数很友好能避免初始时神经元全部饱和。随机种子也需要固定PINN 的训练对初始化很敏感不同种子下最终误差可能差一个数量级。这不是玄学是多峰损失空间的正常现象——后面验证代码时多跑几个种子、看中位数而不是单次结果才是更可靠的评估方式。4.2 损失函数与自动微分的完整实现把前面的推导落到代码上整个网络和损失函数可以这样写import numpy as np import torch import torch.nn as nn torch.manual_seed(42) class PINN(nn.Module): def __init__(self, layers(1, 32, 32, 32, 1)): super().__init__() self.activation nn.Tanh() self.linears nn.ModuleList() for i in range(len(layers) - 1): linear nn.Linear(layers[i], layers[i 1]) nn.init.xavier_normal_(linear.weight) nn.init.zeros_(linear.bias) self.linears.append(linear) def forward(self, x): for i, linear in enumerate(self.linears): x linear(x) if i len(self.linears) - 1: x self.activation(x) return x def physics_loss(model, x_pde, x_bc, u_bc): u model(x_pde) u_x torch.autograd.grad( u, x_pde, grad_outputstorch.ones_like(u), create_graphTrue, retain_graphTrue )[0] u_xx torch.autograd.grad( u_x, x_pde, grad_outputstorch.ones_like(u_x), create_graphTrue )[0] residual u_xx (np.pi ** 2) * torch.sin(np.pi * x_pde) loss_pde torch.mean(residual ** 2) u_bc_pred model(x_bc) loss_bc torch.mean((u_bc_pred - u_bc) ** 2) return loss_pde, loss_bc注意 x_pde 需要在采样之后设置 requires_grad_(True)这样 model(x_pde) 的输出才会带有计算图autograd 的两次求导才有意义。如果你在同一个损失函数里反复调用 model(x_pde)每次前向都会重新建图所以把 requires_grad 设置在采样阶段、而不是损失函数内部逻辑会更清晰。4.3 训练流程为什么是 Adam 预热加 L-BFGS 收尾训练部分我强烈推荐两阶段策略先用 Adam 跑两三千步把损失快速压到较低水平再切换到 L-BFGS 精修。原因很实际Adam 对初值不敏感、稳定性好但它是一阶方法收敛精度有限L-BFGS 是拟牛顿法利用了二阶信息在光滑损失面上收敛深度明显更好缺点是对初始点敏感、容易在训练初期就走歪。两者配合正好互补。需要特别说明的是L-BFGS 能用有一个前提配点在训练过程中必须固定。如果每个 batch 都重新采样配点每次迭代的损失函数本身都在变L-BFGS 内部的近似 Hessian 记忆就全部失效了。所以我的做法是训练开始前一次性采样好配点整个训练过程中不再变动。Adam 阶段你也可以这样做用全批量训练保证后面切换算法时语义一致。L-BFGS 还有一个代码上的特殊要求它需要反复调用同一个“损失函数”去计算损失和梯度因此必须用闭包closure模式。闭包里要包住 zero_grad、前向、反向三步每次调用都完整重算一次。不理解这个模式也没关系照着模板写就行但千万别把打印语句塞进闭包——闭包在一次迭代里会被调用很多次打印会把终端刷爆。4.4 完整可运行代码与运行环境下面是完整的训练脚本包括采样、两阶段训练和最终误差评估def train_adam(model, x_pde, x_bc, u_bc, epochs3000): optimizer torch.optim.Adam(model.parameters(), lr1e-3) scheduler torch.optim.lr_scheduler.CosineAnnealingLR( optimizer, T_maxepochs, eta_min1e-5 ) for epoch in range(epochs): optimizer.zero_grad() loss_pde, loss_bc physics_loss(model, x_pde, x_bc, u_bc) loss loss_pde 10.0 * loss_bc loss.backward() optimizer.step() scheduler.step() if (epoch 1) % 500 0: print(fAdam epoch {epoch1:5d} | fPDE {loss_pde.item():.3e} | BC {loss_bc.item():.3e}) def train_lbfgs(model, x_pde, x_bc, u_bc, steps500): optimizer torch.optim.LBFGS( model.parameters(), lr0.1, history_size50 ) for step in range(steps): def closure(): optimizer.zero_grad() loss_pde, loss_bc physics_loss(model, x_pde, x_bc, u_bc) loss loss_pde 10.0 * loss_bc loss.backward() return loss loss optimizer.step(closure) if (step 1) % 50 0: loss_pde, loss_bc physics_loss(model, x_pde, x_bc, u_bc) print(fL-BFGS step {step1:5d} | ftotal {loss.item():.3e} | PDE {loss_pde.item():.3e} | fBC {loss_bc.item():.3e}) x_pde torch.rand(1000, 1) x_pde.requires_grad_(True) x_bc torch.tensor([[0.0], [1.0]]) u_bc torch.zeros(2, 1) model PINN() train_adam(model, x_pde, x_bc, u_bc) train_lbfgs(model, x_pde, x_bc, u_bc) x_eval torch.linspace(0, 1, 101).view(-1, 1) u_pred model(x_eval).detach().numpy().ravel() u_exact np.sin(np.pi * x_eval.numpy()).ravel() l2_rel np.linalg.norm(u_pred - u_exact) / np.linalg.norm(u_exact) print(fL2 relative error: {l2_rel:.3e})这段代码在一台普通笔记本电脑的 CPU 上运行总共大约 3 到 5 分钟就能训完。全程用不到 GPU因为 1000 个配点和四层小网络的规模实在太小GPU 反而要浪费时间在数据搬运上。如果你手头只有 GPU 环境也完全可以直接跑结果不变。5. 结果分析损失曲线、解曲线与误差量化5.1 损失曲线怎么看正常收敛该长什么样训练跑完第一步不是看解曲线而是看损失曲线。把每轮记录下来的 PDE loss 和 BC loss 用对数坐标画出来你会看到一个典型的收敛形态Adam 阶段PDE loss 从初始的 10⁻¹ 量级快速降到 10⁻⁴ 到 10⁻⁵BC loss 同步下降但幅度略小切换到 L-BFGS 后两者继续被压到 10⁻⁶ 甚至 10⁻⁷。这个数量级的变化是判断 PINN 是否正常收敛的重要参考。如果 PDE loss 在某个平台停住比如一直卡在几十大概率是激活函数选错ReLU 问题如果 BC loss 降不下去而 PDE loss 很好大概率是边界权重偏小如果两个都降得很快但解曲线仍然很差问题多半出在配点数量或初始化上。损失绝对值本身不是最终目标——它反映的是“方程满足程度”而不是“解误差程度”这一点下一节会细说。还有一个小技巧把训练后所有配点上的残差 R(x) 画出来而不是只看均值。正常的残差分布应该围绕 0 随机波动像噪声一样如果残差呈现明显的系统性形状比如一侧全是正的、另一侧全是负的说明解的整体形态有偏网络在用一个错误的函数形状去硬凑方程。这个检查比损失数字更早暴露问题。5.2 预测解与解析解对比关注边界和峰值第二步是把网络输出的解曲线画出来和解析解 sin(πx) 叠在一起看。评估时不要用训练配点而要用更密的均匀网格——我用的是 linspace(0, 1, 101)也就是 101 个均匀点。这很重要训练配点是随机采样点只能告诉你那些位置上的误差两个配点之间解可能已经大幅偏离只有密集网格才能看清全貌。正常结果应该长这样解曲线总体和 sin(πx) 完全重合肉眼看不出差别但如果把局部误差放大你会看到误差最大的位置通常出现在两个端点附近而不是中间峰值处。原因是软边界条件只能让端点近似等于 0而不是严格等于 0训练结束时端点仍会残留 10⁻³ 到 10⁻⁴ 量级的偏移。这不是 bug是软约束的固有代价理解了这一点你就不会对着图表怀疑自己写错了。还可以顺便检查网络输出的二阶导把 model 输出的二阶导 u_xx 画出来对比 -π²sin(πx)。如果两者重合良好说明自动微分链路正确也说明网络确实学到了“物理”而不是只做了个表面拟合。这一步对验证高阶导数尤其有价值因为在更复杂的方程里导数才是物理方程真正约束的东西。5.3 误差量化L2 相对误差与最大误差画图只能定性最终评估要定量。工程中最常用的两个指标是 L2 相对误差和最大绝对误差x_eval torch.linspace(0, 1, 101).view(-1, 1) u_pred model(x_eval).detach().numpy().ravel() u_exact np.sin(np.pi * x_eval.numpy()).ravel() l2_rel np.linalg.norm(u_pred - u_exact) / np.linalg.norm(u_exact) max_abs np.max(np.abs(u_pred - u_exact)) print(fL2 relative error: {l2_rel:.3e}) print(fMax absolute error: {max_abs:.3e})用前面的设置完整跑一遍L2 相对误差通常在 10⁻³ 到 10⁻⁴ 之间最大绝对误差在 10⁻³ 量级。这里有一个重要的经验值要记住损失值达到 10⁻⁶ 并不等于解误差是 10⁻⁶。方程残差和解误差之间隔着算子的稳定性条件二阶导算子会把残差放大。从我的实测看PDE loss 到 10⁻⁶ 时L2 相对误差通常在 10⁻⁴ 左右——所以任何宣称“损失 1e-6 所以解误差 1e-6”的说法都要打个问号误差必须单独算不能拿损失代替。判定标准也给你一个参考一维光滑解配点 1000 个L2 相对误差压进 10⁻³ 算正常10⁻⁴ 算不错。如果某个随机种子下误差到了 10⁻²先别急着调参换一个种子重新跑——多跑几个 seed 取中位数才是对模型能力的正确估计。6. 踩坑记录与后续路线从一维例子走向实际问题6.1 高频踩坑清单现象、原因、解决这一节我把自己实际踩过的、以及带人复现时反复出现的坑整理成一张表可以当排查手册用现象根本原因解决方法损失卡在高位几十量级降不动用了 ReLU 激活二阶导恒为零换 tanh 或 sin 激活报错 requires_grad / grad_fn 缺失autograd.grad 缺 create_graphTrue两处求导都加上 create_graphTrue解曲线整体好但端点翘起边界权重 λ_bc 太小从 10 提到 100或改成硬约束乘子训练很慢、不收敛输入尺度没归一化先把物理范围缩放到 [0,1] 或 [-1,1]配点上看着准配点之间全乱只在配点评估没有密集网格复检用 linspace 做 101 点以上评估每次运行结果差距巨大初始化敏感 / 未固定随机种子固定 seed多次运行取中位数其中第一行是新手最容易踩的一个看似正常的网络用 ReLU 激活训练 5000 步损失纹丝不动地停在几十换成 tanh 之后一两百步就开始下降。这个对比碰一次就忘不了。还有一个普遍误区是过早停止。把 Adam 的 3000 步当成完整的训练不看后续的 L-BFGS 阶段——你会发现 loss 停在 10⁻⁴ 上不去误以为代码有问题实际上只是优化器精度不够。L-BFGS 的几百步通常能把损失再压两个数量级这是 PINN 流水线里性价比最高的一段。6.2 PINN 的适用边界什么时候别用它跑通一个例子之后最容易产生的错觉是“PINN 能解决所有偏微分方程”。诚实地讲至少在现阶段它和传统数值方法的关系是互补而不是替代。我的判断标准是这样一张表场景PINN 的表现建议规则区域的线性稳态方程精度通常不如有限元还更慢优先用传统求解器高维问题维度大于等于 10免网格优势明显配点数不爆炸适合尝试 PINN逆问题、参数辨识天然优势方程中的未知参数可以直接当可学习变量强烈推荐 PINN需要 1e-8 级精度的工程计算PINN 很难稳定达到传统方法更可靠强间断、激波问题朴素 PINN 很难收敛需要特殊技巧域分解、自适应权重等我的经验是判断是否用 PINN先问两个问题——有没有已知的、廉价可获得的物理规律有没有复杂到网格难生成、数据难标注的场景两个都答“是”PINN 才有真正的用武之地否则只是给自己的工作增加难度。6.3 接下来可以尝试的四个方向如果一维泊松方程已经跑顺了下一步可以按难度逐步升级。第一个方向是加时间维把 (x, t) 作为二维输入训练含时间的热传导方程或波动方程同时要加上初值残差。这和一维例子的区别主要是输入多了一个维度流程完全一样适合用来理解 PINN 怎么处理时间演化。第二个方向是 Burgers 方程这是 PINN 考察演技的标准场。它的解会出现激波间断朴素实现很容易在间断附近产生严重误差你会亲身体会到谱偏置和配点密度带来的影响也会理解为什么文献里要引入 Fourier 特征和域分解。第三个方向是逆问题把方程里的未知系数比如热扩散率设为可学习参数让网络在拟合解的同时反推系数这是 PINN 目前最有实际价值的方向之一。第四个方向是换更“懂”物理的激活和架构比如 Siren用正弦激活 专门初始化或 Fourier 特征映射。一维例子用不上它们但当你遇到高频振荡解时这两个是立竿见影的修改方向。每个方向都能从本文的框架延伸出去区别只在损失函数多一两项、输入多一两个维度、网络架构换一两种组件。对我个人而言跑通这个一维例子最大的收获是彻底扭转了对“训练神经网络”的理解神经网络在这里不是工具而是解函数的参数化模型训练过程其实是数值求解一个优化问题而物理规律就是最便宜的监督信号。最后分享一个我一直在用的习惯每次跑新 PINN 之前先做一步“残差自查”——用随机初始化的网络算一次方程残差的分布如果残差的尺度和解析解的导数尺度差了好几个数量级先做输入输出归一化再谈训练。这个小步骤帮我省掉了大量无效调参希望你也能用得上。
延伸阅读

更多相关文章

2026/10/20 8:56:57

分布式存储在大数据场景下的落地实践:选型、调优与排错

1. 为什么大数据场景绕不开分布式存储:从单机瓶颈说起先讲一个我早年遇到的真实场景。当时接手了一个用户行为分析项目,数据量其实不算夸张,每天新增日志大约200GB,但要保留180天,算下来总规模差不多36TB。团队里有人觉…

2026/10/20 8:51:55

OpenClaw 浏览器自动化:视觉语义双通道与 Skills 体系落地实践

1. 浏览器自动化的核心痛点与 OpenClaw 的破局思路浏览器自动化这个领域,说起来历史不短了。从最早的 Selenium 到后来的 Puppeteer、Playwright,工具链一直在进化。但真正做过生产级自动化的人都知道,这些工具虽然强大,却有一个共…

2026/10/20 8:51:55

联邦大模型训练实战:FATE-LLM架构解析与性能调优

1. 联邦学习撞上大模型:为什么这两件事必须凑到一起联邦学习这个概念提出来有些年头了,核心思路一句话就能说清楚:数据不动,模型动。各个参与方在本地用自己的数据训练,只把梯度或者模型参数传到中心做聚合&#xff0c…

2026/10/20 11:28:21

Superpowers 技能体系详解:自动触发 + 工程纪律

一、Superpowers 是什么 Superpowers 是 obra(Jesse Vincent)开源的一套「软件开发方法论 可组合技能库」,专为编码 agent 设计。它的核心机制不是让你手动敲 /命令,而是: 会话一开始就生效,靠技能自身的…

2026/10/20 11:28:21

漏洞复现和渗透测试是一回事吗?90%的新手都搞混了

关键词:漏洞复现、渗透测试、PoC、EXP、零基础入门、学习路径、红队、蓝队、Web安全 本文为安全学习与授权测试用途,所有操作均在本地环境完成,请勿用于未授权目标。0. 一个被90%新手搞混的问题 很多人学安全,一上来就说"我要…

2026/10/20 11:28:21

鱼香ros2(二十)launch脚本

一、launch脚本启动多个节点1、为什么使用launch脚本?通常我们需要三个节点的时候,要使用三个终端,不能同一个终端使用,关闭也需要分别去CtrlC,这样比较麻烦。因此我们可以将三个启动节点的代码合成一个文件里&#xf…

2026/10/20 11:28:21

XX基金-表DDL夯

问题现象1.表 TAMLBLACKLIST 被一个状态为 KILLED 的会话(SID1142, SERIAL#10191)锁住。 2.已尝试 ALTER SYSTEM KILL SESSION 和 DISCONNECT SESSION,均报错 ORA-00031: 标记要终止的会话。 3.由于该会话锁表,导致对该表的任何DD…

2026/10/20 11:23:21

NAS 共享打不开?Win11 两类 SMB 报错排查

文章目录 📖 介绍 📖 🖥️ 演示环境 🖥️ 📒 两类报错各自对应一条策略,先分清再动手 📒 🔍 两类报错长什么样 📋 升级之后到底变了什么 🩺 先确认客户端当前是什么状态 🎯 首选方案:改 NAS 那头 🛟 NAS 动不了时的客户端兜底 🔗 还有一条容易被忽略…

2026/10/19 18:06:35

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/20 1:05:37

单片机C运行时从启动到多任务:中断安全与共享资源冲突

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:05:37

GCC编译四步详解:预处理、编译、汇编、链接实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:05:37

FIW线是什么?FEP注塑线与普通氟塑料线的区别及应用指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑