单调性约束
在建模问题或项目中,可接受模型的函数形式常常会受到某种约束。这可能是出于业务考虑,也可能是因为所研究的科学问题类型。在某些情况下,如果对真实关系具有某种性质有非常强烈的先验信念,则可以使用约束来提高模型的预测性能。
在这种情况下,一种常见的约束类型是某些特征与预测响应之间存在**单调**关系
当 \(x \leq x'\) 时,这是一个**递增约束**;或者
当 \(x \leq x'\) 时,这是一个**递减约束**。
XGBoost 能够对增强模型中使用的任何特征施加单调性约束。
一个简单的例子
为了说明这一点,让我们根据以下方案创建一些具有两个特征和一个响应的模拟数据
响应通常随 \(x_1\) 特征的增加而增加,但叠加了一个正弦变化,导致真实效果是非单调的。对于 \(x_2\) 特征,变化是递减的并带有正弦变化。

让我们在不施加任何单调性约束的情况下,将增强树模型拟合到这些数据

黑线显示了从模型推断出的每个特征的趋势。为了绘制这些图,将区分的特征 \(x_i\) 在一维值网格上输入到模型中,而所有其他特征(在这种情况下只有一个其他特征)都设置为它们的平均值。我们看到模型很好地捕捉了带有叠加振荡波的总体趋势。
这是相同的模型,但拟合了单调性约束

我们看到了约束的效果。对于每个变量,趋势的总体方向仍然很明显,但振荡行为不再存在,因为它会违反我们施加的约束。
在 XGBoost 中强制执行单调性约束
在 XGBoost 中强制执行单调性约束非常简单。这里我们将使用 Python 举例,但同样的一般思想也适用于其他平台。
假设以下代码在没有单调性约束的情况下拟合您的模型
model_no_constraints = xgb.train(params, dtrain,
num_boost_round = 1000, evals = evallist,
early_stopping_rounds = 10)
那么,在有单调性约束的情况下拟合只需添加一个参数即可
params_constrained = params.copy()
params_constrained['monotone_constraints'] = (1,-1)
model_with_constraints = xgb.train(params_constrained, dtrain,
num_boost_round = 1000, evals = evallist,
early_stopping_rounds = 10)
在此示例中,训练数据 X
有两列,通过使用参数值 (1,-1)
,我们告诉 XGBoost 对第一个预测器施加递增约束,对第二个预测器施加递减约束。
其他一些例子
(1,0)
:对第一个预测器施加递增约束,对第二个预测器不施加约束。(0,-1)
:对第一个预测器不施加约束,对第二个预测器施加递减约束。
注意
**'hist' 树构建算法注意事项**。如果 tree_method
设置为 hist
或 approx
,启用单调性约束可能会产生不必要的浅层树。这是因为 hist
方法减少了在每个分裂处要考虑的候选分裂数量。单调性约束可能会清除所有可用的分裂候选,在这种情况下不会进行分裂。为了减少这种影响,您可能需要增加 max_bin
参数以考虑更多的分裂候选。
使用特征名称
XGBoost 的 Python 和 R 包支持使用特征名称而不是特征索引来指定约束。给定一个包含列 ["f0", "f1", "f2"]
的数据框,单调性约束可以指定为 {"f0": 1, "f2": -1}
(Python) 或 list(f0=1, f2=-1)
(R,在使用 'xgboost()' 时,但不使用 'xgb.train()' 时),而 "f1"
将默认为 0
(无约束)。