Compartir a través de


Realizar el ajuste de hiperparámetros en Fabric (versión preliminar)

El ajuste de hiperparámetros es el proceso de búsqueda de los valores óptimos para los parámetros de un modelo de aprendizaje automático que afectan a su rendimiento. Puede ser difícil y lento, especialmente cuando se trabaja con modelos complejos y grandes conjuntos de datos. En este artículo, le mostraremos cómo realizar el ajuste de hiperparámetros en Fabric.

En este tutorial, usaremos el conjunto de datos de vivienda de California, que contiene información sobre el valor medio de la casa y otras características para diferentes bloques del censo en California. Una vez creados los datos, entrenaremos un modelo LightGBM de SynapseML para predecir el valor de la casa en función de las características. A continuación, usaremos FLAML, una biblioteca AutoML rápida y ligera, para encontrar los mejores hiperparámetros para el modelo LightGBM. Por último, compararemos los resultados del modelo optimizado con el modelo de línea base que usa los parámetros predeterminados.

Importante

Esta característica se encuentra en versión preliminar.

Requisitos previos

  • Cree un nuevo entorno de Fabric o asegúrese de que se ejecuta en Fabric Runtime 1.2 (Spark 3.4 o superior y Delta 2.4)
  • Creación de un cuaderno nuevo.
  • Adjunte el cuaderno a un almacén de lago. En la parte izquierda de su cuaderno, seleccione Añadir para añadir una casa lacustre existente o crear una nueva.

Preparación de conjuntos de datos de entrenamiento y prueba

En esta sección, preparamos los conjuntos de datos de entrenamiento y prueba para el modelo LightGBM. Usamos el conjunto de datos de viviendas de California de Sklearn. Creamos una trama de datos (dataframe) de Spark a partir de los datos y usamos un VectorAssembler para combinar las características en una sola columna vectorial.

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)

Después, dividimos aleatoriamente los datos en tres subconjuntos: entrenamiento, validación y prueba, con el 85 %, el 12,75 % y el 2,25 % de los datos, respectivamente. Usamos los conjuntos de entrenamiento y validación para el ajuste de hiperparámetros y el conjunto de pruebas para la evaluación del modelo.

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)

Configuración del experimento ML

Configurar MLflow

Antes de realizar el ajuste de hiperparámetros, es necesario definir una función de entrenamiento que pueda tomar valores diferentes de hiperparámetros y entrenar un modelo LightGBM en los datos de entrenamiento. También es necesario evaluar el rendimiento del modelo en los datos de validación mediante la puntuación de R2, que mide el nivel de adecuación del modelo a los datos.

Para ello, primero se importan los módulos necesarios y se configura el experimento de MLflow. MLflow es una plataforma de código abierto para administrar el ciclo de vida completo del aprendizaje automático. Nos ayuda a realizar un seguimiento y comparar los resultados de diferentes modelos e hiperparámetros.

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

Establecimiento del nivel de registro

Aquí, se configura el nivel de registro para suprimir la salida innecesaria de la biblioteca de Synapse.ml, lo que mantiene los registros más limpios.

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

Entrenar un modelo de línea base

A continuación, definimos la función de entrenamiento que toma cuatro hiperparámetros como entradas: alfa, learningRate, numLeaves y numIterations. Estos son los hiperparámetros que queremos ajustar más adelante mediante FLAML.

La función de entrenamiento también toma dos tramas de datos como entradas: train_data y val_data, que son los conjuntos de datos de entrenamiento y validación, respectivamente. La función de entrenamiento devuelve dos salidas: el modelo entrenado y la puntuación de R2 en los datos de validación.

# 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

Por último, usamos la función de entrenamiento para entrenar un modelo de línea base con los valores predeterminados de los hiperparámetros. También se evalúa el modelo de línea base en los datos de prueba y se imprime la puntuación de 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)

Ajuste de hiperparámetros con FLAML

FLAML es una biblioteca de AutoML rápida y ligera que puede encontrar automáticamente los mejores hiperparámetros para un modelo y un conjunto de datos determinado. Usa una estrategia de búsqueda de bajo coste que se adapta a los comentarios de la métrica de evaluación. En esta sección, usaremos FLAML para ajustar los hiperparámetros del modelo LightGBM que definimos en la sección anterior.

Definición de la función de ajuste

Para usar FLAML, es necesario definir una función de ajuste que tome un diccionario de configuración como entrada y devuelva un diccionario con la métrica de evaluación como clave y el valor de métrica como valor.

El diccionario de configuración contiene los hiperparámetros que queremos ajustar y sus valores. La función de ajuste usará la función de entrenamiento que definimos anteriormente para entrenar y evaluar el modelo con la configuración especificada.

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

Definición del espacio de búsqueda

A continuación, es necesario definir el espacio de búsqueda de los hiperparámetros que queremos ajustar. El espacio de búsqueda es un diccionario que asigna los nombres de hiperparámetros a los intervalos de valores que queremos explorar. FLAML proporciona algunas funciones cómodas para definir diferentes tipos de rangos, como uniform, loguniform y randint.

En este caso, queremos ajustar los cuatro hiperparámetros siguientes: alpha, learningRate, numLeaves y 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),
}

Definición de la prueba de hiperparámetros

Por último, es necesario definir una prueba de hiperparámetros que usará FLAML para optimizar los hiperparámetros. Es necesario pasar la función de ajuste, el espacio de búsqueda, el presupuesto de tiempo, el número de muestras, el nombre de la métrica, el modo y el nivel de detalle a la función flaml.tune.run. También es necesario iniciar una ejecución de MLflow anidada para realizar un seguimiento de los resultados de la prueba.

flaml.tune.run function devolverá un objeto de análisis que contiene la mejor configuración y el mejor valor de métrica.

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

Una vez finalizada la prueba, podemos ver la mejor configuración y el mejor valor de métrica del objeto de análisis.

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

Comparación de los resultados

Después de encontrar los mejores hiperparámetros con FLAML, es necesario evaluar cuánto mejoran el rendimiento del modelo. Para ello, usamos la función de entrenamiento para crear un nuevo modelo con los mejores hiperparámetros del conjunto de datos de entrenamiento completo. A continuación, usamos el conjunto de datos de prueba a fin de calcular la puntuación de R2 para el nuevo modelo y el modelo de línea 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)

Guardar el modelo final

Una vez que hayamos completado la prueba de hiperparámetros, ahora podemos guardar el modelo final optimizado como un modelo de ML en 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.")