Python-应用监督学习-全-

Python 应用监督学习(全)

原文:annas-archive.org/md5/d906d6d9346f8d8b7965d192afaf9a47

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

集成建模是一种用于提高机器学习模型性能的方法。它结合了两个或更多相似或不同的机器学习算法,以提供更优越的能力。本书将帮助你实施一些流行的机器学习算法,涵盖集成机器学习的不同范式,如提升、袋装和堆叠。

这本集成机器学习食谱书将从让你熟悉集成技术的基础和探索性数据分析开始。然后,你将学习实施与统计和机器学习算法相关的任务,以理解多个异构算法的集成。它还将确保你不会错过诸如重采样方法等关键主题。随着你的进步,你将更好地理解袋装、提升、堆叠,并学习如何通过实际案例使用随机森林算法。本书将强调这些集成方法如何使用多个模型来提高机器学习结果,与单个模型相比。在结论章节中,你将深入研究使用神经网络、自然语言处理NLP)等高级集成模型。你还将能够实施涵盖欺诈检测、文本分类和情感分析的模型。

到这本书的结尾,你将能够利用集成技术和机器学习算法的工作机制,使用单个配方构建智能模型。

本书面向对象

这本书是为想要深入研究机器学习算法以构建强大集成模型的数据科学家、机器学习开发人员和深度学习爱好者设计的。掌握 Python 编程和基本统计学是必须的,以帮助你掌握书中的概念。

本书涵盖内容

第一章,更接近你的数据,探讨了数据集并使用 Python 进行探索性数据分析,包括使用统计方法和可视化方法。

第二章,开始集成机器学习之旅,探讨了集成学习是什么以及它如何在现实场景中提供帮助。包括平均、加权平均和最大投票在内的基本集成技术得到了解释。这些技术是集成技术的基础,对它们的理解将为读者在阅读本章后进入更高级阶段奠定基础。

第三章,重采样方法,介绍了一些在处理多个异构算法的集成时将非常有用的算法。本章使用 scikit-learn 准备所有要使用的算法。

第四章,统计和机器学习算法,帮助读者了解机器学习算法使用的各种类型的重采样方法。每种重采样方法都有其优缺点,这些都会向读者解释。读者还将学习执行每种采样类型的代码。

第五章,用 Bagging 打包模型,向读者解释了 bootstrap aggregation 是什么以及如何聚合 bootstrap 结果,这个过程也被称为 bagging。

第六章,当有疑问时,使用随机森林,介绍了随机森林算法。它将向读者介绍随机森林如何以及使用何种集成技术,以及这些技术如何帮助我们的模型避免过拟合。

第七章,使用 Boosting 提升模型性能,介绍了 Boosting 及其如何通过减少方差和提高精度来提升模型性能。本章提供了诸如 Boosting 对异常值和噪声数据不稳健,但灵活且可以与损失函数一起使用等信息。

第八章,使用 Stacking 混合,将 stacking 应用于学习基学习器的最佳组合。本章将使读者熟悉 stacking,也称为堆叠泛化。

第九章,使用 Keras 的均匀集成,是一个关于使用均匀算法(在这种情况下,使用 Keras 的多个神经网络模型)识别手写数字的分类案例研究的完整代码演示。

第十章,使用 H2O 的异构集成分类器,是一个关于使用 scikit-learn 的多个异构算法进行违约预测的分类案例研究的完整代码演示。

第十一章,使用 NLP 进行文本分类的异构集成,是一个关于使用多个异构算法进行情感极性分类的分类案例研究的完整代码演示。在这里,使用 NLP 技术(如语义)来提高分类的准确性。然后,使用挖掘的文本信息来应用集成分类技术进行情感分析。在这个案例研究中,使用了 H2O 库来构建模型。

第十二章,使用 Keras 进行多类分类的均匀集成,是一个关于使用 TensorFlow 的 tf.keras 模块进行数据多样性以实现多类分类的均匀集成的分类案例研究的完整代码演示。

为了充分利用这本书

读者需要具备 Python 编程和基本统计知识,以便帮助您理解书中的概念。

下载示例代码文件

您可以从www.packt.com的账户下载本书的示例代码文件。如果您在其他地方购买了此书,您可以访问www.packt.com/support并注册,以便将文件直接通过电子邮件发送给您。

您可以通过以下步骤下载代码文件:

  1. www.packt.com登录或注册。

  2. 选择支持选项卡。

  3. 点击代码下载与勘误。

  4. 在搜索框中输入书籍名称,并遵循屏幕上的说明。

文件下载完成后,请确保使用最新版本解压缩或提取文件夹:

  • WinRAR/7-Zip for Windows

  • Zipeg/iZip/UnRarX for Mac

  • 7-Zip/PeaZip for Linux

书籍的代码包也托管在 GitHub 上,地址为github.com/PacktPublishing/Ensemble-Machine-Learning-Cookbook。如果代码有更新,它将在现有的 GitHub 仓库中更新。

我们还有来自我们丰富的图书和视频目录的其他代码包,可在github.com/PacktPublishing/找到。查看它们吧!

下载彩色图像

我们还提供了一份包含本书中使用的截图/图表的彩色图像的 PDF 文件。您可以从这里下载:www.packtpub.com/sites/default/files/downloads/9781789136609_ColorImages.pdf

使用的约定

本书使用了多种文本约定。

CodeInText:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称。以下是一个示例:“我们将使用操作系统的依赖功能中的os包,以及用于数据操作的pandas包。”

代码块设置如下:

import os
import pandas as pd

# Set working directory as per your need
os.chdir(".../.../Chapter 1")
os.getcwd()

警告或重要注意事项看起来像这样。

小贴士和技巧看起来像这样。

部分

在本书中,您会发现一些频繁出现的标题(准备工作如何操作...工作原理...还有更多...参见)。

为了清楚地说明如何完成食谱,请按照以下方式使用这些部分:

准备工作

本节告诉您在食谱中可以期待什么,并描述了如何设置任何软件或任何为食谱所需的初步设置。

如何操作…

本节包含遵循食谱所需的步骤。

工作原理…

本节通常包含对上一节发生情况的详细说明。

还有更多…

本节包含有关食谱的附加信息,以便您对食谱有更深入的了解。

参见

本节提供了对其他有用信息的链接,以帮助食谱。

联系我们

我们欢迎读者的反馈。

一般反馈:如果您对本书的任何方面有疑问,请在邮件主题中提及书名,并给我们发送邮件至 customercare@packtpub.com

勘误: 尽管我们已经尽最大努力确保内容的准确性,但错误仍然可能发生。如果您在这本书中发现了错误,我们非常感谢您能向我们报告。请访问 www.packt.com/submit-errata,选择您的书籍,点击勘误提交表单链接,并输入详细信息。

盗版: 如果您在互联网上以任何形式发现我们作品的非法副本,我们非常感谢您能提供位置地址或网站名称。请通过链接材料与我们联系至 copyright@packt.com

如果您有兴趣成为作者:如果您在某个领域有专业知识,并且您有兴趣撰写或为书籍做出贡献,请访问 authors.packtpub.com

评论

请留下评论。一旦您阅读并使用了这本书,为何不在购买它的网站上留下评论呢?潜在读者可以查看并使用您的客观意见来做出购买决定,Packt 公司可以了解您对我们产品的看法,我们的作者也可以看到他们对书籍的反馈。谢谢!

有关 Packt 的更多信息,请访问 packt.com

目录

  1. 版权页

  2. 版权和致谢

    1. 集成机器学习食谱
  3. 关于 Packt

    1. 为什么订阅?

    2. Packt.com

  4. 序言

  5. 贡献者

    1. 关于作者

    2. 关于审稿人

    3. Packt 正在寻找像您这样的作者

  6. 前言

    1. 本书面向的对象

    2. 本书涵盖的内容

    3. 充分利用本书

      1. 下载示例代码文件

      2. 下载彩色图片

      3. 使用的约定

    4. 章节

      1. 准备就绪

      2. 如何操作……

      3. 它是如何工作的...

      4. 还有更多...

      5. 另请参阅

    5. 取得联系

      1. 评论
  7. 更接近你的数据

    1. 简介

    2. 使用 Python 进行数据处理

      1. 准备就绪

      2. 如何做...

      3. 它是如何工作的...

      4. 还有更多...

      5. 另请参阅

    3. 分析、可视化和处理缺失值

      1. 如何做...

      2. 它是如何工作的...

      3. 还有更多...

      4. 另请参阅

    4. 探索性数据分析

      1. 如何做...

      2. 它是如何工作的...

      3. 还有更多...

      4. 另请参阅

  8. 开始使用集成机器学习

    1. 集成机器学习简介

    2. 最大投票

      1. 准备就绪

      2. 如何做...

      3. 它是如何工作的...

      4. 还有更多...

    3. 平均

      1. 准备就绪

      2. 如何做...

      3. 它是如何工作的...

    4. 加权平均

      1. 准备就绪

      2. 如何做...

      3. 它是如何工作的...

      4. 另请参阅

  9. 重采样方法

    1. 采样简介

      1. 准备就绪

      2. 如何做...

      3. 它是如何工作的...

      4. 还有更多...

      5. 另请参阅

    2. k 折交叉验证和留一法交叉验证

      1. 准备就绪

      2. 如何操作...

      3. 它是如何工作的...

      4. 还有更多...

      5. 另请参阅

    3. 自助法

      1. 准备就绪

      2. 如何操作...

      3. 它是如何工作的...

      4. 另请参阅

  10. 统计和机器学习算法

    1. 技术要求

    2. 多重线性回归

      1. 准备就绪

      2. 如何操作...

      3. 它是如何工作的...

      4. 还有更多...

      5. 另请参阅

    3. 逻辑回归

      1. 准备就绪

      2. 如何操作...

      3. 它是如何工作的...

      4. 另请参阅

    4. 朴素贝叶斯

      1. 准备就绪

      2. 如何操作...

      3. 它是如何工作的...

      4. 还有更多...

      5. 另请参阅

    5. 决策树

      1. 准备就绪

      2. 如何操作...

      3. 它是如何工作的...

      4. 还有更多...

      5. 另请参阅

    6. 支持向量机

      1. 准备就绪

      2. 如何操作...

      3. 它是如何工作的...

      4. 还有更多...

      5. 另请参阅

  11. 使用 Bagging 打包模型

    1. 简介

    2. Bootstrap 聚合

      1. 准备就绪

      2. 如何操作...

      3. 它是如何工作的...

      4. 另请参阅

    3. 集成元估计器

      1. 袋分类器

      2. 如何操作...

      3. 它是如何工作的...

      4. 还有更多...

      5. 另请参阅

    4. 袋回归

      1. 准备就绪

      2. 如何操作...

      3. 它是如何工作的...

      4. 另请参阅

  12. 犹豫不决时,使用随机森林

    1. 随机森林简介

    2. 使用 scikit-learn 实现预测信用卡违约的随机森林

      1. 准备就绪

      2. 如何操作...

      3. 它是如何工作的...

      4. 还有更多...

      5. 另请参阅

    3. 使用 H2O 实现随机森林预测信用卡违约

      1. 准备就绪

      2. 如何操作...

      3. 它是如何工作的...

      4. 还有更多...

      5. 另请参阅

  13. 使用提升提高模型性能

    1. 提升简介

    2. 使用 scikit-learn 实现 AdaBoost 进行疾病风险预测

      1. 准备就绪

      2. 如何操作...

      3. 它是如何工作的...

      4. 还有更多...

      5. 另请参阅

    3. 使用 scikit-learn 实现疾病风险预测的梯度提升机

      1. 准备就绪

      2. 如何操作...

      3. 它是如何工作的...

      4. 还有更多...

    4. 使用 scikit-learn 和 XGBoost 实现玻璃识别的极端梯度提升方法

      1. 准备就绪...

      2. 如何操作...

      3. 它是如何工作的...

      4. 更多内容...

      5. 参见

  14. 通过堆叠进行混合

    1. 技术要求

    2. 理解堆叠泛化

    3. 通过组合预测实现堆叠泛化

      1. 准备就绪...

      2. 如何操作...

      3. 它是如何工作的...

      4. 更多内容...

      5. 参见

    4. 使用 H2O 实现针对竞选结果预测的堆叠泛化

      1. 准备就绪...

      2. 如何操作...

      3. 它是如何工作的...

      4. 更多内容...

      5. 参见

  15. 使用 Keras 的同质集成

    1. 简介

    2. 用于能量预测的同质模型集成

      1. 准备就绪

      2. 如何操作...

      3. 它是如何工作的...

      4. 更多内容...

      5. 参见

    3. 用于手写数字分类的同质模型集成

      1. 准备就绪

      2. 如何操作...

      3. 它是如何工作的...

  16. 使用 H2O 的异构集成分类器

    1. 简介

    2. 使用异构集成分类器预测信用卡违约者

      1. 准备就绪

      2. 如何操作...

      3. 它是如何工作的...

      4. 更多内容...

      5. 参见

  17. 使用 NLP 进行文本分类的异构集成

    1. 简介

    2. 使用异质算法集进行垃圾邮件过滤

      1. 准备就绪

      2. 如何做...

      3. 它是如何工作的...

    3. 使用集成模型进行电影评论的情感分析

      1. 准备就绪

      2. 如何做...

      3. 它是如何工作的...

      4. 还有更多...

  18. 使用 Keras 进行多类分类的同质集成

    1. 简介

    2. 用于分类时尚产品的同质模型集合

      1. 准备就绪

      2. 如何做...

      3. 它是如何工作的...

      4. 另请参阅

  19. 你可能喜欢的其他书籍

    1. 留下评论 - 让其他读者了解您的看法

第一章:贴近您的数据

在本章中,我们将介绍以下食谱:

  • 使用 Python 进行数据处理

  • 分析、可视化和处理缺失值

  • 探索性数据分析

简介

在这本书中,我们将介绍各种集成技术,并学习如何集成多个机器学习算法以增强模型性能。我们将使用 pandas、NumPy、scikit-learn 和 Matplotlib,这些都是为与 Python 一起使用而构建的,正如我们将在整本书中做的那样。到目前为止,你应该已经非常熟悉数据处理和探索。

在本章中,我们将回顾如何在 Python 中读取和处理数据,如何分析和处理缺失值,以及如何探索数据以获得更深入的见解。我们将使用各种 Python 包,如numpypandas,用于数据操作和探索,以及seaborn包用于数据可视化。我们将在本书的后续章节中也继续使用这些库中的某些或全部库。我们还将使用 Anaconda 发行版进行我们的 Python 编码。如果您尚未安装 Anaconda,您需要从www.anaconda.com/download下载它。在撰写本书时,Anaconda 的最新版本是 5.2,它包含 Python 3.6 和 Python 2.7。我们建议您下载 Python 3.6 的 Anaconda。我们还将使用可在 GitHub 上找到的HousePrices数据集。

使用 Python 进行数据处理

在现实生活中,往往很难获得一个完整且干净的数据集,其格式正好符合我们的需求。我们接收到的数据通常不能直接用于统计或机器学习算法。我们需要处理原始数据,以便处理后的数据可以用于进一步分析和建模。首先,我们需要导入所需的包,例如pandas,并将我们的数据集读入 Python。

准备工作

我们将使用操作系统的依赖功能中的os包,以及用于数据处理的pandas包。

现在,让我们查看数据定义以了解我们的变量。在下面的代码中,我们列出了一些变量的数据定义。数据集和完整的数据定义可在 GitHub 上找到。以下是数据描述文件的简略版本:

MS SubClass (Nominal): Identifies the type of dwelling involved in the sale
Lot Frontage (Continuous): Linear feet of street connected to property
Alley (Nominal): Type of alley access to property
Overall Qual (Ordinal): Rates the overall material and finish of the house
Overall Cond (Ordinal): Rates the overall condition of the house
Year Built (Discrete): Original construction date
Mas Vnr Type (Nominal): Masonry veneer type
Mas Vnr Area (Continuous): Masonry veneer area in square feet
Garage Type (Nominal): Garage location
Garage Yr Blt (Discrete): Year garage was built
Garage Finish (Ordinal): Interior finish of the garage
Garage Cars (Discrete): Size of garage in car capacity
Garage Area (Continuous): Size of garage in square feet
Garage Qual (Ordinal): Garage quality
Garage Cond (Ordinal): Garage condition
...
...
SalePrice (Continuous): Sale price $$

然后,我们将导入ospandas包,并根据我们的要求设置我们的工作目录,如下面的代码块所示:

import os
import pandas as pd

# Set working directory as per your need
os.chdir(".../.../Chapter 1")
os.getcwd()

下一步是从 GitHub 下载数据集并将其复制到您的当前工作目录。

如何操作...

现在,让我们执行一些数据处理步骤:

  1. 首先,我们将从当前工作目录读取HousePrices.csv数据,并创建我们的第一个用于操作的数据框。我们将其命名为housepricesdata,如下所示:
housepricesdata = pd.read_csv("HousePrices.csv")
  1. 现在让我们看一下我们的数据框,看看它的样子:
# See first five observations from top
housepricesdata.head(5)

您可能看不到所有行;Jupyter 将截断一些变量。为了在 Jupyter 中查看任何输出的所有行和列,请执行以下命令:

# 设置选项以显示所有行和列

pd.options.display.max_rows = None

pd.options.display.max_columns = None

  1. 我们可以用shape查看 DataFrame 的维度。shapepandas DataFrame 的一个属性:
housepricesdata.shape

使用前面的命令,我们可以看到行数和列数,如下所示:

(1460, 81)

在这里,我们可以看到 DataFrame 有1460个观测值和81个列。

  1. 让我们看看 DataFrame 中变量的数据类型:
housepricesdata.dtypes

在下面的代码块中,我们可以看到 DataFrame 中每个变量的数据类型:

Id                 int64
MSSubClass         int64
MSZoning          object
LotFrontage      float64
LotArea            int64
LotConfig         object
LandSlope         object
                  ...   
BedroomAbvGr       int64
KitchenAbvGr       int64
KitchenQual       object
TotRmsAbvGrd       int64
SaleCondition     object
SalePrice          int64
Length: 81, dtype: object

我们现在已准备好开始数据操作,我们可以以许多不同的方式来完成。在本节中,我们将探讨几种我们可以用来操纵和准备数据以便分析的方法。

让我们先总结一下我们的数据。

  1. describe()函数将只显示数值变量的统计信息:
housepricesdata.describe()

我们可以在下面的屏幕截图中看到输出:

图片

  1. 我们将删除id列,因为这对于我们的分析将不是必需的:
# inplace=True will overwrite the DataFrame after dropping Id column
housepricesdata.drop(['Id'], axis=1, inplace=True)
  1. 现在,让我们看看一些对象类型变量(即分类变量)的分布。在下面的示例中,我们将查看LotShapeLandContour。我们可以以相同的方式研究数据集中的其他分类变量,如下面的代码块所示:
# Name the count column as "count"
lotshape_frequencies = pd.crosstab(index=housepricesdata["LotShape"], columns="count") 

landcountour_frequencies = pd.crosstab(index=housepricesdata["LandContour"], columns="count") # Name the count column as "count"

print(lotshape_frequencies)
print("\n") # to keep a blank line for display
print(landcountour_frequencies)
  1. 现在我们将看到如何在不同数据类型之间进行转换。我们注意到变量如MSSubClassOverallQualOverallCond的数据定义都是分类变量。然而,在导入数据集之后,它们却显示为整数。

在对任何变量进行类型转换之前,请确保没有缺失值。

在这里,我们将变量转换为分类数据类型:

# Using astype() to cast a pandas object to a specified datatype
housepricesdata['MSSubClass'] = housepricesdata['MSSubClass'].astype('object')
housepricesdata['OverallQual'] = housepricesdata['OverallQual'].astype('object')
housepricesdata['OverallCond'] = housepricesdata['OverallCond'].astype('object')

# Check the datatype of MSSubClass after type conversion
print(housepricesdata['MSSubClass'].dtype)
print('\n') # to keep a blank line for display

# Check the distribution of the levels in MSSubClass after conversion
# Make a crosstab with pd.crosstab()
# Name the count column as "count"
print(pd.crosstab(index=housepricesdata["MSSubClass"], columns="count")) 

我们可以看到每种房屋类别的观测值数量,如下面的代码块所示:

category

col_0       count
MSSubClass       
20            536
30             69
40              4
45             12
50            144
60            299
70             60
75             16
80             58
85             20
90             52
120            87
160            63
180            10
190            30

有许多变量单独可能并不很有用,但转换它们会给我们带来很多有趣的见解。让我们创建一些新的、有意义的变量。

  1. YearBuiltYearRemodAdd分别代表原始建筑日期和翻修日期。然而,如果它们可以被转换为年龄,这些变量将告诉我们建筑物的年龄以及它们翻修以来的年数。为此,我们创建了两个新的变量,BuildingAgeRemodelAge
# Importing datetime package for date time operations
import datetime as dt

# using date time package to find the current year
current_year = int(dt.datetime.now().year)

# Subtracting the YearBuilt from current_year to find out the age of the building
building_age = current_year - housepricesdata['YearBuilt']

# Subtracting the YearRemonAdd from current_year to find out the age since the
# building was remodelled
remodelled_age = current_year - housepricesdata['YearRemodAdd']
  1. 现在,让我们将这两个变量添加到我们的数据集中:
# Adding the two variables to the DataFrame
housepricesdata['building_age'] = building_age
housepricesdata['remodelled_age'] = remodelled_age

# Checking our DataFrame to see if the two variables got added
housepricesdata.head(5)

我们注意到building_ageremodelled_age现在已添加到 DataFrame 中,如下面的屏幕截图所示:

图片

包含标签数据的变量需要转换为数值形式,以便机器学习算法可以使用。为了解决这个问题,我们将执行编码,将标签转换为数值形式,以便算法可以使用它们。

  1. 我们需要识别需要编码的变量,包括StreetLotShapeLandContour。我们将执行独热编码,这是一种将分类变量表示为二进制向量的方法。我们将使用 Python 中的pandas包来完成此操作:
# We use get_dummies() function to one-hot encode LotShape
one_hot_encoded_variables = pd.get_dummies(housepricesdata['LotShape'],prefix='LotShape')

# Print the one-hot encoded variables to see how they look like
print(one_hot_encoded_variables)

我们可以在以下屏幕截图中看到已创建的独热编码变量:

图片

  1. 将独热编码变量添加到我们的 DataFrame 中,如下所示:
# Adding the new created one-hot encoded variables to our DataFrame
housepricesdata = pd.concat([housepricesdata,one_hot_encoded_variables],axis=1)

# Let's take a look at the added one-hot encoded variables
# Scroll right to view the added variables
housepricesdata.head(5)

我们可以在以下屏幕截图中看到将独热编码变量添加到 DataFrame 后的输出:

图片

  1. 现在,让我们删除原始变量,因为我们已经创建了我们的独热编码变量:
# Dropping the original variable after one-hot encoding the original variable
# inplace = True option will overwrite the DataFrame

housepricesdata.drop(['LotShape'],axis=1, inplace=True)

它是如何工作的...

pandas模块是 Python 标准库的一部分——它是数据操作的关键模块之一。我们还使用了其他包,如osdatetime。在设置我们的工作目录并将 CSV 文件作为pandas DataFrame 读取到 Python 中之后,我们继续查看一些数据操作方法。

前一节中的步骤 1步骤 5展示了如何使用pandas在 Python 中读取 CSV 文件中的数据,以及如何使用dtypes等函数。

pandas包还提供了从各种文件类型读取数据的方法。例如,pandas.read_excel()将 Excel 表读取到pandas DataFrame 中;pandas.read_json()将 JSON 字符串转换为pandas对象;pandas.read_parquet()从文件路径加载 parquet 对象并返回pandas DataFrame。更多信息可以在bit.ly/2yBqtvd找到。

您也可以使用 Python 中的h5py包读取 HDF5 格式文件。h5py包是 Python 对 HDF5 二进制数据格式的接口。HDF®支持多维数据集,数据集中的每个元素本身也可以是一个复杂对象。集合中数据对象的数量或大小没有限制。更多信息可以在www.hdfgroup.org/找到。一个示例代码块如下所示:

import h5py

# With 'r' passed as a parameter to the h5py.File() 
# the file will be read in read-only mode
data = h5py.File('File Name.h5', 'r')

我们查看变量的数据类型,并使用describe()来查看数值变量的摘要统计信息。需要注意的是,describe()仅适用于数值变量,并且足够智能,可以忽略非数值变量。在步骤 6中,我们看到了如何查看分类变量(如LotShapeLandContour)每个级别的计数。我们可以使用相同的代码来查看其他分类变量的分布。

步骤 7中,我们使用pd.crosstab()查看LotShapeLandContour变量的分布。

在交叉表中,一个常见的需求是包括行和列的小计。我们可以使用margins关键字显示小计。我们将margins=True传递给pd.crosstab()函数。我们还可以使用margins_name关键字给小计列命名。margins_name的默认值是All

然后,我们继续学习如何转换数据类型。我们有一些实际上是分类变量,但在数据集中看起来是数值型的变量。这在现实生活中很常见,因此我们需要学习如何类型转换我们的变量。步骤 8展示了如何将数值变量,如MSSubClass,转换为分类类型。在步骤 8中,我们将一些变量转换为分类数据类型。然后我们创建了一个交叉表来可视化分类变量的每个级别的频率。

步骤 9中,我们从现有变量中创建了新的有意义的变量。我们分别从YearBuiltYearRemodAdd创建了新的变量BuildingAgeRemodelAge,以表示建筑物的年龄和自建筑物翻新以来经过的年数。这种方法创建新变量可以为我们分析和建模提供更好的洞察力。创建新特征的过程称为特征工程。在步骤 10中,我们将新变量添加到我们的 DataFrame 中。

从那里,我们继续学习如何对分类变量进行编码。我们需要对分类变量进行编码,因为它们有命名描述。许多机器学习算法不能在标记数据上操作,因为它们需要所有输入和输出变量都是数值型的。在步骤 12中,我们使用独热编码对它们进行了编码。在步骤 11中,我们学习了如何使用get_dummies()函数,它是pandas包的一部分,来创建独热编码变量。在步骤 12中,我们将one-hot_encoded_variables添加到我们的 DataFrame 中。最后,在步骤 13中,我们移除了现在已独热编码的原始变量。

还有更多...

所需的数据操作类型取决于您的业务需求。在这个第一个菜谱中,我们看到了几种执行数据操作的方法,但您能做的事情以及如何为分析操作数据没有限制。

我们也看到了如何将数值变量转换为分类变量。我们可以以多种方式执行此类类型转换。例如,如果需要,我们可以使用以下代码将分类变量转换为数值变量:

# Converting a categorical variable to numerical
# Using astype() to cast a pandas object to a specified datatype

# Here we typecast GarageYrBlt from float64 type to int64 type
housepricesdata['GarageYrBlt'] = housepricesdata['GarageYrBlt'].astype('int64')

只有当GarageYrBlt变量不包含任何缺失值时,才能转换该变量。前面的代码将引发错误,因为GarageYrBlt包含缺失值。

我们已经探讨了如何使用一热编码将分类变量转换为数值变量,以及为什么我们要这样做。除了 一热编码,我们还可以执行其他类型的编码,例如标签编码、频率编码等等。以下代码块提供了一个标签编码的示例代码:

# We use sklearn.preprocessing and import LabelEncoder class
from sklearn.preprocessing import LabelEncoder

# Create instance of LabelEncoder class
lb_make = LabelEncoder()

# We create a new variable LotConfig_code to hold the new numerical labels
# We label encode LotConfig variable
housepricesdata["LotConfig_Code"] = lb_make.fit_transform(housepricesdata["LotConfig"])

# Display the LotConfig variable and its corresponding label encoded numerical values
housepricesdata[["LotConfig", "LotConfig_Code"]]

参考内容

分析、可视化和处理缺失值

缺失值是由不完整的数据引起的。有效地处理缺失值非常重要,因为它们可能导致不准确的推断和结论。在本节中,我们将探讨如何分析、可视化和处理缺失值。

如何做到这一点...

让我们从分析带有缺失值的变量开始。将 pandas 的选项设置为显示所有行和列,如前一小节所示:

  1. 使用以下语法,我们可以看到哪些变量有缺失值:
# Check which variables have missing values

columns_with_missing_values = housepricesdata.columns[housepricesdata.isnull().any()]
housepricesdata[columns_with_missing_values].isnull().sum()

这将产生以下输出:

  1. 你可能还想以百分比的形式查看缺失值。要查看缺失值的计数和百分比,请执行以下命令:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

# To hold variable names
labels = [] 

# To hold the count of missing values for each variable 
valuecount = [] 

# To hold the percentage of missing values for each variable
percentcount = [] 

for col in columns_with_missing_values:
    labels.append(col)
    valuecount.append(housepricesdata[col].isnull().sum())
    # housepricesdata.shape[0] will give the total row count
   percentcount.append(housepricesdata[col].isnull().sum()/housepricesdata.shape[0])

ind = np.arange(len(labels))

fig, (ax1, ax2) = plt.subplots(1,2,figsize=(20,18))

rects = ax1.barh(ind, np.array(valuecount), color='blue')
ax1.set_yticks(ind)
ax1.set_yticklabels(labels, rotation='horizontal')
ax1.set_xlabel("Count of missing values")
ax1.set_title("Variables with missing values")

rects = ax2.barh(ind, np.array(percentcount), color='pink')
ax2.set_yticks(ind)
ax2.set_yticklabels(labels, rotation='horizontal')
ax2.set_xlabel("Percentage of missing values")
ax2.set_title("Variables with missing values")

它将以绝对值和百分比的形式显示缺失值,如下面的截图所示:

我们注意到,例如 AlleyPoolQCFenceMiscFeature 等变量有 80%90% 的值是缺失的。FireplaceQu47.26% 的值是缺失的。还有一些其他变量,如 LotFrontageMasVnrTypeMasVnrAreaBsmtQualBsmtCond 以及一些与车库相关的变量也有缺失值。

但有一个问题。让我们再次看看 Alley 变量。它显示它有 93.76% 的缺失值。现在,再看看前一小节中我们查看的数据描述。Alley 的变量描述显示它有三个级别:砾石铺路无通道。在原始数据集中,'No Access' 被编码为 NA。当 NA 在 Python 中读取时,它被视为 NaN,这意味着缺失了一个值,因此我们需要小心处理。

  1. 现在,我们将用有效值,如 'No Access' 替换 Alley 的缺失值:
# Replacing missing values with 'No Access' in Alley variable
housepricesdata['Alley'].fillna('No Access', inplace=True)
  1. 现在,让我们可视化缺失值并尝试了解如何处理它们。以下代码生成一个图表,展示了缺失值的分布。在这里,我们使用 seaborn 库来绘制图表:
# Lets import seaborn. We will use seaborn to generate our charts
import seaborn as sns

# We will import matplotlib to resize our plot figure
import matplotlib.pyplot as plt
%matplotlib inline
plt.figure(figsize=(20, 10))

# cubehelix palette is a part of seaborn that produces a colormap
cmap = sns.cubehelix_palette(light=1, as_cmap=True, reverse=True)
sns.heatmap(housepricesdata.isnull(), cmap=cmap)

地图的颜色是通过 cubehelix_palette() 函数以线性增加的亮度生成的:

从前面的图中,更容易读取缺失值的分布。图表上的白色标记表示缺失值。请注意,Alley不再报告任何缺失值。

  1. LotFrontage是一个连续变量,其17.74%的值是缺失的。按照以下方式用该变量的中位数替换缺失值:
# Filling in the missing values in LotFrontage with its median value
housepricesdata['LotFrontage'].fillna(housepricesdata['LotFrontage'].median(), inplace=True)
  1. 让我们再次查看缺失值图,以查看LotFrontage中的缺失值是否已填充。复制并执行前面的代码。缺失值图将如下所示:

在这里,我们可以看到在先前的图表中,AlleyLotFrontage不再有缺失值。

  1. 从数据描述中,我们发现几个变量的值被编码为NA。因为这在 Python 中被读取为缺失值,所以我们用它们的实际值替换所有这些值,这些值可以在以下代码块中看到的数据描述中看到:
# Replacing all NA values with their original meaning
housepricesdata['BsmtQual'].fillna('No Basement', inplace=True)
housepricesdata['BsmtCond'].fillna('No Basement', inplace=True)
housepricesdata['BsmtExposure'].fillna('No Basement', inplace=True)
housepricesdata['BsmtFinType1'].fillna('No Basement', inplace=True)
housepricesdata['BsmtFinType2'].fillna('No Basement', inplace=True)

housepricesdata['GarageYrBlt'].fillna(0, inplace=True)

# For observations where GarageType is null, we replace null values in GarageYrBlt=0
housepricesdata['GarageType'].fillna('No Garage', inplace=True)
housepricesdata['GarageFinish'].fillna('No Garage', inplace=True)
housepricesdata['GarageQual'].fillna('No Garage', inplace=True)
housepricesdata['GarageCond'].fillna('No Garage', inplace=True)

housepricesdata['PoolQC'].fillna('No Pool', inplace=True)
housepricesdata['Fence'].fillna('No Fence', inplace=True)
housepricesdata['MiscFeature'].fillna('None', inplace=True)

housepricesdata['FireplaceQu'].fillna('No Fireplace', inplace=True)
  1. 让我们看一下处理了前面的变量之后的缺失值图:

从前面的图中,我们注意到我们刚刚处理的变量中不再有缺失值。然而,我们在MasVnrTypeMasVnrAreaElectrical中还有一些缺失值。

  1. 让我们尝试使用交叉表查看MasVnrTypeMasVnrArea的分布:
# Using crosstab to generate the count of MasVnrType by type of MasVnrArea
print(pd.crosstab(index=housepricesdata["MasVnrType"],\
                  columns=housepricesdata["MasVnrArea"], dropna=False, margins=True)) 

以下输出显示,当MasVnrArea为零时,大多数情况下MasVnrTypeNone

  1. 然后,我们将使用以下代码块中的命令将MasVnrType中的缺失值用None填充,将MasVnrArea用零填充。
# Filling in the missing values for MasVnrType and MasVnrArea with None and 0 respectively
housepricesdata['MasVnrType'].fillna('None', inplace=True)
housepricesdata['MasVnrArea'].fillna(0, inplace=True)

我们在Electrical变量中仍然有一个缺失值。

  1. 让我们看一下Electrical有缺失值的观测值:
housepricesdata['MSSubClass'][housepricesdata['Electrical'].isnull()]

  1. Electrical为空时,我们看到MSSubClass80。让我们看看Electrical类型的分布情况,按MSSubClass分类:
# Using crosstab to generate the count of Electrical Type by MSSubClass
print(pd.crosstab(index=housepricesdata["Electrical"],\
columns=housepricesdata['MSSubClass'], dropna=False, margins=True))

从以下输出中,我们可以看到当MSSubClass80时,大多数Electrical类型的案例都是SBrkr

  1. 好的,接下来请使用以下代码执行,将Electrical变量中的缺失值用SBrKr进行填充:
housepricesdata['Electrical'].fillna('SBrkr', inplace=True)
  1. 然后,让我们再次查看我们的缺失值图,作为最后的检查:
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
plt.figure(figsize=(20, 10))

cmap = sns.cubehelix_palette(light=1, as_cmap=True, reverse=True)
sns.heatmap(housepricesdata.isnull(), cmap=cmap)

我们得到的输出可以在以下图表中查看:

注意到图表已经改变,现在显示我们的 DataFrame 中没有缺失值。

它是如何工作的...

步骤 1步骤 2中,我们以绝对和百分比的形式查看具有缺失值的变量。我们注意到 Alley 变量有超过 93% 的值是缺失的。然而,从数据描述中,我们了解到 Alley 变量有一个 无通道入口 的值,在数据集中编码为 NA。当这个值在 Python 中读取时,所有的 NA 实例都被视为缺失值。在步骤 3中,我们将 Alley 中的 NA 替换为 No Access

注意,我们在步骤 2中使用了 %matplotlib inline。这是一个魔法函数,可以在笔记本本身中渲染图表。

步骤 4中,我们使用了 seaborn 库来绘制缺失值图表。在这个图表中,我们识别了具有缺失值的变量。缺失值用白色表示,而数据的出现用颜色表示。我们从图表中注意到 Alley 没有更多的缺失值。

步骤 4中,我们使用了来自 seaborn 库的 cubehelix_palette() 函数,它生成一个线性减少(或增加)亮度的颜色图。seaborn 库还为我们提供了包括 light_palette()dark_palette() 在内的选项。light_palette() 提供了一个从浅色到颜色的顺序调色板,而 dark_palette() 则生成一个从深色到颜色的顺序调色板。

步骤 5中,我们发现一个数值变量 LotFrontage 的超过 17% 的值是缺失的。我们决定用这个变量的中位数来填充缺失值。我们在步骤 6中重新访问了缺失值图表,看看是否还有变量有缺失值。我们注意到 AlleyLotFrontage 没有任何白色标记,这表明这两个变量都没有进一步的缺失值。

步骤 7中,我们确定了一些用 NA 编码的数据变量。这导致了我们之前遇到的问题,因为 Python 将它们视为缺失值。我们将所有这样的编码值替换为实际信息。

然后,我们在步骤 8中重新访问了缺失值图表。我们看到当时几乎所有的变量都没有缺失值,除了 MasVnrTypeMasVnrAreaElectrical

步骤 9步骤 10中,我们填充了 MasVnrTypeMasVnrArea 变量的缺失值。我们注意到,当 MasVnrArea0.0 时,MasVnrType 总是 None,除了少数罕见情况。因此,我们在两个变量有缺失值的地方用 None 填充 MasVnrType,用 0.0 填充 MasVnrArea。这样我们就只剩下一个有缺失值的变量,即 Electrical

步骤 11中,我们查看哪种类型的房屋缺失了Electrical值。我们注意到MSSubClass表示住宅类型,对于缺失的Electrical值,MSSubClass80,这意味着它是分割的或多层。在步骤 12中,我们检查了Electrical值的分布,该分布是按住宅类型MSSubClass进行的。我们注意到当MSSubClass等于80时,Electrical的大多数值是SBrkr,这代表标准断路器和 Romex。因此,我们决定用SBrkr来填补Electrical中的缺失值。

最后,在步骤 14中,我们再次回顾了缺失值图表,并看到数据集中没有更多的缺失值。

更多...

通过使用前面的图表和缺失值图表,我们很容易地确定了数据集中缺失值的数量、百分比和分布。我们注意到许多变量对于相同的观测值都有缺失值。然而,在查阅数据描述后,我们发现大多数缺失值实际上并不是缺失的,但由于它们被编码为NA,pandas 将其视为缺失值。

对于数据分析师来说,理解数据描述并适当处理缺失值非常重要。

通常,缺失数据可以分为三类:

  • 完全随机缺失MCAR):MCAR 表示缺失值与被研究的对象无关。换句话说,当变量缺失数据的概率与其他测量变量或其自身值无关时,数据就是 MCAR。例如,某些受访者调查的年龄未记录,纯粹是偶然的。

  • 随机缺失MAR):在这里,MAR 的名字有点误导,因为在这种情况下,值的缺失并不是随机的。如果数据的缺失与观察到的其他变量有关,但与数据的潜在值无关,则数据是 MAR。例如,当我们从客户那里收集数据时,富裕的客户比其他客户更不愿意披露他们的收入,从而导致 MAR 数据。

  • 非随机缺失MNAR):如果数据不能被归类为 MCAR 或 MAR,则数据是 MNAR,也称为不可忽略的。例如,也许一些消费者在年龄超过 40 岁时不想分享他们的年龄,因为他们想隐藏它。

可以应用多种策略来填补缺失值,如下所示:

  • 获取缺失数据

  • 留出不完整的观测值

  • 用估计值替换缺失数据,例如均值或中位数

  • 从数据集中的其他变量估计缺失数据

参见

探索性数据分析

我们将继续在上一节中留下的地方,即分析和处理缺失值。数据科学家的大部分时间都花在数据准备和探索上,而不是模型构建和优化。现在我们的数据集中没有缺失值,我们可以继续进行探索性数据分析。

如何做到这一点...

  1. 在数据处理的第一节中,我们看到了数据集的摘要统计信息。然而,自从填充缺失值以来,我们还没有查看过这些信息。

现在让我们使用以下代码查看数据和其基本统计信息:

# To take a look at the top 5 rows in the dataset
housepricesdata.head(5)

# To display the summary statistics for all variables
housepricesdata.describe()
  1. 通过前面的代码,我们可以看到之前章节中变量的摘要统计信息。

现在,让我们看看有多少列按数据类型划分:

# How many columns with different datatypes are there?
housepricesdata.get_dtype_counts()

下面的代码展示了每种数据类型有多少变量。我们可以看到,我们有 3 个浮点型变量,33 个整型变量,45 个对象型变量,以及 4 个无符号整数,它们存储了LotShape变量的单热编码值:

  1. 让我们创建两个变量来存储数值和分类变量的名称:
# Pulling out names of numerical variables by conditioning dtypes NOT equal to object type
numerical_features = housepricesdata.dtypes[housepricesdata.dtypes != "object"].index
print("Number of Numerical features: ", len(numerical_features))

# Pulling out names of categorical variables by conditioning dtypes equal to object type
categorical_features = housepricesdata.dtypes[housepricesdata.dtypes == "object"].index
print("Number of Categorical features: ", len(categorical_features))

这显示了数值和分类变量的数量:

  1. 我们现在将使用之前创建的numerical_features变量来查看数值变量的分布。我们将使用seaborn库来绘制我们的图表:

我们使用 pandas 的melt()方法来重塑我们的 DataFrame。你可能想在使用melt()方法后查看重塑后的数据,以了解 DataFrame 是如何排列的。

melt_num_features = pd.melt(housepricesdata, value_vars=numerical_features)

grid = sns.FacetGrid(melt_num_features, col="variable", col_wrap=5, sharex=False, sharey=False)
grid = grid.map(sns.distplot, "value", color="blue") 

前面的代码展示了使用分布图观察数值变量观测值的单变量分布:

  1. 现在,我们使用categorical_features变量来绘制按每个分类变量划分的房价分布:
melt_cat_features = pd.melt(housepricesdata, id_vars=['SalePrice'], value_vars=categorical_features)

grid = sns.FacetGrid(melt_cat_features, col="variable", col_wrap=2, sharex=False, sharey=False, size=6)
grid.map(sns.boxplot, "value", "SalePrice", palette="Set3")
grid.fig.subplots_adjust(wspace=1, hspace=0.25)

for ax in grid.axes.flat: 
    plt.setp(ax.get_xticklabels(), rotation=90)

在我们的数据集中,我们看到存在各种属性,这些属性可以推动房价。我们可以尝试查看属性与表示房价的SalesPrice变量之间的关系。

让我们查看以下图表中每个分类变量按房价的分布:

  1. 我们现在将使用以下代码查看所有数值变量的相关矩阵:
# Generate a correlation matrix for all the numerical variables
corr=housepricesdata[numerical_features].corr()
print(corr)

这将给出以下输出:

可能很难以先前的格式查看显示的相关性。你可能想通过图形方式查看相关性。

  1. 我们还可以查看数值变量的相关矩阵图。为了做到这一点,我们使用在步骤 3中创建的numerical_features变量来存储所有数值变量的名称:
# Get correlation of numerical variables
df_numerical_features= housepricesdata.select_dtypes(include=[np.number])

correlation= df_numerical_features.corr()
correlation["SalePrice"].sort_values(ascending=False)*100
# Correlation Heat Map (Seaborn library)
f, ax= plt.subplots(figsize=(14,14))
plt.title("Correlation of Numerical Features with Sale Price", y=1, size=20)

# cmap - matplotlib colormap name or object - can be used to set the color options
# vmin and vmax is used to anchor the colormap
sns.heatmap(correlation, square= True, vmin=-0.2, vmax=0.8, cmap="YlGnBu")

在前面的代码中,我们使用了select_dtypes(include=[np.number])来创建df_numeric_features变量。然而,在第 3 步中,我们使用了dtypes[housepricesdata.dtypes != "object"].index。请注意,select_dtypes()返回一个pandas.DataFrame,而dtypes[].index返回一个pandas.Index对象。

我们现在可以如下可视化相关图:

图片

cmap是 Matplotlib 颜色映射对象。颜色映射有多种类别,包括顺序、发散和定性。在顺序颜色中,您可以选择将cmap参数设置为BuPuYlGn。对于定性颜色,您可以设置为Set3Pastel2等值。有关颜色选项的更多信息,请参阅matplotlib.org/tutorials/colors/colormaps.html

  1. 您可能还想评估您的数值变量与SalePrice的相关性,以了解这些数值变量与房价之间的关系:
row_count = 11
col_count = 3

fig, axs = plt.subplots(row_count, col_count, figsize=(12,36))
exclude_columns = ['Id', 'SalePrice']
plot_numeric_features = [col for col in numerical_features if col not in exclude_columns]

for eachrow in range(0, row_count):
    for eachcol in range(0, col_count): 
        i = eachrow*col_count + eachcol
        if i < len(plot_numeric_features):
            sns.regplot(housepricesdata[plot_numeric_features[i]], housepricesdata['SalePrice'], \
                ax = axs[eachrow][eachcol], color='purple', fit_reg=False)

# tight_layout automatically adjusts subplot params so that the subplot(s) fits in to the figure area 
plt.tight_layout() 
plt.show()

以下截图展示了相关图。在这里,我们绘制了每个数值变量与SalePrice之间的相关性:

图片

  1. 如果您想数值地评估您的数值变量与房价的相关性,可以使用以下命令:
# See correlation between numerical variables with house prices
corr=housepricesdata.corr()["SalePrice"]

# Sort the correlation values. 
# Use [::-1] to sort it in descending manner
# Use [::+1] to sort it in ascending manner
corr[np.argsort(corr)[::-1]]

您可以在以下表格中按降序查看相关输出:

图片

它是如何工作的...

第 1 步中,我们首先读取并描述了我们的数据。这一步为我们提供了数据集的摘要统计信息。我们在第 2 步中查看每种数据类型的变量数量。

第 3 步中,我们创建了两个变量,即numerical_featurescategorical_features,分别用于存储数值变量和分类变量的名称。当我们分别处理数值和分类特征时,我们使用了这两个变量。

第 4 步和第 5 步中,我们使用了seaborn库来绘制我们的图表。我们还介绍了 pandas 中的melt()函数,该函数可以用来重塑我们的 DataFrame 并将其馈送到seaborn库的FacetGrid()函数。在这里,我们展示了如何一次性绘制所有数值变量的分布图。我们还展示了如何使用相同的FacetGrid()函数来绘制每个分类变量SalesPrice的分布。

我们在第 6 步中使用 DataFrame 对象的corr()函数生成了相关矩阵。然而,我们注意到,当变量太多时,显示并不便于您识别相关性。在第 7 步中,我们通过使用seaborn库中的heatmap()函数绘制了相关矩阵热图。

corr()函数计算变量的成对相关性,排除缺失值。默认使用pearson方法来计算相关性。根据需要,你也可以使用kendallspearman方法。更多信息可以在bit.ly/2CdXr8n找到。

步骤 8中,我们看到了如何使用散点图矩阵来分析数值变量与房价的相关性。我们使用seaborn库中的regplot()函数生成了散点图矩阵。请注意,我们使用了一个参数fit_reg=False来从散点图中移除回归线。

步骤 9中,我们重复了步骤 8,以数值格式查看数值变量与房价的关系,而不是散点图。我们还通过将[::-1]参数传递给corr()函数来按降序排序输出。

更多内容...

我们已经看到了一些探索数据的方法,无论是从统计上还是从视觉上。Python 中有许多库可以用来可视化数据。其中最广泛使用的是ggplot。在我们查看一些命令之前,让我们先了解ggplot是如何工作的。

ggplot中有七个语法元素层,其中前三层是强制性的:

  • 数据

  • 美学

  • 几何学

  • 分面

  • 统计

  • 坐标

  • 主题

你通常会先提供一个数据集给ggplot()。然后,使用aes()函数提供一个美学映射,将变量映射到xy轴。使用aes(),你还可以设置图表的颜色、大小、形状和位置。然后,使用如geom_point()geom_histogram()之类的函数添加你想要的几何形状。你还可以添加各种选项,例如绘制统计摘要、分面、视觉主题和坐标系。

以下代码是对本章中已经使用过的内容的扩展,因此我们将直接进入ggplot代码部分:

f = pd.melt(housepricesdata, id_vars=['SalePrice'],value_vars= numerical_features[0:9])
ggplot(f,aes('value', 'SalePrice')) + geom_point(color='orange') + facet_wrap('variable',scales='free')

上述代码生成了以下图表:

图片

同样,为了查看数值变量的密度图,我们可以执行以下代码:

f_1 = pd.melt(housepricesdata, value_vars=numerical_features[0:9])
ggplot(f_1, aes('value')) + geom_density(color="red") + facet_wrap('variable',scales='free') 

该图显示了每个数值变量的单变量密度图。geom_density()计算并绘制核密度估计,这是直方图的平滑版本:

图片

参见

seaborn库的指南(bit.ly/2iU2aRU)

第二章:开始学习集成机器学习

在本章中,我们将介绍以下食谱:

  • 最大投票

  • 平均值

  • 加权平均

集成机器学习简介

简而言之,集成机器学习是一种将多个学习者的输出集成并应用于数据集进行预测的技术。这些多个学习者通常被称为基学习器。当使用多个基模型提取预测并将它们组合成一个单一的预测时,这个预测很可能比单个基学习器提供更高的准确性。

集成模型因其性能优于单个模型而闻名。它们可以应用于回归和分类问题。你可以选择使用同一家族的算法构建集成模型,或者选择从不同家族中选择。如果仅使用神经网络在同一个数据集上构建多个模型,那么这个集成模型被称为同质集成模型。如果使用不同的算法,如支持向量机SVMs)、神经网络和随机森林构建多个模型,那么这个集成模型被称为异质集成模型

构建集成模型需要两个步骤:

  1. 基学习器是设计并拟合在训练数据上的学习者

  2. 通过使用特定的集成技术,如最大投票、平均和加权平均,将基学习器组合成一个单一的预测模型。

下图显示了集成模型的结构:

图片

然而,为了得到性能良好的集成模型,基学习器本身应该尽可能准确。衡量模型性能的常见方法是对其泛化误差进行评估。泛化误差是一个术语,用来衡量模型基于一个模型尚未见过的新的数据集进行预测的准确性。

为了表现良好,集成模型需要足够的数据。当拥有大型和非线性数据集时,集成技术证明更有用。

如果包含太多的模型,集成模型可能会过拟合,尽管这种情况并不常见。

无论你如何微调你的模型,都存在高偏差或高方差的风险。即使是最优秀的模型,如果在训练模型时没有考虑到偏差和方差,也可能失败。偏差和方差都代表了预测中的一种错误。实际上,总误差由偏差相关误差、方差相关误差和不可避免的噪声相关误差(或不可减少误差)组成。噪声相关误差主要由于训练数据中的噪声引起,无法消除。然而,偏差和方差引起的误差可以减少。

总误差可以表示如下:

Total Error = Bias ^ 2 + Variance + Irreducible Error

一个如均方误差MSE)这样的度量可以捕捉连续目标变量的所有误差,可以表示如下:

img/89f95d7f-2f1e-4f9e-b2c4-3eda6906ffb2.png

在这个公式中,E代表期望均值,Y代表实际的目标值,而img/9e4b0739-22af-4cc1-816a-a36dd771abf2.png是目标变量的预测值。它可以分解为其组成部分,如偏差、方差和噪声,如下公式所示:

img/21b41a6e-ac17-4354-ae2b-daea48ef913e.png

当偏差指的是真实值与我们的估计期望值有多接近时,方差另一方面衡量的是与期望估计值偏差的程度。具有小 MSE 的估计器是可取的。为了最小化 MSE 误差,我们希望我们的估计值在真实值上居中(0 偏差)并且与真实值(正确)的偏差低(低方差)。换句话说,我们希望对估计值的价值有信心(低方差,低不确定性,分布更尖锐)。高偏差会降低算法在训练数据集上的性能并导致欠拟合。另一方面,高方差的特点是训练误差低而验证误差高。高方差会降低学习者在未见数据上的性能,导致过拟合。

集成模型可以减少模型中的偏差和/或方差。

最大投票法

最大投票法,通常用于分类问题,是结合多个机器学习算法预测的简单方法之一。

在最大投票法中,每个基础模型做出预测并对每个样本进行投票。只有获得最高票数的样本类别被包含在最终的预测类别中。

例如,假设我们有一个在线调查,消费者在五级李克特量表上回答一个问题。我们可以假设一些消费者会给出五分的评价,而其他人会给出四分的评价,依此类推。如果大多数消费者,比如说超过 50%的消费者给出了四分的评价,那么最终的评分就是四分。在这个例子中,将最终评分定为四分类似于对所有评分取众数。

准备工作

在以下步骤中,我们将下载以下包:

首先,导入ospandas包,并根据您的需求设置工作目录:

# import required packages
import os
import pandas as pd

# Set working directory as per your need
os.chdir(".../.../Chapter 2")
os.getcwd()

从 GitHub 下载Cryotherapy.csv数据集并将其复制到您的工作目录。读取数据集:

df_cryotherapydata = pd.read_csv("Cryotherapy.csv")

使用以下代码查看数据:

df_cryotherapydata.head(5)

我们可以看到数据已被正确读取,并且有Result_of_Treatment类变量。然后我们继续创建以Result_of_Treatment作为响应变量的模型。

如何做到这一点...

您可以使用 Python 的scikit-learn库中的VotingClassifier类为分类问题创建一个投票集成模型。以下步骤展示了如何将决策树、SVM 和逻辑回归模型的预测组合起来解决一个分类问题:

  1. 导入构建决策树、SVM 和逻辑回归模型所需的库。我们还导入VotingClassifier进行最大投票:
# Import required libraries
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import VotingClassifier
  1. 我们接下来构建我们的特征集并创建我们的训练和测试数据集:
# We create train & test sample from our dataset
from sklearn.cross_validation import train_test_split

# create feature & response sets
feature_columns = ['sex', 'age', 'Time', 'Number_of_Warts', 'Type', 'Area']
X = df_cryotherapydata[feature_columns]
Y = df_cryotherapydata['Result_of_Treatment']

# Create train & test sets
X_train, X_test, Y_train, Y_test = \
train_test_split(X, Y, test_size=0.20, random_state=1)
  1. 我们使用决策树、SVM 和逻辑回归算法构建我们的模型:
# create the sub models
estimators = []

dt_model = DecisionTreeClassifier(random_state=1)
estimators.append(('DecisionTree', dt_model))

svm_model = SVC(random_state=1)
estimators.append(('SupportVector', svm_model))

logit_model = LogisticRegression(random_state=1)
estimators.append(('Logistic Regression', logit_model))
  1. 我们使用我们选择的每个分类器构建单独的模型:
from sklearn.metrics import accuracy_score

for each_estimator in (dt_model, svm_model, logit_model):
    each_estimator.fit(X_train, Y_train)
    Y_pred = each_estimator.predict(X_test)
    print(each_estimator.__class__.__name__, accuracy_score(Y_test, Y_pred))

我们可以查看每个单个基学习器的准确度得分:

  1. 我们继续集成我们的模型,并使用VotingClassifier来评估集成模型的准确度:
#Using VotingClassifier() to build ensemble model with Hard Voting
ensemble_model = VotingClassifier(estimators=estimators, voting='hard')

ensemble_model.fit(X_train,Y_train)
predicted_labels = ensemble_model.predict(X_test) 

print("Classifier Accuracy using Hard Voting: ", accuracy_score(Y_test, predicted_labels))

我们可以使用Hard Voting查看集成模型的准确度得分:

它是如何工作的...

VotingClassifier实现了两种投票类型——硬投票软投票。在硬投票中,最终的类别标签被预测为被分类模型预测最频繁的类别标签。换句话说,所有分类器的预测被汇总以预测获得最多投票的类别。简单来说,它取预测类别标签的众数。

在对类别标签进行硬投票时,是基于每个分类器的多数投票预测,其中i=1.....n个观察值,我们得到以下结果:

如前节所示,我们有三个模型,一个来自决策树,一个来自 SVM,一个来自逻辑回归。假设这些模型将训练观察值分别分类为类别 1、类别 0 和类别 1。然后通过多数投票,我们得到以下结果:

在这种情况下,我们将观察值分类为类别 1。

在前一节中,在步骤 1中,我们导入了构建模型所需的库。在步骤 2中,我们创建了特征集。我们还分割数据以创建训练和测试样本。在步骤 3中,我们分别使用决策树、SVM 和逻辑回归训练了三个模型。在步骤 4中,我们查看每个基学习器的准确度得分,而在步骤 5中,我们使用VotingClassifier()集成模型并查看集成模型的准确度得分。

还有更多...

许多分类器可以估计类别概率。在这种情况下,类别标签是通过平均类别概率来预测的。这被称为软投票,并且对于一组调优良好的分类器来说是被推荐的。

scikit-learn库中,许多分类算法都有predict_proba()方法来预测类概率。要执行软投票集成,只需在VotingClassifier()中将voting='hard'替换为voting='soft'

以下代码创建了一个使用软投票的集成:

# create the sub models
estimators = []

dt_model = DecisionTreeClassifier(random_state=1)
estimators.append(('DecisionTree', dt_model))

svm_model = SVC(random_state=1, probability=True)
estimators.append(('SupportVector', svm_model))

logit_model = LogisticRegression(random_state=1)
estimators.append(('Logistic Regression', logit_model))

for each_estimator in (dt_model, svm_model, logit_model):
    each_estimator.fit(X_train, Y_train)
    Y_pred = each_estimator.predict(X_test)
    print(each_estimator.__class__.__name__, accuracy_score(Y_test, Y_pred))

# Using VotingClassifier() to build ensemble model with Soft Voting
ensemble_model = VotingClassifier(estimators=estimators, voting='soft')
ensemble_model.fit(X_train,Y_train)
predicted_labels = ensemble_model.predict(X_test) 
print("Classifier Accuracy using Soft Voting: ", accuracy_score(Y_test, predicted_labels))

我们可以看到单个学习器和使用软投票的集成学习器的准确率:

图片

SVC类默认不能估计类概率,因此我们在前面的代码中将它的概率超参数设置为True。当probability=True时,SVC将能够估计类概率。

平均

平均通常用于回归问题,或者在分类任务中估计概率时使用。预测是从多个模型中提取的,并使用预测的平均值来做出最终预测。

准备工作

让我们准备好构建多个学习器,并看看如何实现平均:

从 GitHub 下载whitewines.csv数据集并将其复制到工作目录,然后读取数据集:

df_winedata = pd.read_csv("whitewines.csv")

让我们用以下代码看看数据:

df_winedata.head(5)

在以下屏幕截图中,我们可以看到数据已被正确读取:

图片

如何做...

我们有一个基于葡萄酒特性的数据集。使用这个数据集,我们将构建多个回归模型,其中质量作为响应变量。使用多个学习器,我们可以提取多个预测。平均技术将取每个训练样本所有预测值的平均值:

  1. 导入所需的库:
# Import required libraries
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor
from sklearn.svm import SVR
  1. 创建响应和特征集:
# Create feature and response variable set
from sklearn.cross_validation import train_test_split

# create feature & response variables
feature_columns = ['fixed acidity', 'volatile acidity', 'citric acid', 'residual sugar','chlorides', 'free sulfur dioxide', 'total sulfur dioxide','density', 'pH', 'sulphates', 'alcohol']
X = df_winedata[feature_columns]
Y = df_winedata['quality']
  1. 将数据分为训练集和测试集:
# Create train & test sets
X_train, X_test, Y_train, Y_test = \
train_test_split(X, Y, test_size=0.20, random_state=1)
  1. 使用线性回归、SVR和决策树构建基础回归学习器:
# Build base learners
linreg_model = LinearRegression()
svr_model = SVR()
regressiontree_model = DecisionTreeRegressor()

# Fitting the model
linreg_model.fit(X_train, Y_train)
svr_model.fit(X_train, Y_train)
regressiontree_model.fit(X_train, Y_train)
  1. 使用基础学习器根据测试数据做出预测:
linreg_predictions = linreg_model.predict(X_test)
svr_predictions = svr_model.predict(X_test)
regtree_predictions = regressiontree_model.predict(X_test)
  1. 添加预测值并除以基础学习器的数量:
# We divide the summation of the predictions by 3 i.e. number of base learners 
average_predictions=(linreg_predictions + svr_predictions + regtree_predictions)/3

它是如何工作的...

步骤 1中,我们导入了所需的包。在步骤 2中,我们将特征集和响应变量从数据集中分离出来。在步骤 3中,我们将数据集分为训练样本和测试样本。

注意,我们的响应变量在本质上连续。因此,我们在步骤 4中使用了线性回归、SVR和决策树来构建回归基础学习器。在步骤 5中,我们将测试数据集传递给predict()函数来预测响应变量。最后,在步骤 6中,我们将所有预测值相加,并除以基础学习器的数量,在我们的例子中是三个。

加权平均

与平均一样,加权平均也用于回归任务。或者,它可以在分类问题中估计概率时使用。基础学习器被分配不同的权重,这些权重代表每个模型在预测中的重要性。

加权平均模型应该至少与你的最佳模型一样好。

准备工作

从 GitHub 下载wisc_bc_data.csv数据集并将其复制到你的工作目录。让我们读取数据集:

df_cancerdata = pd.read_csv("wisc_bc_data.csv")

使用以下代码查看数据:

df_cancerdata.head(5)

我们可以看到数据已经被正确读取:

图片

如何做到这一点...

在这里,我们有一个基于癌症肿瘤特性的数据集。使用这个数据集,我们将构建多个分类模型,其中diagnosis作为我们的响应变量。诊断变量具有BM值,表示肿瘤是良性还是恶性。使用多个学习器,我们提取多个预测。加权平均技术取每个训练样本的所有预测值的平均值。

在这个例子中,我们将预测概率视为输出,并使用 scikit-learn 算法的predict_proba()函数来预测类概率:

  1. 导入所需的库:
# Import required libraries
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression
  1. 创建响应和特征集:
# Create feature and response variable set
# We create train & test sample from our dataset
from sklearn.cross_validation import train_test_split

# create feature & response variables
X = df_cancerdata.iloc[:,2:32]
Y = df_cancerdata['diagnosis']

我们使用pandas DataFrame 的iloc()函数检索特征列,该函数是基于整数位置的索引选择,用于按位置选择。iloc()函数接受行和列选择作为其参数,形式为:data.iloc(<row selection>, <column selection>)。行和列选择可以是整数列表或行和列的切片。例如,它可能看起来如下:df_cancerdata.iloc(2:100, 2:30)

  1. 然后,我们将数据分为训练集和测试集:
# Create train & test sets
X_train, X_test, Y_train, Y_test = \
train_test_split(X, Y, test_size=0.20, random_state=1)
  1. 构建基础分类器模型:
# create the sub models
estimators = []

dt_model = DecisionTreeClassifier()
estimators.append(('DecisionTree', dt_model))

svm_model = SVC(probability=True)
estimators.append(('SupportVector', svm_model))

logit_model = LogisticRegression()
estimators.append(('Logistic Regression', logit_model))
  1. 在测试数据上拟合模型:
dt_model.fit(X_train, Y_train)
svm_model.fit(X_train, Y_train)
logit_model.fit(X_train, Y_train)
  1. 使用predict_proba()函数预测类概率:
dt_predictions = dt_model.predict_proba(X_test)
svm_predictions = svm_model.predict_proba(X_test)
logit_predictions = logit_model.predict_proba(X_test)
  1. 为每个模型分配不同的权重以获得最终的预测:
weighted_average_predictions=(dt_predictions * 0.3 + svm_predictions * 0.4 + logit_predictions * 0.3)

它是如何工作的...

步骤 1中,我们导入了构建我们模型所需的库。在步骤 2中,我们创建了响应和特征集。我们使用pandas DataFrame 的iloc()函数检索我们的特征集。在步骤 3中,我们将数据集分为训练集和测试集。在步骤 4中,我们构建了我们的基础分类器。请注意,我们向SVC函数传递了probability=True,以允许SVC()返回类概率。在SVC类中,默认值为probability=False

步骤 5中,我们将模型拟合到训练数据。在步骤 6中,我们使用predict_proba()函数预测测试观察值的类概率。

最后,在步骤 7中,我们为我们的每个模型分配了不同的权重来估计加权平均预测。随之而来的问题是如何选择权重。一种方法是对权重进行均匀采样,并确保它们归一化到 1,然后在测试集上进行验证并重复,同时跟踪提供最高准确率的权重。这是一个随机搜索的例子。

参见

以下是一些 scikit 参考链接:

第三章:重抽样方法

在本章中,我们将介绍抽样的基本概念。我们还将了解重抽样及其重要性。

抽样是从总体中选择观测子集的过程,目的是估计关于整个总体的某些参数。另一方面,重抽样方法用于改进对总体参数的估计。

本章将涵盖以下食谱:

  • 抽样简介

  • k 折交叉验证和留一法交叉验证

  • 自举抽样

抽样简介

抽样技术可以大致分为非概率抽样技术和概率抽样技术。非概率抽样技术基于用户的判断,而在概率抽样中,观测值是通过机会选择的。

概率抽样通常包括简单随机抽样(SRS)、分层抽样和系统抽样:

  • SRS:在简单随机抽样(SRS)中,总体中的每个观测值都有同等的机会被选中作为样本。

  • 分层抽样:在分层抽样中,总体数据被分为不同的组,称为。然后从每个组中抽取概率样本。

  • 系统抽样:在此方法中,通过选择固定间隔的观测值从总体中抽取样本。

如果样本太小或太大,可能会导致错误的发现。因此,确保我们有正确的样本大小非常重要。一个设计良好的样本可以帮助识别可能导致预期结果准确性和可靠性偏差的因素。

由于各种原因,我们的样本可能会引入错误。例如,由于随机抽样可能发生的错误,这被称为抽样误差,或者由于抽取观测值的方法导致样本偏斜,这被称为样本偏差

准备工作

在第一章“更接近你的数据”中,我们操作并准备了HousePrices.csv文件中的数据,并处理了缺失值。在这个例子中,我们将使用最终数据集来演示这些抽样和重抽样技术。

您可以从 GitHub 获取准备好的数据集。

我们将导入所需的库。我们将读取数据并查看数据集的维度:

# import os for operating system dependent functionalities
import os

# import other required libraries
import pandas as pd
from sklearn.model_selection import train_test_split

# Set your working directory according to your requirement
os.chdir(".../Chapter 3/Resampling Methods")
os.getcwd()

让我们读取我们的数据。我们将 DataFrame 名称前缀为df_以使其更容易理解:

df_housingdata = pd.read_csv("Final_HousePrices.csv")

在下一节中,我们将探讨如何使用sklearn.model_selection中的train_test_split()来将我们的数据分割成随机的训练和测试子集。

如何操作...

现在我们已经读取了我们的数据集,让我们看看如何进行抽样:

  1. 我们检查 DataFrame 的维度,如下所示:
df_housingdata.shape

我们可以看到我们的 DataFrame 的维度:

图片

  1. 然后我们检查我们的 DataFrame 是否有任何缺失值:
df_housingdata.isnull().sum()

我们注意到df_housingdata中没有缺失值。

  1. 我们将预测变量和响应变量分别放入两个不同的 DataFrame 中,如下所示:
# create feature & response variables
X = df_housingdata.iloc[:,0:79]
Y = df_housingdata['SalePrice']
  1. 我们使用train_test_split()将我们的预测变量和响应变量数据集分割成训练和测试子集:
# Create train & test sets
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, train_size=0.7, test_size=0.3)
  1. 我们可以按照以下方式找到每个子集中的观测值和列数:
print(X_train.shape)
print(Y_train.shape)
print(X_test.shape)
print(Y_test.shape)

我们可以看到 70%的数据被分配到了训练数据集中,而 30%被分配到了测试数据集中:

它是如何工作的...

步骤 1步骤 2中,我们查看 DataFrame 的维度,发现我们的数据集没有缺失值。在步骤 3中,我们将特征和响应变量分离出来。在步骤 4中,我们使用sklearn.model_selection中的train_test_split()函数来分割我们的数据并创建训练和测试子集。注意,我们传递了两个参数train_sizetest_size,并分别将它们的值设置为0.70.3train_sizetest_size可以取 0.0 到 1.0 之间的值,这些值代表分配给每个数据集的比例。如果提供了一个整数值,那么这个数字代表观测值的绝对数量。

我们可以选择不提供这两个参数中的任何一个,即train_sizetest_size。如果我们把train_size的值设置为None或者根本不提供它,那么它的值会自动设置为与测试大小相补充。同样,如果test_size未指定或者我们将其值设置为None,它的值会自动设置为与训练大小相补充。

步骤 5中,我们查看了由train_test_split()函数创建的子集的形状。

还有更多...

在这个例子中,我们将使用一个数据集来衡量一个二分分类的目标变量。重要的是要理解,我们的目标变量的两个类别在训练和测试子集中分布是相似的:

  1. 我们首先读取我们的数据集并查看其维度:
df_creditcarddata = pd.read_csv("creditcarddefault.csv")
df_creditcarddata.shape

我们有 30,000 个观测值和 25 个变量。最后一个变量,下个月默认付款,是我们的目标变量,其值要么是0要么是1

  1. 我们将数据分成特征集和响应变量,并使用以下代码将其分割成训练和测试子集:
# create feature & response set
X = df_creditcarddata.iloc[:,0:24]
Y = df_creditcarddata['default payment next month']

# Create train & test sets
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, train_size=0.7, test_size=0.3, stratify=Y)

注意,这次我们在train_test_split()函数中使用了参数stratifystratify参数使得分割后的样本中值的比例等于提供给它的变量的值的比例。此外,我们还把响应变量Y分配给了stratify参数。

现在,我们可以看到训练和测试子集中目标变量的二分类分布:

print(pd.value_counts(Y_train.values)*100/Y_train.shape)
print(pd.value_counts(Y_test.values)*100/Y_test.shape)

在下面的输出中,我们可以看到两个子集中类的分布是相同的:

我们还可以传递另一个参数shuffletrain_test_split()。这个参数接受布尔值TrueFalse,以指示在分割数据之前是否要打乱数据。如果shuffle=False,则stratify必须是None

参考信息

k-fold 和留一法交叉验证

机器学习模型在应用于未见数据以进行预测时,通常会面临泛化问题。为了避免这个问题,模型不是使用完整的数据集进行训练。相反,数据集被划分为训练集和测试集。模型在训练数据上训练,并在测试集上评估,而测试集在训练过程中并未看到。这是交叉验证的基本思想。

最简单的交叉验证类型是保留方法,我们在之前的配方“采样介绍”中看到了它。在保留方法中,当我们把数据分成训练集和测试集时,由于数据的高维性,测试集可能并不那么类似于训练集。这可能导致结果的不稳定。因此,我们高效地采样数据非常重要。我们可以使用其他交叉验证方法来解决这个问题,例如留一法交叉验证LOOCV)或k-fold 交叉验证k-fold CV)。

k-fold CV 是一种广泛使用的方法,用于估计测试误差。原始数据集包含N个观测值,被划分为K个子集,并且保留方法被重复K次。在每次迭代中,K-1个子集被用作训练集,其余的用作测试集。误差的计算如下:

图片

在 LOOCV 中,子集的数量K等于数据集中观测值的数量N。LOOCV 使用原始数据集中的一个观测值作为验证集,其余N-1个观测值作为训练集。这被迭代N次,以便样本中的每个观测值在每个迭代中都用作验证数据。这与 k-fold CV 相同,其中K等于N,即数据集中的数据点数量。由于需要大量的迭代,LOOCV 通常需要大量的计算能力。

在 LOOCV 中,每个折叠的估计值高度相关,它们的平均值可能有很高的方差。

测试误差的估计基于单个观测值,表示为MSE = 图片。我们可以计算所有折叠的 MSE 的平均值如下:

图片

这种计算与 k-fold CV 中涉及的计算没有不同。我们将使用 scikit-learn 库来查看如何实现 k-fold CV 和 LOOCV 等技术。

准备工作

在下面的代码块中,我们可以看到如何导入所需的库:

import pandas as pd

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.model_selection import KFold 
import matplotlib.pyplot as plt

我们读取我们的数据,并将特征和响应变量分开:

# Let's read our data. 
df_autodata = pd.read_csv("autompg.csv")

# Fill NAs with the median value
df_autodata['horsepower'].fillna(df_autodata['horsepower'].median(), inplace=True)

# Drop carname variable
df_autodata.drop(['carname'], axis=1, inplace=True)

X = df_autodata.iloc[:,1:8]
Y = df_autodata.iloc[:,0]
X=np.array(X)
Y=np.array(Y)

如何做到这一点...

k 折交叉验证提供给我们训练和测试索引,以便将数据分为训练集和测试集:

  1. 我们将数据集分成 K 个连续的折叠(默认情况下不进行洗牌),其中 K=10
kfoldcv = KFold(n_splits=10)
kf_ytests = []
kf_predictedvalues = []
mean_mse = 0.0

for train_index, test_index in kfoldcv.split(X):
    X_train, X_test = X[train_index], X[test_index] 
    Y_train, Y_test = Y[train_index], Y[test_index]

    model = LinearRegression()
    model.fit(X_train, Y_train) 
    Y_pred = model.predict(X_test)

    # there is only one y-test and y-pred per iteration over the kfoldcv.split, 
    # so we append them to the respective lists.

    kf_ytests += list(Y_test)
    kf_predictedvalues += list(Y_pred)
    mse = mean_squared_error(kf_ytests, kf_predictedvalues)
    r2score = r2_score(kf_ytests, kf_predictedvalues)
    print("R²: {:.2f}, MSE: {:.2f}".format(r2score, mse))
    mean_mse += mse   
  1. 我们可以使用 r2_score() 来查看我们的决定系数,并使用 mse() 来查看我们的均方误差:
print("Average CV Score :" ,mean_mse/10) 

上述代码的结果如下:

  1. 我们将预测值与响应变量的实际值进行绘图:
## Let us plot the model
plt.scatter(kf_ytests, kf_predictedvalues)
plt.xlabel('Reported mpg')
plt.ylabel('Predicted mpg')

上述代码生成的图如下:

它是如何工作的...

步骤 1 中,k 折交叉验证将数据集分成 K 个连续的折叠,其中 K=10。k 折交叉验证提供给我们训练和测试索引,然后分割数据为训练集和测试集。在 步骤 2 中,我们使用了 r2_score() 来查看决定系数,并使用 mse() 来查看均方误差。决定系数和均方误差分别是 79% 和 12.85。在 步骤 3 中,我们绘制了预测值与响应变量 mpg 的实际值。

还有更多...

现在,我们将使用 sklearn.model_selection 中的 LeaveOneOut 来进行相同的 LOOCV 练习:

  1. 我们将再次读取我们的数据,并将其分为特征集和响应集:
# Let's read our data. 
df_autodata = pd.read_csv("autompg.csv")

# Fill NAs with the median value
df_autodata['horsepower'].fillna(df_autodata['horsepower'].median(), inplace=True)

# Drop carname variable
df_autodata.drop(['carname'], axis=1, inplace=True)

X = df_autodata.iloc[:,1:8]
Y = df_autodata.iloc[:,0]
X=np.array(X)
Y=np.array(Y)
  1. 我们使用 LOOCV 来构建我们的模型:
from sklearn.model_selection import LeaveOneOut 
loocv = LeaveOneOut()

loo_ytests = []
loo_predictedvalues = []
mean_mse = 0.0

for train_index, test_index in loocv.split(X):
    # the below requires arrays. So we converted the dataframes to arrays
    X_train, X_test = X[train_index], X[test_index] 
    Y_train, Y_test = Y[train_index], Y[test_index]

    model = LinearRegression()
    model.fit(X_train, Y_train) 
    Y_pred = model.predict(X_test)

    # there is only one y-test and y-pred per iteration over the loo.split, 
    # so we append them to the respective lists.

    loo_ytests += list(Y_test)
    loo_predictedvalues += list(Y_pred)

    mse = mean_squared_error(loo_ytests, loo_predictedvalues)
    r2score = r2_score(loo_ytests, loo_predictedvalues)
    print("R²: {:.2f}, MSE: {:.2f}".format(r2score, mse))
    mean_mse += mse 
  1. 我们可以使用 r2_score() 来查看我们的决定系数,并使用 mse() 来查看我们的均方误差:
print("Average CV Score :" ,mean_mse/X.shape[0]) 

我们可以查看 LOOCV 结果的决定系数和均方误差:

  1. 我们可以将预测值与响应变量的实际值进行绘图:
## Let us plot the model
plt.scatter(kf_ytests, kf_predictedvalues)
plt.xlabel('Reported mpg')
plt.ylabel('Predicted mpg')

上述代码生成的图给出了以下输出:

在 LOOCV 中,分割方法没有随机性,所以它总是会给你相同的结果。

分层 k 折交叉验证方法常用于分类问题。这是 k 折交叉验证方法的一种变体,它返回分层折叠。每个集合包含与原始数据集相似百分比的每个目标类的样本。StratifiedShuffleSplit 是洗牌分割的变体,通过保持每个目标类的相同百分比来创建分割。

参见

自举

Bootstrapping 基于 1949 年由 Quenouille 提出的 jackknife 方法,并在 1958 年由 Tukey 改进。jackknife 方法用于检验假设和估计置信区间。它是通过在每次移除每个观测值后计算估计值,然后计算这些计算的平均值来获得的。对于大小为N的样本,可以通过聚合每个N-1大小的子样本的估计值来找到 jackknife 估计值。它与 bootstrap 样本类似,但 while bootstrap 方法是带替换的抽样,jackknife 方法是不带替换地抽样数据。

Bootstrapping 是一种强大的非参数重抽样技术,用于评估估计值的不确定性。在 bootstrapping 中,从原始样本中反复抽取具有相同大小的大量样本。这允许一个给定的观测值被包含在多个样本中,这被称为带替换的抽样。在 bootstrap 方法中,通过带替换抽样从原始数据中创建n个样本。每个样本的大小相同。n越大,样本集将越接近理想的 bootstrap 样本。

“Bootstrapping 的本质是这样一个想法:在没有关于总体任何其他知识的情况下,从总体中随机抽取大小为 n 的样本中找到的值的分布是总体分布的最佳指南。因此,为了近似如果总体被重新抽样的情况,重新抽样样本是合理的。换句话说,由 n 个观测样本值组成的无限总体,每个值有 1/n 的概率,被用来模拟未知的真实总体。”

–Bryan F. J. Manly

一个 bootstrap 样本的图示表示如下:

图片

如前图所示,S1子集中的部分数据点也出现在S2S4中。

假设我们从原始样本中获得了n个 bootstrap 样本。图片表示n个 bootstrap 样本的估计值,其中i=1,2,3...,n。如果图片表示原始样本的参数估计值,那么图片的标准误差如下给出:

图片

图片如下给出:

图片

图片表示n个 bootstrap 样本的估计值的平均值。

准备中

我们需要像往常一样导入所需的库。这次,我们将使用来自sklean.utilsresample类,这是我们之前没有使用过的:

import pandas as pd
import numpy as np

from sklearn.model_selection import train_test_split
from sklearn.linear_model import SGDRegressor
from sklearn.metrics import mean_squared_error, r2_score
import matplotlib.pyplot as plt

from sklearn.utils import resample

我们加载我们的数据,并用horsepower变量的中位数填充缺失值。我们还删除了carname变量:

# Let's read our data. We prefix the data frame name with "df_" for easier understanding.
df_autodata = pd.read_csv("autompg.csv")
df_autodata['horsepower'].fillna(df_autodata['horsepower'].median(), inplace=True)
df_autodata.drop(['carname'], axis=1, inplace=True)

如何做到这一点...

现在我们已经读取了数据,让我们看看我们如何进行 bootstrap 采样:

  1. 我们编写了一个自定义函数,create_bootstrap_oob(),它接受一个 DataFrame 作为参数,并使用sklearn.utils中的resample()函数创建了一个包含 100 个观察值的 bootstrap 样本:
# This custom function takes a dataframe as an argument
def create_bootstrap_oob(df):
    global df_OOB
    global df_bootstrap_sample 

    # creating the bootstrap sample
    df_bootstrap_sample = resample(df, replace=True, n_samples=100)

    # creating the OOB sample 
    bootstrap_sample_index = tuple(df_bootstrap_sample.index)
    bootstrap_df = df.index.isin(bootstrap_sample_index)
    df_OOB = df[~bootstrap_df]
  1. 我们循环了 50 次迭代,并通过传递df_autodata DataFrame 调用了自定义函数。我们记录了每个 bootstrap 样本中mpg变量的平均值,这些平均值将与我们原始 DataFrame 中mpg变量的平均值进行比较,即df_autodata
iteration=50
bootstap_statistics=list()
originalsample_statistics=list()

for i in range(iteration):
    # Call custom function create_bootstrap_oob(). Pass df_autodata
    create_bootstrap_oob(df_autodata)

    # Capture mean value of mpg variable for all bootstrap samples
    bootstap_statistics.append(df_bootstrap_sample.iloc[:,0].mean())

    originalsample_statistics.append(df_autodata['mpg'].mean())
  1. 我们绘制了每个迭代中mpg变量的平均值,其中考虑了单独的 bootstrap 样本。我们在每个迭代中记录了每个 bootstrap 样本中mpg变量的平均值:
import matplotlib.pyplot as plt
f, ax= plt.subplots(figsize=(6,6))

plt.plot(bootstap_statistics, 'c--', label='Bootstrap Sample Statistic')
plt.plot(originalsample_statistics, 'grey', label='Original Sample Statistic')
plt.xlabel('Iterations')
plt.ylabel('Statistic (Mean of mpg)')
plt.legend(loc=4)
plt.show()

我们最终绘制了mpg变量的平均值与每个迭代的对比图,如下所示:

图片

它是如何工作的...

步骤 1中,我们创建了一个自定义函数,create_bootstrap_oob( ),并使用sklearn.utils中的resample()函数创建了一个包含 100 个观察值的 bootstrap 样本。create_bootstrap_oob( )自定义函数接受一个 DataFrame 作为输入参数,并创建了 bootstrap 和袋外样本OOB)。

我们提到 bootstrap 采样是带有替换的采样。这意味着任何给定的观察值可以在单个样本中出现多次。

步骤 2中,我们循环了 50 次迭代,并通过传递df_autoframe调用了create_bootstrap_oob( )自定义函数。我们记录了每个 bootstrap 样本中mpg变量的平均值。在步骤 3中,我们为每个迭代考虑了一个单独的 bootstrap 样本。我们记录了每个迭代中mpg变量的平均值,然后绘制了每个迭代中mpg变量的平均值。

参见

  • scikit-learn 指南中的sklearn.cross_validation.Bootstrapbit.ly/2RC5MYv

第四章:统计与机器学习算法

在本章中,我们将介绍以下内容:

  • 多元线性回归

  • 逻辑回归

  • 朴素贝叶斯

  • 决策树

  • 支持向量机

技术要求

本章的技术要求与我们在第一章“接近你的数据”中详细说明的要求相同。

访问 GitHub 仓库以获取数据集和代码。这些数据集和代码按章节和主题名称排列。例如,对于线性回归数据集和代码,请访问.../Chapter 3/Linear regression

多元线性回归

多元线性回归是一种用于训练线性模型的技术,它假设多个预测变量(![img/a873ef1f-dd8a-4450-8afa-5e5c3c4b2dc5.png])与连续目标变量(![img/9d832a44-5813-46fb-9d89-681fc4ed016a.png])之间存在线性关系。具有 m 个预测变量的多元线性回归的一般方程如下:

![img/088198d7-1233-4571-9fcc-61c6dba157ad.png]

![img/19a78dc6-d3ee-4492-b89e-6d42851d79b0.png]

训练线性回归模型涉及估计每个预测变量(用字母表示)的系数的值,如上图所示 ![img/a343305a-25c0-4a07-8783-5045b92efe76.png]。在先前的方程中,![img/9c4fefc7-e6fe-4dd2-a801-a379f072ae3c.png] 表示误差项,它服从正态分布,具有零均值和恒定方差。这可以表示如下:

![img/8302e115-092f-45d0-b4be-8689554c67a9.png]

可以使用各种技术来构建线性回归模型。最常用的是普通最小二乘法(OLS)估计。OLS 方法用于生成一个线性回归线,该线试图最小化平方误差的总和。误差是实际数据点到回归线的距离。平方误差的总和衡量了训练实例(每个数据点)与回归线预测值之间的平方差的总体。这可以表示如下:

![img/20b55abb-3b52-41db-8c2e-e1d7b36c8d0c.png]

在先前的方程中,![img/bcc37182-bc5c-4ce3-9f45-8e124cd79d81.png] 是实际的训练实例,而 ![img/5d58def7-dceb-4c83-8add-15c9237d55b0.png] 是回归线预测的值。

在机器学习的背景下,梯度下降是一种常用的技术,可以通过最小化模型的训练误差(通过多次迭代)来优化预测变量的系数。梯度下降首先将系数初始化为零。然后,通过更新系数以最小化误差的目的来更新系数。更新系数是一个迭代过程,并且会一直执行,直到达到最小平方误差。

在梯度下降技术中,有一个称为学习率的超参数,表示为

通过![](https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/ensm-ml-cb/img/6ae71158-dd81-4087-b5ce-5d8e76ce02c4.png)提供给算法。此参数决定了算法向系数最优值移动的速度。如果![](https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/ensm-ml-cb/img/556b7d9a-e0a6-4d3f-af81-cda64717e7ed.png)非常大,算法可能会跳过最优解。然而,如果它太小,算法可能需要太多迭代才能收敛到最优系数值。因此,使用正确的![](https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/ensm-ml-cb/img/f2895db5-0f99-4a3e-801f-5b9594ecf991.png)值非常重要。

在这个菜谱中,我们将使用梯度下降法来训练我们的线性回归模型。

准备工作

在第一章《更接近你的数据》中,我们查看了HousePrices.csv文件,并探讨了如何操作和准备我们的数据。我们还分析了数据集中的缺失值。现在,我们将使用这个最终数据集进行我们的模型构建练习,使用线性回归:

在以下代码块中,我们将首先导入所需的库:

# import os for operating system dependent functionalities
import os

# import other required libraries
import pandas as pd
import numpy as np
import seaborn as sns 
import matplotlib.pyplot as plt

我们使用os.chdir()命令设置我们的工作目录:

# Set your working directory according to your requirement
os.chdir(".../Chapter 4/Linear Regression")
os.getcwd()

让我们读取我们的数据。我们使用df_作为 DataFrame 名称的前缀,以便我们能够轻松理解:

df_housingdata = pd.read_csv("Final_HousePrices.csv")

如何操作...

让我们继续构建我们的模型。我们将从识别数值变量和分类变量开始。我们使用相关矩阵和相关图来研究相关性。

  1. 首先,我们将查看变量及其类型:
# See the variables and their data types
df_housingdata.dtypes
  1. 接下来,我们将查看相关矩阵。corr()方法计算列之间的成对相关系数:
# We pass 'pearson' as the method for calculating our correlation
df_housingdata.corr(method='pearson')
  1. 此外,我们还想研究预测变量与响应变量之间的相关性:
# we store the correlation matrix output in a variable
pearson = df_housingdata.corr(method='pearson')

# assume target attr is the last, then remove corr with itself
corr_with_target = pearson.iloc[-1][:-1]

# attributes sorted from the most predictive
corr_with_target.sort_values(ascending=False)

我们还可能想要按绝对值对相关性进行排序。为了做到这一点,我们可以使用以下命令:corr_with_target[abs(corr_with_target).argsort()[::-1]]

  1. 我们可以使用seaborn包中的heatmap()函数查看相关图:
f, ax = plt.subplots(figsize=(11, 11))

# Generate a mask for the upper triangle
# np.zeros_like - Return an array of zeros with the same shape and type as a given array
# In this case we pass the correlation matrix
# we create a variable “mask” which is a 14 X 14 numpy array

mask = np.zeros_like(pearson, dtype=np.bool)
tt = np.triu_indices_from(mask)

# We create a tuple with triu_indices_from() by passing the “mask” array
# k is used to offset diagonal
# with k=0, we offset all diagnoals
# If we put k=13, means we offset 14-13=1 diagonal

# triu_indices_from() Return the indices for the upper-triangle of arr.
mask[np.triu_indices_from(mask, k=0)] = True

# First 2 param - anchor hues for negative and positive extents of the map.
# 3rd param - Anchor saturation for both extents of the map
# If true, return a matplotlib colormap object rather than a list of colors.

cmap = sns.diverging_palette(10, 129, s=50, as_cmap=True)

# Adjust size of the legend bar with cbar_kws={“shrink”: 0.5}
# cmap=“YlGnBu” gives the color from Yellow-Green-Blue palette

sns.heatmap(pearson, mask=mask, cmap="YlGnBu", vmax=.3, center=0,
           square=True, linewidths=.1, cbar_kws={"shrink": 0.5})

以下截图是相关图。请注意,我们已使用np.zeros_like()np.triu_indices_from()函数移除了热力图的上方三角形:

让我们通过可视化其他变量来探索我们的数据。

  1. 我们可以使用以下方式使用带有核密度估计器的直方图查看我们的目标变量SalePrice的分布:
# Setting the plot size
plt.figure(figsize=(8, 8))

sns.distplot(df_housingdata['SalePrice'], bins=50, kde=True)

以下截图展示了SalePrice变量的分布图:

在统计学中,核密度估计KDE)是一种非参数方法,用于估计随机变量的概率密度函数。核密度估计是一个基本的数据平滑问题,其中关于总体的推断基于有限的数据样本。KDE 是一种技术,它提供了一组数据的一个平滑曲线。如果你想要可视化某些数据,将其作为直方图中离散值的一种连续替代,这可能会很有用。

  1. 我们还可以使用 seaborn 包中的 JointGrid() 来绘制组合图表:
from scipy import stats
g = sns.JointGrid(df_housingdata['YearBuilt'], df_housingdata['SalePrice'])
g = g.plot(sns.regplot, sns.distplot)
g = g.annotate(stats.pearsonr)

使用前面的代码,我们能够绘制出 GarageArea 和 SalePrice 的散点图,同时在每个轴上绘制这些变量的直方图:

  1. 现在,我们将使用最小-最大归一化来缩放我们的数值变量。为此,我们首先需要从我们的数据集中选择仅包含数值变量:
# create a variable to hold the names of the data types viz int16, in32 and so on
num_cols = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']

# Filter out variables with numeric data types
df_numcols_only = df_housingdata.select_dtypes(include=num_cols)
  1. 我们现在将对我们的数值变量应用最小-最大缩放:
# Importing MinMaxScaler and initializing it
from sklearn.preprocessing import MinMaxScaler
min_max=MinMaxScaler()

# Scaling down the numeric variables
# We exclude SalePrice using iloc() on df_numcols_only DataFrame
df_housingdata_numcols=pd.DataFrame(min_max.fit_transform(df_numcols_only.iloc[:,0:36]), columns=df_numcols_only.iloc[:,0:36].columns.tolist())

在下面的表中,我们可以看到我们的数值变量已经被缩放:

  1. 现在,我们将对我们的分类变量执行独热编码:
# We exclude all numeric columns
df_housingdata_catcol = df_housingdata.select_dtypes(exclude=num_cols)

# Steps to one-hot encoding:
# We iterate through each categorical column name
# Create encoded variables for each categorical columns
# Concatenate the encoded variables to the DataFrame
# Remove the original categorical variable
for col in df_housingdata_catcol.columns.values:
   one_hot_encoded_variables = pd.get_dummies(df_housingdata_catcol[col],prefix=col)
   df_housingdata_catcol = pd.concat([df_housingdata_catcol,one_hot_encoded_variables],axis=1)
   df_housingdata_catcol.drop([col],axis=1, inplace=True)
  1. 现在我们创建了一个只包含缩放后的数值变量的 DataFrame。我们还创建了一个只包含编码后的分类变量的 DataFrame。让我们将这两个 DataFrame 合并成一个 DataFrame:
df_housedata = pd.concat([df_housingdata_numcols, df_housingdata_catcol], axis=1)
  1. 然后,我们可以将 SalePrice 变量连接到我们的 df_housedata DataFrame:
# Concatenate SalePrice to the final DataFrame
df_housedata_final = pd.concat([df_housedata, df_numcols_only.iloc[:,36]], axis=1)
  1. 我们可以使用 sklearn.model_selection 中的 train_test_split 类来创建我们的训练和测试数据集:
# Create feature and response variable set
# We create train & test sample from our dataset
from sklearn.model_selection import train_test_split

# create feature & response variables
X = df_housedata_final.iloc[:,0:302]
Y = df_housedata_final['SalePrice']

# Create train & test sets
X_train, X_test, Y_train, Y_test = \
train_test_split(X, Y, test_size=0.30, random_state=1)
  1. 现在,我们可以使用 SGDRegressor() 来构建一个线性模型。我们通过最小化 SGD 的正则化经验损失来拟合这个线性模型:
import numpy as np
from sklearn.linear_model import SGDRegressor

lin_model = SGDRegressor()

# We fit our model with train data
lin_model.fit(X_train, Y_train)

# We use predict() to predict our values
lin_model_predictions = lin_model.predict(X_test)

# We check the coefficient of determination with score()
print(lin_model.score(X_test, Y_test))

# We can also check the coefficient of determination with r2_score() from sklearn.metrics
from sklearn.metrics import r2_score
print(r2_score(Y_test, lin_model_predictions))

通过运行前面的代码,我们发现确定系数大约为 0.81。

注意,r2_score() 函数接受两个参数。第一个参数应该是真实值,而不是预测值,否则它将返回一个错误的结果。

  1. 我们检查测试数据上的 均方根误差RMSE):
from sklearn.metrics import mean_squared_error
mse = mean_squared_error(Y_test, lin_model_predictions)
rmse = np.sqrt(mse)
print(rmse)

运行前面的代码提供了 RMSE 等于 36459.44 的输出。

  1. 我们现在使用 matplotlib.pyplot 来绘制实际值和预测值:
plt.figure(figsize=(8, 8))
plt.scatter(Y_test, lin_model_predictions)
plt.xlabel('Actual Median value of house prices ($1000s)')
plt.ylabel('Predicted Median value of house prices ($1000s)')
plt.tight_layout()

实际值和预测值的结果图将如下所示:

因为图表显示大多数值大约在 45 度的对角线上,所以我们的预测值与实际值非常接近,除了少数几个。

它是如何工作的...

步骤 1中,我们查看变量类型。我们注意到数据集既有数值变量也有非数值变量。在步骤 2中,我们使用Pearson方法计算所有数值变量之间的成对相关性。之后,在步骤 3中,我们看到了所有预测变量与目标变量之间的关系。我们还探讨了如何按绝对值对相关系数进行排序。

步骤 4中,我们绘制了一个热图来可视化变量之间的相关性。然后,我们介绍了来自 NumPy 库的两个函数:zeros_like()triu_indices_from()zeros_like()函数接受相关性矩阵作为输入,并返回一个与给定数组具有相同形状和类型的零数组。triu_indices_from()返回数组上三角的索引。我们使用这两个函数来屏蔽相关性图的上三角部分。我们调用seaborn库中的heatmap()函数来绘制相关性热图,并将我们的相关性矩阵传递给它。我们还使用cmap="YlGnBu"设置矩阵的颜色,并使用cbar_kws={"shrink": 0.5}设置图例条的尺寸。

numpy.tril_indices_from()返回数组下三角的索引。

步骤 5中,我们查看目标变量SalePrice的分布。在步骤 6中,我们使用seaborn库的JointGrid()来展示如何为两个数值变量绘制带有回归线的散点图,同时在同一图表中绘制两个变量的分布。在步骤 7步骤 8中,我们只选择数值变量,并使用最小-最大归一化对变量进行缩放。这会将值缩放到 0 到 1 之间的数值范围。这也被称为特征缩放,并使用以下公式执行:

步骤 9步骤 10步骤 11中,我们对分类变量进行了独热编码,并将编码后的变量添加到 DataFrame 中。我们还删除了原始的分类变量。在步骤 12中,我们将数据集分为训练集和测试集。在步骤 13中,我们使用SGDRegressor()构建了线性回归模型,并打印了确定系数。最后,在步骤 14中,我们绘制了预测值和实际值,以查看模型的性能如何。

还有更多...

考虑一个线性回归模型,假设以下假设函数:

在这种情况下,对于,成本函数是均方误差MSE)。

公式如下:

在这个公式中,图片代表训练实例的数量。图片图片分别是第 i 个训练实例的输入向量和目标向量,而图片代表每个输入变量的参数或系数。图片是使用图片参数预测的第 i 个训练实例的预测值。均方误差(MSE)总是非负的,且越接近零,表示效果越好。

当模型在训练数据上表现不佳时,均方误差(MSE)会更高。因此,学习算法的目的是找到图片的值,以使 MSE 最小化。这可以表示如下:

图片

随机梯度下降法找到图片的值,以最小化成本函数。为了最小化成本函数,它通过计算成本函数导数的斜率来不断改变图片参数。它首先将图片参数初始化为零。图片参数在梯度下降的每一步都会更新:

图片

算法收敛所需的更新次数会随着训练数据的增加而增加。然而,随着训练数据量的增大,算法可能在训练数据中的每个实例都学习之前就收敛了。换句话说,训练数据量的增加不一定需要增加训练最佳模型所需的时间,其中测试误差最小。

每个训练实例都会修改图片。算法将这些图片值平均,以计算最终的图片

图片是学习率,它告诉算法以多快的速度向最小值移动。一个大的图片可能会错过最小误差,而一个小的图片可能会使算法运行时间更长。

在前面的章节中,我们使用了SGDRegressor()函数,但选择了超参数的默认值。现在我们将图片改为 0.0000001,并将max_iter值改为 2000:

lin_model = SGDRegressor(alpha=0.0000001, max_iter=2000)

max_iter 是一个整数值,它告诉算法它可以在训练数据上进行的最大遍历次数。这也被称为 epoch 的数量。

在我们的情况下,前面的代码给出了 RMSE 从 36,459 下降到 31,222,决定系数从 0.81 提高到 0.86 的结果。这些结果会因每次迭代而异。

参见

逻辑回归

在上一节中,我们提到当目标变量是连续的时,线性回归是一个不错的选择。我们现在将转向查看二元逻辑回归模型,该模型可以根据一个或多个预测变量预测观察值落入二元目标变量的两个类别之一的概率。二元逻辑回归通常被称为逻辑回归。

逻辑回归类似于线性回归,不同之处在于因变量是在二元 scale 上测量的。逻辑回归使我们能够对多个预测变量和一个二元目标变量之间的关系进行建模。然而,与线性回归不同,在逻辑回归的情况下,线性函数被用作另一个函数的输入,例如 

在这里,  是 S 形或逻辑函数。S 形函数如下所示:

下面的 graph 表示一个 S 形曲线,其中 y 轴的值介于 0 和 1 之间。它在 0.5 处穿过轴:

输出值介于 0 和 1 之间,是正类的概率。如果我们假设函数返回的值是  0.5,我们可以将输出解释为正。否则,我们将其解释为负。

在逻辑回归的情况下,我们使用一个称为交叉熵的成本函数。对于二元分类,它具有以下形式:

对于 y=1y=0,我们得到以下结果:

当预测概率偏离实际标签时,交叉熵会增加。更大的偏差会导致更高的交叉熵值。在线性回归的情况下,我们看到了使用梯度下降最小化成本的方法。在逻辑回归的情况下,我们也可以使用梯度下降来更新系数并最小化成本函数。

在这个菜谱中,我们将使用 scikit-learn 的 SGDClassfier() 实现。SGDClassifier() 实现了正则化线性模型和随机梯度下降,对于大型数据集来说,它比梯度下降要快得多。这是因为梯度下降考虑了整个训练数据集,而随机梯度下降只考虑更新权重时的一个随机点。

默认情况下,SGDClassifier 可能不如逻辑回归表现好。它可能需要调整超参数。

准备工作

在本节中,我们将使用一个包含 2005 年 4 月至 2005 年 9 月台湾信用卡客户逾期支付、人口统计、信用数据、支付历史和账单信息的数据集。这个数据集来自 UCI ML 存储库,可在 GitHub 上找到:

我们将首先导入所需的库:

# import os for operating system dependent functionalities
import os

# import other required libraries
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.linear_model import SGDClassifier
from sklearn.metrics import roc_curve
from sklearn.metrics import auc
import matplotlib.pyplot as plt

我们使用 os.chdir() 命令设置我们的工作目录:

# Set your working directory according to your requirement
os.chdir(".../Chapter 4/Logistic Regression")
os.getcwd()

让我们读取我们的数据。我们将以 df_ 为前缀命名 DataFrame,使其更容易阅读:

df_creditdata = pd.read_csv("UCI_Credit_Card.csv")

我们现在将使用 SGDClassifier() 来构建我们的模型:

如何操作...

让我们从查看变量和数据类型开始:

  1. 首先,我们将使用 read_csv() 函数查看我们的数据集:
print(df_creditdata.shape)
print(df_creditdata.head())
  1. 我们将使用 dtypes 来查看数据类型:
df_creditdata.dtypes
  1. 我们将删除 ID 列,因为我们在这里不需要它:
df_creditdata.drop(["ID"],axis=1,inplace=True)
  1. 在上一节中,我们看到了如何探索变量之间的相关性。这里我们将跳过这一部分,但建议读者检查相关性,因为多重共线性可能会影响模型。

  2. 然而,我们将检查是否存在任何空值,如下所示:

df_creditdata.isnull().sum()
  1. 然后,我们将分离预测变量和响应变量。我们还将分割我们的训练数据和测试数据:
# split features & response variable
X = df_creditdata.iloc[:,0:23]
Y = df_creditdata['default.payment.next.month']

# Create train & test sets
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.30, random_state=1)
  1. 我们使用 StandardScaler() 标准化预测变量:
scaler = StandardScaler().fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)
  1. 我们接着使用 SGDClassifier() 移动我们的模型:
# We create an instance of SGDClassifier()
logistic_model = SGDClassifier(alpha=0.000001, loss=‘log’, max_iter=100000, penalty=‘l2’)

# We fit our model to the data
fitted_model = logistic_model.fit(X_train, Y_train)

# We use predict_proba() to predict the probabilities
predictedvalues = fitted_model.predict_proba(X_test)

# We print the probabilities to take a glance
predictedvalues
  1. 我们将分离出一个类别的概率。在这种情况下,我们将查看类别 1:
# We take the predicted values of class 1
Y_predicted = predictedvalues[:, 1]

# We check to see if the right values have been considered from the predicted values
print(Y_predicted)
  1. 我们检查模型在训练数据上的准确性:
# Check for accuracy
logistic_model.score(X_test,Y_test)
  1. 我们可以接着看到曲线下面积AUC)值和接收者操作特征ROC)曲线:
# We use roc_curve() to generate fpr & tpr values
fpr, tpr, thresholds = roc_curve(Y_test, Y_predicted)

# we pass the fpr & tpr values to auc() to calculate the area under curve
roc_auc = auc(fpr, tpr)
print(roc_auc)
  1. 我们如下绘制我们的 ROC 曲线:
plt.figure()
plt.plot(fpr,tpr, color=‘orange’, lw=2, label=‘ROC curve (area under curve = %0.2f)’ % roc_auc)
plt.plot([0, 1], [0, 1], color=‘darkgrey’, lw=2, linestyle=‘--’)
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.0])
plt.xlabel(‘False Positive Rate (1-Specificity)’)
plt.ylabel(‘True Positive Rate (Sensitivity)’)
plt.title(‘ROC Curve’)
plt.legend(loc=“upper left”)
plt.show()

下面的图表显示了带有 AUC 值的 ROC 曲线:

图片

通过调整超参数,可以提高模型。也可以通过特征选择来改进。

它是如何工作的...

步骤 1中,我们查看了我们数据集的维度。在步骤 2中,我们瞥了一眼变量的数据类型,并注意到所有变量都是数值型的。在步骤 3中,我们删除了 ID 列,因为它对我们的练习没有用。我们跳过了查看变量之间的相关性,但建议读者添加这一步,以便完全理解和分析数据。

步骤 4中,我们继续检查数据集中是否有缺失值。在这种情况下,我们注意到数据集中没有缺失值。在步骤 5中,我们将预测变量和响应变量分开,并将数据集分为训练数据集,占数据的 70%,以及测试数据集,占数据的 30%。在步骤 6中,我们使用来自 sklearn.metricsStandardScaler() 对训练和测试数据集中的预测变量进行标准化。

之后,在步骤 7中,我们使用来自 sklearn.linear_modelSGDClassifier() 通过随机梯度下降法构建我们的逻辑回归模型。我们设置了超参数,如 alpha、loss、max_iter 和 penalty。我们将 loss='log' 设置为使用 SGDClassifier 进行逻辑回归。我们使用 predict_proba() 来预测测试观察结果的概率,这为我们提供了所有测试观察结果两个类别的概率。

loss 设置为 hingeSGDClassifier() 提供了线性 SVM。(我们将在下一节中介绍 SVM)。损失可以设置为其他值,例如 squared_hinge,它与 hinge 相同,但进行了二次惩罚。

步骤 8步骤 9中,我们过滤掉了类别 1 的概率,并查看我们的模型得分。在步骤 10步骤 11中,我们查看 AUC 值并绘制了 ROC 曲线。我们将在接下来的章节中更深入地探讨每种技术的高斯参数调整。

参见

  • 你可能已经注意到,在步骤 7中,我们使用了 l2 的超参数惩罚。惩罚是正则化项,l2 是默认值。超参数惩罚也可以设置为 l1;然而,这可能会导致稀疏解,将大多数系数推向零。更多关于这个主题的信息可以在以下链接中找到:bit.ly/2RjbSwM

  • scikit-learn 分类度量指南:bit.ly/2NUJl2C

简单贝叶斯

朴素贝叶斯算法是一种概率学习方法。它被称为朴素,因为它假设这个单词中的所有事件都是独立的,这实际上是非常罕见的。然而,尽管有这个假设,朴素贝叶斯算法在时间上已经证明在预测准确性方面提供了很好的性能。

贝叶斯概率理论基于这样一个原则,即事件或潜在结果估计的似然性应该基于多个试验中手头的证据。贝叶斯定理提供了一种计算给定类别的概率的方法,前提是了解先前的观察结果。

这可以写成以下形式:

这个定理的不同元素可以解释如下:

  • p(class|observation):这是在给定观察结果的情况下类别存在的概率。

  • P(observation):这是训练数据被观察到的先验概率。

  • p(class): 这是类别的先验概率。

  • p(observation|class): 这是给定类别成立的条件下观察值的概率。

换句话说,如果 H 是可能假设的空间,那么最可能的假设,即类别!H,是最大化 p(class|observation) 的那个。

给定一个具有属性!的新观察值,贝叶斯算法将其分类为最可能的值:

基于条件独立性假设,我们有以下内容:

朴素贝叶斯分类器的预测如下:

准备工作

基于朴素贝叶斯的分类器是可以在文本分类问题中应用的最基本的算法之一。

在这个菜谱中,我们将使用可以从 GitHub 下载的 spam.csv 文件。

这个 spam.csv 数据集有两个列。一个列包含消息,另一个列包含消息类型,它表明这是一条垃圾邮件还是正常邮件。我们将应用朴素贝叶斯技术来预测一条消息是否可能是垃圾邮件或正常邮件。

我们将首先导入所需的库:

# import os for operating system dependent functionalities
import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB

我们使用 os.chdir() 命令设置工作目录:

os.chdir(".../Chapter 4/Naive Bayes")
os.getcwd()

让我们读取我们的数据。正如我们在前面的章节中所做的那样,我们将以 df_ 为前缀命名 DataFrame,这样我们就可以轻松地读取它:

df_messages = pd.read_csv('spam.csv', encoding='latin-1', \
                          sep=',', names=['labels','message'])

如何做到这一点...

现在我们来了解一下如何构建我们的模型。

  1. 读取数据后,我们使用 head() 函数查看它:
 df_messages.head(3)

在下面的屏幕截图中,我们可以看到有两个列:标签和消息。输出如下:

  1. 我们然后使用 describe() 函数查看每个列的一些指标:
df_messages.describe()

这给我们以下指标:

对于对象数据类型,describe() 函数的结果将提供 metricscountuniquetopfreqtop 指的是最常见的值,而 freq 是这个值的频率。

  1. 我们还可以查看按消息类型划分的指标,如下所示:
df_messages.groupby('labels').describe()

使用前面的命令,我们看到目标变量的每个类别的计数、唯一值的数量和频率:

  1. 为了进一步分析我们的数据集,让我们看看每个消息的单词数和字符数:
df_messages['word_count'] = df_messages['message'].apply(lambda x: len(str(x).split(" ")))
df_messages['character_count'] = df_messages['message'].str.len() 

df_messages[['message','word_count', 'character_count']].head()

lambda 函数用于在 Python 中创建小型、匿名函数。一个 lambda 函数可以接受任意数量的参数,但只能有一个表达式。这个函数作为参数传递给其他函数,如 mapapplyreducefilter

上述代码的输出将如下所示:

  1. 在这种情况下,labels是我们的目标变量。我们有两个类别:spamham。我们可以使用条形图查看 spam 和 ham 消息的分布:
labels_count = pd.DataFrame(df_messages.groupby('labels')['message'].count())
labels_count.reset_index(inplace = True)
plt.figure(figsize=(4,4))
sns.barplot(labels_count['labels'], labels_count['message'])
plt.ylabel('Frequency', fontsize=12)
plt.xlabel('Labels', fontsize=12)
plt.show()

下面的代码是前面代码的输出:

  1. 在下面的代码块中,我们将spam标记为1,将ham标记为0
# create a variable that holds a key-value pair for ham and spam
class_labels = {"ham":0,"spam":1}

# use the class_labels variable with map()
df_messages['labels']=df_messages['labels'].map(class_labels)
df_messages.head()

注意,在下面的屏幕截图下,在labels变量下,所有 ham 和 spam 消息现在分别标记为 0 和 1:

  1. 现在我们将数据分为训练样本和测试样本:
# Split your data into train & test set
X_train, X_test, Y_train, Y_test = train_test_split(df_messages[‘message’],\
                                 df_messages[‘labels’],test_s=0.2,random_state=1)
  1. 我们需要将消息集合转换为标记计数的矩阵。这可以通过使用CountVectorizer()来完成:
# Creating an instance of the CountVectorizer class
# If ‘english’, a built-in stop word list for English is used.
# There are known issues with ‘english’ and you should consider an alternative
vectorizer = CountVectorizer(lowercase=True, stop_words=‘english’, analyzer=‘word’)

# Learn a vocabulary from one or more message using the fit_transform() function
vect_train = vectorizer.fit_transform(X_train)
  1. 我们继续使用朴素贝叶斯算法构建我们的模型:
# Create an instance of MultinomialNB()
model_nb = MultinomialNB()

# Fit your data to the model
model_nb.fit(vect_train,Y_train)

# Use predict() to predict target class
predict_train = model_nb.predict(vect_train)
  1. 我们加载用于评估指标的所需库,如下所示:
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
  1. 我们现在通过使用训练数据评估模型来检查我们的准确性:
# Calculate Train Accuracy
print(‘Accuracy score: {}’.format(accuracy_score(Y_train, predict_train)))

# Calculate other metrics on your train results
print(‘Precision score: {}’.format(precision_score(Y_train, predict_train)))
print(‘Recall score: {}’.format(recall_score(Y_train, predict_train)))
print(‘F1 score: {}’.format(f1_score(Y_train, predict_train)))

这个输出的结果如下:

  1. 现在我们通过使用未见过的测试数据评估模型来检查我们的测试数据准确性:
# We apply the model into our test data
vect_test = vectorizer.transform(X_test)
prediction = model_nb.predict(vect_test)

# Calculate Test Accuracy
print(‘Accuracy score: {}’.format(accuracy_score(Y_test, prediction)))

# Calculate other metrics on your test data
print(‘Precision score: {}’.format(precision_score(Y_test, prediction)))
print(‘Recall score: {}’.format(recall_score(Y_test, prediction)))
print(‘F1 score: {}’.format(f1_score(Y_test, prediction)))

使用前面的代码块,我们按以下方式打印性能指标:

这些结果可能因不同的样本和超参数而有所不同。

它是如何工作的...

步骤 1中,我们查看我们的数据集。在步骤 2步骤 3中,我们查看ham和垃圾邮件类标签的统计信息。在步骤 4中,我们通过查看数据集中每条消息的单词计数和字符计数来扩展我们的分析。在步骤 5中,我们看到了目标变量(ham 和垃圾邮件)的分布,而在步骤 6中,我们使用数字10对目标变量的类标签进行编码。在步骤 7中,我们将数据集分为训练样本和测试样本。在步骤 8中,我们使用sklearn.feature_extraction.text中的CountVectorizer()将消息集合转换为标记计数的矩阵。

如果你事先没有提供字典,也没有使用进行某种特征选择的分析器,那么特征的数量将与通过分析数据找到的词汇表大小相等。有关更多信息,请参阅以下链接:bit.ly/1pBh3T1

步骤 9步骤 10中,我们构建了我们的模型,并从sklearn.metrics中导入所需的类来分别测量各种分数。在步骤 11步骤 12中,我们检查了训练和测试数据集的准确性。

还有更多...

朴素贝叶斯算法有多种变体。这包括多元伯努利朴素贝叶斯、多项式朴素贝叶斯和高斯多项式朴素贝叶斯算法。这些变体可以应用于解决不同的问题。

  • 多元伯努利朴素贝叶斯: 当特征向量提供文档中单词或特征是否出现时的二进制表示时,使用此算法。文档特征向量中的每个标记都与10值相关联。1表示单词出现的标记,而0表示单词不出现的标记。多元伯努利朴素贝叶斯算法可用于特定单词缺失很重要的情况,例如在垃圾邮件内容的检测中。

  • 多项式朴素贝叶斯: 当在分类问题中考虑单词的多次出现时使用。在这个变体中,文本文档由术语的频率来表征,而不是二进制值。频率是一个离散计数,指的是给定单词或标记在文档中出现的次数。多元多项式朴素贝叶斯算法可用于主题建模,这是一种在文档语料库中找到最能代表关键信息的一组单词的方法。

  • 高斯多项式朴素贝叶斯: 在我们拥有连续特征的情况下,处理朴素贝叶斯分类中的连续数据的一种方法是将特征离散化。或者,我们可以应用高斯多项式朴素贝叶斯算法。这假设特征遵循正态分布,并使用高斯核来计算类概率。

参见

  • 在 scikit-learn 中,CountVectorizer()计算单词在文档中出现的次数,并使用该值作为其权重。您还可以使用TfidfVectorizer(),其中分配给每个标记的权重取决于其在文档中的频率以及整个语料库中术语出现的频率。您可以在以下链接中找到有关TfidfVectorizer的更多信息:bit.ly/2sJCoVN

  • scikit-learn 关于多元伯努利朴素贝叶斯分类器的文档:bit.ly/2y3fASv

  • scikit-learn 关于多项式朴素贝叶斯分类器的文档:bit.ly/2P4Ohic

决策树

决策树,一种非参数监督学习方法,是用于预测建模的流行算法。最著名的决策树算法包括迭代二分器ID3)、C4.5、CART 和 C5.0。ID3 仅适用于分类特征。C4.5 是 ID3 的改进,具有处理缺失值和连续属性的能力。树生长过程涉及在每个节点使用信息增益找到最佳分割。然而,C4.5 算法通过在可以产生最大信息增益的合适阈值处分割,将连续属性转换为二分分类属性。

著名统计学家 Leo Breiman 提出了一种名为分类与回归树CART)的决策树算法。与 ID3 和 C4.5 不同,CART 可以生成既能用于分类又能用于回归问题的决策树。此算法也是重要随机森林算法的基础。

决策树是通过递归分割构建的,它根据几个二分独立属性将数据分割成子集。此递归过程可能会多次分割数据,直到达到特定的停止标准后分割过程终止。最佳的分割是最大化分割标准的分割。对于分类学习,用作分割标准的技巧是熵、信息增益、基尼指数和增益率。然而,对于回归任务,则使用标准差减少。

C4.5 和 C5.0 算法使用熵(也称为香农熵)和信息增益来识别最佳属性并决定分割标准。熵是衡量不确定性或随机性的概率度量。

从数学上讲,熵可以表示如下:

图片

在二分类属性的情况下,熵的范围从 0 到 1。对于 n 分类属性,熵可以取 0 到图片之间的值。对于只有一个类的同质变量,熵为零,因为该类为零的概率是 1,图片

要使用熵来识别分割的最佳属性,算法会计算在每个可能的属性上分割所导致的同质性变化。这种变化被称为信息增益。构建决策树就是寻找能够返回最高信息增益的属性。这种信息增益是基于在属性上分割数据集后熵的减少。

信息增益被计算为分割前后的熵之差:

图片

信息增益越高,特征越好。算法会对所有特征计算信息增益。算法选择信息增益最高的特征来创建根节点。在每个节点上计算信息增益以选择该节点的最佳特征。

信息增益也称为 Kullback-Leibler 散度。这衡量的是两个概率分布在同一变量上的差异。简单来说,如果你有两个概率分布,KL 散度衡量这两个分布的相似性。如果 KL 散度为 0,则两个分布相等。

Gini 指数是衡量不纯度程度的指标,也可以用来识别用于分割标准的最佳属性。其计算方法如下:

在前面的公式中,p是训练实例属于特定类的概率。关于 Gini 指数,不纯度越低,越好。

准备工作

要使用 q 决策树算法构建我们的模型,我们将使用backorders.csv文件,该文件可以从以下 GitHub 下载。

此数据集有 23 列。目标变量是went_on_backorder。它标识了一个产品是否已经进入补货。其他 22 个变量是预测变量。数据描述包含在此书的代码中:

我们将首先导入所需的库:

# import os for operating system dependent functionalities
import os

# import other required libraries
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix, roc_curve, auc
import itertools
from sklearn import tree

import seaborn as sns
import matplotlib.pyplot as plt

我们使用os.chdir()命令设置工作目录:

# Set your working directory according to your requirement
os.chdir(".../Chapter 4/Decision Tree")

# Check Working Directory 
os.getcwd()

让我们读取数据。和之前一样,我们将 DataFrame 的名称前缀设置为df_以使其更容易理解:

df_backorder = pd.read_csv("BackOrders.csv")

如何操作...

现在让我们继续构建我们的模型:

  1. 首先,我们想通过shapehead()函数查看数据集的维度和数据。我们还使用describe()查看数值变量的统计信息:
df_backorder.shape
df_backorder.head()
df_backorder.describe()

如果你的输出以科学记数法显示,你可以通过执行以下命令将其更改为标准形式:pd.options.display.float_format = ‘{:.2f}’.format

  1. 通过dtypes,我们可以看到每个变量的数据类型:
df_backorder.dtypes
  1. 我们可以看到sku是一个标识符,在我们的模型构建练习中不会对我们有任何帮助。因此,我们将sku从 DataFrame 中删除,如下所示:
df_backorder.drop('sku', axis=1, inplace=True)
  1. 我们可以使用isnull().sum()命令检查是否有任何缺失值:
df_backorder.isnull().sum()

我们看一下以下截图:

  1. 由于lead_time变量中缺失值的数量大约为 5%,我们将删除所有lead_time缺失的观测值以进行初步分析:
df_backorder = df_backorder.dropna(axis=0)
  1. 我们现在需要编码我们的分类变量。我们只选择分类变量,并调用pd.get_dummies()对非数值变量进行虚拟编码:
non_numeric_attributes = df_backorder.select_dtypes(include=['object']).columns
df_backorder = pd.get_dummies(columns=non_numeric_attributes, data=df_backorder, prefix=non_numeric_attributes, prefix_sep="_",drop_first=True)
df_backorder.dtypes

通过前面的代码,我们可以看到数据类型。我们注意到虚拟编码的变量都是无符号整数(uint8)类型:

  1. 我们将如下查看目标变量的分布:
# Target variable distribution
pd.value_counts(df_backorder['went_on_backorder_Yes'].values)

我们可以看到我们的数据分布相当平衡,大约 81%的观测值属于类别 0,19%属于类别 1:

  1. 现在我们将数据集分为训练集和测试集:
#Performing train test split on the data
X, Y = df_backorder.loc[:,df_backorder.columns!=‘went_on_backorder_Yes’].values, df_backorder.loc[:,‘went_on_backorder_Yes’].values

# Split our dataset into train & test set
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state=1)
  1. 我们将使用DecisionTreeClassifier()构建我们的第一个模型:
# Create an instance of DecisionTreeClassifier()
classifier = tree.DecisionTreeClassifier(random_state=1)

# Fit our model to the data
model_DT_Gini = classifier.fit(X_train, Y_train)
model_DT_Gini

通过model_DT_Gini,我们可以看到已使用的超参数的默认值:

  1. 我们可以使用模型来使用我们的训练集和测试集预测我们的类别标签:
# Predict with our test data
test_predictedvalues = model_DT_Gini.predict(X_test)

# Check accuracy
acc = accuracy_score(Y_test, test_predictedvalues)
print("Accuracy is", acc)

# Check TP, TN, FP, FN
tn, fp, fn, tp = confusion_matrix(Y_test, test_predictedvalues).ravel()
print("TN:",tn, " FP:",fp, " FN:",fn, " TP:",tp)

这给出了准确率以及真阴性TN)、假阳性FP)、假阴性FN)和真阳性TP)值的计数:

  1. 我们现在将使用plot_confusion_matrix函数来绘制我们的混淆矩阵。此函数来自scikit-learn.org,在那里它很容易获得,所以我们不会在这里展示此函数。然而,它已包含在书中的代码中供您参考:
target_names = [ ‘No’, ‘Yes’]

#Pass Actual & Predicted values to confusion_matrix()
cm = confusion_matrix(Y_test, test_predictedvalues)

plt.figure()
plot_confusion_matrix(cm, classes=target_names, normalize=False)
plt.show()

然后,我们可以在混淆矩阵图中看到 TNs、FPs、FNs 和 TPs 的数量:

  1. 我们可以更改超参数来调整我们的模型。我们也可以执行网格搜索以找到提供最佳结果的超参数值。我们可以使用以下代码来设置超参数值:
# set the parameters for grid search
grid_search_parameters = {“criterion”: [“gini”, “entropy”],
             “min_samples_split”: [2],
             “max_depth”: [None, 2, 3],
             “min_samples_leaf”: [1, 5],
             “max_leaf_nodes”: [None],
             }
  1. 我们将使用GridSearchCV()来进行参数网格搜索:
from sklearn.model_selection import GridSearchCV

# Create an instance of DecisionTreeClassifier()
classifier = tree.DecisionTreeClassifier()

# Use GridSearchCV(), pass the values you have set for grid search
model_DT_Grid = GridSearchCV(classifier, grid_search_parameters, cv=10)
model_DT_Grid.fit(X_train, Y_train)
  1. 执行上述命令后,我们可以看到使用best_params_提供的最佳参数值:
model_DT_Grid.best_params_ 
  1. 您可以使用使用GridSearchCV()函数选择的模型:
test_predictedvalues = model_DT_Grid.predict(X_test)

cc = accuracy_score(Y_test, test_predictedvalues)
print("Accuracy is", acc)

tn, fp, fn, tp = confusion_matrix(Y_test, test_predictedvalues).ravel()
print("TN:",tn, " FP:",fp, " FN:",fn, " TP:",tp)

cm = confusion_matrix(Y_test, test_predictedvalues)

plt.figure()
plot_confusion_matrix(cm, classes=target_names, normalize=False)
plt.show()
  1. 为了查看每个标签的指标,我们还可以使用classification_report,如下所示:
from sklearn.metrics import classification_report

target_names = [ 'No', 'Yes']
print(classification_report(Y_test, test_predictedvalues, target_names=target_names))

此步骤给出了以下输出:

这些结果将根据所使用的样本和超参数调整而变化。

它是如何工作的...

步骤 1中,我们检查了数据集的维度。我们还看到了数值变量的统计信息。在步骤 2中,我们检查了每个变量的数据类型。在步骤 3中,我们删除了sku属性,因为它是一个标识符,对我们构建模型将没有帮助。在步骤 4中,我们检查了缺失值,并注意到lead_time属性有 3,403 个缺失值,这大约是总观察值的 5%。在步骤 5中,我们删除了lead_time有缺失值的观测值。请注意,有各种策略可以用来插补缺失值,但在这个练习中我们没有考虑这些策略。

步骤 6中,我们使用 pandas 库中的get_dummies()函数,并将drop_first=True作为参数之一,对分类变量执行 k-1 虚拟编码。在步骤 7中,我们检查了目标变量的分布。我们看到类别标签 0 和 1 的大致比例为 19%-81%,这并不非常平衡。然而,我们有两个类别的足够观察结果,可以继续下一步。在步骤 8中,我们分离了预测变量和响应变量。我们还分割了数据集以创建训练集和测试集。在步骤 9中,我们使用DecisionTreeClassifier()构建我们的模型。我们注意到了默认的超参数值,并注意到默认情况下,DecisionTreeClassifier()使用 Gini 不纯度作为分割标准。

步骤 10中,我们使用模型来预测我们的测试样本。我们记录了整体准确率和 TP、TN、FP 和 FN 值的数量。在步骤 11中,我们使用plot_confusion_matrix()将这些值以混淆矩阵的形式绘制出来。请注意,plot_confusion_matrix()bit.ly/2MdyDU9上即可获得,并且也包含在本章代码文件夹中。

我们随后查看改变超参数值以微调我们的模型。我们执行网格搜索以找到最佳的超参数值。在步骤 12中,我们定义了要应用于网格搜索算法的超参数值的组合。在步骤 13步骤 14中,我们使用GridSearchCV()来寻找最佳的超参数。在步骤 15中,我们使用网格搜索返回的模型来预测我们的测试观测值。最后,在步骤 16中,我们使用sklearn.metrics中的classification_report()生成包括precisionrecallf1-scoresupport在内的各种分数。

更多内容...

有时,一个模型可以完美地分类训练数据,但在处理新数据时却面临困难。这个问题被称为过拟合。模型无法推广到新的测试数据。

我们允许递归分割过程重复,直到我们终止叶节点,因为我们无法进一步分割数据。这个模型会完美地拟合训练数据,但会导致性能不佳。因此,基于树的模型容易过拟合。为了克服这一点,我们需要控制决策树的深度。

有多种方法可以避免过拟合。一种方法是在训练数据被完美分类之前停止增长。以下方法可以采用来实现这种停止方法:

  • 当树达到最大层数时停止

  • 当子集包含少于定义的培训实例数时停止

  • 当达到最小信息增益时停止

另一种方法是允许数据过拟合,然后在树构建后剪枝。这涉及到消除那些明显不相关的节点,同时也最小化了决策树的大小。

参见

支持向量机

支持向量机(SVM)是一种流行的监督学习机器学习算法。它可以用于分类和回归问题。在分类学习中,SVM 通过找到一个最优的分离超平面来区分两个观察类。如果数据是线性可分的且是一维的,我们可能有一个点可以分离数据。在二维空间中,数据可以通过一条直线分离,而在三维空间中,数据可以通过一个平面分离。当我们有超过三个维度时,这被称为超平面。

对于线性 SVM,一个具有 n 个特征向量的数据集 X 表示如下:

图片

双极目标变量 Y 的表示如下:

图片

超平面由以下公式给出:

图片

对于 SVM,两个类别用-1 和+1 表示,而不是 1 和 0。因此,超平面可以写成以下形式:

图片

为了分类数据,我们有以下两个规则:

图片

然而,很可能存在许多可以正确分类训练数据的超平面。对于前面的规则,可能存在无限多个 wb 的解。例如,感知器学习算法将只找到任何线性分类器。然而,SVM 找到的是最优超平面,它距离任何数据点最远。数据点离超平面越远,我们对其正确分类的信心就越大。因此,我们希望数据点尽可能远离超平面,同时仍然能够正确分类它们。最佳超平面是两个类别之间具有最大边缘的超平面。这被称为最大边缘超平面。

支持向量机(SVM)可以从训练数据中选择定义分离超平面的最重要的向量。这些是距离超平面最近的点,被称为支持向量。支持向量是分类最困难的点。同时,这些也是高质量的数据。如果你移除所有其他数据点,只使用支持向量,你可以使用相同的 SVM 模型恢复出精确的决策超平面和边缘。数据点的数量并不重要,重要的是支持向量。

我们将权重 wb 归一化,使得支持向量满足以下条件:

图片

因此,分类规则变为以下:

图片

前面的方程可以组合并表示如下:

图片

初始 SVM 算法只能在数据线性可分的情况下使用。这些被称为硬间隔 SVMs。然而,硬间隔 SVMs 只能在数据完全线性可分且没有噪声的情况下工作。在噪声或异常值的情况下,硬间隔 SVM 可能会失败。

Vladimir Vapnik 提出了软间隔 SVMs 来处理通过松弛变量非线性可分的数据。松弛变量允许在拟合训练数据集时犯错误。在硬间隔分类中,我们将得到一个小的边缘决策边界。在软间隔分类中,我们将得到一个较大的边缘决策边界:

图片

SVMs 可以使用所谓的核技巧进行非常出色的非线性分类。这指的是将预测变量隐式映射到更高维特征空间的变换。流行的核类型包括以下几种:

  • 线性核

  • 多项式核

  • 径向基函数(RBF)核

  • Sigmoid 核

不同的核函数适用于不同的决策函数。我们可以将核函数相加,以实现更复杂的平面。

准备工作

在本章中,我们将使用bank.csv文件,该文件基于银行营销数据,您可以从 GitHub 下载。这些数据与葡萄牙银行在电话中进行直接营销活动相关。目标是预测客户是否会订阅定期存款:

我们将首先导入所需的库:

# import os for operating system dependent functionalities
import os

# import other required libraries
import pandas as pd
import numpy as np
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, confusion_matrix, roc_curve, auc
from sklearn.model_selection import train_test_split

我们使用os.chdir()命令设置工作目录:

# Set your working directory according to your requirement
os.chdir(".../Chapter 4/Support Vector Machine")
os.getcwd()

让我们读取我们的数据。我们将再次使用前缀df_来命名 DataFrame,以便更容易理解:

df_bankdata = pd.read_csv("bank.csv")

如何做到这一点...

在本节中,我们将查看检查空值、标准化数值和独热编码分类变量:

  1. 使用以下命令,我们可以看到在数据集中有十个分类变量和七个数值变量:
df_bankdata.dtypes
  1. 使用以下命令,我们注意到没有缺失值,因此我们可以继续下一步:
df_bankdata.isnull().sum()
  1. 我们可以如下检查目标变量的类别平衡:
print("Total number of class labels: {}".format(df_bankdata.shape[0]))
print("Number of people opted for Term Deposit: {}".format(df_bankdata[df_bankdata.y == 'yes'].shape[0]))
print("Number of people not opted for Term Deposit: {}".format(df_bankdata[df_bankdata.y == 'no'].shape[0]))
  1. 我们可以使用以下命令将目标类别转换为二进制值 1 和 0:
df_bankdata['y'] = (df_bankdata['y']=='yes').astype(int)
  1. 我们现在可以对分类变量进行独热编码。我们只选择本质上是分类的变量。在下面的代码中,我们使用category_column_names来提供非数值变量的名称:
# Using select_dtypes() to select only the non-numerical type variable
column_type = ['object']
df_bank_data_category_cols = df_bankdata.select_dtypes(column_type)

# This will give you the names of the non-numerical variables
category_column_names = df_bank_data_category_cols.columns.values.tolist()
category_column_names
  1. 我们对每个非数值变量运行循环,对它们进行独热编码,并将它们添加回 DataFrame。我们还将删除独热编码后的原始非数值变量:
for each_col in category_column_names:
   dummy_var = pd.get_dummies(df_bank_data_category_cols[each_col], prefix=each_col)
   df_joindata = df_bankdata.join(dummy_var)
   df_joindata.drop([each_col], axis=1, inplace=True)
   df_bankdata = df_joindata
  1. 我们如下分离预测变量和响应变量:
# Separate features & response variable
X=df_bankdata.iloc[:, :-1]
Y=df_bankdata['y']
  1. 我们还将数据分为训练集和测试集:
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state=1)
  1. 我们然后使用默认核的 SVC 构建我们的第一个模型,径向基函数RBF):
# Note, you need not pass kernel='rbf' to the SVC() because its the default
svc_model = SVC(kernel='rbf') 
svc_model.fit(X_train, Y_train)
  1. 我们通过使用 RBF 核构建的 SVC 模型来检查我们的训练和测试准确率:
train_predictedvalues=svc_model.predict(X_train)
test_predictedvalues=svc_model.predict(X_test)

print('Train Accuracy Score:')
print(metrics.accuracy_score(Y_train,train_predictedvalues))

print('Test Accuracy Score:')
print(metrics.accuracy_score(Y_test,test_predictedvalues))

我们得到以下输出:

  1. 我们可以使用多项式核重建我们的 SVC 模型,如下所示:
svc_model =SVC(kernel='poly') 
svc_model.fit(X_train, Y_train)

train_predictedvalues=svc_model.predict(X_train)
test_predictedvalues=svc_model.predict(X_test)

print('Train Accuracy Score:')
print(metrics.accuracy_score(Y_train,train_predictedvalues))

print('Test Accuracy Score:')
print(metrics.accuracy_score(Y_test,test_predictedvalues))

我们使用多项式核得到以下输出:

  1. 我们也可以使用线性核构建 SVC 模型。在先前的代码中,我们可以将kernel='ploy'替换为kernel='linear'
svc_model =SVC(kernel='linear') 

使用线性核,我们得到以下准确率:

我们的结果将取决于所使用的不同类型的核和其他超参数值。

它是如何工作的...

步骤 1中,我们检查了变量的数据类型。我们注意到我们有十个类别和七个数值变量。在步骤 2中,我们检查了缺失值,并看到我们的数据集中没有缺失值。在步骤 3中,我们检查了目标变量的类别平衡,并发现它有yesno的值。在步骤 4中,我们将目标变量转换为 1 和 0,分别代表yesno。在步骤 5步骤 6中,我们对非数值变量进行了独热编码。

步骤 7中,我们将预测变量和响应变量分开,在步骤 8中,我们将数据集分为训练集和测试集。之后,在步骤 9中,我们使用sklearn.svm中的SVC()函数和默认的 RBF 核来构建我们的模型。我们将它应用于我们的训练和测试数据以预测类别。在步骤 10中,我们检查了训练和测试数据的准确率。在步骤 11中,我们更改了超参数以将核设置为多项式。我们注意到训练准确率基本保持不变,但测试准确率有所提高。

使用多项式核,默认度数为 3。你可以将多项式度数改为更高的度数,并注意模型性能的变化。

步骤 12中,我们将核函数改为线性,以查看结果是否比多项式核有所改善。然而,我们没有看到任何显著的改进。

还有更多...

在这个练习中,我们看到了如何在代码中使用各种核函数。核函数必须是对称的。最好它们应该有一个正定(半正定)的 Gram 矩阵。Gram 矩阵是 V 的所有可能内积的矩阵,其中 V 是 m 个向量的集合。为了方便,我们不妨将正定和正半定函数同等对待。在实践中,核矩阵的正定性确保核算法收敛到唯一解。

线性核是所有可用核中最简单的。它与文本分类问题配合得很好。

线性核表示如下:

这里,c是常数项。

多项式核有两个参数:一个常数和次数。没有常数且次数为 1 的多项式核实际上就是一个线性核。随着多项式核次数的增加,决策函数变得更加复杂。较高的次数可以获得良好的训练精度,但模型可能无法泛化到未见过的数据,导致过拟合。多项式核表示如下:

在这里,是斜率,d 是核的次数,c 是常数项。

径向基函数核(RBF),也称为高斯核,是一个更复杂的核,可以超越多项式核。RBF 核如下所示:

参数可以被调整以提高核的性能。这一点很重要:如果被高估,核可能会失去其非线性能力,表现得更加线性。另一方面,如果被低估,决策函数可能会对训练数据中的噪声非常敏感。

并非所有核函数都是严格正定的。虽然 sigmoid 核函数相当广泛地被使用,但它不是正定的。sigmoid 函数如下所示:

在这里,是斜率,c是常数项。请注意,具有 sigmoid 核的支持向量机与双层感知器神经网络相同。

将核技巧添加到 SVM 模型中可以给我们带来新的模型。我们如何选择使用哪种核?第一种方法是尝试 RBF 核,因为它大多数时候都表现得相当好。然而,使用其他核并验证你的结果是个好主意。使用正确的核和正确的数据集可以帮助你构建最好的 SVM 模型。

参见

  • 更多关于正定矩阵的信息可以在这里找到:bit.ly/2NnGeLK

  • 正定核是正定矩阵的推广。你可以在这里了解更多信息:bit.ly/2NlsIs1

  • scikit-learn 关于支持向量回归的文档:bit.ly/2OFZ8ix

第五章:使用 Bagging 对模型进行打包

在本章中,我们讨论以下内容:

  • Bootstrap aggregation

  • 集成元估计器

  • Bagging 回归器

简介

分类器的组合可以帮助显著减少误分类错误。许多研究表明,这种集成方法可以显著减少预测模型的方差。已经提出了几种技术来实现方差减少。例如,在许多情况下,bootstrap aggregating (bagging) 分类树已被证明比单个分类树具有更高的准确性。Bagging 可以应用于基于树的算法以增强预测的准确性,尽管它也可以与基于树的算法以外的其他方法一起使用。

Bootstrap aggregation

Bootstrap aggregation,也称为Bagging,是一种由 Leo Breiman 于 1994 年提出的强大集成方法,用于防止过拟合。Bagging 背后的概念是将多个基础学习者的预测组合起来以创建更准确的输出。

Breiman 表明,Bagging 可以在不稳定的学习算法中成功实现预期结果,在这些算法中,训练数据的小幅变化可能导致预测结果的大幅变化。Breiman 证明了神经网络和决策树等算法是不稳定学习算法的例子。Bootstrap aggregation 在小型数据集上非常有效。

Bagging 的一般过程有助于降低具有高方差的算法的方差。Bagging 还支持分类和回归问题。以下图表显示了 Bootstrap aggregation 流程的工作方式:

图片

使用训练数据集X,我们生成 N 个 bootstrap 样本X1X2,……,XN

对于每个 bootstrap 样本,我们训练一个分类器,图片。组合分类器将平均所有这些单个分类器的输出,如下所示:

图片

在前面的公式中,N 代表样本数量。

在 Bagging 分类器中,使用投票来做出最终预测。Breiman 提出的 Bagging 分类器的伪代码如下:

图片

在 Bagging 回归器的情况下,最终预测是构建在每个 bootstrap 样本上的模型预测的平均值。以下伪代码描述了 Bagging 回归器:

图片

准备工作

我们首先导入所需的库并读取我们的文件。我们使用warnings.filterwarnings()函数从warnings库抑制任何警告:

import warnings
warnings.filterwarnings('ignore')

import os
import pandas as pd
import numpy as np

from sklearn.model_selection import train_test_split
from sklearn.linear_model import SGDRegressor
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.utils import resample

import matplotlib.pyplot as plt

我们现在已设置好工作文件夹。从 GitHub 下载autompg.csv文件,并将其复制到工作文件夹中,如下所示:

os.chdir('.../.../Chapter 5')
os.getcwd()

我们使用read_csv()读取我们的数据,并在数据帧名称前加上df_前缀,以便更容易理解:

df_autodata = pd.read_csv("autompg.csv")

我们检查数据集是否有任何缺失值,如下所示:

# The below syntax returns the column names which has any missing value
columns_with_missing_values=df_autodata.columns[df_autodata.isnull().any()]

# We pass the column names with missing values to the dataframe to count the number
# of missing values
df_autodata[columns_with_missing_values].isnull().sum()

我们注意到horsepower变量有六个缺失值。我们可以使用horsepower变量现有值的均值来填充这些缺失值,以下代码如下:

df_autodata['horsepower'].fillna(df_autodata['horsepower'].median(), inplace=True)

我们注意到carname变量是一个标识符,在我们的模型构建练习中并不有用,因此我们可以将其删除,如下所示:

df_autodata.drop(['carname'], axis=1, inplace=True)

我们可以使用dataframe.head()命令查看数据:

df_autodata.head()

如何做到这一点...

在本节中,我们将了解如何使用 bootstrap 样本构建模型:

  1. 我们首先创建 bootstrap 样本。在第三章“重采样方法”中,我们编写了一个自定义函数create_bootstrap_oob(),用于创建 bootstrap 和袋外OOB)样本。

在以下代码块中,我们看到如何创建 bootstrap 和 OOB 样本:

def create_bootstrap_oob(df):
    global df_OOB
    global df_bootstrap_sample 
    # creating the bootstrap sample
    df_bootstrap_sample = resample(df, replace=True, n_samples=100)

    # creating the OOB sample 
    bootstrap_sample_index = tuple(df_bootstrap_sample.index)
    bootstrap_df = df.index.isin(bootstrap_sample_index)

    df_OOB = df[~bootstrap_df]
  1. 我们使用 bootstrap 样本构建模型,并对所有模型的成本函数进行平均。我们在每个 bootstrap 样本上使用SGDRegressor()。在以下代码块中,我们重用之前编写的自定义函数create_bootstrap_oob()来创建 bootstrap 和 OOB 误差样本:
iteration=50
mse_each_iterations = list()
lm=SGDRegressor()
total_mse=0
average_mse= list()

for i in range(iteration):
    create_bootstrap_oob(df_autodata)

    # Bootstrap sample features set
    X_BS = df_bootstrap_sample.iloc[:,1:8] 

    # bootstrap sample response variable
    Y_BS = df_bootstrap_sample.iloc[:,0] 

    X_OOB = df_OOB.iloc[:,1:8] #OOB sample features
    Y_OOB = df_OOB.iloc[:,0] #OOB sample response variable 

    # fit your model with bootstrap sample
    lm=SGDRegressor()
    lm.fit(X_BS, Y_BS)

    # test your model on out-of-bag sample 
    predictedvalues = lm.predict(X_OOB)

    # capture MSE for the predicted values against OOB actuals
    mse = mean_squared_error(Y_OOB, predictedvalues)

    # create a list of mse values
    mse_each_iterations.append(mse) 
  1. 现在我们将绘制每个构建的模型的均方误差(MSE):
import matplotlib.pyplot as plt
f, ax= plt.subplots(figsize=(8,6))

plt.plot(mse_each_iterations, 'c--', label='MSE by Iteration')

plt.xlabel('Iterations')
plt.ylabel('Mean Squared Error')
plt.legend(loc=1)
plt.show()

图表将如下所示:

图片

它是如何工作的...

步骤 1中,我们执行了自定义函数代码以创建create_bootstrap_oob()函数,该函数为我们创建 bootstrap 和 OOB 样本。在步骤 2中,我们执行以下步骤:

  1. 我们决定进行 50 次迭代,因此我们将iteration变量设置为50

  2. 在每次迭代中,create_bootstrap_oob()函数返回两个 DataFrame 对象,df_bootstrap_sampledf_OOB

  3. 我们分别使用df_bootstrap_sampledf_OOB作为我们的 bootstrap 和 OOB 样本。

  4. 我们将df_bootstrap_sampledf_OOB样本分别拆分为特征集和响应变量。

  5. 我们将SGDRegressor()拟合到我们的 bootstrap 样本以构建我们的模型。

  6. 我们将 OOB 样本传递给模型以预测我们的值。

  7. 我们将预测值与 OOB 样本中的响应变量进行比较。

  8. 我们计算了每次迭代的 MSE。

步骤 3中,我们创建了一个图表来显示直到第五十次迭代的 MSE。这个结果可能会因为随机性而有所不同。

参见

  • 《Leo Breiman 的《Bagging Predictors》》,1994 年 9 月

集成元估计器

Bagging 分类器和 Bagging 回归器是集成元估计器,分别将基础分类器和回归器模型拟合到原始数据集的随机子集。每个模型的预测被组合起来以创建最终预测。这类元估计器将随机化引入模型构建过程并汇总结果。对于数值目标变量,聚合平均迭代,以实现分类结果进行多数投票。

Bagging 分类器

Bagging 分类器在每个分类器模型上训练原始训练集的随机子集,然后汇总预测,并对分类结果进行多数投票。在下面的配方中,我们将查看一个带有自助样本的 Bagging 分类器的实现。

如何做...

  1. 我们从scikit-learn库中导入BaggingClassifierDecisionTreeClassifier。我们还按如下方式导入其他所需的库:
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
  1. 接下来,我们读取数据并查看其维度:
df_winedata = pd.read_csv('winedata.csv')
df_winedata.shape
  1. 我们将特征和响应集分开。我们还把数据分成训练集和测试集。
X = df_winedata.iloc[:,1:14]
Y = df_winedata.iloc[:,0]

X_train, X_test, Y_train, Y_test = train_test_split(X, Y, random_state=1)
  1. 我们创建一个DecisionTreeClassifier类的实例,并将其传递给BaggingClassifier()
dt_model = DecisionTreeClassifier(criterion='entropy')
bag_dt_model = BaggingClassifier(dt_model, max_features=1.0, n_estimators=5, \
                                 random_state=1, bootstrap=True)

注意,在先前的代码块中,我们已声明bootstrap=True。这是默认值,表示以替换方式抽取样本。

  1. 我们将模型拟合到训练数据如下:
bag_dt_model.fit(X_train, Y_train)
  1. 我们可以看到将测试数据传递给模型后的得分:
bag_dt_model.score(X_test, Y_test)
  1. 我们使用predict函数如下预测响应变量:
predictedvalues = bag_dt_model.predict(X_test)
  1. 我们现在将使用代码来绘制混淆矩阵。请注意,此代码已从scikit-learn.org获取。我们执行以下代码以创建plot_confusion_matrix()函数:
# code from 
# http://scikit-learn.org/stable/auto_examples/model_selection/plot_confusion_matrix.html
def plot_confusion_matrix(cm, classes,
                          normalize=False,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):
    """
    This function prints and plots the confusion matrix.
    """
    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=45)
    plt.yticks(tick_marks, classes)

    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, cm[i, j],
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.tight_layout()
    plt.ylabel('Actuals')
    plt.xlabel('Predicted')
  1. 我们使用前面的plot_confusion_matrix()函数来绘制我们的混淆矩阵:
# This variable holds the class labels of our target variable
target_names = [ '1', '2', '3']

import itertools
from sklearn.metrics import confusion_matrix

# Constructing the Confusion Matrix
cm = confusion_matrix(Y_test, predictedvalues)

# Plotting the confusion matrix
plt.figure(figsize=(3,3))
plot_confusion_matrix(cm, classes=target_names, normalize=False)
plt.show()

混淆矩阵图如下所示:

图片

它是如何工作的...

步骤 1中,我们导入了所需的库,使用 Bagging 分类器构建我们的决策树分类器模型。在步骤 2中,我们读取了我们的数据集,它是winedata.csv。在步骤 3中,我们分离了特征集和目标变量。我们还把数据分成训练集和测试集。在步骤 4中,我们创建了一个决策树分类器模型,并将其传递给BaggingClassifier()。在DecisionTreeClassifier()中,criterion参数的默认值是gini,但我们将其更改为entropy。然后我们将我们的决策树模型传递给BaggingClassfier()。在BaggingClassfier()中,我们有包括n_estimatorsbootstrap在内的参数。n_estimators是集成中基础估计器的数量,默认值为10bootstrap参数指示是否用替换方式抽取样本,默认设置为True

步骤 5步骤 6中,我们将模型拟合到训练数据,并查看测试集的分数。在步骤 7中,我们调用了predict()方法并传递了测试特征集。在步骤 8中,我们添加了来自scikit-learn.orgplot_confusion_matrix()的代码,它将混淆矩阵作为其输入参数之一,并绘制混淆矩阵。在步骤 9中,我们通过传递混淆矩阵来调用plot_confusion_matrix()函数以生成混淆矩阵图。

还有更多...

我们还可以使用来自sklearn.model_selectionGridSearchCV()来进行网格搜索最佳参数,并在BaggingClassifier中使用它们:

  1. 首先,我们导入所需的库:
from sklearn.model_selection import GridSearchCV
  1. 然后,我们设置我们的参数值:
param_values = {'n_estimators': [10, 20, 25, 30], 'base_estimator__max_leaf_nodes':[5, 10, 15, 20], 'base_estimator__max_depth':[3, 4, 5]}
  1. 我们实例化DecisionTreeClassifier类,并将其传递给BaggingClassifier()函数。请注意,我们将oob_score设置为True以评估基于 OOB 样本构建的模型:
dt_model = DecisionTreeClassifier()
bag_dt_model_grid = BaggingClassifier(base_estimator=dt_model, oob_score=True, random_state=1) 
  1. 我们使用GridSearchCV()来确定最佳参数:
bc_grid = GridSearchCV(estimator=bag_dt_model_grid, param_grid=param_values, cv=20, n_jobs=-1)
bc_grid.fit(X_train, Y_train)
best_params = bc_grid.best_params_
print(best_params)

前面的代码返回了最佳参数:

图片

  1. 现在,我们取bc_grid.bestparams返回的值,并使用BaggingClassfier()函数重新构建我们的决策树模型。我们将max_leaf_nodes设置为10max_depth设置为3n_estimators设置为20
best_dt_model = DecisionTreeClassifier(criterion='entropy', max_leaf_nodes=10, max_depth=3) 
final_bag_dt_model = BaggingClassifier(base_estimator=best_dt_model, n_estimators=150, bootstrap=True, random_state=1, oob_score=True)

在前面的代码块中,我们将n_estimators设置为150n_estimators参数表示我们想要构建的树的数量。我们将最终模型拟合到我们的训练数据,并使用我们的测试特征集进行预测。

  1. 然后,我们可以在以下代码块中查看我们的 OOB 样本的准确率:
final_bag_dt_model.fit(X_train, Y_train)
bag_predictedvalues = final_bag_dt_model.predict(X_test)

# See the OOB accuracy
acc_oob = final_bag_dt_model.oob_score_
print(acc_oob)

如果我们绘制混淆矩阵,我们可以看到我们在错误分类的数量上有所改进。在早期示例中,有两个类 2 的实例被错误地预测为类 3,但现在我们可以看到错误分类的数量减少到了一个:

图片

参考内容

Bagging 回归器

Bagging 回归器与 Bagging 分类器类似。它们在每个回归器模型上训练原始训练集的随机子集,并汇总预测。然后,由于目标变量是数值的,因此汇总平均在迭代中。在下面的配方中,我们将展示使用自助样本实现的 Bagging 回归器的实现。

准备工作

我们将分别从sklearn.ensemblesklearn.tree导入所需的库BaggingRegressorDecisionTreeRegressor

from sklearn.ensemble import BaggingRegressor
from sklearn.tree import DecisionTreeRegressor

我们读取数据集,它是bostonhousing.csv,并查看 DataFrame 的维度:

df_housingdata = pd.read_csv('bostonhousing.csv')
df_housingdata.shape

现在,我们继续创建我们的特征集和目标变量集。

如何实现...

  1. 我们首先将特征集和响应集分开。在下面的代码块中,我们还将数据分为训练集和测试集:
X = df_housingdata.iloc[:,1:14]
Y = df_housingdata.iloc[:,-1]

X_train, X_test, Y_train, Y_test = train_test_split(X, Y, random_state=1)
  1. 然后,我们将创建一个DecisionTreeClassifier类的实例并将其传递给BaggingClassifier()函数:
dt_model = DecisionTreeRegressor()
bag_dt_model = BaggingRegressor(dt_model, max_features=1.0, n_estimators=5, bootstrap=True, random_state=1, )
  1. 我们将按照以下方式将我们的模型拟合到训练数据集:
bag_dt_model.fit(X_train, Y_train)
  1. 我们可以在以下代码块中看到模型得分:
bag_dt_model.score(X_test, Y_test)
  1. 我们使用predict()函数并将测试数据集传递给它来预测我们的目标变量,如下所示:
predictedvalues = bag_dt_model.predict(X_test)
  1. 我们使用以下代码绘制了我们的实际值和目标变量的预测值的散点图:
#We can plot the actuals and the predicted values 
plt.figure(figsize=(4, 4))
plt.scatter(Y_test, predictedvalues)
plt.xlabel('Actual')
plt.ylabel('Predicted')
plt.tight_layout()

执行前面的代码给出了以下散点图:

matplotlib.pyplot.tight_layout()自动调整子图参数以创建指定的填充。

  1. 在以下代码中,我们将n_estimators参数更改为 30,并重新执行从步骤 3步骤 6的步骤:
bag_dt_model = BaggingRegressor(dt_model, max_features=1.0, n_estimators=30, bootstrap=True, random_state=1, )

这给出了以下得分:

  1. 实际值与预测值的图表如下。这表明,与我们将n_estimator参数的值从5改为30的前一个案例相比,预测值更加准确:

它是如何工作的...

步骤 1中,我们将特征和目标变量集分开。我们还把我们的数据分成训练集和测试集。在步骤 2中,我们创建了一个决策树回归器模型并将其传递给BaggingRegressor()函数。请注意,我们还向BaggingRegressor()函数传递了n_estimator=5参数。如前所述,n_estimator是我们希望算法构建的森林中树的数量。在步骤 3中,我们训练了我们的模型。

步骤 4中,我们查看了模型得分,为 0.71。在步骤 5中,我们使用predict()函数预测测试子集中的目标变量。之后,在步骤 6中,我们绘制了一个散点图来探索实际目标值和预测目标值之间的关系。

步骤 7中,我们将n_estimator参数的值从5改为30并重新构建了我们的模型。这次,我们注意到模型得分提高到了 0.82。在步骤 8中,我们绘制了实际值和预测值,并发现实际值和预测值之间的相关性比我们之前使用n_estimators=5的模型要好得多。

参见

第六章:当有疑问时,使用随机森林

在本章中,我们将涵盖以下食谱:

  • 随机森林简介

  • 使用 scikit-learn 实现随机森林以预测信用卡违约

  • 使用 H2O 实现预测信用卡违约的随机森林

随机森林简介

随机森林是一种基于集成学习的监督机器学习算法,用于回归和分类问题。随机森林背后的基本思想是构建多个决策树并将它们聚合以获得准确的结果。决策树是一种确定性算法,这意味着如果给定的相同数据,每次都会产生相同的树。它们有过度拟合的倾向,因为它们使用给定数据构建最佳树,但在提供未见数据时可能无法泛化。构成随机森林的所有决策树都是不同的,因为我们使用不同的随机子集构建每棵树。随机森林通常比单个决策树更准确,因为它最小化了过度拟合。

以下图表展示了从源样本中进行的引导采样。在每个样本上构建模型,然后将预测组合起来以得出最终结果:

图片

随机森林中的每棵树都是按照以下步骤构建的,其中 A 代表整个森林,a 代表单个树,对于 a = 1A

  1. 使用替换方法创建一个从 xy 标签的 X[a][, ]Y[a] 训练的引导样本,D 训练

  2. X[a]Y[a] 上训练树 f[a]

  3. 取平均值或进行多数投票以得出最终预测

在回归问题中,通过取所有树做出的预测的平均值来对测试实例进行预测。这可以表示如下:

图片

在这里,N 是随机森林中树的总数。a=1 代表森林中的第一棵树,而森林中的最后一棵树是 A。(图片(图片)) 代表单个树的预测。

如果我们有一个分类问题,则使用多数投票或最常见的答案。

使用 scikit-learn 实现预测信用卡违约的随机森林

scikit-learn 库通过提供两个估计器实现随机森林:RandomForestClassifierRandomForestRegressor。它们接受各种参数,其中一些如下所述:

  •  n_estimators:此参数是算法在采取最大投票或平均预测之前构建的树的数量。一般来说,树的数量越多,性能和预测的准确性越好,但这也意味着计算成本更高。

  • max_features: 此参数是随机森林允许在单个树中尝试的最大特征数。

  • min_sample_leaf: 此参数决定了分割内部节点所需的最小叶子数。

  • n_jobs: 此超参数告诉引擎在拟合模型和预测新实例时并行运行多少个作业。如果它的值为 None1,则只运行一个作业。-1 的值意味着它将使用所有处理器。

  • random_state: 此参数将在 random_state 有确定的值时始终产生相同的结果,并且如果它已经给出了相同的超参数和相同的训练数据。

  • oob_score: 此参数也称为 袋外抽样,是一种随机森林交叉验证方法。在此抽样方法中,大约三分之一的 数据未用于训练模型,可用于评估其性能。这些样本称为 袋外样本

准备工作

在这个例子中,我们使用了来自 UCI ML 存储库的信用卡违约数据集。此数据集包含以下信息:

  • 违约支付

  • 人口统计因素

  • 信用数据

  • 付款历史

  • 信用卡客户的账单

数据和数据描述提供在 GitHub 文件夹中:

我们将首先加载所需的库并读取我们的数据集:

import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns

我们将工作文件夹设置为以下内容:

# Set your working directory according to your requirement
os.chdir(".../Chapter 6/Random Forest")
os.getcwd()

让我们现在读取我们的数据。我们将 DataFrame 名称前缀为 df_,这样我们就可以轻松理解它:

df_creditcarddata = pd.read_csv("UCI_Credit_Card.csv")

我们检查数据集的形状:

df_creditcarddata.shape

我们检查数据类型:

df_creditcarddata.dtypes

我们删除了 ID 列,因为这不是必需的:

df_creditcarddata = df_creditcarddata.drop("ID", axis= 1) 

我们可以用各种方式探索我们的数据。让我们看看几种不同的方法:

selected_columns = df_creditcarddata[['AGE','BILL_AMT1','BILL_AMT2','BILL_AMT3','BILL_AMT4','BILL_AMT5','BILL_AMT6', 'LIMIT_BAL']]

selected_columns.hist(figsize=(16, 20), bins=50, xlabelsize=8, ylabelsize=8);

注意,在前面的代码块中,我们使用了分号。分号有助于隐藏 Matplotlib 生成的冗长信息。xlabelsizeylabelsize 用于调整 x 轴和 y 轴的字体大小。

以下图表显示了数值变量的分布:

图片

现在,我们将按年龄组探索支付违约情况。我们将 age 变量分桶,并将分桶的值存储在 df_creditcarddata 中的新变量 age_group 中:

df_creditcarddata['agegroup'] = pd.cut(df_creditcarddata['AGE'], range(0, 100, 10), right=False)
df_creditcarddata.head()

然后,我们使用新的 age_group 变量来绘制每个年龄组的违约数量:

# Default vs Age
pd.crosstab(df_creditcarddata.age_group, \
           df_creditcarddata["default.payment.next.month"]).plot(kind='bar',stacked=False, grid=True) 

plt.title('Count of Defaults by AGE')
plt.xlabel('AGE')
plt.ylabel('# of Default')
plt.legend(loc='upper left')

以下截图显示了每个年龄的违约金额:

图片

由于我们不再需要它,我们可以从 df_creditcarddata 中删除 age_group 变量:

df_creditcarddata = df_creditcarddata.drop(columns = ['age_group'])
df_creditcarddata.head()

现在,我们将根据账户持有者的信用额度查看支付违约情况:

fig_facetgrid = sns.FacetGrid(df_creditcarddata, hue='default.payment.next.month', aspect=4)
fig_facetgrid.map(sns.kdeplot, 'LIMIT_BAL', shade=True)
max_limit_bal = df_creditcarddata['LIMIT_BAL'].max()
fig_facetgrid.set(xlim=(0,max_limit_bal));
fig_facetgrid.set(ylim=(0.0,0.000007));
fig_facetgrid.set(title='Distribution of limit balance by default.payment')
fig_facetgrid.add_legend()

上述代码给出了以下图表:

图片

我们还可以为一些变量分配标签以使解释更好。我们为 GenderMarriageEducation 变量分配标签。

我们还将pay变量的数据类型更改为字符串:

GenderMap = {2:'female', 1:'male'}
MarriageMap = {1:'married', 2:'single', 3:'other', 0: 'other'}
EducationMap = {1:'graduate school', 2:'university', 3:'high school', 4:'others', 5:'unknown', 6:'unknown', 0:'unknown'}

df_creditcarddata['SEX'] = df_creditcarddata.SEX.map(GenderMap)
df_creditcarddata['MARRIAGE'] = df_creditcarddata.MARRIAGE.map(MarriageMap) 
df_creditcarddata['EDUCATION'] = df_creditcarddata.EDUCATION.map(EducationMap)
df_creditcarddata['PAY_0'] = df_creditcarddata['PAY_0'].astype(str) 
df_creditcarddata['PAY_2'] = df_creditcarddata['PAY_2'].astype(str) 
df_creditcarddata['PAY_3'] = df_creditcarddata['PAY_3'].astype(str) 
df_creditcarddata['PAY_4'] = df_creditcarddata['PAY_4'].astype(str) 
df_creditcarddata['PAY_5'] = df_creditcarddata['PAY_5'].astype(str) 
df_creditcarddata['PAY_6'] = df_creditcarddata['PAY_6'].astype(str) 

在本书提供的代码包中还有更多的探索。我们现在继续训练我们的随机森林模型。

如何做到这一点...

现在我们将探讨如何使用随机森林来训练我们的模型:

  1. 我们首先将目标和特征变量分开:
predictor= df_creditcarddata.iloc[:, df_creditcarddata.columns != 'default.payment.next.month']
target= df_creditcarddata.iloc[:, df_creditcarddata.columns == 'default.payment.next.month']
  1. 我们在我们的特征集中将数值变量和非数值变量分开:
# save all categorical columns in list
categorical_columns = [col for col in predictor.columns.values if predictor[col].dtype == 'object']

# dataframe with categorical features
df_categorical = predictor[categorical_columns]

# dataframe with numerical features
df_numeric = predictor.drop(categorical_columns, axis=1)
  1. 我们对分类变量进行虚拟编码:
dummy_code_cat_vars = pd.get_dummies(df_categorical,drop_first=True)
  1. 我们将虚拟编码变量连接到我们的 DataFrame:
df_predictor = pd.concat([df_numeric, dummy_code_cat_vars], axis=1)
df_predictor.head()
  1. 我们将数据集分为训练集和测试集:
from sklearn.model_selection import train_test_split
X_train,X_test, y_train, y_test = train_test_split(df_predictor, target, test_size = 0.30, random_state=0)
print("x_train ",X_train.shape)
print("x_test ",X_test.shape)
print("y_train ",y_train.shape)
print("y_test ",y_test.shape)
  1. 我们使用StandardScaler()对特征进行缩放:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
  1. 我们可能会注意到列名已被更改为数字。我们将列名和索引值赋回缩放后的 DataFrame:
X_train_scaled.columns = X_train.columns.values
X_test_scaled.columns = X_test.columns.values
X_train_scaled.index = X_train.index.values
X_test_scaled.index = X_test.index.values

X_train = X_train_scaled
X_test = X_test_scaled
  1. 我们从sklearn.ensemble导入RandomForestClassifier()。然后我们将构建我们的随机森林分类器模型:
from sklearn.ensemble import RandomForestClassifier

classifier = RandomForestClassifier(random_state = 0, n_estimators = 100,\
 criterion = 'entropy', max_leaf_nodes= 20,oob_score = True, n_jobs = -1 )

# fit the model
model_RF = classifier.fit(X_train, y_train)
  1. 之后,我们计算训练模型的准确率:
acc_random_forest = round(classifier.score(X_train, y_train) * 100, 2)
print(round(acc_random_forest,2,), "%")
  1. 通过将y_testy_pred_proba传递给roc_curve(),我们得到假阳性率FPR)和真阳性率TPR)。我们还可以使用roc_auc_score()得到auc值。使用 FPR、TPR 和 AUC 值,我们在图表上绘制 ROC 曲线,并在图表上标注 AUC 值:
from sklearn import metrics

y_pred_proba = model_RF.predict_proba(X_test)[::,1]
fpr, tpr, _ = metrics.roc_curve(y_test, y_pred_proba)
auc = metrics.roc_auc_score(y_test, y_pred_proba)
plt.plot(fpr,tpr,label="AUC="+str(auc))
plt.legend(loc=4)
plt.show()

以下图表显示了带有标注 AUC 值的 ROC 曲线:

  1. 我们还可以评估其他分数,如下所示:
# predict the model
y_pred_RF = model_RF.predict(X_test)

# evaluate other scores
evaluation_scores = pd.Series({'Model': " Random Forest Classifier ",
'ROC Score' : metrics.roc_auc_score(y_test, y_pred_RF),
'Precision Score': metrics.precision_score(y_test, y_pred_RF),
'Recall Score': metrics.recall_score(y_test, y_pred_RF),
'Accuracy Score': metrics.accuracy_score(y_test, y_pred_RF),
'Kappa Score':metrics.cohen_kappa_score(y_test, y_pred_RF)})

print(evaluation_scores)

上述代码产生了以下评估分数:

  1. 我们还可以根据目标变量的类别评估一些统计信息,在这种情况下是01
from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred_RF))

sklearn.metrics中的classification_report根据目标变量的每个类别给出以下分数:

  1. 我们可以通过特征重要性绘制前 10 个变量,以查看哪些变量对模型很重要:
feature_importances = pd.Series(classifier.feature_importances_, index=X_train.columns)
feature_importances.nlargest(10).plot(kind='barh') #top 10 features

以下截图显示了前 10 个变量及其相对重要性:

我们可以更改超参数以查看模型如何表现更好。我们还可以对超参数值的组合进行网格搜索以微调我们的模型。

它是如何工作的...

步骤 1中,我们将目标和特征变量分开。在步骤 2中,在我们的特征集中,我们将数值变量和非数值变量分开。在步骤 3步骤 4中,我们将非数值变量转换为虚拟编码变量,并将它们添加回 DataFrame。在步骤 5中,我们将数据集分为训练集和测试集,在步骤 6中,我们从sklearn.preprocessing导入StandardScaler()并将相同的缩放应用于我们的特征。

执行步骤 6中的命令后,我们注意到列名已更改为顺序号。因此,在步骤 7中,我们将列名和索引值重新分配给缩放后的 DataFrame。在步骤 8中,我们从sklearn.ensemble中导入RandomForestClassifier()并构建了我们第一个随机森林分类器模型。之后,在步骤 9步骤 10中,我们使用我们的模型计算训练模型的准确度并绘制 ROC 曲线。我们还用 AUC 值标注了 ROC 曲线。

步骤 11中,我们评估了其他分数,包括 kappa 值、精确度、召回率和准确度。

步骤 12中,我们也根据目标变量的每个类别(在这种情况下是01)使用sklearn.metrics中的classification_report对这些分数进行了评估。在那里,classification_report()为我们提供了每个类别的精确度、召回率和 f1 分数等指标,以及每个指标的均值。

classification_report()报告平均值,包括平均总真实阳性、假阴性、假阳性,平均每个标签的无权均值,以及平均每个标签的支持加权均值。它还报告多标签分类的样本平均值。

最后,在步骤 13中,我们研究了前 10 个特征的相对变量重要性。这有助于在特征选择中构建具有正确特征的模型。

有各种特征选择方法可用,例如平均变量重要性、Boruta、递归特征选择以及使用 RF 进行变量选择。

还有更多...

隔离森林是另一种基于决策树的算法,用于异常值和异常检测。该算法基于异常数据点稀少的假设。

该算法的工作方式与随机森林略有不同。它创建了一组决策树,然后计算在树中隔离一个观察所需的路径长度。其想法是孤立观察值或异常值更容易分离,因为区分它们与正常情况所需的条件较少。因此,异常值将比正常观察值有更短的路径,并且因此将更靠近树的根部。当创建了多个决策树时,分数会被平均,这让我们对哪些观察值真正是异常值有一个很好的了解。因此,隔离森林被用于异常值和异常检测。

此外,隔离森林不利用任何距离或密度度量来检测异常。与基于距离和密度的方法相比,这显著降低了计算成本。

在 scikit-learn 中,sklearn.ensemble.IsolationForest提供了隔离森林算法的实现。

参见

  • 隔离森林算法的 scikit-learn 实现可以在这里找到:bit.ly/2DCjGGF

使用 H2O 实现预测信用卡违约的随机森林

H2O 是一个开源的分布式机器学习平台,允许您在大型数据集上构建机器学习模型。H2O 支持监督和非监督算法,并且非常快速、可扩展且易于实现。H2O 的 REST API 允许我们从外部程序(如 R 和 Python)访问其所有功能。在编写本书时,H2O 的最新版本是 H2O v3。

H2O 将闪电般的机器学习带给企业的原因如下所述:

"H2O 的核心代码是用 Java 编写的。在 H2O 中,使用分布式键/值存储来访问和引用数据、模型、对象等,跨越所有节点和机器。算法是在 H2O 的分布式 Map/Reduce 框架上实现的,并利用 Java fork/join 框架进行多线程处理。数据并行读取并分布在整个集群中,以压缩的列格式存储在内存中。H2O 的数据解析器具有内置的智能来猜测传入数据集的模式,并支持从多种格式的多个来源进行数据摄取"

  • 来自 h2o.ai

H2O 为我们提供了分布式随机森林,这是一种用于分类和回归任务的有力工具。它生成多个树,而不是单个树。在分布式随机森林中,我们使用分类和回归模型的平均预测来得出最终结果。

准备工作

Java 对于 H2O 运行是绝对必要的。请确保您已安装 Java,并在 Jupyter 中使用以下命令:

! apt-get install default-jre
! java -version

现在,您需要安装 H2O。要从 Jupyter 安装,请使用以下命令:

! pip install h2o

导入所需的库:

import h2o
import seaborn as sns
import numpy as np
import pandas as pd
import seaborn
import matplotlib.pyplot as plt
%matplotlib inline

from h2o.estimators.random_forest import H2ORandomForestEstimator
from sklearn import metrics

要使用 H2O,我们需要初始化一个实例并将其连接。我们可以这样做:

h2o.init()

默认情况下,前面的命令会尝试连接到一个实例。如果它无法这样做,它将尝试启动一个实例然后连接到它。一旦连接到实例,我们将看到该实例的详细信息,如下所示:

图片

我们将数据读入一个 pandas DataFrame:

df_creditcarddata = pd.read_csv("UCI_Credit_Card.csv")

我们使用 h2o.H2OFrame() 将我们的 pandas DataFrame 转换为 H2O DataFrame。我们将 df_creditcarddata H2O DataFrame 命名:

hf_creditcarddata = h2o.H2OFrame(df_creditcarddata)

检查 H2O DataFrame 中的数据是否已正确加载,如下所示:

hf_creditcarddata.head()

我们可以使用 describe() 方法查看摘要统计信息:

hf_creditcarddata.describe()

我们删除 ID 列,因为这对于我们的模型构建练习不是必需的:

hf_creditcarddata = hf_creditcarddata.drop(["ID"], axis = 1) 

我们现在将进入探索数据和构建模型的过程。

如何做到这一点...

在上一节中,我们对数据进行了各种探索。我们可以探索数据的方式没有限制。在本节中,我们将探讨一些更多技术:

  1. 我们检查每个特征变量与目标变量的相关性:
df_creditcarddata.drop(['default.payment.next.month'], \
     axis = 1).corrwith(df_creditcarddata['default.payment.next.month']).\
     plot.bar(figsize=(20,10), \
     title = 'Correlation with Response variable', \
     fontsize = 15, rot = 45, grid = True)

下面的图显示了每个特征与目标变量的相关性:

图片

  1. 我们检查 H2O DataFrame 中的数据类型。注意,对于pandas DataFrame,我们使用了dtypes。对于 H2O DataFrame,我们使用types
hf_creditcarddata.types
  1. 我们注意到它们都是整型数据类型。我们将它们转换为因子类型,这是分类性质的:
hf_creditcarddata['SEX'] = hf_creditcarddata['SEX'].asfactor()
hf_creditcarddata['EDUCATION'] = hf_creditcarddata['EDUCATION'].asfactor()
hf_creditcarddata['MARRIAGE'] = hf_creditcarddata['MARRIAGE'].asfactor()
hf_creditcarddata['PAY_0'] = hf_creditcarddata['PAY_0'].asfactor()
hf_creditcarddata['PAY_2'] = hf_creditcarddata['PAY_2'].asfactor()
hf_creditcarddata['PAY_3'] = hf_creditcarddata['PAY_3'].asfactor()
hf_creditcarddata['PAY_4'] = hf_creditcarddata['PAY_4'].asfactor()
hf_creditcarddata['PAY_5'] = hf_creditcarddata['PAY_5'].asfactor()
hf_creditcarddata['PAY_6'] = hf_creditcarddata['PAY_6'].asfactor()

我们可以使用hf_creditcarddata.types来检查数据类型,以查看数据类型转换是否已发生。

  1. 我们将二进制目标变量编码为因子类型变量:
hf_creditcarddata['default.payment.next.month'] = \
             hf_creditcarddata['default.payment.next.month'].asfactor() 
hf_creditcarddata['default.payment.next.month'].levels() 
  1. 我们选择特征和target变量:
predictors = ['LIMIT_BAL','SEX','EDUCATION','MARRIAGE','AGE','PAY_0','PAY_2','PAY_3','PAY_4','PAY_5','PAY_6','BILL_AMT1','BILL_AMT2','BILL_AMT3','BILL_AMT4','BILL_AMT5','BILL_AMT6','PAY_AMT1','PAY_AMT2','PAY_AMT3','PAY_AMT4','PAY_AMT5','PAY_AMT6']

target = 'default.payment.next.month'
  1. 现在,我们将 H2O DataFrame 拆分为训练集和测试集。我们使用 70%的数据来训练模型,剩余的 30%用于验证:
splits = hf_creditcarddata.split_frame(ratios=[0.7], seed=123) 
train = splits[0]
test = splits[1] 
  1. 我们使用默认设置构建了随机森林模型。您可以使用以下命令检查测试数据上的模型性能:
from h2o.estimators.random_forest import H2ORandomForestEstimator

RF_D = H2ORandomForestEstimator(model_id = 'RF_D',seed = 123)
RF_D.train(x = predictors, y = target, training_frame = train)

print(RF_D.model_performance(test))

这给我们以下性能指标:

图片

它是如何工作的...

准备就绪部分,我们安装了 JRE 和 H2O。我们使用h2o.init()初始化并连接到一个 H2O 实例。然后我们使用pandas读取我们的数据并将其转换为 H2O DataFrame。我们在 H2O DataFrame 上使用了head()describe()方法,就像我们在pandas DataFrame 上使用它们一样。然后我们从 H2O DataFrame 中删除了ID列。

准备就绪部分进行这些数据探索之后,我们继续下一步。在步骤 1中,我们检查了每个特征与target变量的相关性。在步骤 2中,我们使用了h2o DataFrame 并检查了数据类型。

注意,对于pandas DataFrame,我们使用了dtypes,而对于h2o DataFrame,我们使用了types

步骤 3中,我们使用了asfactor()函数将数值变量转换为分类类型。我们对那些本应为分类类型但显示为数值的变量执行了此操作。

在前面的例子中,我们在pandas DataFrame 上使用了astype()方法。对于 H2O DataFrame,我们使用了asfactor()方法。

步骤 4中,我们在target变量上使用了asfactor()将其转换为分类变量。

步骤 5中,我们将特征和target变量分开。在步骤 6中,我们使用split_frame()在 H2O DataFrame 上拆分了 H2O DataFrame,将数据集分为训练集和测试集。我们使用ratios参数,并将其设置为ratios=[0.7],以便split_frame()将 70%的数据分配给训练集,30%的数据分配给测试集。

步骤 7中,我们从h2o.estimators.random_forest导入了H2ORandomForestEstimator。我们传递了model_id,然后引用它来调用train()函数并传递预测变量和target变量。然后我们通过传递测试子集到model_performance()来查看性能指标。

还有更多...

在我们前面的例子中,我们有 AUC 为0.76和 log 损失为0.44

  1. 我们可以通过将nfolds作为参数传递给H2ORandomForestEstimator()来应用交叉验证:
RF_cv = H2ORandomForestEstimator(model_id = 'RF_cv', 
                                 seed = 12345, 
                                 ntrees = 500, 
                                 sample_rate = 0.9, 
                                 col_sample_rate_per_tree = 0.9, 
                                 nfolds = 10)

RF_cv.train(x = predictors, y = target, training_frame = train)
print(RF_cv.model_performance(test))

我们注意到 AUC 略有提高至0.77,而 log 损失降至0.43

图片

  1. 我们还可以应用网格搜索来从给定选项中提取最佳模型。我们设置选项如下:
search_criteria = {'strategy': "RandomDiscrete"}

hyper_params = {'sample_rate': [0.5, 0.6, 0.7],\
                'col_sample_rate_per_tree': [0.7, 0.8, 0.9],\
                'max_depth': [3, 5, 7]}
  1. 我们使用前面的搜索参数构建模型:
from h2o.grid.grid_search import H2OGridSearch

RF_Grid = H2OGridSearch(
                    H2ORandomForestEstimator(
                        model_id = 'RF_Grid', 
                        ntrees = 200, 
                        nfolds = 10,
                        stopping_metric = 'AUC', 
                        stopping_rounds = 25), 
                    search_criteria = search_criteria, # full grid search
                    hyper_params = hyper_params)
RF_Grid.train(x = predictors, y = target, training_frame = train)
  1. 我们现在按 AUC 降序对所有模型进行排序,然后选择第一个具有最高 AUC 的模型:
RF_Grid_sorted = RF_Grid.get_grid(sort_by='auc',decreasing=True)
print(RF_Grid_sorted)

best_RF_model = RF_Grid_sorted.model_ids[0]
best_RF_from_RF_Grid = h2o.get_model(best_RF_model)
  1. 我们应用最佳模型来测试我们的数据:
best_RF_from_RF_Grid.model_performance(test)
  1. 我们可以绘制到目前为止我们达到的最佳模型的变量重要性:
best_RF_from_RF_G
rid.varimp_plot()

这给出了以下图表:

图片

参见

你可能想了解一下极随机树,它们的实现方式略有不同,但有时可能比随机森林表现得更好。

在集成方法中,每个模型在数据集的子集和用于训练的特征向量子集方面以不同的方式学习。这些子集是随机选取的。极随机树在计算分裂和选择特征子集的方式上具有很高的随机性因素。与随机森林不同,在随机森林中分裂阈值是随机选择的,而在极随机树中,使用判别阈值作为分裂规则。因此,集成整体的方差降低,整体性能可能更好。

在以下链接中可以找到 scikit-learn 对极随机树的实现:bit.ly/2zWsNNS. H2O 也支持极随机树。

第七章:使用提升算法提升模型性能

在本章中,我们将涵盖以下食谱:

  • 提升算法简介

  • 使用 scikit-learn 实现 AdaBoost 进行疾病风险预测

  • 使用 scikit-learn 实现梯度提升进行疾病风险预测

  • 使用 XGBoost 和 scikit-learn 实现极端梯度提升进行玻璃识别

提升算法简介

提升算法是一种集成技术,通过将一组弱学习器组合起来形成一个强学习器,从而帮助提高模型性能和准确性。提升背后的理念是预测器应该从先前预测器所犯的错误中学习。

提升算法有两个关键特性:

  • 首先,它们经历多次迭代

  • 第二,每次迭代都专注于先前迭代错误分类的实例

当一个输入被假设错误分类时,其权重将在下一次迭代中改变,以便下一个假设可以正确分类它。将更多的权重给予在训练数据上提供更好性能的那些。通过多次迭代,这个过程将弱学习器转换为一组强学习器,从而提高模型性能。

在袋装法中,没有自助样本依赖于任何其他自助样本,因此它们都并行运行。提升算法以顺序方式进行,不涉及自助采样。袋装法和提升法都通过将不同模型中的多个估计组合成一个单一估计来减少单个估计的方差。然而,需要注意的是,如果单个模型过拟合,提升算法并不能显著帮助。如果模型过拟合,袋装法可能是一个更好的选择。另一方面,提升算法试图减少偏差,而袋装法很少能改善偏差。

在本章中,我们将介绍不同的提升算法,例如自适应提升AdaBoost)、梯度提升和极端梯度提升XGBoost)。

使用 scikit-learn 实现 AdaBoost 进行疾病风险预测

AdaBoost 是用于二元分类的最早提升算法之一。它由 Freund 和 Schapire 于 1996 年提出。此后,基于 AdaBoost 的许多其他提升算法已被开发出来。

另一种自适应提升的变体被称为AdaBoost-abstain。AdaBoost-abstain 允许每个基线分类器在其相关特征缺失时放弃投票。

AdaBoost 专注于将一组弱学习器组合成一个强学习器。AdaBoost 分类器的过程如下:

  1. 初始时,将一个短决策树分类器拟合到数据上。决策树可以只有一个分割,这被称为决策树桩。评估整体误差。这是第一次迭代。

  2. 在第二次迭代中,正确分类的数据将被赋予较低的权重,而错误分类的类别将被赋予较高的权重。

  3. 在第三次迭代中,将再次对数据进行拟合,并在下一次迭代中更改权重。

  4. 一旦这些迭代完成,每个分类器在每个迭代中都会根据错误率自动计算权重,以得出一个强分类器。

以下截图显示了 AdaBoost 的工作原理:

图片

该算法背后的概念是将权重分配给训练示例,并选择具有最低加权错误的分类器。最后,它通过这些弱学习者的线性组合构建一个强分类器。

AdaBoost 的一般公式如下:

图片

在这里,F(x) 代表一个强分类器,图片 代表权重,而 f(x) 代表一个弱分类器。

AdaBoost 分类器接受各种参数。以下解释了重要的参数:

  • base_estimator:用于训练模型的算法。如果未提供此参数的值,则基本估计器为 DecisionTreeClassifier (max_depth=1)

  • n_estimators:迭代训练的模型数量。

  • learning_rate:每个模型对权重的贡献。默认情况下,learning_rate 的值为 1。较低的 learning_rate 值会使模型训练得更慢,但可能会得到更好的性能分数。

准备工作

首先,导入 ospandas 包,并根据您的需求设置工作目录:

# import required packages
import os
import pandas as pd
import numpy as np

from sklearn.ensemble import AdaBoostClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier

from sklearn.metrics import roc_auc_score, roc_curve, auc
from sklearn.model_selection import train_test_split

# Set working directory as per your need
os.chdir(".../.../Chapter 8")
os.getcwd()

从 GitHub 下载 breastcancer.csv 数据集并将其复制到您的当前工作目录。读取数据集:

df_breastcancer = pd.read_csv("breastcancer.csv")

使用 head() 函数查看前几行:

df_breastcancer.head(5)

注意,diagnosis 变量具有 M 和 B 等值,分别代表恶性和良性。我们将对 diagnosis 变量进行标签编码,以便将 M 和 B 值转换为数值。

我们使用 head() 来查看变化:

# import LabelEncoder from sklearn.preprocessing
from sklearn.preprocessing import LabelEncoder

lb = LabelEncoder()
df_breastcancer['diagnosis'] =lb.fit_transform(df_breastcancer['diagnosis']) 
df_breastcancer.head(5)

然后,我们检查数据集是否有任何空值:

df_breastcancer.isnull().sum()

我们使用 shape() 检查数据集的形状:

df_breastcancer.shape

现在,我们将我们的目标集和特征集分开。我们还把数据集分成训练集和测试集:

# Create feature & response variables
# Drop the response var and id column as it'll not make any sense to the analysis
X = df_breastcancer.iloc[:,2:31]

# Target
Y = df_breastcancer.iloc[:,0]

# Create train & test sets
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.30, random_state=0, stratify= Y)

现在,我们将继续使用 AdaBoost 算法构建我们的模型。

重要的是要注意,准确率和 AUC 分数可能因随机分割和其他随机因素而不同。

如何做到这一点...

现在我们将探讨如何使用 AdaBoost 训练我们的模型:

  1. 在我们构建第一个 AdaBoost 模型之前,让我们使用 DecisionTreeClassifier 训练我们的模型:
dtree = DecisionTreeClassifier(max_depth=3, random_state=0)
dtree.fit(X_train, Y_train)
  1. 我们可以通过以下代码看到我们的准确率和 曲线下面积AUC):
# Mean accuracy
print('The mean accuracy is: ',(dtree.score(X_test,Y_test))*100,'%')

#AUC score
y_pred_dtree = dtree.predict_proba(X_test)
fpr_dtree, tpr_dtree, thresholds = roc_curve(Y_test, y_pred_dtree[:,1])
auc_dtree = auc(fpr_dtree, tpr_dtree)
print ('AUC Value: ', auc_dtree)

我们得到了准确率分数和 AUC 值分别为 91.81% 和 0.91。请注意,由于随机性,这些值可能因用户而异。

  1. 现在,我们将使用 scikit-learn 库构建我们的 AdaBoost 模型。我们将使用AdaBoostClassifier构建我们的AdaBoost模型。AdaBoost默认使用dtree作为基础分类器:
AdaBoost = AdaBoostClassifier(n_estimators=100, base_estimator=dtree, learning_rate=0.1, random_state=0)
AdaBoost.fit(X_train, Y_train)
  1. 我们检查了模型在测试数据上的准确率和 AUC 值:
# Mean accuracy
print('The mean accuracy is: ',(AdaBoost.score(X_test,Y_test))*100,'%')

#AUC score
y_pred_adaboost = AdaBoost.predict_proba(X_test)
fpr_ab, tpr_ab, thresholds = roc_curve(Y_test, y_pred_adaboost[:,1])
auc_adaboost = auc(fpr_ab, tpr_ab)
print ('AUC Value: ', auc_adaboost)

我们注意到我们得到了 92.82%的准确率分数和 0.97 的 AUC 值。这两个指标都高于我们在步骤 1中构建的决策树模型。

  1. 然后,我们必须微调我们的超参数。我们将n_estimators设置为100,将learning_rate设置为0.4
# Tuning the hyperparams
AdaBoost_with_tuning = AdaBoostClassifier(n_estimators=100, base_estimator=dtree, learning_rate=0.4, random_state=0)
AdaBoost_with_tuning.fit(X_train, Y_train)
  1. 现在,我们将检查我们的新模型在测试数据上的准确率和 AUC 值:
# Mean accuracy
print('The mean accuracy is: ',(AdaBoost_with_tuning.score(X_test,Y_test))*100,'%')

#AUC score
y_pred_adaboost_tune = AdaBoost.predict_proba(X_test)
fpr_ab_tune, tpr_ab_tune, thresholds = roc_curve(Y_test, y_pred_adaboost_tune[:,1])
auc_adaboost_tune = auc(fpr_ab_tune, tpr_ab_tune)
print ('AUC Value: ', auc_adaboost_tune)

我们注意到准确率下降到 92.39%,但我们得到了改进的 AUC 值,为 0.98。

它是如何工作的...

步骤 1中,我们使用DecisionTreeClassifier构建了我们的模型。在步骤 2中,我们注意到我们的平均准确率和 AUC 分数分别为 91.81%和 0.91。我们希望通过使用AdaBoost算法来提高这一点。

注意,AdaBoost算法默认使用决策树作为基础分类器。在步骤 3中,我们使用默认的基础学习器AdaBoost训练了我们的模型。我们将n_estimators设置为100,将learning_rate设置为0.1。我们在步骤 4中检查了我们的平均准确率和 AUC 值。我们注意到平均准确率和 AUC 值分别提高了 93.57%和 0.977。

步骤 5中,我们微调了AdaBoost算法的一些超参数,该算法使用决策树作为基础分类器。我们将n_estimators设置为100,将learning_rate设置为0.4步骤 6给出了我们在步骤 5中构建的模型的准确率和 AUC 值。我们看到准确率下降到 93.56%,而 AUC 值保持在 0.981 左右。

更多内容...

在这里,我们将展示如何使用 AdaBoost 训练一个模型,其中基础学习器为支持向量机SVM):

默认情况下,AdaBoost 使用决策树作为基础学习器。我们也可以使用不同的基础学习器。在以下示例中,我们使用 SVM 作为我们的基础学习器,并使用AdaBoost算法。我们使用SVC并使用rbf作为核:

from sklearn.svm import SVC

Adaboost_with_svc_rbf = AdaBoostClassifier(n_estimators=100, base_estimator=SVC(probability=True, kernel='rbf'), learning_rate=1, random_state=0)
Adaboost_with_svc_rbf.fit(X_train, Y_train)

我们可以使用支持向量分类器SVC)作为基础学习器来检查我们的 AdaBoost 模型的准确率和 AUC 值:

# Mean accuracy
print('The mean accuracy is: ',(Adaboost_with_svc_rbf.score(X_test,Y_test))*100,'%') 

#AUC score
y_pred_svc_rbf = Adaboost_with_svc_rbf.predict_proba(X_test)
fpr_svc_rbf, tpr_svc_rbf, thresholds = roc_curve(Y_test, y_pred_svc_rbf[:,1])
auc_svc_rbf = auc(fpr_svc_rbf, tpr_svc_rbf)
print ('AUC Value: ', auc_svc_rbf)

我们注意到准确率和 AUC 值分别下降到 62.57 和 0.92。

现在,我们将使用 SVC 重新构建我们的 AdaBoost 模型。这次,我们将使用线性核:

Adaboost_with_svc_linear =AdaBoostClassifier(n_estimators=100, base_estimator=SVC(probability=True, kernel='linear'), learning_rate=1, random_state=0)
Adaboost_with_svc_linear.fit(X_train, Y_train)

现在,我们得到了 90.64%的平均准确率和 0.96 的合理 AUC 值。

我们现在将使用以下代码绘制一个图表来比较每个模型的 AUC 值:

import matplotlib.pyplot as plt
% matplotlib inline
plt.figure(figsize=(8,8))

plt.plot(fpr_dtree, tpr_dtree,label="Model1: Dtree, auc="+str(auc_dtree))
plt.plot(fpr_ab, tpr_ab,label="Model2: Adaboost, auc="+str(auc_adaboost))
plt.plot(fpr_ab_tune,tpr_ab_tune,label="Model3: Adaboost with Tuning, auc="+str(auc_adaboost_tune))
plt.plot(fpr_svc_rbf, tpr_svc_rbf, label="Model4: Adaboost with SVC (RBF Kernel), auc="+str(auc_svc_rbf))
plt.plot(fpr_svc_lin, tpr_svc_lin, label="Model5: Adaboost with SVC (Linear Kernel), auc="+str(auc_svc_linear))

plt.legend(loc=5)
plt.show()

这给出了以下图表:

我们也可以使用以下代码绘制所有模型的准确率:

import matplotlib.pyplot as plt
% matplotlib inline
plt.figure(figsize=(8,8))

label = ['Decison Tree', 'Adaboost', 'Adaboost with Tuning', 'Adaboost with SVC (RBF)', 'Adaboost with SVC (Linear)']

values = [dtree.score(X_test,Y_test),
        AdaBoost.score(X_test,Y_test),
        AdaBoost_with_tuning.score(X_test,Y_test),
        Adaboost_with_svc_rbf.score(X_test,Y_test),
        Adaboost_with_svc_linear.score(X_test,Y_test)]

def plot_bar_accuracy():
    # this is for plotting purpose
    index = np.arange(len(label))
    plt.bar(index, values)
    plt.xlabel('Algorithms', fontsize=10)
    plt.ylabel('Accuracy', fontsize=10)
    plt.xticks(index, label, fontsize=10, rotation=90)
    plt.title('Model Accuracies')
    plt.show()

plot_bar_accuracy()

这给出了以下输出:

参见

我们也可以使用网格搜索与 AdaBoost:

#grid search using svm
Adaboost_with_svc = AdaBoostClassifier(n_estimators=100, base_estimator=SVC(probability=True, kernel='linear'), learning_rate=1, algorithm= 'SAMME')

Ada_Grid = {'n_estimators': [10,30,40,100],
           'learning_rate': [0.1, 0.2, 0.3]}

estimator = Adaboost_with_svc
Adaboost_with_grid_search = GridSearchCV(estimator,Ada_Grid).fit(X_train, Y_train)
print(Adaboost_with_grid_search.best_params_)
print(Adaboost_with_grid_search.best_score_)

在前面的代码中,我们进行了网格搜索,将n_estimators设置为103040100,将learning_rate设置为0.10.20.3

使用 scikit-learn 实现梯度提升机进行疾病风险预测

梯度提升是一种机器学习技术,它基于提升原理,其中弱学习器迭代地将注意力转向在先前迭代中难以预测的错误观测值,并创建一个弱学习者的集成,通常是决策树。

梯度提升以顺序方式训练模型,并涉及以下步骤:

  1. 将模型拟合到数据中

  2. 将模型拟合到残差中

  3. 创建一个新的模型

虽然 AdaBoost 模型通过使用分配给数据点的权重来识别错误,但梯度提升通过在损失函数中计算梯度来完成同样的工作。损失函数是衡量模型能够拟合其训练数据的程度的一个指标,通常取决于所解决的问题的类型。如果我们谈论回归问题,可能使用均方误差,而在分类问题中,可以使用对数损失。梯度下降过程用于在逐个添加树时最小化损失。模型中现有的树保持不变。

有一些超参数可以调整:

    • N_estimators:这表示模型中的树的数量。通常,它越高,模型学习数据的能力越好。

    • max_depth:这表示我们的树有多深。它用于控制过拟合。

    • min_samples_split:这是分割内部节点所需的最小样本数。过高的值可能会阻止模型学习关系。

    • learning_rate:这控制估计值的变化幅度。通常,较低值与较高的树的数量更受青睐。

    • loss:这指的是在每次分割中最小化的损失函数。算法中默认参数使用deviance,另一个是exponential

    • max_features:这表示在寻找最佳分割时需要考虑的特征数量。

    • criterion:这个函数衡量分割的质量,并支持friedman_msemae来评估模型的性能。

    • subsample:这表示用于拟合单个基学习者的样本的分数。选择小于 1.0 的子样本会导致方差减少和偏差增加。

    • min_impurity_split:这表示一个阈值,用于提前停止树的生长。

准备工作

我们将使用与训练我们的 AdaBoost 模型相同的数据集。在这个例子中,我们将看到如何使用梯度提升机来训练我们的模型。我们还将查看一些可以调整以改进模型性能的超参数。

首先,我们必须导入所有必需的库:

import os
import pandas as pd
import numpy as np

from sklearn.model_selection import train_test_split

from sklearn.ensemble import GradientBoostingClassifier 
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score, roc_auc_score
from sklearn.preprocessing import MinMaxScaler

import matplotlib.pyplot as plt
import itertools

然后,我们读取我们的数据并将目标变量编码为 1 和 0:

# Read the Dataset
df_breastcancer = pd.read_csv("breastcancer.csv")

from sklearn.preprocessing import LabelEncoder
lb = LabelEncoder()
df_breastcancer['diagnosis'] = lb.fit_transform(df_breastcancer['diagnosis']) 
df_breastcancer.head(5)

然后,将我们的目标变量和特征变量分开。我们将数据分为训练集和测试集:

# create feature & response variables
# drop the response var and id column as it'll not make any sense to the analysis
X = df_breastcancer.iloc[:,2:31]

# Target variable
Y = df_breastcancer.iloc[:,0]

# Create train & test sets
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.20, random_state=0, stratify= Y)

这与我们在AdaBoost示例的准备就绪部分中使用的代码相同。

如何操作...

现在,我们将探讨如何使用梯度提升机来训练我们的模型:

  1. 在上一节准备就绪中,我们导入了GradientBoostingClassifiersklearn.ensemble。我们使用GradieBoostingClassfier训练我们的模型:
GBM_model = GradientBoostingClassifier() 
GBM_model.fit(X_train, Y_train)
  1. 在这里,我们必须将我们的测试数据传递给predict()函数,使用我们在步骤 1中构建的模型进行预测:
Y_pred_gbm = GBM_model.predict(X_test)
  1. 现在,我们使用classification_report查看以下指标:
print(classification_report(Y_test, Y_pred_gbm))

classification_report提供了以下输出:

  1. 我们将使用confusion_matrix()生成混淆矩阵。然后,我们将confusion_matrix的输出传递给我们的预定义函数,即plot_confusion_matrix(),以绘制矩阵:

  1. 我们可以使用accuracy_score()roc_auc_score()来检查测试准确率和 AUC 值。

注意,accuracy_scoreroc_auc_score已从sklearn.metrics导入:

它是如何工作的...

步骤 1中,我们训练了一个梯度提升分类器模型。在步骤 2中,我们使用predict()方法对我们的测试数据进行预测。

步骤 3中,我们使用了classification_report()来查看每个类别的各种指标,如precisionrecallf1-score,以及每个指标的均值。classification_report()报告了总真实正例、假负例、假正例、未加权的每个标签的平均值和支持加权的每个标签的平均值。它还报告了多标签分类的样本平均值。

精度指的是分类器不将负例标记为正例的能力,而召回率指的是分类器找到所有正例的能力。f[1]分数是精度和召回率的加权调和平均值。最佳的f[1]分数是 1.0,最差的是 0.0。支持是每个类别的观测数。

步骤 4中,我们使用confusion_matrix()生成混淆矩阵,以查看真实正例、真实负例、假正例和假负例。

步骤 5中,我们使用accuracy_score()roc_auc_score()函数查看了测试数据的准确率和 AUC 值。

在下一节中,我们将使用网格搜索调整超参数,以找到最佳模型。

更多内容...

现在,我们将探讨如何微调梯度提升机的超参数:

  1. 首先,我们从sklearn.model_selection导入GridSearchCV
from sklearn.model_selection import GridSearchCV
  1. 我们将网格参数设置为一个变量:
parameters = {
    "n_estimators":[100,150,200],
    "loss":["deviance"],
    "learning_rate": [0.01, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],
    "min_samples_split":np.linspace(0.1, 0.5, 4),
    "min_samples_leaf": np.linspace(0.1, 0.5, 4),
    "max_depth":[3, 5, 8],
    "max_features":["log2","sqrt"],
    "criterion": ["friedman_mse", "mae"],
    "subsample":[0.3, 0.6, 1.0]
    }
  1. 我们使用GridSeacrhCV,它允许我们将估计量与网格搜索相结合以调整超参数。GridSeacrhCV方法从网格值中选择最佳参数,并将其与估计量一起使用:
grid = GridSearchCV(GradientBoostingClassifier(), parameters, cv=3, n_jobs=-1) 
grid.fit(X_train, Y_train)
  1. 然后,我们可以查看最佳参数:
grid.best_estimator_

看看下面的截图:

图片

  1. 我们将测试数据传递给predict方法以获取预测结果:
grid_predictions = grid.predict(X_test)
  1. 再次,我们可以看到classification_report提供的指标:
print(classification_report(Y_test, grid_predictions))

这给我们以下输出。我们注意到平均precisionf1-score比之前的案例有所提高:

图片

  1. 现在,我们将查看混淆矩阵并绘制它,就像我们之前做的那样:
cnf_matrix = confusion_matrix(Y_test, grid_predictions)
plot_confusion_matrix(cnf_matrix,classes=[0,1])

从前面的代码中,我们得到以下图表:

图片

  1. 现在,我们再次查看准确率和 AUC 值:
print("Accuracy score = {:0.2f}".format(accuracy_score(Y_test, grid_predictions)))
print("Area under ROC curve = {:0.2f}".format(roc_auc_score(Y_test, grid_predictions)))

我们注意到准确率保持不变,但 AUC 从 0.96 提高到 0.97:

图片

使用 scikit-learn 和 XGBoost 实现玻璃识别的极端梯度提升方法

XGBoost 代表极端梯度提升。它是梯度提升机的变体,旨在提高性能和速度。Python 中的 XGBoost 库实现了梯度提升决策树算法。梯度提升的名字来源于它在添加新模型时使用梯度下降算法来最小化损失。XGBoost 可以处理回归和分类任务。

由于在困难的机器学习问题中性能和执行速度出色,XGBoost 成为了 Kaggle 竞赛中参赛者的首选算法。

在 XGBoost 中使用的一些重要参数如下:

  • n_estimators/ntrees:这指定了要构建的树的数量。默认值是 50。

  • max_depth:这指定了最大树深度。默认值是 6。更高的值会使模型更加复杂,可能会导致过拟合。将此值设置为 0 表示没有限制。

  • min_rows:这指定了叶子的最小观测数。默认值是 1。

  • learn_rate:这指定了用于缩小特征权重的学习率。在每次提升步骤之后缩小特征权重可以使提升过程更加保守,并防止过拟合。范围是 0.0 到 1.0。默认值是 0.3。

  • sample_rate:这指定了训练实例(x轴)的行采样比率。例如,将此值设置为 0.5 表示 XGBoost 随机收集一半的数据实例来生长树。默认值是 1,范围是 0.0 到 1.0。更高的值可能会提高训练准确率。

  • col_sample_rate:这指定了每个级别的每个分割的列采样率(y 轴)。默认值是 1.0,范围是从 0 到 1.0。更高的值可能会提高训练准确率。

准备中...

您需要安装XGBoost库才能继续此食谱。您可以使用以下pip命令安装XGBoost库:

!pip install xgboost

导入所需的库:

# Import required libraries
import os
import pandas as pd
import numpy as np

from numpy import sort

from xgboost import XGBClassifier
from xgboost import plot_tree
from xgboost import plot_importance

from sklearn.feature_selection import SelectFromModel
from sklearn.model_selection import train_test_split, KFold, cross_val_score, StratifiedKFold

import matplotlib.pyplot as plt
from sklearn.metrics import accuracy_score, confusion_matrix

import itertools

设置您的当前工作文件夹并读取您的数据:

os.chdir("/.../Chapter 7")
os.getcwd()

df_glassdata = pd.read_csv('glassdata.csv')
df_glassdata.shape

此数据来自 UCI ML 存储库。列名已根据以下链接提供的数据描述进行更改:bit.ly/2EZX6IC

我们来看看数据:

df_glassdata.head()

我们将数据分为目标和特征集,并验证它。请注意,我们忽略了 ID 列:

# split data into X and Y
X = df_glassdata.iloc[:,1:10]
Y = df_glassdata.iloc[:,10]

print(X.shape)
print(Y.shape)

我们确认没有缺失值:

df_glassdata.isnull().sum()

我们将数据集分为训练集和测试集:

# Create train & test sets
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.30, random_state=0)

如何做到这一点...

现在,我们将继续构建我们的第一个 XGBoost 模型:

  1. 首先,我们将训练数据拟合到 XGBoost 分类器:
xg_model = XGBClassifier()
xg_model.fit(X_train, Y_train)
  1. 我们可以可视化训练模型中的单个 XGBoost 决策树。可视化决策树可以提供梯度提升过程的洞察:
plot_tree(xg_model, num_trees=0, rankdir='LR')
fig = pyplot.gcf()
fig.set_size_inches(30, 30)

这给我们以下输出:

num_trees=0时,我们得到第一个提升树。我们可以通过将索引值设置为num_trees参数来查看其他提升树。

  1. 在以下示例中,我们将num_trees设置为5
plot_tree(xg_model, num_trees=5, rankdir='LR')
fig = pyplot.gcf()
fig.set_size_inches(30, 30)

以下截图显示了第 6 个提升树:

您需要在您的系统上安装graphviz库来绘制提升树。

  1. 我们现在将在测试数据上使用predict()来获取预测值。我们可以使用accuracy_score()看到我们的测试准确率:
test_predictions = xg_model.predict(X_test)
test_accuracy = accuracy_score(Y_test, test_predictions)

print("Test Accuracy: %.2f%%" % (test_accuracy * 100.0))

通过执行此代码,我们可以看到测试准确率为 69.23%。

  1. 我们可以通过以下代码查看我们的混淆矩阵:
confusion_matrix(Y_test, predictions)
  1. 然后,我们可以使用预定义的函数plot_confusion_matrix(),该函数来自scikit-learn.org
def plot_confusion_matrix(cm, classes,
                          normalize=False,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):
    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=45)
    plt.yticks(tick_marks, classes)

    fmt = '.2f' if normalize else 'd'
    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, format(cm[i, j], fmt),
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.ylabel('True label')
    plt.xlabel('Predicted label')
    plt.tight_layout()
  1. 然后,我们查看目标变量的unique值以设置目标变量每个级别的名称:
Y.unique()

在以下代码块中,我们可以看到target_names的值为123567。我们相应地为目标变量的每个级别设置名称:

# Set names to each level of our target variable
target_names = [ '1', '2', '3', '5', '6', '7']

# Pass Actual & Predicted values to confusion_matrix()
cm = confusion_matrix(Y_test, predictions)

plt.figure()
plot_confusion_matrix(cm, classes=target_names)
plt.show()

我们现在可以可视化混淆矩阵,如下面的截图所示:

它是如何工作的...

步骤 1中,我们将XGBoostClassfier拟合到我们的训练数据。在步骤 2步骤 3中,我们可视化了单个提升树。为此,我们使用了plot_tree()函数。我们将我们的XGBoost模型传递给plot_tree(),并通过设置num_trees参数来设置树的索引。rankdir='LR'参数从左到右绘制树。将rankdir设置为 UT 将绘制垂直树。

步骤 4 中,我们将测试子集传递给 predict() 以获取测试准确率。步骤 5 给出了混淆矩阵。在 步骤 6 中,我们从 scikit-learn.org 中获取了一个预定义的函数,plot_confusion_matrix()。我们使用此函数来绘制我们的混淆矩阵。在 步骤 7 中,我们查看目标变量的唯一值,以便我们可以为混淆矩阵图中的每个类别设置名称。然后我们绘制了混淆矩阵以评估我们的模型。

更多...

在本节中,我们将探讨如何检查特征重要性并根据该重要性进行特征选择。我们还将探讨如何使用交叉验证评估我们的 XGBoost 模型的性能。

我们可以使用 model.feature_importances_ 检查特征重要性:

print(xg_model.feature_importances_)

我们还可以使用 plot_importance() 可视化特征重要性:

注意,我们已经从 xgboost 库中导入了 plot_importance

plot_importance(xg_model)

执行前面的代码后,我们可以看到以下图表,它显示了按重要性降序排列的特征重要性:

可以使用 SelectFromModel 进行特征选择:

SelectFromModel 类是从 sklearn.feature_selection 导入的。

在以下示例中,SelectFromModel 使用预训练的 XGBoost 模型,并从我们的数据集中提供具有选定特征的子集。它根据阈值值决定选定的特征。

重要性大于或等于阈值值的特征将被保留,而其他任何特征都将被丢弃:

# The threshold value to use for feature selection. 
feature_importance = sort(xg_model.feature_importances_)

# select features using threshold
for each_threshold in feature_importance:
    selection = SelectFromModel(xg_model, threshold=each_threshold, prefit=True)

    # Reduce X_train only to the selected feature
    selected_feature_X_train = selection.transform(X_train)

    # Train the model
    selection_model = XGBClassifier()
    selection_model.fit(selected_feature_X_train, Y_train)

    # Reduce X_test only to the selected feature
    selected_feature_X_test = selection.transform(X_test)

    # Predict using the test value of the selected feature
    predictions = selection_model.predict(selected_feature_X_test)

    accuracy = accuracy_score(Y_test, predictions)
    print("Threshold=%.5f, Number of Features=%d, Model Accuracy: %.2f%%" % (each_threshold, selected_feature_X_train.shape[1],accuracy*100))

从前面的代码中,我们可以看到以下输出:

我们注意到模型的性能随着选定特征数量的增加而波动。根据前面的输出,我们决定选择五个特征,这些特征给我们带来了 72% 的准确率。此外,如果我们使用奥卡姆剃刀原则,我们可能可以选择一个具有四个特征且略微降低的 71% 准确率的更简单模型。

我们还可以使用交叉验证来评估我们的模型。要执行 k 折交叉验证,我们必须从 sklearn.model_selection 中导入 KFold 类。

首先,我们创建 KFold 对象并说明我们希望拥有的拆分数量:

kfold = KFold(n_splits=40, random_state=0)
xg_model_with_kfold = XGBClassifier()

cv_results = cross_val_score(xg_model_with_kfold, X_train, Y_train, cv=kfold, verbose=True)
print("Mean Accuracy: %.2f%% Standard Deviation %.2f%%" % (cv_results.mean()*100, cv_results.std()*100))

使用 cross_val_score(),我们评估我们的模型,它给出了平均和标准差分类准确率。我们注意到我们得到了 77.92% 的平均准确率和 22.33% 的标准差。

在我们的情况下,我们有一个具有六个类别的目标变量。

如果对于多类分类任务有多个类别,在执行交叉验证时,您可以使用分层折:

Stratfold = StratifiedKFold(n_splits=40, random_state=0)
xg_model_with_stratfold = XGBClassifier()

sf_results = cross_val_score(xg_model_with_stratfold, X_train, Y_train, cv=Stratfold, verbose=True)
print("Mean Accuracy: %.2f%% Standard Deviation %.2f%%" % (sf_results.mean()*100, sf_results.std()*100))

使用 StratifiedKFold(),我们得到了改进的平均准确率 81.18% 和降低的标准差 21.37%。

注意,n_splits 不能大于每个类别中的成员数量。

参见

  • LightGBM 是由微软开发的开源软件,用于梯度提升框架。它使用基于树的算法,与其他 梯度提升机 (GBMs) 不同:bit.ly/2QW53jH

第八章:与堆叠结合

在本章中,我们将涵盖以下食谱:

  • 理解堆叠泛化

  • 通过结合预测实现堆叠泛化

  • 使用 H2O 实现营销活动结果预测的堆叠泛化

技术要求

本章的技术要求与我们在早期章节中详细说明的要求相同。

访问 GitHub 仓库以查找数据集和代码。数据集和代码文件按章节编号和主题名称排列。

理解堆叠泛化

堆叠泛化是一组多样化的模型的集成,引入了元学习器的概念。元学习器是一个第二级机器学习算法,它从基础学习器的最优组合中学习:

"堆叠泛化是一种非线性组合泛化器以创建一个新的泛化器的方法,试图最优地整合每个原始泛化器对学习集的看法。每个泛化器有更多的话要说(这不会在其他泛化器的话中重复),结果堆叠泛化就越好。"

- Wolpert (1992), 堆叠泛化

堆叠的步骤如下:

  1. 将您的数据集分为训练集和测试集。

  2. 在训练集上训练几个基础学习器。

  3. 将基础学习器应用于测试集进行预测。

  4. 使用预测作为输入,实际响应作为输出来训练一个高级学习器。

由于基础学习器的预测被混合在一起,堆叠也被称为混合。

以下图表为我们提供了堆叠的概念表示:

图片

对于堆叠泛化来说,基础学习器的预测之间不相关是很重要的。为了从基础学习器中获得不相关的预测,可以使用内部采用不同方法的算法来训练基础学习器。堆叠泛化主要用于最小化基础学习器的泛化误差,可以看作是交叉验证的改进版本。它使用比交叉验证的赢家通吃方法更复杂的策略来结合基础学习器的预测。

通过结合预测实现堆叠泛化

在本节中,我们将探讨如何从头实现堆叠泛化。

我们将执行以下步骤以开始:

  1. 为堆叠构建三个基础学习器。

  2. 结合每个基础学习器的预测。

  3. 使用另一种算法构建元学习器。

准备中...

在这个例子中,我们使用 UCI ML 存储库中的信用卡违约数据集。该数据集包含关于违约支付、人口统计因素、信用数据、支付历史和信用卡客户的账单陈述的信息。数据和数据描述在 GitHub 上提供。

我们将首先加载所需的库并读取我们的数据集:

import os
import numpy as np
import pandas as pd
from sklearn.metrics import accuracy_score

我们将工作文件夹设置为以下内容:

# Set your working directory according to your requirement
os.chdir(".../Chapter 8/")
os.getcwd()

现在我们读取我们的数据。我们将以df_作为 DataFrame 名称的前缀,这样我们就可以轻松理解它:

df_creditcarddata = pd.read_csv("UCI_Credit_Card.csv")

我们删除了ID列,因为这个列不是必需的:

df_creditcarddata = df_creditcarddata.drop("ID", axis= 1) 

我们检查数据集的形状:

df_creditcarddata.shape

我们注意到数据集现在有 30,000 个观测值和 24 列。现在让我们继续训练我们的模型。

如何做到这一点...

  1. 我们分割目标和特征变量:
from sklearn.model_selection import train_test_split

X = df_creditdata.iloc[:,0:23]
Y = df_creditdata['default.payment.next.month']
  1. 将数据分割为训练、验证和测试子集:
# We first split the dataset into train and test subset
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.1, random_state=1)

# Then we take the train subset and carve out a validation set from the same
X_train, X_val, Y_train, Y_val = train_test_split(X_train, Y_train, test_size=0.2, random_state=1)
  1. 检查每个子集的维度以确保我们的分割是正确的:
# Dimensions for train subsets
print(X_train.shape)
print(Y_train.shape)

# Dimensions for validation subsets
print(X_val.shape)
print(Y_val.shape)

# Dimensions for test subsets
print(X_test.shape)
print(Y_test.shape)
  1. 导入基础学习器和元学习器所需的库:
# for the base learners
from sklearn.naive_bayes import GaussianNB
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier

# for the meta learner
from sklearn.linear_model import LogisticRegression
  1. 创建基础学习器的实例并在我们的训练数据上拟合模型:
# The base learners
model_1 = GaussianNB()
model_2 = KNeighborsClassifier(n_neighbors=1)
model_3 = DecisionTreeClassifier()

# Now we train a list of models
base_learner_1 = model_1.fit(X_train, Y_train)
base_learner_2 = model_2.fit(X_train, Y_train)
base_learner_3 = model_3.fit(X_train, Y_train)
  1. 使用我们的验证子集上的基础学习器进行预测:
# We then use the models to make predictions on validation data
val_prediction_base_learner_1 = base_learner_1.predict(X_val)
val_prediction_base_learner_2 = base_learner_2.predict(X_val)
val_prediction_base_learner_3 = base_learner_3.predict(X_val)
  1. 我们从三个基础学习器获得了三组预测结果。我们使用它们来创建堆叠数组:
# And then use the predictions to create a new stacked dataset
import numpy as np
prediction_test_stack = np.dstack([val_prediction_base_learner_1, val_prediction_base_learner_2, val_prediction_base_learner_3])

# Now we stack the actual outcomes i.e. Y_Test with the prediction_stack
final_train_stack = np.dstack([prediction_test_stack, Y_val])
  1. 我们将final_train_stack堆叠数组转换为 DataFrame 并为每个列添加列名。验证维度并查看前几行:
stacked_train_dataframe = pd.DataFrame(final_train_stack[0,0:5400], columns='NB_VAL KNN_VAL DT_VAL Y_VAL'.split())

print(stacked_train_dataframe.shape)
print(stacked_train_dataframe.head(5))

在以下图像中,我们看到堆叠数组现在有 5,400 个观测值和 4 列:

  1. 使用我们在步骤 8中创建的堆叠数组来训练元学习器:
# Build the Mata-learner
meta_learner = LogisticRegression()
meta_learner_model = meta_learner.fit(stacked_train_dataframe.iloc[:,0:3], stacked_train_dataframe['Y_VAL'])
  1. 使用测试子集创建堆叠测试集:
# Take the test data (new data)
# Apply the base learners on this new data to make predictions

# We now use the models to make predictions on the test data and create a new stacked dataset
test_prediction_base_learner_1 = base_learner_1.predict(X_test)
test_prediction_base_learner_2 = base_learner_2.predict(X_test)
test_prediction_base_learner_3 = base_learner_3.predict(X_test)

# Create the stacked data
final_test_stack = np.dstack([test_prediction_base_learner_1, test_prediction_base_learner_2, test_prediction_base_learner_3])
  1. final_test_stack堆叠数组转换为 DataFrame 并为每个列添加列名。验证维度并查看前几行:
stacked_test_dataframe = pd.DataFrame(final_test_stack[0,0:3000], columns='NB_TEST KNN_TEST DT_TEST'.split())
print(stacked_test_dataframe.shape)
print(stacked_test_dataframe.head(5))

我们看到stacked_test_dataframe中的堆叠数组现在有 3,000 个观测值和 3 列:

  1. 检查base_learner在我们原始测试数据上的准确率:
test_prediction_base_learner_1 = base_learner_1.predict(X_test)
test_prediction_base_learner_2 = base_learner_2.predict(X_test)
test_prediction_base_learner_3 = base_learner_3.predict(X_test)

print("Accuracy from GaussianNB:", accuracy_score(Y_test, test_prediction_base_learner_1))
print("Accuracy from KNN:", accuracy_score(Y_test, test_prediction_base_learner_2))
print("Accuracy from Decision Tree:", accuracy_score(Y_test, test_prediction_base_learner_3))

我们注意到准确率如下。请注意,根据采样策略和超参数,结果可能会有所不同:

  1. 在堆叠测试数据上使用元学习器并检查准确率:
test_predictions_meta_learner = meta_learner_model.predict(stacked_test_dataframe)
print("Accuracy from Meta Learner:", accuracy_score(Y_test, test_predictions_meta_learner))

我们看到元学习器在堆叠测试数据上应用后返回的以下输出。这个准确率高于单个基础学习器:

它是如何工作的...

步骤 1中,我们将数据集分为目标和特征集。在步骤 2中,我们创建了我们的训练、验证和测试子集。在步骤 3中,我们检查了每个子集的维度,以验证分割是否正确。

然后我们继续构建基础学习器和元学习器。在步骤 4中,我们导入了用于基础学习器和元学习器的必需库。对于基础学习器,我们使用了高斯朴素贝叶斯、KNN 和决策树,而对于元学习器,我们使用了逻辑回归。

步骤 5中,我们将基础学习器拟合到我们的训练数据集。包括高斯朴素贝叶斯、KNN 和决策树在内的单一模型在 0 层空间中建立。然后我们有了三个基础模型。

步骤 6中,我们使用这三个基础模型在我们的验证子集中预测目标变量。然后我们得到了基础学习器给出的三组预测。

现在将通过堆叠泛化在第一层空间中将基础学习器通过逻辑回归进行集成。在步骤 7中,我们将三组预测值堆叠起来创建了一个数组。我们还把训练数据集的实际目标变量也堆叠到数组中。然后我们的数组中有四列:三列来自基础学习器的三组预测值,以及一列来自训练数据集的目标变量。我们称之为final_train_stack,也称为stacked_train_dataframe,并且根据用于基础学习器的算法命名列。在我们的例子中,我们使用了NB_VALKNN_VALDT_VAL这些名称,因为我们分别使用了高斯朴素贝叶斯、KNN 和决策树分类器。因为基础学习器被拟合到我们的验证子集中,所以我们给列名加上_VAL后缀,以便更容易理解。

步骤 9中,我们使用逻辑回归构建了元学习器并将其拟合到我们的堆叠数据集stacked_train_dataframe。请注意,我们离开了原始数据集,转向了一个包含基础学习器预测值的堆叠数据集。

步骤 10中,我们使用基础模型在我们的测试子集中获取预测结果。我们称之为final_test_stack。在步骤 11中,我们将final_test_stack数组转换为名为stacked_test_dataframe的 DataFrame。请注意,在我们的stacked_test_dataframe中,我们只有三列,这些列包含了在测试子集上应用基础学习器返回的预测值。这三列以使用的算法命名,后缀为_TEST,因此stacked_test_dataframe中的三列是NB_TESTKNN_TESTDT_TEST

步骤 12中,我们检查了基础模型在我们原始测试子集中的准确性。高斯朴素贝叶斯、KNN 和决策树分类器模型分别给出了 0.39、0.69 和 0.73 的准确性评分。

步骤 13中,我们检查了通过在堆叠测试数据上应用元学习器模型所获得的准确性。这给出了 0.77 的准确性,我们可以看到这比单个基础学习器的准确性要高。然而,请记住,仅仅在堆叠算法中添加更多基础学习器并不能保证你会得到更好的准确性。

还有更多...

创建堆叠模型可能很繁琐。mlxtend库提供了简化堆叠模型构建的工具。它提供了StackingClassifier,这是堆叠的集成学习元分类器,并且它还提供了StackingCVClassifier,它使用交叉验证为第二级元学习器准备输入,以防止过拟合。

您可以从pypi.org/project/mlxtend/下载库,或者使用pip install mlxtend命令进行安装。您可以在rasbt.github.io/mlxtend/user_guide/classifier/StackingClassifier/找到一些简单的堆叠分类和带有网格搜索的堆叠分类的优秀示例。

相关内容

您还可以查看ML-Ensemble库。要了解更多关于ML-Ensemble的信息,请访问ml-ensemble.com/ML-Ensemble的使用指南可在bit.ly/2GFsxJN找到。

使用 H2O 实现针对活动结果预测的堆叠泛化

H2O 是一个开源平台,用于构建机器学习和预测分析模型。算法是写在 H2O 的分布式 map-reduce 框架上的。使用 H2O,数据在节点间分布式,并行读取,并以压缩方式存储在内存中。这使得 H2O 非常快。

H2O 的堆叠集成方法是一个用于监督问题的集成机器学习算法,它通过堆叠找到一组预测算法的最佳组合。H2O 的堆叠集成支持回归、二分类和多分类。

在本例中,我们将探讨如何使用 H2O 的堆叠集成构建堆叠模型。我们将使用可在 GitHub 上找到的银行营销数据集。

准备中...

首先,导入h2o库和其他 H2O 模块:

import h2o
from h2o.estimators.random_forest import H2ORandomForestEstimator
from h2o.estimators.gbm import H2OGradientBoostingEstimator
from h2o.estimators.glm import H2OGeneralizedLinearEstimator
from h2o.estimators.stackedensemble import H2OStackedEnsembleEstimator
from h2o.grid.grid_search import H2OGridSearch

使用init()函数初始化h2o实例:

h2o.init()

一旦运行前面的代码,h2o实例将被初始化,我们将看到以下输出:

图片

现在我们已经实例化了H2O实例,我们将继续读取我们的数据集并构建堆叠模型。

如何操作...

  1. 我们使用h2o.import_file()函数读取数据。我们将文件名作为参数传递给函数:
df_bankdata = h2o.import_file("bank-full.csv")
  1. 我们将数据分为训练集和测试集:
# split into train and validation sets
train, test = df_bankdata.split_frame(ratios = [.8], seed = 1234)
  1. 我们检查训练集和测试集的维度,以验证分割是否正确:
train.shape, test.shape
  1. 我们查看前几行以确保数据正确加载:
df_bankdata.head()
  1. 我们将目标列和预测列的名称分别命名为responsepredictors
# Set the predictor names 
predictors = train.columns

# Set the response column name
response = "y"

# Remove the 'y' variable from the predictors
predictors.remove(response)

print(predictors)
  1. 我们使用asfactor()函数将response变量转换为分类类型:
train[response] = train[response].asfactor()
test[response] = test[response].asfactor()
  1. 我们将使用交叉验证来训练我们的基本学习器。我们将nfolds值设置为5。我们还设置了一个变量encodingOneHotExplicit。我们将使用此变量来对分类变量进行编码。
# Number of CV folds
nfolds = 5

# Using the `categorical_encoding` parameter
encoding = "OneHotExplicit"
  1. 我们开始训练我们的基本学习器。我们选择梯度提升机算法来构建我们的第一个基本学习器:
# Train and cross-validate a GBM
base_learner_gbm = H2OGradientBoostingEstimator(distribution="bernoulli",\
                                                ntrees=100,\
                                                max_depth=5,\
                                                min_rows=2,\
                                                learn_rate=0.01,\
                                                nfolds=nfolds,\
                                                fold_assignment="Modulo",\
                                                categorical_encoding = encoding,\
                                                keep_cross_validation_predictions=True)

base_learner_gbm.train(x=predictors, y=response, training_frame=train)
  1. 对于我们的第二个基本学习器,我们使用随机森林:
# Train and cross-validate a RF
base_learner_rf = H2ORandomForestEstimator(ntrees=250,\
                                           nfolds=nfolds,\
                                           fold_assignment="Modulo",\
                                           categorical_encoding = encoding,\
                                           keep_cross_validation_predictions=True)
base_learner_rf.train(x=predictors, y=response, training_frame=train)
  1. 对于我们的第三个基本学习器,我们实现了一个广义线性模型GLM):
# Train and cross-validate a GLM
base_learner_glm = H2OGeneralizedLinearEstimator(family="binomial",\
                                                 model_id="GLM",\
                                                 lambda_search=True,\
                                                 nfolds = nfolds,\
                                                 fold_assignment = "Modulo",\
                                                 keep_cross_validation_predictions = True)

base_learner_glm.train(x = predictors, y = response,training_frame = train)
  1. 在测试集上,根据test AUC获取最佳性能的基本学习器。将其与堆叠集成模型的test AUC进行比较:
# Compare to base learner performance on the test set
gbm_test_performance = base_learner_gbm.model_performance(test)
rf_test_performance = base_learner_rf.model_performance(test)
glm_test_performance = base_learner_glm.model_performance(test)

print("Best AUC from the GBM", gbm_test_performance.auc())
print("Best AUC from the Random Forest", rf_test_performance.auc())
print("Best AUC from the GLM", glm_test_performance.auc())

baselearner_best_auc_test = max(gbm_test_performance.auc(), rf_test_performance.auc(), glm_test_performance.auc())
print("Best AUC from the base learners", baselearner_best_auc_test)

stack_auc_test = perf_stack_test.auc()
print("Best Base-learner Test AUC: ", baselearner_best_auc_test)
print("Ensemble Test AUC: ", stack_auc_test)
  1. 我们使用前面步骤中构建的基本学习器训练一个堆叠集成:
all_models = [base_learner_glm, base_learner_gbm, base_learner_rf]

# Set up Stacked Ensemble. Using Deep Learning as the meta learner
ensemble_deep = H2OStackedEnsembleEstimator(model_id ="stack_model_d", base_models = all_models, metalearner_algorithm = 'deeplearning')

ensemble_deep.train(y = response, training_frame = train)

# Eval ensemble performance on the test data
perf_stack_test = ensemble_deep.model_performance(test)
stack_auc_test = perf_stack_test.auc()
print("Ensemble_deep Test AUC: {0}".format(stack_auc_test))

它是如何工作的...

步骤 1中,我们使用了h2o.import_file()函数来读取我们的数据集。

h2o.import_file()函数返回一个H2OFrame实例。

步骤 2中,我们将H2OFrame拆分为训练集和测试集。在步骤 3中,我们检查了这些子集的维度,以验证我们的拆分是否满足我们的要求。

步骤 4中,我们查看了一些前几行数据,以检查数据是否正确加载。在步骤 5中,我们分离出响应变量和预测变量的列名,并在步骤 6中,我们使用asfactor()函数将响应变量转换为分类类型。

步骤 7中,我们定义了一个名为nfolds的变量,我们使用它进行交叉验证。我们还定义了一个名为encoding的变量,我们在后续步骤中使用它来指示 H2O 对分类变量使用独热编码。在步骤 8步骤 10中,我们构建了我们的基本学习器。

步骤 11中,我们训练了一个梯度提升机模型。我们向一些超参数传递了一些值,如下所示:

  • nfolds:K 折交叉验证的折数。

  • fold_assignment:此选项指定用于交叉验证折分配的方案。此选项仅在指定了nfolds值且未指定fold_column时适用。

  • distribution:指定分布。在我们的情况下,由于响应变量有两个类别,我们将distribution设置为"bernoulli"

  • ntrees:树的数量。

  • max_depth:表示最大树深度。

  • min_rows:叶子节点中允许的最少观测值。

  • learn_rate:学习率取值范围从0.01.0

注意,对于所有基本学习器,交叉验证折数必须相同,并且keep_cross_validation_predictions必须设置为True

步骤 9中,我们使用以下超参数训练了一个随机森林基本学习器:ntreesnfoldsfold_assignment

步骤 10中,我们使用 GLM 训练了我们的算法。请注意,我们尚未对 GLM 中的分类变量进行编码。

H2O 建议用户允许 GLM 处理分类列,因为它可以利用分类列以获得更好的性能和高效的内存利用。

来自 H2o.ai 的建议:"我们强烈建议避免将具有任何级别的分类列进行 one-hot 编码成多个二进制列,因为这非常低效。这对于习惯手动扩展其分类变量以适应其他框架的 Python 用户来说尤其如此"。

步骤 11中,我们为每个基础学习者生成了测试 AUC 值,并打印了最佳的 AUC。

步骤 12中,我们通过使用H2OStackedEnsembleEstimator结合基础学习者的输出,训练了一个堆叠集成模型。我们在测试子集上使用了训练好的集成模型。请注意,默认情况下,GLM 被用作H2OStackedEnsembleEstimator的元学习器。然而,在我们的示例中,我们使用了深度学习作为元学习器。

注意,我们已经为我们的元学习器使用了默认的超参数值。我们可以使用metalearner_params指定超参数值。metalearner_params选项允许您传递一个字典/超参数列表,用于作为元学习器的算法。

微调超参数可以带来更好的结果。

还有更多...

您也可以以不同的方式组装一个模型列表以堆叠。在前面的示例中,我们训练了单个模型并将它们放入列表中进行集成。我们也可以训练一个模型网格:

  1. 我们指定网格的随机森林超参数:
hyper_params = {"max_depth": [3, 4, 5, 8, 10],
                "min_rows": [3,4,5,6,7,8,9,10],
                "mtries": [10,15, 20],
                "ntrees": [100,250,500, 750],
                "sample_rate": [0.7, 0.8, 0.9, 1.0],
                "col_sample_rate_per_tree": [0.5, 0.6, 0.7, 0.8, 0.9, 1.0]}

search_criteria = {"strategy": "RandomDiscrete", "max_models": 3, "seed": 1}
  1. 我们使用前面代码中定义的超参数进行网格训练:
# Train the grid
grid = H2OGridSearch(model=H2ORandomForestEstimator(nfolds=nfolds,\
                                                    fold_assignment="Modulo",\
                                                    keep_cross_validation_predictions=True),\
                     hyper_params=hyper_params,\
                     search_criteria=search_criteria,\
                     grid_id="rf_grid_binomial")

grid.train(x=predictors, y=response, training_frame=train)
  1. 我们使用随机森林网格进行集成训练:
# Train a stacked ensemble using the RF grid
ensemble = H2OStackedEnsembleEstimator(model_id="ensemble_rf_grid_binomial_9", base_models=grid.model_ids)

ensemble.train(x=predictors, y=response, training_frame=train)

# Evaluate ensemble performance on the test data
perf_stack_test = ensemble.model_performance(test)

# Compare to base learner performance on the test set
baselearner_best_auc_test = max([h2o.get_model(model).model_performance(test_data=test).auc() for model in grid.model_ids])

stack_auc_test = perf_stack_test.auc()

print("Best Base-learner Test AUC: ", baselearner_best_auc_test)
print("Ensemble Test AUC: ", stack_auc_test)

上述代码将给出最佳基础学习器测试 AUC,并测试集成模型的 AUC。如果响应变量高度不平衡,考虑微调以下超参数以控制过采样和欠采样:

  • balance_classes:此选项可用于平衡类分布。当启用时,H2O 将减少多数类的样本量或增加少数类的样本量。如果此选项被启用,您还可以指定class_sampling_factorsmax_after_balance_size选项的值。

  • class_sampling_factors:默认情况下,采样因子将在训练期间自动计算以获得类平衡。此行为可以通过class_sampling_factors参数进行更改。此选项为每个类设置过采样或欠采样比率,并需要balance_classes=true

  • max_after_balance_size:在大多数情况下,将balance_classes设置为 true 会增加 DataFrame 的大小。为了减少 DataFrame 的大小,您可以使用max_after_balance_size参数。此参数指定平衡类计数后的训练数据的最大相对大小,默认为5.0

参见

看一看由 Marios Michailidis 在其博士论文中开发的StackNetStackNet在 MIT 许可下可用。它是一个可扩展的分析框架,类似于前馈神经网络,并使用 Wolpert 的堆叠泛化概念来提高机器学习预测任务的准确性。它使用元学习者的概念,即它使用某些算法的预测作为其他算法的特征。StackNet 还可以在多个级别上进行泛化堆叠。然而,它计算密集。最初是用 Java 开发的,但现在也提供了一种较轻的 Python 版本,名为pystacknet

让我们思考一下 StackNet 是如何工作的。在神经网络的情况下,某一层的输出被插入到下一层作为输入,并应用激活函数,如 sigmoid、tanh 或 relu。同样,在 StackNet 的情况下,激活函数可以被任何监督机器学习算法所替代。

堆叠元素可以在两种模式下运行:正常堆叠模式和重新堆叠模式。在正常堆叠模式下,每一层使用前一层的结果进行预测。在重新堆叠模式下,每一层使用前几层的神经元和激活。

使用 StackNet 的示例代码将包括以下步骤:

  1. 导入所需的库(请注意,我们已经从pystacknet库中导入了StackNetClassifierStackNetRegressor):
import numpy as np

# import required libraries from sklearn
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.linear_model import LogisticRegression

from sklearn.metrics import roc_auc_score, log_loss
from sklearn.model_selection import StratifiedKFold

# import StackNetClassifier and StackNetRegressor from pystacknet
from pystacknet.pystacknet import StackNetClassifier,StackNetRegressor
from pystacknet.metrics import rmse,mae
  1. 我们读取数据,删除ID列,并检查数据集的维度:
df_creditcarddata = pd.read_csv("UCI_Credit_Card.csv")

#dropping the ID column, as it would not be required
df_creditcarddata.drop(["ID"],axis=1,inplace=True)

# Check the shape of the data
df_creditcarddata.shape
  1. 我们将目标变量和预测变量分开。同时,我们将数据分为训练集和测试集:
#create the predictor & target set
X = df_creditcarddata.iloc[:,0:23]
Y = df_creditcarddata['default.payment.next.month']

# Create train & test sets
X_train, X_test, Y_train, Y_test = \
train_test_split(X, Y, test_size=0.20, random_state=1)
  1. 我们定义了基学习器和元学习器的模型:
models=[[DecisionTreeClassifier(criterion="entropy", max_depth=5, max_features=0.5, random_state=1),
GradientBoostingClassifier(n_estimators=100, learning_rate=0.1, max_depth=5, max_features=0.5, random_state=1),
LogisticRegression(random_state=1)],
[RandomForestClassifier (n_estimators=500, criterion="entropy", max_depth=5, max_features=0.5, random_state=1)]]
  1. 现在我们使用StackNetClassifier构建堆叠集成。但是请注意,我们使用restacking=False,这意味着它使用正常堆叠模式:
model=StackNetClassifier(models, metric="accuracy", folds=4, restacking=False, use_retraining=True, use_proba=True, random_state=12345, n_jobs=1, verbose=1)

model.fit(X_train,Y_train )

# Uses the meta-learner model to predict the outcome
preds=model.predict_proba(X_test)[:,1]
print ("TEST ACCURACY without RESTACKING, auc %f " % (roc_auc_score(Y_test,preds)))

使用restacking=True时,StackNetClassifier将使用重新堆叠模式来构建模型。

在 Kaggle 竞赛中,StackNet 被用于赢得各种案例研究。关于如何使用StackNet的示例可以在bit.ly/2T7339y找到。

第九章:使用 Keras 的同质集成

在本章中,我们将涵盖以下主题:

  • 用于能量预测的同质模型集成

  • 用于手写数字分类的同质模型集成

简介

在集成模型的情况下,每个基础分类器必须在自身内部具有一定的多样性。这种多样性可以通过以下方式之一获得:

  • 通过使用各种重采样方法或训练数据的随机化来使用不同的训练数据子集

  • 通过为不同的基础学习器使用不同的学习超参数

  • 通过使用不同的学习算法

在集成模型的情况下,如果基础学习器使用了不同的算法,则该集成被称为异质集成方法。如果所有基础学习器在不同的训练集分布上使用了相同的算法,则该集成被称为同质集成

用于能量预测的同质模型集成

在以下示例中,我们将使用 Keras API。Keras 是一个开源的高级框架,用于构建深度神经网络。它建立在 TensorFlow 或 Theano 之上,并在幕后使用它们进行计算。Keras 可以在 CPU 和 GPU 上运行。Keras 的默认设置旨在在大多数情况下提供良好的结果。

Keras 的重点是模型的概念。Keras 支持两种类型的模型。主要类型的模型是一系列层,称为顺序。Keras 中的另一种模型是非顺序模型,称为模型

要构建一个顺序模型,执行以下步骤:

  1. 使用Sequential()实例化一个顺序模型

  2. 使用Dense类逐个添加层

  3. 使用以下方式编译模型:

    • 一个强制性的损失函数

    • 一个强制性的优化器

    • 可选评估参数

  4. 使用数据拟合模型

  5. 评估模型

这里是前面步骤的流程图:

在下面的代码块中,我们可以看到一个简短的代码示例:

# Instantiate a sequential model
seqmodel = Sequential()

# Add layers using the Dense class
seqmodel.add(Dense8, activation='relu')

# Compile the model
seqmodel.compile(loss='binary_crossentropy, optimizer='adam', metric=['accuracy'])

# Fit the model
seqmodel.fit(X_train, Y_train, batch_size=10)

准备工作

我们将首先安装 Keras。为了安装 Keras,您需要在您的系统中安装 Theano 或 TensorFlow。在这个例子中,我们将选择 TensorFlow 作为 Keras 的后端。

TensorFlow 有两种变体:CPU 版本和 GPU 版本。

要安装当前仅 CPU 版本,请使用以下命令:

pip install tensorflow

如果您必须安装 GPU 包,请使用以下命令:

pip install tensorflow-gpu

安装 TensorFlow 后,您需要使用以下命令安装 Keras:

sudo pip install keras

为了升级您已安装的 Keras 库,请使用以下命令:

sudo pip install --upgrade keras

安装完库后,让我们导入所需的库:

import os
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split

from sklearn.metrics import mean_squared_error

from keras.models import Sequential
from keras.layers import Dense

我们根据我们的要求设置我们的工作目录:

os.chdir("..../Chapter 9")
os.getcwd()

我们读取我们的energydata.csv数据集:

df_energydata = pd.read_csv("energydata.csv")

我们检查我们的数据集中是否有任何空值:

df_energydata.isnull().sum() 

如何做到这一点...

我们现在将构建我们的test子集并训练我们的神经网络模型:

  1. test子集分离出来,以便应用模型进行预测:
df_traindata, df_testdata = train_test_split(df_energydata, test_size=0.3)
  1. 检查traintest子集的形状:
print(df_traindata.shape)
print(df_testdata.shape)
  1. test子集拆分为目标和特征变量:
X_test = df_testdata.iloc[:,3:27] 
Y_test = df_testdata.iloc[:,28] 
  1. 通过检查X_testY_test的形状来验证前面的拆分:
print(X_test.shape)
print(Y_test.shape)
  1. 让我们使用 Keras 创建多个神经网络模型。我们使用For...Loop来构建多个模型:
ensemble = 20
frac = 0.7

predictions_total = np.zeros(5921, dtype=float)

for i in range(ensemble):
    print("number of iteration:", i)
    print("predictions_total", predictions_total)

    # Sample randomly the train data
    Traindata = df_traindata.sample(frac=frac)
    X_train = Traindata.iloc[:,3:27] 
    Y_train = Traindata.iloc[:,28] 

    ############################################################

    model = Sequential()
    # Adding the input layer and the first hidden layer
    model.add(Dense(units=16, kernel_initializer = 'normal', activation = 'relu', input_dim = 24))

    # Adding the second hidden layer
    model.add(Dense(units = 24, kernel_initializer = 'normal', activation = 'relu'))

    # Adding the third hidden layer
    model.add(Dense(units = 32, kernel_initializer = 'normal', activation = 'relu'))

    # Adding the output layer
    model.add(Dense(units = 1, kernel_initializer = 'normal', activation = 'relu'))

    # Compiling the ANN
    adam = optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.9, epsilon=None, decay=0.0)
    model.compile(loss='mse', optimizer=adam, metrics=['mean_squared_error'])
    # Fitting the ANN to the Training set

    model.fit(X_train, Y_train, batch_size = 16, epochs = 25)

    ############################################################

    # We use predict() to predict our values
    model_predictions = model.predict(X_test)

    model_predictions = model_predictions.flatten()
    print("TEST MSE for individual model: ", mean_squared_error(Y_test, model_predictions))
    print("")
    print(model_predictions)
    print("")

predictions_total = np.add(predictions_total, model_predictions)
  1. 将预测值的总和除以迭代次数,得到平均预测值。我们使用平均预测值来计算集成模型的均方误差MSE):
predictions_total = predictions_total/ensemble
print("MSE after ensemble: ", mean_squared_error(np.array(Y_test), predictions_total))

它是如何工作的...

下面是集成同质模型工作流程的图示表示:

图示

在前面的图中,我们假设我们有 100 个训练样本。我们在我们的 100 个训练样本上训练了 100 个模型,并将它们应用于我们的测试样本。我们得到了 100 组预测结果,通过平均是否目标变量是数值变量或是否在计算分类问题的概率来集成。在类别预测的情况下,我们会选择最大投票。

步骤 1中,我们分离了我们的训练和测试样本。这是我们在这个菜谱中构建的所有模型用于预测的相同测试样本。在步骤 2中,我们检查了traintest子集的形状。在步骤 3中,我们将测试子集拆分为目标和预测变量,然后在步骤 4中再次检查形状,以确保我们得到了正确的拆分。

步骤 5中,我们使用 Keras 库构建我们的神经网络模型。我们初始化了两个变量,ensemblefrac。我们使用ensemble变量运行一个循环,进行一定次数的迭代(在我们的例子中,我们将其设置为200)。然后我们使用frac变量来分配从训练子集中提取的 bootstrap 样本的数据比例。在我们的例子中,我们将frac设置为0.8

步骤 5中,在for...loop循环迭代中,我们构建了多个神经网络模型,并将这些模型应用于我们的测试子集以获取预测结果。我们通过使用add()方法传递一个层列表来创建顺序模型。在第一层中,我们使用input_dim参数指定输入维度。因为我们有 24 个输入维度,所以将input_dim设置为24。我们还通过设置Activation参数提到了在每个层中要使用的Activation函数。

您也可以通过Activation层设置Activation函数,如下所示:

# Example code to set activation function through the activation layer

from keras.layers import Activation, Dense 

model.add(Dense(64)) 
model.add(Activation('tanh'))

在这一步中,在我们构建模型之前,我们使用compile方法配置学习过程。compile方法接受必选的loss function、必选的optimizer和可选的metrics作为参数。

optimizer参数可以取随机梯度下降SGD)、RMSpropAdagradAdadeltaAdamAdamaxNadam等值。

loss function 可以取值如 mean_squared_errormean_absolute_errormean_absolute_percentage_errormean_squared_logarithmic_errorsquared_hingecategorical_hingebinary_crossentropy。更多详情请参阅 keras.io/losses/

我们还使用 np.add() 方法将预测数组添加到名为 predictions_total 的数组变量中。

一旦我们在 步骤 5 中的 for 循环中完成了所有迭代,我们将预测的总和除以迭代次数,这个迭代次数存储在 ensemble 变量中,并设置为 200,以获得平均预测。我们使用平均预测来计算集成结果的均方误差(MSE)。

还有更多...

如果你具有高计算需求,你可以使用 Google Colaboratory。Colaboratory 是一个免费的 Jupyter 笔记本环境,无需设置,完全在云端运行。它是一个免费的云服务,支持免费的 GPU。你可以使用 Google Colab 来构建你的深度学习应用程序,使用 TensorFlow、Keras、PyTorch 和 OpenCV。

一旦你使用 colab.research.google.com/ 创建了账户,你可以使用你的凭据登录。

一旦你登录,你可以直接点击“文件”菜单来创建你的 Python 笔记本:

图片

一旦你点击了“文件”标签,你会看到“新建 Python 3 笔记本”;一个支持 Python 3 的新笔记本将被创建。

你可以点击左上角的 Untitled0.ipynb 来重命名文件:

图片

前往“编辑”然后“笔记本设置”。一个窗口会弹出,显示你可以有不同的设置:

图片

选择图形处理单元GPU)作为硬件加速器,如前面的截图所示,以便使用免费的 GPU。

关于 Google Colab 的一个很酷的事情是它可以在你自己的 Google Drive 上工作。你可以选择在你的 Google Drive 中创建自己的文件夹,或者使用默认的 Colab 笔记本文件夹。为了使用默认的 Google Colab 笔记本文件夹,请按照以下截图中的步骤操作:

图片

要开始读取你的数据集,你可以将它们存储在 Google Drive 的文件夹中。

在你登录 Google Colab 并创建了一个新的笔记本之后,你必须在笔记本中执行以下代码来挂载驱动器:

from google.colab import drive

# This can be your folder path as per your drive
drive.mount('/content/drive')

当运行前面的代码时,它会要求输入授权码,如下所示:

图片

点击前面的 URL 获取授权码:

图片

将授权码粘贴到文本框中。每次授权时都会得到不同的授权码。授权后,驱动器将被挂载。

一旦挂载驱动器,您可以使用pandas读取.csv文件,就像我们在本章前面所展示的那样。其余的代码,如如何做部分所示,将按原样运行。如果您使用 GPU,您会注意到您的计算性能速度有显著提高。

为了在 Google Colab 中安装额外的库,您需要运行带有!符号的pip install命令。例如,您可以在 Google Colab 实例中运行!pip install utils来安装 utils。

参见

Keras 库中有各种激活函数可供使用:

  • Softmax 激活函数

  • 指数线性单元

  • 缩放指数线性单元

  • Softplus 激活函数

  • 矩形线性单元

  • 双曲正切激活函数

  • Sigmoid 激活函数

  • 线性激活函数

  • 指数激活函数

关于前面提到的激活函数的更多信息,请访问keras.io/activations/.

手写数字分类的同质模型集成

在本例中,我们将使用一个名为 The Street View House Numbers (SVHN)的数据集,该数据集来自ufldl.stanford.edu/housenumbers/。该数据集也以.hd5f格式提供在 GitHub 上。

此数据集是一个真实世界的数据集,并从 Google 街景图像中的门牌号码获得。

我们使用 Google Colab 来训练我们的模型。在第一阶段,我们使用 Keras 构建单个模型。在第二阶段,我们集成多个同质模型并将结果集成。

准备工作

该数据集包含 60,000 个门牌号码图像。每个图像标记为 1 到 10 之间。数字 1 标记为 1,数字 9 标记为 9,数字 0 标记为 10。图像是围绕单个字符的 32 x 32 图像。在某些情况下,我们可以看到图像在视觉上不清晰。

我们导入所需的库:

import os
import matplotlib.pyplot as plt
import numpy as np
from numpy import array

from sklearn.metrics import accuracy_score

from keras.models import Sequential, load_model
from keras.layers.core import Dense, Dropout, Activation

我们挂载 Google Drive:

from google.colab import drive
drive.mount('/content/drive')

现在,我们导入一个名为h5py的库来读取 HDF5 格式文件和我们的数据文件,该文件称为SVHN_single_grey.h5

import h5py

# Open the file as readonly
h5f = h5py.File('/content/drive/My Drive/DLCP/SVHN_single_grey.h5', 'r')

我们加载训练和测试子集,并关闭文件:

# Load the training and test set
x_train = h5f['X_train'][:]
y_train = h5f['y_train'][:]
x_test = h5f['X_test'][:]
y_test = h5f['y_test'][:]

# Close this file
h5f.close()

我们重塑我们的训练和测试子集。我们还更改数据类型为浮点数:

x_train = x_train.reshape(x_train.shape[0], 1024)
x_test = x_test.reshape(x_test.shape[0], 1024)

我们现在通过除以 255.0 来归一化我们的数据。这也将值的类型转换为浮点数:

# normalize inputs from 0-255 to 0-1
x_train = x_train / 255.0
x_test = x_test / 255.0

我们检查训练和测试子集的形状:

print("X_train shape", x_train.shape)
print("y_train shape", y_train.shape)
print("X_test shape", x_test.shape)
print("y_test shape", y_test.shape)

我们看到traintest特征以及我们的目标子集的形状如下:

我们可视化了一些图像。我们还在图像上方打印标签:

# Visualizing the 1st 10 images in our dataset
# along with the labels
%matplotlib inline
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 1))
for i in range(10):
 plt.subplot(1, 10, i+1)
 plt.imshow(x_train[i].reshape(32,32), cmap="gray")
 plt.title(y_train[i], color='r')
 plt.axis("off")
plt.show()

如下所示显示前 10 张图像:

现在我们对目标变量进行独热编码。同时,我们将y_test标签存储在另一个变量中,称为y_test_actuals,以备后用:

# Let us store the original y_test to another variable y_test_actuals
y_test_actuals = y_test

# one-hot encoding using keras' numpy-related utilities
n_classes = 10

print("Before one-hot encoding:")
print("Shape of Y_TRAIN before one-hot encoding: ", y_train.shape)
print("Shape of Y_TEST before one-hot encoding: ", y_test.shape)

y_train = np_utils.to_categorical(y_train, n_classes)
y_test = np_utils.to_categorical(y_test, n_classes)

print("After one-hot encoding:")
print("Shape of Y_TRAIN after one-hot encoding: ", y_train.shape)
print("Shape of Y_TRAIN after one-hot encoding: ", y_test.shape)

独热编码前后的形状如下:

如何做...

现在,我们将使用 Keras 库构建一个单一模型:

  1. 使用序列模型构建一个线性的层堆叠:
# building a linear stack of layers with the sequential model
model = Sequential()
model.add(Dense(512, input_shape=(1024,)))
model.add(Activation('relu')) 

model.add(Dense(512))
model.add(Activation('relu'))

model.add(Dense(10))
model.add(Activation('softmax'))
  1. 编译模型:
# compiling the sequential model
model.compile(loss='categorical_crossentropy', metrics=['accuracy'], optimizer='adam')
  1. 将模型拟合到train数据,并使用test数据进行验证:
# training the model and saving metrics in history
svhn_model = model.fit(x_train, y_train,
          batch_size=128, epochs=100,
          verbose=2,
          validation_data=(x_test, y_test))
  1. 在每个 epoch 绘制模型的准确率:
# plotting the metrics
fig = plt.figure(figsize=(12,4))

#plt.subplot(2,1,1)
plt.plot(svhn_model.history['acc'])
plt.plot(svhn_model.history['val_acc'])
plt.title('Model Accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epochs')
plt.legend(['Train', 'Test'], loc='uppper left')

plt.tight_layout()

我们可以看到以下模型准确率图:

图片

  1. 在每个 epoch 绘制损失:
# plotting the metrics
fig = plt.figure(figsize=(12,4))

plt.plot(svhn_model.history['loss'])
plt.plot(svhn_model.history['val_loss'])
plt.title('Model Loss')
plt.ylabel('Loss')
plt.xlabel('Epochs')
plt.legend(['Train', 'Test'], loc='upper right')

plt.tight_layout()

我们可以看到以下模型损失图:

图片

  1. 重用 scikit-learn 网站上的代码来绘制混淆矩阵:
# code from http://scikit-learn.org
def plot_confusion_matrix(cm, classes,
normalize=False,
title='Confusion matrix',
cmap=plt.cm.Blues):
"""
This function prints and plots the confusion matrix.
"""
plt.imshow(cm, cmap=cmap)
plt.title(title)
plt.colorbar()
tick_marks = np.arange(len(classes))
plt.xticks(tick_marks, classes, rotation=45)
plt.yticks(tick_marks, classes)

thresh = cm.max() / 2.
for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
plt.text(j, i, cm[i, j],
horizontalalignment="center",
color="white" if cm[i, j] > thresh else "black")

plt.ylabel('Actuals')
plt.xlabel('Predicted')
  1. 以数值和图形两种方式绘制混淆矩阵:
target_names = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

# Formulating the Confusion Matrix
import itertools
from sklearn.metrics import confusion_matrix

cm = confusion_matrix(y_test_actuals, predicted_classes)
print(cm)

plt.figure(figsize=(10,10))
plot_confusion_matrix(cm, classes=target_names, normalize=False)
plt.show()

混淆矩阵如下所示:

图片

  1. 现在,我们将查看如何集成多个同质模型的成果。定义一个函数来将模型拟合到训练数据:
# fit model on dataset
def train_models(x_train, y_train):
  # building a linear stack of layers with the sequential model
  model = Sequential()
  model.add(Dense(512, input_shape=(1024,)))
  model.add(Activation('relu')) 
  model.add(Dropout(0.2))

  model.add(Dense(512))
  model.add(Activation('relu'))
  model.add(Dropout(0.2))

  model.add(Dense(10))
  model.add(Activation('softmax'))

  # compiling the sequential model
  model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

  # training the model and saving metrics in history
  svhn_model = model.fit(x_train, y_train, batch_size=32, epochs=25)

  return model
  1. 编写一个函数来集成所有模型的预测:
# make an ensemble prediction for multi-class classification
def ensemble_predictions(models, x_test):
  # make predictions
  y_predicted = [model.predict(x_test) for model in models]
  y_predicted = np.array(y_predicted)

  # sum predictions from all ensemble models
  predicted_total = np.sum(y_predicted, axis=0)

  # argmax across classes
  result = np.argmax(predicted_total, axis=1)

  return result

numpy.argmax返回数组在特定轴上的最大元素的索引。

  1. 编写一个函数来评估模型并获取每个模型的准确率分数:
# evaluate a specific number of members in an ensemble
def evaluate_models(models, no_of_models, x_test, y_test):
 # select a subset of members
 subset = models[:no_of_models]

 # make prediction
 y_predicted_ensemble = ensemble_predictions(subset, x_test)

 # calculate accuracy
 return accuracy_score(y_test_actuals, y_predicted_ensemble)
  1. 拟合所有模型:
# fit all models
no_of_models = 50

models = [train_models(x_train, y_train) for _ in range(no_of_models)]

# evaluate different numbers of ensembles
all_scores = list()
for i in range(1, no_of_models+1):
  score = evaluate_models(models, i, x_test, y_test)
  print("Accuracy Score of model ", i, " ", score)
  all_scores.append(score)
  1. 将准确率分数与每个 epoch 对应:
# plot score vs number of ensemble members
x_axis = [i for i in range(1, no_of_models+1)]
plt.plot(x_axis, all_scores)
plt.show()

它是如何工作的...

第 1 步第 7 步中,我们构建了一个单一的神经网络模型,以了解如何使用标记的图像数据集来训练我们的模型并预测未见过的图像的实际标签。

第 1 步中,我们使用 Keras 的序列模型构建了一个线性的层堆叠。我们定义了三个层:一个输入层、一个隐藏层和一个输出层。由于我们有 32 x 32 的图像,我们将input_shape=1024提供给输入层。我们在第一层和第二层使用了 relu 激活函数。因为这是一个多类分类问题,所以我们为输出层使用了 softmax 激活函数。

第 2 步中,我们使用loss='categorical_crossentropy'optimizer='adam'编译了模型。在第 3 步中,我们将模型拟合到我们的训练数据,并在测试数据上验证它。

第 4 步第 5 步中,我们绘制了每个 epoch 的模型准确率和损失指标。

第 6 步第 7 步中,我们重用了 scikit-learn 网站上的plot_confusion_matrix()函数来绘制我们的混淆矩阵,既以数值形式又以可视形式。

第 8 步开始,我们集成了多个模型。我们编写了三个自定义函数:

  • train_models(): 使用序列层训练和编译我们的模型。

  • ensemble_predictions(): 对预测进行集成,并找到所有观察值跨类中的最大值。

  • evaluate_models(): 计算每个模型的准确率分数。

第 11 步中,我们拟合了所有模型。我们将no_of_models变量设置为50。我们通过调用train_models()函数在循环中训练我们的模型。然后,我们将x_trainy_train传递给每个迭代中构建的每个模型的train_models()函数。我们还调用了evaluate_models(),它返回了每个构建的模型的准确率分数。然后,我们将所有准确率分数附加在一起。

第 12 步中,我们绘制了所有模型的准确率分数。

第十章:使用 H2O 的异构集成分类器

在本章中,我们将介绍以下食谱:

  • 使用异构集成分类器预测信用卡违约者

简介

在本章中,我们将展示如何使用 H2O 构建异构集成分类器,H2O 是一个开源的、分布式的、内存中的机器学习平台。H2O 中提供了大量的监督和无监督算法。

在监督算法中,H2O 为我们提供了神经网络、随机森林(RF)、广义线性模型、梯度提升机、朴素贝叶斯分类器和 XGBoost。

H2O 还为我们提供了一个旨在通过堆叠过程找到一组预测算法最佳组合的堆叠集成方法。H2O 的堆叠集成支持回归和分类。

使用异构集成分类器预测信用卡违约者

我们将以台湾的信用卡支付违约者数据为例。这是我们之前在第三章,“重采样方法”中使用的相同数据集,用于构建逻辑回归模型。在本食谱中,我们将使用不同的算法构建多个模型,并最终构建一个堆叠集成模型。

此数据集包含有关台湾信用卡客户的信息。这包括有关支付违约者、客户的人口统计因素、他们的信用数据和他们的支付历史信息。数据集可在 GitHub 上找到。它也可以从其主要来源 UCI ML 存储库获得:bit.ly/2EZX6IC

在我们的例子中,我们将使用以下来自 H2O 的监督算法来构建我们的模型:

  • 广义线性模型

  • 分布式随机森林

  • 梯度提升机

  • 堆叠集成

我们将了解如何在 Python 中使用这些算法,并学习如何为每个算法设置一些超参数。

准备工作

我们将使用 Google Colab 来构建我们的模型。在第十章,“使用 H2O 的异构集成分类器”,我们在“更多内容”部分解释了如何使用 Google Colaboratory。

我们将首先按照以下步骤在 Google Colab 中安装 H2O:

! pip install h2o

执行前面的命令将显示一些指令,最后一行将显示以下消息(H2O 的版本号将根据最新版本而有所不同):

Successfully installed colorama-0.4.1 h2o-3.22.1.2

我们将按照以下方式导入所有必需的库:

import pandas as pd
import numpy as np

from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, roc_curve, auc
from sklearn import tree

import h2o
from h2o.estimators.glm import H2OGeneralizedLinearEstimator
from h2o.estimators.random_forest import H2ORandomForestEstimator
from h2o.estimators.gbm import H2OGradientBoostingEstimator
from h2o.grid.grid_search import H2OGridSearch
from h2o.estimators.stackedensemble import H2OStackedEnsembleEstimator

import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline

然后,我们将初始化 H2O:

# Initialize H2o
h2o.init()

在成功初始化后,我们将看到以下截图所示的信息。这些信息可能因环境而异:

图片

我们将从 Google Drive 读取我们的数据集。为了做到这一点,我们首先需要挂载驱动器:

from google.colab import drive
drive.mount('/content/drive')

它将指导你访问一个 URL 以获取授权代码。你需要点击该 URL,复制授权代码,并将其粘贴。在成功挂载后,你可以从 Google Drive 的相应文件夹中读取你的文件:

# Reading dataset from Google drive
df_creditcarddata = h2o.import_file("/content/drive/My Drive/Colab Notebooks/UCI_Credit_Card.csv")

注意,使用 h2o.import_file,我们创建 h2o.frame.H2OFrame。这与 pandas DataFrame 类似。然而,在 pandas DataFrame 的情况下,数据存储在内存中,而在这个案例中,数据位于 H2O 集群上。

你可以在 H2O DataFrame 上运行与在 pandas 上类似的方法。例如,为了查看 DataFrame 中的前 10 个观测值,你可以使用以下命令:

df_creditcarddata.head()

为了检查 DataFrame 的维度,我们使用以下命令:

df_creditcarddata.shape

为了查看所有列名,我们运行以下语法:

df_creditcarddata.columns

pandas DataFrame 中,我们使用 dtypes 来查看每列的数据类型。在 H2o DataFrame 中,我们会使用以下:

df_creditcarddata.types

这给出了以下输出。请注意,分类变量以 'enum' 的形式出现:

图片

我们在数据集中有目标变量 default.payment.next.month。这告诉我们哪些客户已经违约或未违约。我们想看到违约者和非违约者的分布:

df_creditcarddata['default.payment.next.month'].table()

这给出了 default.payment.next.month 变量中每个类别的计数:

图片

我们不需要 ID 列进行预测建模,所以我们将其从 DataFrame 中删除:

df_creditcarddata = df_creditcarddata.drop(["ID"], axis = 1) 

我们可以使用 hist() 方法查看数值变量的分布:

import pylab as pl
df_creditcarddata[['AGE','BILL_AMT1','BILL_AMT2','BILL_AMT3','BILL_AMT4','BILL_AMT5','BILL_AMT6', 'LIMIT_BAL']].as_data_frame().hist(figsize=(20,20))
pl.show()

下面的屏幕截图显示了绘制的变量。这可以帮助我们分析每个变量:

图片

为了扩展我们的分析,我们可以通过性别、教育和婚姻状况查看违约者和非违约者的分布:

# Defaulters by Gender
columns = ["default.payment.next.month","SEX"]
default_by_gender = df_creditcarddata.group_by(by=columns).count(na ="all")
print(default_by_gender.get_frame())

# Defaulters by education
columns = ["default.payment.next.month","EDUCATION"]
default_by_education = df_creditcarddata.group_by(by=columns).count(na ="all")
print(default_by_education.get_frame())

# Defaulters by MARRIAGE
columns = ["default.payment.next.month","MARRIAGE"]
default_by_marriage = df_creditcarddata.group_by(by=columns).count(na ="all")
print(default_by_marriage.get_frame())

在下面的屏幕截图中,我们可以看到不同类别的违约者分布:

图片

现在,我们将分类变量转换为因子:

# Convert the categorical variables into factors

df_creditcarddata['SEX'] = df_creditcarddata['SEX'].asfactor()
df_creditcarddata['EDUCATION'] = df_creditcarddata['EDUCATION'].asfactor()
df_creditcarddata['MARRIAGE'] = df_creditcarddata['MARRIAGE'].asfactor()
df_creditcarddata['PAY_0'] = df_creditcarddata['PAY_0'].asfactor()
df_creditcarddata['PAY_2'] = df_creditcarddata['PAY_2'].asfactor()
df_creditcarddata['PAY_3'] = df_creditcarddata['PAY_3'].asfactor()
df_creditcarddata['PAY_4'] = df_creditcarddata['PAY_4'].asfactor()
df_creditcarddata['PAY_5'] = df_creditcarddata['PAY_5'].asfactor()
df_creditcarddata['PAY_6'] = df_creditcarddata['PAY_6'].asfactor()

我们还将二元目标变量 default.payment.next.month 编码为因子变量。转换后,我们使用 levels() 方法检查目标变量的类别:

# Also, encode the binary response variable as a factor
df_creditcarddata['default.payment.next.month'] = df_creditcarddata['default.payment.next.month'].asfactor() 
df_creditcarddata['default.payment.next.month'].levels()

然后,我们将定义我们的预测变量和目标变量:

# Define predictors manually
predictors = ['LIMIT_BAL','SEX','EDUCATION','MARRIAGE','AGE','PAY_0','PAY_2','PAY_3',\
 'PAY_4','PAY_5','PAY_6','BILL_AMT1','BILL_AMT2','BILL_AMT3','BILL_AMT4',\
 'BILL_AMT5','BILL_AMT6','PAY_AMT1','PAY_AMT2','PAY_AMT3','PAY_AMT4','PAY_AMT5','PAY_AMT6']

target = 'default.payment.next.month'

然后,我们使用 split_frame() 方法分割我们的 DataFrame:

splits = df_creditcarddata.split_frame(ratios=[0.7], seed=1) 

以下代码给出了两个分割输出:

splits

在下面的屏幕截图中,我们可以看到以下两个分割:

图片

我们将分割分为训练和测试子集:

train = splits[0]
test = splits[1] 

如何做到这一点...

让我们继续使用本章前面提到的算法来训练我们的模型。我们将首先训练我们的 广义线性模型GLM)模型。我们将构建三个 GLM 模型:

  • 带有默认参数值的 GLM 模型

  • 带有 Lambda 搜索(正则化)的 GLM 模型

  • 带有网格搜索的 GLM 模型

现在我们将在下一节开始训练我们的模型。

  1. 让我们训练我们的第一个模型:
GLM_default_settings = H2OGeneralizedLinearEstimator(family='binomial', \
                                            model_id='GLM_default',nfolds = 10, \
                                            fold_assignment = "Modulo", \
                                            keep_cross_validation_predictions = True)

H2OGeneralizedLinearEstimator拟合一个广义线性模型。它接受一个响应变量和一组预测变量。

H2OGeneralizedLinearEstimator可以处理回归和分类任务。在回归问题的情况下,它返回一个H2ORegressionModel子类,而对于分类,它返回一个H2OBinomialModel子类。

  1. 我们在“准备就绪”部分创建了预测变量和目标变量。将预测变量和目标变量传递给模型:
GLM_default_settings.train(x = predictors, y = target, training_frame = train)
  1. 使用lambda_search参数训练 GLM 模型:
GLM_regularized = H2OGeneralizedLinearEstimator(family='binomial', model_id='GLM', \
                                                lambda_search=True, nfolds = 10, \
                                                fold_assignment = "Modulo", \
                                                keep_cross_validation_predictions = True)

GLM_regularized.train(x = predictors, y = target, training_frame = train)

lambda_search帮助 GLM 找到最优的正则化参数λ。lambda_search参数接受一个布尔值。当设置为True时,GLM 将首先拟合一个具有最高λ值的模型,这被称为最大正则化。然后它将逐步降低λ值,直到达到最小λ值。得到的最佳模型基于最佳的λ值。

  1. 使用 GLM 和网格搜索训练模型:
hyper_parameters = { 'alpha': [0.001, 0.01, 0.05, 0.1, 1.0],
                     'lambda': [0.001, 0.01, 0.1, 1] }
search_criteria = { 'strategy': "RandomDiscrete", 'seed': 1,
                    'stopping_metric': "AUTO",
                    'stopping_rounds': 5 }

GLM_grid_search = H2OGridSearch(H2OGeneralizedLinearEstimator(family='binomial', \
                  nfolds = 10, fold_assignment = "Modulo", \
                  keep_cross_validation_predictions = True),\
                  hyper_parameters, grid_id="GLM_grid", search_criteria=search_criteria)

GLM_grid_search.train(x= predictors,y= target, training_frame=train)
  1. 使用get_grid()方法按auc值排序网格结果:
# Get the grid results, sorted by validation AUC
GLM_grid_sorted = GLM_grid_search.get_grid(sort_by='auc', decreasing=True)
GLM_grid_sorted

在以下屏幕截图中,我们可以看到每个模型的auc分数,它由不同的alphalambda参数组合而成:

图片

  1. 我们可以看到我们的训练数据和交叉验证数据上的模型指标:
# Extract the best model from random grid search
Best_GLM_model_from_Grid = GLM_grid_sorted.model_ids[0]

# model performance
Best_GLM_model_from_Grid = h2o.get_model(Best_GLM_model_from_Grid)
print(Best_GLM_model_from_Grid)

从前面的代码块中,您可以评估模型指标,包括MSERMSENullResidual DevianceAUCGini以及Confusion Matrix。在稍后的阶段,我们将使用网格搜索中的最佳模型进行我们的堆叠集成。

让我们看一下以下图像并评估模型指标:

图片

  1. 使用随机森林训练模型。使用默认设置的随机森林代码如下:
# Build a RF model with default settings
RF_default_settings = H2ORandomForestEstimator(model_id = 'RF_D',\
                                nfolds = 10, fold_assignment = "Modulo", \
                                keep_cross_validation_predictions = True)

# Use train() to build the model
RF_default_settings.train(x = predictors, y = target, training_frame = train)
  1. 要获取模型的摘要输出,请使用以下代码:
RF_default_settings.summary()
  1. 使用网格搜索训练随机森林模型。将超参数设置如下所示:
hyper_params = {'sample_rate':[0.7, 0.9],
                'col_sample_rate_per_tree': [0.8, 0.9],
                'max_depth': [3, 5, 9],
                'ntrees': [200, 300, 400]
               }
  1. 使用H2OGridSearch()上的超参数使用gridsearch训练RF模型:
RF_grid_search = H2OGridSearch(H2ORandomForestEstimator(nfolds = 10, \
                             fold_assignment = "Modulo", \
                             keep_cross_validation_predictions = True, \
                             stopping_metric = 'AUC',stopping_rounds = 5), \
                             hyper_params = hyper_params, \
                             grid_id= 'RF_gridsearch')

# Use train() to start the grid search
RF_grid_search.train(x = predictors, y = target, training_frame = train)
  1. 按 AUC 分数排序结果以查看哪个模型表现最佳:
# Sort the grid models
RF_grid_sorted = RF_grid_search.get_grid(sort_by='auc', decreasing=True)
print(RF_grid_sorted)
  1. 从网格搜索结果中提取最佳模型:
Best_RF_model_from_Grid = RF_grid_sorted.model_ids[0]

# Model performance
Best_RF_model_from_Grid = h2o.get_model(Best_RF_model_from_Grid) 
print(Best_RF_model_from_Grid)

在以下屏幕截图中,我们可以看到网格模型在训练数据和交叉验证数据上的模型指标:

图片

  1. 使用 GBM 训练模型。以下是使用默认设置训练 GBM 的方法:
GBM_default_settings = H2OGradientBoostingEstimator(model_id = 'GBM_default', \
                       nfolds = 10, \
                       fold_assignment = "Modulo", \
                       keep_cross_validation_predictions = True)

# Use train() to build the model
GBM_default_settings.train(x = predictors, y = target, training_frame = train)
  1. 在 GBM 上使用网格搜索。要执行网格搜索,请设置以下超参数:
hyper_params = {'learn_rate': [0.001,0.01, 0.1],
                'sample_rate': [0.8, 0.9],
                'col_sample_rate': [0.2, 0.5, 1],
                'max_depth': [3, 5, 9]}
  1. 使用H2OGridSearch()上的超参数使用网格搜索训练 GBM 模型:
GBM_grid_search = H2OGridSearch(H2OGradientBoostingEstimator(nfolds = 10, \
                        fold_assignment = "Modulo", \
                        keep_cross_validation_predictions = True,\
                        stopping_metric = 'AUC', stopping_rounds = 5),
                        hyper_params = hyper_params, grid_id= 'GBM_Grid')

# Use train() to start the grid search
GBM_grid_search.train(x = predictors, y = target, training_frame = train)
  1. 与早期模型一样,我们可以按 AUC 排序查看结果:
# Sort and show the grid search results
GBM_grid_sorted = GBM_grid_search.get_grid(sort_by='auc', decreasing=True)
print(GBM_grid_sorted)
  1. 从网格搜索中提取最佳模型:
Best_GBM_model_from_Grid = GBM_grid_sorted.model_ids[0]

Best_GBM_model_from_Grid = h2o.get_model(Best_GBM_model_from_Grid) 
print(Best_GBM_model_from_Grid)

我们可以使用H2OStackedEnsembleEstimator构建一个堆叠集成 ML 模型,该模型可以使用我们使用 H2O 算法构建的模型来提高预测性能。H2OStackedEnsembleEstimator帮助我们找到一组预测算法的最佳组合。

  1. 创建一个列表,包含我们使用网格搜索构建的早期模型的最佳模型:
# list the best models from each grid
all_models = [Best_GLM_model_from_Grid, Best_RF_model_from_Grid, Best_GBM_model_from_Grid]
  1. 使用H2OStackedEnsembleEstimator设置堆叠集成模型:
# Set up Stacked Ensemble
ensemble = H2OStackedEnsembleEstimator(model_id = "ensemble", base_models = all_models, metalearner_algorithm = "deeplearning")

# uses GLM as the default metalearner
ensemble.train(y = target, training_frame = train)
  1. 在测试数据上评估集成性能:
# Eval ensemble performance on the test data
Ens_model = ensemble.model_performance(test)
Ens_AUC = Ens_model.auc()
  1. test数据上比较基学习器的性能。以下代码测试了我们构建的所有 GLM 模型的表现:
# Checking the model performance for all GLM models built
model_perf_GLM_default = GLM_default_settings.model_performance(test)
model_perf_GLM_regularized = GLM_regularized.model_performance(test)
model_perf_Best_GLM_model_from_Grid = Best_GLM_model_from_Grid.model_performance(test)

以下代码测试了我们构建的所有随机森林模型的表现:

# Checking the model performance for all RF models built
model_perf_RF_default_settings = RF_default_settings.model_performance(test)
model_perf_Best_RF_model_from_Grid = Best_RF_model_from_Grid.model_performance(test)

以下代码测试了我们构建的所有 GBM 模型的表现:

# Checking the model performance for all GBM models built
model_perf_GBM_default_settings = GBM_default_settings.model_performance(test)
model_perf_Best_GBM_model_from_Grid = Best_GBM_model_from_Grid.model_performance(test)
  1. 要从基学习器中获得最佳的 AUC,请执行以下命令:
# Best AUC from the base learner models
best_auc = max(model_perf_GLM_default.auc(), model_perf_GLM_regularized.auc(), \
 model_perf_Best_GLM_model_from_Grid.auc(), \
 model_perf_RF_default_settings.auc(), \
 model_perf_Best_RF_model_from_Grid.auc(), \
 model_perf_GBM_default_settings.auc(), \
 model_perf_Best_GBM_model_from_Grid.auc())

print("Best AUC out of all the models performed: ", format(best_auc))
  1. 以下命令显示了堆叠集成模型的 AUC:
# Eval ensemble performance on the test data
Ensemble_model = ensemble.model_performance(test)
Ensemble_model = Ensemble_model.auc()

它是如何工作的...

我们使用 Google Colab 来训练我们的模型。在我们在 Google Colab 中安装 H2O 之后,我们初始化了 H2O 实例。我们还导入了所需的库。

为了使用 H2O 库,我们从h2o.estimators中导入了H2OGeneralizedLinearEstimatorH2ORandomForestEstimatorH2OGradientBoostingEstimator。我们还导入了H2OStackedEnsembleEstimator,以使用堆叠集成训练我们的模型。

我们挂载了 Google Drive 并使用h2o.import_file()读取我们的数据集。这创建了一个 H2O DataFrame,它与pandas DataFrame 非常相似。然而,数据不是存储在内存中,而是在远程 H2O 集群中的一个位置。

然后,我们对 H2O DataFrame 进行了基本操作以分析我们的数据。我们查看维度、前几行以及每列的数据类型。shape属性返回一个包含行数和列数的元组。head()方法返回前 10 个观测值。types属性返回每列的数据类型。

注意,H2O DataFrame 中的分类变量被标记为enum

我们的目标变量是default.payment.next.month。使用table()方法,我们看到了目标变量两个类的分布。在这种情况下,table()方法返回了类10的计数。

我们不需要ID列,因此使用带有axis=1参数的drop()方法将其删除。使用axis=1,它删除了列。否则,axis=0的默认值将删除索引中的标签。

我们分析了数值变量的分布。你可以探索数据到任何程度。我们还通过性别、教育、婚姻等各种类别看到了目标变量两个类的分布。

然后,我们使用asfactor()方法将分类变量转换为因子类型。这也适用于目标变量。

我们创建了一个预测变量列表和目标变量列表。我们使用split_frame()方法将我们的 DataFrame 拆分为训练集和测试集。

我们将比率传递给split_frame()方法。在我们的例子中,我们将数据集拆分为 70%和 30%。然而,请注意,这并没有给出精确的 70%-30%拆分。H2O 使用概率拆分方法而不是使用精确比率来拆分数据集。这是为了使在大数据上的拆分更加高效。

在我们将数据集拆分为训练集和测试集之后,我们开始训练我们的模型。我们使用了 GLM、随机森林、梯度提升机GBM)和堆叠集成来训练堆叠模型。

如何做...部分,在步骤 1步骤 2中,我们展示了使用默认设置训练 GLM 模型的代码。我们使用交叉验证来训练我们的模型。

步骤 3中,我们使用lambda_search训练了一个 GLM 模型,这有助于找到最佳的正则化参数。

步骤 4中,我们使用网格搜索参数来训练我们的 GLM 模型。我们设置了超参数并将它们提供给H2OGridSearch()方法。这有助于我们在模型之间搜索最佳参数。在H2OGridSearch()方法中,我们使用了RandomDiscrete搜索标准策略。

默认的搜索标准策略是笛卡尔,它涵盖了超参数组合的全部空间。随机离散策略执行所有提供的超参数组合的随机搜索。

步骤 5中,通过get_grid()方法,我们查看使用提供的参数的不同组合构建的每个模型的 AUC 分数。在步骤 6中,我们从随机网格搜索中提取了最佳模型。我们还可以在最佳模型上使用print()方法来查看模型在训练数据和交叉验证数据上的性能指标。

步骤 7中,我们使用默认设置训练了一个随机森林模型,并在步骤 8 中查看结果的模型摘要。在步骤 9步骤 10中,我们展示了使用网格搜索训练随机森林模型的代码。我们为各种可接受的超参数设置了多个值,例如sample_ratecol_sample_rate_per_treemax_depthntreessample_rate指的是不重复的行采样。它的值在01之间,表示数据的采样百分比。col_sample_rate_per_tree是每个树的不重复列采样。max_depth用于指定每个树应构建的最大深度。更深的树可能在训练数据上表现更好,但将花费更多计算时间,并且可能过拟合,无法泛化到未见过的数据。ntrees参数用于基于树的算法,用于指定在模型上构建的树的数量。

步骤 11步骤 12中,我们打印了由网格搜索生成的每个模型的 AUC 分数,并从中提取了最佳模型。

我们还训练了 GBM 模型来拟合我们的数据。在第 13 步中,我们使用默认设置构建了 GBM。在第 14 步中,我们为网格搜索设置了超参数空间。我们在第 15 步中使用了它,在那里我们训练了我们的 GBM。在 GBM 中,我们为超参数设置了值,例如learn_ratesample_ratecol_sample_ratemax_depthntreeslearn_rate参数用于指定 GBM 算法训练模型的速度。较低的learn_rate参数值更好,可以帮助避免过拟合,但可能会在计算时间上付出代价。

在 H2O 中,learn_rate在 GBM 和 XGBoost 中可用。

第 16 步显示了网格搜索产生的每个模型的 AUC 分数。我们在第 17 步中提取了最佳的网格搜索 GBM。

在第 18 步到第 20 步中,我们使用 H2O 的H2OStackedEnsembleEstimator训练了我们的堆叠集成模型。我们在测试数据上评估了所得模型的性能。

在第 21 步中,我们评估了我们构建的所有 GLM 模型在测试数据上的表现。我们用 RF 和 GBM 训练的所有模型也做了同样的事情。在第 22 步中,我们得到了 AUC 分数最高的模型。在第 23 步中,我们评估了堆叠集成模型在测试数据上的 AUC 分数,以便比较堆叠集成模型与单个基础学习器的性能。

还有更多...

注意,我们使用了交叉验证来训练所有模型。我们使用nfolds选项来设置用于交叉验证的折数。在我们的例子中,我们使用了nfolds=5,但我们也可以将其设置为更高的数字。

每个构建的模型中折数需要相同。

指定了nfolds的值后,我们还可以为fold_assignment参数提供一个值。fold_assignment可以取autorandommodulostratified等值。如果我们将其设置为Auto,算法将自动选择一个选项;目前,它选择Random。将fold_assignment设置为Random将启用将数据随机分割成nfolds个集合。当fold_assignment设置为Modulo时,它使用确定性方法将数据均匀分割成nfolds,这些nfolds不依赖于seed参数。

当我们使用交叉验证方法构建模型时,确保您为所有模型指定一个seed值或使用fold_assignment="Modulo"

在网格搜索中,我们使用了两个参数:stopping_metricstopping_rounds。这些参数适用于 GBM 和随机森林算法,但不适用于 GLM。stopping_metric指定了在指定提前停止时考虑的指标,这可以通过将stopping_rounds设置为一个大于零的值来实现。

在我们的示例中,我们将stopping_metric设置为 AUC,将stopping_rounds设置为五。这意味着如果 AUC 在指定的轮次(在我们的例子中是五轮)内没有改善,算法将测量 AUC 并在进一步训练之前停止。

如果指定了stopping_metric,则必须设置stopping_rounds。当也设置了stopping_tolerance时,如果模型的stopping_metric没有通过stopping_tolerance值改善,模型将在达到stopping_rounds中提到的轮数后停止训练。

参见

H2O 文档可在docs.h2o.ai/找到。

第十一章:使用 NLP 进行文本分类的异构集成

在本章中,我们将涵盖以下主题:

  • 使用异构算法集成的垃圾邮件过滤

  • 使用集成模型进行电影评论的情感分析

简介

文本分类是语言处理和文本挖掘广泛研究的一个领域。使用文本分类机制,我们可以根据其内容将文档分类到预定义的类别中。

在本章中,我们将探讨如何对发送到我们手机上的短文本消息进行分类。虽然我们收到的某些消息很重要,但其他消息可能对我们的隐私构成严重威胁。我们希望能够正确地分类文本消息,以避免垃圾邮件并避免错过重要消息。

使用异构算法集成的垃圾邮件过滤

我们将使用来自 UCI ML 存储库的 SMS 垃圾邮件收集数据集来创建垃圾邮件分类器。使用垃圾邮件分类器,我们可以估计这些消息的极性。我们可以使用各种分类器将消息分类为垃圾邮件或非垃圾邮件。

在这个例子中,我们选择如朴素贝叶斯、随机森林和支持向量机之类的算法来训练我们的模型。

我们使用各种数据清洗和准备机制来准备我们的数据。为了预处理我们的数据,我们将执行以下序列:

  1. 将所有文本转换为小写

  2. 删除标点符号

  3. 删除停用词

  4. 执行词干提取

  5. 数据分词

我们还使用词频-逆文档频率TF-IDF)处理我们的数据,这告诉我们单词在消息或文档中出现的频率。TF 的计算如下:

TF = 单词在文档中出现的次数 / 文档中单词的总数

TF-IDF 根据单词在文档或文档集合中出现的频率对单词的重要性进行数值评分。简单来说,TF-IDF 分数越高,术语越罕见。分数越低,则越常见。TD-IDF 的数学表示如下:

tfidf(w,d,D)= tf(t,d) × idf(t,D)

其中 w 代表单词,d 代表文档,D 代表文档集合。

在这个例子中,我们将使用 SMS 垃圾邮件收集数据集,该数据集包含用于手机垃圾邮件研究收集的有标签消息。此数据集可在 UCI ML 存储库中找到,并在 GitHub 存储库中提供。

准备工作

我们首先导入所需的库:

import os
import numpy as np
import pandas as pd
import itertools
import warnings
import string
import matplotlib.pyplot as plt
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report
from sklearn.metrics import roc_auc_score as auc
from sklearn.metrics import roc_curve
from sklearn.metrics import accuracy_score
from scipy.stats import mode

注意,在这个例子中,我们导入如nltk之类的库来准备我们的数据。我们还从sklearn.feature_extraction导入CountVectorizerTfidfVectorizer模块。这些模块用于机器学习算法中的特征提取。

我们重用来自 scikit-learn 网站的plot_confusion_matrix来绘制混淆矩阵。这是我们之前章节中使用的相同函数:

def plot_confusion_matrix(cm, classes,
                          normalize=False,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):

    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=45)
    plt.yticks(tick_marks, classes)

    fmt = '.2f' if normalize else 'd'
    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, format(cm[i, j], fmt),
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.ylabel('True label')
    plt.xlabel('Predicted label')
    plt.tight_layout()

我们设置工作目录并读取数据集:

os.chdir("/.../Chapter 11/CS - SMS Classification")
os.getcwd()

df_sms = pd.read_csv("sms_labeled_data.csv", encoding = 'utf8')

注意,我们使用encoding='utf8'。这是为了指导read_csv()方法使用 UTF 编码来读取文件。Python 附带了许多编解码器。完整的列表可在docs.python.org/3/library/codecs.html#standard-encodings找到。

在读取数据后,我们检查它是否已正确加载:

df_sms.head()

我们还使用dataframe.shape检查数据集中的观测值和特征数量:

df_sms.shape

我们查看垃圾邮件和正常邮件的数量:

# Gives the count for ham messages
print(df_sms["type"].value_counts()[0])
no_of_ham_messages = df_sms["type"].value_counts()[0]

# Gives the count for spam messages
print(df_sms["type"].value_counts()[1])
no_of_spam_messages = df_sms["type"].value_counts()[1]

我们还可以可视化垃圾邮件和正常邮件的比例:

sms_count = pd.value_counts(df_sms["type"], sort= True)
ax = sms_count.plot(kind='bar', figsize=(10,10), color= ["green", "orange"], fontsize=13)

ax.set_alpha(0.8)
ax.set_title("Percentage Share of Spam and Ham Messages")
ax.set_ylabel("Count of Spam & Ham messages");
ax.set_yticks([0, 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000, 5500])

totals = []
for i in ax.patches:
totals.append(i.get_height())

total = sum(totals)

# set individual bar lables using above list
for i in ax.patches:
string = str(round((i.get_height()/total)*100, 2))+'%'
# get_x pulls left or right; get_height pushes up or down
ax.text(i.get_x()+0.16, i.get_height(), string, fontsize=13, color='black')

使用前面的代码,我们看到以下图表:

图片

我们还定义了一个函数来删除标点符号,将文本转换为小写,并删除停用词:

lemmatizer = WordNetLemmatizer()

# Defining a function to remove punctuations, convert text to lowercase and remove stop words
def process_text(text):
    no_punctuations = [char for char in text if char not in string.punctuation]
    no_punctuations = ''.join(no_punctuations)

    clean_words = [word.lower() for word in nopunc.split() if word.lower() not in stopwords.words('english')]
    clean_words = [lemmatizer.lemmatize(lem) for lem in clean_words]
    clean_words = " ".join(clean_words)

    return clean_words

我们将定义的process_text()函数应用于数据集中的文本变量:

df_sms['text'] = df_sms['text'].apply(text_processing)

我们将特征和目标变量分开,并将数据分为traintest子集:

X = df_sms.loc[:,'text']
Y = df_sms.loc[:,'type']
Y = Y.astype('int')

X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=.3, random_state=1)

我们使用CountVectorizer模块将文本转换为向量:

count_vectorizer = CountVectorizer(stop_words='english')

count_train = count_vectorizer.fit_transform(X_train)
count_test = count_vectorizer.transform(X_test)

我们还使用TfidfVectorizer模块将文本转换为 TF-IDF 向量:

tfidf = TfidfVectorizer(stop_words='english')

tfidf_train = tfidf.fit_transform(X_train)
tfidf_test = tfidf.transform(X_test)

现在让我们继续训练我们的模型。我们在计数数据和 TF-IDF 数据上使用以下算法,并查看单个模型的表现:

  • 朴素贝叶斯

  • 支持向量机

  • 随机森林

我们还结合模型预测以查看集成结果。

如何做到这一点...

让我们从训练我们的模型开始,看看它们在本节中的表现:

  1. 使用朴素贝叶斯算法训练模型。将此算法应用于计数数据和 TF-IDF 数据。

下面的代码用于在计数数据上训练朴素贝叶斯:

from sklearn.naive_bayes import MultinomialNB
nb = MultinomialNB()

nb.fit(count_train, Y_train)
nb_pred_train = nb.predict(count_train)
nb_pred_test = nb.predict(count_test)
nb_pred_train_proba = nb.predict_proba(count_train)
nb_pred_test_proba = nb.predict_proba(count_test)

print('The accuracy for the training data is {}'.format(nb.score(count_train, Y_train)))
print('The accuracy for the testing data is {}'.format(nb.score(count_test, Y_test)))

查看前面模型的traintest准确率:

图片

  1. 使用classification_report()方法打印分类报告。将Y_testnb_pred_test传递给classification_report()方法:
print(classification_report(Y_test, nb_pred_test))

这给出了以下输出,显示了目标变量中每个类的precisionrecallf1-scoresupport

图片

  1. Y_testnb_pred_test传递给plot_confusion_matrix()函数以绘制混淆矩阵,如下所示:
target_names = ['Spam','Ham']

# Pass actual & predicted values to the confusion matrix()
cm = confusion_matrix(Y_test, nb_pred_test)
plt.figure()
plot_confusion_matrix(cm, classes=target_names)
plt.show()

下面的图表显示了真正的负例、假正例、假负例和真正例的值:

图片

注意,在之前的准备部分中,我们使用了TfidfVectorizer模块将文本转换为 TF-IDF 向量。

  1. 将朴素贝叶斯模型拟合到 TF-IDF 训练数据:
nb.fit(tfidf_train, Y_train)
nb_pred_train_tfidf = nb.predict(tfidf_train)
nb_pred_test_tfidf = nb.predict(tfidf_test)

nb_tfidf_pred_train_proba = nb.predict_proba(tfidf_train)
nb_tfidf_pred_test_proba = nb.predict_proba(tfidf_test)

print('The accuracy for the training data is {}'.format(nb.score(count_train, Y_train)))
print('The accuracy for the testing data is {}'.format(nb.score(count_test, Y_test)))
  1. 检查 TF-IDF 测试数据的性能统计信息:
print(classification_report(Y_test, nb_pred_test_tfidf))

target_names = ['Spam','Ham']

# Pass actual & predicted values to the confusion matrix()
cm = confusion_matrix(Y_test, nb_pred_test_tfidf)
plt.figure()

plot_confusion_matrix(cm, classes=target_names)
plt.show()

在下面的屏幕截图,我们可以看到前面代码块的结果:

图片

  1. 使用支持向量机分类器对计数数据进行模型拟合。使用GridSearchCV在估计器的指定参数值范围内进行搜索:
from sklearn.svm import SVC

svc = SVC(kernel='rbf',probability=True)
svc_params = {'C':[0.001, 0.01, 0.1, 1, 10]}

svc_gcv_rbf_count = GridSearchCV(svc, svc_params, cv=5)
svc_gcv_rbf_count.fit(count_train, Y_train)

# We use the grid model to predict the class 
svc_rbf_train_predicted_values = svc_gcv_rbf_count.predict(count_train)
svc_rbf_test_predicted_values = svc_gcv_rbf_count.predict(count_test)

# We use the grid model to predict the class probabilities
svc_gcv_train_proba_rbf = svc_gcv_rbf_count.predict_proba(count_train)
svc_gcv_test_proba_rbf = svc_gcv_rbf_count.predict_proba(count_test)

print('The best parameters {}'.format(svc_gcv_rbf_count.best_params_))
print('The best score {}'.format(svc_gcv_rbf_count.best_score_))

网格搜索为我们提供了最佳模型。我们可以看到最佳模型的参数值和得分:

图片

  1. 使用以下代码查看计数数据的test准确率:
print(classification_report(Y_test, svc_rbf_test_predicted_values))

target_names = ['Spam','Ham']

# Pass actual & predicted values to the confusion matrix()
cm = confusion_matrix(Y_test, svc_rbf_test_predicted_values)
plt.figure()
plot_confusion_matrix(cm,classes=target_names)
plt.show()

这是classification_report()和混淆矩阵的输出:

图片

  1. 使用 TF-IDF 数据使用 SVM:
svc = SVC(kernel='rbf',probability=True)
svc_params = {'C':[0.001, 0.01, 0.1, 1, 10]}

svc_gcv = GridSearchCV(svc,svc_params,cv=5)
svc_gcv.fit(tfidf_train, Y_train)

# We use the grid model to predict the class 
svc_tfidf_rbf_train_predicted_values = svc_gcv.predict(tfidf_train)
svc_tfidf_rbd_test_predicted_values = svc_gcv.predict(tfidf_test)

# We use the grid model to predict the class probabilities
svc_gcv_tfidf_train_proba_rbf = svc_gcv.predict_proba(tfidf_train)
svc_gcv_tfidf_test_proba_rbf = svc_gcv.predict_proba(tfidf_test)

print('The best parameters {}'.format(svc_gcv.best_params_))
print('The best score {}'.format(svc_gcv.best_score_))

以下输出显示了使用 SVM 和 RBF 核在 TF-IDF 数据上训练的模型的最佳得分:

图片

  1. 打印前一个模型的分类报告和混淆矩阵:

图片

  1. 使用网格搜索交叉验证在计数数据上拟合随机森林模型,就像我们对 SVM 所做的那样:
# Set the parameters for grid search
rf_params = {"criterion":["gini","entropy"],"min_samples_split":[2,3],"max_depth":[None,2,3],"min_samples_leaf":[1,5],"max_leaf_nodes":[None],"oob_score":[True]}

# Create an instance of the Random Forest Classifier()
rf = RandomForestClassifier()

# Use gridsearchCV(), pass the values you have set for grid search
rf_gcv = GridSearchCV(rf, rf_params, cv=5)

# Fit the model onto the train data
rf_gcv.fit(count_train, Y_train)

# We use the grid model to predict the class 
rf_train_predicted_values = rf_gcv.predict(count_train)
rf_test_predicted_values = rf_gcv.predict(count_test)

# We use the grid model to predict the class probabilities
rf_gcv_pred_train_proba = rf_gcv.predict_proba(count_train)
rf_gcv_pred_test_proba = rf_gcv.predict_proba(count_test)

print('The best parameters {}'.format(rf_gcv.best_params_))
print('The best score {}'.format(rf_gcv.best_score_))

网格搜索随机森林的网格参数返回最佳参数和最佳得分,如下截图所示:

图片

  1. 使用分类报告和混淆矩阵,查看我们在测试数据上使用计数数据的随机森林模型的性能指标:
print(classification_report(Y_test, rf_test_predicted_values))

target_names = ['Spam','Ham']

# Pass actual & predicted values to the confusion matrix()
cm = confusion_matrix(Y_test, rf_test_predicted_values)
plt.figure()
plot_confusion_matrix(cm,classes=target_names)
plt.show() 

报告如下所示:

图片

  1. 在 TF-IDF 数据上使用网格搜索在随机森林上构建模型:
# Set the parameters for grid search
rf_params = {"criterion":["gini","entropy"],"min_samples_split":[2,3],"max_depth":[None,2,3],"min_samples_leaf":[1,5],"max_leaf_nodes":[None],"oob_score":[True]}

# Create an instance of the Random Forest Classifier()
rf = RandomForestClassifier()

# Use gridsearchCV(), pass the values you have set for grid search
rf_gcv = GridSearchCV(rf, rf_params, cv=5)

rf_gcv.fit(tfidf_train, Y_train)

rf_tfidf_train_predicted_values = rf_gcv.predict(tfidf_train)
rf_tfidf_test_predicted_values = rf_gcv.predict(tfidf_test)

rf_gcv_tfidf_pred_train_proba = rf_gcv.predict_proba(tfidf_train)
rf_gcv_tfidf_pred_test_proba = rf_gcv.predict_proba(tfidf_test)

print('The best parameters {}'.format(rf_gcv.best_params_))
print('The best score {}'.format(rf_gcv.best_score_))

print(classification_report(Y_test, rf_tfidf_test_predicted_values))

target_names = ['Spam','Ham']
# Pass actual & predicted values to the confusion matrix()
cm = confusion_matrix(Y_test, rf_tfidf_test_predicted_values)
plt.figure()
plot_confusion_matrix(cm, classes=target_names)
plt.show()
  1. predict_proba()方法的输出取出来,收集每个模型的预测概率以绘制 ROC 曲线。完整的代码在代码包中提供。

这里是绘制基于朴素贝叶斯模型在计数数据上的 ROC 曲线的代码示例:

fpr, tpr, thresholds = roc_curve(Y_test, nb_pred_test_proba[:,1])
roc_auc = auc(Y_test,nb_pred_test_proba[:,1])

plt.title('ROC Naive Bayes (Count)')
plt.plot(fpr, tpr, 'b',label='AUC = %0.3f'% roc_auc)
plt.legend(loc='lower right')
plt.plot([0,1],[0,1],'r--')
plt.xlim([-0.1,1.0])
plt.ylim([-0.1,1.01])
plt.ylabel('True Positive Rate')
plt.xlabel('False Positive Rate')

在代码包中提供了完整的代码后,我们可以查看所有模型的 ROC 图并进行比较:

图片

  1. 将所有模型的概率平均值并绘制 ROC 曲线:
plt.subplot(4,3,7)

### Test Count Data
d = (nb_pred_test_proba + svc_gcv_test_proba_rbf + rf_gcv_pred_test_proba)/4

fpr, tpr, thresholds = roc_curve(Y_test,d[:,1])
roc_auc = auc(Y_test,d[:,1])

plt.title('ROC Ensemble (Count)')
plt.plot(fpr, tpr, 'b',label='AUC = %0.3f'% roc_auc)
plt.legend(loc='lower right')
plt.plot([0,1],[0,1],'r--')
plt.xlim([-0.1,1.0])
plt.ylim([-0.1,1.01])
plt.ylabel('True Positive Rate')
plt.xlabel('False Positive Rate')

plt.subplot(4,3,8)

### Test TF-IDF Data
d = (nb_tfidf_pred_test_proba + svc_gcv_tfidf_test_proba_rbf + rf_gcv_tfidf_pred_test_proba)/4

fpr, tpr, thresholds = roc_curve(Y_test,d[:,1])
roc_auc = auc(Y_test,d[:,1])

plt.title('ROC Ensemble (TF-IDF)')
plt.plot(fpr, tpr, 'b',label='AUC = %0.3f'% roc_auc)
plt.legend(loc='lower right')
plt.plot([0,1],[0,1],'r--')
plt.xlim([-0.1,1.0])
plt.ylim([-0.1,1.01])
plt.ylabel('True Positive Rate')
plt.xlabel('False Positive Rate')
#plt.show()

plt.tight_layout(pad=1,rect=(0, 0, 3.5, 4))
plt.show()

我们可以在以下屏幕截图中看到 ROC 和 AUC 得分的平均值:

图片

  1. 检查集成结果的准确性。创建一个包含预测结果的数组,如下所示:
predicted_array = np.array([nb_pred_test_tfidf, svc_tfidf_rbd_test_predicted_values, rf_tfidf_test_predicted_values])

print("Each array is the prediction of the respective models")
print(predicted_array)
  1. 计算各个观测值的预测值的众数,以执行最大投票以获得最终的预测结果:
# Using mode on the array, we get the max vote for each observation
predicted_array = mode(predicted_array)

# Check the array
print(predicted_array)

print("The accuracy for test")
accuracy_score(Y_test, predicted_array[0][0])
  1. 分别绘制在计数数据和 TF-IDF 数据上训练的模型的测试准确率:

图片

它是如何工作的...

准备就绪部分,我们导入了所有必需的库并定义了绘制混淆矩阵的函数。我们使用 UTF8 编码读取我们的数据集。我们检查了数据集中垃圾邮件和正常邮件的比例,并使用CountVectorizerTfidfVectorizer模块将文本转换为向量以及 TF-IDF 向量。

之后,我们使用各种算法构建了多个模型。我们还对计数数据和 TF-IDF 数据都应用了每个算法。

模型需要按照以下顺序构建:

  1. 计数数据上的朴素贝叶斯

  2. TF-IDF 数据上的朴素贝叶斯

  3. 计数数据上的 RBF 核 SVM

  4. 基于 RBF 核的 SVM 在 TF-IDF 数据上

  5. 计数数据的随机森林

  6. TF-IDF 数据的随机森林

基于朴素贝叶斯分类器在机器学习中的文本分类被广泛使用。朴素贝叶斯算法基于特征属于一个类的条件概率。在步骤 1中,我们使用朴素贝叶斯算法在计数数据上构建了我们的第一个模型。在步骤 2中,我们使用classification_report()检查性能指标,以查看precisionrecallf1-scoresupport。在步骤 3中,我们调用plot_confusion_matrix()绘制混淆矩阵。

然后,在步骤 4中,我们在 TF-IDF 数据上构建了朴素贝叶斯模型,并在步骤 5中评估了其性能。在步骤 6步骤 7中,我们使用支持向量机在计数数据上训练模型,使用classification_report的输出评估其性能,并绘制了混淆矩阵。我们使用 RBF 核训练了 SVM 模型。我们还展示了使用GridSearchCV寻找最佳参数的示例。在步骤 8步骤 9中,我们重复了步骤 6步骤 7中的操作,但这次我们在 TF-IDF 数据上训练了 SVM。

步骤 10中,我们使用网格搜索在计数数据上训练了一个随机森林模型。我们将criterion超参数设置为ginientropy。我们还为参数设置了多个值,例如min_samples_splitmax_depthmin_samples_leaf。在步骤 11中,我们评估了模型的表现。

然后,在步骤 12中,我们在 TF-IDF 数据上训练了另一个随机森林模型。使用predict_proba()函数,我们在测试数据上得到了类别概率。我们在步骤 13中使用相同的方法绘制了带有 AUC 分数的 ROC 曲线,标注在每个模型的图表上。这有助于我们比较模型的表现。

步骤 14中,我们平均了从计数和 TF-IDF 数据模型中得到的概率,然后绘制了集成结果的 ROC 曲线。从步骤 15步骤 17,我们绘制了基于计数数据和 TF-IDF 数据构建的每个模型的测试准确率。

使用集成模型对电影评论进行情感分析

情感分析是自然语言处理NLP)中另一个广泛研究的研究领域。它是一个流行的任务,用于分析评论以确定评论者提供的评论的情感。在这个例子中,我们将专注于分析来自互联网电影数据库IMDb)的电影评论数据,并根据其是正面还是负面进行分类。

我们有存储在.txt文件中的电影评论,这些文件被分成了两个文件夹:负面和正面。有 1,000 条正面评论和 1,000 条负面评论。这些文件可以从 GitHub 获取。

我们将这个案例研究分成了两部分:

  • 第一部分是准备数据集。我们将读取提供的 .txt 格式的评论文件,将它们附加起来,根据它们被放入哪个文件夹来标记为正面或负面,并创建一个包含标签和文本的 .csv 文件。

  • 在第二部分,我们将在计数数据和 TF-IDF 数据上构建多个基础学习器。我们将评估基础学习器的性能,然后评估预测的集成。

准备就绪

我们首先导入所需的库:

import os
import glob
import pandas as pd

我们将工作文件夹设置如下:

os.chdir("/.../Chapter 11/CS - IMDB Classification")
os.getcwd()

我们设置路径变量并遍历文件夹中的 .txt 文件。

注意,我们有一个子文件夹 /txt_sentoken/pos,其中包含正面评论的 TXT 文件。同样,我们还有一个子文件夹 /txt_sentoken/neg,其中包含负面评论的 TXT 文件。

正面评论的 TXT 文件被读取,评论被附加到一个数组中。我们使用这个数组创建一个 DataFrame,df_pos

path="/.../Chapter 11/CS - IMDB Classification/txt_sentoken/pos/*.txt"

files = glob.glob(path)
text_pos = []

for p in files:
 file_read = open(p, "r")
 to_append_pos = file_read.read()
 text_pos.append(to_append_pos)
 file_read.close()

df_pos = pd.DataFrame({'text':text_pos,'label':'positive'})
df_pos.head()

使用 head() 方法,我们查看正面评论。

我们还遍历负文件夹中的 TXT 文件来读取负面评论,并将它们附加到一个数组中。我们使用这个数组创建一个 DataFrame,df_neg

path="/Users/Dippies/CODE PACKT - EML/Chapter 11/CS - IMDB Classification/txt_sentoken/neg/*.txt"

files = glob.glob(path)
text_neg = []

for n in files:
    file_read = open(n, "r")
    to_append_neg = file_read.read()
    text_neg.append(to_append_neg)
    file_read.close()

df_neg = pd.DataFrame({'text':text_neg,'label':'negative'})
df_neg.head()

最后,我们使用 concat() 方法将正面和负面的 DataFrame 合并成一个单一的 DataFrame:

df_moviereviews=pd.concat([df_pos, df_neg])

我们可以使用 head()tail() 方法查看准备好的 DataFrame:

print(df_moviereviews.head())
print(df_moviereviews.tail())

之前的代码给出了以下输出:

从前面的图像中,我们注意到正面和负面评论是顺序添加的。DataFrame 的前半部分包含正面评论,而后半部分包含负面评论。

让我们打乱数据,使其不保持顺序:

from sklearn.utils import shuffle

df_moviereviews=shuffle(df_moviereviews)
df_moviereviews.head(10)

现在,我们可以看到 DataFrame 中的数据已经被打乱了:

我们验证合并后的 DataFrame 的维度,看看它是否包含 2,000 个观测值,这将是我们将 1,000 个负面评论和 1,000 个正面评论合并的结果:

df_moviereviews.shape

从之前的代码中,我们注意到我们有 2,000 个观测值和 2 列。

我们还可以将结果 DataFrame 写入另一个 .csv 文件,以避免像之前步骤那样从 TXT 文件重新创建 CSV 文件:

df_moviereviews.to_csv("/.../Chapter 11/CS - IMDB Classification/Data_IMDB.csv") 

接下来,我们将定义之前使用过的 plot_confusion_matrix() 方法。

现在,我们可以看到我们数据中正面和负面评论的占比。在我们的案例中,比例正好是 50:50:

df_moviereviews["label"].value_counts().plot(kind='pie')
plt.tight_layout(pad=1,rect=(0, 0, 0.7, 1))

plt.text(x=-0.9,y=0.1, \
         s=(np.round(((df_moviereviews["label"].\
                       value_counts()[0])/(df_moviereviews["label"].value_counts()[0] + \
                       df_moviereviews["label"].value_counts()[1])),2)))

plt.text(x=0.4,y=-0.3, \
         s=(np.round(((df_moviereviews["label"].\
                       value_counts()[1])/(df_moviereviews["label"].value_counts()[0] + \
                       df_moviereviews["label"].value_counts()[1])),2)))

plt.title("% Share of the Positive and Negative reviews in the dataset")

之前代码的输出可以在以下屏幕截图中看到:

现在,我们将“正面”标签替换为“1”,“负面”标签替换为“0”:

df_moviereviews.loc[df_moviereviews["label"]=='positive',"label",]=1
df_moviereviews.loc[df_moviereviews["label"]=='negative',"label",]=0

我们使用各种数据清理和准备机制来准备我们的数据。我们将遵循与之前配方中相同的顺序来预处理我们的数据:

  1. 将所有文本转换为小写

  2. 删除标点符号

  3. 删除停用词

  4. 执行词干提取

  5. 分词数据

接下来,我们将定义一个函数来执行前面的清理步骤:

lemmatizer = WordNetLemmatizer()
def process_text(text):
    nopunc = [char for char in text if char not in string.punctuation]
    nopunc = ''.join(nopunc)

    clean_words = [word.lower() for word in nopunc.split() if word.lower() not in stopwords.words('english')]
    clean_words = [lemmatizer.lemmatize(lem) for lem in clean_words]
    clean_words = " ".join(clean_words)

    return clean_words

我们调用前面的函数来处理我们的文本数据:

df_moviereviews['text'] = df_moviereviews['text'].apply(process_text)

我们现在将构建我们的基础学习器并评估集成结果。

如何操作...

我们首先导入我们需要的剩余库:

  1. 导入所需的库:
import os
import numpy as np
import pandas as pd
import itertools
import warnings
import string
import matplotlib.pyplot as plt
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report
from sklearn.metrics import roc_auc_score as auc
from sklearn.metrics import roc_curve
from sklearn.metrics import accuracy_score
from scipy.stats import mode
  1. 分离目标变量和预测变量:
X = df_moviereviews.loc[:,'text']
Y = df_moviereviews.loc[:,'label']
Y = Y.astype('int')
  1. 执行数据的训练集-测试集划分:
X_train,X_test,y_train,y_test = train_test_split(X, Y, test_size=.3, random_state=1)
  1. 使用CountVectorizer()将文本转换为向量:
count_vectorizer = CountVectorizer()
count_train = count_vectorizer.fit_transform(X_train)
count_test = count_vectorizer.transform(X_test)
  1. 使用TfidfVectorizer()将文本转换为 TF-IDF 向量:
tfidf = TfidfVectorizer()
tfidf_train = tfidf.fit_transform(X_train)
tfidf_test = tfidf.transform(X_test)

我们继续在计数数据和 TF-IDF 数据上训练基础学习器。我们使用随机森林模型、朴素贝叶斯模型和支持向量分类器模型训练基础学习器。

  1. 使用网格搜索在计数数据上训练随机森林模型:
# Set the parameters for grid search
rf_params = {"criterion":["gini","entropy"],\
             "min_samples_split":[2,3],\
             "max_depth":[None,2,3],\
             "min_samples_leaf":[1,5],\
             "max_leaf_nodes":[None],\
             "oob_score":[True]}

# Create an instance of the RandomForestClassifier()
rf = RandomForestClassifier()
warnings.filterwarnings("ignore")

# Use gridsearchCV(), pass the values you have set for grid search
rf_count = GridSearchCV(rf, rf_params, cv=5)

rf_count.fit(count_train, Y_train)

# Predict class predictions & class probabilities with test data
rf_count_predicted_values = rf_count.predict(count_test)
rf_count_probabilities = rf_count.predict_proba(count_test)

rf_count_train_accuracy = rf_count.score(count_train, Y_train)
rf_count_test_accuracy = rf_count.score(count_test, Y_test)

print('The accuracy for the training data is {}'.\
      format(rf_count_train_accuracy))

print('The accuracy for the testing data is {}'.\
      format(rf_count_test_accuracy))
  1. 评估precisionrecallf1-scoresupportaccuracy
print(classification_report(Y_test, rf_count_predicted_values))

# Pass actual & predicted values to the confusion_matrix()
cm = confusion_matrix(Y_test, rf_count_predicted_values)
plt.figure()
plot_confusion_matrix(cm, classes=target_names,normalize=False)
plt.show()

在以下屏幕截图中,我们可以看到前面代码的输出:

图片

  1. 使用网格搜索在 TF-IDF 数据上训练随机森林模型:
# Set the parameters for grid search
rf_params = {"criterion":["gini","entropy"],"min_samples_split":[2,3],"max_depth":[None,2,3],"min_samples_leaf":[1,5],"max_leaf_nodes":[None],"oob_score":[True]}

# Create an instance of the RandomForestClassifier()
rf = RandomForestClassifier()
warnings.filterwarnings("ignore")

# Use gridsearchCV(), pass the values you have set for grid search
rf_tfidf = GridSearchCV(rf, rf_params, cv=5)

rf_tfidf.fit(tfidf_train, Y_train)
  1. 评估模型的性能:
rf_tfidf_predicted_values = rf_tfidf.predict(tfidf_test)
rf_tfidf_probabilities = rf_tfidf.predict_proba(tfidf_test)

rf_train_accuracy = rf_tfidf.score(tfidf_train, Y_train)
rf_test_accuracy = rf_tfidf.score(tfidf_test, Y_test)

print('The accuracy for the training data is {}'.format(rf_train_accuracy))
print('The accuracy for the testing data is {}'.format(rf_test_accuracy))

print(classification_report(Y_test, rf_tfidf_predicted_values))

# Pass actual & predicted values to the confusion_matrix()
cm = confusion_matrix(Y_test, rf_tfidf_predicted_values)
plt.figure()
plot_confusion_matrix(cm, classes=target_names,normalize=False)
plt.show()
  1. 在计数数据上训练朴素贝叶斯模型,并检查测试数据的准确率:
nb_count = MultinomialNB()
nb_count.fit(count_train, Y_train)

nb_count_predicted_values = nb_count.predict(count_test)
nb_count_probabilities = nb_count.predict_proba(count_test)

nb_train_accuracy = nb_count.score(count_train, Y_train)
nb_test_accuracy = nb_count.score(count_test, Y_test)

print('The accuracy for the training data is {}'.format(nb_train_accuracy))
print('The accuracy for the testing data is {}'.format(nb_test_accuracy))
  1. 使用classification_report()和混淆矩阵评估其他模型的性能参数:
print(classification_report(Y_test, nb_predicted_values))

# Pass actual & predicted values to the confusion matrix()
cm = confusion_matrix(Y_test, nb_predicted_values)
plt.figure()
plot_confusion_matrix(cm, classes=target_names,normalize=False)
plt.show()
  1. 在 TF-IDF 数据上训练朴素贝叶斯模型,并按我们之前对早期模型所做的方式评估其性能:
nb_tfidf = MultinomialNB()
nb_tfidf.fit(count_train, Y_train)

nb_tfidf_predicted_values = nb_tfidf.predict(tfidf_test)
nb_tfidf_probabilities = nb_tfidf.predict_proba(tfidf_test)

nb_train_accuracy = nb_tfidf.score(tfidf_train, Y_train)
nb_test_accuracy = nb_tfidf.score(tfidf_test, Y_test)

print('The accuracy for the training data is {}'.format(nb_train_accuracy))
print('The accuracy for the testing data is {}'.format(nb_test_accuracy))

print(classification_report(Y_test, nb_predicted_values))

#Pass actual & predicted values to the confusion matrix()
cm = confusion_matrix(Y_test, nb_predicted_values)
plt.figure()
plot_confusion_matrix(cm, classes=target_names,normalize=False)
plt.show()
  1. 在计数数据上使用具有线性核的支持向量分类器算法训练模型。我们还对 SVC 的C参数进行了网格搜索:
svc_count = SVC(kernel='linear',probability=True)
svc_params = {'C':[0.001, 0.01, 0.1, 1, 10]}

svc_gcv_count = GridSearchCV(svc_count, svc_params, cv=5)
svc_gcv_count.fit(count_train, Y_train)

svc_count_predicted_values = svc_gcv_count.predict(count_test)
svc_count_probabilities = svc_gcv_count.predict_proba(count_test)

svc_count_train_accuracy = svc_gcv_count.score(count_train, Y_train)
svc_count_test_accuracy = svc_gcv_count.score(count_test, Y_test)

print('The accuracy for the training data is {}'.format(svc_gcv_count.score(count_train, Y_train)))
print('The accuracy for the testing data is {}'.format(svc_gcv_count.score(count_test, Y_test)))

print(classification_report(Y_test, svc_count_predicted_values))
# Pass actual & predicted values to the confusion_matrix()
cm = confusion_matrix(Y_test, svc_count_predicted_values)
plt.figure()
plot_confusion_matrix(cm, classes=target_names,normalize=False)
plt.show()
  1. 在 TF-IDF 数据上使用具有线性核的支持向量分类器算法训练模型。我们还对 SVC 的C参数进行了网格搜索:
svc_tfidf = SVC(kernel='linear',probability=True)
svc_params = {'C':[0.001, 0.01, 0.1, 1, 10]}

svc_gcv_tfidf = GridSearchCV(svc_tfidf, svc_params, cv=5)
svc_gcv_tfidf.fit(tfidf_train, Y_train)

svc_tfidf_predicted_values = svc_gcv_tfidf.predict(tfidf_test)
svc_tfidf_probabilities = svc_gcv_tfidf.predict_proba(tfidf_test)

svc_tfidf_train_accuracy = svc_gcv_count.score(tfidf_train, Y_train)
svc_tfidf_test_accuracy = svc_gcv_count.score(tfidf_test, Y_test)

print('The accuracy for the training data is {}'.format(svc_gcv_tfidf.score(count_train, Y_train)))
print('The accuracy for the testing data is {}'.format(svc_gcv_tfidf.score(count_test, Y_test)))

print(classification_report(Y_test, svc_tfidf_predicted_values))
# Pass actual & predicted values to the confusion_matrix()
cm = confusion_matrix(Y_test, svc_tfidf_predicted_values)
plt.figure()
plot_confusion_matrix(cm, classes=target_names)
plt.show()
  1. 绘制每个模型的 ROC 曲线。以下是一个绘图代码示例(完整代码包含在本书的代码包中):
fpr, tpr, thresholds = roc_curve(Y_test, rf_count_probabilities[:,1])
roc_auc = auc(Y_test, rf_count_probabilities[:,1])

plt.title('ROC Random Forest Count Data')
plt.plot(fpr, tpr, 'b',label='AUC = %0.3f'% roc_auc)
plt.legend(loc='lower right')
plt.plot([0,1],[0,1],'r--')
plt.xlim([-0.1,1.0])
plt.ylim([-0.1,1.01])
plt.ylabel('True Positive Rate')
plt.xlabel('False Positive Rate')

在以下屏幕截图中,我们可以比较我们已训练的所有模型的 ROC 曲线:

图片

  1. 在计数和 TF-IDF 数据上绘制集成结果的 ROC 曲线:

图片

  1. 使用最大投票计算集成模型的准确率:
predicted_values_count = np.array([rf_count_predicted_values, \
                                   nb_count_predicted_values, \
                                   svc_count_predicted_values])

predicted_values_tfidf = np.array([rf_tfidf_predicted_values, \
                                   nb_tfidf_predicted_values, \
                                   svc_tfidf_predicted_values])

predicted_values_count = mode(predicted_values_count)
predicted_values_tfidf = mode(predicted_values_tfidf)
  1. 绘制在计数数据和 TF-IDF 数据上训练的每个模型的测试准确率:
count = np.array([rf_count_test_accuracy,\
                  nb_count_test_accuracy,\
                  svc_count_test_accuracy,\
                  accuracy_score(Y_test, predicted_values_count[0][0])])

tfidf = np.array([rf_tfidf_test_accuracy,\
                  nb_tfidf_test_accuracy,\
                  svc_tfidf_test_accuracy,\
                  accuracy_score(Y_test, predicted_values_tfidf[0][0])])

label_list = ["Random Forest", "Naive_Bayes", "SVM_Linear", "Ensemble"] 
plt.plot(count)
plt.plot(tfidf)
plt.xticks([0,1,2,3],label_list)

for i in range(4):
    plt.text(x=i,y=(count[i]+0.001), s=np.round(count[i],4))

for i in range(4):
    plt.text(x=i,y=tfidf[i]-0.003, s=np.round(tfidf[i],4))

plt.legend(["Count","TFIDF"])
plt.title("Test accuracy")

plt.tight_layout(pad=1,rect=(0, 0, 2.5, 2))
plt.show()

以下图表显示了所有模型和集成结果在计数数据和 TF-IDF 数据上的准确率比较:

图片

它是如何工作的...

我们首先导入所需的库。在本章中,我们使用了一个名为glob的模块。glob模块用于定义匹配指定模式到路径、目录和文件名的技术。我们使用 glob 模块来查找指定路径中的所有文件。之后,我们使用open()方法以读取模式打开每个文件。我们读取每个文件,并将其追加到形成包含所有评论的评论数据集。我们还创建了一个标签列,用于标记每个评论为正面或负面标签。

然而,在我们添加了所有正面和负面评论之后,我们注意到它们是按顺序添加的,这意味着前半部分包含了所有正面评论,而后半部分包含了负面评论。我们使用shuffle()方法对这些数据进行随机排序。

我们通过将其转换为小写、删除标点符号和停用词、执行词干提取和分词来清理数据,以创建特征向量。

如何做...部分,我们首先在第 1 步中导入了库。在第 2 步中,我们将目标变量和特征变量分别分离到XY

我们在第 3 步中将数据分为训练集和测试集。我们使用test_size=.3将数据分为训练集和测试集。

第 4 步第 5 步中,我们使用了CountVectorizer()TfidfVectorizer()将文本转换为向量,并将文本转换为 TF-IDF 向量。注意,使用CountVectorizer(),我们生成了count_traincount_test数据集。使用TfidfVectorizer(),我们生成了tfidf_traintfidf_test数据集。

第 6 步中,我们设置了网格搜索的超参数来训练一个随机森林模型。我们在计数数据上训练了我们的随机森林模型,并检查了我们的训练和测试准确率。

我们对构建的所有模型在测试数据上使用了predict()predict_proba()方法来预测类别以及类别概率。

第 7 步中,我们生成了混淆矩阵来评估我们在前一步构建的随机森林模型的表现。在第 8 步第 9 步中,我们在 TF-IDF 数据上对另一个随机森林模型进行了重复训练,并评估了其性能。从第 10 步第 12 步,我们在计数数据和 TF-IDF 数据上训练了朴素贝叶斯模型。

第 13 步第 14 步中,我们分别使用线性核在计数数据和 TF-IDF 数据上训练了支持向量机分类算法。在第 15 步中,我们为构建的每个基本学习器绘制了 ROC 曲线和 AUC 分数。在第 16 步中,我们还绘制了集成模型的 RUC 曲线,以比较与基本学习器的性能。最后,在第 17 步中,我们绘制了每个模型在计数和 TF-IDF 数据上的测试准确率。

还有更多...

在当今世界,文本信息的可用性和流动是无限的。这意味着我们需要各种技术来处理这些文本问题,以提取有意义的信息。例如,词性标注(POS)是自然语言处理(NLP)空间中的基本任务之一。词性标注用于将文本中的单词标注为它们各自的词性。这些标签可以用于更复杂的任务,如句法和语义解析、机器翻译(MT)和问答。

有八个主要词性:

  • 名词

  • 代词

  • 形容词

  • 动词

  • 副词

  • 介词

  • 连词

  • 呼语:

图片

NLTK 库有在分词后应用于文本的获取词性标注的功能。让我们导入所需的库:

import os
import pandas as pd
import nltk
from nltk.tag import pos_tag
from nltk.corpus import stopwords

我们使用之前创建的 DataFrame df_moviereviews。我们将文本转换为小写:

df_moviereviews['text'] =df_moviereviews['text'].apply(lambda x: " ".join(x.lower() for x in x.split()))
df_moviereviews['text'].head()

我们通过去除停用词、标点符号、词形还原和分词来预处理文本:

from nltk.stem.wordnet import WordNetLemmatizer
import string
stop = set(stopwords.words('english'))
exclude = set(string.punctuation) 
lemma = WordNetLemmatizer()
def clean(doc):
    stop_free = " ".join([i for i in doc.lower().split() if i not in stop])
    stop_free = ''.join(ch for ch in stop_free if ch not in exclude)
    normalized = " ".join(lemma.lemmatize(word) for word in stop_free.split())
    return normalized

tokenized_sent = [clean(doc).split() for doc in df_moviereviews["text"]]

我们来看一下第一篇电影评论的前 10 个标记词列表:

tokenized_sent[0][0:10]

这生成了以下输出:

我们进行词性标注:

postag=[nltk.pos_tag(token) for token in tokenized_sent]

我们打印出第一篇电影评论的前 10 个词性标注:

postag[0][0:10]

我们看到词性标注的单词:

分块处理是另一个可以增加词性标注结构性的过程。分块处理用于实体检测;它将多个标记词标记为有意义的实体。有多种分块器可供选择;NLTK 提供了 ne_chunk,它可以识别人名(姓名)、地点和组织。其他常用的分块器包括 OpenNLPYamchaLingpipe。还可以使用分块器的组合,并对结果应用最大投票法来提高分类性能。

第十二章:使用 Keras 进行多类分类的同质集成

在本章中,我们将介绍以下配方:

  • 一个用于分类时尚产品的同质模型集成

引言

在分类问题中,许多研究已经进行了,以找出如何获得更好的分类准确率。当有大量类别需要预测时,这个问题往往更加复杂。在多类分类的情况下,假设目标变量中的每个类别都是相互独立的。多类分类技术涉及训练一个或多个模型来分类目标变量,该变量可以超过两个类别。

一个用于分类时尚产品的同质模型集成

在这个例子中,我们将使用 Fashion-MNIST 数据集。这个数据集包含来自十个类别的 60,000 个时尚产品图像。目标变量可以被分类为十个类别:

  • T 恤/上衣

  • 裤子

  • 开衫

  • 服装

  • 外套

  • 凉鞋

  • 衬衫

  • 运动鞋

  • 靴子

每个图像都是一个 28 x 28 的灰度图像。我们将通过读取数据,在几次迭代中构建几个同质模型,以查看集成是否可以提供更高的准确率。

准备工作

我们将使用 Google Colab 来训练我们的模型。Google Colab 自带 TensorFlow,所以我们不需要在我们的系统中单独安装它。

我们按照以下方式导入所需的库:

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

import tensorflow as tf
from tensorflow import keras
from sklearn.utils import resample
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report
from scipy import stats

我们从tf.keras附带的数据集中加载数据:

# Load the fashion-mnist pre-shuffled train data and test data
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()

我们检查训练集和测试集的维度:

# Print training set shape 
print("x_train shape:", x_train.shape, "y_train shape:", y_train.shape)

这给我们以下输出:

图片

我们记录目标变量中的唯一值:

np.unique(y_train)

我们可以看到有 10 个类别被标记为 0 到 9:

图片

我们可以快速查看前几个观察结果如下:

fig=plt.figure(figsize=(16,8))

# number of columns for images in plot
columns=5 

# number of rows for images in plot
rows=3

for i in range (1,columns*rows+1):
      fig.add_subplot(rows,columns,i)
      plt.title("Actual Class: {}".\
              format((y_train[i])),color='r',fontsize=16)
      plt.imshow(x_train[i])
plt.show()

使用前面的代码,我们绘制了前 15 个图像及其相关标签:

图片

如何做...

我们现在将进入训练模型阶段:

  1. 在下面的代码块中,我们将使用tf.keras在几个迭代中创建多个同质模型:
accuracy = pd.DataFrame( columns=["Accuracy","Precision","Recall"])
predictions = np.zeros(shape=(10000,7))
row_index = 0
for i in range(7):
        # bootstrap sampling 
        boot_train = resample(x_train,y_train,replace=True, n_samples=40000, random_state=None)
        model = tf.keras.Sequential([
            tf.keras.layers.Flatten(input_shape=(28, 28)),
            tf.keras.layers.Dense(256, activation=tf.nn.relu),
            tf.keras.layers.Dense(128, activation=tf.nn.relu),
            tf.keras.layers.Dense(128, activation=tf.nn.relu),
            tf.keras.layers.Dense(128, activation=tf.nn.relu),
            tf.keras.layers.Dense(128, activation=tf.nn.relu),
            tf.keras.layers.Dense(128, activation=tf.nn.relu),
            tf.keras.layers.Dense(128, activation=tf.nn.relu),
            tf.keras.layers.Dense(128, activation=tf.nn.relu),
            tf.keras.layers.Dense(128, activation=tf.nn.relu),
            tf.keras.layers.Dense(128, activation=tf.nn.relu),
            tf.keras.layers.Dense(10, activation=tf.nn.softmax)])

        # compile the model
        model.compile(loss='sparse_categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

        # Train the model
        model.fit(x_train,y_train,epochs=10,batch_size=64)

        # Evaluate accuracy
        score = model.evaluate(x_test, y_test, batch_size=64)
        accuracy.loc[row_index,"Accuracy"]=score[1]

        # Make predictions
        model_pred= model.predict(x_test)
        pred_classes =model_pred.argmax(axis=-1)
        accuracy.loc[row_index, 'Precision'] = precision_score(y_test, pred_classes, average='weighted')
        accuracy.loc[row_index, 'Recall'] = recall_score(y_test, pred_classes,average='weighted')

        # Save predictions to predictions array
        predictions[:,i] = pred_classes

        print(score)
        row_index+=1

        print("Iteration " + str(i+1)+ " Accuracy : " + "{0}".format(score[1]))

我们在每个迭代中提到七个迭代和十个时期。在下面的屏幕截图中,我们可以看到随着模型训练的进展:

图片

  1. 步骤 1的代码中,我们收集了每个迭代在测试数据上的准确率、精确率和召回率:
accuracy

在下面的屏幕截图中,我们可以看到前三个指标在每个迭代中的变化:

图片

  1. 我们将创建一个包含每个迭代中所有模型返回的预测的 DataFrame:
# Create dataframe using prediction of each iteration
df_iteration = pd.DataFrame([predictions[:,0],\
                           predictions[:,1],\
                           predictions[:,2],\
                           predictions[:,3],\
                           predictions[:,4],\
                           predictions[:,5],\
                           predictions[:,6]])
  1. 我们将类型转换为整数:
df_iteration = df_iteration.astype('int64')
  1. 我们执行最大投票来识别每个观察值预测的最可能类别。我们简单地使用mode来找出对于每个观察值预测次数最多的类别:
# find the mode for result
mode = stats.mode(df_iteration)
  1. 我们计算测试数据的准确率:
# calculate the accuracy for test dataset
print(accuracy_score( y_test, mode[0].T))
  1. 我们使用所需的标签生成混淆矩阵:
# confusion matrix
cm = confusion_matrix(y_test, mode[0].T, labels=[0, 1, 2, 3, 4, 5, 6, 7, 8])
  1. 我们绘制了混淆矩阵:
ax= plt.subplot()

# annot=True to annotate cells
sns.heatmap(cm, annot=True, ax = ax, fmt='g', cmap='Blues')

混淆矩阵的图表如下所示:

  1. 我们创建了一个包含所有迭代编号的 DataFrame:
accuracy["Models"]=["Model 1",\
                   "Model 2",\
                   "Model 3",\
                   "Model 4",\
                   "Model 5",\
                   "Model 6",\
                   "Model 7"]
  1. 然后,我们将准确率、精确率和召回率合并到一个单独的表中:
accuracy=accuracy.append(pd.DataFrame([[\
                                        accuracy_score(y_test,\
                                        mode[0].T),0,0,\
                                        "Ensemble Model"]], \
                                        columns=["Accuracy",\
                                        "Precision","Recall",\
                                        "Models"]))

accuracy.index=range(accuracy.shape[0])

accuracy.set_value(7, 'Precision', precision_score(y_test, mode[0].T, average='micro'))
accuracy.set_value(7, 'Recall', recall_score(y_test, mode[0].T, average='micro'))

在下面的屏幕截图中,我们可以看到包含每个模型和集成模型指标的结构的截图:

  1. 我们绘制了每个迭代的准确率和最大投票的准确率:
plt.figure(figsize=(20,8))
plt.plot(accuracy.Models,accuracy.Accuracy)
plt.title("Accuracy across all Iterations and Ensemble")
plt.ylabel("Accuracy")
plt.show()

这给我们以下图表。我们注意到与单个模型相比,最大投票方法返回的准确率是最高的:

  1. 我们还绘制了每个模型和集成模型的精确率和召回率:
plt.figure(figsize=(20,8))
plt.plot(accuracy.Models,accuracy.Accuracy,accuracy.Models,accuracy.Precision)
plt.title("Metrics across all Iterations and models")
plt.legend(["Accuracy","Precision"])
plt.show()

这在下面的屏幕截图中显示:

从前面的屏幕截图中,我们注意到集成模型的精确率和召回率有所提高。

它是如何工作的...

准备就绪部分,我们导入了所需的库。请注意,我们已经导入了TensorFlow库。我们可以通过导入tf.keras.datasets模块直接访问数据集。此模块包含各种内置数据集,包括以下内容:

  • boston_housing:波士顿房价回归数据集

  • cifar10:CIFAR10 小型图像分类数据集

  • fashion_mnist:Fashion-MNIST 数据集

  • imdb:IMDB 情感分类数据集

  • mnist:MNIST 手写数字数据集

  • reuters:路透社主题分类数据集

我们使用了此模块中的fashion_mnist数据集。我们加载了预洗牌的培训和测试数据,并检查了培训和测试子集的形状。

准备就绪部分,我们注意到训练子集的形状是(60000,28,28),这意味着我们有 60000 个大小为 28 X 28 像素的图像。

我们使用unique()方法检查目标变量的不同级别。我们看到从 0 到 9 共有 10 个类别。

我们还快速浏览了一些图像。我们定义了我们所需的列数和行数。运行迭代后,我们使用matplotlib.pyplot.imshow()以灰度形式绘制了图像。我们还使用matplotlib.pyplot.title()打印了每个图像的实际类别标签。

如何做...部分的步骤 1中,我们使用tf.keras模块创建了多个同质模型。在每个迭代中,我们使用resample()方法创建自助样本。我们将replace=True传递给resample()方法,以确保我们有带替换的样本。

在此步骤中,我们还定义了模型架构。我们使用tf.keras.layers向模型中添加层。在每个层中,我们定义了单元数。

“模型架构”指的是整个神经网络结构,它包括称为层的单元组。这些层以链状结构排列。每一层是其前一层的函数。确定模型架构是神经网络的关键。

在我们的例子中,我们进行了一些迭代。我们设置了迭代次数。在每次迭代中,我们编译模型并将其拟合到我们的训练数据上。我们在测试数据上进行了预测,并在 DataFrame 中捕获以下指标:

  • 准确度

  • 精确度

  • 召回率

我们将Rectified Linear Units (RELU)作为隐藏层的激活函数。ReLU 表示为f(x) = max{0, x}。在神经网络中,ReLU 被推荐为默认的激活函数。

注意,在模型架构的最后一层,我们使用了 softmax 作为激活函数。softmax 函数可以看作是 sigmoid 函数的推广。虽然 sigmoid 函数用于表示二元变量的概率分布,但 softmax 函数用于表示具有两个以上类别的目标变量的概率分布。当 softmax 函数用于多类分类时,它为每个类别返回一个介于 0 和 1 之间的概率值。所有概率值的总和将等于 1。

步骤 2中,我们检查了我们在步骤 1中创建的准确度 DataFrame 的结构。我们注意到我们有三个列用于准确度、精确度和召回率,以及每个迭代的指标都被捕获。在步骤 3中,我们将 DataFrame 中的数据类型转换为整数。

步骤 4中,我们使用stats.mode()对每个观测值进行了最大投票。由于我们进行了七次迭代,因此每个观测值都有七个预测。stats.mode()返回了出现次数最多的预测。

步骤 5中,我们检查了使用最大投票预测的模型的准确度。在步骤 6步骤 7中,我们生成了混淆矩阵来可视化正确的预测。图表中的对角线元素是正确的预测,而离对角线元素是误分类。我们发现正确的分类数量比误分类的数量要多。

步骤 8步骤 9中,我们继续创建一个结构来保存性能指标(准确度、精确度和召回率),以及每个迭代和集成标签。我们使用这个结构来绘制性能指标的图表。

步骤 10中,我们绘制了每个迭代和最大投票预测的准确度。同样,在步骤 11中,我们绘制了每个迭代和最大投票预测的精确度和召回率。

从我们在步骤 10步骤 11中生成的图表中,我们注意到最大投票预测的准确度、精确度和召回率都有所提高。

参见

tf.keras 模块为我们提供了 TensorFlow 特定的功能,例如即时执行、数据管道和估计器。您可以查看tf.keras模块为我们提供的各种选项。

在我们的示例中,我们使用了tf.keras.optimizer模块提供的内置优化器类。在我们的示例中,我们使用了Adam 优化器,但您还可以使用其他优化器,例如 Adadelta、Adagrad、Adamax、RMSprop 或 SGD。

在当今时代,Adam 优化器是最佳优化器之一。它是随机梯度下降SGD)的扩展。SGD 考虑所有权重更新使用单个学习率,并且在模型训练过程中学习率保持不变。Adam 算法考虑自适应学习率方法来计算每个参数的单独学习率。

tf.keras.losses 模块为我们提供了各种选项,以便我们可以选择我们的损失函数。我们使用了sparse_categorical_crossentropy。根据您的任务,您可能需要选择其他选项,例如binary_crossentropycategorical_crossentropymean_squared_error等等。

在多类分类的情况下,如果目标变量是独热编码的,请使用categorical_crossentropy。如果目标变量中的类别表示为整数,请使用sparse_categorical_crossentropy

您可以在www.tensorflow.org/api_docs/python/tf/keras上获取有关可用于tf.keras的其他超参数的更详细信息。

posted @ 2025-09-03 10:21  绝不原创的飞龙  阅读(26)  评论(0)    收藏  举报