[Python] 粒子群優化 Particle Swarm Optimization

程式語言:Python
Package:pyswarm
pyswarm 官網

功能:最佳化參數演算法

演算法

  1. 隨機初始化粒子的位置與速率
  2. 計算初始粒子的 fitness
  3. While (直到滿足 threshold)
    1. 更新粒子速度與位置
      vit=wvit1+wprand()(pixit1)+wgrand()(gxit1)xit=xit1+vit
      • vi:粒子的速度
      • xi:粒子的位置
      • w:慣性速度權重
      • wp:本身最佳解速度權重
      • pi:本身最佳解位置
      • wg:全體最佳解速度權重
      • g :全體最佳解位置
    2. 更新粒子的 fitness
注意:fitness function 會影響整體的表現,需考慮清楚
概念來自於鳥群覓食
一群鳥在一個廣大的區域中,想找到最多食物的地方
於是大家都帶著手機出門去,一個留守在基地
一開始,大家先隨機分散出去找,然後用手機回報基地,更新自己所找到最多食物的地方
基地也會記錄目前找到最多食物的地方
所以每隻鳥都知道,自己曾經找到最多食物的地方,跟全體找到最多食物的地方
利用這兩個資訊,更新目前本身的飛行速度
慢慢地,大家會集合到同樣的位置上去,而此位置便可能是最多食物的地方

演算法程式碼

基本 PSO 與 pyswarm
  1. import numpy as np
  2. from pyswarm import pso
  3.  
  4. class PSO:
  5. pBest = []
  6. pBestFitness = []
  7. gBest = None
  8. gBestFitness = np.inf
  9. # 初始化
  10. def __init__(self, fitness, bounds, swarmSize=100, w=0.5, wp=0.5, wg=0.5):
  11. '''
  12. v = w*v + wp*(pBest-x) + wg*(gBest-x)
  13. '''
  14. self.swarmSize = swarmSize
  15. self.fitness = fitness
  16. self.pNum = len(bounds)
  17. self.bounds = bounds
  18. self.w = w
  19. self.wp = wp
  20. self.wg = wg
  21. # 初始化粒子和速度
  22. self.particles = np.zeros((self.swarmSize, self.pNum))
  23. self.v = np.zeros((self.swarmSize, self.pNum))
  24. for i, b in enumerate(self.bounds):
  25. self.particles[:,i] = np.random.uniform(b[0], b[1], self.swarmSize)
  26. self.v[:,i] = np.random.uniform(-b[1]+b[0], b[1]-b[0], self.swarmSize)
  27.  
  28. # 初始化 fitness
  29. self.pBest = np.zeros((self.swarmSize, self.pNum))
  30. self.pBestFitness = np.ones(self.swarmSize) * np.inf
  31. self.updateFitness()
  32. # 更新 fitness
  33. def updateFitness(self):
  34. for i, p in enumerate(self.particles):
  35. fit = self.fitness(p)
  36. if fit < self.pBestFitness[i]:
  37. self.pBest[i] = p
  38. self.pBestFitness[i] = fit
  39.  
  40. if fit < self.gBestFitness:
  41. self.gBest = p
  42. self.gBestFitness = fit
  43. def run(self, threshold=0.01, updateThreshold=1e-4, maxiter=20):
  44. n = 0
  45. while self.gBestFitness > threshold and n < maxiter:
  46. # 更新粒子速度
  47. rp = np.random.rand()
  48. rg = np.random.rand()
  49. self.v = self.w*self.v + self.wp*rp*(self.pBest-self.particles) + self.wg*rg*(self.gBest-self.particles)
  50. # 更新粒子
  51. self.particles = self.particles + self.v
  52. for i, b in enumerate(self.bounds):
  53. self.particles[:,i] = np.clip(self.particles[:,i], b[0], b[1])
  54. # 計算 fitness
  55. old = self.gBestFitness
  56. self.updateFitness()
  57. # 若更新小於 0.001,持續 maxiter 次,則中止
  58. if old - self.gBestFitness < 0.001:
  59. n += 1
  60. else:
  61. n = 0
  62. if n > maxiter:
  63. break
  64. return self.gBest, self.gBestFitness
  65. if __name__ == "__main__":
  66. def fitness(x):
  67. return (x[0]-5)**2 + (x[1]-10)**2 + (x[2]+10)**2
  68.  
  69. xopt, fopt = PSO(fitness, [(-1e10,1e10), (-1e10,1e10), (-1e10,1e10)]).run(threshold=1e-6)
  70. print("自製:{} {}".format(fopt, xopt))
  71. xopt, fopt = pso(func=fitness, lb=(-1e10,-1e10,-1e10), ub=(1e10,1e10,1e10))
  72. print("套件:{} {}".format(fopt, xopt))
PSO + 定期隨機
  1. import numpy as np
  2.  
  3. class PSO:
  4. pBest = []
  5. pBestFitness = []
  6. gBest = None
  7. gBestFitness = np.inf
  8. # 初始化
  9. def __init__(self, fitness, bounds, swarmSize=100, w=0.5, wp=0.5, wg=0.5):
  10. '''
  11. v = w*v + wp*(pBest-x) + wg*(gBest-x)
  12. '''
  13. self.swarmSize = swarmSize
  14. self.fitness = fitness
  15. self.pNum = len(bounds)
  16. self.bounds = bounds
  17. self.w = w
  18. self.wp = wp
  19. self.wg = wg
  20. # 初始化粒子和速度
  21. self.init_particles()
  22.  
  23. # 初始化 fitness
  24. self.pBest = np.zeros((self.swarmSize, self.pNum))
  25. self.pBestFitness = np.ones(self.swarmSize) * np.inf
  26. self.updateFitness()
  27. # 初始化粒子
  28. def init_particles(self):
  29. self.particles = np.zeros((self.swarmSize, self.pNum))
  30. self.v = np.zeros((self.swarmSize, self.pNum))
  31. for i, b in enumerate(self.bounds):
  32. self.particles[:,i] = np.random.uniform(b[0], b[1], self.swarmSize)
  33. self.v[:,i] = np.random.uniform(-b[1]+b[0], b[1]-b[0], self.swarmSize)
  34. # 增加粒子
  35. def produce_particles(self, addSize):
  36. self.swarmSize += addSize
  37. ps = np.zeros((addSize, self.pNum))
  38. vs = np.zeros((addSize, self.pNum))
  39. pbests = np.zeros((addSize, self.pNum))
  40. pBestFitness = np.ones(addSize) * np.inf
  41. for i, b in enumerate(self.bounds):
  42. ps[:,i] = np.random.uniform(b[0], b[1], addSize)
  43. vs[:,i] = np.random.uniform(-b[1]+b[0], b[1]-b[0], addSize)
  44. self.particles = np.concatenate((ps, self.particles))
  45. self.v = np.concatenate((vs, self.v))
  46. self.pBest = np.concatenate((pbests, self.pBest))
  47. self.pBestFitness = np.concatenate((pBestFitness, self.pBestFitness))
  48. # 取代粒子
  49. def replace_particles(self, replaceSize):
  50. # 重新排序
  51. index = np.argsort(self.pBestFitness)[::-1]
  52. self.particles = self.particles[index]
  53. self.v = self.v[index]
  54. self.pBest = self.pBest[index]
  55. self.pBestFitness = self.pBestFitness[index]
  56. ps = np.zeros((replaceSize, self.pNum))
  57. vs = np.zeros((replaceSize, self.pNum))
  58. pbests = np.zeros((replaceSize, self.pNum))
  59. pBestFitness = np.ones(replaceSize) * np.inf
  60. for i, b in enumerate(self.bounds):
  61. ps[:,i] = np.random.uniform(b[0], b[1], replaceSize)
  62. vs[:,i] = np.random.uniform(-b[1]+b[0], b[1]-b[0], replaceSize)
  63.  
  64. self.particles[:replaceSize] = ps
  65. self.v[:replaceSize] = vs
  66. self.pBest[:replaceSize] = pbests
  67. self.pBestFitness[:replaceSize] = pBestFitness
  68. # 更新 fitness
  69. def updateFitness(self):
  70. for i, p in enumerate(self.particles):
  71. fit = self.fitness(p)
  72. if fit < self.pBestFitness[i]:
  73. self.pBest[i] = p
  74. self.pBestFitness[i] = fit
  75.  
  76. if fit < self.gBestFitness:
  77. self.gBest = p
  78. self.gBestFitness = fit
  79. def run(self, threshold=0.01, maxiter=100, randIter=20):
  80. n = 0
  81. try:
  82. while self.gBestFitness > threshold and n < maxiter:
  83. # 更新粒子速度
  84. rp = np.random.rand()
  85. rg = np.random.rand()
  86. self.v = self.w*self.v + self.wp*rp*(self.pBest-self.particles) + self.wg*rg*(self.gBest-self.particles)
  87. # 更新粒子
  88. self.particles = self.particles + self.v
  89. for i, b in enumerate(self.bounds):
  90. self.particles[:,i] = np.clip(self.particles[:,i], b[0], b[1])
  91. # 計算 fitness
  92. old = self.gBestFitness
  93. self.updateFitness()
  94. # print(self.gBestFitness)
  95. if old - self.gBestFitness < threshold/10:
  96. n += 1
  97. if n % randIter == 0:
  98. self.replace_particles(self.swarmSize//3*2)
  99. # print('replace, now Size: {}'.format(self.swarmSize))
  100. self.produce_particles(self.swarmSize//10)
  101. # print('produce, now Size: {}'.format(self.swarmSize))
  102. else:
  103. n = 0
  104. if n > maxiter:
  105. break
  106. print(n)
  107. except KeyboardInterrupt:
  108. print("Interrupt by user")
  109. return self.gBest, self.gBestFitness
  110. if __name__ == "__main__":
  111. def fitness(x):
  112. return (x[0]-5)**2 + (x[1]-10)**2 + (x[2]+10)**2
  113.  
  114. pso = PSO(fitness, [(-1e10,1e10), (-1e10,1e10), (-1e10,1e10)])
  115. print(pso.run(threshold=1e-6))

參考

[pso] 初步 - 粒子移動演算法精髓
Particle swarm optimization
PSO 粒子群演算法

留言