news 2026/9/16 1:03:21

集成学习完全指南:从Bagging到XGBoost、LightGBM与CatBoost

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
集成学习完全指南:从Bagging到XGBoost、LightGBM与CatBoost

做竞赛、搞建模或者调模型调到头秃的朋友,一定绕不开集成学习。我在实际项目里试过单一模型死磕到极致,最后评分纹丝不动,反而是一顿 bagging、boosting 组合拳下去,线上指标直接涨了一截。这篇总结想把我对集成学习从原理到代码的理解完整梳理一遍,覆盖 bagging、随机森林、AdaBoost、GBDT、XGBoost、LightGBM 和 CatBoost,把每个算法的动机、数学直觉、关键参数和真实代码一次性讲清楚。

这篇文章适合正在入门机器学习、准备算法面试、或者已经在用 sklearn/xgboost 但不知道背后在发生什么的人。我不打算讲成教科书,尽量用实际场景和踩坑经历来说话。毕竟集成学习的核心思想其实特别朴素:一个模型拿不准的事,多叫几个模型投票或者接力,整体就能稳很多。但“多叫几个”这件事,背后有门道。

1. 集成学习的设计思路与算法谱系

集成学习之所以有效,根子在于“偏差-方差”这对矛盾。单一决策树如果长得太深,过拟合风险高,方差大;如果长得太浅,拟合能力不够,偏差大。单模型要么偏科,要么不稳定,而集成学习就是通过组合多个基学习器来同时压低偏差和方差,或者在不同维度上做补偿。

1.1 从偏差-方差分解看两个流派

偏差描述模型预测值和真实值的差距,方差描述模型在训练集变动时的波动程度。Bagging 流派的核心思路是降低方差:对训练数据进行有放回采样,训练出多个独立的模型,最后取平均或投票。即使每个模型方差大,只要它们之间的误差相关性较低,平均之后方差就会显著下降。

Boosting 流派则把重心放在降低偏差上:逐个训练模型,每个新模型重点学习前面模型没做对的部分,最后把所有模型加权组合起来。这类方法能一步步把拟合能力撑上去,但方差控制不如 bagging 自然,所以实际使用时常搭配正则化、学习率、早停等手段来抑制过拟合。

1.2 基学习器与样本权重的配合

不管是 bagging 还是 boosting,基学习器最常用的都是决策树。原因是决策树对特征尺度不敏感,能够自动处理非线性关系,而且可以很方便地扩展成随机森林或提升树。逻辑回归、KNN 这类模型也可以做基学习器,但实际工程里很少见,因为集成带来的收益没有树模型明显。

从算法谱系上梳理一下:bagging 的典型代表是随机森林,boosting 的典型代表是 AdaBoost、GBDT、XGBoost、LightGBM、CatBoost。前者的关键词是“并行训练,平均结果”,后者的关键词是“串行迭代,逐步纠错”。理解了这条主线,后面所有细节都能挂上去。

1.3 为什么集成模型几乎成了默认选择

在表格型数据上,集成树模型的表现往往优于精心调参的单一深度学习模型。这不是玄学,而是因为树模型天然擅长处理表格数据中的特征交互和缺失模式,而集成机制又弥补了单棵树的稳定性不足。

我在实际项目里的体感是:先用随机森林快速建立基线,然后上 LightGBM 或 XGBoost 精调,大多数情况下分数就能达到预期。偶尔遇到类别特征特别多或者特征分布特别嘈杂的数据,CatBoost 会表现得更好。这套流程已经成为团队的标准操作方案。

2. Bagging 与随机森林:并行集成的基石

Bagging 这个名字来自 Bootstrap Aggregating。Bootstrap 是有放回抽样,Aggregating 是聚合。它的做法说起来很简单:从原始训练集里随机有放回地抽取多个子集,每个子集训练一个模型,最终预测时回归取平均、分类取投票。

2.1 Bagging 为什么能降低方差

有放回抽样带来的效果是,每个子集和原始数据集有一定重叠,但又不完全相同。这样得到的模型之间存在差异,而差异正是降低方差的前提。数学上可以证明,如果 N 个独立同分布的随机变量方差是 σ²,它们的平均值方差就是 σ²/N。虽然基模型之间并不完全独立,但只要有相关性小于 1,取平均就能让整体方差下降。

这一结论的直觉是:每个模型都会犯不同的错,把它们的错误平均掉一部分,剩下的就是共识部分。实际使用中,Bagging 对高方差模型(比如深决策树、KNN)的提升最显著,对低方差模型(比如线性模型)提升不明显。

2.2 随机森林的“双重随机性”

随机森林在 bagging 的基础上加了一个关键改动:每次分裂时不是搜索所有特征,而是随机抽取一部分特征作为候选。这个做法让模型之间的差异进一步加大,也间接降低树之间的相关性,方差下降的效果更好。

用我自己的话说就是:bagging 只是让每棵树看到不同的样本,随机森林还让每棵树看到不同的特征视角。这也是为什么随机森林比单纯的 bagging 决策树效果更稳定。

随机森林回归算法的标准流程可以概括为:

  1. 从训练集中有放回抽样,生成 n 个样本子集。
  2. 对每个子集训练一棵决策树,每次分裂时随机选取 m 个特征中最优的一个。
  3. 重复训练 n_estimators 棵树。
  4. 回归预测时对每棵树的输出取平均,分类时采取投票或按概率平均。

决策树和随机森林的关系可以这样理解:决策树是砖头,随机森林是盖好的墙。单看一块砖可能不够结实,但很多砖按规则垒起来,墙就稳了。

2.3 随机森林回归与分类代码实战

先用 sklearn 展示随机森林回归模型的基本用法,我用的示例数据集是经典的波士顿房价替代方案(sklearn 已经移除波士顿数据集,用加州房价数据来演示)。

from sklearn.datasets import fetch_california_housing from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_squared_error data = fetch_california_housing() X_train, X_test, y_train, y_test = train_test_split( data.data, data.target, test_size=0.2, random_state=42 ) rf = RandomForestRegressor( n_estimators=200, max_depth=12, min_samples_leaf=4, max_features=0.7, n_jobs=-1, random_state=42, ) rf.fit(X_train, y_train) pred = rf.predict(X_test) print("RMSE:", mean_squared_error(y_test, pred, squared=False))

这里几个参数是实际调参时的重点。n_estimators太小时方差压不下来,一般 100 到 500 之间比较常见,超过一定数量收益就递减了。max_features控制每次分裂考虑的特征数,回归里常用特征总数的三分之一左右,分类里常用根号特征数。min_samples_leaf则控制叶子节点最少样本数,设得大一点能明显抑制过拟合,但也不能太大,否则欠拟合。

我对随机森林最常用的一句评价是:它是个几乎没有太多玄学参数的模型,默认参数下就有不错表现,特别适合作为项目基线。但它也有明显的短板——对噪声特征敏感,对特征含义的解释性远不如单棵决策树。如果项目要求很强的可解释性,就需要权衡。

2.4 Bagging 的一种手写最小实现

学术上理解更透彻的方法是自己手写一遍 bagging 流程。下面我用 numpy 和 sklearn 的决策树封装一个最小版本,帮你直观感受“真有放回采样”的过程。

import numpy as np from sklearn.tree import DecisionTreeRegressor class SimpleBaggingRegressor: def __init__(self, n_estimators=50, max_samples=0.8, max_depth=6): self.n_estimators = n_estimators self.max_samples = max_samples self.max_depth = max_depth self.trees = [] def fit(self, X, y): n = len(X) sample_size = int(n * self.max_samples) for _ in range(self.n_estimators): idx = np.random.choice(n, sample_size, replace=True) tree = DecisionTreeRegressor(max_depth=self.max_depth) tree.fit(X[idx], y[idx]) self.trees.append(tree) def predict(self, X): preds = np.mean([tree.predict(X) for tree in self.trees], axis=0) return preds

这个实现和 sklearn 的 BaggingRegressor 差了很远,但核心思路完全一致。你把这个类放到实验里跑一下就能看到,即便每个决策树都长得比较浅,bagging 之后的整体效果依然稳定。这就是并行集成最朴素的力量。

3. AdaBoost 到 GBDT:提升方法的进化路径

如果说 bagging 是靠“组合”来降低方差,那 boosting 就是靠“纠错”来降低偏差。boosting 每次迭代都会重点关注前一轮分类错误的样本,逐步把整体预测边界修得更好。

3.1 AdaBoost 的样本权重迭代

AdaBoost 的想法很直观:每一轮训练结束后,增加被分错样本的权重,减少被分对样本的权重,然后带着这套权重继续训练下一个弱学习器。这样每个新学习器都会把注意力集中在之前搞不定的那种样本上。最终预测时,每个学习器按照其分类精度进行加权投票。

用带权重的逻辑来理解:分类精度高的弱学习器,在最终投票里嗓门更大;分类精度很低的,即使没被淘汰,话语权也小。

我早期学 AdaBoost 的时候一直有一个疑问:弱学习器怎么接收权重?答案在于不同框架的处理方式不同。sklearn 里的 AdaBoostClassifier 会直接让基学习器的 fit 方法接收 sample_weight 参数,而在理论推导里,权重是通过重采样来体现的。

下面是 sklearn 的 AdaBoost 分类示例:

from sklearn.ensemble import AdaBoostClassifier from sklearn.tree import DecisionTreeClassifier from sklearn.datasets import make_classification from sklearn.model_selection import cross_val_score X, y = make_classification( n_samples=1000, n_features=20, n_informative=15, random_state=42, ) base = DecisionTreeClassifier(max_depth=1) ada = AdaBoostClassifier( estimator=base, n_estimators=200, learning_rate=0.5, random_state=42, ) scores = cross_val_score(ada, X, y, cv=5, scoring="accuracy") print("CV Acc:", scores.mean())

树深设为 1 的决策树也叫“树桩”,只做一个特征的判断,属于典型的弱学习器。有人会问,既然每棵树都这么弱,组合起来怎么会强?答案就在 sample_weight 的迭代策略里:每一轮都在补短板,补到最后,边界就被修得很精细了。

3.2 GBDT 如何用负梯度定义“错误”

GBDT 的全称是 Gradient Boosting Decision Tree。它把 boosting 思想推广到了任意可微损失函数上。核心思路是:每一轮训练一棵决策树,去拟合当前损失函数的负梯度方向。在平方损失场景下,负梯度刚好等于残差;在其他损失函数下,负梯度是残差的广义化。

这个思想的精妙之处在于:我们不需要为每个损失函数去设计专门算法,只需要算出负梯度,后续的树拟合流程完全一样。因此 GBDT 能统一处理回归、二分类、多分类、排序等任务,只要损失函数可导。

GBDT 的训练过程可以概括为:

  1. 用一个常数或平均值初始化模型。
  2. 计算当前模型在训练样本上的负梯度。
  3. 用一棵决策树拟合这个负梯度,构造出树结构。
  4. 沿着树的输出方向更新模型,通常乘以学习率。
  5. 重复步骤 2 到 4,直到达到预设的树数量或验证集指标不再提升。

3.3 手写一个最小 GBDT 回归来理解残差拟合

为了说明 GBDT 的本质,我写过一个最简版本的梯度提升回归器。它没有做复杂的剪枝,仅仅演示“拟合残差”这个过程。

import numpy as np from sklearn.tree import DecisionTreeRegressor class SimpleGBRT: def __init__(self, n_estimators=50, learning_rate=0.1, max_depth=2): self.n_estimators = n_estimators self.learning_rate = learning_rate self.trees = [] self.base_pred = None def fit(self, X, y): self.base_pred = np.mean(y) current = np.full(len(y), self.base_pred) for _ in range(self.n_estimators): residual = y - current tree = DecisionTreeRegressor(max_depth=self.max_depth) tree.fit(X, residual) self.trees.append(tree) current += self.learning_rate * tree.predict(X) def predict(self, X): pred = np.full(len(X), self.base_pred) for tree in self.trees: pred += self.learning_rate * tree.predict(X) return pred

注意这里每棵树拟合的是y - current,也就是残差。对平方损失来说,这就是负梯度。如果你把这个类在带噪声的曲线数据上跑一下,会发现 n_estimators 越大、学习率越小,拟合曲线的细腻程度越高。这也解释了 GBDT 需要同时控制学习率和树数量:树太多而且学习率太高,很容易过拟合。

实际上 sklearn 的GradientBoostingRegressor就是在这个思想上做了大量优化,比如对每棵树乘收缩系数、子采样、分子区域用线性规划求最优输出值等。你理解了最小版本,再去看官方文档里的参数,就全都能对上了。

4. XGBoost、LightGBM、CatBoost:三大梯度提升框架的对比与实战

到了工程应用阶段,我们一般不再自己去写方案,而是直接使用高度优化的框架。XGBoost、LightGBM、CatBoost 都属于 GBDT 的工程化变体,但在优化策略和适用场景上有明显差异。

4.1 XGBoost:二阶导加正则化的工程范本

XGBoost 对 GBDT 最重要的改进是加入了正则项,并且对损失函数做二阶泰勒展开。一阶导数告诉我们该往哪个方向走,二阶导数还告诉我们应该用多大胆的步子。这个信息让每棵树的叶子节点输出值可以直接用解析方式求解,整体收敛路径更稳健。

XGBoost 在搜索最优分裂点时,使用的是预排序加加权分位数近似。简单来说,它会对特征值进行排序,缓存梯度统计量,然后寻找最大增益的分裂点。这个过程准确度高,但排序的代价也高,所以 XGBoost 的训练速度通常比 LightGBM 慢一些。

XGBoost 很著名的功能是它自带缺失值处理:在寻找分裂节点时,如果样本特征缺失,模型会自动学习缺失值应该往左还是往右走。这比手动填充均值要精细得多。

我在实际项目里发现,xgboost 会处理空值这件事非常实用。传统处理流程要先做缺失值填充,如果填充策略不恰当,反而引入噪声。XGBoost 的稀疏感知算法会自动为缺失值学一个最优的方向,这也是为什么同样的数据直接丢给 XGBoost 往往不会太差。

下面是一个 xgboost 二分类模型的完整示例,包括早停设置和特征重要性查看:

import xgboost as xgb from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.metrics import roc_auc_score X, y = make_classification( n_samples=2000, n_features=30, n_informative=20, n_redundant=5, random_state=42, ) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) dtrain = xgb.DMatrix(X_train, label=y_train) dtest = xgb.DMatrix(X_test, label=y_test) params = { "objective": "binary:logistic", "eval_metric": "auc", "eta": 0.05, "max_depth": 5, "subsample": 0.8, "colsample_bytree": 0.7, "lambda": 1.0, } bst = xgb.train( params, dtrain, num_boost_round=1000, evals=[(dtest, "test")], early_stopping_rounds=50, verbose_eval=50, ) pred = bst.predict(dtest) print("AUC:", roc_auc_score(y_test, pred)) print("Feature importance:", bst.get_score(importance_type="gain")[:5])

这里eta就是学习率,它和num_boost_round是一对组合:学习率设小一点,训练轮数就要相应加大。colsample_bytree控制每棵树随机选取的特征比例,是 XGBoost 里降低方差的重要参数。lambda是 L2 正则化系数,用于控制叶子节点权重衰减。

xgb 可以做 nonlinear feature transformation,指的是通过对特征分裂点进行自动分段,让目标变量和特征的复杂非线性关系被捕捉。这种“非线性特征变换”能力来自树模型天然的特征交互学习,不需要我们手动去做多项式特征或分箱特征。换个角度来说,树模型本身就是一种可学习的特征变换器。

4.2 LightGBM:直方图加速和 Leaf-wise 生长

LightGBM 和 XGBoost 最大的区别在于分裂点的搜索策略。LightGBM 使用直方图算法,把连续特征离散化成了固定数量的桶,在桶级别上统计梯度信息,大幅减少了计算量。同时它用互斥特征绑定技术合并稀疏特征,进一步降低内存占用。

另外一个显著差异是树的生长方式。XGBoost 一般按层生长(Level-wise),而 LightGBM 采用叶子生长(Leaf-wise):每一轮从当前所有叶子节点中选择分裂增益最大的那个叶子来分裂。这样做的好处是可以在相同树深度下获得更好的拟合效果,缺点是如果不限制叶子数量和树深度,很容易过拟合。

所以 LightGBM 提供了两个非常关键的参数:num_leavesmin_data_in_leaf。实际调参时,不是先去调学习率,而是先把num_leaves控制在一个合理范围,比如 31 到 127 之间,然后设置min_data_in_leaf来防止过小叶子继续分裂。

LightGBM 的 rank 和 xendcg 也是竞赛里经常被提到的问题。LightGBM 支持排序任务,目标函数是 LambdaRank,评价指标常用 ndcg,对应名字里有 xendcg 相关度量。做搜索排序、推荐排序任务时,LightGBM Ranker 的用法和分类回归略有不同:

import lightgbm as lgb import numpy as np from sklearn.datasets import make_regression X, y = make_regression(n_samples=500, n_features=10, random_state=42) groups = [100] * 5 lgb_train = lgb.Dataset(X, label=y, group=groups) params = { "objective": "lambdarank", "metric": "ndcg", "ndcg_eval_at": [1, 3, 5], "learning_rate": 0.05, "num_leaves": 31, "min_data_in_leaf": 20, } model = lgb.train( params, lgb_train, num_boost_round=100, )

这里group指定每个查询下有多少条样本,排序模型的输入数据必须是按 query 分组的。训练完成后的预测结果是一个分数,按分数排序得到最终列表。

LightGBM 保存到本地 txt 的模型文件也是实际工作中绕不开的问题。模型本质是一堆文本格式的树结构,你可以直接用model.save_model("model.txt")保存,再用model = lgb.Booster(model_file="model.txt")加载。这个 txt 文件里能直观看到每棵树的树结构,比如分裂特征、分裂阈值和叶子节点输出值。

有人问我 C# 调用 Python 生成的 lightgbm 的 txt 模型这个问题。可行的做法是:用 Python 端把模型保存为 txt 或 JSON,然后 C# 项目中通过 LightGBM 的 C API 加载这个模型文件,或者使用 LightGBMSharp 这类封装库。关键是模型文件格式是跨语言的,不依赖 Python 运行时,这也是训练和推理解耦的基础。我补充一个 Python 保存模型再检查树个数的示例:

import lightgbm as lgb model = lgb.LGBMRegressor(n_estimators=100, num_leaves=31) model.fit(X_train, y_train) model.booster_.save_model("lgb_model.txt") loaded = lgb.Booster(model_file="lgb_model.txt") # 查看树个数 print("Tree count:", loaded.num_trees())

可能有人担心保存成 txt 文件会丢失某些信息。其实不会,save_model会完整保存树结构、目标函数、特征名称等关键信息,加载后可以直接用于预测。之所以默认后缀是 txt,正是因为它是文本格式,方便跨平台使用和手动查看。

4.3 CatBoost:有序提升与类别特征内建处理

CatBoost 最招牌的能力是自动处理分类特征。你不需要手动做 one-hot 编码,只要在训练时传入cat_features参数,它内部会基于目标统计和有序提升原则对类别特征做编码,减少信息损失。

另一个特点是它采用对称树结构,每棵树的每一层使用相同的分裂特征和分裂阈值。这个限制看似会让单棵树表达能力下降,但配合有序提升后,整体模型的泛化性能往往更好,而且在推理时可以做大幅度的加速和向量化。

CatBoost 的有序提升针对的是 GBDT 里的一个隐藏问题:在每一轮用当前模型打预测时,训练样本的预测值往往已经“偷看”了自身标签,导致梯度估计产生偏移。CatBoost 通过随机置换样本顺序,用序列中前一部分样本对后续样本做预测,从而减少这种偏移。

我用 CatBoost 处理含大量类别特征的数据集时,体感确实比手动编码更省心,泛化也更好。它在金融风控、用户行为预测这类场景里表现很亮眼。下面是一个带类别特征的示例:

from catboost import CatBoostClassifier import pandas as pd df = pd.DataFrame({ "feature_num": [1.0, 2.0, 3.0, 4.0, 5.0], "feature_cat": ["A", "B", "A", "C", "B"], "label": [0, 1, 0, 1, 1], }) X = df.drop(columns="label") y = df["label"] model = CatBoostClassifier( iterations=100, learning_rate=0.1, depth=6, cat_features=["feature_cat"], verbose=50, ) model.fit(X, y)

注意cat_features需要传入类别特征的列名或索引。CatBoost 还有一个特点是对 GPU 训练支持友好,在数据集较大的情况下可以明显提升训练速度。

四大框架横向对比如下:

框架分裂查找方式树的生长方式类别特征处理缺失值处理核心优势
XGBoost预排序+分位数近似Level-wise需手动编码自动学习路径精度高、文档丰富、生态成熟
LightGBM直方图Leaf-wise需手动编码自动处理训练快、内存低、支持排序
CatBoost预排序+目标编码对称树内建最优处理自动处理类别特征效果极佳、泛化稳定
随机森林CART 分裂全部叶节点并行需手动编码需预处理快速基线、方差低

4.4 三类框架的统一使用范式

无论是 XGBoost、LightGBM 还是 CatBoost,其实都遵循同一套训练范式:构建 Dataset,设置参数,训练,评估,预测,调参。掌握一个之后切换到另一个,成本非常低。技术上的差异主要体现在训练速度和预测性能上。

它们的相同点更多:

  1. 都是 GBDT 的改进版,存在“学习率、树数量、树复杂度”这一组核心超参。
  2. 都支持早停,用验证集指标控制训练轮数。
  3. 都输出特征重要性,可以用于特征选择。
  4. 都支持 GPU 训练,在大数据集上能够明显缩短实验周期。

落到实处,你在项目里的选择逻辑可以很简单:

  • 想要快速验证的时候先上随机森林,作为 baseline。
  • 追求最优精度,且特征数量中等、数据量不是特别大的时候,可以试 XGBoost。
  • 数据量很大且特征很多,优先 LightGBM。
  • 数据里有很多高基数类别特征,并且不想做繁琐编码时,直接用 CatBoost。

5. 常见问题与排查技巧实录

写了这么多年代码,训练了无数个集成模型,下面这些问题是我实际踩过的坑。整理成速查表,直接参考。

5.1 为什么 xgboost 会处理空值,但我手动填充效果反而更差

XGBoost 的空值处理思路是在分裂时自动学两个方向:缺失值样本究竟去左子树还是右子树,以损失函数最小为准。手动填充均值或中位数时,等于帮模型做了一个不够好的决定,限制了模型的灵活性。所以如果你的数据缺失机制比较复杂,先用 XGBoost 默认方案跑一版,看过效果后再考虑要不要手动填充。

5.2 LightGBM 的叶子生长策略为什么容易过拟合

Leaf-wise 生长在每一轮都选增益最大的叶子分裂,训练误差可以降得特别快,但同时也更容易抓住局部噪声。解决办法很直接:限制num_leaves,同时设置min_data_in_leaf比如 20 以上。另外一个常用技巧是调低bagging_fractionfeature_fraction,增加随机性,防止模型过度记忆训练集。

5.3 LightGBM 保存到本地的 txt 有什么特点

txt 模型文件可以直接打开查看树结构,方便排查“模型是否学习了异常分裂点”这类问题。它也可以被 C API 或其他语言加载,因此你在 Python 里训练好的模型,完全可以在服务端用 C# 加载预测。需要强调的是,跨语言加载时保持特征列顺序一致非常关键,否则预测结果就会难以理解。

5.4 XGBoost 的非线性特征变换能力如何落地

树模型会把连续特征自动离散化成一系列分裂点,每个分裂点对应一个条件判断,因此相当于自动构造了一组非线性转换后的特征。如果后续要把树模型和线性模型做融合,可以用tree_predict得到每棵树对样本的叶节点位置,转成 one-hot 特征后传给线性模型,这种方式在 CTR 预估中经常用。

5.5 随机森林回归和 XGBoost 回归如何选择

随机森林回归的优势是训练快、无需太多调参、不容易过拟合,适合作为基线。XGBoost 回归的优势是有学习率控制、有正则项、可以早停,能够利用多轮迭代逼近更复杂的函数。建议先用随机森林建立预测值范围,再切换 XGBoost 精调,观察验证集指标是否真正改善。

5.6 决策树和随机森林里的“随机”到底有什么区别

决策树分裂时会在全部特征中选出最优分裂点,随机森林则只在随机抽取的特征子集里找最优。这个差异会直接降低模型方差,也是随机森林在保持较高准确率的同时不那么容易过拟合的原因。

5.7 如何用 LightGBM 做排序任务(rank 和 xendcg)

LightGBM 的排序目标函数是 lambdarank,评价指标里 ndcg 是更常用的指标。使用lgb.Dataset(..., group=...)时必须保证样本按 query 分组连续存放。ndcg_eval_at可以配置要看前几名,例如 [1, 3, 5] 表示分别计算 NDCG@1、NDCG@3、NDCG@5。训练完成后输出的是分数,按分数降序排列得到最终排序结果。

5.8 模型调参顺序有没有通用路径

我常用的一套路径如下:

  1. 学习率先定在 0.1,树数量通过早停自动决定。
  2. 调树的复杂度参数,如max_depthnum_leavesmin_data_in_leaf
  3. 调采样比例,如subsamplecolsample_bytreebagging_fractionfeature_fraction
  4. 最后调正则化参数,如 XGBoost 的lambdaalpha,LightGBM 的lambda_l1lambda_l2
  5. 验证稳定后,再把学习率降到 0.01,增大迭代轮数做一轮精细训练。

这个顺序的背后逻辑是:先粗调结构复杂度,再引入随机性,最后用正则化收尾。如果一开始就调正则化参数,很难判断模型当前是欠拟合还是过拟合。

5.9 特征重要性怎么解读才不踩坑

XGBoost 的get_score(importance_type="gain")返回的是每个特征作为分裂节点带来的平均增益,数值越大说明该特征对损失下降贡献越大。LightGBM 的feature_importance(importance_type="gain")逻辑类似。但注意:只要某个特征被用于分裂,其重要性通常都很高,哪怕它只是和另一个重要特征高度相关。所以特征重要性更适合做筛查,不适合直接做因果判断。

5.10 树个数设置到多少才算合适

树个数取决于学习率和问题复杂度。通常做法不是拍脑袋设一个固定值,而是设置比较大的num_boost_round,配合验证集做早停。如果早停轮数是 50,意思是验证集指标连续 50 轮没有提升就停止训练,这样树个数就是一个数据自适应结果,而不是人为设定的随机数。

最后想说的实际经验

我在试这些集成算法时踩过不少坑,比如一开始调 LightGBM 的num_leaves调到几百,训练误差确实很低,但线上测试回归很严重;又比如一开始在类别特征上手动做 one-hot,导致高基数特征爆炸,后来换 CatBoost 省了非常多事。调试到后面,你会发现集成学习不是“哪个算法更先进”的问题,而是“哪个算法更匹配数据形态”的问题。

对于新项目,我现在的流程是:先随机森林给出一个可靠的基线,然后根据数据规模选择 LightGBM 或 XGBoost 精调,遇到高基数类别特征就试 CatBoost。整个过程不看魔法,看经验累积。希望这篇整理能帮你少走一些弯路,也欢迎在实践中根据数据和业务做调整。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 1:02:18

管住Cursor的7条铁律:让AI编程不再失控

说实话,我第一次用 Cursor 的时候是有点上头的。AI 补全快得离谱,Tab 一按就是半屏代码,聊几句就能把一个模块生成出来,整个人感觉像换了台法拉利。但用了大概两周之后,我被迫面对一个现实:我的项目开始失控…

作者头像 李华
网站建设 2026/9/16 1:01:10

Web安全测试核心漏洞解析与实战技巧

1. 小迪安全第四节课程核心内容回顾2024年小迪安全课程的第四节主要围绕Web应用安全测试中的关键漏洞展开深度解析。作为一线安全工程师,我认为这节课的价值在于将OWASP Top 10中的抽象概念转化为可实操的检测方法。以下是课程的核心知识框架:SQL注入漏洞…

作者头像 李华
网站建设 2026/9/16 0:58:21

零碳高校智慧能源AI大模型数字化平台规划设计方案:数据驱动、AI赋能、业务闭环

以AI大模型、物联网、大数据、数字孪生等技术为支撑,面向高校构建“零碳/低碳智慧能源”数字化平台,实现能源生产、传输、分配、消费全流程的智能监测、预测、调度、优化与碳资产管理。 该方案以“零碳高校”为目标,以AI大模型为核心引擎&am…

作者头像 李华
网站建设 2026/9/16 0:57:49

贝塞尔光、艾里光与涡旋光:物理原理与SLM相位掩模设计

简介:MATLAB源码包聚焦艾里光束、贝塞尔光束、涡旋光及完美涡旋光束的生成与仿真,适合光学工程、信息光学方向的研究生或科研人员快速上手。压缩包共5个文件,含4个m脚本和1个txt说明,分别实现Airy.m、Bessel.m、perfect_vortex_be…

作者头像 李华