Skip to content
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

Fix the coercion of scores to floats in the optimizer #789

Merged
merged 20 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
599211b
fix the coercion of scrores to floats in the optimizer
motus Jul 10, 2024
2bf7ced
Merge branch 'main' of https://github.com/microsoft/MLOS into sergiym…
motus Jul 10, 2024
20fa3fa
add unit tests for _adjust_signs_df() method
motus Jul 11, 2024
a0baffd
Merge branch 'main' into sergiym/opt/score_sign
motus Jul 12, 2024
973a2d4
add more unit tests for _adjust_signs_df() method
motus Jul 12, 2024
ee3d975
Merge branch 'sergiym/opt/score_sign' of https://github.com/motus/MLO…
motus Jul 12, 2024
e2e901f
Merge branch 'main' of https://github.com/microsoft/MLOS into sergiym…
motus Jul 12, 2024
571e39d
black formatting
motus Jul 12, 2024
d172d1c
Merge branch 'main' into sergiym/opt/score_sign
motus Jul 15, 2024
d08639a
Merge branch 'main' of https://github.com/microsoft/MLOS into sergiym…
motus Jul 15, 2024
9a4863c
Merge branch 'sergiym/opt/score_sign' of https://github.com/motus/MLO…
motus Jul 15, 2024
8b0dac9
Merge branch 'main' into sergiym/opt/score_sign
motus Jul 16, 2024
cad4c56
Merge branch 'main' of https://github.com/microsoft/MLOS into sergiym…
motus Jul 18, 2024
1a34faa
Merge branch 'sergiym/opt/score_sign' of https://github.com/motus/MLO…
motus Jul 18, 2024
b8f11ab
docformatter fixes
motus Jul 19, 2024
602b493
Merge branch 'main' of https://github.com/microsoft/MLOS into sergiym…
motus Jul 19, 2024
9061c7e
Merge branch 'main' into sergiym/opt/score_sign
bpkroth Jul 22, 2024
49d6b19
Merge branch 'main' of https://github.com/microsoft/MLOS into sergiym…
motus Jul 22, 2024
4a1fb25
Merge branch 'sergiym/opt/score_sign' of https://github.com/motus/MLO…
motus Jul 22, 2024
4c70c4a
Better logging/error messages when converting scores to floats
motus Jul 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions mlos_bench/mlos_bench/optimizers/mlos_core_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,20 +116,19 @@ def bulk_register(
pd.DataFrame([{} if score is None else score for score in scores])
)

opt_targets = list(self._opt_targets)
if status is not None:
# Select only the completed trials, set scores for failed trials to +inf.
df_status = pd.Series(status)
# TODO: Be more flexible with values used for failed trials (not just +inf).
# Issue: https://github.com/microsoft/MLOS/issues/523
df_scores.loc[df_status != Status.SUCCEEDED, opt_targets] = float("inf")
df_scores[df_status != Status.SUCCEEDED] = float("inf")
motus marked this conversation as resolved.
Show resolved Hide resolved
df_status_completed = df_status.apply(Status.is_completed)
df_configs = df_configs[df_status_completed]
df_scores = df_scores[df_status_completed]

# TODO: Specify (in the config) which metrics to pass to the optimizer.
# Issue: https://github.com/microsoft/MLOS/issues/745
self._opt.register(configs=df_configs, scores=df_scores[opt_targets].astype(float))
self._opt.register(configs=df_configs, scores=df_scores)

if _LOG.isEnabledFor(logging.DEBUG):
(score, _) = self.get_best_observation()
Expand All @@ -138,10 +137,19 @@ def bulk_register(
return True

def _adjust_signs_df(self, df_scores: pd.DataFrame) -> pd.DataFrame:
"""In-place adjust the signs of the scores for MINIMIZATION problem."""
for opt_target, opt_dir in self._opt_targets.items():
df_scores[opt_target] *= opt_dir
return df_scores
"""Coerce optimization target scores to floats and adjust the signs for
MINIMIZATION problem.
"""
df_targets = df_scores[list(self._opt_targets)]
try:
return df_targets.astype(float) * self._opt_targets.values()
except ValueError as ex:
_LOG.error(
"Some score values cannot be converted to float - check the data ::\n%s",
df_targets,
exc_info=True,
)
raise ValueError("Some score values cannot be converted to float") from ex

def _to_df(self, configs: Sequence[Dict[str, TunableValue]]) -> pd.DataFrame:
"""
Expand Down
86 changes: 86 additions & 0 deletions mlos_bench/mlos_bench/tests/optimizers/mlos_core_opt_df_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ def mlos_core_optimizer(tunable_groups: TunableGroups) -> MlosCoreOptimizer:
"optimizer_type": "FLAML",
"max_suggestions": 10,
"seed": SEED,
"optimization_targets": {
"latency": "min",
"throughput": "max",
},
}
return MlosCoreOptimizer(tunable_groups, test_opt_config)

Expand Down Expand Up @@ -74,3 +78,85 @@ def test_df(mlos_core_optimizer: MlosCoreOptimizer, mock_configs: List[dict]) ->
"vmSize": "Standard_B2s",
},
]


def test_df_str(mlos_core_optimizer: MlosCoreOptimizer, mock_configs: List[dict]) -> None:
"""Test `MlosCoreOptimizer._to_df()` type coercion on tunables with string
values.
"""
df_config_orig = mlos_core_optimizer._to_df(mock_configs)
df_config_str = mlos_core_optimizer._to_df(
[{key: str(val) for (key, val) in config.items()} for config in mock_configs]
)
assert df_config_orig.equals(df_config_str)


def test_adjust_signs_df(mlos_core_optimizer: MlosCoreOptimizer) -> None:
"""Test `MlosCoreOptimizer._adjust_signs_df()` on different types of inputs."""
df_scores_input = pandas.DataFrame(
{
"latency": [88.88, 66.66, 99.99, None],
"throughput": [111, 222, 333, None],
}
)

df_scores_output = pandas.DataFrame(
{
"latency": [88.88, 66.66, 99.99, float("NaN")],
"throughput": [-111, -222, -333, float("NaN")],
}
)

# Make sure we adjust the signs for minimization.
df_scores = mlos_core_optimizer._adjust_signs_df(df_scores_input)
assert df_scores.equals(df_scores_output)

# Check that the same operation works for string inputs.
df_scores = mlos_core_optimizer._adjust_signs_df(df_scores_input.astype(str))
assert df_scores.equals(df_scores_output)
motus marked this conversation as resolved.
Show resolved Hide resolved


def test_adjust_signs_df_nan(mlos_core_optimizer: MlosCoreOptimizer) -> None:
"""Test `MlosCoreOptimizer._adjust_signs_df()` handling None, NaN, and Inf
values.
"""
df_scores = mlos_core_optimizer._adjust_signs_df(
pandas.DataFrame(
{
"latency": ["88.88", "NaN", "Inf", "-Inf", None],
"throughput": ["111", "NaN", "Inf", "-Inf", None],
}
)
)

assert df_scores.equals(
pandas.DataFrame(
{
"latency": [88.88, float("NaN"), float("Inf"), float("-Inf"), float("NaN")],
"throughput": [-111, float("NaN"), float("-Inf"), float("Inf"), float("NaN")],
}
)
)


def test_adjust_signs_df_invalid(mlos_core_optimizer: MlosCoreOptimizer) -> None:
"""Test `MlosCoreOptimizer._adjust_signs_df()` on invalid inputs."""
with pytest.raises(ValueError):
mlos_core_optimizer._adjust_signs_df(
pandas.DataFrame(
{
"latency": ["INVALID"],
"throughput": ["no input"],
}
)
)

with pytest.raises(ValueError):
mlos_core_optimizer._adjust_signs_df(
bpkroth marked this conversation as resolved.
Show resolved Hide resolved
pandas.DataFrame(
{
"latency": ["88.88", ""],
"throughput": ["111", ""],
}
)
)
Loading