3.3 ML mit Python

Kapitel 3 · Maschinelles Lernen

Noah Kolb · Sommersemester 2026

‹ Alle ThemenVertiefung

Worum geht’s

Die beiden vorangegangenen Themen haben das maschinelle Lernen als Idee entwickelt: ein Modell aus Daten schätzen (3.1), Klassen trennen (3.2). Dieses Kapitel beantwortet die unromantische Anschlussfrage — womit rechnet man das eigentlich? Die Antwort der Praxis ist erstaunlich einheitlich: ein Stapel aus wenigen Python-Bibliotheken, der seit gut einem Jahrzehnt den De-facto-Standard bildet. NumPy liefert das schnelle n-dimensionale Array, pandas die tabellarische Sicht mit benannten Spalten, scikit-learn die Algorithmen hinter einer bemerkenswert konsequenten Schnittstelle — und Matplotlib mit Seaborn die Visualisierung. Genau diese vier nennt auch der Foliensatz als die Werkzeuge der Wahl, jeweils mit Verweis auf ein Cheat-Sheet bzw. die offizielle Dokumentation.

Mich reizt an diesem Thema weniger eine neue Theorie als eine Abstraktion: scikit-learn presst kNN, Entscheidungsbaum, SVM und logistische Regression — vier mathematisch grundverschiedene Verfahren — in genau drei Methoden, fit, predict, score. Diese Einheitlichkeit ist kein kosmetisches Detail, sondern das, was den fairen Vergleich von Modellen überhaupt erst billig macht: Man tauscht eine Zeile aus, der Rest der Auswertung bleibt stehen. Genau das mache ich unten — ein und derselbe Datensatz, vier Modelle, eine Schleife. Damit wird die „Mechanik” hinter 3.1 und 3.2 zu lauffähigem Code, und nebenbei trägt dieser Stack das gesamte Praxis-Gerüst dieses Portfolios: jede <Figure>, die ich zeige, entsteht in derselben uv2nix-Umgebung.

Kernkonzepte

Der Stack in Schichten

Die Bibliotheken bauen aufeinander auf, jede mit einer klaren Aufgabe — von der rohen Array-Ebene bis zur fertigen Abbildung.

Die Estimator-API: fit / predict / score

Das tragende Designprinzip von scikit-learn ist, dass jedes lernende Objekt ein Estimator ist und dieselben Methoden anbietet. Ein Modell lernt mit est.fit(X, y), sagt mit est.predict(X_neu) vorher, und bewertet sich selbst mit est.score(X, y). Vorverarbeitungsschritte (Skalierer, Encoder) sind Transformer und ergänzen transform(X). Diese Uniformität ist der Grund, warum man Modelle wie Bauteile austauschen kann.

Pipelines: Vorverarbeitung und Modell als eine Einheit

Wer den Scaler separat auf den gesamten Datensatz fittet und danach kreuzvalidiert, begeht ein subtiles Datenleck: Der Mittelwert μ\mu hat dann schon Information aus dem Validierungsfaltblatt gesehen. Die Pipeline verhindert das, indem sie Transformer und Estimator zu einem Estimator verklebt — fit fittet erst den Scaler, dann das Modell, und zwar bei jeder Kreuzvalidierungs-Falte neu auf deren Trainingsanteil.

Pipeline=StandardScaler    Klassifikator,fit/predict/score wie ein Modell.\texttt{Pipeline} = \texttt{StandardScaler} \;\to\; \texttt{Klassifikator}, \qquad \texttt{fit/predict/score wie ein Modell.}

Kategoriale Merkmale: Encoding

Reale Tabellen enthalten Text-Spalten („Stadt”, „Produktklasse”). Modelle rechnen aber mit Zahlen. One-Hot-Encoding ersetzt eine kategoriale Spalte mit kk Ausprägungen durch kk binäre Indikatorspalten — so wird keine künstliche Ordnung suggeriert, wie es ein naives Durchnummerieren täte. Im Stack erledigt das OneHotEncoder, meist eingebettet in einen ColumnTransformer, der numerische und kategoriale Spalten parallel behandelt.

Saubere Bewertung: train_test_split und cross_val_score

Ein Modell an denselben Daten zu messen, an denen es gelernt hat, belohnt Auswendiglernen. train_test_split trennt deshalb einen Hold-out-Teil ab. Robuster noch ist die k-fache Kreuzvalidierung: Die Daten werden in kk Blöcke geteilt, kk-mal auf k1k-1 Blöcken trainiert und auf dem ausgelassenen getestet. cross_val_score liefert die kk Genauigkeiten; ihr Mittel schätzt die Generalisierung, ihre Streuung deren Verlässlichkeit. Die stratifizierte Variante hält dabei die Klassenanteile in jeder Falte konstant.

Praxis

Es gibt zu diesem Foliensatz kein Übungsblatt. Stattdessen baue ich das kanonische scikit-learn-Beispiel nach — den Klassifikator-Vergleich — und nutze ihn, um die obigen Begriffe an einem konkreten Datensatz sichtbar zu machen. Vier Modelle, jedes in derselben Pipeline, bewertet per Kreuzvalidierung, und ihre Entscheidungsgrenzen nebeneinander gezeichnet.

Der Kern ist die Uniformität der API: Vier grundverschiedene Verfahren stehen in einem Dictionary, jedes in identischer StandardScaler-Pipeline.

def _make_models() -> dict[str, Pipeline]:
    return {
        "k-NN (k=5)":             Pipeline([("scale", StandardScaler()),
                                            ("clf", KNeighborsClassifier(n_neighbors=5))]),
        "Entscheidungsbaum":      Pipeline([("scale", StandardScaler()),
                                            ("clf", DecisionTreeClassifier(max_depth=4))]),
        "SVM (RBF)":              Pipeline([("scale", StandardScaler()),
                                            ("clf", SVC(kernel="rbf", C=1.0))]),
        "Logistische Regression": Pipeline([("scale", StandardScaler()),
                                            ("clf", LogisticRegression())]),
    }

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=SEED)
for name, model in _make_models().items():
    s = cross_val_score(model, X, y, cv=cv, scoring="accuracy")   # eine Zeile pro Modell
    print(f"{name}: {s.mean()*100:.1f} % ± {s.std()*100:.1f}")

Der Datensatz ist absichtlich nicht linear trennbar (make_classification mit zwei Clustern je Klasse) — sonst sähen alle vier Grenzen gleich aus und der Vergleich verschenkte seine Pointe. Das vollständige Skript liegt in python/src/eport_figures/praxis/p_3_3_ml_python.py. Seine Ausgabe:

Datensatz: make_classification, X.shape = (300, 2), Klassen = [152, 148]
X-Typ: ndarray  dtype=float64   y-Typ: ndarray

Kreuzvalidierung (5-fach, stratifiziert) — CV-Genauigkeit:
  k-NN (k=5)                88.3 % ±  3.0   Falten: [0.867, 0.933, 0.85, 0.9, 0.867]
  Entscheidungsbaum         88.7 % ±  4.4   Falten: [0.85, 0.967, 0.867, 0.9, 0.85]
  SVM (RBF)                 89.3 % ±  3.9   Falten: [0.867, 0.933, 0.833, 0.933, 0.9]
  Logistische Regression    87.0 % ±  4.1   Falten: [0.883, 0.933, 0.833, 0.883, 0.817]

  Bestes Modell auf diesem Datensatz: SVM (RBF) (89.3 %).

Hold-out-Genauigkeit (ein 75/25-Split, train_test_split):
  k-NN (k=5)                88.0 %
  Entscheidungsbaum         92.0 %
  SVM (RBF)                 88.0 %
  Logistische Regression    86.7 %
Raster aus vier Konturplots: kNN, Entscheidungsbaum, SVM-RBF und logistische Regression mit ihren Entscheidungsgrenzen auf demselben 2D-Datensatz

Vier Klassifikatoren, ein Datensatz. Man sieht den induktiven Bias jedes Verfahrens unmittelbar: die logistische Regression zieht eine einzige Gerade (linear), der Entscheidungsbaum schneidet rein achsenparallel (treppenförmig), kNN folgt lokal jeder Punktwolke (zerklüftet), die RBF-SVM legt eine glatte, geschwungene Grenze.

Balkendiagramm der mittleren Kreuzvalidierungs-Genauigkeit der vier Modelle mit Fehlerbalken

Mittlere CV-Genauigkeit mit Standardabweichung über die fünf Falten. Die Modelle liegen in einem Band von etwa 87–89 % — und die Fehlerbalken überlappen deutlich.

Die eigentliche Lehre steckt nicht im Sieger, sondern in dessen Knappheit: Auf diesem Datensatz trennen 2,3 Prozentpunkte das beste vom schlechtesten Modell, bei Standardabweichungen von 3–4 Punkten. Die Fehlerbalken überlappen vollständig — der Unterschied ist statistisch nicht belastbar. Genau deshalb darf man ein Modell nie an einer einzelnen Zahl festmachen; der Hold-out-Split bestätigt das, indem er eine andere Reihenfolge produziert (dort führt der Baum mit 92 %). Die Kreuzvalidierung über mehrere Falten ist die ehrlichere Schätzung, weil sie die Streuung mitliefert — eine Brücke direkt zu Thema 3.4.

Querbezüge

  • 3.1 / 3.2 (ML-Grundlagen, Klassifikation): Dieses Kapitel ist deren Implementierung. Die logistische Regression in der Pipeline ist der lineare Klassifikator aus 3.2; die RBF-SVM zeigt, was der Kern-Trick aus 3.2 in der Praxis bedeutet — eine glatte nichtlineare Grenze, ohne den Merkmalsraum je explizit aufzuspannen. Der Entscheidungsbaum verbindet zurück zu den Baumverfahren aus 3.1.
  • 3.4 (Evaluierung): Die Kreuzvalidierung hier ist nur die Spitze. Dass die vier Modelle innerhalb ihrer Fehlerbalken ununterscheidbar sind, ist genau das Thema von 3.4 — Genauigkeit ist nur eine Metrik, und ein einzelner Wert ohne Streuung führt in die Irre.
  • 7.1 (Neuronale Netze): Dasselbe Stack-Argument. Das dortige Perzeptron ist in reinem NumPy gebaut; ein MLPClassifier von scikit-learn wäre die fünfte Zeile in genau diesem Vergleich und folgte derselben fit/predict-API.
  • Lineare Algebra: Die Merkmalsmatrix XRn×dX \in \mathbb{R}^{n \times d}, das Standardisieren als affine Abbildung je Spalte, das Skalarprodukt im SVM-Kern — alles direkte Anwendung. NumPys @-Operator ist die Matrixmultiplikation der Vorlesung.
  • Stochastik: Stratifiziertes Sampling, der Mittelwert-Schätzer über Falten und seine Standardabweichung sind Schätztheorie. Die Frage „ist der Unterschied echt?” ist im Kern ein Hypothesentest.
  • Algorithmen & Datenstrukturen: Hinter den drei API-Methoden stecken sehr unterschiedliche Algorithmen — kNN braucht räumliche Suchstrukturen (k-d-Baum, Ball-Baum), der Entscheidungsbaum ist gierige rekursive Partition, die SVM löst ein quadratisches Optimierungsproblem. Die Estimator-API abstrahiert genau diese Vielfalt weg.

Quellen

  • Foliensatz _345_ML_Python.pdf — Überblick über den Python-ML-Stack: scikit-learn (mit den vier Feldern Classification, Clustering, Data Preparation, Evaluation), die NumPy- und pandas-Cheatsheets von DataCamp sowie Matplotlib und Seaborn zur Visualisierung. Der Satz ist bewusst link-lastig (Verweise auf den scikit-learn-User-Guide, matplotlib.org, seaborn.pydata.org); ich habe ihn als Landkarte genutzt (welche Bibliothek wofür) und die konkrete Estimator-Mechanik darüber hinaus an der Originaldokumentation vertieft, weil Folien hier naturgemäß nur Schlaglichter setzen.
  • scikit-learn User Guide & API-Referenz — die maßgebliche Quelle für die fit/predict/score-Konvention, Pipeline, StratifiedKFold und cross_val_score. Das berühmte „Classifier comparison”-Beispiel der Galerie war die direkte Vorlage für die Abbildung; ich habe es auf vier Modelle und den Pipeline-/CV-Aufbau zugeschnitten und ausprobiert.
  • NumPy- und pandas-Dokumentation — für ndarray-Semantik (dtype, Vektorisierung) und DataFrame-Grundlagen, soweit für die obigen Definitionen nötig.
  • Russell & Norvig, Artificial Intelligence: A Modern Approach, Kap. 19/20 — als konzeptioneller Hintergrund zu Modellklassen und Generalisierung; die Implementierungssicht ergänzt dort die eher mathematische Darstellung.