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 des 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. Il 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 le MLOps tout au long du cycle de vie du modèle.

3. Présentation du cas d'utilisation

Dans cet atelier, vous allez explorer l'ensemble de données London Bicycles Hire (Location de vélos à Londres). Ces données contiennent des informations sur les trajets à vélo effectués par le 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 Vertex AI Workbench. Ensuite, vous chargerez les données dans un notebook Jupyter à l'aide de Pandas, puis vous entraînerez un modèle TensorFlow pour prédire la durée d'un trajet à vélo en fonction du moment où le trajet s'est produit et de la distance parcourue par le vélo.

Cet atelier utilise les 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 graphe de votre modèle TensorFlow. Vous réduisez ainsi le risque de décalage entraînement/inférence en vous assurant que les données d'entraînement et les données d'inférence subissent des transformations identiques. Notez qu'à partir de TensorFlow 2.6, cette API est stable. Si vous utilisez une ancienne version de TensorFlow, vous devez 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 un ensemble de données dans BigQuery

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

Connecteur BQ

Le connecteur BigQuery vous permet d'explorer et d'interroger facilement des ensembles de données BigQuery. Vous pouvez explorer les ensembles de données d'autres projets, en plus des ensembles de données de votre projet. Pour ce faire, cliquez sur le bouton Ajouter un projet.

épingler

Dans cet atelier, vous allez utiliser des données provenant d'ensembles de données publics BigQuery. Faites défiler la page jusqu'à l'ensemble de données london_bicycles. Comme vous pouvez le constater, cet ensemble de données comporte deux tables : cycle_hire et cycle_stations. Examinons chacun d'entre eux.

london_bike_ds

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

cycle_hire_ds

Cliquez sur l'onglet Aperçu pour afficher un échantillon des données. Exécutons une requête simple pour découvrir les trajets populaires. Tout d'abord, cliquez sur le bouton Interroger la table.

cycle_hire_preview_ds

Ensuite, collez le texte suivant dans l'éditeur SQL et 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 révèlent que les trajets à vélo à destination et en provenance de la station Hyde Park Corner ont été 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 allez utiliser les fonctions géographiques de BigQuery. Plus précisément, vous allez convertir chaque chaîne de latitude/longitude en ST_GEOGPOINT et utiliser la fonction ST_DISTANCE pour calculer la distance en ligne droite entre les deux points. Vous utiliserez cette valeur comme indicateur de 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 y a trois tables dans la condition JOIN car nous devons joindre la table des stations deux fois pour obtenir la lat/long 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 comporte une couche de compatibilité de calcul qui vous permet de lancer des noyaux pour TensorFlow, PySpark, R, etc., le tout à partir d'une seule instance de notebook. Dans cet atelier, vous allez créer un notebook à l'aide du kernel TensorFlow.

Créer un DataFrame

Une fois la requête exécutée, cliquez sur "Copier le code" pour le DataFrame. Vous pourrez ainsi coller du code Python dans un notebook qui se connecte au client BigQuery et extrait ces données en tant que DataFrame pandas.

copy_for_df

Revenez ensuite au lanceur d'applications 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 se présenter comme suit:

# 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 afin de réduire le temps d'entraînement. Mais n'hésitez pas à modifier la requête et à expérimenter l'ensemble de données complet.

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 une trame de données réduite contenant uniquement les colonnes nécessaires à la partie de cet exercice consacrée au ML.

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 de type datetime Python. Au lieu d'utiliser cette datetime directement dans le modèle, vous allez créer deux caractéristiques indiquant le jour de la semaine et l'heure de la journée où le trajet à vélo a eu lieu.

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 "Durée" de secondes en minutes pour qu'elle soit plus facile à comprendre.

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 de la journée où le trajet a eu lieu, ainsi que la distance parcourue. À partir de ces informations, vous essaierez 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 Keras Functional. Pour prétraiter les données d'entrée, vous allez utiliser 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 objets tf.data.Dataset, un pour l'entraînement et un pour la validation. Il est possible que des avertissements s'affichent, mais vous pouvez les ignorer.

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

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

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

Notez que ces couches ne peuvent pas être entraînées. 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 allez définir l'état avant d'ajuster le modèle en utilisant la méthode adapt() sur les données d'entraînement. Cela permettra de calculer 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 pour les fonctionnalités Heure et 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))

Ensuite, créez la partie de prétraitement du modèle. Commencez par créer une couche tf.keras.Input pour chacun des éléments géographiques.

# 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, puis 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 compliqué pour cet ensemble de données simple. Elle n'est fournie qu'à titre de démonstration.

J'effectue un entraînement pour une époque juste pour vérifier que le code s'exécute.

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

Entraîner un modèle avec un GPU

Vous allez maintenant entraîner le modèle plus longtemps et utiliser le sélecteur de 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 ajoutant le GPU uniquement 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 Modifier le matériel.

modify_hardware

Cliquez sur "Associer des GPU", puis sélectionnez un GPU NVIDIA T4 Tensor Core.

add_gpu

La configuration du matériel prend 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