Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
1cf9bd2
Update pyproject.toml
fkiraly Oct 22, 2024
aaff76e
Merge remote-tracking branch 'upstream/master' into optimizer-base-class
fkiraly Oct 22, 2024
40fe192
base classes and sklearn
fkiraly Oct 30, 2024
606ba62
Update sklearn_cv_experiment.py
fkiraly Oct 30, 2024
34581ce
refactor integration
fkiraly Oct 30, 2024
3bb0044
add API-design draft for optimizer, experiment and search-space
SimonBlanke Jan 12, 2025
9dcc025
add docstring and explanations
SimonBlanke Jan 15, 2025
b5d6e7c
fix import path
SimonBlanke Jan 18, 2025
ca0379a
move search-space class
SimonBlanke Jan 18, 2025
0fd016e
Merge branch 'dev' into v5-API-design
SimonBlanke Jan 18, 2025
e6da3eb
put v4 API into separate dir
SimonBlanke Jan 26, 2025
8f4fdc6
add src dir for v5 API
SimonBlanke Jan 26, 2025
5580677
create working prototype of v5 API
SimonBlanke Jan 26, 2025
12feb4a
Merge branch 'dev' into v5-API-design
SimonBlanke Jan 27, 2025
87c9076
add example for new API
SimonBlanke Jan 27, 2025
040d63f
get callbacks and catch parameters from experiment
SimonBlanke Feb 14, 2025
e5336f0
set callbacks and catch to empty dict if None
SimonBlanke Feb 14, 2025
1b028c2
use experiment to pass objective-function
SimonBlanke Feb 14, 2025
eec01d0
show experiment name in output (instead of objective-function)
SimonBlanke Feb 14, 2025
d9f12b6
get obj-func from experiment
SimonBlanke Feb 14, 2025
ce8b82f
readd result attributes
SimonBlanke Feb 14, 2025
2e75c3f
remove print
SimonBlanke Feb 23, 2025
66ca1f6
reformat
SimonBlanke Feb 23, 2025
957e7de
add composite optimizer class
SimonBlanke Feb 23, 2025
549e200
use comp-opt class to enable parallel via 'add'-method
SimonBlanke Feb 23, 2025
7b8813c
enable adding more than 2 optimizers together
SimonBlanke Feb 23, 2025
e110061
rename hyper_optimizer -> search and fix multiprocessing
SimonBlanke Feb 23, 2025
a565549
refactor callbacks/catch-parameter
SimonBlanke Mar 4, 2025
b83143f
move old tests; add new test-base file
SimonBlanke Mar 16, 2025
19719e2
implement sklearn-approved experiment class
SimonBlanke Mar 16, 2025
acbfa25
small fix
SimonBlanke Mar 16, 2025
56b6259
add test dir for api; add callback test
SimonBlanke Mar 30, 2025
9b5c0a8
move callbacks feature to separate decorator function
SimonBlanke Mar 30, 2025
dca41b6
add callback tests; adapt tests to new callbacks api
SimonBlanke Mar 30, 2025
09b01e4
rename variables
SimonBlanke Mar 30, 2025
a10d915
small fix
SimonBlanke Mar 30, 2025
0fd35cd
add tests for catch
SimonBlanke Mar 30, 2025
44638c8
add callback tests
SimonBlanke Mar 30, 2025
ce8e188
fix nth_iter
SimonBlanke Mar 30, 2025
ebec3bb
add early_stop tests
SimonBlanke Mar 31, 2025
56f1335
fix search_config
SimonBlanke Mar 31, 2025
8a306bd
add test_initializers.py
SimonBlanke Apr 1, 2025
5ffca25
add test_random_state.py
SimonBlanke Apr 1, 2025
30c1240
add test_search_spaces.py
SimonBlanke Apr 1, 2025
10aece0
add test_results.py
SimonBlanke Apr 1, 2025
429d72c
add test_results_methods.py
SimonBlanke Apr 1, 2025
bba2abf
add test_max_score.py
SimonBlanke Apr 5, 2025
3dc50a1
add test_max_time.py
SimonBlanke Apr 5, 2025
6e87b0b
cleanup imports
SimonBlanke Apr 5, 2025
a19d64c
add test_constr_opt.py
SimonBlanke Apr 5, 2025
9a567bf
fix float error in early stop test
SimonBlanke Apr 12, 2025
a33a5ee
remove test that uses non-public api
SimonBlanke Apr 12, 2025
3a33ba0
fix test
SimonBlanke Apr 12, 2025
909b346
pass objective-function instead of experiment
SimonBlanke Apr 12, 2025
ad12386
add test_distribution.py
SimonBlanke Apr 12, 2025
32a2c8e
add tests for cmd output
SimonBlanke Apr 12, 2025
9fd4482
fix verbosity
SimonBlanke Apr 12, 2025
b90f176
separate verbosity tests
SimonBlanke Apr 12, 2025
83f5431
rename + delete files
SimonBlanke Apr 12, 2025
b376b28
add abstract class and method
SimonBlanke Apr 13, 2025
99d98f4
restructure to enable adaption to different optimization backends
SimonBlanke Apr 13, 2025
18397d0
move files into optimization path
SimonBlanke Apr 27, 2025
6433801
adapt paths to new structure
SimonBlanke Apr 27, 2025
3b565ee
continue restructuring...
SimonBlanke Apr 27, 2025
be8c97f
continue restructuring...
SimonBlanke Apr 27, 2025
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
39 changes: 39 additions & 0 deletions examples/v5_API_example/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import numpy as np
from sklearn.datasets import load_diabetes
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import cross_val_score

from hyperactive.search_config import SearchConfig
from hyperactive.optimizers import HillClimbingOptimizer

from hyperactive.base import BaseExperiment


class SklearnExperiment(BaseExperiment):
def setup(self, estimator, X, y, cv=5):
self.estimator = estimator
self.X = X
self.y = y
self.cv = cv

def _score(self, params):
model = self.estimator(**params)
scores = cross_val_score(model, self.X, self.y, cv=self.cv)
return scores.mean()


data = load_diabetes()
X, y = data.data, data.target


search_config = SearchConfig(
max_depth=list(np.arange(2, 15, 1)),
min_samples_split=list(np.arange(2, 25, 2)),
)

experiment = SklearnExperiment()
experiment.setup(DecisionTreeRegressor, X, y, cv=4)

optimizer1 = HillClimbingOptimizer()
optimizer1.add_search(experiment, search_config, n_iter=100, n_jobs=2)
optimizer1.run()
68 changes: 68 additions & 0 deletions examples/v5_API_example/experiments/keras.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from tensorflow import keras
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

from hyperactive.base import BaseExperiment


X, y = make_classification(n_samples=1000, n_features=20, random_state=42)
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2)


class KerasMultiLayerPerceptron(BaseExperiment):
"""
A class for creating and evaluating a Keras-based Multi-Layer Perceptron (MLP) model.

This class inherits from BaseExperiment and is designed to build a simple MLP
using Keras, compile it with the Adam optimizer, and train it on the provided
training data. The model consists of one hidden dense layer with configurable
size and activation function, followed by an output layer with a sigmoid
activation for binary classification.

Attributes:
X_train (array-like): Training feature data.
X_val (array-like): Validation feature data.
y_train (array-like): Training target data.
y_val (array-like): Validation target data.

Methods:
_score(**params): Builds, compiles, and trains the MLP model using the
specified parameters for the hidden layer, and returns the validation
accuracy.
"""

def __init__(self, X_train, X_val, y_train, y_val):
super().__init__()

self.X_train = X_train
self.X_val = X_val
self.y_train = y_train
self.y_val = y_val

def _score(self, **params):
dense_layer_0 = params["dense_layer_0"]
activation_layer_0 = params["activation_layer_0"]

model = keras.Sequential(
[
keras.layers.Dense(
dense_layer_0,
activation=activation_layer_0,
input_shape=(20,),
),
keras.layers.Dense(1, activation="sigmoid"),
]
)
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.01),
loss="binary_crossentropy",
metrics=["accuracy"],
)
model.fit(
self.X_train,
self.y_train,
batch_size=32,
epochs=10,
validation_data=(self.X_val, self.y_val),
)
return model.evaluate(X_val, y_val)[1]
67 changes: 67 additions & 0 deletions examples/v5_API_example/experiments/sklearn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import GradientBoostingRegressor


from hyperactive.base import BaseExperiment


class SklearnExperiment(BaseExperiment):
"""
Initializes the SklearnExperiment with the given estimator, data, and cross-validation settings.

Parameters
----------
estimator : object
The machine learning estimator to be used for the experiment.
X : array-like
The input data for training the model.
y : array-like
The target values corresponding to the input data.
cv : int, optional
The number of cross-validation folds (default is 4).
"""

def __init__(self, estimator, X, y, cv=4):
super().__init__()

self.estimator = estimator
self.X = X
self.y = y
self.cv = cv

def _score(self, **params):
model = self.estimator(**params)
scores = cross_val_score(model, self.X, self.y, cv=self.cv)
return scores.mean()


class GradientBoostingExperiment(BaseExperiment):
"""
A class for conducting experiments with Gradient Boosting Regressor using cross-validation.

This class inherits from BaseExperiment and allows users to perform experiments
with the GradientBoostingRegressor from sklearn. Users can specify the input
features, target values, and the number of cross-validation folds.

Attributes:
estimator (type): The regression model to be used, default is GradientBoostingRegressor.
X (array-like): The input features for the model.
y (array-like): The target values for the model.
cv (int): The number of cross-validation folds.

Methods:
_score(**params): Evaluates the model using cross-validation and returns the mean score.
"""

def __init__(self, X, y, cv=4):
super().__init__()

self.estimator = GradientBoostingRegressor # The user could also predefine the estimator
self.X = X
self.y = y
self.cv = cv

def _score(self, **params):
model = self.estimator(**params)
scores = cross_val_score(model, self.X, self.y, cv=self.cv)
return scores.mean()
60 changes: 60 additions & 0 deletions examples/v5_API_example/experiments/test_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import numpy as np

from hyperactive.base import BaseExperiment


class SphereFunction(BaseExperiment):
"""
A class representing a Sphere function experiment.

This class inherits from BaseExperiment and implements a simple
Sphere function, which is a common test function for optimization
algorithms. The function is defined as the sum of the squares of
its input parameters plus a constant.

Attributes:
const (float): A constant added to the function's result.
n_dim (int): The number of dimensions for the input parameters.

Methods:
_score(**params): Computes the Sphere function value for the
given parameters.
"""

def __init__(self, const, n_dim=2):
super().__init__()

self.const = const
self.n_dim = n_dim

def _score(self, **params):
return np.sum(np.array(params) ** 2) + self.const


class AckleyFunction(BaseExperiment):
"""
A class representing the Ackley function, used as a benchmark for optimization algorithms.

Attributes:
A (float): A constant used in the calculation of the Ackley function.

Methods:
_score(**params): Computes the Ackley function value for given parameters 'x0' and 'x1'.

The Ackley function is a non-convex function used to test optimization algorithms.
"""

def __init__(self, A):
super().__init__()
self.A = A

def _score(self, **params):
x = params["x0"]
y = params["x1"]

loss1 = -self.A * np.exp(-0.2 * np.sqrt(0.5 * (x * x + y * y)))
loss2 = -np.exp(0.5 * (np.cos(2 * np.pi * x) + np.cos(2 * np.pi * y)))
loss3 = np.exp(1)
loss4 = self.A

return -(loss1 + loss2 + loss3 + loss4)
42 changes: 42 additions & 0 deletions examples/v5_API_example/optimizer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import numpy as np
from sklearn.datasets import load_diabetes
from sklearn.tree import DecisionTreeRegressor


from hyperactive.base.search_space_optional import SearchSpace
from hyperactive.optimizers import (
HillClimbingOptimizer,
RandomRestartHillClimbingOptimizer,
)

from .experiments.test_function import SklearnExperiment


data = load_diabetes()
X, y = data.data, data.target


search_space = {
"max_depth": list(np.arange(2, 15, 1)),
"min_samples_split": list(np.arange(2, 25, 2)),
}

""" optional way of defining search-space
search_space = SearchSpace(
max_depth=list(np.arange(2, 15, 1)),
min_samples_split=list(np.arange(2, 25, 2)),
)
"""

experiment = SklearnExperiment(DecisionTreeRegressor, X, y, cv=4)

optimizer1 = HillClimbingOptimizer(n_iter=50)
optimizer2 = RandomRestartHillClimbingOptimizer(n_iter=50, n_jobs=2)

optimizer1.add_search(experiment, search_space)
optimizer2.add_search(experiment, search_space)

# not sure about this way of combining optimizers. Might not be intuitive what the plus means.
hyper = optimizer1 + optimizer2

hyper.run(max_time=5)
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ dependencies = [
"numpy >=1.18.1, <3.0.0",
"tqdm >=4.48.0, <5.0.0",
"pandas <3.0.0",
"gradient-free-optimizers >=1.2.4, <2.0.0",
"gradient-free-optimizers >=1.5.0, <2.0.0",
"scikit-base <1.0.0",
]

[project.optional-dependencies]
Expand Down
8 changes: 0 additions & 8 deletions src/hyperactive/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,3 @@

__version__ = importlib.metadata.version("hyperactive")
__license__ = "MIT"


from .hyperactive import Hyperactive


__all__ = [
"Hyperactive",
]
6 changes: 6 additions & 0 deletions src/hyperactive/experiment/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Base classes for optimizers and experiments."""

from ._experiment import BaseExperiment
from ._utility import add_callback, add_catch

__all__ = ["BaseExperiment", "add_callback", "add_catch"]
Loading