def evolve(_run, _epoch): if get_rank() != 0: return print("\n======[ Evolving ecosystem ]======\n") # Set the offspring count to 0 cs.Species.Offspring = 0 if not Conf.UnitTestMode: # Increase the age of all networks. for net in cn.Net.Ecosystem.values(): net.age += 1 if _epoch < Conf.Epochs: # Evolve networks in all species. wheel = Rand.RouletteWheel() for species_id, species in cs.Species.Populations.items(): wheel.add(species_id, species.fitness.relative) while not wheel.is_empty(): cs.Species.Populations[wheel.pop()].evolve()
def test_crossover(): wheel = Rand.RouletteWheel() wheel.add('add_layer', 1) wheel.add('remove_layer', 1) wheel.add('add_node', 1) wheel.add('remove_node', 1) wheel.add('grow_kernel', 1) wheel.add('shrink_kernel', 1) nets = [ctx.cn.Net(_isolated = True) for _ in range(20)] for mut in range(10): for n in range(len(nets)): mutation = wheel.spin() net = nets[n] if mutation == 'add_layer': net.add_layer() elif mutation == 'add_node': net.add_nodes() elif mutation == 'grow_kernel': net.grow_kernel() if mutation == 'remove_layer': net.remove_layer() elif mutation == 'remove_node': net.remove_nodes() elif mutation == 'shrink_kernel': net.shrink_kernel() model = net.to('cpu') match = list(model(torch.randn(ctx.Conf.TrainBatchSize, *ctx.cn.Net.Input.Shape)).size()) == [ctx.Conf.TrainBatchSize, len(net.layers[-1].nodes)] if not utest.pass_fail(match, "\tEvaluating the mutated network with random input..."): net.print() for p1 in range(len(nets)): for p2 in range(len(nets)): if p1 != p2: offspring = ctx.cn.Net(_p1 = nets[p1], _p2 = nets[p2], _isolated = True) model = offspring.to('cpu') match = list(model(torch.randn(ctx.Conf.TrainBatchSize, *ctx.cn.Net.Input.Shape)).size()) == [ctx.Conf.TrainBatchSize, len(net.layers[-1].nodes)] if not utest.pass_fail(match, "\tEvaluating the offspring network with random input..."): offspring.print()
def get_random_stride( _input_shape, _stride=[] ): # Pre-determined stride. Dimensions with value 0 are populated with random values. wheel = Rand.RouletteWheel() print('Input shape: {}'.format(_input_shape)) strides = [] # Possible strides. for dim, radius in enumerate(_input_shape[1:]): if (len(_stride) > 0 and _stride[dim] > 0): stride = [_stride[dim]] else: if radius <= 1: stride = [1] else: stride = [s for s in range(1, radius // 2 + 1)] if len(strides) == 0: strides = [[s] for s in stride] else: new_strides = [] for old_stride in strides: for new_stride in stride: new_strides.append([*old_stride, new_stride]) strides = new_strides print('Stride: {}'.format(stride)) print('Strides: {}'.format(strides)) for s in strides: wheel.add(s, Func.exp_prod(s)) # for idx in range(len(wheel.elements)): # print(wheel.elements[idx], "\t", wheel.weights[Rand.WeightType.Raw][idx], "\t", wheel.weights[Rand.WeightType.Inverse][idx]) return wheel.spin()
def test_random_kernel_size(_max=28, _draws=1000): wheel = Rand.RouletteWheel() for i in range(1, _max): if i % 2 == 1: wheel.add(i, math.exp(-i)) kernels = {} for draw in range(_draws): k = wheel.spin() if k not in kernels.keys(): kernels[k] = 0 kernels[k] += 1 for key in sorted(kernels): print(key, ":", kernels[key])
def cull(): if get_rank() != 0: return print("\n======[ Culling ecosystem ]======\n") wheel = Rand.RouletteWheel(Rand.WeightType.Inverse) for net_id, net in cn.Net.Ecosystem.items(): if (net.age > 0 and net_id != cn.Net.Champion): # Old, unfit and simple networks # are more likely to be eliminated. wheel.add(net_id, net.fitness.relative * net.complexity / net.age) removed_nets = [] removed_species = [] while len(cn.Net.Ecosystem) > cn.Net.Max.Count: # Get a random network ID net_id = wheel.pop() if net_id is not None: species_id = cn.Net.Ecosystem[net_id].species_id # print('Removing network {} from species {}'.format(net_id, species_id)) removed_nets.append(net_id) # Remove the network from the species. cs.Species.Populations[species_id].nets.remove(net_id) # Remove the network from the ecosystem. del cn.Net.Ecosystem[net_id] if len(cs.Species.Populations[species_id].nets) == 0: removed_species.append(species_id) del cs.Species.Populations[species_id] if len(removed_nets) > 0: print(f'Removed nets: {removed_nets}') if len(removed_species) > 0: print(f'Removed species: {removed_species}')
def test_random_functions(): print("ND:", Rand.ND()) print("negND:", Rand.negND()) print("posND:", Rand.posND()) print("uni_real(-100.0,100.0):", Rand.uni_real(-100.0, 100.0)) print("uni_int(-100,100):", Rand.uni_int(-100, 100)) print("\n===[ Roulette wheel selection ]===\n") weights = [5, 3, 100, 67, 22, 0, 1e-3] array = [n for n in range(len(weights))] wheel = Rand.RouletteWheel() print("Array:", array) print("Weights:", weights) for index, elem in enumerate(array): wheel.add(elem, weights[index]) samples = {} draws = 10000 for _ in range(draws): sample = wheel.spin() if sample in samples: samples[sample] += 1 else: samples[sample] = 1 print("\nArray samples ({} draws):".format(draws)) for key in sorted(samples.keys()): print("\t", key, ":", samples[key]) print("\n===[ Random key and value from table ]===\n") table = {1: "one", 2: "two", 3: "three", 4: "four", 5: "five"} print("Table:") for key in sorted(table.keys()): print("\t", key, ":", table[key]) print("key:", Rand.key(table)) print("val:", Rand.val(table))
def test_random_mutations(): for i in range(10): wheel = Rand.RouletteWheel() wheel.add('add_layer', 1) wheel.add('add_node', 1) wheel.add('grow_kernel', 1) original_net = ctx.Net(_isolated=True) # Clone the network net = ctx.Net(_p1=original_net, _isolated=True) assert (utest.pass_fail( net.matches(original_net), "Comparing the original network with the cloned one...")) mutations = [] for mut in range(100): mutation = wheel.spin() success = False if mutation == 'add_layer': success, layer = net.add_layer(_test=True) if success: mutations.append((mutation, layer)) elif mutation == 'add_node': success, layer, nodes = net.add_nodes() if success: mutations.append((mutation, layer, nodes)) elif mutation == 'grow_kernel': success, layer, kernel, delta = net.grow_kernel() if success: mutations.append((mutation, layer, kernel, delta)) if success: print("(", mut + 1, ") Mutation:", *mutations[-1]) model = net.to('cpu') assert (model(torch.randn(ctx.TrainBatchSize, *ctx.Net.Input.Shape)).size()) print("\n==============[ Reversing mutations ]==============\n") for mut in range(len(mutations)): mutation = mutations[len(mutations) - mut - 1] success = False if mutation[0] == 'add_layer': success, layer = net.remove_layer(mutation[1]) #if success: #print("Layer", layer, "removed") elif mutation[0] == 'add_node': success, layer, node = net.remove_nodes( mutation[1], _node_indices=mutation[2]) #if success: #print("Node", *nodes, "removed from layer", layer) elif mutation[0] == 'grow_kernel': success, layer, kernel, delta = net.shrink_kernel( mutation[1], mutation[2], mutation[3]) #if success: #print("Dimension", *delta.keys(), "of kernel", kernel, "in layer", layer, "decreased by", abs(*delta.values())) assert (utest.pass_fail(success, "Reversing mutation", len(mutations) - mut, "(", mutation, ")...")) model = net.to('cpu') output = model( torch.randn(ctx.TrainBatchSize, *ctx.Net.Input.Shape)) assert (utest.pass_fail( net.matches(original_net), "Comparing the original network with the one with reversed mutations..." )) print( "======================[ Original network ]======================") original_net.print() print( "======================[ Mutated network ]=======================") net.print() model1 = net.to('cpu') model2 = original_net.to('cpu') input1 = torch.randn(ctx.TrainBatchSize, *ctx.Net.Input.Shape) input2 = torch.zeros(input1.size()) input2.data = input1.data print("Input1:", input1, ", size:", input1.size()) print("Input2:", input2, ", size:", input2.size()) output1 = model1(input1) output2 = model2(input2) assert (utest.pass_fail(torch.allclose(output1, output2), "Comparing the two outputs...")) print(output1) print(output2)
def evolve(self): import cortex.network as cn # Populate the parent wheel. # Networks that have been selected for crossover # will pick a partner at random by spinning the wheel. parent1_wheel = Rand.RouletteWheel() parent2_wheel = Rand.RouletteWheel() for net_id in self.nets: if cn.Net.Ecosystem[net_id].age > 0: parent1_wheel.add(net_id, cn.Net.Ecosystem[net_id].fitness.relative) parent2_wheel.add(net_id, cn.Net.Ecosystem[net_id].fitness.relative) # Iterate over the networks and check if we should perform crossover or mutation while not parent1_wheel.is_empty(): # Choose one parent p1 = cn.Net.Ecosystem[parent1_wheel.pop()] # Choose a partner p2 = cn.Net.Ecosystem[parent2_wheel.spin()] # Fitter networks have a better chance of mating. # Take the average of the two parents' relative fitness values mating_chance = 0.5 * (p1.fitness.relative + p2.fitness.relative ) / (Species.Offspring + 1) if not Species.Enabled: mating_chance *= p1.genome_overlap(p2) # print(f'Chance of mating nets {p1.ID} and {p2.ID}: {mating_chance}') if (Species.Offspring < len(cn.Net.Ecosystem) // 2 and Rand.chance(mating_chance)): if p1 != p2: # Crossover offspring = cn.Net(_p1=p1, _p2=p2) else: # Clone offspring = cn.Net(_p1=p1) offspring.mutate(_structure=False) # Increase the offspring count Species.Offspring += 1 elif (p1 != self.champion and Rand.chance(1.0 - self.fitness.stat.get_offset())): # probabilities = { # 'layer': 1, # 'node': 1, # 'stride': 1, # 'kernel': 1 # } # p1.mutate(_probabilities = probabilities) p1.mutate() if p1.species_id != self.ID: # The network has moved to another species. # Remove it from the other wheel. parent2_wheel.remove(p1.ID)