tensor的數(shù)據(jù)結(jié)構(gòu)、storage()、stride()、storage_offset()

1. tensor的數(shù)據(jù)結(jié)構(gòu)

pytorch中一個tensor對象分為頭信息區(qū)(Tensor)存儲區(qū)(Storage)兩部分。
頭信息區(qū)主要保存tensor的形狀(size)、步長(stride)、數(shù)據(jù)類型(type)等信息;而真正的data(數(shù)據(jù))則以連續(xù)一維數(shù)組的形式放在存儲區(qū),由torch.Storage實例管理著。
注意:storage永遠是一維數(shù)組,任何維度的tensor的實際數(shù)據(jù)都存儲在一維的storage中。

大部分情況下一個tensor有獨立的頭信息區(qū)和storage,但pytorch中也可以多個不同的tensor共享一個storage,這么做是為了節(jié)省內(nèi)存。
也正因如此,從一個已有的tensor創(chuàng)建一個新的tensor時速度很快,因為并不會真正為新tensor開辟新內(nèi)存。
如下圖所示:

tensor結(jié)構(gòu).png

2. storage()

2.1 獲取tensor的storage

>>>a = torch.tensor([[1.0, 4.0],[2.0, 1.0],[3.0, 5.0]])
>>>a.storage()
 1.0
 4.0
 2.0
 1.0
 3.0
 5.0
[torch.FloatStorage of size 6]

2.2 對storage進行索引取值

>>>a_storage = a.storage()
>>>a_storage[2] #獲取第三個值
2.0

2.3 更改storage的值

注意:tensor的值存儲在storage中,若改變了storage的值,勢必會改變tensor的值。

>>>a_storage[0]=100 #改變storage的第1個值
>>>print(a.storage())
>>>print(a)
 100.0
 4.0
 2.0
 1.0
 3.0
 5.0
[torch.FloatStorage of size 6]
tensor([[100.,   4.],
        [  2.,   1.],
        [  3.,   5.]])

2.4 tensor的id()和tensor.storage的id()

注意:當一個tensor創(chuàng)建后,id(tensor)固定不變(這沒毛?。?;但id(tensor.storage)每次執(zhí)行都會變化(這就有點詭異)★★★★,原因還不清楚。

#只運行一次
>>>a = torch.tensor([1,2]) 
#多次運行
>>>print(id(a)) 
>>>print(id(a.storage()))
2638892189312
2638989250912

2638892189312
2638989251056

2638892189312
2638989153376

2.5 多個tensor共用storage(判斷多個tensor是否共用一個storage)

2.5.1 創(chuàng)建三個tensor(注意,只運行一次)

新建三個tensor a、b、c,其中b和c是在a的基礎(chǔ)上創(chuàng)建的,事實上這三者共用了一個storage。

>>>a = torch.tensor([[1,2,3],[4,5,6]])
>>>b = a.view(3,2)
>>>c = a[:,1]
>>>print(a)
tensor([[1, 2, 3],
        [4, 5, 6]])
>>>print(b)
tensor([[1, 2],
        [3, 4],
        [5, 6]])
>>>print(c)
tensor([2, 5])
2.5.2 利用id(tensor)獲取tensor頭信息區(qū)地址(不能判斷是否共用storage)

id(tensor)得到的是tensor“頭信息區(qū)”的地址,任何不同的tensor,其頭信息區(qū)是不一樣的,因此這里得到的三個不同的結(jié)果。
當tensor創(chuàng)建后,id(tensor)固定不變。

>>>print(id(a))
>>>print(id(b))
>>>print(id(c))
2638851940160
2638874315168
2638982342992
2.5.3 查看三個tensor的storage(不能判斷是否共用storage)

雖然結(jié)果是一樣的(但這還不能說是共用同一個storage)。
這里也可以看出,張量c雖然只有2個元素,但其storage依然是完整的1、2、3、4、5、6。這說明并沒有給張量c獨立創(chuàng)建storage,而是共享了a的storage。
可見,tensor的元素和他的storage的元素不一定相同。

>>>print(a.storage())
>>>print(b.storage())
>>>print(c.storage())
 1
 2
 3
 4
 5
 6
[torch.LongStorage of size 6]
 1
 2
 3
 4
 5
 6
[torch.LongStorage of size 6]
 1
 2
 3
 4
 5
 6
[torch.LongStorage of size 6]
2.5.4 利用tensor.data_ptr()查看tensor的首元素的地址(不能判斷是否共用storage)

從前面可知a和b的首元素都是1,因此其首元素地址相同;而c的首元素是2,顯然首元素地址與a和b是不一樣。
多次運行代碼,結(jié)果不變。

>>>print(a.data_ptr())
>>>print(b.data_ptr())
>>>print(c.data_ptr())
2638924337600
2638924337600
2638924337608
2.5.5 ★★利用tensor.storage.data_ptr()查看storage的首元素的地址(可以判斷是否共用storage)

tensor.storage().data_ptr()返回的是storage的首元素地址,如果他們相同,則肯定是同一個storage。

>>>print(a.storage().data_ptr())
>>>print(b.storage().data_ptr())
>>>print(c.storage().data_ptr())
2638924337600
2638924337600
2638924337600
2.5.6 利用id(tensor.storage())獲取storage地址(不能判斷是否共用storage)

這條最奇怪!
根據(jù)前面我們已經(jīng)知道,a、b、c共用storage,因此理論上id(tensor.storage())的結(jié)果是一樣的,但實際上三者結(jié)果不一樣,更奇怪的是每次運行都變化(網(wǎng)上很多例子都說這里三者是相同的,不知道是pytorch后面的版本發(fā)生了變化,還是啥原因)。

>>>print(id(a.storage()))
>>>print(id(b.storage()))
>>>print(id(c.storage()))
第一次運行結(jié)果
2638986051440
2638986049856
2638986050432
第二次運行結(jié)果
2638986050912
2638986047696
2638986048944
2.5.7 共用storage時,一個變,全部變
>>>c[0]=100  #從tensor改變某個元素
>>>print(a)
>>>print(b)
>>>print(c)
tensor([[  1, 100,   3],
        [  4,   5,   6]])
tensor([[  1, 100],
        [  3,   4],
        [  5,   6]])
tensor([100,   5])

>>>b.storage()[3]=-100 #從storage改變某個元素
>>>print(a)
>>>print(b)
>>>print(c)
tensor([[   1,  100,    3],
        [-100,    5,    6]])
tensor([[   1,  100],
        [   3, -100],
        [   5,    6]])
tensor([100,   5])

3. stride()

stride是在指定維度(dim)中從一個元素跳到緊鄰下一個元素所必需的步長。當沒有參數(shù)傳入時,stride()返回由每個維度步長組成的一個元組。如果有整數(shù)參數(shù)傳入,則返回該整數(shù)指定的維度的步長。
注意:前面講過,pytorch中tensor的實際數(shù)據(jù)在內(nèi)存中是按照行優(yōu)先連續(xù)存儲的storage。stride的計算也是按照storage存儲的位置進行計算。
tensor 連續(xù)情況:
如下例中,tensor a總共有0和1兩個dim。
沿著dim0(即縱向),從一個元素跳到下一個元素(如從4到7)要經(jīng)過2、5、7,三個元素;
沿著dim1(即橫向),從一個元素跳到下一個元素(如從4到2)只經(jīng)過2,一個元素。

stride1.jpg

>>>a = torch.tensor([[4,2,5],[7,6,9]])
>>>print(a)
tensor([[4, 2, 5],
        [7, 6, 9]])
>>>print(a.storage())
 4
 2
 5
 7
 6
 9
[torch.LongStorage of size 6]
>>>print(a.stride())
(3, 1)
>>>print(a.stride(0))
3
>>>print(a.stride(1))
1

tensor 不連續(xù)情況:
如下例中,b是a的轉(zhuǎn)置,則b是不連續(xù)的。
沿著dim0(即縱向),從一個元素跳到下一個元素(如從4到2)要經(jīng)過2,一個元素;
沿著dim1(即橫向),從一個元素跳到下一個元素(如從4到7)只經(jīng)過2、5、7,三個元素。


stride2.jpg
>>>a = torch.tensor([[4,2,5],[7,6,9]])
>>>b = a.t()
>>>print(b)
>>>print(b.storage())
>>>print(b.stride())
tensor([[4, 7],
        [2, 6],
        [5, 9]])
 4
 2
 5
 7
 6
 9
[torch.LongStorage of size 6]
(1, 3)

4.storage_offset()

返回tensor的第一個元素與其storage的第一個元素的偏移量。
前面已經(jīng)說過一個tensor的元素和他的storage的元素不一定完全相同(共享別人的storage)。
下例中a的第一個元素是3,a的storage的第一個元素也是3,因此其storage_offset是0;
b的第一個元素是5,而b的storage的第一個元素還是3,從3到5的storage_offset是2。

>>>a = torch.tensor([[3,2,5],[7,6,9]])
>>>print(a)
tensor([[3, 2, 5],
        [7, 6, 9]])
>>>print(a.storage())
 3
 2
 5
 7
 6
 9
[torch.LongStorage of size 6]
>>>print(a.storage_offset())
0

>>>b =a[:,2] #在a的基礎(chǔ)上生成
>>>print(b)
tensor([5, 9])
>>>print(b.storage())
 3
 2
 5
 7
 6
 9
[torch.LongStorage of size 6]
>>>print(b.storage_offset())
2
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容