如何不使用detach()将PyTorch Tensor转换为NumPy数组?
问题描述
实现算法时遇到以下代码块报错:
def get_action(self, state): state = torch.from_numpy(state).float().unsqueeze(0) probs = self.policy_network.forward(Variable(state)) highest_prob_action = np.random.choice(self.num_actions, p=np.squeeze(probs.numpy())) //This line log_prob = torch.log(probs.squeeze(0)[highest_prob_action]) return highest_prob_action, log_prob
为了给np.random.choice传入概率分布,代码中使用了probs tensor。已知使用detach函数可以解决该问题,但代码需要提交到自动评分器,调用detach()会返回错误,因此无法使用该函数。
需求:有没有不使用detach函数的实现方式?
当前probs tensor的结构如下:
tensor([[0.2522, 0.2533, 0.2385, 0.2560]], grad_fn=<SoftmaxBackward>)
需要的最终输出类型示例如下(数值无实际意义):
array([0., 1., 2., 3.], dtype=float32)
错误复现代码
import torch tensor1 = torch.tensor([1.0,2.0],requires_grad=True) print(tensor1) print(type(tensor1)) tensor1 = tensor1.numpy() print(tensor1) print(type(tensor1))
已尝试的方案
按照建议使用torch.multinomial,代码如下:
states.append(state) probs = self.policy.forward(Variable(torch.from_numpy(state).float().unsqueeze(0))) highest_prob_action = np.random.choice(torch.multinomial(probs, self.num_actions).squeeze(0)) # highest_prob_action = np.random.choice(self.num_actions, p=np.squeeze(probs.numpy())) log_prob = torch.log(probs.squeeze(0)[highest_prob_action])
但运行若干轮后会返回如下报错:
130 states.append(state) 131 probs = self.policy.forward(Variable(torch.from_numpy(state).float().unsqueeze(0))) --> 132 highest_prob_action = np.random.choice(torch.multinomial(probs, self.num_actions).squeeze(0)) 133 # highest_prob_action = np.random.choice(self.num_actions, p=np.squeeze(probs.numpy())) 134 log_prob = torch.log(probs.squeeze(0)[highest_prob_action]) RuntimeError: probability tensor contains either `inf`, `nan` or element < 0
解决方案
方案1:适配原numpy实现逻辑,完全不调用detach
将报错行替换为以下代码即可:
highest_prob_action = np.random.choice(self.num_actions, p=np.squeeze(np.array(probs.tolist())))
原理:tolist()会直接将带梯度的tensor中的数值导出为Python原生浮点数列表,整个过程不需要调用detach,也不会修改原probs tensor的梯度关联属性,后续计算log_prob的梯度传递完全不受影响。导出的列表转为numpy数组后就可以直接传入np.random.choice的p参数。
方案2:纯PyTorch实现,避免调用numpy,修复multinomial报错
之前使用multinomial报错是两个原因导致:一是采样数设置错误,不需要采样num_actions个结果,仅需采样1个动作;二是softmax输出可能出现数值不稳定的极小值,触发边界校验报错。修改后代码如下:
states.append(state) probs = self.policy.forward(Variable(torch.from_numpy(state).float().unsqueeze(0))) # 裁剪概率值避免出现0/负数,解决数值不稳定问题 probs = torch.clamp(probs, min=1e-10, max=1.0) # 仅采样1个动作,直接取出索引值 highest_prob_action = torch.multinomial(probs, num_samples=1).item() log_prob = torch.log(probs.squeeze(0)[highest_prob_action])
该方案完全不需要调用numpy相关操作,也不涉及detach调用,计算效率更高,梯度传递更稳定。
内容的提问来源于stack exchange,提问作者Ravish Jha
相关产品推荐
相关产品推荐

