共用方式為


在 Fabric 中執行超參數微調 (預覽)

超參數微調是針對影響其效能的機器學習模型參數尋找最佳值的程序。 它可能具有挑戰性且耗時,尤其是在處理複雜模型和大型資料集時。 在本文中,我們將會示範如何在 Fabric 中執行超參數微調。

在本教學課程中,我們將使用加州住房資料集,其中包含加州不同人口普查區塊中位數房屋值和其他特徵的相關資訊。 一旦預先準備資料,我們將定型 SynapseML LightGBM 模型,以根據特徵預測房屋值。 接下來,我們將使用快速且輕量型的 AutoML 程式庫 FLAML,以尋找 LightGBM 模型的最佳超參數。 最後,我們將會比較微調模型的結果與使用預設參數的基準模型。

重要

這項功能處於預覽狀態。

必要條件

  • 建立新的 Fabric 環境,或確保您是在 Fabric Runtime 1.2 (Spark 3.4 (或更高版本) 和 Delta 2.4) 上執行
  • 建立新的筆記本
  • 將筆記本連結至 Lakehouse。 在筆記本左側,選取 [新增],以新增現有的 Lakehouse 或建立新的 Lakehouse。

準備定型與測試的資料集

在本節中,我們會準備 LightGBM 模型的定型和測試資料集。 我們使用 Sklearn 的加州住房資料集。 我們會從資料建立 Spark DataFrame ,並使用 VectorAssembler 將特徵結合成單一向量資料行。

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)

然後,我們會隨機將資料分割成三個子集:定型、驗證和測試,分別有 85%、12.75% 和 2.25% 的資料。 我們會使用定型和驗證集進行超參數微調,以及使用測試集進行模型評估。

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)

設定 ML 實驗

設定 MLflow

在執行超參數微調之前,我們需要定義可接受不同超參數值的定型函式,並在定型資料上定型 LightGBM 模型。 我們也需要使用 R2 分數來評估驗證資料的模型效能,以測量模型如何符合資料。

若要這樣做,我們會先匯入必要的模組,再設定 MLflow 實驗。 MLflow 是一個開放原始碼平台,可供您管理端對端機器學習生命週期。 它可協助我們追蹤並比較不同模型和超參數的結果。

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

設定記錄層級

在這裡,我們會設定記錄層級來隱藏來自 Synapse.ml 程式庫的不必要輸出,以便保持記錄簡潔。

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

訓練基準模型

接下來,我們會定義接受四個超參數作為輸入的定型函式:alpha、learningRate、numLeaves 及 numIterations。 這些是我們想要稍後使用 FLAML 進行微調的超參數。

定型函式也會接受兩個 DataFrame 作為輸入:train_data 和 val_data,分別是定型和驗證資料集。 定型函式會傳回兩個輸出:已定型的模型和驗證資料的 R2 分數。

# 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

最後,我們將會使用定型函式,以超參數的預設值來定型基準模型。 我們也會評估測試資料的基準模型,並列印 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)

使用 FLAML 執行超參數微調

FLAML 是快速且輕量型的 AutoML 程式庫,會自動尋找指定模型和資料集的最佳超參數。 它會使用低成本的搜尋策略,配合評估計量的意見反應。 在本節中,我們將會使用 FLAML 來微調我們在上一節中定義的 LightGBM 模型的超參數。

定義 tune 函式

若要使用 FLAML,我們需要定義 tune 函式,以將組態字典作為輸入,並傳回以評估計量作為索引鍵,並以計量值作為值的字典。

組態字典包含我們想要微調的超參數及其值。 tune 函式會使用我們稍早定義的定型函式,以使用指定的組態來定型和評估模型。

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

定義搜尋空間

接下來,我們需要定義我們想要微調的超參數的搜尋空間。 搜尋空間是一個字典,會將超參數名稱對應至想要探索的值範圍。 FLAML 提供一些方便的函式來定義不同類型的範圍,例如 uniform、loguniform 和 randint。

在此情況下,我們希望微調下列四個超參數:alpha、learningRate、numLeaves 和 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),
}

定義超參數試用

最後,我們需要定義超參數試用版,以使用 FLAML 來最佳化超參數。 我們需要將 tune 函式、搜尋空間、時間預算、樣本數目、計量名稱、模式和詳細資訊層級傳遞至 flaml.tune.run 函式。 我們也需要啟動巢狀 MLflow 執行,以追蹤試用的結果。

flaml.tune.run function 會傳回包含最佳組態和最佳計量值的分析物件。

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

試用完成後,我們可從分析對象檢視最佳組態和最佳計量值。

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

比較結果

使用 FLAML 尋找最佳的超參數之後,我們需要評估其如何改善模型效能。 若要這樣做,我們會使用定型函式,在完整定型資料集上建立具有最佳超參數的新模型。 然後,我們會使用測試資料集來計算新模型和基準模型的 R2 分數。

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

儲存最終模型

完成超參數試用之後,現在可以將最終的微調模型儲存為 Fabric 中的 ML 模型。

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