NLopt中的无导数算法
(本文所涉及各量皆为C风格,即包含的头文件是
nlopt.h
而非
nlopt.hpp
)
首先说一下NLopt中算法的命名规则,C风格的命名规则为
NLOPT_{G,L}{N,D}_xxxx
,其中
G/L
代表全局最优与局部最优,
N/D
代表无导数与有导数。例如
NLOPT_LN_COBYLA
则表示无导数局部最优的COBYLA算法。
这里的导数是我们算完之后填入
if (grad)
{
/* 各变量的导数 */
}
中的,但是在很多情况下,当问题很复杂时,很难求解出其导数,这个时候就需要使用无导数的算法。
无导数全局最优算法
这种算法的命名为NLOPT_GN_xxxx
。最简单的方法就是在编译器中输入NLOPT_GN_
,然后它会自己弹出来有哪些算法,一个一个试,看哪个满足自己的要求即可:)。
DIRECT和DIRECT-L
DIRECT指的是Dividing Rectangles算法。
首先是基本的NLOPT_GN_DIRECT
和NLOPT_GN_DIRECT_L
。其中NLOPT_GN_DIRECT
将更多的精力放在了全局,而NLOPT_GN_DIRECT_L
算法在局部的表现会更好,其适用于没有过多局部最小值的函数。
其次是NLOPT_GN_DIRECT_L_RAND
,该变体加入了一点随机性以帮助确定将哪个维度二等分。
由于DIRECT算法将各约束缩放为超立方体(与正方体类似,只是正方体为三维,而超立方体为指定维度),因此所有维度的搜索权重是相同的。如果你要搜索的空间与超立方体相差甚远,则更适合用DIRECT的变体NLOPT_GNL_DIRECT_NOSCAL
, NLOPT_GN_DIRECT_L_NOSCAL
和NLOPT_GN_DIRECT_L_RAND_NOSCAL
。
同时,还可以使用基于原始的Fortran语言写的NLopt算法NLOPT_GN_ORIG_DIRECT
, NLOPT_GN_ORIG_DIRECT_L
,在不同的情况下,这两个的表现可能会更好,不过也可能会变差,需要实际尝试。
Controlled Random Search (CRS) with local mutation
CRS算法有点像遗传算法,它俩都是从一群随机种群点开始,通过启发式规则来随机进化这些点。
CRS的初始种群大小在n个维度上默认为10×(n+1)
,但这可以通过nlopt_set_population
函数来改变;初始总体必须至少是n+1
。同时,该算法只支持有界约束问题。
具备局部突变的CRS算法在NLopt中的名字为NLopt_GN_CRS2_LM
。
MLSL (Multi-Level Single-Linkage)
MLSL都是一种“多启动”算法:它通过从随机的或低差异的起始点执行一系列局部优化(使用一些其他的局部优化算法)来工作。MLSL是有区别的,但是“聚类”启发式帮助它避免重复搜索相同的局部最优解,并有一些理论保证在有限数量的局部最小化中找到所有的局部最优解。
MLSL的本地搜索部分可以使用NLopt中的任何其他算法,可以使用基于梯度(D
)或无梯度算法(N
)。本地搜索使用由nlopt_opt_set_local_optimizer
设置的导数/非导数算法。
基于LDS的MLSL算法被指定为NLOPT_G_MLSL_LDS
,而原始的非LDS原始MLSL(使用伪随机数,目前通过Mersenne twister算法)由NLOPT_G_MLSL
表示。在这两种情况下,都必须通过nlopt_opt_set_local_optimizer
指定本地优化算法(可以是基于梯度的,也可以是无衍生的)。
注意:如果用户没有设置停止容忍度,MLSL默认为ftol_rel=10−15
,xtol_rel=10−7
用于局部搜索。请注意,为这些本地搜索设置一个相对较大的容错,运行MLSL,然后在最后运行另一个容错较低的本地优化(使用MLSL结果作为起点),将最优值“清除”到高精度,这是完全合理的。
默认情况下,MLSL的每次迭代都采样4个随机的新试验点,但是这可以通过nlopt_set_population
函数进行更改。
该算法只支持有界约束的问题。
StoGo
StoGO是一种全局优化算法,其系统地把搜索空间(必须是有界约束)分成小的超矩形(通过一种分支界定技术),然后通过一个基于梯度的局部搜索算法(一种BFGS变体)进行搜索,可选地包括一些随机性(因此“Sto”代表“随机”)。
StoGO是用C++编写的,这意味着只有在编译启用C++算法才能包含它,在这种情况下(在Unix上)必须链接到-lnlopt_cxx
而不是-lnlopt
。
StoGO在NLopt中由NLOPT_GD_STOGO
指定,或NLOPT_GD_STOGO_RAND
指定其包含随机性的变体。
该算法只支持有界约束的问题。
AGS
AGS可以处理任意目标和非线性不等式约束。这个方法还需要绑定约束。为了保证收敛性,目标和约束必须在指定的超矩形上满足Lipschitz条件。该算法是无导数的,利用希尔伯特曲线将源问题简化为单变量问题。该算法将单变量空间划分为区间,利用后验概率生成新的点。在每次试验中,AGS试图逐个评估约束条件。如果此时违反了某些约束,则不会计算下一个约束。如果保留了所有的约束条件,即试验点是可行的,那么AGS将对目标进行评估。因此,一些约束(除了第一个)和目标可以在搜索超矩形内部分未定义。当前的AGS算法不支持向量约束。
当空间维数大于5时,机器算法的局限性不允许为希尔伯特建立一个紧密的近似值,所以这种AGS的实现在这个意义上受到限制。它最多支持10个维度,但是该方法可以在6个或更多维度的情况下提前停止。
和StoGO一样,AGS也是用C++写的,但是AGS需要编译器支持C++11且库需要使用C++来built。
AGS在NLopt中由NLOPT_GN_AGS
指定。在ags.h
中声明和描述了通用NLOpt接口中不能调整的AGS附加参数。在AGS源文件夹中给出了一个解决约束问题的例子。
ISRES(改进的随机排序进化策略)
(经本人试验,该算法在没有约束的多维线性问题中表现良好,推荐使用)
该进化策略基于突变规则(具有对数正态步长更新和指数平滑)和微分变异(类似于nelder - meadist的更新规则)的组合。对于没有非线性约束的问题,适应度排序简单地通过目标函数进行,但当包含非线性约束时,采用Runarsson和Yao提出的随机排序。ISRES的种群大小在n个维度中默认为20×(n+1),但这可以通过nlopt_set_population函数进行更改。
该方法除了支持约束外,还支持任意的非线性不等式和等式约束,并且在NLopt中指定为NLOPT_GN_ISRES
。
ESCH(进化算法)
这是一种改进的全局优化进化算法,该算法只支持边界约束,不支持非线性约束。在NLopt中指定为NLOPT_GN_ESCH
无导数局部最优算法
在无导数局部最优算法中,目前只有COBYLA算法支持任意非线性不等式与等式约束,其他算法只支持有界约束或无约束问题。(但是,通过与增广拉格朗日方法相结合,它们中的任何一个都可以应用于非线性约束问题。)
当使用局部无导数算法时,需要考虑的是优化器必须以某种方式决定初始步长。默认情况下,NLopt选择这个初始步长,但这可能并不总是最好的选择。如果不合适的话,可以修改初始步长。
COBYLA(线性逼近的约束优化)
该算法通过n维n+1点的单纯形构造目标函数和约束的连续线性逼近,并在每一步的信赖区域中优化这些逼近。
NLopt的版本在几个方面进行了轻微的修改。首先,合并了所有的NLopt终止条件;其次,我们添加了对边界约束的显式支持(尽管原始的COBYLA可以将边界约束处理为线性约束,但它有时会采取违反边界约束的步骤)。第三,我们允许COBYLA增加信任区域半径,如果预测的改进是近似正确的和单纯形是OK的,遵循一个建议,在SAS手册的PROC NLP似乎提高收敛速度。第四,我们在COBYLA算法中伪随机化单纯形步骤,通过避免不小心采取不能改善条件的步骤来提高鲁棒性(这种情况有时似乎在主动约束中发生);然而,算法仍然是确定性的(使用确定性种子)。此外,我们还支持不同参数的初始步长大小不相等(通过简单的内部调整参数与初始步长成比例),这在不同参数具有非常不同的尺度时非常重要。
底层的COBYLA代码只支持不等式约束,然而等式约束被自动转换为不等式约束对,因此在这个算法中也可以使用等式约束。
该算法在NLopt中指定为NLOPT_LN_COBYLA
。
BOBYQA
因为BOBYQA构造了目标的二次逼近,所以对于不能二次可微的目标函数,它可能执行得很差。
NLopt BOBYQA接口支持不同参数中不相等的初始步长(通过简单的内部调整参数与初始步长成比例),当不同参数具有相差悬殊的尺度时这点非常重要。
该算法在NLopt中指定为NLOPT_LN_BOBYQA
,且在很大程度上取代了下面的NEWUOA算法,NEWUOA算法是Powell提出的相同思想的早期版本。
具有边界约束的NEWUOA
因为NEWUOA构造了一个目标的二次逼近,所以它对于非二次可微的目标函数可能表现不佳。
原算法在NLopt中指定为NLOPT_LN_NEWUOA
,只支持无约束问题。对于具有边界约束的问题,其变体为NLOPT_LN_NEWUOA_BOUND
。
在原有的NEWUOA算法中,Powell通过截断共轭梯度算法求解了一个球形信任区域内的二次子问题(例为TRSAPP和BIGLAG)。在有界约束变体中,使用MMA算法来解决这些子问题的有界约束和球形信任区域。原则上,我们也应该以类似的方式改变BIGDEN子例程(因为BIGDEN也近似地解决了一个信任区域子问题),但是作者只是将它的结果截断为边界(这可能会给出次优收敛,但BIGDEN在极少数情况下才被调用)。
在作者将边界约束添加到NEWUOA后不久,Powell发布了他自己的NEWUOA版本,用于边界约束以及一些数值稳定性和收敛性增强,称为BOBYQA。NLopt现在也合并了BOBYQA,它似乎在很大程度上取代了NEWUOA。
注:NEWUOA要求参数空间的维数n≥2,即实现不处理一维优化问题。
PRAXIS(PRincipal AXIS)
在NLopt中指定为NLOPT_LN_PRAXIS
。
该算法最初是为无约束优化设计的。在NLopt中,在实践中通过在违反约束时返回无穷大(Inf)的简单权能来“实现”绑定约束(这是自动完成的—您不必在自己的函数中这样做)。如果您有约束条件,那么最好使用COBYLA或BOBYQA。
Nelder-Mead Simplex
在NLopt中指定为NLOPT_LN_NELDERMEAD
。
这种方法很简单,而且经久不衰,尽管后来发现它在一些函数中完全不收敛(在一些例子中,它可能收敛到不是局部最小值的点)。坊间证据表明,即使在嘈杂和/或不连续的目标函数中,它也经常表现良好。然而,作者更推荐Subplex方法(下面)。
Sbplx (based on Subplex)
在NLopt中指定为NLOPT_LN_SBPLX
。
Subplex (Nelder-Mead的一种变体,它在子空间序列上使用Nelder-Mead)声称比最初的Nelder-Mead更有效、更可靠,同时保留了后者的设施,但目标不连续,根据我的经验,这些说法在很多情况下似乎都是正确的。(然而,并没有什么证据表明Subplex是全局收敛的,也许它会在一些目标上失败,比如Nelder-Mead)