注意
跳转到末尾以下载完整示例代码。
用户自定义采样器
借助用户自定义采样器,您可以
试验您自己的采样算法,
实现特定于任务的算法以提升优化性能,或
包装其他优化库以将其集成到 Optuna 管道中(例如,BoTorchSampler)。
本节介绍采样器类的内部行为,并展示了一个实现用户自定义采样器的示例。
采样器概述
采样器负责确定在一次 trial 中要评估的参数值。当在目标函数内部调用 suggest API(例如,suggest_float()
)时,内部会创建相应的分布对象(例如,FloatDistribution
)。采样器从该分布中采样一个参数值。采样到的值会返回给 suggest API 的调用者,并在目标函数中进行评估。
要创建一个新的采样器,您需要定义一个继承自 BaseSampler
的类。该基类有三个抽象方法:infer_relative_search_space()
、sample_relative()
和 sample_independent()
。
顾名思义,Optuna 支持两种类型的采样:一种是相对采样,它可以考虑 trial 中参数的相关性;另一种是独立采样,它独立采样每个参数。
在 trial 开始时,会调用 infer_relative_search_space()
来提供 trial 的相对搜索空间。然后调用 sample_relative()
从搜索空间中采样相对参数。在目标函数执行期间,使用 sample_independent()
采样不属于相对搜索空间的参数。
注意
有关更多详细信息,请参阅 BaseSampler
的文档。
示例:实现 SimulatedAnnealingSampler
例如,以下代码定义了一个基于模拟退火 (SA) 的采样器
import numpy as np
import optuna
class SimulatedAnnealingSampler(optuna.samplers.BaseSampler):
def __init__(self, temperature=100):
self._rng = np.random.RandomState()
self._temperature = temperature # Current temperature.
self._current_trial = None # Current state.
def sample_relative(self, study, trial, search_space):
if search_space == {}:
return {}
# Simulated Annealing algorithm.
# 1. Calculate transition probability.
prev_trial = study.trials[-2]
if self._current_trial is None or prev_trial.value <= self._current_trial.value:
probability = 1.0
else:
probability = np.exp(
(self._current_trial.value - prev_trial.value) / self._temperature
)
self._temperature *= 0.9 # Decrease temperature.
# 2. Transit the current state if the previous result is accepted.
if self._rng.uniform(0, 1) < probability:
self._current_trial = prev_trial
# 3. Sample parameters from the neighborhood of the current point.
# The sampled parameters will be used during the next execution of
# the objective function passed to the study.
params = {}
for param_name, param_distribution in search_space.items():
if (
not isinstance(param_distribution, optuna.distributions.FloatDistribution)
or (param_distribution.step is not None and param_distribution.step != 1)
or param_distribution.log
):
msg = (
"Only suggest_float() with `step` `None` or 1.0 and"
" `log` `False` is supported"
)
raise NotImplementedError(msg)
current_value = self._current_trial.params[param_name]
width = (param_distribution.high - param_distribution.low) * 0.1
neighbor_low = max(current_value - width, param_distribution.low)
neighbor_high = min(current_value + width, param_distribution.high)
params[param_name] = self._rng.uniform(neighbor_low, neighbor_high)
return params
# The rest are unrelated to SA algorithm: boilerplate
def infer_relative_search_space(self, study, trial):
return optuna.search_space.intersection_search_space(study.get_trials(deepcopy=False))
def sample_independent(self, study, trial, param_name, param_distribution):
independent_sampler = optuna.samplers.RandomSampler()
return independent_sampler.sample_independent(study, trial, param_name, param_distribution)
注意
为了代码简洁性,上述实现不支持某些特性(例如,最大化)。如果您对如何支持这些特性感兴趣,请参阅 examples/samplers/simulated_annealing.py。
您可以像使用内置采样器一样使用 SimulatedAnnealingSampler
,如下所示:
def objective(trial):
x = trial.suggest_float("x", -10, 10)
y = trial.suggest_float("y", -5, 5)
return x**2 + y
sampler = SimulatedAnnealingSampler()
study = optuna.create_study(sampler=sampler)
study.optimize(objective, n_trials=100)
best_trial = study.best_trial
print("Best value: ", best_trial.value)
print("Parameters that achieve the best value: ", best_trial.params)
Best value: -4.90725931140956
Parameters that achieve the best value: {'x': -0.013583803468734335, 'y': -4.907443831126237}
在此优化中,参数 x
和 y
的值是通过使用 SimulatedAnnealingSampler.sample_relative
方法采样的。
注意
严格来说,在第一次 trial 中,使用 SimulatedAnnealingSampler.sample_independent
方法来采样参数值。因为 SimulatedAnnealingSampler.infer_relative_search_space
中使用的 intersection_search_space()
在没有完整 trial 的情况下无法推断搜索空间。
脚本总运行时间: (0 分钟 0.222 秒)