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

Add tuning history to OptHistory #1188

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion examples/advanced/parallelization_comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

def _count_pipelines(opt_history: Optional[OptHistory]) -> int:
if opt_history is not None:
return reduce(operator.add, map(len, opt_history.individuals), 0)
return reduce(operator.add, map(len, opt_history.generations), 0)
return 0


Expand Down
2 changes: 1 addition & 1 deletion examples/simple/pipeline_and_history_visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def run_pipeline_and_history_visualization():
""" The function runs visualization of the composing history and the best pipeline. """
# Gather pipeline and history.
history = OptHistory.load(Path(fedot_project_root(), 'examples', 'data', 'histories', 'scoring_case_history.json'))
pipeline = PipelineAdapter().restore(history.individuals[-1][-1].graph)
pipeline = PipelineAdapter().restore(history.generations[-1][-1].graph)
# Show visualizations.
pipeline.show()
history_visualizer = PipelineHistoryVisualizer(history)
Expand Down
12 changes: 6 additions & 6 deletions fedot/api/api_utils/api_composer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from golem.core.log import default_log
from golem.core.optimisers.opt_history_objects.opt_history import OptHistory
from golem.core.tuning.simultaneous import SimultaneousTuner
from golem.core.tuning.tuner_interface import BaseTuner

from fedot.api.api_utils.assumptions.assumptions_handler import AssumptionsHandler
from fedot.api.api_utils.params import ApiParams
Expand Down Expand Up @@ -72,11 +73,8 @@ def obtain_model(self, train_data: InputData) -> Tuple[Pipeline, Sequence[Pipeli
fitted_assumption
)
if with_tuning:
best_pipeline = self.tune_final_pipeline(train_data, best_pipeline)
if gp_composer.history:
adapter = self.params.graph_generation_params.adapter
gp_composer.history.tuning_result = adapter.adapt(best_pipeline)
# enforce memory cleaning
best_pipeline = self.tune_final_pipeline(train_data, best_pipeline, gp_composer.history)

gc.collect()

self.log.message('Model generation finished')
Expand Down Expand Up @@ -142,7 +140,8 @@ def compose_pipeline(self, train_data: InputData, initial_assumption: Sequence[P
best_pipeline = best_pipelines[0] if isinstance(best_pipelines, Sequence) else best_pipelines
return best_pipeline, best_pipeline_candidates, gp_composer

def tune_final_pipeline(self, train_data: InputData, pipeline_gp_composed: Pipeline) -> Pipeline:
def tune_final_pipeline(self, train_data: InputData, pipeline_gp_composed: Pipeline,
history: Optional[OptHistory]) -> Tuple[BaseTuner, Pipeline]:
kasyanovse marked this conversation as resolved.
Show resolved Hide resolved
""" Launch tuning procedure for obtained pipeline by composer """
timeout_for_tuning = abs(self.timer.determine_resources_for_tuning()) / 60
tuner = (TunerBuilder(self.params.task)
Expand All @@ -152,6 +151,7 @@ def tune_final_pipeline(self, train_data: InputData, pipeline_gp_composed: Pipel
.with_timeout(datetime.timedelta(minutes=timeout_for_tuning))
.with_eval_time_constraint(self.params.composer_requirements.max_graph_fit_time)
.with_requirements(self.params.composer_requirements)
.with_history(history)
.build(train_data))

if self.timer.have_time_for_tuning():
Expand Down
7 changes: 6 additions & 1 deletion fedot/core/pipelines/tuning/tuner_builder.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from datetime import timedelta
from typing import Type, Union, Iterable, Sequence
from typing import Iterable, Sequence, Type, Union

from golem.core.tuning.optuna_tuner import OptunaTuner
from golem.core.tuning.simultaneous import SimultaneousTuner
Expand Down Expand Up @@ -33,6 +33,7 @@ def __init__(self, task: Task):
self.eval_time_constraint = None
self.additional_params = {}
self.adapter = PipelineAdapter()
self.history = None
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вроде бы новых тестов для history нет, значит во всех тестах он по умолчанию принимается равным None?
В таком случае стоит добавить хотя бы один тест, где он не будет равен None.


def with_tuner(self, tuner: Type[BaseTuner]):
self.tuner_class = tuner
Expand Down Expand Up @@ -89,6 +90,10 @@ def with_adapter(self, adapter):
self.adapter = adapter
return self

def with_history(self, history):
self.history = history
return self

def with_additional_params(self, **parameters):
self.additional_params.update(parameters)
return self
Expand Down
8 changes: 4 additions & 4 deletions test/integration/api_params/test_main_api_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@ class TimeoutParams:
TIMEOUT_CASES = [
TimeoutParams(
test_input={'timeout': -1, 'num_of_generations': 1},
test_answer=lambda hist: len(hist.individuals) == 1 + 2 # num of gens + initial and final gens
test_answer=lambda hist: len(hist.generations) == 1 + 2 # num of gens + initial and final gens
),
TimeoutParams(
test_input={'timeout': None, 'num_of_generations': 1},
test_answer=lambda hist: len(hist.individuals) == 1 + 2
test_answer=lambda hist: len(hist.generations) == 1 + 2
),
TimeoutParams(
test_input={'timeout': 0.1, 'num_of_generations': 15},
test_answer=lambda hist: len(hist.individuals) < 15 + 2
test_answer=lambda hist: len(hist.generations) < 15 + 2
),
TimeoutParams(
test_input={'timeout': -2, 'num_of_generations': 15},
test_answer=ValueError()
),
TimeoutParams(
test_input={'timeout': -1, 'num_of_generations': 3},
test_answer=lambda hist: len(hist.individuals) == 3 + 2
test_answer=lambda hist: len(hist.generations) == 3 + 2
)
]

Expand Down
8 changes: 4 additions & 4 deletions test/integration/composer/test_composer.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,10 @@ def test_parameter_free_composer_build_pipeline_correct(data_fixture, request):
roc_on_valid_gp_composed = roc_auc(y_true=dataset_to_validate.target,
y_score=predicted_gp_composed.predict)

all_individuals = len(gp_composer.history.individuals)
population_len = sum([len(history) for history in gp_composer.history.individuals]) / all_individuals
all_individuals = len(gp_composer.history.generations)
population_len = sum([len(history) for history in gp_composer.history.generations]) / all_individuals

assert population_len != len(gp_composer.history.individuals[0])
assert population_len != len(gp_composer.history.generations[0])
assert roc_on_valid_gp_composed > 0.6


Expand Down Expand Up @@ -226,7 +226,7 @@ def test_gp_composer_with_adaptive_depth(data_fixture, request):

composer.compose_pipeline(data=dataset_to_compose)

generations = composer.history.individuals
generations = composer.history.generations
current_depth = composer.optimizer.requirements.max_depth
assert req.start_depth <= current_depth < max_depth, f"max depth couldn't have been reached in {num_gen}"
assert all(ind.graph.depth < max_depth for ind in generations[-1]), "last generation is too deep"
Expand Down
18 changes: 9 additions & 9 deletions test/integration/composer/test_history.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ def lagged_ridge_rfr_pipeline():
def _test_individuals_in_history(history: OptHistory):
uids = set()
ids = set()
for ind in chain(*history.individuals):
# All individuals in `history.individuals` must have a native generation.
for ind in chain(*history.generations):
# All individuals in `history.generations` must have a native generation.
assert ind.has_native_generation
assert ind.fitness
if ind.native_generation == 0:
Expand Down Expand Up @@ -83,7 +83,7 @@ def test_newly_generated_history(n_jobs: int):
history = auto_model.history

assert history is not None
assert len(history.individuals) == num_of_gens + 2 # initial_assumptions + num_of_gens + final_choices
assert len(history.generations) == num_of_gens + 2 # initial_assumptions + num_of_gens + final_choices
assert len(history.archive_history) == num_of_gens + 2 # initial_assumptions + num_of_gens + final_choices
assert len(history.initial_assumptions) >= 2
assert len(history.final_choices) == 1
Expand Down Expand Up @@ -151,15 +151,15 @@ def test_history_backward_compatibility():
assert all_historical_fitness
assert historical_fitness
# Assert that all fitness properties are valid.
assert len(history.individuals) == len(historical_fitness)
assert len(history.generations) == len(historical_fitness)
assert np.all(len(generation) == len(gen_fitness)
for generation, gen_fitness in zip(history.individuals, historical_fitness))
assert np.all(np.equal([ind.fitness.value for ind in chain(*history.individuals)], all_historical_fitness))
for generation, gen_fitness in zip(history.generations, historical_fitness))
assert np.all(np.equal([ind.fitness.value for ind in chain(*history.generations)], all_historical_fitness))
# Assert that fitness, graph, parent_individuals, and objective are valid
assert all(isinstance(ind.fitness, SingleObjFitness) for ind in chain(*history.individuals))
assert all(ind.graph.nodes for ind in chain(*history.individuals))
assert all(isinstance(ind.fitness, SingleObjFitness) for ind in chain(*history.generations))
assert all(ind.graph.nodes for ind in chain(*history.generations))
assert all(isinstance(parent_ind, Individual)
for ind in chain(*history.individuals)
for ind in chain(*history.generations)
for parent_op in ind.operators_from_prev_generation
for parent_ind in parent_op.parent_individuals)
_test_individuals_in_history(history)
2 changes: 1 addition & 1 deletion test/integration/quality/test_quality_improvement.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def test_multiobjective_improvement():


def check_improvement(history):
first_pop = history.individuals[1]
first_pop = history.generations[1]
pareto_front = history.archive_history[-1]

first_pop_metrics = get_mean_metrics(first_pop)
Expand Down
Loading