Autograd
本篇文章是本人對Pytorch官方教程的原創(chuàng)翻譯(原文鏈接)僅供學(xué)習(xí)交流使用昭殉,轉(zhuǎn)載請注明出處频丘!
autograd
是Pytorch搭建神經(jīng)網(wǎng)絡(luò)最關(guān)鍵的包谣拣。它可以自動計(jì)算tensor操作產(chǎn)生的微分杠袱,也就是說辐怕,autograd是一個define-by-run的框架,可以自動對你的網(wǎng)絡(luò)進(jìn)行反向傳播澈蝙。
在聲明一個tesnor時吓坚,可以指定參數(shù).requires_grad=True
開啟自動求導(dǎo),這樣Pytorch就會跟蹤它的所有操作灯荧,在tensor運(yùn)算完成后礁击,可以調(diào)用.backward()
方法計(jì)算梯度,tesnor的梯度存放在它的.grad
屬性當(dāng)中逗载。
.detach()
方法可以取消對tensor的梯度追蹤哆窿,這樣Pytorch就會把tensor從追蹤記錄中移除,不再繼續(xù)追蹤厉斟。
為了節(jié)約內(nèi)存挚躯、提高效率,可以在代碼塊前注明with torch.no_grad()
捏膨,因?yàn)橛行┳兞侩m然requires_grad=True
但其實(shí)并不需要計(jì)算梯度。
在autograd包中食侮,還有一個非常重要的類就是Function
号涯,除了用戶基于數(shù)據(jù)直接創(chuàng)建的Tensor(像a=torch.Tensor([1, 2, 3,])
這樣),其他的Tensor必然是根據(jù)某些Tensor通過運(yùn)算得到的锯七,F(xiàn)unction類就記錄了這一運(yùn)算過程链快,并存儲在.grad_fn
屬性中。
當(dāng)需要計(jì)算梯度時眉尸,首先需要調(diào)用y.backward()
如果y是一個標(biāo)量的話域蜗,則無需傳參,否則噪猾,必須傳入一個與y規(guī)模相同的tensor霉祸。
這是因?yàn)椋赼utograd包中袱蜡,實(shí)際計(jì)算的是vector-Jacobian積丝蹭。也就是給定任意的向量 ,計(jì)算與Jacobian矩陣 的積。如果恰好是某個標(biāo)量函數(shù)的梯度坪蚁,即:
Jacobian矩陣:
其中:是關(guān)于的多元函數(shù),即:
那么根據(jù)鏈?zhǔn)椒▌t,v-J積的結(jié)果即是關(guān)于的梯度:
定理:奔穿,因此計(jì)算等價于
vector-Jacobian積的這種特性使得模型非常容易擴(kuò)展镜沽。
接下來看一些例子:
import torch
初始化一個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)建一個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ǎo)機(jī)制蔬墩,上文中使用到的out
是一個標(biāo)量,那么對out反向傳播則可以直接調(diào)用out.backward()
彩倚,等價于out.backward(torch.tensor(1))
out.backward()
print(x.grad)
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
檢驗(yàn):
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ù)
此時y
不再是標(biāo)量,自動求導(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():
可以臨時取消對代碼塊內(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ǎo)機(jī)制中隔離出來哥谷,得到的新tensor將不再需要求導(dǎo)岸夯。
print(x.requires_grad)
# y和x數(shù)據(jù)相同,不需要求導(dǎo)
# y不是x的拷貝们妥,對y的修改也會影響x
# 如果直接令y = x猜扮,那么是不會取消追蹤的
y = x.detach()
print(y.requires_grad)
print(x.eq(y).all()) # 對比全部數(shù)據(jù)
True
False
tensor(True)