PyTorch 内存模型:“torch.from_numpy()”与“torch.Tensor()”
Posted
技术标签:
【中文标题】PyTorch 内存模型:“torch.from_numpy()”与“torch.Tensor()”【英文标题】:PyTorch memory model: "torch.from_numpy()" vs "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
正如预期的那样,它在t2
和arr
中进行了更新,因为它们共享相同的内存缓冲区。
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 数组上运行。
【参考方案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.ndarray
、torch.Tensor
或torch.Storage
,则返回一个共享相同数据的新张量。如果给定一个 Python 序列,则从该序列的副本创建一个新的张量。
【讨论】:
【参考方案2】:from_numpy()
自动继承输入数组dtype
。另一方面,torch.Tensor
是torch.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.tensor
和 torch.as_tensor
。
torch.tensor
总是复制数据。例如,torch.tensor(x)
等价于 x.clone().detach()
。
torch.as_tensor
总是尝试避免复制数据。 as_tensor
避免复制数据的一种情况是原始数据是一个 numpy 数组。
【讨论】:
这条评论是关于torch.tensor
和torch.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)
不知道这是不是早期版本的问题?
【讨论】:
以上是关于PyTorch 内存模型:“torch.from_numpy()”与“torch.Tensor()”的主要内容,如果未能解决你的问题,请参考以下文章
Pytorch 模型在 CPU 和 GPU 上都内存不足,无法弄清楚我做错了啥
PyTorch内存模型:“torch.from_numpy()”vs“torch.Tensor()”