功能交互约束
决策树是一种强大的工具,用于发现独立变量(特征)之间的相互作用。在遍历路径中一起出现的变量彼此相互作用,因为子节点的条件基于父节点的条件。例如,下图中的红色高亮路径包含三个变量:\(x_1\)、\(x_7\) 和 \(x_{10}\),因此高亮预测(在高亮叶节点处)是 \(x_1\)、\(x_7\) 和 \(x_{10}\) 之间相互作用的结果。
当树的深度大于一时,许多变量仅基于最小化训练损失进行交互,由此产生的决策树可能捕捉到的是虚假关系(噪声),而不是在不同数据集中通用的合法关系。特征交互约束 允许用户决定哪些变量可以交互,哪些不可以。
潜在的好处包括:
通过专注于有效的交互来获得更好的预测性能——无论是通过领域特定知识还是排序交互的算法
预测中的噪声更少;更好的泛化能力
用户对模型拟合内容的更多控制。例如,用户可能希望排除某些表现良好的交互,因为受到监管限制。
一个简单的例子
特征交互约束是根据允许交互的变量组来表示的。例如,约束 [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]
)。
禁止 |
允许 |
在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个特征在第二层都是合法的分裂候选者。乍一看,这可能看起来像是忽略了指定的约束集,但实际上并非如此。
这导致了一些有趣的特征交互约束的含义。以 [[0, 1], [0, 1, 2], [1, 2]]
为例。假设我们为了演示目的,在我们的训练数据集中只有3个可用特征,细心的读者可能已经发现,上述约束实际上与简单的 [[0, 1, 2]]
相同。因为在根节点中无论选择哪个特征进行分割,其所有后代节点都允许包含每个特征作为合法的分割候选,而不会违反交互约束。
最后一个例子,我们使用 [[0, 1], [1, 3, 4]]
并选择特征 0
作为根节点的分割。在构建的树的第二层,除了 0
本身之外,1
是唯一的合法分割候选,因为它们属于同一个约束集。按照我们示例树的生长路径,第二层的节点在特征 1
处进行分割。但由于 1
也属于第二个约束集 [1, 3, 4]
,在第三层,我们允许包含所有特征作为分割候选,并且仍然遵守其祖先的交互约束。