进化策略算法

news2024/11/26 22:43:01

前言

进化策略 (Evolution Strategy) 后面都简称 ES,其本质就是:种群通过交叉产生后代,我们只保留较好的父代和子代,一直这样迭代下去,
我们的保留方式是:

父代产生后代,然后将后代DNA和原来的父母DNA合并,然后根据适应度排序,
然后选取前POP_SIZE的个体,重新组成一个种群

相关文章:
微生物进化算法(MGA)
遗传算法
前两个文章都是以长度大于1的列表当作DNA序列,
本次我们用实数来代替,即DNA就是一个实数,长度为1

遗传算法(GA)和进化策略(ES)的区别:

  • 选好父母进行繁殖 (GA); 先繁殖, 选好的孩子 (ES)
  • 通常用二进制编码 DNA (GA); 通常 DNA 就是实数, 比如 1.221 (ES)
  • 通过随机让 1 变成 0 这样变异 DNA (GA); 通过正态分布(Normal distribution)变异 DNA (ES)

传统的 GA 的 DNA 形式是这样:

DNA=11010010

而传统的 ES DNA 形式分两种, 它有两条 DNA. 一个 DNA 是控制数值的, 第二个 DNA 是控制这个数值的变异强度. 比如一个问题有4个变量. 那一个 DNA 中就有4个位置存放这4个变量的值 第二个 DNA 中就存放4个变量的变动幅度值.

DNA1=1.23, -0.13, 2.35, 112.5 可以理解为4个正态分布的4个平均值.

DNA2=0.1, 2.44, 5.112, 2.144 可以理解为4个正态分布的4个标准差.

所以这两条 DNA 都需要被 crossover(交叉) 和 mutate(变异).

示例

本次以寻找曲线最大值为例
在这里插入图片描述

首先我们需要通过种群产生后代

首先的 make_kid 功能. 我们随机找到一对父母, 然后将父母的 DNA 和 mut_strength 基因都 crossover 给 kid. 然后再根据 mut_strength mutate 一下 kid 的 DNA. 也就是用正态分布抽一个 DNA sample. 而且 mut_strength 也能变异. 将变异强度变异以后, 他就能在快收敛的时候很自觉的逐渐减小变异强度, 方便收敛.

def make_kid(pop, n_kid):
    """
    生成孩子
    :param pop:
    :param n_kid:
    :return:
    """
    # DNA
    kids = {'DNA': np.empty((n_kid, DNA_SIZE))}
    # 变异强度
    kids['mut_strength'] = np.empty_like(kids['DNA'])
    for kv, ks in zip(kids['DNA'], kids['mut_strength']):
        # 随机从种群选取两个个体,一个父亲DNA,一个母亲DNA
        p1, p2 = np.random.choice(np.arange(POP_SIZE), size=2, replace=False)
        # 生成要变异的结点
        cp = np.random.randint(0, 2, DNA_SIZE, dtype=np.bool_)  # crossover points
        # 将父DNA和母DNA交叉生成子DNA
        kv[cp] = pop['DNA'][p1, cp]
        kv[~cp] = pop['DNA'][p2, ~cp]
        # 将父变异强度序列和母变异强度序列交叉生成子变异强度序列
        ks[cp] = pop['mut_strength'][p1, cp]
        ks[~cp] = pop['mut_strength'][p2, ~cp]


        # 将变异强度也产生一定的变异
        ks[:] = np.maximum(ks + (np.random.rand(*ks.shape)-0.5), 0.)    # 实现将变异强度限制在非负数范围内
        # 根据正态分布变异 mutate (change DNA based on normal distribution)
        kv += ks * np.random.randn(*kv.shape)  # 用于生成符合标准正态分布(均值为0,方差为1)的随机数,形状和kv的一样
        # 将数组中的元素限制在一个指定的范围内 它将数组中小于指定最小值的元素替换为最小值,将大于指定最大值的元素替换为最大值
        kv[:] = np.clip(kv, *DNA_BOUND)    # clip the mutated value
    return kids

之后我们将产生的后代和原来的种群合并,然后筛选重新组成一个种群

def kill_bad(pop, kids):
    """
    将孩子DNA和原来的父母DNA合并,然后根据适应度排序,
    然后选取前POP_SIZE的个体,重新组成一个种群
    :param pop:
    :param kids:
    :return:
    """
    # put pop and kids together
    #将孩子DNA和原来的父母DNA合并
    for key in ['DNA', 'mut_strength']:
        pop[key] = np.vstack((pop[key], kids[key]))

    # 获取适应度
    fitness = get_fitness(F(pop['DNA']))            # calculate global fitness
    # 然后根据适应度排序,然后选取前POP_SIZE的个体,重新组成一个种群
    idx = np.arange(pop['DNA'].shape[0])
    good_idx = idx[fitness.argsort()][-POP_SIZE:]   # selected by fitness ranking (not value)
    for key in ['DNA', 'mut_strength']:
        pop[key] = pop[key][good_idx]
    return pop

完整代码如下:

#!/usr/bin/env python 
# -*- coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt

DNA_SIZE = 1             # DNA (real number)  DNA大小
DNA_BOUND = [0, 5]       # solution upper and lower bounds
N_GENERATIONS = 200
POP_SIZE = 100           # population size
N_KID = 50               # n kids per generation

def F(x): return np.sin(10*x)*x + np.cos(2*x)*x     # to find the maximum of this function


def get_fitness(pred):
    """
    返回适应度
    :param pred:
    :return:
    """
    return pred.flatten()  #将多维数组压平为一维数组。


def make_kid(pop, n_kid):
    """
    生成孩子
    :param pop:
    :param n_kid:
    :return:
    """
    # DNA
    kids = {'DNA': np.empty((n_kid, DNA_SIZE))}
    # 变异强度
    kids['mut_strength'] = np.empty_like(kids['DNA'])
    for kv, ks in zip(kids['DNA'], kids['mut_strength']):
        # 随机从种群选取两个个体,一个父亲DNA,一个母亲DNA
        p1, p2 = np.random.choice(np.arange(POP_SIZE), size=2, replace=False)
        # 生成要变异的结点
        cp = np.random.randint(0, 2, DNA_SIZE, dtype=np.bool_)  # crossover points
        # 将父DNA和母DNA交叉生成子DNA
        kv[cp] = pop['DNA'][p1, cp]
        kv[~cp] = pop['DNA'][p2, ~cp]
        # 将父变异强度序列和母变异强度序列交叉生成子变异强度序列
        ks[cp] = pop['mut_strength'][p1, cp]
        ks[~cp] = pop['mut_strength'][p2, ~cp]


        # 将变异强度也产生一定的变异
        ks[:] = np.maximum(ks + (np.random.rand(*ks.shape)-0.5), 0.)    # 实现将变异强度限制在非负数范围内
        # 根据正态分布变异 mutate (change DNA based on normal distribution)
        kv += ks * np.random.randn(*kv.shape)  # 用于生成符合标准正态分布(均值为0,方差为1)的随机数,形状和kv的一样
        # 将数组中的元素限制在一个指定的范围内 它将数组中小于指定最小值的元素替换为最小值,将大于指定最大值的元素替换为最大值
        kv[:] = np.clip(kv, *DNA_BOUND)    # clip the mutated value
    return kids


def kill_bad(pop, kids):
    """
    将孩子DNA和原来的父母DNA合并,然后根据适应度排序,
    然后选取前POP_SIZE的个体,重新组成一个种群
    :param pop:
    :param kids:
    :return:
    """
    # put pop and kids together
    #将孩子DNA和原来的父母DNA合并
    for key in ['DNA', 'mut_strength']:
        pop[key] = np.vstack((pop[key], kids[key]))

    # 获取适应度
    fitness = get_fitness(F(pop['DNA']))            # calculate global fitness
    # 然后根据适应度排序,然后选取前POP_SIZE的个体,重新组成一个种群
    idx = np.arange(pop['DNA'].shape[0])
    good_idx = idx[fitness.argsort()][-POP_SIZE:]   # selected by fitness ranking (not value)
    for key in ['DNA', 'mut_strength']:
        pop[key] = pop[key][good_idx]
    return pop



if __name__ == '__main__':
    # 随机生成种群
    # 这里直接用0-5的数字表示DNA,DNA长度为1
    pop = dict(DNA=5 * np.random.rand(1, DNA_SIZE).repeat(POP_SIZE, axis=0),  # initialize the pop DNA values
               mut_strength=np.random.rand(POP_SIZE, DNA_SIZE))  # initialize the pop mutation strength values

    plt.ion()  # something about plotting
    x = np.linspace(*DNA_BOUND, 200)
    plt.plot(x, F(x))

    for _ in range(N_GENERATIONS):
        # something about plotting
        if 'sca' in globals(): sca.remove()
        sca = plt.scatter(pop['DNA'], F(pop['DNA']), s=200, lw=0, c='red', alpha=0.5);
        plt.pause(0.05)

        # 进化策略开始
        kids = make_kid(pop, N_KID)
        pop = kill_bad(pop, kids)  # keep some good parent for elitism

    plt.ioff()
    plt.show()

参考:
莫烦Python

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1097103.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++入门-引用

C入门-引用 前置知识点:函数栈帧的复用前置知识点:类型转换时产生的临时变量1.含义2.代码形式3.引用的价值1.传参数传参效率测试补充:C与Java中引用的区别 2.引用做返回值(前置知识:栈帧复用)1.传值返回2.传引用返回传引用返回并用引用接收3.静态变量传引用返回4.引用做返回值真…

Redis数据结构的奇妙世界:一窥底层存储机制【redis第一部分】

Redis数据结构的奇妙世界:一窥底层存储机制【redis第一部分】 前言第一:为什么要使用redis第二:redis的底层数据结构第三:Redis的基本数据类型1. 字符串(String)2. 列表(List)3. 集合…

Ansible的playbook编写和运行示例介绍

目录 一.yaml语法格式 1.定义: 2.yaml支持几种数据类型 (1)纯量: (2)对象 (3)数组 3.playbook-yaml书写的注意事项 二.playbook编写和运行 1.单个简单playbook示例 &#…

2023_Spark_实验二十:SparkStreaming累加计算单词频率

一、需求分析 在服务器端不断产生数据的时候,sparkstreaming客户端需要不断统计服务器端产生的相同数据出现的总数,即累计服务器端产生的相同数据的出现的次数。 二、实验环境 centos7 nc spark2.1.1 windows idea 三、思路分析 流程分析 思路分析…

BUUCTF学习(7): 随便注,固网杯

1、介绍 2、解题 11;show tables;# select * from 1919810931114514 concat(sel,ect from 1919810931114514 ) PEREPARE y from sql; ECCUTE y; -1; sEt sql CONCAt(se,lect * from 1919810931114514;); prePare stmt from sql; EXECUTE stmt; # 结束

代码随想录算法训练营第二十三天丨 回溯算法part01

回溯算法理论基础 #题目分类 #理论 #什么是回溯法 回溯法也可以叫做回溯搜索法,它是一种搜索的方式。 在二叉树系列中,不止一次提到了回溯,例如二叉树:以为使用了递归,其实还隐藏着回溯 (opens new window)。 回溯…

5款令人骄傲的国产优质软件,能让你的电脑办公更加高效

很多人都喜欢用国外软件,其实国内也有不少优秀软件。这些国产软件不输国外软件,能够提高我们的办公效率,帮助我们更好地处理日常事务。今天就给大家分享5款令人骄傲的国产优质软件,它们能让你的电脑办公更加高效。 Listary——文件…

机器学习 - 混淆矩阵:技术与实战全方位解析

目录 一、引言1.1 什么是混淆矩阵?1.2 为什么需要混淆矩阵? 二、基础概念TP, TN, FP, FN解释True Positive (TP)True Negative (TN)False Positive (FP)False Negative (FN) 常见评价指标 三、数学原理条件概率与贝叶斯定理ROC与AUC敏感性与特异性阈值选…

探寻JWT的本质:它是什么?它有什么作用?

JWT(JSON Web Token)是一种基于 JSON 格式的轻量级令牌(token)协议,它被广泛应用于网络应用程序的身份验证和授权。相较于传统的 session-based 认证机制,JWT 具有更好的扩展性和互操作性,同时也…

KdMapper扩展实现之AVG(aswArPot.sys)

1.背景 KdMapper是一个利用intel的驱动漏洞可以无痕的加载未经签名的驱动,本文是利用其它漏洞(参考《【转载】利用签名驱动漏洞加载未签名驱动》)做相应的修改以实现类似功能。需要大家对KdMapper的代码有一定了解。 2.驱动信息 驱动名称aswA…

再玩玩B端搭建

一、背景 在 B 端领域深耕多年,接触了成百上千的 B 端页面,发现对于 B 端产品需求和 C 端有着明显的差异,B端产品一般是基于现有的“业务”形态,将传统线下工作,通过程序化、系统化、信息化转换为线上产品&#xff0c…

网络安全工程师的入门学习的路径

网络安全工程师的入门学习的路径 最近看到网上有很多人在问诸如:“怎样成为网络信息安全工程师”等相关问题,这可能与近几年网络安全事件频发,国家对于互联网信息安全和互联网舆情的重视程度不断提升有关,网络信息安全工程师随之…

【Linux学习笔记】调试工具gdb

1. gdb2. debug和release的认识3. gdb命令 1. gdb gdb是Linux下的一个调试工具,主要内容是利用命令行来调试代码,下面我将以vs2019的调试操作逐一对应到gdb的调试命令。 首先我是在xshell连接远端云服务器搭配Linux环境来使用gdb的,第一步要…

vue 和 后端交互

1.前端的路径请求是(请求参数:key和value): this.$http.delete("http://localhost:8080/user/delete?id"id).then(res>{ 后端是接收前端参数 DeleteMapping("/delete")public String delete(Integer id)…

4.1 网络层提供的两种服务

思维导图: ## 第4章 网络层 ### 概述 网络层主要关注网络互连问题,其中重点是网际协议(IP)。掌握了IP协议的内容,我们就能理解互联网的工作机制。本章还涉及了ICMP、路由选择协议、IPv6特点、IP多播概念,以及VPN、NAT和MPLS。 #…

正点原子嵌入式linux驱动开发——字符设备驱动开发

经过之前这么多篇笔记的学习,Ubuntu操作系统以及完整的Linux系统移植,已经初步掌握了开发板系统搭建的过程,在STM32MP157上搭建了自己的简单开发系统,从这一篇笔记开始就可以证实Linux驱动开发的学习了!之后的正点原子…

VBA之正则表达式(43)-- 从网页中提取指定数据

实例需求:由网页中提取下图中颜色标记部分内容,网页中其他部分与此三行格式相同。 方法1 Sub Demo()Dim objRegex As ObjectDim inputString As StringDim objMatches As ObjectDim objMatch As ObjectSet objRegex CreateObject("VBScript.RegEx…

Drecom 的《Eternal Crypt - Wizardry BC -》加入 The Sandbox 啦!

经典 “Wizardry” 游戏系列的新区块链迭代将通过全球合作拓展 Web3 游戏宇宙。 我们非常高兴地宣布,沙盒游戏公司与富有远见的传奇游戏《Wizardry》系列创造者 Drecom 将建立充满活力的合作伙伴关系。我们将共同推出《Eternal Crypt - Wizardry BC -》&#xff0c…

260. 只出现一次的数字 III (中等,位运算)

还是不会做,思路来自官解 对于整个数组按异或求和,可以得到只出现一次的两个数的异或值,通过这个值我们可以知道这两个数哪一位是相同的,哪一位是不同的假设这两个数字最低的不同发生在第 l 位(因为两个数字不同&…

网络安全 - 一名合格的Web安全工程师之成长路径

最近经常听到公司的招聘专员反馈应聘者简历“水分”太大,尤其是技术岗位,例如Web安全工程师,明明是初级阶段的菜鸟,就敢写资深Web安全工程师;在几个项目做一些基础打杂的工作,就敢写带过团队,项…