拟牛顿法之L-BFGS算法

关于优化算法的求解,比如有梯度下降法,坐标下降法,牛顿法和拟牛顿法。
梯度下降法是基于目标函数梯度的,算法的收敛速度是线性的,并且当问题是病态时或者问题规模较大时,收敛速度尤其慢(几乎不适用);
坐标下降法虽然不用计算目标函数的梯度,但是其收敛速度依然很慢,因此它的适用范围也有局限;
牛顿法是基于目标函数的二阶导数(海森矩阵)的,其收敛速度较快,迭代次数较少,尤其是在最优值附近时,收敛速度是二次的。但牛顿法的问题在于当海森矩阵稠密时,每次迭代的计算量比较大,因为每次都会计算目标函数的海森矩阵的逆,这样一来,当问题规模较大时,不仅计算量大(有时大到不可计算),而且需要的存储空间也多,因此牛顿法在面对海量数据时由于每一步迭代的开销巨大而变得不适用;
拟牛顿法是在牛顿法的基础上引入了海森矩阵的近似矩阵,避免每次迭代都要计算海森矩阵的逆,拟牛顿法的收敛速度介于梯度下降法和牛顿法之间,是超线性的。拟牛顿法的问题也是当问题规模很大时,近似矩阵变得很稠密,在计算和存储上也有很大的开销,因此变得不实用。
另外需要注意的是,牛顿法在每次迭代时不能总是保证海森矩阵是正定的,一旦海森矩阵不是正定的,优化方向就会“跑偏”,从而使得牛顿法失效,也说明了牛顿法的鲁棒性较差。拟牛顿法用海森矩阵的逆矩阵来替代海森矩阵,虽然每次迭代不能保证是最优的优化方向,但是近似矩阵始终是正定的,因此算法总是朝着最优值的方向在搜索。
本文将介绍一种在实际工程中解决大规模优化问题时必然会用到的优化算法:L-BFGS算法。
L-BFGS(Limited-Memory BFGS)是BFGS算法在受限内存时的一种近似算法,而BFGS是数学优化中一种无约束最优化算法。L-BFGS算法就是对拟牛顿算法的一个改进。它的名字已经告诉我们它是基于拟牛顿法BFGS算法的改进。L-BFGS算法的基本思想是:算法只保存并利用最近m次迭代的曲率信息来构造海森矩阵的近似矩阵。
在介绍L-BFGS算法之前,我们先来简单回顾下BFGS算法。

BFGS算法

BFGS校正公式为:

Bk+1=BkBksksTkBksTkBksk+ykyTkyTksk

利用Sherman-Morrison公式可对上式进行变换,得到:
B1k+1=(IskyTkyTksk)TB1k(IyksTkyTksk)+sksTkyTksk

Hk+1=B1k+1 ,则得到:
H1k+1=(IskyTkyTksk)TH1k(IyksTkyTksk)+sksTkyTksk

BGFS算法存在的问题

在BFGS算法中,需要保存近似Hessian矩阵,特别在高维数据时,会占用大量的存储空间,而在实际运算中,我们只需要方向 dk ,因此L-BFGS产生了,L-BFGS是对BFGS算法的一种改进算法。在L-BFGS算法中,只保存最近的次迭代信息,以降低数据的存储空间。

L-BFGS算法思路

ρk=1yTksk,Vk=IyksTkyTksk ,则BFGS算法中的 Hk+1 可以表示为:

Hk+1=VTkHkVk+ρksksTk

若在初始时,假定初始的矩阵 H0=I ,则我们可以得到:
H1H2Hk+1=VT0H0V0+ρ0s0sT0=VT1H1V1+ρ1s1sT1=VT1(VT0H0V0+ρ0s0sT0)V1+ρ1s1sT1=VT1VT0H0V0V1+VT1ρ0s0sT0V1+ρ1s1sT1=(VTkVTk1VT1VT0)H0(V0V1Vk1Vk)+(VTkVTk1VT1)ρ1s1sT1(V1Vk1Vk)++VTkρk1sk1sTk1Vk+ρksksTk

若此时,只保留最近的m步:
Hk+1=(VTkVTk1VTkm)H0(VkmVk1Vk)+(VTkVTk1VTkm)ρ1s1sT1(VkmVk1Vk)++VTkρk1sk1sTk1Vk+ρksksTk

这样在L-BFGS算法中,不再保存完整的Hessian矩阵,而是存储向量序列 {sk} {yk} ,需要矩阵时,使用向量序列 {sk} {yk} 计算就可以得到,而向量序列 {sk} {yk} 也不是所有都要保存,只要保存最新的m步向量即可。

Hk 一个在实践中经常用到的有效方法为:

Hk=rkI
rk=sTk1yk1yTk1yk1

利用最近一次的曲率信息来估计真实Hessian矩阵的大小,这样使得当前搜索方向较为理想,不至于跑得太偏。

见:
https://wenku.baidu.com/view/cd610728fe4733687e21aae3.html
http://www.tuicool.com/articles/EviQ32m
http://blog.csdn.net/acdreamers/article/details/44728041

L-BFGS算法中的方向的计算方法

qkforiαiqiendrkm1forβiriendEnd,dk=rfk=k1 to km do=ρisTiqi+1=qi+1αiyifor=H0qkmi=km,km+1 to k1 do=ρiyTiri1=ri1+si(αiβi)forTheresultisHk+1f=r

算法(参照BFGS算法

lbfgs.py

#coding:UTF-8
'''
Created on 2017年4月24日

@author: zhangdapeng
'''
from numpy import *
from function import *

def lbfgs(fun, gfun, x0):
    result = []#保留最终的结果
    maxk = 500#最大的迭代次数
    delta = 0.55
    sigma = 0.4

    H0 = eye(shape(x0)[0])

    #s和y用于保存最近m个,这里m取6
    s = []
    y = []
    m = 6
    epsilon=1e-10
    k = 1
    gk = mat(gfun(x0))#计算梯度
    dk = -H0 * gk
    while (k < maxk):             
        n = 0
        mk = 0
        gk = mat(gfun(x0))#计算梯度
        if linalg.norm(gk,1)<epsilon:
            break
        while (n < 20):
            newf = fun(x0 + delta ** n * dk)
            oldf = fun(x0)
            if (newf < oldf + sigma * (delta ** n) * (gk.T * dk)[0, 0]):
                mk = n
                break
            n = n + 1

        #LBFGS校正
        x = x0 + delta ** mk * dk
        #print x

        #保留m个
        if k > m:
            s.pop(0)
            y.pop(0)

        #计算最新的
        sk = x - x0
        yk = gfun(x) - gk

        s.append(sk)
        y.append(yk)

        #two-loop的过程
        t = len(s)
        qk = gfun(x)
        a = []
        for i in range(t):#i值从大到小
            alpha = (s[t - i - 1].T * qk) / (y[t - i - 1].T * s[t - i - 1])
            qk = qk - alpha[0, 0] * y[t - i - 1]
            a.append(alpha[0, 0])
        r = H0 * qk

        for i in range(t):
            beta = (y[i].T * r) / (y[i].T * s[i])
            r = r + s[i] * (a[t - i - 1] - beta[0, 0])


        if (yk.T * sk > 0):
            dk = -r            

        k = k + 1
        x0 = x
        result.append(fun(x0))

    return result

function.py

#coding:UTF-8
'''
Created on 2017年4月24日

@author: zhangdapeng
'''

from numpy import *

#fun  原始函数
def fun(x):  
    return 100 * (x[0,0] ** 2 - x[1,0]) ** 2 + (x[0,0] - 1) ** 2  

#对x1,x2求导后的函数  
def gfun(x):  
    result = zeros((2, 1))  
#     对x1求导
    result[0, 0] = 400 * x[0,0] * (x[0,0] ** 2 - x[1,0]) + 2 * (x[0,0] - 1)  
    result[1, 0] = -200 * (x[0,0] ** 2 - x[1,0])  #对x2求导
    return result

testLBFGS.py

#coding:UTF-8 
'''
Created on 2017年4月24日

@author: zhangdapeng
'''
from lbfgs import *

import matplotlib.pyplot as plt  

x0 = mat([[-1.2], [1]])
result = lbfgs(fun, gfun, x0)
print(result[-1])

n = len(result)
ax = plt.figure().add_subplot(111)
x = arange(0, n, 1)
y = result
ax.plot(x,y)

plt.show()

输出:

2.50088628478e-27

输出图片

这里写图片描述

https://en.wikipedia.org/wiki/Limited-memory_BFGS
http://blog.csdn.net/google19890102/article/details/46389869

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值