这是我正在学习的凸优化类的一项作业。分配如下:
通过回溯线搜索实现梯度下降算法,以找到最佳步长。您的实现将与Python的
scipy.optimize.minimize
函数进行比较。要最小化的特定函数是最小二乘函数。 Python库找到的解决方案与您的实现之间的误差必须小于0.001。
我已经实现了,但是错误值徘徊在1左右,并且一直在寻找改善它的方法,但是遇到了一些麻烦。这是我编写的代码:
梯度下降+回溯线搜索实现
import numpy as np
# Gradient descent.
def min_gd(fun, x0, grad, args=()):
alpha = 0.3
beta = 0.8
delta_x = -grad(x0, *args)
t = backtracking_line_search(fun, x0, grad, delta_x, alpha, beta, args)
x_new = x0 + (t * delta_x)
if np.linalg.norm(x_new) ** 2 > np.linalg.norm(x0) ** 2:
return min_gd(fun, x_new, grad, args)
else:
return x_new
# Line search function returns optimal step size.
def backtracking_line_search(fun, x, grad, delta_x, alpha, beta, args=()):
t = 1
derprod = grad(x, *args) @ delta_x
while fun((x + (t * delta_x)), *args) > fun(x, *args) + (alpha * t * derprod):
t *= beta
return t
其他给定功能
import numpy as np
from scipy.optimize import minimize
import gd
# Least Squares function
def LeastSquares(x, A, b):
return np.linalg.norm(A @ x - b) ** 2
# gradient
def grad_LeastSquares(x, A, b):
return 2 * ((A.T @ A) @ x - A.T @ b)
这两个结果之间的误差基本上是使用L2-范数来计算的。
我想到的一些想法是我的梯度下降函数中的公差检查点可能有缺陷。现在,我实质上只是在检查下一步是否大于上一步。但是,我也无法解决如何改进的问题。
任何反馈表示赞赏。
修改
万一有人对我编写的最终代码感到好奇,以使它能以期望的方式工作:
def min_gd(fun, x0, grad, args=()):
alpha = 0.3
beta = 0.8
delta_x = -grad(x0, *args)
t = backtracking_line_search(fun, x0, grad, delta_x, alpha, beta, args)
x_new = x0 + (t * delta_x)
if np.linalg.norm(grad(x_new, *args)) < 0.01:
return x_new
else:
return min_gd(fun, x_new, grad, args)
我只是固定了条件语句,这样我不仅在比较规范,而且还在检查该值是否小于预定的公差级别。
希望这对以后的所有人都有帮助。
答案 0 :(得分:1)
您对公差检查的猜测是正确的:当前向量的范数与收敛无关。一个典型的标准是小的梯度,因此min_gd
应该看起来像
def min_gd(fun, x0, grad, args=()):
alpha = 0.3
beta = 0.8
eps = 0.001
x_new = x0
delta_x = -grad(x0, *args)
while np.linalg.norm(delta_x) > eps:
t = backtracking_line_search(fun, x_new, grad, delta_x, alpha, beta, args)
x_new = x_new + (t * delta_x)
delta_x = -grad(x_new, *args)
return x_new
其中eps
是一些小的正公差。