超参数优化(预览版)

超参数优化是这样一个过程,它查找训练过程中机器学习模型未学习但是在训练过程开始之前被使用者设定的参数的最佳值。 这些参数通常称为超参数,示例包括学习速率、神经网络中的隐藏层数、正则化强度和批次大小。

机器学习模型的性能对超参数的选择高度敏感,最佳超参数集可能会因特定问题和数据集而变化很大。 因此,超参数优化是机器学习管道中的关键步骤,因为它可能会对模型的准确度和通用化性能产生重大影响。

在 Fabric 中,数据科学家可以利用 FLAML(轻量级 Python 资料库)来高效自动化机器学习,并利用 AI 操作来满足超参数优化要求。 在 Fabric 笔记本中,用户可以调用 flaml.tune 进行经济的超参数优化。

重要

此功能目前为预览版

优化工作流

使用 flaml.tune 完成基本优化任务需要三个基本步骤:

  1. 指定与超参数相关的优化目标。
  2. 指定超参数的搜索空间。
  3. 指定优化约束,包括用于执行优化的资源预算约束、配置约束或/和对某个(或多个)特定指标的约束。

优化目标

第一步是指定优化目标。 为此,应首先在用户定义的函数 evaluation_function 中指定超参数的评估过程。 该函数需要超参数配置作为输入。 它只需在标量中返回指标值或返回指标名称和指标值对的字典。

在下面的示例中,我们可以定义名为 xy 的 2 个超参数的评估函数。

import time

def evaluate_config(config: dict):
    """evaluate a hyperparameter configuration"""
    score = (config["x"] - 85000) ** 2 - config["x"] / config["y"]


    faked_evaluation_cost = config["x"] / 100000
    time.sleep(faked_evaluation_cost)
    # we can return a single float as a score on the input config:
    # return score
    # or, we can return a dictionary that maps metric name to metric value:
    return {"score": score, "evaluation_cost": faked_evaluation_cost, "constraint_metric": config["x"] * config["y"]}

搜索空间

接下来,我们将指定超参数的搜索空间。 在搜索空间中,需要为超参数指定有效值以及如何对这些值进行采样(例如,从均匀分布或日志均匀分布)。 在下面的示例中,我们可以提供超参数 xy 的搜索空间。 两者的有效值为介于 [1,100,000] 之间的整数。 这些超参数在指定范围内均匀采样。

from flaml import tune

# construct a search space for the hyperparameters x and y.
config_search_space = {
    "x": tune.lograndint(lower=1, upper=100000),
    "y": tune.randint(lower=1, upper=100000)
}

# provide the search space to tune.run
tune.run(..., config=config_search_space, ...)

使用 FLAML,用户可以自定义特定超参数的域。 这允许用户指定要从中采样参数的类型有效范围。 FLAML 支持以下超参数类型:float、integer 和 categorical。 对于常用域,可参阅以下示例:

config = {
    # Sample a float uniformly between -5.0 and -1.0
    "uniform": tune.uniform(-5, -1),

    # Sample a float uniformly between 3.2 and 5.4,
    # rounding to increments of 0.2
    "quniform": tune.quniform(3.2, 5.4, 0.2),

    # Sample a float uniformly between 0.0001 and 0.01, while
    # sampling in log space
    "loguniform": tune.loguniform(1e-4, 1e-2),

    # Sample a float uniformly between 0.0001 and 0.1, while
    # sampling in log space and rounding to increments of 0.00005
    "qloguniform": tune.qloguniform(1e-4, 1e-1, 5e-5),

    # Sample a random float from a normal distribution with
    # mean=10 and sd=2
    "randn": tune.randn(10, 2),

    # Sample a random float from a normal distribution with
    # mean=10 and sd=2, rounding to increments of 0.2
    "qrandn": tune.qrandn(10, 2, 0.2),

    # Sample a integer uniformly between -9 (inclusive) and 15 (exclusive)
    "randint": tune.randint(-9, 15),

    # Sample a random uniformly between -21 (inclusive) and 12 (inclusive (!))
    # rounding to increments of 3 (includes 12)
    "qrandint": tune.qrandint(-21, 12, 3),

    # Sample a integer uniformly between 1 (inclusive) and 10 (exclusive),
    # while sampling in log space
    "lograndint": tune.lograndint(1, 10),

    # Sample a integer uniformly between 2 (inclusive) and 10 (inclusive (!)),
    # while sampling in log space and rounding to increments of 2
    "qlograndint": tune.qlograndint(2, 10, 2),

    # Sample an option uniformly from the specified choices
    "choice": tune.choice(["a", "b", "c"]),
}

若要详细了解如何在搜索空间中自定义域,请访问有关自定义搜索空间的 FLAML 文档

优化约束

最后一步是指定优化任务的约束。 flaml.tune 的一个值得注意的属性是它可以在所需的资源约束内完成优化过程。 为此,用户可以使用 time_budget_s 参数提供有关挂钟时间的资源约束,或使用 num_samples 参数提供有关试验次数的资源约束。

# Set a resource constraint of 60 seconds wall-clock time for the tuning.
flaml.tune.run(..., time_budget_s=60, ...)

# Set a resource constraint of 100 trials for the tuning.
flaml.tune.run(..., num_samples=100, ...)

# Use at most 60 seconds and at most 100 trials for the tuning.
flaml.tune.run(..., time_budget_s=60, num_samples=100, ...)

若要了解有关添加配置约束的详细信息,请访问高级优化选项的 FLAML 文档

综合运用

定义优化条件后,便可执行优化试验。 为了跟踪试验结果,我们可以利用 MLFlow 自动记录来捕获其中每个运行的指标和参数。 此代码将捕获整个超参数优化试验,并突出显示 FLAML 探索的每个超参数组合。

import mlflow
mlflow.set_experiment("flaml_tune_experiment")
mlflow.autolog(exclusive=False)

with mlflow.start_run(nested=True, run_name="Child Run: "):
    analysis = tune.run(
        evaluate_config,  # the function to evaluate a config
        config=config_search_space,  # the search space defined
        metric="score",
        mode="min",  # the optimization mode, "min" or "max"
        num_samples=-1,  # the maximal number of configs to try, -1 means infinite
        time_budget_s=10,  # the time budget in seconds
    )

注意

启用 MLflow 自动记录后,应在 MLFlow 运行时自动记录指标、参数和模型。 但是,这因框架而异。 可能不会记录特定模型的指标和参数。 例如,不会记录 XGBoost、LightGBM、Spark 和 SynapseML 模型的指标。 可以使用 MLFlow 自动记录文档详细了解可从每个框架捕获哪些指标和参数。

使用 Apache Spark 进行并行优化

flaml.tune 功能支持优化 Apache Spark 和单节点学习器。 此外,在优化单节点学习器(例如 Scikit-Learn 学习器)时,还可以通过设置 use_spark = True 来进行并行优化以加快优化过程。 对于 Spark 群集,默认情况下,FLAML 将为每个执行程序启动一个试验。 你还可以使用 n_concurrent_trials 参数自定义并发试验数量。


analysis = tune.run(
    evaluate_config,  # the function to evaluate a config
    config=config_search_space,  # the search space defined
    metric="score",
    mode="min",  # the optimization mode, "min" or "max"
    num_samples=-1,  # the maximal number of configs to try, -1 means infinite
    time_budget_s=10,  # the time budget in seconds
    use_spark=True,
)
print(analysis.best_trial.last_result)  # the best trial's result
print(analysis.best_config)  # the best config

要详细了解如何并行化优化线索,可访问并行 Spark 作业的 FLAML 文档

将结果可视化

flaml.visualization 模块提供使用 Plotly 绘制优化过程的实用工具函数。 通过利用 Plotly,用户可以使用交互方式浏览其 AutoML 试验结果。 若要使用这些绘图函数,只需将优化后的 flaml.AutoMLflaml.tune.tune.ExperimentAnalysis 对象作为输入提供。

可以在笔记本中使用以下函数:

  • plot_optimization_history:绘制试验中所有试用版的优化历史记录。
  • plot_feature_importance:绘制数据集中每个功能的重要性。
  • plot_parallel_coordinate:绘制试验中的高维参数关系。
  • plot_contour:在试验中将参数关系绘制为等高线图。
  • plot_edf:绘制试验的目标值 EDF(经验分布函数)。
  • plot_timeline:绘制试验的时间线。
  • plot_slice:在研究中将参数关系绘制为切片图。
  • plot_param_importance:绘制试验的超参数重要性。