Pytorch 之 backward
首先看這個自動求導的參數:
- 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(標量,一維向量)來說可以不用填寫
先說一下自己總結的一個通式,適用於所有形式:
對於此式,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