日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

pytorch reshape_PyTorch中的contiguous

發(fā)布時間:2023/12/19 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 pytorch reshape_PyTorch中的contiguous 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本文講解了pytorch中contiguous的含義、定義、實現(xiàn),以及contiguous存在的原因,非contiguous時的解決辦法。并對比了numpy中的contiguous。


contiguous 本身是形容詞,表示連續(xù)的,關(guān)于 contiguous,PyTorch 提供了is_contiguous、contiguous(形容詞動用)兩個方法 ,分別用于判定Tensor是否是 contiguous 的,以及保證Tensor是contiguous的。

PyTorch中的is_contiguous是什么含義?

is_contiguous直觀的解釋是Tensor底層一維數(shù)組元素的存儲順序與Tensor按行優(yōu)先一維展開的元素順序是否一致。

Tensor多維數(shù)組底層實現(xiàn)是使用一塊連續(xù)內(nèi)存的1維數(shù)組(行優(yōu)先順序存儲,下文描述),Tensor在元信息里保存了多維數(shù)組的形狀,在訪問元素時,通過多維度索引轉(zhuǎn)化成1維數(shù)組相對于數(shù)組起始位置的偏移量即可找到對應(yīng)的數(shù)據(jù)。某些Tensor操作(如transpose、permute、narrow、expand)與原Tensor是共享內(nèi)存中的數(shù)據(jù),不會改變底層數(shù)組的存儲,但原來在語義上相鄰、內(nèi)存里也相鄰的元素在執(zhí)行這樣的操作后,在語義上相鄰,但在內(nèi)存不相鄰,即不連續(xù)了(is not contiguous)。

如果想要變得連續(xù)使用contiguous方法,如果Tensor不是連續(xù)的,則會重新開辟一塊內(nèi)存空間保證數(shù)據(jù)是在內(nèi)存中是連續(xù)的,如果Tensor是連續(xù)的,則contiguous無操作。

行優(yōu)先

行是指多維數(shù)組一維展開的方式,對應(yīng)的是列優(yōu)先。C/C++中使用的是行優(yōu)先方式(row major),Matlab、Fortran使用的是列優(yōu)先方式(column major),PyTorch中Tensor底層實現(xiàn)是C,也是使用行優(yōu)先順序。舉例說明如下:

>>> t = torch.arange(12).reshape(3,4) >>> t tensor([[ 0, 1, 2, 3],[ 4, 5, 6, 7],[ 8, 9, 10, 11]])

二維數(shù)組 t 如圖1:

圖1. 3X4矩陣行優(yōu)先存儲邏輯結(jié)構(gòu)

數(shù)組 t 在內(nèi)存中實際以一維數(shù)組形式存儲,通過 flatten 方法查看 t 的一維展開形式,實際存儲形式與一維展開一致,如圖2,

>>> t.flatten() tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])

圖2. 3X4矩陣行優(yōu)先存儲物理結(jié)構(gòu)

而列優(yōu)先的存儲邏輯結(jié)構(gòu)如圖3。

圖3. 3X4矩陣列優(yōu)先存儲邏輯結(jié)構(gòu)

使用列優(yōu)先存儲時,一維數(shù)組中元素順序如圖4:

圖4. 3X4矩陣列優(yōu)先存儲物理結(jié)構(gòu)

說明:圖1、圖2、圖3、圖4來自:What is the difference between contiguous and non-contiguous arrays?

圖1、圖2、圖3、圖4 中顏色相同的數(shù)據(jù)表示在同一行,不論是行優(yōu)先順序、或是列優(yōu)先順序,如果要訪問矩陣中的下一個元素都是通過偏移來實現(xiàn),這個偏移量稱為步長(stride[1])。在行優(yōu)先的存儲方式下,訪問行中相鄰元素物理結(jié)構(gòu)需要偏移1個位置,在列優(yōu)先存儲方式下偏移3個位置。

為什么需要 contiguous ?

1. torch.view等方法操作需要連續(xù)的Tensor。

transpose、permute 操作雖然沒有修改底層一維數(shù)組,但是新建了一份Tensor元信息,并在新的元信息中的 重新指定 stride。torch.view 方法約定了不修改數(shù)組本身,只是使用新的形狀查看數(shù)據(jù)。如果我們在 transpose、permute 操作后執(zhí)行 view,Pytorch 會拋出以下錯誤:

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(). at /Users/soumith/b101_2/2019_02_08/wheel_build_dirs/wheel_3.6/pytorch/aten/src/TH/generic/THTensor.cpp:213

為什么 view 方法要求Tensor是連續(xù)的[2]?考慮以下操作,

>>>t = torch.arange(12).reshape(3,4) >>>t tensor([[ 0, 1, 2, 3],[ 4, 5, 6, 7],[ 8, 9, 10, 11]]) >>>t.stride() (4, 1) >>>t2 = t.transpose(0,1) >>>t2 tensor([[ 0, 4, 8],[ 1, 5, 9],[ 2, 6, 10],[ 3, 7, 11]]) >>>t2.stride() (1, 4) >>>t.data_ptr() == t2.data_ptr() # 底層數(shù)據(jù)是同一個一維數(shù)組 True >>>t.is_contiguous(),t2.is_contiguous() # t連續(xù),t2不連續(xù) (True, False)

t2 與 t 引用同一份底層數(shù)據(jù) a,如下:

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

,兩者僅是stride、shape不同。如果執(zhí)行 t2.view(-1) ,期望返回以下數(shù)據(jù) b(但實際會報錯):

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

在 a 的基礎(chǔ)上使用一個新的 stride 無法直接得到 b ,需要先使用 t2 的 stride (1, 4) 轉(zhuǎn)換到 t2 的結(jié)構(gòu),再基于 t2 的結(jié)構(gòu)使用 stride (1,) 轉(zhuǎn)換為形狀為 (12,)的 b 。但這不是view工作的方式,view 僅在底層數(shù)組上使用指定的形狀進(jìn)行變形,即使 view 不報錯,它返回的數(shù)據(jù)是:

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

這是不滿足預(yù)期的。使用contiguous方法后返回新Tensor t3,重新開辟了一塊內(nèi)存,并使用照 t2 的按行優(yōu)先一維展開的順序存儲底層數(shù)據(jù)。

>>>t3 = t2.contiguous() >>>t3 tensor([[ 0, 4, 8],[ 1, 5, 9],[ 2, 6, 10],[ 3, 7, 11]]) >>>t3.data_ptr() == t2.data_ptr() # 底層數(shù)據(jù)不是同一個一維數(shù)組 False

可以發(fā)現(xiàn) t與t2 底層數(shù)據(jù)指針一致,t3 與 t2 底層數(shù)據(jù)指針不一致,說明確實重新開辟了內(nèi)存空間。

為什么不在view 方法中默認(rèn)調(diào)用contiguous方法?

因為歷史上view方法已經(jīng)約定了共享底層數(shù)據(jù)內(nèi)存,返回的Tensor底層數(shù)據(jù)不會使用新的內(nèi)存,如果在view中調(diào)用了contiguous方法,則可能在返回Tensor底層數(shù)據(jù)中使用了新的內(nèi)存,這樣打破了之前的約定,破壞了對之前的代碼兼容性。為了解決用戶使用便捷性問題,PyTorch在0.4版本以后提供了reshape方法,實現(xiàn)了類似于 tensor.contigous().view(*args)的功能,如果不關(guān)心底層數(shù)據(jù)是否使用了新的內(nèi)存,則使用reshape方法更方便。 [3]

2. 出于性能考慮

連續(xù)的Tensor,語義上相鄰的元素,在內(nèi)存中也是連續(xù)的,訪問相鄰元素是矩陣運算中經(jīng)常用到的操作,語義和內(nèi)存順序的一致性是緩存友好的(What is a “cache-friendly” code?[4]),在內(nèi)存中連續(xù)的數(shù)據(jù)可以(但不一定)被高速緩存預(yù)取,以提升CPU獲取操作數(shù)據(jù)的速度。transpose、permute 后使用 contiguous 方法則會重新開辟一塊內(nèi)存空間保證數(shù)據(jù)是在邏輯順序和內(nèi)存中是一致的,連續(xù)內(nèi)存布局減少了CPU對對內(nèi)存的請求次數(shù)(訪問內(nèi)存比訪問寄存器慢100倍[5]),相當(dāng)于空間換時間。


PyTorch中張量是否連續(xù)的定義

對于任意的 k 維張量 t ,如果滿足對于所有 i,第 i 維相鄰元素間隔 = 第 i+1 維相鄰元素間隔 與 第 i+1 維長度的乘積,則 t 是連續(xù)的。

[6]
  • 使用 表示第 i 維相鄰元素之間間隔的位數(shù),稱為步長,可通過 stride 方法獲得。
  • 使用 表示固定其他維度時,第 i 維元素數(shù)量。

PyTorch中判讀張量是否連續(xù)的實現(xiàn)

PyTorch中通過調(diào)用 is_contiguous 方法判斷 tensor 是否連續(xù),底層實現(xiàn)為 TH 庫中THTensor.isContiguous 方法,為方便加上一些調(diào)試信息,翻譯為 Python 代碼如下:

def isContiguous(tensor):"""判斷tensor是否連續(xù) :param torch.Tensor tensor: :return: bool"""z = 1d = tensor.dim() - 1size = tensor.size()stride = tensor.stride()print("stride={} size={}".format(stride, size))while d >= 0:if size[d] != 1:if stride[d] == z:print("dim {} stride is {}, next stride should be {} x {}".format(d, stride[d], z, size[d]))z *= size[d] else:print("dim {} is not contiguous. stride is {}, but expected {}".format(d, stride[d], z))return Falsed -= 1return True

判定上文中 t、t2 是否連續(xù)的輸出如下:

>>>isContiguous(t) stride=(4, 1) size=torch.Size([3, 4]) dim 1 stride is 1, next stride should be 1 x 4 dim 0 stride is 4, next stride should be 4 x 3True >>>isContiguous(t2) stride=(1, 4) size=torch.Size([4, 3]) dim 1 is not contiguous. stride is 4, but expected 1False

從 isContiguous 實現(xiàn)可以看出,最后1維的 stride 必須為1(邏輯步長),這是合理的,最后1維即邏輯結(jié)構(gòu)上最內(nèi)層數(shù)組,其相鄰元素間隔位數(shù)為1,按行優(yōu)先順序排列時,最內(nèi)層數(shù)組相鄰元素間隔應(yīng)該為1。

numpy中張量是否連續(xù)的定義

對于任意的 N 維張量 t ,如果滿足第 k 維相鄰元素間隔 = 第 K+1維 至 最后一維的長度的乘積,則 t 是連續(xù)的。

  • 使用 表示行優(yōu)先模式下,第 維度相鄰兩個元素之間在內(nèi)存中間隔的字節(jié)數(shù),可通過 strides 屬性獲得。
  • 使用 表示每個元素的字節(jié)數(shù)(根據(jù)數(shù)據(jù)類型而定,如PyTorch中 int32 類型是 4,int64 是8)。
  • 使用 表示固定其他維度時,第 維元素的個數(shù),即 t.shape[j]。

[7]

PyTorch與numpy中contiguous定義的關(guān)系

PyTorch和numpy中對于contiguous的定義看起來有差異,本質(zhì)上是一致的。

首先對于 stride的定義指的都是某維度下,相鄰元素之間的間隔,PyTorch中的 stride 是間隔的位數(shù)(可看作邏輯步長),而numpy 中的 stride 是間隔的字節(jié)數(shù)(可看作物理步長),兩種 stride 的換算關(guān)系為:

再看對于 stride 的計算公式,PyTorch 和 numpy 從不同角度給出了公式。PyTorch 給出的是一個遞歸式定義,描述了兩個相鄰維度 stride 與 size 之間的關(guān)系。numpy 給出的是直接定義,描述了 stride 與 shape 的關(guān)系。PyTorch中的 size 與 numpy 中的 shape 含義一致,都是指 tensor 的形狀。

、 都是指當(dāng)固定其他維度時,該維度下元素的數(shù)量。

參考

  • ^訪問相鄰元素所需要跳過的位數(shù)或字節(jié)數(shù)?https://stackoverflow.com/questions/53097952/how-to-understand-numpy-strides-for-layman?answertab=active#tab-top
  • ^Munging PyTorch's tensor shape from (C, B, H) to (B, C*H)?https://stackoverflow.com/a/53940813/11452297
  • ^view() after transpose() raises non contiguous error #764?https://github.com/pytorch/pytorch/issues/764#issuecomment-317845141
  • ^What is a “cache-friendly” code??https://stackoverflow.com/a/16699282
  • ^計算機緩存Cache以及Cache Line詳解?https://zhuanlan.zhihu.com/p/37749443
  • ^Tensor.view方法對連續(xù)的描述?https://pytorch.org/docs/stable/tensors.html#torch.Tensor.view
  • ^行優(yōu)先布局的stride?https://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html#internal-memory-layout-of-an-ndarray
  • 總結(jié)

    以上是生活随笔為你收集整理的pytorch reshape_PyTorch中的contiguous的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。