【小白學(xué)PyTorch】9.tensor數(shù)據(jù)結(jié)構(gòu)與存儲(chǔ)結(jié)構(gòu)
小白學(xué)PyTorch | 8 實(shí)戰(zhàn)之MNIST小試牛刀
小白學(xué)PyTorch | 7 最新版本torchvision.transforms常用API翻譯與講解
小白學(xué)PyTorch | 6 模型的構(gòu)建訪問遍歷存儲(chǔ)(附代碼)
小白學(xué)PyTorch | 5 torchvision預(yù)訓(xùn)練模型與數(shù)據(jù)集全覽
小白學(xué)PyTorch | 4 構(gòu)建模型三要素與權(quán)重初始化
小白學(xué)PyTorch | 3 淺談Dataset和Dataloader
小白學(xué)PyTorch | 2 淺談?dòng)?xùn)練集驗(yàn)證集和測試集
小白學(xué)PyTorch | 1 搭建一個(gè)超簡單的網(wǎng)絡(luò)
小白學(xué)PyTorch | 動(dòng)態(tài)圖與靜態(tài)圖的淺顯理解
參考目錄:
1 pytorch數(shù)據(jù)結(jié)構(gòu)
1.1 默認(rèn)整數(shù)與浮點(diǎn)數(shù)
1.2 dtype修改變量類型
1.3 變量類型有哪些
1.4 數(shù)據(jù)類型轉(zhuǎn)換
2 torch vs numpy
2.1 兩者轉(zhuǎn)換
2.2 兩者區(qū)別
3 張量
3.1 張量修改尺寸
3.2 張量內(nèi)存存儲(chǔ)結(jié)構(gòu)
3.3 存儲(chǔ)區(qū)
3.4 頭信息區(qū)
1 pytorch數(shù)據(jù)結(jié)構(gòu)
1.1 默認(rèn)整數(shù)與浮點(diǎn)數(shù)
【pytorch默認(rèn)的整數(shù)是int64】
pytorch的默認(rèn)整數(shù)是用64個(gè)比特存儲(chǔ),也就是8個(gè)字節(jié)(Byte)存儲(chǔ)的。
【pytorch默認(rèn)的浮點(diǎn)數(shù)是float32】
pytorch的默認(rèn)浮點(diǎn)數(shù)是用32個(gè)比特存儲(chǔ),也就是4個(gè)字節(jié)(Byte)存儲(chǔ)的。
import torch
import numpy as np
#----------------------
print('torch的浮點(diǎn)數(shù)與整數(shù)的默認(rèn)數(shù)據(jù)類型')
a = torch.tensor([1,2,3])
b = torch.tensor([1.,2.,3.])
print(a,a.dtype)
print(b,b.dtype)
輸出:
torch的浮點(diǎn)數(shù)與整數(shù)的默認(rèn)數(shù)據(jù)類型
tensor([1,?2,?3])?torch.int64
tensor([1.,?2.,?3.])?torch.float32
1.2 dtype修改變量類型
print('torch的浮點(diǎn)數(shù)與整數(shù)的默認(rèn)數(shù)據(jù)類型')
a = torch.tensor([1,2,3],dtype=torch.int8)
b = torch.tensor([1.,2.,3.],dtype = torch.float64)
print(a,a.dtype)
print(b,b.dtype)
輸出結(jié)果:
torch的浮點(diǎn)數(shù)與整數(shù)的默認(rèn)數(shù)據(jù)類型
tensor([1,?2,?3],?dtype=torch.int8)?torch.int8
tensor([1.,?2.,?3.],?dtype=torch.float64)?torch.float64
1.3 變量類型有哪些
張量的數(shù)據(jù)類型其實(shí)和numpy.array基本一一對(duì)應(yīng),除了不支持str,主要有下面幾種形式:
torch.float64?#?等同于(torch.double)
torch.float32?#?默認(rèn),FloatTensor
torch.float16
torch.int64???#?等同于torch.long
torch.int32???#?默認(rèn)
torch.int16
torch.int8
torch.uint8???#?二進(jìn)制碼,表示0-255
torch.bool
在創(chuàng)建變量的時(shí)候,想要?jiǎng)?chuàng)建指定的變量類型,上文中提到了用dtype關(guān)鍵字來控制,但是我個(gè)人更喜歡使用特定的構(gòu)造函數(shù):
print('torch的構(gòu)造函數(shù)')
a = torch.IntTensor([1,2,3])
b = torch.LongTensor([1,2,3])
c = torch.FloatTensor([1,2,3])
d = torch.DoubleTensor([1,2,3])
e = torch.tensor([1,2,3])
f = torch.tensor([1.,2.,3.])
print(a.dtype)
print(b.dtype)
print(c.dtype)
print(d.dtype)
print(e.dtype)
print(f.dtype)
輸出結(jié)果:
torch的構(gòu)造函數(shù)
torch.int32
torch.int64
torch.float32
torch.float64
torch.int64
torch.float32
因此我們可以得到結(jié)果:
torch.IntTensor對(duì)應(yīng)torch.int32torch.LongTensor對(duì)應(yīng)torch.int64,LongTensor常用在深度學(xué)習(xí)中的標(biāo)簽值 ,比方說分類任務(wù)中的類別標(biāo)簽0,1,2,3等,要求用ing64的數(shù)據(jù)類型;torch.FloatTensor對(duì)應(yīng)torch.float32。FloatTensor常用做深度學(xué)習(xí)中可學(xué)習(xí)參數(shù)或者輸入數(shù)據(jù)的類型torch.DoubleTensor對(duì)應(yīng)torch.float64torch.tensor則有一個(gè)推斷的能力,加入輸入的數(shù)據(jù)是整數(shù),則默認(rèn)int64,相當(dāng)于LongTensor;假如輸入數(shù)據(jù)是浮點(diǎn)數(shù),則默認(rèn)float32,相當(dāng)于FLoatTensor。剛好對(duì)應(yīng)深度學(xué)習(xí)中的標(biāo)簽和參數(shù)的數(shù)據(jù)類型,所以一般情況下,直接使用tensor就可以了,但是假如出現(xiàn)報(bào)錯(cuò)的時(shí)候,也要學(xué)會(huì)使用dtype或者構(gòu)造函數(shù)來確保數(shù)據(jù)類型的匹配
1.4 數(shù)據(jù)類型轉(zhuǎn)換
【使用torch.float()方法】
print('數(shù)據(jù)類型轉(zhuǎn)換')
a?=?torch.tensor([1,2,3])
b?=?a.float()
c?=?a.double()
d?=?a.long()
print(b.dtype)
print(c.dtype)
print(d.dtype)
>>>?數(shù)據(jù)類型轉(zhuǎn)換
>>>?torch.float32
>>>?torch.float64
>>>?torch.int64
我個(gè)人比較習(xí)慣這個(gè)的方法。
【使用type方法】
b?=?a.type(torch.float32)
c?=?a.type(torch.float64)
d?=?a.type(torch.int64)
print(b.dtype)?#?torch.float32
print(c.dtype)?#?torch.float64
print(d.dtype)?#?torch.int64
2 torch vs numpy
PyTorch是一個(gè)python包,目的是加入深度學(xué)習(xí)應(yīng)用, torch基本上是實(shí)現(xiàn)了numpy的大部分必要的功能,并且tensor是可以利用GPU進(jìn)行加速訓(xùn)練的。
2.1 兩者轉(zhuǎn)換
轉(zhuǎn)換時(shí)非常非常簡單的:
import?torch
import?numpy?as?np
a?=?np.array([1.,2.,3.])
b?=?torch.tensor(a)
c?=?b.numpy()
print(a)
print(b)
print(c)
輸出結(jié)果:
[1.?2.?3.]
tensor([1.,?2.,?3.],?dtype=torch.float64)
[1.?2.?3.]
下面的內(nèi)容就變得有點(diǎn)意思了,是內(nèi)存復(fù)制相關(guān)的。假如a和b兩個(gè)變量共享同一個(gè)內(nèi)存,那么改變a的話,b也會(huì)跟著改變;如果a和b變量的內(nèi)存復(fù)制了,那么兩者是兩個(gè)內(nèi)存,所以改變a是不會(huì)改變b的。下面是講解numpy和torch互相轉(zhuǎn)換的時(shí)候,什么情況是共享內(nèi)存,什么情況下是內(nèi)存復(fù)制 (其實(shí)這個(gè)問題,也就是做個(gè)了解罷了,無用的小知識(shí))
【Tensor()轉(zhuǎn)換】當(dāng)numpy的數(shù)據(jù)類型和torch的數(shù)據(jù)類型相同時(shí),共享內(nèi)存;不同的時(shí)候,內(nèi)存復(fù)制
print('numpy?和torch互相轉(zhuǎn)換1')
a?=?np.array([1,2,3],dtype=np.float64)
b?=?torch.Tensor(a)
b[0]?=?999
print('共享內(nèi)存'?if?a[0]==b[0]?else?'不共享內(nèi)存')
>>>?不共享內(nèi)存
因?yàn)閚p.float64和torch.float32數(shù)據(jù)類型不同
print('numpy?和torch互相轉(zhuǎn)換2')
a?=?np.array([1,2,3],dtype=np.float32)
b?=?torch.Tensor(a)
b[0]?=?999
print('共享內(nèi)存'?if?a[0]==b[0]?else?'不共享內(nèi)存')
>>>?共享內(nèi)存
因?yàn)閚p.float32和torch.float32數(shù)據(jù)類型相同
【from_numpy()轉(zhuǎn)換】
print('from_numpy()')
a?=?np.array([1,2,3],dtype=np.float64)
b?=?torch.from_numpy(a)
b[0]?=?999
print('共享內(nèi)存'?if?a[0]==b[0]?else?'不共享內(nèi)存')
>>>?共享內(nèi)存
a?=?np.array([1,2,3],dtype=np.float32)
b?=?torch.from_numpy(a)
b[0]?=?999
print('共享內(nèi)存'?if?a[0]==b[0]?else?'不共享內(nèi)存')
>>>?共享內(nèi)存
如果你使用from_numpy()的時(shí)候,不管是什么類型,都是共享內(nèi)存的。
【tensor()轉(zhuǎn)換】
更常用的是這個(gè)tensor(),注意看T的大小寫, 如果使用的是tensor方法,那么不管輸入類型是什么,torch.tensor都會(huì)進(jìn)行數(shù)據(jù)拷貝,不共享內(nèi)存。
【.numpy()】tensor轉(zhuǎn)成numpy的時(shí)候,.numpy方法是內(nèi)存共享的哦。如果想改成內(nèi)存拷貝的話,可以使用.numpy().copy()就不共享內(nèi)存了?;蛘呤褂?code style="padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">.clone().numpy()也可以實(shí)現(xiàn)同樣的效果。clone是tensor的方法,copy是numpy的方法。
【總結(jié)】
記不清的話,就記住,tensor()數(shù)據(jù)拷貝了,.numpy()共享內(nèi)存就行了。
2.2 兩者區(qū)別
【命名】
雖然PyTorch實(shí)現(xiàn)了Numpy的很多功能,但是相同的功能卻有著不同的命名方式,這讓使用者迷惑。
例如創(chuàng)建隨機(jī)張量的時(shí)候:
print('命名規(guī)則')
a?=?torch.rand(2,3,4)
b?=?np.random.rand(2,3,4)
【張量重塑】
這部分會(huì)放在下一章節(jié)詳細(xì)說明~
3 張量
標(biāo)量:數(shù)據(jù)是一個(gè)數(shù)字 向量:數(shù)據(jù)是一串?dāng)?shù)字,也是一維張量 矩陣:數(shù)據(jù)二維數(shù)組,也是二維張量 張量:數(shù)據(jù)的維度超過2的時(shí)候,就叫多維張量
3.1 張量修改尺寸
pytorch常用reshape和view numpy用resize和reshape pytorch也有resize但是不常用
【reshape和view共享內(nèi)存(常用)】
a?=?torch.arange(0,6)
b?=?a.reshape((2,3))
print(b)
c?=?a.view((2,3))
print(c)
a[0]?=?999
print(b)
print(c)
輸出結(jié)果:
tensor([[0,?1,?2],
????????[3,?4,?5]])
tensor([[0,?1,?2],
????????[3,?4,?5]])
tensor([[999,???1,???2],
????????[??3,???4,???5]])
tensor([[999,???1,???2],
????????[??3,???4,???5]])
上面的a,b,c三個(gè)變量其實(shí)是共享同一個(gè)內(nèi)存,遷一而動(dòng)全身。而且要求遵旨規(guī)則:原始數(shù)據(jù)有6個(gè)元素,所以可以修改成的形式,但是無法修改成的形式 ,我們來試試:
a?=?torch.arange(0,6)
b?=?a.reshape((2,4))
會(huì)拋出這樣的錯(cuò)誤:
【torch的resize_(不常用)】
但是pytorch有一個(gè)不常用的函數(shù)(對(duì)我來說用的不多),resize,這個(gè)方法可以不遵守這個(gè)規(guī)則:
a?=?torch.arange(0,6)
a.resize_(2,4)
print(a)
輸出結(jié)果為:
自動(dòng)的補(bǔ)充了兩個(gè)元素。雖然不知道這個(gè)函數(shù)有什么意義。。。。。。
這里可以看到函數(shù)resize后面有一個(gè)_,這個(gè)表示inplace=True的意思,當(dāng)有這個(gè)_或者參數(shù)inplace的時(shí)候,就是表示所作的修改是在原來的數(shù)據(jù)變量上完成的,也就不需要賦值給新的變量了。
【numpy的resize與reshape(常用)】
import?numpy?as?np
a?=?np.arange(0,6)
a.resize(2,3)
print(a)
import?numpy?as?np
a?=?np.arange(0,6)
b?=?a.reshape(2,3)
print(b)
兩個(gè)代碼塊的輸出都是下面的,區(qū)別在于numpy的resize是沒有返回值的,相當(dāng)于inplace=True了,直接在原變量的進(jìn)行修改,而reshape是有返回值的,不在原變量上修改(但是呢reshape是共享內(nèi)存的):
[[0?1?2]
?[3?4?5]]
3.2 張量內(nèi)存存儲(chǔ)結(jié)構(gòu)
tensor的數(shù)據(jù)結(jié)構(gòu)包含兩個(gè)部分:
頭信息區(qū)Tensor:保存張量的形狀size,步長stride,數(shù)據(jù)類型等信息 存儲(chǔ)區(qū)Storage:保存真正的數(shù)據(jù)
頭信息區(qū)Tensor的占用內(nèi)存較小,主要的占用內(nèi)存是Storate。
每一個(gè)tensor都有著對(duì)應(yīng)的storage,一般不同的tensor的頭信息可能不同,但是卻可能使用相同的storage。(這里就是之前共享內(nèi)存的view、reshape方法,雖然頭信息的張量形狀size發(fā)生了改變,但是其實(shí)存儲(chǔ)的數(shù)據(jù)都是同一個(gè)storage)
3.3 存儲(chǔ)區(qū)
我們來查看一個(gè)tensor的存儲(chǔ)區(qū):
import?torch
a?=?torch.arange(0,6)
print(a.storage())
輸出為:
?0
?1
?2
?3
?4
?5
[torch.LongStorage?of?size?6]
然后對(duì)tensor變量做一個(gè)view的變換:
b?=?a.view(2,3)
這個(gè)b.storage()輸出出來時(shí)和a.storate(),相同的,這也是為什么view變換是內(nèi)存共享的了。
#?id()是獲取對(duì)象的內(nèi)存地址
print(id(a)==id(b))?#?False
print(id(a.storage)==id(b.storage))?#?True
可以發(fā)現(xiàn),其實(shí)a和b雖然存儲(chǔ)區(qū)是相同的,但是其實(shí)a和b整體式不同的。自然,這個(gè)不同就不同在頭信息區(qū),應(yīng)該是尺寸size改變了。這也就是頭信息區(qū)不同,但是存儲(chǔ)區(qū)相同,從而節(jié)省大量內(nèi)存
我們更進(jìn)一步,假設(shè)對(duì)tensor切片了,那么切片后的數(shù)據(jù)是否共享內(nèi)存,切片后的數(shù)據(jù)的storage是什么樣子的呢?
print('研究tensor的切片')
a?=?torch.arange(0,6)
b?=?a[2]
print(id(a.storage)==id(b.storage))
輸出結(jié)果為:
>>>?True
沒錯(cuò),就算切片之后,兩個(gè)tensor依然使用同一個(gè)存儲(chǔ)區(qū),所以相比也是共享內(nèi)存的,修改一個(gè)另一個(gè)也會(huì)變化。
#.data_ptr(),返回tensor首個(gè)元素的內(nèi)存地址。
print(a.data_ptr(),b.data_ptr())
print(b.data_ptr()-a.data_ptr())
輸出為:
2080207827328?2080207827344
16
這是因?yàn)閎的第一個(gè)元素和a的第一個(gè)元素內(nèi)存地址相差了16個(gè)字節(jié),因?yàn)槟J(rèn)的tesnor是int64,也就是8個(gè)字節(jié)一個(gè)元素,所以這里相差了2個(gè)整形元素
3.4 頭信息區(qū)
依然是上面那兩個(gè)tensor變量,a和b
a?=?torch.arange(0,6)
b?=?a.view(2,3)
print(a.stride(),b.stride())
輸出為:
(1,)?(3,?1)
變量a是一維數(shù)組,并且就是[0,1,2,3,4,5],所以步長stride是1;而b是二維數(shù)組,是[[0,1,2],[3,4,5]],所以就是先3個(gè)3個(gè)分成第一維度的,然后再1個(gè)1個(gè)的作為第二維度。
由此可見,絕大多數(shù)操作并不修改 tensor 的數(shù)據(jù),只是修改了 tensor 的頭信息,這種做法更節(jié)省內(nèi)存,同時(shí)提升了處理速度。
往期精彩回顧
獲取一折本站知識(shí)星球優(yōu)惠券,復(fù)制鏈接直接打開:
https://t.zsxq.com/662nyZF
本站qq群704220115。
加入微信群請(qǐng)掃碼進(jìn)群(如果是博士或者準(zhǔn)備讀博士請(qǐng)說明):
