import torch import pygad from pygad.torchga import torchga def fitness_func(solution, sol_idx): global data_inputs, data_outputs, torch_ga, model, loss_function model_weights_dict = torchga.model_weights_as_dict(model=model, weights_vector=solution) # Use the current solution as the model parameters. model.load_state_dict(model_weights_dict) predictions = model(data_inputs) abs_error = loss_function(predictions, data_outputs).detach().numpy() + 0.00000001 solution_fitness = 1.0 / abs_error return solution_fitness def callback_generation(ga_instance): print("Generation = {generation}".format(generation=ga_instance.generations_completed)) print("Fitness = {fitness}".format(fitness=ga_instance.best_solution()[1])) # Create the PyTorch model. input_layer = torch.nn.Linear(3, 2) relu_layer = torch.nn.ReLU() output_layer = torch.nn.Linear(2, 1) model = torch.nn.Sequential(input_layer, relu_layer, output_layer) # print(model) # Create an instance of the pygad.torchga.TorchGA class to build the initial population. torch_ga = torchga.TorchGA(model=model, num_solutions=10) loss_function = torch.nn.L1Loss() # Data inputs data_inputs = torch.tensor([[0.02, 0.1, 0.15], [0.7, 0.6, 0.8], [1.5, 1.2, 1.7], [3.2, 2.9, 3.1]]) # Data outputs data_outputs = torch.tensor([[0.1], [0.6], [1.3], [2.5]]) # Prepare the PyGAD parameters. Check the documentation for more information: https://pygad.readthedocs.io/en/latest/README_pygad_ReadTheDocs.html#pygad-ga-class num_generations = 250 # Number of generations. num_parents_mating = 5 # Number of solutions to be selected as parents in the mating pool. initial_population = torch_ga.population_weights # Initial population of network weights parent_selection_type = "sss" # Type of parent selection. crossover_type = "single_point" # Type of the crossover operator. mutation_type = "random" # Type of the mutation operator. mutation_percent_genes = 10 # Percentage of genes to mutate. This parameter has no action if the parameter mutation_num_genes exists. keep_parents = -1 # Number of parents to keep in the next population. -1 means keep all parents and 0 means keep nothing. ga_instance = pygad.GA(num_generations=num_generations, num_parents_mating=num_parents_mating, initial_population=initial_population, fitness_func=fitness_func, parent_selection_type=parent_selection_type, crossover_type=crossover_type, mutation_type=mutation_type, mutation_percent_genes=mutation_percent_genes, keep_parents=keep_parents, on_generation=callback_generation) ga_instance.run() # After the generations complete, some plots are showed that summarize how the outputs/fitness values evolve over generations. ga_instance.plot_result(title="PyGAD & PyTorch - Iteration vs. Fitness", linewidth=4) # Returning the details of the best solution. solution, solution_fitness, solution_idx = ga_instance.best_solution() print("Fitness value of the best solution = {solution_fitness}".format(solution_fitness=solution_fitness)) print("Index of the best solution : {solution_idx}".format(solution_idx=solution_idx)) # Fetch the parameters of the best solution. best_solution_weights = torchga.model_weights_as_dict(model=model, weights_vector=solution) model.load_state_dict(best_solution_weights) predictions = model(data_inputs) print("Predictions : \n", predictions.detach().numpy()) abs_error = loss_function(predictions, data_outputs) print("Absolute Error : ", abs_error.detach().numpy())