智能算法系列之粒子群优化算法

在这里插入图片描述

  本博客封面由ChatGPT + DALL·E 2共同创作而成。

前言

  本篇是智能算法(Python复现)专栏的第三篇文章,主要介绍粒子群优化算法(ParticleSwarm Optimization, PSO)的思想,python实现及相关应用场景模拟。

  粒子群优化算法,简称粒子群算法,也叫作鸟群觅食算法。PSO算法的基本思想受到许多对鸟类的群体行为(觅食行为)进行建模与仿真研究结果的启发,粒子在解空间中追随最优的粒子进行迭代搜索,而不需要像遗传算法那样使用交叉以及变异操作。

1. 算法思想

  PSO算法最初设想是模拟鸟群觅食的过程,想象这样一个场景:一群鸟随机的分布在一个区域,在这个区域只有一块食物,但是所有的鸟都不知道这块食物的具体方位,只知道自己当前的位置距离食物还有多远。找到食物最简单有效的方式就是搜索目前离食物最近的鸟的周围区域。如果把食物当作最优点,而把鸟离食物的距离当作函数的适应度,那么鸟寻觅食物的过程就可以当作函数寻优的过程。由此受到启发,经过简化提出了PSO算法。

  PSO算法作为一种仿生算法,目前还没有完备的数学理论作为基础,但是作为一种新兴的智能优化算法,已经在诸多领域展现了良好的应用前景。

  粒子群优化算法的核心思想是通过模拟鸟群或鱼群等动物的群体行为,以达到求解最优化问题的目的。在粒子群优化中,搜索空间中的每个解都被视为一个粒子,每个粒子的位置表示解的搜索空间中的位置,粒子的速度表示解的搜索方向和速度。在搜索过程中,每个粒子通过学习其他粒子的经验,更新自己的位置和速度。粒子的位置和速度的更新可以分为两个部分:局部搜索和全局搜索。局部搜索是指粒子在其自身经验的基础上进行搜索,全局搜索是指粒子在全局最优解的基础上进行搜索。具体来说,每个粒子会记忆其历史最优解和全局最优解,然后通过调整自己的速度和位置来寻找更好的解。
  粒子群优化算法的基本步骤如下:
  (1) 初始化粒子群:随机生成粒子群中每个粒子的位置和速度。
  (2) 计算每个粒子的适应度:根据粒子的位置计算适应度函数的值。
  (3) 更新每个粒子的历史最优解:将粒子自己的历史最优解更新为当前最优解。
  (4) 更新全局最优解:将整个粒子群的最优解更新为所有粒子历史最优解中最优的一个。
  (5) 更新每个粒子的速度和位置:根据当前粒子自己的历史最优解和整个粒子群的最优解,以及粒子的速度和位置,更新粒子的速度和位置。
  粒子的速度更新公式如下: v ( t + 1 ) = v ( t ) + c 1 r 1 ( t ) [ p b e s t ( t ) − x ( t ) ] + c 2 r 2 ( t ) [ g b e s t ( t ) − x ( t ) ] v(t+1) = v(t) + c_1r_1(t)[pbest(t) - x(t)] + c_2r_2(t)[gbest(t) - x(t)] v(t+1)=v(t)+c1r1(t)[pbest(t)x(t)]+c2r2(t)[gbest(t)x(t)]  其中 c 1 c_1 c1 c 2 c_2 c2表示学习因子,也叫加速因子,具体来说, c 1 c_1 c1用来调节粒子飞向自身最好位置方向的步长, c 2 c_2 c2用来调节粒子飞向全局最好位置方向的步长。 r 1 r_1 r1 r 2 r_2 r2用来保持群体的多样性。 p b e s t pbest pbest表示当前粒子迄今为止搜索到的最优位置, g b e s t gbest gbest为整个粒子群迄今为止搜索到的最优位置。
  粒子的位置更新公式如下: x ( t + 1 ) = x ( t ) + v ( t + 1 ) x(t+1) = x(t) + v(t+1) x(t+1)=x(t)+v(t+1)

在这里插入图片描述

2. 细节梳理

2.1 超参数的选择

  PSO算法中没有实际的机制来控制粒子速度,值太大会导致粒子跳过最好解,太小又会导致对搜索空间的不充分搜索,所以有必要对速度的范围进行限制,一般可以根据搜索的位置范围进行调节,比如,本示例中的搜索范围为[0, 5],粒子的速度范围为[-1, 1]
   c 1 c_1 c1 c 2 c_2 c2这两个参数对粒子群算法的收敛起的作用不是很大,但是适当调整这两个参数,可以减小局部最小值的困扰,当然也会使收敛速度变快。
   r 1 r_1 r1 r 2 r_2 r2可以设置为[0,1]的随机数。

2.2 一些trick

  为了改善基本PSO算法的收敛性能,在更新粒子的速度时引入了惯性权重的概念,即: v ( t + 1 ) = w v ( t ) + c 1 r 1 ( t ) [ p b e s t ( t ) − x ( t ) ] + c 2 r 2 ( t ) [ g b e s t ( t ) − x ( t ) ] v(t+1) = wv(t) + c_1r_1(t)[pbest(t) - x(t)] + c_2r_2(t)[gbest(t) - x(t)] v(t+1)=wv(t)+c1r1(t)[pbest(t)x(t)]+c2r2(t)[gbest(t)x(t)]  其中, w w w称为惯性权重,基本PSO算法是惯性权重 w = 1 w=1 w=1的特殊情况。惯性权重 w w w使粒子保持飞行惯性,使其可以扩展搜索空间,有能力探索新的区域。

  带有惯性权重的粒子群优化算法是最常用的一种粒子群优化算法,本示例就是基于带有惯性权重的粒子群优化算法进行实现的。

  惯性权重 w w w的引入清除了基本PSO算法对速度最大值的需求,因为 w w w的作用就是保持全局和局部搜索能力的平衡。当速度最大值增加时,就人为减少 w w w来达到搜索的平衡,而 w w w的减少可降低需要的迭代次数,就可以将某一维速度的最大值锁定为每维变量的变化范围,只对 w w w进行调节。
  对全局搜索,广泛使用的方法是在前期利用较高的探索能力得到具有超高潜力且多样化的种子,而在后期提升开发能力以加快收敛速度,所以,也可以将 w w w设定为随进化迭代次数线性减少[在本示例中未实现该操作]

  除了上述的改进算法外,还有协同粒子群优化算法、量子粒子群优化算法(Qutantum-behaved Particle SwarmOptimization,QPSO)和混合粒子群优化算法。其中混合粒子群优化算法的研究更为广泛,小博主我计划在下一篇中介绍一下基于模拟退火算法的粒子群优化算法,其性能比单纯的模拟退火算法和粒子群优化算法都要好,敬请期待。

3. 算法实现

3.1 问题场景

  最值问题,求解 f ( x ) = x s i n ( 5 x ) − x c o s ( 2 x ) f(x) = xsin(5x) - xcos(2x) f(x)=xsin(5x)xcos(2x)在定义域[0, 5]上的最小值。我们先手动计算一下:

f ′ ( x ) = 2 x s i n ( 2 x ) + s i n ( 5 x ) − c o s ( 2 x ) + 5 x c o s ( 5 x ) f^\prime (x) = 2 x sin(2 x) + sin(5 x) - cos(2 x) + 5 x cos(5 x) f(x)=2xsin(2x)+sin(5x)cos(2x)+5xcos(5x)  令 f ′ ( x ) = 0 f^\prime (x) = 0 f(x)=0之后,理论上可以求得驻点,但又不太好计算。。。

3.2 python实现

# -*- coding:utf-8 -*-
# Author:   xiayouran
# Email:    [email protected]
# Datetime: 2023/3/16 16:54
# Filename: particle_swarm_optimization.py
import numpy as np
import matplotlib.pyplot as plt
from base_algorithm import BaseAlgorithm
# base_algorithm 的实现已开源在github上
# 链接在文章末尾


__all__ = ['ParticleSwarmOptimization']


class Particle:
    def __init__(self):
        self.position = None    # 粒子的位置
        self.velocity = None    # 粒子的速度
        self.best_position = None   # 个体最优解
        self.fitness = None         # 适应度值


class ParticleSwarmOptimization(BaseAlgorithm):
    def __init__(self, population_size=100, p_dim=1, v_dim=1, max_iter=1000, x_range=(0, 5), seed=10086):
        super(ParticleSwarmOptimization, self).__init__()
        self.__population_size = population_size  # 种群大小
        self.__p_dim = p_dim        # 粒子位置维度
        self.__v_dim = v_dim        # 粒子速度维度
        self.__max_iter = max_iter  # 最大迭代次数
        self.__w = 0.5    # 惯性权重
        self.__c1 = 1.5   # 加速因子1
        self.__c2 = 1.5   # 加速因子2
        self.__population = []    # 粒子群
        self.global_best_particle = None    # 全局最优解
        self.__x_range = x_range
        self.__seed = seed

        np.random.seed(seed)

    def init_population(self):
        for i in range(self.__population_size):
            particle = Particle()
            particle.position = np.random.uniform(*self.__x_range, size=self.__p_dim)   # 随机初始化位置
            particle.velocity = np.random.uniform(-1, 1, size=self.__v_dim)     # 随机初始化速度
            particle.best_position = particle.position  # 初始最优位置
            particle.fitness = self.problem_function(particle.position)     # 计算适应度值
            if self.global_best_particle is None or particle.fitness < self.problem_function(self.global_best_particle.position):
                self.global_best_particle = particle  # 更新全局最优解
            self.__population.append(particle)

    def update_population(self):
        for i in range(self.__population_size):
            particle = self.__population[i]
            r1 = np.random.uniform(0, 1)
            r2 = np.random.uniform(0, 1)
            # Step2: 更新速度
            particle.velocity = self.__w * particle.velocity + \
                                self.__c1 * r1 * (particle.best_position - particle.position) + \
                                self.__c2 * r2 * (self.global_best_particle.position - particle.position)
            # Step3: 更新位置
            particle.position += particle.velocity
            particle.position = np.clip(particle.position, 0, 5)
            # Step4: 更新个体最优解
            if self.problem_function(particle.position) < self.problem_function(particle.best_position):
                particle.best_position = particle.position
                particle.fitness = self.problem_function(particle.position)
            # Step5: 更新全局最优解
            if self.problem_function(particle.position) < self.problem_function(self.global_best_particle.position):
                self.global_best_particle = particle

    def solution(self):
        # Step1: 粒子初始化
        self.init_population()
        for i in range(self.__max_iter):
            self.update_population()
        print('best particle:\nposition: {}\nvelocity: {}'
              '\nfitness: {}\nbest_position: {}'.format(self.global_best_particle.position,
                                                        self.global_best_particle.velocity,
                                                        self.global_best_particle.fitness,
                                                        self.global_best_particle.best_position))

if __name__ == '__main__':
    algo = ParticleSwarmOptimization()
    algo.solution()

  得到的最优解如下:

best particle:
position: [3.43298001]
velocity: [1.10173139e-09]
fitness: [-6.27707976]
best_position: [3.43298001]

  模拟过程如下:

在这里插入图片描述

代码仓库:IALib[GitHub]

  本篇代码已同步至【智能算法(Python复现)】专栏专属仓库:IALib
  运行IALib库中的PSO算法:

git clone [email protected]:xiayouran/IALib.git
cd examples
python main.py -algo pso

猜你喜欢

转载自blog.csdn.net/qq_42730750/article/details/129619922