特征交互约束

决策树是发现自变量(特征)之间交互作用的强大工具。在遍历路径中一起出现的变量彼此之间存在交互作用,因为子节点的条件取决于父节点的条件。例如,下图高亮显示的红色路径包含三个变量:\(x_1\)\(x_7\)\(x_{10}\),因此高亮显示的预测(在高亮显示的叶节点处)是 \(x_1\)\(x_7\)\(x_{10}\) 之间交互作用的产物。

../_images/feature_interaction_illustration1.svg

当树的深度大于 1 时,许多变量仅基于最小化训练损失而进行交互,结果决策树可能会捕获虚假关系(噪声),而不是跨不同数据集泛化的合法关系。特征交互约束允许用户决定哪些变量可以交互,哪些变量不能交互。

潜在的好处包括

  • 通过专注于有效的交互作用(无论是通过领域特定知识还是通过对交互作用进行排名的算法),获得更好的预测性能

  • 预测中的噪声更少;更好的泛化能力

  • 用户对模型可以拟合的内容拥有更多控制权。例如,即使某些交互作用表现良好,用户也可能因监管限制而将其排除。

一个简单的例子

特征交互约束以允许交互的变量组表示。例如,约束 [0, 1] 表示变量 \(x_0\)\(x_1\) 允许彼此交互,但不能与其他变量交互。同样,[2, 3, 4] 表示 \(x_2\)\(x_3\)\(x_4\) 允许彼此交互,但不能与其他变量交互。一组特征交互约束表示为嵌套列表,例如 [[0, 1], [2, 3, 4]],其中每个内部列表是允许彼此交互的特征索引组。

在下图中,左侧决策树违反了第一个约束([0, 1]),而右侧决策树符合第一个和第二个约束([0, 1][2, 3, 4])。

fig1

fig2

禁止

允许

在 XGBoost 中强制执行特征交互约束

在 XGBoost 中强制执行特征交互约束非常简单。这里我们将使用 Python 给出示例,但相同的通用思想也适用于其他平台。

假设以下代码在没有特征交互约束的情况下拟合您的模型

model_no_constraints = xgb.train(params, dtrain,
                                 num_boost_round = 1000, evals = evallist,
                                 early_stopping_rounds = 10)

那么使用特征交互约束进行拟合只需要添加一个参数

params_constrained = params.copy()
# Use nested list to define feature interaction constraints
params_constrained['interaction_constraints'] = '[[0, 2], [1, 3, 4], [5, 6]]'
# Features 0 and 2 are allowed to interact with each other but with no other feature
# Features 1, 3, 4 are allowed to interact with one another but with no other feature
# Features 5 and 6 are allowed to interact with each other but with no other feature

model_with_constraints = xgb.train(params_constrained, dtrain,
                                   num_boost_round = 1000, evals = evallist,
                                   early_stopping_rounds = 10)

使用特征名称代替

XGBoost 的 Python 和 R 包支持使用特征名称而不是特征索引来指定约束。给定一个包含列 ["f0", "f1", "f2"] 的数据框,特征交互约束可以指定为 [["f0", "f2"]](Python)或 list(c("f0", "f2"))(R,当将它们传递给函数 xgboost() 时)。

高级主题

交互约束的直觉很简单。用户可能对不同特征之间的关系有先验知识,并在模型构建期间将其编码为约束。但指定约束也存在一些微妙之处。以约束 [[1, 2], [2, 3, 4]] 为例。第二个特征出现在两个不同的交互集 [1, 2][2, 3, 4] 中。因此,允许与 2 交互的特征的并集是 {1, 3, 4}。在下图中,根节点在特征 2 处分裂。因为它的所有后代都应该能够与它交互,所以在第二层,所有 4 个特征都是合法的分裂候选。乍一看,这可能看起来像忽略了指定的约束集,但事实并非如此。

../_images/feature_interaction_illustration4.png

{1, 2, 3, 4} 表示合法的分裂特征集。

这导致了特征交互约束的一些有趣含义。以 [[0, 1], [0, 1, 2], [1, 2]] 为另一个例子。假设我们的训练数据集中只有 3 个可用特征用于演示目的,细心的读者可能已经发现上述约束与 [[0, 1, 2]] 相同。因为无论根节点选择哪个特征进行分裂,其所有后代都允许将每个特征作为合法的分裂候选,而不会违反交互约束。

最后一个例子,我们使用 [[0, 1], [1, 3, 4]] 并选择特征 0 作为根节点的分裂。在构建的树的第二层,1 是除 0 本身之外唯一的合法分裂候选,因为它们属于同一个约束集。按照我们下面示例树的增长路径,第二层的节点在特征 1 处分裂。但由于 1 也属于第二个约束集 [1, 3, 4],因此在第三层,我们允许将所有特征作为分裂候选,并且仍然符合其祖先的交互约束。

../_images/feature_interaction_illustration6.png

{0, 1, 3, 4} 表示合法的分裂特征集。