已经选择了几个分类算法,希望让它们共同给出答案时,可以用 VotingClassifier。硬投票统计各模型预测的类别票数;软投票平均各模型的类别概率,再选概率最高的类别。两种方法可能给出不同结果,组合模型也不保证比单模型更好。下面训练三个 Iris 教学分类器,手工计算投票结果,与 API 输出逐项核对。
准备三种模型和同一个输入契约
本文组合标准化后的逻辑回归、GaussianNB、深度为 3 的决策树。所有子模型接收相同的四列输入和同一套类别含义。逻辑回归的标准化放在它自己的 Pipeline 内,避免把测试数据提前用于学习预处理。

VotingClassifier API说明 fit() 会克隆并训练传入的估计器,拟合后的模型保存在 estimators_ 和 named_estimators_;原始 estimators 列表中的对象不会因此变成已拟合模型。官方集成学习指南说明硬投票取多数类别,软投票取平均或加权平均概率的最大值。
代码于 2026 年 10 月 1 日在 Windows、Python 3.11.15、scikit-learn 1.7.2、NumPy 2.4.6、SciPy 1.17.1 运行。使用 Iris 内置教学数据,结果只属于这个固定划分,不是通用效果排名或生产环境评测。先创建环境:
python -m venv .venv
.venv\Scripts\python -m pip install "scikit-learn==1.7.2" "numpy==2.4.6" "scipy==1.17.1"
训练投票器,并分别核对票数与概率
将代码保存为 voting_check.py,运行 .venv\Scripts\python voting_check.py。这里把 y 改成真实类别名称字符串,便于检查内部整数编码如何还原;测试集留出 30%,权重默认相等。
import numpy as np
from sklearn.datasets import load_iris
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.tree import DecisionTreeClassifier
data = load_iris()
X = data.data
y = data.target_names[data.target]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, stratify=y, random_state=7)
estimators = [
("lr", make_pipeline(StandardScaler(), LogisticRegression(max_iter=500))),
("nb", GaussianNB()),
("tree", DecisionTreeClassifier(max_depth=3, random_state=7))
]
hard = VotingClassifier(estimators=estimators, voting="hard", n_jobs=1)
soft = VotingClassifier(estimators=estimators, voting="soft", n_jobs=1)
hard.fit(X_train, y_train)
soft.fit(X_train, y_train)
print("类别顺序:", soft.classes_.tolist())
hard_encoded = np.column_stack([
est.predict(X_test) for est in hard.estimators_])
manual_encoded = np.array([
np.argmax(np.bincount(row, minlength=len(hard.classes_)))
for row in hard_encoded])
manual_hard = hard.le_.inverse_transform(manual_encoded)
assert np.array_equal(manual_hard, hard.predict(X_test))
for row in range(3):
print("样本", row, "子模型票:",
hard.le_.inverse_transform(hard_encoded[row]).tolist(),
"硬投票:", manual_hard[row])
probabilities = np.stack([
est.predict_proba(X_test) for est in soft.estimators_])
manual_soft_proba = probabilities.mean(axis=0)
manual_soft = soft.classes_[manual_soft_proba.argmax(axis=1)]
assert np.allclose(manual_soft_proba, soft.predict_proba(X_test))
assert np.array_equal(manual_soft, soft.predict(X_test))
print("第一条平均概率:", np.round(manual_soft_proba[0], 4).tolist())
print("硬/软投票人工计算与API输出均一致")
print("硬投票测试准确率:", f"{accuracy_score(y_test, hard.predict(X_test)):.4f}")
print("软投票测试准确率:", f"{accuracy_score(y_test, soft.predict(X_test)):.4f}")
硬投票的子模型在内部整数编码标签上训练,所以手工计算票数后用 hard.le_.inverse_transform() 还原名称。不要将子模型给出的整数 1 直接当成你的业务类别 1;类别映射取决于该投票器的 classes_。软投票则堆叠三个子模型的 predict_proba(),沿模型维度平均。
本次实际运行结果说明什么
类别顺序: ['setosa', 'versicolor', 'virginica']
样本 0 子模型票: ['versicolor', 'versicolor', 'versicolor'] 硬投票: versicolor
样本 1 子模型票: ['versicolor', 'versicolor', 'versicolor'] 硬投票: versicolor
样本 2 子模型票: ['virginica', 'virginica', 'virginica'] 硬投票: virginica
第一条平均概率: [0.0517, 0.9457, 0.0026]
硬/软投票人工计算与API输出均一致
硬投票测试准确率: 0.9778
软投票测试准确率: 0.9556
第一条样本在三个类别上的平均概率为 [0.0517, 0.9457, 0.0026];按打印的 classes_ 顺序,对应 setosa、versicolor、virginica,因此软投票选 versicolor。断言核对全部 45 条测试记录的手工硬投票与 predict() 一致,手工平均概率与 predict_proba() 一致,并确认平均概率最大值对应最终类别。
本次硬投票测试准确率为 0.9778,软投票为 0.9556。这个结果说明“有概率的软投票”并不自动胜过多数票;它没有证明硬投票普遍更优。若根据这次测试表现选了方法,就不能再把同一测试集当作完全独立的最终验收数据。
在业务模型中怎样选择投票方式
- 只有类别输出,或各模型概率刻度不可靠:先考虑硬投票,并查看模型是否总在相同样本上犯错。
- 所有子模型提供 predict_proba(),且概率质量经过独立检查:可以在训练内部的验证流程中比较软投票。官方 API 推荐软投票用于概率校准良好的分类器,本例没有对三个子模型额外做概率校准。
- 要使用不相等权重:weights 必须与估计器列表顺序对应,只在训练内部的验证数据上决定;不能一边看最终测试答案一边调权重。
- 部署成本有限:记录每个子模型和整体的延迟、内存及依赖。推理需要运行多个模型,这个小样本实验没有验证上线性能。
常见问题
可以把已训练好的模型直接传进去继续使用吗?
VotingClassifier 的 fit() 会克隆并重新训练传入对象。此教程适合统一训练这些算法,不适合直接拼接几份外部已拟合模型文件;这种需求需要另设计预测组合流程并核对所有模型的特征与类别顺序。
硬投票出现同票,或者软投票报缺少概率接口怎么办?
官方指南说明硬投票同票时按类别升序决定,本文使用 np.argmax() 的手工核对与该规则一致;不能把它解释为业务优先级。软投票要求所有有效子模型支持 predict_proba(),缺少接口时应更换合适模型或使用硬投票,不能把任意决策分数直接当概率平均。
下一步可以先在训练集内部选择一组有差异的子模型,保留类别字典,导出一小批样本的每模型票数和概率供人工核对,再用新的独立样本验收完整方案。
Ai菜鸟网。发布者:AI小管家,转载请注明出处:https://www.alyyhw.com/30818.html