From 9607c7f7781096c4d9a525a44a994d11d2272b03 Mon Sep 17 00:00:00 2001 From: fmfn Date: Sat, 24 Nov 2018 18:34:12 -0600 Subject: [PATCH] Update README This commit adds a disclaimer to the readme file telling people of api breaking changing. Moreover it updates the quick start list with new files and add a good chunck of the basic tour to the readme file. --- README.md | 228 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 217 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index c370c9ba0..a77afc54f 100644 --- a/README.md +++ b/README.md @@ -15,22 +15,27 @@ function in as few iterations as possible. This technique is particularly suited for optimization of high cost functions, situations where the balance between exploration and exploitation is important. +## Important notice +With the release of version 1.0.0 a number of breaking API changes were introduced. I understand this can be a headache for some, but these were necessary changes that needed to be done and ultimately made the package better. If you have used this package in the past I suggest you take the basic and advanced tours (found in the examples folder) in order to familiarize yourself with the new API. + ## Quick Start -In the [examples](https://github.com/fmfn/BayesianOptimization/tree/master/examples) -folder you can get a grip of how the method and this package work by: -- Checking out this +See below for a quick tour over the basics of the Bayesian Optimization package. More detailed information, other advanced features, and tips on usage/implementation can be found in the [examples](https://github.com/fmfn/BayesianOptimization/tree/master/examples) folder. I suggest that you: +- Follow the +[basic tour notebook](https://github.com/fmfn/BayesianOptimization/blob/master/examples/basic-tour.ipynb) +to learn how to use the package's most important features. +- Take a look at the +[advanced tour notebook](https://github.com/fmfn/BayesianOptimization/blob/master/examples/advanced-tour.ipynb) +to learn how to make the package more flexible, how to deal with categorical parameters, how to use observers, and more. +- Check out this [notebook](https://github.com/fmfn/BayesianOptimization/blob/master/examples/visualization.ipynb) with a step by step visualization of how this method works. -- Going over this -[script](https://github.com/fmfn/BayesianOptimization/blob/master/examples/usage.py) -to become familiar with this package's basic functionalities. -- Exploring this [notebook](https://github.com/fmfn/BayesianOptimization/blob/master/examples/exploitation%20vs%20exploration.ipynb) +- Explore this [notebook](https://github.com/fmfn/BayesianOptimization/blob/master/examples/exploitation%20vs%20exploration.ipynb) exemplifying the balance between exploration and exploitation and how to control it. -- Checking out these scripts ([sklearn](https://github.com/fmfn/BayesianOptimization/blob/master/examples/sklearn_example.py), -[xgboost](https://github.com/fmfn/BayesianOptimization/blob/master/examples/xgboost_example.py)) -for examples of how to use this package to tune parameters of ML estimators -using cross validation and bayesian optimization. +- Go over this [script](https://github.com/fmfn/BayesianOptimization/blob/master/examples/sklearn_example.py) +for examples of how to tune parameters of Machine Learning models using cross validation and bayesian optimization. +- Finally, take a look at this [script](https://github.com/fmfn/BayesianOptimization/blob/master/examples/async_optimization.py) +for ideas on how to implement bayesian optimization in a distributed fashion using this package. ## How does it work? @@ -48,6 +53,207 @@ This process is designed to minimize the number of steps required to find a comb This project is under active development, if you find a bug, or anything that needs correction, please let me know. + +Basic tour of the Bayesian Optimization package +=============================================== + +## 1. Specifying the function to be optimized + +This is a function optimization package, therefore the first and most important ingreedient is, of course, the function to be optimized. + +**DISCLAIMER:** We know exactly how the output of the function below depends on its parameter. Obviously this is just an example, and you shouldn't expect to know it in a real scenario. However, it should be clear that you don't need to. All you need in order to use this package (and more generally, this technique) is a function `f` that takes a known set of parameters and outputs a real number. + + +```python +def black_box_function(x, y): + """Function with unknown internals we wish to maximize. + + This is just serving as an example, for all intents and + purposes think of the internals of this function, i.e.: the process + which generates its output values, as unknown. + """ + return -x ** 2 - (y - 1) ** 2 + 1 +``` + +## 2. Getting Started + +All we need to get started is to instanciate a `BayesianOptimization` object specifying a function to be optimized `f`, and its parameters with their corresponding bounds, `pbounds`. This is a constrained optimization technique, so you must specify the minimum and maximum values that can be probed for each parameter in order for it to work + + +```python +from bayes_opt import BayesianOptimization + +# Bounded region of parameter space +pbounds = {'x': (2, 4), 'y': (-3, 3)} + +optimizer = BayesianOptimization( + f=black_box_function, + pbounds=pbounds, + random_state=1, +) +``` + +The BayesianOptimization object will work all of the box without much tuning needed. The main method you should be aware of is `maximize`, which does exactly what you think it does. + +There are many parameters you can pass to maximize, nonetheless, the most important ones are: +- `n_iter`: How many steps of bayesian optimization you want to perform. The more steps the more likely to find a good maximum you are. +- `init_points`: How many steps of **random** exploration you want to perform. Random exploration can help by diversifying the exploration space. + + +```python +optimizer.maximize( + init_points=2, + n_iter=3, +) +``` + + | iter | target | x | y | + ------------------------------------------------- + |  1  | -7.135  |  2.834  |  1.322  | + |  2  | -7.78  |  2.0  | -1.186  | + |  3  | -19.0  |  4.0  |  3.0  | + |  4  | -16.3  |  2.378  | -2.413  | + |  5  | -4.441  |  2.105  | -0.005822 | + ================================================= + + +The best combination of parameters and target value found can be accessed via the property `optimizer.max`. + + +```python +print(optimizer.max) +>>> {'target': -4.441293113411222, 'params': {'y': -0.005822117636089974, 'x': 2.104665051994087}} +``` + + +While the list of all parameters probed and their corresponding target values is available via the property `optimizer.res`. + + +```python +for i, res in enumerate(optimizer.res): + print("Iteration {}: \n\t{}".format(i, res)) + +>>> Iteration 0: +>>> {'target': -7.135455292718879, 'params': {'y': 1.3219469606529488, 'x': 2.8340440094051482}} +>>> Iteration 1: +>>> {'target': -7.779531005607566, 'params': {'y': -1.1860045642089614, 'x': 2.0002287496346898}} +>>> Iteration 2: +>>> {'target': -19.0, 'params': {'y': 3.0, 'x': 4.0}} +>>> Iteration 3: +>>> {'target': -16.29839645063864, 'params': {'y': -2.412527795983739, 'x': 2.3776144540856503}} +>>> Iteration 4: +>>> {'target': -4.441293113411222, 'params': {'y': -0.005822117636089974, 'x': 2.104665051994087}} +``` + + +### 2.1 Changing bounds + +During the optimization process you may realize the bounds chosen for some parameters are not adequate. For these situations you can invoke the method `set_bounds` to alter them. You can pass any combination of **existing** parameters and their associated new bounds. + + +```python +optimizer.set_bounds(new_bounds={"x": (-2, 3)}) + +optimizer.maximize( + init_points=0, + n_iter=5, +) +``` + + | iter | target | x | y | + ------------------------------------------------- + | 6 | -5.145 | 2.115 | -0.2924 | + | 7 | -5.379 | 2.337 | 0.04124 | + |  8 | -3.581 |  1.874 | -0.03428 | + |  9 | -2.624 |  1.702 |  0.1472 | + |  10 | -1.762 |  1.442 |  0.1735 | + ================================================= + + +## 3. Guiding the optimization + +It is often the case that we have an idea of regions of the parameter space where the maximum of our function might lie. For these situations the `BayesianOptimization` object allows the user to specify specific points to be probed. By default these will be explored lazily (`lazy=True`), meaning these points will be evaluated only the next time you call `maximize`. This probing process happens before the gaussian process takes over. + +Parameters can be passed as dictionaries or as an iterable. + +```python +optimizer.probe( + params={"x": 0.5, "y": 0.7}, + lazy=True, +) + +optimizer.probe( + params=[-0.3, 0.1], + lazy=True, +) + +# Will probe only the two points specified above +optimizer.maximize(init_points=0, n_iter=0) +``` + + | iter | target | x | y | + ------------------------------------------------- + | 11 | 0.66 | 0.5 | 0.7 | + | 12 | 0.1 | -0.3 | 0.1 | + ================================================= + + +## 4. Saving, loading and restarting + +By default you can follow the progress of your optimization by setting `verbose>0` when instanciating the `BayesianOptimization` object. If you need more control over logging/alerting you will need to use an observer. For more information about observers checkout the advanced tour notebook. Here we will only see how to use the native `JSONLogger` object to save to and load progress from files. + +### 4.1 Saving progress + + +```python +from bayes_opt.observer import JSONLogger +from bayes_opt.event import Events +``` + +The observer paradigm works by: +1. Instantiating an observer object. +2. Tying the observer object to a particular event fired by an optimizer. + +The `BayesianOptimization` object fires a number of internal events during optimization, in particular, everytime it probes the function and obtains a new parameter-target combination it will fire an `Events.OPTIMIZATION_STEP` event, which our logger will listen to. + +**Caveat:** The logger will not look back at previously probed points. + + +```python +logger = JSONLogger(path="./logs.json") +optimizer.subscribe(Events.OPTMIZATION_STEP, logger) + +# Results will be saved in ./logs.json +optimizer.maximize( + init_points=2, + n_iter=3, +) +``` + +### 4.2 Loading progress + +Naturally, if you stored progress you will be able to load that onto a new instance of `BayesianOptimization`. The easiest way to do it is by invoking the `load_logs` function, from the `util` submodule. + + +```python +from bayes_opt.util import load_logs + + +new_optimizer = BayesianOptimization( + f=black_box_function, + pbounds={"x": (-2, 2), "y": (-2, 2)}, + verbose=2, + random_state=7, +) + +# New optimizer is loaded with previously seen points +load_logs(new_optimizer, logs=["./logs.json"]); +``` + +## Next Steps + +This introduction covered the most basic functionality of the package. Checkout the `basic-tour` and `advanced-tour` notebooks in the example folder, yhere you will more detailed explanations and other more advanced functionality. Also, browse the examples folder for implementation tips and ideas. + Installation ============