1. 程式人生 > >Pytorch 之 backward

Pytorch 之 backward

calc tom 一個 梯度 1.5 選項 alt variant 成功

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

技術分享圖片

  • grad_variables:形狀與variable一致,對於y.backward(),grad_variables相當於鏈式法則dz/dx=dz/dy × dy/dx 中的 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 之前,可以先手動求一下導數,應該是: 技術分享圖片

當我們打印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 中文網

PyTorch中的backward [轉]

Calculus on Computational Graphs: Backpropagation

Pytorch 之 backward