Çok Katmanlı LSTM Hakkında

Arkadaşlar herkese merhaba,

Ben LSTM üzerinde çalışma yapıyorum. Tek katmanlı olan LSTM modelimde sorun yok. Verileri alıyorum ve grafiklere dökebiliyorum. Ancak çok katmanlı LSTM modelini nasıl yapmam gerekiyor bulamadım. Bir şeyler yapmaya çalıştım ama hata aldım. Bu konuda yardımcı olabilir misiniz? Kodlarımı paylaşıyorum.

İlk önce kütüphanelerimi yükledim.

import os
import sys
os.chdir('/Users/onurk/Covid-19/')
import pandas as pd
import numpy as np
pd.options.display.max_columns=None
import plotly.graph_objects as go
import plotly.express as px
import matplotlib.pyplot as plt
import time
start = time.process_time()
from plotly.offline import init_notebook_mode, iplot
import warnings
from IPython.core.display import display, HTML
display(HTML("<style>.container { width:100% !important; }</style>"))
warnings.filterwarnings('ignore')
init_notebook_mode(connected=True)
import tensorflow as tf
tf.get_logger().setLevel('INFO')
tf.autograph.set_verbosity(0)
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import LSTM
from tensorflow.keras.callbacks import Callback, ModelCheckpoint, EarlyStopping, ReduceLROnPlateau

Fonksiyonumu yazdım.

def plot_timeseries(x_axis,data,name):
    num_cols=data.shape[1]
    go_arr=[]
    for i in range(num_cols):
        go_arr.append(go.Scatter(x=x_axis, y=data.iloc[:,i],name=data.columns[i]))   
    fig=go.Figure(go_arr)

    fig.update_layout(
    title=name,font=dict(
        family="Courier New, monospace",
        color="#7f7f6a"))
    fig.show()
    

def plot_lines(df, title='', annotations=None):
    common_kw = dict(x=df.index, mode='lines+markers')
    xaxis = dict(title='Time Steps')
    data=[]
    for c in df.columns:
        data.append(go.Scatter(y=df[c], name=c, **common_kw))

    layout = dict(title=title, showlegend=True, annotations=annotations, xaxis=xaxis)
    fig=dict(data=data, layout=layout)
    iplot(fig,show_link=True)

Verilerimi internetten çekerek yükledim.

agir_hasta_sayisi_data_df=pd.read_csv('http://facadium.com.tr/lstm_dataset/agir_hasta_sayisi.csv')
toplam_iyilesen_hasta_sayisi_data_df=pd.read_csv('http://facadium.com.tr/lstm_dataset/toplam_iyilesen_hasta_sayisi.csv')
toplam_vefat_sayisi_data_df=pd.read_csv('http://facadium.com.tr/lstm_dataset/toplam_vefat_sayisi.csv')
toplam_vaka_sayisi_data_df=pd.read_csv('http://facadium.com.tr/lstm_dataset/toplam_vaka_sayisi.csv')
toplam_test_sayisi_data_df=pd.read_csv('http://facadium.com.tr/lstm_dataset/toplam_test_sayisi.csv')
country_pred_df=pd.DataFrame()
model_performance_df=pd.DataFrame()

def get_time_series_data(country_name):
    
    grouped_agir_hasta_sayisi_df=agir_hasta_sayisi_data_df.groupby('Country/Region')
    grouped_toplam_iyilesen_hasta_sayisi_df=toplam_iyilesen_hasta_sayisi_data_df.groupby('Country/Region')
    grouped_toplam_vefat_sayisi_df=toplam_vefat_sayisi_data_df.groupby('Country/Region')
    grouped_toplam_vaka_sayisi_df=toplam_vaka_sayisi_data_df.groupby('Country/Region')
    grouped_toplam_test_sayisi_df=toplam_test_sayisi_data_df.groupby('Country/Region')

    country_agir_hasta_sayisi_df=grouped_agir_hasta_sayisi_df.get_group(country_name)
    country_toplam_iyilesen_hasta_sayisi_df=grouped_toplam_iyilesen_hasta_sayisi_df.get_group(country_name)
    country_toplam_vefat_sayisi_df=grouped_toplam_vefat_sayisi_df.get_group(country_name)
    country_toplam_vaka_sayisi_df=grouped_toplam_vaka_sayisi_df.get_group(country_name)
    country_toplam_test_sayisi_df=grouped_toplam_test_sayisi_df.get_group(country_name)
    
    cleaned_agir_hasta_sayisi_df=country_agir_hasta_sayisi_df.drop(columns=['Province/State','Lat','Long']).sum(axis=0,skipna=True).to_frame()[1:]
    cleaned_toplam_iyilesen_hasta_sayisi_df=country_toplam_iyilesen_hasta_sayisi_df.drop(columns=['Province/State','Lat','Long']).sum(axis=0,skipna=True).to_frame()[1:]
    cleaned_toplam_vefat_sayisi_df=country_toplam_vefat_sayisi_df.drop(columns=['Province/State','Lat','Long']).sum(axis=0,skipna=True).to_frame()[1:]
    cleaned_toplam_vaka_sayisi_df=country_toplam_vaka_sayisi_df.drop(columns=['Province/State','Lat','Long']).sum(axis=0,skipna=True).to_frame()[1:]
    cleaned_toplam_test_sayisi_df=country_toplam_test_sayisi_df.drop(columns=['Province/State','Lat','Long']).sum(axis=0,skipna=True).to_frame()[1:]
    
    ts_df=pd.DataFrame(cleaned_agir_hasta_sayisi_df.values,columns=['agir_hasta_sayisi'])
    ts_df['toplam_iyilesen_hasta_sayisi']=cleaned_toplam_iyilesen_hasta_sayisi_df.values
    ts_df['toplam_vefat_sayisi']=cleaned_toplam_vefat_sayisi_df.values
    ts_df['toplam_vaka_sayisi']=cleaned_toplam_vaka_sayisi_df.values
    ts_df['toplam_test_sayisi']=cleaned_toplam_test_sayisi_df.values
    
    ts_df.index=cleaned_agir_hasta_sayisi_df.index
    return ts_df
def plot(ts_df,country_name):
    plot_timeseries(x_axis=ts_df.index,data=ts_df[['agir_hasta_sayisi','toplam_iyilesen_hasta_sayisi','toplam_vefat_sayisi','toplam_vaka_sayisi','toplam_test_sayisi']],name='Degerlerin Karşılaştırması '+country_name)

def split_sequence(sequence, n_steps):
    X, y = list(), list()
    for i in range(len(sequence)):
        end_ix = i + n_steps
        if end_ix > len(sequence)-1:
            break
        seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
        X.append(seq_x)
        y.append(seq_y)
    return np.asarray(X), np.asarray(y) 

Bu aşamada bir yanlışlık yaptığımın farkındayım. Ancak hatam ne onu bilemiyorum.

def build_model_and_predict(ts_df, country_name):
    n_steps = 4  
    n_features = 1
    n_test = 4
    X, Y = split_sequence(ts_df.agir_hasta_sayisi.values.astype('float32'), n_steps)
    X = X.reshape((X.shape[0], X.shape[1], n_features))

    X_train, X_test, Y_train, Y_test = X[:-n_test], X[-n_test:], Y[:-n_test], Y[-n_test:]  
    c = [
        ModelCheckpoint(country_name+'.hdf5', save_best_only=True, monitor='val_loss', mode='min', verbose=0, save_freq=1),
        EarlyStopping(monitor='val_loss', min_delta=0, patience=600, verbose=0),
        ReduceLROnPlateau(monitor='val_loss',factor=0.2,patience=5,min_lr=0.00001)
    ]
    model = Sequential()
    
    model.add(LSTM(100, activation='relu', kernel_initializer='he_normal', input_shape=(n_steps,1)))
    model.add(Dense(50, activation='relu', kernel_initializer='he_normal'))
    
    model.add(LSTM(100, activation='relu', kernel_initializer='he_normal'))
    model.add(Dense(50, activation='relu', kernel_initializer='he_normal'))
    
    model.add(LSTM(100, activation='relu', kernel_initializer='he_normal', ))
    model.add(Dense(50, activation='relu', kernel_initializer='he_normal'))
    
    model.add(Dense(1))


    model.compile(optimizer='adam', loss='mse', metrics=['mae','mse','accuracy'])

    history=model.fit(X_train, Y_train, epochs=4000, batch_size=32, verbose=0,callbacks=c,validation_data=(X_test, Y_test),shuffle=False)

    [mse, mae, loss, accuracy]= model.evaluate(X_test, Y_test, batch_size=32, verbose=0)

    model_performance=pd.DataFrame({'Country':[country_name],'mae':np.array(mae),'mse':np.array(mse),
                                    'rmse':np.array(np.sqrt(mse)),'accuracy':np.array(accuracy)})
    row = np.asarray(ts_df[-n_steps:].agir_hasta_sayisi.values.astype('float32')).reshape((1, n_steps, n_features))
    yhat = model.predict(row)
    return yhat, model_performance
ts_df=get_time_series_data('Turkiye')
plot(ts_df,'Turkiye')
def plot(agir_hasta_sayisi,country_name):
    plot_timeseries(x_axis=ts_df.index,data=ts_df[['agir_hasta_sayisi']],name='Ağır Hasta Sayısı Grafiği - '+country_name)
pd.set_option("display.max.rows", None)
agir_hasta_sayisi=get_time_series_data('Turkiye')
agir_hasta_sayisi
n_steps = 4 
n_features = 1
X, Y = split_sequence(ts_df.agir_hasta_sayisi.values.astype('int'), n_steps)
for i in range(len(X)):
    print(X[i], Y[i])
pd.set_option("display.max.rows", None)
toplam_test_sayisi=get_time_series_data('Turkiye')
toplam_test_sayisi
ts_df=get_time_series_data('Turkiye')
plot(toplam_test_sayisi,'Turkiye')
country_names=['Turkiye']
for country_name in country_names:
    ts_df=get_time_series_data(country_name)
    yhat, model_performance = build_model_and_predict(ts_df,country_name)
    prediction=round(yhat[0][0])
    model_performance_df=model_performance_df.append(model_performance)
    country_pred_df=country_pred_df.append(pd.DataFrame({'Turkiye':[country_name],'LastValue':ts_df[-1:].agir_hasta_sayisi.values,'PredictedValue':np.array(prediction)}))

İşte bu aşamadan sonra hata alıyorum. Eğer 3 katmanlı yapmak istemeseydim o zaman hata almadan ilerleyebiliyordum. Ancak yukarıda ki kısımda 3 katmanlı yapmaya çalıştığımda hata alıyorum ve ilerleyemiyorum. 2 ve ya 3 katmanlı LSTM Modelini yapmak istediğimde ne yapmam gerekiyor bilemiyorum. Bu konuda yardımcı olabilir misiniz arkadaşlar ? Teşekkürler.

1 Beğeni
model = Sequential()
model.add(LSTM(..., return_sequences=True, input_shape=(...)))
model.add(LSTM(..., return_sequences=True))
model.add(LSTM(..., return_sequences=True))
model.add(LSTM(...))
model.add(Dense(...))

Dense’yi sona eklemeyi dene sadece.

Düzenleme: Başka bir sorun göremedim. Kaynak olarak buna bakabilirsin.

1 Beğeni

Çok teşekkür ederim yardımınız için. Ben aslında önceden bu şekilde yaptım diye hatırlıyorum ama hata almıştım. Şimdi siz paylaşınca sizin verdiğiniz kodlar ile çalıştım hata almadım. Acaba nerede hata yaptım bilmiyorum. Mantık olarak şu şekilde değil mi?

model = Sequential()
model.add(LSTM(..., return_sequences=True, input_shape=(...)))
model.add(LSTM(..., return_sequences=True))
model.add(LSTM(..., return_sequences=True))
model.add(LSTM(...))
model.add(Dense(...))

Yukarıdaki kod satırında kaç LSTM Katmanı ile çalışacak isek o kadar

model.add(LSTM(…, return_sequences=True))

satırı ekliyoruz. LSTM parantezi içerisine de nöron sayısını yazıyoruz. Ben yoksa yanlış mı biliyorum o yüzden mi yaptığım çalışma da hata yapmıştım bilemedim.

1 Beğeni

Ben hatamı anladım. En son kısımda olan

model.add(LSTM(…))

Kodunu yazmadığım zaman hata alıyorum.

Burada mesela 4 defa model.add kullanılmış ancak en son 4. model.add(LSTM(…)) Yazılmazsa hata veriyor. Bunun nedeni hakkında bilginiz var mı acaba?

Yani şu an burada 4 model.add(LSTM(…)) Kullanarak 4 katman yazmış oluyoruz değil mi? Ama en son katmanda model.add(LSTM(…)) Yazılmazsa neden hata veriyor onu bulamadım. Size danışmak istedim.

Geri gönderim yapıyorsun. Pek mantıken açık edebileceğim bir şey değil bende bilmiyorum tam olarak ama söylemek gerekirse.

Burada katman katman belirtiyorsun gibi düşün ilkini giriyorsun aşağıda olsun diyorsun 2. giriyorsun aşağıda 3. aşağıda. Sonuncusuna gelince katmanın görünür bir kısmı olması lazım üst tabaka gibi. Nasıl toprağın üzerinde çim varsa aynı görevi görüyorlar o şekilde.

O zaman

Burada siz 4 katman kullanmış oluyorsunuz değil mi? Çünkü 4 satır var. Yabancı kaynaklarda da en son satırı hep koymuşlar. En son satırın olmadığı bir örnek göremedim. O en son satir da katmana dahil oluyor değil mi? Yoksa en son satir hariç mi katman sayısı sayılıyor?

En sondakide katman sayılıyor diye biliyorum ben

1 Beğeni

model.summary belki yararlı olabilir:

Sondaki return_sequences=False ile:

In []: 
   ...: model = Sequential()
   ...: model.add(LSTM(100, return_sequences=True, input_shape=(7, 3)))
   ...: model.add(LSTM(50, return_sequences=True))
   ...: model.add(LSTM(25))
   ...:
   ...: model.add(Dense(1))

In []: model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
lstm_1 (LSTM)                (None, 7, 100)            41600
_________________________________________________________________
lstm_2 (LSTM)                (None, 7, 50)             30200
_________________________________________________________________
lstm_3 (LSTM)                (None, 25)                7600
_________________________________________________________________
dense (Dense)                (None, 1)                 26
=================================================================
Total params: 79,426
Trainable params: 79,426
Non-trainable params: 0

Sondaki return_sequences=True ile:

In []: 
   ...: model = Sequential()
   ...: model.add(LSTM(100, return_sequences=True, input_shape=(7, 3)))
   ...: model.add(LSTM(50, return_sequences=True))
   ...: model.add(LSTM(25, return_sequences=True))
   ...:
   ...: model.add(Dense(1))

In []: model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
lstm_1 (LSTM)                (None, 7, 100)            41600
_________________________________________________________________
lstm_2 (LSTM)                (None, 7, 50)             30200
_________________________________________________________________
lstm_3 (LSTM)                (None, 7, 25)             7600
_________________________________________________________________
dense_1 (Dense)              (None, 7, 1)              26
=================================================================
Total params: 79,426
Trainable params: 79,426
Non-trainable params: 0

En sondaki Dense layer’ın çıktısının şekline bakarsanız ilkinde (1,) ikincisinde (7, 1) olduğunu görüyoruz (None ile ifade edilen batch size boyutunu saymazsak). Yani ilkinde model “1 tek sayı” çıktı veriyor, ikincisinde ise “7 elemanlı bir vektör”. Amacınız galiba bir tek sayı tahmin etmek, dolayısıyla arkadan gelen işlemler istenilen gibi çalışmıyor olabilir. (“olabilir”, çünkü aldığınız hatayı paylaşmamışsınız ya da ben fark edemedim).

Peki neden? return_sequences ne yapıyor?

Bu figürde yukarıdali modeli resmetmeye çalışıyoruz. İlk başta input layer’ı var. Şeklini (7, 3) dedik; bu (sequence length, number of features)'a denk gelir. Sizde mesela (n_steps, 1) yazıyor. Yani LSTM n_steps geriye bakıyor ve her bir cell’e 1 boyutlu input besleniyor en alltan (x_i (elem) R^1). Bu arada, figürdeki içinde 100, 50, 25 yazan her bir kutu bir LSTMCell’e tekabül ediyor; LSTM figürdeki gibi roll-out edilir gibi düşünülüyor. İçerlerindeki sayılar da o cell’de kullanılan hidden unit sayısını söylüyor.

Sonrasında birbiri ardına LSTM katmanları görüyoruz. return_sequences=True demekle iki LSTM’İ birbirine bağlamış oluyoruz. Her iki LSTM katmanı arasındaki yukarı oklar bu return_sequences=True'nun sayesinde. Sanki LSTM 2’nin input’ları LSTM 1’in output’u gibi düşünüyoruz. Benzer durum 2’den 3’e geçişte de var.

Son LSTM katmanına geldiğimizde return_sequences=True demekle dememek arasındaki farkı figürdeki kesik çizgili oklarla anlayabiliriz. Demediğimizde yalnız en sondaki LSTMCell’in output’u Dense layer’a gönderilir; öbür türlü ise tüm LSTMCell’lerin çıktıları (ayrı ayrı) Dense layer’a gönderilir.

Dense layer’ın amacı ne? Nihai amacımız tek bir sayı üreterek tahmin yapmak, değil mi? İşte son LSTM katmanından gelen 25 boyutlu vektörü bu 25 parametreli dense layer ile tek sayıya indiriyoruz (yaptığı şey iç-çarpım). Dolayısıyla LSTM’lerin sonunda regresyonda 1 çıktı üniteli Dense, klasifikasyonda ise sınıf sayısı kadar çıktı ünitesine sahip Dense kullanmak yaygındır.

Peki Dense nasıl oluyor da 1 tane de 25’lik vektör gelse de 7 tane 25’lik gelse de sorun çıkarmıyor? Keras’taki (ve bilumum diğer framework’lerdeki) Dense layer, gelen inputun son boyutu üzerinden işlem yapar. Yani siz ona beş boyutlu (5, 12, 13, 45, 9)'luk vektör verseniz o size geriye (5, 12, 13, 45, *1*)'lik çıktı verir: ilk N-1 boyuttaki elemanlar olduğu gibi kalıyor, son boyuttaki eleman iç-çarpımlar ile istenen çıktı sayısına indirgeniyor (burada o sayı 1). Dolayısıyla (25,) verdiğimizde de (1,) alırız. Velhasıl, return_sequences=True ile oraya (7, 25)'lik gidip (7, 1) çıkıyor (7 yuvarlak figürdeki); False olduğunda ise (25,) gelip (1,) gidiyor (en sondaki kalın yuvarlak); bunlar model.summary()'de de görülüyor.


Uzun lafın kısası:

  • İki LSTM ardı ardına bağlanırken, keras’ta return_sequences=True kullanırız ilkinin tanımında ki tüm cell’ler output versin ve bunlar bi sonraki LSTM’in cell’lerine input olarak gitsin.

  • Sona gelindiğinde return_sequences=True olsa da olmasa da arkasına bağladığımız Dense layer sorun çıkarmaz (son boyut üzerinden işlediğinden). Ama True olduğunda (pek de anlamlı olmayan) bir vektör çıktısı elde ederiz; True ise de Dense’in çıktı ünitesi kadar boyutta bir output (büyük ihtimalle istenilen budur; yukarıda bu ünite sayısı 1). (İlk durumu seçip (7, 1)'lik çıktı alıp sonrasında bir “layer” daha koyarak bu vektördeki sayıların ortalamasını alarak bu vektörü de tek bir sayıya indirgeyebiliriz mesela; ama bunun olası yararlarının desteklenmesi gerekir diye düşünüyorum, bilemiyorum.)

  • Dense layer kullanarak, LSTM’in “özütlediği” hidden vektörlerden nihai amaca bir köprü kuruyoruz. Bu da network’ün bir parçasıdır ve parametreleri backpropagation ile öğrenilebilir. (Benzer durum CNN’lerde de filtreler/convolution’lar/pooling’ler bittikten sonra elde edilen (mesela bir resmin) "extracted feature"ları üzerine “fully connected layer” kurmayla sağlanır ve, örneğin, resmin 1000 kategoriden birinde sınıflandırılması sağlanır).

  • Yukarıdaki figürdeki model "3 katmanlı LSTM"e sahiptir.

3 Beğeni

Kralın dönüşü. Valla çok teşekkür ediyorum. Diğer sorumda da çok açıklayıcı bilgi vermiştiniz bu soruda da detaylıca bahsettiniz. Ben konuyu anladım. Size ve @Empera0 'ya yardımlarınızdan dolayı çok teşekkür ediyorum.