最优化处理寻找一个函数的最小值(最大值或零)的问题。在这种情况下,这个函数被目标函数。本文中,我们使用 scipy.optimize 来进行黑盒优化。 我们不依赖于我们优化的函数的算术表达式。注意这个表达式通常可以进行优化的。
1. 梯度下降法
梯度下降本身是沿梯度方向变化,对于一些友好的函数我们可以很容易找到最低点,但是对于一些复杂的函数,我们通过梯度下降不一定可以找到最低点。
在scipy中基于共轭梯度下降方法名称带有‘cg’。最小化函数的简单梯度下降方法是scipy.optimize.fmin_cg():
from scipy import optimize
# 这里的f 可以自定义,这里为了更加直观的演示,使用的是较为简单的函数。
def f(x):
return 0.5*(1 - x[0])**2 + (x[1] - 7)**2
# 梯度下降,这里我们要给出函数变量的初始值。
optimize.fmin_cg(f,[0,0])
在这里我们也可以主动传入函数的梯度(基于每个自变量求偏导)
def fprime(x):
return np.array((-2*.5*(1 - x[0]), 2*(x[1] - 7)))
optimize.fmin_cg(f, [0, 0], fprime=fprime)
在我们传入梯度之后,求解的性能会得到提升。
2. 牛顿法
牛顿法的基本思路是,在现有极小点估计值的附近对目标函数做二阶展开,进而找到极小点的下一个估计值。
def f(x):
return 0.5*(1 - x[0])**2 + (x[1] - 7)**2
# 一阶求导
def fprime(x):
return np.array((-2*.5*(1 - x[0]), 2*(x[1] - 7)))
# 二阶求导
def hessian(x):
return np.array(-2*.5*x[0], 2*x[1])
# 最少需要传入一阶导
optimize.fmin_ncg(f,[200,800],fprime=fprime)
optimize.fmin_ncg(f,[200,800],fprime=fprime, fhess=hessian)
3. 拟牛顿法
BFGS: BFGS (Broyden-Fletcher-Goldfarb-Shanno算法) 改进了每一步对Hessian的近似。在一些正常的函数中,BFGS 虽然不如牛顿法快,但是还是比较快的。而且对于一些情况复杂的函数,BFGS 要比牛顿法好,因为它对 Hessian 进行了改进。
def f(x):
return 0.5*(1 - x[0])**2 + (x[1] - 7)**2
# 一阶求导
def fprime(x):
return np.array((-2*.5*(1 - x[0]), 2*(x[1] - 7)))
optimize.fmin_bfgs(f,[200,800],fprime=fprime)
L-BFGS: 限制内存的 BFGS 介于 BFGS 和梯度之间: 在非常高的维度 (> 250) 计算和翻转的Hessian矩阵的成本非常高。L-BFGS保留了低秩的版本。并且在这种情况下可以不为 L-BFGS 求解器传入梯度,添加 approx_grad = 1 即可。
4. Powell 算法
和梯度下降类似的方法
def f(x):
return 0.5*(1 - x[0])**2 + (x[1] - 7)**2
# 一阶求导
def fprime(x):
return np.array((-2*.5*(1 - x[0]), 2*(x[1] - 7)))
optimize.fmin_powell(f,[0,0])
5. Nelder-Mead 算法
对噪音很强壮,他不依赖于计算梯度。因此,它可以在局部光滑的函数上工作。但是,它在光滑、非噪音函数上比基于梯度的方法慢。
def f(x):
return 0.5*(1 - x[0])**2 + (x[1] - 7)**2
# 一阶求导
def fprime(x):
return np.array((-2*.5*(1 - x[0]), 2*(x[1] - 7)))
optimize.fmin(f, [2, 2])
6. 带有限制条件的优化
更多的情况,我们需要关注的不仅仅是目标函数,同时还会对变量有一定的限制条件,下面分别介绍了三种限制条件:边界限制、等于限制、大于(小于)限制。
6.1 边界限制
最优解不会出现在指定边界以外的位置,即在指定边界内寻找最优解
def f(x):
return 0.5*(1 - x[0])**2 + (x[1] - 7)**2
# 一阶求导
def fprime(x):
return np.array((-2*.5*(1 - x[0]), 2*(x[1] - 7)))
# 这里限制了 x[0] 的取值在(1,2), x[1] 的取值在(9,19)
optimize.fmin_l_bfgs_b(f, [0,0], approx_grad=1, bounds=((1,2),(9,19)))
6.2 等于限制
最优解会满足等于限制条件,即在指定等于限制条件内寻找最优解。
def f(x):
return np.sqrt((x[0] - 3)**2 + (x[1] - 2)**2)
# 自定义限制条件
def constraint(x):
return x[0]+x[1] - 4
# eqcons == 0.0
optimize.fmin_slsqp(f, np.array([0, 0]), eqcons=[constraint,])
6.3 大于限制
最优解会满足大于限制条件,即在大于限制条件内寻找最优解。
def f(x):
return np.sqrt((x[0] - 3)**2 + (x[1] - 2)**2)
# 自定义限制条件
def constraint(x):
return x[0]+x[1] - 4
# eqcons >= 0.0
optimize.fmin_slsqp(f, np.array([0, 0]), ieqcons=[constraint,])
如果你对解决优化问题有更多的了解,你会知道许多条件优化问题可以被转化为非条件优化问题,使用被称为拉格朗日乘子法的数学技巧。