譯自《Numerical Optimization: Understanding L-BFGS》,本來只想作爲學習CRF的補充材料,讀完後發現收穫很多,把許多以前零散的知識點都串起來了。對我而言,的確比零散地看論文要輕鬆得多。原文並沒有太多關注實現,對實現感興趣的話推薦原作者的golang實現。
數值優化是許多機器學習算法的核心。一旦你確定用什麼模型,並且準備好了數據集,剩下的工作就是訓練了。估計模型的參數(訓練模型)通常歸結爲最小化一個多元函數,其中輸入是一個高維向量,也就是模型參數。換句話說,如果你求解出:
那麼*就是最佳的模型參數(當然跟你選擇了什麼目標函數有關係)。
在這篇文章中,我將重點放在講解L-BFGS算法的無約束最小化上,該算法在一些能用上批處理優化的ML問題中特別受歡迎。對於更大的數據集,則常用SGD方法,因爲SGD只需要很少的迭代次數就能達到收斂。在以後的文章中,我可能會涉及這些技術,包括我個人最喜歡的AdaDelta 。
注 : 在整個文章中,我會假設你記得多元微積分。所以,如果你不記得什麼是梯度或海森矩陣,你得先複習一下。
牛頓法
大多數數值優化算法都是迭代式的,它們產生一個序列,該序列最終收斂於,使得達到全局最小化。假設,我們有一個估計,我們希望我們的下一個估計有這種屬性:。
牛頓的方法是在點附近使用二次函數近似。假設是二次可微的,我們可以用在點的泰勒展開來近似。
其中,和分別爲目標函數在點處的梯度和Hessian矩陣。當時,上面的近似展開式是成立的。你可能記得微積分中一維泰勒多項式展開,這是其推廣。
爲了簡化符號,將上述二次近似記爲,我們把生成這樣的二次近似的迭代算法中的一些概念簡記如下:
不失一般性,我們可以記,那麼上式可以寫作:
其中和分別表示目標函數在點處的梯度和Hessian矩陣。
我們想找一個,使得在的二次近似最小。上式對求導:
任何使得的都是的局部極值點,如果我們假設是凸函數,則是正定的,那麼局部極值點就是全局極值點(凸二次規劃)。
解出:
這就得到了一個很好的搜索方向,在實際應用中,我們一般選擇一個步長α,即按照下式更新:
使得相比的減小量最大化。
迭代算法僞碼:
步長α的確定可以採用任何line search算法,其中最簡單的一種是backtracking line search。該算法簡單地選取越來越小的步長α,直到的值小到滿意爲止。關於line search算法的詳情請參考Line Search Methods.pdf或Lecture 5- Gradient Descent.pdf。
在軟件工程上,我們可以將牛頓法視作實現了下列Java接口的一個黑盒子:
- public interface TwiceDifferentiableFunction
- {
- // compute f(x)
- double valueAt(double[] x);
- // compute grad f(x)
- double[] gradientAt(double[] x);
- // compute inverse hessian H^-1
- double[][] inverseHessian(double[] x);
- }
如果你有興趣,你還可以通過一些枯燥無味的數學公式,證明對任意一個凸函數,上述算法一定可以收斂到一個唯一的最小值,且不受初值的影響。對於非凸函數,上述算法仍然有效,但只能保證收斂到一個局部極小值。在上述算法於非凸函數的實際應用中,用戶需要注意初值的選取以及其他算法細節。
巨大的海森矩陣
牛頓法最大的問題在於我們必須計算海森矩陣的逆。注意在機器學習應用中,的輸入的維度常常與模型參數對應。十萬維度的參數並不少見(SVM中文文本分類取詞做特徵的話,就在十萬這個量級),在一些圖像識別的場景中,參數可能上十億。所以,計算海森矩陣或其逆並不現實。對許多函數而言,海森矩陣可能根本無法計算,更不用說表示出來求逆了。
所以,在實際應用中牛頓法很少用於大型的優化問題。但幸運的是,即便我們不求出在的精確,而使用一個近似的替代值,上述算法依然有效。
擬牛頓法
如果不求解在的精確,我們要使用什麼樣的近似呢?我們使用一種叫QuasiUpdate的策略來生成的近似,先不管QuasiUpdate具體是怎麼做的,有了這個策略,牛頓法進化爲如下的擬牛頓法:
跟牛頓法相比,只是把的計算交給了QuasiUpdate。爲了輔助QuasiUpdate,計算了幾個中間變量。QuasiUpdate只需要上個迭代的、輸入和梯度的變化量(和)。如果QuasiUpdate能夠返回精確的的逆,則擬牛頓法等價於牛頓法。
在軟件工程上,我們又可以寫一個黑盒子接口,該接口不再需要計算海森矩陣的逆,只需要在內部更新它,再提供一個矩陣乘法的接口即可。事實上,內部如何處理,外部根本無需關心。用Java表示如下:
- public interface DifferentiableFunction
- {
- // compute f(x)
- double valueAt(double[] x);
- // compute grad f(x)
- double[] gradientAt(double[] x);
- }
- public interface QuasiNewtonApproximation
- {
- // update the H^{-1} estimate (using x_{n+1}-x_n and grad_{n+1}-grad_n)
- void update(double[] deltaX, double[] deltaGrad);
- // H^{-1} (direction) using the current H^{-1} estimate
- double[] inverseHessianMultiply(double[] direction);
- }
注意我們唯一用到海森矩陣的逆的地方就是求它與梯度的乘積,所以我們根本不需要在內存中將其顯式地、完全地表示出來。這對接下來要闡述的L-BFGS特別有用。如果你對實現細節感興趣,可以看看作者的golang實現。
近似海森矩陣
QuasiUpdate到底要如何近似海森矩陣呢?如果我們讓QuasiUpdate忽略輸入參數,直接返回單位矩陣,那麼擬牛頓法就退化成了梯度下降法了,因爲函數減小的方向永遠是梯度。梯度下降法依然能保證凸函數收斂到全局最優對應的,但直覺告訴我們,梯度下降法沒有利用到的二階導數信息,收斂速度肯定更慢了。
我們先把的二次近似寫在下面,從這裏找些靈感。
Secant Condition
的一個性質是,它的梯度與在處的梯度一致(近似函數的梯度與原函數的梯度一致,這才叫近似嘛)。也就是說我們希望保證:
我們做個減法:
由中值定理,我們有:
這個式子就是所謂的Secant Condition,該條件保證至少對而言是近似海森矩陣的。
等式兩邊同時乘以,並且由於我們定義過:
於是我們得到:
對稱性
由定義知海森矩陣是函數的二階偏導數矩陣,即,所以海森矩陣一定是對稱的。
BFGS更新
形式化地講,我們希望至少滿足上述兩個條件:
-
對和而言滿足Secant Condition
-
滿足對稱性
給定上述兩個條件,我們還希望相較於的變化量最小。這類似“ MIRA 更新”,我們有許多滿足條件的選項,但我們只選那個變化最小的。這種約束形式化地表述如下:
上面的範數表示weighted frobenius norm。這個約束最小化問題的解是:
式中。我不知道如何推導它,推導的話需要用很多符號,並且費時費力。
這種更新算法就是著名的Broyden–Fletcher–Goldfarb–Shanno (BFGS)算法,該算法是取發明者名字的首字母命名的。
關於BFGS,有一些需要注意的點:
-
只要是正定的,就一定是正定的。所以我們只需要選擇一個正定的即可,甚至可以選擇單位矩陣。
-
和還存在簡單的算術關係,給定和和,我們就能倒推出。
把這些知識放到一起,我們就得出了BFGS更新的算法,給定方向d,該算法可以計算出,卻不需要求矩陣,只需要按照上述表達式不斷地遞推即可:
由於的唯一作用就是計算,我們只需用該更新算法就能實現擬牛頓法。
L-BFGS:省內存的BFGS
BFGS擬牛頓近似算法雖然免去了計算海森矩陣的煩惱,但是我們仍然需要保存每次迭代的和的歷史值。這依然沒有減輕內存負擔,要知道我們選用擬牛頓法的初衷就是減小內存佔用。
L-BFGS是limited BFGS的縮寫,簡單地只使用最近的m個和記錄值。也就是隻儲存和,用它們去近似計算。初值依然可以選取任意對稱的正定矩陣。
L-BFGS改進算法
在實際應用中有許多L-BFGS的改進算法。對不可微分的函數,可以用 othant-wise 的L-BFGS改進算法來訓練正則損失函數。
不在大型數據集上使用L-BFGS的原因之一是,在線算法可能收斂得更快。這裏甚至有一個L-BFGS的在線學習算法,但據我所知,在大型數據集上它們都不如一些SGD的改進算法(包括 AdaGrad 或 AdaDelta)的表現好。
Reference
http://aria42.com/blog/2014/12/understanding-lbfgs