Workshop 4 — kNN Hyperparametersuche
Hyperparameter-Tuning für KNeighborsClassifier auf bank_data_prep.csv
(Klassifikation: Hat der Kunde abgeschlossen, ja/nein?), inklusive Vergleich
mit/ohne Standardisierung.
- Input:
data/bank_data_prep.csv(Output aus Workshop 3 / Bank-Implementation) - Pipeline:
src/hyperparametersearch.py
Aufgabenstellung
Drei Teile gemäss Folie:
- Features von Trainings- und Testdaten mit
StandardScalerstandardisieren. - Beste Parameter für
KNeighborsClassifierfinden:n_neighbors∈ {1..10}p∈ {1, 2, 3}
- Ergebnisse mit vs. ohne Standardisieren vergleichen.
Konzepte kurz erklärt
Was ist p? (Minkowski-Distanz)
p steuert die Distanzmetrik, mit der kNN „Nachbarschaft" misst:
p |
Distanz | Formel | Verhalten |
|---|---|---|---|
| 1 | Manhattan | Σ |x_i − y_i| | Strassen-Netz-Distanz, robuster gegen Ausreisser in Einzeldimensionen |
| 2 | Euklidisch | √Σ(x_i − y_i)² | „Luftlinie", sklearn-Default |
| 3+ | Höhere Minkowski | (Σ|x_i − y_i|^p)^(1/p) | Gewichtet grosse Einzelunterschiede zunehmend stärker |
Bei diesem Datensatz gewinnt p=1 (Manhattan) konsistent über alle Varianten —
plausibel, weil viele Dummy-Features (0/1) drin sind und Manhattan diese
gleichmässiger gewichtet als Euklidisch.
Warum Standardisierung bei kNN nicht optional ist
kNN ist distanzbasiert. Ohne Skalierung dominiert die Variable mit dem
grössten Wertebereich die Distanzberechnung — Dummy-Features (0/1) werden
faktisch ignoriert, weil ihr Beitrag zur Distanz verschwindet gegen z.B. age
(17–98) oder duration (Sekunden). StandardScaler zentriert jede Spalte auf
Mittelwert 0 / Standardabweichung 1 und stellt damit alle Features gleichwertig.
Leakage vermeiden: fit nur auf Train
scaler.fit(X_train) # mean/std NUR aus Train lernen
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test) # mit Train-Statistiken transformieren
Würde der Scaler auf X_test (oder dem gesamten Datensatz) gefittet, flössen
Test-Statistiken in die Vorverarbeitung ein → Leakage. Das Test-Set soll so
behandelt werden, als sähe man es zum ersten Mal — denn so wird es in
Produktion (neue Daten) auch sein.
Hyperparametersuche: manuell vs. GridSearchCV
Folien-Methode: Doppelschleife über n_neighbors × p, Score auf Test-Set
genommen, beste Kombi gewählt. Funktioniert, hat aber zwei Schwächen:
- Optimistic Bias: Tunt gegen das Test-Set. Die gewählte Kombi ist die, die zufällig auf genau diesem einen Split am besten lief.
- Einziger Split: Keine Robustheit gegen ungünstige Train/Test-Aufteilungen.
GridSearchCV löst beides: Cross-Validation auf den Trainingsdaten (k-Fold,
hier 5), Test-Set wird erst am Ende einmal angefasst.
Drei Varianten
| Variante | Skalierung | Hyperparameter-Suche | Bestes k, p | Accuracy |
|---|---|---|---|---|
| A | nein | manuell (Doppelschleife) | k=9, p=1 | 76,6 % (Test) |
| B | ja | manuell (Doppelschleife) | k=9, p=1 | 80,4 % (Test) |
| C | ja | GridSearchCV (5-fold) | k=9, p=1 | 81,1 % (CV) / 80,4 % (Test) |
Lesart:
- A → B: +3,7 pp durch Skalierung. Belegt Aufgabenteil 3.
- B → C: Accuracy bleibt gleich, aber der CV-Score ist der ehrliche Schätzer für die Generalisierung. Der Test-Score von B war leicht optimistisch verzerrt (gegen Test getunt); C bestätigt ihn unabhängig.
Klassifikationsqualität (Variante C)
[[1461 274]
[ 371 1181]]
precision recall f1-score support
no 0.80 0.84 0.82 1735
yes 0.81 0.76 0.79 1552
accuracy 0.80 3287
Beide Klassen werden ausgewogen vorhergesagt (F1 ≈ 0,80 für beide). Das Modell
ist leicht konservativer mit yes-Vorhersagen (Recall 0,76 vs. 0,84 für no).
Befund zum Datensatz
bank_data_prep.csv ist mit 53/47 (no/yes) annähernd balanciert. Das
Original-Bank-Set hat eine ~88/12-Verteilung — der gelieferte Datensatz wurde
also vorab resampled (vermutlich SMOTE oder Random Over/Undersampling via
imbalanced-learn). Konsequenz: Accuracy ist hier eine vertretbare Metrik;
wäre der Datensatz im Original-Verhältnis, wären 80 % Accuracy schlechter
als die triviale „immer no"-Baseline (88 %), und man müsste mit Precision/
Recall/F1 arbeiten.
Projektstruktur
workshop4
├── data/
│ └── bank_data_prep.csv
├── src/
│ └── hyperparametersearch.py
├── devenv.nix
└── README.md
Ausführen
python src/hyperparametersearch.py
Implementierungs-Notizen
Bewusste Entscheidungen, die über die Folien-Vorlage hinausgehen:
fitnur aufX_trainbeimStandardScaler(Folie macht es so, viele Anleitungen nicht — daher hier explizit dokumentiert).GridSearchCVzusätzlich zur Folien-Doppelschleife, um Optimistic Bias sichtbar zu machen und einen leakage-freien CV-Score zu erhalten.classification_report+ Confusion Matrix über die Folien-Anforderungen hinaus, weil reine Accuracy bei Klassifikation nie das ganze Bild zeigt.
Offene Punkte für ein echtes Projekt
Pipelinefür Scaler + kNN. EinPipeline([('scaler', ...), ('knn', ...)])macht aus den zwei Schritten ein Objekt —predict()skaliert dann automatisch mit dem trainierten Scaler. Verhindert strukturell die „Modell trainiert auf skaliert, Vorhersage auf unskaliert"-Klasse von Bugs. Auch GridSearchCV erhält dann die Pipeline statt nur den Classifier, was die Skalierung in jedem CV-Fold korrekt nur auf dem Fold-Train fittet.- Resampling-Schritt im Datenpfad sichtbar machen. Der Resampling-Eingriff passierte ausserhalb dieser Pipeline; in einem echten Projekt gehört er dokumentiert oder selbst (re-)produziert, sonst sind Ergebnisse nicht reproduzierbar.
- Mehr Metriken bei der CV.
GridSearchCV(scoring=...)kann auch auf F1, ROC-AUC u.a. optimieren — sinnvoller als Accuracy, sobald Klassen unbalanciert sind.