神經網絡反向傳播梯度計算數學原理


[神經網絡]反向傳播梯度計算數學原理

1 文章概述

本文通過一段來自於Pytorch官方的warm-up的例子:使用numpy來實現一個簡單的神經網絡。使用基本的數學原理,對其計算過程進行理論推導,以揭示這幾句神奇的代碼后面所包含的原理。

估計對大多數的同學來說,看完這個文章,肯定會是這樣的感覺:字都認識,但是就是不知道講的是啥~!不過對於有心人來說,本文確實能起到點睛之筆,就是你研究很久后,還差一點火候就頓悟了,希望本文能夠幫你頓悟。

關鍵字:Numpy,神經網絡,矩陣分析,反射傳播,梯度下降

 

如果發現圖片裂了,請左轉至 其它平台查看:

https://zhuanlan.zhihu.com/p/32368246

 

2 實現代碼

numpy作為一個科學計算庫,並不包含:計算圖,嘗試學習,梯度等等功能,但是我們可以簡單的通過numpy去擬合一個二層的網絡。

解決的問題:

  1. 隨機生成一組輸入數據,一組輸出數據。
  2. 定義一個神經網絡結構及其參數
  3. 根據輸入數據正向傳播,求出誤差
  4. 根據誤差反向傳播梯度,更新神經元的各個節點的參數

代碼如下:

# -*- coding: utf-8 -*-
import numpy as np

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random input and output data
x = np.random.randn(N, D_in)
y = np.random.randn(N, D_out)

# Randomly initialize weights
w1 = np.random.randn(D_in, H)
w2 = np.random.randn(H, D_out)

learning_rate = 1e-6
for t in range(500):
    # Forward pass: compute predicted y
    h = x.dot(w1)
    h_relu = np.maximum(h, 0)
    y_pred = h_relu.dot(w2)

    # Compute and print loss
    loss = np.square(y_pred - y).sum()
    print(t, loss)

    # Backprop to compute gradients of w1 and w2 with respect to loss
    grad_y_pred = 2.0 * (y_pred - y)
    grad_w2 = h_relu.T.dot(grad_y_pred)
    grad_h_relu = grad_y_pred.dot(w2.T)
    grad_h = grad_h_relu.copy()
    grad_h[h < 0] = 0
    grad_w1 = x.T.dot(grad_h)

    # Update weights
    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2
 

原文見:Learning PyTorch with Examples

 

3 網絡結構

將上面的代碼結構及相應的參數維度繪圖后如下所示:

然后本代碼使用的是一個大小為64的batch,所以輸入的值實際的大小實際上是(64,1000)。

把以上的代碼轉化成數學公式如下,括號里面是相應的矩陣的形狀:

 

4 正向計算

數據流的正向傳播

最后計算出損失函數loss,是實際預測值和先驗數據矩陣的二范數,作為兩組矩陣的距離測度。

正向傳播比較簡單,基本上大學的線性代數的基本知識看幾章,就能很好的理解。這也是后續如果在深度學習框架下面設計網絡的時候,注意設計神經元大小的時候,需要考慮矩陣乘法的可行性,即維度相容。

PS:關於矩陣的范數的定義,詳情見P32的《1.4.3矩陣的內積和范數》

5 反向傳播

 

5.1 實現代碼

 

下面是反射傳播的代碼實現:

 

5.2 數學基礎

 

關於反射傳播的數學原理,可能就不是那么好理解了,因為這里面需要用到矩陣的高級算法,一般的理工科數學的《線性代數》甚至《高等代數》里面都沒有提到相關的內容,所以基本上已經超過了大多數高校學生的知識范圍了。在這個時候,就要祭出張賢達的《矩陣分析》了。

最開始我把自己大學時候的數學書《數學分析》,《高等代數》,《數值計算》都翻了一遍,但是都沒有找到相關的內容。感覺對於矩陣的微分是一個“三不管”的地帶,但是這個內容又是深度學習神經網絡中用得最多的數學原理。然后到網上發現了《矩陣分析與應用》,想想這么厚的一本像百科全書的書,應該是無所不包吧,果然在里面找到了想要的內容。

當然在看書之前,也看了無數的網絡文章,相對比較有價值的就下面兩篇:

《矩陣求導-上》

《矩陣求導-下》

 

當然,像數學工具這種內容,建議大家還是去看書,因為書作為幾十年的經典教材,其推導過程,內容的完整性,認證的嚴密性都是經得起推敲的。網絡文章只能幫大家啟蒙一下,學幾個術語,但是具體想深入了解細節,建議還是看書。

 

言歸正傳。

 

上述的不到10行的反向傳播梯度,更新參數的代碼,在外行人看來是比較神來之筆,完全摸不着頭腦,這是很正常的。因為要理解上述的代碼,需要預先儲備如下知識(《矩陣分析與應用》):

  1. 矩陣的基本運算。頁面P4,章節編號1.1.2
  2. 矩陣的內積與范數。P32, 1.4.3
  3. 矩陣的跡。P49, 1.6.4
  4. 向量化和矩陣化。 P74, 1.11
  5. Jacobian矩陣和梯度矩陣。 P143, 3.1
  6. 一階實矩陣微分與Jacobian矩陣辨識。 P152, 3.2

 

注意事項:函數有不同的分類,所以請大家不要全用《線性代數》里面變元全為實數標量的眼光來看待矩陣的變元和矩陣函數的運算。因為它們是不同的,即使你勉強得到符合代碼的結論,那很可能也是“瞎貓碰到死耗子”。關於函數的微分的討論,光實值函數的分類,就可以分如下幾類(P143, 3.1):

矩陣和Jacobian矩陣在實值區間內是互為轉置。在進行數學推導時,都是先根據Jacobian矩陣的辨識方法求出Jacobian矩陣,然后轉置后就是相應的梯度。

 

當定義一個標量函數關於變量的偏導數時:

Jacobian矩陣和梯度矩陣是關於偏導的不同定義方式,分別是行向量偏導和列向量偏導。只是Jacobian矩陣是一種研究思維上更自然的選擇,但是梯度向量卻是優化和實際工程計算時更自然的選擇。

 

5.3 預測值梯度

 

grad_y_pred = 2.0 * (y_pred - y)

 

下面是推導過程,紅色筆跡是推導過程的依據,請查閱《矩陣分析與應用》

接着前面的公式,繼續求微分:

 

5.4 參數W2梯度

grad_w2 = h_relu.T.dot(grad_y_pred)

5.5 參數H_relu 梯度

grad_h_relu = grad_y_pred.dot(w2.T)

 

5.6 Relu梯度

 

grad_h = grad_h_relu.copy()

grad_h[h < 0] = 0

grad_w1 = x.T.dot(grad_h)

  

 

5.7 參數W1梯度

 

然后后面就是使用梯度和學習率去批量更新參數,實現整個訓練過程了。

 

6 參考資料

《矩陣分析與應用》(第2版) 張賢達 著,清華大學出版社,2011-11,第2版

本文中所有的引用注解,頁面標識都來自於本書。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM