2.7.2.2 基于梯度的方法
2.7.2.2.1 关于梯度下降的一些直觉
这里我们关注直觉,不是代码。代码在后面。
从根本上说,梯度下降在于在梯度方向上前进小步,即最陡峭梯度的方向。
固定步数梯度下降
状况良好的二元函数。
状况糟糕的二元函数。
状况糟糕问题的梯度下降算法的核心问题是梯度并不会指向最低点。
我们可以看到非常各向异性 (状况糟糕) 函数非常难优化。
带回家的信息: 条件数和预条件化
如果你知道变量的自然刻度,预刻度他们以便他们的行为相似。这与预条件化相关。
并且,很明显采用大步幅是有优势的。这在梯度下降代码中使用直线搜索。
适应步数梯度下降
状况良好的二元函数。
状况糟糕的二元函数。
状况糟糕的非二元函数。
状况糟糕的极端非二元函数。
函数看起来越像二元函数 (椭圆半圆边框线), 最优化越简单。
2.7.2.2.2 共轭梯度下降
上面的梯度下降算法是玩具不会被用于真实的问题。
正如从上面例子中看到的,简单梯度下降算法的一个问题是,它试着摇摆穿越峡谷,每次跟随梯度的方法,以便穿越峡谷。共轭梯度通过添加摩擦力项来解决这个问题: 每一步依赖于前两个值的梯度然后急转弯减少了。
共轭梯度下降
状况糟糕的非二元函数。
状况糟糕的极端非二元函数。
在scipy中基于共轭梯度下降方法名称带有‘cg’。最小化函数的简单共轭梯度下降方法是scipy.optimize.fmin_cg():
In [5]:
def f(x): # The rosenbrock函数
return .5*(1 - x[0])**2 + (x[1] - x[0]**2)**2
optimize.fmin_cg(f, [2, 2])
Optimization terminated successfully.
Current function value: 0.000000
Iterations: 13
Function evaluations: 120
Gradient evaluations: 30
Out[5]:
array([ 0.99998968, 0.99997855])
这些方法需要函数的梯度。方法可以计算梯度,但是如果传递了梯度性能将更好:
In [6]:
def fprime(x):
return np.array((-2*.5*(1 - x[0]) - 4*x[0]*(x[1] - x[0]**2), 2*(x[1] - x[0]**2)))
optimize.fmin_cg(f, [2, 2], fprime=fprime)
Optimization terminated successfully.
Current function value: 0.000000
Iterations: 13
Function evaluations: 30
Gradient evaluations: 30
Out[6]:
array([ 0.99999199, 0.99998336])
注意函数只会评估30次,相对的没有梯度是120次。