Vertex AI: cohéberger des modèles sur la même VM pour les prédictions

1. Présentation

Dans cet atelier, vous allez utiliser la fonctionnalité de modèle de co-hébergement de Vertex AI pour héberger plusieurs modèles sur la même VM afin d'obtenir des prédictions en ligne.

Objectifs

Vous allez apprendre à effectuer les opérations suivantes :

  • Créer un élément DeploymentResourcePool
  • Déployer des modèles dans un DeploymentResourcePool

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

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 depuis 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 un commentaire, consultez 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 les produits mis en évidence ci-dessous : Predictions et Workbench.

Présentation des produits Vertex

3. Présentation du cas d'utilisation

Lorsque vous déployez des modèles sur le service de prédiction Vertex AI, chaque modèle est déployé par défaut sur sa propre VM. Pour rendre l'hébergement plus rentable, vous pouvez héberger plusieurs modèles sur la même VM, ce qui se traduit par une meilleure utilisation de la mémoire et des ressources de calcul. Le nombre de modèles que vous choisissez de déployer sur la même VM dépend de leur taille et des modèles de trafic, mais cette fonctionnalité est particulièrement utile dans les cas où vous disposez de nombreux modèles déployés avec un trafic creux.

La prise en charge des modèles de co-hébergement introduit le concept de pool de ressources de déploiement, qui regroupe des modèles pour partager des ressources au sein d'une VM. Les modèles peuvent partager une VM s'ils partagent un point de terminaison, et également s'ils sont déployés sur différents points de terminaison. Actuellement, les modèles d'un même pool de ressources doivent avoir la même image de conteneur, y compris la version du framework des conteneurs prédéfinis de Vertex Prediction. De plus, seuls les conteneurs prédéfinis Vertex Prediction avec le framework de modèle Tensorflow sont compatibles avec cette version. Les autres frameworks de modèles et conteneurs personnalisés ne sont pas encore compatibles.

deployment_pool

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 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 3: 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 sous Autorisation, sélectionnez Compte de service.

create_notebook

Sélectionnez Paramètres avancés.

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

5. Entraîner le modèle

Avant d'essayer la fonctionnalité de co-hébergement, nous devons d'abord entraîner un modèle et stocker les artefacts de modèle enregistrés dans un bucket Cloud Storage. Nous allons utiliser l'exécuteur de notebooks Workbench pour lancer le job d'entraînement.

Étape 1 : Créez un bucket Cloud Storage

Si vous souhaitez utiliser un bucket existant dans votre projet, vous pouvez ignorer cette étape. Sinon, ouvrez une nouvelle session de terminal à partir du lanceur d'applications.

launcher_terminal

Depuis le 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'

Ensuite, exécutez la commande suivante pour créer un bucket dans votre projet.

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

Étape 2: Lancez l'exécution du notebook

Dans le lanceur de votre instance Workbench, ouvrez un nouveau notebook TensorFlow 2.

launcher_tf2

Le code ci-dessous entraîne un classificateur de sentiments binaire (positif ou négatif) sur l'ensemble de données de revue de films IMDB. Collez le code dans votre notebook.

Veillez à remplacer {YOUR_BUCKET} par le bucket que vous avez créé à l'étape précédente (ou un autre bucket de votre projet). C'est dans cet emplacement que nous stockerons les artefacts de modèle enregistrés, dont nous aurons besoin ultérieurement pour importer le modèle dans Vertex AI Model Registry.

import numpy as np

import tensorflow_datasets as tfds
import tensorflow as tf

# REPLACE WITH YOUR BUCKET!
OUTPUT_PATH='gs://{YOUR_BUCKET}/model_output'

BUFFER_SIZE = 10000
BATCH_SIZE = 64
VOCAB_SIZE = 1000

# Load data
dataset, info = tfds.load('imdb_reviews', with_info=True,
                          as_supervised=True)
train_dataset, test_dataset = dataset['train'], dataset['test']

train_dataset = train_dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE).prefetch(tf.data.AUTOTUNE)
test_dataset = test_dataset.batch(BATCH_SIZE).prefetch(tf.data.AUTOTUNE)

# Create text encoder
encoder = tf.keras.layers.TextVectorization(
    max_tokens=VOCAB_SIZE)
encoder.adapt(train_dataset.map(lambda text, label: text))

# Create model
model = tf.keras.Sequential([
    encoder,
    tf.keras.layers.Embedding(
        input_dim=len(encoder.get_vocabulary()),
        output_dim=64,
        # Use masking to handle the variable sequence lengths
        mask_zero=True),
    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(64)),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dense(1)
])

# Compile model
model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              optimizer=tf.keras.optimizers.Adam(1e-4),
              metrics=['accuracy'])

# Fit model
history = model.fit(train_dataset, epochs=10,
                    validation_data=test_dataset,
                    validation_steps=30)

# Save model
model.save(OUTPUT_PATH)

Ensuite, sélectionnez le bouton Execute (Exécuter).

execute_nb

Configurez ensuite votre exécution comme suit, puis cliquez sur ENVOYER.

execution_config

L'onglet "Exécutions" de la console vous permet de suivre l'état de votre job d'entraînement.

execution_status

6. Déployer un modèle

Étape 1: Importez le modèle

Une fois l'exécution terminée, revenez au notebook Workbench pour importer le modèle. Créer un notebook TensorFlow

tf_nb

Tout d'abord, importez le SDK Vertex AI pour Python

from google.cloud import aiplatform

Importez ensuite le modèle en remplaçant {YOUR_BUCKET} par le bucket que vous avez spécifié dans le code d'entraînement.

# replace {YOUR_BUCKET}
model_1 = aiplatform.Model.upload(display_name='text-model-1',
                                  artifact_uri='gs://{YOUR_BUCKET}/model_output',
                                  serving_container_image_uri='us-docker.pkg.dev/vertex-ai/prediction/tf2-cpu.2-8:latest')

À des fins de démonstration, nous importerons ce modèle deux fois, ce qui créera deux ressources de modèle différentes dans Vertex AI. Cela nous permet de tester le déploiement de plusieurs modèles sur un seul point de terminaison au sein d'un pool de ressources de déploiement. Dans un scénario réel, vous disposez de deux modèles différents au lieu de créer des modèles à partir des mêmes artefacts enregistrés. Toutefois, comme il s'agit d'un raccourci, vous n'avez pas besoin de lancer une autre exécution d'entraînement. De plus, vous pouvez choisir de déployer les deux modèles sur différents points de terminaison au sein du même pool de ressources de déploiement.

# replace {YOUR_BUCKET}
model_2 = aiplatform.Model.upload(display_name='text-model-2',
                                  artifact_uri='gs://{YOUR_BUCKET}/model_output',
                                  serving_container_image_uri='us-docker.pkg.dev/vertex-ai/prediction/tf2-cpu.2-8:latest')

Vous devriez maintenant voir les deux modèles dans le registre de modèles Vertex AI. L'état du déploiement est vide, car nous n'avons pas encore déployé les modèles.

model_registry

Étape 2: Créez un point de terminaison

Créer un point de terminaison. Notez que cela diffère du déploiement d'un modèle sur un point de terminaison.

endpoint = aiplatform.Endpoint.create('cohost-endpoint')

Une fois le point de terminaison créé, il s'affiche dans la console.

console_endpoint

Étape 3: Créez le pool de ressources de déploiement

Vous pouvez créer le pool de ressources de déploiement à l'aide de la commande suivante. Veillez à remplacer {YOUR_PROJECT} par l'ID de votre projet.

# replace {YOUR_PROJECT}
PROJECT_ID={YOUR_PROJECT}
REGION="us-central1"
VERTEX_API_URL=REGION + "-aiplatform.googleapis.com"
VERTEX_PREDICTION_API_URL=REGION + "-prediction-aiplatform.googleapis.com"
MULTI_MODEL_API_VERSION="v1beta1"

# Give the pool a name
DEPLOYMENT_RESOURCE_POOL_ID="my-resource-pool"

import json
import pprint
pp = pprint.PrettyPrinter(indent=4)

CREATE_RP_PAYLOAD = {
  "deployment_resource_pool":{
    "dedicated_resources":{
      "machine_spec":{
        "machine_type":"n1-standard-4"
      },
      "min_replica_count":1,
      "max_replica_count":2
    }
  },
  "deployment_resource_pool_id":DEPLOYMENT_RESOURCE_POOL_ID
}
CREATE_RP_REQUEST=json.dumps(CREATE_RP_PAYLOAD)
pp.pprint("CREATE_RP_REQUEST: " + CREATE_RP_REQUEST)

!curl \
-X POST \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
https://{VERTEX_API_URL}/{MULTI_MODEL_API_VERSION}/projects/{PROJECT_ID}/locations/{REGION}/deploymentResourcePools \
-d '{CREATE_RP_REQUEST}'

Vous pouvez voir le pool en exécutant

!curl -X GET \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
https://{VERTEX_API_URL}/{MULTI_MODEL_API_VERSION}/projects/{PROJECT_ID}/locations/{REGION}/deploymentResourcePools/{DEPLOYMENT_RESOURCE_POOL_ID}

Étape 4: Déployer les modèles sur le point de terminaison

Maintenant que le pool de ressources a été créé, nous pouvons déployer les modèles dans le pool de ressources.

Commençons par déployer model_1. Veillez à remplacer MODEL_1_ID et ENDPOINT_ID par leurs ID respectifs.

MODEL_1_ID="{MODEL_1_ID}"
ENDPOINT_ID="{ENDPOINT_ID}"

La commande suivante déploie model_1 sur le point de terminaison au sein du pool de ressources.

MODEL_NAME = "projects/{project_id}/locations/{region}/models/{model_id}".format(project_id=PROJECT_ID, region=REGION, model_id=MODEL_1_ID)
SHARED_RESOURCE = "projects/{project_id}/locations/{region}/deploymentResourcePools/{deployment_resource_pool_id}".format(project_id=PROJECT_ID, region=REGION, deployment_resource_pool_id=DEPLOYMENT_RESOURCE_POOL_ID)

DEPLOY_MODEL_PAYLOAD = {
  "deployedModel": {
    "model": MODEL_NAME,
    "shared_resources": SHARED_RESOURCE
  },
  "trafficSplit": {
    "0": 100
  }
}
DEPLOY_MODEL_REQUEST=json.dumps(DEPLOY_MODEL_PAYLOAD)
pp.pprint("DEPLOY_MODEL_REQUEST: " + DEPLOY_MODEL_REQUEST)

!curl -X POST \
 -H "Authorization: Bearer $(gcloud auth print-access-token)" \
 -H "Content-Type: application/json" \
https://{VERTEX_API_URL}/{MULTI_MODEL_API_VERSION}/projects/{PROJECT_ID}/locations/{REGION}/endpoints/{ENDPOINT_ID}:deployModel \
-d '{DEPLOY_MODEL_REQUEST}'

Cette opération prend quelques minutes. Une fois terminée, vous verrez le modèle déployé sur le point de terminaison dans la console.

model_1_endpoint

Nous pouvons maintenant déployer model_2 dans le même pool de déploiement. Nous allons la déployer sur le même point de terminaison que model_1. Cependant, vous pouvez également choisir de déployer model_2 sur un autre point de terminaison au sein du même pool de ressources.

Mettez à jour MODEL_ID avec l'ID de model_2. Là encore, vous pouvez obtenir cet ID en exécutant model_2.name.

MODEL_2_ID="{MODEL_2_ID}"

Déployez ensuite model_2. Comme model_1 est déjà déployé sur le point de terminaison, nous devons mettre à jour trafficSplit pour que le trafic soit réparti entre les deux modèles. Nous n'aurons pas à mettre à jour le trafficSplit si nous choisissons de déployer model_2 sur un autre point de terminaison au sein du même pool de ressources.

Pour mettre à jour la répartition du trafic, vous devez définir l'ID de la classe "DeployedModel" pour model_1. Notez qu'il est différent de l'ID du modèle.

DEPLOYED_MODEL_1_ID = {DEPLOYED_MODEL_1_ID}

Ensuite, exécutez la commande suivante pour déployer le deuxième modèle.

MODEL_NAME = "projects/{project_id}/locations/{region}/models/{model_id}".format(project_id=PROJECT_ID, region=REGION, model_id=MODEL_2_ID)
SHARED_RESOURCE = "projects/{project_id}/locations/{region}/deploymentResourcePools/{deployment_resource_pool_id}".format(project_id=PROJECT_ID, region=REGION, deployment_resource_pool_id=DEPLOYMENT_RESOURCE_POOL_ID)

#`trafficSplit` is a map from a DeployedModel's ID to the percentage of this Endpoint's traffic that should be forwarded to that DeployedModel.
# The traffic percentage values for an endpoint must add up to 100.
# The key for the model being deployed is "0".

DEPLOY_MODEL_PAYLOAD = {
  "deployedModel": {
    "model": MODEL_NAME,
    "shared_resources": SHARED_RESOURCE
  },
  "trafficSplit": {
    "0": 50,
    DEPLOYED_MODEL_1_ID: 50
  }
}
DEPLOY_MODEL_REQUEST=json.dumps(DEPLOY_MODEL_PAYLOAD)
pp.pprint("DEPLOY_MODEL_REQUEST: " + DEPLOY_MODEL_REQUEST)

!curl -X POST \
 -H "Authorization: Bearer $(gcloud auth print-access-token)" \
 -H "Content-Type: application/json" \
https://{VERTEX_API_URL}/{MULTI_MODEL_API_VERSION}/projects/{PROJECT_ID}/locations/{REGION}/endpoints/{ENDPOINT_ID}:deployModel \
-d '{DEPLOY_MODEL_REQUEST}'

Là encore, dans cet exemple, les deux modèles ont été déployés sur le même point de terminaison, mais vous pouvez également cohéberger des modèles sur le même pool de ressources qui sont déployés sur des points de terminaison différents. Dans ce cas, vous n'auriez pas à vous soucier de la répartition du trafic.

Une fois le second modèle déployé, les deux modèles s'afficheront dans la console.

deployed_models

Étape 5: Obtenez des prédictions

La dernière étape consiste à tester le point de terminaison et à obtenir des prédictions.

Tout d'abord, définissez la phrase test.

x_test=['The movie was cool. The animation and the graphics were out of this world. I would recommend this movie.']

Appelez ensuite la fonction predict sur le point de terminaison, qui renverra une prédiction à partir de l'un des modèles déployés sur le point de terminaison.

endpoint.predict(instances=x_test)

🎉 Félicitations ! 🎉

Vous savez désormais utiliser Vertex AI pour :

  • Cohéberger des modèles sur la même VM pour les prédictions en ligne

Pour en savoir plus sur les différents composants de Vertex, consultez la documentation.

7. Nettoyage

Nous vous recommandons d'annuler le déploiement des modèles sur le point de terminaison si vous ne prévoyez pas de les utiliser. Vous pouvez également supprimer l'intégralité du point de terminaison. Vous pouvez toujours redéployer un modèle sur un point de terminaison si nécessaire.

undeploy_model

Les notebooks gérés par Workbench expirent automatiquement après 180 minutes d'inactivité. Vous n'avez donc pas à vous soucier de l'arrêt de 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".

Arrêter l'instance

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

Supprimer l'espace de stockage