Open OodavidsinoO opened 5 years ago
The line "genome.fitness = np.min(ep_r)/float(EP_STEP)" should be indented one step to the right.
I'm running into the same issue but due to threading and processing the genomes are passed by value instead of reference.
I made some nasty stuff, still testing if this all works.
import gzip
import multiprocessing as mp
import os
import pickle
import random
import signal
import sys
import threading
import time
from multiprocessing import current_process
from queue import Empty
import neat
from neat import Population, CompleteExtinctionException
from neat.six_util import itervalues, iteritems
from tqdm import tqdm
from slither import SlitherIO
class MyPopulation(Population):
def run(self, fitness_function, n=None):
if self.config.no_fitness_termination and (n is None):
raise RuntimeError("Cannot have no generational limit with no fitness termination")
k = 0
while n is None or k < n:
k += 1
self.reporters.start_generation(self.generation)
# Evaluate all genomes using the user-provided function.
# ToDo - MY EDIT HERE
# fitness_function(list(iteritems(self.population)), self.config)
self.population = fitness_function(list(iteritems(self.population)), self.config)
self.species.species[1].members = self.population
# ToDo - MY EDIT HERE
# Gather and report statistics.
best = None
for g in itervalues(self.population):
if best is None or g.fitness > best.fitness:
best = g
self.reporters.post_evaluate(self.config, self.population, self.species, best)
# Track the best genome ever seen.
if self.best_genome is None or best.fitness > self.best_genome.fitness:
self.best_genome = best
if not self.config.no_fitness_termination:
# End if the fitness threshold is reached.
fv = self.fitness_criterion(g.fitness for g in itervalues(self.population))
if fv >= self.config.fitness_threshold:
self.reporters.found_solution(self.config, self.generation, best)
break
# Create the next generation from the current generation.
self.population = self.reproduction.reproduce(self.config, self.species,
self.config.pop_size, self.generation)
# Check for complete extinction.
if not self.species.species:
self.reporters.complete_extinction()
# If requested by the user, create a completely new population,
# otherwise raise an exception.
if self.config.reset_on_extinction:
self.population = self.reproduction.create_new(self.config.genome_type,
self.config.genome_config,
self.config.pop_size)
else:
raise CompleteExtinctionException()
# Divide the new population into species.
self.species.speciate(self.config, self.population, self.generation)
self.reporters.end_generation(self.config, self.population, self.species)
self.generation += 1
if self.config.no_fitness_termination:
self.reporters.found_solution(self.config, self.generation, self.best_genome)
return self.best_genome
run.__doc__ = Population.run.__doc__
def load(filename, klass=MyPopulation):
with gzip.open(filename) as f:
generation, config, population, species_set, rndstate = pickle.load(f)
random.setstate(rndstate)
return klass(config, (population, species_set, generation))
def runner(config, fitness_function, generations=5):
pop = MyPopulation(config)
stats = neat.StatisticsReporter()
checkpoint = neat.Checkpointer(1, 60)
from natsort import natsorted
import glob
files = natsorted(glob.glob('%s/neat-checkpoint-*' % os.path.dirname(__file__)))
if len(files) > 0:
# pop = checkpoint.restore_checkpoint(files[-1])
pop = load(filename=files[-1])
pop.generation += 1
pop.add_reporter(stats)
pop.add_reporter(neat.StdOutReporter(True))
pop.add_reporter(checkpoint)
while not event.is_set():
best_genome = pop.run(fitness_function, generations)
print(best_genome.fitness)
stats.save()
checkpoint.save_checkpoint(
config,
pop.population,
pop.species,
pop.generation
)
def dispatcher(genomes, config):
qi = mp.JoinableQueue()
qo = mp.JoinableQueue()
processes = []
# n_processes = mp.cpu_count()
n_processes = mp.cpu_count() * 2
# n_processes = int(mp.cpu_count() / 2)
for i in range(n_processes):
p = threading.Thread(target=do_agent, args=(qi, qo, event, config), name="Agent-%i" % i)
processes.append(p)
for genome in tqdm(genomes, desc="genomes "):
genome[1].fitness = 0
qi.put(genome)
for p in tqdm(processes, desc="processes started "):
# the sleep seems to fix the premature finish of the workers
# due to the queue not being ready yet
time.sleep(0.1)
p.start()
for p in tqdm(processes, desc="processes joined "):
p.join()
# override the old one, due to pass by value the fitness isn't accounted for
new_population = {}
while not qo.empty():
n, genome = qo.get()
new_population[n] = genome
qo.task_done()
qi.join()
qo.join()
return new_population
def do_agent(qi: mp.Queue, qo: mp.Queue, event: mp.Event, config):
global DEBUG
while not qi.empty() and not event.is_set():
try:
n, genome = qi.get(timeout=1)
with SlitherIO(debug=DEBUG) as slither:
qo.put((n, slither.run(event, config, genome),))
qi.task_done()
except Empty:
pass
def handler(signum, frame):
if current_process().name == "MainProcess":
print('Signal handler called with signal', signum)
event.set()
sys.exit(0)
signal.signal(signal.SIGINT, handler)
if __name__ == '__main__':
DEBUG = False
event = mp.Event()
config_file = os.path.join(os.path.dirname(__file__), 'config')
t = threading.Thread(
target=runner,
args=(
neat.Config(
neat.DefaultGenome,
neat.DefaultReproduction,
neat.DefaultSpeciesSet,
neat.DefaultStagnation,
config_file
),
dispatcher,
),
name="Runner"
)
t.start()
t.join()
event.set()
@patvdleer could you explain how you fixed this error if you were able to? I'm having the same issue
ow damn, it's been a while, IIRC, there is/was? a bug which I fixed by creating my own population class extended on the package. Looking back I think I hacked something together that sets the population on self.species.species[1].members
to prevent the NoneType error.
https://github.com/CodeReclaimers/neat-python/blob/master/neat/population.py
MINE
# ToDo - MY EDIT HERE
# fitness_function(list(iteritems(self.population)), self.config)
self.population = fitness_function(list(iteritems(self.population)), self.config)
self.species.species[1].members = self.population
# ToDo - MY EDIT HERE
Thanks! I found another fix to the problem by using the multiprocessing pool class and using the return value of the function instead of the manager/queue class via pool.apply_async(). I guess there is an issue with the way the manager/queue class handles the genome data.
@MrStashley , @patvdleer , @afzalmushtaque , @OodavidsinoO ,
so im facing the same issue here.... im converting my neat training process from being able to run each game sequentially to implementing multiprocessing.... i was getting the same error ( aka the [Type Error: '>' not supported between instances of 'NoneType' and 'NoneType')
i came across this and i tried making the changes but it still doesnt help.... i understand that i could be doing smtg insanely wrong but im open to suggestions...
this is my first neat project and i rly need help with it coz i need to submit this...
here are my files... lemme know if anything else is required
neat_train.py
`import pygame from run import GameController from constants import * import pygame import neat import os import time import pickle import keyboard import math import datetime import threading import multiprocessing import concurrent.futures
class pacman_game: def init(self): self.game = GameController() self.game.startGame() self.pacman = self.game.pacman self.ghosts = self.game.ghosts # list self.score = self.game.score self.clock = pygame.time.Clock() self.turning_points = self.game.nodes.nodesLUT.keys() self.intersection_points = [[], []] self.last_position = None # Store the last position of the agent self.last_position_time = None for node in self.turning_points: self.intersection_points[0].append(node[0]) self.intersection_points[1].append(node[1])
# (node.position.x, node.position.y)
# print(self.intersection_points[0])
# print(self.intersection_points[1])
# print("pacman at")
# print(self.pacman.position)
def test_ai(self):
run = True
# clock = pygame.time.Clock()
self.game.startGame()
while run:
# clock.tick(600)
self.game.loop()
self.game.render()
pygame.display.update()
game_info = self.game.loop()
# print(game_info.pacman_pos) #prints pacmans coordinates on the board
# print(game_info.ghosts_pos) #prints all ghosts coordinates on the board
def test_ai(self, net):
"""
Test the AI against a human player by passing a NEAT neural network
"""
clock = pygame.time.Clock()
run = True
while run:
game_info = self.game.loop()
clock.tick(60)
pacman_pos = (game_info.pacman_pos_x.x, game_info.pacman_pos_x.y)
for node_pos in self.turning_points:
if pacman_pos == node_pos:
output1 = net.activate((game_info.pacman_pos_x.x, game_info.pacman_pos_x.y,
game_info.ghosts_pos[0].x - game_info.pacman_pos_x.x,
game_info.ghosts_pos[0].y - game_info.pacman_pos_x.y,
game_info.ghosts_pos[1].x - game_info.pacman_pos_x.x,
game_info.ghosts_pos[1].y - game_info.pacman_pos_x.y,
game_info.ghosts_pos[2].x - game_info.pacman_pos_x.x,
game_info.ghosts_pos[2].y - game_info.pacman_pos_x.y,
game_info.ghosts_pos[3].x - game_info.pacman_pos_x.x,
game_info.ghosts_pos[3].y - game_info.pacman_pos_x.y,
game_info.score
))
decision = output1.index(max(output1))
if decision == 0:
# self.game.pacman.direction = UP
keyboard.press('w')
keyboard.release('a')
keyboard.release('s')
keyboard.release('d')
# print("up")
elif decision == 1:
keyboard.press('s')
keyboard.release('d')
keyboard.release('a')
keyboard.release('w')
# print("down")
elif decision == 2:
keyboard.press('a')
keyboard.release('d')
keyboard.release('s')
keyboard.release('w')
# print("left")
elif decision == 3:
keyboard.press('d')
keyboard.release('s')
keyboard.release('a')
keyboard.release('w')
# print("right")
else:
pass
def train_ai(self, genome1, config):
net1 = neat.nn.FeedForwardNetwork.create(genome1, config)
#net2 = neat.nn.FeedForwardNetwork.create(genome2, config)
run = True
self.game.startGame()
# self.pause.setPause(playerPaused=True)
self.game.pause.setPause(playerPaused=False)
time_stamp = None
last_position = None # Store the last position of the agent
last_position_time = None
while run:
dt = self.clock.tick(300) / 1000.0
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
self.pacman.update(dt)
game_info = self.game.loop()
self.game.render()
pygame.display.update()
# Check if Pac-Man's position matches any of the node positions
# print(pacman_pos)
valid = self.move_ai(net1, game_info, genome1)
# print(valid)
game_info = self.game.loop()
self.game.render()
pygame.display.update()
if game_info is not None:
valid = self.move_ai(net1, game_info, genome1)
if valid:
self.calculate_fitness(genome1, game_info)
elif valid is False:
break
def calculate_fitness(self, genome1, game_info):
if genome1.fitness == None:
genome1.fitness = 0
genome1.fitness += game_info.score / 10 + game_info.time_elapsed / 1000
else:
genome1.fitness += game_info.score / 10 + game_info.time_elapsed / 1000
#genome2.fitness += game_info.score / 10 + game_info.time_elapsed / 1000
# print(genome1.fitness)
def move_ai(self, net, game_info, genome1):
dt = self.clock.tick(300) / 1000.0
pacman_pos = (game_info.pacman_pos_x.x, game_info.pacman_pos_x.y)
for node_pos in self.turning_points:
if pacman_pos == node_pos:
output1 = net.activate((game_info.pacman_pos_x.x, game_info.pacman_pos_x.y,
game_info.ghosts_pos[0].x - game_info.pacman_pos_x.x,
game_info.ghosts_pos[0].y - game_info.pacman_pos_x.y,
game_info.ghosts_pos[1].x - game_info.pacman_pos_x.x,
game_info.ghosts_pos[1].y - game_info.pacman_pos_x.y,
game_info.ghosts_pos[2].x - game_info.pacman_pos_x.x,
game_info.ghosts_pos[2].y - game_info.pacman_pos_x.y,
game_info.ghosts_pos[3].x - game_info.pacman_pos_x.x,
game_info.ghosts_pos[3].y - game_info.pacman_pos_x.y,
game_info.score
))
decision = output1.index(max(output1))
if decision == 0:
# self.game.pacman.direction = UP
keyboard.press('w')
keyboard.release('a')
keyboard.release('s')
keyboard.release('d')
# print("up")
elif decision == 1:
keyboard.press('s')
keyboard.release('d')
keyboard.release('a')
keyboard.release('w')
# print("down")
elif decision == 2:
keyboard.press('a')
keyboard.release('d')
keyboard.release('s')
keyboard.release('w')
# print("left")
elif decision == 3:
keyboard.press('d')
keyboard.release('s')
keyboard.release('a')
keyboard.release('w')
# print("right")
else:
pass
self.pacman.update(dt)
pacman_pos = (game_info.pacman_pos_x.x, game_info.pacman_pos_x.y)
# print(pacman_pos)
current_position = (game_info.pacman_pos_x.x, game_info.pacman_pos_x.y)
if game_info.lives < 5:
if genome1.fitness == None:
genome1.fitness = 0
genome1.fitness -= 100
else:
genome1.fitness -= 100
return False
elif current_position != self.last_position:
self.last_position = current_position
current_time = datetime.datetime.now()
time_string = current_time.strftime("%H%M%S")
self.last_position_time = time_string
# print(self.last_position_time)
# print(last_position)
# last_position_time = time.time()
elif current_position == self.last_position:
current_time = datetime.datetime.now()
time_string = current_time.strftime("%H%M%S")
# print(int(time_string) - int(self.last_position_time))
if int(time_string) - int(self.last_position_time) > 1.5:
# print('idle')
if genome1.fitness == None:
genome1.fitness = 0
genome1.fitness -= 10
else:
genome1.fitness -= 10
return False
else:
return True
# self.calculate_fitness(genome1, genome2, game_info)
pass
def train_ai(genome1, config): net1 = neat.nn.FeedForwardNetwork.create(genome1, config)
run = True
pgame = pacman_game()
pgame.game.startGame()
# self.pause.setPause(playerPaused=True)
pgame.game.pause.setPause(playerPaused=False)
time_stamp = None
last_position = None # Store the last position of the agent
last_position_time = None
while run:
dt = pgame.clock.tick(300) / 1000.0
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
pgame.game.pacman.update(dt)
game_info = pgame.game.loop()
pgame.game.render()
pygame.display.update()
# Check if Pac-Man's position matches any of the node positions
# print(pacman_pos)
valid = pgame.move_ai(net1, game_info, genome1)
# print(valid)
game_info = pgame.game.loop()
pgame.game.render()
pygame.display.update()
if game_info is not None:
if genome1.fitness is None:
genome1.fitness = 0
valid = pgame.move_ai(net1, game_info, genome1)
if valid:
pgame.calculate_fitness(genome1, game_info)
elif valid is False:
break
def calculate_fitness(self, genome1, game_info): if genome1.fitness == None: genome1.fitness = 0 genome1.fitness += game_info.score / 10 + game_info.time_elapsed / 1000
fitness_lock = threading.Lock() ''' def eval_genomes(genomes, config): fitness_values = [] """ Run each genome against each other one time to determine the fitness. """ workercount = 0
for i, (genome_id1, genome1) in enumerate(genomes):
print(round(i / len(genomes) * 100), end=" ")
#genome1.fitness = 0
for genome_id2, genome2 in genomes[min(i + 1, len(genomes) - 1):]:
workercount +=1
print(workercount)
with concurrent.futures.ProcessPoolExecutor(max_workers=workercount) as executor:
futures = []
for i, (genome_id1, genome1) in enumerate(genomes):
print(round(i / len(genomes) * 100), end=" ")
#print(genome1)
genome1.fitness = 1
for genome_id2, genome2 in genomes[min(i + 1, len(genomes) - 1):]:
genome2.fitness = 0 if genome2.fitness == None else genome2.fitness
futures.append(executor.submit(run_game, genome1, genome2, config))
for future in concurrent.futures.as_completed(futures):
fitnesses = future.result()
genome1, genome2, fitness1, fitness2 = fitnesses
genome1.fitness += fitness1
#genome2.fitness += fitness2
print("look here",genome1.fitness)
print(genome1.fitness)
#return [(genome_id, genome.fitness) for genome_id, genome in genomes]
'''
def eval_genomes(genomes, config): for genome_id, genome in genomes: game = pacman_game() game.train_ai(genome, config)
'''for i, (genome_id1, genome1) in enumerate(genomes):
print(round(i / len(genomes) * 100), end=" ")
genome1.fitness = 0
for genome_id2, genome2 in genomes[min(i + 1, len(genomes) - 1):]:
genome2.fitness = 0 if genome2.fitness == None else genome2.fitness
game = pacman_game()
game.train_ai(genome1, genome2, config)'''
def run_game(genome1, genome2, config): game = pacman_game() game.train_ai(genome1, config) return genome1, genome2, genome1.fitness, genome2.fitness
def test_best_network(config): with open("best.pickle", "rb") as f: winner = pickle.load(f) winner_net = neat.nn.FeedForwardNetwork.create(winner, config)
#genome = pickle.load(f)
# Convert loaded genome into required data structure
#genomes = [(1, genome)]
#print(genomes)
#pygame.display.set_caption("Pong")
pac_game = pacman_game()
pac_game.game.startGame()
pac_game.game.pause.setPause(playerPaused=False)
pac_game.test_ai(winner_net)
def run_neat(config_f): config = config_f p = neat.Population(config) p.add_reporter(neat.StdOutReporter(True)) stats = neat.StatisticsReporter() p.add_reporter(stats) p.add_reporter(neat.Checkpointer(1))
# Create the ParallelEvaluator with multiprocessing
for genome_id, genome in p.population.items():
genome.fitness = 0
evaluator = neat.ParallelEvaluator(multiprocessing.cpu_count(), train_ai)
# Run the NEAT algorithm with the evaluator
winner = p.run(evaluator.evaluate, 10)
# Save the winner
with open("best.pickle", "wb") as f:
pickle.dump(winner, f)
print("Saved the best genome.")
if name == 'main': local_dir = os.path.dirname(file) config_path = os.path.join(local_dir, 'config.txt')
config = neat.Config(neat.DefaultGenome, neat.DefaultReproduction,
neat.DefaultSpeciesSet, neat.DefaultStagnation,
config_path)
run_neat(config)
#test_best_network(config)
`
parallel.py
`""" Runs evaluation functions in parallel subprocesses in order to evaluate multiple genomes at once. """ from multiprocessing import Pool
class ParallelEvaluator(object): def init(self, num_workers, eval_function, timeout=None, maxtasksperchild=None): """ eval_function should take one argument, a tuple of (genome object, config object), and return a single float (the genome's fitness). """ self.eval_function = eval_function self.timeout = timeout self.pool = Pool(processes=num_workers, maxtasksperchild=maxtasksperchild)
def __del__(self):
self.pool.close()
self.pool.join()
self.pool.terminate()
def evaluate(self, genomes, config):
jobs = []
for ignored_genome_id, genome in genomes:
jobs.append(self.pool.apply_async(self.eval_function, (genome, config)))
# assign the fitness back to each genome
for job, (ignored_genome_id, genome) in zip(jobs, genomes):
genome.fitness = job.get(timeout=self.timeout)
`
population.py
`"""Implements the core evolution algorithm."""
from neat.math_util import mean from neat.reporting import ReporterSet
class CompleteExtinctionException(Exception): pass
class Population(object): """ This class implements the core evolution algorithm:
Go to 1. """
def init(self, config, initial_state=None): self.reporters = ReporterSet() self.config = config stagnation = config.stagnation_type(config.stagnation_config, self.reporters) self.reproduction = config.reproduction_type(config.reproduction_config, self.reporters, stagnation) if config.fitness_criterion == 'max': self.fitness_criterion = max elif config.fitness_criterion == 'min': self.fitness_criterion = min elif config.fitness_criterion == 'mean': self.fitness_criterion = mean elif not config.no_fitness_termination: raise RuntimeError( "Unexpected fitness_criterion: {0!r}".format(config.fitness_criterion))
if initial_state is None:
# Create a population from scratch, then partition into species.
self.population = self.reproduction.create_new(config.genome_type,
config.genome_config,
config.pop_size)
self.species = config.species_set_type(config.species_set_config, self.reporters)
self.generation = 0
self.species.speciate(config, self.population, self.generation)
else:
self.population, self.species, self.generation = initial_state
self.best_genome = None
def add_reporter(self, reporter): self.reporters.add(reporter)
def remove_reporter(self, reporter): self.reporters.remove(reporter)
def run(self, fitness_function, n=None): """ Runs NEAT's genetic algorithm for at most n generations. If n is None, run until solution is found or extinction occurs.
The user-provided fitness_function must take only two arguments:
1. The population as a list of (genome id, genome) tuples.
2. The current configuration object.
The return value of the fitness function is ignored, but it must assign
a Python float to the `fitness` member of each genome.
The fitness function is free to maintain external state, perform
evaluations in parallel, etc.
It is assumed that fitness_function does not modify the list of genomes,
the genomes themselves (apart from updating the fitness member),
or the configuration object.
"""
if self.config.no_fitness_termination and (n is None):
raise RuntimeError("Cannot have no generational limit with no fitness termination")
k = 0
while n is None or k < n:
k += 1
self.reporters.start_generation(self.generation)
# Evaluate all genomes using the user-provided function.
fitness_function(list(self.population.items()), self.config)
# Gather and report statistics.
best = None
for g in self.population.values():
if g.fitness is None:
raise RuntimeError("Fitness not assigned to genome {}".format(g.key))
if best is None or g.fitness > best.fitness:
best = g
self.reporters.post_evaluate(self.config, self.population, self.species, best)
# Track the best genome ever seen.
if self.best_genome is None or best.fitness > self.best_genome.fitness:
self.best_genome = best
if not self.config.no_fitness_termination:
# End if the fitness threshold is reached.
fv = self.fitness_criterion(g.fitness for g in self.population.values())
if fv >= self.config.fitness_threshold:
self.reporters.found_solution(self.config, self.generation, best)
break
# Create the next generation from the current generation.
self.population = self.reproduction.reproduce(self.config, self.species,
self.config.pop_size, self.generation)
# Check for complete extinction.
if not self.species.species:
self.reporters.complete_extinction()
# If requested by the user, create a completely new population,
# otherwise raise an exception.
if self.config.reset_on_extinction:
self.population = self.reproduction.create_new(self.config.genome_type,
self.config.genome_config,
self.config.pop_size)
else:
raise CompleteExtinctionException()
# Divide the new population into species.
self.species.speciate(self.config, self.population, self.generation)
self.reporters.end_generation(self.config, self.population, self.species)
self.generation += 1
if self.config.no_fitness_termination:
self.reporters.found_solution(self.config, self.generation, self.best_genome)
return self.best_genome
`