From 3b3a61ceaf29bde851cd058620d024193fb9f19a Mon Sep 17 00:00:00 2001 From: Jakob Jordan Date: Wed, 16 May 2018 16:37:10 +0200 Subject: [PATCH 1/3] Provide generation and individual id to objective in snes --- es/separable_natural_es.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/es/separable_natural_es.py b/es/separable_natural_es.py index a1bd3de..7dc8b61 100644 --- a/es/separable_natural_es.py +++ b/es/separable_natural_es.py @@ -2,6 +2,7 @@ import numpy as np from . import lib + def optimize(func, mu, sigma, learning_rate_mu=None, learning_rate_sigma=None, population_size=None, max_iter=2000, @@ -45,13 +46,13 @@ def optimize(func, mu, sigma, z = np.vstack([z, mu - sigma * s]) s = np.vstack([s, -s]) + generations_list = [generation] * len(z) + individual_list = range(len(z)) if parallel_threads is None: - fitness = np.fromiter((func(zi) for zi in z), np.float) + fitness = np.fromiter((func(zi, gi, ii) for zi, gi, ii in zip(z, generations_list, individual_list)), np.float) else: - pool = mp.Pool(processes=parallel_threads) - fitness = np.fromiter(pool.map(func, z), np.float) - pool.close() - pool.join() + with mp.Pool(processes=parallel_threads) as pool: + fitness = np.fromiter(pool.starmap(func, zip(z, generations_list, individual_list)), np.float) ni = np.logical_not(np.isnan(fitness)) z = z[ni] @@ -87,4 +88,3 @@ def optimize(func, mu, sigma, 'history_sigma': history_sigma, 'history_fitness': history_fitness, 'history_pop': history_pop} - \ No newline at end of file From c37083c321e349b176496fbda148f5d8c189adb5 Mon Sep 17 00:00:00 2001 From: Jakob Jordan Date: Mon, 2 Jul 2018 14:55:21 +0200 Subject: [PATCH 2/3] Add logging to snes --- es/separable_natural_es.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/es/separable_natural_es.py b/es/separable_natural_es.py index 7dc8b61..108aab2 100644 --- a/es/separable_natural_es.py +++ b/es/separable_natural_es.py @@ -1,20 +1,32 @@ +import logging import multiprocessing as mp import numpy as np from . import lib +logger = logging.getLogger(__name__) + def optimize(func, mu, sigma, learning_rate_mu=None, learning_rate_sigma=None, population_size=None, max_iter=2000, fitness_shaping=True, mirrored_sampling=True, record_history=False, rng=None, - parallel_threads=None): + parallel_threads=None, + verbosity=logging.WARNING): """ Evolution strategies using the natural gradient of multinormal search distributions in natural coordinates. Does not consider covariances between parameters. See Wierstra et al. (2014). Natural evolution strategies. Journal of Machine Learning Research, 15(1), 949-980. """ + logger_ch = logging.StreamHandler() + logger_fh = logging.FileHandler('snes.log', 'w') + logger_ch.setFormatter(logging.Formatter('%(asctime)s %(levelname)s:%(name)s %(message)s')) + logger_fh.setFormatter(logging.Formatter('%(asctime)s %(levelname)s:%(name)s %(message)s')) + logger.setLevel(verbosity) + logger.addHandler(logger_ch) + logger.addHandler(logger_fh) + if not isinstance(mu, np.ndarray): raise TypeError('mu needs to be of type np.ndarray') if not isinstance(sigma, np.ndarray): @@ -38,6 +50,7 @@ def optimize(func, mu, sigma, history_pop = [] history_fitness = [] + logger.info('starting evolution with {} individuals per generation on {} threads'.format(population_size * (1 + int(mirrored_sampling)), parallel_threads)) while True: s = rng.normal(0, 1, size=(population_size, *np.shape(mu))) z = mu + sigma * s @@ -70,6 +83,11 @@ def optimize(func, mu, sigma, mu += learning_rate_mu * sigma * np.dot(utility, s) sigma *= np.exp(learning_rate_sigma / 2. * np.dot(utility, s ** 2 - 1)) + logger.info('generation {}, average fitness {}'.format(generation, np.mean(fitness))) + logger.debug('fitness {}'.format(fitness)) + logger.debug('mu {}'.format(mu)) + logger.debug('sigma {}'.format(sigma)) + if record_history: history_mu.append(mu.copy()) history_sigma.append(sigma.copy()) @@ -79,7 +97,10 @@ def optimize(func, mu, sigma, generation += 1 # exit if max iterations reached - if generation > max_iter or np.all(sigma < 1e-10): + if generation > max_iter: + logger.info('maximum number of iterations reached - exiting') + break + elif np.all(sigma < 1e-10): break return {'mu': mu, From 5fa10c5c99ec0f8ba3b6c04ce113cf23f7bd9098 Mon Sep 17 00:00:00 2001 From: Jakob Jordan Date: Mon, 2 Jul 2018 16:25:19 +0200 Subject: [PATCH 3/3] Add more logging output --- es/separable_natural_es.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/es/separable_natural_es.py b/es/separable_natural_es.py index 108aab2..048a335 100644 --- a/es/separable_natural_es.py +++ b/es/separable_natural_es.py @@ -50,7 +50,11 @@ def optimize(func, mu, sigma, history_pop = [] history_fitness = [] - logger.info('starting evolution with {} individuals per generation on {} threads'.format(population_size * (1 + int(mirrored_sampling)), parallel_threads)) + n_total_individuals = population_size * (1 + int(mirrored_sampling)) + logger.info('starting evolution with {} individuals per generation on {} threads'.format(n_total_individuals, parallel_threads)) + if (parallel_threads is None and n_total_individuals > 1) or (parallel_threads is not None and n_total_individuals > parallel_threads): + logger.warning('more individuals than parallel threads. expect long runtime') + while True: s = rng.normal(0, 1, size=(population_size, *np.shape(mu))) z = mu + sigma * s @@ -101,6 +105,7 @@ def optimize(func, mu, sigma, logger.info('maximum number of iterations reached - exiting') break elif np.all(sigma < 1e-10): + logger.info('convergence of sigma detected - exiting') break return {'mu': mu,