Keras LSTM z szeregami czasowymi 1D

10

Uczę się, jak korzystać z Keras i osiągnąłem znaczny sukces z moim oznaczonym zestawem danych, korzystając z przykładów z głębokiego uczenia się dla Pythona przez Cholleta . Zestaw danych to ~ 1000 szeregów czasowych o długości 3125 z 3 potencjalnymi klasami.

Chciałbym wyjść poza podstawowe warstwy Dense, które dają mi około 70% predykcji, a książka omawia warstwy LSTM i RNN.

Wydaje się, że wszystkie przykłady wykorzystują zestawy danych z wieloma funkcjami dla każdego szeregu czasowego i staram się wypracować, w jaki sposób zaimplementować moje dane.

Jeśli na przykład mam szereg czasowy 1000x3125, w jaki sposób mogę wprowadzić to do czegoś takiego jak warstwa SimpleRNN lub LSTM? Czy brakuje mi podstawowej wiedzy na temat tego, co robią te warstwy?

Aktualny kod:

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()
użytkownik1147964
źródło

Odpowiedzi:

10

Warstwy LSTM wymagają danych o innym kształcie.

Z twojego opisu rozumiem, że początkowy zestaw danych ma 3125 wierszy i 1000 kolumn, przy czym każdy wiersz ma jeden krok. Zmienna docelowa powinna wówczas mieć 3125 wierszy i 1 kolumnę, gdzie każda wartość może być jedną z trzech możliwych wartości. Wygląda na to, że masz problem z klasyfikacją. Aby to sprawdzić w kodzie, zrobiłbym:

>>> X.shape
(3125, 1000)

>>> y.shape
(1000,)

Klasa LSTM wymaga, aby każda pojedyncza próbka składała się z „bloku” czasu. Powiedzmy, że chcesz mieć blok 100 kroków czasowych. Oznacza to, że X[0:100]jest to pojedyncza próbka wejściowa, która odpowiada zmiennej docelowej o wartości y[100]. oznacza to, że rozmiar twojego okna (czyli liczba kroków czasowych lub liczba opóźnień) jest równa 100. Jak wspomniano powyżej, masz 3125 próbek, więc N = 3125. Aby utworzyć pierwszy blok, musimy niestety odrzucić pierwsze 100 próbek y, ponieważ nie możemy utworzyć całego bloku 100 z dostępnych danych (wcześniej potrzebowalibyśmy punktów danych X[0]).

Biorąc to wszystko pod uwagę, LSTM wymaga dostarczenia partii kształtu (N - window_size, window_size, num_features), co przekłada się na (3125 - 100, 100, 1000)== (3025, 100, 1000).

Tworzenie tych bloków czasowych jest trochę kłopotliwe, ale stwórz dobrą funkcję raz, a następnie zapisz ją :)

Jest więcej do zrobienia, może wyglądać na bardziej w przykładach głębokość mojego wyjaśnienia powyżej tutaj ... albo mieć odczytu z dokumentacją LSTM (lub jeszcze lepiej, kod źródłowy! ).

Ostateczny model byłby wtedy dość prosty (na podstawie twojego kodu):

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

Zajrzyj do dokumentacji dotyczącej kształtu wejściowego dla Sequentialmodelu . Mówi w zasadzie, że nie musimy określać liczby partii w ramach input_shape. Można tego dokonać np. batch_size=50Jeśli potrzebujesz, aby był to stały numer.

Wiem, że input_shapeargumentu nie ma w dokumentacji LSTM, ale sama klasa dziedziczy po nim RNN, co z kolei dziedziczy po Layer- więc będzie mógł użyć podanych informacji.

Ostatnia wskazówka: jeśli planujesz dodać kilka warstw LSTM („układając je” w stosy), musisz dodać jeszcze jeden argument do wszystkich oprócz ostatniej LSTM , a mianowicie return_sequences=True.

n1k31t4
źródło
Dziękujemy za wyczerpującą odpowiedź Dexter (!). Odnośnie twoich komentarzy na temat wielkości partii, czy parametr batch_size jest podany w argumencie model.fit, jest innym parametrem hiper niż w przypadku tworzenia własnej niestandardowej partii? Udało mi się uruchomić mój kod przynajmniej przez przekształcenie moich danych z matrycy 1000x3125 w matrycę 3D przy użyciu data = np.reshape (data, (1000,1,3125)). To pozwoliło mi uruchomić LSTM z input_shape (1,3125), ale znowu, nie jestem do końca pewien, co robię. Jeszcze raz bardzo dziękuję za odpowiedź. Rzucę okiem na podane przez ciebie linki i przestudiuję twoją odpowiedź.
user1147964
Nie ma za co! Tak, rozumiesz, jeśli pominiesz batch_sizepodczas definiowania modelu, zostanie on wzięty z tego samego argumentu model.fit(). Powinieneś przekształcić, aby uzyskać (3025, 100, 1000), co oznacza 3025 partii, każdy ze 100 (wierszy) kroków czasowych i 1000 (kolumn) zmiennych. Użycie np.reshapeniestety nie zadziała w tym przypadku (pojawi się błąd), ponieważ dane nakładają się na siebie ... ostateczny kształt zawiera więcej danych niż danych wejściowych. 3025x100x1000> 3125x1000 - np.reshapenie podoba się to, ponieważ jest niejednoznaczne. Sugeruję po prostu zapętlenie zestawu danych, 1 pętla = 1 próbka.
n1k31t4
Myślę, że jestem tutaj trochę zdezorientowany i może to być spowodowane tym, że mogłem już przypadkowo wykonać proces wsadowy. Użyję tutaj określonych wartości. Próbowałem 3 różne pomiary przy 6,25 kHz przez około 3 minuty, co dało 3 szeregi czasowe o długości 1093750. Generuje to matrycę 3x1093750. Następnie podzieliłem każdy TS na przyrosty co 0,5 sekundy, w wyniku czego otrzymano matrycę 1050x3125. Mógłbym technicznie przekształcić to w matrycę 3D o wymiarach 3x350x3125. To daje mi 350 partii o długości 0,5 s. Twoje przekształcanie wydaje się generować o wiele więcej wartości. Dziękujemy za odpowiedź. Przepraszamy
1147964
Żeby dodać, przeczytanie pierwszego zamieszczonego linku sprawia, że ​​myślę, że zmieniam wszystko poprawnie. Przepraszam, jeśli brakuje mi czegoś oczywistego, ale tutaj zaczynają się od TS o długości 5000 i zamieniają ją w matrycę 3D o wymiarach [1 25 200].
user1147964
W porównaniu do metody w twoim linku, mój sposób stworzy o wiele więcej próbek. Wynika to z tego, że korzystam z pewnego rodzaju „toczącego się” okna. Spójrz na to przedstawienie . Nie używają ruchomego okna. Przekształcenie 3 minut w kawałki o wymiarach 350 x 0,5 s jest w porządku (może nie jest potrzebne - jak często przewidujesz?), Każdy fragment powinien mieć wymiary 3 x 3125. „Mógłbym przekształcić to w matrycę 3D o wymiarach 3x350x3125” - brzmi to lepiej, ale po dokonaniu podziałów oczekiwałbym 350x3x3125 (350 fragmentów 3x3125). Każda z tych części może być następnie przetwarzana zgodnie z opisem.
n1k31t4