今天的工作做的不多,主要是后面又玩云服务器去了,那个东西配置了小半天,华为的,学校花了大价钱搞过来的320G运行内存,结果因为是centos7,还是arch的 搞的我连anaconda的官方版都没有,最后只能去GitHub搞一个适用arch的,Python版本只到3.7.1,然后我的代码又是按照新语法来的,有类型,又不支持。
所以今天只做了基于Kmeans++ 来分种群,本来还想搞拓扑结构的,但是,那几篇中文论文质量真的底下,也没说清楚,我知道拓扑结构是咋样的,但是用的是什么策略获取的相邻的点的,看了半天啥也没有,如果是基于距离的话,高纬度就不用做了。目前有可能的是按照粒子的ID来的。如果是那样的话,我感觉不如直接乱来算了。
郑重提示:本文版权归本人所有,任何人不得抄袭,搬运,使用需征得本人同意!
日期:2022.6.21 DAY 2
昨天的时候编码有个问题,一直没发现,今天跑了几次发现结果对不到,于是我拿来以前同学写过的PSO对比了一下,一开始以为是np的问题,可能是哪里深浅复制有问题,仔细看发现,深浅复制的话我使用的都是深拷贝(通过生成新的list实现),然后以为是np的问题,然后,我又按照我的代码格式去写(因为要上分布式,所以能不用矩阵就不用)后来发现其实是我的速度忘记更新给粒子了。
#coding=utf-8
#这个是最基础的PSO算法实现,用于测试当前算法架构的合理性
#此外这个玩意还是用来优化的工具类,整个算法是为了求取最小值
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(os.getcwd())))
from ONEPSO.Bird import Bird
from ONEPSO.Config import *
from ONEPSO.Target import Target
import random
import time
class BasePso(object):
Population = None
Random = random.random
target = Target()
W = W
def __init__(self):
#为了方便,我们这边直接先从1开始
self.Population = [Bird(ID) for ID in range(1,PopulationSize+1)]
def ComputeV(self,bird):
#这个方法是用来计算速度滴
NewV=[]
for i in range(DIM):
v = bird.V[i]*self.W + C1*self.Random()*(bird.PBestX[i]-bird.X[i])\
+C2*self.Random()*(bird.GBestX[i]-bird.X[i])
#这里注意判断是否超出了范围
if(v>V_max):
v = V_max
elif(v<V_min):
v = V_min
NewV.append(v)
return NewV
def ComputeX(self,bird:Bird):
NewX = []
NewV = self.ComputeV(bird)
bird.V = NewV
for i in range(DIM):
x = bird.X[i]+NewV[i]
if(x>X_up):
x = X_up
elif(x<X_down):
x = X_down
NewX.append(x)
return NewX
def InitPopulation(self):
#初始化种群
GBestX = [0. for _ in range(DIM)]
Flag = float("inf")
for bird in self.Population:
bird.PBestX = bird.X
bird.Y = self.target.SquareSum(bird.X)
bird.PbestY = bird.Y
if(bird.Y<=Flag):
GBestX = bird.X
Flag = bird.Y
#便利了一遍我们得到了全局最优的种群
for bird in self.Population:
bird.GBestX = GBestX
bird.GBestY = Flag
def Running(self):
#这里开始进入迭代运算
for iterate in range(1,IterationsNumber+1):
w = LinearW(iterate)
#这个算的GBestX其实始终是在算下一轮的最好的玩意
GBestX = [0. for _ in range(DIM)]
Flag = float("inf")
for bird in self.Population:
#更改为线性权重
self.W = w
x = self.ComputeX(bird)
y = self.target.SquareSum(x)
# 这里还是要无条件更细的,不然这里的话C1就失效了
# if(y<=bird.Y):
# bird.X = x
# bird.Y = y
bird.X = x
bird.Y = y
if(bird.Y<=bird.PbestY):
bird.PBestX=bird.X
bird.PbestY = bird.Y
#个体中的最优一定包含了全局经历过的最优值
if(bird.PbestY<=Flag):
GBestX = bird.PBestX
Flag = bird.PbestY
for bird in self.Population:
bird.GBestX = GBestX
bird.GBestY=Flag
if __name__ == '__main__':
start = time.time()
basePso = BasePso()
basePso.InitPopulation()
basePso.Running()
end = time.time()
# for bird in basePso.Population:
# print(bird)
print(basePso.Population[0])
print("花费时长:",end-start)
这部分修改还是参考原来的那几篇文献来做的。
首先是Kmeans++的实现。
#coding=utf-8
#由于中心点的选取对于PSO多种群来说比较重要
#所以这里选择Kmeans++ 来慎重选择中心点
#距离公式采用欧式距离
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(os.getcwd())))
from ONEPSO.Bird import Bird
from ONEPSO.Config import *
import random
import math
class KmeansAdd(object):
choice = random.sample
ClusterCenters = []
def Divide(self,Popluations,Centers):
#划分,这边也是如果不满足条件就按照最后一个族群来划分
#这里注意一点原来的CID的编号是多少压根就不重要,只要后面准确分类就没有问题
for bird in Popluations:
BirdCID = -1
Flag = float("inf")
for center in Centers:
dist = self.EuclideanDistan(bird,center)
if(dist<=Flag):
Flag=dist
BirdCID+=1
bird.CID=BirdCID
bird.DIST=Flag
def EuclideanDistan(self,bird,center):
#欧式距离公式
x1 = bird.X
x2 = center
dist = 0
for index in range(len(x1)):
dist+=math.pow((x1[index]-x2[index]),2)
return dist
def InitChoiceCenter(self,Population):
#初始化选取中心点,先选择一个,然后帮我完成初始化分类,此时的ClusterCenters存储的就是
#我们初始化得到的中心点
K = 1
self.ClusterCenters = self.choice(Population,K)
RelCenters = []
for i in range(K):
#分配CID
self.ClusterCenters[i].CID=(i)
RelCenters.append(self.ClusterCenters[i].X)
#这个中心点存的是可行域内的点坐标
self.ClusterCenters=RelCenters
#此时会完成第一次划分
self.Divide(Population,self.ClusterCenters)
#构造权重模型
Tim = ClusterNumber-K
for i in range(Tim):
K+=1
Weights = [bird.DIST for bird in Population]
Total = sum(Weights)
Weights=[weight/Total for weight in Weights]
#在采用轮盘算法
x = -1
i = 0
Due = random.random()
while(i<Due):
x+=1
i+=Weights[x]
self.ClusterCenters.append(Population[x].X)
#再次划分中心点,之后通过增加中心点,我们可以完成对子种群的划分
self.Divide(Population, self.ClusterCenters)
def RunningChoiceCenter(self,Population):
#运行时选取中心点,返回计算后的新的中心点
NewClusterCenterPoints={}
Counter = {}
#先初始化一下计数器,新的中心点
for CID in range(ClusterNumber):
Counter[CID]=0
for bird in Population:
if(not NewClusterCenterPoints.get(bird.CID)):
NewClusterCenterPoints[bird.CID]=bird.X
else:
NewClusterCenterPoints[bird.CID] = self.__ADD_X(NewClusterCenterPoints.get(bird.CID),bird.X)
Counter[bird.CID]=Counter[bird.CID] + 1
#计算出新的中心点
for CID in NewClusterCenterPoints.keys():
NewClusterCenterPoints[CID] = self.__Division_X(NewClusterCenterPoints.get(CID),Counter.get(CID))
return list(NewClusterCenterPoints.values())
def RunningDivide(self,Poplution,Iterate=0):
self.InitChoiceCenter(Poplution)
for epoch in range(K_Iterate):
NewClusterCenters = self.RunningChoiceCenter(Poplution)
if(
len(self.ClusterCenters)!=ClusterNumber or self.__SAME_Pred(self.ClusterCenters,NewClusterCenters)>=SAEMPRED
):
# print("Bad",epoch,Iterate,len(self.ClusterCenters))
break
else:
self.Divide(Poplution,NewClusterCenters)
self.ClusterCenters=NewClusterCenters
def __SAME_Pred(self,LastCenters,NewCenters):
#这个玩意是用来计算相似度的,如果发现相似度超过阈值,那么停止迭代
res = 0.
count=0
if(len(LastCenters)!=len(NewCenters)):
return False
for i in range(len(LastCenters)):
for j in range(len(LastCenters[i])):
res+=math.pow((LastCenters[i][j]-NewCenters[i][j]),2)
count+=1
res = 1-(res/count)
return res
def __ADD_X(self,X1,X2):
res = []
for i in range(len(X1)):
res.append(X1[i]+X2[i])
return res
def __Division_X(self,X1,X2):
res =[]
for i in range(len(X1)):
res.append(X1[i]/X2)
return res
这个还是比较简单的,主要是注意初始化和一般的Kmeans不一样。然后这里还是使用欧氏距离来做的,哪一个距离公式会好一点,这个要测试,我感觉是这个会好一点,因为,这个PSO本来就是基于空间的。
之后整合代码如下:
#coding=utf-8
#现在是基于Kmeans++ 写的动态划分子种群的PSO算法。
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(os.getcwd())))
from ONEPSO.BasePso import BasePso
from ONEPSO.Config import *
from ONEPSO.Bird import Bird
import time
from ONEPSO.KmeansAdd import KmeansAdd
class DKMPSO(BasePso):
kmeansAdd = KmeansAdd()
def __init__(self):
super(DKMPSO,self).__init__()
self.kmeansAdd.RunningDivide(self.Population)
def ComputeV(self,bird):
#这个方法是用来计算速度滴
NewV=[]
for i in range(DIM):
v = bird.V[i]*self.W + C1*self.Random()*(bird.PBestX[i]-bird.X[i])\
+C2*self.Random()*(bird.GBestX[i]-bird.X[i]) + C3*self.Random()*(bird.CBestX[i]-bird.X[i])
#这里注意判断是否超出了范围
if(v>V_max):
v = V_max
elif(v<V_min):
v = V_min
NewV.append(v)
return NewV
def ComputeX(self,bird):
NewX = []
NewV = self.ComputeV(bird)
bird.V = NewV
for i in range(DIM):
x = bird.X[i]+NewV[i]
if (x > X_up):
x = X_up
elif (x < X_down):
x = X_down
NewX.append(x)
return NewX
def InitPopulation(self):
#初始化种群
#这个是记录全局最优解的
GBestX = [0. for _ in range(DIM)]
Flag = float("inf")
#还有一个是记录Cluster最优解的
CBest = {}
CFlag = {}
for i in range(ClusterNumber):
CFlag[i]=float("inf")
for bird in self.Population:
bird.PBestX = bird.X
bird.Y = self.target.SquareSum(bird.X)
bird.PbestY = bird.Y
bird.CBestX = bird.X
bird.CBestY = bird.Y
if(bird.Y<=Flag):
GBestX = bird.X
Flag = bird.Y
if(bird.Y<=CFlag.get(bird.CID)):
CBest[bird.CID]=bird.X
CFlag[bird.CID] = bird.Y
#便利了一遍我们得到了全局最优的种群
for bird in self.Population:
bird.GBestX = GBestX
bird.GBestY = Flag
bird.CBestY=CFlag.get(bird.CID)
bird.CBestX=CBest.get(bird.CID)
def Running(self):
#这里开始进入迭代运算
for iterate in range(1,IterationsNumber+1):
w = LinearW(iterate)
#这个算的GBestX其实始终是在算下一轮的最好的玩意
GBestX = [0. for _ in range(DIM)]
Flag = float("inf")
CBest = {}
CFlag = {}
for i in range(ClusterNumber):
CFlag[i] = float("inf")
for bird in self.Population:
#更改为线性权重
self.W = w
x = self.ComputeX(bird)
y = self.target.SquareSum(x)
# 这里还是要无条件更细的,不然这里的话C1就失效了
# if(y<=bird.Y):
# bird.X = x
# bird.PBestX = x
bird.X = x
bird.Y = y
if(bird.Y<=bird.PbestY):
bird.PBestX=bird.X
bird.PbestY = bird.Y
#个体中的最优一定包含了全局经历过的最优值
if(bird.PbestY<=Flag):
GBestX = bird.PBestX
Flag = bird.PbestY
if (bird.Y <= CFlag.get(bird.CID)):
CBest[bird.CID] = bird.X
CFlag[bird.CID] = bird.Y
for bird in self.Population:
bird.GBestX = GBestX
bird.GBestY=Flag
bird.CBestY = CFlag.get(bird.CID)
bird.CBestX = CBest.get(bird.CID)
if((iterate+1)%100==0):
self.kmeansAdd.RunningDivide(self.Population,iterate)
if __name__ == '__main__':
start = time.time()
dkmpso = DKMPSO()
dkmpso.InitPopulation()
dkmpso.Running()
end = time.time()
# for bird in dkmpso.Population:
# print(bird)
print(dkmpso.Population[0])
print("花费时长:",end-start)
其实和原来的直接分没太大区别,只是换了一个划分的标准,然后这里是每100次迭代重新划分一次,其实我感觉就一开始,划分后就不用划分会好一点,这个也是测试会好一点。
首先,我们玩多种群的目的是,第一防止早熟,第二保证均匀性(后面上多目标,如果粒子都往一个方向跑,容前沿很难搞),第三 利用更多的信息,正对高纬度优化问题。
我这里针对10个维度,然后大概对100,200,1000,2000,10000…次迭代做了实验(大概20多个参数,然后每组大概10-20次实验)。
然后设置每轮K次重新计算中心。
首先,总体上看,初始化划分之后就不划分了,效果是最差的。
之后是针对多少轮一次进行测试,大概目前得到的设置方式是,按照迭代次数除以5来是比较好的,也是比较快的。也就是五倍左右
采用最传统的粒子群的效果是最好的
采用我直接分子种群的效果不错
采用这个有KMeans的效果和没有的区别不大,差距不大,综合情况下我推荐没有的。不过这个目前的话Kmeans其实没有用好,因为其实这个速度更新是和论文不一样的。
当然这些都是在有限的实验次数下,初步得到的结果,每个测试不跑个100+ 这个数据我说实话我是不太敢相信的。偶然性太高了。
之后是明天要研究这个拓扑结构的和强化学习的,那个拓扑结构的论文写得是真模棱两可,还有那篇介绍拓扑结构运用的,也是,不是看不起中文论文,是真的水分太大了。而且这中论文的学术造假可太容易做了,因为同样的代码,运行结果是不同的,稍微美化一下,数据就来了。
不过这里的话,我其实还是在做各种测试,说实话,要不断修改。