Search code examples
pythonpython-3.xgenetic-algorithmtournament

Tournament Selection Average Fitness not increasing?


I am trying to implement a genetic tournament selection algorithm, where the fitness of the population on average goes up, but my average fitness isn't changing. enter image description here

I would appreciate if anyone could take a look at my code and advise me on what I am doing wrong. You can find the code here: https://github.com/Mithycal/tournament-selection-algorithm

Code for finding fitness:

for i in range(len(individuals)):
            chosen = individuals[i]
            fitness_scores.append(sum(([a * b for a, b in zip(equation_inputs, chosen)])))

Solution

  • i have taken a look into your code. In this point, tournamentSize is the size of each group right?

    for k in range(tournamentSize):
        randchoice = random.sample(list(individuals), 1)[0] #update individual list so values are different??!
        randvalue = individuals.get(randchoice)
        random_individuals.append(randvalue)
        loc = list(individuals).index(randchoice)
        random_fitness_scores.append(fitness_scores[loc])
        print("\n", randchoice, "participates in the tournament")
    print("\n")
    

    If i remember right in this selection you want to divide your poblation into N groups of individuals, and then you want to keep only the best ( or the n best) of each group.

    I recomend you to change the population representation to:

    individuals = [[random.randint(-4,4) for _ in range(number_of_genes)] for i in pop ] # list
    

    So you could do something like: score() -> custom function that retuns the fitness of an individual

    choosen_individuals = []
    #go throw individual jumping tournamentSize each time
    for k in range(0,len(individuals),tournamentSize): 
        tournament_individuals = individuals[k:k+tournamentSize] # current group
        sorted_group = sorted( [ (score(individual),index) for index,individual in enumerate(tournament_individuals)],reverse = True)
        #sorted_group contains a list of tuples (score,individual_position)
        choosen_individuals.append(tournament_individuals[sorted_group[1]]) # saves the best
    

    I'm leaving you one genetic that i implemented: https://github.com/anigmo97/MUIARFID/blob/master/CUATRIMESTRE_1A/TIA/PROYECTO/algoritmo_gen%C3%A9tico/geneticos.py

    I hope it helps.

    YOUR IMPLEMENTATION

    INDIVIDUAL REPRESENTATION

    Now your individuals (rename to population) are a list of gens. your population is a dict with key (int) and value list of ints. If you think about it, basically you are using the dict as it was a list. I recommend you to change the representation of a population from something like:

    {0 : [ 2,-3], 1: [-1,-1]}

    TO

    [[2,-3],[-1,-1]]

    CHANGING

    individuals = { i : [random.randint(-4,4) for _ in range(number_of_genes)] for i in pop }
    
    population = []
    for i in range(population_size):
        population.append([random.randint(-4,4) for _ in range(number_of_genes)])
    

    INDIVIDUAL SCORING

    You have a list of weights for each gen so we have a list called "weights" with length = number_of_genes. (The individual has the same length).

    With the new representation your scoring can be like:

    def score_individual(individual):
        return sum(([a * b for a, b in zip(weights, individual)]))
    
    def fitness_calc(population):
        fitness_scores = [] #local variable
        for individual in population:
            chosen = list(individuals.values())[i]
            fitness_scores.append(score_individual(individual)) 
        return fitness_scores
    

    POPULATION SORTING

    def sort_population_by_fitness(population):
         return sorted(population,key=lambda i:score_individual(i),reverse=True)
    

    COMPLETE EXAMPLE

    from random import randint,shuffle
    
    def generate_random_weights(num_weights):
        return [randint(-200,200) for x in range(num_weights)]
    
    
    def generate_population(number_of_gens):
        population = []
        for i in range(population_size):
            population.append([randint(-4, 4) for _ in range(number_of_gens)])
        return population
    
    def score_individual(individual):
        return sum(([a * b for a, b in zip(weights, individual)]))
    
    def fitness_calc(population):
        fitness_scores = [] #local variable
        for individual in population:
            fitness_scores.append(score_individual(individual))
        return fitness_scores
    
    def sort_population_by_fitness(population):
        return sorted(population,key=lambda i:score_individual(i),reverse=True)
    
    def calculate_population_score_avg(population):
        scores = [score_individual(i) for i in population]
        return sum(scores)/len(scores)
    
    def make_tournament_selection(population,group_size):
        shuffle(population)
        choosen_individuals = []
        #go throw individual jumping tournamentSize each time
        for k in range(0, len(population), group_size):
            tournament_individuals = population[k:k + group_size]  # current group
            sorted_group = sort_population_by_fitness(tournament_individuals)
            choosen_individuals.append(sorted_group[0])
            print("---->BEST INDIVIDUAL OF THE GROUP {}".format(score_individual(sorted_group[0])))
        return choosen_individuals
    
    def make_n_best_selection(population,num_individuals_to_keep):
        return sort_population_by_fitness(population)[:num_individuals_to_keep]
    
    
    
    if __name__ =="__main__":
        population_size = 20
        number_of_gens = 10
        weights = generate_random_weights(number_of_gens)
        population = generate_population(number_of_gens)
    
        num_generations = 10
        group_size = 5
        score_avgs_by_generation = []
        for i in range(num_generations):
            # make selection
            #population = make_tournament_selection(population,group_size)
            population = make_n_best_selection(population,5)
            print("BEST SCORE IN GENERATION {} = {}".format(
                i,score_individual(sort_population_by_fitness(population)[0]))
            )
            avg_score = calculate_population_score_avg(population)
            score_avgs_by_generation.append(avg_score)
            print("SCORE AVG  IN GENERATION {} = {}\n\n".format(i, avg_score))
    
            # make crossbreeding
            # make mutations
            # add random individuals to add new genetic load
            population += generate_population(10)