5分钟掌握Python随机爬山算法

开发 后端 算法
在本教程中,您将发现用于函数优化的爬山优化算法完成本教程。

 随机爬山是一种优化算法。它利用随机性作为搜索过程的一部分。这使得该算法适用于非线性目标函数,而其他局部搜索算法不能很好地运行。它也是一种局部搜索算法,这意味着它修改了单个解决方案并搜索搜索空间的相对局部区域,直到找到局部最优值为止。这意味着它适用于单峰优化问题或在应用全局优化算法后使用。

在本教程中,您将发现用于函数优化的爬山优化算法完成本教程后,您将知道:

  •  爬山是用于功能优化的随机局部搜索算法。
  •  如何在Python中从头开始实现爬山算法。
  •  如何应用爬山算法并检查算法结果。

教程概述

本教程分为三个部分:他们是:

  •  爬山算法
  •  爬山算法的实现
  •  应用爬山算法的示例

爬山算法

随机爬山算法是一种随机局部搜索优化算法。它以起始点作为输入和步长,步长是搜索空间内的距离。该算法将初始点作为当前最佳候选解决方案,并在提供的点的步长距离内生成一个新点。计算生成的点,如果它等于或好于当前点,则将其视为当前点。新点的生成使用随机性,通常称为随机爬山。这意味着该算法可以跳过响应表面的颠簸,嘈杂,不连续或欺骗性区域,作为搜索的一部分。重要的是接受具有相等评估的不同点,因为它允许算法继续探索搜索空间,例如在响应表面的平坦区域上。限制这些所谓的“横向”移动以避免无限循环也可能是有帮助的。该过程一直持续到满足停止条件,例如最大数量的功能评估或给定数量的功能评估内没有改善为止。该算法之所以得名,是因为它会(随机地)爬到响应面的山坡上,达到局部最优值。这并不意味着它只能用于最大化目标函数。这只是一个名字。实际上,通常,我们最小化功能而不是最大化它们。作为局部搜索算法,它可能会陷入局部最优状态。然而,多次重启可以允许算法定位全局最优。步长必须足够大,以允许在搜索空间中找到更好的附近点,但步幅不能太大,以使搜索跳出包含局部最优值的区域。

爬山算法的实现

在撰写本文时,SciPy库未提供随机爬山的实现。但是,我们可以自己实现它。首先,我们必须定义目标函数和每个输入变量到目标函数的界限。目标函数只是一个Python函数,我们将其命名为Objective()。边界将是一个2D数组,每个输入变量都具有一个维度,该变量定义了变量的最小值和最大值。例如,一维目标函数和界限将定义如下: 

  1. # objective function  
  2. def objective(x):  
  3.  return 0   
  4. # define range for input  
  5. bounds = asarray([[-5.0, 5.0]]) 

接下来,我们可以生成初始解作为问题范围内的随机点,然后使用目标函数对其进行评估。 

  1. # generate an initial point  
  2. solution = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])  
  3. # evaluate the initial point  
  4. solution_eval = objective(solution) 

现在我们可以遍历定义为“ n_iterations”的算法的预定义迭代次数,例如100或1,000。 

  1. # run the hill climb  
  2. for i in range(n_iterations): 

算法迭代的第一步是采取步骤。这需要预定义的“ step_size”参数,该参数相对于搜索空间的边界。我们将采用高斯分布的随机步骤,其中均值是我们的当前点,标准偏差由“ step_size”定义。这意味着大约99%的步骤将在当前点的(3 * step_size)之内。 

  1. # take a step  
  2. candidate = solution + randn(len(bounds)) * step_size 

我们不必采取这种方式。您可能希望使用0到步长之间的均匀分布。例如: 

  1. # take a step  
  2. candidate = solution + rand(len(bounds)) * step_size 

接下来,我们需要评估具有目标函数的新候选解决方案。 

  1. # evaluate candidate point  
  2. candidte_eval = objective(candidate) 

然后,我们需要检查此新点的评估结果是否等于或优于当前最佳点,如果是,则用此新点替换当前最佳点。 

  1. # check if we should keep the new point  
  2. if candidte_eval <= solution_eval:  
  3.  # store the new point  
  4.  solution, solution_eval = candidate, candidte_eval  
  5.  # report progress  
  6.  print('>%d f(%s) = %.5f' % (i, solution, solution_eval)) 

就是这样。我们可以将此爬山算法实现为可重用函数,该函数将目标函数的名称,每个输入变量的范围,总迭代次数和步骤作为参数,并返回找到的最佳解决方案及其评估。 

  1. # hill climbing local search algorithm  
  2. def hillclimbing(objective, bounds, n_iterations, step_size):  
  3.  # generate an initial point  
  4.  solution = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])  
  5.  # evaluate the initial point  
  6.  solution_eval = objective(solution)  
  7.  # run the hill climb  
  8.  for i in range(n_iterations):  
  9.   # take a step  
  10.   candidate = solution + randn(len(bounds)) * step_size  
  11.   # evaluate candidate point  
  12.   candidte_eval = objective(candidate)  
  13.   # check if we should keep the new point 
  14.   if candidte_eval <= solution_eval:  
  15.    # store the new point  
  16.    solution, solution_eval = candidate, candidte_eval  
  17.    # report progress  
  18.    print('>%d f(%s) = %.5f' % (i, solution, solution_eval))  
  19.  return [solution, solution_eval] 

现在,我们知道了如何在Python中实现爬山算法,让我们看看如何使用它来优化目标函数。

应用爬山算法的示例

在本节中,我们将把爬山优化算法应用于目标函数。首先,让我们定义目标函数。我们将使用一个简单的一维x ^ 2目标函数,其边界为[-5,5]。下面的示例定义了函数,然后为输入值的网格创建了函数响应面的折线图,并用红线标记了f(0.0)= 0.0处的最佳值。 

  1. # convex unimodal optimization function  
  2. from numpy import arange  
  3. from matplotlib import pyplot   
  4. # objective function  
  5. def objective(x):  
  6.  return x[0]**2.0   
  7. # define range for input  
  8. r_min, r_max = -5.0, 5.0  
  9. # sample input range uniformly at 0.1 increments  
  10. inputs = arange(r_min, r_max, 0.1)  
  11. # compute targets  
  12. results = [objective([x]) for x in inputs]  
  13. # create a line plot of input vs result  
  14. pyplot.plot(inputs, results)  
  15. # define optimal input value  
  16. x_optima = 0.0  
  17. # draw a vertical line at the optimal input  
  18. pyplot.axvline(x=x_optimals='--'color='red' 
  19. # show the plot  
  20. pyplot.show() 

运行示例将创建目标函数的折线图,并清晰地标记函数的最优值。

接下来,我们可以将爬山算法应用于目标函数。首先,我们将播种伪随机数生成器。通常这不是必需的,但是在这种情况下,我想确保每次运行算法时都得到相同的结果(相同的随机数序列),以便以后可以绘制结果。 

  1. # seed the pseudorandom number generator  
  2. seed(5) 

接下来,我们可以定义搜索的配置。在这种情况下,我们将搜索算法的1,000次迭代,并使用0.1的步长。假设我们使用的是高斯函数来生成步长,这意味着大约99%的所有步长将位于给定点(0.1 * 3)的距离内,例如 三个标准差。 

  1. n_iterations = 1000  
  2. # define the maximum step size  
  3. step_size = 0.1 

接下来,我们可以执行搜索并报告结果。 

  1. # perform the hill climbing search  
  2. best, score = hillclimbing(objective, bounds, n_iterations, step_size)  
  3. print('Done!')  
  4. print('f(%s) = %f' % (best, score)) 

结合在一起,下面列出了完整的示例。 

  1. # hill climbing search of a one-dimensional objective function  
  2. from numpy import asarray  
  3. from numpy.random import randn  
  4. from numpy.random import rand  
  5. from numpy.random import seed   
  6. # objective function  
  7. def objective(x):  
  8.  return x[0]**2.0   
  9. # hill climbing local search algorithm  
  10. def hillclimbing(objective, bounds, n_iterations, step_size):  
  11.  # generate an initial point  
  12.  solution = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])  
  13.  # evaluate the initial point  
  14.  solution_eval = objective(solution)  
  15.  # run the hill climb  
  16.  for i in range(n_iterations): 
  17.   # take a step  
  18.   candidate = solution + randn(len(bounds)) * step_size  
  19.   # evaluate candidate point  
  20.   candidte_eval = objective(candidate)  
  21.   # check if we should keep the new point  
  22.   if candidte_eval <= solution_eval:  
  23.    # store the new point  
  24.    solution, solution_eval = candidate, candidte_eval  
  25.    # report progress  
  26.    print('>%d f(%s) = %.5f' % (i, solution, solution_eval))  
  27.  return [solution, solution_eval]  
  28. # seed the pseudorandom number generator  
  29. seed(5)  
  30. # define range for input  
  31. bounds = asarray([[-5.0, 5.0]])  
  32. # define the total iterations 
  33. n_iterations = 1000  
  34. # define the maximum step size  
  35. step_size = 0.1  
  36. # perform the hill climbing search  
  37. best, score = hillclimbing(objective, bounds, n_iterations, step_size)  
  38. print('Done!')  
  39. print('f(%s) = %f' % (best, score)) 

运行该示例将报告搜索进度,包括每次检测到改进时的迭代次数,该函数的输入以及来自目标函数的响应。搜索结束时,找到最佳解决方案,并报告其评估结果。在这种情况下,我们可以看到在算法的1,000次迭代中有36处改进,并且该解决方案非常接近于0.0的最佳输入,其计算结果为f(0.0)= 0.0。 

  1. >1 f([-2.74290923]) = 7.52355  
  2. >3 f([-2.65873147]) = 7.06885  
  3. >4 f([-2.52197291]) = 6.36035  
  4. >5 f([-2.46450214]) = 6.07377  
  5. >7 f([-2.44740961]) = 5.98981  
  6. >9 f([-2.28364676]) = 5.21504  
  7. >12 f([-2.19245939]) = 4.80688  
  8. >14 f([-2.01001538]) = 4.04016  
  9. >15 f([-1.86425287]) = 3.47544  
  10. >22 f([-1.79913002]) = 3.23687  
  11. >24 f([-1.57525573]) = 2.48143  
  12. >25 f([-1.55047719]) = 2.40398  
  13. >26 f([-1.51783757]) = 2.30383  
  14. >27 f([-1.49118756]) = 2.22364  
  15. >28 f([-1.45344116]) = 2.11249  
  16. >30 f([-1.33055275]) = 1.77037  
  17. >32 f([-1.17805016]) = 1.38780  
  18. >33 f([-1.15189314]) = 1.32686  
  19. >36 f([-1.03852644]) = 1.07854  
  20. >37 f([-0.99135322]) = 0.98278  
  21. >38 f([-0.79448984]) = 0.63121  
  22. >39 f([-0.69837955]) = 0.48773  
  23. >42 f([-0.69317313]) = 0.48049  
  24. >46 f([-0.61801423]) = 0.38194  
  25. >48 f([-0.48799625]) = 0.23814  
  26. >50 f([-0.22149135]) = 0.04906  
  27. >54 f([-0.20017144]) = 0.04007  
  28. >57 f([-0.15994446]) = 0.02558  
  29. >60 f([-0.15492485]) = 0.02400  
  30. >61 f([-0.03572481]) = 0.00128  
  31. >64 f([-0.03051261]) = 0.00093  
  32. >66 f([-0.0074283]) = 0.00006  
  33. >78 f([-0.00202357]) = 0.00000  
  34. >119 f([0.00128373]) = 0.00000  
  35. >120 f([-0.00040911]) = 0.00000  
  36. >314 f([-0.00017051]) = 0.00000  
  37. Done!  
  38. f([-0.00017051]) = 0.000000 

以线图的形式查看搜索的进度可能很有趣,该线图显示了每次改进后最佳解决方案的评估变化。每当有改进时,我们就可以更新hillclimbing()来跟踪目标函数的评估,并返回此分数列表。 

  1. # hill climbing local search algorithm  
  2. def hillclimbing(objective, bounds, n_iterations, step_size):  
  3.  # generate an initial point  
  4.  solution = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])  
  5.  # evaluate the initial point  
  6.  solution_eval = objective(solution)  
  7.  # run the hill climb 
  8.  scores = list()  
  9.  scores.append(solution_eval)  
  10.  for i in range(n_iterations):  
  11.   # take a step  
  12.   candidate = solution + randn(len(bounds)) * step_size  
  13.   # evaluate candidate point  
  14.   candidte_eval = objective(candidate)  
  15.   # check if we should keep the new point  
  16.   if candidte_eval <= solution_eval:  
  17.    # store the new point  
  18.    solution, solution_eval = candidate, candidte_eval  
  19.    # keep track of scores  
  20.    scores.append(solution_eval)  
  21.    # report progress  
  22.    print('>%d f(%s) = %.5f' % (i, solution, solution_eval))  
  23.  return [solution, solution_eval, scores] 

然后,我们可以创建这些分数的折线图,以查看搜索过程中发现的每个改进的目标函数的相对变化。 

  1. # line plot of best scores  
  2. pyplot.plot(scores, '.-')  
  3. pyplot.xlabel('Improvement Number')  
  4. pyplot.ylabel('Evaluation f(x)')  
  5. pyplot.show() 

结合在一起,下面列出了执行搜索并绘制搜索过程中改进解决方案的目标函数得分的完整示例。 

  1. # hill climbing search of a one-dimensional objective function  
  2. from numpy import asarray  
  3. from numpy.random import randn  
  4. from numpy.random import rand  
  5. from numpy.random import seed  
  6. from matplotlib import pyplot   
  7. # objective function  
  8. def objective(x):  
  9.  return x[0]**2.0  
  10. # hill climbing local search algorithm  
  11. def hillclimbing(objective, bounds, n_iterations, step_size):  
  12.  # generate an initial point  
  13.  solution = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])  
  14.  # evaluate the initial point  
  15.  solution_eval = objective(solution)  
  16.  # run the hill climb  
  17.  scores = list()  
  18.  scores.append(solution_eval)  
  19.  for i in range(n_iterations):  
  20.   # take a step  
  21.   candidate = solution + randn(len(bounds)) * step_size  
  22.   # evaluate candidate point  
  23.   candidte_eval = objective(candidate)  
  24.   # check if we should keep the new point  
  25.   if candidte_eval <= solution_eval:  
  26.    # store the new point  
  27.    solution, solution_eval = candidate, candidte_eval  
  28.    # keep track of scores  
  29.    scores.append(solution_eval)  
  30.    # report progress  
  31.    print('>%d f(%s) = %.5f' % (i, solution, solution_eval))  
  32.  return [solution, solution_eval, scores]  
  33. # seed the pseudorandom number generator  
  34. seed(5)  
  35. # define range for input  
  36. bounds = asarray([[-5.0, 5.0]])  
  37. # define the total iterations  
  38. n_iterations = 1000  
  39. # define the maximum step size  
  40. step_size = 0.1  
  41. # perform the hill climbing search  
  42. best, score, scores = hillclimbing(objective, bounds, n_iterations, step_size)  
  43. print('Done!')  
  44. print('f(%s) = %f' % (best, score))  
  45. # line plot of best scores  
  46. pyplot.plot(scores, '.-')  
  47. pyplot.xlabel('Improvement Number')  
  48. pyplot.ylabel('Evaluation f(x)')  
  49. pyplot.show() 

运行示例将执行搜索,并像以前一样报告结果。创建一个线形图,显示在爬山搜索期间每个改进的目标函数评估。在搜索过程中,我们可以看到目标函数评估发生了约36个变化,随着算法收敛到最优值,初始变化较大,而在搜索结束时变化很小,难以察觉。

鉴于目标函数是一维的,因此可以像上面那样直接绘制响应面。通过将在搜索过程中找到的最佳候选解决方案绘制为响应面中的点,来回顾搜索的进度可能会很有趣。我们期望沿着响应面到达最优点的一系列点。这可以通过首先更新hillclimbing()函数以跟踪每个最佳候选解决方案在搜索过程中的位置来实现,然后返回最佳解决方案列表来实现。 

  1. # hill climbing local search algorithm  
  2. def hillclimbing(objective, bounds, n_iterations, step_size):  
  3.  # generate an initial point  
  4.  solution = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])  
  5.  # evaluate the initial point  
  6.  solution_eval = objective(solution) 
  7.  # run the hill climb  
  8.  solutions = list()  
  9.  solutions.append(solution)  
  10.  for i in range(n_iterations):  
  11.   # take a step  
  12.   candidate = solution + randn(len(bounds)) * step_size  
  13.   # evaluate candidate point 
  14.    candidte_eval = objective(candidate)  
  15.   # check if we should keep the new point  
  16.   if candidte_eval <= solution_eval:  
  17.    # store the new point  
  18.    solution, solution_eval = candidate, candidte_eval  
  19.    # keep track of solutions  
  20.    solutions.append(solution) 
  21.    # report progress  
  22.    print('>%d f(%s) = %.5f' % (i, solution, solution_eval))  
  23.  return [solution, solution_eval, solutions] 

然后,我们可以创建目标函数响应面的图,并像以前那样标记最优值。 

  1. # sample input range uniformly at 0.1 increments  
  2. inputs = arange(bounds[0,0], bounds[0,1], 0.1)  
  3. # create a line plot of input vs result  
  4. pyplot.plot(inputs, [objective([x]) for x in inputs], '--')  
  5. # draw a vertical line at the optimal input  
  6. pyplot.axvline(x=[0.0], ls='--'color='red'

最后,我们可以将搜索找到的候选解的序列绘制成黑点。 

  1. # plot the sample as black circles  
  2. pyplot.plot(solutions, [objective(x) for x in solutions], 'o', color='black'

结合在一起,下面列出了在目标函数的响应面上绘制改进解序列的完整示例。 

  1. # hill climbing search of a one-dimensional objective function  
  2. from numpy import asarray  
  3. from numpy import arange  
  4. from numpy.random import randn  
  5. from numpy.random import rand  
  6. from numpy.random import seed  
  7. from matplotlib import pyplot  
  8. # objective function  
  9. def objective(x):  
  10.  return x[0]**2.0  
  11. # hill climbing local search algorithm  
  12. def hillclimbing(objective, bounds, n_iterations, step_size):  
  13.  # generate an initial point  
  14.  solution = bounds[:, 0] + rand(len(bounds)) * (bounds[:, 1] - bounds[:, 0])  
  15.  # evaluate the initial point  
  16.  solution_eval = objective(solution)  
  17.  # run the hill climb  
  18.  solutions = list()  
  19.  solutions.append(solution)  
  20.  for i in range(n_iterations):  
  21.   # take a step  
  22.   candidate = solution + randn(len(bounds)) * step_size  
  23.   # evaluate candidate point  
  24.   candidte_eval = objective(candidate)  
  25.   # check if we should keep the new point  
  26.   if candidte_eval <= solution_eval:  
  27.    # store the new point  
  28.    solution, solution_eval = candidate, candidte_eval  
  29.    # keep track of solutions  
  30.    solutions.append(solution) 
  31.     # report progress  
  32.    print('>%d f(%s) = %.5f' % (i, solution, solution_eval))  
  33.  return [solution, solution_eval, solutions]  
  34. # seed the pseudorandom number generator  
  35. seed(5)  
  36. # define range for input  
  37. bounds = asarray([[-5.0, 5.0]])  
  38. # define the total iterations  
  39. n_iterations = 1000  
  40. # define the maximum step size  
  41. step_size = 0.1  
  42. # perform the hill climbing search  
  43. best, score, solutions = hillclimbing(objective, bounds, n_iterations, step_size)  
  44. print('Done!')  
  45. print('f(%s) = %f' % (best, score))  
  46. # sample input range uniformly at 0.1 increments  
  47. inputs = arange(bounds[0,0], bounds[0,1], 0.1)  
  48. # create a line plot of input vs result 
  49. pyplot.plot(inputs, [objective([x]) for x in inputs], '--')  
  50. # draw a vertical line at the optimal input  
  51. pyplot.axvline(x=[0.0], ls='--'color='red' 
  52. # plot the sample as black circles  
  53. pyplot.plot(solutions, [objective(x) for x in solutions], 'o', color='black' 
  54. pyplot.show() 

运行示例将执行爬山搜索,并像以前一样报告结果。像以前一样创建一个响应面图,显示函数的熟悉的碗形,并用垂直的红线标记函数的最佳状态。在搜索过程中找到的最佳解决方案的顺序显示为黑点,沿着碗形延伸到最佳状态。

 

 

责任编辑:庞桂玉 来源: Python中文社区 (ID:python-china)
相关推荐

2021-03-23 15:35:36

Adam优化语言

2020-12-17 10:00:16

Python协程线程

2021-03-12 09:45:00

Python关联规则算法

2023-10-07 13:13:24

机器学习模型数据

2020-12-07 11:23:32

Scrapy爬虫Python

2017-01-10 09:07:53

tcpdumpGET请求

2020-12-01 12:44:44

PythonHook钩子函数

2020-11-24 11:50:52

Python文件代码

2020-10-27 10:43:24

Redis字符串数据库

2021-06-07 09:51:22

原型模式序列化

2009-11-17 14:50:50

Oracle调优

2018-01-30 05:04:06

2021-04-19 23:29:44

MakefilemacOSLinux

2012-06-28 10:26:51

Silverlight

2020-09-11 09:35:18

前端JavaScript策略模式

2021-04-27 10:16:51

优化机器学习人工智能

2019-07-24 15:29:55

JavaScript开发 技巧

2009-11-05 10:55:22

Visual Stud

2020-05-06 10:10:51

Python代码链式调用

2021-01-11 09:33:37

Maven数目项目
点赞
收藏

51CTO技术栈公众号