首页 / 资讯中心 / 文章详情

集成学习实战:Bagging、Boosting与Stacking选型调参指南

集成学习实战:Bagging、Boosting与Stacking选型调参指南 ★ FEATURED ARTICLE
1. 别急着调参先搞清楚集成学习到底在补什么短板集成学习算法这几个字我在带新人的时候反复强调一点它本质上是把多个弱判断拼成一个稳判断的方法论重点在于结构设计并非某个现成的模型可以随意调包。它的定位通常是把基线模型的方差和偏差分别压一压然后再把几个视角不同的模型做一次加权合并指望它开箱即用就让分数起飞并不现实。做结构化数据建模、风控评分、销量预测、点击率预估、甚至一些图像和文本的分类任务都会遇到它。适合谁看如果你已经能跑通决策树、逻辑回归、GBDT 这类单模型但每次换数据划分分数就跳水或者模型在训练集上漂亮得离谱、上线就崩那这篇基本就是为你准备的如果你是纯新手前两节讲原理的部分也能看懂后面代码段落可以照着跑一遍。我先说个自己踩过的坑。早些年做一个用户流失预测的小项目数据大概八万行、四十来个特征我拿一棵深度不限的决策树去拟合训练集 AUC 逼近 0.99交叉验证只有 0.71换一次随机种子还能再抖 0.03。当时我以为是特征有问题查了两天数据泄漏最后发现纯粹是单棵深树太敏感——样本稍微动一点树的分裂点就整棵换掉预测边界跟着剧烈摆动。这就是典型的高方差。后来换成几百棵树的随机森林交叉验证直接稳定在 0.79 上下波动收敛到 0.005 以内。那一刻我才真正理解集成学习里平均两个字的分量。1.1 用打靶的比喻理解偏差与方差把模型预测想象成打靶。偏差是弹着点整体偏离靶心的距离说明模型本身的假设太简单学不到数据里的规律比如用一条直线去拟合明显的曲线关系方差是弹着点自身的散布范围说明模型对训练样本过于敏感换一批样本就换一个形状。单棵深决策树是典型的低偏差高方差——它几乎能把训练集完全拟合但每次拟合出来的形状都不一样。集成学习的两个主要分支恰好对应这两件事Bagging 一类通过并行训练多棵互相独立的树再平均把散布范围压小Boosting 一类通过串行地在前一轮错误上继续修正把整体偏离拉回靶心。这个说法不严谨但足够好用。真正在数学上泛化误差可以拆成偏差平方、方差、不可约噪声三项集成能影响的是前两项噪声部分谁也救不了。所以当你发现数据本身的标签就带大量随机性比如用户当天心情导致的点击行为把力气花在集成结构上收益很有限不如回去补特征或者重新定义标签。1.2 三条技术路线的边界在哪我把常见的集成方法归成三类每类的适用条件和代价完全不同路线代表方法主要作用训练方式典型代价并行式Bagging 系随机森林、Extra Trees降方差各基学习器互不依赖可并行内存占用高解释性下降串行式Boosting 系AdaBoost、GBDT、XGBoost、LightGBM、CatBoost降偏差后一轮依赖前一轮残差调参敏感训练慢易过拟合元学习式Stacking 系Voting、Stacking、Blending融合不同假设空间两层训练需要折外预测工程复杂度高易泄漏这里有个关键认知Bagging 和 Boosting 并不能随便互换。Bagging 的基学习器最好是低偏差高方差的模型比如完全不剪枝的深树你要是拿一堆高偏差的线性模型去做 Bagging平均完还是那个偏差收益几乎为零。反过来Boosting 的基学习器应该是弱学习器深度通常控制在 3 到 8 层用深度 20 的树去做 GBDT第一轮就把训练集拟合完了后面几百轮全在拟合噪声只会得到一条陡峭的过拟合曲线。1.3 什么场景我反而不建议上集成不是所有任务都值得堆集成。第一种情况是强解释性要求某些业务场景需要给出一份人能逐条核对的决策依据比如评分卡类的模型这时候逻辑回归加特征分箱比一堆树的投票好解释得多。第二种是推理延迟敏感随机森林五百棵树、每棵深度二十单条预测的耗时是单棵树的几百倍如果是在移动端做实时判断这个开销未必划算得先算清楚 QPS 和单次推理预算。第三种是数据量极小几百行样本做五折 Stacking每一折的训练数据只剩几十行元学习器学到的全是噪声反而不如老老实实用一个带正则的逻辑回归。我一般会给自己定个门槛样本量过万、特征维度在几十到几百之间、允许一定程度的黑盒这时候集成基本是性价比最高的选择。低于这个规模先把手上的单模型和特征工程打磨到位收益往往比换算法大。2. Bagging 与随机森林把方差按下去的那条路2.1 自助采样与袋外估计白送一次验证Bagging 的核心动作极其朴素从原始训练集里有放回地抽同样大小的样本重复若干次得到若干份略有差异的数据集每份训一棵树最后投票或平均。有放回抽样意味着每份新数据集里大约只有 63.2% 的原始样本出现至少一次剩下的约 36.8% 一次都没被抽到。这个数字的来源是一个极限当样本量 n 趋于无穷时(1 - 1/n)^n 收敛到 1/e ≈ 0.368所以入袋概率约 63.2%。这个没被抽到的部分就是袋外样本OOB它对当前这棵树来说是完全没见过的数据。把每棵树的袋外预测收集起来就能在不额外划分验证集的情况下得到一个近似无偏的泛化误差估计。我在数据量紧张的时候特别爱用这个设置oob_scoreTrue训练完直接看oob_score_和五折交叉验证的结果通常差不到 1 个百分点但省了一轮训练时间。注意OOB 估计在小样本或者基学习器之间高度相关时会偏乐观别拿它当唯一依据。我的习惯是 OOB 用来快速筛参数最终定稿还是走一次完整交叉验证。2.2 随机森林真正的精髓是第二层随机很多人以为随机森林就是很多棵决策树做 Bagging其实只对了一半。如果只做样本层面的自助采样当特征里有几个极强的预测变量时每棵树几乎都会在靠近根节点的地方用上这几个特征导致所有树的结构高度相似预测结果也高度相关。一堆高度相关的模型做平均方差下降的幅度远小于独立模型平均的效果。随机森林加的第二层随机就是每次分裂时只在随机抽取的一部分特征里挑最优分割点。这一招强制让不同的树关注不同的特征子集人为地拉低了树之间的相关性。这就是为什么max_features这个参数比n_estimators更值得先调后者只影响平均的平滑程度边际收益递减很快前者直接决定树之间的多样性调错了整片森林白搭。经验上分类任务max_features取特征总数的平方根回归任务取三分之一这是文献里的默认建议但我在实际项目里会围绕默认值上下扫一遍。当特征里有大量强相关冗余变量时稍微调大一点效果更稳当特征维度很高几百上千而有效特征很少时调小反而能抑制噪声特征被反复选中。2.3 参数选择与几个容易忽略的操作细节我把随机森林的关键参数和实际调法整理了一下参数含义建议起点调整方向与心得n_estimators树的数量200~500从 100 往上加看 OOB 分数是否还在涨一般不超 1000max_features每次分裂候选特征数分类取 sqrt(p)回归取 p/3优先调冗余特征多时适当调大max_depth最大深度不限制尽量放开让单棵树充分生长方差交给平均去压min_samples_leaf叶节点最小样本数1~5样本噪声大时调大能明显抑制过拟合min_samples_split内部节点最小分裂样本2与上者联动通常只调一个class_weight类别权重依据不平衡程度正负比超过 1:10 时建议设balanced有几个细节是我在实际项目里反复验证过的。第一不要对随机森林做深度剪枝。这跟单棵决策树的思路完全相反单棵树需要靠剪枝控制方差而集成本身就是靠平均来控制方差你把每棵树都剪浅了等于同时抬高偏差又没充分发挥平均的作用。第二n_estimators加到一定程度后分数会进入平台期继续加只增加训练和推理成本我一般画一条树数量—OOB 分数的曲线找到拐点后往上留 30% 余量就停。第三特征重要性有两种算法基于不纯度下降的那种会偏向高基数特征比如连续变量和取值多的类别变量用置换重要性permutation importance更靠谱虽然慢一些但在做特征筛选时我基本只用后者。还有一个容易忽视的点随机森林的输出概率是投票比例天然偏向多数类且分布不够平滑。如果你的下游要用概率值做排序或设阈值别直接用predict_proba的原始输出做一次 Platt 缩放或者等距回归校准排序的稳定性会好不少。3. Boosting 家族一轮一轮把残差啃下来3.1 AdaBoost 的样本权重到底在算什么Boosting 的思路和 Bagging 完全相反它不追求基学习器之间独立而是让后面的模型专门盯着前面做错的样本。AdaBoost 是最早把这个想法形式化的方法——每轮训练结束后把分错的样本权重调高、分对的调低下一轮在这个加权的数据上再训一个弱分类器。同时每个弱分类器有一个话语权系数错误率越低话语权越大公式是 α 0.5 · ln((1 - e) / e)其中 e 是该分类器的加权错误率。这个公式值得盯两眼当 e 0.5跟瞎猜一样时α 0这个分类器直接被无视当 e 趋近 0 时α 迅速增大。它保证了每一轮加入的新成员都是有信息量的而不是来捣乱的。最终预测是所有弱分类器的加权投票。我对 AdaBoost 的感情比较复杂。它在干净数据、二分类、弱分类器是决策树桩深度为 1的场景下表现非常漂亮训练快、几乎不用调参。但它对噪声标签和离群点极其敏感一个被标错的样本会被反复加权后面几十轮全在死磕这一个点模型被拖偏。所以我在真实业务数据上很少直接用它更多是拿它当教学示例来理解 Boosting 的机制。3.2 GBDT 用负梯度代替残差的直觉GBDT 的思路可以这么理解它把整个模型看成一个加法模型每一轮新加一棵树目标就是让整体损失再降一点。当损失是平方误差时最优的下降方向恰好就是残差真实值减预测值所以早期资料都说 GBDT 在拟合残差。但换成其他损失函数比如对数损失、Huber 损失残差这个概念就不成立了统一的做法是沿损失函数的负梯度方向走。用生活化的说法你在下山每一步不需要知道山有多高只需要知道脚下往哪个方向是下坡。负梯度就是那个下坡方向每棵树拟合的是当前模型还差多少这个方向上的分量。这也是 GBDT 能适配各种损失函数的原因——只要损失可微就能算出负梯度。学习率shrinkage在这里的作用是给每一步打个折扣每棵树的输出乘以一个小于 1 的系数再加到总模型上。直觉上就是步子迈小一点多走几步泛化更稳。这个参数的取舍非常直接学习率减半树的数量大致要翻倍训练时间线性增加但精度通常能提一点。我一般的做法是先用 0.1 跑通流程看效果如果还有明显提升空间再降到 0.05 甚至 0.02同时把树的数量上限提到几千并配合早停。3.3 三大主流实现的工程取舍这三个框架我都长期用过各自的取舍非常鲜明维度XGBoostLightGBMCatBoost分裂策略预排序 近似分位点直方图 leaf-wise对称树oblivious tree训练速度中等快尤其大样本高维中等GPU 下明显加速内存占用较高低中等类别特征需手动编码原生支持需注意过拟合原生支持效果最稳小数据表现稳容易过拟合需调小叶子数稳我的常用场景中小规模、需要精细控制正则大规模、需要快速迭代类别特征多、样本量中等LightGBM 的 leaf-wise 生长策略是它的速度来源也是它的坑每次挑增益最大的叶子继续分裂容易长出很不平衡的深枝在小数据上过拟合明显。所以用它的时候我会同时设num_leaves一般不超过 2 的深度次方和min_child_samples小数据上可以设到 20 以上再加max_depth兜底。CatBoost 的对称树结构约束更强反而在小数据上不容易翻车而且它对类别特征的处理用的是有序目标统计量能在一定程度上避免目标泄漏这一点在类别取值很多的时候特别好用。XGBoost 的正则项设计我觉得最值得学它在目标函数里显式加了叶子数量和叶子权重的惩罚项把树的复杂度直接写进了优化目标里这也是它比原始 GBDT 更抗过拟合的核心原因。理解了这一层你就明白gamma、lambda、alpha这几个参数不是玄学——它们分别对应分裂至少要带来多少增益才允许、叶子权重平方和惩罚、叶子权重绝对值惩罚。3.4 调参顺序按依赖关系排不能乱调参这件事最怕乱试。我的固定顺序是这样每一步都基于上一步的结果先定学习率和树数量的组合。学习率设 0.1树数量给个很大的上限比如 3000用早停让验证集自己决定停在哪一轮。记下最优轮数。再调树的复杂度。max_depth或num_leaves配合min_child_weight/min_child_samples。逻辑是先放开一点看会不会过拟合再用最小叶样本数把过拟合压回去。然后加正则。lambdaL2、alphaL1、gamma分裂最小增益。这三个通常只需要小幅度调整作用是把已经不错的模型再稳一稳。最后调采样。subsample行采样和colsample_bytree列采样一般设在 0.7~0.9 之间能同时提速和抑制过拟合。一个很实用的技巧调完一轮之后把学习率减半、树数量上限翻倍重新跑一次早停。如果验证集分数没有再提升说明当前结构已经到瓶颈了继续调树的参数意义不大该回去做特征了。4. Stacking 与 Voting把不同视角的模型缝起来4.1 投票法简单但别小看Voting 是最轻量的集成把几个模型的预测结果做汇总。硬投票是看类别票数谁多算谁软投票是把各模型的预测概率取平均再选概率最大的类。我的经验是软投票几乎总是优于硬投票因为它保留了置信度信息——三个模型里两个用 0.51 的概率猜 A、一个用 0.95 的概率猜 B硬投票会选 A软投票会选 B而后者往往才是对的。加权软投票更进一步给每个模型一个权重。权重的确定方法我一般不去网格搜索组合爆炸太狠而是用各模型在验证集上的表现做个简单的归一化或者用逻辑回归在验证集上学一组权重。这里有个必须注意的点权重必须在独立的验证集上学不能在看过的数据上定否则就是在验证集上过拟合。4.2 Stacking 的关键全在折外预测Stacking 是两层结构第一层训若干个不同的基模型第二层用一个元学习器通常是很简单的模型逻辑回归或小深度的树去学该在什么情况下相信哪个基模型。听起来很美好但九成的翻车都出在一个地方训练元学习器用的特征必须是折外预测不能用基模型在训练集上的预测。原因很直白基模型在它见过的数据上预测会非常准尤其树模型能到 0.99元学习器看到这种几乎全对的特征会学出一套极度自信的权重一上验证集立刻崩盘。正确做法是 K 折交叉把训练集切 K 份每次用 K-1 份训基模型、对剩下 1 份做预测K 轮拼起来刚好覆盖整个训练集得到的就是折外预测。元学习器在这个折外特征上训练才和推理时的分布一致。代码大概长这样import numpy as np from sklearn.model_selection import StratifiedKFold from sklearn.linear_model import LogisticRegression def build_oof_features(base_models, X, y, n_splits5, random_state42): 返回折外预测矩阵和训练好的基模型列表 skf StratifiedKFold(n_splitsn_splits, shuffleTrue, random_staterandom_state) oof np.zeros((X.shape[0], len(base_models))) fitted [[] for _ in base_models] for fold, (tr_idx, va_idx) in enumerate(skf.split(X, y)): X_tr, X_va X.iloc[tr_idx], X.iloc[va_idx] y_tr y.iloc[tr_idx] for i, model in enumerate(base_models): m model.__class__(**model.get_params()) m.fit(X_tr, y_tr) oof[va_idx, i] m.predict_proba(X_va)[:, 1] fitted[i].append(m) return oof, fitted训练完之后元学习器在oof上学预测时把各基模型在全量训练集上重训的版本对测试集做预测拼成同样的特征矩阵喂给元学习器。这个重训步骤很多人会漏掉直接用 K 折里的某一份模型去预测测试集结果和训练时的分布对不上。4.3 Blending 与 Stacking 的取舍Blending 是 Stacking 的简化版不搞 K 折直接把训练集切成两部分一部分训基模型另一部分做预测来训元学习器。好处是实现简单、不容易泄漏坏处是浪费了一部分训练数据且元学习器只在很小的数据上训练方差大。我在样本量上万的时候更倾向 Stacking样本量只有几千的时候用 Blending 更稳。还有一个我自己的经验Stacking 的收益和基模型的差异性正相关。你拿三个参数略有不同的 LightGBM 去做 Stacking收益基本为零因为它们犯的错几乎一样。真正有效的是把假设空间差别很大的模型放一起比如一个线性模型、一个树集成、一个最近邻或者一个简单的神经网络。它们对同一条样本的判断依据完全不同元学习器才有东西可学。5. 一套可以直接照着跑的完整流程5.1 数据准备与基线我拿一个结构化的二分类任务来演示数据规模假设是两万行、三十个特征其中五个是类别特征。第一步永远是搭一个朴素基线用来判断后面所有复杂的模型是否真的带来了增量。import pandas as pd import numpy as np from sklearn.model_selection import train_test_split, StratifiedKFold, cross_val_score from sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import roc_auc_score X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, stratifyy, random_state42 ) # 基线一单棵决策树不剪枝 tree DecisionTreeClassifier(random_state42) scores cross_val_score(tree, X_train, y_train, cv5, scoringroc_auc) print(f单棵深树 AUC: {scores.mean():.4f} ± {scores.std():.4f})跑完通常会看到类似 0.74 ± 0.03 的结果——均值不高、方差还不小这就是高方差最直白的证据。接下来换随机森林rf RandomForestClassifier( n_estimators400, max_featuressqrt, min_samples_leaf3, oob_scoreTrue, n_jobs-1, random_state42, ) rf.fit(X_train, y_train) print(fOOB 分数: {rf.oob_score_:.4f}) print(f测试集 AUC: {roc_auc_score(y_test, rf.predict_proba(X_test)[:, 1]):.4f})按我的经验同样的数据上随机森林能到 0.80 左右而且交叉验证的波动会收敛到 0.008 以内。这一步的收益几乎全部来自方差的下降不需要任何精巧的调参。5.2 提升模型早停是把控过拟合的开关接下来上 LightGBM。我会先划出一块独立的验证集专门用于早停而不是用交叉验证里的验证折——因为早停本身就是在用验证集做决策再用同一份数据评估会偏乐观。import lightgbm as lgb X_tr, X_val, y_tr, y_val train_test_split( X_train, y_train, test_size0.2, stratifyy_train, random_state42 ) params { objective: binary, metric: auc, learning_rate: 0.05, num_leaves: 31, min_child_samples: 20, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 1, lambda_l2: 1.0, verbosity: -1, seed: 42, } dtrain lgb.Dataset(X_tr, y_tr) dval lgb.Dataset(X_val, y_val, referencedtrain) model lgb.train( params, dtrain, num_boost_round3000, valid_sets[dval], callbacks[lgb.early_stopping(100), lgb.log_evaluation(200)], ) print(f最优迭代轮数: {model.best_iteration})这里有几个参数值的来历值得说清楚。num_leaves31对应的是深度约 5 的完全二叉树2^5 - 1 31这是 LightGBM 的默认值也是我判断当前模型复杂度是否合适的锚点——如果你把num_leaves调到 128相当于允许深度 7 的树小数据上大概率过拟合。min_child_samples20意味着一个叶子至少要有 20 个样本才允许生成这是压制 leaf-wise 生长乱窜最直接的手段样本量越小这个值要越大。early_stopping(100)是连续 100 轮验证集指标没有提升就停这个耐心值我一般设在 50 到 200 之间太小会在指标抖动的谷底误停太大浪费时间。还有个小细节早停拿到的best_iteration之后最终模型要用全部训练数据、以这个轮数重新训一遍而不是直接用早停那次的模型因为后者少用了 20% 的数据。这一步经常被忘掉白白损失一点精度。5.3 融合从软投票到 Stacking有了随机森林和 LightGBM 两个模型先试最轻的软投票from sklearn.ensemble import VotingClassifier voting VotingClassifier( estimators[(rf, rf), (lgbm, lgb_wrapper)], votingsoft, weights[1, 2], )权重 1:2 是我按两者在验证集上的 AUC 差距估的LightGBM 通常比随机森林高两三个千分点给它双倍权重。这个权重不需要精细搜索粗调即可收益曲线在这个范围内很平。再往上走就是 Stacking。我一般会用三类基模型逻辑回归线性视角、随机森林Bagging 视角、LightGBMBoosting 视角元学习器用带 L2 正则的逻辑回归C设在 1.0 左右。元学习器一定要简单用复杂的模型去学那几个概率特征等于把前面辛苦控制的方差又加回来了。实测下来在差异化的基模型组合下Stacking 相对最好的单模型通常能提 0.5 到 1.5 个百分点。别指望更多——如果某个组合给你提了五个点先怀疑是不是折外预测做漏了、发生了泄漏。5.4 结果解读别只看一个数字跑完整个流程我习惯把结果整理成一张对照表同时记录训练耗时和推理耗时因为选型从来不是只看精度模型验证集 AUC测试集 AUC训练耗时单条推理耗时单棵深树0.7420.7350.3s0.01ms随机森林400 棵0.8030.79812s0.8msLightGBM早停0.8260.82125s0.02ms软投票1:20.8310.82738s0.9msStacking三类基模型0.8380.82990s1.1ms这张表里有个值得注意的现象Stacking 在验证集上比软投票高 0.7 个点但测试集只高 0.2 个点说明这点增益有一部分是拟合验证集带来的。所以我定稿的时候会看测试集那一列而不是验证集。另外 LightGBM 的推理耗时反而比随机森林低两个数量级这是很多人会忽略的树的棵数少、深度浅单次推理只需要走几十次判断而随机森林四百棵树的遍历开销大得多。如果线上对延迟敏感Boosting 系的性价比其实更好。特征重要性也值得看一眼。我一般取 LightGBM 的 gain 重要性排序重点看两件事一是有没有明显不该出现的特征排在前面这往往意味着数据泄漏二是有没有一批特征的重要性加起来不到 5%可以考虑砍掉简化模型。如果发现某个特征单独就贡献了 40% 的重要性我会去查它的分布和采集逻辑这种极端集中通常不是好事。6. 常见问题与排查实录6.1 训练集 0.99验证集 0.6问题出在哪这是最高频的问题。我的排查顺序是固定的从成本最低的开始先看数据泄漏。检查有没有在划分训练测试集之前做了全局的标准化、填充或者目标编码。这类错误极其隐蔽我有一次把目标编码写在划分之前验证集 AUC 冲到了 0.97高兴了半天后来发现是编码时用到了测试集的标签信息。再看样本重复。同一个实体在训练集和验证集里各出现一次模型相当于背答案。做法是先按实体 ID 去重再按 ID 分组划分而不是随机划分行。最后才是模型本身过拟合。这时候的处置是降复杂度树的数量减半、num_leaves或max_depth砍到原来的三分之一、min_child_samples加大、正则项加上去同时把学习率降下来。这个顺序很重要——先怀疑数据再怀疑模型因为数据问题靠调参是救不回来的。6.2 集成之后反而不如单模型遇到这种情况我一般从三个角度查。第一基模型之间太像了。如果你 Stacking 的几个模型在同一批样本上错得一模一样元学习器学不到任何东西反而因为多一层拟合引入方差。解决办法是换假设空间差别大的模型或者干脆只用单模型。第二元学习器太复杂。用一棵深树或者一个 GBDT 去当元学习器在只有几个概率特征的情况下几乎是必然过拟合。换成带正则的逻辑回归或者干脆用固定权重的软投票。第三样本量太小。Stacking 的每一折训练数据只有原来的 (K-1)/K再分出一部分给元学习器实际用于训练基模型的数据可能只剩六成。样本量在几千以下时我基本不用 Stacking。6.3 训练慢、内存爆掉怎么办LightGBM 在大样本上内存占用高通常是因为特征维度太大。我的几个常规动作把feature_fraction降到 0.6~0.7把稀疏特征转成稀疏矩阵格式把max_bin从默认的 255 降到 127精度损失很小内存和速度收益明显。随机森林那边n_jobs-1开满并行是基本操作但要注意内存是按核数翻倍的核多的时候反而要限制并行数。还有一个容易被忽略的点bagging_fraction设成 0.8 配合bagging_freq1不仅抑制过拟合还能让每轮训练的数据量减少 20%速度提升立竿见影。这是我调参时最先动的几个开关之一。6.4 线下涨了线上反而掉了这类问题一般不是算法的问题。我会按下面的顺序查训练时的特征计算逻辑和线上是否完全一致尤其是时间窗口类的统计特征线下用全量算、线上用滑动窗口算分布完全不同模型文件的版本和特征处理的版本是否匹配阈值的设定是否还是按旧的分数分布定的。集成模型输出的概率分布和单模型往往有系统性偏移如果下游是按固定阈值做决策的换模型之后必须重新校准阈值这一条我吃过不止一次亏。6.5 问题速查表现象最可能的原因首选处置训练集极高、验证集低数据泄漏 / 样本重复 / 模型过复杂先查特征计算管线再降模型复杂度交叉验证波动大单模型方差高 / 样本量不足换 Bagging 系或增加折数集成不如单模型基模型同质化 / 元学习器过复杂换差异化基模型元学习器改线性早停轮数极小50学习率过大 / 数据噪声大学习率减半加大最小叶样本数特征重要性集中在单一特征潜在泄漏或该特征为事后信息核查该特征的采集时点概率输出分布异常未做概率校准用等距回归或 Platt 缩放校准最后分享一个我在实际项目里养成的习惯每次上集成之前先花十分钟把单模型的错例捞出来看五十条人工判别一下这些错误是模型能力不够还是信息本身就不在特征里。前者集成能救后者堆多少棵树都没用。我见过太多人一上来就把五六个模型 Stacking 起来结果回头一看问题出在标签定义本身就有歧义。集成学习是一把很好用的工具但它解决的是如何在已有信息下把判断做得更稳不是如何凭空造出不存在的信息。这个边界想清楚了选型和调参的路会短很多。
阅读完成 · 觉得有帮助?
咨询建站