(七) 约束操作
演化算法通常情况下是无约束的,在这部分中,我们提出了一些方法去约束你的进化算法,这个教程是基于Coello[Coello 2002]的文章展开的。
-
Penalty Function
罚函数是最基本的个体约束方法,当它们处于一个特定区间时,这些个体不可以被计算适应值或者在某些问题中会禁止它们的使用。罚函数根据约束变化的数量对这些个体给出了适应度的缺陷。例如,在一个变化的约束中计算个体适应度,可以对它的适应度指定一个期待的值。这个被指定的值可以是常数或者随着有效解的变化而变化。下面的图展示了一个个体属性的适应度函数g(x)(绿色)和一个罚函数h(x)(红色),约束条件为3< x < 7。连续的曲线代表个体真实的适应度
上图中,左边的那个在是当变量不服从约束时,使用了常量互补方法h(x)= \ Delta(简单粗暴,直接放入可行域?)。中间的使用欧氏距离与之前的h(x)相加,构成了一个碗形的function,h(x) = delta + sqrt((x-x_{0})^2)。最后一张图则使用了一个平方项来提升“碗”的深度,h(x) = delta + (x-x_{0})^2,x_0指的是可行域的中心。
在DEAP中,一个罚函数可以被添加到任何适应度函数当中,使用在tools中的DeltaPenalty装饰符就可以实现这个目标。罚函数使用两个强制参数和一个可选参数。第一个参数是一个可以根据用户定义的界限返回有效个体的函数,第二个是无效个体和有效区域间的距离。最后一个参数则默认为0。在下面的例子中,我们将会展示如何获得上图中最右图的效果。
from math import sin
import random
from deap import tools, base, creator
#creating individuals
IND_LEN = 2 #each individual's length
#creating maxfit
creator.create('MaxFit', base.Fitness, weights = (1.0,))
#creating individual
creator.create('Individual',list, fitness = creator.MaxFit)
#called toolbox
toolbox = base.Toolbox()
#define generating style
toolbox.register('fltrand', random.uniform, 0, 10)
#define individual
toolbox.register('individual',tools.initRepeat, creator.Individual, toolbox.fltrand ,n = IND_LEN)
#define population
toolbox.register('population', tools.initRepeat, list, toolbox.individual)
pop = toolbox.population(n = 200)
#define evalute
def evalFct(individual):
"""Evaluation function for the individual."""
x = individual[0]
return (x - 5)**2 * sin(x) * (x/3),
def feasible(individual):
"""Feasibility function for the individual. Returns True if feasible False
otherwise."""
if 3 < individual[0] < 7:
return True
return False
def distance(individual):
"""A distance function to the feasibility region."""
return (individual[0] - 5.0)**2
toolbox = base.Toolbox()
toolbox.register("evaluate", evalFct)
toolbox.decorate("evaluate", tools.DeltaPenalty(feasible, 7.0, distance))
toolbox.register('crossover', tools.cxTwoPoint)
toolbox.register('mutate', tools.mutFlipBit, indpb = 0.5)
toolbox.register('select', tools.selTournament, tournsize = 3)
def simpleEA():
#paramenters
CXPB = 0.1 #possiblity of crossover
MUPB = 0.1 #possiblity of mutation
GEN = 40 #iteration number
GR_SIZE = 200 #population size
# generating pop
#pop = toolbox.population(n = GR_SIZE)
'''BEGIN!!!'''
print('Start Evo')
#calculating original group's fitness
fitnesses = list(map(toolbox.evaluate, pop))
for ind, fit in zip(pop, fitnesses):
ind.fitness.values = fit
print(" Evaluated %i individuals" % len(pop))
'''Evolution!!!'''
for g in range(0, GEN):
print('--generation %i-- ' %g)
#select
offspring = toolbox.select(pop, len(pop))
offspring = list(map(toolbox.clone, offspring))
#crossover
for child1, child2 in zip(offspring[::1], offspring[1::2]):
if random.random() < CXPB:
toolbox.crossover(child1, child2)
#delete fitness of those children
del child1.fitness.values
del child2.fitness.values
#mutation
for mutant in offspring:
if random.random() < MUPB:
toolbox.mutate(mutant)
del mutant.fitness.values
#evaluate those invalid individuals
invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
fitnesses = list(map(toolbox.evaluate, invalid_ind))
for ind, fit in zip(invalid_ind, fitnesses):
ind.fitness.values = fit
print('Evaluated %i individuals' %len(invalid_ind))
#refresh the group by offsprings
pop[:] = offspring
#gather all fitness in one list
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(" Min %s" % min(fits))
print(" Max %s" % max(fits))
print(" Avg %s" % mean)
print(" Std %s" % std)
print("-- End of (successful) evolution --")
best_ind = tools.selBest(pop, 1)[0]
print("Best individual is %s, %s" % (best_ind, best_ind.fitness.values))
if __name__ == "__main__":
simpleEA()
上面是一个简单地使用罚函数限制x范围的方法,从结果中可以看出,无论如何计算,x均不会超出7。罚函数在解决有约束规划问题上可以有很好的效果,它根据不同的惩罚规则将越界的数据点的适应度进行修正。