Unverified Commit 580c5979 authored by 98may's avatar 98may Committed by GitHub
Browse files

DNGO tuner (#3479)


Co-authored-by: default avatarYuge Zhang <Yuge.Zhang@microsoft.com>
parent 9b0bc378
...@@ -11,3 +11,6 @@ statsmodels==0.12.0 ...@@ -11,3 +11,6 @@ statsmodels==0.12.0
# PPOTuner # PPOTuner
gym gym
# DNGO
pybnn
...@@ -45,7 +45,8 @@ Currently, we support the following algorithms: ...@@ -45,7 +45,8 @@ Currently, we support the following algorithms:
- PPO Tuner is a Reinforcement Learning tuner based on PPO algorithm. `Reference Paper <https://arxiv.org/abs/1707.06347>`__ - PPO Tuner is a Reinforcement Learning tuner based on PPO algorithm. `Reference Paper <https://arxiv.org/abs/1707.06347>`__
* - `PBT Tuner <#PBTTuner>`__ * - `PBT Tuner <#PBTTuner>`__
- PBT Tuner is a simple asynchronous optimization algorithm which effectively utilizes a fixed computational budget to jointly optimize a population of models and their hyperparameters to maximize performance. `Reference Paper <https://arxiv.org/abs/1711.09846v1>`__ - PBT Tuner is a simple asynchronous optimization algorithm which effectively utilizes a fixed computational budget to jointly optimize a population of models and their hyperparameters to maximize performance. `Reference Paper <https://arxiv.org/abs/1711.09846v1>`__
* - `DNGO Tuner <#DNGOTuner>`__
- Use of neural networks as an alternative to GPs to model distributions over functions in bayesian optimization.
Usage of Built-in Tuners Usage of Built-in Tuners
------------------------ ------------------------
...@@ -574,6 +575,41 @@ Population Based Training (PBT) bridges and extends parallel search methods and ...@@ -574,6 +575,41 @@ Population Based Training (PBT) bridges and extends parallel search methods and
Note that, to use this tuner, your trial code should be modified accordingly, please refer to `the document of PBTTuner <./PBTTuner.rst>`__ for details. Note that, to use this tuner, your trial code should be modified accordingly, please refer to `the document of PBTTuner <./PBTTuner.rst>`__ for details.
DNGO Tuner
^^^^^^^^^^
..
Built-in Tuner Name: **DNGOTuner**
DNGO advisor requires `pybnn`, which can be installed with the following command.
.. code-block:: bash
pip install nni[DNGO]
**Suggested scenario**
Applicable to large scale hyperparameter optimization. Bayesian optimization that rapidly finds competitive models on benchmark object recognition tasks using convolutional networks, and image caption generation using neural language models.
**classArgs requirements:**
* **optimize_mode** (*'maximize' or 'minimize'*\ ) - If 'maximize', the tuner will target to maximize metrics. If 'minimize', the tuner will target to minimize metrics.
* **sample_size** (*int, default = 1000*) - Number of samples to select in each iteration. The best one will be picked from the samples as the next trial.
* **trials_per_update** (*int, default = 20*) - Number of trials to collect before updating the model.
* **num_epochs_per_training** (*int, default = 500*) - Number of epochs to train DNGO model.
**Usage example**
.. code-block:: yaml
# config.yml
tuner:
builtinTunerName: DNGOTuner
classArgs:
optimize_mode: maximize
**Reference and Feedback** **Reference and Feedback**
------------------------------ ------------------------------
......
...@@ -243,13 +243,25 @@ Search Space Types Supported by Each Tuner ...@@ -243,13 +243,25 @@ Search Space Types Supported by Each Tuner
- -
- -
- -
* - DNGO Tuner
- :raw-html:`&#10003;`
-
- :raw-html:`&#10003;`
- :raw-html:`&#10003;`
- :raw-html:`&#10003;`
- :raw-html:`&#10003;`
- :raw-html:`&#10003;`
-
-
-
-
Known Limitations: Known Limitations:
* *
GP Tuner and Metis Tuner support only **numerical values** in search space (\ ``choice`` type values can be no-numerical with other tuners, e.g. string values). Both GP Tuner and Metis Tuner use Gaussian Process Regressor(GPR). GPR make predictions based on a kernel function and the 'distance' between different points, it's hard to get the true distance between no-numerical values. GP Tuner, Metis Tuner and DNGO tuner support only **numerical values** in search space (\ ``choice`` type values can be no-numerical with other tuners, e.g. string values). Both GP Tuner and Metis Tuner use Gaussian Process Regressor(GPR). GPR make predictions based on a kernel function and the 'distance' between different points, it's hard to get the true distance between no-numerical values.
* *
Note that for nested search space: Note that for nested search space:
......
import logging
import numpy as np
import torch
import nni.parameter_expressions as parameter_expressions
from nni import ClassArgsValidator
from nni.tuner import Tuner
from pybnn import DNGO
from torch.distributions import Normal
_logger = logging.getLogger(__name__)
def _random_config(search_space, random_state):
chosen_config = {}
for key, val in search_space.items():
if val['_type'] == 'choice':
choices = val['_value']
index = random_state.randint(len(choices))
if all([isinstance(c, (int, float)) for c in choices]):
chosen_config[key] = choices[index]
else:
raise ValueError('Choices with type other than int and float is not supported.')
elif val['_type'] == 'uniform':
chosen_config[key] = random_state.uniform(val['_value'][0], val['_value'][1])
elif val['_type'] == 'randint':
chosen_config[key] = random_state.randint(
val['_value'][0], val['_value'][1])
elif val['_type'] == 'quniform':
chosen_config[key] = parameter_expressions.quniform(
val['_value'][0], val['_value'][1], val['_value'][2], random_state)
elif val['_type'] == 'loguniform':
chosen_config[key] = parameter_expressions.loguniform(
val['_value'][0], val['_value'][1], random_state)
elif val['_type'] == 'qloguniform':
chosen_config[key] = parameter_expressions.qloguniform(
val['_value'][0], val['_value'][1], val['_value'][2], random_state)
else:
raise ValueError('Unknown key %s and value %s' % (key, val))
return chosen_config
class DngoTuner(Tuner):
def __init__(self, optimize_mode='maximize', sample_size=1000, trials_per_update=20, num_epochs_per_training=500):
self.searchspace_json = None
self.random_state = None
self.model = DNGO(do_mcmc=False, num_epochs=num_epochs_per_training)
self._model_initialized = False
self.sample_size = sample_size
self.trials_per_update = trials_per_update
self.optimize_mode = optimize_mode
self.x = []
self.y = []
def receive_trial_result(self, parameter_id, parameters, value, **kwargs):
self.x.append(parameters)
self.y.append(self._get_default_value(value))
if len(self.y) % self.trials_per_update == 0:
self._update_model()
def generate_parameters(self, parameter_id, **kwargs):
if not self._model_initialized:
return _random_config(self.searchspace_json, self.random_state)
else:
# random samples and pick best with model
candidate_x = [_random_config(self.searchspace_json, self.random_state) for _ in range(self.sample_size)]
x_test = np.array([np.array(list(xi.values())) for xi in candidate_x])
m, v = self.model.predict(x_test)
mean = torch.Tensor(m)
sigma = torch.Tensor(v)
u = (mean - torch.Tensor([0.95]).expand_as(mean)) / sigma
normal = Normal(torch.zeros_like(u), torch.ones_like(u))
ucdf = normal.cdf(u)
updf = torch.exp(normal.log_prob(u))
ei = sigma * (updf + u * ucdf)
if self.optimize_mode == 'maximize':
ind = torch.argmax(ei)
else:
ind = torch.argmin(ei)
new_x = candidate_x[ind]
return new_x
def update_search_space(self, search_space):
self.searchspace_json = search_space
self.random_state = np.random.RandomState()
def import_data(self, data):
for d in data:
self.x.append(d['parameter'])
self.y.append(self._get_default_value(d['value']))
self._update_model()
def _update_model(self):
_logger.info('Updating model on %d samples', len(self.x))
x_arr = []
for x in self.x:
x_arr.append([x[k] for k in sorted(x.keys())])
self.model.train(np.array(x_arr), np.array(self.y), do_optimize=True)
self._model_initialized = True
def _get_default_value(self, value):
if isinstance(value, dict) and 'default' in value:
return value['default']
elif isinstance(value, float):
return value
else:
raise ValueError(f'Unsupported value: {value}')
class DNGOClassArgsValidator(ClassArgsValidator):
# DNGO tuner do not have much input arg, so the validation is actually hardly used
def validate_class_args(self, **kwargs):
pass
...@@ -76,3 +76,7 @@ tuners: ...@@ -76,3 +76,7 @@ tuners:
classArgsValidator: nni.algorithms.hpo.regularized_evolution_tuner.EvolutionClassArgsValidator classArgsValidator: nni.algorithms.hpo.regularized_evolution_tuner.EvolutionClassArgsValidator
className: nni.algorithms.hpo.regularized_evolution_tuner.RegularizedEvolutionTuner className: nni.algorithms.hpo.regularized_evolution_tuner.RegularizedEvolutionTuner
source: nni source: nni
- builtinName: DNGOTuner
classArgsValidator: nni.algorithms.hpo.dngo_tuner.DNGOClassArgsValidator
className: nni.algorithms.hpo.dngo_tuner.DNGOTuner
source: nni
...@@ -161,7 +161,7 @@ stages: ...@@ -161,7 +161,7 @@ stages:
- script: | - script: |
set -e set -e
python -m pip install -r dependencies/recommended.txt python -m pip install -r dependencies/recommended.txt
python -m pip install -e .[SMAC,BOHB,PPOTuner] python -m pip install -e .[SMAC,BOHB,PPOTuner,DNGO]
displayName: Install extra dependencies displayName: Install extra dependencies
# Need del later # Need del later
...@@ -246,7 +246,7 @@ stages: ...@@ -246,7 +246,7 @@ stages:
- script: | - script: |
set -e set -e
python -m pip install -r dependencies/recommended_legacy.txt python -m pip install -r dependencies/recommended_legacy.txt
python -m pip install -e .[SMAC,BOHB,PPOTuner] python -m pip install -e .[SMAC,BOHB,PPOTuner,DNGO]
displayName: Install extra dependencies displayName: Install extra dependencies
# Need del later # Need del later
...@@ -335,7 +335,7 @@ stages: ...@@ -335,7 +335,7 @@ stages:
- script: | - script: |
set -e set -e
python -m pip install -r dependencies/recommended.txt python -m pip install -r dependencies/recommended.txt
python -m pip install -e .[SMAC,BOHB,PPOTuner] python -m pip install -e .[SMAC,BOHB,PPOTuner,DNGO]
displayName: Install extra dependencies displayName: Install extra dependencies
# Need del later # Need del later
...@@ -398,6 +398,7 @@ stages: ...@@ -398,6 +398,7 @@ stages:
- script: | - script: |
python -m pip install -r dependencies/recommended.txt python -m pip install -r dependencies/recommended.txt
python -m pip install -e .[DNGO]
displayName: Install extra dependencies displayName: Install extra dependencies
# Need del later # Need del later
......
...@@ -35,6 +35,7 @@ jobs: ...@@ -35,6 +35,7 @@ jobs:
python3 -m pip install keras==2.1.6 python3 -m pip install keras==2.1.6
python3 -m pip install tensorflow==2.3.1 tensorflow-estimator==2.3.0 python3 -m pip install tensorflow==2.3.1 tensorflow-estimator==2.3.0
python3 -m pip install thop python3 -m pip install thop
python3 -m pip install pybnn
python3 -m pip install tianshou>=0.4.1 gym python3 -m pip install tianshou>=0.4.1 gym
sudo apt-get install swig -y sudo apt-get install swig -y
displayName: Install extra dependencies displayName: Install extra dependencies
......
...@@ -30,6 +30,7 @@ jobs: ...@@ -30,6 +30,7 @@ jobs:
python -m pip install torch==1.6.0 torchvision==0.7.0 -f https://download.pytorch.org/whl/torch_stable.html python -m pip install torch==1.6.0 torchvision==0.7.0 -f https://download.pytorch.org/whl/torch_stable.html
python -m pip install 'pytorch-lightning>=1.1.1' python -m pip install 'pytorch-lightning>=1.1.1'
python -m pip install tensorflow==2.3.1 tensorflow-estimator==2.3.0 python -m pip install tensorflow==2.3.1 tensorflow-estimator==2.3.0
python -m pip install pybnn
python -m pip install tianshou>=0.4.1 gym python -m pip install tianshou>=0.4.1 gym
displayName: Install extra dependencies displayName: Install extra dependencies
......
...@@ -88,7 +88,8 @@ def _setup(): ...@@ -88,7 +88,8 @@ def _setup():
extras_require = { extras_require = {
'SMAC': _read_requirements_txt('dependencies/required_extra.txt', 'SMAC'), 'SMAC': _read_requirements_txt('dependencies/required_extra.txt', 'SMAC'),
'BOHB': _read_requirements_txt('dependencies/required_extra.txt', 'BOHB'), 'BOHB': _read_requirements_txt('dependencies/required_extra.txt', 'BOHB'),
'PPOTuner': _read_requirements_txt('dependencies/required_extra.txt', 'PPOTuner') 'PPOTuner': _read_requirements_txt('dependencies/required_extra.txt', 'PPOTuner'),
'DNGO': _read_requirements_txt('dependencies/required_extra.txt', 'DNGO'),
}, },
setup_requires = ['requests'], setup_requires = ['requests'],
......
...@@ -12,6 +12,7 @@ from collections import deque ...@@ -12,6 +12,7 @@ from collections import deque
from unittest import TestCase, main from unittest import TestCase, main
from nni.algorithms.hpo.batch_tuner import BatchTuner from nni.algorithms.hpo.batch_tuner import BatchTuner
from nni.algorithms.hpo.dngo_tuner import DngoTuner
from nni.algorithms.hpo.evolution_tuner import EvolutionTuner from nni.algorithms.hpo.evolution_tuner import EvolutionTuner
from nni.algorithms.hpo.gp_tuner import GPTuner from nni.algorithms.hpo.gp_tuner import GPTuner
from nni.algorithms.hpo.gridsearch_tuner import GridSearchTuner from nni.algorithms.hpo.gridsearch_tuner import GridSearchTuner
...@@ -388,6 +389,12 @@ class BuiltinTunersTestCase(TestCase): ...@@ -388,6 +389,12 @@ class BuiltinTunersTestCase(TestCase):
)) ))
self.import_data_test_for_pbt() self.import_data_test_for_pbt()
def test_dngo(self):
tuner_fn = lambda: DngoTuner(trials_per_update=100, num_epochs_per_training=1)
self.search_space_test_all(tuner_fn, fail_types=["choice_str", "choice_mixed",
"normal", "lognormal", "qnormal", "qlognormal"])
self.import_data_test(tuner_fn, stype='choice_num')
def tearDown(self): def tearDown(self):
file_list = glob.glob("smac3*") + ["param_config_space.pcs", "scenario.txt", "model_path"] file_list = glob.glob("smac3*") + ["param_config_space.pcs", "scenario.txt", "model_path"]
for file in file_list: for file in file_list:
......
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