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
Exemple #14
0
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