facebookresearch / nevergrad

A Python toolbox for performing gradient-free optimization
https://facebookresearch.github.io/nevergrad/
MIT License
3.92k stars 352 forks source link

The same parameters are retried many times #1420

Open fumoboy007 opened 2 years ago

fumoboy007 commented 2 years ago

Steps to reproduce

  1. Run duplicate_nevergrad_params.py.txt. (I added a .txt extension so that I could upload the file to GitHub. The code is also available in the Relevant Code section below.)

Observed Results

Duplicate parameters and their counts:
[(659.0536549844043, 883), (659.0536549844038, 533), (2202.9149671155956, 502), (2785.0314705264955, 457), (868.0305000000001, 239), (3374.9527080495, 234), (3978.27255, 228), (3471.614120055, 226), (1538.385879945, 224), (1179.054705, 222), (3698.3507655, 217), (1635.0472919504998, 213), (4141.9695, 209), (1031.7274499999999, 203), (3830.9452950000004, 199), (1311.6492345, 195), (4323.8550000000005, 182), (686.145, 170), (659.0536549844035, 145), (659.0536549844023, 126), (659.0536549844028, 103), …]

Note that 659.0536549844043 is tried 883 times! Also, at the beginning of the optimization, all of the workers are given 2505.0 to start with.

Expected Results

(I am new to optimization, so I may be misunderstanding something. Please educate me!)

I expected the optimizer to not try the same parameter if it has already previously tried it. Wouldn’t the result always be the same?

Also, the function is abs(x), so why is the final result not 10, which is the lower bound of the parameter?

Relevant Code

from concurrent.futures import ProcessPoolExecutor

import nevergrad
from nevergrad import optimizers

class _ObjectiveFunction:
    def __call__(self, x: float) -> float:
        return abs(x)

def _main():
    parametrization = nevergrad.p.Instrumentation(
        nevergrad.p.Scalar(lower=10, upper=5000)
    )
    parametrization.random_state.seed(seed=0)

    optimizer = optimizers.NGOpt(parametrization=parametrization,
                                 budget=8000,
                                 num_workers=10)

    parameter_to_count_map: dict[float, int] = dict()

    def tell(_, candidate: nevergrad.p.Parameter, value: float):
        parameter: float = candidate.args[0]
        parameter_to_count_map[parameter] = parameter_to_count_map.get(parameter, 0) + 1
    optimizer.register_callback(name='tell',
                                callback=tell)

    with ProcessPoolExecutor(max_workers=optimizer.num_workers) as executor:
        optimizer.minimize(objective_function=_ObjectiveFunction(),
                           executor=executor,
                           verbosity=2)

    print("Duplicate parameters and their counts:\n{}".format(
        sorted(
            filter(lambda parameter_and_count: parameter_and_count[1] > 1,
                   parameter_to_count_map.items()),
            key=lambda parameter_and_count: parameter_and_count[1],
            reverse=True
        )
    ))

if __name__ == '__main__':
    _main()