Blog
Modelos MLP multivariantes (1/2)
- Publicado por: Rafael Fernandez
- Categoría: Blog Deep Learning Nivel Intermedio Python
Serie de entradas múltiples
Modelos MLP multivariantes
Los datos de series cronológicas multivariadas son los datos en los que hay más de una observación para cada paso temporal. Hay dos modelos principales que podemos requerir con datos de series de tiempo multivariadas; que son las siquientes:
- Serie de entradas múltiples
- Múltiple series paralelas
Serie de entradas múltiples
Un problema puede tener dos o mas series temporales de entrada paralela y una serie temporal de salida que depende de la serie temporal de entrada. Las series de tiempo de entrada son paralelas por que cada serie tiene una observación en el mismo paso temporal. Podemos demostrar esto con un ejemplo simple de dos series de tiempo de entrada paralela donde la serie de salida es la simple adición de la serie de entrada.
#definir la secuencia de entrada in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90]) in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95]) out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))])
Podemos remodelar estos tres conjuntos de datos como un único conjunto de datos en el que cada fila es un paso temporal y cada columna es una serie temporal separada. Esta es una forma estándar de almacenar series de tiempo paralelas en un archivo CSV.
#convertir a la estructura [rows, columns] in_seq1 = in_seq1.reshape((len(in_seq1), 1)) in_seq2 = in_seq2.reshape((len(in_seq2), 1)) out_seq = out_seq.reshape((len(out_seq), 1)) #columnas aplicadas horizontales dataset = hstack((in_seq1, in_seq2, out_seq))
Este es el ejemplo completo.
#preparacion de los datos multivariados from numpy import array from numpy import hstack #definir la secuencia de entrada in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90]) in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95]) out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))]) #convertir a la estructura [rows, columns] in_seq1 = in_seq1.reshape((len(in_seq1), 1)) in_seq2 = in_seq2.reshape((len(in_seq2), 1)) out_seq = out_seq.reshape((len(out_seq), 1)) #columnas apiladas horizontalmente dataset = hstack((in_seq1, in_seq2, out_seq)) print(dataset)
Al ejecutar el ejemplo imprime el conjunto de datos con una fila por paso de tiempo y una columna para cada una de las dos series de tiempo paralela de entrada y una de salida.
[[ 10 15 25] [ 20 25 45] [ 30 35 65] [ 40 45 85] [ 50 55 105] [ 60 65 125] [ 70 75 145] [ 80 85 165] [ 90 95 185]]
Al igual que con las series temporales univariadas, debemos estructurar estos datos en muestras con muestras de entrada y salida. Necesitamos dividir los datos en muestras manteniendo el orden de las observaciones a través de las dos secuencias de entrada. Si elegimos tres pasos de tiempo de entrada, entonces la primera muestra tendría el siguiente aspecto:
Entrada:
10, 15 20, 25 30, 35
Salida:
65
Es decir, los tres primero pasos de tiempo de cada serie paralela se proporcionan como entrada al modelo y el modelo asocia con el valor de la serie de salida en el tercer paso de tiempo, en este caso el 65. Podemos ver que, al transformar la serie temporal en muestras de entrada/salida para formar el modelo, tendremos que descartar algunos valores de la serie temporal de salida en los que no tenemos valores en la serie temporal de entrada en pasos temporales anteriores. A su vez, la elección del tamaño del número de pasos de tiempo de entrada tendrá un importante efecto sobre la cantidad de datos de formación que se utilicen. Podemos definir una función llamada split_sequence() que tomará un conjunto con datos como lo hemos definido con filas para pasos de tiempo y columnas para series paralelas y devolver muestras de entrada/salida.
#defini una secuencia multivariante en muestras def split_sequences(sequences, n_steps): X, y = list(), list() for i in range(len(sequences)): #encontrar el final de este patron end_ix = i + n_steps #comprobar si estamos mas alla del conjunto de datos if end_ix > len(sequences): break #recoger las partes de entrada y salida del patron seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1] X.append(seq_x) y.append(seq_y) return array(X), array(y)
Podemos probar una función en nuestro conjunto de datos utilizando tres pasos de tiempo para cada serie de tiempo de entrada como entrada. Como muestra en este ejemplo.
#preparacion de los datos multivariados from numpy import array from numpy import hstack #dividir una secuencia multivariante en muestras def split_sequences(sequences, n_steps): X, y = list(), list() for i in range(len(sequences)): #encontran el final de este patron end_ix = i + n_steps #comprobar si estamos mas alla del conjunto de datos if end_ix > len(sequences): break #recoger las partes de entrada y salida del patron seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1] X.append(seq_x) y.append(seq_y) return array(X), array(y) #definir la secuencia de entrada in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90]) in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95]) out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))]) #convertir a la estructura [rows, columns] in_seq1 = in_seq1.reshape((len(in_seq1), 1)) in_seq2 = in_seq2.reshape((len(in_seq2), 1)) out_seq = out_seq.reshape((len(out_seq), 1)) #columnas aplicadas horizontalmente dataset = hstack((in_seq1, in_seq2, out_seq)) #seleccionar un numero de pasos de tiempo n_steps = 3 #convertir en entrada salida X, y = split_sequences(dataset, n_steps) print(X.shape, y.shape) #resumir los datos for i in range(len(X)): print(X[i], y[i])
Al ejecutar el ejemplo primero se imprime la forma de los componentes X e y. Podemos ver que el componente X tiene una estructura tridimensional. La primera dimensión es el número de muestras, en este caso 7. La segunda dimensión es el número de pasos de tiempo por muestreo, en este caso 3, el valor especifico de la función. Finalmente, la ultima dimensión especifica el número de series temporales paralelas o el número de variables, en este caso 2 para las dos series paralelas. A continuación se puede ver que se imprime la entrada y la salida de cada muestra, mostrando los tres pasos de tiempo para cada una de las dos series de entrada y de salida asociada para cada muestra.
(7, 3, 2) (7,) [[10 15] [20 25] [30 35]] 65 [[20 25] [30 35] [40 45]] 85 [[30 35] [40 45] [50 55]] 105 [[40 45] [50 55] [60 65]] 125 [[50 55] [60 65] [70 75]] 145 [[60 65] [70 75] [80 85]] 165 [[70 75] [80 85] [90 95]] 185
Modelo MLP
Antes de que podamos hacer un MLP sobre estos datos, debemos atenuar la forma de las muestras de entrada. MLP requieren que la forma de la porción de entrada de cada muestra sea un vector. Con una entrada multivariante, tendremos múltiples vectores, uno para cada paso temporal. Podemos atenuar la estructura temporal de cada muestra de entrada, así que:
[[10 15] [20 25] [30 35]]
Se convierte:
[10, 15, 20, 25, 30, 35]
Primero, podemos calcular la longitud de cada vector de entrada como el número de pasos de tiempo multiplicado por el número de características o series temporales. Entonces podemos usar este tamaño vectorial para remodelar la entrada.
#aplanar la entrada n_input = X.shape[1] * X.shape[2] X = X.reshape((X.shape[0], n_input))
Ahora podemos definir un modelo MLP para la entrada multivariante en la que se utiliza la longitud del vector para el argumento de la dimensión de entrada.
#definir modelo model = Sequential() model.add(Dense(100, activation='relu', input_dim=n_input)) model.add(Dense(1)) model.compile(optimizer='adam', loss='mse')
Al hacer una predicción, el modelo espera tres pasos de tiempo para dos series de tiempo de entrada. Podemos predecir el siguiente valor en la serie de salida probando los valores de entrada de:
80, 85 90, 95 100, 105
La forma de la muestra con 3 pasos de tiempo y 2 variables sería [1, 3, 2]. Debemos volver a dar forma a esta muestra para que sea 1 muestra con un vector de 6 elementos o[1, 6]. Es de esperar que el siguiente valor en la secuencia será 100 + 105 ó 205.
# demostrar prediccion x_input = array([[80, 85], [90, 95], [100, 105]]) x_input = x_input.reshape((1, n_input)) yhat = model.predict(x_input, verbose=0)
El ejemplo completo se muestra a continuación.
# ejemplo de mlp multivariado from numpy import array from numpy import hstack from keras.models import Sequential from keras.layers import Dense # dividir una secuencia multivariante en muestras def split_sequences(sequences, n_steps): X, y = list(), list() for i in range(len(sequences)): # para encontrar el final del patron end_ix = i + n_steps # para comprobar si estamas mas alla del conjunto de datdo if end_ix > len(sequences): break # recoger las partes de entrada y salida del patron seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1] X.append(seq_x) y.append(seq_y) return array(X), array(y) # definir la secuencia de entrada in_seq1 = array([10, 20, 30, 40, 50, 60, 70, 80, 90]) in_seq2 = array([15, 25, 35, 45, 55, 65, 75, 85, 95]) out_seq = array([in_seq1[i]+in_seq2[i] for i in range(len(in_seq1))]) # convertir a la estructura [rows, columns] in_seq1 = in_seq1.reshape((len(in_seq1), 1)) in_seq2 = in_seq2.reshape((len(in_seq2), 1)) out_seq = out_seq.reshape((len(out_seq), 1)) # columnas aplicada horizontalmete dataset = hstack((in_seq1, in_seq2, out_seq)) # seleccionar numero de pasos n_steps = 3 # convertir en entrada/salida X, y = split_sequences(dataset, n_steps) # aplanar la entrada n_input = X.shape[1] * X.shape[2] X = X.reshape((X.shape[0], n_input)) # definir modelo model = Sequential() model.add(Dense(100, activation='relu', input_dim=n_input)) model.add(Dense(1)) model.compile(optimizer='adam', loss='mse') # modelo adecuado model.fit(X, y, epochs=2000, verbose=0) # demostrar prediccion x_input = array([[80, 85], [90, 95], [100, 105]]) x_input = x_input.reshape((1, n_input)) yhat = model.predict(x_input, verbose=0) print(yhat)
Ejecutando el ejemplo se preparan los datos, el modelo y se hace una predicción.
[[ 206.567276]]
Dada la naturaliza estocástica del algoritmo, los resultados de la especificación pueden variar. Considere la posibilidad de ejecutar el ejemplo unas cuantas veces.