feature(notizen): add notes from l4 morning

This commit is contained in:
2026-06-04 13:53:41 +02:00
parent 7cd7220cef
commit b53476cf3b
+293
View File
@@ -0,0 +1,293 @@
# Notizen SL Lektion 4 (Foliensatz 12: Algorithmen — Decision Trees)
> Thema: Supervised Learning Algorithmen
> Datum: 04.06.2026
> Dozentin: Violeta Vogel
## Theorie Decision Trees
- Ein Entscheidungsbaum ist ein Modell, das Daten anhand von Wenn-Dann-Regeln klassifiziert oder vorhersagt
- Der Baum versucht Daten so aufzuteilen, dass Gruppen möglichst **rein** werden *(rein, nicht klein — Reinheit ist das Ziel, ein reiner Knoten darf gross sein)*
- nur noch eine Klasse enthalten
- Besteht aus
- Wurzelknoten
- Entscheidungsknoten
- Blattknoten
- Äste
- Arbeitet nach Top-Down Prinzip
- Divide and Conquer
- Merkmale wählen dass die Daten am besten trennt
- Entweder mit Gini Koeffizient oder Entropie-Reduktion trennen
- Aufteilen entlang dieses Merkmales in eine oder mehrere Gruppen
- Rekursiv weiter aufteilen
- Stop wenn
- Daten in einem Knoten homogen sind
- keine sinvollen Splits mehr möglich
- maximale Tiefe erreicht
- Eigenschaften
- Gut nachvollziehbar
- Flexibel
- Geringe Anforderungen an Feature Engineering
- keine Normierung oder Skalierung
- Anfällig zu Overfitting!
- Feature Importance
- Wichtigkeit eines Features
- Wie stark trägt ein Feature zur Vorhersage bei?
- Ein Feature ist wichtig, wenn es
- oft gesplittet werden kann
- früh im Baum vorkommt
- grosse Verbesserungen der Reinheit bringt
- Zwei Arten von Feature Importance
- Impurity based Importance
- schnell
- berechnet man während des Trainings
- Importance ergibt sich aus der Summe aller Reinheitsverbesserungen, die dieses Feature bringt
- die Summen werden zum Schluss normiert, sodass alle Importances zusammen **1** ergeben *(Folie 81 — daher sind es relative Anteile)*
- Wann verwenden?
- Für erste Einschätzungen
- Um Übersicht zu gewinnen
- Wenn Modell nicht zu viele korellierte Features hat
- Permutation Importance
- robust
- langsam
- berechnet man nach dem Training
- misst wie stark die Modellleistung sinkt wenn ein Feature zufällig permutiert (durchgewürfelt) wird
- **sinkt** die Leistung stark → Feature ist wichtig → bleibt *(korrigiert: nicht „verändert sich → raus“)*
- bleibt die Leistung ~gleich → Feature unwichtig → kann raus
- permutieren über alle Features
- Wann verwenden?
- Wenn man eine verlässliche Feature-Wichtigkeit braucht
- Wenn man ein BlackBoxModel erklärbar machen will
- Prüfen auf Bias
- Importance = Baseline Score - Score nach Permutation:
1. Zuerst Baseline Leistung messen
2. Ein Feature permutieren
3. Leistung erneut messen
4. Wichtigkeitswert berechnen
### Gini Impurity
- misst wie wahrscheinlich es ist, dass zwei zufällig ausgewählte Elemente aus einem Knoten verschiedenen Klassen angehören
- Für einen Knoten mit Klassenanteilen p1,p2,...,pK:
- Gini = 1 - sum(i=1,k,pi^2)
- Gini = 0 -> perfekte Reinheit (nur eine Klasse)
- Gini hoch -> starke Durchmischung
- Vorteile
- Effizient
- Schneller als Entropie
- ideal für grosse Datensätze
- Gut geeignet für Random Forests
- Nachteile
- Bevorzugt Features mit vielen Splitpunkten
- Also numerische Features und solche mit vielen Kategorien
- Kann bei unbalancierten Klassen verzerren
### Entropie
- misst wie viel Unsicherheit in einem Datensegment steckt
- Entscheidungsbaum versucht Unsicherheit durch Splits zu reduzieren
- Für einen Knoten mit Klassenanteilen p1,p2,...,pK:
- entropie = -sum(i=1,k,pi*log2(pi))
- Entropie = 0 -> perfekte Reinheit (nur eine Klasse)
- Entropie hoch -> starke durchmischung
- Vorteile
- hohe sensitivität
- reagiert stärker auf Veränderung als Gini
- mathematisch sauber definiert
- gute Trennung bei seltenen Klassen
- Nachteile
- höherer Rechenaufwand als Gini, da Logarithmen berechnet werden müssen
- Ähnliches Verhalten wie Gini, der Aufwand lohnt sich also nicht immer
- Kann bei stark unbalancierten Daten instabil sein
- überempfindliche Splits bei seltenen Klassen
- Kann zu tiefen Bäumen führen
- overfitting
### Splitting
- ist der Prozess bei dem der Entscheidungsbaum die Frage auswählt, die Daten am besten trennt
- testet viele mögliche Splits und bewertet nach Reinheitsmass
- wählt Split mit höchstem Informationsgewinn
1. Alle features testen
- für jedes Feature mögliche Schwellenwerte prüfen
- Feature Alter -> teste 20,25,30,45 ...
- Feautre Einkommen -> teste 40k,50k,69k ...
2. für jeden Split die Reinheit berechnen
- berechnen wie "rein" die beiden entstehenden Gruppen sind, bspw. mit:
- Gini-Impurity
- Entropie
- Misclassification Error
3. Splitqualität bestimmen
- Baum misst wie stark sich die Reinheit verbessert
- je grösser Gain desto besser der Split
4. Den besten Split auswählen
5. Rekursiv weiter splitten
- bis Stopkriterium erreicht ist
### Pruning
- Pruning ist der Oberbegriff fürs Begrenzen der Baum-Komplexität gegen Overfitting; es gibt zwei Spielarten: Pre-Pruning (Wachstum begrenzen) und Post-Pruning (voll wachsen lassen, dann zurückschneiden) *(die Definition „nach dem Training verkleinern“ beschreibt genau genommen nur Post-Pruning)*
- Ziel ist es Overfitting zu reduzieren
- Vorteil
- reduziert overfitting
- verbessert generalisierung
- vereinfacht interpretierbarkeit
- erhöht stabilität
- verhindert unnötige splits
- Nachteile
- kann zu stark vereinfachen
- nicht immer nötig
- erfordert Validierungsdaten um optimal zu funktionieren
- rechenintensiver als pre-pruning
### Pre-Pruning
- setzt Wachstumsgrenzen für den Baum damit er nicht unnötig tief wird
- typische Kriterien
- maximale Tiefe
- minimale Samples pro Blatt
- minimale Verbesserung nötig für Split
- Methoden
- max_depth
- begrenzung der Baumtiefe
- min_samples_split
- nur splitten wenn genügend Datenpunkte verfügbar
- min_samples_leaf
- jedes Blatt muss mindestens n Samples enthalten
- min_impurity_decrease
- Split wird nur akzeptiert wenn er die Reinheit genügend verbessert
- max_leaf_nodes
- begrenzt die Anzahl Blätter direkt → kompakte Bäume
### Entscheidung Pruning/Prepruning
- Vorteile von Pre-Pruning
- schnell
- weniger Rechenaufwand
- verhindert overfitting früh
- einfachere modelle (flache bäume) sind einfacher zu verstehen
- Nachteile von Pre-Pruning
- kann gute Spliuts verhindern wenn Grenzen zu streng sind
- erfordert Hyperparameter-Tuning -> sonst Underfitting
- weniger flexibel als Post-Pruning
### Post-Pruning
- Post-Pruning schneided bereits gewachsene Bäume zurück um Overfitting zu reduzieren
- Methoden
- Cost-Complexity-Pruning *(in sklearn: Parameter `ccp_alpha`)*
- Reduced Error Pruning
- Vorteile
- robuster gegenüber neuen Daten
- verbessert generalisierung
- flexibler als Pre-Pruning, der Baum darf zuerst alle Muster entdecken
- Nachteile
- rechenintensiver
- benötigt Validierungsdaten
- komplexere Implementierung
## Decision Tree Classifier
- ist ein überwachter Machine-Learning-Algorihmus der Daten anhand einer Reihe von regelbasierten Entscheidungen klassifiziert
- Erstell ein umgedrehtes Baumdiegramm
- oben ist das wichtigste Merkmal
- jede Antwort führt zu einem neuen Ast und zu einer weiteren Frage
- bis ein Blattknoten erreicht wird, der die finale Klassenzuordnung enthält
---
## Praxis: Decision Tree Classifier (Code)
> Ergänzung zu den übersprungenen Praxis-Folien (bis Workshop 5). Datengrundlage:
> vorbereiteter Bank-Datensatz, geladen über das Kursmodul `bfh_cas_pml`.
### Basismodell
```python
from bfh_cas_pml import prep_data
from sklearn.tree import DecisionTreeClassifier
X_train, X_test, y_train, y_test = prep_data('bank_data_prep.csv', 'y', seed=1234)
model = DecisionTreeClassifier(random_state=1234) # random_state nur für Reproduzierbarkeit
model.fit(X_train, y_train)
print(model.score(X_test, y_test)) # ≈ 0.83
```
### Diagnose — Overfitting sichtbar machen
```python
print('depth :', model.get_depth()) # 28
print('leaves:', model.get_n_leaves()) # 777
print('train :', model.score(X_train, y_train)) # 1.0 <- perfekt
print('test :', model.score(X_test, y_test)) # 0.83
```
`train=1.0` vs `test=0.83` ist das Lehrbuch-Symptom: der voll ausgewachsene Baum
memoriert die Trainingsdaten (jedes Blatt rein), generalisiert aber schlecht.
→ Abhilfe = Pruning.
### Pre-Pruning per Hyperparameter-Tuning (Workshop 5: `min_impurity_decrease`)
Ein Split wird nur gemacht, wenn er die Impurity um mindestens diesen Wert senkt —
**normiert auf den ganzen Baum und gewichtet nach Knotengröße**:
```
ΔI_norm = (N_node / N_total) * ( I_parent - w_left*I_left - w_right*I_right )
```
Folge: tiefe Knoten betreffen wenige Samples → ihr ΔI_norm ist winzig → sinnvolle
Schwellen liegen bei ~1e-4 … 1e-2 (Float → `np.arange`, nicht `range`).
```python
import numpy as np
model = DecisionTreeClassifier(random_state=1234)
params = np.arange(0, 0.004, 0.0002)
scores = []
for p in params:
model.set_params(min_impurity_decrease=p)
model.fit(X_train, y_train)
scores.append(model.score(X_test, y_test))
best_i = np.argmax(scores) # Index des Maximums
print(f'best={scores[best_i]:.4f} @ {params[best_i]}') # ~0.879 @ 0.0004
```
Vorgehen = Wertebereich **schrittweise eingrenzen** (grob → fein). Erwarteter
Kurvenverlauf: kurzer Anstieg/Bump über der Baseline (0.83), dann Abfall, sobald
nützliche Splits wegfallen; im Extrem Stumpf → Accuracy ≈ Mehrheitsklasse.
> ⚠ Methodik-Caveat: hier wird gegen `X_test` getunt → **optimistic bias** (wie
> in W4). Der „beste“ Score ist optimistisch verzerrt; sauber wäre ein
> Validation-Split bzw. CV.
### Baum visualisieren (gepruntes Modell)
```python
from bfh_cas_pml import inspect_decision_tree_model
inspect_decision_tree_model(
DecisionTreeClassifier(min_impurity_decrease=0.0004, random_state=1234),
X_train, y_train)
# druckt depth/leaves/score und zeichnet plot_tree -> der geprunte Baum ist klein & lesbar
```
### Feature Importance (impurity-based)
```python
import pandas as pd
import seaborn as sns
imp = pd.Series(model.feature_importances_, index=X_train.columns).sort_values()
sns.barplot(x=imp.values, y=imp.index) # horizontaler Bar-Chart wie auf Folie 83
# Top-Features im Bank-Set: duration, nr_employed, month, euribor3m, age
```
Permutation Importance (robuster, in den Folien nur als Theorie):
```python
from sklearn.inspection import permutation_importance
r = permutation_importance(model, X_test, y_test, n_repeats=10, random_state=1234)
perm = pd.Series(r.importances_mean, index=X_train.columns).sort_values()
```