Boltzmann机算法(模拟退火算法)(郑捷 著)

Boltzmann机算法(模拟退火算法)(郑捷 著)

1. 问题的提出

1)TSP问题可以描述:
假设有一个旅行商人要拜访N个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。
2)TSP的应用:
运输路线问题,装配线上的螺帽问题,在排产计划、电路板布线、基因测序和机器人控制等方面有着重要的应用。

2. 模拟退火原理

退火的物理学解释:
缓慢的降温过程释放了材料中的内能,使停留在局部最小值位置(内能)的原子离开原来位置,而随机地在材料内部移动,这就有可能找到比原先更低内能的位置。要达到这个目标,退火过程一般分为三个阶段:加温过程、等温过程、冷却过程。
从算法所需的角度而言,如果把材料看作整个数据集,降温过程看作算法的迭代过程,“释放材料内部应力”意味着激活函数求解的计算,那么找到全局最优的可能性就随着迭代次数增加(随着温度的下降)而增加,就认为寻找到数据集中所有的可能解,而其中最小(或最大)的那个解就近似地看作全局最优解,即以概率1收敛于全局最优解。

3. Boltzmann分布(玻尔兹曼分布)

在统计学和数学中,玻尔兹曼分布是系统各种可能的状态的一个概率分布、概率测度或粒子频度分布。该分布在统计力学中的表达形式为:
这里写图片描述
选择问题对于优化结果就会产生很大的影响:
1)初始温度的设置会影响算法执行的时间;初始温度过高,会导致运行时间过长,不足则容易漏掉全局最优点
2)降温速度的快慢,过快的降温往往会漏掉全局最优点,使算法收敛于某个局部最优;如果速度太慢,则会延迟得到全局最优的时间,从而影响求解问题的规模。

4. 降温策略

这里写图片描述

# -*- coding:utf-8 -*-
"""
Boltzmann机求解TSP问题
completation by20180825
"""
import numpy as np
from numpy import *
import copy
import matplotlib.pyplot as plt
import pandas as pd
import xlrd

class BoltzmannNet(object):
    def __init__(self):
        self.dataMat = [] # 外部导入的数据集
        self.MAX_ITER = 2000  # 外循环迭代次数
        self.T0 = 1000        # 最高温度:这个温度的变化范围应位于最大迭代范围之内
        self.Lambda = 0.97    # 温度下降参数
        self.iteration = 0    # 迭代最优时的迭代次数
        self.dist = []        # 存储生成的距离
        self.pathindx = []    # 存储生成的路径索引
        self.bestdist = 0     # 生成的最优路径长度
        self.bestpath = []    # 生成的最优路径

    def loadDataSet(self,filename):
        self.dataMat = []
        fr = open(filename)
        for line in fr.readlines():
            lineArr = line.strip().split()
            self.dataMat.append([float(lineArr[0]), float(lineArr[1])])
        self.dataMat = mat(self.dataMat)
        return self.dataMat


    def distEclud(self, vecA, vecB):
        # 欧式距离
        distMat = zeros((vecA.shape[0], vecA.shape[0]))
        for i in range(len(vecA)):
            for j in range(len(vecA)):
                distMat[i][j] = linalg.norm(vecA[i] - vecB[:, j])
        return distMat


    def boltzmann(self,newl,oldl,T): # 玻尔兹曼函数
        return exp(-(newl-oldl)/T)


    def pathLen(self,dist,path): # 计算路径长度
        N = len(path)
        plen = 0
        for i in arange(0,N-1): # 长度为N的向量,包含1~N的整数
            plen += dist[path[i],path[i+1]]
        plen += dist[path[0],path[N-1]]
        return plen

    def changePath(self,old_path): # 交换新旧路径
        N = len(old_path)
        if np.random.rand() < 0.25: # 产生两个位置,并交换
            chpos = floor(np.random.rand(1,2)*N).tolist()[0]
            new_path = copy.deepcopy(old_path)
            new_path[int(chpos[0])] = old_path[int(chpos[1])]
            new_path[int(chpos[1])] = old_path[int(chpos[0])]
        else:  # 产生三个位置,交换a-b和b-c
            d = ceil(np.random.rand(1,3)*N).tolist()[0]
            d.sort() # 随机路径排序
            a = int(d[0])
            b = int(d[1])
            c = int(d[2])
            if a != b and b != c:
                new_path = copy.deepcopy(old_path)
                #new_path[a:c-1] = old_path[b-1:c-1]+ old_path[a:b-1] # python2 列表的某段替换
                new_path[a:a + c - b] = copy.copy(old_path[b - 1:c - 1]) # 与上等价
                new_path[a + c - b:c - 1] = copy.copy(old_path[a:b - 1])
                #new_path=new_path.tolist() # 数组转列表
            else:
                new_path = self.changePath(old_path)
        return new_path

    def drawPath(self,plt):# 绘制路径
        px = []
        py = []
        for Seq in self.bestpath:
            px.append(self.dataMat[Seq,0])
            py.append(self.dataMat[Seq,1])
        plt.plot(px,py,'b')


    def drawScatter(self,plt): # 绘制散点图
        px = (self.dataMat[:,0]).tolist()
        py = (self.dataMat[:,1]).tolist()
        plt.scatter(px,py,c='green',marker='o',s=60)
        i=65
        for x,y in zip(px,py):
            plt.annotate(str(chr(i)),xy=(x[0]+40,y[0]),color='black')
            i += 1

    def TrendLine(self,plt,color='b'): # 绘制趋势线
        plt.plot(arange(len(self.dist)),self.dist,color)

    def initBMNet(self,m,n,distMat): # 构造一个初始可行解
        self.pathindx = arange(m).tolist()
        np.random.shuffle(self.pathindx) # 随机生成每个路径
        self.dist.append(self.pathLen(distMat,self.pathindx)) # 每个路径对应的距离
        return self.T0,self.pathindx,m

    # 最短路径的实现
    # 1.导入数据,并根据配置参数初始化网络
    def train(self): # 主函数
        m,n = shape(self.dataMat)
        distMat = self.distEclud(self.dataMat,self.dataMat.T) # 转换为邻接矩阵(距离矩阵)
        # T为当前温度,curpath为当前路径索引,MAX_M为内循环最大迭代次数
        [T,curpath,MAX_M] = self.initBMNet(m,n,distMat)
        step = 0 # 初始化外循环迭代
        while step <= self.MAX_ITER: # 外循环
            m = 0 # 内循环计数器
            while m <= MAX_M:        # 内循环
                curdist = self.pathLen(distMat,curpath) # 计算当前路径距离
                newpath = self.changePath(curpath)      # 交换产生新路径
                newdist = self.pathLen(distMat,newpath) # 计算新路径距离
                # 判断网络是否是一个局部稳态
                if (curdist > newdist):
                    curpath = newpath
                    self.pathindx.append(curpath)
                    self.dist.append(newdist)
                    self.iteration += 1   # 增加迭代次数
                else: # 如果网络处于局部稳态,则执行玻尔兹曼函数
                    if np.random.rand() < self.boltzmann(newdist,curdist,T):
                        curpath = newpath
                        self.pathindx.append(curpath)
                        self.dist.append(newdist)
                        self.iteration += 1  # 增加迭代次数
                m += 1
            step += 1
            T = T*self.Lambda # 降温,返回迭代,直至算法终止

        # 提取最优路径
        self.bestdist = min(self.dist)
        indxes = argmin(self.dist)
        self.bestpath = self.pathindx[indxes]

if __name__ == "__main__":
    # 加载
    bmNet = BoltzmannNet()
    bmNet.loadDataSet("dataSet25.txt")
    bmNet.train()
    print("循环迭代",bmNet.iteration,"次")
    print("最优解:",bmNet.bestdist)
    print("最佳路线:",bmNet.bestpath)
    # 显示优化后的城市图,路径图
    bmNet.drawScatter(plt)
    bmNet.drawPath(plt)
    plt.show()
    # 绘制误差算法收敛曲线
    bmNet.TrendLine(plt)
    plt.show()

 

数据:

6734 1453
2233 10
5530 1424
401 841
3082 1644
7608 4458
7573 3716
7265 1268
6898 1885
1112 2049
5468 2606
5989 2873
4706 2674
4612 2035
6347 2683
6107 669
7611 5184
7462 3590
7732 4723
5900 3561
4483 3369
6101 1110
5199 2182
1633 2809
4307 2322
675 1006
7555 4819
7541 3981
3177 756
7352 4506
7545 2801
3245 3305
6426 3173
4608 1198
23 2216
7248 3779
7762 4595
7392 2244
3484 2829
6271 2135
4985 140
1916 1569
7280 4899
7509 3239
10 2676
6807 2993
5185 3258
3023 1942

 

结果:
循环迭代 5395 次
最优解: 203879.287844
最佳路线: [5, 36, 18, 16, 26, 42, 29, 27, 35, 17, 30, 37, 8, 7, 0, 39, 22, 2, 21, 15, 40, 28, 44, 1, 3, 25, 34, 9, 41, 23, 47, 4, 33, 13, 38, 31, 24, 12, 20, 46, 10, 11, 14, 19, 32, 45, 43, 6]
这里写图片描述
这里写图片描述

 

深度学习 --- 模拟退火算法详解(Simulated Annealing, SA)

zsffuture 2018-11-16 11:46:37 29761 收藏 76
分类专栏: 深度学习
版权
上一节我们深入探讨了,Hopfield神经网络的性质,介绍了吸引子和其他的一些性质,而且引出了伪吸引子,因为伪吸引子的存在导致Hopfield神经网络正确率下降,因此本节致力于解决伪吸引子的存在。在讲解方法之前我们需要再次理解一些什么是伪吸引子,他到底是如何产生的?

简单来说说就是网络动态转移过程,状态掉进了局部最优解里了,就是能量函数没有达到最低,只是掉进了局部能量最低的状态,这和我们梯度容易获得局部最优解差不多,大家这样理解就好,想要深入理解的建议自己多参考文献。为了解决伪吸引子的问题,人们提出了模拟退火算法和玻尔兹曼机彻底解决了伪吸引子的问题,但是带来的另外一个问题就是计算量很大,这些我们会一步步的讲解。好,我们知道了问题,也知道了方法,现在是看看到底如何解决的,但是前提我们需要搞明白什么是模拟退火算法。

模拟退火算法:

       为了解决局部最优解问题, 1983年,Kirkpatrick等提出了模拟退火算法(SA)能有效的解决局部最优解问题。我们知道在分子和原子的世界中,能量越大,意味着分子和原子越不稳定,当能量越低时,原子越稳定。‘退火’是物理学术语,指对物体加温在冷却的过程。模拟退火算法来源于晶体冷却的过程,如果固体不处于最低能量状态,给固体加热再冷却,随着温度缓慢下降,固体中的原子按照一定形状排列,形成高密度、低能量的有规则晶体,对应于算法中的全局最优解。而如果温度下降过快,可能导致原子缺少足够的时间排列成晶体的结构,结果产生了具有较高能量的非晶体,这就是局部最优解。因此就可以根据退火的过程,给其在增加一点能量,然后在冷却,如果增加能量,跳出了局部最优解,这本次退火就是成功的,下面我们就详细讲讲他是如何在局部最优解跳出来到全局最优解的:

        模拟退火算法包含两个部分即Metropolis算法和退火过程。Metropolis算法就是如何在局部最优解的情况下让其跳出来,是退火的基础。1953年Metropolis提出重要性采样方法,即以概率来接受新状态,而不是使用完全确定的规则,称为Metropolis准则,计算量较低。下面先形象的说一下,然后在因此数学公式:

 

    假设开始状态在A,随着迭代次数更新到B的局部最优解,这时发现更新到B时,能力比A要低,则说明接近最优解了,因此百分百转移,状态到达B后,发现下一步能量上升了,如果是梯度下降则是不允许继续向前的,而这里会以一定的概率跳出这个坑,这各概率和当前的状态、能量等都有关系,下面会详细说,如果B最终跳出来了到达C,又会继续以一定的概率跳出来,可能有人会迷惑会不会跳回之前的B呢?下面会解释,直到到达D后,就会稳定下来。所以说这个概率的设计是很重要的,下面从数学方面进行解释。

    假设前一个状态为,系统根据某一指标(梯度下降,上节的能量),状态变为,相应的,系统的能量由变为,定义系统由变为的接受概率P为:

                                         

从上式我们可以看到,如果能量减小了,那么这种转移就被接受(概率为1),如果能量增大了,就说明系统偏离全局最优值位置更远了,此时算法不会立刻将其抛弃,而是进行概率操作:首先在区间【0,1】产生一个均匀分布的随机数,如果P,则此种转移接受,否则拒绝转移,进入下一步,往复循环。其中P以能量的变化量和T进行决定概率P的大小,所以这个值是动态的。

退火算法的参数控制

       Metropolis算法是模拟退火算法的基础,但是直接使用Metropolis算法 可能会导致寻优速度太慢,以至于无法实际使用,为了确保在有限的时间收敛,必须设定控制算法收敛的参数,在上面的公式中,可以调节的参数就是T,T如果过大,就会导致退火太快,达到局部最优值就会结束迭代,如果取值较小,则计算时间会增加,实际应用中采用退火温度表,在退火初期采用较大的T值,随着退火的进行,逐步降低,具体如下:

    (1)初始的温度T(0)应选的足够高,使的所有转移状态都被接受。初始温度越高,获得高质量的解的概率越大,耗费的时间越长。

    (2) 退火速率。 最简单的下降方式是指数式下降:

                                                    

                 其中是小于1的正数,一般取值为0.8到0.99之间。使的对每一温度,有足够的转移尝试,指数式下降的收敛速度比较慢,其他下降方式如下:

                                                      

                                                        

      (3)终止温度

                        如果在若干次迭代的情况下每有可以更新的新状态或者达到用户设定的阈值,则退火完成。

模拟退火的步骤:   

               1.模拟退火算法可以分解为解空间、目标函数和初始解三部分。

               2.模拟退火的基本思想:

                    (1) 初始化:初始温度T(充分大),初始解状态S(是算法迭代的起点),每个T值的迭代次数L

                    (2) 对k=1, …, L做第(3)至第6步:

                    (3) 产生新解S′

                    (4) 计算增量ΔT=C(S′)-C(S),其中C(S)为代价函数

                    (5) 若ΔT<0则接受S′作为新的当前解,否则以概率exp(-ΔT/T)接受S′作为新的当前解.

                    (6) 如果满足终止条件则输出当前解作为最优解,结束程序。

                    终止条件通常取为连续若干个新解都没有被接受时终止算法。

                     (7) T逐渐减少,且T->0,然后转第2步。

模拟退火算法新解的产生和接受可分为如下四个步骤:

第一步是由一个产生函数从当前解产生一个位于解空间的新解;为便于后续的计算和接受,减少算法耗时,通常选择由当前新解经过简单地变换即可产生新解的方法,如对构成新解的全部或部分元素进行置换、互换等,注意到产生新解的变换方法决定了当前新解的邻域结构,因而对冷却进度表的选取有一定的影响。

第二步是计算与新解所对应的目标函数差。因为目标函数差仅由变换部分产生,所以目标函数差的计算最好按增量计算。事实表明,对大多数应用而言,这是计算目标函数差的最快方法。

第三步是判断新解是否被接受,判断的依据是一个接受准则,最常用的接受准则是Metropolis准则: 若ΔT<0则接受S′作为新的当前解S,否则以概率exp(-ΔT/T)接受S′作为新的当前解S。

第四步是当新解被确定接受时,用新解代替当前解,这只需将当前解中对应于产生新解时的变换部分予以实现,同时修正目标函数值即可。此时,当前解实现了一次迭代。可在此基础上开始下一轮试验。而当新解被判定为舍弃时,则在原当前解的基础上继续下一轮试验。

模拟退火算法与初始值无关,算法求得的解与初始解状态S(是算法迭代的起点)无关;模拟退火算法具有渐近收敛性,已在理论上被证明是一种以概率l 收敛于全局最优解的全局优化算法;模拟退火算法具有并行性。

退火算法程序流程图;

 

 

       上面就是模拟退火算法的全部内容了,本节讲的仅仅是可以应用在任何出现局部最优解的的算法上,还没和 Hopfield神经网络结合在一起解决伪吸引子的问题,把退火算法和Hopfield神经网络结合在一起就是玻尔兹曼机了,下一节在详细探讨,本节到此结束。   
————————————————
版权声明:本文为CSDN博主「zsffuture」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42398658/java/article/details/84031235

 

 

参考:

https://www.cnblogs.com/tuhooo/p/5440473.html
http://www.cnblogs.com/hhh5460/p/4319652.html
https://blog.csdn.net/changdejie/article/details/78102894
http://blog.sina.com.cn/s/blog_c0ea025f0102xhk0.html

posted on 2020-06-16 14:59  曹明  阅读(568)  评论(0编辑  收藏  举报