【Pytorch教程】Pytorch tutorials 02-Autograd 中文翻譯

Autograd

本篇文章是本人對(duì)Pytorch官方教程的原創(chuàng)翻譯(原文鏈接)僅供學(xué)習(xí)交流使用,轉(zhuǎn)載請(qǐng)注明出處!

autograd是Pytorch搭建神經(jīng)網(wǎng)絡(luò)最關(guān)鍵的包。它可以自動(dòng)計(jì)算tensor操作產(chǎn)生的微分,也就是說,autograd是一個(gè)define-by-run的框架,可以自動(dòng)對(duì)你的網(wǎng)絡(luò)進(jìn)行反向傳播。

在聲明一個(gè)tesnor時(shí),可以指定參數(shù).requires_grad=True開啟自動(dòng)求導(dǎo),這樣Pytorch就會(huì)跟蹤它的所有操作,在tensor運(yùn)算完成后,可以調(diào)用.backward()方法計(jì)算梯度,tesnor的梯度存放在它的.grad屬性當(dāng)中。

.detach()方法可以取消對(duì)tensor的梯度追蹤,這樣Pytorch就會(huì)把tensor從追蹤記錄中移除,不再繼續(xù)追蹤。

為了節(jié)約內(nèi)存、提高效率,可以在代碼塊前注明with torch.no_grad(),因?yàn)橛行┳兞侩m然requires_grad=True但其實(shí)并不需要計(jì)算梯度。

在autograd包中,還有一個(gè)非常重要的類就是Function,除了用戶基于數(shù)據(jù)直接創(chuàng)建的Tensor(像a=torch.Tensor([1, 2, 3,])這樣),其他的Tensor必然是根據(jù)某些Tensor通過運(yùn)算得到的,F(xiàn)unction類就記錄了這一運(yùn)算過程,并存儲(chǔ)在.grad_fn屬性中。

當(dāng)需要計(jì)算梯度時(shí),首先需要調(diào)用y.backward()如果y是一個(gè)標(biāo)量的話,則無需傳參,否則,必須傳入一個(gè)與y規(guī)模相同的tensor。

這是因?yàn)椋赼utograd包中,實(shí)際計(jì)算的是vector-Jacobian積。也就是給定任意的向量v=(v_1 v_2 \cdots v_m)^T ,計(jì)算v與Jacobian矩陣 J的積{v^T}·J。如果v恰好是某個(gè)標(biāo)量函數(shù)l=g(\overrightarrow{y})的梯度,即:

v=\left( \frac{\partial{l}}{\partial{y_1}} \frac{\partial{l}}{\partial{y_2}} \cdots \frac{\partial{l}}{\partial{y_m}}\right)^T

Jacobian矩陣:

J= \begin{pmatrix} \frac{\partial y_1}{\partial x_1} & \frac{\partial y_1}{\partial x_2} & \cdots & \frac{\partial y_1}{\partial x_n} \\ \frac{\partial y_2}{\partial x_1} & \frac{\partial y_2}{\partial x_2} & \cdots & \frac{\partial y_2}{\partial x_n} \\ \vdots & \vdots & \ddots & \vdots \\ \frac{\partial y_m}{\partial x_1} & \frac{\partial y_m}{\partial x_2} & \cdots & \frac{\partial y_m}{\partial x_n} \\ \end{pmatrix}

其中:{y_i}是關(guān)于{x_1,x_2,\cdots,x_n}的多元函數(shù),即:{y_i}={y_i}\left( x_1,x_2,\cdots,x_n \right)

那么根據(jù)鏈?zhǔn)椒▌t,v-J積的結(jié)果即是l關(guān)于\overrightarrow x的梯度:

{J^T}·v={\begin{pmatrix}\frac{\partial y_1}{\partial x_1} & \frac{\partial y_1}{\partial x_2} & \cdots & \frac{\partial y_1}{\partial x_n} \\ \frac{\partial y_2}{\partial x_1} & \frac{\partial y_2}{\partial x_2} & \cdots & \frac{\partial y_2}{\partial x_n} \\ \vdots & \vdots & \ddots & \vdots \\ \frac{\partial y_m}{\partial x_1} & \frac{\partial y_m}{\partial x_2} & \cdots & \frac{\partial y_m}{\partial x_n} \\ \end{pmatrix}}·{\begin{pmatrix} \frac{\partial{l}}{\partial{y_1}} \\ \frac{\partial{l}}{\partial{y_2}} \\ \vdots \\ \frac{\partial{l}}{\partial{y_m}} \end{pmatrix}} = {\begin{pmatrix} \frac{\partial{l}}{\partial{x_1}} \\ \frac{\partial{l}}{\partial{x_2}} \\ \vdots \\ \frac{\partial{l}}{\partial{x_n}} \end{pmatrix}}

定理:({AB})^T={B^T}{A^T},因此計(jì)算{v^T}·J等價(jià)于{J^T}·v

vector-Jacobian積的這種特性使得模型非常容易擴(kuò)展。


接下來看一些例子:

import torch

初始化一個(gè)tensor,并將它的requires_grad屬性設(shè)為True,追蹤它的運(yùn)算。

x = torch.ones(2, 2, requires_grad=True)
print(x)
tensor([[1., 1.],
        [1., 1.]], requires_grad=True)

創(chuàng)建一個(gè)tensory = x + 2

y = x + 2
print(y)
tensor([[3., 3.],
        [3., 3.]], grad_fn=<AddBackward0>)

y是由x計(jì)算得到的,所以它的.grad_fn不為空

print(y.grad_fn)
<AddBackward0 object at 0x0000028CCEE770C8>

做一些其他運(yù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_(...)方法可以改變tensor的requires_grad屬性。默認(rèn)情況下,requires_grad = 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 0x0000028CCEE83CC8>

接下來嘗試使用自動(dòng)求導(dǎo)機(jī)制,上文中使用到的out是一個(gè)標(biāo)量,那么對(duì)out反向傳播則可以直接調(diào)用out.backward(),等價(jià)于out.backward(torch.tensor(1))

out.backward()
print(x.grad)
tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])

檢驗(yàn):

\begin{equation}\begin{split} out&=\frac{1}{4}\sum_i{z_i}\\ z_i&=3({x_i}+2)^2\\ {z_i}\big|_{{x_i}=1}&=27\\ \frac{\partial out}{\partial x_i}&=\frac{3}{2}({x_i}+2)=4.5\\ \end{split}\end{equation}

x = torch.rand(3, requires_grad=True)

y = x * 2
while y.data.norm() < 1000:  #norm是L-p范數(shù),默認(rèn)求2范數(shù)
    y = y * 2
    
print(y)
tensor([939.6540, 998.4269,   6.6829], grad_fn=<MulBackward0>)

L-P范數(shù) L_p=\left\|x\right\|_p=\sqrt[p]{\sum_{i=1}^n{x_{i}^{p}}}

此時(shí)y不再是標(biāo)量,自動(dòng)求導(dǎo)機(jī)制不能直接計(jì)算Jacobian行列式,反向傳播得到的.grad是vector-Jacobian積。

v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)

print(x.grad)
tensor([1.0240e+02, 1.0240e+03, 1.0240e-01])

with torch.no_grad():可以臨時(shí)取消對(duì)代碼塊內(nèi)的tensor的追蹤。

print(x.requires_grad)
print((x ** 2).requires_grad)

with torch.no_grad():
    print((x ** 2).requires_grad)
    
print((x ** 2).requires_grad)
True
True
False
True

.detach()方法可以將tensor從自動(dòng)求導(dǎo)機(jī)制中隔離出來,得到的新tensor將不再需要求導(dǎo)。

print(x.requires_grad)
# y和x數(shù)據(jù)相同,不需要求導(dǎo)
# y不是x的拷貝,對(duì)y的修改也會(huì)影響x
# 如果直接令y = x,那么是不會(huì)取消追蹤的
y = x.detach() 
print(y.requires_grad)
print(x.eq(y).all())  # 對(duì)比全部數(shù)據(jù)
True
False
tensor(True)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。