Cómo realizar búsquedas en cuadrícula de modelos de aprendizaje profundo para el pronóstico de series temporales
La búsqueda en cuadrícula generalmente no es una operación que podamos realizar con métodos de aprendizaje profundo.
Esto se debe a que los métodos de aprendizaje profundo a menudo requieren grandes cantidades de datos y modelos grandes, lo que da como resultado modelos que tardan horas, días o semanas en entrenarse.
En aquellos casos en los que los conjuntos de datos son más pequeños, como las series temporales univariadas, es posible utilizar una búsqueda en cuadrícula para ajustar los hiperparámetros de un modelo de aprendizaje profundo.
En este tutorial, descubrirá cómo desarrollar un marco para hiperparámetros de búsqueda en cuadrícula para modelos de aprendizaje profundo.
Después de completar este tutorial, sabrá:
- Cómo desarrollar un marco de búsqueda de cuadrícula genérico para ajustar los hiperparámetros del modelo.
- Cómo generar hiperparámetros de búsqueda en cuadrícula para un modelo de perceptrón multicapa en el problema de pronóstico de series temporales univariadas de pasajeros de aerolíneas.
- Cómo adaptar el marco a hiperparámetros de búsqueda de cuadrícula para redes neuronales convolucionales y de memoria a corto plazo.
Pon en marcha tu proyecto con mi nuevo libro Deep Learning for Time Series Forecasting, que incluye tutoriales paso a paso y los archivos de código fuente de Python. para todos los ejemplos.
Empecemos.
- Actualización de mayo de 2019: se solucionó un pequeño problema de doble asignación en el código (gracias Jameson).
Descripción general del tutorial
Este tutorial se divide en cinco partes; ellos son:
- Problema de series temporales
- Marco de búsqueda de cuadrícula
- Perceptrón multicapa de búsqueda de cuadrícula
- Red neuronal convolucional de búsqueda de cuadrícula
- Búsqueda de cuadrícula Red de memoria a largo plazo
Problema de series temporales
El conjunto de datos "pasajero aéreo mensual" resume el número total mensual de pasajeros internacionales en miles de una aerolínea entre 1949 y 1960.
Descargue el conjunto de datos directamente desde aquí:
- pasajeros-aerolineas-mensuales.csv
Guarde el archivo con el nombre 'monthly-airline-passengers.csv' en su directorio de trabajo actual.
Podemos cargar este conjunto de datos como una serie de Pandas usando la función read_csv().
# load
series = read_csv('monthly-airline-passengers.csv', header=0, index_col=0)
Una vez cargado, podemos resumir la forma del conjunto de datos para determinar el número de observaciones.
# summarize shape
print(series.shape)
Luego podemos crear un diagrama lineal de la serie para tener una idea de la estructura de la serie.
# plot
pyplot.plot(series)
pyplot.show()
Podemos unir todo esto; el ejemplo completo se enumera a continuación.
# load and plot dataset
from pandas import read_csv
from matplotlib import pyplot
# load
series = read_csv('monthly-airline-passengers.csv', header=0, index_col=0)
# summarize shape
print(series.shape)
# plot
pyplot.plot(series)
pyplot.show()
Al ejecutar el ejemplo primero se imprime la forma del conjunto de datos.
(144, 1)
El conjunto de datos es mensual y tiene 12 años, o 144 observaciones. En nuestras pruebas, utilizaremos el último año, o 12 observaciones, como conjunto de prueba.
Se crea un diagrama de líneas. El conjunto de datos tiene una tendencia obvia y un componente estacional. El período del componente estacional es de 12 meses.
En este tutorial, presentaremos las herramientas para la búsqueda de cuadrículas, pero no optimizaremos los hiperparámetros del modelo para este problema. En su lugar, demostraremos cómo realizar una búsqueda en cuadrícula de los hiperparámetros del modelo de aprendizaje profundo en general y encontrar modelos con cierta habilidad en comparación con un modelo ingenuo.
A partir de experimentos anteriores, un modelo ingenuo puede lograr un error cuadrático medio, o RMSE, de 50,70 (recuerde que las unidades son miles de pasajeros) manteniendo el valor de hace 12 meses (índice relativo -12).
El desempeño de este modelo ingenuo proporciona un límite a un modelo que se considera hábil para este problema. Cualquier modelo que logre un rendimiento predictivo inferior a 50,70 en los últimos 12 meses tiene habilidad.
Cabe señalar que un modelo ETS sintonizado puede alcanzar un RMSE de 17,09 y un SARIMA sintonizado puede alcanzar un RMSE de 13,89. Estos proporcionan un límite inferior a las expectativas de un modelo de aprendizaje profundo bien ajustado para este problema.
Ahora que hemos definido nuestro problema y las expectativas de la habilidad del modelo, podemos considerar la definición del arnés de prueba de búsqueda de cuadrícula.
Marco de búsqueda de cuadrícula
En esta sección, desarrollaremos un arnés de prueba de búsqueda de cuadrícula que se puede utilizar para evaluar una variedad de hiperparámetros para diferentes modelos de redes neuronales, como MLP, CNN y LSTM.
Esta sección se divide en las siguientes partes:
- División de prueba de tren
- Serie como aprendizaje supervisado
- Validación anticipada
- Repetir evaluación
- Resumir el rendimiento
- Ejemplo resuelto
División de prueba de tren
El primer paso es dividir la serie cargada en conjuntos de tren y de prueba.
Usaremos los primeros 11 años (132 observaciones) para entrenamiento y los últimos 12 para el conjunto de prueba.
La función train_test_split() a continuación dividirá la serie tomando las observaciones sin procesar y el número de observaciones a usar en el conjunto de prueba como argumentos.
# split a univariate dataset into train/test sets
def train_test_split(data, n_test):
return data[:-n_test], data[-n_test:]
Serie como aprendizaje supervisado
A continuación, debemos poder enmarcar la serie univariada de observaciones como un problema de aprendizaje supervisado para poder entrenar modelos de redes neuronales.
Un marco de aprendizaje supervisado de una serie significa que los datos deben dividirse en múltiples ejemplos de los que el modelo aprende y generaliza.
Cada muestra debe tener un componente de entrada y un componente de salida.
El componente de entrada será una cierta cantidad de observaciones previas, como tres años o 36 intervalos de tiempo.
El componente de producción serán las ventas totales del próximo mes porque estamos interesados en desarrollar un modelo para hacer pronósticos de un solo paso.
Podemos implementar esto usando la función shift() en el DataFrame de pandas. Nos permite desplazar una columna hacia abajo (adelante en el tiempo) o hacia atrás (retroceso en el tiempo). Podemos tomar la serie como una columna de datos y luego crear múltiples copias de la columna, desplazarla hacia adelante o hacia atrás en el tiempo para crear las muestras con los elementos de entrada y salida que necesitamos.
Cuando una serie se desplaza hacia abajo, se introducen valores de NaN porque no tenemos valores más allá del inicio de la serie.
Por ejemplo, la serie definida como columna:
(t)
1
2
3
4
Esta columna se puede desplazar e insertar como columna de antemano:
(t-1), (t)
Nan, 1
1, 2
2, 3
3, 4
4 NaN
Podemos ver que en la segunda fila, el valor 1 se proporciona como entrada como una observación en el paso de tiempo anterior, y 2 es el siguiente valor en la serie que puede predecirse o aprenderse mediante el modelo para predecir cuando 1 es presentado como entrada.
Las filas con valores NaN se pueden eliminar.
La siguiente función series_to_supervised() implementa este comportamiento, permitiéndole especificar el número de observaciones de retraso que se usarán en la entrada y el número que se usará en la salida para cada muestra. También eliminará las filas que tengan valores NaN, ya que no se pueden usar para entrenar o probar un modelo.
# transform list into supervised learning format
def series_to_supervised(data, n_in=1, n_out=1):
df = DataFrame(data)
cols = list()
# input sequence (t-n, ... t-1)
for i in range(n_in, 0, -1):
cols.append(df.shift(i))
# forecast sequence (t, t+1, ... t+n)
for i in range(0, n_out):
cols.append(df.shift(-i))
# put it all together
agg = concat(cols, axis=1)
# drop rows with NaN values
agg.dropna(inplace=True)
return agg.values
Validación anticipada
Los modelos de pronóstico de series de tiempo se pueden evaluar en un conjunto de pruebas mediante validación de avance.
La validación directa es un enfoque en el que el modelo realiza un pronóstico para cada observación en el conjunto de datos de prueba, una a la vez. Después de realizar cada pronóstico para un paso de tiempo en el conjunto de datos de prueba, la observación verdadera del pronóstico se agrega al conjunto de datos de prueba y se pone a disposición del modelo.
Los modelos más simples pueden reajustarse con la observación antes de realizar la predicción posterior. Los modelos más complejos, como las redes neuronales, no se reacondicionan dado el costo computacional mucho mayor.
Sin embargo, la observación verdadera para el paso de tiempo se puede utilizar como parte de la entrada para hacer la predicción en el siguiente paso de tiempo.
Primero, el conjunto de datos se divide en conjuntos de tren y de prueba. Llamaremos a la función train_test_split() para realizar esta división y pasar el número preespecificado de observaciones para usar como datos de prueba.
Un modelo se ajustará una vez al conjunto de datos de entrenamiento para una configuración determinada.
Definiremos una función genérica model_fit() para realizar esta operación que se puede completar para el tipo dado de red neuronal que nos pueda interesar más adelante. La función toma el conjunto de datos de entrenamiento y la configuración del modelo y devuelve el modelo ajustado listo para hacer predicciones.
# fit a model
def model_fit(train, config):
return None
Se enumera cada paso de tiempo del conjunto de datos de prueba. Se realiza una predicción utilizando el modelo de ajuste.
Nuevamente, definiremos una función genérica llamada model_predict() que toma el modelo de ajuste, el historial y la configuración del modelo y realiza una predicción de un solo paso.
# forecast with a pre-fit model
def model_predict(model, history, config):
return 0.0
La predicción se agrega a una lista de predicciones y la observación verdadera del conjunto de prueba se agrega a una lista de observaciones que se sembró con todas las observaciones del conjunto de datos de entrenamiento. Esta lista se crea durante cada paso de la validación preliminar, lo que permite al modelo realizar una predicción de un paso utilizando el historial más reciente.
Luego, todas las predicciones se pueden comparar con los valores verdaderos en el conjunto de prueba y calcular una medida de error.
Calcularemos la raíz del error cuadrático medio, o RMSE, entre las predicciones y los valores verdaderos.
RMSE se calcula como la raíz cuadrada del promedio de las diferencias al cuadrado entre los valores previstos y reales. measure_rmse() implementa esto a continuación usando la función scikit-learn mean_squared_error() para calcular primero el error cuadrático medio, o MSE, antes de calcular la raíz cuadrada.
# root mean squared error or rmse
def measure_rmse(actual, predicted):
return sqrt(mean_squared_error(actual, predicted))
La función walk_forward_validation() completa que une todo esto se enumera a continuación.
Toma el conjunto de datos, el número de observaciones que se utilizarán como conjunto de prueba y la configuración del modelo, y devuelve el RMSE para el rendimiento del modelo en el conjunto de prueba.
# walk-forward validation for univariate data
def walk_forward_validation(data, n_test, cfg):
predictions = list()
# split dataset
train, test = train_test_split(data, n_test)
# fit model
model = model_fit(train, cfg)
# seed history with training dataset
history = [x for x in train]
# step over each time-step in the test set
for i in range(len(test)):
# fit model and make forecast for history
yhat = model_predict(model, history, cfg)
# store forecast in list of predictions
predictions.append(yhat)
# add actual observation to history for the next loop
history.append(test[i])
# estimate prediction error
error = measure_rmse(test, predictions)
print(' > %.3f' % error)
return error
Repetir evaluación
Los modelos de redes neuronales son estocásticos.
Esto significa que, dada la misma configuración del modelo y el mismo conjunto de datos de entrenamiento, cada vez que se entrene el modelo se obtendrá un conjunto interno diferente de pesos que, a su vez, tendrá un rendimiento diferente.
Este es un beneficio, ya que permite que el modelo sea adaptable y encuentre configuraciones de alto rendimiento para problemas complejos.
También es un problema a la hora de evaluar el rendimiento de un modelo y elegir un modelo final para realizar predicciones.
Para abordar la evaluación del modelo, evaluaremos la configuración de un modelo varias veces mediante una validación directa e informaremos el error como el error promedio en cada evaluación.
Esto no siempre es posible para redes neuronales grandes y puede que solo tenga sentido para redes pequeñas que pueden instalarse en minutos u horas.
La función repeat_evaluate() a continuación implementa esto y permite especificar el número de repeticiones como un parámetro opcional que por defecto es 10 y devuelve la puntuación RMSE media de todas las repeticiones.
# score a model, return None on failure
def repeat_evaluate(data, config, n_test, n_repeats=10):
# convert config to a key
key = str(config)
# fit and evaluate the model n times
scores = [walk_forward_validation(data, n_test, config) for _ in range(n_repeats)]
# summarize score
result = mean(scores)
print('> Model[%s] %.3f' % (key, result))
return (key, result)
Búsqueda de cuadrícula
Ya tenemos todas las piezas del marco.
Todo lo que queda es una función para impulsar la búsqueda. Podemos definir una función grid_search() que toma el conjunto de datos, una lista de configuraciones para buscar y el número de observaciones para usar como conjunto de prueba y realizar la búsqueda.
Una vez que se calculan las puntuaciones medias para cada configuración, la lista de configuraciones se ordena en orden ascendente para que las mejores puntuaciones aparezcan primero.
La función completa se enumera a continuación.
# grid search configs
def grid_search(data, cfg_list, n_test):
# evaluate configs
scores = [score_model(data, n_test, cfg) for cfg in cfg_list]
# sort configs by error, asc
scores.sort(key=lambda tup: tup[1])
return scores
Ejemplo resuelto
Ahora que hemos definido los elementos del arnés de prueba, podemos unirlos todos y definir un modelo de persistencia simple.
No necesitamos ajustarnos a un modelo, por lo que se implementará la función model_fit() para simplemente devolver Ninguno.
# fit a model
def model_fit(train, config):
return None
Usaremos la configuración para definir una lista de compensaciones de índice en las observaciones anteriores en relación con el tiempo a pronosticar que se usará como predicción. Por ejemplo, 12 utilizará la observación de hace 12 meses (-12) en relación con el tiempo a pronosticar.
# define config
cfg_list = [1, 6, 12, 24, 36]
La función model_predict() se puede implementar para usar esta configuración para conservar el valor en el desplazamiento relativo negativo.
# forecast with a pre-fit model
def model_predict(model, history, offset):
history[-offset]
A continuación se enumera el ejemplo completo del uso del marco con un modelo de persistencia simple.
# grid search persistence models for airline passengers
from math import sqrt
from numpy import mean
from pandas import read_csv
from sklearn.metrics import mean_squared_error
# split a univariate dataset into train/test sets
def train_test_split(data, n_test):
return data[:-n_test], data[-n_test:]
# root mean squared error or rmse
def measure_rmse(actual, predicted):
return sqrt(mean_squared_error(actual, predicted))
# fit a model
def model_fit(train, config):
return None
# forecast with a pre-fit model
def model_predict(model, history, offset):
return history[-offset]
# walk-forward validation for univariate data
def walk_forward_validation(data, n_test, cfg):
predictions = list()
# split dataset
train, test = train_test_split(data, n_test)
# fit model
model = model_fit(train, cfg)
# seed history with training dataset
history = [x for x in train]
# step over each time-step in the test set
for i in range(len(test)):
# fit model and make forecast for history
yhat = model_predict(model, history, cfg)
# store forecast in list of predictions
predictions.append(yhat)
# add actual observation to history for the next loop
history.append(test[i])
# estimate prediction error
error = measure_rmse(test, predictions)
print(' > %.3f' % error)
return error
# score a model, return None on failure
def repeat_evaluate(data, config, n_test, n_repeats=10):
# convert config to a key
key = str(config)
# fit and evaluate the model n times
scores = [walk_forward_validation(data, n_test, config) for _ in range(n_repeats)]
# summarize score
result = mean(scores)
print('> Model[%s] %.3f' % (key, result))
return (key, result)
# grid search configs
def grid_search(data, cfg_list, n_test):
# evaluate configs
scores = [repeat_evaluate(data, cfg, n_test) for cfg in cfg_list]
# sort configs by error, asc
scores.sort(key=lambda tup: tup[1])
return scores
# define dataset
series = read_csv('monthly-airline-passengers.csv', header=0, index_col=0)
data = series.values
# data split
n_test = 12
# model configs
cfg_list = [1, 6, 12, 24, 36]
# grid search
scores = grid_search(data, cfg_list, n_test)
print('done')
# list top 10 configs
for cfg, error in scores[:10]:
print(cfg, error)
Al ejecutar el ejemplo se imprime el RMSE del modelo evaluado mediante validación de avance en los últimos 12 meses de datos.
Cada configuración del modelo se evalúa 10 veces, aunque, debido a que el modelo no tiene elemento estocástico, la puntuación es la misma cada vez.
Al final de la ejecución, se informan las configuraciones y RMSE para las tres configuraciones del modelo con mejor rendimiento.
Podemos ver, como era de esperar, que mantener el valor de hace un año (compensación relativa -12) resultó en el mejor rendimiento para el modelo de persistencia.
...
> 110.274
> 110.274
> 110.274
> Model[36] 110.274
done
12 50.708316214732804
1 53.1515129919491
24 97.10990337413241
36 110.27352356753639
6 126.73495965991387
Ahora que tenemos un instrumento de prueba sólido para los hiperparámetros del modelo de búsqueda de cuadrículas, podemos usarlo para evaluar un conjunto de modelos de redes neuronales.
Perceptrón multicapa de búsqueda de cuadrícula
Hay muchos aspectos del MLP que quizás deseemos ajustar.
Definiremos un modelo muy simple con una capa oculta y definiremos cinco hiperparámetros para ajustar. Ellos son:
- n_input: el número de entradas anteriores que se utilizarán como entrada para el modelo (por ejemplo, 12 meses).
- n_nodes: el número de nodos que se utilizarán en la capa oculta (por ejemplo, 50).
- n_epochs: el número de épocas de entrenamiento (por ejemplo, 1000).
- n_batch: el número de muestras que se incluirán en cada mini lote (por ejemplo, 32).
- n_diff: el orden de diferencia (por ejemplo, 0 o 12).
Las redes neuronales modernas pueden manejar datos sin procesar con poco procesamiento previo, como escalado y diferenciación. Sin embargo, cuando se trata de datos de series temporales, a veces diferenciar las series puede hacer que un problema sea más fácil de modelar.
Recuerde que la diferenciación es la transformación de los datos de manera que un valor de una observación anterior se resta de la observación actual, eliminando la estructura de tendencia o estacionalidad.
Agregaremos soporte para diferenciación al arnés de prueba de búsqueda de cuadrícula, en caso de que agregue valor a su problema específico. Agrega valor al conjunto de datos internos de pasajeros de aerolíneas.
La función difference() a continuación calculará la diferencia de un orden determinado para el conjunto de datos.
# difference dataset
def difference(data, order):
return [data[i] - data[i - order] for i in range(order, len(data))]
La diferenciación será opcional, donde un orden de 0 sugiere que no hay diferenciación, mientras que un orden 1 o un orden 12 requerirán que los datos se diferencien antes de ajustar el modelo y que las predicciones del modelo necesitarán que se invierta la diferenciación antes de devolver el pronóstico. .
Ahora podemos definir los elementos necesarios para encajar el modelo MLP en el arnés de prueba.
Primero, debemos descomprimir la lista de hiperparámetros.
# unpack config
n_input, n_nodes, n_epochs, n_batch, n_diff = config
A continuación, debemos preparar los datos, incluida la diferenciación, transformando los datos a un formato supervisado y separando los aspectos de entrada y salida de las muestras de datos.
# prepare data
if n_diff > 0:
train = difference(train, n_diff)
# transform series into supervised format
data = series_to_supervised(train, n_in=n_input)
# separate inputs and outputs
train_x, train_y = data[:, :-1], data[:, -1]
Ahora podemos definir y ajustar el modelo con la configuración proporcionada.
# define model
model = Sequential()
model.add(Dense(n_nodes, activation='relu', input_dim=n_input))
model.add(Dense(1))
model.compile(loss='mse', optimizer='adam')
# fit model
model.fit(train_x, train_y, epochs=n_epochs, batch_size=n_batch, verbose=0)
La implementación completa de la función model_fit() se enumera a continuación.
# fit a model
def model_fit(train, config):
# unpack config
n_input, n_nodes, n_epochs, n_batch, n_diff = config
# prepare data
if n_diff > 0:
train = difference(train, n_diff)
# transform series into supervised format
data = series_to_supervised(train, n_in=n_input)
# separate inputs and outputs
train_x, train_y = data[:, :-1], data[:, -1]
# define model
model = Sequential()
model.add(Dense(n_nodes, activation='relu', input_dim=n_input))
model.add(Dense(1))
model.compile(loss='mse', optimizer='adam')
# fit model
model.fit(train_x, train_y, epochs=n_epochs, batch_size=n_batch, verbose=0)
return model
Los cinco hiperparámetros elegidos no son de ninguna manera los únicos ni los mejores hiperparámetros del modelo a ajustar. Puede modificar la función para ajustar otros parámetros, como la adición y el tamaño de más capas ocultas y mucho más.
Una vez que el modelo esté ajustado, podemos usarlo para hacer pronósticos.
Si los datos fueron diferenciados, la diferencia debe invertirse para la predicción del modelo. Esto implica agregar el valor en el desplazamiento relativo del historial al valor predicho por el modelo.
# invert difference
correction = 0.0
if n_diff > 0:
correction = history[-n_diff]
...
# correct forecast if it was differenced
return correction + yhat[0]
También significa que se debe diferenciar el historial para que los datos de entrada utilizados para hacer la predicción tengan la forma esperada.
# calculate difference
history = difference(history, n_diff)
Una vez preparados, podemos usar los datos históricos para crear una muestra única como entrada al modelo para realizar una predicción de un solo paso.
La forma de una muestra debe ser [1, n_input] donde n_input es el número elegido de observaciones de retraso que se utilizarán.
# shape input for model
x_input = array(history[-n_input:]).reshape((1, n_input))
Finalmente, se puede hacer una predicción.
# make forecast
yhat = model.predict(x_input, verbose=0)
La implementación completa de la función model_predict() se enumera a continuación.
A continuación, debemos definir el rango de valores a probar para cada hiperparámetro.
Podemos definir una función model_configs() que crea una lista de las diferentes combinaciones de parámetros para probar.
Definiremos un pequeño subconjunto de configuraciones para probar como ejemplo, incluida una diferencia de 12 meses, que esperamos sea necesaria. Le recomendamos que experimente con modelos independientes, revise los gráficos de diagnóstico de la curva de aprendizaje y utilice información sobre el dominio para establecer rangos de valores de los hiperparámetros para la búsqueda en cuadrícula.
También se le recomienda repetir la búsqueda en la cuadrícula para limitar los rangos de valores que parecen mostrar un mejor rendimiento.
A continuación se enumera una implementación de la función model_configs().
# create a list of configs to try
def model_configs():
# define scope of configs
n_input = [12]
n_nodes = [50, 100]
n_epochs = [100]
n_batch = [1, 150]
n_diff = [0, 12]
# create configs
configs = list()
for i in n_input:
for j in n_nodes:
for k in n_epochs:
for l in n_batch:
for m in n_diff:
cfg = [i, j, k, l, m]
configs.append(cfg)
print('Total configs: %d' % len(configs))
return configs
Ahora tenemos todas las piezas necesarias para realizar una búsqueda en cuadrícula de modelos MLP para un problema de pronóstico de series temporales univariadas.
El ejemplo completo se enumera a continuación.
# grid search mlps for airline passengers
from math import sqrt
from numpy import array
from numpy import mean
from pandas import DataFrame
from pandas import concat
from pandas import read_csv
from sklearn.metrics import mean_squared_error
from keras.models import Sequential
from keras.layers import Dense
# split a univariate dataset into train/test sets
def train_test_split(data, n_test):
return data[:-n_test], data[-n_test:]
# transform list into supervised learning format
def series_to_supervised(data, n_in=1, n_out=1):
df = DataFrame(data)
cols = list()
# input sequence (t-n, ... t-1)
for i in range(n_in, 0, -1):
cols.append(df.shift(i))
# forecast sequence (t, t+1, ... t+n)
for i in range(0, n_out):
cols.append(df.shift(-i))
# put it all together
agg = concat(cols, axis=1)
# drop rows with NaN values
agg.dropna(inplace=True)
return agg.values
# root mean squared error or rmse
def measure_rmse(actual, predicted):
return sqrt(mean_squared_error(actual, predicted))
# difference dataset
def difference(data, order):
return [data[i] - data[i - order] for i in range(order, len(data))]
# fit a model
def model_fit(train, config):
# unpack config
n_input, n_nodes, n_epochs, n_batch, n_diff = config
# prepare data
if n_diff > 0:
train = difference(train, n_diff)
# transform series into supervised format
data = series_to_supervised(train, n_in=n_input)
# separate inputs and outputs
train_x, train_y = data[:, :-1], data[:, -1]
# define model
model = Sequential()
model.add(Dense(n_nodes, activation='relu', input_dim=n_input))
model.add(Dense(1))
model.compile(loss='mse', optimizer='adam')
# fit model
model.fit(train_x, train_y, epochs=n_epochs, batch_size=n_batch, verbose=0)
return model
# forecast with the fit model
def model_predict(model, history, config):
# unpack config
n_input, _, _, _, n_diff = config
# prepare data
correction = 0.0
if n_diff > 0:
correction = history[-n_diff]
history = difference(history, n_diff)
# shape input for model
x_input = array(history[-n_input:]).reshape((1, n_input))
# make forecast
yhat = model.predict(x_input, verbose=0)
# correct forecast if it was differenced
return correction + yhat[0]
# walk-forward validation for univariate data
def walk_forward_validation(data, n_test, cfg):
predictions = list()
# split dataset
train, test = train_test_split(data, n_test)
# fit model
model = model_fit(train, cfg)
# seed history with training dataset
history = [x for x in train]
# step over each time-step in the test set
for i in range(len(test)):
# fit model and make forecast for history
yhat = model_predict(model, history, cfg)
# store forecast in list of predictions
predictions.append(yhat)
# add actual observation to history for the next loop
history.append(test[i])
# estimate prediction error
error = measure_rmse(test, predictions)
print(' > %.3f' % error)
return error
# score a model, return None on failure
def repeat_evaluate(data, config, n_test, n_repeats=10):
# convert config to a key
key = str(config)
# fit and evaluate the model n times
scores = [walk_forward_validation(data, n_test, config) for _ in range(n_repeats)]
# summarize score
result = mean(scores)
print('> Model[%s] %.3f' % (key, result))
return (key, result)
# grid search configs
def grid_search(data, cfg_list, n_test):
# evaluate configs
scores = [repeat_evaluate(data, cfg, n_test) for cfg in cfg_list]
# sort configs by error, asc
scores.sort(key=lambda tup: tup[1])
return scores
# create a list of configs to try
def model_configs():
# define scope of configs
n_input = [12]
n_nodes = [50, 100]
n_epochs = [100]
n_batch = [1, 150]
n_diff = [0, 12]
# create configs
configs = list()
for i in n_input:
for j in n_nodes:
for k in n_epochs:
for l in n_batch:
for m in n_diff:
cfg = [i, j, k, l, m]
configs.append(cfg)
print('Total configs: %d' % len(configs))
return configs
# define dataset
series = read_csv('monthly-airline-passengers.csv', header=0, index_col=0)
data = series.values
# data split
n_test = 12
# model configs
cfg_list = model_configs()
# grid search
scores = grid_search(data, cfg_list, n_test)
print('done')
# list top 3 configs
for cfg, error in scores[:3]:
print(cfg, error)
Al ejecutar el ejemplo, podemos ver que hay un total de ocho configuraciones que el marco debe evaluar.
Nota: Sus resultados pueden variar dada la naturaleza estocástica del algoritmo o procedimiento de evaluación, o diferencias en la precisión numérica. Considere ejecutar el ejemplo varias veces y comparar el resultado promedio.
Cada configuración se evaluará 10 veces; eso significa que se crearán y evaluarán 10 modelos mediante validación preliminar para calcular una puntuación RMSE antes de informar y utilizar un promedio de esas 10 puntuaciones para calificar la configuración.
Luego se clasifican las puntuaciones y al final se informan las 3 configuraciones principales con el RMSE más bajo. Se encontró una configuración de modelo hábil en comparación con un modelo ingenuo que reportó un RMSE de 50,70.
Podemos ver que el mejor RMSE de 18.98 se logró con una configuración de [12, 100, 100, 1, 12], que sabemos que se puede interpretar como:
- n_entrada: 12
- n_nodos: 100
- n_épocas: 100
- n_lote: 1
- n_diff: 12
A continuación se muestra un resultado de ejemplo truncado de la búsqueda de cuadrícula.
Total configs: 8
> 20.707
> 29.111
> 17.499
> 18.918
> 28.817
...
> 21.015
> 20.208
> 18.503
> Model[[12, 100, 100, 150, 12]] 19.674
done
[12, 100, 100, 1, 12] 18.982720013625606
[12, 50, 100, 150, 12] 19.33004059448595
[12, 100, 100, 1, 0] 19.5389405532858
Red neuronal convolucional de búsqueda de cuadrícula
Ahora podemos adaptar el marco a los modelos CNN de búsqueda en cuadrícula.
Se puede buscar prácticamente el mismo conjunto de hiperparámetros que con el modelo MLP, excepto que la cantidad de nodos en la capa oculta se puede reemplazar por la cantidad de mapas de filtro y el tamaño del núcleo en las capas convolucionales.
El conjunto de hiperparámetros elegido para la búsqueda de cuadrícula en el modelo CNN es el siguiente:
- n_input: el número de entradas anteriores que se utilizarán como entrada para el modelo (por ejemplo, 12 meses).
- n_filters: el número de mapas de filtro en la capa convolucional (por ejemplo, 32).
- n_kernel: el tamaño del núcleo en la capa convolucional (por ejemplo, 3).
- n_epochs: el número de épocas de entrenamiento (por ejemplo, 1000).
- n_batch: el número de muestras que se incluirán en cada mini lote (por ejemplo, 32).
- n_diff: el orden de diferencia (por ejemplo, 0 o 12).
Algunos hiperparámetros adicionales que quizás desee investigar son el uso de dos capas convolucionales antes de una capa de agrupación, la repetición del patrón de las capas convolucional y de agrupación, el uso de abandono y más.
Definiremos un modelo CNN muy simple con una capa convolucional y una capa de agrupación máxima.
# define model
model = Sequential()
model.add(Conv1D(filters=n_filters, kernel_size=n_kernel, activation='relu', input_shape=(n_input, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(1))
model.compile(loss='mse', optimizer='adam')
Los datos deben prepararse de forma muy similar a como se hace para el MLP.
A diferencia del MLP que espera que los datos de entrada tengan la forma [muestras, características], el modelo CNN 1D espera que los datos tengan la forma [muestras, pasos de tiempo, características] donde las características se asignan a los canales y en en este caso 1 para la variable que medimos cada mes.
# reshape input data into [samples, timesteps, features]
n_features = 1
train_x = train_x.reshape((train_x.shape[0], train_x.shape[1], n_features))
La implementación completa de la función model_fit() se enumera a continuación.
# fit a model
def model_fit(train, config):
# unpack config
n_input, n_filters, n_kernel, n_epochs, n_batch, n_diff = config
# prepare data
if n_diff > 0:
train = difference(train, n_diff)
# transform series into supervised format
data = series_to_supervised(train, n_in=n_input)
# separate inputs and outputs
train_x, train_y = data[:, :-1], data[:, -1]
# reshape input data into [samples, timesteps, features]
n_features = 1
train_x = train_x.reshape((train_x.shape[0], train_x.shape[1], n_features))
# define model
model = Sequential()
model.add(Conv1D(filters=n_filters, kernel_size=n_kernel, activation='relu', input_shape=(n_input, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(1))
model.compile(loss='mse', optimizer='adam')
# fit
model.fit(train_x, train_y, epochs=n_epochs, batch_size=n_batch, verbose=0)
return model
Hacer una predicción con un modelo CNN ajustado es muy parecido a hacer una predicción con un MLP ajustado.
Nuevamente, la única diferencia es que la muestra de datos de entrada debe tener una forma tridimensional.
x_input = array(history[-n_input:]).reshape((1, n_input, 1))
La implementación completa de la función model_predict() se enumera a continuación.
# forecast with the fit model
def model_predict(model, history, config):
# unpack config
n_input, _, _, _, _, n_diff = config
# prepare data
correction = 0.0
if n_diff > 0:
correction = history[-n_diff]
history = difference(history, n_diff)
x_input = array(history[-n_input:]).reshape((1, n_input, 1))
# forecast
yhat = model.predict(x_input, verbose=0)
return correction + yhat[0]
Finalmente, podemos definir una lista de configuraciones para que el modelo evalúe. Como antes, podemos hacer esto definiendo listas de valores de hiperparámetros para intentar combinarlos en una lista. Probaremos una pequeña cantidad de configuraciones para garantizar que el ejemplo se ejecute en un período de tiempo razonable.
La función model_configs() completa se enumera a continuación.
# create a list of configs to try
def model_configs():
# define scope of configs
n_input = [12]
n_filters = [64]
n_kernels = [3, 5]
n_epochs = [100]
n_batch = [1, 150]
n_diff = [0, 12]
# create configs
configs = list()
for a in n_input:
for b in n_filters:
for c in n_kernels:
for d in n_epochs:
for e in n_batch:
for f in n_diff:
cfg = [a,b,c,d,e,f]
configs.append(cfg)
print('Total configs: %d' % len(configs))
return configs
Ahora tenemos todos los elementos necesarios para realizar una búsqueda en cuadrícula de los hiperparámetros de una red neuronal convolucional para el pronóstico de series temporales univariadas.
El ejemplo completo se enumera a continuación.
# grid search cnn for airline passengers
from math import sqrt
from numpy import array
from numpy import mean
from pandas import DataFrame
from pandas import concat
from pandas import read_csv
from sklearn.metrics import mean_squared_error
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers.convolutional import Conv1D
from keras.layers.convolutional import MaxPooling1D
# split a univariate dataset into train/test sets
def train_test_split(data, n_test):
return data[:-n_test], data[-n_test:]
# transform list into supervised learning format
def series_to_supervised(data, n_in=1, n_out=1):
df = DataFrame(data)
cols = list()
# input sequence (t-n, ... t-1)
for i in range(n_in, 0, -1):
cols.append(df.shift(i))
# forecast sequence (t, t+1, ... t+n)
for i in range(0, n_out):
cols.append(df.shift(-i))
# put it all together
agg = concat(cols, axis=1)
# drop rows with NaN values
agg.dropna(inplace=True)
return agg.values
# root mean squared error or rmse
def measure_rmse(actual, predicted):
return sqrt(mean_squared_error(actual, predicted))
# difference dataset
def difference(data, order):
return [data[i] - data[i - order] for i in range(order, len(data))]
# fit a model
def model_fit(train, config):
# unpack config
n_input, n_filters, n_kernel, n_epochs, n_batch, n_diff = config
# prepare data
if n_diff > 0:
train = difference(train, n_diff)
# transform series into supervised format
data = series_to_supervised(train, n_in=n_input)
# separate inputs and outputs
train_x, train_y = data[:, :-1], data[:, -1]
# reshape input data into [samples, timesteps, features]
n_features = 1
train_x = train_x.reshape((train_x.shape[0], train_x.shape[1], n_features))
# define model
model = Sequential()
model.add(Conv1D(filters=n_filters, kernel_size=n_kernel, activation='relu', input_shape=(n_input, n_features)))
model.add(MaxPooling1D(pool_size=2))
model.add(Flatten())
model.add(Dense(1))
model.compile(loss='mse', optimizer='adam')
# fit
model.fit(train_x, train_y, epochs=n_epochs, batch_size=n_batch, verbose=0)
return model
# forecast with the fit model
def model_predict(model, history, config):
# unpack config
n_input, _, _, _, _, n_diff = config
# prepare data
correction = 0.0
if n_diff > 0:
correction = history[-n_diff]
history = difference(history, n_diff)
x_input = array(history[-n_input:]).reshape((1, n_input, 1))
# forecast
yhat = model.predict(x_input, verbose=0)
return correction + yhat[0]
# walk-forward validation for univariate data
def walk_forward_validation(data, n_test, cfg):
predictions = list()
# split dataset
train, test = train_test_split(data, n_test)
# fit model
model = model_fit(train, cfg)
# seed history with training dataset
history = [x for x in train]
# step over each time-step in the test set
for i in range(len(test)):
# fit model and make forecast for history
yhat = model_predict(model, history, cfg)
# store forecast in list of predictions
predictions.append(yhat)
# add actual observation to history for the next loop
history.append(test[i])
# estimate prediction error
error = measure_rmse(test, predictions)
print(' > %.3f' % error)
return error
# score a model, return None on failure
def repeat_evaluate(data, config, n_test, n_repeats=10):
# convert config to a key
key = str(config)
# fit and evaluate the model n times
scores = [walk_forward_validation(data, n_test, config) for _ in range(n_repeats)]
# summarize score
result = mean(scores)
print('> Model[%s] %.3f' % (key, result))
return (key, result)
# grid search configs
def grid_search(data, cfg_list, n_test):
# evaluate configs
scores = [repeat_evaluate(data, cfg, n_test) for cfg in cfg_list]
# sort configs by error, asc
scores.sort(key=lambda tup: tup[1])
return scores
# create a list of configs to try
def model_configs():
# define scope of configs
n_input = [12]
n_filters = [64]
n_kernels = [3, 5]
n_epochs = [100]
n_batch = [1, 150]
n_diff = [0, 12]
# create configs
configs = list()
for a in n_input:
for b in n_filters:
for c in n_kernels:
for d in n_epochs:
for e in n_batch:
for f in n_diff:
cfg = [a,b,c,d,e,f]
configs.append(cfg)
print('Total configs: %d' % len(configs))
return configs
# define dataset
series = read_csv('monthly-airline-passengers.csv', header=0, index_col=0)
data = series.values
# data split
n_test = 12
# model configs
cfg_list = model_configs()
# grid search
scores = grid_search(data, cfg_list, n_test)
print('done')
# list top 10 configs
for cfg, error in scores[:3]:
print(cfg, error)
Al ejecutar el ejemplo, podemos ver que solo se evalúan ocho configuraciones distintas.
Nota: Sus resultados pueden variar dada la naturaleza estocástica del algoritmo o procedimiento de evaluación, o diferencias en la precisión numérica. Considere ejecutar el ejemplo varias veces y comparar el resultado promedio.
Podemos ver que una configuración de [12, 64, 5, 100, 1, 12] logró un RMSE de 18,89, lo cual es hábil en comparación con un modelo de pronóstico ingenuo que logró 50,70.
Podemos descomprimir esta configuración como:
- n_entrada: 12
- n_filtros: 64
- n_kernel: 5
- n_épocas: 100
- n_lote: 1
- n_diff: 12
A continuación se muestra un resultado de ejemplo truncado de la búsqueda de cuadrícula.
Total configs: 8
> 23.372
> 28.317
> 31.070
...
> 20.923
> 18.700
> 18.210
> Model[[12, 64, 5, 100, 150, 12]] 19.152
done
[12, 64, 5, 100, 1, 12] 18.89593462072732
[12, 64, 5, 100, 150, 12] 19.152486150334234
[12, 64, 3, 100, 150, 12] 19.44680151564605
Búsqueda de cuadrícula Red de memoria a largo plazo
Ahora podemos adoptar el marco para la búsqueda en cuadrícula de los hiperparámetros de un modelo LSTM.
Los hiperparámetros para el modelo LSTM serán los mismos cinco que los del MLP; ellos son:
- n_input: el número de entradas anteriores que se utilizarán como entrada para el modelo (por ejemplo, 12 meses).
- n_nodes: el número de nodos que se utilizarán en la capa oculta (por ejemplo, 50).
- n_epochs: el número de épocas de entrenamiento (por ejemplo, 1000).
- n_batch: el número de muestras que se incluirán en cada minilote (por ejemplo, 32).
- n_diff: el orden de diferencia (por ejemplo, 0 o 12).
Definiremos un modelo LSTM simple con una única capa LSTM oculta y la cantidad de nodos especificando la cantidad de unidades en esta capa.
# define model
model = Sequential()
model.add(LSTM(n_nodes, activation='relu', input_shape=(n_input, n_features)))
model.add(Dense(n_nodes, activation='relu'))
model.add(Dense(1))
model.compile(loss='mse', optimizer='adam')
# fit model
model.fit(train_x, train_y, epochs=n_epochs, batch_size=n_batch, verbose=0)
Puede ser interesante explorar el ajuste de configuraciones adicionales, como el uso de una capa de entrada bidireccional, capas LSTM apiladas e incluso modelos híbridos con modelos de entrada CNN o ConvLSTM.
Al igual que el modelo CNN, el modelo LSTM espera que los datos de entrada tengan una forma tridimensional para las muestras, los pasos de tiempo y las características.
# reshape input data into [samples, timesteps, features]
n_features = 1
train_x = train_x.reshape((train_x.shape[0], train_x.shape[1], n_features))
La implementación completa de la función model_fit() se enumera a continuación.
# fit a model
def model_fit(train, config):
# unpack config
n_input, n_nodes, n_epochs, n_batch, n_diff = config
# prepare data
if n_diff > 0:
train = difference(train, n_diff)
# transform series into supervised format
data = series_to_supervised(train, n_in=n_input)
# separate inputs and outputs
train_x, train_y = data[:, :-1], data[:, -1]
# reshape input data into [samples, timesteps, features]
n_features = 1
train_x = train_x.reshape((train_x.shape[0], train_x.shape[1], n_features))
# define model
model = Sequential()
model.add(LSTM(n_nodes, activation='relu', input_shape=(n_input, n_features)))
model.add(Dense(n_nodes, activation='relu'))
model.add(Dense(1))
model.compile(loss='mse', optimizer='adam')
# fit model
model.fit(train_x, train_y, epochs=n_epochs, batch_size=n_batch, verbose=0)
return model
Al igual que la CNN, la muestra de entrada única utilizada para hacer una predicción también debe remodelarse en la estructura tridimensional esperada.
# reshape sample into [samples, timesteps, features]
x_input = array(history[-n_input:]).reshape((1, n_input, 1))
La función model_predict() completa se enumera a continuación.
# forecast with the fit model
def model_predict(model, history, config):
# unpack config
n_input, _, _, _, n_diff = config
# prepare data
correction = 0.0
if n_diff > 0:
correction = history[-n_diff]
history = difference(history, n_diff)
# reshape sample into [samples, timesteps, features]
x_input = array(history[-n_input:]).reshape((1, n_input, 1))
# forecast
yhat = model.predict(x_input, verbose=0)
return correction + yhat[0]
Ahora podemos definir la función utilizada para crear la lista de configuraciones del modelo a evaluar.
El modelo LSTM es bastante más lento de entrenar que los modelos MLP y CNN; como tal, es posible que desee evaluar menos configuraciones por ejecución.
Definiremos un conjunto muy simple de dos configuraciones para explorar: estocástico y descenso de gradiente por lotes.
# create a list of configs to try
def model_configs():
# define scope of configs
n_input = [12]
n_nodes = [100]
n_epochs = [50]
n_batch = [1, 150]
n_diff = [12]
# create configs
configs = list()
for i in n_input:
for j in n_nodes:
for k in n_epochs:
for l in n_batch:
for m in n_diff:
cfg = [i, j, k, l, m]
configs.append(cfg)
print('Total configs: %d' % len(configs))
return configs
Ahora tenemos todo lo que necesitamos para crear una cuadrícula de hiperparámetros de búsqueda para el modelo LSTM para el pronóstico de series temporales univariadas.
El ejemplo completo se enumera a continuación.
# grid search lstm for airline passengers
from math import sqrt
from numpy import array
from numpy import mean
from pandas import DataFrame
from pandas import concat
from pandas import read_csv
from sklearn.metrics import mean_squared_error
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
# split a univariate dataset into train/test sets
def train_test_split(data, n_test):
return data[:-n_test], data[-n_test:]
# transform list into supervised learning format
def series_to_supervised(data, n_in=1, n_out=1):
df = DataFrame(data)
cols = list()
# input sequence (t-n, ... t-1)
for i in range(n_in, 0, -1):
cols.append(df.shift(i))
# forecast sequence (t, t+1, ... t+n)
for i in range(0, n_out):
cols.append(df.shift(-i))
# put it all together
agg = concat(cols, axis=1)
# drop rows with NaN values
agg.dropna(inplace=True)
return agg.values
# root mean squared error or rmse
def measure_rmse(actual, predicted):
return sqrt(mean_squared_error(actual, predicted))
# difference dataset
def difference(data, order):
return [data[i] - data[i - order] for i in range(order, len(data))]
# fit a model
def model_fit(train, config):
# unpack config
n_input, n_nodes, n_epochs, n_batch, n_diff = config
# prepare data
if n_diff > 0:
train = difference(train, n_diff)
# transform series into supervised format
data = series_to_supervised(train, n_in=n_input)
# separate inputs and outputs
train_x, train_y = data[:, :-1], data[:, -1]
# reshape input data into [samples, timesteps, features]
n_features = 1
train_x = train_x.reshape((train_x.shape[0], train_x.shape[1], n_features))
# define model
model = Sequential()
model.add(LSTM(n_nodes, activation='relu', input_shape=(n_input, n_features)))
model.add(Dense(n_nodes, activation='relu'))
model.add(Dense(1))
model.compile(loss='mse', optimizer='adam')
# fit model
model.fit(train_x, train_y, epochs=n_epochs, batch_size=n_batch, verbose=0)
return model
# forecast with the fit model
def model_predict(model, history, config):
# unpack config
n_input, _, _, _, n_diff = config
# prepare data
correction = 0.0
if n_diff > 0:
correction = history[-n_diff]
history = difference(history, n_diff)
# reshape sample into [samples, timesteps, features]
x_input = array(history[-n_input:]).reshape((1, n_input, 1))
# forecast
yhat = model.predict(x_input, verbose=0)
return correction + yhat[0]
# walk-forward validation for univariate data
def walk_forward_validation(data, n_test, cfg):
predictions = list()
# split dataset
train, test = train_test_split(data, n_test)
# fit model
model = model_fit(train, cfg)
# seed history with training dataset
history = [x for x in train]
# step over each time-step in the test set
for i in range(len(test)):
# fit model and make forecast for history
yhat = model_predict(model, history, cfg)
# store forecast in list of predictions
predictions.append(yhat)
# add actual observation to history for the next loop
history.append(test[i])
# estimate prediction error
error = measure_rmse(test, predictions)
print(' > %.3f' % error)
return error
# score a model, return None on failure
def repeat_evaluate(data, config, n_test, n_repeats=10):
# convert config to a key
key = str(config)
# fit and evaluate the model n times
scores = [walk_forward_validation(data, n_test, config) for _ in range(n_repeats)]
# summarize score
result = mean(scores)
print('> Model[%s] %.3f' % (key, result))
return (key, result)
# grid search configs
def grid_search(data, cfg_list, n_test):
# evaluate configs
scores = [repeat_evaluate(data, cfg, n_test) for cfg in cfg_list]
# sort configs by error, asc
scores.sort(key=lambda tup: tup[1])
return scores
# create a list of configs to try
def model_configs():
# define scope of configs
n_input = [12]
n_nodes = [100]
n_epochs = [50]
n_batch = [1, 150]
n_diff = [12]
# create configs
configs = list()
for i in n_input:
for j in n_nodes:
for k in n_epochs:
for l in n_batch:
for m in n_diff:
cfg = [i, j, k, l, m]
configs.append(cfg)
print('Total configs: %d' % len(configs))
return configs
# define dataset
series = read_csv('monthly-airline-passengers.csv', header=0, index_col=0)
data = series.values
# data split
n_test = 12
# model configs
cfg_list = model_configs()
# grid search
scores = grid_search(data, cfg_list, n_test)
print('done')
# list top 10 configs
for cfg, error in scores[:3]:
print(cfg, error)
Al ejecutar el ejemplo, podemos ver que solo se evalúan dos configuraciones distintas.
Nota: Sus resultados pueden variar dada la naturaleza estocástica del algoritmo o procedimiento de evaluación, o diferencias en la precisión numérica. Considere ejecutar el ejemplo varias veces y comparar el resultado promedio.
Podemos ver que una configuración de [12, 100, 50, 1, 12] logró un RMSE de 21,24, lo cual es hábil en comparación con un modelo de pronóstico ingenuo que logró 50,70.
El modelo requiere muchos más ajustes y puede funcionar mucho mejor con una configuración híbrida, como tener un modelo CNN como entrada.
Podemos descomprimir esta configuración como:
- n_entrada: 12
- n_nodos: 100
- n_épocas: 50
- n_lote: 1
- n_diff: 12
A continuación se muestra un resultado de ejemplo truncado de la búsqueda de cuadrícula.
Total configs: 2
> 20.488
> 17.718
> 21.213
...
> 22.300
> 20.311
> 21.322
> Model[[12, 100, 50, 150, 12]] 21.260
done
[12, 100, 50, 1, 12] 21.243775750634093
[12, 100, 50, 150, 12] 21.259553398553606
Extensiones
Esta sección enumera algunas ideas para ampliar el tutorial que quizás desee explorar.
- Más configuraciones. Explore un gran conjunto de configuraciones para uno de los modelos y vea si puede encontrar una configuración que genere un mejor rendimiento.
- Escalado de datos. Actualice el marco de búsqueda de cuadrícula para admitir también el escalado (normalización y/o estandarización) de los datos antes de ajustar el modelo e invertir la transformación para las predicciones.
- Arquitectura de red. Explore la cuadrícula en busca de cambios arquitectónicos más importantes para un modelo determinado, como la adición de más capas ocultas.
- Nuevo conjunto de datos. Explore la búsqueda en cuadrícula de un modelo determinado en un nuevo conjunto de datos de series temporales univariadas.
- Multivariante. Actualice el marco de búsqueda de cuadrícula para admitir pequeños conjuntos de datos de series temporales multivariantes, p. conjuntos de datos con múltiples variables de entrada.
Si explora alguna de estas extensiones, me encantaría saberlo.
Lectura adicional
Esta sección proporciona más recursos sobre el tema si desea profundizar más.
- Cómo hacer una cuadrícula de hiperparámetros de búsqueda para modelos de aprendizaje profundo en Python con Keras
- API de capas centrales de Keras
- API de capas convolucionales de Keras
- API de capas recurrentes de Keras
Resumen
En este tutorial, descubrió cómo desarrollar un marco para hiperparámetros de búsqueda en cuadrícula para modelos de aprendizaje profundo.
Específicamente, aprendiste:
- Cómo desarrollar un marco de búsqueda de cuadrícula genérico para ajustar los hiperparámetros del modelo.
- Cómo generar hiperparámetros de búsqueda en cuadrícula para un modelo de perceptrón multicapa en el problema de pronóstico de series temporales univariadas de pasajeros de aerolíneas.
- Cómo adaptar el marco a hiperparámetros de búsqueda de cuadrícula para redes neuronales convolucionales y de memoria a corto plazo.
¿Tiene alguna pregunta?
Haga sus preguntas en los comentarios a continuación y haré todo lo posible para responder.