A Guide to Genetic ‘Learning’ Algorithms for Optimization
By: Rahulraj Singh
Tags: Genetics, Reinforcement Learning, Data Science, Artificial Intelligence, Algorithms

Converting the image to a one-dimensional vector

The first step of most Image Processing algorithms is to change the image file format to a numerical array since machines perform on numbers. We do the same with our input image and convert it to a vector.

def image_to_vector(img_array):

return numpy.reshape(a=img_array, newshape = (functools.reduce (operator.mul, img_array.shape)))

2. Convert the one-dimensional vector to an array

On the converted vector, we standardize the shape of the vector to create a defined image size.

def vector_to_array(input_vector, shape):

#Checking whether reshaping is possible

if len(input_vector) != functools.reduce(operator.mul, shape):

raise ValueError("Reshaping failed")

return numpy.reshape(a=input_vector, newshape=shape)

3. Defining the Fitness function

The fitness function simply defined is a recursive function that takes a candidate solution to the population as input and produces an output stating how “fit” or in other words how “efficiently optimized” the solution is with respect to the population in consideration.

An ideal fitness function should possess the following characteristics:

` It should be considerably fast to compute since multiple (thousands) iterations of the function are run before reaching a solution. ` It must quantitatively measure the solution’s fitness or determine how fit individuals can be produced from the given solution.

image_encode = image_to_vector(input_image) def fitness_func(int_one, int_two):

fitness_value = numpy.sum(numpy.abs(image_encode-int_one))

fitness_value = numpy.sum(image_encode) - fitness_value

return fitness_value

4. Defining the Callback function

A callback function is executed with every generation of the population and its mutations. The parameters for this function are the GA algorithm itself, the number of processed evaluations so far, and the current population size.

def callback_func(genetic_var):

if genetic_var.generations_completed % 500 == 0:

matplotlib.pyplot.imsave( 'Final_GA_Image_ ' + str( genetic_var.generations_completed )+'.png', vector_to_array(genetic_var.best_solution()[0], input_image.shape))

5. Initiating the Genetic Algorithm class with all of its parameters

As explained in the PyGAD documentation, the Genetic Algorithm initialization takes in multiple parameters that are used in its construction. The above-defined fitness and callback functions are used. The number of populations that need to be iterated through is specified (these can increase based on the complexity of the input). Importantly, the number of generations and number of parents mating are also defined which helps the algorithm in building subsequent child populations. Finally, from the documentation mentioned above, there are a bunch of mutation types to choose from (we have chosen ‘random’ in this example)

# Initiate the Genetic Algorithm class with the given parameters

# Number of Parent Solutions to consider

genetic_var = pygad.GA(num_generations=40999, num_parents_mating=12, # Choosing which fitness function to use

fitness_func=fitness_func, # Lower scale entry point (Should be integer between 0-1)

init_range_low=0, # Higher scale exit point (Should be integer between 0-1)

init_range_high=1, # Number of populations to work through

sol_per_pop=44, # Number of genes to use

num_genes=input_image.size,

mutation_by_replacement=True, # % of genes from the population that need to be mutated

mutation_percent_genes=0.02, # Type of mutation

mutation_type="random", callback_generation=callback_func,

random_mutation_min_val=0, # Mutation minimum and maximum values (range 0-1)

random_mutation_max_val=1)

6. Running the GA and finding the best Solution

The run() function initiates the Genetic Algorithm and finally the best_solution() attribute gives us the best output of the reconstructed image.

# Run the GA instance

genetic_var.run() # Metrics of the best solution

int_one, result_fit, int_two = genetic_var.best_solution()

OUTPUT

These are the images (side-by-side) the input and the reconstructed one. This output was generated after 40998 generations of the initial population. All generations and resulting outputs are present in the repository linked below. We see how the image gradually gets closer to the original input image post multiple generations of processing.

For the detailed implementation of the Genetic Algorithm code using Python, kindly refer to the below-mentioned repository. The number of generations needed for reciprocating an image or other objects depends highly on the nature of the input and its complexity. Therefore, it is important to create a fitness function that evaluates populations extremely fast.