与最佳者对比的基准测试 (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 中找到。