早在之前很多項目尤其是預測類型的項目中,就已經比較廣泛地在實用貝葉斯優化庫了,這是一個非常出色的純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 andpurposes think of the internals of this function, i.e.: the processwhich 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 silentrandom_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 Eventslogger = 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 + 1optimizer = BayesianOptimization(f=None,pbounds={'x': (-2, 2), 'y': (-3, 3)},verbose=2,random_state=1,
)from bayes_opt import UtilityFunctionutility = 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) == intreturn ((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().
這個問題與上面問題往往是交替或者是同時出現的。
目前依舊在研究文檔,不知道是不是自己哪里理解有誤導致的,也歡迎有同樣問題的朋友一起溝通交流。