如何在 OptunaHub 中实现您的采样器

OptunaHub 是一个 Optuna 包注册中心,它是一个由贡献者分享算法的平台。本教程介绍了如何在 OptunaHub 中实现您自己的算法。

在这里,我们将展示如何实现您自己的采样器,即优化算法。如果您想实现采样器以外的算法,请参考其他教程。

通常,Optuna 提供 BaseSampler 类来实现您自己的采样器。然而,从头开始实现一个采样器有点复杂。作为替代,在 OptunaHub 中,您可以使用 optunahub.samplers.SimpleBaseSampler 类,它是一个易于扩展的采样器模板。

您需要安装 optuna 来实现您自己的采样器,并安装 optunahub 来使用模板 SimpleBaseSampler

$ pip install optuna optunahub

首先,导入 optuna, optunahub 以及其他必需的模块。

from __future__ import annotations

from typing import Any

import numpy as np
import optuna

import optunahub

接下来,通过继承 SimpleBaseSampler 类来定义您自己的采样器类。在本例中,我们实现一个返回随机值的采样器。

class MySampler(optunahub.samplers.SimpleBaseSampler):
    # By default, search space will be estimated automatically like Optuna's built-in samplers.
    # You can fix the search spacd by `search_space` argument of `SimpleSampler` class.
    def __init__(
        self, search_space: dict[str, optuna.distributions.BaseDistribution] | None = None
    ) -> None:
        super().__init__(search_space)
        self._rng = np.random.RandomState()

    # You need to implement sample_relative method.
    # This method returns a dictionary of hyperparameters.
    # The keys of the dictionary are the names of the hyperparameters, which must be the same as the keys of the search_space argument.
    # The values of the dictionary are the values of the hyperparameters.
    # In this example, sample_relative method returns a dictionary of randomly sampled hyperparameters.
    def sample_relative(
        self,
        study: optuna.study.Study,
        trial: optuna.trial.FrozenTrial,
        search_space: dict[str, optuna.distributions.BaseDistribution],
    ) -> dict[str, Any]:
        # search_space argument must be identical to search_space argument input to __init__ method.
        # This method is automatically invoked by Optuna and SimpleBaseSampler.

        # If search space is empty, all parameter values are sampled randomly by SimpleBaseSampler.
        if search_space == {}:
            return {}

        params = {}  # type: dict[str, Any]
        for n, d in search_space.items():
            if isinstance(d, optuna.distributions.FloatDistribution):
                params[n] = self._rng.uniform(d.low, d.high)
            elif isinstance(d, optuna.distributions.IntDistribution):
                params[n] = self._rng.randint(d.low, d.high)
            elif isinstance(d, optuna.distributions.CategoricalDistribution):
                params[n] = d.choices[self._rng.randint(len(d.choices))]
            else:
                raise NotImplementedError
        return params

这里,作为一个示例,目标函数定义如下。

def objective(trial: optuna.trial.Trial) -> float:
    x = trial.suggest_float("x", -10, 10)
    y = trial.suggest_int("y", -10, 10)
    z = trial.suggest_categorical("z", ["a", "b", "c"])
    return x**2 + y**2 + {"a": -10, "b": 0, "c": 10}[z] ** 2

该采样器可以像其他 Optuna 采样器一样使用。在以下示例中,我们创建一个研究并使用 MySampler 类对其进行优化。

sampler = MySampler()
study = optuna.create_study(sampler=sampler)
study.optimize(objective, n_trials=100)

最佳参数可以如下获取。

best_params = study.best_params
best_value = study.best_value
print(f"Best params: {best_params}, Best value: {best_value}")
Best params: {'x': -0.08051894464286669, 'y': -1, 'z': 'b'}, Best value: 1.006483300446401

我们可以看到,Optuna 找到的 best_params 值接近最优值 {"x":0, "y": 0, "z": "b"}

在上述示例中,搜索空间在第一次试验时被估计,并通过优化动态更新。如果您的采样器要求在优化前固定搜索空间,您可以在初始化时将搜索空间传递给采样器。传递搜索空间还可以让采样器避免估计搜索空间的开销。有关定义搜索空间的 optuna.distributions 的更多信息,请参见 文档

sampler = MySampler(
    search_space={
        "x": optuna.distributions.FloatDistribution(-10, 10),
        "y": optuna.distributions.IntDistribution(-10, 10),
        "z": optuna.distributions.CategoricalDistribution(["a", "b", "c"]),
    }
)
study = optuna.create_study(sampler=sampler)
study.optimize(objective, n_trials=100)

在下一个教程中,我们将展示如何将您的采样器注册到 OptunaHub。让我们继续阅读 如何在 OptunaHub 中注册您的包。有关实现采样器的更多信息,请参见 用户自定义采样器文档

脚本总运行时间: (0 分钟 0.102 秒)

由 Sphinx-Gallery 生成的图库