Genetisches Programmieren Marc Musch Inhaltsverzeichnis 1 Grundkonzept 2 2 Exkurs: LISP 3 3 Vorbereitung eines Laufes 4 3.1 Festlegen der Terminalmenge und der Funktionenmenge 4 3.2 Bestimmen eines geeigneten Fitnessmaßes 5 3.3 Festlegen der Strategieparameterwerte und des Abbruchkriteriums 5 4 Durchführung eines Laufes 6 4.1 Schritt 1: Initialisierung 6 4.2 Schritt 2: Bewerten der Ausgangslösung 7 4.3 Schritt 3: Bilden der neuen Population 7 4.4 Schritt 4: Wiederholung Schritt 3 bis Abbruchkriterium greift 8 5 Ein einfaches Beispiel: Das 2-Quader-Problem 9 6 Erweiterungen von GP 10 6.1 Mutation 10 6.2 Automatisch Definierte Funktionen (ADFs) 11 6.3 Sonstige Erweiterungen im Überblick 13 Literatur 13 13. Juni 2004
Zusammenfassung Von vielen wird als nächster großer Schritt der Programmierung die Entwicklung von Programmen gesehen die sich selbst schreiben. In den späten 50ern wurden schon erste Versuche unternommen solche Programme, die eine Aufgabenstellung lösen, ohne explizit dafür programmiert worden zu sein, zu entwickeln. Jedoch ohne großen Erfolg, vor allem weil die Computer noch nicht leistungsfähig genug waren. Seit Mitte der 80er hat man diese Forschung wieder verstärkt aufgenommen und als Vorbild dienen evolutionäre Prinzipien aus der Biologie. Vor allem John R. Koza hat starke Fortschritte in diesem Gebiet erzielt. Diese Ausarbeitung beschäftigt sich mit diesem so genannten Genetischen Programmieren. 1 Grundkonzept Um das Grundkonzept der Genetischen Programmierung (GP) zu verstehen, ist es hilfreich einen Blick auf die natürliche Evolution zu werfen. Vor allem drei Grundkonzepte von Charles Darwin dienen als Vorlage für die GP. Abstammungshypothese: Alle Organismen stammen durch einen kontinuierlichen Verzweigungsprozess von gemeinsamen Vorfahren ab. In der GP gibt es eine Anzahl von ersten stochastisch generierten Programm-Individuen, von diesen stammen alle weiteren Programme ab. Das einfache Eltern-Kind Prinzip, ein Kind erbt die genetisch bedingten Eigenschaften der beiden Elternteile. Programme besitzen unterschiedliche Größe und Komplexität, diese setzen sich aus Funktionen, Variablen und Konstanten zusammen. Allmählichkeitshypothese: Evolution vollzieht sich nicht sprunghaft, sondern allmählich in kleinen Schritten. Automatische Programmentwicklung mit Hilfe der GP erfordert hohe Rechenkapazitäten, da die Veränderungen bis zum endgültigen Programm sehr viele Schritte (Generationen) benötigen. Selektionshypothese: Wirksames Prinzip des Wandels ist die natürliche Selektion durch die Umwelt. Das Grundkonzept der Evolution: nur der stärkste überlebt. In der GP dürfen sich nur die fittesten Programm-Individuen weiter verbreiten, die die näher am gewünschten Ergebnis liegen als ihre Konkurrenten. Mit GP können automatisch Programme entwickelt werden, die Probleme anhand von Beispieldaten lösen, ohne dass der Programmierer die richtige Lösung kennen muss. Er muss nur problemangemessene Variablen, Konstanten und Funktionen der GP zur Verfügung stellen. 2
Abbildung 1. Grundsätzlicher Ablauf der Genetischen Programmierung 2 Exkurs: LISP GP-Individuen (Programme) sind meistens baumartig strukturiert und Funktionen müssen genau so wie Variablen und Konstanten umplatziert werden. Daher ist es hilfreich eine Programmiersprache für die Implementierung zu wählen, die diese Eigenschaft bestmöglichst unterstützt: LISP (List Processing Language). Vorteile: Syntaktisch einfache Sprache, keine Unterscheidung zwischen Programmen und Daten, beide zusammen als Listen mit Klammerung aufgebaut (Ähnlichkeit mit Prolog). LISP Ausdrücke lassen sich als Baum darstellen, Größe und Form lassen sich leicht verändern. LISP ist eine interpretierende Sprache, auch geschachtelte LISP-Listen können einfach mit einem Aufruf berechnet werden. Abbildung 2. Programmbaum des LISP Ausdruckes (* 11(+ 3 1 4)) = 88 Natürlich kann GP auch mit anderen Sprachen realisiert werden. Jedoch eignet sich die Baumstruktur sehr gut zur Erklärung von GP. 3
3 Vorbereitung eines Laufes Ein Lauf bezeichnet die Durchführung eines GP Vorganges von der ersten Population bis zur fertigen Version oder bis zum Abbruch. Als Vorbereitung muss eine Sprache definiert werden in der die Programme entstehen sollen, dies geschieht durch die Terminalmenge (terminal set) und die Funktionenmenge (function set). Desweiteren müssen die entstehenden Programme bewertet werden, hierzu wird ein Fitnessmaß verwendet. Außerdem müssen noch einige Strategien aufgestellt werden und man muss sich für ein Abbruchkriterium entscheiden, damit der Lauf terminiert. 3.1 Festlegen der Terminalmenge und der Funktionenmenge Die Elemente der Terminalmenge sind problemangemessene Variablen und Konstanten, die Funktionenmenge umfasst die nötigen Funktionen für das Programm. GP versucht mit diesen beiden Mengen, ein Programm zu erzeugen, das die Aufgabenstellung bestmöglich löst. Die Entscheidung, welche Elemente in die Mengen aufgenommen werden, hat einen großen Einfluss auf den GP-Lauf. Werden nur wenige Elemente ausgewählt kann GP vielleicht keine effiziente Lösung finden, werden hingegen zu viele aufgenommen werden die Lösungen zu kompliziert oder es entsteht keine befriedigende Programmversion bis zum Ende des Laufes. In LISP entsprechen die Elemente der Funktionenmenge den Knoten und die der Terminalmenge den Blättern. Mögliche Elemente der Funktionenmenge sind: arithmetische Operationen, mathematische Funktionen(Sin, Cos, Sqrt,...), boolsche Operationen, Verzweigungen, Iterationen, anwendungsspezifische Funktionen. Weiter wird der Funktionenmenge noch ein argument map zugeordnet, es enthält die Anzahl der Argumente jeder Funktion. In der Terminalmenge gibt es neben den Variablen und Konstanten noch die ephermal random constants, da für manche Probleme Konstanten nötig sind deren Werte jedoch breit gefächert sind (z.b. Regressionsfunktionen). Sie dienen als Platzhalter (Dummy) in einem Knoten bzw. Blatt und werden in jeder Generation neu bestimmt, wären sie normale Konstanten würden sie, wegen ihrer hohen Anzahl, die anderen Elemente bei der zufälligen Wahl verdrängen. Bei der Wahl der beiden Mengen müssen zwei Dinge berücksichtigt werden: Angemessenheit: Es muss möglich sein mit den Elementen der Mengen eine Lösung zu finden. Abgeschlossenheit: Jede Funktion muss jeden Wert als Argument zulassen, der von anderen Funktionen zurückgegeben werden kann und jedes Element der Terminalmenge. 4
Also muss etwa die Division einen gültigen Wert bei der Division durch 0 zurückgeben, meist wird dann eine 1 zurück gegeben. Ein weiteres Problem ergibt sich bei Schleifen, hier muss berücksichtigt werden, dass keine Endlos- Schleifen entstehen, z.b. durch konstantes Abbrechen nach angemessen vielen Schritten. 3.2 Bestimmen eines geeigneten Fitnessmaßes Jedes entstehende Programm muss durch eine Fitnessuntersuchung bewertet werden. Nur Programme mit guten Fitnesswerten dürfen sich fortpflanzen. Hierzu wird eine Fitnessfunktion aufgestellt, diese muss als Ergebnis die unterschiedlichen Qualitäten der Programme möglichst differenziert wiedergeben, das gleiche gilt für Teillösungen. Eine Fitnessfunktion kann mehrere Parameter berücksichtigen, wie neben der Funktionalität auch die Komplexität. Die Fitness eines Programms kann durch so genannte Trainingsdaten berechnet werden, hierbei werden einige Beispielfälle deren Ergebnisse bekannt sind mit dem Programm berechnet, die Summe über die Abweichungen zu den richtigen Ergebnissen wird als Fitnessmaß benützt. Hier bedeutet also ein niedriger Wert, dass das Programm schon nahe an der richtigen Lösung ist. Man kann jedoch auch nur die Anzahl der richtig erkannten Fälle berücksichtigen, dann ist ein hoher Wert gut. Manchmal kann von den durch GP kreierten Programmen die genaue Lösung nicht erwartet werden, dann wird ein Toleranzbereich um das gewünschte Ergebnis aufgestellt. Wenn die Ausgabe des Programms in diesem Bereich liegt, wird es als richtig akzeptiert. 3.3 Festlegen der Strategieparameterwerte und des Abbruchkriteriums Nun muss man sich noch für einige Grundeinstellungen entscheiden. Wichtig ist hier die Populationsgröße, also wieviele Programm-Individuen je Generation nebeneinander existieren dürfen (also auch wie viele als Startpopulation dienen). Diese Anzahl wird vor allem durch die Leistungsfähigkeit der Hardware bestimmt. Des Weiteren muss man sich entscheiden, wann der GP-Lauf beendet werden soll, hierfür gibt es keine generellen Werte, da die benötigte Anzahl der Generationen stark von der Komplexität des gestellten Problems abhängt. Wird die maximale Anzahl von Generationen durchlaufen ist das Endergebnis das beste gefundene Programm im gesamten Lauf. Oft wird der Lauf abgebrochen, wenn ein Programm vorzeitig eine korrekte Lösung liefert. Um gute Strategieparameter zu finden müssen häufig erst einige Läufe durchgeführt werden, da die Ergebnisse wegen den stochastischen Einflüsse stark variieren. Weitere Strategieparameter werden im Abschnitt Durchführung eines Laufes behandelt. 5
4 Durchführung eines Laufes Jetzt zu der eigentlichen Durchführung eines Laufes, dieser gliedert sich in die folgenden vier Schritte. 4.1 Schritt 1: Initialisierung In der Initialisierungsphase muss erstmal eine Ausganspopulation von µ baumstrukturierten (LISP) Programmindividuen erzeugt werden. Hierbei wird zunächst aus der Funktionenmenge eine zufällige Funktion als Wurzel ausgewählt, die Knoten sind dann die Argumente für die Funktion (Anzahl bestimmt durch argument map). Für jeden Knoten wird nun aus der Funktionenmenge und der Terminalmenge zufällig ein Element gewählt, dabei bildet logischerweise ein Element aus der Terminalmenge ein Blatt. Damit diese Bäume nicht zu groß werden, wird eine maximale Höhe festgelegt. So entstehen nun die µ Programme. Duplikate, also identische Bäume, werden für die Ausgangspopulation nicht zugelassen. Abbildung 3. Entstehen eines Programmbaumes der Ausgangspopulation Die Programme in der Ausgangspopulation sollten sich möglichst stark voneinander unterscheiden. Um dies zu erreichen verwendet man beispielsweise die Methode half-ramping. Dabei werden alle Baumhöhen zwischen zwei und der Maximalhöhe gleich oft erzeugt. Für jede dieser Baumhöhen erzeugt man je die Hälfte der Vertreter auf eine der beiden folgenden Weisen: Bei der einen Hälfte sind alle Pfade im Baum zwischen Wurzel und Blättern gleichlang und entsprechen der Höhe des Baumes. Die andere Hälfte der Bäume dieser Höhe wird wie oben beschrieben erzeugt. Ein Kritikpunkt an half-ramping ist, dass die generierten Bäume nicht mehr wirklich zufälligen Charakter haben. [1] 6
4.2 Schritt 2: Bewerten der Ausgangslösung Mit der Fitnessfunktion werden nun die in der Initialisierung entstandenen Programme bewertet, im Normalfall sind die Fitnesswerte extrem schlecht, da die Programme ausschließlich stochastisch erzeugt wurden, trotzdem lassen sie sich damit differenzieren, da es bessere und schlechtere gibt. 4.3 Schritt 3: Bilden der neuen Population Nun muss die leere neue Population mit Programmindividuen aufgefüllt werden. Die Programme der vorherigen Generation sind die Eltern für die nun entstehenden Individuen, dabei werden die nächsten drei Teilschritte so lange ausgeführt bis auch die neue Population µ Programme umfasst. 4.3.1 Schritt 3-1: Operatorwahl Als nächstes muss entschieden werden, wie sich die Eltern fortpflanzen sollen, im Grundkonzept der GP gibt es zwei evolutionäre Möglichkeiten, Reproduktion und Crossover 1. Diesen beiden Operatoren werden Wahrscheinlichkeiten zugeordnet p r und p c, wobei p r +p c = 1. Anhand dieser Wahrscheinlichkeitsverteilung wird ein Operator stochastisch ausgewählt. Es gibt auch noch weitere Operatoren, wie die Mutation (diese wird später behandelt). 4.3.2 Schritt 3-2: Stochastische Selektion und Replikation Die Eltern werden stochastisch selektiert, dabei werden bevorzugt die Programme mit einem guten Fitnesswert ausgewählt, damit eine Verbesserung erzielt wird. Das einfachste Modell ist die fitnessproportionale Selektion, dabei wird ein Individuum mit umso größerer Wahrscheinlichkeit ausgewählt, je größer dessen Fitness ist. Am häufigsten wird jedoch die Turnierselektion (Wettkampfselektion) angewandt. Dabei wird zufällig, gleichverteilt eine bestimmte Anzahl Individuen aus der Generation in eine Auswahlgruppe genommen und aus dieser Gruppe dann das fitteste Individuum als Elter ausgewählt. So werden auch Programme mit einem durchschnittlichen Fitnesswert ausgewählt, falls die Gruppe aus Individuen besteht, die nicht so gute Fitnesswerte haben, und die neue Generation bleibt breit gefächert. Die ausgewählten Programme werden dabei in die neue Population kopiert und verbleiben somit unverändert in der alten, die Selektion entspricht also dem Urnenmodell Ziehen mit Zurücklegen. 1 Hier findet biologisch gesehen ein Austausch von DNA/Gene statt 7
4.3.3 Schritt 3-3: Operatoranwendung und Ergänzen der neuen Population Wird als Operator die Reproduktion gewählt, wird das Individuum ohne Veränderung in die neue Population übernommen. Beim Crossover werden zwei Individuen ausgewählt, in jedem wird stochastisch ein Knoten oder Blatt als Crossoverpunkt bestimmt. Dabei ist die Wahrscheinlichkeit p ip für einen Knoten meist höher als die für ein Blatt p ep. Die ausgewählten Crossoverpunkte werden dann ausgetauscht, falls vorhanden mit ihren gesamten unterliegenden Teilbäumen, und die beiden so neu entstehenden Individuen in die neue Population übernommen. Dabei werden jedoch Individuen welche die maximale Höhe überschreiten verworfen. Zum Schluss wird deren Fitnesswert berechnet. Abbildung 4. Beispiel für ein Crossover 4.4 Schritt 4: Wiederholung Schritt 3 bis Abbruchkriterium greift Schritt 3 wird solange wiederholt, bis das Abbruchkriterium greift, also wenn die maximale Generationenanzahl t max erreicht ist oder eine perfekte Lösung gefunden wurde. So entsteht langsam aus den stochastisch generierten Ausgangsprogrammen eine bessere Lösung für das Anwendungsproblem. Oft wird das Ergebnis dann noch manuell nachbearbeitet um Lesbarkeit und Verständlichkeit zu verbessern. 8
Populationsgröße µ 4000-16000 max. Generationen t max 51 Crossover-Wahrscheinlichkeit p c 0,9 Reproduktions-Wahrscheinlichkeit p r 0,1 Lage Crossover-Punkt p ip / p ep 0,9 / 0,1 max. Baumhöhe Ausgangspopulation 6 max. Baumhöhe während des restl Laufes 17 Initialisierungsmethode Selektionsverfahren half-ramping Wettkampf Tabelle 1 Standardeinstellungen wesentlicher GP Strategieparameter nach Koza [1] 5 Ein einfaches Beispiel: Das 2-Quader-Problem Um die bisherige Theorie in die Praxis umzusetzen nun ein einfaches Beispiel nach Koza [1]. Aufgabenstellung: Es soll ein Programm kreiert werden, das die Differenz D zweier Quader-Volumen berechnet, für einen Programmierer ist das keine schwere Aufgabe, er sieht den einfachen Zusammenhang D = L 1 B 1 H 1 L 2 B 2 H 2. Wir wollen nun jedoch dieses Problem mit GP lösen und nehmen an, dass wir nur zehn Beispielwerte für die Variablen kennen und deren richtige Ergebnisse D. Anhand dieser Trainingsdaten wollen wir nun einen GP-Lauf durchführen, dessen Ergebnis ein Programm ist, das nach Eingabe der sechs Variablen den Wert D berechnet. Festlegen der Terminalmenge und der Funktionenmenge: Die Terminalmenge T beinhaltet hier nur Variablen: T = {L 1, B 1, H 1, L 2, B 2, H 2 }. Ohne genaue Vorstellung von dem Ergebnis, wissen wir doch, dass es sich um ein mathematisches Problem handelt, daher bieten sich für die Funktionenmenge F die vier Grundrechenarten an: F = {+,,, :} Die Division ist die durch Null geschützte. Das argument map zu dieser Funktionenmenge gibt die Anzahl der Argumente für jede Funktion an: {2, 2, 2, 2} Bestimmen eines geeigneten Fitnessmaßes: Durch jedes erzeugte Programm können wir unsere zehn Beispieldaten berechnen lassen. Da wir jedes Ergebnis für D kennen, wissen wir um wie viel das Programm den tatsächlichen Wert verfehlt hat und können eine Fehlerdifferenz bilden. Es bietet sich hier an, alle zehn Fehlerdifferenzen aufzusummieren, wenn diese Summe nun bei einem Programm den Wert 0 erreicht, ist es perfekt. Wir lassen hier jedoch eine Toleranz von 0,01 zu. Festlegen der Strategieparameterwerte und des Abbruchkriteriums: Wir nehmen die Werte aus obiger Tabelle mit µ = 4000. Als Abbruchkriterium t max = 51 oder bis ein perfektes Programm gefunden wird. Nun wird 9
der eigentliche Lauf durchgeführt, wir wollen hierbei ein paar Ergebnisse betrachten. Ergebnisse: In der Ausgangspopulation ist der durchschnittliche Fitnesswert 1.195.092, das beste Programm erzielt einen Wert von 783 und hat den Aufbau (als mathematische Formel dargestellt): H 1 L 1 (B 1 + H 1 B 2 L 2 ) Die Variable H 2 kommt noch gar nicht vor und auch ansonsten hat der Aufbau wenig Ähnlichkeit mit der richtigen Lösung. In den sieben weiteren Generationen hat das jeweilig beste Programm einen Fitnesswert von 778, 510, 138, 117, 53 und 51, in der achten Generation gibt es ein Programm mit Fitnesswert 4,44. es hat den mathematischen Aufbau: B B 1 H 1 L 1 B 2 H 2 L 2 1 +L 1 L 1 B 2 B 2 L 2 L 2 B 2 Korrekt bis auf den letzten Fehlerterm, in Generation 11 taucht dann ein perfektes Programm auf, das sogar genau der obigen Formel entspricht. Dass ein perfektes Programm auftaucht ist nicht immer der Fall, meist gibt es bei GP nur ein Programm mit einer Näherungslösung oder es ist weitaus komplizierter als die Lösung auf die ein Mensch kommen würde. 6 Erweiterungen von GP 6.1 Mutation Die bisherige Form des Crossover wird seit einiger Zeit kritisch diskutiert, da sie als ineffizient gilt, es werden nur vorhandene Teilbäume ausgetauscht und keine neuen erzeugt, dadurch besteht die Gefahr des Feststeckens. Daher wird auch die Mutation als Operator verwendet, sie fließt dann mit der Wahrscheinlichkeit p m in die stochastische Operatorselektion mit ein (p r + p c + p m = 1). Nach der Selektion eines Elters wird zufällig ein Knoten als Mutationspunkt gewählt, hier wird dann der Teilbaum, dessen Wurzel der Mutationspunkt ist, mit einem neuen stochastisch generierten Teilbaum ersetzt. Der stochastisch kreierte Teilbaum hat eine geringere Maximalhöhe als die Ausgangsbäume, um die Wahrscheinlichkeit zu senken, dass ein Baum die Maximalhöhe überschreitet und dadurch das Programm zu groß wird. Tut er dies doch, wird die Änderung verworfen. Eine andere Art der Mutation besteht darin nur ein einzelnes Element des Baumes auszutauschen. Durch Aufnehmen der Mutation kann die Populationsgröße verringert werden ohne die Vielfalt der Programme und ihrer Entwicklungen von Generation zu Generation einzuschränken. 10
6.2 Automatisch Definierte Funktionen (ADFs) In der Informatik ist die divide and conquer Strategie sehr beliebt, man löst ein Problem indem man es in Teilprobleme zerlegt und diese dann einzeln behandelt. In GP versucht man nun auch diese Strategie anzuwenden, ein Konzept sind die Automatisch Definierten Funktionen (ADFs), die inzwischen eine Standarderweiterung in GP-Systemen, zum Lösen komplexer Probleme, sind. Es geht darum modular aufgebaute Programme zu generieren die aus ADFs und einem Hauptprogramm bestehen, dabei ist nicht vorgegeben wie und ob überhaupt einzelne ADFs verwendet werden. Diese Modularisierung führt bei komplexen Problemen zu besserer Verständlichkeit der Programme und macht den Prozess der Problemlösung effizienter, da Lösungen von Teilproblemen wiederholt aufgerufen werden können und dadurch die Programme kleiner werden. Dies bringt zusätzlich den Vorteil, dass die Hardwareanforderung sinkt, da die aufwändige Fitnessberechnung der Programme vereinfacht wird. Der Vorteil von ADFs liegt nur in komplexen Problemstellungen, bei einfachen Problemen verkompliziert die Verwendung von ADFs die Lösung. [3] 6.2.1 Vorbereitung eines Laufes mit ADFs Die Vorbereitungsschritte bei der Verwendung von ADFs sind grundsätzlich die gleichen wie ohne, jedoch müssen einige Programmarchitekturentscheidungen getroffen werden. Diese gelten für alle Programmindividuen der GP- Population. Die Anzahl der ADFs in einem Programm, sowie die Anzahl der Argumente für jede ADF werden fest bestimmt. Bei der Wahl der Argumentenanzahl muss ein Kompromiss gefunden werden. Um den evolutionären Lösungsprozess nicht einzuschränken bietet sich eine hohe Anzahl an Argumenten an, jedoch erhöht dies die Rechenzeit und damit die Hardwareanforderung. Als nächstes muss noch bestimmt werden inwiefern und ob überhaupt sich ADFs gegenseitig aufrufen dürfen, also sollen die ADFs unabhängig voneinander Teilaufgaben bearbeiten oder aufeinander aufbauen. Sich selbst aufrufende ADFs werden jedoch meist nicht erlaubt, Rekursion ist in GP noch ein aktiver Forschungszweig. Der nächste Schritt ist das Festlegen der Terminalmenge und Funktionenmenge. Diese werden jetzt jedoch für jede ADF und das Hauptprogramm einzeln bestimmt. Die Terminalmenge für das Hauptprogramm ist die gleiche wie ohne ADFs, so auch die Funktionenmenge, jedoch wird diese mit den Namen der ADFs erweitert. Die Terminalmenge einer ADF enthält soviele Dummy- Variablen wie sie Argumente hat, deren Werte werden beim Aufruf der ADF festgelegt. Die Funktionenmenge einer ADF enthält die Funktionen die man auch bei GP ohne ADF wählen würde und zusätzlich noch die Namen der ADFs die von dieser ADF aufgerufen werden können. Die restlichen Vorberei- 11
tungsschritte sind die gleichen wie ohne ADFs. Im folgenden ein Beispiel für ein Programmaufbau mit zwei ADFs in LISP. Abbildung 5. Struktur eines LISP-Programmbaumes mit zwei ADFs Oberhalb der gestrichelnden Linie ist die feste Programmarchitektur die bei allen Individuen gleich ist, unterhalb ist der von den evolutionären Algorithmen beeinflussbare Bereich. Mit defun wird in LISP eine Funktion deklariert. Die Funktion progn evaluiert jedes ihrer Argumente sequentiell von links nach rechts, also im Beispiel wird ADF0 evaluiert und dadurch definiert, dann ADF1 und zum Schluss der Hauptprogrammast, der dann auch die entgültige Ausgabe gibt. 6.2.2 Durchführung eines Laufes mit ADFs Es wird wieder stochastisch eine Ausgangspopulation erzeugt, hier muss berücksichtigt werden, dass jedes Programm die festgelegte Programmarchitektur aufweist. Die nicht festen Knoten unterhalb der gestrichelten Linie in Abbildung 5 werden für jede ADF und das Hauptprogramm, mit der jeweiligen Terminalmenge und Funktionenmenge, wie beim Lauf ohne ADFs stochastisch generiert. Bei der Verwendung von ADFs wird das strukturellerhaltende Crossover verwendet. Es wird wie beim normalen Crossover ein Elter ausgewählt und in diesem stochastisch ein Knoten als Crossoverpunkt bestimmt, beim anderen Elter wird die Auswahl jedoch dahingehend eingeschränkt, dass der Crossoverpunkt nur in dem gleichen Teilbaum liegen darf. Also wenn der Crossoverpunkt im ersten Programm in der ADF1 liegt, muss der im zweiten Programm ebenfalls in der ADF1 liegen, damit stets syntaktisch korrekte, gültige Lösungen produziert werden. Wird dadurch eine ADF verändert die mehrmals in einem Programm benützt wird, kann sich ein Crossover besonders stark auswirken. 12
6.3 Sonstige Erweiterungen im Überblick Da GP eine sehr hohe Rechenzeitanforderung hat, bietet sich die Implementierung auf paralleler Hardware an, so soll vor allem die Parallelisierung der aufwändigen Fitnessermittlung einen GP-Lauf beschleunigen. 1999 wurde von Genetic Programming Inc. ein 1000-Pentium Beowulf-Style Cluster Computer (mit 1000 Pentium II 350 MHz Prozessoren) für komplexe GP-Läufe gebaut. Einen anderen Ansatz zur Beschleunigung verfolgen Peter Nordin und Wolfgang Banzhaf. Sie Implementieren die GP-Individuen in Maschinensprache, dadurch entfällt die Interpretierung der Programme zur Laufzeit. So ergeben sich Geschwindigkeitsvorteile bis zum Faktor 100 gegenüber C-Programme. Mit der Implementierung in Maschinensprache haben Nordin und Banzhaf es auch geschafft einen Roboter mit GP zu trainieren. So wurde automatisch im System des Roboters ein Programm generiert das ihn geradeaus fahren lässt und mit dem er Hindernissen ausweichen kann 2. Literatur [1] Volker Nissen: Einführung in Evolutionäre Algorithmen, Vieweg Verlag, 1997, Braunschweig, Wiesbaden [2] Michael Negnevitsky: Artificial Intelligence: A Guide to Intelligent Systems, Addison Wesley, 2002, Harlow, England [3] Genetic Programming Inc., John R. Koza, http://www.genetic-programming.com/ 2 Siehe http://www.informatik.uni-ulm.de/ki/edu/seminare/robotik/ genet-alg.html 13