6.5 Boltzmann机算法
6.5.1 问题的提出
6.5.2 模拟退化原理
6.5.3 Boltzmann分布与退火过程
6.5.4Boltzmann机类与退火过程
Boltzmann网络初始时,需要根据参数设置一系列的初始值,主要参数在_init_中
(1)构造方法如下
class BoltzmannNet(object): def __init__(self): self.dataMat = [] #外部导入的数据集 self.MAX_ITER = 2000 #外循环迭代次数 Self.T0 = 1 000 #最高温度:这个温度变化范围应位于最大迭代范围 self.Lambda = 0.97 #温度下降参数 self.iteration = 0 #达到最优时的迭代次数 self.dist = [] #存储生成的距离 self.pathindx = [] #存储生成的路径索引 self.bestdist = 0 #生成的最优路径长度 self.bestpath = [] #生成的最优路径
(2)加载数据文件类
def loadDataSet(self,filename): #加载数据集 self.dataMat = [] self.classLabels = [] fr = open(filename) for line in fr.readlines(): lineArr = line.strip().split() self.dataMat.append([float(lineArr[0]),float(lineArr[1]),1.0]) self.classLabels.append(int(float(lineArr[2]))) self.dataMat = mat(self.dataMat) m,n = shape(self.dataMat) self.nSampNum = m #样本数量 self.nSampDim = n-1 #样本维度
(3)计算矩阵各向量之间的距离--欧式距离
def distEclud(self,vecA,vecB): #欧式距离 eps = 1.0e-6 return linalg.norm(vecA-vecB) + eps
(4)玻耳兹曼函数
def boltzmann(self,new1,old1,T): return exp(-(new1-old1)/T)
(5)计算路径长度
def pathLen(self,dist,path): N = len(path) plen = 0 for i in xrange(0,N-1): #长度为N的向量,包含1~N的整数 plen += dist[path[i],path[i+1]] plen += dist[path[0],path[N-1]] return plen
(6)交换新旧路径
def changePath(self,old_path): N = len(old_path) if random.rand() < 0.25: #产生两个位置,并交换 chpos = floor(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: d = ceil(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] else: new_path = self.changePath(old_path) return new_path
(7)绘制路径
def drawPath(self,Seq,dataMat,color = 'b'): m,n = shape(dataMat) px = (dataMat[Seq,0]).tolist() py = (dataMat[Seq,1]).tolist() px.append(px[0]) py.append(py[0]) plt.plot(px,py,color)
(8)绘制散点图
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
(9)绘制趋势线
def Trendline(self,plt,color = 'b'): plt.plot(range(len(self.dist)),self.dist,color)
6.5.5 最短路径的实现
Boltzmann网络的具体运行步骤如下
第一步:导入数据,并根据配置参数初始化网络
初始化网络函数如下:
def initBMNet(self,m,n,distMat): self.pathindx = range(m) random.shuffle(self.pathindx) #随机生成每个路径 self.dist.append(self.pathLen(distMat,self.pathindx)) #每个路径对应的距离 return self.T0,self.pathindx,m
#第一步:导入数据,并根据配置参数初始化网络 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 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.bestdist = self.pathindx[indxes]
6.5.6 执行算法
bmNet = BoltzmannNet() bmNet.loadDataSet("dataSet25.txt") bmNet.train() print u"循环迭代",bmNet.bestdist print u"最佳路线",bmNet.bestpath #显示优化后的城市地图、路径图 bmNet.drawScatter(plt) plt.show() bmNet.Trendline(plt) #绘制误差算法收敛曲线 plt.show()
全部代码:
#coding:utf-8 from numpy import * import copy import matplotlib.pyplot as plt 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 = [] self.classLabels = [] fr = open(filename) for line in fr.readlines(): lineArr = line.strip().split() # self.dataMat.append([float(lineArr[0]),float(lineArr[1]),1.0]) self.dataMat.append([float(lineArr[0]),float(lineArr[1])]) self.classLabels.append(int(float(lineArr[2]))) self.dataMat = mat(self.dataMat) m,n = shape(self.dataMat) self.nSampNum = m #样本数量 self.nSampDim = n-1 #样本维度 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,new1,old1,T): return exp(-(new1-old1)/T) def pathLen(self,dist,path): N = len(path) plen = 0 for i in xrange(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 random.rand() < 0.25: #产生两个位置,并交换 chpos = floor(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: d = ceil(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] else: new_path = self.changePath(old_path) return new_path def drawPath(self,Seq,dataMat,color = 'b'): m,n = shape(dataMat) px = (dataMat[Seq,0]).tolist() py = (dataMat[Seq,1]).tolist() px.append(px[0]) py.append(py[0]) plt.plot(px,py,color) 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(range(len(self.dist)),self.dist,color) def initBMNet(self,m,n,distMat): self.pathindx = range(m) random.shuffle(self.pathindx) #随机生成每个路径 self.dist.append(self.pathLen(distMat,self.pathindx)) #每个路径对应的距离 return self.T0,self.pathindx,m #第一步:导入数据,并根据配置参数初始化网络 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 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] bmNet = BoltzmannNet() bmNet.loadDataSet("dataSet25.txt") bmNet.train() print u"循环迭代",bmNet.iteration,u"次" print u"最优解",bmNet.bestdist print u"最佳路线",bmNet.bestpath #显示优化后的城市地图、路径图 bmNet.drawScatter(plt) plt.show() bmNet.Trendline(plt) #绘制误差算法收敛曲线 plt.show()
资料来源:郑捷《机器学习算法原理与编程实践》 仅供学习研究