文章目录

  • 1 什么是进化策略 (Evolution Strategy)
  • 2 简单实现
    • 2.1 和遗传算法的异同
    • 2.2 代码
  • 3 (1+1)-ES
  • 4 Natural Evolution Strategy

1 什么是进化策略 (Evolution Strategy)

爸妈的 DNA 不用再是 01 的这种形式, 我们可以用实数来代替, 咋一看, 觉得牛逼了起来, 因为我们抛开了二进制的转换问题, 从而能解决实际生活中的很多由实数组成的真实问题. 比如我有一个关于 x 的公式, 而这个公式中其他参数, 我都能用 DNA 中的实数代替, 然后进化我的 DNA, 也就是优化这个公式啦. 这样用起来, 的确比遗传算法方便. 同样, 在制造宝宝的时候, 我们也能用到交叉配对, 一部分来自爸爸, 一部分来自妈妈. 可是我们应该如何变异呢? 遗传算法中简单的翻牌做法貌似在这里行不通. 不过进化策略中的另外一个因素起了决定性的作用. 这就是变异强度. 简单来说, 我们将爸妈遗传下来的值看做是正态分布的平均值, 再在这个平均值上附加一个标准差, 我们就能用正态分布产生一个相近的数了. 比如在这个8.8位置上的变异强度为1, 我们就按照1的标准差和8.8的均值产生一个离8.8的比较近的数, 比如8.7. 然后对宝宝每一位上的值进行同样的操作. 就能产生稍微不同的宝宝 DNA 啦. 所以, 变异强度也可以被当成一组遗传信息从爸妈的 DNA 里遗传下来. 甚至遗传给宝宝的变异强度基因也能变异. 进化策略的玩法也能多种多样.

我们总结一下, 在进化策略中, 可以有两种遗传性系被继承给后代, 一种是记录所有位置的均值, 一种是记录这个均值的变异强度, 有了这套体系, 我们就能更加轻松自在的在实数区间上进行变异了. 这种思路甚至还能被用在神经网络的参数优化上, 因为这些参数本来就是一些实数. 在之后的视频中我们会继续提到当今比较流行的将人工神经网络结合上遗传算法或者进化策略的方法.

2 简单实现

2.1 和遗传算法的异同

遗传算法 (后面简称 GA) 和 ES 真 TM 差不多. 导致很多朋友学习的时候, 都傻傻分不清. 不过我具体的列出来, 方便看清楚.

选好父母进行繁殖 (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.

2.2 代码

import numpy as np
import matplotlib.pyplot as pltDNA_SIZE = 1             # DNA (real number)
DNA_BOUND = [0, 5]       # solution upper and lower bounds
N_GENERATIONS = 200
POP_SIZE = 100           # population size
N_KID = 50               # n kids per generationdef F(x): return np.sin(10*x)*x + np.cos(2*x)*x     # to find the maximum of this function# find non-zero fitness for selection
def get_fitness(pred): return pred.flatten()def make_kid(pop, n_kid):# generate empty kid holderkids = {'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']):# crossover (roughly half p1 and half p2)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 pointskv[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]# mutate (change DNA based on normal distribution)ks[:] = np.maximum(ks + (np.random.rand(*ks.shape)-0.5), 0.)    # must > 0kv += ks * np.random.randn(*kv.shape)kv[:] = np.clip(kv, *DNA_BOUND)    # clip the mutated valuereturn kidsdef kill_bad(pop, kids):# put pop and kids togetherfor key in ['DNA', 'mut_strength']:pop[key] = np.vstack((pop[key], kids[key]))fitness = get_fitness(F(pop['DNA']))            # calculate global fitnessidx = 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 poppop = dict(DNA=5 * np.random.rand(1, DNA_SIZE).repeat(POP_SIZE, axis=0),   # initialize the pop DNA valuesmut_strength=np.random.rand(POP_SIZE, DNA_SIZE))                # initialize the pop mutation strength valuesplt.ion()       # something about plotting
x = np.linspace(*DNA_BOUND, 200)
plt.plot(x, F(x))for _ in range(N_GENERATIONS):# something about plottingif '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)# ES partkids = make_kid(pop, N_KID)pop = kill_bad(pop, kids)   # keep some good parent for elitismplt.ioff(); plt.show()

3 (1+1)-ES

(1+1)-ES 是 ES 进化策略的一种形式, 也是众多形式中比较方便有效的一种. 接下来我们来细说他们的类别. 如果要我用一句话来概括 (1+1)-ES: 一个爸爸和一个孩子的战争
像上面看到的, 统一来说都是 (μ/ρ +, λ)-ES, (1+1)-ES 只是一种特殊形式. 这里的 μ 是 population 的数量, ρ 是从 population 中选取的个数, 用来生成宝宝的. λ 是生成的宝宝数, 如果采用的是 “+” 形式的, 就是使用将 ρ + λ 混合起来进行适者生存, 如果是 “,” 形式, 那么就是只使用 λ 进行适者生存.

形式多种多样有些头疼. 不过在这一节中, 我们考虑的只是一个爸爸, 生成一个宝宝, 然后在爸爸和宝宝中进行适者生存的游戏, 选择爸爸和宝宝中比较好的那个当做下一代的爸爸. (1+1)-ES 总结如下:

有一个爸爸;
根据爸爸变异出一个宝宝;
在爸爸和宝宝中选好的那个变成下一代爸爸.
Rechenberg, I. 1973. Evolutionsstrategie – Optimierung technischer Systeme nach Prinzipien der biologischen Evolution, Frommann-Holzboog.

网上有个课件”Tutorial: CMA-ES — Evolution Strategies and Covariance Matrix Adaptation”里面一张图, 让你秒懂这个1/5的意思.


图中的意思是, 还没到收敛的时候(上面左图), 我们增大 MUT_STRENGTH, 如果已经快到收敛了(上右图), 我们就减小 MUT_STRENGTH. 那如何判断是否快到收敛没呢, 就是如果有1/5的变异比原始的 parent 好的话, 就是快收敛了(像上右图). 在上左图中, 有一半比原始 parent 好, 一半比较差, 所以还没到收敛. 在上面提到的课件中, 用一个公式就能概括这种1/5关系.

import numpy as np
import matplotlib.pyplot as pltDNA_SIZE = 1             # DNA (real number)
DNA_BOUND = [0, 5]       # solution upper and lower bounds
N_GENERATIONS = 200
MUT_STRENGTH = 5.        # initial step size (dynamic mutation strength)def F(x): return np.sin(10*x)*x + np.cos(2*x)*x     # to find the maximum of this function# find non-zero fitness for selection
def get_fitness(pred): return pred.flatten()def make_kid(parent):# no crossover, only mutationk = parent + MUT_STRENGTH * np.random.randn(DNA_SIZE)k = np.clip(k, *DNA_BOUND)return kdef kill_bad(parent, kid):global MUT_STRENGTHfp = get_fitness(F(parent))[0]fk = get_fitness(F(kid))[0]p_target = 1/5if fp < fk:     # kid better than parentparent = kidps = 1.     # kid win -> ps = 1 (successful offspring)else:ps = 0.# adjust global mutation strengthMUT_STRENGTH *= np.exp(1/np.sqrt(DNA_SIZE+1) * (ps - p_target)/(1 - p_target))return parentparent = 5 * np.random.rand(DNA_SIZE)   # parent DNAplt.ion()       # something about plotting
x = np.linspace(*DNA_BOUND, 200)for _ in range(N_GENERATIONS):# ES partkid = make_kid(parent)py, ky = F(parent), F(kid)       # for later plotparent = kill_bad(parent, kid)# something about plottingplt.cla()plt.scatter(parent, py, s=200, lw=0, c='red', alpha=0.5,)plt.scatter(kid, ky, s=200, lw=0, c='blue', alpha=0.5)plt.text(0, -7, 'Mutation strength=%.2f' % MUT_STRENGTH)plt.plot(x, F(x)); plt.pause(0.05)plt.ioff(); plt.show()

4 Natural Evolution Strategy

Natural ES 后面简称 NES, 应该就是算一种用适应度诱导的梯度下降法, 如果要我用一句话来概括 NES: 生宝宝, 用好宝宝的梯度辅助找到前进的方向
梯度的本意是一个向量(矢量),表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为该梯度的模)。
生孩子,好孩子加大梯度,坏孩子减小梯度。

import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.contrib.distributions import MultivariateNormalFullCovarianceDNA_SIZE = 2         # parameter (solution) number
N_POP = 20           # population size
N_GENERATION = 100   # training step
LR = 0.02            # learning rate# fitness function
def get_fitness(pred): return -((pred[:, 0])**2 + pred[:, 1]**2)# build multivariate distribution
mean = tf.Variable(tf.random_normal([2, ], 13., 1.), dtype=tf.float32)
cov = tf.Variable(5. * tf.eye(DNA_SIZE), dtype=tf.float32)
mvn = MultivariateNormalFullCovariance(loc=mean, covariance_matrix=cov)
make_kid = mvn.sample(N_POP)                                    # sampling operation# compute gradient and update mean and covariance matrix from sample and fitness
tfkids_fit = tf.placeholder(tf.float32, [N_POP, ])
tfkids = tf.placeholder(tf.float32, [N_POP, DNA_SIZE])
loss = -tf.reduce_mean(mvn.log_prob(tfkids)*tfkids_fit)         # log prob * fitness
train_op = tf.train.GradientDescentOptimizer(LR).minimize(loss) # compute and apply gradients for mean and covsess = tf.Session()
sess.run(tf.global_variables_initializer())                     # initialize tf variables# something about plotting (can be ignored)
n = 300
x = np.linspace(-20, 20, n)
X, Y = np.meshgrid(x, x)
Z = np.zeros_like(X)
for i in range(n):for j in range(n):Z[i, j] = get_fitness(np.array([[x[i], x[j]]]))
plt.contourf(X, Y, -Z, 100, cmap=plt.cm.rainbow); plt.ylim(-20, 20); plt.xlim(-20, 20); plt.ion()# training
for g in range(N_GENERATION):kids = sess.run(make_kid)kids_fit = get_fitness(kids)sess.run(train_op, {tfkids_fit: kids_fit, tfkids: kids})    # update distribution parameters# plotting updateif 'sca' in globals(): sca.remove()sca = plt.scatter(kids[:, 0], kids[:, 1], s=30, c='k');plt.pause(0.01)print('Finished'); plt.ioff(); plt.show()

宝宝们的梯度是这个 gradient 加上这些宝宝们的 fitness fitness, 用梯度乘以 fitness 就是说, 加大力度往带来好 fitness 的梯度下降. 所以之后的宝宝们就会越来越多的下降到最优点啦. 那么我们要梯度下降的参数则是那些正态分布的均值和均方差.

提到梯度下降, 哈哈, 那么那些 scipy, Tensorflow 都可以考虑用一用. 这个教程中将会使用到 Tensorflow 来完成这种梯度下降的做法.
顾名思义,梯度下降法的计算过程就是沿梯度下降的方向求解极小值(也可以沿梯度上升方向求解极大值)。

【深度学习入门到精通系列】进化策略 (Evolution Strategy)相关推荐

  1. 【深度学习入门到精通系列】神经进化 (NeuroEvolution)

    文章目录 1 什么是神经网络进化 (Neuro-Evolution) 2 神经进化 3 NEAT 监督学习 4 NEAT 强化学习 5 Evolution Strategy 强化学习 1 什么是神经网 ...

  2. 【深度学习入门到精通系列】阿里云人工智能平台的使用方法

    文章目录 1 概述 2 获取密钥方法 3 Python SDK 4 demo 1 概述 接口易用 标准化接口封装,大大降低研发人力投入 稳定可靠 服务可靠性99.99%:全球多机房部署,服务全球化:单 ...

  3. 【深度学习入门到精通系列】对抗样本和对抗网络

    文章目录 1 概述 2 对抗样本 3 对抗网络 1 概述 所谓对抗 样本是指将实际样本略加扰动而构造出的合成样本,对该样本,分类器非常容易将其类别判错,这意味着光滑性假设(相似的样本应该以很高的概率被 ...

  4. 【深度学习入门到精通系列】Deep Q Network

    文章目录 1 什么是 DQN 2 DQN 算法更新 (Tensorflow) 3 DQN 神经网络 (Tensorflow) 4 DQN 思维决策 (Tensorflow) 1 什么是 DQN 我们使 ...

  5. 【深度学习入门到精通系列】遗传算法 (Genetic Algorithm)

    文章目录 1 遗传算法概述 2 遗传算法 2.1 找一个好的fitness方程 2.2 DNA 编码 2.3 代码实现 3 配对句子 4 旅行商问题 5 Microbial Genetic Algor ...

  6. 【深度学习入门到精通系列】 深入浅出强化学习 Sarsa

    文章目录 1 什么是 Sarsa 2 Sarsa 算法更新 3 Sarsa 思维决策 4 什么是 Sarsa(lambda) 5 Sarsa-lambda 1 什么是 Sarsa 同样, 我们会经历正 ...

  7. 【深度学习入门到精通系列】开始恢复更新通知~!

    因为放假在家基本不学习哈哈,但是快要开学了,准备开始更新了,我会尽量保持一周3到4篇高质量文章的频率,欢迎大家订阅呀-!! 附上我的微博: ID:LiXiangDL 有问题欢迎打扰!

  8. 【深度学习入门到精通系列】Python批量实现图像镜像翻转

    ''' Python批量实现图像镜像翻转 函数:DataAugment() 函数功能:扩大数据量 输入参数:dir_path----图片库路径 '''import cv2 import os impo ...

  9. 【深度学习入门到精通系列】神经网络中动量的概念

    其中动量系数一般取(0,1),直观上理解就是要是当前梯度方向与前一步的梯度方向一样,那么就增加这一步的权值更新,要是不一样就减少更新.

最新文章

  1. Spring基础专题——第十一章(高级注解编程完结)
  2. Linux free命令详解(转)
  3. docker的安全管理与TLS/LLS加密通信
  4. Koa 还是 Express
  5. CF461D-Appleman and Complicated Task【并查集】
  6. 计算机接口控制采集时序图,自动站实时数据质量控制
  7. python 遍历usb设备_python程序员教你写脚本玩微信跳一跳,只要有耐心,你就是王者!...
  8. python比较时间的最大值_时间戳的最大值
  9. 指向API的函数指针定义方法
  10. Atitit 组织架构的如何划分 划分方法attilax大总结
  11. 工作流_JBPM之Helloword
  12. 联想交换机服务器型号,联想EN1032交换机 ISL vLAG配置
  13. 怎么用软件测试iPad,Apple:如何在iphone、ipad上安装一些常用命令行命令
  14. 微分几何与斯托克定理
  15. TCP Congestion性能测试分析
  16. python中search函数用法_查找匹配函数FIND和SEARCH的基本用法
  17. PowerBI实用技巧:案例五(巧用Switch函数展现指标RGB颜色)
  18. yolo v5 NVIDIA Jetson Xavier NX 部署刷机+安环境(2)
  19. 【资源】OpenCV3编程入门_毛星云
  20. Day8——反转字符串、反转字符串||、替换空格、反转字符串里的单词、左旋字符串

热门文章

  1. yolov3 onnx nms
  2. js中!和!!的区别及用法
  3. python调用c++传递数组
  4. The expression except (OSError, e)
  5. vs怎么设置php文件调试,使用vs code编辑调试php配置的方法
  6. 学生籍贯信息管理系统c语言,学生籍贯信息管理系统(c).doc
  7. java去掉rn,React Native删除第三方开源组件的依赖包
  8. android置组件下面,Android Jetpack架构组件(十二)之Hilt
  9. bash 判断 os 版本_Kali Linux 2020.3开始用ZSH取代Bash旅程
  10. ldd查看可执行程序的依赖库