<jupyter_start><jupyter_text>DeepHyper 101[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/deephyper/tutorials/blob/main/tutorials/colab/DeepHyper_101.ipynb)In this tutorial, we present the basics of DeepHyper.Let us start with installing DeepHyper!<jupyter_code>!pip install deephyper<jupyter_output><empty_output><jupyter_text>Warning    By design asyncio does not allow nested event loops. Jupyter is using Tornado which already starts an event loop. Therefore the following patch is required to run DeepHyper in a Jupyter notebook.<jupyter_code>!pip install nest_asyncio

import nest_asyncio
nest_asyncio.apply()<jupyter_output>Requirement already satisfied: nest_asyncio in /usr/local/lib/python3.7/dist-packages (1.5.1)<jupyter_text>Optimization ProblemIn the definition of our optimization problem we have two components:1. black-box function that we want to optimize2. the search space of input variables Black-Box FunctionDeepHyper 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.<jupyter_code>def f(config):
    return - config["x"]**2<jupyter_output><empty_output><jupyter_text>Search Space of Input VariablesIn 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`.<jupyter_code>from deephyper.problem import HpProblem


problem = HpProblem()

# define the variable you want to optimize
problem.add_hyperparameter((-10.0, 10.0), "x")

problem<jupyter_output><empty_output><jupyter_text>Evaluator InterfaceDeepHyper 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. For Jupyter notebooks we use the Ray evaluator (`method="ray"`).Depending on available resources (`num_cpus`) available and the quantity of resources used by each computation of the black-box function (`num_cpus_per_task`) the number of available workers is automatically computed.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 `LoggerCallback` to follow the completed evaluations and the evolution of the objective.<jupyter_code>from deephyper.evaluator import Evaluator
from deephyper.evaluator.callback import LoggerCallback


# define the evaluator to distribute the computation
evaluator = Evaluator.create(
    f,
    method="ray",
    method_kwargs={
        "num_cpus": 1,
        "num_cpus_per_task": 0.25,
        "callbacks": [LoggerCallback()]
    },
)

print(f"Evaluator has {evaluator.num_workers} available worker{'' if evaluator.num_workers == 1 else 's'}")<jupyter_output>Evaluator has 4 available workers<jupyter_text>Search AlgorithmThe next step is to define the search algorithm that we want to use. Here, we choose `AMBS` (Asynchronous Model-Based Search) 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.<jupyter_code>from deephyper.search.hps import AMBS


# define your search
search = AMBS(problem, evaluator)<jupyter_output><empty_output><jupyter_text>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).<jupyter_code>results = search.search(max_evals=100)<jupyter_output>[00001] -- best objective: -48.01665 -- received objective: -48.01665
[00002] -- best objective: -32.48806 -- received objective: -32.48806
[00003] -- best objective: -6.95366 -- received objective: -6.95366
[00004] -- best objective: -0.02131 -- received objective: -0.02131
[00005] -- best objective: -0.02131 -- received objective: -6.22327
[00006] -- best objective: -0.02131 -- received objective: -6.66244
[00007] -- best objective: -0.02131 -- received objective: -23.36765
[00008] -- best objective: -0.02131 -- received objective: -18.95907
[00009] -- best objective: -0.02131 -- received objective: -16.40159
[00010] -- best objective: -0.02131 -- received objective: -9.62992
[00011] -- best objective: -0.02131 -- received objective: -17.61847
[00012] -- best objective: -0.02131 -- received objective: -24.02175
[00013] -- best objective: -0.02131 -- received objective: -8.78387
[00014] -- best objective: -0.02131 -- received objective: -1.66473
[00015] -- best objective: -0.02131 -- [...]<jupyter_text>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).<jupyter_code>results<jupyter_output><empty_output><jupyter_text>We can also plot the evolution of the objective to verify that we converge correctly toward $0$.<jupyter_code>import matplotlib.pyplot as plt

plt.plot(results.objective)

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

plt.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0974637103206253, 'fraction_numerical': 0.048333067474876375, 'mean_word_length': 4.982824427480916, 'pattern_counts': {'":': 3, '<': 34, '<?xml version=': 0, '>': 34, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '248480', 'license': 'BSD-2-Clause', 'n_tokens_mistral': 2040, 'n_tokens_neox': 1818, 'n_words': 703, 'path': 'tutorials/colab/DeepHyper_101.ipynb', 'repo_name': 'deephyper/tutorials', 'size': '6269'}