Keras LSTM mit 1D-Zeitreihen


10

Ich lerne, wie man Keras verwendet, und ich habe mit meinem beschrifteten Datensatz anhand der Beispiele in Chollets Deep Learning für Python vernünftigen Erfolg gehabt . Der Datensatz ist ~ 1000 Zeitreihen mit einer Länge von 3125 mit 3 möglichen Klassen.

Ich möchte über die grundlegenden dichten Schichten hinausgehen, die mir eine Vorhersagerate von etwa 70% geben, und das Buch geht weiter auf LSTM- und RNN-Schichten ein.

Alle Beispiele scheinen Datensätze mit mehreren Funktionen für jede Zeitreihe zu verwenden, und ich habe Schwierigkeiten, herauszufinden, wie ich meine Daten als Ergebnis implementieren kann.

Wenn ich zum Beispiel 1000x3125-Zeitreihen habe, wie kann ich diese in so etwas wie die SimpleRNN- oder LSTM-Ebene einspeisen? Fehlt mir ein grundlegendes Wissen darüber, was diese Schichten tun?

Aktueller Code:

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()

Antworten:


10

LSTM-Schichten erfordern Daten mit einer anderen Form.

Aus Ihrer Beschreibung geht hervor, dass der Startdatensatz 3125 Zeilen und 1000 Spalten enthält, wobei jede Zeile einen Zeitschritt darstellt. Die Zielvariable sollte dann 3125 Zeilen und 1 Spalte haben, wobei jeder Wert einer von drei möglichen Werten sein kann. Es hört sich also so an, als ob Sie ein Klassifizierungsproblem haben. Um dies im Code zu überprüfen, würde ich tun:

>>> X.shape
(3125, 1000)

>>> y.shape
(1000,)

Die LSTM-Klasse erfordert, dass jede einzelne Stichprobe aus einem Zeitblock besteht. Angenommen, Sie möchten einen Block mit 100 Zeitschritten haben. Dies bedeutet, dass X[0:100]es sich um eine einzelne Eingabestichprobe handelt, die der Zielvariablen bei entspricht y[100]. Dies bedeutet, dass Ihre Fenstergröße (auch bekannt als Anzahl der Zeitschritte oder Anzahl der Verzögerungen) gleich 100 ist. Wie oben angegeben, haben Sie also 3125 Beispiele N = 3125. Um den ersten Block zu bilden, müssen wir leider die ersten 100 Stichproben von verwerfen y, da wir aus den verfügbaren Daten keinen ganzen Block von 100 bilden können (wir würden am Ende vorher die Datenpunkte benötigen X[0]).

In Anbetracht dessen erfordert ein LSTM, dass Sie Formstapel liefern (N - window_size, window_size, num_features), was sich in (3125 - 100, 100, 1000)== übersetzt (3025, 100, 1000).

Das Erstellen dieser Zeitblöcke ist ein bisschen mühsam, aber erstellen Sie einmal eine gute Funktion und speichern Sie sie dann :)

Es gibt noch mehr zu tun, vielleicht sehen Sie sich hier die ausführlicheren Beispiele meiner Erklärung an ... oder lesen Sie die LSTM-Dokumentation (oder noch besser den Quellcode! ).

Das endgültige Modell wäre dann einfach genug (basierend auf Ihrem Code):

#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'])

Schauen Sie sich die Dokumentation zur Eingabeform für das SequentialModell an . Grundsätzlich heißt es, dass wir die Anzahl der darin enthaltenen Chargen nicht angeben müssen input_shape. Dies kann z. B. erfolgen batch_size=50, wenn Sie eine feste Nummer benötigen.

Ich weiß, dass das input_shapeArgument nicht in der Dokumentation für enthalten ist LSTM, aber die Klasse selbst erbt von RNN, was wiederum von erbt Layer- damit sie die von Ihnen angegebenen Informationen verwenden kann.

Ein letzter Tipp: Wenn Sie mehrere LSTM-Ebenen hinzufügen möchten ("stapeln"), müssen Sie allen außer dem letzten ein weiteres Argument hinzufügen LSTM, nämlich das return_sequences=True.


Vielen Dank für die umfassende Antwort Dexter (!). Ist die im Argument model.fit angegebene Chargengröße in Bezug auf Ihre Kommentare zur Stapelgröße ein anderer Hyperparameter als die Erstellung eines eigenen benutzerdefinierten Stapels? Ich habe es geschafft, meinen Code zumindest zum Laufen zu bringen, indem ich meine Daten aus einer 1000x3125-Matrix in eine 3D-Matrix mit data = np.reshape (data, (1000,1,3125)) umgeformt habe. Dadurch konnte ich das LSTM mit input_shape (1.3125) ausführen, aber ich bin mir auch nicht sicher, was ich tue. Nochmals vielen Dank für die Antwort. Ich werde mir die von Ihnen bereitgestellten Links ansehen und Ihre Antwort weiter studieren.
user1147964

Bitte! Ja, Sie haben es verstanden. Wenn Sie batch_sizedas Modell nicht definieren, wird es aus demselben Argument übernommen model.fit(). Sie sollten umformen, um (3025, 100, 1000)3025 Stapel mit jeweils 100 (Zeilen) Zeitschritten und 1000 (Spalten) Variablen zu erhalten. Die Verwendung np.reshapefunktioniert hier leider nicht (es wird eine Fehlermeldung angezeigt), da sich die Daten überlappen ... die endgültige Form enthält mehr Daten als die Eingabe. 3025x100x1000> 3125x1000 - np.reshapemag das nicht, da es mehrdeutig ist. Ich schlage vor, einfach den Datensatz zu durchlaufen, 1 Schleife = 1 Probe.
n1k31t4

Ich denke, ich bin hier etwas verwirrt und es könnte sein, dass ich versehentlich bereits den Stapelverarbeitungsprozess durchgeführt habe. Ich werde hier bestimmte Werte verwenden. Ich habe 3 verschiedene Messungen bei 6,25 kHz für ungefähr 3 Minuten abgetastet, was zu 3 Zeitreihen der Länge 1093750 führte. Dies erzeugt eine 3x1093750-Matrix. Ich segmentierte dann jeden TS in Schritten von 0,5 Sekunden, was zu einer 1050x3125-Matrix führte. Ich könnte dies technisch in eine 3D-Matrix mit den Abmessungen 3x350x3125 umstrukturieren. Dies gibt mir 350, 0,5s lange "Chargen". Ihre Umformung scheint viel mehr Werte zu generieren. Nochmals vielen Dank für die Antwort. Entschuldigung
user1147964

Wenn ich nur den ersten Link lese, den Sie gepostet haben, denke ich, dass ich die Dinge richtig umforme. Entschuldigung, wenn mir etwas Offensichtliches fehlt, aber hier beginnen sie mit einer TS-Länge von 5000 und verwandeln sie in eine 3D-Matrix mit Dimensionen [1 25 200].
user1147964

Im Vergleich zu der Methode in Ihrem Link werden auf meine Weise viel mehr Beispiele erstellt. Das liegt daran, dass ich eine Art "rollendes" Fenster benutze. Schauen Sie sich diese Darstellung an . Sie benutzen kein rollendes Fenster. Es ist in Ordnung, 3 Minuten in 350x0,5s-Blöcke zu verwandeln (möglicherweise nicht erforderlich - wie oft sagen Sie das voraus?). Jeder Block sollte 3x3125 sein. "Ich könnte dies in eine 3D-Matrix mit den Abmessungen 3x350x3125 umstrukturieren" - das klingt besser, aber nach dem Teilen würde ich 350x3x3125 (350 Stücke von 3x3125) erwarten. Jeder dieser Blöcke könnte dann wie von mir beschrieben verarbeitet werden.
n1k31t4
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.