Skip to content

Commit

Permalink
Fixes: hyperspace and its tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Caparrini committed Oct 22, 2024
1 parent ed6ccfd commit c11d251
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 26 deletions.
44 changes: 32 additions & 12 deletions mloptimizer/application/hyperparameter_space_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,51 @@ class HyperparameterSpaceService:
"""
Service to manage hyperparameter spaces.
"""
base_config_path: str = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "infrastructure", "config", "hyperspace")

def __init__(self, base_config_path=None):
if base_config_path is None:
base_config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "infrastructure", "config", "hyperspace")
self.base_config_path = base_config_path
@staticmethod
def _detect_library(estimator_class):
module_name = estimator_class.__module__
supported_modules = ['sklearn', 'catboost', 'xgboost']

def load_hyperparameter_space(self, estimator_class):
for module in supported_modules:
if module_name.startswith(module):
return module

raise ValueError(f"Estimator class {estimator_class.__name__} not supported")

def load_default_hyperparameter_space(self, estimator_class):
"""
Load a default hyperparameter space for an estimator from a JSON file.
"""
file_name = f"{estimator_class.__name__}_default_HyperparamSpace.json"
library = self._detect_library(estimator_class)
file_path = os.path.join(self.base_config_path, library, file_name)

return self.load_hyperparameter_space(file_path)

@staticmethod
def load_hyperparameter_space(hyperparam_space_json_path):
"""
Load a hyperparameter space for an estimator from a JSON file.
"""
file_name = f"{estimator_class.__name__.lower()}_hyperparameter_space.json"
file_path = os.path.join(self.base_config_path, file_name)
if not os.path.exists(hyperparam_space_json_path):
raise FileNotFoundError(f"The file {hyperparam_space_json_path} does not exist")

# Delegate to repository
hyperparam_data = HyperparameterSpaceRepository.load_json(file_path)
hyperparam_data = HyperparameterSpaceRepository.load_json(hyperparam_space_json_path)

return HyperparameterSpace.from_json_data(hyperparam_data)

def save_hyperparameter_space(self, hyperparam_space: HyperparameterSpace, estimator_class, overwrite=False):
@staticmethod
def save_hyperparameter_space(hyperparam_space: HyperparameterSpace,
file_path, overwrite=False):
"""
Save a hyperparameter space for an estimator to a JSON file.
"""
file_name = f"{estimator_class.__name__.lower()}_hyperparameter_space.json"
file_path = os.path.join(self.base_config_path, file_name)
if os.path.exists(file_path) and not overwrite:
raise FileExistsError(f"The file {file_path} already exists.")

# Delegate to repository
hyperparam_data = {
Expand Down
4 changes: 4 additions & 0 deletions mloptimizer/domain/hyperspace/hyperspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,7 @@ def __str__(self):
def __repr__(self):
return (f"HyperparameterSpace(fixed_hyperparams={self.fixed_hyperparams}, "
f"evolvable_hyperparams={self.evolvable_hyperparams})")

def __eq__(self, other):
return (self.fixed_hyperparams == other.fixed_hyperparams and
self.evolvable_hyperparams == other.evolvable_hyperparams)
18 changes: 11 additions & 7 deletions mloptimizer/interfaces/api/hyperspace_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,36 @@ def __init__(self):
self.evolvable_hyperparams = {}
self.service = HyperparameterSpaceService()

def add_int_param(self, name, min_value, max_value):
def add_int_param(self, name: str, min_value: int, max_value: int):
param = Hyperparam(name=name, min_value=min_value, max_value=max_value, hyperparam_type='int')
self.evolvable_hyperparams[name] = param
return self

def add_float_param(self, name, min_value, max_value, scale=100):
def add_float_param(self, name: str, min_value: int, max_value: int, scale=100):
param = Hyperparam(name=name, min_value=min_value, max_value=max_value, hyperparam_type='float', scale=scale)
self.evolvable_hyperparams[name] = param
return self

def add_categorical_param(self, name, values):
def add_categorical_param(self, name: str, values: list):
param = Hyperparam.from_values_list(name=name, values_str=values)
self.evolvable_hyperparams[name] = param
return self

def set_fixed_param(self, name, value):
def set_fixed_param(self, name: str, value):
self.fixed_hyperparams[name] = value
return self

def set_fixed_params(self, fixed_params: dict):
self.fixed_hyperparams = fixed_params
return self

def build(self):
return HyperparameterSpace(fixed_hyperparams=self.fixed_hyperparams, evolvable_hyperparams=self.evolvable_hyperparams)

def load_default_space(self, estimator_class):
"""Load a default hyperparameter space using the application service."""
return self.service.load_hyperparameter_space(estimator_class)
return self.service.load_default_hyperparameter_space(estimator_class)

def save_space(self, hyperparam_space, estimator_class, overwrite=False):
def save_space(self, hyperparam_space, file_path, overwrite=False):
"""Save the hyperparameter space using the application service."""
self.service.save_hyperparameter_space(hyperparam_space, estimator_class, overwrite)
self.service.save_hyperparameter_space(hyperparam_space, file_path, overwrite)
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# mloptimizer/test/interfaces/api/test_hyperparameter_space_builder.py

import os.path
import pytest
from mloptimizer.domain.hyperspace import HyperparameterSpace, Hyperparam
from mloptimizer.application import HyperparameterSpaceService
from sklearn.tree import DecisionTreeClassifier

from mloptimizer.domain.hyperspace import HyperparameterSpace
from mloptimizer.interfaces.api import HyperparameterSpaceBuilder
from mloptimizer.infrastructure.repositories import HyperparameterSpaceRepository

Expand All @@ -20,7 +21,7 @@ def test_add_int_param(mock_service):

def test_add_float_param(mock_service):
builder = HyperparameterSpaceBuilder()
builder.add_float_param("param2", 0.1, 1.0)
builder.add_float_param("param2", 10, 100)
assert "param2" in builder.evolvable_hyperparams


Expand All @@ -35,6 +36,11 @@ def test_set_fixed_param(mock_service):
builder.set_fixed_param("fixed_param1", 5)
assert "fixed_param1" in builder.fixed_hyperparams

def test_set_fixed_params(mock_service):
builder = HyperparameterSpaceBuilder()
builder.set_fixed_params({"fixed_param1": 5, "fixed_param2": 10})
assert "fixed_param1" in builder.fixed_hyperparams
assert "fixed_param2" in builder.fixed_hyperparams

def test_build(mock_service):
builder = HyperparameterSpaceBuilder()
Expand All @@ -55,16 +61,16 @@ def test_load_default_space(mock_service, mocker):
})

builder = HyperparameterSpaceBuilder()
result = builder.load_default_space(estimator_class=object)
result = builder.load_default_space(estimator_class=DecisionTreeClassifier)

assert isinstance(result, HyperparameterSpace)


def test_save_space(mock_service, mocker):
def test_save_space(mock_service, mocker, tmp_path):
space = HyperparameterSpace({}, {})
mocker.patch('mloptimizer.infrastructure.repositories.HyperparameterSpaceRepository.save_json')

builder = HyperparameterSpaceBuilder()
builder.save_space(space, estimator_class=object, overwrite=True)
builder.save_space(space, os.path.join(tmp_path, "hyperspace.json"), overwrite=True)

HyperparameterSpaceRepository.save_json.assert_called_once()

0 comments on commit c11d251

Please sign in to comment.