🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎
📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝
📣系列专栏 - 机器学习【ML】 自然语言处理【NLP】 深度学习【DL】
🖍foreword
✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。
如果你对这个系列感兴趣的话,可以关注订阅哟👋
文章目录
在本章中,您将从使用 XGBoost 赢得 Kaggle 比赛的Kaggle 大师那里学到宝贵的技巧和窍门。虽然我们不会在这里参加 Kaggle 比赛,但您将获得的技能通常可以应用于构建更强大的机器学习模型。具体来说,您将了解为什么额外的保留集至关重要,如何使用均值编码对新的数据列进行特征设计,如何实现VotingClassifier和VotingRegressor以构建不相关的机器学习集成,以及堆叠最终模型的优势.
在本章中,我们将涵盖以下主要主题:
探索 Kaggle 比赛
设计新的数据列
构建不相关的集合
堆叠最终模型
“我只使用了 XGBoost(尝试了其他的,但没有一个表现好到足以最终进入我的合奏)。”
– Qingchen Wang, Kaggle Winner
(Liberty Mutual Property Inspection, Winner's Interview: Qingchen Wang - 菜鸡一枚 - 博客园)
在本节中,我们将通过查看 Kaggle 竞赛的简史来研究 Kaggle 竞赛,它们是如何是结构化的,以及与验证/测试集区分开来的保留/测试集的重要性。
XGBoost 建立了它作为领先的机器学习算法在赢得 Kaggle 比赛方面取得了无与伦比的成功。XGBoost 经常与深度学习一起出现在获奖组合中神经网络等模型,除了直接获胜。XGBoost Kaggle 竞赛获胜者的示例列表显示在分布式(深度)机器学习社区网页上,网址为xgboost/demo at master · dmlc/xgboost · GitHub。如需更多 XGBoost Kaggle 比赛获胜者的名单,可以通过Kaggle 比赛的获胜解决方案(Winning solutions of kaggle competitions)来研究获胜模型。
笔记
虽然 XGBoost 经常出现在获奖者中,但其他机器学习模型也出现了。
如第 5 章XGBoost Unveiled中所述,Kaggle 竞赛是机器学习竞赛,机器学习从业者相互竞争以获得最好的分数并赢得现金奖励。当 XGBoost 在 2014 年的希格斯玻色子机器学习挑战赛中一炮而红时,它立即跃居排行榜,成为 Kaggle 比赛中最受欢迎的机器学习算法之一。
在 2014 年至 2018 年期间,XGBoost 在表格数据(按行和列组织的数据)与神经网络具有优势的非结构化数据(如图像或文本)形成对比时,始终优于竞争对手。随着2017 年LightGBM的出现,一个闪电般的微软在梯度提升的版本中,XGBoost 终于与表格数据进行了一些真正的竞争。
以下介绍性论文LightGBM: A High Efficient Gradient Boosting Decision Tree由八位作者撰写,推荐用于 LightGBM 的介绍:https ://papers.nips.cc/paper/6907-lightgbm-a-highly-efficient-梯度提升决策树.pdf。
实现一台出色的机器Kaggle 中的 XGBoost 或 LightGBM 等算法比赛还不够。同样,微调模型的超参数通常是不够的。虽然单个模型预测很重要,但设计新数据和组合最优模型以获得更高分数同样重要。
值得了解Kaggle 竞赛的结构,以深入了解非相关集成构建和堆叠等技术为何如此普遍。此外,如果你选择走这条路,探索 Kaggle 比赛的结构会让你有信心参加 Kaggle 比赛。
小费
Kaggle 推荐房屋价格:高级回归技术,https ://www.kaggle.com/c/house-prices-advanced-regression-techniques ,适用于希望从基础知识过渡到高级竞赛的机器学习学生。这是许多不提供现金奖励的知识竞赛之一。
Kaggle 比赛存在于 Kaggle 网站上。以下是XGBoost 用户 Owen Zhang 赢得的 2015 年Avito Context Ad Clicks的网站: https ://www.kaggle.com/c/avito-context-ad-clicks/overview 。包括 Owen Zhang 在内的几位 XGBoost Kaggle 比赛的获胜者都是 2015 年的,这表明 XGBoost 在 Tianqi Chin 的标志性论文XGBoost: A Scalable Tree Boosting System于 2016 年发表之前的发行量:https ://arxiv.org/pdf/1603.02754.pdf 。
这是Avito Context Ad Clicks网站的顶部:
图 9.1 – Avito Context Ad Clicks Kaggle 竞赛网站
此概览页面对比赛进行了如下说明:
Overview旁边的其他链接(以蓝色突出显示)包括Data,您可以在其中访问比赛数据。
Notebooks,Kaggler 发布解决方案和入门笔记本的地方。
Discussion,Kagglers 发布和回答问题的地方。
Leaderboard,显示最高分的地方。
Rules,它解释了比赛的运作方式。
此外,请注意最右侧的延迟提交链接,这表明即使比赛结束,提交仍然可以接受,这是 Kaggle 的一般政策。
要下载数据,您需要通过注册一个免费帐户来参加比赛。数据通常分为两个数据集,training.csv是用于构建模型的训练集,而test.csv是用于对模型进行评分的测试集。提交模型后,您将在公共排行榜上获得分数。在比赛结束时,根据私人测试集提交最终模型以确定获胜的解决方案。
重要的是要区分为 Kaggle 比赛构建机器学习模型和自己构建它们。到目前为止,我们已将数据集拆分为训练集和测试集,以确保我们的模型能够很好地泛化。然而,在 Kaggle 比赛中,模型必须在竞争环境中进行测试。出于这个原因,来自测试集的数据仍然是隐藏的。
training.csv:这是您自己训练和评分模型的地方。该训练集应使用train_test_split或cross_val_score拆分为自己的训练集和测试集,以构建能够很好地泛化到新数据的模型。训练期间使用的测试集通常称为验证集,因为它们验证模型。
test.csv:这是一个单独的保留集。在您准备好最终模型以测试从未见过的数据之前,您不会使用测试集。隐藏测试集的目的是保持比赛的完整性。测试数据对参与者隐藏,只有在参与者提交模型后才会显示结果。
保持在为研究或工业建立模型时预留测试。当一个模型使用它已经看到的数据进行测试时,该模型有过度拟合测试集的风险,这种可能性经常出现在 Kaggle 比赛中,当竞争对手痴迷于将自己在公共排行榜中的位置提高千分之几时。
Kaggle 比赛与现实世界相交关于这个保留集。构建机器学习模型的目的是使用未知数据做出准确的预测。例如,如果一个模型在训练集上给出了 100% 的准确率,但在未知数据上只给出了 50% 的准确率,那么该模型基本上一文不值。
在测试集上验证模型和在保留集上测试模型之间的这种区别非常重要。
以下是您自己验证和测试机器学习模型的一般方法:
将数据拆分为训练集和保留集:将保留集远离并抵制查看它的诱惑。
将训练集拆分为训练集和测试集或使用交叉验证:在训练集上拟合新模型并验证模型,来回进行以提高分数。
获得最终模型后,在保留集上对其进行测试:这是对模型的真正测试。如果分数低于预期,则返回步骤 2并重复。不要——这很重要——使用保持集作为新的验证集,来回调整超参数。当这种情况发生时,模型正在调整自身以匹配保留集,这首先破坏了保留集的目的。
在 Kaggle 比赛中,调整机器学习模型过于接近测试集将不起作用。Kaggle 经常将测试集拆分为额外的公共和私有组件。公共测试集让参与者有机会对他们的模型进行评分,并在此过程中进行改进、调整和重新提交。私人测试集直到比赛的最后一天才公布。虽然显示了公共测试集的排名,但比赛获胜者是根据看不见的测试集的结果宣布的。
赢得 Kaggle 比赛需要在私人测试集上获得尽可能高的分数。在 Kaggle 比赛中,每个百分点都很重要。对这种精度的需求,有时被业界嘲笑,导致了创新的机器学习实践来提高分数。理解本章中介绍的这些技术,可以导致更强大的模型和对机器学习的整体更深入的理解。
“我几乎总能找到我想做的开源代码,而且我的时间花在研究和特征工程上要好得多。”
– Owen Zhang,Kaggle 获胜者
许多 Kagglers 和数据科学家承认在研究和特征上花费了大量时间工程。在本节中,我们将使用pandas来设计新的数据列。
机器学习模型如下和他们训练的数据一样好。当数据不足时,建立一个健壮的机器学习模型是不可能的。
一个更具启发性的问题是数据是否可以改进。当从其他列中提取新数据时,这些新数据列被称为是经过工程设计的。
特征工程是从原始列开发新数据列的过程。问题不是你是否应该实施特征工程,而是你应该实施多少特征工程。
让我们在一个预测Uber和Lyft打车费用的数据集上练习特征工程。
除了托管比赛中,Kaggle 举办了大量比赛包括公共数据集的数据集,例如如下,它预测 Uber 和 Lyft 出租车价格:https ://www.kaggle.com/ravi72munde/uber-lyft-cab-prices:
要开始,首先导入本节所需的所有库和模块并消除警告:
- import pandas as pd
- import numpy as np
- from sklearn.model_selection import cross_val_score
- from xgboost import XGBClassifier, XGBRFClassifier
- from sklearn.ensemble import RandomForestClassifier, StackingClassifier
- from sklearn.linear_model import LogisticRegression
- from sklearn.model_selection import train_test_split, StratifiedKFold
- from sklearn.metrics import accuracy_score
- from sklearn.ensemble import VotingClassifier
- import warnings
- warnings.filterwarnings('ignore')
接下来,加载“cab_rides.csv” CSV 文件并查看前五行。将nrows限制为10000以加快计算。有总共 600,000+ 行:
- df = pd.read_csv('cab_rides.csv', nrows=10000)
- df.head()
这是预期的输出:
图 9.2 – 出租车乘坐数据集
该显示显示了广泛的列,包括分类特征和时间戳。
回想一下df.info()还提供有关列类型的信息:
df.info()
- <class 'pandas.core.frame.DataFrame'>
- RangeIndex: 10000 entries, 0 to 9999
- Data columns (total 10 columns):
-
- # Column Non-Null Count Dtype
- --- ------ -------------- -----
- 0 distance 10000 non-null float64
- 1 cab_type 10000 non-null object
- 2 time_stamp 10000 non-null int64
- 3 destination 10000 non-null object
- 4 source 10000 non-null object
- 5 price 9227 non-null float64
- 6 surge_multiplier 10000 non-null float64
- 7 id 10000 non-null object
- 8 product_id 10000 non-null object
- 9 name 10000 non-null object
-
- dtypes: float64(3), int64(1), object(6)
- memory usage: 781.4+ KB
从输出中可以看出,由于非空浮点数少于10,000 个,因此价格列中存在空值。
df[df.isna().any(axis=1)]
以下是输出的前五行:
图 9.3 – cab rides 数据集中的空值
如您所见,这些行没有什么特别明显的地方。可能是从未记录过乘车的价格。
由于价格是目标列,这些可以使用dropna使用inplace =True参数删除行,以确保删除发生在 DataFrame 中:
df.dropna(inplace=True)
您可以通过再次使用df.na()或df.info()来验证不存在空值。
时间戳列通常代表Unix 时间,它是自 1970 年 1 月 1 日以来的毫秒数。具体时间数据可以从时间戳列中提取可能有助于预测出租车票价的信息,例如月份、一天中的小时、是否是高峰时间等:
首先,使用pd.to_datetime将时间戳列转换为时间对象,然后查看前五行:
- df['date'] = pd.to_datetime(df['time_stamp'])
- df.head()
这是预期的输出:
图 9.4 – time_stamp 转换后的 cab 数据集
这个数据有问题。不需要太多领域专业知识就知道 Lyft 和 Uber 在 1970 年不存在。多余的小数位表明转换不正确。
在尝试了几个乘法器后进行适当的转换,我发现10**6给出了适当的结果:
- df['date'] = pd.to_datetime(df['time_stamp']*(10**6))
- df.head()
这是预期的输出:
图 9.5 –“日期”转换后的出租车骑行数据集
使用 datetime 列,您可以在导入datetime后提取新列,例如month、hour和day of week ,如下所示:
- import datetime as dt
- df['month'] = df['date'].dt.month
- df['hour'] = df['date'].dt.hour
- df['dayofweek'] = df['date'].dt.dayofweek
现在,您可以使用这些列来设计更多列,例如是周末还是高峰时间。
下面的函数确定一个根据官方文档:https ://pandas.pydata.org/pandas-docs/stable/reference/,通过检查'dayofweek'是否等于5或 6 (代表星期六或星期日),星期几是周末api/pandas.Series.dt.weekday.html:
- def weekend(row):
- if row['dayofweek'] in [5,6]:
- return 1
- else:
- return 0
接下来,将该函数作为新列df['weekend']应用于 DataFrame ,如下所示:
df['weekend'] = df.apply(weekend, axis=1)
通过查看时间是否在上午 6-10 点( 6-10小时)和下午 3-7 点(15-19小时)之间,可以实施相同的策略来创建高峰时间列:
- def rush_hour(row):
- if (row['hour'] in [6,7,8,9,15,16,17,18]) &
- (row['weekend'] == 0):
- return 1
- else:
- return 0
现在,将该函数应用于新的“rush_hour”列:
df['rush_hour'] = df.apply(rush_hour, axis=1)
最后五行显示新列的变化,如df.tail() 所示:
df.tail()
图 9.6 – 特征工程后 cab 数据集的最后五行
可以继续提取和设计新时间列的过程。
笔记
在设计大量新列时,值得检查新特性是否具有强相关性。本章稍后将探讨数据的相关性。
既然了解了特征工程时间列的实践,我们来特征工程分类列。
之前,我们使用pd.get_dummies进行转换分类列转换为数值列。Scikit-learn 的OneHotEncoder功能是另一个选项,旨在使用稀疏矩阵将分类数据转换为 0 和 1,您将在第 10 章XGBoost模型部署中应用该技术。虽然使用这些选项中的任何一个将分类数据转换为数值数据是标准的,但存在替代方案。
尽管 0 和 1 作为分类列的数值是有意义的,但由于 0 表示不存在而 1 表示存在,因此其他值可能会提供更好的结果。
一种策略是将分类列转换为其频率,这等于每个类别在给定列中出现的次数百分比。因此,不是一列类别,而是将每个类别转换为其在该列中的百分比。
接下来让我们查看将分类值转换为数值的步骤。
工程频率列
设计一个分类列,例如'cab_type',首先查看每个类别的值的数量:
使用.value_counts()方法查看类型的频率:
df['cab_type'].value_counts()
结果如下:
- Uber 4654
- Lyft 4573
- Name: cab_type, dtype: int64
使用groupby将计数放在新列中。df.groupby(column_name)是groupby,而[column_name].transform指定要转换的列,后跟括号中的聚合:
df['cab_freq'] = df.groupby('cab_type')['cab_type'].transform('count')
将新列除以总行数以获得频率:
df['cab_freq'] = df['cab_freq']/len(df)
验证是否已按预期进行更改:
df.tail()
图 9.7 – 设计出租车频率后的出租车乘坐数据集
cab 频率现在显示预期输出。
我们将得出结论部分具有经过竞争测试的特征工程方法,称为均值编码或目标编码。
平均编码变换分类列基于平均目标变量的数值列。例如,如果橙色导致七个目标值 1 和三个目标值 0,则平均编码列将是 7/10 = 0.7。由于使用目标值时存在数据泄漏,因此需要额外的正则化需要离子技术。
当信息发生数据泄露时训练集和测试集,或预测器和目标列之间是共享的。这里的风险是目标列被直接用于影响预测列,这在机器学习中通常是一个坏主意。尽管如此,平均编码已被证明可以产生出色的效果。当数据集很深并且传入数据的平均值分布大致相同时,它可以工作。正则化是采取额外的预防措施以减少过度拟合的可能性。
幸运的是,scikit-learn 提供了TargetEncoder来为您处理均值转换:
首先,从category_encoders导入TargetEndoder。如果这不起作用,请使用以下代码安装category_encoders :
- pip install --upgrade category_encoders
- from category_encoders.target_encoder import TargetEncoder
接下来,初始化encoder,如下:
encoder = TargetEncoder()
最后,引入一个新列并在编码器上使用fit_transform方法应用均值编码。包括正在更改的列和目标列作为参数:
df['cab_type_mean'] = encoder.fit_transform(df['cab_type'], df['price'])
现在,验证更改是否符合预期:
df.tail()
以下是新列的输出摘录:
图 9.8 – 平均编码后的出租车数据集
最右边的列cab_type_mean符合预期。
有关更多信息平均编码,请参阅此 Kaggle 研究:https ://www.kaggle.com/vprokopev/mean-likelihood-encodings-a-comprehensive-study 。
这里的想法并不是说那个意思encoding 比 one-hot encoding 更好,但平均编码是一种经过验证的技术在 Kaggle 比赛中表现良好,可能值得尝试提高分数。
没有理由停下来这里。更多的特征工程可能包括使用groupby和其他编码器对其他列进行统计测量。其他分类列,例如目的地和到达列,可以转换为纬度和经度,然后转换为新的距离度量,例如出租车距离或文森蒂距离,这考虑了球面几何。
在 Kaggle 比赛中,参赛者可能会设计数千个新的列,希望能获得更多的小数位精度。如果您有大量工程列,则可以使用.feature_importances_选择最重要的列,如第 2 章,深度决策树中所述。您还可以消除高度相关的列(在下一节中解释,构建不相关的整体)。
对于这个特定的出租车数据集,还有一个包含天气的附加 CSV 文件。但是如果没有天气文件怎么办?您可以随时研究提供日期的天气数据,并自行包含天气数据。
特征工程是任何数据科学家构建稳健模型的基本技能。这里涵盖的策略是只有一小部分存在的选项。特征工程涉及研究、实验、领域专业知识、标准化列、对新列的机器学习性能的反馈以及最后缩小最终列的范围。
现在您了解了特征工程的各种策略,让我们继续构建非相关集成。
“在我们的最终模型中,我们将 XGBoost 作为一个集成模型,其中包括 20 个 XGBoost 模型、5 个随机森林、6 个随机决策树模型、3 个正则化贪婪森林、3 个逻辑回归模型、5 个 ANN 模型、3 个弹性网络模型和 1 个支持向量机模型。”
–宋,Kaggle 获胜者
(https://hunch243.rssing.com/chan-68612493/all_p1.html)
Kaggle 的获奖模型比赛很少是个人模特;他们几乎总是合奏。集成,我不是指提升或装袋模型,例如随机森林或 XGBoost,而是包含任何不同模型的纯集成,包括 XGBoost、随机森林等。
在本节中,我们将机器学习模型组合成不相关的集成,以提高准确性并减少过拟合。
威斯康星州乳腺癌数据集,用于预测患者是否患有乳腺癌,有 569 行 30 列,可以在https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_breast_cancer.html 查看?高亮=负载乳房癌。
从 scikit-learn导入load_breast_cancer数据集,以便我们可以快速开始构建模型:
from sklearn.datasets import load_breast_cancer
通过设置return_X_y=True参数,将预测列分配给X,将目标列分配给y :
X, y = load_breast_cancer(return_X_y=True)
使用StratifiedKFold准备 5 折交叉验证以保持一致性:
kfold = StratifiedKFold(n_splits=5)
现在,构建一个简单的分类函数,将模型作为输入并返回平均交叉验证分数作为输出:
- def classification_model(model):
- scores = cross_val_score(model, X, y, cv=kfold)
- return scores.mean()
获取几个默认分类器的分数,包括 XGBoost,以及它的替代基础学习器、随机森林和逻辑回归:
classification_model(XGBClassifier())
分数如下:
0.9771619313771154
b) 使用gblinear得分:
classification_model(XGBClassifier(booster='gblinear'))
分数如下:
0.5782952957615277
c) 用飞镖得分:
classification_model(XGBClassifier(booster='dart', one_drop=True))
分数如下:
0.9736376339077782
请注意,对于飞镖助推器,我们设置one_drop=True以确保实际丢弃树。
d) 使用RandomForestClassifier得分:
classification_model(RandomForestClassifier(random_state=2))
分数如下:
0.9666356155876418
e) 使用LogisticRegression得分:
classification_model(LogisticRegression(max_iter=10000))
分数如下:
0.9490451793199813
大多数模型的表现都不错,其中 XGBoost 分类器获得了最高分。然而, gblinear基础学习器的表现并不是特别好,因此我们不会继续使用它。
在实践中,这些中的每一个模型应该调整。由于我们已经在多个章节中介绍了超参数调优,因此此处不再讨论该选项。尽管如此,超参数的知识可以让您有信心尝试具有一些调整值的快速模型。例如,如下代码所示,可以在 XGBoost 上尝试将max_depth降低到2,将n_estimators增加到500,并确保learning_rate设置为0.1:
classification_model(XGBClassifier(max_depth=2, n_estimators=500, learning_rate=0.1))
分数如下:
0.9701133364384411
这是一个非常好的分数。虽然它不是最高的,但它可能对我们的合奏很有价值。
既然我们有了各种各样的模型,那么让我们来了解一下它们之间的相关性。
首先,让我们了解相关性代表什么。
相关性是一种统计测量值介于-1和1之间,表示两组点之间的线性关系强度。相关性为1是一条完全直线,而相关性为0则表明没有任何线性关系。
一些关于相关性的视觉效果应该让事情变得清晰。以下视觉效果取自 Wikipedia 的Correlation and Dependence页面,网址为https://en.wikipedia.org/wiki/Correlation_and_dependence:
图 9.9 – 列出的相关性
许可证信息
DenisBoigelot 的原始上传者是 Imagecreator – 自己的作品,CC0,https: //commons.wikimedia.org/w/index.php?curid=15165296 。
图 9.10 – 0.816 的相关性
许可证信息
作者:Anscombe.svg:Schutz(使用下标的标签):Avenue – Anscombe.svg,CC BY-SA 3.0,https ://commons.wikimedia.org/w/index.php?curid=9838454
第一个例子表明,越高相关性,点通常越接近直线。第二个例子显示相同的数据点相关性可能相差很大。换句话说,相关性提供了有价值的信息,但它并不能说明全部情况。
现在您了解了相关性的含义,让我们将相关性应用于构建机器学习集成。
之间的高度相关机器学习模型在集成中是不可取的。但是为什么呢?
考虑两个分类器的情况,每个分类器有 1,000 个预测。如果这些分类器都做出相同的预测,则不会从第二个分类器中获得新的信息,因此它是多余的。
使用多数规则实现,只有当大多数分类器出错时,预测才是错误的。因此,最好有多种模型得分高但给出不同的预测。如果大多数模型给出相同的预测,则相关性很高,将新模型添加到集成中几乎没有价值。在强模型可能错误的预测中发现差异,使集成有机会产生更好的结果。当模型不相关时,预测会有所不同。
为了计算机器学习模型之间的相关性,我们首先需要比较数据点。机器学习模型产生的不同数据点是它们的预测。获得预测后,我们将它们连接成一个 DataFrame,然后应用.corr方法一次获得所有相关性。
以下是查找机器学习模型之间相关性的步骤:
定义一个返回每个机器学习模型的预测的函数:
- def y_pred(model):
- model.fit(X_train, y_train)
- y_pred = model.predict(X_test)
- score = accuracy_score(y_pred, y_test)
- print(score)
- return y_pred
使用train_test_split为单倍预测准备数据:
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=2)
a) XGBClassifier使用以下内容:
y_pred_gbtree = y_pred(XGBClassifier())
准确度得分如下:
0.951048951048951
b)带有dart的XGBClassifier使用以下内容:
y_pred_dart = y_pred(XGBClassifier(booster='dart', one_drop=True))
0.951048951048951
c) RandomForestClassifier使用以下内容:
y_pred_forest = y_pred(RandomForestClassifier())
准确度得分如下:
0.9370629370629371
d) LogisticRegression使用以下内容:
y_pred_logistic = y_pred(LogisticRegression(max_iter=10000))
准确度得分如下:
0.9370629370629371
笔记
在LogisticRegression中增加max_iter以防止警告(并可能获得准确性)。
e)调整后的XGBClassifier使用以下内容:
y_pred_xgb = y_pred(XGBClassifier(max_depth=2, n_estimators=500, learning_rate=0.1))
准确度得分如下:
0.965034965034965
将预测连接到使用 np.c 的新 DataFrame ( c是串联的缩写):
- df_pred = pd.DataFrame(data= np.c_[y_pred_gbtree, y_pred_dart,
- y_pred_forest, y_pred_logistic, y_pred_xgb],
- columns=['gbtree', 'dart','forest', 'logistic', 'xgb'])
df_pred.corr()
您应该看到以下输出:
图 9.11 – 各种机器学习模型之间的相关性
如您所见,对角线上的所有相关性都是1.0,因为模型与自身之间的相关性必须是完全线性的。所有其他值都相当高。
没有明确的界限来获得非相关阈值。它最终取决于相关性的值和可供选择的模型数量。对于这个例子,我们可以用我们最好的模型xgb选择下两个相关性最小的模型,它们是随机森林和逻辑回归。
现在我们已经选择了我们的模型,我们将使用接下来介绍的VotingClassifier集成将它们组合成一个集成。
Scikit-learn 的VotingClassifier合奏是旨在结合多个分类模型并使用多数规则为每个预测选择输出。请注意,scikit-learn 还附带VotingRegressor,它通过取每个回归模型的平均值来组合多个回归模型。
以下是在 scikit-learn 中创建集成的步骤:
初始化一个空列表:
estimators = []
初始化第一个模型:
logistic_model = LogisticRegression(max_iter=10000)
将模型作为(model_name, model)形式的元组附加到列表中:
estimators.append(('logistic', logistic_model))
根据需要多次重复步骤 2和3 :
- xgb_model = XGBClassifier(max_depth=2, n_estimators=500, learning_rate=0.1)
- estimators.append(('xgb', xgb_model))
- rf_model = RandomForestClassifier(random_state=2)
- estimators.append(('rf', rf_model))
使用模型列表作为输入初始化VotingClassifier(或VotingRegressor ):
ensemble = VotingClassifier(estimators)
使用cross_val_score对分类器进行评分:
- scores = cross_val_score(ensemble, X, y, cv=kfold)
- print(scores.mean())
分数如下:
0.9754075454122031
如您所见,分数有所提高。
既然你明白了构建非相关机器学习集成的目的和技术,让我们继续讨论一种类似但具有潜在优势的技术,称为堆叠。
“对于堆叠和提升,我使用 xgboost,这主要是因为熟悉和经过验证的结果。”
——大卫·奥斯汀,Kaggle 冠军
在这最后一节中,我们将检查 Kaggle 获胜者经常使用的最强大的技巧之一,称为堆叠。
Stacking结合了机器学习模型分为两个不同级别:基础级别,其模型对所有数据进行预测,以及元级别,将基础模型的预测作为输入并使用它们生成最终预测。
换句话说,stacking 中的最终模型不以原始数据为输入,而是以基础机器学习模型的预测为输入。
堆叠模型在 Kaggle 比赛中取得了巨大的成功。大多数 Kaggle 比赛都有合并截止日期,个人和团队可以一起加入。这些合并可以作为团队而不是个人带来更大的成功,因为竞争对手可以构建更大的集合并将他们的模型堆叠在一起。
请注意,堆叠不同于基于元模型的标准集成,该元模型在最后结合了预测。由于元模型以预测值作为输入,因此通常建议使用简单的元模型,例如用于回归的线性回归和用于分类的逻辑回归。
现在您已经了解了堆叠是什么,让我们使用 scikit-learn 应用堆叠。
幸运的是,scikit-learn 自带堆叠回归器和分类器这个过程相当简单。总体思路与上一节中的集成模型非常相似。选择各种基础模型,然后为元模型选择线性回归或逻辑回归。
以下是使用 scikit-learn 进行堆叠的步骤:
创建一个空的基本模型列表:
base_models = []
使用语法(name, model)将所有基本模型作为元组附加到基本模型列表:
- base_models.append(('lr', LogisticRegression()))
- base_models.append(('xgb', XGBClassifier()))
- base_models.append(('rf', RandomForestClassifier(random_state=2)))
堆叠时可以选择更多模型,因为没有多数规则限制,并且线性权重更容易适应新数据。最佳方法是使用非相关性作为松散的指导方针并尝试不同的组合。
选择一个元模型,最好是线性回归进行回归和逻辑回归进行分类:
meta_model = LogisticRegression()
使用base_models为estimator初始化StackingClassifier(或StackingRegressor),为final_estimator使用meta_model:
clf = StackingClassifier(estimators=base_models, final_estimator=meta_model)
验证使用cross_val_score或任何其他评分方法的堆叠模型:
- scores = cross_val_score(clf, X, y, cv=kfold)
- print(scores.mean())
分数如下:
0.9789318428815401
这是迄今为止最强的结果。
正如你所看到的,堆叠是一个令人难以置信的强大的方法,并且优于上一节中的非相关集成。
在本章中,您从 Kaggle 比赛的获胜者那里学到了一些经过充分测试的技巧和窍门。除了探索 Kaggle 竞赛和了解保留集的重要性之外,您还获得了特征工程时间列、特征工程分类列、均值编码、构建非相关集合和堆叠的基本实践。这些先进的技术在精英 Kaggler 中很普遍,它们可以在开发用于研究、竞争和行业的机器学习模型时为您提供优势。
在下一章也是最后一章中,我们将从竞争世界转向技术世界,在那里我们将使用变压器和管道从头到尾构建一个 XGBoost 模型,以完成一个为行业部署做好准备的模型。