Skip to content

Commit

Permalink
Added handling for negative fitness scores, as these would otherwise …
Browse files Browse the repository at this point in the history
…cause incorrect spawn allotment in the current shared fitness scheme.

Updated examples to use non-negative fitness scores, just for the sake of consistency.
Minor parameter tweaks to examples to improve performance.
Demonstrate use of Checkpointer in XOR feed-forward example.
Show more information in IZNN demo plots.
  • Loading branch information
CodeReclaimers committed Jan 16, 2017
1 parent e5a54c2 commit 2e5d4a8
Show file tree
Hide file tree
Showing 13 changed files with 80 additions and 52 deletions.
16 changes: 8 additions & 8 deletions examples/circuits/config
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[NEAT]
max_fitness_threshold = -0.01
pop_size = 350
max_fitness_threshold = 9.99
pop_size = 500
reset_on_extinction = False

[CircuitGenome]
Expand All @@ -23,27 +23,27 @@ compatibility_disjoint_coefficient = 1.0
compatibility_weight_coefficient = 1.0

# connection add/remove rates
conn_add_prob = 0.5
conn_delete_prob = 0.5
conn_add_prob = 0.2
conn_delete_prob = 0.2

# connection enable options
enabled_default = True
enabled_mutate_rate = 0.05

# node add/remove rates
node_add_prob = 0.2
node_delete_prob = 0.2
node_add_prob = 0.1
node_delete_prob = 0.1

# network parameters
num_inputs = 3
num_outputs = 1

[DefaultSpeciesSet]
compatibility_threshold = 3.0
compatibility_threshold = 2.5

[DefaultStagnation]
species_fitness_func = max
max_stagnation = 25
max_stagnation = 15

[DefaultReproduction]
elitism = 2
Expand Down
8 changes: 5 additions & 3 deletions examples/circuits/evolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,10 +402,10 @@ def simulate(genome, config):
outputs = np.array(analysis.node0)
expected = get_expected(inputs)

return -np.sum((expected - outputs) ** 2)
return 10.0 - np.sqrt(np.sum((expected - outputs) ** 2) / len(outputs))
#return -np.max(np.abs(expected - outputs))
except Exception as e:
return -1000
return 0.0


def eval_genomes(genomes, config):
Expand All @@ -429,7 +429,9 @@ def run(config_file):
p.add_reporter(stats)

# Run for up to 300 generations.
winner = p.run(eval_genomes, 1000)
pe = neat.ParallelEvaluator(4, simulate)
#winner = p.run(eval_genomes, 1000)
winner = p.run(pe.evaluate, 1000)

# Display the winning genome.
print('\nBest genome:\n{!s}'.format(winner))
Expand Down
2 changes: 1 addition & 1 deletion examples/memory-fixed/config
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# or the experiment itself. This is the only required section.
[NEAT]
pop_size = 500
max_fitness_threshold = 0.98
max_fitness_threshold = 3.98
reset_on_extinction = 0

[DefaultGenome]
Expand Down
8 changes: 5 additions & 3 deletions examples/memory-fixed/evolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def eval_genome(genome, config):

error += (round(output[0]) - s) ** 2

return 1.0 - 4.0 * (error / (N * num_tests))
return 4.0 - 4.0 * (error / (N * num_tests))


def eval_genomes(genomes, config):
Expand All @@ -71,15 +71,17 @@ def run():
# Demonstration of saving a configuration back to a text file.
config.save('test_save_config.txt')

# Add custom function.
# Demonstration of how to add your own custom activation function.
# This sinc function will be available if my_sinc_function is included in the
# config file activation_options option under the DefaultGenome section.
config.genome_config.add_activation('my_sinc_function', sinc)

pop = neat.Population(config)
stats = neat.StatisticsReporter()
pop.add_reporter(stats)
pop.add_reporter(neat.StdOutReporter())

if 0:
if 1:
pe = neat.ParallelEvaluator(4, eval_genome)
winner = pop.run(pe.evaluate, 1000)
else:
Expand Down
2 changes: 1 addition & 1 deletion examples/memory-variable/config
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ num_inputs = 3
num_hidden = 1
num_outputs = 1
initial_connection = partial 0.5
feed_forward = 0
feed_forward = False
compatibility_disjoint_coefficient = 1.0
compatibility_weight_coefficient = 0.6
conn_add_prob = 0.2
Expand Down
48 changes: 26 additions & 22 deletions examples/neuron-demo/demo-iznn.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,62 @@
import neat


def plot_spikes(spikes, view=False, filename=None, title=None):
def plot_spikes(spikes, title):
""" Plots the trains for a single spiking neuron. """
t_values = [t for t, I, v, u in spikes]
v_values = [v for t, I, v, u in spikes]
u_values = [u for t, I, v, u in spikes]
I_values = [I for t, I, v, u in spikes]
t_values = [t for t, I, v, u, f in spikes]
v_values = [v for t, I, v, u, f in spikes]
u_values = [u for t, I, v, u, f in spikes]
I_values = [I for t, I, v, u, f in spikes]
f_values = [f for t, I, v, u, f in spikes]

fig = plt.figure()
plt.subplot(3, 1, 1)
plt.subplot(4, 1, 1)
plt.ylabel("Potential (mv)")
plt.xlabel("Time (in ms)")
plt.grid()
plt.plot(t_values, v_values, "g-")

if title is None:
plt.title("Izhikevich's spiking neuron model")
else:
plt.title("Izhikevich's spiking neuron model ({0!s})".format(title))
plt.title("Izhikevich's spiking neuron model ({0!s})".format(title))

plt.subplot(3, 1, 2)
plt.subplot(4, 1, 2)
plt.ylabel("Fired")
plt.xlabel("Time (in ms)")
plt.grid()
plt.plot(t_values, f_values, "r-")

plt.subplot(4, 1, 3)
plt.ylabel("Recovery (u)")
plt.xlabel("Time (in ms)")
plt.grid()
plt.plot(t_values, u_values, "r-")

plt.subplot(3, 1, 3)
plt.subplot(4, 1, 4)
plt.ylabel("Current (I)")
plt.xlabel("Time (in ms)")
plt.grid()
plt.plot(t_values, I_values, "r-o")

if filename is not None:
plt.savefig(filename)

if view:
plt.show()
plt.close()
fig = None
fig = plt.figure()
plt.title("Izhikevich's spiking neuron model u/v ({0!s})".format(title))
plt.xlabel("Recovery (u)")
plt.ylabel("Potential (mv)")
plt.grid()
plt.plot(u_values, v_values, 'r-')

return fig
plt.show()
plt.close()


def show(title, a, b, c, d):
n = neat.iznn.IZNeuron(0.0, a, b, c, d, [])
spike_train = []
for i in range(1000):
n.current = 0.0 if i < 100 or i > 800 else 10.0
spike_train.append((1.0 * i, n.current, n.v, n.u))
spike_train.append((1.0 * i, n.current, n.v, n.u, n.fired))
print('{0:d}\t{1:f}\t{2:f}\t{3:f}'.format(i, n.current, n.v, n.u))
n.advance(0.25)

plot_spikes(spike_train, view=False, title=title)
plot_spikes(spike_train, title)

show('regular spiking', **neat.iznn.REGULAR_SPIKING_PARAMS)

Expand Down
2 changes: 1 addition & 1 deletion examples/single-pole-balancing/clean.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#!/usr/bin/env bash
rm *.svg *.gv *.mp4 winner-feedforward
rm *.svg *.gv *.mp4 winner-*
2 changes: 1 addition & 1 deletion examples/xor/config-feedforward
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#--- parameters for the XOR-2 experiment ---#

[NEAT]
max_fitness_threshold = 0.9
max_fitness_threshold = 3.9
pop_size = 150
reset_on_extinction = False

Expand Down
6 changes: 3 additions & 3 deletions examples/xor/config-spiking
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#--- parameters for the XOR-2 experiment ---#

[NEAT]
max_fitness_threshold = 0.9
pop_size = 500
max_fitness_threshold = 9.9
pop_size = 2000
reset_on_extinction = False

[IZGenome]
Expand Down Expand Up @@ -82,7 +82,7 @@ weight_mutate_rate = 0.8
weight_replace_rate = 0.1

[DefaultSpeciesSet]
compatibility_threshold = 3.0
compatibility_threshold = 4.0

[DefaultStagnation]
species_fitness_func = mean
Expand Down
2 changes: 1 addition & 1 deletion examples/xor/evolve-feedforward-parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def eval_genome(genome, config):
"""

net = neat.nn.FeedForwardNetwork.create(genome, config)
error = 1.0
error = 4.0
for xi, xo in zip(xor_inputs, xor_outputs):
output = net.activate(xi)
error -= (output[0] - xo[0]) ** 2
Expand Down
8 changes: 6 additions & 2 deletions examples/xor/evolve-feedforward.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

def eval_genomes(genomes, config):
for genome_id, genome in genomes:
genome.fitness = 1.0
genome.fitness = 4.0
net = neat.nn.FeedForwardNetwork.create(genome, config)
for xi, xo in zip(xor_inputs, xor_outputs):
output = net.activate(xi)
Expand All @@ -34,6 +34,7 @@ def run(config_file):
p.add_reporter(neat.StdOutReporter())
stats = neat.StatisticsReporter()
p.add_reporter(stats)
p.add_reporter(neat.Checkpointer(5))

# Run for up to 300 generations.
winner = p.run(eval_genomes, 300)
Expand All @@ -49,10 +50,13 @@ def run(config_file):
print("input {!r}, expected output {!r}, got {!r}".format(xi, xo, output))

node_names = {-1:'A', -2: 'B', 0:'A XOR B'}
visualize.draw_net(config, winner, True, node_names = node_names)
visualize.draw_net(config, winner, True, node_names=node_names)
visualize.plot_stats(stats, ylog=False, view=True)
visualize.plot_species(stats, view=True)

p = neat.Checkpointer.restore_checkpoint('neat-checkpoint-4')
p.run(eval_genomes, 10)


if __name__ == '__main__':
# Determine path to configuration file. This path manipulation is
Expand Down
8 changes: 4 additions & 4 deletions examples/xor/evolve-spiking.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def simulate(genome, config):

def eval_genome(genome, config):
sum_square_error, simulated = simulate(genome, config)
return 1.0 - sum_square_error
return 10.0 - sum_square_error


def eval_genomes(genomes, config):
Expand Down Expand Up @@ -108,15 +108,15 @@ def run(config_path):
pop.add_reporter(stats)

if 0:
winner = pop.run(eval_genomes, 300)
winner = pop.run(eval_genomes, 3000)
else:
pe = neat.ParallelEvaluator(6, eval_genome)
winner = pop.run(pe.evaluate, 300)
winner = pop.run(pe.evaluate, 3000)

# Display the winning genome.
print('\nBest genome:\n{!s}'.format(winner))

node_names = {-1:'A', -2: 'B', 0:'A XOR B'}
node_names = {-1:'A', -2: 'B'}
visualize.draw_net(config, winner, True, node_names=node_names)
visualize.plot_stats(stats, ylog=False, view=True)
visualize.plot_species(stats, view=True)
Expand Down
20 changes: 18 additions & 2 deletions neat/reproduction.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import random

from neat.indexer import Indexer
from neat.math_util import mean
from neat.six_util import iteritems, iterkeys, itervalues

# TODO: Provide some sort of optional cross-species performance criteria, which
Expand Down Expand Up @@ -56,7 +57,21 @@ def reproduce(self, config, species, pop_size):
# TODO: I don't like this modification of the species and stagnation objects,
# because it requires internal knowledge of the objects.

# Filter out stagnated species and collect the set of non-stagnated species members.
# Find minimum/maximum fitness across the entire population, for use in
# species adjusted fitness computation.
all_fitnesses = []
for sid, s in iteritems(species.species):
all_fitnesses.extend(m.fitness for m in itervalues(s.members))
min_fitness = min(all_fitnesses)
max_fitness = max(all_fitnesses)
# Do not allow the fitness range to be zero, as we divide by it below.
fitness_range = max(1.0, max_fitness - min_fitness)

# Filter out stagnated species, collect the set of non-stagnated
# species members, and compute their average adjusted fitness.
# The average adjusted fitness scheme (normalized to the interval
# [0, 1]) allows the use of negative fitness values without
# interfering with the shared fitness scheme.
num_remaining = 0
species_fitness = []
avg_adjusted_fitness = 0.0
Expand All @@ -67,7 +82,8 @@ def reproduce(self, config, species, pop_size):
num_remaining += 1

# Compute adjusted fitness.
sfitness = sum(m.fitness for m in itervalues(s.members)) / len(s.members)
msf = mean([m.fitness for m in itervalues(s.members)])
sfitness = (msf - min_fitness) / fitness_range
species_fitness.append((sid, s, sfitness))
avg_adjusted_fitness += sfitness

Expand Down

0 comments on commit 2e5d4a8

Please sign in to comment.