PyTorch中inputs=r改1*r致反向传播报错的原因解析
问题背景
我是一名PyTorch新手,正尝试理解计算图与Autograd机制,目前在学习势能与对应力的模型训练。
模型与训练代码
模型定义:
model = nn.Sequential( nn.Linear(1, 32), nn.Linear(32, 32), nn.Tanh(), nn.Linear(32, 32), nn.Tanh(), nn.Linear(32, 1) ) optimizer = torch.optim.Adam(model.parameters()) loss = nn.MSELoss()
训练代码:
# generate data r = torch.linspace(0.95, 3, 50, requires_grad=True).view(-1, 1) E = 1 / r F = -grad(E.sum(), r)[0] inputs = r for epoch in range(10**3): E_pred = model.forward(inputs) F_pred = -grad(E_pred.sum(), r, create_graph=True, retain_graph=True)[0] optimizer.zero_grad() error = loss(E_pred, E.data) + loss(F_pred, F.data) error.backward() optimizer.step()
报错情况
当将inputs = r改为inputs = 1*r后,训练循环触发报错:
RuntimeError: Trying to backward through the graph a second time (or directly access saved tensors after they have already been freed). Saved intermediate values of the graph are freed when you call .backward() or autograd.grad(). Specify retain_graph=True if you need to backward through the graph a second time or if you need to access saved tensors after calling backward.
报错原因分析
原代码
inputs = r的计算图逻辑:此时inputs直接指向原始张量r,E_pred = model(inputs)的计算图起点是r,终点是E_pred。调用grad(E_pred.sum(), r, create_graph=True, retain_graph=True)时,虽构建了从E_pred.sum()到r的反向图,但retain_graph=True保留了模型计算部分的节点,后续error.backward()是对模型参数求导,路径未重复经过已释放的节点,因此正常执行。修改为
inputs = 1*r后的计算图变化:1*r会生成一个新张量,它的计算图依赖于r,此时E_pred的计算路径变为:r → 1*r → model → E_pred。调用grad(E_pred.sum(), r, ...)时,会先反向传播一次从E_pred.sum()到r的路径,这会消耗掉1*r到r之间的计算图节点(retain_graph=True仅针对E_pred.sum()到r的下游图,不覆盖1*r这个上游中间节点)。后续执行error.backward()时,需要从error反向传播到模型参数,路径会经过1*r到r的部分,但这部分节点的缓存已在第一次grad调用后被释放,因此触发重复反向传播的报错。核心差异:
inputs = r时,模型输入与原始张量r直接绑定,两次反向传播路径无重复的中间节点;而inputs = 1*r新增了中间计算节点,导致两次反向传播需要重复访问已被释放的节点缓存,最终触发报错。
内容的提问来源于stack exchange,提问作者ArkadyBogdanov

