1、
學習代碼源自:用python3實現粒子羣優化算法(PSO)-by雨破塵
參考博文:[MATLAB] 經典智能算法1:粒子羣優化算法PSO
2、算法思想
粒子運動思想(即鳥羣捕食運動),同樣是通過適應度來評價解好壞。區域裏只有一塊食物,每一隻鳥並不知道食物在哪裏,只知道距離食物有多遠,最簡單有效的就是搜尋離食物最近的鳥的周圍區域。這裏,每個優化問題的解都是搜索空間中的一隻鳥(粒子),所有的粒子都有一個由被優化的函數決定的適應值,每個粒子還有一個速度決定他們飛翔的方向和距離。然後粒子們就追隨當前的最優粒子在解空間中搜索。
所有的粒子具有以下兩個屬性:速度、位置,其迭代公式爲:
速度更新公式:
爲第k+1時刻,第i個粒子的速度,爲慣性權重, C1和C2分別爲局部搜索和全局搜索的能力常量。
位置更新公式:
爲第i個粒子k時刻的位置,分別爲當前最優解和全局最優解。
a和b爲0~1之間的隨機值。
適應度公式:
適應度函數跟想要實現什麼功能有關,把粒子對應成你問題的候選解,適應度函數用來評價給出的這個候選解(粒子)的好壞(好壞的評價標準需要一個量化指標,也就是,粒子的適應度值)。比如你想求出N維空間中離原點最近的點(答案當然是原點,現在假設不知道答案) 設定粒子維數爲N,表示候選解。 那適應度函數值可以表示爲 f = x1^2 + x2^2 +..................xN^2 只要選擇適應度函數值最小的那組解救可以了。
算法流程圖如下:
PSO.py
import numpy as np
import random
def fit_fun(X): # 適應函數
return -np.abs(np.sin(X[0]) * np.cos(X[1]) * np.exp(np.abs(1 - np.sqrt(X[0] ** 2 + X[1] ** 2) / np.pi)))
#構建適應度函數,求解函數爲Holder函數:sin(x)*cos(y)*e(|1-sqrt(x**2+y**2)/2|)
class Particle:
# 初始化
def __init__(self, x_max, max_vel, dim):
self.__pos = [random.uniform(-x_max, x_max) for i in range(dim)] # 粒子在i維空間的位置
# 粒子的位置,隨機賦予在區間(-x_max, x_max)初值
self.__vel = [random.uniform(-max_vel, max_vel) for i in range(dim)] # 粒子在i維空間的速度
# 粒子的速度,隨機賦予在區間(-x_max, x_max)初值
self.__bestPos = [0.0 for i in range(dim)]
# 粒子在i個維度上最好的位置,將每個方向的粒子都賦予0.0
self.__fitnessValue = fit_fun(self.__pos) # 適應度函數值
def set_pos(self, i, value):
self.__pos[i] = value #位置/解
def get_pos(self): #返回位置/解
return self.__pos
def set_best_pos(self, i, value):#粒子最優位置/解
self.__bestPos[i] = value
def get_best_pos(self):
return self.__bestPos#返回粒子位置/解
def set_vel(self, i, value):#速度
self.__vel[i] = value
def get_vel(self):
return self.__vel#返回速度
def set_fitness_value(self, value):
self.__fitnessValue = value #粒子適應度
def get_fitness_value(self):
return self.__fitnessValue #返回粒子適應度
class PSO:
def __init__(self, dim, size, iter_num, x_max, max_vel, best_fitness_value=float('Inf'), C1=2, C2=2, W=1, Gamma=1.4):
self.C1 = C1
self.C2 = C2
self.Gamma= Gamma #調整迭代的速度
self.W = W #慣性權重
self.dim = dim # 粒子的維度
self.size = size # 粒子個數
self.iter_num = iter_num # 迭代次數
self.x_max = x_max #最大解/位置
self.max_vel = max_vel # 粒子最大速度
self.best_fitness_value = best_fitness_value
self.best_position = [0.0 for i in range(dim)] # 種羣最優位置
self.fitness_val_list = [] # 每次迭代最優適應值
# 對粒子羣所有粒子參數進行初始化
self.Particle_list = [Particle(self.x_max, self.max_vel, self.dim) for i in range(self.size)]
def set_bestFitnessValue(self, value):
self.best_fitness_value = value
def get_bestFitnessValue(self):
return self.best_fitness_value
# 返回粒子羣最優適應度,即本代最優粒子適應度
def set_bestPosition(self, i, value):
self.best_position[i] = value
# 更新本代粒子羣最優位置,令本代最優粒子的位置/解爲本代粒子羣最優位置
def get_bestPosition(self):
return self.best_position
# 返回粒子羣最優位置,即本代最優粒子的位置/解
# 更新粒子速度
def update_vel(self, part):
for i in range(self.dim):#更新粒子第i維的速度
vel_value = self.W * part.get_vel()[i] + self.C1 * random.random() * (part.get_best_pos()[i] - part.get_pos()[i]) \
+ self.C2 * random.random() * (self.get_bestPosition()[i] - part.get_pos()[i])
#權重*上代粒子第i維度的速度+c1*random*(粒子上代在i維度的最優位置-粒子當前在第i維度的最位置)
# +c2*random*(上代粒子羣在i維的最優位置-粒子當前在第i維度的最位置)
if vel_value > self.max_vel:
vel_value = self.max_vel #限速
elif vel_value < -self.max_vel:
vel_value = -self.max_vel
part.set_vel(i, vel_value) #在各個維度更新位置
# 更新粒子的位置
def update_pos(self, part):
for i in range(self.dim): #更新粒子第i維度的位置
pos_value = part.get_pos()[i] + self.Gamma*part.get_vel()[i]
part.set_pos(i, pos_value)#位置更新公式
value = fit_fun(part.get_pos())#更新粒子解的適應度
if value < part.get_fitness_value():#如果粒子的適應度小於上代粒子適應度
part.set_fitness_value(value) #則保留
for i in range(self.dim):
part.set_best_pos(i, part.get_pos()[i])#將i維中的最優位置作爲粒子的最優位置
#如果粒子的適應度大於上代粒子適應度則拋棄
if value < self.get_bestFitnessValue():
#如果粒子的適應度優於於粒子羣全局適應度,則保留
self.set_bestFitnessValue(value)
for i in range(self.dim):
self.set_bestPosition(i, part.get_pos()[i])#更新粒子羣最優位置
def update(self): #更新粒子羣
for i in range(self.iter_num):
for part in self.Particle_list:
self.update_vel(part) # 更新速度
self.update_pos(part) # 更新位置
self.fitness_val_list.append(self.get_bestFitnessValue()) # 每次迭代完把當前的最優適應度存到列表/即,把最優函數極值保存
return self.fitness_val_list, self.get_bestPosition()
test.py
import PSO
import matplotlib.pyplot as plt
import numpy as np
dim = 2
size = 20
iter_num = 1000
x_max = 10
max_vel = 0.5
pso = PSO.PSO(dim, size, iter_num, x_max, max_vel,) #初始化
fit_var_list, best_pos = pso.update() #執行PSO
print("最優解:" + str(best_pos))
print("最優值:" + str(fit_var_list[-1]))
plt.plot(np.linspace(0, iter_num, iter_num), fit_var_list, c="r", alpha=0.5)
plt.show()
程序中,我們採用維度這一概念。維度的本質其實就是模擬粒子的方向,即對當前粒子的多個方向進行多次計算。以多次重複計算方法實現對多個方向尋解的方法。
經過測試發現,比較容易陷入局部最優解:
f(8.0976253838002, 6.480369504941845)=-9.504673324949687;
f(-8.1004040728734, -6.47981022046396)=-9.504636698476002
f(8.09545161260645, -6.48036019465368)=-9.504651029513745
f(-8.09993902133437, 6.47538746374264)=-9.504568797304843
f(-1.62251583841027, -9.7292577958573)=-8.095120892624823
f(1.620608971827874, -9.7290992535129)=-8.095120892624823
等~~~