我的目标很简单,使用遗传算法重现经典" Hello,World"字符串。
我的代码基于此post。代码主要包含4个部分:
target
的比较,定义评估个人好坏的适合度和等级函数。 len(pop)*retain
个人population
。我修改了代码并显示如下:
import numpy as np
import string
from operator import add
from random import random, randint
def population(GENSIZE,target):
p = []
for i in range(0,GENSIZE):
individual = [np.random.choice(list(string.printable[:-5])) for j in range(0,len(target))]
p.append(individual)
return p
def fitness(source, target):
fitval = 0
for i in range(0,len(source)-1):
fitval += (ord(target[i]) - ord(source[i])) ** 2
return (fitval)
def grade(pop, target):
'Find average fitness for a population.'
summed = reduce(add, (fitness(x, target) for x in pop))
return summed / (len(pop) * 1.0)
def evolve(pop, target, retain=0.2, random_select=0.05, mutate=0.01):
graded = [ (fitness(x, target), x) for x in p]
graded = [ x[1] for x in sorted(graded)]
retain_length = int(len(graded)*retain)
parents = graded[:retain_length]
# randomly add other individuals to
# promote genetic diversity
for individual in graded[retain_length:]:
if random_select > random():
parents.append(individual)
# mutate some individuals
for individual in parents:
if mutate > random():
pos_to_mutate = randint(0, len(individual)-1)
individual[pos_to_mutate] = chr(ord(individual[pos_to_mutate]) + np.random.randint(-1,1))
#
parents_length = len(parents)
desired_length = len(pop) - parents_length
children = []
while len(children) < desired_length:
male = randint(0, parents_length-1)
female = randint(0, parents_length-1)
if male != female:
male = parents[male]
female = parents[female]
half = len(male) / 2
child = male[:half] + female[half:]
children.append(child)
parents.extend(children)
return parents
GENSIZE = 40
target = "Hello, World"
p = population(GENSIZE,target)
fitness_history = [grade(p, target),]
for i in xrange(20):
p = evolve(p, target)
fitness_history.append(grade(p, target))
# print p
for datum in fitness_history:
print datum
但似乎结果不能很好地适应target
。
我尝试更改GENESIZE
和循环时间(更多代)
但结果总是卡住。有时,增强循环时间有助于找到最佳解决方案。但是当我将循环时间更改为更大的数字时,例如for i in xrange(10000)
。结果显示错误如:
individual[pos_to_mutate] = chr(ord(individual[pos_to_mutate]) + np.random.randint(-1,1))
ValueError: chr() arg not in range(256)
无论如何,如何修改我的代码并取得好成绩 任何建议都会很感激。
答案 0 :(得分:0)
Python2中的chr
函数只接受范围为0&lt; = i&lt; 256。
你正在传递:
ord(individual[pos_to_mutate]) + np.random.randint(-1,1)
所以你需要检查
的结果 ord(individual[pos_to_mutate]) + np.random.randint(-1,1)
不会超出该范围,如果超出该范围,则在传递给chr
之前采取纠正措施。
修改强>
对ValueError的合理修复可能是在传递给chr
之前将修改后的值取模256:
chr((ord(individual[pos_to_mutate]) + np.random.randint(-1, 1)) % 256)
还有另一个错误:适应度计算没有考虑候选列表的最后一个元素:它应该是:
def fitness(source, target):
fitval = 0
for i in range(0,len(source)): # <- len(source), not len(source) -1
fitval += (ord(target[i]) - ord(source[i])) ** 2
return (fitval)
鉴于源和目标的长度必须相等,该函数可写为:
def fitness(source, target):
return sum((ord(t) - ord(s)) ** 2 for (t, s) in zip(target, source))
真正的问题是,为什么提供的代码不会演变随机字符串,直到达到目标字符串。
我相信,答案是可能的,但需要进行大量的迭代才能完成。
考虑一下,在问题中引用的博客文章中,每次迭代都会生成一个子项,如果孩子更健康,它将替换基因池中最不适合的成员。选择孩子的父母偏向于更健康的父母,增加了孩子进入基因库并提高整体健康水平的可能性。游泳池因此,基因库的成员在几千次迭代中收敛于期望的结果。
在问题的代码中,基于初始条件,突变的概率要低得多,这是evolve
函数的默认值。
被保留的父母只有1%的机会发生变异,三分之一的时间是变异&#34;不会导致更改(零是random.randint(-1,1)的可能结果)。
弃置父母被由合并两名留住的个人创建的个人所取代。由于只保留了20%的父母,因此每个新生儿实际上是现有父母的副本,人口可以收敛于当地最小值,因此不会引入多样性。
除了修复这两个错误之外,在目标上更快收敛的方法是尝试初始条件并考虑更改问题中的代码以注入更多的多样性,例如通过像原始一样改变孩子博客文章,或通过扩大可能的突变范围。