
简介基于物理信息神经网络的完整实现资料覆盖离散时间识别、离散时间推理、连续时间识别、连续时间推理四类典型方法分别对应时间离散采样与连续时空场两类建模场景侧重解决数据驱动下带物理约束的识别与推理问题适合从事物理信息神经网络研究、科学计算及偏微分方程求解的科研人员和工程师使用。压缩包共559个文件内含13份Python源代码、12份CSV误差数据表、9份EPS或PDF结果图、7份MAT数据文件以及503个txt文本说明整体约475.95MB便于代码阅读、实验复现和结果对比。已有1546人学习下载。通过四组Python工程可系统掌握物理信息神经网络在离散与连续时间场景下的识别与推理建模思路配套误差表、图件和原始数据便于分析不同时间处理方式的参数影响、验证算法效果既可作为入门模板也可用于复现论文实验。1. PINN 物理信息网络解决的是什么问题为什么把时间项拆成离散和连续标题里同时出现“离散时间识别/离散时间推理/连续时间识别/连续时间推理”四个动作实际是同一套 PINN物理信息神经网络框架下对时间维度的两种建模方式。PINN 的核心是把控制方程的残差作为损失项加到网络训练里让网络输出既拟合观测数据又不违背物理规律。离散时间识别做的是从若干时刻的快照反推出方程里的未知参数离散时间推理是在给定初值后用有限差分或时间步进把网络沿时间方向延展开。连续时间版本则直接把时间 t 作为网络输入的一维坐标用自动微分计算时间导数识别和推理都在 t 的连续区间内完成。适合流体、热传导、结构振动以及工程中需要在数据不足时仍保持物理一致性的场景也是 pytorch 和 tensorflow 中可复现的 PINN 入门路径。2. 离散时间识别与推理的实现路径从标签数据到物理残差离散时间处理的共同前提是训练数据是若干时间截面上的完整或局部观测值。识别和推理的区别只在损失函数和目标上不同但网络结构和时间离散方式几乎一样。2.1 离散时间识别让网络在多个快照上同时满足 PDE常见做法是设置一个可训练参数unknown_params来代表方程中的待定系数比如扩散系数 a、源项强度 q然后把网络输入设计为(x, t)其中 t 只取离散的时刻集合。损失包含两项数据拟合项每个观测时间点上的网络输出u_net(x,t)与该时点传感器读数之差。物理残差项在随机采样点上计算 PDE 残差残差里含unknown_params。物理残差通过 autograd 计算所以unknown_params也能拿到梯度。这是和纯监督学习最大的不同目标函数里的未知数同时被数据项间接约束、被残差项直接约束。2.2 离散时间推理用有限差分把时间方向串起来推理阶段往往不再有后续时间的数据。这时用一个步长dt把时间域切成网格在时间层之间做物理步进。损失函数只剩物理残差和初始条件。2.2.1 一阶热传导方程的最小识别代码结构下面是一段可以用在 pytorch 中的核心片段完成「离散时间识别」中一个扩散系数的识别import torch import torch.nn as nn class PINN(nn.Module): def __init__(self, n_layers4, n_neurons32): super().__init__() layers [nn.Linear(2, n_neurons), nn.Tanh()] for _ in range(n_layers - 1): layers [nn.Linear(n_neurons, n_neurons), nn.Tanh()] layers [nn.Linear(n_neurons, 1)] self.net nn.Sequential(*layers) def forward(self, x, t): xt torch.cat([x, t], dim1) return self.net(xt).squeeze(-1) model PINN() a_hat nn.Parameter(torch.tensor(0.1, requires_gradTrue)) optimizer torch.optim.Adam([*model.parameters(), a_hat], lr1e-3) def loss_physics(x_colloc, t_colloc): x_colloc.requires_grad_(True) t_colloc.requires_grad_(True) u model(x_colloc, t_colloc) u_x torch.autograd.grad(u, x_colloc, torch.ones_like(u), create_graphTrue)[0] u_xx torch.autograd.grad(u_x, x_colloc, torch.ones_like(u_x), create_graphTrue)[0] u_t torch.autograd.grad(u, t_colloc, torch.ones_like(u), create_graphTrue)[0] # 残差u_t - a_hat * u_xx f u_t - a_hat * u_xx return torch.mean(f**2)这段代码把时间当作普通输入因此即使观测数据只来自几个离散时刻残差项仍然可以在这些时刻之间的任意采样点上计算。requires_grad_(True)打开二阶导create_graphTrue让导数也能求导这样a_hat可以从残差里拿到梯度。识别出的a_hat在训练结束后直接打印即为扩散系数。2.2.2 离散时间推理的循环结构推理阶段要把时间方向显式串起来。常见做法是在每个时间步内把上一时间层的输出作为“虚观测”下一时间层的输出通过有限差分和残差约束联系起来dt 0.01 def rollout(initial_x, initial_u): x_prev initial_x.reshape(-1, 1) u_prev model(x_prev, torch.zeros_like(x_prev)) for step in range(100): t_cur torch.full_like(x_prev, step * dt) x_cur x_prev.clone() u_cur model(x_cur, t_cur) # 用后向欧拉近似时间导数 residual (u_cur - u_prev) / dt - a_hat * torch.autograd.grad( torch.autograd.grad(u_cur, x_cur, torch.ones_like(u_cur), create_graphTrue)[0], x_cur, torch.ones_like(u_cur), create_graphTrue )[0] loss torch.mean(residual**2) torch.mean((u_cur - u_prev)**2) optimizer.zero_grad(); loss.backward(); optimizer.step() u_prev u_cur.detach().requires_grad_(True)这个循环在每一步训练网络使网络在离散时间层上的输出作为解推进。识别和推理的差别在这里体现得很直接识别时残差里的系数是待训练参数推理时系数已经固定残差只对时间和空间坐标求导。离散时间推理因为没有后续观测数据需要在初始层和每一步的残差上做双重约束所以训练步数通常比识别更多。2.3 离散时间方案的适用范围和局限离散时间方式的主要优势是方便把实验中的时间采样点直接用作网络输入。实际工程数据往往是不等间隔采样的离散时间建模不需要插值只要求在采样时刻有观测即可。局限是时间推进步长dt受数值稳定性限制过大的dt会让残差项主导出现振荡和梯度爆炸过小的dt会显著增加训练成本。另外离散时间推理的误差会随步数累积一般只适合短时程问题。3. 连续时间识别与推理把时间作为网络输入维度连续时间方式不再考虑时间层的推进而是让网络直接逼近函数u(x, t)在整个时空域上采样。识别和推理的区别同样体现在未知参数的训练模式上。3.1 连续时间识别在时空域上联立所有残差把时间维放进网络输入后损失函数可以写成L L_data lambda_1 * L_pde lambda_2 * L_ic lambda_3 * L_bc其中L_pde在时空域随机采样点上计算L_ic在 t0 或初始时刻的线上计算L_bc在边界时空点上计算。识别过程中方程未知参数作为网络之外的nn.Parameter与网络权重一同更新。3.1.1 连续时间残差的自动微分写法与离散时间不同连续时间不需要有限差分直接对网络输入t求一阶导数即可。一个典型代码如下def pde_residual(x, t): x x.requires_grad_(True) t t.requires_grad_(True) u model(x, t) u_t torch.autograd.grad(u, t, torch.ones_like(u), create_graphTrue)[0] u_x torch.autograd.grad(u, x, torch.ones_like(u), create_graphTrue)[0] u_xx torch.autograd.grad(u_x, x, torch.ones_like(u_x), create_graphTrue)[0] residual u_t - a_hat * u_xx # a_hat 可训练或冻结 return residual optimizer torch.optim.Adam(list(model.parameters()) [a_hat], lr1e-3)训练循环里每轮采样一批内部时空点、一批初边值点和一批观测点分别计算三部分损失后加权相加。加权系数lambda_1设得越大物理约束越强数据噪声大时应适当减小lambda_1否则网络会为了满足物理残差而忽略噪声观测。3.1.2 观测数据稀疏时怎么提高识别稳定性稀疏观测下直接训练容易把a_hat在训练初期推向很大/很小的值。一个有效技巧是分阶段训练第一个阶段只训练网络权重用纯数据项恢复出大致解场第二个阶段再放开a_hat的梯度。另一个技巧是给a_hat加上先验约束常见做法是在损失里加(a_hat - a_prior)^2 * lambda_prior。这样可以避免系数漂移。3.2 连续时间推理给定初边值求任意时刻的解推理阶段不需要观测数据网络在时空域上被初边值和 PDE 残差约束。网络学到的是一个从 (x,t) 到 u 的连续函数所以推理本质上是「求这个函数在某个时刻 t 的取值」不需要时间递推。3.2.1 推理时的时间边界处理推理质量高度依赖初始条件是否被严格执行。常见的方法是加重对 t0 整条界线的采样密度让初始条件损失收敛到远低于残差的量级。可设定如下采样参数参数推荐值作用内部时空采样点10000决定 PDE 残差覆盖的时空范围初值线采样点200约束初始状态边界采样点1000约束边界条件每个边界至少 200 个点时间域范围根据问题视导数尺度过大则残差权重不均过小则推理时程短训练时用torch.utils.data.DataLoader或直接把随机采样点一次性生成。推理时不再采样而是调用model(x_query, t_query)。3.2.2 连续时间方案的经典坑高次时间导数对二阶及以上时间导数的 PDE连续时间方式需要至少create_graphTrue的多层嵌套计算图会变得很深。因此建议把二阶时间导数用torch.autograd.grad嵌套求一次然后立即释放中间变量。不要多次.backward()造成图累积。4. 四份代码之间的共性与针对性调参标题里的四个代码典型情况是分别对应离散时间识别、离散时间推理、连续时间识别、连续时间推理。它们共享同一个 PINN 骨干网络区别主要在数据加载、时间处理方式和损失函数上。4.1 四份代码对应哪种问题识别代码的损失里必须有a_hat等参数推理代码则把已知参数冻结。离散时间代码的采样点包括离散时间层时间层间距需要手工给定连续时间代码则使用随机采样的 t 坐标。对照关系可以整理成一张表代码类型数据需求未知参数量时间推进方式典型损失构成离散识别多个时层快照1~3无数据 残差离散推理初值 参数0有限差分初值 逐层残差连续识别时空散射观测1~3无数据 初边值 连续残差连续推理初边值 参数0无初边值 连续残差4.2 三个必调参数物理残差权重lambda_pde。这个权重太小时结果退化成纯插值太大时网络会牺牲边界拟合。推荐从 0.1 开始用对数网格试探几次。网络宽度和深度。PINN 不需要特别深4~6 层、每层 32~128 个神经元通常够用。太宽会导致边界区域振荡。时间范围归一化。把 t 归一化到 [0,1] 能显著提升收敛速度。连续时间推理尤其要注意不归一化时自动微分的梯度量级会差几个数量级。还有一个容易被忽略的点四个代码里如果只改识别/推理开关而不改损失函数里的detach()位置很容易出现梯度穿透时间层。离散推理里对上一时间层输出做detach()是防止梯度沿时间步长回传导致的计算图爆炸连续推理则不需要这种操作因为在单一向前传播中时间只是普通输入维度。4.3 损失权重自动调节的替代做法手动调lambda很累。有一个简单方案在每次迭代里计算三部分损失的梯度范数用梯度范数的倒数做临时权重。pytorch 里实现如下pde_grad torch.autograd.grad(L_pde, model.parameters(), retain_graphTrue) data_grad torch.autograd.grad(L_data, model.parameters()) w_pde torch.norm(data_grad[0]) / (torch.norm(pde_grad[0]) 1e-8) total_loss L_data w_pde * L_pde total_loss.backward()这里w_pde是根据两个梯度范数的比值动态计算的保证网络权重梯度不被某一部分损失主导。retain_graphTrue是因为计算pde_grad后还要继续 backward得到data_grad后再算总损失。实际使用中如果发现w_pde震荡过大可以对其做滑动平均。4.4 常见失败模式与验证方法看到 loss 下降但a_hat不收敛一般是物理权重过低或观测点覆盖不足。看到推理结果在边界处突然抖动是边界点过少。看到连续时间推理在 t 较大时偏差明显是时间归一化没做好或初值约束没有降到足够低。验证推理结果的方法把 PDE 残差单独拿出来在推理得到的新解上重新计算残差均方根若残差均方根仍保持训练时的量级说明外推的物理一致性保持住了。另一种验证方法是把前 80% 时间段的数据拿来训练用后 20% 时间段的观测做对比适合有时间序列实测的场景。5. 用一个小例子验证「先识别再推理」的完整流程假设手里只有两个时刻的观测t0 和 t0.1 时 u 在空间上的两个截面目标是识别扩散系数 a然后推理到 t0.3。第一步用第二章的离散识别代码得到a_hat第二步固定a_hat用离散推理由 t0.1 逐步走到 t0.3。推理完成后在同一个查询点 x 上取 30 个时间等距点看解是否平滑t_query torch.linspace(0, 0.3, 30) u_pred [] for t in t_query: with torch.no_grad(): u_pred.append(model(x_ref, t.unsqueeze(0)).item()) # 相邻时间步的解差用于平滑性检查 diff_max max(abs(u_pred[i1] - u_pred[i]) for i in range(len(u_pred)-1)) print(smoothness max diff:, diff_max)若diff_max小于千分之一量级说明推理过程没有出现明显的数值振荡。若相邻差值突然跳跃优先检查推理步长dt和初值约束是否收敛到位。更关键的技巧是“二次残差验证”推理完成后用推理得到的u和固定的a_hat重新计算 PDE 残差并把残差均方根与训练结束时的残差均方根做对比。当两个值在同一数量级说明推理结果依然满足物理方程而不是只做了时间方向上的线性延拓。若推理残差大了两个数量级以上说明推理区间已经超出网络有效表达范围需要缩小时间步或增加网络容量。这个验证动作可以同时用在离散推理和连续推理上也是判断四份代码结果是否可靠的最直接的客观指标。本文还有配套的精品资源点击获取