PSO粒子羣算法(python3.6實現與詳解)

1、

學習代碼源自:用python3實現粒子羣優化算法(PSO)-by雨破塵

參考博文:[MATLAB] 經典智能算法1:粒子羣優化算法PSO 

                  python粒子羣算法的實現 by-winycg    此篇強推

                  粒子羣優化算法(PSO) by森先生

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

等~~~

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章