凸优化学习:使用python实现梯度下降和牛顿法,以优化二元二次凸函数(无约束项)为例

这里,我们以优化下面函数为例:
z = 0.2 * x**2 + 9 * x + 0.5 * y ** 2 + 3 * y

这个函数是二元的,所以,梯度的维度是2,因此,梯度可以表示为数组
derivative_x = 0.4 * x + 9
derivative_y = y + 3
[derivative_x, derivative_y]

需要注意的是,x表示第一维度,y表示第二维度,z表示函数的值。

梯度下降

import math
def func(x, y):
    z = 0.2 * x**2 + 9 * x + 0.5 * y ** 2 + 3 * y
    return z

def get_derivative(x, y):
    derivative_x = 0.4 * x + 9
    derivative_y = y + 3
    # 计算梯度的大小,用来判断迭代终止条件
    norm = math.sqrt(derivative_x ** 2 + derivative_y ** 2)
    return derivative_x, derivative_y, norm
step_size = 0.2
x = 1
y = 40
_, _, norm = get_derivative(x, y)
count = 0
while norm > 0.001:
    z = func(x, y)
    x1, y1, norm = get_derivative(x, y)
    x = x - step_size * x1 # 更新第一维的值
    y = y - step_size * y1 # 更新第二维的值

    count += 1
    print(count, z)
1 929.2
2 579.4148799999999
3 352.050802432
4 203.57403789844477
5 106.04038645804366
6 41.49563639780015
7 -1.6100912410862662
8 -30.718811874893383
9 -50.635813985910055
、、、
102 -105.7499946505782
103 -105.7499954722494
104 -105.74999616771186
105 -105.74999675635132
106 -105.74999725457577
107 -105.74999767627291
108 -105.74999803319739
109 -105.74999833529827
110 -105.74999859099646
111 -105.74999880741939

牛顿法

使用泰勒展开的二阶展开项,便得到下面的公式,从优化f(x)变成优化下面的函数了。

min { f ( x k ) + ∇ f ( x k ) T ( x − x k ) + 1 / 2 ( x − x k ) T ∇ 2 f ( x k ) ( x − x k ) } \left\{f(x^k) + \nabla f(x^k)^T(x-x^k) + 1/2(x-x^k)^T\nabla^2f(x^k)(x-x^k)\right\} { f(xk)+f(xk)T(xxk)+1/2(xxk)T2f(xk)(xxk)}

这个式子是f(x)的二阶近似。因此,它的优点是在优化时使用到了二阶的信息(梯度下降至使用了一阶的),因此具有二阶收敛性,即更快的收敛速度。但是,代价便是计算量变大了,需要计算复杂的hasen矩阵。

  1. x 0 , e , k = 0 x^0, e, k = 0 x0,e,k=0
  2. if ∇ f ( x k ) ≤ e \nabla f(x^k)\leq e f(xk)e
  3. d k = − [ ∇ 2 f ( x k ) ] − 1 ∇ f ( x k ) d^k = -[\nabla^2 f(x^k)]^{-1}\nabla f(x^k) dk=[2f(xk)]1f(xk) ,两矩阵相乘
  4. x k + 1 = x k + d k ; k = k + 1 x^{k+1} = x^{k} + d^k; k = k + 1 xk+1=xk+dk;k=k+1
import numpy as np

def func(x, y):
    z = 0.2 * x**2 + 9 * x + 0.5 * y ** 2 + 3 * y
    return z

def get_derivative(x, y):
    derivative_x = 0.4 * x + 9
    derivative_y = y + 3
    return [derivative_x, derivative_y]

def getNorm(x, y):
    derivative_x, derivative_y = get_derivative(x, y)
    norm = math.sqrt(derivative_x ** 2 + derivative_y ** 2)
    return norm

def hesen(x, y):
	# 需要分别计算xx,xy,yx,yy的二次偏导数
    return [[0.4, 0], [0, 1]]

hesen_matrix = hesen(x, y)

print(hesen_matrix)

inv = np.linalg.inv(hesen_matrix)

d_k = - (inv * get_derivative(x, y))
print(d_k)

derivative = np.array(get_derivative(x, y))
print(derivative)

d_k * derivative
x = 1
y = 40
count = 0
while getNorm(x, y) > 0.0001:
    hesen_matrix = hesen(x, y)
    inv = np.linalg.inv(hesen_matrix)
    d_k = - (inv * np.array(get_derivative(x, y)))
    x = x + d_k[0][0]
    y = y + d_k[1][1]
    count += 1
    z = func(x, y)
    print(count, z)

函数最优值为:-105.75,且只需要迭代一次。很明显,这个结果和梯度下降法一样。

其他

这里的代码仅仅是为了验证这两个优化算法的思想,对代码优雅性暂不做考虑。

还可参考视频:
https://www.bilibili.com/video/BV15C4y1s71j/?spm_id_from=333.999.0.0&vd_source=f0713035f61961749a51689ec9787803

猜你喜欢

转载自blog.csdn.net/liangyihuai/article/details/132043455