Spaces:
Runtime error
Runtime error
| from __future__ import absolute_import | |
| from __future__ import division | |
| from __future__ import print_function | |
| r"""Run grid search. | |
| Look at launch_tuning.sh for details on how to tune at scale. | |
| Usage example: | |
| Tune with one worker on the local machine. | |
| CONFIG="agent=c(algorithm='pg')," | |
| CONFIG+="env=c(task_cycle=['reverse-tune', 'remove-tune'])" | |
| HPARAM_SPACE_TYPE="pg" | |
| OUT_DIR="/tmp/bf_pg_tune" | |
| MAX_NPE=5000000 | |
| NUM_REPETITIONS=50 | |
| rm -rf $OUT_DIR | |
| mkdir $OUT_DIR | |
| bazel run -c opt single_task:tune -- \ | |
| --alsologtostderr \ | |
| --config="$CONFIG" \ | |
| --max_npe="$MAX_NPE" \ | |
| --num_repetitions="$NUM_REPETITIONS" \ | |
| --logdir="$OUT_DIR" \ | |
| --summary_interval=1 \ | |
| --model_v=0 \ | |
| --hparam_space="$HPARAM_SPACE_TYPE" \ | |
| --tuner_id=0 \ | |
| --num_tuners=1 \ | |
| 2>&1 >"$OUT_DIR/tuner_0.log" | |
| learning/brain/tensorboard/tensorboard.sh --port 12345 --logdir "$OUT_DIR" | |
| """ | |
| import ast | |
| import os | |
| from absl import app | |
| from absl import flags | |
| from absl import logging | |
| import numpy as np | |
| from six.moves import xrange | |
| import tensorflow as tf | |
| from single_task import defaults # brain coder | |
| from single_task import run as run_lib # brain coder | |
| FLAGS = flags.FLAGS | |
| flags.DEFINE_integer( | |
| 'tuner_id', 0, | |
| 'The unique ID for this tuning worker.') | |
| flags.DEFINE_integer( | |
| 'num_tuners', 1, | |
| 'How many tuners are there.') | |
| flags.DEFINE_string( | |
| 'hparam_space', 'default', | |
| 'String name which denotes the hparam space to tune over. This is ' | |
| 'algorithm dependent.') | |
| flags.DEFINE_string( | |
| 'fixed_hparams', '', | |
| 'HParams string. Used to fix hparams during tuning.') | |
| flags.DEFINE_float( | |
| 'success_rate_objective_weight', 1.0, | |
| 'How much to weight success rate vs num programs seen. By default, only ' | |
| 'success rate is optimized (this is the setting used in the paper).') | |
| def parse_hparams_string(hparams_str): | |
| hparams = {} | |
| for term in hparams_str.split(','): | |
| if not term: | |
| continue | |
| name, value = term.split('=') | |
| hparams[name.strip()] = ast.literal_eval(value) | |
| return hparams | |
| def int_to_multibase(n, bases): | |
| digits = [0] * len(bases) | |
| for i, b in enumerate(bases): | |
| n, d = divmod(n, b) | |
| digits[i] = d | |
| return digits | |
| def hparams_for_index(index, tuning_space): | |
| keys = sorted(tuning_space.keys()) | |
| indices = int_to_multibase(index, [len(tuning_space[k]) for k in keys]) | |
| return tf.contrib.training.HParams( | |
| **{k: tuning_space[k][i] for k, i in zip(keys, indices)}) | |
| def run_tuner_loop(ns): | |
| """Run tuning loop for this worker.""" | |
| is_chief = FLAGS.task_id == 0 | |
| tuning_space = ns.define_tuner_hparam_space( | |
| hparam_space_type=FLAGS.hparam_space) | |
| fixed_hparams = parse_hparams_string(FLAGS.fixed_hparams) | |
| for name, value in fixed_hparams.iteritems(): | |
| tuning_space[name] = [value] | |
| tuning_space_size = np.prod([len(values) for values in tuning_space.values()]) | |
| num_local_trials, remainder = divmod(tuning_space_size, FLAGS.num_tuners) | |
| if FLAGS.tuner_id < remainder: | |
| num_local_trials += 1 | |
| starting_trial_id = ( | |
| num_local_trials * FLAGS.tuner_id + min(remainder, FLAGS.tuner_id)) | |
| logging.info('tuning_space_size: %d', tuning_space_size) | |
| logging.info('num_local_trials: %d', num_local_trials) | |
| logging.info('starting_trial_id: %d', starting_trial_id) | |
| for local_trial_index in xrange(num_local_trials): | |
| trial_config = defaults.default_config_with_updates(FLAGS.config) | |
| global_trial_index = local_trial_index + starting_trial_id | |
| trial_name = 'trial_' + str(global_trial_index) | |
| trial_dir = os.path.join(FLAGS.logdir, trial_name) | |
| hparams = hparams_for_index(global_trial_index, tuning_space) | |
| ns.write_hparams_to_config( | |
| trial_config, hparams, hparam_space_type=FLAGS.hparam_space) | |
| results_list = ns.run_training( | |
| config=trial_config, tuner=None, logdir=trial_dir, is_chief=is_chief, | |
| trial_name=trial_name) | |
| if not is_chief: | |
| # Only chief worker needs to write tuning results to disk. | |
| continue | |
| objective, metrics = compute_tuning_objective( | |
| results_list, hparams, trial_name, num_trials=tuning_space_size) | |
| logging.info('metrics:\n%s', metrics) | |
| logging.info('objective: %s', objective) | |
| logging.info('programs_seen_fraction: %s', | |
| metrics['programs_seen_fraction']) | |
| logging.info('success_rate: %s', metrics['success_rate']) | |
| logging.info('success_rate_objective_weight: %s', | |
| FLAGS.success_rate_objective_weight) | |
| tuning_results_file = os.path.join(trial_dir, 'tuning_results.txt') | |
| with tf.gfile.FastGFile(tuning_results_file, 'a') as writer: | |
| writer.write(str(metrics) + '\n') | |
| logging.info('Trial %s complete.', trial_name) | |
| def compute_tuning_objective(results_list, hparams, trial_name, num_trials): | |
| """Compute tuning objective and metrics given results and trial information. | |
| Args: | |
| results_list: List of results dicts read from disk. These are written by | |
| workers. | |
| hparams: tf.contrib.training.HParams instance containing the hparams used | |
| in this trial (only the hparams which are being tuned). | |
| trial_name: Name of this trial. Used to create a trial directory. | |
| num_trials: Total number of trials that need to be run. This is saved in the | |
| metrics dict for future reference. | |
| Returns: | |
| objective: The objective computed for this trial. Choose the hparams for the | |
| trial with the largest objective value. | |
| metrics: Information about this trial. A dict. | |
| """ | |
| found_solution = [r['found_solution'] for r in results_list] | |
| successful_program_counts = [ | |
| r['npe'] for r in results_list if r['found_solution']] | |
| success_rate = sum(found_solution) / float(len(results_list)) | |
| max_programs = FLAGS.max_npe # Per run. | |
| all_program_counts = [ | |
| r['npe'] if r['found_solution'] else max_programs | |
| for r in results_list] | |
| programs_seen_fraction = ( | |
| float(sum(all_program_counts)) | |
| / (max_programs * len(all_program_counts))) | |
| # min/max/avg stats are over successful runs. | |
| metrics = { | |
| 'num_runs': len(results_list), | |
| 'num_succeeded': sum(found_solution), | |
| 'success_rate': success_rate, | |
| 'programs_seen_fraction': programs_seen_fraction, | |
| 'avg_programs': np.mean(successful_program_counts), | |
| 'max_possible_programs_per_run': max_programs, | |
| 'global_step': sum([r['num_batches'] for r in results_list]), | |
| 'hparams': hparams.values(), | |
| 'trial_name': trial_name, | |
| 'num_trials': num_trials} | |
| # Report stats per tasks. | |
| tasks = [r['task'] for r in results_list] | |
| for task in set(tasks): | |
| task_list = [r for r in results_list if r['task'] == task] | |
| found_solution = [r['found_solution'] for r in task_list] | |
| successful_rewards = [ | |
| r['best_reward'] for r in task_list | |
| if r['found_solution']] | |
| successful_num_batches = [ | |
| r['num_batches'] | |
| for r in task_list if r['found_solution']] | |
| successful_program_counts = [ | |
| r['npe'] for r in task_list if r['found_solution']] | |
| metrics_append = { | |
| task + '__num_runs': len(task_list), | |
| task + '__num_succeeded': sum(found_solution), | |
| task + '__success_rate': ( | |
| sum(found_solution) / float(len(task_list)))} | |
| metrics.update(metrics_append) | |
| if any(found_solution): | |
| metrics_append = { | |
| task + '__min_reward': min(successful_rewards), | |
| task + '__max_reward': max(successful_rewards), | |
| task + '__avg_reward': np.median(successful_rewards), | |
| task + '__min_programs': min(successful_program_counts), | |
| task + '__max_programs': max(successful_program_counts), | |
| task + '__avg_programs': np.mean(successful_program_counts), | |
| task + '__min_batches': min(successful_num_batches), | |
| task + '__max_batches': max(successful_num_batches), | |
| task + '__avg_batches': np.mean(successful_num_batches)} | |
| metrics.update(metrics_append) | |
| # Objective will be maximized. | |
| # Maximize success rate, minimize num programs seen. | |
| # Max objective is always 1. | |
| weight = FLAGS.success_rate_objective_weight | |
| objective = ( | |
| weight * success_rate | |
| + (1 - weight) * (1 - programs_seen_fraction)) | |
| metrics['objective'] = objective | |
| return objective, metrics | |
| def main(argv): | |
| del argv | |
| logging.set_verbosity(FLAGS.log_level) | |
| if not FLAGS.logdir: | |
| raise ValueError('logdir flag must be provided.') | |
| if FLAGS.num_workers <= 0: | |
| raise ValueError('num_workers flag must be greater than 0.') | |
| if FLAGS.task_id < 0: | |
| raise ValueError('task_id flag must be greater than or equal to 0.') | |
| if FLAGS.task_id >= FLAGS.num_workers: | |
| raise ValueError( | |
| 'task_id flag must be strictly less than num_workers flag.') | |
| if FLAGS.num_tuners <= 0: | |
| raise ValueError('num_tuners flag must be greater than 0.') | |
| if FLAGS.tuner_id < 0: | |
| raise ValueError('tuner_id flag must be greater than or equal to 0.') | |
| if FLAGS.tuner_id >= FLAGS.num_tuners: | |
| raise ValueError( | |
| 'tuner_id flag must be strictly less than num_tuners flag.') | |
| ns, _ = run_lib.get_namespace(FLAGS.config) | |
| run_tuner_loop(ns) | |
| if __name__ == '__main__': | |
| app.run(main) | |