feature(workshop): add workshop5 decision tree tuning

This commit is contained in:
2026-06-04 13:54:33 +02:00
parent b53476cf3b
commit cd5581ada4
9 changed files with 10311 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
# Workshop 05 — Decision Tree Classifier: `min_impurity_decrease` Tuning
## Aufgabe
Untersuchen, wie verschiedene Werte von `min_impurity_decrease` beim
`DecisionTreeClassifier` die erreichbare **Test-Accuracy** beeinflussen.
- Wertebereich **schrittweise eingrenzen** (grob → fein).
- Resultate darstellen:
- grafisch als **Liniendiagramm** (Parameter vs. Accuracy),
- in der **Konsole**: bester Score + zugehöriger Parameterwert.
Hinweis aus den Folien: `range()` liefert Ganzzahlen, `np.arange()` liefert
Gleitkommawerte. `min_impurity_decrease` ist ein Float → `np.arange`.
## Setup
```bash
devenv shell # python + venv (pandas, numpy, sklearn, matplotlib, seaborn)
```
Datengrundlage = vorbereiteter Bank-Datensatz aus W4. Geladen über das
kursinterne Modul (muss auf dem `PYTHONPATH` / im Projektordner liegen):
```python
from bfh_cas_pml import prep_data
X_train, X_test, y_train, y_test = prep_data('bank_data_prep.csv', 'y', seed=1234)
```
Baseline zum Abgleich (voll ausgewachsener Baum):
```python
DecisionTreeClassifier(random_state=1234) # train=1.0, test≈0.8296 → overfit
```
> Falls `bfh_cas_pml` / `bank_data_prep.csv` nicht zur Hand sind: als Fallback
> tut es jeder `train_test_split` auf einem sklearn-Datensatz — die Mechanik
> des Sweeps bleibt identisch.
## Theorie — was `min_impurity_decrease` tut
Ein Split wird **nur** ausgeführt, wenn er die (gewichtete, auf den ganzen
Baum normierte) Impurity um mindestens diesen Wert senkt:
```
ΔI_norm = (N_node / N_total) * ( I_parent
- (N_left/N_node) * I_left
- (N_right/N_node) * I_right )
```
Kernpunkte fürs Verständnis:
- Es ist eine **Pre-Pruning**-Schwelle: schwache Splits werden gar nicht erst
gemacht → der Baum bleibt kleiner → weniger Overfitting.
- Der Wert ist mit dem **Anteil der Beobachtungen im Knoten** gewichtet
(`N_node / N_total`). Tiefe Knoten betreffen wenige Samples → ihr ΔI_norm ist
winzig. Darum liegen sinnvolle Schwellen im Bereich **~1e-4 bis ~1e-2**, nicht
bei 0.1+. (Vgl. das Folien-Rechenbeispiel: ein *guter* Split nahe der Wurzel
ergab 0.0421.)
- `=0` → kein Pruning → Baseline-Baum (overfit).
## Vorgehen — Eingrenzung (der eigentliche Lerninhalt)
1. **Grob**: weiter Bereich, grobe Schritte, um die Region des Maximums zu
lokalisieren — z. B. `np.arange(0, 0.02, 0.001)`.
2. **Fein**: um das gefundene Maximum herum zoomen — z. B.
`np.arange(0, 0.004, 0.0002)`.
3. Wiederholen, bis Lage/Wert des Peaks stabil sind.
Jede Iteration ist ein eigener Sweep (gleicher Loop, anderer `np.arange`).
Im README/Notes die drei (o. ä.) Ranges + jeweils Peak dokumentieren — das
*ist* die geforderte „schrittweise Eingrenzung“.
## Erwartetes Verhalten (Sanity-Check)
- Bei `0` startest du auf der Baseline (~0.83).
- Mit steigendem Wert zunächst Plateau / leichter Bump (Rausch-Splits werden
entfernt), dann **Kante nach unten**, sobald nützliche Splits wegfallen.
- Im Extrem degeneriert der Baum zum Stumpf → Accuracy = Mehrheitsklasse.
Der Bank-Datensatz wurde in W4 ~balanciert resampled → Floor liegt nahe
~0.5. Wenn deine Kurve dort hin abstürzt, ist das korrekt, kein Bug.
## Deliverables
- [ ] Loop über `np.arange`-Range, `set_params(min_impurity_decrease=p)`,
`fit`, `score(X_test, y_test)`, sammeln.
- [ ] `sns.lineplot(x=params, y=scores)` + Scatter-Marker auf `max(scores)`,
Achsen beschriftet (`min_impurity_decrease` / `accuracy`).
- [ ] Konsole: `best score` + zugehöriger Parameterwert.
- [ ] Mind. 2 Eingrenzungs-Stufen (grob + fein) dokumentiert.
## Caveats / Vertiefung (optional)
- **Optimistic bias** (wie in W4): hier wird `min_impurity_decrease` direkt
gegen `X_test` getunt — derselbe Trap wie die manuelle Grid-Search ohne CV.
Der gemeldete „beste“ Score ist dadurch optimistisch verzerrt. Die Folien
machen es so; fürs Deliverable also bewusst übernehmen, aber als Deviation
notieren. Sauber wäre Tuning auf einem Validation-Split bzw. `GridSearchCV`.
- **Verwandter Faden, falls Zeit/Interesse**: `ccp_alpha` (Cost-Complexity /
Minimal-Cost-Complexity-Pruning) ist sklearns „eigentlicher“ Pruning-Knopf
und liefert via `cost_complexity_pruning_path()` direkt eine sinnvolle
Kandidatenliste statt manueller `np.arange`-Rate-Erei — nur als Zeiger, nicht
Teil der Aufgabe.