Accéder au contenu principal

Snowflake Snowpark : introduction complète

Faites vos premiers pas vers la maîtrise du machine learning in-database avec Snowflake Snowpark.
Actualisé 19 sept. 2026  · 15 min lire

Explorer avec l’IA

ChatGPTClaudePerplexity

Qu’est-ce que Snowflake Snowpark ?

Le machine learning traditionnel consiste à déplacer les données depuis les bases de données vers l’endroit où se trouvent les modèles. Avec l’essor récent de l’IA et la taille monumentale des jeux de données actuels, cette approche devient de moins en moins praticable.

Des téraoctets de données doivent être transférés de la base de données vers des applications côté client pour le nettoyage, l’analyse et l’entraînement des modèles. Cet aller-retour en apparence anodin gaspille des ressources précieuses. C’est pourquoi de plus en plus d’entreprises adoptent des technologies in-database pour limiter les mouvements de données et exécuter leurs opérations en douceur.

Parmi les meilleures technologies in-database du marché, on retrouve Snowpark, proposé par Snowflake Cloud. Snowpark est un ensemble de bibliothèques et d’environnements d’exécution qui vous permettent d’exécuter en toute sécurité des langages de programmation dans Snowflake Cloud pour développer des pipelines de données et des modèles de machine learning dans le même environnement que vos bases Snowflake.

Dans ce tutoriel, nous passerons en revue les fondamentaux de Snowpark et comment l’utiliser dans vos projets. Nous partons du principe que vous maîtrisez déjà SQL et Snowflake – si besoin, suivez notre parcours de compétences SQL Fundamentals ou lisez ce tutoriel Snowflake pour débutants.

C’est parti !

Pourquoi Snowflake Snowpark ?

Snowflake Snowpark est un ensemble de bibliothèques et de runtimes qui vous permet d’utiliser en toute sécurité des langages comme Python, Java et Scala pour traiter les données directement au sein de la plateforme cloud de Snowflake.

Cela évite de sortir les données de Snowflake pour les traiter, ce qui améliore l’efficacité et la sécurité. Parmi ses atouts principaux :

  • Traitez les données dans Snowflake : Écrivez du code dans votre langage de prédilection pour manipuler et analyser vos bases SQL dans Snowflake et exécutez-le dans les environnements sécurisés de Snowflake. Plus besoin de déplacer les données ailleurs.
  • Meilleures performances : En traitant les données directement dans Snowflake, Snowpark exploite l’architecture élastique et serverless de la plateforme pour un traitement efficace.
  • Coûts et charge technologique réduits : Puisque Snowflake fournit l’essentiel des ressources, vous n’avez pas à gérer des plateformes séparées pour le calcul et le stockage.
  • Travaillez avec vos outils, où vous voulez : Les API de Snowpark vous permettent de vous connecter à vos bases SQL depuis n’importe quel environnement, comme Jupyter ou VSCode, pour construire des pipelines et des applications de ML. Et surtout, vous pouvez utiliser vos bibliothèques favorites (Pandas, Scikit-learn, XGBoost, etc.) aux côtés des frameworks Snowpark.

En bref, Snowpark offre aux développeurs un moyen à la fois puissant et simple de créer des pipelines de données, des solutions de machine learning et des applications basées sur les données directement dans Snowflake Cloud.

Premiers pas avec Snowpark

Notre objectif final dans ce tutoriel est d’entraîner un modèle dont les hyperparamètres sont optimisés sur une table d’une base Snowflake, en utilisant Snowpark. Pour cela, nous allons :

  1. Créer un environnement virtuel avec les bibliothèques nécessaires.
  2. Ingestion de données d’exemple dans Snowflake.
  3. Créer une session Snowpark pour se connecter à Snowflake.
  4. Charger les données ingérées dans la session.

Création d’un environnement virtuel

Pour ce tutoriel, nous allons utiliser un nouvel environnement conda :

$ conda create -n snowpark python==3.10 -y
$ conda activate snowpark

Remarque : si vous utilisez une installation toute neuve de conda, vous devrez exécuter $conda init avant d’activer l’environnement snowpark.

Après activation, installez les bibliothèques suivantes :

$ pip install snowflake-snowpark-python #The Snowpark API
$ pip install pandas pyarrow numpy matplotlib seaborn

Si vous comptez utiliser Jupyter, installez aussi ipykernel et exécutez la commande suivante pour ajouter l’environnement comme noyau Jupyter :

$ pip install ipykernel
$ ipython kernel install --user --name=snowpark

Importons maintenant quelques bibliothèques générales utiles pour la suite :

import warnings
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
warnings.filterwarnings("ignore")

Ingérer des données d’exemple dans Snowflake

Pour ce tutoriel, nous utiliserons le jeu de données Diamonds de Seaborn – vous pouvez télécharger le fichier CSV directement depuis mon GitHub.

Après vous être connecté à l’application Snowflake, suivez le GIF ci-dessous pour ingérer le jeu de données dans une nouvelle table (créez un compte si vous n’en avez pas encore un) :

snowflake_dashboard.gif

En pratique, vous importerez rarement des fichiers CSV comme tables dans vos bases Snowflake. Le plus souvent, vous travaillerez sur des bases existantes avec des niveaux d’accès définis par les administrateurs de votre entreprise.

Créer une session Snowpark

Nous devons maintenant établir une connexion entre l’API de Snowpark et le cloud Snowflake pour interroger notre base. Cette connexion requiert les identifiants Snowflake suivants : nom de compte, nom d’utilisateur et mot de passe.

L’image ci-dessous montre comment récupérer votre nom d’utilisateur et le nom de votre compte depuis le tableau de bord Snowflake :

image.png

Créez un fichier séparé nommé config.py contenant un unique dictionnaire credentials au format suivant :

credentials = (
   {
       "account": "3-4",  # Combine 3 and 4 with a hyphen
       "username": "bexgboost",  # Your username in lowercase
       "password": "your_password",  # Your Snowflake password
   },
)

Pour des raisons de sécurité, nous stockons les identifiants dans un fichier à part. L’ajouter à .gitignore évite toute fuite accidentelle de vos identifiants Snowflake.

Importons maintenant le dictionnaire credentials et la classe Session de Snowpark :

from config import credentials
from snowflake.snowpark import Session

Pour établir la connexion, nous utiliserons la méthode Session.builder.configs.create() :

connection_parameters = {
   "account": credentials["account"],
   "user": credentials["username"],
   "password": credentials["password"],
}

new_session = Session.builder.configs(connection_parameters).create()
new_session.get_current_user()
'"bexgboost"'

Si votre nom d’utilisateur s’affiche, votre première session est créée avec succès !

Charger les données ingérées dans une session

L’objet new_session a accès à tout ce que votre compte possède dans Snowflake (et à ce à quoi vous avez été autorisé).

Avant d’importer des données, indiquons à la session que nous utilisons la base test_db créée plus haut :

new_session.sql("USE DATABASE test_db;").collect()
[Row(status='Statement executed successfully.')]

Nous utilisons la méthode .sql, qui permet d’exécuter toute requête compatible SQL Snowflake.

Nous pouvons maintenant charger la table diamonds de test_db via la méthode table de l’objet session :

diamonds_df = new_session.table("diamonds")
diamonds_df.show(5)

Top five rows of the diamonds dataset printed from Snowpark dataframes.

La méthode .show() équivaut à DataFrame.describe() de Pandas et nous confirme que la connexion à la table a réussi.

Précision : nous ne faisons que nous connecter à la table diamonds. L’objet diamonds_df ne contient pas les données, comme le montre sa taille :

import sys
sys.getsizeof(diamonds_df)
48

Il ne pèse que 48 octets alors qu’il devrait faire plus de 3 Mo. Prenons un instant pour comprendre pourquoi.

Comprendre les DataFrames Snowpark

Jusqu’ici, nous avons :

  1. Créé un environnement virtuel avec les bonnes bibliothèques.
  2. Ingéré des données d’exemple dans Snowflake.
  3. Créé une session Snowpark pour nous connecter à Snowflake.

Passons maintenant aux DataFrames Snowpark !

Les DataFrames Snowpark sont paresseux (lazy)

Les DataFrames Pandas utilisent votre RAM : ils vivent sur votre machine. À l’inverse, les DataFrames Snowpark résident sur la plateforme cloud de Snowflake, même si vous en voyez la représentation dans votre environnement de code. Les données restent dans le cloud et n’ont pas besoin d’être téléchargées en local pour opérer dessus.

Autre point clé : les DataFrames Snowpark fonctionnent en évaluation paresseuse. Autrement dit, ils n’exécutent rien tant que vous ne le leur demandez pas explicitement.

Ils construisent plutôt une représentation logique des opérations souhaitées, ensuite traduites en requêtes SQL optimisées pour exécution par Snowflake.

Pandas, lui, exécute immédiatement les opérations : on parle d’exécution éager.

Côté performances, l’évaluation paresseuse est très largement plus rapide que l’exécution éager. Les DataFrames lazy s’appuient sur la puissance élastique de Snowflake pour pousser les opérations au niveau de la base, et aller beaucoup plus vite.

Pour mieux saisir la différence, imaginez que nos données dans Snowflake soient une immense bibliothèque. Deux scénarios :

  • Exécution éager (Pandas) : on amène tous les livres à notre bureau (machine locale) pour les analyser un par un.
  • Évaluation paresseuse (Snowpark) : on dit au bibliothécaire (Snowpark) quels livres et quelle analyse on veut, et il récupère puis analyse efficacement l’information pertinente.

Quand utiliser les DataFrames Snowpark

Puisque Snowpark peut se convertir en DataFrame Pandas, quand choisir l’un plutôt que l’autre ?

pandas_diamonds = diamonds_df.to_pandas()
pandas_diamonds.head()

Screenshot 2024-05-03 at 14.18.05.png

La réponse dépend surtout de la taille du jeu de données.

Si votre jeu est petit (comme Diamonds), vous pouvez le télécharger en local et l’utiliser dans Pandas sans souci (c’est ce que fait DataFrame.to_pandas()).

Mais avec les volumes actuels, tout peut vite déraper et vous attendrez des heures pour rapatrier la base. Et souvenez-vous : c’est un aller-retour – toute nouvelle donnée à conserver devra repartir. C’est là que maîtriser l’API Snowpark DataFrame prend tout son sens.

Vous pouvez également exécuter toute expression SQL compatible Snowflake via Session.sql() (voir ci-dessous). Mais je vous recommande vivement d’apprendre l’API DataFrame de Snowpark, qui propose plus d’100 fonctions et expressions SQL préintégrées.

result = new_session.sql(
   """
   SELECT PRICE, CUT FROM DIAMONDS LIMIT 10
"""
)

type(result)
snowflake.snowpark.dataframe.DataFrame
result.show()

A result of an sql expression in Snowpark

Fonctions de transformation des DataFrames Snowpark

Vous passerez beaucoup de temps avec les fonctions de transformation. Elles se trouvent dans le sous-module suivant :

# Par convention, on l'importe sous le nom F
import snowflake.snowpark.functions as F

Toutes les fonctions de F (listez-les via dir(F)) décrivent comment transformer un DataFrame. Exemple :

F.upper(F.col("CUT"))

Ici, la fonction col crée une référence à la colonne et retourne un objet Column. Puis .upper() met les valeurs de CUT en majuscules :

diamonds_df.show(5)

Top five rows of the diamonds dataset after a failed Snowpark transformation.

Pourtant, les valeurs de CUT comportent encore des minuscules. Pourquoi ? Eh bien, F.upper(F.col("CUT")) équivaut à la requête SQL suivante :

SELECT UPPER(CUT);

Que manque-t-il ?

Bien sûr, la clause FROM qui indique la table sur laquelle appliquer la transformation !

Nous devons donc associer l’expression F.upper(F.col("CUT")) à l’objet diamonds_df. Trois approches principales :

  • Pour transformer ou sélectionner des colonnes : passer l’expression dans .select()
  • Pour sélectionner des lignes : passer l’expression dans .filter()
  • Pour créer des colonnes : passer l’expression dans .with_column()

Notre expression transformant une colonne, passons-la à .select() :

our_expression = F.upper(F.col("CUT"))
diamonds_df.select(our_expression).show()

Top five rows of the diamonds dataset after a failed Snowpark transformation.

Changement réussi !

Créons maintenant une expression pour filtrer des lignes :

diamonds_df.filter(F.col("PRICE") > 10000).count()
5222

On trouve plus de 5 000 diamants à plus de 10 000 $ dans le jeu de données. Tentons aussi la création d’une colonne :

(
diamonds_df.with_column("PRICE_SQUARED", F.col("PRICE") ** 2)
.select("PRICE", "PRICE_SQUARED")
.show(5)
)

The result of a Snowpark transformation that uses the `.with_column` function

Ci-dessus, nous montrons aussi que l’enchaînement de méthodes est possible : une nouvelle colonne « PRICE_SQUARED » a été ajoutée. Les fonctions des DataFrames Snowpark sont proches de celles de Pandas.

En résumé, si vous cherchez l’équivalent Snowpark d’une fonction Pandas, elle se trouve soit dans F, soit parmi les méthodes des DataFrames Snowpark. Vérifiez avec dir(F) ou dir(diamonds_df) ou consultez la référence Snowpark Python.

Réaliser une EDA sur des DataFrames Snowpark

Une grande part de l’exploratory data analysis (EDA) consiste à créer des visualisations. Bonne nouvelle : pas besoin de Snowpark pour ça, nous pouvons rester sur la stack data moderne.

L’idée : comme l’EDA vise à dégager des tendances générales, un échantillon représentatif suffit souvent. Une fois téléchargé, on le convertit en DataFrame Pandas et on utilise Matplotlib ou Seaborn.

Commençons par la conversion :

sample = diamonds_df.sample(0.25).to_pandas()
sample.head()

Screenshot 2024-05-03 at 14.19.58.png

Ici, nous téléchargeons 25 % du jeu en DataFrame Pandas. Mettons les noms de colonnes en minuscules :

sample.columns = [col.lower() for col in sample.columns]
sample.columns
Index(['carat', 'cut', 'color', 'clarity', 'depth', 'table', 'price', 'x', 'y', 'z'], dtype='object')

Vous pouvez maintenant faire votre EDA comme d’habitude. Ci-dessous, un nuage de points pour visualiser la relation entre le prix et le carat :

#Remember we imported seaborn as sns
sns.scatterplot(data=sample, x="price", y="carat", s=1)
plt.title("Diamond prices vs. carat")
plt.show()

A scatterplot of diamonds vs carat

Traçons ensuite une matrice de corrélation pour visualiser les corrélations entre variables numériques :

corr_matrix = sample.corr(numeric_only=True)
sns.heatmap(corr_matrix, center=0, square=True, annot=True
plt.title("The correlation matrix of Diamond numeric features")
plt.show()

A heatmap of a correlation matrix of the diamonds dataset

On peut aussi créer un histogramme des catégories de taille (cut) :

cut_counts = sample.groupby("cut")["cut"].count()
cut_counts.plot(kind="bar")
plt.title("Category counts of diamond cuts")
plt.xlabel("Diamond cut")
plt.show()

A countplot of diamond cuts.

Pour vérifier que ces graphiques seraient similaires sur l’ensemble du jeu de données, exécutons la version SQL du diagramme en barres ci-dessus.

Commençons par une expression SQL qui regroupe le jeu Diamonds par CUT :

result = new_session.sql(
   """
   SELECT cut, COUNT(*) AS count
     FROM diamonds
    GROUP BY cut;
"""
)

Convertissons et téléchargeons ensuite le résultat en DataFrame Pandas :

result_pd = result.to_pandas()
result_pd

Part of  a diamonds dataset

Cette fois, utilisons plt.bar() pour tracer le graphique :

plt.bar(x=result_pd["CUT"], height=result_pd["COUNT"])
plt.show()

A countplot of diamond cuts again

L’ordre des barres diffère, mais côte à côte, on lit la même information.

Entraîner un modèle de machine learning dans Snowpark

Nous sommes prêts à entraîner des modèles de machine learning dans Snowpark !

Nous allons commencer par nettoyer le jeu de données.

Nettoyer les données dans Snowpark

Vérifions d’abord que tous les types de colonnes sont corrects :

list(diamonds_df.schema)
[StructField('CARAT', DecimalType(38, 2), nullable=True),
StructField('CUT', StringType(16777216), nullable=True),
StructField('COLOR', StringType(16777216), nullable=True),
StructField('CLARITY', StringType(16777216), nullable=True),
StructField('DEPTH', DecimalType(38, 1), nullable=True),
StructField('"table"', DecimalType(38, 1), nullable=True),
StructField('PRICE', LongType(), nullable=True),
StructField('X', DecimalType(38, 2), nullable=True),
StructField('Y', DecimalType(38, 2), nullable=True),
StructField('Z', DecimalType(38, 2), nullable=True)]

La colonne table a un nom ambigu, renommons-la en TABLE_ (avec un tiret bas final pour éviter tout conflit avec le mot-clé SQL) :

diamonds_df = diamonds_df.with_column_renamed('"table"', "TABLE_")
diamonds_df.columns
['CARAT', 'CUT', 'COLOR', 'CLARITY', 'DEPTH', 'TABLE_', 'PRICE', 'X', 'Y', 'Z']

Nous devons convertir les caractéristiques numériques de DecimalType en DoubleType, car les décimaux ne sont pas encore pris en charge dans Snowpark.

from snowflake.snowpark.types import DoubleType

numeric_features = ["CARAT", "X", "Y", "Z", "DEPTH", "TABLE_"]
for col in numeric_features:
   diamonds_df = diamonds_df.with_column(col, diamonds_df[col].cast(DoubleType()))

list(diamonds_df.select(*numeric_features))
[Column("CARAT"),
Column("X"),
Column("Y"),
Column("Z"),
Column("DEPTH"),
Column("TABLE_")]

Dans l’extrait ci-dessus, nous réutilisons .with_column(). Autre méthode nouvelle : .cast(), une méthode des DataFrames Snowpark.

Snowpark exige également que toutes les valeurs textuelles soient en majuscules et sans espaces avant l’encodage. Actuellement, les valeurs de CUT ne respectent pas cette règle ; corrigeons-les par transformations :

import snowflake.snowpark.functions as F

def remove_space_and_upper(df):
   df = df.with_column("CUT", F.upper(F.replace(F.col("CUT"), " ", "_")))

   return df

diamonds_df = remove_space_and_upper(diamonds_df)

Nos données sont désormais propres. Nous pouvons les enregistrer dans une nouvelle table Snowflake :

diamonds_df.write.mode("overwrite").save_as_table("diamonds_cleaned")

Pensez à utiliser le mode overwrite : vous devrez peut-être ajuster le nettoyage et écraser la table.

Préparer les données pour le modélisation dans Snowpark

Dans cette section, nous gérons les derniers pré-traitements susceptibles d’empêcher l’entraînement. Chargeons la table nettoyée :

clean_df = new_session.table("diamonds_cleaned")
clean_df.show()

Top five rows of the cleaned diamonds dataset

Parfait !

Importons ensuite les sous-modules preprocessing et pipeline de l’espace de noms snowflake.ml.

import snowflake.ml.modeling.preprocessing as snowml
from snowflake.ml.modeling.pipeline import Pipeline

L’API DataFrame de Snowpark est sous snowflake.snowpark, tandis que les modules ML sont sous snowflake.ml. Cela peut dérouter au début : prenez le temps de vous y habituer.

Dans le code ci-dessous, nous :

  • Listons les variables catégorielles à encoder.
  • Définissons les noms de sortie, car Snowpark ajoute les variables encodées comme nouvelles colonnes (sans remplacer les anciennes).
  • Définissons l’ordre des catégories ordinales ; dans Diamonds, toutes les catégories sont ordonnées, la valeur augmente de gauche à droite.
# List all the features for processing
cat_cols = ["CUT", "COLOR", "CLARITY"]
cat_cols_encoded = ["CUT_OE", "COLOR_OE", "CLARITY_OE"]

# We already have numeric_features

# List the correct ordering of categorical features
categories = {
   "CUT": np.array(["IDEAL", "PREMIUM", "VERY_GOOD", "GOOD", "FAIR"]),
   "CLARITY": np.array(
       ["IF", "VVS1", "VVS2", "VS1", "VS2", "SI1", "SI2", "I1", "I2", "I3"]
   ),
   "COLOR": np.array(["D", "E", "F", "G", "H", "I", "J"]),
}

Nous pouvons maintenant construire un pipeline similaire à celui de Scikit-learn :

# Build the pipeline
preprocessing_pipeline = Pipeline(
   steps=[
       (
           "OE",
           snowml.OrdinalEncoder(
               input_cols=cat_cols, output_cols=cat_cols_encoded, categories=categories
           ),
       ),
       (
           "SS",
           snowml.StandardScaler(
               input_cols=numeric_features, output_cols=numeric_features
           ),
       ),
   ]
)

La classe Pipeline accepte une liste d’étapes, chacune étant une classe de pré-traitement. Ici, nous utilisons OrdinalEncoder pour encoder les catégorielles en 0, 1, 3, etc., et StandardScaler pour normaliser les variables numériques.

Enfin, nous enregistrons le pipeline en local avec joblib et le testons sur l’ensemble du jeu :

import joblib
PIPELINE_FILE = "pipeline.joblib"

# Pickle locally first
joblib.dump(preprocessing_pipeline, PIPELINE_FILE)

transformed_diamonds_df = preprocessing_pipeline.fit(clean_df).transform(clean_df)

# transformed_diamonds_df.show() - commented because of long output

Le code s’exécute sans erreur : notre pipeline fonctionne, nous pouvons passer à l’entraînement.

Entraîner un modèle XGBoost dans Snowpark

Pour entraîner le modèle, listons à nouveau les variables et la cible :

# Define the columns again
cat_cols_encoded = ["CUT_OE", "COLOR_OE", "CLARITY_OE"]
numeric_features = ["CARAT", "X", "Y", "Z", "DEPTH", "TABLE_"]

label_cols = ["PRICE"]  # Must be a list
output_cols = ["PRICE_PREDICTED"]  # Required in snowpark

Nous allons ensuite scinder les données avec DataFrame.random_split() en précisant les poids d’entraînement et de test :

# Split the data
train_df, test_df = diamonds_df.random_split(weights=[0.8, 0.2], seed=42)

Puis nous chargeons le pipeline et l’appliquons aux ensembles d’entraînement et de test.

# Load the pre-processing pipeline locally
pipeline = joblib.load("pipeline.joblib")

# Apply it to both dataframes
train_df_transformed = pipeline.fit(train_df).transform(train_df)
test_df_transformed = pipeline.transform(test_df)

Assurez-vous de n’appeler que .transform() sur le jeu de test pour éviter toute fuite de données.

Initialisons ensuite un régresseur XGBoost depuis le sous-module ml.modeling.xgboost :

# Snowpark has models from scikit-learn and lightgbm too
from snowflake.ml.modeling.xgboost import XGBRegressor

# Initialize
regressor = XGBRegressor(
   input_cols=cat_cols_encoded + numeric_features,
   label_cols=label_cols,
   output_cols=output_cols,
)

La classe XGBRegressor requiert les noms de toutes les entrées, de la cible et de la sortie. Une fois définis, appelez .fit() pour l’entraînement :

# Train
regressor.fit(train_df_transformed)

L’entraînement peut être lent avec un compte Snowflake gratuit, limité en ressources de calcul et sans GPU.

Une fois l’entraînement terminé, générons des prédictions :

# Predict

train_preds = regressor.predict(train_df_transformed)
test_preds = regressor.predict(test_df_transformed)

Jetons un coup d’œil aux prédictions :

train_preds.select("PRICE", "PRICE_PREDICTED").show(5)

Generated predictions from XGBoost in Snowpark

Nous devons maintenant mesurer la performance initiale. Snowpark inclut des dizaines de métriques de Scikit-learn dans son sous-module ml.modeling.metrics, que nous importons sous M :

import snowflake.ml.modeling.metrics as M

Nous appelons ensuite deux fois mean_squared_error pour obtenir la racine de l’erreur quadratique moyenne (RMSE) sur train et test (squared=False renvoie la RMSE et non la MSE) :

rmse_train = M.mean_squared_error(
   df=train_preds,
   y_true_col_names=label_cols,
   y_pred_col_names=output_cols,
   squared=False,
)

rmse_test = M.mean_squared_error(
   df=test_preds,
   y_true_col_names=label_cols,
   y_pred_col_names=output_cols,
   squared=False,
)

print(f"Train RMSE score for XGBRegressor: {rmse_train:.4f}")
print(f"Test RMSE score for XGBRegressor: {rmse_test:.4f}")
Train RMSE score for XGBRegressor: 371.9664
Test RMSE score for XGBRegressor: 542.1566

Notre modèle se trompe de 542 $ en moyenne et il sur-apprend peut-être un peu, car l’écart entre les RMSE train et test est important.

Optimisons les hyperparamètres pour corriger cela.

Optimisation des hyperparamètres dans Snowpark

Actuellement, Snowpark propose deux classes d’optimisation :

  • GridSearchCV : recherche exhaustive de toutes les combinaisons avec validation croisée.
  • RandomizedSearchCV : recherche aléatoire dans des distributions d’hyperparamètres avec validation croisée.

XGBoost comporte une douzaine d’hyperparamètres utiles (voir notre tutoriel sur XGBoost en Python). Cela nous oriente vers une recherche aléatoire. Personnellement, je préférerais Optuna (recherche bayésienne), mais Snowpark ne l’offre pas encore.

Définissons la recherche :

from snowflake.ml.modeling.model_selection import RandomizedSearchCV

rscv = RandomizedSearchCV(
   estimator=XGBRegressor(),
   param_distributions={
       "n_estimators": [2000],
       "max_depth": list(range(3, 13)),
       "learning_rate": np.linspace(0.1, 0.5, num=10),
   },
   n_jobs=-1,
   scoring="neg_mean_squared_error",
   input_cols=numeric_features + cat_cols_encoded,
   label_cols=label_cols,
   output_cols=output_cols,
   n_iter=10,
)

rscv.fit(train_df_transformed)

Nous figeons le nombre d’arbres à 2000 et ne faisons varier que max_depth et learning_rate. Le paramètre par défaut n_iter=10 signifie 10 combinaisons tirées au hasard. Pour des résultats plus fiables, augmentez cette valeur.

Mesurons maintenant la performance du meilleur modèle trouvé. Le code est identique, mais nous utilisons l’objet rscv au lieu de regressor :

# Predict
train_preds = rscv.predict(train_df_transformed)
test_preds = rscv.predict(test_df_transformed)
rmse_train = M.mean_squared_error(
   df=train_preds,
   y_true_col_names=label_cols,
   y_pred_col_names=output_cols,
   squared=False,
)

optimal_rmse_test = M.mean_squared_error(
   df=test_preds,
   y_true_col_names=label_cols,
   y_pred_col_names=output_cols,
   squared=False,
)

print(f"Train RMSE score for optimal model: {rmse_train:.4f}")
print(f"Test RMSE score for optimal model: {rmse_test:.4f}")
Train RMSE score for optimal model: 224.2405
Test RMSE score for optimal model: 572.0517

La RMSE d’entraînement diminue, mais celle de test augmente encore. Nous sur-apprenons toujours : il faudra élargir la recherche et inclure d’autres paramètres pour élaguer les arbres de décision utilisés par XGBoost.

Je vous laisse poursuivre cette partie.

Enregistrer le meilleur modèle dans Snowpark

Si nous fermons la session maintenant, nous perdrons notre modèle optimisé. Pour le sauvegarder, utilisons le registre de modèles natif de Snowpark, un espace de stockage virtuel pour conserver tout modèle et ses métadonnées.

Le registre est accessible via la classe Registry et nécessite le nom de la base et du schéma de la session courante. Il demande également un nom de projet pour éviter les conflits avec d’autres modèles :

# Set up for Registry
from snowflake.ml.registry import Registry

# Get the current db and schema name
db_name = new_session.get_current_database()
schema_name = new_session.get_current_schema()

# Define global model name for the project
model_name = "diamond_prices_regression"

# Initialize a registry to log models
registry = Registry(session=new_session, database_name=db_name, schema_name=schema_name)

Nous utiliserons Registry.log_model() pour sauvegarder nos modèles :

# Get sample data to pass into registry for schema
sample = train_df.select(cat_cols_encoded + numeric_features).limit(50)

# Log the first model
v0 = registry.log_model(
   model_name=model_name,
   version_name="v0",
   model=regressor,
   sample_input_data=sample,
)

Nous pouvons consigner une métrique avec .set_metric() (autant que nécessaire) et ajouter un commentaire descriptif :

# Add the models RMSE score
v0.set_metric(metric_name="RMSE", value=rmse_test)

# Add a description
v0.comment = "The first model to predict diamond prices"

Faisons de même pour le meilleur modèle trouvé par recherche aléatoire. N’oubliez pas d’utiliser optimal_rmse_test pour la valeur de métrique.

# Log the optimal model
v1 = registry.log_model(
   model_name=model_name,
   version_name="v1",
   model=regressor,
   sample_input_data=sample,
)

# Add the models RMSE score
v1.set_metric(metric_name="RMSE", value=optimal_rmse_test)

# Add a description
v1.comment = "Optimal model found with RandomizedSearchCV"

Pour vérifier l’enregistrement des modèles, appelez .show_models() :

# Confirm the models are added
registry.show_models()

Pour en savoir plus sur la gestion du registre, consultez cette page de la documentation développeur Snowpark.

Faire de l’inference dans Snowpark

En production, l’inference se fait généralement avec le meilleur modèle du registre. On peut le sélectionner via le résultat de .show_models() ou le récupérer directement par son tag de version comme ci-dessous :

# Doing inference with the optimal model
optimal_version = registry.get_model(model_name).version("v1")
results = optimal_version.run(test_df, function_name="predict")

results.columns

La méthode get_model() retourne un objet model générique, différent de XGBRegressor. Il faut donc appeler .run() en précisant le nom de la fonction pour effectuer l’inference.

Conclusion

Dans ce tutoriel, nous avons parcouru de bout en bout l’entraînement de modèles de machine learning dans Snowflake Snowpark. Bravo !

Nous sommes partis d’un jeu de données brut pour aboutir à un régresseur XGBoost optimisé. Nous avons tout exécuté dans Snowflake Cloud, sans mobiliser de ressources locales.

Nous avons vu l’intérêt des opérations in-database avec de grands volumes de données – et que Snowflake Snowpark compte parmi les meilleurs outils pour les mettre en œuvre.

Pour aller plus loin avec Snowpark ou Snowflake, consultez :

Merci de votre lecture !


Bexruz (Bex) Tuychiev's photo
Author
Bexruz (Bex) Tuychiev
LinkedIn

Je suis créateur de contenu en science des données avec plus de 2 ans d’expérience et l’une des plus grandes audiences sur Medium. J’aime écrire des articles détaillés sur l’IA et le ML avec une pointe de sarcasme, histoire de les rendre un peu moins austères. J’ai publié plus de 130 articles et un cours DataCamp, avec un autre en préparation. Mes contenus ont été vus par plus de 5 millions de personnes, dont 20 000 sont devenues abonnées sur Medium et LinkedIn. 

Sujets
Big Data
Intelligence artificielle

Approfondissez vos connaissances sur Snowflake et le big data !

Cours

Introduction à Snowflake SQL

2 h
56.3K
Ce cours vous emmènera de l'architecture fondamentale de Snowflake à la maîtrise des techniques avancées de SnowSQL.
Afficher les détailsRight Arrow
Commencer Le Cours
Voir plusRight Arrow