gaft icon indicating copy to clipboard operation
gaft copied to clipboard

数值算例测试

Open TsingQAQ opened this issue 8 years ago • 4 comments

TsingQAQ avatar Nov 12 '17 12:11 TsingQAQ

非常感谢正将的GA库

由于平时也会用到一些其他的优化程序,在这里提供一个数值优化算例的测试例子(由于我比较懒而且比较菜,就直接写这里好了),测试函数是10维的Rosenbrock函数 测试结果可以作为一个Validation或者是性能参考

测试案例: 10 维 Rosenbrock 自变量x范围: -2, 2 个体数: 26 最大迭代代数: 1000 核数: 单核 理论最优(小)值: 0.0

测试结果(10次运行) GAFT: 最优解: [68.255, 8.982, 56.928, 11.604, 8.97, 9.827, 8.976, 8.746, 88.473, 9.734] 同时给出另外一个python比较热门的GA库结果作参考: Inspyred: [46, 24, 8.8, 51.9, 19.7, 10, 16, 20.187, 25. 35.15] 注意到Inspyred的1000代运行次数明显快于GAFT

附程序如下:

说明:Optimizer只是我写的一个基类,并没有用到里面任何特别的东西

from PyOptimize.General_Opt import Optimizer

from gaft import GAEngine
from gaft.components import GAIndividual
from gaft.components import GAPopulation
from gaft.operators import TournamentSelection
from gaft.operators import UniformCrossover
from gaft.operators import FlipBitBigMutation

# Built-in best fitness analysis.
from gaft.analysis.fitness_store import FitnessStore
from gaft.plugin_interfaces.analysis import OnTheFlyAnalysis

class pyGaft(Optimizer):
    def __init__(self, objfunc, var_bounds, individual_size, max_iter, air_plot=False):
        super().__init__(objfunc)
        self.max_iter = max_iter

        # 定义个体 / 种群
        self.individual = GAIndividual(ranges=var_bounds, encoding='binary', eps=0.001)
        self.population = GAPopulation(indv_template=self.individual, size=individual_size).init()

        # Create genetic operators.
        # selection = RouletteWheelSelection()
        selection = TournamentSelection()  
        crossover = UniformCrossover(pc=0.8, pe=0.5)   
        mutation = FlipBitBigMutation(pm=0.1, pbm=0.55, alpha=0.6) 

        self.engine = GAEngine(population=self.population, selection=selection,
                               crossover=crossover, mutation=mutation,
                               analysis=[FitnessStore])

        @self.engine.fitness_register
        def fitness(indv):
            """
            适应度函数: 注意这里默认为优化得到最小值
            :param indv:
            :return:
            """
            x = indv.variants
            return -objfunc(x)

        @self.engine.analysis_register
        class ConsoleOutputAnalysis(OnTheFlyAnalysis):
            interval = 1
            master_only = True

            def register_step(self, g, population, engine):
                best_indv = population.best_indv(engine.fitness)
                msg = 'Generation: {}, best fitness: {:.3f}'.format(g, engine.fitness(best_indv))
                self.logger.info(msg)

            def finalize(self, population, engine):
                best_indv = population.best_indv(engine.fitness)
                x = best_indv.variants
                y = engine.fitness(best_indv)
                msg = 'Optimal solution: ({}, {})'.format(x, y)
                self.logger.info(msg)

    def run(self):
        self.engine.run(ng=self.max_iter)

if __name__ == '__main__':
    from PyOptimize.General_Opt import Test_function

    def Rosenbrock(x):
        return Test_function().Rosenbrock(x)[0]

    GAFT_Test = pyGaft(Rosenbrock, [(-2, 2)] * 10, 26, 1000).run()

其中Rosenbreock函数为:

    def Rosenbrock(self, x):
        """
        高阶Rosenbrock函数
        给出最优解
            最优解仍然应在所有x为1时
        :param x:
        :return:
        """
        f = [0.0] * 1
        function_sum = 0
        for i in np.arange(0, len(x)-1):
            function_sum += (1 - x[i]) ** 2 + 100 * ((x[i + 1] - x[i] ** 2) ** 2)
        f[0] = function_sum
        return f

TsingQAQ avatar Nov 12 '17 12:11 TsingQAQ

谢谢对gaft的支持,不知道能否给出具体你迭代的时间和最终优化的目标函数的结果呢。我来看看大致的区别,然后自己跑个基准进行优化。最近我在空余时间打算写个c++的框架,写好后会写一个Python的接口:)

PytLab avatar Nov 12 '17 14:11 PytLab

================================================ 2017/11/19更新: 之前对于Inspyred的优化设置有误,现在改进后重新绘图。目标函数运行次数在26000左右

对结果进行一个可视化: 这个结果是上面Rosenbrock函数测试最优解的结果,两个程序(基于Indpyred的此函数优化与基于GAFT的此函数优化)分别跑10次 1 这个结果如果我没记错的话,和MATLAB的GA优化最优值比较类似,可能MATLAB的会稍微好一点点

程序用时测试 数据收集方法: 写一个main()函数,外面包一个计时装饰器,比较糙 下面这个是程序跑5次的用时 测试平台: Surface Pro 3 I5 4G Ram Windows 10 2

附一下装饰器函数

def func_get_runtime_decorator(func):
    """
     用于debug的函数装饰器: 测试函数的运行时间
     :param func:
     :return:
     """

    def wrapper(*args, **kw):
        print('run: %s():' % func.__name__)
        start = timeit.default_timer()
        try:
            func(*args, **kw)
        except:
            raise RuntimeError('!Error, failed executing: %s()' % func.__name__)
        finally:
            stop = timeit.default_timer()
            time_inc = stop - start
            print('func %s runtime is: %s seconds' % (func.__name__, time_inc))
            print('end run: %s():' % func.__name__)

    return wrapper

另: GAFT优化我是采用上面的优化代码,如果有设置不妥当的地方还希望能够指出。

TsingQAQ avatar Nov 13 '17 03:11 TsingQAQ

@TsingQAQ

我想问一个菜菜的问题...自己能力有限做不到...在这里看到了解决的可能

就是如何运行10次类似于 engine.run(ng=100)

这样的语句 因为直接套循环跑的话 基本上从第二次开始就会从上一次的最优种群作为最初始的一代 (也就是说后面的测试进化曲线基本没动) 明显一开始的种群就不是随机的了... 有没有什么能让引擎重新初始化的方法...而不是从上一次的种群结果开始

虽然每一次从终端里执行一次测试文件就能做到每一次都有正确的进化曲线... 但这么做感觉不是正确的方向

TsingQAQ的例子里 GAFT: 最优解: [68.255, 8.982, 56.928, 11.604, 8.97, 9.827, 8.976, 8.746, 88.473, 9.734]

我的错误程序跑出来的(不是同一个函数,只是一个例子)可能就是 [38.448, 38.808, 38.808, 38.808, 38.808] 请问您是怎么做这个多次的测试的...很想知道

还没断网 赶紧来更新 我只能写出这种方法来连做五次测试(虽然作业要求30次) _20180312235422 以及我原本是想用 population.new()的方式来重新初始化种群...(看了population.py的源码后发现的) 但可能用的不对 一直没有效果...这个问题还是有待比较优雅的解决方法

scarletfrank avatar Mar 12 '18 14:03 scarletfrank