与最佳者对比的基准测试 (BBOB)

一旦你创建了自己的算法,DEAP 的结构允许你非常容易地将其与最佳算法进行基准测试。黑箱优化基准 (BBOB) 的接口与工具箱兼容。事实上,一旦你的新算法被封装在一个主函数中,DEAP 方面几乎不需要做其他事情。本教程将回顾使所有内容与非常基本的 五分之一规则 一起工作的关键步骤。

准备算法

BBOB 利用了许多连续函数,算法将在这些函数上进行测试。这些函数作为参数传递给算法。因此,工具箱应在主函数中记录评估结果。

BBOB 提供的评估函数返回一个作为单个值的适应度。第一步是将每个适应度放入其自己的元组中,这是 DEAP 在单目标优化方面的哲学要求。我们将为此使用一个装饰器。

def tupleize(func):
    """A decorator that tuple-ize the result of a function. This is useful
    when the evaluation function returns a single value.
    """
    def wrapper(*args, **kargs):
        return func(*args, **kargs),
    return wrapper

该算法封装在一个主函数中,该函数接收四个参数:评估函数、问题的维度、最大评估次数和要达到的目标值。如前所述,工具箱在主函数中使用 update() 函数(在示例中描述)和接收到的评估函数进行初始化,该评估函数由我们的元组化器装饰。

然后,目标适应度值被封装在一个 FitnessMin 对象中,以便我们可以轻松地将其与个体进行比较。最后一步是定义算法,这在 五分之一规则 示例中进行了解释。

def main(func, dim, maxfuncevals, ftarget=None):
    toolbox = base.Toolbox()
    toolbox.register("update", update)
    toolbox.register("evaluate", func)
    toolbox.decorate("evaluate", tupleize)

    # Create the desired optimal function value as a Fitness object
    # for later comparison
    opt = creator.FitnessMin((ftarget,))

    # Interval in which to initialize the optimizer
    interval = -5, 5
    sigma = (interval[1] - interval[0])/2.0
    alpha = 2.0**(1.0/dim)

    # Initialize best randomly and worst as a place holder
    best = creator.Individual(random.uniform(interval[0], interval[1]) for _ in range(dim))
    worst = creator.Individual([0.0] * dim)

    # Evaluate the first individual
    best.fitness.values = toolbox.evaluate(best)

    # Evolve until ftarget is reached or the number of evaluation
    # is exhausted (maxfuncevals)
    for g in range(1, maxfuncevals):
        toolbox.update(worst, best, sigma)
        worst.fitness.values = toolbox.evaluate(worst)
        if best.fitness <= worst.fitness:
            # Increase mutation strength and swap the individual
            sigma = sigma * alpha
            best, worst = worst, best
        else:
            # Decrease mutation strength
            sigma = sigma * alpha**(-0.25)

        # Test if we reached the optimum of the function
        # Remember that ">" for fitness means better (not greater)
        if best.fitness > opt:
            return best

    return best

运行基准测试

既然算法已经准备好了,现在是时候在 BBOB 下运行它了。以下代码取自 BBOB 示例,并添加了注释。fgeneric 模块提供了一个 LoggingFunction,它负责输出所有必要的数据,以便将测试的算法与其他已发布和即将发布的算法进行比较。

此记录器包含当前问题实例并提供问题目标。由于它负责记录每次评估函数调用,因此无需保存算法找到的最佳个体(调用 main() 函数)。与提供的算法相关的唯一一行代码在调用 main() 函数中。

from deap import benchmarks

import fgeneric

if __name__ == "__main__":
    # Maximum number of restart for an algorithm that detects stagnation
    maxrestarts = 1000

    # Create a COCO experiment that will log the results under the
    # ./output directory
    e = fgeneric.LoggingFunction("output")

    # Iterate over all desired test dimensions
    for dim in (2, 3, 5, 10, 20, 40):
        # Set the maximum number function evaluation granted to the algorithm
        # This is usually function of the dimensionality of the problem
        maxfuncevals = 100 * dim**2
        minfuncevals = dim + 2

        # Iterate over a set of benchmarks (noise free benchmarks here)
        for f_name in bn.nfreeIDs:

            # Iterate over all the instance of a single problem
            # Rotation, translation, etc.
            for instance in chain(range(1, 6), range(21, 31)):

                # Set the function to be used (problem) in the logger
                e.setfun(*bn.instantiate(f_name, iinstance=instance))

                # Independent restarts until maxfunevals or ftarget is reached
                for restarts in range(0, maxrestarts + 1):
                    if restarts > 0:
                        # Signal the experiment that the algorithm restarted
                        e.restart('independent restart')  # additional info

                    # Run the algorithm with the remaining number of evaluations
                    revals = int(math.ceil(maxfuncevals - e.evaluations))
                    main(e.evalfun, dim, revals, e.ftarget)

                    # Stop if ftarget is reached
                    if e.fbest < e.ftarget or e.evaluations + minfuncevals > maxfuncevals:
                        break

                e.finalizerun()

                print('f%d in %d-D, instance %d: FEs=%d with %d restarts, '
                      'fbest-ftarget=%.4e'
                      % (f_name, dim, instance, e.evaluations, restarts,
                         e.fbest - e.ftarget))

            print('date and time: %s' % time.asctime())

一旦这些实验完成,output 目录中的数据可以用来构建结果文档。关于如何构建文档,请参见 BBOB 网站。

完整的示例可以在文件 examples/bbob 中找到。