當前位置:
首頁 > 最新 > 基於Numpy實現神經網路:反向傳播

基於Numpy實現神經網路:反向傳播

來源:Andrew Trask

編譯:weakish

編者按:和DeepMind數據科學家、Udacity深度學習導師Andrew Trask一起,基於Numpy手寫神經網路,更深刻地理解反向傳播這一概念。

總結:基於可以嘗試和修改的玩具代碼,我能取得最好的學習效果。本教程基於一個非常簡單的玩具樣例(簡短的Python代碼實現)介紹反向傳播這一概念。

如果你對我的文章感興趣,歡迎在推特上關注 @iamtrask,也歡迎給我反饋。

直接給我代碼

其他語言: D、C++、CUDA

不過,上面的代碼有點過於凝練了……讓我們循序漸進。

一、微型玩具網路

基於反向傳播的神經網路嘗試通過輸入來預測輸出。

嘗試通過上表的輸入預測輸出。我們可以通過測量輸入值和輸出值的統計數據來解決這一問題。如果我們這麼干,我們會發現最左邊的輸入和輸出完全相關(perfectly correlated)。最簡單形式的反向傳播,就是通過類似的測量統計數據的方式來建立模型。

雙層神經網路

如你所見,「訓練後輸出:"下面列印出的結果說明這一網路是有效的!!!在我描述這個過程之前,我建議你嘗試探索以上代碼以獲得一些它如何工作的感性認識。以上代碼應該可以在Jupyter Notebook中直接運行(或者你也可以以腳本的形式運行,但我強烈建議使用notebook)。下面是探索代碼的一些提示:

比較第一次迭代和最後一次迭代後的l1

查看nonlin函數定義。該函數給出一個概率作為輸出。

查看迭代過程中l1_error的值是如何改變的。

仔細看看第36行。這段代碼的奧秘主要藏在此處。

查看第39行。網路中的一切是為這個操作準備的。

讓我們逐行討論代碼。

建議:在兩塊屏幕上打開本文,這樣你就可以一邊查看代碼,一邊閱讀文章了。我寫作下文的時候基本上就是這麼做的。

01行:導入numpy。numpy是一個線性代數庫。這是我們唯一的依賴。

04行:這是我們的「非線性」。非線性可以有多種選擇,這裡我們選用的是sigmoid. sigmoid函數將任何值映射到0到1之間的值。我們使用sigmoid將數字轉換為概率。sigmoid函數還有其他一些有利於訓練神經網路的性質。

05行:注意這個函數同時可以生成sigmoid的導數(當時)。sigmoid函數有一個非常棒的特性是它的輸出可以用來創建它的導數。如果sigmoid的輸出是變數的話,它的導數是,非常高效。

如果你不熟悉導數,只需把它想像成sigmoid函數在給定的點上的斜率(如上圖所示,不同點的斜率不同)。想要了解更多關於導數的知識,可以參考Khan Academy的導數教程。

10行:初始化輸入數據集為numpy矩陣。每行是一個「訓練樣本」。每列對應一個輸入節點。因此,我們有3個輸入節點和4個訓練樣本。

16行:初始化輸出數據集。這裡,我水平地生成了數據集(1行4列),以節省字元。是轉置函數。轉置之後,y矩陣有4行1列。和輸入一樣,每行是一個訓練樣本,每列(僅有一列)是一個輸出節點。所以,我們的網路有3個輸入和1個輸出。

20行:設置隨機數種子是一個很好的做法。數字仍然是隨機分布的,但它們在每次訓練中將以完全一致的方式隨機分布。這更便於觀察你的改動對網路的影響。

23行:這是神經網路的權重矩陣。它命名為,意味著它是「突觸(synapse)零」。由於我們的網路只有兩層(輸入和輸出),我們只需要一個權重矩陣就可以連接兩者。它的維度是(3, 1),因為我們有3個輸入和1個輸出。另一種看待它的方式是l0的尺寸是3,l1的尺寸是1. 我們需要將l0中的每個節點連接到l1中的每個節點,因而我們需要一個維度(3, 1)的矩陣。

同時注意,隨機初始化時的均值為零。權重初始化有不少理論。就目前而言,可以簡單地把這一做法(權重初始化均值為零)看成是最佳實踐。

另外需要注意的是,「神經網路」實際上就是這個矩陣。我們有神經網路層l0和l1,但它們其實是基於數據集創建的短暫值。我們並不保存它們。所有的學習所得都儲存在syn0矩陣中。

25行:網路訓練代碼從這裡開始。這是一個「迭代」訓練代碼的for循環,優化網路以擬合數據集。

28行:第一層網路l1直接就是數據。因此我們在這裡明確聲明這一點。還記得X包含4個訓練樣本(行)嗎?在這一實現中,我們將同時處理所有樣本。這被稱為「全batch」(full batch)訓練。因此,我們有4個不同的l0行,但是如有必要,我們可以將它看成一個單獨的訓練樣本。在這裡這些沒有區別。(如果需要,我們可以載入1000甚至10000行數據,而不用修改任何代碼)。

29行:這是我們的預測步驟。基本上,我們首先讓網路「嘗試」基於輸入預測輸出。我們接著研究它的表現,從而加以調整,讓它在下一個迭代中表現更好。

這一行包含兩小步。首先矩陣l0和syn0相乘。接著將輸出傳給sigmoid函數。算下維度:

矩陣乘法是有序的,滿足等式兩邊的維度必須一致。因此最終生成的矩陣的行數等於第一個矩陣的行數,列數等於第二個矩陣的列數。

由於我們載入了4個訓練樣本,我們最終對正確答案做了4次猜測,一個(4 x 1)矩陣。每個輸出對應與網路對給定輸入的猜測。也許讀到這裡你能很直觀地理解為什麼我們之前說如有必要可以載入任意數目的訓練樣本,因為矩陣乘法仍然可以工作。

32行:好了,l1根據每個輸入作出了「猜測」。我們可以通過從猜測(l1)中減去真實答案(y)來看看它的表現如何。只是一個由正數和負數組成的向量,反映了網路離正確還差多少。

36行:現在是關鍵時刻!這是整個模型的奧秘所在!這一行里發生了很多事情,所以讓我們進一步把它分成兩部分。

第一部分:導數

如果l1表示下圖中的三點,那麼上面的代碼生成了下圖曲線的斜率。注意,像x=2.0這樣很高的值(綠點)和像x=-1.0這樣很低的值(紫點)具有相對平緩的斜率。x=0(藍點)處的斜率最高。這起到了非常重要的作用。同時注意所有的導數都在0和1之間。

整個語句:誤差加權導數

「誤差加權導數」有數學上更精確的說法,但我覺得這個名字捕捉到了背後的直覺。是一個(4,1)矩陣。返回一個(4,1)矩陣。我們將它們逐元素相乘。這返回一個(4,1)矩陣l1_delta

當我們將「斜率」乘以錯誤時,我們降低高信度預測的錯誤。再看一遍sigmoid圖像!如果斜率實在很平緩(接近0),那麼這個網路或者具有一個非常高的值,或者具有一個非常低的值。這意味著網路十分自信。然而,如果網路猜測的值接近,那麼它不是那麼自信。我們更劇烈地更新那些缺乏信心的預測,同時傾向於通過乘以一個接近零的數字保留那些自信的預測。

行39:一切就緒,我們可以更新網路了!讓我們看下單個訓練樣本。

譯者註:上圖左側標註L1、L2分別應為L0、L1

對這個樣本而言,我們已經準備好更新權重了。讓我們更新最左邊的權重(9.5)。

對最左邊的權重而言,這將是。推測起來,這會非常輕微地增加9.5. 為什麼只增加一點點?好吧,預測已經相當自信了,而且預測的結果也基本正確。低誤差和低斜率意味著非常小的更新。考慮所有3個權重。這3個權重都將略微增加。

譯者註:上圖左側標註L1、L2分別應為L0、L1

然而,因為我們使用「全batch」配置,我們將對所有4個訓練樣本執行上述操作。因此,這個過程更像上圖所示。所以,第39行做了什麼?它為每個訓練樣本的每個權重計算權重更新,累加起來,然後更新權重,這些都是在一行之內完成的。探索矩陣乘法,你將看到它是如何做到的!

奧秘

所以,既然我們已經了解網路是如何更新的,讓我們回過頭來看一下訓練數據然後反思一下。當輸入和輸出都是1的時候,我們增加兩者之間的權重。當輸入是1、輸出是0時,我們減少兩者之間的權重。

因此,在上表的4個訓練樣本中,第一個輸入與輸出之間的權重將持續增加或保持不變,而其他兩個權重將發現自己在不同的樣本上一會兒增加,一會兒下降(因而無法取得進展)。這一現象導致我們的網路基於輸入和輸出之間的相關性進行學習。

二、稍微加大難度

考慮基於三個輸入欄預測輸出欄。妙在沒有一列和輸出是相關的。每列有50%的機會預測1,50%的機會預測0.

那麼,模式是什麼呢?看起來結果完全和第三列無關,這一列的值永遠是1. 然而,第1列和第2列組合起來看比較清楚。如果第1列和第2列有一列是1(但兩列不同為1!),那麼輸出是1. 這是我們的模式。

這被認為是一個「非線性」模式,因為輸入和輸出之間沒有直接的一一對應關係。相反,存在輸入的組合和輸出的一一對應關係,也就是第1列和第2列。

不管你信不信,圖像識別是一個類似的問題。如果有100張尺寸相同的煙斗和自行車的圖像,沒有任何單獨的像素位置和自行車或煙斗的存在性直接相關。從純統計學角度看,像素可能是隨機的。然而,特定的像素組合不是隨機的,也就是說,像素的組合形成了自行車或煙斗的圖像。

我們的策略

為了將我們的像素組合成和輸出具有一一對應關係的東西,我們需要增加一個網路層。我們的第一層網路將組合輸入,第二層網路以第一層網路的輸出作為輸入,並將其輸入映射到輸出。在我們跳到實現之前,先看下這個表格。

如果我們隨機初始化權重,我們將得到如上表所示的l1的隱藏狀態值。注意到沒有?隱藏權重的第二項(第二個隱藏節點)與輸出已經有隱約的相關性!它並不完美,但確實存在。信不信由你,這是神經網路訓練中很重要的一部分。(一個有爭議的觀點認為這是神經網路訓練的唯一方式。)下面的訓練將放大這一相關性。它將同時更新syn1和syn0,更新syn1以便將隱藏權重映射到輸出,更新syn0以便更好地基於輸入產生權重!

注意:這個增加更多網路層以建模關係的更多組合的領域稱為「深度學習」,得名自建模時採用的越來越深的網路層。

三層神經網路

建議:在兩塊屏幕上打開本文,這樣你就可以一邊查看代碼,一邊閱讀文章了。我寫作下文的時候基本上就是這麼做的。

所有的一切看起來應該很相似!這其實就是將2個之前的實現堆疊在一起。l1的輸出是l2的輸入。唯一不同的是第43行。

行43:使用l2的「信度加權誤差」來確立l1的誤差。為了做到這一點,它簡單地將權重間的誤差從l2傳給l1. 你可以把它的結果叫作「貢獻加權誤差」,因為我們學習l1中的每個節點對l2中的誤差的「貢獻」有多少。這一步驟稱為「反向傳播」,演算法也是因此得名的。我們接著更新syn0,正如我們在雙層實現中所做的那樣。

三、結語和以後的工作

我的建議

如果你對神經網路的態度是嚴肅的,那我有一個建議。嘗試基於回憶重建這個網路。我知道這也許聽起來有點瘋狂,但它切切實實很有幫助。如果你希望能夠根據最新的學術論文創建任意的神經網路架構,或者閱讀和理解不同架構的代碼樣例,我認為這是一個殺手級練習。即使你使用Torch、Caffe、Theano之類的框架,我仍然認為這是有用的。我在進行這一練習之前和神經網路打過好多年交道,這個練習是我在這一領域所做的最好的投資(而且它花不了多久)。

以後的工作

這個玩具例子仍然需要一些掛件才能真正解決當前最先進的架構。如果你打算進一步改進你的網路,下面是一些值得了解的概念。(也許我以後的文章會涉及其中部分內容。)

Alpha

Bias Units(偏置單元)

Mini-Batches

Delta Trimming

Parameterized Layer Sizes(參數化的層尺寸)

Regularization(正則化)

Dropout

Momentum(動量)

Batch Normalization

GPU兼容性

其他酷炫特性

原文地址:https://iamtrask.github.io/2015/07/12/basic-python-network/


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 論智 的精彩文章:

簡單隨機搜索:強化學習的高效途徑
NLP選題:6大研究方向助你開啟科研之路

TAG:論智 |