l bfgs算法java代码_数值优化:理解L-BFGS算法

l bfgs算法java代码_数值优化:理解L-BFGS算法_第1张图片译自《Numerical Optimization: Understanding L-BFGS》,本来只想作为学习CRF的补充材料,读完后发现收获很多,把许多以前零散的知识点都串起来了。对我而言,的确比零散地看论文要轻松得多。原文并没有太多关注实现,对实现感兴趣的话推荐原作者的golang实现。

l bfgs算法java代码_数值优化:理解L-BFGS算法_第2张图片

数值优化是许多机器学习算法的核心。一旦你确定用什么模型,并且准备好了数据集,剩下的工作就是训练了。估计模型的参数(训练模型)通常归结为最小化一个多元函数

636fe018f01050c0c11bcddc281ac25f.png,其中输入

41609b101bc5201ed04b28acd61c0e8d.png是一个高维向量,也就是模型参数。换句话说,如果你求解出:

8376269a51fc05b5fa356aec4a2da232.png

那么

41609b101bc5201ed04b28acd61c0e8d.png*就是最佳的模型参数(当然跟你选择了什么目标函数有关系)。

在这篇文章中,我将重点放在讲解L-BFGS算法的无约束最小化上,该算法在一些能用上批处理优化的ML问题中特别受欢迎。对于更大的数据集,则常用SGD方法,因为SGD只需要很少的迭代次数就能达到收敛。在以后的文章中,我可能会涉及这些技术,包括我个人最喜欢的AdaDelta 。

注 : 在整个文章中,我会假设你记得多元微积分。所以,如果你不记得什么是梯度或海森矩阵,你得先复习一下。

l bfgs算法java代码_数值优化:理解L-BFGS算法_第3张图片

牛顿法

大多数数值优化算法都是迭代式的,它们产生一个序列,该序列最终收敛于

bed0f3386c636276ef1c287298e2b726.png,使得

636fe018f01050c0c11bcddc281ac25f.png达到全局最小化。假设,我们有一个估计

7f4de5c1a404501eade9c9c0dc6ad95a.png,我们希望我们的下一个估计

6eb39d638da5f450a983df69f93e63f2.png有这种属性:

58f8dc5937b2fbf27cec29c650be361d.png

牛顿的方法是在点

7f4de5c1a404501eade9c9c0dc6ad95a.png附近使用二次函数近似

ba2936186d0f6aa02a13a3b9602c2fb8.png。假设

ba2936186d0f6aa02a13a3b9602c2fb8.png是二次可微的,我们可以用

ba2936186d0f6aa02a13a3b9602c2fb8.png在点

72a6c8fc8a1d740b7c6cb933db630a4a.png的泰勒展开来近似

ba2936186d0f6aa02a13a3b9602c2fb8.png

70972faf6b600d3c2cbbf511b65e7bf9.png

其中,

10fdc2fe2473034d4f753fc59af678bc.png

bd01699312069fbb48321a1061415bbe.png分别为目标函数

ba2936186d0f6aa02a13a3b9602c2fb8.png在点

7f4de5c1a404501eade9c9c0dc6ad95a.png处的梯度和Hessian矩阵。当

d67262015a8a0e8cf3f08bd6ea4b4b96.png时,上面的近似展开式是成立的。你可能记得微积分中一维泰勒多项式展开,这是其推广。

为了简化符号,将上述二次近似记为

741655a134b930d2005fd337f81a9d85.png,我们把生成

741655a134b930d2005fd337f81a9d85.png这样的二次近似的迭代算法中的一些概念简记如下:

不失一般性,我们可以记

4567b49c3809e8ae98c2b539378032cd.png,那么上式可以写作:

e8b4a5b22aa43813f2883c3e46e9c03e.png

其中

7fc8ffa3c3d22681439f03c88d945112.png

9f6967cb97db43e3a693c4b0362275a5.png分别表示目标函数在点

7f4de5c1a404501eade9c9c0dc6ad95a.png处的梯度和Hessian矩阵。

我们想找一个

18f38d3617a55001b0224c71ba593afc.png,使得

ba2936186d0f6aa02a13a3b9602c2fb8.png

7f4de5c1a404501eade9c9c0dc6ad95a.png的二次近似最小。上式对

18f38d3617a55001b0224c71ba593afc.png求导:

l bfgs算法java代码_数值优化:理解L-BFGS算法_第4张图片

任何使得

db9f9fd1a611729f0dec40f95e888a2e.png

e0b58e5c18d4a0a11a0bc2a15805d853.png都是

1977233c2d63d4d928fc19a3c9fb07ee.png的局部极值点,如果我们假设

ba2936186d0f6aa02a13a3b9602c2fb8.png是凸函数,则

9f6967cb97db43e3a693c4b0362275a5.png是正定的,那么局部极值点就是全局极值点(凸二次规划)。

解出

e0b58e5c18d4a0a11a0bc2a15805d853.png

f8168e3594e6959e0fc9086cc6a8199b.png

这就得到了一个很好的搜索方向,在实际应用中,我们一般选择一个步长α,即按照下式更新

6eb39d638da5f450a983df69f93e63f2.png

1a0242d355c4538a658bb8bfb094e55a.png

使得

4f2034cd0e4c7acfb953d5de621419fe.png相比

636fe018f01050c0c11bcddc281ac25f.png的减小量最大化。

迭代算法伪码:

l bfgs算法java代码_数值优化:理解L-BFGS算法_第5张图片

步长α的确定可以采用任何line search算法,其中最简单的一种是backtracking line search。该算法简单地选取越来越小的步长α,直到

ba2936186d0f6aa02a13a3b9602c2fb8.png的值小到满意为止。关于line search算法的详情请参考

b126cdf7458bf8e06dec75275f5faa59.gifLine Search Methods.pdf或

b126cdf7458bf8e06dec75275f5faa59.gifLecture 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);

}

如果你有兴趣,你还可以通过一些枯燥无味的数学公式,证明对任意一个凸函数,上述算法一定可以收敛到一个唯一的最小值

7c3069ebb085e460b087c292feaa31cb.png,且不受初值

546ced8a78b04c91bd6e73922ee9314b.png的影响。对于非凸函数,上述算法仍然有效,但只能保证收敛到一个局部极小值。在上述算法于非凸函数的实际应用中,用户需要注意初值的选取以及其他算法细节。

巨大的海森矩阵

牛顿法最大的问题在于我们必须计算海森矩阵的逆。注意在机器学习应用中,

ba2936186d0f6aa02a13a3b9602c2fb8.png的输入的维度常常与模型参数对应。十万维度的参数并不少见(SVM中文文本分类取词做特征的话,就在十万这个量级),在一些图像识别的场景中,参数可能上十亿。所以,计算海森矩阵或其逆并不现实。对许多函数而言,海森矩阵可能根本无法计算,更不用说表示出来求逆了。

所以,在实际应用中牛顿法很少用于大型的优化问题。但幸运的是,即便我们不求出

ba2936186d0f6aa02a13a3b9602c2fb8.png

7f4de5c1a404501eade9c9c0dc6ad95a.png的精确

52a03db64e938b585139914409ea91ee.png,而使用一个近似的替代值,上述算法依然有效。

拟牛顿法

如果不求解

ba2936186d0f6aa02a13a3b9602c2fb8.png

7f4de5c1a404501eade9c9c0dc6ad95a.png的精确

52a03db64e938b585139914409ea91ee.png,我们要使用什么样的近似呢?我们使用一种叫QuasiUpdate的策略来生成

52a03db64e938b585139914409ea91ee.png的近似,先不管QuasiUpdate具体是怎么做的,有了这个策略,牛顿法进化为如下的拟牛顿法:

781ba1d859679c5c015e16fb2d0c4f1d.png

跟牛顿法相比,只是把

52a03db64e938b585139914409ea91ee.png的计算交给了QuasiUpdate。为了辅助QuasiUpdate,计算了几个中间变量。QuasiUpdate只需要上个迭代的

52a03db64e938b585139914409ea91ee.png、输入和梯度的变化量(

2d7bba6a1ebb62c7f71856699b230b2e.png

b2414800181b8007d7fce5e2044bbedb.png)。如果QuasiUpdate能够返回精确的

f091a9ec7a70fa530f2d5103155002a1.png的逆,则拟牛顿法等价于牛顿法。

在软件工程上,我们又可以写一个黑盒子接口,该接口不再需要计算海森矩阵的逆,只需要在内部更新它,再提供一个矩阵乘法的接口即可。事实上,内部如何处理,外部根本无需关心。用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忽略输入参数,直接返回单位矩阵,那么拟牛顿法就退化成了梯度下降法了,因为函数减小的方向永远是梯度

10fdc2fe2473034d4f753fc59af678bc.png。梯度下降法依然能保证凸函数

ba2936186d0f6aa02a13a3b9602c2fb8.png收敛到全局最优对应的

bed0f3386c636276ef1c287298e2b726.png,但直觉告诉我们,梯度下降法没有利用到

ba2936186d0f6aa02a13a3b9602c2fb8.png的二阶导数信息,收敛速度肯定更慢了。

我们先把

ba2936186d0f6aa02a13a3b9602c2fb8.png的二次近似写在下面,从这里找些灵感。

e8b4a5b22aa43813f2883c3e46e9c03e.png

Secant Condition

7ad7bf1644b92e13b7e2a3c7b56d6786.png的一个性质是,它的梯度与

ba2936186d0f6aa02a13a3b9602c2fb8.png

7f4de5c1a404501eade9c9c0dc6ad95a.png处的梯度一致(近似函数的梯度与原函数的梯度一致,这才叫近似嘛)。也就是说我们希望保证:

l bfgs算法java代码_数值优化:理解L-BFGS算法_第6张图片

我们做个减法:

ed21028d73e233008aeaa69b965b8c7d.png

由中值定理,我们有:

6e20183477b4141293888b97116773eb.png

这个式子就是所谓的Secant Condition,该条件保证

a33cd46df313403b4cb8e73cd3b11662.png至少对

47bb099d8c6517f7570d6aff81153f00.png而言是近似海森矩阵的。

等式两边同时乘以

52a03db64e938b585139914409ea91ee.png,并且由于我们定义过:

l bfgs算法java代码_数值优化:理解L-BFGS算法_第7张图片

于是我们得到:

a91233543e17d33e5d4ba0963ce96ee1.png

对称性

由定义知海森矩阵是函数的二阶偏导数矩阵,即

a24f8561ce273303b8c1571b73c84d21.png,所以海森矩阵一定是对称的。

BFGS更新

形式化地讲,我们希望

9f6967cb97db43e3a693c4b0362275a5.png至少满足上述两个条件:

2d7bba6a1ebb62c7f71856699b230b2e.png

b2414800181b8007d7fce5e2044bbedb.png而言满足Secant Condition

满足对称性

给定上述两个条件,我们还希望

9f6967cb97db43e3a693c4b0362275a5.png相较于

6b343bf885ce9bf1c94d161188b1fbec.png的变化量最小。这类似“ MIRA 更新”,我们有许多满足条件的选项,但我们只选那个变化最小的。这种约束形式化地表述如下:

l bfgs算法java代码_数值优化:理解L-BFGS算法_第8张图片

2d597d0b7bce44ac4ec1b3692dbb97bf.png

式中

93c7e9e8f4d2b0b7223297aae90b0800.png。我不知道如何推导它,推导的话需要用很多符号,并且费时费力。

这种更新算法就是著名的Broyden–Fletcher–Goldfarb–Shanno (BFGS)算法,该算法是取发明者名字的首字母命名的。

l bfgs算法java代码_数值优化:理解L-BFGS算法_第9张图片

关于BFGS,有一些需要注意的点:

只要

52a03db64e938b585139914409ea91ee.png是正定的,

cdf5cfce9fb881a9671d5ad26b2e22f7.png就一定是正定的。所以我们只需要选择一个正定的

ecbf84438f3976b352ea95bf49463e57.png即可,甚至可以选择单位矩阵。

52a03db64e938b585139914409ea91ee.png

cdf5cfce9fb881a9671d5ad26b2e22f7.png还存在简单的算术关系,给定

cdf5cfce9fb881a9671d5ad26b2e22f7.png

2d7bba6a1ebb62c7f71856699b230b2e.png

b2414800181b8007d7fce5e2044bbedb.png,我们就能倒推出

52a03db64e938b585139914409ea91ee.png

把这些知识放到一起,我们就得出了BFGS更新的算法,给定方向d,该算法可以计算出

152ba3459e2fae3d14437d66c0adf7a6.png,却不需要求

52a03db64e938b585139914409ea91ee.png矩阵,只需要按照上述表达式不断地递推即可:

l bfgs算法java代码_数值优化:理解L-BFGS算法_第10张图片

由于

52a03db64e938b585139914409ea91ee.png的唯一作用就是计算

7cf0fe3763ef5d889e1ac0fe29f9d9c2.png,我们只需用该更新算法就能实现拟牛顿法。

L-BFGS:省内存的BFGS

BFGS拟牛顿近似算法虽然免去了计算海森矩阵的烦恼,但是我们仍然需要保存每次迭代的

2d7bba6a1ebb62c7f71856699b230b2e.png

b2414800181b8007d7fce5e2044bbedb.png的历史值。这依然没有减轻内存负担,要知道我们选用拟牛顿法的初衷就是减小内存占用。

L-BFGS是limited BFGS的缩写,简单地只使用最近的m个

2d7bba6a1ebb62c7f71856699b230b2e.png

b2414800181b8007d7fce5e2044bbedb.png记录值。也就是只储存

42cf73ba5a6075cf5a33ee515a9cf2d2.png

e54c93b0533bdff0c025b31b0e234d04.png,用它们去近似计算

7cf0fe3763ef5d889e1ac0fe29f9d9c2.png。初值

664292b0728d17cf2c4197e41c6b9132.png依然可以选取任意对称的正定矩阵。

L-BFGS改进算法

在实际应用中有许多L-BFGS的改进算法。对不可微分的函数,可以用othant-wise 的L-BFGS改进算法来训练

f5f7422afad1aca877127b5957400509.png正则损失函数。

不在大型数据集上使用L-BFGS的原因之一是,在线算法可能收敛得更快。这里甚至有一个L-BFGS的在线学习算法,但据我所知,在大型数据集上它们都不如一些SGD的改进算法(包括 AdaGrad 或 AdaDelta)的表现好。

Reference

你可能感兴趣的:(l,bfgs算法java代码)