Python 多元标量函数的梯度下降优化

Python 多元标量函数的梯度下降优化,python,numpy,optimization,scipy,gradient-descent,Python,Numpy,Optimization,Scipy,Gradient Descent,我试图在rosenbrock函数上测试我的梯度下降程序。但无论我如何调整我的学习速度(stepargument)、精度(precisionargument)和迭代次数(iterationargument),我都无法得到非常接近的结果 import numpy as np def minimize(f, f_grad, x, step=1e-3, iterations=1e3, precision=1e-3): count = 0 while True: last

我试图在rosenbrock函数上测试我的梯度下降程序。但无论我如何调整我的学习速度(
step
argument)、精度(
precision
argument)和迭代次数(
iteration
argument),我都无法得到非常接近的结果

import numpy as np

def minimize(f, f_grad, x, step=1e-3, iterations=1e3, precision=1e-3):
    count = 0
    while True:
        last_x = x
        x = x - step * f_grad(x)
        count += 1
        if count > iterations or np.linalg.norm(x - last_x) < precision:
            break
    return x

def rosenbrock(x):
    """The Rosenbrock function"""
    return sum(100.0*(x[1:]-x[:-1]**2.0)**2.0 + (1-x[:-1])**2.0)

def rosenbrock_grad(x):
    """Gradient of Rosenbrock function"""
    xm = x[1:-1]
    xm_m1 = x[:-2]
    xm_p1 = x[2:]
    der = np.zeros_like(x)
    der[1:-1] = 200*(xm-xm_m1**2) - 400*(xm_p1 - xm**2)*xm - 2*(1-xm)
    der[0] = -400*x[0]*(x[1]-x[0]**2) - 2*(1-x[0])
    der[-1] = 200*(x[-1]-x[-2]**2)
    return der

x0 = np.array([1.3, 0.7, 0.8, 1.9, 1.2])
minimize(rosenbrock, rosenbrock_grad, x0, step=1e-6, iterations=1e4, precision=1e-6)
将numpy导入为np
def最小化(f,f_梯度,x,步长=1e-3,迭代次数=1e3,精度=1e-3):
计数=0
尽管如此:
最后的x=x
x=x-阶跃*f_梯度(x)
计数+=1
如果计数>迭代次数或np.linalg.norm(x-last_x)<精度:
打破
返回x
戴夫·罗森布罗克(x):
“”“Rosenbrock函数”“”
回报总额(100.0*(x[1:]-x[:-1]**2.0)**2.0+(1-x[:-1])**2.0)
def rosenbrock_梯度(x):
“”“Rosenbrock函数的梯度”“”
xm=x[1:-1]
xm_m1=x[:-2]
xm_p1=x[2:]
der=np.类零(x)
der[1:-1]=200*(xm-xm_m1**2)-400*(xm_p1-xm**2)*xm-2*(1-xm)
der[0]=-400*x[0]*(x[1]-x[0]**2)-2*(1-x[0])
der[-1]=200*(x[-1]-x[-2]**2)
回程单
x0=np.数组([1.3,0.7,0.8,1.9,1.2])
最小化(rosenbrock,rosenbrock_grad,x0,步长=1e-6,迭代次数=1e4,精度=1e-6)
例如,上面的代码为我提供了
数组([1.01723267,1.03694999,1.07870143,1.16693184,1.36404334])
。但是,如果我在
scipy.optimize
中使用任何内置的优化方法,我可以得到非常接近的答案或完全相等的
数组([1,1,1,1,1.])
(这是真正的答案)

但是,如果在我的程序中使用非常小的
步长
精度
和非常大的
迭代次数
,计算只会在我的计算机上花费很长时间

我不知道这是不是因为

我的程序中有任何错误吗

或者仅仅因为

梯度下降在这里效率很低,而且要求非常小
步骤
精度
和非常大的
迭代
,以产生非常接近的 解决方案

还是因为

我需要做一些特殊的功能缩放


(注:我还尝试绘制二维图,其中函数值在y轴上,迭代次数在x轴上,以“调试”梯度下降,但即使我得到一个好看的下降曲线图,解决方案仍然不是很接近。)

您的方法很容易出现超调。在具有瞬时高梯度的情况下,你的解会跳得很远。在优化中,当一个步骤不能降低成本时,拒绝它通常是合适的

线搜索 一旦你通过计算梯度选择了一个方向,沿着这个方向搜索,直到你将成本降低到梯度范数的一小部分

即从x[n+1]=x-α*梯度开始

α在1.0到0.0之间变化,接受x的值,如果x降低了梯度范数的一部分成本。这是一个很好的收敛规则,称为Armijo规则

其他忠告 首先考虑优化2D Rosenbrock函数,并在该成本域上绘制路径


考虑用数字验证渐变实现是否正确。通常情况下,这就是问题所在。

您的方法容易出现超调。在具有瞬时高梯度的情况下,你的解会跳得很远。在优化中,当一个步骤不能降低成本时,拒绝它通常是合适的

线搜索 一旦你通过计算梯度选择了一个方向,沿着这个方向搜索,直到你将成本降低到梯度范数的一小部分

即从x[n+1]=x-α*梯度开始

α在1.0到0.0之间变化,接受x的值,如果x降低了梯度范数的一部分成本。这是一个很好的收敛规则,称为Armijo规则

其他忠告 首先考虑优化2D Rosenbrock函数,并在该成本域上绘制路径

考虑用数字验证渐变实现是否正确。通常情况下,这就是问题所在。

引用以下内容:

全球最小值位于一个狭长的抛物线形平坦山谷内。找到山谷是微不足道的。然而,要收敛到全局最小值是困难的

梯度下降法是一种简单的算法,因此它不能找到最小值也就不足为奇了。让我们看看2D中不同起点的情况:

正如维基百科所说:它很容易找到山谷,但无法进一步收敛。与函数的其他部分相比,沿着山谷的梯度非常平坦

我认为您的实现工作正常,但Rosenbrock函数可能不是测试它的最合适的函数

与其他答案相反,我进一步认为步长太小而不是太大。问题不在于超调,而在于算法被卡住了。如果我将步长设置为
1e-3
,而不更改其他设置,则算法将在两位数内收敛到最大值。在2D的情况下,尽管从一些起始位置超调了山谷,但仍然会发生这种情况——可以这么说,你需要的是速度,不要在以后被卡住

下面是复制上图的修改代码:

import numpy as np
import matplotlib.pyplot as plt

def minimize(f, f_grad, x, step=1e-3, iterations=1e3, precision=1e-3):
    count = 0
    while True:
        last_x = x
        x_hist.append(x)
        x = x - step * f_grad(x)
        count += 1
        if count > iterations or np.linalg.norm(x - last_x) < precision:
            x_hist.append(x)
            break
    return x

def rosenbrock(x):
    """The Rosenbrock function"""
    return sum(100.0*(x[1:]-x[:-1]**2.0)**2.0 + (1-x[:-1])**2.0)

def rosenbrock_grad(x):
    """Gradient of Rosenbrock function"""
    xm = x[1:-1]
    xm_m1 = x[:-2]
    xm_p1 = x[2:]
    der = np.zeros_like(x)
    der[1:-1] = 200*(xm-xm_m1**2) - 400*(xm_p1 - xm**2)*xm - 2*(1-xm)
    der[0] = -400*x[0]*(x[1]-x[0]**2) - 2*(1-x[0])
    der[-1] = 200*(x[-1]-x[-2]**2)
    return der


k = np.linspace(0, 2, 101)
f = np.empty((k.shape[0], k.shape[0]))
for i, y in enumerate(k):
    for j, x in enumerate(k):
        f[i, j] = rosenbrock(np.array([x, y]))
plt.imshow(np.log10(f), extent=[k[0], k[-1], k[-1], k[0]], cmap='autumn')

for start in [[0.5, 0.5], [1.0, 0.5], [1.5, 0.5],
              [0.5, 1.0], [1.0, 1.0], [1.5, 1.0],
              [0.5, 1.5], [1.0, 1.5], [1.5, 1.5]]:

    x0 = np.array(start)

    x_hist = []

    minimize(rosenbrock, rosenbrock_grad, x0, step=1e-6, iterations=1e4, precision=1e-9)


    x_hist = np.array(x_hist)
    plt.plot(x_hist[:, 0], x_hist[:, 1], 'k')
    plt.plot(x0[0], x0[1], 'ok')
将numpy导入为np
将matplotlib.pyplot作为plt导入
def最小化(f,f_梯度,x,步长=1e-3,迭代次数=1e3,精度=1e-3):
计数=0
尽管如此:
最后的x=x
x_历史附加(x)
x=x-阶跃*f_梯度(x)
计数+=1
如果计数>迭代次数或np.linalg.norm(x-last_x)<精度:
x_历史附加(x)
打破
返回x
戴夫·罗森布罗克(x):
“”“Rosenbrock函数”“”
回报总额(100.0*(x[1:]-x[:-1]**2.0)**2.0+(1-x[:-1])**2.0)
def rosenbrock_梯度(x):
“”“Rosenbrock函数的梯度”“”
xm=x[1:-1]
xm_m1=x[:-2]
xm_p1=x[2:]
der=np.类零(x)
der[1:-1]=200*(xm-xm_m1**2)-400*(xm_p1-