In [2]:
# Import potrebných balíčkov
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder, KBinsDiscretizer
from sklearn.metrics import accuracy_score, precision_score, recall_score
from keras.layers import Dense, Input, Activation, Dropout
from keras.callbacks import EarlyStopping
from keras.models import Model
Using TensorFlow backend.
In [3]:
# Uistíme sa, že máme všetky potrebné dáta
!mkdir -p data/winequality
!wget -nc -O data/winequality.zip https://www.dropbox.com/s/8s0ivlo9yshhxkn/winequality.zip?dl=1
!unzip -oq -d data/winequality data/winequality.zip
--2019-08-28 18:54:17--  https://www.dropbox.com/s/8s0ivlo9yshhxkn/winequality.zip?dl=1
Resolving www.dropbox.com (www.dropbox.com)... 162.125.82.1, 2620:100:6032:1::a27d:5201
Connecting to www.dropbox.com (www.dropbox.com)|162.125.82.1|:443... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: /s/dl/8s0ivlo9yshhxkn/winequality.zip [following]
--2019-08-28 18:54:18--  https://www.dropbox.com/s/dl/8s0ivlo9yshhxkn/winequality.zip
Reusing existing connection to www.dropbox.com:443.
HTTP request sent, awaiting response... 302 Found
Location: https://uc2f2376a5d3f0181fb901f1983f.dl.dropboxusercontent.com/cd/0/get/AnhEOmGF9rMLUWIYpH5SMYDaTsNm3r1QGLXcAS9fi4IQLMvH_Szae0pCkpvg-Gg8wuMn26_swfhqz9wCG7aq5BP-G6C4_gvRNs28sxu3MiNin2XgZAQZUJeejJO4OetX3sE/file?dl=1# [following]
--2019-08-28 18:54:18--  https://uc2f2376a5d3f0181fb901f1983f.dl.dropboxusercontent.com/cd/0/get/AnhEOmGF9rMLUWIYpH5SMYDaTsNm3r1QGLXcAS9fi4IQLMvH_Szae0pCkpvg-Gg8wuMn26_swfhqz9wCG7aq5BP-G6C4_gvRNs28sxu3MiNin2XgZAQZUJeejJO4OetX3sE/file?dl=1
Resolving uc2f2376a5d3f0181fb901f1983f.dl.dropboxusercontent.com (uc2f2376a5d3f0181fb901f1983f.dl.dropboxusercontent.com)... 162.125.82.6, 2620:100:6032:6::a27d:5206
Connecting to uc2f2376a5d3f0181fb901f1983f.dl.dropboxusercontent.com (uc2f2376a5d3f0181fb901f1983f.dl.dropboxusercontent.com)|162.125.82.6|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 94664 (92K) [application/binary]
Saving to: ‘data/winequality.zip’

data/winequality.zi 100%[===================>]  92.45K  --.-KB/s    in 0.05s   

2019-08-28 18:54:19 (1.83 MB/s) - ‘data/winequality.zip’ saved [94664/94664]

Klasifikácia kvality vína pomocou neurónových sietí

V rámci tohto notebook-u si môžete klasifikáciu pomocou neurónových sietí vyskúšať na príklade ďalšej jednoduchej dátovej množiny.

Pozor: Príklad je len ilustračný. Dátová množina je dobre štruktúrovaná (dáta sú rozdelené do stĺpcov s jasným významom atď.), preto by sa na ňu praxi pravdepodobne aplikoval iný model – napr. založený na rozhodovacích stromoch. Umelé neurónové siete a hlboké učenie sa aplikujú skôr v prípade neštruktúrovaných dát, ako je obraz, audio, text a pod.

Načítanie a predspracovanie dátovej množiny

Načítame dátovú množinu z CSV súboru:

In [4]:
df = pd.read_csv("data/winequality/winequality-white.csv")
df.head()
Out[4]:
fixed acidity volatile acidity citric acid residual sugar chlorides free sulfur dioxide total sulfur dioxide density pH sulphates alcohol quality
0 7.0 0.27 0.36 20.7 0.045 45.0 170.0 1.0010 3.00 0.45 8.8 6
1 6.3 0.30 0.34 1.6 0.049 14.0 132.0 0.9940 3.30 0.49 9.5 6
2 8.1 0.28 0.40 6.9 0.050 30.0 97.0 0.9951 3.26 0.44 10.1 6
3 7.2 0.23 0.32 8.5 0.058 47.0 186.0 0.9956 3.19 0.40 9.9 6
4 7.2 0.23 0.32 8.5 0.058 47.0 186.0 0.9956 3.19 0.40 9.9 6

Opis dát nájdeme v prípade potreby v priloženom súbore:

In [5]:
with open("data/winequality/winequality", "r") as file:
    print("".join(file.readlines()))
Citation Request:
  This dataset is public available for research. The details are described in [Cortez et al., 2009]. 
  Please include this citation if you plan to use this database:

  P. Cortez, A. Cerdeira, F. Almeida, T. Matos and J. Reis. 
  Modeling wine preferences by data mining from physicochemical properties.
  In Decision Support Systems, Elsevier, 47(4):547-553. ISSN: 0167-9236.

  Available at: [@Elsevier] http://dx.doi.org/10.1016/j.dss.2009.05.016
                [Pre-press (pdf)] http://www3.dsi.uminho.pt/pcortez/winequality09.pdf
                [bib] http://www3.dsi.uminho.pt/pcortez/dss09.bib

1. Title: Wine Quality 

2. Sources
   Created by: Paulo Cortez (Univ. Minho), Antonio Cerdeira, Fernando Almeida, Telmo Matos and Jose Reis (CVRVV) @ 2009
   
3. Past Usage:

  P. Cortez, A. Cerdeira, F. Almeida, T. Matos and J. Reis. 
  Modeling wine preferences by data mining from physicochemical properties.
  In Decision Support Systems, Elsevier, 47(4):547-553. ISSN: 0167-9236.

  In the above reference, two datasets were created, using red and white wine samples.
  The inputs include objective tests (e.g. PH values) and the output is based on sensory data
  (median of at least 3 evaluations made by wine experts). Each expert graded the wine quality 
  between 0 (very bad) and 10 (very excellent). Several data mining methods were applied to model
  these datasets under a regression approach. The support vector machine model achieved the
  best results. Several metrics were computed: MAD, confusion matrix for a fixed error tolerance (T),
  etc. Also, we plot the relative importances of the input variables (as measured by a sensitivity
  analysis procedure).
 
4. Relevant Information:

   The two datasets are related to red and white variants of the Portuguese "Vinho Verde" wine.
   For more details, consult: http://www.vinhoverde.pt/en/ or the reference [Cortez et al., 2009].
   Due to privacy and logistic issues, only physicochemical (inputs) and sensory (the output) variables 
   are available (e.g. there is no data about grape types, wine brand, wine selling price, etc.).

   These datasets can be viewed as classification or regression tasks.
   The classes are ordered and not balanced (e.g. there are munch more normal wines than
   excellent or poor ones). Outlier detection algorithms could be used to detect the few excellent
   or poor wines. Also, we are not sure if all input variables are relevant. So
   it could be interesting to test feature selection methods. 

5. Number of Instances: red wine - 1599; white wine - 4898. 

6. Number of Attributes: 11 + output attribute
  
   Note: several of the attributes may be correlated, thus it makes sense to apply some sort of
   feature selection.

7. Attribute information:

   For more information, read [Cortez et al., 2009].

   Input variables (based on physicochemical tests):
   1 - fixed acidity
   2 - volatile acidity
   3 - citric acid
   4 - residual sugar
   5 - chlorides
   6 - free sulfur dioxide
   7 - total sulfur dioxide
   8 - density
   9 - pH
   10 - sulphates
   11 - alcohol
   Output variable (based on sensory data): 
   12 - quality (score between 0 and 10)

8. Missing Attribute Values: None


Kódovanie výstupov

Dátová množina udáva kvalitu vína na stupnici od 1 po 10 (stĺpec quality). Vzhľadom na to, že dáta obsahujú dosť veľa šumu, je táto stupnica až príliš jemná, prejdeme preto na hrubšiu stupnicu: rozdelíme si vína len na tri stupne kvality, a to automaticky: pomocou triedy KBinsDiscretizer z balíčka sklearn.

In [6]:
output_preproc = KBinsDiscretizer(3, encode='ordinal', strategy='quantile')
df['quality_quant'] = output_preproc.fit_transform(df[['quality']])
df[['quality', 'quality_quant']].head()
Out[6]:
quality quality_quant
0 6 2.0
1 6 2.0
2 6 2.0
3 6 2.0
4 6 2.0

Rozdelenie dátovej množiny

Dátovú množinu rozdelíme na tréningové, validačné a testovacie dáta; stratifikujeme podľa kvality.

In [0]:
df_train, df_test = train_test_split(df, test_size=0.25,
                                     stratify=df['quality'],
                                     random_state=4)
df_train, df_valid = train_test_split(df_train_valid, test_size=0.05,
                                     stratify=df_train_valid['quality'],
                                     random_state=4)

Vstupy, výstupy a škálovanie

Ďalej potrebujeme rozdeliť dátovú množinu na vstupy a výstupy. Ako požadovaný výstup použijeme stĺpec quality_quant. Stĺpec quality nepoužijeme vôbec. Ostatné stĺpce použijeme ako vstupy.

In [88]:
df.columns
Out[88]:
Index(['fixed acidity', 'volatile acidity', 'citric acid', 'residual sugar',
       'chlorides', 'free sulfur dioxide', 'total sulfur dioxide', 'density',
       'pH', 'sulphates', 'alcohol', 'quality', 'quality_quant'],
      dtype='object')
In [0]:
# kvalitu použijeme ako požadovaný výstup
output = ["quality_quant"]
# ostatné stĺpce použijeme ako vstupy
inputs = df.columns[:-2]

X_train_raw = df_train[inputs]
Y_train_raw = df_train[output].values

X_valid_raw = df_valid[inputs]
Y_valid_raw = df_valid[output].values

X_test_raw = df_test[inputs]
Y_test_raw = df_test[output].values

Zadefinujeme predspracovanie vstupov a výstupov a zároveň ho aj aplikujeme na dáta:

In [0]:
input_preproc = StandardScaler()
X_train = input_preproc.fit_transform(X_train_raw)
X_valid = input_preproc.transform(X_valid_raw)
X_test = input_preproc.transform(X_test_raw)
In [0]:
output_preproc = OneHotEncoder(categories='auto')
Y_train = output_preproc.fit_transform(Y_train_raw)
Y_valid = output_preproc.transform(Y_valid_raw) 
Y_test = output_preproc.transform(Y_test_raw)

Úloha 1

Aplikujte na problém klasifikátor na báze neurónovej siete a otestujte pomocou testovacích dát jeho úspešnosť v zmysle:

  1. matice zámen,
  2. správnosti (accuracy),
  3. presnosti a úplnosti (precision, recall).

Poznámka: Možno bude dobré vložiť do siete viacero vrstiev a s väčším počtom neurónov, než to bolo v prípade dátovej množiny IRIS.


In [0]:

Regularizácia

Aby sme predišli preučeniu a zlepšili zovšeobecnenie, bude dobré použiť aj niektoré metódy regularizácie. Názov regularizácia vyplýva z toho, že snahou je, aby model zachytil skutočné zákonitosti (angl. regularities) v dátach a nie šum. Teda aby správne zovšeobecnil. Existuje mnoho metód, ktoré pomáhajú. My sa v tejto časti notebook-u budeme venovať aspoň niekoľkým základným.

Niektoré metódy regularizácie si budú vyžadovať dáta, pomocou ktorých budeme ladiť regularizčné parametre a pod. Na tento účel nemôžeme použiť testovacie dáta – inak by sme pomocou nich nevedeli na konci spoľahlivo otestovať zovšeobecnenie. Z tréningových dát sa preto na tento účel oddeľuje ešte jedna časť dát: nazývajú sa validačné dáta. Pomocou nich vieme odhadnúť, aká bude asi úspešnosť modelu na dátach, ktoré systém ešte nevidel – ale bez toho, aby sme museli použiť testovacie dáta.

Skoré ukončenie učenia

Ako prvú regularizačnú metódu skúsme aplikovať skoré ukončenie učenia. Znamená to, že učenie zastavíme vtedy, keď sa chyba na validačných dátach prestane znižovať – a to aj vtedy, ak chyba na tréningových dátach ešte stále klesá. Takýto stav totiž indikuje, že sieť už pravdepodobne nebude správne zovšeobecňovať a úspešnosť na tréningových dátach zvyšuje tým, že si vzorky zapamätáva naspamäť.

V keras-e sa dá skoré ukončenie učenia realizovať pomocou triedy Vytvoríme inštanciu triedy EarlyStopping. Pri vytvorení objektu je možné špecifikovať napríklad:

  • Trpezlivosť: (patience) ak sa začne chyba na validačnej množine zväčšovať, koľko krokov ešte počkáme, kým sa ukončí učenie.
  • Návrat k najlepším váham: (restore_best_weights) Ak nastavíme na True, po vypršaní trpezlivosti sa váhy siete prestavia späť na tie, pri ktorých bola chyba na validačných dátach najnižšia.

Parameter patience má hodnotu prednastavenú na 0. Najmä v prípadoch keď je validačná množina pomerne malá, môže byť lepšie zvoliť o niečo väčšie číslo: nedá sa vylúčiť, že v rámci ďalších epoch sa chybu na validačných dátach ešte podarí znížiť.

In [0]:
callbacks = [EarlyStopping(patience=5, restore_best_weights=False)]

Vytvorený zoznam callbacks použijeme ako argument pre funkciu fit. Zároveň špecifikujeme pomocou argumentu validation_split, aký podiel tréningových dát sa použije ako validačná množina. Alternatívne môžeme validačné dáta oddeliť vopred ručne a špecifikovať pomocou argumentu validation_data. Ako príklad:

model.fit(X_train, Y_train, epochs=200, batch_size=128,
          callbacks=callbacks, validation_data=(X_valid, Y_valid))

Úloha 2

Vyskúšajte aplikovať učenie s použitím skorého ukončenia.

Správnosť vyhodnocujte zatiaľ len prostredníctvom validačných dát, nie testovacích.


In [0]:

Metóda Dropout

Ak je architektúra siete nastavená tak, že ku preučeniu dôjde veľmi skoro, nemusí skoré ukončenie učenia samo osebe viesť ku dostatočne dobrým výsledkom. Je možné, že budeme potrebovať ešte iný spôsob zníženia kapacity modelu, ktorý zabezpečí, že model bude konvergovať o niečo pozvoľnejšie.

Môžeme použiť napríklad metódu Dropout. Importujeme si preto z balíčka keras príslušný typ vrstvy:

In [0]:
from keras.layers import Dropout

Keďže Dropout je typ vrstvy, treba ju zaradiť na niektoré miesta do nášho modelu. Neexistuje žiaden jednoznačný recept, ako to urobiť. V našom prípade môžeme Dropout vrstvu vložiť za niektoré alebo za všetky ReLU vrstvy. Pri vytváraní Dropout vrstvy sa špecifikuje miera dropout-u, t.j. aký podiel neurónov sa v každom kroku náhodne vypne. Často sa volí podiel 0.5, ale dajú sa voliť aj iné hodnoty – napr. 0.1 alebo 0.2, ak nechceme kapacitu modelu znížiť príliš agresívne.

Povedzme teda, že by sme za každú vrstvu y = Activation('relu')(y) vložili:

y = Dropout(0.5)(y)

Ak sa použije agresívnejšia forma regularizácie, kapacitu modelu to môže znížiť podstatne. Je teda napríklad možné, že model silno využívajúci Dropout bude potrebovať vrstvy s o niečo väčším počtom neurónov než model bez Dropout vrstiev.

Netriviálna je aj interakcia medzi rôznymi metódami regularizácie: napríklad pri použití metódy Dropout sa dá očakávať, že bude vo výsledkoch na validačnej množine väčší rozptyl (na zmeny váh vplývajú ďalšie stochastické faktory); preto môže byť v prípade s kombináciou so skorým ukončením učenia potrebné použiť podstatne vyššiu hodnotu patience.


Úloha 3

Skúste do siete vložiť niekoľko Dropout vrstiev, prípadne použiť ešte iné metódy regularizácie.

Efektivitu regularizácie testujte počas ladenia parametrov len pomocou validačnej dátovej množiny. Testovacia dátová množina sa použije až nakoniec – len jeden raz!


In [0]:


Úloha 4

Potom, ako ste systém vyladili, otestujte jeho zovšeobecnenie nakoniec aj na testovacích dátach. Správnosť na úrovni okolo 75 – 77% bude na tejto dátovej množine celkom dobrý výsledok.


In [0]:

Klasifikácia pomocou rozhodovacích stromov s gradientným boostingom

Aby sme ukázali, že neurónové siete nemajú na štruktúrovaných dátach podstatnú výhodu a lepšie výsledky sa väčšinou dajú dosiahnuť inými metódami, porovnáme výsledky aj s metódou LightGBM založenou na rozhodovacích stromoch a gradientnom boosting-u. Je dobrá šanca, že výsledok bude lepší než sa nám podarilo dosiahnuť pomocou neurónovej siete: a učenie bude určite trvať podstatne kratší čas. Skutočné výhody neurónových sietí uvidíme až pri náročnejších dátach ako je obraz, zvuk a pod.

In [0]:
from lightgbm import LGBMClassifier
In [ ]:
lgb = LGBMClassifier()
lgb.fit(X_train, Y_train_raw.reshape(-1))
In [94]:
y_test_raw = lgb.predict(X_test)

cm = pd.crosstab(Y_test_raw.reshape(-1),
                 y_test_raw.reshape(-1),
                 rownames=['actual'],
                 colnames=['predicted'])
print(cm)

print("Správnosť: {}.".format(accuracy_score(
    Y_test_raw, y_test_raw
)))
print("Presnosť: {}.".format(precision_score(
    Y_test_raw, y_test_raw, average='macro'
)))
print("Úplnosť: {}.".format(recall_score(
    Y_test_raw, y_test_raw, average='macro'
)))
predicted  0.0  1.0  2.0
actual                  
0.0         12   23   11
1.0          7  223  134
2.0          4   75  736
Správnosť: 0.7926530612244898.
Presnosť: 0.6839524940695482.
Úplnosť: 0.5921914708391102.
In [0]: