优化算法(一):模拟退火算法
前言
在实际日常中,人们会经常遇到如下问题:在某个给定的定义域X内,求函数f(x)对应的最优值。此处以最小值问题举例(最大值问题可以等价转化成最小值问题),形式化为: min f(x). 如果X是离散有限取值,那么可以通过穷取法获得问题的最优解;如果X连续,但f(x)是凸的,那可以通过梯度下降等方法获得最优解;如果X连续且f(x)非凸,虽说根据已有的近似求解法能够找到问题解,可解是否是最优的还有待考量,很多时候若初始值选择的不好,非常容易陷入局部最优值。
随着日常业务场景的复杂化,第三种问题经常遇见。如何有效地避免局部最优的困扰?模拟退火算法应运而生。其实模拟退火也算是启发式算法的一种,具体学习的是冶金学中金属加热-冷却的过程。由S.Kirkpatrick, C.D.Gelatt和M.P.Vecchi在1983年所发明的,V.Čern在1985年也独立发明此演算法。
不过模拟退火算法到底是如何模拟金属退火的原理?主要是将热力学的理论套用到统计学上,将搜寻空间内每一点想像成空气内的分子;分子的能量,就是它本身的动能;而搜寻空间内的每一点,也像空气分子一样带有“能量”,以表示该点对命题的合适程度。演算法先以搜寻空间内一个任意点作起始:每一步先选择一个“邻居”,然后再计算从现有位置到达“邻居”的概率。若概率大于给定的阈值,则跳转到“邻居”;若概率较小,则停留在原位置不动。 ****** ### 一、模拟退火算法基本思想 爬山法是一种贪婪的方法,对于一个优化问题,其大致图像如下图所示:
其目标是要找到函数的最大值,若初始化时,初始点的位置在C处,则会寻找到附近的局部最大值A点处,由于A点出是一个局部最大值点,故对于爬山法来讲,该算法无法跳出局部最大值点。若初始点选择在D处,根据爬山法,则会找到全部最大值点B。这一点也说明了这样基于贪婪的爬山法是否能够取得全局最优解与初始值的选取由很大的关系。
模拟退火算法(Simulated Annealing, SA)的思想借鉴于固体的退火原理,当固体的温度很高的时候,内能比较大,固体的内部粒子处于快速无序运动,当温度慢慢降低的过程中,固体的内能减小,粒子的慢慢趋于有序,最终,当固体处于常温时,内能达到最小,此时,粒子最为稳定。模拟退火算法便是基于这样的原理设计而成。
模拟退火算法从某一较高的温度出发,这个温度称为初始温度,伴随着温度参数的不断下降,算法中的解趋于稳定,但是,可能这样的稳定解是一个局部最优解,此时,模拟退火算法中会以一定的概率跳出这样的局部最优解,以寻找目标函数的全局最优解。如上图中所示,若此时寻找到了A点处的解,模拟退火算法会以一定的概率跳出这个解,如跳到了D点重新寻找,这样在一定程度上增加了寻找到全局最优解的可能性。
****** ### 二、模拟退火算法过程 (1)随机挑选一个单元k,并给它一个随机的位移,求出系统因此而产生的能量变化ΔEk。
(2)若ΔEk⩽0,该位移可采纳,而变化后的系统状态可作为下次变化的起点; 若ΔEk>0,位移后的状态可采纳的概率为
式中T为温度,然后从(0,1)区间均匀分布的随机数中挑选一个数R,若R < Pk ,则将变化后的状态作为下次的起点;否则,将变化前的状态作为下次的起点。
(3)转第(1)步继续执行,知道达到平衡状态为止。
****** ### 三、模拟退火算法的优缺点 模拟退火算法的应用很广泛,可以高效地求解NP完全问题,如货郎担问题(Travelling Salesman Problem,简记为TSP)、最大截问题(Max Cut Problem)、0-1背包问题(Zero One Knapsack Problem)、图着色问题(Graph Colouring Problem)等等,但其参数难以控制,不能保证一次就收敛到最优值,一般需要多次尝试才能获得(大部分情况下还是会陷入局部最优值)。观察模拟退火算法的过程,发现其主要存在如下三个参数问题:
(1) 温度T的初始值设置问题 温度T的初始值设置是影响模拟退火算法全局搜索性能的重要因素之一、初始温度高,则搜索到全局最优解的可能性大,但因此要花费大量的计算时间;反之,则可节约计算时间,但全局搜索性能可能受到影响。
(2) 退火速度问题,即每个T值的迭代次数 模拟退火算法的全局搜索性能也与退火速度密切相关。一般来说,同一温度下的“充分”搜索是相当必要的,但这也需要计算时间。循环次数增加必定带来计算开销的增大。
(3) 温度管理问题
温度管理问题也是模拟退火算法难以处理的问题之一。实际应用中,由于必须考虑计算复杂度的切实可行性等问题,常采用如下所示的降温方式:
T=α×T.α∈(0,1).
注:为了保证较大的搜索空间,α一般取接近于1的值,如0.95、0.9。 ****** ###
四、模拟退火算法Python实战
经过上面理论知识的熏陶,相信大家已经对模拟退火算法有了较深入的理解,接下来通过实战再强化一下大家的认识,此处利用模拟退火算法求解如下优化问题:
min f(x)=(x−2)∗(x+3)∗(x+8)∗(x−9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
def inputfun(x):
return (x-2)*(x+3)*(x+8)*(x-9)
initT = 1000 #初始温度
minT = 1 #温度下限
iterL = 1000 #每个T值的迭代次数
delta = 0.95 #温度衰减系数
k = 1
initx = 10*(2*np.random.rand()-1)
nowt = initT
print("初始解:",initx)
xx = np.linspace(-10,10,300)
yy = inputfun(xx)
plt.figure()
plt.plot(xx,yy)
plt.plot(initx,inputfun(initx),'o')
#模拟退火算法寻找最小值过程
while nowt>minT:
for i in np.arange(1,iterL,1):
funVal = inputfun(initx)
xnew = initx+(2*np.random.rand()-1)
if xnew>=-10 and xnew<=10:
funnew = inputfun(xnew)
res = funnew-funVal
if res<0:
initx = xnew
else:
p = np.exp(-(res)/(k*nowt))
if np.random.rand()<p:
initx = xnew
nowt = nowt * delta
print("最优解:",initx)
print("最优值:",inputfun(initx))
plt.plot(initx,inputfun(initx),'*r')
plt.show()