In [0]:
# Import potrebných balíčkov
import pandas as pd
import numpy as np
from keras.models import Model
from keras.layers import Dense, Input, Activation
from keras.callbacks import Callback
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from keras.utils import plot_model
from IPython.display import Image
In [0]:
# Uistíme sa, že máme všetky potrebné dáta
!mkdir -p data
!wget -nc -O data/iris.csv https://www.dropbox.com/s/v3ptdkv5fvmx5zk/iris.csv?dl=1
File ‘data/iris.csv’ already there; not retrieving.
In [0]:
# Pomocný kód
class NEpochLogger(Callback):
    """
    Trieda na menej časté zobrazovanie priebehu učenia.
    """
    def __init__(self, n_epochs=100):
        super(NEpochLogger, self).__init__()
        self.n_epochs = n_epochs

    def on_epoch_end(self, epoch, logs=None):
        logs = logs or {}
        
        if epoch % self.n_epochs == 0:
            curr_loss = logs.get('loss')
            curr_acc = logs.get('acc') * 100
            print("epoch = {}; loss = {}; acc = {}".format(
                epoch, curr_loss, curr_acc))

Klasifikácia pomocou umelých neurónových sietí

Tento notebook ukazuje, ako sa dá neurónová sieť zostrojená pomocou pythonového balíčka keras aplikovať na jednoduchú klasifikačnú úlohu. Ukážeme, ako sa dá taká sieť vytvoriť a natrénovať. Budeme používať veľmi jednoduchú architektúru – bez konvolučných vrstiev, dávkovej normalizácie a iných podobných špeciálnych vrstiev.

Načítanie dátovej množiny

Ako prvý krok načítame dátovú množinu IRIS:

In [0]:
df = pd.read_csv("data/iris.csv")
df.head()
Out[0]:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm) species
0 5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
2 4.7 3.2 1.3 0.2 setosa
3 4.6 3.1 1.5 0.2 setosa
4 5.0 3.6 1.4 0.2 setosa

Dáta rozdelíme na tréningové a testovacie. 75% dát použijeme na učenie a 25% na testovanie. Aplikujeme tiež stratifikáciu podľa stĺpca species, ktorý obsahuje označenie triedy (pomer tried v oboch častiach dátovej množiny zostane rovnaký, ako bol v pôvodnej dátovej množine).

In [0]:
df_train, df_test = train_test_split(df, test_size=0.25,
                                     stratify=df['species'],
                                     random_state=4)

Prvé 4 stĺpce sú vstupy, posledný výstup:

In [0]:
inputs = df.columns[:4]
output = df.columns[-1:]

X_train_raw = df_train[inputs]
Y_train_l = df_train[output]

X_test_raw = df_test[inputs]
Y_test_l = df_test[output]

Vytvoríme pipeline na predspracovanie. Všetky 4 vstupy budeme štandardizovať.

In [0]:
input_preproc = StandardScaler()
X_train = input_preproc.fit_transform(X_train_raw)
X_test = input_preproc.transform(X_test_raw)

Triedy prekódujeme do reprezentácie 1 z n (one-hot encoding).

In [0]:
output_preproc = OneHotEncoder(sparse=False)
Y_train = output_preproc.fit_transform(Y_train_l)
Y_test = output_preproc.transform(Y_test_l)

Vytvorenie siete

Sieť bude mať toľko vstupov, koľko je stĺpcov v X. Výstupov bude zase toľko, koľko je tried (čo je to isté ako počet stĺpcov v Y).

In [0]:
num_inputs = X_train.shape[1]
num_outputs = Y_train.shape[1]

Teraz začneme vytvárať samotnú sieť. Začneme vstupnou vrstvou, ktorá bude mať num_inputs neurónov:

In [0]:
y = x = Input(shape=(num_inputs, ))

Ďalej pridáme niekoľko úplne prepojených vrstiev s aktivačnou funkciou ReLU. Všimnite si, ako dávame y na vstup každej vrstvy a tiež doňho priraďujeme výstup. Takýmto spôsobom vrstvy zreťazujeme.

In [0]:
# Prvá úplne prepojená vrstva + ReLU.
y = Dense(3)(y)
y = Activation('relu')(y)
# Druhá úplne prepojená vrstva + ReLU.
y = Dense(3)(y)
y = Activation('relu')(y)

Ďalej pridáme výstupnú vrstvu. Ako vieme, potrebujeme použiť aktivačnú funkciu softmax, aby sa všetky výstupy sčítali na 1 a dali sa interpretovať ako pravdepodobnosti.

In [0]:
y = Dense(num_outputs)(y)
y = Activation('softmax')(y)

Napokon sme pripravení vytvoriť aj samotný model. Povieme, aké sú jeho vstupy a výstupy. Následne ho skompilujeme, pričom špecifikujeme chybovú funkciu a optimalizačnú metódu, ktorú treba použiť. Pridaním 'accuracy' (správnosti) medzi metriky docielime, že sa nám počas učenia bude správnosť zobrazovať.

In [0]:
model = Model(x, y)
model.compile(loss='categorical_crossentropy',
              optimizer="adam", metrics=['accuracy'])

S použitím pomocnej funkcie plot_model si môžeme vizualizovať architektúru vytvorenej siete:

In [0]:
plot_model(model, "model.png", )
Image(retina=True, filename='model.png')
Out[0]:

Učenie

Potom ako sme skonštruovali model, môžeme pristúpiť ku samotnému učeniu z tréningových dát. Špecifikujeme počet epoch učenia a veľkosť minidávok.

So sieťou a dátovou množinou týchto rozmerov by učenie nemalo trvať príliš dlho. Na väčšie siete a dátové množiny bude ale treba použiť GPU, ktoré vie učenie značne urýchliť.

In [0]:
model.fit(X_train, Y_train, epochs=2000, batch_size=64,
          verbose=0, callbacks=[NEpochLogger()])
epoch = 0; loss = 1.2011532613209315; acc = 0.8928571428571428
epoch = 100; loss = 0.797002341066088; acc = 87.49999914850507
epoch = 200; loss = 0.5064345683370318; acc = 90.17857142857143
epoch = 300; loss = 0.37383386492729187; acc = 90.17857142857143
epoch = 400; loss = 0.2884487382003239; acc = 92.85714285714286
epoch = 500; loss = 0.21816067823341914; acc = 95.53571343421936
epoch = 600; loss = 0.16964708268642426; acc = 97.3214294229235
epoch = 700; loss = 0.13942675718239375; acc = 97.32142771993365
epoch = 800; loss = 0.11795439038957868; acc = 97.3214294229235
epoch = 900; loss = 0.10226033734423774; acc = 97.32142771993365
epoch = 1000; loss = 0.09027210410152163; acc = 97.3214294229235
epoch = 1100; loss = 0.08088834903069905; acc = 98.21428656578064
epoch = 1200; loss = 0.07330047392419406; acc = 98.21428571428571
epoch = 1300; loss = 0.06710802018642426; acc = 98.21428486279079
epoch = 1400; loss = 0.06205291620322636; acc = 99.10714285714286
epoch = 1500; loss = 0.05773248417036874; acc = 99.10714285714286
epoch = 1600; loss = 0.05403486319950649; acc = 99.10714370863778
epoch = 1700; loss = 0.05082729937774794; acc = 99.10714370863778
epoch = 1800; loss = 0.047992883516209464; acc = 99.10714285714286
epoch = 1900; loss = 0.04547827797276633; acc = 99.10714370863778
Out[0]:
<keras.callbacks.History at 0x7f3ec49da2e8>

Testovanie

Keď sme model natrénovali, potrebujeme ho ešte otestovať na testovacích dátach, aby sme verifikovali, či správne zovšeobecňuje. Spustíme teda model na testovacích dátach.

In [0]:
y_test = model.predict(X_test)

Výsledné pravdepodobnosti transformujeme späť na označenia triedy:

In [0]:
y_test_l = output_preproc.inverse_transform(y_test)[:, 0]
Y_test_l = output_preproc.inverse_transform(Y_test)[:, 0]

Zobrazíme maticu zámen a vypočítame správnosť.

In [0]:
cm = pd.crosstab(Y_test_l, y_test_l,
                 rownames=['actual'],
                 colnames=['predicted'])
print(cm)
predicted   setosa  versicolor  virginica
actual                                   
setosa          13           0          0
versicolor       0          12          0
virginica        0           1         12
In [0]:
print("Správnosť: {}.".format(accuracy_score(
    Y_test_l, y_test_l
)))
Správnosť: 0.9736842105263158.
In [0]: