|
|
|
|
移动端
创建专栏

利用达尔文的理论学习遗传算法

本文借助生物学中达尔文的进化理论来介绍遗传算法,并展示了通过简短的 Python 教程实现遗传算法的案例。

作者:黄小天 路雪 译|2017-10-07 16:13

年前最后一场技术盛宴 | 1月27日与京东、日志易技术大咖畅聊智能化运维发展趋势!


本文借助生物学中达尔文的进化理论来介绍遗传算法,并展示了通过简短的 Python 教程实现遗传算法的案例。

利用达尔文的理论学习遗传算法

在本文中,我将会解释遗传算法的概念。首先,我会介绍它的起源与目标。接着,我会展示如何通过一个简短的 Python 教程实现它。

所以,问题就是:我们如何打造好的人工智能?

一个天真的解决方案就是创建由一系列规则构成的「经验算法」:「如果你遇到这些条件,就这么执行。」我可以想象到通过这样的足够多的规则,就能再现自然智能。但工作量会异常大,最终方案将永远无法使创建者满意。花大把时间创造一样东西却得知它不可能完美,还是挺让人沮丧的吧?

为了避免这一点,我有了一个新想法。如果我舍弃直接的方案而选择再现进化会怎么样。我们可以创造第一条史前鱼,将其放入适合进化的条件内,让它自由地向人类进化。这个想法被称为「遗传算法」,我们将在下文中亲自创建一个。那么,首先让我们回忆一下,试着理解达尔文的自然选择理论。

这个理论很简单:如果一个种群想要兴盛,它必须不断提升自己,这被称为适者生存。种群中最优秀的品质遗传给后代。但是为了保持一些多样性以及适应自然环境的变化,其他个体也不能被遗忘。

从一代到下一代

从一代到下一代

这一算法尤其擅长解决优化问题。

实例:背包问题

背包优化是一个经典的算法问题。你有两样东西:一个容量为固定重量的背包和一系列不同重量和价值的盒子。目标是装满这个背包使其价值最大化却又不超出它的最大承载重量。自 1972 年以来,这一直是一个著名的数学问题。遗传算法可以很好地解决这一问题,因为它本质上是一个具有大量可能答案的优化问题。

背包问题

背包问题

为了亲自测试这一算法的工作原理,我们用它解决一个简单的问题:如何破解同事的密码。

该算法用 Python 3 实现。你可以下载 Windows 版或者使用 brew install python3 、sudo apt-get install python3 或 sudo yum install python3 进行安装。我建议你在 Jupyter notebook 中运行这一代码。

选择一个适应度函数

当你决定创建一个遗传算法时,要做的第一件事情就是创建评估函数,用来评估样本的成功:它允许我们把种群划分为丑小鸭和白天鹅。区分的目标在于成功的样本稍后将有更多机会被挑选来塑造下一代。这看起来很简单,但是不要被愚弄了。

我们的目标是什么?破解密码。因此我们函数的目标是把连续标记中的二值结果「失败或成功」从 0 转换到 100。

这里有一个最简单的方案:

  1. fitness score = (number of char correct) / (total number  

如此,一个带有更大适合度结果的个体将比其他样本更接近成功。因此我们的适合度函数能够精确分类种群。

  1. def fitness (password, test_word):   
  2.     if (len(test_word) != len(password)): 
  3.         print("taille incompatible")        return 
  4.     else: 
  5.         score = 0 
  6.         i = 0 
  7.         while (i < len(password)):          if (password[i] == test_word[i]): 
  8.                 score+=1 
  9.             i+=1 
  10.         return score * 100 / len(password)    

创建个体

那么现在我们知道如何评估个体,但是又如何对其定义呢?这一部分确实需要一点技巧:我们的目标是知道什么是固定不变的规格参数表以及什么是变量。

此处,将其比作遗传会有一定帮助。DNA 实际上是由基因组成,而基因又来自不同的等位基因(该基因的不同版本)。因此你不得不创建你的种群的 DNA。

在我们的情况中,个体是单词(密码的长度当然是相同的),每个字母是一个基因,字母的值是等位基因。在单词「banana」中,「b」是第一个字母的等位基因。

这一创建的意义是什么?现在我们知道每一个个体保持良好形状(一个正确长度的单词),但是种群将会覆盖每一种可能性(每个可能带有这一长度的单词)。

创建第一个种群

现在,我们知道个体的规格参数表,以及如何评估其表现。我们必须开始严格意义上的「进化」。

要记住的主要一点是我们不能把种群导向一个明显很好的方案。我们必须使种群尽可能宽广,覆盖尽可能多的可能性。第一个完美的种群应该覆盖每一个现有的等位基因。

因此,在这个案例中,我们将创建由随机字母组成的单词。

  1. import random 
  2.  
  3. def generateAWord (length): 
  4.     i = 0 
  5.     result = "" 
  6.     while i < length: 
  7.         letter = chr(97 + int(26 * random.random())) 
  8.         result += letter 
  9.         i +=1 
  10.     return result 
  11.  
  12. def generateFirstPopulation(sizePopulation, password): 
  13.     population = [] 
  14.     i = 0 
  15.     while i < sizePopulation: 
  16.         population.append(generateAWord(len(password))) 
  17.         i+=1 
  18.     return population 

从一代到下一代

为此,我们有两件事要做:选择我们当前代中的一个特定部分,并且整合育种者孕育下一代。

1. 育种者选择

我们有很多选择,但是你必须牢记两点:目标是选择前一代的最佳方案并且不把其他方案彻底放在一边。危害是:如果你在算法开始时只选择好的方案,你将很快向局部最小值而不是可能的最佳方案收敛。

我的方案是一方面选择更好的样本 N(在我的代码中,N = best_sample),另一方面选择无需区分适合度的 M 个随机个体(M = lucky_few)。

  1. import operatorimport random 
  2.  
  3. def computePerfPopulation(population, password): 
  4.     populationPerf = {} for individual in population: 
  5.         populationPerf[individual] = fitness(password, individual)  return sorted(populationPerf.items(), key = operator.itemgetter(1), reverse=True
  6.  
  7. def selectFromPopulation(populationSorted, best_sample, lucky_few): 
  8.     nextGeneration = [] for i in range(best_sample): 
  9.         nextGeneration.append(populationSorted[i][0])   for i in range(lucky_few): 
  10.         nextGeneration.append(random.choice(populationSorted)[0]) 
  11.     random.shuffle(nextGeneration)  return nextGeneration 

2. 繁衍

我们继续用生物意义的繁衍进行类比。有性繁殖的目的是混合两个个体的 DNA,这里我们也在做同样的事情。我们有两个个体「Tom」和「Jerry」,他们的 DNA 由其等位基因定义。因此为了混合其 DNA,我们必须混合字母。我挑选了一种最简单的解决方案:随机选择「Tom」或「Jerry」的字母作为子对象的字母。

很明显,父母「Tom」和「Jerry」并不会只生一个孩子,为了确保种群数量稳定,你必须确定每对父母所生孩子的数量(0 代中的个体数量等于下一代中的个体数量)。

  1. import random 
  2.  
  3. def createChild(individual1, individual2): 
  4.     child = "" 
  5.     for i in range(len(individual1)):       if (int(100 * random.random()) < 50): 
  6.             child += individual1[i]     else: 
  7.             child += individual2[i] return child 
  8.  
  9. def createChildren(breeders, number_of_child): 
  10.     nextPopulation = [] for i in range(len(breeders)/2):        for j in range(number_of_child): 
  11.             nextPopulation.append(createChild(bre 

3. 突变

最后一步是个体的自然突变。繁衍之后,每一个个体都存在一定的概率:DNA 发生轻微改变。这一操作的目的是防止算法在局部极小值中受限。

  1. import random 
  2.  
  3. def mutateWord(word): 
  4.     index_modification = int(random.random() * len(word))   if (index_modification == 0): 
  5.         word = chr(97 + int(26 * random.random())) + word[1:]   else: 
  6.         wordword = word[:index_modification] + chr(97 + int(26 * random.random())) + word[index_modification+1:]    return word 
  7.      
  8. def mutatePopulation(population, chance_of_mutation):   for i in range(len(population)):        if random.random() * 100 < chance_of_mutation: 
  9.             population[i] = mutateWord(populati 

现在,你有了构建遗传算法的所有工具。请随意修改我的实现。每一步都有很多种可能的方案,请采取最合适的那个。

原文:https://blog.sicara.com/getting-started-genetic-algorithms-python-tutorial-81ffa1dd72f9

【本文是51CTO专栏机构“机器之心”的原创译文,微信公众号“机器之心( id: almosthuman2014)”】

戳这里,看该作者更多好文

【编辑推荐】

  1. 深度学习 + OpenCV,Python实现实时视频目标检测
  2. 高盛最新调查:Python超过汉语成为未来最重要技能,你准备学哪种编程语言?
  3. 2017新零售与人工智能论坛— Focussend浅谈智能化营销如何助力重构零售消费场景
  4. 靠默契保证的私有制:Python 中的私有
  5. 摄影爱好者玩编程:利用Python和OpenCV打造专业级长时曝光摄影图
【责任编辑:赵宁宁 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

热门职位+更多