ml-finance-python

python scripts for finance machine learning

git clone https://9o.is/git/ml-finance-python.git

neuroevolution.py

(6006B)


      1 from __future__ import print_function, division
      2 import numpy as np
      3 import copy
      4 
      5 class Neuroevolution():
      6     """ Evolutionary optimization of Neural Networks.
      7 
      8     Parameters:
      9     -----------
     10     n_individuals: int
     11         The number of neural networks that are allowed in the population at a time.
     12     mutation_rate: float
     13         The probability that a weight will be mutated.
     14     model_builder: method
     15         A method which returns a user specified NeuralNetwork instance. 
     16     """
     17     def __init__(self, population_size, mutation_rate, model_builder):
     18         self.population_size = population_size
     19         self.mutation_rate = mutation_rate
     20         self.model_builder = model_builder
     21 
     22     def _build_model(self, id):
     23         """ Returns a new individual """
     24         model = self.model_builder(n_inputs=self.X.shape[1], n_outputs=self.y.shape[1])
     25         model.id = id
     26         model.fitness = 0
     27         model.accuracy = 0
     28         
     29         return model
     30 
     31     def _initialize_population(self):
     32         """ Initialization of the neural networks forming the population"""
     33         self.population = []
     34         for _ in range(self.population_size):
     35             model = self._build_model(id=np.random.randint(1000))
     36             self.population.append(model)
     37 
     38     def _mutate(self, individual, var=1):
     39         """ Add zero mean gaussian noise to the layer weights with probability mutation_rate """
     40         for layer in individual.layers:
     41             if hasattr(layer, 'W'):
     42                 # Mutation of weight with probability self.mutation_rate
     43                 mutation_mask = np.random.binomial(1, p=self.mutation_rate, size=layer.W.shape)
     44                 layer.W += np.random.normal(loc=0, scale=var, size=layer.W.shape) * mutation_mask
     45                 mutation_mask = np.random.binomial(1, p=self.mutation_rate, size=layer.w0.shape)
     46                 layer.w0 += np.random.normal(loc=0, scale=var, size=layer.w0.shape) * mutation_mask
     47         
     48         return individual
     49 
     50     def _inherit_weights(self, child, parent):
     51         """ Copies the weights from parent to child """
     52         for i in range(len(child.layers)):
     53             if hasattr(child.layers[i], 'W'):
     54                 # The child inherits both weights W and bias weights w0
     55                 child.layers[i].W = parent.layers[i].W.copy()
     56                 child.layers[i].w0 = parent.layers[i].w0.copy()
     57 
     58     def _crossover(self, parent1, parent2):
     59         """ Performs crossover between the neurons in parent1 and parent2 to form offspring """
     60         child1 = self._build_model(id=parent1.id+1)
     61         self._inherit_weights(child1, parent1)
     62         child2 = self._build_model(id=parent2.id+1)
     63         self._inherit_weights(child2, parent2)
     64 
     65         # Perform crossover
     66         for i in range(len(child1.layers)):
     67             if hasattr(child1.layers[i], 'W'):
     68                 n_neurons = child1.layers[i].W.shape[1]
     69                 # Perform crossover between the individuals' neuron weights
     70                 cutoff = np.random.randint(0, n_neurons)
     71                 child1.layers[i].W[:, cutoff:] = parent2.layers[i].W[:, cutoff:].copy()
     72                 child1.layers[i].w0[:, cutoff:] = parent2.layers[i].w0[:, cutoff:].copy()
     73                 child2.layers[i].W[:, cutoff:] = parent1.layers[i].W[:, cutoff:].copy()
     74                 child2.layers[i].w0[:, cutoff:] = parent1.layers[i].w0[:, cutoff:].copy()
     75         
     76         return child1, child2
     77 
     78     def _calculate_fitness(self):
     79         """ Evaluate the NNs on the test set to get fitness scores """
     80         for individual in self.population:
     81             loss, acc = individual.test_on_batch(self.X, self.y)
     82             individual.fitness = 1 / (loss + 1e-8)
     83             individual.accuracy = acc
     84 
     85     def evolve(self, X, y, n_generations):
     86         """ Will evolve the population for n_generations based on dataset X and labels y"""
     87         self.X, self.y = X, y
     88 
     89         self._initialize_population()
     90 
     91         # The 40% highest fittest individuals will be selected for the next generation
     92         n_winners = int(self.population_size * 0.4)
     93         # The fittest 60% of the population will be selected as parents to form offspring
     94         n_parents = self.population_size - n_winners
     95 
     96         for epoch in range(n_generations):
     97             # Determine the fitness of the individuals in the population
     98             self._calculate_fitness()
     99 
    100             # Sort population by fitness
    101             sorted_i = np.argsort([model.fitness for model in self.population])[::-1]
    102             self.population = [self.population[i] for i in sorted_i]
    103 
    104             # Get the individual with the highest fitness
    105             fittest_individual = self.population[0]
    106             print ("[%d Best Individual - Fitness: %.5f, Accuracy: %.1f%%]" % (epoch, 
    107                                                                         fittest_individual.fitness, 
    108                                                                         float(100*fittest_individual.accuracy)))
    109             # The 'winners' are selected for the next generation
    110             next_population = [self.population[i] for i in range(n_winners)]
    111 
    112             total_fitness = np.sum([model.fitness for model in self.population])
    113             # The probability that a individual will be selected as a parent is proportionate to its fitness
    114             parent_probabilities = [model.fitness / total_fitness for model in self.population]
    115             # Select parents according to probabilities (without replacement to preserve diversity)
    116             parents = np.random.choice(self.population, size=n_parents, p=parent_probabilities, replace=False)
    117             for i in np.arange(0, len(parents), 2):
    118                 # Perform crossover to produce offspring
    119                 child1, child2 = self._crossover(parents[i], parents[i+1])
    120                 # Save mutated offspring for next population
    121                 next_population += [self._mutate(child1), self._mutate(child2)]
    122 
    123             self.population = next_population
    124 
    125         return fittest_individual
    126