如何用遗传算法进化出一只聪明的小鹦鹉
- 2019 年 10 月 6 日
- 笔记
问题
现在有一些样本数据,如下表所示。你是否能找到其中的规律,然后计算出新样本的output是多少?
|
input |
output |
---|---|---|
样本1 |
5, 8, 7 |
9 |
样本2 |
2, 8, 4 |
6 |
样本3 |
3, 0, 5 |
3 |
样本4 |
9, 12, 13 |
15 |
新样本 |
10, 6, 8 |
? |
乍一看,规律似乎并不明显。
仔细看,可能会发现其中的规律:output=input1+input2/2+input3*0,因此问号处的值应该是13。
现在,我们想让一只小鹦鹉来做这个事情。这当然不是现实中的小鹦鹉,而是我们用代码写的小鹦鹉。
小鹦鹉
小鹦鹉要想解出上面的题目,必须会“思考”。这里的思考,我们可以理解为:具有将input转换成output的能力。我们选择神经网络作为鹦鹉的大脑。
神经网络
我们知道,人脑中有无数个神经元,神经元之间通过轴突连接。当某个神经元接收到足够多的信号后,就会被“触发”。而被“触发”的神经元会发送信号到临近的神经元,临近的神经元接收到足够的信号后,又会被“触发”。这样的过程被称之为“思考”。
我们可以用程序模拟这个过程。为了简化,这里只使用一层网络。我们给每个轴突(连接神经元的线)设置一个权重,每一个input乘以相应的轴突权重会得出一个值,然后对这些值再求和,得出output。即output=input1*w1+input2*w2+input3*w3
这就是一个简单的“大脑”。大脑是否聪明,取决于[w1,w2,w3]的值的组合。如果这三个权重值能够满足提出的问题,那它就是“聪明”的,否则就是“笨”的。于是,刚才的问题就转换成:如何找到一组“聪明”的权重组合。
对于文章开头的问题,我们已经知道,[w1=1,w2=0.5,w3=0]这种组合是最好的。那,如何生成这样的“大脑”呢?
上帝之手
现在,我们可以扮演上帝的角色,随性的创造物种。假设我们创造了一只鹦鹉,它的脑子就是一个有3个权重的神经网络。这只鹦鹉只会做上面的题(但不一定能做好)。它的大脑是随机生成的,假设他的权重组合为[0.5,0.3,1.2]。
此时,这只鹦鹉就可以用大脑去“思考”上面的问题了。它会先看样本数据,从样本1开始看。这只鹦鹉根据input算出来的output是:0.5*5+0.3*8+1.2*7=13.3,而样本给出的output是9。于是它的误差(error1)是13.3-9=4.3。同样,还可以知道这只鹦鹉计算其它样本时的误差,它们分别是:
error2=2.2 error3=4.5 error4=8.7
我们知道,鹦鹉计算得越准(误差越小),就越聪明。因此,我们可以给鹦鹉的智商设定一个计算方法:
根据这个公式,可以算出这只鹦鹉的智商(intelligence)为9.15。
我们再随机创建第二只鹦鹉。假设它的大脑的神经网络权重组合为[0.8,0.7,0.1]。
根据同样的方法,可以算出它的智商为35.65。它比第一支鹦鹉“聪明”!
进化
种群
我们可以创建一个鹦鹉种群,这个种群里有若干只鹦鹉,比如有100只。每个鹦鹉的智商也各不相同。下一步就是让这个种群进行自我进化。
进化
物竞天择、适者生存!大自然的这个法则,让地球上的物种不断进化,诞生了越来越高级的物种,乃至出现了我们人类。我们的这个鹦鹉种群也可以通过进化变得越来越聪明。
种群中的鹦鹉比拼的是智商。每一次比拼,我们把低智商的鹦鹉淘汰掉,把高智商鹦鹉留下来。然后,让留下来的鹦鹉繁衍下一代,这样它们和下一代的个体会组成一个新群体。然后再来一次全员智商比拼,再把低智商鹦鹉淘汰掉,高智商留下。留下的鹦鹉再次繁殖。这样不断迭代,经过若干代的筛选后,最后留下的就是特别聪明的鹦鹉。
在最后留下来的鹦鹉中,找到最聪明的那个。让它去“思考”文章开头提到的问题,它会给出一个不错的答案!
繁衍
现实中的鹦鹉是双性繁殖,那是长期进化的结果。我们的鹦鹉还很原始,只能单性繁殖:)
繁殖的方式是把自己复制一份,再做一次“基因突变”。所谓“基因突变”就是对神经网络的权重进行微调。比如有一只鹦鹉,它的大脑神经网络是[2, 3, 3.5],它繁衍的某一个后代可能是[2, 3.01, 3.5]或[1.98, 3, 3.5]等。
这样,后代基本保留了父代智商,又做了微调。这种微调可能是正向的(更聪明),也可能是负向的(更笨)。
代码
import random import copy import numpy as np class Parrot(): brain = 2 * np.random.random((3, 1)) - 1 intelligence = 0 def think(self,inputs): return np.dot(inputs,self.brain) ''' 基因变异 ''' def mutation(self,max=0.05): #随机选中一个神经网络权重,进行微调 index = np.random.randint(0,len(self.brain)) adjustment = np.random.uniform(-max,max) self.brain[index] +=adjustment def copy(self): child = Parrot() child.brain = copy.copy(self.brain) child.intelligence = self.intelligence return child class GA(): def __init__(self, count): #进化的代数 self.gen_num = 0 # 种群中个体数量 self.count = count # 种群个体要尽可能得去满足input和output关系 self.inputs,self.outputs=self.load_data() # 随机生成初始种群 self.population = self.gen_population(count) def load_data(self): inputs = np.array([[5, 8, 7], [2, 8, 4], [3, 0, 5], [9, 12, 13]]) outputs = np.array([[9, 6, 3, 15]]).T return inputs,outputs def create_parrot(self): parrot = Parrot() parrot.brain = 2 * np.random.random((3, 1)) - 1 parrot.intelligence = self.calc_intelligence(parrot) return parrot """ 获取初始种群 """ def gen_population(self,count): return [self.create_parrot() for i in range(count)] #计算适应度 def calc_intelligence(self, parrot): errors = self.outputs - parrot.think(self.inputs) intelligence = 1/np.sum(np.sqrt(np.square(errors))) return intelligence ''' 繁殖 ''' def reproduction(self, mothers): # 新出生的孩子,最终会被加入存活下来的父母之中,形成新一代的种群。 children = [] # 需要繁殖的孩子的量 target_count = len(self.population) - len(mothers) # 开始根据需要的量进行繁殖 while len(children) < target_count: mother = mothers[np.random.randint(0,len(mothers))] child = mother.copy() child.mutation() children.append(child) self.population = mothers + children ''' 选择 ''' def selection(self, retain_rate, random_select_rate): # 计算每一个鹦鹉的智商 graded = [(self.calc_intelligence(parrot), parrot) for parrot in self.population] # 智商从大到小进行排序 graded = [x[1] for x in sorted(graded, key = lambda graded: graded[0], reverse=True)] # 选出智商最高的一部分鹦鹉 retain_length = int(len(graded) * retain_rate) retain_parrots = graded[:retain_length] # 选出智商不高,但是幸存的鹦鹉 for parrot in graded[retain_length:]: if random.random() < random_select_rate: retain_parrots.append(parrot) return retain_parrots ''' 进化 retain_rate:保留最聪明个体的比例 random_select_rate:不够聪明的个体中,也要保留一定比例的个体,以保持物种的多样性。 ''' def evolve(self,retain_rate=0.2, random_select_rate=0.2): self.gen_num += 1 mothers = self.selection(retain_rate, random_select_rate) self.reproduction(mothers) def smartest_parrot(self): graded = [(self.calc_intelligence(parrot), parrot) for parrot in self.population] graded = [x[1] for x in sorted(graded, key = lambda graded: graded[0], reverse=True)] return graded[0] if __name__ == '__main__': #种群数量为100 ga = GA(100) for x in range(300): ga.evolve() winner = ga.smartest_parrot() print("brain=",winner.brain.T) print("output=",winner.think(np.array([10, 6, 8])))
运行
python3 ga.py
运行结果
brain= [[0.99293629 0.498883 0.00599333]] output= [12.97060759]
新样本中的output应该是13,进化出来的鹦鹉给出的答案是12.97060759,已经相当接近!
它的大脑[0.99293629, 0.498883, 0.00599333]与最优解[1,0.5,0]非常接近!
这就是自然选择的力量!
让我们向大自然的规则致敬!