Zatím jsme se zabývali jen regresními úlohami. Učení s učitelem ale zahrnuje dvě hlavní skupiny úloh - regresní úlohy a klasifikační úlohy.
Zatímco u regresních úloh je výstupem modelu spojitá hodnota (float), v klasifikačních úlohách představuje výstup modelu indikátor třídy (label).
Držme se našeho rybího trhu a ukažme si to na příkladu. Úloha predikovat váhu ryby byla regresní úloha, predikovali jsme spojitou hodnotu. Pokud budeme chtít predikovat druh ryby (Perch - okoun, Roach - plotice, Pike - štika, ...), jedná se o predikci kategorické hodnoty, tedy o klasifikaci.
Klasifikační úlohy mají trochu jiné vlastnosti a logiku, než úlohy regresní, proto existují modely přímo určené na takové úlohy. Říká se jim klasifikátory.
Zkusíme se ale nejdřív podívat na úlohu klasifikace z pohledu, který už známe, tedy z pohledu krajiny.
# načeteme si data
import pandas as pd
import numpy as np
np.random.seed(2020) # nastavení náhodného klasifikátoru
data = pd.read_csv("static/fish_data.csv", index_col=0)
data
Nejčastějším druhem ryby je Perch (okoun). Naším cílem je vytvořit klasifikátor, který pro zadané míry (váha, různé délky a šířky) vrátí informaci, zda se jedná o okouna nebo jiný druh. (Máme tedy pro jednoduchost jen dvě třídy, Perch a ostatní.)
Uměla bys tuto úlohu napasovat na krajinu? Co by mohly být souřadnice a co nadmořská výška?
Pokud ses úspěšně poprala s předchozím dotazem, můžeš na klasifikaci použít některý z regresních modelů (ano, asi to nebude ideální, když jde o klasifikaci, ale zkusme nejdříve to, co již umíme). Co ale bude hodnota odezvy a jak ji budeme interpretovat?
Přinášíme opět nějakou základní nabídku klasifikačních modelů:
Vyberete si jeden model a zkuste natrénovat na ryby.
Nejprve připravíme data obdobně jako v minulé hodině. Jako sloupeček odezvy použijeme True
pro okouny a False
pro ostatní ryby. Sloupeček Species
pak už nebudeme potřebovat, stejně tak můžeme vypustit sloupeček ID
.
# připravme data
y = data["Species"] == "Perch"
y = y.astype(int)
X = data.drop(columns=["ID", "Species"])
Dalším krokem je rozdělení na trénovací a validační data. Nezapomeňme na stratifikaci.
# rozdělme na trénovací a validační množinu
from sklearn.model_selection import train_test_split
X_train_raw, X_test_raw, y_train, y_test = train_test_split(X, y, stratify=y)
Data přeškálujeme.
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train_raw)
X_test = scaler.transform(X_test_raw)
Jako model zvolíme rozhodovací strom. Neboj se zkusit jiný klasifikátor dle své volby.
# vezměme klasifikátor
# můžeš změnit
from sklearn.tree import DecisionTreeClassifier
model = DecisionTreeClassifier()
# natrénujte
model.fit(X_train, y_train);
Máme natrénovaný model, jdeme se podívat, jak funguje na validačních datech.
# ohodnoťme validační množinu
pred = model.predict(X_test)
print("Skutečná třída: Predikce:")
for true, predicted in zip(y_test, pred):
print(f"{true:<15} {predicted:<10} {'OK' if true == predicted else 'X'}")
print(f"Počet chyb: {sum(y_test != pred)}")
print(f"Úspěšnost: {100*sum(y_test == pred)/len(y_test):.2f} %")
Úspěšnost není úplně špatná, poznat druh ryby podle rozměrů není jendoduchá úloha.
Představ si ale, že budeme mít v datovou množinu se 100 rybami, 95 z nich bude okounů (typu Perch). Bude ti klasifikátor, který bude mít toto procento úspěšnosti (stejné jako vyšlo nám), připadat dobrý nebo ne? Proč?
Nejprve projdeme klasifikační metriky. Pokud studuješ sama, nastuduj si kapitolu o klasifikačních metrikách a pak se vrať k tomuto cvičení.
Vyber si metriku pro naši úlohu a zkus najít, co nejlepší klasifikátor. Pak si načti testovací množinu a podívej se, jaké tvůj klasifikátor dává výsledky.
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
# zkus naučit různé modely a vyber nejlepší
models = {}
# KNeigbors
for N in 1, 3, 5, 7:
models[("nearest neighbors", N)] = KNeighborsClassifier(n_neighbors=N, weights="distance")
# tree
for d in range(3, 20):
models[("tree", d)] = DecisionTreeClassifier(max_depth=d, class_weight='balanced')
# random forest
for N in range(1, 100):
models[("random forest", N)] = RandomForestClassifier(n_estimators=N, class_weight='balanced')
# SVC
for C in range(-2, 10):
models[("SVC", 10**C)] = SVC(C=10**C, class_weight='balanced')
Vytvořili jsme si slušnou zásobu modelů, uložili jsme je do slovníku. Každý model máme pro různé hodnoty příslušného hyper-parametru.
models
Obdobně jako v minulé hodině vytvoříme funci, která ohodnotí model a vrátí hodnoty vybrané metriky na trénovací a validační množině. Hodnoty vrací ve slovníku (což nám pak umožní snadnější vytvoření dataframu s výsledky).
from sklearn.metrics import f1_score
from sklearn.metrics import confusion_matrix
def train_and_eval(X_train, X_test, y_train, y_test, model):
model.fit(X_train, y_train)
y_pred_test = model.predict(X_test)
y_pred_train = model.predict(X_train)
return {"train": f1_score(y_train, y_pred_train), # metriku můžeš vyměnit za nějakou svojí
"test": f1_score(y_test, y_pred_test)
}
results = []
for name, model in models.items():
res = train_and_eval(X_train, X_test, y_train, y_test, model)
res["model"] = name[0]
res["param"] = name[1]
results.append(res)
df_results = pd.DataFrame(results)
df_results
Závislost úspěsnosti modelu (dle zvolené metriky) na hodnotě příslušného hyperparametru si zobrazíme v grafu.
import seaborn as sns
import matplotlib.pyplot as plt
def zobraz_model(model_name, ax, logx=False):
sns.lineplot(x="param", y="train", data=df_results[df_results["model"]==model_name], label="train", ax=ax)
sns.lineplot(x="param", y="test", data=df_results[df_results["model"]==model_name], label="test", ax=ax)
ax.set_title(model_name.capitalize())
if logx:
ax.set(xscale="log")
fig, axs = plt.subplots(ncols=4, figsize=(16,4))
zobraz_model("nearest neighbors", axs[0])
zobraz_model("tree", axs[1])
zobraz_model("random forest", axs[2])
zobraz_model("SVC", axs[3], logx=True)
Vyber si model, který se na validační množině jeví jako nejlepší. Vyzkoušej jej na testovací data.
# načtení data
test_data = pd.read_csv("static/fish_data_test.csv", index_col=0)
y_real_test = test_data["Species"] == "Perch"
y_real_test = y_real_test.astype(int)
X_real_test = test_data.drop(columns=["ID", "Species"])
X_real_test = scaler.transform(X_real_test)
# predikce
model = models[("SVC", 10**4)]
test_pred = model.predict(X_real_test)
# zkus přidat zvolenou metriku
print(f"Skutečná třída: Predikce:")
for true, predicted in zip(y_real_test, test_pred):
print(f"{true:<15} {predicted:<10} {'OK' if true == predicted else 'X'}")
print(f"Počet chyb: {sum(y_real_test != test_pred)}")
print(f"Úspěšnost: {100*sum(y_real_test == test_pred)/len(y_real_test):.2f} %")