【问题标题】:PyTorch memory model: "torch.from_numpy()" vs "torch.Tensor()"PyTorch 内存模型:“torch.from_numpy()”与“torch.Tensor()”
【发布时间】:2018-07-07 01:23:42
【问题描述】:

我正在尝试深入了解 PyTorch 张量内存模型的工作原理。

# input numpy array
In [91]: arr = np.arange(10, dtype=float32).reshape(5, 2)

# input tensors in two different ways
In [92]: t1, t2 = torch.Tensor(arr), torch.from_numpy(arr)

# their types
In [93]: type(arr), type(t1), type(t2)
Out[93]: (numpy.ndarray, torch.FloatTensor, torch.FloatTensor)

# ndarray 
In [94]: arr
Out[94]: 
array([[ 0.,  1.],
       [ 2.,  3.],
       [ 4.,  5.],
       [ 6.,  7.],
       [ 8.,  9.]], dtype=float32)

我知道 PyTorch 张量共享 NumPy ndarray 的内存缓冲区。因此,改变一个将反映在另一个。所以,我在这里对张量t2中的一些值进行切片和更新@

In [98]: t2[:, 1] = 23.0

正如预期的那样,它在t2arr 中进行了更新,因为它们共享相同的内存缓冲区。

In [99]: t2
Out[99]: 

  0  23
  2  23
  4  23
  6  23
  8  23
[torch.FloatTensor of size 5x2]


In [101]: arr
Out[101]: 
array([[  0.,  23.],
       [  2.,  23.],
       [  4.,  23.],
       [  6.,  23.],
       [  8.,  23.]], dtype=float32)

但是,t1 也已更新。请记住,t1 是使用 torch.Tensor() 构造的,而 t2 是使用 torch.from_numpy() 构造的

In [100]: t1
Out[100]: 

  0  23
  2  23
  4  23
  6  23
  8  23
[torch.FloatTensor of size 5x2]

所以,无论我们使用torch.from_numpy() 还是torch.Tensor() 从一个ndarray 构造一个张量,所有这样的张量和ndarray 共享同一个内存缓冲区。

基于这种理解,我的问题是,当简单的 torch.Tensor() 可以完成这项工作时,为什么还存在一个专用函数 torch.from_numpy()

我查看了 PyTorch 文档,但它没有提到任何关于此的内容?有什么想法/建议吗?

【问题讨论】:

  • 非常有趣的问题。我不知道答案,但我怀疑 torch.Tensor() 可能接受其他形式(例如,列表)的输入,但 torch.from_numpy() 仅在 numpy 数组上运行。

标签: python numpy multidimensional-array deep-learning pytorch


【解决方案1】:

来自_torch_docs.py;还有一个关于“为什么here的可能讨论。

def from_numpy(ndarray): # real signature unknown; restored from __doc__
    """
    from_numpy(ndarray) -> Tensor

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

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

    Example::

        >>> a = numpy.array([1, 2, 3])
        >>> t = torch.from_numpy(a)
        >>> t
        torch.LongTensor([1, 2, 3])
        >>> t[0] = -1
        >>> a
        array([-1,  2,  3])
    """
    pass

取自numpy 文档:

不同的ndarrays 可以共享相同的数据,因此在一个ndarray 中所做的更改可能在另一个ndarray 中可见。也就是说,一个ndarray 可以是另一个ndarray 的“视图”,它所引用的数据由“基础”ndarray 处理。

Pytorchdocs

如果给出numpy.ndarraytorch.Tensortorch.Storage,则返回一个共享相同数据的新张量。如果给定一个 Python 序列,则从该序列的副本创建一个新的张量。

【讨论】:

    【解决方案2】:

    from_numpy() 自动继承输入数组dtype。另一方面,torch.Tensortorch.FloatTensor 的别名。

    因此,如果将int64 数组传递给torch.Tensor,则输出张量为浮点张量,它们不会共享存储。 torch.from_numpy 按预期为您提供torch.LongTensor

    a = np.arange(10)
    ft = torch.Tensor(a)  # same as torch.FloatTensor
    it = torch.from_numpy(a)
    
    a.dtype  # == dtype('int64')
    ft.dtype  # == torch.float32
    it.dtype  # == torch.int64
    

    【讨论】:

      【解决方案3】:

      在 Pytorch 中构建张量的推荐方法是使用以下两个工厂函数:torch.tensortorch.as_tensor

      torch.tensor总是复制数据。例如,torch.tensor(x) 等价于 x.clone().detach()

      torch.as_tensor 总是尝试避免复制数据。 as_tensor 避免复制数据的一种情况是原始数据是一个 numpy 数组。

      【讨论】:

      • 这条评论是关于torch.tensortorch.as_tensor,很高兴知道。但它没有解决 OP 关于torch.from_numpy 的问题。
      【解决方案4】:

      我尝试按照您所说的进行操作并按预期工作: 火炬 1.8.1、Numpy 1.20.1、python 3.8.5

      x = np.arange(8, dtype=np.float64).reshape(2,4)
      y_4mNp = torch.from_numpy(x)
      y_t = torch.tensor(x)
      print(f"x={x}\ny_4mNp={y_4mNp}\ny_t={y_t}") 
      

      所有变量现在都像预期的那样具有相同的值:

      x=[[0. 1. 2. 3.]
       [4. 5. 6. 7.]]
      y_4mNp=tensor([[0., 1., 2., 3.],
              [4., 5., 6., 7.]], dtype=torch.float64)
      y_t=tensor([[0., 1., 2., 3.],
              [4., 5., 6., 7.]], dtype=torch.float64)
      

      From_numpy 确实使用与 np 变量相同的底层内存。 因此,更改 np 或 .from_numpy 变量会相互影响,但不会影响张量变量。 但对 y_t 的更改仅影响其自身,而不影响 numpy 或 from_numpy 变量。

      x[0,1] = 111       ## changed the numpy variable itself directly
      y_4mNp[1,:] = 500  ## changed the .from_numpy variable
      y_t[0,:] = 999     ## changed the tensor variable
      print(f"x={x}\ny_4mNp={y_4mNp}\ny_t={y_t}")
      

      现在输出:

      x=[[  0. 111.   2.   3.]
       [500. 500. 500. 500.]]
      y_4mNp=tensor([[  0., 111.,   2.,   3.],
              [500., 500., 500., 500.]], dtype=torch.float64)
      y_t=tensor([[999., 999., 999., 999.],
              [  4.,   5.,   6.,   7.]], dtype=torch.float64)
      

      不知道这是不是早期版本的问题?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-12-10
        • 2022-08-15
        • 2018-04-12
        • 2018-12-03
        • 1970-01-01
        • 1970-01-01
        • 2021-06-23
        • 2011-02-28
        相关资源
        最近更新 更多