集成学习7天实战:Bagging、Boosting与Stacking全攻略

发布时间:2026/10/11 9:43:18
集成学习7天实战:Bagging、Boosting与Stacking全攻略 简介由杰森·布朗利编写的《Python集成学习算法7天速成》中文翻译版源自MachineLearningMastery系列面向已有Python、NumPy和scikit-learn基础的开发者用七节课快速补齐集成学习核心技能。资源共1个PDF文件压缩包大小481KB便于下载后离线阅读文档按课程01至07组织章节独立性较强依次讲解什么是集成学习、装袋集成、随机森林、AdaBoost、梯度提升、投票集成和堆叠集成每课设有明确任务并提供基于scikit-learn的完整代码示例可照做预测建模实验。针对模型方差、数据泄露和融合策略等容易出错的环节书中给出精简说明帮助读者把多个模型组合成更可靠的预测方案也可迁移到Kaggle等竞赛场景。目前已有108人学习浏览适合希望利用一周时间系统掌握集成学习并提升模型表现的开发者。1. 集成学习 7 天速成为什么“打群架”的模型通常比单打独斗更强如果你正打算用 7 天时间把 Python 集成学习算法从只会调库练到能讲清原理、能上手调优、能排查问题这篇笔记就是按这个目标写的。集成学习的核心思想一句话就能说透单个模型容易偏、容易抖但把一群各有毛病、又不太一样的模型凑在一起投票或接力整体误差通常会被磨平一大截。这也是 Kaggle 竞赛里几乎每支前排队伍都会在最后一步用上 Stacking 或 Boosting 的原因。7 天速成不是让你背公式而是按“并行集成 → 串行集成 → 集成之集成 → 避坑调优”这条主线用 Python 生态里现成的库把每个环节跑通。适合的对象很明确已经会用 sklearn 训练单个模型、但对集成学习只停留在“听过名字”阶段的从业者以及想在项目里把模型精度再往上顶一档的工程师。第一天到第三天你会感受到“多模型投票”带来的稳定收益第四到第六天进入 Boosting 与 Stacking 后更多的功夫要花在防止过拟合和评估偏差上最后一天请务必留给自己把整个实验过程固化成可复现的记录。接下来我们从最基础的并行集成开始。2. 并行集成Voting 与 Bagging先让模型学会“投团队票”2.1 集成学习的理论地基偏差-方差分解与“三个臭皮匠”逻辑先回答一个新手必然要问的问题为什么多个模型放在一起效果反而比最好的单个模型还稳这要从偏差-方差分解说起。一个模型的泛化误差大致能拆成三项偏差bias、方差variance和不可约噪声。偏差大说明模型对规律的理解不够经常欠拟合方差大说明模型对训练数据的波动太敏感换个数据集表现就剧烈起伏。单棵决策树是典型的低偏差高方差模型而线性模型则常常反过来。集成学习的思路就是在这两项之间做文章。Bagging 的核心操作是“并行训练 平均”它通过 Bootstrap 采样让每个子模型看到不同的训练子集子模型之间天然存在差异最后投票或平均时彼此的高方差部分会互相抵消。这背后的数学直觉是如果各基模型误差是独立的那么平均后的方差会按子模型数量下降哪怕基模型之间不完全独立只要不是完全相关方差也能被显著压缩。这里有一张常用的误差拆解表方便你对照自己的项目来判断该用 Bagging 还是 Boosting当前问题主要矛盾推荐的集成方向理由单模型欠拟合训练分很低高偏差Boosting / Stacking串行拟合残差能系统性降低偏差单模型分数不错但测试集波动大高方差Bagging / RandomForest并行平均能压低方差数据噪声大特征冗余多方差与偏差都高先用 Bagging 稳底再上 Boosting直接 Boosting 容易把噪声也学进去“三个臭皮匠胜过诸葛亮”这句话在机器学习里是有前提的皮匠们得有自己的判断不能全是同一个师傅教出来的。如果 10 个模型高度雷同集成只是在放大同一个错误毫无意义。这也是整篇笔记里反复会出现的一条主线多样性是集成的灵魂。2.2 用 scikit-learn 跑通 Voting 与 Bagging最小可执行代码先动手跑一个最朴素的 Voting 分类器。Voting 分硬投票和软投票硬投票是每个模型投一票、少数服从多数软投票是每个模型输出类别概率后加权平均再取概率最高的类别。数据量小、模型校准良好时软投票通常更稳。下面这段代码在一个模拟的二分类数据集上直接对比单模型与 Voting 的效果from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.ensemble import VotingClassifier, RandomForestClassifier from sklearn.linear_model import LogisticRegression from sklearn.tree import DecisionTreeClassifier from sklearn.metrics import accuracy_score # 生成 2000 个样本、20 个特征的模拟二分类数据 X, y make_classification(n_samples2000, n_features20, n_informative15, random_state42) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, stratifyy, random_state42) # 三个差异明显的基模型 lr LogisticRegression(max_iter1000, random_state42) dt DecisionTreeClassifier(max_depth5, random_state42) rf RandomForestClassifier(n_estimators50, max_depth8, random_state42) # 软投票按类别概率加权平均 voting_clf VotingClassifier( estimators[(lr, lr), (dt, dt), (rf, rf)], votingsoft ) voting_clf.fit(X_train, y_train) for name, model in [(LR, lr), (DT, dt), (RF, rf), (Voting, voting_clf)]: acc accuracy_score(y_test, model.predict(X_test)) print(f{name}: {acc:.4f})这段代码里最关键的是votingsoft参数。软投票要求基模型都有predict_proba方法逻辑回归和决策树都满足。如果你换了某些不支持概率输出的模型就只能退回到votinghard。我一般会在跑 Baseline 时先用硬投票确认三个模型分数接近后再切到软投票看是否有提升。接下来是 Bagging 的最小示例。BaggingClassifier 本身是一个通用包装器你可以把任意基模型塞进去它内部通过 Bootstrap 采样生成多个训练子集并行训练多个副本from sklearn.ensemble import BaggingClassifier from sklearn.tree import DecisionTreeClassifier # 用决策树作为基模型训练 200 个副本 bag_clf BaggingClassifier( estimatorDecisionTreeClassifier(max_depth10, random_state42), n_estimators200, max_samples0.8, # 每个子模型采样 80% 的样本 max_features1.0, # 每个子模型使用全部特征 bootstrapTrue, # 有放回采样 oob_scoreTrue, # 计算袋外分数 n_jobs-1, # 使用全部 CPU 核 random_state42 ) bag_clf.fit(X_train, y_train) print(fBagging OOB Score: {bag_clf.oob_score_:.4f}) print(fBagging Test Accuracy: {accuracy_score(y_test, bag_clf.predict(X_test)):.4f})max_samples0.8意味着每个子模型只看到 80% 的样本剩下的 20% 没参与训练正好可以用作天然的验证集。oob_scoreTrue就是利用这些没被采到的样本计算一个袋外分数它约等于交叉验证的效果但省掉了重复训练的开销。对新手来说如果训练集只有几千条建议把max_samples调高到 0.9否则每个子模型看到的样本太少基模型容易欠拟合。2.3 随机森林Bagging 加特征随机性以及 OOB 分数的妙用随机森林可以理解为 Bagging 的一个升级版它在每次节点分裂时不再从全部特征里找最优切分点而是随机抽取一部分特征再从中选最优。这个改动很关键它让每棵树学到的“特长”不同。比如有的树更依赖特征 A有的树更依赖特征 B投票时彼此互补整体方差进一步下降。用 sklearn 训练随机森林只需要改一行from sklearn.ensemble import RandomForestClassifier rf_clf RandomForestClassifier( n_estimators300, max_depthNone, # 不限制深度让树自由生长 min_samples_leaf2, # 叶子节点最少 2 个样本限制过拟合 max_featuressqrt, # 分类问题默认取特征数的平方根 oob_scoreTrue, n_jobs-1, random_state42 ) rf_clf.fit(X_train, y_train) print(fRF OOB Score: {rf_clf.oob_score_:.4f}) print(fRF Test Accuracy: {accuracy_score(y_test, rf_clf.predict(X_test)):.4f}) # 查看特征重要性 importance rf_clf.feature_importances_ top_idx importance.argsort()[-5:][::-1] print(Top-5 重要特征索引:, top_idx)max_featuressqrt是分类问题的默认推荐值回归问题一般用1.0或log2。这段代码里我把max_depth设为None配合min_samples_leaf2来让树自由生长但叶子不能太纯这个组合在中小数据集上往往表现不错。特征重要性feature_importances_是基于“该特征在所有树中带来的不纯度减少量”累加得到的注意它偏向数值型特征和取值多的特征只能用作粗筛不能当作因果证据。OOB 分数在这里还有额外价值你不必专门切一块验证集就能知道模型大概的水平。做法是先跑一遍不设置max_depth的随机森林看 OOB 分数再跑一遍有限制条件的版本对比 OOB 分数涨了说明限制有效跌了说明限制过度。我习惯把 OOB 分数当作调参时的主要参照省下交叉验证的大量时间。3. 串行集成Boosting 家族从 AdaBoost 到 GBDT 再到 XGBoost3.1 Boosting 的核心逻辑每个模型负责“上一轮没做对的事”如果把 Bagging 比作“并行开会、独立发言”Boosting 就是“接力攻坚、步步为营”。它训练一堆模型但顺序有先后第 2 个模型重点关注第 1 个模型做错的样本第 3 个模型重点关注前两个模型的残差如此往复。AdaBoost 的做法是改变样本权重——被分错的样本权重变大下一轮模型被迫把注意力集中在这些难样本上。GBDT梯度提升决策树换了一种更通用的表述每一轮新增的树去拟合前面所有树的负梯度也就是“残差”。对于回归任务残差就是真实值与当前预测值之差对于分类任务这个残差被替换为概率空间上的梯度。为什么拟合残差有效因为每一轮都在修正当前的错误方向相当于沿着损失函数下降的方向逐步逼近最优解。这里有一个新手容易犯的概念混淆AdaBoost 和 GBDT 都叫 Boosting但 AdaBoost 通过样本权重来体现“关注错误”GBDT 通过拟合残差来体现。前者对异常值极其敏感因为异常点权重会被不断放大后者在损失函数选择上更灵活因此衍生出了 XGBoost、LightGBM 等一堆工程化实现。理解了这一点你就能明白为什么大多数比赛和工业项目最终都选了 GBDT 系算法而不是 AdaBoost。3.2 AdaBoost 与梯度提升的最小实现从 sklearn 到手动理解先用 sklearn 把 AdaBoost 跑通代码很简单from sklearn.ensemble import AdaBoostClassifier ada_clf AdaBoostClassifier( estimatorDecisionTreeClassifier(max_depth1, random_state42), # 树桩 n_estimators200, learning_rate0.5, algorithmSAMME, random_state42 ) ada_clf.fit(X_train, y_train) print(fAdaBoost Test Accuracy: {accuracy_score(y_test, ada_clf.predict(X_test)):.4f})AdaBoost 的基模型在 sklearn 里默认就是深度为 1 的决策树桩algorithmSAMME是为了兼容多分类的变体。learning_rate0.5表示每一步只按 0.5 的步长采纳当前模型的贡献这样后续模型还能有修正空间步长太大会导致模型在少数难样本上用力过猛太小则训练缓慢。跑完这段代码后你可以打印ada_clf.estimator_errors_看看每一轮的加权误差误差曲线如果后期开始震荡说明步长没调好或者树桩太弱。再看回归任务的 GBDT 实现同样是 sklearn 一行搞定from sklearn.ensemble import GradientBoostingRegressor from sklearn.metrics import mean_squared_error gb_reg GradientBoostingRegressor( n_estimators200, learning_rate0.05, max_depth3, subsample0.8, random_state42 ) gb_reg.fit(X_train_reg, y_train_reg) # 回归数据集 pred gb_reg.predict(X_test_reg) print(fGBDT RMSE: {mean_squared_error(y_test_reg, pred, squaredFalse):.4f})subsample0.8是随机梯度提升的做法每轮只用 80% 的样本训练当前树。这个参数不是为了省计算而是引入随机性来降低与 Bagging 类似的方差同时加快训练。经验上learning_rate与n_estimators必须联合调整学习率减半需要的树数量大致翻倍。你想快速验证这一点可以分别跑(0.1, 100)和(0.05, 200)两组参数观察测试分数是否接近。3.3 GBDT 的三件套调参学习率、树深度与子采样GBDT 系模型在实践中最需要关注的三个参数就是learning_rate、max_depth和subsample。它们之间是互相牵制的关系。learning_rate控制每棵树贡献的权重。数值越小模型越保守需要越多树来拟合同样的信号数值太大前几棵树就把训练集学满了后面全是噪音拟合。我一般先用固定学习率 0.1 跑一遍记录验证集分数曲线然后把学习率降到 0.05、树数量翻倍再对比曲线。如果两次效果接近说明模型容量足够问题在别处如果 0.05 显著更好说明 0.1 时已经过拟合了。max_depth在 GBDT 中通常取值 3 到 6远小于单棵决策树的深度。原因是 Boosting 每次只拟合残差不需要太复杂的树深度过深反而让当前树“想太多”把残差里的噪声也学进去。subsample是行采样比例0.7 到 0.9 之间是常用区间它对防止过拟合的贡献在数据量小的时候尤其明显。这三个参数的正确调参顺序很重要。我建议先固定learning_rate0.1用交叉验证搜max_depth和subsample找到合适区间后再把学习率降下来、把树数量补上去最后微调早停。顺序反了会浪费时间因为你在一个过拟合状态下调学习率结论没有参考价值。3.4 工程化 BoostingXGBoost 与 LightGBM 该怎么选当你从 sklearn 切到真正的工程化 Boosting 库时先看这张对比表再决定用哪个对比维度XGBoostLightGBM分裂策略预排序 直方图近似基于梯度的单边采样GOSS 互斥特征捆绑EFB训练速度中等大数据集上明显更快内存占用较高更低类别特征支持需手动编码支持原生类别特征擅长场景中小数据、稀疏特征大规模数据、高维特征如果你只是想在 7 天速成里快速拿到可靠结果我的建议是中小数据集优先 XGBoost因为它更稳定、文档更成熟数据量超过几十万行且特征稀疏时直接换 LightGBM速度差距会非常明显。下面用 XGBoost 的 sklearn 接口做一个最小示例from xgboost import XGBClassifier from sklearn.metrics import roc_auc_score xgb_clf XGBClassifier( n_estimators300, learning_rate0.05, max_depth4, subsample0.8, colsample_bytree0.8, # 每棵树随机使用 80% 的特征 eval_metriclogloss, early_stopping_rounds30, random_state42 ) xgb_clf.fit( X_train, y_train, eval_set[(X_test, y_test)], verboseFalse ) print(fXGB AUC: {roc_auc_score(y_test, xgb_clf.predict_proba(X_test)[:, 1]):.4f})这段代码里early_stopping_rounds30是防止过拟合的关键当测试集合上的 logloss 连续 30 轮没有提升训练自动终止best_iteration属性会告诉你最佳树数量。colsample_bytree0.8是随机森林里特征随机性的 Boosting 版本它迫使每棵树只依赖部分特征增加多样性。LightGBM 的接口略有不同它用n_estimators对应同样的概念需要额外设置num_leaves而不是max_depth。num_leaves理论上可以大于2^max_depth因此它能拟合更复杂的局部结构但也更容易过拟合。我的经验是先用默认num_leaves31如果验证集分数不如 XGBoost再逐步调整而不是一上来就改得面目全非。4. 集成之集成Stacking 与 Blending让模型预测值变成新特征4.1 Stacking 为什么能涨点元模型学的是“谁在哪些样本上更可靠”Stacking 的思路比 Bagging 和 Boosting 又进了一步Bagging 让基模型投票Boosting 让基模型接力而 Stacking 干脆把基模型的预测结果当作新特征再训练一个元模型来学习“在什么情况下应该更相信哪个基模型”。举例来说逻辑回归可能在样本 A 上很准随机森林在样本 B 上很准Stacking 的元模型会学到这个规律而不是简单投票。这里最关键的操作是防止“数据泄漏”。如果直接用基模型在训练集上的预测结果作为元模型的输入基模型已经见过这些样本预测结果会偏乐观元模型学到的就是虚高信号。正确做法是用 K 折交叉验证为每个训练样本生成“袋外预测”Out-of-Fold简称 OOF比如 5 折每次用 4 折训练基模型预测剩下 1 折循环 5 次后每个训练样本都拿到一个“没见过它”的预测值。测试集上的处理同样要注意每一折训练好的基模型都要对完整测试集做预测然后取平均得到测试集的最终预测特征。很多人第一版 Stacking 涨不了点几乎都是因为这一步偷懒直接用全量训练模型预测测试集导致特征分布不一致。4.2 用交叉验证生成 OOF 预测完整可复现代码下面这段代码手动实现了 5 折 Stacking 的全流程基模型用随机森林和逻辑回归元模型用逻辑回归import numpy as np from sklearn.model_selection import StratifiedKFold from sklearn.ensemble import RandomForestClassifier from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score # 准备 OOF 矩阵形状是 (训练样本数, 基模型个数) kf StratifiedKFold(n_splits5, shuffleTrue, random_state42) base_models [ (rf, RandomForestClassifier(n_estimators100, max_depth6, random_state42)), (lr, LogisticRegression(max_iter1000, random_state42)) ] # OOF 预测和测试集预测容器 oof_pred np.zeros((len(X_train), len(base_models))) test_pred np.zeros((len(X_test), len(base_models))) for idx, (name, model) in enumerate(base_models): test_fold_pred np.zeros(len(X_test)) # 累计测试集预测 for train_idx, val_idx in kf.split(X_train, y_train): # 拆出当前折的训练集和验证集 X_fold_train, X_fold_val X_train[train_idx], X_train[val_idx] y_fold_train, y_fold_val y_train[train_idx], y_train[val_idx] # 在当前折上训练基模型 model.fit(X_fold_train, y_fold_train) # 对验证集预测概率填充 OOF oof_pred[val_idx, idx] model.predict_proba(X_fold_val)[:, 1] # 对完整测试集预测概率累加后取平均 test_fold_pred model.predict_proba(X_test)[:, 1] / kf.get_n_splits() test_pred[:, idx] test_fold_pred # 训练元模型输入是基模型的预测概率 meta_model LogisticRegression(max_iter1000, random_state42) meta_model.fit(oof_pred, y_train) final_pred meta_model.predict(test_pred) print(fStacking Test Accuracy: {accuracy_score(y_test, final_pred):.4f})这段代码有几个细节值得展开。第一StratifiedKFold保证每折的正负样本比例和全量数据一致分类问题必须用分层采样否则某些折里可能全是一个类别。第二OOF 矩阵的每一列对应一个基模型在全部训练样本上的“干净预测”元模型拿到的特征不会包含“记忆过的答案”。第三测试集预测用的是每一折模型预测的平均值这一步不可省如果某一折因为随机种子问题导致某模型没收敛平均值能平滑掉异常波动。跑通这段代码后建议你检查一下 OOF 矩阵两列之间的相关性。如果相关性超过 0.95说明两个基模型几乎没有差异Stacking 提升空间有限。这时候与其继续堆模型不如回头改进基模型的多样性。4.3 Blending 作为轻量替代五分钟版 StackingBlending 是 Stacking 的简化版它不做交叉验证而是直接把训练集切出一部分比如 10%当作“小验证集”基模型只在剩余 90% 上训练然后对小验证集和测试集做预测元模型用小验证集的预测结果来训练。代码如下from sklearn.model_selection import train_test_split # 从训练集中切出 10% 作为元模型的小验证集 X_meta_train, X_meta_val, y_meta_train, y_meta_val train_test_split( X_train, y_train, test_size0.1, stratifyy_train, random_state42 ) def get_base_predictions(model, X_fit, y_fit, X_predict): model.fit(X_fit, y_fit) return model.predict_proba(X_predict)[:, 1] # 基模型在小验证集和测试集上的预测 rf_meta_val get_base_predictions(RandomForestClassifier(n_estimators100, random_state42, max_depth6), X_meta_train, y_meta_train, X_meta_val) lr_meta_val get_base_predictions(LogisticRegression(max_iter1000, random_state42), X_meta_train, y_meta_train, X_meta_val) rf_test get_base_predictions(RandomForestClassifier(n_estimators100, random_state42, max_depth6), X_meta_train, y_meta_train, X_test) lr_test get_base_predictions(LogisticRegression(max_iter1000, random_state42), X_meta_train, y_meta_train, X_test) # 拼接特征后训练元模型 meta_features_val np.column_stack([rf_meta_val, lr_meta_val]) meta_features_test np.column_stack([rf_test, lr_test]) meta_model LogisticRegression(max_iter1000) meta_model.fit(meta_features_val, y_meta_val) blend_pred meta_model.predict(meta_features_test) print(fBlending Test Accuracy: {accuracy_score(y_test, blend_pred):.4f})Blending 的优点是代码量少、逻辑直白训练成本低适合在时间紧急时快速出一个集成结果。代价是它比 Stacking 多浪费了 10% 的训练数据给元模型小数据集上效果可能不如 OOF 方式。我的建议是7 天速成的第 5 天先跑 Blending 理解流程第 6 天再切到完整的 Stacking两者对比一下就能直观感受到 OOF 的价值。4.4 基模型多样性与元模型选择的实操建议Stacking 涨不涨点一半取决于基模型的选择一半取决于元模型的设计。基模型不要全选同类算法否则学到的错误模式高度一致。我常用的组合是逻辑回归简单线性基线、随机森林高方差、并行、GBDT高偏差、串行、XGBoost带正则的 Boosting。这四个模型的预测概率相关性通常能控制在 0.85 以下相当于提供了四种不同视角。元模型的选择没有统一答案。我的经验是先从逻辑回归开始因为它训练快、结果稳、不容易过拟合如果把 OOF 特征换成逻辑回归后验证分数有明显提升说明信息融合有效再试更复杂的元模型。如果你在元模型里加入原始特征比如把训练数据的原始数值特征也拼进 OOF 矩阵效果可能更好但也更容易过拟合因为元模型现在有了直接“看到”原始特征的能力对 OOF 特征的依赖会下降。这一步要不要做建议以验证集分数为准。另外一个经常被忽略的点如果基模型里有 XGBoost 或 LightGBM它们的predict_proba输出在极端类别不均衡下可能非常集中比如全是 0.01 或 0.99这会干扰元模型的逻辑回归拟合。解决办法是先把 OOF 概率做标签编码或分位变换再输入元模型。这个细节在第 5 章的类别不平衡部分还会再提。5. 集成学习避坑指南5 个真实踩坑记录与排查思路5.1 OOF 与验证集划分不当导致线下涨点、线上翻车现象在本地用随机划分的训练测试集Stacking 之后准确率提升了 2%一上真实环境或线上 A/B 测试效果反而比单模型还差。原因数据里可能存在时间依赖或分组结构。比如同一条用户的多条行为记录被随机分到了训练集和测试集模型等于“见过”这个人了OOF 预测和元模型训练都受到了泄漏信号的干扰。解决检查数据里有没有“同一实体多条记录”的情况比如用户 ID、设备 ID、订单 ID。有的话用GroupKFold按实体分组切分保证同一个实体的数据全部落在同一折里。如果数据是按时间产生的直接按时间点切分训练集和测试集并且用时间前移的交叉验证方式。排查时先画一张数据的时间分布图如果训练集和测试集的类别分布或特征分布有明显漂移先处理分布问题再谈集成。5.2 基模型多样性不足集成活成了“复读机”现象随机森林 XGBoost LightGBM 三个模型做了 Stacking结果和直接用 XGBoost 几乎一样。原因这三个模型虽然算法不同但在同一个数据集上、用同一套特征工程学到的主要规律高度相似。它们对正确样本的看法一致对错误样本的错误方式也雷同元模型没有差异化信息可用。解决先计算 OOF 预测之间的相关性矩阵如果相关系数普遍高于 0.9说明多样性严重不足。这时优先调整方向不是继续加模型而是往特征子集、样本子集或算法类型三个方向制造差异。常见做法是利用特征分组让模型 A 只用数值特征模型 B 只用类别特征模型 C 用全部特征或者同一种算法换不同超参数比如深树和浅树。用这种手法把相关性压到 0.8 以下Stacking 才有实际意义。5.3 早停与学习率n_estimators 加得越多越准是错觉现象训练 Boosting 模型时每次把n_estimators翻倍验证集分数都在涨直到某个点后开始剧烈下降但你看训练集分数还在稳步上升。原因Boosting 的每一轮都在拟合当前残差树的数量越多模型对训练集的记忆越完整。验证集分数开始掉头的那一刻就是模型开始把噪声当成规律的时刻。解决固定使用early_stopping_rounds同时把learning_rate降到 0.05 或更低。一个常用做法是先用 0.1 的学习率找出大致的最佳区间再把学习率减半、树数量翻倍继续观察验证曲线是否改善。注意早停是基于验证集指标不是训练集有些人习惯用训练集 logloss 做早停等于用开卷考试成绩判断学生水平不可取。5.4 类别不均衡下 Boosting 的“强者恒强”现象二分类问题正样本只占 5%负样本占 95%跑完 XGBoost 后准确率 95%看起来很高但一看 F1 分数惨不忍睹正样本几乎全被预测成负类。原因Boosting 在每一轮加权时会把更多权重放在被分错的样本上。但初始阶段负样本数量压倒性占优模型很容易把全部样本预测为负类造成后来的轮次里正样本权重虽然上升但整体决策边界仍偏向负类一侧。解决先换评估指标不要用准确率改用 F1、PR-AUC 或 ROC-AUC。然后给模型传递类别权重参数XGBoost 里是scale_pos_weight通常设为负样本数除以正样本数LightGBM 里用is_unbalanceTrue或scale_pos_weight。如果还不行对少数类做样本加权或上采样别急着用 SMOTE先看类别权重能不能把决策边界拉回来。集成模型内部机制复杂改完权重后一定要同时观察训练集和验证集的分差权重过大会让模型在训练集上过拟合多数类。5.5 随机种子与多线程集成结果像开盲盒现象同一份代码、同一个参数配置每次运行得到的验证集分数都不一样反复横跳 0.5% 左右。原因随机森林的行采样、Boosting 的子采样、交叉验证的分割顺序都依赖随机数生成器同时n_jobs-1并行训练时线程调度的随机性也会影响浮点数累加顺序产生微小差异。解决在代码里给所有模型统一设置random_state交叉验证也用同一个随机种子如果用了 XGBoost 或 LightGBM把它们的random_state也设成同一个整数。不光模型要固定数据 shuffle 的顺序也要固定。如果你发现固定了所有随机种子后结果仍不稳大概率是多线程浮点累加导致此时把n_jobs从-1调成1复测一次如果结果稳定了说明是并行计算引入的数值波动。对上线模型我通常用固定随机种子跑 3 次取均值把这个均值作为提交分数。6. 7 天收尾手法用三份验收报告锁定模型效果别把调参过程当黑匣子7 天速成的最后一天千万不要急着去搜新模型而是做三件事。第一件事是回顾实验记录把 7 天里跑过的每一个模型组合、每一组超参数、对应的验证集分数和 OOF 相关性整理成一张表。没有记录就等于没做过因为回想不出哪组配置是因为改了哪个参数才涨的。我习惯用 Markdown 表格记录每行是一次实验列包括基模型列表、关键参数、验证分数、OOF 相关性、训练用时备注里写当时的判断。这张表就是你下次面对同类问题时的最强参考。第二件事是稳定性验证用一个小脚本对最终选定的集成模型跑 5 次不同随机种子记录每次的验证集分数计算均值和标准差。标准差小于 0.003 才说明模型结果可复现如果标准差偏大说明模型容量或数据切分本身不稳定这时候分析单次分数没有意义。稳定性验证的目的是让你在汇报结果时心里有底涨的那 2% 是真实差异还是随机波动。第三件事是特征重要性归因把最终 Stacking 模型里每个基模型的 OOF 预测重要性输出出来看看元模型到底在依赖谁。如果某个基模型的 OOF 特征在元模型里的系数接近零说明它对这个数据集没有贡献下次可以去掉如果某个特征在随机森林里重要性极高但在 XGBoost 里很低说明它对不同算法有不同价值这个特征值得保留。用permutation_importance计算扰动每个特征后验证分数的下降量比内置的重要性更直观。最后分享一个我自己的教训有一次做时间序列预测花了三天调好了 Stacking线上效果异常优秀但因为实验中途换过两次数据切分方式没有统一记录复盘时完全无法确认哪个版本对应哪个结果最终只能重跑一组基础实验确认。从那以后我把“当天实验当天记录”定成铁律调参过程绝不当黑匣子。这 7 天的速成也许不能让你变成集成学习专家但能让你建立起一套从模型选型到实验验收的完整工作流。希望你也能在最后的验收环节里找到自己最顺手的那一套记录格式。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询