| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 6959c25 commit f5477e3
21 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,146 @@ | |||
| 1 | + # Image Classification | ||
| 2 | + | ||
| 3 | + In the documentation of the `pygad.nn` module, a neural network is created for classifying images from the Fruits360 dataset without being trained using an optimization algorithm. This section discusses how to train such a classifier using the genetic algorithm with the help of the `pygad.gann` module. | ||
| 4 | + | ||
| 5 | + Please make sure that the training data files [dataset_features.npy](https://github.com/ahmedfgad/NumPyANN/blob/master/dataset_features.npy) and [outputs.npy](https://github.com/ahmedfgad/NumPyANN/blob/master/outputs.npy) are available. For downloading them, use these links: | ||
| 6 | + | ||
| 7 | + 1. [dataset_features.npy](https://github.com/ahmedfgad/NumPyANN/blob/master/dataset_features.npy): The features https://github.com/ahmedfgad/NumPyANN/blob/master/dataset_features.npy | ||
| 8 | + 2. [outputs.npy](https://github.com/ahmedfgad/NumPyANN/blob/master/outputs.npy): The class labels https://github.com/ahmedfgad/NumPyANN/blob/master/outputs.npy | ||
| 9 | + | ||
| 10 | + After the data is available, here is the complete code that builds and trains a neural network using the genetic algorithm for classifying images from 4 classes of the Fruits360 dataset. | ||
| 11 | + | ||
| 12 | + Because there are 4 classes, the output layer is assigned has 4 neurons according to the `num_neurons_output` parameter of the `pygad.gann.GANN` class constructor. | ||
| 13 | + | ||
| 14 | + ```python | ||
| 15 | + import numpy | ||
| 16 | + import pygad | ||
| 17 | + import pygad.nn | ||
| 18 | + import pygad.gann | ||
| 19 | + | ||
| 20 | + def fitness_func(ga_instance, solution, sol_idx): | ||
| 21 | + global GANN_instance, data_inputs, data_outputs | ||
| 22 | + | ||
| 23 | + predictions = pygad.nn.predict(last_layer=GANN_instance.population_networks[sol_idx], | ||
| 24 | + data_inputs=data_inputs) | ||
| 25 | + correct_predictions = numpy.where(predictions == data_outputs)[0].size | ||
| 26 | + solution_fitness = (correct_predictions/data_outputs.size)*100 | ||
| 27 | + | ||
| 28 | + return solution_fitness | ||
| 29 | + | ||
| 30 | + def callback_generation(ga_instance): | ||
| 31 | + global GANN_instance, last_fitness | ||
| 32 | + | ||
| 33 | + population_matrices = pygad.gann.population_as_matrices(population_networks=GANN_instance.population_networks, | ||
| 34 | + population_vectors=ga_instance.population) | ||
| 35 | + | ||
| 36 | + GANN_instance.update_population_trained_weights(population_trained_weights=population_matrices) | ||
| 37 | + | ||
| 38 | + print(f"Generation = {ga_instance.generations_completed}") | ||
| 39 | + print(f"Fitness = {ga_instance.best_solution()[1]}") | ||
| 40 | + print(f"Change = {ga_instance.best_solution()[1] - last_fitness}") | ||
| 41 | + | ||
| 42 | + last_fitness = ga_instance.best_solution()[1].copy() | ||
| 43 | + | ||
| 44 | + # Holds the fitness value of the previous generation. | ||
| 45 | + last_fitness = 0 | ||
| 46 | + | ||
| 47 | + # Reading the input data. | ||
| 48 | + data_inputs = numpy.load("dataset_features.npy") # Download from https://github.com/ahmedfgad/NumPyANN/blob/master/dataset_features.npy | ||
| 49 | + | ||
| 50 | + # Optional step of filtering the input data using the standard deviation. | ||
| 51 | + features_STDs = numpy.std(a=data_inputs, axis=0) | ||
| 52 | + data_inputs = data_inputs[:, features_STDs>50] | ||
| 53 | + | ||
| 54 | + # Reading the output data. | ||
| 55 | + data_outputs = numpy.load("outputs.npy") # Download from https://github.com/ahmedfgad/NumPyANN/blob/master/outputs.npy | ||
| 56 | + | ||
| 57 | + # The length of the input vector for each sample (i.e. number of neurons in the input layer). | ||
| 58 | + num_inputs = data_inputs.shape[1] | ||
| 59 | + # The number of neurons in the output layer (i.e. number of classes). | ||
| 60 | + num_classes = 4 | ||
| 61 | + | ||
| 62 | + # Creating an initial population of neural networks. The return of the initial_population() function holds references to the networks, not their weights. Using such references, the weights of all networks can be fetched. | ||
| 63 | + num_solutions = 8 # A solution or a network can be used interchangeably. | ||
| 64 | + GANN_instance = pygad.gann.GANN(num_solutions=num_solutions, | ||
| 65 | + num_neurons_input=num_inputs, | ||
| 66 | + num_neurons_hidden_layers=[150, 50], | ||
| 67 | + num_neurons_output=num_classes, | ||
| 68 | + hidden_activations=["relu", "relu"], | ||
| 69 | + output_activation="softmax") | ||
| 70 | + | ||
| 71 | + # population does not hold the numerical weights of the network instead it holds a list of references to each last layer of each network (i.e. solution) in the population. A solution or a network can be used interchangeably. | ||
| 72 | + # If there is a population with 3 solutions (i.e. networks), then the population is a list with 3 elements. Each element is a reference to the last layer of each network. Using such a reference, all details of the network can be accessed. | ||
| 73 | + population_vectors = pygad.gann.population_as_vectors(population_networks=GANN_instance.population_networks) | ||
| 74 | + | ||
| 75 | + # To prepare the initial population, there are 2 ways: | ||
| 76 | + # 1) Prepare it yourself and pass it to the initial_population parameter. This way is useful when the user wants to start the genetic algorithm with a custom initial population. | ||
| 77 | + # 2) Assign valid integer values to the sol_per_pop and num_genes parameters. If the initial_population parameter exists, then the sol_per_pop and num_genes parameters are useless. | ||
| 78 | + initial_population = population_vectors.copy() | ||
| 79 | + | ||
| 80 | + num_parents_mating = 4 # Number of solutions to be selected as parents in the mating pool. | ||
| 81 | + | ||
| 82 | + num_generations = 500 # Number of generations. | ||
| 83 | + | ||
| 84 | + mutation_percent_genes = 10 # Percentage of genes to mutate. This parameter has no action if the parameter mutation_num_genes exists. | ||
| 85 | + | ||
| 86 | + parent_selection_type = "sss" # Type of parent selection. | ||
| 87 | + | ||
| 88 | + crossover_type = "single_point" # Type of the crossover operator. | ||
| 89 | + | ||
| 90 | + mutation_type = "random" # Type of the mutation operator. | ||
| 91 | + | ||
| 92 | + keep_parents = -1 # Number of parents to keep in the next population. -1 means keep all parents and 0 means keep nothing. | ||
| 93 | + | ||
| 94 | + ga_instance = pygad.GA(num_generations=num_generations, | ||
| 95 | + num_parents_mating=num_parents_mating, | ||
| 96 | + initial_population=initial_population, | ||
| 97 | + fitness_func=fitness_func, | ||
| 98 | + mutation_percent_genes=mutation_percent_genes, | ||
| 99 | + parent_selection_type=parent_selection_type, | ||
| 100 | + crossover_type=crossover_type, | ||
| 101 | + mutation_type=mutation_type, | ||
| 102 | + keep_parents=keep_parents, | ||
| 103 | + on_generation=callback_generation) | ||
| 104 | + | ||
| 105 | + ga_instance.run() | ||
| 106 | + | ||
| 107 | + # After the generations complete, a plot is shown that summarizes how the fitness values evolve over the generations. | ||
| 108 | + ga_instance.plot_fitness() | ||
| 109 | + | ||
| 110 | + # Returning the details of the best solution. | ||
| 111 | + solution, solution_fitness, solution_idx = ga_instance.best_solution() | ||
| 112 | + print(f"Parameters of the best solution : {solution}") | ||
| 113 | + print(f"Fitness value of the best solution = {solution_fitness}") | ||
| 114 | + print(f"Index of the best solution : {solution_idx}") | ||
| 115 | + | ||
| 116 | + if ga_instance.best_solution_generation != -1: | ||
| 117 | + print(f"Best fitness value reached after {ga_instance.best_solution_generation} generations.") | ||
| 118 | + | ||
| 119 | + # Predicting the outputs of the data using the best solution. | ||
| 120 | + predictions = pygad.nn.predict(last_layer=GANN_instance.population_networks[solution_idx], | ||
| 121 | + data_inputs=data_inputs) | ||
| 122 | + print(f"Predictions of the trained network : {predictions}") | ||
| 123 | + | ||
| 124 | + # Calculating some statistics | ||
| 125 | + num_wrong = numpy.where(predictions != data_outputs)[0] | ||
| 126 | + num_correct = data_outputs.size - num_wrong.size | ||
| 127 | + accuracy = 100 * (num_correct/data_outputs.size) | ||
| 128 | + print(f"Number of correct classifications : {num_correct}.") | ||
| 129 | + print(f"Number of wrong classifications : {num_wrong.size}.") | ||
| 130 | + print(f"Classification accuracy : {accuracy}.") | ||
| 131 | + ``` | ||
| 132 | + | ||
| 133 | + After training completes, here are the outputs of the print statements. The number of wrong classifications is only 1 and the accuracy is 99.949%. This accuracy is reached after 482 generations. | ||
| 134 | + | ||
| 135 | + ``` | ||
| 136 | + Fitness value of the best solution = 99.94903160040775 | ||
| 137 | + Index of the best solution : 0 | ||
| 138 | + Best fitness value reached after 482 generations. | ||
| 139 | + Number of correct classifications : 1961. | ||
| 140 | + Number of wrong classifications : 1. | ||
| 141 | + Classification accuracy : 99.94903160040775. | ||
| 142 | + ``` | ||
| 143 | + | ||
| 144 | + The next figure shows how fitness value evolves by generation. | ||
| 145 | + | ||
| 146 | +  | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,153 @@ | |||
| 1 | + # Regression Example 1 | ||
| 2 | + | ||
| 3 | + To train a neural network for regression, follow these instructions: | ||
| 4 | + | ||
| 5 | + 1. Set the `output_activation` parameter in the constructor of the `pygad.gann.GANN` class to `"None"`. It is possible to use the ReLU function if all outputs are nonnegative. | ||
| 6 | + | ||
| 7 | + ```python | ||
| 8 | + GANN_instance = pygad.gann.GANN(... | ||
| 9 | + output_activation="None") | ||
| 10 | + ``` | ||
| 11 | + | ||
| 12 | + 2. Wherever the `pygad.nn.predict()` function is used, set the `problem_type` parameter to `"regression"`. | ||
| 13 | + | ||
| 14 | + ```python | ||
| 15 | + predictions = pygad.nn.predict(..., | ||
| 16 | + problem_type="regression") | ||
| 17 | + ``` | ||
| 18 | + | ||
| 19 | + 3. Design the fitness function to calculate the error (e.g. mean absolute error). | ||
| 20 | + | ||
| 21 | + ```python | ||
| 22 | + def fitness_func(ga_instance, solution, sol_idx): | ||
| 23 | + ... | ||
| 24 | + | ||
| 25 | + predictions = pygad.nn.predict(..., | ||
| 26 | + problem_type="regression") | ||
| 27 | + | ||
| 28 | + solution_fitness = 1.0/numpy.mean(numpy.abs(predictions - data_outputs)) | ||
| 29 | + | ||
| 30 | + return solution_fitness | ||
| 31 | + ``` | ||
| 32 | + | ||
| 33 | + The next code builds a complete example for building a neural network for regression. | ||
| 34 | + | ||
| 35 | + ```python | ||
| 36 | + import numpy | ||
| 37 | + import pygad | ||
| 38 | + import pygad.nn | ||
| 39 | + import pygad.gann | ||
| 40 | + | ||
| 41 | + def fitness_func(ga_instance, solution, sol_idx): | ||
| 42 | + global GANN_instance, data_inputs, data_outputs | ||
| 43 | + | ||
| 44 | + predictions = pygad.nn.predict(last_layer=GANN_instance.population_networks[sol_idx], | ||
| 45 | + data_inputs=data_inputs, problem_type="regression") | ||
| 46 | + solution_fitness = 1.0/numpy.mean(numpy.abs(predictions - data_outputs)) | ||
| 47 | + | ||
| 48 | + return solution_fitness | ||
| 49 | + | ||
| 50 | + def callback_generation(ga_instance): | ||
| 51 | + global GANN_instance, last_fitness | ||
| 52 | + | ||
| 53 | + population_matrices = pygad.gann.population_as_matrices(population_networks=GANN_instance.population_networks, | ||
| 54 | + population_vectors=ga_instance.population) | ||
| 55 | + | ||
| 56 | + GANN_instance.update_population_trained_weights(population_trained_weights=population_matrices) | ||
| 57 | + | ||
| 58 | + print(f"Generation = {ga_instance.generations_completed}") | ||
| 59 | + print(f"Fitness = {ga_instance.best_solution(pop_fitness=ga_instance.last_generation_fitness)[1]}") | ||
| 60 | + print(f"Change = {ga_instance.best_solution(pop_fitness=ga_instance.last_generation_fitness)[1] - last_fitness}") | ||
| 61 | + | ||
| 62 | + last_fitness = ga_instance.best_solution(pop_fitness=ga_instance.last_generation_fitness)[1].copy() | ||
| 63 | + | ||
| 64 | + # Holds the fitness value of the previous generation. | ||
| 65 | + last_fitness = 0 | ||
| 66 | + | ||
| 67 | + # Preparing the NumPy array of the inputs. | ||
| 68 | + data_inputs = numpy.array([[2, 5, -3, 0.1], | ||
| 69 | + [8, 15, 20, 13]]) | ||
| 70 | + | ||
| 71 | + # Preparing the NumPy array of the outputs. | ||
| 72 | + data_outputs = numpy.array([[0.1, 0.2], | ||
| 73 | + [1.8, 1.5]]) | ||
| 74 | + | ||
| 75 | + # The length of the input vector for each sample (i.e. number of neurons in the input layer). | ||
| 76 | + num_inputs = data_inputs.shape[1] | ||
| 77 | + | ||
| 78 | + # Creating an initial population of neural networks. The return of the initial_population() function holds references to the networks, not their weights. Using such references, the weights of all networks can be fetched. | ||
| 79 | + num_solutions = 6 # A solution or a network can be used interchangeably. | ||
| 80 | + GANN_instance = pygad.gann.GANN(num_solutions=num_solutions, | ||
| 81 | + num_neurons_input=num_inputs, | ||
| 82 | + num_neurons_hidden_layers=[2], | ||
| 83 | + num_neurons_output=2, | ||
| 84 | + hidden_activations=["relu"], | ||
| 85 | + output_activation="None") | ||
| 86 | + | ||
| 87 | + # population does not hold the numerical weights of the network instead it holds a list of references to each last layer of each network (i.e. solution) in the population. A solution or a network can be used interchangeably. | ||
| 88 | + # If there is a population with 3 solutions (i.e. networks), then the population is a list with 3 elements. Each element is a reference to the last layer of each network. Using such a reference, all details of the network can be accessed. | ||
| 89 | + population_vectors = pygad.gann.population_as_vectors(population_networks=GANN_instance.population_networks) | ||
| 90 | + | ||
| 91 | + # To prepare the initial population, there are 2 ways: | ||
| 92 | + # 1) Prepare it yourself and pass it to the initial_population parameter. This way is useful when the user wants to start the genetic algorithm with a custom initial population. | ||
| 93 | + # 2) Assign valid integer values to the sol_per_pop and num_genes parameters. If the initial_population parameter exists, then the sol_per_pop and num_genes parameters are useless. | ||
| 94 | + initial_population = population_vectors.copy() | ||
| 95 | + | ||
| 96 | + num_parents_mating = 4 # Number of solutions to be selected as parents in the mating pool. | ||
| 97 | + | ||
| 98 | + num_generations = 500 # Number of generations. | ||
| 99 | + | ||
| 100 | + mutation_percent_genes = 5 # Percentage of genes to mutate. This parameter has no action if the parameter mutation_num_genes exists. | ||
| 101 | + | ||
| 102 | + parent_selection_type = "sss" # Type of parent selection. | ||
| 103 | + | ||
| 104 | + crossover_type = "single_point" # Type of the crossover operator. | ||
| 105 | + | ||
| 106 | + mutation_type = "random" # Type of the mutation operator. | ||
| 107 | + | ||
| 108 | + keep_parents = 1 # Number of parents to keep in the next population. -1 means keep all parents and 0 means keep nothing. | ||
| 109 | + | ||
| 110 | + init_range_low = -1 | ||
| 111 | + init_range_high = 1 | ||
| 112 | + | ||
| 113 | + ga_instance = pygad.GA(num_generations=num_generations, | ||
| 114 | + num_parents_mating=num_parents_mating, | ||
| 115 | + initial_population=initial_population, | ||
| 116 | + fitness_func=fitness_func, | ||
| 117 | + mutation_percent_genes=mutation_percent_genes, | ||
| 118 | + init_range_low=init_range_low, | ||
| 119 | + init_range_high=init_range_high, | ||
| 120 | + parent_selection_type=parent_selection_type, | ||
| 121 | + crossover_type=crossover_type, | ||
| 122 | + mutation_type=mutation_type, | ||
| 123 | + keep_parents=keep_parents, | ||
| 124 | + on_generation=callback_generation) | ||
| 125 | + | ||
| 126 | + ga_instance.run() | ||
| 127 | + | ||
| 128 | + # After the generations complete, a plot is shown that summarizes how the fitness values evolve over the generations. | ||
| 129 | + ga_instance.plot_fitness() | ||
| 130 | + | ||
| 131 | + # Returning the details of the best solution. | ||
| 132 | + solution, solution_fitness, solution_idx = ga_instance.best_solution(pop_fitness=ga_instance.last_generation_fitness) | ||
| 133 | + print(f"Parameters of the best solution : {solution}") | ||
| 134 | + print(f"Fitness value of the best solution = {solution_fitness}") | ||
| 135 | + print(f"Index of the best solution : {solution_idx}") | ||
| 136 | + | ||
| 137 | + if ga_instance.best_solution_generation != -1: | ||
| 138 | + print(f"Best fitness value reached after {ga_instance.best_solution_generation} generations.") | ||
| 139 | + | ||
| 140 | + # Predicting the outputs of the data using the best solution. | ||
| 141 | + predictions = pygad.nn.predict(last_layer=GANN_instance.population_networks[solution_idx], | ||
| 142 | + data_inputs=data_inputs, | ||
| 143 | + problem_type="regression") | ||
| 144 | + print(f"Predictions of the trained network : {predictions}") | ||
| 145 | + | ||
| 146 | + # Calculating some statistics | ||
| 147 | + abs_error = numpy.mean(numpy.abs(predictions - data_outputs)) | ||
| 148 | + print(f"Absolute error : {abs_error}.") | ||
| 149 | + ``` | ||
| 150 | + | ||
| 151 | + The next figure shows how the fitness value changes for the generations used. | ||
| 152 | + | ||
| 153 | +  | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments