Kennen Sie das evolutionäre Berechnungsframework namens DEAP? e? Evolutionsberechnung vor DEAP Wie ist dieser Zustand? Nun, das vorausgesetzte Wissen über evolutionäre Berechnungen ist in diesem Artikel nicht sehr relevant. Der Hauptzweck dieses Artikels ist es zu erklären, wie gut DEAP Python verwendet, um evolutionäre Berechnungen zu implementieren und die Python-Leistung zu verbessern.
Selbst wenn Sie ohne Vorkenntnisse OK sagen, müssen Sie die Wörter verstehen, die danach erscheinen. Daher werde ich kurz die evolutionären Berechnungen erläutern, insbesondere den zur Erklärung verwendeten genetischen Algorithmus (GA).
GA ist ein Algorithmus, der von der Evolution lebender Organismen inspiriert ist, da er als "genetisch" bezeichnet wird. Warum überleben die Lebewesen? Es gibt verschiedene Gründe, aber die folgenden drei sind die Hauptfaktoren bei GA.
Übrigens schrieb ich, dass starke Kreaturen nach Wahl überleben, aber "stark" bedeutet "hohe Fitness". Als nächstes lautet "Was ist der Grad der Konformität?", In GA ist es jedoch der "Wert der Zielfunktion, die Sie lösen möchten". Es ist $ f (\ vec {x}) $. Als nächstes folgt die Geschichte von $ \ vec {x} $.
Ich kam schließlich zum "genetischen" Teil des genetischen Algorithmus. "Vererbung" ist ein Gen. Dieses Gen ist $ \ vec {x} $, wenn es mathematisch geschrieben wird. Die Expression des Gens ist grob zweigeteilt, und bei der Berechnung von $ f (\ vec {x}) $ wird sie durch eine binäre Zeichenfolge von 0/1 ausgedrückt und durch Konvertieren von 0/1 in den Definitionsbereich der Zielfunktion berechnet. Es ist eine Methode namens Real Value GA, die den numerischen Wert als "Gen" verwendet.
In ** Crossing ** schrieb ich, dass die Gene meiner Eltern vererbt werden, aber ich schneide die Gene wie folgt aus und füge sie ein, um ein Kind zu machen. Wo geschnitten werden soll, wird normalerweise durch eine Zufallszahl bestimmt.
Vater: 01101101 Mutter: 11000011
In der Mitte ausschneiden und einfügen (Kind 1 ist die linke Hälfte des Vaters + rechte Hälfte der Mutter, Kind 1 ist die linke Hälfte der Mutter + rechte Hälfte des Vaters)
Kind 1: 01100011 Kind 2: 11001101
** Mutation ** hat eine gewisse Wahrscheinlichkeit, die Bits zu invertieren.
Nach der Kreuzung Mutation von Kind 1 (linkes Ende von 0 auf 1 geändert)
Kind 1: 11100011
Ob das auf diese Weise geschaffene Kind überlebt oder nicht, hängt vom Eignungsgrad (objektiver Funktionswert) des Kindes ab. Dies ist ** Auswahl **. Drehen Sie diese Auswahl, Überkreuzung und Mutation. Dann ist die Idee von GA, dass die Individuen mit hoher Eignung (hoher Zielfunktionswert) überleben und der gewünschte $ \ vec {x} $ (Entwurfsvariablenwert) erhalten wird.
Wie man kreuzt (die Kreuzungstechnik verwendet), mutiert oder wählt, sollte von der Art des Problems abhängen. Natürlich ist die Art des Zielproblems oft unbekannt, daher muss man "verschiedene Dinge ausprobieren".
Nun, es ist lange her, dass es einfach ist, aber es ist Zeit, die Punkte von DEAP zu erklären.
Werfen wir einen Blick auf die Übersicht. Erstens ist der individuelle Ausdruck. Im Fall der "individuellen Expression" ist es notwendig zu überlegen, "wie das Gen implementiert wird" und "wie der Konformitätsgrad implementiert wird".
In DEAP wird zuerst die Klasse definiert, die den Konformitätsgrad darstellt. Von hier aus ist es schon interessant.
from deap import base, creator
creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
In der Übersicht heißt es: "Dadurch wird eine FitnessMin-Klasse erstellt." Nein, warte, was bedeutet es, eine Klasse zu erstellen? Ist es nicht ein Funktionsaufruf? Glaubst du das nicht? Schauen Sie sich als Nächstes [creatore.create reference] an (https://deap.readthedocs.io/en/master/api/creator.html#deap.creator.create) (Link ist in Englisch)
deap.creator.create(name, base[, attribute[, ...]]) Erstellen Sie eine Klasse mit dem Namen name, die die Basis vom Erstellermodul erbt. Sie können die Attribute definieren, die durch die Schlüsselwortargumente nach dem dritten Argument in dieser Klasse angegeben werden. Wenn das Argument eine Klasse ist, wird das Objekt der im Argument übergebenen Klasse erstellt und beim Erstellen des Objekts der zu erstellenden Klasse im Attribut festgelegt. Wenn das Argument keine Klasse ist, wird es als statisches Attribut der Erstellungsklasse festgelegt.
Die Referenz zeigt eine Klassendefinition, die dem Schreiben von "creator.create" für die Foo-Klasse entspricht, aber die vorherige "creator.create" ("FitnessMin", base.Fitness, weight = (-1.0,)) Wenn Sie es auf die gleiche Weise anwenden, ist es wie folgt.
Wird im Erstellermodul ausgeführt
class FitnessMin(base.Fitness):
weights = (-1.0,)
Ich wusste, wie es funktionieren würde. Aber wie geht es dir? Hier hat ** Python das Konzept, dass Klassen auch erstklassige Objekte sind ** [^ 1]. [Abschnitt mit primären WIkipedia-Objekten](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), aber das wichtigste ist hier.
[^ 1]: Auch als erstklassiges Objekt bezeichnet, verwenden wir in diesem Artikel das Wort "Klasse", da es sich von der objektorientierten "Klasse" unterscheidet.
Kann zur Laufzeit erstellt werden.
Ja, Python kann zur Laufzeit Klassen definieren. Es wird selten benötigt, wenn ein Programm normal geschrieben wird, aber es ist nützlich, dies beim Schreiben eines Frameworks tun zu können. Schauen wir uns als nächstes [Funktionen des Python-Referenztyps] an (https://docs.python.org/ja/3/library/functions.html#type).
class type(name, bases, dict) Wenn es drei Argumente hat, gibt es ein neues Typobjekt zurück. Es ist im Wesentlichen eine dynamische Form der Klassenanweisung. Die Namenszeichenfolge ist der Klassenname und hat das Attribut name. Das Basistapel ist eine Liste von Basisklassen mit dem Attribut bases. Das Diktatwörterbuch ist der Namespace, der die Definition des Klassenkörpers enthält und in das Standardwörterbuch für das Attribut dict kopiert wird.
creator.create
verwendet diese Typfunktion, um die Klasse dynamisch zu definieren -L171). Die Funktion globals gibt ein Wörterbuch mit "globalen Variablen in einem Modul" zurück. Auf diese Weise wird die Klasse durch Zuweisen definiert (sie kann nicht von anderen referenziert werden, indem sie nur mit der Typfunktion erstellt wird).
Sollten wir die Klasse nicht einfach normal definieren? Oder wie wäre es mit einer Klasse im bereitgestellten Modul? Ich möchte in verschiedenen Dingen graben, aber es ist interessant, also ist es in Ordnung (lacht)
Die Vererbungsquellenbasis. Zeugen sind ebenfalls interessant, aber ich werde den nächsten Punkt etwas später erläutern.
Die Übersicht definiert dann die einzelne Klasse.
creator.create("Individual", list, fitness=creator.FitnessMin)
Die folgenden Klassen werden im Erstellermodul durch die oben erläuterte Operation "creator.create" definiert.
class Individual(list):
def __init__(self):
self.fitness = FitnessMin()
Der wichtige Punkt hierbei ist, dass "eine Person eine Liste ist [^ 2]. Was in die Liste aufgenommen werden soll, wurde zu diesem Zeitpunkt noch nicht entschieden."
[^ 2]: Es ist möglich, etwas anderes als eine Liste zu einer Oberklasse zu machen, und das Dokument enthält auch ein Beispiel für das Erben einer Menge.
GA generiert normalerweise zufällig frühe Individuen, um die Schleife von Selektion, Crossover und Mutation zu starten. Der folgende Teil definiert die entsprechende Definition. Um genau zu sein, wurde zu diesem Zeitpunkt nicht das tatsächliche Individuum generiert, sondern nur die Definition.
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)
Ein neues Element ist entstanden. Toolbox-Klasse. Wie der Name der Toolbox andeutet, scheint sie sich dafür zu registrieren. Sehen Sie sich zunächst die Toolbox.register-Referenz an (https://deap.readthedocs.io/en/master/api/base.html#deap.base.Toolbox.register).
register(alias, method[, argument[, ...]]) Registrieren Sie die Funktion (Methodenargument) mit dem Namensalias. Sie können die Standardargumente festlegen, die an die registrierte Funktion übergeben werden sollen. Sie kann beim Aufruf der Funktion überschrieben werden.
Überprüfen Sie den unter dem Namen "Person" registrierten Code erneut.
toolbox.register("individual", tools.initRepeat, creator.Individual,
toolbox.attribute, n=IND_SIZE)
Sie müssen sich die tools.initRepeat-Referenz (https://deap.readthedocs.io/en/master/api/tools.html#deap.tools.initRepeat) ansehen, um zu sehen, was dies bewirkt Es gibt.
deap.tools.initRepeat(container, func, n) Rufen Sie die Funktionsfunktion n-mal auf und rufen Sie die Containerfunktion damit als Argument auf.
Die "Container" -Position entspricht der "Person". Dies war eine Liste (eine Klasse, die geerbt hat).
func
ist toolbox.attribute
. Dies ist nur ein anderer Name für random.random
.
Daraus wird die folgende Methode durch Aufrufen von "Toolbox.register (" individual ", tools.initRepeat, creator.Individual, toolbox.attribute, n = IND_SIZE)" definiert.
def individual():
return tools.initRepeat(creator.Individual, random.random, n=IND_SIZE)
"""
Wenn die Verarbeitung von initRepeat weiter erweitert wird, ist dies wie folgt.
return Individual([random.random() for _ in range(IND_SIZE)])
"""
Mit anderen Worten, indem wir "individual ()" aufrufen, definieren wir eine Methode, die 10 Zufallszahlen von [0, 1) generiert und diese in eine Person packt. Dies bedeutet, dass "was in die Liste aufgenommen werden soll" als Antwort auf das "was in die Liste aufgenommen werden soll, wurde zu diesem Zeitpunkt noch nicht entschieden" definiert wurde, das ich zuvor geschrieben habe. Es beeinflusst die Programmstruktur ziemlich stark, abhängig davon, ob GA des binären Gens oder GA des realen Werts durchgeführt wird, aber einer der wichtigsten Punkte ist, dass DEAP relativ leicht definieren kann, "was tatsächlich in die Liste aufgenommen wird". ..
Nun, der interessante Teil ist von hier. In Toolbox.register
wird" Setzen Sie das Standardargument auf die übergebene Funktion und geben Sie ihr einen Alias ". Warum kann das gemacht werden? Was hier herauskommt, ist die Idee der ** Teilanwendung **. Schauen wir uns zunächst Toolbox.register-Code an. Fügen Sie den extrahierten nur in den wichtigen Teil ein.
py:deap/py:base.Von py
def register(self, alias, function, *args, **kargs):
pfunc = partial(function, *args, **kargs)
setattr(self, alias, pfunc)
teilweise ist eine Funktion, die vom functools-Modul bereitgestellt wird.
functools.partial(func, /, *args, **keywords) Gibt ein neues Teilobjekt zurück. Beim Aufruf verhält sich dieses Objekt wie eine Funktion, die mit den Positionsargumentargumenten und den Schlüsselwortargument-Schlüsselwörtern aufgerufen wird.
Werfen wir einen Blick auf die Definition von "Population", um eine Population zu generieren und zu erklären, was bei teilweiser Anwendung Spaß macht. Die Definition von "Individuum" wird ebenfalls zum Vergleich nachgedruckt.
toolbox.register("individual", tools.initRepeat, creator.Individual,
toolbox.attribute, n=IND_SIZE)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
In "individual" werden "alle Argumente angegeben" in "initRepeat", in "populations" wird "n nicht angegeben". Daher müssen Sie beim Aufruf von "Population" n angeben.
pop = toolbox.population(n=50)
Wenn Sie bei der Lösung eines Problems mit GA die Expression (Genlänge) des Gens für das Zielproblem bestimmen, ändert sich dies normalerweise nicht. Wenn andererseits die Anzahl der Personen klein oder groß ist, liegt sie in der Mitte und "Parameter müssen auf verschiedene Weise geändert werden". Daher wird jedes Mal n angegeben. Bei teilweiser Anwendung können Sie mithilfe einer Universalfunktion eine speziellere Funktion erstellen und den Prozess damit beschreiben.
Jetzt, da Sie wissen, was der Rest der Übersicht tut, werde ich es vorerst erklären.
Er erklärte, dass GA verschiedene Methoden zur Selektion, Überkreuzung und Mutation ausprobieren werde. Jede Methode hat methodenspezifische Parameter. Auf der anderen Seite "nimmt im Falle einer Kreuzung" zwei Individuen als Argumente und gibt zwei Nachkommen zurück "und im Fall einer Mutation" nimmt eine Person als Argument und gibt eine mutierte Person zurück ". Wenn es ausgewählt ist, wird "allgemeine Eingabe / Ausgabe der Operation" wie "nimmt die Bevölkerung und die Anzahl der Überlebenden als Argumente und gibt die überlebenden Personen zurück" entschieden. Für diese wird auch eine teilweise Anwendung verwendet, und es wird eine Methode erstellt, bei der die für die Methode eindeutigen Parameter teilweise auf jede der vorbereiteten Methoden angewendet werden.
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)
Zu diesem Zeitpunkt ist es wichtig, Partner zu benennen, zu mutieren, auszuwählen und zu bewerten, wenn "der gesamte Algorithmus verwendet wird, der GA dreht". Einer der "ganzen Algorithmen" eaSimple sagt: ..
deap.algorithms.eaSimple(population, toolbox, cxpb, mutpb, ngen[, stats, halloffame, verbose]) Diese Funktion setzt voraus, dass Mate, Mutate, Select und Evaluate in der Toolbox registriert sind.
Dies bedeutet, dass es ordnungsgemäß aufgerufen werden kann, indem ihm ein Name gegeben wird, den die Framework-Seite erwartet.
Ich habe es lange geschrieben und selbst vergessen, aber der letzte Punkt ist der wichtigste Punkt der Basis. Wenn Sie sich Tutorials: Part 2 ansehen, werden Sie etwas Interessantes finden.
ind1.fitness.values = evaluate(ind1)
print ind1.fitness.valid # True
Warum wird gültig wahr, wenn es Werten zugewiesen wird? Dies wird mit ** Eigenschaften ** erreicht. Darüber hinaus sind Werte selbst tatsächlich Eigenschaften. Hier bleibt bei der Zuordnung diejenige erhalten, die mit den zu Beginn definierten Gewichten multipliziert wird. [^ 3]
[^ 3]: Um sowohl das Minimierungsproblem als auch das Maximierungsproblem durch Umkehren des Codes einheitlich zu behandeln.
gültige Implementierung Implementierung von Werten
Warum gibt es so etwas wie gültig? Dies liegt daran, dass Crossovers und Mutationen ihre eigenen Crossover-Raten bzw. Mutationsraten haben und in Wirklichkeit "Gene erzeugt werden können, die nicht gekreuzt oder mutiert wurden". Da es sich um dasselbe Gen handelt, ist der Zielfunktionswert natürlich der gleiche. In diesem Fall ist es sinnlos, neu zu berechnen und zu vermeiden [^ 4].
[^ 4]: Es scheint, dass einige Testfragen einige Minuten dauern, wenn es sich um echte Fragen handelt. Es ist eine Geschichte über die Rechenleistung, die ich vor langer Zeit gehört habe.
Bisher haben wir uns die tiefe Rückseite von DEAP angesehen. Einige der Python-Rätsel, die Sie normalerweise nicht sehen und die Sie möglicherweise irgendwo verwenden können, wenn Sie sie kennen, sind:
--Dynamische Definition der Klasse mit der Typfunktion
Ich liebe Frameworks, die die Sprachfunktionen voll ausnutzen, aber es scheint, dass DEAP auch Python voll ausnutzt.
Recommended Posts