我试图在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.])
(这是真正的答案)。
但是,如果我在程序中使用非常小的step
,precision
和非常大的iterations
,那么计算就会永远存在于我的计算机上。
我想知道这是否是由于
我程序中的任何错误
或仅仅因为
梯度下降在这里效率低,要求非常小
step
,precision
和非常大的iterations
非常接近 溶液
或因为
我需要做一些特殊功能扩展。
(Ps。我也试图绘制二维图,其中函数的值在y轴上,迭代次数在x轴上,以“调试”梯度下降,但即使我看起来很漂亮下行图,解决方案仍然不是很接近。)
答案 0 :(得分:2)
您的方法很容易出现过冲。在瞬间高梯度的情况下,您的解决方案将跳得很远。在优化时,通常不适合在不能降低成本的情况下采取措施。
通过计算渐变选择方向后,沿方向搜索,直到您将成本降低一定的渐变范数。
即。从$ x _ {[n + 1]} = x - \ alpha * gradient $
开始并将$ \ alpha $从1.0变为0.0,如果将成本降低了梯度的一小部分,则接受x的值。这是一个很好的收敛规则,称为Armijo规则。
首先考虑优化2D Rosenbrock函数,并在该成本字段上绘制路径。
考虑用数字验证渐变实现是否正确。通常情况下,这就是问题所在。
答案 1 :(得分:2)
全球最小值位于一个狭长的抛物线状平坦山谷内。找到山谷是微不足道的。然而,收敛到全球最小值是困难的。
梯度下降是一种简单的算法,因此它找不到最小值也就不足为奇了。让我们看看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 :(得分:0)
想象一下,你正在徒步旅行 knife-edge 越来越窄的山路。 一个常数步长将带你越过边缘,aieeeee; 当你攀爬时,你想要采取更短,更小心的步骤。 同样,要遵循Rosenbrock山谷,一个项目必须 当山谷变窄时,采取更短,更小心的步骤。 将步长减小为step0 / t ^ 0.5或0.25 帮助GD在Rosenbrock上, 但仍然非常对step0敏感。
实际步长(又称学习率)必须适应到问题地形,例如 行搜索顺利问题,Ada * for SGD。
顺便说一句,Rosenbrock函数是一个平方和, 并且有强有力的方法;看到 scipy.optimize.least_squares。