PyTorch autograd求loss对u的梯度失败:一阶/二阶导数计算异常
PyTorch自动求导异常:计算损失对u的梯度时出现RuntimeError
问题原因
核心问题是**torch.autograd.grad生成的导数张量(u_d、u_x、u_xx等)与原模型输出u的计算图链路被切断**:
- u是模型基于X生成的输出,其计算图关联模型参数与X
- 用
torch.autograd.grad(u, X)计算的u_d,本质是u对X的梯度,这个张量的计算图根节点是X,而非u - 后续的u_x、u_t、u_xx均从u_d衍生,loss依赖这些张量,但当尝试从loss回溯到u时,PyTorch的autograd找不到有效的反向传播路径,因此抛出"未在计算图中使用"的错误。
你提到计算loss对u_t、u_xx的梯度正常,是因为这些张量的计算图根节点是X,autograd可以从loss回溯到X,但无法再进一步回溯到u——因为autograd.grad没有保留u到这些导数的链路。
修复方法
推荐使用PyTorch 2.0+提供的函数式自动微分API(torch.func),它能完整保留计算图链路,避免手动调用autograd.grad导致的链路断裂问题。以下是具体修改后的代码:
修改后的主代码
import torch import torch.nn.functional as F from torch.func import jacrev, vmap # Ensure X requires gradients X.requires_grad_(True) # 定义单独的u计算函数,用于自动微分 def compute_u(model, x): return model(x) # 批量计算u对X的雅可比矩阵(处理X的批量维度) jacobian_u = vmap(jacrev(compute_u, argnums=1), in_dims=(None, 0))(self.pinn, X) # 提取一阶导数:∂u/∂x、∂u/∂t u_x = jacobian_u[:, 0, 0] # X形状[N,2],u形状[N,1],雅可比为[N,1,2] u_t = jacobian_u[:, 0, 1] # 定义u_x的计算函数,用于求二阶导数 def compute_u_x(model, x): return jacrev(compute_u, argnums=1)(model, x)[:, 0] # 批量计算u_x对X的雅可比,提取∂²u/∂x² jacobian_u_x = vmap(jacrev(compute_u_x, argnums=1), in_dims=(None, 0))(self.pinn, X) u_xx = jacobian_u_x[:, 0, 0] # 计算扩散方程损失 loss = F.mse_loss(u_t, self.kappa * u_xx) # 现在计算loss对u的梯度可以正常运行 loss_u = torch.autograd.grad( loss, u, grad_outputs=torch.ones_like(loss), retain_graph=True, create_graph=True, )[0] return loss
替代方案(不使用torch.func)
如果必须使用旧版autograd API,可以通过链式法则手动关联梯度,但这种方式代码繁琐且容易出错:
- 先计算loss对u_t和u_xx的梯度:
dl_dt, dl_dxx = torch.autograd.grad(loss, [u_t, u_xx], retain_graph=True) - 再分别计算u_t对u的梯度、u_xx对u的梯度,最后通过链式法则求和得到
loss_u = dl_dt * du_t_du + dl_dxx * du_xx_du
但这种方式需要手动处理高阶导数的链路,远不如torch.func简洁可靠。
关键注意事项
- 确保所有涉及自动微分的张量都开启
requires_grad=True(你已经完成这一步) - 避免多次手动调用
torch.autograd.grad来推导高阶导数,这类操作很容易切断计算图链路 - PyTorch 2.0+的函数式API(torch.func)是处理PINN(物理信息神经网络)中高阶导数的最优方案,能自动保留完整计算图
内容的提问来源于stack exchange,提问作者Wout Rombouts
相关产品推荐
相关产品推荐

