Line Search and Quasi-Newton Methods 線性搜索與擬牛頓法

Gradient Descent

機器學習中不少模型的參數估計都要用到優化算法,梯度降低是其中最簡單也用得最多的優化算法之一。梯度降低(Gradient Descent)[3]也被稱之爲最快梯度(Steepest Descent),可用於尋找函數的局部最小值。梯度降低的思路爲,函數值在梯度反方向降低是最快的,只要沿着函數的梯度反方向移動足夠小的距離到一個新的點,那麼函數值一定是非遞增的,如圖1所示。算法

梯度降低思想的數學表述以下:app

b=aαF(a)f(a)f(b)(1)(1)b=a−α∇F(a)⇒f(a)≥f(b)

其中f(x)f(x)爲存在下界的可導函數。根據該思路,若是咱們從x0x0爲出發點,每次沿着當前函數梯度反方向移動必定距離αkαk,獲得序列x0,x1,,xnx0,x1,⋯,xn:機器學習

xk+1=xkαkf(xk),0kn(2)(2)xk+1=xk−αk∇f(xk),0≤k≤n

對應的各點函數值序列之間的關係爲:ide

f(x0)f(x1)f(x2)f(xn)(3)(3)f(x0)≥f(x1)≥f(x2)≥⋯≥f(xn)

很顯然,當nn達到必定值時,函數f(x)f(x)是會收斂到局部最小值的。算法1簡單描述了通常化的梯度優化方法。在算法1中,咱們須要選擇一個搜索方向dkdk知足如下關係:函數

f(xk+αdk)<f(xk)α(0,ϵ](4)(4)f(xk+αdk)<f(xk)∀α∈(0,ϵ]

dk=f(x)dk=−∇f(x)時f(x)f(x)降低最快,可是隻要知足f(xk)Tdk<0∇f(xk)Tdk<0的dkdk均可以做爲搜素方向。通常搜索方向表述爲以下形式:學習

dk=Bkf(xk)(5)(5)dk=−Bk∇f(xk)

其中BkBk爲正定矩陣。當Bk=IBk=I時對應最快梯度降低算法;當Bk=H(xk)1Bk=H(xk)−1時對應牛頓法,若是H(xk)=2f(xk)H(xk)=∇2f(xk)爲正定矩陣。 在迭代過程當中用於更新xkxk的步長αkαk能夠是常數也能夠是變化的。若是αkαk足夠小,收斂是能夠獲得保證的,但這意味這迭代次數nn要很大時函數纔會收斂(圖2(a));若是αkαk比較大,更新後的點極可能越過局部最優解(圖2(b))。有什麼方法能夠幫助咱們自動肯定最優步長呢?下面要說的線性搜索就包含一組解決方案。測試

 

Line Search

在給定搜索方向dkdk的前提下,線性搜索要解決的問題以下:優化

α=argminα0h(α)=argminα0f(xk+αdk)(6)(6)α=argminα≥0h(α)=argminα≥0f(xk+αdk)

若是h(α)h(α)是可微的凸函數,咱們能經過解析解直接求得上式最優的步長;但非線性的優化問題須要經過迭代形式求得近似的最優步長。對於上式,局部或全局最優解對應的導數爲h(α)=f(xk+αdk)Tdk=0h′(α)=∇f(xk+αdk)Tdk=0。由於dkdk與f(xk)f(xk)在xkxk處的梯度方向夾角大於90度,所以h(0)0h′(0)≤0,若是能找到α^α^使得h(α^)>0h′(α^)>0,那麼一定存在α[0,α^)α⋆∈[0,α^)使得h(α)=0h′(α⋆)=0。有多種迭代算法能夠求得αα⋆的近似值,下面選擇幾種典型的介紹。ui

 

Bisection Search

二分線性搜索(Bisection Line Search)[2]可用於求解函數的根,其思想很簡單,就是不斷將現有區間劃分爲兩半,選擇一定含有使h(α)=0h′(α)=0的半個區間做爲下次迭代的區間,直到尋得h(α)0h′(α⋆)≈0爲止,算法描述見2。二分線性搜素能夠確保h(α)h(α)是收斂的,只要h(α)h(α)在區間(0,α^)(0,α^)上是連續的且h(0)h′(0)和h(α^)h(α^)異號。經歷nn次迭代後,當前區間[αl,αh][αl,αh]的長度爲:atom

L=(12)nα^(7)(7)L=(12)nα^

由迭代的終止條件之一αhαlϵαh−αl≥ϵ知迭代次數的上界爲:

Lϵk[log2(α^ϵ)](8)(8)L≤ϵ⇒k≤[log2⁡(α^ϵ)]

下面給出二分搜索的Python代碼

 

複製代碼
 1 def bisection(dfun,theta,args,d,low,high,maxiter=1e4):
 2     """
 3     #Functionality:find the root of the function(fun) in the interval [low,high]
 4     #@Parameters
 5     #dfun:compute the graident of function f(x)
 6     #theta:Parameters of the model
 7     #args:other variables needed to compute the value of dfun
 8     #[low,high]:the interval which contains the root
 9     #maxiter:the max number of iterations
10     """
11     eps=1e-6
12     val_low=np.sum(dfun(theta+low*d,args)*d.T)
13     val_high=np.sum(dfun(theta+high*d,args)*d.T)
14     if val_low*val_high>0:
15         raise Exception('Invalid interval!')
16     iter_num=1
17     while iter_num<maxiter:
18         mid=(low+high)/2
19         val_mid=np.sum(dfun(theta+mid*d,args)*d.T)
20         if abs(val_mid)<eps or abs(high-low)<eps:
21             return mid
22         elif val_mid*val_low>0:
23             low=mid
24         else:
25             high=mid
26         iter_num+=1
複製代碼

Backtracking

回溯線性搜索(Backing Line Search)[1]基於Armijo準則計算搜素方向上的最大步長,其基本思想是沿着搜索方向移動一個較大的步長估計值,而後以迭代形式不斷縮減步長,直到該步長使得函數值f(xk+αdk)f(xk+αdk)相對與當前函數值f(xk)f(xk)的減少程度大於指望值(知足Armijo準則)爲止。Armijo準則(見圖3)的數學描述以下:

f(xk+αdk)f(xk)+c1αf(xk)Tdk(9)(9)f(xk+αdk)≤f(xk)+c1αf′(xk)Tdk

其中f:RnRf:Rn→R,c1(0,1)c1∈(0,1),αα爲步長,dkRndk∈Rn爲知足f(xk)Tdk<0f′(xk)Tdk<0的搜索方向。可是僅憑Armijo準則不足以求得較好的步長,根據前面的梯度降低的知識可知,只要αα足夠小就能知足Armijo準則。所以經常使用的策略就是從較大的步長開始,而後以τ(0,1)τ∈(0,1)的速度縮短步長,直到知足Armijo準則爲止,這樣選出來的步長不至於過小,對應的算法描述見3。前面介紹的二分線性搜索的目標是求得知足h(α)0h′(α)≈0的最優步長近似值,而回溯線性搜索放鬆了對步長的約束,只要步長能使函數值有足夠大的變化便可。前者能夠少計算幾回搜索方向,但在計算最優步長上花費了很多代價;後者退而求其次,找到一個差很少的步長便可,那麼代價就是要多計算幾回搜索方向。接下來,咱們要證實回溯線性搜索在Armijo準則下的收斂性問題[6]。由於h(0)=f(xk)Tdk<0h′(0)=f′(xk)Tdk<0,且0<c1<10<c1<1,則有

h(0)<c1h(0)<0(10)(10)h′(0)<c1h′(0)<0

根據導數的基本定義,結合上式,有以下關係:

h(0)=limα0h(α)h(0)α=limα0f(xk+αdk)f(xk)α<ch(0)(11)(11)h′(0)=limα→0h(α)−h(0)α=limα→0f(xk+αdk)−f(xk)α<ch′(0)

所以,存在一個步長α^>0α^>0,對任意的α(0,α^)α∈(0,α^),下式均成立

f(xk+αdk)f(xk)α<cf(xk)Tdk(12)(12)f(xk+αdk)−f(xk)α<cf′(xk)Tdk

α(0,α^),f(xk+αdk)<f(xk)+cαf(xk)Tdk∀α∈(0,α^),f(xk+αdk)<f(xk)+cαf′(xk)Tdk。 下面給出基於Armijo準則的線性搜索Python代碼:

 

複製代碼
 1 def ArmijoBacktrack(fun,dfun,theta,args,d,stepsize=1,tau=0.5,c1=1e-3):
 2     """
 3     #Functionality:find an acceptable stepsize via backtrack under Armijo rule
 4     #@Parameters
 5     #fun:compute the value of objective function
 6     #dfun:compute the gradient of objective function
 7     #theta:a vector of parameters of the model
 8     #stepsize:initial step size
 9     #c1:sufficient decrease Parameters
10     #tau:rate of shrink of stepsize
11     """
12     slope=np.sum(dfun(theta,args)*d.T)
13     obj_old=costFunction(theta,args)
14     theta_new=theta+stepsize*d
15     obj_new=costFunction(theta_new,args)
16     while obj_new>obj_old+c1*stepsize*slope:
17         stepsize*=tau
18         theta_new=theta+stepsize*d
19         obj_new=costFunction(theta_new,args)
20     return stepsize
複製代碼

Interpolation

基於Armijo準則的回溯線性搜索的收斂速度沒法獲得保證,特別是要回退不少次後才能落入知足Armijo準則的區間。若是咱們根據已有的函數值和導數信息,採用多項式插值法(Interpolation)[12,6,5,9]擬合函數,而後根據該多項式函數估計函數的極值點,這樣選擇合適步長的效率會高不少。 假設咱們只有xkxk處的函數值f(xk)f(xk)及其倒數f(xk)f′(xk),且第一次嘗試的步長爲α0α0。若是α0α0不知足條件,那麼咱們根據這些信息能夠構造一個二次近似函數hq(α)hq(α)

hq(α)=(h(α0)h(0)α0h(0)α20)α2+h(0)α+h(0)(13)(13)hq(α)=(h(α0)−h(0)−α0h′(0)α02)α2+h′(0)α+h(0)

注意,該二次函數知足hq(0)=h(0)hq(0)=h(0),hq(0)=h(0)hq′(0)=h′(0)和hq(α0)=h(α0)hq(α0)=h(α0),如圖4(a)所示。接下來,根據hq(α)hq(α)的最小值估計下一個步長:

α1=h(0)α202[h(0)+h(0)α0h(α0)](14)(14)α1=h′(0)α022[h(0)+h′(0)α0−h(α0)]

若是α1α1仍然不知足條件,咱們能夠繼續重複上述過程,直到獲得的步長知足條件爲止。假設咱們在整個線性搜索過程當中都用二次插值函數,那麼最好有c1(0,0.5]c1∈(0,0.5],爲何呢?簡單證實一下:若是α0α0不知足Armijo準則,那麼一定存在比α0α0小的步長知足該準則,因此利用二次插值函數估算的步長α1<α0α1<α0才合理。結合α0α0不知足Armijo準則和α1<α0α1<α0,可知c10.5c1≤0.5。 若是咱們已經嘗試了多個步長,卻每次只用上一次步長的相關信息構造二次函數,未免是對計算資源的浪費,其實咱們能夠利用多個步長的信息構造信息量更大更準確的插值函數的。在計算導數的代價大於計算函數值的代價時,應儘可能避免計算h(α)h′(α),下面給出一個三次插值函數hc(α)hc(α),如圖4(b)所示

hc(α)=aα3+bα2+h(0)α+h(0)(15)(15)hc(α)=aα3+bα2+h′(0)α+h(0)

其中

[ab]=1α2i1α2i(αiαi1)[α2i1α3i1α2iα3i][h(αi)h(0)h(0)αih(αi1)h(0)h(0)αi1](16)(16)[ab]=1αi−12αi2(αi−αi−1)[αi−12−αi2−αi−13αi3][h(αi)−h(0)−h′(0)αih(αi−1)−h(0)−h′(0)αi−1]

hc(α)hc(α)求導,可得極值點αi+1[0,αi]αi+1∈[0,αi]的形式以下:

αi+1=b+b23ah(0)−−−−−−−−−−√3a(17)(17)αi+1=−b+b2−3ah′(0)3a

利用以上的三次插值函數求解下一個步長的過程不斷重複,直到步長知足條件爲止。若是出現a=0a=0的狀況,三次插值函數退化爲二次插值函數,在實現該算法時須要注意這點。在此過程當中,若是αiαi過小或αi1αi−1與αiαi太接近,須要重置αi=αi1/2αi=αi−1/2,該保護措施(safeguards)保證下一次的步長不至於過小[6,5]。爲何會有這個做用呢?1)由於αi+1[0,αi]αi+1∈[0,αi],因此當αiαi很小時αi+1αi+1也很小;2)當αi1αi−1與αiαi太靠近時有aba≈b≈∞,根據αi+1αi+1的表達式可知αi+10αi+1≈0。 可是,在不少狀況下,計算函數值後只需付出較小的代價就能順帶計算出導數值或其近似值,這使得咱們能夠用更精確的三次Hermite多項式[6]進行插值,如圖4(c)所示

H3(α)=[1+2αiααiαi1][ααi1αiαi1]2h(αi)+[1+2ααi1αiαi1][αi+1ααiαi1]2h(αi1)+(ααi)[ααi1αiαi1]2h(αi)+(ααi1)[αiααiαi1]2h(αi1)(18)(18)H3(α)=[1+2αi−ααi−αi−1][α−αi−1αi−αi−1]2h(αi)+[1+2α−αi−1αi−αi−1][αi+1−ααi−αi−1]2h(αi−1)+(α−αi)[α−αi−1αi−αi−1]2h′(αi)+(α−αi−1)[αi−ααi−αi−1]2h′(αi−1)

其中,三次Hermite多項式知足H3(αi1)=h(αi1)H3(αi−1)=h(αi−1),H3(αi)=h(αi)H3(αi)=h(αi),H3(αi1)=h(αi1)H3′(αi−1)=h′(αi−1)和H3(αi)=h(αi)H3′(αi)=h′(αi)。H(α)H(α)的最小值只可能在兩側的端點或者極值點處,令H3(α)=0H3′(α)=0可得下一個步長:

αi+1=αi(αiαi1)[h(αi)+d2d1h(αi)h(αi1)+2d2](19)(19)αi+1=αi−(αi−αi−1)[h′(αi)+d2−d1h′(αi)−h′(αi−1)+2d2]

其中

d1=h(αi)+h(αi1)3[h(αi)h(αi1)αiαi1](20)(20)d1=h′(αi)+h′(αi−1)−3[h(αi)−h(αi−1)αi−αi−1]
d2=sign(αiαi1)d21h(αi1)h(αi)−−−−−−−−−−−−−−−√(21)(21)d2=sign(αi−αi−1)d12−h′(αi−1)h′(αi)

下面給出二次插值及三次插值的Python代碼:

 

複製代碼
 1 def quadraticInterpolation(a,h,h0,g0):
 2     """
 3     #Functionality:Approximate h(a) with a quadratic function and return its stationary point
 4     #@Parameters
 5     #a:current stepsize
 6     #h:a function value about stepsize,h(a)=f(x_k+a*d)
 7     #h:h(0)=f(x_k)
 8     #g0:h'(0)=f'(0)
 9     """
10     numerator=g0*a**2
11     denominator=2*(g0*a+h0-h)
12     if abs(denominator)<1e-12:#indicates that a is almost 0
13         return a
14     return numerator/denominator
複製代碼
複製代碼
def cubicInterpolation(a0,h0,a1,h1,h,g):
    """
    #Functionality:Approximate h(x) with a cubic function and return its stationary point
    #This version of cubic interpolation computes h'(x) as few as possible,suitable for the case in which computing derivative is more expensive than computing function values
    #@Parameters
    #a0 and a1 are stepsize it previous two iterations
    #h0:h(a0)
    #h1:h(a1)
    #h:h(0)=f(x)
    #g:h'(0)
    """
    mat=matlib.matrix([[a0**2,-a1**2],[-a0**3,a1**3]])
    vec=matlib.matrix([[h1-h-g*a1],[h0-h-g*a0]])
    ab=mat*vec/(a0**2*a1**2*(a1-a0))
    a=ab[0,0]
    b=ab[1,0]
    if abs(a)<1e-12:#a=0 and cubic function is a quadratic one
        return -g/(2*b)
    return (-b+np.sqrt(b**2-3*a*g))/(3*a)
複製代碼
複製代碼
def cubicInterpolationHermite(a0,h0,g0,a1,h1,g1):
    """
    #Functionality:Approximate h(a) with a cubic Hermite polynomial function and return its stationary point
    #This version of cubic interpolation computes h(a) as few as possible,suitable for the case in which computing derivative is easier than computing function values
    #@Parameters
    #a0 and a1 are stepsize it previous two iterations
    #h0:h(a0)
    #g0:h'(a0)
    #h1:h(a1)
    #g1:h'(a1)
    """
    d1=g0+g1-3*(h1-h0)/(a1-a0)
    d2=np.sign(a1-a0)*np.sqrt(d1**2-g0*g1)
    res=a1-(a1-a0)*(g1+d2-d2)/(g1-g0+2*d2)
    return res
複製代碼

基於Armijo準則的線性搜索的算法描述以下[4]對應的Armijo線性搜索的Python代碼以下:

複製代碼
 1 def ArmijoLineSearch(fun,dfun,theta,args,d,a0=1,c1=1e-3,a_min=1e-7,max_iter=1e5):
 2     """
 3     #Functionality:Line search under Armijo condition with quadratic and cubic interpolation
 4     #@Parameters
 5     #fun:objective Function
 6     #dfun:compute the gradient of fun
 7     #theta:a vector of parameters of the model
 8     #args:other variables needed for fun and func
 9     #d:search direction
10     #a0:initial stepsize
11     #c1:constant used in Armijo condition
12     #a_min:minimun of stepsize
13     #max_iter:maximum of the number of iterations
14     """
15     eps=1e-6
16     c1=min(c1,0.5)#c1 should<=0.5
17     a_pre=h_pre=g_pre=0
18     a_cur=a0
19     f_val=fun(theta,args) #h(0)=f(0)
20     g_val=np.sum(dfun(theta,args)*d.T) #h'(0)=f'(x)^Td
21     h_cur=g_cur=0
22     k=0
23     while a_cur>a_min and k<max_iter:
24         h_cur=fun(theta+a_cur*d,args)
25         g_cur=np.sum(dfun(theta+a_cur*d,args)*d.T)
26         if h_cur<=f_val+c1*a_cur*g_val: #meet Armijo condition
27             return a_cur
28         if not k: #k=0,use quadratic interpolation
29             a_new=quadraticInterpolation(a_cur,h_cur,f_val,g_val)
30         else: #k>0,use cubic Hermite interpolation
31             a_new=cubicInterpolationHermite(a_pre,h_pre,g_pre,a_cur,h_cur,g_cur)
32         if abs(a_new-a_cur)<eps or abs(a_new)<eps: #safeguard procedure
33             a_new=a_cur/2
34         a_pre=a_cur
35         a_cur=a_new
36         h_pre=h_cur
37         g_pre=g_cur
38         k+=1
39     return a_min #failed search
複製代碼

Wolfe Search

前面說到單憑Armijo準則(不考慮回溯策略)選出的步長可能過小,爲了排除這些微小的步長,咱們加上曲率的約束條件(如圖5所示)

h(α)=f(xk+αdk)Tdkc2f(xk)Tdk(22)(22)h′(α)=f′(xk+αdk)Tdk≥c2f′(xk)Tdk

其中c2(c1,1)c2∈(c1,1),c1c1爲Armijo準則中的常量。h(α)h′(α)爲很小的負數甚至爲正數時,說明從xkxk沿着dkdk移動αα後的函數梯度方向與搜索方向的夾角接近90度,繼續向前移動已經不能很明顯減少函數值了,此時能夠中止沿着dkdk繼續搜索;反之,說明繼續減少函數值的空間仍是很大的,能夠繼續向前搜索。Armijo準則與曲率約束二者合起來稱爲Wolfe準則[5]:

{f(xk+αdk)f(xk+αdk)Tdkf(xk)+c1αf(xk)Tdkc2f(xk)Tdk(23)(23){f(xk+αdk)≤f(xk)+c1αf′(xk)Tdkf′(xk+αdk)Tdk≥c2f′(xk)Tdk

其中0<c1<c2<10<c1<c2<1。如圖6所示,知足Wolfe準則的步長也許離h(α)h(α)的極值點較遠。咱們能夠修改曲率約束條件使得步長落到h(α)h(α)的極值點的一個較寬的領域中,強Wolfe準則對步長αα的約束以下:

{f(xk+αdk)|f(xk+αdk)Tdk|f(xk)+c1αf(xk)Tdkc2|f(xk)Tdk|(24)(24){f(xk+αdk)≤f(xk)+c1αf′(xk)Tdk|f′(xk+αdk)Tdk|≥c2|f′(xk)Tdk|

強Wolfe準則不容許h(α)h′(α)爲太大的正數,能夠排除遠離極值點的區間。那麼究竟是否存在知足強Wolfe準則的步長呢?假設h(α)=f(xk+αdk)h(α)=f(xk+αdk)連續可微,在整個α>0α>0的定義域上存在下界。由於0<c1<10<c1<1,因此l(α)=f(xk)+αc1f(xk)Tdkl(α)=f(xk)+αc1f′(xk)Tdk必然與h(α)h(α)至少有一個交點。假設αα′爲最小的交點對應的步長,則有

f(xk+αdk)=f(xk)+αc1f(xk)Tdk(25)(25)f(xk+α′dk)=f(xk)+α′c1f′(xk)Tdk

那麼對於知足α(0,α)α∈(0,α′)的步長必然都知足Armijo準則。根據零值定理,存在α′′(0,α)α″∈(0,α′)知足

f(xk+αdk)f(xk)=αf(xk+α′′dk)Tdk(26)(26)f(xk+α′dk)−f(xk)=α′f′(xk+α″dk)Tdk

結合上面兩個關係式,由0<c1<c20<c1<c2和f(xk)Tdk<0f′(xk)Tdk<0,可得

f(xk+α′′dk)Tdk=c1f(xk)Tdk>c2f(xk)Tdk(27)(27)f′(xk+α″dk)Tdk=c1f′(xk)Tdk>c2f′(xk)Tdk

由此可知,α′′α″知足強Wolfe準則。若是h(α)h(α)是一個較爲平滑的函數,那麼包含α′′α″的較小領域都會知足強Wolfe準則。 若是在線性搜索過程當中利用強Wolfe準則,能夠更精確得找到更靠近極值點的步長,在目前線性搜索中用得不少。基於強Wolfe準則的線性搜索包含兩個階段:第一個階段從初始步長開始,不斷增大步長,直到找到一個知足強Wolfe準則的步長或包含該步長的區間爲止;第二個階段是在已知包含知足強Wolfe準則步長區間的基礎上,不斷縮減區間,直到找到知足強Wolfe準則的步長爲止。基於強Wolfe準則的線性搜索算法描述以下5

 

在算法5中,αnewαnew的更新是由於在區間(αpre,αcur)(αpre,αcur)內沒有知足Wolfe準則的步長,因此要選取一個大於αcurαcur的步長αnewαnew。在算法中,咱們是用二次插值函數計算αnewαnew的,因此要求0<c1<0.50<c1<0.5。固然,也能夠用其餘方法,好比讓αcurαcur乘以一個大於1的常數,只要能較快找到一個包含知足Wolfe的區間便可。因此,該算法每次嘗試的步長αcurαcur的逐漸遞增的;一旦找到了包含知足Wolfe準則的步長的區間,當即調用zoomzoom函數不斷縮短區間,並返回知足Wolfe的步長。根據算法邏輯,咱們能夠推斷出αpreαpre知足Armijo準則,但違背曲率約束,並且導數爲負數。由上述三個條件,可知αpreαpre一定位於知足Wolfe準則的區間的左側的呈降低趨勢的曲線上,只要αcurαcur位於該區間的右側便可。那麼怎樣判斷區間(αpre,αcur)(αpre,αcur)是否包含知足Wolfe準則的步長呢?下面給出三種αcurαcur位於該區間的右側的充分條件:

  1. αcurαcur不知足Armijo準則;
  2. h(αcur)h(αpre)h(αcur)≥h(αpre);
  3. h(αcur)0h′(αcur)≥0

這一點結合圖7就很容易理解了,我在圖中分別用紅色和綠色點標註了αpreαpre和αcurαcur可能的位置,藍色帶數字的圓圈註明了αcurαcur知足哪些條件。基於Wolfe準則的線性搜索Python代碼以下:

複製代碼
 1 def WolfeLineSearch(fun,dfun,theta,args,d,a0=1,c1=1e-4,c2=0.9,a_min=1e-7,max_iter=1e5):
 2     """
 3     #Functionality:find a stepsize meeting Wolfe condition
 4     #@Parameters
 5     #fun:objective Function
 6     #dfun:compute the gradient of fun
 7     #theta:a vector of parameters of the model
 8     #args:other variables needed for fun and func
 9     #d:search direction
10     #a0:intial stepsize
11     #c1:constant used in Armijo condition
12     #c2:constant used in curvature condition
13     #a_min:minimun of stepsize
14     #max_iter:maximum of the number of iterations
15     """
16     eps=1e-16
17     c1=min(c1,0.5)
18     a_pre=0
19     a_cur=a0
20     f_val=fun(theta,args) #h(0)=f(x)
21     g_val=np.sum(dfun(theta,args)*d.T)
22     h_pre=f_val #h'(0)=f'(x)^Td
23     k=0
24     while k<max_iter and abs(a_cur-a_pre)>=eps:
25         h_cur=fun(theta+a_cur*d,args) #f(x+ad)
26         if h_cur>f_val+c1*a_cur*g_val or h_cur>=h_pre and k>0:
27             return zoom(fun,dfun,theta,args,d,a_pre,a_cur,c1,c2)
28         g_cur=np.sum(dfun(theta+a_cur*d,args)*d.T)
29         if abs(g_cur)<=-c2*g_val:#satisfy Wolfe condition
30             return a_cur
31         if g_cur>=0:
32             return zoom(fun,dfun,theta,args,d,a_pre,a_cur,c1,c2)
33         a_new=quadraticInterpolation(a_cur,h_cur,f_val,g_val)
34         a_pre=a_cur
35         a_cur=a_new
36         h_pre=h_cur
37         k+=1
38     return a_min
複製代碼

zoom函數的算法描述見6。zoom函數中須要傳入搜尋區間[αlow,αhigh][αlow,αhigh],其中αlow<αhighαlow<αhigh。本文中的zoom函數與文獻[5]中的內容略有差別,可是本文的zoom函數思路更簡單和清晰。由算法5中分析獲得的調用zoom函數的條件,知道αlowαlow必須知足Armijo準則,且位於全部知足Wolfe準則的步長的左側。咱們先取[αlow,αhigh][αlow,αhigh]區間的中值做爲下一個測試的步長αnewαnew,若是剛好知足Wolfe準則,則直接返回。若是αnewαnew違反Armijo準則或大於h(αlow)h(αlow),顯然區間[αlow,αnew][αlow,αnew]包含知足Wolfe準則的步長,所以用αnewαnew替換αhighαhigh以縮短區間長度;不然,αnewαnew必然也知足Armijo準則,若是h(αnew)>0h′(αnew)>0,則αnewαnew與αhighαhigh都在知足Wolfe準則的區間右側,用αnewαnew替代αhighαhigh,反之則用αnewαnew替代αlowαlow。上述的迭代過程不斷縮短步長,知道求得知足Wolfe準則的步長爲止。若是在有限迭代次數內搜索失敗,則返回必然知足Armijo準則的步長αlowαlow。

zoom函數對應的Python代碼以下:

複製代碼
 1 def zoom(fun,dfun,theta,args,d,a_low,a_high,c1=1e-3,c2=0.9,max_iter=1e4):
 2     """
 3     #Functionality:enlarge the interval to find a stepsize meeting Wolfe condition
 4     #@Parameters
 5     #fun:objective Function
 6     #dfun:compute the gradient of fun
 7     #theta:a vector of parameters of the model
 8     #args:other variables needed for fun and func
 9     #d:search direction
10     #[a_low,a_high]:interval containing a stepsize satisfying Wolfe condition
11     #c1:constant used in Armijo condition
12     #c2:constant used in curvature condition
13     #max_iter:maximum of the number of iterations
14     """
15     if a_low>a_high:
16         print('low:%f,high:%f'%(a_low,a_high))
17         raise Exception('Invalid interval of stepsize in zoom procedure')
18     eps=1e-16
19     h=fun(theta,args) #h(0)=f(x)
20     g=np.sum(dfun(theta,args)*d.T) #h'(0)=f'(x)^Td
21     k=0
22     h_low=fun(theta+a_low*d,args)
23     h_high=fun(theta+a_high*d,args)
24     if h_low>h+c1*a_low*g:
25         raise Exception('Left endpoint violates Armijo condition in zoom procedure')
26     while k<max_iter and abs(a_high-a_low)>=eps:
27         a_new=(a_low+a_high)/2
28         h_new=fun(theta+a_new*d,args)
29         if h_new>h+c1*a_new*g or h_new>h_low:
30             a_high=a_new
31             h_high=h_new
32         else:
33             g_new=np.sum(dfun(theta+a_new*d,args)*d.T)
34             if abs(g_new)<=-c2*g: #satisfy Wolfe condition
35                 return a_new 
36             if g_new*(a_high-a_low)>=0:
37                 a_high=a_new
38                 h_high=h_new
39             else:
40                 a_low=a_new
41                 h_low=h_new
42         k+=1
43     return a_low #a_low definitely satisfy Armijo condition
複製代碼

Newton's Method

牛頓法(Newton's method)[8]以迭代方式求解函數的根,其基本思想是從一個初始點出發,不斷在當前點xkxk處用切線近似函數f(x)f(x),並求得該切線與xx軸的交點做爲下一次的迭代初始點xk+1xk+1,直到找到f(x)=0f(x)=0的近似解爲止。Newton法可用於二次可微函數f(x)f(x)的最優化問題。 在xkxk處用二階泰勒展開來對f(xk)f(xk)其進行逼近。

f(xk+x)f(xk)+f(xk)x+12xTBkx(28)(28)f(xk+△x)≈f(xk)+f′(xk)△x+12△xTBk△x

如今,咱們的目標是在xkxk附近求得使f(x)f(x)取得極小值的x△x。將上式對x△x求導可得函數f(x)f′(x)在xk+1=xk+xxk+1=xk+△x處的線性近似以下:

f(xk+1)=f(xk)+Bk(xk+1xk)(29)(29)f′(xk+1)=f′(xk)+Bk(xk+1−xk)

其中Bk=2f(xk)Bk=∇2f(xk)爲f(x)f(x)在xkxk處對應的Hessian矩陣。因爲函數的極值點通常都對應f(x)=0f′(x)=0,令f(xk+1)=0f′(xk+1)=0並化簡可得迭代公式爲:

xk+1=xkB1kf(xk)(30)(30)xk+1=xk−Bk−1f′(xk)

牛頓迭代法收斂速度很快,對於二次函數能夠一次性找到最優解。但用於求解優化問題時,須要付出很大的代價求得函數的一階導數、二階導數及其逆矩陣。此外,有的函數還存在不可導、Hessian矩陣不可逆、迭代點之間存在循環(即xk+t=xkxk+t=xk)等情形,這些都成爲了牛頓法的應用障礙。牛頓迭代法用於求解極值點f(x)=0f′(x)=0的步驟見算法7。固然,也可用牛頓法求最優步長,只需將算法7中的函數f(x)f(x)替換爲關於步長的函數h(α)h(α)便可。

 

Quasi-Newton Method

擬牛頓(Quasi-Newton)[11]算法可用於求解函數的局部最優解,也就是那些導數爲0的駐點。牛頓法用於解決優化問題時,事先假設原函數可用二次函數近似,而後用一階和二階導數尋找局部最優解。而在擬牛頓算法中,不須要準確計算Hessian矩陣,取而代之的是運用下面的擬牛頓條件分析連續兩個梯度向量獲得的近似值矩陣BkBk:

f(xk+1)f(xk)Bk+1(xk+1xk)(31)(31)f′(xk+1)−f′(xk)≈Bk+1(xk+1−xk)

擬牛頓算法的算法流程見8,其基本思想是利用矩陣BkBk計算牛頓方向的近似值dkdk。各類擬牛頓算法的主要差別在於近似Hessian矩陣的更新策略,表1列出了部分主流的擬牛頓算法的迭代更新規則,其中sk=xk+1xk=αkB1kf(xk)sk=xk+1−xk=−αkBk−1f′(xk),yk=f(xk+1)f(xk)yk=f′(xk+1)−f′(xk)。擬牛頓算法中最經常使用的是BFGS,其針對有限內存的機器的算法變種L-BFGS[4]在機器學習領域又備受青睞。BFGS須要存儲n×nn×n的矩陣HkHk用於近似Hessian矩陣的逆矩陣;而L-BFGS僅須要存儲過去mm(mm通常小於10)對nn維的更新數據(x,f(xi))(x,f′(xi))便可。L-BFGS的空間複雜度爲線性,特別適用於變量很是多的優化問題。BFGS的算法描述很容易寫出來,以下:

 

複製代碼
 1 def BFGS(fun,dfun,theta,args,H=None,mode=0,eps=1e-12,max_iter=1e4):
 2     """
 3     #Functionality:find the minimum of objective function f(x)
 4     #@Parameters
 5     #fun:objective function f(x)
 6     #dfun:compute the gradient of f(x)
 7     #args:parameters needed by fun and dfun
 8     #theta:start vector of parameters of the model
 9     #H:initial inverse Hessian approximation
10     #mode:index of line search algorithm
11     """
12     x_pre=x_cur=theta
13     g=dfun(x_cur,args)
14     I=matlib.eye(theta.size)
15     if not H:#initialize H as an identity matrix
16         H=I
17     k=0
18     while k<max_iter and np.sum(np.abs(g))>eps:
19         d=-g*H
20         step=LineSearch(fun,dfun,x_pre,args,d,1,mode)
21         x_cur=x_pre+step*d
22         s=step*d
23         y=dfun(x_cur,args)-dfun(x_pre,args)
24         ys=np.sum(y*s.T)
25         if abs(ys)<eps:
26             return x_cur
27         change=(ys+np.sum(y*H*y.T))*(s.T*s)/(ys**2)-(H*y.T*s+s.T*y*H)/ys
28         H+=change
29         g=dfun(x_cur,args)
30         x_pre=x_cur
31         k+=1
32     return x_cur
複製代碼

下面咱們分析如何構造下L-BFGS的算法[10,13]。假設咱們如今處於優化過程的第k(km)k(k≥m)次迭代,參數爲xkxk,梯度gk=f(xk)gk=f′(xk),已經保存的mm條更新數據爲sk=xk+1xksk=xk+1−xk及yk=gk+1gkyk=gk+1−gk。咱們最終須要計算的是搜索方向dk=Hkgkdk=−Hkgk,因而令Vk=(IρkyksTk)Vk=(I−ρkykskT),ρk=1/(yTksk)ρk=1/(ykTsk),將表1中BFGS的HkHk的更新規則展開,咱們能夠獲得下式:

===HkgkVTk1Hk1Vk1gk+sk1ρk1sTk1gkVTk1VTk2Hk2Vk2Vk1gk+VTk1sk2ρk2sTk2Vk1gk+sk1ρk1sTk1gk(VTk1VTk2VTkm)Hkm(VkmVk2Vk1)gk+(VTk1VTk2VTkm+1)skmρkmsTkm(Vkm+1Vk1Vk)gk+(VTk1VTk2VTkm+2)skm+1ρkm+1sTkm+1(Vkm+2Vk2Vk1)gk++VTk1sk2ρk2sTk2Vk1gk+sk1ρk1sTk1gk(32)(32)Hkgk=Vk−1THk−1Vk−1gk+sk−1ρk−1sk−1Tgk=Vk−1TVk−2THk−2Vk−2Vk−1gk+Vk−1Tsk−2ρk−2sk−2TVk−1gk+sk−1ρk−1sk−1Tgk=(Vk−1TVk−2T⋯Vk−mT)Hk−m(Vk−m⋯Vk−2Vk−1)gk+(Vk−1TVk−2T⋯Vk−m+1T)sk−mρk−msk−mT(Vk−m+1⋯Vk−1Vk)gk+(Vk−1TVk−2T⋯Vk−m+2T)sk−m+1ρk−m+1sk−m+1T(Vk−m+2⋯Vk−2Vk−1)gk+⋯+Vk−1Tsk−2ρk−2sk−2TVk−1gk+sk−1ρk−1sk−1Tgk

上式很是有規律,這就爲迭代求解奠基了很好的基礎。咱們令q0=gkq0=gk,則當1im1≤i≤m時有

qi=(VkiVk2Vk1)gk(33)(33)qi=(Vk−i⋯Vk−2Vk−1)gk
ai=ρkisTkiqi1(34)(34)ai=ρk−isk−iTqi−1
相關文章
相關標籤/搜索