PyTorch學(xué)習(xí)---2.自動求梯度

自動求梯度

首先給大家介紹幾個基本概念:

方向?qū)?shù):是一個數(shù)宵距;反映的是f(x,y)在P0點沿方向v的變化率栽惶。
偏導(dǎo)數(shù):是多個數(shù)(每元有一個)谅河;是指多元函數(shù)沿坐標(biāo)軸方向的方向?qū)?shù),因此二元函數(shù)就有兩個偏導(dǎo)數(shù)蕉堰。
偏導(dǎo)函數(shù):是一個函數(shù)若锁;是一個關(guān)于點的偏導(dǎo)數(shù)的函數(shù)搁骑。
梯度:是一個向量;每個元素為函數(shù)對一元變量的偏導(dǎo)數(shù)又固;它既有大兄倨鳌(其大小為最大方向?qū)?shù)),也有方向仰冠。
摘自《方向?qū)?shù)與梯度》

梯度從本質(zhì)上來說也是導(dǎo)數(shù)的一種乏冀,實在不好理解可以把它當(dāng)成導(dǎo)數(shù),它的本質(zhì)也是反應(yīng)一個函數(shù)的變化洋只,我們經(jīng)常需要對函數(shù)求梯度(gradient)辆沦。PyTorch提供的autograd包能夠根據(jù)輸入和前向傳播過程自動構(gòu)建計算圖,并執(zhí)行反向傳播识虚。

概念

Tensor是PyTorch的核心類肢扯,如果將其屬性.requires_grad設(shè)置為True,它將開始追蹤(track)在其上的所有操作(這樣就可以利用鏈?zhǔn)椒▌t進(jìn)行梯度傳播了)担锤。完成計算后蔚晨,可以調(diào)用.backward()來完成所有梯度計算。此Tensor的梯度將累積到.grad屬性中肛循。

注意在y.backward()時铭腕,如果y是標(biāo)量,則不需要為backward()傳入任何參數(shù)多糠;否則累舷,需要傳入一個與y同形的Tensor。

如果不想要被繼續(xù)追蹤夹孔,可以調(diào)用.detach()將其從追蹤記錄中分離出來被盈,這樣就可以防止將來的計算被追蹤,這樣梯度就傳不過去了搭伤。此外害捕,還可以用with torch.no_grad()將不想被追蹤的操作代碼塊包裹起來,這種方法在評估模型的時候很常用闷畸,因為在評估模型時,我們并不需要計算可訓(xùn)練參數(shù)(requires_grad=True)的梯度吞滞。

Function是另外一個很重要的類佑菩。Tensor和Function互相結(jié)合就可以構(gòu)建一個記錄有整個計算過程的有向無環(huán)圖(DAG)盾沫。每個Tensor都有一個.grad_fn屬性,該屬性即創(chuàng)建該Tensor的Function, 就是說該Tensor是不是通過某些運(yùn)算得到的殿漠,若是赴精,則grad_fn返回一個與這些運(yùn)算相關(guān)的對象,否則是None绞幌。

測試

創(chuàng)建一個Tensor并設(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

運(yùn)算操作:

y = x + 2
print(y)
print(y.grad_fn)
z = y * y * 3
out = z.mean()
print(z, out)

輸出如下:

tensor([[3., 3.],
[3., 3.]], grad_fn=<AddBackward>)
<AddBackward object at 0x1100477b8>
tensor([[27., 27.],
[27., 27.]], grad_fn=<MulBackward>) tensor(27., grad_fn=<MeanBackward1>)

注意x是直接創(chuàng)建的蕾哟,所以它沒有g(shù)rad_fn, 而y是通過一個加法操作創(chuàng)建的,所以它有一個為<AddBackward>的grad_fn莲蜘。
像x這種直接創(chuàng)建的稱為葉子節(jié)點谭确,葉子節(jié)點對應(yīng)的grad_fn是None。

print(x.is_leaf, y.is_leaf) # True False

通過.requires_grad_()來用in-place的方式改變requires_grad屬性:

a = torch.randn(2, 2) # 缺失情況下默認(rèn) requires_grad = False
a = ((a * 3) / (a - 1))
print(a.requires_grad) # False
a.requires_grad_(True)
print(a.requires_grad) # True
b = (a * a).sum()
print(b.grad_fn)

輸出如下:

False
True
<SumBackward0 object at 0x118f50cc0>

上面是關(guān)于梯度的一些性質(zhì)票渠,下面介紹一些它的計算之類的逐哈。
因為out是一個標(biāo)量,所以調(diào)用backward()時不需要指定求導(dǎo)變量:

out.backward() # 等價于 out.backward(torch.tensor(1.))
print(x.grad)

輸出為:

tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])

這點想必學(xué)過高等數(shù)學(xué)的人都可以輕松的驗證问顷,如果不行昂秃,可以問我。另外有一點需要特別注意杜窄,grad在反向傳播過程中是累加的(accumulated)肠骆,這意味著每一次運(yùn)行反向傳播,梯度都會累加之前的梯度塞耕,所以一般在反向傳播之前需把梯度清零蚀腿。

# 再來反向傳播一次,注意grad是累加的
out2 = x.sum()
out2.backward()
print(x.grad)

out3 = x.sum()
x.grad.data.zero_()
out3.backward()
print(x.grad)

輸出為:

tensor([[5.5000, 5.5000],
[5.5000, 5.5000]])
tensor([[1., 1.],
[1., 1.]])

那么上面為什么說在y.backward()時荷科,如果y是標(biāo)量唯咬,則不需要為backward()傳入任何參數(shù);否則畏浆,需要傳入一個與y同形的Tensor? 簡單來說就是為了避免向量(甚至更高維張量)對張量求導(dǎo)胆胰,而轉(zhuǎn)換成標(biāo)量對張量求導(dǎo)。舉個例子刻获,假設(shè)形狀為 m x n 的矩陣 X 經(jīng)過運(yùn)算得到了 p x q 的矩陣 Y蜀涨,Y 又經(jīng)過運(yùn)算得到了 s x t 的矩陣 Z。那么按照前面講的規(guī)則蝎毡,dZ/dY 應(yīng)該是一個 s x t x p x q 四維張量厚柳,dY/dX 是一個 p x q x m x n的四維張量。問題來了沐兵,怎樣反向傳播别垮?怎樣將兩個四維張量相乘?扎谎?碳想?這要怎么乘烧董??胧奔?就算能解決兩個四維張量怎么乘的問題赁温,四維和三維的張量又怎么乘宣增?導(dǎo)數(shù)的導(dǎo)數(shù)又怎么求饥努,這一連串的問題尸曼,感覺要瘋掉…… 為了避免這個問題,我們不允許張量對張量求導(dǎo)岩遗,只允許標(biāo)量對張量求導(dǎo)扇商,求導(dǎo)結(jié)果是和自變量同形的張量。所以必要時我們要把張量通過將所有張量的元素加權(quán)求和的方式轉(zhuǎn)換為標(biāo)量喘先,舉個例子钳吟,假設(shè)y由自變量x計算而來,w是和y同形的張量窘拯,則y.backward(w)的含義是:先計算l = torch.sum(y * w)红且,則l是個標(biāo)量,然后求l對自變量x的導(dǎo)數(shù)涤姊。
下面給大家舉幾個栗子:

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>)

由于現(xiàn)在 z 不是一個標(biāo)量暇番,所以在調(diào)用backward時需要傳入一個和z同形的權(quán)重向量進(jìn)行加權(quán)求和得到一個標(biāo)量。

v = torch.tensor([[1.0, 0.1], [0.01, 0.001]], dtype=torch.float)
z.backward(v)
print(x.grad)

輸出為:

tensor([2.0000, 0.2000, 0.0200, 0.0020])

另外PyTorch中梯度是累加的思喊,如果在編碼過程中需要暫停梯度的記錄壁酬,也可以進(jìn)行操作:

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

可以看到,上面的y2是沒有g(shù)rad_fn而且y2.requires_grad=False的恨课,而y3是有g(shù)rad_fn的舆乔。如果我們將y3對x求梯度的話會是多少呢?

y3.backward()
print(x.grad)
#輸出tensor(2.)

這里數(shù)學(xué)知識比較好的人可能又要發(fā)問了剂公,為什么和我算的結(jié)果不一樣希俩,是不是錯的?當(dāng)然不是纲辽,因為y2的定義是被torch.no_grad():包含的颜武,所以與y2有關(guān)的梯度是不會回傳的,只有與y1有關(guān)的梯度才會回傳拖吼,就是并沒有計算y2的鳞上。
另外,由于y2.requires_grad=False吊档,所以不能調(diào)用 y2.backward()篙议,會報錯:

RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn

此外,如果你想要修改tensor的數(shù)值怠硼,但是又不希望被autograd記錄(即不會影響反向傳播)涡上,那么你可以對tensor.data進(jìn)行操作趾断。

x = torch.ones(1,requires_grad=True)

print(x.data) # 還是一個tensor
print(x.data.requires_grad) # 但是已經(jīng)是獨立于計算圖之外

y = 2 * x
x.data *= 100 # 只改變了值,不會記錄在計算圖吩愧,所以不會影響梯度傳播

y.backward()
print(x) # 更改data的值也會影響tensor的值
print(x.grad)

輸出為:

tensor([1.])
False
tensor([100.], requires_grad=True)
tensor([2.])

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市增显,隨后出現(xiàn)的幾起案子雁佳,更是在濱河造成了極大的恐慌,老刑警劉巖同云,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件糖权,死亡現(xiàn)場離奇詭異,居然都是意外死亡炸站,警方通過查閱死者的電腦和手機(jī)星澳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旱易,“玉大人禁偎,你說我怎么就攤上這事》Щ担” “怎么了如暖?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長忌堂。 經(jīng)常有香客問我盒至,道長,這世上最難降的妖魔是什么士修? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任枷遂,我火速辦了婚禮,結(jié)果婚禮上棋嘲,老公的妹妹穿的比我還像新娘酒唉。我一直安慰自己,他們只是感情好封字,可當(dāng)我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布黔州。 她就那樣靜靜地躺著,像睡著了一般阔籽。 火紅的嫁衣襯著肌膚如雪流妻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天笆制,我揣著相機(jī)與錄音绅这,去河邊找鬼。 笑死在辆,一個胖子當(dāng)著我的面吹牛证薇,可吹牛的內(nèi)容都是我干的度苔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼浑度,長吁一口氣:“原來是場噩夢啊……” “哼寇窑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起箩张,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤甩骏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后先慷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饮笛,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年论熙,在試婚紗的時候發(fā)現(xiàn)自己被綠了福青。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡脓诡,死狀恐怖无午,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情誉券,我是刑警寧澤指厌,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站踊跟,受9級特大地震影響踩验,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜商玫,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一箕憾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拳昌,春花似錦袭异、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至沈矿,卻和暖如春上真,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背羹膳。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工睡互, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓就珠,卻偏偏與公主長得像寇壳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子妻怎,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容