复杂搜索空间的问题定义

在快速示例中,我们已经展示了如何在搜索空间中定义独立变量。 然而,现实世界中的搜索空间通常很复杂,可能包含空间内条件、禁止条款等。 在本教程中,我们将展示OpenBox如何支持复杂的搜索空间。

分层条件

OpenBox中的搜索空间目前是基于ConfigSpace包构建的。 虽然ConfigSpace支持分层条件,但我们可以直接使用ConfigSpace提供的高级API。

在以下示例中,我们提供了一个使用Conditions构建分层搜索空间的示例:

from openbox import space as sp
from ConfigSpace import EqualsCondition, InCondition

space = sp.Space()
x1 = sp.Categorical("x1", choices=["c1", "c2", "c3", "c4"])
x2 = sp.Real("x2", -5, 10, default_value=0)
x3 = sp.Real("x3", 0, 15, default_value=0)

equal_condition = EqualsCondition(x2, x1, "c1")  # x2 is active when x1 = c1
in_condition = InCondition(x3, x1, ["c2", "c3"])  # x3 is active when x1 = c2 or x1 = c3

space.add_variables([x1, x2, x3])
space.add_conditions([equal_condition, in_condition])

print(space.sample_configuration(5))

示例输出可以如下所示,

[Configuration(values={
  'x1': 'c4',
})
, Configuration(values={
  'x1': 'c1',
  'x2': -4.246561408224157,
})
, Configuration(values={
  'x1': 'c3',
  'x3': 1.7213163807467695,
})
, Configuration(values={
  'x1': 'c3',
  'x3': 13.8469881579991,
})
, Configuration(values={
  'x1': 'c2',
  'x3': 2.9833423891692763,
})
]

在这个例子中,当x1的值为c1时,变量x2是激活的。 当x1的值为c2或c3时,变量x3是激活的。 当x1的值为c4时,x2x3都不激活。 非激活变量的值默认设置为np.nan。 在优化过程中,无效的变量组合不会被采样, 因此不会为无效配置花费评估时间。

通过利用Conditions,用户可以构建具有层次结构的复杂搜索空间。 有关Conditions的更多详细信息,请参阅ConfigSpace文档以获取更多详细信息。

空间变量约束

为了支持变量之间的约束(例如,aa+b<10),我们建议添加一个采样条件,如下例所示,

from openbox import space as sp

def sample_condition(config):
    # require x1 <= x2 and x1 * x2 < 100
    if config['x1'] > config['x2']:
        return False
    if config['x1'] * config['x2'] >= 100:
        return False
    return True
    # return config['x1'] <= config['x2'] and config['x1'] * config['x2'] < 100

cs = sp.ConditionedSpace()
cs.add_variables([...])
cs.set_sample_condition(sample_condition)  # set the sample condition after all variables are added

API set_sample_condition 需要一个函数,该函数以配置作为输入并输出一个布尔值, 该值表示配置是否有效。 在优化过程中,只有满足样本条件(返回值为 True)的配置才会被采样。

请注意,空间变量约束是可以直接检查而不需要运行目标函数的约束,这与约束问题中的黑盒约束不同。