首页人工智能Pytorch【深度学习(PyTorch...

【深度学习(PyTorch篇)】16.张量的存储视图

本系列文章配套代码获取有以下两种途径:

  • 通过百度网盘获取:
链接:https://pan.baidu.com/s/1XuxKa9_G00NznvSK0cr5qw?pwd=mnsj 提取码:mnsj
  • 前往GitHub获取
https://github.com/returu/PyTorch





Pytorch中张量Tensor与Python中列表list在内存分配方面是不同的。

Python中列表list是一种动态数组,它能够容纳任意类型的对象,并且可以随着需要而增长或缩小在内存分配方面,Python列表并不是直接存储数据,而是存储了对数据的引用。这意味着列表中的每个元素实际上是一个指向内存中某处的指针,该处存储了实际的数据。

Pytorch中张量Tensor并不存储对数据的引用,而是直接存储数据本身,的内存布局是连续的。这是因为Tensor通常存储同类型的数据(如float32),并且为了在计算时获得最佳性能,它们被设计为占据一块连续的内存区域。这意味着Tensor中的数据是紧密排列的,没有额外的指针间接层。Tensor可以看作是对这块连续内存块的视图,提供了多维索引和其他方便的操作。这种内存布局使得Tensor在处理大规模数据时更加高效,因为它们可以减少内存碎片和缓存未命中的情况。

图片来源:根据《Deep Learning with PyTorch》绘制
01

张量的存储视图:


在PyTorch中张量中值被分配到由torch.Storage实例所管理的连续内存块中,因此Tensor实例可以被视为其底层存储torch.Storage实例的视图Tensor实例通过使用偏移量、步长等元数据对存储区进行索引。
PyTorch 1.x版本中可以通过storage()函数可以访问Tensor存储区。2.x版本中可以untyped_storage()函数可以访问Tensor存储区。另外,使用data_ptr()可以获得Tensor存储区的内存地址。
下面举例说明:
>>> a = torch.arange(6).reshape(2,3)
>>> a
tensor([[012],
        [345]])

# 对张量a进行reshape操作
>>> b = a.reshape(3,2)
>>> b
tensor([[01],
        [23],
        [45]])

# 对张量a进行索引切片操作
>>> c = a[:,:2]
>>> c
tensor([[01],
        [34]])

# 判断三个tensor的存储区的内存地址是否一样
>>> a.untyped_storage().data_ptr() == b.untyped_storage().data_ptr() == c.untyped_storage().data_ptr()
True
由此可见,上述操作并未修改Tensor的存储区,张量a,b,c引用同一个存储区

这种张量和存储区之间的间接关系使得一些操作开销并不大,因为这些操作不会导致内存的重新分配,而是创建一个新的张量对象,该张量对象具有不同的元数据(占用的内存较少)。
但是,需要注意的是,改某一张量的数据时,其他共享内存地址的张量也会跟着变化,因为底层存储中的数据被改变了同样,如果直接更改存储区的数据(尽管通常不推荐这样做),那么任何使用该存储的张量也会反映出这些更改。
>>> c[0,0] = 9

>>> a
tensor([[9, 1, 2],
        [3, 4, 5]]
)

>>> b
tensor([[9, 1],
        [2, 3],
        [4, 5]]
)
在大多数情况下,我们不需要直接与torch.Storage对象交互,而是通过创建和操作张量来管理数据,此时PyTorch会自动处理底层存储的分配和释放。
02

张量元数据:


为在存储区中建立索引,张量Tensor依赖于一些元数据(形状、偏移量、步长)来确定如何从存储中访问元素。
就是说,上述示例中的张量a,b,c虽然共享存储区,但是每个张量的形状、偏移量、步长是不同的,由此来从存储区中访问元素。
  • 形状是一个元祖,用于表示张量在每个维度上有多少个元素,通过tensor.size()获取;
  • 偏移量是指在tensor的第一个元素与storage的第一个元素的偏移量,通过tensor.storage_offset()获取
  • 步长是一个元祖,指在存储区中为了获得下一个元素需要跳过的元素数量,通过tensor.stride()获取

# 张量abc的形状
>>> a.size() , b.size() , c.size()
(torch.Size([2, 3]), torch.Size([3, 2]), torch.Size([2, 2]))

# 张量abc的步长
>>> a.stride() , b.stride() , c.stride()
((3, 1), (2, 1), (3, 1))

# 张量abc的偏移量
>>> a.storage_offset() , b.storage_offset() , b.storage_offset()
(0, 0, 0)
本次以张量a为例,来解释下元数据的意义。
  • 张量a中的第一个元素0,storage的第一个元素的偏移量storage_offset=0,即存储区第1个元素;
  • 步长stride=(3,1),即在行方向上为了获得下一个元素需要跳过1个元素,在列向上为了获得下一个元素需要跳过3个元素。


更多内容可以前往官网查看

https://pytorch.org/


本篇文章来源于微信公众号: 码农设计师

RELATED ARTICLES

欢迎留下您的宝贵建议

Please enter your comment!
Please enter your name here

- Advertisment -

Most Popular

Recent Comments