Învățare supervizată

2.5 Arborii de decizie

Un arbore de decizie pune întrebări simple în cascadă („glucoza > 140?") până ajunge la un răspuns. Este modelul cel mai ușor de explicat — și fundamentul Random Forest și Gradient Boosting.

Teorie

Construirea arborilor

Fiecare nod alege întrebarea care separă cel mai bine clasele (feature + prag). Datele se împart pe cele două ramuri și procesul se repetă recursiv până când nodurile sunt „pure" (o singură clasă) sau se atinge o limită. Frunza dă predicția: clasa majoritară din ea.

Criterii de separare: Gini și entropia

Impuritate Gini: G = 1 − Σ pᵢ² — probabilitatea ca două exemple luate la întâmplare din nod să aibă clase diferite. 0 = nod pur.

Entropia: H = −Σ pᵢ·log₂(pᵢ) — câtă „dezordine" e în nod. Se alege separarea cu cel mai mare câștig informațional.

Exemplu: un nod cu 50/50 din două clase are Gini = 1 − (0.5² + 0.5²) = 0.5 (maxim); un nod cu 90/10 are Gini = 1 − (0.81 + 0.01) = 0.18. În practică, Gini și entropia dau arbori aproape identici; Gini e implicit fiind mai rapid.

Controlul adâncimii și pruning

Lăsat liber, arborele crește până memorează fiecare exemplu — overfitting garantat. Frâne:

ParametruEfect
max_depthadâncimea maximă a arborelui
min_samples_splitminim de exemple ca un nod să se mai împartă
min_samples_leafminim de exemple într-o frunză
ccp_alphapruning: taie după construire ramurile care nu ajută

Pre-pruning = limitezi creșterea din start (parametrii de sus); post-pruning = lași arborele să crească, apoi tai ramurile slabe (ccp_alpha).

Interpretare și utilizarea scikit-learn

from sklearn.tree import DecisionTreeClassifier, plot_tree
import matplotlib.pyplot as plt

model = DecisionTreeClassifier(max_depth=4, criterion="gini", random_state=42)
model.fit(X_train, y_train)

# desenezi arborele — argument imbatabil la explicarea soluției
plt.figure(figsize=(16, 8))
plot_tree(model, feature_names=X_train.columns,
          class_names=True, filled=True, fontsize=8)
plt.show()

# ce feature-uri contează
for f, imp in sorted(zip(X_train.columns, model.feature_importances_),
                     key=lambda t: -t[1]):
    print(f"{f}: {imp:.3f}")
Arborii nu au nevoie de scalare — pragurile funcționează pe orice scară. De aceea sunt primul model de încercat pe date tabelare „murdare".

Problemă rezolvată: clasificarea tipului de vin

Clasificarea tipului de vin Ușor Rezolvată — 99p

Pe baza analizelor chimice (aciditate, zahăr, pH, alcool), determinăm tipul vinului. Arborele oferă și explicația: care analize contează cel mai mult.

Soluția pas cu pas
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score

train = pd.read_csv("train.csv")
test = pd.read_csv("test.csv")
feats = [c for c in train.columns if c not in ("SampleID", "tip")]

# alegem adâncimea prin cross-validation
for d in [3, 4, 5, 6, 8, None]:
    m = DecisionTreeClassifier(max_depth=d, random_state=42)
    print(d, cross_val_score(m, train[feats], train["tip"], cv=5).mean().round(3))

model = DecisionTreeClassifier(max_depth=5, random_state=42)
model.fit(train[feats], train["tip"])

pd.DataFrame({
    "SampleID": test["SampleID"],
    "tip": model.predict(test[feats])
}).to_csv("submission.csv", index=False)

Observație tipică: adâncimea None (nelimitată) dă scor de validare mai slab decât 5 — overfitting vizibil în cifre.

Probleme propuse

1. Gini de mână Exercițiu

Un nod are 8 exemple din clasa A și 2 din clasa B. Calculează impuritatea Gini și entropia. O separare le împarte în (8A, 0B) și (0A, 2B) — care e Gini-ul mediu ponderat după separare?

2. Arborele desenat Exercițiu

Antrenează un arbore cu max_depth=3 pe Iris, desenează-l cu plot_tree și tradu în cuvinte drumul complet către clasa „virginica".

3. Overfitting măsurat Exercițiu

Pentru max_depth de la 1 la 20, desenează acuratețea pe train și pe validare pe același grafic. Unde se despart curbele?

4. Predicția performanței școlare Platformă

Problemă tabelară rezolvată cu succes cu modele pe bază de arbori.