Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix unexpected recommendation #951

Merged
merged 8 commits into from
Dec 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

**Cautious:** current `master` branch and `0.4.2.postX` version introduce tentative APIs which may be removed in the near future. Use version `0.4.2` for a more stable version.

- By default, the optimizer now returns the best set of parameter as recommendation [#951](https://github.com/facebookresearch/nevergrad/pull/951), considering that the function is deterministic. The previous behavior would use an estimation of noise to provide the pessimistic best point, leading to unexpected behaviors [#947](https://github.com/facebookresearch/nevergrad/pull/947). You can can back to this behavior by specifying: :code:`parametrization.descriptors.deterministic_function = False`
- as an **experimental** feature we have added some preliminary support for constraint management through penalties.
From then on the prefered option for penalty is to register a function returning a positive float when the constraint is satisfied.
While we will wait fore more testing before documenting it, this may already cause instabilities and errors when adding cheap constraints.
Expand Down
7 changes: 4 additions & 3 deletions nevergrad/optimization/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def __init__(
] = utils.Archive() # dict like structure taking np.ndarray as keys and Value as values
self.current_bests = {
x: utils.MultiValue(self.parametrization, np.inf, reference=self.parametrization)
for x in ["optimistic", "pessimistic", "average"]
for x in ["optimistic", "pessimistic", "average", "minimum"]
}
# pruning function, called at each "tell"
# this can be desactivated or modified by each implementation
Expand Down Expand Up @@ -395,7 +395,7 @@ def _update_archive_and_bests(self, candidate: p.Parameter, loss: tp.FloatLoss)
# update current best records
# this may have to be improved if we want to keep more kinds of best losss

for name in ["optimistic", "pessimistic", "average"]:
for name in self.current_bests:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yes cool as a shortcut :-)

if mvalue is self.current_bests[name]: # reboot
best = min(self.archive.values(), key=lambda mv, n=name: mv.get_estimation(n)) # type: ignore
# rebuild best point may change, and which value did not track the updated value anyway
Expand Down Expand Up @@ -488,7 +488,8 @@ def recommend(self) -> p.Parameter:
"""
recom_data = self._internal_provide_recommendation() # pylint: disable=assignment-from-none
if recom_data is None:
return self.current_bests["pessimistic"].parameter
name = "minimum" if self.parametrization.descriptors.deterministic_function else "pessimistic"
return self.current_bests[name].parameter
Comment on lines +491 to +492
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@teytaud this is a drastic change in my opinion, you may want to crosscheck it in the xps

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is safe but yes I'll check xps... complicated though are the absolute indicators are just new.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we can wait a bit to merge if it is safer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or I provide a fix for the noisy variants

return self.parametrization.spawn_child().set_standardized_data(recom_data, deterministic=True)

def _internal_tell_not_asked(self, candidate: p.Parameter, loss: tp.FloatLoss) -> None:
Expand Down
31 changes: 5 additions & 26 deletions nevergrad/optimization/recorded_recommendations.csv
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ AvgHammersleySearch,0.2104283942,-1.1503493804,-0.1397102989,0.8416212336,,,,,,,
AvgHammersleySearchPlusMiddlePoint,0.5244005127,-1.1503493804,-0.1397102989,0.8416212336,,,,,,,,,,,,
AvgMetaRecenteringNoHull,0.6962783408,-0.1604212797,-0.6145401633,0.8813954947,,,,,,,,,,,,
AvgRandomSearch,1.7163318206,-1.0929434198,-0.5715059829,0.2691248199,,,,,,,,,,,,
BO,0.0450718775,0.4964703331,-0.8734357921,0.2235333819,,,,,,,,,,,,
BPRotationInvariantDE,-0.5273201105,-0.156805946,-1.2551455501,3.327306828,2.3993996197,1.2830831401,0.8566151549,-1.6450076557,,,,,,,,
CM,1.0082049151,-0.9099785499,-1.025147209,1.2046460074,,,,,,,,,,,,
CMA,1.012515477,-0.9138805701,-1.029555946,1.2098418178,,,,,,,,,,,,
Expand All @@ -39,7 +38,7 @@ FCMA,1.012515477,-0.9138691467,-1.0295302074,1.2097964496,,,,,,,,,,,,
FCMAp13,0.1012515477,-0.0913869147,-0.1029530207,0.120979645,,,,,,,,,,,,
FCMAs03,0.3037546431,-0.274160744,-0.3088590622,0.3629389349,,,,,,,,,,,,
FastGADiscreteOnePlusOne,0.7531428339,1.095956118,0.0,1.3423563714,,,,,,,,,,,,
FastGANoisyDiscreteOnePlusOne,0.0,0.0,0.0,0.0,,,,,,,,,,,,
FastGANoisyDiscreteOnePlusOne,0.7531428339,1.095956118,0.0,1.3423563714,,,,,,,,,,,,
FastGAOptimisticNoisyDiscreteOnePlusOne,0.7531428339,1.095956118,0.0,1.3423563714,,,,,,,,,,,,
HAvgMetaRecentering,0.6962783408,-0.1604212797,-0.6145401633,0.8813954947,,,,,,,,,,,,
HCHAvgCauchyLHSSearch,-0.527971877,1.341890246,2.6790716005,3.5963545262,,,,,,,,,,,,
Expand All @@ -58,7 +57,6 @@ HammersleySearchPlusMiddlePoint,0.5244005127,-1.1503493804,-0.1397102989,0.84162
HyperOpt,0.1739870283,-1.2730174397,-0.5747313482,0.4130359177,,,,,,,,,,,,
IsoEMNA,1.012515477,-0.9138691467,-1.0295302074,1.2097964496,,,,,,,,,,,,
IsoEMNATBPSA,0.0,0.0,0.0,0.0,,,,,,,,,,,,
LBO,-1.334614043,-0.3148519487,-0.4016795326,-0.8378659256,,,,,,,,,,,,
LHSSearch,-0.3978418928,0.827925915,1.2070034191,1.3637174061,,,,,,,,,,,,
LargeHaltonSearch,-67.4489750196,43.0727299295,-25.3347103136,-56.5948821933,,,,,,,,,,,,
LhsDE,-0.8072358182,0.6354687554,1.575403308,1.1808277036,2.5888168575,-0.1627990771,-3.656466139,-1.040475202,,,,,,,,
Expand All @@ -72,7 +70,6 @@ MetaNGOpt8,0.0051270781,-0.1202306759,-1.0295302074,1.2098266949,,,,,,,,,,,,
MetaRecentering,0.6962783408,-0.1604212797,-0.6145401633,0.8813954947,,,,,,,,,,,,
MetaTuneRecentering,0.925614596,-0.2132599411,-0.8169539561,1.1717046001,,,,,,,,,,,,
MicroCMA,1.0125e-06,-9.139e-07,-1.0296e-06,1.2098e-06,,,,,,,,,,,,
MidQRBO,0.0775339933,-0.0417186599,0.7446977353,2.0954518463,,,,,,,,,,,,
MilliCMA,0.0010125155,-0.0009138806,-0.0010295559,0.0012098418,,,,,,,,,,,,
MiniDE,0.8273276988,-1.2921051963,-0.4797521288,0.2138608624,0.7088815721,0.7346249014,-2.6392592028,-1.0729615222,,,,,,,,
MiniLhsDE,-0.0313128807,0.2738703026,-0.1988242191,0.9942001938,0.7167500893,-0.0350394443,-1.5341684983,-0.3039246928,,,,,,,,
Expand All @@ -98,7 +95,7 @@ NelderMead,0.0,0.0,0.0,0.00025,,,,,,,,,,,,
NoisyBandit,1.012515477,-0.9138691467,-1.0295302074,1.2097964496,,,,,,,,,,,,
NoisyDE,0.7325595717,-0.3250848292,-0.4968122173,1.9884218193,1.8577990761,1.7725922124,-0.966685952,-1.5886443264,,,,,,,,
NoisyDiscreteOnePlusOne,0.7531428339,0.0,0.0,0.0,,,,,,,,,,,,
NoisyOnePlusOne,0.945971663,-1.3628517589,0.4848789934,-3.4014712681,,,,,,,,,,,,
NoisyOnePlusOne,0.0,0.0,0.0,0.0,,,,,,,,,,,,
ORandomSearch,-0.4729858315,0.6814258794,-0.2424394967,1.700735634,,,,,,,,,,,,
OScrHammersleySearch,-0.9674215661,0.0,0.4307272993,0.8416212336,,,,,,,,,,,,
OnePlusOne,1.0082049151,-0.9099785499,-1.025147209,1.2046460074,,,,,,,,,,,,
Expand All @@ -112,17 +109,15 @@ ParametrizationDE,0.6007366746,0.1949881274,0.1103879146,3.4094354968,1.29215485
PolyCMA,2.3277181089,-1.1879729078,0.3734037502,-0.3130347979,,,,,,,,,,,,
Portfolio,1.3829941271,-0.318639364,-1.2206403488,1.7506860713,,,,,,,,,,,,
PortfolioDiscreteOnePlusOne,0.0,0.2169245995,-0.4007924638,1.4805504707,,,,,,,,,,,,
PortfolioNoisyDiscreteOnePlusOne,-0.581080115,0.8731348442,-0.4007924638,0.9105652814,,,,,,,,,,,,
PortfolioNoisyDiscreteOnePlusOne,0.0,0.2169245995,-0.4007924638,1.4805504707,,,,,,,,,,,,
PortfolioOptimisticNoisyDiscreteOnePlusOne,0.0,0.2169245995,-0.4007924638,1.4805504707,,,,,,,,,,,,
Powell,1.0,0.0,0.0,0.0,,,,,,,,,,,,
Powell,0.0,0.0,0.0,0.0,,,,,,,,,,,,
QORandomSearch,0.0051270781,-0.1202276702,-0.8069818786,2.871819395,,,,,,,,,,,,
QOScrHammersleySearch,-0.9674215661,0.0,0.4307272993,0.8416212336,,,,,,,,,,,,
QRBO,-1.0,0.0,0.5773502692,1.3763819205,,,,,,,,,,,,
QrDE,-1.2126139659,-0.1022395502,0.5946113194,0.5365857957,3.38269024,1.3718912761,-3.2278357447,-1.2566858857,,,,,,,,
RBO,0.0450718775,0.4964703331,-0.8734357921,0.2235333819,,,,,,,,,,,,
RCobyla,0.3839256844,-0.7978993518,0.1026429819,0.278528965,,,,,,,,,,,,
RPowell,0.4729858315,-0.6814258794,0.2424394967,-1.700735634,,,,,,,,,,,,
RSQP,0.52701,-0.91857,-0.24244,9.70074,,,,,,,,,,,,
RSQP,0.5270143425,-0.9185741644,-0.2424395202,9.700735504,,,,,,,,,,,,
RandomScaleRandomSearch,0.0606364451,-0.0547288191,-0.0616554051,0.0724509972,,,,,,,,,,,,
RandomScaleRandomSearchPlusMiddlePoint,0.0606364451,-0.0547288191,-0.0616554051,0.0724509972,,,,,,,,,,,,
RandomSearch,1.012515477,-0.9138691467,-1.0295302074,1.2097964496,,,,,,,,,,,,
Expand Down Expand Up @@ -157,22 +152,6 @@ TwoPointsDE,1.1400386808,0.3380024444,0.4755144618,2.6390460807,0.6911075733,1.1
WidePSO,-0.6812441446,0.5983676495,-0.8175040638,0.6316334032,2.5244820846,-0.5231002869,-0.9560245994,0.9725688328,-2.1777820207,0.072862412,,,,,,
Zero,0.0,0.0,0.0,0.0,,,,,,,,,,,,
cGA,0.0509603282,0.1315286387,-0.0393104602,0.7333300801,,,,,,,,,,,,
chainBOwithLHS,-0.1730911669,0.0515057177,-0.4634794355,1.0045589936,,,,,,,,,,,,
chainBOwithLHS30,0.6991689969,-1.3049222668,-0.2437593771,1.7486119476,,,,,,,,,,,,
chainBOwithLHSdim,-0.6957116825,-0.7918966559,-0.1188601118,1.0720844027,,,,,,,,,,,,
chainBOwithLHSsqrt,0.7107307877,-0.2588250957,0.5058275008,0.5603737131,,,,,,,,,,,,
chainBOwithMetaRecentering,-0.0654095715,0.5393771764,-0.5229115313,0.4111273229,,,,,,,,,,,,
chainBOwithMetaRecentering30,-0.7164989833,-0.2529034911,-0.9688200533,1.3895163915,,,,,,,,,,,,
chainBOwithMetaRecenteringdim,-0.4950377634,0.0,0.1853578422,0.3621806559,,,,,,,,,,,,
chainBOwithMetaRecenteringsqrt,2.7359628375,0.5468476926,0.5655551417,2.1203763212,,,,,,,,,,,,
chainBOwithMetaTuneRecentering,0.1685271298,1.1635635054,1.9259762446,2.5130632786,,,,,,,,,,,,
chainBOwithMetaTuneRecentering30,-0.8324268941,-0.2938227025,-1.125572941,1.6143369928,,,,,,,,,,,,
chainBOwithMetaTuneRecenteringdim,-0.6772164449,0.0,0.2535713196,0.4954666378,,,,,,,,,,,,
chainBOwithMetaTuneRecenteringsqrt,2.1335889648,0.4495330997,0.4601737354,2.5858509526,,,,,,,,,,,,
chainBOwithR,0.6308912623,-0.4085254501,0.4143342909,-1.3521583864,,,,,,,,,,,,
chainBOwithR30,0.0051270781,-0.1202276702,-0.8069818786,2.871819395,,,,,,,,,,,,
chainBOwithRdim,0.0051270781,-0.1202276702,-0.8069818786,2.871819395,,,,,,,,,,,,
chainBOwithRsqrt,-0.1142333045,-0.2595936976,-1.0400374779,3.2851862192,,,,,,,,,,,,
chainCMAPowell,0.7531428339,-1.5347405243,0.0051272063,-0.1202321788,,,,,,,,,,,,
chainCMASQP,0.7531428339,-1.5347405243,0.0051272063,-0.1202321788,,,,,,,,,,,,
chainCMAwithLHS,-0.1333838812,0.1800143959,-0.4233788987,0.7827612496,,,,,,,,,,,,
Expand Down
25 changes: 25 additions & 0 deletions nevergrad/optimization/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import numpy as np
import nevergrad.common.typing as tp
from nevergrad.common import testing
import nevergrad as ng
from . import optimizerlib
from . import experimentalvariants as xpvariants
from . import base
Expand Down Expand Up @@ -89,6 +90,8 @@ def test_tell_types(value: tp.Any, error: bool) -> None:

def test_base_optimizer() -> None:
zeroptim = xpvariants.Zero(parametrization=2, budget=4, num_workers=1)
# add descriptor to replicate old behavior, returning pessimistic best
zeroptim.parametrization.descriptors.deterministic_function = False
representation = repr(zeroptim)
expected = "parametrization=Array{(2,)}"
assert expected in representation, f"Unexpected representation: {representation}"
Expand Down Expand Up @@ -152,3 +155,25 @@ def test_naming() -> None:
np.testing.assert_equal(
repr(opt), f"Instance of BlubluOptimizer(parametrization={instru_str}, budget=4, num_workers=1)"
)


class MinStorageFunc:
"""Stores the minimum value obtained so far"""

def __init__(self) -> None:
self.min_loss = float("inf")

def __call__(self, score: int) -> float:
self.min_loss = min(score, self.min_loss)
return score


def test_recommendation_correct() -> None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A possible test would be the example in the issue.
That said, I find the fix quite cool and convincing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually that is the same test, just simplified a bit, but it does exactly the same

# Run this several times to debug:
# pytest nevergrad/optimization/test_base.py::test_recommendation_correct --count=20 --exitfirst
func = MinStorageFunc()
choice_size = 20
param = ng.p.Choice(range(choice_size)).set_name(f"Choice{choice_size}")
optimizer = optimizerlib.OnePlusOne(parametrization=param, budget=300, num_workers=1)
recommendation = optimizer.minimize(func)
assert func.min_loss == recommendation.value
1 change: 1 addition & 0 deletions nevergrad/optimization/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def test_value_and_point() -> None:
np.testing.assert_almost_equal(v.variance, 0.3536, decimal=4)
assert v.optimistic_confidence_bound < v.pessimistic_confidence_bound
assert v.get_estimation("optimistic") < v.get_estimation("pessimistic")
assert v.get_estimation("minimum") == 3
np.testing.assert_raises(NotImplementedError, v.get_estimation, "blublu")
repr(v)
assert v.parameter.value == 12
Expand Down
4 changes: 4 additions & 0 deletions nevergrad/optimization/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class MultiValue:
def __init__(self, parameter: p.Parameter, y: float, *, reference: p.Parameter) -> None:
self.count = 1
self.mean = y
self._minimum = y
self.square = y * y
# TODO May be safer to use a default variance which depends on y for scale invariance?
self.variance = 1.0e6
Expand All @@ -61,6 +62,8 @@ def get_estimation(self, name: str) -> float:
return self.optimistic_confidence_bound
elif name == "pessimistic":
return self.pessimistic_confidence_bound
elif name == "minimum":
return self._minimum
elif name == "average":
return self.mean
else:
Expand All @@ -74,6 +77,7 @@ def add_evaluation(self, y: float) -> None:
y: float
the new evaluation
"""
self._minimum = min(self._minimum, y)
self.mean = (self.count * self.mean + y) / float(self.count + 1)
self.square = (self.count * self.square + y * y) / float(self.count + 1)
self.square = max(self.square, self.mean ** 2)
Expand Down