Python遗传算法框架DEAP-Operators and Algorithms

477 查看

Before starting with complex algorithms, we will see some basics of DEAP. First, we will start by creating simple individuals (as seen in the Creating Types tutorial) and make them interact with each other using different operators. Afterwards, we will learn how to use the algorithms and other tools.
在开始复杂的算法之前,我们将会看到一些基础的东西。首先,我们开始创建简单的个体(之前的教程中介绍了)和让它们利用操作算子互相作用。然后,我们将学习怎么使用算法和其他工具。

A First Individual(第一个个体)

First import the required modules and register the different functions required to create individuals that are lists of floats with a minimizing two objectives fitness.
一开始导入需要模块和注册不同的函数去创建个体(浮点数列表),求解的是一个最小化二目标适应度问题。

import random
 
from deap import base
from deap import creator
from deap import tools
 
IND_SIZE = 5
 
creator.create("FitnessMin", base.Fitness, weights=(-1.0, -1.0))
creator.create("Individual", list, fitness=creator.FitnessMin)
 
toolbox = base.Toolbox()
toolbox.register("attr_float", random.random)
toolbox.register("individual", tools.initRepeat, creator.Individual,
                 toolbox.attr_float, n=IND_SIZE)

The first individual can now be built by adding the appropriate line to the script.
第一个个体现在能用合适一行代码去创建添加了。

ind1 = toolbox.individual()

Printing the individual ind1 and checking if its fitness is valid will give something like this
打印出个体ind1,检查它的适应度是否有效:

print ind1               # [0.86..., 0.27..., 0.70..., 0.03..., 0.87...]
print ind1.fitness.valid # False

The individual is printed as its base class representation (here a list) and the fitness is invalid because it contains no values.
这个个体打印出来了。这个适应度是无效的,因为没有值。

Evaluation(重头戏来了,评价函数/适应度函数)

The evaluation is the most personal part of an evolutionary algorithm, it is the only part of the library that you must write yourself. A typical evaluation function takes one individual as argument and returns its fitness as a tuple. As shown in the in the core section, a fitness is a list of floating point values and has a property valid to know if this individual shall be re-evaluated. The fitness is set by setting the values to the associated tuple. For example, the following evaluates the previously created individual ind1 and assigns its fitness to the corresponding values.
评价函数部分是评价算法中最“个人”的部分,它是库中你必须自己写的一部分。一个典型的评价函数需要一个个体作为参数,然后返回这个个体的fitness(以tuple格式)。在核心部分所示,一个适应度值是一个浮点值的列表并有一个有效的属性来知道这个个体是否应当重新评估。这个适应度值是通过设置值和元祖关联。举个例子,下面的选择评价之前创建的个体ind1,同时,分配它的fitness给相应的值。
(⊙o⊙)…让我回忆起专业英语翻译文章的感觉了,难以读懂。硬着头皮往下看吧。

def evaluate(individual):
    # Do some hard computing on the individual
    a = sum(individual)
    b = len(individual)
    return a, 1. / b
 
ind1.fitness.values = evaluate(ind1)
print ind1.fitness.valid    # True    原来有`fitness`是True!搜嘎
print ind1.fitness          # (2.73, 0.2)上面创建的个体IND_SIZE=5,所以这里是1/5=0.2

Dealing with single objective fitness is not different, the evaluation function must return a tuple because single-objective is treated as a special case of multi-objective.
与处理单目标适应度不同,这里评价函数必须返回一个元祖因为单目标是作为多目标的一个特例而已。

Mutation(变异)

There is a variety of mutation operators in the deap.tools module. Each mutation has its own characteristics and may be applied to different types of individuals. Be careful to read the documentation of the selected operator in order to avoid undesirable behaviour.
deap.tools module模块那儿有很多变异操作算子。每个变异算子有它自己的特征,可以应用到不同类型的个体上。仔细阅读选择算子的文档,从而避免无法理解的行为。

The general rule for mutation operators is that they only mutate, this means that an independent copy must be made prior to mutating the individual if the original individual has to be kept or is a reference to another individual (see the selection operator).
变异算子的总体规则就是他们只是变异,这意味着一个独立的副本必须在变异个体操作之前,如果原始个体必须要保存或者是另外个体的参考(看选择算子)。

In order to apply a mutation (here a gaussian mutation) on the individual ind1, simply apply the desired function.
为了在个体ind1上施行变异(这里是高斯变异),简单应用了所需的函数。

mutant = toolbox.clone(ind1)
ind2, = tools.mutGaussian(mutant, mu=0.0, sigma=0.2, indpb=0.2)
del mutant.fitness.values

The fitness’ values are deleted because they’re not related to the individual anymore. As stated above, the mutation does mutate and only mutate an individual it is neither responsible of invalidating the fitness nor anything else. The following shows that ind2 and mutant are in fact the same individual.
适应度值被删除了,因为它们不再和这个个体相关了(因为变异了嘛!!!)。如上面所述,这个变异算子只是变异并且只是变异一个个体,它也不对适应度的无效负责或者其它。下面展示了ind2,变异算子事实上同样的个体。(不是很明白这里)

print ind2 is mutant    # True
print mutant is ind1    # False

Crossover(交叉)

The second kind of operator that we will present is the crossover operator. There is a variety of crossover operators in the deap.tools module. Each crossover has its own characteristics and may be applied to different types of individuals. Be careful to read the documentation of the selected operator in order to avoid undesirable behaviour.
我们将要展示的第二种操作算子是交叉算子。在deap.tools module模块中有很多交叉算子。每种交叉算子有它自己的特性,可能适用于不同类型的个体。仔细阅读选择算子的文档从而避免无法理解的的行为。

The general rule for crossover operators is that they only mate individuals, this means that an independent copies must be made prior to mating the individuals if the original individuals have to be kept or are references to other individuals (see the selection operator).
交叉算子的总体规则是它们值负责交叉个体,这意味着在交叉个体之前需独立的副本,如果原始个体不得不被保存或者作为其他个体的参考(看选择算子)。

Lets apply a crossover operation to produce the two children that are cloned beforehand.
让我们施行交叉算子来产生两个子孙(事先已经被“克隆”了)。

child1, child2 = [toolbox.clone(ind) for ind in (ind1, ind2)]
tools.cxBlend(child1, child2, 0.5)
del child1.fitness.values
del child2.fitness.values

Selection(选择)

Selection is made among a population by the selection operators that are available in the deap.tools module. The selection operator usually takes as first argument an iterable container of individuals and the number of individuals to select. It returns a list containing the references to the selected individuals. The selection is made as follow.
选择是通过选择算子在种群中操作的,它可以在deap.tools module模块中获得。这个选择算子操作通常第一个参数是个体们的可迭代容器,以及要选择个体的个数。它返回的是选择个体们的应用列表。选择算子操作示例如下:

selected = tools.selBest([child1, child2], 2)
print child1 in selected    # True

Usually duplication of the entire population will be made after selection or before variation.
通常在选择或者变异之后,会做整个种群的副本。

selected = toolbox.select(population, LAMBDA)
offspring = [toolbox.clone(ind) for ind in selected]

Using the Toolbox(使用工具箱)

The toolbox is intended to contain all the evolutionary tools, from the object initializers to the evaluation operator. It allows easy configuration of each algorithm. The toolbox has basically two methods, register() and unregister(), that are used to add or remove tools from the toolbox.
工具箱是来包含所有进化工具的,从对象的初始化到评价操作算子。它允许每种算法的配置。工具箱有两个基本的方法,register()unregister(),这个用来从工具箱中增加或者移除工具的。

This part of the tutorial will focus on registration of the evolutionary tools in the toolbox rather than the initialization tools. The usual names for the evolutionary tools are mate(), mutate(), evaluate() and select(), however, any name can be registered as long as it is unique. Here is how they are registered in the toolbox.
教程的这部分将会专注于工具箱中进化工具的注册而不是初始化工具。进化工具通常的名字是mate(),mutate(),evaluate(),select(),然而,任何名字都是可以被注册的,只要它是独一无二的。这里展示了它们是怎么被注册进工具箱的:

from deap import base
from deap import tools

toolbox = base.Toolbox()

def evaluateInd(individual):
    # Do some computation
    return result,

toolbox.register("mate", tools.cxTwoPoint)
toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=1, indpb=0.2)
toolbox.register("select", tools.selTournament, tournsize=3)
toolbox.register("evaluate", evaluateInd)

Using the toolbox for registering tools helps keeping the rest of the algorithms independent from the operator set. Using this scheme makes it very easy to locate and change any tool in the toolbox if needed.
使用工具箱注册工具有助于保持其余算法独立于操作符的集合。使用这种方案使得它很容易定位和改变工具箱中的任何工具。

Using the Tools(使用工具)

When building evolutionary algorithms the toolbox is used to contain the operators, which are called using their generic name. For example, here is a very simple generational evolutionary algorithm.
当建立进化算法时,工具箱是用来容纳这个操作算子的,就是被称为使用它们的通用名字。例如,这里是一个非常简单的进化算法。

for g in range(NGEN):
    # Select the next generation individuals
    offspring = toolbox.select(pop, len(pop))
    # Clone the selected individuals
    offspring = map(toolbox.clone, offspring)

    # Apply crossover on the offspring
    for child1, child2 in zip(offspring[::2], offspring[1::2]):
        if random.random() < CXPB:
            toolbox.mate(child1, child2)
            del child1.fitness.values
            del child2.fitness.values

    # Apply mutation on the offspring
    for mutant in offspring:
        if random.random() < MUTPB:
            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 = toolbox.map(toolbox.evaluate, invalid_ind)
    for ind, fit in zip(invalid_ind, fitnesses):
        ind.fitness.values = fit

    # The population is entirely replaced by the offspring
    pop[:] = offspring

This is a complete algorithm. It is generic enough to accept any kind of individual and any operator, as long as the operators are suitable for the chosen individual type. As shown in the last example, the usage of the toolbox allows to write algorithms that are as close as possible to pseudo code. Now it is up to you to write and experiment on your own.
这是一个完整的算法。它是足够通用的来接收各种个体和任何操作算子,只要操作算子是是适合选择的个体类型。正如最后一个例子所示,工具箱的使用允许写算法(和伪代码尽可能相近)。现在取决于你自己写、实验你的算法。

Tool Decoration(工具装饰)

Tool decoration is a very powerful feature that helps to control very precise things during an evolution without changing anything in the algorithm or operators. A decorator is a wrapper that is called instead of a function. It is asked to make some initialization and termination work before and after the actual function is called.
工具装饰是一个非常强大的特性,在进化过程中它有助于控制非常精确的东西而不需要改变算法或者算子的其他东西。一个装饰器被称为一个包装器而不是一个函数。它是在一些初始化和终止工作的时候被访问,在实际函数调用之前或者之后。

For example, in the case of a constrained domain, one can apply a decorator to the mutation and crossover in order to keep any individual from being out-of-bound. The following defines a decorator that checks if any attribute in the list is out-of-bound and clips it if this is the case. The decorator is defined using three functions in order to receive the min and max arguments. Whenever the mutation or crossover is called, bounds will be checked on the resulting individuals.
例如,在约束域的例子里,一个能对变异和交叉引用装饰器为了避免个体出界,如果出界就去除它。装饰器使用三个函数来定义为了接受最小和最大的参数。变异或交叉操作无论什么时候被调用,都会对结果个体们进行范围检查。
╮(╯▽╰)╭无奈,不明白啊,加油

def checkBounds(min, max):
    def decorator(func):
        def wrapper(*args, **kargs):
            offspring = func(*args, **kargs)
            for child in offspring:
                for i in xrange(len(child)):
                    if child[i] > max:
                        child[i] = max
                    elif child[i] < min:
                        child[i] = min
            return offspring
        return wrapper
    return decorator

toolbox.register("mate", tools.cxBlend, alpha=0.2)
toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=2)

toolbox.decorate("mate", checkBounds(MIN, MAX))
toolbox.decorate("mutate", checkBounds(MIN, MAX))

This will work on crossover and mutation because both return a tuple of individuals. The mutation is often considered to return a single individual but again like for the evaluation, the single individual case is a special case of the multiple individual case.
这将会作用在交叉和变异算子上因为两者都返回一个个体们的元祖。变异通常被认为返回一个单一个体但是却像评估算子一样,单一个体的例子是多目标的一个特殊例子。