对于XGBoost来说,默认的超参数确实能让它跑起来,但要是想追求最佳效果,那就得针对自己的数据手动调一调了。下面这几个参数,在XGBoost里算是重中之重:
etanum_boost_roundmax_depthsubsamplecolsample_bytreegammamin_child_weightlambdaalpha
XGBoost的API有两种调用方式:一种是咱们最常见的原生API,另一种是兼容Scikit-learn API的版本,后者能和Sklearn生态系统无缝集成。这里我们主要关注原生API,不过下面这张图可以帮助你对照这两个API的参数,万一以后用得上呢:

如果打算用Optuna以外的工具来做超参数调优,这张表也能参考一下。这些参数之间是怎么相互影响的?看下面这张图就清楚了:

这些关系不是绝对的,但大致情况就是上图这样。因为还有一些其他参数可能会对我们提到的这10个参数产生额外的影响。
1、objective
这是模型的训练目标。

简单来说,这个参数指定了模型要干什么活——它会影响决策树的种类和损失函数。
2、num_boost_round - n_estimators
num_boost_round 指定了训练过程中要生成多少棵决策树(在XGBoost里通常称为基础学习器)。默认值是100,但对于今天的大型数据集来说,这点树远远不够。
增加这个参数会生成更多的树,但随着模型变得更复杂,过拟合的风险也会显著上升。
从Kaggle那里学到一个技巧:把 num_boost_round 设成一个很大的数,比如100,000,然后利用早停机制来获得最佳版本。
在每个提升轮次中,XGBoost都会生成新的决策树,用来修正前面所有树的整体得分——这就是它为什么叫“提升”的原因。这个过程会一直持续到跑完 num_boost_round 轮,无论性能有没有提升。
但有了早停技术,当验证指标不再提高时,训练就会自动停止。这样既节省了时间,也能防止过拟合。
有了这个技巧,我们甚至都不需要专门去调 num_boost_round 了。代码实现起来大概是这个样子:
# Define the rest of the params params = {...} # Build the train/validation sets dtrain_final = xgb.DMatrix(X_train, label=y_train) dvalid_final = xgb.DMatrix(X_valid, label=y_valid) bst_final = xgb.train(
params,
dtrain_final,
num_boost_round=100000 # 设一个大数
evals=[(dvalid_final, "validation")],
early_stopping_rounds=50, # 启用早停
verbose_eval=False,
)
上面的代码让XGBoost生成10万棵决策树,但由于启用了早停,一旦验证分数在最后50轮中没有改善,训练就会停止。一般情况下,树的数量落在5000到10000之间就够了。控制 num_boost_round 也是影响训练时间最大的因素之一,毕竟更多的树意味着消耗更多的资源。
3、eta - learning_rate
在每一轮中,所有已有的树都会对给定的输入返回一个预测值。举个例子,五棵树对样本N的预测可能是这样的:
Tree 1: 0.57
Tree 2: 0.9
Tree 3: 4.25
Tree 4: 6.4
Tree 5: 2.1
为了得到最终的预测,需要把这些输出累加起来。但在那之前,XGBoost会用一个称为 eta 或学习率的参数对这些输出进行缩放。缩放后的最终输出是这样的:
output = eta * (0.57 + 0.9 + 4.25 + 6.4 + 2.1)
学习率设得大,就给集成中每棵树的贡献赋予了更大的权重,但这可能导致过拟合或不稳定,同时也会加快训练。而较低的学习率则会抑制每棵树的贡献,让学习过程更慢但更稳健。这种正则化效果对复杂和有噪声的数据集尤其有帮助。
学习率与 num_boost_round、max_depth、subsample 和 colsample_bytree 等其他参数呈反比关系。较低的学习率需要这些参数取较高的值,反之亦然。不过一般来说,我们不用太担心这些参数之间的相互作用,因为自动调优会帮我们找到最佳组合。
4、subsample和colsample_bytree
子抽样(subsample)能给训练过程引入更多的随机性,从而有助于对抗过拟合。
subsample = 0.7 意味着集成中的每棵决策树都会在随机选择的70%的可用数据上进行训练。值为1.0则表示使用全部数据(不进行子抽样)。
与 subsample 类似的是 colsample_bytree。顾名思义,它控制每棵决策树会使用多大比例的特征。colsample_bytree = 0.8 意味着每棵树会随机使用80%的可用特征(列)。
调整这两个参数可以控制偏差和方差之间的权衡。取较小的值能降低树之间的相关性,增加集成中的多样性,有助于提升泛化能力、减少过拟合。
但也有可能引入更多噪声,增加模型的偏差。而取较大的值则会提高树之间的相关性,降低多样性,可能导致过拟合。
5、max_depth
最大深度 max_depth 控制着决策树在训练过程中最多能长到多少层。

更深的树能够捕捉特征之间更复杂的相互作用。但过深的树也有更高的过拟合风险,因为它们可能会记住训练数据中的噪声或不相关的模式。为了控制这种复杂性,可以通过限制 max_depth 来生成更浅、更简单的树,让它们去捕获更通用的模式。
max_depth 的数值需要在复杂性和泛化能力之间找到一个好的平衡点。
6、7、alpha, lambda
这两个参数放在一起说,因为 alpha(L1)和 lambda(L2)都是用来帮助对抗过拟合的正则化参数。
它们与其他正则化参数的区别在于,它们可以把不重要或不重要的特征的权重缩小到0(尤其是 alpha),从而得到一个特征更少、复杂度更低的模型。
此外,alpha 和 lambda 的效果可能会受到 max_depth、subsample 和 colsample_bytree 等其他参数的影响。更高的 alpha 或 lambda 值可能需要调整其他参数来补偿增加的正则化。例如,较高的 alpha 值可能受益于较大的 subsample 值,这样可以保持模型多样性并防止欠拟合。
8、gamma
如果你读过XGBoost的官方文档,会发现它对 gamma 的解释是:
“在树的叶节点上进行进一步分区所需的最小损失减少。”
英文原文是:the minimum loss reduction required to make a further partition on a leaf node of the tree.
这句话除了写它的人,恐怕其他人都不太容易看懂。我们来拆解一下。下面是一个两层决策树:

为了证明通过分裂叶节点来给树增加更多层是合理的,XGBoost需要先算出这个操作能显著降低损失函数。
但“显著”到底是多少呢?gamma 就是扮演这个阈值角色的——它决定了是否要对一个叶节点做进一步的分割。
如果潜在分裂后损失函数的减少(通常称为增益)小于设定的 gamma 值,那么这个分裂就不会执行。也就是说,叶节点保持不变,树也不会从那个点继续生长。
所以,调优的目标是找到那个能带来最大损失函数减少的最佳分割——这才意味着模型性能的改善。
9、min_child_weight
XGBoost的初始训练过程是从一棵只有单个根节点的决策树开始的,这个节点包含了所有的训练实例(行)。然后,随着XGBoost选择潜在的特征和分裂标准来最小化损失,更深的节点就会包含越来越少的实例。

如果让XGBoost随意生长,树可能会长到最后的节点里只剩下少数几个无关紧要的实例。这显然是不可取的,因为这正是过拟合的表现。
所以,XGBoost为每个节点中继续分裂所需的最小实例数设置了一个阈值。通过对节点中的所有实例进行加权,并计算权重的总和,如果这个最终权重小于 min_child_weight,那么分裂就会停止,该节点成为叶节点。
上面这个解释是对整个过程最简化的版本,因为我们的重点在于讲清楚它的概念。
总结
以上就是对这10个重要超参数的解释。如果想深入了解,还有不少东西值得继续钻研。所以,给ChatGPT下面这两个提示,或许能帮你获得更透彻的理解:
1) Explain the {parameter_name} XGBoost parameter in detail and how to choose values for it wisely.
2) Describe how {parameter_name} fits into the step-by-step tree-building process of XGBoost.
肯定比啃官方文档来得直观,对吧。
