Skip to content
This repository has been archived by the owner on Sep 18, 2024. It is now read-only.

Add Metis Tuner #534

Merged
merged 37 commits into from
Jan 7, 2019
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
20bf28b
update readme in ga_squad
xuehui1991 Sep 20, 2018
14d8f60
update readme
xuehui1991 Sep 20, 2018
d842121
fix typo
xuehui1991 Sep 20, 2018
0d67525
Update README.md
xuehui1991 Sep 20, 2018
b52854a
Update README.md
xuehui1991 Sep 20, 2018
374491d
Update README.md
xuehui1991 Sep 20, 2018
c805d2a
update readme
xuehui1991 Sep 20, 2018
c825d49
Merge branch 'update_ga_squad' of https://github.com/xuehui1991/nni i…
xuehui1991 Sep 20, 2018
6fdde2c
Merge branch 'master' of https://github.com/Microsoft/nni
xuehui1991 Sep 25, 2018
d41a8b0
Merge branch 'master' of https://github.com/Microsoft/nni
xuehui1991 Sep 25, 2018
cc765b8
Merge branch 'master' into update_ga_squad
xuehui1991 Sep 26, 2018
ef5eba8
Merge branch 'update_ga_squad' of https://github.com/xuehui1991/nni i…
xuehui1991 Sep 26, 2018
8ff4508
Merge branch 'xuehui1991-update_ga_squad'
xuehui1991 Sep 26, 2018
629222f
update tuner docs about Anneal
xuehui1991 Dec 4, 2018
ef74ffa
update
xuehui1991 Dec 4, 2018
22b3add
fix path
xuehui1991 Dec 4, 2018
c97fd11
update reference
xuehui1991 Dec 4, 2018
0afbffd
fix bug in config file
xuehui1991 Dec 5, 2018
7022e6e
Merge remote-tracking branch 'upstream/master'
xuehui1991 Dec 14, 2018
770677c
update nni_arch_overview.png
xuehui1991 Dec 14, 2018
e002331
update
xuehui1991 Dec 14, 2018
d066896
update
xuehui1991 Dec 14, 2018
096f69b
update
xuehui1991 Dec 14, 2018
47218a7
Merge remote-tracking branch 'upstream/master'
xuehui1991 Dec 27, 2018
54455c7
add metis tuner code
xuehui1991 Dec 27, 2018
8eb5a9c
1. fix bug about import 2.update other sdk file
xuehui1991 Dec 28, 2018
97d68f2
add auto-gbdt-example and remove unused code
xuehui1991 Dec 28, 2018
d308ecc
add metis_tuner into README
xuehui1991 Dec 28, 2018
578f6e2
update the README
xuehui1991 Dec 28, 2018
beb07f3
update README | remove unused variable
xuehui1991 Jan 2, 2019
13b7ca2
fix typo
xuehui1991 Jan 2, 2019
da7f523
add sklearn into requirments
xuehui1991 Jan 2, 2019
ac557bd
Update src/sdk/pynni/nni/metis_tuner/metis_tuner.py
PurityFan Jan 4, 2019
dff4e22
Update docs/HowToChooseTuner.md
PurityFan Jan 4, 2019
32e9166
Update docs/HowToChooseTuner.md
PurityFan Jan 4, 2019
46a9001
fix typo | add more comments
xuehui1991 Jan 4, 2019
4255e43
Merge branch 'metis_branch' of https://github.com/xuehui1991/nni into…
xuehui1991 Jan 4, 2019
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
42 changes: 41 additions & 1 deletion docs/HowToChooseTuner.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ For now, NNI has supported the following tuner algorithms. Note that NNI install
- [Grid Search](#Grid)
- [Hyperband](#Hyperband)
- [Network Morphism](#NetworkMorphism) (require pyTorch)
- [Metis Tuner](#MetisTuner) (require sklearn)


## Supported tuner algorithms

Expand Down Expand Up @@ -178,7 +180,7 @@ _Usage_:
<a name="NetworkMorphism"></a>
**Network Morphism**

[Network Morphism](7) provides functions to automatically search for architecture of deep learning models. Every child network inherits the knowledge from its parent network and morphs into diverse types of networks, including changes of depth, width and skip-connection. Next, it estimates the value of child network using the history architecture and metric pairs. Then it selects the most promising one to train. More detail can be referred to [here](../src/sdk/pynni/nni/networkmorphism_tuner/README.md).
[Network Morphism][7] provides functions to automatically search for architecture of deep learning models. Every child network inherits the knowledge from its parent network and morphs into diverse types of networks, including changes of depth, width and skip-connection. Next, it estimates the value of child network using the history architecture and metric pairs. Then it selects the most promising one to train. More detail can be referred to [here](../src/sdk/pynni/nni/networkmorphism_tuner/README.md).

_Installation_:
NetworkMorphism requires [pyTorch](https://pytorch.org/get-started/locally), so users should install it first.
Expand All @@ -205,6 +207,43 @@ _Usage_:
```


<a name="MetisTuner"></a>
**Metis Tuner**

[Metis][10] offers the following benefits when it comes to tuning parameters:
While most tools predicts only the optimal configuration, Metis gives you two outputs: (a) current prediction of optimal configuration, and (b) suggestion for the next trial. No more guess work!
xuehui1991 marked this conversation as resolved.
Show resolved Hide resolved

While most tools assume training datasets do not have noisy data, Metis actually tells you if you need to re-sample a particular hyper-parameter.

While most tools have problems of being exploitation-heavy, Metis' search strategy balances exploration, exploitation, and (optional) re-sampling.

Metis belongs to the class of sequential model-based optimization (SMBO), and it is based on the Bayesian Optimization framework. To model the parameter-vs-performance space, Metis uses both Gaussian Process and GMM. Since each trial can impose a high time cost, Metis heavily trades inference computations with naive trial. At each iteration, Metis does two tasks:
xuehui1991 marked this conversation as resolved.
Show resolved Hide resolved
It finds the global optimal point in the Gaussian Process space. This point represents the optimal configuration.
xuehui1991 marked this conversation as resolved.
Show resolved Hide resolved
It identifies the next hyper-parameter candidate. This is achieved by inferring the potential information gain of exploration, exploitation, and re-sampling.
xuehui1991 marked this conversation as resolved.
Show resolved Hide resolved

Note that the only acceptable types of search space are `choice`, `quniform`, `randint`.

More details can be found in our paper: https://www.microsoft.com/en-us/research/publication/metis-robustly-tuning-tail-latencies-cloud-systems/


_Installation_:
NetworkMorphism requires [sklearn](https://scikit-learn.org/), so users should install it first.
xuehui1991 marked this conversation as resolved.
Show resolved Hide resolved


_Suggested scenario_:
Similar to TPE and SMAC, Metis is a black-box tuner. If your system takes a long time to finish each trial, Metis is more favorable than other approaches such as random search. Furthermore, Metis provides guidance on the subsequent trial. Here is an [example](../examples/trials/auto-gbdt/search_space_metis.json) about the use of Metis.

_Usage_:
```yaml
# config.yaml
tuner:
builtinTunerName: MetisTuner
classArgs:
#choice: maximize, minimize
optimize_mode: maximize
```


# How to use Assessor that NNI supports?

For now, NNI has supported the following assessor algorithms.
Expand Down Expand Up @@ -273,3 +312,4 @@ _Usage_:
[7]: https://arxiv.org/abs/1806.10282
[8]: https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/46180.pdf
[9]: http://aad.informatik.uni-freiburg.de/papers/15-IJCAI-Extrapolation_of_Learning_Curves.pdf
[10]:https://www.microsoft.com/en-us/research/publication/metis-robustly-tuning-tail-latencies-cloud-systems/
21 changes: 21 additions & 0 deletions examples/trials/auto-gbdt/config_metis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
authorName: default
experimentName: example_auto-gbdt-metis
trialConcurrency: 1
maxExecDuration: 10h
maxTrialNum: 10
#choice: local, remote, pai
trainingServicePlatform: local
searchSpacePath: search_space_metis.json
#choice: true, false
useAnnotation: false
tuner:
#choice: TPE, Random, Anneal, Evolution, BatchTuner
#SMAC (SMAC should be installed through nnictl)
builtinTunerName: MetisTuner
classArgs:
#choice: maximize, minimize
optimize_mode: minimize
trial:
command: python3 main.py
codeDir: .
gpuNum: 0
5 changes: 5 additions & 0 deletions examples/trials/auto-gbdt/search_space_metis.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"num_leaves":{"_type":"choice","_value":[31, 28, 24, 20]},
"learning_rate":{"_type":"choice","_value":[0.01, 0.05, 0.1, 0.2]},
"bagging_freq":{"_type":"choice","_value":[1, 2, 4, 8, 10]}
}
2 changes: 1 addition & 1 deletion src/nni_manager/rest_server/restValidationSchemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export namespace ValidationSchemas {
checkpointDir: joi.string().allow('')
}),
tuner: joi.object({
builtinTunerName: joi.string().valid('TPE', 'Random', 'Anneal', 'Evolution', 'SMAC', 'BatchTuner', 'GridSearch', 'NetworkMorphism'),
builtinTunerName: joi.string().valid('TPE', 'Random', 'Anneal', 'Evolution', 'SMAC', 'BatchTuner', 'GridSearch', 'NetworkMorphism', 'MetisTuner'),
codeDir: joi.string(),
classFileName: joi.string(),
className: joi.string(),
Expand Down
4 changes: 3 additions & 1 deletion src/sdk/pynni/nni/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
'Medianstop': 'nni.medianstop_assessor.medianstop_assessor',
'GridSearch': 'nni.gridsearch_tuner.gridsearch_tuner',
'NetworkMorphism': 'nni.networkmorphism_tuner.networkmorphism_tuner',
'Curvefitting': 'nni.curvefitting_assessor.curvefitting_assessor'
'Curvefitting': 'nni.curvefitting_assessor.curvefitting_assessor',
'MetisTuner': 'nni.metis_tuner.metis_tuner'
}

ClassName = {
Expand All @@ -40,6 +41,7 @@
'BatchTuner': 'BatchTuner',
'GridSearch': 'GridSearchTuner',
'NetworkMorphism':'NetworkMorphismTuner',
'MetisTuner':'MetisTuner',

'Medianstop': 'MedianstopAssessor',
'Curvefitting': 'CurvefittingAssessor'
Expand Down
58 changes: 58 additions & 0 deletions src/sdk/pynni/nni/metis_tuner/Regression_GMM/CreateModel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Copyright (c) Microsoft Corporation
# All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge,
# to any person obtaining a copy of this software and associated
# documentation files (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and
# to permit persons to whom the Software is furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
# BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import os
import sys
from operator import itemgetter

import sklearn.mixture as mm

sys.path.insert(1, os.path.join(sys.path[0], '..'))


def create_model(samples_x, samples_y_aggregation, percentage_goodbatch=0.34):
'''
Create the Gussian Mixture Model
xuehui1991 marked this conversation as resolved.
Show resolved Hide resolved
'''
samples = [samples_x[i] + [samples_y_aggregation[i]] for i in range(0, len(samples_x))]

# Sorts so that we can get the top samples
samples = sorted(samples, key=itemgetter(-1))
samples_goodbatch_size = int(len(samples) * percentage_goodbatch)
samples_goodbatch = samples[0:samples_goodbatch_size]
samples_badbatch = samples[samples_goodbatch_size:]

samples_x_goodbatch = [sample_goodbatch[0:-1] for sample_goodbatch in samples_goodbatch]
#samples_y_goodbatch = [sample_goodbatch[-1] for sample_goodbatch in samples_goodbatch]
samples_x_badbatch = [sample_badbatch[0:-1] for sample_badbatch in samples_badbatch]

# === Trains GMM clustering models === #
#sys.stderr.write("[%s] Train GMM's GMM model\n" % (os.path.basename(__file__)))
bgmm_goodbatch = mm.BayesianGaussianMixture(n_components=max(1, samples_goodbatch_size - 1))
bad_n_components = max(1, len(samples_x) - samples_goodbatch_size - 1)
bgmm_badbatch = mm.BayesianGaussianMixture(n_components=bad_n_components)
bgmm_goodbatch.fit(samples_x_goodbatch)
bgmm_badbatch.fit(samples_x_badbatch)

model = {}
model['clusteringmodel_good'] = bgmm_goodbatch
model['clusteringmodel_bad'] = bgmm_badbatch
return model

104 changes: 104 additions & 0 deletions src/sdk/pynni/nni/metis_tuner/Regression_GMM/Selection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Copyright (c) Microsoft Corporation
# All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge,
# to any person obtaining a copy of this software and associated
# documentation files (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and
# to permit persons to whom the Software is furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
# BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import os
import random
import sys

import nni.metis_tuner.lib_acquisition_function as lib_acquisition_function
import nni.metis_tuner.lib_constraint_summation as lib_constraint_summation
import nni.metis_tuner.lib_data as lib_data

sys.path.insert(1, os.path.join(sys.path[0], '..'))


CONSTRAINT_LOWERBOUND = None
CONSTRAINT_UPPERBOUND = None
CONSTRAINT_PARAMS_IDX = []


def _ratio_scores(parameters_value, clusteringmodel_gmm_good, clusteringmodel_gmm_bad):
'''
The ratio is smaller the better
'''
ratio = clusteringmodel_gmm_good.score([parameters_value]) / clusteringmodel_gmm_bad.score([parameters_value])
sigma = 0
return ratio, sigma

def selection_r(x_bounds,
x_types,
clusteringmodel_gmm_good,
clusteringmodel_gmm_bad,
num_starting_points=100,
minimize_constraints_fun=None):
'''
Call selection
'''
minimize_starting_points = [lib_data.rand(x_bounds, x_types)\
for i in range(0, num_starting_points)]
outputs = selection(x_bounds, x_types,
clusteringmodel_gmm_good,
clusteringmodel_gmm_bad,
minimize_starting_points,
minimize_constraints_fun)
return outputs

def selection(x_bounds,
x_types,
clusteringmodel_gmm_good,
clusteringmodel_gmm_bad,
minimize_starting_points,
minimize_constraints_fun=None):
'''
Select the lowest mu value
'''
results = lib_acquisition_function.next_hyperparameter_lowest_mu(\
_ratio_scores, [clusteringmodel_gmm_good, clusteringmodel_gmm_bad],\
x_bounds, x_types, minimize_starting_points, \
minimize_constraints_fun=minimize_constraints_fun)

return results

def _rand_with_constraints(x_bounds, x_types):
'''
Random generate the variable wit constraints
xuehui1991 marked this conversation as resolved.
Show resolved Hide resolved
'''
outputs = None
x_bounds_withconstraints = [x_bounds[i] for i in CONSTRAINT_PARAMS_IDX]
x_types_withconstraints = [x_types[i] for i in CONSTRAINT_PARAMS_IDX]
x_val_withconstraints = lib_constraint_summation.rand(x_bounds_withconstraints,
x_types_withconstraints,
CONSTRAINT_LOWERBOUND,
CONSTRAINT_UPPERBOUND)
if x_val_withconstraints is not None:
outputs = [None] * len(x_bounds)
for i, _ in enumerate(CONSTRAINT_PARAMS_IDX):
outputs[CONSTRAINT_PARAMS_IDX[i]] = x_val_withconstraints[i]
for i, _ in enumerate(outputs):
if outputs[i] is None:
outputs[i] = random.randint(x_bounds[i][0], x_bounds[i][1])
return outputs

def _minimize_constraints_fun_summation(x):
'''
Minimize constraints fun summation
'''
summation = sum([x[i] for i in CONSTRAINT_PARAMS_IDX])
return CONSTRAINT_UPPERBOUND >= summation >= CONSTRAINT_LOWERBOUND
Empty file.
52 changes: 52 additions & 0 deletions src/sdk/pynni/nni/metis_tuner/Regression_GP/CreateModel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright (c) Microsoft Corporation
# All rights reserved.
#
# MIT License
#
# Permission is hereby granted, free of charge,
# to any person obtaining a copy of this software and associated
# documentation files (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and
# to permit persons to whom the Software is furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
# BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

import os
import sys
import numpy

import sklearn.gaussian_process as gp

sys.path.insert(1, os.path.join(sys.path[0], '..'))


def create_model(samples_x, samples_y_aggregation,
n_restarts_optimizer=250, is_white_kernel=False):
'''
Trains GP regression model
'''
kernel = gp.kernels.ConstantKernel(constant_value=1,
constant_value_bounds=(1e-12, 1e12)) * \
gp.kernels.Matern(nu=1.5)
if is_white_kernel is True:
kernel += gp.kernels.WhiteKernel(noise_level=1, noise_level_bounds=(1e-12, 1e12))
regressor = gp.GaussianProcessRegressor(kernel=kernel,
n_restarts_optimizer=n_restarts_optimizer,
normalize_y=True,
alpha=0)
regressor.fit(numpy.array(samples_x), numpy.array(samples_y_aggregation))

model = {}
model['model'] = regressor
model['kernel_prior'] = str(kernel)
model['kernel_posterior'] = str(regressor.kernel_)
model['model_loglikelihood'] = regressor.log_marginal_likelihood(regressor.kernel_.theta)

return model
Loading