Pytorch 之 backward


首先看這個自動求導的參數:

  • grad_variables:形狀與variable一致,對於y.backward(),grad_variables相當於鏈式法則dz/dx=dz/d× dy/d中的 dz/dy。grad_variables也可以是tensor或序列。
  • retain_graph:反向傳播需要緩存一些中間結果,反向傳播之后,這些緩存就被清空,可通過指定這個參數不清空緩存,用來多次反向傳播。
  • create_graph:對反向傳播過程再次構建計算圖,可通過backward of backward實現求高階導數。

注意variables 和 grad_variables 都可以是 sequence。對於scalar(標量,一維向量)來說可以不用填寫grad_variables參數,若填寫的話就相當於系數。若variables非標量則必須填寫grad_variables參數。下面結合參考示例來解釋一下這個參數怎么用。

先說一下自己總結的一個通式,適用於所有形式:

       對於此式,x的梯度x.grad為 

 

1.scalar標量

注意參數requires_grad=True讓其成為一個葉子節點,具有求導功能。

手動求導結果:

代碼實現:

import torch as t
from torch.autograd import Variable as v

a = v(t.FloatTensor([2, 3]), requires_grad=True)    # 注意這里為一維,標量
b = a + 3
c = b * b * 3
out = c.mean()
out.backward(retain_graph=True) # 這里可以不帶參數,默認值為‘1’,由於下面我們還要求導,故加上retain_graph=True選項

結果:

a.grad
Out[184]: 
Variable containing:
  15  
18
[torch.FloatTensor of size 1x2]

結果與手動計算一樣

 backward帶參數呢?此時的參數為系數

將梯度置零:

a.grad.data.zero_()

再次求導驗證輸入參數僅作為系數:

n.backward(torch.Tensor([[2,3]]), retain_graph=True)

 結果:(2和3應該分別作為系數相乘)

a.grad
Out[196]: 
Variable containing:
  30
54
[torch.FloatTensor of size 1x2]

驗證了我們的想法。

 

 

2.張量

import torch
from torch.autograd import Variable as V

m = V(torch.FloatTensor([[2, 3]]), requires_grad=True)   # 注意這里有兩層括號,非標量
n = V(torch.zeros(1, 2))
n[0, 0] = m[0, 0] ** 2
n[0, 1] = m[0, 1] ** 3

求導 :(此時的[[1, 1]]為系數,僅僅作為簡單乘法的系數),注意 retain_graph=True,下面我們還要求導,故置為True。

n.backward(torch.Tensor([[1,1]]), retain_graph=True)

結果:

m.grad
Out[184]: 
Variable containing:
  4  27
[torch.FloatTensor of size 1x2]

將梯度置零:

m.grad.data.zero_()

再次求導驗證輸入參數僅作為系數:

n.backward(torch.Tensor([[2,3]]))

結果:4,27 × 2,3 =8,81  驗證了系數這一說法

 m.grad
Out[196]: 
Variable containing:
  8  81
[torch.FloatTensor of size 1x2]

注意backward參數,由於是非標量,不填寫參數將會報錯。

 

 

3.  另一種重要情形

        之前我們求導都相當於是loss對於x的求導,沒有接觸中間過程。然而對於下面的鏈式法則我們知道如果知道中間的導數結果,也可以直接計算對於輸入的導數。而grad_variables參數在某種意義上就是中間結果。即上面都是z.backward()之類,那么考慮y.backward(b) 或 y.backward(x)是什么意思呢?

 

下面給出一個例子解釋清楚:

import torch
from torch.autograd import Variable
x = Variable(torch.randn(3), requires_grad=True)
y = Variable(torch.randn(3), requires_grad=True)
z = Variable(torch.randn(3), requires_grad=True)
print(x)
print(y)
print(z)

t = x + y
l = t.dot(z)

結果:

# x
Variable containing: 
 0.9168
 1.3483
 0.4293
[torch.FloatTensor of size 3]

# y
Variable containing:
 0.4982
 0.7672
 1.5884
[torch.FloatTensor of size 3]

# z
Variable containing:
 0.1352
-0.4037
-0.2425
[torch.FloatTensor of size 3]

在調用 backward 之前,可以先手動求一下導數,應該是: l = (x+y)^Tz, dl/dx = dl/dy = z, dl/dz=x+y=t, dl/dt=z

當我們打印x.grad和y.grad時都是 x.grad = y.grad = z。 當我們打印z.grad 時為 z.grad = t = x + y。這里都沒有問題。重要的來了:

先置零:

x.grad.data.zero_()
y.grad.data.zero_()
z.grad.data.zero_()

看看下面這個情況:

t.backward(z)
print(x.grad)
print(y.grad)
print(z.grad)

此時的結果為: 

x和y的導數仍然與上面一樣為z。而z的導數為0。解釋
t.backward(z): 若求x.grad: z * dt/dx 即為dl/dt × dt/dx=z
               若求y.grad: z * dt/dy   即為dl/dt × dt/dy=z
               若求z.grad: z * dt/dz   即為dl/dt × dt/dz = z×0 = 0

 再驗證一下我們的想法:

清零后看看下面這種情況:

t.backward(x)
print(x.grad)
print(y.grad)
print(z.grad)
x和y的導數仍然相等為x。而z的導數為0。解釋
t.backward(x): 若求x.grad: x * dt/dx 即為x × 1 = x
               若求y.grad: x * dt/dy   即為x × 1 = x
               若求z.grad: x * dt/dz   即為x × 0 = 0
驗證成功。

 另:k.backward(p)接受的參數p必須要和k的大小一樣。這一點也可以從通式看出來。

        

 

參考:

PyTorch 的 backward 為什么有一個 grad_variables 參數?

PyTorch 中文網

PyTorch中的backward [轉]

Calculus on Computational Graphs: Backpropagation


免責聲明!

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



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