Blog
Predicción con Series Temporales con LSTM, Redes neuronales recurrentes
- Publicado por: Rafael Fernandez
- Categoría: Deep Learning
-En esta lección descubrirá cómo desarrollar redes LSTM en Python utilizando el software de gestión de redes Keras para abordar un problema de predicción de series temporales con sus ejemplos.
-Cómo implementar y desarrollar redes LSTM para sus problemas con series temporales de predicción y otros problemas de secuencias más generales.
-Cómo desarrollar redes LSTM para un problema de predicción de series temporales enmarcado como regresión.
-Cómo desarrollar redes LSTM para un problema de predicción de series de tiempo usando una ventana para tanto las características como los pasos de tiempo.
-Cómo desarrollar y hacer predicciones usando redes LSTM que mantienen el estado (memoria) a través de secuencias muy largas.
Los problemas de predicción de series temporales son complejos para el modelado predictivo. A diferencia de modelos predictivos de regresión, las series temporales también añaden la complejidad de una dependencia de secuencia entre las variables de entrada. Un poderoso tipo de red neuronal diseñada para manejar secuencias la dependencia se llaman Redes Neuronales Recurrentes. Las redes de memoria larga-corto plazo por sus siglas LSTM es un tipo de red neuronal recurrente que se utiliza en el aprendizaje profundo debido a sus grandes arquitecturas las cuales pueden ser entrenadas con éxito.
Desarrollaremos una serie de LSTMs para un problema de predicción de series de tiempo estándar. El problema y la configuración elegida para las redes LSTM es a efectos de demostración por lo que no estarán optimizadas. Estos ejemplos le mostrarán exactamente cómo puede desarrollar sus habilidades propias de redes LSTM para problemas de modelado predictivo de series de tiempo.
Red LSTM para Regresión
El problema que vamos a tratar en esta lección es el de los pasajeros de las aerolíneas internacionales, el cual es un problema de predicción, descrito en el post anterior. Podemos decir que el problema es una regresión como se hizo en el capítulo anterior. Es decir, dado el número de pasajeros (en unidades de miles) este mes, ¿cuál es el número de pasajeros del próximo mes? Este ejemplo reutiliza la misma carga y preparación de datos del capítulo anterior, especialmente el uso de la función create_dataset().
Las LSTMs son sensibles a la escala de los datos de entrada, especialmente cuando se utilizan las funciones de activación sigmoide (por defecto) o tanh. Puede ser una buena práctica reescalar los datos al rango de 0 a 1, también llamado normalización. Podemos normalizar fácilmente el conjunto de datos utilizando la clase de preprocesamiento MinMaxScaler de la biblioteca scikit-learn.
# normalizacion de los datos scaler = MinMaxScaler(feature_range=(0, 1)) dataset = scaler.fit_transform(dataset)
La red LSTM espera que los datos de entrada (X) se proporcionen con una estructura de matriz específica en forma de: [muestras, pasos de tiempo, características]. Nuestros datos preparados están en la forma: [muestras, características] y estamos enmarcando el problema como un paso de tiempo para cada muestra. Podemos transformar los datos de entrada del entrenamiento preparado y de la prueba en la estructura esperada utilizando numpy.reshape() de la siguiente manera:
# cambiar la forma de la entrada para que sea[muestras, pasos de tiempo, características] trainX = numpy.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1])) testX = numpy.reshape(testX, (testX.shape[0], 1, testX.shape[1]))
Ahora estamos listos para diseñar y poner en marcha nuestra red LSTM para este problema. La red tiene una capa visible con 1 entrada, una capa oculta con 4 bloques LSTM o neuronas y una capa de salida que hace una sola predicción de valor. La función de activación sigmoide por defecto se utiliza para los bloques de memoria LSTM. La red está entrenada para 100 épocas y se utiliza un tamaño de lote de 1.
# creacion de la red LSTM model = Sequential() model.add(LSTM(4, input_shape=(1, look_back))) model.add(Dense(1)) model.compile(loss='mean_squared_error', optimizer='adam') model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=2)
Aquí os mostramos el código completo
# LSTM para predecir a los pasajeros de las aerolíneas internacionales import numpy import matplotlib.pyplot as plt from pandas import read_csv import math from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM from sklearn.preprocessing import MinMaxScaler from sklearn.metrics import mean_squared_error # convertimos un array de valores en una matriz de conjuntos de datos def create_dataset(dataset, look_back=1): dataX, dataY = [], [] for i in range(len(dataset)-look_back-1): a = dataset[i:(i+look_back), 0] dataX.append(a) dataY.append(dataset[i + look_back, 0]) return numpy.array(dataX), numpy.array(dataY) # semilla aleatoria para reproducibilidad numpy.random.seed(7) # cargamos el conjunto de datos dataframe = read_csv('international-airline-passengers.csv', usecols=[1], engine='python',skipfooter=3) dataset = dataframe.values dataset = dataset.astype('float32') # normalizamos el conjunto de datos scaler = MinMaxScaler(feature_range=(0, 1)) dataset = scaler.fit_transform(dataset) # dividimos entre entranmiento y test train_size = int(len(dataset) * 0.67) test_size = len(dataset) - train_size train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:] # remodelamos X=t y Y=t+1 look_back = 1 trainX, trainY = create_dataset(train, look_back) testX, testY = create_dataset(test, look_back) # remodelamos la entrada para que sea[muestras, pasos de tiempo, características] trainX = numpy.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1])) testX = numpy.reshape(testX, (testX.shape[0], 1, testX.shape[1])) #creamos la LSTM network model = Sequential() model.add(LSTM(4, input_shape=(1, look_back))) model.add(Dense(1)) model.compile(loss='mean_squared_error', optimizer='adam') model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=2) # hacemos las predicciones trainPredict = model.predict(trainX) testPredict = model.predict(testX) # invertimos las predicciones trainPredict = scaler.inverse_transform(trainPredict) trainY = scaler.inverse_transform([trainY]) testPredict = scaler.inverse_transform(testPredict) testY = scaler.inverse_transform([testY]) # calculamos el error rms trainScore = math.sqrt(mean_squared_error(trainY[0], trainPredict[:,0])) print('Resultado del entrenamiento: %.2f RMSE' % (trainScore)) testScore = math.sqrt(mean_squared_error(testY[0], testPredict[:,0])) print('Resultado del test: %.2f RMSE' % (testScore)) # predicciones del entrenamiento de cambio para plotear trainPredictPlot = numpy.empty_like(dataset) trainPredictPlot[:, :] = numpy.nan trainPredictPlot[look_back:len(trainPredict)+look_back, :] = trainPredict # predicciones del test de cambio para plotear testPredictPlot = numpy.empty_like(dataset) testPredictPlot[:, :] = numpy.nan testPredictPlot[len(trainPredict)+(look_back*2)+1:len(dataset)-1, :] = testPredict # plot baseline and predictions plt.plot(scaler.inverse_transform(dataset)) # ploteamos linea base y predicciones plt.plot(trainPredictPlot,'r', linewidth = 2) plt.plot(testPredictPlot,'m', linewidth = 2) plt.legend( ('Datos', 'Prediccion datos entramiento', 'Prediccion sobre los datos de test'), loc = 'upper left') plt.grid(True) plt.title("Datos sobre pasajeros por mes desde 1949 a 1960", fontsize = 15) plt.xlabel("Tiempo (nº meses)", fontsize = 10) plt.ylabel("nº de pasajeros", fontsize = 10) plt.show()
Ejecutando el modelo obtenemos la siguiente salida:
Epoch 96/100 - 0s - loss: 0.0020 Epoch 97/100 - 0s - loss: 0.0020 Epoch 98/100 - 0s - loss: 0.0020 Epoch 99/100 - 0s - loss: 0.0020 Epoch 100/100 - 0s - loss: 0.0020 Resultado del entrenamiento: 22.92 RMSE Resultado del test: 47.53 RMSE
Podemos ver que el modelo tiene un error medio de unos casi 23 pasajeros (en miles) en el conjunto de datos de formación y de unos 47 pasajeros (en miles) en el conjunto de datos de prueba. No está nada mal.
LSTM para Regresión Usando el Método de la Ventana
También podemos formular el problema de manera que se puedan utilizar múltiples pasos recientes para hacer la predicción para el próximo paso temporal. Esto se denomina ventana y el tamaño de la ventana es un parametro que se puede ajustar para cada problema. Por ejemplo, dada la hora actual (t) queremos predecir el valor la próxima secuencia (t+1), podemos usar el tiempo actual (t) así como los dos tiempos anteriores (t-1 y t-2) como variables de entrada. De esta forma las variables de entrada son t-2, t-1, t y la variable de salida es t+1.
La función create_dataset() que creamos en la sección anterior nos permite crear esta función para la formulación del problema de las series temporales aumentando el argumento de look_back hacia atrás de 1 a 3. El conjunto de datos con esta formulación tiene el siguiente aspecto:
X1 X2 X3 Y 112 118 132 129 118 132 129 121
Podemos volver a ejecutar el ejemplo de la sección anterior con el tamaño de ventana más grande. El listado completo del código con sólo el cambio de tamaño de la ventana se enumera a continuación para completarlo.
# LSTM para predecir a los pasajeros de las aerolíneas internacionales con ventanas de regresion import numpy import matplotlib.pyplot as plt from pandas import read_csv import math from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM from sklearn.preprocessing import MinMaxScaler from sklearn.metrics import mean_squared_error # convertimos un array de valores en una matriz de conjuntos de datos def create_dataset(dataset, look_back=1): dataX, dataY = [], [] for i in range(len(dataset)-look_back-1): a = dataset[i:(i+look_back), 0] dataX.append(a) dataY.append(dataset[i + look_back, 0]) return numpy.array(dataX), numpy.array(dataY) # semilla aleatoria para reproducibilidad numpy.random.seed(7) # cargamos el conjunto de datos dataframe = read_csv('international-airline-passengers.csv', usecols=[1], engine='python', skipfooter=3) dataset = dataframe.values dataset = dataset.astype('float32') # normalizamos el conjunto de datos scaler = MinMaxScaler(feature_range=(0, 1)) dataset = scaler.fit_transform(dataset) # dividimos entre entranmiento y test train_size = int(len(dataset) * 0.67) test_size = len(dataset) - train_size train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:] # remodelamos X=t y Y=t+1 look_back = 3 trainX, trainY = create_dataset(train, look_back) testX, testY = create_dataset(test, look_back) # remodelamos la entrada para que sea[muestras, pasos de tiempo, características] trainX = numpy.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1])) testX = numpy.reshape(testX, (testX.shape[0], 1, testX.shape[1])) # creamos la LSTM network model = Sequential() model.add(LSTM(4, input_shape=(1, look_back))) model.add(Dense(1)) model.compile(loss='mean_squared_error', optimizer='adam') model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=2) # hacemos las predicciones trainPredict = model.predict(trainX) testPredict = model.predict(testX) # invertimos las predicciones trainPredict = scaler.inverse_transform(trainPredict) trainY = scaler.inverse_transform([trainY]) testPredict = scaler.inverse_transform(testPredict) testY = scaler.inverse_transform([testY]) # calculamos el error rms trainScore = math.sqrt(mean_squared_error(trainY[0], trainPredict[:,0])) print('Resultado del entrenamiento: %.2f RMSE' % (trainScore)) testScore = math.sqrt(mean_squared_error(testY[0], testPredict[:,0])) print('Resultado del test: %.2f RMSE' % (testScore)) # predicciones del entrenamiento de cambio para plotear trainPredictPlot = numpy.empty_like(dataset) trainPredictPlot[:, :] = numpy.nan trainPredictPlot[look_back:len(trainPredict)+look_back, :] = trainPredict # predicciones del test de cambio para plotear testPredictPlot = numpy.empty_like(dataset) testPredictPlot[:, :] = numpy.nan testPredictPlot[len(trainPredict)+(look_back*2)+1:len(dataset)-1, :] = testPredict # plot baseline and predictions plt.plot(scaler.inverse_transform(dataset)) # ploteamos linea base y predicciones plt.plot(trainPredictPlot,'r', linewidth = 2) plt.plot(testPredictPlot,'m', linewidth = 2) plt.legend( ('Datos', 'Prediccion datos entramiento', 'Prediccion sobre los datos de test'), loc = 'upper left') plt.grid(True) plt.title("Datos sobre pasajeros por mes desde 1949 a 1960", fontsize = 15) plt.xlabel("Tiempo (nº meses)", fontsize = 10) plt.ylabel("nº de pasajeros", fontsize = 10) plt.show()
Al ejecutar el código obtenemos
Epoch 97/100 - 0s - loss: 0.0021 Epoch 98/100 - 0s - loss: 0.0021 Epoch 99/100 - 0s - loss: 0.0022 Epoch 100/100 - 0s - loss: 0.0020 Resultado del entrenamiento: 24.19 RMSE Resultado del test: 58.03 RMSE
Podemos ver que el error se ha incrementado ligeramente en comparación con el de la sección anterior. El tamaño de la ventana y la arquitectura de la red no fueron ajustados, esto es sólo una demostración de cómo enmarcar un problema de predicción.
LSTM para Regresión con Pasos de Tiempo
Es posible que haya notado que la preparación de datos para la red LSTM incluye pasos de tiempo. Algunos problemas de secuencia pueden tener un número variado de pasos de tiempo por muestra. Por ejemplo, puede tener mediciones de una máquina física que conducen a un punto de falla o a un punto de sobrecarga. Cada incidente sería una muestra, las observaciones que conducen al evento serían los pasos temporales y las variables observadas serían las características. Los pasos de tiempo proporcionan otra manera de expresar nuestro problema de series de tiempo. Como en el ejemplo de la ventana anterior, podemos tomar pasos temporales previos en nuestra serie temporal como entradas para predecir la salida en el siguiente paso de tiempo.
En lugar de formular las observaciones pasadas como características de entrada separadas, podemos usarlas como pasos de tiempo de la característica de una entrada, que de hecho es un marco más preciso del problema.
Podemos hacer esto usando la misma representación de datos que en el ejemplo anterior basado en ventanas, excepto que cuando remodelamos los datos establecemos que las columnas sean la dimensión de pasos de tiempo y cambiamos la dimensión de características a 1. Por ejemplo:
# remodelamos la entrada para que sea[muestras, pasos de tiempo, características] trainX = numpy.reshape(trainX, (trainX.shape[0], trainX.shape[1], 1)) testX = numpy.reshape(testX, (testX.shape[0], testX.shape[1], 1))
Mostrámos el código completo:
# LSTM para predecir a los pasajeros de las aerolíneas internacionales con ventanas de regresion import numpy import matplotlib.pyplot as plt from pandas import read_csv import math from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM from sklearn.preprocessing import MinMaxScaler from sklearn.metrics import mean_squared_error # convertimos un array de valores en una matriz de conjuntos de datos def create_dataset(dataset, look_back=1): dataX, dataY = [], [] for i in range(len(dataset)-look_back-1): a = dataset[i:(i+look_back), 0] dataX.append(a) dataY.append(dataset[i + look_back, 0]) return numpy.array(dataX), numpy.array(dataY) # semilla aleatoria para reproducibilidad numpy.random.seed(7) # cargamos el conjunto de datos dataframe = read_csv('international-airline-passengers.csv', usecols=[1], engine='python', skipfooter=3) dataset = dataframe.values dataset = dataset.astype('float32') # normalizamos el conjunto de datos scaler = MinMaxScaler(feature_range=(0, 1)) dataset = scaler.fit_transform(dataset) # dividimos entre entranmiento y test train_size = int(len(dataset) * 0.67) test_size = len(dataset) - train_size train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:] # remodelamos X=t y Y=t+1 look_back = 3 trainX, trainY = create_dataset(train, look_back) testX, testY = create_dataset(test, look_back) # remodelamos la entrada para que sea[muestras, pasos de tiempo, características] trainX = numpy.reshape(trainX, (trainX.shape[0], trainX.shape[1], 1)) testX = numpy.reshape(testX, (testX.shape[0], testX.shape[1], 1)) # creamos la LSTM network model = Sequential() model.add(LSTM(4, input_shape=(look_back,1))) model.add(Dense(1)) model.compile(loss='mean_squared_error', optimizer='adam') model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=2) # hacemos las predicciones trainPredict = model.predict(trainX) testPredict = model.predict(testX) # invertimos las predicciones trainPredict = scaler.inverse_transform(trainPredict) trainY = scaler.inverse_transform([trainY]) testPredict = scaler.inverse_transform(testPredict) testY = scaler.inverse_transform([testY]) # calculamos el error rms trainScore = math.sqrt(mean_squared_error(trainY[0], trainPredict[:,0])) print('Resultado del entrenamiento: %.2f RMSE' % (trainScore)) testScore = math.sqrt(mean_squared_error(testY[0], testPredict[:,0])) print('Resultado del test: %.2f RMSE' % (testScore)) # predicciones del entrenamiento de cambio para plotear trainPredictPlot = numpy.empty_like(dataset) trainPredictPlot[:, :] = numpy.nan trainPredictPlot[look_back:len(trainPredict)+look_back, :] = trainPredict # predicciones del test de cambio para plotear testPredictPlot = numpy.empty_like(dataset) testPredictPlot[:, :] = numpy.nan testPredictPlot[len(trainPredict)+(look_back*2)+1:len(dataset)-1, :] = testPredict # plot baseline and predictions plt.plot(scaler.inverse_transform(dataset)) # ploteamos linea base y predicciones plt.plot(trainPredictPlot,'r', linewidth = 2) plt.plot(testPredictPlot,'m', linewidth = 2) plt.legend( ('Datos', 'Prediccion datos entramiento', 'Prediccion sobre los datos de test'), loc = 'upper left') plt.grid(True) plt.title("Datos sobre pasajeros por mes desde 1949 a 1960", fontsize = 15) plt.xlabel("Tiempo (nº meses)", fontsize = 10) plt.ylabel("nº de pasajeros", fontsize = 10) plt.show()
Ejecutando el ejemplo mencionado obtenemos
Epoch 97/100 - 0s - loss: 0.0021 Epoch 98/100 - 0s - loss: 0.0020 Epoch 99/100 - 0s - loss: 0.0021 Epoch 100/100 - 0s - loss: 0.0020 Resultado del entrenamiento: 23.70 RMSE Resultado del test: 58.88 RMSE
Podemos ver que los resultados son ligeramente mejores que en el ejemplo anterior, y la estructura de los datos de entrada tiene mucho más sentido.
LSTM con memoria entre Lotes
La red LSTM tiene una memoria que es capaz de recordar a través de largas secuencias. Normalmente, el estado dentro de la red se reajusta después de cada lote de entrenamiento al probar el modelo y cuando llama a model.predict() o model.evaluate(). Podemos obtener el control cuando el estado interno de la red LSTM se borra en Keras haciendo que la capa LSTM sea con estado. Esto significa que puede construir estados a lo largo de toda la secuencia de entrenamiento e incluso mantenerlos si es necesario para hacer predicciones.
Requiere que los datos de formación no se borren cuando se está conectando la red. También requiere restablecimiento explícito del estado de la red después de cada exposición a los datos de entrenamiento (época) mediante llamadas a los estados model.reset(). Esto significa que debemos crear nuestro propio bucle exterior de épocas y dentro de cada época llamar por ejemplo a model.fit() y model.reset states():
for i in range(100): model.fit(trainX, trainY, epochs=1, batch_size=batch_size, verbose=2, shuffle=False) model.reset_states()
Finalmente, cuando se construye la capa LSTM, el parámetro con estado debe ser True y en lugar de especificar las dimensiones de entrada, debemos codificar el número de muestras en un lote, el número de pasos de tiempo en una muestra y el número de características en un paso de tiempo configurando el parámetro de forma del batch input. Por ejemplo:
model.add(LSTM(4, batch_input_shape=(batch_size, time_steps, features), stateful=True))
Este mismo tamaño de lote debe usarse luego al evaluar el modelo y hacer predicciones, por ejemplo:
model.predict(trainX, batch_size=batch_size)
Podemos adaptar el ejemplo de paso de tiempo anterior para usar una LSTM con estado. El listado completo del código se proporciona a continuación:
# LSTM para predecir a los pasajeros de las aerolíneas internacionales con memoria entre Lotes import numpy import matplotlib.pyplot as plt from pandas import read_csv import math from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM from sklearn.preprocessing import MinMaxScaler from sklearn.metrics import mean_squared_error # convertimos un array de valores en una matriz de conjuntos de datos def create_dataset(dataset, look_back=1): dataX, dataY = [], [] for i in range(len(dataset)-look_back-1): a = dataset[i:(i+look_back), 0] dataX.append(a) dataY.append(dataset[i + look_back, 0]) return numpy.array(dataX), numpy.array(dataY) # semilla aleatoria para reproducibilidad numpy.random.seed(7) # cargamos el conjunto de datos dataframe = read_csv('international-airline-passengers.csv', usecols=[1], engine='python', skipfooter=3) dataset = dataframe.values dataset = dataset.astype('float32') # normalizamos el conjunto de datos scaler = MinMaxScaler(feature_range=(0, 1)) dataset = scaler.fit_transform(dataset) # dividimos entre entranmiento y test train_size = int(len(dataset) * 0.67) test_size = len(dataset) - train_size train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:] # remodelamos X=t y Y=t+1 look_back = 3 trainX, trainY = create_dataset(train, look_back) testX, testY = create_dataset(test, look_back) # remodelamos la entrada para que sea[muestras, pasos de tiempo, características] trainX = numpy.reshape(trainX, (trainX.shape[0], trainX.shape[1], 1)) testX = numpy.reshape(testX, (testX.shape[0], testX.shape[1], 1)) # creamos la LSTM network batch_size = 1 model = Sequential() model.add(LSTM(4, batch_input_shape=(batch_size, look_back, 1), stateful=True)) model.add(Dense(1)) model.compile(loss='mean_squared_error', optimizer='adam') for i in range(100): model.fit(trainX, trainY, epochs=1, batch_size=batch_size, verbose=2, shuffle=False) model.reset_states() # hacemos las predicciones trainPredict = model.predict(trainX, batch_size=batch_size) model.reset_states() testPredict = model.predict(testX, batch_size=batch_size) # invertimos las predicciones trainPredict = scaler.inverse_transform(trainPredict) trainY = scaler.inverse_transform([trainY]) testPredict = scaler.inverse_transform(testPredict) testY = scaler.inverse_transform([testY]) # calculamos el error rms trainScore = math.sqrt(mean_squared_error(trainY[0], trainPredict[:,0])) print('Resultado del entrenamiento: %.2f RMSE' % (trainScore)) testScore = math.sqrt(mean_squared_error(testY[0], testPredict[:,0])) print('Resultado del test: %.2f RMSE' % (testScore)) # predicciones del entrenamiento de cambio para plotear trainPredictPlot = numpy.empty_like(dataset) trainPredictPlot[:, :] = numpy.nan trainPredictPlot[look_back:len(trainPredict)+look_back, :] = trainPredict # predicciones del test de cambio para plotear testPredictPlot = numpy.empty_like(dataset) testPredictPlot[:, :] = numpy.nan testPredictPlot[len(trainPredict)+(look_back*2)+1:len(dataset)-1, :] = testPredict # plot baseline and predictions plt.plot(scaler.inverse_transform(dataset)) # ploteamos linea base y predicciones plt.plot(trainPredictPlot,'r', linewidth = 2) plt.plot(testPredictPlot,'m', linewidth = 2) plt.legend( ('Datos', 'Prediccion datos entramiento', 'Prediccion sobre los datos de test'), loc = 'upper left') plt.grid(True) plt.title("Datos sobre pasajeros por mes desde 1949 a 1960", fontsize = 15) plt.xlabel("Tiempo (nº meses)", fontsize = 10) plt.ylabel("nº de pasajeros", fontsize = 10) plt.show()
Ejecutando el ejemplos:
Epoch 1/1 - 0s - loss: 0.0017 Epoch 1/1 - 0s - loss: 0.0017 Epoch 1/1 - 0s - loss: 0.0017 Epoch 1/1 - 0s - loss: 0.0017 Epoch 1/1 - 0s - loss: 0.0017 Resultado del entrenamiento: 20.79 RMSE Resultado del test: 55.42 RMSE
Vemos que los resultados son mejores que algunos y peores que otros. El modelo puede necesitar más módulos y puede necesitar capacitación para más épocas y poder internalizar la estructura del problema.
LSTMs apiladas con memoria entre lotes
Finalmente, echaremos un vistazo a uno de los grandes beneficios de las LSTM, el cual es el hecho de que puedan ser entrenadas exitosamente cuando se apilan en arquitecturas de red profundas. Las redes LSTM se pueden apilar en Keras de la misma manera que otros tipos de capas se pueden apilar. Una adición a la configuración que se requiere es que una capa LSTM antes de cada capa LSTM posterior deba devolver la secuencia. Esto se puede hacer estableciendo el parámetro de secuencias de retorno o return_sequences en la capa True. Podemos extender la LSTM con estado en la sección anterior para tener dos capas, de la siguiente manera:
model.add(LSTM(4, batch_input_shape=(batch_size, look_back, 1), stateful=True, return_sequences=True)) model.add(LSTM(4, batch_input_shape=(batch_size, look_back, 1), stateful=True))
El código completo es el siguiente:
#LSTMs apiladas con memoria entre lotes para predecir a los pasajeros de las aerolíneas internacionales import numpy import matplotlib.pyplot as plt from pandas import read_csv import math from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM from sklearn.preprocessing import MinMaxScaler from sklearn.metrics import mean_squared_error # convertimos un array de valores en una matriz de conjuntos de datos def create_dataset(dataset, look_back=1): dataX, dataY = [], [] for i in range(len(dataset)-look_back-1): a = dataset[i:(i+look_back), 0] dataX.append(a) dataY.append(dataset[i + look_back, 0]) return numpy.array(dataX), numpy.array(dataY) # semilla aleatoria para reproducibilidad numpy.random.seed(7) # cargamos el conjunto de datos dataframe = read_csv('international-airline-passengers.csv', usecols=[1], engine='python', skipfooter=3) dataset = dataframe.values dataset = dataset.astype('float32') # normalizamos el conjunto de datos scaler = MinMaxScaler(feature_range=(0, 1)) dataset = scaler.fit_transform(dataset) # dividimos entre entranmiento y test train_size = int(len(dataset) * 0.67) test_size = len(dataset) - train_size train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:] # remodelamos X=t y Y=t+1 look_back = 3 trainX, trainY = create_dataset(train, look_back) testX, testY = create_dataset(test, look_back) # remodelamos la entrada para que sea[muestras, pasos de tiempo, características] trainX = numpy.reshape(trainX, (trainX.shape[0], trainX.shape[1], 1)) testX = numpy.reshape(testX, (testX.shape[0], testX.shape[1], 1)) # creamos la LSTM network batch_size = 1 model = Sequential() model.add(LSTM(4, batch_input_shape=(batch_size, look_back, 1), stateful=True, return_sequences=True)) model.add(LSTM(4, batch_input_shape=(batch_size, look_back, 1), stateful=True)) model.add(Dense(1)) model.compile(loss='mean_squared_error', optimizer='adam') for i in range(100): model.fit(trainX, trainY, epochs=1, batch_size=batch_size, verbose=2, shuffle=False) model.reset_states() # hacemos las predicciones trainPredict = model.predict(trainX, batch_size=batch_size) model.reset_states() testPredict = model.predict(testX, batch_size=batch_size) # invertimos las predicciones trainPredict = scaler.inverse_transform(trainPredict) trainY = scaler.inverse_transform([trainY]) testPredict = scaler.inverse_transform(testPredict) testY = scaler.inverse_transform([testY]) # calculamos el error rms trainScore = math.sqrt(mean_squared_error(trainY[0], trainPredict[:,0])) print('Resultado del entrenamiento: %.2f RMSE' % (trainScore)) testScore = math.sqrt(mean_squared_error(testY[0], testPredict[:,0])) print('Resultado del test: %.2f RMSE' % (testScore)) # predicciones del entrenamiento de cambio para plotear trainPredictPlot = numpy.empty_like(dataset) trainPredictPlot[:, :] = numpy.nan trainPredictPlot[look_back:len(trainPredict)+look_back, :] = trainPredict # predicciones del test de cambio para plotear testPredictPlot = numpy.empty_like(dataset) testPredictPlot[:, :] = numpy.nan testPredictPlot[len(trainPredict)+(look_back*2)+1:len(dataset)-1, :] = testPredict # plot baseline and predictions plt.plot(scaler.inverse_transform(dataset)) # ploteamos linea base y predicciones plt.plot(trainPredictPlot,'r', linewidth = 2) plt.plot(testPredictPlot,'m', linewidth = 2) plt.legend( ('Datos', 'Prediccion datos entramiento', 'Prediccion sobre los datos de test'), loc = 'upper left') plt.grid(True) plt.title("Datos sobre pasajeros por mes desde 1949 a 1960", fontsize = 15) plt.xlabel("Tiempo (nº meses)", fontsize = 10) plt.ylabel("nº de pasajeros", fontsize = 10) plt.show()
Ejecutando el código podemos ver:
Epoch 1/1 - 0s - loss: 0.0039 Epoch 1/1 - 0s - loss: 0.0039 Epoch 1/1 - 0s - loss: 0.0038 Epoch 1/1 - 0s - loss: 0.0038 Epoch 1/1 - 0s - loss: 0.0037 Epoch 1/1 - 0s - loss: 0.0036 Resultado del entrenamiento: 29.85 RMSE Resultado del test: 79.27 RMSE
Las predicciones en el conjunto de datos de prueba son de nuevo peores. Esto evidencia la necesidad de incrementar las épocas de entrenamiento adicional.
➡ Aprende mucho mas en nuestro curso de Deep Learning:
Esto no predice nada… El resultado es siempre el del día anterior, sencillamente.
Rafael, no predice porque siempre va atrás un paso en el tiempo, si se sincronizaran los gráficos en forma exacta, ahí si.
Hola Mario, gracias por comentar lo revisaré en cuanto pueda.
Si esta bien implementado, los datos set, son los que la red no conoce. Si es así, la red esta prediciendo y bastante bien para una red muy básica.
Buena nota para compartir tus conocimientos. Sigue así que esto suma, mejor dicho , multiplica!!
Insisto que debería estar sincronizado en el tiempo, al ir un paso atrás, en el fondo lo que predice lo implementa un paso después, tengo los mismos resultados al implementar en un backpropagation una secuencia de datos, finalmente va prediciendo pero lo ingresa un paso después. lo cual deja sin efecto el resultado.
Tendrás un ejemplo de series de tiempo usando datos textuales como parámetros para hacer predicciones?
Algo así como
01-01-1990 estudios de ciencias forenses 0
01-01-1991 contabilidad 1
01-01-1992 administración 3
predecir la tercer columna considerando la segunda columna como parámetro junto con la fecha como serie de tiempo.
Me seria de mucha ayuda! Gracias.
Buenas Saul, por ahora no disponemos de ese ejemplo pero cuando lo tengamos te escribiremos, saludos!
buan dia, podria ayudarme en el diseño de un algoritmo similar de LSTM para la prediccion de la radiacion solar,