PyTorch學習筆記2-自動微分

記得深度學習三巨頭之一Yann LeCun曾經說過:“深度學習已死,可微編程永生”,就是說深度學習只是一種計算範式,而背後的可微分編程,具有更廣闊的應用前景。在這裏我們將探索PyTorch中的自動微分技術。

Tensor函數

PyTorch中的自動微分是基於Tensor的,以Tensor作爲參數的函數,實際上是將Tensor中的每個元素應用該函數。只講概念比較抽象,我們來看一個具體的例子。
我們定義xR5\boldsymbol{x} \in R^{5},定義如下函數:
y=f(x)=[f(x1)f(x2)f(x3)f(x4)f(x5)] y=f(\boldsymbol{x})=\begin{bmatrix} f(x_{1}) \\ f(x_{2}) \\ f(x_{3}) \\ f(x_{4}) \\ f(x_{5}) \end{bmatrix}
下面的程序我們以x2x^{2}爲例:

import torch
x = torch.tensor([1.0, 2.0, 3.0, 4.0, 5.0])
y = x ** 2
print(y)
# 輸出結果爲:tensor([ 1.,  4.,  9., 16., 25.])

在深度學習中,有三種最常見的情況:標量對向量的微分、向量對向量的微分、向量對矩陣的微分,其實還有張量對張量的微分,因爲在實際中比較少見,所以這裏就不再討論了。

標量對向量的微分

通常神經網絡的代價函數會是一個標量,對於多分類問題,神經網絡的輸出爲一個向量,這時就需要求標量對向量的微分。我們定義如下函數:
y=i=15xi2 y = \sum_{i=1}^{5} x_{i}^{2}
我們知道求微分可以採用解析法、數值法和自動微分法,由於這個問題比較簡單,我們可以直接應用解析法:
yxi=2xi,i=1,2,3,4,5 \frac{ \partial{y} }{ \partial{x_{i}} } = 2x_{i}, \quad i=1,2,3,4,5
我們定義標量對向量的微分爲:
yx=[yx1yx2yx3yx4yx5]R1×n,xRn \frac{ \partial{y} }{ \partial{ \boldsymbol{x} } } = \begin{bmatrix} \frac{ \partial{y} }{ \partial{x_{1}} } & \frac{ \partial{y} }{ \partial{x_{2}} } & \frac{ \partial{y} }{ \partial{x_{3}} } & \frac{ \partial{y} }{ \partial{x_{4}} } & \frac{ \partial{y} }{ \partial{x_{5}} } \end{bmatrix} \in R^{1 \times n}, \quad \boldsymbol{x} \in R^{n}
我們可以通過PyTorch中的自動微分來完成這一任務:

x = torch.tensor([1.0, 2.0, 3.0, 4.0, 5.0], requires_grad=True, dtype=torch.float32)
y = torch.sum(x ** 2)
print(y.item()) # 打印計算值
y.backward() # 利用自動微分計算微分
print('shape:{0}; {1}'.format(x.grad.size(), x.grad)) # 打印y對x的微分值
# 打印結果:shape:torch.Size([5]); tensor([ 2.,  4.,  6.,  8., 10.])

第1行:

  • 第1行:我們在定義Tensor時,如果加入requires_grad,表明我們需要求對其的微分;
  • 第2行:計算函數值,在實際應用中就是神經網絡的前向傳播過程;
  • 第4行:調用自動微分,就是實際應用中的神經網絡的反向傳播過程;
    我們可以看到打印的結果與我們用解析法求出的內容一致。

向量對向量微分

實際上向量對向量微分叫做Jacobian矩陣,向量yRm\boldsymbol{y} \in R^{m}對向量xRn\boldsymbol{x} \in R^{n}的微分定義爲:
yx=[y1x1y1x2...y1xny2x1y2x2...y2xn............ymx1ymx2...ymxn]Rm×n \frac{ \partial{\boldsymbol{y}} }{ \partial{\boldsymbol{x}} } = \begin{bmatrix} \frac{\partial{y_{1}}}{\partial{x_{1}}} & \frac{\partial{y_{1}}}{\partial{x_{2}}} & ... & \frac{\partial{y_{1}}}{\partial{x_{n}}} \\ \frac{\partial{y_{2}}}{\partial{x_{1}}} & \frac{\partial{y_{2}}}{\partial{x_{2}}} & ... & \frac{\partial{y_{2}}}{\partial{x_{n}}} \\ ... & ... & ... & ... \\ \frac{\partial{y_{m}}}{\partial{x_{1}}} & \frac{\partial{y_{m}}}{\partial{x_{2}}} & ... & \frac{\partial{y_{m}}}{\partial{x_{n}}} \\ \end{bmatrix} \in R^{m \times n}
我們假設有如下向量,例如是神經網絡某層的輸入向量:
zl=[1.02.03.04.05.0] \boldsymbol{z}^{l}=\begin{bmatrix} 1.0 \\ 2.0 \\ 3.0 \\ 4.0 \\ 5.0 \end{bmatrix}
經過該層的激活函數(sigmoid函數)之後,得到本層的輸出向量:
al=[z12z22z13z14z15] \boldsymbol{a}^{l}=\begin{bmatrix} z_{1}^{2} \\ z_{2}^{2} \\ z_{1}^{3} \\ z_{1}^{4} \\ z_{1}^{5} \\ \end{bmatrix}
因爲本神經元的輸入只會影響本神經元的輸出,因此我們的al\boldsymbol{a}^{l}如上所示。根據解析法可得其Jacobian矩陣爲:
alzl=[a1lz1l00000a2lz2l00000a3lz3l00000a3lz3l00000a3lz3l] \frac{ \partial{\boldsymbol{a}^{l}} }{ \partial{\boldsymbol{z}^{l}} }=\begin{bmatrix} \frac{ \partial{a}_{1}^{l} }{ \partial{z}_{1}^{l} } & 0 & 0 & 0 & 0 \\ 0 & \frac{ \partial{a}_{2}^{l} }{ \partial{z}_{2}^{l} } & 0 & 0 & 0 \\ 0 & 0 & \frac{ \partial{a}_{3}^{l} }{ \partial{z}_{3}^{l} } & 0 & 0 \\ 0 & 0 & 0 & \frac{ \partial{a}_{3}^{l} }{ \partial{z}_{3}^{l} } & 0 \\ 0 & 0 & 0 & 0 & \frac{ \partial{a}_{3}^{l} }{ \partial{z}_{3}^{l} } \end{bmatrix}
其實際上只有對角線上有值,因此在PyTorch中,爲了更高效的處理問題,我們通常不需要原始的Jacobian矩陣,而是將其乘以一個與al\boldsymbol{a}^{l}同維且元素均爲1的向量,得到的結果向量的元素爲對角線上的元素。
alzl[1.01.01.01.01.0]=[a1lz1l00000a2lz2l00000a3lz3l00000a3lz3l00000a3lz3l][1.01.01.01.01.0]=[a1lz1la2lz2la2lz3la4lz4la5lz5l] \frac{ \partial{\boldsymbol{a}^{l}} }{ \partial{\boldsymbol{z}^{l}} } \cdot \begin{bmatrix} 1.0 \\ 1.0 \\ 1.0 \\ 1.0 \\ 1.0 \end{bmatrix}=\begin{bmatrix} \frac{ \partial{a}_{1}^{l} }{ \partial{z}_{1}^{l} } & 0 & 0 & 0 & 0 \\ 0 & \frac{ \partial{a}_{2}^{l} }{ \partial{z}_{2}^{l} } & 0 & 0 & 0 \\ 0 & 0 & \frac{ \partial{a}_{3}^{l} }{ \partial{z}_{3}^{l} } & 0 & 0 \\ 0 & 0 & 0 & \frac{ \partial{a}_{3}^{l} }{ \partial{z}_{3}^{l} } & 0 \\ 0 & 0 & 0 & 0 & \frac{ \partial{a}_{3}^{l} }{ \partial{z}_{3}^{l} } \end{bmatrix} \cdot \begin{bmatrix} 1.0 \\ 1.0 \\ 1.0 \\ 1.0 \\ 1.0 \end{bmatrix}= \cdot \begin{bmatrix} \frac{ \partial{a}_{1}^{l} }{ \partial{z}_{1}^{l} } \\ \frac{ \partial{a}_{2}^{l} }{ \partial{z}_{2}^{l} } \\ \frac{ \partial{a}_{2}^{l} }{ \partial{z}_{3}^{l} } \\ \frac{ \partial{a}_{4}^{l} }{ \partial{z}_{4}^{l} } \\ \frac{ \partial{a}_{5}^{l} }{ \partial{z}_{5}^{l} } \end{bmatrix}
在PyTorch中代碼如下所示:

    x = torch.tensor([1.0, 2.0, 3.0, 4.0, 5.0], requires_grad=True)
    y = x**2
    y.backward(torch.ones_like(y))
    print('y_x:{0}'.format(x.grad))
    # 輸出:y_x:tensor([ 2.,  4.,  6.,  8., 10.])

向量對矩陣求微分

在深度學習中,經常會出現上一層的輸入向量zlRNl\boldsymbol{z}^{l} \in R^{N_{l}}對連接權值WlRNl×Nl1W^{l} \in R^{N_{l} \times N_{l-1}}的微分,公式爲:
zl=Wlal1+bl \boldsymbol{z}^{l}=W^{l} \cdot \boldsymbol{a}^{l-1} + \boldsymbol{b}^{l}
其中al1RNl1\boldsymbol{a}^{l-1} \in R^{N_{l-1}}blRNl\boldsymbol{b}^{l} \in R^{N_{l}}
通過解析法,我們可以得到其微分公式爲:
zlWl=[a1l1a2l1...al1l1a1l1a2l1...al1l1............a1l1a2l1...al1l1]=[(al1)T(al1)T...(al1)T]RNl×Nl1 \frac{ \partial{ \boldsymbol{z}^{l} } }{ \partial{W^{l}} } = \begin{bmatrix} a_{1}^{l-1} & a_{2}^{l-1} & ... & a_{l-1}^{l-1} \\ a_{1}^{l-1} & a_{2}^{l-1} & ... & a_{l-1}^{l-1} \\ ... & ... & ... & ... \\ a_{1}^{l-1} & a_{2}^{l-1} & ... & a_{l-1}^{l-1} \\ \end{bmatrix} = \begin{bmatrix} (\boldsymbol{a}^{l-1})^{T} \\ (\boldsymbol{a}^{l-1})^{T} \\ ... \\ (\boldsymbol{a}^{l-1})^{T} \end{bmatrix} \in R^{ N_{l} \times N_{l-1} }
PyTorch代碼如下所示:

    a_l_1 = torch.tensor([1.0, 2.0, 3.0, 4.0, 5.0], requires_grad=True)
    W_l = torch.tensor([
        [101.0, 102.0, 103.0, 104.0, 105.0],
        [201.0, 202.0, 203.0, 204.0, 205.0],
        [301.0, 302.0, 303.0, 304.0, 305.0]
    ], requires_grad=True)
    b_l = torch.tensor([1001.0, 1002.0, 1003.0], requires_grad=True)
    z_l = torch.matmul(W_l, a_l_1) + b_l
    z_l.backward(torch.ones_like(z_l))
    print('y_x:{0}'.format(W_l.grad))
    ''' 打印輸出
    y_x:tensor([[1., 2., 3., 4., 5.],
        [1., 2., 3., 4., 5.],
        [1., 2., 3., 4., 5.]])
    '''

上述程序的打印結果與理論分析的結果一致,證明我們的求法是正確的。

發佈了204 篇原創文章 · 獲贊 1236 · 訪問量 124萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章