Nauč se Python > Kurzy > Datový kurz PyLadies > Strojové učení - knihovna scikit-learn, celkové workflow > Knihovna Scikit-learn

Knihovna Scikit-learn, přehled užitečných funkcí

V předešlé hodině jsme se vyhýbali programování, jak jen se to dalo. Teď už si ale chceš také sama vše vyzkoušet. Abys mohla úlohu rozmyšlenou v domácím úkolu naprogramovat, projdeme si nejdůležitější funkce, které budeš potřebovat.

Především budeme používat knihovnu Sciki-learn a samozrejmě také pandas. Potřebné věci projdeme na příkladu.

In [1]:
import pandas as pd
import numpy as np
np.random.seed(42)

Načtení a příprava dat

☑ výběr vstupních proměnných ☑ rozdělení na trénovací a testovací data ☑ chybějící hodnoty ☑ kategorické hodnoty ☑ přeškálování / normování hodnot

Na začátku vždy bude potřeba připravit data. Čištění dat a použití knihovny pandas už bys měla ovládat, zaměříme se jen na věci, které jsou specifické pro strojové učení.

Načíst data tedy umíš.

In [2]:
df_platy = pd.read_csv("static/salaries.csv", index_col=0)
df_platy.sample(10)
Out[2]:
rank discipline yrs.since.phd yrs.service sex salary
66 AssocProf B 9 8 Male 100522
115 Prof A 12 0 Female 105000
17 Prof B 19 20 Male 101000
142 AssocProf A 15 10 Male 81500
157 AssocProf B 12 18 Male 113341
127 Prof A 28 26 Male 155500
141 AssocProf A 14 8 Male 100102
31 Prof B 20 4 Male 132261
19 Prof A 37 23 Male 124750
168 Prof B 18 19 Male 130664

Pro predikci použijeme jako příznaky rank, discipline, yrs.since.phd, yrs.service a sex, predikovat budeme hodnotu salary.

Pro učení potřebujeme všechny hondoty převést na čísla (float). Pokud by data obsahovala chybějící hodnoty, nejjednodušší řešení je takové řádky zahodit. (Bonus: pokud bys měla data s větším množstvím chybějících hodnot, podívej se na možnosti sklearn.impute)

Důležité je vypořádat se s kategorickými hodnotami. Sloupce obsahující hodnoty typu Boolean nebo dvě hodnoty (např. muž/žena), lze snadno převést na hodnoty $[0,1]$.

In [3]:
df_platy = df_platy.replace({"Male": 0, "Female": 1})
df_platy.sample(10)
Out[3]:
rank discipline yrs.since.phd yrs.service sex salary
10 Prof B 18 18 1 129000
43 Prof B 40 27 0 101299
34 AsstProf B 4 2 0 80225
118 Prof A 39 36 0 117515
5 Prof B 40 41 0 141500
150 AsstProf B 4 3 0 95079
56 AssocProf B 14 5 0 83900
15 Prof B 20 18 0 104800
170 Prof B 25 18 0 181257
114 Prof A 37 37 0 104279

Pro kategorické proměnné s více možnostmi použijeme tzv. onehot encoding.

Např. sloupec rank obsahuje hodnoty Prof, AsstProf a AssocProf. K zakódování pomocí onehot encoding potřebujeme tři sloupce:

Původní hodnota Kód
Prof 1 0 0
AsstProf 0 1 0
AssocProf 0 0 1

Knihovna Scikitlearn nabízí sklearn.preprocessing.OneHotEncoder, při práci s pandas však můžeme použít rovnou metodu get_dummies. (Pozn. dummies proto, že nám přibudou pomocné proměnné (sloupce), které se označují jako dummy variables.)

In [4]:
df_platy = pd.get_dummies(df_platy)
df_platy
Out[4]:
yrs.since.phd yrs.service sex salary rank_AssocProf rank_AsstProf rank_Prof discipline_A discipline_B
1 19 18 0 139750 0 0 1 0 1
2 20 16 0 173200 0 0 1 0 1
3 4 3 0 79750 0 1 0 0 1
4 45 39 0 115000 0 0 1 0 1
5 40 41 0 141500 0 0 1 0 1
... ... ... ... ... ... ... ... ... ...
194 19 19 0 86250 1 0 0 0 1
195 48 53 0 90000 1 0 0 0 1
196 9 7 0 113600 1 0 0 0 1
197 4 4 0 92700 0 1 0 0 1
198 4 4 0 92000 0 1 0 0 1

198 rows × 9 columns

Poslední krok předzpracování bývá přeškálování hodnot. Je to už ale zásah do dat, který využívá hodnot celé množiny. Proto by nebylo fér používat pro něj i část, která bude později sloužit na testování. Je nejvyšší čas, oddělit testovací množinu.

Vytvoření trénovací a testovací množiny

V teorii strojového učení se vstupy modelu (příznaky, vstupní proměnné) typicky označují písmenem X a výstupy písmenem y. Řada programátorů toto používá i k označování proměnných v kódu. X představuje matici (neboli tabulku), kde každý řádek odpovídá jednomu datovému vzorku a každý sloupec jednomu příznaku (vstupní proměnné). y je vektor, neboli jeden sloupec s odezvou.

Na vyzobnutí odezvy může hodit metoda pop. Její nevýhodou je ale nemožnost opakovaně spouštět buňku.

In [5]:
#y = df_platy.pop("salary")
#X = df_platy 
y = df_platy["salary"]
X = df_platy.drop(columns=["salary"])

print(X.columns)
print(y.name)
Index(['yrs.since.phd', 'yrs.service', 'sex', 'rank_AssocProf',
       'rank_AsstProf', 'rank_Prof', 'discipline_A', 'discipline_B'],
      dtype='object')
salary
In [6]:
X.head()
Out[6]:
yrs.since.phd yrs.service sex rank_AssocProf rank_AsstProf rank_Prof discipline_A discipline_B
1 19 18 0 0 0 1 0 1
2 20 16 0 0 0 1 0 1
3 4 3 0 0 1 0 0 1
4 45 39 0 0 0 1 0 1
5 40 41 0 0 0 1 0 1
In [7]:
y.head()
Out[7]:
1    139750
2    173200
3     79750
4    115000
5    141500
Name: salary, dtype: int64

Zbývá data rozdělit na trénovací a testovací. K tomu slouží metoda train_test_split. Data nám rozdělí náhodně na trénovací a testovací sadu. Velikost testovací množiny můžeme specifikovat parametrem test_size, jeho defaultní hodnota je 0.25, t. j. 25%.

In [8]:
from sklearn.model_selection import train_test_split 

X_train, X_test, y_train, y_test = train_test_split(X, y)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

Zpátky ke škálování

Přeškálování není vždy nutné, ale některým modelům to může pomoci. Využijeme StandardScaler.

StandardScaler nám hodnoty přeškáluje, aby zhruba odpovídaly normálnímu rozdělení. Některé algoritmy to předpokládají. Může se pak např. stát, že příznak (sloupeček), která má výrazně větší rozptyl než ostatní, je brán jako významnější.

In [9]:
from sklearn.preprocessing import StandardScaler 

priznaky_ke_konverzi = ["yrs.since.phd", "yrs.service"]

transformace = StandardScaler()

# just to get rid of SettingWithCopyWarning 
X_train = X_train.copy()
X_test = X_test.copy()

X_train.loc[:, priznaky_ke_konverzi] = transformace.fit_transform(X_train[priznaky_ke_konverzi])
X_test.loc[:, priznaky_ke_konverzi] = transformace.transform(X_test[priznaky_ke_konverzi])

X_train.sample(10)
Out[9]:
yrs.since.phd yrs.service sex rank_AssocProf rank_AsstProf rank_Prof discipline_A discipline_B
61 -0.821632 -0.603607 0 1 0 0 0 1
20 1.582294 1.680759 1 0 0 1 1 0
192 1.902818 0.538576 0 0 0 1 0 1
139 -0.741501 -0.685192 0 1 0 0 1 0
10 -0.100454 0.212238 1 0 0 1 0 1
44 1.502164 1.843928 0 0 0 1 0 1
183 -0.901763 -0.603607 0 1 0 0 0 1
36 -1.142155 -1.256283 1 0 1 0 0 1
149 1.341902 0.864914 1 0 0 1 0 1
123 0.380331 0.538576 0 0 0 1 1 0

Modely

Můžeme přejít k samotnému učení. Vybereme si model. Přehled modelů najdeš v sekci Supervised learnig.

Na regresi můžeš použít:

  • LinearRegression

  • Lasso

    • hyperparametry:
      • alpha, float, default=1.0
  • SVR

    • hyperparametry:
      • kernel, default rbf, one of ‘linear’, ‘poly’, ‘rbf’, ‘sigmoid’
      • C, float, optional (default=1.0)

Na klasifikační úlohy (ke kterým se dostaneme v příští hodině) využiješ:

Vytvoříme instanci vybraného modelu (jde nám teď jen o způsob použití knihovny, vezmeme nejjednodušší lineární regresi):

In [10]:
from sklearn.linear_model import LinearRegression

model = LinearRegression()

Trénování

Model natrénujeme na trénovací množině:

In [11]:
model.fit(X_train, y_train)
Out[11]:
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False)

Predikce

Natrénovaný model typicky chceme použít k ohodnocení nějakých nových datových vzorků, k tomu máme metodu predict. Zavolejme ji jak na trénovací, tak na testovací data.

In [12]:
train_predikce = model.predict(X_train)
test_predikce = model.predict(X_test)

Vypišme si prvních deset testovacích vzorků a jejich predikce:

In [13]:
print(f"skutečný plat    predicke platu     ")
for i in range(10):
    print(f"{y_test.iloc[i]:>10.2f}         {test_predikce[i]:>10.2f}")
skutečný plat    predicke platu     
  93418.00           97738.57
 128148.00          135101.30
 101299.00          131878.40
  82100.00           82375.82
  73000.00           67784.49
  86100.00           84455.33
 168635.00          115889.21
 113278.00          115355.42
 120806.00          118690.71
 150743.00          131604.85

Evaluace modelu

Můžeme využít funkci score, která nám vrátí hodnotu $R^2$ metriky:

In [14]:
print("R2 na trénovací množině: ", model.score(X_train, y_train))
print("R2 na testovací množině: ", model.score(X_test, y_test))
R2 na trénovací množině:  0.5404681269886975
R2 na testovací množině:  0.49459922918324023

Funkce pro všechny možné metriky najdeš v sklearn.metrics. (nyní nás zajímají regresní metriky)

In [15]:
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score

MAE_train = mean_absolute_error(y_train, train_predikce)
MAE_test  = mean_absolute_error(y_test, test_predikce)
MSE_train = mean_squared_error(y_train, train_predikce) 
MSE_test = mean_squared_error(y_test, test_predikce)
R2_train = r2_score(y_train, train_predikce)
R2_test = r2_score(y_test, test_predikce)

print("    Trénovací data  Testovací data")
print(f"MSE {MSE_train:>14.3f}  {MSE_test:>14.3f}")
print(f"MAE {MAE_train:>14.3f}  {MAE_test:>14.3f}")
print(f"R2  {R2_train:>14.3f}  {R2_test:>14.3f}")
    Trénovací data  Testovací data
MSE  390137429.883   371972816.562
MAE      14194.085       13919.789
R2           0.540           0.495

Uložení modelu

Někdy si potřebujeme naučený model uchovat na další použití. Model lze uložit do souboru a zase načíst pomocí pickle. Kujme pikle:

In [16]:
import pickle 

with open("model.pickle", "wb") as soubor:
    pickle.dump(model, soubor)


with open("model.pickle", "rb") as soubor:
    staronovy_model = pickle.load(soubor)

staronovy_model.score(X_test, y_test)
Out[16]:
0.49459922918324023

Bonusy:

  • volba vhodného modelu a jeho hyper-parametrů se skrývá pod klíčovým slovem model selection. Knihovna Scikit-learn obsahuje různé pomůcky k ulehčení toho výběru. Přesahuje to ale rámec tohoto kurzu, narazíš-li na to toho téma při samostudiu, pročti si sklear.model_selection.

  • v příkladu výše jsme použili různé transformace nad daty a pak teprve tvorbu modelu. Až budeš v těchto věcech zběhlejší, bude se ti hodit propojit tyto věci dohromady. K tomu slouží tzv. pipeline.


Toto je stránka lekce z kurzu, který probíhá nebo proběhl naživo s instruktorem.