自動求梯度(pytorch版本)——2020.2.20


一、Tensor用於自動求梯度

"tensor"這個單詞⼀般可譯作“張量”,張量可以看作是⼀個多維數組。標量可以看作是0維張量,向量可以看作1維張量,矩陣可以看作是⼆維張量。

    在深度學習中,我們經常需要對函數求梯度(gradient)。PyTorch提供的autograd 包能夠根據輸⼊和前向傳播過程⾃動構建計算圖,並執⾏反向傳播。本節將介紹如何使⽤autograd包來進⾏⾃動求梯度的有關操作。

概念
    Pytorch中的Tensor 是這個包的核⼼類,如果將其屬性 .requires_grad 設置為 True ,它將開始追蹤(track)在其上的所有操作(這樣就可以利⽤鏈式法則進⾏梯度傳播了)。完成計算后,可以調⽤ .backward() 來完成所有梯度計算。此 Tensor 的梯度將累積到 .grad 屬性中。

注意在 y.backward() 時,如果 y 是標量,則不需要為backward()傳⼊任何參數;否則,需要傳⼊⼀個與 y 同形的 Tensor

    如果不想要被繼續追蹤,可以調⽤ .detach() 將其從追蹤記錄中分離出來,這樣就可以防⽌將來的計算被追蹤,這樣梯度就傳不過去了。此外,還可以⽤ with torch.no_grad() 將不想被追蹤的操作代碼塊包裹起來,這種⽅法在評估模型的時候很常⽤,因為在評估模型時,我們並不需要計算可訓練參數(requires_grad=True)的梯度。

    Function 是另外⼀個很重要的類。 TensorFunction 互相結合就可以構建⼀個記錄有整個計算過程的有向⽆環圖(DAG)。每個 Tensor 都有⼀個 .grad_fn 屬性,該屬性即創建該 TensorFunction , 就是說該 Tensor 是不是通過某些運算得到的,若是,則 grad_fn 返回⼀個與這些運算相關的對象,否則是None。

import torch

# 通過設置`requires_grad=Ytue`,使得操作通過鏈式法則進行梯度傳播
x = torch.ones(2, 2, requires_grad=True)
print(x)
print(x.grad_fn)

# +的運算操作
y = x+2
print(y)
print(y.grad_fn)
print(x.is_leaf,y.is_leaf)

# 復雜一點的運算操作
z = y*y*2
print(z)
print(z.grad_fn)
out = z.mean()
print(z, out)

    輸出結果:

二、梯度

    因為 out 是⼀個標量,所以調⽤ backward() 時不需要指定求導變量:

# 通過`requires_grad()`來用`in-place`的方式來改變`requieres_grad`屬性
a = torch.rand(2,2)
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)

輸出結果:

因為 out 是⼀個標量,所以調⽤ backward() 時不需要指定求導變量:

# 因為 out 是⼀個標量,所以調⽤ backward() 時不需要指定求導變量:
out.backward()
print(x.grad)

輸出結果:(教程是4.5,因為前面我的數據處理不同)

    量都為向量的函數\(\vec y = f\left(\vec x \right)\) , 那么\(\vec y\) 關於\(\vec x\)的梯度就是⼀個雅可⽐矩陣(Jacobian matrix):

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

    ⽽ torch.autograd 這個包就是⽤來計算⼀些雅克⽐矩陣的乘積的。例如,如果v 是⼀個標量函數的\(l = g \left(\vec y \right)\)的梯度:

\[v = \begin{pmatrix} \frac{\partial l}{\partial y_1} & \cdots & \frac{\partial l}{\partial y_m} \end{pmatrix} \]

    那么根據鏈式法則我們有\(l\) 關於\(\vec x\) 的雅克⽐矩陣就為:

\[vJ = \begin{pmatrix} \frac{\partial l}{\partial y_1} & \cdots & \frac{\partial l}{\partial y_m} \\ \end{pmatrix} \begin{pmatrix} \frac{\partial y_1}{\partial x_1} & \cdots & \frac{\partial y_1}{\partial x_n} \\ \vdots & \ddots & \vdots \\ \frac{\partial y_m}{\partial x_1} & \cdots & \frac{\partial y_m}{\partial x_n} \end{pmatrix} = \begin{pmatrix} \frac{\partial l}{\partial x_1} & \cdots & \frac{\partial l}{\partial x_n} \end{pmatrix} \]

注意:grad在反向傳播過程中是累加的(accumulated),這意味着每⼀次運⾏反向傳播,梯度都會累加之前的梯度,所以⼀般在反向傳播之前需把梯度清零。

    再來反向傳播⼀次,注意grad是累加的:

out2 = x.sum()
out2.backward()
print(x.grad)

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

    輸出結果:

Question:為什么在 y.backward() 時,如果 y 是標量,則不需要為 backward() 傳⼊任何參數,否則,需要傳⼊⼀個與 y 同形的 Tensor ?
Ans:為了避免向量(或者更高維張量)對張量進行求導,而轉換成標量對張量進行求導(通過將所有張量的元素加權求和的方式轉化為標量。)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM