粒子羣算法(particle swarm optimization,PSO)是計算智能領域中的一種生物啓發式方法,屬於羣體智能優化算法的一種,常見的羣體智能優化算法主要有以下幾類:
(1)蟻羣算法(Ant Colony Optimization,簡稱ACO)[1992年提出];
(2)粒子羣優化算法(Particle Swarm Optimization,簡稱PSO)[1995年提出](簡單易於實現,也是目前應用最爲普遍的羣體智能優化算法);
(3)菌羣優化算法(Bacterial Foraging Optimization,簡稱BFO)[2002年提出];
(4)蛙跳算法(Shuffled Frog Leading Algorithm,簡稱SFLA)[2003年提出];
(5)人工蜂羣算法(Artificial Bee Colony Algorithm,簡稱ABC)[2005年提出];
除了上述幾種常見的羣體智能算法之外,還有一些並非普遍應用的羣體智能算法,好比螢火蟲算法、布穀鳥算法、蝙蝠算法以及磷蝦羣算法等等。python
而其中的粒子羣優化算法(PSO)源於對鳥類捕食行爲的研究,鳥類捕食時,找到食物最簡單有限的策略就是搜尋當前距離食物最近的鳥的周圍。算法
舉個通俗的例子:dom
一羣鳥在尋找食物,在這個區域內有一個食物,全部的鳥都不知道食物在哪裏,但食物發出了香味,鳥經過香味的濃烈能判斷出本身的當前位置距離食物有多遠,同時鳥羣之間是能夠交流並告知離食物最近的鳥的位置的。試想一下這個時候會發生什麼?ide
鳥甲:哈哈哈,原來我離食物最近!函數
鳥乙、丙、丁…:我得趕忙往鳥甲那裏過去看看!學習
同時各自鳥在位置不停變化的時候,離食物的距離也不斷的變化,因此必定有過離食物最近的位置,這是他們的一個參考。優化
鳥某某:我剛纔的位置好像離食物很近了,我得往那裏再靠近點!spa
經過這樣一個過程,不斷的變化,最終鳥羣會向食物方向彙集,達到目標。在這個變化的過程當中,影響鳥的運動狀態變化的有兩個因素:3d
而鳥的每次的位置變化,除了考慮以上的兩個因素還有一個因素就是慣性。而對位置的變化,經過變化量(速度)來表示。code
經過以上的例子,咱們能夠把鳥抽象爲沒有質量和體積的微粒(點),並延伸到N維空間,粒子I 在N維空間的位置表示爲矢量Xi=(x1,x2,…,xN),飛行速度表示爲矢量Vi=(v1,v2,…,vN).每一個粒子都有一個由目標函數決定的適應值(fitness value),而且知道本身到目前爲止發現的最好位置(pbest)和如今的位置Xi.這個能夠看做是粒子本身的飛行經驗.除此以外,每一個粒子還知道到目前爲止整個羣體中全部粒子發現的最好位置(gbest)(gbest是pbest中的最好值).這個能夠看做是粒子同伴的經驗.粒子就是經過本身的經驗和同伴中最好的經驗來決定下一步的運動。歸結爲下面的兩個公式:
(公式-1)
(公式-2)
其中,爲慣性權重因子,(什麼是權重因子,這個後面會講到,此處大概知道有這麼一個參數便可);d=1,2,…,D;i=1,2,…,n;k爲當前迭代次數;Vid爲粒子的速度;c1和c2是非負的常數,稱爲加速因子;r1和r2是分佈於[0,1]區間的隨機數。爲了防止粒子的滿目搜索,通常建議限制位置和速度在必定的區間。
假設有以下非線性函數,須要在必定範圍內找出最大值和最大值的位置,本文用Python實現。
(公式-3)
咱們先經過python把上面函數使用matplotlib把它畫處理,讓咱們有個直觀的感覺。
# -*- coding: utf-8 -*- from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = Axes3D(fig) X = np.arange(-2, 2, 0.05) Y = np.arange(-2, 2, 0.05) X, Y = np.meshgrid(X, Y) Z = np.sin(np.sqrt(X**2+Y**2))/np.sqrt(X**2+Y**2)+np.exp((np.cos(2*np.pi*X)+np.cos(2*np.pi*Y))/2)-2.71289 ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='hot') plt.show()
此處使用mpl_toolkits.mplot3d畫3D圖,在代碼中,分別使用numpy中的arange建立等差長度爲0.05的數據點向量,區間爲[-2,2]之間,而後使用meshgrid將向量轉換給對應的矩陣,這樣的話,後面的函數式中就可使用np中的三角函數和數學函數了,若是不這樣的話,還得一個一個點的根據公式去作循環,拼接Z的向量點。
運行出來的圖以下:
從函數圖形中能夠看出,這個函數有不少的局部極大值,而真正的極限位置就是在(0,0),極大值爲1.005。下面就經過粒子羣算法來找尋這個極大值和極大值的位置。根據粒子羣算法函數求極值的算法流程以下圖所示:
在pycham做爲python的IDE中,創建一個名爲PSO.py的文件,文件中編寫一個名PSO的類。
設置慣性參數爲常數1,也就是公式-1中的爲常數1;設置加速度因子爲非負的兩個常數,公式-1的中的c1和c2;設置迭代次數爲300;設置粒子羣的規模爲20;爲了防止粒子的盲目搜索,將位置和速度限制在區間[-2,2]、[-0.5,0.5];
具體代碼以下:
def __init__(self): self.w = self.getweight() self.lr = self.getlearningrate() self.maxgen = self.getmaxgen() self.sizepop = self.getsizepop() self.rangepop = self.getrangepop() self.rangespeed = self.getrangespeed() def getweight(self): # 慣性權重 weight = 1 return weight def getlearningrate(self): # 分別是粒子的個體和社會的學習因子,也稱爲加速常數 lr = (0.49445,1.49445) return lr def getmaxgen(self): # 最大迭代次數 maxgen = 300 return maxgen def getsizepop(self): # 種羣規模 sizepop = 20 return sizepop def getrangepop(self): # 粒子的位置的範圍限制,x、y方向的限制相同 rangepop = (-2,2) return rangepop def getrangespeed(self): # 粒子的速度範圍限制 rangespeed = (-0.5,0.5) return rangespeed
隨機初始化粒子位置和粒子的速度,並根據適應度函數計算粒子適應度值,咱們的適應度函數就是公式-3這個函數。
首先定義一個適應度函數
def func(self,x): # x輸入粒子位置 # y 粒子適應度值 if (x[0]==0)&(x[1]==0): y = np.exp((np.cos(2*np.pi*x[0])+np.cos(2*np.pi*x[1]))/2)-2.71289 else: y = np.sin(np.sqrt(x[0]**2+x[1]**2))/np.sqrt(x[0]**2+x[1]**2)+np.exp((np.cos(2*np.pi*x[0])+np.cos(2*np.pi*x[1]))/2)-2.71289 return y
而後初始化粒子和計算適應度值
def initpopvfit(self,sizepop): pop = np.zeros((sizepop,2)) v = np.zeros((sizepop,2)) fitness = np.zeros(sizepop) for i in range(sizepop): pop[i] = [(np.random.rand()-0.5)*self.rangepop[0]*2,(np.random.rand()-0.5)*self.rangepop[1]*2] v[i] = [(np.random.rand()-0.5)*self.rangepop[0]*2,(np.random.rand()-0.5)*self.rangepop[1]*2] fitness[i] = self.func(pop[i]) return pop,v,fitness
def getinitbest(self,fitness,pop): # 羣體最優的粒子位置及其適應度值 gbestpop,gbestfitness = pop[fitness.argmax()].copy(),fitness.max() #個體最優的粒子位置及其適應度值,使用copy()使得對pop的改變不影響pbestpop,pbestfitness相似 pbestpop,pbestfitness = pop.copy(),fitness.copy() return gbestpop,gbestfitness,pbestpop,pbestfitness
此處須要分別找到羣體極值和個體粒子的極值。
經過循環迭代,不斷的更新粒子的位置和速度,根據新粒子的適應度值更新個體和羣體的極值。這部分的代碼以下:
def run(self): pop, v, fitness = self.initpopvfit(self.sizepop) gbestpop, gbestfitness, pbestpop, pbestfitness = self.getinitbest(fitness, pop) result = np.zeros(self.maxgen) for i in range(self.maxgen): # 速度更新 for j in range(self.sizepop): v[j] += self.lr[0] * np.random.rand() * (pbestpop[j] - pop[j]) + self.lr[1] * np.random.rand() * ( gbestpop - pop[j]) v[v < self.rangespeed[0]] = self.rangespeed[0] v[v > self.rangespeed[1]] = self.rangespeed[1] # 粒子位置更新 for j in range(self.sizepop): pop[j] += 0.5 * v[j] pop[pop < self.rangepop[0]] = self.rangepop[0] pop[pop > self.rangepop[1]] = self.rangepop[1] # 適應度更新 for j in range(self.sizepop): fitness[j] = self.func(pop[j]) for j in range(self.sizepop): if fitness[j] > pbestfitness[j]: pbestfitness[j] = fitness[j] pbestpop[j] = pop[j].copy() if pbestfitness.max() > gbestfitness: gbestfitness = pbestfitness.max() gbestpop = pop[pbestfitness.argmax()].copy() result[i] = gbestfitness return result
經過在主函數文件中,實例化PSO類,調用run這個函數,運行上面的代碼
from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt import numpy as np from PSO import * pso = PSO() result = pso.run() plt.figure() plt.plot(result) plt.show()
當迭代了300次以後,畫出每代最優個體適應度值的曲線,結果以下:
最終獲得的最優個體適應度值爲:1.00538866933,對應的粒子位置爲:[ 2.37513360e-05 3.41265823e-04],經過比較,PSO算法能獲得最優值,接近函數實際的最優值1.005。
經過以上,證實PSO算法具備較強的函數極值尋優能力。
講到這裏,咱們再來回顧一下以前粒子更新速度(公式-1)中的這個慣性權重因子,所謂慣性權重因子,體現的是粒子繼承上一次迭代速度的能力。較大的一個慣性權重因子有利於全局的搜索,而一個小的慣性權重因子則更加利於局部的搜索。爲了更好的平衡算法的全局搜索與局部搜索的能力,常給慣性權重因子改成隨迭代次數變化的一個函數,通常經常使用的慣性權重因子函數有如下幾種:
(公式-4)
這幾種的變化圖以下:
從變化圖中,能夠看到(公式-5)中,前期變化較慢,取值較大,維持了算法的全局搜索能力;後期
變化較快,極大的提升了算法的局部尋優能力,經過這種
權重的變化函數,能夠取得比
固定不變的那種狀況要好的效果。