Keras LSTM: un pronóstico de múltiples funciones de varias etapas de tiempo – resultados pobres

Tengo un conjunto de datos de series de tiempo que contiene datos de un año completo (la fecha es el índice). Los datos se midieron cada 15 minutos (durante todo el año), lo que resulta en 96 pasos al día. Los datos ya están normalizados. Las variables están correlacionadas. Todas las variables excepto el VAR son medidas meteorológicas.

El VAR es estacional en un período de un día y en un período de una semana (ya que se ve un poco diferente en el fin de semana, pero más o menos lo mismo cada fin de semana). Los valores VAR son estacionarios. Me gustaría predecir los valores de VAR para los próximos dos días (192 pasos adelante) y para los próximos siete días (672 pasos adelante).

Aquí está la muestra del conjunto de datos:

DateIdx VAR dewpt hum press temp 2017-04-17 00:00:00 0.369397 0.155039 0.386792 0.196721 0.238889 2017-04-17 00:15:00 0.363214 0.147287 0.429245 0.196721 0.233333 2017-04-17 00:30:00 0.357032 0.139535 0.471698 0.196721 0.227778 2017-04-17 00:45:00 0.323029 0.127907 0.429245 0.204918 0.219444 2017-04-17 01:00:00 0.347759 0.116279 0.386792 0.213115 0.211111 2017-04-17 01:15:00 0.346213 0.127907 0.476415 0.204918 0.169444 2017-04-17 01:30:00 0.259660 0.139535 0.566038 0.196721 0.127778 2017-04-17 01:45:00 0.205564 0.073643 0.523585 0.172131 0.091667 2017-04-17 02:00:00 0.157650 0.007752 0.481132 0.147541 0.055556 2017-04-17 02:15:00 0.122101 0.003876 0.476415 0.122951 0.091667 

Gráfico de entrada de datos

He decidido usar LSTM en Keras. Teniendo datos de todo el año, he usado datos de los últimos 329 días como datos de entrenamiento y el rest para una validación durante el entrenamiento. train_X -> contiene medidas completas incluyendo VAR de 329 días train_Y -> contiene solo VAR de 329 días. El valor se desplaza un paso adelante. El rest de los pasos de tiempo van a test_X y test_Y.

Aquí está el código que preparo train_X y train_Y:

 #X -> is the whole dataframe #Y -> is a vector of VAR from whole dataframe, already shifted 1 step ahead #329 * 96 = 31584 train_X = X[:31584] train_X = train_X.reshape(train_X.shape[0],1,5) train_Y = Y[:31584] train_Y = train_Y.reshape(train_Y.shape[0],1) 

Para predecir el próximo valor de VAR, me gustaría usar los últimos 672 pasos de tiempo (medidas de toda la semana). Por esta razón, he establecido batch_size=672 , para que el comando ‘ajustar’ se vea así:

 history = model.fit(train_X, train_Y, epochs=50, batch_size=672, validation_data=(test_X, test_Y), shuffle=False) 

Aquí está la architecture de mi red:

 model = models.Sequential() model.add(layers.LSTM(672, input_shape=(None, 672), return_sequences=True)) model.add(layers.Dropout(0.2)) model.add(layers.LSTM(336, return_sequences=True)) model.add(layers.Dropout(0.2)) model.add(layers.LSTM(168, return_sequences=True)) model.add(layers.Dropout(0.2)) model.add(layers.LSTM(84, return_sequences=True)) model.add(layers.Dropout(0.2)) model.add(layers.LSTM(21, return_sequences=False)) model.add(layers.Dense(1)) model.compile(loss='mae', optimizer='adam') model.summary() 

En la siguiente ttwig podemos ver que la red ha aprendido “algo” después de 50 épocas:

Ttwig del proceso de aprendizaje.

Para el propósito de la predicción, he preparado un conjunto de datos que contienen los últimos 672 pasos con todos los valores y 96 sin el valor de VAR, lo cual debería predecirse. También utilicé la autorregresión, así que actualicé el VAR después de cada predicción y lo usé para la siguiente predicción.

El conjunto de datos predX (usado para la predicción) se ve así:

 print(predX['VAR'][668:677]) DateIdx VAR 2017-04-23 23:00:00 0.307573 2017-04-23 23:15:00 0.278207 2017-04-23 23:30:00 0.284390 2017-04-23 23:45:00 0.309118 2017-04-24 00:00:00 NaN 2017-04-24 00:15:00 NaN 2017-04-24 00:30:00 NaN 2017-04-24 00:45:00 NaN 2017-04-24 01:00:00 NaN Name: VAR, dtype: float64 

Aquí está el código (autorregresión) que he usado para predecir los siguientes 96 pasos:

 stepsAhead = 96 historySteps = 672 for i in range(0,stepsAhead): j = i + historySteps ypred = model.predict(predX.values[i:j].reshape(1,historySteps,5)) predX['VAR'][j] = ypred 

Desafortunadamente los resultados son muy pobres y están muy lejos de las expectativas:

Una plot con datos predichos

Resultados combinados con un día anterior:

Datos previstos combinados con un día anterior.

Excepto en la pregunta ‘¿Qué he hecho mal?’, Me gustaría hacer algunas preguntas:

Q1. Durante el modelo fifing, acabo de poner toda la historia en lotes de tamaño 672. ¿Es correcto? ¿Cómo debo organizar el conjunto de datos para el ajuste del modelo? ¿Que opciones tengo? ¿Debo usar el enfoque de “ventana deslizante” (como en el enlace aquí: https://machinelearningmastery.com/promise-recurrent-neural-networks-time-series-forecasting/ )?

Q2. ¿Son las 50 épocas suficientes? ¿Cuál es la práctica común aquí? ¿Tal vez la red esté inadecuada, lo que da como resultado una mala predicción? Hasta ahora he intentado 200 épocas con el mismo resultado.

Q3. ¿Debo probar una architecture diferente? ¿Es la red propuesta ‘lo suficientemente grande’ para manejar tal información? Tal vez una red “con estado” es el enfoque correcto aquí?

Q4. ¿Implementé correctamente la autorregresión? ¿Hay algún otro enfoque para hacer una predicción para muchos pasos adelante, por ejemplo, 192 o 672 como en mi caso?

Parece que hay una confusión sobre cómo organizar los datos para entrenar a un RNN. Así que vamos a cubrir las preguntas:

  1. Una vez que tenga un conjunto de datos 2D (total_samples, 5) , puede usar TimeseriesGenerator para crear una ventana deslizante que generará (batch_size, past_timesteps, 5) para usted. En este caso, utilizará .fit_generator para entrenar la red.
  2. Si obtienes el mismo resultado, 50 épocas deberían estar bien. Por lo general, se ajusta en función del rendimiento de su red. Pero debe mantenerlo fijo si está comparando dos architectures de red diferentes.
  3. La architecture es realmente grande, ya que pretende predecir todos los 672 valores futuros a la vez. Puede diseñar la red para que aprenda a predecir una medida a la vez. En el momento de la predicción, puede predecir un punto y alimentarlo nuevamente para predecir el siguiente hasta que obtenga 672.
  4. Esto se relaciona con la respuesta 3, puede aprender a predecir uno por uno y encadenar las predicciones a un número n de predicciones después del entrenamiento.

El modelo de predicción de un solo punto podría verse como:

 model = Sequential() model.add(LSTM(128, return_sequences=True, input_shape=(past_timesteps, 5)) model.add(LSTM(64)) model.add(Dense(1)) 

1) Los lotes no son las secuencias. La entrada X es la secuencia. La entrada debe tener la forma [None, sequence_length, number_of_features] . El 1er eje será completado por Keras con los lotes. Pero no son las secuencias. Las secuencias están en el segundo eje. El 3er eje son las columnas de características. El tamaño de lote 672 puede ser demasiado grande. Puedes probar valores más pequeños 128 , 64 o 32 .

2) Casi seguro que su red se viste de gala. La red tiene demasiadas capas LSTM. Probaría solo 2 capas de LSTM como sugirió @nuric y vería cómo funciona.

3) También parece haber una confusión acerca de las unidades LSTM (o tamaño LSTM). No tiene que ser 672 . De hecho, 672 es demasiado grande. Un buen punto de partida es 128 .

4) La architecture NN está prediciendo un valor único de VAR . En ese caso, asegúrese de que su Y tenga un solo valor para cada secuencia de X

5) Alternativamente, puede hacer que el último LSTM produzca una secuencia. En ese caso, cada entrada Y es una secuencia VAR desplazada un paso por delante. Volviendo a 4), asegúrese de que Y tenga la forma correcta correspondiente a la de X y la architecture NN.

6) La gráfica muestra que 50 épocas son suficientes para converger. Una vez que ajusta X , Y y NN, haga lo mismo para ver el número de épocas.

7) Por último una idea sobre las fechas. Si desea incluir las fechas en X , una idea es codificarlas en días de la semana. Entonces tu X sería [dewpt, hum, press, temp, MON, TUE, ..., SAT, SUN] .

Su principal problema aquí, como lo indican otros, es el tamaño de su red. Los LSTM son excelentes para aprender dependencias a largo plazo, pero ciertamente no son magia. Personalmente, no he tenido mucho éxito con ninguna secuencia con más de 100 pasos de tiempo. Lo que encontrará es que terminará sufriendo el ‘problema de gradientes de explosión / desaparición’ porque su red es demasiado grande.

No reiteraré lo que otros han dicho sobre la remodelación de sus datos al formato adecuado, pero una vez que lo haya hecho, le recomiendo comenzar con pasos pequeños (10/15 pasos) y pronosticar solo el resultado del siguiente paso, y continuar a partir de ahí. . Esto no quiere decir que no pueda predecir una secuencia mucho más larga y más hacia el futuro, pero comenzar poco a poco lo ayudará a comprender cómo se está comportando el RNN antes de construirlo.