# Import potrebných balíčkov
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import OneHotEncoder
from sklearn.metrics import accuracy_score, precision_score, recall_score
from IPython.display import Image
import keras.backend as K
from keras.models import Model
from keras.callbacks import EarlyStopping
from keras.datasets import mnist
from keras.layers import (Conv2D, MaxPooling2D, Flatten, Dense,
Dropout, Activation, Input)
from keras.utils import plot_model
Tento príklad bude ilustrovať, ako sa dá zostrojiť jednoduchá konvolučná sieť na klasifikáciu obrazu na dátovej množine MNIST obsahujúcej rukou písané číslice.
Začneme načítaním dátovej množiny MNIST. Tento krok bude veľmi jednoduchý, keďže keras
má presne na tento účel vstavanú hotovú funkciu. Dátová množina bude už rozdelená na tréningovú a testovaciu časť.
(X_train_raw, Y_train_raw), (X_test_raw, Y_test_raw)\
= mnist.load_data()
Zobrazme teraz niekoľko náhodne zvolených vzoriek z dátovej množiny.
num_rows = 4; num_cols = 4
fig, axes = plt.subplots(num_rows, num_cols)
for row in axes:
for ax in row:
ax.imshow(X_train_raw[np.random.randint(0,
len(X_train_raw)-1)])
ax.set_xticks([])
ax.set_yticks([])
Teraz budeme dáta konvertovať na 32-bitové float-y a preškálujeme do rozsahu [0, 1] (t.j. delíme 255):
X_train = X_train_raw.astype('float32') / 255
X_test = X_test_raw.astype('float32') / 255
Tiež pridáme osobitný rozmer pre farebné kanály: napriek tomu, že máme len jeden.
X_train = X_train.reshape(X_train.shape + (1,))
X_test = X_test.reshape(X_test.shape + (1,))
Na výstupnú triedu aplikujemem kódovanie 1 z n.
encoder = OneHotEncoder(categories='auto', sparse=False)
Y_train = encoder.fit_transform(Y_train_raw.reshape((-1, 1)))
Y_test = encoder.transform(Y_test_raw.reshape((-1, 1)))
Skontrolujeme tvar dátovej množiny, aby sme zistili, aký rozmer má mať vstup a výstup neurónovej siete.
X_train.shape
Y_train.shape
Ako zvyčajne, sieť začneme zostavovať od vstupnej vrstvy. Naše obrázky sú rozmeru 28 x 28 a sú čierno-biele, čo znamená, že pracujeme len s jedným farebným kanálom.
y = x = Input(shape=(28, 28, 1))
Ďalej budeme na vstup aplikovať zopár konvolučných a združovacích vrstiev. Budeme ich reťaziť dovtedy, kým rozmer výstup nebude rozumne malý.
Bude tiež treba špecifikovať počet filtrov (buď vyskúšame nejakú ľubovoľnú hodnotu a dúfame, že bude fungovať alebo môžeme použiť niektorú metódu na ladenie hyperparametrov, napr. bayesovskú optimalizáciu).
Treba tiež špecifikovať veľkosť konvolučného jadra. Jadro veľkosti 3 x 3 väčšinou funguje dobre. Padding nastavíme na 'valid' (podrobnejšie vysvetlenie nájdete v slajdoch).
y = Conv2D(filters=32,
kernel_size=(3, 3),
padding='valid')(y)
y = Activation('relu')(y)
y = MaxPooling2D(pool_size=(2, 2))(y)
Teraz znovu skontrolujeme rozmer výstupu, aby sme zistili, aký veľký je.
shape = K.int_shape(y)
size = np.product(shape[1:])
print("Shape: {}. Total size: {}".format(shape, size))
To je stále dosť veľký výstup. Pridajme ešte jednu vrstvu.
y = Conv2D(filters=16,
kernel_size=(3, 3),
padding='valid')(y)
y = Activation('relu')(y)
y = MaxPooling2D(pool_size=(2, 2))(y)
shape = K.int_shape(y)
size = np.product(shape[1:])
print("Shape: {}. Total size: {}".format(shape, size))
To už je rozumnejšia veľkosť. Teraz zalomme operáciou Flatten
príslušnú maticu do vektora a aplikujme naň ešte niekoľko úplne prepojených vrstiev.
y = Flatten()(y)
y = Dense(128)(y)
y = Activation('relu')(y)
Nasleduje už len výstupná vrstva, ktorá má toľko neurónov, koľko je tried, a aktivačnú funkciu softmax.
num_classes = 10
y = Dense(num_classes)(y)
y = Activation('softmax')(y)
Vytvoríme a skompilujeme model.
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:
plot_model(model, "model.png", )
Image(retina=True, filename='model.png')
model.fit(X_train, Y_train, epochs=5, batch_size=128)
Teraz model otestujeme na testovacích dátach.
y_test_raw = model.predict(X_test)
y_test = encoder.inverse_transform(y_test_raw)
cm = pd.crosstab(Y_test_raw.reshape(-1),
y_test.reshape(-1),
rownames=['actual'],
colnames=['predicted'])
sns.heatmap(cm.values, annot=True, cmap='coolwarm')
plt.xlabel("predicted")
plt.ylabel("actual")
print("Správnosť: {}.".format(accuracy_score(
Y_test_raw, y_test
)))
print("Presnosť: {}.".format(precision_score(
Y_test_raw, y_test, average='macro'
)))
print("Úplnosť: {}.".format(recall_score(
Y_test_raw, y_test, average='macro'
)))
Náš klasifikátor ako tak funguje, ale výsledky nie sú príliš pôsobivé: mohol by zovšeobecňovať aj lepšie.
validation_split
funkcie fit
).