news 2026/8/31 7:35:36

物理信息神经网络(PINN)入门:PyTorch与TensorFlow框架对比与实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
物理信息神经网络(PINN)入门:PyTorch与TensorFlow框架对比与实现

PINN,也就是物理信息神经网络(Physics-Informed Neural Networks),是这两年科学计算和深度学习交叉领域里关注度很高的方向。它的核心思路很简单:把偏微分方程/常微分方程的残差作为损失项,直接嵌入到神经网络训练过程中,让网络不再只是“拟合数据”,而是“遵守物理规律地去拟合数据”。这门技术最大的价值在于,它能把传统数值求解 PDE 的思路,和自动微分、GPU 并行、深度学习框架的能力结合起来,在数据不足甚至没有数据的情况下也能求解偏微分方程。

这篇内容是《PINN入门 30 讲》系列课程的第 11 讲,重点放在 PINN 和深度学习框架的关系上。很多刚接触 PINN 的读者会卡在同一个地方:到底选 PyTorch 还是 TensorFlow?两个框架写 PINN 有什么区别?自动微分怎么用?损失函数里的 PDE 残差怎么算?本讲就是回答这些问题的一次完整实操课。我们会从环境搭建开始,分别用 PyTorch 和 TensorFlow 实现同一个 PINN 算例,跑通训练并验证精度,然后把训练过程中常见的坑全部列出来。适合刚了解 PINN 概念、准备开始写代码的读者。

1. 核心能力速览

项目属性说明
项目类型PINN 入门系列课程第 11 讲:框架选型与代码实现
核心主题PINN(物理信息神经网络)、PyTorch、TensorFlow、自动微分
主要功能用深度学习框架实现 PDE/ODE 求解、正向/反向问题建模
推荐环境Python 3.9 及以上;PyTorch 或 TensorFlow 二选一
运行硬件CPU 可运行,有 NVIDIA GPU 显存加速更佳
显存占用以本讲一维 ODE 算例来说,显存占用非常低,一般显卡均可运行
是否支持批量任务支持,可通过批量构造采样点实现;本讲验证的是单算例流程
是否支持 API 服务本讲不涉及 API,属于算法实现层
适合场景科研入门、PINN 学习、框架选型评估、小规模 PDE 求解

这一讲的目标不是讲解完整的 PINN 库,而是把 PINN 最核心的计算流程“用两个主流深度学习框架各写一遍”。理解这一讲内容后,再去读 DeepXDE、NVIDIA Modulus 这类 PINN 开源库,会更容易上手。

2. 适用场景与使用边界

PINN 适合解决什么问题,可以从正向和反向两个角度看。正向问题就是给定 PDE 和边界条件,让网络学会输出解函数。比如热传导方程、波动方程、流体中的稳态或瞬态问题。反向问题则是已知部分观测数据,利用 PINN 反推 PDE 中的未知参数。例如根据温度场离散点预测材料导热系数,根据流速数据猜测粘性系数。这类问题的特点是:传统数值方法往往需要网格生成和迭代求解器,而 PINN 用统一框架就能把正反问题“一起解”,尤其在数据稀疏的时候有优势。

但 PINN 并不是万能的,使用边界要说清楚。第一,对于高维、多尺度、强非线性问题,训练成本很高,收敛稳定性也需要额外调优,这时候传统有限元/有限体积法往往更可靠。第二,PINN 输出是一个“近似解函数”,不是网格节点上的离散精确解,对误差有严格要求的工程场景,需要与传统数值解交叉验证。第三,如果问题本身没有明确的物理规律,或者物理模型本身就不准,PINN 并不会带来额外收益,反而可能把模型错误固化到训练结果里。

还有一个不能忽视的点是合规边界。PINN 属于数学建模和数值计算方法,本身不涉及敏感内容。但实际使用时,如果训练数据来自真实物理实验、工业检测或测绘数据,要注意数据来源授权和隐私保护。使用 PyTorch 和 TensorFlow 开源框架时,也要遵守各自的开源许可证,生产环境发布前确认合规要求。

3. PINN 环境准备:PyTorch 与 TensorFlow 二选一

搭建 PINN 环境前,先理解为什么这两个框架都值得讲。PyTorch 采用动态图机制,代码写起来更像 Python 原生逻辑,调试方便,科学计算社区相关代码很多。TensorFlow 的 Keras 接口非常简洁,同时通过 GradientTape 也能完成自动微分,在部分部署场景里有成熟方案。PINN 的本质是“神经网络 + 自动微分 + 损失函数组合”,所以两个框架都满足需求,区别更多在写代码的体验、生态组件和部署路径。

环境准备遵循一个原则:先把基础运行环境隔离好,再安装深度学习框架,最后用一段小代码验证自动微分可用。先创建一个独立的 conda 虚拟环境,避免不同项目的依赖互相干扰:

conda create -n pinn python=3.9 conda activate pinn

然后根据是否有 NVIDIA GPU,选择安装方式。PyTorch 和 TensorFlow 都支持 CPU 和 GPU 版本,对于本讲算例,CPU 就能轻松跑通。如果你机器上已经有 NVIDIA 驱动,并安装了合适版本的 CUDA,可以安装对应 GPU 版本加速训练;如果没有独立显卡,直接安装 CPU 版本即可,不影响本讲理解 PINN 流程。

安装时不要盲目复制网络上的命令,尤其是 CUDA 版本必须和本机驱动匹配。最稳妥的做法是打开两个官网,看当前稳定版本的安装命令。下面给出通用安装命令模板。

4. PyTorch 安装与验证

在 conda 环境中安装 PyTorch:

pip install torch

如果希望安装 GPU 版本,需要根据 PyTorch 官方安装页选择的 CUDA 版本,用指定 index-url 安装。例如 CUDA 12.1 的安装命令通常类似:

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121

这里的 cu121 只是示例,实际要按 PyTorch 官方页面提供的版本调整。安装完成后,进入 Python 环境,运行一段验证代码:

import torch print("PyTorch 版本:", torch.__version__) # 创建一个需要求梯度的张量 x = torch.linspace(0, 2, 10, requires_grad=True) y = torch.sin(x) # 自动计算 dy/dx y.backward(torch.ones_like(y)) print("dy/dx:", x.grad)

如果能够正常输出 dy/dx,说明 PyTorch 和自动微分机制已经就绪。再验证一下 GPU 是否可用:

print("CUDA 是否可用:", torch.cuda.is_available())

如果输出 True,说明后续可以调用 GPU 训练;如果输出 False,也没关系,本讲算例 CPU 就能完成。

5. TensorFlow 安装与验证

TensorFlow 的安装同样简单,CPU 版本直接使用 pip:

pip install tensorflow

GPU 版本需要 CUDA、cuDNN 与 TensorFlow 版本匹配,安装复杂度略高于 PyTorch。建议先跑通 CPU 版本,再根据需求决定是否配置 GPU。安装后验证版本和自动微分能力:

import tensorflow as tf print("TensorFlow 版本:", tf.__version__) # GradientTape 自动微分 x = tf.linspace(0.0, 2.0, 10) x = tf.Variable(x) with tf.GradientTape() as tape: y = tf.sin(x) dy_dx = tape.gradient(y, x) print("dy/dx:", dy_dx.numpy())

输出结果会是一个包含 cos(x) 数值的数组。GradientTape 是 TensorFlow 中实现 PINN 的关键工具,后续代码里会重复用到。下面如果我们分别用 PyTorch 和 TensorFlow 实现同一个 PINN 算例,你会看到它们在写法上的差别。

6. 用 PyTorch 实现第一个 PINN:一阶 ODE 算例

现在进入核心实操。我们用一个非常经典的一阶常微分方程算例来演示:

dy/dx = -y,y(0) = 1

这个方程的真实解是 y = e^{-x}。虽然问题简单,但麻雀虽小五脏俱全,它包含 PINN 的全部关键流程:物理方程残差计算、边界条件处理、自动微分求导、损失加权训练。

6.1 定义神经网络结构

PINN 的神经网格不需要太复杂,一般两三层的全连接网络加上 tanh 激活函数就能处理很多问题。这里我们定义一个 1-20-20-1 的 MLP:

import torch import torch.nn as nn class PINN(nn.Module): def __init__(self): super(PINN, self).__init__() self.net = nn.Sequential( nn.Linear(1, 20), nn.Tanh(), nn.Linear(20, 20), nn.Tanh(), nn.Linear(20, 1) ) def forward(self, x): return self.net(x)

选择 tanh 而不是 ReLU,是因为 PDE 求解要求输出函数具有一定的光滑性,ReLU 输出存在不可导点,会影响自动微分结果。

6.2 构造损失函数

PINN 的损失函数由 PDE 残差和边界条件两部分组成。PDE 残差就是让网络输出满足方程 dy/dx + y = 0 的逼近误差。在 PyTorch 中,可以通过torch.autograd.grad自动计算网络输出对输入坐标的导数:

def pde_loss(model, x): x.requires_grad_(True) y = model(x) dy_dx = torch.autograd.grad( outputs=y, inputs=x, grad_outputs=torch.ones_like(y), create_graph=True, retain_graph=True )[0] return torch.mean((dy_dx + y) ** 2) def bc_loss(model, x_bc, y_bc): y_pred = model(x_bc) return torch.mean((y_pred - y_bc) ** 2)

在训练循环中,x 的采样坐标在每次迭代可以直接重新生成,也可以使用固定采样点。由于这里问题非常简单,推荐使用固定采样点,更容易复现:

# 在 [0, 2] 区间内采样内部点 x_train = torch.linspace(0, 2, 100).reshape(-1, 1) # 边界条件:y(0) = 1 x_bc = torch.zeros(1, 1) y_bc = torch.ones(1, 1)

6.3 训练循环

model = PINN() optimizer = torch.optim.Adam(model.parameters(), lr=0.01) for epoch in range(3000): optimizer.zero_grad() loss_pde = pde_loss(model, x_train) loss_bc = bc_loss(model, x_bc, y_bc) loss = loss_pde + loss_bc loss.backward() optimizer.step() if epoch % 500 == 0: print(f"Epoch {epoch:5d}, Loss: {loss.item():.6e}, " f"PDE: {loss_pde.item():.6e}, BC: {loss_bc.item():.6e}")

看到 loss 逐渐下降,说明模型开始学会了这个物理方程。训练完成后,把预测结果和真实解 e^{-x} 对比一下:

import matplotlib.pyplot as plt import numpy as np x_test = torch.linspace(0, 2, 100).reshape(-1, 1) y_pred = model(x_test).detach().numpy() y_true = np.exp(-x_test.numpy()) plt.plot(x_test.numpy(), y_true, label="True: e^{-x}") plt.plot(x_test.numpy(), y_pred, "--", label="PINN Predict") plt.legend() plt.xlabel("x") plt.ylabel("y") plt.title("PINN solving dy/dx = -y") plt.show()

如果曲线贴合,说明 PINN 已经正确学到了一阶 ODE 的解。这个流程虽然简单,但后面无论面对热传导方程、流体方程还是更复杂的问题,代码结构都不会变:网络输出,自动微分求导,PDE 残差,边界/初始条件损失,加权优化。

7. 用 TensorFlow 实现同样的 PINN

同样的算例,用 TensorFlow 再写一遍。这里最大的区别在于自动微分方式。PyTorch 使用torch.autograd.grad显式求导,而 TensorFlow 使用tf.GradientTape作为上下文管理器来记录梯度。

7.1 定义网络结构

import tensorflow as tf model = tf.keras.Sequential([ tf.keras.layers.Dense(20, activation="tanh"), tf.keras.layers.Dense(20, activation="tanh"), tf.keras.layers.Dense(1) ])

7.2 定义复合损失函数

def pinn_loss(): # 内部采样点 x_in = tf.linspace(0.0, 2.0, 100) x_in = tf.reshape(x_in, (-1, 1)) with tf.GradientTape() as tape: tape.watch(x_in) y = model(x_in) dy_dx = tape.gradient(y, x_in) # PDE 残差:dy/dx + y = 0 pde_loss = tf.reduce_mean(tf.square(dy_dx + y)) # 边界条件:y(0) = 1 x_bc = tf.zeros((1, 1)) y_bc = model(x_bc) bc_loss = tf.reduce_mean(tf.square(y_bc - 1.0)) return pde_loss + bc_loss

注意到一个细节:在 GradientTape 中,我们使用tape.watch(x_in)来追踪输入张量,因为x_in不是模型中的可训练变量。如果没有这一行,tape.gradient(y, x_in)会返回 None。

7.3 训练循环

optimizer = tf.keras.optimizers.Adam(learning_rate=0.01) for epoch in range(3000): with tf.GradientTape() as tape: loss = pinn_loss() grads = tape.gradient(loss, model.trainable_variables) optimizer.apply_gradients(zip(grads, model.trainable_variables)) if epoch % 500 == 0: print(f"Epoch {epoch:5d}, Loss: {loss.numpy():.6e}")

TensorFlow 版和 PyTorch 版在结果上应该非常接近。你把两个实现放在一起对比,就会发现核心数学完全一样,区别只是 API 表达方式。这正好说明:选 PyTorch 还是 TensorFlow,不应该成为 PINN 入门的障碍,关键是把自动微分和损失组合的逻辑理解透彻。

8. 训练效果验证与性能观察

PINN 训练完成后,不能只看 loss 变低就认为成功,还要做数值验证。上一节中我们将预测值和真实解析解画在一起做视觉比对,这是第一步。更严谨一点,可以计算 L2 相对误差:

# PyTorch 版验证 x_test = torch.linspace(0, 2, 200).reshape(-1, 1) y_pred = model(x_test).detach().numpy().flatten() y_true = np.exp(-x_test.numpy()).flatten() l2_error = np.linalg.norm(y_pred - y_true) / np.linalg.norm(y_true) print(f"L2 相对误差: {l2_error:.6e}")

如果误差在 1e-3 到 1e-2 数量级,说明训练是成功的。若误差偏大,可以增加采样点数、加大网络宽度或增加训练轮次,但要注意过拟合和训练时间成本。

性能观察也是 PINN 实操中需要关注的点。在 CPU 上运行本讲算例,训练时间通常在十几秒到几十秒之间,显存占用很低。如果有 NVIDIA GPU,训练会更快,但对于这个小算例提升幅度不会太夸张,因为网络规模和采样点数量都很小。训练时可以通过nvidia-smi查看显存占用,观察程序是否真的使用了 GPU:

nvidia-smi

如果看到 Python 进程占用显存,说明 GPU 加速生效。对于后续更大规模的二维、三维 PDE 问题,GPU 的作用会更明显,所以环境准备阶段把 GPU 版本装好是有意义的。

除了训练耗时,还需要关注损失曲线形态。理想的损失曲线是平滑下降并趋于稳定。如果损失曲线震荡剧烈或者直接上升,需要检查学习率是否过大、网络初始化是否合适、采样点分布是否合理。

9. 常见问题与排查方法

问题现象可能原因排查方式解决方案
安装 PyTorch/torchvision 后 import 报错Python 版本不兼容或 pip 源异常检查 Python 版本和 pip 源升级到 Python 3.9+,切换官方 pip 源安装
torch.cuda.is_available() 返回 FalsePyTorch 的 CUDA 版本与驱动不匹配运行nvidia-smi查看驱动版本按 PyTorch 官网选择匹配的 CUDA index-url 重装
TensorFlow 版本过高导致 API 不兼容部分旧教程代码在新版本中变更查看版本迁移文档使用稳定版 API 或固定 TensorFlow 2.x 版本
PyTorch 中backward()报错 "element 0 of tensors does not require grad"输入张量没有 requires_grad=True;或网络输出不含需要梯度的路径检查 x_train 是否设置 requires_grad,检查模型参数是否可训练确认 x_train.requires_grad_() 已调用
TensorFlow 中tape.gradient返回 None没有对输入张量执行 tape.watch确认在 pytorch 对应的是 requires_grad,而 TensorFlow 中需要显式 tape.watch(x_in)在 GradientTape 中加入 tape.watch(x_in)
损失不下降,一直维持在很大值学习率过大/过小;网络初始化不合理;采样点未覆盖求解区域打印各损失分量,观察 PDE 和 BC 哪个没有下降调小学习率,使用 Xavier/He 初始化,重采样训练点
Loss 快速降到很低,但预测曲线偏差大网络过拟合边界条件,或 PDE 残差权重过小检查 loss_pde 和 loss_bc 比例;直接绘制预测曲线调整损失权重,让 PDE 和 BC 在损失中保持平衡,比如 1:1 或按问题特征加权
显存不足 (CUDA out of memory)网络规模或训练 batch 过大查看 nvidia-smi 中显存占用降低 batch size,使用小网络,或者切换到 CPU 先验证逻辑
训练结果每次复现不一致随机初始化导致随机性固定随机种子设置 torch.manual_seed(42) 或 tf.random.set_seed(42)

PINN 训练中还有一个非常典型的坑:在 PyTorch 的autograd.grad中忘记设置create_graph=True。如果我们需要高阶导数,比如求解二阶扩散方程 u_xx,必须让一阶导数的计算图被保留下来,否则无法继续求二阶导。这个点是初学 PINN 最容易忽略的。

# 二阶导数示例 def second_derivative(model, x): x.requires_grad_(True) u = model(x) # 一阶导:需要 create_graph=True,否则不能继续求二阶导 u_x = torch.autograd.grad(u, x, grad_outputs=torch.ones_like(u), create_graph=True)[0] u_xx = torch.autograd.grad(u_x, x, grad_outputs=torch.ones_like(u_x), create_graph=True)[0] return u_xx

在 TensorFlow 中同样可以在嵌套的 GradientTape 中实现高阶导数:

def second_derivative_tf(model, x): with tf.GradientTape() as tape1: tape1.watch(x) with tf.GradientTape() as tape2: tape2.watch(x) u = model(x) u_x = tape2.gradient(u, x) u_xx = tape1.gradient(u_x, x) return u_xx

这两个例子已经足够覆盖绝大多数 PINN 编程需求了。后面遇到热传导方程、波动方程、扩散方程时,你只需要把这里的导数阶数、残差项和数据项替换成对应方程形式即可。

10. PINN 工程化最佳实践

PINN 从“跑通一个算例”到“真正解决一个 PDE 问题”,中间有不少工程化细节值得提前注意。这里分享几条对新手最实用的建议。

第一条,保持一个最小可用模板。本讲的 PyTorch 和 TensorFlow 代码就是很好的起点,把网络定义、损失函数、训练循环拆成清晰函数或模块,以后遇到新问题只需要修改方程残差的部分。不要每次从零写脚本,那样容易引入低级错误。

第二条,训练前先做归一化。坐标 x 的取值范围如果过大,神经网络很难收敛。一般建议把求解区域归一化到 [-1, 1] 或 [0, 1] 区间。对于物理量 y,如果真值尺度过大或过小,也可以做无量纲化处理。虽然本讲算例范围是 [0, 2],不归一化也能收敛,但二维、三维问题会越来越敏感。

第三条,用损失分项日志替代单一总 loss。PINN 训练时,把 PDE 残差、边界条件、数据拟合项分开打印。很多时候总 loss 在下降,但边界条件这一项已经发散,说明权重失衡。看到各分项数值变化,才能判断问题出在哪。

第四条,固定随机种子。PINN 训练结果受网络初始化和采样点分布影响明显。固定随机种子能保证实验可复现,这在写论文、调参数、比较算法时非常关键。

第五条,关于批量任务和参数扫描。如果你的目标是系统的参数研究,比如改变方程系数、边界条件、网络宽度、学习率,建议写一个简单的 shell 循环或 Python 脚本,把采样点数量、训练轮次、网络层数作为参数传入,并把训练日志和模型权重保存到独立目录。这样自动化跑实验会高效很多。

# 伪代码示例:批量参数扫描 for hidden_size in 10 20 50; do python train_pinn.py --hidden_size $hidden_size --epochs 3000 done

第六条,生产环境部署时,留意模型导出和推理速度。PyTorch 可以导出 TorchScript 或 ONNX 格式,TensorFlow 可以使用 SavedModel 格式,都能做轻量化推理。不过 PINN 最主要的应用场景还是“求解+验证”,模型导出不是每篇文章都覆盖的重点,但如有部署需求,从第一步就使用干净的代码结构会带来方便。

11. 总结与下一步

本讲把 PINN 和两个主流深度学习框架之间的关系讲清楚了。我们分别用 PyTorch 和 TensorFlow 实现了同一个一阶 ODE 算例,走通了“定义网络、计算 PDE 残差、施加边界条件、训练、可视化验证”的完整流程。两个框架的核心逻辑完全一致,区别只在自动微分的 API 写法上。PyTorch 用torch.autograd.grad,TensorFlow 用tf.GradientTape,理解了这两者的用法,后续无论换哪个框架写 PDE 求解,思路都能平移。

如果你是刚接触 PINN,建议先在本讲代码上做两件事。第一件,把这个一阶 ODE 改成二阶 ODE,比如 u'' + u = 0,u(0)=1,u'(0)=0,体会一下高阶导数如何计算。第二件,把训练采样方式从固定网格点改成每次迭代随机采样,观察训练稳定性和收敛速度的变化。跑通这些改动后,再进入热传导方程、二维 Poisson 方程等真正的 PDE 算例。

下一讲我们会把 PINN 扩展到一个真正有用的 PDE 场景:一维 Burgers 方程或一维热传导方程。到那时,网络结构、损失权重、时间项离散这几个问题会一起出现,建议收藏本讲代码作为基础模板。如果你手头正好有正在学习 PINN 的朋友,这篇框架对比笔记可以直接转给他参考。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/31 7:33:54

HyperMesh 14.0汽车内外饰件快速建模实战指南

这次我们来看一个 CAE 前处理场景里的高频需求:汽车内外饰件快速建模。这里的“建模”不是三维造型,而是把 CAD 数据转换成可用于仿真分析的高质量有限元网格模型。HyperMesh 14.0 从几何清理、中面抽取、网格划分到连接创建,基本都能在一个软…

作者头像 李华
网站建设 2026/8/31 7:30:57

全桥LLC谐振变换器开环仿真:Simulink搭建与波形解读

各位做电力电子仿真和电源设计的朋友,大家好! 在 DC-DC 变换器的学习与研发中,LLC 谐振变换器一直是非常热门且实用的拓扑。无论是通信电源、服务器电源,还是新能源汽车的车载充电机(OBC),都能…

作者头像 李华
网站建设 2026/8/31 7:30:08

Matlab实现密度加权相控阵天线阵列综合

简介:本资源是一份面向通信工程、雷达系统及天线设计领域初学者与中级工程师的MATLAB实践代码包,聚焦相控阵天线中圆周阵列的密度加权优化方法,用于提升旁瓣抑制比、改善方向图对称性与波束可控性。压缩包共含5个文件(4个.m脚本1个…

作者头像 李华
网站建设 2026/8/31 7:26:57

MATLAB自相关函数详解:从信号方差协方差到周期检测与功率谱

简介:本资源是一套面向信号处理初学者与MATLAB实践者的教学辅助代码包,聚焦自相关、协方差、信号方差等核心统计特性分析,解决理论理解抽象、公式推导与实际计算脱节的学习痛点,适用于通信、电子、自动化等专业本科生及工程入门者…

作者头像 李华
网站建设 2026/8/31 7:25:08

Nginx 与 LVS 软件负载均衡对比及 Java 项目实践

Nginx 与 LVS 软件负载均衡对比及 Java 项目实践软件负载均衡常见方案:Nginx(7 层负载均衡)与 LVS(Linux 内核 4 层负载均衡)。 本文梳理两者区别、选型方法、与 Java 项目搭配使用方式,以及 Nginx「插件」…

作者头像 李华
网站建设 2026/8/31 7:23:54

贴牌“天气丹风”高端面霜,代工厂你没问清这三件事就等着退货吧

前两天车间里来了个客户,拎着个韩系高端抗老套盒的空瓶子进门,开口就问:这套韩系高端抗老套盒体系贴牌做一套得多少钱。▼ 源头车间质检备案与合作授权说明 ▼我没接话,先反问他一句话——你要做的是“韩系高端抗老套盒体系风格”…

作者头像 李华