pytorch中backward()函數詳解

最近由於實際需要在學習pytorch,作爲深度學習中最爲重要的反向傳播計算,pytorch用非常簡單的backward( )函數就實現了,但是在實現過程中對於其參數存在一些疑問,下面就從pytorch中反向傳播求導的計算方式,backward( )函數參數來進行說明。

這裏首先還是放出backward( )函數的pytorch文檔,因爲整個說明主要還是圍繞這個函數來進行的。

在這裏插入圖片描述

問題描述

從上面的文檔可以看到backward函數有一個奇怪的參數:grad_tensors,在實現pytorch的官方教程中可以發現:

import torch
import torch.nn as nn

x = torch.tensor([2, 3, 4], dtype=torch.float, requires_grad=True)
print(x)
y = x * 2
while y.norm() < 1000:
    y = y * 2
print(y)

y.backward(torch.ones_like(y))
print(x.grad)

上面的程序的輸出爲:

tensor([2., 3., 4.], requires_grad=True)
tensor([ 512.,  768., 1024.], grad_fn=<MulBackward0>)
tensor([256., 256., 256.])

這裏我們分佈來講述上面的過程:

  1. 創建一個張量x,並設置其 requires_grad參數爲True,程序將會追蹤所有對於該張量的操作,當完成計算後通過調用 .backward(),自動計算所有的梯度, 這個張量的所有梯度將會自動積累到 .grad 屬性。
  2. 創建一個關於x的函數y,由於x的requires_grad參數爲True,所以y對應的用於求導的參數grad_fn<MulBackward0>。這是因爲在自動梯度計算中還有另外一個重要的類FunctionTensorFunction互相連接並生成一個非循環圖,它表示和存儲了完整的計算曆史。 每個張量都有一個.grad_fn屬性,這個屬性引用了一個創建了TensorFunction(除非這個張量是用戶手動創建的,即,這個張量的 grad_fnNone,例如1中創建的x的 grad_fnNone
  3. 我們進行反向傳播並輸出x的梯度值,而這裏出現了一個參數torch.ones_like(y)即爲grad_tensors參數,這裏便引入了我們的問題:

爲什麼在求導的過程中需要引入這個參數,如果我們不引入這個參數的話,則會報下面的錯誤:

RuntimeError: grad can be implicitly created only for scalar outputs

即爲提示我們輸出不是一個標量

下面就開始分析這個問題以及這個參數的作用。

pytorch實現反向傳播中求導的方法

這裏主要參考pytorch計算圖文章中的講解:

由上面的文章知道pytorch是動態圖機制,在訓練模型時候,每迭代一次都會構建一個新的計算圖。而計算圖其實就是代表程序中變量之間的關係。對於y=(a+b)(b+c)y=(a+b)(b+c)這個例子可以構建如下計算圖:

在這裏插入圖片描述

上面的計算圖中每一個葉子節點都是一個用戶自己創建的變量,在網絡backward時候,需要用鏈式求導法則求出網絡最後輸出的梯度,然後再對網絡進行優化,如下就是網絡的求導過程。

在這裏插入圖片描述

通過觀察上面的計算圖可以發現一個很重要的點:

pytorch在利用計算圖求導的過程中根節點都是一個標量,即一個數。當根節點即函數的因變量爲一個向量的時候,會構建多個計算圖對該向量中的每一個元素分別進行求導,這也就引出了下一節的內容

這裏順帶說一下:

pytoch構建的計算圖是動態圖,爲了節約內存,所以每次一輪迭代完也即是進行了一次backward函數計算之後計算圖就被在內存釋放,因此如果你需要多次backward只需要在第一次反向傳播時候添加一個retain_graph=True標識,讓計算圖不被立即釋放。實際上文檔中retain_graphcreate_graph兩個參數作用相同,因爲前者是保持計算圖不釋放,而後者是創建計算圖,因此如果我們不想要計算圖釋放掉,將任意一個參數設置爲True都行。(這一段說明的內容與本文主要說明的內容無關,只是順帶說明一下)

backward函數

結合上面兩節的分析,可以發現,pytorch在求導的過程中,分爲下面兩種情況:

  • 如果是標量對向量求導(scalar對tensor求導),那麼就可以保證上面的計算圖的根節點只有一個,此時不用引入grad_tensors參數,直接調用backward函數即可
  • 如果是(向量)矩陣對(向量)矩陣求導(tensor對tensor求導),實際上是先求出Jacobian矩陣中每一個元素的梯度值(每一個元素的梯度值的求解過程對應上面的計算圖的求解方法),然後將這個Jacobian矩陣與grad_tensors參數對應的矩陣進行對應的點乘,得到最終的結果。

Jacobian矩陣參考:Jacobian矩陣和Hessian矩陣

爲什麼pytorch不支持tensor對tensor求導,而是引入grad_tensors參數參考:PyTorch 的 backward 爲什麼有一個 grad_variables 參數?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章