Skip to content

Commit 224aa44

Browse files
authored
[fix] Hotfix debug no training in simple intensifier (#370)
* [fix] Fix the no-training-issue when using simple intensifier * [test] Add a test for the modification * [fix] Modify the default budget so that the budget is compatible Since the previous version does not consider the provided budget_type when determining the default budget, I modified this part so that the default budget does not mix up the default budget for epochs and runtime. Note that since the default pipeline config defines epochs as the default budget, I also followed this rule when taking the default value. * [fix] Fix a mypy error * [fix] Change the total runtime for single config in the example Since the training sometimes does not finish in time, I increased the total runtime for the training so that we can accomodate the training in the given amount of time. * [fix] [refactor] Fix the SMAC requirement and refactor some conditions
1 parent 6554702 commit 224aa44

File tree

4 files changed

+63
-23
lines changed

4 files changed

+63
-23
lines changed

autoPyTorch/evaluation/tae.py

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,23 @@ def __init__(
201201

202202
self.search_space_updates = search_space_updates
203203

204+
def _check_and_get_default_budget(self) -> float:
205+
budget_type_choices = ('epochs', 'runtime')
206+
budget_choices = {
207+
budget_type: float(self.pipeline_config.get(budget_type, np.inf))
208+
for budget_type in budget_type_choices
209+
}
210+
211+
# budget is defined by epochs by default
212+
budget_type = str(self.pipeline_config.get('budget_type', 'epochs'))
213+
if self.budget_type is not None:
214+
budget_type = self.budget_type
215+
216+
if budget_type not in budget_type_choices:
217+
raise ValueError(f"budget type must be in {budget_type_choices}, but got {budget_type}")
218+
else:
219+
return budget_choices[budget_type]
220+
204221
def run_wrapper(
205222
self,
206223
run_info: RunInfo,
@@ -218,26 +235,19 @@ def run_wrapper(
218235
RunValue:
219236
Contains information about the status/performance of config
220237
"""
221-
if self.budget_type is None:
222-
if run_info.budget != 0:
223-
raise ValueError(
224-
'If budget_type is None, budget must be.0, but is %f' % run_info.budget
225-
)
226-
else:
227-
if run_info.budget == 0:
228-
# SMAC can return budget zero for intensifiers that don't have a concept
229-
# of budget, for example a simple bayesian optimization intensifier.
230-
# Budget determines how our pipeline trains, which can be via runtime or epochs
231-
epochs_budget = self.pipeline_config.get('epochs', np.inf)
232-
runtime_budget = self.pipeline_config.get('runtime', np.inf)
233-
run_info = run_info._replace(budget=min(epochs_budget, runtime_budget))
234-
elif run_info.budget <= 0:
235-
raise ValueError('Illegal value for budget, must be greater than zero but is %f' %
236-
run_info.budget)
237-
if self.budget_type not in ('epochs', 'runtime'):
238-
raise ValueError("Illegal value for budget type, must be one of "
239-
"('epochs', 'runtime'), but is : %s" %
240-
self.budget_type)
238+
# SMAC returns non-zero budget for intensification
239+
# In other words, SMAC returns budget=0 for a simple intensifier (i.e. no intensification)
240+
is_intensified = (run_info.budget != 0)
241+
default_budget = self._check_and_get_default_budget()
242+
243+
if self.budget_type is None and is_intensified:
244+
raise ValueError(f'budget must be 0 (=no intensification) for budget_type=None, but got {run_info.budget}')
245+
if self.budget_type is not None and run_info.budget < 0:
246+
raise ValueError(f'budget must be greater than zero but got {run_info.budget}')
247+
248+
if self.budget_type is not None and not is_intensified:
249+
# The budget will be provided in train evaluator when budget_type is None
250+
run_info = run_info._replace(budget=default_budget)
241251

242252
remaining_time = self.stats.get_remaing_time_budget()
243253

@@ -261,6 +271,10 @@ def run_wrapper(
261271

262272
self.logger.info("Starting to evaluate configuration %s" % run_info.config.config_id)
263273
run_info, run_value = super().run_wrapper(run_info=run_info)
274+
275+
if not is_intensified: # It is required for the SMAC compatibility
276+
run_info = run_info._replace(budget=0.0)
277+
264278
return run_info, run_value
265279

266280
def run(

examples/40_advanced/example_single_configuration.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@
6666
pipeline, run_info, run_value, dataset = estimator.fit_pipeline(dataset=dataset,
6767
configuration=configuration,
6868
budget_type='epochs',
69-
budget=10,
70-
run_time_limit_secs=100
69+
budget=5,
70+
run_time_limit_secs=75
7171
)
7272

7373
# The fit_pipeline command also returns a named tuple with the pipeline constraints

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ imgaug>=0.4.0
1010
ConfigSpace>=0.4.14,<0.5
1111
pynisher>=0.6.3
1212
pyrfr>=0.7,<0.9
13-
smac==0.14.0
13+
smac>=0.14.0
1414
dask
1515
distributed>=2.2.0
1616
catboost

test/test_evaluation/test_evaluation.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,32 @@ def test_silent_exception_in_target_function(self):
394394
self.assertNotIn('exit_status', info[1].additional_info)
395395
self.assertNotIn('traceback', info[1])
396396

397+
def test_eval_with_simple_intensification(self):
398+
config = unittest.mock.Mock(spec=int)
399+
config.config_id = 198
400+
401+
ta = ExecuteTaFuncWithQueue(backend=BackendMock(), seed=1,
402+
stats=self.stats,
403+
memory_limit=3072,
404+
metric=accuracy,
405+
cost_for_crash=get_cost_of_crash(accuracy),
406+
abort_on_first_run_crash=False,
407+
logger_port=self.logger_port,
408+
pynisher_context='fork',
409+
budget_type='runtime'
410+
)
411+
ta.pynisher_logger = unittest.mock.Mock()
412+
run_info = RunInfo(config=config, cutoff=3000, instance=None,
413+
instance_specific=None, seed=1, capped=False)
414+
415+
for budget in [0.0, 50.0]:
416+
# Simple intensification always returns budget = 0
417+
# Other intensifications return a non-zero value
418+
self.stats.submitted_ta_runs += 1
419+
run_info = run_info._replace(budget=budget)
420+
run_info_out, _ = ta.run_wrapper(run_info)
421+
self.assertEqual(run_info_out.budget, budget)
422+
397423

398424
@pytest.mark.parametrize("metric,expected", [(accuracy, 1.0), (log_loss, MAXINT)])
399425
def test_get_cost_of_crash(metric, expected):

0 commit comments

Comments
 (0)