數值優化:理解L-BFGS算法

譯自《Numerical Optimization: Understanding L-BFGS》,本來只想作爲學習CRF的補充材料,讀完後發現收穫很多,把許多以前零散的知識點都串起來了。對我而言,的確比零散地看論文要輕鬆得多。原文並沒有太多關注實現,對實現感興趣的話推薦原作者的golang實現梯度.png

數值優化是許多機器學習算法的核心。一旦你確定用什麼模型,並且準備好了數據集,剩下的工作就是訓練了。估計模型的參數(訓練模型)通常歸結爲最小化一個多元函數f(x).png,其中輸入CodeCogsEqn (3).png是一個高維向量,也就是模型參數。換句話說,如果你求解出:

CodeCogsEqn (1).png

那麼CodeCogsEqn (3).png*就是最佳的模型參數(當然跟你選擇了什麼目標函數有關係)。 

在這篇文章中,我將重點放在講解L-BFGS算法的無約束最小化上,該算法在一些能用上批處理優化的ML問題中特別受歡迎。對於更大的數據集,則常用SGD方法,因爲SGD只需要很少的迭代次數就能達到收斂。在以後的文章中,我可能會涉及這些技術,包括我個人最喜歡的AdaDelta 。

注 : 在整個文章中,我會假設你記得多元微積分。所以,如果你不記得什麼是梯度或海森矩陣,你得先複習一下。

牛頓法

大多數數值優化算法都是迭代式的,它們產生一個序列,該序列最終收斂於CodeCogsEqn (4).png,使得f(x).png達到全局最小化。假設,我們有一個估計屏幕快照 2016-08-11 下午8.10.12.png,我們希望我們的下一個估計屏幕快照 2016-08-11 下午8.11.00.png有這種屬性:屏幕快照 2016-08-11 下午8.11.27.png

牛頓的方法是在點屏幕快照 2016-08-11 下午8.10.12.png附近使用二次函數近似屏幕快照 2016-08-11 下午8.13.06.png。假設屏幕快照 2016-08-11 下午8.13.06.png是二次可微的,我們可以用屏幕快照 2016-08-11 下午8.13.06.png在點屏幕快照 2016-08-11 下午8.14.16.png的泰勒展開來近似屏幕快照 2016-08-11 下午8.13.06.png

屏幕快照 2016-08-11 下午8.15.12.png

其中,屏幕快照 2016-08-11 下午8.17.05.png屏幕快照 2016-08-11 下午8.17.24.png分別爲目標函數屏幕快照 2016-08-11 下午8.13.06.png在點屏幕快照 2016-08-11 下午8.10.12.png處的梯度和Hessian矩陣。當屏幕快照 2016-08-11 下午8.18.28.png時,上面的近似展開式是成立的。你可能記得微積分中一維泰勒多項式展開,這是其推廣。

爲了簡化符號,將上述二次近似記爲屏幕快照 2016-08-11 下午8.21.18.png,我們把生成屏幕快照 2016-08-11 下午8.21.18.png這樣的二次近似的迭代算法中的一些概念簡記如下:

不失一般性,我們可以記屏幕快照 2016-08-11 下午8.19.33.png,那麼上式可以寫作:

屏幕快照 2016-08-11 下午8.22.34.png

其中屏幕快照 2016-08-11 下午8.23.12.png屏幕快照 2016-08-11 下午8.24.27.png分別表示目標函數在點屏幕快照 2016-08-11 下午8.10.12.png處的梯度和Hessian矩陣。

我們想找一個屏幕快照 2016-08-11 下午8.32.42.png,使得屏幕快照 2016-08-11 下午8.13.06.png屏幕快照 2016-08-11 下午8.10.12.png的二次近似最小。上式對屏幕快照 2016-08-11 下午8.32.42.png求導:

屏幕快照 2016-08-11 下午8.35.10.png

任何使得屏幕快照 2016-08-11 下午8.38.01.png屏幕快照 2016-08-11 下午8.38.31.png都是屏幕快照 2016-08-11 下午8.38.53.png的局部極值點,如果我們假設屏幕快照 2016-08-11 下午8.13.06.png是凸函數,則屏幕快照 2016-08-11 下午8.24.27.png正定的,那麼局部極值點就是全局極值點(凸二次規劃)。

解出屏幕快照 2016-08-11 下午8.38.31.png

屏幕快照 2016-08-11 下午8.54.55.png

這就得到了一個很好的搜索方向,在實際應用中,我們一般選擇一個步長α,即按照下式更新屏幕快照 2016-08-11 下午8.11.00.png

屏幕快照 2016-08-11 下午8.58.16.png

使得屏幕快照 2016-08-11 下午8.59.06.png相比f(x).png的減小量最大化。

迭代算法僞碼:

屏幕快照 2016-08-11 下午9.03.34.png

步長α的確定可以採用任何line search算法,其中最簡單的一種是backtracking line search。該算法簡單地選取越來越小的步長α,直到屏幕快照 2016-08-11 下午8.13.06.png的值小到滿意爲止。關於line search算法的詳情請參考Line Search Methods.pdfLecture 5- Gradient Descent.pdf

在軟件工程上,我們可以將牛頓法視作實現了下列Java接口的一個黑盒子:


 
  1. public interface TwiceDifferentiableFunction
  2. {
  3.     // compute f(x)
  4.     double valueAt(double[] x);
  5.  
  6.     // compute grad f(x)
  7.     double[] gradientAt(double[] x);
  8.  
  9.     // compute inverse hessian H^-1
  10.     double[][] inverseHessian(double[] x);
  11. }

如果你有興趣,你還可以通過一些枯燥無味的數學公式,證明對任意一個凸函數,上述算法一定可以收斂到一個唯一的最小值屏幕快照 2016-08-11 下午9.54.52.png,且不受初值屏幕快照 2016-08-11 下午9.55.27.png的影響。對於非凸函數,上述算法仍然有效,但只能保證收斂到一個局部極小值。在上述算法於非凸函數的實際應用中,用戶需要注意初值的選取以及其他算法細節。

巨大的海森矩陣

牛頓法最大的問題在於我們必須計算海森矩陣的逆。注意在機器學習應用中,屏幕快照 2016-08-11 下午8.13.06.png的輸入的維度常常與模型參數對應。十萬維度的參數並不少見(SVM中文文本分類取詞做特徵的話,就在十萬這個量級),在一些圖像識別的場景中,參數可能上十億。所以,計算海森矩陣或其逆並不現實。對許多函數而言,海森矩陣可能根本無法計算,更不用說表示出來求逆了。

所以,在實際應用中牛頓法很少用於大型的優化問題。但幸運的是,即便我們不求出屏幕快照 2016-08-11 下午8.13.06.png屏幕快照 2016-08-11 下午8.10.12.png的精確屏幕快照 2016-08-12 下午8.38.44.png,而使用一個近似的替代值,上述算法依然有效。

擬牛頓法

如果不求解屏幕快照 2016-08-11 下午8.13.06.png屏幕快照 2016-08-11 下午8.10.12.png的精確屏幕快照 2016-08-12 下午8.38.44.png,我們要使用什麼樣的近似呢?我們使用一種叫QuasiUpdate的策略來生成屏幕快照 2016-08-12 下午8.38.44.png的近似,先不管QuasiUpdate具體是怎麼做的,有了這個策略,牛頓法進化爲如下的擬牛頓法:

屏幕快照 2016-08-12 下午8.50.21.png

跟牛頓法相比,只是把屏幕快照 2016-08-12 下午8.38.44.png的計算交給了QuasiUpdate。爲了輔助QuasiUpdate,計算了幾個中間變量。QuasiUpdate只需要上個迭代的屏幕快照 2016-08-12 下午8.38.44.png、輸入和梯度的變化量(屏幕快照 2016-08-12 下午8.54.12.png屏幕快照 2016-08-12 下午8.54.18.png)。如果QuasiUpdate能夠返回精確的屏幕快照 2016-08-12 下午8.57.16.png的逆,則擬牛頓法等價於牛頓法。

在軟件工程上,我們又可以寫一個黑盒子接口,該接口不再需要計算海森矩陣的逆,只需要在內部更新它,再提供一個矩陣乘法的接口即可。事實上,內部如何處理,外部根本無需關心。用Java表示如下:


 
  1. public interface DifferentiableFunction
  2. {
  3.     // compute f(x)
  4.     double valueAt(double[] x);
  5.  
  6.     // compute grad f(x)
  7.     double[] gradientAt(double[] x);
  8. }
  9.  
  10. public interface QuasiNewtonApproximation
  11. {
  12.     // update the H^{-1} estimate (using x_{n+1}-x_n and grad_{n+1}-grad_n)
  13.     void update(double[] deltaX, double[] deltaGrad);
  14.     
  15.     // H^{-1} (direction) using the current H^{-1} estimate
  16.     double[] inverseHessianMultiply(double[] direction);
  17. }

注意我們唯一用到海森矩陣的逆的地方就是求它與梯度的乘積,所以我們根本不需要在內存中將其顯式地、完全地表示出來。這對接下來要闡述的L-BFGS特別有用。如果你對實現細節感興趣,可以看看作者的golang實現。

近似海森矩陣

QuasiUpdate到底要如何近似海森矩陣呢?如果我們讓QuasiUpdate忽略輸入參數,直接返回單位矩陣,那麼擬牛頓法就退化成了梯度下降法了,因爲函數減小的方向永遠是梯度屏幕快照 2016-08-11 下午8.17.05.png。梯度下降法依然能保證凸函數屏幕快照 2016-08-11 下午8.13.06.png收斂到全局最優對應的CodeCogsEqn (4).png,但直覺告訴我們,梯度下降法沒有利用到屏幕快照 2016-08-11 下午8.13.06.png的二階導數信息,收斂速度肯定更慢了。

我們先把屏幕快照 2016-08-11 下午8.13.06.png的二次近似寫在下面,從這裏找些靈感。

屏幕快照 2016-08-11 下午8.22.34.png

Secant Condition

屏幕快照 2016-08-12 下午9.28.30.png的一個性質是,它的梯度與屏幕快照 2016-08-11 下午8.13.06.png屏幕快照 2016-08-11 下午8.10.12.png處的梯度一致(近似函數的梯度與原函數的梯度一致,這才叫近似嘛)。也就是說我們希望保證:

屏幕快照 2016-08-12 下午9.31.14.png

我們做個減法:

屏幕快照 2016-08-12 下午9.33.53.png

由中值定理,我們有:

屏幕快照 2016-08-12 下午9.37.12.png

這個式子就是所謂的Secant Condition,該條件保證屏幕快照 2016-08-12 下午9.49.21.png至少對屏幕快照 2016-08-12 下午9.50.06.png而言是近似海森矩陣的。

等式兩邊同時乘以屏幕快照 2016-08-12 下午8.38.44.png,並且由於我們定義過:

屏幕快照 2016-08-12 下午9.39.06.png

於是我們得到:

屏幕快照 2016-08-12 下午9.40.50.png

對稱性

由定義知海森矩陣是函數的二階偏導數矩陣,即屏幕快照 2016-08-12 下午9.44.57.png,所以海森矩陣一定是對稱的。

BFGS更新

形式化地講,我們希望屏幕快照 2016-08-11 下午8.24.27.png至少滿足上述兩個條件:

  1. 屏幕快照 2016-08-12 下午8.54.12.png屏幕快照 2016-08-12 下午8.54.18.png而言滿足Secant Condition

  2. 滿足對稱性

給定上述兩個條件,我們還希望屏幕快照 2016-08-11 下午8.24.27.png相較於屏幕快照 2016-08-12 下午9.53.27.png的變化量最小。這類似“ MIRA 更新”,我們有許多滿足條件的選項,但我們只選那個變化最小的。這種約束形式化地表述如下:

屏幕快照 2016-08-12 下午9.55.33.png

上面的範數屏幕快照 2016-08-12 下午9.57.15.png表示weighted frobenius norm。這個約束最小化問題的解是:

屏幕快照 2016-08-12 下午9.59.02.png

式中屏幕快照 2016-08-12 下午10.00.37.png。我不知道如何推導它,推導的話需要用很多符號,並且費時費力。

這種更新算法就是著名的Broyden–Fletcher–Goldfarb–Shanno (BFGS)算法,該算法是取發明者名字的首字母命名的。

bfgs.png

關於BFGS,有一些需要注意的點:

  • 只要屏幕快照 2016-08-12 下午8.38.44.png是正定的,屏幕快照 2016-08-12 下午10.11.17.png就一定是正定的。所以我們只需要選擇一個正定的屏幕快照 2016-08-12 下午10.13.05.png即可,甚至可以選擇單位矩陣。

  • 屏幕快照 2016-08-12 下午8.38.44.png屏幕快照 2016-08-12 下午10.11.17.png還存在簡單的算術關係,給定屏幕快照 2016-08-12 下午10.11.17.png屏幕快照 2016-08-12 下午8.54.12.png屏幕快照 2016-08-12 下午8.54.18.png,我們就能倒推出屏幕快照 2016-08-12 下午8.38.44.png

把這些知識放到一起,我們就得出了BFGS更新的算法,給定方向d,該算法可以計算出屏幕快照 2016-08-12 下午10.17.59.png,卻不需要求屏幕快照 2016-08-12 下午8.38.44.png矩陣,只需要按照上述表達式不斷地遞推即可:

屏幕快照 2016-08-12 下午10.19.22.png

由於屏幕快照 2016-08-12 下午8.38.44.png的唯一作用就是計算屏幕快照 2016-08-12 下午10.21.08.png,我們只需用該更新算法就能實現擬牛頓法。

L-BFGS:省內存的BFGS

BFGS擬牛頓近似算法雖然免去了計算海森矩陣的煩惱,但是我們仍然需要保存每次迭代的屏幕快照 2016-08-12 下午8.54.12.png屏幕快照 2016-08-12 下午8.54.18.png的歷史值。這依然沒有減輕內存負擔,要知道我們選用擬牛頓法的初衷就是減小內存佔用。

L-BFGS是limited BFGS的縮寫,簡單地只使用最近的m個屏幕快照 2016-08-12 下午8.54.12.png屏幕快照 2016-08-12 下午8.54.18.png記錄值。也就是隻儲存屏幕快照 2016-08-12 下午10.29.13.png屏幕快照 2016-08-12 下午10.29.32.png,用它們去近似計算屏幕快照 2016-08-12 下午10.21.08.png。初值屏幕快照 2016-08-12 下午10.30.56.png依然可以選取任意對稱的正定矩陣。

L-BFGS改進算法

在實際應用中有許多L-BFGS的改進算法。對不可微分的函數,可以用 othant-wise 的L-BFGS改進算法來訓練屏幕快照 2016-08-12 下午10.34.38.png正則損失函數。

不在大型數據集上使用L-BFGS的原因之一是,在線算法可能收斂得更快。這裏甚至有一個L-BFGS的在線學習算法,但據我所知,在大型數據集上它們都不如一些SGD的改進算法(包括 AdaGrad 或 AdaDelta)的表現好。

Reference

http://aria42.com/blog/2014/12/understanding-lbfgs

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章