最优化技术——牛顿法

最优化技术——牛顿法

没错,到期末了,我又开始学最优化了

参考目录

解决的问题

解决的是无约束的极小化问题,用数学语言描述如下:
m i n x f ( x ) (0) \mathop{min}\limits_{x}f(x) \tag{0} xminf(x)(0)

简单一维牛顿法

从现有极小值估计点出发,对f(x)做泰勒展开,进而找到极小值的下一个估计值。设 x k x_k xk是当前极小值的估计值,有:
φ ( x ) = f ( x k ) + f ’ ( x k ) ( x − x k ) + 1 2 f ’ ’ ( x k ) ( x − x k ) 2 (1) \varphi (x) = f(x_k) +f’(x_k)(x - x_k) + \frac{1}{2}f’’(x_k)(x-x_k)^2 \tag{1} φ(x)=f(xk)+f(xk)(xxk)+21f(xk)(xxk)2(1)
φ \varphi φ表示的是f(x) 在x附近的二阶泰勒展开式,由于求的是最值由极值条件可知: φ \varphi φ应当满足:
φ ’ ( x ) = 0 (2) \varphi’(x) = 0 \tag{2} φ(x)=0(2)
将1、2两个式子整合可知:
f ’ ( x k ) + f ’ ’ ( x k ) ( x − x k ) = 0 f’(x_k) + f’’(x_k)(x - x_k) = 0 f(xk)+f(xk)(xxk)=0
所以可以求得:
x = x k − f ’ ( x k ) f ’ ’ ( x k ) x = x_k - \frac{f’(x_k)}{f’’(x_k)} x=xkf(xk)f(xk)
于是,一直这样迭代下去就可以得到我们的极值了。当然,我们也可以看出来牛顿法的局限性,那就是:如果牛顿法的优化目标是一个复杂的函数,那么我们很难手推出它的一阶导数和二阶导数,甚至有时候,函数可能并不存在一阶导数和二阶导数。

当然了,这并不影响我们写个程序实现它:

import numpy as np
from sympy import *

# 使用牛顿法去找一个一维函数的极小值
def Newton(aimFunc, verb, stPoint = None, step = 10):
    """
    使用牛顿法确定一个函数的最小值
    @param aimFunc 目标函数,sympy对象
    @param verb    变量,也是求导对象
    @param stPoint 初始位置,如果没有就随机一个
    @param step    迭代次数
    """

    if(stPoint is None):
        stPoint = ( np.random.random() - 0.5 ) * 1e5
    curPoint = stPoint
    for i in range(step):
        # 一阶导数值
        onP = diff(aimFunc, verb).evalf(subs ={'x1':curPoint})
        # 二阶导数值
        twP = diff(diff(aimFunc, verb), verb).evalf(subs ={'x1':curPoint})
        curPoint -= onP / twP
    return curPoint


x = symbols('x1')
# 定义目标函数
aimFunction = x ** 2 + 2 * x + 1
print(Newton(aimFunction, x))

我们的目标函数是
f ( x ) = ( x + 1 ) 2 f(x) = (x + 1 )^2 f(x)=(x+1)2
即使我们把初始值定在了1*10^9,但是牛顿法依然能在10次迭代后算到极值-1.00000000000000,说明牛顿发收敛很快。

多维牛顿法

很显然,现实世界中,基本上没有什么需要使用nb优化算法的一维问题,现实世界中的问题一般维度很高,在这一小节,我们介绍二维的牛顿法,以此为例,你可以把它推广到n维。

首先看,对于一维函数,他有导数来代表因变量随自变量变换的趋势。那么多维函数有什么?没错,就是梯度,那么我们就可以将泰勒展开推广到n维的函数上去了:
φ ( x ) = f ( x k ) + ∇ f ( x k ) ( x − x k ) + 1 2 ( x − x k ) T ∇ 2 f ( x k ) ( x − x k ) \varphi(x) = f(x_k)+\nabla f(x_k)(x - x_k) + \frac{1}{2}(x - x_k)^T \nabla^2f(x_k)(x-x_k) φ(x)=f(xk)+f(xk)(xxk)+21(xxk)T2f(xk)(xxk)
其中, ∇ f \nabla f f称为f的梯度向量 ∇ 2 f \nabla^2 f 2f称为f的海森矩阵,他们的定义是:
∇ f = [ ∂ f ∂ x 1 f r a c ∂ f ∂ x 2 … f r a c ∂ f ∂ x n ] , ∇ 2 f = [ ∂ f ∂ x 1 ∂ x 1 ∂ f ∂ x 1 ∂ x 2 … ∂ f ∂ x 1 ∂ x n f r a c ∂ f ∂ x 2 ∂ x 1 ∂ f ∂ x 2 ∂ x 2 … ∂ f ∂ x 2 ∂ x n … … … f r a c ∂ f ∂ x n ∂ x 1 ∂ f ∂ x n ∂ x 2 … ∂ f ∂ x n ∂ x n ] \nabla f= \left[\begin{matrix}\frac{\partial f}{\partial x_1} \\frac{\partial f}{\partial x_2} \\… \\frac{\partial f}{\partial x_n}\end{matrix}\right],\nabla^2 f= \left[\begin{matrix}\frac{\partial f}{\partial x_1\partial x_1} & \frac{\partial f}{\partial x_1\partial x_2} & …&\frac{\partial f}{\partial x_1\partial x_n}\\frac{\partial f}{\partial x_2\partial x_1} & \frac{\partial f}{\partial x_2\partial x_2} & …&\frac{\partial f}{\partial x_2\partial x_n}\\… & … & & …\\frac{\partial f}{\partial x_n\partial x_1} & \frac{\partial f}{\partial x_n\partial x_2} & …&\frac{\partial f}{\partial x_n\partial x_n}\end{matrix}\right] f=x1ffracfx2fracfxn,2f=x1x1ffracfx2x1fracfxnx1x1x2fx2x2fxnx2fx1xnfx2xnfxnxnf
那么同理,需要求极值,所以需要是驻点,所以:
∇ φ ( x ) = 0 \nabla\varphi(x) = 0 φ(x)=0
解得:
x k + 1 = x k − ∇ 2 f − 1 ∗ ∇ f x_{k+1} = x_k - {\nabla^2 f}^{-1}*\nabla f xk+1=xk2f1f
最后,迭代就完事了。

下面来看代码:

import numpy as np
from sympy import *

# 使用牛顿法去找一个一维函数的极小值
def NDimNewton(aimFunc, verbs, stPoint = None, step = 10):
    """
    使用牛顿法确定一个函数的最小值
    @param aimFunc 目标函数,sympy对象
    @param verbs   变量,也是求导对象
    @param stPoint 初始位置,如果没有就随机一个
    @param step    迭代次数
    """

    if(stPoint is None):
        stPoint = ( np.random.random(verbs.shape) - 0.5 ) * 1e5
    curPoint = stPoint
    # 一阶梯度向量
    alpha = np.zeros(curPoint.shape[0])
    # 黑塞矩阵
    H     = np.zeros((curPoint.shape[0], curPoint.shape[0]))

    # 根据迭代次数进行迭代
    for i in range(step):
        print(curPoint)
        # 计算偏导数梯度向量
        for j in range(curPoint.shape[0]):
            alpha[j] = diff(aimFunc, verbs[j]).evalf(subs ={'x1':curPoint[0], 'x2':curPoint[1]})

        # 计算黑塞矩阵
        for j in range(curPoint.shape[0]):
            for k in range(curPoint.shape[0]):
                H[j][k] = diff(diff(aimFunc, verbs[j]), verbs[k]).evalf(subs ={'x1':curPoint[0], 'x2':curPoint[1]})
        
    
        print(H)
        # 更新点
        curPoint -= np.dot(np.array(np.linalg.inv(H), dtype=np.float), np.array(alpha, dtype=np.float))
    return curPoint


x1, x2 = symbols('x1 x2')
# 定义目标函数
aimFunction = 60 - 10 * x1 - 4 * x2 + x1 ** 2 + x2 ** 2 - x1 * x2
print(NDimNewton(aimFunction, np.array([x1, x2]), np.array([1e9, 1e9])))

可以看到,下降路径中,一次就下降到最终结果了。。。那我实验报告怎么写呢???

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值