23  Step 2: Initialization of the Empty fun_control Dictionary

MAX_TIME = 1
INIT_SIZE = 20
PREFIX = "18"

spotpython supports the visualization of the hyperparameter tuning process with TensorBoard. The following example shows how to use TensorBoard with spotpython. The fun_control dictionary is the central data structure that is used to control the optimization process. It is initialized as follows:

from spotpython.utils.init import fun_control_init
from spotpython.hyperparameters.values import set_control_key_value
from spotpython.utils.eda import print_res_table
fun_control = fun_control_init(
    PREFIX=PREFIX,
    TENSORBOARD_CLEAN=True,
    max_time=MAX_TIME,
    fun_evals=inf,
    tolerance_x = np.sqrt(np.spacing(1)))
Moving TENSORBOARD_PATH: runs/ to TENSORBOARD_PATH_OLD: runs_OLD/runs_2025_02_17_22_39_22_0
Tip: TensorBoard
  • Since the spot_tensorboard_path argument is not None, which is the default, spotpython will log the optimization process in the TensorBoard folder.
  • The TENSORBOARD_CLEAN argument is set to True to archive the TensorBoard folder if it already exists. This is useful if you want to start a hyperparameter tuning process from scratch. If you want to continue a hyperparameter tuning process, set TENSORBOARD_CLEAN to False. Then the TensorBoard folder will not be archived and the old and new TensorBoard files will shown in the TensorBoard dashboard.

23.1 Step 3: SKlearn Load Data (Classification)

Randomly generate classification data. Here, we use similar data as in Comparison of kernel ridge regression and SVR.

import numpy as np

rng = np.random.RandomState(42)

X = 5 * rng.rand(10, 1)
y = np.sin(1/X).ravel()*np.cos(X).ravel()

# Add noise to targets
y[::5] += 3 * (0.5 - rng.rand(X.shape[0] // 5))

X_plot = np.linspace(0, 5, 100000)[:, None]
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split

n_features = 1
target_column = "y"
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42
)
train = pd.DataFrame(np.hstack((X_train, y_train.reshape(-1, 1))))
test = pd.DataFrame(np.hstack((X_test, y_test.reshape(-1, 1))))
train.columns = [f"x{i}" for i in range(1, n_features+1)] + [target_column]
test.columns = [f"x{i}" for i in range(1, n_features+1)] + [target_column]
train.head()
x1 y
0 1.872701 1.286910
1 4.330881 -0.085207
2 3.659970 -0.234389
3 3.540363 -0.256848
4 0.780093 0.681389
n_samples = len(train)
# add the dataset to the fun_control
fun_control.update({"data": None, # dataset,
               "train": train,
               "test": test,
               "n_samples": n_samples,
               "target_column": target_column})

23.2 Step 4: Specification of the Preprocessing Model

Data preprocesssing can be very simple, e.g., you can ignore it. Then you would choose the prep_model “None”:

prep_model = None
fun_control.update({"prep_model": prep_model})

A default approach for numerical data is the StandardScaler (mean 0, variance 1). This can be selected as follows:

from sklearn.preprocessing import StandardScaler
prep_model = StandardScaler
fun_control.update({"prep_model": prep_model})

Even more complicated pre-processing steps are possible, e.g., the follwing pipeline:

categorical_columns = []
one_hot_encoder = OneHotEncoder(handle_unknown="ignore", sparse_output=False)
prep_model = ColumnTransformer(
         transformers=[
             ("categorical", one_hot_encoder, categorical_columns),
         ],
         remainder=StandardScaler,
     )

23.3 Step 5: Select Model (algorithm) and core_model_hyper_dict

The selection of the algorithm (ML model) that should be tuned is done by specifying the its name from the sklearn implementation. For example, the SVC support vector machine classifier is selected as follows:

from spotpython.hyperparameters.values import add_core_model_to_fun_control
from spotpython.hyperdict.sklearn_hyper_dict import SklearnHyperDict
from sklearn.svm import SVR
add_core_model_to_fun_control(core_model=SVR,
                              fun_control=fun_control,
                              hyper_dict=SklearnHyperDict,
                              filename=None)

Now fun_control has the information from the JSON file. The corresponding entries for the core_model class are shown below.

fun_control['core_model_hyper_dict']
{'C': {'type': 'float',
  'default': 1.0,
  'transform': 'None',
  'lower': 0.1,
  'upper': 10.0},
 'kernel': {'levels': ['linear', 'poly', 'rbf', 'sigmoid'],
  'type': 'factor',
  'default': 'rbf',
  'transform': 'None',
  'core_model_parameter_type': 'str',
  'lower': 0,
  'upper': 3},
 'degree': {'type': 'int',
  'default': 3,
  'transform': 'None',
  'lower': 3,
  'upper': 3},
 'gamma': {'levels': ['scale', 'auto'],
  'type': 'factor',
  'default': 'scale',
  'transform': 'None',
  'core_model_parameter_type': 'str',
  'lower': 0,
  'upper': 1},
 'coef0': {'type': 'float',
  'default': 0.0,
  'transform': 'None',
  'lower': 0.0,
  'upper': 0.0},
 'epsilon': {'type': 'float',
  'default': 0.1,
  'transform': 'None',
  'lower': 0.01,
  'upper': 1.0},
 'shrinking': {'levels': [0, 1],
  'type': 'factor',
  'default': 0,
  'transform': 'None',
  'core_model_parameter_type': 'bool',
  'lower': 0,
  'upper': 1},
 'tol': {'type': 'float',
  'default': 0.001,
  'transform': 'None',
  'lower': 0.0001,
  'upper': 0.01},
 'cache_size': {'type': 'float',
  'default': 200,
  'transform': 'None',
  'lower': 100,
  'upper': 400}}
sklearn Model Selection

The following sklearn models are supported by default:

  • RidgeCV
  • RandomForestClassifier
  • SVC
  • SVR
  • LogisticRegression
  • KNeighborsClassifier
  • GradientBoostingClassifier
  • GradientBoostingRegressor
  • ElasticNet

They can be imported as follows:

from sklearn.linear_model import RidgeCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.svm import SVR
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.linear_model import ElasticNet

23.4 Step 6: Modify hyper_dict Hyperparameters for the Selected Algorithm aka core_model

spotpython provides functions for modifying the hyperparameters, their bounds and factors as well as for activating and de-activating hyperparameters without re-compilation of the Python source code. These functions were described in Section D.15.1.

23.4.1 Modify hyperparameter of type numeric and integer (boolean)

Numeric and boolean values can be modified using the modify_hyper_parameter_bounds method.

sklearn Model Hyperparameters

The hyperparameters of the sklearn SVC model are described in the sklearn documentation.

  • For example, to change the tol hyperparameter of the SVC model to the interval [1e-5, 1e-3], the following code can be used:
from spotpython.hyperparameters.values import modify_hyper_parameter_bounds
modify_hyper_parameter_bounds(fun_control, "tol", bounds=[1e-5, 1e-3])
modify_hyper_parameter_bounds(fun_control, "epsilon", bounds=[0.1, 1.0])
# modify_hyper_parameter_bounds(fun_control, "degree", bounds=[2, 5])
fun_control["core_model_hyper_dict"]["tol"]
{'type': 'float',
 'default': 0.001,
 'transform': 'None',
 'lower': 1e-05,
 'upper': 0.001}

23.4.2 Modify hyperparameter of type factor

Factors can be modified with the modify_hyper_parameter_levels function. For example, to exclude the sigmoid kernel from the tuning, the kernel hyperparameter of the SVR model can be modified as follows:

from spotpython.hyperparameters.values import modify_hyper_parameter_levels
# modify_hyper_parameter_levels(fun_control, "kernel", ["poly", "rbf"])
modify_hyper_parameter_levels(fun_control, "kernel", ["rbf"])
fun_control["core_model_hyper_dict"]["kernel"]
{'levels': ['rbf'],
 'type': 'factor',
 'default': 'rbf',
 'transform': 'None',
 'core_model_parameter_type': 'str',
 'lower': 0,
 'upper': 0}

23.4.3 Optimizers

Optimizers are described in Section 4.2.

23.5 Step 7: Selection of the Objective (Loss) Function

There are two metrics:

  1. metric_river is used for the river based evaluation via eval_oml_iter_progressive.
  2. metric_sklearn is used for the sklearn based evaluation.
from sklearn.metrics import mean_absolute_error, accuracy_score, roc_curve, roc_auc_score, log_loss, mean_squared_error
fun_control.update({
               "metric_sklearn": mean_squared_error,
               "weights": 1.0,
               })
metric_sklearn: Minimization and Maximization
  • Because the metric_sklearn is used for the sklearn based evaluation, it is important to know whether the metric should be minimized or maximized.
  • The weights parameter is used to indicate whether the metric should be minimized or maximized.
  • If weights is set to -1.0, the metric is maximized.
  • If weights is set to 1.0, the metric is minimized, e.g., weights = 1.0 for mean_absolute_error, or weights = -1.0 for roc_auc_score.

23.5.1 Predict Classes or Class Probabilities

If the key "predict_proba" is set to True, the class probabilities are predicted. False is the default, i.e., the classes are predicted.

fun_control.update({
               "predict_proba": False,
               })

23.6 Step 8: Calling the SPOT Function

23.6.1 The Objective Function

The objective function is selected next. It implements an interface from sklearn’s training, validation, and testing methods to spotpython.

from spotpython.fun.hypersklearn import HyperSklearn
fun = HyperSklearn().fun_sklearn

The following code snippet shows how to get the default hyperparameters as an array, so that they can be passed to the Spot function.

from spotpython.hyperparameters.values import get_default_hyperparameters_as_array
X_start = get_default_hyperparameters_as_array(fun_control)

23.6.2 Run the Spot Optimizer

The class Spot [SOURCE] is the hyperparameter tuning workhorse. It is initialized with the following parameters:

  • fun: the objective function
  • fun_control: the dictionary with the control parameters for the objective function
  • design: the experimental design
  • design_control: the dictionary with the control parameters for the experimental design
  • surrogate: the surrogate model
  • surrogate_control: the dictionary with the control parameters for the surrogate model
  • optimizer: the optimizer
  • optimizer_control: the dictionary with the control parameters for the optimizer
Note: Total run time

The total run time may exceed the specified max_time, because the initial design (here: init_size = INIT_SIZE as specified above) is always evaluated, even if this takes longer than max_time.

from spotpython.utils.init import design_control_init, surrogate_control_init
design_control = design_control_init()
set_control_key_value(control_dict=design_control,
                        key="init_size",
                        value=INIT_SIZE,
                        replace=True)

surrogate_control = surrogate_control_init(noise=True,
                                           n_theta=2)
from spotpython.spot import Spot
spot_tuner = Spot(fun=fun,
                   fun_control=fun_control,
                   design_control=design_control,
                   surrogate_control=surrogate_control)
spot_tuner.run(X_start=X_start)
spotpython tuning: 0.11831336203046443 [----------] 0.62% 
spotpython tuning: 0.11831336203046443 [----------] 1.32% 
spotpython tuning: 0.11831336203046443 [----------] 1.99% 
spotpython tuning: 0.11831336203046443 [----------] 2.81% 
spotpython tuning: 0.11467654823166384 [----------] 3.67% 
spotpython tuning: 0.11467654823166384 [----------] 4.62% 
spotpython tuning: 0.1052243056750549 [#---------] 5.87% 
spotpython tuning: 0.10405556663192285 [#---------] 7.25% 
spotpython tuning: 0.10405556663192285 [#---------] 8.88% 
spotpython tuning: 0.08698965578693191 [#---------] 10.41% 
spotpython tuning: 0.08423427702103133 [#---------] 12.26% 
spotpython tuning: 0.08423427702103133 [#---------] 13.67% 
spotpython tuning: 0.08423427702103133 [##--------] 15.09% 
spotpython tuning: 0.08423427702103133 [##--------] 16.62% 
spotpython tuning: 0.08423427702103133 [##--------] 18.23% 
spotpython tuning: 0.08423427702103133 [##--------] 19.75% 
spotpython tuning: 0.08423427702103133 [##--------] 21.27% 
spotpython tuning: 0.08423427702103133 [##--------] 22.90% 
spotpython tuning: 0.08423427702103133 [##--------] 24.44% 
spotpython tuning: 0.08423427702103133 [###-------] 25.94% 
spotpython tuning: 0.08423427702103133 [###-------] 27.61% 
spotpython tuning: 0.08423427702103133 [###-------] 29.20% 
spotpython tuning: 0.08423427702103133 [###-------] 30.52% 
spotpython tuning: 0.08423427702103133 [###-------] 32.04% 
spotpython tuning: 0.08423427702103133 [###-------] 33.58% 
spotpython tuning: 0.08423427702103133 [####------] 35.09% 
spotpython tuning: 0.08423427702103133 [####------] 36.57% 
spotpython tuning: 0.08423427702103133 [####------] 38.19% 
spotpython tuning: 0.08423427702103133 [####------] 40.01% 
spotpython tuning: 0.08423427702103133 [####------] 41.88% 
spotpython tuning: 0.08423427702103133 [####------] 43.59% 
spotpython tuning: 0.08423427702103133 [#####-----] 45.04% 
spotpython tuning: 0.08423427702103133 [#####-----] 46.60% 
spotpython tuning: 0.08423427702103133 [#####-----] 48.41% 
spotpython tuning: 0.08423427702103133 [#####-----] 50.00% 
spotpython tuning: 0.08423427702103133 [#####-----] 51.73% 
spotpython tuning: 0.08423427702103133 [#####-----] 53.39% 
spotpython tuning: 0.08423427702103133 [######----] 55.02% 
spotpython tuning: 0.08423427702103133 [######----] 56.87% 
spotpython tuning: 0.08423427702103133 [######----] 58.50% 
spotpython tuning: 0.08423427702103133 [######----] 60.24% 
spotpython tuning: 0.08423427702103133 [######----] 61.81% 
spotpython tuning: 0.08423427702103133 [######----] 63.55% 
spotpython tuning: 0.08423427702103133 [######----] 64.99% 
spotpython tuning: 0.08423427702103133 [#######---] 66.74% 
spotpython tuning: 0.08423427702103133 [#######---] 68.43% 
spotpython tuning: 0.08423427702103133 [#######---] 69.98% 
spotpython tuning: 0.08423427702103133 [#######---] 71.72% 
spotpython tuning: 0.08423427702103133 [#######---] 73.55% 
spotpython tuning: 0.08423427702103133 [########--] 75.26% 
spotpython tuning: 0.08423427702103133 [########--] 76.98% 
spotpython tuning: 0.08423427702103133 [########--] 78.94% 
spotpython tuning: 0.08423427702103133 [########--] 80.79% 
spotpython tuning: 0.08423427702103133 [########--] 82.47% 
spotpython tuning: 0.08423427702103133 [########--] 84.11% 
spotpython tuning: 0.08423427702103133 [#########-] 85.82% 
spotpython tuning: 0.08423427702103133 [#########-] 87.52% 
spotpython tuning: 0.08423427702103133 [#########-] 89.29% 
spotpython tuning: 0.08423427702103133 [#########-] 91.07% 
spotpython tuning: 0.08423427702103133 [#########-] 92.99% 
spotpython tuning: 0.08423427702103133 [#########-] 94.75% 
spotpython tuning: 0.08423427702103133 [##########] 96.28% 
spotpython tuning: 0.08423427702103133 [##########] 97.52% 
spotpython tuning: 0.08423427702103133 [##########] 98.85% 
spotpython tuning: 0.08423427702103133 [##########] 100.00% Done...

Experiment saved to 18_res.pkl
<spotpython.spot.spot.Spot at 0x13173b7a0>

23.6.3 TensorBoard

Now we can start TensorBoard in the background with the following command, where ./runs is the default directory for the TensorBoard log files:

tensorboard --logdir="./runs"
from spotpython.utils.init import get_tensorboard_path
get_tensorboard_path(fun_control)
'runs/'

After the hyperparameter tuning run is finished, the progress of the hyperparameter tuning can be visualized. The black points represent the performace values (score or metric) of hyperparameter configurations from the initial design, whereas the red points represents the hyperparameter configurations found by the surrogate model based optimization.

spot_tuner.plot_progress(log_y=True)

Results can also be printed in tabular form.

print_res_table(spot_tuner)
| name       | type   | default   |   lower |   upper | tuned              | transform   |   importance | stars   |
|------------|--------|-----------|---------|---------|--------------------|-------------|--------------|---------|
| C          | float  | 1.0       |     0.1 |    10.0 | 5.0858474654131784 | None        |         0.82 | .       |
| kernel     | factor | rbf       |     0.0 |     0.0 | rbf                | None        |         0.00 |         |
| degree     | int    | 3         |     3.0 |     3.0 | 3.0                | None        |         0.00 |         |
| gamma      | factor | scale     |     0.0 |     1.0 | scale              | None        |         0.03 |         |
| coef0      | float  | 0.0       |     0.0 |     0.0 | 0.0                | None        |         0.00 |         |
| epsilon    | float  | 0.1       |     0.1 |     1.0 | 0.1                | None        |       100.00 | ***     |
| shrinking  | factor | 0         |     0.0 |     1.0 | 1                  | None        |         0.12 | .       |
| tol        | float  | 0.001     |   1e-05 |   0.001 | 0.001              | None        |         0.51 | .       |
| cache_size | float  | 200.0     |   100.0 |   400.0 | 331.7133108917936  | None        |         0.01 |         |

A histogram can be used to visualize the most important hyperparameters.

spot_tuner.plot_importance(threshold=0.0025)

23.7 Get Default Hyperparameters

The default hyperparameters, which will be used for a comparion with the tuned hyperparameters, can be obtained with the following commands:

from spotpython.hyperparameters.values import get_one_core_model_from_X
from spotpython.hyperparameters.values import get_default_hyperparameters_as_array
X_start = get_default_hyperparameters_as_array(fun_control)
model_default = get_one_core_model_from_X(X_start, fun_control, default=True)
model_default
SVR(cache_size=200.0, shrinking=False)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.

23.8 Get SPOT Results

In a similar way, we can obtain the hyperparameters found by spotpython.

from spotpython.hyperparameters.values import get_one_core_model_from_X
X_tuned = spot_tuner.to_all_dim(spot_tuner.min_X.reshape(1,-1))
model_spot = get_one_core_model_from_X(X_tuned, fun_control)

23.8.1 Plot: Compare Predictions

model_default.fit(X_train, y_train)
y_default = model_default.predict(X_plot)
model_spot.fit(X_train, y_train)
y_spot = model_spot.predict(X_plot)
import matplotlib.pyplot as plt
plt.scatter(X[:100], y[:100], c="orange", label="data", zorder=1, edgecolors=(0, 0, 0))
plt.plot(
    X_plot,
    y_default,
    c="red",
    label="Default SVR")

plt.plot(
    X_plot, y_spot, c="blue", label="SPOT SVR")

plt.xlabel("data")
plt.ylabel("target")
plt.title("SVR")
_ = plt.legend()

23.8.2 Detailed Hyperparameter Plots

spot_tuner.plot_important_hyperparameter_contour(filename=None)
C:  0.820214955779512
gamma:  0.02636130276329082
epsilon:  100.0
shrinking:  0.12265925828497815
tol:  0.510877246876061
cache_size:  0.01071942373384086

23.8.3 Parallel Coordinates Plot

spot_tuner.parallel_plot()