I am using the DEAP library to maximize a metric, and I noticed that whenever I restart the algorithm (which is supposed to create a random list of binary values - 1s and 0s) it is producing the same initial values.
I became suspicious and copied their basic DEAP example here and re-ran the algorithms again:
import array, random
from deap import creator, base, tools, algorithms
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
creator.create("Individual", array.array, typecode='b', fitness=creator.FitnessMax)
toolbox = base.Toolbox()
toolbox.register("attr_bool", random.randint, 0, 1)
toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_bool, 10)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
def evalOneMax(individual):
return sum(individual),
toolbox.register("evaluate", evalOneMax)
toolbox.register("mate", tools.cxTwoPoints)
toolbox.register("mutate", tools.mutFlipBit, indpb=0.05)
toolbox.register("select", tools.selTournament, tournsize=3)
population = toolbox.population(n=10)
NGEN=40
for gen in range(NGEN):
offspring = algorithms.varAnd(population, toolbox, cxpb=0.5, mutpb=0.1)
fits = toolbox.map(toolbox.evaluate, offspring)
for fit, ind in zip(fits, offspring):
ind.fitness.values = fit
population = offspring
The code above is exactly their example, but with the population and individual size reduced to 10. I ran the algorithm 5 times and it produced exact copies of each other. I also added a print statement to get the below output:
>python testGA.py
[1, 0, 1, 0, 1, 0, 1, 1, 1, 1]
Starting the Evolution Algorithm...
Evaluating Individual: [0, 1, 0, 1, 0, 1, 1, 1, 1, 0]
Evaluating Individual: [1, 1, 0, 1, 0, 1, 0, 1, 0, 0]
Evaluating Individual: [0, 0, 1, 0, 0, 1, 1, 0, 0, 1]
Evaluating Individual: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Evaluating Individual: [0, 1, 1, 0, 1, 0, 1, 1, 0, 1]
Evaluating Individual: [1, 0, 1, 1, 1, 0, 0, 1, 0, 0]
Evaluating Individual: [0, 1, 0, 0, 0, 1, 0, 0, 0, 1]
Evaluating Individual: [1, 1, 0, 1, 0, 1, 0, 1, 1, 1]
Evaluating Individual: [1, 1, 1, 1, 0, 0, 1, 0, 0, 0]
Evaluating Individual: [0, 0, 1, 1, 1, 1, 0, 1, 1, 1]
This output is generated every time I call the function - In that order. They are exactly identical.
I have read that I shouldn't have to seed the random.randint function, and I tested it by writing a basic script that just prints out a list of 10 random ints ranged 0 to 1. This workd fine, it just seems to produce the same values when I feed it through DEAP.
Is this normal? How can I ensure that, when I run the algorithm, I get different 'individuals' every time?
EDIT:
Sorry for the late reply, here is the full source I am using:
import random, sys
from deap import creator, base, tools
class Max():
def __init__(self):
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
creator.create("Individual", list, fitness=creator.FitnessMax)
INDIVIDUAL_SIZE = 10
self.toolbox = base.Toolbox()
self.toolbox.register("attr_bool", random.randint, 0, 1)
self.toolbox.register("individual", tools.initRepeat, creator.Individual, self.toolbox.attr_bool, n=INDIVIDUAL_SIZE)
self.toolbox.register("population", tools.initRepeat, list, self.toolbox.individual)
self.toolbox.register("mate", tools.cxTwoPoints)
self.toolbox.register("mutate", tools.mutFlipBit, indpb=0.05)
self.toolbox.register("select", tools.selTournament, tournsize=3)
self.toolbox.register("evaluate", self.evaluate)
print self.main()
def evaluate(self, individual):
# Some debug code
print 'Evaluating Individual: ' + str(individual)
return sum(individual),
def main(self):
CXPB, MUTPB, NGEN = 0.5, 0.2, 40
random.seed(64)
pop = self.toolbox.population(n=10)
print "Starting the Evolution Algorithm..."
fitnesses = list(map(self.toolbox.evaluate, pop))
for ind, fit in zip(pop, fitnesses):
ind.fitness.values = fit
# ----------------------------------------------------------
# Killing the program here - just want to see the population created
sys.exit()
print "Evaluated %i individuals" % (len(pop))
for g in range(NGEN):
print "-- Generation %i --" % (g)
# Select the next genereation individuals
offspring = self.toolbox.select(pop, len(pop))
# Clone the selected individuals
offspring = list(map(self.toolbox.clone, offspring))
# Apply crossover and mutation on the offspring
for child1, child2 in zip(offspring[::2], offspring[1::2]):
if random.random() < CXPB:
self.toolbox.mate(child1, child2)
del child1.fitness.values
del child2.fitness.values
for mutant in offspring:
if random.random() < MUTPB:
self.toolbox.mutate(mutant)
del mutant.fitness.values
# Evaluate the individuals with an invalid fitness
invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
fitnesses = map(self.toolbox.evaluate, invalid_ind)
for ind, fit in zip(invalid_ind, fitnesses):
ind.fitness.values = fit
print "\tEvaluated %i individuals" % (len(pop))
pop[:] = offspring
fits = [ind.fitness.values[0] for ind in pop]
length = len(pop)
mean = sum(fits) / length
sum2 = sum(x*x for x in fits)
std = abs(sum2 / length - mean**2)**0.5
print "\tMin %s" % (min(fits))
print "\tMax %s" % (max(fits))
print "\tAvg %s" % (mean)
print "\tStd %s" % (std)
class R_Test:
def __init__(self):
print str([random.randint(0, 1) for i in range(10)])
if __name__ == '__main__':
#rt = R_Test()
mx = Max()
The R_Test class is there to test the random generation in Python. I read here that the seed is dynamically called if not given in Python, and I wanted to test this.
How I have been executing the above code has been as such:
> python testGA.py
... the 10 outputs
> python testGA.py
... the exact same outputs
> python testGA.py
... the exact same outputs
> python testGA.py
... the exact same outputs
> python testGA.py
... the exact same outputs
Obviously 5 times isn't exactly a strenuous test, but the fact that all 10 values are the same 5 times in a row raised a red flag.