Autograd:自动求导
2019年年初,ApacheCN組織志愿者翻譯了PyTorch1.0版本中文文檔(github地址),同時(shí)也獲得了PyTorch官方授權(quán),我相信已經(jīng)有許多人在中文文檔官網(wǎng)上看到了。不過目前校對(duì)還缺人手,希望大家踴躍參與。之前一段時(shí)間我們和PyTorch的有關(guān)負(fù)責(zé)人Bruce Lin一直在進(jìn)行郵件交流。在之后適當(dāng)?shù)臅r(shí)候,我們會(huì)組織志愿者進(jìn)行其他有關(guān)PyTorch的項(xiàng)目,歡迎大家加入我們,關(guān)注我們。更希望我們的一系列工作能夠?qū)Υ蠹矣兴鶐椭?/p>
譯者:bat67
校對(duì)者:FontTian
PyTorch中,所有神經(jīng)網(wǎng)絡(luò)的核心是 autograd 包。先簡(jiǎn)單介紹一下這個(gè)包,然后訓(xùn)練我們的第一個(gè)的神經(jīng)網(wǎng)絡(luò)。
autograd 包為張量上的所有操作提供了自動(dòng)求導(dǎo)機(jī)制。它是一個(gè)在運(yùn)行時(shí)定義(define-by-run)的框架,這意味著反向傳播是根據(jù)代碼如何運(yùn)行來決定的,并且每次迭代可以是不同的.
讓我們用一些簡(jiǎn)單的例子來看看吧。
張量
torch.Tensor 是這個(gè)包的核心類。如果設(shè)置它的屬性 .requires_grad 為 True,那么它將會(huì)追蹤對(duì)于該張量的所有操作。當(dāng)完成計(jì)算后可以通過調(diào)用 .backward(),來自動(dòng)計(jì)算所有的梯度。這個(gè)張量的所有梯度將會(huì)自動(dòng)累加到.grad屬性.
要阻止一個(gè)張量被跟蹤歷史,可以調(diào)用 .detach() 方法將其與計(jì)算歷史分離,并阻止它未來的計(jì)算記錄被跟蹤。
為了防止跟蹤歷史記錄(和使用內(nèi)存),可以將代碼塊包裝在 with torch.no_grad(): 中。在評(píng)估模型時(shí)特別有用,因?yàn)槟P涂赡芫哂?requires_grad = True 的可訓(xùn)練的參數(shù),但是我們不需要在此過程中對(duì)他們進(jìn)行梯度計(jì)算。
還有一個(gè)類對(duì)于autograd的實(shí)現(xiàn)非常重要:Function。
Tensor 和 Function 互相連接生成了一個(gè)無圈圖(acyclic graph),它編碼了完整的計(jì)算歷史。每個(gè)張量都有一個(gè) .grad_fn 屬性,該屬性引用了創(chuàng)建 Tensor 自身的Function(除非這個(gè)張量是用戶手動(dòng)創(chuàng)建的,即這個(gè)張量的 grad_fn 是 None )。
如果需要計(jì)算導(dǎo)數(shù),可以在 Tensor 上調(diào)用 .backward()。如果 Tensor 是一個(gè)標(biāo)量(即它包含一個(gè)元素的數(shù)據(jù)),則不需要為 backward() 指定任何參數(shù),但是如果它有更多的元素,則需要指定一個(gè) gradient 參數(shù),該參數(shù)是形狀匹配的張量。
import torch創(chuàng)建一個(gè)張量并設(shè)置requires_grad=True用來追蹤其計(jì)算歷史
x = torch.ones(2, 2, requires_grad=True) print(x)輸出:
tensor([[1., 1.],[1., 1.]], requires_grad=True)對(duì)這個(gè)張量做一次運(yùn)算:
y = x + 2 print(y)輸出:
tensor([[3., 3.],[3., 3.]], grad_fn=<AddBackward0>)y是計(jì)算的結(jié)果,所以它有g(shù)rad_fn屬性。
print(y.grad_fn)輸出:
<AddBackward0 object at 0x7f1b248453c8>對(duì)y進(jìn)行更多操作
z = y * y * 3 out = z.mean()print(z, out)輸出:
tensor([[27., 27.],[27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward0>).requires_grad_(...) 原地改變了現(xiàn)有張量的 requires_grad 標(biāo)志。如果沒有指定的話,默認(rèn)輸入的這個(gè)標(biāo)志是 False。
a = torch.randn(2, 2) a = ((a * 3) / (a - 1)) print(a.requires_grad) a.requires_grad_(True) print(a.requires_grad) b = (a * a).sum() print(b.grad_fn)輸出:
False True <SumBackward0 object at 0x7f1b24845f98>梯度
現(xiàn)在開始進(jìn)行反向傳播,因?yàn)?out 是一個(gè)標(biāo)量,因此 out.backward() 和 out.backward(torch.tensor(1.)) 等價(jià)。
out.backward()輸出導(dǎo)數(shù) d(out)/dx
print(x.grad)輸出:
tensor([[4.5000, 4.5000],[4.5000, 4.5000]])我們的得到的是一個(gè)數(shù)取值全部為4.5的矩陣。
讓我們來調(diào)用 out 張量 “o”“o”“o”。
就可以得到 o=14∑izio = \frac{1}{4}\sum_i z_io=41?i∑?zi?,zi=3(xi+2)2z_i = 3(x_i+2)^2zi?=3(xi?+2)2 和 zi∣xi=1=27z_i\bigr\rvert_{x_i=1} = 27zi?∣∣?xi?=1?=27 因此, ?o?xi=32(xi+2)\frac{\partial o}{\partial x_i} = \frac{3}{2}(x_i+2)?xi??o?=23?(xi?+2),因而 ?o?xi∣xi=1=92=4.5\frac{\partial o}{\partial x_i}\bigr\rvert_{x_i=1} = \frac{9}{2} = 4.5?xi??o?∣∣?xi?=1?=29?=4.5。
數(shù)學(xué)上,若有向量值函數(shù) y?=f(x?)\vec{y}=f(\vec{x})y?=f(x),那么 y?\vec{y}y? 相對(duì)于 x?\vec{x}x 的梯度是一個(gè)雅可比矩陣:
J=(?y1?x1??ym?x1????y1?xn??ym?xn)J=\left(\begin{array}{ccc} \frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{1}}\\ \vdots & \ddots & \vdots\\ \frac{\partial y_{1}}{\partial x_{n}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}} \end{array}\right) J=?????x1??y1????xn??y1????????x1??ym????xn??ym???????
通常來說,torch.autograd 是計(jì)算雅可比向量積的一個(gè)“引擎”。也就是說,給定任意向量 v=(v1v2?vm)Tv=\left(\begin{array}{cccc} v_{1} & v_{2} & \cdots & v_{m}\end{array}\right)^{T}v=(v1??v2????vm??)T,計(jì)算乘積 vT?Jv^{T}\cdot JvT?J。如果 vvv 恰好是一個(gè)標(biāo)量函數(shù) l=g(y?)l=g\left(\vec{y}\right)l=g(y?) 的導(dǎo)數(shù),即 v=(?l?y1??l?ym)Tv=\left(\begin{array}{ccc}\frac{\partial l}{\partial y_{1}} & \cdots & \frac{\partial l}{\partial y_{m}}\end{array}\right)^{T}v=(?y1??l?????ym??l??)T,那么根據(jù)鏈?zhǔn)椒▌t,雅可比向量積應(yīng)該是 lll 對(duì) x?\vec{x}x 的導(dǎo)數(shù):
JT?v=(?y1?x1??ym?x1????y1?xn??ym?xn)(?l?y1??l?ym)=(?l?x1??l?xn)J^{T}\cdot v=\left(\begin{array}{ccc} \frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{1}}\\ \vdots & \ddots & \vdots\\ \frac{\partial y_{1}}{\partial x_{n}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}} \end{array}\right)\left(\begin{array}{c} \frac{\partial l}{\partial y_{1}}\\ \vdots\\ \frac{\partial l}{\partial y_{m}} \end{array}\right)=\left(\begin{array}{c} \frac{\partial l}{\partial x_{1}}\\ \vdots\\ \frac{\partial l}{\partial x_{n}} \end{array}\right) JT?v=?????x1??y1????xn??y1????????x1??ym????xn??ym????????????y1??l???ym??l??????=?????x1??l???xn??l??????
(注意:行向量的vT?Jv^{T}\cdot JvT?J也可以被視作列向量的JT?vJ^{T}\cdot vJT?v)
雅可比向量積的這一特性使得將外部梯度輸入到具有非標(biāo)量輸出的模型中變得非常方便。
現(xiàn)在我們來看一個(gè)雅可比向量積的例子:
x = torch.randn(3, requires_grad=True)y = x * 2 while y.data.norm() < 1000:y = y * 2print(y)輸出:
tensor([-278.6740, 935.4016, 439.6572], grad_fn=<MulBackward0>)在這種情況下,y 不再是標(biāo)量。torch.autograd 不能直接計(jì)算完整的雅可比矩陣,但是如果我們只想要雅可比向量積,只需將這個(gè)向量作為參數(shù)傳給 backward:
v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float) y.backward(v)print(x.grad)輸出:
tensor([4.0960e+02, 4.0960e+03, 4.0960e-01])也可以通過將代碼塊包裝在 with torch.no_grad(): 中,來阻止autograd跟蹤設(shè)置了 .requires_grad=True 的張量的歷史記錄。
print(x.requires_grad) print((x ** 2).requires_grad)with torch.no_grad():print((x ** 2).requires_grad)輸出:
True True False后續(xù)閱讀:
autograd 和 Function 的文檔見:https://pytorch.org/docs/autograd
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的Autograd:自动求导的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么是PyTorch?
- 下一篇: 使用PyTorch创建神经网络