数值计算之 梯度下降法与函数极值

前言

本篇将开始介绍优化算法。首先是梯度下降法,在最小二乘与深度学习中,都是最常用的最优化求解方法和思想。

微积分基础

一元函数的极值,导数与泰勒展开

对于一元函数 f ( x ) f(x) f(x)而言,当 x 0 x_0 x0满足以下条件时, f ( x 0 ) f(x_0) f(x0)取得极值:
f ′ ( x 0 ) = 0 , f ′ ′ ( x 0 ) ≠ 0 f'(x_0)=0,f''(x_0)\ne 0 f(x0)=0,f(x0)=0
f ( x ) f(x) f(x) x 0 x_0 x0处泰勒展开:
f ( x ) = f ( x 0 ) + f ′ ( x 0 ) ( x − x 0 ) + 1 2 ! f ′ ′ ( x 0 ) ( x − x 0 ) 2 + o ( ( x − x 0 ) 2 ) f(x)=f(x_0)+f'(x_0)(x-x_0)+\frac{1}{2!}f''(x_0)(x-x_0)^2+o((x-x_0)^2) f(x)=f(x0)+f(x0)(xx0)+2!1f(x0)(xx0)2+o((xx0)2)
可以从泰勒展开中看出,当 f ′ ( x ) = 0 , f ′ ′ ( x 0 ) > 0 f'(x)=0,f''(x_0)> 0 f(x)=0,f(x0)>0,在 x 0 x_0 x0附近必然有 f ( x ) > f ( x 0 ) f(x)>f(x_0) f(x)>f(x0)成立;当 f ′ ( x ) = 0 , f ′ ′ ( x 0 ) < 0 f'(x)=0,f''(x_0)< 0 f(x)=0,f(x0)<0,在 x 0 x_0 x0附近必然有 f ( x ) < f ( x 0 ) f(x)<f(x_0) f(x)<f(x0)成立。这就把一元函数的局部最值与泰勒公式联系起来了。

多元函数的泰勒展开

对于实值向量函数 f ( x ) f(\bf x) f(x),其在 x 0 \bf x_0 x0处的泰勒展开可表示为:
f ( x ) = f ( x 0 ) + ∇ f ( x 0 ) ⋅ ( x − x 0 ) + ( x − x 0 ) T H ( x 0 ) ( x − x 0 ) + o n f({\bf x})=f({\bf x_0})+ \nabla f({\bf x_0}) \cdot ({\bf x}-{\bf x_0})+ ({\bf x}-{\bf x_0})^TH({\bf x_0}) ({\bf x}-{\bf x_0}) +o^n f(x)=f(x0)+f(x0)(xx0)+(xx0)TH(x0)(xx0)+on
类似的,当梯度 ∇ f ( x 0 ) = 0 \nabla f({\bf x_0})={\bf 0} f(x0)=0, 海森矩阵 H H H正定时, f ( x 0 ) f(\bf x_0) f(x0)是一个局部极值点。

从多元函数的泰勒展开也能看出,如果 x 0 \bf x_0 x0处的梯度不为 0 \bf 0 0,则在周围存在某一点 x \bf x x使得 f ( x ) > f ( x 0 ) f({\bf x})>f({\bf x_0}) f(x)>f(x0),同时也存在某一点 x \bf x x使得 f ( x ) < f ( x 0 ) f({\bf x})<f({\bf x_0}) f(x)<f(x0)

自然而然的想到,如果我们要寻找函数的极大值,可以从 x 0 {\bf x_0} x0开始,选择周围的某一点 x = Δ x + x 0 {\bf x}=\Delta \bf x+ x_0 x=Δx+x0,使得 f ( x ) > f ( x 0 ) f({\bf x})>f({\bf x_0}) f(x)>f(x0),到达点 x 1 \bf x_1 x1,然后再选择周围某一个点 x = Δ x + x 1 {\bf x}=\Delta \bf x+ x_1 x=Δx+x1,继续迭代到满足局部极值条件为止。寻找极小值同理。

这就是梯度下降(上升)法的思想。

梯度下降法

以上迭代具有两个核心问题:①如何选择周围点,也就是如何选择 Δ x \Delta \bf x Δx;②如何判断满足局部极值条件,也是就是什么时候结束迭代。下面以最常见的寻找局部最小为例。

梯度方向

首先讨论问题①,回到多元泰勒展开式:
f ( x ) = f ( x 0 ) + ∇ f ( x 0 ) ⋅ ( x − x 0 ) + 1 2 ( x − x 0 ) T H ( x 0 ) ( x − x 0 ) + o n f({\bf x})=f({\bf x_0})+ \nabla f({\bf x_0}) \cdot ({\bf x}-{\bf x_0})+ \frac{1}{2} ({\bf x}-{\bf x_0})^TH({\bf x_0}) ({\bf x}-{\bf x_0}) +o^n f(x)=f(x0)+f(x0)(xx0)+21(xx0)TH(x0)(xx0)+on
梯度 ∇ f ( x 0 ) ≠ 0 \nabla f({\bf x_0}) \ne {\bf 0} f(x0)=0。假设我们要寻找的 Δ x \Delta \bf x Δx的长度固定,则每次迭代的函数增量为:
Δ f = f ( x ) − f ( x 0 ) = ∇ f ( x 0 ) ⋅ ( Δ x ) + o n \Delta f=f({\bf x})-f({\bf x_0})=\nabla f({\bf x_0}) \cdot (\Delta \bf x)+o^n Δf=f(x)f(x0)=f(x0)(Δx)+on
等式右边是一个内积,可以简化表示为:
g x 0 = ∇ f ( x 0 ) e Δ x = Δ x Δ f = f ( x ) − f ( x 0 ) = g x 0 ⋅ e Δ x = ∣ g x 0 ∣ ∣ e Δ x ∣ cos ⁡ θ g_{\bf x_0}=\nabla f({\bf x_0}) \\ e_{\Delta \bf x} = {\Delta \bf x} \\ \Delta f=f({\bf x})-f({\bf x_0}) = g_{\bf x_0} \cdot e_{\Delta \bf x}=|g_{\bf x_0}||e_{\Delta \bf x}|\cos \theta gx0=f(x0)eΔx=ΔxΔf=f(x)f(x0)=gx0eΔx=gx0eΔxcosθ
其中, cos ⁡ θ \cos \theta cosθ是梯度向量 ∇ f ( x 0 ) \nabla f(\bf x_0) f(x0) Δ x \Delta \bf x Δx的夹角。 θ = 0 \theta=0 θ=0 Δ f > 0 \Delta f>0 Δf>0并且取最大值; θ = π \theta=\pi θ=π Δ f < 0 \Delta f<0 Δf<0且取最小值。

由此得出一个结论:梯度方向是函数上升最快的方向,梯度反方向是函数下降最快的方向

因此,在迭代过程中,我们选择的 Δ x \Delta \bf x Δx的方向与梯度方向相反,即:
Δ x = − ∣ ∣ Δ x ∣ ∣ 2 ∣ ∣ ∇ f ( x 0 ) ∣ ∣ 2 ∇ f ( x 0 ) \Delta {\bf x}=-\frac{||\Delta {\bf x}||_2}{||\nabla f({\bf x_0})||_2}\nabla f({\bf x_0}) Δx=f(x0)2Δx2f(x0)

另外,可以通过学习率 λ \lambda λ控制 Δ x \Delta \bf x Δx的长度。一方面是由于泰勒展开只在 x 0 \bf x_0 x0的局部范围才成立,另一个方面是较大的 Δ x \Delta \bf x Δx可能会导致函数找不到局部极值区域,而较小的 Δ x \Delta \bf x Δx需要更多的迭代次数。梯度下降法的迭代方程可表示为:
h ( x ) = x − λ ∇ f ( x ) h({\bf x})={\bf x} - \lambda \nabla f({\bf x}) h(x)=xλf(x)

终止条件

对于问题②,有比较多的方式来终止迭代,比如设置最大迭代次数,设置增量阈值,梯度限制等。

可以设置函数值增量 Δ f \Delta f Δf阈值来控制迭代过程:
i f Δ f l a s t < t h r e s h o l d t h e n x = h l a s t ( x ) if \quad \Delta f_{last}<threshold \\ then \quad {\bf x}= {h_{last}(\bf x)} ifΔflast<thresholdthenx=hlast(x)
也可以设置 Δ x \Delta \bf x Δx的长度阈值:
i f Δ x < t h r e s h o l d t h e n x = x l a s t if \quad \Delta {\bf x}<threshold \\ then \quad {\bf x}= {\bf x}_{last} ifΔx<thresholdthenx=xlast

代码举例

这里给出一个我自己写的求二元函数 f ( x , y ) = x 2 + y 2 f(x,y)=x^2+y^2 f(x,y)=x2+y2 f ( x , y ) = x 2 + 2 y 2 + 2 x y + 3 x − y − 2 f(x,y)=x^2+2y^2+2xy+3x-y-2 f(x,y)=x2+2y2+2xy+3xy2的极小值的代码:

def partial_derivate_xy(x, y, F):
    dx = (F(x + 0.001, y) - F(x, y))/0.001
    dy = (F(x, y + 0.001) - F(x, y))/0.001
    return dx, dy


def non_linear_func(x, y):
    fxy = 0.5 * (x ** 2 + y ** 2)
    return fxy


def non_linear_func_2(x, y):
    fxy = x*x + 2*y*y + 2*x*y + 3*x - y - 2
    return fxy


def non_linear_func_3(x, y):
    fxy = 0.5 * (x ** 2 - y ** 2)
    return fxy


def grad_decent(x, y, F, lr):
    grad_x, grad_y = partial_derivate_xy(x, y, F)
    x_opt = x - lr * grad_x
    y_opt = y - lr * grad_y
    return x_opt, y_opt


def optimizer(x0, y0, F, lr=0.01, th=0.0001):
    x = x0
    y = y0
    counter = 0
    while True:
        x_opt, y_opt = grad_decent(x, y, F, lr)
        if (x_opt - x)**2 + (y_opt - y)**2 < th**2:
            break
        x = x_opt
        y = y_opt
        counter = counter + 1
        print('iter: {}'.format(counter), 'optimized (x, y) = ({}, {})'.format(x, y))
    return x, y


if __name__ == '__main__':
    x0 = 2.
    y0 = 2.
    result_x, result_y = optimizer(x0, y0, non_linear_func)
    print(result_x, result_y)

后记

本篇记录了梯度下降法的原理,后续会再总结SGD,Adam等优化器。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值