# 1. DeepHyper 101# In this tutorial, we present the basics of DeepHyper.

[ ]:

!pip install deephyper


## 1.1. Optimization Problem#

In the definition of our optimization problem we have two components:

1. black-box function that we want to optimize

2. the search space of input variables

### 1.1.1. Black-Box Function#

DeepHyper is developed to optimize black-box functions. Here, we define the function $$f(x) = - x ^ 2$$ that we want to maximise (the maximum being $$f(x=0) = 0$$ on $$I_x = [-10;10]$$). The black-box function f takes as input a config dictionnary from which we retrieve the variables of interest.

:

def f(config):
return - config["x"]**2


### 1.1.2. Search Space of Input Variables#

In this example, we have only one variable $$x$$ for the black-box functin $$f$$. We empirically decide to optimize this variable $$x$$ on the interval $$I_x = [-10;10]$$. To do so we use the HpProblem from DeepHyper and add a real hyperparameter by using a tuple of two floats.

:

from deephyper.problem import HpProblem

problem = HpProblem()

# define the variable you want to optimize

problem

:

Configuration space object:
Hyperparameters:
x, Type: UniformFloat, Range: [-10.0, 10.0], Default: 0.0


## 1.2. Evaluator Interface#

DeepHyper uses an API called Evaluator to distribute the computation of black-box functions and adapt to different backends (e.g., threads, processes, MPI, Ray). An Evaluator object wraps the black-box function f that we want to optimize. Then a method parameter is used to select the backend and method_kwargs defines some available options of this backend.

Tip

The method="thread" provides parallel computation only if the black-box is releasing the global interpretor lock (GIL). Therefore, if you want parallelism in Jupyter notebooks you should use the Ray evaluator (method="ray") after installing Ray with pip install ray.

It is possible to define callbacks to extend the behaviour of Evaluator each time a function-evaluation is launched or completed. In this example we use the TqdmCallback to follow the completed evaluations and the evolution of the objective with a progress-bar.

:

from deephyper.evaluator import Evaluator
from deephyper.evaluator.callback import TqdmCallback

# define the evaluator to distribute the computation
evaluator = Evaluator.create(
f,
method_kwargs={
"num_workers": 4,
"callbacks": [TqdmCallback()]
},
)

print(f"Evaluator has {evaluator.num_workers} available worker{'' if evaluator.num_workers == 1 else 's'}")

Evaluator has 4 available workers

/Users/romainegele/Documents/Argonne/deephyper/deephyper/evaluator/_evaluator.py:91: UserWarning: Applying nest-asyncio patch for IPython Shell!
warnings.warn(


## 1.3. Search Algorithm#

The next step is to define the search algorithm that we want to use. Here, we choose CBO (Centralized Bayesian Optimization) which is a sampling based Bayesian optimization strategy. This algorithm has the advantage of being asynchronous thanks to a constant liar strategy which is crutial to keep a good utilization of the resources when the number of available workers increases.

:

from deephyper.search.hps import CBO

search = CBO(problem, evaluator)


Then, we can execute the search for a given number of iterations by using the search.search(max_evals=...). It is also possible to use the timeout parameter if one needs a specific time budget (e.g., restricted computational time in machine learning competitions, allocation time in HPC).

:

results = search.search(max_evals=100)

100%|██████████| 100/100 [00:16<00:00,  5.15it/s, objective=-5.33e-5]


Finally, let us visualize the results. The search(...) returns a DataFrame also saved locally under results.csv (in case of crash we don’t want to loose the possibly expensive evaluations already performed).

The DataFrame contains as columns: 1. the optimized hyperparameters: such as x in our case. 2. the id of each evaluated function (increased incrementally following the order of created evaluations). 3. the objective maximised which directly match the results of the $$f$$-function in our example. 4. the time of termination of each task elapsed_sec (in secondes, since the creation of the Evaluator) and the duration (in secondes).

:

results

:

x job_id objective timestamp_submit timestamp_gather
0 -6.511903 4 -42.404881 4.898395 4.899235
1 2.908865 2 -8.461494 4.898384 4.907161
2 -4.990976 1 -24.909839 4.898372 4.908248
3 0.723351 3 -0.523236 4.898390 4.908822
4 2.374280 8 -5.637203 5.006047 5.006618
... ... ... ... ... ...
95 -0.063146 96 -0.003987 20.316671 20.318553
96 1.653335 99 -2.733516 21.108188 21.108735
97 1.715107 100 -2.941592 21.108193 21.109278
98 0.109778 98 -0.012051 21.108183 21.109476
99 0.109489 97 -0.011988 21.108174 21.109731

100 rows × 5 columns

We can also plot the evolution of the objective to verify that we converge correctly toward $$0$$.

:

import matplotlib.pyplot as plt

plt.plot(results.objective)

plt.xlabel("Iterations")
plt.ylabel("$y = f(x)$")

plt.show() 