ML Atlas

Machine Learning von Grund auf · Lektion 4.5 · 24 von 33 fertigen Lektionen · 14 Min.

Datenleck

Kurz gesagt

Ein Datenleck ist Information aus dem Test oder der Zukunft, die ins Training gelangt. Auf reinem Rauschen täuscht es 82 % vor. Lerne 8 Typen und finde sie.

Aufwärmen Zwei Fragen aus früheren Lektionen. Eine Minute, und sie blockiert die Lektion nicht.
Aus Lektion 4.4: Feature Engineering Warum kann eine einzige Schwelle allein auf den Flavonoiden Rebsorte 2 nicht abtrennen?

Antwort anzeigen

Richtige Antwort: Weil Rebsorte 2 in der Mitte liegt und eine Schwelle kein mittleres Intervall herausschneidet

Im Mittel 2,08 gegen 2,98 und 0,78 bei den anderen. Jede Schwelle lässt auf der Seite „Rebsorte 2“ auch eine andere Rebsorte. Die beste Schwelle trifft im Training 61,6% gegen 60,0% für „immer andere“, und die lineare SVM sagt Rebsorte 2 nirgends vorher: 60,4%.

Aus Lektion 4.3: Kategorien und Skalierung Die Inseln sind als Biscoe = 0, Dream = 1, Torgersen = 2 kodiert. Was nimmt eine lineare Regression mit einem einzigen Gewicht für diese Spalte an?

Antwort anzeigen

Richtige Antwort: Dass Dream in gleichen Schritten genau zwischen Biscoe und Torgersen liegt

Ein Gewicht heißt gleiche Schritte: Das Modell sagt 625 g weniger von Biscoe zu Dream und weitere 625 g von Dream zu Torgersen vorher. In den Daten wiegen Dream und Torgersen fast gleich viel.

In Lektion 3.2 gab die Kreuzvalidierung eine ehrliche Schätzung des Modells. Ehrlich unter einer Bedingung: Nichts aus dem Testteil darf vorher das Training berühren. Wenn doch, sprechen wir von einem Datenleck. Das Modell sieht in der Validierung großartig aus und versagt im echten Einsatz. Kapoor und Narayanan sammelten Berichte anderer Forschender über Datenlecks in 294 wissenschaftlichen Arbeiten aus 17 Fachgebieten. Fangen wir mit dem saubersten Beispiel an: Daten, in denen es gar nichts zu lernen gibt.

Erst vorhersagen, dann prüfen Du hast 60 Proben und 1000 Spalten reines Rauschen, und die 0/1-Labels sind zufällig. Du wählst auf allen Proben die 10 Spalten, die am stärksten mit dem Label korrelieren, und bewertest das Modell dann mit 5-facher Kreuzvalidierung. Welchen Wert zeigt die Validierung?
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: Über 80 %, obwohl diese Daten gar kein Signal enthalten

Die Validierung zeigt 81,7 %. Wählst du die Spalten in jedem Fold nur auf den Trainingsdaten, fällt der Wert auf 43,3 %, ein Münzwurf. Über 20 neue Rauschdatensätze liegt das Mittel bei 86,6 % mit Leck und 49,8 % ehrlich.

Probier es aus

  • Schiebe bei der Merkmalswahl vor dem Aufteilen k von 5 auf 50. Wie hoch steigt der Wert auf reinem Rauschen?
  • Wechsle beim gleichen k zur Wahl innerhalb der Validierung. Wo landet der Balken, und wo die Punkte der 20 neuen Rauschdatensätze?
  • Finde im zweiten Teil des Labors in jedem der 8 Abläufe den undichten Schritt.

Wenn du es im Labor geprüft hast, benennen wir, was du siehst.

Die Idee in einfachen Worten

Ein Datenleck ist jede Information, die das Modell im Moment einer echten Vorhersage nicht hätte und die trotzdem in sein Training oder seine Bewertung gelangt ist. Die Validierung soll die Zukunft spielen: Daten, die das Modell nie gesehen hat. Das Leck verdirbt dieses Spiel, also steigt der Wert, obwohl das Modell kein bisschen besser ist.

Im Labor gibt es unter 1000 Rauschspalten immer einige, die zufällig zu den Labels passen. Die beste hat eine Korrelation von 0,48 mit dem Label, während für Rauschen etwa 0,13 typisch ist. Die Wahl auf allen 60 Proben passt die Spalten auch an die Proben an, die später den Test spielen. Die Kreuzvalidierung prüft das Modell also auf Daten, die es schon geformt haben, und zeigt 81,7 %. Die Wahl innerhalb eines Folds sieht nur 48 Trainingsproben. Spalten, die zufällig zu ihnen passen, sagen nichts über die 12 Testproben, also fällt der Wert auf 43,3 %, innerhalb eines Münzwurfs.

Kapoor und Narayanan ordneten Datenlecks in 8 Typen in drei Gruppen:

  • keine saubere Aufteilung: kein getrennter Testdatensatz, Vorverarbeitung an alle Daten angepasst, Merkmalsauswahl auf allen Daten, Duplikate auf beiden Seiten der Aufteilung;
  • ein Merkmal, das du bei der Vorhersage nicht kennst, etwa die Tage im Krankenhaus, wenn das Modell die Aufnahme vorhersagen soll;
  • ein Test aus einer anderen Verteilung als der Einsatz: eine zufällige Aufteilung von Zeitdaten, dieselben Personen in Training und Test, ein Test, der anders ausgewählt ist als die Daten, mit denen das Modell arbeiten wird.

Jeden davon findest du im zweiten Teil des Labors.

Jak to policzono
  • Rauschen: 60 Proben, 1000 Spalten aus einer Normalverteilung, 30 Labels 0 und 30 Labels 1 in zufälliger Reihenfolge. Der Browser erzeugt es mit dem Seed 1; die Punkte sind 20 weitere Datensätze mit anderen Seeds.
  • Modell: die k Spalten mit der größten Korrelation mit dem Label (im Betrag), ein Nächster-Schwerpunkt-Klassifikator, stratifizierte 5-fache Kreuzvalidierung (6 Proben jeder Klasse in jedem Fold).
  • Biopsien im „Häufigen Fehler“: Breast Cancer Wisconsin (Diagnostic), UCI, Lizenz CC BY 4.0, 569 Biopsien. Typen von Datenlecks: S. Kapoor, A. Narayanan, „Leakage and the reproducibility crisis in machine-learning-based science“, Patterns, 2023.

Warum es funktioniert

Eine Bewertung ist nur ehrlich, wenn die Testdaten keine Entscheidung über das Modell beeinflusst haben. Merkmalsauswahl ist auch eine Entscheidung, genauso wie das Auffüllen fehlender Werte, das Skalieren, die Wahl einer Schwelle oder die Wahl des besten von mehreren Modellen. Hat eine davon die Test-Labels gesehen, ist der Test nicht mehr unabhängig. Je größer die Auswahl (10 Spalten aus 1000), desto mehr zufällige Anpassung geht in den Wert über.

Ein einzelner Rauschdatensatz könnte zufällig so ausfallen. Aber in keinem der 20 neuen Datensätze im Labor fällt der Wert mit Leck bei k = 10 unter 81,7 %. Im Code unten läuft dasselbe Experiment auf 50 Rauschdatensätzen in scikit-learn: Die Wahl vor dem Aufteilen ergibt im Mittel 86,4 % (mindestens 73,3 %), die Wahl in jedem Fold 50,0 %.

Häufiger Fehler

Richtig oder falsch: „Ein Datenleck ist nur das in den Merkmalen versteckte Label.“ Falsch. Im Labor enthält keine Spalte das Label, und trotzdem zeigt die Validierung 81,7 % auf reinem Rauschen. Was leckt, ist eine Entscheidung, die auf allen Daten getroffen wurde. Bei den Biopsien ergibt das Oversampling der bösartigen vor der Kreuzvalidierung eine Sensitivität von 97,1 %, das Oversampling nur in den Trainingsdaten 92,1 %, in 50 von 50 Aufteilungen. Die Kopien landen auf beiden Seiten der Aufteilung, also erkennt das Modell Biopsien, die es schon gesehen hat. Duplikate in den Daten wirken genauso, und eine zufällige Aufteilung von Zeitdaten bringt die Zukunft ins Training.

Nicht jedes Leck ist gleich gefährlich. Ein auf allen Biopsien angepasster Skalierer ändert die Genauigkeit eines Nächster-Nachbar-Modells im Mittel um 0,08 Prozentpunkte: Er sieht die Labels nicht, also trägt er sehr wenig. Merkmalsauswahl und Oversampling nutzen die Labels, also tragen sie viel. Die Regel ist eine: Passe jeden Schritt, der etwas aus den Daten lernt, nur auf dem Trainingsteil an.

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. Die Wahl von 10 Spalten auf allen 60 Proben ergibt in der Kreuzvalidierung 81,7 % auf reinem Rauschen. Woher kommt dieser Wert?
Antwort anzeigen

Richtige Antwort: Die Spalten wurden auch nach den Labels der späteren Testproben gewählt

Bei der Wahl in jedem Fold nur auf den Trainingsdaten hat dasselbe Modell 43,3 %. Über 20 neue Rauschdatensätze liegt das Mittel bei 86,6 % mit Leck und 49,8 % ehrlich.

2. Du füllst fehlende Werte mit dem Mittelwert der ganzen Datei, teilst in Training und Test, trainierst auf dem Training und bewertest auf dem Test. Welcher Schritt ist undicht?
Antwort anzeigen

Richtige Antwort: Das Auffüllen der Lücken mit dem Mittelwert der ganzen Datei

Der Mittelwert enthält die Testdaten, also formt der Test das Training. Passe die Imputation nur auf dem Training an, am besten in einer Pipeline.

3. Skalieren vor dem Aufteilen ändert den Wert bei den Biopsien im Mittel um 0,08 Prozentpunkte, Oversampling der bösartigen vor dem Aufteilen hebt die Sensitivität von 92,1 % auf 97,1 %. Woher kommt der Unterschied?
Antwort anzeigen

Richtige Antwort: Der Skalierer ignoriert die Labels, aber Kopien bösartiger landen im Test

Ein Leck trägt so viel, wie die Entscheidung über den Test weiß. Der Skalierer kennt nur Mittelwerte und Abweichungen, eine Kopie einer bösartigen Biopsie im Test aber ist eine Antwort, die das Modell schon gesehen hat.

Das Wichtigste

  • Ein Datenleck ist Information, die das Modell bei der Vorhersage nicht hätte und die trotzdem sein Training oder seine Bewertung berührt hat.
  • Passe jeden Schritt, der aus Daten lernt (Auswahl, Imputation, Skalierung, Oversampling), nur auf dem Trainingsteil an, am besten in einer Pipeline.
  • Teile die Daten so, wie das Modell eingesetzt wird: nach Zeit, nach Person, ohne Duplikate.
  • Sieht ein Wert zu gut aus, prüfe ihn zuerst auf ein Datenleck.

Tiefer: Formel, Code, Praxis

Die Mathematik

Raten auf n Proben ergibt eine Genauigkeit mit Mittelwert 0,5 und Standardabweichung √(0,5 · 0,5 / n). Bei n = 60 sind das 0,065, ein Münzwurf ist also 50 % ± 13 Prozentpunkte (zwei Abweichungen).

Bei reinem Rauschen hat die Korrelation einer Spalte mit dem Label den Mittelwert 0 und eine Standardabweichung von etwa 1 / √n, bei n = 60 etwa 0,13. Unter 1000 Spalten liegt die beste mehrere Abweichungen von null entfernt: 0,48 im Labor.

Ehrliche Kreuzvalidierung berechnet  = Mittel über k von A(f_k, T_k), wobei das Modell f_k (samt Merkmalsauswahl) nur aus den Daten außerhalb des Folds T_k entsteht. Werden die Merkmale auf allen Daten gewählt, hängt die Auswahl von den Labels in T_k ab, also ist f_k nicht unabhängig von T_k, und  schätzt nicht mehr die Genauigkeit auf neuen Daten.

Im Code (Python)

Dasselbe Experiment in scikit-learn: die Wahl von 10 Spalten auf 50 Rauschdatensätzen und bei den Biopsien Skalieren und Oversampling der bösartigen vor dem Aufteilen und innerhalb jedes Folds, auf 50 Aufteilungen.

import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.model_selection import StratifiedKFold, cross_val_score, cross_val_predict
from sklearn.neighbors import NearestCentroid, KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import recall_score

def noise(seed):  # 60 Proben x 1000 Spalten reines Rauschen, Labels 30/30 in zufälliger Reihenfolge
    rng = np.random.default_rng(seed)
    X, y = rng.normal(size=(60, 1000)), rng.permutation(np.repeat([0, 1], 30))
    cv = StratifiedKFold(5, shuffle=True, random_state=seed)
    leak = cross_val_score(NearestCentroid(), SelectKBest(f_classif, k=10).fit_transform(X, y), y, cv=cv).mean()  # 10 Spalten auf allen 60 gewählt
    honest = cross_val_score(make_pipeline(SelectKBest(f_classif, k=10), NearestCentroid()), X, y, cv=cv).mean()  # in jedem Fold gewählt
    return leak, honest
r = np.array([noise(s) for s in range(50)])
print(f"pure noise, 50 datasets: picked before the split {r[:, 0].mean():.1%} (lowest {r[:, 0].min():.1%}), inside CV {r[:, 1].mean():.1%}")

X, y = load_breast_cancer(return_X_y=True)
y = 1 - y  # 1 = bösartig
knn = lambda: KNeighborsClassifier(1)
scale = []
for s in range(50):
    cv = StratifiedKFold(5, shuffle=True, random_state=s)
    before = cross_val_score(knn(), StandardScaler().fit_transform(X), y, cv=cv).mean()  # Skalierer auf allen Biopsien angepasst
    inside = cross_val_score(make_pipeline(StandardScaler(), knn()), X, y, cv=cv).mean()  # auf jedem Trainingsteil angepasst
    scale.append(abs(before - inside))
print(f"breast cancer, 1-NN, 50 splits: scaling before the split changes accuracy by {np.mean(scale) * 100:.2f} points on average")
def oversample(rows, rng):  # kopiert zufällige bösartige, bis beide Klassen gleich groß sind
    mal, ben = rows[y[rows] == 1], rows[y[rows] == 0]
    return np.r_[rows, rng.choice(mal, len(ben) - len(mal))]
rec = []
for s in range(50):
    rng, cv = np.random.default_rng(s), StratifiedKFold(5, shuffle=True, random_state=s)
    rows = oversample(np.arange(len(y)), rng)  # Kopien VOR dem Aufteilen landen auch in den Test-Folds
    before = recall_score(y[rows], cross_val_predict(make_pipeline(StandardScaler(), knn()), X[rows], y[rows], cv=cv))
    pred = np.empty(len(y))
    for tr, te in cv.split(X, y):  # Kopien nur innerhalb jedes Trainingsteils
        t = oversample(tr, rng)
        pred[te] = make_pipeline(StandardScaler(), knn()).fit(X[t], y[t]).predict(X[te])
    rec.append((before, recall_score(y, pred)))
rec = np.array(rec)
print(f"oversampling malignant before the split: recall {rec[:, 0].mean():.1%}, inside each training part: {rec[:, 1].mean():.1%}; higher before in {(rec[:, 0] > rec[:, 1]).sum()} of 50")

Ausgabe

pure noise, 50 datasets: picked before the split 86.4% (lowest 73.3%), inside CV 50.0%
breast cancer, 1-NN, 50 splits: scaling before the split changes accuracy by 0.08 points on average
oversampling malignant before the split: recall 97.1%, inside each training part: 92.1%; higher before in 50 of 50

Das Labor zeigt einen Rauschdatensatz und 20 weitere, der Code 50 andere Datensätze, also weichen die Zahlen etwas ab: Im Labor ergibt das Leck 81,7 % auf dem ersten Datensatz und im Mittel 86,6 % auf den nächsten 20, im Code im Mittel 86,4 %. Die Schlussfolgerung ist in jedem Datensatz dieselbe.

Für die Praxis
  • Stecke in scikit-learn jeden Schritt, der aus Daten lernt, zusammen mit dem Modell in eine Pipeline, und bewerte erst das Ganze mit cross_val_score: Dann sieht das fit jedes Schritts nur den Trainingsteil.
  • Teile Zeitdaten mit TimeSeriesSplit und gruppierte Daten (Patienten, Kunden, Fotos einer Kamera) mit GroupKFold.
  • Nutze Oversampling und SMOTE in einer Pipeline aus imbalanced-learn, nur auf den Trainingsteilen (Lektion 2.5).
  • Auf Kaggle steckt das Leck manchmal in der Datei selbst: IDs, Reihenfolge der Zeilen, Zeitstempel. Ein Wert, der viel besser ist als bei allen anderen, ist ein Signal, nach einem Leck zu suchen, bevor du feierst.

Mehr im Atlas

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