graph LR
A[Feature A > X?<br>Node] -->|Yes| B(Feature B ≤ Y?<br>Node)
A -->|No| C{Feature C > Z?<br>Node}
B -->|Yes| D((Class 1<br>Leaf))
B -->|No| E((Class 2<br>Leaf))
C -->|Yes| F((Class 3<br>Leaf))
C -->|No| G((Class 4<br>Leaf))
style A fill:#f9f,stroke:#333,stroke-width:2px
style B fill:#f9f,stroke:#333,stroke-width:2px
style C fill:#f9f,stroke:#333,stroke-width:2px
style D fill:#ccf,stroke:#333,stroke-width:2px
style E fill:#ccf,stroke:#333,stroke-width:2px
style F fill:#ccf,stroke:#333,stroke-width:2px
style G fill:#ccf,stroke:#333,stroke-width:2px
Forelesningsnotat: Beslutningstrær
Slide-versjon
Videre til beslutningstrær
Vi skal først se på hva beslutningstrær er for noe, og prøve å designe et par slike. Så kjører vi litt live-programmering av beslutningstrær med scikit-learn. Vi venter med skoger og boosting til neste gang.
Beslutningstre
- En trestruktur for prediksjoner.
- Hver node representerer en test
- Hver gren representerer utfallet av testen
- Hvert blad representerer en prediksjon.
Eksempel: Boston housing
import numpy as np
import pandas as pd
df = pd.read_csv("data/HousingData.csv")
print(df.drop(columns=["AGE", "CHAS", "ZN", "DIS"])) CRIM INDUS NOX RM RAD TAX PTRATIO B LSTAT MEDV
0 0.00632 2.31 0.538 6.575 1 296 15.3 396.90 4.98 24.0
1 0.02731 7.07 0.469 6.421 2 242 17.8 396.90 9.14 21.6
2 0.02729 7.07 0.469 7.185 2 242 17.8 392.83 4.03 34.7
3 0.03237 2.18 0.458 6.998 3 222 18.7 394.63 2.94 33.4
4 0.06905 2.18 0.458 7.147 3 222 18.7 396.90 NaN 36.2
.. ... ... ... ... ... ... ... ... ... ...
501 0.06263 11.93 0.573 6.593 1 273 21.0 391.99 NaN 22.4
502 0.04527 11.93 0.573 6.120 1 273 21.0 396.90 9.08 20.6
503 0.06076 11.93 0.573 6.976 1 273 21.0 396.90 5.64 23.9
504 0.10959 11.93 0.573 6.794 1 273 21.0 393.45 6.48 22.0
505 0.04741 11.93 0.573 6.030 1 273 21.0 396.90 7.88 11.9
[506 rows x 10 columns]
Verdi på husene
Vi ønsker å estimere median verdi på husene i et område, fra andre kolonner i datasettet.
print(f"Mean value: {np.mean(df.MEDV)}")
print(f"Variance of value: {np.var(df.MEDV)}")Mean value: 22.532806324110677
Variance of value: 84.41955615616556
Grunt beslutningstre
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import train_test_split
from sklearn.tree import plot_tree
import matplotlib.pyplot as plt
df = pd.read_csv("data/HousingData.csv")
X = df.drop(columns=["MEDV"]).values
feature_names = df.drop(columns=["MEDV"]).columns
y = df["MEDV"].values
# Trener modellen
tree = DecisionTreeRegressor(max_depth=2, random_state=4)
tree.fit(X, y)
feature_names = list(feature_names)
feature_names[feature_names.index("RM")] = "Rooms per dwelling"
plt.figure(figsize=(12, 7))
plot_tree(tree, feature_names=feature_names, filled=True)
plt.show()
Litt dypere
df = pd.read_csv("data/HousingData.csv")
X = df.drop(columns=["MEDV"]).values
feature_names = df.drop(columns=["MEDV"]).columns
y = df["MEDV"].values
# Trener modellen
tree = DecisionTreeRegressor(max_depth=3, random_state=4)
tree.fit(X, y)
feature_names = list(feature_names)
feature_names[feature_names.index("RM")] = "Rooms per dwelling"
plt.figure(figsize=(12, 7))
plot_tree(tree, feature_names=feature_names, filled=True)
plt.show()
Fordeler
- Intuitivt og lett å tolke: Kan ligne menneskelig beslutningstaking.
- Kan uten videre predikere flere klasser (i motsetning til vanlig logistisk regresjon)
Ulemper:
- Tendens til overtilpasning: Kan bli for komplekse og tilpasse seg treningsdataene for godt.
- Ustabile: Små endringer i dataene kan føre til store endringer i treet.
- “Grådig” algoritme: Lokal optimalisering, ikke garantert globalt optimalt tre.
På grunn av disse ulempene lager man gjerne en random forest av beslutningstrær for å få en bedre prediktor. Det skal vi se på en annen dag.
Eksempel: iris-datasettet
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
prop_cycle = mpl.rcParams['axes.prop_cycle']
colors = prop_cycle.by_key()['color']
iris = load_iris()
X, y = np.array(iris.data), np.array(iris.target)
target_names = iris.target_names
feature_names = iris.feature_names
color_list = [colors[i] for i in y]
names = [target_names[i] for i in y]
df = pd.DataFrame({
feature_names[0] : X[:,0],
feature_names[1] : X[:,1],
feature_names[2] : X[:,2],
feature_names[3] : X[:,3],
"species" : names
})
plot_pairs = [[0,1], [2, 3], [0, 2], [0, 3], [1, 2], [1,3]]
plt.figure(figsize=(10, 5))
for i, pair in enumerate(plot_pairs):
plt.subplot(2,3,i+1)
for species, frame in df.groupby("species"):
plt.plot(frame[feature_names[pair[0]]], frame[feature_names[pair[1]]], "o", label=species)
plt.xlabel(feature_names[pair[0]])
plt.ylabel(feature_names[pair[1]])
plt.legend()
plt.tight_layout()
Er det potensiale for å bruke et beslutningstre for å vurdere iris-arter her?
Lage beslutningstre med scikit-learn
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
# Laster inn Iris datasettet
iris = load_iris()
X, y = iris.data, iris.target
predictor_names = ["sepal length"]
# Initialiserer og trener et beslutningstre
dtree = DecisionTreeClassifier(max_depth=3, random_state=0) #
#Begrenser dybden for visualisering
dtree.fit(X, y)
# Visualiserer beslutningstreet
plt.figure(figsize=(12, 7))
plot_tree(dtree, feature_names=iris.feature_names, class_names=iris.target_names, filled=True)
plt.show()
Kommer dette beslutningstreet til å score bra eller dårlig?
Med test-train-split
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
iris = load_iris()
X, y = iris.data, iris.target
dtree = DecisionTreeClassifier(max_depth=3, random_state=0) #
dtree.fit(X, y)
dummy_prediction = dtree.predict([
[0.1, 0.1, 0.1, 0.1],
[0.5, 0.5, 0.5, 0.9]])
print(iris.target_names[dummy_prediction])
print(iris.feature_names)['setosa' 'versicolor']
['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
Note
Bruk from sklearn.model_selection sin train_test_split til å lage et beslutningstre med treningsdata og sjekk hvor god presisjon det har på valideringsdataene.
Visualisering av prediksjonen
X_2d = X[:, 2:4] # Sepal length and sepal width
# --- Visualisering av beslutningsgrenser og datapunkter ---
# 1. Lag et meshgrid for å plotte beslutningsregionene
x_min, x_max = X_2d[:, 0].min() - 1, X_2d[:, 0].max() + 1
y_min, y_max = X_2d[:, 1].min() - 1, X_2d[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
np.arange(y_min, y_max, 0.02))
# 2. Prediker klassen for hvert punkt i meshgridet
Z = dtree.predict(np.c_[xx.ravel(), yy.ravel(), xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
# 3. Plot beslutningsregionene som en contour plot
plt.figure(figsize=(10, 7))
plt.contourf(xx, yy, Z, alpha=0.4, cmap=plt.cm.RdYlBu) # Fyll regionene med farger
# 4. Plot de faktiske datapunktene oppå
colors = ['r', 'y', 'b'] # Farger for hver Iris klasse
for i, color in zip(range(len(target_names)), colors):
idx = np.where(y == i)
plt.scatter(X_2d[idx, 0], X_2d[idx, 1], c=color, label=target_names[i],
cmap=plt.cm.RdYlBu, edgecolor='black', s=20)
# 5. Legg til labels og tittel
plt.xlabel(feature_names[2])
plt.ylabel(feature_names[3])
plt.title('Beslutningstre beslutningsgrenser på Iris datasettet (2 features)')
plt.legend(loc='best', shadow=False, scatterpoints=1)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
plt.show()/var/folders/qn/3_cqp_vx25v4w6yrx68654q80000gp/T/ipykernel_61724/4173824724.py:21: UserWarning:
No data for colormapping provided via 'c'. Parameters 'cmap' will be ignored

Neste gang
- Random forest og boosting
- Hvordan lære noe av trær og skoger, feature importance, partial dependence.