Files
cas-pml/SL/aufgaben/workshop4/README.md
T

152 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
1. Features von Trainings- und Testdaten mit `StandardScaler` standardisieren.
2. Beste Parameter für `KNeighborsClassifier` finden:
- `n_neighbors` ∈ {1..10}
- `p` ∈ {1, 2, 3}
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`
(1798) oder `duration` (Sekunden). `StandardScaler` zentriert jede Spalte auf
Mittelwert 0 / Standardabweichung 1 und stellt damit alle Features gleichwertig.
### Leakage vermeiden: `fit` nur auf Train
```python
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:
1. **Optimistic Bias:** Tunt gegen das Test-Set. Die gewählte Kombi ist die,
die zufällig auf *genau diesem einen* Split am besten lief.
2. **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
```sh
python src/hyperparametersearch.py
```
## Implementierungs-Notizen
Bewusste Entscheidungen, die über die Folien-Vorlage hinausgehen:
- **`fit` nur auf `X_train`** beim `StandardScaler` (Folie macht es so, viele
Anleitungen nicht — daher hier explizit dokumentiert).
- **`GridSearchCV` zusä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
- **`Pipeline` für Scaler + kNN.** Ein `Pipeline([('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.