ahmedfgad / GeneticAlgorithmPython

Source code of PyGAD, a Python 3 library for building the genetic algorithm and training machine learning algorithms (Keras & PyTorch).
https://pygad.readthedocs.io
BSD 3-Clause "New" or "Revised" License
1.78k stars 451 forks source link

ga_instance.best_solution() only returning best parameters and fitness of previous Generation #291

Open Resch-Said opened 2 months ago

Resch-Said commented 2 months ago

So, I played around with pygad a bit and realized that you only get the best solution of the previous generation. This is particularly noticeable with a stop criteria.

I would like to put the stress especially on the part stop_criteria="reach_500". I put some prints in the on_generation method, so you can see that the limit is reached and the final solution is printed, but when I run the ga_instance.best_solution(), I only get the result of the previous generation.

I have uploaded a sample code here and the output as an image which makes it easy to recognize.

image

import pygad
import numpy

function_inputs = [2, 8, 9, 5]
desired_output = 10

def fitness_func(ga_instance, solution, solution_idx):
    output = numpy.sum(solution * function_inputs)
    fitness = 1.0 / (numpy.abs(output - desired_output) + 0.000001)
    return fitness

fitness_function = fitness_func

def _on_generation(ga_instance):
    print("Generation : ", ga_instance.generations_completed)
    print("Fitness of the best solution :", ga_instance.best_solution()[1])
    print("Best solution :", ga_instance.best_solution()[0])
    print("--------------------------------------------------")

ga_instance = pygad.GA(
    num_generations=5000,
    num_parents_mating=5,
    fitness_func=fitness_function,
    sol_per_pop=10,
    num_genes=len(function_inputs),
    stop_criteria="reach_500",
    on_generation=_on_generation,
)

ga_instance.run()

solution, solution_fitness, solution_idx = ga_instance.best_solution()
print(f"Parameters of the best solution : {solution}")
print(f"Fitness value of the best solution = {solution_fitness}")
print(f"best_solutions_fitness[-1] : {ga_instance.best_solutions_fitness[-1]}") # You can get the best fitness like this
print(f"Index of the best solution : {solution_idx}")

prediction = numpy.sum(numpy.array(function_inputs) * solution)
print(f"Predicted output based on the best solution : {prediction}")

print(
    f"Best fitness value reached after {ga_instance.best_solution_generation} generations."
)
moritz-weber commented 2 weeks ago

Not really an explanation for this weird behavior, but maybe a workaround. In the multi-objective example (https://pygad.readthedocs.io/en/latest/pygad_more.html#multi-objective-optimization), they use:

solution, solution_fitness, solution_idx = ga_instance.best_solution(ga_instance.last_generation_fitness)

This works well for me and (seemingly) always returns the best solution of the last generation.