From e430a0a35552c3e4f147f864f79549593f3c1921 Mon Sep 17 00:00:00 2001 From: tjkessler Date: Sun, 31 Mar 2019 14:40:07 -0400 Subject: [PATCH 1/8] PEP8 styling overhaul, logical method placement --- ecabc/__init__.py | 2 +- ecabc/abc.py | 627 ++++++++++++++++++++++++++++++---------------- ecabc/bees.py | 78 ++++-- setup.py | 24 +- 4 files changed, 474 insertions(+), 257 deletions(-) diff --git a/ecabc/__init__.py b/ecabc/__init__.py index 2aa2b27..274aa27 100644 --- a/ecabc/__init__.py +++ b/ecabc/__init__.py @@ -1,3 +1,3 @@ import ecabc.abc import ecabc.bees -__version__ = '2.2.2' +__version__ = '2.2.3' diff --git a/ecabc/abc.py b/ecabc/abc.py index 2774454..404b9bd 100644 --- a/ecabc/abc.py +++ b/ecabc/abc.py @@ -2,46 +2,80 @@ # -*- coding: utf-8 -*- # # ecabc/abc.py -# v.2.2.2 -# Developed in 2018 by Sanskriti Sharma & Hernan Gelaf-Romer +# v.2.2.3 # -# This program implements an artificial bee colony to tune ecnet hyperparameters +# Developed in 2019 by Sanskriti Sharma +# & Hernan Gelaf-Romer +# +# This file implements an artificial bee colony to tune user-supplied +# function parameters # -import sys as sy +# Stdlib imports import os.path from random import randint -import numpy as np -from colorlogging import ColorLogger -import pickle import multiprocessing from copy import deepcopy +# 3rd party imports +import numpy as np +from colorlogging import ColorLogger try: import ujson as json except: import json as json -# artificial bee colony packages +# ECabc imports from ecabc.bees import OnlookerBee, EmployerBee class ABC: - ''' - ABC object: Manages employer and onlooker bees to optimize a set of generic values - given a generic user defined fitness function. Handles data transfer and manipulation - between bees. - ''' + def __init__(self, fitness_fxn, num_employers=50, value_ranges=[], + print_level='info', file_logging='disable', args={}, + processes=4): + '''ABC object: manages employer and onlooker bees to optimize a set + of generic values for a user-supplied fitness function. Handles data + transfer and manipulation between bees. - def __init__(self, fitness_fxn, num_employers=50, value_ranges=[], print_level='info', file_logging='disable', args={}, processes=4): - self._logger = ColorLogger(stream_level=print_level, file_level=file_logging) + Args: + fitness_fxn (callable): fitness function supplied by the user; + should accept a tuple of tunable ints/floats, and optionally + additional user-defined arguments (kwargs) + num_employers (int): number of employer bees the colony utilizes + value_ranges (list): each element defines a tunable variable in + the form "(type ('int' or 'float'), (min_val, max_val))"; + initial, random values for each bee will between "min_val" and + "max_val" + print_level (string): console logging level: "debug", "info", + "warn", "crit", "error", "disable" + file_logging (string): file logging level: "debug", "info", "warn", + "crit", "error", "disable" + args (dict): additional user-defined arguments to pass to fitness + function; these are not tuned + processes (int): number of concurrent processes the algorithm will + utililze via multiprocessing.Pool + + Return: + None + ''' + + self._logger = ColorLogger( + stream_level=print_level, + file_level=file_logging + ) self._value_ranges = value_ranges + if num_employers < 2: + self._logger.log( + 'warn', + 'Two employers are needed: setting to two' + ) + num_employers = 2 self._num_employers = num_employers self._best_values = [] self._best_score = None self._best_error = None - self._minimize = True #minimizes error not score + self._minimize = True self._fitness_fxn = fitness_fxn self.__onlooker = OnlookerBee() self._limit = num_employers*len(value_ranges) @@ -60,182 +94,237 @@ def __init__(self, fitness_fxn, num_employers=50, value_ranges=[], print_level=' raise ValueError('submitted *fitness_fxn* is not callable') def add_argument(self, arg_name, arg_value): - ''' - Add an argument that will be processed by the fitness - function. Doing this after you have initiliazed the abc - employers and have started running the abc may produce - some weird results + '''Add an additional argument to be passed to the fitness function + via additional arguments dictionary; this argument/value is not tuned Args: - arg_name: The keyword name of your argument - arg_value: The value of the given argument + arg_name (string): name/dictionary key of argument + arg_value (any): dictionary value of argument ''' + if len(self._employers) > 0: - self._logger.log('warn', 'Adding an argument after the employers have been created') + self._logger.log( + 'warn', + 'Adding an argument after the employers have been created' + ) if self._args is None: self._args = {} self._args[arg_name] = arg_value def add_value(self, value_type, value_min, value_max): - ''' - Add another value that will be factored into the calculation - of the bee's fitness. Calling this after the abc has run for - a few iterations may produce wonky results + '''Add a tunable value to the ABC (fitness function must be + configured to handle it) + Args: - value_type: Either of type 'int' or type 'float' - value_min: Minimum numerical value - value_max: Maximum numerical value + value_type (string): type of the value, 'int' or 'float' + value_min (int or float): minimum bound for the value + value_max (int or float): maximum bound for the value + + Returns: + None ''' + if len(self._employers) > 0: - self._logger.log('warn', 'Adding a value after employers have been created') + self._logger.log( + 'warn', + 'Adding a value after employers have been created' + ) value = (value_type, (value_min, value_max)) self._value_ranges.append(value) + self._limit = self._num_employers*len(self._value_ranges) + self._logger.log( + 'debug', + 'Limit set to {}'.format(self._limit) + ) @property def args(self): - ''' - Arguments that will be passed to the fitness function at runtime - ''' + '''Arguments that will be passed to the fitness function at runtime''' + return self._args @args.setter def args(self, args): + '''Set additional arguments to be passed to the fitness function + + Args: + args (dict): additional arguments + ''' self._args = args - self._logger.log('debug', "Args set to {}".format(args)) + self._logger.log('debug', 'Args set to {}'.format(args)) @property def minimize(self): + '''If True, minimizes fitness function return value rather than + derived score ''' - Boolean value that describes whether the bee colony is minimizing - or maximizing the generic fitness function - ''' + return self._minimize @minimize.setter def minimize(self, minimize): + '''Configures the ABC to minimize fitness function return value or + derived score + + Args: + minimize (bool): if True, minimizes fitness function return value; + if False, minimizes derived score + ''' + self._minimize = minimize - self._logger.log('debug', "Minimize set to {}".format(minimize)) + self._logger.log('debug', 'Minimize set to {}'.format(minimize)) @property def num_employers(self): + '''Number of employer bees present in the ABC''' + return self._num_employers @num_employers.setter def num_employers(self, num_employers): - if num_employers < 10: - self._logger.log('warn', "Cannot set num_employers to < 10, setting to 10") - self._num_employers = 10 - else: - self._num_employers = num_employers - self._logger.log('debug', "Number of employers set to {}".format(num_employers)) - #self._limit = num_employers * len(self._value_ranges) - self._logger.log('info','Limit . is {} ({} * {})'.format(self._limit, num_employers, len(self._value_ranges))) + '''Sets the number of employer bees; at least two are required + + Args: + num_employers (int): number of employer bees + ''' + + if num_employers < 2: + self._logger.log( + 'warn', + 'Two employers are needed: setting to two' + ) + num_employers = 2 + self._num_employers = num_employers + self._logger.log('debug', 'Number of employers set to {}'.format( + num_employers + )) + self._limit = num_employers * len(self._value_ranges) + self._logger.log('debug', 'Limit set to {}'.format(self._limit)) @property def value_ranges(self): + '''Value types, min/max values for tunable parameters''' + return self._value_ranges @value_ranges.setter def value_ranges(self, value_ranges): + '''Set the types, min/max values for tunable parameters + + Args: + value_ranges (list): each element defines a tunable variable in + the form "(type ('int' or 'float'), (min_val, max_val))"; + initial, random values for each bee will between "min_val" and + "max_val" + ''' + self._value_ranges = value_ranges - self._logger.log('debug', "Value ranges set to {}".format(value_ranges)) + self._logger.log('debug', 'Value ranges set to {}'.format( + value_ranges + )) @property def best_performer(self): - ''' - Return the best performing values: (score, values, error) - ''' + '''Return the best performing values: (score, values, error)''' + return (self._best_score, self._best_values, self._best_error) @property def best_employers(self): - ''' - Return a list of best performing employer bees - ''' + '''Return a list of best performing employer bees''' + return self.__onlooker.best_employers @property def limit(self): + '''Maximum number of cycles a bee is allowed to stay at its current + food source before abandoning it (moving to a randomly generated one) ''' - Get the maximum amount of times a bee can perform below average - before completely abandoning its current food source and seeking - a randomly generated one - ''' + return self._limit @limit.setter def limit(self, limit): + '''Set the maximum number of cycles a bee is allowed to stay at its + current food source before abandoning it (moving to a randomly + generated one); by default, this is set to the number of employers + times the number of tunable values + + Args: + limit (int): maximum number of cycles ''' - Set the maximum amount of times a bee can perform below average - before completely bandoning its current food source and seeking - a randomly generate done - ''' + self._limit = limit @property def processes(self): + '''How many concurrent processes the ABC will utililze for fitness + function evaluation via multiprocessing.Pool ''' - Value which indicates how many processes are allowed to be a spawned - for various methods/calculations at a time. If the number is less than 1, - multiprocessing will be disabled and the program will run everything synchroniously - ''' + return self._processes @processes.setter def processes(self, processes): + '''Set the number of concurrent processes the ABC will utilize for + fitness function evaluation; if <= 1, single process is used + + Args: + processes (int): number of concurrent processes + ''' + if self._processes > 1: self._pool.close() self._pool.join() - - self._processes = processes - if self._processes > 1: self._pool = multiprocessing.Pool(processes) else: self._pool = None - self._logger.log('debug', "Number of processes set to {}".format(processes)) + self._logger.log('debug', 'Number of processes set to {}'.format( + processes + )) def infer_process_count(self): + '''Infers the number of CPU cores in the current system, sets the + number of concurrent processes accordingly ''' - Set the amount of processes that will be used to - the amount of CPU's in your system - ''' + try: self.processes = multiprocessing.cpu_count() except NotImplementedError: - self._logger.log('error', "Could not get cpu count, setting amount of processes back to 4") + self._logger.log( + 'error', + 'Could infer CPU count, setting number of processes back to 4' + ) self.processes = 4 - def run_iteration(self): - ''' - Run a single iteration of the bee colony. This will run the employer phase - on an already created set of employer bees, calculate the probablities of - each bee, run the onlooker phase and then check positions. At the end of - this method, the best_performer attribute may or may not have been updated - if a better food source was found - ''' - self._employer_phase() - self._calc_probability() - self._onlooker_phase() - self._check_positions() - def create_employers(self): + '''Generate employer bees. This should be called directly after the + ABC is initialized. ''' - Generate a set of employer bees. This method must be called in order to generate a set - of usable employers bees. Other methods depend on this. - ''' + self.__verify_ready(True) employers = [] for i in range(self._num_employers): employer = EmployerBee(self.__gen_random_values()) if self._processes <= 1: - employer.error = self._fitness_fxn(employer.values, **self._args) + employer.error = self._fitness_fxn( + employer.values, **self._args + ) employer.score = employer.get_score() if np.isnan(employer.score): - print("NAN:" + str(employer.error)) - self._logger.log('debug', "Bee number {} created".format(i + 1)) + self._logger.log('warn', 'NaN bee score: {}, {}'.format( + employer.id, employer.score + )) + self._logger.log('debug', 'Bee number {} created'.format( + i + 1 + )) self.__update(employer.score, employer.values, employer.error) else: - employer.error = self._pool.apply_async(self._fitness_fxn, [employer.values], self._args) + employer.error = self._pool.apply_async( + self._fitness_fxn, + [employer.values], + self._args + ) employers.append(employer) self._employers.append(employer) for idx, employer in enumerate(employers): @@ -243,19 +332,35 @@ def create_employers(self): employer.error = employer.error.get() employer.score = employer.get_score() if np.isnan(employer.score): - print("NAN:" + str(employer.error)) - self._logger.log('debug', "Bee number {} created".format(idx + 1)) + self._logger.log('warn', 'NaN bee score: {}, {}'.format( + employer.id, employer.score + )) + self._logger.log('debug', 'Bee number {} created'.format( + i + 1 + )) self.__update(employer.score, employer.values, employer.error) except Exception as e: raise e - self._logger.log('debug','Employer creation complete') + self._logger.log('debug', 'Employer creation complete') - def _employer_phase(self): + def run_iteration(self): + '''Runs a single iteration of the ABC; employer phase -> probability + calculation -> onlooker phase -> check positions ''' - Part of run_iteration. Iterates through the employer bees and calls merge bee on each - bee so get a new position and then moves bee to the new position. + + self._employer_phase() + self._calc_probability() + self._onlooker_phase() + self._check_positions() + + def _employer_phase(self): + '''Iterates through the employer bees and merges each with another + random bee (one value is moved in accordance with the second bee's + value); if the mutation performs better, the bee is moved to the new + position ''' - self._logger.log('debug',"Employer bee phase") + + self._logger.log('debug', 'Employer bee phase') modified = [] for bee in self._employers: if self._processes <= 1: @@ -263,41 +368,56 @@ def _employer_phase(self): self._move_bee(bee, new_values) else: modified.append(( - bee, + bee, self._pool.apply_async(self._merge_bee, [bee]) )) for pair in modified: self._move_bee(pair[0], pair[1].get()) - def _move_bee(self, bee, new_values): - ''' - Moves bee to passed in position - Args: - new_values: The new position which is the output of merge bee + def _calc_probability(self): + '''Determines the probability that each bee will be chosen during the + onlooker phase; also determines if a new best-performing bee is found ''' - if bee.score > new_values[0]: - bee.failed_trials += 1 - else: - bee.values = new_values[1] - bee.score = np.nan_to_num(new_values[0]) - bee.error = new_values[2] - bee.failed_trials = 0 - self._logger.log('debug', "Bee assigned to new merged position") + + self._logger.log('debug', 'Calculating bee probabilities') + self.__verify_ready() + self._total_score = 0 + for employer in self._employers: + self._total_score += employer.score + if self.__update(employer.score, employer.values, employer.error): + self._logger.log( + 'info', + 'Update to best performer -' + ' error: {} | score: {} | values: {}'.format( + employer.error, + employer.score, + employer.values + ) + ) + for employer in self._employers: + employer.calculate_probability(self._total_score) def _onlooker_phase(self): + '''Well-performing bees (chosen probabilistically based on fitness + score) have a value merged with a second random bee ''' - Chooses employer bee based on calculated probability. For that bee, merges bee then moves bee - (similar to employer phase) - ''' + self.__verify_ready() - self._logger.log('debug',"Onlooker bee phase") + self._logger.log('debug', 'Onlooker bee phase') modified = [] for _ in self._employers: - chosen_bee = np.random.choice(self._employers, p = [e.probability for e in self._employers]) + chosen_bee = np.random.choice( + self._employers, + p=[e.probability for e in self._employers] + ) if self._processes <= 1: new_values = self._merge_bee(chosen_bee) self._move_bee(chosen_bee, new_values) - self.__update(chosen_bee.score, chosen_bee.values, chosen_bee.error) + self.__update( + chosen_bee.score, + chosen_bee.values, + chosen_bee.error + ) else: modified.append(( chosen_bee, @@ -306,30 +426,13 @@ def _onlooker_phase(self): for pair in modified: self._move_bee(pair[0], pair[1].get()) self.__update(pair[0].score, pair[0].values, pair[0].error) - - def _calc_probability(self): - ''' - Calculates the total of bee score for probability. Will also update - the best score and calls employer bee method calculate probability - ''' - self._logger.log('debug', "calculating total") - self.__verify_ready() - self._total_score = 0 - for employer in self._employers: - self._total_score += employer.score - # While iterating through employers, look for the best fitness score/value pairing - if self.__update(employer.score, employer.values, employer.error): - self._logger.log('info', "Best score update to error: {} | score: {} | values: {}".format(employer.error, employer.score, employer.values)) - - # Now calculate each bee's probability - for employer in self._employers: - employer.calculate_probability(self._total_score) def _check_positions(self): + '''Checks each bee to see if it abandons its current food source (has + not found a better one in self._limit iterations); if abandoning, it + becomes a scout and generates a new, random food source ''' - If the bee with the highest number of failed trials is above th limit, it is - converted into a scout bee which means it gets a whole new position in all dimensions - ''' + self.__verify_ready() max_trials = 0 scout = None @@ -337,126 +440,206 @@ def _check_positions(self): if (bee.failed_trials >= max_trials): max_trials = bee.failed_trials scout = bee - if scout != None and scout.failed_trials > self._limit: - self._logger.log('debug', "Sending scout (error of {} with limit of {})".format(scout.error, scout.failed_trials)) - scout.values = self. __gen_random_values() - - def import_settings(self, filename): - ''' - Import settings from a JSON file - ''' - if not os.path.isfile(filename): - self._logger.log('error', "file: {} not found, continuing with default settings".format(filename)) - else: - with open(filename, 'r') as jsonFile: - data = json.load(jsonFile) - self._value_ranges = data['valueRanges'] - self._best_values = data['best_values'] - self._best_values = [] - for index, value in enumerate(data['best_values']): - if self._value_ranges[index] == 'int': - self._best_values.append(int(value)) - else: - self._best_values.append(float(value)) - self.minimize = data['minimize'] - self.num_employers = data['num_employers'] - self._best_score = float(data['best_score']) - self.limit = data['limit'] - - def save_settings(self, filename): - ''' - Save settings to a JSON file - ''' - data = dict() - data['valueRanges'] = self._value_ranges - data['best_values'] = [str(value) for value in self._best_values] - data['minimize'] = self._minimize - data['num_employers'] = self._num_employers - data['best_score'] = str(self._best_score) - data['limit'] = self._limit - data['best_error'] = self._best_error - with open(filename, 'w') as outfile: - json.dump(data, outfile, indent=4, sort_keys=True) + if scout is not None and scout.failed_trials > self._limit: + self._logger.log( + 'debug', + 'Sending scout (error of {} with limit of {})'.format( + scout.error, scout.failed_trials + ) + ) + scout.values = self.__gen_random_values() def _merge_bee(self, bee): - ''' - A dimension and a second bee are randomly chosen. The first bee in shifted - in that dimension with respect to the second bee + '''Shifts a random value for a supplied bee with in accordance with + another random bee's value + Args: - bee: First employer bee + bee (EmployerBee): supplied bee to merge + + Returns: + tuple: (score of new position, values of new position, fitness + function return value of new position) ''' - # choose random dimension + random_dimension = randint(0, len(self._value_ranges) - 1) - # choose random 2nd bee - second_bee = randint(0, self._num_employers-1) - # Avoid both bees being the same + second_bee = randint(0, self._num_employers - 1) while (bee.id == self._employers[second_bee].id): - second_bee = randint(0, self._num_employers -1) - # bee[dimension] = bee[dimension] + rand(-1, 1) * (rand_bee[dimension] - bee[dimension]) + second_bee = randint(0, self._num_employers - 1) new_bee = deepcopy(bee) - new_bee.values[random_dimension] = self.__onlooker.calculate_positions(new_bee.values[random_dimension], - self._employers[second_bee].values[random_dimension], self._value_ranges[random_dimension]) - fitness_score = new_bee.get_score(self._fitness_fxn(new_bee.values, **self._args)) + new_bee.values[random_dimension] = self.__onlooker.calculate_positions( + new_bee.values[random_dimension], + self._employers[second_bee].values[random_dimension], + self._value_ranges[random_dimension] + ) + fitness_score = new_bee.get_score(self._fitness_fxn( + new_bee.values, + **self._args + )) return (fitness_score, new_bee.values, new_bee.error) - def __update(self, score, values, error): + def _move_bee(self, bee, new_values): + '''Moves a bee to a new position if new fitness score is better than + the bee's current fitness score + + Args: + bee (EmployerBee): bee to move + new_values (tuple): (new score, new values, new fitness function + return value) ''' - Update the best score and values if the given - score is better than the current best score + + score = np.nan_to_num(new_values[0]) + if bee.score > score: + bee.failed_trials += 1 + else: + bee.values = new_values[1] + bee.score = score + bee.error = new_values[2] + bee.failed_trials = 0 + self._logger.log('debug', 'Bee assigned to new merged position') + + def __update(self, score, values, error): + '''Update the best score and values if the given score is better than + the current best score + + Args: + score (float): new score to evaluate + values (list): new value ranges to evaluate + error (float): new fitness function return value to evaluate + + Returns: + bool: True if new score is better, False otherwise ''' + if self._minimize: - if self._best_score == None or score > self._best_score: + if self._best_score is None or score > self._best_score: self._best_score = score self._best_values = values.copy() self._best_error = error - self._logger.log('debug','New best food source memorized: {}'.format(self._best_error)) + self._logger.log( + 'debug', + 'New best food source memorized: {}'.format( + self._best_error + ) + ) return True elif not self._minimize: - if self._best_score == None or score < self._best_score: + if self._best_score is None or score < self._best_score: self._best_score = score self._best_values = values.copy() self._best_error = error - self._logger.log('debug','New best food source memorized: {}'.format(self._best_error)) + self._logger.log( + 'debug', + 'New best food source memorized: {}'.format( + self._best_error + ) + ) return True return False def __gen_random_values(self): + '''Generate random values based on supplied value ranges + + Returns: + list: random values, one per tunable variable ''' - Generate a random list of values based on the allowed value ranges - ''' + values = [] - if self._value_ranges == None: - self._logger.log('crit', "must set the type/range of possible values") - raise RuntimeError("must set the type/range of possible values") + if self._value_ranges is None: + self._logger.log( + 'crit', + 'Must set the type/range of possible values' + ) + raise RuntimeError("Must set the type/range of possible values") else: - # t[0] contains the type of the value, t[1] contains a tuple (min_value, max_value) for t in self._value_ranges: if t[0] == 'int': values.append(randint(t[1][0], t[1][1])) elif t[0] == 'float': values.append(np.random.uniform(t[1][0], t[1][1])) else: - self._logger.log('crit', "value type must be either an 'int' or a 'float'") - raise RuntimeError("value type must be either an 'int' or a 'float'") + self._logger.log( + 'crit', + 'Value type must be either an `int` or a `float`' + ) + raise RuntimeError( + 'Value type must be either an `int` or a `float`' + ) return values def __verify_ready(self, creating=False): + '''Some cleanup, ensures that everything is set up properly to avoid + random errors during execution + + Args: + creating (bool): True if currently creating employer bees, False + for checking all other operations ''' - Some cleanup, ensures that everything is set up properly to avoid random - errors during execution - ''' + if len(self._value_ranges) == 0: - self._logger.log('crit', 'Attribute value_ranges must have at least one value') - raise RuntimeWarning('Attribute value_ranges must have at least one value') - if len(self._employers) == 0 and creating == False: - self._logger.log('crit', "Need to create employers") - raise RuntimeWarning("Need to create employers") + self._logger.log( + 'crit', + 'Attribute value_ranges must have at least one value' + ) + raise RuntimeWarning( + 'Attribute value_ranges must have at least one value' + ) + if len(self._employers) == 0 and creating is False: + self._logger.log('crit', 'Need to create employers') + raise RuntimeWarning('Need to create employers') - def __getstate__(self): + def import_settings(self, filename): + '''Import settings from a JSON file + + Args: + filename (string): name of the file to import from ''' - Returns appropriate dictionary for correctly - pickling the ABC object in case of multiprocssing + + if not os.path.isfile(filename): + self._logger.log( + 'error', + 'File: {} not found, continuing with default settings'.format( + filename + ) + ) + else: + with open(filename, 'r') as jsonFile: + data = json.load(jsonFile) + self._value_ranges = data['valueRanges'] + self._best_values = data['best_values'] + self._best_values = [] + for index, value in enumerate(data['best_values']): + if self._value_ranges[index] == 'int': + self._best_values.append(int(value)) + else: + self._best_values.append(float(value)) + self.minimize = data['minimize'] + self.num_employers = data['num_employers'] + self._best_score = float(data['best_score']) + self.limit = data['limit'] + + def save_settings(self, filename): + '''Save settings to a JSON file + + Arge: + filename (string): name of the file to save to + ''' + + data = dict() + data['valueRanges'] = self._value_ranges + data['best_values'] = [str(value) for value in self._best_values] + data['minimize'] = self._minimize + data['num_employers'] = self._num_employers + data['best_score'] = str(self._best_score) + data['limit'] = self._limit + data['best_error'] = self._best_error + with open(filename, 'w') as outfile: + json.dump(data, outfile, indent=4, sort_keys=True) + + def __getstate__(self): + '''Returns appropriate dictionary for correctly pickling the ABC + object in case of multiprocssing ''' + state = self.__dict__.copy() del state['_logger'] del state['_pool'] diff --git a/ecabc/bees.py b/ecabc/bees.py index fe6e44f..1018e54 100644 --- a/ecabc/bees.py +++ b/ecabc/bees.py @@ -2,17 +2,22 @@ # -*- coding: utf-8 -*- # # ecabc/bees.py -# v.2.2.2 -# Developed in 2018 by Sanskriti Sharma & Hernan Gelaf-Romer +# v.2.2.3 +# +# Developed in 2019 by Sanskriti Sharma +# & Hernan Gelaf-Romer # # This program defines the bee objects created in the artificial bee colony # -import numpy as np +# Stdlib imports from random import randint - import uuid +# 3rd party imports +import numpy as np + + class EmployerBee: ''' Class which stores individual employer bee information such as its id, @@ -21,6 +26,15 @@ class EmployerBee: ''' def __init__(self, values=[]): + '''EmployerBee object: stores individual employer bee information such + as ID, position (current values), fitness function return value, + fitness score, and probability of being chosen during the onlooker + phase + + Args: + values (list): position/current values for the bee + ''' + self.values = values self.score = None self.probability = 0 @@ -29,13 +43,16 @@ def __init__(self, values=[]): self.error = None def get_score(self, error=None): - ''' - Calculate bee's score given it's error. - + '''Calculate bee's fitness score given a value returned by the fitness + function + Args: - error: The error generated by an employer bee by running the - fitness function + error (float): value returned by the fitness function + + Returns: + float: derived fitness score ''' + if error is not None: self.error = error if self.error >= 0: @@ -44,13 +61,15 @@ def get_score(self, error=None): return 1 + abs(self.error) def calculate_probability(self, fitness_total): - ''' - Calculates probability based on a given fitness total - + '''Calculates the probability that the bee is chosen during the + onlooker phase + Args: - fitness_total: The sum of the fitness scores of all the employer bees + fitness_total (float): sum of fitness scores from all bees ''' - self.probability = self.score / fitness_total + + self.probability = self.score / fitness_total + class OnlookerBee: ''' @@ -59,21 +78,32 @@ class OnlookerBee: ''' def __init__(self): + '''OnlookerBee object: stores best-performing bees, function for + calculating merged position of two bees + ''' + self.best_employers = [] def calculate_positions(self, first_bee_val, second_bee_val, value_range): - ''' - Calculate the positions when merging two bees - + '''Calculate the new value/position for two given bee values + Args: - first_bee_val: The position of the first bee in a single dimension - second bee_val: The position of the second bee in a single dimension - value_range: A tuple containing the upper and lower limits of the dimension + first_bee_val (int or float): value from the first bee + second_bee_val (int or float): value from the second bee + value_ranges (tuple): "(value type, (min_val, max_val))" for the + given value + + Returns: + int or float: new value ''' + value = first_bee_val + np.random.uniform(-1, 1) \ - * (first_bee_val - second_bee_val) - if value_range[0] == 'int': value = int(value) - if value > value_range[1][1]: value = value_range[1][1] - if value < value_range[1][0]: value = value_range[1][0] + * (first_bee_val - second_bee_val) + if value_range[0] == 'int': + value = int(value) + if value > value_range[1][1]: + value = value_range[1][1] + if value < value_range[1][0]: + value = value_range[1][0] return value diff --git a/setup.py b/setup.py index 27750dd..8421c35 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,16 @@ from setuptools import setup -setup(name = 'ecabc', -version = "2.2.2", -description = 'Artificial bee colony for parameters tuning based on fitness scores', -url = 'https://github.com/ECRL/ecabc', -author = 'Sanskriti Sharma, Hernan Gelaf-Romer', -author_email = 'Sanskriti_Sharma@student.uml.edu, Hernan_Gelafromer@student.uml.edu', -license = 'MIT', -packages = ['ecabc'], -install_requires = ["numpy", "ColorLogging"], -zip_safe = False) +setup( + name='ecabc', + version='2.2.3', + description='Artificial bee colony for parameters tuning based on fitness' + ' scores', + url='https://github.com/ECRL/ecabc', + author='Sanskriti Sharma, Hernan Gelaf-Romer', + author_email='Sanskriti_Sharma@student.uml.edu, ' + 'Hernan_Gelafromer@student.uml.edu', + license='MIT', + packages=['ecabc'], + install_requires=['numpy', 'ColorLogging'], + zip_safe=False +) From d3adca39080a5d428c9737ddf3ff239faeb5169e Mon Sep 17 00:00:00 2001 From: tjkessler Date: Sun, 31 Mar 2019 14:44:13 -0400 Subject: [PATCH 2/8] Removed redundant docstrings --- ecabc/bees.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ecabc/bees.py b/ecabc/bees.py index 1018e54..bda0cd0 100644 --- a/ecabc/bees.py +++ b/ecabc/bees.py @@ -19,11 +19,6 @@ class EmployerBee: - ''' - Class which stores individual employer bee information such as its id, - position (known as values), error, score and probability of getting chosen - in the onlooker phase. - ''' def __init__(self, values=[]): '''EmployerBee object: stores individual employer bee information such @@ -72,10 +67,6 @@ def calculate_probability(self, fitness_total): class OnlookerBee: - ''' - Class to store best performing bees, and also calculate positions - for any given bees - ''' def __init__(self): '''OnlookerBee object: stores best-performing bees, function for From 4a2624fa351904531192691716ce6185f5c1d76c Mon Sep 17 00:00:00 2001 From: Sanskriti Sharma Date: Thu, 11 Apr 2019 15:14:19 -0400 Subject: [PATCH 3/8] Joss paper files --- paper/paper.bib | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ paper/paper.md | 42 ++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 paper/paper.bib create mode 100644 paper/paper.md diff --git a/paper/paper.bib b/paper/paper.bib new file mode 100644 index 0000000..9931635 --- /dev/null +++ b/paper/paper.bib @@ -0,0 +1,68 @@ +@online{ecabc_github, +author = {Sanskriti Sharma}, +title = {ECabc: A feature tuning program focused on Artificial Neural Network hyperparameters}, +year = {2019}, +url = {https://github.com/ECRL/ECabc}, +urldate = {2019-04-12} +} + +@online{ecabc_pypi, +author = {Sanskriti Sharma, Hernan Gelaf-Romer}, +title = {ecabc 2.2.2}, +year = {2019}, +url = {https://pypi.python.org/pypi/ecabc/}, +urldate = {2019-04-12} +} + +@online{uml_ecrl, +author = {John Hunter Mack}, +title = {Energy and Combustion Research Laboratory}, +year = {2019}, +url = {http://faculty.uml.edu/Hunter_Mack/}, +urldate = {2019-04-12} +} + +@book{Hassoun_2009, +Author = {{Hassoun}, M.}, +Booktitle = {Fundamentals of Artificial Neural Networks, by Hassoun, Mohamad H.~ISBN 9788120313569 (HB).~Published by PHI Learning, Delhi, India, 2009.}, +Publisher = {PHI Learning}, +Title = {{Fundamentals of Artificial Neural Networks}}, +Year = 2009 +} + +@article{Castillo_2000, +author = {P. Castillo, et al}, +title = "{G-Prop: Global Optimization of Multilayer Perceptrons Using GAs}", +journal = {Neurocomputer}, +year = 2000, +month = may, +volume = 35, +doi = {10.1016/s0925-2312(00)00302-7}, +} + +@proceedings{Cam_2015, +author = {Z. Cam et al}, +title = {Learning Parameter Optimization of Multi-Layer Perceptron Using Artificial Bee Colony, Genetic Algorithm and Particle Swarm Optimization}, +booktitle = {2015 IEEE 13th International Symposium on Applied Machine Intelligence and Informatics}, +publisher = {IEEE}, +year = {2015}, +doi = {10.1109/sami.2015.7061899} +} + +@article{Kessler_2017_1, +author = {T. Kessler, et al}, +title = {ECNet: Large scale machine learning projects for fuel property prediction}, +journal = {Journal of Open Source Software}, +year = {2017}, +doi = {10.21105/joss.00401} +} + +@article{Kessler_2017_2, +author = {T. Kessler, et al}, +title = {Artificial neural network based predictions of cetane number for furanic biofuel additives}, +journal = {Fuel}, +volume = {206}, +pages = {171 - 179}, +year = {2017}, +doi = {10.1016/j.fuel.2017.06.015} +} diff --git a/paper/paper.md b/paper/paper.md new file mode 100644 index 0000000..2e4aa8b --- /dev/null +++ b/paper/paper.md @@ -0,0 +1,42 @@ +--- +title: 'ECabc: A feature tuning program focused on Artificial Neural Network hyperparameters' +tags: +- artificial bee colony +- hyperparameter optimization +- machine learning +- artificial neural networks +authors: +- name: Sanskriti Sharma +orcid: 0000-0002-9884-7351 +affiliation: 1 +- name: Hernan Gelaf-Romer +orcid: N/A +affiliation: 1 +- name: Travis Kessler +orcid: 0000-0002-7363-4050 +affiliation: 1 +- name: John Hunter Mack +orcid: N/A +affiliation: 1 +affiliations: +- name: UMass Lowell Energy and Combustion Research Laboratory +index: 1 +date: 12 April 2019 +bibliography: paper.bib +nocite: | +@ecabc_github, @ecabc_pypi, @uml_ecrl +--- + +# Summary + +A rich body of literature exists regarding the optimization of artificial neural networks (ANN), a method comprised of densely interconnected adaptive processing units [@Hassoun_2009]. This need for optimization arises from a large number of user-set parameters that greatly affect the quality of the ANN’s training. Traditionally, this has been done manually. However, presently with higher computing capacity, it is possible to utilize algorithmic approaches which greatly increase the speed of optimization while enhancing model performance. Commonly used optimization routines include genetic algorithms [@Castillo_2000] and particle swarm optimizations [@Cam_2015]. However, research by Z. Cam et al [@Cam_2015] has shown that an Artificial Bee Colony (ABC) performs best by providing higher accuracy and a shorter run time. + +ECabc is an open source Python package based on an Artificial Bee Colony, used for tuning functions such as hyperparameters of artificial neural networks. The method is modelled after the honey foraging techniques of bees, where the search space for solutions is in as many dimensions as the number of parameters being tuned. The algorithm is outlined in Figure 1. + +![state_diagram](/images/state_diagram.png) + +The algorithm begins with initializing a pre-decided number of bees and running the fitness function for each bee, thus evaluating them. The fitness function is a user defined function that is used to generate the solutions and the error associated with each employer bee's values at a given position. After evaluation, the bees enter the ‘Employer Bee Phase’ during which each bee is moved in one random ‘dimension’ and then reevaluated. If the new position provides a better fitness score, the old position is abandoned and the bee memorizes the new one. This is repeated for every employer bee. Next, ECabc enters the ‘Calculate Probabilities’ state where the fitness scores of all the employer bees are totaled and the best position is memorized. Then a probability is calculated for each bee, which is the bee’s fitness score divided by the total score. The next phase of the algorithm is the ‘Onlooker Bee Phase’, in which employer bees are chosen based on their probabilities, i.e. those with a higher probability will have a higher likelihood of being chosen. The chosen bee is then moved in one dimension and then reevaluated. Again, if the new score is better, the old score is forgotten in favor of it. Every time a bee is moved in one dimension but doesn’t choose the new position, the bee’s counter goes up. Then in the ‘Scout Bee Phase’, if the counter has hit a certain pre-decided number, the bee is given a new, randomly assigned position. Ultimately, the best fitness value of the entire population is compared to a pre-decided value. If it doesn’t meet that standard, the ECabc returns to the Employer Bee Phase. Alternatively, a number of iterations can be and for each iteration, the algorithm will return to the Employer Bee Phase. + +While it has several applications, ECabc has been successfully used by the Energy and Combustion Research Laboratory (ECRL) at the University of Massachusetts Lowell [@uml_ecrl] to tune the hyperparameters of ECNet, a large-scale machine learning project for predicting fuel properties [@Kessler_2017_1]. ECNet provides scientists an open source tool for predicting key fuel properties of potential next-generation biofuels, reducing the need for costly fuel synthesis and experimentation [@Kessler_2017_2]. By increasing the accuracy of ECNet and similar models efficiently, ECabc helps to provide a higher degree of confidence in discovering new, optimal fuels. A single run of ECabc on ECNet yielded a lower average root mean square error (RMSE) for cetane number (CN) and yield sooting index (YSI) when compared to the RMSE generated by a year of manual tuning. While the manual tuning generated an RMSE of 10.13, the ECabc was able to yield an RMSE of 8.06 in one run of 500 iterations. + +# References From d5e08fbbb2df0b6f2a163c5e4c633f8dc20d255c Mon Sep 17 00:00:00 2001 From: Sanskriti Sharma Date: Thu, 11 Apr 2019 19:14:13 -0400 Subject: [PATCH 4/8] Readme update --- README.md | 127 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 97 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 55571aa..241ea77 100644 --- a/README.md +++ b/README.md @@ -5,52 +5,119 @@ [![PyPI version](https://badge.fury.io/py/ecabc.svg)](https://badge.fury.io/py/ecabc) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ECRL/ecabc/blob/master/LICENSE) -**ECabc** is a generic, small scale feature tuning program that works with any **fitness function**, and **value set**. An **employer bee** is an object which stores a set of values, and a fitness score that correlates to that value, which are both passed by the user. The **onlooker bee** will create a new set of random values, which will then be assigned to a poorly performing employer bee as a replacement. +**ECabc** is a generic, small scale feature tuning program based on the Artificial Bee Colony by N. Karboga that imitates the honey foraging techniques of bees. ECabc optimizes user supplied functions called the **fitness function** using a given set of variables known as the **value set**. The bee colony consists of three types of bees: employers, onlookers and scouts. An **employer bee** is an object which stores a set of values and a **fitness score** that correlates to that value as well as the bee's probability of being picked by an onlooker bee. An **onlooker bee** is an object that chooses employer bees with a high probability and calculates new positions for them. The **scout bee** will create a new set of random values, which will then be assigned to a poorly performing employer bee as a replacement. -The fitness function that is passed must take a tuple of **(value_type, (value_min, value_max))**, with the value types allowed either being a type **float** or a type **int**. The value_type should be passed in as a string. The user may define whether they would like the fitness cost to be minimized or maximized. The user may also decide whether they would like visual feeback by turning print statements either one or off. - -All scores will be saved in a file that you can specify in the constructor argument. The file name will default to **settings.json** and will hold the information of each iterations best fitness score and values. +### Research applications +While it has several applications, ECabc has been successfully used by the Energy and Combustion Research Laboratory (ECRL) at the University of Massachusetts Lowell to tune the hyperparameters of ECNet, a large-scale machine learning project for predicting fuel properties. ECNet provides scientists an open source tool for predicting key fuel properties of potential next-generation biofuels, reducing the need for costly fuel synthesis and experimentation. By increasing the accuracy of ECNet and similar models efficiently, ECabc helps to provide a higher degree of confidence in discovering new, optimal fuels. A single run of ECabc on ECNet yielded a lower average root mean square error (RMSE) for cetane number (CN) and yield sooting index (YSI) when compared to the RMSE generated by a year of manual tuning. While the manual tuning generated an RMSE of 10.13, the ECabc was able to yield an RMSE of 8.06 in one run of 500 iterations. # Installation ### Prerequisites: -- Have python 3.5 installed +- Have python 3.X installed - Have the ability to install python packages ### Method 1: pip -If you are working in a Linux/Mac environment -- **sudo pip install ecabc** +If you are working in a Linux/Mac environment: +```python +sudo pip install ecabc +``` -Alternatively, in a windows environment, make sure you are running cmd as administrator -- **pip install ecabc** +Alternatively, in a windows environment, make sure you are running cmd as administrator: +```python +pip install ecabc +``` -Note: if multiple Python releases are installed on your system (e.g. 2.7 and 3.5), you may need to execute the correct version of pip. For Python 3.5, change **"pip install ecabc"** to **"pip3 install ecabc"**. +To update your version of ECabc to the latest release version, use +```python +pip install --upgrade ecabc +``` + +Note: if multiple Python releases are installed on your system (e.g. 2.7 and 3.6), you may need to execute the correct version of pip. For Python 3.6, change **"pip install ecabc"** to **"pip3 install ecabc"**. ### Method 2: From source -- Download the ECabc repository, navigate to the download location on the command line/terminal, and execute -**"python setup.py install"**. +- Download the ECabc repository, navigate to the download location on the command line/terminal, and execute: +```python +python setup.py install +``` Additional package dependencies (Numpy) will be installed during the ECabc installation process. -To update your version of ECabc to the latest release version, use "**pip install --upgrade ecabc**". - # Usage -The artificial bee colony can take a mulitude of parameters. -- **value_ranges**: Your value ranges. Values must be passed as a list of tuples with a **type/(min_value, max_value)** pairing. See the example file for more details. -- **fitness_fxn**: The user defined function that will be used to generate the cost of each employer bee's values. -- **file_logging**: Accepts a 'debug'/'info'/'warn'/'error'/'crit' or 'disable'. If set to disable, will not file log for abc. This **can** be quite exepensive. If your fitness function is trivial, this will add an unecessary amount of time to reach target goal. You should instead just output to console. This is set to disable by default. -- **print_level**: Accepts 'debug'/'info'/'warn'/'error'/'crit' or 'disable'. This will print out log information to the console, and is less costly compared to saving logs to a file. If set to disable, won't output to console. Defaults to logging.INFO. -- **processes**: Decide how many processes you'd like to have running at a time. A process will run the fitness function once per iteration. Processes run in parallel, and thus the more processes you utilize, the more fitness functions can run concurrently, cutting program run time significantly. If your fitness function takes 5 seconds to execute. Utilizing 50 bees, and 5 processes, calculating the values for all bees will take 50 seconds, rather than 250. Be mindful that this will increase CPU usage heavily, and you should be careful with how many processes you allow a time, to avoid a crash or computer freeze. **If your fitness function is trivial, set processes to 0. Process spawning is expensive, and only worth it for costly fitness functions.** Defaults to 4. -- **limit**: The maximum amount of times a bee will attempt to find a new food source before abandoning it's current food source for a new random one. A bee will compare food sources around it's own a 'limit' amount of times, before replacing its food source with a completely random one if not of these searches are better than where it currently is. This helps to avoid stagnation. This defaults to 20. -- **args**: Any additional arguments that your fitness function must take outside of the values given in value_ranges. This defaults to None. Expects a dictionary with a keyword - value pair. If the argument for you fitness function is called test_argument, and you'd like that value to be 10, then you would pass {'test_argument', 10} as an argument to the abc. - -The artificial bee colony also utilizes a variety to methods to toggle certain settings. -- **minimize**: If set to true, the bee colony will minimize the fitness function, otherwise it will maximize it. -- **import_settings**: Accepts a json file by name. If the file exists, the artificial bee colony will import and use these settings, then return True. If the file doesn't exist, an error message will be logged, settings will be set to default, and the function will return False. -- **save_settings**: Accepts a json file name. If the file exists, the artificial bee colony settings will be saved to this file. - -# 2.0.0 Update -Update 2.0.0 changed ecabc quite a bit. We have given more control to the user by making the program no longer self terminating. Instead the user can utilize the run_iteration method to run the abc, and surround it with a necessary loop in order to ensure the abc is working to their liking. An example of the abc in action can be seen in the code snippet below. + +To get started import ECabc +```python +from ecabc import * +``` +Then define your fitness function as a function. The fitness function is the user defined function whose solution is being optimized +```python +def fitness_function: +``` +After that, in the main function define your value ranges i.e. the user defined ranges for the variables which are being optimized +```python +values = [('int', (0,10)), ('int', (0,100)), ('float',(0,80)), ('float', (0, 360))] +``` +Optionally, one can also add args. Any additional arguments that your fitness function must take outside of the values given in value_ranges. This defaults to None. +```python +arguments = {'test_argument', 10} +``` +Then call ECabc as follows: +```python +abc = ABC(fitness_fxn=fitness_function, value_ranges=values, args = arguments) +``` +Certain setting also need to be toggled, such as +```python +abc._minimize = True +``` +And the settings can be imported and saved as follows +```python +abc._import_settings = example.json +abc._save_settings = output.json +``` +Then call create_employers on it to generate your population of employer bees. This ony needs to be done once +```python +abc.create_employers() +``` +After this, the code should enter a loop with a break condition. The contents of ECabc that should be in the loop have been encompassed in `run_iteration()` for simplicity. +```python +while True: +abc.run_iteration() +if (abc.best_performer[0] < 2): + break +``` +The above snippet shows the setup if one wants to run ECabc until a certain output value has been obtained. Alternatively one could just set it up so that it runs for a preset number of cycles as follows: +```python +for i in range(500): + run_iteration() +``` +Other parameters that can be specified in the loop are: +file logging: debug'/'info'/'warn'/'error'/'crit' or 'disable +```python +abc._logger.file_logging = 'info' +abc._logger.file_logging = 'debug' +abc._logger.file_logging = 'warn' +abc._logger.file_logging = 'error' +abc._logger.file_logging = 'crit' +abc._logger.file_logging = 'disable' +``` +print_level. This will print out log information to the console: +```python +abc._logger.print_level = 'info' +abc._logger.print_level = 'debug' +abc._logger.print_level = 'warn' +abc._logger.print_level = 'error' +abc._logger.print_level = 'crit' +abc._logger.print_level = 'disable' +``` +and processes: +```python +processes = 1 +``` +Finally, to view the output: +```python +print(abc.best_performer[2], abc.best_performer[1]) +``` +where best_performer[2] is the values and best_performer[1] is the fitness score associated with it. + # Example From 777edff3174781a75fde7e0a909d276c3a620008 Mon Sep 17 00:00:00 2001 From: Sanskriti Sharma Date: Fri, 12 Apr 2019 15:31:57 -0400 Subject: [PATCH 5/8] Joss corrections --- README.md | 38 ++++++++++++++++++++------------------ paper/images/state.png | Bin 0 -> 62779 bytes 2 files changed, 20 insertions(+), 18 deletions(-) create mode 100644 paper/images/state.png diff --git a/README.md b/README.md index 241ea77..857faf9 100644 --- a/README.md +++ b/README.md @@ -18,17 +18,17 @@ While it has several applications, ECabc has been successfully used by the Energ ### Method 1: pip If you are working in a Linux/Mac environment: -```python +``` sudo pip install ecabc ``` Alternatively, in a windows environment, make sure you are running cmd as administrator: -```python +``` pip install ecabc ``` To update your version of ECabc to the latest release version, use -```python +``` pip install --upgrade ecabc ``` @@ -36,7 +36,7 @@ Note: if multiple Python releases are installed on your system (e.g. 2.7 and 3.6 ### Method 2: From source - Download the ECabc repository, navigate to the download location on the command line/terminal, and execute: -```python +``` python setup.py install ``` @@ -48,9 +48,11 @@ To get started import ECabc ```python from ecabc import * ``` -Then define your fitness function as a function. The fitness function is the user defined function whose solution is being optimized +Then define your fitness function as a function. The fitness function is the user defined function whose solution is being optimized. Pass in the values and args and have it return the output that is being optimized ```python -def fitness_function: +def fitness_function(values,args): + ***code*** + return output ``` After that, in the main function define your value ranges i.e. the user defined ranges for the variables which are being optimized ```python @@ -92,21 +94,21 @@ for i in range(500): Other parameters that can be specified in the loop are: file logging: debug'/'info'/'warn'/'error'/'crit' or 'disable ```python -abc._logger.file_logging = 'info' -abc._logger.file_logging = 'debug' -abc._logger.file_logging = 'warn' -abc._logger.file_logging = 'error' -abc._logger.file_logging = 'crit' -abc._logger.file_logging = 'disable' +abc._logger.file_level = 'info' +abc._logger.file_level = 'debug' +abc._logger.file_level = 'warn' +abc._logger.file_level = 'error' +abc._logger.file_level = 'crit' +abc._logger.file_level = 'disable' ``` print_level. This will print out log information to the console: ```python -abc._logger.print_level = 'info' -abc._logger.print_level = 'debug' -abc._logger.print_level = 'warn' -abc._logger.print_level = 'error' -abc._logger.print_level = 'crit' -abc._logger.print_level = 'disable' +abc._logger.stream_level = 'info' +abc._logger.stream_level = 'debug' +abc._logger.stream_level = 'warn' +abc._logger.stream_level = 'error' +abc._logger.stream_level = 'crit' +abc._logger.stream_level = 'disable' ``` and processes: ```python diff --git a/paper/images/state.png b/paper/images/state.png new file mode 100644 index 0000000000000000000000000000000000000000..7cb0690363985a6690f030420f01910f297c2d2e GIT binary patch literal 62779 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 By43&x literal 0 HcmV?d00001 From ae8e742c534ca64ea1179e7a8e0f97e905b20ea7 Mon Sep 17 00:00:00 2001 From: Sanskriti Sharma Date: Fri, 12 Apr 2019 15:31:57 -0400 Subject: [PATCH 6/8] Joss corrections --- README.md | 38 ++++++++++++++++++++------------------ images/state.png | Bin 0 -> 62779 bytes 2 files changed, 20 insertions(+), 18 deletions(-) create mode 100644 images/state.png diff --git a/README.md b/README.md index 241ea77..857faf9 100644 --- a/README.md +++ b/README.md @@ -18,17 +18,17 @@ While it has several applications, ECabc has been successfully used by the Energ ### Method 1: pip If you are working in a Linux/Mac environment: -```python +``` sudo pip install ecabc ``` Alternatively, in a windows environment, make sure you are running cmd as administrator: -```python +``` pip install ecabc ``` To update your version of ECabc to the latest release version, use -```python +``` pip install --upgrade ecabc ``` @@ -36,7 +36,7 @@ Note: if multiple Python releases are installed on your system (e.g. 2.7 and 3.6 ### Method 2: From source - Download the ECabc repository, navigate to the download location on the command line/terminal, and execute: -```python +``` python setup.py install ``` @@ -48,9 +48,11 @@ To get started import ECabc ```python from ecabc import * ``` -Then define your fitness function as a function. The fitness function is the user defined function whose solution is being optimized +Then define your fitness function as a function. The fitness function is the user defined function whose solution is being optimized. Pass in the values and args and have it return the output that is being optimized ```python -def fitness_function: +def fitness_function(values,args): + ***code*** + return output ``` After that, in the main function define your value ranges i.e. the user defined ranges for the variables which are being optimized ```python @@ -92,21 +94,21 @@ for i in range(500): Other parameters that can be specified in the loop are: file logging: debug'/'info'/'warn'/'error'/'crit' or 'disable ```python -abc._logger.file_logging = 'info' -abc._logger.file_logging = 'debug' -abc._logger.file_logging = 'warn' -abc._logger.file_logging = 'error' -abc._logger.file_logging = 'crit' -abc._logger.file_logging = 'disable' +abc._logger.file_level = 'info' +abc._logger.file_level = 'debug' +abc._logger.file_level = 'warn' +abc._logger.file_level = 'error' +abc._logger.file_level = 'crit' +abc._logger.file_level = 'disable' ``` print_level. This will print out log information to the console: ```python -abc._logger.print_level = 'info' -abc._logger.print_level = 'debug' -abc._logger.print_level = 'warn' -abc._logger.print_level = 'error' -abc._logger.print_level = 'crit' -abc._logger.print_level = 'disable' +abc._logger.stream_level = 'info' +abc._logger.stream_level = 'debug' +abc._logger.stream_level = 'warn' +abc._logger.stream_level = 'error' +abc._logger.stream_level = 'crit' +abc._logger.stream_level = 'disable' ``` and processes: ```python diff --git a/images/state.png b/images/state.png new file mode 100644 index 0000000000000000000000000000000000000000..7cb0690363985a6690f030420f01910f297c2d2e GIT binary patch literal 62779 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 By43&x literal 0 HcmV?d00001 From 7b73b09e69194b916ed146be04a705d96d701796 Mon Sep 17 00:00:00 2001 From: Sanskriti Sharma Date: Fri, 12 Apr 2019 16:24:09 -0400 Subject: [PATCH 7/8] Edited paper.md so picture works --- paper/paper.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paper/paper.md b/paper/paper.md index 2e4aa8b..e259735 100644 --- a/paper/paper.md +++ b/paper/paper.md @@ -33,7 +33,7 @@ A rich body of literature exists regarding the optimization of artificial neural ECabc is an open source Python package based on an Artificial Bee Colony, used for tuning functions such as hyperparameters of artificial neural networks. The method is modelled after the honey foraging techniques of bees, where the search space for solutions is in as many dimensions as the number of parameters being tuned. The algorithm is outlined in Figure 1. -![state_diagram](/images/state_diagram.png) +![state_diagram](/images/state.png) The algorithm begins with initializing a pre-decided number of bees and running the fitness function for each bee, thus evaluating them. The fitness function is a user defined function that is used to generate the solutions and the error associated with each employer bee's values at a given position. After evaluation, the bees enter the ‘Employer Bee Phase’ during which each bee is moved in one random ‘dimension’ and then reevaluated. If the new position provides a better fitness score, the old position is abandoned and the bee memorizes the new one. This is repeated for every employer bee. Next, ECabc enters the ‘Calculate Probabilities’ state where the fitness scores of all the employer bees are totaled and the best position is memorized. Then a probability is calculated for each bee, which is the bee’s fitness score divided by the total score. The next phase of the algorithm is the ‘Onlooker Bee Phase’, in which employer bees are chosen based on their probabilities, i.e. those with a higher probability will have a higher likelihood of being chosen. The chosen bee is then moved in one dimension and then reevaluated. Again, if the new score is better, the old score is forgotten in favor of it. Every time a bee is moved in one dimension but doesn’t choose the new position, the bee’s counter goes up. Then in the ‘Scout Bee Phase’, if the counter has hit a certain pre-decided number, the bee is given a new, randomly assigned position. Ultimately, the best fitness value of the entire population is compared to a pre-decided value. If it doesn’t meet that standard, the ECabc returns to the Employer Bee Phase. Alternatively, a number of iterations can be and for each iteration, the algorithm will return to the Employer Bee Phase. From dac814d23bdf68a79cba17f0a7c98fec2fb28adb Mon Sep 17 00:00:00 2001 From: Sanskriti Sharma Date: Fri, 12 Apr 2019 17:38:37 -0400 Subject: [PATCH 8/8] Update README.md --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 857faf9..8376e5b 100644 --- a/README.md +++ b/README.md @@ -168,4 +168,11 @@ if __name__ == '__main__': print("execution time = {}".format(time.time() - start)) ``` +# Contributing, Reporting Issues and Other Support: + +To contribute to ECabc, make a pull request. Contributions should include tests for new features added, as well as extensive documentation. + +To report problems with the software or feature requests, file an issue. When reporting problems, include information such as error messages, your OS/environment and Python version. + +For additional support/questions, contact Sanskriti Sharma (sanskriti_sharma@student.uml.edu), Travis Kessler (travis.j.kessler@gmail.com), Hernan Gelaf-Romer (hernan_gelafromer@student.uml.edu) and/or John Hunter Mack (Hunter_Mack@uml.edu).