diff --git a/Semester_2/Einheit_04/Grundlagen-Probabilistische_Algorithmen.ipynb b/Semester_2/Einheit_04/Grundlagen-Probabilistische_Algorithmen.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..38cce4ed5c496d423627b12b7a513c115a26f8e3 --- /dev/null +++ b/Semester_2/Einheit_04/Grundlagen-Probabilistische_Algorithmen.ipynb @@ -0,0 +1,700 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e7ac2738-e0bb-4c7f-a03f-10fa7ec5b71e", + "metadata": { + "tags": [] + }, + "source": [ + "# <font color='blue'>**Probabilistische Algorithmen**</font>\n", + "\n", + "## **<font color='blue'>Zufallszahlen</font>** \n", + "\n", + "Die Abbildung der Zufälligkeit in einem Algorithmus wird durch die Nutzung von **Zufallszahlen** realisiert. Leider sind Zufallszahlen auf einem **deterministischen Computer** nicht wirklich zufällig, deshalb spricht man auch von Pseudo-Zufallszahlen. \n", + "Üblicherweise werden Zufallszahlen in Form von Folgen generiert, deren Wertebereich durch die verwendeten Datentypen begrenzt ist. \n", + "Bei gleichen **Startbedingungen** ergeben sich jedoch immer die gleichen Zahlenfolgen. \n", + "Solche Zahlenfolgen sollten grundsätzlich immer im Hinblick auf ihre Verteilung - z.B. Gleichverteilung - überprüft werden. \n", + "\n", + "### **<font color='blue'>Lineares Modulo-Kongruenzverfahren}</font>** \n", + "\n", + "Ein Standardverfahren zur Bestimmung von Zufallszahlen \n", + "ist das lineare Modulo-Kon\\-gru\\-enz\\-verfahren (LCM). \n", + "Hier ist die Zahlenfolge durch die Rekursion \n", + "\n", + "\\begin{equation}\n", + "\\begin{split}\n", + "x _{n+1} \\;=\\; & (\\;a \\; x_n + c \\;) \\;\\text{mod}\\; m \\\\\n", + "&\\text{mit} \\;\\; m > 0\\;, \\;\\;\\; 2<a<m \\;\\;\n", + "\\text{und}\\;\\; 0 \\leq c < m \\;\\;\\;\n", + "\\text{sowie}\\;\\;\\; m, a, c \\in \\mathbb{N}\n", + "\\end{split}\n", + "\\end{equation}\n", + "\n", + "definiert. Die aus einem Startwert $x_0$ \n", + "entstehenden Folgen besitzen die Periodenlänge $m$\n", + "und enthalten die Zahlen $ 0, ..., m-1 $.\n", + "Oftmals wird $ m = 2^b $ gesetzt, wobei $b$ die \n", + "Wortlänge des verwendeten Datentyps ist. \n", + "Als wichtige Bedingungen für eine Gleichverteilung \n", + "dürfen $c$ und $m$ keine gemeinsamen Primfaktoren haben, \n", + "was durch die Wahl einer großen Primzahl entweder \n", + "für $c$ oder für $m$ gewährleistet wird, \n", + "und $a-1$ muss ein Vielfaches des Produkts der Primfaktoren von $m$ sein.\n", + "\n", + "Als einfaches Beispiel sei hier die Folge angegeben für \n", + "\n", + "\\begin{equation}\n", + "\\begin{split}\n", + "a = 13 = & (2 \\cdot 2 \\cdot 3+1) , \\;\\; c = 1, \\;\\; \n", + "m = 16 = (2 \\cdot 2 \\cdot 2 \\cdot 2) \\;\\;\\;\n", + "\\text{und}\\;\\;\\; x_0 = 0 \\;\\; : \\\\ \\\\\n", + "\\rightarrow \\;\\;\\; & 0, 1, 14, 7, 12, 13, 10, 3, 8, 9, 6, 15, 4, 5, 2, 11,\\;\\;\\; 0, 1, 14, 7, \\; ... \\\\\n", + "\\end{split}\n", + "\\end{equation}\n", + "\n", + "Man erkennt die Periodizität nach $m=16$ Zahlen und \n", + "die Tatsache, dass jede Zahl des Wertebereichs \n", + "je Peridode genau einmal auftritt. \n", + "\n", + "Eine Klassen-Implementierung könnte wie folgt mit $a = 526 \\; (=3*5*5*7+1)$, $c = 121441 $ (Primzahl) und $m = 7441875 \\; (=3*3*3*3*3*5*5*5*5*7*7)$\n", + "aussehen: \n" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "4900139a-9b1e-4ea0-a06b-7c1a75d55f1a", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.0625,\n", + " 0.875,\n", + " 0.4375,\n", + " 0.75,\n", + " 0.8125,\n", + " 0.625,\n", + " 0.1875,\n", + " 0.5,\n", + " 0.5625,\n", + " 0.375,\n", + " 0.9375,\n", + " 0.25,\n", + " 0.3125,\n", + " 0.125,\n", + " 0.6875,\n", + " 0.0,\n", + " 0.0625,\n", + " 0.875,\n", + " 0.4375,\n", + " 0.75]" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "class Random:\n", + "\n", + " def __init__( self, seed, m=526, a=121441, c=7441875 ):\n", + " self.current = seed\n", + " self.m = m\n", + " self.a = a \n", + " self.c = c \n", + " \n", + " def random(self):\n", + " self.current = ( self.a * self.current + self.c ) % self.m\n", + " return self.current / self.m\n", + "\n", + "rand = Random(0,m=16,a=13,c=1,) \n", + "[rand.random() for i in range(20)]" + ] + }, + { + "cell_type": "markdown", + "id": "e4cb460b-a786-48c7-8b4c-9ea8e006b406", + "metadata": {}, + "source": [ + "\n", + "Für viele Programmiersprachen stehen aber auch Bibliotheksfunktionen zur Verfügung. \n", + "Die **Python-Funktion** `random.seed(a=None)` initialisiert den \n", + "Zufallszahlengenerator mit dem seed-Wert `a`, während der wiederholte Aufruf von \n", + "random.random() die eigentliche Folge liefert." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "9617f503-227d-488b-b20b-529bb1f2d506", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0.8444218515250481, 0.7579544029403025, 0.420571580830845, 0.25891675029296335, 0.5112747213686085, 0.4049341374504143, 0.7837985890347726, 0.30331272607892745, 0.4765969541523558, 0.5833820394550312]\n" + ] + } + ], + "source": [ + "import random\n", + "random.seed(a=0)\n", + "print( [random.random() for i in range(10)] )" + ] + }, + { + "cell_type": "markdown", + "id": "dfe9c6f0-fef8-4359-8b4b-79be71e091f1", + "metadata": { + "tags": [] + }, + "source": [ + "Möchte man Zufallszahlen im Intervall $[a,b)$ erzeugen, so empfiehlt es sich die Operation \n", + "\n", + "\n", + " x = a + (b-a) * random()\n", + "\n", + "auszuführen. Wichtig ist hierbei, \n", + "dass bei den Operationen alle Operanden\n", + "Gleitkommazahlen sind." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "41915ab5-da68-403b-8bb2-f6120aeb24e1", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[16.88772796878589, 19.093176751828867, 16.974452210201353, 12.212161941402083, 18.79117350101749, 16.206988141223086, 11.331642201163177, 15.49033482059635, 18.920116396859395, 18.997928139729595]\n" + ] + } + ], + "source": [ + "import random\n", + "a = 10. \n", + "b = 20.\n", + "print( [ a + (b-a) * random.random() for i in range(10) ] )" + ] + }, + { + "cell_type": "markdown", + "id": "d024bdde-545c-4c2a-916f-ab80e3717c1e", + "metadata": { + "tags": [] + }, + "source": [ + "## **<font color='blue'>Optimierung</font>** \n", + "\n", + "Optimierungsprobleme werden mittels Monte-Carlo-Methoden durch die Vorgabe der Entwurfs- oder Entscheidungsparameter - zusammengefaßt im Vektor $ X = [x_1, x_2, ... , x_n] $ - über Zufallszahlen behandelt. \n", + "In welchem Bereich die einzelnen Variablen $x_i$ zufällig gewählt werden hängt natürlich von der Problemstellung ab. \n", + "Varianten ergeben sich auch dadurch, dass die Zufallszahlen für $x_i$ aus dem gesamten Wertebereich gewählt werden können oder dass auf einen vorhandenen Wert für $x_i$ eine zufällige Abweichung $\\Delta x_i$ aufaddiert wird.\n", + "\n", + "Für jeden zufälligen Vektor $X$ werden dann die Ziel- bzw. Gütefunktion $Q(X)$ bestimmt und die Gleichheit- und Ungleichheitsrestriktionen in der Form:\n", + "\n", + "\\begin{equation}\n", + " R(X) = 0 \\qquad \\text{und} \\qquad U(X) > 0 \n", + "\\end{equation}\n", + "\n", + "im Hinblick auf die Zulässigkeit des Vektors $X$ ausgewertet. \n", + "Beispielsweise kann bei einem zu optimierenden Bauteil die Zielfunktion die Masse sein und die Restriktionen das Einhalten der zulässigen Spannungen beschreiben. \n", + "\n", + "Aus dem betrachteten Satz an zufälligen Vektoren $X$ wird abschließend die Lösung herausgesucht, die das Gütekriterien unter (bestmöglicher) Einhaltung der Restriktionen am besten erfüllt.\n", + "Gegebenenfalls kann die so gefundene Lösung Ausgangspunkt weiterer Verfahren (z.B. lokale Suche) sein oder auch einer Wiederholung des Verfahrens, wenn ein Startvektor durch zufällig gewählte $\\Delta x_i$ modifiziert wird. \n", + "\n", + "Diese Methodik eignet sich bei schwierig zu differenzierenden Gütefunktionen, bei denen andere, z.B. gradientenbasierte Verfahren versagen würden. \n", + "Darüberhinaus ist sie in der Lage globale Minima oder Maxima zumindest annähernd zu finden. Wichtig ist dabei eine ausreichend große Anzahl an Zufallsvektoren, um die Wahrscheinlichkeit einer richtigen Lösung zu erhöhen. \n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "3e3d1714-626d-4be4-a351-7742b86f4d2c", + "metadata": { + "tags": [] + }, + "source": [ + "## **<font color='blue'>Genetische Verfahren</font>**\n", + "\n", + "Genetische Verfahren versuchen, die Schritte einer sukzessiven, stochastischen Veränderung der Vektoren $X$ zur Findung von verbesserten Lösungen am Ablauf der biologischen Evolution zu orientieren. \n", + "Daher finden sich die Begrifflichkeiten der Evolutionbiologie bei den Elementen der genetischen Verfahren wieder. \n", + "Der Vektor $X$ wird **Genom** eines Individuum bezeichnet, seine Elemente als **Chromosomen**. \n", + "\n", + "* **Population**, Menge möglicher Lösungen\n", + "* **Chromosom**, eine mögliche Lösung für ein Individuum \n", + "* **Genotyp**, Elemente enthalten in Chromosomen\n", + "* **Phenotyp**, Wert eines Gentyps \n", + "\n", + "\n", + "\n", + "Die Mischung der Genome verschiedener Individuen (Eltern) wird als **Rekombination** bzw. **Crossover** bezeichnet und führt zum Generieren von Kindern.\n", + "Die zufällige Veränderung eines Genoms erfolgt hier im Rahmen der **Mutation**.\n", + "Welche Individuen ausgewählt werden, um Kinder zu erzeugen oder um am nächsten Entwicklungsschritt teilzunehmen, wird bei einer **Selektion** entschieden. \n", + "\n", + "\n", + "### **<font color='blue'>Genereller Ablauf</font>**\n", + "\n", + "Der schematische Gesamtablauf beginnt mit einer ersten Population, einer Anzahl an Individuen, deren Chromosome initialisiert, d.h. mit zufälligen Werten belegt und bewertet werden. \n", + "Aus dieser Population werden Individuen als Eltern für eine Rekombination selektiert.\n", + "Die Rekombination liefert die Kindergeneration, aus der einige Individuen einer Mutation unterworfen werden. \n", + "Der Bewertung der neuen Individuen folgt das Zusammenführen mit der Elterngeneration. \n", + "Ein weitere Selektion reduziert diese vergrößerte Population auf die Anzahl der Individuen der Ausgangspopulation, indem die am schlechtesten bewerteten Individuen entfernt werden. \n", + "Diese Population dient dann als Ausgang für den nächsten Evolutionsschritt, der wieder mit einer Selektion von Eltern beginnt. \n", + "Dieser Evolutionsprozess ist iterativ und wird fortgeführt bis ein Abbruchkriterium erfüllt wird (maximale Zahl der Schritte, keine Verbesserung der Lösung). \n", + "\n", + "\n", + "\n", + "### **<font color='blue'>Verfahrenselemente</font>**\n", + "\n", + "Die genetischen Verfahren gehen zum einen auf die \n", + "**Evolutionsstrategien** von Rechenberg und \n", + "zum anderen auf die **genetische Algorithmen** von John Henry Holland zurück.\n", + "Mit Evolutionsstrategien wurden ursprünglich ingenieurtechnische Problemen behandelt.\n", + "Mit genetischen Algorithmen wurde versucht die grundsätzliche Struktur, \n", + "mit der in der natürlichen Evolution Informationen gespeichert und verarbeitet werden, \n", + "auf die Computeralgorithmen zu übertragen. \n", + "Genetische Algorithmen gehen genauer auf die natürlichen Gegebenheiten \n", + "der natürlichen Evolution ein, \n", + "wenngleich die Unterschiede der Evolutionsstrategien und \n", + "der genetischen Algorithmen nur in Verfahrensdetails zu erkennen sind: \n", + "\n", + "#### **<font color='blue'>Codierung</font>**\n", + "Bei Evolutionsstrategien werden die Individuen mit realzahl-codierten Parametersätzen dargestellt, während bei den genetischen Algorithmen meist eine binäre Codierung stattfindet.\n", + "\n", + "Beispiele der Codierung: \n", + "\n", + "\n", + "\n", + "#### **<font color='blue'>Bewertung</font>**\n", + "\n", + "Beurteilung der Individuen wird in erster Linie durchgeführt, um die **Qualität einer möglichen Lösung** zu bewerten. Hierzu wird eine **Bewertungsfunktion** bzw. **Qualitätsfunktion** ausgewertet. Bei den genetischen Algorithmen kommt noch zusätzlich eine Bewertung im Hinblick auf eine Teilnahme an einer Rekombination durchgeführt. Hierzu wird eine **Fitnessfunktion** herangezogen, die sich von der Qualitätsfunktion unterscheiden kann. \n", + "\n", + "#### **<font color='blue'>Selektion</font>**\n", + "\n", + "Eine Selektion wird an zwei Stellen durchgeführt: \n", + "eine Auswahl von **Individuen zur Erzeugung neuer Individuen**\n", + "und eine Auswahl zur **Bildung der nächsten Generation**. \n", + "Während bei **Evolutionsstrategien** die Elterselektion vollkommen zufällig geschieht (Zufallswahl), \n", + "wird bei **genetischen Algorithmen** eine Selektion mit einer zur Bewertung mittels der Fitnessfunktion proportionalen Wahrscheinlichkeit ausgeführt (Turnierauswahl, Rouletteauswahl). \n", + "Dies führt dazu, dass hoch bewertete Individuen ihre Erbinformationen mit größerer Wahrscheinlichkeit verbreiten können als durchschnittlich oder schlecht bewertete Individuen.\n", + "\n", + "* **Zufallswahl / Random Selection**, eine Methode zur zufälligen Auswahl von Chromosomenpaaren aus den Elternchromosomen, ohne dass die Fitnesswerte eine Rolle spielen. Einfach ausgedrückt: Es werden nur Zufallswerte erzeugt, um das Elternchromosom auszuwählen.\n", + "\n", + "* **Turnierauswahl / Tournament selection**, bei dieser Auswahlmethode wird eine Auswahl auf der Grundlage von Fitnesswerten getroffen. \n", + "Die Auswahl beginnt damit, dass mittels einer zufälligen Wahl mehrere potenzielle Eltern ausgewählt werden, aus denen das Elternteil mit dem besten Fitnesswert ausgewählt wird.\n", + "\n", + "* **Rouletteauswahl / Roulette wheel selection**, die Anwendung dieser Auswahlmethode basiert auf der Wahrscheinlichkeit eines jeden Chromosoms. Die Größe des Anteils der Chromosomen im Roulettekessel hängt vom Fitnesswert ab. Die Auswahl erfolgt, indem ein Zufallswert aus dem Bereich aller Fitnesswerte gezogen wird.\n", + "\n", + "Zur Bildung der nächsten Generation ist grundsätzlich nur eine Einfachselektion eines Individuums möglich, bei der Auswahl von Eltern aber auch eine Mehrfachselektion.\n", + "Bei der Selektion zur Bildung der nächsten Generation wird die Bewertungsfunktion herangezogen. \n", + "Verfahrensunterschiede ergeben sich durch ein vollständiges Ersetzen der Population durch die besten, neu erzeugten Kinder oder durch die Auswahl der am besten beurteilten Individuen aus der Gemeinschaft von Eltern und Kindern. \n", + "\n", + "#### **<font color='blue'>Rekombination</font>**\n", + "\n", + "Die Rekombination spielt bei den genetischen Algorithmen \n", + "eine wichtigere Rolle als bei den Evolutionsstrategien. \n", + "Das Rekombinationsverfahren bzw. Crossover-Verfahren\n", + "dient dazu, den **Suchraum schneller und zielgerichteter zu durchschreiten**, \n", + "als dies durch zufälliges Suchen möglich wäre.\n", + "\n", + "Zum Austausch der Erbinformationen sind (mindestens) zwei Elter-Chromosomen\n", + "notwendig und resultieren in zwei neuen Chromosomen.\n", + "\n", + "Das einfachste Verfahren ist das **One-Point-Crossover** \n", + "bzw. die **1-Punkt-Kreuzung**. \n", + "Hierbei übernimmt ein Nachkomme $X_c$ bis zu einer \n", + "zufällig gewählten Bruchstelle $k$ die ersten Chromosomen\n", + "von Elter $X_a$ und die restlichen vom Elter $X_b$. \n", + "**Elter** ist die nur in der Genetik übliche Singularform von Eltern).\n", + "Ein zweites Kind $X_d$ entsteht durch das komplementäre Vorgehen: \n", + "\n", + "\\begin{equation}\\;\\;\n", + "\\begin{matrix} \n", + "X_a = [ x_{1\\,a},... , x_{k\\,a}, x_{k+1\\,a},... , x_{n\\,a}] \\\\\n", + "X_b = [ x_{1\\,b},... , x_{k\\,b}, \\; x_{k+1\\,b},... , x_{n\\,b}]\n", + "\\end{matrix} \n", + " \\;\\;\\;\\; \\rightarrow \\;\\;\\;\\;\n", + "\\begin{matrix} \n", + "X_c = [ x_{1\\,a},... , x_{k\\,a}, \\; x_{k+1\\,b},... , x_{n\\,b}]\\\\\n", + "X_d = [ x_{1\\,b},... , x_{k\\,b}, \\; x_{k+1\\,a},... , x_{n\\,a}]\n", + "\\end{matrix} \n", + "\\end{equation}\n", + "\n", + "<img src=\"./Pics/Cross-Over-1.gif\" width=\"40%\" height=\"40%\">\n", + "\n", + "Mit dieser Vorgehensweise werden benachbarte Informationen, \n", + "die in den Sequenzen $[ x_{1\\,a},... , x_{k\\,a}]$ usw. \n", + "enthalten sind auf die Kinder übertragen. \n", + "Verallgemeinerungen sind die $n$-Punkt-Kreuzungen \n", + "wie z.B. die **2-Punkt-Kreuzung** mit einer \n", + "zweiten Bruchstelle.\n", + "\n", + "<img src=\"./Pics/Cross-Over-2.gif\" width=\"40%\" height=\"40%\">\n", + "\n", + "\n", + "Demgegenüber steht das **Uniform-Crossover** bzw. die **Zufallsschablone**. \n", + "Hier übernimmt der Nachkomme $X_c$ die Informationen $i$ von $X_a$ \n", + "mit der Wahrscheinlichkeit von $p = 0,5$ ansonsten von $X_b$.\n", + "Ein zweites Kind $X_d$ entsteht durch die komplementäre Übernahme.\n", + "\n", + "\\begin{equation}\n", + "X_c = [ x_{1\\,c},... , x_{i\\,c}, ... , x_{n\\,c}] \n", + "\\qquad \\text{mit} \\quad \n", + " x_{i\\,c} = \n", + "\\begin{cases}\n", + "x_{i\\,a} & \\text{mit} \\; p = 0,5 \\; , \\\\\n", + "x_{i\\,b} & \\text{sonst.}\n", + "\\end{cases}\n", + "\\end{equation}\n", + "\n", + "Die Quell und Zielindizes können auch zufällig gewählt werden: \n", + "\n", + "<img src=\"./Pics/Cross-Over-3.gif\" width=\"40%\" height=\"40%\">\n", + "\n", + "#### **<font color='blue'>Mutation</font>**\n", + "\n", + "Die Mutation dient in erster Linie der Vermeidung zu schneller Konvergenz und der Überwindung lokaler Optima.\n", + "Bei Evolutionsstrategien besitzt die Mutation eine wichtige Rolle, da die Evolutionsstrategien meist auf der Verdoppelung der Individuen basiert.\n", + "Durch die Mutation der Kopien entsteht ein neues Individuum mit einem modifizierten Variablensatz (Genen). \n", + "Die Modifikation erfolgt in der Regel ungerichtet, d.h. jede Information $x_i$ wird unabhängig verändert.\n", + " \n", + "Die Mutation erfolgt bei Evolutionsstrategien nach dem Prinzip der statistischen Normalverteilung, wodurch geringfügige Änderungen des Erbguts mit größerer Wahrscheinlichkeit auftreten als große. \n", + "Zur Bestimmung einer Mutation $x_m$ wird auf den Vektor des Nachkommens $X_c$ ein Vektor von unabhängigen Gauß-verteilten Zufallszahlen \n", + "mit dem Mittelwert 0 und der Standardabweichung $\\sigma$ aufaddiert:\n", + "\n", + "\\begin{equation}\n", + " X_m = [ x_{1\\,m},... , x_{i\\,m}, ... , x_{i\\,m}] \n", + "\\qquad \\text{mit} \\quad\n", + " x_{i\\,m} = x_{i\\,c} + N(0,\\sigma_i) \\; . \n", + "\\end{equation}\n", + "\n", + "Die Streuung der einzelnen Parameter ist problemabhängig. \n", + "Um nun möglichst schnell das Optimum zu erreichen, gibt es Techniken, die Streuungen $\\sigma_i$ in Abhängigkeit von Erfolg oder Misserfolg der Mutationen anzupassen.\n", + "\n", + "Für genetische Algorithmen mit ihrer binären Codierung ($x_i \\in \\{0,1\\}$) wird die Mutation meist durch ein Negieren des Chromosoms bei einer vorgegebenen Wahrscheinlichkeit durchgeführt: \n", + "\n", + "\\begin{equation}\n", + "X_m = [ x_{1\\,m},... , x_{i\\,m}, ... , x_{n\\,m}] \n", + "\\qquad \\text{mit} \\quad\n", + " x_{i\\,m} = \n", + "\\begin{cases}\n", + "\\urcorner x_{i\\,m} & \\text{mit z.B. } p = 1/n \\; , \\\\\n", + "x_{i\\,m} & \\text{sonst.}\n", + "\\end{cases}\n", + "\\end{equation}\n", + "\n", + "$\\urcorner$ ist darin der Negationsoperator, \n", + "der hier aus einer $0$ eine $\\urcorner 0 = 1 $ macht \n", + "und umgekehrt: $\\urcorner 1 = 0 $.\n", + "\n", + "<img src=\"./Pics/Mutation-1.gif\" width=\"40%\" height=\"40%\"> <img src=\"./Pics/Mutation-2.gif\" width=\"40%\" height=\"40%\">\n", + "\n", + "Ein-und Multi-Punkt-Mutation \n", + "\n", + "<img src=\"./Pics/Mutation-3.gif\" width=\"40%\" height=\"40%\">\n", + "\n", + "Austausch-Mutation / Swap-Muatation\n", + "\n", + "### **<font color='blue'>Grundarten</font>**\n", + "\n", + "Betrachtet man die Populationsgröße und die Zahl der Kinder, die in jeder Generation erzeugt werden, unterscheidet man folgende Varianten, deren Nomenklatur typisch für die Evolutionstrategien ist: \n", + "\n", + "* $(1+1)$ <br>\n", + "In jeder Generation besteht die Population aus **einem Individuum**.\n", + "Aus dem Elter wird 1 neues Individuum durch **Kopieren** generiert und anschließend **mutiert**. \n", + "Der fittere der beiden Individuen wird in die nächste Generation übernommen.\n", + "(vgl. Monte-Carlo-Methoden, Simulated Annealing)\n", + "\n", + "* $(\\mu\\,/\\,\\rho+1)$ <br> \n", + "In jeder Generation besteht die Population aus $\\mu$ **Individuen**.\n", + "Per Zufall werden $\\rho$ **Eltern** ausgewählt und mit ihnen per Rekombination 1 **Kind** generiert und mutiert. \n", + "Das am **wenigsten fitte Individuum** wird aus den $\\mu+1$ Individuen entfernt. \n", + "Die verbliebenen bilden die neue Generation.\n", + "\n", + "* $(\\mu\\,/\\,\\rho+\\lambda)$ <br>\n", + "Wie eben besteht eine Population aus $\\mu$ **Individuen**.\n", + "Es werden $\\lambda$ **Kinder** aus jeweils $\\rho$ **Eltern** generiert und mutiert, wobei $\\lambda \\geq \\mu$. \n", + "Die $\\mu$ **fittesten Individuen von Eltern und Kindern** bilden dann wieder die nächste Generation.\n", + "\n", + "* $(\\mu\\,/\\,\\rho,\\lambda)$ <br>\n", + "Wieder besteht eine Population aus $\\mu$ **Individuen**.\n", + "Mit ihnen werden $\\lambda$ **Kinder** durch Rekombination aus jeweils $\\rho$ **Eltern** generiert und mutiert, wobei wieder $\\lambda \\geq \\mu$ ist. \n", + "Die $\\mu$ **fittesten Kinder** bilden die nächste Generation.\n", + "Da die Eltern nicht mehr in der nächsten Generation enthalten sind,\n", + "kann es keine unsterblichen Individuen geben.\n", + "Jedoch können dadurch auch Generationen entstehen, \n", + "die schlechter sind als ihre Vorgänger. \n", + "\n", + "* $[\\mu'\\,/\\,\\rho'\\#\\lambda' (\\mu\\,/\\,\\rho\\#\\lambda)^\\gamma]$ mit $\\# \\in [+,]$ <br>\n", + "Grundsätzlich lassen sich auch mehrere Populationen **parallel** behandeln. \n", + "Bei der Nomenklatur bezieht sich dann die innere **Klammerebene** auf die Individuen der **einzelnen Population**, die äußere auf die **verschiedenen Populationen**. \n", + "Dies bedeutet, dass aus $\\mu'$ **Elternpopulationen** $\\lambda'$ **Kinderpopulationen** - ggf. durch Rekombination aus jeweils $\\rho'$ **Populationen** - erzeugt werden. \n", + "Die Kinderpopulationen verhalten sich dann für $\\gamma$ **Generationen** nach der Art $(\\mu/\\rho\\#\\lambda)$, wobei $\\#$ gleich $+$ oder $,$ sein kann. \n", + "Abschließend werden die $\\mu'+\\lambda'$ bzw. $\\lambda'$ Populationen bewertet und die besten $\\mu'$ wieder für den nächsten Schritt als Menge der Ausgangspopulationen herangezogen. \n" + ] + }, + { + "cell_type": "markdown", + "id": "4ff7fd2d-b88d-40b0-a2ac-2ebb4590e2fd", + "metadata": { + "tags": [] + }, + "source": [ + "## **<font color='blue'>Rucksackproblem</font>**" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "ebffe1ed-4a60-45b8-9a29-26e63694d6e0", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "gen\tnevals\tmax\tavg \n", + "0 \t44 \t805\t595.818\n", + "1 \t42 \t840\t694.818\n", + "2 \t40 \t887\t755.614\n", + "3 \t40 \t902\t806.864\n", + "4 \t40 \t940\t829.205\n", + "5 \t42 \t937\t856.886\n", + "6 \t42 \t965\t886.182\n", + "7 \t37 \t965\t899.886\n", + "8 \t38 \t967\t923.091\n", + "9 \t41 \t965\t943.273\n", + "10 \t42 \t965\t948.114\n", + "11 \t40 \t965\t955.818\n", + "12 \t40 \t965\t960.841\n", + "13 \t40 \t965\t964.25 \n", + "14 \t42 \t965\t958.068\n", + "15 \t41 \t965\t961.523\n", + "16 \t37 \t965\t956.068\n", + "17 \t41 \t965\t960.568\n", + "18 \t37 \t965\t957.273\n", + "19 \t42 \t965\t959.318\n", + "20 \t42 \t965\t962.045\n", + "21 \t44 \t965\t961.136\n", + "22 \t39 \t965\t960.045\n", + "++ Brute-Force Permutationen: 484\n", + "-- Bestes Individuum : [1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1]\n", + "-- Beste Fitness : 967.0\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "<Figure size 640x480 with 1 Axes>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from deap import base, creator, tools, algorithms\n", + "\n", + "import random\n", + "import numpy \n", + "import matplotlib.pyplot as plt\n", + "\n", + "random.seed( 42 ) # Für reproduzierbar zufällige Zahlen\n", + "\n", + "# item, weight, value\n", + "if False: \n", + " items = [ ( \"A\", 100, 40 ), \n", + " ( \"B\", 50, 35 ), \n", + " ( \"C\", 45, 18 ), \n", + " ( \"D\", 20, 4 ), \n", + " ( \"E\", 10, 10 ), \n", + " ( \"F\", 5, 2 ) ] \n", + " maxCapacity = 100\n", + "else: \n", + " items = [ (\"map\", 9, 150),\n", + " (\"compass\", 13, 35),\n", + " (\"water\", 153, 200),\n", + " (\"sandwich\", 50, 160),\n", + " (\"glucose\", 15, 60),\n", + " (\"tin\", 68, 45),\n", + " (\"banana\", 27, 60),\n", + " (\"apple\", 39, 40),\n", + " (\"cheese\", 23, 30),\n", + " (\"beer\", 52, 10),\n", + " (\"suntan cream\", 11, 70),\n", + " (\"camera\", 32, 30),\n", + " (\"t-shirt\", 24, 15),\n", + " (\"trousers\", 48, 10),\n", + " (\"umbrella\", 73, 40),\n", + " (\"waterproof trousers\", 42, 70),\n", + " (\"waterproof overclothes\", 43, 75),\n", + " (\"note-case\", 22, 80),\n", + " (\"sunglasses\", 7, 20),\n", + " (\"towel\", 18, 12),\n", + " (\"socks\", 4, 50),\n", + " (\"book\", 30, 10) ]\n", + " maxCapacity = 400\n", + "\n", + "NBR_ITEMS = len( items )\n", + "\n", + "# Definition der Fitness-Funktion \n", + "def getKnapsackValue(zeroOneList):\n", + " totalWeight = totalValue = 0\n", + " for i in range(len(zeroOneList)):\n", + " item, weight, value = items[i]\n", + " if totalWeight + weight <= maxCapacity:\n", + " totalWeight += zeroOneList[i] * weight\n", + " totalValue += zeroOneList[i] * value\n", + " return totalValue, \n", + "\n", + "\n", + "# Konstanten des genetischen Algorithmus\n", + "POPULATION_SIZE = 2*NBR_ITEMS \n", + "MAX_GENERATIONS = 1*NBR_ITEMS \n", + "P_CROSSOVER = 0.9 # Wahrscheinlichkeit der Rekombination \n", + "P_MUTATION = 0.1 # Wahrscheinlichkeit der Mutation \n", + "HALL_OF_FAME_SIZE = 1\n", + "\n", + "\n", + "# Neue Klasse \"Fitness\", abgeleitet von base.Fitness, mit Attribut weights \n", + "# Fitness ist die Maximierung des Gesamtgewichts\n", + "creator.create( \"FitnessMax\", base.Fitness, weights=(1.0,))\n", + "\n", + "# Neue Klasse \"Individual\", abgeleitet von \"list\", mit Attribut fitness.\n", + "creator.create( \"Individual\", list, fitness=creator.FitnessMax)\n", + "\n", + "# Initalisieren eine neue Toolbox \n", + "toolbox = base.Toolbox()\n", + "\n", + "# Registrieren des Attribut-Generatorfunktion randint() unter \"attr_item\" : Liefert zufällig 0 oder 1\n", + "toolbox.register( \"zeroOrOne\", random.randint, 0, 1 )\n", + "\n", + "# Registrieren einer Individuum-Generatorfunktion unter \"individualCreator\" : initRepeat(container, func, n)\n", + "toolbox.register( \"individualCreator\", tools.initRepeat, creator.Individual, toolbox.zeroOrOne, NBR_ITEMS )\n", + "\n", + "# Registrieren einer Populations-Generatorfunktion unter \"populationCreator\" : initRepeat(container, func, n), d.h. n muss beim Aufruf gegeben werden. \n", + "toolbox.register( \"populationCreator\", tools.initRepeat, list, toolbox.individualCreator )\n", + "\n", + "# Registrieren der Fitness-Berechung unter \"evaluate\"\n", + "toolbox.register( \"evaluate\", getKnapsackValue)\n", + "\n", + "# Registrieren der Selektion : Tournament mit Turniergröße 3\n", + "toolbox.register( \"select\", tools.selTournament, tournsize=3 )\n", + "\n", + "# Registrieren der Rekombination Single-point crossover:\n", + "toolbox.register( \"mate\", tools.cxTwoPoint )\n", + "\n", + "# Registrieren der Mutation FlipBit : indpb Wahrscheinlichkeit des Flips\n", + "toolbox.register( \"mutate\", tools.mutFlipBit, indpb=1.0/NBR_ITEMS )\n", + "\n", + "\n", + "# Gesamtablauf\n", + "def main():\n", + "\n", + " # Erzugen einer initialen Population (Generation 0)\n", + " population = toolbox.populationCreator( n=POPULATION_SIZE )\n", + "\n", + " # Definition des Statistik-Objects\n", + " stats = tools.Statistics( lambda ind: ind.fitness.values )\n", + " stats.register( \"max\", numpy.max )\n", + " stats.register( \"avg\", numpy.mean )\n", + "\n", + " # Definition des Hall-of-Fame Objekts\n", + " hof = tools.HallOfFame(HALL_OF_FAME_SIZE)\n", + "\n", + " # perform the Genetic Algorithm flow with hof feature added:\n", + " population, logbook = algorithms.eaSimple( population, toolbox, \n", + " cxpb=P_CROSSOVER, mutpb=P_MUTATION,\n", + " ngen=MAX_GENERATIONS, \n", + " stats=stats, halloffame=hof, verbose=True)\n", + "\n", + " # print best solution found:\n", + " best = hof.items[0]\n", + " print( \"++ Brute-Force Permutationen:\", NBR_ITEMS**2 ) \n", + " print( \"-- Bestes Individuum : \", best )\n", + " print( \"-- Beste Fitness : \", best.fitness.values[0] )\n", + "\n", + " # extract statistics:\n", + " maxFitnessValues, meanFitnessValues, nevals = logbook.select( \"max\", \"avg\", \"nevals\" )\n", + " \n", + " for i in range(1,len(nevals)): \n", + " nevals[i] = nevals[i-1] + nevals[i]\n", + "\n", + " # plot statistics:\n", + " #sns.set_style(\"whitegrid\")\n", + " plt.plot( maxFitnessValues, color='red' )\n", + " plt.plot( meanFitnessValues, color='green' )\n", + " plt.plot( nevals, color='blue' )\n", + " plt.xlabel( 'Generation' )\n", + " plt.ylabel( 'Max / Average Fitness' )\n", + " plt.title( 'Max and Average fitness over Generations' )\n", + " plt.grid()\n", + " plt.show()\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " main()" + ] + }, + { + "cell_type": "markdown", + "id": "f7f74e23-10e4-41d2-b16f-e3103254a027", + "metadata": { + "tags": [] + }, + "source": [ + "### **<font color='blue'>Übungsaufgabe</font>**\n", + "\n", + "Wie sieht der Algorithmus für das Rucksackproblem aus, wenn man die Indizes der ausgewählten Elemente im Lösungsvektor (Python `set`) speichert? " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c11c0a14-0714-40f6-919c-c2209899256b", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "@deathbeds/ipydrawio": { + "xml": "" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Semester_2/Einheit_04/Grundlagen_DEAP.ipynb b/Semester_2/Einheit_04/Grundlagen_DEAP.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..ff4d4492a81a12ab5ad5bda29aafacd0a6d63ee4 --- /dev/null +++ b/Semester_2/Einheit_04/Grundlagen_DEAP.ipynb @@ -0,0 +1,182 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b063833e-e3ce-4dcc-82a8-1fa689b46a34", + "metadata": { + "tags": [] + }, + "source": [ + "## **<font color='blue'>DEAP - Distributed Evolutionary Algorithms in Python</font>**\n", + "\n", + "DEAP ist ein Framework für evolutionäre Berechnungen, dass insbesondere dem schnellen Prototyping und dem Testen von Ideen dient. \n", + "Sein Design versucht, Algorithmen explizit und Datenstrukturen transparent zu machen. \n", + "Es beinhaltet auch eine einfache Parallelität, bei der sich die Benutzer nicht mit Implementierungsdetails wie Synchronisierung und Lastausgleich befassen müssen, sondern nur mit der funktionalen Dekomposition.\n", + "\n", + "DEAP ist aus drei Kern-Modulen sowie weiteren Modulen aufgebaut:\n", + "\n", + "<img src=\"./Pics/Deap_Architecture.png\" width=\"40%\" height=\"40%\">\n", + "\n", + "* **Base** <br>\n", + "Das Basismodul **Base** enthält **Objekte** und **Datenstrukturen**, die in evolutionären Verfahren häufig verwendet werden und nicht bereits in der Python-Standardbibliothek implementiert sind. Da Python bereits viele der benötigten Datenstrukturen bereitstellt, implementiert dieses Modul eigentlich nur drei Klassen: eine **generische Fitness**, eine **Toolbox** und einen **speziellen Baum**. \n", + "Die Toolbox ist ein Container für die Werkzeuge (**Operatoren**), die der Benutzer in seinem evolutionären Algorithmus verwenden möchte.\n", + "Beispielsweise kann eine Nutzer eine eigene Mutation nutzen, indem er eine nahekommende verfügbare `MutationXYZ` bei der Registrierung der eigenen `Mutation` in der Toolbox nutzt. \n", + " \n", + "* **Creator** <br>\n", + "Das **Creator**-Modul ist eine **Meta-Fabrik**, die die **Erstellung von Klassen** durch Vererbung und Komposition unter Verwendung eines funktionalen Programmierparadigma ermöglicht, d.h. ohne den Aufwand des Nutzers für die Definition von Klassen. **Attribute**, sowohl Daten als auch Funktionen, können dynamisch hinzugefügt werden, um neue Klassen zu erstellen.\n", + "\n", + "* **Tools** <br>\n", + "Das Modul **Tools** enthält häufig verwendete Operatoren (**Operators**) der evolutionären Algorithmen. Sie werden eingesetzt, um die Individuen in ihrer Umgebung zu **verändern, auszuwählen** und zu **verschieben**. \n", + "Außerdem bietet es Objekte, die verschiedene Analyseaufgaben wie Checkpointing, Statistikauswertung und Genealogie erleichtern. \n", + "Eine Toolbox selbst ist eine Behälter für die vom Nutzer ausgewählten Werkzeuge.\n", + "\n", + "* **Algorithms** <br>\n", + "Die Kernfunktionalitäten von DEAP werden durch das Algorithmenmodul **Algorithms** ergänzt, das häufig verwendete **Populations-Methoden** enthält: \n", + "z.B. (μ , λ), (μ + λ). DEAP ist jedoch in keiner Weise auf diese Verfahren beschränkt.\n", + "Sie sind nur ein Ausgangspunkt für Nutzer, die ihre eigenen maßgeschneiderten Algorithmen entwickeln wollen.\n", + "\n", + "* **GP - Genetic Programming** <br>\n", + "Operatoren und Werkzeuge, die nicht in den Kernmodulen enthalten sind, finden sich in eigenen Modulen, \n", + "z.B. Operatoren und Datenstrukturen der Genetischen Programmierung (GP) im **GP**-Modul. \n", + "\n", + "* **Benchmark** <br>\n", + "Das Modul **Benchmark** enthält verschiedene State-of-the-Art-Benchmark-Funktionen, die zur Bewertung der Algorithmenleistung verwendet werden können. \n", + "\n", + "* **DTM - Distributed Task Manager** <br>\n", + "Das letzte Modul des Frameworks mit dem Namen **DTM**, der für Distributed Task Manager, behandelt die Parallelität.\n", + "\n", + "### **<font color='blue'>Wichtige Klassen und Funktionen</font>**\n", + "\n", + "* **Creator**\n", + "\n", + " * `creator.create(name, base[, attribute[, ... ]])` : Erzeugt eine neue Klasse mit dem Namen `name`, die von `base` im Creator-Modul erbt. Die neue Klasse kann Attribute haben, die durch die nachfolgenden Schlüsselwortargumente definiert werden. \n", + "<br>\n", + "\n", + "* **Toolbox**\n", + " \n", + " * Class `base.Toolbox()` : Erzeugt ein neues Toolboxobjekt.\n", + " * `register(alias, method[, argument[, ... ]])` : Registriert eine Methode `method` in der Toolbox unter dem Namen `alias`. Es können Standardargumente angeben werden, die automatisch übergeben werden, wenn die registrierte Methode aufgerufen wird. Die Standardargumente können dann beim Funktionsaufruf überschrieben werden.\n", + "<br>\n", + " \n", + "* **Fitness**\n", + " \n", + " * Class `base.Fitness([values ])` : Die Fitness ist ein Maß für die Qualität einer Lösung. Wenn `values` als Tupel angegeben werden, wird die Fitness mit diesen Werten initialisiert. Die Werte der Fitness `values` können über `individual.fitness.values = values` gesetzt werden.\n", + "<br>\n", + "\n", + "* **Operatoren**\n", + "Sie stellen die Grundfunktionen für die Transformation (Rekombination, Mutation) oder Selektion der Individuen bereit. Werden beispielsweise zwei Individuen für eine Rekombination bereitgestellt, so erfolgt diese Rekombination in-place. Das Lösen der Nachkommen von ihren Eltern und das Zurücksetzen der Fitness obliegt dem Nutzer. Im Allgemeinen bietet es sich an mit dem Aufruf der `toolbox.clone()` Funktion ein Individuum zu clonen und mittel `del` das Attribut `values` zu leeren. \n", + "\n", + " * **Initalisierung** - in DEAP implementiert: \n", + " * `tools.initRepeat(container, func, n)` : Aufruf des Funktions`container`s mit einer Generatorfunktion, die dem n-fachen Aufruf der Funktion func entspricht.\n", + " * `container` – Datentyp zum Speichern der Ergebnisse von `func`.\n", + " * `func` – Funktion, die n-mal aufgerufen wird, um den `container` zu füllen.\n", + " * `n` – Anzahl der Wiederholungen von `func`.\n", + " * Return: Eine Instanz des gefüllten `container` \n", + " \n", + " <br>\n", + "\n", + " * **Rekombination/Crossover** - in DEAP implementiert: \n", + " * `tools.cxOnePoint(ind1, ind2)` : Führt eine Ein-Punkt-Kreuzung an den gegebenen Individuen `ind1`, `ind2` durch. Beide Individuen werden in-place verändert und zurückgegeben.\n", + " * `tools.cxTwoPoint(ind1, ind2)` : Führt eine Zwei-Punkt-Kreuzung an den gegebenen Individuen `ind1`, `ind2` durch. Beide Individuen werden in-place verändert und zurückgegeben. Nutzt die `randint()` von Python. \n", + " * `tools.cxUniform(ind1, ind2, indpb)` : Führt eine uniforme Kreuzung an den gegebenen Individuen `ind1`, `ind2` durch. Die Attribute der beiden Individuen werden in-place entsprechend der `indpb`-Wahrscheinlichkeit vertauscht. Nutzt die `random()` von Python. \n", + "\n", + " <br>\n", + " \n", + " * **Mutationen** - in DEAP implementiert: \n", + " * `tools.mutFlipBit(individual, indpb)` : Umkehrung des Attribute mittels des not-Operators bei gegebener Wahrscheinlichkeit `indpb` des gegebenen Individuums und Rückgabe der Mutante. Anwendung normalerweise auf boolesche Werte.\n", + " * `tools.mutGaussian(individual, mu, sigma, indpb)` : Gaußsche Mutation mit dem Mittelwert `mu` und der Standardabweichung `sigma` auf das gegebene Individuum mit reellen Attributen. `indpb` ist die Wahrscheinlichkeit mit der ein Attribut mutiert werden soll.\n", + " \n", + " <br>\n", + " \n", + " * **Selektion** - in DEAP implementiert: \n", + " * `tools.selTournament(individuals, k, tournsize)` : Auswahl von `k` Individuen aus den gegebenen Individuen `individuals` unter Verwendung von `k` Turnieren mit `tournsize` Individuen.\n", + " * `tools.selRoulette(individuals, k)` : Auswahl von `k` Individuen aus den gegebenen Individuen durch `k` Drehungen eines Roulettes. Die Auswahl erfolgt nur aufgrund des erste Fitnesswerts.\n", + " * `tools.selBest(individuals, k)` : Auswahl der `k` besten Individuen aus den gegebenen Individuen.\n", + "\n", + " <br>\n", + " \n", + " * **Statistik** - in DEAP implementiert: \n", + " * Class `tools.Statistics([key][, n])` : Liefert ein Statistikobjekt, das die verlangten Daten enthält. Bei der Erstellung erhält das Statistikobjekt ein Schlüsselargument, um die erforderlichen Daten zu erhalten. Ein Statistik-Objekt kann für jede registrierte Funktion als 3-dimensionale Matrix angesehen werden.\n", + " * `register(name, function)` : Registrierung einer Funktion, die bei jedem Aufruf von `update()` auf die Sequenz angewendet wird.\n", + " * `update(seq, index=0, add=False)` : Wendet auf die Eingabesequenz `seq` jede registrierte Funktion an und speichert das Ergebnis in einer Liste, zugehörig zur Funktion und dem Datenindex `index`.\n", + " * `tools.mean(seq), tools.median(seq), tools.var(seq), tools.std(seq)` : Returns the arithmetic mean, the median, the variance and the square root of the variance of the sequence `seq`.\n", + " \n", + " \n", + " <br>\n", + " \n", + " * **HallOfFame** - in DEAP implementiert: \n", + " * Class `tools.HallOfFame(maxsize)` : Die Ruhmeshalle enthält die besten Individuen, die im Laufe der Evolution in der Population vorhanden waren. Sie ist immer so sortiert, dass das erste Element der Ruhmeshalle das Individuum ist, das den besten jemals erreichten ersten Fitnesswert hat. \n", + " * Class `tools.ParetoFront([similar])` : Die Ruhmeshalle der Pareto-Front enthält alle nicht-dominierten Individuen, die jemals in der Population vorhanden waren.\n", + " \n", + "* **Algorithms**\n", + "\n", + "Das Modul Algorithmen soll Algorithmen enthalten, um sehr gängige evolutionäre Algorithmen auszuführen. \n", + "Die hier verwendeten Methoden dienen eher der Bequemlichkeit als als Referenz, \n", + "da die Implementierung jedes evolutionären Algorithmus unendlich variieren kann. \n", + "Die meisten Algorithmen in diesem Modul verwenden die in der Toolbox registrierten Operatoren. \n", + "Im Allgemeinen werden die Schlüsselwörter `mate()` für Crossover, `mutate()` für Mutation, `select()` für Selektion und `evaluate()` für Evaluation verwendet.\n", + "\n", + " * `algorithms.eaSimple(population, toolbox, cxpb, mutpb, ngen[, stats, halloffame, verbose ])` : Dieser Algorithmus reproduziert den einfachen evolutionären Algorithmus. \n", + "Diese Funktion erwartet, dass die Aliase `toolbox.mate()`, `toolbox.mutate()`, `toolbox.select()` und `toolbox.evaluate()` registriert sind.\n", + "Der Algorithmus verwendet λ = κ = μ und geht wie folgt vor. Zunächst wird die Population (P(0)) initialisiert, indem jedes Individuum bewertet wird. \n", + "Dann wird die Evolutionsschleife gestartet, die mit der Auswahl der Population P(g+1) beginnt. Der Crossover-Operator wird auf einen Teil von P(g+1) entsprechend der `cxpb`-Wahrscheinlichkeit angewandt. Die resultierenden und die unveränderten Individuen werden in P'(g+1) platziert. \n", + "Danach wird ein Teil von P'(g+1) über `mutpb` ausgewählt, mutiert und in P''(g+1) platziert, die unberührten Individuen werden auch in P''(g + 1) übertragen. Schließlich werden diese neuen Individuen bewertet und die Evolutionsschleife wird fortgesetzt, bis `ngen` Generationen abgeschlossen sind. Die Operatoren werden in der folgenden Reihenfolge angewendet: \n", + "\n", + "<br>\n", + "\n", + " evaluate(population)\n", + " for i in range(ngen):\n", + " descendants = select(population)\n", + " descendants = mate(descendants)\n", + " descendants = mutate(descendants)\n", + " evaluate(descendants)\n", + " population = descendants\n", + "\n", + " * `algorithms.eaMuPlusLambda(population, toolbox, mu, lambda_, cxpb, mutpb, ngen[, stats, halloffame, verbose ])` : Dies ist der (μ + λ) evolutionäre Algorithmus. `mu` ist die Anzahl der Individuen, die für die nächste Generation ausgewählt werden, `lambda`die Anzahl der Kinder, die in jeder Generation erzeugt werden. \n", + " Zunächst werden alle Individuen bewertet. Dann beginnt die Evolutionsschleife mit der Erzeugung von `lambda` Nachkommen aus der Population, die Nachkommen werden durch Kreuzung, Mutation oder Reproduktion erzeugt proportional zu den Wahrscheinlichkeiten `cxpb`, `mutpb` und `1 - (cxpb + mutpb)`. Die Nachkommen werden ausgewertet und die Population der nächsten Generation wird sowohl aus den Nachkommen als auch aus der Population ausgewählt.\n", + " \n", + "<br>\n", + " \n", + " evaluate(population)\n", + " for i in range(ngen):\n", + " descendants = varOr(population, toolbox, lambda_, cxpb, mutpb)\n", + " evaluate(descendants)\n", + " population = select(population + descendants, mu)\n", + "\n", + " * **Variations**\n", + " * `algorithms.varAnd(population, toolbox, cxpb, mutpb)` : Teil eines evolutionären Algorithmus, der nur den Variationsteil (Crossover und Mutation) anwendet. Die veränderten Individuen haben noch keine Fitness-Bewertung. Die Individuen werden geklont, so dass die erzeugte Population unabhängig ist von der Eingabepopulation ist.\n", + " Bei der Variation wird zunächst die elterliche Population $P_p$ mit der `toolbox.clone()`-Methode dupliziert und das Ergebnis in die Nachkommenpopulation $P_o$ gesetzt. Eine erste Schleife über $P_o$ wird ausgeführt, um aufeinanderfolgende Individuen zu paaren. Entsprechend der Kreuzungswahrscheinlichkeit `cxpb` werden die Individuen $x_i$ und $x_{i+1}$ mit der Methode `toolbox.mate()` gepaart. Die resultierenden Kinder $y_i$ und $y_{i+1}$ ersetzen ihre jeweiligen Eltern in $P_o$. \n", + "Eine zweite Schleife über das resultierende $P_o$ wird ausgeführt, um jedes Individuum mit einer Wahrscheinlichkeit `mutpb` zu mutieren. \n", + "Wenn ein Individuum mutiert ist, ersetzt es seine nicht mutierte Version in $P_o$. \n", + "Das resultierende $P_o$ wird zurückgegeben.\n", + "Diese Variante wird `And` genannt, weil sie sowohl Crossover als auch Mutation auf die Individuen anwenden kann. \n", + "Man beachte, dass beide Operatoren nicht systematisch angewandt werden, die resultierenden Individuen können nur durch Crossover, nur durch Mutation, durch Crossover und Mutation und durch Reproduktion entsprechend der angegebenen Wahrscheinlichkeiten erzeugt werden.\n", + " \n", + " * `algorithms.varOr(population, toolbox, lambda_, cxpb, mutpb)` : Bei jeder `lambda`-Iteration wählt die Variation eine der drei Operationen: Kreuzung, Mutation oder Reproduktion. Im Falle einer Kreuzung werden zwei Individuen zufällig aus der elterlichen Population $P_p$ ausgewählt. Diese Individuen werden mit der Methode `toolbox.clone()` geklont und dann mit der Methode `toolbox.mate()` gepaart. Nur das erste Kind wird an die Nachkommenpopulation $P_o$ angehängt, das zweite Kind wird verworfen. <br> \n", + " Im Falle einer Mutation wird ein Individuum zufällig aus $P_p$ ausgewählt, geklont und dann mit Hilfe der Methode `toolbox.mutate()` mutiert. Die resultierende Mutante wird an $P_o$ angehängt. Im Falle einer Reproduktion wird ein Individuum nach dem Zufallsprinzip aus $P_p$ ausgewählt, geklont und an $P_o$ angehängt. <br> \n", + " Diese Variante wird `Or` genannt, weil niemals ein Nachkomme aus beiden Operationen Crossover und Mutation resultieren wird. Die Summe der beiden Wahrscheinlichkeiten soll in [0, 1] liegen, die Reproduktionswahrscheinlichkeit ist `1 - cxpb - mutpb`." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Semester_2/Einheit_04/Pics/.ipynb_checkpoints/Mutation-1-checkpoint.gif b/Semester_2/Einheit_04/Pics/.ipynb_checkpoints/Mutation-1-checkpoint.gif new file mode 100644 index 0000000000000000000000000000000000000000..5674408068e619515a18c1450e278ca0def2b5c3 Binary files /dev/null and b/Semester_2/Einheit_04/Pics/.ipynb_checkpoints/Mutation-1-checkpoint.gif differ diff --git a/Semester_2/Einheit_04/Pics/.ipynb_checkpoints/Mutation-2-checkpoint.gif b/Semester_2/Einheit_04/Pics/.ipynb_checkpoints/Mutation-2-checkpoint.gif new file mode 100644 index 0000000000000000000000000000000000000000..9a791d56f80e8c1d4d7eefd6c843a34f445e69bc Binary files /dev/null and b/Semester_2/Einheit_04/Pics/.ipynb_checkpoints/Mutation-2-checkpoint.gif differ diff --git a/Semester_2/Einheit_04/Pics/.ipynb_checkpoints/Mutation-3-checkpoint.gif b/Semester_2/Einheit_04/Pics/.ipynb_checkpoints/Mutation-3-checkpoint.gif new file mode 100644 index 0000000000000000000000000000000000000000..841d84cfad431bff9f48d0e12ea71834f413745c Binary files /dev/null and b/Semester_2/Einheit_04/Pics/.ipynb_checkpoints/Mutation-3-checkpoint.gif differ diff --git a/Semester_2/Einheit_04/Pics/Ablauf-GA.svg b/Semester_2/Einheit_04/Pics/Ablauf-GA.svg new file mode 100644 index 0000000000000000000000000000000000000000..aa54b27810ef41258333ca4c26318ee383701df6 --- /dev/null +++ b/Semester_2/Einheit_04/Pics/Ablauf-GA.svg @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="550px" preserveAspectRatio="none" style="width:417px;height:550px;" version="1.1" viewBox="0 0 417 550" width="417px" zoomAndPan="magnify"><defs><filter height="300%" id="fgrty9rvwu9y2" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><ellipse cx="211.5" cy="20" fill="#000000" filter="url(#fgrty9rvwu9y2)" rx="10" ry="10" style="stroke: none; stroke-width: 1.0;"/><rect fill="#FEFECE" filter="url(#fgrty9rvwu9y2)" height="39.0679" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="171" x="126" y="50"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="151" x="136" y="74.9659">initialisiere Population</text><rect fill="#FEFECE" filter="url(#fgrty9rvwu9y2)" height="39.0679" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="296" x="63.5" y="176.1358"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="276" x="73.5" y="201.1018">bestimme Fitnesswert für Elternselektion</text><rect fill="#FEFECE" filter="url(#fgrty9rvwu9y2)" height="39.0679" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="292" x="65.5" y="235.2038"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="272" x="75.5" y="260.1697">selektiere Individuen/Eltern für Paarung</text><rect fill="#FEFECE" filter="url(#fgrty9rvwu9y2)" height="39.0679" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="197" x="113" y="294.2717"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="177" x="123" y="319.2376">erzeuge Rekombinationen</text><rect fill="#FEFECE" filter="url(#fgrty9rvwu9y2)" height="39.0679" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="157" x="133" y="359.3056"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="137" x="143" y="384.2716">erzeuge Mutationen</text><rect fill="#FEFECE" filter="url(#fgrty9rvwu9y2)" height="39.0679" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="335" x="44" y="418.3735"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="315" x="54" y="443.3395">bestimme Fitnesswert für Populationsselektion</text><rect fill="#FEFECE" filter="url(#fgrty9rvwu9y2)" height="39.0679" rx="12.5" ry="12.5" style="stroke: #A80036; stroke-width: 1.5;" width="300" x="61.5" y="477.4415"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="280" x="71.5" y="502.4074">selektiere Individuen für neue Population</text><polygon fill="#FEFECE" filter="url(#fgrty9rvwu9y2)" points="141,109.0679,282,109.0679,294,121.0679,282,133.0679,141,133.0679,129,121.0679,141,109.0679" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="30" x="215.5" y="148.0339">nein</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="141" x="141" y="126.4999">Stopkriteria erreicht?</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="12" x="117" y="116.9659">ja</text><ellipse cx="24" cy="167.0679" fill="none" filter="url(#fgrty9rvwu9y2)" rx="10" ry="10" style="stroke: #000000; stroke-width: 1.0;"/><ellipse cx="24.5" cy="167.5679" fill="#000000" filter="url(#fgrty9rvwu9y2)" rx="6" ry="6" style="stroke: none; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="211.5" x2="211.5" y1="30" y2="50"/><polygon fill="#A80036" points="207.5,40,211.5,50,215.5,40,211.5,44" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="211.5" x2="211.5" y1="215.2038" y2="235.2038"/><polygon fill="#A80036" points="207.5,225.2038,211.5,235.2038,215.5,225.2038,211.5,229.2038" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="211.5" x2="211.5" y1="274.2717" y2="294.2717"/><polygon fill="#A80036" points="207.5,284.2717,211.5,294.2717,215.5,284.2717,211.5,288.2717" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="211.5" x2="211.5" y1="333.3396" y2="359.3056"/><polygon fill="#A80036" points="207.5,349.3056,211.5,359.3056,215.5,349.3056,211.5,353.3056" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="211.5" x2="211.5" y1="398.3735" y2="418.3735"/><polygon fill="#A80036" points="207.5,408.3735,211.5,418.3735,215.5,408.3735,211.5,412.3735" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="211.5" x2="211.5" y1="457.4415" y2="477.4415"/><polygon fill="#A80036" points="207.5,467.4415,211.5,477.4415,215.5,467.4415,211.5,471.4415" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="211.5" x2="211.5" y1="133.0679" y2="176.1358"/><polygon fill="#A80036" points="207.5,166.1358,211.5,176.1358,215.5,166.1358,211.5,170.1358" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="211.5" x2="211.5" y1="516.5094" y2="526.5094"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="211.5" x2="391" y1="526.5094" y2="526.5094"/><polygon fill="#A80036" points="387,339.3056,391,329.3056,395,339.3056,391,335.3056" style="stroke: #A80036; stroke-width: 1.5;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="391" x2="391" y1="121.0679" y2="526.5094"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="391" x2="294" y1="121.0679" y2="121.0679"/><polygon fill="#A80036" points="304,117.0679,294,121.0679,304,125.0679,300,121.0679" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="129" x2="24" y1="121.0679" y2="121.0679"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="24" x2="24" y1="121.0679" y2="157.0679"/><polygon fill="#A80036" points="20,147.0679,24,157.0679,28,147.0679,24,151.0679" style="stroke: #A80036; stroke-width: 1.0;"/><line style="stroke: #A80036; stroke-width: 1.5;" x1="211.5" x2="211.5" y1="89.0679" y2="109.0679"/><polygon fill="#A80036" points="207.5,99.0679,211.5,109.0679,215.5,99.0679,211.5,103.0679" style="stroke: #A80036; stroke-width: 1.0;"/><!-- +@startuml + +skinparam defaultFontSize 14 +skinparam classAttributeIconSize 0 +scale max 1024 width + +start + +:initialisiere Population; + +while (Stopkriteria erreicht?) is (nein) + :bestimme Fitnesswert für Elternselektion; + :selektiere Individuen/Eltern für Paarung; + :erzeuge Rekombinationen; + :erzeuge Mutationen; + :bestimme Fitnesswert für Populationsselektion; + :selektiere Individuen für neue Population; +endwhile (ja) + +stop +@enduml + +PlantUML version 1.2018.13(Mon Nov 26 18:11:51 CET 2018) +(GPL source distribution) +Java Runtime: OpenJDK Runtime Environment +JVM: OpenJDK 64-Bit Server VM +Java Version: 11.0.18+10-post-Ubuntu-0ubuntu120.04.1 +Operating System: Linux +OS Version: 5.15.0-71-generic +Default Encoding: UTF-8 +Language: de +Country: DE +--></g></svg> \ No newline at end of file diff --git a/Semester_2/Einheit_04/Pics/Ablauf-GA.uml b/Semester_2/Einheit_04/Pics/Ablauf-GA.uml new file mode 100644 index 0000000000000000000000000000000000000000..afde4a6cb86825d5661c84bf86a8598e9c26baa5 --- /dev/null +++ b/Semester_2/Einheit_04/Pics/Ablauf-GA.uml @@ -0,0 +1,26 @@ + +@startuml + +skinparam defaultFontSize 14 +skinparam classAttributeIconSize 0 +scale max 1024 width + +start + +:initialisiere Population; + +while (Stopkriteria erreicht?) is (nein) + :bestimme Fitnesswert für Elternselektion; + :selektiere Individuen/Eltern für Paarung; + :erzeuge Rekombinationen; + :erzeuge Mutationen; + :bestimme Fitnesswert für Populationsselektion; + :selektiere Individuen für neue Population; +endwhile (ja) + +stop +@enduml + + + + diff --git a/Semester_2/Einheit_04/Pics/Chromosom.png b/Semester_2/Einheit_04/Pics/Chromosom.png new file mode 100644 index 0000000000000000000000000000000000000000..915fe7e65787725e1e6163a9dc97e7ebd0d7345a Binary files /dev/null and b/Semester_2/Einheit_04/Pics/Chromosom.png differ diff --git a/Semester_2/Einheit_04/Pics/Codierung.png b/Semester_2/Einheit_04/Pics/Codierung.png new file mode 100644 index 0000000000000000000000000000000000000000..65b3691c6c38ec1ce983bd45fb04c35f4fe8df72 Binary files /dev/null and b/Semester_2/Einheit_04/Pics/Codierung.png differ diff --git a/Semester_2/Einheit_04/Pics/Cross-Over-1.gif b/Semester_2/Einheit_04/Pics/Cross-Over-1.gif new file mode 100644 index 0000000000000000000000000000000000000000..18c170781ec9b3f55d360554e58b8f7bd020ad82 Binary files /dev/null and b/Semester_2/Einheit_04/Pics/Cross-Over-1.gif differ diff --git a/Semester_2/Einheit_04/Pics/Cross-Over-2.gif b/Semester_2/Einheit_04/Pics/Cross-Over-2.gif new file mode 100644 index 0000000000000000000000000000000000000000..3847efffa8df41769b5fe1ca1cbeaf50cd5fe6e9 Binary files /dev/null and b/Semester_2/Einheit_04/Pics/Cross-Over-2.gif differ diff --git a/Semester_2/Einheit_04/Pics/Cross-Over-3.gif b/Semester_2/Einheit_04/Pics/Cross-Over-3.gif new file mode 100644 index 0000000000000000000000000000000000000000..d18243e96bdad22b869709d856757aaff4fb5e23 Binary files /dev/null and b/Semester_2/Einheit_04/Pics/Cross-Over-3.gif differ diff --git a/Semester_2/Einheit_04/Pics/Deap_Architecture.png b/Semester_2/Einheit_04/Pics/Deap_Architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..161ea3ad442bd2fd7c59f1f41b0a5d214fe677e5 Binary files /dev/null and b/Semester_2/Einheit_04/Pics/Deap_Architecture.png differ diff --git a/Semester_2/Einheit_04/Pics/Generationmodel.png b/Semester_2/Einheit_04/Pics/Generationmodel.png new file mode 100644 index 0000000000000000000000000000000000000000..422ac2f86c19400062f6f870dc8523214ebbdcdc Binary files /dev/null and b/Semester_2/Einheit_04/Pics/Generationmodel.png differ diff --git a/Semester_2/Einheit_04/Pics/Mutation-1.gif b/Semester_2/Einheit_04/Pics/Mutation-1.gif new file mode 100644 index 0000000000000000000000000000000000000000..5674408068e619515a18c1450e278ca0def2b5c3 Binary files /dev/null and b/Semester_2/Einheit_04/Pics/Mutation-1.gif differ diff --git a/Semester_2/Einheit_04/Pics/Mutation-2.gif b/Semester_2/Einheit_04/Pics/Mutation-2.gif new file mode 100644 index 0000000000000000000000000000000000000000..9a791d56f80e8c1d4d7eefd6c843a34f445e69bc Binary files /dev/null and b/Semester_2/Einheit_04/Pics/Mutation-2.gif differ diff --git a/Semester_2/Einheit_04/Pics/Mutation-3.gif b/Semester_2/Einheit_04/Pics/Mutation-3.gif new file mode 100644 index 0000000000000000000000000000000000000000..841d84cfad431bff9f48d0e12ea71834f413745c Binary files /dev/null and b/Semester_2/Einheit_04/Pics/Mutation-3.gif differ diff --git a/Semester_2/Einheit_04/Pics/Roulette.png b/Semester_2/Einheit_04/Pics/Roulette.png new file mode 100644 index 0000000000000000000000000000000000000000..f2af12d3af03f6c7b1c4c9fcaba189d08ab36f3c Binary files /dev/null and b/Semester_2/Einheit_04/Pics/Roulette.png differ