0

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)])))
Destaq
  • 655
  • 9
  • 23

2 Answers2

1

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)

Community
  • 1
  • 1
Ángel Igualada
  • 891
  • 9
  • 13
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/209234/discussion-on-answer-by-angel-igualada-tournament-selection-average-fitness-not). – Samuel Liew Mar 08 '20 at 02:02
1

A couple of remarks:

I suspect that in next_generation you forgot to add next_generation to your list of globals. As it is, this function isn't doing anything.

Since the global variable rerun is never changed to False, run_generation you construct a new set of individuals every generation instead of calculating a next generation. The only place where rerun is updated is in form_generation where rerun is a local variable, not a global variable, but I suspect this is the same mistake as in the previous point.

Futhermore, you should check if the two parents are different. As it is now, it is very likely that an individual will mate with itself resulting in a child that is identical to its parent (except for the occasional mutation).

And finally, you really should try to avoid using globals, or at least try to only use them for global constants. They make it very hard to follow the flow of a program, and as you can see they are the source of all sorts of bugs.

Heike
  • 24,102
  • 2
  • 31
  • 45
  • When I set rerun to True after it is False, I get a straight line on the graph. And the genes stay the same each time, and the fitness scores of each individual stay the same every generation. – Destaq Mar 07 '20 at 18:38
  • That's because you choose the parent with the highest score every time without checking if it has been chosen already. Unless two individuals happen to have the same score, this will means that it's very likely that a child will effectively only have one parent instead of two namely the one with the highest score. – Heike Mar 07 '20 at 18:46