做机器学习项目时,经常遇到这样的情况:调参调了很久的单一模型,准确率卡在某个数值上不去,而换一个模型结构,错误样本又不完全重合。这时候如果把多个模型的预测结果合理地组合起来,往往能获得比任何单个模型都好的效果,这就是模型融合的价值。本文主要介绍两种在Python中实现简单且效果稳定的融合方法:Voting投票法和Blending多层融合法,并给出可以直接运行的完整代码。

一、为什么模型融合能提升效果
模型融合的理论基础可以用“三个臭皮匠顶个诸葛亮”来概括,但更严谨的解释是偏差与方差的互补。不同结构的模型,比如逻辑回归、随机森林、梯度提升树、支持向量机,它们的决策边界形状完全不同,犯错的样本点也往往不一样。当模型A和模型B对某个样本给出不同答案时,取平均或投票的结果通常比随机选择其中一个更可靠。
从统计角度看,多个弱相关模型的预测误差在合并时会相互抵消一部分,整体方差下降,泛化能力提升。这也是为什么在Kaggle等数据竞赛中,排名靠前的方案几乎都是多个模型的集成,而不是单个精调的模型。融合的前提是基模型之间要有“多样性”,如果三个模型完全一样,融合就没有意义。
需要注意的是,融合并非万能药。如果所有基模型都系统性地偏向同一个错误方向,融合后的结果依然会错。因此在实践中,通常会挑选原理差异大的异构模型参与融合,比如一个线性模型加两个树模型,再配一个基于距离的模型。
二、Voting投票法:硬投票与软投票的实现
Voting是最容易理解的融合方式。硬投票直接统计每个基模型预测的类别标签,得票最多的类别作为最终结果,类似选举中的少数服从多数。软投票则更进一步,它要求基模型能输出概率(调用predict_proba),把各模型对每个类别的概率取平均后,再选择概率最大的类别。软投票通常效果更好,因为它利用了概率中包含的置信度信息:一个模型51%的概率预测为正类,和99%的概率预测为正类,在硬投票里权重一样,但在软投票里差异很大。
sklearn中提供了VotingClassifier来快速实现这两种方式,下面是完整示例:
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import VotingClassifier, RandomForestClassifier, GradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
# 构造二分类数据集
X, y = make_classification(n_samples=2000, n_features=20, random_state=42)
# 准备三个异构基模型
lr = LogisticRegression(max_iter=1000)
rf = RandomForestClassifier(n_estimators=200, random_state=42)
svc = SVC(probability=True) # 软投票需要概率,SVC要开启probability参数
# 硬投票:按类别标签投票
voting_hard = VotingClassifier(
estimators=[('lr', lr), ('rf', rf), ('svc', svc)],
voting='hard'
)
# 软投票:按预测概率加权平均
voting_soft = VotingClassifier(
estimators=[('lr', lr), ('rf', rf), ('svc', svc)],
voting='soft'
)
print('逻辑回归得分: %.4f' % cross_val_score(lr, X, y, cv=5).mean())
print('随机森林得分: %.4f' % cross_val_score(rf, X, y, cv=5).mean())
print('硬投票得分: %.4f' % cross_val_score(voting_hard, X, y, cv=5).mean())
print('软投票得分: %.4f' % cross_val_score(voting_soft, X, y, cv=5).mean())
运行后通常能看到软投票的交叉验证分数高于硬投票,也高于大部分单模型。软投票还有一个进阶用法:通过weights参数给不同模型设置权重,比如表现更好的模型权重更大,示例如下:
voting_weighted = VotingClassifier(
estimators=[('lr', lr), ('rf', rf), ('svc', svc)],
voting='soft',
weights=[1, 2, 1.5] # 随机森林表现好,给更高权重
)
使用软投票时有一个细节要特别注意:某些模型如SVC默认不输出概率,必须设置probability=True,否则会抛出异常。另外对于回归任务,sklearn提供了VotingRegressor,原理是对多个回归模型的预测值直接取平均或加权平均,用法与分类版本类似。
三、Blending多层融合:用第一层预测作为第二层特征
Voting的局限在于融合逻辑过于简单,要么投票要么平均,无法学习出更复杂的组合方式。Blending的核心思想是把融合本身也交给一个模型来学:第一层放置多个异构基模型,它们对验证集的预测结果被当作新的特征,喂给第二层的元模型(也叫次级模型),由元模型学出最优的组合权重。
关键在于数据划分方式。如果直接用训练集既训练基模型又生成元特征,元模型会看到基模型已经“背下来”的数据,导致严重的数据泄漏,融合效果虚高。正确做法是把数据切成三部分:训练集训练基模型,验证集生成元特征并训练元模型,测试集只用于最终评估。下面是完整的原生实现:
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
X, y = make_classification(n_samples=5000, n_features=25, random_state=42)
# 三段式划分:训练集 / 验证集(生成元特征) / 测试集
X_train_full, X_test, y_train_full, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
X_train, X_val, y_train, y_val = train_test_split(X_train_full, y_train_full, test_size=0.25, random_state=42)
# 第一层:三个异构基模型
base_models = [
LogisticRegression(max_iter=1000),
RandomForestClassifier(n_estimators=300, random_state=42),
GradientBoostingClassifier(random_state=42)
]
# 基模型在训练集上训练,在验证集上输出概率作为元特征
val_features = []
test_features = []
for model in base_models:
model.fit(X_train, y_train)
val_features.append(model.predict_proba(X_val)[:, 1]) # 取正类概率
test_features.append(model.predict_proba(X_test)[:, 1])
# 拼接元特征矩阵:(样本数 x 基模型数)
X_val_meta = np.column_stack(val_features)
X_test_meta = np.column_stack(test_features)
# 第二层:元模型学习如何组合
meta_model = LogisticRegression()
meta_model.fit(X_val_meta, y_val)
final_pred = meta_model.predict(X_test_meta)
print('Blending最终准确率: %.4f' % accuracy_score(y_test, final_pred))
# 对比单个模型
for model in base_models:
print('单模型准确率: %.4f' % accuracy_score(y_test, model.predict(X_test)))
元模型的选择上,一般推荐简单的模型如逻辑回归或轻量梯度提升树。元模型太复杂容易在验证集上过拟合,因为元特征的数据量通常不大、特征维度也不高。对于多分类问题,可以把每个基模型对每个类别的概率都保留下来,元特征维度变成“基模型数乘以类别数”。
四、Voting与Blending的对比与选择建议
两种方案各有适用场景,可以从下面几个维度来衡量:
| 对比维度 | Voting | Blending |
|---|---|---|
| 实现难度 | 极低,几行代码 | 中等,需自己管理数据划分 |
| 融合逻辑 | 固定规则(投票或平均) | 由元模型学习最优组合 |
| 数据利用 | 全部数据可直接交叉验证 | 需要额外划分验证集,训练数据变少 |
| 过拟合风险 | 低 | 元模型设计不当时偏高 |
| 上限 | 中等 | 更高,接近Stacking |
实际项目中的建议是:先快速尝试软投票作为基线,如果各模型结果差异明显且存在明显互补性,再投入精力做Blending。Blending还可以进一步扩展成K折Stacking,即用交叉验证的方式生成元特征,让每个样本都有被基模型预测的机会,数据利用更充分。另外,参与融合的基模型数量控制在3到8个比较合适,过多模型带来的收益递减,还会增加维护成本。
最后提醒一点:融合提升的前提是单个模型本身足够强。如果基模型准确率都只有60%,融合很难带来质变。正确的路径是先把每个基模型调到合理水平,确保它们各有侧重,再通过Voting或Blending把这些差异转化为整体收益。