A Python implementation of global optimization with gaussian processes.

Overview


Bayesian Optimization

Travis Codecov Pypi

Pure Python implementation of bayesian global optimization with gaussian processes.

  • PyPI (pip):
$ pip install bayesian-optimization
  • Conda from conda-forge channel:
$ conda install -c conda-forge bayesian-optimization

This is a constrained global optimization package built upon bayesian inference and gaussian process, that attempts to find the maximum value of an unknown 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.

Quick Start

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 folder. I suggest that you:

  • Follow the basic tour notebook to learn how to use the package's most important features.
  • Take a look at the advanced tour notebook 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 with a step by step visualization of how this method works.
  • Explore this notebook exemplifying the balance between exploration and exploitation and how to control it.
  • Go over this script for examples of how to tune parameters of Machine Learning models using cross validation and bayesian optimization.
  • Explore the domain reduction notebook to learn more about how search can be sped up by dynamically changing parameters' bounds.
  • Finally, take a look at this script for ideas on how to implement bayesian optimization in a distributed fashion using this package.

How does it work?

Bayesian optimization works by constructing a posterior distribution of functions (gaussian process) that best describes the function you want to optimize. As the number of observations grows, the posterior distribution improves, and the algorithm becomes more certain of which regions in parameter space are worth exploring and which are not, as seen in the picture below.

BayesianOptimization in action

As you iterate over and over, the algorithm balances its needs of exploration and exploitation taking into account what it knows about the target function. At each step a Gaussian Process is fitted to the known samples (points previously explored), and the posterior distribution, combined with a exploration strategy (such as UCB (Upper Confidence Bound), or EI (Expected Improvement)), are used to determine the next point that should be explored (see the gif below).

BayesianOptimization in action

This process is designed to minimize the number of steps required to find a combination of parameters that are close to the optimal combination. To do so, this method uses a proxy optimization problem (finding the maximum of the acquisition function) that, albeit still a hard problem, is cheaper (in the computational sense) and common tools can be employed. Therefore Bayesian Optimization is most adequate for situations where sampling the function to be optimized is a very expensive endeavor. See the references for a proper discussion of this method.

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 ingredient 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.

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 instantiate 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

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 out 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.
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.

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.

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.

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   |
=================================================

2.2 Sequential Domain Reduction

Sometimes the initial boundaries specified for a problem are too wide, and adding points to improve the response surface in regions of the solution domain is extraneous. Other times the cost function is very expensive to compute, and minimizing the number of calls is extremely beneficial.

When it's worthwhile to converge on an optimal point quickly rather than try to find the optimal point, contracting the domain around the current optimal value as the search progresses can speed up the search progress considerably. Using the SequentialDomainReductionTransformer the bounds of the problem can be panned and zoomed dynamically in an attempt to improve convergence.

sequential domain reduction

An example of using the SequentialDomainReductionTransformer is shown in the domain reduction notebook. More information about this method can be found in the paper "On the robustness of a simple domain reduction scheme for simulation‐based optimization".

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 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.

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 instantiating 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

from bayes_opt.logger 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.

logger = JSONLogger(path="./logs.json")
optimizer.subscribe(Events.OPTIMIZATION_STEP, logger)

# Results will be saved in ./logs.json
optimizer.maximize(
    init_points=2,
    n_iter=3,
)

By default the previous data in the json file is removed. If you want to keep working with the same logger, the reset paremeter in JSONLogger should be set to False.

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.

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, where you will find detailed explanations and other more advanced functionality. Also, browse the examples folder for implementation tips and ideas.

Installation

Installation

The latest release can be obtained by two ways:

  • With PyPI (pip):

    pip install bayesian-optimization
    
  • With conda (from conda-forge channel):

    conda install -c conda-forge bayesian-optimization
    

The bleeding edge version can be installed with:

pip install git+https://github.com/fmfn/BayesianOptimization.git

If you prefer, you can clone it and run the setup.py file. Use the following commands to get a copy from Github and install all dependencies:

git clone https://github.com/fmfn/BayesianOptimization.git
cd BayesianOptimization
python setup.py install

Citation

If you used this package in your research and is interested in citing it here's how you do it:

@Misc{,
    author = {Fernando Nogueira},
    title = {{Bayesian Optimization}: Open source constrained global optimization tool for {Python}},
    year = {2014--},
    url = " https://github.com/fmfn/BayesianOptimization"
}

Dependencies

  • Numpy
  • Scipy
  • Scikit-learn

References:

Comments
  • Error with scipy 1.8.0

    Error with scipy 1.8.0

    I am getting an error with scipy 1.8.0

    File "/home/brendan/python/TestVenv/lib/python3.8/site-packages/bayes_opt/util.py", line 65, in acq_max
    if max_acq is None or -res.fun[0] >= max_acq:
    TypeError: 'float' object is not subscriptable
    

    The problem appears to be that in this new version of scipy, res.fun is returned as a number instead of an array. I haven't yet found exactly where in scipy the change is. A fix may be as simple as adding an if statement to handle this case.

    I'll keep looking into it but for now I suggest limiting scipy installations to 1.7 or less.

    bug Versioning 
    opened by bwheelz36 18
  • Manually adding points and changing the utility function

    Manually adding points and changing the utility function

    I'm using the suggest-register paradigm and I'm doing two things that I think break something in the operation and I wanted to know how to do it correctly.

    At some point, I want to suggest a parameter by myself, externally. So what I do is

    my_pnt_from_an_oracle = 0
    next_point = {'x': my_pnt_from_an_oracle} 
    optimizer.register(params=next_point, target=f(my_pnt_from_an_oracle) )
    _ = optimizer.suggest(utility) # I also call this, although I'm not sure it is necessary
    

    After that, I also want to change the utility to exploitation rather than exploration because I happen to know that the point I'm manually adding, is supposed to be at least some local maxima and also that it is for sure higher than all the points that were suggested until now. So I change:

    utility = UtilityFunction(kind="ei", kappa=0, xi=1e-4) #xi was 10 originally
    

    But now whenever I run the suggest-register combo, I don't seem to get any suggestions that are closer to my_pnt_from_an_oracle to try to find a better maximum. I think it may not be registered correctly? Although, it appears in optimizer.res.

    Any suggestion on how to add a point and change the utility in the middle of an operation?

    opened by sguysc 17
  • `StopIteration: Queue is empty` error in optimizer.maximize()

    `StopIteration: Queue is empty` error in optimizer.maximize()

    I am running into the error StopIteration: Queue is empty, no more objects to retrieve when I run optimizer.maximize(...). Specifically, the traceback is as follows (with pieces referring to my code removed).

    Traceback (most recent call last):
      File "/mnt/mesos/sandbox/.local/lib/python3.8/site-packages/bayes_opt/bayesian_optimization.py", line 179, in maximize
        x_probe = next(self._queue)
      File "/mnt/mesos/sandbox/.local/lib/python3.8/site-packages/bayes_opt/bayesian_optimization.py", line 25, in __next__
        raise StopIteration("Queue is empty, no more objects to retrieve.")
    StopIteration: Queue is empty, no more objects to retrieve.
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
    [ ... ]
      File "/mnt/mesos/sandbox/.local/lib/python3.8/site-packages/bayes_opt/bayesian_optimization.py", line 182, in maximize
        x_probe = self.suggest(util)
      File "/mnt/mesos/sandbox/.local/lib/python3.8/site-packages/bayes_opt/bayesian_optimization.py", line 131, in suggest
        suggestion = acq_max(
      File "/mnt/mesos/sandbox/.local/lib/python3.8/site-packages/bayes_opt/util.py", line 55, in acq_max
        res = minimize(lambda x: -ac(x.reshape(1, -1), gp=gp, y_max=y_max),
      File "/mnt/mesos/sandbox/.local/lib/python3.8/site-packages/scipy/optimize/_minimize.py", line 617, in minimize
        return _minimize_lbfgsb(fun, x0, args, jac, bounds,
      File "/mnt/mesos/sandbox/.local/lib/python3.8/site-packages/scipy/optimize/lbfgsb.py", line 294, in _minimize_lbfgsb
        raise ValueError("LBFGSB - one of the lower bounds is greater than an upper bound.")
    ValueError: LBFGSB - one of the lower bounds is greater than an upper bound.
    

    I found another instance of this issue on StackOverflow. As a commenter mentioned, I am usually able to solve this problem by tweaking the values of pbounds, but it's not a robust solution.

    Thanks in advance!

    opened by noa-codes 17
  • init_points when using the Suggest-Evaluate-Register Paradigm & more

    init_points when using the Suggest-Evaluate-Register Paradigm & more

    Hi, thanks for this great piece of software. I have been playing with it for some time and would like to get some experts' opinion on some of the issues I'm facing now.

    • Is there a way to let the optimiser explore more initial points in the Suggest-Evaluate-Register Paradigm?
    • If there is an 'invalidate' point during the optimisation iterations, what is the best way to 'skip' that invalid point? So far I'm using a try/except structure to throw a constant value as the output of my blackbox function, and then tell the optimiser to only register a point when the output is not that constant value. Is this the right way to do it?
    • Is there a 'maximum' number of input parameters BO can handle? I read somewhere that it shouldn't exceed 20 but not sure how reliable this is.
    • Lastly a general question about BO. Do the input parameters of BO need to be a 'direct' observation of the blackbox function? For example, if I have n points (x_1 to x_n) and the output is simply y = sum(x_1 to x_n). Instead of using x_1 to x_n as inputs, I create a new variable 'idx', and each 'idx' corresponds to a unique set of numbers x_1 to x_n. You can think of idx as a random seed that we use to generate n numbers in Python. Will this be a valid optimisation problem that BO can solve? My intuition tells me yes since BO sees the random number generation as part of the blackbox function, but I wanted to get some other opinions.

    Thanks! Xiao

    opened by xshang93 16
  • Issues with SequentialDomainReductionTransformer - one of the lower bounds is greater than an upper bound

    Issues with SequentialDomainReductionTransformer - one of the lower bounds is greater than an upper bound

    High level description of issue

    I am wondering how to best identify the cause of and ultimately resolve a persistent error I'm having with:

    ValueError: LBFGSB - one of the lower bounds is greater than an upper bound.

    This happens after toying around a lot with various parameters to no avail.

    Not sure what information is helpful and will update based on anything you all say would be helpful in diagnosing. Included here for now is:

    • Model Background and inputs
    • Some outputs / plot from the real model when it breaks down
    • Some plots I have tried to use to track the issue when the model functions properly
    • Toy model that results in similar error
      • error message

    Thanks for any help/insight you can spare!!!

    Model Background and inputs

    I am not sure which values or what information are most useful for helping identify the problem/solution but will list out a few parameters / information.

    • Information

      • Error only happens with the SequentialDomainReductionTransformer passed to the bounds_transformer of the optimizer (standard optimizer does not throw this error)
      • Error happens even at very low values for init points or when probes are passed
      • the bayes optimizer is optimizing for 14 variables
      • to deal with more nuanced constraints, before a 'guess' is evaluated by the model, it is checked by a separate function which determines if the constraints are violated (e.g., variable_a > variable_b > variable_c). If that constraint is violated a 'spiked' score is returned rather than evaluating the whole model (to be clear this is happening within the optimization function, there's just a "if constraints violated" clause at the beginning that decides whether to pass a score of -20,000 or to run the whole model).
    • Parameters:

      • First, I have toyed around with these a lot and still gotten errors, but hopefully this gives an idea of a standard failed run. Please let me know what other information would be useful.
      • To SequentialDomainReductionTransformer:
        • gamma_osc = 0.4
        • gamma_pan = 1.0
        • eta = 0.99
      • to BayesianOptimization:
        • pbounds: (there are 14 but most look like either of the two listed here:
          • 'res#ElectricityRates#ur_monthly_fixed_charge': (0.0, 50.0)
          • 'res#ElectricityRates#period_1#rate': (0.05, 0.35)
        • random_state = np.random.RandomState(100)
      • probes are created dynamically in a separate function and are hard-wired to pass the more nuanced constraints I discussed above (this was a kludge so that the model would have an idea of what parameters would pass, otherwise the model kept returning the spiked score as it kept getting the same score despite changing parameter inputs). So if the constraints was variable_a > variable_b > variable_c, but variable_[a-c] had the same p_bounds of (0.01, 1), then the probes for the variables would be coded to contain points within the following bounds to ensure the optimization function does not pass a spiked score: variable_a: (.8, .9), variable_b: (.5, .8), variable_c: (.01, .4).
      • to optimizer.maximize:
        • init_points = 25
        • n_iter = 100
        • acq = 'ucb'
        • alpha = 1e-1
        • kappa = 10
        • kappa_decay = 0.999
        • kapa_decay_delay = 0

    Error from original model

    once the error is thrown I can look at bounds_transformer.bounds and find where the lower bounds are exceeding upper bounds ([any([i[0] > i[1] for i in j]) for j in bounds_transformer.bounds]). This is an example of where those bounds are being exceeded:

    bounds_transformer.bounds[4]
    array([[5.00000000e-02, 1.61679624e-01],
           [1.94822450e-01, 4.44400120e-01],
           [2.03126994e-01, 4.50000000e-01],
           [1.50000000e-01, 9.17096386e-02], # HERE lower bound exceeds upper bound
           [2.87093063e-01, 8.39717864e-01],
           [2.50000000e-01, 7.32347686e-01],
           [1.82423989e+01, 5.43952019e+01],
           [9.27935306e-02, 3.01482727e-01],
           [2.42864682e-01, 4.50000000e-01],
           [2.43336354e-01, 4.50000000e-01],
           [1.50000000e-01, 2.00541524e-01],
           [2.50000000e-01, 7.11258368e-01],
           [3.89103366e-01, 8.50000000e-01],
           [0.00000000e+00, 2.57757652e+01]])
    

    plotting some outputs for that particular variable:

    import matplotlib.pyplot as plt
    x_min_bound = [b[3][0] for b in bounds_transformer.bounds]
    x_max_bound = [b[3][1] for b in bounds_transformer.bounds]
    x = [x[3] for x in optimizer.space.params]
    
    plt.plot(x_min_bound[1:], label='x lower bound')
    plt.plot(x_max_bound[1:], label='x upper bound')
    plt.plot(x[1:], label='x')
    plt.legend()
    

    yields the below ... no idea why the upper bound and lower bound are crossing over but I assume that that cannot be good!

    Figure_1

    Plots from when the model works properly

    Here are some plots I used to track things from when I lower the init and probe count so the model functions

    First here is the final score from the bayes optimization process over iterations (you can see some model inputs across the top): bayes_internal_20220616_135939

    Next, here are the individual parameters during that same run bayes_bounds_20220616_135939

    Finally, probably not useful, but here are the same variables that were evaluated, but plotted as boxplots (red star corresponds to those values that yielded best score) along with the score on the far right (the whole model tries to minimize the difference between costs and revenues) tariff_internal_results_20220616_135939

    Toy model that results in error

    The actual model in question is quite large, but I can replicate the LBFGSB error on a toy model shown below (essentially by blowing up the kappa value and including 50+ init points during the maximize call).

    import numpy as np
    from bayes_opt import BayesianOptimization
    from bayes_opt import SequentialDomainReductionTransformer
    import matplotlib.pyplot as plt
    
    def optfunc(**kwargs):
        x = np.fromiter(kwargs.values(), dtype=float)
        arg1 = -0.2 * np.sqrt(0.5 * (x[0] ** 2 + x[1] ** 2))
        arg2 = 0.5 * (np.cos(2. * np.pi * x[0]) + np.cos(2. * np.pi * x[1]))
        arg3 = x[2]*x[3]
        arg4 = ((x[4] + x[5])*x[6])/x[7]
        return -1.0 * (-20. * arg1 - arg2 + 20. + np.e + arg3 - arg4)
    
    pbounds = {'x': (-15, 5), 'y': (-5, 15), 'z': (-5, 65), 'a': (-5, 50), 
               'b': (-5, 53), 'c': (-5, 45), 'd': (-75, 5), 'e': (-53, 5)}
    
    bounds_transformer = SequentialDomainReductionTransformer()
    
    mutating_optimizer = BayesianOptimization(
        f=ackley,
        pbounds=pbounds,
        verbose=0,
        random_state=1,
        bounds_transformer=bounds_transformer
    )
    
    mutating_optimizer.maximize(
        init_points=100,
        n_iter=10,
        kappa = 1000, kappa_decay = .995, kappa_decay_delay = 0
    )
    
    

    Error message

    StopIteration                             
    Traceback (most recent call last)
    File ~\.conda\envs\la100es_tariffs\lib\site-packages\bayes_opt\bayesian_optimization.py:179, in BayesianOptimization.maximize(self, init_points, n_iter, acq, kappa, kappa_decay, kappa_decay_delay, xi, **gp_params)
        178 try:
    --> 179     x_probe = next(self._queue)
        180 except StopIteration:
    
    File ~\.conda\envs\la100es_tariffs\lib\site-packages\bayes_opt\bayesian_optimization.py:25, in Queue.__next__(self)
         24 if self.empty:
    ---> 25     raise StopIteration("Queue is empty, no more objects to retrieve.")
         26 obj = self._queue[0]
    
    StopIteration: Queue is empty, no more objects to retrieve.
    
    During handling of the above exception, another exception occurred:
    
    ValueError                                Traceback (most recent call last)
    Input In [17], in <cell line: 1>()
    ----> 1 mutating_optimizer.maximize(
          2     init_points=100,
          3     n_iter=10,
          4     kappa = 1000, kappa_decay = .995, kappa_decay_delay = 0
          5 )
    
    File ~\.conda\envs\la100es_tariffs\lib\site-packages\bayes_opt\bayesian_optimization.py:182, in BayesianOptimization.maximize(self, init_points, n_iter, acq, kappa, kappa_decay, kappa_decay_delay, xi, **gp_params)
        180 except StopIteration:
        181     util.update_params()
    --> 182     x_probe = self.suggest(util)
        183     iteration += 1
        185 self.probe(x_probe, lazy=False)
    
    File ~\.conda\envs\la100es_tariffs\lib\site-packages\bayes_opt\bayesian_optimization.py:131, in BayesianOptimization.suggest(self, utility_function)
        128     self._gp.fit(self._space.params, self._space.target)
        130 # Finding argmax of the acquisition function.
    --> 131 suggestion = acq_max(
        132     ac=utility_function.utility,
        133     gp=self._gp,
        134     y_max=self._space.target.max(),
        135     bounds=self._space.bounds,
        136     random_state=self._random_state
        137 )
        139 return self._space.array_to_params(suggestion)
    
    File ~\.conda\envs\la100es_tariffs\lib\site-packages\bayes_opt\util.py:55, in acq_max(ac, gp, y_max, bounds, random_state, n_warmup, n_iter)
         51 x_seeds = random_state.uniform(bounds[:, 0], bounds[:, 1],
         52                                size=(n_iter, bounds.shape[0]))
         53 for x_try in x_seeds:
         54     # Find the minimum of minus the acquisition function
    ---> 55     res = minimize(lambda x: -ac(x.reshape(1, -1), gp=gp, y_max=y_max),
         56                    x_try.reshape(1, -1),
         57                    bounds=bounds,
         58                    method="L-BFGS-B")
         60     # See if success
         61     if not res.success:
    
    File ~\.conda\envs\la100es_tariffs\lib\site-packages\scipy\optimize\_minimize.py:623, in minimize(fun, x0, args, method, jac, hess, hessp, bounds, constraints, tol, callback, options)
        620     return _minimize_newtoncg(fun, x0, args, jac, hess, hessp, callback,
        621                               **options)
        622 elif meth == 'l-bfgs-b':
    --> 623     return _minimize_lbfgsb(fun, x0, args, jac, bounds,
        624                             callback=callback, **options)
        625 elif meth == 'tnc':
        626     return _minimize_tnc(fun, x0, args, jac, bounds, callback=callback,
        627                          **options)
    
    File ~\.conda\envs\la100es_tariffs\lib\site-packages\scipy\optimize\lbfgsb.py:294, in _minimize_lbfgsb(fun, x0, args, jac, bounds, disp, maxcor, ftol, gtol, eps, maxfun, maxiter, iprint, callback, maxls, finite_diff_rel_step, **unknown_options)
        292 # check bounds
        293 if (new_bounds[0] > new_bounds[1]).any():
    --> 294     raise ValueError("LBFGSB - one of the lower bounds is greater than an upper bound.")
        296 # initial vector must lie within the bounds. Otherwise ScalarFunction and
        297 # approx_derivative will cause problems
        298 x0 = np.clip(x0, new_bounds[0], new_bounds[1])
    
    ValueError: LBFGSB - one of the lower bounds is greater than an upper bound.
    
    opened by MasonBowen 15
  • Maintenance and new releases

    Maintenance and new releases

    Hi all,

    As many of you have noticed, the maintenance of this code has been somewhat lagging lately. The original developer of this code has very little to no time to maintain it. They have given me maintainer rights over the repository, and over the last few months I have been trying to close issues, fix bugs, merge pull requests, fix the CI, etc. I do not at this point have the rights to manage releases on PyPi and I'm not that confident I will get those rights any time soon. So, the question is how can we ensure this great code remains viable and useful moving forward.

    • does anyone else currently have maintainer rights? It would be ideal if there were more than one person managing this.
    • Would anyone be willing to take on a maintainer role? I cannot grant such a role myself, but we can ask fernando to do so.
    • For new releases: the only way I can release new code on pypi would be under a new name such as bayesian-optimization2. I don't love the idea but I'm not sure what else to do. I don't use conda so it would also be helpful to have some input from someone with experience releasing code on conda forge.

    Thoughts?

    opened by bwheelz36 14
  • GP with UCB bug?

    GP with UCB bug?

    I have a weird behaviour of the GP with UCB (with noisy data) the posterior of the GP changes completely after a point, e.g. from: ucb8 xi 0 0 k 11 0t to ucb9 xi 0 0 k 11 0t

    and from ucb11 xi 0 0 k 11 0t to ucb12 xi 0 0 k 11 0t

    Another weird behavior is that is some cases UCB does not select the upper bound (the last selected point is in yellow). I would expect that -2 is selected, but is not: ucb12 xi 0 0 k 11 0t

    ucb13 xi 0 0 k 11 0t

    ucb14 xi 0 0 k 11 0t

    is this a bug or is this normal?

    opened by davidenitti 14
  • KeyError: 'Data point X is not unique'

    KeyError: 'Data point X is not unique'

    Hi and congrats for this sweet piece of software, really love it :)

    I've implemented an async solution very similar to the one in the example notebook, with 13 virtual machines connecting to a master server hosting the Tornado webserver (like in the notebook), and it seems like I'm now constantly running over the same symptoms on the master server (the server registering the tested points and their respective target in the async example):

    Error: 'Data point [3.47653914e+04 2.10539196e+02 3.15656650e+00 6.77134492e+00  1.01962491e+01] is not unique'
    Traceback (most recent call last):
      File "playground_master.py", line 72, in post
        self._bo.register(params=body["params"], target=body["target"])
      File "/usr/local/lib/python3.5/dist-packages/bayes_opt/bayesian_optimization.py", line 104, in register
        self._space.register(params, target)
      File "/usr/local/lib/python3.5/dist-packages/bayes_opt/target_space.py", line 161, in register
        raise KeyError('Data point {} is not unique'.format(x))
    KeyError: 'Data point [3.47653914e+04 2.10539196e+02 3.15656650e+00 6.77134492e+00 1.01962491e+01] is not unique'
    

    It seems like the BO is suggesting to the 13 VMs to test points (set of 5 values in my case) which are already tested, almost in a constant manner after ~ 800 points tested.

    Here's my troubleshooting so far:

    • Using the load/save examples in the front page of BO, I save all points tested in a JSON file and for this instance of the problem, I see 817 points already registered in the JSON
    • It seems like this data point thrown in the traceback is indeed ALREADY in the JSON, with an associated target value
    • The 13 slave VMs are still trying to ask for "suggestions" (further set of points to test), but it seems like BO gives set of points already tested ; I still see some rare cases when it's not tested yet and the counts case increase slightly to 818, 819 points.. (but most of the time the traceback is thrown)

    I'm a little bit surprised you can end up with such scenario given my PBOUND is very broad, and so, has a lot to points to be worked on without having to test the same ones again:

                {'learning_timesteps': (5000, 40000),
                'timesteps_per_batch': (4, 72),
                'observation_loopback': (1, 20),
                'mmr__timeperiod': (7, 365),
                'model': (-0.49, 5.49) }
    

    This is how I initialized the utility, which, as far as I understood, is responsible for suggesting points that were already tested: _uf = UtilityFunction(kind="ucb", kappa=2.576, xi=0)

    Should I modify the acquisition function? or some of the hyperparameters "kappa", "xi" ?

    I see https://github.com/fmfn/BayesianOptimization/issues/92 related to this but I'm not doing any manual point probing / not doing any initialization, I really sticked to the Async notebook example, so I'm not sure this issue applies to me :(

    Let me know if I can share further information & more context to this Thanks in advance for the help :) Lucas

    opened by PoCk3T 12
  • Add observer pattern to implement callbacks

    Add observer pattern to implement callbacks

    Adds a simple observer pattern, allowing callbacks. Currently implemented callbacks:

    • INIT_DONE_EVENT: Initializaiton (bo.init()) finished
    • FIT_STEP_DONE_EVENT: Optimization iteration finished
    • FIT_DONE_EVENT: Optimization finished

    Sample code (taken from exploitation vs exploration (UCB kappa=1.0 cell) sample for readability):

    class Observer:
        def update(self, event, instance):
            print("Observable dispatched event {}".format(event))
    
    observer = Observer()
    
    bo = BayesianOptimization(f=lambda x: f[int(x)],
                              pbounds={"x": (0, len(f)-1)},
                              verbose=0)
    
    bo.register(INIT_DONE_EVENT, observer)
    bo.register(FIT_DONE_EVENT, observer)
    bo.register(FIT_STEP_DONE_EVENT, observer)
    
    bo.maximize(init_points=2, n_iter=3, acq="ucb", kappa=1, **gp_params)
    

    Output:

    Observable dispatched event initialized
    Observable dispatched event fit_step_done
    Observable dispatched event fit_step_done
    Observable dispatched event fit_step_done
    Observable dispatched event fit_done
    

    The observer instance is notified via update(self, event, instance), giving it all the flexibility it needs, not limiting any user specific use cases, as you save the results so far already in self.res.

    I saw the PR #100, this is basically an advanced version as you requested. I am open for changes to this.

    opened by aprams 12
  • replace non-unique point error with warning?

    replace non-unique point error with warning?

    As has been noted in #158, the code will crash if it is asked to probe a point it has already probed.

    This condition occurs not infrequently when using the utility functions. In addition, when working with very noisy data, it is completely valid to probe the same point more than once.

    Describe the solution you'd like I suggest simply replacing the error with a warning, and maybe a parameter called '_n_repeated_points_probed' - that would allow users to handle the issue on their end. Thoughts?

    Are you able and willing to implement this feature yourself and open a pull request?

    • [x] Yes, I can provide this feature.
    enhancement 
    opened by bwheelz36 11
  • Advanced constrained optimization

    Advanced constrained optimization

    Model constraints using Gaussian Process Regression and incorporate them into the selection of new points to sample.

    Cf. #326 #304 #266 #276 #190 (sort of) #189.

    For examples on how to use, see examples/constraints.ipynb.

    I think some more documentation is necessary, but I would like to get feedback on the current state before I do that.

    opened by till-m 11
  • Convergence criteria

    Convergence criteria

    Is your feature request related to a problem? Please describe. Currently, only fixed number of iteration can be set to the optimizer. However, a convergence criteria would be useful.

    Describe the solution you'd like By using a convergence criteria, a maximum number of steps OR the convergence criteria could be used to stop the iteration (whichever happens first). The reason why the iteration stopped should be available after the execution.

    Are you able and willing to implement this feature yourself and open a pull request?

    • [X] Yes, I can provide this feature.

    If anyone can do this issue, that would be great, however, I'm also fine with doing it myself in a form of PR.

    enhancement 
    opened by gaborantal 3
  • Fix acqusistion function bug when std output by GP is 0.0

    Fix acqusistion function bug when std output by GP is 0.0

    In order to calculate the acquisition function EI and PI for a given point x, we will use mean and std output by the gaussian process. However, std may be 0.0(e.g., the gaussian process will set std to 0.0 when we sample a point x that has been sampled before), which will cause an error when calculating EI and PI. In function _ei, when std is 0.0, we can't calculate the EI value using a * norm.cdf(z) + std * norm.pdf(z), instead, we should set the EI value to 0.0. So does function _poi. Therefore, I changed function _ei and _poi to fix this bug. I refer to Acquisition functions in Bayesian Optimization, which discussed the acquisition function in detail and explained the derivation process of PI and EI

    opened by csbo98 1
  • Parameter Types: Handling and Implementation

    Parameter Types: Handling and Implementation

    Why? Support for non-float parameters types is by far the most-request feature that this package doesn't have as of now.

    Why this issue? I would like to explicitly discuss if and how to implement parameter types. In that sense this issue isn't a feature request, but intended to server as a space to collect discussions about this topic.

    How? For my perspective, Garrido-Merchán and Hernández-Lobato seems to make the most sense. This means converting the parameters within the kernel: $$\tilde{k}( x_i, x_j)=k(T(x_i), T(x_j))$$ where $T$ acts on elements of $x_i$ according to their type.

    What is necessary? Essentially all of this "only" requires three functions to transform the parameters:

    • A function that converts the "canonical" representation of the parameters to the representation used by the kernel.
      • float and int parameters remain unchangend, one-hot-encoding is applied to categorical variables
    • A function that converts the kernel representation to the canonical representation, used whenever the user interacts with parameters (logs, optimizer.max(), etc)
      • float and int parameters remain unchangend, reverse the one-hot-encoding.
    • A function that converts the all-float parameter suggestions produced by _space.random_sample() and acq_max() into kernel representation.

    Naturally, it requires changing a lot of other code, too; and particularly in ways that make everything a bit messier. Additionally, wrapping the kernel requires some slightly hacky python magic due to the way sklearn.gaussian_process' kernels are set up.

    Alternatives Instead of offering proper support, we could simply refer users to @leifvan's implementation here. Alternatively, we could set up a notebook that demonstrates his approach. I'm almost favouring this approach since I'm worried about cluttering the API too much.

    Are you able and willing to implement this feature yourself and open a pull request?

    • [x] Yes, I can provide this feature -- it's pretty much ready/functional too, I will push it soon and link it here.
    enhancement 
    opened by till-m 6
  • Proper parallel optimisation

    Proper parallel optimisation

    I have a single threaded function which I want to optimise. I'm trying to write a wrapper that would handle multiple runs at the same time, but I'm noticing considerable degradation in results as I increase the number of parallel evaluations.

    Here is the rough logic of what I'm doing:

    from __future__ import annotations
    
    from pprint import pprint
    from typing import Callable
    
    import numpy as np
    from bayes_opt.bayesian_optimization import BayesianOptimization
    from bayes_opt.util import UtilityFunction
    from tqdm import tqdm, trange
    
    
    def multivariable_func(r: float, x: float, y: float, diff: float) -> float:
        r = int(r)
        diff = diff > 0.5
    
        loss = (r - 5) ** 2
        loss += (x**2 + y**2 - r) ** 2
        loss += abs(x - y) * (-1) ** int(diff)
        loss += 0.5 * x
        loss += -0.25 * int(diff)
        return -loss
    
    
    def optimize(func: Callable[..., float], num_iter: int, bounds: dict[str, tuple[float, float]], num_workers=0):
        init_samples = int(np.sqrt(num_iter))
    
        optimizer = BayesianOptimization(f=None, pbounds=bounds, verbose=0)
        init_kappa = 10
        kappa_decay = (0.1 / init_kappa) ** (1 / (num_iter - init_samples))
        utility = UtilityFunction(
            kind="ucb", kappa=init_kappa, xi=0.0, kappa_decay=kappa_decay, kappa_decay_delay=init_samples
        )
    
        init_queue = [optimizer.suggest(utility) for _ in range(init_samples)]
        result_queue = []
        tbar = tqdm(total=num_iter, leave=False)
        while len(optimizer.res) < num_iter:
            sample = init_queue.pop(0) if init_queue else optimizer.suggest(utility)
            loss = func(**sample)
            result_queue.append((sample, loss))
            if len(result_queue) >= num_workers:
                try:
                    optimizer.register(*result_queue.pop(0))
                    utility.update_params()
                    tbar.update()
                except KeyError:
                    pass
        return optimizer.max
    
    
    bounds = {"r": [-10, 10], "x": [-10, 10], "y": [-10, 10], "diff": [0, 1]}
    
    all_results = {}
    for num_workers in tqdm([1, 2, 4, 8], desc="Checking num_workers"):
        results = []
        for idx in trange(2, desc=f"Sampling with {num_workers=}"):
            best = optimize(multivariable_func, 400, bounds, num_workers)
            results.append(best["target"])
        all_results[num_workers] = np.mean(results)
        tqdm.write(f"Result for optimizing with {num_workers=}: {all_results[num_workers]}")
    print("\n")
    pprint(all_results)
    

    The result_queue variable is simulating evaluation across multiple processes.

    Here are the results:

    {1: 4.320798413579277,
     2: 4.320676522735756,
     4: 3.5379530743926133,
     8: 2.175667857740832}
    

    As can be seen, the more processes I use, the worse the final result is. I don't understand why that would happen, even if a couple fewer suggestions are evaluated properly, the results should not differ so much.

    What am I missing?

    enhancement 
    opened by Rizhiy 15
  • log files are not valid json format

    log files are not valid json format

    It is a small issue, but annoying non the less. When I try to commit a json log file, it gets flagged by the json5 pre-commit hook.

    check json5..............................................................Failed
    - hook id: check-json5
    - exit code: 1
    
    log.json: Failed to json decode (<string>:2 Unexpected "{" at column 1)
    

    Possible solutions:

    1. Read of the original file, update it and write the whole updated json
    2. Do not use the .json extension as stated the documentation, but for example txt instead.
    3. Hacky way would be to write a [ on first opening of the file. And on subsequent writes start with a ,. After the last write do a ].
    bug 
    opened by rruiter87 3
Releases(v1.4.2)
  • v1.4.2(Dec 4, 2022)

  • v1.4.1(Nov 29, 2022)

  • v1.4.0(Nov 27, 2022)

    • some bug fixes for contraints models
    • update code to explicitly handle duplicate points
    • update the maximize method for a more consistent approach in setting gp params and acquisition functions.
    Source code(tar.gz)
    Source code(zip)
  • v1.3.1(Oct 26, 2022)

  • v1.3.0(Oct 24, 2022)

    First update after many years. Among many new features detailed below, will now work with scipy > 1.8

    What's Changed

    • Added option to keep using an older log (Save -> Load -> Save) by @alonfnt in https://github.com/fmfn/BayesianOptimization/pull/230
    • Added docstrings to the main functions. by @alonfnt in https://github.com/fmfn/BayesianOptimization/pull/238
    • Check bounds_transformation is correct type by @alonfnt in https://github.com/fmfn/BayesianOptimization/pull/240
    • Add virtual environments folders to gitignore by @alonfnt in https://github.com/fmfn/BayesianOptimization/pull/251
    • Add license classifier to setup.py by @dotcs in https://github.com/fmfn/BayesianOptimization/pull/253
    • Fixes the error caused by scipy 1.8 minimize results changing. by @samFarrellDay in https://github.com/fmfn/BayesianOptimization/pull/303
    • Update test badge by @bwheelz36 in https://github.com/fmfn/BayesianOptimization/pull/322
    • switch to github actions for CI by @bwheelz36 in https://github.com/fmfn/BayesianOptimization/pull/323
    • Fix typo, Update util.py by @zhaofeng-shu33 in https://github.com/fmfn/BayesianOptimization/pull/293
    • docs: fix simple typo, provieded -> provided by @timgates42 in https://github.com/fmfn/BayesianOptimization/pull/267
    • 2 typo fixes in advanced-tour.ipynb by @parkjin-nim in https://github.com/fmfn/BayesianOptimization/pull/272
    • Update some of the SequentialDomain transformer by @osullivryan in https://github.com/fmfn/BayesianOptimization/pull/332
    • print both digits of the exponent in scientific notation form for flo… by @citrusvanilla in https://github.com/fmfn/BayesianOptimization/pull/282
    • Create feature_request.md by @bwheelz36 in https://github.com/fmfn/BayesianOptimization/pull/345
    • Revert "Create feature_request.md" by @bwheelz36 in https://github.com/fmfn/BayesianOptimization/pull/346
    • Queue by @alonfnt in https://github.com/fmfn/BayesianOptimization/pull/239
    • Add issue templates by @till-m in https://github.com/fmfn/BayesianOptimization/pull/348
    • changed the way x_try is sent to minimize - fixes #350 by @bwheelz36 in https://github.com/fmfn/BayesianOptimization/pull/351
    • Advanced constrained optimization by @till-m in https://github.com/fmfn/BayesianOptimization/pull/344
    • Fix JSONLogger with constraints by @till-m in https://github.com/fmfn/BayesianOptimization/pull/364
    • update version and setup.py by @bwheelz36 in https://github.com/fmfn/BayesianOptimization/pull/367

    New Contributors

    • @alonfnt made their first contribution in https://github.com/fmfn/BayesianOptimization/pull/230
    • @dotcs made their first contribution in https://github.com/fmfn/BayesianOptimization/pull/253
    • @samFarrellDay made their first contribution in https://github.com/fmfn/BayesianOptimization/pull/303
    • @bwheelz36 made their first contribution in https://github.com/fmfn/BayesianOptimization/pull/322
    • @zhaofeng-shu33 made their first contribution in https://github.com/fmfn/BayesianOptimization/pull/293
    • @timgates42 made their first contribution in https://github.com/fmfn/BayesianOptimization/pull/267
    • @parkjin-nim made their first contribution in https://github.com/fmfn/BayesianOptimization/pull/272
    • @citrusvanilla made their first contribution in https://github.com/fmfn/BayesianOptimization/pull/282
    • @till-m made their first contribution in https://github.com/fmfn/BayesianOptimization/pull/348

    Full Changelog: https://github.com/fmfn/BayesianOptimization/compare/1.2.0...v1.3.0

    Source code(tar.gz)
    Source code(zip)
  • 1.2.0(May 16, 2020)

  • v1.1.0(Feb 17, 2020)

  • v1.0.3(Feb 17, 2020)

  • v1.0.1(Feb 12, 2019)

  • v1.0.0(Nov 25, 2018)

  • 0.6.0(Dec 17, 2017)

Owner
fernando
Formerly: Theoretical Physics; currently: Machine Learning; seldom: Open Source
fernando
A highly efficient and modular implementation of Gaussian Processes in PyTorch

GPyTorch GPyTorch is a Gaussian process library implemented using PyTorch. GPyTorch is designed for creating scalable, flexible, and modular Gaussian

null 3k Jan 2, 2023
library for nonlinear optimization, wrapping many algorithms for global and local, constrained or unconstrained, optimization

NLopt is a library for nonlinear local and global optimization, for functions with and without gradient information. It is designed as a simple, unifi

Steven G. Johnson 1.4k Dec 25, 2022
Supplementary code for the AISTATS 2021 paper "Matern Gaussian Processes on Graphs".

Matern Gaussian Processes on Graphs This repo provides an extension for gpflow with Matérn kernels, inducing variables and trainable models implemente

null 41 Dec 17, 2022
A Tensorflow based library for Time Series Modelling with Gaussian Processes

Markovflow Documentation | Tutorials | API reference | Slack What does Markovflow do? Markovflow is a Python library for time-series analysis via prob

Secondmind Labs 24 Dec 12, 2022
This is code to fit per-pixel environment map with spherical Gaussian lobes, using LBFGS optimization

Spherical Gaussian Optimization This is code to fit per-pixel environment map with spherical Gaussian lobes, using LBFGS optimization. This code has b

null 41 Dec 14, 2022
Gradient-free global optimization algorithm for multidimensional functions based on the low rank tensor train format

ttopt Description Gradient-free global optimization algorithm for multidimensional functions based on the low rank tensor train (TT) format and maximu

null 5 May 23, 2022
Racing line optimization algorithm in python that uses Particle Swarm Optimization.

Racing Line Optimization with PSO This repository contains a racing line optimization algorithm in python that uses Particle Swarm Optimization. Requi

Parsa Dahesh 6 Dec 14, 2022
Genetic Algorithm, Particle Swarm Optimization, Simulated Annealing, Ant Colony Optimization Algorithm,Immune Algorithm, Artificial Fish Swarm Algorithm, Differential Evolution and TSP(Traveling salesman)

scikit-opt Swarm Intelligence in Python (Genetic Algorithm, Particle Swarm Optimization, Simulated Annealing, Ant Colony Algorithm, Immune Algorithm,A

郭飞 3.7k Jan 3, 2023
Implementation of "Fast and Flexible Temporal Point Processes with Triangular Maps" (Oral @ NeurIPS 2020)

Fast and Flexible Temporal Point Processes with Triangular Maps This repository includes a reference implementation of the algorithms described in "Fa

Oleksandr Shchur 20 Dec 2, 2022
Official Pytorch implementation of ICLR 2018 paper Deep Learning for Physical Processes: Integrating Prior Scientific Knowledge.

Deep Learning for Physical Processes: Integrating Prior Scientific Knowledge: Official Pytorch implementation of ICLR 2018 paper Deep Learning for Phy

emmanuel 47 Nov 6, 2022
Official implementation of deep Gaussian process (DGP)-based multi-speaker speech synthesis with PyTorch.

Multi-speaker DGP This repository provides official implementation of deep Gaussian process (DGP)-based multi-speaker speech synthesis with PyTorch. O

sarulab-speech 24 Sep 7, 2022
QuakeLabeler is a Python package to create and manage your seismic training data, processes, and visualization in a single place — so you can focus on building the next big thing.

QuakeLabeler Quake Labeler was born from the need for seismologists and developers who are not AI specialists to easily, quickly, and independently bu

Hao Mai 15 Nov 4, 2022
A Python package for faster, safer, and simpler ML processes

Bender ?? A Python package for faster, safer, and simpler ML processes. Why use bender? Bender will make your machine learning processes, faster, safe

Otovo 6 Dec 13, 2022
JumpDiff: Non-parametric estimator for Jump-diffusion processes for Python

jumpdiff jumpdiff is a python library with non-parametric Nadaraya─Watson estimators to extract the parameters of jump-diffusion processes. With jumpd

Rydin 28 Dec 10, 2022
Official code for the ICLR 2021 paper Neural ODE Processes

Neural ODE Processes Official code for the paper Neural ODE Processes (ICLR 2021). Abstract Neural Ordinary Differential Equations (NODEs) use a neura

Cristian Bodnar 50 Oct 28, 2022
Self-Adaptable Point Processes with Nonparametric Time Decays

NPPDecay This is our implementation for the paper Self-Adaptable Point Processes with Nonparametric Time Decays, by Zhimeng Pan, Zheng Wang, Jeff M. P

zpan 2 Sep 24, 2022
PyTorch implementation of "ContextNet: Improving Convolutional Neural Networks for Automatic Speech Recognition with Global Context" (INTERSPEECH 2020)

ContextNet ContextNet has CNN-RNN-transducer architecture and features a fully convolutional encoder that incorporates global context information into

Sangchun Ha 24 Nov 24, 2022
Implementation of Self-supervised Graph-level Representation Learning with Local and Global Structure (ICML 2021).

Self-supervised Graph-level Representation Learning with Local and Global Structure Introduction This project is an implementation of ``Self-supervise

MilaGraph 50 Dec 9, 2022