In the previous blog, I have discussed the genetic algorithm and one of its application in the neural network (Training a neural network with a genetic algorithm ). In this blog, I have used a genetic algorithm to solve the problem of neural network architecture search.
Genetic Algorithm is really helpful if you do not want to waste your time in using brute force trial and error method for selecting hyperparameters. To know more about the genetic algorithm you can read this blog.
In this tutorial, to demonstrate the use of genetic algorithm I have used Snake Game with Deep Learning where it’s been difficult to find out which neural network architecture will give us the best results. So, the genetic algorithm can be used to find out the best network architecture among the number of hyperparameters.
Different values of hyperparameters are used to create an initial population. I have used the following parameters in the genetic algorithm to find the best value for them.
- Number of hidden Layers.
- Units per hidden layer
- Activation function
- Network optimizer
1 2 3 4 5 6 7 8 9 10 11 12 |
def hyperparameters(): parameters = [] units_per_layer = [20,25,30,35,40] layers = [1, 2, 3, 4, 5,6,7] activation = ['relu', 'tanh', 'sigmoid'] optimizer = ['adam', 'rmsprop'] parameters.append(units_per_layer) parameters.append(layers) parameters.append(activation) parameters.append(optimizer) return parameters |
Creating Initial Population
Random parameters are used to create the Initial population. For creating the population first, you have to decide population size. Each individual in the population will have four values.
I have taken 20 chromosomes in the population.
1 2 3 4 5 6 7 8 9 10 11 12 |
def generate_population(size): parameters = hyperparameters() population = [] i=0 while i < size: chromosome = [random.choice(parameters[0]), random.choice(parameters[1]), random.choice(parameters[2]), random.choice(parameters[3])] if chromosome not in population: population.append(chromosome) i+=1 return population |
Fitness Function
Fitness function can vary as per the need of different genetic algorithms. Here, I have used the average score for different network architectures. Individuals with the highest average score are fittest ones.
Selection
After evaluating each individual in the population, I have selected top 5 fittest individuals from the population. And also selected 3 individuals from the non-top performers. This will keep us away from getting stuck in the local maximum.
Remaining 12 individuals are created from these 8 individuals using Crossover.
Crossover and Mutation
To produce better offspring for the next generation, I have selected two parents randomly from the 8 individuals selected above and generated other 12 individuals.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
def children_population(size, population): child_population = [] k = 0 population_size = len(population) while k < size : i, j = random.sample(range(0, population_size), 2) parent1 = population[i] parent2 = population[j] child = new_child(parent1, parent2) if child not in child_population and child not in population: child_population.append(child) k +=1 for newest_child in child_population: population.append(newest_child) return population def new_child(parent1, parent2): child = [] parent_size = len(parent1) i=0 while i<parent_size: child.append(random.choice([parent1[i],parent2[i]])) i += 1 return child |
In certain new children formed, some of their genes can be subjected to a mutation with a low random probability. Mutation is required to maintain some amount of randomness in the genetic algorithm.
1 2 3 4 5 6 7 8 9 10 |
def mutation(population): parameters = hyperparameters() for chromosome in population: if random.random() < 0.1 : key = random.choice([0,1,2,3]) parameters = hyperparameters() mutate_key = random.choice(parameters[key]) chromosome[key] = mutate_key return population |
Now we have created all the necessary functions required for a genetic algorithm. Now, we define a model function using keras library. Then we will train this model with different hyperparameters and search for the best using genetic algorithm.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
def train_model(parameters): units_per_layer = parameters[0] layers = parameters[1] activation = parameters[2] optimizer = parameters[3] model = Sequential() model.add(Dense(units=9,input_dim=9)) for _ in range(layers): model.add(Dense(units=units_per_layer, activation=activation)) model.add(Dense(units = 3, activation = 'softmax')) return model population_with_avg_score = [] for chromosome in population: model = train_model(chromosome) model.compile(loss='mean_squared_error', optimizer=chromosome[3], metrics=['accuracy']) model.fit((np.array(training_data_x).reshape(-1,9)),( np.array(training_data_y).reshape(-1,3)), batch_size = 256,epochs= 3) max_score, avg_score = run_game_with_ML(model,display,clock) chromosome.append(avg_score) chromosome.append(max_score) population_with_avg_score.append(chromosome) |
Here, I have used 10 generations and 20 individuals in the population. It can vary according to your need.
Now, you might have got some feeling about how the genetic algorithm can be applied to find neural architecture instead of using the brute-force method. Hope you enjoy reading.
If you have any doubt/suggestion please feel free to ask and I will do my best to help or improve myself. Good-bye until next time.