遗传算法与深度学习实战(12)——粒子群优化详解与实现
0. 前言
粒子群优化 (Particle Swarm Optimization
, PSO
) 是一种借鉴适者生存和群集行为概念的进化计算方法。在本节中,我们使用 PSO
来近似求解函数所需的最优参数,展示 PSO
在解决参数函数输入上的强大能力。
1. 粒子群优化
1.1 粒子群优化原理
粒子群优化 (Particle Swarm Optimization
, PSO
) 是从单个生物的自然分组中获得启发,例如鸟群或鱼群(通常称为群)。生物体在群内相互作用而无需中央监督,朝着共同目标努力。这种观察到的行为产生了一种计算方法,该方法可以通过使用一组由类似于群内生物的粒子表示的候选解决方案来解决或优化给定问题。粒子在搜索空间中移动,以寻找最佳解决方案,它们的移动由涉及其位置和速度(方向速度)的简单规则控制。
1.2 算法流程
PSO
算法是迭代的,并且在每次迭代中,将评估每个粒子的位置,并在必要时更新其到目前为止的最佳位置以及整个粒子组中的最佳位置。每个粒子的速度根据以下信息更新:
- 粒子的当前速度和运动方向
- 迄今为止找到的粒子的最佳位置(局部最佳)
- 迄今为止,整个群中的最佳位置(全局最佳)
- 然后,根据新计算的速度更新粒子的位置
迭代过程一直进行到满足某些停止条件(例如迭代限制)为止。此时,算法将组的当前最佳位置作为解决方案。
2. 实现 PSO 解决方程
2.1 问题描述
粒子群优化 (Particle Swarm Optimization
, PSO
) 的常见应用是解决给定方程或函数中所需的输入参数。例如,如果我们想要将炮弹射出指定的距离,可以利用以下方程:
其中,
v
i
v_i
vi 表示炮弹射速,
a
a
a 表示炮弹角度。我们可以尝试使用多种数学和优化方法来解决以上方程,但本节中,我们将使用 PSO
求解所需的炮弹初始速度和发射角度的最佳值。
2.2 代码实现
(1) 首先,导入所需库:
import math
import time
import numpy as np
from deap import base
from deap import benchmarks
from deap import creator
from deap import tools
from IPython.display import clear_output
(2) 定义 generate
运算符和同名函数,generate()
函数创建了一个粒子数组,其起始位置由 pmin
和 pmax
定义。在粒子群优化期间,每个粒子都有一个初始的速度或距离,并可以在优化过程中更新。在更新过程中,粒子根据适应度移动到新的位置:
def generate(size, pmin, pmax, smin, smax):
part = creator.Particle(np.random.uniform(pmin, pmax, size))
part.speed = np.random.uniform(smin, smax, size)
part.smin = smin
part.smax = smax
return part
(3) 定义更新运算符函数 updateParticle()
,该函数负责在群优化的每次迭代的过程中更新粒子的位置。PSO
的核心是不断地将粒子集群在适应度最高的粒子周围。在 updateParticle()
函数中,粒子通过改变速度和位置来进行集群:
def updateParticle(part, best, phi1, phi2):
u1 = np.random.uniform(0, phi1, len(part))
u2 = np.random.uniform(0, phi2, len(part))
v_u1 = u1 * (part.best - part)
v_u2 = u2 * (best - part)
part.speed += v_u1 + v_u2
for i, speed in enumerate(part.speed):
if abs(speed) < part.smin:
part.speed[i] = math.copysign(part.smin, speed)
elif abs(speed) > part.smax:
part.speed[i] = math.copysign(part.smax, speed)
part += part.speed
下图展示了 PSO
如何将各种粒子集群在优化目标区域周围。如果我们将角度和速度绘制在图上,则可以将每个粒子视为射击炮弹的尝试。PSO
的目标是找到最优参数(速度和角度),以将炮弹射出目标距离。
(4) 定义注册到 toolbox
的评估函数 evaluate()
。首先,定义目标距离,由于方程式中的速度项包含平方运算,我们只允许正值,防止出现负值。同样地,我们假设角度以度为单位,然后将其转换为弧度用于方程中。最后,我们使用方程计算距离,并计算其与目标距离的差值。然后,返回差值的平方值,以将平方误差项的总和作为元组从函数中返回:
distance = 575 #@param {type:"slider", min:10, max:1000, step:5}
def evaluate(individual):
v = individual[0] if individual[0] > 0 else 0 #velocity
a = individual[1] * math.pi / 180 #angle to radians
return ((2*v**2 * math.sin(a) * math.cos(a))/9.8 - distance)**2,
设置toolbox,注册关键函数,用于生成、更新和评估粒子。
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
creator.create("Particle", np.ndarray, fitness=creator.FitnessMax, speed=list,
smin=None, smax=None, best=None)
toolbox = base.Toolbox()
toolbox.register("particle",
generate, size=2, pmin=-6, pmax=6, smin=-3, smax=3)
toolbox.register("population",
tools.initRepeat, list, toolbox.particle)
toolbox.register("update",
updateParticle, phi1=200, phi2=200)
toolbox.register("evaluate", evaluate)
(5) 基本操作设置完成后,继续编写粒子群代码,相比之前介绍的其他遗传算法或遗传编程示例要简单得多。与遗传算法或遗传编程不同,在粒子群优化中,粒子在整个模拟中存在。由于粒子寿命较长,我们可以跟踪每个粒子的最佳适应度值。跟踪每个粒子的最佳适应度允许当前最佳粒子在代码清单中被交换,用 best
表示。在群集中重新定位粒子的地方调用 toolbox.update
,根据最佳粒子的位置,使用 updateParticle()
函数:
import matplotlib.pyplot as plt
def plot_population(pop):
xs = [x for x,_ in pop]
ys = [y for _,y in pop]
plt.scatter(xs,ys)
plt.show()
pop = toolbox.population(n=500)
stats = tools.Statistics(lambda ind: ind.fitness.values)
stats.register("avg", np.mean)
stats.register("std", np.std)
stats.register("min", np.min)
stats.register("max", np.max)
logbook = tools.Logbook()
logbook.header = ["gen", "evals"] + stats.fields
GEN = 100
best = None
for g in range(GEN):
for part in pop:
part.fitness.values = tuple(np.subtract((0,), toolbox.evaluate(part)))
if part.best is None or part.best.fitness < part.fitness:
part.best = creator.Particle(part)
part.best.fitness.values = part.fitness.values
if best is None or best.fitness < part.fitness:
best = creator.Particle(part)
best.fitness.values = part.fitness.values
for part in pop:
toolbox.update(part, best)
if (g+1) % 10 == 0:
logbook.record(gen=g, evals=len(pop), **stats.compile(pop))
clear_output()
print(best)
plot_population(pop)
print(logbook.stream)
time.sleep(1)
随着模拟的运行,可以看到粒子开始收敛到最佳或多个最佳解,如下图所示。需要注意的是,此问题中的角度和速度有两个正确的解,同时粒子在解的一定距离范围分布。这是由于选择了超参数 pmin
、pmax
、smin
、smax
、phi1
和 phi2
,可以通过调整这些值来改变粒子的分布。如果想让粒子的分布范围更小,需要将这些超参数调整为较小的值,然后重新运行。
(6) 最后,评估最佳粒子的预设解决方案。但由于该问题有两个解,我们有可能评估多个最佳解。从输出值中,可以看到 PSO
可以相对快速地近似解决炮弹射击给定距离的问题。
v, a = best
a = a * math.pi / 180 #angle to radians
distance = (2*v**2 * math.sin(a) * math.cos(a))/9.8
print(distance)
# 575.0449970299468
PSO
可以应用于多种其他问题,并具有不同的效果。群集优化是一种轻量级的方法,用于找到未知参数,PSO
可以对深度学习系统的超参数进行简单的优化。可以通过完成以下问题进一步理解粒子群算法:
- 修改目标距离,观察其对
PSO
解决方案的影响 - 修改
pmin
、pmax
、smin
和smax
输入,然后重新运行 - 修改
phi1
和phi2
参数,然后重新运行,观察其对PSO
解决方案的影响
小结
粒子群优化 (Particle Swarm Optimization
, PSO
) 使用一组个体在解决空间中移动,随着粒子的移动,适应度更高的个体引导粒子聚焦于更好的解决方案,PSO
可用于寻找函数或复杂问题的解决方案参数。在本节中,介绍了 PSO
算法的基本原理与算法流程,并实现 PSO
解决炮弹射程方程。
系列链接
遗传算法与深度学习实战(1)——进化深度学习
遗传算法与深度学习实战(2)——生命模拟及其应用
遗传算法与深度学习实战(3)——生命模拟与进化论
遗传算法与深度学习实战(4)——遗传算法详解与实现
遗传算法与深度学习实战(5)——遗传算法中常用遗传算子
遗传算法与深度学习实战(6)——遗传算法框架DEAP
遗传算法与深度学习实战(7)——DEAP框架初体验
遗传算法与深度学习实战(8)——使用遗传算法解决N皇后问题
遗传算法与深度学习实战(9)——使用遗传算法解决旅行商问题
遗传算法与深度学习实战(10)——使用遗传算法重建图像
遗传算法与深度学习实战(11)——遗传编程详解与实现