粒子群算法 PSO 讲解 + python源码

Particle Swarm Optimization(PSO)

这个算法是用来寻找某个函数function的最优解的,这个的目的和贪心算法、模拟退火算法等是一致的。

对于贪心算法而言,如果目标是个连续函数,其可以对当前起始点求梯度方向,然后朝着那个方向迈一个步长,我们都知道这种算法一个缺点在于极其容易陷入局部最优解。

对于模拟退火而言,其在本质上可以看作一个简单地随机梯度下降法,他给非当前最佳选择一个备选的可能性,使得其相对贪心算法而言没有那么容易困在局部最优解附近。

这两种算法的局限有一个共通点:他们只能获取当前一个点附近的相关信息。他们都只能求得当前这个点的梯度方向,而对更加全面,更加整体的信息没有把控。换言之,如果我们能以某种方法对全局的信息有所了解,就有更大的可能性找到全局最优解。

我们用人话重复一下上面的意思,考虑一种极限情况,我们遍历了所有点的函数值,这个时候我们肯定知道全局最优解在哪儿。换一种表达方式,我们拿到了所有可能的输入,把所有的点一一尝试,取argmax即可。

但是遍历在时间上通常是难以接受的,尤其是对于多变量的规划问题而言,所以我们退而求其次,与其用所有的输入,不如只利用有限个点/粒子(假设是n个),把他们随机初始化在解空间上,这样我们就得到了n份信息,而非前面提及的两个算法中那样,只能依靠一棵独苗。

这就是PSO算法的核心思想,对于随机初始化在解空间上的n个点,我们每次在更新n个点的位置的时候,可以充分考虑每个点当前的位置、自己的最佳历史位置、所有点的最佳历史位置,用这三个信息,通过给予这些信息不同权重,我们可以对当前粒子的位置进行更新。

下面我们将尝试使用公式的方法对以上算法进行说明。
初始输入: i n p u t = { X i : X i = ( x 1 , x 2 ) , x i ∈ R } i = 1 n input = \{X_i: X_i=(x_1, x_2), x_i \in R\}_{i=1}^n input={Xi:Xi=(x1,x2),xiR}i=1n,一共有n个2维的输入向量,这里设定成二维是为了方便后面的叙述,实际应用中可以换做任意的d维。
初始方向: i n i t i a l _ d i r e c t i o n = { V i : V i = ( v 1 , v 2 ) , v i ∈ R , v i = r a n d o m ( ) } initial\_direction=\{V_i:V_i=(v_1, v_2), v_i \in R, v_i = random() \} initial_direction={Vi:Vi=(v1,v2),viR,vi=random()},是随机指定的方向,需要提前说明的是,在PSO算法中,当前的方向从来都不是当前的梯度方向,而是由另一种方法确定下来的方向
个人历史最佳值/方向(personal/individual best): p b e s t _ v a l u e , p b e s t _ p o s i t i o n pbest\_value, pbest\_position pbest_value,pbest_position
全局历史最佳值/方向(social/global best): g b e s t _ v a l u e , g b e s t _ p o s i t i o n gbest\_value, gbest\_position gbest_value,gbest_position

权值:
当前方向的权值(inertia, 可以理解为惯性的意思):w
个人历史最佳方向的权值:c1
总体历史最佳方向的权值:c2

对于每个点而言,其新的方向是:

n e w _ d i r e c t i o n = w ∗ o l d _ d i r e c t i o n + c 1 ∗ ( p b e s t _ d i r e c t i o n − o l d _ d i r e c t i o n ) + c 2 ∗ ( g b e s t _ d i r e c t i o n − o l d _ d i r e c t i o n ) \begin{align} new\_direction = &w * old\_direction + \\ &c_1 * (pbest\_direction - old\_direction) + \\ &c_2 * (gbest\_direction-old\_direction) \end{align} new_direction=wold_direction+c1(pbest_directionold_direction)+c2(gbest_directionold_direction)

这里面的old_direction指当前的方向,如果是第一次进行迭代,其指示的应该是初始方向,即 i n i t i a l _ d i r e c t i o n initial\_direction initial_direction

视频教程

链接:删掉youtube.com/这些watch?v=Jhg字DMAm-imI
这哥们英语口音比较重,字幕识别也辨别不出来,建议有条件能看的把注意力主要放在ppt上,顺带提一嘴,最后他给的几个例子,内容是一堆黑点向中心聚集,他是为了直观描述权重的相对大小对各个点收敛方式和速度的影响。

文档教程

https://machinelearningmastery.com/a-gentle-introduction-to-particle-swarm-optimization/
在看完上面那个视频之后,如果还不太清楚,可以看看这个教程。国外的教程写的都让人看得懂,国内的感觉有的是故意让你看不懂捏,什么自主构建的学术壁垒hhh

python 源码

import numpy as np
import matplotlib.pyplot as plt
import time
from tqdm import tqdm

class particle:
    def __init__(self, x, type='min'):
        self.type = type
        self.position = np.array(x)
        self.direction = np.random.rand(len(x)) * 0.1
        self.value = None
        self.pbest_value = None
        self.pbest_position = None

    def update_value(self, new_value):
        self.value = new_value
        if type(self.pbest_position) == type(None) or \
            (new_value < self.pbest_value and self.type == 'min') or \
            (new_value > self.pbest_value and self.type == 'max'):
            self.pbest_position = self.position
            self.pbest_value = self.value

    def individual_direction(self):
        return self.pbest_position - self.position

    def social_direction(self, gbest_position):
        return gbest_position - self.position

class PSO():
    def __init__(self, n_particles, particle_dim, scale, weight, function, type='min'):
        self.n_particles = n_particles
        self.particle_dim = particle_dim
        self.scale = scale
        self.function = function
        self.weight = weight
        self.type = type
        self.init_particles()
    
    def init_particles(self):
        position_origin = np.random.rand(self.n_particles, self.particle_dim) * self.scale
        self.particles = [particle(x) for x in position_origin]
        for p in self.particles:
            p.update_value(self.function(p.position))

    def get_gbest_particle(self):
        if self.type == 'min':
            arg = np.argmin([self.function(p.position) for p in self.particles])
        else:
            arg = np.argmax([self.function(p.position) for p in self.particles])
        return self.particles[arg]
    
    def optimize(self, n_iteration):
        for i in tqdm(range(n_iteration)):
            self.gbest_particle = self.get_gbest_particle()
            old_directions = [p.direction for p in self.particles]
            individual_directions = [p.individual_direction() for p in self.particles]
            social_directions = [p.social_direction(self.gbest_particle.position) for p in self.particles]
            r_weight = np.random.rand(2)
            new_directions = [self.weight['w'] * inertia + self.weight['c1'] * r_weight[0] * individual + self.weight['c2'] * r_weight[1] * social for (inertia, individual, social) in zip(old_directions, individual_directions, social_directions)]
            for p, new_pos in zip(self.particles, new_directions):
                p.position += new_pos
                p.direction = new_pos
                p.update_value(self.function(p.position))
        self.gbest_particle = self.get_gbest_particle()
        return self.gbest_particle

def f(pos):
    return (pos[0]-1)**2  + (pos[1]-1) ** 2

weight = {'w' : 0.4, 'c1' : 0.2, 'c2' : 0.2}
pso = PSO(20, 2, 5, weight, f)
gbest_particle = pso.optimize(30)

print(f(gbest_particle.position), gbest_particle.position)

可能比较乱,后续跟进DPSO的代码,会整理放在GitHub上

你可能感兴趣的:(算法,python)