跳至主要内容

在 Python 中使用 Optuna 进行深度强化学习

探索如何使用 Optuna 掌握超参数调优。学习定义超参数、设置目标函数,并在深度强化学习中利用采样与剪枝技术。
· 11分钟 阅读

使用 AI 探索

机器学习模型存在对性能影响重大的超参数。这些参数就像烤蛋糕时的合适温度或翻煎饼时的合适角度。在强化学习中,超参数会影响策略与奖励的近似;若调优得当,超参数优化能显著提升结果表现。

本文将介绍 Optuna,这是一款强大的开源超参数优化框架。若您刚接触该主题,可先阅读 Understanding Machine Learning,这是一门零代码入门课程。想用 Python 边学边练,可尝试我们的 Machine Learning Fundamentals with Python 课程(使用 scikit-learn)。随后,MLOps Concepts 将帮助您了解如何将机器学习模型从本地笔记本迁移到生产环境。最后,尝试 Preprocessing for Machine Learning in Python,这门课程与本文高度相关,将教您为机器学习模型清洗和准备数据。

什么是 Optuna?

Optuna 是一个开源的超参数优化框架,用于自动化超参数搜索。它可与任意机器学习或深度学习框架配合使用。Optuna 的一些关键能力包括:

  • 易于并行与跨任务扩展: 随着计算需求增加,Optuna 提供灵活性以利用分布式训练,并允许连接 Kubernetes 或 Dask 等云端后端。
  • 自动搜索最优超参数:当您为每个超参数指定取值范围时,Optuna 会搜索最佳组合。
  • 先进的采样与剪枝算法:Optuna 提供多种先进算法,如 Median Pruner 与 WilcoxonPruner,可基于统计检验进行剪枝。
  • 易用性:Optuna 上手简单,实现只需 5-6 行代码!
  • Optuna Dashboard:Optuna 现已支持通过 Dashboard 存储并分析所有实验结果。
  • Optuna Hub:允许共享和使用贡献者开发的功能。

什么是强化学习(RL)?

illustration of reinforcement learning using a robot and two flags, one green and one orange

寻找最优路径。来源:作者制图

强化学习(RL)融合了 机器学习 与 最优控制。它关注智能体应如何在动态环境中采取行动以最大化其累积奖励。简单来说,强化学习是教会智能体在环境中做出最优决策,以最大化其奖励。智能体每一步的奖励在接近目标时可能为正或负。

与传统机器学习不同,强化学习算法通过做出决策以达到最优结果,类似我们为达成目标所采用的试错学习过程。深度强化学习与强化学习的区别在于前者使用深度神经网络来逼近复杂函数。也可以说,常规强化学习更偏向确定性方法,而深度强化学习则采用随机性方法。

在强化学习模型中使用 Optuna

在同一环境中,通往目标的路径可能有很多。与监督学习相比,深度强化学习对学习率、gamma、步数等超参数的选择更加敏感。

不佳的超参数选择会导致收敛效果差或不稳定。使用不同随机种子(用于初始化网络权重与环境)带来的性能差异会进一步加剧这一挑战。

本节将演示如何设置 Optuna 及相关依赖、定义搜索空间,并针对 MountainCarContinuous-v0 算法运行优化。MountainCarContinuous-v0 是 RL-Zoo(一个提供评估与绘图脚本的训练框架)中的强化学习算法之一。MountainCarContinuous-v0 建模了一个环境,其中智能体(一辆小车)尝试实现其目标(到达旗帜)。

a gif showing a car riding up to reach the flag at the top of the  mountain to illustrate mountain car algorithm

调优后的 MountainCarContinuous-v0 算法演示。作者制图。

如何开始使用 Optuna

开始之前,您需要:

  1. 设置与模型相关的配置。
  2. 定义您的超参数,并为每个参数建议一个取值范围。
  3. 定义目标函数,其中包含您要优化的模型或函数。还应定义一个回调(CallBack)以返回周期性评估结果。
  4. 创建一个 Optuna study,并在指定的试验次数内运行该优化函数。

设置 Optuna 和 StableBaseline 3

创建新虚拟环境后,安装所需的包。注意,Optuna 支持 Python 3.7 或更新版本。若您想回顾如何创建虚拟环境,请阅读我们的 Virtual Environment in Python 教程。

pip install optunapip install stable-baselines3pip install sb3-contrib

导入必要的包

我们将使用 gym 包来创建智能体执行动作的环境。

from stable_baselines3.common.env_util import make_vec_envfrom stable_baselines3 import A2Cimport gymimport optunafrom optuna.pruners import MedianPrunerfrom optuna.samplers import TPESamplerfrom optuna.visualization import plot_optimization_history, plot_param_importancesfrom typing import Any, Dictimport torchimport torch.nn as nnprint(optuna.__version__)

本项目将使用 Advantage Actor-Critic(A2C)中的 MountainCarContinuous-v0 算法。Advantage Actor-Critic(A2C) 是一种 Actor-Critic 算法,结合了基于价值(衡量采取某动作的奖励)与基于策略(控制智能体动作)的方法,通过降低方差来稳定训练。

定义超参数

我们先设定本任务将使用的配置。

N_TRIALS = 100  # Maximum number of trialsN_JOBS = 1 # Number of jobs to run in parallelN_STARTUP_TRIALS = 5  # Stop random sampling after N_STARTUP_TRIALSN_EVALUATIONS = 2  # Number of evaluations during the trainingN_TIMESTEPS = 100000  # Training budgetEVAL_FREQ = int(N_TIMESTEPS / N_EVALUATIONS)N_EVAL_ENVS = 5N_EVAL_EPISODES = 10TIMEOUT = int(60 * 15)  # 15 minutesENV_ID = "MountainCarContinuous-v0"DEFAULT_HYPERPARAMS = {    "policy": "MlpPolicy",    "env": ENV_ID,}

我们将为这些超参数建议一个取值范围。

def a2c_hyper_params(trial: optuna.Trial) -> dict:    """Sample A2C hyperparameters for Optuna trial."""    return {        "learning_rate": trial.suggest_float("learning_rate", 1e-5, 1e-2),        "gamma": trial.suggest_float("gamma", 0.9, 0.9999),        "n_steps": trial.suggest_int("n_steps", 5, 2048),        "ent_coef": trial.suggest_float("ent_coef", 1e-8, 1e-2),        "vf_coef": trial.suggest_float("vf_coef", 0.1, 1.0),        "max_grad_norm": trial.suggest_float("max_grad_norm", 0.3, 10)    }

定义目标函数

我们定义了一个回调函数 TrialEvalCallback(),用于返回优化任务的周期性评估结果。请查看 Google Colab 笔记本了解该回调的实现。

def objective(trial: optuna.Trial) -> float:    """    This will be used by Optuna to evaluate one set of hyperparameters at a time.Given a trial object, it will sample hyperparameters, evaluate it and report the result.    :param trial: Optuna trial object    :return: Mean episodic reward after training    """    kwargs = DEFAULT_HYPERPARAMS.copy()    # 1. Sample hyperparameters and update the keyword arguments    kwargs.update(a2c_hyper_params(trial))    # 2. Create the RL model    model = A2C(**kwargs)    # 3. Create envs used for evaluation using make_vec_env, ENV_ID and N_EVAL_ENVS    eval_envs = make_vec_env(ENV_ID, n_envs=N_EVAL_ENVS)    # 4. Create the TrialEvalCallback callback     eval_callback = TrialEvalCallback(        eval_envs,        trial,        n_eval_episodes=N_EVAL_EPISODES,        eval_freq=EVAL_FREQ,        deterministic=True,        verbose=0,    )    nan_encountered = False    try:        # Train the model        model.learn(N_TIMESTEPS, callback=eval_callback)    except AssertionError as e:        # Sometimes, random hyperparams can generate NaN        print(e)        nan_encountered = True    finally:        # Free memory        model.env.close()        eval_envs.close()    # Tell the optimizer that the trial failed    if nan_encountered:        return float("nan")    if eval_callback.is_pruned:        raise optuna.exceptions.TrialPruned()    return eval_callback.last_mean_reward

高效的采样与剪枝机制

剪枝是指在给定预算后,削减那些不太可能产生最优结果的参数或搜索空间区域。在 Optuna 中,剪枝会在训练早期淘汰表现较差的试验,从而缩小参数集合并缩短到达最优搜索空间的时间。Optuna 提供多种剪枝器,包括采用中位数停止准则的 Median Pruner。您可在Optuna 文档中了解更多剪枝器信息。

实现 Optuna 的 MedianPruner

Median Pruner 算法会在当前最佳结果劣于以往结果中位数时进行剪枝。这里除以 3 是因为我们仅希望在使用了 1/3 的最大预算后才开始剪枝。

pruner = MedianPruner(n_startup_trials=N_STARTUP_TRIALS, n_warmup_steps=N_EVALUATIONS // 3)

实现 Optuna 的 TPESampler

选择采样器,可选随机、TPESampler、CMAES 等。

sampler = TPESampler(n_startup_trials=N_STARTUP_TRIALS)

创建 study 并开始优化任务

现在我们可以创建一个 Optuna study 了。

study = optuna.create_study(sampler=sampler, pruner=pruner, direction="maximize")try:study.optimize(objective, n_trials=N_TRIALS, n_jobs=N_JOBS, timeout=TIMEOUT)except KeyboardInterrupt:    passprint("Number of finished trials: ", len(study.trials))print("Best trial:")trial = study.best_trialprint(f" Value: {trial.value}")print(" Params: ")for key, value in trial.params.items():    print(f" {key}: {value}")print(" User attrs:")for key, value in trial.user_attrs.items():    print(f" {key}: {value}")

Optuna 的高级功能

我们来看看 Optuna 的一些高级功能。

Optuna 的可视化工具

Optuna 提供 Dashboard,可用于可视化、跟踪和分析以往与当前的运行。在过去,您可能需要连接 MLFlow 等跟踪工具来管理结果。

Optuna Dashboard example

Optuna Dashboard。来源:Optuna

与分布式系统集成以开展大规模实验

Optuna 对分布式训练的支持允许您连接 Kubernetes 或 Dask 等分布式后端,在大规模实验中利用更多计算资源。您还可以连接存储后端来存储与管理会话结果。云端可用 Dask 存储,本地运行可用 SQLite。

在深度强化学习中使用 Optuna 的建议

以下是避免超参数调优常见陷阱的一些建议:

  • 评估是否需要进行超参数调优: 先考虑调参是否真的是您所需的。有时,仅需延长训练步数。
  • 做好结果不一致的心理准备:在强化学习中,即便使用相同的超参数多次运行同一实验,也无法保证得到相同结果。
  • 权衡搜索空间大小带来的挑战:与典型的机器学习问题一样,若搜索空间过小,可能永远得不到最优参数;若过大,可能耗时极长。

Optuna 与 HyperOpt 及其他工具的对比

最后一节,我们从易用性、支持度和其他维度对 Optuna、HyperOpt 与 RayTune 进行对比。

 
功能OptunaHyperOptRayTune
易用性用户友好的 API前期配置繁琐功能全面但 API 更复杂
是否支持 PyTorch/TensorFlow 等 ML 框架是是是
是否支持分布式/并行优化是支持有限是
剪枝/停止是否,仅早停是
可视化工具是否是
搜索算法TPESampler、Random Sampler、CmAESampler、GridSampler、QMCSampler、NSGAIISampler、PartialFixedSampler、GPSamplerTPE、随机搜索随机搜索、网格搜索、HyperBand、BOHB、PBT、ASHA

结论

本文讨论了超参数优化与强化学习的相关概念,并介绍了如何设置 Optuna 并将其连接到您常用的强化学习算法。建议您亲手实践这些概念,并应用到您已完成或计划启动的项目中。

若想进一步阅读并加深理解,建议浏览 Optuna 官方文档以及这篇 关于超参数优化的强化学习教程。

此外,您还可以参考更多 Datacamp 资源以继续学习,包括我们的 What is Reinforcement Learning From AI Feedback? 与 What is Reinforcement Learning from Human Feedback? 教程。若您正准备相关面试,不妨查看我们的 25 Machine Learning Projects for All Levels 与 Top 25 Machine Learning Interview Questions 博文。

Bunmi Akinremi

作者

Bunmi Akinremi

机器学习工程师与诗人

常见问题解答

什么是 Optuna?它如何工作?

Optuna 是一个开源的超参数优化框架,旨在自动化机器学习模型的调参过程。您需要定义要调优的超参数、目标函数,并运行 study。

为何在深度强化学习中使用 Optuna?

Optuna 能处理复杂且高维的搜索空间,剪枝不具潜力的试验,并可轻松集成主流深度学习框架。

如何将 Optuna 集成到我现有的强化学习项目中?

要将 Optuna 集成到深度强化学习中,您应选择要调优的超参数并为其提供搜索空间,定义用于评估模型表现的目标函数,并创建一个运行该函数 n 次试验的 study。本文提供了分步指南。

Optuna 能与 TensorFlow、PyTorch 等流行的深度强化学习框架配合使用吗?

是的,Optuna 支持 TensorFlow 与 PyTorch 框架。

强化学习算法中哪些超参数可以优化?

您可以优化学习率、步数、gamma、激活函数以及网络结构。

主题