hpo.py 3.83 KB
Newer Older
1
2
3
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.

4
5
"""Wrappers of HPO tuners as NAS strategy."""

QuanluZhang's avatar
QuanluZhang committed
6
import logging
7
import time
8
from typing import Optional
QuanluZhang's avatar
QuanluZhang committed
9

10
from nni.nas import Sampler, submit_models, query_available_resources, is_stopped_exec, budget_exhausted
11
from .base import BaseStrategy
QuanluZhang's avatar
QuanluZhang committed
12
13
14

_logger = logging.getLogger(__name__)

15

QuanluZhang's avatar
QuanluZhang committed
16
17
class TPESampler(Sampler):
    def __init__(self, optimize_mode='minimize'):
18
19
20
        # Move import here to eliminate some warning messages about dill.
        from nni.algorithms.hpo.hyperopt_tuner import HyperoptTuner

QuanluZhang's avatar
QuanluZhang committed
21
        self.tpe_tuner = HyperoptTuner('tpe', optimize_mode)
22
23
        self.cur_sample: Optional[dict] = None
        self.index: Optional[int] = None
QuanluZhang's avatar
QuanluZhang committed
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
        self.total_parameters = {}

    def update_sample_space(self, sample_space):
        search_space = {}
        for i, each in enumerate(sample_space):
            search_space[str(i)] = {'_type': 'choice', '_value': each}
        self.tpe_tuner.update_search_space(search_space)

    def generate_samples(self, model_id):
        self.cur_sample = self.tpe_tuner.generate_parameters(model_id)
        self.total_parameters[model_id] = self.cur_sample
        self.index = 0

    def receive_result(self, model_id, result):
        self.tpe_tuner.receive_trial_result(model_id, self.total_parameters[model_id], result)

    def choice(self, candidates, mutator, model, index):
41
        assert isinstance(self.index, int) and isinstance(self.cur_sample, dict)
QuanluZhang's avatar
QuanluZhang committed
42
43
44
45
        chosen = self.cur_sample[str(self.index)]
        self.index += 1
        return chosen

46

Yuge Zhang's avatar
Yuge Zhang committed
47
class TPE(BaseStrategy):
48
    """
Yuge Zhang's avatar
Yuge Zhang committed
49
50
    The Tree-structured Parzen Estimator (TPE) is a sequential model-based optimization (SMBO) approach.

Yuge Zhang's avatar
Yuge Zhang committed
51
52
    Find the details in
    `Algorithms for Hyper-Parameter Optimization <https://papers.nips.cc/paper/2011/file/86e8f7ab32cfd12577bc2619bc635690-Paper.pdf>`__.
Yuge Zhang's avatar
Yuge Zhang committed
53

54
55
56
57
    SMBO methods sequentially construct models to approximate the performance of hyperparameters based on historical measurements,
    and then subsequently choose new hyperparameters to test based on this model.
    """

QuanluZhang's avatar
QuanluZhang committed
58
59
60
    def __init__(self):
        self.tpe_sampler = TPESampler()
        self.model_id = 0
61
        self.running_models = {}
QuanluZhang's avatar
QuanluZhang committed
62

63
    def run(self, base_model, applied_mutators):
QuanluZhang's avatar
QuanluZhang committed
64
65
66
67
68
69
70
        sample_space = []
        new_model = base_model
        for mutator in applied_mutators:
            recorded_candidates, new_model = mutator.dry_run(new_model)
            sample_space.extend(recorded_candidates)
        self.tpe_sampler.update_sample_space(sample_space)

71
        _logger.info('TPE strategy has been started.')
72
        while not budget_exhausted():
73
74
            avail_resource = query_available_resources()
            if avail_resource > 0:
QuanluZhang's avatar
QuanluZhang committed
75
                model = base_model
76
                _logger.debug('New model created. Applied mutators: %s', str(applied_mutators))
QuanluZhang's avatar
QuanluZhang committed
77
78
79
80
81
82
                self.tpe_sampler.generate_samples(self.model_id)
                for mutator in applied_mutators:
                    mutator.bind_sampler(self.tpe_sampler)
                    model = mutator.apply(model)
                # run models
                submit_models(model)
83
                self.running_models[self.model_id] = model
QuanluZhang's avatar
QuanluZhang committed
84
                self.model_id += 1
85
86
87
            else:
                time.sleep(2)

88
            _logger.debug('num of running models: %d', len(self.running_models))
89
90
91
92
93
            to_be_deleted = []
            for _id, _model in self.running_models.items():
                if is_stopped_exec(_model):
                    if _model.metric is not None:
                        self.tpe_sampler.receive_result(_id, _model.metric)
94
                        _logger.debug('tpe receive results: %d, %s', _id, _model.metric)
95
96
97
                    to_be_deleted.append(_id)
            for _id in to_be_deleted:
                del self.running_models[_id]
Yuge Zhang's avatar
Yuge Zhang committed
98
99
100
101


# alias for backward compatibility
TPEStrategy = TPE