正文

对GAFT进行性能分析(Profiling)

关于如何对Python程序进行性能分析生成分析报告并可视化分析报告,我在之前的一篇博客里《Python优化第一步: 性能分析实践》进行了详细的介绍,这里我就直接分析了。

为了能针对gaft中不同的函数进行分析,借助Python内置的cProfile和pstats模块我写了个装饰器方便分析并生成不同的分析统计文件。

defdo_profile(filename,sortby='tottime'):

'''

Constructor for function profiling decorator.

'''

def_do_profile(func):

'''

Function profiling decorator.

'''

@wraps(func)

defprofiled_func(*args,**kwargs):

'''

Decorated function.

'''

# Flag for doing profiling or not.

DO_PROF=os.getenv('PROFILING')

ifDO_PROF:

profile=cProfile.Profile()

profile.enable()

result=func(*args,**kwargs)

profile.disable()

ps=pstats.Stats(profile).sort_stats(sortby)

ps.dump_stats(filename)

else:

result=func(*args,**kwargs)

returnresult

returnprofiled_func

return_do_profile

defdo_profile(filename,sortby='tottime'):

'''

Constructor for function profiling decorator.

'''

def_do_profile(func):

'''

Function profiling decorator.

'''

@wraps(func)

defprofiled_func(*args,**kwargs):

'''

Decorated function.

'''

# Flag for doing profiling or not.

DO_PROF=os.getenv('PROFILING')

ifDO_PROF:

profile=cProfile.Profile()

profile.enable()

result=func(*args,**kwargs)

profile.disable()

ps=pstats.Stats(profile).sort_stats(sortby)

ps.dump_stats(filename)

else:

result=func(*args,**kwargs)

returnresult

returnprofiled_func

return_do_profile

对上面的带参数的装饰器我在这里稍微解释下,装饰器构造器do_profile的两个参数filename和sortby分别指定分析结果报告的文件名以及统计结果的排序方式。它会对需要进行性能分析的函数进行装饰,然后在函数运行完后在当前目录生成结果报告。例如我需要对gaft中遗传算法迭代主循环进行分析,则需要:

@do_profile(filename='gaft_run.prof')

defrun(self,ng=100):

...

@do_profile(filename='gaft_run.prof')

defrun(self,ng=100):

...

同时为了方便,我还需要一个环境变量PROFILING来启动分析:

export PROFILING=y

export PROFILING=y

分析结果

这里为了方便查看函数的相互调用关系,我是用了pyprof2calltree 然后使用Mac上的QCacheGrind来可视化分析结果:

pyprof2calltree -i gaft_run.prof -k

pyprof2calltree -i gaft_run.prof -k

将Python的profiling文件转换并直接调用QCacheGrind便可以方便的查看分析相关信息。

通过调用关系图可以看到,gaft的初始版本的min,max,mean等函数多次调用best_indv和worst_indv会多次调用适应度函数来相互比较,而通常情况下用户自定义的适应度函数都是需要额外去调用外部程序的,一般都比较费时。所以必须要通过优化best_indv和worst_indv对fitness的调用次数才能提升gaft的效率。

优化GAFT

函数返回值缓存

从之前我写的best_indv中可以看到,我将fitness作为key用于获取最大值,Python内置的max函数会内部调用fitness进行相互比较来获取最大值,这个时候便对fitness进行了多余的调用,因为在遗传算法中,每一代的population中的个体是不会发生变化的我们只需要在每一次迭代的一开始调用fitnessn次就好了(n为种群大小),每一代中再次需要用到适应度值的地方直接获取。这样需要我们对种群中的个体进行惰性求值,也就是对所有的fitness的值进行缓存。这种操作我在优化自己的催化动力学程序的时候也使用过,叫做函数返回值缓存.

但是在gaft中这种缓存有稍微麻烦一点,因为缓存并不是缓存一次就可以一直用了,它会随着条件的变化需要重新计算种群中所有个体的适应度然后重新缓存。

重新计算适应度值需要同时满足的条件

种群中的所有个体没有发生任何变化 (如果变化了那肯定要重新计算适应度值了)。

已有缓存的适应度值 (如果是第一次那肯定需要计算一次所有个体的适应度值)。

计算适应度值的适应度函数与之前比较没有发生变化(如果计算适应度函数都改变了,那当然需要重新估计适应度值了)。

种群中的所有个体没有发生任何变化 (如果变化了那肯定要重新计算适应度值了)。

已有缓存的适应度值 (如果是第一次那肯定需要计算一次所有个体的适应度值)。

计算适应度值的适应度函数与之前比较没有发生变化(如果计算适应度函数都改变了,那当然需要重新估计适应度值了)。

函数返回值缓存描述符

为此我写了个装饰器来缓存函数的返回值:

classMemoized(object):

'''

Deor for population statistical varibles caching.

'''

def__init__(self,func):

self.func=func

self.result=None

self.fitness=None

def__get__(self,instance,cls):

self.instance=instance

returnself

def__call__(self,fitness):

if((notself.instance._updated)# population not changed

and(self.resultisnotNone)# result already cached

and(fitness==self.fitness)):# fitness not changed

# Return cached result directly.

returnself.result

else:

# Update fitness function.

self.fitness=fitness

# Update and memoize result.

self.result=self.func(self.instance,fitness)

# Recover flag.

self.instance._updated=False

returnself.result

classMemoized(object):

'''

Deor for population statistical varibles caching.

'''

def__init__(self,func):

self.func=func

self.result=None

self.fitness=None

def__get__(self,instance,cls):

self.instance=instance

returnself

def__call__(self,fitness):

if((notself.instance._updated)# population not changed

and(self.resultisnotNone)# result already cached

and(fitness==self.fitness)):# fitness not changed

# Return cached result directly.

returnself.result

else:

# Update fitness function.

self.fitness=fitness

# Update and memoize result.

self.result=self.func(self.instance,fitness)

# Recover flag.

self.instance._updated=False

returnself.result

动态监视种群的变化

好了上面我们可以通过描述符来缓存函数返回值,但是一旦种群不满足上述的三个条件就需要重新计算适应度值,那我们如何监控种群的变化呢?

我在GAPopulation中添加了一个标记_updated用于标记种群是否已经发生了变化, 然后我们的任务就是在其他能够影响到种群的地方试图去更新这个flag。

如何能更Pythonic的更新这个标记呢?

所谓的种群发生变化,也是就种群中的个体列表发生了变化,种群中的个体我都放在了一个列表中,我需要监控这个列表是否发生变化以便更新flag,具体又是那些变化呢?

列表整体是否发生了变化(赋值操作)

列表中的元素是否发生变化(对列表中的元素赋值操作,列表的append, extend操作等)

列表整体是否发生了变化(赋值操作)

列表中的元素是否发生变化(对列表中的元素赋值操作,列表的append, extend操作等)

好了我们要具体怎么实现呢?

1)对于第一种,由于Python中无法进行赋值运算符重载,但是我们可以通过描述符的__set__来处理:

classGAIndividuals(object):

'''

Deor for all individuals in population.

'''

def__init__(self,name):

self.name='_{}'.format(name)

def__get__(self,instance,owner):

returninstance.__dict__[self.name]

def__set__(self,instance,value):

instance.__dict__[self.name]=value

# Update flag.

instance._updated=True

classGAIndividuals(object):

'''

Deor for all individuals in population.

'''

def__init__(self,name):

self.name='_{}'.format(name)

def__get__(self,instance,owner):

returninstance.__dict__[self.name]

def__set__(self,instance,value):

instance.__dict__[self.name]=value

# Update flag.

instance._updated=True

2)对于第二种情况,我们需要对Python的List类型的相应方法进行override

但是嘞,即使重写了list的接口,又如何更新population中的变量呢?这个时候就需要用闭包了。在GAPopulation的构造函数__init__中定义list的派生类,并立即实例化,这时候派生类的便可以获取population对象了,于是GAPopulation的构造函数可以这些写:

def__init__(self,indv_template,size=100):

# ...

# Flag for monitoring changes of population.

self._updated=False

# Container for all individuals.

classIndvList(list):

'''

A proxy class inherited from built-in list to contain all

individuals which can update the population._updated flag

automatically when its content is changed.

'''

# NOTE: Use 'this' here to avoid name conflict.

def__init__(this,*args):

super(this.__class__,this).__init__(*args)

def__setitem__(this,key,value):

'''

Override __setitem__ in built-in list type.

'''

old_value=this[key]

ifold_value==value:

return

super(this.__class__,self).__setitem__(key,value)

# Update population flag.

self._updated=True

defappend(this,item):

'''

Override append method of built-in list type.

'''

super(this.__class__,this).append(item)

# Update population flag.

self._updated=True

defextend(this,iterable_item):

ifnotiterable_item:

return

super(this.__class__,this).extend(iterable_item)

# Update population flag.

self._updated=True

self._individuals=IndvList()

def__init__(self,indv_template,size=100):

# ...

# Flag for monitoring changes of population.

self._updated=False

# Container for all individuals.

classIndvList(list):

'''

A proxy class inherited from built-in list to contain all

individuals which can update the population._updated flag

automatically when its content is changed.

'''

# NOTE: Use 'this' here to avoid name conflict.

def__init__(this,*args):

super(this.__class__,this).__init__(*args)

def__setitem__(this,key,value):

'''

Override __setitem__ in built-in list type.

'''

old_value=this[key]

ifold_value==value:

return

super(this.__class__,self).__setitem__(key,value)

# Update population flag.

self._updated=True

defappend(this,item):

'''

Override append method of built-in list type.

'''

super(this.__class__,this).append(item)

# Update population flag.

self._updated=True

defextend(this,iterable_item):

ifnotiterable_item:

return

super(this.__class__,this).extend(iterable_item)

# Update population flag.

self._updated=True

self._individuals=IndvList()

优化效果

通过上面对代码的优化,我们看看我们优化的效果如何,使用分析描述符来分析GAEngine.run跑一代种群的情况,其中种群大小为10。如下图为cProfile生成的分析报告对比:

可以看到优化后的跑一代种群的时间缩短为将近原来的1/7! 优化效果还是很明显的。

然后看一看调用关系图:

energy_fitness的调用次数从3807降到了621次!

总结

本文记录了遗传算法框架GAFT的一次profiling和优化过程,通过缓存值的方式极大的减少了适值函数的调用次数,在时间上,跑一代种群的效率提升了7倍左右。

关注「大数据与机器学习文摘」,成为Top 1%

python框架优化方法_Python 遗传算法框架 GAFT 优化小记相关推荐

  1. python制作考试系统_python测试框架数据生成工具最全资源汇总

    xUnit frameworks 单元测试框架 frameworks 框架 unittest - python自带的单元测试库,开箱即用 unittest2 - 加强版的单元测试框架,适用于Pytho ...

  2. python 爬虫哪个好_Python爬虫框架哪个最好用最简单

    想使用python爬虫框架开发个爬虫,但目前了解有限,希望大家帮忙推荐几个,谢谢啦~ 另外,大家平常工作中都使用python干些什么事情呢?网站开发?爬虫?还是运维? scrapy 爬虫框架.纯pyt ...

  3. python __reduce__魔法方法_Python魔法方法指南

    (译)Python魔法方法指南 简介 本指南归纳于我的几个月的博客,主题是 魔法方法 . 什么是魔法方法呢?它们在面向对象的Python的处处皆是.它们是一些可以让你对类添加"魔法" ...

  4. python的open方法_Python os.open() 方法

    Python os.open() 方法 概述 os.open() 方法用于打开一个文件,并且设置需要的打开选项,模式参数mode参数是可选的,默认为 0777. 语法 open()方法语法格式如下: ...

  5. python字符串反转方法_Python程序使用堆栈和反转方法反转字符串

    python字符串反转方法 Given a string and we have to reverse it by using stack and by using reversed method i ...

  6. python函数isdisjoint方法_Python学习之---Python中的内置函数(方法)(更新中。。。)...

    add(item)#将item添加到s中,如果item已经在s中,则无任何效果 break#退出循环,不会再运行循环中余下的代码 bool()#将参数转换为布尔型 bytes()#将值转成bytes类 ...

  7. python列表get方法_Python json.get方法代码示例

    本文整理汇总了Python中json.get方法的典型用法代码示例.如果您正苦于以下问题:Python json.get方法的具体用法?Python json.get怎么用?Python json.g ...

  8. python contains魔法方法_Python魔法方法详解

    据说,Python 的对象天生拥有一些神奇的方法,它们总被双下划线所包围,他们是面向对象的 Python 的一切. 他们是可以给你的类增加魔力的特殊方法,如果你的对象实现(重载)了这些方法中的某一个, ...

  9. python 财务分析可视化方法_Python数据可视化的四种简易方法

    Python数据可视化的四种简易方法 作者:PHPYuan 时间:2018-11-28 03:40:43 摘要: 本文讲述了热图.二维密度图.蜘蛛图.树形图这四种Python数据可视化方法. 数据可视 ...

最新文章

  1. 为什么DL模型能够正确分类?SCOUTER(ICCV21')从“正”“反”方面说服你。
  2. Python3学习之路
  3. Linux 操作系统原理 — 系统结构
  4. PyQt5 技术篇-调用颜色对话框(QColorDialog)获取颜色,调色板的调用。
  5. 都快下班了,才来写日记
  6. 从前序与中序遍历序列构造二叉树Python解法
  7. MySQL中事物的详解
  8. mysql 3t_编译安装mysql
  9. 品牌设计工作室html5模板
  10. 2021 年各家大厂的 AI Lab 现状如何?
  11. 计算机网络学习笔记(26. Web缓存技术)
  12. QT.pro工程文件中判断宏定义是否存在
  13. 数据结构 以数组的形式存储数据(c语言 干货满满)
  14. 常见的十大量化投资策略(附源码)
  15. Centos升级GLIBC
  16. Failed to recurse into submodule path 'third_party/onnx'
  17. 在WPS中提取出的照片在哪找_教你使用 Excel 快速更换照片背景底色
  18. 帝国cms支持php多少,帝国cms支持php吗
  19. Factory reset会黑屏一段时间进入Provision首页
  20. CentOS7 阻止笔记本合盖时休眠

热门文章

  1. ElasticSearch 最全详细使用教程
  2. Windows 10 安装 scrapy 步骤
  3. 1 Linux SSH安全加固
  4. scp命令和rsync命令
  5. 原始的Ajax请求方式 (XMLHttpRequest)
  6. Mysql数据库中的 Group by 语句的特殊之处(select 中的项目不必出现在Group by中)---不建议使用!
  7. 【HDFS】HDFS与getconf结合使用,获取配置信息
  8. Django链接Mysql 8.0 出现错误(1045:Access denied for user ‘root‘@‘localhost‘ (using password: NO) 的一种解决方法
  9. Python3基础 try-指定except-as reason 捕获打开一个不存在的文件的时候,会产生OSError异常的示例
  10. 恒丰银行助手提示注册表异常但修复不了的解决方法