예제 #1
0
    def handle_trial_end(self, data):
        '''
        data: it has three keys: trial_job_id, event, hyper_params
            trial_job_id: the id generated by training service
            event: the job's state
            hyper_params: the hyperparameters (a string) generated and returned by tuner
        '''
        hyper_params = json_tricks.loads(data['hyper_params'])
        bracket_id, i, _ = hyper_params['parameter_id'].split('_')
        hyper_configs = self.brackets[int(bracket_id)].inform_trial_end(int(i))
        if hyper_configs is not None:
            _logger.debug('bracket %s next round %s, hyper_configs: %s',
                          bracket_id, i, hyper_configs)
            self.generated_hyper_configs = self.generated_hyper_configs + hyper_configs
            for _ in range(self.credit):
                if not self.generated_hyper_configs:
                    break
                params = self.generated_hyper_configs.pop()
                ret = {
                    'parameter_id': params[0],
                    'parameter_source': 'algorithm',
                    'parameters': params[1]
                }
                send(CommandType.NewTrialJob, json_tricks.dumps(ret))
                self.credit -= 1

        return True
예제 #2
0
    def handle_report_metric_data(self, data):
        trial_job_id = data['trial_job_id']
        if data['type'] == 'FINAL':
            id_ = data['parameter_id']
            if id_ in _customized_parameter_ids:
                self.tuner.receive_customized_trial_result(
                    id_, _trial_params[id_], data['value'], trial_job_id)
            else:
                self.tuner.receive_trial_result(id_, _trial_params[id_],
                                                data['value'], trial_job_id)
        elif data['type'] == 'PERIODICAL':
            if self.assessor is not None:
                self._handle_intermediate_metric_data(data)
            else:
                pass
        elif data['type'] == 'REQUEST_PARAMETER':
            assert data['trial_job_id'] is not None
            assert data['parameter_index'] is not None
            param_id = _create_parameter_id()
            param = self.tuner.generate_parameters(param_id, trial_job_id)
            send(
                CommandType.SendTrialJobParameter,
                _pack_parameter(param_id,
                                param,
                                trial_job_id=data['trial_job_id'],
                                parameter_index=data['parameter_index']))
        else:
            raise ValueError('Data type not supported: {}'.format(
                data['type']))

        return True
    def _request_one_trial_job(self):
        """get one trial job, i.e., one hyperparameter configuration."""
        if not self.generated_hyper_configs:
            if self.curr_s < 0:
                self.curr_s = self.s_max
            _logger.debug('create a new bracket, self.curr_s=%d', self.curr_s)
            self.brackets[self.curr_s] = Bracket(self.curr_s, self.s_max,
                                                 self.eta, self.R,
                                                 self.optimize_mode)
            next_n, next_r = self.brackets[self.curr_s].get_n_r()
            _logger.debug('new bracket, next_n=%d, next_r=%d', next_n, next_r)
            assert self.searchspace_json is not None and self.random_state is not None
            generated_hyper_configs = self.brackets[
                self.curr_s].get_hyperparameter_configurations(
                    next_n, next_r, self.searchspace_json, self.random_state)
            self.generated_hyper_configs = generated_hyper_configs.copy()
            self.curr_s -= 1

        assert self.generated_hyper_configs
        params = self.generated_hyper_configs.pop()
        ret = {
            'parameter_id': params[0],
            'parameter_source': 'algorithm',
            'parameters': params[1]
        }
        send(CommandType.NewTrialJob, json_tricks.dumps(ret))
예제 #4
0
    def _handle_intermediate_metric_data(self, data):
        if data['type'] != 'PERIODICAL':
            return True
        if self.assessor is None:
            return True

        trial_job_id = data['trial_job_id']
        if trial_job_id in _ended_trials:
            return True

        history = _trial_history[trial_job_id]
        history[data['sequence']] = data['value']
        ordered_history = _sort_history(history)
        if len(ordered_history
               ) < data['sequence']:  # no user-visible update since last time
            return True

        try:
            result = self.assessor.assess_trial(trial_job_id, ordered_history)
        except Exception as e:
            _logger.exception('Assessor error')

        if isinstance(result, bool):
            result = AssessResult.Good if result else AssessResult.Bad
        elif not isinstance(result, AssessResult):
            msg = 'Result of Assessor.assess_trial must be an object of AssessResult, not %s'
            raise RuntimeError(msg % type(result))

        if result is AssessResult.Bad:
            _logger.debug('BAD, kill %s', trial_job_id)
            send(CommandType.KillTrialJob, json_tricks.dumps(trial_job_id))
        else:
            _logger.debug('GOOD')
예제 #5
0
    def handle_initialize(self, data):
        """Initialize Tuner, including creating Bayesian optimization-based parametric models 
        and search space formations

        Parameters
        ----------
        data: search space
            search space of this experiment

        Raises
        ------
        ValueError
            Error: Search space is None
        """
        logger.info('start to handle_initialize')
        # convert search space jason to ConfigSpace
        self.handle_update_search_space(data)

        # generate BOHB config_generator using Bayesian optimization
        if self.search_space:
            self.cg = CG_BOHB(configspace=self.search_space,
                              min_points_in_model=self.min_points_in_model,
                              top_n_percent=self.top_n_percent,
                              num_samples=self.num_samples,
                              random_fraction=self.random_fraction,
                              bandwidth_factor=self.bandwidth_factor,
                              min_bandwidth=self.min_bandwidth)
        else:
            raise ValueError('Error: Search space is None')
        # generate first brackets
        self.generate_new_bracket()
        send(CommandType.Initialized, '')
예제 #6
0
 def handle_add_customized_trial(self, data):
     # data: parameters
     id_ = _create_parameter_id()
     _customized_parameter_ids.add(id_)
     send(CommandType.NewTrialJob,
          _pack_parameter(id_, data, customized=True))
     return True
예제 #7
0
    def _request_one_trial_job(self):
        """get one trial job, i.e., one hyperparameter configuration.

        If this function is called, Command will be sent by BOHB:
        a. If there is a parameter need to run, will return "NewTrialJob" with a dict:
        { 
            'parameter_id': id of new hyperparameter
            'parameter_source': 'algorithm'
            'parameters': value of new hyperparameter
        }
        b. If BOHB don't have parameter waiting, will return "NoMoreTrialJobs" with
        {
            'parameter_id': '-1_0_0',
            'parameter_source': 'algorithm',
            'parameters': ''
        }
        """
        if not self.generated_hyper_configs:
            ret = {
                'parameter_id': '-1_0_0',
                'parameter_source': 'algorithm',
                'parameters': ''
            }
            send(CommandType.NoMoreTrialJobs, json_tricks.dumps(ret))
            return
        assert self.generated_hyper_configs
        params = self.generated_hyper_configs.pop()
        ret = {
            'parameter_id': params[0],
            'parameter_source': 'algorithm',
            'parameters': params[1]
        }
        self.parameters[params[0]] = params[1]
        send(CommandType.NewTrialJob, json_tricks.dumps(ret))
        self.credit -= 1
예제 #8
0
 def handle_initialize(self, data):
     logger.info("Advisor initialized: {}".format(data))
     self.handle_update_search_space(data)
     self.parameters_count = 0
     self.parameter_best_metric = defaultdict(float)
     self.parameter_cooldown = defaultdict(int)
     send(CommandType.Initialized, '')
예제 #9
0
 def handle_initialize(self, data):
     '''
     data is search space
     '''
     self.handle_update_search_space(data)
     send(CommandType.Initialized, '')
     return True
예제 #10
0
파일: test_protocol.py 프로젝트: zymale/nni
 def test_send_too_large(self):
     _prepare_send()
     exception = None
     try:
         send(CommandType.NewTrialJob, ' ' * 1000000)
     except AssertionError as e:
         exception = e
     self.assertIsNotNone(exception)
예제 #11
0
 def handle_initialize(self, data):
     """callback for initializing the advisor
     Parameters
     ----------
     data: dict
         search space
     """
     self.handle_update_search_space(data)
     send(CommandType.Initialized, '')
예제 #12
0
 def handle_request_trial_jobs(self, data):
     # data: number or trial jobs
     ids = [_create_parameter_id() for _ in range(data)]
     params_list = self.tuner.generate_multiple_parameters(ids)
     assert len(ids) == len(params_list)
     for i, _ in enumerate(ids):
         send(CommandType.NewTrialJob,
              _pack_parameter(ids[i], params_list[i]))
     return True
예제 #13
0
    def handle_initialize(self, data):
        """data is search space

        Parameters
        ----------
        data: int
            number of trial jobs
        """
        self.handle_update_search_space(data)
        send(CommandType.Initialized, '')
예제 #14
0
 def handle_request_trial_jobs(self, data):
     """
     Parameters
     ----------
     data: int
         number of trial jobs
     """
     for _ in range(data):
         ret = self._get_one_trial_job()
         send(CommandType.NewTrialJob, json_tricks.dumps(ret))
예제 #15
0
    def handle_report_metric_data(self, data):
        """
        Parameters
        ----------
        data:
            it is an object which has keys 'parameter_id', 'value', 'trial_job_id', 'type', 'sequence'.

        Raises
        ------
        ValueError
            Data type not supported
        """
        if data['type'] == MetricType.REQUEST_PARAMETER:
            assert multi_phase_enabled()
            assert data['trial_job_id'] is not None
            assert data['parameter_index'] is not None
            assert data['trial_job_id'] in self.job_id_para_id_map
            self._handle_trial_end(
                self.job_id_para_id_map[data['trial_job_id']])
            ret = self._get_one_trial_job()
            if data['trial_job_id'] is not None:
                ret['trial_job_id'] = data['trial_job_id']
            if data['parameter_index'] is not None:
                ret['parameter_index'] = data['parameter_index']
            self.job_id_para_id_map[data['trial_job_id']] = ret['parameter_id']
            send(CommandType.SendTrialJobParameter, json_tricks.dumps(ret))
        else:
            value = extract_scalar_reward(data['value'])
            bracket_id, i, _ = data['parameter_id'].split('_')
            bracket_id = int(bracket_id)

            # add <trial_job_id, parameter_id> to self.job_id_para_id_map here,
            # because when the first parameter_id is created, trial_job_id is not known yet.
            if data['trial_job_id'] in self.job_id_para_id_map:
                assert self.job_id_para_id_map[
                    data['trial_job_id']] == data['parameter_id']
            else:
                self.job_id_para_id_map[
                    data['trial_job_id']] = data['parameter_id']

            if data['type'] == MetricType.FINAL:
                # sys.maxsize indicates this value is from FINAL metric data, because data['sequence'] from FINAL metric
                # and PERIODICAL metric are independent, thus, not comparable.
                self.brackets[bracket_id].set_config_perf(
                    int(i), data['parameter_id'], sys.maxsize, value)
                self.completed_hyper_configs.append(data)
            elif data['type'] == MetricType.PERIODICAL:
                self.brackets[bracket_id].set_config_perf(
                    int(i), data['parameter_id'], data['sequence'], value)
            else:
                raise ValueError('Data type not supported: {}'.format(
                    data['type']))
예제 #16
0
 def _send_new_trial(self):
     while self.unsatisfied_jobs:
         ret = self._get_one_trial_job()
         if ret is None:
             break
         one_unsatisfied = self.unsatisfied_jobs.pop(0)
         ret['trial_job_id'] = one_unsatisfied['trial_job_id']
         ret['parameter_index'] = one_unsatisfied['parameter_index']
         # update parameter_id in self.job_id_para_id_map
         self.job_id_para_id_map[ret['trial_job_id']] = ret['parameter_id']
         send(CommandType.SendTrialJobParameter, json_tricks.dumps(ret))
     for _ in range(self.credit):
         self._request_one_trial_job()
예제 #17
0
 def _send_new_trial(self):
     self.parameters_count += 1
     new_trial = {
         "parameter_id": self.parameters_count,
         "parameters": {
             "optimizer": param.choice(self.searchspace_json["optimizer"], self.random_state),
             "learning_rate": param.loguniform(self.searchspace_json["learning_rate"][0],
                                               self.searchspace_json["learning_rate"][1],
                                               self.random_state)
         },
         "parameter_source": "algorithm"
     }
     logger.info("New trial sent: {}".format(new_trial))
     send(CommandType.NewTrialJob, json_tricks.dumps(new_trial))
예제 #18
0
 def handle_report_metric_data(self, data):
     logger.info("Metric reported: {}".format(data))
     if data['type'] == MetricType.REQUEST_PARAMETER:
         raise ValueError("Request parameter not supported")
     elif data["type"] == MetricType.PERIODICAL:
         parameter_id = data["parameter_id"]
         if data["value"] > self.parameter_best_metric[parameter_id]:
             self.parameter_best_metric[parameter_id] = data["value"]
             self.parameter_cooldown[parameter_id] = 0
         else:
             self.parameter_cooldown[parameter_id] += 1
             logger.info("Accuracy dropped, cooldown {}, sending a new trial".format(
                 self.parameter_cooldown[parameter_id]))
             self._send_new_trial()
             if self.parameter_cooldown[parameter_id] >= self.k:
                 logger.info("Send kill signal to {}".format(data))
                 send(CommandType.KillTrialJob, json_tricks.dumps(data["trial_job_id"]))
예제 #19
0
    def _request_one_trial_job(self):
        """get one trial job, i.e., one hyperparameter configuration.

        If this function is called, Command will be sent by BOHB:
        a. If there is a parameter need to run, will return "NewTrialJob" with a dict:
        {
            'parameter_id': id of new hyperparameter
            'parameter_source': 'algorithm'
            'parameters': value of new hyperparameter
        }
        b. If BOHB don't have parameter waiting, will return "NoMoreTrialJobs" with
        {
            'parameter_id': '-1_0_0',
            'parameter_source': 'algorithm',
            'parameters': ''
        }
        """
        ret = self._get_one_trial_job()
        if ret is not None:
            send(CommandType.NewTrialJob, json_tricks.dumps(ret))
            self.credit -= 1
예제 #20
0
파일: test_protocol.py 프로젝트: zymale/nni
 def test_send_zh(self):
     out_file = _prepare_send()
     send(CommandType.NewTrialJob, '你好')
     self.assertEqual(out_file.getvalue(), 'TR000006你好'.encode('utf8'))
예제 #21
0
파일: test_protocol.py 프로젝트: zymale/nni
 def test_send_en(self):
     out_file = _prepare_send()
     send(CommandType.NewTrialJob, 'CONTENT')
     self.assertEqual(out_file.getvalue(), b'TR000007CONTENT')
예제 #22
0
    def test_assessor(self):
        _reverse_io()
        send(
            CommandType.ReportMetricData,
            '{"trial_job_id":"A","type":"PERIODICAL","sequence":0,"value":2}')
        send(
            CommandType.ReportMetricData,
            '{"trial_job_id":"B","type":"PERIODICAL","sequence":0,"value":2}')
        send(
            CommandType.ReportMetricData,
            '{"trial_job_id":"A","type":"PERIODICAL","sequence":1,"value":3}')
        send(CommandType.TrialEnd,
             '{"trial_job_id":"A","event":"SYS_CANCELED"}')
        send(CommandType.TrialEnd, '{"trial_job_id":"B","event":"SUCCEEDED"}')
        send(CommandType.NewTrialJob, 'null')
        _restore_io()

        assessor = NaiveAssessor()
        dispatcher = MsgDispatcher(None, assessor)
        try:
            dispatcher.run()
        except Exception as e:
            self.assertIs(type(e), AssertionError)
            self.assertEqual(e.args[0],
                             'Unsupported command: CommandType.NewTrialJob')

        self.assertEqual(_trials, ['A', 'B', 'A'])
        self.assertEqual(_end_trials, [('A', False), ('B', True)])

        _reverse_io()
        command, data = receive()
        self.assertIs(command, CommandType.KillTrialJob)
        self.assertEqual(data, '"A"')
        self.assertEqual(len(_out_buf.read()), 0)
예제 #23
0
def _test_tuner():
    _reverse_io()  # now we are sending to Tuner's incoming stream
    send(
        CommandType.UpdateSearchSpace,
        "{\"learning_rate\": {\"_value\": [0.0001, 0.001, 0.002, 0.005, 0.01], \"_type\": \"choice\"}, \"optimizer\": {\"_value\": [\"Adam\", \"SGD\"], \"_type\": \"choice\"}}"
    )
    send(CommandType.RequestTrialJobs, '2')
    send(
        CommandType.ReportMetricData,
        '{"parameter_id":0,"type":"PERIODICAL","value":10,"trial_job_id":"abc"}'
    )
    send(CommandType.ReportMetricData,
         '{"parameter_id":1,"type":"FINAL","value":11,"trial_job_id":"abc"}')
    send(CommandType.AddCustomizedTrialJob, '{"param":-1}')
    send(CommandType.ReportMetricData,
         '{"parameter_id":2,"type":"FINAL","value":22,"trial_job_id":"abc"}')
    send(CommandType.RequestTrialJobs, '1')
    send(CommandType.TrialEnd, '{"trial_job_id":"abc"}')
    _restore_io()

    tuner = NaiveMultiPhaseTuner()
    dispatcher = MultiPhaseMsgDispatcher(tuner)
    dispatcher.run()

    _reverse_io()  # now we are receiving from Tuner's outgoing stream

    command, data = receive()  # this one is customized
    print(command, data)
예제 #24
0
    def handle_report_metric_data(self, data):
        """reveice the metric data and update Bayesian optimization with final result

        Parameters
        ----------
        data:
            it is an object which has keys 'parameter_id', 'value', 'trial_job_id', 'type', 'sequence'.

        Raises
        ------
        ValueError
            Data type not supported
        """
        logger.debug('handle report metric data = %s', data)

        if data['type'] == MetricType.REQUEST_PARAMETER:
            assert multi_phase_enabled()
            assert data['trial_job_id'] is not None
            assert data['parameter_index'] is not None
            assert data['trial_job_id'] in self.job_id_para_id_map
            self._handle_trial_end(
                self.job_id_para_id_map[data['trial_job_id']])
            ret = self._get_one_trial_job()
            if ret is None:
                self.unsatisfied_jobs.append({
                    'trial_job_id':
                    data['trial_job_id'],
                    'parameter_index':
                    data['parameter_index']
                })
            else:
                ret['trial_job_id'] = data['trial_job_id']
                ret['parameter_index'] = data['parameter_index']
                # update parameter_id in self.job_id_para_id_map
                self.job_id_para_id_map[
                    data['trial_job_id']] = ret['parameter_id']
                send(CommandType.SendTrialJobParameter, json_tricks.dumps(ret))
        else:
            assert 'value' in data
            value = extract_scalar_reward(data['value'])
            if self.optimize_mode is OptimizeMode.Maximize:
                reward = -value
            else:
                reward = value
            assert 'parameter_id' in data
            s, i, _ = data['parameter_id'].split('_')
            logger.debug('bracket id = %s, metrics value = %s, type = %s', s,
                         value, data['type'])
            s = int(s)

            # add <trial_job_id, parameter_id> to self.job_id_para_id_map here,
            # because when the first parameter_id is created, trial_job_id is not known yet.
            if data['trial_job_id'] in self.job_id_para_id_map:
                assert self.job_id_para_id_map[
                    data['trial_job_id']] == data['parameter_id']
            else:
                self.job_id_para_id_map[
                    data['trial_job_id']] = data['parameter_id']

            assert 'type' in data
            if data['type'] == MetricType.FINAL:
                # and PERIODICAL metric are independent, thus, not comparable.
                assert 'sequence' in data
                self.brackets[s].set_config_perf(int(i), data['parameter_id'],
                                                 sys.maxsize, value)
                self.completed_hyper_configs.append(data)

                _parameters = self.parameters[data['parameter_id']]
                _parameters.pop(_KEY)
                # update BO with loss, max_s budget, hyperparameters
                self.cg.new_result(loss=reward,
                                   budget=data['sequence'],
                                   parameters=_parameters,
                                   update_model=True)
            elif data['type'] == MetricType.PERIODICAL:
                self.brackets[s].set_config_perf(int(i), data['parameter_id'],
                                                 data['sequence'], value)
            else:
                raise ValueError('Data type not supported: {}'.format(
                    data['type']))
예제 #25
0
    def test_tuner(self):
        _reverse_io()  # now we are sending to Tuner's incoming stream
        send(CommandType.RequestTrialJobs, '2')
        send(CommandType.ReportMetricData, '{"parameter_id":0,"type":"PERIODICAL","value":10}')
        send(CommandType.ReportMetricData, '{"parameter_id":1,"type":"FINAL","value":11}')
        send(CommandType.UpdateSearchSpace, '{"name":"SS0"}')
        send(CommandType.AddCustomizedTrialJob, '{"param":-1}')
        send(CommandType.ReportMetricData, '{"parameter_id":2,"type":"FINAL","value":22}')
        send(CommandType.RequestTrialJobs, '1')
        send(CommandType.KillTrialJob, 'null')
        _restore_io()

        tuner = NaiveTuner()
        try:
            tuner.run()
        except Exception as e:
            self.assertIs(type(e), AssertionError)
            self.assertEqual(e.args[0], 'Unsupported command: CommandType.KillTrialJob')

        _reverse_io()  # now we are receiving from Tuner's outgoing stream
        self._assert_params(0, 2, [ ], None)
        self._assert_params(1, 4, [ ], None)

        command, data = receive()  # this one is customized
        data = json.loads(data)
        self.assertIs(command, CommandType.NewTrialJob)
        self.assertEqual(data, {
            'parameter_id': 2,
            'parameter_source': 'customized',
            'parameters': { 'param': -1 }
        })

        self._assert_params(3, 6, [[1,4,11,False], [2,-1,22,True]], {'name':'SS0'})

        self.assertEqual(len(_out_buf.read()), 0)  # no more commands
예제 #26
0
    def test_msg_dispatcher(self):
        _reverse_io()  # now we are sending to Tuner's incoming stream
        send(CommandType.RequestTrialJobs, '2')
        send(CommandType.ReportMetricData,
             '{"parameter_id":0,"type":"PERIODICAL","value":10}')
        send(CommandType.ReportMetricData,
             '{"parameter_id":1,"type":"FINAL","value":11}')
        send(CommandType.UpdateSearchSpace, '{"name":"SS0"}')
        send(CommandType.RequestTrialJobs, '1')
        send(CommandType.KillTrialJob, 'null')
        _restore_io()

        tuner = NaiveTuner()
        dispatcher = MsgDispatcher(tuner)
        nni.msg_dispatcher_base._worker_fast_exit_on_terminate = False

        dispatcher.run()
        e = dispatcher.worker_exceptions[0]
        self.assertIs(type(e), AssertionError)
        self.assertEqual(e.args[0],
                         'Unsupported command: CommandType.KillTrialJob')

        _reverse_io()  # now we are receiving from Tuner's outgoing stream
        self._assert_params(0, 2, [], None)
        self._assert_params(1, 4, [], None)

        self._assert_params(2, 6, [[1, 4, 11, False]], {'name': 'SS0'})

        self.assertEqual(len(_out_buf.read()), 0)  # no more commands