参考来源:
    CSDN:torch.contiguous() 方法
    CSDN:python——Contiguous 用法

    **x.is_contiguous()**:判断 **tensor** 是否连续。
    **x.contiguous()**:把 **tensor** 变成在内存中连续分布的形式。
    **contiguous****view** 只能用在 **contiguous****variable** 上。如果在 **view** 之前用了 **transpose****permute** 等,需要用 **contiguous()** 来返回一个 **contiguous copy**

    **torch.contiguous()** 方法语义上是“连续的”,经常与 **torch.permute()****torch.transpose()****torch.view()** 方法一起使用,要理解这样使用的缘由,得从 pytorch 多维数组的低层存储开始说起:
    **touch.view()** 方法对张量改变“形状”其实并没有改变张量在内存中真正的形状,可以理解为:

    1. view 方法没有拷贝新的张量,没有开辟新内存,与原张量共享内存;
    2. view 方法只是重新定义了访问张量的规则,使得取出的张量按照我们希望的形状展现。

    pytorchnumpy 在存储 M x N 的数组时,均是按照行优先将数组拉伸至一维存储,比如对于一个二维张量

    1. # An highlighted block
    2. t = torch.tensor([[2, 1, 3], [4, 5, 9]])

    在内存中实际上是

    1. [2, 1, 3, 4, 5, 9]

    按照行优先原则,数字在语义和在内存中都是连续的,当我们使用 torch.transpose() 方法或者 torch.permute() 方法对张量翻转后,改变了张量的形状

    1. # An highlighted block
    2. t2 = t.transpose(0, 1)
    3. print(t2)
    4. """
    5. tensor([[2,4],
    6. [1,5],
    7. [3,9])
    8. """

    此时如果对 t2 使用 view 方法,会报错:

    1. t2.view((2,3))
    2. """
    3. Traceback (most recent call last):
    4. File "test.py", line 14, in <module>
    5. t2.view((2,3))
    6. RuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.
    7. """

    原因是:改变了形状的 t2 语义上是 32 列的,在内存中还是跟 t 一样,没有改变,导致如果按照语义的形状进行 view 拉伸,数字不连续,此时 torch.contiguous() 方法就派上用场了 。

    1. # An highlighted block
    2. t3 = t2.contiguous()
    3. print(t3)
    4. """
    5. tensor([[2,4],
    6. [1,5],
    7. [3,9])
    8. """

    可以看到 t3t2 一样,都是 32 列的张量,此时再对 t3 使用 view 方法:

    1. # An highlighted block
    2. t4 = t3.view(-1)
    3. print(t4)
    4. """
    5. tensor([2, 4, 1, 5, 3, 9])
    6. """

    t3 已经按照语义的形状展开了,t2t 共享内存是怎样的呢?

    1. # An highlighted block
    2. print(t.view(-1))
    3. """
    4. tensor([2, 1, 3, 4, 5, 9])
    5. """

    可以看出 contiguous 方法改变了多维数组在内存中的存储顺序,以便配合 view 方法使用; **torch.contiguous()** 方法首先拷贝了一份张量在内存中的地址,然后将地址按照形状改变后的张量的语义进行排列。