Keras LSTM с 1D временными рядами

10

Я изучаю, как использовать Keras, и я добился разумного успеха с моим помеченным набором данных, используя примеры в Chollet Deep Learning for Python . Набор данных ~ 1000 временных рядов с длиной 3125 с 3 потенциальными классами.

Я хотел бы выйти за рамки базовых плотных слоев, которые дают мне около 70% прогнозирования, и далее в книге обсуждаются уровни LSTM и RNN.

Кажется, что во всех примерах используются наборы данных с несколькими функциями для каждой временной серии, и я изо всех сил пытаюсь понять, как реализовать мои данные в результате.

Если, например, у меня есть временной ряд 1000x3125, как мне передать это во что-то вроде слоя SimpleRNN или LSTM? Мне не хватает фундаментальных знаний о том, что делают эти слои?

Текущий код:

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()
user1147964
источник

Ответы:

10

Слоям LSTM требуются данные другой формы.

Из вашего описания я понимаю, что начальный набор данных содержит 3125 строк и 1000 столбцов, где каждая строка представляет собой один временной шаг. Целевая переменная должна иметь 3125 строк и 1 столбец, где каждое значение может быть одним из трех возможных значений. Похоже, что вы делаете проблему классификации. Чтобы проверить это в коде, я бы сделал:

>>> X.shape
(3125, 1000)

>>> y.shape
(1000,)

Класс LSTM требует, чтобы каждый отдельный образец состоял из «блока» времени. Допустим, вы хотите иметь блок из 100 временных шагов. Это означает X[0:100], что единственная входная выборка соответствует целевой переменной at y[100]. это означает, что размер вашего окна (то есть количество временных шагов или количество лагов) равен 100. Как указано выше, у вас есть 3125 выборок, поэтому N = 3125. Чтобы сформировать первый блок, мы, к сожалению, должны отбросить первые 100 выборок y, так как мы не можем сформировать целый блок из 100 доступных данных (в конечном итоге нам понадобятся точки данных X[0]).

Учитывая все это, LSTM требует, чтобы вы доставляли партии в форме (N - window_size, window_size, num_features), что означает (3125 - 100, 100, 1000)== (3025, 100, 1000).

Создание этих временных блоков немного хлопотно, но однажды создайте хорошую функцию, а затем сохраните ее :)

Предстоит проделать большую работу, возможно, посмотрите более подробные примеры моего объяснения выше здесь ... или прочитайте документацию по LSTM (или, что еще лучше, исходный код! ).

Окончательная модель будет достаточно простой (на основе вашего кода):

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

Посмотрите документацию, касающуюся формы ввода для Sequentialмодели . В основном это говорит о том, что нам не нужно указывать количество партий внутри input_shape. Это может быть сделано, например batch_size=50, если вам нужно, чтобы он был фиксированным числом.

Я знаю, что input_shapeаргумент отсутствует в документации для LSTM, но сам класс наследует от RNN, который, в свою очередь, наследует от Layer- так что он сможет использовать предоставленную вами информацию.

Последний совет: если вы планируете добавить несколько слоев LSTM («укладывать их»), вам нужно добавить еще один аргумент для всех, кроме последнего LSTM , а именно return_sequences=True.

n1k31t4
источник
Спасибо за исчерпывающий ответ Декстер (!). Что касается ваших комментариев о размере пакета, является ли параметр batch_size, указанный в аргументе model.fit, другим гиперпараметром по сравнению с созданием моей собственной пользовательской партии? Мне удалось заставить мой код, по крайней мере, работать, преобразовав мои данные из матрицы 1000x3125 в трехмерную матрицу, используя data = np.reshape (data, (1000,1,3125)). Это позволило мне запустить LSTM с помощью input_shape (1,3125), но опять же, я не совсем уверен, что я делаю. Еще раз большое спасибо за ответ. Я посмотрю на предоставленные вами ссылки и еще раз изучу ваш ответ.
user1147964
Пожалуйста! Да, вы получили его, если вы опустите его batch_sizeпри определении модели, он будет взят из того же аргумента внутри model.fit(). Вы должны (3025, 100, 1000)изменить форму, чтобы получить , что означает 3025 пакетов, каждый из 100 (строки) временных шагов и 1000 (столбцы) переменных. Использование np.reshape, к сожалению, не будет работать для этого (вы получите ошибку), из-за того, что у вас будут наложения данных ... конечная фигура имеет больше данных, чем входных данных. 3025x100x1000> 3125x1000 - np.reshapeне нравится, так как это неоднозначно. Я предлагаю просто зацикливаться на наборе данных, 1 петля = 1 выборка.
n1k31t4
Я думаю, что я немного запутался здесь, и это может быть потому, что я, возможно, непреднамеренно уже выполнил процесс пакетирования. Здесь я буду использовать конкретные значения. Я произвел выборку из 3 различных измерений на частоте 6,25 кГц в течение примерно 3 минут, в результате чего получился 3 временной ряд длиной 1093750. Это создает матрицу 3x1093750. Затем я сегментировал каждый TS с шагом 0,5 секунды, в результате чего получилась матрица 1050x3125. Я мог бы технически перестроить это в 3D-матрицу с размерами 3x350x3125. Это дает мне 350, 0,5 с длиной "партии". Похоже, что ваше изменение создает гораздо больше значений. Спасибо за ответ еще раз. Извините
user1147964
Просто добавлю, что чтение первой ссылки, которую вы разместили, заставляет меня думать, что я правильно меняю вещи. Извиняюсь, если я упускаю что-то очевидное, но здесь они начинаются с длины TS 5000 и превращают ее в трехмерную матрицу с размерами [1 25 200].
user1147964
По сравнению с методом, указанным в вашей ссылке, мой способ создаст гораздо больше образцов. Это потому, что я использую своего рода «скользящее» окно. Посмотрите на это изображение . Они не используют скользящее окно. Делать 3 минуты на порции 350x0.5s - это нормально (возможно, не нужно - как часто вы прогнозируете?), Каждый блок должен быть 3x3125. «Я мог бы реструктурировать это в трехмерную матрицу с размерами 3x350x3125» - это звучит лучше, но после разделения я бы ожидал 350x3x3125 (350 кусков 3x3125). Каждый из этих кусков может быть обработан, как я описал.
n1k31t4