Keras LSTM con serie storiche 1D


10

Sto imparando a usare Keras e ho avuto un discreto successo con il mio set di dati con etichetta usando gli esempi di Deep Learning for Python di Chollet . Il set di dati è ~ 1000 serie storiche con lunghezza 3125 con 3 potenziali classi.

Vorrei andare oltre i livelli Dense di base che mi danno circa il 70% di tasso di previsione e il libro continua a discutere i livelli LSTM e RNN.

Tutti gli esempi sembrano utilizzare set di dati con più funzionalità per ogni serie di eventi e di conseguenza faccio fatica a capire come implementare i miei dati.

Se, ad esempio, ho serie temporali 1000x3125, come posso inserirle in qualcosa come il livello SimpleRNN o LSTM? Mi manca una conoscenza di base di ciò che fanno questi strati?

Codice attuale:

import pandas as pd
import numpy as np
import os
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM, Dropout, SimpleRNN, Embedding, Reshape
from keras.utils import to_categorical
from keras import regularizers
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

def readData():
    # Get labels from the labels.txt file
    labels = pd.read_csv('labels.txt', header = None)
    labels = labels.values
    labels = labels-1
    print('One Hot Encoding Data...')
    labels = to_categorical(labels)

    data = pd.read_csv('ts.txt', header = None)

    return data, labels

print('Reading data...')
data, labels = readData()

print('Splitting Data')
data_train, data_test, labels_train, labels_test = train_test_split(data, labels)

print('Building Model...')
#Create model
model = Sequential()
## LSTM / RNN goes here ##
model.add(Dense(3, activation='softmax'))

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

print('Training NN...')
history = model.fit(data_train, labels_train, epochs=1000, batch_size=50,
    validation_split=0.25,verbose=2)

results = model.evaluate(data_test, labels_test)

predictions = model.predict(data_test)

print(predictions[0].shape)
print(np.sum(predictions[0]))
print(np.argmax(predictions[0]))

print(results)

acc = history.history['acc']
val_acc = history.history['val_acc']
epochs = range(1, len(acc) + 1)

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and Validation Accuracy')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()

Risposte:


10

I livelli LSTM richiedono dati di forma diversa.

Dalla tua descrizione, ho capito che il set di dati iniziale ha 3125 righe e 1000 colonne, in cui ogni riga è un passo temporale. La variabile target dovrebbe quindi avere 3125 righe e 1 colonna, dove ogni valore può essere uno dei tre valori possibili. Quindi sembra che tu stia facendo un problema di classificazione. Per verificare questo nel codice, farei:

>>> X.shape
(3125, 1000)

>>> y.shape
(1000,)

La classe LSTM richiede che ogni singolo campione sia costituito da un "blocco" di tempo. Diciamo che vuoi avere un blocco di 100 passi temporali. Questo significa che X[0:100]è un singolo campione di input, che corrisponde alla variabile target in y[100]. ciò significa che la dimensione della finestra (ovvero il numero di passi temporali o il numero di ritardi) è pari a 100. Come indicato sopra, hai 3125 campioni, quindi N = 3125. Per formare il primo blocco, purtroppo dobbiamo scartare i primi 100 campioni di y, poiché non possiamo formare un intero blocco di 100 dai dati disponibili (finiremmo per aver bisogno dei punti dati prima X[0]).

Alla luce di tutto ciò, un LSTM richiede di consegnare lotti di forme (N - window_size, window_size, num_features), che si traducono in (3125 - 100, 100, 1000)== (3025, 100, 1000).

La creazione di questi periodi di tempo è un po 'una seccatura, ma crea una buona funzione una volta, quindi salvala :)

C'è ancora molto lavoro da fare, forse guarda esempi più approfonditi della mia spiegazione qui sopra ... o leggi la documentazione di LSTM (o meglio, il codice sorgente! ).

Il modello finale sarebbe quindi abbastanza semplice (basato sul tuo codice):

#Create model
model = Sequential()
model.add(LSTM(units=32, activation='relu',
               input_shape=(100, 1000))    # the batch size is neglected!
model.add(Dense(3, activation='softmax'))

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

Dai un'occhiata alla documentazione relativa alla forma di input per il Sequentialmodello . Sostanzialmente afferma che non è necessario specificare il numero di lotti all'interno input_shape. Può essere fatto ad es batch_size=50. Se si richiede che sia un numero fisso.

So che l' input_shapeargomento non è nella documentazione per LSTM, ma la classe stessa eredita RNN, che a sua volta eredita da Layer- quindi sarà in grado di utilizzare le informazioni fornite.

Un ultimo suggerimento: se prevedi di aggiungere diversi livelli LSTM ("impilandoli"), dovrai aggiungere un altro argomento a tutti tranne l' ultimo LSTM , vale a dire il return_sequences=True.


Grazie per la risposta esaustiva Dexter (!). Per quanto riguarda i tuoi commenti sulla dimensione del batch, batch_size specificato nell'argomento model.fit è un iperparametro diverso rispetto alla creazione del mio batch personalizzato? Sono riuscito a far funzionare il mio codice almeno rimodellando i miei dati da una matrice 1000x3125 in una matrice 3D usando data = np.reshape (data, (1000,1,3125)). Questo mi ha permesso di eseguire LSTM con input_shape (1.3125) ma, di nuovo, non sono proprio sicuro di quello che sto facendo. Ancora una volta, grazie mille per la risposta. Daremo un'occhiata ai link che hai fornito e studierò la tua risposta ancora.
user1147964

Prego! Sì, ce l'hai, se lasci fuori batch_sizequando definisci il modello, sarà preso dallo stesso argomento all'interno model.fit(). Dovresti rimodellare per ottenere (3025, 100, 1000), il che significa 3025 lotti, ognuno di 100 (righe) timestep e 1000 (colonne) variabili. L'uso np.reshapenon funzionerà purtroppo per questo (otterrai un errore), a causa del fatto che avrai sovrapposizioni di dati ... la forma finale ha più dati dell'input. 3025x100x1000> 3125x1000 - np.reshapenon mi piace perché è ambiguo. Suggerisco semplicemente di eseguire il ciclo sull'insieme di dati, 1 loop = 1 campione.
n1k31t4

Penso di essere un po 'confuso qui e potrebbe essere perché potrei aver inavvertitamente già fatto il processo di batch. Utilizzerò valori specifici qui. Ho campionato 3 diverse misurazioni a 6,25 kHz per circa 3 minuti, ottenendo 3 serie temporali di lunghezza 1093750. Ciò genera una matrice 3x1093750. Ho quindi segmentato ogni TS in incrementi di 0,5 secondi, risultando in una matrice 1050x3125. Potrei tecnicamente ristrutturarlo in una matrice 3D con dimensioni 3x350x3125. Questo mi dà "lotti" lunghi 350, 0,5 secondi. Il tuo rimodellamento sembra generare molti più valori Grazie ancora per la risposta. Siamo spiacenti
user1147964

Solo per aggiungere, leggere il primo link che hai pubblicato mi fa pensare che sto rimodellando le cose correttamente. Mi scuso se mi manca qualcosa di ovvio, ma qui iniziano con un TS lunghezza 5000 e lo trasforma in una matrice 3D con dimensioni [1 25 200].
user1147964

Rispetto al metodo nel tuo link, il mio modo creerà molti più esempi. Questo perché sto usando una specie di finestra "scorrevole". Dai un'occhiata a questa rappresentazione . Non usano una finestra mobile . Fare 3 minuti in pezzi di 350x0.5s è ok (forse non è necessario, quanto spesso prevedi?), Ogni pezzo dovrebbe essere 3x3125. "Potrei ristrutturarlo in una matrice 3D con dimensioni 3x350x3125" - questo suona meglio, ma dopo aver effettuato le divisioni mi aspetterei 350x3x3125 (350 pezzi di 3x3125). Ognuno di questi blocchi potrebbe quindi essere elaborato come ho descritto.
n1k31t4,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.