Skip to content

Commit 10b5c5d

Browse files
mohyperlight
1 parent abe234d commit 10b5c5d

2 files changed

Lines changed: 178 additions & 1 deletion

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
77

88
[project]
99
name = "spotpython"
10-
version = "0.28.6"
10+
version = "0.28.7"
1111
authors = [
1212
{ name="T. Bartz-Beielstein", email="tbb@bartzundbartz.de" }
1313
]

src/spotpython/fun/mohyperlight.py

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import logging
2+
import numpy as np
3+
import pprint
4+
from numpy.random import default_rng
5+
from spotpython.light.trainmodel import train_model
6+
from spotpython.hyperparameters.values import assign_values, generate_one_config_from_var_dict, get_var_name
7+
8+
logger = logging.getLogger(__name__)
9+
py_handler = logging.FileHandler(f"{__name__}.log", mode="w")
10+
py_formatter = logging.Formatter("%(name)s %(asctime)s %(levelname)s %(message)s")
11+
py_handler.setFormatter(py_formatter)
12+
logger.addHandler(py_handler)
13+
14+
15+
class MoHyperLight:
16+
"""
17+
Multi-Objective Hyperparameter Tuning for Lightning.
18+
19+
Args:
20+
seed (int): seed for the random number generator. See Numpy Random Sampling.
21+
log_level (int): log level for the logger.
22+
23+
Attributes:
24+
seed (int): seed for the random number generator.
25+
rng (Generator): random number generator.
26+
fun_control (dict): dictionary containing control parameters for the hyperparameter tuning.
27+
log_level (int): log level for the logger.
28+
29+
Examples:
30+
>>> hyper_light = MoHyperLight(seed=126, log_level=50)
31+
>>> print(hyper_light.seed)
32+
126
33+
"""
34+
35+
def __init__(self, seed: int = 126, log_level: int = 50) -> None:
36+
self.seed = seed
37+
self.rng = default_rng(seed=self.seed)
38+
self.log_level = log_level
39+
logger.setLevel(log_level)
40+
logger.info(f"Starting the logger at level {log_level} for module {__name__}:")
41+
42+
def check_X_shape(self, X: np.ndarray, fun_control: dict) -> np.ndarray:
43+
"""
44+
Checks the shape of the input array X and raises an exception if it is not valid.
45+
46+
Args:
47+
X (np.ndarray):
48+
input array.
49+
fun_control (dict):
50+
dictionary containing control parameters for the hyperparameter tuning.
51+
52+
Returns:
53+
np.ndarray:
54+
input array with valid shape.
55+
56+
Raises:
57+
Exception:
58+
if the shape of the input array is not valid.
59+
60+
Examples:
61+
>>> import numpy as np
62+
from spotpython.utils.init import fun_control_init
63+
from spotpython.light.regression.netlightregression import NetLightRegression
64+
from spotpython.hyperdict.light_hyper_dict import LightHyperDict
65+
from spotpython.hyperparameters.values import add_core_model_to_fun_control
66+
from spotpython.fun.mohyperlight import MoHyperLight
67+
from spotpython.hyperparameters.values import get_var_name
68+
fun_control = fun_control_init()
69+
add_core_model_to_fun_control(core_model=NetLightRegression,
70+
fun_control=fun_control,
71+
hyper_dict=LightHyperDict)
72+
hyper_light = MoHyperLight(seed=126, log_level=50)
73+
n_hyperparams = len(get_var_name(fun_control))
74+
# generate a random np.array X with shape (2, n_hyperparams)
75+
X = np.random.rand(2, n_hyperparams)
76+
X == hyper_light.check_X_shape(X, fun_control)
77+
array([[ True, True, True, True, True, True, True, True, True],
78+
[ True, True, True, True, True, True, True, True, True]])
79+
80+
"""
81+
try:
82+
X.shape[1]
83+
except ValueError:
84+
X = np.array([X])
85+
if X.shape[1] != len(get_var_name(fun_control)):
86+
raise Exception("Invalid shape of input array X.")
87+
return X
88+
89+
def fun(self, X: np.ndarray, fun_control: dict = None) -> np.ndarray:
90+
"""
91+
Evaluates the function for the given input array X of shape (n,k)
92+
and control parameters specified as a dict.
93+
Calls the train_model function from spotpython.light.trainmodel
94+
to train the model and evaluate the results.
95+
96+
Args:
97+
X (np.ndarray):
98+
input array of shape (n, k) where n is the number of configurations evaluated
99+
and k is the number of hyperparameters.
100+
fun_control (dict):
101+
dictionary containing control parameters for the hyperparameter tuning.
102+
103+
Returns:
104+
(np.ndarray):
105+
(n,) array containing the `n` evaluation results.
106+
107+
Examples:
108+
>>> from math import inf
109+
import numpy as np
110+
from spotpython.data.diabetes import Diabetes
111+
from spotpython.hyperdict.light_hyper_dict import LightHyperDict
112+
from spotpython.fun.mohyperlight import MoHyperLight
113+
from spotpython.utils.init import fun_control_init
114+
from spotpython.utils.eda import print_exp_table
115+
from spotpython.spot import spot
116+
from spotpython.hyperparameters.values import get_default_hyperparameters_as_array
117+
PREFIX="000"
118+
data_set = Diabetes()
119+
fun_control = fun_control_init(
120+
PREFIX=PREFIX,
121+
save_experiment=True,
122+
fun_evals=inf,
123+
max_time=1,
124+
data_set = data_set,
125+
core_model_name="light.regression.NNLinearRegressor",
126+
hyperdict=LightHyperDict,
127+
_L_in=10,
128+
_L_out=1,
129+
TENSORBOARD_CLEAN=True,
130+
tensorboard_log=True,
131+
seed=42,)
132+
print_exp_table(fun_control)
133+
X = get_default_hyperparameters_as_array(fun_control)
134+
# set epochs to 2^8:
135+
X[0, 1] = 8
136+
# set patience to 2^10:
137+
X[0, 7] = 10
138+
print(f"X: {X}")
139+
# combine X and X to a np.array with shape (2, n_hyperparams)
140+
# so that two values are returned
141+
X = np.vstack((X, X))
142+
hyper_light = MoHyperLight(seed=125, log_level=50)
143+
hyper_light.fun(X, fun_control)
144+
"""
145+
z_res = np.array([], dtype=float)
146+
self.check_X_shape(X=X, fun_control=fun_control)
147+
var_dict = assign_values(X, get_var_name(fun_control))
148+
# type information and transformations are considered in generate_one_config_from_var_dict:
149+
for config in generate_one_config_from_var_dict(var_dict, fun_control):
150+
if fun_control["show_config"]:
151+
print("\nIn fun(): config:")
152+
pprint.pprint(config)
153+
logger.debug(f"\nconfig: {config}")
154+
# extract parameters like epochs, batch_size, lr, etc. from config
155+
# config_id = generate_config_id(config)
156+
try:
157+
logger.debug("fun: Calling train_model")
158+
df_eval = train_model(config, fun_control)
159+
logger.debug("fun: train_model returned")
160+
except Exception as err:
161+
if fun_control["verbosity"] > 0:
162+
print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
163+
if fun_control["verbosity"] > 1:
164+
pprint.pprint(fun_control)
165+
print(f"Error in fun(). Call to train_model failed. {err=}, {type(err)=}")
166+
print("Setting df_eval to np.nan\n")
167+
logger.error(f"Error in fun(). Call to train_model failed. {err=}, {type(err)=}")
168+
logger.error("Setting df_eval to np.nan")
169+
df_eval = np.nan
170+
# Multiply results by the weights. Positive weights mean that the result is to be minimized.
171+
# Negative weights mean that the result is to be maximized, e.g., accuracy.
172+
z_val = fun_control["weights"] * df_eval
173+
# Append, since several configurations can be evaluated at once.
174+
z_res = np.append(z_res, z_val)
175+
# Finally, z_res is a 1-dim array
176+
# of shape (n,) where n is the number of configurations evaluated.
177+
return z_res

0 commit comments

Comments
 (0)