本文經過一段來自於Pytorch官方的warm-up的例子:使用numpy來實現一個簡單的神經網絡。使用基本的數學原理,對其計算過程進行理論推導,以揭示這幾句神奇的代碼後面所包含的原理。html
估計對大多數的同窗來講,看完這個文章,確定會是這樣的感受:字都認識,可是就是不知道講的是啥~!不過對於有心人來講,本文確實能起到點睛之筆,就是你研究好久後,還差一點火候就頓悟了,但願本文可以幫你頓悟。python
關鍵字:Numpy,神經網絡,矩陣分析,反射傳播,梯度降低算法
若是發現圖片裂了,請左轉至 其它平臺查看:網絡
https://zhuanlan.zhihu.com/p/32368246框架
numpy做爲一個科學計算庫,並不包含:計算圖,嘗試學習,梯度等等功能,可是咱們能夠簡單的經過numpy去擬合一個二層的網絡。dom
解決的問題:函數
代碼以下:工具
# -*- 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學習
將上面的代碼結構及相應的參數維度繪圖後以下所示:優化
而後本代碼使用的是一個大小爲64的batch,因此輸入的值實際的大小其實是(64,1000)。
把以上的代碼轉化成數學公式以下,括號裏面是相應的矩陣的形狀:
數據流的正向傳播
最後計算出損失函數loss,是實際預測值和先驗數據矩陣的二範數,做爲兩組矩陣的距離測度。
正向傳播比較簡單,基本上大學的線性代數的基本知識看幾章,就能很好的理解。這也是後續若是在深度學習框架下面設計網絡的時候,注意設計神經元大小的時候,須要考慮矩陣乘法的可行性,即維度相容。
PS:關於矩陣的範數的定義,詳情見P32的《1.4.3矩陣的內積和範數》
下面是反射傳播的代碼實現:
關於反射傳播的數學原理,可能就不是那麼好理解了,由於這裏面須要用到矩陣的高級算法,通常的理工科數學的《線性代數》甚至《高等代數》裏面都沒有提到相關的內容,因此基本上已經超過了大多數高校學生的知識範圍了。在這個時候,就要祭出張賢達的《矩陣分析》了。
最開始我把本身大學時候的數學書《數學分析》,《高等代數》,《數值計算》都翻了一遍,可是都沒有找到相關的內容。感受對於矩陣的微分是一個「三無論」的地帶,可是這個內容又是深度學習神經網絡中用得最多的數學原理。而後到網上發現了《矩陣分析與應用》,想一想這麼厚的一本像百科全書的書,應該是無所不包吧,果真在裏面找到了想要的內容。
固然在看書以前,也看了無數的網絡文章,相對比較有價值的就下面兩篇:
《矩陣求導-上》https://zhuanlan.zhihu.com/p/24709748
《矩陣求導-下》https://zhuanlan.zhihu.com/p/24863977
固然,像數學工具這種內容,建議你們仍是去看書,由於書做爲幾十年的經典教材,其推導過程,內容的完整性,認證的嚴密性都是經得起推敲的。網絡文章只能幫你們啓蒙一下,學幾個術語,可是具體想深刻了解細節,建議仍是看書。
言歸正傳。
上述的不到10行的反向傳播梯度,更新參數的代碼,在外行人看來是比較神來之筆,徹底摸不着頭腦,這是很正常的。由於要理解上述的代碼,須要預先儲備以下知識(《矩陣分析與應用》):
注意事項:函數有不一樣的分類,因此請你們不要全用《線性代數》裏面變元全爲實數標量的眼光來看待矩陣的變元和矩陣函數的運算。由於它們是不一樣的,即便你勉強獲得符合代碼的結論,那極可能也是「瞎貓碰到死耗子」。關於函數的微分的討論,光實值函數的分類,就能夠分以下幾類(P143, 3.1):
矩陣和Jacobian矩陣在實值區間內是互爲轉置。在進行數學推導時,都是先根據Jacobian矩陣的辨識方法求出Jacobian矩陣,而後轉置後就是相應的梯度。
當定義一個標量函數關於變量的偏導數時:
Jacobian矩陣和梯度矩陣是關於偏導的不一樣定義方式,分別是行向量偏導和列向量偏導。只是Jacobian矩陣是一種研究思惟上更天然的選擇,可是梯度向量倒是優化和實際工程計算時更天然的選擇。
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)
而後後面就是使用梯度和學習率去批量更新參數,實現整個訓練過程了。
《矩陣分析與應用》(第2版) 張賢達 著,清華大學出版社,2011-11,第2版
本文中全部的引用註解,頁面標識都來自於本書。