功能交互约束

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

../_images/feature_interaction_illustration1.svg

当树的深度大于一时,许多变量仅基于最小化训练损失进行交互,由此产生的决策树可能捕捉到的是虚假关系(噪声),而不是在不同数据集中通用的合法关系。特征交互约束 允许用户决定哪些变量可以交互,哪些不可以。

潜在的好处包括:

  • 通过专注于有效的交互来获得更好的预测性能——无论是通过领域特定知识还是排序交互的算法

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

  • 用户对模型拟合内容的更多控制。例如,用户可能希望排除某些表现良好的交互,因为受到监管限制。

一个简单的例子

特征交互约束是根据允许交互的变量组来表示的。例如,约束 [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 包支持使用特征名称而不是特征索引来指定约束。给定一个包含列 ["f0", "f1", "f2"] 的数据框,特征交互约束可以指定为 [["f0", "f2"]]

高级主题

交互约束背后的直觉很简单。用户可能对不同特征之间的关系有先验知识,并在模型构建过程中将其编码为约束。但在指定约束时也有一些微妙之处。以约束 [[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 作为根节点的分割。在构建的树的第二层,除了 0 本身之外,1 是唯一的合法分割候选,因为它们属于同一个约束集。按照我们示例树的生长路径,第二层的节点在特征 1 处进行分割。但由于 1 也属于第二个约束集 [1, 3, 4],在第三层,我们允许包含所有特征作为分割候选,并且仍然遵守其祖先的交互约束。

../_images/feature_interaction_illustration6.png

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