Pytorch 内存模型:"torch.from_numpy()" 是如何工作的?

Pytorch memory model: how does "torch.from_numpy()" work?

我正在努力深入了解 torch.from_numpy() 的工作原理。

import numpy as np
import torch

arr = np.zeros((3, 3), dtype=np.float32)
t = torch.from_numpy(arr)
print("arr: {0}\nt: {1}\n".format(arr, t))

arr[0,0]=1
print("arr: {0}\nt: {1}\n".format(arr, t))

print("id(arr): {0}\nid(t): {1}".format(id(arr), id(t)))

输出如下所示:

arr: [[0. 0. 0.]
 [0. 0. 0.]
 [0. 0. 0.]]
t: tensor([[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]])

arr: [[1. 0. 0.]
 [0. 0. 0.]
 [0. 0. 0.]]
t: tensor([[1., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]])

id(arr): 2360964353040
id(t): 2360964352984

这是来自 torch.from_numpy():

的文档的一部分

from_numpy(ndarray) -> Tensor

Creates a :class:Tensor from a :class:numpy.ndarray.

The returned tensor and :attr:ndarray share the same memory. Modifications to the tensor will be reflected in the :attr:ndarray and vice versa. The returned tensor is not resizable.

这取自 id() 的文档:

Return the identity of an object.

This is guaranteed to be unique among simultaneously existing objects. (CPython uses the object's memory address.)

那么问题来了: 既然ndarrayarr和张量t共享同一个内存,为什么它们的内存地址不同?

任何 ideas/suggestions?

是的,tarr 是位于不同内存区域的不同 Python 对象(因此不同 id),但都指向相同的内存地址,其中包含数据(连续(通常)C 数组)。

numpy 使用绑定到 Python 函数的 C 代码在此区域上运行,torch 也是如此(但使用 C++)。 id() 不知道数据本身的内存地址,只知道它是 "wrappers"。

编辑: 当您分配 b = a 时(假设 anp.array),两者都是对相同 [=78= 的引用]包装器(np.ndarray)。换句话说,它们是同一个对象,但名称不同

这就是 Python 的分配方式,请参阅 documentation。以下所有情况也会 return True

import torch
import numpy as np

tensor = torch.tensor([1,2,3])
tensor2 = tensor
id(tensor) == id(tensor2)

arr = np.array([1, 2, 3, 4, 5])
arr2 = arr
id(arr) == id(arr2)

some_str = "abba"
other_str = some_str
id(some_str) == id(other_str)

value = 0
value2 = value
id(value) == id(value2)

现在,当您在 np.ndarray 上使用 torch.from_numpy 时,您有两个不同 类 的对象(torch.Tensor 和原始 np.ndarray)。由于它们属于不同类型,因此它们不能具有相同的 id。可以将此案例视为类似于以下案例:

value = 3
string_value = str(3)

id(value) == id(string_value)

这里很直观,string_valuevalue 是位于不同内存位置的两个不同对象。

编辑 2:

总而言之,Python对象和底层C数组的概念必须分开。 id() 不知道 C 绑定(怎么可能?),但它知道 Python 结构(torch.Tensornp.ndarray)的内存地址。

numpytorch.tensor的情况下,您可能会遇到以下情况:

  • 在 Python 级别上分开,但对数组使用相同的内存区域 (torch.from_numpy)
  • 在 Python 级别和底层内存区域(一个 torch.tensor 和另一个 np.array)分开。可以通过 from_numpy 后跟 clone() 或类似的深度复制操作来创建。
  • 在 Python 级别和底层内存区域相同(例如,两个 torch.tensor 对象,一个引用另一个,如上所述)