329 lines
11 KiB
Plaintext
329 lines
11 KiB
Plaintext
{
|
|
"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",
|
|
"\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
|
|
}
|