Créer un data lakehouse unifié pour l'IA avec Apache Iceberg et BigLake

1. Introduction

Dans cet atelier de programmation, vous allez explorer les fonctionnalités du data lakehouse unifié sur Google Cloud. Vous interagirez avec des ensembles de données publics diffusés via le catalogue Apache Iceberg REST sur BigLake, puis vous appliquerez les fonctionnalités d'IA de Google Cloud aux données structurées et non structurées.

Vous interrogerez l'ensemble de données classique sur les taxis new-yorkais à l'aide d'Apache Iceberg, vous explorerez la fonctionnalité temporelle pour auditer les modifications apportées aux données, puis vous utiliserez BigQuery ML et Gemini pour exécuter des modèles d'IA sur vos données.

Objectifs de l'atelier

  • Utiliser Google Cloud Serverless pour Apache Spark afin d'interroger des ensembles de données publics Apache Iceberg hébergés sur BigLake.
  • Interroger des données structurées au format Apache Iceberg
  • Démontrer la fonctionnalité temporelle dans Apache Iceberg
  • Utiliser BigQuery ML pour entraîner un modèle prédictif sur des données structurées
  • Créer une table d'objets BigLake (données non structurées) et utiliser Gemini pour analyser des images

Ce dont vous avez besoin

  • Un navigateur Web tel que Chrome.
  • Un projet Google Cloud avec facturation activée.

Coût et durée prévus

  • Durée : environ 45 minutes.
  • Coût estimé : moins de 2 $. Nous utilisons des ensembles de données publics et des requêtes sans serveur pour limiter les coûts.

2. Préparation

Dans cette étape, vous allez préparer votre environnement et activer les API nécessaires.

Démarrer Cloud Shell

Vous exécuterez la plupart des commandes à partir de Google Cloud Shell.

  1. Cliquez sur Activer Cloud Shell en haut de la console Google Cloud.
  2. Vérifiez l'authentification :
    gcloud auth list
    
  3. Confirmez votre projet :
    gcloud config get project
    
  4. Si le projet n'est pas défini, définissez-le à l'aide de votre ID de projet :
    gcloud config set project <YOUR_PROJECT_ID>
    

Activer les API

Exécutez la commande suivante pour activer les API requises pour BigQuery, Cloud Resource Manager et Vertex AI :

gcloud services enable \
  bigquery.googleapis.com \
  aiplatform.googleapis.com \
  cloudresourcemanager.googleapis.com

Configurer l'environnement et créer un bucket de dépendances

  1. Définissez vos variables d'environnement dans votre terminal :
    export PROJECT_ID=$(gcloud config get project)
    export REGION=us-central1
    export DEPS_BUCKET=$PROJECT_ID-deps-bucket
    
  2. Créez le bucket Cloud Storage des dépendances. Les scripts PySpark sont importés ici au moment de l'envoi du job :
    gcloud storage buckets create gs://$DEPS_BUCKET --location=$REGION
    

3. Se connecter au catalogue public Apache Iceberg

Dans cette étape, vous allez vous connecter à un catalogue Apache Iceberg en direct et de qualité production hébergé sur BigLake de Google Cloud.

Exécuter Spark SQL avec l'interface de ligne de commande par lot Serverless pour Apache Spark

Nous allons utiliser Google Cloud Serverless pour Apache Spark afin d'exécuter des jobs PySpark sans avoir à gérer l'infrastructure. Nous allons le configurer pour qu'il pointe vers le catalogue REST public BigLake.

  1. Définissez les propriétés du catalogue REST BigLake pour éviter de les répéter.Cette configuration indique à Spark :
    • d'utiliser les bibliothèques iceberg-spark-runttime et iceberg-gcp-bundle ;
    • de configurer un catalogue nommé my_catalog à l'aide du point de terminaison du catalogue REST BigLake ;
    • d'utiliser Google Cloud Storage (GCS) pour lire les fichiers de données au lieu du système de fichiers local par défaut ;
    • de définir ce catalogue my_catalog comme par défaut pour notre session ;
    • d'utiliser des identifiants vendus pour une sécurité renforcée et un accès aux données simplifié.
    export METASTORE_PROPERTIES="^|^spark.jars.packages=org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.10.0,org.apache.iceberg:iceberg-gcp-bundle:1.10.0|\
    spark.sql.catalog.my_catalog=org.apache.iceberg.spark.SparkCatalog|\
    spark.sql.catalog.my_catalog.type=rest|\
    spark.sql.catalog.my_catalog.uri=https://biglake.googleapis.com/iceberg/v1/restcatalog|\
    spark.sql.catalog.my_catalog.warehouse=gs://biglake-public-nyc-taxi-iceberg|\
    spark.sql.catalog.my_catalog.io-impl=org.apache.iceberg.gcp.gcs.GCSFileIO|\
    spark.sql.catalog.my_catalog.header.x-goog-user-project=$PROJECT_ID|\
    spark.sql.catalog.my_catalog.header.X-Iceberg-Access-Delegation=vended-credentials|\
    spark.sql.catalog.my_catalog.rest.auth.type=org.apache.iceberg.gcp.auth.GoogleAuthManager|\
    spark.sql.defaultCatalog=my_catalog|\
    spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions|\
    spark.log.level=ERROR"
    
  2. Créez un fichier de requête de test simple :
    cat <<EOF > test.py
    from pyspark.sql import SparkSession
    
    spark = SparkSession.builder.getOrCreate()
    
    spark.sql("SHOW TABLES IN public_data").show()
    EOF
    
  3. Envoyez le job par lot :
    gcloud dataproc batches submit pyspark \
      --project=$PROJECT_ID \
      --region=$REGION \
      --version=2.3 \
      --properties="$METASTORE_PROPERTIES" \
      --deps-bucket=gs://$DEPS_BUCKET \
      test.py
    
    La sortie obtenue doit ressembler à ceci :
    +-----------+----------------+-----------+
    |  namespace|       tableName|isTemporary|
    +-----------+----------------+-----------+
    |public_data|     nyc_taxicab|      false|
    |public_data|nyc_taxicab_2021|      false|
    +-----------+----------------+-----------+
    

4. Interroger des données Iceberg structurées

Une fois connecté, vous disposez d'un accès SQL complet aux ensembles de données. Nous allons interroger l'ensemble de données sur les taxis new-yorkais modélisé sous forme de table Iceberg.

Exécuter une requête d'agrégation standard

Créez un fichier nommé query.py :

cat <<EOF > query.py
from pyspark.sql import SparkSession

spark = SparkSession.builder.getOrCreate()

query = """
SELECT
  passenger_count,
  COUNT(1) AS num_trips,
  ROUND(AVG(total_amount), 2) AS avg_fare,
  ROUND(AVG(trip_distance), 2) AS avg_distance
FROM public_data.nyc_taxicab
WHERE data_file_year = 2021 AND passenger_count > 0
GROUP BY passenger_count
ORDER BY num_trips DESC
"""

spark.sql(query).show()
EOF

Envoyez-le à l'aide de Serverless pour Apache Spark :

gcloud dataproc batches submit pyspark \
  --project=$PROJECT_ID \
  --region=$REGION \
  --version=2.3 \
  --properties="$METASTORE_PROPERTIES" \
  --deps-bucket=gs://$DEPS_BUCKET \
  query.py

La sortie obtenue dans votre console doit ressembler à ceci :

+---------------+---------+--------+------------+
|passenger_count|num_trips|avg_fare|avg_distance|
+---------------+---------+--------+------------+
|              1| 21508009|   18.82|        3.03|
|              2|  4424746|   20.22|        3.40|
|              3|  1164846|   19.84|        3.27|
|              5|   718282|   18.88|        3.07|
|              4|   466485|   20.61|        3.44|
|              6|   452467|   18.97|        3.11|
|              7|       78|   65.24|        3.71|
|              8|       49|   57.39|        5.88|
|              9|       35|   73.26|        6.20|
|             96|        1|   17.00|        2.00|
|            112|        1|   15.00|        2.00|
+---------------+---------+--------+------------+

Pourquoi utiliser Apache Iceberg ici ?

  • Élagage de partition : la requête filtre sur data_file_year = 2021. Iceberg permet au moteur d'ignorer complètement l'analyse des données d'autres années.
  • Agilité du moteur : vous pouvez exécuter cette requête dans Spark, Trino ou BigQuery sans copier de données.

5. Fonctionnalité temporelle dans Apache Iceberg

L'une des fonctionnalités les plus puissantes d'Iceberg est la fonctionnalité temporelle. Elle vous permet d'interroger les données telles qu'elles existaient dans une version ou un instantané antérieur.

Afficher l'historique de la table

Créez un fichier nommé history.py :

cat <<EOF > history.py
from pyspark.sql import SparkSession

spark = SparkSession.builder.getOrCreate()

spark.sql("SELECT * FROM public_data.nyc_taxicab.history").show()
EOF

Envoyez-le :

gcloud dataproc batches submit pyspark \
  --project=$PROJECT_ID \
  --region=$REGION \
  --version=2.3 \
  --properties="$METASTORE_PROPERTIES" \
  --deps-bucket=gs://$DEPS_BUCKET \
  history.py

La sortie obtenue dans votre console doit ressembler à ceci :

+--------------------+-------------------+-------------------+-------------------+
|     made_current_at|        snapshot_id|          parent_id|is_current_ancestor|
+--------------------+-------------------+-------------------+-------------------+
|2026-01-07 21:32:...|6333415779680505547|               NULL|               true|
|2026-01-07 21:34:...|1840345522877675925|6333415779680505547|               true|
|2026-01-07 21:36:...|7203554539964460256|1840345522877675925|               true|
|2026-01-07 21:38:...|4573466015237516024|7203554539964460256|               true|
|2026-01-07 21:40:...|3353190952148867790|4573466015237516024|               true|
|2026-01-07 21:42:...|1335547378580631681|3353190952148867790|               true|
|2026-01-07 21:44:...|8203141258229894239|1335547378580631681|               true|
|2026-01-07 21:46:...|1597048231706307813|8203141258229894239|               true|
|2026-01-07 21:48:...|6247811509231462655|1597048231706307813|               true|
|2026-01-07 21:50:...|2527184310045633322|6247811509231462655|               true|
|2026-01-07 21:52:...|2512764101237223642|2527184310045633322|               true|
|2026-01-07 21:52:...|7045957533358062548|2512764101237223642|               true|
|2026-01-07 21:53:...| 531753237516076726|7045957533358062548|               true|
|2026-01-07 21:53:...|4184653573199718274| 531753237516076726|               true|
|2026-01-07 21:54:...|5125223829492177301|4184653573199718274|               true|
|2026-01-07 21:54:...|6844673237417600305|5125223829492177301|               true|
|2026-01-07 21:54:...|6634828203344518093|6844673237417600305|               true|
|2026-01-07 21:55:...|7637728273407236194|6634828203344518093|               true|
|2026-01-07 21:55:...|3424071684958740192|7637728273407236194|               true|
|2026-01-07 21:55:...|1743746294196424254|3424071684958740192|               true|
+--------------------+-------------------+-------------------+-------------------+

Vous verrez des lignes représentant différents ID d'instantané et le moment où ils ont été validés.

Comparer le nombre de lignes actuel et antérieur

Créez un fichier nommé timetravel.py :

cat <<EOF > timetravel.py
from pyspark.sql import SparkSession

spark = SparkSession.builder.getOrCreate()

query = """
SELECT 'Current State' AS version, COUNT(*) AS count FROM public_data.nyc_taxicab
UNION ALL
SELECT 'Past State' AS version, COUNT(*) AS count FROM public_data.nyc_taxicab VERSION AS OF 4573466015237516024
"""

spark.sql(query).show()
EOF

Envoyez-le :

gcloud dataproc batches submit pyspark \
  --project=$PROJECT_ID \
  --region=$REGION \
  --version=2.3 \
  --properties="$METASTORE_PROPERTIES" \
  --deps-bucket=gs://$DEPS_BUCKET \
  timetravel.py

La sortie obtenue dans votre console doit ressembler à ceci :

+-------------+----------+
|      version|     count|
+-------------+----------+
|Current State|1293069366|
|   Past State|  72878594|
+-------------+----------+

Cela vous permet d'auditer les modifications apportées aux données au fil du temps.

6. IA structurée avec BigQuery ML

Maintenant que vous avez exploré les données Iceberg, utilisons les fonctionnalités d'IA de BigQuery. Étant donné que le catalogue public Iceberg est en lecture seule, nous pouvons utiliser BigQuery pour entraîner un modèle dans notre espace de travail en lisant des tables publiques.

Créer un ensemble de données local

Commencez par créer un ensemble de données dans votre projet pour stocker le modèle d'IA à l'aide de l'interface de ligne de commande bq :

bq mk --location=$REGION --project_id=$PROJECT_ID iceberg_ai

Entraîner un modèle de régression linéaire

Vous allez maintenant entraîner un modèle de régression linéaire à l'aide de la table publique BigLake Iceberg.

Créez un fichier de requête et entraînez le modèle à l'aide de bq query :

cat <<'EOF' > train_model.sql
CREATE OR REPLACE MODEL `iceberg_ai.predict_fare`
OPTIONS(model_type='LINEAR_REG', input_label_cols=['fare_amount']) AS
SELECT fare_amount, passenger_count, CAST(trip_distance AS FLOAT64) AS trip_distance
FROM `bigquery-public-data`.`biglake-public-nyc-taxi-iceberg`.public_data.nyc_taxicab
WHERE fare_amount > 0 AND trip_distance > 0 AND RAND() < 0.01; -- Using 1% of data to downsample
EOF

bq query --location=$REGION --use_legacy_sql=false < train_model.sql

Effectuer des prédictions à l'aide du modèle

Maintenant que le modèle est entraîné, vous pouvez l'utiliser pour prédire les montants des tarifs pour les nouveaux trajets à l'aide de ML.PREDICT.

Créez un fichier de requête et exécutez la prédiction à l'aide de bq query :

cat <<'EOF' > predict_fare.sql
SELECT
  predicted_fare_amount, passenger_count, trip_distance
FROM
  ML.PREDICT(MODEL `iceberg_ai.predict_fare`,
    (
    SELECT 2 AS passenger_count, 5.0 AS trip_distance
    )
  );
EOF

bq query --location=$REGION --use_legacy_sql=false < predict_fare.sql

La sortie obtenue doit ressembler à ceci :

+-----------------------+-----------------+---------------+
| predicted_fare_amount | passenger_count | trip_distance |
+-----------------------+-----------------+---------------+
|     14.12252095150709 |               2 |           5.0 |
+-----------------------+-----------------+---------------+

7. IA non structurée avec BigLake

Les données ne se limitent pas aux lignes et aux colonnes. Les data lakehouses unifiés gèrent également les données non structurées (images, PDF). Utilisons des tables d'objets et des références d'objets pour interroger des données non structurées.

Les tables d'objets sont des tables externes en lecture seule dans BigQuery qui répertorient les objets dans un chemin Cloud Storage. Chaque ligne représente un fichier, avec des colonnes pour les métadonnées telles que uri, size et une colonne ref spéciale contenant l'ObjectRef.

Les références d'objets (ObjectRef) pointent vers les données réelles d'un seul fichier. Les fonctions BigQuery ML modernes (telles que AI.GENERATE ou AI.AGG) utilisent ObjectRef pour lire le contenu des fichiers (images, audio ou texte) à des fins d'analyse sans charger les octets dans une table standard.

Créer un ensemble de données pour l'IA non structurée

Commencez par créer un deuxième ensemble de données dans votre projet pour stocker les tables d'objets à l'aide de l'interface de ligne de commande bq dans la région multirégionale US :

bq mk --location=US --project_id=$PROJECT_ID iceberg_object_ai

Créer une connexion externe

Pour interroger des données stockées dans Cloud Storage (tables d'objets et données non structurées) à partir de BigQuery, vous devez créer une connexion externe.

Exécutez la commande suivante dans Cloud Shell pour créer une connexion à une ressource Cloud :

bq mk --connection --project_id=$PROJECT_ID --location=US --connection_type=CLOUD_RESOURCE my-conn

Recherchez l'ID de compte de service créé pour votre connexion :

CONNECTION_SA=$(bq show --format=json --project_id=$PROJECT_ID --connection $PROJECT_ID.us.my-conn | jq -r '.serviceAccountId // .cloudResource.serviceAccountId')

Attribuez au compte de service les rôles Utilisateur Vertex AI et Lecteur d'objets de l'espace de stockage afin qu'il puisse appeler des modèles Gemini et lire des données GCS :

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$CONNECTION_SA" \
  --role="roles/aiplatform.user"

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$CONNECTION_SA" \
  --role="roles/storage.objectViewer"

Créer une table d'objets

Nous allons utiliser la connexion externe my-conn créée dans la section précédente pour accéder aux données non structurées. Créez un fichier de requête et créez la table d'objets à l'aide de bq query :

cat <<'EOF' > create_object_table.sql
CREATE EXTERNAL TABLE `iceberg_object_ai.sample_images`
WITH CONNECTION `us.my-conn`
OPTIONS (
  object_metadata = 'SIMPLE',
  uris = ['gs://cloud-samples-data/vision/landmark/*']
);
EOF

bq query --use_legacy_sql=false < create_object_table.sql

Utiliser Gemini sur des données d'objet

Exécutez maintenant une requête à l'aide de Gemini pour évaluer les images sans les télécharger.

Interrogez les images à l'aide de SQL standard via bq query :

cat <<EOF > query_images.sql
SELECT
  uri,
  image_analysis.description
FROM (
  SELECT
    uri,
    AI.GENERATE(
      (
        'Identify what is happening in the image.',
        ref
      ),
      connection_id => 'us.my-conn',
      endpoint => 'gemini-2.5-flash-lite',
      output_schema => 'event STRING, severity STRING, description STRING'
    ) AS image_analysis
  FROM
    iceberg_object_ai.sample_images
);
EOF

bq query --use_legacy_sql=false < query_images.sql

Exemple de résultat :

+----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                           uri                            |                                                                                                                                                                                                                             description                                                                                                                                                                                                                             |
+----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| gs://cloud-samples-data/vision/landmark/eiffel_tower.jpg | The Eiffel Tower stands tall against a cloudy sky, overlooking the Seine River in Paris. Boats are docked along the riverbank, and trees line the opposite shore, with bridges and buildings visible in the distance.                                                                                                                                                                                                                                               |
| gs://cloud-samples-data/vision/landmark/pofa.jpg         | A wide shot shows the Palace of Fine Arts, a monumental structure in San Francisco, California. The building features a large rotunda with a dome, surrounded by colonnades. In front of the rotunda is a lagoon. Several people are walking around the grounds. The sky is blue with a few scattered clouds.                                                                                                                                                       |
| gs://cloud-samples-data/vision/landmark/st_basils.jpeg   | A monument stands in front of Saint Basil's Cathedral in Moscow under a bright blue sky with scattered white clouds. The cathedral features distinctive onion domes in various colors and patterns, including red, blue and white stripes, green and beige stripes, and red and blue diamonds. A large green tree partially obscures the left side of the cathedral. People are visible in the foreground near the base of the monument and the cathedral entrance. |
+----------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

Explorer directement les ObjectRef : analyse des sentiments

Bien que les tables d'objets gèrent automatiquement les références de fichiers, vous pouvez interagir directement avec ces objets à l'aide des références d'objets BigQuery pour exécuter une analyse à la volée sur des fichiers uniques.

Par exemple, vous pouvez utiliser un petit fichier texte stocké dans votre propre bucket GCS (à l'aide de la variable $DEPS_BUCKET créée précédemment) et l'analyser à l'aide de OBJ.MAKE_REF avec bq query.

Commencez par créer un petit fichier texte et importez-le dans votre bucket :

cat <<'EOF' > review.txt
This product is fantastic! It exceeded my expectations. The quality is top-notch. I highly recommend it to everyone!
EOF

gcloud storage cp review.txt gs://${DEPS_BUCKET}/review.txt

Interrogez maintenant le fichier à l'aide de OBJ.MAKE_REF dans SQL standard :

cat <<EOF > sentiment_analysis.sql
SELECT
  AI.GENERATE(
    (
      'Analyze the sentiment of this text file. Is it positive, negative, or neutral? Explain why.',
      OBJ.MAKE_REF('gs://${DEPS_BUCKET}/review.txt', 'us.my-conn')
    ),
    connection_id => 'us.my-conn',
    endpoint => 'gemini-2.5-flash-lite'
  ).result AS ml_generate_text_result;
EOF

bq query --use_legacy_sql=false < sentiment_analysis.sql

Exemple de résultat :

+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                                 ml_generate_text_result                                                                                  |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| This text file has a **strongly positive** sentiment.                                                                                                                                    |
|                                                                                                                                                                                          |
| Here's why:                                                                                                                                                                              |
|                                                                                                                                                                                          |
| *   **Positive Keywords:** The text is filled with unequivocally positive words and phrases:                                                                                             |
|     *   "fantastic"                                                                                                                                                                      |
|     *   "exceeded my expectations"                                                                                                                                                       |
|     *   "top-notch"                                                                                                                                                                      |
|     *   "highly recommend"                                                                                                                                                               |
|                                                                                                                                                                                          |
| *   **Enthusiastic Language:** The use of exclamation marks ("!") further amplifies the positive tone, indicating excitement and strong approval.                                        |
|                                                                                                                                                                                          |
| *   **Lack of Negative or Neutral Elements:** There are no words, phrases, or implications that suggest any dissatisfaction, criticism, or even indifference.                            |
|                                                                                                                                                                                          |
| In summary, the author's language is enthusiastic and uses multiple strong positive descriptors, leaving no room for doubt that their opinion of the product is overwhelmingly positive. |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

8. Effectuer un nettoyage

Pour éviter que votre compte Google Cloud ne soit facturé en permanence, supprimez les ressources créées lors de cet atelier de programmation.

Supprimer l'ensemble de données et la connexion

Exécutez la commande suivante dans Cloud Shell pour supprimer vos ensembles de données et votre connexion :

bq rm -r -f --location=$REGION iceberg_ai
bq rm -r -f --location=US iceberg_object_ai
bq rm --connection $PROJECT_ID.US.my-conn

Supprimer les buckets GCS et les fichiers locaux

Nettoyez les buckets GCS et les fichiers locaux :

# Delete GCS buckets
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
gcloud storage rm -r gs://dataproc-temp-${REGION}-${PROJECT_NUMBER}-*
gcloud storage rm -r gs://dataproc-staging-${REGION}-${PROJECT_NUMBER}-*
gcloud storage rm -r gs://${DEPS_BUCKET}

# Delete local files
rm -f train_model.sql predict_fare.sql create_object_table.sql query_images.sql sentiment_analysis.sql test.py query.py history.py timetravel.py review.txt

Vous pouvez également supprimer l'intégralité du projet si vous l'avez créé uniquement pour cet atelier.

9. Félicitations

Félicitations ! Vous avez créé un data lakehouse unifié à l'aide d'Apache Iceberg, de BigLake et de BigQuery AI.

Connaissances acquises

  • Comment se connecter aux catalogues REST publics Apache Iceberg et les interroger
  • Utiliser la fonctionnalité temporelle dans Iceberg pour auditer les versions d'ensemble de données
  • Entraîner des modèles BigQuery ML sur des données structurées
  • Connecter des données non structurées (images) à l'aide de tables d'objets et d'ObjectRef
  • Utiliser Gemini directement dans BigQuery SQL pour analyser des images

Documents de référence