Pytorch 自動求導機制
文章目錄
- Pytorch 自動求導機制
-
- 1. 簡單的自動求導(輸出是1維)
- 2. 多元數組的自動求導(輸出是多元)
- 3. 多次自動求導
自動求導是 PyTorch 中非常重要的特性,能夠讓我們避免手動去計算非常複雜的導數,這能夠極大地減少了我們構模組化型的時間。
每個Tensor都有個标志:
requires_grad
,它都允許從梯度計算中精細地排除子圖,并可以提高效率。
requires_grad=True
要求梯度
requires_grad=False
不要求梯度
什麼是排除子圖?
答: 排除沒必要的梯度計算。
requires_grad
如果有一個單一的輸入操作需要梯度,它的輸出也需要梯度。相反,隻有所有輸入都不需要梯度,輸出才不需要。如果其中所有的變量都不需要梯度進行,後向計算不會在子圖中執行。
步驟:
- 導入Variable并指定
,requires_grad=True
-
y.backward()
-
x.grad
1. 簡單的自動求導(輸出是1維)
import torch
from torch.autograd import Variable
x = torch.Tensor([2])
# tensor 變成Variable
x = Variable(x,requires_grad=True)
y.backward()
print(x.grad)
tensor([12.])
例2: z = ( x + 2 ) 2 + 3 z = (x + 2)^2 + 3 z=(x+2)2+3, z z z對 x x x進行求導。
x = Variable(torch.Tensor([2]), requires_grad=True)
y = x + 2
z = y ** 2 + 3
print(z)
tensor([19.], grad_fn=<AddBackward0>)
# 使用自動求導
z.backward()
print(x.grad)
tensor([8.])
例3:更複雜的例子
x = Variable(torch.randn(5, 10), requires_grad=True)
y = Variable(torch.randn(5, 5), requires_grad=True)
w = Variable(torch.randn(10, 5), requires_grad=True)
out = torch.mean(y - torch.mm(x, w)) # torch.matmul 是做矩陣乘法或者的使用torch.mm()
print(out)
out.backward()
tensor(-0.8683, grad_fn=<MeanBackward0>)
# 得到 x 的梯度
print(x.grad)
tensor([[ 0.0479, 0.0470, 0.0192, 0.1219, 0.2274, -0.1751, 0.0755, 0.0062,
-0.1455, 0.0992],
[ 0.0479, 0.0470, 0.0192, 0.1219, 0.2274, -0.1751, 0.0755, 0.0062,
-0.1455, 0.0992],
[ 0.0479, 0.0470, 0.0192, 0.1219, 0.2274, -0.1751, 0.0755, 0.0062,
-0.1455, 0.0992],
[ 0.0479, 0.0470, 0.0192, 0.1219, 0.2274, -0.1751, 0.0755, 0.0062,
-0.1455, 0.0992],
[ 0.0479, 0.0470, 0.0192, 0.1219, 0.2274, -0.1751, 0.0755, 0.0062,
-0.1455, 0.0992]])
# 得到 y 的的梯度
print(y.grad)
tensor([[0.0400, 0.0400, 0.0400, 0.0400, 0.0400],
[0.0400, 0.0400, 0.0400, 0.0400, 0.0400],
[0.0400, 0.0400, 0.0400, 0.0400, 0.0400],
[0.0400, 0.0400, 0.0400, 0.0400, 0.0400],
[0.0400, 0.0400, 0.0400, 0.0400, 0.0400]])
# 得到 w 的梯度
print(w.grad)
tensor([[0.0219, 0.0219, 0.0219, 0.0219, 0.0219],
[0.0160, 0.0160, 0.0160, 0.0160, 0.0160],
[0.0847, 0.0847, 0.0847, 0.0847, 0.0847],
[0.1473, 0.1473, 0.1473, 0.1473, 0.1473],
[0.0931, 0.0931, 0.0931, 0.0931, 0.0931],
[0.0924, 0.0924, 0.0924, 0.0924, 0.0924],
[0.1073, 0.1073, 0.1073, 0.1073, 0.1073],
[0.0393, 0.0393, 0.0393, 0.0393, 0.0393],
[0.0226, 0.0226, 0.0226, 0.0226, 0.0226],
[0.0419, 0.0419, 0.0419, 0.0419, 0.0419]])
2. 多元數組的自動求導(輸出是多元)
例4:多元數組的自動求導機制
m = Variable(torch.FloatTensor([[2, 3]]), requires_grad=True) # 建構一個 1 x 2 的矩陣
n = Variable(torch.zeros(1, 2)) # 建構一個相同大小的 0 矩陣
print(m)
print(n)
tensor([[2., 3.]], requires_grad=True)
tensor([[0., 0.]])
# 通過 m 中的值計算新的 n 中的值
n[0, 0] = m[0, 0] ** 2
n[0, 1] = m[0, 1] ** 3
print(n)
tensor([[ 4., 27.]], grad_fn=<CopySlices>)
import numpy as np
a =np.array([[1,2,3],[4,5,6]])
a[1,1]==a[1][1] #True
# 隻有二維數組或矩陣,tensor才能這樣取數
True
n = ( n 0 , n 1 ) = ( m 0 2 , m 1 3 ) = ( 2 2 , 3 3 ) n = (n_0,\ n_1) = (m_0^2,\ m_1^3) = (2^2,\ 3^3) n=(n0, n1)=(m02, m13)=(22, 33),對 n n n進行反向傳播,也就是 n n n對 m m m的導數。
∂ n ∂ m = ∂ ( n 0 , n 1 ) ∂ ( m 0 , m 1 ) \frac{\partial n}{\partial m} = \frac{\partial (n_0,\ n_1)}{\partial (m_0,\ m_1)} ∂m∂n=∂(m0, m1)∂(n0, n1)
在 PyTorch 中,如果要調用自動求導,需要往
backward()
中傳入一個參數,這個參數的形狀和 n 一樣大,比如是 ( w 0 , w 1 ) (w_0,\ w_1) (w0, w1),那麼自動求導的結果就是:
∂ n ∂ m 0 = w 0 ∂ n 0 ∂ m 0 + w 1 ∂ n 1 ∂ m 0 \frac{\partial n}{\partial m_0} = w_0 \frac{\partial n_0}{\partial m_0} + w_1 \frac{\partial n_1}{\partial m_0} ∂m0∂n=w0∂m0∂n0+w1∂m0∂n1
∂ n ∂ m 1 = w 0 ∂ n 0 ∂ m 1 + w 1 ∂ n 1 ∂ m 1 \frac{\partial n}{\partial m_1} = w_0 \frac{\partial n_0}{\partial m_1} + w_1 \frac{\partial n_1}{\partial m_1} ∂m1∂n=w0∂m1∂n0+w1∂m1∂n1
n.backward(torch.ones_like(n)) # 将 (w0, w1) 取成 (1, 1)
print(m.grad)
tensor([[ 4., 27.]])
關鍵:為什麼要
torch.ones_like(n)
?【向量求導】
n不是一個标量,是一個向量,需要傳入一個維數一樣的1向量,使得梯度是每個分量的求梯度的和。
驗證:
∂ n ∂ m 0 = w 0 ∂ n 0 ∂ m 0 + w 1 ∂ n 1 ∂ m 0 = 2 m 0 + 0 = 2 × 2 = 4 \frac{\partial n}{\partial m_0} = w_0 \frac{\partial n_0}{\partial m_0} + w_1 \frac{\partial n_1}{\partial m_0} = 2 m_0 + 0 = 2 \times 2 = 4 ∂m0∂n=w0∂m0∂n0+w1∂m0∂n1=2m0+0=2×2=4
∂ n ∂ m 1 = w 0 ∂ n 0 ∂ m 1 + w 1 ∂ n 1 ∂ m 1 = 0 + 3 m 1 2 = 3 × 3 2 = 27 \frac{\partial n}{\partial m_1} = w_0 \frac{\partial n_0}{\partial m_1} + w_1 \frac{\partial n_1}{\partial m_1} = 0 + 3 m_1^2 = 3 \times 3^2 = 27 ∂m1∂n=w0∂m1∂n0+w1∂m1∂n1=0+3m12=3×32=27
3. 多次自動求導
通過調用
backward
我們可以進行一次自動求導,如果我們再調用一次
backward
,會發現程式報錯,沒有辦法再做一次。這是因為 PyTorch 預設做完一次自動求導之後,計算圖就被丢棄了,是以兩次自動求導需要手動設定一個東西,我們通過下面的小例子來說明。
x = Variable(torch.FloatTensor([3]), requires_grad=True)
y = x * 2 + x ** 2 + 3
print(y)
tensor([18.], grad_fn=<AddBackward0>)
設定
retain_graph
為
True
來保留計算圖
y.backward(retain_graph=True)
print(x.grad)
tensor([8.])
y.backward() # 再做一次自動求導,這次不保留計算圖
print(x.grad)
tensor([16.])
注意:這個張量的所有梯度将會自動累加到
.grad
屬性.
y.backward() # 第三次做自動求導,這次不保留計算圖
print(x.grad)# 報錯
練習
x = [ x 0 x 1 ] = [ 2 3 ] x = \left[ \begin{matrix} x_0 \ x_1 \end{matrix} \right] = \left[ \begin{matrix} 2 \ 3 \end{matrix} \right] x=[x0 x1]=[2 3]
k = ( k 0 , k 1 ) = ( x 0 2 + 3 x 1 , 2 x 0 + x 1 2 ) k = (k_0,\ k_1) = (x_0^2 + 3 x_1,\ 2 x_0 + x_1^2) k=(k0, k1)=(x02+3x1, 2x0+x12)
求:
j = [ ∂ k 0 ∂ x 0 ∂ k 0 ∂ x 1 ∂ k 1 ∂ x 0 ∂ k 1 ∂ x 1 ] j = \left[ \begin{matrix} \frac{\partial k_0}{\partial x_0} & \frac{\partial k_0}{\partial x_1} \ \frac{\partial k_1}{\partial x_0} & \frac{\partial k_1}{\partial x_1} \end{matrix} \right] j=[∂x0∂k0∂x1∂k0 ∂x0∂k1∂x1∂k1]
k=Variable(torch.zeros(2))
k[0]=x[0]**2+3*x[1]
k[1]=2*x[0]+x[1]**2
print(k)
tensor([13., 13.], grad_fn=<CopySlices>)
j = torch.zeros(2, 2)
k.backward(torch.FloatTensor([1, 0]), retain_graph=True)
j[0] = x.grad.data
x.grad.data.zero_() # 歸零之前求得的梯度
k.backward(torch.FloatTensor([0, 1]))
j[1] = x.grad.data
print(j)
tensor([[4., 3.],
[2., 6.]])
k.backward(torch.ones_like(k),retain_graph=True)
print(x.grad)
參考:自動求導機制