前饋完全連接的神經網絡-ANNT

本文是關於ANNT庫,它提供瞭一些常見的神經網絡體系結構的實現,並將它們應用於不同的任務。

理論背景

由於主題並不新鮮,人工神經網絡理論,不同架構及其培訓有很多可用資源。在這裡,我們不會過多地介紹理論細節並對其進行簡要描述,並提供其他材料的鏈接,以更全面地介紹該主題。

現代人工神經網絡的許多想法都受其生物學版本的啟發。神經元或神經細胞通常是神經系統的核心組成部分,尤其是大腦。它是一種可電激勵的電池,通過電氣和化學信號接收,處理和傳輸信息。神經元之間的這些信號通過稱為突觸的專門連接發生。神經元可以相互連接形成神經回路。人類平均大腦有大約1000億個神經元,可能與其他10000個神經元相連,形成約1000萬億個突觸連接。

典型的神經元由細胞體(體細胞),樹突和軸突組成。樹突是由細胞體產生的薄結構,通常延伸數百微米並且分支多次。軸突是一種特殊的細胞延伸,起源於細胞體,在人體中移動一米甚至在其他物種中移動一段距離。大多數神經元通過樹突接收信號並沿軸突發出信號。因此,樹突可以被想象為神經元的輸入,同時軸突的輸出。

人工神經元

一個人工神經元是代表生物神經元模型的數學函數。人工神經元接收一個或多個輸入(表示神經樹突的潛力)並將它們相加以產生輸出(或激活,表示神經元沿其軸突傳遞的動作電位)。通常,每個輸入都是單獨加權的,並且總和通過稱為激活函數或傳遞函數的非線性函數。

將它放入數學方程式中,下一個公式描述瞭一個簡單的人工神經元:

其中x j值是神經元的輸入,w j值是輸入的權重,b是偏差值,m是輸入的數量。為瞭使事情更緊湊,可以用矢量符號重寫公式(這裡xw是輸入,權重表示為列向量):

第一個人工神經元是Warren McCulloch和Walter Pitts於1943年提出的閾值邏輯單元(TLU)。作為傳遞函數,它采用閾值函數。最初,隻考慮簡單模型的二進制輸入/輸出和可能的權重的一些限制。從一開始就已經註意到,任何佈爾函數都可以由這些設備的網絡實現,從可以實現AND和OR功能的事實中可以很容易地看出。

在20世紀80年代後期,當神經網絡的研究恢復強度時,開始考慮具有更多連續形狀的神經元。區分激活函數的可能性允許直接使用梯度下降和其他優化算法來調整權重和偏差值。

AND / OR示例

如上所述,單個神經元可以實現諸如OR,AND或NAND之類的功能。要實現這些功能,神經元的重量可以初始化為以下權重:

將這些權重和偏差值放入神經元方程並假設它使用閾值激活函數(1 > 對於u > = 0,否則為0)我們可以檢查神經元是否確實起作用。

我們可以用一個神經元做一些更復雜的事嗎?像XOR功能一樣,例如?不是。原因在於,當單個神經元用於分類問題時,它隻能用直線分離數據點。但是,XOR輸入不能線性分離。下圖顯示瞭所有三個功能的數據點:OR,AND和XOR。對於OR和AND數據點,可以繪制一條將它們分成類的直線,而對於XOR數據點則不能這樣做。

事實上,上面的分離線是從重量和偏差值獲得的。對於OR函數,我們使用瞭b = -0.5,w 1 = 1和w 2 = 1。給出下一個總和:1 * x 1 + 1 * x 2 – 0.5。將其轉換為線性方程,我們得到:x 2 = 0.5 – x 1 – 用於分隔OR數據點的線。

是否可以用多個神經元實現XOR功能?當然。請記住,XOR可以使用OR,AND和NAND功能實現:XOR(x 1x 2)= AND(OR(x 1x 2),NAND(x 1x 2))。這意味著加入2層網絡的3個神經元將完成這項工作。

人工神經網絡

由於單個神經元無法完成,因此將它們連接到網絡中 – 人工神經網絡。每個網絡包含多個層,而這些層又包含多個神經元。人工神經網絡有許多不同的架構,它們在神經元如何在層之間連接以及輸入信號如何通過網絡傳遞的方式上有所不同。在本文中,我們將從最簡單的架構開始 – 前饋完全連接的網絡。

在這種類型的人工神經網絡中,下一層的每個神經元都連接到前一層的所有神經元(而沒有其他神經元),而第一層中的每個神經元都連接到所有輸入。信號僅在這些網絡中從一個方向傳播 – 從輸入到輸出。這種類型的網絡可以在不同的分類和回歸任務中很好地完成。

註意:將網絡輸入表示為輸入層,最後一層表示為輸出層,所有其他層表示為隱藏層是很常見的。由於輸入層更多地是命名約定,並且它實際上並不代表網絡本身中的實體,因此當我們談論網絡中的層數時,它不會被計為整個文章中的層。因此,如果我們說我們有一個3層網絡,我們假設我們有一個帶有2個隱藏層和一個輸出層的網絡。

為瞭提供前饋全連接網絡的數學模型,讓我們就一些變量命名和結構達成一致:

  • l – 網絡中的層數;
  • Ñ (K) -在神經元的數目 ķ 層;
  • n (0) – 進入網絡的輸入數量;
  • 米(K) -的輸入數到 ķ 層(註:米(K) = Ñ (K-1));
  • Ý (K) -的輸出列向量 ķ 層,的長度 Ñ (K) ;
  • y (0) – 輸入網絡的列向量(向量 x);
  • b (k)的 -為對偏差值的列向量 ķ 層,的長度 Ñ (K) ;
  • w ^ (k)的 -權重為的矩陣 ķ 層。 i行矩陣包含該層的第 i 神經元的權重。這意味著矩陣的大小是 n (k)乘以 m (k)

通過上述所有定義,可以使用下面的簡單公式計算前饋完全連接網絡的輸出(假設計算順序從第一層到最後一層):

或者,為瞭使其緊湊,這裡的矢量符號是相同的:

這基本上都是關於前饋全連接網絡的數學!或者非常接近它。問題是:這些公式隻能做什麼?小。除非我們為我們想要解決的問題正確地初始化權重和偏差值,否則使用上述公式實現的人工神經網絡是無用的。對於上面的簡單OR / AND函數,我們已經手工制作瞭可以完成工作的權重/偏差。但是對於比這更復雜的事情來說,這些價值並不是一個微不足道的過程。這是學習算法發揮作用的地方。

激活功能

要完成神經網絡推理所需的數學運算,最初的人工神經元模型使用閾值函數來計算輸入加權和的輸出。盡管很簡單,但閾值函數有很多缺點。主要的一個是它的衍生物,它沒有在x = 0處定義,而在其他任何地方它都等於零。正如我們將進一步看到的,用於神經網絡訓練的梯度下降算法要求激活函數是可微分的並且在寬范圍的輸入值上具有非零梯度。

最流行的激活函數之一是sigmoid function,

S形函數的形狀提醒階梯函數(閾值)的形狀,但不是那麼尖銳。它是平滑的,可微分的,非二進制的,在(0,1)范圍內定義 – 似乎是一個很好的選擇。雖然它並不完美,但它也存在問題。然而,事實證明它適用於使用前饋完全連接網絡完成的不同分類任務,因此我們現在將堅持使其簡單化。

sigmoid

雙曲正切函數 – Tanh

雙曲正切函數 – Tanh

很少有其他流行的激活功能可供選擇:

  • 雙曲正切,與sigmoid函數的形狀類似,但在(-1,1)范圍內提供輸出。
  • Softmax函數,將任意實數值的矢量“壓縮”到實數值的相同維度向量,其中每個條目在(0,1)范圍內,並且所有條目加起來為1 – 適用於分類任務,其中神經元網絡的輸出可以被視為屬於某個類的概率。
  • 整流器,它是深度神經網絡架構中的流行激活功能,因為它允許更好的梯度傳播,因此具有更少的消失梯度問題。

為什麼我們需要激活功能呢?沒有它可以嗎?如果我們正在進行回歸任務,我們可以將其從網絡的輸出層中刪除,因此我們需要無界輸出。但我們無法將其從隱藏層中刪除。隱藏層中的激活功能增加瞭非線性,因此網絡可以學習非線性特征。這使我們能夠解決XOR問題等任務,例如,類不是線性可分的。從隱藏層移除激活功能將破壞學習非線性特征的能力,並且實際上將任何多層網絡轉變為單層網絡。是的,沒有激活功能的多個圖層可以隻用一個圖層替換,這將完成相同的工作。或者更好的說不會這樣做,因為在添加任何額外的層時沒有任何意義。

所以現在數學看起來完整的神經網絡推理 – 在訓練階段完成後計算網絡的新數據輸出,即當我們調整網絡的權重/偏差時。但是,我們沒有它們。我們需要找到一種訓練神經網絡的方法,因此它可以做一些有用的事情。

訓練人工神經網絡

對於訓練前饋全連接人工神經網絡,我們將使用監督學習算法。這意味著我們將擁有一個訓練數據集,它提供可能的輸入和目標輸出的樣本。學習算法的一個非常簡單的概念是,未經訓練的神經網絡(隨機初始化)被給予來自訓練數據集的樣本輸入,並且計算那些的相應輸出。然後將網絡產生的輸出與其需要產生的目標輸出進行比較,並計算一些誤差值。基於該誤差值,然後以減少該誤差的方式更新網絡的參數(權重和偏差),即使產生的和目標輸出之間的差異更小。一個計算輸出的周期,那麼錯誤值並最終更新網絡的參數稱為訓練時期。通常,訓練算法重復指定數量的歷元或直到誤差值變得足夠小。

成本函數

我們需要做的第一件事是定義錯誤函數,或者通常稱為成本函數。有許多流行的功能可供選擇,適合不同的任務。但是,為簡單起見,我們將從均方誤差(MSE)函數開始,這是回歸任務的常見選擇。假設我們有一個帶有m個樣本的訓練集,它們由輸入的x (j)向量和目標輸出的t (j)向量表示(即使大多數回歸任務假定單個輸出,我們會將其視為向量的向量)使數學一般)。對於每個可能的輸入,網絡計算相應的y (j)輸出矢量。現在,如果我們刪除上標,我們也可以使用yt來表示任意網絡的輸出及其相應的目標。假設網絡在其輸出層中具有n個神經元並且輸出向量中具有相同數量的元素,則可以如下定義單個訓練示例的MSE成本函數:

如果我們想要計算整個訓練數據集的成本函數值,那麼我們可以在所有可用樣本中對其進行平均:

註意:正如成本函數的名稱所示,它應該是平方誤差的平均值。邏輯上建議平方誤差的總和應除以n。然而,將它除以2 n並不會改變這個想法,而是在衍生品方面簡化瞭進一步的數學運算。

現在,當我們定義瞭成本函數時,我們可以得到一個數值,可用於判斷人工神經網絡在訓練數據集上的表現。在訓練神經網絡時,監視此值以查看它是否隨著時間的推移而改善是有用的,如果是這樣,有多快。

隨機梯度下降

定義成本函數後,我們現在可以進一步進入神經網絡訓練並更新其權重/偏差,因此它表現更好。我們所擁有的是經典優化問題 – 我們需要找到這樣的網絡參數,以便成本函數接近其最小值(局部最小值)。為此,我們可以使用梯度下降優化算法。該算法基於如下觀察:如果在a點鄰域中定義並且可微分多變量函數F(x),則如果從a的負梯度的方向上的a,則F(x)減小得最快。此時起作用,即-∇F (a)。因此,Gradient Descent算法的參數更新規則定義如下:

對於足夠小的參數λ值,F(a n + 1) <= F(a n)。通過對函數F的某些假設,可以保證收斂到局部最小值。

在訓練人工神經網絡的情況下,我們需要最小化我們所擁有的訓練集的成本函數。考慮到訓練集是固定的,輸入樣本和目標輸出可以被視為常數。因此,成本函數隻是網絡權重的函數(偏差值是特殊類型的權重,目前保持簡單),我們需要對其進行優化以最大限度地降低成本。從隨機初始化權重開始,使用下一個公式通過迭代更新權重來完成具有梯度下降算法的神經網絡的訓練過程:

λ參數被稱為學習率,並影響訓練神經網絡(接近當地最低成本函數的速度)的速度。參數的最佳值取決於神經網絡的架構,訓練設置等,因此根據經驗和實驗進行選擇。如果設置得太低,收斂到局部最小值可能會花費太長時間來訓練網絡。另一方面,如果設置得太高,則成本函數可能會振蕩甚至發散。

在進一步進入權重更新和計算成本函數的梯度之前,讓我們看看Gradient Descent算法的問題是什麼。很多時候,訓練集可能會變得非常大 – 數十到數十萬樣本甚至數百萬。計算整個集合的成本函數可能會非常昂貴,包括CPU / GPU和內存。另一種解決方案是使用隨機梯度下降(SGD)算法,隨機選取訓練樣本(或在訓練紀元開始時設置的隨機訓練集),僅針對該訓練樣本計算成本函數,然後基於該單個樣本進行參數更新。它為訓練集中的所有樣本重復此類更新迭代,但是以隨機順序。在實踐中,隨機梯度下降經常導致更快的訓練,因為模型在一個時期內多次獲得小改進,而不是單個參數的每個時期的更新具有真正的梯度下降。這是由於這樣的事實導致訓練集通常具有許多相似的樣本,這些樣本彼此之間的變化很小。因此,對某些樣本進行更新通常可以改善未來樣本的結果。

因此,根據SGD算法,我們的神經網絡權重更新規則僅基於單個隨機示例j

已經分析瞭隨機梯度下降的收斂性,並且觀察到當學習速率λ以適當的速率減小時,當目標函數是凸的時,SGD幾乎肯定地收斂到全局最小值,否則幾乎肯定地收斂到局部最小值。

迷你批量梯度下降(或僅批量梯度下降)是另一種替代算法 – 介於上述兩者之間。它類似於漸變下降,但不是計算參數在整個訓練集上的更新,而是在一批指定大小上進行。與隨機梯度下降類似,樣品隨機分配到每批(或預先混洗)。

雖然批量梯度下降是目前大多數應用的首選設置,但我們現在將堅持使用隨機梯度下降來簡化其餘的訓練算法。

鏈規則和梯度

現在是時候詳細說明神經網絡的權重更新規則瞭。現在讓我們看一下前饋完全連接神經網絡的最後一層的權重更新過程。我們假設最後一層有n個神經元/輸出,每個都有m個輸入; y i是第i 神經元的輸出,u i是其輸入的加權和(輸入到激活函數); t i是第i 神經元的目標輸出; x j是第j 輸入(來自前一層的相應神經元); w ^i,j j 輸入的 i 神經元的權重; b i是第 i 神經元的偏差值。根據SGD算法,每個權重 w i,j的更新是基於成本函數相對於該權重的偏導數,可以這樣寫:

要計算成本函數的偏導數,我們需要使用所謂的鏈規則。原因是成本函數不是網絡權重的簡單函數。相反,它是網絡輸出和目標輸出的函數,其中網絡的輸出是加權輸入和的函數,最後加權和可以表示為網絡權重的函數。例如,假設我們有一個函數f(x),其中x是另一個函數x(t),最後t也是函數t(a,b)。或者它可以寫成f(x(t(a,b)))。假設我們需要找到關於af的偏導數。使用鏈規則可以這樣做:

將相同的想法應用於成本函數的偏導數,我們可以得到下一個公式:

讓我們現在找到鏈的每個偏導數。雖然我們現在使用的MSE成本函數假設平方誤差的均值,但在計算其導數時更常見的是使用總和。考慮到這一點,成本函數相對於第i 神經元輸出的偏導數是這樣寫的:

因此,關於網絡輸出的MSE成本函數的偏導數隻是實際輸出和目標輸出之間的差異,可以將其視為預測誤差。在我們有更多輸出神經元的情況下,我們更好地計算每個神經元的這種誤差,而不管輸出層中神經元的數量。這就是為什麼通常省略除以n的原因。

下一步是計算激活函數相對於其輸入的導數。由於我們使用sigmoid激活函數,我們得到下一個衍生物:

註意,sigmoid函數的導數可以用兩種方式定義。第一個是基於函數的參數,即u i。然而,當涉及人工神經網絡時,沒有人真正這樣做。使用函數本身的值計算sigmoid的導數要快得多,考慮到它是在計算網絡輸出時計算的。

最後,我們可以定義神經元的加權和u i的偏導數,其權重,w i,j和偏差值,b i

綜上所述,我們得到瞭最後一層神經元的權重和偏差值的下一個更新規則:

上述公式可用於訓練僅具有單層的前饋完全連接的人工神經網絡。但是,大多數應用程序都需要多層網絡。這就是錯誤反向傳播算法到位的地方。

錯誤反向傳播

為瞭獲得隱藏圖層的權重更新規則,我們可以使用與以前相同的鏈規則技術。我們已經看到瞭如何在輸出層中找到關於神經元輸出的成本函數的偏導數。讓我們將其表示為E i – 輸出層中神經元的誤差項。

現在讓我們定義公式E” Ĵ -成本函數的偏導數相對於輸出Ĵ 在先前層(輸出層前的層)神經元。我們將再次使用鏈規則,但我們需要記住一個重要的事情。由於我們已經完全連接瞭人工神經網絡,因此前一層的每個輸出都連接到下一層中的每個神經元。哪個會反映在錯誤術語計算中。

現在讓我們做一些替換。首先讓我們將E i項拉入公式。然後讓我們記得,Ĵ 先前層,的輸出Y” Ĵ,可以表示為輸入到當前層,X Ĵ。然後我們可以用更通用的方式重寫上面的公式:

上述公式中的E i術語是故意保留的。如果我們進一步應用鏈規則來查找另一個隱藏層的錯誤項,我們將再次使用相同的公式。這意味著,一旦使用成本相對於網絡輸出的偏導數計算輸出層的誤差項,可以使用上面的公式從下一層的誤差項計算所有先前層的誤差項。

通過上述概括,我們現在可以記下前饋完全連接的人工神經網絡的所有層的權重更新規則。

上述算法稱為錯誤反向傳播。一旦計算出輸出層的誤差,就使用偏導數機制將其向後傳播通過神經網絡。因此,當談到人工神經網絡時,談論前向和後向傳球是很常見的。正向通道是計算網絡的輸出 – 信號從輸入流向輸出。向後傳遞是計算網絡參數的更新 – 錯誤值從輸出流向輸入。

請記住,如果我們使用MSE作為成本函數並使用sigmoid作為激活函數,則上述所有內容都是有效的。如果使用其他成本或激活函數,則上述公式將更改。但不是很多 – 隻有相應的偏導數項才會有所不同。

嗯,這就是現在的理論。顯然,關於前饋全連接人工神經網絡及其訓練還有很多話要說。但這應該足以引入,而提供的鏈接可以作為額外的信息來源。

ANNT庫

在實現ANNT庫的代碼時,目標是使其靈活且易於擴展和使用。因此,面向對象的范例是從最初的步驟中獲取的。在設計人工神經網絡的類層次結構時,決定將網絡層作為最小建模實體。通過這種方式,可以實現更好的性能(與某些實現方式下的單個神經元建模相反),並且可以靈活地從不同類型的層構建不同的神經網絡架構。

雖然理論部分表明激活函數是神經元的一部分,但它們的實現被分成特殊的激活層類。不同的成本函數也作為單獨的類實現,以便根據要解決的任務輕松選擇一個。由於這種粒度,在理論部分中顯示的權重更新規則將不會在代碼中找到。相反,每個類通過計算所需的誤差梯度來實現其自身的反向傳播算法部分。

例如,XMSECost該類僅計算y i – t i部分。然後XSigmoidActivation該類將y i(1-y i)部分添加到頂部。最後,XFullyConnectedLayer負責計算關於權重和誤差梯度的偏導數以傳遞到前一層。這樣就可以將不同的激活和成本函數插入到神經網絡的模型中,而無需對整個權重的更新算法進行硬編碼。

Gradient Descent更新規則也會移動到單獨的類中。如前所述,更新權重的公式看起來是針對算法的:w (t + 1)= w (t) – λ*Δw (t)。但是,它不是唯一可行的算法,而且通常不是提供更快速訓練的算法。例如,另一種流行的算法稱為具有動量的梯度下降,其具有如下更新規則:v (t) =μ* v (t-1) +λ*Δw (t) ; w (t + 1) = w (t) – v (t)。由於有許多不同種類的梯度下降算法,將這些作為單獨的類來實現是合乎邏輯的。

XNeuralNetwork類代表實際的神經網絡。網絡的體系結構實際上取決於放入其中的層的類型。在本文中,我們將僅看到前饋完全連接的ANN的示例。

最後,還有兩個額外的類。它XNetworkInference僅用於計算網絡輸出,這是我們在訓練神經網絡時所需要的。雖然該XNetworkTraining課程提供瞭必要的基礎設施來進行神經網絡的實際訓練。請註意,僅在訓練階段需要成本函數和參數的更新算法(優化器)。

另外需要註意的是,ANNT庫使用SIMD指令(SSE2和AVX)來矢量化計算,以及使用OpenMP來並行化計算。在運行時檢查對SIMD的支持,並使用可用的指令集。但是,如果由於某種原因需要禁用任何內容,則Config.hpp可以編輯該文件。

構建代碼

該代碼附帶MSVC(2015版)解決方案文件和GCC make文件。使用MSVC解決方案非常簡單 – 每個示例的解決方案文件都包含示例本身和庫的項目。所以MSVC選項就像打開所需示例的解決方案文件和點擊構建按鈕一樣簡單。如果使用GCC,則需要首先構建庫,然後通過運行make來構建所需的示例應用程序。

用法示例

為瞭演示ANNT庫如何在前饋完全連接的人工神經網絡的不同應用中使用,我們將探索代碼提供的5個示例。註意:這些示例都沒有聲稱所演示的神經網絡架構最適合其任務。實際上,這些例子都沒有說人工神經網絡是可行的方法。相反,他們唯一的目的是提供使用該庫的演示。

註意:下面的代碼片段隻是示例應用程序的一小部分。要查看示例的完整代碼,請參閱本文提供的源代碼包。

函數逼近

第一個演示的例子是函數逼近(回歸)。對於此任務,我們給出瞭一個數據集,其中包含某些函數的X / Y值,並將噪聲添加到Y值。然後,任務是訓練單輸入單輸出神經網絡,其將輸出給定輸入X的函數Y的近似值。例如,下面是此應用程序的兩個示例數據集。藍線表示基本功能,而橙色點表示噪聲添加到Y值的數據點。然後給出神經網絡有噪聲的X / Y.在訓練期間配對。訓練完成後,網絡將僅使用X值計算Y值,這樣我們就可以看到近似值的接近程度。

行數據集拋物線數據集

在線數據集的情況下,網絡可以像沒有激活功能的單個神經元一樣簡單。這稱為線性回歸。但是,在拋物線數據集的情況下,我們需要一個額外的隱藏層來應對非線性。可以使用下面的代碼創建一個簡單的2層神經網絡。

// prepare fully connected ANN with two layers
// the output layer is not followed by activation function,
// so that we have unbounded output
shared_ptr<XNeuralNetwork> net = make_shared<XNeuralNetwork>( );

net->AddLayer( make_shared<XFullyConnectedLayer>( 1, 10 ) ); // 1 input, 10 neurons
net->AddLayer( make_shared<XSigmoidActivation>( ) );
net->AddLayer( make_shared<XFullyConnectedLayer>( 10, 1 ) ); // 10 inputs, 1 neuron

赞(0)