前言

本文对遗传算法中的几种选择策略进行了总结, 其中包括:

  1. Proportionate Roulette Wheel Selection
  2. Linear Ranking Selection
  3. Exponential Ranking Selection
  4. Tournament Selection

对于每种选择策略我都使用Python进行了相应的实现并以内置插件的形式整合进了本人所写的遗传算法框架GAFT中。对需要使用遗传算法优化问题以及学习遗传算法的童鞋可以作为参考.

项目链接:

遗传算法中的选择算子

遗传算法(genetic algorithms, GAs)是一种自适应的启发式搜索算法, 它模仿达尔文进化论中的“适者生存”的原则, 最终获取优化目标的最优解。下图描述了一个简单的遗传算法流程:

对于种群中需要进行杂交的物种选择方法有很多,而且选择一种合适的选择策略对于遗传算法整体性能的影响将是很大的。如果一个选择算法选择多样性降低,便会导致种群过早的收敛到局部最优点而不是我们想要的全局最优点,也就是所谓的”早熟”。而选择策略过于发散则会导致算法难以收敛到最优点。因此在这两点中我们需要进行平衡才能使遗传算法以一种高效的方式收敛到全局最优点。

GAFT框架中的算子插件

GAFT是我根据自己需求开发的一个遗传算法框架,相关介绍的博客可以参见《GAFT-一个使用Python实现的遗传算法框架》,《使用MPI并行化遗传算法框架GAFT》。该框架提供了插件接口,用户可以通过自定义算子以及on-the-fly分析插件来放到gaft框架中运行遗传算法流程对目标问题进行优化。

本部分我稍微介绍下gaft关于遗传算子相关接口规范,以及编写能用于gaft的算子的编写方法。

在gaft中遗传算子的编写都是需要继承框架内置的基类,然后根据基类提供的接口,实现自己的算子。其中基类的定义都在/gaft/plugin_interfaces/operators/目录下,下面我以选择算子为例,介绍下接口。

gaft中选择算子的基类为GASelection,其中在遗传算法流程中会调用该类实例的select方法,进而根据算子中的相关选择策略完成从种群中选取一对物种作为父亲和母亲产生子代。基类的定义为:

 
 
 
 
 
 

Python

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class GASelection(metaclass=SelectionMeta):
    '''
    Class for providing an interface to easily extend the behavior of selection
    operation.
    '''
    def select(self, population, fitness):
        '''
        Called when we need to select parents from a population to later breeding.
        :param population: The current population.
        :type population: GAPopulation
        :return parents: Two selected individuals for crossover.
        :type parents: Tuple of tow GAIndividual objects.
        '''
        raise NotImplementedError

select的方法的参数为当前种群population以及相应的适应度函数fitness,其中population需要是GAPopulation对象,fitness也必须是callable的对象。

当然,这些在Python这种动态类型语言中貌似看起来有些鸡肋,但是为了能够更加规范使用者,我利用Python的元类在实例化类对象的时候对接口的实现以及接口的参数类型加以限制。具体的实现都在/gaft/plugin_interfaces/metaclasses.py中,有兴趣的童鞋可以看看实现方法。

具体自定义算子的编写方法我将在下一部分同选择策略一起贴出来。

不同的选择策略

本部分我主要对四种不同的选择策略进行总结并加以gaft插件形式的Python实现。

选择算子决定了哪些个体将会从种群中被选择出来用于繁衍下一代种群中的新个体。其主要的原则就是:

the better is an individual; the higher is its chance of being a parent

选择算子在遗传算法迭代中将适应度函数引入进来,因为适应度函数式标定一个个体是否足够“好”的重要标准。但是选择过程又不能仅仅完全依赖于适应度函数,因为一个种群中的最优物种并不一定是在全局最优点附近。因此我们也应该给相对来说并那么“好”的个体一点机会让他们繁衍后代, 避免“早熟”。support by 漳州叉车

Proportionate Roulette Wheel Selection

此轮盘赌选择策略,是最基本的选择策略之一,种群中的个体被选中的概率与个体相应的适应度函数的值成正比。我们需要将种群中所有个体的适应度值进行累加然后归一化,最终通过随机数对随机数落在的区域对应的个体进行选取,类似赌场里面的旋转的轮盘。

每个个体ai被选中的概率为:

好了,下面可以将此算法写成一个可以gaft中执行的算子。

 
 
 
 
 

Python

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from random import random
from bisect import bisect_right
from itertools import accumulate
 
from ...plugin_interfaces.operators.selection import GASelection
 
class RouletteWheelSelection(GASelection):
    def __init__(self):
        '''
        Selection operator with fitness proportionate selection(FPS) or
        so-called roulette-wheel selection implementation.
        '''
        pass
 
    def select(self, population, fitness):
        '''
        Select a pair of parent using FPS algorithm.
        '''
        # Normalize fitness values for all individuals.
        fit = [fitness(indv) for indv in population.individuals]
        min_fit = min(fit)
        fit = [(i - min_fit) for i in fit]
 
        # Create roulette wheel.
        sum_fit = sum(fit)
        wheel = list(accumulate([i/sum_fit for i in fit]))
 
        # Select a father and a mother.
        father_idx = bisect_right(wheel, random())
        father = population[father_idx]
        mother_idx = (father_idx + 1) % len(wheel)
        mother = population[mother_idx]
 
        return father, mother

过程主要分为下面几个:

  1. 继承GASelection
  2. 实现select方法
  3. select的参数为GAPopulation实例和适应度函数
  4. 根据算法选择出两个需要繁衍的物种并返回即可

Tournament Selection

由于算法执行的效率以及易实现的的特点,锦标赛选择算法是遗传算法中最流行的选择策略。在本人的实际应用中的确此策略比基本的轮盘赌效果要好些。他的策略也很直观,就是我们再整个种群中抽取n个个体,让他们进行竞争(锦标赛),抽取其中的最优的个体。参加锦标赛的个体个数成为tournament size。通常当n=2便是最常使用的大小,也称作Binary Tournament Selection.

Tournament Selection的优势:

  1. 更小的复杂度O(n)
  2. 易并行化处理
  3. 不易陷入局部最优点
  4. 不需要对所有的适应度值进行排序处理

下图显示了n=3的Tournament Selection的过程:

可以开始写成自定义算子在gaft运行了:

 
 
 
 
 
 

Python

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from random import sample
 
from ...plugin_interfaces.operators.selection import GASelection
 
class TournamentSelection(GASelection):
    def __init__(self, tournament_size=2):
        '''
        Selection operator using Tournament Strategy with tournament size equals
        to two by default.
        '''
        self.tournament_size = tournament_size
 
    def select(self, population, fitness):
        '''
        Select a pair of parent using Tournament strategy.
        '''
        # Competition function.
        complete = lambda competitors: max(competitors, key=fitness)
 
        # Check validity of tournament size.
        if self.tournament_size >= len(population):
            msg = 'Tournament size({}) is larger than population size({})'
            raise ValueError(msg.format(self.tournament_size, len(population)))
 
        # Pick winners of two groups as parent.
        competitors_1 = sample(population.individuals, self.tournament_size)
        competitors_2 = sample(population.individuals, self.tournament_size)
        father, mother = complete(competitors_1), complete(competitors_2)
 
        return father, mother

Linear Ranking Selection

下面两个介绍的选择策略都是基于排序的选择策略,上面提到的第一种基本轮盘赌选择算法,有一个缺点,就是如果一个个体的适应度值为0的话,则被选中的概率将会是0, 这个个体将不能产生后代。于是我们需要一种基于排序的算法,来给每个个体安排相应的选中概率。

在Linear Ranking Selection中,种群中的个体首先根据适应度的值进行排序,然后给所有个体赋予一个序号,最好的个体为N, 被选中的概率为Pmax, 最差的个体序号为1, 被选中的概率为Pmin,于是其他的在他们中间的个体的概率便可以根据如下公式得到:

实现代码:

 
 
 
 
 
 

Python

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from random import random
from itertools import accumulate
from bisect import bisect_right
 
from ...plugin_interfaces.operators.selection import GASelection
 
class LinearRankingSelection(GASelection):
    def __init__(self, pmin=0.1, pmax=0.9):
        '''
        Selection operator using Linear Ranking selection method.
        Reference: Baker J E. Adaptive selection methods for genetic
        algorithms[C]//Proceedings of an International Conference on Genetic
        Algorithms and their applications. 1985: 101-111.
        '''
        # Selection probabilities for the worst and best individuals.
        self.pmin, self.pmax = pmin, pmax
 
    def select(self, population, fitness):
        '''
        Select a pair of parent individuals using linear ranking method.
        '''
        # Individual number.
        NP = len(population)
        # Add rank to all individuals in population.
        sorted_indvs = sorted(population.individuals, key=fitness, reverse=True)
 
        # Assign selection probabilities linearly.
        # NOTE: Here the rank i belongs to {1, ..., N}
        p = lambda i: (self.pmin + (self.pmax - self.pmin)*(i-1)/(NP-1))
        probabilities = [self.pmin] + [p(i) for i in range(2, NP)] + [self.pmax]
 
        # Normalize probabilities.
        psum = sum(probabilities)
        wheel = list(accumulate([p/psum for p in probabilities]))
 
        # Select parents.
        father_idx = bisect_right(wheel, random())
        father = population[father_idx]
        mother_idx = (father_idx + 1) % len(wheel)
        mother = population[mother_idx]
 
        return father, mother

Exponential Ranking Selection

类似上面的Linear Ranking选择策略,这种指数排序便是在确定每个个体的选择概率的时候使用了指数形式的表达式, 其中c为底数,满足0<c<1:

实现代码:

 
 
 
 
 
 

Python

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from random import random
from itertools import accumulate
from bisect import bisect_right
 
from ...plugin_interfaces.operators.selection import GASelection
 
class ExponentialRankingSelection(GASelection):
    def __init__(self, base=0.5):
        '''
        Selection operator using Exponential Ranking selection method.
        :param base: The base of exponent
        :type base: float in range (0.0, 1.0)
        '''
        if not (0.0 < base < 1.0):
            raise ValueError('The base of exponent c must in range (0.0, 1.0)')
        self.base = base
 
    def select(self, population, fitness):
        '''
        Select a pair of parent individuals using exponential ranking method.
        '''
        # Individual number.
        NP = len(population)
        # NOTE: Here the rank i belongs to {1, ..., N}
        p = lambda i: self.base**(NP - i)
        probabilities = [p(i) for i in range(1, NP + 1)]
        # Normalize probabilities.
        psum = sum(probabilities)
        wheel = list(accumulate([p/psum for p in probabilities]))
        # Select parents.
        father_idx = bisect_right(wheel, random())
        father = population[father_idx]
        mother_idx = (father_idx + 1) % len(wheel)
        mother = population[mother_idx]
        return father, mother

总结

本文对于遗传算法中四种不同的选择策略进行了介绍和总结,同时对于本文所写的遗传算法框架的自定义算子接口进行了简要的介绍,针对本文中的选择策略分别根据接口的要求实现了相应的算子,这些算子也作为GAFT框架的内置算子放入到GAFT中,对于使用GAFT的童鞋可以直接拿来使用。

参考

  • Shukla, Anupriya, Hari Mohan Pandey, and Deepti Mehrotra. “Comparative review of selection techniques in genetic algorithm.” Futuristic Trends on Computational Analysis and Knowledge Management (ABLAZE), 2015 International Conference on. IEEE, 2015.

遗传算法中几种不同选择算子及Python实现的更多相关文章

  1. 图像边缘检测——几种图像边缘检测算子的学习及python 实现

    本文学习利用python学习边缘检测的滤波器,首先读入的图片代码如下: import cv2 from pylab import * saber = cv2.imread("construc ...

  2. Libliner 中的-s 参数选择:primal 和dual

    Libliner 中的-s 参数选择:primal 和dual LIBLINEAR的优化算法主要分为两大类,即求解原问题(primal problem)和对偶问题(dual problem).求解原问 ...

  3. iOS开发UI篇—iOS开发中三种简单的动画设置

    iOS开发UI篇—iOS开发中三种简单的动画设置 [在ios开发中,动画是廉价的] 一.首尾式动画 代码示例: // beginAnimations表示此后的代码要“参与到”动画中 [UIView b ...

  4. [转]js中几种实用的跨域方法原理详解

    转自:js中几种实用的跨域方法原理详解 - 无双 - 博客园 // // 这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同 ...

  5. 转-Web Service中三种发送接受协议SOAP、http get、http post

    原文链接:web服务中三种发送接受协议SOAP/HTTP GET/HTTP POST 一.web服务中三种发送接受协议SOAP/HTTP GET/HTTP POST 在web服务中,有三种可供选择的发 ...

  6. Redis 中 5 种数据结构的使用场景介绍

    这篇文章主要介绍了Redis中5种数据结构的使用场景介绍,本文对Redis中的5种数据类型String.Hash.List.Set.Sorted Set做了讲解,需要的朋友可以参考下 一.redis ...

  7. C#中四种常用集合的运用(非常重要)

    C#中4个常用的集合 1.ArrayList ArrayList类似于数组,有人也称它为数组列表.ArrayList可以动态维护,而数组的容量是固定的. 它的索引会根据程序的扩展而重新进行分配和调整. ...

  8. Spring中三种配置Bean的方式

    Spring中三种配置Bean的方式分别是: 基于XML的配置方式 基于注解的配置方式 基于Java类的配置方式 一.基于XML的配置 这个很简单,所以如何使用就略掉. 二.基于注解的配置 Sprin ...

  9. Java中23种经典设计模式详解

    Java中23种设计模式目录1. 设计模式 31.1 创建型模式 41.1.1 工厂方法 41.1.2 抽象工厂 61.1.3 建造者模式 101.1.4 单态模式 131.1.5 原型模式 151. ...

随机推荐

  1. Web开发生存工具使用指南

    这里安利两款我认为开发中能够极大的提高生产力的工具,Charles 和 Postman. P.S. Charles(查尔斯)..不要再读查理斯了,金刚狼中被老铁扎心的博士就叫 CharlesP.P.S ...

  2. redis学习笔记(三)

    Spring data redis: 要求: Redis 版本 > 2.6 与 Lettuce 或 Jedis 集成,两种java开源Redis库. Spring redis主要做的两件事: 连 ...

  3. Redis开启远程访问及密码认证

    配置 redis.conf 文件 [root@localhost bin]# vi /usr/local/redis/bin/redis.conf 将 bind 127.0.0.1 注释掉 将 pro ...

  4. 【Mac】安装 tesserocr 遇到的一些坑(‘cinttypes' file not found)

    问题描述 tesserocr 是 Python 的一个光学字符识别库,它其实是对 tesseract 做的一层 Python API 封装,所以在安装这个库之前我已经用 Homebrew 成功安装好了 ...

  5. Go语言中其他数据与字符串类型的转换

    1 概述 Go语言是强类型语言,因此总会需要将字符串转成需要的类型.比如整型和字符串转换,字符串和布尔型的转换等.本文就介绍如何完成这些转换,以下是Go语言关于字符串转换的整理说明,主要是与切片类型的 ...

  6. 20155207 《Java程序设计》实验报告二:Java面向对象程序设计

    实验要求 1.初步掌握单元测试和TDD 2.理解并掌握面向对象三要素:封装.继承.多态 3.初步掌握UML建模 4.熟悉S.O.L.I.D原则 5.了解设计模式 实验内容 一.单元测试 1.三种代码 ...

  7. 20155234 2006-2007-2 《Java程序设计》第4周学习总结

    20155234 2006-2007-2 <Java程序设计>第4周学习总结 教材学习内容总结 为了避免重复的行为定义使用继承. 要学会如何正确判断使用继承的时机以及继承之后如何活用多态. ...

  8. 20155313 2016-2017-2 《Java程序设计》第二周学习总结

    20155313 2016-2017-2 <Java程序设计>第二周学习总结 教材学习内容总结 1.1 基本类型 整数:可细分为short整数(占2字节).int整数(占4字节)与long ...

  9. EXCEL 处理重复数据名字后面追加值

    近期要用 EXCEL 处理重复数据名字后面追加值的,如图: 先排序,再根据条件追加 [公式]=+B6&IF(COUNTIF($B$6:B6,B6)-1>0,"_" & ...

  10. DataGrid中的DataGridCheckBoxColumn用法 ..

    <my:DataGridCheckBoxColumn Header=" /> private void btnDeleteNote_Click(object sender, Ro ...