PyTorch二次反向传播报错求助:retain_graph=True无效
问题分析与解决
首先看代码里的几个明显错误:
- 函数名大小写不统一:定义的是
CalculateLoss,但闭包里调用的是calculateLosses(),拼写错误会导致找不到函数。 - 优化器调用时传的是
closure1,但定义的闭包函数是closure,参数名错误。
而核心的RuntimeError问题,根源在于LBFGS优化器的特性:它会多次调用传入的闭包函数(用来反复计算损失和梯度、做线搜索),但你的代码把前向传播(solution1 = my_neural_network(x))和梯度计算(grads_sol = gradients(...))放在了闭包外面,每个epoch只计算一次。当LBFGS第二次调用闭包时,会复用之前的计算图,而第一次backward()已经释放了图中的中间张量,就会触发"反向传播第二次"的错误——哪怕加retain_graph=True也没用,因为你复用的是旧的计算图,而不是当前参数下的新图。
修正后的代码
optimizer1 = torch.optim.LBFGS(my_neural_network.parameters(), lr=lr1) StepLR1 = torch.optim.lr_scheduler.StepLR(optimizer1, step_size=..., gamma=...) # 假设你已定义调度器参数 # 如果x是固定输入不需要优化,设置requires_grad=False x = torch.tensor([200, 100, 0, 1, 15], dtype=torch.float32, requires_grad=False) for epoch in range(no_of_epochs + 1): def calculate_loss(): # 把前向传播移到闭包内部,每次调用都重新计算当前参数下的输出 solution1 = my_neural_network(x) # 重新计算梯度,基于当前的solution1;create_graph=True保留梯度计算图,支持后续对网络参数求导 grads_sol = torch.autograd.grad(solution1, x, create_graph=True)[0] # 你的损失计算逻辑 loss1 = function_special(solution1, grads_sol, x) return loss1 def closure(): optimizer1.zero_grad() loss1 = calculate_loss() loss1.backward() return loss1 # 调用正确的闭包函数 optimizer1.step(closure) StepLR1.step()
关键说明
- 闭包必须包含完整前向流程:LBFGS会多次执行闭包来调整参数,所以每次闭包调用都要重新跑前向传播、梯度计算、损失计算,不能复用外部的旧张量,否则会重复使用同一个计算图。
- 关于
create_graph=True:如果你要计算的损失function_special依赖于grads_sol(对x的梯度),而最终要对网络参数求导,那么调用torch.autograd.grad时必须加create_graph=True,这样梯度的计算图会被保留,才能继续对网络参数求导。 - 是否需要
detach:只有当你想切断某个张量的梯度传播时才用detach。比如如果x是固定输入,设置requires_grad=False就足够;如果function_special里有不需要参与梯度的部分,可以对对应的张量调用detach()。 - 优化写法建议:不要在循环里重复定义函数,更好的做法是把
calculate_loss改成接收网络、x等参数的外部函数,避免每次循环创建新函数对象。
内容的提问来源于stack exchange,提问作者math_guy
相关产品推荐
相关产品推荐

