PyTorch的張量tensor和自動求導(dǎo)autograd詳解
PyTorch是一個基于python的科學(xué)計算包,除了可以替代NumPy構(gòu)建數(shù)組張量來利用GPU進行計算之外,還是一個高靈活性、速度快的深度學(xué)習(xí)平臺
張量
張量(Tensor)實際上是一個多維數(shù)組,使用GPU可以加速張量的運算。
創(chuàng)建tensor:可以通過torch.tensor()
將普通數(shù)組轉(zhuǎn)化為tensor,torch.empty()
或rand()
可以創(chuàng)建隨機tensor,torch.zeros()
、ones()
創(chuàng)建數(shù)據(jù)都是0或1的tensor。
t.clone()
可以克隆一個張量,t.size()
可以獲取張量的形狀。
import torch t = torch.tensor([[5, 6], [7, 8]]) # 轉(zhuǎn)化為tensor t1 = torch.empty(5, 3) # 創(chuàng)建隨機5×3的tensor t2 = torch.rand(5, 3) t3 = torch.zeros(5, 3, dtype=torch.float16) # 創(chuàng)建全為0的5×3數(shù)組 t4 = torch.ones(5, 3) # 創(chuàng)建全為1的 t5 = torch.ones_like(t3) # 創(chuàng)建形狀和t3一樣、全為1的數(shù)組 print(t5.size()) # 輸出:torch.Size([5, 3])
操作tensor:tensor可以像Numpy一樣直接對數(shù)組進行索引、截取、切片等操作。
view(-1,2)
可以改變tensor的形狀,類似于numpy.reshape(),這里修改為第一維長度為2,第二維-1代表自動計算維度。permute()
可以交換數(shù)組維度位置,類似于numpy.transpose()squeeze()
可以壓縮張量中長度為1的維度,比如數(shù)組[[1,2,3]]形狀為(1,3),經(jīng)過壓縮后變成長度為3的數(shù)組[1,2,3]。反之,unsqueeze()
可以在指定位置將維度增加長度為1expand()
可以按原數(shù)據(jù)為元素擴大維度,即將原來的數(shù)據(jù)復(fù)制多份并一起組成更高維度,擴大tensor不需要分配新內(nèi)存,只是僅僅新建一個tensor的視圖。使用時注意保持新維度與原維度的統(tǒng)一。
t = torch.tensor([[1, 2, 3], [4, 5, 6]]) print(t[0, :2]) # 選取首行前兩個元素,輸出tensor([1, 2]) print(t.view(-1, 2)) # 改變形狀 ''' tensor([[1, 2], [3, 4], [5, 6]]) ''' t = torch.unsqueeze(t, dim=0) # 給張量第一維增加一個維度 print(t) ''' tensor([[[1, 2, 3], [4, 5, 6]]]) ''' print(t.permute(2, 1, 0)) # 交換維度位置 ''' tensor([[1, 3, 5], [2, 4, 6]]) ''' print(t.expand(2, 2, 3)) # 將(2,3)的tensor擴維成為(2,2,3) ''' tensor([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]]) '''
通過tensor進行四則運算也十分方便,下面以加法為例記錄幾種使用的形式:
x = torch.tensor([[1, 2, 3], [4, 5, 6]]) y = torch.tensor([[7, 8, 9], [10, 11, 12]]) print(x + y) # 用運算符 print(torch.add(x, y)) # 用add()函數(shù) z = torch.empty(2, 3) torch.add(x, y, out=z) # 指明輸出對象 print(z) y.add_(x) # 在原數(shù)組上操作 print(y)
值得注意的是,pytorch中在原張量上進行的操作函數(shù)后面都跟有一個_
,例如其他的還有在原張量上復(fù)制x.copy_()。
和NumPy數(shù)組相互轉(zhuǎn)化:通過tensor.numpy()
可以將tensor轉(zhuǎn)為numpy數(shù)組,但二者會共享一個內(nèi)存空間,即對tensor進行的修改操作之后,輸出numpy的結(jié)果也會隨之改變。如果希望產(chǎn)生一個新的可以通過clone()函數(shù)。通過torch.from_numpy()
可以從numpy數(shù)組創(chuàng)建tensor,同樣地二者共享內(nèi)存。
t = torch.ones(5) print(t) n1 = t.numpy() # 轉(zhuǎn)化為numpy n2 = t.clone().numpy() t.add_(1) # 對原來的tensor進行操作 print(n1) print(n2) n = np.ones(5) t2 = torch.from_numpy(n) # 從numpy創(chuàng)建tensor ''' tensor([1., 1., 1., 1., 1.]) 原來的t [2. 2. 2. 2. 2.] 操作t后對應(yīng)的n1也隨之改變 [1. 1. 1. 1. 1.] 但是克隆產(chǎn)生的n2不會改變 '''
使用不同設(shè)備計算tensor,例如下面使用to()
來將tensor移入和移出GPU
if torch.cuda.is_available(): device = torch.device("cuda") # a CUDA device object y = torch.ones_like(x, device=device) # 直接在GPU上創(chuàng)建tensor x = x.to(device) # 或者使用.to("cuda"),將tensor移入GPU z = x + y print(z) print(z.to("cpu", torch.double)) # 將tensor移出GPU,也能在移動時改變dtype ''' tensor([2., 2., 2., 2., 2.], device='cuda:0') tensor([2., 2., 2., 2., 2.], dtype=torch.float64) '''
自動求導(dǎo)
PyTorch中,所有神經(jīng)網(wǎng)絡(luò)的核心是 autograd
包,它為張量上的所有操作提供了自動求導(dǎo)機制。
Pytorch通過Tensor
來儲存數(shù)據(jù),我們可以把它看作一個節(jié)點,通過Function
實現(xiàn)數(shù)據(jù)操作,即節(jié)點之間轉(zhuǎn)換的路徑。
如下所示為一個訓(xùn)練過程,輸入張量x,經(jīng)過一系列操作之后得到輸出y。
如下所示為實現(xiàn)上面過程的代碼,首先通過tensor定義張量,通過屬性requires_grad
指定是否需要自動求導(dǎo)。之后執(zhí)行正向操作*w1、+b、*w2,最后求平均得到輸出y。
接著通過backward()
執(zhí)行反向傳播,就可以得到張量的grad值了。
# 定義張量 x = torch.ones(5, requires_grad=True) w1 = torch.tensor(2.0, requires_grad=True) w2 = torch.tensor(3.0, requires_grad=True) b = torch.tensor(4.0, requires_grad=False) # 執(zhí)行正向操作 l1 = x * w1 l2 = l1 + b l3 = l2 * w2 y = l3.mean() # 反向傳播 y.backward() print(l1.data, l1.grad, l1.grad_fn) # tensor([2., 2., 2., 2., 2.]) None <MulBackward0 object at 0x0000024D8E921BE0> print(l2.data, l2.grad, l2.grad_fn) # tensor([6., 6., 6., 6., 6.]) None <AddBackward0 object at 0x000001B960FC0F98> print(y) # tensor(18., grad_fn=<MeanBackward0>) print(w1.grad, w2.grad) # tensor(3.) tensor(6.) print(x.grad) # tensor([1.2000, 1.2000, 1.2000, 1.2000, 1.2000])
從上面的輸出可以看到x一開始為[1,1,1,1,1],乘以w1后為[2., 2., 2., 2., 2.],+b之后為[6., 6., 6., 6., 6.],再乘以3為[18., 18., 18., 18., 18.],求均值得到y(tǒng)為18。
Tensor的grad_fn
屬性用于記錄上一步是經(jīng)過怎樣的操作得到自己的,用于反向傳播時進行求導(dǎo)。例如l1.grad_fn為MulBackward0代表其反向傳播操作為乘法
反向傳播后通過grade
屬性可以獲得最后輸出對本張量的導(dǎo)數(shù)值。例如上面的操作中輸出為y,反向傳播后,w2.grad獲得的就是dy/dw2。由鏈?zhǔn)角髮?dǎo)法則可得dy/dw2=dy/dl3 × dl3/dw2。由于y由l3求均值得到,即y=1/5(Σl3),所以dy/dl3=1/5,又l3=l2 * w2,所以dl3/dw2=l2,所以dy/dw2=1/5*l2=1/5(6., 6., 6., 6., 6.)=6。同理通過反向鏈?zhǔn)角髮?dǎo)得到w1、x的grad
注意到l1、l2的grade值為None,這是由于張量l1、l2、l3都是中間計算結(jié)果,它們被稱為非葉張量,相對地由用戶創(chuàng)建的x、w1、w2被稱為葉張量(leaf tensor)。pytorch為了節(jié)約內(nèi)存并不會保存中間張量的導(dǎo)數(shù)值,只會用grad_fn來記錄是通過什么操作產(chǎn)生的。如果我們希望查看的話,可以通過retain_grad()
來保存非葉張量的導(dǎo)數(shù)值。
... l1.retain_grad() l2.retain_grad() y.backward() print(l1.data, l1.grad, l1.grad_fn) # tensor([2., 2., 2., 2., 2.]) tensor([0.6000, 0.6000, 0.6000, 0.6000, 0.6000]) <MulBackward0 object at 0x000001DF52100F60> print(l2.data, l2.grad, l2.grad_fn) # tensor([6., 6., 6., 6., 6.]) tensor([0.6000, 0.6000, 0.6000, 0.6000, 0.6000]) <AddBackward0 object at 0x000001DF52111048>
如果我們設(shè)置了張量requires_grad,但在某個訓(xùn)練過程中不需要記錄梯度grad,可以把代碼塊包裹在with torch.no_grad():
中,這樣里面的所有張量都不會保存grad
x = torch.ones(5, requires_grad=True) print((x ** 2).requires_grad) # 輸出True,記錄grad with torch.no_grad(): print((x ** 2).requires_grad) # 輸出False,不記錄
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
python自動查詢12306余票并發(fā)送郵箱提醒腳本
這篇文章主要為大家詳細介紹了Python自動查詢12306余票并發(fā)送郵箱提醒腳本,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05Python的控制結(jié)構(gòu)之For、While、If循環(huán)問題
這篇文章主要介紹了Python的控制結(jié)構(gòu)之For、While、If循環(huán)問題,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06anaconda創(chuàng)建、查看、激活與刪除虛擬環(huán)境指令總結(jié)
在跑項目時常常會安裝很多的包,也通常會遇到需要安裝指定版本的包,以及包與包不兼容的問題,下面這篇文章主要給大家介紹了關(guān)于anaconda創(chuàng)建、查看、激活與刪除虛擬環(huán)境指令的相關(guān)資料,需要的朋友可以參考下2022-11-11Python獲取系統(tǒng)默認(rèn)字符編碼的方法
這篇文章主要介紹了Python獲取系統(tǒng)默認(rèn)字符編碼的方法,涉及Python中sys模塊getdefaultencoding方法的使用技巧,需要的朋友可以參考下2015-06-06python使用MQTT給硬件傳輸圖片的實現(xiàn)方法
最近因需要用python寫一個微服務(wù)來用MQTT給硬件傳輸圖片,其中python用的是flask框架。這篇文章主要介紹了python使用MQTT給硬件傳輸圖片,需要的朋友可以參考下2019-05-05