def add_successful_genome_for_test(self, current_gen, use_this_genome): """ This function adds a pre programmed genome which is known to converge for the XOR dataset. :param current_gen: :param use_this_genome: Whether this genome should be added to the population or not :return: """ # Wait for current_gen > 1 because if using backprop the first gen skips using backprop. if current_gen > 1 and use_this_genome: node_list = [ NodeGene(node_id=0, node_type='source'), NodeGene(node_id=1, node_type='source'), NodeGene(node_id=2, node_type='output', bias=0.5), NodeGene(node_id=3, node_type='hidden', bias=1), NodeGene(node_id=4, node_type='hidden', bias=1), NodeGene(node_id=5, node_type='hidden', bias=1), NodeGene(node_id=6, node_type='hidden', bias=1), ] connection_list = [ConnectionGene(input_node=0, output_node=3, innovation_number=1, enabled=True, weight=np.random.randn()), ConnectionGene(input_node=1, output_node=3, innovation_number=2, enabled=True, weight=np.random.randn()), ConnectionGene(input_node=0, output_node=4, innovation_number=3, enabled=True, weight=np.random.randn()), ConnectionGene(input_node=1, output_node=4, innovation_number=4, enabled=True, weight=np.random.randn()), ConnectionGene(input_node=3, output_node=5, innovation_number=5, enabled=True, weight=np.random.randn()), ConnectionGene(input_node=4, output_node=5, innovation_number=6, enabled=True, weight=np.random.randn()), ConnectionGene(input_node=3, output_node=6, innovation_number=7, enabled=True, weight=np.random.randn()), ConnectionGene(input_node=4, output_node=6, innovation_number=8, enabled=True, weight=np.random.randn()), ConnectionGene(input_node=5, output_node=2, innovation_number=9, enabled=True, weight=np.random.rand()), ConnectionGene(input_node=6, output_node=2, innovation_number=10, enabled=True, weight=np.random.randn()) ] test_genome = Genome(connections=connection_list, nodes=node_list, key=1) test_genome.fitness = -99999999999 self.population[32131231] = test_genome
def create_new_population(self, population_size, num_features): population = {} node_list = [] connection_list = [] # Create the source nodes for node in range(num_features): node_list.append(NodeGene(node_id=node, node_type='source')) # Add the output node (There is only one in this case) node_list.append( NodeGene(node_id=num_features, node_type='output', bias=1)) # Save the innovations for the first generation. for source_node_id in range(num_features): # Increment for the new innovation self.global_innovation_number += 1 # The output node will always have the node_id equal to the number of features self.innovation_tracker[( source_node_id, num_features)] = self.global_innovation_number # For each feature there will be a connection to the output for i in range(num_features): connection = (i, num_features) # The connection was already saved, so this should return true assert (connection in self.innovation_tracker) connection_list.append( ConnectionGene( input_node=i, output_node=num_features, innovation_number=self.innovation_tracker[connection], enabled=True)) # Create a population of size population_size for index in range(population_size): # Deep copies otherwise changing the connection weight change's it for every genome that has the same # reference to the class deep_copy_connections = copy.deepcopy(connection_list) deep_copy_nodes = copy.deepcopy(node_list) # Set all the connections to a random weight for each genome for connection in deep_copy_connections: connection.weight = np.random.randn() # Increment since the index value has been assigned self.genome_indexer += 1 # Create the genome population[index] = Genome(connections=deep_copy_connections, nodes=deep_copy_nodes, key=self.genome_indexer) self.show_population_weight_distribution(population=population) return population
def main(): node_list = [ NodeGene(node_id=0, node_type='source'), NodeGene(node_id=1, node_type='source'), NodeGene(node_id=2, node_type='output', bias=1), NodeGene(node_id=3, node_type='output', bias=1), NodeGene(node_id=4, node_type='output', bias=1) ] connection_list = [ ConnectionGene(input_node=0, output_node=2, innovation_number=1, weight=-0.351, enabled=True), ConnectionGene(input_node=0, output_node=3, innovation_number=2, weight=-0.351, enabled=True), ConnectionGene(input_node=0, output_node=4, innovation_number=3, weight=-0.351, enabled=True), ConnectionGene(input_node=1, output_node=2, innovation_number=4, weight=-0.351, enabled=True), ConnectionGene(input_node=1, output_node=3, innovation_number=5, weight=-0.351, enabled=True), ConnectionGene(input_node=1, output_node=4, innovation_number=6, weight=-0.351, enabled=True) ] genome = GenomeMultiClass(connections=connection_list, nodes=node_list, key=3) x_data, y_data = create_data(n_generated=500) genome_nn = GenomeNeuralNetwork(genome=genome, create_weights_bias_from_genome=False, activation_type='sigmoid', learning_rate=0.1, x_train=x_data, y_train=y_data) print(genome.num_layers_including_input) print(genome.constant_weight_connections) print(genome.layer_connections_dict)
def main(): node_list = [ NodeGene(node_id=1, node_type='source'), NodeGene(node_id=2, node_type='source'), NodeGene(node_id=3, node_type='hidden'), NodeGene(node_id=4, node_type='hidden'), NodeGene(node_id=5, node_type='output') ] # Note that one of the connections isn't enabled connection_list = [ ConnectionGene(input_node=1, output_node=5, innovation_number=1, enabled=True), ConnectionGene(input_node=1, output_node=4, innovation_number=2, enabled=True), ConnectionGene(input_node=2, output_node=3, innovation_number=3, enabled=True), ConnectionGene(input_node=2, output_node=4, innovation_number=4, enabled=True), ConnectionGene(input_node=3, output_node=4, innovation_number=7, enabled=True), ConnectionGene(input_node=3, output_node=5, innovation_number=8, enabled=True), ConnectionGene(input_node=4, output_node=5, innovation_number=6, enabled=True) ] genome = Genome(connections=connection_list, nodes=node_list, key=3) print(genome.num_layers_including_input) print(genome.constant_weight_connections) print(genome.layer_connections_dict)
def add_node(self, reproduction_instance, innovation_tracker): """ Add a node between two existing nodes """ # Create the new node new_node = NodeGene(node_id=(max(self.nodes.keys()) + 1), node_type='hidden', bias=1) # The connection where the node will be added connections_list = list(self.connections.values()) # Remove any connections which aren't enabled as a candidate where a node can be added for connection in connections_list: if not connection.enabled: connections_list.remove(connection) connection_to_add_node = random.choice(connections_list) # Disable the connection since it will be replaced connection_to_add_node.enabled = False input_node = connection_to_add_node.input_node output_node = connection_to_add_node.output_node # Create new connection gene. Which has a weight of 1 first_combination = (input_node, new_node.node_id) if first_combination in innovation_tracker: first_new_connection = ConnectionGene( input_node=input_node, output_node=new_node.node_id, innovation_number=innovation_tracker[first_combination], weight=1) else: # Increment since there is a new innovation reproduction_instance.global_innovation_number += 1 first_new_connection = ConnectionGene( input_node=input_node, output_node=new_node.node_id, innovation_number=reproduction_instance. global_innovation_number, weight=1) # Save the innovation since it's new innovation_tracker[ first_combination] = reproduction_instance.global_innovation_number second_combination = (new_node.node_id, output_node) # The second connection keeps the weight of the connection it replaced if second_combination in innovation_tracker: second_new_connection = ConnectionGene( input_node=new_node.node_id, output_node=output_node, innovation_number=innovation_tracker[second_combination], weight=connection_to_add_node.weight) else: # Increment since there is a new innovation reproduction_instance.global_innovation_number += 1 second_new_connection = ConnectionGene( input_node=new_node.node_id, output_node=output_node, innovation_number=reproduction_instance. global_innovation_number, weight=connection_to_add_node.weight) # save the innovation if it doesn't already exist innovation_tracker[ second_combination] = reproduction_instance.global_innovation_number # Add the new node and connections self.nodes[new_node.node_id] = new_node self.connections[ first_new_connection.innovation_number] = first_new_connection self.connections[ second_new_connection.innovation_number] = second_new_connection return first_new_connection, second_new_connection
def add_connection(self, reproduction_instance, innovation_tracker): """ Add a random connection :param innovation_tracker: The innovations that have happened in the past :param reproduction_instance: An instance of the reproduction class so we can access the global innovation number """ # Keeps a list of nodes which can't be chosen from to be the start node of the new connection viable_start_nodes = [] # Any node that isn't the output node is a viable start_node for node_id, node in self.nodes.items(): if node.node_type != 'output': viable_start_nodes.append(node_id) possible_combinations = itertools.combinations(self.get_active_nodes(), 2) cleaned_possible_combinations = self.clean_combinations( possible_combinations=possible_combinations) # Get all the existing combinations already_existing_connections = set() for connection in self.connections.values(): already_existing_connections.add( (connection.input_node, connection.output_node)) possible_new_connections = [] for connection in cleaned_possible_combinations: # If it's not an existing combination then we can choose from it randomly if connection not in already_existing_connections: possible_new_connections.append(connection) if possible_new_connections: # Pick randomly from the possible new choices new_connection = random.choice(possible_new_connections) if new_connection in innovation_tracker: # If the connection was already made then use whatever the innovation number that was assigned to # that connection new_connection_gene = ConnectionGene( input_node=new_connection[0], output_node=new_connection[1], innovation_number=innovation_tracker[new_connection], weight=np.random.randn()) else: reproduction_instance.global_innovation_number += 1 new_connection_gene = ConnectionGene( input_node=new_connection[0], output_node=new_connection[1], innovation_number=reproduction_instance. global_innovation_number, weight=np.random.randn()) # Save the connection as a current gen innovation innovation_tracker[ new_connection] = reproduction_instance.global_innovation_number # Add the connection the the genome self.connections[ new_connection_gene.innovation_number] = new_connection_gene return new_connection_gene
def create_new_population(self, population_size, num_features, num_classes): population = {} source_node_list = [] output_node_list = [] # Create the source and output nodes for node_id in range(num_features + num_classes): if node_id < num_features: source_node_list.append( NodeGene(node_id=node_id, node_type='source')) else: output_node_list.append( NodeGene(node_id=node_id, node_type='output', bias=1)) # Save innovations on population creation for source_node in source_node_list: for output_node in output_node_list: # Increment for the new innovation self.global_innovation_number += 1 # The output node will always have the node_id equal to the number of features self.innovation_tracker[( source_node.node_id, output_node.node_id)] = self.global_innovation_number connection_list = [] # For each feature there will be a connection to the output for source_node in source_node_list: for output_node in output_node_list: connection = (source_node.node_id, output_node.node_id) # The connection was already saved, so this should return true assert (connection in self.innovation_tracker) connection_list.append( ConnectionGene( input_node=source_node.node_id, output_node=output_node.node_id, innovation_number=self.innovation_tracker[connection], enabled=True)) all_nodes_list = source_node_list + output_node_list # Create a population of size population_size for index in range(population_size): # Deep copies otherwise changing the connection weight change's it for every genome that has the same # reference to the class deep_copy_connections = copy.deepcopy(connection_list) deep_copy_nodes = copy.deepcopy(all_nodes_list) # Set all the connections to a random weight for each genome for connection in deep_copy_connections: connection.weight = np.random.randn() # Increment since the index value has been assigned self.genome_indexer += 1 # Create the genome population[index] = GenomeMultiClass( connections=deep_copy_connections, nodes=deep_copy_nodes, key=self.genome_indexer) self.show_population_weight_distribution(population=population) return population