一個框架看懂優化算法之異同 SGD/AdaGrad/Adam


Adam那么棒,為什么還對SGD念念不忘 (1) —— 一個框架看懂優化算法

 

機器學習界有一群煉丹師,他們每天的日常是:

 

拿來葯材(數據),架起八卦爐(模型),點着六味真火(優化算法),就搖着蒲扇等着丹葯出爐了。

 

不過,當過廚子的都知道,同樣的食材,同樣的菜譜,但火候不一樣了,這出來的口味可是千差萬別。火小了夾生,火大了易糊,火不勻則半生半糊。

 

機器學習也是一樣,模型優化算法的選擇直接關系到最終模型的性能。有時候效果不好,未必是特征的問題或者模型設計的問題,很可能就是優化算法的問題。

 

說到優化算法,入門級必從SGD學起,老司機則會告訴你更好的還有AdaGrad/AdaDelta,或者直接無腦用Adam。可是看看學術界的最新paper,卻發現一眾大神還在用着入門級的SGD,最多加個Moment或者Nesterov ,還經常會黑一下Adam。比如 UC Berkeley的一篇論文就在Conclusion中寫道:

 

Despite the fact that our experimental evidence demonstrates that adaptive methods are not advantageous for machine learning, the Adam algorithm remains incredibly popular. We are not sure exactly as to why ……

 

無奈與酸楚之情溢於言表。

 

這是為什么呢?難道平平淡淡才是真?


一個框架回顧優化算法

 

首先我們來回顧一下各類優化算法。

 

深度學習優化算法經歷了 SGD -> SGDM -> NAG ->AdaGrad -> AdaDelta -> Adam -> Nadam 這樣的發展歷程。Google一下就可以看到很多的教程文章,詳細告訴你這些算法是如何一步一步演變而來的。在這里,我們換一個思路,用一個框架來梳理所有的優化算法,做一個更加高屋建瓴的對比。

 

首先定義:待優化參數: [公式] ,目標函數: [公式] ,初始學習率 [公式]

而后,開始進行迭代優化。在每個epoch [公式] :

  1. 計算目標函數關於當前參數的梯度: [公式]
  2. 根據歷史梯度計算一階動量和二階動量:[公式]
  3. 計算當前時刻的下降梯度: [公式]
  4. 根據下降梯度進行更新: [公式]

掌握了這個框架,你可以輕輕松松設計自己的優化算法。

 

我們拿着這個框架,來照一照各種玄乎其玄的優化算法的真身。步驟3、4對於各個算法都是一致的,主要的差別就體現在1和2上。

 

SGD

先來看SGD。SGD沒有動量的概念,也就是說:

[公式]

代入步驟3,可以看到下降梯度就是最簡單的

[公式]

SGD最大的缺點是下降速度慢,而且可能會在溝壑的兩邊持續震盪,停留在一個局部最優點。

 

SGD with Momentum

為了抑制SGD的震盪,SGDM認為梯度下降過程可以加入慣性。下坡的時候,如果發現是陡坡,那就利用慣性跑的快一些。SGDM全稱是SGD with momentum,在SGD基礎上引入了一階動量:

[公式]

一階動量是各個時刻梯度方向的指數移動平均值,約等於最近 [公式] 個時刻的梯度向量和的平均值。

 

也就是說,t時刻的下降方向,不僅由當前點的梯度方向決定,而且由此前累積的下降方向決定。 [公式] 的經驗值為0.9,這就意味着下降方向主要是此前累積的下降方向,並略微偏向當前時刻的下降方向。想象高速公路上汽車轉彎,在高速向前的同時略微偏向,急轉彎可是要出事的。

 

SGD with Nesterov Acceleration

SGD 還有一個問題是困在局部最優的溝壑里面震盪。想象一下你走到一個盆地,四周都是略高的小山,你覺得沒有下坡的方向,那就只能待在這里了。可是如果你爬上高地,就會發現外面的世界還很廣闊。因此,我們不能停留在當前位置去觀察未來的方向,而要向前一步、多看一步、看遠一些。

 

NAG全稱Nesterov Accelerated Gradient,是在SGD、SGD-M的基礎上的進一步改進,改進點在於步驟1。我們知道在時刻t的主要下降方向是由累積動量決定的,自己的梯度方向說了也不算,那與其看當前梯度方向,不如先看看如果跟着累積動量走了一步,那個時候再怎么走。因此,NAG在步驟1,不計算當前位置的梯度方向,而是計算如果按照累積動量走了一步,那個時候的下降方向:

[公式]

然后用下一個點的梯度方向,與歷史累積動量相結合,計算步驟2中當前時刻的累積動量。

 

AdaGrad

此前我們都沒有用到二階動量。二階動量的出現,才意味着“自適應學習率”優化算法時代的到來。SGD及其變種以同樣的學習率更新每個參數,但深度神經網絡往往包含大量的參數,這些參數並不是總會用得到(想想大規模的embedding)。對於經常更新的參數,我們已經積累了大量關於它的知識,不希望被單個樣本影響太大,希望學習速率慢一些;對於偶爾更新的參數,我們了解的信息太少,希望能從每個偶然出現的樣本身上多學一些,即學習速率大一些。

 

怎么樣去度量歷史更新頻率呢?那就是二階動量——該維度上,迄今為止所有梯度值的平方和:

[公式]

我們再回顧一下步驟3中的下降梯度:

[公式]

可以看出,此時實質上的學習率由 [公式] 變成了 [公式] 。 一般為了避免分母為0,會在分母上加一個小的平滑項。因此[公式] 是恆大於0的,而且參數更新越頻繁,二階動量越大,學習率就越小。

 

這一方法在稀疏數據場景下表現非常好。但也存在一些問題:因為[公式] 是單調遞增的,會使得學習率單調遞減至0,可能會使得訓練過程提前結束,即便后續還有數據也無法學到必要的知識。

 

AdaDelta / RMSProp

 

由於AdaGrad單調遞減的學習率變化過於激進,我們考慮一個改變二階動量計算方法的策略:不累積全部歷史梯度,而只關注過去一段時間窗口的下降梯度。這也就是AdaDelta名稱中Delta的來歷。

 

修改的思路很簡單。前面我們講到,指數移動平均值大約就是過去一段時間的平均值,因此我們用這一方法來計算二階累積動量:

[公式]

這就避免了二階動量持續累積、導致訓練過程提前結束的問題了。

 

Adam

 

談到這里,Adam和Nadam的出現就很自然而然了——它們是前述方法的集大成者。我們看到,SGD-M在SGD基礎上增加了一階動量,AdaGrad和AdaDelta在SGD基礎上增加了二階動量。把一階動量和二階動量都用起來,就是Adam了——Adaptive + Momentum。

 

SGD的一階動量:

[公式]

加上AdaDelta的二階動量:

[公式]

 

優化算法里最常見的兩個超參數 [公式] 就都在這里了,前者控制一階動量,后者控制二階動量。

 

Nadam

 

最后是Nadam。我們說Adam是集大成者,但它居然遺漏了Nesterov,這還能忍?必須給它加上,按照NAG的步驟1:

[公式]

這就是Nesterov + Adam = Nadam了。

 

說到這里,大概可以理解為什么j經常有人說 Adam / Nadam 目前最主流、最好用的優化算法了。新手上路,先拿來一試,收斂速度嗖嗖滴,效果也是杠杠滴。

 

那為什么Adam還老招人黑,被學術界一頓鄙夷?難道只是為了發paper灌水嗎?

 

請繼續閱讀:

Adam那么棒,為什么還對SGD念念不忘 (2)—— Adam的兩宗罪

Adam那么棒,為什么還對SGD念念不忘 (3)—— 優化算法的選擇與使用策略

 

————————————————————

補充:指數移動平均值的偏差修正

 

前面我們講到,一階動量和二階動量都是按照指數移動平均值進行計算的:

[公式]

[公式]

實際使用過程中,參數的經驗值是

[公式]

初始化:

[公式]

這個時候我們看到,在初期, [公式] 都會接近於0,這個估計是有問題的。因此我們常常根據下式進行誤差修正:

[公式]

[公式]

 

————————————————————

行有所思,學有所得,陋鄙之言,請多指教。

歡迎關注我的微信公眾號 Julius-AI


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM