
简介本资源面向从事科学计算与深度学习交叉研究的学生、工程师提供基于PINN物理信息网络的四种时间处理范式离散时间识别、离散时间推理、连续时间识别与连续时间推理每类均配有独立可运行的Python源码与配套数据帮助理解物理约束如何嵌入神经网络并完成正反问题求解。压缩包共559个文件以503个txt结果记录、13个py脚本、12个csv误差表、9个eps与9个pdf图表文档、7个mat数据文件为主另含少量pyc、yml、html、md与nb整体约475.95MB目录按方法分模块组织便于对照实验与复现。目前已有1546人学习下载。读者可获取四套完整代码、误差对比表与可视化图表快速搭建PINN实验环境掌握不同时间离散策略下的训练与推理流程并借助误差表与图表验证模型精度适合作为课程设计、论文复现或算法改进的参考起点。1. 从四个代码包说起PINN 做时间域识别到底在识别什么如果你手上只有一组带噪的时序观测却想同时拿到「系统演化的连续轨迹」和「离散采样点上的状态」传统做法要么先拟合再求导要么用数值差分硬算噪声一大就全崩。物理信息神经网络PINN换了个思路把控制方程当成损失函数的一部分让网络在拟合数据的同时满足物理约束于是连续时间识别和离散时间识别可以共用一套骨架。这个标题里的四个代码包对应的正是两条轴连续时间推理 vs 离散时间推理以及识别反推方程参数/结构vs 推理已知方程求状态。很多人第一次跑 pinn 代码直接拿官方例子改改就上结果 loss 不降、参数乱飞其实问题往往出在没分清自己到底要解哪一类任务。这篇笔记就按「先立住概念、再动手复现、最后讲坑」的顺序把四个代码包背后的方法拆开讲清楚适合已经会写 python、想把这套东西用到自己时序数据上的工程师。2. 连续时间识别与离散时间识别损失函数怎么摆2.1 两种时间域的本质差别连续时间识别假设观测是某个连续动力系统在若干时刻的采样网络输入是时间 t输出是状态 u(t)同时用自动微分算出 u_t、u_xx 这些导数项代入残差方程。它的好处是时间点可以任意密推理时想插值到没观测过的时刻直接喂 t 就行。代价是每次前向都要算高阶导数计算图会变长显存吃紧。离散时间识别则把时间当成一个递推步网络学的是 u(tΔt) 与 u(t) 之间的映射或者直接用多步残差形式。它不需要对时间求导数值上更稳适合采样间隔固定、观测点密集的场景。但它的「物理约束」是写在离散格式里的比如用 Crank-Nicolson 或显式欧拉把 PDE 离散后再构造残差所以步长 Δt 本身成了超参选错会让残差和真实物理对不上。选型上我一般这样判断观测点少于 200 个、时间跨度大、想要连续曲线走连续时间观测点上千、间隔均匀、只关心下一步预测走离散时间。四个代码包里通常两个走连续、两个走离散识别和推理各占一份这样对比起来最直观。2.2 损失函数的四项构成不管哪条路PINN 的 loss 基本都是四块拼起来# 以连续时间识别为例loss 四项构成 loss_data mse(net_u(t_obs), u_obs) # 数据拟合项 loss_phys mse(residual(t_col, net_u), 0.0) # 物理残差项 loss_ic mse(net_u(t_ic), u_ic) # 初始条件项 loss_bc mse(net_u(t_bc), u_bc) # 边界条件项 loss w_data*loss_data w_phys*loss_phys w_ic*loss_ic w_bc*loss_bc逻辑说明t_col是配点collocation points不需要观测值只用来算残差t_obs才是有标签的数据点。参数说明四个权重w_*是最容易翻车的地方常见做法是先让w_data主导把数据拟合住再逐步加大w_phys也就是所谓的「课程学习」。如果一上来w_phys给太大网络会先满足方程而忽略数据最后识别出的参数偏离真实值。提示配点数量一般取观测点的 25 倍太少残差约束不住太多训练慢且容易过拟合到残差为零的平凡解。2.3 识别任务里参数是怎么被反推的识别inverse和推理forward的区别在于识别时方程里有一两个未知系数比如 λ把它设成torch.nn.Parameter一起训练。残差写成u_t - λ*u_xx训练完看 λ 收敛到多少。这里有个细节λ 的初始值别设 0设成 1 附近否则梯度信号太弱。离散时间识别同理把离散格式里的系数当可训练参数即可。四个代码包里识别类的那两个核心改动就在这一行参数声明上其余骨架和推理类完全一致。3. 用 python 把四个代码包跑起来环境、数据、训练3.1 环境配置与依赖python 环境这块我建议直接用 conda 建独立环境别在 base 里折腾。pinn 代码对 pytorch 版本比较敏感自动微分和torch.autograd.grad的行为在小版本间有差异。conda create -n pinn python3.9 -y conda activate pinn pip install torch1.13.1 numpy scipy matplotlib逻辑说明python 3.9 是兼容性最稳的版本torch 1.13 的 autograd 接口和大多数公开 pinn 代码一致。参数说明如果你用 GPU把 torch 换成对应 cuda 版本没有 GPU 也能跑这四个代码包的规模 CPU 几分钟就出结果别被「深度学习」四个字吓住。vscode 配置 python 环境时记得把解释器指到pinn这个 conda 环境否则 import torch 会报找不到。3.2 数据准备与配点生成四个代码包一般自带生成数据的脚本但你要换成自己的数据得注意格式。观测数据存成两列时间 t 和状态 unumpy 的.npy或.csv都行。import numpy as np # 生成带噪观测真实 u sin(x) 演化加 1% 高斯噪声 t np.linspace(0, 1, 100).reshape(-1, 1) u_true np.sin(2 * np.pi * t) u_obs u_true 0.01 * np.random.randn(*u_true.shape) # 配点在时间域内均匀撒 300 个点不需要标签 t_col np.linspace(0, 1, 300).reshape(-1, 1) np.save(u_obs.npy, u_obs) np.save(t_col.npy, t_col)逻辑说明观测点和配点分开存训练时观测点算loss_data配点算loss_phys。参数说明噪声水平 1% 是常见测试值超过 5% 时连续时间识别的参数反推会明显漂移这时要么加正则要么改用离散格式。配点 300 个对应 100 个观测点比例 3:1符合前面说的经验区间。3.3 训练循环与关键超参import torch optimizer torch.optim.Adam(net.parameters(), lr1e-3) scheduler torch.optim.lr_scheduler.StepLR(optimizer, step_size2000, gamma0.5) for epoch in range(10000): optimizer.zero_grad() loss compute_loss(net, t_obs, u_obs, t_col) loss.backward() optimizer.step() scheduler.step() if epoch % 1000 0: print(fepoch {epoch}, loss {loss.item():.6f})逻辑说明Adam 起步学习率 1e-3每 2000 步衰减一半。参数说明迭代 10000 次是这类小规模问题的常用值loss 通常在 3000 步内降到 1e-4 量级。如果 1000 步后 loss 还在 1e-1 不动先查配点是不是落在边界外再查权重是不是把物理项压死了。离散时间代码包里训练循环一样只是compute_loss内部换成离散残差不需要torch.autograd.grad。4. 四个代码包各自的定位与改动点4.1 连续时间推理包已知方程求状态这个包假设方程系数已知网络只学状态 u(t)。改动点最少你只需要把residual函数里的方程换成自己的。比如从热方程换成波动方程就是把u_t - alpha*u_xx改成u_tt - c**2*u_xx注意二阶时间导数要算两次 grad。def residual(net, t): t.requires_grad_(True) u net(t) u_t torch.autograd.grad(u, t, torch.ones_like(u), create_graphTrue)[0] u_tt torch.autograd.grad(u_t, t, torch.ones_like(u_t), 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] return u_tt - c**2 * u_xx逻辑说明create_graphTrue是必须的否则二阶导算不出来。参数说明c是波速已知就写死未知就设成 Parameter 走识别流程。这个包适合验证「我的方程写对没有」先用已知解析解的数据跑一遍看网络能不能复现。4.2 连续时间识别包反推未知系数和上面唯一的差别是系数变成可训练参数。训练完打印net.lambda_.item()看收敛值。常见问题是 λ 收敛到负值或量级差几个数量级原因通常是数据噪声太大或配点太少。解决办法先把 λ 固定成真值跑一遍确认骨架没问题再放开训练。4.3 离散时间推理包递推格式求下一步这个包不碰自动微分残差直接写成离散格式。比如显式欧拉u_{n1} - u_n - dt*f(u_n) 0。网络输入是 u_n输出 u_{n1}。改动点是dt要和你的采样间隔一致不一致残差就系统性偏大。4.4 离散时间识别包离散格式里的参数反推同理把离散格式里的系数设成 Parameter。这个包数值最稳适合噪声大、采样密的数据。缺点是只能预测采样点上的值想要连续曲线还得插值。5. 避坑与排查loss 不降、参数乱飞的五个血泪经验5.1 现象loss 卡在 1e-1 不动原因物理残差权重过大网络优先满足方程而放弃拟合数据或者配点全落在观测区外。解决把w_phys从 1e-2 起步每 1000 步乘 1.5 逐步加大检查配点范围是否覆盖观测区间。5.2 现象识别出的参数收敛到错误值原因数据噪声超过 5%或 λ 初始值离真值太远导致梯度消失。解决先降噪或改用离散格式λ 初始值设成 1 附近别设 0。5.3 现象连续时间推理显存爆掉原因配点太多加上二阶导计算图长度是配点数的平方级。解决配点降到 200 以内或分批喂配点每批 50 个。5.4 现象离散时间推理预测下一步误差累积原因训练时只监督单步推理时多步递推误差放大。解决训练时用多步展开让 loss 覆盖 35 步的预测代码里把u_{n1}再喂回网络算u_{n2}。5.5 现象换了自己的数据后完全不收敛原因数据没归一化时间尺度和状态尺度差几个数量级。解决把 t 和 u 都归一化到 [0,1] 或零均值单位方差训练完再反归一化输出。6. 进阶技巧用残差分布验证识别结果可不可信跑完四个代码包怎么判断结果可信别只看 loss 数值看残差在时间域上的分布。把训练好的网络在 1000 个均匀时间点上算残差画出来。如果残差在观测区小、在观测区外突然变大说明网络只是记住了数据物理约束没真正起作用识别出的参数不可信。真正收敛的结果残差在整个时间域上应该均匀地小量级和噪声水平相当。t_test torch.linspace(0, 1, 1000).reshape(-1, 1) res residual(net, t_test).detach().numpy() print(残差均值, np.abs(res).mean(), 残差最大, np.abs(res).max())逻辑说明残差均值应该和你的观测噪声同量级最大残差不应超过均值的 10 倍。参数说明如果最大残差是均值的几十倍说明局部区域物理约束失效通常是配点在那稀疏补点重训即可。我自己的习惯是每换一个数据集先拿连续时间推理包跑一遍已知解析解确认骨架和归一化没问题再上识别包反推参数。这个顺序能省掉大量「到底是代码错还是数据错」的排查时间。四个代码包不用全跑按你的任务选一个推理加一个识别对照着看理解会快很多。希望帮到你。本文还有配套的精品资源点击获取