[PYTHON] Implémentation de la régression logistique avec la méthode d'optimisation des groupes de particules

J'ai écrit un algorithme génétique dans cet article il y a plusieurs fois, mais cette fois j'ai essayé de faire un algorithme d'optimisation fait avec la même idée.

Qu'est-ce que l'optimisation des groupes de particules?

Je vois souvent des poissons et des oiseaux se déplacer en groupe. Même si ces essaims trouvent des ennemis ou de la nourriture, ils effectueront des mouvements optimaux avec des mouvements non perturbés. C'est une méthode pour l'utiliser pour l'optimisation des fonctions.

Les références sont ici Introduction à l'algorithme de calcul évolutif Optimisation des groupes de particules et système non linéaire

la mise en oeuvre

Je n'ai rien fait de compliqué et je l'ai implémenté de la manière la plus simple.

functions.py


import random

# min~Faire des nombres au hasard entre max
def randRange(a : float,b : float) -> float:
    return a + (b-a)*random.random()

#Générer n particules

def makeParticles(n : int,ranges : dict) -> list:
    ls = [{key:0 for key in ranges.keys()} for key in range(n)]
    for i in range(n):
        for key in ranges.keys():
            a,b = ranges[key]
            ls[i][key] = randRange(a,b)
    return ls

#Ajouter les mêmes éléments de deux dictionnaires
def dictAdd(*dic : dict) -> dict:
    ansDic = {}
    for key in dic[0].keys():
        ansDic[key] = 0
        for i in range(len(dic)):
            ansDic[key] += dic[i][key]
    return ansDic

#Soustrayez les mêmes éléments de deux dictionnaires
def dictDiff(dic1 : dict,dic2 : dict) -> dict:
    ansDic = {}
    for key in dic1.keys():
        ansDic[key] = dic1[key] - dic2[key]
    return ansDic

#Produit des mêmes éléments de deux dictionnaires
def dictMul(dic1 : dict, dic2 : dict) -> dict:
    ansDic = {}
    for key in dic1.keys():
        ansDic[key] = dic1[key] * dic2[key]

#Multipliez chaque élément du dict
def dictNumMul(dic : dict,n) -> dict:
    ansDic = {}
    for key in dic.keys():
        ansDic[key] = dic[key] * n
    return ansDic

PSO.py


import functions as func
import numpy as np
import copy

"""
time_max Nombre maximum d'itérations
swam_taille Nombre de particules
inertie Coefficient d'inertie
ap Meilleur coefficient d'accélération personnel
ag Meilleur facteur d'accélération mondial
"""


class PSO:
    def __init__(self,time_max = 1000,swam_size = 100,
                inertia = 0.9,ap = 0.8,ag = 0.8):
        self.time_max = time_max
        self.swam_size = swam_size
        self.inertia = inertia
        self.ap = ap
        self.ag = ag
        self.pBest = [np.inf for i in range(swam_size)]
        self.gBest = np.inf
        self.gPosi = {}

    """
Passez la fonction que vous souhaitez minimiser à l'argument f de la fonction d'optimisation
dict pour l'argument para
    {"Nom de l'argument":[valeur minimum,Valeur maximum]}
Je le passe comme ça.
    """
    #Recevez la fonction que vous souhaitez optimiser et ses arguments
    def optimize(self,f,para):
        self.function = f
        self.particleInitialization(para)
        self.Update()
        for i in range(self.time_max):
            self.move()
            self.Update()
        return self.gPosi,self.gBest

    #Initialiser les particules
    def particleInitialization(self,ls : dict):
        self.particle = func.makeParticles(self.swam_size,ls)
        self.pPosi = copy.deepcopy(self.particle)
        self.velocity = [{key:0 for key in ls.keys()} for i in range(self.swam_size)]

    #Bouge toi
    def move(self):
        for i in range(self.swam_size):
            v0 = func.dictNumMul(self.velocity[i],self.inertia)
            v1 = func.dictNumMul(func.dictDiff(self.pPosi[i],self.particle[i]),self.ap*func.randRange(0,1))
            v2 = func.dictNumMul(func.dictDiff(self.gPosi,self.particle[i]),self.ag*func.randRange(0,1))
            v = func.dictAdd(v0,v1,v2)
            self.particle[i] = func.dictAdd(self.particle[i],v)
            self.velocity[i] = v

    #Mettre à jour le record personnel et le meilleur mondial
    def Update(self):
        for i in range(self.swam_size):
            cost = self.function(**self.particle[i])
            if cost < self.pBest[i]:
                self.pBest[i] = cost
                self.pPosi[i] = copy.deepcopy(self.particle[i])
            if cost < self.gBest:
                self.gBest = cost
                self.gPosi = copy.deepcopy(self.particle[i])

Essayez d'utiliser

main.py


import PSO

#Définissez la fonction que vous souhaitez minimiser

def f(x,y):
    return x**4+4*x**3-8*(x+1)**2-1 + y**4 + x**3 - 5*(y - 2)**2 - 3*x
def main():
    pso = PSO.PSO(time_max=1000)
    #Premières particules-10000<x<10000,-10000<y<Générer dans la plage de 10000 et minimiser
    a,b = pso.optimize(f,{"x":[-10000,10000],"y":[-10000,10000]})
    print("Coordonner",a,"valeur minimum",b)

if __name__ == "__main__":
    main()

production Coordonnées {'x': -4.412547925687121, 'y': -2.187602966185929} Valeur minimale -196.17514912856427

J'ai pu le minimiser.

[Résultats avec wolframalpha](https://www.wolframalpha.com/input/?i=minimize%5Bx%5E4%2B4x%5E3-8%28x%2B1%29%5E2-1+%2B+ y% 5E4 +% 2B + x% 5E3 + - + 5 *% 28y + - + 2% 29% 5E2 + - + 3 * x% 5D)

Postscript

C'était tout quand je l'ai téléchargé, mais c'est vraiment terne, donc j'aimerais effectuer une régression logistique en utilisant la méthode d'optimisation des groupes de particules.

Ici, la limite de classification est prédite en minimisant la fonction d'erreur d'entropie croisée par la méthode d'optimisation du groupe de particules.

logistic.py


import functions as func
import math
import PSO
import numpy
#Limite lors de l'étiquetage des nombres aléatoires générés
def f(x,y):
    if x + 2 * y < 11:return 1
    else:return 0

#Fonction Sigmaid
def sigmoid(x):
    if 600 > x > -600:
        return 1/(1 + math.exp(-x))
    elif x > 0:
        return 1
    else:
        return 0

#Pour renvoyer l'infini quand il est nul
def llog(x):
    if x == 0:
        return -numpy.inf
    return math.log(x)

#Générer des données avec des nombres aléatoires
data = [[func.randRange(0,10),func.randRange(0,10)] for i in range(100)]
label = [f(x,y) for x,y in data]

#Fonction de coût pour minimiser
def loss(w1,w2,b):
    ent = 0
    for i in range(len(data)):
        x = sigmoid(data[i][0]*w1 + data[i][1]*w2 + b)
        y = label[i]
        ent -= y * llog(x) + (1-y) * llog(1-x)
    return ent

def main():
    pso = PSO.PSO()
    a,b = pso.optimize(loss,{"w1":[-100,100],"w2":[-100,100],"b":[-100,100]})
    print(a,b)

    #Évaluer si le modèle créé peut classer correctement les données d'entraînement
    c = 0
    for i in range(100):
        n = sigmoid(data[i][0]*a["w1"] + data[i][1]*a["w2"] + a["b"])
        ans = 1 if n > 0.5 else 0
        if ans == label[i]:
            c += 1
    print("Précision de classification",c/100)

if __name__ == "__main__":
    main()

résultat

{'w1': -5.345994566644921, 'w2': -10.19111400412491, 'b': 56.97834543330356} 2.744570556357321 Précision de classification 1.0

J'ai pu le classer fermement.

Recommended Posts

Implémentation de la régression logistique avec la méthode d'optimisation des groupes de particules
À propos des paramètres d'optimisation des groupes de particules (PSO)
Implémentation de la régression logistique avec NumPy
Analyse de régression logistique Self-made avec python
PRML Chapitre 4 Implémentation Python de la régression logistique bayésienne
Retour logistique
Retour logistique
Essayez Theano avec les données MNIST de Kaggle ~ Retour logistique ~
Implémenter un modèle de régression logistique en temps discret avec stan
Méthode d'analyse de régression
Implémentation de la méthode de gradient 1
Précautions lors de l'exécution de la régression logistique avec Statsmodels
Résolution du problème de l'iris avec scikit-learn ver1.0 (régression logistique)