作者從神經網絡簡單的數學定義開始,沿著損失函數、激活函數和反向傳播等方法進一步描述基本的優化算法。在理解這些基礎后,本文詳細描述了動量法等當前十分流行的學習算法。此外,本系列將在后面介紹 Adam 和遺傳算法等其它重要的神經網絡訓練方法。
I. 簡介
本文是作者關于如何「訓練」神經網絡的一部分經驗與見解,處理神經網絡的基礎概念外,這篇文章還描述了梯度下降(GD)及其部分變體。此外,該系列文章將在在后面一部分介紹了當前比較流行的學習算法,例如:
-
動量隨機梯度下降法(SGD)
-
RMSprop 算法
-
Adam 算法(自適應矩估計)
-
遺傳算法
作者在第一部分以非常簡單的神經網絡介紹開始,簡單到僅僅足夠讓人理解我們所談論的概念。作者會解釋什么是損失函數,以及「訓練」神經網絡或者任何其他的機器學習模型到底意味著什么。作者的解釋并不是一個關于神經網絡全面而深度的介紹,事實上,作者希望我們讀者已經對這些相關的概念早已了然于心。如果讀者想更好地理解神經網絡具體是如何運行的,讀者可以閱讀《深度學習》等相關書籍,或參閱文末提供的相關學習資源列表。
本文作者以幾年前在 kaggle 上進行的貓狗鑒別競賽(https://www.kaggle.com/c/dogs-vs-cats)為例來解釋所有的東西。在這個比賽中我們面臨的任務是,給定一張圖片,判斷圖中的動物是貓還是狗。
II. 定義神經網絡
人工神經網絡(ANN)的產生受到了人腦工作機制的啟發。盡管這種模擬是很不嚴格的,但是 ANN 確實和它們生物意義上的創造者有幾個相似之處。它們由一定數量的神經元組成。所那么,我們來看一下一個單獨的神經元吧。
單個神經元
我們接下來要談論的神經元是一個與 Frank Rosenblatt 在 1957 年提出的最簡單的被稱作「感知機,perception」的神經元稍微有所不同的版本。我所做的所有修改都是為了簡化,因為我在這篇文章中不會涉及神經網絡的深入解釋。我僅僅試著給讀者給出一個關于神經網絡如何工作的直覺認識。
什么是神經元呢?它是一個數學函數,并以一定量的數值作為輸入(隨便你想要多少作為輸入),我在上圖畫出的神經元有兩個輸入。我們將每個輸入記為 x_k,這里 k 是輸入的索引。對于每一個輸入 x_k,神經元會給它分配另一個數 w_k,由這些參數 w_k 組成的向量叫做權重向量。正是這些權值才使得每個神經元都是獨一無二的。在測試的過程中,權值是不會變化的,但是在訓練的過程中,我們要去改變這些權值以「調節」我們的網絡。我會在后面的文章中討論這個內容。正如前面提到的,一個神經元就是一個數學函數。但是它是哪種函數呢?它是權值和輸入的一種線性組合,還有基于這種組合的某種非線性函數。我會繼續做進一步解釋。讓我們來看一下首先的線性組合部分。
輸入和權值的線性組合。
上面的公式就是我提到的線性組合。我們要將輸入和對應的權值相乘,然后對所有的結果求和。結果就會一個數字。最后一部分—就是給這個數字應用某種非線性函數。今天最常用的非線性函數即一種被稱作 ReLU(rectified linear unit) 的分段線性函數,其公式如下:
線性整流單元的表達式。
如果我們的數字大于 0,我們就會使用這個數字,如果它小于 0,我們就會用 0 去代替它。這個被用在線性神經元上的非線性函數被稱作激活函數。我們必須使用某種非線性函數的原因在后面會變得很明顯。總結一下,神經元使用固定數目的輸入和(標量),并輸出一個標量的激活值。前面畫出的神經元可以概括成一個公式,如下所示:
將我要寫的內容稍微提前一下,如果我們以貓狗鑒別的任務為例,我們會把圖片作為神經元的輸入。也許你會疑問:當神經元被定義為函數的時候,如何向它傳遞圖片。你應該記住,我們將圖片存儲在計算機中的方式是將它拿一個數組代表的,數組中的每一個數字代表一個像素的亮度。所以,將圖片傳遞到神經元的方式就是將 2 維(或者 3 維的彩色圖片)數組展開,得到一個一維數組,然后將這些數字傳遞到神經元。不幸的是,這會導致我們的神經網絡會依賴于輸入圖片的大小,我們只能處理由神經網絡定義的某個固定大小的圖片。現代神經網絡已經發現了解決這個問題的方法,但是我們在這里還是在這個限制下設計神經網絡。
現在我們定義一下神經網絡。神經網絡也是一個數學函數,它就是很多相互連接的神經元,這里的連接指的是一個神經元的輸出被用為另一個神經元的輸入。下圖是一個簡單的神經網絡,希望用這張圖能夠將這個定義解釋得更加清楚。
一個簡單的神經網絡。
上圖定義的神經網絡具有 5 個神經元。正如你所看到的,這個神經網絡由 3 個全連接層堆疊而成,即每一層的每個神經元都連接到了下一層的每一個神經元。你的神經網絡有多少層、每一層有多少個神經元、神經元之間是怎么鏈接的,這這些因素共同定義了一個神經網絡的架構。第一層叫做輸入層,包含兩個神經元。這一層的神經元并不是我之前所說的神經元,從某種意義而言,它并不執行任何計算。它們在這里僅僅代表神經網絡的輸入。而神經網絡對非線性的需求源于以下兩個事實:1)我們的神經元是連在一起的;2)基于線性函數的函數還是線性的。所以,如果不對每個神經元應用一個非線性函數,神經網絡也會是一個線性函數而已,那么它并不比單個神經元強大。最后一點需要強調的是:我們通常是想讓一個神經網絡的輸出大小在 0 到 1 之間,所以我們會將它按照概率對待。例如,在貓狗鑒別的例子中,我們可以把接近于 0 的輸出視為貓,將接近于 1 的輸出視為狗。為了完成這個目標,我們會在最后一個神經元上應用一個不同的激活函數。我們會使用 sigmoid 激活函數。關于這個激活函數,你目前只需要知道它地返回值是一個介于 0 到 1 的數字,這正好是我們想要的。解釋完這些之后,我們可以定義一個和上圖對應的神經網絡了。
定義一個神經網絡的函數。w 的上標代表神經元的索引,下標代表輸入的索引。
最后,我們得到了某種函數,它以幾個數作為輸入,輸出另一個介于 0 到 1 之間的數。實際上,這個函數怎樣表達并不重要,重要的是我們通過一些權重將一個非線性函數參數化了,我們可以通過改變這些權重來改變這個非線性函數。
III. 損失函數
在開始討論神經網絡的訓練之前,最后一個需要定義的就是損失函數了。損失函數是一個可以告訴我們,神經網絡在某個特定的任務上表現有多好的函數。做這件事的最直覺的辦法就是,對每一個訓練樣本,都沿著神經網絡傳遞得到一個數字,然后將這個數字與我們想要得到的實際數字做差再求平方,這樣計算出來的就是預測值與真實值之間的距離,而訓練神經網絡就是希望將這個距離或損失函數減小。
上式中的 y 代表我們想要從神經網絡得到的數字,y hat 指的一個樣本通過神經網絡得到的實際結果,i 是我們的訓練樣本的索引。我們還是以貓狗鑒別為例。我們有一個數據集,由貓和狗的圖片組成,如果圖片是狗,對應的標簽是 1,如果圖片是貓,對應的標簽是 0。這個標簽就是對應的 y,在向神經網絡傳遞一張圖片的時候我們想通過神經網絡的得到的結果。為了計算損失函數,我們必須遍歷數據集中的每一張圖片,為每一個樣本計算 y,然后按照上面的定義計算損失函數。如果損失函數比較大,那么說明我們的神經網絡性能并不是很好,我們想要損失函數盡可能的小。為了更深入地了解損失函數和神經網絡之間的聯系,我們可以重寫這個公式,將 y 換成網絡的實際函數。
IV. 訓練
在開始訓練神經網絡的時候,要對權值進行隨機初始化。顯然,初始化的參數并不會得到很好的結果。在訓練的過程中,我們想以一個很糟糕的神經網絡開始,得到一個具有高準確率的網絡。此外,我們還希望在訓練結束的時候,損失函數的函數值變得特別小。提升網絡是有可能的,因為我們可以通過調節權值去改變函數。我們希望找到一個比初始化的模型性能好很多的函數。
問題在于,訓練的過程相當于最小化損失函數。為什么是最小化損失而不是最大化呢?結果證明損失是比較容易優化的函數。
有很多用于函數優化的算法。這些算法可以是基于梯度的,也可以不是基于梯度的,因為它們既可以使用函數提供的信息,還可以使用函數梯度提供的信息。最簡單的基于梯度的算法之一叫做隨機梯度下降(SGD),這也是我在這篇文章中要介紹的算法。讓我們來看一下它是如何運行的吧。
首先,我們要記住關于某個變量的導數是什么。我們拿比較簡單的函數 f(x) = x 為例。如果還記得高中時候學過的微積分法則,我們就會知道,這個函數在每個 x 處的導數都是 1。那么導數能夠告訴我們哪些信息呢?導數描述的是:當我么讓自變量朝正方向變化無限小的步長時,函數值變化有多快的速率。它可以寫成下面的數學形式:
它的意思是:函數值的變化量(方程的左邊)近似等于函數在對應的某個變量 x 處的導數與 x 的增量的乘積。回到我們剛才所舉的最簡單的例子 f(x) = x,導數處處是 1,這意味著如果我們將 x 朝正方向變化一小步ε,函數輸出的變化等于 1 和ε的乘積,剛好是ε本身。檢查這個規則是比較容易的。實際上這個并不是近似值,它是精確的。為什么呢?因為我們的導數對于每一個 x 都是相同的。但是這并不適用于絕大多數函數。讓我們來看一個稍微復雜一點的函數 f(x) = x^2。
通過微積分知識我們可以知道,這個函數的導數是 2*x。現在如果我們從某個 x 開始移動某個步長的ε,很容易能夠發現對應的函數增量并不精確地等于上面的公式中的計算結果。
現在,梯度是由偏導數組成的向量,這個向量的元素是這個函數所依賴的某些變量對應的導數。對于我們目前所考慮的簡單函數來說,這個向量只有一個元素,因為我們所用的函數只有一個輸入。對于更加復雜的函數(例如我們的損失函數)而言,梯度會包含函數對應的每個變量的導數。
為了最小化某個損失函數,我們可以怎么使用這個由導數提供的信息呢?還是回到函數 f(x) = x^2。顯然,這個函數在 x=0 的點取得最小值,但是計算機如何知道呢?假設我們開始的時候得到的 x 的隨機初始值為 2,此時函數的導數等于 4。這意味著如果 x 朝著正方向改變,函數的增量會是 x 增量的 4 倍,因此函數值反而會增加。相反,我們希望最小化我們的函數,所以我們可以朝著相反的方向改變 x,也就是負方向,為了確保函數值降低,我們只改變一小步。但是我們一步可以改變多大呢? 我們的導數只保證當 x 朝負方向改變無限小的時候函數值才會減小。因此,我們希望用一些超參數來控制一次能夠改變多大。這些超參數叫做學習率,我們后面會談到。我們現在看一下,如果我們從-2 這個點開始,會發生什么。這里的導數是-4,這意味著如果朝著正方向改變 x,函數值會變小,這正是我們想要的結果。
注意到這里的規律了嗎?當 x>0 的時候,我們導數值也大于 0,我們需要朝著負方向改變,當 x<0 的時候,我們導數值小于 0,我們需要朝著正方向改變,我們總需要朝著導數的反方向改變 x。讓我們對梯度也引用同樣的思路。梯度是指向空間某個方向的向量,實際上它指向的是函數值增加最劇烈的方向。由于我們要最小化我們的函數,所以我們會朝著與梯度相反的方向改變自變量。現在在我們應用這個思想。在神經網絡中,我們將輸入 x 和輸出 y 視為固定的數。我們要對其求導數的變量是權值 w,因為我們可以通過改變這些權值類提升神經網絡。如果我們對損失函數計算權值對應的梯度,然后朝著與梯度相反的方向改變權值,我們的損失函數也會隨之減小,直至收斂到某一個局部極小值。這個算法就叫做梯度下降。在每一次迭代中更新權重的算法如下所示:
每一個權重值都要減去它對應的導數和學習率的乘積。
上式中的 Lr 代表的是學習率,它就是控制每次迭代中步長大小的變量。這是我們在訓練神經網絡的時候要調節的重要超參數。如果我么選擇的學習率太大,會導致步進太大,以至于跳過最小值,這意味著你的算法會發散。如果你選擇的學習率太小,收斂到一個局部極小值可能會花費太多時間。人們開發出了一些很好的技術來尋找一個最佳的學習率,然而這個內容超出本文所涉及的范圍了。
不幸的是,我們不能應用這個算法來訓練神經網絡,原因在于損失函數的公式。
正如你可以在我之前的定義中看到的一樣,我們損失函數的公式是和的平均值。從微積分原理中我們可以知道,微分的和就是和的微分。所以,為了計算損失函數的梯度,我們需要遍歷我們的數據集中的每一個樣本。在每一次迭代中進行梯度下降是非常低效的,因為算法的每次迭代僅僅以很小的步進提升了損失函數。為了解決這個問題,還有另外一個小批量梯度下降算法。該算法更新權值的方法是不變的,但是我們不會去計算精確的梯度。相反,我們會在數據集的一個小批量上近似計算梯度,然后使用這個梯度去更新權值。Mini-batch 并不能保證朝著最佳的方向改變權值。事實上,它通常都不會。在使用梯度下降算法的時候,如果所選擇的學習率足夠小的話,能夠保證你的損失函數在每一次迭代中都會減小。但是使用 Mini-batch 的時候并不是這樣。你的損失函數會隨著時間減小,但是它會有波動,也會具有更多的「噪聲」。
用來估計梯度的 batch 大小是你必須選擇的另一個超參數。通常,我們希望盡可能地選擇能處理的較大 batch。但是我很少見到別人使用比 100 還大的 batch size。
mini-batch 梯度下降的極端情況就是 batch size 等于 1,這種形式的梯度下降叫做隨機梯度下降(SGD)。通常在很多文獻中,當人們說隨機梯度下降的時候,實際上他們指的就是 mini-batch 隨機梯度下降。大多數深度學習框架都會讓你選擇隨機梯度下降的 batch size。
以上是梯度下降和它變體的基本概念。但近來越來越多的人在使用更高級的算法,其中大多數都是基于梯度的,作者下一部分就主要介紹這些最優化方法。
VII. 反向傳播(BP)
關于基于梯度的算法,剩下的唯一一件事就是如何計算梯度了。最快速的方法就是解析地給出每一個神經元架構的導數。我想,當梯度遇到神經網絡的時候,我不應該說這是一個瘋狂的想法。我們在前面定義的一個很簡單的神經網絡就已經相當艱難了,而它只有區區 6 個參數。而現代神經網絡的參數動輒就是數百萬。
第二種方法就是使用我們從微積分中學到的下面的這個公式去近似計算梯度,事實上這也是最簡單的方法。
盡管這個方法是非常容易實現的,但是它卻是非常耗計算資源的。
最后一種計算梯度的方法,是對解析難度和計算成本的折中,這個方法被稱作反向傳小節。反向傳播不在本文的討論范圍,如果你想了解更多的話,可以查看 Goodfellow《深度學習》第六章第五小節,該章節對反向傳播算法有非常詳盡的介紹。
VI. 它為什么會起作用?
當我初次了解神經網絡以及它是如何工作的時候,我理解所有的方程,但是我不是十分確定它們為啥會起作用。這個想法對我而言有些怪誕:用幾個函數,求一些導數,最終會得到一個能夠認出圖片中是貓還是狗。為什么我不能給你們一個很好的關于為啥神經網絡會如此好的奏效的直覺知識呢?請注意以下兩個方面。
1. 我們想要用神經網絡解決的問題必須被以數學的形式表達出來。例如,對于對于貓狗鑒別:我們需要找到一個函數,它能夠把一副圖片中的所有像素作為輸入,然后輸出圖片中的內容是狗的概率。你可以用這種方法去定義任何一個分類問題。
2. 或許并不是很清楚,為什么會有一個能夠從一副圖片中把貓和狗區分開來的函數。這里的思想是:只要你有一些具有輸入和標簽的數據集,總會存在一個能夠在一個給定數據集上性能良好的函數。問題在于這個函數會相當復雜。這時候神經網絡就能夠有所幫助了。有一個「泛逼近性原理,universal approximation theorem」,指的是具有一個隱藏層的神經網絡可以近似任何一個你想要的的函數,你想要它近似得多好,就能有多好。
動量隨機梯度下降算法
-這是關于訓練神經網絡和機器學習模型優化算法系列的第二部分,第一部分是關于隨機梯度下降的。在這一部分,假定讀者對神經網絡和梯度下降算法已有基本了解。如果讀者對神經網絡一無所知,或者不知道神經網絡是如何訓練的,可以在閱讀這部分之前閱讀第一部分。
在本節中,除了經典的 SGD 算法外,我們還會對動量法進行討論,這種算法一般比隨機梯度下降算法更好更快。動量法 [1] 或具有動量的 SGD 是一種方法,這種方法有助于加速向量向著正確的方向梯度下降,從而使其收斂速度更快。這是最流行的優化算法之一,許多各方向上最先進的模型都是用這種方法進行訓練的。在講高級的算法相關方程之前,我們先來看一些有關動量的基礎數學知識。
指數加權平均
指數加權平均用于處理數字序列。假設我們有一些嘈雜的序列 S。在這個例子中,我繪制了余弦函數并添加了一些高斯噪聲。如下圖所示:
注意,盡管這些點看起來非常接近,但它們的 x 坐標是不同的。也就是說,對每個點而言,其 x 坐標是唯一的標識,因此這也是定義序列 S 中每個點的索引。
我們需要處理這些數據,而非直接使用它們。我們需要某種「移動」的平均值,這個平均值會使數據「去噪」從而使其更接近原始函數。指數加權平均值可以產生如下所示的圖片:
動量——來自指數加權平均的數據
如我們所見,這是一個相當不錯的結果。與噪聲很大的數據相比,我們得到了更平滑的曲線,這意味著與初始數據相比,我們得到了與原始函數更接近的結果。指數加權平均值用下面的公式定義了新的序列 V:
序列 V 是上面的散點圖中的黃色部分。Beta 是取值為 0 到 1 的另一個超參數。在上述例子中,取 Beta = 0.9。0.9 是一個很好的值,經常用于具有動量的 SGD 方法。我們可以這樣對 Beta 進行直觀理解:我們對序列后面的 1 /(1- beta)的點進行近似平均。讓我們看看 beta 的選擇會對新序列 V 產生怎樣的影響。
Beta 取值不同時的指數加權平均結果。
如我們所見,Beta 取值越小,序列 V 波動越大。因為我們平均的例子更少,因此結果與噪聲數據更「接近」。隨著 Beta 值越大,比如當 Beta = 0.98 時,我們得到的曲線會更加圓滑,但是該曲線有點向右偏移,因為我們取平均值的范圍變得更大(beta = 0.98 時取值約為 50)。Beta = 0.9 時,在這兩個極端間取得了很好的平衡。
數學部分
這個部分對你在項目中使用動量而言不是必要的,所以可以跳過。但這部分更直觀地解釋了動量是如何工作的。
讓我們對指數加權平均新序列 V 的三個連續元素的定義進行擴展。
V——新序列。S——原始序列。
將其進行合并,我們可以得到:
再對其進行簡化,可得:
從這個等式中可以看出,新序列的第 T 個值取決于原始序列 S 的所有先前的數值 1…t。來自 S 的所有數值被賦了一定的權重。這個權重是序列 S 的第(t-i)個值乘以(1- beta)得到的權重。因為 Beta 小于 1,所以當我們對某個正數的冪取 beta 時,值會變得更小。所以序列 S 的原始值的權重會小得多,也因此序列 S 對序列 V 產生的點積影響較小。從某些角度來說,該權重小到我們幾乎可以說我們「忘記」了這個值,因為其影響小到幾乎無法注意到。使用這個近似值的好處在于當權重小于 1 / e 時,更大的 beta 值會要求更多小于 1 / e 的權值。這就是為什么 beta 值越大,我們就要對更多的點積進行平均。下面的圖表顯示的是與 threshold = 1 / e 相比,隨著序列 S 初始值變化,權重變小的速度,在此我們「忘記」了初始值。
最后要注意的是,第一次迭代得到的平均值會很差,因為我們沒有足夠的值進行平均。我們可以通過使用序列 V 的偏差修正版而不是直接使用序列 V 來解決這一問題。
式中 b = beta。當 t 值變大時,b 的 t 次冪與零無法進行區分,因此不會改變 V 值。但是當 t 取值較小時,這個方程會產生較好的結果。但是因為動量的存在使得機器學習過程穩定得很快,因此人們通常會懶得應用這一部分。
動量 SGD 法
我們已經定義了一種方法來獲得一些序列的「移動」平均值,該值會與數據一起變化。我們該如何將其應用于神經網絡的訓練中呢?它可以平均我們的梯度。我將在下文中解釋它是如何在動量中完成的這一工作,并將繼續解釋為什么它可能會得到更好的效果。
我將提供兩個定義來定義具有動量的 SGD 方法,這幾乎就是用兩種不同的方式表達同一個方程。首先,是吳恩達在 Coursera 深度學習專業化(https://www.deeplearning.ai/)的課程中提出的定義。他解釋的方式是,我們定義一個動量,這是我們梯度的移動平均值。然后我們用它來更新網絡的權重。如下所示:
式中 L 是損失函數,三角形符號是梯度 w.r.t 權重,α 是學習率。另一種最流行的表達動量更新規則的方式不那么直觀,只是省略了(1 - beta)項。
這與第一組方程式非常相似,唯一的區別是需要通過(1 - β)項來調整學習率。
Nesterov 加速漸變
Nesterov 動量是一個版本略有不同的動量更新,最近越來越受歡迎。在這個版本中,首先會得到一個當前動量指向的點,然后從這個點計算梯度。如下圖所示:
Nesterov 動量可用下式定義:
動量工作原理
在這里我會解釋為什么在絕大多數情況下動量法會比經典 SGD 法更好用。
使用隨機梯度下降的方法,我們不會計算損失函數的確切導數。相反,我們是對一小批數據進行估算的。這意味著我們并不總是朝著最佳的方向前進,因為我們得到的結果是「嘈雜的」。正如我在上文中列出的圖表。所以,指數的加權平均可以提供一個更好的估計值,該估計值比通過嘈雜計算得到的結果更接近實際值的導數。這就是動量法可能比傳統 SGD 更好的原因之一。
另一個原因在于溝谷(ravine)。溝谷是一個區域,在其中,曲線在一個維度比另一個維度陡得多。在深度學習中,溝谷區可近似視為局部最低點,而這一特性無法用 SGD 方法得到。SGD 傾向于在狹窄的溝谷上擺動,因為負梯度將沿著陡峭的一側下降,而非沿著溝谷向最優點前進。動量有助于加速梯度向正確的方向前進。如下圖所示:
左圖——沒有動量的 SGD,右圖——有動量的 SGD
結論
希望本節會提供一些關于具有動量的 SGD 方法是如何起作用以及為什么會有用的想法。實際上它是深度學習中最流行的優化算法之一,與更高級的算法相比,這種方法通常被人們更頻繁地使用。
-
神經網絡
+關注
關注
42文章
4771瀏覽量
100713 -
遺傳算法
+關注
關注
0文章
237瀏覽量
20610 -
ardm
+關注
關注
0文章
1瀏覽量
1958
原文標題:從零開始:教你如何訓練神經網絡
文章出處:【微信號:gh_ecbcc3b6eabf,微信公眾號:人工智能和機器人研究院】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論