Vertex AI Workbench: entraîner un modèle TensorFlow avec des données de BigQuery

1. Présentation

Dans cet atelier, vous allez apprendre à utiliser Vertex AI Workbench pour l'exploration de données et l'entraînement de modèles de ML.

Objectifs

Vous allez apprendre à effectuer les opérations suivantes :

  • Créer et configurer une instance Vertex AI Workbench
  • Utiliser le connecteur BigQuery de Vertex AI Workbench
  • Entraîner un modèle sur un noyau Vertex AI Workbench

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

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.

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 Vertex AI Workbench.

Vertex AI Workbench permet aux utilisateurs de créer rapidement des workflows de bout en bout basés sur des notebooks, grâce à une intégration approfondie avec des services de données (Dataproc, Dataflow, BigQuery, Dataplex, etc.) et Vertex AI. Cette solution permet aux data scientists de se connecter aux services de données GCP, d'analyser des ensembles de données, de tester différentes techniques de modélisation, de déployer des modèles entraînés en production et de gérer les MLOps tout au long du cycle de vie des modèles.

3. Présentation du cas d'utilisation

Dans cet atelier, vous allez explorer l'ensemble de données « London Bicycles Hire ». Ces données contiennent des informations sur les trajets à vélo effectués dans le cadre du programme public de vélos en libre-service de Londres depuis 2011. Vous commencerez par explorer cet ensemble de données dans BigQuery à l'aide du connecteur BigQuery de Vertex AI Workbench. Vous chargerez ensuite les données dans un notebook Jupyter à l'aide de pandas et entraînerez un modèle TensorFlow pour prédire la durée d'un trajet à vélo en fonction du moment où il a eu lieu et de la distance parcourue.

Cet atelier utilise des couches de prétraitement Keras pour transformer et préparer les données d'entrée pour l'entraînement du modèle. Cette API vous permet d'intégrer le prétraitement directement dans le graphique de votre modèle TensorFlow, ce qui réduit le risque de biais entre l'entraînement et la diffusion en veillant à ce que les données d'entraînement et les données de diffusion subissent des transformations identiques. Notez qu'à partir de TensorFlow 2.6, cette API est stable. Si vous utilisez une ancienne version de TensorFlow, vous devrez importer le symbole expérimental.

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.

service_account

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.

Une fois l'instance créée, sélectionnez OUVRIR JUPYTERLAB :

enable_terminal

5. Explorer l'ensemble de données dans BigQuery

Dans l'instance Vertex AI Workbench, accédez à la gauche et cliquez sur le connecteur BigQuery dans les notebooks.

Connecteur BQ

Le connecteur BigQuery vous permet d'explorer et d'interroger facilement des ensembles de données BigQuery. En plus des ensembles de données de votre projet, vous pouvez explorer des ensembles de données dans d'autres projets en cliquant sur le bouton Ajouter un projet.

épingler

Pour cet atelier, vous utiliserez les données des ensembles de données publics BigQuery. Faites défiler l'écran jusqu'à ce que vous trouviez l'ensemble de données london_bicycles. Vous verrez que cet ensemble de données comporte deux tables : cycle_hire et cycle_stations. Examinons chacune de ces tables.

london_bike_ds

Tout d'abord, double-cliquez sur la table cycle_hire. Vous verrez que la table s'ouvre dans un nouvel onglet avec son schéma, ainsi que des métadonnées telles que le nombre de lignes et la taille.

cycle_hire_ds

Si vous cliquez sur l'onglet Preview (Aperçu), vous pourrez voir un échantillon des données. Exécutons une requête simple pour voir quels sont les trajets les plus populaires. Tout d'abord, cliquez sur le bouton Query table (Interroger la table).

cycle_hire_preview_ds

Collez ensuite le code suivant dans l'éditeur SQL, puis cliquez sur Submit Query (Envoyer la requête).

SELECT
  start_station_name,
  end_station_name,
  IF(start_station_name = end_station_name,
    TRUE,
    FALSE) same_station,
  AVG(duration) AS avg_duration,
  COUNT(*) AS total_rides
FROM
  `bigquery-public-data.london_bicycles.cycle_hire`
GROUP BY
  start_station_name,
  end_station_name,
  same_station
ORDER BY
  total_rides DESC

Les résultats de la requête indiquent que les trajets à vélo vers et depuis la station Hyde Park Corner sont les plus populaires.

journey_query_results

Ensuite, double-cliquez sur la table cycle_stations, qui fournit des informations sur chaque station.

Nous voulons joindre les tables cycle_hire et cycle_stations. La table cycle_stations contient la latitude et la longitude de chaque station. Vous utiliserez ces informations pour estimer la distance parcourue lors de chaque trajet à vélo en calculant la distance entre les stations de départ et d'arrivée.

Pour effectuer ce calcul, vous utiliserez les fonctions géographiques de BigQuery. Plus précisément, vous convertirez chaque chaîne de latitude/longitude en ST_GEOGPOINT et utiliserez la fonction ST_DISTANCE pour calculer la distance en ligne droite en mètres entre les deux points. Vous utiliserez cette valeur comme proxy pour la distance parcourue lors de chaque trajet à vélo.

Copiez la requête suivante dans votre éditeur SQL, puis cliquez sur "Submit Query" (Envoyer la requête). Notez qu'il existe trois tables dans la condition JOIN, car nous devons joindre la table des stations deux fois pour obtenir la latitude et la longitude de la station de départ et de la station d'arrivée du vélo.

WITH staging AS (
    SELECT
        STRUCT(
            start_stn.name,
            ST_GEOGPOINT(start_stn.longitude, start_stn.latitude) AS POINT,
            start_stn.docks_count,
            start_stn.install_date
        ) AS starting,
        STRUCT(
            end_stn.name,
            ST_GEOGPOINT(end_stn.longitude, end_stn.latitude) AS point,
            end_stn.docks_count,
            end_stn.install_date
        ) AS ending,
        STRUCT(
            rental_id,
            bike_id,
            duration, --seconds
            ST_DISTANCE(
                ST_GEOGPOINT(start_stn.longitude, start_stn.latitude),
                ST_GEOGPOINT(end_stn.longitude, end_stn.latitude)
            ) AS distance, --meters
            start_date,
            end_date
        ) AS bike
        FROM `bigquery-public-data.london_bicycles.cycle_stations` AS start_stn
        LEFT JOIN `bigquery-public-data.london_bicycles.cycle_hire` as b
        ON start_stn.id = b.start_station_id
        LEFT JOIN `bigquery-public-data.london_bicycles.cycle_stations` AS end_stn
        ON end_stn.id = b.end_station_id
        LIMIT 700000)

SELECT * from STAGING

6. Entraîner un modèle de ML sur un noyau TensorFlow

Vertex AI Workbench dispose d'une couche de compatibilité de calcul qui vous permet de lancer des noyaux pour TensorFlow, PySpark, R, etc., à partir d'une seule instance de notebook. Dans cet atelier, vous allez créer un notebook à l'aide du noyau TensorFlow.

Créer un DataFrame

Une fois la requête exécutée, cliquez sur "Copy code for DataFrame" (Copier le code pour le DataFrame). Vous pourrez ainsi coller le code Python dans un notebook qui se connecte au client BigQuery et extrait ces données sous forme de DataFrame pandas.

copy_for_df

Revenez ensuite au lanceur et créez un notebook TensorFlow 2.

tf_kernel

Dans la première cellule du notebook, collez le code copié à partir de l'éditeur de requête. Le résultat doit ressembler à ceci :

# The following two lines are only necessary to run once.
# Comment out otherwise for speed-up.
from google.cloud.bigquery import Client, QueryJobConfig
client = Client()

query = """WITH staging AS (
    SELECT
        STRUCT(
            start_stn.name,
            ST_GEOGPOINT(start_stn.longitude, start_stn.latitude) AS POINT,
            start_stn.docks_count,
            start_stn.install_date
        ) AS starting,
        STRUCT(
            end_stn.name,
            ST_GEOGPOINT(end_stn.longitude, end_stn.latitude) AS point,
            end_stn.docks_count,
            end_stn.install_date
        ) AS ending,
        STRUCT(
            rental_id,
            bike_id,
            duration, --seconds
            ST_DISTANCE(
                ST_GEOGPOINT(start_stn.longitude, start_stn.latitude),
                ST_GEOGPOINT(end_stn.longitude, end_stn.latitude)
            ) AS distance, --meters
            start_date,
            end_date
        ) AS bike
        FROM `bigquery-public-data.london_bicycles.cycle_stations` AS start_stn
        LEFT JOIN `bigquery-public-data.london_bicycles.cycle_hire` as b 
        ON start_stn.id = b.start_station_id
        LEFT JOIN `bigquery-public-data.london_bicycles.cycle_stations` AS end_stn
        ON end_stn.id = b.end_station_id
        LIMIT 700000)

SELECT * from STAGING"""
job = client.query(query)
df = job.to_dataframe()

Pour les besoins de cet atelier, nous limitons l'ensemble de données à 700 000 éléments afin de réduire le temps d'entraînement. N'hésitez pas à modifier la requête et à tester l'ensemble de données.

Importez ensuite les bibliothèques nécessaires.

from datetime import datetime
import pandas as pd
import tensorflow as tf

Exécutez le code suivant pour créer un DataFrame réduit qui ne contient que les colonnes nécessaires à la partie ML de cet exercice.

values = df['bike'].values
duration = list(map(lambda a: a['duration'], values))
distance = list(map(lambda a: a['distance'], values))
dates = list(map(lambda a: a['start_date'], values))
data = pd.DataFrame(data={'duration': duration, 'distance': distance, 'start_date':dates})
data = data.dropna()

La colonne start_date est un datetime Python. Au lieu d'utiliser directement ce datetime dans le modèle, vous allez créer deux caractéristiques qui indiquent le jour de la semaine et l'heure du trajet à vélo.

data['weekday'] = data['start_date'].apply(lambda a: a.weekday())
data['hour'] = data['start_date'].apply(lambda a: a.time().hour)
data = data.drop(columns=['start_date'])

Enfin, convertissez la colonne de durée de secondes en minutes pour faciliter la compréhension.

data['duration'] = data['duration'].apply(lambda x:float(x / 60))

Examinez les premières lignes du DataFrame mis en forme. Pour chaque trajet à vélo, vous disposez désormais de données sur le jour de la semaine et l'heure du trajet, ainsi que sur la distance parcourue. À partir de ces informations, vous allez essayer de prédire la durée du trajet.

data.head()

data_head

Avant de pouvoir créer et entraîner le modèle, vous devez diviser les données en ensembles d'entraînement et de validation.

# Use 80/20 train/eval split
train_size = int(len(data) * .8)
print ("Train size: %d" % train_size)
print ("Evaluation size: %d" % (len(data) - train_size))

# Split data into train and test sets
train_data = data[:train_size]
val_data = data[train_size:]

Créer un modèle TensorFlow

Vous allez créer un modèle TensorFlow à l'aide de l'API fonctionnelle Keras. Pour prétraiter les données d'entrée, vous utiliserez l'API des couches de prétraitement Keras.

La fonction utilitaire suivante crée un tf.data.Dataset à partir du DataFrame pandas.

def df_to_dataset(dataframe, label, shuffle=True, batch_size=32):
  dataframe = dataframe.copy()
  labels = dataframe.pop(label)
  ds = tf.data.Dataset.from_tensor_slices((dict(dataframe), labels))
  if shuffle:
    ds = ds.shuffle(buffer_size=len(dataframe))
  ds = ds.batch(batch_size)
  ds = ds.prefetch(batch_size)
  return ds

Utilisez la fonction ci-dessus pour créer deux tf.data.Dataset, l'un pour l'entraînement et l'autre pour la validation. Vous pouvez voir des avertissements, mais vous pouvez les ignorer en toute sécurité.

train_dataset = df_to_dataset(train_data, 'duration')
validation_dataset = df_to_dataset(val_data, 'duration')

Vous utiliserez les couches de prétraitement suivantes dans le modèle :

  • Couche de normalisation : effectue une normalisation des caractéristiques d'entrée.
  • Couche IntegerLookup : transforme les valeurs catégorielles entières en index d'entiers.
  • Couche CategoryEncoding : transforme les caractéristiques catégorielles entières en représentations one-hot, multi-hot ou TF-IDF denses.

Notez qu'il est impossible d'entraîner ces couches. Au lieu de cela, vous définissez l'état de la couche de prétraitement en l'exposant aux données d'entraînement via la méthode adapt().

La fonction suivante crée une couche de normalisation que vous pouvez utiliser sur la caractéristique de distance. Vous définirez l'état avant d'ajuster le modèle à l'aide de la méthode adapt() sur les données d'entraînement. Cela calculera la moyenne et la variance à utiliser pour la normalisation. Plus tard, lorsque vous transmettrez l'ensemble de données de validation au modèle, cette même moyenne et cette même variance calculées sur les données d'entraînement seront utilisées pour mettre à l'échelle les données de validation.

def get_normalization_layer(name, dataset):
  # Create a Normalization layer for our feature.
  normalizer = tf.keras.layers.Normalization(axis=None)

  # Prepare a Dataset that only yields our feature.
  feature_ds = dataset.map(lambda x, y: x[name])

  # Learn the statistics of the data.
  normalizer.adapt(feature_ds)

  return normalizer

De même, la fonction suivante crée un encodage de catégorie que vous utiliserez sur les caractéristiques d'heure et de jour de la semaine.

def get_category_encoding_layer(name, dataset, dtype, max_tokens=None):
  index = tf.keras.layers.IntegerLookup(max_tokens=max_tokens)

  # Prepare a Dataset that only yields our feature
  feature_ds = dataset.map(lambda x, y: x[name])

  # Learn the set of possible values and assign them a fixed integer index.
  index.adapt(feature_ds)

  # Create a Discretization for our integer indices.
  encoder = tf.keras.layers.CategoryEncoding(num_tokens=index.vocabulary_size())

  # Apply one-hot encoding to our indices. The lambda function captures the
  # layer so we can use them, or include them in the functional model later.
  return lambda feature: encoder(index(feature))

Créez ensuite la partie prétraitement du modèle. Commencez par créer une couche tf.keras.Input pour chacune des caractéristiques.

# Create a Keras input layer for each feature
numeric_col = tf.keras.Input(shape=(1,), name='distance')
hour_col = tf.keras.Input(shape=(1,), name='hour', dtype='int64')
weekday_col = tf.keras.Input(shape=(1,), name='weekday', dtype='int64')

Créez ensuite les couches de normalisation et d'encodage de catégorie, et stockez-les dans une liste.

all_inputs = []
encoded_features = []

# Pass 'distance' input to normalization layer
normalization_layer = get_normalization_layer('distance', train_dataset)
encoded_numeric_col = normalization_layer(numeric_col)
all_inputs.append(numeric_col)
encoded_features.append(encoded_numeric_col)

# Pass 'hour' input to category encoding layer
encoding_layer = get_category_encoding_layer('hour', train_dataset, dtype='int64')
encoded_hour_col = encoding_layer(hour_col)
all_inputs.append(hour_col)
encoded_features.append(encoded_hour_col)

# Pass 'weekday' input to category encoding layer
encoding_layer = get_category_encoding_layer('weekday', train_dataset, dtype='int64')
encoded_weekday_col = encoding_layer(weekday_col)
all_inputs.append(weekday_col)
encoded_features.append(encoded_weekday_col)

Après avoir défini les couches de prétraitement, vous pouvez définir le reste du modèle. Vous allez concaténer toutes les caractéristiques d'entrée et les transmettre à une couche dense. La couche de sortie est une unité unique, car il s'agit d'un problème de régression.

all_features = tf.keras.layers.concatenate(encoded_features)
x = tf.keras.layers.Dense(64, activation="relu")(all_features)
output = tf.keras.layers.Dense(1)(x)
model = tf.keras.Model(all_inputs, output)

Enfin, compilez le modèle.

model.compile(optimizer = tf.keras.optimizers.Adam(0.001),
              loss='mean_squared_logarithmic_error')

Maintenant que vous avez défini le modèle, vous pouvez visualiser l'architecture.

tf.keras.utils.plot_model(model, show_shapes=True, rankdir="LR")

keras_model

Notez que ce modèle est assez complexe pour cet ensemble de données simple. Il est destiné à des fins de démonstration.

Entraînons-nous pendant une époque pour confirmer que le code s'exécute.

model.fit(train_dataset, validation_data = validation_dataset, epochs = 1)

Entraîner un modèle avec un GPU

Ensuite, vous entraînerez le modèle plus longtemps et utiliserez le commutateur matériel pour accélérer l'entraînement. Vertex AI Workbench vous permet de modifier le matériel sans arrêter votre instance. En n'ajoutant le GPU que lorsque vous en avez besoin, vous pouvez réduire les coûts.

Pour modifier le profil matériel, cliquez sur le type de machine en haut à droite, puis sélectionnez Modify hardware (Modifier le matériel).

modify_hardware

Sélectionnez "Attach GPUs" (Associer des GPU), puis sélectionnez un GPU NVIDIA T4 Tensor Core.

add_gpu

La configuration du matériel prendra environ cinq minutes. Une fois le processus terminé, entraînons le modèle un peu plus longtemps. Vous remarquerez que chaque époque prend désormais moins de temps.

model.fit(train_dataset, validation_data = validation_dataset, epochs = 5)

🎉 Félicitations ! 🎉

Vous savez désormais utiliser Vertex AI Workbench pour :

  • Explorer les données dans BigQuery
  • Utiliser le client BigQuery pour charger des données dans Python
  • Entraîner un modèle TensorFlow avec des couches de prétraitement Keras et un GPU

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

7. 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".

supprimer