STUDENT PERFORMANCE PREDICTION - Klasifikační úloha

Martin Milton, Alexandr Gorobtsov

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

Importy základních knihoven

In [1]:

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

Zde si modifikujeme interactiveshell, pro output všech řádků

In [2]:

Nahrání csv, data insights a exploration

In [3]:

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

  • 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
In [4]:
<bound method NDFrame.describe of       gender race/ethnicity parental level of education         lunch  \
0     female        group B           bachelor's degree      standard   
1     female        group C                some college      standard   
2     female        group B             master's degree      standard   
3       male        group A          associate's degree  free/reduced   
4       male        group C                some college      standard   
...      ...            ...                         ...           ...   
1295    male        group B          associate's degree  free/reduced   
1296    male        group C            some high school  free/reduced   
1297  female        group C                 high school      standard   
1298    male        group B           bachelor's degree      standard   
1299    male        group A                 high school      standard   

     test preparation course  math score  reading score  writing score  
0                       none          72             72             74  
1                  completed          69             90             88  
2                       none          90             95             93  
3                       none          47             57             44  
4                       none          76             78             75  
...                      ...         ...            ...            ...  
1295                    none          67             60             59  
1296                    none          39             38             32  
1297                    none          62             79             77  
1298                    none          65             62             56  
1299               completed          54             50             47  

[1300 rows x 8 columns]>
Out[4]:
gender race/ethnicity parental level of education lunch test preparation course math score reading score writing score
0 female group B bachelor's degree standard none 72 72 74
1 female group C some college standard completed 69 90 88
2 female group B master's degree standard none 90 95 93
3 male group A associate's degree free/reduced none 47 57 44
4 male group C some college standard none 76 78 75
5 female group B associate's degree standard none 71 83 78
6 female group B some college standard completed 88 95 92
7 male group B some college free/reduced none 40 43 39
8 male group D high school free/reduced completed 64 64 67
9 female group B high school free/reduced none 38 60 50

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
In [5]:

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
In [6]:
Out[6]:
<AxesSubplot:xlabel='math score', ylabel='Count'>
Out[6]:
Text(15, 90, 'Šikmost: -0.24\nŠpičatost: 0.1')
Out[6]:
<AxesSubplot:xlabel='reading score', ylabel='Count'>
Out[6]:
Text(15, 90, 'Šikmost: -0.24\nŠpičatost: -0.13')
Out[6]:
<AxesSubplot:xlabel='writing score', ylabel='Count'>
Out[6]:
Text(15, 90, 'Šikmost: -0.27\nŠpičatost: -0.04')

Transformace - změna názvů sloupců pro lepší manipulaci

In [7]:
In [8]:
Out[8]:
gender race parents lunch preparation math reading writing
0 female group B bachelor's degree standard none 72 72 74
1 female group C some college standard completed 69 90 88
2 female group B master's degree standard none 90 95 93
3 male group A associate's degree free/reduced none 47 57 44
4 male group C some college standard none 76 78 75

Zobrazíme si rozložení numerických sloupců i tímto způsobem pro vyzkoušení

  • 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
In [9]:
Out[9]:
array([[<AxesSubplot:title={'center':'math'}>,
        <AxesSubplot:title={'center':'reading'}>],
       [<AxesSubplot:title={'center':'writing'}>, <AxesSubplot:>]],
      dtype=object)

Kontrola null hodnot

  • žádná data naštěstí nechybí, takže není potřeba se tímto krokem zabývat
In [10]:
Out[10]:
gender         0
race           0
parents        0
lunch          0
preparation    0
math           0
reading        0
writing        0
dtype: int64

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

Ukázka vyřešení doplnění chybějících hodnot

  • 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

Příklad 1: Využití manuálního doplnění "fillna()" a vypočítaného mediánu z adekvátního sloupce

In [11]:

Příklad 2: Upravení pipeline a využití SimpleImputer

In [12]:
In [13]:
In [14]:

Nalezení potenciálních závislostí v datech

  • 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
In [15]:
Out[15]:
<matplotlib.collections.PathCollection at 0x2232fbe98e0>
Out[15]:
[Text(0.5, 0, 'Math score'), Text(0, 0.5, 'Writing score')]
Out[15]:
Text(0.5, 1.0, 'Závislost skóre z matematiky na skóre z psaní')

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

In [16]:
Out[16]:
<matplotlib.collections.PathCollection at 0x223300a0550>
Out[16]:
[Text(0.5, 0, 'Math score'), Text(0, 0.5, 'Writing score')]
Out[16]:
Text(0.5, 1.0, 'Závislost skóre z matematiky na skóre z psaní')
In [17]:
Out[17]:
<matplotlib.collections.PathCollection at 0x2232fcb1e50>
Out[17]:
[Text(0.5, 0, 'Math score'), Text(0, 0.5, 'Reading score')]
Out[17]:
Text(0.5, 1.0, 'Závislost skóre z matematiky na skóre z čtení')

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ů
In [18]:
Out[18]:
<seaborn.axisgrid.PairGrid at 0x2232fcdb5e0>

Transformace datasetu

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
In [19]:
  • ověříme pro jistotu opět, že žádné hodnoty nechybějí
In [20]:
Out[20]:
gender         0
race           0
parents        0
lunch          0
preparation    0
math           0
reading        0
writing        0
math_grade     0
dtype: int64

Příprava a naplnění "target" sloupce

  • 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
In [21]:
In [22]:
Out[22]:
gender         0
race           0
parents        0
lunch          0
preparation    0
math           0
reading        0
writing        0
math_grade     0
dtype: int64
  • 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
In [23]:
Out[23]:
gender race parents lunch preparation math reading writing math_grade
  • 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
In [24]:
Out[24]:
gender                   female
race                    group C
parents        some high school
lunch              free/reduced
preparation                none
math                          0
reading                      17
writing                      10
math_grade                    0
Name: 59, dtype: object

Přepsání sloupce kategoriálními hodnotami

  • 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í
In [25]:
In [26]:
Out[26]:
gender         0
race           0
parents        0
lunch          0
preparation    0
math           0
reading        0
writing        0
math_grade     0
dtype: int64
In [27]:
Out[27]:
gender race parents lunch preparation math reading writing math_grade
In [28]:
gender           object
race             object
parents          object
lunch            object
preparation      object
math              int64
reading           int64
writing           int64
math_grade     category
dtype: object

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
In [29]:
Out[29]:
borderline    490
good          312
repeat        240
failed        180
excellent      78
Name: math_grade, dtype: int64
In [30]:
Out[30]:
gender race parents lunch preparation math reading writing math_grade
0 female group B bachelor's degree standard none 72 72 74 borderline
1 female group C some college standard completed 69 90 88 borderline
2 female group B master's degree standard none 90 95 93 excellent
3 male group A associate's degree free/reduced none 47 57 44 failed
4 male group C some college standard none 76 78 75 good
... ... ... ... ... ... ... ... ... ...
1295 male group B associate's degree free/reduced none 67 60 59 borderline
1296 male group C some high school free/reduced none 39 38 32 failed
1297 female group C high school standard none 62 79 77 borderline
1298 male group B bachelor's degree standard none 65 62 56 borderline
1299 male group A high school standard completed 54 50 47 repeat

1300 rows × 9 columns

Smazání původního sloupce math z datasetu

In [31]:
Out[31]:
gender race parents lunch preparation reading writing math_grade
0 female group B bachelor's degree standard none 72 74 borderline
1 female group C some college standard completed 90 88 borderline
2 female group B master's degree standard none 95 93 excellent

Převod hodnot vybraných vhodných sloupců do binárních hodnot

In [32]:
In [33]:
In [34]:
In [35]:
In [36]:
Out[36]:
isMale race parents lunchStandard preparationCompleted reading writing math_grade
0 0 group B bachelor's degree 1 0 72 74 borderline
1 0 group C some college 1 1 90 88 borderline
2 0 group B master's degree 1 0 95 93 excellent
3 1 group A associate's degree 0 0 57 44 failed

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
In [37]:
Out[37]:
array(["bachelor's degree", 'some college', "master's degree",
       "associate's degree", 'high school', 'some high school'],
      dtype=object)
Out[37]:
array(['group B', 'group C', 'group A', 'group D', 'group E'],
      dtype=object)
In [38]:
isMale                     int32
race                      object
parents                   object
lunchStandard              int32
preparationCompleted       int32
reading                    int64
writing                    int64
math_grade              category
dtype: object
  • 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
In [39]:
In [40]:
Out[40]:
race parents
0 group B bachelor's degree
1 group C some college
2 group B master's degree

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é
In [41]:
In [42]:
Out[42]:
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
0 0 1 0 0 0 0 1 0 0 0 0
1 0 0 1 0 0 0 0 0 0 1 0
2 0 1 0 0 0 0 0 0 1 0 0
  • 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
In [43]:
Out[43]:
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
0 0 1 0 0 0 0 1 0 0 0 0
1 0 0 1 0 0 0 0 0 0 1 0
2 0 1 0 0 0 0 0 0 1 0 0
3 1 0 0 0 0 1 0 0 0 0 0
4 0 0 1 0 0 0 0 0 0 1 0
... ... ... ... ... ... ... ... ... ... ... ...
1295 0 1 0 0 0 1 0 0 0 0 0
1296 0 0 1 0 0 0 0 0 0 0 1
1297 0 0 1 0 0 0 0 1 0 0 0
1298 0 1 0 0 0 0 1 0 0 0 0
1299 1 0 0 0 0 0 0 1 0 0 0

1300 rows × 11 columns

Normalizace hodnot skóre

  • 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
In [44]:
In [45]:
Out[45]:
isMale race parents lunchStandard preparationCompleted reading writing math_grade
0 0 group B bachelor's degree 1 0 0.72 0.74 borderline
1 0 group C some college 1 1 0.90 0.88 borderline
2 0 group B master's degree 1 0 0.95 0.93 excellent

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
In [46]:

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.

In [47]:
Out[47]:
isMale lunchStandard preparationCompleted reading writing math_grade 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
0 0 1 0 0.72 0.74 borderline 0 1 0 0 0 0 1 0 0 0 0
1 0 1 1 0.90 0.88 borderline 0 0 1 0 0 0 0 0 0 1 0
2 0 1 0 0.95 0.93 excellent 0 1 0 0 0 0 0 0 1 0 0
3 1 0 0 0.57 0.44 failed 1 0 0 0 0 1 0 0 0 0 0
4 1 1 0 0.78 0.75 good 0 0 1 0 0 0 0 0 0 1 0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1295 1 0 0 0.60 0.59 borderline 0 1 0 0 0 1 0 0 0 0 0
1296 1 0 0 0.38 0.32 failed 0 0 1 0 0 0 0 0 0 0 1
1297 0 1 0 0.79 0.77 borderline 0 0 1 0 0 0 0 1 0 0 0
1298 1 1 0 0.62 0.56 borderline 0 1 0 0 0 0 1 0 0 0 0
1299 1 1 1 0.50 0.47 repeat 1 0 0 0 0 0 0 1 0 0 0

1300 rows × 17 columns

In [48]:
isMale                           int32
lunchStandard                    int32
preparationCompleted             int32
reading                        float64
writing                        float64
math_grade                    category
race_group A                     uint8
race_group B                     uint8
race_group C                     uint8
race_group D                     uint8
race_group E                     uint8
parents_associate's degree       uint8
parents_bachelor's degree        uint8
parents_high school              uint8
parents_master's degree          uint8
parents_some college             uint8
parents_some high school         uint8
dtype: object
In [49]:

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"})

In [50]:
isMale                         float64
lunchStandard                  float64
preparationCompleted           float64
reading                        float64
writing                        float64
math_grade                    category
race_group A                   float64
race_group B                   float64
race_group C                   float64
race_group D                   float64
race_group E                   float64
parents_associate's degree     float64
parents_bachelor's degree      float64
parents_high school            float64
parents_master's degree        float64
parents_some college           float64
parents_some high school       float64
dtype: object

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)

In [51]:
Out[51]:
isMale lunchStandard preparationCompleted reading writing math_grade 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
0 0.0 1.0 0.0 0.72 0.74 borderline 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0
1 0.0 1.0 1.0 0.90 0.88 borderline 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0
2 0.0 1.0 0.0 0.95 0.93 excellent 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0
3 1.0 0.0 0.0 0.57 0.44 failed 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0
4 1.0 1.0 0.0 0.78 0.75 good 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1295 1.0 0.0 0.0 0.60 0.59 borderline 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0
1296 1.0 0.0 0.0 0.38 0.32 failed 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0
1297 0.0 1.0 0.0 0.79 0.77 borderline 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0
1298 1.0 1.0 0.0 0.62 0.56 borderline 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0
1299 1.0 1.0 1.0 0.50 0.47 repeat 1.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0

1300 rows × 17 columns

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

VYTVOŘENÍ TRÉNOVACÍCH A TESTOVACÍCH PROMĚNNÝCH A MODELU

  • 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
In [52]:
In [53]:

BASELINE Model pro zjištění potenciální účinnosti našeho modelu

  • 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
  • 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
  • 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 %
In [54]:
Out[54]:
DummyClassifier(strategy='stratified')
Out[54]:
DummyClassifier(strategy='stratified')
Out[54]:
array(['borderline', 'borderline', 'failed', ..., 'good', 'good',
       'borderline'], dtype=object)
Out[54]:
0.26692307692307693

DummyClassifier(strategy='stratified')

DummyClassifier(strategy='stratified')

array(['repeat', 'failed', 'repeat', ..., 'borderline', 'borderline', 'borderline'], dtype=object)

0.22230769230769232

  • 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 %.
In [55]:
Out[55]:
DummyClassifier(strategy='uniform')
Out[55]:
DummyClassifier(strategy='uniform')
Out[55]:
array(['repeat', 'failed', 'borderline', ..., 'borderline', 'repeat',
       'borderline'], dtype=object)
Out[55]:
0.21153846153846154

DummyClassifier(strategy='uniform')

DummyClassifier(strategy='uniform')

array(['good', 'repeat', 'excellent', ..., 'borderline', 'good', 'excellent'], dtype=object)

0.18076923076923077

Logistická regrese

  • V následující části byl vytvořen a využit model Logistické regrese pro predikci
  • Import funkce cross_val_score (v úvodu bloku) z knihovny sklearn, která bude následně využita pro corssvalidation našich modelů
  • 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
In [56]:
C:\Users\Martin\AppData\Roaming\Python\Python38\site-packages\sklearn\linear_model\_logistic.py:763: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
Out[56]:
LogisticRegression(random_state=42)
  • 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
In [57]:
Out[57]:
Pipeline(steps=[('standardscaler', StandardScaler()),
                ('logisticregression', LogisticRegression())])
  • Po výstupu metody score() našeho modelu, který jsme spustili s pomocí pipeline nám vychází accuracy téměř 71 %
  • Z testovaných modelů se tento model Logistické regrese s využitím pipeline a StandardScaleru projevil, jako nejlepší pro náš účel
In [58]:
Out[58]:
0.7076923076923077
  • Po výstupu metody cross_val_score() našeho modelu, který jsme spustili s pomocí pipeline nám vychází accuracy téměř 68 %
In [59]:
Out[59]:
0.678021978021978
  • Zde bez použití pipeline je accuracy o něco málo nižší na zaokrouhleně 64,4 %
In [60]:
Out[60]:
0.643956043956044
In [61]:
C:\Users\Martin\AppData\Roaming\Python\Python38\site-packages\sklearn\linear_model\_logistic.py:763: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
C:\Users\Martin\AppData\Roaming\Python\Python38\site-packages\sklearn\linear_model\_logistic.py:763: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
C:\Users\Martin\AppData\Roaming\Python\Python38\site-packages\sklearn\linear_model\_logistic.py:763: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
C:\Users\Martin\AppData\Roaming\Python\Python38\site-packages\sklearn\linear_model\_logistic.py:763: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
Out[61]:
0.6098901098901099
In [62]:
  • 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
In [63]:
Out[63]:
['failed', 'good', 'borderline', 'failed', 'repeat', ..., 'repeat', 'borderline', 'borderline', 'repeat', 'good']
Length: 910
Categories (5, object): ['failed' < 'repeat' < 'borderline' < 'good' < 'excellent']
In [64]:
Out[64]:
array(['borderline', 'borderline', 'borderline', 'borderline',
       'borderline', 'good', 'borderline', 'borderline', 'borderline',
       'good', 'good', 'borderline', 'borderline', 'borderline', 'good',
       'borderline', 'borderline', 'good', 'good', 'failed', 'borderline',
       'borderline', 'borderline', 'good', 'repeat', 'failed', 'good',
       'borderline', 'failed', 'borderline', 'borderline', 'borderline',
       'failed', 'repeat', 'borderline', 'borderline', 'borderline',
       'borderline', 'good', 'borderline', 'borderline', 'failed',
       'repeat', 'good', 'borderline', 'good', 'borderline', 'failed',
       'failed', 'good', 'good', 'borderline', 'good', 'good',
       'borderline', 'failed', 'borderline', 'borderline', 'borderline',
       'borderline', 'borderline', 'borderline', 'borderline',
       'borderline', 'failed', 'borderline', 'borderline', 'good', 'good',
       'borderline', 'borderline', 'borderline', 'borderline',
       'borderline', 'borderline', 'good', 'borderline', 'borderline',
       'good', 'borderline', 'good', 'failed', 'repeat', 'borderline',
       'failed', 'failed', 'borderline', 'failed', 'borderline', 'failed',
       'failed', 'borderline', 'good', 'borderline', 'good', 'borderline',
       'borderline', 'good', 'borderline', 'good', 'good', 'borderline',
       'good', 'borderline', 'borderline', 'borderline', 'good', 'good',
       'good', 'borderline', 'good', 'failed', 'borderline', 'repeat',
       'repeat', 'borderline', 'failed', 'borderline', 'borderline',
       'borderline', 'borderline', 'borderline', 'borderline',
       'borderline', 'good', 'good', 'failed', 'borderline', 'good',
       'borderline', 'borderline', 'borderline', 'borderline',
       'borderline', 'borderline', 'good', 'borderline', 'borderline',
       'good', 'borderline', 'good', 'borderline', 'borderline',
       'borderline', 'borderline', 'good', 'borderline', 'good', 'repeat',
       'borderline', 'borderline', 'repeat', 'borderline', 'repeat',
       'borderline', 'good', 'borderline', 'borderline', 'borderline',
       'borderline', 'good', 'borderline', 'failed', 'good', 'good',
       'failed', 'failed', 'borderline', 'repeat', 'borderline',
       'borderline', 'failed', 'borderline', 'good', 'borderline',
       'borderline', 'borderline', 'good', 'good', 'good', 'repeat',
       'good', 'borderline', 'borderline', 'borderline', 'borderline',
       'failed', 'good', 'borderline', 'good', 'borderline', 'borderline',
       'borderline', 'borderline', 'borderline', 'borderline', 'good',
       'borderline', 'good', 'failed', 'borderline', 'borderline',
       'borderline', 'borderline', 'borderline', 'failed', 'borderline',
       'good', 'good', 'borderline', 'borderline', 'borderline',
       'borderline', 'borderline', 'borderline', 'good', 'good',
       'borderline', 'borderline', 'borderline', 'good', 'good',
       'borderline', 'good', 'failed', 'good', 'borderline', 'borderline',
       'borderline', 'borderline', 'good', 'borderline', 'failed', 'good',
       'repeat', 'good', 'borderline', 'borderline', 'borderline',
       'borderline', 'borderline', 'borderline', 'borderline',
       'borderline', 'repeat', 'failed', 'borderline', 'borderline',
       'borderline', 'failed', 'borderline', 'borderline', 'borderline',
       'borderline', 'borderline', 'good', 'borderline', 'repeat', 'good',
       'borderline', 'borderline', 'borderline', 'borderline',
       'borderline', 'failed', 'good', 'borderline', 'borderline',
       'borderline', 'borderline', 'borderline', 'borderline', 'failed',
       'borderline', 'good', 'failed', 'borderline', 'borderline', 'good',
       'borderline', 'borderline', 'borderline', 'repeat', 'borderline',
       'repeat', 'borderline', 'borderline', 'good', 'repeat', 'good',
       'borderline', 'borderline', 'good', 'borderline', 'failed', 'good',
       'borderline', 'borderline', 'good', 'borderline', 'failed', 'good',
       'borderline', 'borderline', 'good', 'borderline', 'borderline',
       'good', 'failed', 'borderline', 'repeat', 'failed', 'borderline',
       'repeat', 'borderline', 'borderline', 'borderline', 'borderline',
       'good', 'borderline', 'failed', 'borderline', 'borderline',
       'failed', 'borderline', 'good', 'borderline', 'good', 'borderline',
       'borderline', 'failed', 'failed', 'failed', 'borderline', 'failed',
       'borderline', 'borderline', 'borderline', 'good', 'good', 'repeat',
       'borderline', 'good', 'good', 'borderline', 'borderline',
       'borderline', 'borderline', 'failed', 'good', 'failed', 'good',
       'borderline', 'good', 'repeat', 'borderline', 'failed', 'good',
       'borderline', 'borderline', 'repeat', 'borderline', 'good',
       'borderline', 'borderline', 'borderline', 'borderline',
       'borderline', 'borderline', 'borderline', 'borderline', 'failed',
       'borderline', 'good', 'borderline', 'borderline', 'borderline',
       'borderline', 'borderline', 'good', 'failed', 'good', 'good',
       'good', 'borderline', 'borderline', 'good', 'borderline', 'good',
       'borderline'], dtype=object)

Random Forest

  • 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 %
In [65]:
Out[65]:
RandomForestClassifier()
In [66]:
Out[66]:
0.6186520947176685

SVC

  • 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 %
In [67]:
Out[67]:
SVC(kernel='linear')
In [68]:
Out[68]:
0.650455373406193

XGBoost

  • 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 %
In [69]:
C:\Users\Martin\AppData\Roaming\Python\Python38\site-packages\xgboost\sklearn.py:1224: UserWarning: The use of label encoder in XGBClassifier is deprecated and will be removed in a future release. To remove this warning, do the following: 1) Pass option use_label_encoder=False when constructing XGBClassifier object; and 2) Encode your labels (y) as integers starting with 0, i.e. 0, 1, 2, ..., [num_class - 1].
  warnings.warn(label_encoder_deprecation_msg, UserWarning)
Out[69]:
XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1,
              colsample_bynode=1, colsample_bytree=1, enable_categorical=False,
              gamma=5, gpu_id=-1, importance_type=None,
              interaction_constraints='', learning_rate=0.300000012,
              max_delta_step=0, max_depth=6, min_child_weight=1, missing=nan,
              monotone_constraints='()', n_estimators=100, n_jobs=-1,
              num_parallel_tree=1, objective='multi:softprob', predictor='auto',
              random_state=0, reg_alpha=0, reg_lambda=1, scale_pos_weight=None,
              subsample=1, tree_method='exact', validate_parameters=1,
              verbosity=0)
In [70]:
C:\Users\Martin\AppData\Roaming\Python\Python38\site-packages\xgboost\sklearn.py:1224: UserWarning: The use of label encoder in XGBClassifier is deprecated and will be removed in a future release. To remove this warning, do the following: 1) Pass option use_label_encoder=False when constructing XGBClassifier object; and 2) Encode your labels (y) as integers starting with 0, i.e. 0, 1, 2, ..., [num_class - 1].
  warnings.warn(label_encoder_deprecation_msg, UserWarning)
C:\Users\Martin\AppData\Roaming\Python\Python38\site-packages\xgboost\sklearn.py:1224: UserWarning: The use of label encoder in XGBClassifier is deprecated and will be removed in a future release. To remove this warning, do the following: 1) Pass option use_label_encoder=False when constructing XGBClassifier object; and 2) Encode your labels (y) as integers starting with 0, i.e. 0, 1, 2, ..., [num_class - 1].
  warnings.warn(label_encoder_deprecation_msg, UserWarning)
C:\Users\Martin\AppData\Roaming\Python\Python38\site-packages\xgboost\sklearn.py:1224: UserWarning: The use of label encoder in XGBClassifier is deprecated and will be removed in a future release. To remove this warning, do the following: 1) Pass option use_label_encoder=False when constructing XGBClassifier object; and 2) Encode your labels (y) as integers starting with 0, i.e. 0, 1, 2, ..., [num_class - 1].
  warnings.warn(label_encoder_deprecation_msg, UserWarning)
C:\Users\Martin\AppData\Roaming\Python\Python38\site-packages\xgboost\sklearn.py:1224: UserWarning: The use of label encoder in XGBClassifier is deprecated and will be removed in a future release. To remove this warning, do the following: 1) Pass option use_label_encoder=False when constructing XGBClassifier object; and 2) Encode your labels (y) as integers starting with 0, i.e. 0, 1, 2, ..., [num_class - 1].
  warnings.warn(label_encoder_deprecation_msg, UserWarning)
C:\Users\Martin\AppData\Roaming\Python\Python38\site-packages\xgboost\sklearn.py:1224: UserWarning: The use of label encoder in XGBClassifier is deprecated and will be removed in a future release. To remove this warning, do the following: 1) Pass option use_label_encoder=False when constructing XGBClassifier object; and 2) Encode your labels (y) as integers starting with 0, i.e. 0, 1, 2, ..., [num_class - 1].
  warnings.warn(label_encoder_deprecation_msg, UserWarning)
0.6505494505494506

GridSearch a Ladění hyperparametrů pro RandomForestClassifier

  • 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
In [71]:
Out[71]:
dict_keys(['bootstrap', 'ccp_alpha', 'class_weight', 'criterion', 'max_depth', 'max_features', 'max_leaf_nodes', 'max_samples', 'min_impurity_decrease', 'min_impurity_split', 'min_samples_leaf', 'min_samples_split', 'min_weight_fraction_leaf', 'n_estimators', 'n_jobs', 'oob_score', 'random_state', 'verbose', 'warm_start'])
In [72]:
In [73]:
Fitting 3 folds for each of 16 candidates, totalling 48 fits
In [74]:
Out[74]:
RandomForestClassifier(max_depth=5, random_state=42)
In [75]:
Out[75]:
{'max_depth': 5, 'n_estimators': 100}
In [76]:
Out[76]:
0.6263715187308204
In [77]:
Best: 0.626372 using {'max_depth': 5, 'n_estimators': 100}
0.374729 (0.002134) with: {'max_depth': 1, 'n_estimators': 10}
0.374729 (0.002134) with: {'max_depth': 1, 'n_estimators': 100}
0.376929 (0.004569) with: {'max_depth': 1, 'n_estimators': 1000}
0.376929 (0.004569) with: {'max_depth': 1, 'n_estimators': 2000}
0.613159 (0.018480) with: {'max_depth': 5, 'n_estimators': 10}
0.626372 (0.009528) with: {'max_depth': 5, 'n_estimators': 100}
0.624182 (0.004884) with: {'max_depth': 5, 'n_estimators': 1000}
0.623082 (0.009991) with: {'max_depth': 5, 'n_estimators': 2000}
0.590129 (0.021553) with: {'max_depth': 10, 'n_estimators': 10}
0.615403 (0.022261) with: {'max_depth': 10, 'n_estimators': 100}
0.623064 (0.018196) with: {'max_depth': 10, 'n_estimators': 1000}
0.623075 (0.017568) with: {'max_depth': 10, 'n_estimators': 2000}
0.596737 (0.028030) with: {'max_depth': 20, 'n_estimators': 10}
0.604406 (0.009273) with: {'max_depth': 20, 'n_estimators': 100}
0.618692 (0.027885) with: {'max_depth': 20, 'n_estimators': 1000}
0.614296 (0.023779) with: {'max_depth': 20, 'n_estimators': 2000}
  • 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ů

Využití GridSearch a hledání klíčových hyperparametrů pro model Logistické regrese

In [78]:
Out[78]:
dict_keys(['C', 'class_weight', 'dual', 'fit_intercept', 'intercept_scaling', 'l1_ratio', 'max_iter', 'multi_class', 'n_jobs', 'penalty', 'random_state', 'solver', 'tol', 'verbose', 'warm_start'])
In [79]:
In [80]:
In [81]:
In [82]:
In [83]:
Best: 0.686813 using {'C': 1.0, 'penalty': 'l2', 'solver': 'lbfgs'}
0.684249 (0.033684) with: {'C': 100, 'penalty': 'l2', 'solver': 'newton-cg'}
0.683883 (0.034011) with: {'C': 100, 'penalty': 'l2', 'solver': 'lbfgs'}
0.620879 (0.041483) with: {'C': 100, 'penalty': 'l2', 'solver': 'liblinear'}
0.683516 (0.034331) with: {'C': 100, 'penalty': 'l2', 'solver': 'sag'}
0.683883 (0.035289) with: {'C': 100, 'penalty': 'l2', 'solver': 'saga'}
0.000000 (0.000000) with: {'C': 100, 'penalty': 'l1', 'solver': 'newton-cg'}
0.000000 (0.000000) with: {'C': 100, 'penalty': 'l1', 'solver': 'lbfgs'}
0.621245 (0.041091) with: {'C': 100, 'penalty': 'l1', 'solver': 'liblinear'}
0.000000 (0.000000) with: {'C': 100, 'penalty': 'l1', 'solver': 'sag'}
0.684249 (0.035544) with: {'C': 100, 'penalty': 'l1', 'solver': 'saga'}
0.683516 (0.034448) with: {'C': 100, 'penalty': 'none', 'solver': 'newton-cg'}
0.683516 (0.034448) with: {'C': 100, 'penalty': 'none', 'solver': 'lbfgs'}
0.000000 (0.000000) with: {'C': 100, 'penalty': 'none', 'solver': 'liblinear'}
0.683516 (0.034448) with: {'C': 100, 'penalty': 'none', 'solver': 'sag'}
0.684615 (0.035113) with: {'C': 100, 'penalty': 'none', 'solver': 'saga'}
0.681319 (0.037961) with: {'C': 10, 'penalty': 'l2', 'solver': 'newton-cg'}
0.681685 (0.037264) with: {'C': 10, 'penalty': 'l2', 'solver': 'lbfgs'}
0.618315 (0.040618) with: {'C': 10, 'penalty': 'l2', 'solver': 'liblinear'}
0.681319 (0.037961) with: {'C': 10, 'penalty': 'l2', 'solver': 'sag'}
0.680586 (0.038376) with: {'C': 10, 'penalty': 'l2', 'solver': 'saga'}
0.000000 (0.000000) with: {'C': 10, 'penalty': 'l1', 'solver': 'newton-cg'}
0.000000 (0.000000) with: {'C': 10, 'penalty': 'l1', 'solver': 'lbfgs'}
0.620879 (0.041483) with: {'C': 10, 'penalty': 'l1', 'solver': 'liblinear'}
0.000000 (0.000000) with: {'C': 10, 'penalty': 'l1', 'solver': 'sag'}
0.684982 (0.035020) with: {'C': 10, 'penalty': 'l1', 'solver': 'saga'}
0.683516 (0.034448) with: {'C': 10, 'penalty': 'none', 'solver': 'newton-cg'}
0.683516 (0.034448) with: {'C': 10, 'penalty': 'none', 'solver': 'lbfgs'}
0.000000 (0.000000) with: {'C': 10, 'penalty': 'none', 'solver': 'liblinear'}
0.683516 (0.034448) with: {'C': 10, 'penalty': 'none', 'solver': 'sag'}
0.684982 (0.034673) with: {'C': 10, 'penalty': 'none', 'solver': 'saga'}
0.686447 (0.039083) with: {'C': 1.0, 'penalty': 'l2', 'solver': 'newton-cg'}
0.686813 (0.038566) with: {'C': 1.0, 'penalty': 'l2', 'solver': 'lbfgs'}
0.608425 (0.038434) with: {'C': 1.0, 'penalty': 'l2', 'solver': 'liblinear'}
0.686447 (0.038669) with: {'C': 1.0, 'penalty': 'l2', 'solver': 'sag'}
0.686447 (0.039083) with: {'C': 1.0, 'penalty': 'l2', 'solver': 'saga'}
0.000000 (0.000000) with: {'C': 1.0, 'penalty': 'l1', 'solver': 'newton-cg'}
0.000000 (0.000000) with: {'C': 1.0, 'penalty': 'l1', 'solver': 'lbfgs'}
0.621612 (0.039283) with: {'C': 1.0, 'penalty': 'l1', 'solver': 'liblinear'}
0.000000 (0.000000) with: {'C': 1.0, 'penalty': 'l1', 'solver': 'sag'}
0.682784 (0.037074) with: {'C': 1.0, 'penalty': 'l1', 'solver': 'saga'}
0.683516 (0.034448) with: {'C': 1.0, 'penalty': 'none', 'solver': 'newton-cg'}
0.683516 (0.034448) with: {'C': 1.0, 'penalty': 'none', 'solver': 'lbfgs'}
0.000000 (0.000000) with: {'C': 1.0, 'penalty': 'none', 'solver': 'liblinear'}
0.683516 (0.034448) with: {'C': 1.0, 'penalty': 'none', 'solver': 'sag'}
0.684615 (0.035113) with: {'C': 1.0, 'penalty': 'none', 'solver': 'saga'}
0.652747 (0.039885) with: {'C': 0.1, 'penalty': 'l2', 'solver': 'newton-cg'}
0.652747 (0.039885) with: {'C': 0.1, 'penalty': 'l2', 'solver': 'lbfgs'}
0.597070 (0.034205) with: {'C': 0.1, 'penalty': 'l2', 'solver': 'liblinear'}
0.652747 (0.039885) with: {'C': 0.1, 'penalty': 'l2', 'solver': 'sag'}
0.652747 (0.039885) with: {'C': 0.1, 'penalty': 'l2', 'solver': 'saga'}
0.000000 (0.000000) with: {'C': 0.1, 'penalty': 'l1', 'solver': 'newton-cg'}
0.000000 (0.000000) with: {'C': 0.1, 'penalty': 'l1', 'solver': 'lbfgs'}
0.606960 (0.039792) with: {'C': 0.1, 'penalty': 'l1', 'solver': 'liblinear'}
0.000000 (0.000000) with: {'C': 0.1, 'penalty': 'l1', 'solver': 'sag'}
0.660806 (0.039696) with: {'C': 0.1, 'penalty': 'l1', 'solver': 'saga'}
0.683516 (0.034448) with: {'C': 0.1, 'penalty': 'none', 'solver': 'newton-cg'}
0.683516 (0.034448) with: {'C': 0.1, 'penalty': 'none', 'solver': 'lbfgs'}
0.000000 (0.000000) with: {'C': 0.1, 'penalty': 'none', 'solver': 'liblinear'}
0.683516 (0.034448) with: {'C': 0.1, 'penalty': 'none', 'solver': 'sag'}
0.684615 (0.034419) with: {'C': 0.1, 'penalty': 'none', 'solver': 'saga'}
0.567766 (0.031125) with: {'C': 0.01, 'penalty': 'l2', 'solver': 'newton-cg'}
0.567766 (0.031125) with: {'C': 0.01, 'penalty': 'l2', 'solver': 'lbfgs'}
0.573993 (0.045635) with: {'C': 0.01, 'penalty': 'l2', 'solver': 'liblinear'}
0.567766 (0.031125) with: {'C': 0.01, 'penalty': 'l2', 'solver': 'sag'}
0.567766 (0.031125) with: {'C': 0.01, 'penalty': 'l2', 'solver': 'saga'}
0.000000 (0.000000) with: {'C': 0.01, 'penalty': 'l1', 'solver': 'newton-cg'}
0.000000 (0.000000) with: {'C': 0.01, 'penalty': 'l1', 'solver': 'lbfgs'}
0.383516 (0.011116) with: {'C': 0.01, 'penalty': 'l1', 'solver': 'liblinear'}
0.000000 (0.000000) with: {'C': 0.01, 'penalty': 'l1', 'solver': 'sag'}
0.412088 (0.017433) with: {'C': 0.01, 'penalty': 'l1', 'solver': 'saga'}
0.683516 (0.034448) with: {'C': 0.01, 'penalty': 'none', 'solver': 'newton-cg'}
0.683516 (0.034448) with: {'C': 0.01, 'penalty': 'none', 'solver': 'lbfgs'}
0.000000 (0.000000) with: {'C': 0.01, 'penalty': 'none', 'solver': 'liblinear'}
0.683516 (0.034448) with: {'C': 0.01, 'penalty': 'none', 'solver': 'sag'}
0.684982 (0.034673) with: {'C': 0.01, 'penalty': 'none', 'solver': 'saga'}
  • 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ů

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

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í.


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


In [ ]: