资讯中心

物理信息神经网络求解温度场:从热传导方程到PINN实战

📅 2026/11/3 4:38:52
物理信息神经网络求解温度场:从热传导方程到PINN实战
简介面向物理信息神经网络PINN入门学习者这套资源仅用单文件PyTorch代码不依赖外部数据即可求解一维热传导方程并输出温度场。实现将PDE残差、初始条件与边界条件直接编码进损失函数通过自动微分训练网络典型轮次下与解析解的L2相对误差可稳定收敛至5e-4以下。压缩包共14个文件以Python脚本环境检查与主程序、txt说明、markdown教程、bat/sh一键运行脚本及PNG结果图为主整体容量仅512KB结构清晰易上手。已有79人学习下载适合想绕开旧版TensorFlow教程、用纯PyTorch理解PINN原理的初学者。除完整可运行代码外还提供中文入门指南、与原版论文实现的对比说明、训练日志与预测结果npz方便对照理论细节、调参复现并观察动态热图与误差曲线。1. 只给一个方程神经网络真能算出温度场——先说结论再拆门道只给神经网络一个热传导方程不画网格、不写有限元矩阵它能算出温度场吗答案是能而且在一维、二维稳态问题上能做到和传统数值解同一量级的精度。这个思路的关键一步是把方程从“待求解的对象”变成“约束网络行为的损失函数”——网络不需要见过大量仿真数据也不需要查表它靠的是对一个数学表达式反复求导和优化。这类方法在业内叫物理信息神经网络PINN本质上是在解一个带物理强约束的最优化问题。它特别适合两类场景一是边界条件或热源参数需要反复变化的设计阶段二是传统网格在高维或复杂几何区域上很吃力的场合。但“只给一个方程”这句话听着轻巧背后还藏着边界条件、初值、权重设置和优化策略这些才是温度场解得好不好的真正分水岭。2. 把热传导方程塞进损失函数前馈神经网络解温度场的最小实现2.1 为什么是前馈网络而不是卷积网络先回答一个很多人会问的问题温度场不是可以当图像处理吗为什么不用卷积神经网络卷积神经网络擅长处理规则网格上的图像信号它的卷积核天然利用了“相邻像素有空间相关性”这个先验。可我们这里要解的是连续坐标上的温度函数输入是坐标 x输出是温度 T输入输出都是低维稠密向量。把区域离散成图像再做卷积等于先把问题离散化再把离散解当成图像去回归绕了一圈还丢了物理约束。用前馈神经网络MLP做底座是最自然的做法坐标直接进网络自动微分算梯度PDE 残差写成损失项整个过程不需要任何网格化操作。如果你之前接触过不同的神经网络比如卷积、Transformer到这里可以先把它们放下。方程约束下的求解任务MLP 的“万能逼近”能力加上自动微分已经足够支撑一维到三维的温度场而且参数量比卷积网络小一个数量级训练起来也快得多。2.2 最小可跑代码一维含内热源的稳态温度场用一个最简单的例子把整个流程立起来一维稳态导热长度为 1两侧边界温度固定为 0内部有均匀内热源。控制方程和边界条件是方程d²T/dx² -Q/k这里把源项归一化成 1边界T(0)0T(1)0解析解用来后面验证T(x) x(1-x)/2网络做的事情很简单输入 x输出 T(x)。损失函数由两部分组成一个是区域内部的 PDE 残差平方和一个是边界点的温度偏差平方和。import torch import torch.nn as nn import numpy as np seed 42 torch.manual_seed(seed) np.random.seed(seed) # 前馈网络输入坐标 x输出温度 T class Net(nn.Module): def __init__(self, hidden20, layers3): super().__init__() self.input_layer nn.Linear(1, hidden) self.hidden_layers nn.ModuleList( [nn.Linear(hidden, hidden) for _ in range(layers - 1)] ) self.output_layer nn.Linear(hidden, 1) # 初始化对 PINN 的收敛影响很大用 xavier 控制初始梯度量级 nn.init.xavier_uniform_(self.input_layer.weight) for h in self.hidden_layers: nn.init.xavier_uniform_(h.weight) nn.init.xavier_uniform_(self.output_layer.weight) def forward(self, x): x torch.tanh(self.input_layer(x)) for h in self.hidden_layers: x torch.tanh(h(x)) return self.output_layer(x) # 输出层不加激活 # 区域内 PDE 残差d2T/dx^2 1理论值应为 0 def pde_residual(model, x): x x.clone().requires_grad_(True) T model(x) dT torch.autograd.grad(T, x, grad_outputstorch.ones_like(T), create_graphTrue)[0] d2T torch.autograd.grad(dT, x, grad_outputstorch.ones_like(dT), create_graphTrue)[0] return d2T 1.0 # 边界条件T(0)T(1)0 def bc_loss(model): x_bc torch.tensor([[0.0], [1.0]]) T0 model(x_bc) return torch.mean(T0**2) model Net() optimizer torch.optim.Adam(model.parameters(), lr1e-3) n_in 256 # 区域内部采样点数 lambda_bc 50.0 # 边界条件的权重 for step in range(8000): optimizer.zero_grad() x_in torch.rand(n_in, 1) # 每次迭代重新采样 (0,1) 内部点 pde torch.mean(pde_residual(model, x_in)**2) bc bc_loss(model) loss pde lambda_bc * bc loss.backward() optimizer.step() if step % 2000 0: print(fstep {step}: pde{pde.item():.3e}, bc{bc.item():.3e})这里有几个参数值得解释。hidden20, layers3表示网络有 1 个输入层、3 个隐藏层、1 个输出层每层 20 个神经元这个规模对一维问题足够了。内部采样点数n_in256每次迭代都重新随机采等于训练过程中不断“刷新”约束点比固定一批点不容易过拟合。lambda_bc50.0是因为边界上只有 2 个点内部有 256 个点如果不对边界加权边界约束在 MSE 平均后被内部残差稀释边界就锁不住。8000 步 Adam 在这个规模下跑几十秒loss 能稳步下降。提示用torch.autograd.grad求二阶导时两次都要加create_graphTrue否则计算图被截断loss.backward()时梯度传不回模型参数网络压根不更新。2.3 损失函数的三块拼图PDE 残差、边界条件和初值上面的损失函数只有 PDE 残差和边界条件两块因为稳态问题没有初值。换成瞬态热传导方程 ∂T/∂t α ∂²T/∂x²就必须补第三块在 t0 时刻采样一组点约束 T(x,0)T0(x)把初值偏差的平方和也加进 loss。实际工程里我一般把损失写成这种形式loss λ_pde * MSE_pde λ_bc * MSE_bc λ_ic * MSE_icλ 是每个约束的权重也是后面最需要调的参数。很多人第一次跑 PINN以为照抄论文里的权重就行其实不同问题里 PDE 残差和边界偏差的量级可以差出几个数量级权重不匹配就会出现“温度内部正确但边界没贴住”或者反过来“边界贴得死但内部全歪”的怪相。这部分会在第 3 章展开。为什么要用方程约束而不是直接拿数据回归因为训练数据通常只有边界上的几个点内部完全靠方程本身提供监督信号。这正是“只给神经网络一个方程”的实质方程把物理规律变成无限多个约束点网络在满足这些约束的同时插值出整体温度场。想通这一点后面所有参数调整都好解释了。3. 让它真的收敛的四个关键参数网络宽度、激活函数、采样策略与损失权重3.1 激活函数先选 tanhReLU 的二阶导是零如果直接照搬图像分类常用的 ReLU温度场大概率训练不出正常形状。原因很直接我们的 PDE 残差里要求网络的输出对输入求二阶导而 ReLU 的二阶导处处为 0。也就是说PDE 残差项对网络参数来说根本没有梯度等于这个约束完全失效网络只能靠边界损失在学内部温度场自然是一塌糊涂。可以用一小段代码验证这个结论x torch.linspace(-1, 1, 10, requires_gradTrue) relu torch.relu(x) d1 torch.autograd.grad(relu, x, grad_outputstorch.ones_like(x), create_graphTrue)[0] d2 torch.autograd.grad(d1, x, grad_outputstorch.ones_like(d1), create_graphTrue)[0] print(d2) # 全 0tanh 是 PINN 里用得最广的激活函数因为它光滑、有界、二阶导能刻画曲率而且梯度不容易爆炸。像 sin 这类激活在周期性强的问题里也有奇效但对温度场这种偏平滑的解tanh 足够稳。经验上我遇到的温度场 PINN 案例里坦率说 90% 用 tanh 都能收。3.2 隐藏层宽度与深度别把网络堆太深网络不是越深越好。PINN 训练本身是个多目标优化几个约束互相竞争网络一旦太深自由度太高更容易出现局部振荡解——loss 看着低温度场却在高频抖动。我一般先按下面的表起步再根据结果微调参数推荐起步值说明隐藏层数31~2 层表达能力不足5 层以上训练困难和过拟合风险同时上升每层神经元20~50一维问题 20 足够二维问题用 40~50三维再加总参数规模1e3~1e4温度场本身平滑不需要百万参数的大网络如果验证发现解太粗糙先加宽度而不是加深度如果出现局部振荡优先减深度和调采样点而不是盲目加层。网络结构这块的翻车率其实不高真正让人头疼的是后面两个参数。3.3 采样策略内部采样点的数量和分布内部采样点就是给网络“上课”的题题出得不好网络再聪明也白搭。纯随机torch.rand在低维问题里够用但在高维或区域形状复杂时容易留下空洞。改进方向有两个一是用低差异序列代替纯随机比如 Sobol 序列或拉丁超立方采样让点分布更均匀二是做自适应采样训练到中途算一遍残差在残差绝对值大的区域补采点。自适应采样的思路很朴素残差大的地方就是网络还没学会的地方把训练火力集中过去。常见做法是每 2000 步暂停一次在区域里随机撒一批候选点算每个点的 PDE 残差绝对值取最大的那 10%~20% 并入训练集继续训练。这个技巧对温度场这种解比较平滑的问题收益不一定巨大但对手上有现成代码的人是成本最低的精度提升手段。3.4 损失权重量级纪律这是温度场 PINN 最容易翻车的地方。边界点的温度可能是 0.1 的量级内部 PDE 残差可能是几十的量级如果不做权重处理网络做梯度下降时会优先把大的残差压小边界偏差则被容忍。解决思路不是去改网络而是先看量级。第一步跑 500 步分别打印 pde 和 bc 这两个损失项的数字观察谁大谁小第二步按“大损失乘以小权重小损失乘以大权重”的原则设置 λ。前面例子里的 λ_bc50 就是这么定出来的。注意这个权重不是论文里的某个魔法数字它和你的温度量纲、源项大小强相关。换一个物理参数不同的模型权重很可能要重新试一轮。一个更省事的做法是采用归一化损失把温度 T 和目标边界温度都除以特征温度 T0让输出变成无量纲量同时把 PDE 右侧源项也归一化。这样 pde 和 bc 的量级天然接近权重的敏感度会下降很多。3.5 优化器顺序Adam 热身、L-BFGS 精调PINN 社区里一个不成文的习惯是先用 Adam 把 loss 压到平稳再切换到 L-BFGS 做精调。原因在于 Adam 的梯度估计带动量前期不容易被个别大梯度带偏而 L-BFGS 是拟牛顿法对初始点的梯度平滑度要求高。从随机初始化直接上 L-BFGS很多情况下头几步 loss 就爆了。# Adam 热身 3000 步之后切到 L-BFGS optimizer torch.optim.Adam(model.parameters(), lr1e-3) for step in range(3000): # ... 训练循环和前面一致 ... optimizer.step() optimizer torch.optim.LBFGS(model.parameters(), lr1e-2, max_iter300) def closure(): optimizer.zero_grad() pde torch.mean(pde_residual(model, x_in)**2) bc bc_loss(model) loss pde lambda_bc * bc loss.backward() return loss optimizer.step(closure)L-BFGS 的好处是最后一段收敛很快往往几十步就能把 loss 再压低一个数量级。代价是每次迭代要重算全部采样点的损失内存开销大所以采样点数量不能太大。我一般内部点超过 1000 个就先不切 L-BFGSAdam 多跑一会更划算。4. 用解析解给神经网络“验身”误差怎么算、残差怎么看4.1 有解析解时先做绝对对比前面的一维问题有解析解 T(x)x(1-x)/2这是验证网络结果正确与否的黄金标准。训练完成后要做两层检查第一层是看训练损失有没有降到预期区间第二层是用解析解对比温度和误差单看 loss 的教训后面会讲。# 用 1001 个均匀点做验证防止“抽查”到网络盲区 x_val torch.linspace(0, 1, 1001).reshape(-1, 1) with torch.no_grad(): T_pred model(x_val).reshape(-1) T_ref x_val.reshape(-1) * (1 - x_val.reshape(-1)) / 2.0 l2_rel torch.norm(T_pred - T_ref) / torch.norm(T_ref) max_abs (T_pred - T_ref).abs().max().item() print(f相对 L2 误差: {l2_rel:.2e}, 最大绝对误差: {max_abs:.2e})相对 L2 误差能反映整体偏差最大绝对误差能暴露局部尖峰。一维稳态问题调好权重后相对 L2 误差压到 1e-3 以下很常见二维问题 1e-2 左右也属于正常水平。如果最大绝对误差远大于平均误差大概率温度场某个局部出现了锯齿得回到采样策略去查。4.2 验证点要密防止“自欺”有个典型场景训练时内部采样 256 个点loss 降得很好用这 256 个点算误差也漂亮。但只要把验证点加密到 1001 个温度曲线立刻出现高频波纹。原因不复杂网络在训练采样点之间没有被约束它可以自由振荡而振荡幅度恰好不会在采样点上暴露。所以验证必须避开训练点的分布用一套独立的、更密的网格。一维问题用几百到几千个均匀点二维问题在内部用加密的笛卡尔网格边界上单独撒一圈点。这一步的成本很低但能拦住绝大多数“看着 loss 很低、实际解是坏的”的情况。4.3 只看总 loss 会翻车单独看最大残差总 loss 是 pde 和 bc 的加权和权重一旦失衡两个分项会互相掩盖问题。比如 λ_bc 设得太大loss 曲线里 bc 项占主导pde 项已经恶化到 1e-1 却显示不出来反过来 λ_pde 太大边界悄悄就偏了。我每次训练完都会额外算一个不参与训练的指标区域内残差绝对值的最大值。这是模型在区域内最“不老实”的位置也是对物理方程违反程度的最直观度量。x_res torch.linspace(0, 1, 1001).reshape(-1, 1).clone().requires_grad_(True) T_res model(x_res) dT torch.autograd.grad(T_res, x_res, grad_outputstorch.ones_like(T_res), create_graphTrue)[0] d2T torch.autograd.grad(dT, x_res, grad_outputstorch.ones_like(dT), create_graphTrue)[0] max_res (d2T 1.0).abs().max().item() print(f最大 PDE 残差: {max_res:.2e})残差的最大值和均方根值要同时看。如果最大残差是均值的几十倍说明问题集中在局部多半要补采样点如果两个值差不多大说明网络整体欠拟合该加宽度或训练步数。4.4 没有解析解时网格无关性验证工程里大部分温度场问题没有解析解这时可以与传统数值解交叉验证。常见做法是拿一个成熟的有限差分或有限元代码在同一套边界条件下算三档网格粗、中、细步长按倍数递减比如 0.02、0.01、0.005。把三档解在相同坐标点上的温度差算出来当最细和最粗两档的最大温差小于 1% 时认为数值解已经网格无关拿它当参考解。参考解和神经网络解都在同一组验证点上算误差。这里要注意一个坑神经网络解是连续函数想在哪取值都行有限差分解只在离散节点上定义验证前需要在空间上插值到同一坐标。插值本身会引入误差所以在粗网格上比出来的差异可能包含插值误差这一点在对比时要心里有数。网格无关系数是否足够好取决于工程精度需求温度场分析里 1% 以内通常可接受。5. 温度场 PINN 的 5 个常见坑与排查现象、原因、解决5.1 边界没锁住loss 停在平台期现象训练到 3000 步后总 loss 不再下降边界处的温度始终偏离设定值 0.02~0.05内部曲线形状倒是基本正确。原因边界点只有两个内部点有几百个MSE 平均之后边界约束的占比被稀释。同时边界偏差的量级比内部 PDE 残差小梯度下降自然先照顾大的部分。解决把边界权重 λ_bc 从 1 提高到 50 甚至 100重新训练。更直观的排查办法是训练时把 pde 和 bc 两个损失分开打印看到 bc 比 pde 大出两个数量级就说明边界权重偏低。另一个有效手段是把边界点重复采样比如一组边界点在每次迭代里出现 8 次等效于变相提高边界占比。5.2 换了 ReLU温度场变成“平顶”现象网络输出贴着一个常数附近PDE 残差始终降不下去温度曲线没有弯曲的形状。原因ReLU 的二阶导处处为 0PDE 残差项对网络参数的梯度为 0等于内部约束从未生效网络只靠边界点在“硬填”两个点中间区域自然一片平坦。解决把全部隐藏层激活函数换成 tanh 或 sin重新初始化再训练。这个坑在从图像任务转过来的人身上特别常见因为图像分类里 ReLU 是默认选择顺手就带过来了。判断方法很简单打印一阶导和二阶导二阶导全 0 就换激活函数。5.3 采样太稀疏验证时温度曲线全是锯齿现象训练 loss 正常下降但用加密验证点画出来的温度曲线有高频波纹像是叠加了一层噪声。原因训练采样点之间的区域没有任何约束网络在点数有限的情况下拟合出一个在采样点上准确、在采样点间自由振荡的解。这本质上是 PINN 的插值自由度在“钻空子”。解决把内部采样点从 256 加到 1000 以上或者做自适应采样。我一般会先看最大 PDE 残差如果残差值集中在几个局部区域就在这些区域周围加密采点再续训 2000 步通常锯齿会明显收敛。5.4 换一个随机种子结果差好几个百分点现象同样代码、同样参数只改了随机种子训练出来的温度场误差忽高忽低最大绝对误差可能差 3 倍。原因网络初始化、内部采样点分布、Adam 的随机抽样三处都是随机的。温度场本身是个平滑解理论上对初始化不敏感但 PINN 的多目标损失有多个局部最优随机性会把你推向不同的局部最优。解决固定torch.manual_seed和numpy.random.seed保证问题可复现。工程上更稳妥的做法是同一个配置跑 5 次取温度场的平均值作为最终结果同时记录标准差标准差大说明该配置下模型不稳定需要调权重或采样密度。5.5 L-BFGS 一上来就把 loss 搞爆现象Adam 跑得好好的一换成 L-BFGSloss 直接跳高一个数量级之后很难救回来。原因L-BFGS 是确定性优化器对初始迭代点的曲率估计很敏感。如果初始权重还带着随机性或者 loss 表面有大量小的“毛刺”L-BFGS 会顺着错误的曲率方向走一大步直接翻车。解决先用 Adam 跑 3000 步以上把总 loss 压到 1e-2 以下再切换 L-BFGS。切换时保持网络结构不变只换优化器如果 L-BFGS 仍然不稳定把它的max_iter从 300 降到 50分多次调用每次之间穿插一轮 Adam 校准。记住 L-BFGS 是最后精调的角色不是主力。6. 进阶从一维到二维、瞬态以及一个残差监控技巧6.1 二维稳态温度场的改动其实很小从一维到二维网络结构不用大改只需要把输入从单坐标 x 变成坐标对 (x,y)PDE 残差相应改成二阶偏导的叠加。以二维含内热源稳态问题为例方程是 ∂²T/∂x² ∂²T/∂y² -Q/k残差函数改写成这个样子def pde_residual_2d(model, xy): xy xy.clone().requires_grad_(True) T model(xy) # 输入变成 Nx2 的张量 dT torch.autograd.grad(T, xy, grad_outputstorch.ones_like(T), create_graphTrue)[0] # dT 的形状是 Nx2第 0 列是 dT/dx第 1 列是 dT/dy dTxx torch.autograd.grad(dT[:, 0], xy, grad_outputstorch.ones_like(dT[:, 0]), create_graphTrue)[0][:, 0] dTyy torch.autograd.grad(dT[:, 1], xy, grad_outputstorch.ones_like(dT[:, 1]), create_graphTrue)[0][:, 1] return dTxx dTyy 1.0输入变成二维后内部采样点要在单位正方形里用torch.rand(n, 2)生成边界采样要沿四条边分别撒点。损失结构、权重策略、优化器搭配和第 2、3 章完全一致。这套打法在 Burgers 方程、麦克斯韦方程这类更复杂的 PDE 上也成立区别只是残差表达式的复杂程度。6.2 瞬态温度场把时间当成输入特征瞬态问题 ∂T/∂t α ∂²T/∂x² 也没有推翻前面的框架只需要在网络的输入里多加一维时间 t。输入变成 (x,t)输出仍是 T内部采样在 (x,t) 的时空区域里随机取点损失函数补上初值条件 T(x,0)T0(x) 对应的残差项。这背后的思路和 Neural ODE 里“用神经网络参数化方程”是同一个方向方程不交给数值求解器而是作为约束条件嵌入网络的训练目标。对工程师而言好处是初值、边值任意变化时不需要改求解器只需要重新采样和训练。6.3 一个我每次跑温度场都会看的指标最大残差最后一章留个实用习惯。我训练完不会只记总 loss还会单独算区域内最大 PDE 残差并且给这个指标设一个验收线稳态问题里最大残差至少要小于源项量级的 1e-2我才会认为这个温度场可以交付达不到就回去调权重或加采样点。这个习惯救过我一次。当时一个二维模型总 loss 降到了 1e-5以为收敛了结果最大残差还在 0.1 量级说明网络在某个局部区域严重违反热传导方程只是那个区域恰好没有训练点。后来在那个区域补采点续训温度场的最大绝对误差立刻降了一个数量级。从此我记住一条经验PINN 的 loss 曲线可以骗人残差分布不会。希望这套方法能帮你少走一段弯路把你的温度场问题真正算稳。本文还有配套的精品资源点击获取

看完文章,想为自己的企业也做一次专业网站诊断?

尧图顾问免费为您评估现有网站,并给出建站/改版建议与报价方案。

免费获取方案