1. 投票法简介
在之前的学习,我们了解到一个通过数据学习得来的算法模型在实际预测中,存在两种误差,即方差和偏差。其中偏差是指预测值和实际值的差值;方差是指模型在同一分布的不同数据集上的扰动,即由于样本数据不同而带来的误差。
而我们不断开发新的机器学习算法开发目的就是寻找方法来降低这些误差。
而投票法就是这样的方法,它是集成学习中常用的技巧,可以帮助我们提高模型的泛化能力,减少模型的错误率。
这也符合我们经常听到的所谓兼听则明的思想。集思广益大部分时候可以获得比单个个体的想法更好,这个是投票法朴素的解释。
投票法在回归模型与分类模型上均可使用:
- 回归投票法:预测结果是所有模型预测结果的平均值。
- 分类投票法:预测结果是所有模型种出现最多的预测结果。
分类投票法又可以被划分为硬投票与软投票:
- 硬投票:预测结果是所有投票结果最多出现的类。
- 软投票:预测结果是所有投票结果中概率加和最大的类。
2. 投票法的原理分析
投票法是一种遵循少数服从多数原则的集成学习模型,通过多个模型的集成降低方差,从而提高模型的鲁棒性。
在理想情况下,投票法的预测效果应当优于任何一个基模型的预测效果。
下面我们使用一个例子说明硬投票:
对于某个样本:
模型 1 的预测结果是 类别 A
模型 2 的预测结果是 类别 B
模型 3 的预测结果是 类别 B
有2/3的模型预测结果是B,因此硬投票法的预测结果是B
同样的例子说明软投票:
对于某个样本:
模型 1 的预测结果是 类别 A 的概率为 99%
模型 2 的预测结果是 类别 A 的概率为 49%
模型 3 的预测结果是 类别 A 的概率为 49%
最终对于类别A的预测概率的平均是 (99 + 49 + 49) / 3 = 65.67%,因此软投票法的预测结果是A。
从这个例子我们可以看出,软投票法与硬投票法可以得出完全不同的结论。相对于硬投票,软投票法考虑到了预测概率这一额外的信息,
因此可以得出比硬投票法更加准确的预测结果。
在投票法中,我们还需要考虑到不同的基模型可能产生的影响。理论上,基模型可以是任何已被训练好的模型。
但在实际应用上,想要投票法产生较好的结果,需要满足两个条件:
- 基模型之间的效果不能差别过大。当某个基模型相对于其他基模型效果过差时,该模型很可能成为噪声。
- 基模型之间应该有较小的同质性。例如在基模型预测效果近似的情况下,基于树模型与线性模型的投票,往往优于两个树模型或两个线性模型。
3. 算法实现
- 手动实现,不使用sklearn中封装好的算法:
我们首先手动实现硬投票法实现分类问题,我们使用葡萄酒数据集,并使用KNN,决策树和LR模型
1.1 导入数据集,以及相应的算法模型:
import pandas as pd
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LogisticRegression
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
导入数据集,并拆分为训练集和测试集
# 加葡萄酒据集
wine = datasets.load_wine()
X, y = wine.data, wine.target
X_train, X_test, y_train, y_test = train_test_split(X, y , test_size = 0.3)
print(X_train.shape, y_train.shape)
print(X_test.shape, y_test.shape)
print(X[:5], y[:5])
>>>
(124, 13) (124,)
(54, 13) (54,)
[[1.423e+01 1.710e+00 2.430e+00 1.560e+01 1.270e+02 2.800e+00 3.060e+00
2.800e-01 2.290e+00 5.640e+00 1.040e+00 3.920e+00 1.065e+03]
[1.320e+01 1.780e+00 2.140e+00 1.120e+01 1.000e+02 2.650e+00 2.760e+00
2.600e-01 1.280e+00 4.380e+00 1.050e+00 3.400e+00 1.050e+03]
[1.316e+01 2.360e+00 2.670e+00 1.860e+01 1.010e+02 2.800e+00 3.240e+00
3.000e-01 2.810e+00 5.680e+00 1.030e+00 3.170e+00 1.185e+03]
[1.437e+01 1.950e+00 2.500e+00 1.680e+01 1.130e+02 3.850e+00 3.490e+00
2.400e-01 2.180e+00 7.800e+00 8.600e-01 3.450e+00 1.480e+03]
[1.324e+01 2.590e+00 2.870e+00 2.100e+01 1.180e+02 2.800e+00 2.690e+00
3.900e-01 1.820e+00 4.320e+00 1.040e+00 2.930e+00 7.350e+02]] [0 0 0 0 0]
1.2 创建模型并输出预测结果精度
knn = KNeighborsClassifier(n_neighbors= 2)
dt = DecisionTreeClassifier(max_depth=2)
lr = LogisticRegression(tol= 0.1, random_state= 10, solver='lbfgs')
for mod in [knn, dt, lr]:
mod.fit(X_train, y_train)
y_predict = mod.predict(X_test)
mod_score = accuracy_score(y_test, y_predict)
print('using modle {}, the accury is {:.3f}'.format(mod, mod_score))
>>>
using modle KNeighborsClassifier(n_neighbors=2), the accury is 0.648
using modle DecisionTreeClassifier(max_depth=2), the accury is 0.889
using modle LogisticRegression(random_state=10, tol=0.1), the accury is 0.944
1.3 下面手动实现hard voting 即硬投票
我们首先打印出上面三个算法的预测结果,打印前20个
knn.fit(X_train, y_train)
y_knn = knn.predict(X_test)
print(y_knn[:20])
dt.fit(X_train, y_train)
y_dt = dt.predict(X_test)
print(y_dt[:20])
lr.fit(X_train, y_train)
y_lr = lr.predict(X_test)
print(y_lr[:20])
>>>
[0 2 1 2 1 0 1 1 1 0 0 2 2 2 0 0 1 0 1 1]
[1 0 1 2 2 1 1 2 1 0 2 1 1 2 0 0 1 0 1 2]
[1 1 1 2 2 1 1 2 1 0 0 1 1 2 0 0 1 0 1 2]
下面使用投票法结合三者
hard_predict = []
for i in range(y_test.shape[0]):
counts = [0 for _ in range(2)] #二分类,先初始化两个类别的票数均为0
counts[y_knn[i]] += 1
counts[y_dt[i]] += 1
counts[y_lr[i]] += 1
# get the max valve with hard voting for final predictions
final = np.argmax(counts)
hard_predict.append(final)
print(hard_predict[:20])
>>>
[1, 0, 1, 2, 2, 1, 1, 2, 1, 0, 0, 1, 1, 2, 0, 0, 1, 0, 1, 2]
使用voting算法的默认参数获得的正确率为: 0.926
对比集成的结果和上面3个模型的预测,可以明显的看到精度的提升。
- 在sklearn的ensemble 模块中也封装了 VotingClassifier用于分类可以直接调用
下面我们使用一个完整的例子演示投票法的使用:
首先我们创建一个1000个样本,20个特征的随机数据集:
from sklearn.datasets import make_classification
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.model_selection import cross_val_score
import matplotlib.pyplot as plt
plt.style.use('bmh')
from sklearn.ensemble import VotingClassifier
from sklearn.datasets import make_classification
# define dataset
X, y = make_classification(n_samples=1000, n_features=20, n_informative=15, n_redundant=5, random_state=2)
# summarize the dataset
print(X.shape, y.shape)
>>>(1000, 20) (1000,)
2.2 我们使用多个KNN模型作为基模型演示投票法,其中每个模型采用不同的邻居值K参数:
# get a voting ensemble of models
def get_voting():
# define the base models
models = list()
models.append(('knn1', KNeighborsClassifier(n_neighbors=1)))
models.append(('knn3', KNeighborsClassifier(n_neighbors=3)))
models.append(('knn5', KNeighborsClassifier(n_neighbors=5)))
models.append(('knn7', KNeighborsClassifier(n_neighbors=7)))
models.append(('knn9', KNeighborsClassifier(n_neighbors=9)))
# define the voting ensemble
ensemble = VotingClassifier(estimators=models, voting='hard')
return ensemble
"""
2.3 创建一个模型列表来评估投票带来的提升,
包括KNN模型配置的每个独立版本和硬投票模型。
下面的get_models()函数可以为我们创建模型列表进行评估。
# get a list of models to evaluate
def get_models():
models = dict()
models['knn1'] = KNeighborsClassifier(n_neighbors=1)
models['knn3'] = KNeighborsClassifier(n_neighbors=3)
models['knn5'] = KNeighborsClassifier(n_neighbors=5)
models['knn7'] = KNeighborsClassifier(n_neighbors=7)
models['knn9'] = KNeighborsClassifier(n_neighbors=9)
models['hard_voting'] = get_voting()
return models
# 下面的evaluate_model()函数接收一个模型实例,并以分层10倍交叉验证三次重复的分数列表的形式返回。
# evaluate a give model using cross-validation
def evaluate_model(model, X, y):
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
scores = cross_val_score(model, X, y, scoring='accuracy', cv=cv, n_jobs=-1, error_score='raise')
return scores
2.4 报告每个算法的平均性能,还可以创建一个箱形图和须状图来比较每个算法的精度分数分布。
models = get_models()
# evaluate the models and store results
results, names = list(), list()
for name, model in models.items():
scores = evaluate_model(model, X, y)
results.append(scores)
names.append(name)
print('>%s %.3f (%.3f)' % (name, np.mean(scores), np.std(scores)))
# plot model performance for comparison
plt.boxplot(results, labels=names, showmeans=True)
plt.show()
image.png
本文结
参考:
Datawhale开源项目:机器学习集成学习与模型融合(基于python)
作者: 李祖贤,陈琰钰,赵可,杨毅远,薛传雨
网友评论