【MLOps】优化超参数

直觉

优化是在我们的实验中微调超参数以优化特定目标的过程。它可能是一个涉及计算的过程,具体取决于参数的数量、搜索空间和模型架构。超参数不仅包括模型的参数,还包括来自预处理、分割等的参数(选择)。当我们查看所有可以调整的不同参数时,它很快就变成了一个非常大的搜索空间。然而,仅仅因为某些东西是超参数并不意味着我们需要调整它。

  • lower=True修复一些超参数(例如在预处理期间)并将它们从调整子集中删除是绝对可以的。请务必注意您正在修复哪些参数以及您这样做的理由。
  • 您最初可以只调整一小部分但有影响力的超参数子集,您认为这些子集会产生最佳结果。

我们希望优化我们的超参数,以便我们能够了解它们中的每一个如何影响我们的目标。通过在合理的搜索空间中进行多次试验,我们可以确定不同参数的接近理想值。这也是确定较小参数是否产生与较大参数(效率)相似的性能的好机会。

工具

超参数调整有很多选项(Optuna、Ray tune、Hyperopt等)。我们将使用 Optuna 是因为它的简单性、流行性和效率,尽管它们都同样如此。这实际上归结为熟悉程度以及库是否具有易于测试和可用的特定实现。

应用

执行超参数优化时需要考虑许多因素,幸运的是 Optuna 允许我们轻松实现它们。我们将进行一项小型研究,其中我们将调整一组参数(当我们将代码移动到 Python 脚本时,我们将对参数空间进行更彻底的研究)。以下是研究的过程:

  1. 定义目标(指标)并确定优化方向。
  2. [OPTIONAL]选择一个采样器来确定后续试验的参数。(默认是基于树的采样器)。
  3. [OPTIONAL]选择一个修枝剪提早结束没有希望的试验。
  4. 定义要在每个试验中调整的参数以及要采样的值的分布。
pip install optuna==2.10.0 numpyencoder==0.3.0 -q
import optuna

我们将使用与以前相同的训练函数,因为我们已经添加了在trial参数不是时修剪特定运行的功能None

# Pruning (inside train() function)
trial.report(val_loss, epoch)
if trial.should_prune():
    raise optuna.TrialPruned()

客观的

我们需要定义一个目标函数,该函数将使用试验和一组参数并生成要优化的指标(f1在我们的例子中)。

def objective(args, trial):
    """Objective function for optimization trials."""
    # Parameters to tune
    args.analyzer = trial.suggest_categorical("analyzer", ["word", "char", "char_wb"])
    args.ngram_max_range = trial.suggest_int("ngram_max_range", 3, 10)
    args.learning_rate = trial.suggest_loguniform("learning_rate", 1e-2, 1e0)
    args.power_t = trial.suggest_uniform("power_t", 0.1, 0.5)

    # Train & evaluate
    artifacts = train(args=args, df=df, trial=trial)

    # Set additional attributes
    performance = artifacts["performance"]
    print(json.dumps(performance, indent=2))
    trial.set_user_attr("precision", performance["precision"])
    trial.set_user_attr("recall", performance["recall"])
    trial.set_user_attr("f1", performance["f1"])

    return performance["f1"]

学习

我们已准备好使用MLFlowCallback开始我们的研究,这样我们就可以跟踪所有不同的试验。

from numpyencoder import NumpyEncoder
from optuna.integration.mlflow import MLflowCallback
NUM_TRIALS = 20  # small sample for now
# Optimize
pruner = optuna.pruners.MedianPruner(n_startup_trials=5, n_warmup_steps=5)
study = optuna.create_study(study_name="optimization", direction="maximize", pruner=pruner)
mlflow_callback = MLflowCallback(
    tracking_uri=mlflow.get_tracking_uri(), metric_name="f1")
study.optimize(lambda trial: objective(args, trial),
            n_trials=NUM_TRIALS,
            callbacks=[mlflow_callback])
A new study created in memory with name: optimization
Epoch: 00 | train_loss: 1.34116, val_loss: 1.35091
...
Epoch: 90 | train_loss: 0.32167, val_loss: 0.57661
Stopping early!
Trial 0 finished with value: 0.7703281822265505 and parameters: {'analyzer': 'char', 'ngram_max_range': 10, 'learning_rate': 0.025679294001785473, 'power_t': 0.15046698128066294}. Best is trial 0 with value: 0.7703281822265505.

...

Trial 10 pruned.

...

Epoch: 80 | train_loss: 0.16680, val_loss: 0.43964
Epoch: 90 | train_loss: 0.16134, val_loss: 0.43686
Trial 19 finished with value: 0.8470890576153735 and parameters: {'analyzer': 'char_wb', 'ngram_max_range': 4, 'learning_rate': 0.08452049154544644, 'power_t': 0.39657115651885855}. Best is trial 3 with value: 0.8470890576153735.
# Run MLFlow server and localtunnel
get_ipython().system_raw("mlflow server -h 0.0.0.0 -p 8000 --backend-store-uri $PWD/experiments/ &")
!npx localtunnel --port 8000

1. 点击 Experiments 下左侧的“优化”实验。2. 通过单击每个运行左侧的切换框或单击标题中的切换框来选择该实验中的所有运行来选择要比较的运行。3. 单击比较按钮。

  1. 在对比页面中,我们可以通过各种镜头(轮廓、平行坐标等)查看结果

# All trials
trials_df = study.trials_dataframe()
trials_df = trials_df.sort_values(["user_attrs_f1"], ascending=False)  # sort by metric
trials_df.head()
数字 价值 日期时间开始 日期时间完成 期间 params_analyzer params_learning_rate params_ngram_max_range params_power_t user_attrs_f1 user_attrs_precision user_attrs_recall 状态
3 3 0.847089 2022-05-18 18:16:58.108105 2022-05-18 18:17:03.569948 0 天 00:00:05.461843 char_wb 0.088337 4 0.118196 0.847089 0.887554 0.833333 完全的
19 19 0.847089 2022-05-18 18:17:58.219462 2022-05-18 18:18:00.642571 0 天 00:00:02.423109 char_wb 0.084520 4 0.396571 0.847089 0.887554 0.833333 完全的
12 12 0.840491 2022-05-18 18:17:41.845179 2022-05-18 18:17:45.792068 0 天 00:00:03.946889 char_wb 0.139578 7 0.107273 0.840491 0.877431 0.826389 完全的
13 13 0.840491 2022-05-18 18:17:45.862705 2022-05-18 18:17:49.657014 0 天 00:00:03.794309 char_wb 0.154396 7 0.433669 0.840491 0.877431 0.826389 完全的
15 15 0.836255 2022-05-18 18:17:50.464948 2022-05-18 18:17:54.446481 0 天 00:00:03.981533 char_wb 0.083253 7 0.106982 0.836255 0.881150 0.819444 完全的
# Best trial
print (f"Best value (f1): {study.best_trial.value}")
print (f"Best hyperparameters: {json.dumps(study.best_trial.params, indent=2)}")
Best value (f1): 0.8535985582060417
Best hyperparameters: {
  "analyzer": "char_wb",
  "ngram_max_range": 4,
  "learning_rate": 0.08981103667371809,
  "power_t": 0.2583427488720579
}
# Save best parameter values
args = {**args.__dict__, **study.best_trial.params}
print (json.dumps(args, indent=2, cls=NumpyEncoder))
{
  "lower": true,
  "stem": false,
  "analyzer": "char_wb",
  "ngram_max_range": 4,
  "alpha": 0.0001,
  "learning_rate": 0.08833689034118489,
  "power_t": 0.1181958972675695
}

你可能感兴趣的:(机器学习(ML),python,深度学习,开发语言)