PyTorch实现简单神经网络求解ODE结果不正确原因排查
问题根源定位
你的怀疑完全正确,核心错误出在导数计算环节,同时还存在神经网络结构设计、权重维度、输入处理的多处连带bug,具体如下:
- 第一处致命错误:
torch.gradient完全无法用于PINN(物理信息神经网络)的导数计算torch.gradient是基于相邻点数值做有限差分近似的工具,计算过程完全不接入PyTorch自动求导的计算图,反向传播时根本无法把导数的误差正确回传给网络权重,训练过程中权重更新方向从根源上就是错的。求解这类ODE的导数必须用torch.autograd.grad,它才是走计算图的自动微分实现,能正确建立网络输出对输入的导数依赖关系。
正确的导数计算写法参考:preds_gradient = torch.autograd.grad( outputs=preds, inputs=inputs, grad_outputs=torch.ones_like(preds), create_graph=True )[0] - 第二处错误:网络权重维度设计完全错误
你定义W1时把第一维设成了采样点数量、W4输出维度也绑定了采样点长度50,相当于网络输出维度和采样点强绑定,换一组采样点网络直接无法运行,且矩阵乘法逻辑完全不符合全连接层规范。标准全连接层权重形状应为(上一层神经元数, 当前层神经元数):输入是单个x值(维度为1)时,第一层权重形状应为(1, neuron_1),最后一层输出单个ψ值(维度为1),权重形状应为(neuron_3, 1),不需要和采样点数量产生任何关联。 - 第三处错误:输入类型处理无效
代码中写了inputs.float()但没有把返回值赋值回inputs变量,实际上inputs后续还是双精度类型,和单精度的权重计算时会出现隐式类型转换问题,干扰训练稳定性。 - 第四处错误:梯度清零逻辑不全
你只清空了各层权重的梯度,没有清空开了requires_grad=True的输入张量的梯度,每轮反向传播后输入的梯度会持续累积,干扰后续轮次的梯度计算。 - 额外优化点:手写全连接层、手写参数更新逻辑非常容易出现维度bug、更新逻辑错误,直接用PyTorch自带的
nn.Module搭建网络、调用封装好的优化器更新参数稳定性会高很多;另外学习率1e-2搭配sigmoid激活偏大,训练容易震荡。
修正后可运行的核心代码
import numpy as np import torch import torch.nn as nn import matplotlib.pyplot as plt # 输入处理 inputs = torch.linspace(0, 2, 50, dtype=torch.float32).reshape(-1, 1) inputs.requires_grad = True A = 0.0 # 用内置模块搭建全连接网络,避免维度错误 class ODE_NN(nn.Module): def __init__(self): super().__init__() self.net = nn.Sequential( nn.Linear(1, 9), nn.Sigmoid(), nn.Linear(9, 12), nn.Sigmoid(), nn.Linear(12, 8), nn.Sigmoid(), nn.Linear(8, 1) ) def forward(self, x): return self.net(x) NN = ODE_NN() trial_solution = lambda I: A + I * NN(I) ODE_rhs = lambda x: x mse_loss = nn.MSELoss() optimizer = torch.optim.Adam(NN.parameters(), lr=1e-3) # 训练循环 for epoch in range(10000): optimizer.zero_grad() preds = trial_solution(inputs) # 自动微分计算预测值对输入的导数 preds_grad = torch.autograd.grad( preds, inputs, grad_outputs=torch.ones_like(preds), create_graph=True )[0] target_grad = ODE_rhs(inputs) loss = mse_loss(preds_grad, target_grad) loss.backward() optimizer.step() if epoch % 1000 == 0: print(f"Epoch {epoch}, Loss: {loss.item():.6f}") # 结果可视化 with torch.no_grad(): xx = torch.linspace(0, 2, 50).reshape(-1, 1) pred_res = trial_solution(xx).numpy() true_res = 0.5 * xx.numpy() ** 2 plt.plot(xx.numpy(), pred_res, label='神经网络预测值') plt.plot(xx.numpy(), true_res, label='真实解 $\psi(x)=0.5x^2$') plt.legend() plt.show()
修正后训练10000轮损失可降到1e-6以下,预测曲线和真实解几乎完全重合。
内容的提问来源于stack exchange,提问作者phys_birthdaycake
相关产品推荐
相关产品推荐

