基于遗传算法(GA)进化的小游戏

本文涉及的产品
全球加速 GA,每月750个小时 15CU
简介: 本文是一篇使用Python语言模拟生物体和食物之间的关系和基于遗传算法进化生物体的实例教程

在这篇文章中,我们模拟了一个包含生物体和食物的环境,生物体为了生存必须尽可能多的消耗食物。在模拟环境中,生物体将由一个简单的、完全连接的神经网络来控制。神经网络的输入是一种标准化值,从-1到+1之间,这表示最近的食物颗粒的方向。这个方向是通过最近的食物颗粒(+/-180度)方向计算出来的。下面是两个生物体和食物颗粒的示例:

 

10a088d939a2733c1251e88cc06deed94755c383

因为我们的输入范围从-1到+1,所以输出范围应当也从-1到+1,因此tanh函数将会成为理想的激活函数。下面是一个神经网络的图和它的输入、输出以及它的隐藏层:

77b8a6e6a4e2cff2b811392410b4edaf3823e9f7 

由于神经网络仅仅是简单的矩阵乘法,因此利用NumPy只需要几行代码就可以了。在进化过程中,一些权重会被优化。最后,我将默认的隐藏节点数设置为5,这个可以根据你自己的喜好设置。接下来是编写Python代码模拟整个生态:

一、生物


生物类包含了神经网络以及更新它的速度和位置的函数。当一个生物体被初始化时,它的位置、方向、速度、加速度和神经网络的权重都会随机产生。下面是这个生物类的代码:

class organism():
    def __init__(self, settings, wih=None, who=None, name=None):
        self.x = uniform(settings['x_min'], settings['x_max'])  # position (x)
        self.y = uniform(settings['y_min'], settings['y_max'])  # position (y)
        self.r = uniform(0,360)                 # orientation   [0, 360]
        self.v = uniform(0,settings['v_max'])   # velocity      [0, v_max]
        self.dv = uniform(-settings['dv_max'], settings['dv_max'])   # dv
        self.d_food = 100   # distance to nearest food
        self.r_food = 0     # orientation to nearest food
        self.fitness = 0    # fitness (food count)
        self.wih = wih
        self.who = who
        self.name = name
    # NEURAL NETWORK
    def think(self):
        # SIMPLE MLP
        af = lambda x: np.tanh(x)               # activation function
        h1 = af(np.dot(self.wih, self.r_food))  # hidden layer
        out = af(np.dot(self.who, h1))          # output layer
        # UPDATE dv AND dr WITH MLP RESPONSE
        self.nn_dv = float(out[0])   # [-1, 1]  (accelerate=1, deaccelerate=-1)
        self.nn_dr = float(out[1])   # [-1, 1]  (left=1, right=-1)
    # UPDATE HEADING
    def update_r(self, settings):
        self.r += self.nn_dr * settings['dr_max'] * settings['dt']
        self.r = self.r % 360
    # UPDATE VELOCITY
    def update_vel(self, settings):
        self.v += self.nn_dv * settings['dv_max'] * settings['dt']
        if self.v < 0: self.v = 0
        if self.v > settings['v_max']: self.v = settings['v_max']
    # UPDATE POSITION
    def update_pos(self, settings):
        dx = self.v * cos(radians(self.r)) * settings['dt']
        dy = self.v * sin(radians(self.r)) * settings['dt']
        self.x += dx
        self.y += dy

二、食物


食物类包含坐标位置和能量值,能量值的多少将直接影响生物体的健康。现在,这个能量值保持不变,并设为1,但如果你想要修改它,它可以被随机化或改变为任何值。此外,当食物被生物体消耗后,respawn函数就会被调用,用于重新生成食物颗粒的位置。这使得每个模拟时间内的食物颗粒总数保持不变。

class food():
    def __init__(self, settings):
        self.x = uniform(settings['x_min'], settings['x_max'])
        self.y = uniform(settings['y_min'], settings['y_max'])
        self.energy = 1
    def respawn(self,settings):
        self.x = uniform(settings['x_min'], settings['x_max'])
        self.y = uniform(settings['y_min'], settings['y_max'])
        self.energy = 1

三、进化


生物体将使用遗传算法(GA)进行优化,遗传算法(GA)是属于进化算法(EA)的更大范围。遗传算法模仿自然的生物进化过程,从最初的种群开始,通过选择、交叉和变异产生后代,形成一个最优的解决方案。对于本教程,EA计划如下:

1、选择:简单的选择形式被称为精英主义,即选择最优秀的下一代。

2、跨界:在精英主义阶段中随机选择个体作为父母,并产生一个新的后代。因此我们要处理神经网络的权重,下面的方程将两个父母之间的性状遗传给后代:

offspring=parent1(a)+parent2(1−a)

a是在0和1之间随机生成的值,而父1和父2表示神经网络的权重。因为有两个正在混合的矩阵(wih和who),因此方程实际上是这样的:

offspring_wih=parent_wih1(a)+parent_wih2(1−a)

offspring_who=parent_who1(a)+parent_who2(1−a)

3、突变:一旦产生新的后代就会产生一个介于0和1之间的随机数。如果这个值低于用户初始化的突变阈值,就会发生突变。在发生突变的情况下,两个神经网络权重矩阵中的一个随机权重将被一个随机值所取代,这个值在初始值的+/-10%之间。这将会在生物体中产生微小的变化,从而有可能产生更健康的生物体。突变效果仅限于原始值的+/-10%,因为我们希望避免在神经网络中造成灾难性的故障而导致整个生物群体瘫痪。

下面的图显示了这个EA方案:

 

f15d9133423b82edbeb3a8023f8c9535542a6d38 

这个EA代码程序如下所示:

ef evolve(settings, organisms_old, gen):
    elitism_num = int(floor(settings['elitism'] * settings['pop_size']))
    new_orgs = settings['pop_size'] - elitism_num
    #--- GET STATS FROM CURRENT GENERATION ----------------+
    stats = defaultdict(int)
    for org in organisms_old:
        if org.fitness > stats['BEST'] or stats['BEST'] == 0:
            stats['BEST'] = org.fitness
        if org.fitness < stats['WORST'] or stats['WORST'] == 0:
            stats['WORST'] = org.fitness
        stats['SUM'] += org.fitness
        stats['COUNT'] += 1
    stats['AVG'] = stats['SUM'] / stats['COUNT']
    #--- ELITISM (KEEP BEST PERFORMING ORGANISMS) ---------+
    orgs_sorted = sorted(organisms_old, key=operator.attrgetter('fitness'), reverse=True)
    organisms_new = []
    for i in range(0, elitism_num):
        organisms_new.append(organism(settings, wih=orgs_sorted[i].wih, who=orgs_sorted[i].who, name=orgs_sorted[i].name))
    #--- GENERATE NEW ORGANISMS ---------------------------+
    for w in range(0, new_orgs):
        # SELECTION (TRUNCATION SELECTION)
        canidates = range(0, elitism_num)
        random_index = sample(canidates, 2)
        org_1 = orgs_sorted[random_index[0]]
        org_2 = orgs_sorted[random_index[1]]
        # CROSSOVER
        crossover_weight = random()
        wih_new = (crossover_weight * org_1.wih) + ((1 - crossover_weight) * org_2.wih)
        who_new = (crossover_weight * org_1.who) + ((1 - crossover_weight) * org_2.who)
        # MUTATION
        mutate = random()
        if mutate <= settings['mutate']:
            # PICK WHICH WEIGHT MATRIX TO MUTATE
            mat_pick = randint(0,1)     
            # MUTATE: WIH WEIGHTS
            if mat_pick == 0:
                index_row = randint(0,settings['hnodes']-1)
                wih_new[index_row] = wih_new[index_row] * uniform(0.9, 1.1)
                if wih_new[index_row] >  1: wih_new[index_row] = 1
                if wih_new[index_row] < -1: wih_new[index_row] = -1
            # MUTATE: WHO WEIGHTS
            if mat_pick == 1:
                index_row = randint(0,settings['onodes']-1)
                index_col = randint(0,settings['hnodes']-1)
                who_new[index_row][index_col] = who_new[index_row][index_col] * uniform(0.9, 1.1)
                if who_new[index_row][index_col] >  1: who_new[index_row][index_col] = 1
                if who_new[index_row][index_col] < -1: who_new[index_row][index_col] = -1
        organisms_new.append(organism(settings, wih=wih_new, who=who_new, name='gen['+str(gen)+']-org['+str(w)+']'))
    return organisms_new, stats

四、模拟


最后是模拟实际运行的关键代码,模拟函数将在每一代调用一次。模拟时间步骤则是通过将总模拟时间除以时间间隔dt来确定的。例如,如果模拟时间设置为100秒,而dt等于1/25秒,那么总共需要模拟2500个时间步骤。在每一个步骤中,将进行以下操作:

1、碰撞检测:检查生物和食物颗粒之间的碰撞。当检测到碰撞时,该生物体将得到更新,食物颗粒将在一个新的随机位置上重生。

2、寻找最近的食物颗粒:对于每一个生物体,必须确定最近的食物颗粒。

3、确定最近的食物颗粒的方向:一旦最近的食物颗粒被一个生物体确定,就必须计算出这个颗粒的方向。

4、查询神经网络:由于使用了更新的方向值,因此每个生物的神经网络都将变得不同。

5、更新生物体:基于神经网络的响应,生物体的速度和位置都得到了更新。

下面是模拟函数:


def simulate(settings, organisms, foods, gen):
    total_time_steps = int(settings['gen_time'] / settings['dt'])
    #--- CYCLE THROUGH EACH TIME STEP ---------------------+
    for t_step in range(0, total_time_steps, 1):
        # PLOT SIMULATION FRAME
        #if gen == settings['gens'] - 1 and settings['plot']==True:
        if gen==49:
            plot_frame(settings, organisms, foods, gen, t_step)
        # UPDATE FITNESS FUNCTION
        for food in foods:
            for org in organisms:
                food_org_dist = dist(org.x, org.y, food.x, food.y)
                # UPDATE FITNESS FUNCTION
                if food_org_dist <= 0.075:
                    org.fitness += food.energy
                    food.respawn(settings)
                # RESET DISTANCE AND HEADING TO NEAREST FOOD SOURCE
                org.d_food = 100
                org.r_food = 0
        # CALCULATE HEADING TO NEAREST FOOD SOURCE
        for food in foods:
            for org in organisms:
                # CALCULATE DISTANCE TO SELECTED FOOD PARTICLE
                food_org_dist = dist(org.x, org.y, food.x, food.y)
                # DETERMINE IF THIS IS THE CLOSEST FOOD PARTICLE
                if food_org_dist < org.d_food:
                    org.d_food = food_org_dist
                    org.r_food = calc_heading(org, food)
        # GET ORGANISM RESPONSE
        for org in organisms:
            org.think()
        # UPDATE ORGANISMS POSITION AND VELOCITY
        for org in organisms:
            org.update_r(settings)
            org.update_vel(settings)
            org.update_pos(settings)
return organisms


五、结果


在创建所有的主要组件之后,最终代码就可以组装起来了。注意:在上面的文章中,忽略了一些更小的函数,因为它们太简单了,阅读它们要比直接阅读代码花费更长的时间。所有的代码我都放在GitHub上了,你可以点击这里下载

我使用了matplotlib来显示模拟。当你运行整个代码时,输出应该与此类似:

 

4717630f6ec9b5adb12cedb5b4774b1749ae22bb


以上为译文

文章原标题《

Evolving Simple Organisms using a Genetic Algorithm and Deep Learning from Scratch with Python

》,作者:Nathan,译者:黄小凡,审校:袁虎。

文章为简译,更为详细的内容,请查看

 

相关文章
|
1月前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于GA遗传优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真
该算法结合了遗传算法(GA)与分组卷积神经网络(GroupCNN),利用GA优化GroupCNN的网络结构和超参数,提升时间序列预测精度与效率。遗传算法通过模拟自然选择过程中的选择、交叉和变异操作寻找最优解;分组卷积则有效减少了计算成本和参数数量。本项目使用MATLAB2022A实现,并提供完整代码及视频教程。注意:展示图含水印,完整程序运行无水印。
|
16天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于GA-PSO-SVM算法的混沌背景下微弱信号检测matlab仿真
本项目基于MATLAB 2022a,展示了SVM、PSO、GA-PSO-SVM在混沌背景下微弱信号检测中的性能对比。核心程序包含详细中文注释和操作步骤视频。GA-PSO-SVM算法通过遗传算法和粒子群优化算法优化SVM参数,提高信号检测的准确性和鲁棒性,尤其适用于低信噪比环境。
|
1月前
|
算法 决策智能
基于GA-PSO遗传粒子群混合优化算法的TSP问题求解matlab仿真
本文介绍了基于GA-PSO遗传粒子群混合优化算法解决旅行商问题(TSP)的方法。TSP旨在寻找访问一系列城市并返回起点的最短路径,属于NP难问题。文中详细阐述了遗传算法(GA)和粒子群优化算法(PSO)的基本原理及其在TSP中的应用,展示了如何通过编码、选择、交叉、变异及速度和位置更新等操作优化路径。算法在MATLAB2022a上实现,实验结果表明该方法能有效提高求解效率和解的质量。
|
3月前
|
算法
基于GA-PSO遗传粒子群混合优化算法的CVRP问题求解matlab仿真
本文介绍了一种基于GA-PSO混合优化算法求解带容量限制的车辆路径问题(CVRP)的方法。在MATLAB2022a环境下运行,通过遗传算法的全局搜索与粒子群算法的局部优化能力互补,高效寻找最优解。程序采用自然数编码策略,通过选择、交叉、变异操作及粒子速度和位置更新,不断迭代直至满足终止条件,旨在最小化总行驶距离的同时满足客户需求和车辆载重限制。
|
4月前
|
传感器 机器学习/深度学习 算法
基于GA遗传算法的WSN网络节点覆盖优化matlab仿真
本研究应用遗传优化算法于无线传感器网络(WSN),优化节点布局与数量,以最小化节点使用而最大化网络覆盖率。MATLAB2022a环境下,算法通过选择、交叉与变异操作,逐步改进节点配置,最终输出收敛曲线展现覆盖率、节点数及适应度值变化。无线传感器网络覆盖优化问题通过数学建模,结合遗传算法,实现目标区域有效覆盖与网络寿命延长。算法设计中,采用二进制编码表示节点状态,适应度函数考量覆盖率与连通性,通过选择、交叉和变异策略迭代优化,直至满足终止条件。
|
4月前
|
算法 数据安全/隐私保护
基于GA遗传优化算法的Okumura-Hata信道参数估计算法matlab仿真
在MATLAB 2022a中应用遗传算法进行无线通信优化,无水印仿真展示了算法性能。遗传算法源于Holland的理论,用于全局优化,常见于参数估计,如Okumura-Hata模型的传播损耗参数。该模型适用于150 MHz至1500 MHz的频段。算法流程包括选择、交叉、变异等步骤。MATLAB代码执行迭代,计算目标值,更新种群,并计算均方根误差(RMSE)以评估拟合质量。最终结果比较了优化前后的RMSE并显示了SNR估计值。
68 7
|
5月前
|
算法
基于GA遗传优化的混合发电系统优化配置算法matlab仿真
**摘要:** 该研究利用遗传算法(GA)对混合发电系统进行优化配置,旨在最小化风能、太阳能及电池储能的成本并提升系统性能。MATLAB 2022a用于实现这一算法。仿真结果展示了一系列图表,包括总成本随代数变化、最佳适应度随代数变化,以及不同数据的分布情况,如负荷、风速、太阳辐射、弃电、缺电和电池状态等。此外,代码示例展示了如何运用GA求解,并绘制了发电单元的功率输出和年变化。该系统原理基于GA的自然选择和遗传原理,通过染色体编码、初始种群生成、适应度函数、选择、交叉和变异操作来寻找最优容量配置,以平衡成本、效率和可靠性。
|
4月前
|
机器学习/深度学习 数据采集 算法
Python实现GA(遗传算法)对SVM分类模型参数的优化
Python实现GA(遗传算法)对SVM分类模型参数的优化
209 0
|
5月前
|
算法
m基于GA遗传优化的高斯白噪声信道SNR估计算法matlab仿真
**MATLAB2022a模拟展示了遗传算法在AWGN信道中估计SNR的效能。该算法利用生物进化原理全局寻优,解决通信系统中复杂环境下的SNR估计问题。核心代码执行多代选择、重组和突变操作,逐步优化SNR估计。结果以图形形式对比了真实SNR与估计值,并显示了均方根误差(RMSE),体现了算法的准确性。**
59 0
|
6月前
|
机器学习/深度学习 算法
【MATLAB】GA_BP神经网络时序预测算法
【MATLAB】GA_BP神经网络时序预测算法
144 8