从台大林轩田《机器学习技法》开篇第一讲就能感受到,这门课对"集成学习"的梳理方式和市面上绝大多数教程不太一样。它不直接甩给你一堆随机森林、Adaboost的API调用,而是先把这些方法全部统一到一个框架里——就是我们今天要聊的聚合模型(Aggregation Model)。简单说,聚合模型的核心思想只有一句话:单个模型的判断总是有偏的,把一群模型的判断用某种方式合起来,反而能逼近更稳、更强的结果。
这篇文章适合谁看?如果你已经把决策树、SVM、逻辑回归这些单一模型跑得比较熟,但一直没搞明白"为什么随机森林就是比单棵树稳"、"Adaboost到底在优化什么"、"Stacking和Bagging到底是什么关系",那这篇文章就是给你准备的。我会把聚合模型的数学直觉、方法分类、经典算法背后的设计逻辑,以及我自己在项目里实测对比的一些踩坑经验,一次性讲透。
1. 为什么一个模型做不到的事,一群模型能做到
1.1 单模型的"偏科"困境
先聊一个最基础的问题:我们训练单个模型时,到底在做什么?
不管你是用逻辑回归、SVM还是深度网络,本质上都是在一组假设空间 H 里找一个 g,让它在训练数据 D 上的损失尽量小。但这里有个绕不开的矛盾:假设空间选大了,模型容易过拟合,在训练集上很准、换一批数据就露馅;假设空间选小了,模型又欠拟合,连训练集的特征都学不到位。
我举个生活里的例子。你在一个陌生城市打听哪家火锅好吃,问了三个本地人。第一个人只爱吃辣,推荐的全是重辣锅;第二个人只认老字号,推荐的店都开了二十年以上;第三个人是个探店博主,专挑网红店。每个人给你的答案都带着自己的偏好,也就是"偏科"。如果你只信其中一个,大概率踩坑。
聚合模型的思路特别朴素:三个人都推荐的店,综合来看最值得试。因为单独的某个人可能会因为口味偏见给你带偏,但把几个口味不同的人的意见合在一起,个别人的极端偏好就被稀释了。
放到机器学习里,"本地人"就是不同的模型 g_t,"推荐"就是模型的预测结果,"综合意见"就是聚合之后的 G。这里面的关键是,参与聚合的模型不能是同一个模子刻出来的——如果三个人口味完全一样,那问三个人和问一个人没区别。
1.2 偏差-方差分解:聚合的两个收益方向
为什么把多个模型的结果平均一下就能变好?这需要用偏差-方差分解(Bias-Variance Decomposition)来说清楚。
对于回归问题,单个模型在某个样本 x 上的预期误差可以分解成三部分:
- 偏差(Bias):模型预测的期望值与真实值之间的差距,反映模型的"系统性错误"。
- 方差(Variance):模型在不同训练集上预测结果的波动程度,反映模型"换个数据就变脸"的程度。
- 噪声(Noise):数据本身自带的不可约误差。
公式长这样:
E[(g(x) - f(x))^2] = (E[g(x)] - f(x))^2 + E[(g(x) - E[g(x)])^2] + σ² = Bias²(g(x)) + Variance(g(x)) + Noise关键在于,聚合对偏差和方差的影响方向是不一样的。
你拿 K 个独立训练的模型做平均,得到 G(x) = (1/K) Σ g_k(x)。假设每个模型的方差都是 σ_g²,且相互独立,那 G 的方差就是:
Variance(G) = Variance((1/K) Σ g_k) = σ_g² / K方差直接缩到了原来的 1/K。这就是Bagging、随机森林这类方法有效的核心来源——它们通过并行训练多个独立模型再平均,把方差压下去。
但偏差这块,平均操作几乎不动它。因为 E[G] = E[g_k] = bias 本身就是同一个值,平均不会改变整体期望。所以如果你想通过聚合来降低偏差,走均匀平均这条路是行不通的,得换一种玩法——这就是Boosting的思路:串行地训练模型,每个新模型专门去拟合前面模型没做对的残差,一步步把系统性偏差修掉。
1.3 三个臭皮匠成立的数学条件
我见过不少人有一种误解,觉得"模型越多越复杂,聚合之后肯定过拟合"。这个直觉在部分场景下是错的,但在某些情况下又说得通。要搞清楚边界,得看聚合成立的数学条件。
林轩田课程里给过一个非常漂亮的分解。考虑一个由 T 个模型 g_t 聚合得到的 G,它的验证误差可以分解成两个部分:
E_val(G) ≤ avg_E_val(g_t) 的平均表现 + 聚合带来的额外收益/损失更具体地说,对于分类问题,用均匀投票(uniform voting)做聚合时,G 的错误率不会超过所有 g_t 平均错误率的某个上界,而且这个上界在很多条件下比单模型更紧。
这里有一个非常反直觉的结论:聚合模型 G 的复杂度理论上可以比单个 g_t 更低,而不是更高。原因在于,多个模型投票实际上在做"互相纠错"。就算每个 g_t 都在某些样本上犯错,只要它们犯错的样本不一样,投票结果 G 在这些样本上依然能给出正确判断。
当然,这要求模型之间满足一定的"多样性"。如果所有 g_t 犯的错误高度重合,投票纠错能力就很弱,聚合效果大打折扣。所以后面讲到的Bagging、随机森林、AdaBoost,本质上都在做同一件事:在保持模型质量不崩的前提下,尽量增加模型之间的多样性。
打个比方,三个臭皮匠能赛过诸葛亮,前提是他们各自有各自的短板和长板,而不是三个人全在同一条路上栽跟头。
2. 聚合家族全景:从均匀投票到条件融合
2.1 Uniform Blending:最简单的强基线
聚合模型家族里最简单的一支叫Uniform Blending,翻译过来就是均匀融合。它的规则非常简单:
- 分类问题:所有 g_t 投票,票数多的类别胜出。
- 回归问题:所有 g_t 的预测值取平均。
G(x) = sign( Σ_{t=1}^T g_t(x) ) // 二分类 G(x) = (1/T) Σ_{t=1}^T g_t(x) // 回归你可能会说,这不就是把几个模型的结果平均一下吗,有什么技术含量?但就是这么一个简单的操作,在实际项目里的表现经常出奇地好。
我自己的经验是,在做特征工程或者模型选型的时候,先跑一个Uniform Blending当基线,往往比你在单模型上调半天参都靠谱。因为单模型的超参数搜索经常是在"过拟合验证集"的边缘疯狂试探,而均匀融合天然自带一层正则化效果——极端预测被平均操作拉回中心,稳定性明显提升。
为什么均匀平均能抗过拟合?可以从两个角度理解:
一是"多数投票"的角度。如果 T 足够大,投票结果的符号只取决于大多数 g_t 的共识,个别模型的极端错误被稀释掉了。这和现实中的陪审团制度一个道理:一个人可能被偏见影响,但12个人里大多数人理性的概率要高得多。
二是"正则化"的角度。均匀融合相当于把假设空间限制在"所有 g_t 的均值"这个子空间里,模型的自由度比单独一个复杂模型更小,泛化边界更紧。
不过均匀融合有个前提——参与投票的模型得有一定质量。如果一堆模型里混进了好几个表现很差的"猪队友",投票结果反而会被带偏。林轩田课程里给过一个条件:每个 g_t 的错误率只要略优于随机猜测,且模型之间足够独立,聚合效果依然有保障。
2.2 Linear Blending:让数据决定每个模型的权重
均匀融合把所有模型一视同仁,但现实里模型之间是有差距的。你在同一个任务上训练一个逻辑回归、一个XGBoost、一个神经网络,大概率XGBoost就是比逻辑回归准。这时候你还会想给它们一样的投票权吗?
不会。于是就有了Linear Blending(线性融合),思路是给每个模型学一个权重 α_t,然后加权求和:
G(x) = sign( Σ α_t g_t(x) ) // 二分类 G(x) = Σ α_t g_t(x) // 回归那这个 α_t 怎么确定?直接在所有训练数据上回归一遍不就行了?
不行。这里有一个很隐蔽的坑——如果你在训练集上学习权重,权重会被过拟合到训练噪声上。林轩田课程里特意强调,学习 α_t 的时候必须用验证集,而不是训练集。理由是:g_t 本身已经是基于训练集训练出来的,再用同一批数据去学它们的融合权重,就相当于拿学生的考试成绩去评估这个学生答题的可靠性,得到的反馈严重失真。
正确的做法是把数据切出一块验证集,把每个 g_t 在验证集上的预测结果当作新的特征,然后在这组新特征上训练一个线性模型(逻辑回归或者线性回归),学到的系数就是 α_t。
这个过程在业界有个更常见的名字——Stacking(层叠泛化)。但严格来说,Stacking 比 Linear Blending 更通用,因为它不限于线性组合,你可以用任意模型(比如另一个XGBoost)来学习g_t预测结果的组合方式。后面我会专门讲。
2.3 Conditional Blending与Stacking:让模型按条件分工
线性融合虽然比均匀融合灵活,但它有个隐含假设:所有样本上,模型的权重都是一样的。
这个假设在很多场景下不成立。举个实际例子,我在金融风控项目里同时训练了一个逻辑回归模型和一个GBDT模型。逻辑回归在低逾期率人群上表现稳定,GBDT在处理高维稀疏的异常特征时更敏锐。这两个模型在不同人群上的能力侧重点完全不同。
如果只用线性融合,相当于给它们定了两个"死权重":在所有样本上,逻辑回归占0.4、GBDT占0.6。但现实是,在某些样本上,逻辑回归的预测应该占主导,在另一些样本上GBDT的判断才更可靠。
Conditional Blending(条件融合)就是来解决这个问题的。它不再是学一组固定的权重,而是学一个函数 β_t(x),根据输入 x 的特征来决定每个模型的权重:
G(x) = Σ β_t(x) * g_t(x)这个 β_t(x) 的学习方式,最经典的做法就是上面提到的 Stacking——把第一层多个基模型的输出当作新的特征向量,喂给第二层的元模型(meta-learner),元模型自己会学会"在什么特征条件下,更相信哪个模型"。
Stacking 在工业界的应用非常广。比如 Kaggle 比赛的高分方案里,Stacking 几乎是标配:第一层放各种异构模型,第二层用逻辑回归或者简单的线性模型做融合。为什么第二层常用简单模型?因为第一层的预测结果已经是对原始特征的高度抽象,如果第二层再用个复杂模型,很容易把第一层模型之间的相关性噪声也一并学进去,导致过拟合。
我自己做Stacking的经验是:第二层用逻辑回归通常是性价比最高的选择,除非第一层模型之间差异极大、特征维度很高,才考虑用GBDT或者带正则的线性模型。
2.4 Bagging:怎么在数据层面制造"同而不同"的模型
前面讲的 Blending 都隐含一个前提:你手里已经有一堆训练好的 g_t。可是在实际项目里,我们往往只有一份数据集,怎么从零开始"造"出一群多样化的模型来?
答案是Bagging(Bootstrap Aggregating)。核心就两步:对训练集做自助采样(bootstrap sampling),得到 T 份稍有不同的训练子集;然后在每份子集上独立训练一个模型,最后做均匀融合或投票。
For t = 1 to T: 从 D 中有放回地随机采样 n 个样本,得到 D_t 在 D_t 上训练 g_t G(x) = sign( Σ g_t(x) )为什么有放回采样能制造多样性?因为每一份 D_t 里会漏掉原数据集里大约 36.8% 的样本,同时某些样本会重复出现多次。每个模型看到的"世界"都不一样,它们的错误模式自然就不一样。
这里有个非常关键的点:Bagging 的收益主要来自"降低方差"。如果你的基模型是低方差高偏差的那种(比如深度很浅的决策树桩),Bagging 效果并不明显,因为模型的偏差根本没有被修正。所以实践中,Bagging 最常见的搭配是高方差模型——最典型的就是深度决策树,CART树几乎不剪枝、长得又深又复杂,单棵树在训练集上表现极好但泛化很抖,一Bagging,方差立刻被压下来,效果立竿见影。
Random Forest 就是这个思路的集大成者,下面会详细讲。
3. Bagging、Boosting与Random Forest:聚合的三种工程化形态
3.1 AdaBoost:通过调整样本权重来聚合弱学习器
如果说Bagging是"并行"制造多样性,那AdaBoost(Adaptive Boosting)就是"串行"制造多样性——每一步训练的新模型,都会更关注前一个模型犯错的样本。
它的核心机制是给每个样本维护一个权重 u_t^(n),每一轮迭代做三件事:
- 在当前样本权重分布下训练一个基模型 g_t;
- 计算 g_t 的加权错误率 ε_t;
- 根据 ε_t 更新样本权重:分错的样本权重调大,分对的样本权重调小,然后进入下一轮。
更新权重的公式长这样:
u_{t+1}^{(n)} = u_t^{(n)} * exp( -α_t * y^{(n)} * g_t(x^{(n)}) )其中 α_t = 0.5 * ln((1 - ε_t) / ε_t),这个系数同时充当了两个角色:一个是用来更新样本权重的缩放因子,另一个是最后聚合时每个模型的话语权。
为什么错分样本的权重要调大?用一个朴素的直觉解释:前面几轮模型普遍搞不定的样本,说明它们处于特征空间的"硬骨头"区域,新模型必须投入更多注意力才能啃下来。这样每一轮的新模型和之前的模型关注点天然不同,多样性就出来了。
从偏差-方差的角度看,AdaBoost 和Bagging的路径完全相反:Bagging在降方差,AdaBoost在降偏差。因为每一轮都在修正上一轮的残差,整个模型的系统性错误不断被压缩。这也是为什么 AdaBoost 的基模型可以非常弱——决策树桩(深度为1的决策树)就够了——只要每一轮都在原有基础上进步一点点,最后组合出来的强分类器就能达到很低的偏差。
需要注意,AdaBoost对噪声很敏感。如果数据集里有些标注错误的样本,AdaBoost会把大量权重集中在这些错标样本上,后面的模型被这些噪声样本带着跑,整体效果反而崩掉。我在真实项目里遇到过这种情况,后来改用带样本权重的GBDT或者直接把异常样本清洗掉才缓解。
3.2 Random Forest:在Bagging之上加一道随机性
随机森林是Bagging的一个"加强版",它和Bagging的区别在于:每棵决策树在训练时,每次节点分裂只随机挑选一部分特征作为候选。
Bagging通过数据采样制造了模型之间的差异,但问题是,如果数据里只有少数几个特征特别强(比如某个特征和标签的相关性极高),那所有树在节点分裂时几乎都会优先选那几个强特征。结果就是:树和树之间长得越来越像,多样性大打折扣,聚合效果被削弱。
随机森林的做法是,在每次分裂时,从全部 d 个特征里随机抽 m 个(通常取 m ≈ √d)来作为候选分裂特征。这样每棵树都只能用"局部信息"来做决策,树和树之间的差异性显著变大。极端情况下,一个强特征没有被抽到,树就只能依赖次优特征做分裂,相当于被迫从不同角度去看数据。
这里有一个反直觉但很重要的点:单棵随机森林的树,准确率通常低于单棵普通CART树。因为它可用的特征变少了。但随机森林整体的表现,却远优于相同数量的普通Bagging树。原因在于,聚合模型的最优状态不是"每个成员都最强",而是"成员之间足够不同,且错误相互独立"。
随机森林还有几个工程上很实用的附赠品:
- OOB(Out-of-Bag)评估:每棵树没用到的约36.8%样本,可以直接用来做无偏验证,不需要额外切验证集。我在项目里经常会用
oob_score快速评估模型,省时省力。 - 特征重要性:通过统计每个特征在所有树上带来的不纯度降低(Gini impurity decrease)总和,可以排序出特征重要性,用来做特征筛选。
3.3 聚合方法的选择逻辑:先判断误差来源
聊到这儿,你会发现一个有意思的现象:聚合模型不是"万能的胶水",不同聚合方法对应的是不同类型的问题。我在实际项目中总结了一套选择逻辑,分享给大家。
先问自己一个问题:你当前的单模型,主要误差来自偏差还是方差?
怎么判断?很简单。如果模型在训练集上的表现很好,但验证集一塌糊涂,那大概率是方差过高(过拟合)。这种情况下,优先选Bagging / Random Forest,通过并行聚合把方差压下去。逻辑是:每个高方差模型的期望近似是对的,只是波动大,平均之后波动被抵消,期望保留。
反过来,如果模型在训练集和验证集上的表现都很差,那说明偏差占主导(欠拟合)。这时候优先选Boosting,通过串行迭代去修正系统性错误。逻辑是:单个弱模型根本没有逼近真实函数的能力,只有通过不断强化对错分样本的关注,才能一步步逼近。
如果你的基础模型类型差异很大、各有各的擅长区域,那可以考虑Stacking / Conditional Blending——让模型按样本条件分工。这个方法的成本是复杂度更高、更容易过拟合,需要额外留出验证集来训练元模型。
不过这里有一点必须提醒:实际任务中,偏差和方差从来不是非黑即白。大部分情况下,单模型同时存在一定程度的偏差和方差。所以最终的实践往往不是单选一种,而是组合使用——比如先做特征工程降低偏差,再用随机森林降低方差。
3.4 聚合模型的理论安全网:为什么G不会比g_t差太多
林轩田课程里对聚合模型还有一个非常重要的理论保证:通过聚合,得到的 G 的验证误差有一个可以被约束的上界。
具体来说,对于均匀投票的 G,它的验证误差满足:
E_val(G) ≤ E_in(G) + Ω(模型复杂度)而模型复杂度这一项,并不会因为聚合了 T 个模型而线性增长。因为投票这个操作本身相当于让多个模型共同决定输出,G 的"有效复杂度"远低于"T 个模型复杂度之和"。
更关键的一条是:
E_val(G) 不会显著超过所有 g_t 的平均验证误差也就是说,聚合不保证选出最好的那个模型,但它保证给你一个"接近最好"的结果。这在现实中意味着什么?意味着你完全不需要花费大量时间在单模型超参数调优上——你只需要训练几个"还说得过去"的模型,然后聚合起来,效果往往比某个疯狂调参、在验证集上无数次拟合出的单模型更稳、更抗揍。
这一点对整个机器学习工程实践的影响是巨大的。很多团队在单模型上投入80%的时间调参,收益其实很低。把一部分精力转移到构建多样性强、聚合策略合理的模型集成上,往往更容易获得性能上的突破。
4. 实操验证与避坑记录:哪些聚合真的值得用
4.1 一个10分钟跑完的对照实验
光说不练假把式。我在本地用一组中等规模的数据(8000个样本,20个特征,二分类)做了一个快速实验,把几种聚合方式的实际效果跑给你看。代码很简单,大家可以直接在Jupyter里复现。
import numpy as np from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.tree import DecisionTreeClassifier from sklearn.linear_model import LogisticRegression from sklearn.ensemble import ( RandomForestClassifier, BaggingClassifier, AdaBoostClassifier, VotingClassifier, StackingClassifier ) from sklearn.metrics import roc_auc_score X, y = make_classification( n_samples=8000, n_features=20, n_informative=15, n_redundant=5, random_state=42, class_sep=0.8 ) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42 ) # 单棵决策树(不剪枝,高方差) single_tree = DecisionTreeClassifier(random_state=42) single_tree.fit(X_train, y_train) print("Single Tree AUC:", roc_auc_score(y_test, single_tree.predict_proba(X_test)[:, 1])) # Bagging + 决策树 bagging = BaggingClassifier( estimator=DecisionTreeClassifier(random_state=42), n_estimators=50, random_state=42, n_jobs=-1 ) bagging.fit(X_train, y_train) print("Bagging AUC:", roc_auc_score(y_test, bagging.predict_proba(X_test)[:, 1])) # Random Forest rf = RandomForestClassifier(n_estimators=50, max_features='sqrt', random_state=42, n_jobs=-1) rf.fit(X_train, y_train) print("Random Forest AUC:", roc_auc_score(y_test, rf.predict_proba(X_test)[:, 1])) # AdaBoost + 决策树桩 ada = AdaBoostClassifier( estimator=DecisionTreeClassifier(max_depth=1, random_state=42), n_estimators=50, random_state=42 ) ada.fit(X_train, y_train) print("AdaBoost AUC:", roc_auc_score(y_test, ada.predict_proba(X_test)[:, 1])) # 逻辑回归 + GBDT 的 Stacking from sklearn.ensemble import GradientBoostingClassifier lr = LogisticRegression(max_iter=1000) gbdt = GradientBoostingClassifier(n_estimators=50, random_state=42) stack = StackingClassifier( estimators=[('lr', lr), ('gbdt', gbdt)], final_estimator=LogisticRegression(), cv=5 ) stack.fit(X_train, y_train) print("Stacking AUC:", roc_auc_score(y_test, stack.predict_proba(X_test)[:, 1]))我自己跑下来的结果大概是这样的趋势(具体数值会因随机种子不同而波动,但相对关系稳定):
| 模型 | 验证 AUC | 说明 |
|---|---|---|
| 单棵决策树 | 0.82左右 | 高方差,训练集表现好但测试集一般 |
| Bagging + 决策树 | 0.88左右 | 方差下降,AUC明显提升 |
| Random Forest | 0.90左右 | 在Bagging基础上加了特征随机,多样性增强 |
| AdaBoost | 0.87左右 | 适合弱学习器,偏差持续下降 |
| Stacking | 0.91左右 | 逻辑回归+GBDT互补,融合效果最好 |
从这张表能直观看出两件事:第一,聚合在大多数情况下确实有效;第二,不同聚合方式的有效性取决于基模型的类型和你对误差来源的判断。决策树单棵方差大,Bagging和RF的收益最显著;GBDT本身已经很强,加Stacking融合逻辑回归,是从"多样性"里再榨一部分收益。
4.2 聚合模型最常见的几个坑,我全踩过
聚合模型虽然强,但绝不是无脑堆模型就能变强。我把这几年踩过的坑整理出来,每一个都是真金白银换来的教训。
坑一:基模型之间高度相关,聚合形同虚设。
我接过一个项目,团队里有同事用同一个XGBoost算法、同一个训练集、只改了随机种子训练了20个模型,然后做平均。结果自然是一点提升都没有——因为这些模型输出的预测结果相关系数接近0.99,平均下来跟没平均一样。
正确做法是保证模型差异性:换算法、换特征子集、换样本权重分布、换超参数配置。聚合的收益上限基本取决于基模型之间的相关系数,相关性越低,收益越大。
坑二:把验证集反复用于学习融合权重,导致信息泄漏。
做Stacking时,如果你用全部训练数据做交叉验证得到第一层模型的验证预测,然后用这些预测去训练第二层模型,过程中必须保证第一层的预测结果是"OOF"(Out-of-Fold)的——即每个样本的预测来自一个没有见过该样本训练的模型。
很多入门教程没强调这点,直接对训练集一整个fit再predict,然后拿去训练第二层。这样第二层看到的输入特征里有大量第一层模型的"记忆泄漏",在交叉验证时分数虚高,一到线上就崩。这是Kaggle新手最常见的问题之一,但也是industry里最容易犯的低级错误。
坑三:Boosting类算法在噪声大的数据上直接翻车。
前面提到过,AdaBoost会把大量权重集中在难分类的样本上。如果这个"难"是因为标注错误导致的,模型就会拼命去拟合这些噪声,整体泛化能力大幅下降。我在处理一份用户行为日志时遇到过,有个别渠道的标签误报率特别高,模型训练完一测,那些渠道的预测置信度反而最高——因为模型在噪声上"过拟合"了。
一个有效的缓解方法是:先用一个普通的随机森林或逻辑回归跑一遍,检查训练样本的预测置信度,把置信度高但标签异常的低占比样本筛出来做人工复核,清洗之后再跑Boosting。
坑四:盲目追求堆更多模型,导致线上推理延迟失控。
Stacking不是模型越多越好。每加一层模型,线上推理就要多算一次前向传播。我见过有的团队为了在排行榜上提升0.001的AUC,堆了十几个模型做多层Stacking,结果线上延迟从20ms涨到500ms,根本没法用。
我的建议是:把线上延迟预算当成一个超参数来对待。先定好推理时间预算,再反推最多能放几个模型。通常一层的Stacking(5-8个基模型+1个元模型)已经能拿到大部分融合收益,再堆层的边际收益极低。
4.3 聚合模型的边界:什么时候它救不了你
平心而论,聚合模型也不是万能的。有几个场景下,你花再多精力做集成,效果依然不理想。
数据质量极差的时候。如果特征本身包含大量噪声,或者标签存在系统性偏差,聚合模型能做的只是把这些噪声"平均得更平滑",但没办法创造信息。我常跟团队说,一个精心清洗的特征,胜过十个粗制滥造的模型集成。
数据量极少的时候。比如只有200个样本。这时候你训练10个模型,每个模型都欠拟合,聚合起来无非是10个欠拟合模型的平均,依然是欠拟合。小数据场景下,优先选强先验模型或做迁移学习,比堆集成靠谱。
非平稳环境下的在线推理。聚合模型的结构基本是固定的,如果数据分布随时间漂移,模型集成的整体漂移速度也不会比单模型好多少。这种情况下需要的是在线学习、模型监控和自动重训练机制,而不是一劳永逸的离线集成。
数据的强序列依赖。对于时间序列预测、自然语言处理等有明显序列结构的数据,标准聚合模型把样本当独立同分布来处理,天然不适合。你需要用序列模型本身的集成方式(比如时序交叉验证的Bagging),或者跑深度学习模型自带的集成策略。
说白了,聚合模型解决的是"模型不确定性的稳健化"问题,而不是"信息不足"问题。如果数据里根本没有足够的信号,任何聚合都只是在噪声里找安慰。
5. 我在实际项目里的三个判断经验
最后分享几个我这些年做模型集成时沉淀下来的判断经验,希望能帮你少走一点弯路。
第一,先想清楚聚合的目的,再选聚合方法。如果你的模型是欠拟合,最该做的是换更强的模型或者加特征,而不是急着上Bagging。Bagging降方差、Boosting降偏差,目标错了,方法再用劲也白搭。
第二,验证集在聚合流程里非常金贵。做Blending和Stacking时,验证集既承担了选模型的职责,又承担了学权重的职责。如果数据量不够,建议用嵌套交叉验证(nested CV)来防止权重学习时的信息泄漏。这个细节做对了,你的线上效果会稳定不少。
第三,基线模型的质量比数量更重要。我在一个信贷风控项目里试过30个模型的巨型Stacking,最终线上AUC只比5个精心挑选的模型Stacking高了0.002,但调试和推理成本和复杂度翻了好几倍。从那以后我给自己立了一条规矩:先做减法再做加法——先把特征和单模型做到80分的水平,再考虑聚合;聚合时从3-5个模型开始,确认有收益再加,而不是一上来就堆20个模型。
聚合模型这门课最打动我的地方,是它把一个很朴素的东方智慧——"兼听则明,偏信则暗"——用数学语言严密地表达了出来,并且给出了一套完整的工程实现路径。它不复杂,但很深刻。你用好了,它就是一把能在绝大多数建模任务里稳定带来收益的利器。