news 2026/5/14 17:40:06

用PyTorch手把手教你搭建第一个PINN:从解f‘(x)=f(x)开始(附完整代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用PyTorch手把手教你搭建第一个PINN:从解f‘(x)=f(x)开始(附完整代码)

用PyTorch从零构建PINN:求解f'(x)=f(x)的实战指南

在科学计算与工程领域,微分方程求解一直是个核心挑战。传统数值方法如有限差分法虽然成熟,但面对复杂问题时往往计算成本高昂。物理信息神经网络(PINN)的出现,为这一领域带来了全新的思路——它巧妙地将物理定律编码到神经网络中,让AI直接学习微分方程的解。本文将带你用PyTorch一步步实现一个PINN,求解经典的微分方程f'(x)=f(x),直观感受神经网络如何"学会"指数函数的解。

1. 环境准备与问题定义

在开始编码前,我们需要明确几个关键概念。PINN的核心思想是将微分方程作为神经网络的约束条件,通过优化网络参数来满足这些约束。对于方程f'(x)=f(x),其解析解为f(x)=Ce^x(C为常数),这将成为我们验证PINN效果的重要参照。

首先配置基础环境:

import torch import torch.nn as nn import numpy as np import matplotlib.pyplot as plt from torch.autograd import grad print(f"PyTorch版本: {torch.__version__}") print(f"CUDA可用: {torch.cuda.is_available()}")

提示:虽然可以在CPU上运行本教程,但使用GPU(如Colab的免费GPU)可以显著加快训练速度。

我们需要定义问题的数学表述:

  • 微分方程:f'(x) - f(x) = 0
  • 边界条件:f(0) = 1(这将确定常数C=1)
  • 定义域:x ∈ [0, 2]

2. 构建神经网络模型

PINN的基础是一个全连接神经网络,它将坐标x作为输入,输出对应的函数值f(x)。网络结构不需要太复杂,通常2-3个隐藏层就足够:

class PINN(nn.Module): def __init__(self, layers=[1, 20, 20, 1]): super().__init__() self.net = nn.Sequential( nn.Linear(layers[0], layers[1]), nn.Tanh(), nn.Linear(layers[1], layers[2]), nn.Tanh(), nn.Linear(layers[2], layers[3]) ) def forward(self, x): return self.net(x)

这个网络有几个关键设计选择:

  1. 激活函数:使用tanh而非ReLU,因为tanh的平滑性更适合微分运算
  2. 层宽度:20个神经元提供了足够的表达能力
  3. 输出层:线性激活,不限制输出范围

我们可以实例化模型并检查其参数数量:

model = PINN() total_params = sum(p.numel() for p in model.parameters()) print(f"模型总参数: {total_params}")

3. 设计损失函数

PINN的独特之处在于其损失函数的设计,它需要同时考虑:

  1. 微分方程残差:衡量网络输出是否满足f'(x)=f(x)
  2. 边界条件:确保网络在边界点(x=0)输出正确值

首先定义微分方程残差的计算:

def compute_residual(model, x): x.requires_grad_(True) f = model(x) f_x = grad(f, x, torch.ones_like(f), create_graph=True)[0] residual = f_x - f return residual

然后组合边界条件和微分方程残差:

def loss_fn(model, x_domain, x_bc): # 边界条件损失 f_pred_bc = model(x_bc) loss_bc = torch.mean((f_pred_bc - 1.0)**2) # f(0)=1 # 微分方程残差损失 residual = compute_residual(model, x_domain) loss_pde = torch.mean(residual**2) # 总损失 return loss_bc + loss_pde

注意:这里我们简单地将两项损失相加,在实际应用中可能需要调整权重。

4. 训练流程实现

训练PINN需要特别关注采样策略和学习率调度:

def train(model, epochs=5000, lr=0.01): optimizer = torch.optim.Adam(model.parameters(), lr=lr) scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, 'min', patience=500, factor=0.5, verbose=True) # 训练数据 x_domain = torch.linspace(0, 2, 100).view(-1, 1) x_bc = torch.tensor([[0.0]], requires_grad=True) losses = [] for epoch in range(epochs): optimizer.zero_grad() loss = loss_fn(model, x_domain, x_bc) loss.backward() optimizer.step() scheduler.step(loss) if epoch % 500 == 0: print(f"Epoch {epoch}: Loss = {loss.item():.4f}") losses.append(loss.item()) return losses

训练过程的关键参数:

参数说明
epochs5000训练轮次
lr0.01初始学习率
采样点100定义域内均匀采样
优化器Adam自适应学习率优化器
调度器ReduceLROnPlateau损失平台时降低学习率

开始训练并监控损失:

model = PINN() loss_history = train(model) plt.plot(loss_history) plt.yscale('log') plt.xlabel('Epoch') plt.ylabel('Loss (log scale)') plt.title('Training Loss History') plt.show()

5. 结果分析与可视化

训练完成后,我们可以将PINN的预测与解析解进行对比:

def plot_results(model): x_test = torch.linspace(0, 2, 100).view(-1, 1) with torch.no_grad(): f_pred = model(x_test).numpy() x_test = x_test.numpy() f_exact = np.exp(x_test) plt.figure(figsize=(10, 6)) plt.plot(x_test, f_exact, 'b-', linewidth=2, label='Exact solution') plt.plot(x_test, f_pred, 'r--', linewidth=2, label='PINN prediction') plt.xlabel('x') plt.ylabel('f(x)') plt.legend() plt.title('Comparison of PINN prediction and exact solution') plt.show() plot_results(model)

为了更深入理解PINN的学习过程,我们可以检查不同训练阶段的结果:

def plot_training_progress(): model = PINN() x_test = torch.linspace(0, 2, 100).view(-1, 1) plt.figure(figsize=(10, 6)) for epoch in [0, 100, 500, 1000, 5000]: train(model, epochs=epoch) with torch.no_grad(): f_pred = model(x_test).numpy() plt.plot(x_test.numpy(), f_pred, label=f'Epoch {epoch}') f_exact = np.exp(x_test.numpy()) plt.plot(x_test.numpy(), f_exact, 'k-', linewidth=3, label='Exact') plt.legend() plt.xlabel('x') plt.ylabel('f(x)') plt.title('PINN Learning Progress') plt.show() plot_training_progress()

6. 高级技巧与优化建议

在实际应用中,有几个技巧可以提升PINN的性能:

  1. 自适应采样

    • 在残差大的区域增加采样点
    • 动态调整训练过程中的采样分布
  2. 损失权重调整

    • 为边界条件和PDE残差分配不同权重
    • 可以基于误差分析动态调整
  3. 网络架构改进

    • 使用残差连接改善梯度流动
    • 尝试不同的激活函数组合
  4. 集成学习

    • 训练多个网络取平均
    • 减少随机初始化带来的方差

实现自适应权重的示例代码:

class AdaptiveWeightLoss: def __init__(self, initial_weight=1.0): self.weight = torch.tensor(initial_weight) self.lambda_history = [] def update(self, loss_pde, loss_bc): # 简单的启发式更新规则 ratio = loss_pde.detach() / loss_bc.detach() self.weight = 0.9 * self.weight + 0.1 * ratio self.lambda_history.append(self.weight.item()) return self.weight

7. 扩展到更复杂问题

掌握了这个基础案例后,你可以尝试将PINN应用于更复杂的问题:

  • 高阶微分方程:如f''(x) + f(x) = 0
  • 偏微分方程:如热传导方程、波动方程
  • 参数反问题:同时学习方程解和未知参数
  • 多物理场耦合问题:如流体-结构相互作用

例如,求解二阶ODE的修改点:

def compute_residual_second_order(model, x): x.requires_grad_(True) f = model(x) f_x = grad(f, x, torch.ones_like(f), create_graph=True)[0] f_xx = grad(f_x, x, torch.ones_like(f_x), create_graph=True)[0] residual = f_xx + f # f'' + f = 0 return residual

在实现这些扩展时,要注意梯度计算可能会变得更加敏感,可能需要调整网络架构和训练策略。

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

深度解析:GroundingDINO配置文件实战选择与性能优化策略

深度解析:GroundingDINO配置文件实战选择与性能优化策略 【免费下载链接】GroundingDINO [ECCV 2024] Official implementation of the paper "Grounding DINO: Marrying DINO with Grounded Pre-Training for Open-Set Object Detection" 项目地址: ht…

作者头像 李华
网站建设 2026/5/14 17:36:02

终极指南:如何用缠论量化插件实现精准交易分析

终极指南:如何用缠论量化插件实现精准交易分析 【免费下载链接】Indicator 通达信缠论可视化分析插件 项目地址: https://gitcode.com/gh_mirrors/ind/Indicator 通达信缠论可视化分析插件是一款将复杂的缠论技术分析理论转化为直观交易工具的专业软件。这款…

作者头像 李华
网站建设 2026/5/14 17:32:25

对比Token Plan套餐与按量计费如何帮助控制项目预算

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 对比Token Plan套餐与按量计费如何帮助控制项目预算 在项目开发与运营中,大模型API的调用成本是重要的考量因素。如何让…

作者头像 李华