Az előző részben megnéztük a Keras alapjait. Majd megformáztuk a megfigyeléseinket a keras.layers.LSTM() elvárásainak megfelelően, mind „stateless” mind „stateful” formában. A mai részben megnézzük, hogy tanítjuk ezeket a modelleket és össze fogjuk hasonlítani az eredményüket.
Stateful modell
Kezdjük a stateful modellel. Először is hozzunk létre egy stateful Keras modellt. Ehhez két réteget kell definiálunk: a HMM réteget, és a Kimeneti réteget. A Hosszú munkamemóriájú rétegnél, négy dolgot kell beállítani:
- Menyi párhuzamos HMM cella lesz a rétegben, ennek a száma hasonló elvet követ, mint a hagyományos Feed Forward Hálózat Rejtett rétegének neuronszámának meghatározása.
- input_shape — a réteg Bemeneti réteg felé mutató kapcsolatainak alakja. Ez egy tuple objektum aminek két eleme van:
- timestemps — a formázott adatok 2. dimenziója.
- input_dim — a formázott adatok 3. dimenziója.
- batch_input_shape — ez egy olyan argumentum amit csak stateful esetben kell megadnunk. Ez szintén egy tuple objektum, aminek három eleme van.
- batch_size — Ezt általában 1-re állítjuk. Ez adja meg a Kerasnak, hogy menyi mintát dolgozzon fel Visszajátszás előtt. Azért szoktuk 1-re állítani mert így nem kell a későbbiekben trükközni a teszt és a tréning adatok eltérő hosszúsága miatt. Ugyanis a modell batch_size értékének egy olyan számnak kell lennie, ami közös osztója mint a tréning mint a teszt adatok batch_size méretének.
- timestemps — ugyanaz mint a input_shape esetén
- input_dim — ugyanaz mint a input_shape esetén
- stateful — egy Bollean változó, hogy a modell stateful vagy nem. Értelemszerűen nekünk itt True-ra kell állítanunk.
A következő réteg a Kimeneti réteg lesz. A mi esetünkben ez egy egyszerű teljesen csatolt réteg aminek egy kimeneti neuronja van. Csak egyetlen elvárt értéket akarunk előrejelezni és regressziós problémával állunk szemben.
Az alábbi kód 14. sorában pedig beállítjuk, hogy számítjuk a Loss-t (esetünkben „mean squared error”) és milyen optimalizációs algoritmust (Adam) használunk:
from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM batch_size = 1 model_stateful = Sequential() model_stateful.add(LSTM(20, input_shape=(timesteps, input_dim), batch_input_shape=(batch_size,timesteps,input_dim), stateful=True)) model_stateful.add(Dense(1)) model_stateful.compile(loss='mse', optimizer='adam')
Most már csak tanítani kell az adatokat. A bemeneti adatoktól függően persze néha szükséges még minimum-maximumot skálázni és one-hot kódolni a kategória változókat, de a mi példánk esetén ettől eltekinthetünk:
# hol vágjuk fel teszt és tréning adatokra a megfigyeléseket cutoff = int(len(df_stateful_elso)*0.8) megfigyelesek = [( df_stateful_elso, elso_megf_y), (df_stateful_masodik, masodik_megf_y )] test = [] histories = [] # végiglépkedünk a megfigyeléseken for m in range(len(megfigyelesek)): # teszt és tréning adatokra vágjuk a megfigyeléseket train = megfigyelesek[m][0][:cutoff] test_m = megfigyelesek[m][0][cutoff:] test.append( ( test_m, megfigyelesek[m][1][test_m.index] ) ) # tréning adatok formázása train_X = train.values train_y = megfigyelesek[m][1][train.index] train_X = train_X.reshape((-1, timesteps, input_dim)) # tanulás epochs = 200 for epoch in range(epochs): print('Epoch', epoch + 1, '/', epochs) history = model_stateful.fit(train_X, train_y, batch_size=batch_size, epochs=1, verbose=True, shuffle=False) histories.append(history) # új epoch vagy mefigyelés jön szóval kezdeti állapotba állítjuk az htx és a ctx-et model_stateful.reset_states()
Mielőtt tovább lépnénk figyeljünk meg néhány dolgot a fenti kódon.
Előszőr is, ahogy az előző részben említettem a stateful modell esetén, nekünk kell meghírni a htx és ctx inicializálást. Ezt pedig minden egyes új epoch, vagy új megfigyelés kezdete előtt meg kell manuálisan tennünk. Ez történik a fenti kód 32. sorában, a reset_states() hívásával.
Másodszor, a fit() hívásakor megőrizzük a megfigyelések sorrendjét. Ezt a shuffle változó False értékre állításával érjük el. A Keras alapértelmezetten összekeveri a bemeneti adatokat a batch_size tengely mentén, ami viszont teljesen tönkretenné a stateful modellünket.
Harmadszor, a fit() függvényben meg kell adni a batch_size értéket. E a modell definiálásakor említett 1 érték fentebb. Hogy miért, arról szintén fentebb van szó.
Sebesség terén ne várjunk csodákat a stateful modelltől. A fenti kód előrevetíti, hogy igen lassan fog futni. Minden egyes epoch során meg kell hívnunk a a fit() függvényt, ami sok erőforrást fog felemészteni.
A modell tréningje után már csak az maradt, hogy a teszt adatokon ellenőrizzük a pontosságát az előrejelzésnek:
test_X = (test[0][0]).values test_y = test[0][1] test_X = test_X.reshape((-1, timesteps, input_dim)) predict = model_stateful.predict(test_X, batch_size=batch_size)
Amit ábrázolhatunk is:
import matplotlib.pyplot as plt plt.clf() plt.plot(predict, label="előrejelzés") plt.plot(test_y, label="valóság") plt.legend() plt.xlabel("idő") plt.ylabel("érték") plt.show()

Mint fentebb is látható a stateful modell ránézésre elég jól megbirkózott a feladattal.
Stateless — timesteps == 2
Most lássuk a stateless modellt abban az esetben amikor a timesteps kettő, mint a fenti stateful esetben. Először is állítsuk elő a teszt és tréning adatokat:
megfigyelesek = [( df_stateless_elso, elso_megf_y), (df_stateless_masodik, masodik_megf_y )] train_X_stateless = [] train_y_stateless = [] test_X_stateless = [] test_y_stateless = [] for megfigyeles in megfigyelesek: train_t = megfigyeles[0][:cutoff] train_t_X = train_t.values train_y_stateless.append( megfigyeles[1][train_t.index] ) train_X_stateless.append( train_t_X.reshape((-1, timesteps, input_dim)) ) test_t = megfigyeles[0][cutoff:] test_t_X = test_t.values test_y_stateless.append( megfigyeles[1][test_t.index] ) test_X_stateless.append( test_t_X.reshape((-1, timesteps, input_dim)) ) train_X_stateless = np.concatenate(train_X_stateless) train_y_stateless = np.concatenate(train_y_stateless)
Majd definiáljuk a stateless Keras modellt:
from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM model_stateless = Sequential() model_stateless.add(LSTM(20, input_shape=(timesteps, input_dim), stateful=False)) model_stateless.add(Dense(1)) model_stateless.compile(loss='mse', optimizer='adam')
Mint látható csak a HMM rétegben van eltérés a kettő között. A stateless eset sokkal egyszerűbb.
Tanítsuk a modellünket:
model_stateless.fit(train_X_stateless, train_y_stateless, epochs=200, verbose=1, shuffle=False)
Végül pedig teszteljük:
test_X_stateless = np.concatenate(test_X_stateless) test_y_stateless = np.concatenate(test_y_stateless) predict = model_stateless.predict(test_X_stateless) import matplotlib.pyplot as plt plt.plot(predict[:100], label="előrejelzés") plt.plot(test_y_stateless[:100], label="valóság") plt.legend() plt.xlabel("idő") plt.ylabel("érték") plt.show()

Hát ez nem jött össze. Mint fentebb is látható, ahogy vártuk a stateless modellnek egyáltalán nem sikerült használható előrejelzést készítenie.
A Stateless modell — timesteps == 3
Mi történik ha növeljük a timesteps értéket a stateless modell esetén. Ha 3-ra állítjuk, vagyis amikor az egyes batch-okban már benne vannak azok az adatok, amik alapján a valós elvárt értékeket számítottuk. Akkor a stateless modell hirtelen megmutatja előnyeit. A teszt adatok eredménye ebben az esetben:

Ilyenkor a stateless messze pontosabb előrejelzést eredményez kb. 1% tréning idő alatt mint a stateful.
Halmozott HMM
Az eddigiek során olyan modelleket láttunk, amikben egy rejtett réteg volt, a HMM. Természetesen van lehetőség arra, hogy növeljük ezt a rétegszámot és több Hosszú Munkamemóriájú réteget helyezünk egymás után, így mélyítve a Neurális Hálózatunkat. Ehhez csak a return_sequences argumentumot igaz értékre kell állítanunk az utolsó rejtett réteget kivéve:
model_stateless = Sequential() model_stateless.add(LSTM(20, input_shape=(timesteps, input_dim), stateful=False, return_sequences=True)) model_stateless.add(LSTM(10, input_shape=(timesteps, input_dim), stateful=False, return_sequences=True)) model_stateless.add(LSTM(4, input_shape=(timesteps, input_dim), stateful=False)) model_stateless.add(Dense(1)) model_stateless.compile(loss='mse', optimizer='adam')
Összegzés
A fenti eredmények végkövetkeztetése, hogy ha sejtésünk sincs arról van-e időbeli kapcsolat az adatok között, és rengetek előforrásunk van, akkor használunk stateful modellt. Ezzel szemben ha tudjuk max milyen távolra akarunk visszanézni, akkor használjunk stateless modellt.