梯度下降優化算法綜述
翻譯來自塞巴斯蒂安·魯德文章,他是自然語言處理的博士,AYLIEN的一位研究科學家。他的博客關於機器學習、深度學習、NLP。
原文連接查看參考文獻:http://sebastianruder.com/optimizing-gradient-descent/(要VPN才能打開,貌似)
原文中的部分算法和公式沒有補全,後續有人評論再補
目錄:
梯度下降的各種變體
批量梯度下降(Batch gradient descent)
隨機梯度下降(Stochastic gradient descent)
小批量梯度下降(Mini-batch gradient descent)
面臨的挑戰
梯度下降的優化算法
Momentum法
Nesterov加速梯度法
Adagrad法
Adadelta法
RMSprop法
適應性動量估計法(Adam)
幾種算法的可視化
該選擇哪種優化器
對SGD進行平行或分佈式運算
Hogwild!
Downpour SGD
容忍延遲的SGD算法
TensorFlow
彈性平均梯度下降法(Elastic Averaging SGD)
優化SGD的其他手段
重排(Shuffling )和遞進學習(Curriculum Learning)
批量標準化(Batch normalization)
早停(Early Stopping)
梯度噪聲(Gradient noise)
結論
參考資料
梯度下降法,是當今最流行的優化(optimization)算法,亦是至今最常用的優化神經網絡的方法。與此同時,最新的深度學習程序庫都包含了各種優化梯度下降的算法。但它們的算法則不被公開,都作爲黑箱優化器被使用,這也就是爲什麼它們的優勢和劣勢往往難以被實際地解釋。
本文旨在讓你對不同的優化梯度下降法的算法有一個直觀認識,以幫助你使用這些算法。我們首先會考察梯度下降法的各種變體,然後會簡要地總結在訓練(神經網絡或是機器學習算法)的過程中可能遇到的挑戰。接着,我們將會討論一些最常見的優化算法,研究它們的解決這些挑戰的動機及推導出更新規律(update rules)的過程。我們還會簡要探討一下,在平行計算或是分佈式處理情況下優化梯度下降法的算法和架構。最後,我們會考慮一下其他有助於優化梯度下降法的策略。
梯度下降法的核心,是最小化目標函數 J(θ),其中θ是模型的參數,θ∈Rd。它的方法是,在每次迭代中,對每個變量,按照目標函數在該變量梯度的相反方向,更新對應的參數值。其中,學習率η決定了函數到達(局部)最小值的迭代次數。換句話說,我們在目標函數的超平面上,沿着斜率下降的方向前進,直到我們遇到了超平面構成的「谷底」。如果你不熟悉梯度下降法的話,你可以在這裏找到一個很好的關於優化神經網絡的介紹。
本文討論了三種梯度下降法的變體——它們的不同之處在於,一次性使用多少數據來計算目標函數的梯度。對於不同的數據量,我們需要在參數更新準確性和參數更新花費時間兩方面做出權衡。
Vanilla 梯度下降法,也就是大家所熟知的批量梯度下降法,在整個數據集上(求出罰函數 J(θ 並)對每個參數 θ 求目標函數 J(θ) 的偏導數:
在該方法中,每次更新我們都需要在整個數據集上求出所有的偏導數。因此批量梯度下降法的速度會比較慢,甚至對於較大的、內存無法容納的數據集,該方法都無法被使用。同時,梯度下降法不能以「在線」的形式更新我們的模型,也就是不能再運行中加入新的樣本進行運算。
批量梯度下降法的實現代碼,如下所示:
for i in range(nb_epochs):
params_grad = evaluate_gradient(loss_function, data, params)
params = params - learning_rate * params_grad
對於給定的迭代次數,我們首先基於輸入的罰函數 loss_function 對輸入的參數向量 params 計算梯度向量 params_grad。注意,最新的深度學習程序庫中,提供了自動求導的功能,能夠高效、快速地求給定函數對於特定參數的導數。如果你希望自己寫代碼求出梯度值,那麼「梯度檢查」會是一個不錯的注意。(你可以參考這裏,瞭解關於如何檢查梯度的相關建議。)
然後,我們對參數減去梯度值乘學習率的值,也就是在反梯度方向,更新我們參數。當目標函數 J(θ) 是一凸函數時,則批量梯度下降法必然會在全局最小值處收斂;否則,目標函數則可能會局部極小值處收斂。
相比批量梯度下降法,隨機梯度下降法的每次更新,是對數據集中的一個樣本(x,y)求出罰函數,然後對其求相應的偏導數:
因爲批量梯度下降法在每次更新前,會對相似的樣本求算梯度值,因而它在較大的數據集上的計算會有些冗餘(redundant)。而隨機梯度下降法通過每次更新僅對一個樣本求梯度,去除了這種冗餘的情況。因而,它的運行速度被大大加快,同時也能夠「在線」學習。
隨機梯度下降法更新值的方差很大,在頻繁的更新之下,它的目標函數有着如下圖所示的劇烈波動。
SGD 函數波動,來源:Wikipedia
相比批量梯度下降法的收斂會使目標函數落入一個局部極小值,SGD 收斂過程中的波動,會幫助目標函數跳入另一個可能的更小的極小值。另一方面,這最終會讓收斂到特定最小值的過程複雜化,因爲該方法可能持續的波動而不停止。但是,當我們慢慢降低學習率的時候,SGD 表現出了與批量梯度下降法相似的收斂過程,也就是說,對非凸函數和凸函數,必然會分別收斂到它們的極小值和最小值。
相比批量梯度下降法的代碼,在如下的代碼中,我們僅僅加入了一個循環,用以遍歷所有的訓練樣本並求出相應的梯度值。注意,如這裏所說,在每次迭代中,我們會打亂訓練數據集。
for i in range(nb_epochs):
np.random.shuffle(data)
for example in data:
params_grad = evaluate_gradient(loss_function, example, params)
params = params - learning_rate * params_grad
小批量梯度下降法集合了上述兩種方法的優勢,在每次更新中,對 n 個樣本構成的一批數據,計算罰函數 J(θ),並對相應的參數求導:
這種方法,(a) 降低了更新參數的方差(variance),使得收斂過程更爲穩定;(b) 能夠利用最新的深度學習程序庫中高度優化的矩陣運算器,能夠高效地求出每小批數據的梯度。通常一小批數據含有的樣本數量在 50 至 256 之間,但對於不同的用途也會有所變化。小批量梯度下降法,通常是我們訓練神經網絡的首選算法。同時,有時候我們也會使用隨機梯度下降法,來稱呼小批量梯度下降法(譯者注:在下文中,我們就用 SGD 代替隨機梯度下降法)。注意:在下文對於隨機梯度法優化的介紹中,爲方便起見,我們會省略式子中的參數 x(i:i+n),y(i:i+n)。
如下的代碼所示,我們不再對每個樣本進行循環,而是對每批帶有 50 個樣本的小批數據進行循環:
for i in range(nb_epochs):
np.random.shuffle(data)
for batch in get_batches(data, batch_size=50):
params_grad = evaluate_gradient(loss_function, batch, params)
params = params - learning_rate * params_grad
由於 Vanilla 小批量梯度下降法並不能保證良好地收斂,這給我們留下了如下待解決的挑戰:
選擇適當的學習率是一個難題。太小的學習率會導致較慢的收斂速度,而太大的學習率則會阻礙收斂,並會引起罰函數在最小值處震盪,甚至有可能導致結果發散;
我們可以設置一個關於學習率地列表,通過如退火的方法,在學習過程中調整學習率——按照一個預先定義的列表、或是當每次迭代中目標函數的變化小於一定閾值時來降低學習率。但這些列表或閾值,需要根據數據集地特性,被提前定義。
此外,我們對所有的參數都採用了相同的學習率。但如果我們的數據比較稀疏,同時特徵有着不同的出現頻率,那麼我們不希望以相同的學習率來更新這些變量,我們希望對較少出現的特徵有更大的學習率。
在對神經網絡最優化非凸的罰函數時,另一個通常面臨的挑戰,是如何避免目標函數被困在無數的局部最小值中,以導致的未完全優化的情況。Dauphin 及其他人 [19] 認爲,這個困難並不來自於局部最小值,而是來自於「鞍點」,也就是在一個方向上斜率是正的、在一個方向上斜率是負的點。這些鞍點通常由一些函數值相同的面環繞,它們在各個方向的梯度值都爲 0,所以 SGD 很難從這些鞍點中脫開。
在如下的討論中,我們將會列舉一些應對上述問題的算法,它們被廣泛應用於深度學習社區。同時,我們不會討論那些不能應用於高維數據集的方法,例如牛頓法等針對二階問題的方法。
SGD 很難在陡谷——一種在一個方向的彎曲程度遠大於其他方向的表面彎曲情況——中找到正確更新方向。而這種陡谷,經常在局部極值中出現。在這種情況下,如圖 2 所示,SGD 在陡谷的周圍震盪,向局部極值處緩慢地前進。
圖2
圖3
動量法 [2],如圖 3 所示,則幫助 SGD 在相關方向加速前進,並減少它的震盪。他通過修改公式中,在原有項前增加一個折損係數γ,來實現這樣的功能:
注意:在其他的一些算法實現中,公式中的符號也許有所不同。動量項 γ 往往被設置爲 0.9 或爲其他差不多的值。
從本質上說,動量法,就彷彿我們從高坡上推下一個球,小球在向下滾動的過程中積累了動量,在途中他變得越來越快(直到它達到了峯值速度,如果有空氣阻力的話,γ<1)。在我們的算法中,相同的事情發生在我們的參數更新上:動量項在梯度指向方向相同的方向逐漸增大,對梯度指向改變的方向逐漸減小。由此,我們得到了更快的收斂速度以及減弱的震盪。
但當一個小球從山谷上滾下的時候,盲目的沿着斜率方向前行,其效果並不令人滿意。我們需要有一個更「聰明」的小球,它能夠知道它再往哪裏前行,並在知道斜率再度上升的時候減速。
Nesterov 加速梯度法(NAG)是一種能給予梯度項上述「預測」功能的方法。我們知道,我們使用動量項γvt-1 來「移動」參數項θ。通過計算θ-γvt-1,我們能夠得到一個下次參數位置的近似值——也就是能告訴我們參數大致會變爲多少。那麼,通過基於未來參數的近似值而非當前的參數值計算相得應罰函數 J(θ-γvt-1) 並求偏導數,我們能讓優化器高效地「前進」並收斂:
在該情況下,我們依然設定動量係數γ 在 0.9 左右。如下圖 4 所示,動量法首先計算當前的梯度值(小藍色向量),然後在更新的積累向量(大藍色向量)方向前進一大步。但 NAG 法則首先(試探性地)在之前積累的梯度方向(棕色向量)前進一大步,再根據當前地情況修正,以得到最終的前進方向(綠色向量)。這種基於預測的更新方法,使我們避免過快地前進,並提高了算法地響應能力(responsiveness),大大改進了 RNN 在一些任務上的表現 [8]。
Nesterov Update 法,來源:G. Hinton's lecture 6c
參考這裏,以查看 Ilya Sutskever 在它博士論文中,對 NAG 機理的更爲詳盡的解釋 [9]。
因爲我們現在能根據我們罰函數的梯度值來調整我們的更新,並能相應地加速 SGD,我們也希望能夠對罰函數中的每個參數調整我們的更新值,基於它們的重要性以進行或大或小的更新。
Adagrad[3] 是一個基於梯度的優化算法,它的主要功能是:它對不同的參數調整學習率,具體而言,對低頻出現的參數進行大的更新,對高頻出現的參數進行小的更新。因此,他很適合於處理稀疏數據。Dean 等人 [14] 發現,Adagrad 法大大提升了 SGD 的魯棒性,並在谷歌使用它訓練大規模的神經網絡,其諸多功能包括識別 Youtube 視頻中的貓。此外,Pennington 等人 [5] 使用它訓練 GloVe 單詞向量映射(Word Embedding),在其中不頻繁出現的詞語需要比頻繁出現的更大的更新值。
在這之前,我們對於所有的參數使用相同的學習率進行更新。但 Adagrad 則不然,對不同的訓練迭代次數 t,adagrad 對每個參數都有一個不同的學習率。我們首先考察 adagrad 每個參數的的更新過程,然後我們再使之向量化。爲簡潔起見,我們記在迭代次數 t 下,對參數θi 求目標函數梯度的結果爲 gt,i:
那麼普通 SGD 的更新規則爲:
而 adagrad 將學習率η進行了修正,對迭代次數 t,基於每個參數之前計算的梯度值,將每個參數的學習率η按如下方式修正:
其中 是一個對角陣,其中對角線上的元素是從一開始到t 時刻目標函數對於參數 梯度的平方和。是一個平滑項,ε以避免分母爲 0 的情況,它的數量級通常在1xe-8。有趣的是,如果不開方的話,這個算法的表現會變得很糟。
因爲 在其對角線上,含有過去目標函數對於參數 梯度的平方和,我們可以利用一個元素對元素的向量乘法,將我們的表達式向量化:
Adagrad 主要優勢之一,是它不需要對每個學習率手工地調節。而大多數算法,只是簡單地使用一個相同地默認值如 0.1,來避免這樣地情況。
Adagrad 地主要劣勢,是他在分母上的項中積累了平方梯度和。因爲每次加入的項總是一個正值,所以累積的和將會隨着訓練過程而增大。因而,這會導致學習率不斷縮小,並最終變爲一個無限小值——此時,這個算法已經不能從數據中學到額外的信息。而下面的算法,則旨在解決這個問題。
Adadelta 法 [6] 是 Adagrad 法的一個延伸,它旨在解決它學習率不斷單調下降的問題。相比計算之前所有梯度值的平方和,Adadelta 法僅計算在一個大小爲ω的時間區間內梯度值的累積和。
但該方法並不會存儲之前 ω個梯度的平方值,而是將梯度值累積值按如下的方式遞歸地定義:它被定義爲關於過去梯度值的衰減均值(decade average),當前時間的梯度均值是基於過去梯度均值和當前梯度值平方的加權平均,其中γ是類似上述動量項的權值。
與動量項的設定類似,我們設定 爲以 0.9 左右的值。爲明確起見,我們將我們的 SGD 更新規則寫爲關於參數更新向量 的形式:
由此,我們剛剛在 Adagrad 法中推導的的參數更新規則的向量表示,變爲如下形式:
我們現在將其中的對角矩陣 用上述定義的基於過去梯度平方和的衰減均值 替換:
因爲分母表達式的形式與梯度值的方均根(root mean squared,RMS)形式類似,因而我們使用相應的簡寫來替換:
作者還注意到,在該更新中(在 SGD、動量法或者 Adagrad 也類似)的單位並不一致,也就是說,更新值的量綱與參數值的假設量綱並不一致。爲改進這個問題,他們定義了另外一種指數衰減的衰減均值,他是基於參數更新的平方而非梯度的平方來定義的:
因此,對該問題的方均根爲:
因爲 值未知,所以我們使用 時刻的方均根來近似。將前述規則中的學習率 替換爲,我們最終得到了 Adadelta 法的更新規則:
藉助 Adadelta 法,我們甚至不需要預設一個默認學習率,因爲它已經從我們的更新規則中被刪除了。
RMSprop 是由 Geoff Hinton 在他 Coursera 課程中提出的一種適應性學習率方法,至今仍未被公開發表。
RMSprop 法和 Adadelta 法幾乎同時被發展出來。他們 解決 Adagrad 激進的學習率縮減問題。實際上,RMSprop 和我們推導出的 Adadelta 法第一個更規則相同:
RMSprop 也將學習率除以了一個指數衰減的衰減均值。Hinton 建議設定 爲 0.9,對 而言,0.001 是一個較好的默認值。
適應性動量估計法(Adam)[15] 是另一種能對不同參數計算適應性學習率的方法。除了存儲類似 Adadelta 法或 RMSprop 中指數衰減的過去梯度平方均值 外,Adam 法也存儲像動量法中的指數衰減的過去梯度值均值 :
mt和vt 分別是梯度的一階矩(均值)和二階矩(表示不確定度的方差),這也就是該方法名字的來源。因爲當 mt和vt 一開始被初始化爲 0 向量時,Adam 的作者觀察到,該方法會有趨向 0 的偏差,尤其是在最初的幾步或是在衰減率很小(即 B1和B2 接近 1)的情況下。
他們使用偏差糾正係數,來修正一階矩和二階矩的偏差:
他們使用這些來更新參數,更新規則跟我們在 Adadelta 和 RMSprop 法中看到的一樣,服從 Adam 的更新規則:
作者認爲參數的默認值應設爲:B1=0.9
B2=0.999 , ε=10E-8 。他們的經驗表明,Adam 在實踐中表現很好,和其他適應性學習算法相比也比較不錯。
如下的兩個動畫(圖像版權:Alec Radford)給了我們關於特定優化算法在優化過程中行爲的直觀感受。你可以參見這裏,以獲取 Karpathy 對相同圖像的一些描述,及另關於一些相關算法的細緻討論。
在圖 5 中,我們可以看到,在罰函數的等高線圖中,優化器的位置隨時間的變化情況。注意到,Adagrad、 Adadelta 及 RMSprop 法幾乎立刻就找到了正確前進方向並以相似的速度很快收斂。而動量法和 NAG 法,則找錯了方向,如圖所示,讓小球沿着梯度下降的方向前進。但 NAG 法能夠很快改正它的方向向最小指出前進,因爲他能夠往前看並對前面的情況做出響應。
圖 6 展現了各算法在鞍點附近的表現。如上面所說,這對對於 SGD 法、動量法及 NAG 法制造了一個難題。他們很難打破」對稱性「帶來的壁壘,儘管最後兩者設法逃脫了鞍點。而 Adagrad 法、RMSprop 法及 Adadelta 法都能快速的沿着負斜率的方向前進。
圖5:SGD optimization on loss surface contours
圖6:SGD optimization on saddle point10−8
如我們所見,適應性學習率方法,也就是 Adagrad 法、Adadelta 法 、RMSprop 法及 Adam 法最適合處理上述情況,並有最好的收斂效果。
那麼,我們該如何選擇優化器呢?如果你的輸入數據較爲稀疏(sparse),那麼使用適應性學習率類型的算法會有助於你得到好的結果。此外,使用該方法的另一好處是,你在不調參、直接使用默認值的情況下,就能得到最好的結果。
總的來說,RMSprop 法是一種基於 Adagrad 法的拓展,他從根本上解決學習率驟縮的問題。Adadelta 法於 RMSprop 法大致相同,除了前者使用了。而 Adam 法,則基於 RMSprop 法添加了偏差修正項和動量項。在我們地討論範圍中,RMSprop、Adadelta 及 Adam 法都是非常相似地算法,在相似地情況下都能做的很好。Kingma 及其他人 [15] 展示了他們的偏差修正項幫助 Adam 法,在最優化過程快要結束、梯度變得越發稀疏的時候,表現略微優於 RMSprop 法。總的來說,Adam 也許是總體來說最好的選擇。
有趣的是,很多最新的論文,都直接使用了(不帶動量項的)Vanilla SGD 法,配合一個簡單的學習率(退火)列表。如論文所示,這些 SGD 最終都能幫助他們找到一個最小值,但會花費遠多於上述方法的時間。並且這些方法非常依賴於魯棒的初始化值及退火列表。因此,如果你非常在你的模型能快速收斂,或是你需要訓練一個深度或複雜模型,你可能需要選擇上述的適應性模型。
現如今,大規模數據集隨處可見、小型計算機集羣也易於獲得。因而,使用分佈式方法進一步加速 SGD 是一個慣常的選擇。
SGD 它本事是序列化的:通過一步一步的迭代,我們最終求到了最小值。運行它能夠得到不錯的收斂結果,但是特別是對於大規模的數據集,它的運行速度很慢。相比而言,異步 SGD 的運行速度相對較快,但在不同的工作機之間的關於非完全優化的溝通可能會導致較差的收斂結果。此外,我們能夠對 SGD 進行平行運算而不需要一個計算機集羣。下文討論了相關的算法或架構,它們或關於平行計算或者對其進行了分佈式優化。
Niu 等人提出了一種叫做 Hogwild! 的更新規則,它允許在平行 GPU 上進行 SGD 更新。處理器。這僅能在輸入數據集是稀疏的時起效,在每次更新過程中僅會修正一部分的參數值。他們展示了,在這種情況下,這個更新規則達到了最優化的收斂速度,因爲處理器不太會覆蓋有用的信息。
Downpour SGD 是一個異步的 SGD 法變體,它被 Dean 等人 [4] 用在了谷歌的 DistBelief 架構中(它是 TensorFlow 的前身)。他對訓練集地子集同步地運行模型的多個副本。這些模型將它們的更新值發送到參數服務器,服務器被分爲了許多臺主機。每一臺主機都負責存儲和上載模型的一部分參數。但是,副本之間卻沒有相互的通信——例如,共享權重值或者更新值——其參數面臨着發散的風險,會阻止收斂。
McMahan 和 Streeter [12] 改良了 AdaGrad 法使之能夠用於平行運算的場景。通過實現延遲容忍的算法,它不僅能能夠適應於過去的梯度,還能夠適應於更新的延遲。在實踐中,它的表現很好。
TensorFlow[13] 是谷歌最近開源的一個實現和部署大規模機器學習模型的架構。它基於他們之前對於使用 DistBelief 的經驗,並已在內部被部署在一系列的移動設備及大規模的分佈式系統上進行計算。爲了分佈式執行,一個計算圖被分爲了許多子圖給不同的設備,設備之間的通信使用了發送和接受節點對。2016 年 4 月 13 日更新:一個分佈式 TensorFlow 的版本已經被髮布。
張等人 [14] 提出了彈性平均梯度下降法(EASGD),他使不同工作機之間不同的 SGD 以一個「彈性力」連接,也就是一個儲存於參數服務器的中心變量。這允許局部變量比中心變量更大地波動,理論上允許了對參數空間更多的探索。他們的經驗表明,提高的探索能力有助於在尋找新的局部極值中提升(優化器的)表現。
最後,我們將討論一些其他手段,他們可以與前述的方法搭配使用,並能進一步提升 SGD 的效果。你可以參考 [22],以瞭解一些其他常用策略。
總體而言,我們希望避免訓練樣本以某種特定順序傳入到我們的學習模型中,因爲這會向我們的算法引入偏差。因此,在每次迭代後,對訓練數據集中的樣本進行重排(shuffling),會是一個不錯的注意。
另一方面,在某些情況下,我們會需要解決難度逐步提升的問題。那麼,按照一定的順序遍歷訓練樣本,會有助於改進學習效果及加快收斂速度。這種構建特定遍歷順序的方法,叫做遞進學習(Curriculum Learning)[16].
Zaremba 和 Sutskever [17] 僅使用了遞進學習法訓練 LSTMs 來學習簡單的項目,但結果表明,遞進學習法使用的混合策略的表現好於樸素策略——後者不斷地重排數據,反而增加了學習過程的難度。
我們通常設置我們參數初值的均值和方差分別爲 0 和單位值,以幫助模型進行學習。隨着學習過程的進行,每個參數被不同程度地更新,相應地,參數的正則化特徵也隨之失去了。因此,隨着訓練網絡的越來越深,訓練的速度會越來越慢,變化值也會被放大。
批量標準化 [18] 對每小批數據都重新進行標準化,並也會在操作中逆傳播(back-propgate)變化量。在模型中加入批量標準化後,我們能使用更高的學習率且不要那麼在意初始化參數。此外,批量正則化還可以看作是一種正則化手段,能夠減少(甚至去除)留出法的使用。
誠如 Geoff Hinton 所言:「Early stopping (is) beautiful free lunch(早停是美妙的免費午餐,又簡單效果又好)」(NIPS 2015 Tutorial Sildes, Slide 63)。在訓練過程中,你應該時刻關注模型在驗證集上的誤差情況,並且在改誤差沒有明顯改進的時候停止訓練。
Neelakentan 等人 [21] 在每次梯度的更新中,向其中加入一個服從合高斯分佈 N(0,σ^2) 的噪聲值:
並按照如下的方式修正方差:
他們指出,這種方式能夠提升神經網絡在不良初始化前提下的魯棒性,並能幫助訓練特別是深層、複雜的神經網絡。他們發現,加入噪聲項之後,模型更有可能發現並跳出在深度網絡中頻繁出現的局部最小值。
在本文中,我們首先分析了梯度下降法的三個變體,在其中小批量梯度下降法最受歡迎。接着,我們研究了常用的優化 SGD 的算法,包括:動量法、Nesterov accelerated gradient 法、Adagrad 法、Adadelta 法、RMSprop 法、Adam 法及其他優化異步 SGD 的算法。最終,我們討論了另外一些改進 SGD 的策略,包括樣本重排法(shuffling)、遞進學習(curriculum learning)、批量標準化(Batch Normali·zation)及早停(early stopping)等。