xxxxxxxxxx
# STUDENT PERFORMANCE PREDICTION - Klasifikační úloha
xxxxxxxxxx
Martin Milton, Alexandr Gorobtsov
Martin Milton, Alexandr Gorobtsov
xxxxxxxxxx
## Kaggle dataset
> Předmětem tohoto projektu je analyzovat data a predikovat na základě několika atributů, včetně výsledků z čtení a psaní, jak dopadnou studenti z matematiky.
> Pro potřeby této práce byly přetvořeny data do strojově zpracovatelné podoby jako například normalizace číselných údajů do škály od 0 do 1, přetvořním některých datových parametrů do binární podoby jakou jsou například "parental level of education", "lunch" apod. Cílovou proměnou pro predikaci byl určen sloupec "math score".
https://www.kaggle.com/spscientist/students-performance-in-exams
Předmětem tohoto projektu je analyzovat data a predikovat na základě několika atributů, včetně výsledků z čtení a psaní, jak dopadnou studenti z matematiky. Pro potřeby této práce byly přetvořeny data do strojově zpracovatelné podoby jako například normalizace číselných údajů do škály od 0 do 1, přetvořním některých datových parametrů do binární podoby jakou jsou například "parental level of education", "lunch" apod. Cílovou proměnou pro predikaci byl určen sloupec "math score".
https://www.kaggle.com/spscientist/students-performance-in-exams
xxxxxxxxxx
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.dummy import DummyClassifier
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
import xgboost as xgb
from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import RepeatedStratifiedKFold
xxxxxxxxxx
> Pro potřeby tohoto projektu a zároveň v rámci našeho zdokonalení a také vyzkoušení nových možností jsme importovali funkce z celé řady knihoven, které nejsou standardně obsaženy v Python a nějakým způsobem zlepšují, usnadňují či zefektivňují práci a manipulaci s daty.
>>- Seaborn - pro vyzkoušení knihovny a jiného zápisu možnosti vykreslení grafů v rámci prozkoumávání dat;
>>- pipeline.make_pipeline - pro výrazné zjednodušení práce s vytvořením a spuštěním potenicálně několika kroků transformací nad datasetem, typicky škálování dat, doplnění chybějících dat a také případný encoding;
>>- train_test_split - jedná se o standard v ML při rozdělování datasetu na trénovací a testovací data, usnadní a zredukuje potřebné množství kódu pro tuto činnost;
>>- DummyClassifier - slouží v našem případě jako jednoduchý imputer hodnot pro vytvoření baseline modelu, výhodou je, že lze zvolit strategii doplňování hodnot, která se nejlépe hodí pro daný účel.
> Dále je importována celá řada funkcí převážně z knihovny sklearn, které byly využity a vyzkoušeny, některé jako modely strojového učení, i s využitím doplňujícíh parametrů.
>Pro potřeby této práce je zapotřebí také doinstalovat balíčky seaborn a xgboost.
>>- pip install seaborn
>>- pip install xgboost
Pro potřeby tohoto projektu a zároveň v rámci našeho zdokonalení a také vyzkoušení nových možností jsme importovali funkce z celé řady knihoven, které nejsou standardně obsaženy v Python a nějakým způsobem zlepšují, usnadňují či zefektivňují práci a manipulaci s daty.
- Seaborn - pro vyzkoušení knihovny a jiného zápisu možnosti vykreslení grafů v rámci prozkoumávání dat;
- pipeline.make_pipeline - pro výrazné zjednodušení práce s vytvořením a spuštěním potenicálně několika kroků transformací nad datasetem, typicky škálování dat, doplnění chybějících dat a také případný encoding;
- train_test_split - jedná se o standard v ML při rozdělování datasetu na trénovací a testovací data, usnadní a zredukuje potřebné množství kódu pro tuto činnost;
- DummyClassifier - slouží v našem případě jako jednoduchý imputer hodnot pro vytvoření baseline modelu, výhodou je, že lze zvolit strategii doplňování hodnot, která se nejlépe hodí pro daný účel.
Dále je importována celá řada funkcí převážně z knihovny sklearn, které byly využity a vyzkoušeny, některé jako modely strojového učení, i s využitím doplňujícíh parametrů.
Pro potřeby této práce je zapotřebí také doinstalovat balíčky seaborn a xgboost.
- pip install seaborn
- pip install xgboost
xxxxxxxxxx
## Zde si modifikujeme interactiveshell, pro output všech řádků
xxxxxxxxxx
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
xxxxxxxxxx
data = pd.read_csv("data/StudentsPerformance.csv")
xxxxxxxxxx
### Abychom se mohli rozhodnout, jak budeme s daty dále pracovat, zobrazíme si dataset a vlastnosti jeho sloupců pomocí několika následujících řádků kódu
xxxxxxxxxx
>* z následujícího lze vidět, že máme 1300 záznamů
>* vypozorovat lze rovněž, že máme 3 hlavní numerické sloupce, z nichž Math Score bude transformován a byl vybrán jako cílová proměnná
>* cílová proměnná bude převedena na kategoriální pro splnění podmínek
>* nechybí žádné hodnoty
>* také je vhodné si v této fázi prohlédnout všechna data v jednotlivých sloupcích a již promyslet, co se s nimi potenciálně bude dále dít
- z následujícího lze vidět, že máme 1300 záznamů
- vypozorovat lze rovněž, že máme 3 hlavní numerické sloupce, z nichž Math Score bude transformován a byl vybrán jako cílová proměnná
- cílová proměnná bude převedena na kategoriální pro splnění podmínek
- nechybí žádné hodnoty
- také je vhodné si v této fázi prohlédnout všechna data v jednotlivých sloupcích a již promyslet, co se s nimi potenciálně bude dále dít
xxxxxxxxxx
print(data.describe)
data.head(10)
xxxxxxxxxx
### Vytvoříme proměnnou scores
>- do této porměnné vkládáme pouze sloupce z dataframe "data", kde je obsažen řetězec score, což v našem případě zahrnuje konkrétně všechny 3 numerické sloupce obsahující výsledky testů z našeho datasetu
- do této porměnné vkládáme pouze sloupce z dataframe "data", kde je obsažen řetězec score, což v našem případě zahrnuje konkrétně všechny 3 numerické sloupce obsahující výsledky testů z našeho datasetu
xxxxxxxxxx
scores = data.columns[data.columns.str.contains('score')].tolist()
xxxxxxxxxx
### Vizualizace rozložení známek z testů
>- tohoto docílíme pro kontrolu a pro vyzkoušení graficky
>- pomocí "for cyklu" projdeme všechny 3 typy testů a adekvátně k nim zobrazíme histogramy rozložení jejich známek za využití knihovny seaborn
>- tak trochu navíc zároveň vypisujeme i šikmost a špičatost, kde šikmost zjišťuje, zda jsou hodnoty rozloženy okolo průměru symetricky, špičatost pak porovnává koncentraci hodnot blízko průměru a dále od něho
- tohoto docílíme pro kontrolu a pro vyzkoušení graficky
- pomocí "for cyklu" projdeme všechny 3 typy testů a adekvátně k nim zobrazíme histogramy rozložení jejich známek za využití knihovny seaborn
- tak trochu navíc zároveň vypisujeme i šikmost a špičatost, kde šikmost zjišťuje, zda jsou hodnoty rozloženy okolo průměru symetricky, špičatost pak porovnává koncentraci hodnot blízko průměru a dále od něho
xxxxxxxxxx
for score in scores:
sns.histplot(data[score], element='bars', kde=True)
plt.text(x=15, y=90, s=f"Šikmost: {round(data[score].skew(),2)}\nŠpičatost: {round(data[score].kurt(),2)}")
plt.show()
xxxxxxxxxx
## Transformace - změna názvů sloupců pro lepší manipulaci
xxxxxxxxxx
data.columns = ["gender","race","parents","lunch", "preparation","math","reading","writing"]
xxxxxxxxxx
data.head(5)
xxxxxxxxxx
## Zobrazíme si rozložení numerických sloupců i tímto způsobem pro vyzkoušení
xxxxxxxxxx
>- dost možná jednodušší a rychlejší zápis pro zobrazení rozložení známek, pak může být za využití hist(), kde zvolíme velikosti grafů a také bins, což je parametr pro nastavení velikosti skoků osy x v grafu
- dost možná jednodušší a rychlejší zápis pro zobrazení rozložení známek, pak může být za využití hist(), kde zvolíme velikosti grafů a také bins, což je parametr pro nastavení velikosti skoků osy x v grafu
xxxxxxxxxx
data.hist(figsize=(15,15), bins=20)
xxxxxxxxxx
>- žádná data naštěstí nechybí, takže není potřeba se tímto krokem zabývat
- žádná data naštěstí nechybí, takže není potřeba se tímto krokem zabývat
xxxxxxxxxx
data.isnull().sum()
xxxxxxxxxx
## V případě chybějících hodnot
>- postup by byl takový, že využijeme některé z funkcí typu imputer, které by nám umožnily na základě námi zvolené strategie (například nejčastější hodnota, průměr, apod.) automaticky doplnit chybějící hodnoty v datasetu
- postup by byl takový, že využijeme některé z funkcí typu imputer, které by nám umožnily na základě námi zvolené strategie (například nejčastější hodnota, průměr, apod.) automaticky doplnit chybějící hodnoty v datasetu
xxxxxxxxxx
### Ukázka vyřešení doplnění chybějících hodnot
xxxxxxxxxx
>- V případě, kdy bychom zjistili, že chybí hodnoty v datasetu, byly by prázdné / nan hodnoty doplněny například pomocí SimpleImputer a to například za využití mediánu a využití funkce "fillna()"
>- Nebo druhým způsobem tak, že si importujeme z knihovny sklearn SimpleImputer a pomocí něj dle zvolené strategie necháme doplnit chybějící hodnoty
- V případě, kdy bychom zjistili, že chybí hodnoty v datasetu, byly by prázdné / nan hodnoty doplněny například pomocí SimpleImputer a to například za využití mediánu a využití funkce "fillna()"
- Nebo druhým způsobem tak, že si importujeme z knihovny sklearn SimpleImputer a pomocí něj dle zvolené strategie necháme doplnit chybějící hodnoty
xxxxxxxxxx
Příklad 1: Využití manuálního doplnění "fillna()" a vypočítaného mediánu z adekvátního sloupce
Příklad 1: Využití manuálního doplnění "fillna()" a vypočítaného mediánu z adekvátního sloupce
xxxxxxxxxx
# data2 = pd.read_csv("data/StudentsPerformance.csv")
# impute_value = data2['reading score'].median()
# data2['reading score'] = data2['reading score'].fillna(impute_value)
xxxxxxxxxx
xxxxxxxxxx
Příklad 2: Upravení pipeline a využití SimpleImputer
Příklad 2: Upravení pipeline a využití SimpleImputer
xxxxxxxxxx
# from sklearn.pipeline import Pipeline
# from sklearn.impute import SimpleImputer
xxxxxxxxxx
#PŘÍKLAD PRO NUMERICKÉ PROMĚNNÉ
#num_pipeline = Pipeline([
# ("imputer", SimpleImputer(strategy="median")),
# ("scaler", StandardScaler()),
#])
xxxxxxxxxx
#PŘÍKLAD PRO KATEGORIÁLNÍ PROMĚNNÉ
#cat_pipeline = Pipeline ([
# ("imputer", SimpleImputer(strategy="most_frequent")),
# #přidáme ještě navíc imputer kategorialni pro doplneni chybejicich hodnot, zde je pouze jina strategie, MOST FREQUENT
# ("OneHot", OneHotEncoder())
#])
xxxxxxxxxx
>- Zde si zobrazíme za pomocí vyzualizace vztah mezi jednotlivými výsledky testů několika různými způsoby
>- nejdříve pomocí Scatter plotu zde vizualizujeme výsledky jednotlivých testů tak, že vždy na osy vkládáme dva rozdílné druhy testu
>- můžeme si všimnout, že například hned první vizualizace Math Score a Writing score naznačuje, že je vysoká korelace mezi výsledky testů z matematiky a ze psaní > to znamená, že data tímto způsobem prozkoumáme dále
- Zde si zobrazíme za pomocí vyzualizace vztah mezi jednotlivými výsledky testů několika různými způsoby
- nejdříve pomocí Scatter plotu zde vizualizujeme výsledky jednotlivých testů tak, že vždy na osy vkládáme dva rozdílné druhy testu
- můžeme si všimnout, že například hned první vizualizace Math Score a Writing score naznačuje, že je vysoká korelace mezi výsledky testů z matematiky a ze psaní > to znamená, že data tímto způsobem prozkoumáme dále
xxxxxxxxxx
fig, ax = plt.subplots()
ax.scatter(data["math"],data["writing"])
ax.set(xlabel='Math score', ylabel='Writing score')
plt.style.use('fast')
plt.title("Závislost skóre z matematiky na skóre z psaní")
plt.show()
xxxxxxxxxx
___
> Malá chyba v zobrazení scatterplotov – v 16 zobrazenie závislosti premennej
math samej na sebe, taktiež nesedí popis grafu
- zde se je účelem demonstrovat/vizualizovat podobu závislosti 1
___
Malá chyba v zobrazení scatterplotov – v 16 zobrazenie závislosti premennej math samej na sebe, taktiež nesedí popis grafu
xxxxxxxxxx
fig, ax = plt.subplots()
ax.scatter(data["math"],data["math"])
ax.set(xlabel='Math score', ylabel='Math score')
plt.style.use('fast')
plt.title("Závislost skóre z matematiky na skóre z matematiky") #ukázka korelace 1
plt.show()
xxxxxxxxxx
fig, ax = plt.subplots()
ax.scatter(data["math"],data["reading"])
ax.set(xlabel='Math score', ylabel='Reading score')
plt.style.use('fast')
plt.title("Závislost skóre z matematiky na skóre z čtení")
plt.show()
xxxxxxxxxx
### Zobrazení matice závislostí za využití Seaborn "pairplot"
>- toto je opět jednodušší zápis, jak docílit podobného výsledku
>- zde hned přehledně vidíme matici devíti grafů
- toto je opět jednodušší zápis, jak docílit podobného výsledku
- zde hned přehledně vidíme matici devíti grafů
xxxxxxxxxx
sns.pairplot(data=data)
xxxxxxxxxx
### Vytvoření nového sloupce
>- vytváříme nový sloupec math_grade, který bude sloužit pro nás stěžejní kategoriální cílovou proměnnou
>- nejdříve si do ní nahrajeme původní sloupec se skóre testu z matematiky
- vytváříme nový sloupec math_grade, který bude sloužit pro nás stěžejní kategoriální cílovou proměnnou
- nejdříve si do ní nahrajeme původní sloupec se skóre testu z matematiky
xxxxxxxxxx
data["math_grade"] = data["math"] #vytvoříme nový sloupec
xxxxxxxxxx
>- ověříme pro jistotu opět, že žádné hodnoty nechybějí
- ověříme pro jistotu opět, že žádné hodnoty nechybějí
xxxxxxxxxx
data.isnull().sum()
xxxxxxxxxx
>- definujeme si dvě proměnné "kategorie" a "grades"
>>- kategorie nám bude soužit pro definici rozmezí, kterými určíme jaké výsledky testu mají intervaly
>>- grades následně definuje 5 unikátních hodnot/názvů, které předtím stanoveným intervalů, následně přiřadíme
- definujeme si dvě proměnné "kategorie" a "grades"
- kategorie nám bude soužit pro definici rozmezí, kterými určíme jaké výsledky testu mají intervaly
- grades následně definuje 5 unikátních hodnot/názvů, které předtím stanoveným intervalů, následně přiřadíme
xxxxxxxxxx
kategorie = [-1, 49, 59, 74, 89, 100]
grades = ['failed', 'repeat', 'borderline', 'good', 'excellent']
xxxxxxxxxx
data.isnull().sum()
xxxxxxxxxx
>- zde je potřeba si dát pozor, jelikož při špatné definici intervalů známek se může stát, že nějaké hodnoty nebudou zahrnuty do intervalu a následně nám budou chybět některé kategoriální proměnné v sloupci "math_grade"
>- proto opět pro jistotu provedeme kontrolu na chybějící hodnoty
- zde je potřeba si dát pozor, jelikož při špatné definici intervalů známek se může stát, že nějaké hodnoty nebudou zahrnuty do intervalu a následně nám budou chybět některé kategoriální proměnné v sloupci "math_grade"
- proto opět pro jistotu provedeme kontrolu na chybějící hodnoty
xxxxxxxxxx
data[data["math_grade"].isna()]
xxxxxxxxxx
>- identifikován byl jeden záznam, který nebyl zahrnut a to z toho důvodu, že první interval začínal v definici od 0, tím, že nyní začíná od -1 je již vše v pořádku
- identifikován byl jeden záznam, který nebyl zahrnut a to z toho důvodu, že první interval začínal v definici od 0, tím, že nyní začíná od -1 je již vše v pořádku
xxxxxxxxxx
data.iloc[59]
xxxxxxxxxx
>- za využití funkce "pd.cut()" a výše definovaných proměnných "kategorie" a "grades" docílíme jednoduše toho, že se nám hodnoty slovního popisu známek správně u každého výsledku testu přiřadí
- za využití funkce "pd.cut()" a výše definovaných proměnných "kategorie" a "grades" docílíme jednoduše toho, že se nám hodnoty slovního popisu známek správně u každého výsledku testu přiřadí
xxxxxxxxxx
data['math_grade'] = pd.cut(data['math_grade'], kategorie, labels=grades)
xxxxxxxxxx
data.isnull().sum()
xxxxxxxxxx
data[data["math_grade"].isna()]
xxxxxxxxxx
print(data.dtypes)
xxxxxxxxxx
### Vypíšeme počty jednotlivých známek dle přidělených kategorií
>- vidíme, že nejvíce známek spadá do kategorie borderline, pak good atd.
>- nejméně studentů je excelentních
- vidíme, že nejvíce známek spadá do kategorie borderline, pak good atd.
- nejméně studentů je excelentních
xxxxxxxxxx
data["math_grade"].value_counts()
xxxxxxxxxx
data
xxxxxxxxxx
data.drop(["math"], axis=1, inplace=True)
data.head(3)
xxxxxxxxxx
### Převod hodnot vybraných vhodných sloupců do binárních hodnot
xxxxxxxxxx
data["gender"] = (data["gender"]=="male").astype(int) #isMale
xxxxxxxxxx
data["lunch"] = (data["lunch"]=="standard").astype(int) #isStandard
xxxxxxxxxx
data["preparation"] = (data["preparation"]=="completed").astype(int) #isCompleted
xxxxxxxxxx
data = data.rename(columns={"gender":"isMale","lunch":"lunchStandard","preparation":"preparationCompleted"})
xxxxxxxxxx
data.head(4)
xxxxxxxxxx
### Výpis unikátních hodnot z sloupců Parent a Race pro možnost jejich transformace do numerických hodnot, se kterými lze dále manipulovat
>- funkce unique() nám zde poslouží tak, že vypíše přesně to, co potřebujeme
>- již zde vidíme, že je vypsán dtype=object, ale i přesto pro kontrolu a ověření zbylých sloupců vypíšeme data.dtypes
- funkce unique() nám zde poslouží tak, že vypíše přesně to, co potřebujeme
- již zde vidíme, že je vypsán dtype=object, ale i přesto pro kontrolu a ověření zbylých sloupců vypíšeme data.dtypes
xxxxxxxxxx
data["parents"].unique()
data["race"].unique()
xxxxxxxxxx
print(data.dtypes)
xxxxxxxxxx
> * do proměnné categories si vkládáme všechny sloupce z dataframe "data", které jsou typu "object"
> * následně vypíšeme hlavičku a ověříme, že jsme novou proměnnou dobře naplnili
- do proměnné categories si vkládáme všechny sloupce z dataframe "data", které jsou typu "object"
- následně vypíšeme hlavičku a ověříme, že jsme novou proměnnou dobře naplnili
xxxxxxxxxx
categories = data.select_dtypes("object")
xxxxxxxxxx
categories.head(3)
xxxxxxxxxx
### Pro normalizaci zbylých kategoriálních sloupců zde využijeme funkce "get_dummies"
>- tato funkce nám převede všechny unikátní hodnoty sloupců "race" a "parents" na nové sloupce s binárními hodnotami
>- to nám umožní s daty dále pracovat v kontextu modelu strojového učení a zároveň to bude pro model lépe uchopitelné
- tato funkce nám převede všechny unikátní hodnoty sloupců "race" a "parents" na nové sloupce s binárními hodnotami
- to nám umožní s daty dále pracovat v kontextu modelu strojového učení a zároveň to bude pro model lépe uchopitelné
xxxxxxxxxx
dummy_data = pd.get_dummies(categories)
xxxxxxxxxx
dummy_data.head(3)
xxxxxxxxxx
>- Po výpisu máme mnoho sloupců, které se nám vytvořili z hodnot právě v sloupcích se dosaženým vzděláním rodičů a také ve sloupci rasové příslušnosti
- Po výpisu máme mnoho sloupců, které se nám vytvořili z hodnot právě v sloupcích se dosaženým vzděláním rodičů a také ve sloupci rasové příslušnosti
xxxxxxxxxx
dummy_data
xxxxxxxxxx
>- zde převedeme hodnoty výsledků testů z čtení a psaní na hodnoty mezi 0 a 1 jednoduše tím, že je vydělíme 100
- zde převedeme hodnoty výsledků testů z čtení a psaní na hodnoty mezi 0 a 1 jednoduše tím, že je vydělíme 100
xxxxxxxxxx
data["reading"]=data["reading"]/100
data["writing"]=data["writing"]/100
xxxxxxxxxx
data.head(3)
xxxxxxxxxx
### Vytvoření dataframe "data_prepared", který složíme pro naše potřeby
>- jednak z námi transformovaných sloupců isMale, lunchStandard, preparationCompleted a také testů
>- a následně k nim připojíme sloupce vygenerované funkcí get_dummies
>- využijeme pd.concat() a tím získáme proměnnou v žádaném stavu
- jednak z námi transformovaných sloupců isMale, lunchStandard, preparationCompleted a také testů
- a následně k nim připojíme sloupce vygenerované funkcí get_dummies
- využijeme pd.concat() a tím získáme proměnnou v žádaném stavu
xxxxxxxxxx
data_prepared = pd.concat([data["isMale"],data["lunchStandard"],data["preparationCompleted"],data["reading"],data["writing"],data["math_grade"],dummy_data], axis=1)
xxxxxxxxxx
___
> Data jsou z nějakého důvodu škálovaná jen při použití logistické regrese, ale
důvod k tomu není popsán
- data byla naškálována již původně manuálně a to transformacemi a také následně aritmetickými operacemi nad sloupci, následně pak byl v jedné instanci pro vyzkoušení a porovnání efektu použit také StandardScaler v rámci pipeline.
___
Data jsou z nějakého důvodu škálovaná jen při použití logistické regrese, ale důvod k tomu není popsán
xxxxxxxxxx
data_prepared
xxxxxxxxxx
print(data_prepared.dtypes)
xxxxxxxxxx
data_prepared = data_prepared.astype({'isMale':'float', 'lunchStandard':'float', 'preparationCompleted':'float',
'race_group A':'float', 'race_group B':'float', 'race_group C':'float', 'race_group D':'float',
'race_group E':'float', 'parents_associate\'s degree':'float',
'parents_bachelor\'s degree':'float', 'parents_high school':'float',
'parents_master\'s degree':'float', 'parents_some college':'float',
'parents_some high school':"float"})
xxxxxxxxxx
data_prepared = data_prepared.astype({'isMale':'int', 'lunchStandard':'int', 'preparationCompleted':'int',
'race_group A':'int', 'race_group B':'int', 'race_group C':'int', 'race_group D':'int',
'race_group E':'int', 'parents_associate\'s degree':'int',
'parents_bachelor\'s degree':'int', 'parents_high school':'int',
'parents_master\'s degree':'int', 'parents_some college':'int',
'parents_some high school':"int"})
data_prepared = data_prepared.astype({'isMale':'int', 'lunchStandard':'int', 'preparationCompleted':'int', 'race_group A':'int', 'race_group B':'int', 'race_group C':'int', 'race_group D':'int', 'race_group E':'int', 'parents_associate's degree':'int', 'parents_bachelor's degree':'int', 'parents_high school':'int', 'parents_master's degree':'int', 'parents_some college':'int', 'parents_some high school':"int"})
xxxxxxxxxx
print(data_prepared.dtypes)
xxxxxxxxxx
cols = ['isMale', 'lunchStandard', 'preparationCompleted', 'reading',
'writing', 'race_group A', 'race_group B', 'race_group C',
'race_group D', 'race_group E', 'parents_associate\'s degree',
'parents_bachelor\'s degree', 'parents_high school',
'parents_master\'s degree', 'parents_some college',
'parents_some high school']
data_prepared[cols] = data[cols].apply(pd.to_numeric, errors='coerce', axis=1)
cols = ['isMale', 'lunchStandard', 'preparationCompleted', 'reading', 'writing', 'race_group A', 'race_group B', 'race_group C', 'race_group D', 'race_group E', 'parents_associate's degree', 'parents_bachelor's degree', 'parents_high school', 'parents_master's degree', 'parents_some college', 'parents_some high school'] data_prepared[cols] = data[cols].apply(pd.to_numeric, errors='coerce', axis=1)
xxxxxxxxxx
data_prepared
xxxxxxxxxx
### - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
xxxxxxxxxx
## VYTVOŘENÍ TRÉNOVACÍCH A TESTOVACÍCH PROMĚNNÝCH A MODELU
xxxxxxxxxx
>- Pomocí funkce train_test_split byl dataset rozdělen na trénovací a testovací část s velikostí testovacího datasetu 30 % všech dat
>- Pro konzistenci a možnost replikace našich výsledků jsme také využili parametru random_state=42
- Pomocí funkce train_test_split byl dataset rozdělen na trénovací a testovací část s velikostí testovacího datasetu 30 % všech dat
- Pro konzistenci a možnost replikace našich výsledků jsme také využili parametru random_state=42
xxxxxxxxxx
predictors = ['isMale', 'lunchStandard', 'preparationCompleted', 'reading',
'writing', 'race_group A', 'race_group B', 'race_group C',
'race_group D', 'race_group E', 'parents_associate\'s degree',
'parents_bachelor\'s degree', 'parents_high school',
'parents_master\'s degree', 'parents_some college',
'parents_some high school']
xxxxxxxxxx
X = data_prepared[predictors].values
y = data_prepared['math_grade'].values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3,random_state=42)#random_state=42
xxxxxxxxxx
## BASELINE Model pro zjištění potenciální účinnosti našeho modelu
xxxxxxxxxx
>- Pro zajištění možné hranice toho, jak si stojí náš model - respektive pro porovnání s vyvíjeným modelem byl vytvořen tzv. Baseline model, který nám udává, jaké accuracy je dosaženo při zcela triviálním či náhodném dosazování predikcí/cílových hodnot
- Pro zajištění možné hranice toho, jak si stojí náš model - respektive pro porovnání s vyvíjeným modelem byl vytvořen tzv. Baseline model, který nám udává, jaké accuracy je dosaženo při zcela triviálním či náhodném dosazování predikcí/cílových hodnot
xxxxxxxxxx
>- Využito je funkce DummyClassifier z sklearn, který nám pomůže dle zvolené strategie právě hodnoty cílové proměnné přiřadit
- Využito je funkce DummyClassifier z sklearn, který nám pomůže dle zvolené strategie právě hodnoty cílové proměnné přiřadit
xxxxxxxxxx
>- První varianta DummyClassifier s parametrem strategy="stratified" nám "predikované" hodnoty rozřadí podle jejich poměrného zastoupení v původních datech. Lze vidět, že se dosahuje accuracy okolo 25 %
- První varianta DummyClassifier s parametrem strategy="stratified" nám "predikované" hodnoty rozřadí podle jejich poměrného zastoupení v původních datech. Lze vidět, že se dosahuje accuracy okolo 25 %
xxxxxxxxxx
dummy_clf = DummyClassifier(strategy="stratified")
dummy_clf.fit(X, y)
DummyClassifier(strategy='stratified')
dummy_clf.predict(X)
dummy_clf.score(X, y)
xxxxxxxxxx
DummyClassifier(strategy='stratified')
DummyClassifier(strategy='stratified')
array(['repeat', 'failed', 'repeat', ..., 'borderline', 'borderline',
'borderline'], dtype=object)
0.22230769230769232
DummyClassifier(strategy='stratified')
DummyClassifier(strategy='stratified')
array(['repeat', 'failed', 'repeat', ..., 'borderline', 'borderline', 'borderline'], dtype=object)
0.22230769230769232
xxxxxxxxxx
>- Druhá varianta DummyClassifier s parametrem strategy="uniform" nám "predikované" hodnoty rozřadí zcela náhodně. Lze vidět, že se dosahuje accuracy okolo 18 %.
- Druhá varianta DummyClassifier s parametrem strategy="uniform" nám "predikované" hodnoty rozřadí zcela náhodně. Lze vidět, že se dosahuje accuracy okolo 18 %.
xxxxxxxxxx
dummy_clf = DummyClassifier(strategy="uniform")
dummy_clf.fit(X, y)
DummyClassifier(strategy='uniform')
dummy_clf.predict(X)
dummy_clf.score(X, y)
xxxxxxxxxx
DummyClassifier(strategy='uniform')
DummyClassifier(strategy='uniform')
array(['good', 'repeat', 'excellent', ..., 'borderline', 'good',
'excellent'], dtype=object)
0.18076923076923077
DummyClassifier(strategy='uniform')
DummyClassifier(strategy='uniform')
array(['good', 'repeat', 'excellent', ..., 'borderline', 'good', 'excellent'], dtype=object)
0.18076923076923077
xxxxxxxxxx
>- V následující části byl vytvořen a využit model Logistické regrese pro predikci
- V následující části byl vytvořen a využit model Logistické regrese pro predikci
xxxxxxxxxx
>- Import funkce cross_val_score (v úvodu bloku) z knihovny sklearn, která bude následně využita pro corssvalidation našich modelů
- Import funkce cross_val_score (v úvodu bloku) z knihovny sklearn, která bude následně využita pro corssvalidation našich modelů
xxxxxxxxxx
>- Rovněž je již importováno LogisticRegression
>>- model_lr slouží jako naše proměnná pro samotnou logistickou regresi, zde je opět použit a bude i nadále někde používán random_state=42
>>- následně model trénujeme na trénovacích datech a to tak, že předáváme metodě fit subsety X_train a y_train
- Rovněž je již importováno LogisticRegression
- model_lr slouží jako naše proměnná pro samotnou logistickou regresi, zde je opět použit a bude i nadále někde používán random_state=42
- následně model trénujeme na trénovacích datech a to tak, že předáváme metodě fit subsety X_train a y_train
xxxxxxxxxx
model_lr = LogisticRegression(random_state=42)#random_state=42
model_lr.fit(X_train, y_train)
xxxxxxxxxx
>- Abychom ošetřili problematiku škálování dat, tak také importujeme
>>- make_pipeline
>>- StandardScaler
>- StandardScaler by nám zde měl pomoci a predikci také potenciálně ještě vylepšit
>- a sekundárně také vytvoříme predikci s pomocí pipeline, ve které využijeme zároveň funkce StandardScaler() a také LogisticRegression() a tím zajistíme provedení těchto kroků sekvenčně za sebou
- Abychom ošetřili problematiku škálování dat, tak také importujeme
- make_pipeline
- StandardScaler
- StandardScaler by nám zde měl pomoci a predikci také potenciálně ještě vylepšit
- a sekundárně také vytvoříme predikci s pomocí pipeline, ve které využijeme zároveň funkce StandardScaler() a také LogisticRegression() a tím zajistíme provedení těchto kroků sekvenčně za sebou
xxxxxxxxxx
pipe = make_pipeline(StandardScaler(), LogisticRegression())
pipe.fit(X_train, y_train)
xxxxxxxxxx
>- Po výstupu metody score() našeho modelu, který jsme spustili s pomocí pipeline nám vychází accuracy téměř 71 %
- Po výstupu metody score() našeho modelu, který jsme spustili s pomocí pipeline nám vychází accuracy téměř 71 %
xxxxxxxxxx
>- Z testovaných modelů se tento model Logistické regrese s využitím pipeline a StandardScaleru projevil, jako nejlepší pro náš účel
- Z testovaných modelů se tento model Logistické regrese s využitím pipeline a StandardScaleru projevil, jako nejlepší pro náš účel
xxxxxxxxxx
y_prob = pipe.score(X_test, y_test)
pipe.score(X_test, y_test)
xxxxxxxxxx
>- Po výstupu metody cross_val_score() našeho modelu, který jsme spustili s pomocí pipeline nám vychází accuracy téměř 68 %
- Po výstupu metody cross_val_score() našeho modelu, který jsme spustili s pomocí pipeline nám vychází accuracy téměř 68 %
xxxxxxxxxx
cross_val_score(pipe, X_train, y_train, cv=5).mean()
xxxxxxxxxx
>- Zde bez použití pipeline je accuracy o něco málo nižší na zaokrouhleně 64,4 %
- Zde bez použití pipeline je accuracy o něco málo nižší na zaokrouhleně 64,4 %
xxxxxxxxxx
model_lr.score(X_train,y_train)
xxxxxxxxxx
cross_val_score(model_lr, X_train, y_train, cv=5).mean()
xxxxxxxxxx
y_predict = model_lr.predict(X_test)
xxxxxxxxxx
>- Pro ukázku je možné si vypsat proměnné y_train a y_predict, které by například také bylo možné porovnat manuálně a ručně tak zjistit, jak si daný model vede
- Pro ukázku je možné si vypsat proměnné y_train a y_predict, které by například také bylo možné porovnat manuálně a ručně tak zjistit, jak si daný model vede
xxxxxxxxxx
y_train
xxxxxxxxxx
y_predict
xxxxxxxxxx
>- vyzkoušen byl také Random Forest, s pomocí kterého byly natrénovány data
>- u tohoto algoritmu je vhodné definovat parametr n_estimators, který nám udává počet stromů se kterými je pracováno
>- accuracy vychází hůře než u našeho nejlepšího modelu a to necelých 62 %
- vyzkoušen byl také Random Forest, s pomocí kterého byly natrénovány data
- u tohoto algoritmu je vhodné definovat parametr n_estimators, který nám udává počet stromů se kterými je pracováno
- accuracy vychází hůře než u našeho nejlepšího modelu a to necelých 62 %
xxxxxxxxxx
forest = RandomForestClassifier(n_estimators=100)
forest.fit(X_train, y_train)
xxxxxxxxxx
cross_val_score(forest, X_train, y_train, cv=15).mean()
xxxxxxxxxx
>- vyzkoušen byl také SVC - Support Vector Classification, s pomocí kterého byly natrénovány data
>- u tohoto algoritmu je vhodné definovat parametr kernel, default je "rbf" což v našem případě není nejvhodnější a lepší je specifikovat, že chceme využít kernel="linear"
>- accuracy vychází také hůře než u našeho nejlepšího modelu a to pouze okolo 65 %
- vyzkoušen byl také SVC - Support Vector Classification, s pomocí kterého byly natrénovány data
- u tohoto algoritmu je vhodné definovat parametr kernel, default je "rbf" což v našem případě není nejvhodnější a lepší je specifikovat, že chceme využít kernel="linear"
- accuracy vychází také hůře než u našeho nejlepšího modelu a to pouze okolo 65 %
xxxxxxxxxx
svc = SVC(kernel = "linear")
svc.fit(X_train, y_train)
xxxxxxxxxx
cross_val_score(svc, X_train, y_train, cv=15).mean()
xxxxxxxxxx
>- vyzkoušen byl také XGBoost, s pomocí kterého byly natrénovány data
>- zde jsme definovali verbosity=0, což omezí upozornění/warnings při výpisu
>- accuracy vychází také hůře než u našeho nejlepšího modelu a to pouze okolo 65 %
- vyzkoušen byl také XGBoost, s pomocí kterého byly natrénovány data
- zde jsme definovali verbosity=0, což omezí upozornění/warnings při výpisu
- accuracy vychází také hůře než u našeho nejlepšího modelu a to pouze okolo 65 %
xxxxxxxxxx
reg = xgb.XGBClassifier(verbosity=0, gamma=5, n_jobs=-1)
reg.fit(X_train,y_train)
xxxxxxxxxx
print(cross_val_score(reg, X_train, y_train, cv=5).mean())
xxxxxxxxxx
## GridSearch a Ladění hyperparametrů pro RandomForestClassifier
xxxxxxxxxx
>- využijeme GridSearch pro nalezení optimálních parametrů například pro algoritmus RandomForestClassifier
>- definujeme hyperparametry s definicí toho, jakých mají nabývat a jejich kombinace bude následně ověřena
>>- n_estimators
>>- max_depth
- využijeme GridSearch pro nalezení optimálních parametrů například pro algoritmus RandomForestClassifier
- definujeme hyperparametry s definicí toho, jakých mají nabývat a jejich kombinace bude následně ověřena
- n_estimators
- max_depth
xxxxxxxxxx
RandomForestClassifier().get_params().keys()
xxxxxxxxxx
n_estimators = [10, 100, 1000, 2000]
max_depth = [1, 5, 10, 20]
param_grid = dict(n_estimators=n_estimators, max_depth=max_depth)
xxxxxxxxxx
# rf představuje tedy základní model
rf = RandomForestClassifier(random_state=42)
# zde vytváříme zmíněný gridsearch
grid = GridSearchCV(estimator=rf,
param_grid=param_grid,
cv=3,
verbose=2,
n_jobs=-1)
grid_result = grid.fit(X_train, y_train)
xxxxxxxxxx
grid_result.best_estimator_
xxxxxxxxxx
grid_result.best_params_
xxxxxxxxxx
grid_result.best_score_
xxxxxxxxxx
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
print("%f (%f) with: %r" % (mean, stdev, param))
xxxxxxxxxx
>- výsledkem gridsearch u v této instanci a zároveň našim zjištěním je, že nejvhodnější parametry jsou při použití tohoto algoritmu:
>> {'max_depth': 5, 'n_estimators': 100}
>- s využitím těchto parametrů u RandomForestClassifier jsme dosáhli zlepšení skóre o zhruba 1 % accuracy oproti prostému modelu, který využívá default nastavení hyperparametrů
- výsledkem gridsearch u v této instanci a zároveň našim zjištěním je, že nejvhodnější parametry jsou při použití tohoto algoritmu:
{'max_depth': 5, 'n_estimators': 100}
- s využitím těchto parametrů u RandomForestClassifier jsme dosáhli zlepšení skóre o zhruba 1 % accuracy oproti prostému modelu, který využívá default nastavení hyperparametrů
xxxxxxxxxx
## Využití GridSearch a hledání klíčových hyperparametrů pro model Logistické regrese
xxxxxxxxxx
LogisticRegression().get_params().keys()
xxxxxxxxxx
scaler = StandardScaler()
xxxxxxxxxx
X_train_scaled = scaler.fit_transform(X_train.astype(np.float64))
xxxxxxxxxx
model = LogisticRegression()
solvers = ['newton-cg', 'lbfgs', 'liblinear', 'sag', 'saga']
penalty = ['l2','l1','none']
c_values = [100, 10, 1.0, 0.1, 0.01]
xxxxxxxxxx
grid = dict(solver=solvers,penalty=penalty,C=c_values)
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3)
grid_search = GridSearchCV(estimator=model, param_grid=grid, n_jobs=-1, cv=cv, scoring='accuracy',error_score=0)
grid_result = grid_search.fit(X_train_scaled, y_train)
xxxxxxxxxx
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
print("%f (%f) with: %r" % (mean, stdev, param))
xxxxxxxxxx
>- výsledkem gridsearch a zároveň našim zjištěním je, že nejvhodnější parametry jsou při použití tohoto algoritmu:
>> {'C': 1.0, 'penalty': 'l2', 'solver': 'saga'}
>- s využitím těchto parametrů jsme dosáhli zlepšení skóre o zhruba 6-8 % oproti prostému modelu, který využívá default nastavení hyperparametrů
- výsledkem gridsearch a zároveň našim zjištěním je, že nejvhodnější parametry jsou při použití tohoto algoritmu:
{'C': 1.0, 'penalty': 'l2', 'solver': 'saga'}
- s využitím těchto parametrů jsme dosáhli zlepšení skóre o zhruba 6-8 % oproti prostému modelu, který využívá default nastavení hyperparametrů
xxxxxxxxxx
### - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
xxxxxxxxxx
## Závěr
> Po evaluaci veškerých využitých metod jsme došli k tomu, že nejlepší metodou predikce známek z matematiky studentů pro tento dataset je využití logistické regrese v kombinaci se standard scaler. Výsledkem tohoto modelu jsou predikce se správností 70 %. V porovnání s výsledkem baseline modelu je to 2,5 násobné zlepšení.
Po evaluaci veškerých využitých metod jsme došli k tomu, že nejlepší metodou predikce známek z matematiky studentů pro tento dataset je využití logistické regrese v kombinaci se standard scaler. Výsledkem tohoto modelu jsou predikce se správností 70 %. V porovnání s výsledkem baseline modelu je to 2,5 násobné zlepšení.
xxxxxxxxxx
___
> ### Reakce oponentura
- GridSearch byl na vyzkoušení jeho efektu pro splnění požadovaného časového a náročnostního limitu využit pouze u nejvíce potentních modelů.
- Evaluace byla provedena opravdu za pomoci správnosti a i přesto, že další metriky byly uvažovány a hodnoceny, byly pro jednoduchost vyřazeny z notebooku.
- Určité prvky figurující v samotné práci s daty či při vyhodnocování modelů byly záměrně navrženy za účelem demonstrace nějaké skutečnosti a našeho myšlenkového pochodu, tudíž se neobjevuje vše všude ve všech instancíh, ale na vybraných místech.
- Jak již bylo zmíněno, hodně prvků v práci bylo provedeno za účelem edukativním a za účelem splnění všech bodů zadání, což se nám podařilo, v realitě a i v produkčním projektu by se samozřejmě postup byl jiný, např.
>- zmíněné využití GridSearch s naprosto všemi relevantními parametry na naprosto všechny modely
>- vizualizace naprosto všech vyhodnocovaních metrik i přesto, že nemusí být relevantní
>- využití random-state pro zcela všechny funkce, které jej podporují atd.
___
> Malá chyba v zobrazení scatterplotov – v 16 zobrazenie závislosti premennej
math samej na sebe, taktiež nesedí popis grafu
- zde se je účelem demonstrovat/vizualizovat podobu závislosti 1
___
___
> Data jsou z nějakého důvodu škálovaná jen při použití logistické regrese, ale
důvod k tomu není popsán
- data byla naškálována již původně manuálně a to transformacemi a také následně aritmetickými operacemi nad sloupci, následně pak byl v jedné instanci pro vyzkoušení a porovnání efektu použit také StandardScaler v rámci pipeline.
___
> ### Lessons learned
- V rámci zpracování projektu jsme narazili na mnoho úskalí, která se nám podařilo překonat a posunulo nás to dál v oblastech analýzy dat a také modelů strojového učení.
- Dle vyzkoušených modelů a také dle provedeného research nyní máme přehled o modelech, jejich potenciálnímu využití a také o typických krocích, které je dobré provádět při práci s daty, modelování a také vyhodnocení.
- Vyhledání chybného záznamu (ohraničení kategorií známek)
- Práce s daty a význam jednotlivých transformací
- Důležitost porozumění datům
- Vysoký význam správné interpretace výsledků a také využití správných metrik pro hodnocení samotných výsledků je stěžejní
> ### Poznámky
- Scaler a výsledky
- Převedení dat do binární podoby, složitost výpočtů a s tím spojené další problémy
>- růst doby výpočtů
>- nutnost výběru vhodného postupu a metod hned od začátku
>- orientace v postupech a průběhu celého datového projektu
>- dosažení dobrých výsledků v adekvátním/akceptovatelném čase
- Exponenciální růst doby výpočtu s narůstajícím počtem parametrů a druhů modelů například v GridSearch
___
Reakce oponentura¶
GridSearch byl na vyzkoušení jeho efektu pro splnění požadovaného časového a náročnostního limitu využit pouze u nejvíce potentních modelů.
Evaluace byla provedena opravdu za pomoci správnosti a i přesto, že další metriky byly uvažovány a hodnoceny, byly pro jednoduchost vyřazeny z notebooku.
Určité prvky figurující v samotné práci s daty či při vyhodnocování modelů byly záměrně navrženy za účelem demonstrace nějaké skutečnosti a našeho myšlenkového pochodu, tudíž se neobjevuje vše všude ve všech instancíh, ale na vybraných místech.
Jak již bylo zmíněno, hodně prvků v práci bylo provedeno za účelem edukativním a za účelem splnění všech bodů zadání, což se nám podařilo, v realitě a i v produkčním projektu by se samozřejmě postup byl jiný, např.
- zmíněné využití GridSearch s naprosto všemi relevantními parametry na naprosto všechny modely
- vizualizace naprosto všech vyhodnocovaních metrik i přesto, že nemusí být relevantní
- využití random-state pro zcela všechny funkce, které jej podporují atd.
Malá chyba v zobrazení scatterplotov – v 16 zobrazenie závislosti premennej math samej na sebe, taktiež nesedí popis grafu
Data jsou z nějakého důvodu škálovaná jen při použití logistické regrese, ale důvod k tomu není popsán
Lessons learned¶
V rámci zpracování projektu jsme narazili na mnoho úskalí, která se nám podařilo překonat a posunulo nás to dál v oblastech analýzy dat a také modelů strojového učení.
Dle vyzkoušených modelů a také dle provedeného research nyní máme přehled o modelech, jejich potenciálnímu využití a také o typických krocích, které je dobré provádět při práci s daty, modelování a také vyhodnocení.
Vyhledání chybného záznamu (ohraničení kategorií známek)
Práce s daty a význam jednotlivých transformací
Důležitost porozumění datům
Vysoký význam správné interpretace výsledků a také využití správných metrik pro hodnocení samotných výsledků je stěžejní
Poznámky¶
Scaler a výsledky
Převedení dat do binární podoby, složitost výpočtů a s tím spojené další problémy
- růst doby výpočtů
- nutnost výběru vhodného postupu a metod hned od začátku
- orientace v postupech a průběhu celého datového projektu
- dosažení dobrých výsledků v adekvátním/akceptovatelném čase
Exponenciální růst doby výpočtu s narůstajícím počtem parametrů a druhů modelů například v GridSearch
xxxxxxxxxx