Optimizando los hiperparámetros de una red neuronal con TensorBoard

Enrique Blanco    24 marzo, 2021

Al crear modelos de Machine Learning se deben investigar múltiples parámetros como el número de nodos de las capas, diferentes optimizadores, valores de tasas de aprendizaje, distintas tasas de dropout, etc. Por lo tanto, un paso importante en el flujo de trabajo del Machine Learning es identificar los mejores hiperparámetros para el problema que se está abordando, lo que a menudo implica un gran número de pruebas. Este proceso se conoce como Optimización de hiperparámetros.

Para poder realizarlo, TensorFlow 2.0 proporciona el panel de HParams en TensorBoard, que nos permitirá visualizar el comportamiento de nuestro modelo en función de los valores que definen nuestro modelo en nuestro dashboard.

Hace unos meses escribimos un breve post sobre TensorBoard, un kit de herramientas de TensorFlow que permite monitorizar métricas como la precisión y la función de pérdida durante el entrenamiento de modelos profundos.

En este anterior artículo investigamos, dentro de las funcionalidades más básicas de TensorBoard, cómo se podía monitorizar las métricas de un modelo de Machine Learning, la evolución de los pesos y sesgos de nuestra arquitectura, visualizar el grafo de nuestro modelo e incluso  realizar un sencillo análisis de matriz de confusión de nuestro modelo una vez estuviera entrenado.

En el presente post vamos a probar a encontrar los hiperparámetros de una red neuronal recurrente dedicada a multivariate time series forecasting. Para más detalles sobre cómo abordar un problema de este tipo, puede leer el siguiente enlace.

Figura 1. Grafo del modelo recurrente usado para la predicción de time series en el dashboard de TensorBoard.

Definiendo los valores de los hiperparámetros a probar

Con TensorBoard, se puede realizar un seguimiento de la precisión y la pérdida del modelo en cada época; y también con diferentes valores de hiperparámetros. La precisión de seguimiento para diferentes los diferentes valores nos ayudará a ajustar el modelo más rápido.

A continuación, se muestra un ejemplo de cómo listar los intervalos de valores para cada hiperparámetro.

# ## Create hyperparameters
HP_INPUT_WIDTH = hp.HParam('num_input_width', hp.Discrete([12, 24, 48, 96]))
HP_OUTPUT_WIDTH = hp.HParam('num_output_width', hp.Discrete([6, 12, 24, 48]))
HP_RNN_UNITS = hp.HParam('num_rnn_units', hp.Discrete([30, 40, 60, 80, 100]))
HP_DROPOUT = hp.HParam('dropout', hp.Discrete([0.05, 0.1, 0.2]))
HP_OPTIMIZER = hp.HParam('optimizer', hp.Discrete(['adam']))
HP_LEARNING_RATE = hp.HParam('learning_rate', hp.Discrete([0.001]))
METRIC_ACCURACY = 'accuracy'

Creando, compilando y ajustando el modelo

Suponiendo que ya tenemos realizada la partición de nuestro dataset de time series en train, test y/o validación y que ese dataset está convenientemente preprocesado, definimos la arquitectura del modelo (haciendo uso de Keras) junto con algunos callbacks como EarlyStopping o el propio callback de TensorBoard que nos permitirá loguear el desempeño de nuestra red para cada combinación posible de hiperparámetros.

def create_model(hparams):

  # Define model architecture
  input_shape = (hparams[HP_INPUT_WIDTH], num_features)
  inp = tf.keras.layers.Input(input_shape)
  x = tf.keras.layers.LSTM(hparams[HP_RNN_UNITS], return_sequences=False)(inp)
  x = tf.keras.layers.Flatten()(x)
  x = get_dropout(x, p=hparams[HP_DROPOUT], mc=True)
  # Shape => [batch, 1,  out_steps*features]
  x = tf.keras.layers.Dense(hparams[HP_OUTPUT_WIDTH]*num_features,
            kernel_initializer=tf.keras.initializers.VarianceScaling())(x)
  out = tf.keras.layers.Reshape([hparams[HP_OUTPUT_WIDTH], num_features])(x)

  
  # Build model
  model = tf.keras.models.Model(inputs=inp, outputs=out)

  # Setting the optimizer and learning rate
  optimizer = hparams[HP_OPTIMIZER]
  learning_rate = hparams[HP_LEARNING_RATE]
  if optimizer == 'adam':
    optimizer = tf.optimizers.Adam(learning_rate=learning_rate)
  else:
    raise ValueError("Unexpected optimizer name: %r" % (optimizer_name,))

  # Define Early Stopping
  early_stopping = tf.keras.callbacks.EarlyStopping(monitor='val_loss',
                                                    patience=3,
                                                    mode='min')
  # Define path where TensorBoard logs will be stored
  log_dir = os.path.join(tensorboard_path, 
                        'lstm_verbose',  
                        datetime.datetime.now().strftime("%Y%m%d-%H%M%S"))
  
  with tf.summary.create_file_writer(log_dir).as_default():
      hp.hparams_config(
      hparams=
      [
       HP_INPUT_WIDTH, HP_OUTPUT_WIDTH,  
       HP_RNN_UNITS, HP_DROPOUT, 
       HP_OPTIMIZER, HP_LEARNING_RATE
      ],
       metrics=[hp.Metric(METRIC_ACCURACY, display_name='accuracy')],
      )
  
  # We compile the model
  model.compile(loss=tf.losses.MeanSquaredError(),
                optimizer=optimizer,
                metrics=[
                         tf.metrics.MeanAbsoluteError(), 
                         tf.keras.metrics.MeanSquaredError(),
                         'accuracy'
                         ])
  # Define a Window Generator to feed the neural network
  multi_window = WindowGenerator(input_width=hparams[HP_INPUT_WIDTH],
                                 label_width=hparams[HP_OUTPUT_WIDTH],
                                 shift=hparams[HP_OUTPUT_WIDTH])

  # Fitting the model
  history = model.fit(multi_window.train, epochs=max_epochs,
                      validation_data=multi_window.val,
                      callbacks=[early_stopping,\
                                tf.keras.callbacks.TensorBoard(log_dir),
                                hp.KerasCallback(log_dir, hparams)])
  
  return history.history['val_accuracy'][-1]

Para cada ejecución del modelo, necesitamos registrar el resumen de hparams con el hiperparámetro y la precisión de las épocas finales. Necesitamos convertir la precisión de validación de la última época en un valor escalar.

def run(run_dir, hparams):
  with tf.summary.create_file_writer(run_dir).as_default():
    hp.hparams(hparams)  # record the values used in this trial
    accuracy = create_model(hparams)
    # converting to tf scalar
    accuracy = tf.reshape(tf.convert_to_tensor(accuracy), []).numpy()
    tf.summary.scalar(METRIC_ACCURACY, accuracy, step=1)

Ejecutando el modelo con diferentes valores de hiperparámetros

En este paso se usa Grid Search para probar todas las combinaciones posibles.

session_num = 0
for num_input_units in HP_INPUT_WIDTH.domain.values:
  for num_output_units in HP_OUTPUT_WIDTH.domain.values:
    for num_rnn_units in HP_RNN_UNITS.domain.values:
      for dropout_rate in HP_DROPOUT.domain.values:
        for optimizer in HP_OPTIMIZER.domain.values:
            for learning_rate in HP_LEARNING_RATE.domain.values:
              hparams = {
                  HP_INPUT_WIDTH: num_input_units,
                  HP_OUTPUT_WIDTH: num_output_units,
                  HP_RNN_UNITS: num_rnn_units, 
                  HP_DROPOUT: dropout_rate,
                  HP_OPTIMIZER: optimizer,
                  HP_LEARNING_RATE: learning_rate,
              }
              run_name = "run-%d" % session_num
              print('--- Starting trial: %s' % run_name)
              print({h.name: hparams[h] for h in hparams})
              run(os.path.join(tensorboard_path, 
                                'lstm_mc_hgs_verbose',  
                                datetime.datetime.now().strftime("%Y%m%d-%H%M%S")),
                    hparams)
              session_num += 1

Visualizando los resultados de la búsqueda

Ejecutamos en un terminal (tras haber activado nuestro entorno virtual de Python) y ejecutamos el siguiente comando:

tensorboard --logdir "/logs/lstm_verbose/" --port 6006

Si nos vamos a http://localhost:6006/ podremos acceder al dashboard de TensorBoard.

Si se ha logueado todo correctamente, en el dashboard se tendrá disponible una pestaña llamada HParams, que muestra las ejecuciones individuales para cada combinación de hiperparámetros ordenadas de acuerdo a los valores de METRIC_ACCURACY especificada arriba.

Figura 2. Resumen de experimentos realizados con estrategia de Grid Search en HParams con TensorBoard.
Figura 2. Resumen de experimentos realizados con estrategia de Grid Search en HParams con TensorBoard.
Figura 3. Parallel Coordinates View de HParams en el dashboard de TensorBoard.
Figura 3. Parallel Coordinates View de HParams en el dashboard de TensorBoard.
Figura 4. Scatter Plot Matrix View de HParams en el dashboard de TensorBoard.
Figura 4. Scatter Plot Matrix View de HParams en el dashboard de TensorBoard.

Como se ve en la figura anterior, el panel izquierdo del panel proporciona capacidades de filtrado que están activas en todas las vistas en el panel de HParams:

  • Filtrar qué hiperparámetros/métricas se muestran en el panel;
  • Filtrar qué valores de hiperparámetros/métricas se muestran en el panel;
  • Filtrar por estado de ejecución (en ejecución o terminadas);
  • Ordenar por hiperparámetro/métrica en la vista de tabla;
  • Número de grupos de sesiones para mostrar (útil para el rendimiento cuando hay muchos experimentos).

El panel de HParams tiene tres vistas diferentes, con diversa información útil:

  • La vista de tabla (Figura 2) enumera las ejecuciones, sus hiperparámetros y sus métricas.
  • La pestaña de coordenadas paralelas (Figura 3) muestra cada tramo como una línea que pasa por un eje para cada hiperparámetro y métrica.
  • La vista de gráfico de dispersión (Figura 4) muestra gráficos que comparan cada hiperparámetro/métrica con cada métrica. Esto puede ayudar a identificar correlaciones.

Conclusión

Os invitamos a intentar realizar un experimento similar al expuesto en este post con la red convolucional que construimos en Supervisa tu entrenamiento de redes neuronales con TensorBoard para Fashion MNIST, probando a modificar el número de filtros, el tamaño de kernel, el número de unidades en las capas densas finales y/o distintos optimizadores distintos de Adam. Para más detalle sobre Hyperparameter Tuning, puede consultar el tutorial de TensorFlow.

Para mantenerte al día con LUCA visita nuestra página web,  suscríbete a LUCA Data Speaks o síguenos en TwitterLinkedIn YouTube.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *