您好,登錄后才能下訂單哦!
小編給大家分享一下PyTorch怎么如何自動(dòng)計(jì)算梯度,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
在PyTorch中,torch.Tensor類是存儲(chǔ)和變換數(shù)據(jù)的重要工具,相比于Numpy,Tensor提供GPU計(jì)算和自動(dòng)求梯度等更多功能,在深度學(xué)習(xí)中,我們經(jīng)常需要對(duì)函數(shù)求梯度(gradient)。
PyTorch提供的autograd包能夠根據(jù)輸入和前向傳播過程自動(dòng)構(gòu)建計(jì)算圖,并執(zhí)行反向傳播。
本篇將介紹和總結(jié)如何使用autograd包來進(jìn)行自動(dòng)求梯度的有關(guān)操作。
Tensor是這個(gè)pytorch的自動(dòng)求導(dǎo)部分的核心類,如果將其屬性.requires_grad=True,它將開始追蹤(track) 在該tensor上的所有操作,從而實(shí)現(xiàn)利用鏈?zhǔn)椒▌t進(jìn)行的梯度傳播。完成計(jì)算后,可以調(diào)用.backward()來完成所有梯度計(jì)算。此Tensor的梯度將累積到.grad屬性中。
如果不想要被繼續(xù)對(duì)tensor進(jìn)行追蹤,可以調(diào)用.detach()將其從追蹤記錄中分離出來,接下來的梯度就傳不過去了。此外,還可以用with torch.no_grad()將不想被追蹤的操作代碼塊包裹起來,這種方法在評(píng)估模型的時(shí)候很常用,因?yàn)榇藭r(shí)并不需要繼續(xù)對(duì)梯度進(jìn)行計(jì)算。
Function是另外一個(gè)很重要的類。Tensor和Function互相結(jié)合就可以構(gòu)建一個(gè)記錄有整個(gè)計(jì)算過程的有向無環(huán)圖(DAG)。每個(gè)Tensor都有一個(gè).grad_fn屬性,該屬性即創(chuàng)建該Tensor的Function, 就是說該Tensor是不是通過某些運(yùn)算得到的,若是,則grad_fn返回一個(gè)與這些運(yùn)算相關(guān)的對(duì)象,否則是None。
首先我們創(chuàng)建一個(gè)tensor,同時(shí)設(shè)置requires_grad=True:
x = torch.ones(2, 2, requires_grad=True) print(x) print(x.grad_fn) '''
輸出:
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
None
'''
像x這種直接創(chuàng)建的tensor 稱為葉子節(jié)點(diǎn),葉子節(jié)點(diǎn)對(duì)應(yīng)的grad_fn是None。如果進(jìn)行一次運(yùn)算操作:
y = x + 1 print(y) print(y.grad_fn) ''' tensor([[2., 2.], [2., 2.]], grad_fn=<AddBackward>) <AddBackward object at 0x1100477b8> '''
而y是通過一個(gè)加法操作創(chuàng)建的,所以它有一個(gè)為操作的grad_fn。
嘗試進(jìn)行更復(fù)雜的操作:
z = y ** 2 out = z.mean() print(z, out) ''' tensor([[4., 4.], [4., 4.]], grad_fn=<PowBackward0>) tensor(4., grad_fn=<MeanBackward0>) '''
上面的out是一個(gè)標(biāo)量4,通常對(duì)于標(biāo)量直接使用out.backward()進(jìn)行求導(dǎo),不需要指定求導(dǎo)變量,后面進(jìn)行詳細(xì)說明。
也可以通過.requires_grad_()改變r(jià)equires_grad屬性:
a = torch.randn(3, 2) # 缺失情況下默認(rèn) requires_grad = False a = (a ** 2) print(a.requires_grad) # False a.requires_grad_(True) #使用in-place操作,改變屬性 print(a.requires_grad) # True b = (a * a).sum() print(b.grad_fn) ''' False True <SumBackward0 object at 0x7fd8c16edd30> '''
torch.autograd實(shí)現(xiàn)梯度求導(dǎo)的鏈?zhǔn)椒▌t,用來計(jì)算一些雅克比矩陣的乘積,即函數(shù)的一階導(dǎo)數(shù)的乘積。
注意:grad在反向傳播過程中是累加的(accumulated),每一次運(yùn)行反向傳播,梯度都會(huì)累加之前的梯度,所以一般在反向傳播之前需把梯度清零x.grad.data.zero_()。
x = torch.ones(2, 2, requires_grad=True) y = x + 1 z = y ** 2 out = z.mean() print(z, out) out.backward() print(x.grad) # 注意grad是累加的 out2 = x.sum() out2.backward() print(out2) print(x.grad) out3 = x.sum() x.grad.data.zero_() out3.backward() print(out3) print(x.grad) ''' tensor([[4., 4.], [4., 4.]], grad_fn=<PowBackward0>) tensor(4., grad_fn=<MeanBackward0>) tensor([[1., 1.], [1., 1.]]) tensor(4., grad_fn=<SumBackward0>) tensor([[2., 2.], [2., 2.]]) tensor(4., grad_fn=<SumBackward0>) tensor([[1., 1.], [1., 1.]]) '''
Tensor的自動(dòng)求導(dǎo)對(duì)于標(biāo)量比如上面的out.backward()十分方便,但是當(dāng)反向傳播的對(duì)象不是標(biāo)量時(shí),需要在y.backward()種加入一個(gè)與out同形的Tensor,不允許張量對(duì)張量求導(dǎo),只允許標(biāo)量對(duì)張量求導(dǎo),求導(dǎo)結(jié)果是和自變量同形的張量。
這是為了避免向量(甚至更高維張量)對(duì)張量求導(dǎo),而轉(zhuǎn)換成標(biāo)量對(duì)張量求導(dǎo)。
x = torch.tensor([1.0, 2.0, 3.0, 4.0], requires_grad=True) y = 2 * x z = y.view(2, 2) print(z) ''' tensor([[2., 4.], [6., 8.]], grad_fn=<ViewBackward>) '''
顯然上面的tensor z不是一個(gè)標(biāo)量,所以在調(diào)用 z.backward()時(shí)需要傳入一個(gè)和z同形的權(quán)重向量進(jìn)行加權(quán)求和得到一個(gè)標(biāo)量。
c = torch.tensor([[1.0, 0.1], [0.01, 0.001]], dtype=torch.float) z.backward(c) print(x.grad) ''' tensor([[2., 4.], [6., 8.]], grad_fn=<ViewBackward>) tensor([2.0000, 0.2000, 0.0200, 0.0020]) '''
我們可以使用detach()或者torch.no_grad()語句停止梯度追蹤:
x = torch.tensor(1.0, requires_grad=True) y1 = x ** 2 with torch.no_grad(): y2 = x ** 3 y3 = y1 + y2 print(x.requires_grad) print(y1, y1.requires_grad) # True print(y2, y2.requires_grad) # False print(y3, y3.requires_grad) # True ''' True tensor(1., grad_fn=<PowBackward0>) True tensor(1.) False tensor(2., grad_fn=<ThAddBackward>) True '''
我們嘗試計(jì)算梯度:
y3.backward() print(x.grad) # y2.backward() #這句會(huì)報(bào)錯(cuò),因?yàn)榇藭r(shí) y2.requires_grad=False,,無法調(diào)用反向傳播 ''' tensor(2.) '''
這里結(jié)果為2,是因?yàn)槲覀儧]有獲得y2的梯度,僅僅是對(duì)y1做了一次反向傳播,作為最后的梯度輸出。
如果我們想要修改tensor的數(shù)值,但是不希望保存在autograd的記錄中,require s_grad = False, 即不影響到正在進(jìn)行的反向傳播,那么可以用tensor.data進(jìn)行操作。但是這種操作需要注意可能會(huì)產(chǎn)生一些問題,比如標(biāo)量為0
x = torch.ones(1,requires_grad=True) print(x.data) # 仍然是一個(gè)tensor print(x.data.requires_grad) # 但是已經(jīng)是獨(dú)立于計(jì)算圖之外 y = 2 * x x.data *= 100 # 只改變了值,不會(huì)記錄在計(jì)算圖,所以不會(huì)影響梯度傳播 y.backward() print(x) # 更改data的值也會(huì)影響tensor的值 print(x.grad)
pytorch0.4以后保留了.data() 但是官方文檔建議使用.detach(),因?yàn)槭褂脁.detach時(shí),任何in-place變化都會(huì)使backward報(bào)錯(cuò),因此.detach()是從梯度計(jì)算中排除子圖的更安全方法。
如下面的例子:
torch.tensor([1,2,3.], requires_grad = True) out = a.sigmoid() c = out.detach() c.zero_() # in-place為0 ,tensor([ 0., 0., 0.]) print(out) # modified by c.zero_() !! tensor([ 0., 0., 0.]) out.sum().backward() # Requires the original value of out, but that was overwritten by c.zero_() ''' RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation ''' a = torch.tensor([1,2,3.], requires_grad = True) out = a.sigmoid() c = out.data c.zero_() # tensor([ 0., 0., 0.]) print(out) # out was modified by c.zero_() tensor([ 0., 0., 0.]) out.sum().backward() a.grad # 這么做不會(huì)報(bào)錯(cuò),但是a已經(jīng)被改變,最后計(jì)算的梯度實(shí)際是錯(cuò)誤的 ''' tensor([ 0., 0., 0.]) '''
補(bǔ)充:pytorch如何計(jì)算導(dǎo)數(shù)_Pytorch 自動(dòng)求梯度(autograd)
深度學(xué)習(xí)其實(shí)就是一個(gè)最優(yōu)化問題,找到最小的loss值,因?yàn)樽宰兞窟^多,想要找到最小值非常困難。所以就出現(xiàn)了很多最優(yōu)化方法,梯度下降就是一個(gè)非常典型的例子。本文針對(duì)python的pytorch庫中的自動(dòng)求梯度進(jìn)行了詳細(xì)的解釋
pytorch里面的tensor可以用來存儲(chǔ)向量或者標(biāo)量。
torch.tensor(1) # 標(biāo)量 torch.tensor([1]) # 1*1 的向量
tensor還可以指定數(shù)據(jù)類型,以及數(shù)據(jù)存儲(chǔ)的位置(可以存在顯存里,硬件加速)
torch.tensor([1,2], dtype=torch.float64)
在數(shù)學(xué)里,梯度的定義如下:
可以看出,自變量相對(duì)于因變量的每一個(gè)偏導(dǎo)乘以相應(yīng)的單位向量,最后相加,即為最后的梯度向量。
在pytorch里面,我們無法直接定義函數(shù),也無法直接求得梯度向量的表達(dá)式。更多的時(shí)候,我們其實(shí)只是求得了函數(shù)的在某一個(gè)點(diǎn)處相對(duì)于自變量的偏導(dǎo)。
我們先假設(shè)一個(gè)一元函數(shù):y = x^2 + 3x +1,在pytorch里面,我們假設(shè)x = 2, 那么
>>> x = torch.tensor(2, dtype=torch.float64, requires_grad=True) >>> y = x * x + 3 * x + 1 >>> y.backward() >>> x.grad tensor(7., dtype=torch.float64)
可以看出,最后y相對(duì)于x的導(dǎo)數(shù)在x=2的地方為7。在數(shù)學(xué)里進(jìn)行驗(yàn)證,那么就是
y' = 2*x + 3, 當(dāng)x=2時(shí),y' = 2 * 2 + 3 = 7, 完全符合torch自動(dòng)求得的梯度值。
接下來計(jì)算二元函數(shù)時(shí)的情況:
>>> x1 = torch.tensor(1.0) >>> x2 = torch.tensor(2.0, requires_grad=True) >>> y = 3*x1*x1 + 9 * x2 >>> y.backward() tensor(6.) >>> x2.grad tensor(9.)
可以看出,我們可以求得y相對(duì)于x2的偏導(dǎo)數(shù)。
以上討論的都是標(biāo)量的情況,接下來討論自變量為向量的情況。
mat1 = torch.tensor([[1,2,3]], dtype=torch.float64, requires_grad=True) >>> mat2 tensor([[1.], [2.], [3.]], dtype=torch.float64, requires_grad=True)
mat1是一個(gè)1x3的矩陣,mat2是一個(gè)3x1的矩陣,他們倆的叉乘為一個(gè)1x1的矩陣。在pytorch里面,可以直接對(duì)其進(jìn)行backward,從而求得相對(duì)于mat1或者是mat2的梯度值。
>>> y = torch.mm(mat1, mat2) >>> y.backward() >>> mat1.grad tensor([[1., 2., 3.]], dtype=torch.float64) >>> mat2.grad tensor([[1.], [2.], [3.]], dtype=torch.float64)
其實(shí)可以把mat1中的每一個(gè)元素當(dāng)成一個(gè)自變量,那么相對(duì)于mat1的梯度向量,就是分別對(duì)3個(gè)x進(jìn)行求偏導(dǎo)。
相當(dāng)于是y = mat1[0] * mat2[0] + mat1[1] * mat2[1] + mat1[2] * mat2[2]
然后分別求y對(duì)于mat1,mat2每個(gè)元素的偏導(dǎo)。
另外,如果我們最后輸出的是一個(gè)N x M 的一個(gè)向量,我們要計(jì)算這個(gè)向量相對(duì)于自變量向量的偏導(dǎo),那么我們就需要在backward函數(shù)的參數(shù)里傳入?yún)?shù)。
如上圖所述,其實(shí)pytorch的autograd核心就是計(jì)算一個(gè) vector-jacobian 乘積, jacobian就是因變量向量相對(duì)于自變量向量的偏導(dǎo)組成的矩陣,vector相當(dāng)于是因變量向量到一個(gè)標(biāo)量的函數(shù)的偏導(dǎo)。最后就是標(biāo)量相對(duì)于一個(gè)向量的梯度向量。
1.PyTorch是相當(dāng)簡(jiǎn)潔且高效快速的框架;2.設(shè)計(jì)追求最少的封裝;3.設(shè)計(jì)符合人類思維,它讓用戶盡可能地專注于實(shí)現(xiàn)自己的想法;4.與google的Tensorflow類似,F(xiàn)AIR的支持足以確保PyTorch獲得持續(xù)的開發(fā)更新;5.PyTorch作者親自維護(hù)的論壇 供用戶交流和求教問題6.入門簡(jiǎn)單
以上是“PyTorch怎么如何自動(dòng)計(jì)算梯度”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。