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.
# připravme data
y = data["Species"] == "Perch"
y = y.astype(int)
X = data.drop(columns=["ID", "Species"])
# 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)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train_raw)
X_test = scaler.transform(X_test_raw)
# vezměme klasifikátor
# můžeš změnit
from sklearn.tree import DecisionTreeClassifier
model = DecisionTreeClassifier()
# natrénujte
model.fit(X_train, y_train)
# 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.
# zkus naučit různé modely a vyber nejlepší
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.metrics import confusion_matrix
# 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')
from sklearn.metrics import f1_score
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
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)
# 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} %")