PyTorch二次反向传播报RuntimeError 中间结果已释放加retain_graph无效
报错核心诱因
和retain_graph参数的设置无关,触发报错的原因有三个:
- q_target生成环节未关闭梯度追踪,在critic更新的计算图外,额外建立了关联actor、critic的无意义前向链路。第一次执行
loss_critic.backward()时,链路关联的中间结果会被连带释放,后续反向actor loss时就会触发“二次遍历已释放计算图”的报错 - 用默认无梯度的
torch.zeros预分配张量存储带梯度的网络输出,这种in-place索引赋值会直接打断梯度链路,哪怕正确设置retain_graph也无法追踪完整梯度流向 - 存在关键字拼写错误:代码中写的
retain_grah漏写了字母p,实际传入backward的参数无效,根本没有起到保留计算图的作用
调整逻辑
- 将q_target生成的所有逻辑放入
torch.no_grad()上下文,彻底切断目标值计算的梯度追踪,从根源上隔离无关计算图 - 用列表存储带梯度的网络输出,最后通过
torch.stack()拼接为张量,避免in-place赋值打断梯度流 - 移除无效的retain_graph设置,保证各环节计算图独立
修正后代码
# 生成目标Q值,全程关闭梯度追踪 q_target_list = [] with torch.no_grad(): for j, r in enumerate(reward): next_state_t = torch.transpose(next_state[j], 0, 1) action_next = self.actor_network(next_state_t) q_t = self.critic_network(next_state_t, action_next.view(1, 1)) q_target_val = r + (done[j] * gamma * q_t) q_target_list.append(q_target_val) q_target = torch.stack(q_target_list).squeeze() # 计算当前Q值,保留梯度用于critic更新 q_list = [] for j, r in enumerate(reward): state_t = torch.transpose(state[j], 0, 1) q_val = self.critic_network(state_t, action[j].view(1, 1)) q_list.append(q_val) q = torch.stack(q_list).squeeze() loss_critic = F.mse_loss(q, q_target) self.critic_optimizer.zero_grad() loss_critic.backward() self.critic_optimizer.step() # 计算actor损失,保留梯度用于策略更新 b_list = [] for j, r in enumerate(reward): state_t = torch.transpose(state[j], 0, 1) action_cur = self.actor_network(state_t) b_val = self.critic_network(state_t, action_cur.view(1, 1)) b_list.append(b_val) b = torch.stack(b_list).squeeze() loss_actor = -torch.mean(b) self.actor_optimizer.zero_grad() loss_actor.backward() self.actor_optimizer.step()
原理说明:q_target是训练的监督信号,不需要参与梯度计算,通过
no_grad隔离后,这部分的前向计算不会生成关联网络参数的计算图,也就不会和后续critic、actor的更新流程产生图耦合,从根源上避免重复反向的报错。
内容的提问来源于stack exchange,提问作者Mr_qiaozhi
相关产品推荐
相关产品推荐

