使用 MPI for Python 并行化遗传算法

前言

本文中作者使用MPI的Python接口mpi4py来将自己的遗传算法框架GAFT进行多进程并行加速。并对加速效果进行了简单测试。

项目链接:

正文

我们在用遗传算法优化目标函数的时候,函数通常都是高维函数,其导数一般比较难求取。这样我们的适应度函数计算通常都是比较费时的计算。

例如在使用遗传算法寻找最优结构时候通常需要调用量化软件进行第一性原理计算结构的total energy,这是非常费时的过程; 例如我们优化力场参数的时候,以力场计算出的能量同基准能量之前的误差作为适应度,也需要调用相应的力场程序获取总能量来求取,同样这个过程也是相对耗时的。

这就会导致一个问题,当我们的种群比较大的时候,我们需要利用适应度信息来产生下一代种群,这时候每一代繁殖的过程将会很耗时。但有幸的是,种群的选择交叉变异过程对于种群中的个体都是相互独立的过程,我们可以将这一部分进行并行处理来加速遗传算法的迭代。

使用mpi4py

由于实验室的集群都是MPI环境,我还是选择使用MPI接口来将代码并行化,这里我还是用了MPI接口的Python版本mpi4py来将代码并行化。关于mpi4py的使用,我之前写过一篇博客专门做了介绍,可以参见《Python多进程并行编程实践-mpi4py的使用》

将mpi4py的接口进一步封装

为了能让mpi的接口在GAFT中更方便的调用,我决定将mpi4py针对遗传算法中需要用的地方进行进一步封装,为此我单独写了个MPIUtil类, 详细代码参见gaft/mpiutil.py

封装通信子常用的接口

例如进程同步, 获取rank,进程数,判断是否为主进程等。

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

class MPIUtil(object):

def __init__(self):

logger_name = ‘gaft.{}‘.format(self.__class__.__name__)

self._logger = logging.getLogger(logger_name)

# Wrapper for common MPI interfaces.

def barrier(self):

if MPI_INSTALLED:

mpi_comm = MPI.COMM_WORLD

mpi_comm.barrier()

@property

def rank(self):

if MPI_INSTALLED:

mpi_comm = MPI.COMM_WORLD

return mpi_comm.Get_rank()

else:

return 0

@property

def size(self):

if MPI_INSTALLED:

mpi_comm = MPI.COMM_WORLD

return mpi_comm.Get_size()

else:

return 1

@property

def is_master(self):

return self.rank == 0

组内集合通信接口

由于本次并行化的任务是在种群繁衍时候进行的,因此我需要将上一代种群进行划分,划分成多个子部分,然后在每个进程中对划分好的子部分进行选择交叉变异等遗传操作。在最后将每个字部分得到的子种群进行收集合并。为此写了几个划分和收集的接口:

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

def split_seq(self, sequence):

‘‘‘

Split the sequence according to rank and processor number.

‘‘‘

starts = [i for i in range(0, len(sequence), len(sequence)//self.size)]

ends = starts[1: ] + [len(sequence)]

start, end = list(zip(starts, ends))[self.rank]

return sequence[start: end]

def split_size(self, size):

‘‘‘

Split a size number(int) to sub-size number.

‘‘‘

if size < self.size:

warn_msg = (‘Splitting size({}) is smaller than process ‘ +

‘number({}), more processor would be ‘ +

‘superflous‘).format(size, self.size)

self._logger.warning(warn_msg)

splited_sizes = [1]*size + [0]*(self.size - size)

elif size % self.size != 0:

residual = size % self.size

splited_sizes = [size // self.size]*self.size

for i in range(residual):

splited_sizes[i] += 1

else:

splited_sizes = [size // self.size]*self.size

return splited_sizes[self.rank]

def merge_seq(self, seq):

‘‘‘

Gather data in sub-process to root process.

‘‘‘

if self.size == 1:

return seq

mpi_comm = MPI.COMM_WORLD

merged_seq= mpi_comm.allgather(seq)

return list(chain(*merged_seq))

用于限制程序在主进程执行的装饰器

有些函数例如日志输出,数据收集的函数,我只希望在主进程执行,为了方便,写了个装饰器来限制函数在主进程中执行:

Python

1

2

3

4

5

6

7

8

9

10

11

def master_only(func):

‘‘‘

Decorator to limit a function to be called

only in master process in MPI env.

‘‘‘

@wraps(func)

def _call_in_master_proc(*args, **kwargs):

if mpi.is_master:

return func(*args, **kwargs)

return _call_in_master_proc

在遗传算法主循环中添加并行

主要在种群繁衍中对种群针对进程数进行划分然后并行进行遗传操作并合并子种群完成并行,代码改动很少。详见:https://github.com/PytLab/gaft/blob/master/gaft/engine.py#L67

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

# Enter evolution iteration.

for g in range(ng):

# Scatter jobs to all processes.

local_indvs = []

local_size = mpi.split_size(self.population.size // 2)

# Fill the new population.

for _ in range(local_size):

# Select father and mother.

parents = self.selection.select(self.population, fitness=self.fitness)

# Crossover.

children = self.crossover.cross(*parents)

# Mutation.

children = [self.mutation.mutate(child) for child in children]

# Collect children.

local_indvs.extend(children)

# Gather individuals from all processes.

indvs = mpi.merge_seq(local_indvs)

# The next generation.

self.population.individuals = indvs

测试加速效果

测试一维搜索

下面我针对项目中的一维优化的例子进行并行加速测试来看看加速的效果。例子代码在/examples/ex01/

由于自己本子核心数量有限,我把gaft安装在实验室集群上使用MPI利用多核心进行并行计算一维优化,种群大小为50,代数为100,针对不同核心数可以得到不同的优化时间和加速比。可视化如下图:

核心数与优化时间的关系:

核心数与加速比:

测试力场优化

这里我对自己要研究的对象进行加速测试,这部分代码并未开源,针对每个个体的适应度计算都需要调用其他的计算程序,因此此过程相比直接有函数表达式的目标函数计算要耗时很多。厦门chache

同样,我针对不同核心数看看使用MPI在集群上加速的效果:

核心数与优化时间的关系:

核心数与加速比:

可见针对上述两个案例,MPI对遗传算法的加速还是比较理想的,程序可以扔到集群上飞起啦~~~

总结

本文主要总结了使用mpi4py对遗传算法进行并行化的方法和过程,并对加速效果进行了测试,可见MPI对于遗传算法框架GAFT的加速效果还是较为理想的。带有MPI并行的遗传算法框架目前也已更新并上传至GitHub(https://github.com/PytLab/gaft) 欢迎围观[]~( ̄▽ ̄)~*

时间: 2024-11-12 00:28:07

使用 MPI for Python 并行化遗传算法的相关文章

python并行化介绍及使用

本篇将要介绍python的并行化,及简单的应用. 主要介绍map函数的使用,一手包办了序列操作.参数传递和结果保存等一系列的操作. 首先是引入库: from multiprocessing.dummy import Pool pool=Pool(4) results=pool.map(爬取函数,网址列表) 本文将一个简单的例子来看一下如何使用map函数以及这种方法与普通方法的对比情况. import time from multiprocessing.dummy import Pool def

连通域标记算法并行化(MPI+OpenMP)

1 背景 图像连通域标记算法是从一幅栅格图像(通常为二值图像)中,将互相邻接(4邻接或8邻接)的具有非背景值的像素集合提取出来,为不同的连通域填入数字标记,并且统计连通域的数目.通过对栅格图像中进行连通域标记,可用于静态地分析各连通域斑块的分布,或动态地分析这些斑块随时间的集聚或离散,是图像处理非常基础的算法.目前常用的连通域标记算法有1)扫描法(二次扫描法.单向反复扫描法等).2)线标记法.3)区域增长法.二次扫描法由于简单通用而被广泛使用! 图1 连通域标记示意图 随着所要处理的数据量越来越

遗传算法用python简单解释

遗传算法模仿了生物遗传进化的过程,可以在给定范围内搜索最优解.遗传算法的设计一般包括参数编码.初始群体的设定.适应度函数的设计.遗传操作设计(选择.交叉.变异).控制参数设定等. 0.问题 在这里,我们基于python使用遗传算法尝试搜索函数 \(y = -x^2+2x+5\) 在区间\([0,63]\)内的最大值,简便起见只取区间内的整数. 1.参数编码 对于本问题,用6个二进制位即可表示0~63的所有整数,其中每组编码可视为一条染色体或个体,具体编码如下: \([0,0,0,0,0,0,0]

70个Python练手项目

前言: 不管学习那门语言都希望能做出实际的东西来,这个实际的东西当然就是项目啦,不用多说大家都知道学编程语言一定要做项目才行. 这里整理了70个Python实战项目列表,都有完整且详细的教程,你可以从中选择自己想做的项目进行参考学习练手,你也可以从中寻找灵感去做自己的项目. 70个Python项目列表: 1.[Python 图片转字符画]2.[200行Python代码实现2048]3.[Python3 实现火车票查询工具]4.[高德API+Python解决租房问题 ]5.[Python3 色情图

【Python秘籍】十进制整数与二进制数的转换

最近在用Python写遗传算法时,发现需要将十进制的整数转换成二进制数,那么怎么来转换呢?当然如果你学过进制转换的有关计算方法,你可以手动编写一些函数来实现,不过总体来说还是比较麻烦的,这里介绍Python内置的两个函数bin()和int(),利用这两个函数可以轻轻松松完成转换. 一.十进制整数转换成二进制数 代码如下: num = 8 numb = bin(num) print(numb) 这段代码的输出结果如下: 0b1000 0b是什么呢?有过一定计算机专业基础的人一定知道,其实他只是表示

分布式机器学习的故事

王益博士,称得上机器学习领域的资深从业者,本人之前有幸拜读过王益博士的一些paper和slides,对其从事的"分布式机器学习"方向尤感兴趣. 王益博士之前写过一篇<分布式机器学习的故事>,总结了自己从业多年的经验和感悟.可惜的是,这篇原始博文已经删除了,现在能找到的是原始的六篇讲稿素材:A New Era:Infrequent itemset mining:Application Driven:Implement Your MapReduce:Deep Learning:

我的分布式机器学习故事

我的分布式机器学习故事 从毕业加入Google开始做分布式机器学习,到后来转战腾讯广告业务,至今已经七年了.我想说说我见到的故事和我自己的实践经历.这段经历给我的感觉是:虽然在验证一个新的并行算法的正确性的时候,我们可以利用现有框架,尽量快速实现,但是任何一个有价值的机器学习思路,都值得拥有自己独特的架构.所以重点在有一个分布式操作系统,方便大家开发自己需要的架构(框架),来支持相应的算法.如果你关注大数据,听完我说的故事,应该会有感触. 大数据和分布式机器学习 特点 说故事之前,先提纲挈领的描

boost 库编译选项

boost大部分库只需要包含头文件即可使用,而有部分需要编译的,如下: E:\Qt\Qt3rdlib\boost_1_58_0>bjam --show-libraries The following libraries require building: - atomic - chrono - container - context - coroutine - date_time - exception - filesystem - graph - graph_parallel - iostre

Visual Studio 2013 编译 boost-1_56_0_b1

1.首先下载源代码包:http://www.boost.org/users/history/version_1_56_0.html  目前最新版本 2.解压缩到D:\boost-1_56_0_b1目录下 3.点击 D:\boost-1_56_0_b1目录下 bootstrap.bat 批处理文件  会生成bjam.exe和project-config.jam这两个文件 4.在开始菜单中 找到 Visual Studio 2013---Visual Studio Tools----VS2013 开