Partager via


Effectuer le réglage des hyperparamètres dans Fabric (aperçu)

Le réglage des hyperparamètres est le processus qui consiste à trouver les valeurs optimales des paramètres d’un modèle Machine Learning qui affectent ses performances. Il peut s’avérer difficile et chronophage, en particulier lorsqu’il s’agit de modèles complexes et de jeux de données volumineux. Dans cet article, nous allons vous montrer comment effectuer le réglage des hyperparamètres dans Fabric.

Dans ce tutoriel, nous utiliserons le jeu de données sur le logement en Californie, qui contient des informations sur la valeur médiane des maisons et d’autres caractéristiques pour différents blocs de recensement en Californie. Une fois les données préparées, nous entraînerons un modèle SynapseML LightGBM pour prédire la valeur de la maison en fonction des caractéristiques. Nous utiliserons ensuite FLAML, une bibliothèque AutoML rapide et légère, pour trouver les meilleurs hyperparamètres pour le modèle LightGBM. Enfin, nous comparerons les résultats du modèle ajusté avec le modèle de référence qui utilise les paramètres par défaut.

Important

Cette fonctionnalité est en préversion.

Prérequis

  • Créez un nouvel environnement Fabric ou vérifiez que vous utilisez le runtime Fabric 1.2 (Spark 3.4 (ou supérieur) et Delta 2.4)
  • Créez un nouveau bloc-notes.
  • Attachez votre notebook à lakehouse. Sur le côté gauche de votre bloc-notes, sélectionnez Ajouter pour ajouter une maison de lac existante ou en créer une nouvelle.

Préparation de jeux de données d’apprentissage et de test

Dans cette section, nous préparons les jeux de données d’entraînement et de test pour le modèle LightGBM. Nous utilisons le jeu de données sur les logements californiens de Sklearn. Nous créons un dataframe Spark à partir des données et utilisons un VectorAssembler pour combiner les caractéristiques en une seule colonne de vecteurs.

from sklearn.datasets import fetch_california_housing
from pyspark.sql import SparkSession

# Load the Scikit-learn California Housing dataset
sklearn_dataset = fetch_california_housing()

# Convert the Scikit-learn dataset to a Pandas DataFrame
import pandas as pd
pandas_df = pd.DataFrame(sklearn_dataset.data, columns=sklearn_dataset.feature_names)
pandas_df['target'] = sklearn_dataset.target

# Create a Spark DataFrame from the Pandas DataFrame
spark_df = spark.createDataFrame(pandas_df)

# Display the data
display(spark_df)

Nous divisons ensuite aléatoirement les données en trois sous-ensembles : entraînement, validation et test, avec respectivement 85 %, 12,75 % et 2,25 % des données. Nous utilisons les jeux d’entraînement et de validation pour le réglage des hyperparamètres et le jeu de test pour l’évaluation du modèle.

from pyspark.ml.feature import VectorAssembler

# Combine features into a single vector column
featurizer = VectorAssembler(inputCols=sklearn_dataset.feature_names, outputCol="features")
data = featurizer.transform(spark_df)["target", "features"]

# Split the data into training, validation, and test sets
train_data, test_data = data.randomSplit([0.85, 0.15], seed=41)
train_data_sub, val_data_sub = train_data.randomSplit([0.85, 0.15], seed=41)

Configurer l’expérience ML

Configurer MLflow

Avant de procéder au réglage des hyperparamètres, nous devons définir une fonction d’entraînement qui peut prendre différentes valeurs d’hyperparamètres et entraîner un modèle LightGBM sur les données d’entraînement. Nous devons également évaluer les performances du modèle sur les données de validation à l’aide du score R2, qui mesure l’adéquation du modèle aux données.

Pour ce faire, nous importons d’abord les modules nécessaires et mettons en place l’expérience MLflow. MLflow est une plateforme open source qui permet de gérer le cycle de vie du machine learning de bout en bout. Elle nous aide à suivre et à comparer les résultats de différents modèles et hyperparamètres.

# Import MLflow and set up the experiment name
import mlflow

mlflow.set_experiment("flaml_tune_sample")

# Enable automatic logging of parameters, metrics, and models
mlflow.autolog(exclusive=False)

Définir le niveau de journalisation

Nous configurons ici le niveau de journalisation pour supprimer les sorties inutiles de la bibliothèque Synapse.ml, ce qui permet de garder les journaux plus propres.

import logging
 
logging.getLogger('synapse.ml').setLevel(logging.ERROR)

Effectuer l’entraînement d’un modèle de référence

Nous définissons ensuite la fonction d’entraînement qui prend quatre hyperparamètres en entrée : alpha, learningRate, numLeaves et numIterations. Ce sont les hyperparamètres que nous voulons régler ultérieurement à l’aide de FLAML.

La fonction d’entraînement prend également deux dataframes en entrée : train_data et val_data, qui sont respectivement les jeux de données d’entraînement et de validation. La fonction d’entraînement renvoie deux résultats : le modèle entraîné et le score R2 sur les données de validation.

# Import LightGBM and RegressionEvaluator
from synapse.ml.lightgbm import LightGBMRegressor
from pyspark.ml.evaluation import RegressionEvaluator

def train(alpha, learningRate, numLeaves, numIterations, train_data=train_data_sub, val_data=val_data_sub):
    """
    This train() function:
     - takes hyperparameters as inputs (for tuning later)
     - returns the R2 score on the validation dataset

    Wrapping code as a function makes it easier to reuse the code later for tuning.
    """
    with mlflow.start_run() as run:

        # Capture run_id for prediction later
        run_details = run.info.run_id

        # Create a LightGBM regressor with the given hyperparameters and target column
        lgr = LightGBMRegressor(
            objective="quantile",
            alpha=alpha,
            learningRate=learningRate,
            numLeaves=numLeaves,
            labelCol="target",
            numIterations=numIterations,
            dataTransferMode="bulk"
        )

        # Train the model on the training data
        model = lgr.fit(train_data)

        # Make predictions on the validation data
        predictions = model.transform(val_data)
        # Define an evaluator with R2 metric and target column
        evaluator = RegressionEvaluator(predictionCol="prediction", labelCol="target", metricName="r2")
        # Compute the R2 score on the validation data
        eval_metric = evaluator.evaluate(predictions)

        mlflow.log_metric("r2_score", eval_metric)

    # Return the model and the R2 score
    return model, eval_metric, run_details

Enfin, nous utilisons la fonction d’entraînement pour entraîner un modèle de référence avec les valeurs par défaut des hyperparamètres. Nous évaluons également le modèle de référence sur les données de test et imprimons le score R2.

# Train the baseline model with the default hyperparameters
init_model, init_eval_metric, init_run_id = train(alpha=0.2, learningRate=0.3, numLeaves=31, numIterations=100, train_data=train_data, val_data=test_data)
# Print the R2 score of the baseline model on the test data
print("R2 of initial model on test dataset is: ", init_eval_metric)

Effectuer le réglage des hyperparamètres avec FLAML

FLAML est une bibliothèque AutoML rapide et légère qui permet de trouver automatiquement les meilleurs hyperparamètres pour un modèle et un jeu de données donnés. Elle utilise une stratégie de recherche peu coûteuse qui s’adapte au retour d’information de la métrique d’évaluation. Dans cette section, nous allons utiliser FLAML pour ajuster les hyperparamètres du modèle LightGBM que nous avons défini dans la section précédente.

Définir la fonction de réglage

Pour utiliser FLAML, nous devons définir une fonction de réglage qui prend un dictionnaire de configuration en entrée et renvoie un dictionnaire avec la métrique d’évaluation comme clé et la valeur de la métrique comme valeur.

Le dictionnaire de configuration contient les hyperparamètres que nous voulons régler et leurs valeurs. La fonction de réglage utilisera la fonction d’entraînement que nous avons définie plus tôt pour entraîner et évaluer le modèle avec la configuration donnée.

# Import FLAML
import flaml

# Define the tune function
def flaml_tune(config):
    # Train and evaluate the model with the given config
    _, metric, run_id = train(**config)
    # Return the evaluation metric and its value
    return {"r2": metric}

Définir l’espace de recherche

Nous devons ensuite définir l’espace de recherche pour les hyperparamètres que nous voulons régler. L’espace de recherche est un dictionnaire qui associe les noms des hyperparamètres aux plages de valeurs que nous voulons explorer. FLAML fournit des fonctions pratiques pour définir différents types d’intervalles, tels que uniform, loguniform et randint.

Dans ce cas, nous voulons régler les quatre hyperparamètres suivants : alpha, learningRate, numLeaves et numIterations.

# Define the search space
params = {
    # Alpha is a continuous value between 0 and 1
    "alpha": flaml.tune.uniform(0, 1),
    # Learning rate is a continuous value between 0.001 and 1
    "learningRate": flaml.tune.uniform(0.001, 1),
    # Number of leaves is an integer value between 30 and 100
    "numLeaves": flaml.tune.randint(30, 100),
    # Number of iterations is an integer value between 100 and 300
    "numIterations": flaml.tune.randint(100, 300),
}

Définir l’essai d’hyperparamètre

Enfin, nous devons définir un essai d’hyperparamètre qui utilisera FLAML pour optimiser les hyperparamètres. Nous devons passer la fonction de réglage, l’espace de recherche, le budget temps, le nombre d’échantillons, le nom de la métrique, le mode et le niveau de verbosité à la fonction flaml.tune.run. Nous devons également lancer une exécution MLflow imbriquée pour suivre les résultats de l’essai.

La fonction flaml.tune.run function retournera un objet d’analyse contenant la meilleure configuration et la meilleure valeur de métrique.

# Start a nested MLflow run
with mlflow.start_run(nested=True, run_name="Child Run: "):
    # Run the hyperparameter trial with FLAML
    analysis = flaml.tune.run(
        # Pass the tune function
        flaml_tune,
        # Pass the search space
        params,
        # Set the time budget to 120 seconds
        time_budget_s=120,
        # Set the number of samples to 100
        num_samples=100,
        # Set the metric name to r2
        metric="r2",
        # Set the mode to max (we want to maximize the r2 score)
        mode="max",
        # Set the verbosity level to 5
        verbose=5,
        )

Une fois l’essai terminé, nous pouvons visualiser la meilleure configuration et la meilleure valeur de métrique à partir de l’objet d’analyse.

# Get the best config from the analysis object
flaml_config = analysis.best_config
# Print the best config
print("Best config: ", flaml_config)
print("Best score on validation data: ", analysis.best_result["r2"])

Comparer les résultats

Après avoir trouvé les meilleurs hyperparamètres avec FLAML, nous devons évaluer dans quelle mesure ils améliorent les performances du modèle. Pour ce faire, nous utilisons la fonction d’entraînement pour créer un nouveau modèle avec les meilleurs hyperparamètres sur le jeu de données d’entraînement complet. Nous utilisons ensuite le jeu de données de test pour calculer le score R2 du nouveau modèle et du modèle de référence.

# Train a new model with the best hyperparameters 
flaml_model, flaml_metric, flaml_run_id = train(train_data=train_data, val_data=test_data, **flaml_config)

# Print the R2 score of the baseline model on the test dataset
print("On the test dataset, the initial (untuned) model achieved R^2: ", init_eval_metric)
# Print the R2 score of the new model on the test dataset
print("On the test dataset, the final flaml (tuned) model achieved R^2: ", flaml_metric)

Enregistrer le modèle final

Une fois que nous avons terminé notre essai d’hyperparamètres, nous pouvons maintenant enregistrer le modèle final réglé en tant que modèle ML dans Fabric.

# Specify the model name and the path where you want to save it in the registry
model_name = "housing_model"  # Replace with your desired model name
model_path = f"runs:/{flaml_run_id}/model"

# Register the model to the MLflow registry
registered_model = mlflow.register_model(model_uri=model_path, name=model_name)

# Print the registered model's name and version
print(f"Model '{registered_model.name}' version {registered_model.version} registered successfully.")