Unverified Commit 98f66f76 authored by Yuge Zhang's avatar Yuge Zhang Committed by GitHub
Browse files

Strengthen builtin tuners UT and fix hyperopt randint (#1959)

* strengthen UT and fix hyperopt randint

* fix a ridiculous mistake
parent a922f9f0
...@@ -102,6 +102,9 @@ class GridSearchTuner(Tuner): ...@@ -102,6 +102,9 @@ class GridSearchTuner(Tuner):
""" """
Parse type of randint parameter and return a list Parse type of randint parameter and return a list
""" """
if param_value[0] >= param_value[1]:
raise ValueError("Randint should contain at least 1 candidate, but [%s, %s) contains none.",
param_value[0], param_value[1])
return np.arange(param_value[0], param_value[1]).tolist() return np.arange(param_value[0], param_value[1]).tolist()
def _expand_parameters(self, para): def _expand_parameters(self, para):
......
...@@ -118,6 +118,8 @@ def json2vals(in_x, vals, out_y, name=NodeType.ROOT): ...@@ -118,6 +118,8 @@ def json2vals(in_x, vals, out_y, name=NodeType.ROOT):
vals[NodeType.VALUE], vals[NodeType.VALUE],
out_y, out_y,
name=name + '[%d]' % _index) name=name + '[%d]' % _index)
if _type == 'randint':
out_y[name] -= in_x[NodeType.VALUE][0]
else: else:
for key in in_x.keys(): for key in in_x.keys():
json2vals(in_x[key], vals[key], out_y, json2vals(in_x[key], vals[key], out_y,
......
{ {
"choice_str": { "choice_str": {
"_type": "choice", "_type": "choice",
"_value": ["cat", "dog", "elephant", "cow", "sheep", "panda"], "_value": ["cat", "dog", "elephant", "cow", "sheep", "panda"]
"fail": ["metis", "gp"]
}, },
"choice_int": { "choice_int": {
"_type": "choice", "_type": "choice",
...@@ -10,8 +9,7 @@ ...@@ -10,8 +9,7 @@
}, },
"choice_mixed": { "choice_mixed": {
"_type": "choice", "_type": "choice",
"_value": [0.3, "cat", 1, null], "_value": [0.3, "cat", 1, null]
"fail": ["metis", "gp"]
}, },
"choice_float": { "choice_float": {
"_type": "choice", "_type": "choice",
......
...@@ -5,6 +5,7 @@ import glob ...@@ -5,6 +5,7 @@ import glob
import json import json
import logging import logging
import os import os
import random
import shutil import shutil
import sys import sys
from unittest import TestCase, main from unittest import TestCase, main
...@@ -15,6 +16,7 @@ from nni.gp_tuner.gp_tuner import GPTuner ...@@ -15,6 +16,7 @@ from nni.gp_tuner.gp_tuner import GPTuner
from nni.gridsearch_tuner.gridsearch_tuner import GridSearchTuner from nni.gridsearch_tuner.gridsearch_tuner import GridSearchTuner
from nni.hyperopt_tuner.hyperopt_tuner import HyperoptTuner from nni.hyperopt_tuner.hyperopt_tuner import HyperoptTuner
from nni.metis_tuner.metis_tuner import MetisTuner from nni.metis_tuner.metis_tuner import MetisTuner
try: try:
from nni.smac_tuner.smac_tuner import SMACTuner from nni.smac_tuner.smac_tuner import SMACTuner
except ImportError: except ImportError:
...@@ -34,20 +36,28 @@ class BuiltinTunersTestCase(TestCase): ...@@ -34,20 +36,28 @@ class BuiltinTunersTestCase(TestCase):
- [X] generate_multiple_parameters - [X] generate_multiple_parameters
- [ ] import_data - [ ] import_data
- [ ] trial_end - [ ] trial_end
- [ ] receive_trial_result - [x] receive_trial_result
""" """
def setUp(self):
self.test_round = 3
self.params_each_round = 50
self.exhaustive = False
def search_space_test_one(self, tuner_factory, search_space): def search_space_test_one(self, tuner_factory, search_space):
tuner = tuner_factory() tuner = tuner_factory()
self.assertIsInstance(tuner, Tuner) self.assertIsInstance(tuner, Tuner)
tuner.update_search_space(search_space) tuner.update_search_space(search_space)
parameters = tuner.generate_multiple_parameters(list(range(0, 50))) for i in range(self.test_round):
logger.info(parameters) parameters = tuner.generate_multiple_parameters(list(range(i * self.params_each_round,
self.check_range(parameters, search_space) (i + 1) * self.params_each_round)))
if not parameters: # TODO: not strict logger.debug(parameters)
raise ValueError("No parameters generated") self.check_range(parameters, search_space)
return parameters for k in range(min(len(parameters), self.params_each_round)):
tuner.receive_trial_result(self.params_each_round * i + k, parameters[k], random.uniform(-100, 100))
if not parameters and not self.exhaustive:
raise ValueError("No parameters generated")
def check_range(self, generated_params, search_space): def check_range(self, generated_params, search_space):
EPS = 1E-6 EPS = 1E-6
...@@ -91,7 +101,8 @@ class BuiltinTunersTestCase(TestCase): ...@@ -91,7 +101,8 @@ class BuiltinTunersTestCase(TestCase):
for layer_name in item["_value"].keys(): for layer_name in item["_value"].keys():
self.assertIn(v[layer_name]["chosen_layer"], item["layer_choice"]) self.assertIn(v[layer_name]["chosen_layer"], item["layer_choice"])
def search_space_test_all(self, tuner_factory, supported_types=None, ignore_types=None): def search_space_test_all(self, tuner_factory, supported_types=None, ignore_types=None, fail_types=None):
# Three types: 1. supported; 2. ignore; 3. fail.
# NOTE(yuge): ignore types # NOTE(yuge): ignore types
# Supported types are listed in the table. They are meant to be supported and should be correct. # Supported types are listed in the table. They are meant to be supported and should be correct.
# Other than those, all the rest are "unsupported", which are expected to produce ridiculous results # Other than those, all the rest are "unsupported", which are expected to produce ridiculous results
...@@ -103,16 +114,18 @@ class BuiltinTunersTestCase(TestCase): ...@@ -103,16 +114,18 @@ class BuiltinTunersTestCase(TestCase):
if supported_types is None: if supported_types is None:
supported_types = ["choice", "randint", "uniform", "quniform", "loguniform", "qloguniform", supported_types = ["choice", "randint", "uniform", "quniform", "loguniform", "qloguniform",
"normal", "qnormal", "lognormal", "qlognormal"] "normal", "qnormal", "lognormal", "qlognormal"]
if fail_types is None:
fail_types = []
if ignore_types is None:
ignore_types = []
full_supported_search_space = dict() full_supported_search_space = dict()
for single in search_space_all: for single in search_space_all:
single_keyword = single.split("_")
space = search_space_all[single] space = search_space_all[single]
expected_fail = not any([t in single_keyword for t in supported_types]) or "fail" in single_keyword if any(single.startswith(t) for t in ignore_types):
if ignore_types is not None and any([t in ignore_types for t in single_keyword]):
continue continue
if "fail" in space: expected_fail = not any(single.startswith(t) for t in supported_types) or \
if self._testMethodName.split("_", 1)[1] in space.pop("fail"): any(single.startswith(t) for t in fail_types) or \
expected_fail = True "fail" in single # name contains fail (fail on all)
single_search_space = {single: space} single_search_space = {single: space}
if not expected_fail: if not expected_fail:
# supports this key # supports this key
...@@ -129,11 +142,14 @@ class BuiltinTunersTestCase(TestCase): ...@@ -129,11 +142,14 @@ class BuiltinTunersTestCase(TestCase):
self.search_space_test_one(tuner_factory, full_supported_search_space) self.search_space_test_one(tuner_factory, full_supported_search_space)
def test_grid_search(self): def test_grid_search(self):
self.exhaustive = True
self.search_space_test_all(lambda: GridSearchTuner(), self.search_space_test_all(lambda: GridSearchTuner(),
supported_types=["choice", "randint", "quniform"]) supported_types=["choice", "randint", "quniform"])
def test_tpe(self): def test_tpe(self):
self.search_space_test_all(lambda: HyperoptTuner("tpe")) self.search_space_test_all(lambda: HyperoptTuner("tpe"),
ignore_types=["uniform_equal", "qloguniform_equal", "loguniform_equal", "quniform_clip_2"])
# NOTE: types are ignored because `tpe.py line 465, in adaptive_parzen_normal assert prior_sigma > 0`
def test_random_search(self): def test_random_search(self):
self.search_space_test_all(lambda: HyperoptTuner("random_search")) self.search_space_test_all(lambda: HyperoptTuner("random_search"))
...@@ -148,6 +164,7 @@ class BuiltinTunersTestCase(TestCase): ...@@ -148,6 +164,7 @@ class BuiltinTunersTestCase(TestCase):
supported_types=["choice", "randint", "uniform", "quniform", "loguniform"]) supported_types=["choice", "randint", "uniform", "quniform", "loguniform"])
def test_batch(self): def test_batch(self):
self.exhaustive = True
self.search_space_test_all(lambda: BatchTuner(), self.search_space_test_all(lambda: BatchTuner(),
supported_types=["choice"]) supported_types=["choice"])
...@@ -156,14 +173,18 @@ class BuiltinTunersTestCase(TestCase): ...@@ -156,14 +173,18 @@ class BuiltinTunersTestCase(TestCase):
self.search_space_test_all(lambda: EvolutionTuner(population_size=100)) self.search_space_test_all(lambda: EvolutionTuner(population_size=100))
def test_gp(self): def test_gp(self):
self.test_round = 1 # NOTE: GP tuner got hanged for multiple testing round
self.search_space_test_all(lambda: GPTuner(), self.search_space_test_all(lambda: GPTuner(),
supported_types=["choice", "randint", "uniform", "quniform", "loguniform", supported_types=["choice", "randint", "uniform", "quniform", "loguniform",
"qloguniform"], "qloguniform"],
ignore_types=["normal", "lognormal", "qnormal", "qlognormal"]) ignore_types=["normal", "lognormal", "qnormal", "qlognormal"],
fail_types=["choice_str", "choice_mixed"])
def test_metis(self): def test_metis(self):
self.test_round = 1 # NOTE: Metis tuner got hanged for multiple testing round
self.search_space_test_all(lambda: MetisTuner(), self.search_space_test_all(lambda: MetisTuner(),
supported_types=["choice", "randint", "uniform", "quniform"]) supported_types=["choice", "randint", "uniform", "quniform"],
fail_types=["choice_str", "choice_mixed"])
def test_networkmorphism(self): def test_networkmorphism(self):
pass pass
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment