早在之前很多项目尤其是预测类型的项目中,就已经比较广泛地在实用贝叶斯优化库了,这是一个非常出色的纯python实现的项目,地址在这里,如下所示:
写这篇文章主要有两个目的,一方面是觉得这个工具库挺不错的值得学习使用;另一方面是这段时间陆陆续续在使用的过程中出现了很多莫名的错误,虽然一部分在官方的issues中已经得到了解决,但是还是有几个问题依旧是无法解决,所以就想着一方面找时间看下官方的介绍说明看看有没有合适的办法,另一方面就是作为一个开放讨论的平台,欢迎有同样问题的朋友交流沟通。
安装方式很简单:
$ pip install bayesian-optimization
贝叶斯优化通过构造函数的后验分布(高斯过程)来工作,该后验分布最好地描述了要优化的函数。随着观测值数量的增加,后验分布得到改善,算法更加确定参数空间中哪些区域值得探索,哪些区域不值得探索,如下图所示。
当您反复迭代时,该算法会根据其对目标函数的了解来平衡其探索和开发需求。在每个步骤中,将高斯过程拟合到已知样本(先前探索的点),并使用后验分布结合探索策略(例如UCB(置信上限)或EI(预期改进)),以确定应探索的下一个点(动图gif)。
此过程旨在最小化找到接近最佳组合的参数组合所需的步骤数。为此,该方法使用了一个代理优化问题(寻找捕获函数的最大值),尽管这仍然是一个困难的问题,但成本较低(在计算意义上),并且可以使用通用工具。因此,贝叶斯优化是最适合的情况下采样的功能进行优化是一个非常昂贵的努力。
官方项目中提供了基础使用教程和进阶使用教程还有一些对应的开发实例。
基础使用教程
这是一个基于贝叶斯推理和高斯过程的约束全局优化包,它试图在尽可能少的迭代中找到未知函数的最大值。该技术特别适用于高成本函数的优化,在这种情况下,勘探和开发之间的平衡非常重要。贝叶斯优化通过构造函数的后验分布(高斯过程)来工作,该后验分布最好地描述了要优化的函数。随着观测值数量的增加,后验分布得到改善,算法更加确定参数空间中哪些区域值得探索,哪些区域不值得探索,如下图所示。
当您反复迭代时,该算法会根据其对目标函数的了解来平衡其探索和开发需求。在每个步骤中,将高斯过程拟合到已知样本(先前探索的点),并使用后验分布结合探索策略(例如UCB(置信上限)或EI(预期改进)),以确定应探索的下一个点(参见下面的gif)。
此过程旨在最小化找到接近最佳组合的参数组合所需的步骤数。为此,该方法使用了一个代理优化问题(寻找捕获函数的最大值),尽管这仍然是一个困难的问题,但成本较低(在计算意义上),并且可以使用通用工具。因此,贝叶斯优化是最适合的情况下采样的功能进行优化是一个非常昂贵的努力。
1、指定优化函数
def black_box_function(x, y):
"""Function with unknown internals we wish to maximize.
This is just serving as an example, for all intents and
purposes think of the internals of this function, i.e.: the process
which generates its output values, as unknown.
"""
return -x ** 2 - (y - 1) ** 2 + 1
2、实践应用
from bayes_opt import BayesianOptimization
# Bounded region of parameter space
pbounds = {'x': (2, 4), 'y': (-3, 3)}
optimizer = BayesianOptimization(
f=black_box_function,
pbounds=pbounds,
verbose=2, # verbose = 1 prints only when a maximum is observed, verbose = 0 is silent
random_state=1,
)
optimizer.maximize(
init_points=2,
n_iter=3,
)
print(optimizer.max)
for i, res in enumerate(optimizer.res):
print("Iteration {}: \n\t{}".format(i, res))
#修改参数边界
optimizer.set_bounds(new_bounds={"x": (-2, 3)})
optimizer.maximize(
init_points=0,
n_iter=5,
)
3、优化
optimizer.probe(
params={"x": 0.5, "y": 0.7},
lazy=True,
)
print(optimizer.space.keys)
optimizer.probe(
params=[-0.3, 0.1],
lazy=True,
)
optimizer.maximize(init_points=0, n_iter=0)
4、存储、加载、继续
#Saving progress
from bayes_opt.logger import JSONLogger
from bayes_opt.event import Events
logger = JSONLogger(path="./logs.log")
optimizer.subscribe(Events.OPTIMIZATION_STEP, logger)
optimizer.maximize(
init_points=2,
n_iter=3,
)
#Loading progress
from bayes_opt.util import load_logs
new_optimizer = BayesianOptimization(
f=black_box_function,
pbounds={"x": (-2, 2), "y": (-2, 2)},
verbose=2,
random_state=7,
)
print(len(new_optimizer.space))
load_logs(new_optimizer, logs=["./logs.log"]);
print("New optimizer is now aware of {} points.".format(len(new_optimizer.space)))
new_optimizer.maximize(
init_points=0,
n_iter=10,
)
进阶使用教程
1.、Suggest-Evaluate-Register Paradigm
# Let's start by defining our function, bounds, and instantiating an optimization object.
def black_box_function(x, y):
return -x ** 2 - (y - 1) ** 2 + 1
optimizer = BayesianOptimization(
f=None,
pbounds={'x': (-2, 2), 'y': (-3, 3)},
verbose=2,
random_state=1,
)
from bayes_opt import UtilityFunction
utility = UtilityFunction(kind="ucb", kappa=2.5, xi=0.0)
next_point_to_probe = optimizer.suggest(utility)
print("Next point to probe is:", next_point_to_probe)
target = black_box_function(**next_point_to_probe)
print("Found the target value to be:", target)
optimizer.register(
params=next_point_to_probe,
target=target,
)
#The maximize loop
for _ in range(5):
next_point = optimizer.suggest(utility)
target = black_box_function(**next_point)
optimizer.register(params=next_point, target=target)
print(target, next_point)
print(optimizer.max)
2、Dealing with discrete parameters
def func_with_discrete_params(x, y, d):
# Simulate necessity of having d being discrete.
assert type(d) == int
return ((x + y + d) // (1 + d)) / (1 + (x + y) ** 2)
def function_to_be_optimized(x, y, w):
d = int(w)
return func_with_discrete_params(x, y, d)
optimizer = BayesianOptimization(
f=function_to_be_optimized,
pbounds={'x': (-10, 10), 'y': (-10, 10), 'w': (0, 5)},
verbose=2,
random_state=1,
)
optimizer.set_gp_params(alpha=1e-3)
optimizer.maximize()
3、Tuning the underlying Gaussian Process
optimizer = BayesianOptimization(
f=black_box_function,
pbounds={'x': (-2, 2), 'y': (-3, 3)},
verbose=2,
random_state=1,
)
optimizer.set_gp_params(alpha=1e-3, n_restarts_optimizer=5)
optimizer.maximize(
init_points=1,
n_iter=5
)
from bayes_opt.event import DEFAULT_EVENTS, Events
optimizer = BayesianOptimization(
f=black_box_function,
pbounds={'x': (-2, 2), 'y': (-3, 3)},
verbose=2,
random_state=1,
)
class BasicObserver:
def update(self, event, instance):
"""Does whatever you want with the event and `BayesianOptimization` instance."""
print("Event `{}` was observed".format(event))
my_observer = BasicObserver()
optimizer.subscribe(
event=Events.OPTIMIZATION_STEP,
subscriber=my_observer,
callback=None, # Will use the `update` method as callback
)
def my_callback(event, instance):
print("Go nuts here!")
optimizer.subscribe(
event=Events.OPTIMIZATION_START,
subscriber="Any hashable object",
callback=my_callback,
)
optimizer.maximize(init_points=1, n_iter=2)
进一步的详情可以自行了解官方教程即可。
简单的实践介绍就到这里,下面说两个还记得的报错问题。
1、StopIteration: Queue is empty error in optimizer.maximize()
关于这个问题,官方论坛和stackoverflow论坛里面都有很多讨论,也有给出一些对应的解决办法,但是都没有能解决我的问题。
2、"ValueError: array must not contain infs or NaNs" happens after self.suggest().
这个问题与上面问题往往是交替或者是同时出现的。
目前依旧在研究文档,不知道是不是自己哪里理解有误导致的,也欢迎有同样问题的朋友一起沟通交流。