为什么在Pytorch张量上调用.numpy()之前要调用.detach()?

为什么在Pytorch张量上调用.numpy()之前要调用.detach()?,numpy,pytorch,autodiff,Numpy,Pytorch,Autodiff,我正试图更好地理解原因 在与刚才链接的问题相关的报告中,Blupon表示: 你需要将你的张量转换成另一个张量,除了它的实际值定义之外,不需要梯度 在他链接到的第一个讨论中,albanD指出: 这是预期的行为,因为移动到numpy将破坏图形,因此不会计算梯度 如果您实际上不需要渐变,那么可以显式地.detach()使用需要渐变的张量来获得具有相同内容但不需要渐变的张量。然后,可以将另一个张量转换为numpy数组 在他链接到的第二个讨论中,apaszke写道: 变量不能转换为numpy,因为它们是保

我正试图更好地理解原因

在与刚才链接的问题相关的报告中,Blupon表示:

你需要将你的张量转换成另一个张量,除了它的实际值定义之外,不需要梯度

在他链接到的第一个讨论中,albanD指出:

这是预期的行为,因为移动到numpy将破坏图形,因此不会计算梯度

如果您实际上不需要渐变,那么可以显式地.detach()使用需要渐变的张量来获得具有相同内容但不需要渐变的张量。然后,可以将另一个张量转换为numpy数组

在他链接到的第二个讨论中,apaszke写道:

变量不能转换为numpy,因为它们是保存操作历史的张量的包装器,而numpy没有这样的对象。您可以使用.data属性检索变量持有的张量。那么,这应该是可行的:var.data.numpy()

我已经研究了PyTorch的自动微分库的内部工作原理,但我仍然对这些答案感到困惑。为什么移动到numpy会破坏图形?是因为numpy数组上的任何操作都不会在autodiff图中跟踪吗

什么是变量?它和张量有什么关系

我觉得这里需要一个彻底的高质量堆栈溢出答案,向尚不了解自动差异化的PyTorch新用户解释原因

特别是,我认为通过一个图来说明该图并在本例中显示断开是如何发生的会很有帮助:


我问,为什么搬到numpy会破坏图表?是因为numpy阵列上的任何操作都不会在autodiff图中被跟踪吗?

是的,新张量不会通过
梯度fn
连接到旧张量,因此对新张量的任何操作都不会将梯度带回到旧张量

编写
my_tensor.detach().numpy()
只是说,“我将根据numpy数组中该张量的值进行一些非跟踪计算。”

深入学习(d2l)教科书,虽然没有提到为什么在转换为numpy数组之前分离是有意义的


感谢jodag帮助回答这个问题。正如他所说,变量是过时的,所以我们可以忽略这个评论

我认为目前为止我能找到的最好答案是:

要阻止张量跟踪历史记录,可以调用.detach()将其与计算历史记录分离,并防止跟踪未来的计算

在我在问题中引用的albanD的评论中:

如果您实际上不需要渐变,那么可以显式地.detach()使用需要渐变的张量来获得具有相同内容但不需要渐变的张量。然后,可以将另一个张量转换为numpy数组


换句话说,
detach
方法意味着“我不想要渐变”,并且不可能通过
numpy
操作来跟踪渐变(毕竟,这就是PyTorch张量的用途!)我认为这里要理解的最关键的一点是
火炬张量
np.ndarray
之间的区别:
虽然这两个对象都用于存储n维矩阵(aka),
torch.tensors
有一个额外的“层”——用于存储导致相关n维矩阵的计算图

所以,如果您只对高效简便的方法感兴趣,那么
np.ndarray
torch.tensor
可以互换使用

然而,
torch.tensor
s被设计用于优化环境中,因此它们不仅包含数值张量,而且(更重要的是)包含这些数值的计算图。然后使用该计算图(使用)计算损失函数w.r.t的导数,每个用于计算损失的自变量

如前所述,
np.ndarray
对象没有这个额外的“计算图”层,因此,当将
torch.tensor
转换为
np.ndarray
时,必须使用
detach()
命令显式删除该张量的计算图


计算图
从你的观点来看,这个概念似乎有点模糊。我将尝试用一个简单的例子来说明这一点。
考虑两个(向量)变量的简单函数,<代码> x < /代码>和<代码> w <代码>:

x=torch.rand(4,需要_grad=True)
w=torch.rand(4,需要_grad=True)
y=x@w#x和w的内积
z=y**2#内积平方
如果我们只对
z
的值感兴趣,我们不必担心任何图形,我们只需从输入
x
w
向前移动,计算
y
,然后计算
z

然而,如果我们不太关心
z
的值,而是想问一个问题“对于给定的
x
,什么是
w
使
z
最小化”?
为了回答这个问题,我们需要计算
z
w.r.t
w

的导数 我们怎么做呢?
使用我们知道的
dz/dw=dz/dy*dy/dw
。也就是说,要计算
z
w.r.t
w
的梯度,我们需要从
z
返回到
w
,当我们跟踪从
z
w
的步骤时,计算每个步骤的操作梯度。我们追溯的这个“路径”是
z
的计算图,它告诉我们如何计算
z
w.r.t的导数
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))
w.grad  # the resulting gradient of z w.r.t w
tensor([0.8010, 1.9746, 1.5904, 1.0408])
2*y*x
tensor([0.8010, 1.9746, 1.5904, 1.0408], grad_fn=<MulBackward0>)
z
tensor(1.4061, grad_fn=<PowBackward0>)
y
tensor(1.1858, grad_fn=<DotBackward>)
import torch
tensor = torch.rand(2)
numpy_array = tensor.numpy()
print('Before edit:')
print(tensor)
print(numpy_array)

tensor[0] = 10

print()
print('After edit:')
print('Tensor:', tensor)
print('Numpy array:', numpy_array)
Before edit:
Tensor: tensor([0.1286, 0.4899])
Numpy array: [0.1285522  0.48987144]

After edit:
Tensor: tensor([10.0000,  0.4899])
Numpy array: [10.        0.48987144]