Desirability Optimization¶
This example demonstrates how to set up a multi-target optimization problem using the
desirability approach
.
The focus lies on defining the target objects with the necessary transformations
enabling the desirability computation, and the creation of the corresponding
optimization objective.
Imports¶
import pandas as pd
from baybe import Campaign
from baybe.objectives import DesirabilityObjective
from baybe.parameters.numerical import NumericalContinuousParameter
from baybe.targets import NumericalTarget
Defining the Search Space¶
Because the search space is of secondary importance in this example, we keep it simple and consider only a single parameter, without loss of generality:
searchspace = NumericalContinuousParameter("parameter", (0, 1)).to_searchspace()
Defining the Targets¶
Next, we define our optimization targets. Because desirability computation relies on averaging target values, it is required that all targets are properly normalized. This can be achieved by applying appropriate target transformations, for which BayBE offers several built-in choices and also offers full customization for advanced use cases.
Target Normalization
If you know what you are doing, you can also disable the normalization check
via the
require_normalization
flag, with the consequence that the selected averaging method is executed on the
target values no matter if they are normalized or not.
For our example, we consider three simple targets reflecting different optimization
goals. The first target takes values in the interval [0, 100] and is to be maximized.
The normalized_ramp()
constructor helps
us achieve this by applying an affine transformation whose output is clamped to the
unit interval:
target_max = NumericalTarget.normalized_ramp("target_max", cutoffs=(0, 100))
The second target takes values in the interval [-10, 0] and is to be minimized:
target_min = NumericalTarget.normalized_ramp(
"target_min", cutoffs=(-10, 0), descending=True
)
For the third target, we like to match a certain value. To do so, we apply a target
transformation that penalizes the distance to this value using a
bell-shaped curve
centered around it:
target_match = NumericalTarget.match_bell("target_match", match_value=50, sigma=5)
Customization
Note that you can easily change the specifics of the applied transformations by resorting to other target constructors or specifying custom transformation logic. For more details, see our target userguide.
Creating the Objective¶
The targets are collected in a
DesirabilityObjective
, which takes care of the
averaging process. The specifics of the averaging can be configured by specifying
optional weights for the targets and the type of averaging to be used:
targets = [target_max, target_min, target_match]
objective = DesirabilityObjective(targets, weights=[20, 20, 60], scalarizer="MEAN")
Getting Recommendations¶
We can now use the objective, like any other, to
query recommendations, e.g. by setting
up a Campaign
:
campaign = Campaign(searchspace, objective)
recommendations = campaign.recommend(batch_size=3)
print(recommendations)
parameter
0 0.137022
1 0.254427
2 0.695105
Accessing Desirability Values¶
Once the target measurements are available, …
recommendations[target_max.name] = [65, 35, 87]
recommendations[target_min.name] = [-8, -3, -5]
recommendations[target_match.name] = [55, 25, 48]
… we can access the corresponding desirability values via the objective:
campaign.add_measurements(recommendations)
desirability = objective.transform(recommendations, allow_extra=True)
print(pd.concat([recommendations, desirability], axis=1))
parameter target_max target_min target_match Desirability
0 0.137022 65 -8 55 0.653918
1 0.254427 35 -3 25 0.130002
2 0.695105 87 -5 48 0.827870