Introducción a los modelos de Deep Learning para pronóstico
Split serie de tiempo (sequence) en muestras (samples):
Se crea función para partir ( split ) los datos en muestras ( samples ):
X |
y |
---|---|
sample input |
sample output |
sample input |
sample output |
sample input |
sample output |
sequence = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
X, y
[1, 2, 3], [4]
[2, 3, 4], [5]
[3, 4, 5], [6]
...
import numpy as np
def split_sequence(sequence, n_steps):
X, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps
if end_ix > len(sequence)-1:
break
seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
X.append(seq_x)
y.append(seq_y)
return np.array(X), np.array(y)
timeSerie = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
n_steps = 3
split_sequence(timeSerie, n_steps)
(array([[1, 2, 3],
[2, 3, 4],
[3, 4, 5],
[4, 5, 6],
[5, 6, 7],
[6, 7, 8],
[7, 8, 9]]),
array([ 4, 5, 6, 7, 8, 9, 10]))
X, y = split_sequence(timeSerie, n_steps)
for i in range(len(X)):
print(X[i], y[i])
[1 2 3] 4
[2 3 4] 5
[3 4 5] 6
[4 5 6] 7
[5 6 7] 8
[6 7 8] 9
[7 8 9] 10
Con el ejemplo anterior hay 7 muestras ( samples ).
X.shape
(7, 3)
y.shape
(7,)
Datos en 3D para los modelos LSTM y CNN
Los datos deben estar organizados en [samples, timesteps, features]
.
Samples: una secuencia es una muestra ( sample ). Ejemplo de una muestra:
[1 2] 3
.Time Steps: es un punto de observación en la muestra ( sample ). Por tanto, varios time step son una muestra ( sample ).
Features: Es una observación en un time step. Un time step se compone de una o más Features.
Anteriormente se tenían 7 muestras, cada muestra con 3 time steps y
cada time step con 1 Feature : [1 2 3] 4
, es decir,
[7, 3, 1]
.
Cuando se define la capa de entrada en la red LSTM, el modelo asume que
se tienen una o varias muestras y solo se debe especificar el número de
time steps y el número de Features. Esto se especifica en el
argumento input_shape=(time step, Feature)
.
Hasta ahora X está en 2D: (7, 3)
, se debe convertir en 3D. Se
necesita que sea (7, 3, 1)
. Esto se hace con
X = X.reshape((7, 3, 1))
, donde 7 corresponde a las columnas de X y
3 a las filas, entonces se puede hacer de la siguiente manera usando
X.shape[0]
para el número de columnas y X.shape[1]
para el
número de filas.
X
array([[1, 2, 3],
[2, 3, 4],
[3, 4, 5],
[4, 5, 6],
[5, 6, 7],
[6, 7, 8],
[7, 8, 9]])
X = X.reshape((X.shape[0], X.shape[1], 1))
X.shape
(7, 3, 1)
MLP ( Multilayer Perceptrons ) para pronóstico de series de tiempo
Un simple MLP es una sola capa oculta para input y una capa para el output para predecir.
En este modelo los datos de entrada tienen que estar en 2D:
[samples, features]
.
Importar librerías:
import numpy as np
from keras.models import Sequential
from keras.layers import Dense
Función para partir los datos en 2D, es decir, en muestras:
def split_sequence(sequence, n_steps):
X, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps
if end_ix > len(sequence)-1:
break
seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
X.append(seq_x)
y.append(seq_y)
return np.array(X), np.array(y)
Serie de tiempo (datos):
timeSerie = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
Partir los datos en 2D con la función antes definida (crear las muestras):
n_steps = 3
X, y = split_sequence(timeSerie, n_steps)
X
array([[1, 2, 3],
[2, 3, 4],
[3, 4, 5],
[4, 5, 6],
[5, 6, 7],
[6, 7, 8],
[7, 8, 9]])
Definir el modelo MLP:
Primera capa oculata con 100 unidades y capa de salida con una unidad para predecir un solo valor.
model = Sequential()
model.add(Dense(100, activation='relu', input_dim=n_steps))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
Ajuste del modelo MLP:
model.fit(X, y, epochs=2000, verbose=0)
<tensorflow.python.keras.callbacks.History at 0x21a6ee935e0>
Predicción:
Predicción del siguiente valor por fuera de la muestra al ingresar la
secuencia [8, 9, 10]
como entrada. Se espera que el modelo haga la
predicción de [11]
.
La función .predict()
exige que los datos de entrada para predecir
tenga la forma de una muestra, es decir, [1, time step]
.
x_input = np.array([8, 9, 10])
x_input = x_input.reshape((1, n_steps)) # 2D: 1 fila y 3 columnas.
yhat = model.predict(x_input, verbose=0)
yhat
array([[10.999008]], dtype=float32)
LSTM para pronóstico de series de tiempo
Vanilla LSTM: Tiene una sola capa con unidades de LSTM.
La dimensión de cada muestra de entrada se especifica en el argumento
input_shape
cuando se define la primera capa oculta.
Los datos de entrada tienen que estar en 3D:
[samples, timesteps, features]
. Anteriomente se mostró la forma de
cambiar la dimensión de los datos de entrada de 2D a 3D así:
X = X.reshape((X.shape[0], X.shape[1], 1))
donde las Features se
especificó como 1. Ahora se puede cambiar el código a lo siguiente:
n_features = 1 X = X.reshape((X.shape[0], X.shape[1], n_features))
Importar librerías:
import numpy as np
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
Función para partir los datos en 2D, es decir, en muestras:
def split_sequence(sequence, n_steps):
X, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps
if end_ix > len(sequence)-1:
break
seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
X.append(seq_x)
y.append(seq_y)
return np.array(X), np.array(y)
Serie de tiempo (datos):
timeSerie = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
Partir los datos en 2D con la función antes definida (crear las muestras):
n_steps = 3
X, y = split_sequence(timeSerie, n_steps)
X.shape
(7, 3)
Cambiar la dimensión de 2D a 3D:
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
X.shape
(7, 3, 1)
Definir el modelo LSTM:
Primera capa oculata con 50 unidades y capa de salida con una unidad para predecir un solo valor.
model = Sequential()
model.add(LSTM(50, activation='relu', input_shape=(n_steps, n_features)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
Ajuste del modelo LSTM:
model.fit(X, y, epochs=200, verbose=0)
<tensorflow.python.keras.callbacks.History at 0x21a703178b0>
Predicción:
Predicción del siguiente valor por fuera de la muestra al ingresar la
secuencia [8, 9, 10]
como entrada. Se espera que el modelo haga la
predicción de [11]
.
x_input = np.array([8, 9, 10])
x_input = x_input.reshape((1, n_steps, n_features)) # 3D: 1 fila, 3 columnas, 1 Feature.
yhat = model.predict(x_input, verbose=0)
yhat
array([[12.4446]], dtype=float32)
yhat.shape
(1, 1)
LSTM multi capa ( Stacked LSTM model ):
Este modelo se adicional capas una encima de la otra.
El LSTM require que los datos de entra esté en 3D, pero la salida de la
LSTM se produce en 2D. Por tanto, se puede configurar
return sequences=True
para obtener una salida en 3D de la capa
oculta LSTM como entrada de la siguiente capa oculta LSTM
Importar librerías:
import numpy as np
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
Función para partir los datos en 2D, es decir, en muestras:
def split_sequence(sequence, n_steps):
X, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps
if end_ix > len(sequence)-1:
break
seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
X.append(seq_x)
y.append(seq_y)
return np.array(X), np.array(y)
Serie de tiempo (datos):
timeSerie = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
Partir los datos en 2D con la función antes definida (crear las muestras):
n_steps = 3
X, y = split_sequence(timeSerie, n_steps)
X.shape
(7, 3)
Cambiar la dimensión de 2D a 3D:
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
X.shape
(7, 3, 1)
Definir el modelo LSTM:
Primera capa oculata con 50 unidades y capa de salida con una unidad para predecir un solo valor.
model = Sequential()
model.add(LSTM(50, activation='relu', return_sequences=True, input_shape=(n_steps, n_features))) # Primera capa oculta LSTM.
model.add(LSTM(50, activation='relu')) # Segunda capa oculta LSTM.
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
Ajuste del modelo LSTM:
model.fit(X, y, epochs=200, verbose=0)
<tensorflow.python.keras.callbacks.History at 0x21a72d42e80>
Predicción:
Predicción del siguiente valor por fuera de la muestra al ingresar la
secuencia [8, 9, 10]
como entrada. Se espera que el modelo haga la
predicción de [11]
.
x_input = np.array([8, 9, 10])
x_input = x_input.reshape((1, n_steps, n_features)) # 3D: 1 fila, 3 columnas, 1 Feature.
yhat = model.predict(x_input, verbose=0)
yhat
array([[10.31719]], dtype=float32)
Bidirectional LSTM
LSTM Bidireccional permite que el modelo aprenda la secuencia de entrada y la secuencia de salida.
Importar librerías:
import numpy as np
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from keras.layers import Bidirectional
Función para partir los datos en 2D, es decir, en muestras:
def split_sequence(sequence, n_steps):
X, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps
if end_ix > len(sequence)-1:
break
seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
X.append(seq_x)
y.append(seq_y)
return np.array(X), np.array(y)
Serie de tiempo (datos):
timeSerie = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
Partir los datos en 2D con la función antes definida (crear las muestras):
n_steps = 3
X, y = split_sequence(timeSerie, n_steps)
X.shape
(7, 3)
Cambiar la dimensión de 2D a 3D:
n_features = 1
X = X.reshape((X.shape[0], X.shape[1], n_features))
X.shape
(7, 3, 1)
Definir el modelo LSTM:
Primera capa oculata con 50 unidades y capa de salida con una unidad para predecir un solo valor.
model = Sequential()
model.add(Bidirectional(LSTM(50, activation='relu'), input_shape=(n_steps, n_features)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
Ajuste del modelo LSTM:
model.fit(X, y, epochs=200, verbose=0)
<tensorflow.python.keras.callbacks.History at 0x21a72d42dc0>
Predicción:
Predicción del siguiente valor por fuera de la muestra al ingresar la
secuencia [8, 9, 10]
como entrada. Se espera que el modelo haga la
predicción de [11]
.
x_input = np.array([8, 9, 10])
x_input = x_input.reshape((1, n_steps, n_features)) # 3D: 1 fila, 3 columnas, 1 Feature.
yhat = model.predict(x_input, verbose=0)
yhat
array([[12.07001]], dtype=float32)