我认为理解这里最关键的一点是不同之处之间torch.tensor
and np.ndarray
:
虽然这两个对象都用于存储 n 维矩阵(又名“张量” https://en.wikipedia.org/wiki/Tensor), torch.tensors
有一个额外的“层” - 它存储导致关联的 n 维矩阵的计算图。
因此,如果您只对在矩阵上执行数学运算的有效且简单的方法感兴趣np.ndarray
or torch.tensor
可以互换使用。
然而,torch.tensor
s 的设计目的是在以下情况下使用梯度下降 https://en.wikipedia.org/wiki/Gradient_descent优化,因此它们不仅包含具有数值的张量,还包含(更重要的是)导致这些值的计算图。然后使用该计算图(使用导数的链式法则 https://en.wikipedia.org/wiki/Chain_rule)来计算损失函数相对于用于计算损失的每个自变量的导数。
正如之前所提,np.ndarray
对象没有这个额外的“计算图”层,因此,当转换torch.tensor
to np.ndarray
你必须明确地使用以下方法删除张量的计算图detach()
命令。
计算图
从你的comments https://stackoverflow.com/questions/63582590/why-do-we-call-detach-before-calling-numpy-on-a-pytorch-tensor/63869655?noredirect=1#comment112956796_63869655好像这个概念有点模糊。我将尝试用一个简单的例子来说明它。
考虑两个(向量)变量的简单函数,x
and w
:
x = torch.rand(4, requires_grad=True)
w = torch.rand(4, requires_grad=True)
y = x @ w # inner-product of x and w
z = y ** 2 # square the inner product
如果我们只对价值感兴趣z
,我们不需要担心任何图表,我们只需移动forward从输入中,x
and w
, 计算y
进而z
.
然而,如果我们不太关心它的价值,会发生什么?z
,而是想问这个问题“什么是w
that 最小化 z
对于给定的x
"?
为了回答这个问题,我们需要计算衍生物 of z
w.r.t w
.
我们怎样才能做到这一点?
使用链式法则 https://en.wikipedia.org/wiki/Chain_rule我们知道dz/dw = dz/dy * dy/dw
。也就是说,计算梯度z
w.r.t w
我们需要搬家backward https://stackoverflow.com/a/57249287/1714410 from z
回到w
计算gradient我们追踪每一步的操作back https://stackoverflow.com/a/57249287/1714410我们的脚步从z
to w
。我们追溯的这条“路径”就是计算图 of z
它告诉我们如何计算导数z
w.r.t 导致的输入z
:
z.backward() # ask pytorch to trace back the computation of z
我们现在可以检查梯度z
w.r.t w
:
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>)
since dz/dy = 2*y
and dy/dw = x
.
路径上的每个张量都存储其对计算的“贡献”:
z
tensor(1.4061, grad_fn=<PowBackward0>)
And
y
tensor(1.1858, grad_fn=<DotBackward>)
如你看到的,y
and z
不仅存储“远期”值<x, w>
or y**2
但也计算图 -- the grad_fn
当追溯梯度时需要计算导数(使用链式法则)z
(输出)到w
(输入)。
These grad_fn
是必不可少的组成部分torch.tensors
没有它们,我们就无法计算复杂函数的导数。然而,np.ndarray
他们根本没有这种能力,也没有这种信息。
请参见这个答案 https://stackoverflow.com/a/57249287/1714410有关使用追溯导数的更多信息backwrd()
功能。
既然两者np.ndarray
and torch.tensor
有一个共同的“层”存储 n 维数字数组,pytorch 使用相同的存储来节省内存:
numpy() → numpy.ndarray https://pytorch.org/docs/stable/tensors.html#torch.Tensor.numpy
退货self
张量作为 NumPy ndarray。这个张量和返回的ndarray共享相同的底层存储。 self 张量的更改将反映在 ndarray 中,反之亦然。
另一个方向也以同样的方式工作:
torch.from_numpy(ndarray) → Tensor https://pytorch.org/docs/stable/generated/torch.from_numpy.html#torch.from_numpy
从 numpy.ndarray 创建张量。
返回的张量和ndarray共享相同的记忆。对张量的修改将反映在 ndarray 中,反之亦然。
因此,当创建一个np.array
from torch.tensor
反之亦然,双方都反对参考内存中相同的底层存储。自从np.ndarray
不存储/表示与数组关联的计算图,该图应该是明确地删除使用detach()
当共享 numpy 和 torch 时希望引用相同的张量。
请注意,如果您出于某种原因希望仅使用 pytorch 进行数学运算而不进行反向传播,则可以使用with torch.no_grad() https://pytorch.org/docs/stable/generated/torch.no_grad.html#torch.no_grad上下文管理器,在这种情况下不会创建计算图torch.tensor
s and np.ndarray
s 可以互换使用。
with torch.no_grad():
x_t = torch.rand(3,4)
y_np = np.ones((4, 2), dtype=np.float32)
x_t @ torch.from_numpy(y_np) # dot product in torch
np.dot(x_t.numpy(), y_np) # the same dot product in numpy