物理信息神经网络(PINN):融合物理规律与深度学习的工程建模范式
物理信息神经网络(PINN)通过将物理偏微分方程嵌入损失函数,有效解决了传统深度学习模型在工程应用中的两大痛点:对海量标注数据的依赖和预测结果违背物理规律的问题。本文以锂电池锂离子扩散的质量守恒问题为例,详细阐述了PINN的实现方法,包括:1)将扩散方程、边界条件等物理约束转化为损失项;2)构建多层感知机网络来拟合浓度分布;3)设计加权损失函数平衡数据拟合与物理约束;4)提供完整的训练流程和可视化
引言
传统深度学习模型依赖海量标注数据,且易产生违背物理常识的预测结果(如电池健康度随使用时间上升、流体速度违反动量守恒),在工程场景中难以落地。物理信息神经网络(Physics-Informed Neural Networks, PINN)通过将质量守恒、能量守恒、动量守恒等物理偏微分方程(PDE)直接嵌入损失函数,让模型在梯度下降过程中同时满足数据拟合与物理规律约束。即使在实验数据极少(甚至无数据)的情况下,PINN 仍能沿物理规律收敛,完美结合了传统工科的领域知识与现代机器学习的非线性表达能力。
本文以锂电池锂离子扩散的质量守恒问题为核心案例,从原理剖析、代码实现、逐行解析、工程价值四个维度,系统讲解 PINN 的设计思路与落地方法,所有代码均提供教师级别的详细注释与逻辑分析。
一、PINN 核心原理
1.1 核心思想
PINN 不改变神经网络的基础结构(如 MLP/LSTM/CNN),而是通过损失函数的 “软约束” 实现物理规律的嵌入:
- 数据拟合损失:保证模型贴合少量实验数据;
- PDE 残差损失:惩罚违背核心物理方程的预测结果;
- 边界 / 初始条件损失:强制模型满足物理问题的约束前提。
总损失函数公式:Total Loss=λdata⋅Lossdata+λpde⋅Losspde+λbc⋅Lossbc
1.2 问题定义(锂电池锂离子扩散)
以电池电极中锂离子一维扩散的质量守恒问题为例:
- 核心 PDE(扩散方程):∂t∂c=D⋅∂x2∂2c(c为锂离子浓度,D为扩散系数,x为空间坐标,t为时间);
- 边界条件:x=0时c=0(电极表面浓度为 0),x=L时∂x∂c=0(电极内部无通量);
- 初始条件:t=0时c=c0(初始浓度分布)。
二、完整代码实现与教师级解析
2.1 环境准备
依赖库安装:
bash
运行
pip install numpy torch matplotlib
2.2 代码整体结构
预览
查看代码
物理PDE定义
PINN网络构建
损失函数设计
模型训练
结果可视化验证
graph TD
A[物理PDE定义] --> B[PINN网络构建]
B --> C[损失函数设计]
C --> D[模型训练]
D --> E[结果可视化验证]
物理PDE定义
PINN网络构建
损失函数设计
模型训练
结果可视化验证
![]()
豆包
你的 AI 助手,助力每日工作学习
2.3 模块 1:物理 PDE 与约束定义
python
运行
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from torch.autograd import grad
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# 设备配置:优先使用GPU加速(符合并行计算求解PDE的工程需求)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
torch.manual_seed(42) # 固定随机种子,保证实验可复现
class BatteryPDE:
"""
锂电池锂离子扩散的质量守恒PDE定义类
核心功能:
1. 定义扩散方程、边界条件、初始条件
2. 计算PDE残差(违背物理规律的程度)
3. 计算边界/初始条件损失(违背约束的惩罚)
"""
def __init__(self, D=1e-10, L=1e-4, c0=1000.0):
"""
初始化物理常数(电池电化学领域的基础参数)
:param D: 锂离子扩散系数 (m²/s),工程上典型值为1e-10量级
:param L: 电极厚度 (m),微米级电极对应1e-4 m
:param c0: 初始锂离子浓度 (mol/m³),典型值为1000 mol/m³
"""
self.D = D # 核心物理参数:扩散系数
self.L = L # 几何约束:电极厚度
self.c0 = c0 # 初始状态约束:初始浓度
def pde_residual(self, x, t, c_pred):
"""
计算PDE残差(核心:衡量预测结果违背质量守恒的程度)
残差公式:residual = ∂c/∂t - D·∂²c/∂x²
残差为0 → 完全满足扩散方程(质量守恒)
:param x: 空间坐标张量 (batch, 1)
:param t: 时间张量 (batch, 1)
:param c_pred: 模型预测的浓度张量 (batch, 1)
:return: residual: PDE残差张量 (batch, 1)
"""
# 1. 计算一阶偏导数 ∂c/∂t(对时间的偏导)
# grad参数说明:
# - outputs: 被求导的张量(c_pred)
# - inputs: 求导的自变量(t)
# - grad_outputs: 梯度权重(全1张量,保证梯度方向正确)
# - create_graph: 保留计算图,让残差的梯度能反向传播到网络权重
c_t = grad(c_pred, t, grad_outputs=torch.ones_like(c_pred), create_graph=True)[0]
# 2. 计算一阶偏导数 ∂c/∂x(对空间的偏导)
c_x = grad(c_pred, x, grad_outputs=torch.ones_like(c_pred), create_graph=True)[0]
# 3. 计算二阶偏导数 ∂²c/∂x²(对空间的二阶偏导)
c_xx = grad(c_x, x, grad_outputs=torch.ones_like(c_x), create_graph=True)[0]
# 4. 计算PDE残差(核心:质量守恒约束)
residual = c_t - self.D * c_xx
return residual
def boundary_condition_loss(self, x, t, c_pred):
"""
计算边界/初始条件损失(硬约束:违背则施加强惩罚)
:param x: 空间坐标张量
:param t: 时间张量
:param c_pred: 预测浓度张量
:return: bc_loss: 边界条件损失标量
"""
bc_loss = 0.0 # 初始化边界损失
# 约束1:x=0时,c=0(电极表面锂离子浓度为0)
# 掩码筛选x≈0的点(浮点精度容忍:<1e-6)
x0_mask = (x < 1e-6).squeeze()
if torch.any(x0_mask): # 仅当存在边界点时计算损失
c_x0 = c_pred[x0_mask]
bc_loss += torch.mean(c_x0 **2) # MSE损失:预测值与0的差的平方均值
# 约束2:x=L时,∂c/∂x=0(电极内部无锂离子通量)
xL_mask = (torch.abs(x - self.L) < 1e-6).squeeze()
if torch.any(xL_mask):
# 计算x=L处的一阶偏导数 ∂c/∂x
c_x = grad(c_pred[xL_mask], x[xL_mask], grad_outputs=torch.ones_like(c_pred[xL_mask]), create_graph=True)[0]
bc_loss += torch.mean(c_x** 2) # MSE损失:偏导数与0的差的平方均值
# 约束3:t=0时,c=c0(初始时刻的浓度分布)
t0_mask = (t < 1e-6).squeeze()
if torch.any(t0_mask):
c_t0 = c_pred[t0_mask]
bc_loss += torch.mean((c_t0 - self.c0) **2) # MSE损失:预测值与初始浓度的差的平方均值
return bc_loss
代码解析(模块 1):
- 物理常数初始化:所有参数均为锂电池电化学的工程典型值,保证物理意义的合理性;
- PDE 残差计算:
- 利用 PyTorch 自动微分替代传统数值方法的手动离散化,无需推导有限元 / 有限差分格式;
create_graph=True是核心:保留梯度计算图,让 PDE 残差的梯度能反向传播到神经网络权重,实现物理约束对训练的指导;
- 边界条件损失:
- 采用掩码筛选边界点,避免无关点干扰;
- 对每个约束项计算 MSE 损失并累加,违背约束则损失值剧增(即 “强惩罚”)。
2.4 模块 2:PINN 神经网络构建
python
运行
class PINN(nn.Module):
"""
物理信息神经网络(PINN):用MLP拟合浓度分布c(x,t),同时满足PDE约束
输入:x(空间)、t(时间)→ 输出:c(锂离子浓度)
"""
def __init__(self, input_dim=2, hidden_dim=128, output_dim=1):
"""
初始化PINN网络
:param input_dim: 输入维度(x+t → 2维)
:param hidden_dim: 隐藏层维度(128为经验值,可根据问题复杂度调整)
:param output_dim: 输出维度(浓度c → 1维)
"""
super(PINN, self).__init__()
# 全连接网络(MLP):简单但能拟合复杂的时空函数
# 选择Tanh激活函数的原因:输出范围对称(-1,1),适合拟合物理量的连续分布
self.net = nn.Sequential(
nn.Linear(input_dim, hidden_dim), # 输入层:2→128
nn.Tanh(), # 激活函数
nn.Linear(hidden_dim, hidden_dim),# 隐藏层1:128→128
nn.Tanh(), # 激活函数
nn.Linear(hidden_dim, hidden_dim),# 隐藏层2:128→128
nn.Tanh(), # 激活函数
nn.Linear(hidden_dim, output_dim) # 输出层:128→1
)
def forward(self, x, t):
"""
前向传播:输入时空坐标,输出浓度预测
:param x: 空间坐标张量 (batch, 1)
:param t: 时间坐标张量 (batch, 1)
:return: c_pred: 浓度预测张量 (batch, 1)
"""
# 拼接x和t作为网络输入(时空联合输入)
input_ = torch.cat([x, t], dim=1)
# 网络前向计算
c_pred = self.net(input_)
return c_pred
代码解析(模块 2):
- 网络结构选择:采用 MLP(多层感知机)而非 CNN/LSTM,因为一维扩散问题的时空映射关系可由 MLP 充分拟合;若扩展到高维 / 动态场景,可替换为 CNN(空间特征)+ LSTM(时间特征),核心逻辑不变;
- 激活函数:Tanh 函数的输出范围对称,避免 ReLU 的 “死亡神经元” 问题,更适合拟合物理量的连续分布;
- 输入拼接:将空间x和时间t拼接为 2 维输入,让网络学习时空联合的浓度分布c(x,t)。
2.5 模块 3:PINN 训练流程
python
运行
def train_pinn(use_data=True, num_data_points=50):
"""
训练PINN模型(支持有数据/无数据两种场景)
:param use_data: 是否使用实验数据(True=少量数据,False=纯PDE约束)
:param num_data_points: 实验数据点数量(模拟工程中少量的实验数据)
:return: trained_model: 训练好的PINN模型, pde: PDE定义类实例
"""
# 1. 初始化PDE和PINN模型
pde = BatteryPDE(D=1e-10, L=1e-4, c0=1000.0) # 物理规律实例化
model = PINN(input_dim=2, hidden_dim=128).to(device) # 网络实例化并移至设备
optimizer = optim.Adam(model.parameters(), lr=1e-4) # 优化器(Adam适合非凸优化)
# 学习率调度器:每1000轮学习率衰减10%,避免后期震荡
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=1000, gamma=0.9)
# 2. 生成训练点(两类采样点:PDE约束点 + 实验数据点)
# 2.1 PDE约束采样点(1000个,远多于实验数据,保证物理约束的主导性)
num_pde_points = 1000
x_pde = torch.rand(num_pde_points, 1).to(device) * pde.L # x∈[0, L]
t_pde = torch.rand(num_pde_points, 1).to(device) * 3600 # t∈[0, 1小时]
# 关键:开启梯度计算,才能对x/t求偏导(计算PDE残差)
x_pde.requires_grad = True
t_pde.requires_grad = True
# 2.2 实验数据点(模拟工程中少量的实验测量数据)
if use_data:
# 生成少量时空采样点(50个,模拟“可怜的实验数据”)
x_data = torch.rand(num_data_points, 1).to(device) * pde.L
t_data = torch.rand(num_data_points, 1).to(device) * 3600
# 用扩散方程解析解生成“真实”浓度(加噪声模拟实验测量误差)
sqrt_Dt = torch.sqrt(pde.D * t_data + 1e-8) # 避免除0
c_true = pde.c0 * torch.erfc(x_data / (2 * sqrt_Dt)) # 解析解
c_data = c_true + torch.randn_like(c_true) * 0.05 * pde.c0 # 加5%噪声
else:
x_data, t_data, c_data = None, None, None # 无数据时置空
# 3. 训练参数配置
epochs = 5000 # 训练轮数(PINN需足够轮数收敛到物理解)
# 损失权重:平衡数据拟合与物理约束的优先级
lambda_data = 10.0 # 数据损失权重(少量数据需加大权重)
lambda_pde = 1.0 # PDE残差损失权重(核心物理约束)
lambda_bc = 10.0 # 边界条件损失权重(硬约束需加大权重)
model.train() # 模型设为训练模式
# 4. 训练循环
loss_history = [] # 记录损失变化
for epoch in range(epochs):
optimizer.zero_grad() # 清空梯度(避免累积)
# ---------------------- 损失1:PDE残差损失(核心物理约束) ----------------------
c_pde_pred = model(x_pde, t_pde) # 预测PDE采样点的浓度
pde_residual = pde.pde_residual(x_pde, t_pde, c_pde_pred) # 计算PDE残差
loss_pde = torch.mean(pde_residual **2) # 残差的MSE损失
# ---------------------- 损失2:边界/初始条件损失 ----------------------
loss_bc = pde.boundary_condition_loss(x_pde, t_pde, c_pde_pred)
# ---------------------- 损失3:实验数据拟合损失(可选) ----------------------
if use_data:
c_data_pred = model(x_data, t_data) # 预测实验数据点的浓度
loss_data = torch.mean((c_data_pred - c_data) **2) # 数据拟合MSE损失
else:
loss_data = torch.tensor(0.0).to(device) # 无数据时损失为0
# ---------------------- 总损失:加权求和 ----------------------
total_loss = lambda_data * loss_data + lambda_pde * loss_pde + lambda_bc * loss_bc
# ---------------------- 反向传播与权重更新 ----------------------
total_loss.backward() # 反向传播:计算总损失对网络权重的梯度
optimizer.step() # 更新权重:最小化总损失
scheduler.step() # 学习率衰减
# ---------------------- 记录与打印 ----------------------
loss_history.append(total_loss.item())
if (epoch + 1) % 500 == 0: # 每500轮打印一次
print(f"Epoch [{epoch+1}/{epochs}], Total Loss: {total_loss.item():.6f}, "
f"PDE Loss: {loss_pde.item():.6f}, Data Loss: {loss_data.item():.6f}")
# 可视化训练损失(对数坐标更易观察收敛趋势)
plt.plot(loss_history)
plt.xlabel("Epoch")
plt.ylabel("Total Loss (Log Scale)")
plt.title("PINN Training Loss (PDE + Data Constraint)")
plt.yscale("log")
plt.grid(True)
plt.show()
return model, pde
代码解析(模块 3):
- 采样点设计:
- PDE 约束点(1000 个):覆盖全时空域,保证物理约束的全局性;
- 实验数据点(50 个):模拟工程中少量、带噪声的实验测量数据;
- 损失权重设计:
- λdata=10:少量数据需加大权重,保证模型贴合实验测量值;
- λpde=1:核心物理约束的基础权重;
- λbc=10:边界 / 初始条件为硬约束,加大权重强制满足;
- 训练逻辑:
- 反向传播时,PDE 残差和边界损失的梯度会传递到网络权重,强制模型调整参数以满足物理规律;
- 无数据时(
use_data=False),模型仅通过最小化 PDE+BC 损失收敛到物理解,实现 “无数据求解 PDE”。
2.6 模块 4:结果可视化与验证
python
运行
def visualize_pinn(model, pde):
"""
可视化PINN预测的浓度分布,验证物理合理性
:param model: 训练好的PINN模型
:param pde: PDE定义类实例
"""
model.eval() # 模型设为评估模式(关闭Dropout等训练层)
# 1. 生成时空网格(覆盖全定义域,用于可视化)
x_grid = np.linspace(0, pde.L, 100) # 空间网格:0→L,100个点
t_grid = np.linspace(0, 3600, 50) # 时间网格:0→1小时,50个点
X, T = np.meshgrid(x_grid, t_grid) # 生成二维网格
# 2. 转换为张量并移至设备
x_tensor = torch.tensor(X.flatten(), dtype=torch.float32).reshape(-1, 1).to(device)
t_tensor = torch.tensor(T.flatten(), dtype=torch.float32).reshape(-1, 1).to(device)
# 3. 模型预测(无梯度计算,提升速度)
with torch.no_grad():
c_pred = model(x_tensor, t_tensor).cpu().numpy().reshape(X.shape)
# 4. 3D可视化浓度分布(时空全局视图)
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111, projection='3d')
# 绘图参数:X*1e4将m转换为1e-4 m(工程常用单位),T/3600将s转换为h
surf = ax.plot_surface(X*1e4, T/3600, c_pred, cmap='viridis', edgecolor='none')
ax.set_xlabel('Position (1e-4 m)')
ax.set_ylabel('Time (h)')
ax.set_zlabel('Li+ Concentration (mol/m³)')
ax.set_title('PINN Predicted Li+ Concentration (Mass Conservation)')
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
# 5. 切片可视化(t=0.5h时的浓度分布,验证物理趋势)
t_idx = np.where(t_grid == t_grid[25])[0][0] # 选择t=0.5h的切片
plt.figure(figsize=(8, 5))
plt.plot(X[t_idx]*1e4, c_pred[t_idx], 'b-', label='PINN Prediction')
plt.axhline(y=0, color='r', linestyle='--', label='x=0 Boundary (c=0)')
plt.xlabel('Position (1e-4 m)')
plt.ylabel('Li+ Concentration (mol/m³)')
plt.title(f'Li+ Concentration at t={t_grid[t_idx]/3600:.1f}h')
plt.legend()
plt.grid(True)
plt.show()
# 主程序:执行训练与可视化
if __name__ == "__main__":
# 场景1:少量实验数据 + PDE约束(工程中最常见场景)
print("===== 训练场景1:少量数据 + PDE约束 =====")
model_with_data, pde = train_pinn(use_data=True, num_data_points=50)
visualize_pinn(model_with_data, pde)
# 场景2:无实验数据,仅PDE+边界条件约束(验证物理约束的主导性)
print("\n===== 训练场景2:无数据 + PDE约束 =====")
model_no_data, pde = train_pinn(use_data=False)
visualize_pinn(model_no_data, pde)
代码解析(模块 4):
- 可视化设计:
- 3D 视图:展示浓度随时间、空间的全局分布,直观验证物理趋势(如浓度随时间扩散、随空间递增);
- 切片视图:验证特定时间点的浓度分布是否符合边界条件(x=0 处 c=0);
- 无梯度计算:
torch.no_grad()关闭梯度计算,避免不必要的内存消耗; - 双场景验证:
- 场景 1:少量数据 + PDE 约束 → 模型既贴合数据又符合物理;
- 场景 2:无数据 + PDE 约束 → 模型仅通过物理规律收敛到合理解。
三、PINN 的工程价值与扩展
3.1 核心优势总结
表格
| 维度 | 传统深度学习 | PINN |
|---|---|---|
| 数据需求 | 海量 | 极少(几十条)甚至无数据 |
| 物理合理性 | 易违背 | 强制满足守恒律 / 边界条件 |
| 泛化能力 | 差(换工况崩) | 强(基于通用物理规律) |
| 可解释性 | 黑箱 | 高(输出物理参数 + 方程) |
| 工程落地难度 | 高 | 低(符合工程师的物理直觉) |
3.2 扩展方向
- 多物理场耦合:将动量守恒、能量守恒的 PDE 加入损失函数,适配电池热 - 电 - 化学耦合问题;
- 复杂网络结构:替换 MLP 为 CNN/LSTM,适配高维空间(如二维电极)、动态时序(如快充工况);
- 工业级优化:
- 采用自适应采样(如梯度提升采样)提升 PDE 约束点的效率;
- 结合迁移学习,将预训练的 PINN 模型迁移到不同电池型号 / 工况。
四、总结
物理信息神经网络(PINN)通过将物理偏微分方程嵌入损失函数,无需修改神经网络结构即可实现 “数据拟合 + 物理约束” 的双重目标。本文以锂电池锂离子扩散的质量守恒问题为例,从物理建模、网络构建、损失设计、训练验证四个维度,提供了教师级别的代码解析与工程化实现方案。
PINN 的核心价值在于:用现代机器学习的并行计算能力加速求解传统工科的偏微分方程,即使在实验数据极少的工程场景中,也能保证预测结果的物理合理性与泛化能力,完美解决了纯数据驱动模型 “瞎算”“乱抖” 的问题,是连接传统工科与现代 AI 的桥梁。
更多推荐

所有评论(0)