几种常见梯度优化方法

优化算法是机器学习领域的重要内容,本文介绍几种常见的无约束的优化算法。关于无约束问题优化方法的一般讨论请参考此文。

  • 梯度下降法 (Gradient Descent)
  • 动量法 (Momentum)
  • 共轭梯度法(Conjugate Gradient)
    • 约束与共轭
    • 最优步长
    • Gram-Schmidt方法
    • 共轭梯度
  • 自然梯度法(Natural Gradient)
    • Fisher信息矩阵
    • 自然梯度法的目标公式

梯度下降法 (Gradient Descent)

凡是接触过机器学习、强化学习、凸优化等领域的读者都会对梯度下降法非常熟悉。鉴于此原因,这里也不做详细介绍,仅给出基本方法。关于梯度下降在凸优化中的应用请参考此文。

学过数学的人都知道,函数的梯度方向表示了函数增长速度最快的方向,那么和它相反的方向就可以看作是函数值减少速度最快的方向。就机器学习模型优化问题而言,当目标被设定为求解目标函数的最小值的时候,只要朝着梯度下降的方向前进,就能不断接近最优值。

这里给出一种最简单的方法——固定步长方法:

def gd(x_start, step, g):
    x = x_start\
    for i in range(20):
        grad = g(x)
        x -= grad * step
        print '[ Epoch {0} ] grad = {1}, x = {2}'.format(i,grad,x)
        if abs(grad) < 1e-6:
            break
    return x

这里 x 保存当前优化过程中的参数值,g 是待优化函数 f(x) 的导数,step 是步长。步长的选择十分重要,步长太小导致收敛速度太慢;而步长太大则导致不收敛问题。关于调整步长目前已经有成熟的方法,这里不作细致讨论。

动量法 (Momentum)

动量代表了优化过程中累计的能量,它将在后面的优化过程中持续作用,推动目标值前进。动量会以一定的形式衰减。基于上一节的梯度下降方法,这里给出动量法的简单实现:

def momentum(x_start, step, g,discount=0.7):
    x = np.array(x_start,dtype = 'floast64')
    pre_grad = np.zeros_like(x)
    for i in range(50):
        grad = g(x)
        pre_grad = pre_grad * discount + grad * step
        x -= pre_grad
        print '[ Epoch {0} ] grad = {1}, x = {2}'.format(i,grad,x)
        if abs(grad) < 1e-6:
            break
    return x

可以看出,代码中多了 pre_grad。这个变量用于存储历史动量的积累,每一轮都会乘以一个打折量 discount 。动量法可以帮助目标值穿越“狭窄山谷”形状的优化曲面,从而达到最终的最优点。很多时候,目标函数在不同维度上的“陡峭”程度差别很大,固定步长的方法可在每一个维度上以相同的步长进行迭代,这就导致某些维度上更新乏力,而某些维度上大量进行无用功。

使用了动量后,历史的更新会以衰减的形式不断作用在这些方向上,反复做的无用功可以相互抵消,而更新不足的方向则在加强。然而,动量优化也存在一些问题,前几轮迭代累计不足,目标值在反复做无用功的方向震荡更大,带来更强烈的抖动。又有科研人员发明了解决方法——Nesterov算法。我们对上述代码稍加改造:

import numpy as np
def nesterov(x_start, step, g, discount = 0.7):
    x = np.array(x_start,dtype = 'floast64')
    pre_grad = np.zeros_like(x)
    for i in range(50):
        x_future = x - step * discount * pre_grad
        grad = g(x_future)
        pre_grad = pre_grad * discount + grad
        x -= pre_grad * step
        print '[ Epoch {0} ] grad = {1}, x = {2}'.format(i,grad,x)
        if abs(grad) < 1e-6:
            break
    return x

可以看出,这里用 x_future 存储下一步的变量,然后计算更新后的优化点的梯度。这是与动力法的主要区别。想象一个场景,优化点累积到了某个抖动方向后的梯度后,对动量算法来说,虽然梯度指向累计梯度相反的方向,但是刚开始动量不够大,所以优化点还是会在累计的方向上前进一小段。对 Nesterov 方法来说,如果在该方向前进一小段,则梯度中指向累积梯度相反的方向的量会大很多,最终两个方向梯度抵消,反而使抖动方向的量迅速减少。

共轭梯度法(Conjugate Gradient)

共轭梯度法是用来求解正定二次规划的一种优化方法,它具有二次终止性。前面的讨论中,我们并没有给出优化问题的具体形式,本节我们首先将待优化问题形式定义为:

minx12XTAXbTX min x 1 2 X T A X − b T X
其中 A A 为半正定矩阵, b b 为已知量,对公式进行求导并令导数为 0,得:
AX=b A X ∗ = b
其中 X X ∗ 为最优值。我们虽然可以直接求解上述方程,但是对于高纬度问题计算量较大,所以我们希望通过优化的方式找到最优解。

约束与共轭

梯度下降法的每一步都朝着局部最优的方向前进,但是几轮迭代它前进的方向可能非常相似,因为这个方向没有更新完,未来还会存在这个方向的残差。于是,我们对优化提出了更高要求——能不能再选中优化方向和步长上更新智能,每朝一个方向走,就把这个方向走到极致(极致就是再往前走就会远离目标)。我们引入一个误差变量 et e t

et=xxt e t = x ∗ − x t
误差反映了参数的最优点和当前点之间的距离(向量),那么我们的目标可以定义为: 每一步优化后,当前的误差和刚才优化的方向正交。优化的方向不一定是梯度方向。

我们令 rt r t 表示第 t t 轮的更新方向,可以得到:

rTtet+1=0 r t T e t + 1 = 0
有了这个约束,如果我们的优化空间有 d d 维,理论上最多只需要迭代 d d 轮就可以求解出来。然而公式中的误差项我们并不知道。未解决此问题,我们引入 共轭这个概念。在上述正交约束中加入一个矩阵 A A 使得:
rTtAet+1=0 r t T A e t + 1 = 0
现状两者关系变成了共轭正交。之前所说的正交可以理解为 A=I A = I 时的共轭正交。共轭正交向量还具有其它性质。我们可以将共轭正交的关系看作是 rt r t 与经过线性变换后的 et+1 e t + 1 构成的正交关系。这个新的正交实际上只是多绕了一个弯,与原来的正交差距不大,有个这个设定,下面的内容就比较好理解了。

最优步长

共轭梯度法属于线搜索的一种,类似于梯度下降,优化过程分两步:

  1. 确定优化方向;
  2. 确定优化步长。

这里我们先来介绍如何确定优化步长,优化方向的确定留到后面。假设当前参数为 Xt X t

rTtAet+1=rTtA[et+XtXt+1]=rTtA[et+αtrt]=rTtAet+αtrTtArt=0 r t T A e t + 1 = r t T A [ e t + X t − X t + 1 ] = r t T A [ e t + α t r t ] = r t T A e t + α t r t T A r t = 0

αt=rTtAetrTtArt=rTtA(XXt)rTtArt α t = − r t T A e t r t T A r t = − r t T A ( X ∗ − X t ) r t T A r t
回想我们对优化问题的定义可知 AX=b A X ∗ = b ,第 t t 轮的梯度 gt=AXtb g t = A X t − b ,于是:
αt=rTtgtrTtArt α t = r t T g t r t T A r t
到这里,公式中的未知量 et e t 已被抵消,公式变得可解。

Gram-Schmidt方法

下面我们讨论如何确定优化方向。我们要解决的主要问题是如何让优化方向和误差正交。由于每一次优化后,剩下的误差和本次的优化方向正交(共轭正交),所以可以看出每一个优化方向都是彼此正交的。现在问题变成了如何构造这些正交的方向。

Gram-Schmidt 方法是线性代数中常见的一种向量正交化方法。这个算法的大意是在 N N 维空间中输入 N N 个线性无关的向量,输出是 N N 个相互正交的向量,也就是我们最终想要的向量组合。假设输入的 N N 个向量为 u1,u2,,uN u 1 , u 2 , … , u N 输出的 N N 个向量为 d1,d2,,dN d 1 , d 2 , … , d N , 它的具体做法如下:

  1. 对于第一个向量,保持不变: d1=u1 d 1 = u 1
  2. 对于第二个向量,去掉其与第一个向量共线的部分: d2=u2+β1d1 d 2 = u 2 + β 1 d 1
  3. 对于第三个向量,去掉其与第一、二个向量共线的部分: d3=u3+2i=1β3,idi d 3 = u 3 + ∑ i = 1 2 β 3 , i d i
  4. 对于第 N N 个向量,去掉其与前 N1 N − 1 个向量共线的部分: dN=uNN1i=1βN,idi d N = u N − ∑ i = 1 N − 1 β N , i d i

如何确定这些 β β 呢?利用共轭正交的性质有:

dTlAdt=0,(l=1,2,,t1) d l T A d t = 0 , ( l = 1 , 2 , … , t − 1 )
进一步展开:
dTlAdt=dTlA(ut+i=1t1βt,idi)=dTlAut+dTlAi=1t1βt,idi d l T A d t = d l T A ( u t + ∑ i = 1 t − 1 β t , i d i ) = d l T A u t + d l T A ∑ i = 1 t − 1 β t , i d i
利用正交的性质(任意两个互异输出均相互共轭正交),公式可进一步简化为:
dTlAut+dTlAβt,ldl=0 d l T A u t + d l T A β t , l d l = 0
所以我们最终得到:
βt,l=dTlAutdTlAdl β t , l = − d l T A u t d l T A d l

共轭梯度

到这里,还有两个问题待解决:

  1. 要用什么向量构造这些正交向量?
  2. 随着向量数量的增加,我们需要计算的参数越来越多。这个计算量能不能减少呢?

解决上述两个问题的方法就是——用每一步的梯度构建这些正交向量,同时利用梯度的特点减少计算量。这便是共轭梯度法名称的由来。

最后我们来解决第二个问题。要解决此问题,我需要证明三个推论,这里我们依次证明。

推论一:第 t t 步计算的梯度 gt g t 和前 t1 t − 1 步的更新 {dj}t1j=1 { d j } j = 1 t − 1 正交。
证明:最优点 x x ∗ 到初始参数值 x1 x 1 的距离为 e1 e 1

e1=i=1Tαidi e 1 = ∑ i = 1 T α i d i
这样我们就用更新方向表示了误差,那么:当 i<j i < j
dTigj=dTi(AXjb)=dTi(AXjAX)=dTiAej=dTiAi=1Tαidi d i T g j = d i T ( A X j − b ) = d i T ( A X j − A X ∗ ) = d i T A e j = d i T A ∑ i = 1 T α i d i
由于利用 Gram-Schmidt 方法构造的更新方向均是互相共轭正交的,所以
dTigj=0,i<j d i T g j = 0 , i < j
证毕。

推论二:第 t t 步计算的梯度 gt g t 和前 t1 t − 1 步的梯度 {gj}t1j=1 { g j } j = 1 t − 1 正交。
证明:我们可以直接利用推论一证明推论二。注意这里的 gi g i 相当于Gram-Schmidt方法中提到的输入 ui u i

gTigj=(dik=1i1βkdk)Tgj=0 g i T g j = ( d i − ∑ k = 1 i − 1 β k d k ) T g j = 0
证毕。
此时我们发现,每一步的梯度与此前的梯度正交,因为我们能用梯度作为线性无关的向量组,从而组成共轭正交的向量组。

推论三:第 t t 步计算的梯度 gt g t 和前 t2 t − 2 步的更新 {dj}t2j=1 { d j } j = 1 t − 2 正交。
证明:

Xi+1=Xi+αidi X i + 1 = X i + α i d i

dTiAgjgTjAdi=(gTjAdi)TA  )=gTjA1αi(Xi+1Xi)=1αigTj(AXi+1AXi)=1αigTj(gi+1gi)=0,i+1<j ( d i T A g j = ( g j T A d i ) T (  A  是对称矩阵 ) g j T A d i = g j T A 1 α i ( X i + 1 − X i ) = 1 α i g j T ( A X i + 1 − A X i ) = 1 α i g j T ( g i + 1 − g i ) = 0 , i + 1 < j  (推论二) 
证毕。

所以可以看出,使用 Gram-Schmidt 方法可以保证更新方向共轭正交,对于第 t t 轮优化,我们只计算 βt1 β t − 1 即可,其它 β β 均是0。

下面来简单看下这个算法的实现:

import numpy as np
def cg(f_Ax,b,cg_iters=10,callback = None,verbose=False,residual_tol=1e-10):
    p = b.copy()
    r = b.copy()
    x = np.zeros_like(b)
    rdotr = r.dot(r)
    for i in range(cg_iters):
        z = f_Ax(p)
        v = rdotr / p.dot(z)
        x += v*p
        r -= v*z
        newrdotr = r.dot(r)
        mu = newrdotr/rdotr
        p = r + mu*p
        rdotr = newrdotr
        if rdotr < residual_tol:
            break
    return x

代码中用到了残差 r r ,真正的优化方向是 p p ,优化步长是 v v

自然梯度法(Natural Gradient)

当优化问题的两个坐标轴的尺度差异比较大时,使用一个统一的学习率会产生问题:某一个坐标轴可能会发散。尺度不同在优化问题中很难避免,虽然每一轮迭代对参数的更新量相差不多,但他们对模型的影响完全不同。梯度下降法针对的目标是参数,我们更新的目标也是参数,而优化的真正目标是找到最优的模型。每一轮迭代中,对参数的改进和对模型的改进是不同的。梯度下降法只考虑了在梯度方向上对参数进行更新,并没有考虑模型层面的更新,因此就会出现模型更新不均匀的现象。

自然梯度法不简单地使用学习率对参数更新进行量化,而是对模型效果进行量化。假设我们的待优化模型为 f(w) f ( w ) ,其中参数为 w w 。按照梯度下降法的思想,我们定义每一轮的迭代都要解决这样一个子问题:

minΔwf(w+Δw)s.t. |Δw|<ϵ min Δ w f ( w + Δ w ) s.t.  | Δ w | < ϵ
将原函数进行一阶Taylor展开,问题变为:
minΔwf(w)+wf(w)Δws.t. |Δw|<ϵ min Δ w f ( w ) + ∇ w f ( w ) Δ w s.t.  | Δ w | < ϵ
如果我们改为对模型的距离进行约束,就可以得到:
minΔwf(w)+wf(w)Δws.t. KL(f(w),f(w+Δw))<ϵ min Δ w f ( w ) + ∇ w f ( w ) Δ w s.t.  K L ( f ( w ) , f ( w + Δ w ) ) < ϵ
这里模型的变化用 KL散度进行衡量(注意KL散度没有对称性):
KL(f(x,w1)f(x,w2))=xf(x,w1)logf(x,w1)f(x,w2)dx K L ( f ( x , w 1 ) ‖ f ( x , w 2 ) ) = ∫ x f ( x , w 1 ) log ⁡ f ( x , w 1 ) f ( x , w 2 ) d x
它可以比较两个概率分布之间的差异。这边是自然梯度法的优化形式。可以看出, 有了模型层面的约束,每一轮迭代无论参数发生多大变化,模型的变化都会限制在一定的范围内。然而KL散度的计算并不容易。这个问题如何解决呢?

Fisher信息矩阵

KL散度可以变成熵和交叉熵的运算:

KL(f(w)f(w+Δw))=Efw[logf(w)f(w+Δw)]=Efw[logf(w)]Efw[logf(w+Δw)] K L ( f ( w ) ‖ f ( w + Δ w ) ) = E f w [ log ⁡ f ( w ) f ( w + Δ w ) ] = E f w [ log ⁡ f ( w ) ] − E f w [ log ⁡ f ( w + Δ w ) ]
通过对上式右边第二项进行Taylor展开,我们最终可以得到:
KL(f(w)f(w+Δw))Efw[logf(w)]Efw[logf(w)+wlogf(w)Δw+12ΔwT2wlogf(w)Δw)]=Efw[wlogf(w)Δw]Efw[12ΔwT2wlogf(w)Δw)]=[xf(w)1f(w)wf(w)dx]Δw12ΔwT[xf(w)2wlogf(w)dx]Δw K L ( f ( w ) ‖ f ( w + Δ w ) ) ≈ E f w [ log ⁡ f ( w ) ] − E f w [ log ⁡ f ( w ) + ∇ w log ⁡ f ( w ) Δ w + 1 2 Δ w T ∇ w 2 log ⁡ f ( w ) Δ w ) ] = − E f w [ ∇ w log ⁡ f ( w ) Δ w ] − E f w [ 1 2 Δ w T ∇ w 2 log ⁡ f ( w ) Δ w ) ] = − [ ∫ x f ( w ) 1 f ( w ) ∇ w f ( w ) d x ] Δ w − 1 2 Δ w T [ ∫ x f ( w ) ∇ w 2 log ⁡ f ( w ) d x ] Δ w
我们对 f(w) f ( w ) 的定义一般都是一个连续、可微、有界、性质优良的函数,所以第一项积分和微分可以互换,上式最终变为:
KL(f(w)f(w+Δw))=12ΔwTEfw[2wlogf(w)]Δw K L ( f ( w ) ‖ f ( w + Δ w ) ) = − 1 2 Δ w T E f w [ ∇ w 2 log ⁡ f ( w ) ] Δ w
这里包含一个二阶导的期望值,仍然比较复杂。

为了进一步简化,我们引入Fisher信息矩阵。我们首先定义 Score函数 为对数似然函数的一阶导数:

lfw=wf(x,w) l f w = ∇ w f ( x , w )
那么可以看出,Score函数的期望值是0,即:
Efw[lfw]=xf(w)wlogf(w)dx=0 E f w [ l f w ] = ∫ x f ( w ) ∇ w log ⁡ f ( w ) d x = 0
Fisher信息矩阵可以通过Score函数定义:
Ifw=Efw[lfwlTfw] I f w = E f w [ l f w l f w T ]
可以证明:在概率分布函数具备良好性质时, Fisher信息矩阵和KL散度的二阶导数的相反数相等。证明过程非常直接,此处从略。

自然梯度法的目标公式

通过前面的推演,我们的目标函数变为:

minΔwf(w)+wf(w)Δws.t. 12ΔwTIfwΔw<ϵ min Δ w f ( w ) + ∇ w f ( w ) Δ w s.t.  1 2 Δ w T I f w Δ w < ϵ
该问题可以通过 拉格朗日乘子法表示为:
minΔwf(w)+wf(w)Δw+λ[12ΔwTIfwΔwϵ] min Δ w f ( w ) + ∇ w f ( w ) Δ w + λ [ 1 2 Δ w T I f w Δ w − ϵ ]
求导取极限点,可以得到:
wf(w)+λIfwΔw=0Δw=1λI1fwwf(w) ∇ w f ( w ) + λ I f w Δ w = 0 Δ w = − 1 λ I f w − 1 ∇ w f ( w )
公式中 1λ 1 λ 可以作为梯度下降法的学习率类似的分量,那么自然梯度法的优化方向就可以看作 I1fwwf(w) I f w − 1 ∇ w f ( w ) ,与梯度下降法不同,它需要额外求解 Fisher 信息矩阵的逆。

感谢冯超——《强化学习精要:核心算法与TensorFlow实现》(电子工业出版社)

你可能感兴趣的:(Convex,Optimization)