
简介面向希望入门物理信息神经网络PINN的 PyTorch 学习者这是一套可直接运行的一维热传导方程求解工具包不依赖外部数据与旧版 TensorFlow仅用一个 Python 脚本即可完成训练、预测与温度场可视化。包内共14个文件、约512KB涵盖主程序、环境检查与一键安装运行的 bat/sh 脚本以及中文说明文档md/txt、预测热图、损失曲线与数值结果png/npz便于零基础用户从环境部署到结果复现全流程操作。已有79人浏览学习。实现严格将热传导方程初始条件与边界条件写入损失函数通过自动微分实时计算偏导数在典型训练轮次下L2 相对误差可收敛至 5e-4 以下。配套 README、论文实现对比说明、运行日志等材料还能帮助读者理解 PINN 原理、拆解代码逻辑并结合可视化输出自主调参开展实验。1. 只给一个方程神经网络真能交出温度场吗先给结论能但远没有你想的那么省事。把热传导方程丢给一个前馈神经网络它确实能在训练结束后输出整个时空区域上的温度分布而且不需要划分网格、不需要组装刚度矩阵。但这里有一个反直觉的真相——方程本身不是给网络看的而是用来构造损失函数的。网络从头到尾不知道自己在解热传导方程它只是在一个由方程残差、边界条件、初始条件拼成的约束场里做函数拟合。做得好它给出的温度场精度接近差分法做得不好它会一本正经地输出一个处处满足边界条件、却在物理上完全说不通的常数场。这篇文章写给两类人一类是传热、流体或结构方向的工程师想用神经网络替掉部分仿真成本另一类是刚接触PINN思路的学生想知道神经网络算温度场的完整链条——损失怎么设计、边界怎么进去、参数怎么调、算完怎么验证。我会按自己实际做这个方向时最常用的一套方案来写不绕开细节也不回避翻车现场。2. 把热传导方程翻译成损失函数最小可跑通的前馈网络2.1 凭什么用神经网络解偏微分方程而不是直接差分传统数值解法的核心是离散化把连续的温度场切成网格把偏导数换成差分商然后解一个巨型线性方程组。网格质量直接决定计算精度边界复杂的几何体光画网格就能占掉一半工期。神经网络走的是另一条路它把解函数当成一个可微分的拟合物输入是空间坐标和时间输出是温度网络权重的数量决定了表达能力的上限。求解过程绕开了网格本质上是把一个偏微分方程求解问题变成了一个优化问题——找到一组权重让网络输出在定义域内尽可能满足热传导方程和边界/初值条件。用这种方式处理温度场有几个实际好处不需要网格天然适合高维或边界不规则的区域解是连续函数后处理不用插值同一套代码改一下损失函数就能换方程比如从热传导方程换成Burgers方程。代价也很明确神经网络求解没有理论上的误差上界你要靠验证兜底训练时间并不比分网格少尤其是二维以上问题。从最经典的例子入手理解整个框架最好——一维热传导方程∂u/∂t α · ∂²u/∂x²定义域是x∈[0,1]t∈[0,1]α是热扩散系数。给定初始温度分布和两端边界条件目标是算出整个区域上任意坐标、任意时刻的温度u(x,t)。这里不需要网格的概念U(x, t)就是网络拟合的对象。输入两个标量输出一个标量中间的全连接层就是前馈神经网络的常规结构。这类思路在文献里的正式称呼是PINN但现在不纠结名字先把损失函数搭起来。2.2 三个损失项偏微分方程残差、边界条件、初始条件网络输出我们记作u_net(x,t)把它代回热传导方程等号两边一定不会严丝合缝差值就是PDE残差loss_pde mean( (∂u_net/∂t - α · ∂²u_net/∂x²)² )只优化这个loss_pde不够因为网络还没被告知边界上温度是多少、初始时刻温度分布是什么样。同一个方程对应无数个解边界和初值才是把问题唯一锁定的条件。所以完整损失函数是三项的加权和loss_total λ_pde · loss_pde λ_bc · loss_bc λ_ic · loss_ic其中loss_bc是网络输出在边界点与给定边界条件的均方误差loss_ic是网络输出在t0时刻与初始温度分布的均方误差。三项缺一不可。只保留PDE残差网络会找到一堆满足方程但完全不匹配物理场景的解只保留初边值网络就把温度场当成一个纯插值问题处理区域内部完全不受方程约束。这里有一个关键认知神经网络不是去解方程而是在找一个同时让三项损失都尽量小的函数。这个函数的拟合过程靠的是自动微分求梯度而不是数值格式。网络对PDE残差求梯度用的是反向传播套二阶导而传统差分的稳定性限制在这里被换成了优化问题的可收敛性——没有CFL条件也不保证收敛。2.3 最小可运行代码十行逻辑跑通一维热传导下面这段代码是完整可训练的最小实现我用PyTorch写没有引入任何第三方物理库。它做的工作是生成训练样本点定义前馈网络用自动微分算损失然后循环训练。import torch import torch.nn as nn import numpy as np alpha 0.1 # 热扩散系数 # 网络输入为 (x, t)输出为温度 u net nn.Sequential( nn.Linear(2, 32), nn.Tanh(), nn.Linear(32, 32), nn.Tanh(), nn.Linear(32, 32), nn.Tanh(), nn.Linear(32, 1) ) # 定义初始条件 u(x,0) sin(pi * x) def ic(x): return torch.sin(torch.pi * x) # 生成内部采样点用于计算 PDE 残差 N_pde 1000 x_pde torch.rand(N_pde, 1) # x 在 [0,1] 均匀采样 t_pde torch.rand(N_pde, 1) # t 在 [0,1] 均匀采样 x_pde.requires_grad_(True) t_pde.requires_grad_(True) optimizer torch.optim.Adam(net.parameters(), lr1e-3) for step in range(5000): u net(torch.cat([x_pde, t_pde], dim1)) # 对输入求一阶导和二阶导得到 PDE 残差 du_dt torch.autograd.grad(u, t_pde, grad_outputstorch.ones_like(u), create_graphTrue)[0] du_dx torch.autograd.grad(u, x_pde, grad_outputstorch.ones_like(u), create_graphTrue)[0] du_dxx torch.autograd.grad(du_dx, x_pde, grad_outputstorch.ones_like(du_dx), create_graphTrue)[0] # PDE 残差du/dt - alpha * d2u/dx2 0 loss_pde torch.mean((du_dt - alpha * du_dxx) ** 2) # 初始条件残差t0 时 u sin(pi*x) t0 torch.zeros(N_pde, 1) u0 net(torch.cat([x_pde, t0], dim1)) loss_ic torch.mean((u0 - ic(x_pde)) ** 2) # 边界条件残差x0 与 x1 处 u 0 xb torch.tensor([0.0, 1.0]).reshape(-1, 1) tb torch.rand(2 * N_pde, 1) ub net(torch.cat([xb.repeat(N_pde, 1), tb], dim1)) loss_bc torch.mean(ub ** 2) loss loss_pde loss_ic 10.0 * loss_bc optimizer.zero_grad() loss.backward() optimizer.step() if step % 500 0: print(fstep {step}, loss_pde{loss_pde.item():.6f}, floss_ic{loss_ic.item():.6f}, loss_bc{loss_bc.item():.6f})这段代码的逻辑流程是先用torch.rand在单位正方形上撒训练点把x和t拼成一个两列的输入张量网络输出预测温度场u然后通过torch.autograd.grad两次求导得到∂u/∂t和∂²u/∂x²残差就是这两项与热扩散系数的差初始条件损失和边界条件损失各占一项最后按权重合并反向传播。参数说明网络是三层32节点全连接激活函数选双曲正切输入层两个神经元分别对应x和t输出层一个神经元对应温度。Adam优化器学习率1e-3训练5000步。这些参数不是拍脑袋定的——对一维热传导这种光滑问题三层32节点已经足够表达解函数tanh因为在零点附近二阶导连续对PDE残差计算更友好。内部采样点1000个在二维时空域上属于中等密度边界条件损失我给了10倍权重因为边界点数少、容易在总损失里被淹没。这个代码跑通之后你会看到loss_pde在前几百步快速下降随后进入慢速收敛区间。这时候真正要注意的不是每步打印的loss值有多好看而是训练结束后网络输出的温度场是否真的符合物理常识。这就是下一章要解决的问题。3. 边界条件与初值不进损失函数会怎样约束方式的三种做法3.1 软约束边界点必须在损失函数里占座上一章的代码是典型的软约束做法边界条件和初始条件各自成为损失函数的一项和PDE残差一起被优化。它的优点是实现简单、通用性强无论Dirichlet边界还是Neumann边界都能写进loss里。缺点也很突出——边界条件不会被精确满足网络在边界附近的温度值总是带一点偏差惩罚权重λ_bc越大偏差越小但太大又会让优化过程稳定不下来。软约束最常见的坑是权重失衡。边界采样点通常只有几百个内部采样点有几千个PDE残差在总损失里天然占据主导网络会优先满足内部方程而牺牲边界精度。我一般会把边界损失权重调10到100倍具体数值看loss曲线的收敛情况。判断标准是训练收敛后把边界温度单独挑出来画一条曲线看它离精确边界值有多远。如果偏差超过内部误差一个数量级就继续加大权重。3.2 强约束把边界条件写进网络输出表达式软约束是让网络尽量满足边界强约束是让网络不可能不满足边界。对于规则区域和常值边界条件可以在网络外面包一层变换层把边界信息直接嵌入网络输出。举个具体例子。上一章的问题边界条件是u(0,t)0和u(1,t)0也就是两端温度恒为零。可以构造一个辅助函数u_net(x,t) x · (1-x) · N(x,t)其中N(x,t)就是神经网络的原始输出。注意x(1-x)在x0和x1处恒等于零不管N(x,t)输出什么值乘上这个因子后边界条件被严格满足连loss_bc都可以删掉。这个技巧学名叫hard constraint用起来非常丝滑。它相当于在求解之前就把变量换到满足边界条件的函数空间里优化问题少了一项约束网络内部点任务更纯粹收敛也快一些。这个技巧只对常数Dirichlet边界有效。如果边界条件是u(0,t)t这种非零时变值就要把变换改成:u_net(x,t) x · t x · (1-x) · N(x,t)如果换成Neumann边界比如∂u/∂x在边界上等于给定热流就不能用这种简单乘法了。Neumann边界本质上约束的是导数需要在输出表达式里额外构造一个关于x求导后自动等于目标值的项复杂程度高很多。所以我实际做工程时Dirichlet边界一律用强约束Neumann边界保留软约束。3.3 时间维度当输入还是滚动时间步进温度场是瞬态问题时间必须进入网络。最常见的选择是把时间t和坐标x一样作为一个普通输入网络同时接收t和x输出那一时刻的温度值。这种做法的好处是整个时空域一次性训练完成要预测新时刻的温度直接把新时间喂给网络不需要重新训练。问题在于神经网络并不知道时间有先后顺序把t0.9和t0.1各是什么时序关系完全靠数据体现。如果初始条件约束不够强网络给出的温度演化曲线可能是先降温再升温这种物理上荒谬的结果。我的经验是对瞬态问题时间轴上t0附近的初始点要加密采样并且初始条件损失权重给大一些强迫网络把时间轴起点钉死。另一种做法是时间步进把t0的初始场作为输入网络只负责预测下一个时刻的温度场然后滚动下去。这更像是传统数值解法里的时间推进思路每一步都是一个PDE约束因果顺序被结构性地保留。代价是训练要分成多步每步的误差会累积长时间预测精度衰减明显。做实时温度预测时我倾向选定输入法一次训练可以快速查询任意时刻做长时间演化分析则更看好时间步进的结构化约束。3.4 边界与初值处理的工程选择表不同场景下约束方式的选择可以直接对照下面的经验表这些选择不是理论最优而是工程里踩过后总结出的可用组合。表格中的建议权重是初始调节值后续要靠验证集误差微调。问题类型约束方式损失权重初始值适用场景常数Dirichlet边界强约束乘法变换不参与损失规则区域、边界条件简单固定时变Dirichlet边界强约束加法构造不参与损失边界温度随时间按已知规律变化Neumann边界软约束λ_bc 50~100边界热流固定或随位置变化Robin边界软约束λ_bc 100对流换热边界涉及组合导数初始条件软约束λ_ic 1~10所有瞬态问题必须保留这张表我给过好几个入手PINN温度场的人最常见的走弯路场景是边界条件简单却用了软约束导致边界误差始终消不掉初始条件权重太低导致早期时刻的温度分布拉回不去。4. 采样策略、归一化与激活函数影响收敛的三个隐藏参数4.1 采样分布优先于采样数量训练样本点的生成方式对收敛的影响经常被新手忽略。torch.rand生成的是均匀随机分布这在低维问题里够用但温度场的求解往往有边界层或局部剧烈变化区域均匀撒点会让网络把注意力平均分配给整个区域愿意学大趋势不擅长抠细节。更稳的做法是分区域采样。对一维热传导问题我通常在边界附近加密采样点在初始条件t0附近加密采样点内部区域保持均匀但密度可稍低。具体做法是把定义域划分成几个子区域每个区域独立采样再合并。对二维问题效果更明显——靠近几何体拐角的地方温度梯度大采样密度加倍往往比整体增加样本点数更有效。拉丁超立方采样也值得引入。它比完全随机采样更均匀在同样的样本数量下残差方差更低。PyTorch没有内建拉丁超立方但可以直接用scipy.stats.qmc.LatinHypercube生成样本再映射到定义域。我实测过同样3000个内部点拉丁超立方比均匀随机在loss_pde收敛上快约两成后期精度也略好。4.2 归一化输入量纲差异会让前馈网络白跑温度场问题的输入是坐标和时间物理量纲完全不同。x的单位是米t的单位是秒如果x取值在0到0.01之间、t取值在0到10000之间网络输入层两个维度数值差异巨大初始权重下的输出会被数值大的t主导PDE残差的梯度方向也会被带偏。这个问题的严重程度和网络深度成正比层数越多未归一化的输入越容易让中间层激活进入饱和区。归一化标准做法是空间坐标归一到[-1,1]时间坐标归一到[0,1]或[-1,1]。数学形式是x_norm 2*(x - x_min)/(x_max - x_min) - 1。不要只做均值平移不做缩放因为神经网络对输入尺度非常敏感。热扩散系数α也要跟着变换——如果x被缩放∂²u/∂x²在计算时用的是归一化后的坐标PDE残差里的α要做相应的量纲换算否则损失函数整体偏了一个尺度。网络输出的温度值如果跨度很大比如从300K到1200K最好也做一层线性缩放。常见做法是让网络输出单位区间内的值再乘以温差加上最低温度还原。这样做的原因是网络输出层初始权重在零附近直接输出大数值需要训练把权重推到很大的量级收敛速度明显变慢。4.3 激活函数为什么ReLU在温度场问题上不香不少从图像或NLP切过来的朋友默认激活函数用ReLU结果在PINN上翻车。原因是ReLU在x大于零区域是线性函数二阶导数恒为零。在计算∂²u/∂x²时ReLU输出被求两次导之后直接归零PDE残差里扩散项消失网络收到的梯度信息只剩下时间项解出来的温度场既不平滑也不物理。双曲正切tanh是PINN的默认选择。它的二阶导数非零且有限满足光滑温度场的要求。Sigmoid也可以但它在两端饱和区梯度趋近于零深层网络容易梯度消失。实际调参时我用两个版本搭配浅层网络用tanh层数超过六层时考虑对tanh的输入做归一化处理限制中间层激活值不过早饱和。激活函数还有一个容易被忽视的影响网络输出在边界约束变换后会经过乘法因子而乘法因子在定义域上是光滑函数如果网络本身的高频表达能力不够温度场的细节会被抹平。此时加宽隐藏层比加深更有效。我一般先加宽度到64节点不够再加层而不是一上来就堆深度。4.4 训练调度与一组可直接复用的收敛参数上一章的最小代码里优化器全程用Adam、学习率固定1e-3。实际做的时候我不会让Adam一根筋跑到最后而是分段调度先Adam跑几千步把损失从很差的初始化拉到一个合理的量级再切换到L-BFGS这种拟牛顿方法做局部精修。L-BFGS对光滑损失函数的收敛很稳在PINN这种高维非凸问题上经常能把loss压低一到两个数量级。下面给一组我在一维/二维温度场问题里反复用过的默认参数新手可以直接拿这组参数起步不必一开始就全参数网格搜索参数项推荐值说明隐藏层宽度32~64节点一维问题32够二维复杂几何用到64隐藏层深度3~5层超过6层对光滑问题收益很小激活函数tanh二阶导非零适合PDE残差内部采样点1000~4000按区域分布采样不在整域均匀撒边界采样点内部采样点总数的5%~10%软约束边界时尽量多给初始条件采样点时间t0处单独撒500~1000点瞬态问题的高优先级区域学习率Adam阶段1e-3~1e-4L-BFGS阶段默认Adam先粗收敛L-BFGS后精修损失权重λ_bc50~100λ_ic10边界点少权重要明显高于PDE输入归一化x、t均映射到[-1,1]不归一化基本白训训练步数Adam 3000~8000步L-BFGS 200~500次迭代一维问题可以更少训练完成后不要直接信任最后一步的loss值那只能说明优化器把损失函数压低了不代表物理正确性。正确的验收方式是回到第5章和第6章检查输出曲线形状、与有限差分参考解对照误差。这套参数能保证你快速跑通一个可用的温度场求解器但真正上线前必须做验证。5. 温度场算不出来时的避坑与排查四条常见失灵记录5.1 现象输出几乎是常数PDE完全没起作用第一次跑热传导方程训练的常见画面损失函数在前几百步飞快下降然后loss_pde降到很小但训练结束把温度场画出来整个区域温度几乎不变边界条件和初始条件却满足得很好。这个结果在数学上合格——一个常数场确实满足边界也满足∂u/∂t0、∂²u/∂x²0它精确地解出了一个退化的热传导方程。原因在于网络的初始化输出在零附近边界条件损失非常大的情况下优化器先在边界上把输出压平网络参数进入一个局部最优——输出接近零PDE残差天然很小梯度信号也跟着消失于是整个训练就停在了这口井里。解决方法是破坏平凡解的吸引力。我常用三招一是把初始条件损失权重调大比如λ_ic100让网络在t0附近被强拉向正弦分布打破常数场的对称性二是训练初期对PDE残差给更高权重在边界条件还没压平输出之前就让内部点先学出空间差异性三是把网络最后一层偏置初始化改为零但加一个随机扰动避免初始输出全部为零。这三招不用全上先试第一招大多数一维问题就能解决。5.2 现象边界附近残差很大内部温度还挺合理网络输出在区域内部看起来光滑物理但在边界附近温度明显偏离指定值或者边界处温度虽然正确立刻往里走就开始翘起来。这个问题的典型特征是loss_pde已经很低loss_bc却居高不下两者之间在打架。原因是软约束的边界损失和PDE残差本质上是竞争关系。边界点上的约束要求网络在边界处精确满足给定值PDE残差则要求边界点及其邻域满足热传导方程这两组约束在一个有限容量的网络里很难同时完全满足。边界采样点数量太少时更严重——边界对整个区域的影响被严重削弱。解决思路有两层。第一层是增加边界采样点密度尤其是靠近两个端点或拐角的位置边界点数提到内部点数的10%以上。第二层是改用强约束把Dirichlet边界直接写进网络输出从结构上消掉这个竞争关系。我一般先做第一层如果边界误差还在1%以上就上第二层。5.3 现象损失降到很低但预测温度场出现振荡训练结束后把温度场可视化发现空间分布不平滑出现锯齿状波纹。这种结果在数值格式里会让人直接怀疑网格不够细或者用了不稳定的格式在神经网络求解里第一反应应该是激活函数和训练调度的锅。最常见原因是训练后期学习率过高。Adam的默认学习率1e-3在训练初期很好用但损失进入较平缓的区域后这个学习率会导致权重在小范围内来回抖动网络输出在局部被高频扰动污染。解决方法是训练后半段把学习率降到1e-4或者直接切换L-BFGS。另一个常见原因是激活函数在深层网络中进入饱和区tanh的输出如果大量聚集在正负1附近梯度接近零网络失去局部调整能力输出就会在高频和低频模式之间摆动。排查手段是把训练过程中的loss_pde单独打点如果曲线是锯齿状下降而不是平滑下降基本可以断定是学习率偏大。把学习率除以10再跑一遍观察锯齿是否消失。如果锯齿消失但损失仍然略高这个损失差就是高频振荡付出的代价值得接受。5.4 现象训练集上精度很好换一套验证点误差突然变大这是最隐蔽的一个坑也最让人上头。网络在训练采样点上残差很低你把它在同样区域换成更密的网格去取值温度场与参考解出现肉眼可见的偏差。这种情况在网络里叫过拟合但对PINN来说含义略有不同——网络学到了训练点的精确值却并没有真正学到温度场这个函数本身的形式。原因有两点采样点本身的分布不均匀网络在稀疏区域自由发挥训练点与验证点是同一批随机样本网络把噪声也一并拟合进去。我这里建议训练点与验证点必须分开生成绝不能复用同一批点——否则误差永远看起来很好看。解决方法是引入验证集并且定期检查。我会在训练过程中每500步用独立的验证采样点计算一次温度误差画成曲线观察误差是不是跟训练loss一起下降。如果训练loss降而验证误差升说明网络开始钻训练样本的空子应当增加采样点数量、减少网络宽度或者提高损失函数里PDE残差的权重因为过拟合通常发生在边界和内部约束的边界地带——网络会找到一个在训练点上两头都凑合、但在训练点之外剧烈摆动的函数。6. 验证才是算出温度场的关键差分参考解与误差量化网络训练完毕第一个动作不是直接拿去出图而是找参考解做对照。一维热传导方程在简单初始条件和边界条件下是有解析解的比如u(x,t) e^(-απ²t) · sin(πx)。拿解析解验证网络输出是最干净的方案比数值解更可靠因为数值解自身也有离散误差。我们手头如果没有解析解就退而求其次用有限差分法在细网格上算一个高精度的解当金标准。误差量化我用的指标是相对L2误差计算方式是网络输出与参考解在整个时空域上逐点做差平方和开根号后除以参考解的L2范数。一维问题目标控制在1%以内二维问题可放宽到3%。具体做法是在验证阶段生成一个网格比如x方向100个点、t方向100个点共一万个验证点这些点不参与训练单独喂给训练完的网络和差分法程序分别计算温度场再对比。训练完后我习惯把网络输出和参考解在几个典型时刻各画一张曲线图。t0.25、t0.5、t0.75三个时刻最直观能看出温度峰值衰减速率对不对、边界处的斜率平滑与否。如果曲线形状接近、相对L2误差在阈值内这个网络才有资格投入后续使用否则回到第4章调参数。我个人的习惯是每个新温度场问题都先跑一遍这个验证流程哪怕是一个看起来很简单的一维问题。因为神经网络求解偏微分方程是一个没有先验误差保证的黑匣子看着收敛和真算对了之间差着一整条验证链。跑通一次验证流程再换二维问题、再换不规则边界你心里会对这套方法到底行不行有一个越来越准确的判断。希望这篇笔记能帮你在神经网络求解温度场的路上少走几步弯路把该踩的坑提前踩完。本文还有配套的精品资源点击获取