{ "cells": [ { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "# Data Science 2 (Regression)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import pandas as pd\n", "\n", "from sklearn.linear_model import LinearRegression\n", "from sklearn.metrics import mean_squared_error\n", "from sklearn.model_selection import cross_validate, train_test_split\n", "from sklearn.pipeline import Pipeline\n", "from sklearn.preprocessing import PolynomialFeatures\n", "\n", "# Matplotlib parameters\n", "plt.rcParams[\"figure.dpi\"] = 150\n", "plt.rcParams[\"figure.figsize\"] = [4, 3]" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Aufgabe 1: Einstieg\n", "\n", "Wir werden in diesem Aufgabenblatt im Kontext von Regressionsanalysen Overfitting und Underfitting untersuchen.\n", "Dafür arbeiten wir zunächst mit einem kleinen Beispiel-Datensatz, welcher zwei Attribute `x` und `y` beinhaltet:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "# Datensatz einlesen\n", "df = pd.read_csv(\"../resources/10_data_science/simple_regression.csv\")\n", "\n", "# Datensatz visualisieren\n", "ax = df.plot(x=\"x\", y=\"y\", style=\"o\")\n", "ax.set_xlabel(\"x\")\n", "ax.set_ylabel(\"y\")\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "#### Fragen: \n", "1. Wenn wir Attribut `x` verwenden wollen, um Vorhersagen zu Attribut `y` zu treffen, handelt es sich um Regression oder Klassifikation? Begründen Sie Ihre Antwort.\n", "2. Wie sähe Ihrer Meinung nach ein gutes Modell für diese Daten aus?" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Aufgabe 2: Lineare Regression\n", "Wir bringen nun den Datensatz in eine Form, die von der scikit-learn Bibliothek verstanden wird. Dazu teilen wir die Attribute in zwei Numpy Arrays auf:\n", "1. Array `X` enhält alle unabhängigen Variablen (bei uns also nur `x`, sonst aber oft (deutlich) mehr als ein Attribut) \n", "2. Array `y` enhält die abhängige Variable (bei uns `y`)\n", "\n", "Die Arrays haben die folgenden Dimensionen:\n", "- `X` hat Dimension Dimension $N \\times M$, wobei $N$ die Zahl der Datenpunkte ist und $M$ die Zahl der unabhängigen Attribute. Wir haben 41 Datenpunkte und nur ein unabhängiges Attribut, also $N=41$ und $M=1$. \n", "- `y` hat Dimension $N \\times 1$. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "# Vorbereiten der Arrays\n", "X = df[[\"x\"]].values.reshape(-1, 1)\n", "y = df[[\"y\"]].values.reshape(-1, 1)\n", "\n", "# Dimensionen des DataFrames und der Arrays anzeigen\n", "df.shape, X.shape, y.shape" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "Wir geben Ihnen nun eine Funktion vor, die für Sie im Folgenden hilfreich sein kann. Sie visualisiert die Vorhersagen eines Regressions-Modells: " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "def visualize_regression(model, X, y_true, figsize=(5, 4)):\n", " \"\"\"Visualisiert die Vorhersagen eines gegebenen Regressions-Modells auf einem gegebenen Datensatz.\"\"\"\n", " # Fortlaufende x-Werte generieren (um eine Linie zu zeichnen)\n", " fit_X = np.reshape(np.arange(np.min(X), np.max(X), 0.1), (-1, 1))\n", " # Modell-Vorhersagen für diese x-Werte generieren\n", " fit_y = model.predict(fit_X)\n", "\n", " # Datensatz plotten (als Punkte)\n", " plt.figure(figsize=figsize)\n", " plt.scatter(X, y_true)\n", "\n", " # Modell-Vorhersagen plotten (als rote Linie)\n", " plt.plot(fit_X, fit_y, color=\"red\")\n", " plt.show()" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "Wir erstellen nun mit linearer Regression ein Modell für unseren Datensatz. Dazu verwenden wir die Implementierung von Linearer Regression, die scikit-learn bereitstellt: \n", "https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "lr = LinearRegression()\n", "model_1 = lr.fit(X, y)" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "### Aufgabe 2.1 Visualisierung des Modells\n", "\n", "Visualisieren Sie das Modell. Nutzen Sie dazu die `visualize_regression` Funktion.\n", "- Beschreiben Sie, was Ihnen auffällt.\n", "- Halten Sie das Modell für geeignet, um über `y` Vorhersagen zu treffen? Warum bzw. warum nicht?\n", "- Haben Sie Ideen, wie man das Modell verbessern könnte?\n", "\n", "Notiz: Im Code von `visualize_regression` können Sie sehen, wie `model.predict()` genutzt wird, um Modell-Vorhersagen für Daten zu treffen. Dies können Sie auch mit eigenen Daten ausprobieren. Es ist am Anfang nur etwas irritierend, die Daten in das richtige Array-Format zu bringen.\n", "\n", "Hier ein Beispiel: wenn Sie eine Vorhersage für $x=100$ machen wollen, geht das z.B. so: `model_1.predict(np.array([[100]]))`." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "# your code goes here" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "#### Fragen\n", "1. Was fällt Ihnen auf? Halten Sie das Modell für geeignet, um Vorhersagen für `y` zu treffen?\n", "2. Haben Sie Ideen, wie man das Modell verbessern könnte?" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Aufgabe 3: polynomielle (lineare) Regression\n", "\n", "Wir werden das Modell nun verbessern, indem wir polynomielle Regression verwenden. Dazu ist es üblich, das Datenset mit polynomiellen Attributen zu erweitern und dann lineare Regression auf dem erweiterten Datenset durchzuführen.\n", "\n", "Scikit-learn stellt diese Funktion über [PolynomialFeatures](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.PolynomialFeatures.html) bereit. \n", "\n", "Zum Verständnis untersuchen wir uns zunächst, wie diese polynomiellen Attribute aussehen. Dazu betrachen wir, wie `PolynomialFeatures` mit dem Parameter `degree=2` die ersten 3 Datenpunkte in X verändert. Der Parameter `2` bestimmt, dass alle Polynome bis Grad 2 hinzugefügt werden, also $x^0$, $x^1$ (was schon existiert), und $x^2$. Laut [Dokumentation](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.PolynomialFeatures.html) erstellt `PolynomialFeatures(degree=2)` aus einem Datenset mit einem Attribut $x$ die Attribute $1$, $x$, $x^2$ ($1$ ist ein konstanter \"Intercept\"):" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "# Die ersten drei Datenpunkte in X\n", "X[:3]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "# Die ersten drei Datenpunkte in X mit polynomiellen Attributen\n", "# Die Spalten sind: x^0, x^1, x^2\n", "PolynomialFeatures(degree=2).fit_transform(X)[:3]" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "### Aufgabe 3.1 Good Fit\n", "Finden Sie ein gutes Modell, probieren Sie dazu verschiedene Werte für `degree` und visualisieren Sie die entstehenden Modelle.\n", "- Welchen Degree halten Sie am besten für diesen Datensatz geeignet?\n", "\n", "Wir verwenden im Folgenden eine `Pipeline`, um das Hinzufügen von Attributen und die Lineare Regression zu kombinieren. Den Code dazu haben wir Ihnen vorgegeben. \n", "Sorgen Sie dafür, dass in `pipeline_good_fit` eine Pipeline mit einem guten Wert für `degree` gespeichert ist. Die Pipeline werden wir später weiter verwenden." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "# Pipeline vorbereiten\n", "pipeline_good_fit = Pipeline(\n", " [\n", " (\"poly\", PolynomialFeatures(degree=1)), # TODO: Degree verändern\n", " (\"linear\", LinearRegression()),\n", " ]\n", ")\n", "\n", "# Ein Modell für unser Datenset lernen\n", "model_good_fit = pipeline_good_fit.fit(X, y)\n", "\n", "# TODO: visualisieren" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "### Aufgabe 3.2 Overfitting\n", "Overfitten Sie das Modell. Probieren Sie auch hierzu verschiedene Werte für `degree` aus. \n", "\n", "Sorgen Sie dafür, dass die Pipeline in `pipeline_overfit` overfitted. Auch diese Werden wir später noch verwenden." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "# Pipeline vorbereiten\n", "pipeline_overfit = Pipeline(\n", " [\n", " (\"poly\", PolynomialFeatures(degree=1)), # TODO: Degree verändern\n", " (\"linear\", LinearRegression()),\n", " ]\n", ")\n", "\n", "# Ein Modell für unser Datenset lernen\n", "model_overfit = pipeline_overfit.fit(X, y)\n", "\n", "# TODO: visualisieren" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Aufgabe 4: Modell-Evaluierung\n", "Subjektive, visuelle Evaluierungen sind eine Möglichkeit, Modelle zu evaluieren. Objektive Metriken geben aber oft eine besseres Bild. Im Folgenden werden wir dafür die Metrik [*mean squared error*](https://de.wikipedia.org/wiki/Mittlere_quadratische_Abweichung) (MSE, deutsch: mittlere quadratische Abweichung) verwenden. Diese wird sehr häufig für Regressionsanalysen verwendet. Die Formel für den MSE ist:\n", "\n", "$$MSE(\\mathbf{y}, \\mathbf{\\hat y}) = \\frac{1}{N} \\sum_{i=1}^{N} (y_i - \\hat y_i)^2$$\n", "\n", "Zur Notation:\n", "- $\\mathbf{y}$ steht für den Vektor der korrekten Zielwerte.\n", "- $\\mathbf{\\hat y}$ steht für den Vektor der von einem Modell vorhergesagten Werte. \n", "- In unserem Code haben wir für $\\mathbf{y}$ bisher `y` geschrieben und `y_pred` für $\\mathbf{\\hat y}$ (sonst ist oft auch `y_hat` üblich). \n", "- $y_i$ steht für den \"i-ten\" Eintrag in Vektor $\\mathbf{y}$. \n", "- $N$ ist die Zahl der Datenpunkte (also die Länge der Vektoren $\\mathbf{y}$ und $\\mathbf{\\hat y}$). " ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "### Aufgabe 4.1 MSE\n", "Berechnen Sie zunächst von Hand den MSE für ein einfaches Beispiel:\n", "\n", "$$\\mathbf{y} = (2, 1, -3)$$\n", "$$\\mathbf{\\hat y} = (1, 1, 2)$$" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "### Aufgabe 4.2 Evaluation\n", "Evaluieren Sie die Modelle, die Sie oben erstellt haben. Berechnen Sie hierzu den MSE für die Modelle auf unserem Datensatz und vergleichen Sie die Werte. \n", "- Welches Modell funktioniert laut dieser Metrik am besten?\n", "- Sehen Sie Probleme mit dieser Evaluierungsstrategie? Wenn ja, welche?\n", "\n", "Nutzen Sie für die Berechnung des MSE die scikit-learn Funktion [`mean_squared_error`](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.mean_squared_error.html). \n", "\n", "Erinnerung: Sie können Modell-Vorhersagen mit `model.predict(X)` erzeugen." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "# your code goes here" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Aufgabe 5: Test/Train-Split\n", "Wir werden nun den Datensatz in zwei Teile, \"train\" und \"test\", aufteilen, um zu vermeiden, dass auf demselben Datensatz trainiert und evaluiert wird. Wir erhalten zwei Datensätze:\n", "- Für Modell-Training: `X_train` + `y_train`\n", "- Für die Evaluation: `X_test` + `y_test`\n", "\n", "[Mehr Informationen zu Training/Validation/Test split](https://en.wikipedia.org/wiki/Training,_validation,_and_test_sets)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=25)" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "### Aufgabe 5.1\n", "Trainieren Sie die verschiedenen Modelle auf dem *Train-Split* und evaluieren Sie die trainierten Modelle anschließend auf dem *Test-Split*. \n", "- Welches Modell ist mit dieser Methodik das \"beste\"?\n", "\n", "Hinweis: Sie können mit `some_pipeline.fit(X_todo, y_todo)` neue Modelle trainieren und nach wie vor über `model.predict(X_todo)` Vorhersagen machen." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "# your code goes here" ] }, { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "## Exkurs: Kreuzvalidierung*\n", "Zur Evaluierung wird oft Kreuzvalidierung verwendet. In scikit-learn geht dies einfach über `model_selection.cross_validate`. \n", "\n", "**Aufgabe:** Evaluieren Sie die obigen Modell mit Kreuzvalidierung. Wählen Sie hierzu frei die Zahl der Folds. \n", "- Welches Modell ist mit Kreuzvalidierung das \"beste\"?" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "# your code goes here" ] } ], "metadata": {}, "nbformat": 4, "nbformat_minor": 4 }