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)存。
如下圖所示:

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,一個元素。

>>>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,三個元素。

>>>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