ML Atlas

Machine Learning von Grund auf · Lektion 3.2 · 15 von 33 fertigen Lektionen · 10 Min.

Validierung und Kreuzvalidierung

Kurz gesagt

Wer Einstellungen mit den Testdaten wählt, verdirbt sie. Kreuzvalidierung testet ein Modell nacheinander auf jedem Teil der Daten und zeigt, wie stabil es ist.

Aufwärmen Zwei Fragen aus früheren Lektionen. Eine Minute, und sie blockiert die Lektion nicht.
Aus Lektion 3.1: Überanpassung: Wenn ein Modell auswendig lernt Warum machen mehr Trainingsdaten ein flexibles Modell meist ungefährlicher?

Antwort anzeigen

Richtige Antwort: Mit mehr Punkten mittelt sich das Rauschen heraus, und die Kurve jagt keinen Einzelpunkten nach

Das Rauschen in einzelnen Punkten bleibt, aber bei vielen Punkten hebt es sich auf, sodass die Anpassung dem echten Trend folgt.

Aus Lektion 2.6: Metrik und Schwelle nach den Kosten der Fehler Ein Übersehen kostet 10, ein Fehlalarm 1, und das Modell ist kalibriert. Welche Schwelle folgt aus der Entscheidungstheorie?

Antwort anzeigen

Richtige Antwort: 1 / (1 + 10) ≈ 0,09, denn ein Alarm lohnt sich schon bei kleinem Risiko

Ein Alarm lohnt sich, wenn p · 10 > (1 − p) · 1, also p > 1/11. Im Lab kommt die Schwelle 0,09 auf Kosten von 125. Die günstigste Schwelle 0,06 kostet 111 nur auf den Scores, auf denen sie gewählt wurde.

In der letzten Lektion haben wir den besten Grad des Polynoms gewählt, indem wir in die Testdaten gespäht haben. Das wirkte harmlos, aber überleg, was es bedeutet: Wir haben mehrere Modelle ausprobiert und das behalten, das den Testdaten am besten gefiel. Die Testdaten sind keine ungesehene Prüfung mehr – sie sind zu einer Jury geworden, die wir bei der Vorbereitung befragt haben. Wie wählen wir Einstellungen, ohne unsere einzige ehrliche Messung zu verderben? Probier es zuerst selbst aus, bevor du die Erklärung liest.

Erst vorhersagen, dann prüfen Du erhöhst die Zahl der Folds k von 2 auf 10 (insgesamt 178 Weine). Was passiert mit dem Abstand zwischen dem schlechtesten und dem besten Fold?
Wie sicher bist du?

Interaktives Labor

Dieses interaktive Labor läuft im Browser mit aktiviertem JavaScript. Der Text drumherum beschreibt, was es zeigt.

Antwort anzeigen

Richtige Antwort: Er wächst, weil jeder Test-Fold weniger Weine enthält

Bei k = 2 hat der schlechteste Fold 94,4 % und der beste 97,8 % (Abstand 3,4 Punkte). Bei k = 10 reicht die Spanne von 83,3 % bis 100,0 % (16,7 Punkte), denn ein Test-Fold umfasst nur 17 oder 18 Weine und ein Fehler wiegt mehr als 5 Punkte. Der Mittelwert ändert sich kaum (96,1 % und 96,7 %).

Probier es aus

  • Stell den k-Regler auf den kleinsten Wert und lies die Genauigkeit jedes Folds ab. Wie verschieden sind die Folds voneinander?
  • Erhöhe den k-Regler Schritt für Schritt. Beobachte Mittelwert und Streuung und achte darauf, dass jeder Fold jetzt weniger Weine enthält.
  • Such bei einem großen k nach Folds mit ungewöhnlich niedriger oder hoher Genauigkeit. Wenn ein Fold nur eine Handvoll Weine enthält, verschiebt ein einziger Fehler seinen Wert stark.
  • Vergleiche die Streuung mit den Unterschieden zwischen Einstellungen, die du vielleicht abstimmen möchtest. Ist ein kleiner Gewinn im Mittelwert größer als die Streuung?

Die Idee in einfachen Worten

Die erste Lösung ist ein dritter Datenstapel. Teile die Zeilen in Trainings-, Validierungs- und Testdaten auf. Trainiere Kandidatenmodelle auf den Trainingsdaten, vergleiche sie auf den Validierungsdaten, wähle den Sieger und öffne erst dann, ein einziges Mal, die Testdaten, um den endgültigen Wert zu berichten. Die Validierungsdaten fangen das ganze Spähen ab, damit die Testdaten es nicht müssen.

Das Problem: Jede Aufteilung kostet Daten. Bei einem kleinen Datensatz bleiben bei Validierungsdaten, die groß genug für einen verlässlichen Wert sind, zu wenige Zeilen fürs Training übrig, und Lektion 2 hat gezeigt, dass kleine Datenmengen einen sprunghaften Wert liefern. Die Kreuzvalidierung löst das, indem sie die Daten geschickt wiederverwendet.

Stell dir vor, du beurteilst eine neue Köchin. Ein einziges Gericht verrät wenig: Vielleicht war es ihr bestes, vielleicht ihr schlechtestes. Also lässt du dir an verschiedenen Tagen mehrere Gerichte kochen und schaust auf den Durchschnitt und darauf, wie stark sie schwanken. Die Kreuzvalidierung macht dasselbe mit einem Modell.

Warum es funktioniert

Bei der k-fachen Kreuzvalidierung werden die Daten (ohne die endgültigen Testdaten) in k gleich große Teile zerlegt, die Folds heißen. Das Modell wird k-mal trainiert. Jedes Mal wird ein Fold zur Validierung zurückgehalten, und die übrigen k − 1 Folds dienen dem Training. Am Ende wurde jede Zeile genau einmal zur Validierung und k − 1-mal zum Training benutzt. Jeder Trainingslauf sieht diesen Anteil der Daten:

Trainingsanteil = (k − 1) / k

Das Ergebnis ist nicht ein Wert, sondern k Werte, einer pro Fold. Ihr Mittelwert ist eine verlässlichere Schätzung als jede einzelne Aufteilung, weil er nicht von einer glücklichen oder unglücklichen Aufteilung der Zeilen abhängt. Ihre Streuung ist genauso wertvoll: Sie zeigt, wie stark der Wert davon abhängt, welche Zeilen das Modell zufällig bekommen hat. Zwei Modelle, deren Mittelwerte sich um weniger als diese Streuung unterscheiden, lassen sich nicht wirklich auseinanderhalten.

Die Wahl von k ist ein Kompromiss. Ein kleines k (etwa 2) trainiert jedes Modell nur auf der Hälfte der Daten, also ist jedes schwächer als das, das du am Ende baust, und die Schätzung fällt pessimistisch aus. Ein großes k trainiert auf fast allen Daten, aber jeder Fold ist klein, sodass die einzelnen Fold-Werte verrauschter werden, und die Rechnung dauert k-mal so lange wie eine einzelne Anpassung. Werte von 5 oder 10 sind ein gängiger Kompromiss.

Das Beispiel in dieser Lektion ist der Wine-Datensatz: 178 Weine aus derselben Region Italiens, gekeltert aus drei verschiedenen Rebsorten und jeweils beschrieben durch 13 Ergebnisse einer chemischen Analyse. Die Aufgabe ist, die drei Rebsorten auseinanderzuhalten.

Häufiger Fehler

Die Kreuzvalidierung schützt dich nur, wenn alles, was aus Daten gelernt wird, innerhalb jedes Folds geschieht. Wenn du Merkmale skalierst oder die „besten“ Merkmale zuerst auf allen Zeilen auswählst und danach kreuzvalidierst, sind Informationen aus den Validierungs-Folds bereits ins Training gesickert, und die Werte sind zu optimistisch. Der zweite Fehler ist, Dutzende Einstellungen per Kreuzvalidierung abzustimmen und den besten kreuzvalidierten Wert als Endergebnis zu berichten. Nach so vielen Vergleichen hat der Sieger teils einfach Glück gehabt; deshalb zählen separate Testdaten, die nur einmal benutzt werden, nach wie vor.

Mit ehrlicher Bewertung im Werkzeugkasten können wir mächtigere Modelle erkunden, ohne uns selbst zu täuschen. In der nächsten Lektion lernen wir das am leichtesten lesbare von ihnen kennen: den Entscheidungsbaum.

Quiz

Drei Fragen zur Selbstkontrolle. Wähle eine Antwort oder klappe die Lösung unter der Frage auf. Mit der Tastatur: Pfeiltasten wählen, Enter oder Leertaste bestätigt.

1. Wie oft wird bei 5-facher Kreuzvalidierung jede Zeile zur Validierung benutzt?
Antwort anzeigen

Richtige Antwort: Genau einmal, und zwar im passenden Fold

Jede Zeile gehört zu einem Fold, und jeder Fold dient in genau einem der fünf Läufe als Validierungsdaten.

2. Modell A hat eine etwas höhere mittlere kreuzvalidierte Genauigkeit als Modell B, aber die Streuung über die Folds ist viel größer als der Unterschied. Was folgt daraus?
Antwort anzeigen

Richtige Antwort: Die Daten zeigen keinen verlässlichen Unterschied zwischen den Modellen

Wenn der Unterschied der Mittelwerte kleiner ist als die Schwankung zwischen den Folds, kann er leicht Zufall sein.

3. Warum ist es ein Fehler, die Merkmale vor der Kreuzvalidierung auf allen Zeilen zu skalieren?
Antwort anzeigen

Richtige Antwort: Kennzahlen aus den Validierungs-Folds sickern ins Training, die Ergebnisse fallen zu optimistisch aus

Jeder Schritt, der aus Daten lernt, darf nur auf den Trainings-Folds angepasst werden, sonst sind die Validierungs-Folds nicht mehr ungesehen.

Das Wichtigste

  • Wer Einstellungen mit den Testdaten wählt, verdirbt sie; nimm stattdessen Validierungsdaten oder Kreuzvalidierung.
  • Bei der k-fachen Kreuzvalidierung wird jede Zeile genau einmal zur Validierung benutzt.
  • Der Mittelwert der Fold-Werte schätzt die Leistung; die Streuung zeigt, wie stark sie von der Aufteilung abhängt.
  • Jede datengetriebene Vorbereitung muss innerhalb jedes Folds geschehen, um Datenlecks zu vermeiden.

Tiefer: Formel, Code, Praxis

Die Mathematik

Bei der k-fachen Kreuzvalidierung werden die n Zeilen in k Folds zerlegt. Jede Anpassung trainiert auf n · (k − 1) / k Zeilen und testet auf den restlichen n / k. Der Wert ist der Durchschnitt über die Folds: CV = (1/k) · Σ score_f. Die Streuung ist ihre Standardabweichung s = √( Σ (score_f − CV)² / (k − 1) ).

Ein Fold mit m Testzeilen liefert eine Genauigkeit, die sich in Schritten von 1 / m bewegt. Ist die wahre Genauigkeit p, beträgt ihr Standardfehler etwa √( p · (1 − p) / m ). Er wächst, wenn m schrumpft, deshalb werden einzelne Folds mit steigendem k verrauschter, auch wenn der Mittelwert weniger pessimistisch wird. Bei 178 Weinen und k = 10 kostet ein Fehler in einem Fold mit 17 oder 18 Weinen 5,6 bis 5,9 Punkte.

Die Fold-Werte sind nicht ganz unabhängig: Die Trainingssets überlappen sich. Deshalb unterschätzt s / √k die wahre Unsicherheit des Mittelwerts.

Im Code (Python)

Die 178 Weine mit 5 nächsten Nachbarn und standardisierten Merkmalen. Um das Muster hinter der einzelnen Mischung des Labs zu sehen, wird die Lücke zwischen dem schlechtesten und dem besten Fold über 20 Mischungen gemittelt. Die Folds sind stratifiziert und mit einem Seed gemischt, die Zahlen sind also nicht die 3,4 und 16,7 Punkte des Labs:

import numpy as np
from sklearn.datasets import load_wine
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

X, y = load_wine(return_X_y=True)
model = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=5))  # der Scaler wird in jedem Fold neu angepasst
for k in (2, 10):
    runs = [cross_val_score(model, X, y, cv=StratifiedKFold(k, shuffle=True, random_state=s)) for s in range(20)]
    gap = np.mean([a.max() - a.min() for a in runs])
    print(f"k={k:2d}: mean accuracy {np.mean(runs):.3f}, average worst-to-best gap {gap:.3f}")

Ausgabe

k= 2: mean accuracy 0.956, average worst-to-best gap 0.020
k=10: mean accuracy 0.966, average worst-to-best gap 0.113

Der Mittelwert bewegt sich kaum (0,956 und 0,966), aber die durchschnittliche Lücke zwischen dem schlechtesten und dem besten Fold wächst von 0,020 auf 0,113, wenn k von 2 auf 10 steigt.

Für die Praxis
  • Passe jeden gelernten Schritt (Skalieren, Imputieren, Merkmalsauswahl) innerhalb jedes Folds an. In scikit-learn packst du sie in eine Pipeline und übergibst diese an cross_val_score.
  • Nutze bei Klassifikation stratifizierte Folds, damit jeder Fold alle Klassen enthält. cross_val_score macht das bei Klassifikatoren standardmäßig, wenn cv eine Zahl ist, aber dieser Standard mischt nicht.
  • Mische, bevor du die Folds schneidest, wenn die Zeilen geordnet sind, zum Beispiel nach Klasse oder Datum. load_wine ist nach Klasse sortiert, übergib also StratifiedKFold(k, shuffle=True, random_state=0), wie oben. Bei Zeitreihen nicht mischen: Nimm eine vorwärtsgerichtete Aufteilung.
  • Gehören Zeilen zu Gruppen (mehrere Zeilen pro Patient), halte jede Gruppe mit GroupKFold in einem Fold. Sonst steckt derselbe Patient im Training und in der Validierung.
  • Gib den Mittelwert und die Streuung an. Unterscheiden sich zwei Modelle um weniger als die Streuung, nenne keines von beiden besser.

Mehr im Atlas

Einträge der Enzyklopädie zu den Ideen dieser Lektion: