def test_mutate(): """Unit test for mutate function. Checks output type and checks test individual to see if mutation took place successfully.""" copy_child = ['FB', 0, 0, 984, 0.09, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0,\ (1, 2), 0, "checkerboard", "checkerboard", 0, 0, 0, 0, 0, 0] all_vals = [] params = Segmentors.parameters() for key in params.pkeys: all_vals.append(eval(params.ranges[key])) assert isinstance(GeneticSearch.mutate(copy_child, all_vals, 0.5, True), list) assert GeneticSearch.mutate(copy_child, all_vals, 0.5, True) ==\ ['FB', 1390, 0.173, 984, 0.09, 9927, 587, 0, 0.55, 0, 0, 0, 0, 1000, 0,\ (1, 2), 0, 'disk', 'checkerboard', 9, 2907, -47, (0.0, 0.0, 0.0), 0, 0]
def makeToolbox(pop_size): """Make a genetic algorithm toolbox using DEAP. The toolbox uses premade functions for crossover, mutation, evaluation and fitness. Keyword arguments: pop_size -- The size of our population, or how many individuals we have """ # Minimizing fitness function creator.create("FitnessMin", base.Fitness, weights=(-0.000001, )) creator.create("Individual", list, fitness=creator.FitnessMin) # The functions that the GA knows toolbox = base.Toolbox() # Genetic functions toolbox.register("mate", skimageCrossRandom) # crossover #toolbox.register("mutate", mutate) # Mutation toolbox.register("mutate", Segmentors.mutateAlgo) # Mutation toolbox.register("evaluate", Segmentors.runAlgo) # Fitness toolbox.register("select", tools.selTournament, tournsize=5) # Selection toolbox.register("map", futures.map) # So that we can use scoop # DO: May want to later do a different selection process # We choose the parameters, for the most part, random params = Segmentors.parameters() for key in params.pkeys: toolbox.register(key, random.choice, eval(params.ranges[key])) func_seq = [] for key in params.pkeys: func_seq.append(getattr(toolbox, key)) # Here we populate our individual with all of the parameters toolbox.register("individual", tools.initCycle, creator.Individual, func_seq, n=1) # And we make our population toolbox.register("population", tools.initRepeat, list, toolbox.individual, n=pop_size) return toolbox
class Evolver(object): """Perform the genetic algorithm by initializing a population and evolving it over a specified number of generations to find the optimal algorithm and parameters for the problem. Functions: newpopulation -- Initialize a new population. writepop -- Records our population in the file "filename". readpop -- Reads in existing population from "filename". popfitness -- Calculates the fitness values for our population. mutate -- Performs mutation and crossover on population. nextgen -- Generates the next generation of our population. run -- Runs the genetic algorithm. """ AllVals = [] my_p = Segmentors.parameters() for key in my_p.pkeys: AllVals.append(eval(my_p.ranges[key])) def __init__(self, img, mask, pop_size=10): """Set default values for the variables. Keyword arguments: img -- The original training image mask -- The ground truth segmentation mask for the img pop_size -- Integer value denoting size of our population, or how many individuals there are (default 10) """ # Build Population based on size self.img = img self.mask = mask self.tool = makeToolbox(pop_size) self.hof = deap.tools.HallOfFame(10) self.best_avgs = [] self.gen = 0 self.cxpb, self.mutpb, self.flip_prob = 0.9, 0.9, 0.9 def newpopulation(self): """Initialize a new population.""" return self.tool.population() def writepop(self, tpop, filename='test.json'): """Record the population in the file "filename". Keyword arguments: tpop -- The population to be recorded. filename -- string denoting file in which to record the population. (default 'test.json') """ logging.getLogger().info(f"Writting population to {filename}") with open(filename, 'w') as outfile: json.dump(tpop, outfile) def readpop(self, filename='test.json'): """Read in existing population from "filename".""" logging.getLogger().info(f"Reading population from {filename}") self.tool.register("population_read", initPopulation, list, creator.Individual, filename) self.tool.register("individual_guess", initIndividual, creator.Individual) self.tool.register("population_guess", initPopulation, list, self.tool.individual_guess, "my_guess.json") return self.tool.population_read() def popfitness(self, tpop): """Calculate the fitness values for the population, and log general statistics about these values. Uses hall of fame (hof) to keep track of top 10 individuals. Keyword arguments: tpop -- current population Outputs: extract_fits -- Fitness values for our population tpop -- current population """ new_image = [self.img for i in range(0, len(tpop))] new_val = [self.mask for i in range(0, len(tpop))] fitnesses = map(self.tool.evaluate, new_image, new_val, tpop) # DO: Dirk is not sure exactly why we need these for ind, fit in zip(tpop, fitnesses): ind.fitness.values = fit extract_fits = [ind.fitness.values[0] for ind in tpop] self.hof.update(tpop) #Algo = AlgorithmSpace(AlgoParams) # Evaluating the new population leng = len(tpop) mean = sum(extract_fits) / leng self.best_avgs.append(mean) sum1 = sum(i*i for i in extract_fits) stdev = abs(sum1 / leng - mean ** 2) ** 0.5 logging.getLogger().info(f"Generation: {self.gen}") logging.getLogger().info(f" Min: {min(extract_fits)}") logging.getLogger().info(f" Max: {max(extract_fits)}") logging.getLogger().info(f" Avg: {mean}") logging.getLogger().info(f" Std: {stdev}") logging.getLogger().info(f" Size: {leng}") #logging.info(" Time: ", time.time() - initTime) logging.getLogger().info(f"Best Fitness: {self.hof[0].fitness.values}") logging.getLogger().info(f"{self.hof[0]}") # Did we improve the population? # past_pop = tpop # past_min = min(extract_fits) # past_mean = mean self.gen += self.gen return extract_fits, tpop def mutate(self, tpop): """Return new population with mutated individuals. Perform both mutation and crossover. Keyword arguments: tpop -- current population Output: final -- new population with mutated individuals. """ # Calculate next population #TODO: There is an error here. We need to make sure the best hof is included? my_sz = len(tpop) #Length of current population top = min(10,max(1,round(0.1 * my_sz))) top = min(top, len(self.hof)) var = max(1,round(0.4 * my_sz)) var = min(var, len(self.hof)) ran = my_sz - top - var print(f"pop[0:{top}:{var}:{ran}]") print(f"pop[0:{top}:{top+var}:{my_sz}]") # offspring = self.tool.select(tpop, var) # offspring = list(map(self.tool.clone, offspring)) # original code offspring = copy.deepcopy(list(self.hof)) # crossover for child1, child2 in zip(offspring[::2], offspring[1::2]): # Do we crossover? if random.random() < self.cxpb: self.tool.mate(child1, child2) # The parents may be okay values so we should keep them # in the set del child1.fitness.values del child2.fitness.values # mutation for mutant in offspring: if random.random() < self.mutpb: self.tool.mutate(mutant, self.AllVals, self.flip_prob) del mutant.fitness.values # new #population = self.newpopulation() pop = self.tool.population() final = pop[0:ran] print(f"pop size should be {len(final)}") final += self.hof[0:top] print(f"pop size should be {len(final)}") final += offspring[0:var] print(f"pop size should be {len(final)}") print(f"pop[0:{top}:{var}:{ran}]") print(f"pop size should be {len(final)}") # Replacing the old population return final def nextgen(self, tpop): """Generate the next generation of the population. Keyword arguments: tpop -- current population """ _, tpop = self.popfitness(tpop) return self.mutate(tpop) def run(self, ngen=10, population=None,startfile=None, checkpoint=None, cp_freq=1): """Run the genetic algorithm, updating the population over ngen number of generations. Keywork arguments: ngen -- number of generations to run the genetic algorithm. startfile -- File containing existing population (default None) checkpoint -- File containing existing checkpoint (default None) Output: population -- Resulting population after ngen generations. """ if startfile: try: print(f"Reading in {startfile}") population = self.readpop(startfile) except FileNotFoundError: print("WARNING: Start file not found") except: raise if not population: print(f"Inicializing new randome population") population = self.newpopulation() if checkpoint: self.writepop(population, filename=f"{checkpoint}") for cur_g in range(0, ngen+1): print(f"generation {cur_g} of population size {len(population)}") _, population = self.popfitness(population) bestsofar = self.hof[0] seg = Segmentors.algoFromParams(bestsofar) mask = seg.evaluate(self.img) fitness = Segmentors.FitnessFunction(mask, self.mask) print(f"#BEST - {fitness} - {bestsofar}") if checkpoint and cur_g%cp_freq == 0: print(f"Writing Checkpoint file - {checkpoint}") copyfile(f"{checkpoint}", f"{checkpoint}.prev") self.writepop(population, filename=f"{checkpoint}") for cur_p in range(len(population)): logging.getLogger().info(population[cur_p]) if cur_g < ngen+1: population = self.mutate(population) if checkpoint: print(f"Writing Checkpoint file - {checkpoint}") copyfile(f"{checkpoint}", f"{checkpoint}.prev") self.writepop(population, filename=f"{checkpoint}") for cur_p in range(len(population)): logging.getLogger().info(population[cur_p]) return population
def test_parameters(): """Unit test for parameters function. Checks formatting of parameter.""" param = Segmentors.parameters() assert param.printparam('min_size') ==\ "min_size=0.0\n\tparameter for felzenszwalb\n\t[i for i in range(0,10000)]\n"