投票法的原理和案例分析


转载声明

原文作者:Datawhale学习社区

原文链接:Datawhale学习社区

著作权归作者所有,任何形式的转载都请联系作者。


投票法的原理分析

集成学习就是要发挥集体决策的优势,以单个分类模型的分类结果为基础,采用少数服从多数的原则确定模型预测的类别标签。通过多个模型的集成降低方差,从而提高模型的准确性。在理想情况下,投票法的预测效果应当优于任何一个基模型的预测效果。

降低方差对偏差有什么影响??

投票法在回归模型与分类模型上均可使用:

  • 回归投票法:预测结果是所有模型预测结果的平均值。
  • 分类投票法:预测结果是所有模型种出现最多的预测结果。

分类投票法又可以被划分为硬投票与软投票:

  • 硬投票:预测结果是所有投票结果最多出现的类。
  • 软投票:预测结果是所有投票结果中概率加和最大的类。

投票法的注意事项

  • 基模型之间的效果不能差别过大。当某个基模型相对于其他基模型效果过差时,该模型很可能成为噪声。

  • 基模型之间应该有较小的同质性。例如在基模型预测效果近似的情况下,基于树模型与线性模型的投票,往往优于两个树模型或两个线性模型。

  • 这意味着所有模型对预测的贡献是一样的。如果一些模型在某些情况下很好,而在其他情况下很差,这是使用投票法时需要考虑到的一个问题。


基于sklearn,介绍pipe管道的使用以及voting的使用

Sklearn中提供了回归投票器(VotingRegressor)分类投票器(VotingClassifier) 两个投票方法。

这两种模型的操作方式相同,并采用相同的参数。
使用模型需要提供一个模型列表,列表中每个模型采用元组Tuple的结构表示,第一个元素代表名称,第二个元素代表模型,需要保证每个模型必须拥有唯一的名称。

例如:定义两个模型

from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.ensemble import VotingClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

estimators参数,

models = [('lr',LogisticRegression()),('svm',SVC())]
ensemble = VotingClassifier(estimators=models)

可以通过pipeline简化预处理模型过程

models = [('lr',LogisticRegression()),
          ('svm',make_pipeline(StandardScaler(),SVC()))]
ensemble = VotingClassifier(estimators=models)

voting参数,选择分类投票器类型

models = [('lr',LogisticRegression()),('svm',SVC())]
ensemble = VotingClassifier(estimators=models, voting='soft')

实际案例

创建一个1000个样本,20个特征的随机数据集:

# test classification dataset
from sklearn.datasets import make_classification
# define dataset
def get_dataset():
    X, y = make_classification(  n_samples=1000, 
                                 n_features=20,
                                 n_informative=15,
                                 n_redundant=5, 
                                 random_state=2)
    # summarize the dataset
    return X,y

使用多个KNN模型作为基模型演示投票法,其中每个模型采用不同的邻居值K参数:

from sklearn.neighbors import KNeighborsClassifier
from matplotlib import pyplot

# 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

创建一个模型字典来评估投票带来的提升,包括KNN模型配置的每个独立版本和硬投票模型

# 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
#Added by ljq

from sklearn.model_selection import cross_val_score

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

我们得到的结果如下:

knn1 0.873 (0.030)
knn3 0.889 (0.038)
knn5 0.895 (0.031)
knn7 0.899 (0.035)
knn9 0.900 (0.033)
hard_voting 0.902 (0.034)

显然投票的效果略大于任何一个基模型。通过箱形图我们可以看到硬投票方法对交叉验证整体预测结果分布带来的提升。


Box-Plot

拓展练习

用投票法对鸢尾花数据进行分类

# -*- coding: utf-8 -*-
"""
Created on Wed Apr 14 21:52:32 2021

@author: PC
"""
# 导入工具包
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
# 导入数据集
from sklearn import datasets
# 导入管道
from sklearn.pipeline import make_pipeline
# 数据预处理:归一化
from sklearn.preprocessing import StandardScaler
# logistic回归
from sklearn.linear_model import LogisticRegression
# 分类向量机
from sklearn.svm import SVC
# 分类决策树
from sklearn.tree import DecisionTreeClassifier
# 导入投票器
from sklearn.ensemble import VotingClassifier
# 评分
from sklearn.model_selection import cross_val_score
#直接k折检验,不用分割数据集了
from sklearn.model_selection import RepeatedStratifiedKFold

先对数据进行观察

def load_data():
    # 下载数据并展示内容关键字
    iris = datasets.load_iris()
    print(iris.keys()) 
    print(iris.data)               # 展示所有数据
    print(iris.feature_names)       # 展示特征名
    print(iris.target)              # 展示目标结果
    print(iris.target_names)        # 展示目标结果含义
    print(type(iris.data))          # 数据类型
    print(type(iris.target))        # 目标结果类型
    print(iris.data.shape)          # 确认行列维度
    print(iris.target.shape)

load_data()

共有150个样本,4个特征,1个目标值

dict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename'])

[[5.1 3.5 1.4 0.2]
 [4.9 3.  1.4 0.2]
 [4.7 3.2 1.3 0.2]
 [4.6 3.1 1.5 0.2]
         …
 [5.9 3.  5.1 1.8]]

['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']

[0 0 0 0 ……1 1 1 1 1 …… 2 2 2]

['setosa' 'versicolor' 'virginica']

<class 'numpy.ndarray'>

<class 'numpy.ndarray'>

(150, 4)

(150,)
models = [('lr',LogisticRegression()),
          ('svm',make_pipeline(StandardScaler(),SVC(probability=True))),
          ('tree', DecisionTreeClassifier(random_state=10))]

ensemble = VotingClassifier(estimators=models,voting='hard')
    

def get_models():
    models = dict()
    models['lr'] = LogisticRegression()
    models['svm'] = make_pipeline(StandardScaler(),SVC(probability=True))
    models['tree'] = DecisionTreeClassifier(random_state=10)
    models['hard_voting'] = ensemble
    return models

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

# 给X,Y进行赋值
iris = datasets.load_iris()
X = iris.data
y = iris.target
        
# get the models to evaluate
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)

结果

>lr 0.964 (0.041)
>svm 0.962 (0.048)
>tree 0.951 (0.057)
>hard_voting 0.964 (0.041)
Figure 2021-04-15 005414.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,427评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,551评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,747评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,939评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,955评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,737评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,448评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,352评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,834评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,992评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,133评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,815评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,477评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,022评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,147评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,398评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,077评论 2 355

推荐阅读更多精彩内容