【问题标题】:Gradient descent optimization for multivariate scalar functions多元标量函数的梯度下降优化
【发布时间】:2017-08-05 01:00:36
【问题描述】:

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

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)

例如,上面的代码给了我array([ 1.01723267, 1.03694999, 1.07870143, 1.16693184, 1.36404334])。但是如果我在scipy.optimize 中使用任何内置优化方法,我可以获得非常接近的答案或完全等于array([ 1., 1., 1., 1., 1.])(这是真正的答案)。

但是,如果我在我的程序中使用非常小的 stepprecision 和非常大的 iterations,则计算将永远在我的计算机上进行。

不知道是不是因为

我的程序中的任何错误

或者只是因为

梯度下降在这里效率低下,要求非常小 stepprecision 和非常大的 iterations 以产生非常接近的结果 解决方案

或者因为

我需要做一些特殊的特征缩放。

(Ps。我还尝试绘制二维图,其中函数的值在 y 轴上,迭代次数在 x 轴上以“调试”梯度下降,但即使我得到了一个漂亮的下坡图,解还是不是很接近。)

【问题讨论】:

    标签: python numpy optimization scipy gradient-descent


    【解决方案1】:

    引用Rosenbrock Wikipedia page

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

    梯度下降是一种简单的算法,因此它找不到最小值可能也就不足为奇了。让我们看看在 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')
    

    【讨论】:

      【解决方案2】:

      您的方法容易出现过冲。在瞬时高梯度的情况下,您的解决方案将跳得很远。在优化中,当无法降低成本时拒绝采取措施通常是合适的。

      线搜索

      通过计算梯度选择方向后,沿该方向搜索,直到将成本降低梯度范数的一部分。

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

      并且将 α 从 1.0 变为 0.0,如果已经将成本降低了梯度范数的一部分,则接受 x 的值。这是一个很好的收敛规则,称为 Armijo 规则。

      其他建议

      考虑首先优化 2D Rosenbrock 函数,然后在该成本域上绘制路径。

      考虑通过数值验证您的渐变实现是否正确。很多时候,这就是问题所在。

      【讨论】:

      • 赞赏。我想知道如果我选择了一个固定的学习率但是它很小,我会在一些迭代后仍然会超出问题吗?
      【解决方案3】:

      想象一下你正在沿着一条 knife-edge 越来越窄的山路。 恒定步长会让你越过边缘,aieeeee; 您想在攀爬时采取更短、更小心的步骤。 同样,要跟随 Rosenbrock 山谷,程序必须 随着山谷变窄,采取更短、更小心的步骤。 将步长减小为 step0 / t^0.5 或 0.25 对罗森布鲁克的 GD 有所帮助, 但对 step0 仍然非常敏感。

      真正的步长,也就是学习率,必须适应问题领域,例如 线搜索平滑问题,Ada* 为 SGD.

      顺便说一下,R​​osenbrock 函数是平方和, 并且有强大的方法;看 scipy.optimize.least_squares.

      【讨论】:

        猜你喜欢
        • 2021-07-16
        • 2020-03-23
        • 2018-08-18
        • 2017-06-05
        • 1970-01-01
        • 1970-01-01
        • 2019-01-31
        • 2014-01-11
        • 2021-12-18
        相关资源
        最近更新 更多