Knapsack Problem: Inheriting from Set
Again for this example we will use a very simple problem, the 0-1 Knapsack. The purpose of this example is to show the simplicity of DEAP and the ease to inherit from anything else than a simple list or array.
Many evolutionary algorithm textbooks mention that the best way to have an efficient algorithm is to have a representation close the problem. Here, what can be closer to a bag than a set? Lets make our individuals inherit from the set
class.
creator.create("Fitness", base.Fitness, weights=(-1.0, 1.0))#最小化第一个参数背包重量,最大化第二个参数背包价值
creator.create("Individual", set, fitness=creator.Fitness)
用set()函数创建个体…具体怎么用待查。
That’s it. You now have individuals that are, in fact sets, they have the usual attribute
fitness
. The fitness is a minimization of the first objective (the weight of the bag) and a maximization of the second objective (the value of the bag). We will now create a dictionary of 100 random items to map the values and weights.
# Create the item dictionary: item name is an integer, and value is
# a (weight, value) 2-uple.
items = {}
# Create random items and store them in the items' dictionary.
for i in range(NBR_ITEMS):
items[i] = (random.randint(1, 10), random.uniform(0, 100))
用随机函数创建背包,重量为[1,10],价值为[0,100]。
We now need to initialize a population and the individuals therein. For this, we will need a Toolbox
to register our generators since sets can also be created with an input iterable.
toolbox = base.Toolbox()
toolbox.register("attr_item", random.randrange, NBR_ITEMS)
toolbox.register("individual", tools.initRepeat, creator.Individual,
toolbox.attr_item, IND_INIT_SIZE)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
利用toolbox.register初始化种群和个体,用法与前一篇文章相同。
Voilà! The last thing to do is to define our evaluation function.
最后定义进化函数。
def evalKnapsack(individual):
weight = 0.0
value = 0.0
for item in individual:
weight += items[item][0]
value += items[item][1]
if len(individual) > MAX_ITEM or weight > MAX_WEIGHT:
return 10000, 0 # Ensure overweighted bags are dominated
return weight, value
Everything is ready for evolution. Ho no wait, since DEAP’s developers are lazy, there is no crossover and mutation operators that can be applied directly on sets. Lets define some. For example, a crossover, producing two children from two parents, could be that the first child is the intersection of the two sets and the second child their absolute difference.
万事俱备,只差操作算子了,首先是选择….这段代码没有看懂….
def cxSet(ind1, ind2):
"""Apply a crossover operation on input sets. The first child is the
intersection of the two sets, the second child is the difference of the
two sets.
"""
temp = set(ind1) # Used in order to keep type
ind1 &= ind2 # Intersection (inplace)
ind2 ^= temp # Symmetric Difference (inplace)
return ind1, ind2
A mutation operator could randomly add or remove an element from the set input individual.
变异,随机挑选个体淘汰,或者加入一些随机生成的个体。
def mutSet(individual):
"""Mutation that pops or add an element."""
if random.random() < 0.5:
if len(individual) > 0: # We cannot pop from an empty set
individual.remove(random.choice(sorted(tuple(individual))))
else:
individual.add(random.randrange(NBR_ITEMS))
return individual,
We then register these operators in the toolbox. Since it is a multi-objective problem, we have selected the SPEA-II selection scheme :
selSPEA2()
然后我们在toolbox里面注册操作算子,因为这是一个多目标的问题,我们选择SPEA-II方法….具体方法还要了解一下….为什么前面自己定义了选择和变异,还要用toolbox里面的选择、变异函数…不懂。
toolbox.register("evaluate", evalKnapsack)
toolbox.register("mate", cxSet)
toolbox.register("mutate", mutSet)
toolbox.register("select", tools.selNSGA2)
From here, all that is left to do is either write the algorithm or use provided in algorithms. Here we will use the eaMuPlusLambda() algorithm.
def main():
NGEN = 50
MU = 50
LAMBDA = 100
CXPB = 0.7
MUTPB = 0.2
pop = toolbox.population(n=MU)
hof = tools.ParetoFront()
stats = tools.Statistics(lambda ind: ind.fitness.values)
stats.register("avg", numpy.mean, axis=0)
stats.register("std", numpy.std, axis=0)
stats.register("min", numpy.min, axis=0)
stats.register("max", numpy.max, axis=0)
algorithms.eaMuPlusLambda(pop, toolbox, MU, LAMBDA, CXPB, MUTPB, NGEN, stats,
halloffame=hof)
return pop, stats, hof
Finally, a ParetoFront may be used to retrieve the best non dominated individuals of the evolution and a Statistics object is created for compiling four different statistics over the generations. The Numpy functions are registered in the statistics object with the axis=0 argument to compute the statistics on each objective independently. Otherwise, Numpy would compute a single mean for both objectives.
The complete examples/ga/knapsack.
完整代码:
# This file is part of DEAP.
#
# DEAP is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# DEAP is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with DEAP. If not, see <http://www.gnu.org/licenses/>.
import random
import numpy
from deap import algorithms
from deap import base
from deap import creator
from deap import tools
IND_INIT_SIZE = 5
MAX_ITEM = 50
MAX_WEIGHT = 50
NBR_ITEMS = 20
# To assure reproductibility, the RNG seed is set prior to the items
# dict initialization. It is also seeded in main().
random.seed(64)
# Create the item dictionary: item name is an integer, and value is
# a (weight, value) 2-uple.
items = {}
# Create random items and store them in the items' dictionary.
for i in range(NBR_ITEMS):
items[i] = (random.randint(1, 10), random.uniform(0, 100))
creator.create("Fitness", base.Fitness, weights=(-1.0, 1.0))
creator.create("Individual", set, fitness=creator.Fitness)
toolbox = base.Toolbox()
# Attribute generator
toolbox.register("attr_item", random.randrange, NBR_ITEMS)
# Structure initializers
toolbox.register("individual", tools.initRepeat, creator.Individual,
toolbox.attr_item, IND_INIT_SIZE)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
def evalKnapsack(individual):
weight = 0.0
value = 0.0
for item in individual:
weight += items[item][0]
value += items[item][1]
if len(individual) > MAX_ITEM or weight > MAX_WEIGHT:
return 10000, 0 # Ensure overweighted bags are dominated
return weight, value
def cxSet(ind1, ind2):
"""Apply a crossover operation on input sets. The first child is the
intersection of the two sets, the second child is the difference of the
two sets.
"""
temp = set(ind1) # Used in order to keep type
ind1 &= ind2 # Intersection (inplace)
ind2 ^= temp # Symmetric Difference (inplace)
return ind1, ind2
def mutSet(individual):
"""Mutation that pops or add an element."""
if random.random() < 0.5:
if len(individual) > 0: # We cannot pop from an empty set
individual.remove(random.choice(sorted(tuple(individual))))
else:
individual.add(random.randrange(NBR_ITEMS))
return individual,
toolbox.register("evaluate", evalKnapsack)
toolbox.register("mate", cxSet)
toolbox.register("mutate", mutSet)
toolbox.register("select", tools.selNSGA2)
def main():
random.seed(64)
NGEN = 50
MU = 50
LAMBDA = 100
CXPB = 0.7
MUTPB = 0.2
pop = toolbox.population(n=MU)
hof = tools.ParetoFront()
stats = tools.Statistics(lambda ind: ind.fitness.values)
stats.register("avg", numpy.mean, axis=0)
stats.register("std", numpy.std, axis=0)
stats.register("min", numpy.min, axis=0)
stats.register("max", numpy.max, axis=0)
algorithms.eaMuPlusLambda(pop, toolbox, MU, LAMBDA, CXPB, MUTPB, NGEN, stats,
halloffame=hof)
return pop, stats, hof
if __name__ == "__main__":
main()