Condividi tramite


Eseguire l'ottimizzazione degli iperparametri in Fabric (anteprima)

L'ottimizzazione degli iperparametri è il processo di ricerca dei valori ottimali per i parametri di un modello di apprendimento automatico che influiscono sulle sue prestazioni. Può essere impegnativo e dispendioso in termini di tempo, soprattutto quando si gestiscono modelli complessi e set di dati di grandi dimensioni. In questo articolo verrà illustrato come eseguire l'ottimizzazione degli iperparametri in Fabric.

In questa esercitazione si userà il set di dati delle abitazioni in California, che contiene informazioni sul valore mediano di una casa e altre funzionalità per diversi blocchi di censimento in California. Una volta che i dati vengono preparati, verrà eseguito il training di un modello SynapseML LightGBM per stimare il valore della casa in base alle funzionalità. Si userà quindi FLAML, un catalogo AutoML veloce e leggero, per trovare gli iperparametri migliori per il modello LightGBM. Infine, verranno confrontati i risultati del modello ottimizzato con il modello di base che usa i parametri predefiniti.

Importante

Questa funzionalità si trova in anteprima.

Prerequisiti

  • Creare un nuovo ambiente Fabric o assicurarsi che l’esecuzione avvenga in Fabric Runtime 1.2 (Spark 3.4 (o versione successiva) e Delta 2.4)
  • Creare un nuovo notebook.
  • Collegare il notebook a un lakehouse. Sul lato sinistro del notebook, selezionare Aggiungi per aggiungere un lakehouse esistente o crearne uno nuovo.

Preparare i set di dati di training e di test

In questa sezione vengono preparati i set di dati di training e test per il modello LightGBM. Viene usato il set di dati delle abitazioni in California di Sklearn. Viene creato un dataframe Spark dai dati e viene usato un VectorAssembler per combinare le funzionalità in una sola colonna vettoriale.

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)

I dati vengono quindi suddivisi in modo casuale in tre subset: training, convalida e test, rispettivamente con 85%, 12,75% e 2,25% dei dati. Vengono usati i set di training e convalida per l'ottimizzazione degli iperparametri e il set di test per la valutazione del modello.

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)

Configurare l'esperimento di apprendimento automatico

Configurare MLflow

Prima di eseguire l'ottimizzazione degli iperparametri, è necessario definire una funzione di training in grado di accettare valori diversi di iperparametri ed eseguire il training di un modello LightGBM sui dati di training. È anche necessario valutare le prestazioni del modello sui dati di convalida usando il punteggio R2, che misura il livello di prestazioni del modello per i dati.

A tale scopo, vengono prima importati i moduli necessari e viene configurato l'esperimento MLflow. MLflow è una piattaforma open source per la gestione del ciclo di vita end-to-end di Machine Learning. Consente di monitorare e confrontare i risultati di modelli e iperparametri diversi.

# 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)

Impostare il livello di registrazione

In questo caso viene configurato il livello di registrazione per eliminare l'output non necessario dal catalogo Synapse.ml, mantenendo i log più puliti.

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

Eseguire il training di un modello di base

Successivamente, viene definita la funzione di training che accetta quattro iperparametri come input: alpha, learningRate, numLeaves e numIterations. Questi sono gli iperparametri da ottimizzare in un secondo momento usando FLAML.

La funzione di training accetta anche due dataframe come input: train_data e val_data, rispettivamente i set di dati di training e convalida. La funzione di training restituisce due output: il modello sottoposto a training e il punteggio R2 sui dati di convalida.

# 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

Si usa infine la funzione di training per eseguire il training di un modello di base con i valori predefiniti degli iperparametri. Si valuta anche il modello di base sui dati di test e si stampa il punteggio 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)

Eseguire l'ottimizzazione degli iperparametri con FLAML

FLAML è un catalogo AutoML veloce e leggero che consente di trovare automaticamente gli iperparametri migliori per un determinato modello e set di dati. Usa una strategia di ricerca a basso costo che si adatta al feedback della metrica di valutazione. In questa sezione si userà FLAML per ottimizzare gli iperparametri del modello LightGBM definito nella sezione precedente.

Definire la funzione di ottimizzazione

Per usare FLAML, è necessario definire una funzione di ottimizzazione che accetta un dizionario config come input e restituisce un dizionario con la metrica di valutazione come chiave e il valore della metrica come valore.

Il dizionario config contiene gli iperparametri da ottimizzare e i relativi valori. La funzione di ottimizzazione userà la funzione di training definita in precedenza per eseguire il training e valutare il modello con la configurazione specificata.

# 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}

Definire lo spazio di ricerca

Successivamente, è necessario definire lo spazio di ricerca per gli iperparametri da ottimizzare. Lo spazio di ricerca è un dizionario che esegue il mapping dei nomi degli iperparametri agli intervalli di valori da esplorare. FLAML offre alcune funzioni utili per definire diversi tipi di intervalli, ad esempio uniform, loguniform e randint.

In questo caso, si vogliono ottimizzare i quattro iperparametri seguenti: alpha, learningRate, numLeaves e 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),
}

Definire la versione di valutazione degli iperparametri

Infine, è necessario definire una versione di valutazione degli iperparametri che userà FLAML per ottimizzare gli iperparametri. È necessario passare la funzione di ottimizzazione, lo spazio di ricerca, il budget temporale, il numero di campioni, il nome delle metriche, la modalità e il livello di dettaglio alla funzione flaml.tune.run. È anche necessario avviare un'esecuzione MLflow annidata per tenere traccia dei risultati della versione di valutazione.

flaml.tune.run function restituisce un oggetto di analisi che contiene la configurazione e il valore della metrica migliori.

# 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,
        )

Al termine della versione di valutazione, è possibile visualizzare la configurazione e il valore della metrica migliori dall'oggetto di analisi.

# 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"])

Confrontare i risultati

Dopo aver trovato gli iperparametri migliori con FLAML, è necessario valutare quanto migliorano le prestazioni del modello. A tale scopo, viene usata la funzione di training per creare un nuovo modello con gli iperparametri migliori nel set di dati di training completo. Viene quindi usato il set di dati di test per calcolare il punteggio R2 sia per il nuovo modello che per il modello di base.

# 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)

Salvare il modello finale

Dopo aver completato la versione di valutazione degli iperparametri, è ora possibile salvare il modello finale ottimizzato come modello di apprendimento automatico in 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.")