前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >numpy 和 pytorch tensor 的内存连续性 contiguous

numpy 和 pytorch tensor 的内存连续性 contiguous

作者头像
为为为什么
发布2022-08-05 15:52:23
1.9K0
发布2022-08-05 15:52:23
举报
文章被收录于专栏:又见苍岚又见苍岚

numpy 和 pytorch tensor 存在内存是否连续的情况,对运行速度甚至网络运行结果都存在影响。

含义

  • contiguous 本身是形容词**,**表示连续的。所谓contiguous array,指的是数组在内存中存放的地址也是连续的(注意内存地址实际是一维的),即访问数组中的下一个元素,直接移动到内存中的下一个地址就可以。
  • 在numpy和torch的数据结构中,都有表示变量是否在内存中数据连续存储的概念。
  • 连续存储又分为按照行优先(C order)和按照列优先(Fortran order)
行优先 C order

行是指多维数组一维展开的方式,对应的是列优先。C/C++中使用的是行优先方式(row major),Matlab、Fortran使用的是列优先方式(column major),PyTorch中Tensor底层实现是C,也是使用行优先顺序,因此也称为 C order

  • Pascal, C,C++,Python都是行优先存储的。
列优先 Fortran order

Fortran Order则指的是列优先的顺序(Column-major Order),即内存中同列的元素存在一起。

  • Fortran,MatLab是列优先存储的。

数据举例

行优先

考虑一个2维数组arr = np.arange(12).reshape(3,4)。这个数组看起来结构是这样的:

在计算机的内存里,数组arr实际存储是像下图所示的:

这意味着arrC连续的C contiguous)的,因为在内存是行优先的,即某个元素在内存中的下一个位置存储的是它同行的下一个值。

如果想要向下移动一列,则只需要跳过3个块既可(例如,从0到4只需要跳过1,2和3)。

代码语言:javascript
复制
import numpy as np

if __name__ == '__main__':
    arr = np.arange(12).reshape(3, 4)
    print(arr.flags)
    print(arr)

    pass

  • 输出:
代码语言:javascript
复制
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : False
  WRITEABLE : True
  ALIGNED : True
  WRITEBACKIFCOPY : False
  UPDATEIFCOPY : False

[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
  • C_CONTIGUOUS 为 True 表示该矩阵行连续

也就是其中的行 [ 0 1 2 3] 在内存中连续,那么 [0 4 8] 就不会连续了,因此 F_CONTIGUOUS 为 False

列优先

上述数组的转置arr.T则没有了C连续特性,因为同一行中的相邻元素现在并不是在内存中相邻存储的了:

这里要说明一下,如果直接用这些值创建的numpy变量是连续的,因为Python默认 C order,新创建的numpy都是行优先的 但是我们创建arr时是以 0 - 11 为顺序创建的,其中[0 1 2 3] [4 5 6 7] [8 9 10 11]连续,矩阵转置后只改变引用,内存数据并不发生变化 类似的操作如numpy 的 slicetranspose转置 或 tensor中的 permute 等操作都可能导致改变之前数据与内存的行连续状况

  • 转置后,内存上仍然是 [0 1 2 3] [4 5 6 7] [8 9 10 11]连续,在当前矩阵上就是列连续,因此这是个Fortran order 连续的矩阵
代码语言:javascript
复制
import numpy as np

if __name__ == '__main__':
    arr = np.arange(12).reshape(3, 4)
    arr = arr.T
    print(arr.flags)
    print(arr)

    pass
  • 输出:
代码语言:javascript
复制
  C_CONTIGUOUS : False
  F_CONTIGUOUS : True
  OWNDATA : False
  WRITEABLE : True
  ALIGNED : True
  WRITEBACKIFCOPY : False
  UPDATEIFCOPY : False

[[ 0  4  8]
 [ 1  5  9]
 [ 2  6 10]
 [ 3  7 11]]

查看连续性

numpy
  • 可以使用 data.contiguousdata.c_contiguousdata.f_contiguous 属性
  • 或者使用 flags 属性
代码语言:javascript
复制
import numpy as np


if __name__ == '__main__':
    arr = np.arange(12).reshape(3, 4)

    print(arr.data.contiguous)  # True
    print(arr.data.c_contiguous)  # True
    print(arr.data.f_contiguous)  # False

    print(arr.flags)
    print(arr)

    pass
  • 输出
代码语言:javascript
复制
True
True
False
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : False
  WRITEABLE : True
  ALIGNED : True
  WRITEBACKIFCOPY : False
  UPDATEIFCOPY : False

[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

pytorch
  • pytorch 的 tensor 有方法 is_contiguous 用来查看是否 C 连续
代码语言:javascript
复制
import torch
import numpy as np


if __name__ == '__main__':
    arr = np.arange(12).reshape(3, 4)
    ten = torch.from_numpy(arr)

    print(ten.is_contiguous())  # True

    ten_t = torch.from_numpy(arr.T)
    print(ten_t.is_contiguous())  # False

    pass

  • 输出
代码语言:javascript
复制
True
False

产生的影响

性能影响

从性能上来说,获取内存中相邻的地址比不相邻的地址速度要快很多(从RAM读取一个数值的时候可以连着一起读一块地址中的数值,并且可以保存在Cache中),这意味着对连续数组的操作会快很多。

由于arr是C连续的,因此对其进行行操作比进行列操作速度要快 通常来说

代码语言:javascript
复制
np.sum(arr, axis=1) # 按行求和

会比

代码语言:javascript
复制
np.sum(arr, axis=0) # 按列求和

稍微快些。 同理,在arr.T上,列操作比行操作会快些。

结果影响

其实写这篇博客的原因,就是我的onnx模型对于完全相同数据的tensor产生了完全不同的表现,险些三观俱碎。挣扎了几个小时后发现原来是数据的连续性在作祟。

  • 对 pyhton 中算法平台的影响

平台

影响

numpy

计算不连续的变量,结果不会受到影响

pytorch

输入不连续的tensor,结果不会受到影响

onnx

输入不连续的tensor,结果直接爆炸

  • 在 python 的 tensor 中,如果不是C连续的tensor,在执行 view 方法时会报错:
代码语言:javascript
复制
invalid argument 2: view size is not compatible with input tensor's size and stride (at least one dimension 
spans across two contiguous subspaces). Call .contiguous() before .view(). 

  • 在 numpy 中某些需要连续的操作在遇到不连续的变量时也会报错:
代码语言:javascript
复制
ValueError: some of the strides of a given numpy array are negative.
This is currently not supported, but will be added in future releases.

连带影响
  • 不连续的numpy转为tensor后也是不连续的
  • 不连续的tensor转为numpy后也是不连续的

修正连续性

变量可以通过重新开辟空间,将数据连续拷贝进去的方法将不连续的数据变成某种连续方式。

numpy
  • numpy 变量中连续性可以用自带的函数修正,不连续的变量通过函数 np.ascontiguousarray(arr)变为C连续,np.asfortranarray(arr)变为Fortran连续
代码语言:javascript
复制
import numpy as np


if __name__ == '__main__':
    arr = np.arange(12).reshape(3, 2, 2)
    print(arr.data.c_contiguous)  # True arr C连续

    tran_arr = arr.transpose(2, 0, 1)
    print(tran_arr.data.contiguous)  # False tran_arr不连续

    c_arr = np.ascontiguousarray(tran_arr)  # 变为 C 连续
    print(c_arr.flags)

    f_arr = np.asfortranarray(tran_arr)  # 变为 Fortran 连续
    print(f_arr.flags)

    pass

  • 输出:
代码语言:javascript
复制
True
False
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  WRITEBACKIFCOPY : False
  UPDATEIFCOPY : False

  C_CONTIGUOUS : False
  F_CONTIGUOUS : True
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  WRITEBACKIFCOPY : False
  UPDATEIFCOPY : False

pytorch
  • pytorch 的 tensor 在python中运行,需要C连续的变量,因此只有C连续的函数 contiguous()
代码语言:javascript
复制
import torch
import numpy as np


if __name__ == '__main__':
    arr = np.arange(12).reshape(3, 2, 2)

    tran_arr = arr.transpose(2, 0, 1)
    print(tran_arr.data.contiguous)  # False tran_arr不连续

    ten = torch.from_numpy(tran_arr)
    print(ten.is_contiguous())  # False 不连续的numpy产生不连续的tensor

    c_ten = ten.contiguous()
    print(c_ten.is_contiguous())  # True Tensor变成连续的

    c_ten_arr = c_ten.numpy()
    print(c_ten_arr.flags)  # tensor 的连续函数结果是变成C连续的变量

    pass

  • 输出
代码语言:javascript
复制
False
False
True
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : False
  WRITEABLE : True
  ALIGNED : True
  WRITEBACKIFCOPY : False
  UPDATEIFCOPY : False

参考资料

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021年8月2日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 含义
    • 行优先 C order
      • 列优先 Fortran order
      • 数据举例
        • 行优先
          • 列优先
          • 查看连续性
            • numpy
              • pytorch
              • 产生的影响
                • 性能影响
                  • 结果影响
                    • 连带影响
                    • 修正连续性
                      • numpy
                        • pytorch
                        • 参考资料
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档