-
Notifications
You must be signed in to change notification settings - Fork 68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Metadata for SMAC enabling Multi-Fidelity #771
Open
jsfreischuetz
wants to merge
28
commits into
microsoft:main
Choose a base branch
from
jsfreischuetz:multi-fidelity
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
548af15
Implement metadata for multifidelity in SMAC
jsfreischuetz 36ac67a
Merge branch 'main' into multi-fidelity
jsfreischuetz 4cc133b
Merge branch 'main' into multi-fidelity
bpkroth 5ab03c8
Merge branch 'main' into multi-fidelity
bpkroth bfd2a42
Update mlos_core/mlos_core/optimizers/README
jsfreischuetz 16208f4
Update mlos_core/mlos_core/optimizers/README
jsfreischuetz 938f8f0
Update mlos_core/mlos_core/optimizers/README
jsfreischuetz 81d6d56
Update mlos_core/mlos_core/optimizers/README
jsfreischuetz bf2f3cc
Update mlos_core/mlos_core/optimizers/README
jsfreischuetz 1686c7c
some comments
jsfreischuetz d263613
more comments for README
jsfreischuetz 6766b8d
Update mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimi…
jsfreischuetz bae1763
Update mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimi…
jsfreischuetz 53af62b
mergE
jsfreischuetz 81e8bb0
Merge branch 'multi-fidelity' of github.com:jsfreischuetz/MLOS into m…
jsfreischuetz dcff9cc
Update mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimi…
jsfreischuetz 50ef16c
Update mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimi…
jsfreischuetz c32bd67
Update mlos_core/mlos_core/optimizers/utils.py
jsfreischuetz 2b15694
Update mlos_core/mlos_core/tests/optimizers/optimizer_metadata_test.py
jsfreischuetz 574b8cc
Update mlos_core/mlos_core/optimizers/utils.py
jsfreischuetz 3d4c055
Update mlos_core/mlos_core/optimizers/utils.py
jsfreischuetz 41ee533
Update mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimi…
jsfreischuetz cfa936a
Update mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimi…
jsfreischuetz abd3eb6
Update mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimi…
jsfreischuetz e0ac571
comment
jsfreischuetz c1e0845
Merge branch 'multi-fidelity' of github.com:jsfreischuetz/MLOS into m…
jsfreischuetz 9234599
comments
jsfreischuetz 054fce3
Merge branch 'main' into multi-fidelity
jsfreischuetz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,29 @@ | ||
# Optimizers | ||
|
||
This is a directory that contains wrappers for different optimizers to integrate into MLOS. | ||
This is implemented though child classes for the `BaseOptimizer` class defined in `optimizer.py`. | ||
|
||
The main goal of these optimizers is to `suggest` configurations, possibly based on prior trial data to find an optimum based on some objective(s). | ||
This process is interacted with through `register` and `suggest` interfaces. | ||
|
||
The following definitions are useful for understanding the implementation | ||
|
||
- `configuration`: a vector representation of a configuration of a system to be evaluated. | ||
- `score`: the objective(s) associated with a configuration | ||
- `context`: additional (static) information about the evaluation used to extend the internal model used for suggesting samples. | ||
For instance, a descriptor of the VM size (vCore count and # of GB of RAM), and some descriptor of the workload. | ||
The intent being to allow either sharing or indexing of trial info between "similar" experiments in order to help make the optimization process more efficient for new scenarios. | ||
> Note: This is not yet implemented. | ||
- `metadata`: additional information about the evaluation, such as the runtime budget used during evaluation. | ||
This data is typically specific to the given optimizer backend and may be returned during a `suggest` call and expected to be provided again during the subsequent `register` call. | ||
|
||
- `register`: this is a function that takes a `configuration`, `score`, and, optionally, `metadata` and `context` about the evaluation to update the model for future evaluations. | ||
|
||
- `register`: this is a function that takes a `configuration`, `score`, and, optionally, `metadata` and `context` about the evaluation to update the model for future evaluations. | ||
- `suggest`: this function returns a new configuration for evaluation. | ||
|
||
Some optimizers will return additional metadata for evaluation, that should be used during the register phase. | ||
- `get_observations`: returns all observations reported to the optimizer as a triplet of DataFrames `(config, score, context, metadata)`. | ||
- `get_best_observations`: returns the best observation as a triplet of best `(config, score, context, metadata)` DataFrames. | ||
- `get_observations`: returns all observations reported to the optimizer as a triplet of DataFrames `(config, score, context, metadata)`. | ||
- `get_best_observations`: returns the best observation as a triplet of best `(config, score, context, metadata)` DataFrames. |
261 changes: 231 additions & 30 deletions
261
mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py
Large diffs are not rendered by default.
Oops, something went wrong.
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,58 @@ | ||
# | ||
# Copyright (c) Microsoft Corporation. | ||
# Licensed under the MIT License. | ||
# | ||
""" | ||
Contains utils used for implementing the mlos_core optimizers | ||
""" | ||
import inspect | ||
from typing import Any, Callable, Dict, List, Optional | ||
import pandas as pd | ||
|
||
|
||
def to_metadata(metadata: Optional[pd.DataFrame]) -> Optional[List[pd.Series]]: | ||
""" | ||
Converts a list of metadata dataframe objects to a list of metadata series objects. | ||
|
||
Parameters | ||
jsfreischuetz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
---------- | ||
metadata : Optional[pd.DataFrame] | ||
The dataframe to convert to metadata | ||
|
||
Returns | ||
------- | ||
Optional[List[pd.Series]] | ||
The list of metadata series objects | ||
""" | ||
if metadata is None: | ||
return None | ||
return [idx_series[1] for idx_series in metadata.iterrows()] | ||
|
||
|
||
def filter_kwargs(function: Callable, **kwargs: Any) -> Dict[str, Any]: | ||
bpkroth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
Filters arguments provided in the kwargs dictionary to be restricted to the arguments legal for | ||
the called function. | ||
|
||
Parameters | ||
---------- | ||
function : Callable | ||
function over which we filter kwargs for. | ||
kwargs: | ||
kwargs that we are filtering for the target function | ||
|
||
Returns | ||
------- | ||
dict | ||
kwargs with the non-legal argument filtered out | ||
""" | ||
sig = inspect.signature(function) | ||
filter_keys = [ | ||
param.name | ||
for param in sig.parameters.values() | ||
if param.kind == param.POSITIONAL_OR_KEYWORD | ||
] | ||
filtered_dict = { | ||
filter_key: kwargs[filter_key] for filter_key in filter_keys & kwargs.keys() | ||
} | ||
return filtered_dict |
119 changes: 119 additions & 0 deletions
119
mlos_core/mlos_core/tests/optimizers/optimizer_metadata_test.py
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,119 @@ | ||
# | ||
# Copyright (c) Microsoft Corporation. | ||
# Licensed under the MIT License. | ||
# | ||
""" | ||
Tests for Optimizers using Metadata. | ||
""" | ||
|
||
from typing import Callable | ||
|
||
import logging | ||
import pytest | ||
|
||
import pandas as pd | ||
import ConfigSpace as CS | ||
|
||
from smac import MultiFidelityFacade as MFFacade | ||
from smac.intensifier.successive_halving import SuccessiveHalving | ||
|
||
from mlos_core.optimizers import ( | ||
OptimizerType, OptimizerFactory, BaseOptimizer) | ||
from mlos_core.tests import SEED | ||
|
||
_LOG = logging.getLogger(__name__) | ||
_LOG.setLevel(logging.DEBUG) | ||
|
||
|
||
def smac_verify_best(metadata: pd.DataFrame, best: bool = False) -> bool: | ||
""" | ||
Function to verify if the metadata used by SMAC is in a legal state | ||
|
||
Parameters | ||
---------- | ||
metadata : pd.DataFrame | ||
metadata returned by SMAC | ||
|
||
best: bool | ||
If we are testing just the best contexts or not | ||
|
||
Returns | ||
------- | ||
bool | ||
if the metadata that is returned is valid | ||
""" | ||
|
||
max_budget = metadata["budget"].max() | ||
assert isinstance(max_budget, float) | ||
assert max_budget == 9 | ||
|
||
if not best: | ||
min_budget = metadata["budget"].min() | ||
assert isinstance(min_budget, float) | ||
assert min_budget == 1 | ||
|
||
return True | ||
|
||
|
||
@pytest.mark.parametrize(('optimizer_type', 'verify', 'kwargs'), [ | ||
# Enumerate all supported Optimizers | ||
*[(member, verify, kwargs) | ||
for member, verify, kwargs in [( | ||
OptimizerType.SMAC, | ||
smac_verify_best, | ||
{ | ||
"seed": SEED, | ||
"facade": MFFacade, | ||
"intensifier": SuccessiveHalving, | ||
"min_budget": 1, | ||
"max_budget": 9 | ||
} | ||
)]], | ||
]) | ||
def test_optimizer_metadata(optimizer_type: OptimizerType, verify: Callable[[pd.DataFrame, bool], bool], kwargs: dict) -> None: | ||
""" | ||
Toy problem to test if metadata is properly being handled for each supporting optimizer | ||
""" | ||
max_iterations = 100 | ||
|
||
def objective(point: pd.DataFrame) -> pd.DataFrame: | ||
# mix of hyperparameters, optimal is to select the highest possible | ||
return pd.DataFrame({"score": point["x"] + point["y"]}) | ||
|
||
input_space = CS.ConfigurationSpace(seed=SEED) | ||
# add a mix of numeric datatypes | ||
input_space.add_hyperparameter(CS.UniformIntegerHyperparameter(name='x', lower=0, upper=5)) | ||
input_space.add_hyperparameter(CS.UniformFloatHyperparameter(name='y', lower=0.0, upper=5.0)) | ||
|
||
optimizer: BaseOptimizer = OptimizerFactory.create( | ||
parameter_space=input_space, | ||
optimization_targets=['score'], | ||
optimizer_type=optimizer_type, | ||
optimizer_kwargs=kwargs, | ||
) | ||
|
||
with pytest.raises(ValueError, match="No observations"): | ||
optimizer.get_best_observations() | ||
|
||
with pytest.raises(ValueError, match="No observations"): | ||
optimizer.get_observations() | ||
|
||
for _ in range(max_iterations): | ||
config, metadata = optimizer.suggest() | ||
assert isinstance(metadata, pd.DataFrame) | ||
|
||
optimizer.register(configs=config, scores=objective(config), metadata=metadata) | ||
bpkroth marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
(all_configs, all_scores, all_contexts, all_metadata) = optimizer.get_observations() | ||
assert isinstance(all_configs, pd.DataFrame) | ||
assert isinstance(all_scores, pd.DataFrame) | ||
assert all_contexts is None | ||
assert isinstance(all_metadata, pd.DataFrame) | ||
assert verify(all_metadata, False) | ||
|
||
(best_configs, best_scores, best_contexts, best_metadata) = optimizer.get_best_observations() | ||
assert isinstance(best_configs, pd.DataFrame) | ||
assert isinstance(best_scores, pd.DataFrame) | ||
assert best_contexts is None | ||
assert isinstance(best_metadata, pd.DataFrame) | ||
assert verify(best_metadata, True) |
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
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These
Tuples
are getting a little large and hard to read (recall a previous version of this PR where the order of them was mistakenly swapped at one point).Think we discussed creating a NamedTuple or small DataClass for them instead so that they can be accessed by name in order to make it more readable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want I can do this in this PR, or another follow up PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a predecessor PR would be better. Much like we did with adding the metadata args and named args first.