CSDN上找了一圈粒子群算法的python代码,全是以类来写的,由于对类不感冒(其实是不咋会用),于是参照matlab版本的代码写了一个纯函数调用不带类的版本,并不复杂,没必要使用。
原理部分别的博客已经介绍得很清楚了,就不再介绍了,放个链接:https://blog.csdn.net/daaikuaichuan/article/details/81382794这个讲的比较直白易懂了。
参考:粒子群算法的matlab实现
首先放一些参数的意义和常见的取值范围:
对照matlab代码,用python实现如下。我这里是将训练好的神经网络模型当成适应度函数,并给它加上了惩罚度,如何调用神经网络模型利用遗传算法寻优感兴趣的可以看看我的另一篇文章:利用深度学习模型基于遗传算法(GA)寻求最优解,迁移到其他问题将适应度函数def F(x):替换成你需要求解的就可以了。
'''
autor: wsw
Time: 2021.8.28
'''
from tensorflow_core import keras
import numpy as np
from tensorflow.keras.models import load_model
import os
import matplotlib.pyplot as plt
import random
SIZE = 100 #初始化种群数量,初始种群取50-1000都是可以的,虽然初始种群越大收敛性会更好,不过太大了也会影响速度
DIM = 1 #空间维数,自变量的个数
N = 100 #迭代次数,一般取100~4000,太少解不稳定,太多浪费时间
x_limit = [0.2, 1] #位置参数限制 变量x的取值范围 燃料极水的摩尔分数
v_limit = [-0.1, 0.1] #速度限制 一般设为每维变量变化范围的10%~20% 如果粒子飞行速度过快,很可能直接飞过最优解位置,但是如果飞行速度过慢,会使得收敛速度变慢
w = 0.8 #惯性权重 该参数反映了个体历史成绩对现在的影响,一般取0.5~1;
c1 = 0.5 #自我学习因子,一般取0~4,此处要根据自变量的取值范围来定,并且学习因子分为个体和群体两种
c2 = 0.5 #群体学习因子
'''输入固定参数'''
V = 1.4
I = 24000
T = 1083
V_BOUND = [0.7, 1.8]
I_BOUND = [4.39, 30309.58]
T_BOUND = [1023, 1123]
Q_BOUND = [-1.4588, 9.4078]
'''将输入参数按照训练模型时采用的归一化方法归一化'''
nor_V = 0.8 * (V - 0.7) / (1.8 - 0.7) + 0.2
nor_I = 0.8 * (I - 4.39) / (30309.58 - 4.39) + 0.2
nor_T = 0.8 * (T - 1023) / (1123 - 1023) + 0.2
def rmse(y_true, y_pred): #模型中自定义的评价函数,加载模型时需要加上
return keras.backend.sqrt(keras.backend.mean(keras.backend.square(y_pred - y_true), axis=-1))
folder = "D:\Desktop\模型文件" #模型所在位置
os.chdir(folder)
global model
global Q_MIN_EPOCH
global xH2O
model = load_model('D:\Desktop\AI+遗传算法\由xH2O预测SCCM\checkpoint6_830_12点19\model.h5',custom_objects={'rmse': rmse})
'''适应度函数,将所有种群输入模型得到预测值'''
def F(x):
num = x.shape[0]
X2 = []
T = False
SCCM_max = 4 #非热中性点和非最低热值点可以取的最大值(归一化后)
Q_real_min = 10 #热量真实值最小值初值
Q_real_max = 0 #热量真实值最大值初值
for i in range(num): #将种群参数加上固定参数组成训练模型的输入
X = [nor_V, nor_I, nor_T, x[i, 0]]
X2.append(X)
Xtest = np.array(X2)
X_test = Xtest.reshape(-1, 4)
Y_pre = model.predict(X_test)
for i in range(num): #遍历,判断能否达到热中性
if Y_pre[i, 0] > 3.8 or Y_pre[i, 0] < 0: # 当预测值出现偏差超出最大流量限制,或比最小流量小时,跳出循环
print("\n参数选择异常,SCCM>1200或SCCM<300,请重新选择合理参数\n")
exit()
Q_real = (Y_pre[i, -1] - 0.2) / 0.8 * (9.4078 + 1.4588) - 1.4588 # 将输出参数由归一化值转为实际值
# if Y_pre[i, 0] < SCCM_min: #流量的最小值(归一化后) 最小值可能比能达到的最大值SCCM_MAX大 所以不如取0
# SCCM_min = Y_pre[i, 0]
if abs(Q_real) < 1e-7:
T = True
if Y_pre[i, 0] < SCCM_max:
SCCM_max = 0.7 * Y_pre[i, 0] #如果能达到热中性,所有热中性点中的最小SCCM为非热中性点可以达到的最大值,非热中性点适应度应当更小,加上惩罚数
if abs(Q_real) > Q_real_max:
Q_real_max = abs(Q_real) #得到该条件下热量真实值最大值
i_last = 0
if T == False: #如果达不到热中性计算热量最小值
for i in range(num):
Q_real = (Y_pre[i, -1] - 0.2) / 0.8 * (9.4078 + 1.4588) - 1.4588 # 将输出参数由归一化值转为实际值
if abs(Q_real) < Q_real_min:
Q_real_min = abs(Q_real)
i_last = i
SCCM_max = 0.7 * Y_pre[i, 0] #最小放热量对应的流量,应成为其他点可以达到的最大适应度
# if SCCM_max < 0: #防止非热中性点的流量归一化值在加上惩罚数以后小于最小0
# SCCM_max = 0.0000001
for i in range(num): #限制条件:热中性,同时排除个别误差值较大的数据的影响
Q_real = (Y_pre[i, -1] - 0.2) / 0.8 * (9.4078 + 1.4588) - 1.4588 # 将输出参数由归一化值转为实际值
if abs(Q_real) > 1e-7:
if abs(Q_real) > Q_real_min:
Y_TEMP = 1e-7 + (Q_real_max - abs(Q_real))/(Q_real_max-Q_real_min)*SCCM_max #惩罚函数,无法达到热中性时减小适应度,离最低Q越近适应度越大,不能超过SCCM_max
if Y_TEMP < Y_pre[i, 0]: #加上惩罚函数的适应度小于原适应度,替换原适应度
Y_pre[i, 0] = Y_TEMP
Q_MIN_EPOCH.append(Q_real_min)
xH2O.append(Xtest[i_last, 3])
return Y_pre[:, 0] #返回燃料极流量归一化值
'''计算适应度'''
def get_fitness(PSO):
pred = F(PSO)
return pred #返回由深度学习模型预测的SCCM归一化值
if __name__ == "__main__":
PSO = np.random.uniform(x_limit[0], x_limit[1], (1, DIM)) #粒子位置初始化
VEL = np.random.uniform(v_limit[0], v_limit[1], (1, DIM)) #粒子速度初始化
max_fit_record = [] #记录最大值
Q_MIN_EPOCH = [] #记录最小热量
xH2O = [] #记录对应的水的摩尔分数
for i in range(SIZE - 1):
pso = np.random.uniform(x_limit[0], x_limit[1], (1, DIM))
PSO = np.concatenate((PSO, pso), axis=0)
vel = np.random.uniform(v_limit[0], v_limit[1], (1, DIM))
VEL = np.concatenate((VEL, vel), axis=0)
x_max = PSO #每个个体的历史最佳位置
y_max = np.zeros(DIM) #种群的历史最佳位置
fxm = np.zeros((SIZE, 1)) #每个个体的历史最佳适应度
fym = 0 #种群历史最佳适应度
#群体更新
for i in range(N):
fx = get_fitness(PSO) #个体适应度
for j in range(SIZE):
if fxm[i] < fx[i]: #更新个体历史最佳适应度
fxm[i] = fx[i] #更新个体历史最佳位置
x_max[i, :] = PSO[i, :]
if fym < np.max(fxm):
fym = np.max(fxm)
nmax = np.argmax(fxm) #更新群体历史最佳适应度
y_max = x_max[nmax, :] #更新群体历史最佳位置
VEL = VEL * w + c1 * random.random() * (x_max - PSO) + c2 * random.random() * (np.tile(y_max, (SIZE, 1)) - PSO) #速度更新
VEL[VEL > v_limit[1]] = v_limit[1] #边界速度处理
VEL[VEL < v_limit[0]] = v_limit[0]
PSO[PSO > x_limit[1]] = x_limit[1]
PSO[PSO > x_limit[0]] = x_limit[0]
max_fit_record.append(fym)
xH2O_real = (xH2O[-1] - 0.2) / 0.8 * (0.9 - 0.1) + 0.1
SCCM_max = (fym - 0.2) / 0.8 * (550 - 350) + 350
#输出最优解
print('最大流量SCCM:', SCCM_max)
print('此时水的摩尔分数:', xH2O_real)
print('此时最小热量Q_min:', Q_MIN_EPOCH[-1])
fig = plt.figure()
plt.subplot(221)
plt.plot(max_fit_record, label='generation')
plt.legend() # 用于给图像加图例。
plt.xlabel('epoch')
plt.ylabel('max_fitness')
plt.subplot(222)
plt.plot(Q_MIN_EPOCH, label='Q_real_min')
plt.legend() # 用于给图像加图例。
plt.xlabel('epoch')
plt.ylabel('Q_real_min')
plt.subplot(223)
plt.plot(xH2O, label='xH2O')
plt.legend() # 用于给图像加图例。
plt.xlabel('epoch')
plt.ylabel('xH2O')
plt.show()
粒子群算法的实现思路比较简单,不需要编码、交叉、变异,相对遗传算法代码量会更少,不涉及惩罚函数、神经网络模型直接看主函数部分就好。
if __name__ == "__main__":
PSO = np.random.uniform(x_limit[0], x_limit[1], (1, DIM)) #粒子位置初始化
VEL = np.random.uniform(v_limit[0], v_limit[1], (1, DIM)) #粒子速度初始化
max_fit_record = [] #记录最大值
Q_MIN_EPOCH = [] #记录最小热量
xH2O = [] #记录对应的水的摩尔分数
for i in range(SIZE - 1):
pso = np.random.uniform(x_limit[0], x_limit[1], (1, DIM))
PSO = np.concatenate((PSO, pso), axis=0)
vel = np.random.uniform(v_limit[0], v_limit[1], (1, DIM))
VEL = np.concatenate((VEL, vel), axis=0)
x_max = PSO #每个个体的历史最佳位置
y_max = np.zeros(DIM) #种群的历史最佳位置
fxm = np.zeros((SIZE, 1)) #每个个体的历史最佳适应度
fym = 0 #种群历史最佳适应度
#群体更新
for i in range(N):
fx = get_fitness(PSO) #个体适应度
for j in range(SIZE):
if fxm[i] < fx[i]: #更新个体历史最佳适应度
fxm[i] = fx[i] #更新个体历史最佳位置
x_max[i, :] = PSO[i, :]
if fym < np.max(fxm):
fym = np.max(fxm)
nmax = np.argmax(fxm) #更新群体历史最佳适应度
y_max = x_max[nmax, :] #更新群体历史最佳位置
VEL = VEL * w + c1 * random.random() * (x_max - PSO) + c2 * random.random() * (np.tile(y_max, (SIZE, 1)) - PSO) #速度更新
VEL[VEL > v_limit[1]] = v_limit[1] #边界速度处理
VEL[VEL < v_limit[0]] = v_limit[0]
PSO[PSO > x_limit[1]] = x_limit[1]
PSO[PSO > x_limit[0]] = x_limit[0]
max_fit_record.append(fym)
#输出最优解
print('适应度:', fym)