Connaissez-vous le cadre de calcul évolutif appelé DEAP? e? Calcul d'évolution avant DEAP Quel est cet état? Eh bien, la connaissance préalable des calculs évolutifs n'est pas très pertinente dans cet article. L'objectif principal de cet article est d'expliquer dans quelle mesure DEAP utilise Python pour implémenter des calculs évolutifs et pour aider à améliorer la puissance de Python.
Même si vous dites OK sans aucune connaissance préalable, vous devez comprendre les mots qui apparaîtront après cela, donc je vais expliquer brièvement les calculs évolutifs, en particulier l'algorithme génétique (GA) utilisé pour l'explication.
GA est un algorithme inspiré de l'évolution des organismes vivants, comme on dit qu'il est «génétique». Pourquoi les créatures vivantes survivent-elles? Il y a plusieurs raisons, mais les trois suivants sont les principaux facteurs de GA.
Au fait, j'ai écrit que les créatures fortes survivent par choix, mais «forte» signifie «haute forme». Vient ensuite "Quel est le degré de conformité?", Mais en GA c'est la "valeur de la fonction objectif que vous voulez résoudre". C'est $ f (\ vec {x}) $. Vient ensuite l'histoire de $ \ vec {x} $.
Je suis finalement arrivé à la partie «génétique» de l'algorithme génétique. «L'héritage» est un gène. Ce gène est $ \ vec {x} $ lorsqu'il est écrit mathématiquement. La représentation génique est grossièrement divisée en deux, et lors du calcul de $ f (\ vec {x}) $, elle est exprimée par une chaîne binaire de 0/1, et elle est calculée en convertissant de 0/1 à la zone de définition de la fonction objectif. C'est une méthode appelée valeur réelle GA qui utilise la valeur numérique telle qu'elle est en tant que "gène".
Dans ** Crossing **, j'ai écrit que les gènes de mes parents sont hérités, mais je coupe et colle les gènes comme suit pour faire un enfant. L'endroit où couper est généralement décidé par un nombre aléatoire.
Père: 01101101 Mère: 11000011
Couper et coller au milieu (l'enfant 1 est la moitié gauche du père + la moitié droite de la mère, l'enfant 1 est la moitié gauche de la mère + la moitié droite du père)
Enfant 1: 01100011 Enfant 2: 11001101
** Mutation ** a une certaine probabilité d'inverser les bits.
Après croisement, mutation enfant 1 (extrémité gauche modifiée de 0 à 1)
Enfant 1: 11100011
La survie ou non de l'enfant ainsi créé dépend du degré d'aptitude (valeur de la fonction objective) de l'enfant. Ceci est la ** sélection **. Faites pivoter cette sélection, ce croisement et cette mutation. Ensuite, l'idée de GA est que les individus avec une grande aptitude (valeur de fonction objective élevée) survivent et la $ \ vec {x} $ (valeur de la variable de conception) souhaitée est obtenue.
Comment traverser (utiliser la technique de croisement), muter ou choisir doit dépendre de la nature du problème. Bien sûr, la nature du problème cible est souvent inconnue, il faut donc «essayer diverses choses».
Eh bien, cela fait longtemps, en disant que c'est facile, mais il est temps d'expliquer les points de DEAP.
Jetons un coup d'œil à la Présentation. Il y a d'abord l'expression individuelle. Lorsque nous disons «expression individuelle», nous devons considérer «comment mettre en œuvre le gène» et «comment mettre en œuvre le degré d'aptitude».
Dans DEAP, la classe qui représente le degré de conformité est définie en premier. C'est déjà intéressant d'ici.
from deap import base, creator
creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
L'aperçu dit: "Cela créera une classe FitnessMin." Non, attendez, que signifie "créer une classe"? N'est-ce pas un appel de fonction? Vous ne pensez pas cela? Ensuite, jetez un œil à creator.create reference (le lien est en anglais)
deap.creator.create(name, base[, attribute[, ...]]) Créez une classe nommée nom qui hérite de base du module créateur. Vous pouvez définir les attributs spécifiés par les arguments de mot-clé après le troisième argument de cette classe. Si l'argument est une classe, l'objet de la classe passé dans l'argument sera créé et défini dans l'attribut lors de la création de l'objet de la classe à créer. Si l'argument n'est pas une classe, il sera défini comme un attribut statique de la classe de création.
La référence montre une définition de classe équivalente à l'écriture de creator.create
pour la classe Foo, mais la précédente creator.create (" FitnessMin ", base.Fitness, weights = (-1.0,))
Si vous l'appliquez de la même manière, ce sera comme suit.
Sera exécuté dans le module créateur
class FitnessMin(base.Fitness):
weights = (-1.0,)
Je savais comment cela fonctionnerait. Mais comment vas-tu? C'est là que ** Python a le concept que les classes sont aussi des objets de première classe ** [^ 1]. [Section d'objet principal de WIkipedia](https://ja.wikipedia.org/wiki/%E7%AC%AC%E4%B8%80%E7%B4%9A%E3%82%AA%E3% 83% 96% E3% 82% B8% E3% 82% A7% E3% 82% AF% E3% 83% 88), mais le plus important est ici.
[^ 1]: Aussi appelé objet de première classe, nous utiliserons le mot «classe» dans cet article car il est différent de la «classe» orientée objet.
Peut être construit au moment de l'exécution.
Oui, Python peut définir des classes lors de l'exécution. C'est rarement nécessaire lors de l'écriture d'un programme normalement, mais il est utile de pouvoir le faire lors de l'écriture d'un framework. Ensuite, jetons un coup d'œil aux Fonctions de type Référence Python.
class type(name, bases, dict) S'il a trois arguments, il renvoie un nouvel objet de type. C'est essentiellement une forme dynamique de déclaration de classe. La chaîne de nom est le nom de la classe et a l'attribut name. Le taple de base est une liste de classes de base qui ont l'attribut bases. Le dictionnaire dict est l'espace de noms qui contient la définition du corps de la classe et est copié dans le dictionnaire standard pour l'attribut dict.
creator.create
utilise cette fonction de type pour [définir dynamiquement la classe](https://github.com/DEAP/deap/blob/1.3.0/deap/creator.py#L143 -L171). La fonction globals renvoie un dictionnaire de "variables globales dans un module". En utilisant cela, la classe est définie en l'affectant (elle ne peut pas être référencée à partir d'autres simplement en la créant avec la fonction type).
Ne devrions-nous pas simplement définir la classe normalement? Ou que diriez-vous de créer une classe dans le module fourni? Je veux creuser dans diverses choses, mais c'est intéressant donc ça va (rires)
La source d'héritage base.Fitness est également intéressante, mais j'expliquerai le point moe un peu plus tard.
L'aperçu définit ensuite la classe individuelle.
creator.create("Individual", list, fitness=creator.FitnessMin)
Les classes suivantes seront définies dans le module créateur par l'opération de creator.create
expliquée ci-dessus.
class Individual(list):
def __init__(self):
self.fitness = FitnessMin()
Le point important ici est que "un individu est une liste [^ 2]. Ce qu'il faut mettre dans la liste n'a pas été décidé à ce stade."
[^ 2]: Il est possible de faire autre chose qu'une liste de superclasse, et le document donne également un exemple d'héritage d'un ensemble.
GA génère généralement au hasard les premiers individus pour démarrer la boucle de sélection, de croisement et de mutation. La partie suivante définit la définition correspondante. Pour être précis, à ce stade, l'individu réel n'a pas été généré, juste la définition.
import random
from deap import tools
IND_SIZE = 10
toolbox = base.Toolbox()
toolbox.register("attribute", random.random)
toolbox.register("individual", tools.initRepeat, creator.Individual,
toolbox.attribute, n=IND_SIZE)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
Un nouvel élément est apparu. Classe de boîte à outils. Comme le nom de la boîte à outils l'indique, elle semble s'inscrire pour cela. Pour l'instant, jetez un œil à la référence Toolbox.register (https://deap.readthedocs.io/en/master/api/base.html#deap.base.Toolbox.register).
register(alias, method[, argument[, ...]]) Enregistrez la fonction (argument de méthode) avec l'alias de nom. Vous pouvez définir les arguments par défaut à transmettre à la fonction enregistrée. Il peut être écrasé lorsque la fonction est appelée.
Vérifiez à nouveau le code enregistré sous le nom «individuel».
toolbox.register("individual", tools.initRepeat, creator.Individual,
toolbox.attribute, n=IND_SIZE)
Vous devez consulter la référence tools.initRepeat (https://deap.readthedocs.io/en/master/api/tools.html#deap.tools.initRepeat) pour voir ce que cela fait Il y a.
deap.tools.initRepeat(container, func, n) Appelez la fonction func n fois et appelez la fonction conteneur avec elle comme argument.
Correspondant à la position de container
est ʻIndividual. C'était une liste (une classe qui a hérité).
func est
toolbox.attribute. Ceci est juste un autre nom pour
random.random. À partir de là, la méthode suivante est définie en appelant
Toolbox.register (" individual ", tools.initRepeat, creator.Individual, toolbox.attribute, n = IND_SIZE)`.
def individual():
return tools.initRepeat(creator.Individual, random.random, n=IND_SIZE)
"""
Si le traitement de initRepeat est encore développé, ce sera comme suit.
return Individual([random.random() for _ in range(IND_SIZE)])
"""
En d'autres termes, en appelant ʻindividual () `, nous définissons une méthode qui génère 10 nombres aléatoires de [0, 1) et les emballe dans un individu. Cela signifie que "ce qu'il faut mettre dans la liste" a été défini en réponse au "ce qu'il faut mettre dans la liste n'a pas été décidé à ce stade" que j'ai écrit plus tôt. Cela affecte un peu la structure du programme selon que GA du gène binaire est effectué ou GA de la valeur réelle est effectué, mais dans DEAP, c'est l'un des points moe que "ce qui entre réellement dans la liste" peut être défini relativement facilement. ..
Eh bien, la partie intéressante est d'ici. Dans Toolbox.register
," Définir l'argument par défaut sur la fonction passée et lui donner un alias "est terminé. Pourquoi est-ce possible? Ce qui ressort ici est l'idée de ** application partielle **. Pour l'instant, jetons un œil au Toolbox.register code. Collez celui extrait uniquement dans la partie importante.
py:deap/py:base.De py
def register(self, alias, function, *args, **kargs):
pfunc = partial(function, *args, **kargs)
setattr(self, alias, pfunc)
partial est une fonction fournie par le module functools.
functools.partial(func, /, *args, **keywords) Renvoie un nouvel objet partiel. Lorsqu'il est appelé, cet objet se comporte comme une fonction appelée avec l'argument positionnel args et les mots-clés de l'argument mot-clé.
Jetons un coup d'œil à la définition de «population» pour générer une population pour expliquer ce qui est amusant avec une application partielle. La définition d '«individu» est également réimprimée à des fins de comparaison.
toolbox.register("individual", tools.initRepeat, creator.Individual,
toolbox.attribute, n=IND_SIZE)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
Dans ʻindividual, "tous les arguments sont spécifiés" dans ʻinitRepeat
, mais dans population
, "n n'est pas spécifié". Par conséquent, vous devez spécifier n lorsque vous appelez population
.
pop = toolbox.population(n=50)
Lors de la résolution d'un problème avec GA, si vous décidez de l'expression (longueur du gène) du gène pour le problème cible, cela ne change généralement pas. En revanche, si le nombre d'individus est petit ou grand, il sera au milieu et "paramètres à changer de diverses manières". Par conséquent, n est spécifié à chaque fois. Avec une application partielle, vous pouvez créer une fonction plus spécialisée à l'aide d'une fonction à usage général et décrire le processus qui l'utilise.
Maintenant que vous savez ce que fait le reste de la vue d'ensemble, je vais vous l'expliquer pour l'instant.
Il a expliqué que GA essaiera diverses méthodes de sélection, de croisement et de mutation. Chaque méthode a des paramètres spécifiques à la méthode. En revanche, dans le cas du croisement, "prend deux individus comme arguments et renvoie deux descendants", et dans le cas d'une mutation, "prend un individu comme argument et renvoie un individu muté". , S'il est sélectionné, "entrée / sortie générale de l'opération" telle que "prend la population et le nombre de survivants comme arguments et renvoie les individus survivants" est décidé. Une application partielle est également utilisée pour ces derniers, et une méthode est créée dans laquelle les paramètres uniques à la méthode sont partiellement appliqués à chacune des méthodes préparées.
def evaluate(individual):
return sum(individual),
toolbox.register("mate", tools.cxTwoPoint)
toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=1, indpb=0.1)
toolbox.register("select", tools.selTournament, tournsize=3)
toolbox.register("evaluate", evaluate)
À ce stade, il est important de nommer mate, muter, sélectionner, évaluer lors de l'utilisation de "tout l'algorithme qui transforme GA". EaSimple, qui est l'un des "algorithmes complets", dit ce qui suit: ..
deap.algorithms.eaSimple(population, toolbox, cxpb, mutpb, ngen[, stats, halloffame, verbose]) Cette fonction suppose que l'accouplement, la mutation, la sélection et l'évaluation sont enregistrés dans la boîte à outils.
Cela signifie qu'il peut être appelé correctement en lui donnant un nom que le côté du framework attend.
Je l'ai écrit pendant longtemps et je l'ai oublié moi-même, mais le dernier point est le point le plus basique. Si vous regardez Tutoriels: Partie 2, vous trouverez quelque chose d'intéressant.
ind1.fitness.values = evaluate(ind1)
print ind1.fitness.valid # True
Pourquoi valid devient True lorsqu'il est attribué à des valeurs? Ceci est réalisé avec ** Propriétés **. De plus, les valeurs elles-mêmes sont en fait des propriétés. Ici, lorsque l'affectation est faite, celle multipliée par les poids définis au début est conservée. [^ 3]
[^ 3]: Afin de gérer à la fois le problème de minimisation et le problème de maximisation de manière unifiée en inversant le code.
implémentation valide Implémentation des valeurs
Pourquoi y a-t-il quelque chose appelé valide comme ça? Ceci est dû au fait que les croisements et les mutations ont leurs propres taux de croisement et de mutation, respectivement, et en réalité, il y a des cas où «des gènes qui n'ont ni croisé ni muté peuvent être produits». Puisqu'il s'agit du même gène, la valeur de la fonction objective est naturellement la même. Dans ce cas, il est inutile de recalculer et de l'éviter [^ 4].
[^ 4]: Il semble que certaines questions de test prennent plusieurs minutes si ce sont de vraies questions. C'est une histoire sur la puissance de calcul que j'ai entendue il y a longtemps.
Jusqu'à présent, nous avons examiné l'arrière-plan profond de DEAP. Certains des mystères Python que vous ne voyez généralement pas et que vous pourrez peut-être utiliser quelque part si vous les connaissez sont:
--Définition dynamique de la classe à l'aide de la fonction de type
J'adore les frameworks qui utilisent pleinement les fonctionnalités du langage, mais j'ai l'impression que DEAP utilise également pleinement Python.
Recommended Posts