算法介绍

Hello,今天给大家介绍一种不基于梯度的优化算法 Nelder Mead。

Nelder Mead 算法通常是用来求解非线性(nonlinear)、导函数未知情况下目标函数的最大值或者最小值。学过梯度下降的同学应该知道,梯度下降类算法的每一步都需要计算当前位置的梯度,从而更新当前解使得最终逐渐逼近最优解。但在某一些情况下,目标函数的梯度难以求得或是函数值离散的情况下,这时候便无法直接使用梯度类算法来求解了。

Nelder Mead 算法的思想十分简单,它本质上是受空间中 Simplex 各个顶点之间关系所启发而迭代优化的一类算法。在经过多次迭代后,算法逐渐收敛到最优解。Nelder Mead 是说,我既然不使用梯度,那么能不能在空间中模拟出一个梯度,算法使用 n+1n+1 个点来构造出一个 nn 维搜索空间下的 Simplex。例如在二维空间下使用三个点构成一个 Simplex,此时是一个三角形。然后在每个 iteration 中,对这个 Simplex 进行移动、收缩或者是扩张,以使该 Simplex 往好的方向变化。

(注意:文中所说的最优解并非解析形式的最优解,基于梯度和不基于梯度的这些优化方法都是为了解决难以求得解析解而使用其他办法来逼近的一类方法)

在详细介绍算法流程之前,给大家先看几张图,直观的理解一下 Nelder Mead 算法。(图片均来源于 Wikipedia)

算法流程

我们以二维空间下寻找最优解为例,在二维空间下,一般我们会选取 2+12+1 个点构成一个 Simplex。

然后开始一个 iteration,每一次 iteration 可能遇到不同的情况,接下来我们一一讲解。

reflection

如下图所示,我们可以根据目标函数计算得到 Simplex 各个顶点的好坏,假设最左边的点是 worst point。

一个朴素的思想是,从一个差的点往好的点的方向走,那是否可能会找到一个潜在好的点。

在 reflection 操作中,我们会试探 worst point 关于另外两点连线中点的 reflection point 怎么样,将该点记为 location probed by reflection step。

expansion

假设经过一次 reflection 得到的结果比原先好,那我想更好是不是可以沿着这个方向再走一点呢?这就是 expansion,我们将 expansion 后的点记为 location probed by expansion step。

最终,如果 expansion 操作的结果比单纯 reflection 更好,此时接受 location probed by expansion step,否则接受 location probed by reflection step,接受的新点与原先的 nn 个好点共同组成新的 Simplex。

为了更清晰一点理解,假设 worst point 距离 Simplex 中另外两个好点连线的中点距离为 stepstep,那么 location probed by reflection step 距离 worst point 为 2×step2×step,而 location probed by expansion step 距离 worst point 为 3×step3×step。

contraction

假设经过一次 reflection 得到的结果比原先差,那可能说明我沿着 reflection 的方向走的太远了,但我认为这个方向应该没有多大问题。于是尝试缩小步长,此时的步长为 0.5×step0.5×step,我们记该点为 location probed by contraction step,这就是 contraction 操作。如果 contraction 以后得到的点比 worst point 更好,那么我们接受这个点,并与原先的好点组成新的 Simplex。

shrink

还有一种情况是,即使我执行了 contraction 操作,得到的点依然不好。那此时说明我们的 Simplex 可能太大了,执行 shrink 操作将所有非最优点全部往最优点(图中画圈的点)靠近其之间距离的一半,此时由 nn 个新点与旧的最优点组成新的 Simplex。

restart

在 Nelder-Mead 算法中,随着迭代的进行,Simplex 可能会变得越来越小,且每次更新的幅度都非常小,此时程序陷入一个假死的状态,为了解决该问题,我们引入了 restart 的概念。

restart 即如果程序触发我们预先设定的阈值,则重置当前的 Simplex。

在我的实验中,设定了两种阈值与不同的重置方法:

  1. 假如最优点与最差点之间经过目标函数得到的差异小于 epseps,则保留最优点随机初始化其他点
  2. 假如最优点保持了 maxAllowRepeatmaxAllowRepeat 次迭代且没有变化,则重置 Simplex 所有点

实验中,我所设定的 eps=0.001eps=0.001,maxAllowRepeat=1000maxAllowRepeat=1000

实验展示

Ellipsoid Problem

问题定义:

minf(x)=∑i=1dix2ix∈[−5.12,5.12],i=1,…,dminf(x)=∑i=1dixi2x∈[−5.12,5.12],i=1,…,d

效果展示:(其中红色的点为第一类 restart,绿色点为第二类 restart)

Global best value: 0.0000

Rosenbrock Problem

问题定义:

minf(x)=∑i=1d(100(xi+1–x2i)2+(1−xi)2)x∈[−2.048,2.048],i=1,…,dminf(x)=∑i=1d(100(xi+1–xi2)2+(1−xi)2)x∈[−2.048,2.048],i=1,…,d

效果展示:

Global best value: 1.2414

Ackley Problem

问题定义:

minf(x)=−20e−0.21d∑di=1x2i√−e1d∑di=1cos(2πxi)x∈[−32.768,32.768],i=1,…,dminf(x)=−20e−0.21d∑i=1dxi2−e1d∑i=1dcos⁡(2πxi)x∈[−32.768,32.768],i=1,…,d

效果展示:

Global best value: -22.7164

Griewank Problem

问题定义:

minf(x)=1+∑i=1dx2i4000−∏i=1dcos(xii√)x∈[−600,600],i=1,…,dminf(x)=1+∑i=1dxi24000−∏i=1dcos⁡(xii)x∈[−600,600],i=1,…,d

Global best value: 0.0302

Python 实现

尚未解决的问题:每一次反射等操作后没有检查每个点是否还在设定范围内,不过感觉问题不大。

import numpy as np
import matplotlib.pyplot as plt
from typing import Callable, Tuple, NoReturndef ellipsoid_problem(x: np.ndarray) -> np.ndarray:res = np.array(0.0)for di, xi in enumerate(x):res = res + (di + 1) * xi * xireturn resdef rosenbrock_problem(x: np.ndarray) -> np.ndarray:res = np.array(0.0)d = len(x)for i in range(d - 1):res = res + 100 * ((x[i + 1] - x[i] * x[i]) ** 2) + (1 - x[i]) ** 2res = res + 100 * ((-x[d - 1] * x[d - 1]) ** 2) + (1 - x[d - 1]) ** 2return resdef ackley_problem(x: np.ndarray) -> np.ndarray:d = len(x)tmp1, tmp2 = 0.0, 0.0for i in range(d):tmp1 += x[i] * x[i] * 1.0 / dtmp2 += np.cos(2.0 * np.pi * x[i])tmp1 = -0.2 * np.sqrt(tmp1)tmp2 = tmp2 * 1.0 / dres = -20.0 * np.exp(tmp1) - np.exp(tmp2)return resdef griewank_problem(x: np.ndarray) -> np.ndarray:d = len(x)tmp1, tmp2 = 0.0, 1.0for i in range(d):tmp1 = tmp1 + x[i] * x[i] / 4000.0tmp2 = tmp2 * np.cos(x[i] / np.sqrt(i + 1))res = 1 + tmp1 - tmp2return resdef sampling(n: int, d: int, low: float, high: float) -> np.ndarray:"""生成 n 个随机点,且每个点有 d 维,其各个坐标在 [low, high) 之间"""return np.random.rand(n, d) * (high - low) + lowdef restart(x: np.array, low: float, high: float, is_reset: bool, f: Callable[[np.ndarray], np.ndarray]) -> Tuple[np.ndarray, np.ndarray]:"""restart, is_reset 控制是否完全重置,否则保留最优值重置其他位置"""tmp_x = sampling(n=x.shape[0], d=x.shape[1], low=low, high=high)if not is_reset:f_value = f(x.T)best_idx = f_value.argmin()tmp_x[best_idx] = x[best_idx]tmp_f_value = f(tmp_x.T)return tmp_x, tmp_f_valuedef downhill_simplex_method(n: int, low: float, high: float, restart_num: int,f: Callable[[np.ndarray], np.ndarray]) -> NoReturn:"""n 维空间需要初始化 n+1 个点,逼近函数最小值"""x = sampling(n=n + 1, d=n, low=low, high=high)f_value = f(x.T)vertice_min_list = []  # 存储每一次迭代所产生的最优值vertice_min_point = []vertice_restart_reset = []  # 记录当前位置是否经历过 restart,以及 reseteps, max_allow_repeat = 1e-3, 1000  # restart 的条件,以及最大允许同一个最优值持续的轮数restart_idx = 0while True:# 找出最差的点 bad_idxbad_idx = f_value.argmax()# 求反射的 step,为差点到好点所有向量之和的一半step = (x.sum(axis=0) - x[bad_idx] * x.shape[0]) / 2.0reflection_point = x[bad_idx] + 2 * stepreflection_value = f(reflection_point)if (reflection_value < f_value[bad_idx]):x[bad_idx] = reflection_pointf_value[bad_idx] = reflection_value# 反射一次效果变好了,尝试再扩展一步expansion_point = reflection_point + stepexpansion_value = f(expansion_point)if (expansion_value < reflection_value):# 如果扩展以后效果更好,则保留这一个点,否则继续使用 reflection pointx[bad_idx] = expansion_pointf_value[bad_idx] = expansion_valueelse:# 反射不好,尝试收缩contraction_point = x[bad_idx] + step / 2.0contraction_value = f(contraction_point)if (contraction_value < f_value[bad_idx]):# 效果变好,接受x[bad_idx] = contraction_pointf_value[bad_idx] = contraction_valueelse:# shrinkbest_idx = f_value.argmin()shrink_step = (x[best_idx] - x) / 2.0x = x + shrink_stepf_value = f(x.T)f_value_min = np.min(f_value)f_value_max = np.max(f_value)vertice_min_list.append(f_value_min)vertice_min_point.append(x[f_value.argmin()])vertice_restart_reset.append(0)value_span = f_value_max - f_value_minif value_span < eps or (len(vertice_min_list) > max_allow_repeatand vertice_min_list[-max_allow_repeat] == f_value_min):if restart_idx < restart_num:print('restart... ', restart_idx + 1)restart_idx = restart_idx + 1is_reset = False if value_span < eps else Truevertice_restart_reset[-1] = 1 + int(is_reset)x, f_value = restart(x=x,low=low,high=high,is_reset=is_reset,f=f)else:break# print('{:.4f} {:.4f}'.format(value_span, f_value.min()))last_best_idx = f_value.argmin()global_best_ids = np.argmin(vertice_min_list).item()print('last best point: ', x[last_best_idx])print('last best value: {:.4f}'.format(f_value[last_best_idx]))print('global best point: ', vertice_min_point[global_best_ids])print('global best value: {:.4f}'.format(vertice_min_list[global_best_ids]))# plot showplt.title('func: {}, n: {}, restart_num: {}'.format(getattr(f, '__name__'), n, restart_num), fontsize=25)plt.xticks(fontsize=20)plt.yticks(fontsize=20)plt.xlabel('Iteration', fontsize=25)plt.ylabel('f(x)', fontsize=25)plt_x = np.arange(len(vertice_min_list))plt_y = np.array(vertice_min_list)plt.plot(plt_x, plt_y)  # 画最优值曲线restart_point_x = plt_x[np.array(vertice_restart_reset) == 1]plt.scatter(restart_point_x, plt_y[restart_point_x], c='r')  # 描 restart 点restart_point_x = plt_x[np.array(vertice_restart_reset) == 2]plt.scatter(restart_point_x,plt_y[restart_point_x],c='green',linewidths=10)  # 描 reset 点plt.show()if __name__ == '__main__':n = 5low, high = -5.12, 5.12restart_num = 50downhill_simplex_method(n=n,low=low,high=high,restart_num=restart_num,f=ellipsoid_problem)# n = 5# low, high = -2.048, 2.048# restart_num = 50# downhill_simplex_method(n=n,#                         low=low,#                         high=high,#                         restart_num=restart_num,#                         f=rosenbrock_problem)# n = 5# low, high = -32.768, 32.768# restart_num = 100# downhill_simplex_method(n=n,#                         low=low,#                         high=high,#                         restart_num=restart_num,#                         f=ackley_problem)# n = 5# low, high = -600.0, 600.0# restart_num = 100# downhill_simplex_method(n=n,#                         low=low,#                         high=high,#                         restart_num=restart_num,#                         f=griewank_problem)pass

非梯度类启发式搜索算法:Nelder Mead相关推荐

  1. Nelder Mead算法推荐阅读博文

    近期在看优化算法,翻了很多教程,发现下面这两篇不错的文章,讲的很浅显易懂,记录一下.建议先看第一篇再看第二篇里面具体的算法步骤,会更加容易理解. 非梯度类启发式搜索算法:Nelder Mead – D ...

  2. 单纯性搜索算法 matlab函数,matlab : Nelder mead simplex 单纯形直接搜索算法;

    function [ param ] = NeldSearch( param ) %NERDSEARCH 此处显示有关此函数的摘要 % nelder mead simplex 单纯形直接搜索算法: % ...

  3. 【人工智能】1.问题求解:启发式搜索算法

    A* 算法是启发式搜索算法中的经典,经常应用于图搜索.路径搜索和规划中.这里以八数码问题状态空间图的搜索为例,初步介绍以A*算法为代表的启发式搜索. 搜索算法可分为两大类:无信息的搜索算法和有信息的搜 ...

  4. A*启发式搜索算法详解 人工智能

    A*启发式搜索算法详解 人工智能 我们尝试解决的问题是把一个游戏对象(game object)从出发点移动到目的地.路径搜索(Pathfinding)的目标是找到一条好的路径--避免障碍物.敌人,并把 ...

  5. Nelder Mead SIMPLEX Algorithm

    Nelder Mead SIMPLEX Algorithm 单纯形 算法 Nelder-Mead 算法的简单实现 示例 Rosenbrock函数 用Nelder-Mead Simplex算法优化 最后 ...

  6. 转载:改进的双向启发式搜索算法及其在车载导航仪中的应用

    非常感谢作者. 摘要:介绍单车辆路径规划的有关算法,针对车载导航仪的应用,对双向启动式搜索算法进行了改进和优化,提出了可靠有效的搜索终止条件和搜索切换标准,给出了改进算法的流程.最后给出了四种算法的实 ...

  7. c语言 如何读多种数据类型 非类,c语言程序设计教学大纲(非电气类)文档.doc

    c语言程序设计教学大纲(非电气类)文档 <C语言程序设计>课程教学大纲 主任 教研室主任 大纲执笔人 姜长洪 王海荣 C语言备课组 一.课程基本信息 课程编号:×××× 课程名称:C语言程 ...

  8. 你必须会的启发式搜索算法--A*算法

    一.算法原理 A* 算法,就是解决在一个平面 grid地图中寻找起点到终点的最短路径问题的算法,类似于Dijkstra算法和BFS算法一样,属于广度优先搜索.实际上它还是一个启发式搜索算法,什么叫启发 ...

  9. 2021年 第13届 全国大学生数学竞赛 初赛(非数学类)试题详细解答

    [2020年第12届全国大学生数学竞赛--资源分享 ][1~11届省赛决赛考题及题解(数学类.非数学类).推荐学习网址.复习备考书籍推荐] 2019年 第11届 全国大学生数学竞赛 初赛(非数学类)试 ...

最新文章

  1. 内存分配策略(二):JMM,Java Memory Model
  2. HTML做3D立体特效,html5 3D立体粒子波浪动画特效代码
  3. Android自定义View实现滴滴验证码输入框效果
  4. Java基础day22
  5. 任意编程语言访问PostgreSQL:C++接口
  6. mysql验证配置_详解MySQL|教你一招如何自动验证 MySQL 配置正确性
  7. Vue2.0 --- vue-cli脚手架中全局引入JQ
  8. 斯坦福大学NLP公开课CS224n上映啦!华人助教陪你追剧
  9. hexo需要先编辑好html文件吗,教你定制Hexo的landscape打造自己的主题_html/css_WEB-ITnose...
  10. threejs 热力图做成材质_脉冲滤筒除尘器滤芯的材质
  11. flash电脑安装包_一百余款电脑软件及安装方式,忍不住收藏起来
  12. 可用于龙芯2F的gmp
  13. 客户端无法向springcloud注册中心注册服务,提示连接超时
  14. 基于51单片机远近光灯切换电路设计方案
  15. 一站式数字藏品收款系统开发解决方案
  16. Python实现图像的手绘效果
  17. 【无标题】微信小程序:强制更新(测试编译)
  18. 使用layui框架迅速搭建后台管理页面
  19. 数据分析师该这样霸气回应“0.00008的转化也很好”的谬论
  20. Lenovo笔记本电脑触摸板无反应-解决方法

热门文章

  1. 推荐一个有用的Excel操作类库 LinqToExcel
  2. Ncut源码编译错误的解决方法
  3. flink maven依赖自用(项目用)
  4. 液晶分子平行排列MATLAB程序,液晶百问:液晶分子排列方向如何改变
  5. MOSFET的双脉冲测试-1
  6. 【机器人栅格地图】基于蚁群优化遗传算法求解机器人栅格地图最短路径规划问题附Matlab源码
  7. 探索在原生网页中使用自定义数据属性
  8. 消除SDK更新时的“https://dl-ssl.google.com refused”错误
  9. 【转】为什么很多看起来不是很复杂的网站,比如 Facebook、淘宝,都需要大量顶尖高手来开发?...
  10. Java+JSP+Mysql+Tomcat实现Web图书管理系统