Broyden-Fletcher-Goldfarb-Shanno algorithm
Broyden-Fletcher-Goldfarb-Shanno(BFGS)法(BFGS method)是一种拟牛顿法,指用BFGS矩阵作为拟牛顿法中的对称正定迭代矩阵的方法,此法是1970年前后由柏萝登(C.G.Broyden)、弗莱彻(R.Fletcher)、戈德福布(D.Goldfarb),以及生纳(D.F.Shanno)所研究,故得名,由于BFGS法对一维搜索的精度要求不高,并且由迭代产生的BFGS矩阵不易变为奇异矩阵,因而BFGS法比DFP法在计算中具有更好的数值稳定性。
下图中从左到右依次是Broyden,Fletcher,Goldfarb,Shanno.四位数学家,他们名字的首字母是BFGS,所以算法的名字就叫做BFGS算法.接下来我们就一起来学习BFGS算法的内容。
2-牛顿法求根问题
牛顿发现,一个函数的跟从物理的角度就可以根据函数图像迭代求得.牛顿法求根的思路是:
a.在X轴上随机一点 x 1 x_1 x1, 经过 x 1 x_1 x1做X轴的垂线,得到垂线与函数 f ( x ) f(x) f(x)图像的交点 f ( x 1 ) f(x_1) f(x1).
b.通过 f ( x 1 ) f(x_1) f(x1)做函数的切线,得到切线与X轴的交点 x 2 x_2 x2.
c.迭代a/b两步.
下面附上一张动图方便理解:
通过图片我们可以看到.在X轴上取的点会随着迭代次数的增加而越来越接近函数的根.经过无限多次的迭代,
x
n
x_{n}
xn就等于函数
f
(
x
)
f(x)
f(x)的根.但牛顿法在实际应用的时候我们不会让算法就这么迭代下去,所以当
x
k
−
1
x_{k-1}
xk−1和
x
k
x_{k}
xk相同或者两个值的差小于一个阈值的时候,
x
k
x_{k}
xk就是函数
f
(
x
)
f(x)
f(x)的根.
图片是上边动图从到的动作.可以看到,三角形绿色的直角边的值是
x
1
−
x
2
x_1-x_2
x1−x2 ,
x
1
x_1
x1是已知的(随机出来的),而且函数表达式
f
(
x
)
f(x)
f(x)也是已知的.所以三角形的另一条直角边也是已知的.根据导函数定义,函数在点
x
x
x的导函数就是
f
′
(
x
)
=
f
(
x
+
Δ
x
)
Δ
x
f'(x)=\frac{f(x+\Delta x)}{\Delta x}
f′(x)=Δxf(x+Δx)
f
′
(
x
)
f'(x)
f′(x)变换一下得到:
Δ
x
=
f
(
x
+
Δ
x
)
f
′
(
x
)
\Delta x = \frac{f(x+\Delta x)}{f'(x)}
Δx=f′(x)f(x+Δx)
可以得出每一次迭代的表达式都是
x
k
=
x
k
−
1
−
Δ
x
k
−
1
=
x
k
−
1
−
f
(
x
k
−
1
)
f
′
(
x
k
−
1
)
x_k =x_{k-1} -\Delta x_{k-1}= x_{k-1} - \frac{f(x_{k-1})}{f'(x_{k-1})}
xk=xk−1−Δxk−1=xk−1−f′(xk−1)f(xk−1)
所以,根据牛顿法求根的思路,我们可以总结一下使用牛顿法求根的步骤:
a.已知函数 f ( x ) f(x) f(x)的情况下,随机产生点 x 0 x_{0} x0.
b.由已知的 x 0 x_{0} x0点按照 x k = x k − 1 − f ( x k − 1 ) f ′ ( x k − 1 ) x_{k} =x_{k-1} - \frac{ f(x_{k-1})}{f^{'}(x_{k-1})} xk=xk−1−f′(xk−1)f(xk−1)的公式进行 k k k次迭代.
c.如果 x k x_{k} xk与上一次的迭代结果 x k − 1 x_{k-1} xk−1相同或者相差结果小于一个阈值时,本次结果 x k x_{k} xk就是函数 f ( x ) f(x) f(x)的根。
以上为牛顿法的求根的思路.
3-牛顿法求函数的驻点
我们知道,机器学习过程中的函数最优化问题,大部分优化的都是目标函数的导函数,要求得导函数为零时的点或近似点来作为机器学习算法表现最好的点.把牛顿求根法的函数换成要优化的导函数就行了.要求的的导函数为零的点叫做驻点.所以用牛顿法求函数驻点同求函数的根是没有任何区别的.只是
f
(
x
)
f(x)
f(x)的变为了
f
′
(
x
)
f'(x)
f′(x),原来的迭代公式变成了一阶导函数和二阶导函数了:
x
k
=
x
k
−
1
−
f
′
(
x
k
−
1
)
f
′
′
(
x
k
−
1
)
x_k=x_{k-1} - \frac{f'(x_{k-1})}{f''(x_{k-1})}
xk=xk−1−f′′(xk−1)f′(xk−1)
这就是迭代公式,我们通过几何直觉,得到了求解函数根的办法,那这么厉害的一个想法,有没有什么理论依据作为支撑呢?当然有了,要不我也不这么问.
4-牛顿法求驻点的本质
牛顿法求驻点的本质其实是二阶泰勒展开.我们来看二阶泰勒展开式:
f
(
x
)
≈
f
(
x
k
)
+
f
′
(
x
k
)
(
x
−
x
k
)
+
1
2
f
′
′
(
x
k
)
(
x
−
x
k
)
2
f(x)\approx f(x_k) + f'(x_k)(x-x_k) + \frac{1}2f''(x_k)(x-x_k)^2
f(x)≈f(xk)+f′(xk)(x−xk)+21f′′(xk)(x−xk)2
用二阶泰勒展开式来近似要求解的原函数,为什么要用二阶,不用更高阶?因为内存原因,我们知道算法在计算机中运行的时候是需要很大的内存空间的。泰勒展开式展开的次数越多,结果越精确,没有使用三阶四阶或者更高阶展开式的原因就是目前硬件内存不足以存储计算过程中演变出来更复杂体积更庞大的矩阵。
当我们要对原函数求解时,可令
f
′
(
x
)
=
0
f'(x)=0
f′(x)=0时的结果就是原函数的解.所以对求导,消去常数项后得到公式如下:
f
′
(
x
k
)
+
f
′
′
(
x
k
)
(
x
−
x
k
)
=
0
f'(x_k)+f''(x_k)(x-x_k)=0
f′(xk)+f′′(xk)(x−xk)=0
经过变换后所得的
x
=
x
k
−
f
′
(
x
k
)
f
′
′
(
x
k
)
x=x_k - \frac{f'(x_k)}{f''(x_k)}
x=xk−f′′(xk)f′(xk)就是上面的公式。所以,牛顿法求驻点的本质就是对函数进行二阶泰勒展开后变换所得到的结果.
在一元函数求解的问题中,我们可以很愉快的使用牛顿法求驻点,但我们知道,在机器学习的优化问题中,我们要优化的都是多元函数,所以x往往不是一个实数,而是一个向量.所以将牛顿求根法利用多元函数中时,x是一个向量,
f
′
(
x
)
f'(x)
f′(x)是一个向量也就是多元函数的梯度。
而对
f
′
(
x
)
f'(x)
f′(x)求导以后得到的是一个矩阵,叫做Hessian矩阵.
所以迭代公式演变为:
x
k
+
1
=
x
k
−
H
k
−
1
g
k
,
k
=
0
,
1
,
.
.
.
\mathbf{x}_{k+1} = \mathbf{x}_k - \mathbf{H}_k^{-1}g_k , k=0,1,...
xk+1=xk−Hk−1gk,k=0,1,...
公式中,
g
k
g_k
gk为迭代公式公式中的
f
′
(
x
)
f'(x)
f′(x)为多元函数的梯度,可看做是一个Nx1的矩阵,
H
k
−
1
\mathbf{H}_k^{-1}
Hk−1代表二阶导函数的逆矩阵,仍然是一个NxN的矩阵。
N*N的
H
k
−
1
\mathbf{H}_k^{-1}
Hk−1矩阵给我们带来了一个非常不喜欢的问题,这个矩阵太难运算的。当x的维度特别多的时候,我们想求得
H
k
−
1
\mathbf{H}_k^{-1}
Hk−1是非常困难的.而牛顿法求驻点又是一个迭代算法,所以这个困难我们还要面临无限多次,导致了牛顿法求驻点在机器学习中无法使用.有没有什么解决办法呢?
答案就是BFGS算法。
BFGS算法
BFGS算法是通过迭代来逼近 H k − 1 \mathbf{H}_k^{-1} Hk−1的算法.逼近的方式如下:
D k + 1 = + ( I − s k y k T y k T s k ) D k ( I − s k y k T y k T s k ) + s k y k T y k T s k D_{k+1} = +(\mathbf I-\frac{s_ky_k^T}{y_k^Ts_k})D_k(\mathbf I-\frac{s_ky_k^T}{y_k^Ts_k})+\frac{s_ky_k^T}{y_k^Ts_k} Dk+1=+(I−ykTskskykT)Dk(I−ykTskskykT)+ykTskskykT
公式中的{D_k} 就是
H
k
−
1
\mathbf{H}_k^{-1}
Hk−1,
s
k
=
x
k
+
1
−
x
k
,
y
k
=
g
k
+
1
−
g
k
s_k=x_{k+1}-x_k,y_k=g_{k+1}-g_k
sk=xk+1−xk,yk=gk+1−gk是原函数的导函数,I是单位矩阵.
D
k
D_k
Dk的迭代公式复杂无比,还好我们不用记住它.BFGS是通过迭代来逼近
H
k
−
1
\mathbf{H}_k^{-1}
Hk−1矩阵,其中第一步的D矩阵是单位矩阵.
我们要通过牛顿求驻点法和BFGS算法来求得一个函数的根,两个算法都需要迭代,所以我们干脆让他俩一起迭代就好了.两个算法都是慢慢逼近函数根,所以经过k次迭代以后,所得到的解就是机器学习中目标函数导函数的根.这种两个算法共同迭代的计算方式,我们称之为On The Fly.
回顾一下梯度下降的表达式
Θ
k
=
Θ
k
+
1
−
α
∙
g
\Theta_k=\Theta_{k+1}-\alpha \bullet g
Θk=Θk+1−α∙g ,在BFGS算法迭代的第一步x2=x1-D1*g1,单位矩阵与梯度g相乘,就等于梯度g,形式上同梯度下降的表达式是相同的.相当于学习率等于1的梯度下降,所以BFGS算法可以理解为从梯度下降逐步转换为牛顿法求函数解的一个算法.
虽然我们使用了BFGS算法来利用单位矩阵逐步逼近H矩阵,但是每次计算的时候都要存储Dk矩阵,Dk矩阵有多大呢.假设我们的数据集有十万个维度(不算特别大),那么每次迭代所要存储D矩阵的结果是74.5GB.我们无法保存如此巨大的矩阵内容,如何解决呢?
答案是:使用L-BFGS算法.
L-BFGS算法
L-BFGS算法翻译过来就是有限内存中进行BFGS算法,L是limited memory的意思。
我们每一次对D矩阵的迭代,都是通过迭代计算
s
k
s_k
sk和
y
k
y_k
yk得到的。既然存不下D矩阵,那么我们存储下所有的
s
k
s_k
sk和
y
k
y_k
yk,比如想要得到
D
10
D_{10}
D10就用单位矩阵
I
I
I同存储下的
s
1
s_1
s1和
y
1
y_1
y1到
s
10
s_{10}
s10和
y
10
y_{10}
y10计算就可以了.这样一个时间换空间的办法可以让我们在数据集有10000个维度的情况下,由存储10000 x 10000的矩阵变为了存储十个1 x 10000的10个向量,有效节省了内存空间.
但是,仅仅是这样还是不够的,因为当迭代次数非常大的时候,我们的内存同样存不下。这个时候只能丢掉一些存不下的数据。假设我们设置的存储向量数为100,当s和y迭代超过100时,就会扔掉第一个s和y,存储
s
2
s_2
s2到
s
101
s_{101}
s101和
y
2
y_2
y2到
y
101
y_{101}
y101,每多一次迭代就对应的扔掉最前边的s和y。假如最后收敛次数是1000的话,只保留s901,y901从而计算出D901 ,然后再保留s902,y902计算出D902,依次根据s1000,y1000,从而算出D1000,按理说需要D900才能计算出D901 ,此时我们粗暴的将D901置为单位矩阵I,这样虽然损失了精度,但确可以保证使用有限的内存将函数的解通过BFGS算法求得到。所以L-BFGS算法可以理解为对BFGS算法的又一次近似。
虽然L-BFGS算法是线性收敛,但是每次迭代的开销非常小,因此L-BFGS算法执行速度还是很快的,而且由于每一步迭代都能保证近似矩阵的正定,因此算法的鲁棒性还是很强的。
算法L-BFGS的步骤如下所示。
到这里,L-BFGS算法的逻辑和由来就已经讲解完毕了文章中唯一没有讲到的地方就是BFGS算法的推导过程,因为推导过程比较长而且不是我们学习的重点.如果大家有兴趣的话可以去百度BFGS算法推导过程.
示例
Rosenbrock函数
x=1时,函数取得最小值0。
Rosenbrock 函数的梯度向量:
该表达式适用于i=1,2,…,N-2。 特殊情况是x0,xN0-1偏导数:
优化问题:
min
f
(
x
)
\min f(x)
minf(x)
Python 求解
import numpy as np
from scipy.optimize import minimize
def rosen(x):
"""The Rosenbrock function"""
return sum(100.0*(x[1:]-x[:-1]**2.0)**2.0 + (1-x[:-1])**2.0)
def rosen_der(x):
xm = x[1:-1]
xm_m1 = x[:-2]
xm_p1 = x[2:]
der = np.zeros_like(x)
der[1:-1] = 200*(xm-xm_m1**2) - 400*(xm_p1 - xm**2)*xm - 2*(1-xm)
der[0] = -400*x[0]*(x[1]-x[0]**2) - 2*(1-x[0])
der[-1] = 200*(x[-1]-x[-2]**2)
return der
x0 = np.array([1.3, 0.7, 0.8, 1.9, 1.2])
res = minimize(rosen, x0, method='BFGS', jac=rosen_der,
options={'disp': True})
print(res.x)
原文链接:https://blog.youkuaiyun.com/weixin_34258078/article/details/93755754
参考
https://blog.youkuaiyun.com/weixin_39445556/article/details/84502260
https://www.bilibili.com/read/cv11416331
https://en.wikipedia.org/wiki/Broyden%E2%80%93Fletcher%E2%80%93Goldfarb%E2%80%93Shanno_algorithm
https://zhuanlan.zhihu.com/p/29672873
https://blog.youkuaiyun.com/yskyskyer123/article/details/81145697
https://zhuanlan.zhihu.com/p/29672873
https://docs.scipy.org/doc/scipy/reference/tutorial/optimize.html#id22