You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently I have a version that basically hacks into the internals of metahyper to get an ask() and tell() interface to all NePs has to offer in terms of optimizers. This implementation basically relieves NeP's of actually having to evaluate anything, I just want the suggestions from the optimizers.
Updating to 0.10.0 gives a new warning:
WARNING:amltk.optimization.optimizers.neps:There are 1 configs that were sampled, but have no worker assigned. Sometimes this is due to a delay in the filesystem communication, but most likely some configs crashed during their execution or a jobtime-limit was reached.
I can't really complain as NePs doesn't expose this. I'd like to keep NePs as an optional dependancy for AMLTK but I would need a stable API to base off of.
classNEPSOptimizer(Optimizer[NEPSTrialInfo]):
"""An optimizer that uses SMAC to optimize a config space."""def__init__(
self,
*,
space: SearchSpace,
optimizer: BaseOptimizer,
working_dir: Path,
bucket: Bucket|None=None,
ignore_errors: bool=True,
loss_value_on_error: float|None=None,
cost_value_on_error: float|None=None,
) ->None:
"""Initialize the optimizer. Args: space: The space to use. optimizer: The optimizer to use. working_dir: The directory to use for the optimization. bucket: The bucket to give to trials generated from this optimizer. ignore_errors: Whether the optimizers should ignore errors from trials. loss_value_on_error: The value to use for the loss if the trial fails. cost_value_on_error: The value to use for the cost if the trial fails. """super().__init__(bucket=bucket)
self.space=spaceself.optimizer=optimizerself.working_dir=working_dirself.ignore_errors=ignore_errorsself.loss_value_on_error=loss_value_on_errorself.cost_value_on_error=cost_value_on_errorself.optimizer_state_file=self.working_dir/"optimizer_state.yaml"self.base_result_directory=self.working_dir/"results"self.serializer=metahyper.utils.YamlSerializer(self.optimizer.load_config)
self.working_dir.mkdir(parents=True, exist_ok=True)
self.base_result_directory.mkdir(parents=True, exist_ok=True)
@classmethoddefcreate( # noqa: PLR0913cls,
*,
space: (
SearchSpace|ConfigurationSpace|Mapping[str, ConfigurationSpace|Parameter]
),
bucket: Bucket|None=None,
searcher: str|BaseOptimizer="default",
working_dir: str|Path="neps",
overwrite: bool=True,
loss_value_on_error: float|None=None,
cost_value_on_error: float|None=None,
max_cost_total: float|None=None,
ignore_errors: bool=True,
searcher_kwargs: Mapping[str, Any] |None=None,
) ->Self:
"""Create a new NEPS optimizer. Args: space: The space to use. bucket: The bucket to give to trials generated by this optimizer. searcher: The searcher to use. working_dir: The directory to use for the optimization. overwrite: Whether to overwrite the working directory if it exists. loss_value_on_error: The value to use for the loss if the trial fails. cost_value_on_error: The value to use for the cost if the trial fails. max_cost_total: The maximum cost to use for the optimization. !!! warning This only effects the optimization if the searcher utilizes the budget for it's actual suggestion of the next config. If the searcher does not use the budget. This parameter has no effect. The user is still expected to stop `ask()`'ing for configs when they have reached some budget. ignore_errors: Whether the optimizers should ignore errors from trials or whether they should be taken into account. Please set `loss_on_value` and/or `cost_value_on_error` if you set this to `False`. searcher_kwargs: Additional kwargs to pass to the searcher. """space=_to_neps_space(space)
searcher=_to_neps_searcher(
space=space,
searcher=searcher,
loss_value_on_error=loss_value_on_error,
cost_value_on_error=cost_value_on_error,
max_cost_total=max_cost_total,
ignore_errors=ignore_errors,
searcher_kwargs=searcher_kwargs,
)
working_dir=Path(working_dir)
ifworking_dir.exists() andoverwrite:
logger.info(f"Removing existing working directory {working_dir}")
shutil.rmtree(working_dir)
returncls(
space=space,
bucket=bucket,
optimizer=searcher,
working_dir=working_dir,
loss_value_on_error=loss_value_on_error,
cost_value_on_error=cost_value_on_error,
)
@overridedefask(self) ->Trial[NEPSTrialInfo]:
"""Ask the optimizer for a new config. Returns: The trial info for the new config. """withself.optimizer.using_state(self.optimizer_state_file, self.serializer):
(
config_id,
config,
pipeline_directory,
previous_pipeline_directory,
) =metahyper.api._sample_config( # type: ignoreoptimization_dir=self.working_dir,
sampler=self.optimizer,
serializer=self.serializer,
logger=logger,
)
ifisinstance(config, SearchSpace):
_config=config.hp_values()
else:
_config= {
k: v.valueifisinstance(v, Parameter) elsevfork, vinconfig.items()
}
info=NEPSTrialInfo(
name=str(config_id),
config=deepcopy(_config),
pipeline_directory=pipeline_directory,
previous_pipeline_directory=previous_pipeline_directory,
)
trial=Trial(
name=info.name,
config=info.config,
info=info,
seed=None,
bucket=self.bucket,
)
logger.debug(f"Asked for trial {trial.name}")
returntrial@overridedeftell(self, report: Trial.Report[NEPSTrialInfo]) ->None:
"""Tell the optimizer the result of the sampled config. Args: report: The report of the trial. """logger.debug(f"Telling report for trial {report.trial.name}")
info=report.infoassertinfoisnotNone# This is how NEPS handles errorsresult: Literal["error"] |dict[str, Any]
ifreport.statusin (Trial.Status.CRASHED, Trial.Status.FAIL):
result="error"else:
result=report.resultsmetadata: dict[str, Any] = {"time_end": report.time.end}
ifresult=="error":
ifnotself.ignore_errors:
ifself.loss_value_on_errorisnotNone:
report.results["loss"] =self.loss_value_on_errorifself.cost_value_on_errorisnotNone:
report.results["cost"] =self.cost_value_on_errorelse:
if (loss:=result.get("loss")) isnotNone:
report.results["loss"] =float(loss)
else:
raiseValueError(
"The 'loss' should be provided if the trial is successful"f"\n{result=}",
)
cost=result.get("cost")
if (cost:=result.get("cost")) isnotNone:
cost=float(cost)
result["cost"] =costaccount_for_cost=result.get("account_for_cost", True)
ifaccount_for_cost:
withself.optimizer.using_state(
self.optimizer_state_file,
self.serializer,
):
self.optimizer.used_budget+=costmetadata["budget"] = {
"max": self.optimizer.budget,
"used": self.optimizer.used_budget,
"eval_cost": cost,
"account_for_cost": account_for_cost,
}
elifself.optimizer.budgetisnotNone:
raiseValueError(
"'cost' should be provided when the optimizer has a budget"f"\n{result=}",
)
# Dump resultsself.serializer.dump(result, info.pipeline_directory/"result")
# Load and dump metadataconfig_metadata=self.serializer.load(info.pipeline_directory/"metadata")
config_metadata.update(metadata)
self.serializer.dump(config_metadata, info.pipeline_directory/"metadata")
@override@classmethoddefpreferred_parser(cls) ->NEPSPreferredParser:
"""The preferred parser for this optimizer."""# TODO: We might want a custom one for neps.SearchSpace, for now we will# use config space but without conditions as NePs doesn't support conditionalsreturnpartial(configspace_parser, conditionals=False)
The text was updated successfully, but these errors were encountered:
Currently I have a version that basically hacks into the internals of
metahyper
to get anask()
andtell()
interface to all NePs has to offer in terms of optimizers. This implementation basically relieves NeP's of actually having to evaluate anything, I just want the suggestions from the optimizers.Updating to
0.10.0
gives a new warning:I can't really complain as
NePs
doesn't expose this. I'd like to keep NePs as an optional dependancy for AMLTK but I would need a stable API to base off of.The text was updated successfully, but these errors were encountered: