關於Pytorch中autograd和backward的一些筆記


參考自《Pytorch autograd,backward詳解》:

1 Tensor

Pytorch中所有的計算其實都可以回歸到Tensor上,所以有必要重新認識一下Tensor。

如果我們需要計算某個Tensor的導數,那么我們需要設置其.requires_grad屬性為True。為方便說明,在本文中對於這種我們自己定義的變量,我們稱之為葉子節點(leaf nodes),而基於葉子節點得到的中間或最終變量則可稱之為結果節點

另外一個Tensor中通常會記錄如下圖中所示的屬性:

  • data: 即存儲的數據信息
  • requires_grad: 設置為True則表示該 Tensor 需要求導
  • grad: 該 Tensor 的梯度值,每次在計算 backward 時都需要將前一時刻的梯度歸零,否則梯度值會一直累加,這個會在后面講到。
  • grad_fn: 葉子節點通常為 None,只有結果節點的 grad_fn 才有效,用於指示梯度函數是哪種類型。
  • is_leaf: 用來指示該 Tensor 是否是葉子節點。

舉例:

x = torch.rand(3, requires_grad=True)
y = x ** 2
z = x + x

print(
    'x requires grad: {},  is leaf: {},  grad: {},  grad_fn: {}.'
        .format(x.requires_grad, x.is_leaf, x.grad, x.grad_fn)
)
print(
    'y requires grad: {},  is leaf: {},  grad: {},  grad_fn: {}.'
        .format(y.requires_grad, y.is_leaf, y.grad, y.grad_fn)
)
print(
    'z requires grad: {},  is leaf: {},  grad: {},  grad_fn: {}.'
        .format(z.requires_grad, z.is_leaf, z.grad, z.grad_fn)
)

運行結果:

x requires grad: True,  is leaf: True,  grad: None,  grad_fn: None.
y requires grad: True,  is leaf: False,  grad: None,  grad_fn: <PowBackward0 object at 0x0000021A3002CD88>.
z requires grad: True,  is leaf: False,  grad: None,  grad_fn: <AddBackward0 object at 0x0000021A3002CD88>.

 

2 torch.autograd.backward

如下代碼:

x = torch.tensor(1.0, requires_grad=True)
y = torch.tensor(2.0, requires_grad=True)
z = x**2+y
z.backward()
print(z, x.grad, y.grad)

>>> tensor(3., grad_fn=<AddBackward0>) tensor(2.) tensor(1.)

當 z 是一個標量,當調用它的 backward 方法后會根據鏈式法則自動計算出葉子節點的梯度值。

但是如果遇到 z 是一個向量或者是一個矩陣的情況,這個時候又該怎么計算梯度呢?這種情況我們需要定義grad_tensor來計算矩陣的梯度。在介紹為什么使用之前我們先看一下源代碼中backward的接口是如何定義的:

torch.autograd.backward(
        tensors, 
        grad_tensors=None, 
        retain_graph=None, 
        create_graph=False, 
        grad_variables=None)
  • tensor: 用於計算梯度的 tensor。也就是說這兩種方式是等價的:torch.autograd.backward(z) == z.backward()
  • grad_tensors: 在計算非標量的梯度時會用到。他其實也是一個tensor,它的shape一般需要和前面的tensor保持一致。
  • retain_graph: 通常在調用一次 backward 后,pytorch 會自動把計算圖銷毀,所以要想對某個變量重復調用 backward,則需要將該參數設置為True
  • create_graph: 當設置為True的時候可以用來計算更高階的梯度
  • grad_variables: 這個官方說法是 grad_variables' is deprecated. Use 'grad_tensors' instead. 也就是說這個參數后面版本中應該會丟棄,直接使用grad_tensors就好了。

pytorch設計了grad_tensors這么一個參數。它的作用相當於“權重”。

先看一個例子:

x = torch.ones(2,requires_grad=True)
z = x + 2
z.backward()

>>> ...
RuntimeError: grad can be implicitly created only for scalar outputs

上面的報錯信息意思是只有對標量輸出它才會計算梯度,而求一個矩陣對另一矩陣的導數束手無策。

$X = \begin{bmatrix} x_0 & x_1 \end{bmatrix} \Rightarrow Z = \begin{bmatrix} x_0 + 2 & x_1 + 2 \end{bmatrix} \Rightarrow \frac{\partial Z}{\partial X} = ?$

那么我們只要想辦法把 $Z$ 轉變成一個標量不就好了?比如我們可以對 $Z$ 求和,然后用求和得到的標量在分別對 $x_0, x_1$ 求導,這樣不會對結果有影響,例如:

$Z_{sum} = \sum z_i = x_0 + x_1 + 4$

$\frac{\partial Z_{sum}}{\partial x_0} = \frac{\partial Z_{sum}}{\partial x_1} = 1$

x = torch.ones(2,requires_grad=True)
z = x + 2
z.sum().backward()
print(x.grad)

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

grad_tensors這個參數就扮演了幫助求和的作用。

換句話說,就是對 $Z$ 和一個權重張量grad_tensors進行 hadamard product 后求和。這也是 grad_tensors 需要與傳入的 tensor 大小一致的原因。

x = torch.ones(2,requires_grad=True)
z = x + 2
z.backward(torch.ones_like(z)) # grad_tensors需要與輸入tensor大小一致
print(x.grad)

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

 

3 torch.autograd.grad

torch.autograd.grad(
        outputs, 
        inputs, 
        grad_outputs=None, 
        retain_graph=None, 
        create_graph=False, 
        only_inputs=True, 
        allow_unused=False)

看了前面的內容后再看這個函數就很好理解了,各參數作用如下:

  • outputs: 結果節點,即被求導數
  • inputs: 葉子節點
  • grad_outputs: 類似於backward方法中的grad_tensors
  • retain_graph: 同上
  • create_graph: 同上
  • only_inputs: 默認為True,如果為True,則只會返回指定input的梯度值。 若為False,則會計算所有葉子節點的梯度,並且將計算得到的梯度累加到各自的.grad屬性上去。
  • allow_unused: 默認為False, 即必須要指定input,如果沒有指定的話則報錯。

注意該函數返回的是 tuple 類型。


免責聲明!

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



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