Keras LSTM com séries temporais 1D


10

Estou aprendendo a usar o Keras e tive um sucesso razoável com meu conjunto de dados rotulado usando os exemplos do Deep Learning for Python da Chollet . O conjunto de dados é de ~ 1000 séries temporais com comprimento 3125 e 3 classes potenciais.

Gostaria de ir além das camadas básicas densas, que me dão uma taxa de previsão de cerca de 70%, e o livro continua discutindo as camadas LSTM e RNN.

Todos os exemplos parecem usar conjuntos de dados com vários recursos para cada série temporal e estou lutando para descobrir como implementar meus dados como resultado.

Se, por exemplo, tenho séries temporais de 1000x3125, como faço para alimentar isso em algo como a camada SimpleRNN ou LSTM? Estou perdendo algum conhecimento fundamental do que essas camadas fazem?

Código atual:

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

Respostas:


10

As camadas LSTM requerem dados de uma forma diferente.

Pela sua descrição, entendo que o conjunto de dados inicial possui 3125 linhas e 1000 colunas, em que cada linha é uma etapa do tempo. A variável de destino deve ter 3125 linhas e 1 coluna, em que cada valor pode ser um dos três valores possíveis. Parece que você está com um problema de classificação. Para verificar isso no código, eu faria:

>>> X.shape
(3125, 1000)

>>> y.shape
(1000,)

A classe LSTM exige que cada amostra seja composta por um 'bloco' de tempo. Digamos que você queira ter um bloco de 100 etapas de tempo. Isso significa que X[0:100]é uma amostra de entrada única, que corresponde à variável de destino em y[100]. isso significa que o tamanho da sua janela (também conhecido como número de intervalos de tempo ou número de defasagens) é igual a 100. Como mencionado acima, você possui 3125 amostras N = 3125. Para formar o primeiro bloco, infelizmente precisamos descartar as 100 primeiras amostras y, pois não podemos formar um bloco inteiro de 100 a partir dos dados disponíveis (acabaríamos precisando dos pontos de dados antes X[0]).

Dado tudo isso, um LSTM exige que você forneça lotes de forma (N - window_size, window_size, num_features), o que se traduz em (3125 - 100, 100, 1000)== (3025, 100, 1000).

Criar esses blocos de tempo é um pouco complicado, mas crie uma boa função uma vez e salve-a :)

Há mais trabalho a ser feito, talvez veja exemplos mais detalhados da minha explicação acima aqui ... ou leia a documentação do LSTM (ou, melhor ainda, o código-fonte! ).

O modelo final seria simples o suficiente (com base no seu código):

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

Veja a documentação referente ao formato da entrada para o Sequentialmodelo . Basicamente, diz que não precisamos especificar o número de lotes input_shape. Isso pode ser feito com batch_size=50, por exemplo , se você precisar que seja um número fixo.

Eu sei que o input_shapeargumento não está na documentação de LSTM, mas a própria classe herda de RNN, que por sua vez é herdada de Layer- portanto, ela poderá usar as informações fornecidas.

Uma última dica: se você planeja adicionar várias camadas LSTM ('empilhá-las'), será necessário adicionar mais um argumento a todos, exceto o último LSTM , a saber, o return_sequences=True.


Obrigado pela resposta abrangente Dexter (!). Em relação aos seus comentários sobre o tamanho do lote, o batch_size especificado no argumento model.fit é um hiper parâmetro diferente em comparação com a criação do meu próprio lote personalizado? Consegui fazer com que meu código fosse executado pelo menos remodelando meus dados de uma matriz de 1000x3125 em uma matriz 3D usando data = np.reshape (data, (1000,1,3125)). Isso me permite executar o LSTM com input_shape (1,3125), mas, novamente, não tenho muita certeza do que estou fazendo. Mais uma vez, muito obrigado pela resposta. Vou dar uma olhada nos links que você forneceu e estudar sua resposta um pouco mais.
user1147964

De nada! Sim, você entendeu, se você deixar de fora batch_sizeao definir o modelo, ele será retirado do mesmo argumento interno model.fit(). Você deve remodelar para obter (3025, 100, 1000), o que significa 3025 lotes, cada um com 100 (linhas) timesteps e 1000 (colunas) variáveis. Usando np.reshapevai infelizmente não trabalho para isso (você receberá um erro), devido ao fato de que você vai ter sobreposições de dados ... o formato final tem mais dados do que a entrada. 3025x100x1000> 3125x1000 - np.reshapenão gosta disso, pois é ambíguo. Sugiro simplesmente fazer um loop no conjunto de dados, 1 loop = 1 amostra.
N1k31t4

Acho que estou um pouco confuso aqui e pode ser porque eu já inadvertidamente já fiz o processo de lote. Usarei valores específicos aqui. Fiz uma amostragem de 3 medições diferentes a 6,25 kHz por aproximadamente 3 minutos, resultando em 3 séries temporais de comprimento 1093750. Isso gera uma matriz 3x1093750. Segmente cada TS em incrementos de 0,5 segundo, resultando em uma matriz de 1050x3125. Eu poderia tecnicamente reestruturar isso em uma matriz 3D com dimensões 3x350x3125. Isso me dá 350 lotes de 0,5s de comprimento. Sua reformulação parece gerar muitos outros valores. Obrigado pela resposta novamente. Desculpe
user1147964

Só para adicionar, ler o primeiro link que você postou me faz pensar que estou remodelando as coisas corretamente. Desculpe se estou perdendo algo óbvio, mas aqui eles começam com um comprimento TS de 5000 e o transformam em uma matriz 3D com dimensões [1 25 200].
user1147964

Comparado com o método no seu link, meu caminho criará muito mais amostras. Isso ocorre porque estou usando uma espécie de janela 'rolante'. Dê uma olhada nesta representação . Eles não usam uma janela rolante . Fazer 3 minutos em pedaços de 350x0,5s é bom (talvez não seja necessário - quantas vezes você prevê?), Cada pedaço deve ser de 3x3125. "Eu poderia reestruturar isso em uma matriz 3D com dimensões 3x350x3125" - isso soa melhor, mas depois de fazer as divisões, eu esperaria 350x3x3125 (350 pedaços de 3x3125). Cada um desses pedaços pode ser processado como eu descrevi.
N1k31t4
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.