PyTorch學習:自動求導

自動求導是 PyTorch 中非常重要的特性,能夠讓我們避免手動去計算非常複雜的導數,這能夠極大地減少了我們構建模型的時間,這也是其前身 Torch 這個框架所不具備的特性,下面我們通過例子看看 PyTorch 自動求導的獨特魅力以及探究自動求導的更多用法。

import torch
from torch.autograd import Variable
# 簡單情況的自動求導
# 下面我們顯示一些簡單情況的自動求導,"簡單"體現在計算的結果都是標量,也就是一個數,我們對這個標量進行自動求導。
x = Variable(torch.Tensor([2]), requires_grad=True)
y = x + 2
z = y ** 2 + 3
print(z)
# 使用自動求導
z.backward()
print(x.grad)
tensor([ 19.])
tensor([ 8.])
通過上面的一些列操作,我們從 x 得到了最後的結果out,我們可以將其表示爲數學公式
z=(x+2)2+3z=(x+2)2+3
那麼我們從 z 對 x 求導的結果就是
∂z/∂x=2(x+2)=2(2+2)=8
# 對於上面這樣一個簡單的例子,我們驗證了自動求導,同時可以發現發現使用自動求導非常方便。
# 如果是一個更加複雜的例子,那麼手動求導就會顯得非常的麻煩,所以自動求導的機制能夠幫助我們省去麻煩的數學計算,下面我們可以看一個更加複雜的例子。
x = Variable(torch.randn(10, 20), requires_grad=True)
y = Variable(torch.randn(10, 5), requires_grad=True)
w = Variable(torch.randn(20, 5), requires_grad=True)
out = torch.mean(y - torch.matmul(x, w)) # torch.matmul 是做矩陣乘法
out.backward()
# 得到 x 的梯度
print(x.grad)
# 得到 y 的的梯度
print(y.grad)
# 得到 w 的梯度
print(w.grad)

tensor(1.00000e-02 *
       [[-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
         -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
         -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
        [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
         -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
         -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
        [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
         -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
         -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
        [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
         -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
         -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
        [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
         -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
         -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
        [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
         -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
         -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
        [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
         -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
         -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
        [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
         -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
         -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
        [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
         -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
         -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601],
        [-3.9407,  3.5175, -1.6757,  7.0566,  4.7556,  4.1101,  3.7159,
         -1.5029, -0.9861, -2.6751,  5.5320,  0.8253, -1.1554,  4.2152,
         -7.8965,  1.3745,  7.6813, -1.8536,  0.2535, -4.9601]])
tensor(1.00000e-02 *
       [[ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
        [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
        [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
        [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
        [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
        [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
        [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
        [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
        [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000],
        [ 2.0000,  2.0000,  2.0000,  2.0000,  2.0000]])
tensor([[ 0.1169,  0.1169,  0.1169,  0.1169,  0.1169],
        [ 0.0960,  0.0960,  0.0960,  0.0960,  0.0960],
        [-0.0079, -0.0079, -0.0079, -0.0079, -0.0079],
        [-0.0947, -0.0947, -0.0947, -0.0947, -0.0947],
        [-0.0520, -0.0520, -0.0520, -0.0520, -0.0520],
        [ 0.0512,  0.0512,  0.0512,  0.0512,  0.0512],
        [-0.0107, -0.0107, -0.0107, -0.0107, -0.0107],
        [ 0.0929,  0.0929,  0.0929,  0.0929,  0.0929],
        [ 0.1115,  0.1115,  0.1115,  0.1115,  0.1115],
        [-0.0069, -0.0069, -0.0069, -0.0069, -0.0069],
        [ 0.0536,  0.0536,  0.0536,  0.0536,  0.0536],
        [ 0.0340,  0.0340,  0.0340,  0.0340,  0.0340],
        [-0.0295, -0.0295, -0.0295, -0.0295, -0.0295],
        [ 0.0401,  0.0401,  0.0401,  0.0401,  0.0401],
        [ 0.0659,  0.0659,  0.0659,  0.0659,  0.0659],
        [ 0.0636,  0.0636,  0.0636,  0.0636,  0.0636],
        [ 0.0617,  0.0617,  0.0617,  0.0617,  0.0617],
        [-0.0532, -0.0532, -0.0532, -0.0532, -0.0532],
        [ 0.1524,  0.1524,  0.1524,  0.1524,  0.1524],
        [ 0.0155,  0.0155,  0.0155,  0.0155,  0.0155]])

# 複雜情況的自動求導
# 上面我們展示了簡單情況下的自動求導,都是對標量進行自動求導.
# 下面我們會介紹對多維數組的自動求導機制。
m = Variable(torch.FloatTensor([[2, 3]]), requires_grad=True) # 構建一個 1 x 2 的矩陣
n = Variable(torch.zeros(1, 2)) # 構建一個相同大小的 0 矩陣
print(m)
print(n)
# 通過 m 中的值計算新的 n 中的值
n[0, 0] = m[0, 0] ** 2
n[0, 1] = m[0, 1] ** 3
print(n)
n.backward(torch.ones_like(n)) # 將 (w0, w1) 取成 (1, 1)
print(m.grad)

tensor([[ 2.,  3.]])
tensor([[ 0.,  0.]])
tensor(2.)
tensor([[  4.,  27.]])
tensor([[  4.,  27.]])

將上面的式子寫成數學公式,可以得到

下面我們直接對 n 進行反向傳播,也就是求 n 對 m 的導數。

這時我們需要明確這個導數的定義,即如何定義

在 PyTorch 中,如果要調用自動求導,需要往backward()中傳入一個參數,這個參數的形狀和 n 一樣大,比如是 (w0, w1)(w0, w1),那麼自動求導的結果就是:

通過自動求導我們得到了梯度是 4 和 27,我們可以驗算一下

 

通過驗算我們可以得到相同的結果

 

# 多次自動求導
# 通過調用 backward 我們可以進行一次自動求導,如果我們再調用一次 backward,會發現程序報錯,沒有辦法再做一次。
# 這是因爲 PyTorch 默認做完一次自動求導之後,計算圖就被丟棄了,所以兩次自動求導需要手動設置一個東西,我們通過下面的小例子來說明。
x = Variable(torch.FloatTensor([3]), requires_grad=True)
y = x * 2 + x ** 2 + 3
print(y)
y.backward(retain_graph=True) # 設置 retain_graph 爲 True 來保留計算圖
print(x.grad)
y.backward() # 再做一次自動求導,這次不保留計算圖
print(x.grad)

tensor([ 18.])
tensor([ 8.])
tensor([ 16.])

可以發現 x 的梯度變成了 16,因爲這裏做了兩次自動求導,所以講第一次的梯度 8 和第二次的梯度 8 加起來得到了 16 的結果。

x = Variable(torch.FloatTensor([2, 3]), requires_grad=True)
k = Variable(torch.zeros(2))
k[0] = x[0] ** 2 + 3 * x[1]
k[1] = x[1] ** 2 + 2 * x[0]
print(k)
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([ 13.,  13.])
tensor([[ 4.,  3.],
        [ 2.,  6.]])

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