feature(notizen): add notes from l4 morning
This commit is contained in:
@@ -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()
|
||||
```
|
||||
Reference in New Issue
Block a user