Website-Suche

Arbeiten mit verschiedenen genetischen Algorithmus-Darstellungen in Python


Je nach Art des zu optimierenden Problems unterstützt der genetische Algorithmus (GA) zwei verschiedene Genrepräsentationen: binär und dezimal. Der binäre GA hat nur zwei Werte für seine Gene, nämlich 0 und 1. Dies ist einfacher zu handhaben, da die Genwerte im Vergleich zum dezimalen GA, bei dem wir verschiedene Formate wie Float oder Ganzzahl und begrenzte oder unbegrenzte Bereiche verwenden können, eingeschränkt sind.

Dieses Tutorial behandelt, wie die PyGAD-Bibliothek die beiden GA-Darstellungen, binär und dezimal, unterstützt. Der Verlauf dieses Tutorials ist wie folgt:

  • Einführung in PyGAD
  • Die Kontrolle des Genumfangs in der Ausgangspopulation
  • Gen-Typ (int oder float)
  • Vermeiden, die ursprüngliche Reichweite zu überschreiten
  • Kontinuierliche und diskrete Genbereiche
  • Benutzerdefinierte Werte für jedes Gen
  • Einige Gene anpassen, während andere randomisiert werden.
  • Der binäre genetische Algorithmus
  • Benutzervorgedefinierte Anfangspopulation

Einführung

PyGAD ist eine Python-Bibliothek zur Implementierung des genetischen Algorithmus. Um es zu installieren und loszulegen, schauen Sie sich das Tutorial 5 Anwendungen des genetischen Algorithmus mit PyGAD an. Wie der Name schon sagt, zeigen wir Ihnen, wie Sie fünf verschiedene Anwendungen mit der Bibliothek entwickeln.

Beim Aufbau eines spielenden Agenten für CoinTex mithilfe des genetischen Algorithmus wurde PyGAD verwendet, um einen Agenten zu erstellen, der ein Spiel namens CoinTex spielt.

PyGAD ist auch in Read the Docs dokumentiert.

Bevor Sie mit diesem Tutorial beginnen, stellen Sie sicher, dass Sie mindestens die PyGad-Version 2.6.0 installiert haben.

pip install pygad>=2.6.0

Der nächste Abschnitt behandelt, wie man PyGAD verwendet, um den Wertebereich für die Gene anzupassen.

Steuere den Bereich des Gens in der Ausgangspopulation

Wie wir besprochen haben, hat der GA zwei Darstellungen für seine Gene:

  1. Binär
  2. Dezimal

Für das binäre GA hat jedes Gen nur zwei Werte: 0 oder 1. Auf der anderen Seite kann die dezimale Darstellung jeden Dezimalwert für das Gen verwenden. In diesem Abschnitt wird erörtert, wie der Wertebereich der Gene eingeschränkt werden kann.

In einigen Problemen kann es für einen Benutzer nützlich sein, den Bereich gültiger Genwerte einzuschränken. Zum Beispiel, sagen wir, dass jeder Genwert zwischen 5 und 15 liegen muss, ohne Ausnahmen. Wie können wir das erreichen?

PyGAD unterstützt zwei Parameter zur Handhabung dieses Szenarios: init_range_low und init_range_high. Der Parameter init_range_low gibt die untere Grenze des Bereichs an, aus dem die Anfangspopulation erstellt wird, während init_range_high die obere Grenze angibt. Beachten Sie, dass init_range_high exklusiv ist. Wenn also init_range_low=5 und init_range_high=15 ist, dann sind die möglichen Genwerte von 5 bis, aber nicht einschließlich, 15. Dieses Beispiel wird im folgenden Codeausschnitt gezeigt.

Neben diesen Argumenten müssen wir auch unsere Fitnessfunktion, fitness_func, und die Anzahl der Generationen, num_generations, angeben.

Obwohl sie optional sind, müssen die nächsten drei Parameter angegeben werden, wenn die anfängliche Population zufällig erstellt wird:

  • num_parents_mating: Anzahl der Eltern zu paaren.
  • sol_per_pop: Auf 3 gesetzt, was bedeutet, dass die Population 3 Lösungen hat.
  • num_genes: Jede Lösung hat 4 Gene.
import pygad

def fitness_function(solution, solution_idx):
    return sum(solution)

ga_instance = pygad.GA(num_generations=1,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=5,
                       init_range_high=15)

Sobald die Instanz erstellt ist, wird die zufällige Anfangspopulation im Attribut initial_population vorbereitet. Die folgende Zeile gibt sie aus. Da die Bevölkerung 3 Lösungen hat, wobei jede Lösung 4 Gene hat, hat die Bevölkerung die Form (3, 4). Beachten Sie, dass jedes Gen einen Wert zwischen 5 und 15 hat. Standardmäßig ist der Typ der Gene float.

print(ga_instance.initial_population)
print(ga_instance.initial_population.shape)
[[14.02138539 10.13561641 13.77733116 5]
 [13.28398269 14.13789428 12.6097329   7.51336248]
 [ 9.42208693  6.97035939 14.54414418  6.54276097]]

(3, 4)

Der folgende Code setzt init_range_low auf 1 und init_range_high auf 3, um zu sehen, wie sich der Bereich der Gene verändert.

ga_instance = pygad.GA(num_generations=1,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=1,
                       init_range_high=3)

print(ga_instance.initial_population)

Wie unten angegeben, hat die zufällig erstellte Population alle Gene zwischen 1 und 3. Es ist möglich, ein Gen mit dem Wert 1 zu haben, aber es ist unmöglich, einen Wert von 3 zu haben.

[[1.00631559 2.91140666 1.30055502 2.10605866]
 [2.23160212 2.32108812 1.90731624 1]
 [2.23293791 1.9496456  1.25106388 2.46866602]]

Beachten Sie, dass die Parameter init_range_low und init_range_high lediglich den Bereich der Gene in der Anfangspopulation begrenzen. Was passiert, wenn sich die Lösungen über mehrere Generationen entwickeln? Dies könnte dazu führen, dass die Gene den anfänglichen Bereich überschreiten.

Um ein Experiment durchzuführen, wird der Parameter num_generations auf 10 gesetzt und die Methode run() wird aufgerufen, um die Lösungen durch die 10 Generationen zu entwickeln.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=1,
                       init_range_high=3)

ga_instance.run()

Nachdem die run()-Methode abgeschlossen ist, druckt der nächste Code Folgendes aus:

  • Die ursprüngliche Bevölkerung unter Verwendung des Attributs initial_population.
  • Die endgültige Bevölkerung unter Verwendung des Attributs population.
print(ga_instance.initial_population)
print(ga_instance.population)
[[1.08808272 1.16951518 1.30742402 1.40566555]
 [2.88777068 2.49699173 2.47277427 2.36010308]
 [1.94598736 2.10177613 1.57860387 1.45981019]]

[[3.7134492  1.9735615  3.39366783 2.21956642]
 [3.7134492  2.49699173 2.47277427 2.36010308]
 [2.94450144 1.9735615  3.39366783 2.36010308]]

Für die anfängliche Population liegen alle Gene zwischen 1 und 3. In der endgültigen Population überschritten einige Gene den Bereich, wie das erste und dritte Gen in der ersten Lösung. Wie kann man die Gene in jeder Population dazu zwingen, innerhalb des Bereichs zu bleiben? Dies wird im Abschnitt Den Bereich nicht überschreiten besprochen.

Eine weitere Anmerkung ist, dass der Typ des Gens Fließkommazahlen ist. Einige Probleme funktionieren möglicherweise nur mit Ganzzahlen. Der nächste Abschnitt behandelt, wie man den Typ der Gene mit dem gene_type-Parameter angibt.

Gen-Typ (int oder float)

Standardmäßig weist PyGAD der anfänglichen Population zufällige Fließkommawerte zu. Falls der Benutzer möchte, dass die Werte Ganzzahlen sind, steht dafür der Parameter gene_type zur Verfügung. Dies wird in PyGAD 2.6.0 und höheren Versionen unterstützt.

Es unterstützt 2 Werte:

  1. float: Es ist der Standardwert. Das bedeutet, dass die Gene Fließkommazahlen sind.
  2. Die Gene werden von Fließkommazahlen in Ganzzahlen umgewandelt.

Der nächste Code setzt den Parameter gene_type auf int, um die zufällige Anfangspopulation dazu zu zwingen, ganzzahlige Gene zu haben.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=1,
                       init_range_high=3,

                       gene_type=int)

print(ga_instance.initial_population)

Die zufällige Anfangspopulation ist unten abgedruckt. Beachten Sie, dass der Bereich des Gens von 1 bis 3 ausgeschlossen ist. Das bedeutet, dass 1 und 2 die einzigen Ganzzahlen sind. Daher hat die Population nur die Werte 1 und 2.

[[1 1 2 1]
 [1 2 2 1]
 [1 2 1 2]]

Wenn der Bereich von 5 bis 10 geändert wird, dann sind die möglichen Genwerte 5, 6, 7, 8 und 9.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=5,
                       init_range_high=10,

                       gene_type=int)

print(ga_instance.initial_population)
[[5 9 7 8]
 [5 7 9 8]
 [5 5 6 7]]

Beachten Sie, dass das Setzen des gene_type-Parameters auf entweder int oder float nicht verhindert, dass die Gene den Bereich überschreiten, der mit den init_range_low- und init_range_high-Parametern festgelegt ist. Dies wird im nächsten Abschnitt besprochen.

Vermeiden Sie es, den ursprünglichen Bereich zu überschreiten

Die zufällig erzeugte Anfangspopulation hat ihre Gene im Bereich der beiden Parameter init_range_low und init_range_high. Aber das garantiert nicht, dass ihre Gene immer innerhalb dieses Bereichs liegen. Der Grund dafür ist, dass die Werte der Gene sich aufgrund der Mutationsoperation ändern.

Standardmäßig wird die Mutationsoperation vom Typ random auf alle Gene angewendet. Dies führt zu einigen zufälligen Änderungen der Gene, die dazu führen, dass ihre Werte den ursprünglichen Bereich überschreiten. Je nach Art des zu lösenden Problems kann das Überschreiten des Bereichs ein Problem darstellen oder nicht.

Wenn das Problem seine Gene innerhalb eines bestimmten Bereichs haben muss, gibt es verschiedene Optionen, um alle Gene in allen Generationen innerhalb dieses Bereichs zu erzwingen. Diese Optionen sind wie folgt zusammengefasst:

  1. Verwenden Sie keine random Mutation.
  2. Deaktivieren Sie die Mutationsoperation.
  3. Verwenden Sie den Parameter mutation_by_replacement. Dies ist die praktischste Option.

Lass uns jede dieser Optionen besprechen.

Verwenden Sie keine random Mutation

Die Art der verwendeten Mutationsoperation wird über den Parameter mutation_type angegeben. Die unterstützten Typen sind:

  1. Zufällig: mutation_type=random
  2. Tausch: mutation_type=swap
  3. Inversion: mutation_type=inversion
  4. Schütteln: mutation_type=scramble

Von diesen 4 Typen kann nur die zufällige Mutation die Genwerte außerhalb des Bereichs ändern. Eine Möglichkeit, die Gene innerhalb des ursprünglichen Bereichs zu halten, besteht darin, einen anderen Mutationstyp als die zufällige Mutation zu verwenden.

Der nächste Code verwendet die swap Mutation. Selbst nach der Ausführung der run() Methode befinden sich die Genwerte immer noch im ursprünglichen Bereich.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=5,
                       init_range_high=10,

                       mutation_type="swap",

                       gene_type=int)

ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[6 9 8 7]
 [5 5 8 8]
 [9 8 5 6]]

[[8 9 6 9]
 [8 9 6 9]
 [8 9 6 9]]

Diese Option ist in vielen Situationen möglicherweise nicht machbar, da die anderen Typen die ursprünglichen Genwerte beibehalten und nur deren Reihenfolge ändern. Es werden keine Änderungen an den Genen vorgenommen.

Deaktivieren Sie die Mutationsoperation

PyGAD kann die Mutationsoperation deaktivieren, indem der mutation_type-Parameter auf None gesetzt wird. Auch wenn es die Genwerte im ursprünglichen Bereich beibehält, deaktiviert es eine der Hauptoptionen zur evolutionären Lösung.

Der folgende Code deaktiviert die Mutationsoperation. Nach 10 Generationen liegen die Gene immer noch im festgelegten Bereich.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=5,
                       init_range_high=10,

                       mutation_type=None,

                       gene_type=int)

ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[7 9 5 9]
 [5 6 6 8]
 [8 5 6 6]]

[[7 9 5 9]
 [7 9 6 6]
 [7 9 5 6]]

Verwenden Sie den mutation_by_replacement Parameter

Die vorherigen 2 Optionen skarifizieren entweder durch die random-Mutation oder durch die Mutation selbst, um das Gen im ursprünglichen Bereich zu halten. Die praktikabelste Option, die die Verwendung der random-Mutation unterstützt und gleichzeitig die Gene innerhalb des festgelegten Bereichs hält, ist der boolean Parameter mutation_by_replacement.

Normalerweise erzeugt eine zufällige Mutation einen zufälligen Wert. Dieser Wert wird dann dem aktuellen Genwert hinzugefügt. Angenommen, es gibt ein Gen mit dem Wert 2,5 und der festgelegte Bereich liegt zwischen 1 und 3, ohne diese Werte einzuschließen. Wenn der zufällige Wert 0,7 beträgt, ergibt das Hinzufügen zu dem aktuellen Genwert 2,5+0,7=3,2, was außerhalb des Bereichs liegt.

Wenn der Parameter mutation_by_replacement auf True gesetzt ist, ersetzt er (fügt nicht hinzu) den Genwert durch den zufälligen Wert. Wenn der zufällige Wert also 0,7 ist, wird der neue Genwert 0,7 sein. Wenn der gene_type auf int gesetzt ist, wird das Ergebnis 1.0 sein.

Der Benutzer kann den Bereich steuern, aus dem der zufällige Wert generiert wird, indem er die 2 Parameter random_mutation_min_val und random_mutation_max_val verwendet, die die unteren und oberen Grenzen angeben.

Um die Gene innerhalb des Bereichs zu halten, muss jeder dieser Parameter die folgende Bedingung erfüllen:

init_range_low <= param <= init_range_high

Für das beste Erlebnis setzen Sie random_mutation_min_val=init_range_low und random_mutation_max_val=init_range_high.

Der nächste Code gibt ein Beispiel für die Verwendung von 3 Parametern, die in diesem Abschnitt besprochen wurden (mutation_by_replacement, random_mutation_min_val und random_mutation_max_val).

ga_instance = pygad.GA(num_generations=1000,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=5,
                       init_range_high=10,

                       random_mutation_min_val=5,
                       random_mutation_max_val=10,

                       mutation_by_replacement=True,

                       gene_type=int)

ga_instance.run()

Über viele Generationen hinweg werden die Gene nicht den Bereich überschreiten. Der folgende Code gibt die Anfangs- und Endbevölkerung aus. Die Gene in der Endbevölkerung überschreiten nicht den Bereich.

print(ga_instance.initial_population)
print(ga_instance.population)
[[5 8 8 5]
 [9 8 8 9]
 [5 9 8 9]]

[[9 9 9 9]
 [9 9 9 9]
 [9 9 8 9]]

Durch die Verwendung der 3 Parameter mutation_by_replacement ist es möglich, dass der GA nur mit binären Genen (d.h. Genen mit den Werten 0 und 1) funktioniert. Dies erreicht man, indem man Folgendes tut:

  • Setze init_range_low=random_mutation_min_val=0.
  • Setze init_range_high=random_mutation_max_val=2.
  • Setzen Sie mutation_by_replacement=True.
  • Setze gene_type=int.
ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=0,
                       init_range_high=2,

                       random_mutation_min_val=0,
                       random_mutation_max_val=2,

                       mutation_by_replacement=True,

                       gene_type=int)

ga_instance.run()

Hier sind die anfänglichen und endgültigen Populationen, bei denen alle Gene entweder 0 oder 1 sind.

print(ga_instance.initial_population)
print(ga_instance.population)
[[1 1 0 1]
 [0 1 0 0]
 [0 1 1 1]]

[[0 1 0 1]
 [0 1 0 0]
 [0 1 1 0]]

Bitte beachten Sie, dass dies nicht der einzige Weg ist, um den binären GA zu unterstützen. Mit dem gene_space-Parameter ist es auch möglich, den binären GA zu unterstützen. Dieser Parameter wird im nächsten Abschnitt eingeführt.

Kontinuierliche und diskrete Genbereiche

Die vorherige Diskussion geht davon aus, dass der Bereich, aus dem die Gene entnommen werden, kontinuierlich ist. Wenn der Bereich also von 1 bis 5 reicht, sind alle Werte innerhalb dieses Bereichs (1, 2, 3 und 4) akzeptabel. Was ist, wenn einige Werte innerhalb eines Bereichs nicht erlaubt sind oder die Werte nicht einem kontinuierlichen Bereich folgen (z.B. -2, 18, 43 und 78)? Zu diesem Zweck unterstützt PyGAD einen Parameter namens gene_space, um den Raum der Gene-Werte anzugeben.

Der gene_space Parameter ermöglicht es dem Benutzer, alle möglichen Genwerte aufzulisten. Er akzeptiert eine Liste oder ein Tupel, in dem alle möglichen Genwerte aufgeführt sind.

Der nächste Code verwendet den gene_space-Parameter, um die möglichen Werte für alle Gene aufzulisten. Infolgedessen werden alle Gene aus den aufgelisteten 4 Werten ausgewählt.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       gene_space=[-2, 18, 43, 78])

ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[78 43 78 -2]
 [18 -2 78 78]
 [43 43 18 -2]]

[[-2 -2 18 78]
 [18 -2 78 78]
 [-2 -2 18 78]]

Beachten Sie, dass alle Gene aus denselben Werten entnommen werden. Mit anderen Worten, die Werte im gene_space-Parameter sind global für alle Gene. Was ist, wenn jedes Gen unterschiedliche Werte hat? Im nächsten Abschnitt wird erläutert, wie individuelle Werte für jedes Gen festgelegt werden können.

Benutzerdefinierte Werte für jedes Gen

Wenn der Parameter gene_space eine nicht geschachtelte Liste/Tuple akzeptiert, werden die Werte in dieser Liste/Tuple verwendet, um die Werte aller Gene zu sampeln. Es kann vorkommen, dass einige Gene ihre eigenen unterschiedlichen Werte haben. Der Parameter gene_space akzeptiert die Werte jedes einzelnen Gens separat. Dies geschieht durch die Erstellung einer geschachtelten Liste/Tuple, in der jedes Element die möglichen Werte für das entsprechende Gen enthält.

Angenommen, es gibt 4 Gene und jedes Gen hat seinen eigenen Wertebereich. Eine Liste der möglichen Werte für jedes Gen wird wie unten angegeben vorbereitet. Beachten Sie, dass kein Gen seine Werte in einer Reihenfolge hat. Jedes Gen kann eine unterschiedliche Anzahl von Werten haben.

  1. Gen 1: [-4, 2]
  2. Gen 2: [0, 5, 7, 22, 84]
  3. Gen 3: [-8, -3, 0, 4]
  4. Gen 4: [1, 6, 16, 18]

Alle 4 Listen sind als Elemente im gene_space-Parameter wie unten angegeben hinzugefügt.

gene_space = [[-4, 2],
              [0, 5, 7, 22, 84],
              [-8, -3, 0, 4],
              [1, 6, 16, 18] ]

Der nächste Code erstellt eine Instanz der Klasse pygad.GA, die das Attribut gene_space verwendet. Die gedruckte Anfangs- und Endpopulation zeigt, wie jedes Gen aus seinem eigenen Raum entnommen wird. Zum Beispiel sind die Werte des ersten Gens für alle Lösungen -4 und 2.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       gene_space=[[-4, 2],
                                   [0, 5, 7, 22, 84],
                                   [-8, -3, 0, 4],
                                   [1, 6, 16, 18] ])

ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[-4. 84.  0. 18.]
 [ 2.  7.  4.  1.]
 [ 2.  0. -8.  6.]]

[[-4.  7.  4.  1.]
 [ 2.  7.  4.  1.]
 [-4.  7.  4.  6.]]

Die Werte der vorhergehenden 4 Gene folgten keiner Sequenz. Es kann sein, dass die Werte einiger Gene einer Sequenz folgen. Die Werte der 4 Gene sind unten aufgeführt. Die Werte des ersten Gens reichen von 0 bis 5 (exklusiv) und die Werte des zweiten Gens reichen von 16 bis 27 (exklusiv). Die Werte der dritten und vierten Gene sind die gleichen wie zuvor.

  1. Gen 1: [0, 1, 2, 3, 4]
  2. Gen 2: [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]
  3. Gen 3: [-8, -3, 0, 4]
  4. Gen 4: [1, 6, 16, 18]

Der neue Wert des gene_space Parameters wird unten angegeben.

gene_space = [ [0, 1, 2, 3, 4],
               [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26],
               [-8, -3, 0, 4],
               [1, 6, 16, 18] ]

Was ist, wenn ein Gen eine Sequenz von beispielsweise 1.000 Werten hat? Müssen wir seine einzelnen Elemente auflisten? Glücklicherweise erlaubt PyGAD, den Bereich eines einzelnen Gens mit der Funktion range() anzugeben. Wenn der Wertebereich des ersten Gens von 0 bis nicht einschließlich 5 reicht, kann er mit range(0, 5) modelliert werden. Für das zweite Gen, das von 16 bis nicht einschließlich 26 reicht, wird sein Wertebereich mit range(16, 27) dargestellt.

Nach der Verwendung der range()-Funktion ist der neue Wert des gene_space-Parameters unten angegeben.

gene_space = [ range(5), range(16, 27), [-8, -3, 0, 4], [1, 6, 16, 18] ]

Hier ist der Code, der den aktualisierten gene_space verwendet.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       gene_space=[range(5),
                                   range(16, 27),
                                   [-8, -3, 0, 4],
                                   [1, 6, 16, 18] ])

ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[ 0. 19. -8. 18.]
 [ 2. 26.  4.  6.]
 [ 3. 18. -3.  1.]]

[[ 3. 25.  0.  6.]
 [ 0. 26.  4.  18.]
 [ 3. 22.  0.  6.]]

Es ist möglich, ein Gen auf einen einzigen Wert festzulegen. Dies geschieht, indem man dessen Element im gene_space-Parameter auf diesen einzigen Wert setzt. Hier ist ein Beispiel, in dem das erste Gen auf 4 und das dritte Gen auf 5 gesetzt wird. Diese 2 Gene werden ihre Werte niemals ändern.

gene_space = [4,
              range(16, 27),
              5,
              [1, 6, 16, 18] ]

Hier ist der Code, der den letzten gene_space Wert verwendet. In der anfänglichen und finalen Population ändern sich die ersten und dritten Gene nie.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       gene_space=[4,
                                   range(16, 27),
                                   5,
                                   [1, 6, 16, 18] ])

ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[ 4. 21.  5. 16.]
 [ 4. 18.  5. 16.]
 [ 4. 24.  5. 16.]]

[[ 4. 18.  5.  1.]
 [ 4. 18.  5. 16.]
 [ 4. 18.  5. 18.]]

Passen Sie einige Gene an, während Sie andere randomisieren.

Laut der vorherigen Diskussion des gene_space-Parameters hat jedes Gen seinen eigenen Genraum, der entweder durch das Hardcoden der einzelnen Werte oder durch die Verwendung der range()-Funktion festgelegt wird.

In einigen Fällen muss der Benutzer möglicherweise einige Gene dazu zwingen, auf bestimmte Werte beschränkt zu sein, während andere Gene randomisiert werden können. Zum Beispiel, wenn das Chromosom ein Gen hat, das entweder -1 oder 1 sein muss, die anderen Gene jedoch jeden zufälligen Wert haben können. Wie macht man das?

Um das Gen zu randomisieren, weisen Sie dessen Element im gene_space-Parameter den Wert None zu. Das bedeutet, dass der Wert für dieses Gen randomisiert wird. Die nächste Zeile weist der ersten Gen die Liste [-1, 1] und den verbleibenden 3 Genen None zu. Die letzten 3 Gene werden zufällige Werte haben.

gene_space = [[-1, 1], None, None, None]

Der nächste Code verwendet den letzten gene_space Wert. Beachten Sie, dass das erste Gen aus der Liste [-1, 1] ausgewählt wird, während die anderen Gene zufällige Werte haben.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       gene_space=[[-1, 1], None, None, None])
ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[ 1.          0.28682682  1.39230915  1.12768838]
 [-1.         -1.05781089  1.71296713  2.56994039]
 [ 1.          3.78611876 -3.80634854  2.15975074]]

[[-1.         -1.05781089  1.88097581  2.56994039]
 [-1.         -1.05781089  1.71296713  2.56994039]
 [-1.         -1.05781089  1.3061504   2.56994039]]

Beachten Sie, dass die zufälligen Gene zufällig aus Werten innerhalb des von den 2 Parametern init_range_low und init_range_high angegebenen Bereichs initialisiert werden. Wenn die Art der Mutation zufällig ist, wird der zufällig dem Gen hinzugefügte Wert aus dem Bereich, der von den 2 Parametern random_mutation_min_val und random_mutation_max_val angegeben ist, entnommen. Darüber hinaus wird die Art des zufälligen Wertes entsprechend dem Parameter gene_type bestimmt. Schließlich, wenn mutation_by_replacement auf True gesetzt ist, wird der zufällige Wert nicht hinzugefügt, sondern ersetzt das Gen. Beachten Sie, dass diese Parameter nur die Gene betreffen, deren Raum auf None gesetzt ist.

Der nächste Code zwingt den initialen Wert des Gens, zwischen 10 und 20, exklusiv, zu liegen. Der zufällige Mutationsbereich liegt zwischen 30 und 40, exklusiv. Der gene_type ist auf int gesetzt.

ga_instance = pygad.GA(num_generations=1000,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       init_range_low=10,
                       init_range_high=20,

                       random_mutation_min_val=30,
                       random_mutation_max_val=40,

                       gene_space=[[-1, 1], None, None, None],

                       gene_type=int)

ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[ 1. 16. 14. 10.]
 [-1. 12. 16. 14.]
 [-1. 17. 19. 13.]]

[[-1. 12. 16. 48.]
 [-1. 15. 26. 14.]
 [-1. 12. 16. 14.]]

Binärer genetischer Algorithmus

Im Abschnitt Verwenden Sie den Parameter mutation_by_replacement unterstützte PyGAD den binären genetischen Algorithmus durch die Verwendung der folgenden Parameter.

  • init_range_low=random_mutation_min_val=0.
  • init_range_high=random_mutation_max_val=2.
  • Mutation durch Ersetzung=True.
  • gen_typ=int.

Es ist auch möglich, den binären GA zu unterstützen, indem der Parameter gene_space verwendet wird. Dies geschieht, indem dieser Parameter auf den globalen Raum [0, 1] gesetzt wird. Das bedeutet, dass alle Gene entweder den Wert 0 oder 1 haben.

Der nächste Code setzt den Parameter gene_space auf [0, 1]. Dies zwingt die Werte aller Gene dazu, entweder 0 oder 1 zu sein.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       sol_per_pop=3,
                       num_genes=4,
                       fitness_func=fitness_function,

                       gene_space=[0, 1])
ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[1 1 1 0]
 [0 1 0 0]
 [1 1 1 0]]

[[0 1 1 0]
 [0 1 0 1]
 [0 1 0 0]]

Benutzerdefinierte Anfangspopulation

Manchmal möchte der Benutzer mit einer benutzerdefinierten Anfangspopulation ohne irgendeine Zufallsgenerierung beginnen. PyGAD unterstützt einen Parameter namens initial_population, der es dem Benutzer ermöglicht, eine benutzerdefinierte Anfangspopulation anzugeben.

Der nächste Code weist der initial_population-Parameter eine verschachtelte Liste zu, in der es 3 Lösungen gibt, wobei jede Lösung 4 Gene hat. In diesem Fall sind die Parameter num_genes und sol_per_pop nicht erforderlich, da sie aus dem Wert, der dem initial_population-Parameter zugewiesen wird, abgeleitet werden.

ga_instance = pygad.GA(num_generations=10,
                       num_parents_mating=2,
                       fitness_func=fitness_function,

                       initial_population=[[34, 32, 24, -2],
                                           [3, 7, 2, 7],
                                           [-2, -4, -6, 1]])
ga_instance.run()

print(ga_instance.initial_population)
print(ga_instance.population)
[[34 32 24 -2]
 [ 3  7  2  7]
 [-2 -4 -6  1]]

[[3 7 2 6]
 [3 7 2 7]
 [3 7 2 7]]

Fazit

Dieses Tutorial verwendete die PyGAD-Bibliothek, um sowohl mit den binären als auch den dezimalen Darstellungen des genetischen Algorithmus zu arbeiten. Das Tutorial erörterte die verschiedenen Parameter in PyGAD, um dem Benutzer die Kontrolle darüber zu ermöglichen, wie die anfängliche Population erstellt wird, neben der Steuerung des Mutationsvorgangs.

Mit dem Parameter gene_type können die Genwerte entweder Fließkommazahlen oder Ganzzahlen sein. Der Parameter mutation_by_replacement wird verwendet, um die Gene in ihrem ursprünglichen Bereich zu halten. Der Parameter initial_population akzeptiert eine benutzerdefinierte Anfangspopulation.

Der gene_space-Parameter hilft, falls die Genwerte keiner Reihenfolge folgen. In diesem Fall werden die diskreten Genwerte als Liste übergeben. Dieser Parameter akzeptiert einen benutzerdefinierten Wertebereich für jedes Gen. Darüber hinaus ermöglicht er, dass einige Gene aus einem definierten Bereich ausgewählt und andere zufällig ausgewählt werden.