{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Maschinelles Lernen (ML) - Übung 3\n", "# Perzeptronen und mehrschichtige Perzeptronen\n", "## 3.1 Das einfache Perzeptron als linearer Klassifikator\n", "\n", "In dieser Übung wird ein einfaches Perzeptron (engl. perceptron) programmiert, trainiert und als linearer Klassifikator eingesetzt.\n", "\n", "![perceptron.pdf](Handout/fig/perceptron/perceptron.png)\n", "\n", "Weitere Informationen zur Wirkweise des einfachen Perzeptrons sind im PDF-Handout für diese Übung zu finden." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Aufgabe 1:** In dieser Übung sollen Sie ein Perzeptron objektorientiert erstellen. Dazu ist bereits eine vorstrukturierte Klasse *Perceptron* mit den Methoden *_init_* zur Initialisierung des Perzeptrons (z.B. Gewichte, Lernrate), *predict* zur Klassifizierung eines Datenpunktes und *fit* zum Trainieren des Perzeptrons vorbereitet.\t\tVervollständigen Sie diese Methoden." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# matplotlib: Modul zum Plotten von Daten\n", "from matplotlib import pyplot as plt \n", "# numpy: Mathematikbibliothek\n", "import numpy as np \n", "import pandas as pd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Hilfen für die Klasse Perzeptron\n", "\n", "##### Allgemein\n", "Verwendete Variblenbezeichnungen:\n", "- inputs $\\hat{=} \\textrm{ } \\lbrace x_1, \\ldots x_n \\rbrace$; Sollte als Numpy Array definiert werden.\n", "- eta $\\hat{=} \\textrm{ } \\eta$\n", "\n", "##### Methode _init_(self, number_of_inputs, epochs, learning_rate):\n", "In Python entspricht \\_\\_init\\_\\_ dem Konstruktor. Die \\_\\_init\\_\\_() Methode initialisiert die Klasse Perceptron. Definiere und initialisiere hier die folgenden Variablen: \n", "- die zu lernenden Gewichte $\\textbf{w}$,\n", "- die maximale Anzahl der Epochen (Lernzyklen), die der Lernalgorithmus durchlaufen darf und\n", "- die Lernrate, die den Grad der Veränderung der Gewichte bei jedem Schritt durch die Trainingsdaten bestimmt.\n", "\n", "##### Methode predict(self, inputs):\n", "Die predict(...) Methode enthält die Aktivierungsfunktion h(z) (hier: die Signum-Funktion):\n", "\n", "\\begin{equation}\n", " h(z) =\n", " \\begin{cases}\n", "\t\t\t-1 \\textrm{ falls } z < 0 \\textrm{,} \\\\\t\n", "\t\t\t0 \\textrm{ falls } z = 0 \\textrm{,} \\\\\t\n", "\t\t\t1 \\textrm{ falls } z > 0 \\textrm{.} \\\\\n", "\t\t\\end{cases}\n", "\\end{equation} \n", "Die Eingabe der Methode (inputs) sollte als NumPy Array/Vektor definiert werden.\n", "\n", "##### Methode fit(self, training_inputs, labels):\n", "Die Methode fit(...) benötigt zwei Argumente:\n", "- training_inputs ist eine Liste von numpy-Vektoren und\n", "- labels ist ein Array von erwarteten Ausgabewerten.\n", " \n", "Beim Trainieren des Perzeptrons soll folgende Funktionalität implementiert werden: \n", "Ein einzelner Trainingsdatenpunkt wird betrachtet und eine Vorhersage (Methode predict) getroffen. Auf Basis der Vorhersage $\\hat{y}$ werden die Gewichte nach folgender Regel aktualisiert (siehe auch Handout):\n", "\\begin{equation}\n", "\\textbf{w} \\leftarrow \\textbf{w} + \\eta \\cdot \\left( y - \\hat{y} \\right) \\cdot \\textbf{x} \\textrm{.} \n", "\\end{equation}\n", "\n", "Der Block aus predict und update der Gewichte wird solange iterativ ausgeführt bis die maximale Anzahl der Epochen erreicht ist (oder optional bis die Fehlerfunktion konvergiert ist). \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "class Perceptron(object):\n", " def __init__(self, number_of_inputs, epochs, eta):\n", " \"\"\"\n", " Beispielaufruf des Konstruktors:\n", " >>> Perceptron(2, 100, 0.1)\n", " \"\"\"\n", " ### Dein Code kommt hierhin:\n", " \n", " ##########################\n", " pass\n", " \n", " def predict(self, inputs):\n", " \"\"\"\n", " Beispiel des Funktionsaufrufes:\n", " >>> inputs = np.array([0, 1])\n", " >>> h = perceptron.predict(inputs) \n", " \"\"\"\n", " \n", " # Dein Code kommt hierhin: \n", " \n", " ##########################\n", " \n", " pass\n", "\n", " def fit(self, training_inputs, labels):\n", " \"\"\"\n", " Beispiel des Funktionsaufrufs:\n", " >>> perceptron.fit(train_input, labels)\n", " \"\"\"\n", " \n", " # Dein Code kommt hierhin:\n", "\n", " ##########################\n", " pass\n", " \n", " def status(self):\n", " \"\"\"\n", " Die Methode status(...) gibt die aktuellen Gewichte aus.\n", "\n", " Beispiel des Funktionsaufrufes und der Ausgabe:\n", " >>> perceptron.status()\n", " Perceptron weights: [0. 1. 1.]\n", " \"\"\"\n", " print(\"Perceptron weights: \", self.weights)\n", " \n", " def getWeights(self):\n", " return self.weights" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Aufgabe 2:** Wenden Sie das implementierte Perzeptron auf das `AND`, `OR` und `XOR` Problem im zweidimensionalen Raum an. Berechnen Sie anhand der gelernten Gewichte $\\mathbf{w}$ des Perzeptrons die Geradengleichung der Diskriminanzgeraden und plotten Sie diese zusammen mit den Datenpunkten des jeweiligen Problems." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Beispiel mit AND\n", "train_input_AND = np.array([\n", " [0, 0],\n", " [0, 1],\n", " [1, 0],\n", " [1, 1]\n", " ])\n", "\n", "labels_AND = np.array([-1, -1, -1, 1])\n", "\n", "# Dein Code kommt hier hin:\n", "# Perceptron anlegen und trainieren\n", "\n", "\n", "# Geradengleichung berechnen und plotten\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Beispiel mit OR\n", "train_input_OR = np.array([\n", " [0, 0],\n", " [0, 1],\n", " [1, 0],\n", " [1, 1]\n", " ])\n", "\n", "labels_OR = np.array([-1, 1, 1, 1])\n", "\n", "# Dein Code kommt hier hin:\n", "# Perceptron anlegen und trainieren\n", "\n", "\n", "# Geradengleichung berechnen und plotten\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Beispiel mit XOR\n", "train_input_XOR = np.array([\n", " [0, 0],\n", " [0, 1],\n", " [1, 0],\n", " [1, 1]\n", " ])\n", "\n", "labels_XOR = np.array([-1, 1, 1, -1])\n", "\n", "# Dein Code kommt hier hin:\n", "# Perceptron anlegen und trainieren\n", "\n", "\n", "# Geradengleichung berechnen und plotten" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Aufgabe 3:** Warum wird im Perzeptron der Bias $x_0$ benötigt beziehungsweise wieso werden bei $n$ Merkmalen ($x_1, \\ldots, x_n$), $n+1$ Gewichte ($w_0, \\ldots, w_n$) benötigt? " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Antwort:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Aufgabe 4:** Wenden Sie das Perzeptron auf das Problem der Banknotenklassifizierung der letzten Übung an. Wählen und berechnen Sie dafür wieder zwei geeignete Merkmale der Trainingsbanknoten (Momentenberechnung auf den Farbkanälen mit `banknotes[i].compute_feature(moment, color)`). Mit welcher Genauigkeit (engl. *accuracy*) werden die Testbanknoten klassifiziert? Wie sind die erreichten Ergebnisse des Perzeptrons im Vergleich zum linearen Klassifikator der letzten Übung zu bewerten?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Hier wird der Iris-Datensatz geladen und vorbereitet (siehe letzte Übung)\n", "\n", "# Datensatz laden\n", "names = [\"sepal-length\", \"sepal-width\", \"petal-length\", \"petal-width\", \"class\"]\n", "iris_data = pd.read_csv(\"iris.csv\", names = names)\n", "\n", "# Klassen auswählen (Bei Bedarf ändern)\n", "iris_data = iris_data.loc[lambda x: x['class'] != 'Iris-setosa']\n", "\n", "# Merkmale auswählen (Bei Bedarf ändern)\n", "iris_features = ['petal-length', 'petal-width']\n", "X = iris_data[iris_features]\n", "\n", "# Pandas-Datenformat in reine Liste umwandeln\n", "X = X.values\n", "\n", "# Label vorbereiten\n", "from sklearn.preprocessing import LabelEncoder\n", "lb_make = LabelEncoder()\n", "iris_data[\"class_code\"] = lb_make.fit_transform(iris_data[\"class\"])\n", "y = iris_data.class_code\n", "# Pandas-Datenformat in reine Liste umwandeln\n", "y = y.values\n", "# Die Signum-Funktion unseres Perzeptrons benötigt die Label -1, 1\n", "y[y==0] = -1\n", " \n", "# Trainings- und Testdatensplit\n", "from sklearn.model_selection import train_test_split\n", "X_train, X_test, y_train, y_test = (\n", " train_test_split(X, y, test_size=.2, random_state=np.random.seed(42)))\n", "\n", "# Scatterplot der ausgewählten Merkmale und Klassen\n", "fig, ax = plt.subplots()\n", "ax.scatter(X[(y==-1),0] , X[(y==-1),1])\n", "ax.scatter(X[(y==1),0] , X[(y==1),1])\n", "ax.set(xlabel = iris_features[0], ylabel = iris_features[1])\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Perzeptron auf Iris-Datensatz trainieren und anwenden\n", "# Dein Code kommt hierhin:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Aufgabe 5:** Welchen Einfluss haben die Hyperparameter *Epoche* und *Lernrate* auf die Klassifizierung der Banknoten? Lassen sich die vorherigen Ergebnisse noch verbessern?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Antwort:" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.7.4" } }, "nbformat": 4, "nbformat_minor": 2 }