Machine Learning Mastery 优化教程(一)

原文:Machine Learning Mastery

协议:CC BY-NC-SA 4.0

用于函数优化的一维测试函数

原文:https://machinelearningmastery.com/1d-test-functions-for-function-optimization/

最后更新于 2021 年 9 月 25 日

函数优化是一个研究领域,它寻求函数的输入,从而得到函数的最大或最小输出。

有大量的优化算法,在简单和易于可视化的测试函数上研究和开发优化算法的直觉是很重要的。

一维函数取单个输入值,输出输入的单个评估。它们可能是研究函数优化时最简单的测试函数。

一维函数的好处是,它们可以被可视化为二维图,x 轴上的函数输入和 y 轴上的函数输出。函数的已知最佳值和函数的任何采样值也可以画在同一个图上。

在本教程中,您将发现学习函数优化时可以使用的标准一维函数。

用我的新书机器学习优化启动你的项目,包括分步教程和所有示例的 Python 源代码文件。

Let’s get started.外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

函数优化的一维(1D)测试功能
图片由 Max Benidze 提供,保留部分权利。

教程概述

我们可以使用许多不同类型的简单一维测试函数。

然而,在函数优化领域中,有一些常用的标准测试函数。在测试不同的算法时,我们可能希望选择测试函数的特定属性。

我们将在本教程中探索少量简单的一维测试函数,并用五个不同的组按照它们的属性来组织它们;它们是:

  1. 凸单峰函数
  2. 非凸单峰函数
  3. 多模态函数
  4. 不连续函数(非光滑)
  5. 噪声函数

每个函数都将使用 Python 代码呈现,带有目标目标函数的函数实现和该函数的示例,该示例显示为带有明确标记的函数最优值的线图。

所有函数都表示为最小化问题,例如,找到导致函数最小(最小值)输出的输入。通过给所有输出加上负号,任何最大化函数都可以变成最小化函数。类似地,任何最小化函数都可以用同样的方法最大化。

这些功能不是我发明的;它们取自文献。有关参考资料,请参见进一步阅读部分。

然后,您可以选择并复制粘贴一个或多个函数的代码,以便在自己的项目中研究或比较优化算法的行为。

凸单峰函数

一个凸函数是一个可以在域内任意两点之间画一条线,并且这条线保持在域内的函数。

对于显示为二维图的一维函数,这意味着该函数具有碗形,并且两者之间的线保持在碗的上方。

单峰表示函数有一个单一的最优值。凸函数可以是单峰的,也可以不是单峰的;类似地,单峰函数可以是凸的,也可以不是凸的。

下面函数的范围限制在-5.0 和 5.0 之间,最佳输入值为 0.0。

# convex unimodal optimization function
from numpy import arange
from matplotlib import pyplot

# objective function
def objective(x):
	return x**2.0

# define range for input
r_min, r_max = -5.0, 5.0
# sample input range uniformly at 0.1 increments
inputs = arange(r_min, r_max, 0.1)
# compute targets
results = objective(inputs)
# create a line plot of input vs result
pyplot.plot(inputs, results)
# define optimal input value
x_optima = 0.0
# draw a vertical line at the optimal input
pyplot.axvline(x=x_optima, ls='--', color='red')
# show the plot
pyplot.show()

运行该示例会创建函数的线图,并用红线标记 optima。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

凸单峰优化函数的线图

通过增加或减少一个恒定值,例如 5 + x².,该功能可以在数字线上向前或向后移动

如果希望将最佳输入从 0.0 的值移开,这可能是有用的。

非凸单峰函数

如果不能在域中的两点之间画一条线,并且这条线仍在域中,则函数是非凸的。

这意味着可以在域中找到两个点,在这两个点之间的直线与函数的线图相交。

典型地,如果一维函数的一个图有不止一个山或谷,那么我们立即知道该函数是非凸的。然而,非凸函数可以是单峰的,也可以不是单峰的。

我们感兴趣优化的大多数实函数都是非凸的。

下面函数的范围限制在-10.0 和 10.0 之间,最佳输入值为 0.67956。

# non-convex unimodal optimization function
from numpy import arange
from numpy import sin
from numpy import exp
from matplotlib import pyplot

# objective function
def objective(x):
	return -(x + sin(x)) * exp(-x**2.0)

# define range for input
r_min, r_max = -10.0, 10.0
# sample input range uniformly at 0.1 increments
inputs = arange(r_min, r_max, 0.1)
# compute targets
results = objective(inputs)
# create a line plot of input vs result
pyplot.plot(inputs, results)
# define optimal input value
x_optima = 0.67956
# draw a vertical line at the optimal input
pyplot.axvline(x=x_optima, ls='--', color='red')
# show the plot
pyplot.show()

运行该示例会创建函数的线图,并用红线标记 optima。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

非凸单峰优化函数的线图

多模态函数

A 多模态功能是指具有一个以上“模式或 optima(如谷值)的功能。

多峰函数是非凸的。

可能有一个全局最优解和一个或多个局部或欺骗性最优解。或者,可能有多个全局最优值,即多个不同的输入导致相同的函数最小输出。

让我们看几个多模态函数的例子。

多模式功能 1

范围以-2.7 和 7.5 为界,最佳输入值为 5.145735。

# multimodal function
from numpy import sin
from numpy import arange
from matplotlib import pyplot

# objective function
def objective(x):
	return sin(x) + sin((10.0 / 3.0) * x)

# define range for input
r_min, r_max = -2.7, 7.5
# sample input range uniformly at 0.1 increments
inputs = arange(r_min, r_max, 0.1)
# compute targets
results = objective(inputs)
# create a line plot of input vs result
pyplot.plot(inputs, results)
# define optimal input value
x_optima = 5.145735
# draw a vertical line at the optimal input
pyplot.axvline(x=x_optima, ls='--', color='red')
# show the plot
pyplot.show()

运行该示例会创建函数的线图,并用红线标记 optima。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

多模态优化函数 1 的线图

多模态函数 2

范围以 0.0 和 1.2 为界,最佳输入值为 0.96609。

# multimodal function
from numpy import sin
from numpy import arange
from matplotlib import pyplot

# objective function
def objective(x):
	return -(1.4 - 3.0 * x) * sin(18.0 * x)

# define range for input
r_min, r_max = 0.0, 1.2
# sample input range uniformly at 0.01 increments
inputs = arange(r_min, r_max, 0.01)
# compute targets
results = objective(inputs)
# create a line plot of input vs result
pyplot.plot(inputs, results)
# define optimal input value
x_optima = 0.96609
# draw a vertical line at the optimal input
pyplot.axvline(x=x_optima, ls='--', color='red')
# show the plot
pyplot.show()

运行该示例会创建函数的线图,并用红线标记 optima。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

多模态优化函数 2 的线图

多模态函数 3

范围以 0.0 和 10.0 为界,最佳输入值为 7.9787。

# multimodal function
from numpy import sin
from numpy import arange
from matplotlib import pyplot

# objective function
def objective(x):
	return -x * sin(x)

# define range for input
r_min, r_max = 0.0, 10.0
# sample input range uniformly at 0.1 increments
inputs = arange(r_min, r_max, 0.1)
# compute targets
results = objective(inputs)
# create a line plot of input vs result
pyplot.plot(inputs, results)
# define optimal input value
x_optima = 7.9787
# draw a vertical line at the optimal input
pyplot.axvline(x=x_optima, ls='--', color='red')
# show the plot
pyplot.show()

运行该示例会创建函数的线图,并用红线标记 optima。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

多模态优化函数 3 的线图

不连续函数(非光滑)

一个函数可能有一个间断,这意味着函数输入的平滑变化可能导致输出的非平滑变化。

我们可以将具有这种性质的函数称为非光滑函数或不连续函数。

有许多不同类型的不连续性,尽管一个常见的例子是函数输出值的跳跃或方向的急剧变化,这在函数的图中很容易看到。

间断函数

范围在-2.0 和 2.0 之间,最佳输入值为 1.0。

# non-smooth optimization function
from numpy import arange
from matplotlib import pyplot

# objective function
def objective(x):
	if x > 1.0:
		return x**2.0
	elif x == 1.0:
		return 0.0
	return 2.0 - x

# define range for input
r_min, r_max = -2.0, 2.0
# sample input range uniformly at 0.1 increments
inputs = arange(r_min, r_max, 0.1)
# compute targets
results = [objective(x) for x in inputs]
# create a line plot of input vs result
pyplot.plot(inputs, results)
# define optimal input value
x_optima = 1.0
# draw a vertical line at the optimal input
pyplot.axvline(x=x_optima, ls='--', color='red')
# show the plot
pyplot.show()

运行该示例会创建函数的线图,并用红线标记 optima。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

不连续优化函数的线图

噪声函数

一个函数可能有噪声,这意味着每个评估可能有一个随机分量,每次都会稍微改变函数的输出。

通过向输入值添加小的高斯随机数,可以使任何无噪声函数变得有噪声。

下面函数的范围限制在-5.0 和 5.0 之间,最佳输入值为 0.0。

# noisy optimization function
from numpy import arange
from numpy.random import randn
from matplotlib import pyplot

# objective function
def objective(x):
	return (x + randn(len(x))*0.3)**2.0

# define range for input
r_min, r_max = -5.0, 5.0
# sample input range uniformly at 0.1 increments
inputs = arange(r_min, r_max, 0.1)
# compute targets
results = objective(inputs)
# create a line plot of input vs result
pyplot.plot(inputs, results)
# define optimal input value
x_optima = 0.0
# draw a vertical line at the optimal input
pyplot.axvline(x=x_optima, ls='--', color='red')
# show the plot
pyplot.show()

运行该示例会创建函数的线图,并用红线标记 optima。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

噪声优化函数的线图

进一步阅读

如果您想更深入地了解这个主题,本节将提供更多资源。

文章

摘要

在本教程中,您发现了学习函数优化时可以使用的标准一维函数。

您是否在使用上述任何功能?
下面的评论让我知道是哪一个。

你有什么问题吗?
在下面的评论中提问,我会尽力回答。

用于函数优化的二维测试函数

原文:https://machinelearningmastery.com/2d-test-functions-for-function-optimization/

最后更新于 2021 年 10 月 12 日

函数优化是一个研究领域,它寻求函数的输入,从而得到函数的最大或最小输出。

有大量的优化算法,在简单和易于可视化的测试函数上研究和开发优化算法的直觉是很重要的。

二维函数取两个输入值(x 和 y),输出对输入的单次求值。它们是研究函数优化时使用的最简单的测试函数。二维函数的好处是,它们可以被可视化为等高线图或表面图,显示问题域的地形,域的最优值和样本用点标记。

在本教程中,您将发现学习函数优化时可以使用的标准二维函数。

用我的新书机器学习优化启动你的项目,包括分步教程和所有示例的 Python 源代码文件。

Let’s get started.外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

函数优化的二维(2D)测试功能
摄于摄于,版权所有。

教程概述

二维函数是采用两个输入变量并计算目标值的函数。

我们可以把两个输入变量想象成一个图上的两个轴,x 和 y,函数的每个输入都是图上的一个点,函数的结果可以作为图上的高度。

这允许将功能概念化为表面,并且我们可以基于表面的结构来表征功能。例如,输入点的丘陵导致目标函数的相对结果较大,输入点的山谷导致目标函数的相对结果较小。

一个曲面可能有一个主要特征或全局最优,或者它可能有许多地方需要优化才能卡住。该表面可能是光滑的、有噪声的、凸的,以及我们在测试优化算法时可能关心的所有其他性质。

我们可以使用许多不同类型的简单二维测试函数。

然而,在函数优化领域中,有一些常用的标准测试函数。在测试不同的算法时,我们可能希望选择测试函数的特定属性。

我们将在本教程中探索少量简单的二维测试函数,并通过它们的属性用两个不同的组来组织它们;它们是:

  1. 单峰函数
    1. 单峰函数 1
    2. 单峰函数 2
    3. 单峰函数 3
  2. 多模态函数
    1. 多模式功能 1
    2. 多模态函数 2
    3. 多模态函数 3

每个函数都将使用 Python 代码呈现,目标函数的函数实现和函数的采样显示为表面图。

所有函数都表示为最小化函数,例如,找到导致函数最小(最小值)输出的输入。通过给所有输出加上负号,任何最大化函数都可以变成最小化函数。类似地,任何最小化函数都可以用同样的方法最大化。

这些功能不是我发明的;它们取自文献。有关参考资料,请参见进一步阅读部分。

然后,您可以选择并复制粘贴一个或多个要在自己的项目中使用的函数的代码,以研究或比较优化算法的行为。

单峰函数

单峰意味着函数有一个全局最优值。

一个单峰函数可以是凸的,也可以不是凸的。凸函数是这样一种函数,其中可以在域中的任意两点之间画一条线,并且这条线保持在域中。对于显示为等高线或曲面图的二维函数,这意味着该函数具有碗形,并且两者之间的线保持在碗的上方或内部。

让我们看几个单峰函数的例子。

单峰函数 1

该范围被限制在-5.0 和 5.0,一个全局最优值在[0.0,0.0]。

# unimodal test function
from numpy import arange
from numpy import meshgrid
from matplotlib import pyplot
from mpl_toolkits.mplot3d import Axes3D

# objective function
def objective(x, y):
	return x**2.0 + y**2.0

# define range for input
r_min, r_max = -5.0, 5.0
# sample input range uniformly at 0.1 increments
xaxis = arange(r_min, r_max, 0.1)
yaxis = arange(r_min, r_max, 0.1)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# compute targets
results = objective(x, y)
# create a surface plot with the jet color scheme
figure = pyplot.figure()
axis = figure.gca(projection='3d')
axis.plot_surface(x, y, results, cmap='jet')
# show the plot
pyplot.show()

运行该示例会创建函数的曲面图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

单峰优化函数 1 的曲面图

单峰函数 2

该范围被限制在-10.0 和 10.0,一个全局最优值在[0.0,0.0]。

# unimodal test function
from numpy import arange
from numpy import meshgrid
from matplotlib import pyplot
from mpl_toolkits.mplot3d import Axes3D

# objective function
def objective(x, y):
	return 0.26 * (x**2 + y**2) - 0.48 * x * y

# define range for input
r_min, r_max = -10.0, 10.0
# sample input range uniformly at 0.1 increments
xaxis = arange(r_min, r_max, 0.1)
yaxis = arange(r_min, r_max, 0.1)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# compute targets
results = objective(x, y)
# create a surface plot with the jet color scheme
figure = pyplot.figure()
axis = figure.gca(projection='3d')
axis.plot_surface(x, y, results, cmap='jet')
# show the plot
pyplot.show()

运行该示例会创建函数的曲面图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

单峰优化函数 2 的曲面图

单峰函数 3

该范围被限制在-10.0 和 10.0,并且在[pi,pi]处有一个全局最优值。这个函数被称为 Easom 的函数。

# unimodal test function
from numpy import cos
from numpy import exp
from numpy import pi
from numpy import arange
from numpy import meshgrid
from matplotlib import pyplot
from mpl_toolkits.mplot3d import Axes3D

# objective function
def objective(x, y):
	return -cos(x) * cos(y) * exp(-((x - pi)**2 + (y - pi)**2))

# define range for input
r_min, r_max = -10, 10
# sample input range uniformly at 0.01 increments
xaxis = arange(r_min, r_max, 0.01)
yaxis = arange(r_min, r_max, 0.01)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# compute targets
results = objective(x, y)
# create a surface plot with the jet color scheme
figure = pyplot.figure()
axis = figure.gca(projection='3d')
axis.plot_surface(x, y, results, cmap='jet')
# show the plot
pyplot.show()

运行该示例会创建函数的曲面图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

单峰优化函数 3 的曲面图

多模态函数

A 多模态功能是指具有一个以上“模式或 optima(如谷值)的功能。

多峰函数是非凸的。

可能有一个全局最优解和一个或多个局部或欺骗性最优解。或者,可能有多个全局最优值,即多个不同的输入导致相同的函数最小输出。

让我们看几个多模态函数的例子。

多模式功能 1

该范围被限制在-5.0 和 5.0,一个全局最优值在[0.0,0.0]。这个函数被称为阿克利函数

# multimodal test function
from numpy import arange
from numpy import exp
from numpy import sqrt
from numpy import cos
from numpy import e
from numpy import pi
from numpy import meshgrid
from matplotlib import pyplot
from mpl_toolkits.mplot3d import Axes3D

# objective function
def objective(x, y):
	return -20.0 * exp(-0.2 * sqrt(0.5 * (x**2 + y**2))) - exp(0.5 * (cos(2 * pi * x) + cos(2 * pi * y))) + e + 20

# define range for input
r_min, r_max = -5.0, 5.0
# sample input range uniformly at 0.1 increments
xaxis = arange(r_min, r_max, 0.1)
yaxis = arange(r_min, r_max, 0.1)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# compute targets
results = objective(x, y)
# create a surface plot with the jet color scheme
figure = pyplot.figure()
axis = figure.gca(projection='3d')
axis.plot_surface(x, y, results, cmap='jet')
# show the plot
pyplot.show()

运行该示例会创建函数的曲面图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

多模态优化函数 1 的曲面图

多模态函数 2

该范围以-5.0 和 5.0 为界,并在[3.0,2.0],[-2.805118,3.131312],[-3.779310,-3.283186],[3.584428,-1.848126]处作为四个全局最优。这个功能被称为 Himmelblau 的功能

# multimodal test function
from numpy import arange
from numpy import meshgrid
from matplotlib import pyplot
from mpl_toolkits.mplot3d import Axes3D

# objective function
def objective(x, y):
	return (x**2 + y - 11)**2 + (x + y**2 -7)**2

# define range for input
r_min, r_max = -5.0, 5.0
# sample input range uniformly at 0.1 increments
xaxis = arange(r_min, r_max, 0.1)
yaxis = arange(r_min, r_max, 0.1)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# compute targets
results = objective(x, y)
# create a surface plot with the jet color scheme
figure = pyplot.figure()
axis = figure.gca(projection='3d')
axis.plot_surface(x, y, results, cmap='jet')
# show the plot
pyplot.show()

运行该示例会创建函数的曲面图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

多模态优化函数 2 的曲面图

多模态函数 3

范围以-10.0 和 10.0 为界,函数为四个全局最优值,分别为[8.05502,9.66459],[-8.05502,9.66459],[8.05502,-9.66459],[-8.05502,-9.66459]。这个函数被称为霍尔德表函数。

# multimodal test function
from numpy import arange
from numpy import exp
from numpy import sqrt
from numpy import cos
from numpy import sin
from numpy import e
from numpy import pi
from numpy import absolute
from numpy import meshgrid
from matplotlib import pyplot
from mpl_toolkits.mplot3d import Axes3D

# objective function
def objective(x, y):
	return -absolute(sin(x) * cos(y) * exp(absolute(1 - (sqrt(x**2 + y**2)/pi))))

# define range for input
r_min, r_max = -10.0, 10.0
# sample input range uniformly at 0.1 increments
xaxis = arange(r_min, r_max, 0.1)
yaxis = arange(r_min, r_max, 0.1)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# compute targets
results = objective(x, y)
# create a surface plot with the jet color scheme
figure = pyplot.figure()
axis = figure.gca(projection='3d')
axis.plot_surface(x, y, results, cmap='jet')
# show the plot
pyplot.show()

运行该示例会创建函数的曲面图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

多模态优化函数 3 的曲面图

进一步阅读

如果您想更深入地了解这个主题,本节将提供更多资源。

文章

摘要

在本教程中,您发现了学习函数优化时可以使用的标准二维函数。

您是否在使用上述任何功能?
下面的评论让我知道是哪一个。

你有什么问题吗?
在下面的评论中提问,我会尽力回答。

粒子群优化的温和介绍

原文:https://machinelearningmastery.com/a-gentle-introduction-to-particle-swarm-optimization/

最后更新于 2021 年 10 月 12 日

粒子群优化算法是一种生物启发算法,它是一种在解空间中寻找最优解的简单算法。它与其他优化算法的不同之处在于,它只需要目标函数,并且不依赖于目标的梯度或任何微分形式。它的超参数也很少。

在本教程中,您将通过一个例子学习粒子群算法的基本原理及其算法。参加完本教程后,您将会知道:

  • 粒子群算法下的粒子群及其行为是什么
  • 粒子群算法能解决什么样的优化问题
  • 如何使用粒子群优化算法解决问题
  • 粒子群算法有哪些变化

用我的新书机器学习优化启动你的项目,包括分步教程和所有示例的 Python 源代码文件。

Let’s get started.

粒子群

粒子群优化是肯尼迪和埃伯哈特在 1995 年提出的。正如在原始论文中提到的,社会生物学家认为一群鱼或一群鸟在一个群体中移动“可以从所有其他成员的经验中受益”。换句话说,例如,当一只鸟在飞行并随机寻找食物时,鸟群中的所有鸟都可以分享它们的发现,并帮助整个鸟群获得最佳的狩猎。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

粒子群优化。
唐·德博尔德摄,版权所有。

我们可以模拟一群鸟的运动的同时,也可以想象每一只鸟都是帮助我们在高维解空间中寻找最优解,而鸟群找到的最优解就是空间中的最优解。这是一个启发式解,因为我们永远无法证明真正的全局最优解是可以找到的,而它通常不是。然而,我们经常发现粒子群算法找到的解非常接近全局最优解。

示例优化问题

粒子群算法最适合用来寻找多维向量空间上定义的函数的最大值或最小值。假设我们有一个函数 f ( X ) f(X) f(X)从向量参数 X X X(如坐标 ( x , y ) (x,y) (xy)在一个平面中)产生一个实值,并且 X X X可以取空间中几乎任何值(例如, f ( X ) f(X) f(X)是高度,我们可以为平面上的任何点找到一个),那么我们可以应用 PSO。粒子群算法将返回它发现的产生最小 f ( X ) f(X) f(X)的参数 X X X

让我们从下面的函数开始

$
f(x,y)=(x-3.14)²+(y-2.72)²+\ sin(3x+1.41)+\ sin(4y-1.73)
$

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

f(x,y)图

从上面的图中我们可以看到,这个函数看起来像一个弯曲的鸡蛋纸盒。它不是一个凸函数,因此很难找到它的最小值,因为找到的一个局部最小值不一定是全局最小值

那么如何才能找到这个函数的最小值呢?当然,我们可以求助于穷举搜索:如果我们检查平面上每个点的 f ( x , y ) f(x,y) f(xy)的值,我们可以找到最小点。或者我们可以在平面上随机找一些样本点,看看在 f ( x , y ) f(x,y) f(xy)上哪个给出的值最低,如果我们认为搜索每个点太贵的话。然而,我们也从 f ( x , y ) f(x,y) f(xy)的形状中注意到,如果我们找到了一个具有较小值 f ( x , y ) f(x,y) f(xy)的点,那么在它的邻近点附近更容易找到一个更小的值。

粒子群优化就是这样做的。类似于鸟群寻找食物,我们从平面上的多个随机点开始(称之为粒子),让它们在随机方向寻找最小点。在每一步中,每个粒子都应该围绕它曾经找到的最小点以及整个粒子群找到的最小点进行搜索。经过一定的迭代,我们认为函数的最小点是这个粒子群探索过的最小点。

算法详细信息

假设我们有 P P P粒子,我们把粒子 i i i在迭代 t t t中的位置表示为 X i ( t ) X^i(t) Xi(t),在上面的例子中,我们把它表示为坐标 x i ( t = ( x i ( t ) , y i ( t ) ) . x^i(t =(x^i(t),y^i(t)). xi(t=(xi(t)yi(t)).除了位置,我们还有每个粒子的速度,表示为 V i ( t ) = ( v x i ( t ) , v y i ( t ) ) V^i(t)=(v_x^i(t),v_y^i(t)) Vi(t)=(vxi(t)vyi(t)).在下一次迭代中,每个粒子的位置将被更新为
$
x^i(t+1)= xi(t)+vi(t+1)
$
或者等效地,
$
\ begin { aligned }
x^i(t+1)&= xi(t)+v_xi(t+1)\
y^i(t+1)&= yi(t)+v_yi(t+1)
\ end { aligned }
$
同时, 速度也由规则
$
v^i(t+1)=
w vi(t)+c_1r_1(pbesti–x^i(t)+c _ 2r _ 2(gbest–x^i(t)】
$
更新,其中 r 1 r_1 r1 r 2 r_2 r2是 0 到 1 之间的随机数,常量 w w w c 1 c_1 c1、和 c 2 c_2 c2是粒子群算法的参数, p b e s t i pbest^i pbesti是粒子 i i i g b e s t gbest gbest探索过的给出最佳 f ( X ) f(X) f(X)值的位置

请注意, p b e s t i pbest^i pbesti X i ( t ) X^i(t) Xi(t)是两个位置向量, p b e s t i − x i ( t ) pbest^i-x^i(t) pbestixi(t)之间的差异是向量减法。将这个减法与原始速度 V i ( t ) V^i(t) Vi(t)相加,将使粒子回到 p b e s t i pbest^i pbesti.位置 X^i(t)$.的差价也是如此

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

向量减法。
公共领域本杰明·d·埃沙姆绘制的图表。

我们称参数 w w w为惯性重量常数。它介于 0 和 1 之间,决定了粒子应该保持其先前速度(即搜索的速度和方向)的多少。参数$ C1 和 和 C2 $分别称为认知系数和社会系数。它们控制在细化粒子本身的搜索结果和识别群体的搜索结果之间应该给予多少权重。我们可以认为这些参数控制着勘探开采之间的权衡。

p b e s t i pbest^i pbesti g b e s t gbest gbest的位置在每次迭代中都会更新,以反映迄今为止找到的最佳位置。

该算法区别于其他优化算法的一个有趣的特性是它不依赖于目标函数的梯度。例如,在梯度下降中,我们通过将 X X X移动到 − ∇ f ( X ) -\nabla f(X) f(X)的方向来寻找函数 f ( X ) f(X) f(X)的最小值,因为这是函数下降最快的地方。对于当前位置 X X X的任何粒子,它如何移动并不取决于哪个方向是“下坡”,而只取决于 p b e s t pbest pbest g b e s t gbest gbest在哪里。如果区分 f ( X ) f(X) f(X)很困难,这使得粒子群算法特别适合。

粒子群算法的另一个特点是易于并行化。当我们操纵多个粒子来寻找最优解时,每个粒子都可以并行更新,我们只需要在每次迭代中收集一次 g b e s t gbest gbest的更新值。这使得地图缩减架构成为实现粒子群优化的完美候选。

履行

这里我们展示了如何实现粒子群算法来找到最优解。

对于上面显示的相同函数,我们可以首先将其定义为 Python 函数,并在等高线图中显示:

import numpy as np
import matplotlib.pyplot as plt

def f(x,y):
    "Objective function"
    return (x-3.14)**2 + (y-2.72)**2 + np.sin(3*x+1.41) + np.sin(4*y-1.73)

# Contour plot: With the global minimum showed as "X" on the plot
x, y = np.array(np.meshgrid(np.linspace(0,5,100), np.linspace(0,5,100)))
z = f(x, y)
x_min = x.ravel()[z.argmin()]
y_min = y.ravel()[z.argmin()]
plt.figure(figsize=(8,6))
plt.imshow(z, extent=[0, 5, 0, 5], origin='lower', cmap='viridis', alpha=0.5)
plt.colorbar()
plt.plot([x_min], [y_min], marker='x', markersize=5, color="white")
contours = plt.contour(x, y, z, 10, colors='black', alpha=0.4)
plt.clabel(contours, inline=True, fontsize=8, fmt="%.0f")
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

f(x,y)等值线图

这里我们在 0 ≤ x , y ≤ 5 0\le x,y\le 5 0xy5的范围内绘制了函数 f ( x , y ) f(x,y) f(xy)的曲线。我们可以在该区域的随机位置创建 20 个粒子,以及在平均值为 0、标准偏差为 0.1 的正态分布上采样的随机速度,如下所示:

n_particles = 20
X = np.random.rand(2, n_particles) * 5
V = np.random.randn(2, n_particles) * 0.1

我们可以在相同的等高线图上显示它们的位置:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

粒子的初始位置

由此,我们已经可以发现 g b e s t gbest gbest是所有粒子找到的最佳位置。由于粒子根本没有探索,它们目前的位置也是它们的 p b e s t i pbest^i pbesti:

pbest = X
pbest_obj = f(X[0], X[1])
gbest = pbest[:, pbest_obj.argmin()]
gbest_obj = pbest_obj.min()

向量pbest_obj是每个粒子找到的目标函数的最佳值。类似地,gbest_obj是群找到的目标函数的最佳标量值。我们在这里使用min()argmin()函数,因为我们将其设置为最小化问题。gbest的位置标记为下方的一颗星

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

gbest 的位置标记为星形

让我们设置 c 1 = c 2 = 0.1 c_1=c_2=0.1 c1=c2=0.1 w = 0.8 w=0.8 w=0.8。然后我们可以根据上面提到的公式更新位置和速度,然后更新 p b e s t i pbest^i pbesti g b e s t gbest gbest之后:

c1 = c2 = 0.1
w = 0.8
# One iteration
r = np.random.rand(2)
V = w * V + c1*r[0]*(pbest - X) + c2*r[1]*(gbest.reshape(-1,1)-X)
X = X + V
obj = f(X[0], X[1])
pbest[:, (pbest_obj >= obj)] = X[:, (pbest_obj >= obj)]
pbest_obj = np.array([pbest_obj, obj]).max(axis=0)
gbest = pbest[:, pbest_obj.argmin()]
gbest_obj = pbest_obj.min()

以下是第一次迭代后的位置。我们用黑点标记每个粒子的最佳位置,以区别于它们的当前位置,当前位置设置为蓝色。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一次迭代后粒子的位置

我们可以多次重复上面的代码段,看看粒子是如何探索的。这是第二次迭代后的结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

两次迭代后粒子的位置

这是在第五次迭代之后,注意 g b e s t gbest gbest的位置(用星号表示)发生了变化:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

5 次迭代后粒子的位置

而经过第 20 次迭代,我们已经非常接近最优:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

20 次迭代后粒子的位置

这个动画展示了随着算法的发展,我们是如何找到最优解的。看看你是否会发现一群鸟的运动有些相似之处:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

粒子运动的动画

那么我们的解决方案有多接近呢?在这个特定的例子中,我们通过穷举搜索找到的全局最小值在坐标 ( 3.182 , 3.131 ) (3.182,3.131) (3.1823.131)处,而上面的 PSO 算法找到的最小值在 ( 3.185 , 3.130 ) (3.185,3.130) (3.1853.130)

变化

所有的 PSO 算法大部分都和我们上面提到的一样。在上面的例子中,我们将粒子群算法设置为以固定的迭代次数运行。设置迭代次数来动态运行以响应进度是微不足道的。例如,一旦我们在多次迭代中看不到对全局最佳解决方案 g b e s t gbest gbest的任何更新,我们就可以让它停止。

对粒子群算法的研究主要集中在如何确定超参数 w w w c 1 c_1 c1、和 c 2 c_2 c2或随着算法的进展改变它们的值。例如,有人建议惯性权重线性递减。也有建议试图使认知系数 c_1 减少,而社会系数 c 2 减少,而社会系数 c_2 减少,而社会系数c2增加,以在开始时带来更多的探索,在结束时带来更多的开发。例如,见史和埃伯哈特(1998 年)和埃伯哈特和史(2000 年)。

完整示例

应该很容易看出我们如何改变上面的代码来求解更高维的目标函数,或者从最小化切换到最大化。以下是找到上面提出的函数 f ( x , y ) f(x,y) f(xy)的最小值的完整示例,以及生成绘图动画的代码:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

def f(x,y):
    "Objective function"
    return (x-3.14)**2 + (y-2.72)**2 + np.sin(3*x+1.41) + np.sin(4*y-1.73)

# Compute and plot the function in 3D within [0,5]x[0,5]
x, y = np.array(np.meshgrid(np.linspace(0,5,100), np.linspace(0,5,100)))
z = f(x, y)

# Find the global minimum
x_min = x.ravel()[z.argmin()]
y_min = y.ravel()[z.argmin()]

# Hyper-parameter of the algorithm
c1 = c2 = 0.1
w = 0.8

# Create particles
n_particles = 20
np.random.seed(100)
X = np.random.rand(2, n_particles) * 5
V = np.random.randn(2, n_particles) * 0.1

# Initialize data
pbest = X
pbest_obj = f(X[0], X[1])
gbest = pbest[:, pbest_obj.argmin()]
gbest_obj = pbest_obj.min()

def update():
    "Function to do one iteration of particle swarm optimization"
    global V, X, pbest, pbest_obj, gbest, gbest_obj
    # Update params
    r1, r2 = np.random.rand(2)
    V = w * V + c1*r1*(pbest - X) + c2*r2*(gbest.reshape(-1,1)-X)
    X = X + V
    obj = f(X[0], X[1])
    pbest[:, (pbest_obj >= obj)] = X[:, (pbest_obj >= obj)]
    pbest_obj = np.array([pbest_obj, obj]).min(axis=0)
    gbest = pbest[:, pbest_obj.argmin()]
    gbest_obj = pbest_obj.min()

# Set up base figure: The contour map
fig, ax = plt.subplots(figsize=(8,6))
fig.set_tight_layout(True)
img = ax.imshow(z, extent=[0, 5, 0, 5], origin='lower', cmap='viridis', alpha=0.5)
fig.colorbar(img, ax=ax)
ax.plot([x_min], [y_min], marker='x', markersize=5, color="white")
contours = ax.contour(x, y, z, 10, colors='black', alpha=0.4)
ax.clabel(contours, inline=True, fontsize=8, fmt="%.0f")
pbest_plot = ax.scatter(pbest[0], pbest[1], marker='o', color='black', alpha=0.5)
p_plot = ax.scatter(X[0], X[1], marker='o', color='blue', alpha=0.5)
p_arrow = ax.quiver(X[0], X[1], V[0], V[1], color='blue', width=0.005, angles='xy', scale_units='xy', scale=1)
gbest_plot = plt.scatter([gbest[0]], [gbest[1]], marker='*', s=100, color='black', alpha=0.4)
ax.set_xlim([0,5])
ax.set_ylim([0,5])

def animate(i):
    "Steps of PSO: algorithm update and show in plot"
    title = 'Iteration {:02d}'.format(i)
    # Update params
    update()
    # Set picture
    ax.set_title(title)
    pbest_plot.set_offsets(pbest.T)
    p_plot.set_offsets(X.T)
    p_arrow.set_offsets(X.T)
    p_arrow.set_UVC(V[0], V[1])
    gbest_plot.set_offsets(gbest.reshape(1,-1))
    return ax, pbest_plot, p_plot, p_arrow, gbest_plot

anim = FuncAnimation(fig, animate, frames=list(range(1,50)), interval=500, blit=False, repeat=True)
anim.save("PSO.gif", dpi=120, writer="imagemagick")

print("PSO found best solution at f({})={}".format(gbest, gbest_obj))
print("Global optimal at f({})={}".format([x_min,y_min], f(x_min,y_min)))

进一步阅读

这些是最初提出粒子群优化算法的论文,以及早期关于优化其超参数的研究:

  • 粒子群优化算法。在国际神经网络会议记录中;电气和电子工程师学会。第四卷。1995.1942-1948 页。DOI: 10.1109/ICNN.1995.488968
  • 粒子群优化中惯性权重和收缩因子的比较。在2000 年进化计算大会(CEC '00) 的会议记录中。第一卷。2000.第 84-88 页。DOI: 10.1109/CEC.2000.870279
  • 一种改进的粒子群优化算法。在 1998 年 IEEE 进化计算国际会议记录中。第 69-73 页。DOI: 10.1109/ICEC.1998.699146

摘要

在本教程中,我们学习了:

  • 粒子群优化是如何工作的
  • 如何实现粒子群算法
  • 算法中一些可能的变化

由于粒子群优化算法没有太多的超参数,对目标函数也没有很大的限制,因此可以用来解决很多问题。

从零开始编写 Adam 优化算法

原文:https://machinelearningmastery.com/adam-optimization-from-scratch/

最后更新于 2021 年 10 月 12 日

梯度下降是一种优化算法,它遵循目标函数的负梯度来定位函数的最小值。

梯度下降的一个限制是对所有输入变量使用单一步长(学习率)。对梯度下降的扩展,如 AdaGrad 和 RMSProp,更新了算法,为每个输入变量使用单独的步长,但可能导致步长迅速减小到非常小的值。

自适应运动估计算法,简称为亚当,是梯度下降的扩展,是 AdaGrad 和 RMSProp 等技术的自然延续,这些技术自动为目标函数的每个输入变量调整学习率,并通过使用梯度的指数递减移动平均来更新变量,从而进一步平滑搜索过程。

在本教程中,您将发现如何从零开始使用亚当优化算法开发梯度下降。

完成本教程后,您将知道:

  • 梯度下降是一种优化算法,它使用目标函数的梯度来导航搜索空间。
  • 梯度下降可以更新为使用一个自动自适应步长为每个输入变量使用一个衰减平均偏导数,称为亚当。
  • 如何从零开始实现 Adam 优化算法,并将其应用于一个目标函数并评估结果。

用我的新书机器学习优化启动你的项目,包括分步教程和所有示例的 Python 源代码文件。

Let’s get started.外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

亚当从零开始的梯度下降优化
唐·格雷厄姆摄,版权所有。

教程概述

本教程分为三个部分;它们是:

  1. 梯度下降
  2. 亚当优化算法
  3. 亚当梯度下降
    1. 二维测试问题
    2. 带亚当的梯度下降优化
    3. 亚当的形象

梯度下降

梯度下降是一种优化算法。

它在技术上被称为一阶优化算法,因为它明确地利用了目标函数的一阶导数。

  • 一阶方法依赖于梯度信息来帮助指导搜索最小值…

—第 69 页,优化算法,2019。

一阶导数,或简称为“导数,是目标函数在特定点的变化率或斜率,例如对于特定输入。

如果目标函数有多个输入变量,它被称为多元函数,输入变量可以被认为是一个向量。反过来,多元目标函数的导数也可以作为向量,并且通常被称为梯度

  • 梯度:多元目标函数的一阶导数。

对于特定输入,导数或梯度指向目标函数最陡上升的方向。

梯度下降是指一种最小化优化算法,它遵循目标函数梯度下降的负值来定位函数的最小值。

梯度下降算法需要一个正在优化的目标函数和目标函数的导数函数。目标函数 f() 返回给定输入集的得分,导数函数 f’() 给出给定输入集的目标函数的导数。

梯度下降算法要求问题中有一个起点( x ),比如输入空间中随机选择的一个点。

然后计算导数,并在输入空间中采取一个步骤,该步骤预计会导致目标函数的下坡运动,假设我们正在最小化目标函数。

下坡移动是通过首先计算在输入空间中移动多远来实现的,计算方法是步长(称为 alpha 或学习率)乘以梯度。然后从当前点减去这一点,确保我们逆着梯度或目标函数向下移动。

  • x(t)= x(t-1)–步长* f’(x(t-1))

给定点处的目标函数越陡,梯度的幅度就越大,反过来,搜索空间中的步长也就越大。使用步长超参数来缩放所采取的步长。

  • 步长(α):超参数,控制算法每次迭代在搜索空间中逆着梯度移动多远。

如果步长太小,搜索空间中的移动将会很小,并且搜索将会花费很长时间。如果步长过大,搜索可能会绕过搜索空间并跳过 optima。

现在我们已经熟悉了梯度下降优化算法,让我们来看看 Adam 算法。

亚当优化算法

自适应运动估计算法,简称 Adam,是梯度下降优化算法的扩展。

Diederik Kingma 和 Jimmy 巴雷在 2014 年的论文中描述了该算法,题为“T0”Adam:一种随机优化方法

Adam 旨在加速优化过程,例如减少达到最优所需的函数求值次数,或者提高优化算法的能力,例如产生更好的最终结果。

这是通过计算正在优化的每个输入参数的步长来实现的。重要的是,基于每个变量遇到的梯度(偏导数),每个步长在搜索过程中自动调整。

我们提出了 Adam,这是一种高效的随机优化方法,只需要一阶梯度,几乎不需要内存。该方法根据梯度的第一和第二矩的估计来计算不同参数的个体自适应学习率;亚当这个名字来源于自适应矩估计

——亚当:一种随机优化的方法

这包括保持梯度的第一和第二矩,例如每个输入变量的指数衰减平均梯度(第一矩)和方差(第二矩)。

移动平均线本身是梯度的第一个矩(平均值)和第二个原始矩(非间隔方差)的估计值。

——亚当:一种随机优化的方法

让我们逐步了解算法的每个元素。

首先,作为搜索的一部分,我们必须为每个被优化的参数保持一个矩向量和指数加权无穷范数,分别称为 m 和 v(实际上是希腊字母 nu)。它们在搜索开始时被初始化为 0.0。

  • m = 0
  • v = 0

该算法在时间 t 上迭代执行,从 t=1 开始,并且每次迭代包括计算一组新的参数值 x ,例如从 x(t-1)x(t)

如果我们专注于更新一个参数,这可能很容易理解算法,这可以推广到通过向量运算更新所有参数。

首先,计算当前时间步长的梯度(偏导数)。

  • g(t) = f’(x(t-1))

接下来,使用梯度和超参数β1更新第一时刻。

  • m(t)=β1 * m(t-1)+(1–β1)* g(t)

然后使用平方梯度和超参数β2更新第二个时刻。

  • v(t)=β2 * v(t-1)+(1–β2)* g(t)²

第一和第二矩是有偏差的,因为它们是用零值初始化的。

……这些移动平均线被初始化为(向量)0,导致力矩估计偏向于零,尤其是在初始时间步长期间,尤其是当衰减率很小时(即β值接近 1)。好消息是,这种初始化偏差可以很容易地被抵消,导致偏差校正的估计…

——亚当:一种随机优化的方法

接下来,第一和第二时刻被偏差校正,从第一时刻开始:

  • mhat(t)= m(t)/(1–β1(t))

然后第二个瞬间:

  • vhat(t)= v(t)/(1–β2(t))

注意,*β1(t)β2(t)*指的是在算法迭代过程中按时间表衰减的β1 和β2 超参数。可以使用静态衰减时间表,尽管本文建议如下:

  • β1(t)= beta1^t
  • β2(t)= beta2^t

最后,我们可以计算这个迭代的参数值。

  • x(t)= x(t-1)-alpha * mhat(t)/(sqrt(vhat(t))+EPS)

其中α为步长超参数, eps 为小值(ε)如 1e-8,保证我们不会遇到被零除的误差, sqrt() 为平方根函数。

注意,可以使用对论文中列出的更新规则进行更有效的重新排序:

  • alpha(t)= alpha * sqrt(1–beta 2(t))/(1–beta 1(t))
  • x(t)= x(t-1)-alpha(t)* m(t)/(sqrt(v(t))+EPS)

回顾一下,该算法有三个超参数,它们是:

  • α:初始步长(学习率),典型值为 0.001。
  • β1:第一动量衰减因子,典型值为 0.9。
  • β2:无穷大范数的衰减因子,典型值为 0.999。

就这样。

关于亚当算法在亚当算法背景下的完整推导,我推荐阅读这篇论文。

接下来,让我们看看如何在 Python 中从零开始实现该算法。

亚当梯度下降

在本节中,我们将探索如何用 Adam 实现梯度下降优化算法。

二维测试问题

首先,让我们定义一个优化函数。

我们将使用一个简单的二维函数,它对每个维度的输入进行平方,并定义从-1.0 到 1.0 的有效输入范围。

下面的 objective()函数实现了这个功能

# objective function
def objective(x, y):
	return x**2.0 + y**2.0

我们可以创建数据集的三维图,以获得对响应表面曲率的感觉。

下面列出了绘制目标函数的完整示例。

# 3d plot of the test function
from numpy import arange
from numpy import meshgrid
from matplotlib import pyplot

# objective function
def objective(x, y):
	return x**2.0 + y**2.0

# define range for input
r_min, r_max = -1.0, 1.0
# sample input range uniformly at 0.1 increments
xaxis = arange(r_min, r_max, 0.1)
yaxis = arange(r_min, r_max, 0.1)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# compute targets
results = objective(x, y)
# create a surface plot with the jet color scheme
figure = pyplot.figure()
axis = figure.gca(projection='3d')
axis.plot_surface(x, y, results, cmap='jet')
# show the plot
pyplot.show()

运行该示例会创建目标函数的三维表面图。

我们可以看到熟悉的碗形,全局最小值在 f(0,0) = 0。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

测试目标函数的三维图

我们还可以创建函数的二维图。这将有助于我们以后绘制搜索进度。

以下示例创建了目标函数的等高线图。

# contour plot of the test function
from numpy import asarray
from numpy import arange
from numpy import meshgrid
from matplotlib import pyplot

# objective function
def objective(x, y):
	return x**2.0 + y**2.0

# define range for input
bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]])
# sample input range uniformly at 0.1 increments
xaxis = arange(bounds[0,0], bounds[0,1], 0.1)
yaxis = arange(bounds[1,0], bounds[1,1], 0.1)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# compute targets
results = objective(x, y)
# create a filled contour plot with 50 levels and jet color scheme
pyplot.contourf(x, y, results, levels=50, cmap='jet')
# show the plot
pyplot.show()

运行该示例会创建目标函数的二维等高线图。

我们可以看到碗的形状被压缩成带有颜色梯度的轮廓。我们将使用此图来绘制搜索过程中探索的具体点。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

测试目标函数的二维等高线图

现在我们有了一个测试目标函数,让我们看看如何实现亚当优化算法。

带亚当的梯度下降优化

我们可以将带有 Adam 的梯度下降应用于测试问题。

首先,我们需要一个函数来计算这个函数的导数。

  • f(x) = x²
  • f’(x) = x * 2

x² 的导数在每个维度上都是 x * 2。导数()函数实现如下。

# derivative of objective function
def derivative(x, y):
	return asarray([x * 2.0, y * 2.0])

接下来,我们可以实现梯度下降优化。

首先,我们可以在问题的边界中选择一个随机点作为搜索的起点。

这假设我们有一个定义搜索范围的数组,每个维度有一行,第一列定义维度的最小值,第二列定义维度的最大值。

...
# generate an initial point
x = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
score = objective(x[0], x[1])

接下来,我们需要将第一和第二时刻初始化为零。

...
# initialize first and second moments
m = [0.0 for _ in range(bounds.shape[0])]
v = [0.0 for _ in range(bounds.shape[0])]

然后,我们运行由“ n_iter ”超参数定义的算法的固定迭代次数。

...
# run iterations of gradient descent
for t in range(n_iter):
	...

第一步是使用*导数()*函数计算当前解的梯度。

...
# calculate gradient
gradient = derivative(solution[0], solution[1])

第一步是计算当前参数集的导数。

...
# calculate gradient g(t)
g = derivative(x[0], x[1])

接下来,我们需要执行亚当更新计算。为了可读性,我们将使用命令式编程风格一次一个变量地执行这些计算。

实际上,为了提高效率,我建议使用 NumPy 向量运算。

...
# build a solution one variable at a time
for i in range(x.shape[0]):
	...

首先,我们需要计算时刻。

...
# m(t) = beta1 * m(t-1) + (1 - beta1) * g(t)
m[i] = beta1 * m[i] + (1.0 - beta1) * g[i]

然后第二个瞬间。

...
# v(t) = beta2 * v(t-1) + (1 - beta2) * g(t)²
v[i] = beta2 * v[i] + (1.0 - beta2) * g[i]**2

然后是第一和第二时刻的偏差校正。

...
# mhat(t) = m(t) / (1 - beta1(t))
mhat = m[i] / (1.0 - beta1**(t+1))
# vhat(t) = v(t) / (1 - beta2(t))
vhat = v[i] / (1.0 - beta2**(t+1))

然后最后更新变量值。

...
# x(t) = x(t-1) - alpha * mhat(t) / (sqrt(vhat(t)) + eps)
x[i] = x[i] - alpha * mhat / (sqrt(vhat) + eps)

然后对每个正在优化的参数重复这一过程。

迭代结束时,我们可以评估新的参数值,并报告搜索的表现。

...
# evaluate candidate point
score = objective(x[0], x[1])
# report progress
print('>%d f(%s) = %.5f' % (t, x, score))

我们可以将所有这些联系到一个名为 adam() 的函数中,该函数采用目标函数和导数函数以及算法超参数的名称,并返回搜索结束时找到的最佳解及其评估。

下面列出了完整的功能。

# gradient descent algorithm with adam
def adam(objective, derivative, bounds, n_iter, alpha, beta1, beta2, eps=1e-8):
	# generate an initial point
	x = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
	score = objective(x[0], x[1])
	# initialize first and second moments
	m = [0.0 for _ in range(bounds.shape[0])]
	v = [0.0 for _ in range(bounds.shape[0])]
	# run the gradient descent updates
	for t in range(n_iter):
		# calculate gradient g(t)
		g = derivative(x[0], x[1])
		# build a solution one variable at a time
		for i in range(x.shape[0]):
			# m(t) = beta1 * m(t-1) + (1 - beta1) * g(t)
			m[i] = beta1 * m[i] + (1.0 - beta1) * g[i]
			# v(t) = beta2 * v(t-1) + (1 - beta2) * g(t)²
			v[i] = beta2 * v[i] + (1.0 - beta2) * g[i]**2
			# mhat(t) = m(t) / (1 - beta1(t))
			mhat = m[i] / (1.0 - beta1**(t+1))
			# vhat(t) = v(t) / (1 - beta2(t))
			vhat = v[i] / (1.0 - beta2**(t+1))
			# x(t) = x(t-1) - alpha * mhat(t) / (sqrt(vhat(t)) + eps)
			x[i] = x[i] - alpha * mhat / (sqrt(vhat) + eps)
		# evaluate candidate point
		score = objective(x[0], x[1])
		# report progress
		print('>%d f(%s) = %.5f' % (t, x, score))
	return [x, score]

:为了可读性,我们特意使用了列表和命令式编码风格,而不是矢量化操作。请随意将实现调整为使用 NumPy 阵列的矢量化实现,以获得更好的表现。

然后,我们可以定义我们的超参数,并调用 adam() 函数来优化我们的测试目标函数。

在这种情况下,我们将使用算法的 60 次迭代,初始步长为 0.02,β1 和β2 值分别为 0.8 和 0.999。这些超参数值是经过一点点反复试验后发现的。

...
# seed the pseudo random number generator
seed(1)
# define range for input
bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]])
# define the total iterations
n_iter = 60
# steps size
alpha = 0.02
# factor for average gradient
beta1 = 0.8
# factor for average squared gradient
beta2 = 0.999
# perform the gradient descent search with adam
best, score = adam(objective, derivative, bounds, n_iter, alpha, beta1, beta2)
print('Done!')
print('f(%s) = %f' % (best, score))

将所有这些联系在一起,下面列出了使用 Adam 进行梯度下降优化的完整示例。

# gradient descent optimization with adam for a two-dimensional test function
from math import sqrt
from numpy import asarray
from numpy.random import rand
from numpy.random import seed

# objective function
def objective(x, y):
	return x**2.0 + y**2.0

# derivative of objective function
def derivative(x, y):
	return asarray([x * 2.0, y * 2.0])

# gradient descent algorithm with adam
def adam(objective, derivative, bounds, n_iter, alpha, beta1, beta2, eps=1e-8):
	# generate an initial point
	x = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
	score = objective(x[0], x[1])
	# initialize first and second moments
	m = [0.0 for _ in range(bounds.shape[0])]
	v = [0.0 for _ in range(bounds.shape[0])]
	# run the gradient descent updates
	for t in range(n_iter):
		# calculate gradient g(t)
		g = derivative(x[0], x[1])
		# build a solution one variable at a time
		for i in range(x.shape[0]):
			# m(t) = beta1 * m(t-1) + (1 - beta1) * g(t)
			m[i] = beta1 * m[i] + (1.0 - beta1) * g[i]
			# v(t) = beta2 * v(t-1) + (1 - beta2) * g(t)²
			v[i] = beta2 * v[i] + (1.0 - beta2) * g[i]**2
			# mhat(t) = m(t) / (1 - beta1(t))
			mhat = m[i] / (1.0 - beta1**(t+1))
			# vhat(t) = v(t) / (1 - beta2(t))
			vhat = v[i] / (1.0 - beta2**(t+1))
			# x(t) = x(t-1) - alpha * mhat(t) / (sqrt(vhat(t)) + eps)
			x[i] = x[i] - alpha * mhat / (sqrt(vhat) + eps)
		# evaluate candidate point
		score = objective(x[0], x[1])
		# report progress
		print('>%d f(%s) = %.5f' % (t, x, score))
	return [x, score]

# seed the pseudo random number generator
seed(1)
# define range for input
bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]])
# define the total iterations
n_iter = 60
# steps size
alpha = 0.02
# factor for average gradient
beta1 = 0.8
# factor for average squared gradient
beta2 = 0.999
# perform the gradient descent search with adam
best, score = adam(objective, derivative, bounds, n_iter, alpha, beta1, beta2)
print('Done!')
print('f(%s) = %f' % (best, score))

运行该示例将 Adam 优化算法应用于我们的测试问题,并报告算法每次迭代的搜索表现。

:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

在这种情况下,我们可以看到,在大约 53 次搜索迭代后,找到了一个接近最优的解,输入值接近 0.0 和 0.0,评估为 0.0。

...
>50 f([-0.00056912 -0.00321961]) = 0.00001
>51 f([-0.00052452 -0.00286514]) = 0.00001
>52 f([-0.00043908 -0.00251304]) = 0.00001
>53 f([-0.0003283  -0.00217044]) = 0.00000
>54 f([-0.00020731 -0.00184302]) = 0.00000
>55 f([-8.95352320e-05 -1.53514076e-03]) = 0.00000
>56 f([ 1.43050285e-05 -1.25002847e-03]) = 0.00000
>57 f([ 9.67123406e-05 -9.89850279e-04]) = 0.00000
>58 f([ 0.00015359 -0.00075587]) = 0.00000
>59 f([ 0.00018407 -0.00054858]) = 0.00000
Done!
f([ 0.00018407 -0.00054858]) = 0.000000

亚当的形象

我们可以在域的等高线图上绘制亚当搜索的进度。

这可以为算法迭代过程中的搜索进度提供直觉。

我们必须更新 adam() 函数,以维护搜索过程中找到的所有解决方案的列表,然后在搜索结束时返回该列表。

下面列出了带有这些更改的功能的更新版本。

# gradient descent algorithm with adam
def adam(objective, derivative, bounds, n_iter, alpha, beta1, beta2, eps=1e-8):
	solutions = list()
	# generate an initial point
	x = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
	score = objective(x[0], x[1])
	# initialize first and second moments
	m = [0.0 for _ in range(bounds.shape[0])]
	v = [0.0 for _ in range(bounds.shape[0])]
	# run the gradient descent updates
	for t in range(n_iter):
		# calculate gradient g(t)
		g = derivative(x[0], x[1])
		# build a solution one variable at a time
		for i in range(bounds.shape[0]):
			# m(t) = beta1 * m(t-1) + (1 - beta1) * g(t)
			m[i] = beta1 * m[i] + (1.0 - beta1) * g[i]
			# v(t) = beta2 * v(t-1) + (1 - beta2) * g(t)²
			v[i] = beta2 * v[i] + (1.0 - beta2) * g[i]**2
			# mhat(t) = m(t) / (1 - beta1(t))
			mhat = m[i] / (1.0 - beta1**(t+1))
			# vhat(t) = v(t) / (1 - beta2(t))
			vhat = v[i] / (1.0 - beta2**(t+1))
			# x(t) = x(t-1) - alpha * mhat(t) / (sqrt(vhat(t)) + ep)
			x[i] = x[i] - alpha * mhat / (sqrt(vhat) + eps)
		# evaluate candidate point
		score = objective(x[0], x[1])
		# keep track of solutions
		solutions.append(x.copy())
		# report progress
		print('>%d f(%s) = %.5f' % (t, x, score))
	return solutions

然后,我们可以像以前一样执行搜索,这次检索解决方案列表,而不是最佳最终解决方案。

...
# seed the pseudo random number generator
seed(1)
# define range for input
bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]])
# define the total iterations
n_iter = 60
# steps size
alpha = 0.02
# factor for average gradient
beta1 = 0.8
# factor for average squared gradient
beta2 = 0.999
# perform the gradient descent search with adam
solutions = adam(objective, derivative, bounds, n_iter, alpha, beta1, beta2)

然后,我们可以像以前一样创建目标函数的等高线图。

...
# sample input range uniformly at 0.1 increments
xaxis = arange(bounds[0,0], bounds[0,1], 0.1)
yaxis = arange(bounds[1,0], bounds[1,1], 0.1)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# compute targets
results = objective(x, y)
# create a filled contour plot with 50 levels and jet color scheme
pyplot.contourf(x, y, results, levels=50, cmap='jet')

最后,我们可以将搜索过程中找到的每个解决方案绘制成由一条线连接的白点。

...
# plot the sample as black circles
solutions = asarray(solutions)
pyplot.plot(solutions[:, 0], solutions[:, 1], '.-', color='w')

将所有这些联系在一起,下面列出了对测试问题执行 Adam 优化并将结果绘制在等高线图上的完整示例。

# example of plotting the adam search on a contour plot of the test function
from math import sqrt
from numpy import asarray
from numpy import arange
from numpy.random import rand
from numpy.random import seed
from numpy import meshgrid
from matplotlib import pyplot
from mpl_toolkits.mplot3d import Axes3D

# objective function
def objective(x, y):
	return x**2.0 + y**2.0

# derivative of objective function
def derivative(x, y):
	return asarray([x * 2.0, y * 2.0])

# gradient descent algorithm with adam
def adam(objective, derivative, bounds, n_iter, alpha, beta1, beta2, eps=1e-8):
	solutions = list()
	# generate an initial point
	x = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])
	score = objective(x[0], x[1])
	# initialize first and second moments
	m = [0.0 for _ in range(bounds.shape[0])]
	v = [0.0 for _ in range(bounds.shape[0])]
	# run the gradient descent updates
	for t in range(n_iter):
		# calculate gradient g(t)
		g = derivative(x[0], x[1])
		# build a solution one variable at a time
		for i in range(bounds.shape[0]):
			# m(t) = beta1 * m(t-1) + (1 - beta1) * g(t)
			m[i] = beta1 * m[i] + (1.0 - beta1) * g[i]
			# v(t) = beta2 * v(t-1) + (1 - beta2) * g(t)²
			v[i] = beta2 * v[i] + (1.0 - beta2) * g[i]**2
			# mhat(t) = m(t) / (1 - beta1(t))
			mhat = m[i] / (1.0 - beta1**(t+1))
			# vhat(t) = v(t) / (1 - beta2(t))
			vhat = v[i] / (1.0 - beta2**(t+1))
			# x(t) = x(t-1) - alpha * mhat(t) / (sqrt(vhat(t)) + ep)
			x[i] = x[i] - alpha * mhat / (sqrt(vhat) + eps)
		# evaluate candidate point
		score = objective(x[0], x[1])
		# keep track of solutions
		solutions.append(x.copy())
		# report progress
		print('>%d f(%s) = %.5f' % (t, x, score))
	return solutions

# seed the pseudo random number generator
seed(1)
# define range for input
bounds = asarray([[-1.0, 1.0], [-1.0, 1.0]])
# define the total iterations
n_iter = 60
# steps size
alpha = 0.02
# factor for average gradient
beta1 = 0.8
# factor for average squared gradient
beta2 = 0.999
# perform the gradient descent search with adam
solutions = adam(objective, derivative, bounds, n_iter, alpha, beta1, beta2)
# sample input range uniformly at 0.1 increments
xaxis = arange(bounds[0,0], bounds[0,1], 0.1)
yaxis = arange(bounds[1,0], bounds[1,1], 0.1)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# compute targets
results = objective(x, y)
# create a filled contour plot with 50 levels and jet color scheme
pyplot.contourf(x, y, results, levels=50, cmap='jet')
# plot the sample as black circles
solutions = asarray(solutions)
pyplot.plot(solutions[:, 0], solutions[:, 1], '.-', color='w')
# show the plot
pyplot.show()

运行该示例会像以前一样执行搜索,只是在这种情况下,会创建目标函数的等高线图。

在这种情况下,我们可以看到,搜索过程中找到的每个解决方案都显示一个白点,从 optima 上方开始,逐渐靠近图中心的 optima。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

显示亚当搜索结果的测试目标函数等高线图

进一步阅读

如果您想更深入地了解这个主题,本节将提供更多资源。

报纸

蜜蜂

  • num py . random . rand API。
  • num py . asar ray API。
  • Matplotlib API

文章

摘要

在本教程中,您发现了如何使用亚当优化算法从零开始开发梯度下降。

具体来说,您了解到:

  • 梯度下降是一种优化算法,它使用目标函数的梯度来导航搜索空间。
  • 梯度下降可以更新为使用一个自动自适应步长为每个输入变量使用一个衰减平均偏导数,称为亚当。
  • 如何从零开始实现 Adam 优化算法,并将其应用于一个目标函数并评估结果。

你有什么问题吗?
在下面的评论中提问,我会尽力回答。

Python 中的盆地跳跃优化

原文:https://machinelearningmastery.com/basin-hopping-optimization-in-python/

最后更新于 2021 年 10 月 12 日

盆地跳跃是全局优化算法。

它是为了解决化学物理中的问题而发展起来的,尽管它是一种适用于非线性多目标函数的有效算法。

在本教程中,您将发现盆地跳跃全局优化算法。

完成本教程后,您将知道:

  • 流域跳跃优化是使用随机扰动来跳跃流域的全局优化,以及用于优化每个流域的局部搜索算法。
  • 如何使用 python 中的盆地跳跃优化算法 API?
  • 用盆地跳跃法解决多目标全局优化问题的实例。

用我的新书机器学习优化启动你的项目,包括分步教程和所有示例的 Python 源代码文件。

Let’s get started.外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Python 中的盆地跳跃优化
图片由佩德罗·策克利提供,保留部分权利。

教程概述

本教程分为三个部分;它们是:

  1. 流域跳跃优化
  2. 盆地跳跃应用编程接口
  3. 盆地跳跃示例
    1. 具有局部最优的多峰优化
    2. 多全局最优的多峰优化

流域跳跃优化

盆地跳跃是一种全局优化算法,开发用于化学物理领域。

盆地跳跃(BH)或蒙特卡罗最小化(MCM)是迄今为止化学物理中最可靠的寻找原子团簇和大分子体系最低能量结构的算法。

——盆跳偶跳,2004。

局部优化是指旨在定位单变量目标函数的最优值或在认为存在最优值的区域中操作的优化算法。而全局优化算法旨在定位潜在的多个局部(非全局)最优中的单个全局最优。

大卫·威尔士和乔纳森·多伊在他们 1997 年的论文《通过盆地跳跃和包含多达 110 个原子的伦纳德-琼斯团簇的最低能量结构的全局优化》中描述了盆地跳跃

该算法包括两个循环步骤,一个是扰动好的候选解,另一个是对扰动解进行局部搜索。

[盆地跳跃]将复杂的能源景观转化为盆地的集合,并通过跳跃来探索它们,这是通过随机蒙特卡罗移动和使用 Metropolis 标准的接受/拒绝来实现的。

——盆跳偶跳,2004。

扰动允许搜索算法跳到搜索空间的新区域,并潜在地定位导致不同最优值的新盆地,例如技术名称中的“T0”盆地跳跃。

局部搜索允许算法穿过新盆地到达最优位置。

新的最优值可以保留作为新的随机扰动的基础,否则,它被丢弃。保留新解的决定由带有“温度”变量的随机决策函数控制,很像模拟退火

温度根据算法的迭代次数进行调整。这允许在运行初期温度较高时接受任意解决方案,并允许在后期温度较低时只接受质量较好的解决方案的更严格政策。

以这种方式,该算法很像具有不同(扰动的)起始点的迭代局部搜索。

该算法运行指定次数的迭代或函数求值,并且可以运行多次以增加全局最优解被定位或相对好的解被定位的信心。

既然我们已经从较高的层次上熟悉了基本的跳转算法,那么我们就来看看 Python 中用于流域跳转的 API。

盆地跳跃应用编程接口

通过盆地跳跃()SciPy 功能,在 Python 中可以进行盆地跳跃。

该函数采用要最小化的目标函数的名称和初始起点。

...
# perform the basin hopping search
result = basinhopping(objective, pt)

另一个重要的超参数是通过“ niter ”参数运行搜索集的迭代次数,默认为 100。

这可以设置为数千次或更多次迭代。

...
# perform the basin hopping search
result = basinhopping(objective, pt, niter=10000)

应用于候选解的扰动量可以通过“步长”来控制,该步长定义了在问题域边界的上下文中应用的最大变化量。默认情况下,该值设置为 0.5,但应该设置为域中合理的值,这可能允许搜索找到新的盆地。

例如,如果搜索空间的合理界限是-100 到 100,那么 5.0 或 10.0 单位的步长可能是合适的(例如,域的 2.5%或 5%)。

...
# perform the basin hopping search
result = basinhopping(objective, pt, stepsize=10.0)

默认情况下,使用的本地搜索算法是“ L-BFGS-B ”算法。

可以通过将“ minimizer_kwargs ”参数设置为一个目录,该目录的关键字为“ method ,该值作为要使用的本地搜索算法的名称,如“NelderMead”可以使用 SciPy 库提供的任何本地搜索算法。

...
# perform the basin hopping search
result = basinhopping(objective, pt, minimizer_kwargs={'method':'nelder-mead'})

搜索的结果是一个优化结果对象,在这里可以像字典一样访问属性。搜索成功与否可通过“成功或“消息键进入。

可通过“ nfev 访问功能评估的总数,可通过“ x 键访问为搜索找到的最佳输入。

既然我们已经熟悉了 Python 中的盆地跳跃 API,那么让我们来看看一些工作过的例子。

盆地跳跃示例

在本节中,我们将看一些在多模态目标函数上使用流域跳跃算法的例子。

多模态目标函数是具有多个最优解的函数,例如一个全局最优解和多个局部最优解,或者具有相同目标函数输出的多个全局最优解。

我们将看看这两个函数的流域跳跃的例子。

具有局部最优的多峰优化

阿克利函数是一个目标函数的例子,它有一个全局最优解和多个局部最优解,局部搜索可能会陷入其中。

因此,需要一种全局优化技术。它是一个二维目标函数,其全局最优值为[0,0],计算结果为 0.0。

下面的示例实现了 Ackley,并创建了一个显示全局最优值和多个局部最优值的三维曲面图。

# ackley multimodal function
from numpy import arange
from numpy import exp
from numpy import sqrt
from numpy import cos
from numpy import e
from numpy import pi
from numpy import meshgrid
from matplotlib import pyplot
from mpl_toolkits.mplot3d import Axes3D

# objective function
def objective(x, y):
	return -20.0 * exp(-0.2 * sqrt(0.5 * (x**2 + y**2))) - exp(0.5 * (cos(2 * pi * x) + cos(2 * pi * y))) + e + 20

# define range for input
r_min, r_max = -5.0, 5.0
# sample input range uniformly at 0.1 increments
xaxis = arange(r_min, r_max, 0.1)
yaxis = arange(r_min, r_max, 0.1)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# compute targets
results = objective(x, y)
# create a surface plot with the jet color scheme
figure = pyplot.figure()
axis = figure.gca(projection='3d')
axis.plot_surface(x, y, results, cmap='jet')
# show the plot
pyplot.show()

运行该示例会创建阿克利函数的曲面图,显示大量的局部最优值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

阿克利多峰函数的三维表面图

我们可以将盆地跳跃算法应用于阿克利目标函数。

在这种情况下,我们将使用从-5 到 5 之间的输入域中抽取的随机点开始搜索。

...
# define the starting point as a random sample from the domain
pt = r_min + rand(2) * (r_max - r_min)

我们将使用 0.5 的步长,200 次迭代,以及默认的局部搜索算法。这种配置是经过反复试验后选择的。

...
# perform the basin hopping search
result = basinhopping(objective, pt, stepsize=0.5, niter=200)

搜索完成后,它将报告搜索状态、执行的迭代次数以及通过评估找到的最佳结果。

...
# summarize the result
print('Status : %s' % result['message'])
print('Total Evaluations: %d' % result['nfev'])
# evaluate solution
solution = result['x']
evaluation = objective(solution)
print('Solution: f(%s) = %.5f' % (solution, evaluation))

将这些联系在一起,下面列出了将盆地跳跃应用于阿克利目标函数的完整示例。

# basin hopping global optimization for the ackley multimodal objective function
from scipy.optimize import basinhopping
from numpy.random import rand
from numpy import exp
from numpy import sqrt
from numpy import cos
from numpy import e
from numpy import pi

# objective function
def objective(v):
	x, y = v
	return -20.0 * exp(-0.2 * sqrt(0.5 * (x**2 + y**2))) - exp(0.5 * (cos(2 * pi * x) + cos(2 * pi * y))) + e + 20

# define range for input
r_min, r_max = -5.0, 5.0
# define the starting point as a random sample from the domain
pt = r_min + rand(2) * (r_max - r_min)
# perform the basin hopping search
result = basinhopping(objective, pt, stepsize=0.5, niter=200)
# summarize the result
print('Status : %s' % result['message'])
print('Total Evaluations: %d' % result['nfev'])
# evaluate solution
solution = result['x']
evaluation = objective(solution)
print('Solution: f(%s) = %.5f' % (solution, evaluation))

运行该示例会执行优化,然后报告结果。

:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

在这种情况下,我们可以看到,该算法定位了输入非常接近零的最优值,并且目标函数评估实际上为零。

我们可以看到,算法的 200 次迭代导致了 86,020 次函数求值。

Status: ['requested number of basinhopping iterations completed successfully']
Total Evaluations: 86020
Solution: f([ 5.29778873e-10 -2.29022817e-10]) = 0.00000

多全局最优的多峰优化

Himmelblau 函数是具有多个全局最优值的目标函数的一个例子。

具体来说,它有四个最优解,每个最优解都有相同的目标函数评估。它是一个二维目标函数,在[3.0,2.0],[-2.805118,3.131312],[-3.779310,-3.283186],[3.584428,-1.848126]处具有全局最优值。

这意味着全局优化算法的每次运行都可能找到不同的全局最优解。

下面的例子实现了 Himmelblau,并创建了一个三维表面图,以给出目标函数的直觉。

# himmelblau multimodal test function
from numpy import arange
from numpy import meshgrid
from matplotlib import pyplot
from mpl_toolkits.mplot3d import Axes3D

# objective function
def objective(x, y):
	return (x**2 + y - 11)**2 + (x + y**2 -7)**2

# define range for input
r_min, r_max = -5.0, 5.0
# sample input range uniformly at 0.1 increments
xaxis = arange(r_min, r_max, 0.1)
yaxis = arange(r_min, r_max, 0.1)
# create a mesh from the axis
x, y = meshgrid(xaxis, yaxis)
# compute targets
results = objective(x, y)
# create a surface plot with the jet color scheme
figure = pyplot.figure()
axis = figure.gca(projection='3d')
axis.plot_surface(x, y, results, cmap='jet')
# show the plot
pyplot.show()

运行该示例会创建 Himmelblau 函数的表面图,将四个全局最优值显示为深蓝色盆地。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Himmelblau 多模态函数的三维表面图

我们可以将流域跳跃算法应用于 Himmelblau 目标函数。

与前面的示例一样,我们将使用从输入域中抽取的一个介于-5 和 5 之间的随机点开始搜索。

我们将使用 0.5 的步长,200 次迭代,以及默认的局部搜索算法。在搜索结束时,我们将报告最佳位置 optima 的输入,

# basin hopping global optimization for the himmelblau multimodal objective function
from scipy.optimize import basinhopping
from numpy.random import rand

# objective function
def objective(v):
	x, y = v
	return (x**2 + y - 11)**2 + (x + y**2 -7)**2

# define range for input
r_min, r_max = -5.0, 5.0
# define the starting point as a random sample from the domain
pt = r_min + rand(2) * (r_max - r_min)
# perform the basin hopping search
result = basinhopping(objective, pt, stepsize=0.5, niter=200)
# summarize the result
print('Status : %s' % result['message'])
print('Total Evaluations: %d' % result['nfev'])
# evaluate solution
solution = result['x']
evaluation = objective(solution)
print('Solution: f(%s) = %.5f' % (solution, evaluation))

运行该示例会执行优化,然后报告结果。

在这种情况下,我们可以看到算法在大约[3.0,2.0]处定位了一个 optima。

我们可以看到,算法的 200 次迭代导致了 7660 次函数求值。

Status : ['requested number of basinhopping iterations completed successfully']
Total Evaluations: 7660
Solution: f([3\. 1.99999999]) = 0.00000

如果我们再次运行搜索,我们可能会发现一个不同的全局最优解。

例如,在下面,我们可以看到一个 optima 位于大约[-2.805118,3.131312],不同于之前的运行。

Status : ['requested number of basinhopping iterations completed successfully']
Total Evaluations: 7636
Solution: f([-2.80511809 3.13131252]) = 0.00000

进一步阅读

如果您想更深入地了解这个主题,本节将提供更多资源。

报纸

蜜蜂

文章

摘要

在本教程中,您发现了盆地跳跃全局优化算法。

具体来说,您了解到:

  • 流域跳跃优化是使用随机扰动来跳跃流域的全局优化,以及用于优化每个流域的局部搜索算法。
  • 如何使用 python 中的盆地跳跃优化算法 API?
  • 用盆地跳跃法解决多目标全局优化问题的实例。

你有什么问题吗?
在下面的评论中提问,我会尽力回答。

BFGS 优化算法的温和介绍

原文:https://machinelearningmastery.com/bfgs-optimization-in-python/

最后更新于 2021 年 10 月 12 日

布赖登算法、弗莱彻算法、戈德法布算法和夏诺算法,或称 BFGS 算法,是一种局部搜索优化算法。

它是一种二阶优化算法,这意味着它利用了目标函数的二阶导数,属于一类被称为拟牛顿法的算法,这些算法对不能计算二阶导数的优化问题近似二阶导数(称为黑森法)。

BFGS 算法可能是数值优化中最广泛使用的二阶算法之一,通常用于拟合机器学习算法,如逻辑回归算法。

在本教程中,您将发现 BFGS 二阶优化算法。

完成本教程后,您将知道:

  • 二阶优化算法是利用二阶导数的算法,二阶导数被称为多元目标函数的 Hessian 矩阵。
  • BFGS 算法可能是最受欢迎的数值优化二阶算法,属于一组所谓的拟牛顿方法。
  • 如何使用 Python 中的 BFGS 和 L-BFGS-B 算法最小化目标函数。

用我的新书机器学习优化启动你的项目,包括分步教程和所有示例的 Python 源代码文件。

Let’s get started.外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

温和介绍 BFGS 优化算法
图片由提莫牛顿-西蒙斯,保留部分权利。

教程概述

本教程分为三个部分;它们是:

  1. 二阶优化算法
  2. BFGS 优化算法
  3. BFGS 的成功范例

二阶优化算法

优化包括找到最大化或最小化目标函数的输入参数值。

牛顿法优化算法是那些利用目标函数二阶导数的算法。

你可能从微积分中回想起,函数的一阶导数是函数在特定点的变化率或曲率。导数可以通过优化算法朝着函数的最小值(导致目标函数最小输出的输入值)沿着下坡(或上坡)行进。

利用一阶导数的算法称为一阶优化算法。一阶算法的一个例子是梯度下降优化算法。

  • 一阶方法:利用一阶导数寻找目标函数最优值的优化算法。

二阶导数是导数的导数,或变化率的变化率。

二阶导数可以用来更有效地定位目标函数的最优值。这在一般情况下更有意义,因为我们关于目标函数的信息越多,优化它就越容易。

二阶导数使我们既能知道向哪个方向移动(像一阶导数一样),又能估计向那个方向移动多远,称为步长。

另一方面,二阶信息允许我们对目标函数进行二次近似,并近似合适的步长以达到局部最小值…

—第 87 页,优化算法,2019。

利用二阶导数的算法被称为二阶优化算法。

  • 二阶方法:利用二阶导数寻找目标函数最优值的优化算法。

二阶优化算法的一个例子是牛顿法。

当一个目标函数有一个以上的输入变量时,这些输入变量可以一起被认为是一个向量,这可能是线性代数所熟悉的。

梯度是多元函数导数的推广。它捕捉了函数的局部斜率,使我们能够预测从一个点向任何方向迈出一小步的效果。

—第 21 页,优化算法,2019。

类似地,多个输入变量的一阶导数也可以是向量,其中每个元素称为偏导数。这个偏导数向量被称为梯度。

  • 梯度:目标函数多个输入变量的偏一阶导数向量。

这一思想推广到多元输入的二阶导数,这是一个包含二阶导数的矩阵,称为 Hessian 矩阵。

  • 黑森:目标函数多个输入变量的偏二阶导数矩阵。

如果在我们计算导数的点上,二阶导数都是连续的,那么黑森矩阵是正方形和对称的。这通常是解决实值优化问题时的情况,也是使用许多二阶方法时的预期。

多元函数的 Hessian 是包含关于输入的所有二阶导数的矩阵。二阶导数捕捉关于函数局部曲率的信息。

—第 21 页,优化算法,2019。

因此,通常描述二阶优化算法利用或遵循目标函数的黑森最优。

现在我们对二阶优化算法有了一个高层次的理解,让我们仔细看看 BFGS 算法。

BFGS 优化算法

BFGS 是二阶优化算法。

这是一个首字母缩略词,以该算法的四个共同发现命名:布赖登、弗莱彻、戈德法布和尚诺。

这是一种局部搜索算法,旨在解决具有单一最优解的凸优化问题。

BFGS 算法可能最好理解为属于一组算法,这些算法是牛顿方法优化算法的扩展,被称为准牛顿方法。

牛顿法是一种利用黑森矩阵的二阶优化算法。

牛顿法的一个局限性是它需要计算黑森矩阵的逆。这是一个计算量很大的操作,根据目标函数的性质,可能不稳定。

拟牛顿法是二阶优化算法,它利用梯度近似黑森矩阵的逆,这意味着黑森矩阵及其逆不需要在算法的每一步都可用或精确计算。

拟牛顿法是非线性优化中应用最广泛的方法之一。它们被集成到许多软件库中,在解决各种中小型问题时非常有效,尤其是在黑森难以计算的情况下。

—第 411 页,线性和非线性优化,2009。

不同拟牛顿优化算法之间的主要区别在于计算逆黑森近似的具体方式。

BFGS 算法是更新逆 Hessian 计算的一种特定方式,而不是每次迭代都重新计算它。它,或者它的扩展,可能是最流行的拟牛顿甚至二阶优化算法之一,用于数值优化。

最流行的拟牛顿算法是 BFGS 方法,以它的发现者布赖登、弗莱彻、戈德法布和夏诺命名。

—第 136 页,数值优化,2006。

使用黑森函数(如果可用)的一个好处是,它可以用来确定移动的方向和步长,以改变输入参数,从而最小化(或最大化)目标函数。

像 BFGS 这样的拟牛顿方法近似逆黑森,然后可以用来确定移动的方向,但我们不再有步长。

BFGS 算法通过在选定的方向上使用直线性搜索来确定在该方向上移动多远来解决这个问题。

对于 BFGS 算法使用的推导和计算,我推荐本教程末尾的进一步阅读部分中的资源。

Hessian 及其反函数的大小与目标函数的输入参数数量成正比。因此,对于数百、数千或数百万个参数,矩阵的大小会变得非常大。

……BFGS 算法必须存储需要 O(n2)内存的逆 Hessian 矩阵 M,这使得 BFGS 算法对于大多数现代深度学习模型来说不切实际,这些模型通常有数百万个参数。

—第 317 页,深度学习,2016。

有限内存 BFGS (或 L-BFGS)是 BFGS 算法的扩展,解决了拥有大量参数的成本。它通过不要求存储逆矩阵的整个近似,通过假设在算法的前一次迭代中(在近似中使用)对逆矩阵 Hessian 进行简化来实现这一点。

现在,我们已经从高层次上熟悉了 BFGS 算法,让我们看看如何利用它。

BFGS 的成功范例

在本节中,我们将看一些使用 BFGS 优化算法的例子。

我们可以使用最小化()SciPy 函数在 Python 中实现优化任意函数的 BFGS 算法。

该函数需要多个参数,但最重要的是,我们可以将目标函数的名称指定为第一个参数,将搜索的起点指定为第二个参数,并将“方法参数指定为“ BFGS ”。用于计算目标函数导数的函数的名称可以通过“ jac ”参数指定。

...
# perform the bfgs algorithm search
result = minimize(objective, pt, method='BFGS', jac=derivative)

我们来看一个例子。

首先,我们可以定义一个简单的二维目标函数,一个碗函数,例如 x².简单来说,就是输入变量的平方和,在 f(0,0) = 0.0 时取一个最优值。

# objective function
def objective(x):
	return x[0]**2.0 + x[1]**2.0

接下来,让我们为函数的导数定义一个函数,它是[x2,y2]。

# derivative of the objective function
def derivative(x):
	return [x[0] * 2, x[1] * 2]

我们将把函数的边界定义为一个盒子,每个维度的范围是-5 和 5。

...
# define range for input
r_min, r_max = -5.0, 5.0

搜索的起点将是搜索域中随机生成的位置。

...
# define the starting point as a random sample from the domain
pt = r_min + rand(2) * (r_max - r_min)

然后,我们可以通过指定目标函数的名称、起始点、我们想要使用的方法(BFGS)和导数函数的名称,应用 BFGS 算法来找到目标函数的最小值。

...
# perform the bfgs algorithm search
result = minimize(objective, pt, method='BFGS', jac=derivative)

然后,我们可以查看结果,报告一条消息,说明算法是否成功完成,以及已执行的目标函数的评估总数。

...
# summarize the result
print('Status : %s' % result['message'])
print('Total Evaluations: %d' % result['nfev'])

最后,我们可以报告找到的输入变量及其对目标函数的评估。

...
# evaluate solution
solution = result['x']
evaluation = objective(solution)
print('Solution: f(%s) = %.5f' % (solution, evaluation))

将这些联系在一起,完整的示例如下所示。

# bfgs algorithm local optimization of a convex function
from scipy.optimize import minimize
from numpy.random import rand

# objective function
def objective(x):
	return x[0]**2.0 + x[1]**2.0

# derivative of the objective function
def derivative(x):
	return [x[0] * 2, x[1] * 2]

# define range for input
r_min, r_max = -5.0, 5.0
# define the starting point as a random sample from the domain
pt = r_min + rand(2) * (r_max - r_min)
# perform the bfgs algorithm search
result = minimize(objective, pt, method='BFGS', jac=derivative)
# summarize the result
print('Status : %s' % result['message'])
print('Total Evaluations: %d' % result['nfev'])
# evaluate solution
solution = result['x']
evaluation = objective(solution)
print('Solution: f(%s) = %.5f' % (solution, evaluation))

运行该示例将 BFGS 算法应用于我们的目标函数,并报告结果。

:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

在这种情况下,我们可以看到算法执行了四次迭代,发现了非常接近 optima f(0.0,0.0) = 0.0 的解,至少达到了有用的准确率水平。

Status: Optimization terminated successfully.
Total Evaluations: 4
Solution: f([0.00000000e+00 1.11022302e-16]) = 0.00000

*最小化()*功能还支持 L-BFGS 算法,该算法的内存需求低于 BFGS。

具体来说,算法的 L-BFGS-B 版本,其中-B 后缀表示算法的“盒装”版本,其中可以指定域的边界。

这可以通过将“方法参数指定为“ L-BFGS-B 来实现。

...
# perform the l-bfgs-b algorithm search
result = minimize(objective, pt, method='L-BFGS-B', jac=derivative)

下面列出了此更新的完整示例。

# l-bfgs-b algorithm local optimization of a convex function
from scipy.optimize import minimize
from numpy.random import rand

# objective function
def objective(x):
	return x[0]**2.0 + x[1]**2.0

# derivative of the objective function
def derivative(x):
	return [x[0] * 2, x[1] * 2]

# define range for input
r_min, r_max = -5.0, 5.0
# define the starting point as a random sample from the domain
pt = r_min + rand(2) * (r_max - r_min)
# perform the l-bfgs-b algorithm search
result = minimize(objective, pt, method='L-BFGS-B', jac=derivative)
# summarize the result
print('Status : %s' % result['message'])
print('Total Evaluations: %d' % result['nfev'])
# evaluate solution
solution = result['x']
evaluation = objective(solution)
print('Solution: f(%s) = %.5f' % (solution, evaluation))

运行示例应用程序将 L-BFGS-B 算法应用于我们的目标函数并报告结果。

:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

同样,我们可以看到函数的最小值是在很少的评估中找到的。

Status : b'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL'
Total Evaluations: 3
Solution: f([-1.33226763e-15 1.33226763e-15]) = 0.00000

将测试问题的维度增加到数百万个参数,并比较两种算法的内存使用和运行时间,这可能是一个有趣的练习。

进一步阅读

如果您想更深入地了解这个主题,本节将提供更多资源。

蜜蜂

文章

摘要

在本教程中,您发现了 BFGS 二阶优化算法。

具体来说,您了解到:

  • 二阶优化算法是利用二阶导数的算法,二阶导数被称为多元目标函数的 Hessian 矩阵。
  • BFGS 算法可能是最受欢迎的数值优化二阶算法,属于一组所谓的拟牛顿方法。
  • 如何使用 Python 中的 BFGS 和 L-BFGS-B 算法最小化目标函数。

你有什么问题吗?
在下面的评论中提问,我会尽力回答。

遗传编程书籍

原文:https://machinelearningmastery.com/books-on-genetic-programming/

最后更新于 2021 年 10 月 12 日

遗传编程 (GP)是一种进化程序以解决特定的定义明确的问题的算法。

这是一种自动编程类型,旨在解决具有挑战性的问题,任务定义明确,解决方案可以很容易地以低成本进行检查,尽管可能的解决方案的搜索空间很大,并且几乎没有解决问题的最佳方法的直觉。

这通常包括开放的问题,如控制器设计、电路设计,以及预测建模任务,如特征选择、分类和回归。

对于初学者来说,很难在这个领域开始,因为有大量的文献可以追溯到几十年前。

在本教程中,您将发现关于遗传编程的顶级书籍。

用我的新书机器学习优化启动你的项目,包括分步教程和所有示例的 Python 源代码文件。

Let’s get started.外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

遗传程序设计书籍
摄影作者卢卡·泰利版权所有。

教程概述

有许多关于遗传编程的书,可以按类型分组。

我们将探索关于基因编程的顶级书籍,分为三大类;它们是:

  1. 遗传程序设计
  2. 教科书
  3. 会议记录

遗传程序设计

约翰·科扎是一名计算机科学家,曾在遗传算法的发明者霍兰德手下学习。

在 20 世纪 80 年代末和 90 年代初,Koza 被认为是统一了新生的基因编程领域。

他以将遗传算法应用于电路设计而闻名,这导致了新的可申请专利的发明,并将遗传算法描述为将例行公事地产生“人类竞争的”结果。

他写了一系列关于遗传编程的四本教科书,如下:

他最近的一本书《遗传程序设计 4》是一个很好的开始。

本书开头的表格总结了四个关键要点;它们是:

1.基因编程现在定期提供高回报的人类竞争机器智能。
2。遗传编程是一种自动化的发明机器。
3。遗传编程可以以参数化拓扑的形式自动创建问题的一般解决方案。
4。遗传程序设计带来了一系列质量上更实质性的结果,与计算机时间消耗的五个大约数量级的增加同步。

—第 1 页,遗传编程四:常规人机竞争智能,2003。

这本书的目录如下:

  • 第一章:引言
  • 第二章:遗传程序设计的背景
  • 第三章:控制器的自动综合
  • 第四章:电路的自动合成
  • 第五章:电路拓扑、尺寸、布局和布线的自动综合
  • 第六章:天线的自动合成
  • 第七章:遗传网络的自动合成
  • 第八章:代谢途径的自动合成
  • 第九章:控制器参数化拓扑的自动综合
  • 第十章:电路参数化拓扑的自动综合
  • 第十一章:用电路条件发展算子自动综合参数化拓扑
  • 第十二章:PID 控制器改进整定规则的自动综合
  • 第十三章:改进控制器的参数化拓扑的自动综合
  • 第十四章:负面反馈的重塑
  • 第十五章:六个 2000 年后专利电路的自动化改造
  • 第十六章:遗传编程可能非常适合的问题
  • 第十七章:并行实现和计算机时间
  • 第十八章:摩尔定律的历史观点和遗传程序设计产生的定性更实质性结果的进展
  • 第十九章:结论

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

遗传编程四:常规人机竞争智能

教科书

已经出版了许多关于遗传编程的教科书,是为对该领域感兴趣的本科生和研究生设计的。

也许最受欢迎的书包括以下几本:

我会推荐最近的《遗传程序设计基础》

因此,遗传规划的基础不应该仅仅被看作是一个技术的集合,人们需要知道这些技术才能做好遗传规划,还应该被看作是第一次尝试绘制和探索遗传规划作为一种搜索算法背后的机制和基本原则。在写这本书的时候,我们希望对整个人工智能的理论基础有一点点了解。

——IIX 页,遗传程序设计基础,2002。

这本书的目录如下:

  • 第一章:引言
  • 第二章:健身景观
  • 第三章:程序组件模式理论
  • 第四章:悲观的 GP 图式理论
  • 第五章:精确 GP 模式定理
  • 第六章:全科医生图式理论的启示
  • 第七章:遗传规划搜索空间
  • 第八章:全科医生搜索空间:理论分析
  • 第九章:例子一:人工蚂蚁
  • 第十章:例子二:最大值问题
  • 第十一章:全科医生的融合和膨胀
  • 第十二章:结论

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

遗传程序设计基础

也许关于 GP 的最受欢迎的书籍之一是由该领域的顶尖学者自行出版的,面向对将遗传编程应用于他们的项目感兴趣的学生和开发人员。

这是书中的一个片段:

已经写了许多描述全科医生方面的书。有些提供了对整个领域的一般性介绍。然而,在过去的十年里,没有关于 GP 的新入门书籍问世,任何想了解 GP 的人都被迫独自痛苦地绘制地形图。这本书试图填补这一空白,为新手和老前辈提供一个现代的全科医生领域指南。

——遗传编程领域指南,2008 年。

这本书的目录如下:

  • 第一章:引言
  • 第二章:基于树的遗传规划中的表示、初始化和运算
  • 第三章:准备运行基因编程
  • 第四章:遗传编程运行示例
  • 第五章:基于树的遗传规划中的替代初始化和操作
  • 第六章:基于模块化、语法化和发展树的全科医生
  • 第七章:线性和图形遗传规划
  • 第八章:概率遗传规划
  • 第九章:多目标遗传规划
  • 第十章:快速分布式遗传编程
  • 第十一章:GP 理论及其应用
  • 第十二章:应用
  • 第十三章:GP 故障排除
  • 第十四章:结论

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

遗传程序设计领域指南

通常用新的名称来指代专门用于不同应用和表示的遗传编程算法版本,例如“线性遗传编程”、“笛卡尔遗传编程”和“语法进化

关于这些专门类型的遗传编程算法的一些教科书包括以下内容:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

语法进化手册

会议记录

在机器学习中分享发现的主要方式是通过会议,会议记录提供了来自会议的顶级论文的集合。

在任何给定的会议上提交的论文都可以围绕主题跳跃,如果没有该领域的一些基础知识,很难理解。然而,它们可以让你快速掌握当前流行的技术。

我建议关注任何程序的最新问题。没有必要追溯这些年。

你可能想看三个会议记录;它们是:

  • 遗传规划理论与实践
  • 遗传编程欧洲会议
  • 遗传规划的进展

让我们依次仔细看看每一个:

遗传规划理论与实践

遗传程序设计理论和实践会议每年举行一次,会议记录由斯普林格印刷。

这可能是关于全科医生的第一次会议。在编写本报告时,这是第 17 (XVII)期。

最后三个问题如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

遗传规划理论与实践

遗传编程欧洲会议

基因编程欧洲会议是另一个主要的基因编程会议。

像遗传程序设计理论和实践一样,这次会议及其发表的会议记录已经进行了几十年,在撰写本文时已经是第 23 年了。

最后三个问题如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

遗传程序设计:第 23 届欧洲会议

遗传规划的进展

遗传程序设计进展”是麻省理工学院出版社出版的一卷,包含收集到的论文。

90 年代中后期只出版过三次。然而,这些内容可能有助于加深对该领域的理解。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

遗传程序设计进展 3

推荐书籍

我读了大部分列出的书。

如果你想得到一本关于基因编程的书,我会推荐以下几本:

它将介绍该领域,并向您展示如何快速获得结果。

如果你正在寻找一个更全面的图书馆,我会推荐以下三种:

我书架上有这三本书。

有了这三本书,你将有一个坚实的理论基础,一个如何在实践中应用该技术的想法,以及一个已经实现的人类竞争结果的类型和用于实现它们的算法的想法。

摘要

在本教程中,您发现了关于遗传编程的顶级书籍。

你读过以上的书吗?
你怎么看?

我错过了你最喜欢的书吗?
在下面的评论里告诉我。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值