Skip to content

Commit c636020

Browse files
authored
Merge pull request #219 from automl/polynom_surrogate
Polynomial test for ablation path
2 parents 8e74e23 + 0659c81 commit c636020

File tree

13 files changed

+238
-32
lines changed

13 files changed

+238
-32
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# Version 1.3.5
2+
3+
## Access Specifier
4+
- Changed access specifier from '_fit' to 'fit' in the RF surrogate
5+
6+
## Ablation tests
7+
- Added Polynomial "model" class for testing ablation path behavior
8+
- Added extra test case using the polynomial to validate ablation output correctness
9+
110
# Version 1.3.4
211

312
## Bug-Fixes

deepcave/evaluators/ablation.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,25 +75,24 @@ def calculate(
7575
self,
7676
objectives: Optional[Union[Objective, List[Objective]]], # noqa
7777
budget: Optional[Union[int, float]] = None, # noqa
78-
n_trees: int = 50, # noqa
79-
seed: int = 0, # noqa
78+
model: Any = None,
8079
) -> None:
8180
"""
8281
Calculate the ablation path performances and improvements.
8382
83+
To use standard Random Forest surrogate do not pass a model.
84+
The option to pass another model is just for testing purposes.
85+
8486
Parameters
8587
----------
8688
objectives : Optional[Union[Objective, List[Objective]]]
8789
The objective(s) to be considered.
8890
budget : Optional[Union[int, float]]
8991
The budget to be considered. If None, all budgets of the run are considered.
9092
Default is None.
91-
n_trees : int
92-
The number of trees for the surrogate model.
93-
Default is 50.
94-
seed : int
95-
The seed for the surrogate model.
96-
Default is 0.
93+
model :
94+
The surrogate model to use for the prediction of the perfromances.
95+
By default None.
9796
"""
9897
if isinstance(objectives, list) and len(objectives) > 1:
9998
raise ValueError("Only one objective is supported for ablation paths.")
@@ -103,25 +102,30 @@ def calculate(
103102
performances: OrderedDict = OrderedDict()
104103
improvements: OrderedDict = OrderedDict()
105104

105+
self._model = model
106+
106107
df = self.run.get_encoded_data(objective, budget, specific=True)
107108

108109
# Obtain all configurations with theirs costs
109110
df = df.dropna(subset=[objective.name])
110111
X = df[list(self.run.configspace.keys())].to_numpy()
111112
Y = df[objective.name].to_numpy()
112113

113-
# A Random Forest Regressor is used as surrogate model
114-
self._model = RandomForestSurrogate(self.cs, seed=seed, n_trees=n_trees)
115-
self._model._fit(X, Y)
116-
117114
# Get the incumbent configuration
118115
incumbent_config, _ = self.run.get_incumbent(budget=budget, objectives=objective)
119116
incumbent_encode = self.run.encode_config(incumbent_config)
120117

121118
# Get the default configuration
119+
122120
self.default_config = self.cs.get_default_configuration()
123121
default_encode = self.run.encode_config(self.default_config)
124122

123+
# The default model is a RF Surrogate, but it cant be passed as parameter directly
124+
# because it needs access to its config space
125+
if self._model is None:
126+
self._model = RandomForestSurrogate(self.cs, seed=0, n_trees=50)
127+
128+
self._model.fit(X, Y)
125129
# Obtain the predicted cost of the default and incumbent configuration
126130
def_cost, def_std = self._model.predict(np.array([default_encode]))
127131
def_cost, def_std = def_cost[0], def_std[0]
@@ -166,6 +170,7 @@ def calculate(
166170
performances[max_hp] = (max_hp_cost, max_hp_std)
167171
impr_std = np.sqrt(def_std**2 + max_hp_std**2)
168172
improvements[max_hp] = ((def_cost - max_hp_cost), impr_std)
173+
169174
# New 'default' cost and std
170175
def_cost = max_hp_cost
171176
def_std = max_hp_std
@@ -248,7 +253,6 @@ def _ablation(
248253
if hp in incumbent_config.keys() and hp in self.default_config.keys():
249254
config_copy = copy.copy(self.default_config)
250255
config_copy[hp] = incumbent_config[hp]
251-
252256
new_cost, _ = self._model.predict(np.array([self.run.encode_config(config_copy)]))
253257
if objective.optimize == "upper":
254258
new_cost = -new_cost
@@ -262,6 +266,7 @@ def _ablation(
262266
else:
263267
continue
264268
hp_count = len(list(self.cs.keys()))
269+
265270
if max_hp != "":
266271
# For the maximum impact hyperparameter, switch the default with the incumbent value
267272
self.default_config[max_hp] = incumbent_config[max_hp]
@@ -270,6 +275,7 @@ def _ablation(
270275
)
271276
if objective.optimize == "upper":
272277
max_hp_cost = -max_hp_cost
278+
273279
return True, max_hp, max_hp_cost[0], max_hp_std[0]
274280
else:
275281
self.logger.info(

deepcave/evaluators/epm/random_forest_surrogate.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,16 @@ def _fit(self, X: np.ndarray, y: np.ndarray) -> None:
7878
Corresponding target values.
7979
"""
8080
self._model.train(X, y)
81+
82+
def fit(self, X: np.ndarray, y: np.ndarray) -> None:
83+
"""
84+
Train the surrogate model.
85+
86+
Parameters
87+
----------
88+
X : np.ndarray
89+
Input data points.
90+
y : np.ndarray
91+
Corresponding target values.
92+
"""
93+
self._fit(X, y)

deepcave/evaluators/mo_ablation.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,7 @@ def calculate(
122122
self,
123123
objectives: Optional[Union[Objective, List[Objective]]], # noqa
124124
budget: Optional[Union[int, float]] = None, # noqa
125-
n_trees: int = 50, # noqa
126-
seed: int = 0, # noqa
125+
model: Any = None,
127126
) -> None:
128127
"""
129128
Calculate the MO ablation path performances and improvements.
@@ -135,12 +134,9 @@ def calculate(
135134
budget : Optional[Union[int, float]]
136135
The budget to be considered. If None, all budgets of the run are considered.
137136
Default is None.
138-
n_trees : int
139-
The number of trees for the surrogate model.
140-
Default is 50.
141-
seed : int
142-
The seed for the surrogate model.
143-
Default is 0.
137+
model : Any
138+
For mo ablation this parameter does not do anything, except fit the head.
139+
By default None.
144140
"""
145141
assert isinstance(objectives, list)
146142
for objective in objectives:
@@ -166,8 +162,9 @@ def calculate(
166162

167163
# train one model per objective
168164
Y = df[normed].to_numpy()
169-
model = RandomForestSurrogate(self.cs, seed=seed, n_trees=n_trees)
170-
model._fit(X, Y)
165+
if model is None:
166+
model = RandomForestSurrogate(self.cs, seed=0, n_trees=50)
167+
model.fit(X, Y)
171168
self.models.append(model)
172169

173170
weightings = get_weightings(objectives_normed, df)

deepcave/plugins/hyperparameter/ablation_paths.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ def process(run: AbstractRun, inputs: Dict[str, Any]) -> Dict[str, Any]:
338338
data: Dict[Any, Any] = {}
339339
for budget_id, budget in enumerate(budgets):
340340
assert isinstance(budget, (int, float))
341-
evaluator.calculate(objective, budget, n_trees=n_trees, seed=0)
341+
evaluator.calculate(objective, budget)
342342
if isinstance(objective, list):
343343
assert isinstance(evaluator, MOAblation)
344344
data[budget_id] = evaluator.get_importances()

deepcave/runs/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,6 +1317,7 @@ def get_encoded_data(
13171317
config_ids = []
13181318

13191319
results = self.get_all_costs(budget, statuses, seed)
1320+
13201321
for config_id, config_costs in results.items():
13211322
config = self.configs[config_id]
13221323
for seed, costs in config_costs.items():

deepcave/worker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
This module can create a redis queue worker.
1919
"""
2020

21-
from rq import Connection, Worker
21+
from rq import Connection, Worker # type: ignore
2222

2323
from deepcave import queue
2424

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"0": {
3+
"alpha": 0.0,
4+
"beta": 0.0,
5+
"gamma": 0.0
6+
},
7+
"1": {
8+
"alpha": 1.0,
9+
"beta": 1.0,
10+
"gamma": 1.0
11+
}
12+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"name": null, "hyperparameters": [{"type": "uniform_float", "name": "alpha", "lower": 0.0, "upper": 1.0, "default_value": 0.0, "log": false, "meta": null}, {"type": "uniform_float", "name": "beta", "lower": 0.0, "upper": 1.0, "default_value": 0.0, "log": false, "meta": null}, {"type": "uniform_float", "name": "gamma", "lower": 0.0, "upper": 1.0, "default_value": 0.0, "log": false, "meta": null}], "conditions": [], "forbiddens": [], "python_module_version": "1.2.0", "format_version": 0.4}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[0, 20, -1, [0.0, 0.0], 0.0, 0.0, 1, {}]
2+
[0, 40, -1, [0.0, 0.0], 0.0, 0.0, 1, {}]
3+
[0, 60, -1, [0.0, 0.0], 0.0, 0.0, 1, {}]
4+
[1, 20, -1, [1.0, 1.0], 0.0, 0.0, 1, {}]
5+
[1, 40, -1, [1.0, 1.0], 0.0, 0.0, 1, {}]
6+
[1, 60, -1, [1.0, 1.0], 0.0, 0.0, 1, {}]

0 commit comments

Comments
 (0)