DEAP / deap

Distributed Evolutionary Algorithms in Python
http://deap.readthedocs.org/
GNU Lesser General Public License v3.0
5.8k stars 1.12k forks source link

DEAP & SCOOP: different result / Hall of Fame for each node? #285

Open ghost opened 6 years ago

ghost commented 6 years ago

Already posted it on Stackoverflow but might be helpful here as well: https://stackoverflow.com/questions/51047445/python-deap-scoop-different-result-hall-of-fame-for-each-node

I'm using

to run the genetic algorithm example scoop/examples/deap_ga_onemax.py from https://github.com/soravux/scoop/blob/master/examples/deap_ga_onemax.py

on a HPC cluster using a SLURM script, see code posted below.

Please note, that line number 71 in deap_ga_onemax.py has been commented: #random.seed(64)

Python code:

"""
Code from the deap framework, available at:
https://code.google.com/p/deap/source/browse/examples/ga/onemax_short.py
Conversion to its parallel form took two lines:
from scoop import futures
toolbox.register("map", futures.map)
"""
import array
import random

import numpy

from deap import algorithms
from deap import base
from deap import creator
from deap import tools
from scoop import futures

creator.create("FitnessMax", base.Fitness, weights=(1.0,))
creator.create("Individual", array.array, typecode='b',                 
fitness=creator.FitnessMax)

toolbox = base.Toolbox()

# Attribute generator
toolbox.register("attr_bool", random.randint, 0, 100)

# Structure initializers
toolbox.register("individual", tools.initRepeat, creator.Individual,     
toolbox.attr_bool, 100)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)

def evalOneMax(individual):
    return sum(individual),

toolbox.register("evaluate", evalOneMax)
toolbox.register("mate", tools.cxTwoPoint)
toolbox.register("mutate", tools.mutFlipBit, indpb=0.05)
toolbox.register("select", tools.selTournament, tournsize=3)
toolbox.register("map", futures.map)

def main():
    #random.seed(64)

    pop = toolbox.population(n=100)
    hof = tools.HallOfFame(1)
    stats = tools.Statistics(lambda ind: ind.fitness.values)
    stats.register("avg", numpy.mean)
    stats.register("std", numpy.std)
    stats.register("min", numpy.min)
    stats.register("max", numpy.max)

    pop, log = algorithms.eaSimple(pop, toolbox, cxpb=0.5, mutpb=0.2, ngen=10,stats=stats, halloffame=hof, verbose=True)

        return pop, log, hof

if __name__ == "__main__":
    pop, log, hof = main()
    print(hof)

SLURM script:

#!/bin/bash

#SBATCH -J xxx
#SBATCH -A xxx
#SBATCH -N 4
#SBATCH -n 4
#SBATCH --time=00:03:00
#SBATCH --mem-per-cpu=1024M

#echo "ERROR"
hosts=$(srun bash -c hostname)
srun /projects/p_thermo_dat/software/python36/bin/python -m scoop --host $hosts -v scoop_deap_test.py > $(date +"%Y%m%d_%I%M%p")_log_scoop_deap_test.txt

exit

Problem: Running deap_ga_onemax.py in parallel on 4 nodes with 4 processors each results in 4 different hall of fames. Running on 3 nodes results in 3 different hall of fames and so on. This way, each node has its own hall of fame.

How can I obtain one hall of fame containing the results from all nodes?

Any insight would be very helpful...

fmder commented 6 years ago

From what I understand you run 4 times (in parallel) the optimization with 4 cpu each. Is that correct? If so, this is absolutly normal. Try to run the optimisation 4 times in a row and you won't get the same results. Welcome to stochastic optimization :-).

profPlum commented 5 years ago

Looks like your problem is that you're running the same GA algorithm 4 times independently on each node. If you want one hall of fame the algorithm needs to be run one time, this requires a master-slave setup, I don't know how you achieve this with scoop (scoop appeared very buggy to me) but with mpi4py you would do something like this:

from mpi4py.futures import MPIPoolExecutor if name=='main': with MPIPoolExecutor() as executor: toolbox.register("map", executor.map)

start GA here

then run the code like this: NUM_MASTERS=1 # should only ever be 1 NUM_SLAVES=5 mpiexec -n $NUM_MASTERS -usize (($NUM_SLAVES + $NUM_MASTERS)) python GA_script.py