def test_crossover(self): parent_1, parent_2 = NetworkGenotype(schema), NetworkGenotype(schema) child = crossover(parent_1, parent_2) # Child must be different from both parents self.assertFalse( torch.equal(parent_1.chromosomes['conv.weight'], child.chromosomes['conv.weight']) and torch.equal(parent_1.chromosomes['conv.bias'], child.chromosomes['conv.bias']) and torch.equal(parent_1.chromosomes['fc.weight'], child.chromosomes['fc.weight']) and torch.equal(parent_1.chromosomes['fc.bias'], child.chromosomes['fc.bias']) ) self.assertFalse( torch.equal(parent_2.chromosomes['conv.weight'], child.chromosomes['conv.weight']) and torch.equal(parent_2.chromosomes['conv.bias'], child.chromosomes['conv.bias']) and torch.equal(parent_2.chromosomes['fc.weight'], child.chromosomes['fc.weight']) and torch.equal(parent_2.chromosomes['fc.bias'], child.chromosomes['fc.bias']) ) # Child must get chromosome from one of parents self.assertTrue( torch.equal(parent_1.chromosomes['conv.weight'], child.chromosomes['conv.weight']) or torch.equal(parent_2.chromosomes['conv.weight'], child.chromosomes['conv.weight']) ) self.assertTrue( torch.equal(parent_1.chromosomes['conv.bias'], child.chromosomes['conv.bias']) or torch.equal(parent_2.chromosomes['conv.bias'], child.chromosomes['conv.bias']) ) self.assertTrue( torch.equal(parent_1.chromosomes['fc.weight'], child.chromosomes['fc.weight']) or torch.equal(parent_2.chromosomes['fc.weight'], child.chromosomes['fc.weight']) ) self.assertTrue( torch.equal(parent_1.chromosomes['fc.bias'], child.chromosomes['fc.bias']) or torch.equal(parent_2.chromosomes['fc.bias'], child.chromosomes['fc.bias']) )
def test_network_genotype_clone(self): network_schema = { 'conv1': ConvChromosome(3, 16, 4, 2), 'fc1': LinearChromosome(12, 2) } genotype = NetworkGenotype(network_schema) chromosomes = genotype.chromosomes clone_genotype = genotype.clone() cloned_chromosomes = clone_genotype.chromosomes self.assertTrue(genotype.schema == clone_genotype.schema) self.assertTrue( torch.equal(chromosomes['conv1.weight'], cloned_chromosomes['conv1.weight'])) self.assertTrue( torch.equal(chromosomes['conv1.bias'], cloned_chromosomes['conv1.bias'])) self.assertTrue( torch.equal(chromosomes['fc1.weight'], cloned_chromosomes['fc1.weight'])) self.assertTrue( torch.equal(chromosomes['fc1.bias'], cloned_chromosomes['fc1.bias'])) clone_genotype.chromosomes['fc1.bias'][0] = 0. self.assertFalse( torch.equal(chromosomes['fc1.bias'], cloned_chromosomes['fc1.bias'])) # Ensure deepcopy
def crossover(a: NetworkGenotype, b:NetworkGenotype, random_generator: RandomGenerator) -> NetworkGenotype: c1, c2 = a.clone(), b.clone() gene_length = len(a.genes) idx_to_cross = random_generator.randint(gene_length, int(0.5 * gene_length)) # Uniform crossover w. 50% of genes from each parent for i in idx_to_cross: c1.genes[i] = b.genes[i] c2.genes[i] = a.genes[i] return (c1, c2)
def eval_fitness(self, genotype: NetworkGenotype, max_iterations, num_episodes=1, visualize=False): env = self.env_manger(**self.env_args) with torch.no_grad(): model = genotype.to_network().to(env.device) fitness = 0. for ep in range(num_episodes): state = env.reset() done = False num_iterations = 0 while not done and (max_iterations is None or num_iterations < max_iterations): if visualize: env.render() action = model(state.unsqueeze(0)).argmax().item() state, _, done, _ = env.step(action) num_iterations += 1 if self.logger is not None: self.logger.log_data(env) dist_travel = env.maze.dist_from_start() env.close() return dist_travel
def test_mutation(self): parent = NetworkGenotype(schema) child = mutate(parent, mutation_power=1.) self.assertFalse(torch.equal(parent.chromosomes['conv.weight'], child.chromosomes['conv.weight'])) self.assertFalse(torch.equal(parent.chromosomes['conv.bias'], child.chromosomes['conv.weight'])) self.assertFalse(torch.equal(parent.chromosomes['fc.weight'], child.chromosomes['fc.weight'])) self.assertFalse(torch.equal(parent.chromosomes['fc.bias'], child.chromosomes['fc.weight']))
def test_select_elites(self): genotypes = [NetworkGenotype(schema) for i in range(7)] fitnesses = [0.01, 1.0, 0.8, 0.2, 0.9, 0.75, 0.2] elites = select_elites(genotypes, fitnesses, 3) self.assertEqual(elites[0], genotypes[2]) self.assertEqual(elites[1], genotypes[4]) self.assertEqual(elites[2], genotypes[1])
def crossover(a: NetworkGenotype, b: NetworkGenotype) -> NetworkGenotype: c = a.clone() for name in a.chromosomes.keys(): a_chromosome, b_chromosome = a.chromosomes[name], b.chromosomes[name] c.chromosomes[name] = a_chromosome.clone( ) if np.random.rand() <= 0.5 else b_chromosome.clone() return c
def eval_fitness(self, genotype: NetworkGenotype, max_iterations, num_episodes=1, visualize=False): env = self.env_manger(**self.env_args) with torch.no_grad(): model = genotype.to_network().to(env.device) fitness = 0. for ep in range(num_episodes): state = env.reset() done = False total_reward = 0. num_iterations = 0 while not done and (max_iterations is None or num_iterations < max_iterations): if visualize: env.render() action = model(state.unsqueeze(0)).argmax().item() state, reward, done, _ = env.step(action) total_reward += reward num_iterations += 1 fitness += total_reward / float(num_episodes) if self.logger is not None: self.logger.log_data(env) env.close() return fitness, {}
def mutate(genotype: NetworkGenotype, random_generator: RandomGenerator, mutation_rate, mutation_power=1.) -> NetworkGenotype: child = genotype.clone() gene_length = len(genotype.genes) idx_to_mutate = random_generator.randint(gene_length, int(mutation_rate * gene_length)) for i in idx_to_mutate: pertubation = mutation_power * torch.randn_like(child.genes[i]) child.genes[i] += pertubation return child
def test_gen_new_population(self): ga = SimpleGA(SimpleCNN, 10, FitnessEvaluator(), selection_pressure=0.2) old_gen = [NetworkGenotype(schema) for i in range(10)] fitnesses = [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.] new_gen = ga.new_generation(old_gen, fitnesses) self.assertEqual(len(new_gen), ga.num_populations) self.assertTrue(old_gen[5] in new_gen) # Best individual survive self.assertTrue(True)
def test_create_network_weight(self): genotype = NetworkGenotype(TestModel.genetic_schema()) model = TestModel(genotype) self.assertTrue( torch.equal(model.conv.weight, genotype.chromosomes['conv.weight'])) self.assertTrue( torch.equal(model.conv.bias, genotype.chromosomes['conv.bias'])) self.assertTrue( torch.equal(model.fc.weight, genotype.chromosomes['fc.weight'])) self.assertTrue( torch.equal(model.fc.bias, genotype.chromosomes['fc.bias']))
def test_network_genotype_parameter_dimension(self): network_schema = { 'conv1': ConvChromosome(3, 16, 4, 2), 'fc1': LinearChromosome(12, 2) } network_genotype = NetworkGenotype(network_schema) conv1_weight = network_genotype.chromosomes['conv1.weight'] conv1_bias = network_genotype.chromosomes['conv1.bias'] fc1_weight = network_genotype.chromosomes['fc1.weight'] fc1_bias = network_genotype.chromosomes['fc1.bias'] self.assertTrue(torch.Size([16, 3, 4, 4]) == conv1_weight.shape) self.assertTrue(torch.Size([16]) == conv1_bias.shape) self.assertTrue(torch.Size([2, 12]) == fc1_weight.shape) self.assertTrue(torch.Size([2]) == fc1_bias.shape)
def test_network_genotype_create_parameters(self): network_schema = { 'conv1': ConvChromosome(3, 16, 4, 4), 'conv2': ConvChromosome(16, 32, 5, 2), 'fc1': LinearChromosome(12, 8), 'fc2': LinearChromosome(8, 2), } network_genotype = NetworkGenotype(network_schema) self.assertTrue('conv1.weight' in network_genotype.chromosomes) self.assertTrue('conv1.bias' in network_genotype.chromosomes) self.assertTrue('conv2.weight' in network_genotype.chromosomes) self.assertTrue('conv2.bias' in network_genotype.chromosomes) self.assertTrue('fc1.weight' in network_genotype.chromosomes) self.assertTrue('fc1.bias' in network_genotype.chromosomes) self.assertTrue('fc2.weight' in network_genotype.chromosomes) self.assertTrue('fc2.bias' in network_genotype.chromosomes)
def run(self, num_generations): populations = [ NetworkGenotype(self.model_type.genetic_schema()) for i in range(self.num_populations) ] fitnesses = np.zeros(self.num_populations) for gen in range(num_generations): with tqdm(total=len(populations), desc=f'Generation {gen+1}') as t: for i, p in enumerate(populations): fitnesses[i] = self.fitness_evaluator.eval_fitness( self.model_type, p) t.update() t.set_postfix(max_f=fitnesses.max(), min_f=fitnesses.min(), avg_f=fitnesses.mean()) best = populations[np.argmax(fitnesses)] new_gen = self.new_generation(populations, fitnesses) populations = new_gen return best
def test_create_network_from_schema(self): genotype = NetworkGenotype(TestModel.genetic_schema()) model = TestModel(genotype) self.assertTrue(isinstance(getattr(model, 'conv'), nn.Conv2d)) self.assertTrue(isinstance(getattr(model, 'fc'), nn.Linear))
def mutate(genotype: NetworkGenotype, mutation_power) -> NetworkGenotype: child = genotype.clone() for _, chromosome in child.chromosomes.items(): chromosome += mutation_power * torch.randn(chromosome.shape) return child
def set_weigths(self, genotype: NetworkGenotype): self.load_state_dict(genotype.to_state_dict())
def test_gen_population_mutation(self): parents = [NetworkGenotype(schema) for i in range(5)] children = gen_population_mutation(parents, 50) self.assertEqual(len(children), 50)
def test_gen_population_crossover(self): parents = [NetworkGenotype(schema) for i in range(5)] children = gen_population_crossover(parents, 10) self.assertEqual(len(children), 10)