-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #202 from y0z/feature/benchmarks-tutorial
Add benchmarks tutorials
- Loading branch information
Showing
5 changed files
with
167 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
""" | ||
.. _benchmarks_basic: | ||
How to Implement Your Benchmark Problems with OptunaHub (Basic) | ||
=============================================================== | ||
OptunaHub provides the ``optunahub.benchmarks`` module for implementing benchmark problems. | ||
In this tutorial, we will explain how to implement your own benchmark problems using ``optunahub.benchmarks``. | ||
""" | ||
|
||
################################################################################################### | ||
# First of all, import `optuna` and other required modules. | ||
from __future__ import annotations | ||
|
||
import optuna | ||
from optunahub.benchmarks import BaseProblem | ||
|
||
|
||
################################################################################################### | ||
# Next, define your own problem class by inheriting ``BaseProblem`` class. | ||
# Here, let's implement a simple 2-dimensional sphere function. | ||
# | ||
# You need to implement the following methods defined in the ``BaseProblem`` class.: | ||
# | ||
# - ``search_space``: This method returns the dictionary of search space of the problem. Each dictionary element consists of the parameter name and distribution (see `optuna.distributions <https://optuna.readthedocs.io/en/stable/reference/distributions.html>`__). | ||
# - ``directions``: This method returns the directions (minimize or maximize) of the problem. The return type is the list of `optuna.study.direction <https://optuna.readthedocs.io/en/stable/reference/generated/optuna.study.StudyDirection.html>`__. | ||
# - ``evaluate``: This method evaluates the objective function given a dictionary of input parameters. | ||
class Sphere2D(BaseProblem): | ||
@property | ||
def search_space(self) -> dict[str, optuna.distributions.BaseDistribution]: | ||
return { | ||
"x0": optuna.distributions.FloatDistribution(low=-5, high=5), | ||
"x1": optuna.distributions.FloatDistribution(low=-5, high=5), | ||
} | ||
|
||
@property | ||
def directions(self) -> list[optuna.study.StudyDirection]: | ||
return [optuna.study.StudyDirection.MINIMIZE] | ||
|
||
def evaluate(self, params: dict[str, float]) -> float: | ||
return params["x0"] ** 2 + params["x1"] ** 2 | ||
|
||
|
||
################################################################################################### | ||
# Since ``BaseProblem`` provides the default implementation of ``__call__(self, trial: optuna.Trial)`` that calls the ``evaluate`` method internally, the problem instance can be directly used as an objective function for ``study.optimize``. | ||
sphere2d = Sphere2D() | ||
study = optuna.create_study(directions=sphere2d.directions) | ||
study.optimize(sphere2d, n_trials=20) | ||
|
||
|
||
################################################################################################### | ||
# The constructor of the problem class can be customized to introduce additional attributes. | ||
# To give an illustration, we show an example with a dynamic dimensional sphere function. | ||
class SphereND(BaseProblem): | ||
def __init__(self, dim: int) -> None: | ||
self.dim = dim | ||
|
||
@property | ||
def search_space(self) -> dict[str, optuna.distributions.BaseDistribution]: | ||
return { | ||
f"x{i}": optuna.distributions.FloatDistribution(low=-5, high=5) | ||
for i in range(self.dim) | ||
} | ||
|
||
@property | ||
def directions(self) -> list[optuna.study.StudyDirection]: | ||
return [optuna.study.StudyDirection.MINIMIZE] | ||
|
||
def evaluate(self, params: dict[str, float]) -> float: | ||
return sum(params[f"x{i}"] ** 2 for i in range(self.dim)) | ||
|
||
|
||
sphere3d = SphereND(dim=3) | ||
study = optuna.create_study(directions=sphere3d.directions) | ||
study.optimize(sphere3d, n_trials=20) | ||
|
||
################################################################################################### | ||
# After implementing your own benchmark problem, you can register it with OptunaHub. | ||
# See :doc:`002_registration` for how to register your benchmark problem with OptunaHub. | ||
# | ||
# In :ref:`benchmarks_advanced`, we explain how to implement more complex benchmark problems such as a problem with dynamic search space. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
""" | ||
.. _benchmarks_advanced: | ||
How to Implement Your Benchmark Problems with OptunaHub (Advanced) | ||
================================================================== | ||
OptunaHub provides the ``optunahub.benchmarks`` module for implementing benchmark problems. | ||
In this tutorial, we will explain how to implement complex benchmark problems such as a problem with dynamic search space using ``optunahub.benchmarks``. | ||
For the implementation of simple benchmark problems, please refer to :ref:`benchmarks_basic`. | ||
""" | ||
|
||
################################################################################################### | ||
# Implementing a Problem with Dynamic Search Space | ||
# ------------------------------------------------- | ||
# | ||
# Here, let's implement a problem with a dynamic search space. | ||
# | ||
# First of all, import `optuna` and other required modules. | ||
from __future__ import annotations | ||
|
||
import optuna | ||
from optunahub.benchmarks import BaseProblem | ||
|
||
|
||
################################################################################################### | ||
# Next, define your own problem class by inheriting ``BaseProblem`` class. | ||
# To implement a problem with a dynamic search space, ``__call__(self, trial: optuna.Trial)`` method must be overridden so that we can define a dynamic search space in the define-by-run manner. | ||
# Please note that ``direcitons`` property must also be implemented. | ||
class DynamicProblem(BaseProblem): | ||
def __call__(self, trial: optuna.Trial) -> float: | ||
x = trial.suggest_float("x", -5, 5) | ||
if x < 0: | ||
# Parameter `y` exists only when `x` is negative. | ||
y = trial.suggest_float("y", -5, 5) | ||
return x**2 + y | ||
else: | ||
return x**2 | ||
|
||
@property | ||
def directions(self) -> list[optuna.study.StudyDirection]: | ||
return [optuna.study.StudyDirection.MINIMIZE] | ||
|
||
@property | ||
def search_space(self) -> dict[str, optuna.distributions.BaseDistribution]: | ||
# You can implement this property as you like, or leave it unimplemented (``BaseProblem`` provides this default behavior). | ||
raise NotImplementedError | ||
|
||
def evaluate(self, params: dict[str, float]) -> float: | ||
# You can implement this method as you like, or leave it unimplemented (``BaseProblem`` provides this default behavior). | ||
raise NotImplementedError | ||
|
||
|
||
################################################################################################### | ||
# The implementations of the ``search_space`` and ``evaluate`` are non-trivial when the search space is dynamic. | ||
# However, since ``__call__(self, trial: optuna.Trial)`` does not have to depend on both the ``evaluate`` method and the ``search_space`` attribute internally, their implementations are up to users. | ||
# If possible, you could provide their implementations, but this is not necessary to make your benchmark problem work. | ||
# Please note that calling them will result in ``NotImplementedError`` if you leave them unimplemented. | ||
|
||
################################################################################################### | ||
# Then, you can optimize the problem with Optuna as usual. | ||
dynamic_problem = DynamicProblem() | ||
study = optuna.create_study(directions=dynamic_problem.directions) | ||
study.optimize(dynamic_problem, n_trials=20) | ||
|
||
################################################################################################### | ||
# After implementing your own benchmark problem, you can register it with OptunaHub. | ||
# See :doc:`002_registration` for how to register your benchmark problem with OptunaHub. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
Tutorial | ||
======== | ||
|
||
If you are new to Optuna or want a general introduction, we highly recommend the below video. | ||
Through the following tutorials, you can learn how to develop and register features for OptunaHub. |