インテリジェントな最適化のための粒子群最適化 (PSO) (Matlab、Python、C++ 実装)

1. アルゴリズムの紹介

粒子群最適化 (PSO) はバイオニック アルゴリズムであり、解空間で最適な解を見つけるための単純なアルゴリズムです。これは、目的関数のみを必要とし、目的の勾配や微分形式に依存しないという点で、他の最適化アルゴリズムとは異なります。ハイパーパラメータもほとんどありません。

1995 年にケネディとエバーハートによって提案されました。

グループ反復では、粒子 (部分) が解空間内の最適な粒子を追跡して検索します。

PSO と差分進化アルゴリズムは、最新の最適化手法の分野で研究のホットスポットとなっています

粒子群最適化アルゴリズムは新しいアルゴリズムであり、遺伝的アルゴリズムと多くの類似点があり、大域的最適解に収束する確率が非常に高くなります。

① 従来のアルゴリズムと比較して、計算速度が非常に速く、グローバル検索能力も強力です。

②PSO は人口規模にあまり影響を受けないため、初期人口は 500 ~ 1000 に設定され、速度はほとんど影響しません。

③粒子群最適化アルゴリズムは連続関数の極値問題に適しており、非線形問題や多重ピーク問題に対する強力な大域探索能力を備えています。

アルゴリズムの利点:

①シンプルで実装が簡単、②収束が早い、③設定パラメータが少ない

アルゴリズムの基本的な考え方:

PSO のアイデアは鳥の捕食行動の研究に由来しています。

餌を求めて群がる鳥の行動をシミュレートし、鳥間の集団協力を通じて個体群の最適な目標を達成するための群知能に基づく最適化手法です。

PSO は、社会的行動 (社会のみのモデル) と個人の認知 (認知のみのモデル) を組み合わせます。

アルゴリズムの紹介:

①それぞれの最適化問題は粒子と呼ばれる鳥として想像されます。すべての粒子は n 次元空間で検索されます。

② すべての粒子は、現在位置の品質を判断するための適合値を決定する適合関数によって決定されます。

③各粒子には、見つけた最適な位置を記憶する記憶機能が備わっていなければなりません。

④また、各粒子には速度(速度)があり、飛行距離と方向を決定します。この速度は、自身とその仲間の飛行経験に応じて動的に調整されます。

関連概念:

グループ (Swarm): 遺伝的アルゴリズムの母集団に相当する粒子の集合

パーティクル: グループ内の個人を検索します

位置 (Position); 速度 (Velocity); 粒子自体の現在の最適な位置 (個別の最適位置): Pbest

グループのグローバルベストポジション(グローバルベストポジション):Gbest

2. アルゴリズム処理

3. PSOの要素

人口規模 (NP)

NP は整数です。

NP が小さい場合、局所最適に陥る可能性が高くなります。

NP が大きい場合、PSO の最適化能力は非常に優れていますが、アルゴリズムの収束は遅くなります。

グループの数が一定のレベルまで増加すると、それ以上増加しても大きな影響はなくなります。

慣性重み係数 (w)

w は非負の数です。

w=1 基本 PSO アルゴリズム

w=0 は粒子自体の速度記憶を失います。

個人の認知定数(c1)

c1=0 無私の PSO、「社会のみで自己はなし」。

グループの多様性が急速に失われ、局所最適に陥りやすくそこから抜け出すことができません。

社会経験定数 (c2)

c2=0 利己的な PSO、「自分だけ、社会なし」。

社会的な情報共有がまったく行われていないため、アルゴリズムの収束が遅くなります。

最高速度(Vmax)

アルゴリズムを維持するために使用される探査パワーとマイニングパワーのバランス

Vmax が大きいと探索能力は強いですが、粒子が最適解の上を飛び越えやすくなります。

Vmax が小さい場合、マイニング能力は強力ですが、局所的な最適解に陥りやすくなります。

Vmax は通常、各寸法の可変範囲の 10% ~ 20% に設定されます。

近隣トポロジ

PSO アルゴリズムでは、粒子の近傍は母集団内の粒子に隣接する粒子の集合を表し、粒子はその近傍にある粒子と情報を交換できます。近傍のサイズによって情報交換の範囲が決まります。粒子の近傍は、グループ全体であることも、その周囲のいくつかの粒子で構成されることもあり、近傍トポロジによって、グループ内の粒子の隣接関係が決まります。スター トポロジ (グローバル ネイバーフッド トポロジとも呼ばれる)、バッドシェイプ トポロジ、およびホイール トポロジの 3 つの主な近傍トポロジがあります。

近傍構造が大域近傍構造とみなされる場合、粒子 pi の局所極値 gi は大域極値となり、通常 gbest と表されます。

上記の 3 つの近隣構造物に関する実験研究を通じて、ケネディは次のような結論に達しました。

大域近傍構造を使用する PSO アルゴリズムは収束速度が速くなりますが、局所最適に陥りやすいです。

円形近傍構造を用いた PSO アルゴリズムは収束速度が遅いですが、高い確率で最適解を見つけることができます。

ただし、ラウンド近傍構造を使用する PSO アルゴリズムはあまり効果的ではありません。

グローバル履歴最適解: Gbest;

局所的な履歴最適解: Lbest;

不同于遗传算法,粒子群算法不需要编码,直接利用粒子的位置来表示自变量,每个粒子的位置都由自变量的个数和取值范围决定,而速度由自变量的个数和速度限制决定,形式如下,其中d代表空间维数(自变量数):

step4处错误,应改为根据粒子的适应值来更新个体的历史极值并计算全局极值。

应用举例:

粒子群优化算法改进研究

以函数xsin(x)cos(2x)-2xcos(3x)+3xsin(4x)为例

位置和速度的初始化即在位置和速度限制内随机生成一个N x d 的矩阵,对于此题,位置初始化也就是在0~50内随机生成一个20x1的数据矩阵,而对于速度则不用考虑约束,一般直接在0~1内随机生成一个20x1的数据矩阵

粒子群的另一个特点就是记录每个个体的历史最优和种群的历史最优,因此而二者对应的最优位置和最优值也需要初始化。其中每个个体的历史最优位置可以先初始化为当前位置,而种群的历史最优位置则可初始化为原点。对于最优值,如果求最大值则初始化为负无穷,相反地初始化为正无穷。

每次搜寻都需要将当前的适应度和最优解同历史的记录值进行对比,如果超过历史最优值,则更新个体和种群的历史最优位置和最优解。

每次更新完速度和位置都需要考虑速度和位置的限制,需要将其限制在规定范围内,此处仅举出一个常规方法,即将超约束的数据约束到边界(当位置或者速度超出初始化限制时,将其拉回靠近的边界处)。当然,你不用担心他会停住不动,因为每个粒子还有惯性和其他两个参数的影响。


clear
%函数表达式,求解这个函数的最大值
f=@(x)x.*sin(x).*cos(2*x)-2*x.*sin(3*x)+3*x.*sin(4*x);
N=20;%初始种群个数
d=1;%可行解维数
ger=200;%最大迭代次数
limit=[0,50];%设置位置参数限制 即x范围
vlimit=[-10,10];%设置速度限制
w=0.8;%惯性权重
c1=0.5;%自我学习因子
c2=0.5;%群体学习因子
figure(1);ezplot(f,[0,0.01,limit(2)]);
x=limit(1)+(limit(2)-limit(1)).*rand(N,d);%初始化种群位置
% rand() 函数用于生成一个或多个 0 到 1 之间的随机数。
% 调用 rand() 函数时,如果不带参数,则返回一个 0 到 1 之间的随机数;
% 如果带一个参数,则返回一个该参数指定大小的矩阵,其中包含 0 到 1 之间的随机数。
% 如果带两个参数,则返回一个该参数指定大小的矩阵,其中包含 0 到 1 之间的随机数。
v=rand(N,d);%初始化种群速度
xm=x;%每个个体的历史最佳位置
ym=zeros(1,d);%种群的历史最佳位置
fxm=ones(N,1)*inf;%每个个体的历史最佳适应度 inf是表示无穷大
fym=inf;%种群历史最佳适应度

%hold on是当前轴及图像保持而不被刷新,准备接受此后将绘制的图形,多图共存,
%即启动图形保持功能,当前坐标轴和图形都将保持,
%从此绘制的图形都将添加在这个图形的基础上,并自动调整坐标轴的范围
%hold off使当前轴及图像不再具备被刷新的性质,新图出现时,取消原图。即关闭图形保持功能。
hold on
plot(xm,f(xm),'ro');
title('初始状态图');

figure(2)
%群体更新
iter=1;
%record=zeros(ger,1);%记录器
while iter<=ger
    fx=f(x);%个体当前适应度
    for i=1:N
        if fx(i)<fxm(i)
            fxm(i)=fx(i);%更新个体历史最佳适应度
            xm(i,:)=x(i,:);%更新个体历史最佳位置
        end
    end
    if min(fxm)<fym
        [fym,nmin]=min(fxm);%更新群体历史最佳适应度
        ym=xm(nmin,:);
    end
    %先更新速度,再更新位置
    v=v*w+c1*rand*(xm-x)+c2*rand*(repmat(ym,N,1)-x);%速度更新,rempat函数是矩阵的复制函数
    %xm是行向量,ym是一个数
    %边界速度处理
    v(v>limit(2))=vlimit(2);
    v(v<limit(1))=vlimit(1);
    x=x+v;%位置更新
    
    %边界位置处理
    x(x>limit(2))=limit(2);
    x(x<limit(1))=limit(1);
    
    record(iter)=fym;%最大值记录
    
    x0=0:0.01:limit(2);
    subplot(1,2,1);%一行两列的第一个图
    plot(x0,f(x0),'b-',x,f(x),'ro');title('状态位置变化')
    subplot(1,2,2);%一行两列的第二个图
    plot(record);title('最优适应度进化过程')
    pause(0.05);%pause(n)暂停执行n秒,然后继续执行。必须启用暂停,此调用才能生效。
    iter=iter+1;
end
x0=0:0.01:limit(2);
figure(3);
plot(x0,f(x0),'b-',x,f(x),'ro');title('最终状态位置')
%disp函数会直接将内容输出在Matlab命令窗口中
% 同时输出字符串和数字:
disp(['最小值:',num2str(fym)]);
disp(['变量取值:',num2str(ym)]);
    


最终结果:

最小值:-215.4596

变量取值:46.7056

待求解问题:

Rosenbrock’s,取值范围为[-10,10],取值范围内的理想最优解为0,将其搜索的空间维度设为20。

#库的导入
import numpy as np
import random
import matplotlib.pyplot as plt
#待求解问题
def function(x):
    y1 = 0
    for i in range(len(x) - 1):
        y2 = 100 * ((x[i + 1] - x[i] ** 2) ** 2) + (x[i] - 1) ** 2
        y1 = y1 + y2
    y = abs(0 - y1)
    return y

rangepop=[-10,10]    #取值范围
pn=30   #种群数量
iterators = 1000    #迭代次数
w=0.9   #惯性因子
#两个加速系数
c1=2
c2=2
#a1用于存储种群个体位置信息,v用于存储种群个体移动速度,fitness用于存储个体适应度值
a1=np.zeros((pn,20))
v = np.zeros((pn, 20))
fitness=np.zeros(pn)

#对种群个体、移动速度进行初始化,计算初始适应度值
for j in range(pn):
    a1[j] = np.random.uniform(low=-10, high=10,size=(1, 20))
    v[j] = np.zeros((1,20))
    fitness[j] = function(a1[j])
#allpg,bestpg分别表示种群历史最优个体和适应度值
allpg,bestpg=a1[fitness.argmin()].copy(),fitness.min()
#poppn,bestpn分别存储个体历史最优位置和适应度值
poppn,bestpn=a1.copy(),fitness.copy()
#bestfitness用于存储每次迭代时的种群历史最优适应度值
bestfitness=np.zeros(iterators)
#开始迭代
for i in range(iterators):
    print("generation:",i)
    for m in range(pn):
        r1 = np.random.rand()
        r2 = np.random.rand()
        #计算移动速度
        v[m]=w*v[m]+c1*r1*(poppn[m]-a1[m])+c2*r2*(allpg-a1[m])
        #计算新的位置
        a1[m]=a1[m]+v[m]
        #确保更新后的位置在取值范围内
        a1[a1<rangepop[0]]=rangepop[0]
        a1[a1>rangepop[1]]=rangepop[1]
        #计算适应度值
        fitness[m] = function(a1[m])
        #更新个体历史最优适应度值
        if fitness[m]<bestpn[m]:
            bestpn[m]=fitness[m]
            poppn[m]=a1[m].copy()
    #更新种群历史最优适应度值
    if bestpn.min()<bestpg:
        bestpg=bestpn.min()
        allpg=poppn[bestpn.argmin()].copy()
    bestfitness[i]=bestpg
    print("the best fitness is:",bestfitness[i])

#将结果进行绘图
fig=plt.figure(figsize=(12, 10), dpi=300)
plt.title('The change of best fitness',fontdict={'weight':'normal','size': 30})
x=range(1,1001,1)
plt.plot(x,bestfitness,color="red",label="PSO",linewidth=3.0, linestyle="-")
plt.tick_params(labelsize=25)
plt.xlabel("Epoch",fontdict={'weight':'normal','size': 30})
plt.ylabel("Fitness value",fontdict={'weight':'normal','size': 30})
plt.xticks(range(0,1001,100))
plt.legend(loc="upper right",prop={'size':20})
plt.savefig("PSO.png")
plt.show()
#include <iostream>
#include<time.h>
#include <random>

using namespace std;

//使用c++实现粒子群算法
//需要寻优的非线性函数为:
//f(x,y) = sin(sqrt(x^2+y^2))/(sqrt(x^2+y^2)) + exp((cos(2*PI*x)+cos(2*PI*y))/2) - 2.71289
//该函数有很多局部极大值点,而极限位置为(0,0),在(0,0)附近取得极大值

// Author: yuzewei

const int N = 20;             //粒子群数量
const int dim = 2;            //粒子群维度
const int gen = 300;//100;    //迭代次数
const double PI = 3.1415926;
const double w = 0.9;//0.6;   //惯性权重
const double c1 = 2;// 1.49445;     //自学习,学习因子
const double c2 = 2; // 1.49445;    //群体最优学习,学习因子
const double pmin = -2.0;     //粒子的移动区间范围
const double pmax = 2.0;
const double vmin = -0.8;// -0.5;   //粒子的移动速度范围
const double vmax = 0.8;// 0.5;

double P[N][dim];       //粒子位置
double V[N][dim];       //粒子速度
double fitness[N];      //粒子适应度
double pbest[N][dim];   //粒子最优位置

double gbest[dim];      //粒子群最优位置
int gbestGen = -1;      //记录达到最优位置时的迭代次数

double func(double *p)
{
    double x = *p;
    double y = *(p + 1);
    double result = sin(sqrt(x * x + y * y)) / (sqrt(x * x + y * y)) 
                    + exp((cos(2 * PI * x) + cos(2 * PI * y)) / 2) - 2.71289;
    return result;
}

int getGbestIndex(double *fit)
{
    int index = -1;
    double max = *fit;
    for (int i = 0; i < N; i++)
    {
        if (*(fit + i) > max)
        {
            max = *(fit + i);
            index = i;
        }
    }
    return index;
}

void init()
{
    int index = -1; //记录位置最佳的粒子编号

    default_random_engine e;
    uniform_real_distribution<double> rand1(pmin, pmax);
    uniform_real_distribution<double> rand2(vmin, vmax);
    e.seed(time(0));

    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < dim; j++)
        {
            P[i][j] = rand1(e);
            V[i][j] = rand2(e);
            pbest[i][j] = P[i][j];
        }
        fitness[i] = func(P[i]);
    }

    index = getGbestIndex(fitness);
    gbest[0] = pbest[index][0];
    gbest[1] = pbest[index][1];
    gbestGen = 0;
}


void printParticles()
{
    /*for (int i = 0; i < N; i++)
    {
        cout << "第" << i << "个粒子" << endl;
        cout << "  位置:(" << P[i][0] << "," << P[i][1] << ")" << endl;
        cout << "  速度:(" << V[i][0] << "," << V[i][1] << ")" << endl;
        cout << "  适应度:" << fitness[i] << endl;
        cout << "  个体最优:(" << pbest[i][0] << "," << pbest[i][1] << ")" << endl;
    }*/
    //cout << endl;
    cout<<"群体最优:(" << gbest[0] << "," << gbest[1] << ")" << endl;
    cout << "适应度为:" << func(gbest) << endl;
}

void PSOiterator()
{
    cout << "开始迭代!" << endl;
    cout << "第1代粒子群:" << endl;
    printParticles();

    for (int g = 0; g < gen - 1; g++)
    {
        cout << "第" << g + 2 << "代粒子群:" << endl;

        int index = -1; //记录最优粒子个体编号

        default_random_engine e;
        uniform_real_distribution<double> rand(0, 1.0);
        e.seed(time(0));

        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j < dim; j++)
            {
                //更新速度
                V[i][j] = w * V[i][j] + rand(e) * (pbest[i][j] - P[i][j])
                    + rand(e) * (gbest[j] - P[i][j]);
                if (V[i][j] < vmin)
                    V[i][j] = vmin;
                if (V[i][j] > vmax)
                    V[i][j] = vmax;

                //更新位置
                P[i][j] = P[i][j] + V[i][j];
                if (P[i][j] < pmin)
                    P[i][j] = pmin;
                if (P[i][j] > pmax)
                    P[i][j] = pmax;
            }

            fitness[i] = func(P[i]); //更新适应度

            //更新个体最优
            if (fitness[i] > func(pbest[i]))
            {
                pbest[i][0] = P[i][0];
                pbest[i][1] = P[i][1];
            }

        }

        //更新整体最优
        index = getGbestIndex(fitness);
        if (func(P[index]) > func(gbest))
        {
            gbest[0] = P[index][0];
            gbest[1] = P[index][1];
            gbestGen = g + 1;
        }

        printParticles();
    }
}

void printResult()
{
    cout << "迭代结束!" << endl;
    cout << "总共进行了" << gen << "次迭代,";
    cout<<"第"<<gbestGen<<"代得到最优位置:(" << gbest[0] << "," << gbest[1] << ")" << endl;
    cout << "此位置对应的函数值为:" << func(gbest) << endl;
}

int main()
{
    init();
    PSOiterator();
    printResult();
    return 0;
}

おすすめ

転載: blog.csdn.net/weixin_52732185/article/details/129409506