Die ML-Pipeline

Egal ob Logistic Regression oder CNN — der Weg von 54.000 Fotos zu einer belastbaren Zahl ist immer derselbe. Wer diese Pipeline einmal sauber verinnerlicht, erkennt 80 % aller ML-Fehler sofort: Es sind fast immer Reihenfolge-Fehler.

Eiserne Regel: Alles, was aus den Daten gelernt wird (Skalierung, Modell, Feature-Auswahl), darf nur die Trainingsdaten sehen. Test-Daten sind tabu, bis die allerletzte Zahl berichtet wird.

Die sechs Schritte

1
Daten laden
Bilder + Labels aus der Ordnerstruktur einlesen. Ordnername = Klasse.
2
Split: Train / Validation / Test
Z.B. 70/15/15 — stratifiziert, damit jede der 38 Klassen in jedem Teil im gleichen Verhältnis vorkommt.
3
Preprocessing
Bilder skalieren, Features extrahieren, normalisieren. Scaler wird nur auf Train gefittet, dann auf alles angewendet.
4
Training
Das Verfahren lernt seine Parameter aus den Trainingsdaten — Gewichte, Splits oder Filter, je nach Modell.
5
Validierung & Tuning
Hyperparameter (C, n_estimators, Lernrate…) am Validation-Set vergleichen — so oft du willst.
6
Finale Evaluation
Genau einmal auf dem Test-Set messen. Diese Zahl kommt in den Bericht.

In Python: Dateiliste + stratifizierter Split

Für die klassischen Verfahren sammeln wir erst Pfade und Labels, gesplittet wird bevor irgendetwas berechnet wird:

PYTHONsplit.py
from pathlib import Path
from sklearn.model_selection import train_test_split

DATA_DIR = Path("plantvillage dataset/color")

pfade, labels = [], []
for klassen_ordner in sorted(DATA_DIR.iterdir()):
    for bild in klassen_ordner.glob("*.jpg"):
        pfade.append(bild)
        labels.append(klassen_ordner.name)

# 70 % Train, 30 % Rest — stratify hält die Klassenverhältnisse
X_train, X_rest, y_train, y_rest = train_test_split(
    pfade, labels, test_size=0.30, stratify=labels, random_state=42
)
# Rest nochmal halbieren: 15 % Validation, 15 % Test
X_val, X_test, y_val, y_test = train_test_split(
    X_rest, y_rest, test_size=0.50, stratify=y_rest, random_state=42
)

print(len(X_train), len(X_val), len(X_test))  # ~38000, ~8150, ~8150
Warum eigentlich?Warum ist das Test-Set heilig?
Das Test-Set simuliert Zukunft: Daten, die das Modell im Einsatz sehen wird und die heute niemand kennt. Jedes Mal, wenn du eine Entscheidung anhand des Test-Scores triffst („mit C=10 ist Test besser, nehmen wir!“), fließt Information aus dem Test-Set ins Modell — und der Score wird zur Selbsttäuschung. Genau dafür existiert das Validation-Set: Es ist das Test-Set, das du verbrauchen darfst.
Häufiger DenkfehlerData Leakage — der Klassiker

Leakage = Information aus Validation/Test sickert ins Training. Die zwei häufigsten Varianten:

  • Scaler auf allem fitten: scaler.fit(X) vor dem Split berechnet Mittelwert und Streuung auch aus Test-Daten. Richtig: scaler.fit(X_train), dann scaler.transform(X_test).
  • Near-Duplicates über den Split: PlantVillage enthält teils mehrere Fotos desselben Blatts. Landet eins in Train und eins in Test, prüft der Test nur noch Wiedererkennen statt Generalisieren — die Scores werden künstlich gut.
Tiefer reinCross-Validation statt festem Validation-Set
Bei kleinen Datasets verschenkt ein festes Validation-Set wertvolle Trainingsdaten. k-Fold Cross-Validation teilt Train in k Stücke, trainiert k-mal (jedes Stück einmal als Validierung) und mittelt die Scores. Bei 54.000 Bildern ist das für die klassischen Verfahren machbar (cross_val_score in scikit-learn), beim CNN meist zu teuer — dort bleibt es beim festen Split. Das Test-Set bleibt in beiden Welten unangetastet.