# 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
# 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
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čítame dátovú množinu z CSV súboru:
df = pd.read_csv("data/winequality/winequality-white.csv")
df.head()
Opis dát nájdeme v prípade potreby v priloženom súbore:
with open("data/winequality/winequality", "r") as file:
print("".join(file.readlines()))
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
.
output_preproc = KBinsDiscretizer(3, encode='ordinal', strategy='quantile')
df['quality_quant'] = output_preproc.fit_transform(df[['quality']])
df[['quality', 'quality_quant']].head()
Dátovú množinu rozdelíme na tréningové, validačné a testovacie dáta; stratifikujeme podľa kvality.
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)
Ď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.
df.columns
# 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:
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)
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)
Aplikujte na problém klasifikátor na báze neurónovej siete a otestujte pomocou testovacích dát jeho úspešnosť v zmysle:
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.
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.
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:
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.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ť.
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))
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.
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:
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
.
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!
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.
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.
from lightgbm import LGBMClassifier
lgb = LGBMClassifier()
lgb.fit(X_train, Y_train_raw.reshape(-1))
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'
)))