def test_mutate_gene(mock_random_sample): config_dict = {"filter_size": [1, 2, 3, 5, 6], "num_filter": [8, 16, 32, 64], "has_pooling_layer": [False, True]} network_table = NetworkBlockTable(config_dict) rows = 2 cols = 3 l = 2 original_individual = initialize_individual_default(network_table, num_rows=rows, num_columns=cols, levelback=l) mutated_individual = mutate(individual=original_individual, mutate_proba=0.1, num_rows=rows, num_cols=cols, levelback=l, network_block_table=network_table, input_layer_shape=(None, 32, 32, 1) ) num_diff = 0 original_genes = original_individual.genotype.gene_nodes mutated_genes = mutated_individual.genotype.gene_nodes for i in range(1, len(original_genes)): if original_genes[i] != mutated_genes[i]: num_diff += 1 # Check that every node has been mutated expect the input node: assert num_diff == len(original_genes) - 1 # Check input and output node correctness assert mutated_individual.genotype.gene_nodes[0].connection == None assert mutated_individual.genotype.gene_nodes[0].network_block == None assert mutated_individual.genotype.gene_nodes[rows * cols + 1].connection != None assert mutated_individual.genotype.gene_nodes[rows * cols + 1].network_block == None
def test_mutate_individual_with_valid_representation_constraint(): config_dict = {"filter_size": [1, 3, 5], "num_filter": [8, 16, 32, 64, 128, 256], "has_pooling_layer": [True, False]} network_table = NetworkBlockTable(config_dict) rows = 4 cols = 20 l = 5 original_individual = initialize_individual_default(network_table, num_rows=rows, num_columns=cols, levelback=l) mutated_individual = mutate(individual=original_individual, mutate_proba=0.9, num_rows=rows, num_cols=cols, levelback=l, network_block_table=network_table, input_layer_shape=(None, 32, 32, 1)) layer_shapes = get_layer_shapes_from_phenotype(input_shape=(None, 32, 32, 1), phenotype=mutated_individual.phenotype) print("--------------------------------------") pprint.pprint(layer_shapes) print("--------------------------------------") representation_layer = layer_shapes[-1] assert representation_layer[1] >= 1 assert representation_layer[2] >= 1
def test_deep_copy_individual(): config_dict = { "filter_size": [1, 3, 5], "num_filter": [8, 16], "has_pooling_layer": [False, True] } network_table = NetworkBlockTable(config_dict) g1 = GeneNode(None, None) g2 = GeneNode(network_table.lookup_dict[6], 0) g3 = GeneNode(network_table.lookup_dict[3], 1) g4 = GeneNode(None, 2) i1 = Individual(Genotype([g1, g2, g3, g4])) i2 = copy.deepcopy(i1) # i2 = i1.__deepcopy__() assert i1 is not i2 assert i1.genotype.gene_nodes[0].network_block is i2.genotype.gene_nodes[ 0].network_block assert i1.genotype.gene_nodes[0].connection is i2.genotype.gene_nodes[ 0].connection i1.genotype.gene_nodes[0].network_block = network_table.lookup_dict[1] i1.genotype.gene_nodes[0].connection = 3 assert i1 is not i2 assert i1.genotype.gene_nodes[ 0].network_block is not i2.genotype.gene_nodes[0].network_block assert i1.genotype.gene_nodes[0].connection is not i2.genotype.gene_nodes[ 0].connection
def test_decode_phenotype_from_genotype_mark_as_coding(): config_dict = {"filter_size": [3, 5], "num_filter": [16, 32], "has_pooling_layer": [False, True]} network_table = NetworkBlockTable(config_dict) genotype_nodes = [GeneNode(None, None), GeneNode(network_table.lookup_dict[1], 0), GeneNode(network_table.lookup_dict[6], 1), # The following two genes are intentionally non-coding: GeneNode(network_table.lookup_dict[3], 1), GeneNode(network_table.lookup_dict[1], 3), # output node GeneNode(None, 2)] test_genotype = Genotype(gene_nodes=genotype_nodes) ind = Individual(genotype=test_genotype) coding_nodes = 0 node: GeneNode for node in ind.genotype.gene_nodes: if node.coding: coding_nodes += 1 # from the list: node1, node2 are the coding nodes, input and output nodes are automatically added to a network # and are not marked as coding here. assert coding_nodes == 2
def test_mutate_active_gene_forced(): config_dict = {"filter_size": [1, 2], "num_filter": [8, 16], "has_pooling_layer": [False]} network_table = NetworkBlockTable(config_dict) rows = 2 cols = 3 l = 2 individual = initialize_individual_default(network_table, num_rows=rows, num_columns=cols, levelback=l) mutated_individual = mutate_active_gene_forced(individual=individual, mutate_proba=0.1, num_rows=2, num_cols=2, levelback=l, network_block_table=network_table, input_layer_shape=(None, 32, 32, 1)) assert mutated_individual.phenotype != individual.phenotype
def test_integration_genereate_and_decode_individual(): config_dict = {"filter_size": [1, 3, 5], "num_filter": [8, 16, 32, 64, 128], "has_pooling_layer": [False, True]} network_table = NetworkBlockTable(config_dict) for i in range(10): print(f"Initalize individual number {i}") individual = initialize_individual_default(network_table, num_rows=3, num_columns=20, levelback=5) individual: Individual phenotype = individual.phenotype model = generate_model_from_phenotype(phenotype, input_layer_shape=(None, 32, 32, 1)) assert model is not None, "Error occurred during model creation." assert tuples_are_equal(model.layers[0].output_shape[0], model.layers[-1].output_shape) assert tuples_are_equal(model.layers[1].output_shape, model.layers[-2].output_shape) K.clear_session()
def test_mutate_passive_gene(): config_dict = {"filter_size": [1, 2, 3, 4, 5], "num_filter": [8, 16], "has_pooling_layer": [False]} network_table = NetworkBlockTable(config_dict) rows = 4 cols = 10 l = 2 individual_mutated = initialize_individual_default(network_table, num_rows=rows, num_columns=cols, levelback=l) individual_original = copy.deepcopy(individual_mutated) mutate_passive_gene(individual_mutated, mutate_proba=0.1, num_rows=rows, num_cols=cols, levelback=l, network_block_table=network_table) p1 = individual_mutated.phenotype p2 = individual_original.phenotype assert p1 == p2 assert individual_original.genotype != individual_mutated.genotype
def test_network_bock_table_generation_1(): config_dict = { "filter_size": [1], "num_filter": [8], "has_pooling_layer": [False] } network_table = NetworkBlockTable(config_dict) created_network_combination = network_table.lookup_dict[0] expected_network_combination = ConvNetworkBlock(block_type="Conv", filter_size=1, num_filter=8, has_pooling_layer=False) assert expected_network_combination == created_network_combination
def test_decode_phenotype_from_genotype(): config_dict = {"filter_size": [3, 5], "num_filter": [16, 32], "has_pooling_layer": [False, True]} network_table = NetworkBlockTable(config_dict) genotype_nodes = [GeneNode(None, None), GeneNode(network_table.lookup_dict[1], 0), GeneNode(network_table.lookup_dict[6], 1), # The following two genes are intentionally non-coding: GeneNode(network_table.lookup_dict[3], 1), GeneNode(network_table.lookup_dict[1], 3), GeneNode(None, 2)] test_genotype = Genotype(gene_nodes=genotype_nodes) expected_phenotype = Phenotype([ConvNetworkBlock("Conv", num_filter=16, filter_size=3, has_pooling_layer=True), ConvNetworkBlock("Conv", num_filter=32, filter_size=5, has_pooling_layer=False)]) decoded_phenotype = Genotype.decode_phenotype_from_genotype(test_genotype) assert decoded_phenotype == expected_phenotype
def test_initialize_individual_with_representation_size_constraint(): config_dict = {"filter_size": [1, 3, 5], "num_filter": [8, 16, 32, 64, 128, 256], "has_pooling_layer": [True, False]} network_table = NetworkBlockTable(config_dict) rows = 4 cols = 20 l = 5 max_representation_size = 1024 individual = initialize_individual_default(network_table, num_rows=rows, num_columns=cols, levelback=l, max_representation_size=max_representation_size) layer_shapes = get_layer_shapes_from_phenotype(input_shape=(None, 32, 32, 1), phenotype=individual.phenotype) print("--------------------------------------") pprint.pprint(layer_shapes) print("--------------------------------------") representation_layer_size = calculate_layer_size(layer_shapes[-1]) assert representation_layer_size <= max_representation_size
def test_decode_phenotype_from_genotype3(): """ Tests the special case where input is directly connected to output. @return: """ config_dict = {"filter_size": [3, 5], "num_filter": [16, 32], "has_pooling_layer": [False, True]} network_table = NetworkBlockTable(config_dict) genotype_nodes = [GeneNode(None, None), # The following two genes are intentionally non-coding: GeneNode(network_table.lookup_dict[3], 1), GeneNode(network_table.lookup_dict[1], 3), GeneNode(None, 0)] test_genotype = Genotype(gene_nodes=genotype_nodes) expected_phenotype = Phenotype([]) decoded_phenotype = Genotype.decode_phenotype_from_genotype(test_genotype) assert decoded_phenotype == expected_phenotype
def test_save_load_individual(): config_dict = { "filter_size": [1, 3, 5], "num_filter": [8, 16], "has_pooling_layer": [False, True] } network_table = NetworkBlockTable(config_dict) g1 = GeneNode(None, None) g2 = GeneNode(network_table.lookup_dict[6], 0) g3 = GeneNode(network_table.lookup_dict[3], 1) g4 = GeneNode(None, 2) i1 = Individual(Genotype([g1, g2, g3, g4])) path = "tests_data/" i1.save(path, "individual.pickle", None) i2 = Individual.load(path, "individual.pickle", None) assert i1 == i2
def test_initialize_individual(mock_randint): config_dict = {"filter_size": [1, 2], "num_filter": [8, 16], "has_pooling_layer": [False]} network_table = NetworkBlockTable(config_dict) expected_input_node = GeneNode(None, None) exptected_block = ConvNetworkBlock(block_type="Conv", filter_size=2, num_filter=16, has_pooling_layer=False) rows = 2 cols = 3 l = 2 individual = initialize_individual_default(network_table, num_rows=rows, num_columns=cols, levelback=l) genes = individual.genotype.gene_nodes num_genes = len(genes) assert num_genes == rows * cols + 2 gene: GeneNode for i, gene in enumerate(genes): if i == 0: assert gene == expected_input_node elif i == num_genes - 1: assert gene.network_block is None assert gene.connection in range(0, rows * cols) else: assert gene.network_block == exptected_block
def evolutionary_search( X_train, fitness_eval_func, fitness_func_args, maximize_fitness, search_config: "SearchConfiguration", num_generations, mutation_probability, num_children, num_rows, num_cols, levelback, path=None, start_generation=0, start_individual: "Individual" = None, fitness_history=None, best_fitness_history=None, generation_history=None, logger_factory: ModelInfoLoggerFactory = None, input_layer_shape=None, num_evaluations=1, initialize_individual=initialize_individual_default, random_sample_block_function=random_sample_block_default): epochs = search_config.epochs batch_size = search_config.batch_size # lists to track the results fitness_history = [] if fitness_history is None else fitness_history best_fitness_history = [] if best_fitness_history is None else best_fitness_history generation_history = [] if generation_history is None else generation_history # Initialization: network_table = NetworkBlockTable(search_config.block_search_space, network_type=search_config.network_type) if start_individual is None: model_logger = logger_factory.create(0, "parent") parent = initialize_individual( network_table, num_rows=num_rows, num_columns=num_cols, levelback=levelback, input_layer_shape=input_layer_shape, max_representation_size=search_config.max_representation_size) fitness_list = [] for i in range(num_evaluations): print( f"\n \n Training the first individual {i + 1}/{num_evaluations}" ) model_logger_temp = logger_factory.create_muted( log_keras_train=True) parent.model = train_individual( parent, X_train, batch_size, epochs, input_layer_shape=input_layer_shape, model_info_logger=model_logger_temp if i > 0 else model_logger, network_type=search_config.network_type) fitness_temp = fitness_eval_func(parent.model, **fitness_func_args) fitness_list.append(fitness_temp) fitness_array = np.array(fitness_list) parent.fitness = fitness_array.mean() model_logger.log_fitness(parent.fitness) if num_evaluations > 1: model_logger.log_fitness_std(fitness_array.std()) parent.generation = 0 generation_history.append(0) fitness_history.append(parent.fitness) print( f"\n First individual scored a fitness of {parent.fitness} ({fitness_array.std()})" ) else: parent = start_individual parent.fitness = start_individual.fitness parent.model = start_individual.model # Begin of evolution: generation = start_generation while generation < num_generations + start_generation: print( f"Creating generation: {generation + 1}/{num_generations + start_generation}" ) child_fitnesses = [] children = [] for i in range(num_children): print(f"\n\nCreating and training child {i + 1}/{num_children}") model_logger = logger_factory.create(generation, child=i) child = mutate_active_gene_forced( parent, mutate_proba=mutation_probability, num_rows=num_rows, num_cols=num_cols, levelback=levelback, network_block_table=network_table, input_layer_shape=input_layer_shape, max_representation_size=search_config.max_representation_size, random_sample_block_function=random_sample_block_function) child.model = train_individual( child, X_train, batch_size, epochs, input_layer_shape=input_layer_shape, model_info_logger=model_logger, network_type=search_config.network_type) child.fitness = fitness_eval_func(child.model, **fitness_func_args) fitness_list = [child.fitness] # If child fitness is better retrain model and average fitness to make sure that this better fitness # did not only occur for change. This is only done if num_evaluations > 1. if child.fitness > parent.fitness and maximize_fitness \ or child.fitness < parent.fitness and not maximize_fitness: if num_evaluations > 1: print( "\n\nChild has new best fitness. Checking for variance of architecture by retraining. \n" ) for j in range(num_evaluations - 1): print(f"Retraining {j + 1}/{num_evaluations - 1}") model_logger_temp = logger_factory.create_muted( log_keras_train=True) child.model = train_individual( child, X_train, batch_size, epochs, input_layer_shape=input_layer_shape, model_info_logger=model_logger_temp, network_type=search_config.network_type) fitness_temp = fitness_eval_func(child.model, **fitness_func_args) fitness_list.append(fitness_temp) fitness_array = np.array(fitness_list) child.fitness = fitness_array.mean() model_logger.log_fitness_std(fitness_array.std()) model_logger.log_fitness(child.fitness) child.generation = generation children.append(child) child_fitnesses.append(child.fitness) print( f"Child scored a fitness of {child.fitness}({fitness_array.std()})" ) if maximize_fitness: best_child_index = np.argmax(child_fitnesses) else: best_child_index = np.argmin(child_fitnesses) if child_fitnesses[best_child_index] > parent.fitness and maximize_fitness \ or child_fitnesses[best_child_index] < parent.fitness and not maximize_fitness: parent = children[best_child_index] best_fitness = child_fitnesses[best_child_index] else: mutate_passive_gene(individual=parent, mutate_proba=mutation_probability, num_rows=num_rows, num_cols=num_cols, levelback=levelback, network_block_table=network_table) best_fitness = parent.fitness parent.generation = generation __track_results(child_fitnesses, fitness_history, best_fitness, best_fitness_history, generation, generation_history, num_children, parent, path) print( f"Evaluation of generation {generation + 1}/{num_generations + start_generation} finished. Best fitness: {best_fitness}" ) generation += 1 history = { "generation": np.array(generation_history), "fitness": np.array(fitness_history), "best_fitness": np.array(best_fitness_history) } return parent, history