Various documentation improvements (#912)

# Pull Request

## Title

Various documentation improvements

---

## Description

- [x] Some spelling fixups
- [x] sidebar navigation depth tweaks
- [x] Tunables improvements
- [x] Split type related things out from tunable.py to tunable_types.py
  - [x] Associated import fixups
  - [x] Add Examples to Tunable class
- [x] Optimizer documentation improvements
  - [x] Add more examples
  - [x] Add more cross references
- [x] "See Also" rendering mixups.
- [x] mlos_viz dabl doc fixups
- [x] LlamaTune doc fixups
- [ ] ~Environments~ (future PR)
- [ ] ~Services~ (future PR)
- [x] Disable Azure intersphinx resolution:
https://github.com/Azure/azure-sdk-for-python/issues/39316,
https://github.com/Quansight-Labs/intersphinx_registry/issues/57

See Also: #891

---

## Type of Change

- Documentation update
- Refactor

---

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Brian Kroth 2025-01-21 17:40:26 -06:00 коммит произвёл GitHub
Родитель 6b30ee0177
Коммит f2b3e814c7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
56 изменённых файлов: 2010 добавлений и 366 удалений

Просмотреть файл

@ -35,6 +35,7 @@
"iloc",
"ipykernel",
"iterrows",
"jsons",
"jsonschema",
"jupyterlab",
"keepalive",

Просмотреть файл

@ -12,6 +12,8 @@ scriptpath=$(readlink -f "$0")
scriptdir=$(dirname "$scriptpath")
cd "$scriptdir"
SKIP_NGINX_BUILD=${SKIP_NGINX_BUILD:-false}
if [ -f ../.devcontainer/.env ]; then
source ../.devcontainer/.env
fi
@ -28,13 +30,15 @@ cmd="${1:-}"
if [ "$cmd" == 'start' ]; then
set -x
tmpdir=$(mktemp -d)
docker build --progress=plain -t mlos-doc-nginx \
--build-arg http_proxy=${http_proxy:-} \
--build-arg https_proxy=${https_proxy:-} \
--build-arg no_proxy=${no_proxy:-} \
--build-arg NGINX_PORT=$NGINX_PORT \
-f Dockerfile "$tmpdir"
rmdir "$tmpdir"
if ! $SKIP_NGINX_BUILD; then
docker build --progress=plain -t mlos-doc-nginx \
--build-arg http_proxy=${http_proxy:-} \
--build-arg https_proxy=${https_proxy:-} \
--build-arg no_proxy=${no_proxy:-} \
--build-arg NGINX_PORT=$NGINX_PORT \
-f Dockerfile "$tmpdir"
rmdir "$tmpdir"
fi
docker run -d --name mlos-doc-nginx \
-v "$repo_root/doc/nginx-default.conf":/etc/nginx/templates/default.conf.template \
-v "$repo_root/doc":/doc \

Просмотреть файл

@ -136,8 +136,10 @@ def is_on_github_actions():
intersphinx_mapping = get_intersphinx_mapping(
packages={
"asyncssh",
"azure-core",
"azure-identity",
# Azure SDKs removed their intersphinx publishing.
# https://github.com/Azure/azure-sdk-for-python/issues/39316
# "azure-core",
# "azure-identity",
"configspace",
"matplotlib",
"numpy",
@ -166,12 +168,15 @@ CUSTOM_REF_TYPE_MAP: dict[tuple[str, str], str] = {
("ConcreteOptimizer", "class"): "data",
("ConcreteSpaceAdapter", "class"): "data",
("DistributionName", "class"): "data",
("mlos_bench.tunables.tunable_types.DistributionName", "class"): "data",
("FlamlDomain", "class"): "data",
("mlos_core.spaces.converters.flaml.FlamlDomain", "class"): "data",
("TunableValue", "class"): "data",
("mlos_bench.tunables.tunable.TunableValue", "class"): "data",
("mlos_bench.tunables.tunable_types.TunableValue", "class"): "data",
("TunableValueType", "class"): "data",
("mlos_bench.tunables.tunable_types.TunableValueType", "class"): "data",
("TunableValueTypeName", "class"): "data",
("mlos_bench.tunables.tunable_types.TunableValueTypeName", "class"): "data",
("T_co", "class"): "data",
("CoroReturnType", "class"): "data",
("FutureReturnType", "class"): "data",
@ -230,6 +235,9 @@ nitpick_ignore = [
nitpick_ignore_regex = [
# Ignore some external references that don't use sphinx for their docs.
(r"py:.*", r"flaml\..*"),
# Azure SDKs removed their intersphinx publishing.
# https://github.com/Azure/azure-sdk-for-python/issues/39316
(r"py:.*", r"azure\..*"),
]
# Which documents to include in the build.
@ -295,6 +303,11 @@ autoapi_keep_files = not is_on_github_actions() # for local testing
#
html_theme = "sphinx_rtd_theme"
html_theme_options = {
"canonical_url": "https://microsoft.github.io/MLOS/",
"navigation_depth": -1,
}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".

Просмотреть файл

@ -10,6 +10,8 @@ MLOS Documentation
Documentation is generated from both the `source tree markdown <source_tree_docs/index.html>`_ and the Python docstrings for each of the packages with navigation links on the side.
MLOS also has a fairly large test suite, so when in doubt, check the `code <https://github.com/microsoft/MLOS>`_ for additional examples or reach out to the maintainers on `Github <https://github.com/microsoft/MLOS>`_!
.. toctree::
:caption: Source Tree Documentation
:maxdepth: 4

Просмотреть файл

@ -15,7 +15,9 @@ Overview
``mlos_bench`` can be installed from `pypi <https://pypi.org/project/mlos-bench>`_
via ``pip install mlos-bench`` and executed using the ``mlos_bench`` `command
<../../mlos_bench.run.usage.html>`_ using a collection of `json` `configs
<https://github.com/microsoft/MLOS/tree/main/mlos_bench/mlos_bench/config/>`_.
<https://github.com/microsoft/MLOS/tree/main/mlos_bench/mlos_bench/config/>`_
which are managed and documented in the :py:mod:`~mlos_bench.config` module and
elsewhere in this package documentation.
It is intended to be used with :py:mod:`mlos_core` via
:py:class:`~mlos_bench.optimizers.mlos_core_optimizer.MlosCoreOptimizer` to help
@ -59,8 +61,9 @@ intended to be modular and composable to allow reuse and faster development of n
benchmarking environments or autotuning experiments.
Where possible, the framework will provide common configs for reuse (e.g., deploying
a VM on Azure, or run benchbase against a database system) to allow users to focus
on the specifics of their experiments.
a VM on Azure, or run `benchbase <https://github.com/cmu-db/benchbase>`_ against
a database system) to allow users to focus on the specifics of their
experiments.
Where none are currently available, one can create them external to MLOS, however
users are also encouraged to `submit PRs or Issues
@ -125,18 +128,20 @@ critically, non-reusable scripts to setup and later parse results and are hence
harder to scale to many users.
With these features as a part of the MLOS ecosystem, benchmarking can become a
*service* that any developer, admin, research, etc. can use and adapt.
*service* that any developer, admin, researcher, etc. can use and adapt.
See below for more information on the classes in this package.
Notes
-----
Note that while the docstrings in this package are generated from the source code
and hence sometimes more focused on the implementation details, most user
interactions with the package will be through the `json configs
<https://github.com/microsoft/MLOS/tree/main/mlos_bench/mlos_bench/config/>`_. Even
so it may be useful to look at the source code to understand how those are
interpreted.
Note that while the docstrings in this package are generated from the source
code and hence sometimes more focused on the implementation details, we do try
to provide some (testable) examples here, but most user interactions with the
package will be through the `json configs
<https://github.com/microsoft/MLOS/tree/main/mlos_bench/mlos_bench/config/>`_.
Even so it may be useful to look at the documentation here (perhaps especially
starting with :py:mod:`mlos_bench.config`) and, if necessary, the `source code
<https://github.com/microsoft/MLOS>`_ to understand how those are interpreted.
Examples
--------

Просмотреть файл

@ -208,12 +208,14 @@ Well Known Variables
Here is a list of some well known variables that are provided or required by the
system and may be used in the config files:
- ``$experiment_id``: A unique identifier for the experiment.
- ``$experiment_id``: A unique identifier for the ``Experiment``.
Typically provided in globals.
- ``$trial_id``: A unique identifier for the trial currently being executed.
- ``$trial_id``: A unique identifier for the ``Trial`` currently being executed.
This can be useful in the configs for :py:mod:`mlos_bench.environments` for
instance (e.g., when writing scripts).
- TODO: Document more variables here.
- ``$trial_runner_id``: A unique identifier for the ``TrialRunner``.
This can be useful when running multiple trials in parallel (e.g., to
provision a numbered VM per worker).
Tunable Configs
^^^^^^^^^^^^^^^

Просмотреть файл

@ -7,8 +7,8 @@ A module for managing config schemas and their validation.
See Also
--------
mlos_bench.config.schemas.config_schemas : The module handling the actual schema
definitions and validation.
mlos_bench.config.schemas.config_schemas :
The module handling the actual schema definitions and validation.
"""
from mlos_bench.config.schemas.config_schemas import CONFIG_SCHEMA_DIR, ConfigSchema

Просмотреть файл

@ -116,11 +116,12 @@ Notes
See Also
--------
:py:mod:`mlos_bench.config` : Overview of the configuration system.
:py:mod:`mlos_bench.services` : Overview of the Services available to the
Environments and their configurations.
:py:mod:`mlos_bench.tunables` : Overview of the Tunables available to the
Environments and their configurations.
:py:mod:`mlos_bench.config` :
Overview of the configuration system.
:py:mod:`mlos_bench.services` :
Overview of the Services available to the Environments and their configurations.
:py:mod:`mlos_bench.tunables` :
Overview of the Tunables available to the Environments and their configurations.
"""
from mlos_bench.environments.base_environment import Environment

Просмотреть файл

@ -19,8 +19,8 @@ from mlos_bench.config.schemas import ConfigSchema
from mlos_bench.dict_templater import DictTemplater
from mlos_bench.environments.status import Status
from mlos_bench.services.base_service import Service
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
from mlos_bench.util import instantiate_from_config, merge_parameters
if TYPE_CHECKING:

Просмотреть файл

@ -12,8 +12,8 @@ from typing import Any, Literal
from mlos_bench.environments.base_environment import Environment
from mlos_bench.environments.status import Status
from mlos_bench.services.base_service import Service
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
_LOG = logging.getLogger(__name__)

Просмотреть файл

@ -25,8 +25,8 @@ from mlos_bench.environments.script_env import ScriptEnv
from mlos_bench.environments.status import Status
from mlos_bench.services.base_service import Service
from mlos_bench.services.types.local_exec_type import SupportsLocalExec
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
from mlos_bench.util import datetime_parser, path_join
_LOG = logging.getLogger(__name__)

Просмотреть файл

@ -17,8 +17,8 @@ from mlos_bench.environments.status import Status
from mlos_bench.services.base_service import Service
from mlos_bench.services.types.fileshare_type import SupportsFileShareOps
from mlos_bench.services.types.local_exec_type import SupportsLocalExec
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
_LOG = logging.getLogger(__name__)

Просмотреть файл

@ -14,8 +14,9 @@ import numpy
from mlos_bench.environments.base_environment import Environment
from mlos_bench.environments.status import Status
from mlos_bench.services.base_service import Service
from mlos_bench.tunables.tunable import Tunable, TunableValue
from mlos_bench.tunables.tunable import Tunable
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
_LOG = logging.getLogger(__name__)

Просмотреть файл

@ -22,8 +22,8 @@ from mlos_bench.environments.status import Status
from mlos_bench.services.base_service import Service
from mlos_bench.services.types.host_ops_type import SupportsHostOps
from mlos_bench.services.types.remote_exec_type import SupportsRemoteExec
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
_LOG = logging.getLogger(__name__)

Просмотреть файл

@ -16,8 +16,8 @@ from collections.abc import Iterable
from mlos_bench.environments.base_environment import Environment
from mlos_bench.services.base_service import Service
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
from mlos_bench.util import try_parse_val
_LOG = logging.getLogger(__name__)

Просмотреть файл

@ -29,8 +29,8 @@ from mlos_bench.services.config_persistence import ConfigPersistenceService
from mlos_bench.services.local.local_exec import LocalExecService
from mlos_bench.services.types.config_loader_type import SupportsConfigLoading
from mlos_bench.storage.base_storage import Storage
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
from mlos_bench.util import try_parse_val
_LOG_LEVEL = logging.INFO

Просмотреть файл

@ -3,10 +3,274 @@
# Licensed under the MIT License.
#
"""
Interfaces and wrapper classes for optimizers to be used in mlos_bench for autotuning or
benchmarking.
Interfaces and wrapper classes for optimizers to be used in :py:mod:`mlos_bench` for
autotuning or benchmarking.
TODO: Improve documentation here.
Overview
++++++++
One of the main purposes of the mlos_bench :py:class:`.Optimizer` class is to
provide a wrapper for the :py:mod:`mlos_core.optimizers` via the
:py:class:`.MlosCoreOptimizer` in order to perform autotuning.
However, several other *config suggesters* that conform to the Optimizer APIs are
also available for use:
- :py:class:`.GridSearchOptimizer` :
Useful for exhaustive search of a *small* parameter space.
- :py:class:`.OneShotOptimizer` :
Useful for one-off config experimentation and benchmarking.
- :py:class:`.ManualOptimizer` :
Useful for repeatedly testing a small set of known configs.
API
+++
Like the mlos_core :py:class:`~mlos_core.optimizers.optimizer.BaseOptimizer`, the
core APIs here are :py:meth:`.Optimizer.suggest` and :py:meth:`.Optimizer.register`.
The :py:meth:`.Optimizer.bulk_register` method is also available to pre-warm a new
Optimizer instance using observations from a prior set of
:py:class:`~mlos_bench.storage.base_storage.Storage.Trial` runs (e.g., from the
:py:mod:`mlos_bench.storage`).
.. note::
We also refer to this as "merging" this only makes sense if the past Trials
were run from a set of Experiments *compatible* with this one (e.g., same
software, workload, VM size, overlapping parameter spaces, etc.).
Automatically determining whether that makes sense to do is challenging and
is left to the user to ensure for now.
Stopping Conditions
^^^^^^^^^^^^^^^^^^^
Currently the :py:meth:`.Optimizer.not_converged` method only checks that the number
of suggestions is less than the ``max_suggestions`` property of the Optimizer
config.
However, in the future we intend to implement more sophisticated stopping conditions
(e.g., total time, convergence, cost budget, etc.).
Spaces
++++++
Unlike mlos_core, the :py:mod:`mlos_bench.optimizers` operate on
:py:mod:`~mlos_bench.tunables` instead of :py:class:`ConfigSpace.ConfigurationSpace`
instances, so mlos_bench handles conversions internally (see
:py:mod:`mlos_bench.optimizers.convert_configspace`).
Config
++++++
Typically these tunables are combined from the individual Environments they are
associated with and loaded via JSON config files.
In the Examples used within this module's documentation we will simply represent
them as JSON strings for explanatory purposes.
Several properties are common to all Optimizers, but some are specific to the
Optimizer being used.
The JSON schemas control what is considered a valid configuration for an Optimizer.
In the case of an :py:class:`.MlosCoreOptimizer`, the valid options can often be
inferred from the constructor arguments of the corresponding
:py:class:`mlos_core.optimizers` class.
Similarly for the SpaceAdapterType, the valid options can be inferred from the
individual :py:mod:`mlos_core.spaces.adapters` class constructors.
Generally speaking though the JSON config for an Optimizer will look something
like the following:
.. code-block:: json
{
"class": "mlos_bench.optimizers.mlos_core_optimizer.MlosCoreOptimizer",
"description": "MlosCoreOptimizer",
"config": {
"max_suggestions": 1000,
"optimization_targets": {
// Your optimization target(s) mapped to their respective
// optimization goals.
"throughput": "max",
"cost": "min",
},
"start_with_defaults": true,
"seed": 42,
// Optionally override the default space adapter type.
// Must be one of the mlos_core SpaceAdapterType enum values.
// e.g., LlamaTune is a method for automatically doing space reduction
// from the original space.
"space_adapter_type": "LLAMATUNE",
"space_adapter_config": {
// Optional space adapter configuration.
// The JSON schema controls the valid properties here.
// In general check the constructor arguments of the specified
// SpaceAdapterType.
"num_low_dims": 10,
"max_unique_values_per_param": 20,
}
// Now starts a collection of key-value pairs that are specific to
// the Optimizer class chosen.
// Override the default optimizer type.
// Must be one of the mlos_core OptimizerType enum values.
"optimizer_type": "SMAC", // e.g., "RANDOM", "FLAML", "SMAC"
// Optionally provide some additional configuration options for the optimizer.
// Note: these are optimizer-specific and may not be supported by all optimizers.
// For instance the following example is only supported by the SMAC optimizer.
// In general, for MlosCoreOptimizers you can look at the arguments
// to the corresponding OptimizerType in the mlos_core module.
"n_random_init": 20,
"n_random_probability": 0.25, // increased to prioritize exploration
}
However, it can also be as simple as the following and sane defaults will be
used for the rest.
.. code-block:: json
{
"class": "mlos_bench.optimizers.MlosCoreOptimizer"
}
Notes
-----
The full set of supported properties is specified in the `JSON schemas for optimizers
<https://github.com/microsoft/MLOS/blob/main/mlos_bench/mlos_bench/config/schemas/optimizers/>`_.
and can be seen in some of the `test examples in the source tree
<https://github.com/microsoft/MLOS/tree/main/mlos_bench/mlos_bench/tests/config/schemas/optimizers/test-cases/good/>`_.
See Also
--------
:py:mod:`mlos_bench.config` :
For more information about the mlos_bench configuration system.
Examples
--------
Note: All of the examples in this module are expressed in Python for testing
purposes.
>>> # Load tunables from a JSON string.
>>> # Note: normally these would be automatically loaded from the Environment(s)'s
>>> # `include_tunables` config parameter.
>>> #
>>> import json5 as json
>>> import mlos_core.optimizers
>>> from mlos_bench.environments.status import Status
>>> from mlos_bench.services.config_persistence import ConfigPersistenceService
>>> service = ConfigPersistenceService()
>>> json_config = '''
... {
... "group_1": {
... "cost": 1,
... "params": {
... "flags": {
... "type": "categorical",
... "values": ["on", "off", "auto"],
... "default": "auto",
... },
... "int_param": {
... "type": "int",
... "range": [1, 100],
... "default": 10,
... },
... "float_param": {
... "type": "float",
... "range": [0, 100],
... "default": 50.0,
... }
... }
... }
... }
... '''
>>> tunables = service.load_tunables(jsons=[json_config])
>>> # Here's the defaults:
>>> tunables.get_param_values()
{'flags': 'auto', 'int_param': 10, 'float_param': 50.0}
>>> # Load a JSON config string for an MlosCoreOptimizer.
>>> # You must specify an mlos_bench Optimizer class in the JSON config.
>>> # (e.g., "mlos_bench.optimizers.mlos_core_optimizer.MlosCoreOptimizer")
>>> # All optimizers support the following config properties at a minimum:
>>> sorted(Optimizer.BASE_SUPPORTED_CONFIG_PROPS)
['max_suggestions', 'optimization_targets', 'seed', 'start_with_defaults']
>>> # When using the MlosCoreOptimizer, we can also specify some additional
>>> # properties, for instance the optimizer_type, which is one of the mlos_core
>>> # OptimizerType enum values:
>>> print([member.name for member in mlos_core.optimizers.OptimizerType])
['RANDOM', 'FLAML', 'SMAC']
>>> # We can also specify an optional space_adapter_type, which can sometimes
>>> # help manipulate the configuration space to something more manageable.
>>> print([member.name for member in mlos_core.spaces.adapters.SpaceAdapterType])
['IDENTITY', 'LLAMATUNE']
>>> # Here's an example JSON config for an MlosCoreOptimizer.
>>> optimizer_json_config = '''
... {
... "class": "mlos_bench.optimizers.mlos_core_optimizer.MlosCoreOptimizer",
... "description": "MlosCoreOptimizer",
... "config": {
... "max_suggestions": 1000,
... "optimization_targets": {
... "throughput": "max",
... "cost": "min",
... },
... "start_with_defaults": true,
... "seed": 42,
... // Override the default optimizer type
... // Must be one of the mlos_core OptimizerType enum values.
... "optimizer_type": "SMAC",
... // Optionally provide some additional configuration options for the optimizer.
... // Note: these are optimizer-specific and may not be supported by all optimizers.
... "n_random_init": 25,
... "n_random_probability": 0.01,
... // Optionally override the default space adapter type
... // Must be one of the mlos_core SpaceAdapterType enum values.
... // LlamaTune is a method for automatically doing space reduction
... // from the original space.
... /*
... "space_adapter_type": "LLAMATUNE",
... "space_adapter_config": {
... // Note: these values are probably too low,
... // but it's just for demonstration.
... "num_low_dims": 2,
... "max_unique_values_per_param": 10,
... }
... */
... }
... }
... '''
>>> config = json.loads(optimizer_json_config)
>>> optimizer = service.build_optimizer(
... tunables=tunables,
... service=service,
... config=config,
... )
>>> suggested_config_1 = optimizer.suggest()
>>> # Default should be suggested first, per json config.
>>> suggested_config_1.get_param_values()
{'flags': 'auto', 'int_param': 10, 'float_param': 50.0}
>>> # Get another suggestion.
>>> # Note that multiple suggestions can be pending prior to
>>> # registering their scores, supporting parallel trial execution.
>>> suggested_config_2 = optimizer.suggest()
>>> suggested_config_2.get_param_values()
{'flags': 'auto', 'int_param': 99, 'float_param': 5.8570134453475}
>>> # Register some scores.
>>> # Note: Maximization problems track negative scores to produce a minimization problem.
>>> optimizer.register(suggested_config_1, Status.SUCCEEDED, {"throughput": 42, "cost": 19})
{'throughput': -42.0, 'cost': 19.0}
>>> optimizer.register(suggested_config_2, Status.SUCCEEDED, {"throughput": 7, "cost": 17.2})
{'throughput': -7.0, 'cost': 17.2}
>>> (best_score, best_config) = optimizer.get_best_observation()
>>> best_score
{'throughput': 42.0, 'cost': 19.0}
>>> assert best_config == suggested_config_1
"""
from mlos_bench.optimizers.base_optimizer import Optimizer

Просмотреть файл

@ -2,8 +2,14 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
#
"""Base class for an interface between the benchmarking framework and mlos_core
optimizers.
"""
Base class for an interface between the benchmarking framework and :py:mod:`mlos_core`
optimizers and other config suggestion methods.
See Also
--------
mlos_bench.optimizers :
For more information on the available optimizers and their usage.
"""
import logging
@ -19,16 +25,16 @@ from mlos_bench.config.schemas import ConfigSchema
from mlos_bench.environments.status import Status
from mlos_bench.optimizers.convert_configspace import tunable_groups_to_configspace
from mlos_bench.services.base_service import Service
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
from mlos_bench.util import strtobool
_LOG = logging.getLogger(__name__)
class Optimizer(ContextManager, metaclass=ABCMeta): # pylint: disable=too-many-instance-attributes
"""An abstract interface between the benchmarking framework and mlos_core
optimizers.
"""An abstract interface between the benchmarking framework and :py:mod:`mlos_core`
optimizers and other config suggestion methods.
"""
# See Also: mlos_bench/mlos_bench/config/schemas/optimizers/optimizer-schema.json
@ -312,6 +318,8 @@ class Optimizer(ContextManager, metaclass=ABCMeta): # pylint: disable=too-many-
)
if status.is_succeeded() == (score is None): # XOR
raise ValueError("Status and score must be consistent.")
# FIXME: should maximization problems return -score values to the user, or
# keep that as an internal nuance.
return self._get_scores(status, score)
def _get_scores(

Просмотреть файл

@ -2,10 +2,12 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
#
"""Functions to convert TunableGroups to ConfigSpace for use with the mlos_core
optimizers.
"""Functions to convert :py:class:`.TunableGroups` that :py:mod:`mlos_bench` uses to to
:py:class:`ConfigSpace.ConfigurationSpace` for use with the
:py:mod:`mlos_core.optimizers`.
"""
import enum
import logging
from collections.abc import Hashable
@ -23,8 +25,9 @@ from ConfigSpace import (
from ConfigSpace.hyperparameters import NumericalHyperparameter
from ConfigSpace.types import NotSet
from mlos_bench.tunables.tunable import Tunable, TunableValue
from mlos_bench.tunables.tunable import Tunable
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
from mlos_bench.util import try_parse_val
from mlos_core.spaces.converters.util import (
QUANTIZATION_BINS_META_KEY,
@ -34,14 +37,9 @@ from mlos_core.spaces.converters.util import (
_LOG = logging.getLogger(__name__)
class TunableValueKind:
"""
Enum for the kind of the tunable value (special or not).
class TunableValueKind(enum.Enum):
"""Enum for the kind of the tunable value (special or not)."""
It is not a true enum because ConfigSpace wants string values.
"""
# pylint: disable=too-few-public-methods
SPECIAL = "special"
RANGE = "range"
@ -170,19 +168,19 @@ def _tunable_to_configspace(
),
CategoricalHyperparameter(
name=type_name,
choices=[TunableValueKind.SPECIAL, TunableValueKind.RANGE],
choices=[TunableValueKind.SPECIAL.value, TunableValueKind.RANGE.value],
weights=switch_weights,
default_value=TunableValueKind.SPECIAL,
default_value=TunableValueKind.SPECIAL.value,
),
]
)
conf_space.add(
[
EqualsCondition(
conf_space[special_name], conf_space[type_name], TunableValueKind.SPECIAL
conf_space[special_name], conf_space[type_name], TunableValueKind.SPECIAL.value
),
EqualsCondition(
conf_space[tunable.name], conf_space[type_name], TunableValueKind.RANGE
conf_space[tunable.name], conf_space[type_name], TunableValueKind.RANGE.value
),
]
)
@ -242,10 +240,10 @@ def tunable_values_to_configuration(tunables: TunableGroups) -> Configuration:
if tunable.special:
(special_name, type_name) = special_param_names(tunable.name)
if tunable.value in tunable.special:
values[type_name] = TunableValueKind.SPECIAL
values[type_name] = TunableValueKind.SPECIAL.value
values[special_name] = tunable.value
else:
values[type_name] = TunableValueKind.RANGE
values[type_name] = TunableValueKind.RANGE.value
values[tunable.name] = tunable.value
else:
values[tunable.name] = tunable.value
@ -263,7 +261,7 @@ def configspace_data_to_tunable_values(data: dict) -> dict[str, TunableValue]:
specials = [special_param_name_strip(k) for k in data.keys() if special_param_name_is_temp(k)]
for k in specials:
(special_name, type_name) = special_param_names(k)
if data[type_name] == TunableValueKind.SPECIAL:
if data[type_name] == TunableValueKind.SPECIAL.value:
data[k] = data[special_name]
if special_name in data:
del data[special_name]
@ -278,7 +276,8 @@ def special_param_names(name: str) -> tuple[str, str]:
Generate the names of the auxiliary hyperparameters that correspond to a tunable
that can have special values.
NOTE: `!` characters are currently disallowed in Tunable names in order handle this logic.
NOTE: ``!`` characters are currently disallowed in :py:class:`.Tunable` names in
order handle this logic.
Parameters
----------
@ -299,7 +298,8 @@ def special_param_name_is_temp(name: str) -> bool:
"""
Check if name corresponds to a temporary ConfigSpace parameter.
NOTE: `!` characters are currently disallowed in Tunable names in order handle this logic.
NOTE: ``!`` characters are currently disallowed in :py:class:`.Tunable` names in
order handle this logic.
Parameters
----------
@ -318,7 +318,8 @@ def special_param_name_strip(name: str) -> str:
"""
Remove the temporary suffix from a special parameter name.
NOTE: `!` characters are currently disallowed in Tunable names in order handle this logic.
NOTE: ``!`` characters are currently disallowed in :py:class:`.Tunable` names in
order handle this logic.
Parameters
----------

Просмотреть файл

@ -2,7 +2,104 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
#
"""Grid search optimizer for mlos_bench."""
"""
Grid search Optimizer for mlos_bench.
Grid search is a simple optimizer that exhaustively searches the configuration space.
To do this it generates a grid of configurations to try, and then suggests them one by one.
Therefore, the number of configurations to try is the product of the
:py:attr:`~mlos_bench.tunables.tunable.Tunable.cardinality` of each of the
:py:mod:`~mlos_bench.tunables`.
(i.e., non :py:attr:`quantized <mlos_bench.tunables.tunable.Tunable.quantization_bins>`
tunables are not supported).
Examples
--------
>>> # Load tunables from a JSON string.
>>> # Note: normally these would be automatically loaded from the Environment(s)'s
>>> # `include_tunables` config parameter.
>>> #
>>> import json5 as json
>>> from mlos_bench.environments.status import Status
>>> from mlos_bench.services.config_persistence import ConfigPersistenceService
>>> service = ConfigPersistenceService()
>>> json_config = '''
... {
... "group_1": {
... "cost": 1,
... "params": {
... "colors": {
... "type": "categorical",
... "values": ["red", "blue", "green"],
... "default": "green",
... },
... "int_param": {
... "type": "int",
... "range": [1, 3],
... "default": 2,
... },
... "float_param": {
... "type": "float",
... "range": [0, 1],
... "default": 0.5,
... // Quantize the range into 3 bins
... "quantization_bins": 3,
... }
... }
... }
... }
... '''
>>> tunables = service.load_tunables(jsons=[json_config])
>>> # Check the defaults:
>>> tunables.get_param_values()
{'colors': 'green', 'int_param': 2, 'float_param': 0.5}
>>> # Now create a GridSearchOptimizer from a JSON config string.
>>> optimizer_json_config = '''
... {
... "class": "mlos_bench.optimizers.grid_search_optimizer.GridSearchOptimizer",
... "description": "GridSearchOptimizer",
... "config": {
... "max_suggestions": 100,
... "optimization_targets": {"score": "max"},
... "start_with_defaults": true
... }
... }
... '''
>>> config = json.loads(optimizer_json_config)
>>> grid_search_optimizer = service.build_optimizer(
... tunables=tunables,
... service=service,
... config=config,
... )
>>> # Should have 3 values for each of the 3 tunables
>>> len(list(grid_search_optimizer.pending_configs))
27
>>> next(grid_search_optimizer.pending_configs)
{'colors': 'red', 'float_param': 0, 'int_param': 1}
>>> suggested_config_1 = grid_search_optimizer.suggest()
>>> # Default should be suggested first, per json config.
>>> suggested_config_1.get_param_values()
{'colors': 'green', 'int_param': 2, 'float_param': 0.5}
>>> # Get another suggestion.
>>> # Note that multiple suggestions can be pending prior to
>>> # registering their scores, supporting parallel trial execution.
>>> suggested_config_2 = grid_search_optimizer.suggest()
>>> suggested_config_2.get_param_values()
{'colors': 'red', 'int_param': 1, 'float_param': 0.0}
>>> # Register some scores.
>>> # Note: Maximization problems track negative scores to produce a minimization problem.
>>> grid_search_optimizer.register(suggested_config_1, Status.SUCCEEDED, {"score": 42})
{'score': -42.0}
>>> grid_search_optimizer.register(suggested_config_2, Status.SUCCEEDED, {"score": 7})
{'score': -7.0}
>>> (best_score, best_config) = grid_search_optimizer.get_best_observation()
>>> best_score
{'score': 42.0}
>>> assert best_config == suggested_config_1
"""
import logging
from collections.abc import Iterable, Sequence
@ -15,14 +112,21 @@ from mlos_bench.environments.status import Status
from mlos_bench.optimizers.convert_configspace import configspace_data_to_tunable_values
from mlos_bench.optimizers.track_best_optimizer import TrackBestOptimizer
from mlos_bench.services.base_service import Service
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
_LOG = logging.getLogger(__name__)
class GridSearchOptimizer(TrackBestOptimizer):
"""Grid search optimizer."""
"""
Grid search optimizer.
See :py:mod:`above <mlos_bench.optimizers.grid_search_optimizer>` for more details.
"""
MAX_CONFIGS = 10000
"""Maximum number of configurations to enumerate."""
def __init__(
self,
@ -52,7 +156,7 @@ class GridSearchOptimizer(TrackBestOptimizer):
raise ValueError(
f"Unquantized tunables are not supported for grid search: {self._tunables}"
)
if size > 10000:
if size > self.MAX_CONFIGS:
_LOG.warning(
"Large number %d of config points requested for grid search: %s",
size,

Просмотреть файл

@ -3,21 +3,99 @@
# Licensed under the MIT License.
#
"""
Manual config suggestor (Optimizer) for mlos_bench that proposes an explicit sequence of
Manual config suggester (Optimizer) for mlos_bench that proposes an explicit sequence of
configurations.
This is useful for testing and validation, as it allows you to run a sequence of
configurations in a cyclic fashion.
TODO: Add an example configuration.
Examples
--------
>>> # Load tunables from a JSON string.
>>> # Note: normally these would be automatically loaded from the Environment(s)'s
>>> # `include_tunables` config parameter.
>>> #
>>> import json5 as json
>>> from mlos_bench.environments.status import Status
>>> from mlos_bench.services.config_persistence import ConfigPersistenceService
>>> service = ConfigPersistenceService()
>>> json_config = '''
... {
... "group_1": {
... "cost": 1,
... "params": {
... "colors": {
... "type": "categorical",
... "values": ["red", "blue", "green"],
... "default": "green",
... },
... "int_param": {
... "type": "int",
... "range": [1, 3],
... "default": 2,
... },
... "float_param": {
... "type": "float",
... "range": [0, 1],
... "default": 0.5,
... // Quantize the range into 3 bins
... "quantization_bins": 3,
... }
... }
... }
... }
... '''
>>> tunables = service.load_tunables(jsons=[json_config])
>>> # Check the defaults:
>>> tunables.get_param_values()
{'colors': 'green', 'int_param': 2, 'float_param': 0.5}
>>> # Now create a ManualOptimizer from a JSON config string.
>>> optimizer_json_config = '''
... {
... "class": "mlos_bench.optimizers.manual_optimizer.ManualOptimizer",
... "description": "ManualOptimizer",
... "config": {
... "max_cycles": 3,
... "tunable_values_cycle": [
... {"colors": "red", "int_param": 1, "float_param": 0.0},
... {"colors": "blue", "int_param": 3, "float_param": 1.0},
... // special case: {} - represents the defaults, without
... // having to copy them from the tunables JSON
... // (which is presumably specified elsewhere)
... {},
... ],
... }
... }
... '''
>>> config = json.loads(optimizer_json_config)
>>> optimizer = service.build_optimizer(
... tunables=tunables,
... service=service,
... config=config,
... )
>>> # Run the optimizer.
>>> # Note that the cycle will repeat 3 times, as specified in the config.
>>> while optimizer.not_converged():
... suggestion = optimizer.suggest()
... print(suggestion.get_param_values())
{'colors': 'red', 'int_param': 1, 'float_param': 0.0}
{'colors': 'blue', 'int_param': 3, 'float_param': 1.0}
{'colors': 'green', 'int_param': 2, 'float_param': 0.5}
{'colors': 'red', 'int_param': 1, 'float_param': 0.0}
{'colors': 'blue', 'int_param': 3, 'float_param': 1.0}
{'colors': 'green', 'int_param': 2, 'float_param': 0.5}
{'colors': 'red', 'int_param': 1, 'float_param': 0.0}
{'colors': 'blue', 'int_param': 3, 'float_param': 1.0}
{'colors': 'green', 'int_param': 2, 'float_param': 0.5}
"""
import logging
from mlos_bench.optimizers.mock_optimizer import MockOptimizer
from mlos_bench.services.base_service import Service
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
_LOG = logging.getLogger(__name__)

Просмотреть файл

@ -2,7 +2,139 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
#
"""A wrapper for mlos_core optimizers for mlos_bench."""
"""
A wrapper for :py:mod:`mlos_core.optimizers` for :py:mod:`mlos_bench`.
Config
------
The JSON config for an :py:class:`.MlosCoreOptimizer` generally takes the
following basic structure:
See Also
--------
:py:mod:`mlos_bench.optimizers` :
Another working example of an :py:class:`.MlosCoreOptimizer`.
:py:mod:`mlos_core.optimizers` :
Documentation on the underlying mlos_core Optimizers.
:py:mod:`mlos_core.spaces.adapters` :
Documentation on the underlying mlos_core SpaceAdapters.
Examples
--------
>>> # Load tunables from a JSON string.
>>> # Note: normally these would be automatically loaded from the Environment(s)'s
>>> # `include_tunables` config parameter.
>>> #
>>> import json5 as json
>>> import mlos_core.optimizers
>>> from mlos_bench.environments.status import Status
>>> from mlos_bench.services.config_persistence import ConfigPersistenceService
>>> service = ConfigPersistenceService()
>>> json_config = '''
... {
... "group_1": {
... "cost": 1,
... "params": {
... "flags": {
... "type": "categorical",
... "values": ["on", "off", "auto"],
... "default": "auto",
... },
... "int_param": {
... "type": "int",
... "range": [1, 100],
... "default": 10,
... },
... "float_param": {
... "type": "float",
... "range": [0, 100],
... "default": 50.0,
... }
... }
... }
... }
... '''
>>> tunables = service.load_tunables(jsons=[json_config])
>>> # Here's the defaults:
>>> tunables.get_param_values()
{'flags': 'auto', 'int_param': 10, 'float_param': 50.0}
>>> # When using the MlosCoreOptimizer, we can also specify some additional
>>> # properties, for instance the optimizer_type, which is one of the mlos_core
>>> # OptimizerType enum values:
>>> print([member.name for member in mlos_core.optimizers.OptimizerType])
['RANDOM', 'FLAML', 'SMAC']
>>> # We can also specify
>>> # properties, for instance the optimizer_type, which is one of the mlos_core
>>> # OptimizerType enum values:
>>> print([member.name for member in mlos_core.optimizers.OptimizerType])
['RANDOM', 'FLAML', 'SMAC']
>>> # Here's an example JSON config for an MlosCoreOptimizer.
>>> optimizer_json_config = '''
... {
... "class": "mlos_bench.optimizers.mlos_core_optimizer.MlosCoreOptimizer",
... "description": "MlosCoreOptimizer",
... "config": {
... "max_suggestions": 1000,
... "optimization_targets": {
... "throughput": "max",
... "cost": "min",
... },
... "start_with_defaults": true,
... "seed": 42,
... // Override the default optimizer type
... // Must be one of the mlos_core OptimizerType enum values.
... "optimizer_type": "SMAC",
... // Optionally provide some additional configuration options for the optimizer.
... // Note: these are optimizer-specific and may not be supported by all optimizers.
... "n_random_init": 25,
... "n_random_probability": 0.01,
... // Optionally override the default space adapter type
... // Must be one of the mlos_core SpaceAdapterType enum values.
... // LlamaTune is a method for automatically doing space reduction
... // from the original space.
... "space_adapter_type": "LLAMATUNE",
... "space_adapter_config": {
... // Note: these values are probably too low,
... // but it's just for demonstration.
... "num_low_dims": 2,
... "max_unique_values_per_param": 10,
... }
... }
... }
... '''
>>> config = json.loads(optimizer_json_config)
>>> optimizer = service.build_optimizer(
... tunables=tunables,
... service=service,
... config=config,
... )
>>> suggested_config_1 = optimizer.suggest()
>>> # Normally default values should be suggested first, per json config.
>>> # However, since LlamaTune is being employed here, the first suggestion may
>>> # be projected to a slightly different space.
>>> suggested_config_1.get_param_values()
{'flags': 'auto', 'int_param': 1, 'float_param': 55.5555555555556}
>>> # Get another suggestion.
>>> # Note that multiple suggestions can be pending prior to
>>> # registering their scores, supporting parallel trial execution.
>>> suggested_config_2 = optimizer.suggest()
>>> suggested_config_2.get_param_values()
{'flags': 'on', 'int_param': 78, 'float_param': 88.8888888888889}
>>> # Register some scores.
>>> # Note: Maximization problems track negative scores to produce a minimization problem.
>>> optimizer.register(suggested_config_1, Status.SUCCEEDED, {"throughput": 42, "cost": 19})
{'throughput': -42.0, 'cost': 19.0}
>>> optimizer.register(suggested_config_2, Status.SUCCEEDED, {"throughput": 7, "cost": 17.2})
{'throughput': -7.0, 'cost': 17.2}
>>> (best_score, best_config) = optimizer.get_best_observation()
>>> best_score
{'throughput': 42.0, 'cost': 19.0}
>>> assert best_config == suggested_config_1
"""
import logging
import os
@ -20,8 +152,8 @@ from mlos_bench.optimizers.convert_configspace import (
special_param_names,
)
from mlos_bench.services.base_service import Service
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
from mlos_core.data_classes import Observations
from mlos_core.optimizers import (
DEFAULT_OPTIMIZER_TYPE,
@ -77,6 +209,12 @@ class MlosCoreOptimizer(Optimizer):
if space_adapter_type is not None:
space_adapter_type = getattr(SpaceAdapterType, space_adapter_type)
assert isinstance(space_adapter_type, SpaceAdapterType)
if space_adapter_type == SpaceAdapterType.LLAMATUNE:
# This is probably a sane default, especially when
# bulk_registering old configs (e.g., Experiment resume), but is
# not currently exposed in the config schema.
space_adapter_config["use_approximate_reverse_mapping"] = True
self._opt: BaseOptimizer = OptimizerFactory.create(
parameter_space=self.config_space,
@ -181,8 +319,8 @@ class MlosCoreOptimizer(Optimizer):
(special_name, type_name) = special_param_names(tunable.name)
tunables_names += [special_name, type_name]
is_special = df_configs[tunable.name].apply(tunable.special.__contains__)
df_configs[type_name] = TunableValueKind.RANGE
df_configs.loc[is_special, type_name] = TunableValueKind.SPECIAL
df_configs[type_name] = TunableValueKind.RANGE.value
df_configs.loc[is_special, type_name] = TunableValueKind.SPECIAL.value
if tunable.type == "int":
# Make int column NULLABLE:
df_configs[tunable.name] = df_configs[tunable.name].astype("Int64")

Просмотреть файл

@ -2,7 +2,14 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
#
"""Mock optimizer for mlos_bench."""
"""
Mock optimizer for mlos_bench.
Mostly intended for testing and validation. This optimizer produces random suggestions.
The range of the suggestions can be controlled by a config.
See the test cases or example json configs for more details.
"""
import logging
import random
@ -11,8 +18,9 @@ from collections.abc import Callable, Sequence
from mlos_bench.environments.status import Status
from mlos_bench.optimizers.track_best_optimizer import TrackBestOptimizer
from mlos_bench.services.base_service import Service
from mlos_bench.tunables.tunable import Tunable, TunableValue
from mlos_bench.tunables.tunable import Tunable
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
_LOG = logging.getLogger(__name__)

Просмотреть файл

@ -2,7 +2,87 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
#
"""No-op optimizer for mlos_bench that proposes a single configuration."""
"""
No-op optimizer for mlos_bench that proposes a single configuration.
Explicit configs (partial or full) are possible using configuration files.
Examples
--------
>>> # Load tunables from a JSON string.
>>> # Note: normally these would be automatically loaded from the Environment(s)'s
>>> # `include_tunables` config parameter.
>>> #
>>> import json5 as json
>>> from mlos_bench.environments.status import Status
>>> from mlos_bench.services.config_persistence import ConfigPersistenceService
>>> service = ConfigPersistenceService()
>>> json_config = '''
... {
... "group_1": {
... "cost": 1,
... "params": {
... "colors": {
... "type": "categorical",
... "values": ["red", "blue", "green"],
... "default": "green",
... },
... "int_param": {
... "type": "int",
... "range": [1, 3],
... "default": 2,
... },
... "float_param": {
... "type": "float",
... "range": [0, 1],
... "default": 0.5,
... // Quantize the range into 3 bins
... "quantization_bins": 3,
... }
... }
... }
... }
... '''
>>> tunables = service.load_tunables(jsons=[json_config])
>>> # Check the defaults:
>>> tunables.get_param_values()
{'colors': 'green', 'int_param': 2, 'float_param': 0.5}
>>> # Load a JSON config of some tunable values to explicitly test.
>>> # Normally these would be provided by the
>>> # `mlos_bench --tunable-values`
>>> # CLI option.
>>> tunable_values_json = '''
... {
... "colors": "red",
... "int_param": 1,
... "float_param": 0.0
... }
... '''
>>> tunable_values = json.loads(tunable_values_json)
>>> tunables.assign(tunable_values).get_param_values()
{'colors': 'red', 'int_param': 1, 'float_param': 0.0}
>>> assert not tunables.is_defaults()
>>> # Now create a OneShotOptimizer from a JSON config string.
>>> optimizer_json_config = '''
... {
... "class": "mlos_bench.optimizers.one_shot_optimizer.OneShotOptimizer",
... }
... '''
>>> config = json.loads(optimizer_json_config)
>>> optimizer = service.build_optimizer(
... tunables=tunables,
... service=service,
... config=config,
... )
>>> # Run the optimizer.
>>> # Note that it will only run for a single iteration and return the values we set.
>>> while optimizer.not_converged():
... suggestion = optimizer.suggest()
... print(suggestion.get_param_values())
{'colors': 'red', 'int_param': 1, 'float_param': 0.0}
"""
import logging

Просмотреть файл

@ -2,7 +2,7 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
#
"""Mock optimizer for mlos_bench."""
"""TrackBestOptimizer base class for mlos_bench."""
import logging
from abc import ABCMeta
@ -10,8 +10,8 @@ from abc import ABCMeta
from mlos_bench.environments.status import Status
from mlos_bench.optimizers.base_optimizer import Optimizer
from mlos_bench.services.base_service import Service
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
_LOG = logging.getLogger(__name__)

Просмотреть файл

@ -2,9 +2,14 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
#
"""Helper functions to load, instantiate, and serialize Python objects that encapsulate
a benchmark :py:class:`.Environment`, :py:mod:`~mlos_bench.tunables`,
:py:class:`.Service` functions, etc from JSON configuration files and strings.
"""
Helper functions to load, instantiate, and serialize Python objects that encapsulate a
benchmark :py:class:`.Environment`, :py:mod:`~mlos_bench.tunables`, :py:class:`.Service`
functions, etc from JSON configuration files and strings.
See Also
--------
mlos_bench.config : Overview of the configuration system.
"""
import logging
@ -21,8 +26,8 @@ from mlos_bench.environments.base_environment import Environment
from mlos_bench.optimizers.base_optimizer import Optimizer
from mlos_bench.services.base_service import Service
from mlos_bench.services.types.config_loader_type import SupportsConfigLoading
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
from mlos_bench.util import (
instantiate_from_config,
merge_parameters,
@ -417,7 +422,7 @@ class ConfigPersistenceService(Service, SupportsConfigLoading):
parent_args: dict[str, TunableValue] | None = None,
service: Service | None = None,
) -> Environment:
# pylint: disable=too-many-arguments,too-many-positional-arguments
# pylint: disable=too-many-arguments
"""
Factory method for a new :py:class:`.Environment` with a given config.
@ -589,7 +594,7 @@ class ConfigPersistenceService(Service, SupportsConfigLoading):
parent_args: dict[str, TunableValue] | None = None,
service: Service | None = None,
) -> Environment:
# pylint: disable=too-many-arguments,too-many-positional-arguments
# pylint: disable=too-many-arguments
"""
Load and build new :py:class:`.Environment` from the config file or JSON string.
@ -611,6 +616,10 @@ class ConfigPersistenceService(Service, SupportsConfigLoading):
-------
env : Environment
A new benchmarking environment.
See Also
--------
mlos_bench.environments : Examples of environment configurations.
"""
config = self.load_config(json, ConfigSchema.ENVIRONMENT)
assert isinstance(config, dict)
@ -624,7 +633,7 @@ class ConfigPersistenceService(Service, SupportsConfigLoading):
parent_args: dict[str, TunableValue] | None = None,
service: Service | None = None,
) -> list[Environment]:
# pylint: disable=too-many-arguments,too-many-positional-arguments
# pylint: disable=too-many-arguments
"""
Load and build a list of Environments from the config file or JSON string.
@ -647,6 +656,10 @@ class ConfigPersistenceService(Service, SupportsConfigLoading):
-------
env : list[Environment]
A list of new benchmarking environments.
See Also
--------
mlos_bench.environments : Examples of environment configurations.
"""
config = self.load_config(json, ConfigSchema.ENVIRONMENT)
return [self.build_environment(config, tunables, global_config, parent_args, service)]
@ -679,6 +692,10 @@ class ConfigPersistenceService(Service, SupportsConfigLoading):
-------
service : Service
A collection of service methods.
See Also
--------
mlos_bench.services : Examples of service configurations.
"""
_LOG.info("Load services: %s parent: %s", jsons, parent.__class__.__name__)
service = Service({}, global_config, parent)
@ -711,6 +728,10 @@ class ConfigPersistenceService(Service, SupportsConfigLoading):
-------
tunables : TunableGroups
The larger collection of tunable parameters.
See Also
--------
mlos_bench.tunables : Examples of tunable parameter configurations.
"""
_LOG.info("Load tunables: '%s'", jsons)
if parent is None:

Просмотреть файл

@ -20,7 +20,7 @@ from mlos_bench.services.local.temp_dir_context import TempDirContextService
from mlos_bench.services.types.local_exec_type import SupportsLocalExec
if TYPE_CHECKING:
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_types import TunableValue
_LOG = logging.getLogger(__name__)

Просмотреть файл

@ -10,7 +10,7 @@ from collections.abc import Iterable
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
from mlos_bench.config.schemas.config_schemas import ConfigSchema
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_types import TunableValue
# Avoid's circular import issues.
if TYPE_CHECKING:

Просмотреть файл

@ -11,7 +11,7 @@ import tempfile
from collections.abc import Iterable, Mapping
from typing import Protocol, runtime_checkable
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_types import TunableValue
@runtime_checkable

Просмотреть файл

@ -10,8 +10,9 @@ tunable parameters).
See Also
--------
:py:mod:`mlos_bench.storage` : The base storage module for mlos_bench, which
includes some basic examples in the documentation.
:py:mod:`mlos_bench.storage` :
The base storage module for mlos_bench, which includes some basic examples in
the documentation.
"""
from abc import ABCMeta, abstractmethod
from datetime import datetime
@ -23,7 +24,7 @@ from pytz import UTC
from mlos_bench.environments.status import Status
from mlos_bench.storage.base_tunable_config_data import TunableConfigData
from mlos_bench.storage.util import kv_df_to_dict
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_types import TunableValue
if TYPE_CHECKING:
from mlos_bench.storage.base_tunable_config_trial_group_data import (

Просмотреть файл

@ -10,8 +10,9 @@ be used by one or more trials.
See Also
--------
:py:mod:`mlos_bench.storage` : The base storage module for mlos_bench, which
includes some basic examples in the documentation.
:py:mod:`mlos_bench.storage` :
The base storage module for mlos_bench, which includes some basic examples in
the documentation.
"""
from abc import ABCMeta, abstractmethod
from typing import Any
@ -19,7 +20,7 @@ from typing import Any
import pandas
from mlos_bench.storage.util import kv_df_to_dict
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_types import TunableValue
class TunableConfigData(metaclass=ABCMeta):

Просмотреть файл

@ -10,8 +10,9 @@ easier analysis.
See Also
--------
:py:mod:`mlos_bench.storage` : The base storage module for mlos_bench, which
includes some basic examples in the documentation.
:py:mod:`mlos_bench.storage` :
The base storage module for mlos_bench, which includes some basic examples in
the documentation.
"""
from abc import ABCMeta, abstractmethod

Просмотреть файл

@ -24,8 +24,9 @@ tree for some configuration examples.
See Also
--------
:py:mod:`mlos_bench.storage` : The base storage module for mlos_bench, which
includes some basic examples in the documentation.
:py:mod:`mlos_bench.storage` :
The base storage module for mlos_bench, which includes some basic examples in
the documentation.
"""
from mlos_bench.storage.sql.storage import SqlStorage

Просмотреть файл

@ -7,7 +7,7 @@
import pandas
from mlos_bench.tunables.tunable import TunableValue, TunableValueTypeTuple
from mlos_bench.tunables.tunable_types import TunableValue, TunableValueTypeTuple
from mlos_bench.util import try_parse_val

Просмотреть файл

@ -10,8 +10,8 @@ from typing import Any
import pytest
from mlos_bench.environments.base_environment import Environment
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
def check_env_success(

Просмотреть файл

@ -8,7 +8,7 @@
import pytest
from mlos_bench.environments.base_environment import Environment
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_types import TunableValue
_GROUPS = {
"group": ["a", "b"],

Просмотреть файл

@ -14,8 +14,8 @@ from mlos_bench.services.config_persistence import ConfigPersistenceService
from mlos_bench.tests import requires_docker
from mlos_bench.tests.environments import check_env_success
from mlos_bench.tests.services.remote.ssh import SshTestServerInfo
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
@requires_docker

Просмотреть файл

@ -13,8 +13,8 @@ import pytest
from mlos_bench.environments.status import Status
from mlos_bench.optimizers.grid_search_optimizer import GridSearchOptimizer
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
# pylint: disable=redefined-outer-name

Просмотреть файл

@ -11,7 +11,7 @@ from mlos_bench.environments.status import Status
from mlos_bench.optimizers.base_optimizer import Optimizer
from mlos_bench.optimizers.mlos_core_optimizer import MlosCoreOptimizer
from mlos_bench.optimizers.mock_optimizer import MockOptimizer
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_types import TunableValue
# pylint: disable=redefined-outer-name

Просмотреть файл

@ -11,7 +11,7 @@ import pytest
from mlos_bench.services.config_persistence import ConfigPersistenceService
from mlos_bench.services.local.local_exec import LocalExecService
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_types import TunableValue
from mlos_bench.util import path_join
# pylint: disable=redefined-outer-name

Просмотреть файл

@ -13,7 +13,7 @@ from mlos_bench.services.local.temp_dir_context import TempDirContextService
from mlos_bench.services.types.local_exec_type import SupportsLocalExec
if TYPE_CHECKING:
from mlos_bench.tunables.tunable import TunableValue
from mlos_bench.tunables.tunable_types import TunableValue
_LOG = logging.getLogger(__name__)

Просмотреть файл

@ -7,7 +7,8 @@
import json5 as json
import pytest
from mlos_bench.tunables.tunable import Tunable, TunableValueTypeName
from mlos_bench.tunables.tunable import Tunable
from mlos_bench.tunables.tunable_types import TunableValueTypeName
def test_tunable_name() -> None:

Просмотреть файл

@ -7,7 +7,8 @@
import json5 as json
import pytest
from mlos_bench.tunables.tunable import Tunable, TunableValueTypeName
from mlos_bench.tunables.tunable import Tunable
from mlos_bench.tunables.tunable_types import TunableValueTypeName
def test_categorical_distribution() -> None:

Просмотреть файл

@ -21,8 +21,8 @@ from mlos_bench.optimizers.convert_configspace import (
special_param_names,
tunable_groups_to_configspace,
)
from mlos_bench.tunables.tunable import DistributionName
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import DistributionName
_CS_HYPERPARAMETER = {
("float", "beta"): BetaFloatHyperparameter,

Просмотреть файл

@ -87,9 +87,9 @@ def configuration_space() -> ConfigurationSpace:
),
CategoricalHyperparameter(
name=kernel_sched_migration_cost_ns_type,
choices=[TunableValueKind.SPECIAL, TunableValueKind.RANGE],
choices=[TunableValueKind.SPECIAL.value, TunableValueKind.RANGE.value],
weights=[0.5, 0.5],
default_value=TunableValueKind.SPECIAL,
default_value=TunableValueKind.SPECIAL.value,
),
]
)
@ -98,12 +98,12 @@ def configuration_space() -> ConfigurationSpace:
EqualsCondition(
spaces[kernel_sched_migration_cost_ns_special],
spaces[kernel_sched_migration_cost_ns_type],
TunableValueKind.SPECIAL,
TunableValueKind.SPECIAL.value,
),
EqualsCondition(
spaces["kernel_sched_migration_cost_ns"],
spaces[kernel_sched_migration_cost_ns_type],
TunableValueKind.RANGE,
TunableValueKind.RANGE.value,
),
]
)

Просмотреть файл

@ -5,8 +5,9 @@
"""Unit tests for deep copy of tunable objects and groups."""
from mlos_bench.tunables.covariant_group import CovariantTunableGroup
from mlos_bench.tunables.tunable import Tunable, TunableValue
from mlos_bench.tunables.tunable import Tunable
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
def test_copy_tunable_int(tunable_int: Tunable) -> None:

Просмотреть файл

@ -30,28 +30,36 @@ Tunable
The :py:class:`~mlos_bench.tunables.tunable.Tunable` class is used to define a
single tunable parameter.
A ``Tunable`` can be a ``categorical`` or numeric (``int`` or ``float``) and always has
at least a domain (``range`` or set of ``values``) and default.
A ``Tunable`` has a :py:attr:`~.Tunable.type` and can be a ``categorical`` or
numeric (``int`` or ``float``) and always has at least a domain
(:py:attr:`~.Tunable.range` or set of :py:attr:`~.Tunable.values`) and a
:py:attr:`~.Tunable.default`.
Each type can also have a number of additional properties that can optionally be set
to help control the sampling of the tunable.
For instance:
- Numeric tunables can have a ``distribution`` property to specify the sampling
distribution. ``log`` sampling can also be enabled for numeric tunables.
- Categorical tunables can have a ``values_weights`` property to specify biased
sampling of the values
- ``special`` values can be marked to indicate that they need more explicit testing
This can be useful for values that indicate "automatic" or "disabled" behavior.
- Numeric tunables can have a :py:attr:`~.Tunable.distribution` property to specify the sampling
distribution. :py:attr:`log <.Tunable.is_log>` sampling can also be enabled for
numeric tunables.
- Categorical tunables can have a :py:attr:`values_weights <.Tunable.weights>`
property to specify biased sampling of the values
- :py:attr:`~.Tunable.special` values can be marked to indicate that they need more
explicit testing. This can be useful for values that indicate "automatic" or
"disabled" behavior.
The full set of supported properties can be found in the `JSON schema for tunable
The :py:class:`~mlos_bench.tunables.tunable.Tunable` class attributes documentation
and :py:class:`~mlos_bench.tunables.tunable_types.TunableDict` class documentation
provides some more information and example on the available properties.
The full set of supported properties is specified in the `JSON schema for tunable
parameters
<https://github.com/microsoft/MLOS/blob/main/mlos_bench/mlos_bench/config/schemas/tunables/tunable-params-schema.json>`_
and seen in some of the `test examples in the source tree
and can be seen in some of the `test examples in the source tree
<https://github.com/microsoft/MLOS/tree/main/mlos_bench/mlos_bench/tests/config/schemas/tunable-params/test-cases/good/>`_.
CovariantGroup
++++++++++++++
CovariantTunableGroup
+++++++++++++++++++++
The :py:class:`~mlos_bench.tunables.covariant_group.CovariantTunableGroup` class is
used to define a group of related tunable parameters that are all configured
@ -65,6 +73,15 @@ TunableGroups
The :py:class:`~mlos_bench.tunables.tunable_groups.TunableGroups` class is used to
define an entire set of tunable parameters (e.g., combined set of covariant groups).
Limitations
-----------
Currently we lack a config language for expressing constraints between tunables
(e.g., ``a < b`` or ``a + b < c``)
This is supported in the underlying :py:mod:`mlos_core` library, but is not yet
exposed in the ``mlos_bench`` config API.
Usage
^^^^^
@ -81,27 +98,34 @@ Then individual covariant groups can be enabled via the ``tunable_params`` and
See the :py:mod:`mlos_bench.config` and :py:mod:`mlos_bench.environments` module
documentation for more information.
In benchmarking-only mode (e.g., without an ``Optimizer`` specified), ``mlos_bench``
can still run with a particular set of ``--tunable-values`` (e.g., a simple
key-value file declaring a set of values to assign to the set of configured tunable
parameters) in order to manually explore a configuration space.
In benchmarking-only mode (e.g., without an ``Optimizer`` specified),
`mlos_bench <../../../mlos_bench.run.usage.html>`_ can still run with a
particular set of ``--tunable-values`` (e.g., a simple key-value file declaring
a set of values to assign to the set of configured tunable parameters) in order
to manually explore a configuration space.
See the :py:mod:`mlos_bench.run` module documentation for more information.
See the :py:class:`~mlos_bench.optimizers.one_shot_optimizer.OneShotOptimizer`
and :py:mod:`mlos_bench.run` module documentation and the for more information.
During an Environment's ``setup`` and ``run`` phases the tunables can be exported to
a JSON file using the ``dump_params_file`` property of the Environment config for
the user scripts to use when configuring the target system.
The ``meta`` property of the tunable config can be used to add additional
information for this step (e.g., a unit suffix to append to the value).
During an Environment's
:py:meth:`~mlos_bench.environments.base_environment.Environment.setup` and
:py:meth:`~mlos_bench.environments.base_environment.Environment.run` phases the
tunables can be exported to a JSON file using the ``dump_params_file`` property
of the Environment config for the user scripts to use when configuring the
target system.
The :py:attr:`~.Tunable.meta` property of the tunable config can be used to add
additional information for this step (e.g., a unit suffix to append to the
value).
See the :py:mod:`mlos_bench.environments` module documentation for more information.
Examples
--------
Here's a short (incomplete) example of some of the TunableGroups JSON configuration
options, expressed in Python (for testing purposes).
However, most of the time you will be loading these from a JSON config file stored
along with the associated Environment config.
Here's a short (incomplete) example of some of the :py:class:`.TunableGroups`
JSON configuration options, expressed in Python (for testing purposes).
However, most of the time you will be loading these from a JSON config file
stored along with the associated
:py:class:`~mlos_bench.environments.base_environment.Environment` config.
For more tunable parameters examples refer to the `JSON schema
<https://github.com/microsoft/MLOS/blob/main/mlos_bench/mlos_bench/config/schemas/tunables/tunable-params-schema.json>`_
@ -116,6 +140,7 @@ There are also examples of `tunable values in the source tree
>>> from mlos_bench.services.config_persistence import ConfigPersistenceService
>>> service = ConfigPersistenceService()
>>> json_config = '''
... // Use json5 (or jsonc) syntax to allow comments and other more flexible syntax.
... {
... "group_1": {
... "cost": 1,
@ -131,18 +156,21 @@ There are also examples of `tunable values in the source tree
... },
... "int_param": {
... "type": "int",
... "range": [-1, 10],
... "range": [1, 10],
... "default": 5,
... // Mark some values as "special", that need more explicit testing.
... // e.g., maybe these indicate "automatic" or "disabled" behavior for
... // the system being tested instead of an explicit size
... "special": [-1, 0],
... // Optionally specify a sampling distribution.
... // Optionally specify a sampling distribution
... // to influence which values to prioritize.
... "distribution": {
... "type": "uniform" // alternatively, "beta" or "normal"
... },
... // Free form key-value pairs that can be used with the
... // tunable upon sampling for composing configs.
... // These can be retrieved later to help generate
... // config files from the sampled tunables.
... "meta": {
... "suffix": "MB"
... }
@ -197,10 +225,13 @@ Notes
-----
Internally, :py:class:`.TunableGroups` are converted to
:external:py:class:`ConfigSpace.ConfigurationSpace` objects for use with
:py:mod:`mlos_core`.
:py:mod:`mlos_core` using the :py:mod:`mlos_bench.optimizers.convert_configspace`.
See the "Spaces" section in the :py:mod:`mlos_core` module documentation for more
information.
In order to handle sampling of :py:attr:`.Tunable.special` values, the ``!``
character is prohibited from being used in the name of a :py:class:`.Tunable`.
See Also
--------
:py:mod:`mlos_bench.config` : Overview of the configuration system.
@ -210,8 +241,9 @@ See Also
:py:meth:`.TunableGroups.assign` : Notes on special cases for assigning tunable values.
"""
from mlos_bench.tunables.tunable import Tunable, TunableValue
from mlos_bench.tunables.tunable import Tunable
from mlos_bench.tunables.tunable_groups import TunableGroups
from mlos_bench.tunables.tunable_types import TunableValue
__all__ = [
"Tunable",

Просмотреть файл

@ -2,18 +2,33 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
#
"""Tunable parameter definition."""
"""
CovariantTunableGroup class definition.
A collection of :py:class:`.Tunable` parameters that are updated together (e.g.,
with the same cost).
See Also
--------
mlos_bench.tunables.tunable_groups : TunableGroups class definition.
"""
import copy
from collections.abc import Iterable
from mlos_bench.tunables.tunable import Tunable, TunableValue
from mlos_bench.tunables.tunable import Tunable
from mlos_bench.tunables.tunable_types import TunableValue
class CovariantTunableGroup:
"""
A collection of tunable parameters.
A collection of :py:class:`.Tunable` parameters.
Changing any of the parameters in the group incurs the same cost of the experiment.
See Also
--------
mlos_bench.tunables.tunable_groups : TunableGroups class definition.
"""
def __init__(self, name: str, config: dict):

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -2,14 +2,102 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
#
"""TunableGroups definition."""
"""
TunableGroups definition.
A collection of :py:class:`.CovariantTunableGroup` s of :py:class:`.Tunable`
parameters.
Used to define the configuration space for an
:py:class:`~mlos_bench.environments.base_environment.Environment` for an
:py:class:`~mlos_bench.optimizers.base_optimizer.Optimizer` to explore.
Config
++++++
The configuration of the tunable parameters is generally given via a JSON config file.
The syntax looks something like this:
.. code-block:: json
{ // starts a TunableGroups config (e.g., for one Environment)
"group1": { // starts a CovariantTunableGroup config
"cost": 7,
"params": {
"param1": { // starts a Tunable config, named "param1"
"type": "int",
"range": [0, 100],
"default": 50
},
"param2": { // starts a new Tunable config, named "param2", within that same group
"type": "float",
"range": [0.0, 100.0],
"default": 50.0
},
"param3": {
"type": "categorical",
"values": ["on", "off", "auto"],
"default": "auto"
}
}
},
"group2": { // starts a new CovariantTunableGroup config
"cost": 7,
"params": {
"some_param1": {
"type": "int",
"range": [0, 10],
"default": 5
},
"some_param2": {
"type": "float",
"range": [0.0, 100.0],
"default": 50.0
},
"some_param3": {
"type": "categorical",
"values": ["red", "green", "blue"],
"default": "green"
}
}
}
}
The JSON config is expected to be a dictionary of covariant tunable groups.
Each covariant group has a name and a cost associated with changing any/all of the
parameters in that covariant group.
Each group has a dictionary of :py:class:`.Tunable` parameters, where the key is
the name of the parameter and the value is a dictionary of the parameter's
configuration (see the :py:class:`.Tunable` class for more information on the
different ways they can be configured).
Generally tunables are associated with an
:py:class:`~mlos_bench.environments.base_environment.Environment` and included along
with the Environment's config directory (e.g., ``env-name-tunables.mlos.jsonc``) and
referenced in the Environment config using the ``include_tunables`` property.
See Also
--------
:py:mod:`mlos_bench.tunables` :
For more information on tunable parameters and their configuration.
:py:mod:`mlos_bench.tunables.tunable` :
Tunable parameter definition.
:py:mod:`mlos_bench.config` :
Configuration system for mlos_bench.
:py:mod:`mlos_bench.environments` :
Environment configuration and setup.
"""
import copy
import logging
from collections.abc import Generator, Iterable, Mapping
from mlos_bench.config.schemas import ConfigSchema
from mlos_bench.tunables.covariant_group import CovariantTunableGroup
from mlos_bench.tunables.tunable import Tunable, TunableValue
from mlos_bench.tunables.tunable import Tunable
from mlos_bench.tunables.tunable_types import TunableValue
_LOG = logging.getLogger(__name__)
@ -30,8 +118,8 @@ class TunableGroups:
See Also
--------
:py:mod:`mlos_bench.tunables` : for more information on tunable parameters and
their configuration.
:py:mod:`mlos_bench.tunables` :
For more information on tunable parameters and their configuration.
"""
if config is None:
config = {}
@ -356,8 +444,8 @@ class TunableGroups:
param_values : Mapping[str, TunableValue]
Dictionary mapping Tunable parameter names to new values.
As a special behavior when the mapping is empty the method will restore
the default values rather than no-op.
As a special behavior when the mapping is empty (``{}``) the method will
restore the default values rather than no-op.
This allows an empty dictionary in json configs to be used to reset the
tunables to defaults without having to copy the original values from the
tunable_params definition.

Просмотреть файл

@ -0,0 +1,386 @@
#
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
#
"""
Helper types for :py:class:`~mlos_bench.tunables.tunable.Tunable`.
The main class of interest to most users in this module is :py:class:`.TunableDict`,
which provides the typed conversions from a JSON config to a config used for
creating a :py:class:`~mlos_bench.tunables.tunable.Tunable`.
The other types are mostly used for type checking and documentation purposes.
"""
# NOTE: pydoctest doesn't scan variable docstrings so we put the examples in the
# Tunable class docstrings.
# These type aliases are moved here mostly to allow easier documentation reading of
# the Tunable class itself.
from collections.abc import Sequence
from typing import TYPE_CHECKING, Any, Literal, TypeAlias, TypedDict
if TYPE_CHECKING:
# Used to allow for shorter docstring references.
from mlos_bench.tunables.tunable import Tunable
TunableValue: TypeAlias = int | float | str | None
"""A :py:class:`TypeAlias` for a :py:class:`~.Tunable` parameter value."""
TunableValueType: TypeAlias = type[int] | type[float] | type[str]
"""A :py:class:`TypeAlias` for :py:class:`~.Tunable` value
:py:attr:`data type <.Tunable.dtype>`.
See Also
--------
:py:attr:`Tunable.dtype <.Tunable.dtype>` : Example of accepted types.
"""
TunableValueTypeTuple = (int, float, str, type(None))
"""
Tunable value ``type`` tuple.
Notes
-----
For checking whether a param is a :py:data:`.TunableValue` with
:py:func:`isinstance`.
"""
TunableValueTypeName = Literal["int", "float", "categorical"]
"""
The accepted string names of a :py:class:`~.Tunable` value :py:attr:`~.Tunable.type`.
See Also
--------
:py:attr:`Tunable.type <.Tunable.type>` : Example of accepted type names.
"""
TUNABLE_DTYPE: dict[TunableValueTypeName, TunableValueType] = {
"int": int,
"float": float,
"categorical": str,
}
"""
Maps :py:class:`~.Tunable` types to their corresponding Python data types by name.
See Also
--------
:py:attr:`Tunable.dtype <.Tunable.dtype>` : Example of type mappings.
"""
TunableValuesDict = dict[str, TunableValue]
"""Tunable values dictionary type."""
DistributionName = Literal["uniform", "normal", "beta"]
"""
The :py:attr:`~.Tunable.distribution` type names for a :py:class:`~.Tunable` value.
See Also
--------
:py:attr:`Tunable.distribution <.Tunable.distribution>` :
Example of accepted distribution names.
"""
class DistributionDictOpt(TypedDict, total=False): # total=False allows for optional fields
"""
A :py:class:`TypedDict` for a :py:class:`~.Tunable` parameter's optional
:py:attr:`~.Tunable.distribution_params` config.
Mostly used for type checking. These are the types expected to be received from
the json config.
Notes
-----
:py:class:`.DistributionDict` contains the required fields for the
:py:attr:`Tunable.distribution <.Tunable.distribution>` parameter.
See Also
--------
:py:attr:`Tunable.distribution_params <.Tunable.distribution_params>` :
Examples of distribution parameters.
"""
def __init__(self, *args, **kwargs): # type: ignore # pylint: disable=useless-super-delegation
""".. comment: don't inherit the docstring"""
super().__init__(*args, **kwargs)
params: dict[str, float] | None
"""
The parameters for the distribution.
See Also
--------
:py:attr:`Tunable.distribution_params <.Tunable.distribution_params>` :
Examples of distribution parameters.
"""
class DistributionDict(DistributionDictOpt):
"""
A :py:class:`TypedDict` for a :py:class:`~.Tunable` parameter's required
:py:attr:`~.Tunable.distribution` config parameters.
Mostly used for type checking. These are the types expected to be received from the
json config.
See Also
--------
:py:attr:`Tunable.distribution <.Tunable.distribution>` :
Examples of Tunables with distributions.
:py:attr:`Tunable.distribution_params <.Tunable.distribution_params>` :
Examples of distribution parameters.
"""
def __init__(self, *args, **kwargs): # type: ignore # pylint: disable=useless-super-delegation
""".. comment: don't inherit the docstring"""
super().__init__(*args, **kwargs)
type: DistributionName
"""
The name of the distribution.
See Also
--------
:py:attr:`Tunable.distribution <.Tunable.distribution>` :
Examples of distribution names.
"""
class TunableDictOpt(TypedDict, total=False): # total=False allows for optional fields
"""
A :py:class:`TypedDict` for a :py:class:`~.Tunable` parameter's optional config
parameters.
Mostly used for type checking. These are the types expected to be received from
the json config.
Notes
-----
:py:class:`TunableDict` contains the required fields for the
:py:class:`~.Tunable` parameter.
"""
def __init__(self, *args, **kwargs): # type: ignore # pylint: disable=useless-super-delegation
""".. comment: don't inherit the docstring"""
super().__init__(*args, **kwargs)
# Optional fields:
description: str | None
"""
Description of the :py:class:`~.Tunable` parameter.
See Also
--------
:py:attr:`Tunable.description <.Tunable.description>`
"""
values: list[str | None] | None
"""
List of values (or categories) for a "categorical" type :py:class:`~.Tunable`
parameter.
A list of values is required for "categorical" type Tunables.
See Also
--------
:py:attr:`Tunable.categories <.Tunable.categories>`
:py:attr:`Tunable.values <.Tunable.values>`
"""
range: Sequence[int] | Sequence[float] | None
"""
The range of values for an "int" or "float" type :py:class:`~.Tunable` parameter.
Must be a sequence of two values: ``[min, max]``.
A range is required for "int" and "float" type Tunables.
See Also
--------
:py:attr:`Tunable.range <.Tunable.range>` : Examples of ranges.
:py:attr:`Tunable.values <.Tunable.values>`
"""
special: list[int] | list[float] | None
"""
List of special values for an "int" or "float" type :py:class:`~.Tunable` parameter.
These are values that are considered special by the target system (e.g.,
``null``, ``0``, ``-1``, ``auto``, etc.) and should be sampled with higher
weights.
See Also
--------
:py:attr:`Tunable.special <.Tunable.special>` : Examples of special values.
"""
quantization_bins: int | None
"""
The number of quantization bins for an "int" or "float" type :py:class:`~.Tunable`
parameter.
See Also
--------
:py:attr:`Tunable.quantization_bins <.Tunable.quantization_bins>` :
Examples of quantized Tunables.
"""
log: bool | None
"""
Whether to use log sampling for an "int" or "float" type :py:class:`~.Tunable`
parameter.
See Also
--------
:py:attr:`Tunable.is_log <.Tunable.is_log>`
"""
distribution: DistributionDict | None
"""
Optional sampling distribution configuration for an "int" or "float" type
:py:class:`~.Tunable` parameter.
See Also
--------
:py:attr:`Tunable.distribution <.Tunable.distribution>` :
Examples of distributions.
:py:attr:`Tunable.distribution_params <.Tunable.distribution_params>` :
Examples of distribution parameters.
"""
values_weights: list[float] | None
"""
Optional sampling weights for the values of a "categorical" type
:py:class:`~.Tunable` parameter.
See Also
--------
:py:attr:`Tunable.weights <.Tunable.weights>` : Examples of weighted sampling Tunables.
"""
special_weights: list[float] | None
"""
Optional sampling weights for the special values of an "int" or "float" type
:py:class:`~.Tunable` parameter.
See Also
--------
:py:attr:`Tunable.weights <.Tunable.weights>` : Examples of weighted sampling Tunables.
"""
range_weight: float | None
"""
Optional sampling weight for the main ranges of an "int" or "float" type
:py:class:`~.Tunable` parameter.
See Also
--------
:py:attr:`Tunable.range_weight <.Tunable.range_weight>` :
Examples of weighted sampling Tunables.
"""
meta: dict[str, Any]
"""
Free form dict to store additional metadata for the :py:class:`~.Tunable` parameter
(e.g., unit suffix, etc.)
See Also
--------
:py:attr:`Tunable.meta <.Tunable.meta>` : Examples of Tunables with metadata.
"""
class TunableDict(TunableDictOpt):
"""
A :py:class:`TypedDict` for a :py:class:`~.Tunable` parameter's required config
parameters.
Mostly used for type checking. These are the types expected to be received from
the json config.
Examples
--------
>>> # Example values of the TunableDict
>>> TunableDict({'type': 'int', 'default': 0, 'range': [0, 10]})
{'type': 'int', 'default': 0, 'range': [0, 10]}
>>> # Example values of the TunableDict with optional fields
>>> TunableDict({'type': 'categorical', 'default': 'a', 'values': ['a', 'b']})
{'type': 'categorical', 'default': 'a', 'values': ['a', 'b']}
"""
def __init__(self, *args, **kwargs): # type: ignore # pylint: disable=useless-super-delegation
""".. comment: don't inherit the docstring"""
super().__init__(*args, **kwargs)
# Required fields
type: TunableValueTypeName
"""
The name of the type of the :py:class:`~.Tunable` parameter.
See Also
--------
:py:attr:`Tunable.type <.Tunable.type>` : Examples of type names.
"""
default: TunableValue
"""
The default value of the :py:class:`~.Tunable` parameter.
See Also
--------
:py:attr:`Tunable.default <.Tunable.default>`
"""
def tunable_dict_from_dict(config: dict[str, Any]) -> TunableDict:
"""
Creates a TunableDict from a regular dict.
Notes
-----
Mostly used for type checking while instantiating a
:py:class:`~.Tunable` from a json config.
Parameters
----------
config : dict[str, Any]
A regular dict that represents a :py:class:`.TunableDict`.
Returns
-------
TunableDict
Examples
--------
>>> # Example values of the TunableDict
>>> import json5 as json
>>> config = json.loads("{'type': 'int', 'default': 0, 'range': [0, 10]}")
>>> config
{'type': 'int', 'default': 0, 'range': [0, 10]}
>>> typed_dict = tunable_dict_from_dict(config)
>>> typed_dict
{'type': 'int', 'description': None, 'default': 0, 'values': None, 'range': [0, 10], 'quantization_bins': None, 'log': None, 'distribution': None, 'special': None, 'values_weights': None, 'special_weights': None, 'range_weight': None, 'meta': {}}
""" # pylint: disable=line-too-long # noqa: E501
_type = config.get("type")
if _type not in TUNABLE_DTYPE:
raise ValueError(f"Invalid parameter type: {_type}")
_meta = config.get("meta", {})
return TunableDict(
type=_type,
description=config.get("description"),
default=config.get("default"),
values=config.get("values"),
range=config.get("range"),
quantization_bins=config.get("quantization_bins"),
log=config.get("log"),
distribution=config.get("distribution"),
special=config.get("special"),
values_weights=config.get("values_weights"),
special_weights=config.get("special_weights"),
range_weight=config.get("range_weight"),
meta=_meta,
)

Просмотреть файл

@ -17,9 +17,15 @@ for all Optimizers and provides the core
This module also provides a simple :py:class:`~.OptimizerFactory` class to
:py:meth:`~.OptimizerFactory.create` an Optimizer.
Examples
Generally speaking, the :py:mod:`mlos_core` package is intended to be used in
conjunction with the :py:mod:`mlos_bench` package, which provides a higher-level
interface for running benchmarks and autotuning experiments.
See Also
--------
TODO: Add example usage here.
mlos_bench.optimizers.mlos_core_optimizer :
The mlos_bench Optimizer class that uses the mlos_core Optimizers to run
autotuning experiments.
Notes
-----

Просмотреть файл

@ -20,9 +20,15 @@ information on how to do this with :py:mod:`mlos_bench`.
This module provides a simple :py:class:`.SpaceAdapterFactory` class to
:py:meth:`~.SpaceAdapterFactory.create` space adapters.
Examples
Generally speaking, the :py:mod:`mlos_core` package is intended to be used in
conjunction with the :py:mod:`mlos_bench` package, which provides a higher-level
interface for running benchmarks and autotuning experiments.
See Also
--------
TODO: Add example usage here.
mlos_bench.optimizers.mlos_core_optimizer :
The mlos_bench Optimizer class that uses the mlos_core Optimizers and
SpaceAdapters to run autotuning experiments.
Notes
-----

Просмотреть файл

@ -33,6 +33,7 @@ class BaseSpaceAdapter(metaclass=ABCMeta):
def __init__(self, *, orig_parameter_space: ConfigSpace.ConfigurationSpace):
self._orig_parameter_space: ConfigSpace.ConfigurationSpace = orig_parameter_space
# Use the same random state as the original parameter space.
self._random_state = orig_parameter_space.random
def __repr__(self) -> str:

Просмотреть файл

@ -65,11 +65,17 @@ class LlamaTuneAdapter(BaseSpaceAdapter): # pylint: disable=too-many-instance-a
The original (user-provided) parameter space to optimize.
num_low_dims : int
Number of dimensions used in the low-dimensional parameter search space.
special_param_values_dict : dict | None
Dictionary of special
special_param_values : dict | None
Dictionary of special parameter values.
Each key is the name of a parameter, and the value is either:
- an integer (special value), or
- a tuple of an integer and a float (special integer value and biasing percentage)
max_unique_values_per_param : int | None
Number of unique values per parameter. Used to discretize the parameter space.
If `None` space discretization is disabled.
use_approximate_reverse_mapping : bool
Whether to use an approximate reverse mapping to help register
configurations during resume.
"""
super().__init__(orig_parameter_space=orig_parameter_space)

Просмотреть файл

@ -32,10 +32,15 @@ __version__ = VERSION
class MlosVizMethod(Enum):
"""What method to use for visualizing the experiment results."""
"""What method to use for visualizing the Experiment results."""
DABL = "dabl"
"""Use DABL for automatic data correlation and visualization of
:py:class:`~.ExperimentData`.
"""
AUTO = DABL # use dabl as the current default
"""The default automatic :py:class:`~.ExperimentData` visualization method."""
def ignore_plotter_warnings(plotter_method: MlosVizMethod = MlosVizMethod.AUTO) -> None:
@ -46,7 +51,7 @@ def ignore_plotter_warnings(plotter_method: MlosVizMethod = MlosVizMethod.AUTO)
Parameters
----------
plotter_method: MlosVizMethod
The method to use for visualizing the experiment results.
The method to use for visualizing the Experiment results.
"""
base.ignore_plotter_warnings()
if plotter_method == MlosVizMethod.DABL:
@ -67,14 +72,14 @@ def plot(
**kwargs: Any,
) -> None:
"""
Plots the results of the experiment.
Plots the results of the given :py:class:`~.ExperimentData`.
Intended to be used from a Jupyter notebook.
Parameters
----------
exp_data: ExperimentData
The experiment data to plot.
The Experiment data to plot.
results_df : pandas.DataFrame | None
Optional `results_df` to plot.
If not provided, defaults to :py:attr:`.ExperimentData.results_df` property.
@ -82,7 +87,7 @@ def plot(
Optional objectives to plot.
If not provided, defaults to :py:attr:`.ExperimentData.objectives` property.
plotter_method: MlosVizMethod
The method to use for visualizing the experiment results.
The method to use for visualizing the Experiment results.
filter_warnings: bool
Whether or not to filter some warnings from the plotter.
kwargs : dict