Vertex AI : Réglage d'hyperparamètres distribué

1. Présentation

Dans cet atelier, vous allez apprendre à utiliser Vertex AI pour le réglage des hyperparamètres et l'entraînement distribué. Cet atelier utilise TensorFlow pour le code du modèle, mais les concepts s'appliquent également à d'autres frameworks de ML.

Objectifs

Vous allez apprendre à effectuer les opérations suivantes :

  • Entraîner un modèle à l'aide de l'entraînement distribué sur un conteneur personnalisé
  • Lancer plusieurs essais de votre code d'entraînement pour les réglages d'hyperparamètres automatisés

Le coût total d'exécution de cet atelier sur Google Cloud est d'environ 6 $.

2. Présentation de Vertex AI

Cet atelier utilise la toute dernière offre de produits d'IA de Google Cloud. Vertex AI simplifie l'expérience de développement en intégrant toutes les offres de ML de Google Cloud. Auparavant, les modèles entraînés avec AutoML et les modèles personnalisés étaient accessibles via des services distincts. La nouvelle offre regroupe ces deux types de modèles mais aussi d'autres nouveaux produits en une seule API. Vous pouvez également migrer des projets existants vers Vertex AI. Pour envoyer des commentaires, accédez à la page d'assistance.

Vertex AI comprend de nombreux produits différents qui permettent de gérer les workflows de ML de bout en bout. Cet atelier se concentre sur Training et Workbench.

Présentation des produits Vertex

3. Présentation du cas d'utilisation

Dans cet atelier, vous allez utiliser le réglage des hyperparamètres afin d'identifier les paramètres optimaux pour un modèle de classification d'images entraîné sur l'ensemble de données "chevaux et humains" à partir d'ensembles de données TensorFlow.

Réglages d'hyperparamètres

Le réglage des hyperparamètres avec Vertex AI Training consiste à exécuter plusieurs tests sur votre application d'entraînement, avec, pour les hyperparamètres que vous avez choisis, des valeurs comprises dans les limites que vous avez définies. Vertex AI effectue le suivi des résultats de chaque essai et procède aux ajustements nécessaires pour les essais ultérieurs.

Pour utiliser les réglages d'hyperparamètres avec Vertex AI Training, vous devez apporter deux modifications à votre code d'entraînement :

  1. Définissez un argument de ligne de commande dans votre module d'entraînement principal pour chaque hyperparamètre à régler.
  2. Utilisez la valeur transmise dans ces arguments afin de définir l'hyperparamètre correspondant dans le code de votre application d'entraînement.

Entraînement distribué

Si vous disposez d'un seul GPU, TensorFlow utilisera cet accélérateur pour entraîner le modèle plus rapidement, sans qu'aucune action supplémentaire de votre part ne soit nécessaire. Toutefois, si vous souhaitez bénéficier d'un avantage supplémentaire en utilisant plusieurs GPU, vous devez utiliser tf.distribute, le module de TensorFlow qui permet d'effectuer des calculs sur plusieurs appareils.

Cet atelier utilise tf.distribute.MirroredStrategy, que vous pouvez ajouter à vos applications d'entraînement avec un nombre réduit de modifications du code. Cette stratégie crée une copie du modèle sur chaque GPU de votre ordinateur. Les mises à jour de gradient suivantes s'effectueront de manière synchrone. Cela signifie que chaque GPU calcule la propagation avant et arrière du modèle sur une autre tranche des données d'entrée. Les gradients calculés à partir de chacune de ces tranches sont ensuite agrégés sur l'ensemble des GPU, puis une moyenne est calculée dans un processus appelé all-reduce. Les paramètres du modèle sont mis à jour à l'aide de la moyenne des gradients.

Vous n'avez pas besoin de connaître tous les détails pour suivre cet atelier, mais si vous souhaitez en savoir plus sur le fonctionnement de l'entraînement distribué dans TensorFlow, vous pouvez consulter la vidéo ci-dessous :

4. Configurer votre environnement

Pour suivre cet atelier de programmation, vous aurez besoin d'un projet Google Cloud Platform dans lequel la facturation est activée. Pour créer un projet, suivez ces instructions.

Étape 1 : Activez l'API Compute Engine

Accédez à Compute Engine et cliquez sur Activer si ce n'est pas déjà fait.

Étape 2 : Activez l'API Container Registry

Accédez à Container Registry et cliquez sur Activer si ce n'est pas déjà fait. Vous utiliserez ce service afin de créer un conteneur pour votre tâche d'entraînement personnalisé.

Étape 3 : Activez l'API Vertex AI

Accédez à la section Vertex AI de Cloud Console, puis cliquez sur Activer l'API Vertex AI.

Tableau de bord Vertex AI

Étape 4 : Créez une instance Vertex AI Workbench

Dans la section Vertex AI de Cloud Console, cliquez sur Workbench :

Menu Vertex AI

Activez l'API Notebooks si ce n'est pas déjà fait.

Notebook_api

Une fois l'API activée, cliquez sur NOTEBOOKS GÉRÉS :

Notebooks_UI

Sélectionnez ensuite NOUVEAU NOTEBOOK.

new_notebook

Attribuez un nom à votre notebook, puis cliquez sur Paramètres avancés.

create_notebook

Sous "Paramètres avancés", activez l'arrêt en cas d'inactivité et définissez le nombre de minutes sur 60. Cela entraîne l'arrêt automatique du notebook lorsqu'il n'est pas utilisé. Vous ne payez donc pas de frais inutiles.

idle_timeout

Dans la section "Sécurité", sélectionnez "Activer le terminal" si ce n'est pas déjà fait.

enable-terminal

Vous pouvez conserver tous les autres paramètres avancés tels quels.

Cliquez ensuite sur Créer. Le provisionnement de l'instance prend quelques minutes.

Une fois l'instance créée, sélectionnez Ouvrir JupyterLab.

open_jupyterlab

La première fois que vous utilisez une nouvelle instance, vous êtes invité à vous authentifier. Pour ce faire, suivez les étapes indiquées dans l'interface utilisateur.

authenticate

5. Rédiger le code d'entraînement

Pour commencer, à partir du menu de lancement, ouvrez une fenêtre de terminal dans votre instance de notebook :

launcher_terminal

Créez un répertoire appelé vertex-codelab et utilisez la commande cd pour y accéder.

mkdir vertex-codelab
cd vertex-codelab

Exécutez la commande suivante afin de créer un répertoire pour le code d'entraînement et un fichier Python dans lequel vous pourrez ajouter le code :

mkdir trainer
touch trainer/task.py

Votre répertoire vertex-codelab doit maintenant contenir les éléments suivants :

+ trainer/
    + task.py

Ensuite, ouvrez le fichier task.py que vous venez de créer et collez tout le code ci-dessous dans ce fichier.

import tensorflow as tf
import tensorflow_datasets as tfds
import argparse
import hypertune
import os

NUM_EPOCHS = 10
BATCH_SIZE = 64

def get_args():
  '''Parses args. Must include all hyperparameters you want to tune.'''

  parser = argparse.ArgumentParser()
  parser.add_argument(
      '--learning_rate',
      required=True,
      type=float,
      help='learning rate')
  parser.add_argument(
      '--momentum',
      required=True,
      type=float,
      help='SGD momentum value')
  parser.add_argument(
      '--num_units',
      required=True,
      type=int,
      help='number of units in last hidden layer')
  args = parser.parse_args()
  return args

def preprocess_data(image, label):
  '''Resizes and scales images.'''

  image = tf.image.resize(image, (150,150))
  return tf.cast(image, tf.float32) / 255., label

def create_dataset(batch_size):
  '''Loads Horses Or Humans dataset and preprocesses data.'''

  data, info = tfds.load(name='horses_or_humans', as_supervised=True, with_info=True)

  # Create train dataset
  train_data = data['train'].map(preprocess_data)
  train_data  = train_data.shuffle(1000)
  train_data  = train_data.batch(batch_size)

  # Create validation dataset
  validation_data = data['test'].map(preprocess_data)
  validation_data  = validation_data.batch(batch_size)

  return train_data, validation_data

def create_model(num_units, learning_rate, momentum):
  '''Defines and compiles model.'''

  inputs = tf.keras.Input(shape=(150, 150, 3))
  x = tf.keras.layers.Conv2D(16, (3, 3), activation='relu')(inputs)
  x = tf.keras.layers.MaxPooling2D((2, 2))(x)
  x = tf.keras.layers.Conv2D(32, (3, 3), activation='relu')(x)
  x = tf.keras.layers.MaxPooling2D((2, 2))(x)
  x = tf.keras.layers.Conv2D(64, (3, 3), activation='relu')(x)
  x = tf.keras.layers.MaxPooling2D((2, 2))(x)
  x = tf.keras.layers.Flatten()(x)
  x = tf.keras.layers.Dense(num_units, activation='relu')(x)
  outputs = tf.keras.layers.Dense(1, activation='sigmoid')(x)
  model = tf.keras.Model(inputs, outputs)
  model.compile(
      loss='binary_crossentropy',
      optimizer=tf.keras.optimizers.SGD(learning_rate=learning_rate, momentum=momentum),
      metrics=['accuracy'])
  return model

def main():
  args = get_args()

  # Create distribution strategy
  strategy = tf.distribute.MirroredStrategy()

  # Get data
  GLOBAL_BATCH_SIZE = BATCH_SIZE * strategy.num_replicas_in_sync
  train_data, validation_data = create_dataset(GLOBAL_BATCH_SIZE)

  # Wrap variable creation within strategy scope
  with strategy.scope():
    model = create_model(args.num_units, args.learning_rate, args.momentum)

  # Train model
  history = model.fit(train_data, epochs=NUM_EPOCHS, validation_data=validation_data)

  # Define metric
  hp_metric = history.history['val_accuracy'][-1]

  hpt = hypertune.HyperTune()
  hpt.report_hyperparameter_tuning_metric(
      hyperparameter_metric_tag='accuracy',
      metric_value=hp_metric,
      global_step=NUM_EPOCHS)

if __name__ == "__main__":
    main()

Examinons de plus près le code et les composants spécifiques à l'entraînement distribué et au réglage des hyperparamètres.

Entraînement distribué

  1. L'objet MirroredStrategy est créé dans la fonction main(). Vous devez ensuite encapsuler la création des variables du modèle dans le champ d'application de la stratégie. Cette étape indique à TensorFlow les variables à mettre en miroir sur les GPU.
  2. La taille du lot est mise à l'échelle par la valeur num_replicas_in_sync. La mise à l'échelle de la taille de lot est une bonne pratique lorsque vous utilisez des stratégies de chargement de données synchrones en parallèle dans TensorFlow. Pour en savoir plus, accédez à cette page.

Réglages d'hyperparamètres

  1. Le script importe la bibliothèque hypertune. Plus tard, lors de la création de l'image de conteneur, nous devrons installer cette bibliothèque.
  2. La fonction get_args() définit un argument de ligne de commande pour chaque hyperparamètre que vous souhaitez régler. Dans cet exemple, les hyperparamètres qui seront réglés sont le taux d'apprentissage, la valeur du momentum dans l'optimiseur et le nombre d'unités dans la dernière couche cachée du modèle, mais n'hésitez pas à effectuer des tests avec d'autres hyperparamètres. La valeur transmise dans ces arguments doit ensuite être utilisée pour définir l'hyperparamètre correspondant dans le code (par exemple, learning_rate = args.learning_rate).
  3. À la fin de la fonction main(), la bibliothèque hypertune permet de définir la métrique que vous souhaitez optimiser. Dans TensorFlow, la méthode Keras model.fit renvoie un objet History. L'attribut History.history est un enregistrement des valeurs de perte d'entraînement et des métriques à des époques successives. Si vous transmettez des données de validation à model.fit, l'attribut History.history inclut également les valeurs de perte de validation et celles des métriques. Par exemple, si vous aviez entraîné un modèle pendant trois époques avec des données de validation et que vous aviez fourni accuracy comme métrique, l'attribut History.history ressemblerait au dictionnaire suivant.
{
 "accuracy": [
   0.7795261740684509,
   0.9471358060836792,
   0.9870933294296265
 ],
 "loss": [
   0.6340447664260864,
   0.16712145507335663,
   0.04546636343002319
 ],
 "val_accuracy": [
   0.3795261740684509,
   0.4471358060836792,
   0.4870933294296265
 ],
 "val_loss": [
   2.044623374938965,
   4.100203514099121,
   3.0728273391723633
 ]

Si vous souhaitez que le service de réglage des hyperparamètres découvre les valeurs qui maximisent la justesse de la validation du modèle, vous devez définir la métrique en tant que dernière entrée (ou NUM_EPOCS - 1) de la liste val_accuracy. Transmettez ensuite cette métrique à une instance de HyperTune. Vous pouvez choisir la chaîne que vous voulez pour hyperparameter_metric_tag, mais vous devrez la réutiliser ultérieurement lorsque vous lancerez la tâche de réglage des hyperparamètres.

6. Conteneuriser le code

La première étape pour conteneuriser votre code consiste à créer un fichier Dockerfile. Vous allez placer dans ce Dockerfile toutes les commandes nécessaires à l'exécution de l'image. Il installera toutes les bibliothèques nécessaires et configurera le point d'entrée du code d'entraînement.

Étape 1 : Écrivez un fichier Dockerfile

Depuis votre terminal, assurez-vous que vous vous trouvez dans le répertoire vertex-codelab et créez un fichier Dockerfile vide :

touch Dockerfile

Votre répertoire vertex-codelab doit maintenant contenir les éléments suivants :

+ Dockerfile
+ trainer/
    + task.py

Ouvrez le Dockerfile et copiez-y le code suivant :

FROM gcr.io/deeplearning-platform-release/tf2-gpu.2-7

WORKDIR /

# Installs hypertune library
RUN pip install cloudml-hypertune

# Copies the trainer code to the docker image.
COPY trainer /trainer

# Sets up the entry point to invoke the trainer.
ENTRYPOINT ["python", "-m", "trainer.task"]

Ce fichier Dockerfile utilise l'image Docker avec GPU de TensorFlow Enterprise 2.7 comme conteneur de deep learning. Les conteneurs de deep learning sur Google Cloud sont fournis avec de nombreux frameworks de ML et de science des données courants préinstallés. Une fois cette image téléchargée, ce Dockerfile configure le point d'entrée du code d'entraînement.

Étape 2 : Créez le conteneur

Depuis votre terminal, exécutez la commande suivante afin de définir une variable d'environnement pour votre projet, en veillant à remplacer your-cloud-project par l'ID de votre projet :

PROJECT_ID='your-cloud-project'

Définissez une variable avec l'URI de votre image de conteneur dans Google Container Registry :

IMAGE_URI="gcr.io/$PROJECT_ID/horse-human-codelab:latest"

Configurer Docker

gcloud auth configure-docker

Ensuite, créez le conteneur en exécutant la commande suivante à partir de la racine de votre répertoire vertex-codelab :

docker build ./ -t $IMAGE_URI

Enfin, transférez-le vers Google Container Registry :

docker push $IMAGE_URI

Étape 3 : Créez un bucket Cloud Storage

Dans notre tâche d'entraînement, nous allons transmettre le chemin d'accès à un bucket de préproduction.

Exécutez la commande suivante dans votre terminal pour créer un bucket dans votre projet.

BUCKET_NAME="gs://${PROJECT_ID}-hptune-bucket"
gsutil mb -l us-central1 $BUCKET_NAME

7. Lancer la tâche de réglage des hyperparamètres

Étape 1 : Créez une tâche d'entraînement personnalisée avec réglage des hyperparamètres

Depuis le menu de lancement, ouvrez un nouveau notebook TensorFlow 2.

new_notebook

Importez le SDK Python Vertex AI.

from google.cloud import aiplatform
from google.cloud.aiplatform import hyperparameter_tuning as hpt

Pour lancer la tâche de réglage des hyperparamètres, vous devez d'abord définir la valeur de worker_pool_specs, qui spécifie le type de machine et l'image Docker. La spécification suivante définit une machine comportant deux GPU NVIDIA Tesla V100.

Vous devrez remplacer {PROJECT_ID} par l'ID de votre projet dans image_uri.

# The spec of the worker pools including machine type and Docker image
# Be sure to replace PROJECT_ID in the "image_uri" with your project.

worker_pool_specs = [{
    "machine_spec": {
        "machine_type": "n1-standard-4",
        "accelerator_type": "NVIDIA_TESLA_V100",
        "accelerator_count": 2
    },
    "replica_count": 1,
    "container_spec": {
        "image_uri": "gcr.io/{PROJECT_ID}/horse-human-codelab:latest"
    }
}]

Ensuite, définissez le parameter_spec, qui est un dictionnaire spécifiant les paramètres à optimiser. La clé du dictionnaire correspond à la chaîne que vous avez attribuée à l'argument de ligne de commande pour chaque hyperparamètre, et la valeur du dictionnaire correspond à la spécification du paramètre.

Pour chaque hyperparamètre, vous devez définir le type ainsi que les limites pour les valeurs que le service de réglage testera. Les hyperparamètres peuvent être de type Double, Entier, Catégoriel ou Discret. Si vous sélectionnez le type "Double" ou "Entier", vous devez indiquer des valeurs minimale et maximale. Si vous sélectionnez un type "Catégoriel" ou "Discret", vous devez indiquer les valeurs. Pour les types "Double" et "Entier", vous devez également fournir la valeur de scaling. Pour savoir comment choisir la meilleure valeur de scaling, consultez cette vidéo.

# Dictionary representing parameters to optimize.
# The dictionary key is the parameter_id, which is passed into your training
# job as a command line argument,
# And the dictionary value is the parameter specification of the metric.
parameter_spec = {
    "learning_rate": hpt.DoubleParameterSpec(min=0.001, max=1, scale="log"),
    "momentum": hpt.DoubleParameterSpec(min=0, max=1, scale="linear"),
    "num_units": hpt.DiscreteParameterSpec(values=[64, 128, 512], scale=None)
}

La dernière spécification à définir est metric_spec, le dictionnaire qui représente la métrique à optimiser. La clé du dictionnaire est l'option hyperparameter_metric_tag, que vous avez définie dans le code de votre application d'entraînement, et sa valeur correspond à l'objectif d'optimisation.

# Dicionary representing metrics to optimize.
# The dictionary key is the metric_id, which is reported by your training job,
# And the dictionary value is the optimization goal of the metric.
metric_spec={'accuracy':'maximize'}

Une fois toutes les spécifications définies, vous allez créer une CustomJob, c'est-à-dire la spécification commune qui sera utilisée pour exécuter votre tâche lors de chacun des tests de réglage des hyperparamètres.

Vous devez remplacer {YOUR_BUCKET} par le bucket que vous avez créé précédemment.

# Replace YOUR_BUCKET
my_custom_job = aiplatform.CustomJob(display_name='horses-humans',
                              worker_pool_specs=worker_pool_specs,
                              staging_bucket='gs://{YOUR_BUCKET}')

Ensuite, créez et exécutez HyperparameterTuningJob.

hp_job = aiplatform.HyperparameterTuningJob(
    display_name='horses-humans',
    custom_job=my_custom_job,
    metric_spec=metric_spec,
    parameter_spec=parameter_spec,
    max_trial_count=6,
    parallel_trial_count=2,
    search_algorithm=None)

hp_job.run()

Voici quelques arguments à prendre en compte :

  • max_trial_count : vous devez définir le nombre maximal d'essais que le service va réaliser. Un plus grand nombre d'essais permet généralement d'obtenir de meilleurs résultats, mais il existe un point de retours décroissants au-delà duquel les essais supplémentaires n'ont que peu ou pas d'effet sur la métrique que vous essayez d'optimiser. Il est recommandé de commencer avec un petit nombre d'essais et d'attendre d'avoir une idée de l'impact des hyperparamètres choisis avant d'augmenter le nombre d'essais.
  • parallel_trial_count : si vous utilisez des essais en parallèle, le service provisionne plusieurs clusters de traitement pour l'entraînement. L'augmentation du nombre d'essais parallèles a pour effet de réduire le temps nécessaire à l'exécution de la tâche de réglage des hyperparamètres. Toutefois, elle peut réduire l'efficacité globale de la tâche. En effet, la stratégie de réglage par défaut utilise les résultats des essais précédents pour ajuster les valeurs affectées lors des essais suivants.
  • search_algorithm : vous pouvez définir l'algorithme de recherche sur "Grille", "Aléatoire" ou utiliser la valeur par défaut (Aucun). L'option par défaut applique l'optimisation bayésienne pour rechercher l'espace des valeurs possibles pour les hyperparamètres. Il s'agit de l'algorithme recommandé. Cliquez ici pour en savoir plus sur cet algorithme.

Une fois la tâche lancée, vous pouvez suivre son état d'avancement dans l'onglet TÂCHES DE RÉGLAGE D'HYPERPARAMÈTRES de l'interface utilisateur.

HP_job

Une fois la tâche terminée, vous pouvez afficher et trier les résultats de vos essais pour découvrir la meilleure combinaison de valeurs d'hyperparamètres.

HP_results

🎉 Félicitations ! 🎉

Vous avez appris à utiliser Vertex AI pour :

  • Exécuter une tâche de réglage des hyperparamètres avec l'entraînement distribué

Pour en savoir plus sur les différentes parties de Vertex AI, consultez la documentation.

8. Nettoyage

Comme le notebook est configuré pour expirer au bout de 60 minutes d'inactivité, il n'est pas nécessaire d'arrêter l'instance. Si vous souhaitez arrêter l'instance manuellement, cliquez sur le bouton "Arrêter" dans la section "Vertex AI Workbench" de la console. Si vous souhaitez supprimer le notebook définitivement, cliquez sur le bouton "Supprimer".

delete

Pour supprimer le bucket de stockage, utilisez le menu de navigation de Cloud Console pour accéder à Stockage > Cloud Storage, sélectionnez votre bucket puis cliquez sur "Supprimer" :

Supprimer l'espace de stockage