Python-机器学习秘籍第二版-全-

Python 机器学习秘籍第二版(全)

原文:annas-archive.org/md5/1a114450f966ee5154c07d3ee2c9ce43

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

这本备受期待的《Python 机器学习食谱,第二版》的第二版将使您能够采用一种全新的方法来处理现实世界的机器学习和深度学习任务。

通过超过 100 个食谱的帮助,您将学习如何使用 Python 生态系统中的现代库构建强大的机器学习应用程序。本书还将指导您如何使用基于食谱的方法实现各种机器学习算法,包括分类、聚类和推荐引擎。本书强调实用解决方案,书中专门的部分将帮助您将监督学习和无监督学习技术应用于现实世界问题。在结论章节中,您将掌握包括强化学习、深度神经网络和自动化机器学习在内的领域的先进技术食谱。

通过本书的学习,您将通过现实世界的例子掌握应用机器学习技术的技能,并能够充分利用 Python 生态系统的全部功能。

本书面向对象

本书面向数据科学家、机器学习开发者、深度学习爱好者以及希望使用机器学习技术和算法解决现实世界挑战的 Python 程序员。如果您在工作中面临挑战,并希望获得现成的代码解决方案来覆盖机器学习和深度学习领域的关键任务,那么这本书正是您所需要的。

本书涵盖内容

第一章,监督学习领域,涵盖了各种机器学习范式,这将帮助您了解该领域是如何划分为多个子组的。本章简要讨论了监督学习和无监督学习之间的区别,以及回归、分类和聚类的概念。我们将学习如何为机器学习预处理数据。我们将详细讨论回归分析,并学习如何将其应用于一些现实世界问题,包括房价估计和自行车需求分布。

第二章,构建分类器,展示了如何使用各种模型进行数据分类。我们将讨论包括逻辑回归和朴素贝叶斯模型在内的技术。我们将学习如何评估分类算法的准确性。我们将讨论交叉验证的概念,并学习如何使用它来验证我们的机器学习模型。我们将了解验证曲线及其绘制方法。我们将将这些监督学习技术应用于现实世界问题,例如收入区间估计和活动识别。

第三章,预测建模,涵盖了预测建模的前提以及为什么它是必需的。我们将了解支持向量机(SVMs)及其工作原理。我们将学习如何使用它们来分类数据。我们将讨论超参数的概念以及它们如何影响 SVMs 的性能。我们将学习如何使用网格搜索找到最佳的超参数集。我们将讨论如何估计输出的置信度度量。我们将讨论集成学习和该组中的各种算法,例如决策树和随机森林。然后我们将学习如何将这些技术应用于现实世界的事件预测。

第四章,无监督学习聚类,介绍了无监督学习的概念以及我们希望通过它实现的目标。我们将学习如何执行数据聚类,以及如何应用 k-means 聚类算法来实现它。我们将使用样本数据可视化聚类过程。我们将讨论混合模型和高斯混合模型。然后我们将应用这些技术来使用客户信息进行市场细分。

第五章,数据可视化,讨论了如何可视化数据以及为什么这对于机器学习是有用的。我们将学习如何使用 Matplotlib 与我们的数据交互并使用各种技术可视化它。我们将讨论直方图及其有用性。我们将探索不同的数据可视化方法,包括折线图、散点图和气泡图。我们将学习如何使用热图、执行动画和进行 3D 绘图。

第六章,构建推荐引擎,介绍了推荐引擎并展示了如何使用它来检查电影推荐。我们将构建一个 k-最近邻分类器来找到数据集中的相似用户,然后使用 TensorFlow 中的过滤模型生成电影推荐。

第七章,分析文本数据,展示了如何分析文本数据。我们将理解各种概念,如词袋模型、分词和词干提取。我们将了解可以从文本中提取的特征。我们将讨论如何构建文本分类器。然后我们将使用这些技术来推断句子的情感。我们还将学习如何自动识别未知段落的主题。然后我们将转向评估回归和分类模型,并进入可以帮助我们选择模型的食谱。

第八章,语音识别,展示了如何处理语音数据。我们将学习包括窗口和卷积在内的概念。我们将了解如何从语音数据中提取特征。我们将学习关于隐马尔可夫模型及其如何用于自动识别正在说的单词。

第九章,剖析时间序列和序列数据,介绍了结构化学习的概念。我们将了解时间序列数据的各种特性。我们将学习关于条件随机字段及其如何用于预测。然后我们将使用这种技术来分析股市数据。

第十章,图像内容分析,展示了如何分析图像。我们将学习如何检测关键点和从图像中提取特征。我们将讨论视觉词袋的概念及其如何应用于图像分类。我们将学习如何构建视觉代码簿并提取用于图像分类的特征向量。然后我们将了解如何使用极端随机森林进行目标识别。

第十一章,生物识别面部识别,展示了如何进行面部识别。我们将了解面部检测和面部识别之间的区别。我们将讨论降维及其如何通过 PCA 实现。我们将学习关于费舍尔面孔及其如何用于面部识别的知识。我们将对实时视频进行面部检测。然后我们将使用这些技术来识别摄像头前的人。

第十二章,强化学习技术,讨论了强化学习技术及其应用。它还讨论了强化学习设置的元素、强化学习的方法及其挑战,包括马尔可夫决策过程、探索-利用困境、折现未来奖励和 Q 学习。

第十三章,深度神经网络,讨论了深度神经网络。我们将学习感知器及其如何用于构建神经网络。我们将探索深度神经网络中多层之间的相互连接。我们将讨论神经网络如何学习训练数据并构建模型。我们将学习关于损失函数和反向传播的知识。然后我们将使用这些技术来进行光学字符识别。我们将使用不同的深度学习框架,包括 TensorFlow、PyTorch 和 Caffe。

第十四章,无监督表示学习,讨论了以无监督方式学习图像、视频和自然语言语料库等数据的表示问题。我们将探讨自动编码器和它们的应用,词嵌入和 t-SNEs。我们还将使用降噪自动编码器通过词嵌入来检测欺诈交易。最后,我们将继续使用各种食谱来实现 LDA。

第十五章,自动化机器学习和迁移学习,讨论了基于自动化机器学习和迁移学习的食谱。我们将学习如何使用 Auto-WEKA,以及如何使用 AutoML 生成机器学习管道。我们将学习如何使用 Auto-Keras,然后继续使用 MLBox 进行泄漏检测。此外,我们将学习如何通过多个食谱实现迁移学习。

第十六章,解锁生产问题,讨论了与生产相关的问题。我们将探讨读者如何处理非结构化数据,以及我们如何跟踪机器学习模型的变化。我们还将学习如何优化重新训练计划以及如何部署我们的机器学习模型。

为了充分利用本书

熟悉 Python 编程和机器学习概念将有所帮助。

下载示例代码文件

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

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

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

  2. 选择支持选项卡。

  3. 点击代码下载和勘误表。

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

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

  • Windows 系统上的 WinRAR/7-Zip

  • Mac 系统上的 Zipeg/iZip/UnRarX

  • Linux 系统上的 7-Zip/PeaZip

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

我们还有来自我们丰富的图书和视频目录中的其他代码包可供选择,请访问github.com/PacktPublishing/。查看它们吧!

下载彩色图像

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

使用的约定

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

CodeInText:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称。以下是一个示例:“我们将使用您已经提供的作为参考的simple_classifier.py文件。”

代码块设置如下:

import numpy as np
import matplotlib.pyplot as plt

X = np.array([[3,1], [2,5], [1,8], [6,4], [5,2], [3,5], [4,7], [4,-1]])

当我们希望您注意代码块中的特定部分时,相关的行或项目将以粗体显示:

[default]
exten => s,1,Dial(Zap/1|30)
exten => s,2,Voicemail(u100)
exten => s,102,Voicemail(b100)
exten => i,1,Voicemail(s0)

任何命令行输入或输出都应如下编写:

data = np.array([[3, -1.5, 2, -5.4], [0, 4, -0.3, 2.1], [1, 3.3, -1.9, -4.3]])

粗体:表示新术语、重要单词或您在屏幕上看到的单词。例如,菜单或对话框中的单词在文本中显示如下。以下是一个示例:“从管理面板中选择系统信息。”

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

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

部分

在本书中,您会发现一些经常出现的标题(准备就绪如何做到这一点...它是如何工作的...还有更多...也见)。

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

准备就绪

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

如何做到这一点...

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

它是如何工作的...

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

还有更多…

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

也见

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

联系我们

我们欢迎读者的反馈。

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

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

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

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

评论

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

如需了解 Packt 的更多信息,请访问 packt.com.

第一章:监督学习领域

在本章中,我们将涵盖以下食谱:

  • 在 Python 中创建数组

  • 使用均值移除进行数据预处理

  • 数据缩放

  • 归一化

  • 二值化

  • One-hot 编码

  • 标签编码

  • 构建线性回归器

  • 计算回归精度

  • 实现模型持久化

  • 构建岭回归器

  • 构建多项式回归器

  • 估计房价

  • 计算特征的相对重要性

  • 估计自行车需求分布

技术要求

在本书的整个过程中,我们将使用各种 Python 包,如 NumPy、SciPy、scikit-learn 和 Matplotlib 来构建各种东西。如果您使用 Windows,建议您使用与 SciPy 兼容的 Python 版本。您可以在www.scipy.org/install.html查看兼容版本列表。这些发行版已经预装了所有必要的包。如果您使用 MacOS X 或 Ubuntu,安装这些包相对简单。以下是安装和文档的一些有用链接:

在继续之前,请确保您的机器上已安装了这些包。在每个食谱中,我们将详细解释我们将使用的函数,以便使其简单快捷。

简介

机器学习是一个跨学科领域,它诞生于计算机科学、统计学、神经生物学和控制理论交叉的地方,并且在这些领域之间产生了协同效应。它在各个领域都发挥了关键作用,并且彻底改变了编程软件的视野。对于人类,以及更广泛地,对于每一个生物体,学习是通过经验使系统适应其环境的一种适应形式。这种适应过程必须在没有人类干预的情况下导致改进。为了实现这一目标,系统必须能够学习,这意味着它必须能够通过检查与它相关的一系列示例来提取有关给定问题的有用信息。

如果您熟悉机器学习的基础知识,您当然会知道什么是监督学习。为了快速回顾,监督学习指的是基于标记样本构建机器学习模型。算法生成一个函数,通过一组标记示例将输入值连接到期望的输出,其中每个数据输入都有其相对的输出数据。这用于构建预测模型。例如,如果我们构建一个基于各种参数(如大小、地区等)来估算房价的系统,我们首先需要创建一个数据库并对其进行标记。我们需要告诉我们的算法哪些参数对应于哪些价格。基于这些数据,我们的算法将学习如何使用输入参数来计算房价。

无监督学习与我们刚才讨论的内容截然不同。这里没有可用的标记数据。算法试图在没有一组用于构建描述性模型的预分类示例的帮助下,从一般输入中获取知识。让我们假设我们有一组数据点,我们只想将它们分成多个组。我们并不确切知道分离的标准会是什么。因此,无监督学习算法将尝试以最佳方式将给定的数据集分成固定数量的组。我们将在接下来的章节中讨论无监督学习。

在以下菜谱中,我们将探讨各种数据预处理技术。

Python 中的数组创建

数组是许多编程语言的基本元素。数组是顺序对象,其行为与列表非常相似,只是它们包含的元素类型受到限制。类型是在使用单个字符类型码创建对象时指定的。

准备就绪

在本菜谱中,我们将介绍数组创建过程。我们首先将使用 NumPy 库创建一个数组,然后显示其结构。

如何操作...

让我们看看如何在 Python 中创建一个数组:

  1. 要开始,按照以下方式导入 NumPy 库:
>> import numpy as np

我们刚刚导入了一个必要的包,numpy。这是 Python 进行科学计算的基本包。它包含了许多其他内容,例如以下内容:

  • 强大的 N 维数组对象

  • 复杂的广播功能

  • 用于集成 C、C++和 FORTRAN 代码的工具

  • 有用的线性代数、傅里叶变换和随机数功能

除了其明显的用途外,NumPy 还用作高效的多维通用数据容器。可以找到任意数据类型。这使得 NumPy 能够与不同类型的数据库集成。

记住,要导入 Python 初始发行版中不存在的库,您必须使用pip install命令,后跟库的名称。此命令应仅使用一次,而不是每次运行代码时都使用。

  1. 让我们创建一些示例数据。在 Python 终端中添加以下行:
>> data = np.array([[3, -1.5, 2, -5.4], [0, 4, -0.3, 2.1], [1, 3.3, -1.9, -4.3]])

np.array函数创建一个 NumPy 数组。NumPy 数组是一个值网格,所有值类型相同,通过一个非负整数元组进行索引。rankshape是 NumPy 数组的基本特性。rank变量是数组的维度数。shape变量是一个整数元组,返回数组在每个维度上的大小。

  1. 我们使用以下代码片段显示新创建的数组:
>> print(data)

返回以下结果:

[[ 3\. -1.5  2\.  -5.4]
 [ 0\.  4\.  -0.3  2.1]
 [ 1\.  3.3 -1.9 -4.3]]

我们现在可以开始对这个数据进行操作。

它是如何工作的...

NumPy 是 Python 环境中用于科学计算的扩展包,它是基础性的。这是因为它增加了现有工具的典型特性,包括 N 维数组的典型特征、逐元素操作、线性代数中的大量数学运算,以及集成和调用用 C、C++和 FORTRAN 编写的源代码的能力。在这个菜谱中,我们学习了如何使用 NumPy 库创建数组。

还有更多...

NumPy 为我们提供了创建数组的各种工具。例如,要创建一个从 0 到 10 的等距值的单维数组,我们将使用arange()函数,如下所示:

>> NpArray1 = np.arange(10)
>> print(NpArray1)

返回以下结果:

[0 1 2 3 4 5 6 7 8 9]

要创建一个从 0 到 50 的数值数组,步长为 5(使用预定的步长),我们将编写以下代码:

>> NpArray2 = np.arange(10, 100, 5)
>> print(NpArray2)

下面的数组被打印出来:

[10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95]

此外,要创建一个在两个极限值之间且在此范围内等距的 50 个数字的单维数组,我们将使用linspace()函数:

>> NpArray3 = np.linspace(0, 10, 50)
>> print(NpArray3)

返回以下结果:

[ 0\. 0.20408163 0.40816327 0.6122449 0.81632653 1.02040816
 1.2244898 1.42857143 1.63265306 1.83673469 2.04081633 2.24489796
 2.44897959 2.65306122 2.85714286 3.06122449 3.26530612 3.46938776
 3.67346939 3.87755102 4.08163265 4.28571429 4.48979592 4.69387755
 4.89795918 5.10204082 5.30612245 5.51020408 5.71428571 5.91836735
 6.12244898 6.32653061 6.53061224 6.73469388 6.93877551 7.14285714
 7.34693878 7.55102041 7.75510204 7.95918367 8.16326531 8.36734694
 8.57142857 8.7755102 8.97959184 9.18367347 9.3877551 9.59183673
 9.79591837 10\. ]

这些只是 NumPy 的一些简单示例。在接下来的章节中,我们将更深入地探讨这个主题。

参见

使用均值移除进行数据预处理

在现实世界中,我们通常必须处理大量的原始数据。这些原始数据不能直接被机器学习算法摄取。为了准备机器学习数据,我们必须在将其输入各种算法之前对其进行预处理。这是一个耗时的工作,在某些情况下几乎占整个数据分析过程的 80%,然而,对于数据分析工作流程的其余部分来说至关重要,因此有必要学习这些技术的最佳实践。在我们将数据发送到任何机器学习算法之前,我们需要交叉检查数据的质和准确度。如果我们无法正确访问存储在 Python 中的数据,或者如果我们不能从原始数据切换到可以分析的数据,我们就无法继续。数据可以通过多种方式进行预处理——标准化、缩放、归一化、二值化和独热编码是一些预处理技术的例子。我们将通过简单的示例来介绍它们。

准备工作

标准化均值移除是一种通过移除每个特征的均值来简单地将数据中心化的技术,然后通过将非常数特征除以其标准差来缩放它。通常从每个特征中移除均值以便它以零为中心是有益的。这有助于我们从特征中去除偏差。用于实现此目的的公式如下:

图片

标准化导致特征重新缩放,这反过来又代表了标准正态分布的特性:

  • 均值 = 0

  • 标准差 = 1

在此公式中,均值是均值,sd是从均值的标准差。

如何做...

让我们看看如何在 Python 中预处理数据:

  1. 让我们先导入库:
>> from sklearn import preprocessing

sklearn 库是 Python 编程语言的免费软件机器学习库。它具有各种分类、回归和聚类算法,包括支持向量机SVMs)、随机森林、梯度提升、k-means 和 DBSCAN,并且旨在与 Python 数值和科学库 NumPy 和 SciPy 互操作。

  1. 为了理解均值移除对我们数据的影响,我们首先可视化我们刚刚创建的向量的均值和标准差:
>> print("Mean: ",data.mean(axis=0))
>> print("Standard Deviation: ",data.std(axis=0))

mean() 函数返回数据的样本算术平均值,它可以是序列或迭代器。std() 函数返回标准差,它是数组元素分布的度量。axis 参数指定这些函数计算的轴(0 为列,1 为行)。

返回以下结果:

Mean: [ 1.33333333 1.93333333 -0.06666667 -2.53333333]
Standard Deviation: [1.24721913 2.44449495 1.60069429 3.30689515]
  1. 现在我们可以进行标准化:
>> data_standardized = preprocessing.scale(data)

preprocessing.scale() 函数沿任何轴标准化数据集。此方法将数据中心在均值上,并调整组件的大小以使其具有单位方差。

  1. 现在我们重新计算标准化数据上的均值和标准差:
>> print("Mean standardized data: ",data_standardized.mean(axis=0))
>> print("Standard Deviation standardized data: ",data_standardized.std(axis=0))

返回以下结果:

Mean standardized data: [ 5.55111512e-17 -1.11022302e-16 -7.40148683e-17 -7.40148683e-17]
Standard Deviation standardized data: [1\. 1\. 1\. 1.]

你可以看到平均值几乎为 0,标准差为 1。

它是如何工作的...

sklearn.preprocessing包提供了几个常见的实用函数和转换类,以修改适合我们需求的表示中的特征。在这个配方中,使用了scale()函数(z 分数标准化)。总结来说,z 分数(也称为标准分数)表示观察点或数据的值相对于观察或测量的平均值超出多少个标准差。超过平均值的值有正的 z 分数,而低于平均值的值有负的 z 分数。z 分数是一个没有维度的量,通过从单个粗略分数中减去总体平均值,然后除以总体的标准差来获得。

还有更多...

标准化在我们不知道数据分布的最小值和最大值时特别有用。在这种情况下,无法使用其他形式的数据转换。作为转换的结果,归一化值没有最小值和固定的最大值。此外,这种技术不受异常值存在的影响,或者至少与其他方法不同。

参见

数据缩放

数据集中每个特征值可能在随机值之间变化。因此,有时对它们进行缩放以使这成为一个公平的竞争环境是很重要的。通过这个统计过程,可以比较属于不同分布和不同变量的相同变量。

记住,在训练机器学习算法之前重新缩放数据是一种良好的实践。通过缩放,数据单位被消除,这使得你可以轻松地比较来自不同位置的数据。

准备工作

我们将使用最小-最大方法(通常称为特征缩放)来获取所有缩放数据在范围[0, 1]内。实现这一目标的公式如下:

为了将特征缩放到给定的最小值和最大值之间——在我们的例子中,在 0 和 1 之间——使得每个特征的绝对最大值缩放到单位大小,可以使用preprocessing.MinMaxScaler()函数。

如何操作...

让我们看看如何在 Python 中缩放数据:

  1. 让我们先定义data_scaler变量:
>> data_scaler = preprocessing.MinMaxScaler(feature_range=(0, 1))
  1. 现在,我们将使用fit_transform()方法,该方法先拟合数据然后转换它(我们将使用与上一个配方相同的数据):
>> data_scaled = data_scaler.fit_transform(data)

返回一个特定形状的 NumPy 数组。为了理解这个函数如何转换数据,我们显示数组中每列的最小值和最大值。

  1. 首先,对于原始数据,然后对于处理后的数据:
>> print("Min: ",data.min(axis=0))
>> print("Max: ",data.max(axis=0))

以下结果被返回:

Min: [ 0\. -1.5 -1.9 -5.4]
Max: [3\. 4\. 2\. 2.1]
  1. 现在,让我们使用以下代码对缩放数据进行相同的操作:
>> print("Min: ",data_scaled.min(axis=0))
>> print("Max: ",data_scaled.max(axis=0))

以下结果被返回:

Min: [0\. 0\. 0\. 0.]
Max: [1\. 1\. 1\. 1.]

缩放后,所有特征值都在指定的值之间。

  1. 要显示缩放数组,我们将使用以下代码:
>> print(data_scaled)

输出将如下所示:

[[ 1\.          0\.          1\.          0\.        ] 
 [ 0\.          1\.          0.41025641  1\.        ]
 [ 0.33333333  0.87272727  0\.          0.14666667]]

现在,所有数据都包含在同一个区间内。

它是如何工作的...

当数据有不同的范围时,对响应变量的影响可能比数值范围较小的数据更高,这可能会影响预测精度。我们的目标是提高预测精度并确保这种情况不会发生。因此,我们可能需要在不同特征下缩放值,使它们落在相似的范围之内。通过这个统计过程,可以比较属于不同分布的相同变量或不同变量,或者以不同单位表示的变量。

还有更多...

特征缩放 包括限制一组值在一定预定义区间内的波动。这保证了所有 功能都有相同的尺度,但处理异常值并不好。这是因为极端值成为新变化范围的极端。通过这种方式,实际值通过保持与异常值的距离而被压缩。

相关内容

归一化

数据归一化用于当你想要调整特征向量中的值,以便它们可以在一个共同的尺度上进行测量时。在机器学习中使用的最常见的归一化形式之一是将特征向量的值调整为总和为 1。

准备工作

要归一化数据,可以使用preprocessing.normalize()函数。此函数将输入向量单独缩放到单位范数(向量长度)。提供了三种类型的范数,l[1]l[2]max,它们将在下面解释。如果x是长度为n的协变量向量,则归一化向量为y=x/z,其中z定义如下:

范数是一个函数,它将正长度分配给属于向量空间中的每个向量,除了 0。

如何做...

让我们看看如何在 Python 中归一化数据:

  1. 正如我们所说,要归一化数据,可以使用preprocessing.normalize()函数如下(我们将使用与上一个配方相同的数据):
>> data_normalized = preprocessing.normalize(data, norm='l1', axis=0)
  1. 要显示归一化数组,我们将使用以下代码:
>> print(data_normalized)

以下输出被返回:

[[ 0.75 -0.17045455  0.47619048  -0.45762712]
 [ 0\.    0.45454545 -0.07142857   0.1779661 ]
 [ 0.25  0.375      -0.45238095  -0.36440678]]

这通常用于确保数据集不会因为其特征的基本性质而人为地被提升。

  1. 如前所述,沿列(特征)归一化的数组必须返回一个等于 1 的总和。让我们检查每一列:
>> data_norm_abs = np.abs(data_normalized)
>> print(data_norm_abs.sum(axis=0))

在代码的第一行,我们使用了np.abs()函数来计算数组中每个元素的绝对值。在代码的第二行,我们使用了sum()函数来计算每列的总和(axis=0)。以下结果被返回:

[1\. 1\. 1\. 1.]

因此,每列元素绝对值的总和等于 1,所以数据被归一化。

它是如何工作的...

在这个食谱中,我们将可用的数据归一化到单位范数。每个至少有一个非零成分的样本都会独立于其他样本进行缩放,使其范数等于 1。

还有更多...

在文本分类和聚类问题中,将输入缩放到单位范数是一个非常常见的任务。

相关内容

二值化

二值化用于将数值特征向量转换为布尔向量。在数字图像处理领域,图像二值化是将彩色或灰度图像转换为二值图像的过程,即只包含两种颜色(通常是黑白)的图像。

准备工作

这种技术用于对象的识别、形状的识别,特别是字符的识别。通过二值化,可以区分感兴趣的对象及其背景。相反,骨架化是对象的一种基本和简化的表示,通常作为后续真实识别的前奏。

如何做...

让我们看看如何在 Python 中二值化数据:

  1. 要进行二值化,我们将使用preprocessing.Binarizer()函数如下(我们将使用与上一个食谱相同的数据):
>> data_binarized = preprocessing.Binarizer(threshold=1.4).transform(data)

preprocessing.Binarizer()函数根据设定的threshold值对数据进行二值化。大于threshold的值映射为 1,而小于或等于threshold的值映射为 0。默认的threshold值为 0,只有正数映射为 1。在我们的例子中,设定的threshold值为1.4,所以大于1.4的值映射为 1,而小于1.4的值映射为 0。

  1. 要显示二值化数组,我们将使用以下代码:
>> print(data_binarized)

返回以下输出:

[[ 1\.  0\.  1\.  0.]
 [ 0\.  1\.  0\.  1.]
 [ 0\.  1\.  0\.  0.]]

这是一个非常有用的技术,通常在我们对数据有一些先验知识时使用。

它是如何工作的...

在这个菜谱中,我们对数据进行二值化。这种技术的核心思想是画一条固定的分界线。因此,这是一个找到适当的阈值,并断言所有光强度低于一定值的图像点属于对象(背景),而所有光强度更高的点属于背景(对象)。

还有更多...

二值化是计数数据上的一种常见操作,分析师可以选择只考虑特征的呈现或缺失,而不是发生次数的数量。否则,它可以作为考虑随机布尔变量的估计器的预处理步骤。

参见

One-hot 编码

我们经常处理稀疏且散布各处的数值。我们实际上不需要存储这些值。这就是 one-hot 编码发挥作用的地方。我们可以将 one-hot 编码视为一个工具,它可以紧缩特征向量。它查看每个特征并确定不同值的总数。它使用一个one-of-k方案来编码值。特征向量中的每个特征都是基于这个方案进行编码的。这有助于我们在空间方面更加高效。

准备工作

假设我们正在处理四维特征向量。为了编码特征向量中的n特征,编码器将遍历每个特征向量中的n特征,并计算不同值的数量。如果不同值的数量是k,它将特征转换为一个k维向量,其中只有一个值为 1,其余值都为 0。让我们用一个简单的例子来理解它是如何工作的。

如何实现...

让我们看看如何在 Python 中编码数据:

  1. 让我们以一个有四行(向量)和三列(特征)的数组为例:
>> data = np.array([[1, 1, 2], [0, 2, 3], [1, 0, 1], [0, 1, 0]])
>> print(data)

打印出以下结果:

[[1 1 2]
 [0 2 3]
 [1 0 1]
 [0 1 0]]

让我们分析每个列(特征)中存在的值:

  • 第一个特征有两个可能的值:0,1

  • 第二个特征有三个可能的值:0,1,2

  • 第三个特征有四个可能的值:0,1,2,3

因此,总的来说,每个特征中可能存在的值的总和为 2 + 3 + 4 = 9。这意味着需要 9 个条目来唯一表示任何向量。这三个特征将如下表示:

  • 特征 1 从索引0开始

  • 特征 2 从索引2开始

  • 特征 3 从索引5开始

  1. 要将分类整数特征编码为 one-hot 数值数组,可以使用preprocessing.OneHotEncoder()函数如下:
>> encoder = preprocessing.OneHotEncoder()
>> encoder.fit(data)

第一行代码设置了编码器,然后fit()函数将OneHotEncoder对象拟合到数据数组。

  1. 现在我们可以使用单热编码转换数据数组。为此,将使用 transform() 函数,如下所示:
>> encoded_vector = encoder.transform([[1, 2, 3]]).toarray()

如果你打印 encoded_vector,预期的输出将是:

[[0\. 1\. 0\. 0\. 1\. 0\. 0\. 0\. 1.]]

结果很清晰:第一个特征(1)的索引为 1,第二个特征(3)的索引为 4,第三个特征(3)的索引为 8。正如我们可以验证的那样,只有这些位置被 1 占据;所有其他位置都是 0。记住,Python 从 0 开始索引位置,所以 9 个条目将有从 0 到 8 的索引。

它是如何工作的...

preprocessing.OneHotEncoder() 函数将分类整数特征编码为单热数值数组。从表示分类特征(离散)所取值的整数或字符串数组开始,此函数使用单热编码方案对特征进行编码,返回虚拟变量。这为每个类别创建一个二进制列,并返回一个稀疏数组或密集数组。

更多内容...

经常发生的情况是你需要转换分类数据。这是因为许多机器学习算法不能直接处理分类数据。为了使用这些方法,首先需要将分类数据转换为数值数据。这既适用于输入变量也适用于输出变量。

相关内容

标签编码

在监督学习中,我们通常处理各种标签。这些可以是数字或单词。如果是数字,则算法可以直接使用它们。然而,标签通常需要以人类可读的形式存在。因此,人们通常用单词标记训练数据。

准备工作

标签编码是指将单词标签转换为数值形式,以便算法可以理解如何操作它们。让我们看看如何做到这一点。

如何做…

让我们看看如何在 Python 中执行标签编码:

  1. 创建一个新的 Python 文件并导入 preprocessing() 包:
>> from sklearn import preprocessing
  1. 此软件包包含用于数据预处理的所需的各种函数。要使用介于 0 和 n_classes-1 之间的值对标签进行编码,可以使用 preprocessing.LabelEncoder() 函数。让我们定义标签编码器,如下所示:
>> label_encoder = preprocessing.LabelEncoder()
  1. label_encoder 对象知道如何理解单词标签。让我们创建一些标签:
>> input_classes = ['audi', 'ford', 'audi', 'toyota', 'ford', 'bmw']
  1. 我们现在准备好对这些标签进行编码——首先,使用 fit() 函数来拟合标签编码器,然后打印类映射编码器:
>> label_encoder.fit(input_classes)
>> print("Class mapping: ")
>> for i, item in enumerate(label_encoder.classes_):
...    print(item, "-->", i)
  1. 运行代码,你将在终端上看到以下输出:
Class mapping:
audi --> 0
bmw --> 1
ford --> 2
toyota --> 3
  1. 如前一个输出所示,单词已经被转换为零索引的数字。现在,当你遇到一组标签时,你可以简单地转换它们,如下所示:
>> labels = ['toyota', 'ford', 'audi']
>> encoded_labels = label_encoder.transform(labels)
>> print("Labels =", labels)
>> print("Encoded labels =", list(encoded_labels))

这是你在终端上看到的输出:

Labels = ['toyota', 'ford', 'audi']
Encoded labels = [3, 2, 0]
  1. 这比手动维护单词和数字之间的映射要容易得多。你可以通过将数字转换回单词标签来检查正确性:
>> encoded_labels = [2, 1, 0, 3, 1]
>> decoded_labels = label_encoder.inverse_transform(encoded_labels)
>> print("Encoded labels =", encoded_labels)
>> print("Decoded labels =", list(decoded_labels))

为了将标签转换回原始编码,应用了inverse_transform()函数。以下是输出:

Encoded labels = [2, 1, 0, 3, 1]
Decoded labels = ['ford', 'bmw', 'audi', 'toyota', 'bmw']

如您所见,映射被完美地保留。

它是如何工作的...

在这个食谱中,我们使用了preprocessing.LabelEncoder()函数将单词标签转换为数值形式。为此,我们首先设置了一系列标签对应于尽可能多的汽车品牌。然后我们将这些标签转换为数值。最后,为了验证程序的运行情况,我们打印出对应于每个标记类别的值。

更多内容...

在最后的两个食谱中,标签编码独热编码,我们看到了如何转换数据。这两种方法都适用于处理分类数据。但两种方法的优缺点是什么?让我们来看看:

  • 标签编码可以将分类数据转换为数值数据,但如果获得的值用于数学运算,则强加的顺序性会引发问题。

  • 独热编码的优点是结果是二进制而不是有序的,并且所有内容都在正交向量空间中。缺点是对于高基数,特征空间可能会爆炸。

参见

构建线性回归器

线性回归是指通过输入变量的线性组合来寻找潜在函数。上一个例子有一个输入变量和一个输出变量。简单的线性回归容易理解,但它是回归技术的基础。一旦这些概念被理解,我们将更容易处理其他类型的回归。

考虑以下图表:

线性回归方法包括精确地确定一条能够代表二维平面上点分布的直线,也就是说,如果对应于观察点的点接近这条线,那么所选模型将能够有效地描述变量之间的联系。

理论上,有无限多条可能近似观察到的线,而在实践中,只有一个数学模型能够优化数据的表示。在线性数学关系的情况下,可以通过x变量的观察值的线性函数获得y变量的观察值。对于每个观察值,我们将使用以下公式:

在前面的公式中,x是解释变量,y是响应变量。α和β参数,分别代表线的斜率和与 y 轴的截距,必须根据收集到的模型中包含的两个变量的观察值来估计。

斜率,α,特别有趣,即解释变量每增加一个增量时平均响应的变化。这个系数的变化又如何呢?如果斜率是正的,回归线从左到右上升,如果斜率是负的,线从左到右下降。当斜率为零时,解释变量对响应值没有影响。但不仅仅是α的符号建立了变量之间关系的权重。更普遍地说,它的值也很重要。在正斜率的情况下,当解释变量较高时,平均响应也较高,而在负斜率的情况下,当解释变量较高时,平均响应较低。

线性回归的主要目的是获取连接输入变量和输出变量的潜在线性模型。这反过来又通过线性函数减少了实际输出和预测输出之间的差异的平方和。这种方法被称为普通最小二乘法。在这个方法中,系数是通过确定数值来估计的,这些数值最小化了观察到的响应和拟合响应之间的平方偏差之和,根据以下方程:

图片

这个量表示每个实验数据点(x[i],y[i])到对应直线上的点的距离的平方和。

你可能会说,可能存在一条曲线更适合这些点,但线性回归不允许这样做。线性回归的主要优势是它不复杂。如果你进行非线性回归,你可能会得到更精确的模型,但它们会慢一些。如图所示,模型试图使用直线来近似输入数据点。让我们看看如何在 Python 中构建线性回归模型。

准备工作

回归用于找出输入数据与连续值输出数据之间的关系。这通常表示为实数,我们的目标是估计计算输入到输出映射的核心函数。让我们从一个非常简单的例子开始。考虑以下输入和输出之间的映射:

1 --> 2
3 --> 6
4.3 --> 8.6
7.1 --> 14.2

如果我让你估计输入和输出之间的关系,你可以通过分析模式轻松做到这一点。我们可以看到,在每种情况下,输出都是输入值的两倍,因此转换如下:

图片

这是一个简单的函数,将输入值与输出值相关联。然而,在现实世界中,情况通常并非如此。现实世界中的函数并不那么直接!

您已经提供了一个名为VehiclesItaly.txt的数据文件。该文件包含逗号分隔的行,其中第一元素是输入值,第二元素是与该输入值对应的输出值。我们的目标是找到州车辆注册数与州人口之间的线性回归关系。您应该使用此作为输入参数。正如预期的那样,Registrations变量包含在意大利注册的车辆数量,而Population变量包含不同地区的总人口。

如何做到这一点...

让我们看看如何在 Python 中构建线性回归器:

  1. 创建一个名为regressor.py的文件,并添加以下行:
filename = "VehiclesItaly.txt"
X = []
y = []
with open(filename, 'r') as f:
    for line in f.readlines():
        xt, yt = [float(i) for i in line.split(',')]
        X.append(xt)
        y.append(yt)

我们只是将输入数据加载到Xy中,其中X代表自变量(解释变量),而y代表因变量(响应变量)。在前面的代码循环中,我们解析每一行并根据逗号操作符进行分割。然后我们将它们转换为浮点值并保存在Xy中。

  1. 当我们构建机器学习模型时,我们需要一种方法来验证我们的模型并检查它是否表现令人满意。为此,我们需要将我们的数据分成两组——训练数据集和测试数据集。训练数据集将用于构建模型,而测试数据集将用于查看训练好的模型在未知数据上的表现。所以,让我们继续将数据分成训练集和测试集:
num_training = int(0.8 * len(X))
num_test = len(X) - num_training

import numpy as np

# Training data
X_train = np.array(X[:num_training]).reshape((num_training,1))
y_train = np.array(y[:num_training])

# Test data
X_test = np.array(X[num_training:]).reshape((num_test,1))
y_test = np.array(y[num_training:])

首先,我们将 80%的数据留作训练数据集,剩余的 20%用于测试数据集。然后,我们构建了四个数组:X_trainX_testy_trainy_test

  1. 我们现在准备好训练模型了。让我们创建一个regressor对象,如下所示:
from sklearn import linear_model

# Create linear regression object
linear_regressor = linear_model.LinearRegression()

# Train the model using the training sets
linear_regressor.fit(X_train, y_train)

首先,我们从sklearn库中导入了linear_model方法,这些方法是用于回归的方法,其中目标值预计是输入变量的线性组合。然后,我们使用了LinearRegression()函数,该函数执行普通最小二乘线性回归。最后,使用fit()函数来拟合线性模型。传递了两个参数——训练数据(X_train)和目标值(y_train)。

  1. 我们刚刚根据我们的训练数据训练了线性回归器。fit()方法接受输入数据并训练模型。为了看到它如何拟合,我们必须用拟合的模型预测训练数据:
y_train_pred = linear_regressor.predict(X_train)
  1. 为了绘制输出,我们将使用matplotlib库,如下所示:
import matplotlib.pyplot as plt
plt.figure()
plt.scatter(X_train, y_train, color='green')
plt.plot(X_train, y_train_pred, color='black', linewidth=4)
plt.title('Training data')
plt.show()

当你在终端中运行这个程序时,会显示以下图表:

图表

  1. 在前面的代码中,我们使用了训练好的模型来预测训练数据的输出。这并不能告诉我们模型在未知数据上的表现,因为我们是在训练数据上运行它的。这仅仅给我们一个模型在训练数据上拟合程度的印象。看起来它做得还不错,正如你可以在前面的图表中看到的那样!

  2. 让我们根据这个模型预测测试数据集的输出并绘制它,如下所示:

y_test_pred = linear_regressor.predict(X_test)
plt.figure()
plt.scatter(X_test, y_test, color='green')
plt.plot(X_test, y_test_pred, color='black', linewidth=4)
plt.title('Test data')
plt.show()

当你在终端中运行此代码时,会返回以下输出:

图片

如你所预期,一个州的 人口与车辆注册数量之间存在正相关关系。

工作原理...

在这个菜谱中,我们寻找了一个州的车牌注册数量与该州人口之间的线性回归关系。为此,我们使用了sklearn库中linear_model方法的LinearRegression()函数。在构建模型后,我们首先使用训练模型的数据来直观地验证模型拟合数据的好坏。然后,我们使用测试数据来验证结果。

还有更多...

最好的方式是通过特殊的图表来展示模拟的结果。事实上,我们已经在本节中使用了这种技术。我指的是我们绘制了包含回归线的分布散点图的图表。在第五章《可视化数据》中,我们将看到其他可以让我们检查模型假设的图表。

参见

计算回归精度

现在我们已经知道了如何构建回归器,了解如何评估回归器的质量也同样重要。在这种情况下,误差被定义为实际值与回归器预测值之间的差异。

准备工作

让我们快速看一下可以用来衡量回归器质量的指标。回归器可以使用许多不同的指标进行评估。scikit-learn库中有一个模块提供了计算以下所有指标的功能。这是sklearn.metrics模块,它包括评分函数、性能指标、成对指标和距离计算。

如何实现...

让我们看看如何在 Python 中计算回归精度:

  1. 现在我们将使用之前菜谱中开发的线性回归模型可用的函数来评估其性能:
import sklearn.metrics as sm
print("Mean absolute error =", round(sm.mean_absolute_error(y_test, y_test_pred), 2)) 
print("Mean squared error =", round(sm.mean_squared_error(y_test, y_test_pred), 2)) 
print("Median absolute error =", round(sm.median_absolute_error(y_test, y_test_pred), 2)) 
print("Explain variance score =", round(sm.explained_variance_score(y_test, y_test_pred), 2)) 
print("R2 score =", round(sm.r2_score(y_test, y_test_pred), 2))

以下结果被返回:

Mean absolute error = 241907.27
Mean squared error = 81974851872.13
Median absolute error = 240861.94
Explain variance score = 0.98
R2 score = 0.98

R2 分数接近 1 表示模型能够很好地预测数据。跟踪每一个单一指标可能会变得繁琐,所以我们选择一两个指标来评估我们的模型。一个好的做法是确保均方误差低,解释方差分数高。

它是如何工作的...

可以使用许多不同的指标来评估回归器,如下所示:

  • 均方绝对误差:这是给定数据集中所有数据点绝对误差的平均值。

  • 均方误差:这是给定数据集中所有数据点误差平方的平均值。这是最受欢迎的指标之一!

  • 中值绝对误差:这是给定数据集中所有误差的中位数。这个指标的主要优点是它对异常值具有鲁棒性。测试数据集中一个错误的数据点不会扭曲整个误差指标,这与平均误差指标相反。

  • 解释方差分数:这个分数衡量我们的模型能够解释数据集中变化的程度。分数为 1.0 表示我们的模型是完美的。

  • R2 分数:这个分数读作 R-squared,这个分数指的是确定系数。这告诉我们我们的模型将如何预测未知样本。最佳可能的分数是 1.0,但分数也可以是负数。

更多...

sklearn.metrics模块包含一系列简单的函数,用于测量预测误差:

  • _score结尾的函数返回一个要最大化的值;越高越好

  • _error_loss结尾的函数返回一个要最小化的值;越低越好

参见

实现模型持久化

当我们训练一个模型时,如果能将其保存为文件以便稍后通过重新加载它来使用,那就太好了。

准备工作

让我们看看如何通过编程实现模型持久化。为此,可以使用pickle模块。pickle模块用于存储 Python 对象。这个模块是 Python 标准库的一部分,与你的 Python 安装一起提供。

如何做...

让我们看看如何在 Python 中实现模型持久化:

  1. 将以下行添加到regressor.py文件中:
import pickle

output_model_file = "3_model_linear_regr.pkl"

with open(output_model_file, 'wb') as f:
    pickle.dump(linear_regressor, f) 
  1. 回归器对象将被保存到saved_model.pkl文件中。让我们看看如何加载它并使用它,如下所示:
with open(output_model_file, 'rb') as f:
    model_linregr = pickle.load(f)

y_test_pred_new = model_linregr.predict(X_test)
print("New mean absolute error =", round(sm.mean_absolute_error(y_test, y_test_pred_new), 2))

以下结果被返回:

New mean absolute error = 241907.27

这里,我们只是将回归器从文件中加载到model_linregr变量中。你可以将前面的结果与前面的结果进行比较,以确认它们是相同的。

它是如何工作的...

pickle模块将任意 Python 对象转换成一系列字节。这个过程也称为对象的序列化。表示对象的字节流可以传输或存储,随后重建以创建具有相同特征的新对象。其逆操作称为反序列化

更多内容...

在 Python 中,还有另一种执行序列化的方法,即使用marshal模块。通常,推荐使用pickle模块来序列化 Python 对象。marshal模块可以用来支持 Python 的.pyc文件。

参见

构建岭回归

线性回归的一个主要问题是它对异常值很敏感。在现实世界的数据收集过程中,错误地测量输出是很常见的。线性回归使用普通最小二乘法,试图最小化误差的平方。异常值往往会引起问题,因为它们对总体误差的贡献很大。这往往会破坏整个模型。

让我们尝试深化对异常值概念的理解:异常值是与其他值相比特别极端的值(明显远离其他观察值的值)。异常值是一个问题,因为它们可能会扭曲数据分析结果;更具体地说,描述性统计和相关性。我们需要在数据清洗阶段找到这些异常值,然而,我们也可以在数据分析的下一阶段开始处理它们。异常值可以是单变量的,当它们对单个变量有极端值时;也可以是多变量的,当它们在多个变量上有独特的值组合时。让我们考虑以下图表:

图片

右下角的两个点显然是异常值,但这个模型试图拟合所有点。因此,整体模型往往不够准确。异常值是分布的极端值,与分布的其他部分相比,它们要么极高,要么极低,因此代表了相对于其他分布的孤立案例。通过视觉检查,我们可以看到以下输出是一个更好的模型:

图片

普通最小二乘法在构建模型时会考虑每一个数据点。因此,实际模型最终看起来就像前面图表中显示的虚线所示。我们可以清楚地看到这个模型是不理想的。

正则化方法涉及修改性能函数,通常选择为训练集上回归误差平方和。当有大量变量可用时,线性模型的平方估计通常具有较低的偏差,但相对于变量较少的模型具有较高的方差。在这些条件下,存在过拟合问题。为了通过允许更大的偏差但较小的方差来提高预测精度,我们可以使用变量选择方法和降维,但这些方法可能在第一种情况下计算负担较大,或者在第二种情况下难以解释。

解决过拟合问题的另一种方法是通过修改估计方法,忽略无偏参数估计器的要求,而是考虑使用有偏估计器的可能性,这可能有较小的方差。存在几种有偏估计器,其中大多数基于正则化:岭回归LassoElasticNet是最受欢迎的方法。

准备工作

岭回归是一种正则化方法,对系数的大小施加惩罚。正如我们在构建线性回归器部分所说,在普通最小二乘法中,系数是通过确定数值来估计的,这些数值最小化了观察到的响应和拟合响应之间的平方偏差之和,如下面的方程所示:

图片

岭回归,为了估计β系数,从残差平方和RSS)的基本公式开始,并添加惩罚项。λ(≥0)定义为调整参数,它乘以β系数平方的总和(不包括截距)以定义惩罚期,如下面的方程所示:

图片

显然,λ = 0 意味着模型中没有惩罚,也就是说,我们会产生与最小二乘法相同的估计。另一方面,当λ趋向于无穷大时,意味着有很高的惩罚效果,这将使许多系数接近于零,但不会意味着它们被排除在模型之外。让我们看看如何在 Python 中构建岭回归。

如何做…

让我们看看如何在 Python 中构建岭回归:

  1. 您可以使用在先前的例子中使用的相同数据:构建线性回归器VehiclesItaly.txt)。此文件每行包含两个值。第一个值是解释变量,第二个是响应变量。

  2. regressor.py中添加以下行。让我们用一些参数初始化一个岭回归:

from sklearn import linear_model
ridge_regressor = linear_model.Ridge(alpha=0.01, fit_intercept=True, max_iter=10000)
  1. alpha参数控制复杂性。当alpha接近 0 时,岭回归器趋向于更像具有普通最小二乘法的线性回归器。因此,如果你想使其对异常值具有鲁棒性,你需要给alpha分配一个更高的值。我们考虑了0.01这个适中的值。

  2. 让我们按照以下步骤训练这个回归器:

ridge_regressor.fit(X_train, y_train)
y_test_pred_ridge = ridge_regressor.predict(X_test)
print( "Mean absolute error =", round(sm.mean_absolute_error(y_test, y_test_pred_ridge), 2))
print( "Mean squared error =", round(sm.mean_squared_error(y_test, y_test_pred_ridge), 2))
print( "Median absolute error =", round(sm.median_absolute_error(y_test, y_test_pred_ridge), 2))
print( "Explain variance score =", round(sm.explained_variance_score(y_test, y_test_pred_ridge), 2))
print( "R2 score =", round(sm.r2_score(y_test, y_test_pred_ridge), 2))

运行此代码以查看错误指标。你可以构建一个线性回归器,在相同的数据上比较和对比结果,以查看将正则化引入模型的影响。

它是如何工作的...

岭回归是一种正则化方法,对系数的大小施加惩罚。岭回归在岭系数通过减少一个多少有些不同的量来计算的情况下与最小二乘法相同。在岭回归中,尺度变换有显著的影响。因此,为了避免根据预测的尺度测量获得不同的结果,建议在估计模型之前对所有预测变量进行标准化。为了标准化变量,我们必须从它们的平均值中减去,并除以它们的标准差。

参见

构建多项式回归器

线性回归模型的一个主要约束是它试图将线性函数拟合到输入数据。多项式回归模型通过允许函数为多项式来克服这个问题,从而提高了模型的准确性。

准备工作

多项式模型应该在响应变量和解释变量之间存在曲线关系的地方应用。有时,多项式模型也可以用来在解释变量的小范围内模拟非线性关系。一个多项式二次(平方)或三次(立方)项将线性回归模型转换为多项式曲线。然而,由于是解释变量被平方或立方,而不是 beta 系数,因此它仍然被视为线性模型。这使得它成为建模曲线的一种简单易行的方法,无需创建大的非线性模型。让我们考虑以下图表:

我们可以看到数据点的模式存在自然曲线。这个线性模型无法捕捉到这一点。让我们看看多项式模型会是什么样子:

图片

点线代表线性回归模型,实线代表多项式回归模型。该模型的曲率由多项式的次数控制。随着模型曲率的增加,其准确性也会提高。然而,曲率也会增加模型的复杂性,使其运行速度变慢。这是一个权衡:你必须根据计算限制来决定你希望模型有多准确。

如何做到这一点...

让我们看看如何在 Python 中构建一个多项式回归器:

  1. 在这个例子中,我们只处理二次抛物线回归。现在,我们将展示如何用多项式建模数据。我们测量了一天中几个小时内的温度。我们想知道即使在未测量的时候,温度的趋势。然而,这些时间是在我们的测量开始时间和结束时间之间:
import numpy as np

Time = np.array([6, 8, 11, 14, 16, 18, 19])
Temp = np.array([4, 7, 10, 12, 11.5, 9, 7])
  1. 现在,我们将展示一天中几个时间点的温度:
import matplotlib.pyplot as plt
plt.figure()
plt.plot(Time, Temp, 'bo')
plt.xlabel("Time")
plt.ylabel("Temp")
plt.title('Temperature versus time')
plt.show()

以下图表是生成的:

图片

如果我们分析图表,可以注意到数据可以由以下方程式表示的二次多项式建模的曲线模式:

图片

未知系数 β[0]、β[1] 和 β[2] 通过减小平方和的值来估计。这是通过最小化数据与模型之间的偏差来实现的,直到其最低值(最小二乘拟合)。

  1. 让我们计算多项式系数:
beta = np.polyfit(Time, Temp, 2)

numpy.polyfit() 函数返回一个多项式系数,该多项式的次数为 n(由我们给出),是数据最佳拟合的。函数返回的系数按降幂排列(最高次幂首先),如果 n 是多项式的次数,其长度为 n+1

  1. 在创建模型后,让我们验证它实际上是否适合我们的数据。为此,使用模型在均匀分布的时间点上评估多项式。要评估指定点上的模型,我们可以使用 poly1d() 函数。此函数返回一个多项式在由我们提供的点上的值。输入参数是一个长度为 n+1 的向量,其元素是按多项式降幂排列的系数:
p = np.poly1d(beta)

如您在即将出现的图表中看到的,这接近输出值。如果我们想让它更接近,我们需要增加多项式的次数。

  1. 现在我们可以将原始数据和模型绘制在同一张图上:
xp = np.linspace(6, 19, 100)
plt.figure()
plt.plot(Time, Temp, 'bo', xp, p(xp), '-')
plt.show()

打印出以下图表:

图片

如果我们分析图表,我们可以看到曲线充分地拟合了我们的数据。这个模型比简单的线性回归模型更好地拟合了数据。在回归分析中,保持模型阶数尽可能低是很重要的。在第一次分析中,我们将模型保持为一阶多项式。如果这还不满意,那么就尝试二阶多项式。使用更高阶的多项式可能导致错误的评估。

它是如何工作的...

当线性回归不够好时,应使用多项式回归。在多项式回归中,我们采用一种模型,其中一些预测变量以等于或大于二的次数出现,以曲线拟合数据。多项式回归通常用于变量之间的关系看起来是曲线的情况。

更多内容...

我们应该在多项式的哪个次数上停止?这取决于我们寻求的精度程度。多项式的次数越高,模型的精度就越高,但计算起来就越困难。此外,还需要验证找到的系数的显著性,但让我们直接进入正题。

参考内容

估计房价

是时候将我们的知识应用到现实世界的问题上了。让我们将这些原则应用到估计房价的问题上。这是用来理解回归的最受欢迎的例子之一,它也是一个很好的入门点。这是直观且相关的,因此在我们进行更复杂的机器学习操作之前,更容易理解这些概念。我们将使用带有 AdaBoost 的决策树回归器来解决这个问题。

准备工作

决策树是一种树,其中每个节点都做出一个简单的决策,这些决策有助于最终输出。叶节点代表输出值,分支代表基于输入特征做出的中间决策。AdaBoost代表自适应提升,这是一种用于提高另一个系统结果准确性的技术。这种方法通过加权求和结合不同版本的算法(称为弱学习器)的输出,以获得最终输出。在 AdaBoost 算法的每个阶段收集到的信息会反馈到系统中,以便后期的学习器专注于难以分类的训练样本。这样,它提高了系统的准确性。

使用 AdaBoost,我们在数据集上拟合一个回归器。我们计算误差,然后根据这个误差估计再次在相同的数据集上拟合回归器。我们可以将其视为回归器的微调,直到达到所需的精度。您得到一个包含影响房价的各种参数的数据集。我们的目标是估计这些参数与房价之间的关系,以便我们可以使用这些参数来估计给定未知输入参数的价格。

如何做到这一点...

让我们看看如何在 Python 中估计房价:

  1. 创建一个名为 housing.py 的新文件,并添加以下行:
import numpy as np
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import AdaBoostRegressor
from sklearn import datasets
from sklearn.metrics import mean_squared_error, explained_variance_score
from sklearn.utils import shuffle
import matplotlib.pyplot as plt
  1. 有一个标准的住房数据集,人们通常用它来开始机器学习。您可以在 archive.ics.uci.edu/ml/machine-learning-databases/housing/ 下载它。我们将使用数据集的一个略微修改版本,该版本与代码文件一起提供。

    好事是 scikit-learn 提供了一个直接加载数据集的函数:

housing_data = datasets.load_boston()

每个数据点都有 12 个影响房价的输入参数。您可以使用 housing_data.data 访问输入数据,并使用 housing_data.target 访问相应的价格。以下属性是可用的:

  • crim:城镇人均犯罪率

  • zn:用于超过 25,000 平方英尺地块的住宅用地比例

  • indus:城镇非零售商业面积的比例

  • chas:查尔斯河虚拟变量(= 1 如果地块边界是河流;否则为 0)

  • nox:一氧化氮浓度(每千万分之一)

  • rm:每套住宅的平均房间数

  • age:1940 年前建造的业主自住单元比例

  • dis:到五个波士顿就业中心的加权距离

  • rad:到达放射状高速公路的指数

  • tax:每 $10,000 的全额物业税税率

  • ptratio:城镇的师生比例

  • lstat:人口中低阶层百分比

  • target:业主自住房屋的中值(单位:$1000)

在这些参数中,target 是响应变量,而其他 12 个变量是可能的预测变量。分析的目标是拟合一个回归模型,以最好地解释 target 的变化。

  1. 让我们将这些分为输入和输出。为了使这独立于数据的顺序,让我们也对其进行打乱:
X, y = shuffle(housing_data.data, housing_data.target, random_state=7)

sklearn.utils.shuffle() 函数以一致的方式打乱数组或稀疏矩阵,以进行集合的随机排列。打乱数据可以减少方差,并确保模式保持一般性,减少过拟合。random_state 参数控制我们如何打乱数据,以便我们可以得到可重复的结果。

  1. 让我们将数据分为训练集和测试集。我们将分配 80% 用于训练,20% 用于测试:
num_training = int(0.8 * len(X))
X_train, y_train = X[:num_training], y[:num_training]
X_test, y_test = X[num_training:], y[num_training:]

记住,机器学习算法通过使用有限的数据集来训练模型。在训练阶段,模型根据其对训练集的预测进行评估。但算法的目标是生成一个模型,该模型可以预测以前未见过的观察结果,换句话说,它能够从已知数据和未知数据开始,通过泛化问题。因此,数据被分为两个数据集:训练集和测试集。训练集用于训练模型,而测试集用于验证系统泛化的能力。

  1. 我们现在已准备好拟合一个决策树回归模型。让我们选择一个最大深度为 4 的树,这意味着我们不让树变得任意深:
dt_regressor = DecisionTreeRegressor(max_depth=4)
dt_regressor.fit(X_train, y_train)

DecisionTreeRegressor 函数已被用于构建决策树回归器。

  1. 让我们也用 AdaBoost 拟合决策树回归模型:
ab_regressor = AdaBoostRegressor(DecisionTreeRegressor(max_depth=4), n_estimators=400, random_state=7)
ab_regressor.fit(X_train, y_train)

AdaBoostRegressor 函数已被用于比较结果,以查看 AdaBoost 真正如何提升决策树回归器的性能。

  1. 让我们来评估决策树回归器的性能:
y_pred_dt = dt_regressor.predict(X_test)
mse = mean_squared_error(y_test, y_pred_dt)
evs = explained_variance_score(y_test, y_pred_dt)
print("#### Decision Tree performance ####")
print("Mean squared error =", round(mse, 2))
print("Explained variance score =", round(evs, 2))

首先,我们使用了 predict() 函数根据测试数据预测响应变量。接下来,我们计算了均方误差和解释方差。均方误差 是实际值与预测值之间平方差的平均值,它是在所有输入数据点的实际值与预测值之间。解释方差 是一个指标,以比例的形式表示,表明我们的数据中有多少可变性是由所讨论的模型解释的。

  1. 现在,让我们评估 AdaBoost 的性能:
y_pred_ab = ab_regressor.predict(X_test)
mse = mean_squared_error(y_test, y_pred_ab)
evs = explained_variance_score(y_test, y_pred_ab)
print("#### AdaBoost performance ####")
print("Mean squared error =", round(mse, 2))
print("Explained variance score =", round(evs, 2))

这里是终端上的输出:

#### Decision Tree performance ####
Mean squared error = 14.79
Explained variance score = 0.82

#### AdaBoost performance ####
Mean squared error = 7.54
Explained variance score = 0.91

当我们使用 AdaBoost 时,如前所述的输出所示,误差更低,方差分数更接近 1。

它是如何工作的...

DecisionTreeRegressor 构建决策树回归器。决策树用于根据多个输入变量预测响应或类别 yx1x2,…,xn。如果 y 是连续响应,则称为回归树,如果 y 是分类的,则称为分类树。该算法基于以下程序:我们在树的每个节点处查看输入 x[i] 的值,并根据答案继续向左或向右分支。当我们到达一个叶子节点时,我们将找到预测值。在回归树中,我们试图将数据空间划分为很小的部分,在每个部分上装备一个简单的不同模型。树的非叶子部分只是找出我们将使用哪个模型进行预测的方法。

回归树是由一系列节点组成的,这些节点将根分支分割成两个子分支。这种细分继续级联。每个新的分支,然后,可以进入另一个节点,或者保持为叶子节点,带有预测值。

还有更多...

AdaBoost 回归器是一个元估计器,它首先在真实数据集上装备一个回归器,然后在同一数据集上添加额外的回归器副本,但实例的权重会根据当前预测的错误进行调整。因此,连续的回归器会关注困难案例。这将帮助我们比较结果,并看到 AdaBoost 真正如何提高决策树回归器的性能。

参考信息

计算特征的相对重要性

所有特征都同等重要吗?在这种情况下,我们使用了 13 个输入特征,它们都对模型做出了贡献。然而,这里的一个重要问题是,我们如何知道哪些特征更重要? 显然,并非所有特征都对输出做出了同等贡献。如果我们想稍后丢弃其中的一些,我们需要知道哪些特征相对不那么重要。Scikit-learn 提供了这种功能。

准备工作

让我们计算特征的相对重要性。特征重要性提供了一个衡量标准,它表示每个特征在模型构建中的价值。一个属性被用来构建模型的次数越多,其相对重要性就越大。这种重要性会针对数据集中的每个属性显式计算,允许你将属性相互分类和比较。特征重要性是模型中的一个属性(feature_importances_)。

如何实现

让我们看看如何计算特征的相对重要性:

  1. 让我们看看如何提取它。将以下行添加到 housing.py 文件中:
DTFImp= dt_regressor.feature_importances_
DTFImp= 100.0 * (DTFImp / max(DTFImp))
index_sorted = np.flipud(np.argsort(DTFImp))
pos = np.arange(index_sorted.shape[0]) + 0.5

回归器对象有一个可调用的 feature_importances_ 方法,它给出了每个特征的相对重要性。为了比较结果,重要性值已经被归一化。然后,我们将索引值排序并颠倒顺序,以便它们按重要性降序排列。最后,为了显示目的,x 轴上标签的位置已经被居中。

  1. 为了可视化结果,我们将绘制条形图:
plt.figure()
plt.bar(pos, DTFImp[index_sorted], align='center')
plt.xticks(pos, housing_data.feature_names[index_sorted])
plt.ylabel('Relative Importance')
plt.title("Decision Tree regressor")
plt.show()
  1. 我们只从 feature_importances_ 方法中获取值,并将它们缩放到 0 到 100 之间。让我们看看以下输出中基于决策树的回归器会得到什么:

因此,决策树回归器表示最重要的特征是 RM。

  1. 现在,我们对 AdaBoost 模型执行类似的程序:
ABFImp= ab_regressor.feature_importances_ 
ABFImp= 100.0 * (ABFImp / max(ABFImp))
index_sorted = np.flipud(np.argsort(ABFImp))
pos = np.arange(index_sorted.shape[0]) + 0.5
  1. 为了可视化结果,我们将绘制条形图:
plt.figure()
plt.bar(pos, ABFImp[index_sorted], align='center')
plt.xticks(pos, housing_data.feature_names[index_sorted])
plt.ylabel('Relative Importance')
plt.title("AdaBoost regressor")
plt.show()

让我们看看 AdaBoost 在以下输出中说了什么:

图片

根据 AdaBoost,最重要的特征是 LSTAT。实际上,如果你在这组数据上构建各种回归器,你会发现最重要的特征实际上是 LSTAT。这显示了使用基于决策树的回归器与 AdaBoost 结合的优势。

工作原理...

特征重要性提供了一个衡量指标,表示每个特征在模型构建中的价值。一个属性在构建模型中使用的越多,其相对重要性就越大。在这个菜谱中,使用了feature_importances_属性来从模型中提取特征的相对重要性。

更多内容...

相对重要性返回每个特征在决策树构建中的效用。一个属性在决策树中用于预测的越多,其相对重要性就越大。这种重要性是针对数据集中的每个属性显式计算的,允许你分类和比较属性。

参考资料链接

估计自行车需求分布

让我们使用不同的回归方法来解决自行车需求分布问题。我们将使用随机森林回归器来估计输出值。随机森林是一组决策树。这基本上使用由数据集的各种子集构建的决策树集合,然后使用平均化来提高整体性能。

准备工作

我们将使用提供的bike_day.csv文件。这个文件也可以在archive.ics.uci.edu/ml/datasets/Bike+Sharing+Dataset找到。这个数据集有 16 列。前两列对应序列号和实际日期,所以我们不会用它们来进行分析。最后三列对应不同的输出类型。最后一列是第十四列和第十五列值的总和,所以当我们构建模型时可以省略这两列。 让我们继续看看如何在 Python 中实现这一点。我们将逐行分析代码以理解每个步骤。

如何操作...

让我们看看如何估计自行车需求分布:

  1. 我们首先需要导入几个新的包,如下所示:
import csv
import numpy as np
  1. 我们正在处理 CSV 文件,因此 CSV 包对于处理这些文件很有用。让我们将数据导入 Python 环境:
filename="bike_day.csv"
file_reader = csv.reader(open(filename, 'r'), delimiter=',')
X, y = [], []
for row in file_reader:
    X.append(row[2:13])
    y.append(row[-1])

这段代码只是从 CSV 文件中读取了所有数据。csv.reader() 函数返回一个 reader 对象,它将遍历给定 CSV 文件中的行。从 CSV 文件中读取的每一行都返回为一个字符串列表。因此,返回了两个列表:Xy。我们已经将数据与输出值分开,并返回了它们。现在我们将提取特征名称:

feature_names = np.array(X[0])

特征名称在我们在图上显示它们时很有用。因此,我们必须从 X 和 y 中移除第一行,因为它们是特征名称:

X=np.array(X[1:]).astype(np.float32)
y=np.array(y[1:]).astype(np.float32)

我们还将这两个列表转换成了两个数组。

  1. 让我们打乱这两个数组,使它们与文件中数据排列的顺序无关:
from sklearn.utils import shuffle
X, y = shuffle(X, y, random_state=7)  
  1. 正如我们之前所做的那样,我们需要将数据分为训练数据和测试数据。这次,让我们用 90%的数据进行训练,剩下的 10%用于测试:
num_training = int(0.9 * len(X))
X_train, y_train = X[:num_training], y[:num_training]
X_test, y_test = X[num_training:], y[num_training:]
  1. 让我们继续训练回归器:
from sklearn.ensemble import RandomForestRegressor
rf_regressor = RandomForestRegressor(n_estimators=1000, max_depth=10, min_samples_split=2)
rf_regressor.fit(X_train, y_train)

RandomForestRegressor() 函数构建了一个随机森林回归器。在这里,n_estimators 指的是估计器的数量,即我们想在随机森林中使用的决策树的数量。max_depth 参数指的是每棵树的最大深度,而 min_samples_split 参数指的是在树中分裂一个节点所需的数据样本数量。

  1. 让我们来评估随机森林回归器的性能:
y_pred = rf_regressor.predict(X_test)
from sklearn.metrics import mean_squared_error, explained_variance_score
mse = mean_squared_error(y_test, y_pred)
evs = explained_variance_score(y_test, y_pred)
print( "#### Random Forest regressor performance ####")
print("Mean squared error =", round(mse, 2))
print("Explained variance score =", round(evs, 2))

返回以下结果:

#### Random Forest regressor performance ####
Mean squared error = 357864.36
Explained variance score = 0.89
  1. 让我们提取特征的重要性:
RFFImp= rf_regressor.feature_importances_ 
RFFImp= 100.0 * (RFFImp / max(RFFImp))
index_sorted = np.flipud(np.argsort(RFFImp))
pos = np.arange(index_sorted.shape[0]) + 0.5

为了可视化结果,我们将绘制一个条形图:

import matplotlib.pyplot as plt
plt.figure()
plt.bar(pos, RFFImp[index_sorted], align='center')
plt.xticks(pos, feature_names[index_sorted])
plt.ylabel('Relative Importance')
plt.title("Random Forest regressor")
plt.show()

下面的输出是绘制的:

图片

看起来温度是控制自行车租赁的最重要因素。

它是如何工作的...

随机森林是由一组简单的回归器(决策树)组成的特殊回归器,这些回归器表示为独立同分布的随机向量,其中每个向量选择单个树的平均预测。这种结构在回归精度方面取得了显著改进,并属于集成学习的范畴。随机森林中的每一棵树都是从训练集数据的一个随机子集中构建和训练的。因此,这些树不使用完整的数据集,并且对于每个节点,不再选择最佳属性,而是从一组随机选择的属性中选择最佳属性。

随机性是一个因素,它成为回归器构建的一部分,目的是增加它们的多样性,从而减少相关性。随机森林最终返回的结果只是不同树在回归情况下返回的数值结果的平均值,或者在使用随机森林算法进行分类时,返回由最多树返回的类别。

还有更多…

让我们看看当你将数据集中的第十四列和第十五列包含进来时会发生什么。在特征重要性图中,除了这两个特征之外,其他所有特征都必须归零。原因是输出可以通过简单地累加第十四列和第十五列来获得,因此算法不需要任何其他特征来计算输出。在for循环内做出以下更改(其余代码保持不变):

X.append(row[2:15])

如果你现在绘制特征重要性图,你会看到以下内容:

如预期的那样,它表明只有这两个特征是重要的。直观上这是有道理的,因为最终输出是这两个特征的简单累加。因此,这两个变量与输出值之间存在直接关系。因此,回归器说它不需要任何其他变量来预测输出。这是一个在数据集中消除冗余变量的极其有用的工具。但这并不是与先前模型唯一的区别。如果我们分析模型的表现,我们可以看到显著的改进:

#### Random Forest regressor performance ####
Mean squared error = 22552.26
Explained variance score = 0.99

因此,我们解释了 99%的方差:一个非常好的结果。

另有一个名为bike_hour.csv的文件,其中包含有关每小时自行车共享情况的数据。我们需要考虑第 3 列到第 14 列,所以让我们在代码中做出以下更改(其余代码保持不变):

filename="bike_hour.csv"
file_reader = csv.reader(open(filename, 'r'), delimiter=',')
X, y = [], []
for row in file_reader:
    X.append(row[2:14])
    y.append(row[-1])

如果你运行新的代码,你会看到回归器的性能显示如下:

#### Random Forest regressor performance ####
Mean squared error = 2613.86
Explained variance score = 0.92

特征重要性图将看起来如下:

这表明一天中的小时是最重要的特征,如果你这样想的话,直观上是有道理的!下一个重要的特征是温度,这与我们之前的分析一致。

参见

第二章:构建分类器

在本章中,我们将涵盖以下配方:

  • 构建简单的分类器

  • 构建逻辑回归分类器

  • 构建朴素贝叶斯分类器

  • 将数据集拆分为训练集和测试集

  • 使用交叉验证评估准确性

  • 可视化混淆矩阵

  • 提取性能报告

  • 根据特征评估汽车

  • 提取验证曲线

  • 提取学习曲线

  • 估算收入区间

  • 预测葡萄酒质量

  • 新闻组热门话题分类

技术要求

要在本章中处理配方,你需要以下文件(可在 GitHub 上找到):

  • simple_classifier.py

  • logistic_regression.py

  • naive_bayes.py

  • data_multivar.txt

  • splitting_dataset.py

  • confusion_matrix.py

  • `performance_report.py`

  • car.py

  • car.data.txt

  • income.py

  • adult.data.txt

  • wine.quality.py

  • wine.txt

  • post.classification

简介

在机器学习领域,分类指的是使用数据的特征将其分为一定数量的类别的过程。这与我们在第一章“监督学习领域”中讨论的回归不同,其中输出是一个实数。监督学习分类器使用标记的训练数据构建模型,然后使用此模型对未知数据进行分类。

分类器可以是任何实现分类的算法。在简单的情况下,分类器可以是一个直接的数学函数。在更现实的情况下,分类器可以采取非常复杂的形式。在学习过程中,我们将看到分类可以是二元的,其中我们将数据分为两个类别,或者它是多类的,其中我们将数据分为两个以上的类别。为处理分类问题而设计的数学技术往往处理两个类别,因此我们以不同的方式扩展它们以处理多类问题。

评估分类器的准确性对于机器学习至关重要。我们需要了解的是,我们如何使用可用的数据,并一窥模型在现实世界中的表现。在本章中,我们将探讨处理所有这些内容的配方。

构建简单的分类器

分类器是一个具有某些特征的系统,允许你识别所检查样本的类别。在不同的分类方法中,组被称为类别。分类器的目标是建立分类标准以最大化性能。分类器的性能通过评估泛化能力来衡量。泛化意味着将正确的类别分配给每个新的实验观察结果。这些类别被识别的方式区分了不同可用的方法。

准备工作

分类器根据从一系列样本(数据集)中提取的知识来识别新目标对象的类别。从一个数据集开始,分类器提取一个模型,然后使用该模型对新实例进行分类。

如何做到这一点...

让我们看看如何使用一些训练数据构建一个简单的分类器:

  1. 我们将使用已经提供给你作为参考的simple_classifier.py文件。首先,我们导入numpymatplotlib.pyplot包,就像我们在第一章,“监督学习领域”中所做的那样,然后我们创建一些样本数据:
import numpy as np
import matplotlib.pyplot as plt

X = np.array([[3,1], [2,5], [1,8], [6,4], [5,2], [3,5], [4,7], [4,-1]])
  1. 让我们给这些点分配一些标签:
y = [0, 1, 1, 0, 0, 1, 1, 0]
  1. 由于我们只有两个类别,y列表包含 0 和 1。一般来说,如果你有N个类别,那么y中的值将范围从 0 到N-1。让我们根据标签将数据分开成类别:
class_0 = np.array([X[i] for i in range(len(X)) if y[i]==0])
class_1 = np.array([X[i] for i in range(len(X)) if y[i]==1])
  1. 为了了解我们的数据,让我们按照以下方式绘制它:
plt.figure()
plt.scatter(class_0[:,0], class_0[:,1], color='black', marker='s')
plt.scatter(class_1[:,0], class_1[:,1], color='black', marker='x')
plt.show()

这是一个散点图,我们使用方块和交叉来绘制点。在这个上下文中,marker参数指定了你想要使用的形状。我们用方块表示class_0中的点,用交叉表示class_1中的点。如果你运行这段代码,你会看到以下输出:

图片

  1. 在前两行中,我们只是使用Xy之间的映射来创建两个列表。如果你被要求视觉检查数据点并绘制一条分隔线,你会怎么做?你会在它们之间简单地画一条线。让我们来做这件事:
line_x = range(10)
line_y = line_x
  1. 我们刚刚使用数学方程y = x创建了一条线。让我们按照以下方式绘制它:
plt.figure()
plt.scatter(class_0[:,0], class_0[:,1], color='black', marker='s')
plt.scatter(class_1[:,0], class_1[:,1], color='black', marker='x')
plt.plot(line_x, line_y, color='black', linewidth=3)
plt.show()
  1. 如果你运行这段代码,你应该看到以下输出:

图片

前面的例子展示了在两个类别之间构建分隔线是如何简单的。在这个简单的例子中,这个操作很容易,但在许多情况下,在两个类别之间构建分隔线可能非常困难。

它是如何工作的...

在这个例子中,我们展示了构建分类器是多么简单。我们从平面上尽可能多的点(x, y)的识别对开始。因此,我们给这些点中的每一个分配了一个类别(0,1),以便将它们分成两组。为了理解这些点的空间排列,我们通过为每个类别关联不同的标记来可视化它们。最后,为了将这两组分开,我们绘制了y = x方程的线。

还有更多...

我们使用以下规则构建了一个简单的分类器——输入点(a, b)属于class_0当且仅当a大于或等于b;否则,它属于class_1。如果你逐个检查这些点,你会看到这实际上是正确的。就是这样!你刚刚构建了一个可以分类未知数据的线性分类器。它是一个线性分类器,因为分隔线是直线。如果它是曲线,那么它就变成了非线性分类器。

这种形式工作得很好,因为点的数量有限,我们可以直观地检查它们。如果有成千上万的点呢?我们将如何泛化这个过程?让我们在下一个菜谱中讨论这个问题。

参考阅读

构建逻辑回归分类器

尽管名称中包含 regression 一词,但逻辑回归实际上用于分类目的。给定一组数据点,我们的目标是构建一个模型,该模型可以在我们的类别之间绘制线性边界。它通过解决从训练数据导出的一组方程来提取这些边界。在这个菜谱中,我们将构建一个逻辑回归分类器。

准备工作

逻辑回归是一种非线性回归模型,当因变量是二元时使用。目的是确定一个观测值可以生成因变量一个或另一个值的概率;它也可以根据其特征将观测值分类到两个类别中。

如何实现它……

让我们看看如何构建一个逻辑回归分类器:

  1. 让我们看看如何在 Python 中实现这个功能。我们将使用提供的 logistic_regression.py 文件作为参考。假设你已经导入了必要的包,让我们创建一些样本数据,以及相应的训练标签:
import numpy as np
from sklearn import linear_model
import matplotlib.pyplot as plt
X = np.array([[4, 7], [3.5, 8], [3.1, 6.2], [0.5, 1], [1, 2], [1.2, 1.9], [6, 2], [5.7, 1.5], [5.4, 2.2]])
y = np.array([0, 0, 0, 1, 1, 1, 2, 2, 2])

在这里,我们假设我们有三个类别(012)。

  1. 让我们初始化逻辑回归分类器:
classifier = linear_model.LogisticRegression(solver='lbfgs', C=100)

对于前面的函数,可以指定许多输入参数,但其中两个重要的参数是 solverCsolver 参数指定算法将用于解决方程组的 solver 类型。C 参数控制正则化强度。较低的值表示较高的正则化强度。

  1. 让我们训练分类器:
classifier.fit(X, y)
  1. 让我们绘制数据点和边界。为此,首先,我们需要定义绘图的范围,如下所示:
x_min, x_max = min(X[:, 0]) - 1.0, max(X[:, 0]) + 1.0
y_min, y_max = min(X[:, 1]) - 1.0, max(X[:, 1]) + 1.0

前面的值表示我们想在图中使用的值的范围。这些值通常从我们数据中的最小值到最大值。为了清晰起见,我们在前面的行中添加了一些缓冲区,例如 1.0

  1. 为了绘制边界,我们需要在点网格上评估函数并绘制它。让我们继续定义网格:
# denotes the step size that will be used in the mesh grid
step_size = 0.01

# define the mesh grid
x_values, y_values = np.meshgrid(np.arange(x_min, x_max, step_size), np.arange(y_min, y_max, step_size))

x_valuesy_values 变量包含函数将被评估的点网格。

  1. 让我们计算分类器对所有这些点的输出:
# compute the classifier output
mesh_output = classifier.predict(np.c_[x_values.ravel(), y_values.ravel()])

# reshape the array
mesh_output = mesh_output.reshape(x_values.shape)
  1. 让我们使用彩色区域绘制边界:
# Plot the output using a colored plot 
plt.figure()

# choose a color scheme you can find all the options 
# here: http://matplotlib.org/examples/color/colormaps_reference.html
plt.pcolormesh(x_values, y_values, mesh_output, cmap=plt.cm.gray)

这基本上是一个 3D 绘图器,它接受 2D 点和相关的值,使用颜色方案绘制不同的区域。

  1. 让我们在图上叠加训练点:
# Overlay the training points on the plot 
plt.scatter(X[:, 0], X[:, 1], c=y, s=80, edgecolors='black', linewidth=1, cmap=plt.cm.Paired)

# specify the boundaries of the figure
plt.xlim(x_values.min(), x_values.max())
plt.ylim(y_values.min(), y_values.max())

# specify the ticks on the X and Y axes
plt.xticks((np.arange(int(min(X[:, 0])-1), int(max(X[:, 0])+1), 1.0)))
plt.yticks((np.arange(int(min(X[:, 1])-1), int(max(X[:, 1])+1), 1.0)))

plt.show()

在这里,plt.scatter 在二维图上绘制点。X[:, 0] 指定我们应该取沿着 0 轴(在我们的情况下是x轴)的所有值,而 X[:, 1] 指定轴 1(y轴)。c=y 参数表示颜色序列。我们使用目标标签通过 cmap 来映射颜色。基本上,我们想要基于目标标签的不同颜色。因此,我们使用 y 作为映射。显示图形的界限是通过 plt.xlimplt.ylim 来设置的。为了标记轴上的值,我们需要使用 plt.xticksplt.yticks。这些函数通过值标记轴,这样我们更容易看到点的位置。在先前的代码中,我们希望刻度位于最小值和最大值之间,并有一个单位宽度的缓冲区。同时,我们希望这些刻度是整数。因此,我们使用 int() 函数来四舍五入值。

  1. 如果你运行这段代码,你应该看到以下输出:

  1. 让我们看看 C 参数如何影响我们的模型。C 参数表示误分类的惩罚。如果我们将其设置为 1.0,我们将得到以下结果:

  1. 如果我们将 C 设置为 10000,我们将得到以下结果:

随着 C 的增加,误分类的惩罚更高。因此,边界变得更加优化。

它是如何工作的...

逻辑回归是监督学习算法家族中的分类方法。使用统计方法,逻辑回归允许我们生成一个结果,实际上它代表了一个给定输入值属于给定类的概率。在二项逻辑回归问题中,输出属于某一类的概率将是 P,而属于另一类的概率将是 1-P(其中 P 是一个介于 0 和 1 之间的数字,因为它表示概率)。

逻辑回归使用逻辑函数来确定输入值的分类。也称为Sigmoid函数,逻辑函数是一个 S 形曲线,可以将任何实数值映射到 0 到 1 之间的值(不包括极端值)。它可以由以下方程描述:

这个函数将实数值转换为 0 到 1 之间的数字。

更多内容...

为了获得用概率术语表示的逻辑回归方程,我们需要在逻辑回归方程中包含概率:

回想一下,e 函数是自然对数(ln)的逆函数,我们可以写出:

此函数被称为 logit 函数。另一方面,logit 函数允许我们将概率(因此,一个介于 0 和 1 之间的值)与实数范围的全部关联起来。它是一个链接函数,代表逻辑函数的逆函数。

参考信息

构建朴素贝叶斯分类器

分类器解决的是在较大集合中识别具有某些特征的个人子集的问题,可能使用一组称为先验(训练集)的个人子集。朴素贝叶斯分类器是一种监督学习分类器,它使用贝叶斯定理来构建模型。在本教程中,我们将构建一个朴素贝叶斯分类器。

准备工作

贝叶斯分类器的潜在原理是,某些个人基于某些观察结果,以一定的概率属于感兴趣的类别。这个概率基于这样的假设,即观察到的特征可以是相互依赖的或相互独立的;在第二种情况下,贝叶斯分类器被称为朴素,因为它假设在感兴趣的类别中,特定特征的呈现或缺失与其他特征的呈现或缺失无关,这大大简化了计算。让我们继续构建朴素贝叶斯分类器。

如何做到这一点…

让我们看看如何构建朴素贝叶斯分类器:

  1. 我们将使用提供的 naive_bayes.py 作为参考。让我们导入一些库:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.naive_bayes import GaussianNB
  1. 您提供了一个 data_multivar.txt 文件。这个文件包含我们将在这里使用的数据。每行包含逗号分隔的数值数据。让我们从这个文件中加载数据:
input_file = 'data_multivar.txt'
X = []
y = []
with open(input_file, 'r') as f:
    for line in f.readlines():
        data = [float(x) for x in line.split(',')]
        X.append(data[:-1])
        y.append(data[-1])
X = np.array(X)
y = np.array(y)

我们现在已将输入数据加载到 X 中,并将标签加载到 y 中。有四个标签:0、1、2 和 3。

  1. 让我们构建朴素贝叶斯分类器:
classifier_gaussiannb = GaussianNB()
classifier_gaussiannb.fit(X, y)
y_pred = classifier_gaussiannb.predict(X)

gauusiannb 函数指定高斯朴素贝叶斯模型。

  1. 让我们计算分类器的 accuracy 测量值:
accuracy = 100.0 * (y == y_pred).sum() / X.shape[0]
print("Accuracy of the classifier =", round(accuracy, 2), "%")

返回以下准确度:

Accuracy of the classifier = 99.5 %
  1. 让我们绘制数据和边界。我们将使用在先前的教程中遵循的程序,构建逻辑回归分类器
x_min, x_max = min(X[:, 0]) - 1.0, max(X[:, 0]) + 1.0
y_min, y_max = min(X[:, 1]) - 1.0, max(X[:, 1]) + 1.0

# denotes the step size that will be used in the mesh grid
step_size = 0.01

# define the mesh grid
x_values, y_values = np.meshgrid(np.arange(x_min, x_max, step_size), np.arange(y_min, y_max, step_size))

# compute the classifier output
mesh_output = classifier_gaussiannb.predict(np.c_[x_values.ravel(), y_values.ravel()])

# reshape the array
mesh_output = mesh_output.reshape(x_values.shape)

# Plot the output using a colored plot 
plt.figure()

# choose a color scheme 
plt.pcolormesh(x_values, y_values, mesh_output, cmap=plt.cm.gray)

# Overlay the training points on the plot 
plt.scatter(X[:, 0], X[:, 1], c=y, s=80, edgecolors='black', linewidth=1, cmap=plt.cm.Paired)

# specify the boundaries of the figure
plt.xlim(x_values.min(), x_values.max())
plt.ylim(y_values.min(), y_values.max())

# specify the ticks on the X and Y axes
plt.xticks((np.arange(int(min(X[:, 0])-1), int(max(X[:, 0])+1), 1.0)))
plt.yticks((np.arange(int(min(X[:, 1])-1), int(max(X[:, 1])+1), 1.0)))

plt.show()

您应该看到以下内容:

图片

在这里对边界的限制没有线性要求。在前面的配方中, 构建逻辑回归分类器,我们使用了所有数据进行训练。在机器学习中,有一个好的做法是训练和测试数据不重叠。理想情况下,我们需要一些未使用的数据进行测试,以便我们可以准确估计模型在未知数据上的表现。scikit-learn 中有一个很好的处理这个问题的方案,如下一个配方所示。

它是如何工作的...

贝叶斯分类器 是基于贝叶斯定理应用的分类器。这个分类器需要有关问题的先验和条件概率的知识;这些量通常未知,但通常是可估计的。如果可以获取定理中涉及的概率的可靠估计,贝叶斯分类器通常是可靠的,并且可能是紧凑的。

给定事件 (E) 发生的概率,是事件本身有利情况的数量 (s) 与可能情况总数 (n) 的比率,前提是所有考虑的情况都是等可能的。这可以用以下公式更好地表示:

图片

给定两个事件, AB,如果这两个事件是独立的(一个事件的发生不影响另一个事件发生的概率),那么事件的联合概率等于 AB 的概率乘积:

图片

如果两个事件是相关的(即,一个事件的发生会影响另一个事件发生的概率),那么相同的规则可能适用,前提是 P(B | A) 是在事件 B 发生的情况下事件 A 发生的概率。这个条件引入了条件概率,我们现在将深入探讨:

图片

在事件 B 发生的条件下,计算事件 A 发生的概率被称为 条件概率,用 P(A | B) 表示。它是使用以下公式计算的:

图片

AB 为两个相关事件,正如我们所述,它们之间的联合概率是使用以下公式计算的:

图片

或者,类似地,我们可以使用以下公式:

图片

通过观察这两个公式,我们看到它们有第一个相等的成员。这表明即使第二个成员也相等,所以可以写出以下方程:

图片

通过解这些关于条件概率的方程,我们得到以下结果:

图片

提出的公式代表了贝叶斯定理的数学表述。使用哪一个取决于我们寻找什么。

还有更多...

1763 年,托马斯·贝叶斯牧师在英国发表了一篇文章;这篇文章因其含义而闻名。根据文章,对现象的预测不仅取决于科学家从实验中获得的观察结果,还取决于他自己对研究现象的看法和理解,甚至在开始实验之前。这些前提在 20 世纪由一些杰出的学者发展,如布鲁诺·德·菲尼蒂(《预测:其逻辑法则,其主观来源》,1937 年),L J 萨维奇(《统计学基础再思考》,1959 年)等人。

参见

分割数据集以进行训练和测试

让我们看看如何正确地将我们的数据分割成训练集和测试集。正如我们在第一章中提到的,监督学习领域,在构建线性回归器配方中,当我们构建机器学习模型时,我们需要一种方法来验证我们的模型,以检查其是否在令人满意的水平上运行。为此,我们需要将我们的数据分成两组——一个训练集和一个测试集。训练集将用于构建模型,测试集将用于查看训练好的模型在未知数据上的表现。

在本配方中,我们将学习如何分割数据集以进行训练和测试阶段。

准备工作

基于机器学习的模型的基本目标是做出准确的预测。在使用模型进行预测之前,有必要评估模型的预测性能。为了估计模型预测的质量,有必要使用你以前从未见过的数据。在相同的数据上训练预测模型并进行测试是一种方法上的错误:一个仅仅对刚刚看到的样本标签进行分类的模型会有很高的分数,但无法预测新的数据类别。在这些条件下,模型的泛化能力会较低。

如何操作…

让我们看看如何分割数据集:

  1. 配方的第一部分与之前的配方类似,构建朴素贝叶斯分类器(加载Splitting_dataset.py文件):
import numpy as np
import matplotlib.pyplot as plt
from sklearn.naive_bayes import GaussianNB 

input_file = 'data_multivar.txt'

X = []
y = []
with open(input_file, 'r') as f:
    for line in f.readlines():
        data = [float(x) for x in line.split(',')]
        X.append(data[:-1])
        y.append(data[-1]) 

X = np.array(X)
y = np.array(y)

#Splitting the dataset for training and testing
from sklearn import model_selection
X_train, X_test, y_train, y_test = model_selection.train_test_split(X, y, test_size=0.25, random_state=5)

#Building the classifier
classifier_gaussiannb_new = GaussianNB()
classifier_gaussiannb_new.fit(X_train, y_train)

在这里,我们按照test_size参数指定的,分配了 25%的数据用于测试,剩余的 75%数据将用于训练。

  1. 让我们在测试数据上评估分类器:
y_test_pred = classifier_gaussiannb_new.predict(X_test)
  1. 让我们计算分类器的accuracy度量:
accuracy = 100.0 * (y_test == y_test_pred).sum() / X_test.shape[0]
print("Accuracy of the classifier =", round(accuracy, 2), "%")

打印出以下结果:

Accuracy of the classifier = 98.0 %
  1. 让我们在测试数据上绘制数据点和边界:
#Plot a classifier
#Define the data
X= X_test
y=y_test

# define ranges to plot the figure 
x_min, x_max = min(X[:, 0]) - 1.0, max(X[:, 0]) + 1.0
y_min, y_max = min(X[:, 1]) - 1.0, max(X[:, 1]) + 1.0

# denotes the step size that will be used in the mesh grid
step_size = 0.01

# define the mesh grid
x_values, y_values = np.meshgrid(np.arange(x_min, x_max, step_size), np.arange(y_min, y_max, step_size))

# compute the classifier output
mesh_output = classifier_gaussiannb_new.predict(np.c_[x_values.ravel(), y_values.ravel()])

# reshape the array
mesh_output = mesh_output.reshape(x_values.shape)

# Plot the output using a colored plot 
plt.figure()

# choose a color scheme
plt.pcolormesh(x_values, y_values, mesh_output, cmap=plt.cm.gray)

# Overlay the training points on the plot 
plt.scatter(X[:, 0], X[:, 1], c=y, s=80, edgecolors='black', linewidth=1, cmap=plt.cm.Paired)

# specify the boundaries of the figure
plt.xlim(x_values.min(), x_values.max())
plt.ylim(y_values.min(), y_values.max())

# specify the ticks on the X and Y axes
plt.xticks((np.arange(int(min(X[:, 0])-1), int(max(X[:, 0])+1), 1.0)))
plt.yticks((np.arange(int(min(X[:, 1])-1), int(max(X[:, 1])+1), 1.0)))

plt.show()
  1. 您应该看到以下内容:

它是如何工作的...

在这个菜谱中,我们使用scikit-learn库的train_test_split()函数来分割数据。这个函数将数组或矩阵分割成随机的训练和测试子集。将输入数据随机分割成训练和测试数据源确保了训练和测试数据源的数据分布相似。当不需要保留输入数据的顺序时,您可以选择此选项。

还有更多...

性能估计取决于所使用的数据。因此,简单地将数据随机分割成训练和测试集并不能保证结果具有统计学意义。在不同随机分割上重复评估以及计算性能的平均值和标准差可以创建一个更可靠的估计。

然而,即使在不同的随机分割上重复评估,也可能防止最复杂的数据在测试(或训练)阶段被分类。

参考阅读

使用交叉验证指标评估精度

交叉验证是机器学习中的一个重要概念。在前一个菜谱中,我们将数据分割成训练和测试数据集。然而,为了使其更加稳健,我们需要用不同的子集重复这个过程。如果我们只为特定的子集微调,我们可能会过度拟合模型。过度拟合是指我们将模型过度微调到数据集上,以至于它在未知数据上的表现不佳。我们希望我们的机器学习模型在未知数据上表现良好。在这个菜谱中,我们将学习如何使用交叉验证指标来评估模型精度。

准备工作…

当我们处理机器学习模型时,我们通常关注三个指标——精确度、召回率和 F1 分数。我们可以使用参数评分来获取所需性能指标。精确度是指正确分类的项目数占列表中所有项目总数的百分比。召回率是指检索到的项目数占训练列表中所有项目总数的百分比。

如何做到这一点…

让我们看看如何使用交叉验证指标来评估模型精度:

  1. 我们将使用在构建朴素贝叶斯分类器配方中使用的分类器(加载naive_bayes.py文件)。我们将从准确度度量开始:
from sklearn import model_selection
num_validations = 5
accuracy = model_selection.cross_val_score(classifier_gaussiannb,
        X, y, scoring='accuracy', cv=num_validations)
print "Accuracy: " + str(round(100*accuracy.mean(), 2)) + "%"
  1. 我们将使用前面的函数来计算精确度召回率F1分数:
f1 = model_selection.cross_val_score(classifier_gaussiannb,
 X, y, scoring='f1_weighted', cv=num_validations)
print "F1: " + str(round(100*f1.mean(), 2)) + "%"
precision = model_selection.cross_val_score(classifier_gaussiannb,
 X, y, scoring='precision_weighted', cv=num_validations)
print "Precision: " + str(round(100*precision.mean(), 2)) + "%"
recall = model_selection.cross_val_score(classifier_gaussiannb,
 X, y, scoring='recall_weighted', cv=num_validations)
print "Recall: " + str(round(100*recall.mean(), 2)) + "%"

它是如何工作的...

让我们考虑一个包含 100 个项目的测试数据集,其中 82 个对我们来说是有兴趣的。现在,我们希望我们的分类器为我们识别这 82 个项目。我们的分类器挑选出 73 个项目作为感兴趣的项目。在这些 73 个项目中,只有 65 个实际上是感兴趣的项目,其余的 8 个被错误分类。我们可以以下方式计算精确度:

  • 正确识别的数量 = 65

  • 识别的总数 = 73

  • 精确度 = 65 / 73 = 89.04%

为了计算召回率,我们使用以下方法:

  • 数据集中感兴趣项目的总数 = 82

  • 正确检索的项目数量 = 65

  • 召回率 = 65 / 82 = 79.26%

一个好的机器学习模型需要同时具有良好的精确度和良好的召回率。虽然很容易使其中一个达到 100%,但另一个指标会受到影响!我们需要同时保持这两个指标都很高。为了量化这一点,我们使用 F1 分数,它是精确度和召回率的组合。这实际上是精确度和召回率的调和平均数:

图片

在前面的情况下,F1 分数将如下:

图片

还有更多...

在交叉验证中,所有可用数据都按固定大小的组交替作为测试集和训练集使用。因此,每个模式要么被分类(至少一次),要么用于训练。然而,所获得的效果取决于特定的划分。因此,重复交叉验证几次可能是有用的,以便独立于特定的划分。

参见

可视化混淆矩阵

混淆矩阵是一个表格,我们用它来了解分类模型的性能。这有助于我们了解如何将测试数据分类到不同的类别。当我们想要微调我们的算法时,我们需要在做出这些更改之前了解数据是如何被错误分类的。有些类别比其他类别更差,混淆矩阵将帮助我们了解这一点。让我们看看以下内容:

图片

在前面的图表中,我们可以看到我们如何将数据分类到不同的类别中。理想情况下,我们希望所有非对角线元素都是 0。这将表示完美的分类!让我们考虑类别 0。总体而言,52 个项目实际上属于类别 0。如果我们把第一行的数字加起来,我们会得到 52。现在,其中 45 个项目被正确预测,但我们的分类器说其中 4 个属于类别 1,3 个属于类别 2。我们可以对剩余的 2 行也进行同样的分析。值得注意的是,有 11 个来自类别 1 的项目被错误分类为类别 0。这构成了这个类别中数据点的约 16%。这是一个我们可以用来优化我们模型的见解。

准备工作

混淆矩阵确定了分类错误的性质,因为我们的分类结果与真实数据进行了比较。在这个矩阵中,对角线单元格显示了正确分类的案例数量;所有其他单元格显示了错误分类的案例。

如何做到这一点...

让我们看看如何可视化混淆矩阵:

  1. 我们将使用您已经提供的 confusion_matrix.py 文件作为参考。让我们看看如何从我们的数据中提取混淆矩阵:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix

我们在这里使用一些样本数据。我们有 4 个类别,其值从 0 到 3。我们还有预测标签。我们使用 confusion_matrix 方法提取混淆矩阵并绘制它。

  1. 让我们继续定义这个函数:
# Show confusion matrix
def plot_confusion_matrix(confusion_mat):
    plt.imshow(confusion_mat, interpolation='nearest', cmap=plt.cm.Paired)
    plt.title('Confusion matrix')
    plt.colorbar()
    tick_marks = np.arange(4)
    plt.xticks(tick_marks, tick_marks)
    plt.yticks(tick_marks, tick_marks)
    plt.ylabel('True label')
    plt.xlabel('Predicted label')
    plt.show()

我们使用 imshow 函数来绘制混淆矩阵。函数中的其他一切都很简单!我们只是使用相关函数设置标题、颜色条、刻度和标签。tick_marks 参数从 0 到 3,因为我们数据集中有 4 个不同的标签。np.arange 函数给我们这个 numpy 数组。

  1. 让我们定义数据(真实和预测),然后我们将调用 confusion_matrix 函数:
y_true = [1, 0, 0, 2, 1, 0, 3, 3, 3]
y_pred = [1, 1, 0, 2, 1, 0, 1, 3, 3]
confusion_mat = confusion_matrix(y_true, y_pred)
plot_confusion_matrix(confusion_mat)
  1. 如果你运行前面的代码,你会看到以下内容:

对角线的颜色很强烈,我们希望它们很强烈。黑色表示零。非对角线空间中有几个灰色方块,表示错误分类。例如,当真实标签为 0 时,预测标签为 1,正如我们在第一行中看到的那样。实际上,所有错误分类都属于类别 1,因为在第二列中有 3 行非零。从矩阵中很容易看出这一点。

它是如何工作的...

混淆矩阵显示了模型做出的实际和预测分类的信息。这些系统的性能通过矩阵中的数据来评估。

下表显示了双类分类器的混淆矩阵:

预测为正 预测为负
实际为真 TP FN
实际为假 FP TN

混淆矩阵中的条目具有以下含义:

  • TP 表示一个实例被正确预测为正数的正确预测数量

  • FN 是一个实例被错误地预测为负的预测数量

  • FP 是一个实例被错误地预测为正的预测数量

  • TN 是一个实例被正确预测为负的预测数量

更多...

混淆矩阵显示了算法的性能。每一行返回实际类别的实例,而每一列返回预期类别的实例。混淆矩阵这一术语源于它使得容易看出系统是否混淆了两个类别。

参考信息

提取性能报告

使用交叉验证指标评估准确度 的配方中,我们计算了一些指标来衡量模型的准确度。让我们记住它的含义。准确度返回正确分类的百分比。精确度返回正确分类的正分类的百分比。召回率(灵敏度)返回测试集中被分类为正的正元素百分比。最后,在 F1 中,精确度和召回率都用于计算分数。在这个配方中,我们将学习如何提取性能报告。

准备工作

我们在 scikit-learn 中还有一个可以直接打印精确度、召回率和 F1 分数的函数。让我们看看如何做这个。

如何操作...

让我们看看如何提取性能报告:

  1. 将以下行添加到新的 Python 文件中(加载 performance_report.py 文件):
from sklearn.metrics import classification_report
y_true = [1, 0, 0, 2, 1, 0, 3, 3, 3]
y_pred = [1, 1, 0, 2, 1, 0, 1, 3, 3]
target_names = ['Class-0', 'Class-1', 'Class-2', 'Class-3']
print(classification_report(y_true, y_pred, target_names=target_names))
  1. 如果你运行此代码,你将在你的终端上看到以下内容:

图片

而不是单独计算这些指标,你可以直接使用前面的函数从你的模型中提取这些统计数据。

它是如何工作的...

在这个配方中,我们使用了 scikit-learn 库的 classification_report() 函数来提取性能报告。此函数构建一个文本报告,显示主要的分类指标。返回每个类别的精确度、召回率和 F1 分数的文本摘要。参照前一个配方中提到的混淆矩阵中的术语,这些指标按以下方式计算:

  • 精确度是 tp / (tp + fp) 的比率,其中 tp 是真正例的数量,fp 是假正例的数量。精确度是分类器不将负样本标记为正样本的能力。

  • 召回率是 tp / (tp + fn) 的比率,其中 tp 是真正例的数量,fn 是假负例的数量。召回率是分类器找到正样本的能力。

  • F1 分数被认为是精确率和召回率的加权调和平均值,其中 F-beta 分数在 1 处达到峰值,在 0 处达到最低分。

还有更多…

报告的平均值包括 微观平均(平均总真实阳性、假阴性和假阳性)、宏观平均(平均每个标签的无权平均值)、加权平均(平均每个标签的支持加权平均值)和 样本平均(仅适用于多标签分类)。

参见

根据汽车特征进行评估

在本食谱中,我们将看看如何将分类技术应用于现实世界问题。我们将使用一个包含一些关于汽车细节的数据集,例如车门数量、后备箱空间、维护成本等。我们的目标是确定汽车的质量。对于分类的目的,质量可以取四个值:不可接受、可接受、良好或非常好。

准备工作

您可以在此处下载数据集:archive.ics.uci.edu/ml/datasets/Car+Evaluation

您需要将数据集中的每个值都视为字符串。我们考虑数据集中的六个属性。以下是属性及其可能取的值:

  • buying: 这些将是 非常高

  • maint: 这些将是 非常高

  • doors: 这些将是 2345更多

  • persons: 这些将是 24更多

  • lug_boot: 这些将是

  • safety: 这些将是

由于每行都包含字符串,我们需要假设所有特征都是字符串,并设计一个分类器。在上一章中,我们使用随机森林构建了一个回归器。在本食谱中,我们将使用随机森林作为分类器。

如何操作…

让我们看看如何根据汽车特征进行评估:

  1. 我们将使用您已经提供的 car.py 文件作为参考。让我们继续并导入一些包:
from sklearn import preprocessing
from sklearn.ensemble import RandomForestClassifier
  1. 让我们加载数据集:
input_file = 'car.data.txt'
# Reading the data
X = []
count = 0
with open(input_file, 'r') as f:
    for line in f.readlines():
        data = line[:-1].split(',')
        X.append(data)
X = np.array(X)

每行包含一个以逗号分隔的单词列表。因此,我们解析输入文件,分割每一行,然后将列表追加到主数据中。我们忽略每行的最后一个字符,因为它是一个换行符。Python 包只处理数值数据,因此我们需要将这些属性转换成那些包可以理解的形式。

  1. 在上一章中,我们讨论了标签编码。这就是我们在这里将字符串转换为数字所使用的:
# Convert string data to numerical data
label_encoder = []
X_encoded = np.empty(X.shape)
for i,item in enumerate(X[0]):
    label_encoder.append(preprocessing.LabelEncoder())
    X_encoded[:, i] = label_encoder[-1].fit_transform(X[:, i])
X = X_encoded[:, :-1].astype(int)
y = X_encoded[:, -1].astype(int)

由于每个属性可以取有限数量的值,我们可以使用标签编码器将它们转换为数字。我们需要为每个属性使用不同的标签编码器。例如,lug_boot属性可以取三个不同的值,我们需要一个知道如何编码这个属性的标签编码器。每行的最后一个值是类别,所以我们将其分配给y变量。

  1. 让我们训练分类器:
# Build a Random Forest classifier
params = {'n_estimators': 200, 'max_depth': 8, 'random_state': 7}
classifier = RandomForestClassifier(**params)
classifier.fit(X, y)

你可以调整n_estimatorsmax_depth参数,看看它们如何影响分类准确率。我们实际上很快就会以标准化的方式进行这项操作。

  1. 让我们进行交叉验证:
# Cross validation
from sklearn import model_selection

accuracy = model_selection.cross_val_score(classifier, 
        X, y, scoring='accuracy', cv=3)
print("Accuracy of the classifier: " + str(round(100*accuracy.mean(), 2)) + "%")

一旦我们训练了分类器,我们需要看看它的表现。我们使用三折交叉验证来计算这里的准确率。以下结果被返回:

Accuracy of the classifier: 78.19%
  1. 构建分类器的主要目标之一是将其用于孤立和未知的数据实例。让我们使用一个数据点,看看我们如何使用这个分类器对其进行分类:
# Testing encoding on single data instance
input_data = ['high', 'low', '2', 'more', 'med', 'high']
input_data_encoded = [-1] * len(input_data)
for i,item in enumerate(input_data):
    input_data_encoded[i] = int(label_encoder[i].transform([input_data[i]]))
input_data_encoded = np.array(input_data_encoded)

第一步是将数据转换为数值数据。我们需要使用在训练期间使用的标签编码器,因为我们希望它保持一致性。如果输入数据点中存在未知值,标签编码器会抱怨,因为它不知道如何处理这些数据。例如,如果你将列表中的第一个值从high改为abcd,那么标签编码器将无法工作,因为它不知道如何解释这个字符串。这就像一个错误检查,用来查看输入数据点是否有效。

  1. 我们现在准备好预测这个数据点的输出类别:
# Predict and print output for a particular datapoint
output_class = classifier.predict([input_data_encoded])
print("Output class:", label_encoder[-1].inverse_transform(output_class)[0])

我们使用predict()方法来估计输出类别。如果我们输出编码后的输出标签,对我们来说没有任何意义。因此,我们使用inverse_transform方法将此标签转换回其原始形式并打印输出类别。以下结果被返回:

Output class: acc

它是如何工作的...

随机森林是由 Leo Breiman(美国加州大学伯克利分校)基于分类树的使用而开发的。他通过将其整合到蒙特卡洛模拟过程中扩展了分类树技术,并将其命名为随机森林。它基于创建大量树分类器,每个分类器都旨在对单个实例进行分类,其中一些特征已被评估。比较森林中每棵树提供的分类建议,以确定请求应归因于哪个类别:即获得最多投票的那个类别。

还有更多...

随机森林有三个调整参数:树的数量、终端节点的最小幅度以及每个节点中采样的变量数量。由于不存在过拟合,前两个参数仅从计算角度考虑才显得重要。

参见

提取验证曲线

在之前的食谱根据特征评估汽车中,我们使用了随机森林来构建分类器,但我们并不确切知道如何定义参数。在我们的情况下,我们处理了两个参数:n_estimatorsmax_depth。它们被称为超参数,分类器的性能取决于它们。看到我们改变超参数时性能如何受到影响将是非常有用的。这就是验证曲线发挥作用的地方。

准备工作

验证曲线帮助我们理解每个超参数如何影响训练分数。基本上,所有其他参数都保持不变,我们根据我们的范围改变感兴趣的超参数。然后我们将能够可视化这如何影响分数。

如何操作…

让我们看看如何提取验证曲线:

  1. 将以下代码添加到与之前食谱相同的 Python 文件中,根据特征评估汽车
# Validation curves
import matplotlib.pyplot as plt
from sklearn.model_selection import validation_curve

classifier = RandomForestClassifier(max_depth=4, random_state=7)

parameter_grid = np.linspace(25, 200, 8).astype(int)
train_scores, validation_scores = validation_curve(classifier, X, y, "n_estimators", parameter_grid, cv=5)
print("##### VALIDATION CURVES #####")
print("\nParam: n_estimators\nTraining scores:\n", train_scores)
print("\nParam: n_estimators\nValidation scores:\n", validation_scores)

在这种情况下,我们通过固定max_depth参数来定义分类器。我们想要估计使用最佳估计器的数量,因此使用parameter_grid定义了我们的搜索空间。它将通过从 25 迭代到 200,以 8 步为间隔来提取训练和验证分数。

  1. 如果你运行它,你将在你的终端上看到以下内容:

  1. 让我们绘制它:
# Plot the curve
plt.figure()
plt.plot(parameter_grid, 100*np.average(train_scores, axis=1), color='black')
plt.title('Training curve')
plt.xlabel('Number of estimators')
plt.ylabel('Accuracy')
plt.show()
  1. 这是你将得到的结果:

  1. 让我们对max_depth参数做同样的操作:
classifier = RandomForestClassifier(n_estimators=20, random_state=7)
parameter_grid = np.linspace(2, 10, 5).astype(int)
train_scores, valid_scores = validation_curve(classifier, X, y, 
        "max_depth", parameter_grid, cv=5)
print("\nParam: max_depth\nTraining scores:\n", train_scores)
print("\nParam: max_depth\nValidation scores:\n", validation_scores)

我们将n_estimators参数固定为 20,以查看性能如何随max_depth的变化而变化。以下是终端上的输出:

  1. 让我们绘制它:
# Plot the curve
plt.figure()
plt.plot(parameter_grid, 100*np.average(train_scores, axis=1), color='black')
plt.title('Validation curve')
plt.xlabel('Maximum depth of the tree')
plt.ylabel('Accuracy')
plt.show()
  1. 如果你运行此代码,你将得到以下结果:

它是如何工作的…

在这个食谱中,我们使用了 scikit-learn 库的validation_curve函数来绘制验证曲线。此函数确定不同参数值下的训练和测试分数,并计算具有不同参数值的估计器的分数。

更多内容…

选择估计器的超参数是设置模型的基本程序之一。在可用的程序中,网格搜索是最常用的方法之一。此程序选择在验证集或多个验证集上具有最高分数的超参数。

参考阅读

提取学习曲线

学习曲线帮助我们理解训练数据集的大小如何影响机器学习模型。当你必须处理计算限制时,这非常有用。让我们继续通过改变训练数据集的大小来绘制学习曲线。

准备工作

学习曲线显示了不同数量的训练样本的估计器的验证和训练分数。

如何做...

让我们看看如何提取学习曲线:

  1. 将以下代码添加到与之前菜谱中相同的 Python 文件中,提取验证曲线
from sklearn.model_selection import validation_curve

classifier = RandomForestClassifier(random_state=7)

parameter_grid = np.array([200, 500, 800, 1100])
train_scores, validation_scores = validation_curve(classifier, X, y, "n_estimators", parameter_grid, cv=5)
print("\n##### LEARNING CURVES #####")
print("\nTraining scores:\n", train_scores)
print("\nValidation scores:\n", validation_scores)

我们想使用 200、500、800 和 1,100 个样本的训练数据集来评估性能指标。我们使用五折交叉验证,如validation_curve方法中 cv 参数所指定的。

  1. 如果你运行此代码,你将在终端上得到以下输出:

  1. 让我们绘制它:
# Plot the curve
plt.figure()
plt.plot(parameter_grid, 100*np.average(train_scores, axis=1), color='black')
plt.title('Learning curve')
plt.xlabel('Number of training samples')
plt.ylabel('Accuracy')
plt.show()
  1. 这里是输出:

虽然较小的训练集似乎能给出更好的准确度,但它们容易过拟合。如果我们选择更大的训练数据集,它会消耗更多资源。因此,我们需要在这里进行权衡,以选择合适的训练数据集大小。

它是如何工作的...

在这个菜谱中,我们使用了 scikit-learn 库的validation_curve函数来绘制学习曲线。此函数确定不同训练集大小的交叉验证训练和测试分数。

更多内容...

学习曲线使我们能够检查添加训练数据是否带来好处。它还允许我们估计来自方差误差和偏差误差的贡献。如果验证分数和训练分数在训练集大小太低时与训练集大小收敛,我们将不会从更多的训练数据中受益。

相关内容

估算收入区间

我们将构建一个分类器,根据 14 个属性估计一个人的收入区间。可能的输出类别是高于 50,000 或低于或等于 50,000。在这个数据集中有一个小小的转折,即每个数据点都是数字和字符串的混合。数值数据很有价值,在这些情况下我们不能使用标签编码器。我们需要设计一个可以同时处理数值和非数值数据的系统。

准备工作

我们将使用可从archive.ics.uci.edu/ml/datasets/Census+Income获取的普查收入数据集。

该数据集具有以下特征:

  • 实例数量:48,842

  • 属性数量:14

以下是一个属性列表:

  • Age: 连续型

  • Workclass: 文本

  • fnlwgt: 连续型

  • Education: 文本

  • Education-num: 连续型

  • Marital-status: 文本

  • Occupation: 文本

  • Relationship: 文本

  • Race: 文本

  • Sex: 女性 或 男性

  • Capital-gain: 连续型

  • Capital-loss: 连续型

  • Hours-per-week: 连续型

  • Native-country: 文本

如何做到这一点…

让我们看看如何估计收入区间:

  1. 我们将使用已提供的作为参考的income.py文件。我们将使用朴素贝叶斯分类器来实现这一点。让我们导入一些包:
import numpy as np
from sklearn import preprocessing
from sklearn.naive_bayes import GaussianNB
  1. 让我们加载数据集:
input_file = 'adult.data.txt'
# Reading the data
X = []
y = []
count_lessthan50k = 0
count_morethan50k = 0
num_images_threshold = 10000
  1. 我们将从数据集中使用 20,000 个数据点——每个类别 10,000 个,以避免类别不平衡。在训练过程中,如果你使用属于单个类别的许多数据点,分类器往往会偏向该类别。因此,最好为每个类别使用相同数量的数据点:
with open(input_file, 'r') as f:
    for line in f.readlines():
        if '?' in line:
            continue
        data = line[:-1].split(', ')
        if data[-1] == '<=50K' and count_lessthan50k < num_images_threshold:
            X.append(data)
            count_lessthan50k = count_lessthan50k + 1
        elif data[-1] == '>50K' and count_morethan50k < num_images_threshold:
            X.append(data)
            count_morethan50k = count_morethan50k + 1
        if count_lessthan50k >= num_images_threshold and count_morethan50k >= num_images_threshold:
            break
X = np.array(X)

这又是一个以逗号分隔的文件。我们就像之前一样,将数据加载到X变量中。

  1. 我们需要将字符串属性转换为数值数据,同时保留原始数值数据:
# Convert string data to numerical data
label_encoder = []
X_encoded = np.empty(X.shape)
for i,item in enumerate(X[0]):
    if item.isdigit():
        X_encoded[:, i] = X[:, i]
    else:
        label_encoder.append(preprocessing.LabelEncoder())
        X_encoded[:, i] = label_encoder[-1].fit_transform(X[:, i])
X = X_encoded[:, :-1].astype(int)
y = X_encoded[:, -1].astype(int)

isdigit()函数帮助我们识别数值数据。我们将字符串数据转换为数值数据,并将所有标签编码器存储在一个列表中,以便我们在需要分类未知数据时使用。

  1. 让我们训练分类器:
# Build a classifier
classifier_gaussiannb = GaussianNB()
classifier_gaussiannb.fit(X, y)
  1. 让我们将数据拆分为训练集和测试集以提取性能指标:
# Cross validation
from sklearn import model_selection
X_train, X_test, y_train, y_test = model_selection.train_test_split(X, y, test_size=0.25, random_state=5)
classifier_gaussiannb = GaussianNB()
classifier_gaussiannb.fit(X_train, y_train)
y_test_pred = classifier_gaussiannb.predict(X_test)
  1. 让我们提取性能指标:
# compute F1 score of the classifier
f1 = model_selection.cross_val_score(classifier_gaussiannb,
        X, y, scoring='f1_weighted', cv=5)
print("F1 score: " + str(round(100*f1.mean(), 2)) + "%")

返回以下结果:

F1 score: 75.9%
  1. 让我们看看如何对单个数据点进行分类。我们需要将数据点转换为我们的分类器可以理解的形式:
# Testing encoding on single data instance
input_data = ['39', 'State-gov', '77516', 'Bachelors', '13', 'Never-married', 'Adm-clerical', 'Not-in-family', 'White', 'Male', '2174', '0', '40', 'United-States']
count = 0
input_data_encoded = [-1] * len(input_data)
for i,item in enumerate(input_data):
    if item.isdigit():
        input_data_encoded[i] = int([input_data[i]])
    else:
        input_data_encoded[i] = int(label_encoder[count].transform([input_data[i]]))
        count = count + 1 
input_data_encoded = np.array(input_data_encoded)
  1. 我们现在准备好进行分类:
# Predict and print output for a particular datapoint
output_class = classifier_gaussiannb.predict([input_data_encoded])
print(label_encoder[-1].inverse_transform(output_class)[0])

正如之前一样,我们使用predict方法获取output类别,并使用inverse_transform方法将这个标签转换回其原始形式,以便在终端上打印出来。以下为返回结果:

<=50K

它是如何工作的...

贝叶斯分类器的潜在原理是,基于某些观察,某些个体以一定的概率属于感兴趣的类别。这个概率基于这样的假设,即观察到的特征可以相互依赖或独立;在后一种情况下,贝叶斯分类器被称为朴素,因为它假设在感兴趣的给定类别中,特定特征的呈现与否与其他特征的呈现与否无关,这大大简化了计算。让我们继续构建一个朴素贝叶斯分类器。

更多内容...

将贝叶斯理论应用于分类的概念非常直观:如果我观察一个特定的可测量特征,我可以在观察后估计这个特征代表某个类别的概率。

参见

预测葡萄酒的质量

在这个食谱中,我们将根据种植的葡萄酒的化学特性来预测葡萄酒的质量。代码使用了一个葡萄酒数据集,它包含一个包含 177 行和 13 列的 DataFrame;第一列包含类别标签。这些数据来自意大利(皮埃蒙特)同一地区种植的葡萄酒的化学分析,但来自三种不同的品种——即内比奥洛、巴贝拉和格里尼奥洛葡萄。来自内比奥洛葡萄的葡萄酒被称为巴罗洛。

准备工作

数据包括在三种类型的葡萄酒中发现的几种成分的量,以及一些光谱变量。属性如下:

  • 酒精

  • 苹果酸

  • 灰分

  • 灰分的碱性

  • 总酚

  • 黄酮

  • 非黄酮酚

  • 原花青素

  • 颜色强度

  • 颜色

  • 稀释葡萄酒的 OD280/OD315

  • �脯氨酸

DataFrame 的第一列包含类别,表示三种类型的葡萄酒之一(0、1 或 2)。

如何做到这一点...

让我们看看如何预测葡萄酒的质量:

  1. 我们将使用已提供的wine.quality.py文件作为参考。我们像往常一样,首先导入 NumPy 库并加载数据(wine.txt):
import numpy as np
input_file = 'wine.txt'
X = []
y = []
with open(input_file, 'r') as f:
  for line in f.readlines():
     data = [float(x) for x in line.split(',')]
     X.append(data[1:])
     y.append(data[0])
X = np.array(X)
y = np.array(y)

返回两个数组:X(输入数据)和y(目标)。

  1. 现在,我们需要将我们的数据分为两组:一个训练数据集和一个测试数据集。训练数据集将用于构建模型,测试数据集将用于查看这个训练模型在未知数据上的表现:
from sklearn import model_selection
X_train, X_test, y_train, y_test = model_selection.train_test_split(X, y, test_size=0.25, random_state=5)

返回四个数组:X_trainX_testy_trainy_test。这些数据将用于训练和验证模型。

  1. 让我们训练这个分类器:
from sklearn.tree import DecisionTreeClassifier

classifier_DecisionTree = DecisionTreeClassifier()
classifier_DecisionTree.fit(X_train, y_train)

为了训练模型,使用了决策树算法。决策树算法基于一种非参数监督学习方法,用于分类和回归。目标是构建一个模型,使用从数据特征推断出的决策规则来预测目标变量的值。

  1. 现在是计算分类器准确率的时候了:
y_test_pred = classifier_DecisionTree.predict(X_test)

accuracy = 100.0 * (y_test == y_test_pred).sum() / X_test.shape[0]
print("Accuracy of the classifier =", round(accuracy, 2), "%")

返回以下结果:

Accuracy of the classifier = 91.11 %
  1. 最后,将计算混淆矩阵以计算模型性能:
from sklearn.metrics import confusion_matrix

confusion_mat = confusion_matrix(y_test, y_test_pred)
print(confusion_mat)

返回以下结果:

[[17  2  0]
 [ 1 12  1]
 [ 0  0 12]]

对角线上没有出现的值表示分类错误。因此,分类器只犯了四个错误。

它是如何工作的...

在这个菜谱中,基于种植的葡萄酒的化学性质预测了葡萄酒的质量。为此,使用了决策树算法。决策树以图形方式显示所做的或建议的选择。事情并不总是如此清晰,以至于两个解决方案之间的选择是立即的。通常,一个决定是由一系列级联条件决定的。用表格和数字表示这个概念是困难的。事实上,即使表格代表了一个现象,它也可能使读者困惑,因为选择的理由并不明显。

还有更多...

树状结构使我们能够通过突出显示我们插入的分支来确定选择或评估,从而以清晰的易读性提取信息。决策树技术通过创建一个具有可能结果的模型来识别策略或追求目标是有用的。决策树图立即指引结果的阅读。图表比充满数字的表格更有说服力。人类的大脑更喜欢先看到解决方案,然后再回头理解解决方案的合理性,而不是一系列代数描述、百分比和数据来描述结果。

参见

新组趋势主题分类

新闻组是关于许多问题的讨论组,由位于世界各地的新闻服务器提供,这些服务器收集来自客户端的消息并将它们传输给所有用户,另一方面,传输给连接到网络的其它新闻服务器。这项技术的成功归功于讨论中的用户互动。每个人都必须遵守组规。

准备工作

在这个菜谱中,我们将构建一个分类器,使我们能够将主题的成员资格分类到特定的讨论组中。这个操作将有助于验证主题是否与讨论组相关。我们将使用以下 URL 中可用的 20 个新闻组数据集中的数据:qwone.com/~jason/20Newsgroups/

这是一个大约有 20,000 篇新闻组文档的集合,分为 20 个不同的新闻组。最初由 Ken Lang 收集,并在Newsweeder 论文:学习过滤网络新闻中发表,这个数据集特别适用于处理文本分类问题。

如何做到这一点…

在这个菜谱中,我们将学习如何执行新闻组趋势主题分类:

  1. 我们将使用已经提供给你作为参考的post.classification.py文件。我们开始导入数据集如下:
from sklearn.datasets import fetch_20newsgroups

这个数据集包含在sklearn.datasets库中;这样,我们将很容易恢复数据。正如预期的那样,数据集包含与 20 个新闻组相关的帖子。我们将仅分析以下两个新闻组:

NewsClass = ['rec.sport.baseball', 'rec.sport.hockey']
  1. 下载数据:
DataTrain = fetch_20newsgroups(subset='train',categories=NewsClass, shuffle=True, random_state=42)
  1. 数据有两个属性:datatarget。显然,data代表输入,target是输出。让我们检查哪些新闻组已被选中:
print(DataTrain.target_names)

打印以下结果:

['rec.sport.baseball', 'rec.sport.hockey']
  1. 让我们检查形状:
print(len(DataTrain.data))
print(len(DataTrain.target))

返回以下结果:

1197
1197
  1. 为了从文本中提取特征,我们将使用以下CountVectorizer()函数:
from sklearn.feature_extraction.text import CountVectorizer

CountVect = CountVectorizer()
XTrainCounts = CountVect.fit_transform(DataTrain.data)
print(XTrainCounts.shape)

返回以下结果:

(1197, 18571)

这样,我们已经对单词的出现次数进行了计数。

  1. 现在我们将文档中每个单词出现的次数除以文档中单词的总数:
from sklearn.feature_extraction.text import TfidfTransformer

TfTransformer = TfidfTransformer(use_idf=False).fit(XTrainCounts)
XTrainNew = TfTransformer.transform(XTrainCounts)
TfidfTransformer = TfidfTransformer()
XTrainNewidf = TfidfTransformer.fit_transform(XTrainCounts)
  1. 现在我们可以构建分类器:
from sklearn.naive_bayes import MultinomialNB

NBMultiClassifier = MultinomialNB().fit(XTrainNewidf, DataTrain.target)
  1. 最后,我们将计算分类器的准确率:
NewsClassPred = NBMultiClassifier.predict(XTrainNewidf)

accuracy = 100.0 * (DataTrain.target == NewsClassPred).sum() / XTrainNewidf.shape[0]
print("Accuracy of the classifier =", round(accuracy, 2), "%")

返回以下结果:

Accuracy of the classifier = 99.67 %

它是如何工作的...

在这个菜谱中,我们构建了一个分类器,用于将主题的成员资格分类到特定的讨论组中。为了从文本中提取特征,需要一个分词过程。在分词阶段,在每个单独的句子中,识别出称为标记的原子元素;基于识别出的标记,可以执行句子本身的分析和评估。一旦提取了文本的特征,就构建了一个基于多项式朴素贝叶斯算法的分类器。

还有更多...

当特征表示文档中单词(文本或视觉)的频率时,朴素贝叶斯多项式算法用于文本和图像。

参见

第三章:预测建模

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

  • 使用支持向量机SVMs)构建线性分类器

  • 使用 SVM 构建非线性分类器

  • 解决类别不平衡问题

  • 提取置信度测量

  • 寻找最优超参数

  • 构建事件预测器

  • 估计交通流量

  • 使用 TensorFlow 简化机器学习工作流程

  • 实现堆叠方法

技术要求

为了处理本章中的食谱,你需要以下文件(可在 GitHub 上找到):

  • svm.py

  • data_multivar.txt

  • svm_imbalance.py

  • data_multivar_imbalance.txt

  • svm_confidence.py

  • perform_grid_search.py

  • building_event_binary.txt

  • building_event_multiclass.txt

  • `event.py`

  • traffic_data.txt

  • traffic.py

  • IrisTensorflow.py

  • stacking.py

简介

预测建模可能是数据分析中最激动人心的领域之一。近年来,由于许多不同领域中有大量数据可用,它受到了很多关注。它在数据挖掘领域非常常用,用于预测未来的趋势。

预测建模是一种分析技术,用于预测系统的未来行为。它是一系列算法,可以识别独立输入变量与目标响应之间的关系。我们根据观察创建一个数学模型,然后使用这个模型来估计未来会发生什么。

在预测建模中,我们需要收集已知响应的数据来训练我们的模型。一旦我们创建了该模型,我们将使用一些指标来验证它,然后使用它来预测未来的值。我们可以使用许多不同类型的算法来创建预测模型。在本章中,我们将使用 SVM 来构建线性和非线性模型。

预测模型是通过使用可能影响系统行为的多个特征构建的。例如,为了估计天气条件,我们可能使用各种类型的数据,如温度、气压、降水和其他大气过程。同样,当我们处理其他类型的系统时,我们需要决定哪些因素可能影响其行为,并在训练模型之前将它们作为特征向量的一部分包括在内。

使用 SVM 构建线性分类器

SVMs(支持向量机)是我们可以用来创建分类器和回归器的监督学习模型。SVM 通过求解一组数学方程式,找到两个点集之间最佳分离边界。让我们看看如何使用 SVM 构建线性分类器。

准备工作

让我们可视化我们的数据,以理解当前的问题。我们将使用svm.py文件来完成这项工作。在我们构建 SVM 之前,让我们了解我们的数据。我们将使用已经提供给你的data_multivar.txt文件。让我们看看如何可视化数据:

  1. 创建一个新的 Python 文件,并向其中添加以下行(完整的代码在已经提供给你的svm.py文件中):
import numpy as np 
import matplotlib.pyplot as plt 

import utilities  

# Load input data 
input_file = 'data_multivar.txt' 
X, y = utilities.load_data(input_file) 
  1. 我们只导入了一些包并命名了输入文件。让我们看看load_data()方法:
# Load multivar data in the input file 
def load_data(input_file): 
    X = [] 
    y = [] 
    with open(input_file, 'r') as f: 
        for line in f.readlines(): 
            data = [float(x) for x in line.split(',')] 
            X.append(data[:-1]) 
            y.append(data[-1])  

    X = np.array(X) 
    y = np.array(y) 

    return X, y 
  1. 我们需要将数据分离成类别,如下所示:
class_0 = np.array([X[i] for i in range(len(X)) if y[i]==0]) 
class_1 = np.array([X[i] for i in range(len(X)) if y[i]==1]) 
  1. 现在我们已经分离了数据,让我们绘制它:
plt.figure() 
plt.scatter(class_0[:,0], class_0[:,1], facecolors='black', edgecolors='black', marker='s') 
plt.scatter(class_1[:,0], class_1[:,1], facecolors='None', edgecolors='black', marker='s') 
plt.title('Input data') 
plt.show() 

如果你运行此代码,你将看到以下内容:

图片

前面包含两种类型的点——实心方块空心方块。在机器学习的术语中,我们说我们的数据包含两个类别。我们的目标是构建一个可以将实心方块与空心方块分开的模型。

如何做到这一点...

在这个菜谱中,我们将学习如何使用支持向量机(SVMs)构建线性分类器:

  1. 我们需要将我们的数据集分成训练集和测试集。向同一 Python 文件中添加以下行:
# Train test split and SVM training 
from sklearn import cross_validation 
from sklearn.svm import SVC 

X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=0.25, random_state=5) 
  1. 让我们使用linear核初始化 SVM 对象。向文件中添加以下行:
params = {'kernel': 'linear'} 
classifier = SVC(**params, gamma='auto') 
  1. 我们现在准备好训练线性 SVM 分类器:
classifier.fit(X_train, y_train) 
  1. 我们现在可以看到分类器的表现:
utilities.plot_classifier(classifier, X_train, y_train, 'Training dataset') 
plt.show() 

如果你运行此代码,你将得到以下内容:

图片

plot_classifier函数与我们在第一章,“监督学习领域”中讨论的相同。它有一些小的补充。

你可以查看已经提供给你的utilities.py文件以获取更多详细信息。

  1. 让我们看看它在测试数据集上的表现。向svm.py文件中添加以下行:
y_test_pred = classifier.predict(X_test) 
utilities.plot_classifier(classifier, X_test, y_test, 'Test dataset') 
plt.show()

如果你运行此代码,你将看到以下输出:

图片

如你所见,分类器在输入数据上的边界被清楚地识别。

  1. 让我们计算训练集的准确率。向同一文件中添加以下行:
from sklearn.metrics import classification_report 

target_names = ['Class-' + str(int(i)) for i in set(y)]
print("\n" + "#"*30)
print("\nClassifier performance on training dataset\n")
print(classification_report(y_train, classifier.predict(X_train), target_names=target_names))
print("#"*30 + "\n")

如果你运行此代码,你将在你的终端看到以下内容:

图片

  1. 最后,让我们看看测试数据集的分类报告:
print("#"*30)
print("\nClassification report on test dataset\n")
print(classification_report(y_test, y_test_pred, target_names=target_names))
print("#"*30 + "\n")
  1. 如果你运行此代码,你将在终端看到以下内容:

图片

从我们可视化数据的输出截图来看,我们可以看到实心方块被空心方块完全包围。这意味着数据不是线性可分的。我们无法画一条漂亮的直线来分离这两组点!因此,我们需要一个非线性分类器来分离这些数据点。

它是如何工作的...

SVMs 是一组监督学习方法,可用于分类和回归。对于两个线性可分的多维模式类别,在所有可能的分离超平面中,SVM 算法确定能够以最大可能间隔分离类别的那个超平面。间隔是训练集中两个类别的点与识别的超平面的最小距离。

边界的最大化与泛化能力相关。如果训练集的模式以大边界被分类,你可以希望即使测试集的模式接近类别之间的边界也能被正确处理。在下面的内容中,你可以看到三条线(l1l2l3)。线 l1 无法分离两个类别,线 l2 可以分离它们,但边界较小,而线 l3 最大化两个类别之间的距离:

图片

SVMs 可以用来分离那些线性分类器无法分离的类别。对象坐标通过称为特征函数的非线性函数映射到一个称为特征空间的空间中。这个空间是多维的,在这个空间中,两个类别可以用线性分类器分离。因此,初始空间被重新映射到新空间,此时分类器被识别,然后返回到初始空间。

更多内容...

SVMs 是文献中最近引入的一类学习机器。SVMs 来自于关于学习统计理论的观念,并具有理论上的泛化特性。SVMs 的功能机制所遵循的理论是由 Vapnik 在 1965 年(统计学习理论)提出的,后来在 1995 年由 Vapnik 本人和其他人进一步完善。SVMs 是模式分类中最广泛使用的工具之一。Vapnik 建议直接解决感兴趣的问题,即确定类别之间的决策表面(分类边界),而不是估计类别的概率密度。

参见

使用 SVMs 构建非线性分类器

SVM 提供了多种选项来构建非线性分类器。我们需要使用各种核函数来构建非线性分类器。在这个例子中,让我们考虑两种情况。当我们想要表示两组点之间的曲线边界时,我们可以使用多项式函数或径向基函数来完成。

准备工作

在这个菜谱中,我们将使用之前菜谱中使用的相同文件,即使用 SVM 构建线性分类器,但在这个情况下,我们将使用不同的核来处理一个明显非线性的问题。

如何做...

让我们看看如何使用 SVM 构建非线性分类器:

  1. 对于第一种情况,让我们使用多项式核来构建一个非线性分类器。在相同的 Python 文件(svm.py)中,查找以下行:
params = {'kernel': 'linear'} 

将此行替换为以下内容:

params = {'kernel': 'poly', 'degree': 3} 

这意味着我们使用一个degree3的多项式函数。如果我们增加度数,这意味着我们允许多项式曲线更弯曲。然而,曲线的弯曲是有代价的,因为这意味着它将花费更多的时间来训练,因为它更昂贵。

  1. 如果你现在运行此代码,你将得到以下结果:

  1. 你还将在你的终端上看到以下分类报告:

  1. 我们还可以使用径向基函数核来构建一个非线性分类器。在相同的 Python 文件中,查找以下行:
params = {'kernel': 'poly', 'degree': 3} 
  1. 将此行替换为以下一行:
params = {'kernel': 'rbf'} 
  1. 如果你现在运行此代码,你将得到以下结果:

  1. 你还将在你的终端上看到以下分类报告:

它是如何工作的...

在这个菜谱中,我们使用 SVM 分类器通过解决一组数学方程来找到点数据集的最佳分离边界。为了解决非线性问题,我们使用了核方法。核方法因此得名于核函数,这些函数用于在特征空间中操作,而不是通过计算函数空间中所有数据副本的图像之间的内积来计算数据坐标。内积的计算通常比显式计算坐标更便宜。这种方法被称为核策略

还有更多...

SVM 的主要观点是,只要仔细选择核及其所有参数,就可以解决任何通用问题——例如,对输入数据集进行完全过拟合。这种方法的问题在于,它与数据集的大小成比例地扩展得相当差,因为它通常归因于 D2 因子,即使在这种情况下,通过优化这一方面可以获得更快的实现。问题在于确定最佳的核并提供最佳的参数。

参见

解决类别不平衡问题

到目前为止,我们处理了所有类别中数据点数量相似的问题。在现实世界中,我们可能无法以如此有序的方式获取数据。有时,一个类别的数据点数量可能比其他类别的数据点数量多得多。如果发生这种情况,那么分类器往往会偏向。边界不会反映你数据的真实性质,只是因为两个类别之间数据点的数量存在很大差异。因此,考虑这种差异并中和它是很重要的,这样我们的分类器才能保持公正。

准备工作

在这个食谱中,我们将使用一个新的数据集,名为data_multivar_imbalance.txt,其中每行有三个值;前两个代表点的坐标,第三个是该点所属的类别。我们的目标是,再次,构建一个分类器,但这次,我们必须面对数据平衡问题。

如何做到这一点...

让我们看看如何解决类别不平衡问题:

  1. 让我们导入库:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
import utilities
  1. 让我们加载数据(data_multivar_imbalance.txt):
input_file = 'data_multivar_imbalance.txt' 
X, y = utilities.load_data(input_file) 

  1. 让我们可视化数据。可视化代码与之前的食谱中完全相同。你还可以在名为svm_imbalance.py的文件中找到它,该文件已经提供给你:
# Separate the data into classes based on 'y'
class_0 = np.array([X[i] for i in range(len(X)) if y[i]==0])
class_1 = np.array([X[i] for i in range(len(X)) if y[i]==1])
# Plot the input data
plt.figure()
plt.scatter(class_0[:,0], class_0[:,1], facecolors='black', edgecolors='black', marker='s')
plt.scatter(class_1[:,0], class_1[:,1], facecolors='None', edgecolors='black', marker='s')
plt.title('Input data')
plt.show()
  1. 如果你运行它,你会看到以下:

  1. 让我们使用线性核构建一个 SVM。代码与之前的食谱中相同,使用 SVM 构建非线性分类器
from sklearn import model_selection
X_train, X_test, y_train, y_test = model_selection.train_test_split(X, y, test_size=0.25, random_state=5)
params = {'kernel': 'linear'}
classifier = SVC(**params, gamma='auto')
classifier.fit(X_train, y_train)
utilities.plot_classifier(classifier, X_train, y_train, 'Training dataset')
plt.show()
  1. 让我们打印一个分类报告:
from sklearn.metrics import classification_report
target_names = ['Class-' + str(int(i)) for i in set(y)]
print("\n" + "#"*30)
print("\nClassifier performance on training dataset\n")
print(classification_report(y_train, classifier.predict(X_train), target_names=target_names))
print("#"*30 + "\n")
print("#"*30)
print("\nClassification report on test dataset\n")
print(classification_report(y_test, y_test_pred, target_names=target_names))
print("#"*30 + "\n")
  1. 如果你运行它,你会看到以下:

  1. 你可能会 wonder 为什么这里没有边界!Well, 这是因为分类器无法将两个类别分开,导致Class-0的准确率为 0%。你还会在终端上看到打印出的分类报告,如下截图所示:

  1. 如我们所预期,Class-0的精确率为 0%,所以让我们继续解决这个问题!在 Python 文件中,搜索以下行:
params = {'kernel': 'linear'}
  1. 将前面的行替换为以下内容:
params = {'kernel': 'linear', 'class_weight': 'balanced'}  
  1. class_weight参数将计算每个类别的数据点数量,以调整权重,使不平衡不会对性能产生不利影响。

  2. 运行此代码后,你会得到以下输出:

  1. 让我们查看分类报告:

  1. 如我们所见,Class-0现在以非零百分比准确率被检测到。

它是如何工作的...

在这个配方中,我们使用 SVM 分类器来找到点数据集之间的最佳分离边界。为了解决数据平衡问题,我们再次使用了线性核方法,但在 fit 方法中实现了一个 class_weight 关键字。class_weight 变量是一个形式为 {class_label: value} 的字典,其中 value 是一个大于 0 的浮点数,它修改了类 (class_label)C 参数,将其设置为通过将旧的 C 值与值属性中指定的值相乘得到的新值 (C * value)。

更多内容...

C 是一个超参数,它决定了观察到的错误分类的惩罚。因此,我们使用权重来管理不平衡的类别。这样,我们将为类别分配一个新的 C 值,定义如下:

图片

其中 C 是惩罚,w[i] 是与类别 i 的频率成反比的权重,C[i] 是类别 iC 值。这种方法建议增加对代表性较小的类别的惩罚,以防止它们被代表性最大的类别超越。

scikit-learn 库中,当使用 SVC 时,我们可以通过设置 class_weight='balanced' 来自动设置 C[i] 的值。

参见

提取置信度测量

很好知道我们以多大的置信度对未知数据进行分类。当一个新数据点被分类到已知类别时,我们可以训练 SVM 来计算该输出的置信水平。置信水平指的是参数值落在指定值范围内的概率。

准备工作

在这个配方中,我们将使用 SVM 分类器来找到点数据集之间的最佳分离边界。此外,我们还将对获得的结果的置信水平进行测量。

如何做...

让我们看看如何提取置信度测量:

  1. 完整的代码在 svm_confidence.py 文件中给出,已经提供给你。我们将在下面讨论这个配方的代码。让我们定义一些输入数据:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
import utilities

# Load input data
input_file = 'data_multivar.txt'
X, y = utilities.load_data(input_file)
  1. 在这一点上,我们将数据分为训练集和测试集,然后我们将构建分类器:
from sklearn import model_selection
X_train, X_test, y_train, y_test = model_selection.train_test_split(X, y, test_size=0.25, random_state=5)
params = {'kernel': 'rbf'}
classifier = SVC(**params, gamma='auto')
classifier.fit(X_train, y_train)
  1. 定义输入数据点:
input_datapoints = np.array([[2, 1.5], [8, 9], [4.8, 5.2], [4, 4], [2.5, 7], [7.6, 2], [5.4, 5.9]])
  1. 让我们测量边界距离:
print("Distance from the boundary:")
for i in input_datapoints:
    print(i, '-->', classifier.decision_function([i])[0])
  1. 你将在你的终端上看到以下内容:

图片

  1. 边界距离给我们关于数据点的某些信息,但它并没有确切地告诉我们分类器对输出标签的置信度如何。为了做到这一点,我们需要 Platt 缩放。这是一种将距离度量转换为类别之间概率度量的方法。让我们继续使用 Platt 缩放训练一个 SVM:
# Confidence measure 
params = {'kernel': 'rbf', 'probability': True} 
classifier = SVC(**params, gamma='auto') 

probability 参数告诉 SVM 它应该训练以计算概率。

  1. 让我们训练分类器:
classifier.fit(X_train, y_train) 
  1. 让我们计算这些输入数据点的置信度测量值:
print("Confidence measure:")
for i in input_datapoints:
    print(i, '-->', classifier.predict_proba([i])[0])

predict_proba 函数测量置信值。

  1. 你将在你的终端上看到以下内容:

图片

  1. 让我们看看点相对于边界的位置:
utilities.plot_classifier(classifier, input_datapoints, [0]*len(input_datapoints), 'Input datapoints', 'True') 
  1. 如果你运行这个,你将得到以下结果:

图片

工作原理...

在这个菜谱中,我们基于 SVM 构建了一个分类器。一旦获得分类器,我们使用一组点来衡量这些点与边界的距离,然后为这些点中的每一个测量置信水平。在估计参数时,简单地识别一个值通常是不够的。因此,建议在估计参数的同时,给出该参数的合理值范围,这被定义为置信区间。因此,它与一个累积概率值相关联,间接地,从概率的角度来看,它描述了相对于随机变量最大值的幅度,该随机变量衡量的是随机事件落在该区间内的概率,并且等于该区间图形上由随机变量的概率分布曲线所围成的面积。

更多内容...

置信区间衡量一个统计量(如民意调查)的可靠性。例如,如果 40%的受访样本表示选择某个产品,那么可以以 99%的置信水平推断,总消费者人口中有 30%到 50%的比例将表示支持该产品。从同一受访样本中,以 90%的置信区间,可以假设该产品受到好评的比例现在在 37%到 43%之间。

参见

寻找最优超参数

如前一章所述,超参数对于确定分类器的性能很重要。让我们看看如何提取 SVM 的最优超参数。

准备工作

在机器学习算法中,学习过程中会获得各种参数。相比之下,超参数是在学习过程开始之前设置的。给定这些超参数,训练算法从数据中学习参数。在这个菜谱中,我们将使用网格搜索方法从基于 SVM 算法的模型中提取超参数。

如何做...

让我们看看如何找到最佳超参数:

  1. 完整的代码在perform_grid_search.py文件中给出,该文件已经提供给你。我们开始导入库:
from sklearn import svm
from sklearn import model_selection
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import classification_report
import pandas as pd
import utilities 
  1. 然后,我们加载数据:
input_file = 'data_multivar.txt'
X, y = utilities.load_data(input_file)
  1. 我们将数据分为训练集和测试集:
X_train, X_test, y_train, y_test = model_selection.train_test_split(X, y, test_size=0.25, random_state=5)
  1. 现在,我们将在这里使用交叉验证,这是我们之前菜谱中提到的。一旦你加载数据并将其分为训练集和测试集,请将以下内容添加到文件中:
# Set the parameters by cross-validation
parameter_grid = {"C": [1, 10, 50, 600],
                  'kernel':['linear','poly','rbf'],
                  "gamma": [0.01, 0.001],
                  'degree': [2, 3]}
  1. 让我们定义我们想要使用的度量标准:
metrics = ['precision'] 
  1. 让我们开始搜索每个度量指标的最佳超参数:
for metric in metrics:

    print("#### Grid Searching optimal hyperparameters for", metric)

    classifier = GridSearchCV(svm.SVC(C=1), 
            parameter_grid, cv=5,scoring=metric,return_train_score=True) 

    classifier.fit(X_train, y_train)
  1. 让我们看看分数:
    print("Scores across the parameter grid:")
    GridSCVResults = pd.DataFrame(classifier.cv_results_)
    for i in range(0,len(GridSCVResults)):
        print(GridSCVResults.params[i], '-->', round(GridSCVResults.mean_test_score[i],3))    
  1. 让我们打印最佳参数集:
    print("Highest scoring parameter set:", classifier.best_params_)
  1. 如果你运行此代码,你将在你的终端看到以下内容:
#### Grid Searching optimal hyperparameters for precision
Scores across the parameter grid:
{'C': 1, 'degree': 2, 'gamma': 0.01, 'kernel': 'linear'} --> 0.676
{'C': 1, 'degree': 2, 'gamma': 0.01, 'kernel': 'poly'} --> 0.527
{'C': 1, 'degree': 2, 'gamma': 0.01, 'kernel': 'rbf'} --> 0.98
{'C': 1, 'degree': 2, 'gamma': 0.001, 'kernel': 'linear'} --> 0.676
{'C': 1, 'degree': 2, 'gamma': 0.001, 'kernel': 'poly'} --> 0.533
...
...
{'C': 600, 'degree': 2, 'gamma': 0.001, 'kernel': 'linear'} --> 0.676
{'C': 600, 'degree': 2, 'gamma': 0.001, 'kernel': 'poly'} --> 0.9
{'C': 600, 'degree': 2, 'gamma': 0.001, 'kernel': 'rbf'} --> 0.983
{'C': 600, 'degree': 3, 'gamma': 0.01, 'kernel': 'linear'} --> 0.676
{'C': 600, 'degree': 3, 'gamma': 0.01, 'kernel': 'poly'} --> 0.884
{'C': 600, 'degree': 3, 'gamma': 0.01, 'kernel': 'rbf'} --> 0.967
{'C': 600, 'degree': 3, 'gamma': 0.001, 'kernel': 'linear'} --> 0.676
{'C': 600, 'degree': 3, 'gamma': 0.001, 'kernel': 'poly'} --> 0.533
{'C': 600, 'degree': 3, 'gamma': 0.001, 'kernel': 'rbf'} --> 0.983
Highest scoring parameter set: {'C': 10, 'degree': 2, 'gamma': 0.01, 'kernel': 'rbf'}
  1. 如前所述的输出所示,它搜索所有最佳超参数。在这种情况下,超参数是kernel的类型、C值和gamma。它将尝试这些参数的各种组合以找到最佳参数。让我们在测试数据集上测试它:
    y_true, y_pred = y_test, classifier.predict(X_test)
    print("Full performance report:\n")
    print(classification_report(y_true, y_pred))
  1. 如果你运行此代码,你将在你的终端看到以下内容:

图片

  1. 我们之前提到过,优化超参数有不同的技术。我们将应用RandomizedSearchCV方法。为此,只需使用相同的数据并更改分类器。在刚刚看到的代码中,我们添加了一个额外的部分:
# Perform a randomized search on hyper parameters
from sklearn.model_selection import RandomizedSearchCV
parameter_rand = {'C': [1, 10, 50, 600],
                  'kernel':['linear','poly','rbf'],
                  'gamma': [0.01, 0.001],
                  'degree': [2, 3]}
metrics = ['precision']
for metric in metrics:
    print("#### Randomized Searching optimal hyperparameters for", metric)
    classifier = RandomizedSearchCV(svm.SVC(C=1), 
             param_distributions=parameter_rand,n_iter=30,           
             cv=5,return_train_score=True)
    classifier.fit(X_train, y_train)
    print("Scores across the parameter grid:")
    RandSCVResults = pd.DataFrame(classifier.cv_results_)
    for i in range(0,len(RandSCVResults)):
         print(RandSCVResults.params[i], '-->', 
                 round(RandSCVResults.mean_test_score[i]
  1. 如果你运行此代码,你将在你的终端看到以下内容:
#### Randomized Searching optimal hyperparameters for precision
Scores across the parameter grid:
{'kernel': 'rbf', 'gamma': 0.001, 'degree': 2, 'C': 50} --> 0.671
{'kernel': 'rbf', 'gamma': 0.01, 'degree': 3, 'C': 600} --> 0.951
{'kernel': 'linear', 'gamma': 0.01, 'degree': 3, 'C': 50} --> 0.591
{'kernel': 'poly', 'gamma': 0.01, 'degree': 2, 'C': 10} --> 0.804
...
...
{'kernel': 'rbf', 'gamma': 0.01, 'degree': 3, 'C': 10} --> 0.92
{'kernel': 'poly', 'gamma': 0.001, 'degree': 3, 'C': 600} --> 0.533
{'kernel': 'linear', 'gamma': 0.001, 'degree': 2, 'C': 10} --> 0.591
{'kernel': 'poly', 'gamma': 0.01, 'degree': 3, 'C': 50} --> 0.853
{'kernel': 'linear', 'gamma': 0.001, 'degree': 2, 'C': 600} --> 0.591
{'kernel': 'poly', 'gamma': 0.01, 'degree': 3, 'C': 10} --> 0.844
Highest scoring parameter set: {'kernel': 'rbf', 'gamma': 0.01, 'degree': 3, 'C': 600}
  1. 让我们在测试数据集上测试它:
 print("Highest scoring parameter set:", classifier.best_params_)
 y_true, y_pred = y_test, classifier.predict(X_test)
 print("Full performance report:\n")
 print(classification_report(y_true, y_pred))

  1. 返回以下结果:

图片

它是如何工作的...

在之前的菜谱“使用 SVM 构建非线性分类器”中,我们反复修改 SVM 算法的核以获得数据分类的改进。基于菜谱开头给出的超参数定义,很明显,核代表一个超参数。在这个菜谱中,我们随机设置这个超参数的值并检查结果以找出哪个值决定了最佳性能。然而,随机选择算法参数可能是不够的。

此外,通过随机设置参数来比较不同算法的性能是困难的,因为一个算法可能比另一个算法使用不同的参数集表现更好。而且如果参数改变,算法可能比其他算法有更差的结果。

因此,随机选择参数值不是我们找到模型最佳性能的最佳方法。相反,建议开发一个算法,该算法可以自动找到特定模型的最佳参数。寻找超参数的方法有几种,如下所示:网格搜索、随机搜索和贝叶斯优化。

网格搜索算法

网格搜索算法通过自动寻找降低模型最佳性能的超参数集来实现这一点。

sklearn.model_selection.GridSearchCV()函数对估计器的指定参数值进行穷举搜索。穷举搜索(也称为直接搜索或暴力搜索)是对所有可能性的全面检查,因此代表了一种高效的方法,其中每个可能性都被测试以确定它是否是解决方案。

随机搜索算法

GridSearchCV方法不同,这种方法并不是测试所有参数值,而是以固定数量对参数设置进行采样。要测试的参数设置是通过n_iter属性设置的。如果参数以列表形式呈现,则执行无放回采样。如果至少提供一个参数作为分布,则使用替换采样。

贝叶斯优化算法

贝叶斯超参数优化器的目标是构建目标函数的概率模型,并使用它来选择最适合用于真实目标函数的超参数。贝叶斯统计学不仅允许我们预见一个值,还可以预见一个分布,这是这种方法成功的关键。

与已处理的两种方法(网格搜索和随机搜索)相比,贝叶斯方法存储过去评估的结果,并使用这些结果形成一个概率模型,将超参数与目标函数得分的概率相关联。

这个模型被称为目标函数的代理,比目标函数本身更容易优化。通过以下程序获得此结果:

  1. 构建目标函数的代理概率模型。

  2. 在代理上给出最佳结果的超参数被搜索。

  3. 这些超参数应用于真实的目标函数。

  4. 通过结合新的结果来更新代理模型。

  5. 重复步骤 2-4,直到达到预定的迭代次数或最大时间。

以这种方式,在评估目标函数之后,代理概率模型被更新。要使用贝叶斯超参数优化器,有几种库可用:scikit-optimizespearmintSMAC3

更多...

通常,超参数是指那些用户可以自由设置的值,并且通常通过适当的研究进行优化,以在验证数据上最大化准确性。甚至选择一种技术而不是另一种技术也可以被视为一个分类超参数,其值与我们可选择的方法的数量一样多。

参见

构建事件预测器

让我们将本章中的所有这些知识应用到现实世界的问题中。我们将构建一个 SVM 来预测进出建筑的人数。数据集可在 archive.ics.uci.edu/ml/datasets/CalIt2+Building+People+Counts 找到。我们将使用这个数据集的一个略微修改版本,以便更容易分析。修改后的数据可在提供的 building_event_binary.txtbuilding_event_multiclass.txt 文件中找到。在这个菜谱中,我们将学习如何构建事件预测器。

准备中

在我们开始构建模型之前,让我们先了解数据格式。building_event_binary.txt 中的每一行都由六个以逗号分隔的字符串组成。这六个字符串的顺序如下:

  • 日期

  • 时间

  • 离开建筑的人数

  • 进入建筑的人数

  • 指示是否为事件的输出

前五个字符串构成输入数据,我们的任务是预测建筑中是否正在发生事件。

building_event_multiclass.txt 中的每一行也由六个以逗号分隔的字符串组成。这个文件比之前的文件更细粒度,因为输出是建筑中正在发生的确切事件类型。这六个字符串的顺序如下:

  • 日期

  • 时间

  • 离开建筑的人数

  • 进入建筑的人数

  • 指示事件类型的输出

前五个字符串形成输入数据,我们的任务是预测建筑物中正在进行的事件类型。

如何操作...

让我们看看如何构建一个事件预测器:

  1. 我们将使用已经提供给你的event.py作为参考。创建一个新的 Python 文件,并添加以下行:
import numpy as np 
from sklearn import preprocessing 
from sklearn.svm import SVC 

input_file = 'building_event_binary.txt' 

# Reading the data 
X = [] 
count = 0 
with open(input_file, 'r') as f: 
    for line in f.readlines(): 
        data = line[:-1].split(',') 
        X.append([data[0]] + data[2:]) 

X = np.array(X) 

我们只是将所有数据加载到了X中。

  1. 让我们将数据转换为数值形式:
# Convert string data to numerical data 
label_encoder = []  
X_encoded = np.empty(X.shape) 
for i,item in enumerate(X[0]): 
    if item.isdigit(): 
        X_encoded[:, i] = X[:, i] 
    else: 
        label_encoder.append(preprocessing.LabelEncoder()) 
        X_encoded[:, i] = label_encoder[-1].fit_transform(X[:, i]) 

X = X_encoded[:, :-1].astype(int) 
y = X_encoded[:, -1].astype(int) 
  1. 让我们使用径向基函数、Platt 缩放和类别平衡来训练 SVM:
# Build SVM 
params = {'kernel': 'rbf', 'probability': True, 'class_weight': 'balanced'}  
classifier = SVC(**params, gamma='auto') 
classifier.fit(X, y) 
  1. 我们现在准备好进行交叉验证:
from sklearn import model_selection

accuracy = model_selection.cross_val_score(classifier, 
        X, y, scoring='accuracy', cv=3)
print("Accuracy of the classifier: " + str(round(100*accuracy.mean(), 2)) + "%")
  1. 让我们在新的数据点上测试我们的 SVM:
# Testing encoding on single data instance
input_data = ['Tuesday', '12:30:00','21','23']
input_data_encoded = [-1] * len(input_data)
count = 0

for i,item in enumerate(input_data):
    if item.isdigit():
        input_data_encoded[i] = int(input_data[i])
    else:
        input_data_encoded[i] = int(label_encoder[count].transform([input_data[i]]))
        count = count + 1 

input_data_encoded = np.array(input_data_encoded)

# Predict and print(output for a particular datapoint
output_class = classifier.predict([input_data_encoded])
print("Output class:", label_encoder[-1].inverse_transform(output_class)[0])
  1. 如果你运行此代码,你将在你的终端上看到以下输出:
Accuracy of the classifier: 93.95%
Output class: noevent  
  1. 如果你使用building_event_multiclass.txt文件作为输入数据文件而不是building_event_binary.txt,你将在你的终端上看到以下输出:
Accuracy of the classifier: 65.33%
Output class: eventA

它是如何工作的...

在这个菜谱中,我们使用了在 15 周内,每天 48 个时间间隔观察到的进入和离开建筑物的人的数据。因此,我们构建了一个能够预测建筑物中如会议等事件存在的分类器,这决定了在那个时间段内建筑物内人数的增加。

更多内容...

在菜谱的后面,我们在不同的数据库上使用了相同的分类器来预测在建筑物内举行的活动类型。

相关内容

估算交通流量

SVMs 的一个有趣的应用是预测交通流量,基于相关数据。在之前的菜谱中,我们使用 SVM 作为分类器。在这个菜谱中,我们将使用 SVM 作为回归器来估算交通流量。

准备工作

我们将使用在archive.ics.uci.edu/ml/datasets/Dodgers+Loop+Sensor可用的数据集。这是一个在洛杉矶道奇主场棒球比赛中统计经过的车辆数量的数据集。我们将使用该数据集的略微修改版,以便更容易分析。你可以使用已经提供给你的traffic_data.txt文件。该文件中的每一行都包含逗号分隔的字符串,格式如下:

  • 日期

  • 时间

  • 对手队伍

  • 是否有棒球比赛正在进行

  • 经过的车辆数量

如何操作...

让我们看看如何估算交通流量:

  1. 让我们看看如何构建 SVM 回归器。我们将使用您已提供的作为参考的 traffic.py。创建一个新的 Python 文件,并添加以下行:
# SVM regressor to estimate traffic 

import numpy as np 
from sklearn import preprocessing 
from sklearn.svm import SVR 

input_file = 'traffic_data.txt' 

# Reading the data 
X = [] 
count = 0 
with open(input_file, 'r') as f: 
    for line in f.readlines(): 
        data = line[:-1].split(',') 
        X.append(data) 

X = np.array(X) 

我们将所有输入数据加载到 X 中。

  1. 让我们编码这些数据:
# Convert string data to numerical data 
label_encoder = []  
X_encoded = np.empty(X.shape) 
for i,item in enumerate(X[0]): 
    if item.isdigit(): 
        X_encoded[:, i] = X[:, i] 
    else: 
        label_encoder.append(preprocessing.LabelEncoder()) 
        X_encoded[:, i] = label_encoder[-1].fit_transform(X[:, i]) 

X = X_encoded[:, :-1].astype(int) 
y = X_encoded[:, -1].astype(int) 
  1. 让我们使用径向基函数构建和训练 SVM 回归器:
# Build SVR 
params = {'kernel': 'rbf', 'C': 10.0, 'epsilon': 0.2}  
regressor = SVR(**params) 
regressor.fit(X, y) 

在前面的行中,C 参数指定了误分类的惩罚,而 epsilon 指定了不应用惩罚的限制范围内。

  1. 让我们执行交叉验证以检查回归器的性能:
# Cross validation
import sklearn.metrics as sm

y_pred = regressor.predict(X)
print("Mean absolute error =", round(sm.mean_absolute_error(y, y_pred), 2))
  1. 让我们在一个数据点上测试它:
# Testing encoding on single data instance
input_data = ['Tuesday', '13:35', 'San Francisco', 'yes']
input_data_encoded = [-1] * len(input_data)
count = 0
for i,item in enumerate(input_data):
    if item.isdigit():
        input_data_encoded[i] = int(input_data[i])
    else:
        input_data_encoded[i] = int(label_encoder[count].transform([input_data[i]]))
        count = count + 1 

input_data_encoded = np.array(input_data_encoded)

# Predict and print output for a particular datapoint
print("Predicted traffic:", int(regressor.predict([input_data_encoded])[0]))
  1. 如果你运行此代码,你将在你的终端上看到以下输出:
 Mean absolute error = 4.08
    Predicted traffic: 29

它是如何工作的...

在这个菜谱中,我们使用了在洛杉矶 101 号北高速公路上收集的数据,靠近道奇队比赛的球场。这个位置足够靠近球场,可以检测到比赛期间交通的增加。

观察是在 25 周内进行的,每天有 288 个时间间隔(每 5 分钟一次)。我们基于 SVM 算法构建了一个回归器来预测道奇球场是否有棒球比赛。特别是,我们可以根据以下预测器的值估计通过该位置的汽车数量:日期、时间、对手球队以及是否正在进行棒球比赛。

更多...

支持向量回归SVR)与 SVMs 的原理相同。事实上,SVR 是从 SVMs 调整而来的,其中因变量是数值而不是分类的。使用 SVR 的主要优点之一是它是一种非参数技术。

参见

使用 TensorFlow 简化机器学习工作流程

TensorFlow 是一个开源的数值计算库。该库由谷歌程序员创建。它提供了构建深度学习模型所需的所有工具,并为开发者提供了一个黑盒接口来编程。

准备工作

在这个菜谱中,我们将介绍 TensorFlow 框架,使用简单的神经网络来分类 iris 物种。我们将使用 iris 数据集,该数据集包含以下物种的 50 个样本:

  • 爱丽丝·塞托萨

  • 爱丽丝·维吉尼卡

  • 爱丽丝·维吉尼卡

从每个样本测量了四个特征,即花萼和花瓣的长度和宽度,单位为厘米。

包含以下变量:

  • 花萼长度(厘米)

  • 花萼宽度(厘米)

  • 花瓣长度(厘米)

  • 花瓣宽度(厘米)

  • 类别:setosaversicolorvirginica

如何做...

让我们看看如何使用 TensorFlow 简化机器学习工作流程:

  1. 我们像往常一样,首先导入库:
from sklearn import datasets
from sklearn import model_selection
import tensorflow as tf

前两个库仅用于加载数据和分割数据。第三个库加载了tensorflow库。

  1. 加载iris数据集:
iris = datasets.load_iris()
  1. 加载并分割特征和类别:
x_train, x_test, y_train, y_test = model_selection.train_test_split(iris.data, 
                                                                    iris.target, 
                                                                    test_size=0.7, 
                                                                    random_state=1)

数据被分为 70%用于训练和 30%用于测试。random_state=1参数是随机数生成器使用的种子。

  1. 现在,我们将构建一个包含一个隐藏层和 10 个节点的简单神经网络:
feature_columns = tf.contrib.learn.infer_real_valued_columns_from_input(x_train)
classifier_tf = tf.contrib.learn.DNNClassifier(feature_columns=feature_columns, 
                                                 hidden_units=[10], 
                                                 n_classes=3)
  1. 然后我们调整网络:
classifier_tf.fit(x_train, y_train, steps=5000)
  1. 然后我们将进行预测:
predictions = list(classifier_tf.predict(x_test, as_iterable=True))
  1. 最后,我们将计算模型的accuracy指标:
n_items = y_test.size
accuracy = (y_test == predictions).sum() / n_items
print("Accuracy :", accuracy)

返回以下结果:

Accuracy : 0.9333333333333333

它是如何工作的...

在这个配方中,我们使用了tensorflow库来构建一个简单的神经网络,用于从四个测量的特征中分类鸢尾花种类。这样,我们看到了如何使用tensorflow库实现基于机器学习算法的模型是多么简单。关于这个主题,以及一般深度神经网络,将在第十三章,“深度神经网络”中详细分析。

更多内容...

TensorFlow 为 Python、C、C++、Java、Go 和 Rust 提供了原生 API。可用的第三方 API 包括 C#、R 和 Scala。自 2017 年 10 月起,它已集成即时执行功能,允许立即执行由 Python 引用的操作。

相关内容

实现堆叠方法

不同的方法组合可以带来更好的结果:这个说法在我们生活的不同方面都适用,并且也适用于基于机器学习的算法。堆叠是将各种机器学习算法组合的过程。这项技术归功于美国数学家、物理学家和计算机科学家 David H. Wolpert。

在这个配方中,我们将学习如何实现堆叠方法。

准备工作

我们将使用heamy库来堆叠我们在前一个配方中使用的两个模型。heamy库是一套用于竞争性数据科学的有用工具。

如何做...

让我们看看如何实现堆叠方法:

  1. 我们首先导入库:
from heamy.dataset import Dataset
from heamy.estimator import Regressor
from heamy.pipeline import ModelsPipeline

from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error
  1. 加载boston数据集,已在第一章,“监督学习领域”,用于估算房价配方:
data = load_boston()
  1. 分割数据:
X, y = data['data'], data['target']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=2)
  1. 让我们创建数据集:
Data = Dataset(X_train,y_train,X_test)
  1. 现在,我们可以构建在堆叠过程中使用的两个模型:
RfModel = Regressor(dataset=Data, estimator=RandomForestRegressor, parameters={'n_estimators': 50},name='rf')
LRModel = Regressor(dataset=Data, estimator=LinearRegression, parameters={'normalize': True},name='lr')
  1. 是时候堆叠这些模型了:
Pipeline = ModelsPipeline(RfModel,LRModel)
StackModel = Pipeline.stack(k=10,seed=2)
  1. 现在,我们将对堆叠数据进行LinearRegression模型的训练:
Stacker = Regressor(dataset=StackModel, estimator=LinearRegression)
  1. 最后,我们将计算结果以验证模型:
Results = Stacker.predict()
Results = Stacker.validate(k=10,scorer=mean_absolute_error)

它是如何工作的...

堆叠泛化通过推断分类器/回归器相对于提供的训练数据集的偏差来工作。这种推断是通过将原始泛化器的假设推广到第二个空间来实现的,该空间的输入是原始泛化器的假设,输出是正确的假设。当与多个生成器一起使用时,堆叠泛化是交叉验证的替代方案。

更多信息...

堆叠泛化试图通过忽略或纠正它们的缺点来利用每个算法的优点。它可以被视为一种纠正你算法中错误的机制。另一个执行堆叠过程的库是 StackNet。

StackNet 是一个基于 Wolpert 的多级堆叠泛化的 Java 框架,旨在提高机器学习预测问题中的准确性。StackNet 模型作为一个神经网络运行,其传递函数的形式可以是任何监督机器学习算法。

参见

第四章:使用无监督学习进行聚类

在本章中,我们将涵盖以下食谱:

  • 使用 k-means 算法进行聚类数据

  • 使用矢量量化压缩图像

  • 使用层次聚类对数据进行分组

  • 评估聚类算法的性能

  • 使用基于密度的空间聚类应用噪声DBSCAN)算法估计簇的数量

  • 在股票市场数据中寻找模式

  • 构建客户细分模型

  • 使用自动编码器重建手写数字图像

技术要求

为了处理本章中的食谱,你需要以下文件(可在 GitHub 上找到):

  • kmeans.py

  • data_multivar.txt

  • vector_quantization.py

  • flower_image.jpg

  • agglomerative.py

  • performance.py

  • data_perf.txt

  • estimate_clusters.py

  • stock_market.py

  • symbol_map.json

  • stock_market_data.xlsx

  • customer_segmentation.py

  • wholesale.csv

  • AutoencMnist.py

引言

无监督学习是机器学习中的一个范例,其中我们构建模型而不依赖于标记的训练数据。到目前为止,我们处理的数据都是以某种方式标记的。这意味着学习算法可以查看这些数据,并学习根据标签对其进行分类。在无监督学习的世界中,我们没有这种机会!这些算法用于当我们想要使用相似度度量在数据集中找到子组时。

在无监督学习中,数据库中的信息会自动提取。所有这些都是在没有分析内容先验知识的情况下发生的。在无监督学习中,没有关于示例所属类别或对应给定输入的输出的信息。我们希望有一个模型能够发现有趣的属性,例如具有相似特征的组,这在聚类中发生。这些算法的应用示例是搜索引擎。这些应用能够根据一个或多个关键词创建与我们的搜索相关的链接列表。

这些算法通过比较数据和寻找相似性或差异来工作。这些算法的有效性取决于它们可以从数据库中提取信息的有用性。可用的数据仅涉及描述每个示例的特征集。

最常见的方法之一是聚类。你可能已经经常听到这个术语被使用;当我们想要在我们的数据中找到簇时,我们主要使用它进行数据分析。这些簇通常是通过使用某种相似度度量来找到的,例如欧几里得距离。无监督学习在许多领域得到广泛应用,例如数据挖掘、医学成像、股票市场分析、计算机视觉和市场细分。

使用 k-means 算法进行聚类数据

k-means 算法是最受欢迎的聚类算法之一。此算法使用数据的各种属性将输入数据划分为 k 个子组。通过一种优化技术实现分组,我们试图最小化数据点与簇对应质心之间的距离平方和。

准备工作

在这个方法中,我们将使用 k-means 算法将数据分组到由相对质心标识的四个簇中。我们还将能够追踪边界以识别每个簇的相关区域。

如何做到这一点...

让我们看看如何使用 k-means 算法进行聚类数据分析:

  1. 此方法的完整代码在您已经提供的 kmeans.py 文件中给出。现在让我们看看它是如何构建的。创建一个新的 Python 文件,并导入以下包:
import numpy as np 
import matplotlib.pyplot as plt 
from sklearn.cluster import KMeans 
  1. 现在让我们加载输入数据并定义簇的数量。我们将使用您已经提供的 data_multivar.txt 文件:
input_file = ('data_multivar.txt')
# Load data
x = []
with open(input_file, 'r') as f:
    for line in f.readlines():
        data = [float(i) for i in line.split(',')]
        x.append(data)

data = np.array(x)
num_clusters = 4
  1. 我们需要看看输入数据的样子。让我们继续在 Python 文件中添加以下代码行:
plt.figure() 
plt.scatter(data[:,0], data[:,1], marker='o',  
        facecolors='none', edgecolors='k', s=30) 
x_min, x_max = min(data[:, 0]) - 1, max(data[:, 0]) + 1 
y_min, y_max = min(data[:, 1]) - 1, max(data[:, 1]) + 1 
plt.title('Input data') 
plt.xlim(x_min, x_max) 
plt.ylim(y_min, y_max) 
plt.xticks(()) 
plt.yticks(()) 

如果你运行此代码,你将得到以下输出:

图片

  1. 我们现在准备好训练模型了。让我们初始化 kmeans 对象并对其进行训练:
kmeans = KMeans(init='k-means++', n_clusters=num_clusters, n_init=10) 
kmeans.fit(data)
  1. 数据训练完成后,我们需要可视化边界。让我们继续在 Python 文件中添加以下代码行:
# Step size of the mesh 
step_size = 0.01 

# Plot the boundaries 
x_min, x_max = min(data[:, 0]) - 1, max(data[:, 0]) + 1 
y_min, y_max = min(data[:, 1]) - 1, max(data[:, 1]) + 1 
x_values, y_values = np.meshgrid(np.arange(x_min, x_max, step_size), np.arange(y_min, y_max, step_size)) 

# Predict labels for all points in the mesh 
predicted_labels = kmeans.predict(np.c_[x_values.ravel(), y_values.ravel()]) 
  1. 我们刚刚在点网格上评估了模型。让我们绘制这些结果以查看边界:
# Plot the results 
predicted_labels = predicted_labels.reshape(x_values.shape) 
plt.figure() 
plt.clf() 
plt.imshow(predicted_labels, interpolation='nearest', 
           extent=(x_values.min(), x_values.max(), y_values.min(), y_values.max()), 
           cmap=plt.cm.Paired, 
           aspect='auto', origin='lower') 

plt.scatter(data[:,0], data[:,1], marker='o',  
        facecolors='none', edgecolors='k', s=30) 
  1. 现在让我们在它上面叠加 centroids
centroids = kmeans.cluster_centers_ 
plt.scatter(centroids[:,0], centroids[:,1], marker='o', s=200, linewidths=3, 
        color='k', zorder=10, facecolors='black') 
x_min, x_max = min(data[:, 0]) - 1, max(data[:, 0]) + 1 
y_min, y_max = min(data[:, 1]) - 1, max(data[:, 1]) + 1 
plt.title('Centoids and boundaries obtained using KMeans') 
plt.xlim(x_min, x_max) 
plt.ylim(y_min, y_max) 
plt.xticks(()) 
plt.yticks(()) 
plt.show() 

如果你运行此代码,你应该看到以下输出:

图片

四个质心和它们的边界被充分突出显示。

它是如何工作的...

K-means 算法是由詹姆斯·麦克昆(James MacQueen)开发的,他在 1967 年设计它是为了根据对象的属性将对象分组到 k 个分区。它是 期望最大化expectation-maximization,简称 EM)算法的一种变体,其目标是确定由高斯分布生成的 k 个数据点所属的组。两种算法之间的区别在于欧几里得距离计算方法。在 k-means 中,假设对象的属性可以表示为向量,从而形成一个向量空间。目标是使总簇内方差(或标准差)最小化。每个簇由一个质心来标识。

算法遵循以下迭代过程:

  1. 选择 k 个簇的数量

  2. 初始时创建 k 个分区,并将每个条目分区随机分配,或者使用一些启发式信息

  3. 计算每个组的质心

  4. 计算每个观测值与每个簇质心之间的距离

  5. 然后,通过将每个数据点与最近的质心关联来构建一个新的分区

  6. 新簇的质心被重新计算

  7. 重复步骤 4 到 6,直到算法收敛

更多内容...

该算法的目的是定位k个质心,每个聚类一个。每个质心的位置尤其重要,因为不同的位置会导致不同的结果。最佳选择是将它们尽可能地彼此分开。当这样做的时候,你必须将每个对象与最近的质心关联起来。这样,我们将得到一个初步分组。完成第一次循环后,我们通过重新计算新的k个质心作为簇的重心来进入下一个循环。一旦定位到这些新的k个质心,你需要在新数据集和新的最近质心之间建立新的连接。在这些操作结束时,执行一个新的循环。由于这个循环,我们可以注意到k个质心会逐步改变位置,直到它们被修改。因此,质心不再移动。

参考资料还有

使用矢量量化压缩图像

k 均值聚类的最主要应用之一是矢量量化。简单来说,矢量量化是四舍五入的N维版本。当我们处理一维数据,例如数字时,我们使用四舍五入技术来减少存储该值所需的内存。例如,如果我们只想精确到小数点后第二位,我们只需存储 23.73;或者,如果我们不关心小数位,我们可以只存储 24。这取决于我们的需求和愿意做出的权衡。

类似地,当我们将这个概念扩展到N维数据时,它就变成了矢量量化。当然,其中还有很多细微之处!矢量量化在图像压缩中非常流行,我们使用比原始图像更少的位数来存储每个像素,以达到压缩的目的。

准备工作

在这个菜谱中,我们将使用一个示例图像,然后我们将通过减少位数进一步压缩图像。

如何操作...

让我们看看如何使用矢量量化来压缩图像:

  1. 该菜谱的完整代码在已经提供给你的vector_quantization.py文件中给出。让我们看看它是如何构建的。我们首先导入所需的包。创建一个新的 Python 文件,并添加以下行:
import argparse 

import numpy as np 
from scipy import misc  
from sklearn import cluster 
import matplotlib.pyplot as plt
  1. 让我们创建一个函数来解析输入参数。我们将能够传递图像和每像素位数作为输入参数:
def build_arg_parser(): 
    parser = argparse.ArgumentParser(description='Compress the input image \ 
            using clustering') 
    parser.add_argument("--input-file", dest="input_file", required=True, 
            help="Input image") 
    parser.add_argument("--num-bits", dest="num_bits", required=False, 
            type=int, help="Number of bits used to represent each pixel") 
    return parser 
  1. 让我们创建一个函数来压缩输入图像:
def compress_image(img, num_clusters): 
    # Convert input image into (num_samples, num_features)  
    # array to run kmeans clustering algorithm  
   X = img.reshape((-1, 1))   

    # Run kmeans on input data 
    kmeans = cluster.KMeans(n_clusters=num_clusters, n_init=4, random_state=5) 
    kmeans.fit(X) 
    centroids = kmeans.cluster_centers_.squeeze() 
    labels = kmeans.labels_ 

    # Assign each value to the nearest centroid and  
    # reshape it to the original image shape 
    input_image_compressed = np.choose(labels, centroids).reshape(img.shape) 

    return input_image_compressed 
  1. 压缩图像后,我们需要看看它如何影响质量。让我们定义一个函数来绘制输出图像:
def plot_image(img, title): 
    vmin = img.min() 
    vmax = img.max() 
    plt.figure() 
    plt.title(title) 
    plt.imshow(img, cmap=plt.cm.gray, vmin=vmin, vmax=vmax)
  1. 我们现在准备好使用所有这些函数。让我们定义一个主要函数,它接受输入参数,处理它们,并提取输出图像:
if __name__=='__main__': 
    args = build_arg_parser().parse_args() 
    input_file = args.input_file 
    num_bits = args.num_bits 

    if not 1 <= num_bits <= 8: 
        raise TypeError('Number of bits should be between 1 and 8') 

    num_clusters = np.power(2, num_bits) 

    # Print compression rate 
    compression_rate = round(100 * (8.0 - args.num_bits) / 8.0, 2) 
    print("The size of the image will be reduced by a factor of", 8.0/args.num_bits) 
    print("Compression rate = " + str(compression_rate) + "%") 
  1. 让我们加载输入图像:
    # Load input image 
    input_image = misc.imread(input_file, True).astype(np.uint8) 

    # original image  
    plot_image(input_image, 'Original image') 
  1. 现在,让我们使用输入参数来压缩这张图像:
    # compressed image  
    input_image_compressed = compress_image(input_image, num_clusters) 
    plot_image(input_image_compressed, 'Compressed image; compression rate = '  
            + str(compression_rate) + '%') 

    plt.show() 
  1. 我们现在准备好运行代码;在你的终端上运行以下命令:
    $ python vector_quantization.py --input-file flower_image.jpg --num-bits 4

返回以下结果:

The size of the image will be reduced by a factor of 2.0
Compression rate = 50.0%

输入图像看起来如下:

你应该得到一个压缩图像作为输出:

  1. 让我们进一步压缩图像,通过减少位数到2。在你的终端上运行以下命令:
    $ python vector_quantization.py --input-file flower_image.jpg --num-bits 2  

返回以下结果:

The size of the image will be reduced by a factor of 4.0
Compression rate = 75.0%

你应该得到以下压缩图像作为输出:

  1. 如果你将位数减少到1,你可以看到它将变成只有黑白两种颜色的二值图像。运行以下命令:
    $ python vector_quantization.py --input-file flower_image.jpg --num-bits 1  

返回以下结果:

The size of the image will be reduced by a factor of 8.0
Compression rate = 87.5%

你将得到以下输出:

我们已经看到,通过进一步压缩图像,图像质量已经大幅下降。

它是如何工作的...

向量量化是一种用于信号压缩、图像编码和语音的算法。我们使用几何标准(欧几里得距离)来寻找簇。因此,它是一个无监督训练的例子。这是一种通过原型向量的分布来建模概率密度函数的技术。向量量化通过使用与它们更接近的相似数量的点来将大量点(向量)划分为簇。每个簇由其质心点(如 k-means)表示。

更多内容...

向量量化算法可以用来将数据集划分为多个簇。该算法基于计算欧几里得距离来分配样本到它们所属的簇。算法包括以下步骤:

  1. 在开始时,所有向量都被分配到同一个簇,其质心是所有向量的平均值。

  2. 对于每个质心,引入一个扰动,生成两个新的簇中心。旧的代表性被丢弃。

  3. 每个载体根据最小距离标准重新分配到新的簇中。

  4. 新的代表性是分配给每个簇的向量的平均值。这些将成为簇的新中心。

  5. 如果满足结束条件,算法终止。如果不满足,返回步骤 2。

参见

使用聚合聚类分组数据

在我们讨论层次聚类之前,我们需要了解层次聚类。层次聚类是指一系列通过连续分割或合并来创建树状聚类的聚类算法,它们使用树来表示。层次聚类算法可以是自底向上或自顶向下。那么,这意味着什么呢?在自底向上的算法中,每个数据点被视为一个单独的聚类,包含一个对象。然后这些聚类依次合并,直到所有聚类合并成一个巨大的聚类。这被称为聚合聚类。另一方面,自顶向下的算法从一个巨大的聚类开始,依次分割这些聚类,直到达到单个数据点。

准备工作

在层次聚类中,我们通过递归地使用自顶向下或自底向上的方式对实例进行分区来构建聚类。我们可以将这些方法分为以下几类:

  • 聚合算法(自底向上):在这里,我们从单个统计单元获得解决方案。在每次迭代中,我们聚合最相关的统计单元,直到形成一个单一的聚类为止。

  • 划分算法(自顶向下):在这里,所有单元属于同一个类别,并且与其它单元不相似的单元在每个后续迭代中添加到一个新的聚类中。

这两种方法都会产生一个树状图。这代表了一个嵌套的对象组,以及组之间变化的相似性水平。通过在所需的相似性水平处切割树状图,我们可以得到数据对象的聚类。聚类的合并或分割是通过一个相似性度量来完成的,该度量优化了一个标准。

如何做...

让我们看看如何使用聚合聚类来分组数据:

  1. 这个菜谱的完整代码在提供的agglomerative.py文件中给出。现在让我们看看它是如何构建的。创建一个新的 Python 文件,并导入必要的包:
import numpy as np 
import matplotlib.pyplot as plt 
from sklearn.cluster import AgglomerativeClustering 
from sklearn.neighbors import kneighbors_graph 
  1. 让我们定义一个执行聚合聚类的函数:
def perform_clustering(X, connectivity, title, num_clusters=3, linkage='ward'): 
    plt.figure() 
    model = AgglomerativeClustering(linkage=linkage,  
                    connectivity=connectivity, n_clusters=num_clusters) 
    model.fit(X) 
  1. 让我们提取标签并指定图形中标记的形状:
    # extract labels 
    labels = model.labels_ 
` 
    # specify marker shapes for different clusters 
    markers = '.vx' 
  1. 遍历数据点并相应地使用不同的标记进行绘图:
    for i, marker in zip(range(num_clusters), markers): 
        # plot the points belong to the current cluster 
        plt.scatter(X[labels==i, 0], X[labels==i, 1], s=50,  
                    marker=marker, color='k', facecolors='none') 

    plt.title(title)
  1. 为了展示聚合聚类的优势,我们需要在空间上连接且空间上彼此靠近的数据点上运行它。我们希望连接的数据点属于同一簇,而不是仅仅空间上彼此靠近的数据点。现在让我们定义一个函数来获取螺旋线上的数据点集:
def get_spiral(t, noise_amplitude=0.5): 
    r = t 
    x = r * np.cos(t) 
    y = r * np.sin(t) 

    return add_noise(x, y, noise_amplitude) 
  1. 在前面的函数中,我们向曲线添加了一些噪声,因为它增加了一些不确定性。让我们定义这个函数:
def add_noise(x, y, amplitude): 
    X = np.concatenate((x, y)) 
    X += amplitude * np.random.randn(2, X.shape[1]) 
    return X.T 
  1. 现在让我们定义另一个函数来获取位于玫瑰曲线上的数据点:
def get_rose(t, noise_amplitude=0.02): 
    # Equation for "rose" (or rhodonea curve); if k is odd, then 
    # the curve will have k petals, else it will have 2k petals 
    k = 5        
    r = np.cos(k*t) + 0.25  
    x = r * np.cos(t) 
    y = r * np.sin(t) 

    return add_noise(x, y, noise_amplitude) 
  1. 为了增加更多多样性,让我们也定义一个hypotrochoid函数:
def get_hypotrochoid(t, noise_amplitude=0): 
    a, b, h = 10.0, 2.0, 4.0 
    x = (a - b) * np.cos(t) + h * np.cos((a - b) / b * t)  
    y = (a - b) * np.sin(t) - h * np.sin((a - b) / b * t)  

    return add_noise(x, y, 0)
  1. 我们现在准备好定义主函数:
if __name__=='__main__': 
    # Generate sample data 
    n_samples = 500  
    np.random.seed(2) 
    t = 2.5 * np.pi * (1 + 2 * np.random.rand(1, n_samples)) 
    X = get_spiral(t) 

    # No connectivity 
    connectivity = None  
    perform_clustering(X, connectivity, 'No connectivity') 

    # Create K-Neighbors graph  
    connectivity = kneighbors_graph(X, 10, include_self=False) 
    perform_clustering(X, connectivity, 'K-Neighbors connectivity') 

    plt.show() 

如果你运行此代码,如果我们不使用任何连通性,你将得到以下输出:

第二个输出图看起来如下:

如你所见,使用连通性功能使我们能够将相互连接的数据点分组,而不是根据它们的空间位置进行聚类。

它是如何工作的...

在聚合聚类中,每个观测开始于其簇,随后簇被合并。合并簇的策略如下:

  • 伍德聚类最小化所有簇内平方差的和。

  • 最大或完全链接用于最小化观测对簇之间的最大距离。

  • 平均链接用于最小化所有观测对簇之间的距离的平均值。

  • 单链接用于最小化观测对簇之间最近观察的距离。

还有更多…

为了决定哪些簇必须合并,有必要定义簇之间差异的度量。在大多数层次聚类方法中,使用特定的指标来量化两个元素对之间的距离,以及一个链接标准,它将两个元素集(簇)之间的差异定义为两个集中元素对之间距离的函数。

这些常用指标如下:

  • 欧几里得距离

  • 曼哈顿距离

  • 均匀规则

  • 马哈拉诺比斯距离,它通过变量不同的尺度和相关性来校正数据

  • 两个向量之间的角度

  • 汉明距离,它衡量将一个成员转换为另一个成员所需的最小替换次数

参见

评估聚类算法的性能

到目前为止,我们已经构建了不同的聚类算法,但还没有测量它们的性能。在监督学习中,将原始标签与预测值进行比较来计算它们的准确率。相比之下,在无监督学习中,我们没有标签,因此我们需要找到一种方法来衡量我们算法的性能。

准备工作

评估聚类算法的一个好方法是看簇之间的分离程度。簇是否分离得很好?簇中的数据点是否足够紧密?我们需要一个可以量化这种行为的指标。我们将使用一个称为轮廓系数的指标。这个分数为每个数据点定义;这个系数的定义如下:

在这里,x是当前数据点到同一簇中所有其他数据点的平均距离,而y是当前数据点到下一个最近簇中所有数据点的平均距离。

如何做...

让我们看看如何评估聚类算法的性能:

  1. 这个菜谱的完整代码在已经提供给你的performance.py文件中给出。现在让我们看看它是如何构建的。创建一个新的 Python 文件,并导入以下包:
import numpy as np 
import matplotlib.pyplot as plt 
from sklearn import metrics 
from sklearn.cluster import KMeans 
  1. 让我们从已经提供给你的data_perf.txt文件中加载输入数据:
input_file = ('data_perf.txt')

x = []
with open(input_file, 'r') as f:
    for line in f.readlines():
        data = [float(i) for i in line.split(',')]
        x.append(data)

data = np.array(x)
  1. 为了确定最佳簇的数量,让我们遍历一系列值并查看它在何处达到峰值:
scores = [] 
range_values = np.arange(2, 10) 

for i in range_values: 
    # Train the model 
    kmeans = KMeans(init='k-means++', n_clusters=i, n_init=10) 
    kmeans.fit(data) 
    score = metrics.silhouette_score(data, kmeans.labels_,  
                metric='euclidean', sample_size=len(data)) 

    print("Number of clusters =", i)
    print("Silhouette score =", score)

    scores.append(score) 
  1. 现在,让我们绘制图表以查看它在何处达到峰值:
# Plot scores 
plt.figure() 
plt.bar(range_values, scores, width=0.6, color='k', align='center') 
plt.title('Silhouette score vs number of clusters') 

# Plot data 
plt.figure() 
plt.scatter(data[:,0], data[:,1], color='k', s=30, marker='o', facecolors='none') 
x_min, x_max = min(data[:, 0]) - 1, max(data[:, 0]) + 1 
y_min, y_max = min(data[:, 1]) - 1, max(data[:, 1]) + 1 
plt.title('Input data') 
plt.xlim(x_min, x_max) 
plt.ylim(y_min, y_max) 
plt.xticks(()) 
plt.yticks(()) 

plt.show()
  1. 如果你运行这段代码,你将在终端上得到以下输出:
Number of clusters = 2
Silhouette score = 0.5290397175472954
Number of clusters = 3
Silhouette score = 0.5572466391184153
Number of clusters = 4
Silhouette score = 0.5832757517829593
Number of clusters = 5
Silhouette score = 0.6582796909760834
Number of clusters = 6
Silhouette score = 0.5991736976396735
Number of clusters = 7
Silhouette score = 0.5194660249299737
Number of clusters = 8
Silhouette score = 0.44937089046511863
Number of clusters = 9
Silhouette score = 0.3998899991555578

柱状图看起来如下:

就像这些分数一样,最佳配置是五个簇。让我们看看数据实际上看起来是什么样子:

我们可以直观地确认数据实际上有五个簇。我们只是以包含五个不同簇的小数据集为例。当你处理包含无法轻易可视化的高维数据的巨大数据集时,这种方法变得非常有用。

它是如何工作的...

sklearn.metrics.silhouette_score函数计算所有样本的平均轮廓系数。对于每个样本,计算两个距离:平均簇内距离(x)和平均最近簇距离(y)。一个样本的轮廓系数由以下方程给出:

实际上,y是样本与不包含样本的最近簇之间的距离。

更多内容...

最佳值为 1,最差值为-1。0 表示重叠的簇,而小于 0 的值表示该特定样本被错误地附加到簇中。

参见

使用 DBSCAN 算法估计簇数

当我们讨论 k-means 算法时,我们看到了必须给出簇的数量作为输入参数之一。在现实世界中,我们不会有这样的信息可用。我们可以肯定地扫描参数空间,使用轮廓系数得分找出最佳簇数,但这将是一个昂贵的流程!一个返回我们数据中簇数的方法将是解决这个问题的绝佳解决方案。DBSCAN 正是为我们做到这一点。

准备工作

在这个菜谱中,我们将使用sklearn.cluster.DBSCAN函数执行 DBSCAN 分析。我们将使用与之前评估聚类算法性能data_perf.txt)菜谱中相同的相同数据,以比较两种方法。

如何做...

让我们看看如何使用 DBSCAN 算法自动估计簇数:

  1. 此菜谱的完整代码已在estimate_clusters.py文件中提供。现在让我们看看它是如何构建的。创建一个新的 Python 文件,并导入必要的包:
from itertools import cycle 
import numpy as np 
from sklearn.cluster import DBSCAN 
from sklearn import metrics 
import matplotlib.pyplot as plt 
  1. data_perf.txt文件中加载数据。这是我们在上一个菜谱中使用的相同文件,这将帮助我们比较同一数据集上的方法:
# Load data
input_file = ('data_perf.txt')

x = []
with open(input_file, 'r') as f:
    for line in f.readlines():
        data = [float(i) for i in line.split(',')]
        x.append(data)

X = np.array(x)
  1. 我们需要找到最佳参数,所以让我们初始化几个变量:
# Find the best epsilon 
eps_grid = np.linspace(0.3, 1.2, num=10) 
silhouette_scores = [] 
eps_best = eps_grid[0] 
silhouette_score_max = -1 
model_best = None 
labels_best = None
  1. 让我们扫描参数空间:
for eps in eps_grid: 
    # Train DBSCAN clustering model 
    model = DBSCAN(eps=eps, min_samples=5).fit(X) 

    # Extract labels 
    labels = model.labels_
  1. 对于每次迭代,我们需要提取性能指标:
    # Extract performance metric  
    silhouette_score = round(metrics.silhouette_score(X, labels), 4) 
    silhouette_scores.append(silhouette_score) 

    print("Epsilon:", eps, " --> silhouette score:", silhouette_score) 
  1. 我们需要存储最佳得分及其相关的 epsilon 值:
    if silhouette_score > silhouette_score_max: 
        silhouette_score_max = silhouette_score 
        eps_best = eps 
        model_best = model 
        labels_best = labels 
  1. 让我们现在绘制条形图,如下所示:
# Plot silhouette scores vs epsilon 
plt.figure() 
plt.bar(eps_grid, silhouette_scores, width=0.05, color='k', align='center') 
plt.title('Silhouette score vs epsilon') 

# Best params 
print("Best epsilon =", eps_best) 
  1. 让我们存储最佳模型和标签:
# Associated model and labels for best epsilon 
model = model_best  
labels = labels_best
  1. 一些数据点可能保持未分配。我们需要识别它们,如下所示:
# Check for unassigned datapoints in the labels 
offset = 0 
if -1 in labels: 
    offset = 1 
  1. 按如下方式提取簇数:
# Number of clusters in the data  
num_clusters = len(set(labels)) - offset  

print("Estimated number of clusters =", num_clusters)
  1. 我们需要提取所有核心样本,如下所示:
# Extracts the core samples from the trained model 
mask_core = np.zeros(labels.shape, dtype=np.bool) 
mask_core[model.core_sample_indices_] = True 
  1. 让我们可视化结果簇。我们将首先提取唯一标签并指定不同的标记:
# Plot resultant clusters  
plt.figure() 
labels_uniq = set(labels) 
markers = cycle('vo^s<>') 
  1. 现在,让我们遍历簇并使用不同的标记绘制数据点:
for cur_label, marker in zip(labels_uniq, markers): 
    # Use black dots for unassigned datapoints 
    if cur_label == -1: 
        marker = '.' 

    # Create mask for the current label 
    cur_mask = (labels == cur_label) 

    cur_data = X[cur_mask & mask_core] 
    plt.scatter(cur_data[:, 0], cur_data[:, 1], marker=marker, 
             edgecolors='black', s=96, facecolors='none') 
    cur_data = X[cur_mask & ~mask_core] 
    plt.scatter(cur_data[:, 0], cur_data[:, 1], marker=marker, 
             edgecolors='black', s=32) 
plt.title('Data separated into clusters') 
plt.show()
  1. 如果你运行此代码,你将在你的终端上得到以下输出:
Epsilon: 0.3 --> silhouette score: 0.1287
Epsilon: 0.39999999999999997 --> silhouette score: 0.3594
Epsilon: 0.5 --> silhouette score: 0.5134
Epsilon: 0.6 --> silhouette score: 0.6165
Epsilon: 0.7 --> silhouette score: 0.6322
Epsilon: 0.7999999999999999 --> silhouette score: 0.6366
Epsilon: 0.8999999999999999 --> silhouette score: 0.5142
Epsilon: 1.0 --> silhouette score: 0.5629
Epsilon: 1.0999999999999999 --> silhouette score: 0.5629
Epsilon: 1.2 --> silhouette score: 0.5629
Best epsilon = 0.7999999999999999
Estimated number of clusters = 5

这将生成以下条形图:

图片

让我们看看标记的数据点和以下输出中用实心点标记的未分配数据点:

图片

它是如何工作的...

DBSCAN 通过将数据点视为密集簇的组来工作。如果一个点属于一个簇,那么应该有很多其他点也属于同一个簇。我们可以控制的参数之一是此点与其他点之间的最大距离。这被称为epsilon。给定簇中的任意两点之间的距离不应超过 epsilon。这种方法的主要优点之一是它可以处理异常值。如果有一些点位于低密度区域中孤立存在,DBSCAN 将将这些点检测为异常值,而不是强迫它们进入一个簇。

更多内容…

DBSCAN 具有以下优缺点:

优点 缺点

|

  • 它不需要事先知道簇的数量。

  • 它可以找到任意形状的簇。

  • 它只需要两个参数。

|

  • 聚类的质量取决于其距离度量。

  • 它无法对密度差异大的数据集进行分类。

|

参考信息

在股票市场数据中寻找模式

让我们看看如何使用无监督学习进行股票市场分析。由于我们不知道有多少个簇,我们将在簇上使用一个称为亲和传播AP)的算法。它试图为我们数据中的每个簇找到一个代表性的数据点,以及数据点对之间的相似度度量,并将所有数据点视为其各自簇的潜在代表,也称为示例

准备工作

在这个菜谱中,我们将分析公司在指定时间段内的股票市场变化。我们的目标是找出哪些公司在时间上的报价行为相似。

如何操作...

让我们看看如何寻找股票市场数据中的模式:

  1. 此菜谱的完整代码已在提供的 stock_market.py 文件中给出。现在让我们看看它是如何构建的。创建一个新的 Python 文件,并导入以下包:
import json
import sys
import pandas as pd

import numpy as np
from sklearn import covariance, cluster
  1. 我们需要一个包含所有符号及其相关名称的文件。这些信息位于提供的 symbol_map.json 文件中。让我们按照以下方式加载它:
# Input symbol file 
symbol_file = 'symbol_map.json' 
  1. 让我们从 symbol_map.json 文件中读取数据:
# Load the symbol map 
with open(symbol_file, 'r') as f: 
    symbol_dict = json.loads(f.read()) 

symbols, names = np.array(list(symbol_dict.items())).T 
  1. 现在让我们加载数据。我们将使用一个 Excel 文件(stock_market_data.xlsx);这是一个多工作表文件,每个符号对应一个:
quotes = []

excel_file = 'stock_market_data.xlsx'

for symbol in symbols:
    print('Quote history for %r' % symbol, file=sys.stderr)
    quotes.append(pd.read_excel(excel_file, symbol))
  1. 由于我们需要一些特征点进行分析,我们将使用每天开盘价和收盘价之间的差异来分析数据:
# Extract opening and closing quotes 
opening_quotes = np.array([quote.open for quote in quotes]).astype(np.float) 
closing_quotes = np.array([quote.close for quote in quotes]).astype(np.float) 

# The daily fluctuations of the quotes  
delta_quotes = closing_quotes - opening_quotes 
  1. 让我们构建一个图模型:
# Build a graph model from the correlations 
edge_model = covariance.GraphicalLassoCV(cv=3) 
  1. 在我们使用之前,我们需要标准化数据:
# Standardize the data  
X = delta_quotes.copy().T 
X /= X.std(axis=0) 
  1. 现在让我们使用这些数据来训练模型:
# Train the model 
with np.errstate(invalid='ignore'): 
    edge_model.fit(X) 
  1. 现在我们已经准备好构建聚类模型,如下所示:
# Build clustering model using affinity propagation 
_, labels = cluster.affinity_propagation(edge_model.covariance_) 
num_labels = labels.max() 

# Print the results of clustering 
for i in range(num_labels + 1): 
    print "Cluster", i+1, "-->", ', '.join(names[labels == i]) 
  1. 如果你运行此代码,你将在终端得到以下输出:
Cluster 1 --> Apple, Amazon, Yahoo
Cluster 2 --> AIG, American express, Bank of America, DuPont de Nemours, General Dynamics, General Electrics, Goldman Sachs, GlaxoSmithKline, Home Depot, Kellogg
Cluster 3 --> Boeing, Canon, Caterpillar, Ford, Honda
Cluster 4 --> Colgate-Palmolive, Kimberly-Clark
Cluster 5 --> Cisco, Dell, HP, IBM
Cluster 6 --> Comcast, Cablevision
Cluster 7 --> CVS
Cluster 8 --> ConocoPhillips, Chevron

识别出八个簇。从初步分析中,我们可以看到,分组的公司似乎对待相同的产品:IT、银行、工程、洗涤剂和计算机。

它是如何工作的...

AP 是一种基于点(项目)之间传递消息概念的聚类算法。与 k-means 等聚类算法不同,AP 不需要预先定义聚类数量。AP 搜索输入集的代表成员(样本),实际上这些成员代表了各个簇。

AP 算法的核心是识别一组样本。在输入中,取数据对之间的相似性矩阵。数据作为消息交换实值,直到出现合适的样本,从而获得良好的簇。

更多内容…

为了执行 AP 聚类,使用了sklearn.cluster.affinity_propagation()函数。在具有相似相似度和偏好的训练样本的情况下,聚类中心和标签的分配取决于偏好。如果偏好小于相似度,则将返回单个聚类中心和每个样本的 0 标签。否则,每个训练样本将成为其聚类中心,并分配一个唯一的标记。

参见

构建客户细分模型

无监督学习的主要应用之一是市场细分。这是当我们不是始终有标记数据可用时的情况,但重要的是要对市场进行细分,以便人们可以针对个体群体。这在广告、库存管理、实施分销策略和大众媒体中非常有用。让我们来应用无监督学习到一个这样的用例,看看它如何有用。

准备工作

我们将处理一个批发商及其客户。我们将使用可用的数据archive.ics.uci.edu/ml/datasets/Wholesale+customers。电子表格包含客户对不同类型商品消费的数据,我们的目标是找到簇,以便他们可以优化他们的销售和分销策略。

如何实现它…

让我们看看如何构建一个客户细分模型:

  1. 这个菜谱的完整代码在已经提供给你的customer_segmentation.py文件中给出。现在让我们看看它是如何构建的。创建一个新的 Python 文件,并导入以下包:
import csv 
import numpy as np 
from sklearn.cluster import MeanShift, estimate_bandwidth 
import matplotlib.pyplot as plt 
  1. 让我们加载wholesale.csv文件中的输入数据,该文件已经提供给你:
# Load data from input file 
input_file = 'wholesale.csv' 
file_reader = csv.reader(open(input_file, 'rt'), delimiter=',') 
X = [] 
for count, row in enumerate(file_reader): 
    if not count: 
        names = row[2:] 
        continue 

    X.append([float(x) for x in row[2:]]) 

# Input data as numpy array 
X = np.array(X) 
  1. 让我们构建一个均值漂移模型:
# Estimating the bandwidth  
bandwidth = estimate_bandwidth(X, quantile=0.8, n_samples=len(X)) 

# Compute clustering with MeanShift 
meanshift_estimator = MeanShift(bandwidth=bandwidth, bin_seeding=True) 
meanshift_estimator.fit(X) 
labels = meanshift_estimator.labels_ 
centroids = meanshift_estimator.cluster_centers_ 
num_clusters = len(np.unique(labels)) 

print("Number of clusters in input data =", num_clusters) 
  1. 让我们按照以下方式打印我们获得的簇中心:
print("Centroids of clusters:")
print('\t'.join([name[:3] for name in names]))
for centroid in centroids:
    print('\t'.join([str(int(x)) for x in centroid]))
  1. 让我们可视化一些特征以获得输出的感觉:
# Visualizing data 

centroids_milk_groceries = centroids[:, 1:3] 

# Plot the nodes using the coordinates of our centroids_milk_groceries 
plt.figure() 
plt.scatter(centroids_milk_groceries[:,0], centroids_milk_groceries[:,1],  
        s=100, edgecolors='k', facecolors='none') 

offset = 0.2 
plt.xlim(centroids_milk_groceries[:,0].min() - offset * centroids_milk_groceries[:,0].ptp(), 
        centroids_milk_groceries[:,0].max() + offset * centroids_milk_groceries[:,0].ptp(),) 
plt.ylim(centroids_milk_groceries[:,1].min() - offset * centroids_milk_groceries[:,1].ptp(), 
        centroids_milk_groceries[:,1].max() + offset * centroids_milk_groceries[:,1].ptp()) 

plt.title('Centroids of clusters for milk and groceries') 
plt.show() 
  1. 如果你运行此代码,你将在终端上得到以下输出:

图片

你将得到以下输出,描述了特征的中心点,牛奶杂货,其中牛奶位于x轴上,杂货位于y轴上:

图片

在这个输出中,已识别簇的八个中心点被清晰地表示出来。

它是如何工作的…

在这个菜谱中,我们通过使用均值漂移算法来面对聚类问题。这是一种通过移动点到众数的方式来迭代地将数据点分配到簇中的聚类类型。众数是出现频率最高的值。

算法迭代地将每个数据点分配到最近簇的中心。最近簇的中心由大多数邻近点所在的位置确定。因此,在每次迭代中,每个数据点都会接近位于最多点的地方,这即是或会导致簇中心。当算法停止时,每个点都会被分配到一个簇中。与 k-means 算法不同,均值漂移算法不需要事先指定簇的数量;这是由算法自动确定的。均值漂移算法在图像处理和人工智能领域得到了广泛的应用。

还有更多…

为了执行均值漂移聚类,使用了sklearn.cluster.MeanShift()函数。此函数使用平坦核执行均值漂移聚类。均值漂移聚类使我们能够在均匀密度的样本中识别点聚集。候选中心点通过给定区域内点的平均值进行更新。然后,在后期处理阶段过滤这些点以消除可能的重复,形成最终的中心点集。

参考以下内容

使用自动编码器重建手写数字图像

自动编码器是一种神经网络,其目的是将输入编码到小维度,并使获得的结果能够重建输入本身。自动编码器由以下两个子网络组成:编码器和解码器。添加了一个损失函数到这些函数中,它是数据压缩表示和分解表示之间信息损失量的距离。编码器和解码器将与距离函数可微分,因此编码和解码函数的参数可以被优化以最小化重建损失,使用梯度随机化。

准备工作

手写识别HWR)在现代技术中得到了广泛应用。书写文本图像可以通过光学扫描(光学字符识别OCR)或智能文字识别从纸张上离线获取。书法识别展示了计算机接收和解释手写输入的能力,这些输入可以从纸张文件、触摸屏、照片和其他设备等来源理解。HWR 包括各种技术,通常需要 OCR。然而,一个完整的脚本识别系统还管理格式,执行正确的字符分割,并找到最可能的单词。

修改后的国家标准与技术研究院MNIST)是一个包含手写数字的大型数据库。它包含 70,000 个数据示例。它是 MNIST 更大数据集的一个子集。数字的分辨率为 28 x 28 像素,存储在一个 70,000 行和 785 列的矩阵中;784 列形成 28 x 28 矩阵中每个像素值,一个值是实际的数字。这些数字已经被尺寸归一化和居中在固定大小的图像中。

如何做到这一点...

让我们看看如何构建自动编码器来重建手写数字图像:

  1. 此菜谱的完整代码已提供在AutoencMnist.py文件中。让我们看看它是如何构建的。创建一个新的 Python 文件,并导入以下包:
from keras.datasets import mnist
  1. 要导入 MNIST 数据集,必须使用以下代码:
(XTrain, YTrain), (XTest, YTest) = mnist.load_data()

print('XTrain shape = ',XTrain.shape)
print('XTest shape = ',XTest.shape)
print('YTrain shape = ',YTrain.shape)
print('YTest shape = ',YTest.shape)
  1. 导入数据集后,我们打印了数据的形状,以下结果被返回:
XTrain shape = (60000, 28, 28)
XTest shape = (10000, 28, 28)
YTrain shape = (60000,)
YTest shape = (10000,)
  1. 数据库中的 70,000 个项目被分为 60,000 个项目用于训练,10,000 个项目用于测试。数据输出由 0 到 9 范围内的整数表示。让我们如下进行检查:
import numpy as np
print('YTrain values = ',np.unique(YTrain))
print('YTest values = ',np.unique(YTest))
  1. 以下结果将被打印:
YTrain values = [0 1 2 3 4 5 6 7 8 9]
YTest values = [0 1 2 3 4 5 6 7 8 9]
  1. 分析可用的数组中两个值的分布可能是有用的。首先,我们计算出现的次数:
unique, counts = np.unique(YTrain, return_counts=True)
print('YTrain distribution = ',dict(zip(unique, counts)))
unique, counts = np.unique(YTest, return_counts=True)
print('YTrain distribution = ',dict(zip(unique, counts)))
  1. 以下结果被返回:
YTrain distribution = {0: 5923, 1: 6742, 2: 5958, 3: 6131, 4: 5842, 5: 5421, 6: 5918, 7: 6265, 8: 5851, 9: 5949}
YTrain distribution = {0: 980, 1: 1135, 2: 1032, 3: 1010, 4: 982, 5: 892, 6: 958, 7: 1028, 8: 974, 9: 1009}
  1. 我们也可以在图中看到它,如下所示:
import matplotlib.pyplot as plt
plt.figure(1)
plt.subplot(121)
plt.hist(YTrain, alpha=0.8, ec='black')
plt.xlabel("Classes")
plt.ylabel("Number of occurrences")
plt.title("YTrain data")

plt.subplot(122)
plt.hist(YTest, alpha=0.8, ec='black')
plt.xlabel("Classes")
plt.ylabel("Number of occurrences")
plt.title("YTest data")
plt.show()
  1. 为了比较在两个输出数据集(YTrainYTest)上获得的结果,我们绘制并并排显示了两个直方图,如下所示:

图片

  1. 从前面的输出分析中,我们可以看到在两个数据集中,10 个数字以相同的比例表示。事实上,柱状图似乎具有相同的尺寸,即使垂直轴有不同的范围。

  2. 现在,我们必须将所有值归一化到 0 和 1 之间:

XTrain = XTrain.astype('float32') / 255
XTest = XTest.astype('float32') / 255
  1. 为了降低维度,我们将 28 x 28 的图像展平成大小为 784 的向量:
XTrain = XTrain.reshape((len(XTrain), np.prod(XTrain.shape[1:])))
XTest = XTest.reshape((len(XTest), np.prod(XTest.shape[1:])))
  1. 现在,我们将使用 Keras 功能 API 构建模型。让我们开始导入库:
from keras.layers import Input 
from keras.layers import Dense
from keras.models import Model
  1. 然后,我们可以构建 Keras 模型,如下所示:
InputModel = Input(shape=(784,))
EncodedLayer = Dense(32, activation='relu')(InputModel)
DecodedLayer = Dense(784, activation='sigmoid')(EncodedLayer)
AutoencoderModel = Model(InputModel, DecodedLayer)
AutoencoderModel.summary()

以下输出显示了模型架构:

图片

  1. 因此,我们必须为训练配置模型。为此,我们将使用compile方法,如下所示:
AutoencoderModel.compile(optimizer='adadelta', loss='binary_crossentropy')
  1. 在这一点上,我们可以训练模型,如下所示:
history = AutoencoderModel.fit(XTrain, XTrain,
batch_size=256,
epochs=100,
shuffle=True,
validation_data=(XTest, XTest))
  1. 我们的模式现在准备好了,我们可以使用它来自动重建手写数字。为此,我们将使用predict()方法:
DecodedDigits = AutoencoderModel.predict(XTest)
  1. 现在,我们已经完成了;模型已经被训练,并将随后用于做出预测。因此,我们可以打印出起始的手写数字以及从我们的模型中重建的数字。当然,我们只会对数据集中包含的 60,000 个数字中的部分进行操作。实际上,我们将限制自己只显示前五个;在这种情况下,我们还将使用matplotlib库:
n=5
plt.figure(figsize=(20, 4))
for i in range(n):
 ax = plt.subplot(2, n, i + 1)
 plt.imshow(XTest[i+10].reshape(28, 28))
 plt.gray()
 ax.get_xaxis().set_visible(False)
 ax.get_yaxis().set_visible(False)
 ax = plt.subplot(2, n, i + 1 + n)
 plt.imshow(DecodedDigits[i+10].reshape(28, 28))
 plt.gray()
 ax.get_xaxis().set_visible(False)
 ax.get_yaxis().set_visible(False)
plt.show()

结果如下所示:

图片

如您在前面的输出中看到的,结果非常接近原始数据,这意味着模型运行良好。

它是如何工作的...

自编码器是一种神经网络,其目的是将输入编码到小维度,并得到的结果以便能够重建输入本身。自编码器由以下两个子网络的组合组成。

首先,我们有一个编码器,它计算以下函数:

图片

给定一个x输入,编码器将其编码到 z 变量中,这个变量也称为潜在变量。z通常比x小得多。

第二,我们有一个解码器,它计算以下函数:

图片

由于 z 是编码器产生的 x 的代码,解码器必须将其解码,以便 x'x 相似。自动编码器的训练旨在最小化输入和结果之间的均方误差。

还有更多...

Keras 是一个 Python 库,它提供了一种简单且清晰的方式来创建各种深度学习模型。Keras 代码是在 MIT 许可证下发布的。Keras 基于简约和简洁的原则构建,它提供了一个无装饰的编程模型,以最大化可读性。它允许以非常模块化的方式表达神经网络,将模型视为一个序列或单个图。

参考以下内容

  • 请参阅 Keras 库的官方文档:keras.io/

  • 请参阅《Keras 2.x Projects》,作者 Giuseppe Ciaburro,Packt Publishing 出版。

第五章:可视化数据

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

  • 绘制三维散点图

  • 绘制气泡图

  • 动画气泡图

  • 绘制饼图

  • 绘制日期格式的时间序列数据

  • 绘制直方图

  • 可视化热图

  • 动态信号动画

  • 使用 Seaborn 库进行操作

技术要求

为了处理本章中的食谱,您将需要以下文件(可在 GitHub 上找到):

  • scatter_3d.py

  • bubble_plot.py

  • dynamic_bubble_plot.py

  • pie_chart.py

  • time_series.py

  • aapl.csv

  • histogram.py

  • heatmap.py

  • moving_wave_variable.py

  • seaborn.boxplot.py

数据可视化简介

数据可视化是机器学习的一个重要支柱。它帮助我们制定正确的策略来理解数据。数据的视觉表示有助于我们选择正确的算法。数据可视化的一个主要目标是使用图表和图表清晰地传达信息。

在现实世界中,我们经常遇到数值数据。我们希望通过图表、线条、点、条形等来编码这些数值数据,以直观地显示这些数字中包含的信息。这使得复杂的数据分布更容易理解和使用。这个过程在各种情况下都得到应用,包括比较分析、跟踪增长、市场分布、民意调查等等。

我们使用不同的图表来展示变量之间的模式或关系。我们使用直方图来显示数据的分布。当我们想要查找特定的测量值时,我们使用表格。在本章中,我们将探讨各种场景并讨论在这些情况下我们可以使用哪些可视化。

绘制三维散点图

可以使用散点图来表示定量变量之间的关系。这种图表的一个版本是三维散点图,用于展示三个变量之间的关系。

准备工作

在本食谱中,您将学习如何绘制三维散点图并在三维中可视化它们。

如何做到...

让我们看看如何绘制三维散点图:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在您已提供的 scatter_3d.py 文件中):
import numpy as np 
import matplotlib.pyplot as plt  
  1. 创建空图,如下所示:
# Create the figure 
fig = plt.figure() 
ax = fig.add_subplot(111, projection='3d') 
  1. 定义我们应该生成的值的数量:
# Define the number of values 
n = 250 
  1. 创建一个 lambda 函数来生成给定范围内的值:
# Create a lambda function to generate the random values in the given range 
f = lambda minval, maxval, n: minval + (maxval - minval) * np.random.rand(n) 
  1. 使用 lambda 函数生成 x、y 和 z 值:
# Generate the values 
x_vals = f(15, 41, n) y_vals = f(-10, 70, n) 
z_vals = f(-52, -37, n) 
  1. 按照以下方式绘制这些值:
# Plot the values 
ax.scatter(x_vals, y_vals, z_vals, c='k', marker='o') 
ax.set_xlabel('X axis') 
ax.set_ylabel('Y axis') 
ax.set_zlabel('Z axis') 

plt.show() 

如果您运行前面的代码,您将看到以下输出:

图片

它是如何工作的...

散点图帮助我们理解两个定量特征之间是否存在统计关联。如果一个变量增加,另一个变量倾向于减少,我们有一个不一致的关联。如果一个变量增加,另一个变量也倾向于增加,我们有一个一致的关联。如果一个变量变化,而另一个变量倾向于不变化,我们没有任何关联。为了分析这种趋势,分析标记的位置。如果标记在图形的三维空间中的任何方向上接近形成一条直线,则对应变量的相关性很高。如果标记在图中均匀分布,则相关性低,或为零。

更多...

三维散点图用于显示三个变量之间的关系。可以通过匹配标记的颜色或大小添加第四个变量,将另一个变量添加到图中。

参见

绘制气泡图

气泡图是一种图表,其中每个表示的实体由三个不同的数值参数定义。前两个参数用作两个笛卡尔轴的值,而第三个参数用于确定气泡的半径。气泡图用于描述各个科学领域的关联关系。

准备工作

让我们看看如何绘制气泡图。在二维气泡图中,每个圆的大小代表该特定点的振幅。

如何做到这一点...

让我们看看如何绘制气泡图:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在您已经提供的bubble_plot.py文件中):
import numpy as np 
import matplotlib.pyplot as plt  
  1. 定义我们应该生成的值的数量:
# Define the number of values 
num_vals = 40 
  1. xy生成随机值:
# Generate random values 
x = np.random.rand(num_vals) 
y = np.random.rand(num_vals) 
  1. 定义气泡图中每个点的面积值:
# Define area for each bubble 
# Max radius is set to a specified value 
max_radius = 25 
area = np.pi * (max_radius * np.random.rand(num_vals)) ** 2   
  1. 定义颜色:
# Generate colors 
colors = np.random.rand(num_vals) 
  1. 绘制这些值:
# Plot the points 
plt.scatter(x, y, s=area, c=colors, alpha=1.0) 

plt.show() 

如果您运行此代码,您将看到以下输出:

图片

它是如何工作的...

之前显示的变量可以根据它们的大小以及它们相对于数值轴的位置进行比较。实际上,气泡图的x轴和y轴是数值尺度,因此数据表示的位置描述了两个数值,而图形的面积取决于第三个参数的值。

在绘制气泡图时,需要注意以下事实:圆的面积与半径的平方成正比,因此如果半径与第三个值成比例,结果将不成比例地强调第三个值。为了有一个正确权衡的刻度,半径必须与第三个值的平方根成比例。在绘制气泡图时,这种错误是常见的。

更多...

气泡图可以被视为散点图的一种变体,其中点被气泡所取代。如果数据有三个系列,每个系列包含一组数据,则可以使用此类图表代替散点图。

参见

动画气泡图

动画气泡图是一种动态的气泡图。它允许通过时间和交互式地高效可视化相关性。它特别有用,因为它允许我们有效地和交互式地可视化变量之间的相关性如何随时间变化。

准备工作

让我们看看如何动画气泡图。当您想要可视化瞬态和动态数据时,这将很有用。

如何做到这一点...

让我们看看如何动画气泡图:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在您已经提供的dynamic_bubble_plot.py文件中):
import numpy as np 
import matplotlib.pyplot as plt 
from matplotlib.animation import FuncAnimation  
  1. 让我们定义一个tracker函数,该函数将动态更新气泡图:
def tracker(cur_num): 
    # Get the current index  
    cur_index = cur_num % num_points 
  1. 定义颜色:
    # Set the color of the datapoints  
    datapoints['color'][:, 3] = 1.0 
  1. 更新圆的大小:
    # Update the size of the circles  
    datapoints['size'] += datapoints['growth'] 
  1. 更新集合中最旧数据点的位置:
    # Update the position of the oldest datapoint  
    datapoints['position'][cur_index] = np.random.uniform(0, 1, 2) 
    datapoints['size'][cur_index] = 7 
    datapoints['color'][cur_index] = (0, 0, 0, 1) 
    datapoints['growth'][cur_index] = np.random.uniform(40, 150) 
  1. 更新散点图的参数:
    # Update the parameters of the scatter plot  
    scatter_plot.set_edgecolors(datapoints['color']) 
    scatter_plot.set_sizes(datapoints['size']) 
    scatter_plot.set_offsets(datapoints['position']) 
  1. 定义main函数并创建一个空图:
if __name__=='__main__': 
    # Create a figure  
    fig = plt.figure(figsize=(9, 7), facecolor=(0,0.9,0.9)) 
    ax = fig.add_axes([0, 0, 1, 1], frameon=False) 
    ax.set_xlim(0, 1), ax.set_xticks([]) 
    ax.set_ylim(0, 1), ax.set_yticks([]) 
  1. 定义在任何给定时间点图上将会有的点的数量:
    # Create and initialize the datapoints in random positions  
    # and with random growth rates. 
    num_points = 20 
  1. 使用随机值定义datapoints
    datapoints = np.zeros(num_points, dtype=[('position', float, 2), 
            ('size', float, 1), ('growth', float, 1), ('color', float, 4)]) 
    datapoints['position'] = np.random.uniform(0, 1, (num_points, 2)) 
    datapoints['growth'] = np.random.uniform(40, 150, num_points) 
  1. 创建将在每一帧更新的散点图:
    # Construct the scatter plot that will be updated every frame 
    scatter_plot = ax.scatter(datapoints['position'][:, 0], datapoints['position'][:, 1], 
                      s=datapoints['size'], lw=0.7, edgecolors=datapoints['color'], 
                      facecolors='none') 
  1. 通过使用tracker函数开始动画:
    # Start the animation using the 'tracker' function  
    animation = FuncAnimation(fig, tracker, interval=10) 

    plt.show() 

如果您运行此代码,您将看到以下输出:

图片

它是如何工作的...

在这个菜谱中,我们简单地使用了一系列随时间变化的气泡图,通过构建一个更新这些图参数的函数来构建动画。为此,我们首先构建了一个更新这些图参数的函数。然后,我们定义了跟踪当前参数的气泡图的代码。最后,我们使用FuncAnimation()函数从单个气泡图中创建动画。

更多...

Matplotlib 的FuncAnimation()函数通过重复调用一个特定的函数来创建动画。

参见

绘制饼图

圆形图,通常被称为饼图,是描述性统计中用于图形表示在类别(名义值)类别上测量的定量变量的方法,以避免无意中建立不存在的类别顺序。

准备工作

让我们看看如何绘制饼图。这在你想可视化一组标签的百分比时非常有用。

如何做到这一点...

让我们看看如何绘制饼图,如下所示:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在已经提供给你的pie_chart.py文件中):
import matplotlib.pyplot as plt  
  1. 定义标签和值:
# Labels and corresponding values in counter clockwise direction 
data = {'Apple': 26,  
        'Mango': 17, 
        'Pineapple': 21,  
        'Banana': 29,  
        'Strawberry': 11} 
  1. 定义用于可视化的颜色:
# List of corresponding colors 
colors = ['orange', 'lightgreen', 'lightblue', 'gold', 'cyan'] 
  1. 定义一个变量来突出显示饼图的一部分,使其与其他部分分离。如果你不想突出任何部分,将所有值设置为0
# Needed if we want to highlight a section 
explode = (0, 0, 0, 0, 0)   
  1. 绘制饼图。请注意,如果你使用的是 Python 3,你应该在以下函数调用中使用list(data.values())
# Plot the pie chart 
plt.pie(data.values(), explode=explode, labels=data.keys(),  
        colors=colors, autopct='%1.1f%%', shadow=False, startangle=90) 

# Aspect ratio of the pie chart, 'equal' indicates tht we  
# want it to be a circle 
plt.axis('equal') 

plt.show() 

如果你运行此代码,你将看到以下输出:

图片

  1. 如果你将 explode 数组更改为(0, 0.2, 0, 0, 0),它将突出显示芒果部分。你将看到以下输出:

图片

它是如何工作的...

饼图是通过将圆分成角度幅度与频率类别成比例的切片来构建的。切片所标识的面积与频率成比例。为了使图表更清晰,不同的切片被填充了不同的颜色。

还有更多...

饼图用于显示产品和品牌的市场份额,或政治党派在选举中所占的百分比。当百分比差异很大或元素太多时,图表将无法正常工作,因为这会使饼图过于参差不齐。

参见

绘制日期格式的时间序列数据

时间序列是一系列现象的观察结果,在连续的瞬间或时间间隔内进行。通常,即使不是必要的,它们也是均匀分布的或长度相同。商品价格趋势、股票市场指数、政府债券收益率和失业率只是时间序列的几个例子。

准备工作

让我们看看如何使用日期格式绘制时间序列数据。这将在可视化随时间变化的股票数据时很有用。

如何做...

让我们看看如何绘制日期格式化的时间序列数据,如下所示:

  1. 创建一个新的 Python 文件并导入以下包(完整代码在已提供的time_series.py文件中):
import numpy 
import matplotlib.pyplot as plt 
from matplotlib.mlab import csv2rec 
from matplotlib.ticker import Formatter 
  1. 定义一个用于格式化日期的函数。__init__函数设置了类变量:
# Define a class for formatting 
class DataFormatter(Formatter): 
    def __init__(self, dates, date_format='%Y-%m-%d'): 
        self.dates = dates 
        self.date_format = date_format 
  1. 在任何给定时间提取值,并以下列格式返回:
    # Extract the value at time t at position 'position' 
    def __call__(self, t, position=0): 
        index = int(round(t)) 
        if index >= len(self.dates) or index < 0: 
            return '' 

        return self.dates[index].strftime(self.date_format) 
  1. 定义main函数。我们将使用您已经提供的苹果股票报价 CSV 文件(aapl.csv)。加载 CSV 文件:
    # Load csv file into numpy record array 
    data = csv2rec('aapl.csv') 
  1. 从这些值中提取一个子集以进行绘图:
    # Take a subset for plotting 
    data = data[-70:] 
  1. 创建formatter对象并用日期初始化它:
    # Create the date formatter object 
    formatter = DataFormatter(data.date) 
  1. 定义xy轴:
    # X axis 
    x_vals = numpy.arange(len(data)) 

    # Y axis values are the closing stock quotes 
    y_vals = data.close  
  1. 绘制数据:
    # Plot data 
    fig, ax = plt.subplots() 
    ax.xaxis.set_major_formatter(formatter) 
    ax.plot(x_vals, y_vals, 'o-') 
    fig.autofmt_xdate() 
    plt.show() 

如果你运行此代码,你将看到以下输出:

它是如何工作的...

在这个菜谱中,我们使用日期格式绘制了时间序列数据。我们首先定义了一个用于格式化日期的类。我们在特定位置提取了时间t的值。然后,我们将 CSV 文件加载到 NumPy 记录数组中。因此,我们提取了用于绘图的子集,并创建了日期formatter对象。最后,我们设置了xy轴并绘制了数据。

还有更多...

可视化是时间序列分析的基础。查看原始数据可以提供识别时间结构的工具,例如趋势、周期和季节性。然而,日期的格式化代表了一个你必须学习的操作,以便获得正确的轴可视化。

参考信息

绘制直方图

直方图是数值分布的再现,显示了分布的形状。它由相邻的矩形(箱)组成,其底边与轴对齐,并配备了度量单位。

更多信息请参考 Giuseppe Ciaburro 所著的《MATLAB for Machine Learning》一书。

准备工作

我们将在这个菜谱中查看如何绘制直方图。我们将比较两组数据并构建一个比较直方图。

如何做...

让我们看看如何绘制直方图,如下所示:

  1. 创建一个新的 Python 文件并导入以下包(完整代码在已提供的histogram.py文件中):
import numpy as np 
import matplotlib.pyplot as plt  
  1. 在这个菜谱中,我们将比较苹果和橙子的产量。让我们定义一些值:
# Input data 
apples = [30, 25, 22, 36, 21, 29] 
oranges = [24, 33, 19, 27, 35, 20] 

# Number of groups 
num_groups = len(apples) 
  1. 创建图形并定义其参数:
# Create the figure 
fig, ax = plt.subplots() 

# Define the X axis 
indices = np.arange(num_groups) 

# Width and opacity of histogram bars 
bar_width = 0.4 
opacity = 0.6 
  1. 绘制直方图:
# Plot the values 
hist_apples = plt.bar(indices, apples, bar_width,  
        alpha=opacity, color='g', label='Apples') 

hist_oranges = plt.bar(indices + bar_width, oranges, bar_width, 
        alpha=opacity, color='b', label='Oranges') 
  1. 设置绘图参数:
plt.xlabel('Month') 
plt.ylabel('Production quantity') 
plt.title('Comparing apples and oranges') 
plt.xticks(indices + bar_width, ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun')) 
plt.ylim([0, 45]) 
plt.legend() 
plt.tight_layout() 

plt.show() 

如果你运行此代码,你将看到以下输出:

它是如何工作的...

直方图是一种特殊的笛卡尔图,横轴上有离散值,纵轴上有大小,通过柱子的高度表示,我们称之为箱。在物理学中,直方图使我们能够研究实验的结果,因为它们为我们提供了图形指示,说明了计数或频率是如何根据考虑的离散值分布的。

要构建直方图,请执行以下步骤:

  1. 首先,你必须决定测试的数量。

  2. 然后,你必须选择分箱,即在横轴上变量域的区间划分。因此,你必须定义区间的数量,其中第j个箱将具有预定的宽度。

  3. 最后,你必须计算出现次数以与个别箱关联。

更多内容...

在以下情况下,直方图最有用:

  • 要表示的数据是数值类型。

  • 你想要可视化数据分布的形式,以便查看它是否为正态分布,并分析一个过程是否可以(或不能)满足所施加的要求。

  • 你想要确定两个或更多过程输出是否不同。

  • 你想要快速传达数据分布。

  • 你想要检查在某个时间段内过程是否发生了变化。

参考信息

可视化热图

热图是一种图表,其中矩阵中包含的个别值通过颜色的渐变来表示。分形图和树形图通常使用相同的颜色编码系统来表示变量的层次。例如,如果我们测量网页上的点击次数或鼠标指针最常经过的区域,我们将获得一个以暖色突出显示的特定区域的热图,即那些最能吸引我们注意力的区域。

准备工作

我们将探讨如何在这个配方中可视化热图。这是一个数据点的图示表示,其中两个组是点对点关联的。矩阵中包含的个别值在图中表示为颜色值。

如何做...

让我们看看如何可视化热图:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在提供的heatmap.py文件中):
import numpy as np 
import matplotlib.pyplot as plt 
  1. 定义两组:
# Define the two groups  
group1 = ['France', 'Italy', 'Spain', 'Portugal', 'Germany']  
group2 = ['Japan', 'China', 'Brazil', 'Russia', 'Australia'] 
  1. 生成一个随机的二维矩阵:
# Generate some random values 
data = np.random.rand(5, 5) 
  1. 创建一个图形:
# Create a figure 
fig, ax = plt.subplots() 
  1. 创建热图:
# Create the heat map 
heatmap = ax.pcolor(data, cmap=plt.cm.gray) 
  1. 绘制这些值:
# Add major ticks at the middle of each cell 
ax.set_xticks(np.arange(data.shape[0]) + 0.5, minor=False) 
ax.set_yticks(np.arange(data.shape[1]) + 0.5, minor=False) 

# Make it look like a table  
ax.invert_yaxis() 
ax.xaxis.tick_top() 

# Add tick labels 
ax.set_xticklabels(group2, minor=False) 
ax.set_yticklabels(group1, minor=False) 

plt.show() 

如果你运行此代码,你将看到以下输出:

图片

它是如何工作的...

我们已经说过,热图是一种数据图形表示类型,其中值通过不同的颜色范围来表示。其表示通常包括以下内容:

  • 暖色:红色、橙色和黄色,用于最感兴趣的区域

  • 冷色:绿色或蓝色,用于表现较差的区域

通常,热图需要大量的数据样本,它们的主要目的是获取关于特定变量趋势的有用数据。这种分析方法使我们能够评估分析区域内感兴趣变量的分布。这样,特定区域暖色浓度的集中将突出变量取最高值的地方。

还有更多...

热图的创造者是 Cormac Kinney,他在 20 世纪 90 年代中期开发了这个解决方案,为股市操作员提供了一个收集各种金融数据的极快工具。

参见

动画动态信号

当我们可视化实时信号时,看看波形是如何建立起来的是很不错的。一个动态系统是一个数学模型,它代表了一个具有有限自由度的对象,该对象根据确定性法则随时间演变。动态系统由相空间中的一个向量来识别,即系统状态的空间,其中状态是一个术语,表示一组物理量,称为状态变量,它们表征了系统的动力学。

准备工作

在这个菜谱中,我们将探讨如何动画化动态信号,并在实时遇到时可视化它们。

如何做到...

让我们看看如何动画化动态信号,如下所示:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在您已经提供的moving_wave_variable.py文件中):
import numpy as np 
import matplotlib.pyplot as plt 
import matplotlib.animation as animation  
  1. 创建一个生成阻尼正弦波信号的函数:
# Generate the signal 
def generate_data(length=2500, t=0, step_size=0.05): 
    for count in range(length): 
        t += step_size 
        signal = np.sin(2*np.pi*t) 
        damper = np.exp(-t/8.0) 
        yield t, signal * damper  
  1. 定义一个initializer函数来初始化绘图参数:
# Initializer function 
def initializer(): 
    peak_val = 1.0 
    buffer_val = 0.1 
  1. 按照以下方式设置这些参数:
    ax.set_ylim(-peak_val * (1 + buffer_val), peak_val * (1 + buffer_val)) 
    ax.set_xlim(0, 10) 
    del x_vals[:] 
    del y_vals[:] 
    line.set_data(x_vals, y_vals) 
    return line 
  1. 定义一个函数来绘制值:
def draw(data): 
    # update the data 
    t, signal = data 
    x_vals.append(t) 
    y_vals.append(signal) 
    x_min, x_max = ax.get_xlim() 
  1. 如果值超出了当前的x轴限制,那么更新并扩展图表:
    if t >= x_max: 
        ax.set_xlim(x_min, 2 * x_max) 
        ax.figure.canvas.draw() 

    line.set_data(x_vals, y_vals) 

    return line 
  1. 定义main函数:
if __name__=='__main__': 
    # Create the figure 
    fig, ax = plt.subplots() 
    ax.grid() 
  1. 提取以下行:
    # Extract the line 
    line, = ax.plot([], [], lw=1.5) 
  1. 创建变量并将它们初始化为空列表:
    # Create the variables 
    x_vals, y_vals = [], [] 
  1. 使用animator对象定义并开始动画:
    # Define the animator object 
    animator = animation.FuncAnimation(fig, draw, generate_data,  
            blit=False, interval=10, repeat=False, init_func=initializer) 

    plt.show() 

如果您运行此代码,您将看到以下输出:

图片

它是如何工作的...

在这个菜谱中,我们动画化了动态信号,并将它们实时可视化。为此,我们使用了阻尼正弦波。当我们荡秋千时,我们知道在初始推动后,它会振荡一段时间然后停止。秋千是一个摆的例子,其运动由阻尼正弦波表示。阻尼正弦波是一个振幅随时间增加而趋近于零的正弦函数。

更多内容...

正弦波用于描述许多振荡现象。当正弦波被抑制时,每个后续峰值随时间减小。最常见的阻尼形式是指数阻尼,其中后续峰值的包络是指数衰减曲线。

参见

使用 Seaborn 库

箱线图,也称为须状图,是一种使用简单分散度和位置指数来描述样本分布的图表。箱线图可以水平或垂直绘制,通过两个段落的矩形分区来表示。矩形(箱)由第一个四分位数(25%分位数)和第三个四分位数(75%分位数)界定,并通过中位数(50%分位数)分割。

准备工作

在这个菜谱中,我们将绘制箱线图来展示波士顿数据集中包含的预测变量的分布,我们在第一章的监督学习领域中使用的估算房价菜谱中已经使用过这些数据,链接。

如何操作...

让我们看看如何使用seaborn库,如下所示:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在已提供的seaborn.boxplot.py文件中):
import pandas as pd
from sklearn import datasets
import seaborn as sns  
  1. 加载sklearn.datasets库中包含的数据集:
boston = datasets.load_boston()
  1. 将数据转换为 pandas DataFrame
BostonDF = pd.DataFrame(boston.data, columns=boston.feature_names)
  1. 提取前12个特征作为预测变量:
    Predictors = BostonDF[BostonDF.columns[0:12]]
  1. 使用seaborn库绘制箱线图:
sns.set(style="ticks")
sns.boxplot(data = Predictors)

让我们查看以下输出中的结果:

我们可以看到预测变量有不同的值范围。这使得图表难以阅读;某些预测变量的变异性没有得到突出显示。在这些情况下,有必要对数据进行缩放。

  1. 导入sklearn.preprocessing.MinMaxScaler库并缩放数据,如下所示:
from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()
DataScaled = scaler.fit_transform(Predictors)
  1. 现在,我们再次绘制箱线图以查看差异:
sns.set(style="ticks")
sns.boxplot(data = DataScaled)

结果如下截图所示:

在这种情况下,我们可以看到数据在 0-1 之间。这样,所有预测变量的数据变异性都是可识别的。

它是如何工作的...

Seaborn 库是一个基于 Matplotlib 的数据可视化库。它适用于绘制吸引人且信息丰富的统计图形,并且与 Pandas 数据结构紧密集成。Seaborn 提供了许多可以帮助我们进行数据源视觉分析的功能。

这些功能可以在seaborn.pydata.org/introduction.html中查看。

更多内容...

在箱线图中,箱体外的部分(即须须)代表样本的最小值和最大值。通过这种方式,四个等量的范围,由四分位数界定,被图形化地表示出来。

参见

第六章:构建推荐引擎

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

  • 构建数据处理的功能组合

  • 构建机器学习管道

  • 寻找最近邻

  • 构建 k 最近邻分类器

  • 构建 k 最近邻回归器

  • 计算欧几里得距离得分

  • 计算皮尔逊相关得分

  • 在数据集中寻找相似用户

  • 生成电影推荐

  • 实现排名算法

  • 使用 TensorFlow 构建过滤模型

技术要求

为了处理本章中的食谱,你需要以下文件(这些文件可在 GitHub 上找到):

  • function_composition.py

  • pipeline.py

  • knn.py

  • nn_classification.py

  • nn_regression.py

  • euclidean_score.py

  • pearson_score.py

  • find_similar_users.py

  • movie_recommendations.py

  • LambdaMARTModel.py

  • train.txt

  • vali.txt

  • test.txt

  • TensorFilter.py

介绍推荐引擎

推荐引擎是一个可以预测用户可能感兴趣的内容的模型。当我们将其应用于电影等场景时,这变成了电影推荐引擎。我们通过预测当前用户可能会如何评分来过滤数据库中的项目。这有助于我们将用户与数据集中的正确内容相连接。这有什么相关性?如果你有一个庞大的目录,那么用户可能或可能找不到所有与他们相关的所有内容。通过推荐正确的内容,你可以增加消费。像 Netflix 这样的公司严重依赖推荐来保持用户的参与度。

推荐引擎通常使用协同过滤或基于内容的过滤来生成一组推荐。两种方法之间的区别在于推荐挖掘的方式。协同过滤从当前用户的过去行为以及其他用户的评分构建模型。然后我们使用这个模型来预测这个用户可能会感兴趣的内容。另一方面,基于内容的过滤使用项目本身的特征来向用户推荐更多项目。项目之间的相似性是这里的驱动力。在本章中,我们将重点介绍协同过滤。

构建数据处理的功能组合

任何机器学习系统的主要部分之一是数据处理管道。在数据被输入到机器学习算法进行训练之前,我们需要以不同的方式对其进行处理,使其适合该算法。拥有一个健壮的数据处理管道对于构建准确和可扩展的机器学习系统至关重要。有很多基本功能可用,数据处理管道通常是由这些功能的组合构成的。与其以嵌套或循环的方式调用这些函数,不如使用函数式编程范式来构建组合。

准备工作

让我们看看如何将这些基本函数组合成一个可重用的函数组合。在这个菜谱中,我们将创建三个基本函数,并查看如何构建一个管道。

如何做...

让我们看看如何构建数据处理的功能组合:

  1. 创建一个新的 Python 文件,并添加以下行(完整的代码在提供的function_composition.py文件中):
import numpy as np
from functools import reduce 
  1. 让我们定义一个函数,将3加到数组的每个元素上:
def add3(input_array): 
    return map(lambda x: x+3, input_array) 
  1. 现在,让我们定义第二个函数,将2乘以数组的每个元素:
def mul2(input_array): return map(lambda x: x*2, input_array) 
  1. 现在,让我们再定义一个函数,从数组的每个元素中减去5
def sub5(input_array): 
    return map(lambda x: x-5, input_array)
  1. 让我们定义一个函数组合器,它接受函数作为输入参数,并返回一个组合函数。这个组合函数基本上是一个按顺序应用所有输入函数的函数:
def function_composer(*args): 
    return reduce(lambda f, g: lambda x: f(g(x)), args) 

我们使用reduce函数通过依次应用序列中的函数来组合所有输入函数。

  1. 现在,我们已经准备好使用这个函数组合器了。让我们定义一些数据和一系列操作:
if __name__=='__main__': 
    arr = np.array([2,5,4,7]) 

    print("Operation: add3(mul2(sub5(arr)))") 
  1. 如果我们使用常规方法,我们会依次应用,如下所示:
    arr1 = add3(arr) 
    arr2 = mul2(arr1) 
    arr3 = sub5(arr2) 
    print("Output using the lengthy way:", list(arr3)) 
  1. 现在,让我们使用函数组合器在单行中实现相同的功能:
    func_composed = function_composer(sub5, mul2, add3) 
    print("Output using function composition:", list(func_composed(arr)))  
  1. 我们也可以用前一种方法在单行中做同样的事情,但符号会变得非常嵌套和难以阅读。此外,它不可重用;如果你想重用这个操作序列,你必须再次写下整个内容:
    print("Operation: sub5(add3(mul2(sub5(mul2(arr)))))\nOutput:", \
            list(function_composer(mul2, sub5, mul2, add3, sub5)(arr)))
  1. 如果你运行此代码,你将在终端上得到以下输出:
Operation: add3(mul2(sub5(arr)))
Output using the lengthy way: [5, 11, 9, 15]
Output using function composition: [5, 11, 9, 15]
Operation: sub5(add3(mul2(sub5(mul2(arr)))))
Output: [-10, 2, -2, 10]

它是如何工作的...

在这个菜谱中,我们创建了三个基本函数,并学习了如何组合一个管道。为此,我们使用了reduce()函数。这个函数接受一个函数和一个序列,并返回一个单一值。

reduce()函数计算返回值,如下所示:

  • 首先,函数通过使用序列的前两个元素来计算结果。

  • 接下来,函数使用上一步得到的结果和序列中的下一个值。

  • 这个过程会一直重复,直到序列的末尾。

还有更多...

在菜谱开头使用的三个基本函数利用了map()函数。这个函数用于将一个函数应用于特定值的所有元素。结果返回一个 map 对象;这个对象是一个迭代器,因此我们可以遍历其元素。为了打印这个对象,我们将 map 对象转换为序列对象,即列表。

参见

构建机器学习管道

scikit-learn库用于构建机器学习管道。当我们定义函数时,库将构建一个组合对象,使数据通过整个管道。这个管道可以包括函数,如预处理、特征选择、监督学习和无监督学习。

准备工作

在这个菜谱中,我们将构建一个管道,用于获取输入特征向量,选择前* k* 个特征,然后使用随机森林分类器进行分类。

如何做到这一点...

让我们看看如何构建机器学习管道:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在您已经提供的 pipeline.py文件中):
from sklearn.datasets import samples_generator 
from sklearn.ensemble import RandomForestClassifier 
from sklearn.feature_selection import SelectKBest, f_regression 
from sklearn.pipeline import Pipeline 
  1. 让我们生成一些样本数据来玩耍,如下所示:
# generate sample data 
X, y = samples_generator.make_classification( 
        n_informative=4, n_features=20, n_redundant=0, random_state=5) 

这行生成了 20 维的特征向量,因为这是默认值。您可以使用上一行中的 n_features参数来更改它。

  1. 我们管道的第一步是在数据点进一步使用之前选择* k* 个最佳特征。在这种情况下,让我们将 k 设置为 10
# Feature selector  
selector_k_best = SelectKBest(f_regression, k=10)
  1. 下一步是使用随机森林分类器方法对数据进行分类:
# Random forest classifier 
classifier = RandomForestClassifier(n_estimators=50, max_depth=4)
  1. 现在,我们已经准备好构建管道。 Pipeline()方法允许我们使用预定义的对象来构建管道:
# Build the machine learning pipeline 
pipeline_classifier = Pipeline([('selector', selector_k_best), ('rf', classifier)]) 

我们还可以为管道中的块分配名称。在上行中,我们将 selector 名称分配给我们的特征选择器,将 rf分配给我们的随机森林分类器。您可以使用任何其他随机名称!

  1. 我们还可以在过程中更新这些参数。我们可以使用之前步骤中分配的名称来设置参数。例如,如果我们想在特征选择器中将 k 设置为 6,在随机森林分类器中将 n_estimators设置为 25,我们可以像以下代码所示那样做。请注意,这些是之前步骤中给出的变量名称:
pipeline_classifier.set_params(selector__k=6,  
        rf__n_estimators=25) 
  1. 让我们继续训练分类器:
# Training the classifier 
pipeline_classifier.fit(X, y) 
  1. 现在让我们预测训练数据的输出,如下所示:
# Predict the output prediction = pipeline_classifier.predict(X) print("Predictions:\n", prediction) 
  1. 现在,让我们估计这个分类器的性能,如下所示:
# Print score 
print("Score:", pipeline_classifier.score(X, y))                         
  1. 我们还可以查看哪些特征将被选择,所以让我们继续并打印它们:
# Print the selected features chosen by the selector features_status = pipeline_classifier.named_steps['selector'].get_support() selected_features = [] for count, item in enumerate(features_status): if item: selected_features.append(count) print("Selected features (0-indexed):", ', '.join([str(x) for x in selected_features]))
  1. 如果您运行此代码,您将在您的终端上得到以下输出:
Predictions:
 [1 1 0 1 0 0 0 0 1 1 1 1 0 1 1 0 0 1 0 0 0 0 0 1 0 1 0 0 1 1 0 0 0 1 0 0 1
 1 1 1 1 1 1 1 1 0 0 1 1 0 1 1 0 1 0 1 1 0 0 0 1 1 1 0 0 1 0 0 0 1 1 0 0 1
 1 1 0 0 0 1 0 1 0 1 0 0 1 1 1 0 1 0 1 1 1 0 1 1 0 1]
Score: 0.95
Selected features (0-indexed): 0, 5, 9, 10, 11, 15

它是如何工作的...

选择* k* 个最佳特征的优势在于我们将能够处理低维数据。这有助于减少计算复杂性。我们选择* k* 个最佳特征的方式基于单变量特征选择。这执行单变量统计测试,然后从特征向量中提取表现最好的特征。单变量统计测试是指只涉及一个变量的分析技术。

更多内容...

一旦进行这些测试,特征向量中的每个特征都会被分配一个分数。基于这些分数,我们选择前k个特征。我们将此作为分类器管道中的预处理步骤。一旦我们提取了前k个特征,就形成了一个 k 维特征向量,我们将其用作随机森林分类器的输入训练数据。

参考信息

寻找最近邻

最近邻模型指的是一类旨在根据训练数据集中最近邻的数量做出决策的算法。最近邻方法包括找到与新的点距离相近的预定义数量的训练样本,并预测标签。样本的数量可以是用户定义的、一致的或不同的,这取决于点的局部密度。距离可以用任何度量标准来计算——标准欧几里得距离是最常见的选项。基于邻居的方法只是简单地记住所有训练数据。

准备工作

在本菜谱中,我们将使用笛卡尔平面上的一系列点来寻找最近邻。

如何做到这一点...

让我们看看如何找到最近邻,如下所示:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码已包含在您提供的knn.py文件中):
import numpy as np 
import matplotlib.pyplot as plt 
from sklearn.neighbors import NearestNeighbors 
  1. 让我们创建一些二维样本数据:
# Input data 
X = np.array([[1, 1], [1, 3], [2, 2], [2.5, 5], [3, 1],  
        [4, 2], [2, 3.5], [3, 3], [3.5, 4]])
  1. 我们的目标是找到任何给定点的三个最近邻,所以让我们定义这个参数:
# Number of neighbors we want to find 
num_neighbors = 3 
  1. 让我们定义一个不在输入数据中的随机数据点:
# Input point 
input_point = [2.6, 1.7] 
  1. 我们需要看看这些数据看起来像什么;让我们按如下方式绘制它:
# Plot datapoints 
plt.figure() 
plt.scatter(X[:,0], X[:,1], marker='o', s=25, color='k') 
  1. 为了找到最近邻,我们需要定义具有正确参数的NearestNeighbors对象,并在输入数据上对其进行训练:
# Build nearest neighbors model 
knn = NearestNeighbors(n_neighbors=num_neighbors, algorithm='ball_tree').fit(X) 
  1. 我们现在可以找到输入点到输入数据中所有点的distances参数:
distances, indices = knn.kneighbors(input_point) 
  1. 我们可以打印k 最近邻,如下所示:
# Print the 'k' nearest neighbors 
print("k nearest neighbors")
for rank, index in enumerate(indices[0][:num_neighbors]):
    print(str(rank+1) + " -->", X[index])

indices数组已经排序,所以我们只需要解析它并打印数据点。

  1. 现在,让我们绘制输入数据点和突出显示 k 个最近邻:
# Plot the nearest neighbors  
plt.figure() 
plt.scatter(X[:,0], X[:,1], marker='o', s=25, color='k') 
plt.scatter(X[indices][0][:][:,0], X[indices][0][:][:,1],  
        marker='o', s=150, color='k', facecolors='none') 
plt.scatter(input_point[0], input_point[1], 
        marker='x', s=150, color='k', facecolors='none') 

plt.show() 
  1. 如果你运行此代码,你将在你的终端上得到以下输出:
k nearest neighbors
1 --> [2\. 2.]
2 --> [3\. 1.]
3 --> [3\. 3.]

这是输入数据点的图示:

第二个输出图显示了测试数据点的位置和三个最近邻,如下面的截图所示:

它是如何工作的...

在这个菜谱中,我们通过使用笛卡尔平面上的点来寻找最近邻。为此,根据训练对象的位置和特征将空间划分为区域。即使它不是初始条件所明确要求的,这也可以被认为是算法的训练集。为了计算距离,对象通过多维空间中的位置向量来表示。最后,如果一个点是最接近被考察对象的 k 个例子中最频繁的,则将其分配到某个类别。邻近性是通过点之间的距离来衡量的。邻居是从一组已知正确分类的对象中选取的。

更多内容...

为了构建最近邻模型,使用了BallTree算法。BallTree是一种在多维空间中组织点的数据结构。该算法之所以得名,是因为它将数据点划分为一系列嵌套的超球体,称为球体。它在许多应用中很有用,尤其是最近邻搜索。

参考以下内容

构建 k 最近邻分类器

k 最近邻算法是一种使用训练数据集中的 k 个最近邻来寻找未知对象类别的算法。当我们想要找到未知点所属的类别时,我们找到 k 个最近邻并进行多数投票。

准备工作

在这个菜谱中,我们将从包含一系列在笛卡尔平面上排列的点输入数据开始创建一个 k 最近邻分类器,这些点显示了三个区域内的分组。

如何做到这一点...

让我们看看如何构建 k-最近邻分类器:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已为你提供的nn_classification.py文件中):
import numpy as np 
import matplotlib.pyplot as plt 
import matplotlib.cm as cm 
from sklearn import neighbors, datasets 

from utilities import load_data 
  1. 我们将使用data_nn_classifier.txt文件作为输入数据。让我们加载这个输入数据:
# Load input data 
input_file = 'data_nn_classifier.txt' 
data = load_data(input_file) 
X, y = data[:,:-1], data[:,-1].astype(np.int) 

前两列包含输入数据,最后一列包含标签。因此,我们将它们分离成Xy,如前述代码所示。

  1. 现在,让我们可视化输入数据,如下所示:
# Plot input data 
plt.figure() 
plt.title('Input datapoints') 
markers = '^sov<>hp' 
mapper = np.array([markers[i] for i in y]) 
for i in range(X.shape[0]): 
    plt.scatter(X[i, 0], X[i, 1], marker=mapper[i],  
            s=50, edgecolors='black', facecolors='none') 

我们遍历所有数据点,并使用适当的标记来区分类别。

  1. 为了构建分类器,我们需要指定我们想要考虑的最近邻数量。让我们定义这个参数:
# Number of nearest neighbors to consider 
num_neighbors = 10
  1. 为了可视化边界,我们需要定义一个网格并在该网格上评估分类器。让我们定义步长:
# step size of the grid 
h = 0.01   
  1. 我们现在准备好构建 k-最近邻分类器。让我们定义它并训练它,如下所示:
# Create a K-Neighbours Classifier model and train it 
classifier = neighbors.KNeighborsClassifier(num_neighbors, weights='distance') 
classifier.fit(X, y)
  1. 我们需要创建一个网格来绘制边界。让我们定义如下:
# Create the mesh to plot the boundaries 
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1 
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1 
x_grid, y_grid = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)) 
  1. 现在,让我们评估所有点的classifier输出:
# Compute the outputs for all the points on the mesh 
predicted_values = classifier.predict(np.c_[x_grid.ravel(), y_grid.ravel()]) 
  1. 让我们绘制它,如下所示:
# Put the computed results on the map 
predicted_values = predicted_values.reshape(x_grid.shape) 
plt.figure() 
plt.pcolormesh(x_grid, y_grid, predicted_values, cmap=cm.Pastel1) 
  1. 现在我们已经绘制了颜色网格,让我们叠加训练数据点以查看它们相对于边界的位置:
# Overlay the training points on the map 
for i in range(X.shape[0]): 
    plt.scatter(X[i, 0], X[i, 1], marker=mapper[i],  
            s=50, edgecolors='black', facecolors='none') 

plt.xlim(x_grid.min(), x_grid.max()) 
plt.ylim(y_grid.min(), y_grid.max()) 
plt.title('k nearest neighbors classifier boundaries')
  1. 现在,我们可以考虑一个测试数据点并查看分类器是否表现正确。让我们定义它并绘制它,如下所示:
# Test input datapoint 
test_datapoint = [4.5, 3.6] 
plt.figure() 
plt.title('Test datapoint') 
for i in range(X.shape[0]): 
    plt.scatter(X[i, 0], X[i, 1], marker=mapper[i],  
            s=50, edgecolors='black', facecolors='none') 

plt.scatter(test_datapoint[0], test_datapoint[1], marker='x',  
        linewidth=3, s=200, facecolors='black') 
  1. 我们需要使用以下模型提取 k-最近邻分类器:
# Extract k nearest neighbors 
dist, indices = classifier.kneighbors(test_datapoint) 
  1. 让我们绘制 k-最近邻分类器并突出显示它:
# Plot k nearest neighbors 
plt.figure() 
plt.title('k nearest neighbors') 

for i in indices: 
    plt.scatter(X[i, 0], X[i, 1], marker='o',  
            linewidth=3, s=100, facecolors='black') 

plt.scatter(test_datapoint[0], test_datapoint[1], marker='x',  
        linewidth=3, s=200, facecolors='black') 

for i in range(X.shape[0]): 
    plt.scatter(X[i, 0], X[i, 1], marker=mapper[i],  
            s=50, edgecolors='black', facecolors='none') 

plt.show() 
  1. 现在,让我们在终端上打印classifier输出:
print("Predicted output:", classifier.predict(test_datapoint)[0])

打印出以下结果:

Predicted output: 2

此外,展示了一系列图表。第一个输出图表描述了输入数据点的分布:

图片

第二个输出图表描述了使用k-最近邻分类器获得的边界:

图片

第三个输出图表描述了测试数据点的位置:

图片

第四个输出图表描述了 10 个最近邻的位置:

图片

它是如何工作的...

k-最近邻分类器存储所有可用的数据点,并根据相似性度量对新数据点进行分类。这个相似性度量通常以距离函数的形式出现。这个算法是一种非参数技术,这意味着在制定之前不需要找出任何潜在参数。我们只需要选择一个对我们来说合适的k值。

一旦我们找到了 k 近邻分类器,我们就进行多数投票。新的数据点通过 k 近邻分类器的多数投票进行分类。这个数据点被分配给其 k 个最近邻中最常见的类别。如果我们把k的值设为1,那么这仅仅是一个最近邻分类器的案例,我们只需将数据点分配给训练数据集中其最近邻的类别。

更多内容...

k 近邻算法基于通过考虑训练集最接近的 k 个样本的类别来对未知样本进行分类的概念。新的样本将被分配给大多数 k 个最近样本所属的类别。因此,k 的选择对于将样本分配到正确的类别非常重要。如果 k 太小,分类可能对噪声敏感;如果 k 太大,分类可能计算成本高,并且邻域可能包括属于其他类别的样本。

参考以下内容

构建一个 k 近邻回归器

我们学习了如何使用 k 近邻算法构建分类器。好事是,我们也可以将此算法用作回归器。对象的输出由其属性值表示,这是其 k 个最近邻的值的平均值。

准备工作

在这个菜谱中,我们将看到如何使用 k 近邻算法构建回归器。

如何操作...

让我们看看如何构建一个 k 近邻回归器:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的nn_regression.py文件中):
import numpy as np 
import matplotlib.pyplot as plt 
from sklearn import neighbors 
  1. 让我们生成一些样本高斯分布数据:
# Generate sample data 
amplitude = 10 
num_points = 100 
X = amplitude * np.random.rand(num_points, 1) - 0.5 * amplitude 
  1. 我们需要向数据中添加一些噪声,以引入一些随机性。添加噪声的目的是看看我们的算法是否能够克服它,并且仍然以鲁棒的方式运行:
# Compute target and add noise 
y = np.sinc(X).ravel()  
y += 0.2 * (0.5 - np.random.rand(y.size))
  1. 现在,让我们可视化它,如下所示:
# Plot input data 
plt.figure() 
plt.scatter(X, y, s=40, c='k', facecolors='none') 
plt.title('Input data') 
  1. 我们刚刚生成了一些数据,并在所有这些点上评估了一个连续值函数。让我们定义一个更密集的点网格:
# Create the 1D grid with 10 times the density of the input data 
x_values = np.linspace(-0.5*amplitude, 0.5*amplitude, 10*num_points)[:, np.newaxis] 

我们定义这个更密集的网格是因为我们想要评估我们的回归器在这些所有点上,并查看它如何近似我们的函数。

  1. 现在我们定义我们想要考虑的最近邻的数量:
# Number of neighbors to consider  
n_neighbors = 8 
  1. 让我们使用之前定义的参数初始化并训练 k 近邻回归器:
# Define and train the regressor 
knn_regressor = neighbors.KNeighborsRegressor(n_neighbors, weights='distance') 
y_values = knn_regressor.fit(X, y).predict(x_values) 
  1. 让我们通过将输入和输出数据重叠在一起来查看回归器的表现:
plt.figure() 
plt.scatter(X, y, s=40, c='k', facecolors='none', label='input data') 
plt.plot(x_values, y_values, c='k', linestyle='--', label='predicted values') 
plt.xlim(X.min() - 1, X.max() + 1) 
plt.ylim(y.min() - 0.2, y.max() + 0.2) 
plt.axis('tight') 
plt.legend() 
plt.title('K Nearest Neighbors Regressor') 

plt.show()
  1. 如果你运行此代码,第一个图表描述了输入数据点:

图片

第二个图表展示了回归器预测的值:

图片

它是如何工作的...

回归器的目标是预测连续值输出。在这种情况下,我们没有固定数量的输出类别。我们只有一组实值输出值,我们希望我们的回归器预测未知数据点的输出值。在这种情况下,我们使用 sinc 函数来演示 k 近邻回归器。这也被称为 卡丹尔正弦函数sinc 函数由以下方程定义:

图片

x0 时,sin(x)/x 取得不定形 0/0。因此,我们必须计算当 x 趋近于 0 时该函数的极限。我们使用一组值进行训练,并为测试定义了一个更密集的网格。如图所示,输出曲线接近训练输出。

还有更多...

这种方法的主要优点是它不需要学习或构建模型;它可以以任意方式调整其决策边界,产生最灵活的模型表示;并且它还保证了增加训练集的可能性。然而,此算法也有许多缺点,包括易受数据噪声的影响、对无关特征的敏感,以及需要相似度度量来评估邻近性。

参考信息

计算欧几里得距离得分

现在我们已经对机器学习管道和最近邻分类器有了足够的背景知识,让我们开始讨论推荐引擎。为了构建推荐引擎,我们需要定义一个相似性度量,这样我们就可以找到与给定用户相似的数据库中的用户。欧几里得距离得分就是这样一种度量,我们可以用它来计算数据点之间的距离。我们将讨论转向电影推荐引擎。

准备工作

在这个菜谱中,我们将了解如何计算两个用户之间的欧几里得得分。

如何操作...

让我们看看如何计算欧几里得距离得分:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已经为你提供的euclidean_score.py文件中):
import json 
import numpy as np 
  1. 我们现在将定义一个函数来计算两个用户之间的欧几里得得分。第一步是检查用户是否存在于数据库中:
# Returns the Euclidean distance score between user1 and user2  
def euclidean_score(dataset, user1, user2): 
    if user1 not in dataset: 
        raise TypeError('User ' + user1 + ' not present in the dataset') 

    if user2 not in dataset: 
        raise TypeError('User ' + user2 + ' not present in the dataset') 
  1. 为了计算得分,我们需要提取两个用户都评价过的电影:
    # Movies rated by both user1 and user2 
    rated_by_both = {}  

    for item in dataset[user1]: 
        if item in dataset[user2]: 
            rated_by_both[item] = 1 
  1. 如果没有共同的电影,那么用户之间没有相似性(或者至少,根据数据库中的评分我们无法计算它):
    # If there are no common movies, the score is 0  
    if len(rated_by_both) == 0: 
        return 0 
  1. 对于每个共同评分,我们只计算平方差的和的平方根并归一化,使得得分在 0 到 1 之间:
    squared_differences = []  

    for item in dataset[user1]: 
        if item in dataset[user2]: 
            squared_differences.append(np.square(dataset[user1][item] - dataset[user2][item])) 

    return 1 / (1 + np.sqrt(np.sum(squared_differences)))  

如果评分相似,则平方差的和将非常低。因此,得分将变得很高,这正是我们想要的这个度量标准。

  1. 我们将使用movie_ratings.json文件作为我们的数据文件。让我们按照以下方式加载它:
if __name__=='__main__': 
    data_file = 'movie_ratings.json' 

    with open(data_file, 'r') as f: 
        data = json.loads(f.read()) 
  1. 让我们考虑两个随机用户并计算欧几里得距离得分:
    user1 = 'John Carson' 
    user2 = 'Michelle Peterson' 

    print("Euclidean score:")
    print(euclidean_score(data, user1, user2)) 
  1. 当你运行此代码时,你将在终端上看到以下欧几里得距离得分:
0.29429805508554946

工作原理...

在大多数情况下,最近邻算法中使用的距离定义为两个点之间的欧几里得距离

根据以下公式计算点数:

在二维平面上,欧几里得距离表示两点之间的最小距离,因此是连接两点的直线。这个距离是两个向量元素平方差的和的平方根,如前一个公式所示。

还有更多...

有其他类型的度量用于计算距离。所有这些类型通常试图避免平方根,因为它们在计算上很昂贵,并且是几个错误的原因。度量包括闵可夫斯基曼哈顿余弦距离。

参考信息

计算皮尔逊相关系数

欧几里得距离得分是一个好的度量标准,但它有一些缺点。因此,皮尔逊相关系数在推荐引擎中经常被使用。两个统计变量之间的皮尔逊相关系数是一个指数,表示它们之间可能存在的线性关系。它衡量两个数值变量同时变化的趋势。

准备工作

在这个菜谱中,我们将看到如何计算皮尔逊相关系数。

如何做...

让我们看看如何计算皮尔逊相关系数:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码已包含在为你提供的pearson_score.py文件中):
import json 
import numpy as np 
  1. 我们将定义一个函数来计算数据库中两个用户之间的皮尔逊相关系数。我们的第一步是确认这些用户存在于数据库中:
# Returns the Pearson correlation score between user1 and user2  
def pearson_score(dataset, user1, user2): 
    if user1 not in dataset: 
        raise TypeError('User ' + user1 + ' not present in the dataset') 

    if user2 not in dataset: 
        raise TypeError('User ' + user2 + ' not present in the dataset') 
  1. 下一步是获取这两位用户都评分的电影:
    # Movies rated by both user1 and user2 
    rated_by_both = {} 

    for item in dataset[user1]: 
        if item in dataset[user2]: 
            rated_by_both[item] = 1 

    num_ratings = len(rated_by_both)  
  1. 如果没有共同的电影,那么这些用户之间没有可识别的相似性;因此,我们返回0
    # If there are no common movies, the score is 0  
    if num_ratings == 0: 
        return 0
  1. 我们需要计算共同电影评分的平方值之和:
    # Compute the sum of ratings of all the common preferences  
    user1_sum = np.sum([dataset[user1][item] for item in rated_by_both]) 
    user2_sum = np.sum([dataset[user2][item] for item in rated_by_both]) 
  1. 现在,让我们计算所有共同电影评分的平方值之和:
    # Compute the sum of squared ratings of all the common preferences  
    user1_squared_sum = np.sum([np.square(dataset[user1][item]) for item in rated_by_both]) 
    user2_squared_sum = np.sum([np.square(dataset[user2][item]) for item in rated_by_both]) 
  1. 现在让我们计算乘积之和:
    # Compute the sum of products of the common ratings  
    product_sum = np.sum([dataset[user1][item] * dataset[user2][item] for item in rated_by_both]) 
  1. 我们现在准备计算计算皮尔逊相关系数所需的各种元素:
    # Compute the Pearson correlation 
    Sxy = product_sum - (user1_sum * user2_sum / num_ratings) 
    Sxx = user1_squared_sum - np.square(user1_sum) / num_ratings 
    Syy = user2_squared_sum - np.square(user2_sum) / num_ratings 
  1. 我们需要处理分母变为0的情况:
    if Sxx * Syy == 0: 
        return 0 
  1. 如果一切正常,我们返回皮尔逊相关系数,如下所示:
    return Sxy / np.sqrt(Sxx * Syy) 
  1. 现在让我们定义main函数并计算两个用户之间的皮尔逊相关系数:
if __name__=='__main__': 
    data_file = 'movie_ratings.json' 

    with open(data_file, 'r') as f: 
        data = json.loads(f.read()) 

    user1 = 'John Carson' 
    user2 = 'Michelle Peterson' 

    print("Pearson score:")
    print(pearson_score(data, user1, user2)) 
  1. 如果你运行此代码,你将在终端上看到以下皮尔逊相关系数:
Pearson score:
0.39605901719066977

它是如何工作的...

皮尔逊相关系数的r系数衡量的是变量在间隔或等效比率之间的相关性。它由两个变量的标准化分数的乘积之和(z[x] * z[y])除以受试者(或观察)的数量得出,如下所示:

这个系数可以取介于-1.00(两个变量之间存在完美的负相关)和+1.00(两个变量之间存在完美的正相关)之间的值。相关性为 0 表示两个变量之间没有关系。

还有更多...

必须记住,皮尔逊公式与线性关系相关,因此,所有不同形式的关系都可能产生异常结果。

参考资料还包括

在数据集中找到相似的用户

在构建推荐引擎时,最重要的任务之一是找到相似的用户。这在创建提供给这些用户的推荐时非常有用。

准备工作

在这个菜谱中,我们将看到如何构建一个模型来查找相似用户。

如何操作...

让我们看看如何在数据集中查找相似用户:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已为你提供的 find_similar_users.py 文件中):
import json 
import numpy as np 

from pearson_score import pearson_score 
  1. 让我们定义一个函数来查找与输入用户相似的用户。它接受三个输入参数:数据库、输入用户以及我们正在寻找的相似用户数量。我们的第一步是检查用户是否存在于数据库中。如果用户存在,我们需要计算该用户与数据库中所有其他用户的皮尔逊相关得分:
# Finds a specified number of users who are similar to the input user 
def find_similar_users(dataset, user, num_users): 
    if user not in dataset: 
        raise TypeError('User ' + user + ' not present in the dataset') 

    # Compute Pearson scores for all the users 
    scores = np.array([[x, pearson_score(dataset, user, x)] for x in dataset if user != x]) 
  1. 下一步是将这些分数按降序排列:
    # Sort the scores based on second column 
    scores_sorted = np.argsort(scores[:, 1]) 

    # Sort the scores in decreasing order (highest score first)  
    scored_sorted_dec = scores_sorted[::-1] 
  1. 现在我们提取 k 个最高得分并返回它们:
    # Extract top 'k' indices 
    top_k = scored_sorted_dec[0:num_users]  

    return scores[top_k]  
  1. 现在我们定义主函数并加载输入数据库:
if __name__=='__main__': 
    data_file = 'movie_ratings.json' 

    with open(data_file, 'r') as f: 
        data = json.loads(f.read()) 
  1. 我们想找到与 John Carson 相似的三个用户。我们通过以下步骤来完成:
    user = 'John Carson'
    print("Users similar to " + user + ":\n")
    similar_users = find_similar_users(data, user, 3) 
    print("User\t\t\tSimilarity score\n")
    for item in similar_users:
        print(item[0], '\t\t', round(float(item[1]), 2))
  1. 如果你运行此代码,你将在你的终端上看到以下输出:
Users similar to John Carson:

User               Similarity score
Michael Henry                  0.99
Alex Roberts                   0.75
Melissa Jones                  0.59

它是如何工作的...

在这个菜谱中,我们正在寻找与输入用户相似的用户。给定数据库、输入用户以及我们正在寻找的相似用户数量,我们首先检查用户是否存在于数据库中。如果用户存在,计算该用户与数据库中所有其他用户的皮尔逊相关得分。

还有更多...

为了计算皮尔逊相关得分,使用了 pearson_score() 函数。这个函数在之前的 计算皮尔逊相关得分 菜谱中定义。

相关内容

生成电影推荐

在这个菜谱中,我们将生成电影推荐。

准备工作

在这个菜谱中,我们将使用之前菜谱中构建的所有功能来构建一个电影推荐引擎。让我们看看如何构建它。

如何操作...

让我们看看如何生成电影推荐:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已为你提供的 movie_recommendations.py 文件中):
import json 
import numpy as np 

from pearson_score import pearson_score 
  1. 我们将定义一个函数来为给定用户生成电影推荐。第一步是检查该用户是否存在于数据集中:
# Generate recommendations for a given user 
def generate_recommendations(dataset, user): 
    if user not in dataset: 
        raise TypeError('User ' + user + ' not present in the dataset') 
  1. 现在我们来计算该用户与数据集中所有其他用户的皮尔逊得分:
    total_scores = {} 
    similarity_sums = {} 

    for u in [x for x in dataset if x != user]: 
        similarity_score = pearson_score(dataset, user, u) 

        if similarity_score <= 0: 
            continue 
  1. 我们需要找到该用户尚未评分的电影:
        for item in [x for x in dataset[u] if x not in dataset[user] or dataset[user][x] == 0]: 
            total_scores.update({item: dataset[u][item] * similarity_score}) 
            similarity_sums.update({item: similarity_score}) 
  1. 如果用户观看了数据库中的每一部电影,那么我们无法为该用户推荐任何内容。让我们处理这个条件:
    if len(total_scores) == 0: 
        return ['No recommendations possible'] 
  1. 现在我们有了这些分数的列表。让我们创建一个电影排名的归一化列表:
    # Create the normalized list 
    movie_ranks = np.array([[total/similarity_sums[item], item]  
            for item, total in total_scores.items()]) 
  1. 我们需要根据分数按降序对列表进行排序:
    # Sort in decreasing order based on the first column 
    movie_ranks = movie_ranks[np.argsort(movie_ranks[:, 0])[::-1]] 
  1. 我们终于准备好提取电影推荐了:
    # Extract the recommended movies 
    recommendations = [movie for _, movie in movie_ranks] 

    return recommendations
  1. 现在,让我们定义main函数并加载数据集:
if __name__=='__main__': 
    data_file = 'movie_ratings.json' 

    with open(data_file, 'r') as f: 
        data = json.loads(f.read()) 
  1. 现在,让我们为Michael Henry生成推荐,如下所示:
    user = 'Michael Henry'
    print("Recommendations for " + user + ":")
    movies = generate_recommendations(data, user) 
    for i, movie in enumerate(movies):
        print(str(i+1) + '. ' + movie)
  1. John Carson用户观看了所有电影。因此,如果我们尝试为他生成推荐,应该显示 0 个推荐。让我们看看这是否会发生,如下所示:
    user = 'John Carson' 
    print("Recommendations for " + user + ":")
    movies = generate_recommendations(data, user) 
    for i, movie in enumerate(movies):
        print(str(i+1) + '. ' + movie)
  1. 如果你运行此代码,你将在你的终端上看到以下输出:
Recommendations for Michael Henry:
1\. Jerry Maguire
2\. Inception
3\. Anger Management
Recommendations for John Carson:
1\. No recommendations possible

它是如何工作的...

在这个菜谱中,我们已经构建了一个电影推荐引擎。要为特定用户生成推荐,将执行以下步骤:

  1. 首先,我们检查用户是否存在于数据库中

  2. 然后,我们计算皮尔逊相关系数

  3. 然后,我们创建归一化列表

  4. 然后,我们根据第一列按降序排序这个列表

  5. 最后,我们提取推荐的电影

更多内容...

要构建电影推荐引擎,使用了pearson_score()函数。该函数在之前的计算皮尔逊相关系数评分菜谱中定义。

参考信息

实现排名算法

学习排名(LTR)是一种用于构建信息检索系统分类模型的方法。训练数据由包含诱导部分顺序的文章列表组成,该顺序为每篇文章提供一个数值或序数评分,或一个二元判断。模型的目的是根据从文章中获得的判断来考虑的评分,将元素重新排序到新的列表中。

准备工作

在这个菜谱中,我们将使用pyltr包,这是一个 Python LTR 工具包,包含排名模型、评估指标和数据整理助手。

如何做...

让我们看看如何实现排名算法:

  1. 创建一个新的 Python 文件并导入以下包(完整代码在已提供的LambdaMARTModel.py文件中):
import pyltr
  1. 我们将加载 Letor 数据集中包含的数据(train.txtvali.txttest.txt):
with open('train.txt') as trainfile, \
        open('vali.txt') as valifile, \
        open('test.txt') as testfile:
    TrainX, Trainy, Trainqids, _ = pyltr.data.letor.read_dataset(trainfile)
    ValX, Valy, Valqids, _ = pyltr.data.letor.read_dataset(valifile)
    TestX, Testy, Testqids, _ = pyltr.data.letor.read_dataset(testfile)
    metric = pyltr.metrics.NDCG(k=10)
  1. 现在我们对数据进行验证:
    monitor = pyltr.models.monitors.ValidationMonitor(
                ValX, Valy, Valqids, metric=metric, stop_after=250)
  1. 我们将按照以下步骤构建模型:
 model = pyltr.models.LambdaMART(
    metric=metric,
    n_estimators=1000,
    learning_rate=0.02,
    max_features=0.5,
    query_subsample=0.5,
    max_leaf_nodes=10,
    min_samples_leaf=64,
    verbose=1,
)
  1. 现在,我们可以使用文本数据来拟合模型:
model.fit(TestX, Testy, Testqids, monitor=monitor)
  1. 接下来,我们可以按照以下方式预测数据:
Testpred = model.predict(TestX)
  1. 最后,我们按照以下方式打印结果:
print('Random ranking:', metric.calc_mean_random(Testqids, Testy))
print('Our model:', metric.calc_mean(Testqids, Testy, Testpred))

以下结果将被打印:

Early termination at iteration 480
Random ranking: 0.27258472902087394
Our model: 0.5487673789992693

它是如何工作的...

LambdaMART 是 LambdaRank 的增强树版本,而 LambdaRank 又是基于 RankNet 的。RankNet、LambdaRank 和 LambdaMART 是用于解决许多上下文中的分类问题的算法。RankNet、LambdaRank 和 LambdaMART 是由微软研究小组的 Chris Burges 及其团队开发的。RankNet 是第一个被开发的,随后是 LambdaRank,然后是 LambdaMART。

RankNet 基于神经网络的使用,但其底层模型并不仅限于神经网络。RankNet 的成本函数旨在最小化排序中的反转次数。RankNet 使用随机梯度下降来优化成本函数。

研究人员发现,在 RankNet 训练过程中,不需要成本,只需要与模型分数相比的成本梯度(λ)。你可以将这些梯度想象成附加在分类列表中每个文档上的小箭头,指示我们可以移动这些文档的方向。LambdaRank 基于这个假设。

还有更多...

最后,LambdaMART 结合了 LambdaRank 中的方法和多重回归加性树MART)中的方法。虽然 MART 使用增强梯度的决策树进行预测,但 LambdaMART 使用来自 LambdaRank 的成本函数的增强梯度决策树来解决排序任务。LambdaMART 证明比 LambdaRank 和原始 RankNet 更有效。

参考信息

使用 TensorFlow 构建过滤模型

协同过滤是指一类工具和机制,它允许从大量未区分的知识中检索有关给定用户兴趣的预测信息。协同过滤在推荐系统环境中被广泛使用。协同算法的一个知名类别是矩阵分解。

协同过滤概念背后的基本假设是,每个表现出一定偏好集合的用户将继续在未来表现出这些偏好。协同过滤的一个流行例子是从给定用户的基本口味和偏好知识开始的电影推荐系统。应注意的是,尽管这些信息是针对单个用户的,但他们是从整个用户系统中处理过的知识中推导出来的。

准备工作

在这个菜谱中,我们将看到如何使用 TensorFlow 构建用于个性化推荐的协同过滤模型。我们将使用 MovieLens 1M 数据集,该数据集包含大约 6,000 名用户对大约 4,000 部电影的大约 100 万条评分。

如何做到这一点...

让我们看看如何使用 TensorFlow 构建过滤模型:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在您已经提供的TensorFilter.py文件中):
import numpy as np
import pandas as pd
import tensorflow as tf
  1. 我们将加载您已经提供的 MovieLens 1M 数据集中包含的数据(ratings.csv):
Data = pd.read_csv('ratings.csv', sep=';', names=['user', 'item', 'rating', 'timestamp'], header=None)

Data = Data.iloc[:,0:3]

NumItems = Data.item.nunique() 
NumUsers = Data.user.nunique()

print('Item: ', NumItems)
print('Users: ', NumUsers)

以下返回结果:

Item: 3706
Users: 6040
  1. 现在,让我们进行数据缩放,如下所示:
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
Data['rating'] = Data['rating'].values.astype(float)
DataScaled = pd.DataFrame(scaler.fit_transform(Data['rating'].values.reshape(-1,1)))
Data['rating'] = DataScaled
  1. 我们将构建用户物品矩阵,如下所示:
UserItemMatrix = Data.pivot(index='user', columns='item', values='rating')
UserItemMatrix.fillna(0, inplace=True)

Users = UserItemMatrix.index.tolist()
Items = UserItemMatrix.columns.tolist()

UserItemMatrix = UserItemMatrix.as_matrix()
  1. 现在,我们可以设置一些网络参数,如下所示:
NumInput = NumItems
NumHidden1 = 10
NumHidden2 = 5
  1. 现在,我们将初始化 TensorFlow 占位符。然后,权重偏置被随机初始化:
X = tf.placeholder(tf.float64, [None, NumInput])

weights = {
    'EncoderH1': tf.Variable(tf.random_normal([NumInput, NumHidden1], dtype=tf.float64)),
    'EncoderH2': tf.Variable(tf.random_normal([NumHidden1, NumHidden2], dtype=tf.float64)),
    'DecoderH1': tf.Variable(tf.random_normal([NumHidden2, NumHidden1], dtype=tf.float64)),
    'DecoderH2': tf.Variable(tf.random_normal([NumHidden1, NumInput], dtype=tf.float64)),
}

biases = {
    'EncoderB1': tf.Variable(tf.random_normal([NumHidden1], dtype=tf.float64)),
    'EncoderB2': tf.Variable(tf.random_normal([NumHidden2], dtype=tf.float64)),
    'DecoderB1': tf.Variable(tf.random_normal([NumHidden1], dtype=tf.float64)),
    'DecoderB2': tf.Variable(tf.random_normal([NumInput], dtype=tf.float64)),
}
  1. 现在,我们可以构建编码器和解码器模型,如下所示:
def encoder(x):
    Layer1 = tf.nn.sigmoid(tf.add(tf.matmul(x, weights['EncoderH1']), biases['EncoderB1']))
    Layer2 = tf.nn.sigmoid(tf.add(tf.matmul(Layer1, weights['EncoderH2']), biases['EncoderB2']))
    return Layer2

def decoder(x):
    Layer1 = tf.nn.sigmoid(tf.add(tf.matmul(x, weights['DecoderH1']), biases['DecoderB1']))
    Layer2 = tf.nn.sigmoid(tf.add(tf.matmul(Layer1, weights['DecoderH2']), biases['DecoderB2']))
    return Layer2
  1. 我们将构建模型并预测值,如下所示:
EncoderOp = encoder(X)
DecoderOp = decoder(EncoderOp)

YPred = DecoderOp

YTrue = X
  1. 现在,我们将定义损失优化器,并最小化平方误差和评估指标:
loss = tf.losses.mean_squared_error(YTrue, YPred)
Optimizer = tf.train.RMSPropOptimizer(0.03).minimize(loss)
EvalX = tf.placeholder(tf.int32, )
EvalY = tf.placeholder(tf.int32, )
Pre, PreOp = tf.metrics.precision(labels=EvalX, predictions=EvalY)
  1. 让我们现在初始化变量,如下所示:
Init = tf.global_variables_initializer()
LocalInit = tf.local_variables_initializer()
PredData = pd.DataFrame()
  1. 最后,我们可以开始训练我们的模型:
with tf.Session() as session:
    Epochs = 120
    BatchSize = 200

    session.run(Init)
    session.run(LocalInit)

    NumBatches = int(UserItemMatrix.shape[0] / BatchSize)
    UserItemMatrix = np.array_split(UserItemMatrix, NumBatches)

    for i in range(Epochs):

        AvgCost = 0

        for batch in UserItemMatrix:
            _, l = session.run([Optimizer, loss], feed_dict={X: batch})
            AvgCost += l

        AvgCost /= NumBatches

        print("Epoch: {} Loss: {}".format(i + 1, AvgCost))

    UserItemMatrix = np.concatenate(UserItemMatrix, axis=0)

    Preds = session.run(DecoderOp, feed_dict={X: UserItemMatrix})

    PredData = PredData.append(pd.DataFrame(Preds))

    PredData = PredData.stack().reset_index(name='rating')
    PredData.columns = ['user', 'item', 'rating']
    PredData['user'] = PredData['user'].map(lambda value: Users[value])
    PredData['item'] = PredData['item'].map(lambda value: Items[value])

    keys = ['user', 'item']
    Index1 = PredData.set_index(keys).index
    Index2 = Data.set_index(keys).index

    TopTenRanked = PredData[~Index1.isin(Index2)]
    TopTenRanked = TopTenRanked.sort_values(['user', 'rating'], ascending=[True, False])
    TopTenRanked = TopTenRanked.groupby('user').head(10)

    print(TopTenRanked.head(n=10))

以下结果返回:


 user item rating
2651 1 2858 0.295800
1106 1 1196 0.278715
1120 1 1210 0.251717
2203 1 2396 0.227491
1108 1 1198 0.213989
579  1 593  0.201507
802  1 858  0.196411
2374 1 2571 0.195712
309  1 318  0.191919
2785 1 2997 0.188679

这是针对1个用户的 top 10 结果。

它是如何工作的...

协同过滤方法侧重于寻找对相同对象做出相似判断的用户,从而在用户之间建立联系,向他们推荐两个用户中任何一个都正面评价或与之互动的对象。这样,我们寻找的是用户之间的关联,而不是对象之间的关联。

还有更多...

用户物品矩阵代表用户对某个对象的偏好,但如果按列读取,则突出显示某个电影是被喜欢还是不喜欢的。这样,你可以看到两个对象之间的相似性也可以通过观察喜欢相同电影的同一群人,以某种方式相似地表达出来。

相关内容

第七章:分析文本数据

在本章中,我们将涵盖以下食谱:

  • 使用标记化进行数据预处理

  • 对文本数据进行词干提取

  • 使用词形还原将文本转换为基本形式

  • 使用分块划分文本

  • 构建词袋模型

  • 构建文本分类器

  • 识别名字的性别

  • 分析句子的情感

  • 使用主题建模在文本中识别模式

  • 使用 spaCy 进行词性标注

  • 使用 gensim 的 Word2Vec

  • 使用浅层学习进行垃圾邮件检测

技术要求

为了完成本章的食谱,您需要以下文件(可在 GitHub 上找到):

  • tokenizer.py

  • stemmer.py

  • lemmatizer.py

  • chunking.py

  • bag_of_words.py

  • tfidf.py

  • `gender_identification.py`

  • sentiment_analysis.py

  • topic_modeling.py

  • data_topic_modeling.txt

  • PosTagging.py

  • GensimWord2Vec.py

  • LogiTextClassifier.py

  • spam.csv

简介

文本分析和自然语言处理(NLP)是现代人工智能系统的一个基本组成部分。计算机擅长理解结构严格、变化有限的数据。然而,当我们处理非结构化、自由形式的文本时,事情开始变得困难。开发 NLP 应用具有挑战性,因为计算机很难理解其背后的概念。我们的交流方式也有很多微妙的变体,这些变体可能以方言、上下文、俚语等形式存在。

为了解决这个问题,基于机器学习的 NLP 应用被开发出来。这些算法在文本数据中检测模式,以便我们可以从中提取见解。人工智能公司大量使用 NLP 和文本分析来提供相关结果。NLP 最常见的一些应用包括搜索引擎、情感分析、主题建模、词性标注和实体识别。NLP 的目标是开发一套算法,以便我们可以用普通的英语与计算机交互。如果我们能实现这一点,那么我们就无需编程语言来指导计算机应该做什么。在本章中,我们将探讨一些专注于文本分析和如何从文本数据中提取有意义信息的食谱。

在本章中,我们将大量使用名为自然语言工具包(NLTK)的 Python 包。在继续之前,请确保您已经安装了它:

  • 您可以在www.nltk.org/install.html找到安装步骤。

  • 您还需要安装NLTK Data,它包含许多语料库和训练模型。这是文本分析的一个基本组成部分!您可以在www.nltk.org/data.html找到安装步骤。

使用标记化进行数据预处理

分词是将文本分割成一系列有意义的片段的过程。这些片段被称为标记。例如,我们可以将一大段文本分割成单词,或者将其分割成句子。根据手头的任务,我们可以定义自己的条件将输入文本分割成有意义的标记。让我们看看如何做到这一点。

准备工作

分词是文本计算分析的第一步,涉及将字符序列分割成称为标记的最小分析单位。标记包括各种文本部分类别(单词、标点、数字等),也可以是复杂单位(如日期)。在这个菜谱中,我们将展示如何将一个复杂的句子分割成许多标记。

如何做到这一点...

让我们看看如何使用分词进行数据预处理:

  1. 创建一个新的 Python 文件并添加以下行(完整的代码在已经提供给你的tokenizer.py文件中)。让我们导入包和语料库:
import nltk
nltk.download('punkt')
  1. 让我们定义一些用于分析的样本text
text = "Are you curious about tokenization? Let's see how it works! We need to analyze a couple of sentences with punctuations to see it in action." 
  1. 让我们从句子分词开始。NLTK 提供了一个句子标记器,所以让我们导入它:
# Sentence tokenization 
from nltk.tokenize import sent_tokenize 
  1. 在输入text上运行句子标记器并提取标记:
sent_tokenize_list = sent_tokenize(text) 
  1. 打印句子列表以查看是否正确工作:
print("Sentence tokenizer:")
print(sent_tokenize_list) 
  1. 单词分词在 NLP 中非常常用。NLTK 附带了几种不同的单词分词器。让我们从基本的单词分词器开始:
# Create a new word tokenizer 
from nltk.tokenize import word_tokenize 

print("Word tokenizer:")
print(word_tokenize(text)) 
  1. 如果你想要将这个标点符号分割成单独的标记,那么你需要使用WordPunct标记器:
# Create a new WordPunct tokenizer 
from nltk.tokenize import WordPunctTokenizer 

word_punct_tokenizer = WordPunctTokenizer() 
print("Word punct tokenizer:")
print(word_punct_tokenizer.tokenize(text)) 
  1. 如果你运行此代码,你将在你的终端上看到以下输出:
Sentence tokenizer:
['Are you curious about tokenization?', "Let's see how it works!", 'We need to analyze a couple of sentences with punctuations to see it in action.']

Word tokenizer:
['Are', 'you', 'curious', 'about', 'tokenization', '?', 'Let', "'s", 'see', 'how', 'it', 'works', '!', 'We', 'need', 'to', 'analyze', 'a', 'couple', 'of', 'sentences', 'with', 'punctuations', 'to', 'see', 'it', 'in', 'action', '.']

Word punct tokenizer:
['Are', 'you', 'curious', 'about', 'tokenization', '?', 'Let', "'", 's', 'see', 'how', 'it', 'works', '!', 'We', 'need', 'to', 'analyze', 'a', 'couple', 'of', 'sentences', 'with', 'punctuations', 'to', 'see', 'it', 'in', 'action', '.']

它是如何工作的...

在这个菜谱中,我们展示了如何将一个复杂的句子分割成许多标记。为此,使用了nltk.tokenize包的三个方法——sent_tokenizeword_tokenizeWordPunctTokenizer

  • sent_tokenize函数返回文本的句子标记副本,使用 NLTK 推荐的句子标记器。

  • word_tokenize将字符串标记化以分割除句点以外的标点符号。

  • WordPunctTokenizer使用正则表达式\w+|[^\w\s]+将文本标记化为一个由字母和非字母字符组成的序列。

还有更多...

分词是一个过程,根据所分析的语言,可能是一个非常复杂的任务。例如,在英语中,我们可能满足于考虑没有空格的字符序列和各种标点符号。在像日语或中文这样的语言中,其中单词不是由空格分开,而是由不同符号的组合,这可能会完全改变意义,任务也更为复杂。但一般来说,即使在单词由空格分开的语言中,也必须定义精确的标准,因为标点符号通常是模糊的。

参见

词干提取文本数据

当我们处理文本文档时,我们会遇到不同形式的单词。考虑单词play。这个单词可以以各种形式出现,如 play、plays、player、playing 等。这些都是具有相似意义的单词家族。在文本分析过程中,提取这些单词的基本形式是有用的。这将帮助我们提取一些统计数据来分析整个文本。词干提取的目标是将这些不同形式减少到共同的基形式。这使用启发式过程来截断单词的末尾以提取基形式。

准备工作

在这个菜谱中,我们将使用nltk.stem包,它提供了一个用于从单词中移除形态词缀的处理接口。对于不同的语言,有不同的词干提取器可用。对于英语,我们将使用PorterStemmerLancasterStemmerSnowballStemmer

如何操作...

让我们看看如何词干提取文本数据:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在已经提供给你的stemmer.py文件中):
from nltk.stem.porter import PorterStemmer 
from nltk.stem.lancaster import LancasterStemmer 
from nltk.stem.snowball import SnowballStemmer 
  1. 让我们定义一些用于操作的words,如下所示:
words = ['table', 'probably', 'wolves', 'playing', 'is',  
        'dog', 'the', 'beaches', 'grounded', 'dreamt', 'envision'] 
  1. 我们将定义一个我们想要使用的stemmers列表:
# Compare different stemmers stemmers = ['PORTER', 'LANCASTER', 'SNOWBALL'] 
  1. 初始化所有三个词干提取器所需的必要对象:
stemmer_porter = PorterStemmer() stemmer_lancaster = LancasterStemmer() stemmer_snowball = SnowballStemmer('english') 
  1. 为了以整洁的表格形式打印输出数据,我们需要以正确的方式对其进行格式化:
formatted_row = '{:>16}' * (len(stemmers) + 1) 
print('\n', formatted_row.format('WORD', *stemmers), '\n')
  1. 让我们遍历words列表,并使用以下三个词干提取器对其进行词干提取:
for word in words: stemmed_words = [stemmer_porter.stem(word), stemmer_lancaster.stem(word), stemmer_snowball.stem(word)] print(formatted_row.format(word, *stemmed_words)) 
  1. 如果你运行此代码,你将在你的终端中看到以下输出。观察 LANCASTER 词干提取器对几个单词的不同行为:

它是如何工作的...

所有三种词干提取算法基本上都旨在实现相同的目标。三种词干提取算法之间的区别基本上在于它们操作的严格程度。如果你观察输出,你会看到 LANCASTER 词干提取器比其他两个词干提取器更严格。PORTER 词干提取器在严格性方面是最小的,而 LANCASTER 是最严格的。从 LANCASTER 词干提取器得到的词干往往显得混乱和模糊。该算法非常快,但它会大量减少单词。因此,一个好的经验法则是使用 SNOWBALL 词干提取器。

还有更多...

词干提取是将单词的屈折形式还原到其词根形式的过程,称为词干。词干不一定对应于单词的形态学词根(词元):通常,相关单词被映射到相同的词干就足够了,即使后者不是单词的有效词根。创建词干提取算法一直是计算机科学中的一个普遍问题。词干提取过程应用于搜索引擎的查询扩展,以及其他自然语言处理问题。

参见

使用词形还原将文本转换为它的基本形式

词形还原的目标也是将单词还原到其基本形式,但这是一种更结构化的方法。在先前的菜谱中,您看到我们使用词干提取器获得的基词实际上并没有什么意义。例如,单词 wolves 被还原为 wolv,这并不是一个真正的单词。词形还原通过使用词汇和词的形态学分析来解决这个问题。它移除了屈折词尾,如-ing 或-ed,并返回单词的基本形式。这种基本形式被称为词元。如果您对单词wolves进行词形还原,您将得到wolf作为输出。输出取决于标记是动词还是名词。

准备工作

在这个菜谱中,我们将使用nltk.stem包将单词的屈折形式还原到其规范形式,称为词元

如何做到这一点...

让我们看看如何使用词形还原将文本转换为它的基本形式:

  1. 创建一个新的 Python 文件并导入以下包(完整代码在已提供的lemmatizer.py文件中):
import nltk
nltk.download('wordnet')
from nltk.stem import WordNetLemmatizer 
  1. 让我们定义与我们在词干提取过程中使用的相同的一组单词:
words = ['table', 'probably', 'wolves', 'playing', 'is', 'dog', 'the', 'beaches', 'grounded', 'dreamt', 'envision'] 
  1. 我们将比较两个词形还原器:NOUNVERB词形还原器。让我们列出它们:
# Compare different lemmatizers 
lemmatizers = ['NOUN LEMMATIZER', 'VERB LEMMATIZER'] 
  1. 基于词形还原器WordNet创建对象:
lemmatizer_wordnet = WordNetLemmatizer() 
  1. 为了以表格形式打印输出,我们需要以正确的方式格式化它:
formatted_row = '{:>24}' * (len(lemmatizers) + 1) 
print('\n', formatted_row.format('WORD', *lemmatizers), '\n') 
  1. 遍历单词并对它们进行词形还原:
for word in words: 
    lemmatized_words = [lemmatizer_wordnet.lemmatize(word, pos='n'), 
           lemmatizer_wordnet.lemmatize(word, pos='v')] 
    print(formatted_row.format(word, *lemmatized_words)) 
  1. 如果您运行此代码,您将看到以下输出:

观察当词形还原器对单词进行词形还原时,NOUNVERB词形还原器的区别,如图所示的前一个截图

它是如何工作的...

词形还原是将单词的屈折形式还原为其规范形式的过程,称为词元。在自然语言处理过程中,词形还原是自动确定给定单词的单词的算法过程。这个过程可能涉及其他语言处理活动,如形态学和语法分析。在许多语言中,单词以不同的屈折形式出现。规范形式与其词性的组合称为单词的词素。在结构词理学中,词素是构成语言词汇的最小单位。因此,每种语言的词汇可能对应于词典中的词元形式。

还有更多...

在 NLTK 中,对于词形还原,有WordNet可用,但这个资源仅限于英语语言。这是一个包含大量英语词汇的大型词汇数据库。在这个包中,名词、动词、形容词和副词被分组为认知同义词集(synsets),每个集合都表达一个独特概念。synsets 通过语义和词汇概念关系相互连接。通过浏览器可以导航这个显著相关的单词和概念的网络。WordNet根据单词的意义分组单词,不仅连接词形(字母字符串),还连接特定的单词。因此,在网络中彼此靠近的单词在语义上是区分的。此外,WordNet还标记单词之间的语义关系。

参见

使用块划分文本

块划分指的是根据任何随机条件将输入文本分割成片段。这与分词不同,因为没有任何限制,并且块不需要有任何意义。这在文本分析中非常常用。在处理大型文本文档时,最好分块进行。

如何做到这一点...

让我们看看如何使用块划分来划分文本:

  1. 创建一个新的 Python 文件并导入以下包(完整代码在已提供的chunking.py文件中):
import numpy as np 
nltk.download('brown')
from nltk.corpus import brown 
  1. 让我们定义一个函数来split文本成块。第一步是根据空格划分文本:
# Split a text into chunks  
def splitter(data, num_words): 
    words = data.split(' ') 
    output = [] 
  1. 初始化一些必需的变量:
    cur_count = 0 
    cur_words = [] 
  1. 让我们遍历words
    for word in words: 
        cur_words.append(word) 
        cur_count += 1 
  1. 一旦达到所需的单词数量,重置变量:
        if cur_count == num_words: 
            output.append(' '.join(cur_words)) 
            cur_words = [] 
            cur_count = 0 
  1. 将块追加到output变量中,并返回它:
    output.append(' '.join(cur_words) ) 

    return output 
  1. 我们现在可以定义主函数。从brown语料库加载数据。我们将使用前 10,000 个单词:
if __name__=='__main__': 
    # Read the data from the Brown corpus 
    data = ' '.join(brown.words()[:10000]) 
  1. 定义每个块中的单词数量:
    # Number of words in each chunk  
    num_words = 1700 
  1. 初始化一些相关变量:
    chunks = [] 
    counter = 0 
  1. 在此文本 data 上调用 splitter 函数并打印输出:
    text_chunks = splitter(data, num_words) 

    print("Number of text chunks =", len(text_chunks)) 
  1. 如果你运行此代码,你将在终端中看到生成的分块数量被打印出来:
Number of text chunks = 6

它是如何工作的...

分块(也称为浅层解析)是对一个命题的分析,该命题由一个主语和一个谓语以简单形式构成。主语通常是名词短语,而谓语是由一个动词和零个或多个补语及副词构成的动词短语。一个分块由一个或多个相邻的标记组成。

对于分块问题有众多方法。例如,在指定的任务中,一个分块被表示为一个由方括号分隔的词组,其中有一个表示分块类型的标签。所使用的数据集是从给定的语料库中提取与期刊文章相关的部分,并从语料库的句法树中提取信息块得到的。

还有更多...

布朗大学当代美国英语标准语料库(或简称 brown 语料库)是由亨利·库切拉(Henry Kucera)和 W. Nelson Francis 在 1960 年代在罗德岛普罗维登斯的布朗大学编制的语料库。它包含 500 个英文文本摘录,这些文本摘录来自 1961 年在美国出版的作品,总共有大约一百万个单词。

参考以下内容

构建词袋模型

当涉及到处理由数百万个单词组成的文本文档时,将它们转换为数值表示是必要的。这样做的原因是为了使它们能够用于机器学习算法。这些算法需要数值数据以便分析并输出有意义的信息。这就是词袋方法出现的地方。这基本上是一个从所有文档中的所有单词中学习词汇的模型。它通过构建文档中所有单词的直方图来对每个文档进行建模。

准备工作

在这个菜谱中,我们将使用 sklearn.feature_extraction.text 包构建一个词袋模型来提取文档词矩阵。

如何实现...

让我们看看如何构建一个词袋模型,如下所示:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码已在提供的 bag_of_words.py 文件中):
import numpy as np 
from nltk.corpus import brown 
from chunking import splitter 
  1. 让我们定义 main 函数。从 brown 语料库中加载输入 data
if __name__=='__main__': 
    # Read the data from the Brown corpus 
    data = ' '.join(brown.words()[:10000]) 
  1. 将文本数据分为五个分块:
    # Number of words in each chunk  
    num_words = 2000 

    chunks = [] 
    counter = 0 

    text_chunks = splitter(data, num_words) 
  1. 创建一个基于以下文本分块的字典:
    for text in text_chunks: 
        chunk = {'index': counter, 'text': text} 
        chunks.append(chunk) 
        counter += 1 
  1. 下一步是提取文档词矩阵。这基本上是一个矩阵,它计算文档中每个单词的出现次数。我们将使用scikit-learn来完成这项任务,因为它在这方面比 NLTK 有更好的支持。导入以下包:
    # Extract document term matrix 
    from sklearn.feature_extraction.text import CountVectorizer 
  1. 定义对象并提取文档词矩阵:
    vectorizer = CountVectorizer(min_df=5, max_df=.95) 
    doc_term_matrix = vectorizer.fit_transform([chunk['text'] for chunk in chunks]) 
  1. vectorizer对象中提取词汇并打印:
    vocab = np.array(vectorizer.get_feature_names()) 
    print("Vocabulary:")
    print(vocab)
  1. 打印文档词矩阵
    print("Document term matrix:") 
    chunk_names = ['Chunk-0', 'Chunk-1', 'Chunk-2', 'Chunk-3', 'Chunk-4'] 
  1. 要以表格形式打印它,你需要按以下方式格式化:
    formatted_row = '{:>12}' * (len(chunk_names) + 1) 
    print('\n', formatted_row.format('Word', *chunk_names), '\n') 
  1. 遍历单词并打印每个单词在不同片段中出现的次数:
    for word, item in zip(vocab, doc_term_matrix.T): 
        # 'item' is a 'csr_matrix' data structure 
        output = [str(x) for x in item.data] 
        print(formatted_row.format(word, *output)) 
  1. 如果你运行此代码,你将在终端中看到两个主要输出。第一个输出是词汇表,如下面的截图所示:
Vocabulary:
['about' 'after' 'against' 'aid' 'all' 'also' 'an' 'and' 'are' 'as' 'at'
 'be' 'been' 'before' 'but' 'by' 'committee' 'congress' 'did' 'each'
 'education' 'first' 'for' 'from' 'general' 'had' 'has' 'have' 'he'
 'health' 'his' 'house' 'in' 'increase' 'is' 'it' 'last' 'made' 'make'
 'may' 'more' 'no' 'not' 'of' 'on' 'one' 'only' 'or' 'other' 'out' 'over'
 'pay' 'program' 'proposed' 'said' 'similar' 'state' 'such' 'take' 'than'
 'that' 'the' 'them' 'there' 'they' 'this' 'time' 'to' 'two' 'under' 'up'
 'was' 'were' 'what' 'which' 'who' 'will' 'with' 'would' 'year' 'years']
  1. 第二件事是文档词矩阵,它相当长。前几行看起来如下:

图片

它是如何工作的...

考虑以下句子:

  • 句子 1: 褐色的狗正在跑步。

  • 句子 2: 黑色的狗在黑色的房间里。

  • 句子 3: 在房间里跑步是禁止的。

如果你考虑这三个句子,你将拥有以下九个独特的单词:

  • the

  • brown

  • dog

  • is

  • running

  • black

  • in

  • room

  • forbidden

现在,让我们将每个句子转换成直方图,使用每个句子中单词的数量。每个特征向量将是九维的,因为我们有九个独特的单词:

  • 句子 1: [1, 1, 1, 1, 1, 0, 0, 0, 0]

  • 句子 2: [2, 0, 1, 1, 0, 2, 1, 1, 0]

  • 句子 3: [0, 0, 0, 1, 1, 0, 1, 1, 1]

一旦我们提取了这些特征向量,我们可以使用机器学习算法来分析它们。

更多内容...

词袋模型是一种在信息检索和自然语言处理中用于通过忽略词序来表示文档的方法。在这个模型中,每个文档被认为包含单词,类似于证券交易所;这允许基于列表管理这些单词,其中每个股票包含列表中的某些单词。

相关内容

构建文本分类器

文本分类的主要目的是将文本文档分类到不同的类别中。这是 NLP 中的一个重要分析技术。我们将使用一种基于tf-idf统计方法的技巧,它代表词频-逆文档频率。这是一个分析工具,帮助我们了解一个词在文档集中的重要性。这作为了一个用于分类文档的特征向量。

准备工作

在这个菜谱中,我们将使用词频-逆文档频率方法来评估一个词在集合或语料库中对于一个文档的重要性,并构建一个文本分类器。

如何操作...

让我们看看如何构建一个文本分类器:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已经提供给你的tfidf.py文件中):
from sklearn.datasets import fetch_20newsgroups 
  1. 让我们选择一个类别列表并使用字典映射命名它们。这些类别是我们刚刚导入的新闻组数据集的一部分:
category_map = {'misc.forsale': 'Sales', 'rec.motorcycles': 'Motorcycles', 
 'rec.sport.baseball': 'Baseball', 'sci.crypt': 'Cryptography', 
 'sci.space': 'Space'}
  1. 根据我们刚刚定义的类别加载训练数据:
training_data = fetch_20newsgroups(subset='train',  
        categories=category_map.keys(), shuffle=True, random_state=7) 
  1. 导入特征提取器:
# Feature extraction 
from sklearn.feature_extraction.text import CountVectorizer 
  1. 使用训练数据提取特征:
vectorizer = CountVectorizer() X_train_termcounts = vectorizer.fit_transform(training_data.data) print("Dimensions of training data:", X_train_termcounts.shape)
  1. 我们现在准备好训练分类器了。我们将使用多项式朴素贝叶斯分类器:
# Training a classifier 
from sklearn.naive_bayes import MultinomialNB 
from sklearn.feature_extraction.text import TfidfTransformer 
  1. 定义几个随机的输入句子:
input_data = [ "The curveballs of right handed pitchers tend to curve to the left", "Caesar cipher is an ancient form of encryption", "This two-wheeler is really good on slippery roads" 
] 
  1. 定义tfidf_transformer对象并对其进行训练:
# tf-idf transformer 
tfidf_transformer = TfidfTransformer() 
X_train_tfidf = tfidf_transformer.fit_transform(X_train_termcounts) 
  1. 一旦我们有了特征向量,就使用这些数据训练多项式朴素贝叶斯分类器:
# Multinomial Naive Bayes classifier 
classifier = MultinomialNB().fit(X_train_tfidf, training_data.target) 
  1. 使用词频转换输入数据:
X_input_termcounts = vectorizer.transform(input_data) 
  1. 使用tfidf_transformer模块转换输入数据:
X_input_tfidf = tfidf_transformer.transform(X_input_termcounts) 
  1. 使用训练好的分类器预测这些输入句子的输出类别:
# Predict the output categories 
predicted_categories = classifier.predict(X_input_tfidf) 
  1. 按照以下方式打印输出:
# Print the outputs 
for sentence, category in zip(input_data, predicted_categories): 
    print('\nInput:', sentence, '\nPredicted category:', \
            category_map[training_data.target_names[category]])
  1. 如果你运行此代码,你将在你的终端中看到以下输出:
Dimensions of training data: (2968, 40605)

Input: The curveballs of right handed pitchers tend to curve to the left 
Predicted category: Baseball

Input: Caesar cipher is an ancient form of encryption 
Predicted category: Cryptography

Input: This two-wheeler is really good on slippery roads 
Predicted category: Motorcycles

它是如何工作的...

tf-idf 技术常用于信息检索。目标是理解文档中每个词的重要性。我们想要识别在文档中多次出现的词。同时,像isbe这样的常见词并不能真正反映内容的本质。因此,我们需要提取真正的指示词。每个词的重要性随着计数的增加而增加。同时,随着它出现的频率增加,这个词的频率也会增加。这两者往往相互平衡。我们从每个句子中提取词频。一旦我们将其转换为特征向量,我们就可以训练分类器来对这些句子进行分类。

词频TF)衡量一个词在给定文档中出现的频率。由于多个文档的长度不同,直方图中的数字往往变化很大。因此,我们需要对其进行归一化,以便使其成为公平的竞争环境。为了实现归一化,我们可以将词频除以给定文档中的总词数。逆文档频率IDF)衡量一个给定词的重要性。当我们计算 TF 时,所有词都被视为同等重要。为了平衡常见词的频率,我们需要降低它们的权重并放大罕见词。我们需要计算包含给定词的文档数量与总文档数量的比率,然后除以总文档数量。IDF 是通过取该比率的负算法来计算的。

还有更多...

简单的词,如isthe,在各种文档中出现的频率很高。然而,这并不意味着我们可以根据这些词来表征文档。同时,如果一个词只出现一次,那么它也没有用。因此,我们寻找出现次数较多但不会造成噪声的词。这被公式化为 tf-idf 技术,并用于分类文档。搜索引擎经常使用这个工具按相关性排序搜索结果。

参见

识别姓名的性别

识别姓名的性别是 NLP 中的一个有趣任务。我们将使用启发式方法,即姓名的最后几个字符是其定义特征。例如,如果姓名以la结尾,那么它很可能是女性姓名,如 Angela 或 Layla。另一方面,如果姓名以im结尾,那么它很可能是男性姓名,如 Tim 或 Jim。由于我们不确定要使用确切多少个字符,我们将进行实验。

准备工作

在这个菜谱中,我们将使用名称语料库提取标记的名称,然后我们将根据名称的最后一部分来分类性别。

如何实现...

让我们看看如何识别性别:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已经提供给你的gender_identification.py文件中):
import nltk
nltk.download('names')

import random from nltk.corpus import names from nltk import NaiveBayesClassifier from nltk.classify import accuracy as nltk_accuracy 
  1. 我们需要定义一个函数来从输入词中提取特征:
# Extract features from the input word def gender_features(word, num_letters=2): return {'feature': word[-num_letters:].lower()} 
  1. 让我们定义主函数。我们需要一些标记的训练数据:
if __name__=='__main__': 
    # Extract labeled names 
    labeled_names = ([(name, 'male') for name in names.words('male.txt')] + 
            [(name, 'female') for name in names.words('female.txt')]) 
  1. 初始化随机数生成器并打乱训练数据:
    random.seed(7) 
    random.shuffle(labeled_names) 
  1. 定义一些输入名称进行实验:
    input_names = ['Leonardo', 'Amy', 'Sam'] 
  1. 由于我们不知道需要考虑多少个结尾字符,我们将从 15 扫描参数空间。每次,我们将提取以下特征:
    # Sweeping the parameter space 
    for i in range(1, 5): 
        print('\nNumber of letters:', i) 
        featuresets = [(gender_features(n, i), gender) for (n, gender) in labeled_names] 
  1. 将其分为训练集和测试集:
        train_set, test_set = featuresets[500:], featuresets[:500] 
  1. 我们将使用朴素贝叶斯分类器来完成这项工作:
        classifier = NaiveBayesClassifier.train(train_set) 
  1. 对参数空间中的每个值评估 classifier 模型:
        # Print classifier accuracy 
        print('Accuracy ==>', str(100 * nltk_accuracy(classifier, test_set)) + str('%')) 

# Predict outputs for new inputs 
        for name in input_names: 
            print(name, '==>', classifier.classify(gender_features(name, i))) 
  1. 如果你运行此代码,你将在你的终端中看到以下输出:
Number of letters: 1
Accuracy ==> 76.2%
Leonardo ==> male
Amy ==> female
Sam ==> male

Number of letters: 2
Accuracy ==> 78.6%
Leonardo ==> male
Amy ==> female
Sam ==> male

Number of letters: 3
Accuracy ==> 76.6%
Leonardo ==> male
Amy ==> female
Sam ==> female

Number of letters: 4
Accuracy ==> 70.8%
Leonardo ==> male
Amy ==> female
Sam ==> female

它是如何工作的...

在这个菜谱中,我们使用了语料库的名称来提取标记的名称,然后根据名称的最后一部分对性别进行分类。朴素贝叶斯分类器是一种监督学习分类器,它使用贝叶斯定理来构建模型。这个主题在第二章的构建朴素贝叶斯分类器菜谱中有所涉及,即构建分类器

更多内容...

贝叶斯分类器被称为朴素,因为它天真地假设在感兴趣的特定类别中,某个特定特征的呈现或缺失与其他特征的呈现或缺失无关,这极大地简化了计算。让我们继续构建朴素贝叶斯分类器。

相关内容

分析句子的情感

情感分析是自然语言处理中最受欢迎的应用之一。情感分析是指确定给定文本是正面还是负面的过程。在某些变体中,我们将中性视为第三个选项。这项技术通常用于发现人们对特定主题的看法。这用于分析各种形式中用户的情感,如营销活动、社交媒体、电子商务等。

准备工作

在这个菜谱中,我们将通过使用 movie_reviews 语料库中的数据,使用朴素贝叶斯分类器来分析句子的情感。

如何做到这一点...

让我们看看如何分析句子的情感:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已经提供给你的 sentiment_analysis.py 文件中):
import nltk.classify.util 
from nltk.classify import NaiveBayesClassifier 
from nltk.corpus import movie_reviews 
  1. 定义一个提取特征的功能:
def extract_features(word_list): 
    return dict([(word, True) for word in word_list]) 
  1. 我们需要为此准备训练数据,因此我们将使用 NLTK 中的电影评论:
if __name__=='__main__': 
    # Load positive and negative reviews   
    positive_fileids = movie_reviews.fileids('pos') 
    negative_fileids = movie_reviews.fileids('neg') 
  1. 让我们将它们分为正面和负面评论:
    features_positive = [(extract_features(movie_reviews.words(fileids=[f])),  
            'Positive') for f in positive_fileids] 
    features_negative = [(extract_features(movie_reviews.words(fileids=[f])),  
            'Negative') for f in negative_fileids] 
  1. 将数据分为训练集和测试集:
    # Split the data into train and test (80/20) 
    threshold_factor = 0.8 
    threshold_positive = int(threshold_factor * len(features_positive)) 
    threshold_negative = int(threshold_factor * len(features_negative)) 
  1. 提取特征:
    features_train = features_positive[:threshold_positive] + features_negative[:threshold_negative] 
    features_test = features_positive[threshold_positive:] + features_negative[threshold_negative:]   
    print("Number of training datapoints:", len(features_train))
    print("Number of test datapoints:", len(features_test))
  1. 我们将使用一个 NaiveBayesClassifier。定义对象并对其进行训练:
    # Train a Naive Bayes classifier 
    classifier = NaiveBayesClassifier.train(features_train) 
    print("Accuracy of the classifier:", nltk.classify.util.accuracy(classifier, features_test))
  1. classifier 对象包含了它在分析过程中获得的最具信息量的单词。这些单词基本上在将评论分类为正面或负面评论方面具有很大的影响力。让我们把它们打印出来:
    print("Top 10 most informative words:")
    for item in classifier.most_informative_features()[:10]:
        print(item[0])
  1. 创建几个随机的输入句子:
    # Sample input reviews 
    input_reviews = [ 
        "It is an amazing movie",  
        "This is a dull movie. I would never recommend it to anyone.", 
        "The cinematography is pretty great in this movie",  
        "The direction was terrible and the story was all over the place"  
    ] 
  1. 在这些输入句子上运行分类器并获取预测:
    print("Predictions:") 
    for review in input_reviews: 
        print("Review:", review) 
        probdist = classifier.prob_classify(extract_features(review.split())) 
        pred_sentiment = probdist.max() 
  1. 打印输出:
        print("Predicted sentiment:", pred_sentiment) 
        print("Probability:", round(probdist.prob(pred_sentiment), 2))
  1. 如果你运行此代码,你将在终端中看到三个主要输出。第一个是准确性,如下面的代码片段所示:
Number of training datapoints: 1600
Number of test datapoints: 400
Accuracy of the classifier: 0.735
  1. 下一个项目是最具信息量的单词列表:
Top 10 most informative words:

outstanding
insulting
vulnerable
ludicrous
uninvolving
astounding
avoids
fascination
seagal
anna
  1. 最后一个项目是预测列表,这些预测基于输入句子:
Predictions:

Review: It is an amazing movie
Predicted sentiment: Positive
Probability: 0.61

Review: This is a dull movie. I would never recommend it to anyone.
Predicted sentiment: Negative
Probability: 0.77

Review: The cinematography is pretty great in this movie
Predicted sentiment: Positive
Probability: 0.67

Review: The direction was terrible and the story was all over the place
Predicted sentiment: Negative
Probability: 0.63

工作原理...

在这里,我们使用了 NLTK 的朴素贝叶斯分类器。在特征提取函数中,我们基本上提取了所有唯一的单词。然而,NLTK 分类器需要数据以字典的形式排列。因此,我们以这种方式排列它,以便 NLTK classifier 对象可以接受它。一旦我们将数据分为训练集和测试集,我们就训练了分类器,以将句子分类为正面或负面。

如果你查看最具信息量的单词,你可以看到我们有诸如“杰出”这样的词来表示正面评论,以及诸如“侮辱”这样的词来表示负面评论。这是有趣的信息,因为它告诉我们哪些单词被用来表示强烈的反应。

更多内容...

情感分析这个术语指的是使用 NLP 技术、文本分析和计算语言学在书面或口语文本来源中查找信息。如果从大量数据中提取这种主观信息,并且因此从大量人群的意见中提取,情感分析也可以称为意见挖掘

相关内容

使用主题建模在文本中识别模式

主题建模指的是在文本数据中识别隐藏模式的过程。目标是揭示一组文档中的隐藏主题结构。这将帮助我们更好地组织文档,以便我们可以用于分析。这是 NLP 研究的一个活跃领域。

准备工作

在这个菜谱中,我们将使用名为gensim的库,通过主题建模来识别文本中的模式。

如何操作...

让我们看看如何通过主题建模来识别文本中的模式:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在已经提供给你的 topic_modeling.py 文件中):
from nltk.tokenize import RegexpTokenizer   
from nltk.stem.snowball import SnowballStemmer 
from gensim import models, corpora 
from nltk.corpus import stopwords 
  1. 定义一个函数来加载数据。我们将使用你已提供的data_topic_modeling.txt文本文件:
# Load input data 
def load_data(input_file): 
    data = [] 
    with open(input_file, 'r') as f: 
        for line in f.readlines(): 
            data.append(line[:-1]) 

    return data 
  1. 让我们定义一个class来预处理文本。这个预处理器将负责创建所需的对象并从输入文本中提取相关特征:
# Class to preprocess text 
class Preprocessor(object): 
    # Initialize various operators 
    def __init__(self): 
        # Create a regular expression tokenizer 
        self.tokenizer = RegexpTokenizer(r'\w+') 
  1. 我们需要一个停用词列表,这样我们就可以从分析中排除它们。这些是常见的词,如intheis等等:
        # get the list of stop words  
        self.stop_words_english = stopwords.words('english') 
  1. 定义SnowballStemmer模块:
        # Create a Snowball stemmer  
        self.stemmer = SnowballStemmer('english') 
  1. 定义一个处理函数,该函数负责分词、去除停用词和词干提取:
    # Tokenizing, stop word removal, and stemming 
    def process(self, input_text): 
        # Tokenize the string 
        tokens = self.tokenizer.tokenize(input_text.lower()) 
  1. 从文本中去除停用词:
        # Remove the stop words  
        tokens_stopwords = [x for x in tokens if not x in self.stop_words_english] 
  1. 对标记执行词干提取:
        # Perform stemming on the tokens  
        tokens_stemmed = [self.stemmer.stem(x) for x in tokens_stopwords] 
  1. 返回处理后的标记:
        return tokens_stemmed 
  1. 现在我们已经准备好定义主函数。从文本文件中加载数据:
if __name__=='__main__': 
    # File containing linewise input data  
    input_file = 'data_topic_modeling.txt' 

    # Load data 
    data = load_data(input_file) 
  1. 定义一个基于我们定义的类的对象:
    # Create a preprocessor object 
    preprocessor = Preprocessor() 
  1. 我们需要处理文件中的文本并提取处理后的标记:
    # Create a list for processed documents 
    processed_tokens = [preprocessor.process(x) for x in data] 
  1. 创建一个基于分词文档的字典,以便它可以用于主题建模:
    # Create a dictionary based on the tokenized documents 
    dict_tokens = corpora.Dictionary(processed_tokens) 
  1. 我们需要使用处理后的标记创建文档-词矩阵,如下所示:
    # Create a document term matrix 
    corpus = [dict_tokens.doc2bow(text) for text in processed_tokens] 
  1. 假设我们知道文本可以分为两个主题。我们将使用一种称为潜在狄利克雷分配LDA)的主题建模技术。定义所需的参数并初始化LdaModel对象:
    # Generate the LDA model based on the corpus we just created 
    num_topics = 2 
    num_words = 4 

    ldamodel = models.ldamodel.LdaModel(corpus,  
            num_topics=num_topics, id2word=dict_tokens, passes=25) 
  1. 一旦它识别出两个主题,我们可以通过查看最有贡献的单词来看到它是如何区分这两个主题的:
    print("Most contributing words to the topics:")
    for item in ldamodel.print_topics(num_topics=num_topics, num_words=num_words):
        print ("Topic", item[0], "==>", item[1])
  1. 完整的代码在topic_modeling.py文件中。如果你运行此代码,你将在你的终端看到以下输出:
Most contributing words to the topics:
Topic 0 ==> 0.057*"need" + 0.034*"order" + 0.034*"work" + 0.034*"modern"
Topic 1 ==> 0.057*"need" + 0.034*"train" + 0.034*"club" + 0.034*"develop"

它是如何工作的...

主题建模通过识别文档中的重要单词或主题来工作。这些单词往往决定了主题的内容。我们使用正则表达式分词器,因为我们只想得到单词,而不想得到任何标点符号或其他类型的标记。因此,我们使用它来提取标记。去除停用词是另一个重要步骤,因为这有助于我们消除由isthe等词引起的噪声。之后,我们需要对单词进行词干提取以得到它们的基形式。整个流程被包装为文本分析工具中的预处理块。这正是我们在做的事情!

我们使用一种称为 LDA 的技术来建模主题。LDA 基本上将文档表示为不同主题的混合,这些主题倾向于产生单词。这些单词以一定的概率产生。目标是找到这些主题!这是一个生成模型,试图找到负责生成给定文档集的主题集。

还有更多...

如您从输出中看到的,我们有诸如 talent 和 train 这样的词来表征体育主题,而我们有 encrypt 来表征密码学主题。我们正在处理一个非常小的文本文件,这就是为什么一些词可能看起来不太相关。显然,如果你处理更大的数据集,准确性将会提高。

参考以下内容

使用 spaCy 进行词性标注

词性标注PoS tagging)是指为与特定词汇类别相对应的单词进行标记的过程。常见的语言类别包括名词、动词、形容词、冠词、代词、副词、连词等等。

准备工作

在这个菜谱中,我们将使用名为spacy的库来进行词性标注。

如何操作...

让我们看看如何使用spacy进行词性标注:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已经提供给你的PosTagging.py文件中):
import spacy
  1. 加载en_core_web_sm模型:
nlp = spacy.load('en_core_web_sm')
  1. 让我们定义一个输入文本:
 Text = nlp(u'We catched fish, and talked, and we took a swim now and then to keep off sleepiness')

作为来源,我使用了基于马克·吐温的小说《哈克贝利·费恩历险记》的一段文字。

  1. 最后,我们将进行词性标注:
for token in Text:
    print(token.text, token.lemma_, token.pos_, token.tag_, token.dep_,
          token.shape_, token.is_alpha, token.is_stop) 
  1. 以下结果被返回:
We -PRON- PRON PRP nsubj Xx True False
catched catch VERB VBD ROOT xxxx True False
fish fish NOUN NN dobj xxxx True False
, , PUNCT , punct , False False
and and CCONJ CC cc xxx True True
talked talk VERB VBD conj xxxx True False
, , PUNCT , punct , False False
and and CCONJ CC cc xxx True True
we -PRON- PRON PRP nsubj xx True True
took take VERB VBD conj xxxx True False
a a DET DT det x True True
swim swim NOUN NN dobj xxxx True False
now now ADV RB advmod xxx True True
and and CCONJ CC cc xxx True True
then then ADV RB advmod xxxx True True
to to PART TO aux xx True True
keep keep VERB VB conj xxxx True True
off off PART RP prt xxx True True
sleepiness sleepiness NOUN NN dobj xxxx True False

它是如何工作的...

词性标注涉及为文档/语料库中的每个单词分配一个标签。所使用的标签集的选择取决于语言。输入是一个单词字符串和要使用的标签集,输出是每个单词与最佳标签的关联。一个单词可能有多个兼容的标签(歧义)。词性标注器的任务是通过对单词所在上下文中的最合适的标签进行选择来解决这些歧义。

还有更多...

为了进行词性标注,我们使用了spacy库。这个库可以提取语言特征,例如词性标注、依存标签和命名实体,并自定义分词器以及与基于规则的匹配器协同工作。

要安装en_core_web_sm模型,请使用以下代码:

$ python -m spacy download en

相关内容

使用 gensim 进行 Word2Vec

词嵌入使我们能够记住单词的语义和句法信息,从未知语料库开始,构建一个向量空间,其中单词的向量在相同的语言环境中出现时更接近,也就是说,如果它们被识别为语义相似。Word2Vec 是一组用于生成词嵌入的模板;该包最初由 Tomas Mikolov 用 C 语言编写,后来在 Python 和 Java 中实现。

准备工作

在这个菜谱中,我们将使用 gensim 库构建 Word2Vec 模型。

如何操作...

让我们看看如何使用 gensim 进行词嵌入:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在您已经提供的 GensimWord2Vec.py 文件中):
import gensim
from nltk.corpus import abc
  1. 基于 Word2Vec 方法构建模型:
model= gensim.models.Word2Vec(abc.sents())
  1. 让我们从数据中提取词汇并将其放入一个 list
 X= list(model.wv.vocab)
  1. 现在,我们将找到与单词 'science' 的相似性:
data=model.wv.most_similar('science')
  1. 最后,我们将 print data
print(data)

将返回以下结果:

[('law', 0.938495397567749), ('general', 0.9232532382011414), ('policy', 0.9198083877563477), ('agriculture', 0.918685793876648), ('media', 0.9151924252510071), ('discussion', 0.9143469929695129), ('practice', 0.9138249754905701), ('reservoir', 0.9102856516838074), ('board', 0.9069126844406128), ('tight', 0.9067160487174988)]

工作原理...

Word2Vec 是一个简单的双层人工神经网络,旨在处理自然语言;该算法需要输入语料库并返回一组向量,这些向量代表文本中单词的语义分布。对于语料库中包含的每个单词,以唯一的方式构建一个向量,以在创建的多维空间中表示它作为一个点。在这个空间中,如果单词被识别为语义上更相似,它们将更接近。

更多内容...

在这个菜谱中,我们使用了澳大利亚国家语料库(abc),这是一个优秀的语言数据集合,包括基于文本和数字的数据。要使用这个语料库,您必须使用以下代码下载它:

import nltk
nltk.download('abc')

相关内容

浅层学习用于垃圾邮件检测

垃圾邮件意味着发送大量不受欢迎的消息(通常是商业性质的)。它可以通过任何媒介实现,但最常用的媒介是电子邮件和短信。垃圾邮件的主要目的是广告,从最常见的商业优惠到非法材料的销售提议,如盗版软件和处方药,以及从可疑的金融项目到真正的欺诈尝试。

准备工作

在这个菜谱中,我们将使用逻辑回归模型进行垃圾邮件检测。为此,将使用为手机垃圾邮件研究收集的标记短信消息集合。这个数据集包含 5,574 条真实的非编码英语消息,根据它们是否合法(ham)或垃圾邮件(spam)进行标记。

如何操作...

让我们看看如何进行浅层学习垃圾邮件检测:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在您已经提供的 LogiTextClassifier.py 文件中):
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model.logistic import LogisticRegression
from sklearn.model_selection import train_test_split
  1. 加载您提供的 spam.csv 文件:
df = pd.read_csv('spam.csv', sep=',',header=None, encoding='latin-1')
  1. 让我们提取用于训练和测试的数据:
X_train_raw, X_test_raw, y_train, y_test = train_test_split(df[1],df[0])
  1. 我们需要将 DataFrame 中包含的文本数据 vectorize
vectorizer = TfidfVectorizer()
X_train = vectorizer.fit_transform(X_train_raw)
  1. 我们现在可以构建逻辑回归模型:
classifier = LogisticRegression(solver='lbfgs', multi_class='multinomial')
classifier.fit(X_train, y_train)
  1. 定义两个短信消息作为测试数据:
X_test = vectorizer.transform( ['Customer Loyalty Offer:The NEW Nokia6650 Mobile from ONLY å£10 at TXTAUCTION!', 'Hi Dear how long have we not heard.'] )
  1. 最后,我们将使用模型进行预测:
predictions = classifier.predict(X_test)
print(predictions)

将返回以下结果:

['spam' 'ham']

这些表明第一条短信被识别为垃圾邮件,而第二条短信被识别为正常邮件

它是如何工作的...

逻辑回归分析是一种估计回归函数的方法,该函数最好地将二元属性的概率与一组解释变量联系起来。逻辑攻击是一种非线性回归模型,当因变量是二元的时使用。模型的目标是确定一个观测值可以生成因变量一个或另一个值的概率;它也可以根据其特征将观测值分类为两个类别。

还有更多...

除了因变量的测量尺度外,逻辑回归分析与线性回归分析的区别在于,对于这种分析,假设y服从正态分布,而如果y是二元的,其分布显然是二项分布。同样,在线性回归分析中,从回归得到的y估计值从-∞到+∞变化,而在逻辑回归分析中,y估计值在 0 到 1 之间变化。

参见

第八章:语音识别

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

  • 读取和绘制音频数据

  • 将音频信号转换到频域

  • 使用自定义参数生成音频信号

  • 合成音乐

  • 提取频域特征

  • 构建隐马尔可夫模型HMMs

  • 构建语音识别器

  • 构建一个文本到语音TTS)系统

技术要求

为了处理本章中的食谱,你需要以下文件(可在 GitHub 上找到):

  • read_plot.py

  • input_read.wav

  • freq_transform.py

  • input_freq.wav

  • generate.py

  • synthesize_music.py

  • extract_freq_features.py

  • input_freq.wav

  • speech_recognizer.py

  • tts.py

介绍语音识别

语音识别指的是识别和理解口语的过程。输入以音频数据的形式出现,语音识别器将处理这些数据以从中提取有意义的信息。这有很多实际应用,例如语音控制设备、将口语转录成文字以及安全系统。

语音信号在本质上非常灵活。同一种语言中有许多不同的语音变体。语音有许多不同的元素,如语言、情感、音调、噪声和口音。严格定义一组可以构成语音的规则是困难的。即使有所有这些变化,人类也非常擅长相对轻松地理解所有这些。因此,我们需要机器以同样的方式理解语音。

在过去的几十年里,研究人员一直在研究语音的各个方面,例如识别说话者、理解单词、识别口音和翻译语音。在这些任务中,自动语音识别一直是许多研究人员的焦点。在本章中,我们将学习如何构建一个语音识别器

读取和绘制音频数据

让我们看看如何读取音频文件并可视化信号。这将是一个好的起点,并将使我们更好地理解音频信号的基本结构。在我们开始之前,我们需要理解音频文件是实际音频信号的数字化版本。实际的音频信号是复杂的、连续值的波。为了保存数字版本,我们采样信号并将其转换为数字。例如,语音通常以 44,100 Hz 的频率采样。这意味着信号的每一秒被分解成 44,100 个部分,并且在这些时间戳处的值被存储。换句话说,你每 1/44,100 秒存储一个值。由于采样率很高,当我们通过媒体播放器听信号时,我们会感觉到信号是连续的。

准备工作

在这个食谱中,我们将使用wavfile包从.wav输入文件中读取音频文件。因此,我们将用图表绘制信号。

如何操作...

我们将使用以下步骤使用wavfile包读取和绘制音频:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已提供的read_plot.py文件中):
import numpy as np 
import matplotlib.pyplot as plt 
from scipy.io import wavfile 
  1. 我们将使用wavfile包从已提供的input_read.wav输入文件中读取音频文件:
# Read the input file 
sampling_freq, audio = wavfile.read('input_read.wav') 
  1. 让我们打印出这个信号的参数:
# Print the params 
print('Shape:', audio.shape)
print('Datatype:', audio.dtype)
print('Duration:', round(audio.shape[0] / float(sampling_freq), 3), 'seconds')
  1. 音频信号以 16 位有符号整数数据存储;我们需要归一化这些值:
# Normalize the values 
audio = audio / (2.**15) 
  1. 现在,让我们提取前 30 个值以进行绘图,如下所示:
# Extract first 30 values for plotting 
audio = audio[:30] 
  1. x轴是时间轴。让我们考虑它应该使用采样频率因子进行缩放的事实来构建这个轴:
# Build the time axis 
x_values = np.arange(0, len(audio), 1) / float(sampling_freq) 
  1. 将单位转换为秒,如下所示:
# Convert to seconds 
x_values *= 1000
  1. 现在我们按照以下方式绘制:
# Plotting the chopped audio signal 
plt.plot(x_values, audio, color='black') 
plt.xlabel('Time (ms)') 
plt.ylabel('Amplitude') 
plt.title('Audio signal') 
plt.show() 
  1. 完整的代码在read_plot.py文件中。如果您运行此代码,您将看到以下信号:

您还将在您的终端上看到以下输出打印:

Shape: (132300,)
Datatype: int16
Duration: 3.0 seconds

它是如何工作的...

波形音频文件是不压缩的文件。该格式在 Windows 3.1 中引入,作为多媒体应用中使用的声音的标准格式。其技术规范和描述可以在多媒体编程接口和数据规范 1.0文档中找到(www.aelius.com/njh/wavemetatools/doc/riffmci.pdf)。它基于 1991 年引入的资源交换文件格式RIFF)规范,构成了在 Windows 环境中运行的多媒体文件的元格式。RIFF 结构将数据块组织在称为块段的区域中,每个块段描述 WAV 文件的一个特征(如采样率、比特率和音频通道数),或包含样本的值(在这种情况下,我们指的是块数据)。块是 32 位(有一些例外)。

还有更多...

为了读取 WAV 文件,使用了scipy.io.wavfile.read()函数。此函数从 WAV 文件返回数据以及采样率。返回的采样率是一个 Python 整数,数据以与文件对应的 NumPy 数组形式返回。

参见

将音频信号转换为频域

音频信号由不同频率、幅度和相位的正弦波复杂混合而成。正弦波也被称为正弦波。音频信号的频率内容中隐藏着大量信息。事实上,音频信号在很大程度上由其频率内容来表征。整个语音和音乐的世界都基于这一事实。在继续之前,你需要了解一些关于傅里叶变换的知识。

准备工作

在这个菜谱中,我们将看到如何将音频信号转换到频域。为此,我们使用numpy.fft.fft()函数。此函数使用高效的快速傅里叶变换FFT)算法计算一维n离散傅里叶变换DFT)。

如何操作...

让我们看看如何将音频信号转换到频域:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在提供的freq_transform.py文件中):
import numpy as np 
from scipy.io import wavfile 
import matplotlib.pyplot as plt 
  1. 读取提供的input_freq.wav文件:
# Read the input file 
sampling_freq, audio = wavfile.read('input_freq.wav') 
  1. 按以下方式归一化信号:
# Normalize the values 
audio = audio / (2.**15) 
  1. 音频信号只是一个 NumPy 数组。因此,你可以使用以下代码提取长度:
# Extract length 
len_audio = len(audio)
  1. 让我们应用傅里叶变换。傅里叶变换后的信号沿中心对称,所以我们只需要取变换信号的半个部分。我们的最终目标是提取功率信号,因此我们需要在准备阶段对信号中的值进行平方:
# Apply Fourier transform 
transformed_signal = np.fft.fft(audio) 
half_length = np.ceil((len_audio + 1) / 2.0) 
transformed_signal = abs(transformed_signal[0:int(half_length)]) 
transformed_signal /= float(len_audio) 
transformed_signal **= 2
  1. 按以下方式提取信号的长度:
# Extract length of transformed signal 
len_ts = len(transformed_signal) 
  1. 我们需要根据信号的长度将信号加倍:
# Take care of even/odd cases 
if len_audio % 2: 
    transformed_signal[1:len_ts] *= 2 
else: 
    transformed_signal[1:len_ts-1] *= 2 
  1. 使用以下公式提取功率信号:
# Extract power in dB 
power = 10 * np.log10(transformed_signal) 
  1. x 轴是时间轴;我们需要根据采样频率对其进行缩放,然后将其转换为秒:
# Build the time axis 
x_values = np.arange(0, half_length, 1) * (sampling_freq / len_audio) / 1000.0 
  1. 按以下方式绘制信号:
# Plot the figure 
plt.figure() 
plt.plot(x_values, power, color='black') 
plt.xlabel('Freq (in kHz)') 
plt.ylabel('Power (in dB)') 
plt.show()
  1. 如果你运行这段代码,你将看到以下输出:

它是如何工作的...

声音频谱是声音水平的图形表示,通常以分贝dB)为单位,取决于频率(Hz)。如果要分析的声音是所谓的纯音(随时间恒定的单一频率信号),例如,一个完美的正弦波,信号频谱将在正弦波频率处有一个单一成分,并有一定水平的 dB。在现实中,任何真实信号都由大量幅度随时间连续变化的正弦波成分组成。对于这些信号,由于信号能量中总有难以用正弦波表示的部分,因此无法分析纯音。事实上,根据傅里叶变换定理,将信号表示为正弦谐波成分之和,仅对平稳信号有效,而平稳信号通常并不对应于真实声音。

更多内容...

声音的频率分析基于傅里叶变换定理。也就是说,任何周期性信号都可以通过将具有多个整频的周期性信号的正弦波(称为谐波)相加来生成,这些整频是周期性信号频率的倍数(称为基频)。

参考内容

使用自定义参数生成音频信号

声音是一种特殊的波,其中由振动体(即声源)引起的压力变化在周围介质(通常是空气)中传播。以下是一些声源的例子:

  • 乐器中,振动部分可以是击打的弦(如吉他),或者用弓拉(如小提琴)。

  • 我们的声音是由从肺部出来的空气振动我们的声带而产生的。

  • 任何导致空气移动的现象(如鸟的拍打翅膀、突破音障的飞机、爆炸的炸弹或锤子敲打砧子)都具有适当的物理特性。

为了通过电子设备再现声音,必须将其转换成模拟声音,即由将声波的机械能转换为电能的转换产生的电流。为了能够使用计算机中的声音信号,必须将模拟信号转换成由将模拟声音转换为表示为 0 和 1(比特)流的音频信号。

准备工作

在这个菜谱中,我们将使用 NumPy 生成音频信号。正如我们之前讨论的,音频信号是正弦波的复杂混合。因此,在生成我们自己的音频信号时,我们要牢记这一点。

如何操作...

让我们看看如何使用自定义参数生成音频信号:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码已包含在您提供的generate.py文件中):
import numpy as np 
import matplotlib.pyplot as plt 
from scipy.io.wavfile import write 
  1. 我们需要定义输出文件,生成的音频将存储在该文件中:
# File where the output will be saved 
output_file = 'output_generated.wav' 
  1. 现在让我们指定音频生成参数。我们想要生成一个 3 秒长的信号,采样频率为 44,100Hz,音调频率为 587Hz。时间轴上的值将从-2pi2pi
# Specify audio parameters 
duration = 3  # seconds 
sampling_freq = 44100  # Hz 
tone_freq = 587 
min_val = -2 * np.pi 
max_val = 2 * np.pi 
  1. 让我们生成时间轴和音频信号。音频信号是一个具有之前提到的参数的简单正弦波:
# Generate audio 
t = np.linspace(min_val, max_val, duration * sampling_freq) 
audio = np.sin(2 * np.pi * tone_freq * t)
  1. 现在,让我们向信号添加一些噪声:
# Add some noise 
noise = 0.4 * np.random.rand(duration * sampling_freq) 
audio += noise
  1. 在存储之前,我们需要将值缩放到 16 位整数:
# Scale it to 16-bit integer values 
scaling_factor = pow(2,15) - 1 
audio_normalized = audio / np.max(np.abs(audio)) 
audio_scaled = np.int16(audio_normalized * scaling_factor)
  1. 将此信号写入输出文件:
# Write to output file 
write(output_file, sampling_freq, audio_scaled) 
  1. 使用前 100 个值绘制信号:
# Extract first 100 values for plotting 
audio = audio[:100] 
  1. 按如下方式生成时间轴:
# Build the time axis 
x_values = np.arange(0, len(audio), 1) / float(sampling_freq) 
  1. 将时间轴转换为秒:
# Convert to seconds 
x_values *= 1000 
  1. 按如下方式绘制信号:
# Plotting the chopped audio signal 
plt.plot(x_values, audio, color='black') 
plt.xlabel('Time (ms)') 
plt.ylabel('Amplitude') 
plt.title('Audio signal') 
plt.show()
  1. 如果您运行此代码,您将得到以下输出:

它是如何工作的...

在这个菜谱中,我们使用了 NumPy 库来生成音频信号。我们已经看到数字声音是一系列数字,因此生成声音只需要构建一个表示音乐音调的数组。首先,我们将文件名设置为输出保存的位置。然后,我们指定了音频参数。因此,我们使用正弦波生成了音频。然后我们添加了一些噪声,所以我们将它们缩放到 16 位整数值。最后,我们将信号写入输出文件。

更多...

在信号的编码中,分配给单个样本的每个值都用位表示。每个位对应于 6 dB 的动态范围。使用的位数越多,单个样本可以表示的 dB 范围就越高。

一些典型值如下:

  • 每个样本 8 位对应于 256 个级别。

  • 每个样本 16 位(CD 使用的数量)对应于 65,536 个级别。

参见

音乐合成

在传统乐器中,声音是通过机械部件的振动产生的。在合成乐器中,振动是通过时间函数描述的,称为信号,它们表达了声压随时间的变化。声音合成是一个允许您人工生成声音的过程。确定声音音色的参数根据所使用的合成类型而有所不同,可以直接由作曲家提供,或者通过适当的输入设备上的操作,或者从现有声音的分析中得出。

准备工作

在这个菜谱中,我们将看到如何合成一些音乐。为此,我们将使用各种音符,如AGD,以及它们相应的频率,来生成一些简单的音乐。

如何做到这一点...

让我们看看如何合成一些音乐:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码已包含在您提供的synthesize_music.py文件中):
import json 
import numpy as np 
from scipy.io.wavfile import write 
  1. 定义一个基于输入参数(如幅度和频率)合成音调的函数:
# Synthesize tone 
def synthesizer(freq, duration, amp=1.0, sampling_freq=44100):
  1. 构建时间轴值:
    # Build the time axis 
    t = np.linspace(0, duration, round(duration * sampling_freq)) 
  1. 使用输入参数(如幅度和频率)构建音频样本:
    # Construct the audio signal 
    audio = amp * np.sin(2 * np.pi * freq * t) 

    return audio.astype(np.int16)  
  1. 让我们定义主函数。您已经提供了一个名为tone_freq_map.json的 JSON 文件,其中包含一些音符及其频率:
if __name__=='__main__': 
    tone_map_file = 'tone_freq_map.json' 
  1. 按照以下方式加载该文件:
    # Read the frequency map 
    with open(tone_map_file, 'r') as f: 
        tone_freq_map = json.loads(f.read()) 
  1. 现在,假设我们想要生成一个持续两秒的G音符:
    # Set input parameters to generate 'G' tone 
    input_tone = 'G' 
    duration = 2     # seconds 
    amplitude = 10000 
    sampling_freq = 44100    # Hz 
  1. 使用以下参数调用该函数:
    # Generate the tone 
    synthesized_tone = synthesizer(tone_freq_map[input_tone], duration, amplitude, sampling_freq) 
  1. 将生成的信号写入输出文件,如下所示:
    # Write to the output file 
    write('output_tone.wav', sampling_freq, synthesized_tone)

生成单个音调的.wav文件(output_tone.wav)。在媒体播放器中打开此文件并聆听。这就是G音符!

  1. 现在,让我们做一些更有趣的事情。让我们按顺序生成一些音符,以赋予它音乐感。定义一个音符序列及其持续时间(以秒为单位):
    # Tone-duration sequence 
    tone_seq = [('D', 0.3), ('G', 0.6), ('C', 0.5), ('A', 0.3), ('Asharp', 0.7)] 
  1. 遍历此列表并对每个元素调用合成器函数:
    # Construct the audio signal based on the chord sequence 
    output = np.array([]) 
    for item in tone_seq: 
        input_tone = item[0] 
        duration = item[1] 
        synthesized_tone = synthesizer(tone_freq_map[input_tone], duration, amplitude, sampling_freq) 
        output = np.append(output, synthesized_tone, axis=0) 
    output = output.astype(np.int16)
  1. 将信号写入输出文件:
    # Write to the output file 
    write('output_tone_seq.wav', sampling_freq, output) 
  1. 您现在可以在媒体播放器中打开output_tone_seq.wav文件并聆听。您可以感受到音乐的魅力!

它是如何工作的...

音乐是一种难以简而言之的独创性和创造性的作品。音乐家阅读乐谱时,会识别出音符在乐谱上的位置。通过类比,我们可以将声音的合成视为已知音符特征频率的序列。在这个配方中,我们已经使用这个程序来合成一系列音符。

更多内容...

要人工生成音乐,使用合成器。所有合成器都有以下基本组件,它们协同工作以产生声音:

  • 生成波形并改变音调的振荡器

  • 一个滤波器用于在波形中去除某些频率以改变音色

  • 一个放大器用于控制信号的音量

  • 一个调制器用于创建效果

参考以下内容

提取频域特征

将音频信号转换为频域的配方中,我们讨论了如何将信号转换为频域。在大多数现代语音识别系统中,人们使用频域特征。在将信号转换为频域后,你需要将其转换为可用的形式。梅尔频率倒谱系数MFCC)是这样做的好方法。MFCC 取信号的功率谱,然后使用滤波器组和离散余弦变换DCT)的组合来提取特征。

准备中

在这个配方中,我们将看到如何使用python_speech_features包来提取频域特征。你可以在python-speech-features.readthedocs.org/en/latest找到安装说明。所以,让我们看看如何提取 MFCC 特征。

如何操作...

让我们看看如何提取频域特征:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在提供的extract_freq_features.py文件中,已经为你准备好了):
import numpy as np 
import matplotlib.pyplot as plt 
from scipy.io import wavfile  
from python_speech_features import mfcc, logfbank 
  1. 读取提供的input_freq.wav输入文件:
# Read input sound file 
sampling_freq, audio = wavfile.read("input_freq.wav") 
  1. 按如下方式提取 MFCC 和滤波器组特征:
# Extract MFCC and Filter bank features 
mfcc_features = mfcc(audio, sampling_freq) 
filterbank_features = logfbank(audio, sampling_freq) 
  1. 打印参数以查看生成了多少个窗口:
# Print parameters 
print('MFCC:\nNumber of windows =', mfcc_features.shape[0])
print('Length of each feature =', mfcc_features.shape[1])
print('\nFilter bank:\nNumber of windows =', filterbank_features.shape[0])
print('Length of each feature =', filterbank_features.shape[1])
  1. 现在,让我们可视化 MFCC 特征。我们需要转换矩阵,以便时域是水平的:
# Plot the features 
mfcc_features = mfcc_features.T 
plt.matshow(mfcc_features) 
plt.title('MFCC') 
  1. 现在,让我们可视化滤波器组特征。同样,我们需要转换矩阵,以便时域是水平的:
filterbank_features = filterbank_features.T 
plt.matshow(filterbank_features) 
plt.title('Filter bank') 
plt.show() 
  1. 如果你运行此代码,你将在以下输出中看到 MFCC 特征:

图片

滤波器组特征将如下所示:

图片

你将在你的终端上看到以下输出:

MFCC:
Number of windows = 40
Length of each feature = 13

Filter bank:
Number of windows = 40
Length of each feature = 26

它是如何工作的...

倒谱是对信号的 dB 频谱应用傅里叶变换的结果。其名称来源于单词spectrum的前四个字母的倒置。它由 Bogert 等人于 1963 年定义。因此,信号的倒谱是信号傅里叶变换的对数值的傅里叶变换。

脉冲谱图用于分析信号频谱内容的变化率。最初,它是为了分析地震、爆炸和对雷达信号的响应而发明的。目前,它是音乐信息学中区分人声的非常有效的工具。对于这些应用,频谱首先通过梅尔尺度上的频带来转换。结果是梅尔频谱系数,或 MFCC。它用于语音识别和音高检测算法。

还有更多...

频谱分析用于将包含激励信息的信号部分与喉部执行的传递函数分离。提升作用(频域中的滤波)的目标是将激励信号从传递函数中分离出来。

参见

构建 HMM

现在,我们已经准备好讨论语音识别了。我们将使用 HMM 进行语音识别;HMM 在建模时间序列数据方面非常出色。由于音频信号是一种时间序列信号,HMM 非常适合我们的需求。HMM 是一个表示观察序列概率分布的模型。我们假设输出是由隐藏状态生成的。因此,我们的目标是找到这些隐藏状态,以便我们可以建模信号。

准备工作

在这个菜谱中,我们将看到如何使用hmmlearn包构建一个 HMM。在继续之前,您需要安装hmmlearn包。让我们看看如何构建 HMM。

如何做到这一点...

让我们看看如何构建 HMM:

  1. 创建一个新的 Python 文件并定义一个类来建模 HMM(完整的代码在您已经提供的speech_recognizer.py文件中):
# Class to handle all HMM related processing 
class HMMTrainer(object): 
  1. 让我们初始化类;我们将使用高斯 HMM 来建模我们的数据。n_components参数定义了隐藏状态的数量。cov_type定义了传递矩阵中的协方差类型,而n_iter表示在停止训练之前它将进行的迭代次数:
    def __init__(self, model_name='GaussianHMM', n_components=4, cov_type='diag', n_iter=1000): 

前述参数的选择取决于具体问题。您需要了解您的数据,以便以明智的方式选择这些参数。

  1. 初始化变量,如下所示:
        self.model_name = model_name 
        self.n_components = n_components 
        self.cov_type = cov_type 
        self.n_iter = n_iter 
        self.models = [] 
  1. 使用以下参数定义模型:
        if self.model_name == 'GaussianHMM': 
            self.model = hmm.GaussianHMM(n_components=self.n_components,  
                    covariance_type=self.cov_type, n_iter=self.n_iter) 
        else: 
            raise TypeError('Invalid model type') 
  1. 输入数据是一个 NumPy 数组,其中每个元素都是一个由k个维度组成的特征向量:
    # X is a 2D numpy array where each row is 13D 
    def train(self, X): 
        np.seterr(all='ignore') 
        self.models.append(self.model.fit(X))
  1. 定义一个基于模型提取分数的方法:
    # Run the model on input data 
    def get_score(self, input_data): 
        return self.model.score(input_data) 
  1. 我们构建了一个类来处理 HMM 训练和预测,但我们需要一些数据来看到它的实际应用。我们将在下一个菜谱中使用它来构建一个语音识别器。

它是如何工作的...

HMM 是一个模型,其中系统被假定为具有不可观察状态的马尔可夫过程。一个随机过程被称为马尔可夫过程,当选择一个特定的t实例进行观察时,该过程的演变,从t开始,只依赖于t,而不依赖于任何以前的实例。因此,一个过程是马尔可夫的,当给定观察时刻时,只有特定的实例决定了过程的未来演变,而这种演变不依赖于过去。

还有更多...

因此,HMM 是一种马尔可夫链,其中状态是不可直接观察的。更确切地说,可以理解为以下内容:

  • 该链有多个状态

  • 状态根据马尔可夫链演变

  • 每个状态都根据一定的概率分布生成一个事件,这个概率分布只依赖于状态

  • 事件是可观察的,但状态是不可观察的

HMMs 特别以其在语音演讲、手写、纹理识别和生物信息学中识别时间模式的应用而闻名。

参考以下内容

构建语音识别器

语音识别是通过计算机识别人类口语的过程,随后通过计算机或更具体地说,通过专门的语音识别系统进行处理。语音识别系统用于电话应用(如自动呼叫中心)的自动语音应用、允许将演讲口述到计算机的听写系统、导航系统卫星的控制系统或汽车的语音命令电话。

准备工作

我们需要一个语音文件数据库来构建我们的语音识别器。我们将使用在code.google.com/archive/p/hmm-speech-recognition/downloads可用的数据库。该数据库包含 7 个不同的单词,每个单词都与 15 个音频文件相关联。下载 ZIP 文件并解压包含 Python 文件的文件夹(将包含数据的文件夹重命名为data)。这是一个小型数据集,但足以理解如何构建一个可以识别 7 个不同单词的语音识别器。我们需要为每个类别构建一个 HMM 模型。当我们想要识别新输入文件中的单词时,我们需要在该文件上运行所有模型并选择得分最高的模型。我们将使用我们在前面的配方中构建的 HMM 类。

如何做到这一点...

让我们看看如何构建一个语音识别器:

  1. 创建一个新的 Python 文件并导入以下包(完整代码已在提供的speech_recognizer.py文件中):(完整代码位于提供的speech_recognizer.py文件中)
import os 
import argparse  

import numpy as np 
from scipy.io import wavfile  
from hmmlearn import hmm 
from python_speech_features import mfcc 
  1. 定义一个函数来解析命令行中的输入参数:
# Function to parse input arguments 
def build_arg_parser():
    parser = argparse.ArgumentParser(description='Trains the HMM classifier')
    parser.add_argument("--input-folder", dest="input_folder", required=True,
            help="Input folder containing the audio files in subfolders")
    return parser
  1. 让我们使用在之前的构建 HMM菜谱中定义的HMMTrainer类:
class HMMTrainer(object):
 def __init__(self, model_name='GaussianHMM', n_components=4, cov_type='diag', n_iter=1000):
 self.model_name = model_name
 self.n_components = n_components
 self.cov_type = cov_type
 self.n_iter = n_iter
 self.models = []

if self.model_name == 'GaussianHMM':
 self.model = hmm.GaussianHMM(n_components=self.n_components, 
 covariance_type=self.cov_type, n_iter=self.n_iter)
 else:
 raise TypeError('Invalid model type')

# X is a 2D numpy array where each row is 13D
 def train(self, X):
 np.seterr(all='ignore')
 self.models.append(self.model.fit(X))

# Run the model on input data
 def get_score(self, input_data):
 return self.model.score(input_data)
  1. 定义主函数,并解析输入参数:
if __name__=='__main__': 
    args = build_arg_parser().parse_args() 
    input_folder = args.input_folder 
  1. 初始化将保存所有 HMM 模型的变量:
    hmm_models = [] 
  1. 解析包含所有数据库音频文件的输入目录:
    # Parse the input directory 
    for dirname in os.listdir(input_folder):
  1. 提取子文件夹的名称:
        # Get the name of the subfolder  
        subfolder = os.path.join(input_folder, dirname) 

        if not os.path.isdir(subfolder):  
            continue 
  1. 子文件夹的名称是此类别的标签;使用以下代码提取它:
        # Extract the label 
        label = subfolder[subfolder.rfind('/') + 1:] 
  1. 初始化训练变量:
        # Initialize variables 
        X = np.array([]) 
        y_words = [] 
  1. 遍历每个子文件夹中的音频文件列表:
        # Iterate through the audio files (leaving 1 file for testing in each class) 
        for filename in [x for x in os.listdir(subfolder) if x.endswith('.wav')][:-1]: 
  1. 按照以下方式读取每个音频文件:
            # Read the input file 
            filepath = os.path.join(subfolder, filename) 
            sampling_freq, audio = wavfile.read(filepath) 
  1. 按照以下方式提取 MFCC 特征:
            # Extract MFCC features 
            mfcc_features = mfcc(audio, sampling_freq) 
  1. 按照以下方式将此内容追加到X变量中:
            # Append to the variable X 
            if len(X) == 0: 
                X = mfcc_features 
            else: 
                X = np.append(X, mfcc_features, axis=0)
  1. 按照以下方式附加相应的标签:
            # Append the label 
            y_words.append(label)    
  1. 一旦从当前类中的所有文件中提取了特征,就训练并保存 HMM 模型。由于 HMM 是无监督学习的一个生成模型,我们不需要为每个类别构建 HMM 模型。我们明确假设将为每个类别构建单独的 HMM 模型:
        # Train and save HMM model 
        hmm_trainer = HMMTrainer() 
        hmm_trainer.train(X) 
        hmm_models.append((hmm_trainer, label)) 
        hmm_trainer = None 
  1. 获取未用于训练的测试文件列表:
    # Test files 
    input_files = [ 
            'data/pineapple/pineapple15.wav', 
            'data/orange/orange15.wav', 
            'data/apple/apple15.wav', 
            'data/kiwi/kiwi15.wav' 
            ] 
  1. 按照以下方式解析输入文件:
    # Classify input data 
    for input_file in input_files: 
  1. 按照以下方式读取每个音频文件:
        # Read input file 
        sampling_freq, audio = wavfile.read(input_file) 
  1. 按照以下方式提取 MFCC 特征:
        # Extract MFCC features 
        mfcc_features = mfcc(audio, sampling_freq) 
  1. 定义存储最大分数和输出标签的变量:
        # Define variables 
        max_score = float('-inf')
        output_label = None
  1. 遍历所有模型并将输入文件通过每个模型运行:
        # Iterate through all HMM models and pick  
        # the one with the highest score 
        for item in hmm_models: 
            hmm_model, label = item 
  1. 提取分数并存储最大分数:
            score = hmm_model.get_score(mfcc_features) 
            if score > max_score: 
                max_score = score 
                output_label = label 
  1. 打印真实标签和预测标签:
         # Print the output
        print("True:", input_file[input_file.find('/')+1:input_file.rfind('/')])
        print("Predicted:", output_label)
  1. 完整代码在speech_recognizer.py文件中。使用以下命令运行此文件:
$ python speech_recognizer.py --input-folder data

以下结果将在您的终端返回:

True: pineapple
Predicted: data\pineapple
True: orange
Predicted: data\orange
True: apple
Predicted: data\apple
True: kiwi
Predicted: data\kiwi

工作原理...

在这个菜谱中,我们使用 HMM 创建了一个语音识别系统。为此,我们首先创建了一个分析输入参数的函数。然后,使用一个类来处理所有与 HMM 相关的处理。因此,我们已对输入数据进行分类,并预测了测试数据的标签。最后,我们打印了结果。

还有更多...

语音识别系统基于对适当处理的输入音频与系统训练期间创建的数据库的比较。在实践中,软件应用程序试图识别说话者所说的单词,在数据库中寻找相似的声音,并检查哪个单词对应。这自然是一个非常复杂的操作。此外,它不是在整词上进行的,而是在构成它们的音素上进行的。

参见

构建语音合成系统

语音合成是用于人工再现人类声音的技术。用于此目的的系统称为语音合成器,可以通过软件或硬件实现。由于它们能够将文本转换为语音,语音合成系统也被称为 TTS 系统。还有一些系统可以将音标转换为语音。

语音合成可以通过连接存储在数据库中的声音录音来实现。不同的语音合成系统根据存储的语音样本的大小而有所不同。也就是说,存储单个音素或双音素的系统可以在牺牲整体清晰度的前提下获得最大数量的组合,而其他为特定用途设计的系统会重复自己,记录整个单词或整个句子,以达到高质量的结果。

合成器可以使用声音特性和其他人类特征创建一个完全合成的声音。语音合成器的质量是根据其与人类声音的相似程度和可理解性水平来评估的。性能良好的语音转换程序在可访问性方面可以发挥重要作用;例如,允许视力受损或阅读障碍的人收听计算机上编写的文档。对于这类应用(自 1980 年代初以来),许多操作系统都包括了语音合成功能。

准备工作

在这个菜谱中,我们将介绍一个 Python 库,它允许我们创建语音合成系统。我们将运行pyttsx跨平台语音合成包装库。

如何操作...

让我们看看如何构建一个语音合成系统:

  1. 首先,我们必须为 Python 3 库安装pyttsx(Python 3 的离线 TTS)及其相关依赖项:
$ pip install pyttsx3
  1. 为了避免可能的错误,还需要安装pypiwin32库:
$ pip install pypiwin32
  1. 创建一个新的 Python 文件并导入pyttsx3包(完整的代码已包含在您提供的tts.py文件中):
import pyttsx3;
  1. 我们创建一个将使用指定驱动程序的引擎实例:
engine = pyttsx3.init();
  1. 要更改语速,请使用以下命令:
rate = engine.getProperty('rate')
engine.setProperty('rate', rate-50)
  1. 要更改说话者的声音,请使用以下命令:
voices = engine.getProperty('voices')
engine.setProperty('voice', 'TTS_MS_EN-US_ZIRA_11.0')
  1. 现在,我们将使用say方法来排队一个命令,以朗读一个语音。语音输出将根据队列中此命令之前设置的属性:
engine.say("You are reading the Python Machine Learning Cookbook");
engine.say("I hope you like it.");
  1. 最后,我们将调用runAndWait()方法。此方法在处理所有当前排队的命令时将阻塞,并适当地调用引擎通知的回调。当在此调用之前排队的所有命令从队列中清空时,它将返回:
engine.runAndWait();

在这个阶段,将会有不同的声音来朗读我们提供的文本。

它是如何工作的...

语音合成系统或引擎由两部分组成:前端和后端。前端部分负责将文本转换为音标符号,而后端部分则解释这些音标符号并将它们朗读出来,从而将它们转换成人工语音。前端有两个关键功能;首先,它对书面文本进行分析,将所有数字、缩写和缩写词转换成全词。这一预处理步骤被称为分词。第二个功能包括将每个单词转换成其对应的音标符号,并对修改后的文本进行语言分析,将其细分为韵律单位,即介词、句子和句号。将音标转录分配给单词的过程被称为从文本到音素的转换,或从图形到音素的转换。

更多内容...

经典 TTS 系统的演变被称为WaveNet,它似乎知道如何说话,能够准确发音,并能流畅地朗读整个句子。WaveNet 是一个生成原始音频的深度神经网络。它是由位于伦敦的人工智能公司 DeepMind 的研究人员创建的。WaveNet 使用一个深度生成模型来模拟声波,可以模仿任何人类的语音。WaveNet 朗读的句子听起来比更先进的 TTS 更接近人类语音,相似度提高了 50%。为了证明这一点,创建了英语和普通话的样本,并使用平均意见评分MOS)系统,这是音频评估的标准,将人工智能生成的样本与正常 TTS、参数化 TTS 以及真实语音的样本进行了比较。

参考以下内容

第九章:解构时间序列和顺序数据

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

  • 将数据转换为时间序列格式

  • 切分时间序列数据

  • 对时间序列数据进行操作

  • 从时间序列数据中提取统计数据

  • 为顺序数据构建 HMM

  • 为顺序文本数据构建 CRF

  • 分析股票市场数据

  • 使用 RNN 预测时间序列数据

技术要求

为了处理本章中的食谱,你需要以下文件(可在 GitHub 上找到):

  • convert_to_timeseries.py

  • data_timeseries.txt

  • slicing_data.py

  • operating_on_data.py

  • extract_stats.py

  • hmm.py

  • `data_hmm.txt`

  • crf.py

  • AmazonStock.py

  • AMZN.csv

  • LSTMstock.py

介绍时间序列

时间序列数据基本上是一系列随时间收集的测量值。这些测量值是在预定变量和固定时间间隔下进行的。时间序列数据的一个主要特征是顺序很重要!

我们收集的观察结果列表按时间线排序,它们出现的顺序在很大程度上揭示了潜在的规律。如果你改变顺序,这将会完全改变数据的意义。顺序数据是一个广义的概念,包括任何以顺序形式出现的数据,包括时间序列数据。

我们的目标是构建一个描述时间序列或任何一般序列模式的模型。这些模型用于描述时间序列模式的重要特征。我们可以使用这些模型来解释过去如何影响未来。我们还可以使用它们来查看两个数据集如何相关联,预测未来的值,或者控制基于某些指标的给定变量。

为了可视化时间序列数据,我们倾向于使用折线图或条形图来绘制。时间序列数据分析常用于金融、信号处理、天气预报、轨迹预测、预测地震或任何需要处理时间数据的领域。我们在时间序列和顺序数据分析中构建的模型应考虑数据的顺序并提取邻居之间的关系。让我们继续查看一些食谱,以分析 Python 中的时间序列和顺序数据。

将数据转换为时间序列格式

时间序列构成了一系列现象的观察结果,这些观察是在连续的瞬间或时间间隔内进行的,通常(即使不是必然的)是均匀分布或长度相同。因此,时间是时间序列分析中的一个基本参数。因此,我们首先必须对代表某种现象长期观察的数据进行操作有一定的信心。

准备工作

我们将首先了解如何将一系列观察结果转换为时间序列数据并可视化它。我们将使用一个名为pandas的库来分析时间序列数据。在继续之前,请确保你已经安装了pandas。你可以在以下链接找到pandas的安装说明:pandas.pydata.org/pandas-docs/stable/install.html

如何操作...

让我们看看如何将数据转换为时间序列格式:

  1. 创建一个新的 Python 文件(完整的代码在提供的convert_to_timeseries.py文件中),并导入以下包:
import numpy as np 
import pandas as pd 
import matplotlib.pyplot as plt 
  1. 让我们定义一个函数,该函数读取输入文件并将顺序观察结果转换为时间索引数据:
def convert_data_to_timeseries(input_file, column, verbose=False): 
  1. 我们将使用一个包含四个列的文本文件。第一列表示年份,第二列表示月份,第三和第四列表示数据。让我们将其加载到一个 NumPy 数组中:
    # Load the input file 
    data = np.loadtxt(input_file, delimiter=',') 
  1. 由于这是按时间顺序排列的,第一行包含开始日期,最后一行包含结束日期。让我们提取这个数据集的开始和结束日期:
    # Extract the start and end dates 
    start_date = str(int(data[0,0])) + '-' + str(int(data[0,1])) 
    end_date = str(int(data[-1,0] + 1)) + '-' + str(int(data[-1,1] % 12 + 1)) 
  1. 此函数还有一个verbose模式。因此,如果将其设置为true,它将打印一些信息。让我们打印出开始和结束日期:
    if verbose: 
        print("Start date =", start_date)
        print("End date =", end_date) 
  1. 让我们创建一个包含每月间隔日期序列的pandas变量:
    # Create a date sequence with monthly intervals 
    dates = pd.date_range(start_date, end_date, freq='M') 
  1. 我们的下一步是将给定的列转换为时间序列数据。你可以使用月份和年份(而不是索引)来访问此数据:
    # Convert the data into time series data 
    data_timeseries = pd.Series(data[:,column], index=dates) 
  1. 使用verbose模式打印出前 10 个元素:
    if verbose: 
        print("Time series data:\n", data_timeseries[:10]) 
  1. 返回时间索引变量,如下所示:
    return data_timeseries 
  1. 定义主函数,如下所示:
if __name__=='__main__': 
  1. 我们将使用已经提供给你的data_timeseries.txt文件:
    # Input file containing data 
    input_file = 'data_timeseries.txt' 
  1. 从这个文本文件中加载第三列并将其转换为时间序列数据:
    # Load input data 
    column_num = 2 
    data_timeseries = convert_data_to_timeseries(input_file, column_num) 
  1. pandas库提供了一个很好的绘图函数,可以直接在变量上运行:
    # Plot the time series data 
    data_timeseries.plot() 
    plt.title('Input data') 

    plt.show() 

如果你运行代码,你将看到以下输出:

图片

工作原理...

在这个菜谱中,我们学习了如何将一系列观察结果转换为时间序列数据并显示它。为此,我们首先以.txt格式加载了输入文件,因此我们提取了开始和结束日期。然后,我们创建了一个按月间隔的日期序列,并将数据转换为时间序列数据。最后,我们绘制了时间序列数据。

更多内容...

pandas库特别适合处理所有领域的时间序列数据,这得益于其广泛的特性和功能。这些特性利用了 NumPy 的datetime64timedelta64变量,以及来自其他 Python 库(如scikits.timeseries)的大量功能。这些特性使得pandas在处理时间序列数据时特别高效。

相关内容

切片时间序列数据

切片切块是两个指代数据集的术语,意味着将大的 DataFrame 划分成更小的部分或从不同的角度检查它们以更好地理解。这个术语来自烹饪术语,描述了每个厨师都必须掌握的两种刀工。切片意味着切割,而切块意味着将食物切成非常小且均匀的部分,这两个动作通常按顺序执行。在数据分析中,切片和切块术语通常涉及系统地减少大型数据集以提取更多信息。

准备工作

在这个菜谱中,我们将学习如何切片时间序列数据。这将帮助你从时间序列数据的不同区间中提取信息。我们将学习如何使用日期来处理我们数据的子集。

如何操作...

让我们看看如何执行切片时间序列数据:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的 slicing_data.py 文件中给出):
import numpy as np 
from convert_to_timeseries import convert_data_to_timeseries 

在这里,convert_to_timeseries 是我们在前一个菜谱 将数据转换成时间序列格式 中定义的函数,它读取输入文件并将顺序观测值转换为时间索引数据。

  1. 我们将使用与前一个菜谱中相同的文本文件(data_timeseries.txt)来切片和切块数据:
# Input file containing data 
input_file = 'data_timeseries.txt' 
  1. 我们将只提取第三列:
# Load data 
column_num = 2 
data_timeseries = convert_data_to_timeseries(input_file, column_num) 
  1. 假设我们想要提取给定 startend 年份之间的数据。让我们定义如下:
# Plot within a certain year range 
start = '2000' 
end = '2015' 
  1. 在给定的年份范围内绘制数据:
plt.figure() 
data_timeseries[start:end].plot() 
plt.title('Data from ' + start + ' to ' + end) 
  1. 我们也可以根据一定的月份范围来切片数据:
# Plot within a certain range of dates 
start = '2008-1' 
end = '2008-12' 
  1. 按以下方式绘制数据:
plt.figure() 
data_timeseries[start:end].plot() 
plt.title('Data from ' + start + ' to ' + end) 
plt.show() 

如果你运行代码,你会看到以下截图:

以下截图显示了一个较小的时间框架;因此,它看起来像是我们将其放大了:

工作原理...

在这个菜谱中,我们学习了如何分解时间序列数据。首先,我们导入了包含在 .txt 文件中的数据。这些数据使用我们在前一个菜谱中定义的函数转换成了时间序列格式。因此,我们首先在一定的年份范围内绘制了数据,然后在一定的日期范围内绘制了数据。

更多内容...

为了将数据转换成时间序列格式,我们使用了 pandas 库。这个库在处理时间序列数据方面特别高效。

相关内容

对时间序列数据进行操作

现在我们知道了如何切片数据和提取各种子集,让我们讨论如何对时间序列数据进行操作。您可以通过许多不同的方式过滤数据。pandas 库允许您以任何您想要的方式对时间序列数据进行操作。

准备工作

在这个菜谱中,我们将使用 .txt 文件中的数据并加载它。然后,我们将使用某个阈值过滤数据,以提取仅满足特定要求的起始数据集的一部分。

如何操作...

让我们看看我们如何对时间序列数据进行操作:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的 operating_on_data.py 文件中给出):
import pandas as pd 
import matplotlib.pyplot as plt 
from convert_to_timeseries import convert_data_to_timeseries 

在这里,convert_to_timeseries 是我们在上一个菜谱中定义的函数,将数据转换为时间序列格式,该函数读取输入文件并将顺序观测值转换为时间索引数据。

  1. 我们将使用与之前菜谱中相同的文本文件(记住,Python 从位置 0 开始列出数据,因此第三列和第四列的索引是 2 和 3):
# Input file containing data 
input_file = 'data_timeseries.txt' 
  1. 在这个 .txt 文件中,我们将使用第三列和第四列(记住,Python 从位置 0 开始列出数据,所以第三列和第四列的索引是 2 和 3):
# Load data 
data1 = convert_data_to_timeseries(input_file, 2) 
data2 = convert_data_to_timeseries(input_file, 3) 
  1. 将数据转换为 pandas DataFrame:
dataframe = pd.DataFrame({'first': data1, 'second': data2}) 
  1. 在给定年份范围内绘制数据:
# Plot data 
dataframe['1952':'1955'].plot() 
plt.title('Data overlapped on top of each other') 
  1. 假设我们想绘制给定年份范围内两列之间的差异。我们可以使用以下行来完成此操作:
# Plot the difference 
plt.figure() 
difference = dataframe['1952':'1955']['first'] - dataframe['1952':'1955']['second'] 
difference.plot() 
plt.title('Difference (first - second)') 
  1. 如果我们想根据第一列和第二列的不同条件过滤数据,我们只需指定这些条件并绘制如下:
# When 'first' is greater than a certain threshold 
# and 'second' is smaller than a certain threshold 
dataframe[(dataframe['first'] > 60) & (dataframe['second'] < 20)].plot(style='o') 
plt.title('first > 60 and second < 20') 

plt.show() 

如果您运行前面的代码,第一个输出将如下所示:

图片

第二张输出截图表示差异,如下所示:

图片

第三张输出截图表示过滤后的数据,如下所示:

图片

工作原理...

在这个菜谱中,我们学习了如何过滤时间序列中的数据。首先,我们在两个年份之间(从 1952 年到 1955 年)绘制了数据。然后,我们在特定时间间隔内(从 1952 年到 1955 年)绘制了两个列中包含的数据之间的差异。最后,我们使用某个阈值绘制数据,以提取仅满足特定要求的起始数据集的一部分——特别是当第一列大于 60 且第二列小于 20 时。

还有更多...

要同时进行两列过滤,使用了&运算符。&(与)运算符是两个命题之间的逻辑运算符(布尔运算符),表示逻辑合取。给定两个命题,A 和 B,逻辑合取确定第三个命题,C,只有当两个命题都为真时,该命题才为真。

参见

从时间序列数据中提取统计数据

我们想要分析时间序列数据的主要原因是从中提取有趣的统计数据。这提供了有关数据性质的大量信息。

准备工作

在本食谱中,我们将探讨如何提取一些统计数据。

如何操作...

让我们看看如何从时间序列数据中提取统计数据:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的extract_stats.py文件中给出):
import pandas as pd 
import matplotlib.pyplot as plt 
from convert_to_timeseries import convert_data_to_timeseries 

convert_to_timeseries函数是我们之前在将数据转换为时间序列格式食谱中定义的函数,它读取输入文件并将顺序观测值转换为时间索引数据。

  1. 我们将使用之前食谱中用于分析的相同文本文件(data_timeseries.txt):
# Input file containing data 
input_file = 'data_timeseries.txt' 
  1. 加载数据的列(第三和第四列):
# Load data 
data1 = convert_data_to_timeseries(input_file, 2) 
data2 = convert_data_to_timeseries(input_file, 3) 
  1. 创建一个pandas数据结构来存储这些数据。这个 DataFrame 就像一个有键和值的字典:
dataframe = pd.DataFrame({'first': data1, 'second': data2}) 
  1. 现在让我们开始提取一些统计数据。要提取最大值和最小值,请使用以下代码:
# Print max and min 
print('Maximum:\n', dataframe.max())
print('Minimum:\n', dataframe.min())
  1. 要打印数据的平均值或仅打印行平均值,请使用以下代码:
# Print mean 
print('Mean:\n', dataframe.mean())
print('Mean row-wise:\n', dataframe.mean(1)[:10])
  1. 滚动平均值是时间序列处理中常用的重要统计量。最著名的应用之一是平滑信号以去除噪声。“滚动平均值”指的是在时间尺度上滑动窗口中信号的均值计算。让我们考虑窗口大小为24并绘制如下:
# Plot rolling mean 
DFMean = dataframe.rolling(window=24).mean()
plt.plot(DFMean) 
  1. 相关系数在理解数据性质方面很有用,如下所示:
# Print correlation coefficients 
print('Correlation coefficients:\n', dataframe.corr()) 
  1. 让我们使用窗口大小为60来绘制这个:
# Plot rolling correlation 
plt.figure()
DFCorr= dataframe.rolling(window=60).corr(pairwise=False)
plt.plot(DFCorr)
plt.show()

如果运行前面的代码,滚动平均值将如下所示:

第二个输出指示滚动相关性(以下输出是在matplotlib窗口中执行缩放矩形操作的结果):

  1. 在终端的上半部分,您将看到打印出的最大值、最小值和平均值,如下所示:

  1. 在终端的下半部分,您将看到打印出行平均值统计和相关性系数,如下所示:

工作原理...

在这个菜谱中,我们学习了如何提取一些统计数据。我们首先计算了从数据集中提取的两个列的最小值、最大值和平均值。然后,我们计算了 DataFrame 前 10 行的每一行的平均值。最后,我们对两个特征进行了相关性分析。

更多内容...

为了进行相关性分析,使用了pandas.DataFrame.corr函数。此函数计算列之间的成对相关性,排除 N/A 或空值。以下方法可用:

  • pearson:这是标准的相关系数

  • kendall:这是肯德尔 tau相关系数

  • spearman:这是斯皮尔曼秩相关系数

相关内容

为序列数据构建 HMMs

隐藏马尔可夫模型HMMs)特别适合于序列数据分析问题。它们在语音分析、金融、词序列、天气预报等领域得到广泛应用。

任何产生输出序列的数据源都可以产生模式。请注意,HMMs 是生成模型,这意味着一旦它们学会了底层结构,它们就可以生成数据。HMMs 在其基本形式中不能区分类别。这与可以学习区分类别但不能生成数据的判别模型形成对比。

准备工作

假设我们想要预测明天的天气是晴朗、寒冷还是雨天。为此,我们查看所有参数,如温度、压力等,而底层状态是隐藏的。在这里,底层状态指的是三个可用的选项:晴朗、寒冷或雨天。

如何操作...

让我们看看如何为序列数据构建 HMMs:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的hmm.py文件中给出):
import numpy as np
import matplotlib.pyplot as plt 
from hmmlearn.hmm import GaussianHMM 
  1. 我们将使用名为data_hmm.txt的文件中的数据,该文件已经提供给你。该文件包含逗号分隔的行。每行包含三个值:一个年份、一个月和一段浮点数据。让我们将其加载到一个 NumPy 数组中:
# Load data from input file 
input_file = 'data_hmm.txt' 
data = np.loadtxt(input_file, delimiter=',') 
  1. 让我们将数据列堆叠起来进行分析。实际上我们不需要技术上列堆叠,因为这只有一个列。然而,如果你有多个列要分析,可以使用以下结构:
# Arrange data for training  
X = np.column_stack([data[:,2]]) 
  1. 使用四个组件创建和训练 HMM。组件的数量是一个超参数,我们必须选择。在这里,通过选择四个,我们说数据正在使用四个潜在状态生成。我们将看到性能如何随着这个参数的变化而变化:
# Create and train Gaussian HMM  
print("Training HMM....") 
num_components = 4 
model = GaussianHMM(n_components=num_components, covariance_type="diag", n_iter=1000) 
model.fit(X) 
  1. 运行预测器以获取隐藏状态:
# Predict the hidden states of HMM  
hidden_states = model.predict(X) 
  1. 计算隐藏状态的均值和方差:
print("Means and variances of hidden states:")
for i in range(model.n_components):
    print("Hidden state", i+1)
    print("Mean =", round(model.means_[i][0], 3))
    print("Variance =", round(np.diag(model.covars_[i])[0], 3))
  1. 如我们之前讨论的,HMMs 是生成模型。所以,让我们生成,例如,1000个样本并绘制这个:
# Generate data using model 
num_samples = 1000 
samples, _ = model.sample(num_samples)  
plt.plot(np.arange(num_samples), samples[:,0], c='black') 
plt.title('Number of components = ' + str(num_components)) 
plt.show() 

完整的代码在提供的hmm.py文件中给出。如果你运行前面的代码,你会看到以下输出:

图片

  1. 你可以通过实验n_components参数来观察随着它的增加曲线如何变得更加平滑。你基本上可以给它更多的自由度来训练和定制,允许更多的隐藏状态。如果你将其增加到8,你会看到以下输出:

图片

  1. 如果你将其增加到12,它将变得更加平滑:

图片

在终端中,你会得到以下输出:

Training HMM....
Means and variances of hidden states:
Hidden state 1
Mean = 5.592
Variance = 0.253
Hidden state 2
Mean = 1.098
Variance = 0.004
Hidden state 3
Mean = 7.102
Variance = 0.003
Hidden state 4
Mean = 3.098
Variance = 0.003
Hidden state 5
Mean = 4.104
Variance = 0.003

它是如何工作的...

HMM 是一种模型,其中被建模的系统被假定为具有未观察状态的马尔可夫过程。一个随机过程被称为马尔可夫的,当在观察中选择了某个特定的t实例后,该过程的演变,从t开始,只依赖于t,而不以任何方式依赖于之前的实例。因此,一个过程是马尔可夫的,当给定观察时刻时,只有这个实例决定了过程的未来演变,而这一演变不依赖于过去。在这个菜谱中,我们学习了如何使用 HMMs 生成时间序列。

还有更多…

在这个菜谱中,我们使用了hmmlearn来构建和训练 HMMs,它实现了 HMMs。HMM 是一种生成概率模型,其中使用一系列隐藏内部状态计算一系列可观察变量。隐藏状态不能直接观察到。

参见

构建用于序列文本数据的 CRFs

条件随机场CRFs)是用于分析结构化数据的概率模型。它们通常用于标记和分割序列数据。与生成模型 HMMs 相比,CRFs 是判别模型。CRFs 被广泛用于分析序列、股票、语音、单词等。在这些模型中,给定一个特定的标记观察序列,我们定义这个序列的条件概率分布。这与 HMMs 不同,在 HMMs 中,我们定义标签和观察序列的联合分布。

准备工作

在这个菜谱中,我们将使用一个名为pystruct的库来构建和训练 CRFs。确保你在继续之前安装它。你可以找到安装说明在pystruct.github.io/installation.html

如何做到这一点...

让我们看看我们如何为序列文本数据构建 CRFs:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的crf.py文件中给出):
import argparse 
import numpy as np
from pystruct.datasets import load_letters
from pystruct.models import ChainCRF
from pystruct.learners import FrankWolfeSSVM
  1. 定义一个参数解析器以接受C值作为输入参数。在这里,C是一个超参数,它控制你希望你的模型有多具体,同时不失泛化能力:
def build_arg_parser():
    parser = argparse.ArgumentParser(description='Trains the CRF classifier')
    parser.add_argument("--c-value", dest="c_value", required=False, type=float,
            default=1.0, help="The C value that will be used for training")
    return parser
  1. 定义一个class来处理所有与 CRF 相关的处理:
class CRFTrainer(object): 
  1. 定义一个init函数来初始化值:
    def __init__(self, c_value, classifier_name='ChainCRF'): 
        self.c_value = c_value 
        self.classifier_name = classifier_name 
  1. 我们将使用ChainCRF来分析数据。我们需要添加一个错误检查,如下所示:
        if self.classifier_name == 'ChainCRF': 
            model = ChainCRF() 
  1. 定义我们将与 CRF 模型一起使用的分类器。我们将使用一种 SVM 类型来实现这一点:
            self.clf = FrankWolfeSSVM(model=model, C=self.c_value, max_iter=50)  
        else: 
            raise TypeError('Invalid classifier type') 
  1. 加载letters数据集。这个数据集包括分割后的字母及其相关的特征向量。我们不会分析图像,因为我们已经有了特征向量。每个单词的第一个字母已经被移除,所以我们只剩下小写字母:
    def load_data(self): 
        letters = load_letters() 
  1. 将数据和标签加载到各自的变量中:
        X, y, folds = letters['data'], letters['labels'], letters['folds'] 
        X, y = np.array(X), np.array(y) 
        return X, y, folds 
  1. 定义一种训练方法,如下所示:
    # X is a numpy array of samples where each sample 
    # has the shape (n_letters, n_features)  
    def train(self, X_train, y_train): 
        self.clf.fit(X_train, y_train) 
  1. 定义一个方法来评估模型的表现:
    def evaluate(self, X_test, y_test): 
        return self.clf.score(X_test, y_test) 
  1. 定义一个方法来分类新数据:
    # Run the classifier on input data 
    def classify(self, input_data): 
        return self.clf.predict(input_data)[0] 
  1. 字母被索引在一个编号数组中。为了检查输出并使其可读,我们需要将这些数字转换成字母。定义一个函数来做这件事:
def decoder(arr): 
    alphabets = 'abcdefghijklmnopqrstuvwxyz' 
    output = '' 
    for i in arr: 
        output += alphabets[i]  

    return output 
  1. 定义主函数并解析输入参数:
if __name__=='__main__': 
    args = build_arg_parser().parse_args() 
    c_value = args.c_value 
  1. 使用类和C值初始化变量:
    crf = CRFTrainer(c_value) 
  1. 加载letters数据:
    X, y, folds = crf.load_data() 
  1. 将数据分为训练集和测试集:
    X_train, X_test = X[folds == 1], X[folds != 1] 
    y_train, y_test = y[folds == 1], y[folds != 1] 
  1. 按照以下方式训练 CRF 模型:
    print("Training the CRF model...")
    crf.train(X_train, y_train) 
  1. 评估 CRF 模型的表现:
    score = crf.evaluate(X_test, y_test) 
    print("Accuracy score =", str(round(score*100, 2)) + '%') 
  1. 让我们随机取一个测试向量并使用模型进行预测:
    print("True label =", decoder(y_test[0]))
    predicted_output = crf.classify([X_test[0]])
    print("Predicted output =", decoder(predicted_output))
  1. 如果你运行前面的代码,你将在你的终端上得到以下输出。正如我们所见,单词应该是commanding。CRF 在预测所有字母方面做得相当不错:
Training the CRF model...
Accuracy score = 77.93%
True label = ommanding
Predicted output = ommanging

它是如何工作的...

HMMs 假设当前输出在统计上与先前输出独立。这是 HMMs 确保推理以稳健方式工作所必需的。然而,这个假设并不总是必须成立的!在时间序列设置中,当前输出往往依赖于先前输出。CRFs 相对于 HMMs 的主要优势之一是它们本质上是有条件的,这意味着我们不会假设输出观测之间有任何独立性。使用 CRFs 而不是 HMMs 还有其他一些优势。CRFs 在许多应用中往往优于 HMMs,例如语言学、生物信息学、语音分析和如此等等。在这个菜谱中,我们将学习如何使用 CRFs 来分析字母序列。

还有更多...

PyStruct 是一个易于使用的机器学习算法的结构化库。它实现了最大间隔和感知器方法。PyStruct 中实现的算法示例包括 CRFs、最大间隔马尔可夫 随机字段M3Ns)和结构化 SVMs。

参见

分析股票市场数据

股票市场一直是一个非常热门的话题;这是因为股票市场趋势涉及真正令人印象深刻的交易量。这个话题引起的兴趣显然与通过股票市场标题的良好预测获得财富的机会有关。购买股票价格与卖出股票价格之间的正差价意味着投资者获得了收益。但是,正如我们所知,股票市场的表现取决于多个因素。

准备工作

在这个菜谱中,我们将探讨如何分析一家非常流行的公司的股票价格:我指的是亚马逊,一家位于华盛顿州西雅图的美国电子商务公司,它是世界上最大的互联网公司。

如何操作...

让我们看看我们如何分析股票市场数据:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的AmazonStock.py文件中):
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from random import seed
  1. 从提供的AMZN.csv文件中获取股票报价:
seed(0)
Data = pd.read_csv('AMZN.csv',header=0, usecols=['Date', 'Close'],parse_dates=True,index_col='Date')
  1. 要提取导入数据集的初步信息,我们可以调用info()函数:
print(Data.info())

返回以下结果:

<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 4529 entries, 2000-11-21 to 2018-11-21
Data columns (total 1 columns):
Close 4529 non-null float64
dtypes: float64(1)
memory usage: 70.8 KB
None

此函数打印有关 DataFrame 的信息,包括索引和dtypes列、non-null值和memory usage

  1. 要显示导入的 DataFrame 的前五行,我们可以使用head()函数,如下所示:
print(Data.head())

此函数根据位置返回对象的前 n 行。这对于快速测试对象中是否包含正确的数据类型很有用。默认情况下(如果省略 n),显示前五行。以下结果被返回:

 Close
Date 
2000-11-21 24.2500
2000-11-22 25.1875
2000-11-24 28.9375
2000-11-27 28.0000
2000-11-28 25.0312
  1. 要预览其中包含的数据,我们可以计算一系列基本统计量。为此,我们将使用以下方式的 describe() 函数:
print(Data.describe())

describe() 函数生成描述性统计量,总结数据集的中心趋势、离散程度和分布形式,排除 NaN 值。此函数分析数值和对象序列,以及混合数据类型的 DataFrame 列集。以下结果被返回:

 Close
count      4529.000000
mean        290.353723
std         407.211585
min           5.970000
25%          39.849998
50%         117.889999
75%         327.440002
max        2039.510010
  1. 现在,我们将对时间序列进行初步的视觉探索性分析:
plt.figure(figsize=(10,5))
plt.plot(Data)
plt.show()

在以下图表中,显示了从 2000 年 11 月 21 日到 2018 年 11 月 21 日的亚马逊股票价格:

从先前图表的分析中,我们可以看到价格随着时间的推移显著增加。特别是,从 2015 年开始,这种增长显示出指数趋势。

  1. 现在,让我们尝试更深入地了解亚马逊股票随时间记录的变化。为了在 Python 中计算百分比变化,我们将使用 pct_change() 函数。此函数返回给定数量的期间的百分比变化:
DataPCh = Data.pct_change()

我们刚刚计算的结果与回报的概念相符。

  1. 要计算回报的对数,我们将使用来自 numpylog() 函数:
LogReturns = np.log(1 + DataPCh) 
print(LogReturns.tail(10))

tail() 函数根据位置从对象返回最后 n 行。这对于快速验证数据很有用——例如,在排序或追加行之后。以下值被返回(LogReturns 对象的最后 10 行):

 Close
Date 
2018-11-08     -0.000330
2018-11-09     -0.024504
2018-11-12     -0.045140
2018-11-13     -0.003476
2018-11-14     -0.019913
2018-11-15      0.012696
2018-11-16     -0.016204
2018-11-19     -0.052251
2018-11-20     -0.011191
2018-11-21      0.014123
  1. 现在,我们将绘制我们计算出的回报对数的图表:
plt.figure(figsize=(10,5))
plt.plot(LogReturns)
plt.show()

如我们之前所做的那样,我们首先设置图表的维度,然后我们将绘制图表,最后我们将可视化它。以下图表显示了回报的对数:

它是如何工作的...

要研究一个现象的演变,仅有一个时间序列图是不够的;我们需要比较不同时间点的现象强度,即计算从一个时期到另一个时期的强度变化。此外,分析相邻时间段内现象变化的趋势可能也很有趣。我们用 Y1,..., Yt,..., Yn 表示时间序列。时间序列是变量的实验观察的按时间顺序记录,例如价格趋势、股票市场指数、价差和失业率。因此,它是一系列按时间顺序排列的数据,我们希望从中提取信息以表征观察到的现象,并预测未来的值。

两个不同时间之间发生的变化(让我们用 tt + 1 来表示)可以使用以下比率来衡量:

图片

此指数是一个百分比比率,称为百分比变化。特别是,这是现象 Y 在时间 t + 1 相对于之前时间 t 的百分比变化率。这种方法提供了关于数据随时间变化更详细的解释。使用这种技术,我们可以追踪个别股票和大型市场指数的价格,以及比较不同货币的价值。

更多内容...

与价格相比,使用回报的优势在于标准化,这使得我们能够以可比较的指标来衡量所有变量,从而允许评估两个或更多变量之间的分析关系。

参见

使用 RNN 预测时序数据

长短期记忆LSTM)是循环神经网络RNNs)的一种特定架构。RNNs 基于对过去事件记忆的需求;这种行为在普通网络中是不可能的,这就是为什么 RNNs 被用于经典网络无法产生结果的领域,例如预测与先前数据相关的时序数据(天气、报价等)。

LSTM 网络由相互连接的细胞(LSTM 块)组成。每个细胞反过来又由三种类型的端口组成:输入门、输出门和遗忘门。它们分别对细胞内存执行写入、读取和重置功能,因此 LSTM 模块能够调节存储和删除的内容。这得益于存在各种称为的元素,这些门由一个 sigmoid 神经网络层和一个逐点乘积组成。每个门的输出在(0,1)范围内,表示其中流动的信息百分比。

准备工作

在这个菜谱中,我们将探讨如何将 LSTM 模型应用于预测一家非常受欢迎的公司的未来股价:我指的是总部位于华盛顿州西雅图的美国电子商务公司亚马逊,它是世界上最大的互联网公司。

如何操作...

让我们看看如何使用 RNN 来预测时序数据:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的LSTMstock.py文件中给出)。文件的前一部分在之前的菜谱中已经处理过,分析股票市场数据。我们只报告它以完整算法:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from random import seed

seed(0)

Data = pd.read_csv('AMZN.csv',header=0, usecols=['Date', 'Close'],parse_dates=True,index_col='Date')
  1. 在训练 LSTM 算法之前对数据进行缩放是一种良好的实践。通过缩放,消除了数据单位,这使得你可以轻松地比较来自不同位置的数据。在这个例子中,我们将使用最小-最大方法(通常称为特征缩放)来获取所有缩放数据在[0, 1]范围内的数据。为了执行特征缩放,我们可以使用sklearn库中可用的预处理包:
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
DataScaled = scaler.fit_transform(Data)
  1. 现在,让我们将数据分为训练和测试模型两部分。训练和测试模型是进一步使用模型进行预测性分析的基础。给定一个包含 4,529 行数据的数据集,我们将其按方便的比例(例如 70:30)分割,并将 3,170 行分配给训练,1,359 行分配给测试:
np.random.seed(7)
TrainLen = int(len(DataScaled) * 0.70)
TestLen = len(DataScaled) - TrainLen
TrainData = DataScaled[0:TrainLen,:] 
TestData = DataScaled[TrainLen:len(DataScaled),:]

print(len(TrainData), len(TestData))

以下结果被返回:

3170 1359
  1. 现在,我们需要输入和输出以训练和测试我们的网络。很明显,输入由数据集中现有的数据表示。因此,我们必须构建我们的输出;我们将通过假设我们想要预测时间t + 1的亚马逊股价相对于时间t存储的值来做到这一点。一个循环网络具有记忆,这是通过固定所谓的步长来维持的。步长是关于反向传播在计算权重更新时的梯度时回溯多长时间的问题。这样,我们设置TimeStep=1。然后,我们定义一个函数,它接受一个数据集和一个时间步长,然后返回输入和输出数据:
def DatasetCreation(dataset, TimeStep=1):
  DataX, DataY = [], []
  for i in range(len(dataset)-TimeStep-1):
    a = dataset[i:(i+TimeStep), 0]
    DataX.append(a)
    DataY.append(dataset[i + TimeStep, 0])
  return np.array(DataX), np.array(DataY)

在这个函数中,dataX = Input = data(t)是输入变量,DataY = output = data(t + 1)是下一个时间段的预测值。

  1. 让我们使用这个函数来设置我们在下一阶段(网络建模)中将要使用的训练和测试数据集:
TimeStep = 1
TrainX, TrainY = DatasetCreation(TrainData, TimeStep)
TestX, TestY = DatasetCreation(TestData, TimeStep)

在 LSTM/RNN 网络中,每个 LSTM 层的输入必须包含以下信息:

  • 观测数:收集到的观测数

  • 时间步长:时间步长是样本中的观测点

  • 特征:每步一个特征

因此,有必要为那些经典网络预见的添加一个时间维度。因此,输入形状如下:

(观测数,时间步数,每步特征数)

这样,每个 LSTM 层的输入就变成了三维的。

  1. 为了将输入数据集转换为 3D 形式,我们将使用np.reshape()函数,如下所示:
TrainX = np.reshape(TrainX, (TrainX.shape[0], 1, TrainX.shape[1]))
TestX = np.reshape(TestX, (TestX.shape[0], 1, TestX.shape[1]))
  1. 现在数据已经处于正确的格式,是时候创建模型了。让我们先导入库:
from keras.models import Sequential
from keras.layers import LSTM
from keras.layers import Dense
  1. 我们将使用Sequential模型,即层的一个线性堆叠。为了创建一个序列模型,我们必须将一个层实例的列表传递给构造函数。我们也可以通过add()方法简单地添加层:
model = Sequential()
model.add(LSTM(256, input_shape=(1, TimeStep)))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='mean_squared_error', optimizer='adam',metrics=['accuracy'])
model.fit(TrainX, TrainY, epochs=100, batch_size=1, verbose=1)
model.summary()

以下结果被打印出来:

  1. 为了评估我们刚刚调整的模型的表现,我们可以使用evaluate()函数,如下所示:
score = model.evaluate(TrainX, TrainY, verbose=0)
print('Keras Model Loss = ',score[0])
print('Keras Model Accuracy = ',score[1])

前面的函数在测试模式下显示模型的损失值和指标值。这是分批计算的。以下结果被返回:

Keras Model Loss = 2.4628453362992094e-06
Keras Model Accuracy = 0.0003156565656565657
  1. 模型现在已准备好使用。因此,我们可以用它来执行我们的预测:
TrainPred = model.predict(TrainX)
TestPred = model.predict(TestX)
  1. 预测必须以原始形式报告,以便可以与实际值进行比较:
TrainPred = scaler.inverse_transform(TrainPred)
TrainY = scaler.inverse_transform([TrainY])
TestPred = scaler.inverse_transform(TestPred)
TestY = scaler.inverse_transform([TestY])
  1. 为了验证数据的正确预测,我们现在可以通过绘制适当的图表来可视化结果。为了正确显示时间序列,需要预测偏移。这个操作必须在训练集和测试集上执行:
TrainPredictPlot = np.empty_like(DataScaled)
TrainPredictPlot[:, :] = np.nan
TrainPredictPlot[1:len(TrainPred)+1, :] = TrainPred
  1. 如我们之前所述,然后必须在测试集上执行相同的操作:
TestPredictPlot = np.empty_like(DataScaled)
TestPredictPlot[:, :] = np.nan
TestPredictPlot[len(TrainPred)+(1*2)+1:len(DataScaled)-1, :] = TestPred
  1. 最后,我们必须绘制实际数据和预测结果:
plt.figure(figsize=(10,5))
plt.plot(scaler.inverse_transform(DataScaled))
plt.plot(TrainPredictPlot)
plt.plot(TestPredictPlot)
plt.show()

下面的截图显示了实际数据和预测结果:

图片

它是如何工作的...

在本食谱的开头,我们说 LSTM 模块能够调节存储和删除的内容。这要归功于存在各种称为门的元素,它们由一个 sigmoid 神经网络层和一个逐点乘积组成。LSTM 模块的第一部分决定从单元格中删除哪些信息。门接收输入并为每个单元格状态返回一个介于 0 和 1 之间的值。门输出可以取两个值:

  • 0:完全重置单元格状态

  • 1:单元格值的总存储

数据存储分为两个阶段:

  • 第一部分委托给一个名为输入门层的 sigmoid 层;它执行一个操作,确定哪些值需要更新。

  • 第二阶段则委托给一个tanh层,该层创建一个待更新的值向量。为了创建一个更新的值集,将两个层的输出结合起来。

最后,结果将由一个 sigmoid 层给出,该层确定哪些单元格部分将对输出做出贡献,并从当前单元格状态中,通过tanh函数过滤,以获得-1 到 1 的范围。此操作的输出乘以 sigmoid 层的值,以便只给出所需的输出。

更多内容...

RNN 是一种神经网络,其中存在信息双向流动。换句话说,在前馈网络中,信号的传播只在单一方向上连续进行,从输入到输出,而循环网络则不同。在循环网络中,这种传播也可以发生在前一个神经层之后的神经层之间,属于同一层的神经元之间,甚至是一个神经元与其自身之间。

参见

第十章:分析图像内容

本章我们将涵盖以下食谱:

  • 使用 OpenCV-Python 操作图像

  • 检测边缘

  • 直方图均衡化

  • 检测角点

  • 检测 SIFT 特征点

  • 构建星特征检测器

  • 使用视觉代码簿和向量量化创建特征

  • 使用超随机森林训练图像分类器

  • 构建物体识别器

  • 使用 LightGBM 进行图像分类

技术要求

要浏览本章的食谱,你需要以下文件(可在 GitHub 上找到):

  • operating_on_images.py

  • capri.jpg

  • edge_detector.py

  • chair.jpg

  • histogram_equalizer.py

  • sunrise.jpg

  • corner_detector.py

  • box.png

  • feature_detector.py

  • table.jpg

  • star_detector.py

  • trainer.py

  • object_recognizer.py

  • LightgbmClassifier.py

介绍计算机视觉

计算机视觉是一个研究如何处理、分析和理解视觉数据内容的领域。在图像内容分析中,我们使用大量的计算机视觉算法来构建我们对图像中对象的了解。计算机视觉涵盖了图像分析的各个方面,如物体识别、形状分析、姿态估计、3D 建模、视觉搜索等。人类在识别和识别周围事物方面非常出色!计算机视觉的最终目标是使用计算机准确模拟人类的视觉系统。

计算机视觉包括多个分析层次。在低级视觉中,我们处理像素处理任务,例如边缘检测形态处理光流。在中级和高级视觉中,我们处理诸如物体识别3D 建模运动分析以及视觉数据的各个方面。随着层次的提高,我们倾向于深入探讨视觉系统的概念性方面,并尝试根据活动和意图提取视觉数据的描述。需要注意的是,高级层次往往依赖于低级层次的输出进行分析。

这里最常见的问题之一是:计算机视觉与图像处理有何不同?图像处理研究像素级别的图像变换。图像处理系统的输入和输出都是图像。一些常见的例子包括边缘检测、直方图均衡化图像压缩。计算机视觉算法在很大程度上依赖于图像处理算法来执行其任务。在计算机视觉中,我们处理更复杂的事情,包括在概念层面上理解视觉数据。这样做的原因是我们想要构建图像中对象的具有意义的描述。计算机视觉系统的输出是对给定图像中 3D 场景的解释。这种解释可以以各种形式出现,具体取决于任务。

使用 OpenCV-Python 操作图像

在本章中,我们将使用一个名为开源计算机视觉库OpenCV)的库来分析图像。OpenCV 是世界上最受欢迎的计算机视觉库。由于它针对许多不同的平台进行了高度优化,因此已成为行业中的事实标准。在继续之前,请确保您已安装具有 Python 支持的库。您可以从opencv.org下载并安装 OpenCV。有关各种操作系统的详细安装说明,您可以参考网站上的文档部分。

准备工作

在本食谱中,我们将探讨如何使用 OpenCV-Python 操作图像。在本食谱中,我们将查看如何加载和显示图像。我们还将查看如何裁剪、调整大小并将图像保存到输出文件中。

如何做到这一点...

让我们看看如何使用 OpenCV-Python 操作图像:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在提供的operating_on_images.py文件中给出):
import sys 
import cv2 
  1. 将输入图像指定为文件的第一个参数,并使用图像读取函数读取它。我们将使用您提供的forest.jpg文件,如下所示:
# Load and display an image -- 'forest.jpg' 
input_file = sys.argv[1] 
img = cv2.imread(input_file) 
  1. 按以下方式显示输入图像:
cv2.imshow('Original', img)
  1. 我们现在将裁剪此图像。提取输入图像的高度和宽度,然后指定边界:
# Cropping an image 
h, w = img.shape[:2] 
start_row, end_row = int(0.21*h), int(0.73*h) 
start_col, end_col= int(0.37*w), int(0.92*w) 
  1. 使用 NumPy 风格的切片裁剪图像并显示:
img_cropped = img[start_row:end_row, start_col:end_col] 
cv2.imshow('Cropped', img_cropped) 
  1. 将图像调整到原始大小的1.3倍并显示:
# Resizing an image 
scaling_factor = 1.3 
img_scaled = cv2.resize(img, None, fx=scaling_factor,               fy=scaling_factor,  
interpolation=cv2.INTER_LINEAR) 
cv2.imshow('Uniform resizing', img_scaled) 
  1. 之前的方法将在两个维度上均匀缩放图像。假设我们想要根据特定的输出维度扭曲图像。我们将使用以下代码:
img_scaled = cv2.resize(img, (250, 400), interpolation=cv2.INTER_AREA) 
cv2.imshow('Skewed resizing', img_scaled) 
  1. 将图像保存到输出文件中:
# Save an image 
output_file = input_file[:-4] + '_cropped.jpg' 
cv2.imwrite(output_file, img_cropped) 

cv2.waitKey() 

waitKey()函数将显示图像,直到你在键盘上按下一个键。

  1. 我们将在终端窗口中运行此代码:
$ python operating_on_images.py capri.jpg

你将在屏幕上看到以下四幅图像(意大利卡普里岛的法拉乔尼):

图片

它是如何工作的...

在本食谱中,我们学习了如何使用 OpenCV-Python 库操作图像。以下任务被执行:

  • 加载和显示图像

  • 裁剪图像

  • 调整图像大小

  • 保存图像

更多内容...

OpenCV 是一个由英特尔和俄罗斯下诺夫哥罗德研究中心最初开发的免费软件库。后来,它由 Willow Garage 维护,现在由 Itseez 维护。主要用于与该库一起开发的编程语言是 C++,但也可以通过 C、Python 和 Java 进行接口。

参见

检测边缘

边缘检测是计算机视觉中最受欢迎的技术之一。它被用作许多应用的前处理步骤。通过边缘检测,你可以在数字图像中标记光强度突然变化的位置。图像属性的突然变化旨在突出重要的物理世界事件或变化,这些图像是这些事件或变化的表示。这些变化确定了,例如,表面方向不连续性、深度不连续性等。

准备中

在这个菜谱中,我们将学习如何使用不同的边缘检测器来检测输入图像中的边缘。

如何做...

让我们看看如何检测边缘:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在提供的edge_detector.py文件中给出):
import sys 
import cv2 
  1. 加载输入图像。我们将使用chair.jpg
# Load the input image -- 'chair.jpg' 
# Convert it to grayscale  
input_file = sys.argv[1] 
img = cv2.imread(input_file, cv2.IMREAD_GRAYSCALE)
  1. 提取图像的高度和宽度:
h, w = img.shape 
  1. 索贝尔滤波器是一种边缘检测器,它使用一个 3 x 3 核分别检测水平和垂直边缘:
sobel_horizontal = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5) 
  1. 运行垂直索贝尔检测器:
sobel_vertical = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5) 
  1. 拉普拉斯边缘检测器可以在两个方向上检测边缘。我们使用它如下:
laplacian = cv2.Laplacian(img, cv2.CV_64F) 
  1. 尽管拉普拉斯解决了索贝尔的不足,但输出仍然非常嘈杂。Canny 边缘检测器由于处理问题的方法而优于所有这些,它是一个多阶段过程,并使用滞后性来得到干净的边缘:
canny = cv2.Canny(img, 50, 240) 
  1. 显示所有输出图像:
cv2.imshow('Original', img) 
cv2.imshow('Sobel horizontal', sobel_horizontal) 
cv2.imshow('Sobel vertical', sobel_vertical) 
cv2.imshow('Laplacian', laplacian) 
cv2.imshow('Canny', canny) 

cv2.waitKey() 
  1. 我们将在终端窗口中使用以下命令运行代码:
$ python edge_detector.py siracusa.jpg

你将在屏幕上看到以下五张图像(意大利西西里岛的古代剧院):

图片

截图顶部是原始图像、水平索贝尔边缘检测器输出和垂直索贝尔边缘检测器输出。注意检测到的线条倾向于垂直。这是因为它是一个水平边缘检测器,并且倾向于检测这个方向的变化。截图底部是拉普拉斯边缘检测器输出和 Canny 边缘检测器,它很好地检测到了所有边缘。

它是如何工作的...

索贝尔算子是一个微分算子,它计算表示图像亮度的函数梯度的近似值。在图像的每个点上,索贝尔算子可以对应于梯度向量或该向量的范数。索贝尔算子使用的算法是基于图像与一个分离的、整数值的滤波器的卷积,这个滤波器在垂直和水平方向上应用,因此在计算能力方面是经济的。

拉普拉斯边缘检测器是零交叉方法的一部分,它寻找二阶导数穿过零的点,这通常是拉普拉斯函数或非线性函数的微分表达式。

更多...

Canny 算法使用多阶段计算方法来寻找真实图像中通常存在的许多类型的轮廓。为此,算法必须尽可能多地识别和标记图像中的良好位置。此外,标记的轮廓必须尽可能接近图像的真实轮廓。最后,给定的图像轮廓只能标记一次,并且如果可能的话,图像中存在的噪声不应导致检测到错误的轮廓。

参见

直方图均衡化

直方图均衡化是修改图像像素强度以增强图像对比度的过程。人眼喜欢对比度!这就是为什么几乎所有的相机系统都使用直方图均衡化来使图像看起来很棒。

准备工作

有趣的是,直方图均衡化过程对于灰度图像和彩色图像是不同的。处理彩色图像时有一个陷阱,我们将在本食谱中看到它。让我们看看如何做到这一点。

如何做到这一点...

让我们看看我们如何执行直方图均衡化:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的histogram_equalizer.py文件中给出):
import sys 
import cv2   
  1. 加载输入图像。我们将使用sunrise.jpg图像:
# Load input image -- 'sunrise.jpg' 
input_file = sys.argv[1] 
img = cv2.imread(input_file) 
  1. 将图像转换为灰度并显示:
# Convert it to grayscale 
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 
cv2.imshow('Input grayscale image', img_gray) 
  1. 均衡灰度图像的直方图并显示:
# Equalize the histogram 
img_gray_histeq = cv2.equalizeHist(img_gray) 
cv2.imshow('Histogram equalized - grayscale', img_gray_histeq) 
  1. OpenCV 默认以BGR格式加载图像,所以让我们首先将其从BGR转换为YUV
# Histogram equalization of color images 
img_yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV) 
  1. 按如下方式均衡 Y 通道:
img_yuv[:,:,0] = cv2.equalizeHist(img_yuv[:,:,0]) 
  1. 将其转换回BGR
img_histeq = cv2.cvtColor(img_yuv, cv2.COLOR_YUV2BGR) 
  1. 显示输入和输出图像:
cv2.imshow('Input color image', img) 
cv2.imshow('Histogram equalized - color', img_histeq) 

cv2.waitKey()
  1. 我们将在终端窗口中运行代码:
$ python histogram_equalizer.py gubbio.jpg

您将在屏幕上看到以下四幅图像(意大利古比奥的中世纪城市):

它是如何工作的...

直方图均衡化是一种数字图像处理方法,您可以使用图像直方图来校准对比度。直方图均衡化增加了许多图像的总体对比度,尤其是在可用图像数据由非常接近的强度值表示时。通过这种适应,强度可以在直方图上更好地分布。这样,局部对比度低的区域可以获得更大的对比度。直方图均衡是通过扩展频繁强度的大多数值来实现的。

还有更多…

为了均衡彩色图像的直方图,我们需要遵循不同的步骤。直方图均衡化仅适用于强度通道。RGB 图像由三个颜色通道组成,我们不能单独对这些通道应用直方图均衡化过程。在我们做任何事情之前,我们需要将强度信息从颜色信息中分离出来。因此,我们首先将其转换为 YUV 颜色空间,然后均衡 Y 通道,最后将其转换回 RGB 以获得输出。

参见

检测角点

角点检测是计算机视觉中的一个重要过程。它帮助我们识别图像中的显著点。这是最早用于开发图像分析系统的特征提取技术之一。

准备工作

在这个菜谱中,我们将学习如何通过在识别的点放置标记来检测盒子的角点。

如何做到这一点...

让我们看看我们如何检测角点:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的corner_detector.py文件中给出):
import sys 
import cv2 
import numpy as np
  1. 加载输入图像。我们将使用box.png
# Load input image -- 'box.png' 
input_file = sys.argv[1] 
img = cv2.imread(input_file) 
cv2.imshow('Input image', img) 
  1. 将图像转换为灰度并转换为浮点值。我们需要浮点值以便角点检测器工作:
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 
img_gray = np.float32(img_gray) 
  1. 灰度图像上运行Harris 角点检测器函数:
# Harris corner detector  
img_harris = cv2.cornerHarris(img_gray, 7, 5, 0.04) 
  1. 为了标记角点,我们需要膨胀图像,如下所示:
# Resultant image is dilated to mark the corners 
img_harris = cv2.dilate(img_harris, None) 
  1. 让我们阈值化图像以显示重要点:
# Threshold the image  
img[img_harris > 0.01 * img_harris.max()] = [0, 0, 0] 
  1. 显示输出图像:
cv2.imshow('Harris Corners', img) 
cv2.waitKey() 
  1. 我们将在终端窗口中运行代码:
$ python corner_detector.py box.png

你将在屏幕上看到以下两个图像:

它是如何工作的...

角点检测是计算机视觉中用于提取特征类型并推断图像内容的方法。它常用于运动检测、图像记录、视频跟踪、图像拼接、图像全景创建、3D 建模和物体识别。它是一个与兴趣点检测类似的话题。

更多内容...

角点检测方法可以分为两组:

  • 基于提取轮廓和随后识别对应于最大曲率或边缘段相交点的技术的技巧

  • 直接从图像像素的灰度强度中搜索角点的算法

参见

检测 SIFT 特征点

尺度不变特征变换SIFT)是计算机视觉领域最受欢迎的特征之一。David Lowe 在他的开创性论文中首次提出了这一概念。它已经成为用于图像识别和内容分析的最有效的特征之一。它对尺度、方向、强度等因素具有鲁棒性。这构成了我们物体识别系统的基础。

准备工作

在这个菜谱中,我们将学习如何检测 SIFT 特征点。

如何操作...

让我们看看如何检测 SIFT 特征点:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的feature_detector.py文件中给出):
import sys 
import cv2 
import numpy as np  
  1. 加载输入图像。我们将使用table.jpg
# Load input image -- 'table.jpg' 
input_file = sys.argv[1] 
img = cv2.imread(input_file)
  1. 将此图像转换为灰度:
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 
  1. 初始化 SIFT 检测器对象并提取关键点:
sift = cv2.xfeatures2d.SIFT_create() 
keypoints = sift.detect(img_gray, None) 
  1. 关键点是显著点,但它们不是特征。这基本上给出了显著点的位置。SIFT 还充当一个非常有效的特征提取器。

  2. 按如下方式在输入图像上绘制关键点:

img_sift = np.copy(img) 
cv2.drawKeypoints(img, keypoints, img_sift, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) 
  1. 显示输入和输出图像:
cv2.imshow('Input image', img) 
cv2.imshow('SIFT features', img_sift) 
cv2.waitKey() 
  1. 我们将在终端窗口中运行此代码:
$ python feature_detector.py flowers.jpg

你将在屏幕上看到以下两个图像:

它是如何工作的...

对于图像中的每个对象,都会提取一些有趣的点来描述对象的特点。这个从训练图像中获得的特征,用于在包含许多其他对象的测试图像中定位对象时识别对象。为了获得可靠的识别,从训练图像中提取的特征必须可检测,即使存在尺度变化、噪声和光照。这些点通常放置在图像的高对比度区域,如对象轮廓。

还有更多...

在 Lowe 的方法中,SIFT 对象的特征点在第一阶段从一组参考图像中提取出来,然后它们被存储在数据库中。在新的图像中识别对象是通过将新图像的每个特征与之前获得的数据库中的特征逐一比较,并基于特征向量的欧几里得距离寻找特征来实现的。从新图像中的完整匹配集中,识别出与对象及其位置、尺度、方向一致的关键点子集,以过滤出最佳匹配。

参见

构建星形特征检测器

SIFT 特征检测器在许多情况下都很好。然而,当我们构建对象识别系统时,我们可能在提取 SIFT 特征之前想要使用不同的特征检测器。这将给我们提供灵活性,以级联不同的块以获得最佳性能。

准备工作

在这个菜谱中,我们将使用S****tar 特征检测器从图像中检测特征。

如何操作...

让我们看看我们如何构建一个星形特征检测器:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的star_detector.py文件中给出):
import sys 
import cv2  
  1. 定义一个类来处理与星形特征检测相关的所有函数:
class StarFeatureDetector(object): 
    def __init__(self): 
        self.detector = cv2.xfeatures2d.StarDetector_create() 
  1. 定义一个函数来在输入图像上运行检测器:
    def detect(self, img): 
        return self.detector.detect(img) 
  1. main函数中加载输入图像。我们将使用table.jpg
if __name__=='__main__': 
    # Load input image -- 'table.jpg' 
    input_file = sys.argv[1] 
    input_img = cv2.imread(input_file) 
  1. 将图像转换为灰度:
    # Convert to grayscale 
    img_gray = cv2.cvtColor(input_img, cv2.COLOR_BGR2GRAY) 
  1. 使用星形特征检测器检测特征:
    # Detect features using Star feature detector 
    keypoints = StarFeatureDetector().detect(input_img)
  1. 在输入图像上绘制关键点:
    cv2.drawKeypoints(input_img, keypoints, input_img,  
               flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) 
  1. 显示输出图像:
    cv2.imshow('Star features', input_img) 
    cv2.waitKey() 
  1. 我们将在终端窗口中运行代码:
$ python star_detector.py table.jpg

你将在屏幕上看到以下图像:

它是如何工作的...

在这个菜谱中,我们学习了如何使用 OpenCV-Python 库构建星形特征检测器。以下任务被执行了:

  • 加载图像

  • 转换为灰度

  • 使用星形特征检测器检测特征

  • 绘制关键点和显示图像

更多内容...

Star 函数检测器基于CenSurE中心周围极值)。两个检测器之间的区别在于多边形的选取:

  • CenSurE 使用正方形、六边形和八边形作为圆形的替代品

  • 星形通过两个叠加的正方形来近似圆形:一个垂直和一个旋转 45 度

参考内容

使用视觉代码簿和向量量化创建特征

要构建一个目标识别系统,我们需要从每张图片中提取特征向量。每张图片都需要一个可以用于匹配的签名。我们使用一个叫做Visual Codebook 的概念来构建图像签名。这个 codebook 基本上是我们用来为训练数据集中的图像签名提供表示的字典。我们使用向量量化来聚类许多特征点并得出质心。这些质心将作为我们 Visual Codebook 的元素。

准备工作

在这个菜谱中,我们将使用 Visual Codebook 和向量量化来创建特征。为了构建一个健壮的目标识别系统,你需要成千上万张图片。有一个叫做Caltech256的数据集在这个领域非常受欢迎!它包含 256 类图片,每类包含数千个样本。

如何操作…

让我们看看如何使用 Visual Codebook 和向量量化来创建特征:

  1. 这是一个篇幅较长的菜谱,所以我们只看重要的函数。完整的代码在提供的build_features.py文件中给出。让我们看看定义用于提取特征的类:
class FeatureBuilder(object): 
  1. 定义一个从输入图像中提取特征的方法。我们将使用 Star 检测器来获取关键点,然后使用 SIFT 从这些位置提取描述符:
    def extract_ features(self, img): 
        keypoints = StarFeatureDetector().detect(img) 
        keypoints, feature_vectors = compute_sift_features(img, keypoints) 
        return feature_vectors 
  1. 我们需要从所有描述符中提取质心:
    def get_codewords(self, input_map, scaling_size, max_samples=12): 
        keypoints_all = [] 

        count = 0 
        cur_label = '' 
  1. 每张图片都会产生大量的描述符。我们只需使用少量图片,因为质心在此之后不会变化太多:
        for item in input_map: 
            if count >= max_samples: 
                if cur_class != item['object_class']: 
                    count = 0 
            else: 
                continue 

        count += 1 
  1. 打印进度如下:
        if count == max_samples: 
            print("Built centroids for", item['object_class']) 
  1. 提取当前标签:
        cur_class = item['object_class'] 
  1. 读取图片并调整大小:
        img = cv2.imread(item['image_path']) 
        img = resize_image(img, scaling_size) 
  1. 提取特征:
        feature_vectors = self.extract_image_features(img) 
        keypoints_all.extend(feature_vectors)  
  1. 使用向量量化对特征点进行量化。向量量化是N维度的舍入:
        kmeans, centroids = BagOfWords().cluster(keypoints_all) 
        return kmeans, centroids 
  1. 定义一个处理词袋模型和向量量化的类:
class BagOfWords(object): 
    def __init__(self, num_clusters=32): 
        self.num_dims = 128 
        self.num_clusters = num_clusters 
        self.num_retries = 10
  1. 定义一个量化数据点的函数。我们将使用k-means 聚类来实现这一点:
def cluster(self, datapoints): 
    kmeans = KMeans(self.num_clusters,  
        n_init=max(self.num_retries, 1), 
        max_iter=10, tol=1.0) 
  1. 提取质心,如下所示:
    res = kmeans.fit(datapoints) 
    centroids = res.cluster_centers_ 
    return kmeans, centroids 
  1. 定义一个数据归一化的方法:
def normalize(self, input_data): 
    sum_input = np.sum(input_data) 

    if sum_input > 0: 
        return input_data / sum_input 
    else: 
        return input_data 
  1. 定义一个获取特征向量的方法:
def construct_feature(self, img, kmeans, centroids): 
    keypoints = StarFeatureDetector().detect(img) 
    keypoints, feature_vectors = compute_sift_features(img, keypoints) 
    labels = kmeans.predict(feature_vectors) 
    feature_vector = np.zeros(self.num_clusters) 
  1. 构建直方图并对其进行归一化:
    for i, item in enumerate(feature_vectors): 
        feature_vector[labels[i]] += 1 

        feature_vector_img = np.reshape(feature_vector,  
((1, feature_vector.shape[0]))) 
        return self.normalize(feature_vector_img) 
  1. 定义一个方法,然后提取 SIFT 特征:
# Extract SIFT features 
def compute_sift_features(img, keypoints): 
    if img is None: 
        raise TypeError('Invalid input image') 

    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 
    keypoints, descriptors = cv2.xfeatures2d.SIFT_create().compute(img_gray, keypoints) 
    return keypoints, descriptors 

如我们之前提到的,请参考build_features.py以获取完整的代码。你应该按照以下方式运行代码:

$ python build_features.py --data-folder /path/to/training_images/ --codebook-file codebook.pkl --feature-map-file feature_map.pkl

这将生成两个文件,分别叫做codebook.pklfeature_map.pkl。我们将在下一个菜谱中使用这些文件。

它是如何工作的...

在这个菜谱中,我们使用了一个视觉教科书作为字典,然后我们使用它来为图像签名创建表示,这些图像签名包含在训练集中。因此,我们使用向量量化来分组许多特征点并创建质心。这些质心作为我们视觉教科书的元素。

还有更多…

我们从图像的各个点提取特征,计算提取特征的值频率,并根据找到的频率对图像进行分类,这是一种类似于在向量空间中表示文档的技术。这是一个向量量化过程,我用它创建一个字典来离散化特征空间的可能值。

参见

使用极端随机森林训练图像分类器

一个物体识别系统使用图像分类器将图像分类到已知类别。极端随机森林ERFs)在机器学习领域非常受欢迎,因为它们的速度和准确性。该算法基于决策树。与经典决策树相比,它们的区别在于树分割点的选择。通过为随机选择的每个特征创建随机子划分,并选择这些子划分之间的最佳分割,来完成将节点样本分割成两组的最佳分割。

准备工作

在这个菜谱中,我们将使用 ERFs 来训练我们的图像分类器。我们基本上基于我们的图像特征构建决策树,然后训练森林以做出正确的决策。

如何操作...

让我们看看如何使用 ERFs 训练图像分类器:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的trainer.py文件中给出):
import argparse 
import _pickle as pickle

import numpy as np
from sklearn.ensemble import ExtraTreesClassifier
from sklearn import preprocessing
  1. 定义一个参数解析器:
def build_arg_parser():
    parser = argparse.ArgumentParser(description='Trains the classifier')
    parser.add_argument("--feature-map-file", dest="feature_map_file", required=True,
            help="Input pickle file containing the feature map")
    parser.add_argument("--model-file", dest="model_file", required=False,
            help="Output file where the trained model will be stored")
    return parser
  1. 定义一个类来处理 ERF 训练。我们将使用标签编码器来编码我们的训练标签:
class ERFTrainer(object):
    def __init__(self, X, label_words):
        self.le = preprocessing.LabelEncoder() 
        self.clf = ExtraTreesClassifier(n_estimators=100, 
                max_depth=16, random_state=0) 
  1. 对标签进行编码并训练分类器:
        y = self.encode_labels(label_words) 
        self.clf.fit(np.asarray(X), y) 
  1. 定义一个函数来编码标签:
def encode_labels(self, label_words): 
    self.le.fit(label_words)  
    return np.array(self.le.transform(label_words), dtype=np.float32) 
  1. 定义一个函数来分类未知数据点:
def classify(self, X): 
    label_nums = self.clf.predict(np.asarray(X)) 
    label_words = self.le.inverse_transform([int(x) for x in label_nums])  
    return label_words 
  1. 定义main函数并解析输入参数:
if __name__=='__main__': 
    args = build_arg_parser().parse_args() 
    feature_map_file = args.feature_map_file 
    model_file = args.model_file
  1. 加载我们在上一个菜谱中创建的特征图:
    # Load the feature map 
    with open(feature_map_file, 'rb') as f: 
        feature_map = pickle.load(f) 
  1. 提取特征向量:
    # Extract feature vectors and the labels
    label_words = [x['object_class'] for x in feature_map]
    dim_size = feature_map[0]['feature_vector'].shape[1] 
    X = [np.reshape(x['feature_vector'], (dim_size,)) for x in feature_map]
  1. 基于训练数据训练 ERF:
    # Train the Extremely Random Forests classifier 
    erf = ERFTrainer(X, label_words)  
  1. 按如下方式保存训练好的 ERF 模型:
    if args.model_file: 
        with open(args.model_file, 'wb') as f: 
            pickle.dump(erf, f) 
  1. 现在,你应该在终端中运行代码:
    $ python trainer.py --feature-map-file feature_map.pkl 
    --model-file erf.pkl

这将生成一个名为erf.pkl的文件。我们将在下一个菜谱中使用此文件。

它是如何工作的...

在这个菜谱中,我们使用了 ERFs 来训练我们的图像分类器。首先,我们定义了一个参数解析器函数和一个处理 ERF 训练的类。我们使用标签编码器来编码我们的训练标签。然后,我们加载了在使用视觉代码簿和矢量量化创建特征菜谱中获得的特征图。因此,我们提取了特征向量和标签,最后我们训练了 ERF 分类器。

更多内容…

为了训练图像分类器,使用了sklearn.ensemble.ExtraTreesClassifier函数。此函数构建了一个超随机树分类器。

相关内容

构建对象识别器

在之前的菜谱使用超随机森林训练图像分类器中,我们使用了 ERFs 来训练我们的图像分类器。现在我们已经训练了一个 ERF 模型,让我们继续构建一个能够识别未知图像内容的对象识别器。

准备工作

在这个菜谱中,我们将学习如何使用训练好的 ERF 模型来识别未知图像的内容。

如何操作...

让我们看看如何构建对象识别器:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的object_recognizer.py文件中):
import argparse 
import _pickle as pickle

import cv2

import build_features as bf
from trainer import ERFTrainer
  1. 定义参数解析器:
def build_arg_parser():
    parser = argparse.ArgumentParser(description='Extracts features \
            from each line and classifies the data')
    parser.add_argument("--input-image", dest="input_image", required=True,
            help="Input image to be classified")
    parser.add_argument("--model-file", dest="model_file", required=True,
            help="Input file containing the trained model")
    parser.add_argument("--codebook-file", dest="codebook_file", 
            required=True, help="Input file containing the codebook")
    return parser
  1. 定义一个类来处理图像标签提取函数:
class ImageTagExtractor(object):
    def __init__(self, model_file, codebook_file):
        with open(model_file, 'rb') as f:
            self.erf = pickle.load(f)

        with open(codebook_file, 'rb') as f:
            self.kmeans, self.centroids = pickle.load(f)
  1. 定义一个函数,使用训练好的 ERF 模型来预测输出:
    def predict(self, img, scaling_size):
        img = bf.resize_image(img, scaling_size)
        feature_vector = bf.BagOfWords().construct_feature(
                img, self.kmeans, self.centroids)
        image_tag = self.erf.classify(feature_vector)[0]
        return image_tag
  1. 定义main函数并加载输入图像:
if __name__=='__main__':
    args = build_arg_parser().parse_args()
    model_file = args.model_file
    codebook_file = args.codebook_file
    input_image = cv2.imread(args.input_image)
  1. 适当缩放图像,如下所示:
    scaling_size = 200 
  1. 在终端上打印输出:
    print("Output:", ImageTagExtractor(model_file, 
            codebook_file).predict(input_image, scaling_size))
  1. 现在,你应该按照以下方式运行代码:
$ python object_recognizer.py --input-image imagefile.jpg --model-file erf.pkl --codebook-file codebook.pkl

工作原理...

在这个菜谱中,我们使用了一个训练好的 ERF 模型来识别未知图像的内容。为此,我们使用了前两个菜谱中讨论的算法,即使用视觉代码簿和矢量量化创建特征使用超随机森林训练图像分类器

更多内容…

随机森林是一个由许多决策树组成的聚合分类器,输出与单个树类输出相对应的类别。随机森林的诱导算法由 Leo Breiman 和 Adele Cutler 开发。它基于创建一组广泛的分类树,每棵树都旨在对单个植物进行分类,该植物任何性质的特征都已被评估。比较森林中每棵树提供的分类建议,将植物归类的类别是获得最多指示或投票的类别。

参见

使用 Light GBM 进行图像分类

梯度提升在回归和分类问题中用于生成一系列弱预测模型的预测模型,通常是决策树。这种方法类似于提升方法,并对其进行了推广,允许优化任意可微分的loss函数。

轻量梯度提升机LightGBM)是梯度提升的一种特定变体,经过一些修改使其特别有利。它基于分类树,但在每一步选择分割叶子节点的方式更为有效。

准备工作

在这个菜谱中,我们将学习如何使用 LightGBM 对手写数字进行分类。为此,我们将使用修改后的国家标准与技术研究院MNIST)数据集。这是一个包含大量手写数字的大型数据库。它包含 70,000 个数据示例。它是 NIST 更大数据集的一个子集。这些数字具有 28 x 28 像素的分辨率,并存储在一个 70,000 行和 785 列的矩阵中;784 列形成 28 x 28 矩阵中每个像素的值,一个值是实际的数字。这些数字已被尺寸归一化并居中在固定大小的图像中。

如何做...

让我们看看如何使用 LightGBM 进行图像分类:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的LightgbmClassifier.py文件中给出):
import numpy as np
import lightgbm as lgb
from sklearn.metrics import mean_squared_error
from keras.datasets import mnist
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
  1. 要导入mnist数据集,必须使用以下代码:
(XTrain, YTrain), (XTest, YTest) = mnist.load_data()

返回以下元组:

  • XTrainXTest:一个形状为(num_samples2828)的灰度图像数据的uint8数组

  • YTrainYTest:一个形状为(num_samples)的数字标签(0-9 范围内的整数)的uint8数组

  1. 因此,每个样本图像由一个 28 x 28 的矩阵组成。为了降低维度,我们将 28 x 28 的图像展平成大小为 784 的向量:
XTrain = XTrain.reshape((len(XTrain), np.prod(XTrain.shape[1:])))
XTest = XTest.reshape((len(XTest), np.prod(XTest.shape[1:]))) 
  1. 现在,我们将从包含 0 到 9 数字的数据集中提取,但只提取前两个(0 和 1),因为我们想构建一个二元分类器。为此,我们将使用numpy.where函数:
TrainFilter = np.where((YTrain == 0 ) | (YTrain == 1))
TestFilter = np.where((YTest == 0) | (YTest == 1))

XTrain, YTrain = XTrain[TrainFilter], YTrain[TrainFilter]
XTest, YTest = XTest[TestFilter], YTest[TestFilter]
  1. 我们为lightgbm创建了一个数据集:
LgbTrain = lgb.Dataset(XTrain, YTrain)
LgbEval = lgb.Dataset(XTest, YTest, reference=LgbTrain)
  1. 现在,我们必须将模型参数指定为字典:
Parameters = {
    'boosting_type': 'gbdt',
    'objective': 'binary',
    'metric': 'binary_logloss',
    'num_leaves': 31,
    'learning_rate': 0.05,
    'feature_fraction': 0.9,
    'bagging_fraction': 0.8,
    'bagging_freq': 5,
    'verbose': 0
}
  1. 让我们训练模型:
gbm = lgb.train(Parameters,
                LgbTrain,
                num_boost_round=10,
                valid_sets=LgbTrain)
  1. 我们的模式现在准备好了,我们可以用它来自动分类手写数字。为此,我们将使用predict()方法:
YPred = gbm.predict(XTest, num_iteration=gbm.best_iteration)
YPred = np.round(YPred)
YPred = YPred.astype(int)
  1. 现在,我们可以评估模型:
print('Rmse of the model is:', mean_squared_error(YTest, YPred) ** 0.5)

返回以下结果:

Rmse of the model is: 0.05752992848417943
  1. 要更详细地分析二分类中犯的错误,我们需要计算混淆矩阵:
ConfMatrix = confusion_matrix(YTest, YPred)
print(ConfMatrix)

返回以下结果:

[[ 978 2]
 [ 5 1130]]
  1. 最后,我们将计算模型的准确率:
print(accuracy_score(YTest, YPred))

返回以下结果:

0.9966903073286052

因此,该模型能够以高精度对手写数字的图像进行分类。

它是如何工作的...

在这个食谱中,我们使用了 LightGBM 来分类手写数字。LightGBM 是梯度提升的一种特定变体,经过一些修改使其特别有利。它基于分类树,但在每一步选择分裂叶子节点的方式更为有效。

当提升操作在深度上生长树时,LightGBM 通过结合两个标准来做出这个选择:

  • 基于梯度下降的优化

  • 为了避免过拟合问题,设置了最大深度的限制

这种增长方式被称为叶式

还有更多...

Light GBM 有许多优点:

  • 所展示的过程平均比类似算法快一个数量级。这是因为它没有完全生长树,而且还利用了变量的分箱(将变量分成子组的过程,既为了加快计算速度,也是一种正则化方法)。

  • 更经济的内存使用:分箱过程涉及较少的内存使用。

  • 与常规提升算法相比,准确率更高:因为它使用叶式过程,得到的树更复杂。同时,为了避免过拟合,对最大深度进行了限制。

  • 该算法易于并行化。

参见

第十一章:生物识别人脸识别

在本章中,我们将涵盖以下食谱:

  • 从网络摄像头捕获和处理视频

  • 使用 Haar 级联构建人脸检测器

  • 构建眼鼻检测器

  • 执行主成分分析

  • 执行核主成分分析

  • 执行盲源分离

  • 使用局部二值模式直方图构建人脸识别器

  • 使用基于 HOG 的模型识别人脸

  • 面部特征点识别

  • 通过人脸识别进行用户身份验证

技术要求

为了处理本章中的食谱,你需要以下文件(可在 GitHub 上找到):

  • video_capture.py

  • face_detector.py

  • eye_nose_detector.py

  • pca.py

  • kpca.py

  • blind_source_separation.py

  • mixture_of_signals.txt

  • face_recognizer.py

  • FaceRecognition.py

  • FaceLandmarks.py

  • UserAuthentification.py

简介

人脸识别指的是在给定图像中识别一个人的任务。这与人脸检测不同,人脸检测是在给定图像中定位人脸。在人脸检测过程中,我们不在乎这个人是谁;我们只是识别包含人脸的图像区域。因此,在典型的生物识别人脸识别系统中,在识别之前,我们需要确定人脸的位置。

人脸识别对人类来说非常容易。我们似乎毫不费力地就能做到,而且我们一直在做!我们如何让机器做到同样的事情呢?我们需要了解我们可以用哪些面部特征来唯一地识别一个人。我们的大脑似乎有一种内部结构,能够对特定的特征做出反应,例如边缘、角点、运动等。人类视觉皮层将这些特征组合成一个单一的连贯推理。如果我们想让我们的机器以准确率识别人脸,我们需要以类似的方式制定问题。我们需要从输入图像中提取特征,并将它们转换为有意义的表示。

从网络摄像头捕获和处理视频

网络摄像头当然不是一项创新技术;它们的出现可以追溯到 20 世纪 90 年代初,从那时起,由于视频聊天程序、街景摄像头和宽带互联网连接的普及,它们越来越受欢迎。目前,网络摄像头是每个人的对象,并且几乎总是集成在显示器框架中,如台式机、上网本和笔记本电脑。网络摄像头最常用的用途是传输视频流和录制。

在第一种情况下,网络摄像头用于视频聊天程序、电视广播和街景摄像头——这些摄像头拍摄特定位置的固定点。在第二种情况下,网络摄像头用于创建可以上传到互联网的照片和视频,例如 YouTube 或社交网站。这些类型网络摄像头的优点是,它们可以取代更传统的相机使用方式,即使它们的视频质量较差。

准备工作

在这个菜谱中,我们将使用网络摄像头来捕获视频数据。让我们看看如何使用 OpenCV-Python 从网络摄像头捕获视频片段。

如何做到这一点...

让我们看看如何按照以下步骤捕获和处理网络摄像头的视频:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的video_capture.py文件中给出):
import cv2 
  1. OpenCV 提供了一个可以用来从网络摄像头捕获图像的视频捕获对象。0输入参数指定了网络摄像头的 ID。如果你连接了一个 USB 摄像头,那么它将有一个不同的 ID:
# Initialize video capture object 
cap = cv2.VideoCapture(0) 
  1. 定义使用网络摄像头捕获的帧的缩放因子。
scaling_factor = 0.5 
  1. 启动一个无限循环并持续捕获帧,直到你按下Esc键。从网络摄像头读取帧:
# Loop until you hit the Esc key 
while True: 
    # Capture the current frame 
    ret, frame = cap.read() 
  1. 调整帧大小是可选的,但在你的代码中仍然是一个有用的功能:
    frame = cv2.resize(frame, None, fx=scaling_factor, fy=scaling_factor,  
            interpolation=cv2.INTER_AREA) 
  1. 显示帧:
    cv2.imshow('Webcam', frame)
  1. 在捕获下一帧之前等待 1 毫秒:
    c = cv2.waitKey(1) 
    if c == 27: 
        break 
  1. 释放视频捕获对象:
cap.release() 
  1. 在退出代码之前关闭所有活动窗口:
cv2.destroyAllWindows() 

如果你运行此代码,你将看到网络摄像头的视频。

它是如何工作的...

在这个菜谱中,我们使用了网络摄像头通过 OpenCV-Python 捕获视频数据。为此,执行了以下操作:

  1. 初始化视频捕获对象。

  2. 定义图像大小缩放因子。

  3. 循环直到你按下Esc键:

    1. 捕获当前帧。

    2. 调整帧大小。

    3. 显示图像。

    4. 检测是否按下了Esc键。

  4. 释放视频捕获对象。

  5. 关闭所有活动窗口。

更多内容...

OpenCV 提供了一个非常简单的接口来捕获网络摄像头的实时流。要捕获视频,你需要创建一个VideoCapture对象。它的参数可以是设备索引或视频文件的名称。然后我们可以逐帧获取它们。然而,我们不要忘记释放捕获。

参见

使用 Haar 级联构建人脸检测器

正如我们之前讨论的,人脸检测是确定输入图像中人脸位置的过程。在这个菜谱中,我们将使用Haar 级联进行人脸检测。这是通过在多个尺度上从图像中提取许多简单特征来实现的。这些简单特征是边缘、线和矩形特征,它们很容易计算。然后通过创建简单分类器的级联来训练它们。

准备工作

在这个菜谱中,我们将学习如何确定由我们的网络摄像头捕获的视频帧中人脸的位置。这个过程使用了自适应增强技术来提高其鲁棒性。

如何做到这一点...

让我们看看如何使用 Haar 级联构建人脸检测器:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的face_detector.py文件中给出):
import cv2 
import numpy as np  
  1. 加载人脸检测器级联文件。这是一个我们可以用作检测器的训练模型:
face_cascade = cv2.CascadeClassifier('cascade_files/haarcascade_frontalface_alt.xml')
  1. 检查级联文件是否正确加载:
if face_cascade.empty(): 
    raise IOError('Unable to load the face cascade classifier xml file')
  1. 创建视频捕获对象:
cap = cv2.VideoCapture(0) 
  1. 定义图像下采样的缩放因子:
scaling_factor = 0.5 
  1. 继续循环,直到按下Esc键:
# Loop until you hit the Esc key 
while True: 
    # Capture the current frame and resize it 
    ret, frame = cap.read() 
  1. 调整帧大小:
    frame = cv2.resize(frame, None, fx=scaling_factor, fy=scaling_factor,  
            interpolation=cv2.INTER_AREA) 
  1. 将图像转换为灰度图。我们需要灰度图像来运行人脸检测器:
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 
  1. 在灰度图像上运行人脸检测器。1.3参数指的是每个阶段的缩放乘数。5参数指的是每个候选矩形应该具有的最小邻居数,以便我们保留它。这个候选矩形基本上是一个可能检测到人脸的潜在区域:
    face_rects = face_cascade.detectMultiScale(gray, 1.3, 5) 
  1. 在每个检测到的人脸区域周围绘制矩形:
    for (x,y,w,h) in face_rects: 
        cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0), 3) 
  1. 显示输出图像:
    cv2.imshow('Face Detector', frame)
  1. 在进行下一次迭代之前等待 1 毫秒。如果用户按下 Esc 键,则跳出循环:
    c = cv2.waitKey(1) 
    if c == 27: 
        break
  1. 在退出代码之前释放和销毁对象:
cap.release() 
cv2.destroyAllWindows() 

如果你运行此代码,你将在摄像头视频中看到被检测到的人脸。

它是如何工作的...

在这个配方中,我们学习了如何确定由摄像头捕获的视频帧中的人脸位置。为此,执行了以下操作:

  1. 加载人脸级联文件。

  2. 检查人脸级联文件是否已加载。

  3. 初始化视频捕获对象。

  4. 定义缩放因子。

  5. 循环直到按下Esc键:

    1. 捕获当前帧并调整其大小。

    2. 转换为灰度图。

    3. 在灰度图像上运行人脸检测器。

    4. 在图像上绘制矩形。

    5. 显示图像。

    6. 检查是否按下了Esc键。

  6. 释放视频捕获对象并关闭所有窗口。

还有更多...

Haar 级联是一种基于机器学习的方法,其中通过许多正负图像训练级联函数。然后,它被用来检测其他图像中的对象。

参见

构建眼睛和鼻子检测器

在之前的配方中,使用 Haar 级联构建人脸检测器,我们使用了 Haar 级联方法来检测由摄像头捕获的视频帧中的人脸位置。这种方法可以扩展到检测所有类型的对象。这正是我们将在这里讨论的内容。

准备工作

在这个配方中,我们将看到如何使用 Haar 级联方法来检测输入视频中的人脸的眼睛和鼻子。

如何做到这一点...

让我们看看我们如何构建眼睛和鼻子检测器:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的eye_nose_detector.py文件中给出):
import cv2 
import numpy as np 
  1. 加载人脸、眼睛和鼻子级联文件:
# Load face, eye, and nose cascade files 
face_cascade = cv2.CascadeClassifier('cascade_files/haarcascade_frontalface_alt.xml') 
eye_cascade = cv2.CascadeClassifier('cascade_files/haarcascade_eye.xml') 
nose_cascade = cv2.CascadeClassifier('cascade_files/haarcascade_mcs_nose.xml')
  1. 检查文件是否正确加载:
# Check if face cascade file has been loaded 
if face_cascade.empty(): 
    raise IOError('Unable to load the face cascade classifier xml file') 

# Check if eye cascade file has been loaded 
if eye_cascade.empty(): 
    raise IOError('Unable to load the eye cascade classifier xml file') 

# Check if nose cascade file has been loaded 
if nose_cascade.empty(): 
    raise IOError('Unable to load the nose cascade classifier xml file') 
  1. 初始化视频捕获对象:
# Initialize video capture object and define scaling factor 
cap = cv2.VideoCapture(0) 
  1. 定义缩放因子:
scaling_factor = 0.5 
  1. 保持循环,直到用户按下Esc键:
while True: 
    # Read current frame, resize it, and convert it to grayscale 
    ret, frame = cap.read() 
  1. 调整帧大小:
    frame = cv2.resize(frame, None, fx=scaling_factor, fy=scaling_factor,  
            interpolation=cv2.INTER_AREA) 
  1. 将图像转换为灰度:
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 
  1. 在灰度图像上运行人脸检测器:
    # Run face detector on the grayscale image 
    faces = face_cascade.detectMultiScale(gray, 1.3, 5)
  1. 由于我们知道面孔总是有眼睛和鼻子,我们只能在面部区域运行这些检测器:
    # Run eye and nose detectors within each face rectangle 
    for (x,y,w,h) in faces: 
  1. 提取面部 ROI:
        # Grab the current ROI in both color and grayscale images 
        roi_gray = gray[y:y+h, x:x+w] 
        roi_color = frame[y:y+h, x:x+w] 
  1. 运行眼睛检测器:
        # Run eye detector in the grayscale ROI 
        eye_rects = eye_cascade.detectMultiScale(roi_gray) 
  1. 运行鼻子检测器:
        # Run nose detector in the grayscale ROI 
        nose_rects = nose_cascade.detectMultiScale(roi_gray, 1.3, 5) 
  1. 在眼睛周围画圆圈:
        # Draw green circles around the eyes 
        for (x_eye, y_eye, w_eye, h_eye) in eye_rects: 
            center = (int(x_eye + 0.5*w_eye), int(y_eye + 0.5*h_eye)) 
            radius = int(0.3 * (w_eye + h_eye)) 
            color = (0, 255, 0) 
            thickness = 3 
            cv2.circle(roi_color, center, radius, color, thickness) 
  1. 在鼻子上画一个矩形:
        for (x_nose, y_nose, w_nose, h_nose) in nose_rects: 
            cv2.rectangle(roi_color, (x_nose, y_nose), (x_nose+w_nose,  
                y_nose+h_nose), (0,255,0), 3) 
            break 
  1. 显示图像:
    # Display the image 
    cv2.imshow('Eye and nose detector', frame)
  1. 在进行下一次迭代之前等待 1 毫秒。如果用户按下Esc键,则中断循环:
    # Check if Esc key has been pressed 
    c = cv2.waitKey(1) 
    if c == 27: 
        break 
  1. 在退出代码之前释放和销毁对象:
# Release video capture object and close all windows 
cap.release() 
cv2.destroyAllWindows() 

如果你运行此代码,你将看到网络摄像头视频中检测到的人的眼睛和鼻子。

工作原理…

在这个菜谱中,我们学习了如何在输入视频中检测人的眼睛和鼻子。为此,已经执行了以下操作:

  1. 加载面部、眼睛和鼻子级联文件。

  2. 检查面部、眼睛和鼻子级联文件是否已加载。

  3. 初始化视频捕获对象并定义缩放因子。

  4. 在帧上循环:

    1. 读取当前帧,调整大小,并将其转换为灰度。

    2. 在灰度图像上运行人脸检测器。

    3. 在每个面部矩形内运行眼睛和鼻子检测器。

    4. 显示图像。

    5. 检查是否按下了Esc键。

  5. 释放视频捕获对象并关闭所有窗口。

更多内容…

在网络摄像头中识别面部元素对于识别主题可能很有用。全局视觉信息和局部特征(眼睛和鼻子的形态)在面部感知和识别中是基本的。事实上,关于面部识别的研究表明,男性更容易识别具有突出特征的面孔,如鹰钩鼻、眯眼等。

相关内容

执行主成分分析

主成分分析(PCA)是一种常用于计算机视觉和机器学习的降维技术。当我们处理具有高维度的特征时,训练机器学习系统变得过于昂贵。因此,在训练系统之前,我们需要降低数据的维度。然而,当我们降低维度时,我们不想丢失数据中存在的信息。这就是 PCA 发挥作用的地方!PCA 识别数据的重要成分,并按重要性顺序排列。

准备工作

在这个菜谱中,我们将看到如何对输入数据进行主成分分析(PCA)。

如何操作…

让我们看看如何对一些输入数据进行 PCA:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的pca.py文件中给出):
import numpy as np 
from sklearn import decomposition  
  1. 让我们为我们的输入数据定义五个维度。前两个维度将是独立的,但接下来的三个维度将依赖于前两个维度。这基本上意味着我们可以没有最后三个维度,因为它们不会给我们提供任何新的信息:
# Define individual features 
x1 = np.random.normal(size=250) 
x2 = np.random.normal(size=250) 
x3 = 3*x1 + 2*x2
x4 = 6*x1 - 2*x2
x5 = 3*x3 + x4
  1. 让我们创建具有这些特征的数据库:
# Create dataset with the above features 
X = np.c_[x1, x3, x2, x5, x4] 
  1. 创建一个 PCA 对象:
# Perform Principal Component Analysis 
pca = decomposition.PCA() 
  1. 在输入数据上拟合 PCA 模型:
pca.fit(X) 
  1. 打印维度的方差:
# Print variances 
variances = pca.explained_variance_ 
print('Variances in decreasing order:\n', variances)
  1. 如果某个维度是有用的,那么它将为方差提供一个有意义的值。让我们设置一个阈值并识别重要的维度:
# Find the number of useful dimensions 
thresh_variance = 0.8 
num_useful_dims = len(np.where(variances > thresh_variance)[0]) 
print('Number of useful dimensions:', num_useful_dims)
  1. 正如我们之前讨论的那样,PCA 已经确定在这个数据集中只有两个维度是重要的:
# As we can see, only the 2 first components are useful 
pca.n_components = num_useful_dims 
  1. 让我们将数据集从五维集转换为二维集:
XNew = pca.fit_transform(X)
print('Shape before:', X.shape)
print('Shape after:', XNew.shape)
  1. 如果你运行这段代码,你将在你的终端上看到以下内容:
Variances in decreasing order:
[2.77392134e+02 1.51557851e+01 9.54279881e-30 7.73588070e-32 9.89435444e-33]
 Number of useful dimensions: 2
Shape before: (250, 5)
Shape after: (250, 2) 

如我们所见,前两个成分包含了模型的所有方差。

它是如何工作的...

PCA 生成了一组新的变量,其中包含不相关的变量,也称为主成分。每个主成分是原始变量的线性组合。所有主成分都是相互正交的,因此没有冗余信息。

主成分作为一个整体构成了数据空间的正交基。PCA 的目标是用最少的几个主成分解释最大的方差。PCA 是一种多维缩放,其中变量被线性变换到低维空间,从而保留关于变量的最大信息量。因此,主成分是原始变量经过线性变换后的组合。

还有更多…

方差衡量一组数字从它们的平均值偏离的程度。它表示各个值从算术平均值偏差的平方的平均值。

参见

执行核主成分分析

PCA 擅长减少维度数量,但它以线性方式工作。如果数据不是以线性方式组织的,PCA 将无法完成所需的工作。这就是核 PCA 出现的地方。

准备工作

在这个菜谱中,我们将看到如何对输入数据进行核 PCA,并将结果与 PCA 在相同数据上的表现进行比较。

如何操作...

让我们看看我们如何执行核 PCA:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的kpca.py文件中给出):
import numpy as np 
import matplotlib.pyplot as plt 

from sklearn.decomposition import PCA, KernelPCA 
from sklearn.datasets import make_circles 
  1. 定义随机数生成器的seed值。这是生成分析数据样本所需的:
# Set the seed for random number generator 
np.random.seed(7) 
  1. 生成分布在内切圆中的数据以展示 PCA 在这种情况下不起作用:
# Generate samples 
X, y = make_circles(n_samples=500, factor=0.2, noise=0.04)
  1. 对这些数据进行 PCA 分析:
# Perform PCA 
pca = PCA() 
X_pca = pca.fit_transform(X) 
  1. 对这些数据进行核 PCA 分析:
# Perform Kernel PCA 
kernel_pca = KernelPCA(kernel="rbf", fit_inverse_transform=True, gamma=10) 
X_kernel_pca = kernel_pca.fit_transform(X) 
X_inverse = kernel_pca.inverse_transform(X_kernel_pca) 
  1. 绘制原始输入数据:
# Plot original data 
class_0 = np.where(y == 0) 
class_1 = np.where(y == 1) 
plt.figure() 
plt.title("Original data") 
plt.plot(X[class_0, 0], X[class_0, 1], "ko", mfc='none') 
plt.plot(X[class_1, 0], X[class_1, 1], "kx") 
plt.xlabel("1st dimension") 
plt.ylabel("2nd dimension") 
  1. 绘制 PCA 转换后的数据:
# Plot PCA projection of the data 
plt.figure() 
plt.plot(X_pca[class_0, 0], X_pca[class_0, 1], "ko", mfc='none') 
plt.plot(X_pca[class_1, 0], X_pca[class_1, 1], "kx") 
plt.title("Data transformed using PCA") 
plt.xlabel("1st principal component") 
plt.ylabel("2nd principal component") 
  1. 绘制核 PCA 转换后的数据:
# Plot Kernel PCA projection of the data 
plt.figure() 
plt.plot(X_kernel_pca[class_0, 0], X_kernel_pca[class_0, 1], "ko", mfc='none') 
plt.plot(X_kernel_pca[class_1, 0], X_kernel_pca[class_1, 1], "kx") 
plt.title("Data transformed using Kernel PCA") 
plt.xlabel("1st principal component") 
plt.ylabel("2nd principal component")
  1. 使用核方法将数据转换回原始空间,以显示逆变换是保持不变的:
# Transform the data back to original space 
plt.figure() 
plt.plot(X_inverse[class_0, 0], X_inverse[class_0, 1], "ko", mfc='none') 
plt.plot(X_inverse[class_1, 0], X_inverse[class_1, 1], "kx") 
plt.title("Inverse transform") 
plt.xlabel("1st dimension") 
plt.ylabel("2nd dimension") 

plt.show() 
  1. 完整的代码在提供的kpca.py文件中给出,供您参考。如果您运行此代码,您将看到四个图。第一个图是原始数据:

第二个图展示了使用 PCA 转换后的数据:

第三个图展示了使用核 PCA 转换后的数据。注意图中点在右侧是如何聚集的:

第四个图展示了数据逆变换回原始空间:

它是如何工作的...

核主成分分析核 PCA)基于 PCA,同时使用核方法的技术。在 PCA 中,原始的线性 PCA 操作是在再生核 Hilbert 空间中执行的。

核方法是一类用于分析和模式方案的算法,其中最著名的元素是 SVMs。核方法通过将数据映射到多维特征空间来解决一个问题,在这个空间中,每个坐标对应于元素数据的特征,将数据转换为一组欧几里得空间点。由于映射可以是通用的(例如,不一定是线性的),因此以这种方式找到的关系因此非常通用。

还有更多...

核方法以核函数命名,核函数用于在特征空间上操作,而不必在空间中计算数据坐标,而是通过在函数空间中计算所有数据副本的图像之间的内积。核方法通常比显式计算坐标的计算成本低。核技巧将这种方法称为问题解决

参考内容

进行盲源分离

盲源分离指的是从混合信号中分离信号的过程。假设有一组不同的信号发生器生成信号,一个共同的接收器接收所有这些信号。现在,我们的任务是利用这些信号的特性,从混合中分离这些信号。我们将使用独立成分分析ICA)来实现这一点。

准备工作

在这个菜谱中,我们将使用.txt文件中的数据,使用ICA来分离其中的信号。

如何操作...

让我们看看我们如何进行盲源分离:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的blind_source_separation.py文件中):
import numpy as np 
import matplotlib.pyplot as plt 
from sklearn.decomposition import PCA, FastICA  
  1. 我们将使用提供的mixture_of_signals.txt文件中的数据。让我们加载数据:
# Load data 
input_file = 'mixture_of_signals.txt' 
X = np.loadtxt(input_file) 
  1. 创建 ICA 对象:
# Compute ICA 
ica = FastICA(n_components=4) 
  1. 根据 ICA 重建信号:
# Reconstruct the signals 
signals_ica = ica.fit_transform(X) 
  1. 提取混合矩阵:
# Get estimated mixing matrix 
mixing_mat = ica.mixing_   
  1. 进行 PCA 比较:
# Perform PCA  
pca = PCA(n_components=4) 
# Reconstruct signals based on orthogonal components 
signals_pca = pca.fit_transform(X)
  1. 定义一个信号列表以绘制它们:
# Specify parameters for output plots  
models = [X, signals_ica, signals_pca] 
  1. 指定图表的颜色:
colors = ['blue', 'red', 'black', 'green'] 
  1. 绘制输入信号:
# Plotting input signal 
plt.figure() 
plt.title('Input signal (mixture)') 
for i, (sig, color) in enumerate(zip(X.T, colors), 1): 
    plt.plot(sig, color=color) 
  1. 绘制 ICA 分离的信号:
# Plotting ICA signals  
plt.figure() 
plt.title('ICA separated signals') 
plt.subplots_adjust(left=0.1, bottom=0.05, right=0.94,  
        top=0.94, wspace=0.25, hspace=0.45) 
  1. 使用不同颜色的子图绘制:
for i, (sig, color) in enumerate(zip(signals_ica.T, colors), 1): 
    plt.subplot(4, 1, i) 
    plt.title('Signal ' + str(i)) 
    plt.plot(sig, color=color) 
  1. 绘制 PCA 分离的信号:
# Plotting PCA signals   
plt.figure() 
plt.title('PCA separated signals') 
plt.subplots_adjust(left=0.1, bottom=0.05, right=0.94,  
        top=0.94, wspace=0.25, hspace=0.45) 
  1. 在每个子图中使用不同的颜色:
for i, (sig, color) in enumerate(zip(signals_pca.T, colors), 1): 
    plt.subplot(4, 1, i) 
    plt.title('Signal ' + str(i)) 
    plt.plot(sig, color=color) 

plt.show() 

如果你运行此代码,你会看到三个图表。第一个图表展示了输入,它是由信号混合而成的:

图片

第二个图表展示了使用 ICA 分离的信号:

图片

第三张图展示了使用 PCA 分离的信号:

图片

它是如何工作的...

ICA 是一种计算处理方法,用于将多变量信号分离成其加性子分量,假设非高斯信号的来源之间存在相互的统计独立性。这是盲源分离的一个特例。该方法通过最大化估计组件的统计独立性来找到独立成分。

更多内容...

ICA 算法的应用示例在脑电图EEG)领域,但它也被广泛用于从mother.ICA技术中分离出胎儿的心电图ECG)。这可以扩展到对非物理数据的分析,这些数据可以是语义的或语言的。例如,ICA 已被应用于使计算机理解一组新闻列表存档中的讨论主题。

相关内容

使用局部二值模式直方图构建人脸识别器

我们现在准备好构建人脸识别器了。我们需要一个用于训练的人脸数据集,所以我们提供了一个名为faces_dataset的文件夹,其中包含足够用于训练的一小部分图像。这个数据集是www.vision.caltech.edu/Image_Datasets/faces/faces.tar中可用数据集的一个子集。这个数据集包含足够多的图像,我们可以用它们来训练一个人脸识别系统。

我们将使用局部二值模式直方图来构建我们的人脸识别系统。在我们的数据集中,你会看到不同的人。我们的任务是构建一个能够学会将这些人区分开来的系统。当我们看到一张未知图像时,我们的系统会将其分配到现有的某个类别中。

准备工作

在这个菜谱中,我们将看到如何使用局部二值模式直方图构建人脸识别器,并使用人脸数据集来训练模型。

如何做到这一点...

让我们看看如何使用局部二值模式直方图构建人脸识别器:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的face_recognizer.py文件中给出):
import os 

import cv2 
import numpy as np 
from sklearn import preprocessing  
  1. 让我们定义一个类来处理与标签编码相关的所有任务:
# Class to handle tasks related to label encoding 
class LabelEncoder(object): 
  1. 定义一个方法来编码标签。在输入训练数据中,标签由单词表示。然而,我们需要数字来训练我们的系统。此方法将定义一个预处理对象,该对象可以通过维护正向和反向映射以有组织的方式将单词转换为数字:
    # Method to encode labels from words to numbers 
    def encode_labels(self, label_words): 
        self.le = preprocessing.LabelEncoder() 
        self.le.fit(label_words) 
  1. 定义一个方法将单词转换为数字:
    # Convert input label from word to number 
    def word_to_num(self, label_word): 
        return int(self.le.transform([label_word])[0]) 
  1. 定义一个方法将数字转换回原始单词:
    # Convert input label from number to word 
    def num_to_word(self, label_num): 
        return self.le.inverse_transform([label_num])[0] 
  1. 定义一个方法从输入文件夹中提取图像和标签:
# Extract images and labels from input path 
def get_images_and_labels(input_path): 
    label_words = [] 
  1. 递归遍历输入文件夹并提取所有图像路径:
    # Iterate through the input path and append files 
    for root, dirs, files in os.walk(input_path): 
        for filename in (x for x in files if x.endswith('.jpg')): 
            filepath = os.path.join(root, filename) 
            label_words.append(filepath.split('/')[-2])
  1. 初始化变量:
    # Initialize variables 
    images = [] 
    le = LabelEncoder() 
    le.encode_labels(label_words) 
    labels = [] 
  1. 解析输入目录以进行训练:
    # Parse the input directory 
    for root, dirs, files in os.walk(input_path): 
        for filename in (x for x in files if x.endswith('.jpg')): 
            filepath = os.path.join(root, filename) 
  1. 以灰度格式读取当前图像:
            # Read the image in grayscale format 
            image = cv2.imread(filepath, 0)  
  1. 从文件夹路径中提取标签:
            # Extract the label 
            name = filepath.split('/')[-2] 
  1. 在此图像上执行人脸检测:
            # Perform face detection 
            faces = faceCascade.detectMultiScale(image, 1.1, 2, minSize=(100,100)) 
  1. 提取 ROI 并返回它们,以及标签编码器:
            # Iterate through face rectangles 
            for (x, y, w, h) in faces: 
                images.append(image[y:y+h, x:x+w]) 
                labels.append(le.word_to_num(name)) 

    return images, labels, le 
  1. 定义主函数和面部级联文件的路径:
if __name__=='__main__': 
    cascade_path = "cascade_files/haarcascade_frontalface_alt.xml" 
    path_train = 'faces_dataset/train' 
    path_test = 'faces_dataset/test'
  1. 加载人脸级联文件:
    # Load face cascade file 
    faceCascade = cv2.CascadeClassifier(cascade_path) 
  1. 为人脸识别器对象创建局部二值模式直方图:
    # Initialize Local Binary Patterns Histogram face recognizer 
    recognizer = cv2.face.createLBPHFaceRecognizer() 
  1. 提取此输入路径的图像、标签和标签编码器:
    # Extract images, labels, and label encoder from training dataset 
    images, labels, le = get_images_and_labels(path_train) 
  1. 使用我们提取的数据训练人脸识别器:
    # Train the face recognizer  
    print "\nTraining..." 
    recognizer.train(images, np.array(labels)) 
  1. 在未知数据上测试人脸识别器:
    # Test the recognizer on unknown images 
    print '\nPerforming prediction on test images...' 
    stop_flag = False 
    for root, dirs, files in os.walk(path_test): 
        for filename in (x for x in files if x.endswith('.jpg')): 
            filepath = os.path.join(root, filename) 
  1. 加载图像:
            # Read the image 
            predict_image = cv2.imread(filepath, 0) 
  1. 使用人脸检测器确定人脸的位置:
            # Detect faces 
            faces = faceCascade.detectMultiScale(predict_image, 1.1,  
                    2, minSize=(100,100)) 
  1. 对于每个面部 ROI,运行人脸识别器:
            # Iterate through face rectangles 
            for (x, y, w, h) in faces: 
                # Predict the output 
                predicted_index, conf = recognizer.predict( 
                        predict_image[y:y+h, x:x+w])
  1. 将标签转换为单词:
                # Convert to word label 
                predicted_person = le.num_to_word(predicted_index) 
  1. 在输出图像上叠加文本并显示:
                # Overlay text on the output image and display it 
                cv2.putText(predict_image, 'Prediction: ' + predicted_person,  
                        (10,60), cv2.FONT_HERSHEY_SIMPLEX, 2, (255,255,255), 6) 
                cv2.imshow("Recognizing face", predict_image) 
  1. 检查用户是否按下了Esc键。如果是,则跳出循环:
            c = cv2.waitKey(0) 
            if c == 27: 
                stop_flag = True 
                break 

        if stop_flag: 
            break 

如果你运行此代码,你将得到一个输出窗口,显示测试图像的预测输出。你可以按空格按钮以保持循环。测试图像中有三个人。

它是如何工作的...

本地二值模式直方图算法基于一个非参数算子,该算子综合了图像的局部结构。在特定的像素点,LBP 算子将该像素与属于考虑的邻域像素之间的颜色强度比较的有序二进制序列关联起来。特别是,如果中心像素的强度大于或等于相邻像素的强度,则分配一个值为 1。否则,分配 0。因此,对于 8 像素的邻域,例如,将有 2⁸种可能的组合。

还有更多...

要将此算子应用于面部识别问题,想法是将图像划分为m个局部区域,并从每个区域中提取直方图。要提取的特征向量由这些局部直方图的连接组成。

参见

使用基于 HOG 的模型进行面部识别

通过面部识别,我们指的是返回图像中存在的人脸位置的过程。在使用 Haar 级联构建面部检测器的配方中,我们已经讨论了这个问题。在这个配方中,我们将使用face_recognition库对这些面部执行一系列操作。

面部识别的主要目标是检测面部特征,忽略围绕它的所有其他内容。这是许多商业设备上的一个特性,它允许你确定何时以及如何在一个图像中应用焦点,以便你可以捕捉到它。在计算机视觉的世界里,通常将面部检测算法家族分为两大类。这两个类别之间的区别在于它们对信息的不同使用,这些信息来自对面部结构和特性的先验知识:

  • 第一类包括基于提取规格特征的算法

  • 第二类采用全局方法进行图像分析

准备中

在这个菜谱中,我们将了解如何使用face_recognition库从复杂图像中执行面部识别。在继续之前,请安装face_recognition库。这个库基于dlib库,我们必须在继续之前安装它。dlib是一个现代的 C++工具包,它包含机器学习算法和用于在 C++中创建复杂软件以解决现实世界问题的工具。您可以在pypi.org/project/face_recognition/找到有关安装该包的信息。

如何做到这一点...

让我们看看如何使用基于 HOG 的模型来识别面部:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的FaceRecognition.py文件中给出):
from PIL import Image
import face_recognition

Python 图像库PIL)是 Python 编程语言的免费库,它增加了打开、操作和保存许多不同图像文件格式的支持。face_recognition是一个 Python 库,可以从 Python 脚本或命令行中识别和操作面部。

  1. 让我们将family.jpg文件加载到 NumPy 数组中:
image = face_recognition.load_image_file("family.jpg")
  1. 我们现在将使用默认的基于 HOG 的模型在图像中找到所有的面部:
face_locations = face_recognition.face_locations(image) 
  1. 定义一种将单词转换为数字的方法:
print("Number {} face(s) recognized in this image.".format(len(face_locations)))
  1. 打印图像中每个面部位置:
for face_location in face_locations:

    top, right, bottom, left = face_location
    print("Face location Top: {}, Left: {}, Bottom: {}, Right: {}".format(top, left, bottom, right))
  1. 最后,我们需要访问实际的面部本身:
    face_image = image[top:bottom, left:right]
    pil_image = Image.fromarray(face_image)
    pil_image.show()

将返回每个识别出的面部的缩略图。

它是如何工作的...

方向梯度直方图HOG)是一种用于物体识别的特征描述符。该算法计算图像局部区域中梯度方向的出现的次数。与其他用于相同目的的技术(尺度不变特征变换、边缘方向直方图、形状上下文)不同,因为它使用均匀分布的密集网格单元,并使用局部叠加归一化来提高准确性。

还有更多...

首次介绍这项技术的是 Navneet Dalal 和 Bill Triggs(2005),他们是国家信息与自动化研究所INRIA)的研究员,当时他们正在研究静态图像中行人检测的问题。

参见

面部特征点识别

由于其方向性,面部识别也很复杂。同一个面部,从观察者的不同方向看去,可能会让算法将其识别为不同的面部。为了解决这个问题,我们可以使用面部特征点,这些点位于面部上的特定位置,如眼睛、眉毛、嘴唇、鼻子等。通过使用这种技术,你可以在任何面部上识别多达 68 个点。

准备工作

在这个菜谱中,我们将看到如何提取面部特征作为面部特征点。

如何做到这一点...

让我们看看如何执行面部特征点识别:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的FaceLandmarks.py文件中给出):
from PIL import Image, ImageDraw
import face_recognition
  1. 让我们将ciaburro.jpg文件加载到 NumPy 数组中:
image = face_recognition.load_image_file("ciaburro.jpg")
  1. 让我们在图像中的所有面部中找到所有面部特征:
FaceLandmarksList = face_recognition.face_landmarks(image)
  1. 打印图像中识别到的面部数量:
print("Number {} face(s) recognized in this image.".format(len(FaceLandmarksList)))

返回以下结果:

Number 1 face(s) recognized in this image
  1. 创建一个 PIL imagedraw 对象,以便我们可以在图片上绘制:
PilImage = Image.fromarray(image)
DrawPilImage = ImageDraw.Draw(PilImage)
  1. 在这一点上,我们将插入一个循环,返回列表中每个面部特征的点位置,并在图像上绘制线条:
for face_landmarks in FaceLandmarksList:
  1. 首先,我们打印出此图像中每个面部特征的位置:
    for facial_feature in face_landmarks.keys():
        print("{} points: {}".format(facial_feature, face_landmarks[facial_feature]))
  1. 然后我们用线条在图像中追踪每个面部特征:
    for facial_feature in face_landmarks.keys():
        DrawPilImage.line(face_landmarks[facial_feature], width=5)
  1. 最后,我们绘制带有突出显示特征点的图像:
PilImage.show()

在以下图像中,我们可以看到输入图像和带有突出显示特征点的图像:

图片

此外,特征点的位置如下打印:

图片

它是如何工作的...

在这个菜谱中,我们学习了如何从图像中提取面部特征点,以及如何在同一图像上绘制这些点。以下特征点被检测到:

  • 下巴

  • 左眉毛

  • 右眉毛

  • 鼻梁

  • 鼻尖

  • 左眼

  • 右眼

  • 上嘴唇

  • 下嘴唇

对于检测到的每个特征,连接检测点的线条被绘制出来以显示轮廓。

更多内容...

为了提取面部特征点,使用了face_recognition库。这个库通过使用 Vahid Kazemi 和 Josephine Sullivan 在以下论文中介绍的方法来完成这项任务:One Millisecond Face Alignment with an Ensemble of Regression Trees。为了估计面部特征点的位置,使用了一个回归树集成。

参见

通过面部识别进行用户身份验证

基于面部识别的认证技术已经是一个巩固的现实了几十年。如果我们如此体贴地经常更换它,我们不再需要携带口袋卡,存储在手机上,或使用记忆术来记住每次都不同的一个。我们需要做的是验证我们已有的东西。为此,我们只需看看我们的网络摄像头。基于面部识别的识别系统试图通过将刚刚获取的面部图像与数据库中现有的图像进行比较来识别一个人,以找到可能的对应关系。这会导致允许或禁止访问。

准备工作

在这个菜谱中,我们将看到如何使用face_recognition库构建基于面部识别的识别系统。

如何做...

让我们看看如何通过使用面部识别来执行用户认证:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的UserAuthentification.py文件中给出):
import face_recognition
  1. 让我们将所有图像文件加载到 NumPy 数组中:
Image1 = face_recognition.load_image_file("giuseppe.jpg")
Image2 = face_recognition.load_image_file("tiziana.jpg")
UnknownImage = face_recognition.load_image_file("tiziana2.jpg")

已加载三张图像:前两张图像指的是我们之前看到的面孔,而第三张是要比较的图像(tiziana)。

  1. 获取每个图像文件中每个面孔的面部编码:
try:
    Image1Encoding = face_recognition.face_encodings(Image1)[0]
    Image2Encoding = face_recognition.face_encodings(Image2)[0]
    UnknownImageEncoding = face_recognition.face_encodings(UnknownImage)[0]
except IndexError:
    print("Any face was located. Check the image files..")
    quit()
  1. 让我们定义已知的面孔:
known_faces = [
    Image1Encoding,
    Image2Encoding
]
  1. 让我们比较已知面孔与我们刚刚加载的未知面孔:
results = face_recognition.compare_faces(known_faces, UnknownImageEncoding)
  1. 最后,我们将打印比较的结果:
print("Is the unknown face a picture of Giuseppe? {}".format(results[0]))
print("Is the unknown face a picture of Tiziana? {}".format(results[1]))
print("Is the unknown face a new person that we've never seen before? {}".format(not True in results))

返回以下结果:

Is the unknown face a picture of Giuseppe? False
Is the unknown face a picture of Tiziana? True
Is the unknown face a new person that we've never seen before? False

如我们所见,认证系统识别用户为 Tiziana。

它是如何工作的...

在这个菜谱中,我们学习了如何构建基于面部识别的识别系统。为此,我们从已知数据库中的每个面孔中提取了一些基本度量。通过这样做,我们能够将这些基本度量与其他需要认证的面孔的基本度量进行比较。

这些度量是使用深度卷积神经网络进行的。学习过程通过同时分析三张图像来工作:

  • 包含已知人员面孔的图像(锚点)

  • 同一已知人员的另一张图像(正面)

  • 一个完全不同的人的图像(负面)

在这个阶段,算法会检查为这三张图像各自生成的度量。然后它调整神经网络的权重,以确保为面孔 1 和 2 生成的度量稍微接近一些,而面孔 2 和 3 的度量稍微远一些。这种技术被称为Triplet Loss

更多...

到目前为止,我们说过,基于机器学习的算法成功的关键在于学习阶段使用的示例数量。数量越多,模型的准确性就越高。在我们本章处理的情况中,这不能被认为是有效的。这是因为,在面部识别算法中,我们可用的示例非常有限。

因此,典型的卷积神经网络的结构和形成将不会工作,因为它无法使用现有数据量学习所需的功能。在这些情况下,我们采用单次学习方法,其中我们构建一个相似度函数,该函数比较两个图像并告诉你是否存在匹配。

参见

第十二章:强化学习技术

在本章中,我们将涵盖以下食谱:

  • 使用 MDP 进行天气预报

  • 使用 DP 优化金融投资组合

  • 寻找最短路径

  • 使用 Q 学习决定折现因子

  • 实现深度 Q 学习算法

  • 开发基于 AI 的动态建模系统

  • 基于 Double Q 学习的深度强化学习

  • 带有对抗性 Q 学习的深度 Q 网络算法

技术要求

为了处理本章中的食谱,你需要以下文件(可在 GitHub 上找到):

  • MarkovChain.py

  • KPDP.py

  • DijkstraNX.py

  • FrozenQlearning.py

  • FrozenDeepQLearning.py

  • dqn_cartpole.py

  • DoubleDQNCartpole.py

  • DuelingDQNCartpole.py

简介

强化学习代表了一类能够学习和适应环境变化的算法。它基于算法选择的外部刺激的概念。正确的选择将导致奖励,而错误的选择将导致惩罚。系统的目标是实现最佳结果。

在监督学习中,正确的输出是明确指定的(有教师指导的学习)。但并非总是可能这样做。通常,我们只有定性信息。可用的信息被称为强化信号。在这些情况下,系统不会提供有关如何更新智能体行为的任何信息(例如,权重)。无法定义成本函数或梯度。系统的目标是创建能够从经验中学习的智能体。

在下面的屏幕截图中,我们可以看到一个流程图,显示了强化学习与环境之间的交互:

下面是正确应用强化学习算法的步骤:

  1. 智能体的准备

  2. 环境观察

  3. 选择最优策略

  4. 执行动作

  5. 计算相应的奖励(或惩罚)

  6. 更新策略的开发(如有必要)

  7. 重复步骤 2 到 5,直到智能体学习到最优策略

强化学习试图最大化执行动作或动作集以实现目标所获得的奖励。

使用 MDP 进行天气预报

为了避免负载问题和计算困难,将智能体-环境交互视为一个马尔可夫决策过程MDP)。MDP 是一个离散时间随机控制过程。

随机过程是用于研究遵循随机或概率定律的现象演化的数学模型。众所周知,在所有自然现象中,无论是由于其本质还是由于观测误差,都存在一个随机或偶然的成分。

这个组件导致以下情况:在t的每一个实例中,对现象的观察结果是随机数或随机变量,st。不可能确定结果会是什么;你只能声明它将取几个可能值中的一个,每个值都有给定的概率。

当观察到一个特定的t实例时,如果随机过程的演变,从t开始,只依赖于t而不依赖于任何先前的实例,则称该随机过程为马尔可夫。因此,当给定观察时刻时,只有这个实例决定了过程的未来演变,而这种演变不依赖于过去。

准备工作

在这个菜谱中,我们想要构建一个统计模型来预测天气。为了简化模型,我们假设只有两种状态:晴天和雨天。让我们进一步假设我们已经进行了一些计算,并发现明天的时刻某种程度上基于今天的时间。

如何操作...

让我们看看如何使用 MDP 进行天气预报:

  1. 我们将使用已经为你提供的MarkovChain.py文件作为参考。首先,我们导入numpytimematplotlib.pyplot包:
import numpy as np
import time
from matplotlib import pyplot
  1. 让我们设置随机数生成器的种子和天气状态:
np.random.seed(1)
states = ["Sunny","Rainy"]
  1. 在这一点上,我们必须定义天气条件的可能转移:
TransStates = [["SuSu","SuRa"],["RaRa","RaSu"]]
TransnMatrix = [[0.75,0.25],[0.30,0.70]]
  1. 然后,我们插入以下检查以验证我们没有在定义转移矩阵时出错:
if sum(TransnMatrix[0])+sum(TransnMatrix[1]) != 2:
     print("Warning! Probabilities MUST ADD TO 1\. Wrong transition matrix!!")
     raise ValueError("Probabilities MUST ADD TO 1")
  1. 让我们设置初始条件:
WT = list()
NumberDays = 200
WeatherToday = states[0]
print("Weather initial condition =",WeatherToday)
  1. 现在,我们可以预测NumberDays变量设置的每一天的天气条件。为此,我们将使用以下while循环:
i = 0
while i < NumberDays:
    if WeatherToday == "Sunny":
    TransWeather = np.random.choice(TransStates[0],replace=True,p=TransnMatrix[0])
        if TransWeather == "SuSu":
            pass
        else:
            WeatherToday = "Rainy"
    elif WeatherToday == "Rainy":
        TransWeather = np.random.choice(TransStates[1],replace=True,p=TransnMatrix[1])
        if TransWeather == "RaRa":
            pass
        else:
            WeatherToday = "Sunny"
    print(WeatherToday)
    WT.append(WeatherToday)
    i += 1    
    time.sleep(0.2)

它由一个控制条件和循环体组成。在循环的入口处以及每次执行循环体内的所有指令后,都要验证控制条件的有效性。当由布尔表达式组成的条件返回false时,循环结束。

  1. 在这一点上,我们已经为未来 200 天生成了预报。让我们使用以下代码绘制图表:
pyplot.plot(WT)
pyplot.show()

下面的图显示了从晴天开始的未来 200 天的天气条件:

初看之下,似乎晴天比雨天多。

它是如何工作的...

马尔可夫链是随机现象的数学模型,该现象随时间演变,过去只通过现在影响未来。换句话说,随机模型描述了一系列可能的事件,其中每个事件的概率只依赖于前一个事件达到的状态。因此,马尔可夫链具有无记忆性。

因此,马尔可夫链的结构完全由以下转移矩阵表示:

转移概率矩阵的性质直接来源于组成它们的元素的本质。

还有更多…

将马尔可夫链通过转移矩阵描述的一个非常直观的替代方法是将其与一个有向图(转移图)关联。转移矩阵和转移图提供了关于同一马尔可夫链的相同信息。

参见

使用动态规划优化金融投资组合

金融投资组合的管理是一种旨在以最能代表投资者需求的方式组合金融产品的活动。这需要评估各种特征的整体评估,例如风险偏好、预期回报和投资者消费,以及未来回报和风险的估计。动态规划DP)代表了一组算法,可以在形式为 MDP 的环境完美模型下计算最优策略。DP 的基本思想,以及强化学习的一般思想,是使用状态值和动作来寻找好的策略。

准备工作

在这个菜谱中,我们将解决背包问题:一个小偷进入一栋房子,想要偷走贵重物品。他们将它们放入背包中,但受到重量的限制。每个对象都有自己的价值和重量。他们必须选择有价值但重量不过重的对象。小偷不能超过背包的重量限制,同时,他们必须优化他们的收益。

如何做到这一点…

让我们看看我们如何使用动态规划优化金融投资组合:

  1. 我们将使用已经为你提供的KPDP.py文件作为参考。此算法从定义一个KnapSackTable()函数开始,该函数将选择满足问题所提出的两个约束条件的对象的最优组合:对象的总重量等于 10,以及所选对象的最大值,如下面的代码所示:
def KnapSackTable(weight, value, P, n):
T = [[0 for w in range(P + 1)]
for i in range(n + 1)]
  1. 然后,我们在所有对象和所有重量值上设置一个迭代循环,如下所示:
for i in range(n + 1):
    for w in range(P + 1):
        if i == 0 or w == 0:
            T[i][w] = 0
        elif weight[i - 1] <= w:
            T[i][w] = max(value[i - 1]
                + T[i - 1][w - weight[i - 1]],
                        T[i - 1][w])
        else:
            T[i][w] = T[i - 1][w]
  1. 现在,我们可以记住获得的结果,这代表了可以装入背包的对象的最大价值,如下所示:
res = T[n][P]
print("Total value: " ,res)
  1. 我们迄今为止遵循的程序并没有表明哪个子集提供了最优解。我们必须使用一种集合程序来提取这个信息:
w = P
totweight=0
for i in range(n, 0, -1):
    if res <= 0:
        break
  1. 如果当前元素与上一个元素相同,我们将继续下一个,如下所示:
if res == T[i - 1][w]:
    continue
  1. 如果它们不相同,那么当前对象将被包含在背包中,并将打印出此项目,如下所示:
else:
    print("Item selected: ",weight[i - 1],value[i - 1])
    totweight += weight[i - 1]
    res = res - value[i - 1]
    w = w - weight[i – 1]
  1. 最后,打印出包含的总重量,如下所示:
print("Total weight: ",totweight)

这样,我们就定义了一个函数,允许我们构建表格。

  1. 现在,我们必须定义输入变量并将它们传递给函数,如下所示:
objects = [(5, 18),(2, 9), (4, 12), (6,25)]
print("Items available: ",objects)
print("***********************************")
  1. 在这一点上,我们需要从对象中提取权重和变量值。我们将它们放入一个单独的数组中,以便更好地理解步骤,如下所示:
value = []
weight = []
for item in objects:
    weight.append(item[0])
    value.append(item[1])
  1. 最后,设置背包可以携带的总重量和可用物品的数量,如下所示:
P = 10
n = len(value)
  1. 最后,我们打印出结果:
KnapSackTable(weight, value, P, n)
The following results are returned:
Items available: [(5, 18), (2, 9), (4, 12), (6, 25)]
*********************************
Total value: 37
Item selected: 6 25
Item selected: 4 12
Total weight: 10

动态规划算法使我们能够获得最优解,从而节省了计算成本。

它是如何工作的...

例如,考虑找到连接两个位置的最佳路径的问题。最优性原理指出,它包含的每个子路径,在任意中间位置和最终位置之间,必须依次是最优的。基于这个原理,动态规划通过一次做出一个决策来解决问题。在每一步,都会确定未来的最佳策略,而不考虑过去的决策(它是一个马尔可夫过程),假设后者也是最优的。

还有更多...

动态规划是一种更有效地解决递归问题的技术。为什么是这样呢?在递归过程中,我们通常会反复解决子问题。在动态规划中,这种情况不会发生:我们记住这些子问题的解决方案,这样我们就不必再次解决它们。这被称为备忘录化。如果变量的值在给定步骤上依赖于先前计算的结果,并且如果相同的计算反复进行,那么存储中间结果以避免重复计算昂贵的计算是有利的。

参考以下内容

寻找最短路径

给定一个加权图和一个指定的顶点 X,我们通常会需要找到从 X 到图中每个其他顶点的路径。识别连接图中的两个或多个节点的路径在许多其他离散优化问题中表现为子问题,并且在现实世界中也有许多应用。

准备工作

在本食谱中,我们将使用Dijkstra算法找到两点之间的最短路径。我们还将使用networkx包在 Python 中表示图。

如何做到这一点…

让我们看看我们如何找到最短路径:

  1. 我们将使用已经提供的DijkstraNX.py文件作为参考。首先,我们导入我们将在这里使用的库:
import networkx as nx
import matplotlib.pyplot as plt
  1. 然后,创建了一个图对象并添加了顶点:
G = nx.Graph()
G.add_node(1)
G.add_node(2)
G.add_node(3)
G.add_node(4)
  1. 随后,添加了加权边:
G.add_edge(1, 2, weight=2)
G.add_edge(2, 3, weight=2)
G.add_edge(3, 4, weight=3)
G.add_edge(1, 3, weight=5)
G.add_edge(2, 4, weight=6)
  1. 在这一点上,我们已经通过给边添加带有权重指示的标签来绘制了图:
pos = nx.spring_layout(G, scale=3)
nx.draw(G, pos,with_labels=True, font_weight='bold')
edge_labels = nx.get_edge_attributes(G,'r')
nx.draw_networkx_edge_labels(G, pos, labels = edge_labels)
plt.show()

为了做到这一点,使用了draw_networkx_edge_labels函数。以下图表显示了这一结果:

  1. 最后,计算从节点一到四的最短路径:
print(nx.shortest_path(G,1,4,weight='weight'))
  1. shortest_path函数计算图中节点之间的最短路径和路径长度。以下结果是:
[1, 2, 3, 4]
  1. 最后,计算了最短路径的长度:
print(nx.nx.shortest_path_length(G,1,4,weight='weight'))

以下结果是:

7

如我们所验证的,我们已经获得了相同的结果。

它是如何工作的…

Dijkstra 算法能够解决从源点s到所有节点的最短路径问题。算法维护一个标签d(i)到节点,表示节点 i 的最短路径长度的上界。

在每一步中,算法将节点集V划分为两个集合:永久标记节点的集合和仍然临时标记的节点的集合。永久标记节点的距离代表从源点到这些节点的最短路径距离,而临时标签包含一个可以大于或等于最短路径长度的值。

还有更多…

算法的基本思想是从源点开始,尝试永久标记后续节点。一开始,算法将源点的距离值设为零,并将其他距离初始化为任意高值(按照惯例,我们将距离的初始值设为:d[i] = + ∞, ∀i ∈ V)。

在每次迭代中,节点标签 i 是包含除了 i 之外只有永久标记节点的源点到路径上的最小距离的值。算法选择标签值最低的临时标记节点,将其永久标记,并更新其相邻节点的所有标签。当所有节点都被永久标记时,算法终止。

参考以下内容

使用 Q 学习决定折扣因子

Q 学习是使用最广泛的强化学习算法之一。这得益于它能够比较可用动作的预期效用,而无需环境模型。多亏了这项技术,我们可以在完成 MDP 的每个给定状态下找到最优动作。

强化学习问题的一般解决方案是通过学习过程估计一个评估函数。这个函数必须能够通过奖励的总和来评估特定策略的便利性或不利性。实际上,Q 学习试图最大化 Q 函数(动作值函数)的值,它表示当我们执行动作a在状态s时,最大化的折现未来奖励。

准备工作

在这个菜谱中,我们将通过提供一个基于 Q 学习的第一个解决方案来处理在网格世界中控制角色移动的问题。

如何做到这一点...

让我们看看我们如何使用 Q 学习来决定折扣因子:

  1. 我们将使用已经提供的FrozenQlearning.py文件作为参考。让我们先导入库:
import gym
import numpy as np
  1. 然后,我们将继续创建环境,通过调用make方法:
env = gym.make('FrozenLake-v0')

此方法创建我们的智能体将运行的 环境。

  1. 现在,让我们初始化参数,从QTable开始:
QTable = np.zeros([env.observation_space.n,env.action_space.n])
  1. 让我们定义一些参数:
alpha = .80
gamma = .95
NumEpisodes = 2000

在这里,alpha是学习率,gamma是折扣因子,NumEpisodes是剧集数。

  1. 现在,我们将创建一个列表来包含总奖励:
RewardsList = []
  1. 在这一点上,在设置参数后,可以开始 Q 学习周期:
for i in range(NumEpisodes):
    CState = env.reset()
    SumReward = 0
    d = False
    j = 0
    while j < 99:
        j+=1
        Action = np.argmax(QTable[CState,:] + np.random.randn(1,env.action_space.n)*(1./(i+1)))
        NState,Rewards,d,_ = env.step(Action)
        QTable[CState,Action] = QTable[CState,Action] + alpha*(Rewards + gamma*np.max(QTable[NState,:]) - QTable[CState,Action])
        SumReward += Rewards
        CState = NState
        if d == True:
            break

    RewardsList.append(SumReward)

在每个剧集结束时,奖励列表将增加一个新值。

  1. 最后,我们打印结果:
print ("Score: " +  str(sum(RewardsList)/NumEpisodes))
print ("Final Q-Table Values")
print (QTable)

下面的截图显示了最终的 Q 表:

为了提高结果,需要调整配置参数的回退。

它是如何工作的…

FrozenLake环境是一个 4 × 4 的网格,包含四个可能区域:安全S)、冰冻F)、H)和目标G)。智能体控制网格世界中角色的移动,并在网格中移动直到达到目标或洞。网格中的一些方格是可通行的,而其他方格会导致智能体掉入水中。如果掉入洞中,它必须从头开始,并得到 0 的奖励。此外,智能体将移动的方向是不确定的,并且仅部分取决于所选方向。如果智能体找到一个可通行的路径到达目标方格,它将得到奖励。智能体有四种可能的移动:上、下、左和右。这个过程会持续进行,直到它从每个错误中学习并达到目标。

更多内容...

Q-learning 通过增量估计函数值 q(s, a),在环境的每个步骤中更新状态-动作对的值,遵循更新时间差分方法估计值的一般公式的逻辑。Q-learning 具有离线策略特性;也就是说,虽然策略是根据 q(s, a) 估计的值来改进的,但值函数会根据一个严格贪婪的次级策略来更新估计:给定一个状态,选择的行为总是最大化 max q(s, a) 值的那个行为。然而,π 策略在估计值方面起着重要作用,因为通过它确定了要访问和更新的状态-动作对。

参考以下内容

实现深度 Q-learning 算法

深度 Q-learning 代表了基本 Q-learning 方法的演变。状态-动作被神经网络所取代,目的是逼近最优值函数。与 Q-learning 方法相比,它被用来构建网络以请求输入和动作,并提供其期望回报,深度 Q-learning 革新了结构,只请求环境的状态,并提供尽可能多的状态-动作值,这些值对应于环境中可以执行的动作数量。

准备工作

在这个菜谱中,我们将使用深度 Q-learning 方法来控制网格世界中角色的移动。在这个菜谱中,将使用 keras-rl 库;要了解更多信息,请参考《开发基于 AI 的动态建模系统》菜谱。

如何操作...

让我们看看如何实现深度 Q-learning 算法:

  1. 我们将使用提供的 FrozenDeepQLearning.py 文件作为参考。让我们首先导入库:
import gym
import numpy as np
from keras.models import Sequential
from keras.layers.core import Dense, Reshape
from keras.layers.embeddings import Embedding
from keras.optimizers import Adam
from rl.agents.dqn import DQNAgent
from rl.policy import BoltzmannQPolicy
from rl.memory import SequentialMemory
  1. 然后,我们将定义环境和设置种子:
ENV_NAME = 'FrozenLake-v0'
env = gym.make(ENV_NAME)
np.random.seed(1)
env.seed(1)
  1. 现在,我们将提取智能体可用的动作:
Actions = env.action_space.n

Actions 变量现在包含在所选环境中可用的所有动作。Gym 不会总是告诉你这些动作的含义,但只会告诉你哪些动作是可用的。

  1. 现在,我们将使用 keras 库构建一个简单的神经网络模型:
model = Sequential()
model.add(Embedding(16, 4, input_length=1))
model.add(Reshape((4,)))
print(model.summary())

现在,神经网络模型已经准备好使用,所以让我们配置和编译我们的智能体。使用 DQN 的问题之一是,算法中使用的神经网络倾向于忘记先前的经验,因为它用新的经验覆盖了它们。

  1. 因此,我们需要一个包含先前经验和观察结果列表来用先前经验重新构建模型。为此,定义了一个将包含先前经验的内存变量,并设置了一个策略:
memory = SequentialMemory(limit=10000, window_length=1)
policy = BoltzmannQPolicy()
  1. 我们只需要定义智能体:
Dqn = DQNAgent(model=model, nb_actions=Actions,
               memory=memory, nb_steps_warmup=500,
               target_model_update=1e-2, policy=policy,
               enable_double_dqn=False, batch_size=512
               )
  1. 让我们继续编译和拟合模型:
Dqn.compile(Adam())
Dqn.fit(env, nb_steps=1e5, visualize=False, verbose=1, log_interval=10000)
  1. 训练结束时,有必要保存获得的权重:
Dqn.save_weights('dqn_{}_weights.h5f'.format(ENV_NAME), overwrite=True)
  1. 最后,我们将对算法进行 20 个回合的评估:
Dqn.test(env, nb_episodes=20, visualize=False)

我们的智能体现在能够识别出通往目标的路径。

它是如何工作的…

强化学习问题的一般解决方案是,通过学习过程估计一个评估函数。这个函数必须能够通过奖励的总和来评估特定策略的便利性或不利性。实际上,Q 学习试图最大化Q函数(动作值函数)的值,它表示在状态s中执行动作a时的最大折现未来奖励。DQN 代表了基本 Q 学习方法的演变,其中状态-动作被神经网络取代,目的是逼近最优值函数。

还有更多…

OpenAI Gym 是一个帮助我们实现基于强化学习算法的库。它包括一个不断增长的基准问题集合,这些问题提供了一个公共接口,以及一个网站,人们可以在那里分享他们的结果并比较算法性能。

OpenAI Gym 专注于强化学习的回合设置。换句话说,智能体的经验被划分为一系列回合。智能体的初始状态由一个分布随机采样,交互过程一直进行,直到环境达到终端状态。这个程序在每个回合中重复进行,目的是最大化每个回合的总奖励期望,并在尽可能少的回合内达到高水平的表现。

参考以下内容

开发基于 AI 的动态建模系统

Segway是一种利用计算机科学、电子学和机械学创新组合的个人交通工具。它作为身体的延伸;就像舞伴一样,能够预测每一个动作。其工作原理基于反向摆系统。反向摆系统是控制理论和研究文献中常见的例子。它的流行部分原因在于它没有控制是不稳定的,并且具有非线性动态,但更重要的是,它有多个实际应用,例如控制火箭的起飞或 Segway。

准备工作

在这个配方中,我们将分析由连接到车上的刚性杆组成的物理系统的功能,使用不同的方法来模拟系统。杆通过一个铰链连接到车架上,可以自由地围绕它旋转。这个被称为反向摆的机械系统是控制理论中的经典问题。

如何做…

让我们看看我们如何开发一个基于 AI 的动态建模系统:

  1. 我们将使用已经为你提供的dqn_cartpole.py文件作为参考。让我们先导入库:
import numpy as np
import gym
from keras.models import Sequential
from keras.layers import Dense, Activation, Flatten
from keras.optimizers import Adam
from rl.agents.dqn import DQNAgent
from rl.policy import BoltzmannQPolicy
from rl.memory import SequentialMemory
  1. 现在,我们将定义和加载环境:
ENV_NAME = 'CartPole-v0'
env = gym.make(ENV_NAME)
  1. 为了设置seed值,使用了 NumPy 库的random.seed()函数,如下所示:
np.random.seed(123)
env.seed(123)
  1. 现在,我们将提取代理可用的动作:
nb_actions = env.action_space.n
  1. 我们将使用keras库构建一个简单的神经网络模型:
model = Sequential()
model.add(Flatten(input_shape=(1,) + env.observation_space.shape))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(nb_actions))
model.add(Activation('linear'))
print(model.summary())
  1. 将设置一个memory变量和一个policy
memory = SequentialMemory(limit=50000, window_length=1)
policy = BoltzmannQPolicy()
  1. 我们只需要定义代理:
dqn = DQNAgent(model=model, nb_actions=nb_actions, memory=memory, nb_steps_warmup=10,
               target_model_update=1e-2, policy=policy)
  1. 让我们继续编译和拟合模型:
dqn.compile(Adam(lr=1e-3), metrics=['mae'])
dqn.fit(env, nb_steps=1000, visualize=True, verbose=2)
  1. 在训练结束时,有必要保存获得的权重:
dqn.save_weights('dqn_{}_weights.h5f'.format(ENV_NAME), overwrite=True)

网络或整个结构的权重保存发生在HDF5文件中,这是一个高效且灵活的存储系统,支持复杂的多维数据集。

  1. 最后,我们将对算法进行 10 个回合的评估:
dqn.test(env, nb_episodes=5, visualize=True)

它是如何工作的…

在这个配方中,我们使用了keras–rl包来开发一个基于 AI 的动态建模系统。这个包在 Python 中实现了某些深度强化学习算法,并且与 Keras 的深度学习库无缝集成。

此外,keras-rl可以立即与 OpenAI Gym 一起工作。OpenAI Gym 包括一个不断增长的基准问题集合,展示了通用的接口和网站,人们可以在那里分享他们的结果并比较算法性能。这个库将在下一章中适当介绍——现在,我们将限制自己使用它。

还有更多…

这些选择并不限制keras-rl包的使用,从keras-rl的使用可以很容易地适应我们的需求。你可以使用内置的 Keras 回调和指标,或者定义其他的。因此,通过扩展一些简单的抽象类,很容易实现自己的环境,甚至算法。

参考以下内容

双 Q-learning 的深度强化学习

在 Q-learning 算法中,使用与当前股票选择策略相同的 Q 函数评估未来的最大近似动作值。在某些情况下,这可能会高估动作值,从而减慢学习速度。DeepMind 研究人员在以下论文中提出了一个名为Double Q-learning的变体:Deep reinforcement learning with Double Q-learning,H van Hasselt,A Guez,和 D Silver,2016 年 3 月,在第三十届 AAAI 人工智能会议上。作为解决这个问题的方案,作者们提出了修改 Bellman 更新的方法。

准备就绪

在这个菜谱中,我们将使用 Double Q-learning 算法控制一个倒立摆系统。

如何做…

让我们看看如何使用 Double Q-learning 进行深度强化学习:

  1. 我们将使用已经为你提供的DoubleDQNCartpole.py文件作为参考。让我们首先导入库:
import numpy as np
import gym
from keras.models import Sequential
from keras.layers import Dense, Activation, Flatten
from keras.optimizers import Adam
from rl.agents.dqn import DQNAgent
from rl.policy import BoltzmannQPolicy
from rl.memory import SequentialMemory
  1. 现在,我们将定义和加载环境:
ENV_NAME = 'CartPole-v0'
env = gym.make(ENV_NAME)
  1. 要设置seed值,使用 NumPy 库的random.seed()函数,如下所示:
np.random.seed(1)
env.seed(1)
  1. 现在,我们将提取代理可用的动作:
nb_actions = env.action_space.n
  1. 我们将使用keras库构建一个简单的神经网络模型:
model = Sequential()
model.add(Flatten(input_shape=(1,) + env.observation_space.shape))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(nb_actions))
model.add(Activation('linear'))
print(model.summary())
  1. 将设置一个memory变量和一个policy
memory = SequentialMemory(limit=50000, window_length=1)
policy = BoltzmannQPolicy()
  1. 我们只需要定义代理:
dqn = DQNAgent(model=model, nb_actions=nb_actions, memory=memory,   
               nb_steps_warmup=10, enable_double_dqn=True,  
               target_model_update=1e-2,policy=policy)

要启用双网络,我们必须将enable_double_dqn选项设置为True

  1. 让我们继续编译和拟合模型:
dqn.compile(Adam(lr=1e-3), metrics=['mae'])
dqn.fit(env, nb_steps=1000, visualize=True, verbose=2)
  1. 在训练结束时,有必要保存获得的权重:
dqn.save_weights('dqn_{}_weights.h5f'.format(ENV_NAME), overwrite=True)

网络或整个结构的权重保存发生在HDF5文件中,这是一个高效且灵活的存储系统,支持复杂的多元数据集。

  1. 最后,我们将对算法进行 10 个回合的评估:
dqn.test(env, nb_episodes=5, visualize=True)

它是如何工作的…

动作值的高估是由于在 Bellman 方程中使用的最大运算符。最大运算符在选择和评估动作时使用相同的值。现在,如果我们选择具有最大值的最佳动作,我们最终会选择一个次优动作(错误地假设了最大值)而不是最佳动作。我们可以通过有两个独立的 Q 函数来解决这个问题,每个 Q 函数都独立学习。一个 Q1 函数用于选择动作,另一个 Q2 函数用于评估动作。为此,只需更改目标函数。

还有更多…

实质上,以下两个网络被使用:

  • DQN 网络用于选择在下一个状态中采取的最佳动作(具有最高 Q 值的动作)

  • 目标网络,用于计算在下一个状态下执行该动作的目标 Q 值

参见

深度 Q 网络算法与对抗 Q 学习

为了通过使我们的网络架构更接近强化学习的最后挑战之一来提高收敛速度,王等人提出了以下论文中 DQN 模型性能的明显改进:*对抗网络架构用于深度强化学习,Z Wang, T Schaul, M Hessel, H van Hasselt, M Lanctot, and N de Freitas, 2015, arXiv 预印本 arXiv:1511.06581。

准备工作

在这个菜谱中,我们将使用对抗 Q 学习算法控制一个倒立摆系统。

如何做…

让我们看看如何使用对抗 Q 学习执行深度 Q 网络算法:

  1. 我们将使用已提供的DuelingDQNCartpole.py文件作为参考。让我们首先导入库:
import numpy as np
import gym
from keras.models import Sequential
from keras.layers import Dense, Activation, Flatten
from keras.optimizers import Adam
from rl.agents.dqn import DQNAgent
from rl.policy import BoltzmannQPolicy
from rl.memory import SequentialMemory
  1. 现在,我们将定义和加载环境:
ENV_NAME = 'CartPole-v0'
env = gym.make(ENV_NAME)
  1. 要设置seed值,使用 NumPy 库的random.seed()函数,如下所示:
np.random.seed(2)
env.seed(2)
  1. 现在,我们将提取智能体可用的动作:
nb_actions = env.action_space.n
  1. 我们将使用 Keras 库构建一个简单的神经网络模型:
model = Sequential()
model.add(Flatten(input_shape=(1,) + env.observation_space.shape))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(nb_actions))
model.add(Activation('linear'))
print(model.summary())
  1. 将设置一个memory变量和一个policy
memory = SequentialMemory(limit=50000, window_length=1)
policy = BoltzmannQPolicy()

我们只需定义智能体:

dqn = DQNAgent(model=model, nb_actions=nb_actions, memory=memory,
               nb_steps_warmup=10, enable_dueling_network=True,  
               dueling_type='avg',target_model_update=1e-2,
               policy=policy)

为了启用对抗网络,我们必须指定dueling_type为以下之一:'avg''max''naive'

  1. 让我们继续编译和拟合模型:
dqn.compile(Adam(lr=1e-3), metrics=['mae'])
dqn.fit(env, nb_steps=1000, visualize=True, verbose=2)
  1. 训练结束时,有必要保存获得的权重:
dqn.save_weights('dqn_{}_weights.h5f'.format(ENV_NAME), overwrite=True)

网络或整个结构的权重保存发生在HDF5文件中,这是一个高效且灵活的存储系统,支持复杂的多元数据集。

  1. 最后,我们将对算法进行 10 个回合的评估:
dqn.test(env, nb_episodes=5, visualize=True)

它是如何工作的…

在强化学习中,函数 Q 和值函数扮演着基本角色:

  • Q 函数指定了智能体在状态s中执行动作的好坏

  • 值函数指定了智能体处于状态s时的好坏

为了进一步提高 DQN 的性能,我们引入了一个新的函数,称为advantage函数,它可以定义为value函数和benefit函数之间的差异。benefit函数指定了智能体在执行动作方面相对于其他动作的好坏。

因此,价值函数指定了状态的好坏,而优势函数指定了动作的好坏。然后,这两个函数的组合告诉我们代理在某个状态下执行动作的好坏,这就是我们的Q函数。因此,我们可以将我们的Q函数定义为价值函数和优势函数的总和。

对抗式 DQN 本质上是一个 DQN,其中完全连接的最终层被分为两个流:

  • 一个计算价值函数

  • 另一个计算优势函数

最后,使用聚合级别将两个流合并以获得Q函数。

还有更多…

通过神经网络对价值函数的近似远非稳定。为了实现收敛,基本算法应通过引入避免振荡和发散的技术进行修改。

最重要的技术称为经验重放。在剧集期间,在每一步,代理的经验被存储在一个数据集中,称为重放记忆。在算法的内部循环中,不是基于刚刚执行的唯一转换在网络上进行训练,而是从重放记忆中随机选择转换的一个子集,并根据这个转换子集计算出的损失进行训练。

重放技术的经验,即从重放记忆中随机选择转换,消除了连续转换之间的相关性问题,并减少了不同更新之间的方差。

参见

  • 查看 Giuseppe Ciaburro 的《Keras 强化学习项目》,Packt Publishing

  • 更多信息请参阅 《深度强化学习的对抗网络架构》arxiv.org/abs/1511.06581

第十三章:深度神经网络

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

  • 构建感知器

  • 构建单层神经网络

  • 使用深度神经网络构建深度神经网络

  • 创建矢量量化器

  • 构建用于序列数据分析的循环神经网络

  • 可视化 OCR 数据库中的字符

  • 使用神经网络构建光学字符识别器

  • 在人工神经网络中实现优化算法

技术要求

为了处理本章中的食谱,你需要以下文件(可在 GitHub 上获取):

  • perceptron.py

  • single_layer.py

  • data_single_layer.txt

  • deep_neural_network.py

  • vector_quantization.py

  • data_vq.txt

  • recurrent_network.py

  • visualize_characters.py

  • ocr.py

  • IrisClassifier.py

简介

我们的大脑非常擅长识别和识别事物。我们希望机器能够做到同样的。神经网络是一个模仿人类大脑以模拟我们的学习过程的框架。神经网络被设计用来从数据中学习并识别潜在的模式。与所有学习算法一样,神经网络处理数字。因此,如果我们想要实现任何涉及图像、文本、传感器等现实世界任务,我们必须在将它们输入神经网络之前将它们转换为数值格式。我们可以使用神经网络进行分类、聚类、生成和其他许多相关任务。

神经网络由神经元层组成。这些神经元模仿人类大脑中的生物神经元。每一层基本上是一组独立的神经元,它们与相邻层的神经元相连。输入层对应于我们提供的输入数据,输出层包含我们期望的输出。所有介于输入层和输出层之间的层都称为隐藏层。如果我们设计一个具有更多隐藏层的神经网络,那么我们给它更多的自由度,以更高的精度进行自我训练。

假设我们希望神经网络根据我们的需求对数据进行分类。为了使神经网络按预期工作,我们需要提供标记的训练数据。然后神经网络将通过优化成本函数来自我训练。这个成本函数是实际标签与神经网络预测标签之间的误差。我们持续迭代,直到误差低于某个阈值。

深度神经网络究竟是什么?深度神经网络是由许多隐藏层组成的神经网络。通常,这属于深度学习的范畴。这是一个致力于研究这些由多层组成的神经网络的领域,这些网络被用于许多垂直领域。

构建感知器

让我们以一个感知器开始我们的神经网络冒险。感知器是一个执行所有计算的单一神经元。这是一个非常简单的模型,但它是构建复杂神经网络的基石。以下是其外观:

神经元使用不同的权重组合输入,然后添加一个偏置值来计算输出。这是一个简单的线性方程,将输入值与感知器的输出联系起来。

准备工作

在这个菜谱中,我们将使用一个名为neurolab的库来定义一个具有两个输入的感知器。在继续之前,请确保您已安装它。您可以在pythonhosted.org/neurolab/install.html找到安装说明。让我们继续看看如何设计和开发这个神经网络。

如何操作...

让我们看看如何构建一个感知器:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在提供的perceptron.py文件中):
import numpy as np 
import neurolab as nl 
import matplotlib.pyplot as plt 
  1. 定义一些输入数据和相应的标签:
# Define input data 
data = np.array([[0.3, 0.2], [0.1, 0.4], [0.4, 0.6], [0.9, 0.5]]) 
labels = np.array([[0], [0], [0], [1]])
  1. 让我们绘制这些数据以查看数据点的位置:
# Plot input data 
plt.figure() 
plt.scatter(data[:,0], data[:,1]) 
plt.xlabel('X-axis') 
plt.ylabel('Y-axis') 
plt.title('Input data')
  1. 让我们定义一个具有两个输入的perceptron。此函数还需要我们指定输入数据中的最小值和最大值:
# Define a perceptron with 2 inputs; 
# Each element of the list in the first argument  
# specifies the min and max values of the inputs 
perceptron = nl.net.newp([[0, 1],[0, 1]], 1) 
  1. 让我们训练perceptron模型:
# Train the perceptron 
error = perceptron.train(data, labels, epochs=50, show=15, lr=0.01) 
  1. 让我们绘制结果,如下所示:
# plot results 
plt.figure() 
plt.plot(error) 
plt.xlabel('Number of epochs') 
plt.ylabel('Training error') 
plt.grid() 
plt.title('Training error progress') 

plt.show() 

如果您运行此代码,您将看到两个图表。第一个图表显示了输入数据,如下所示:

第二个图表显示了训练错误进度,如下所示:

它是如何工作的...

在这个菜谱中,我们使用了一个执行所有计算的单一神经元。为了训练perceptron,以下参数被设置。epochs的数量指定了通过我们的训练数据集的完整遍历次数。show参数指定了我们希望显示进度的频率。lr参数指定了perceptron的学习率。它是算法在参数空间中搜索时的步长大小。如果这个值很大,那么算法可能会更快地移动,但可能会错过最优值。如果这个值很小,那么算法将击中最优值,但会较慢。因此,这是一个权衡;因此,我们选择0.01的值。

还有更多…

我们可以将perceptron的概念理解为任何接受多个输入并产生一个输出的东西。这是神经网络的最简单形式。1958 年,Frank Rosenblatt 提出了perceptron的概念,它是一个具有输入层和输出层以及旨在最小化错误的训练规则的物体。这个称为误差反向传播的学习函数根据网络的实际输出与给定输入之间的差异,依赖于网络的实际输出和期望输出,来改变连接权重(突触)。

参考以下内容

构建单层神经网络

在之前的配方中,构建感知器,我们学习了如何创建感知器;现在让我们创建一个单层神经网络。单层神经网络由单层中的多个神经元组成。总体而言,我们将有一个输入层、一个隐藏层和一个输出层,如下面的图所示:

图片

准备工作

在这个配方中,我们将学习如何使用neurolab库创建单层神经网络。

如何做到这一点...

让我们看看如何构建一个单层神经网络:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在提供的single_layer.py文件中给出):
import numpy as np 
import matplotlib.pyplot as plt 
import neurolab as nl  
  1. 我们将使用data_single_layer.txt文件中的数据。让我们加载这个文件:
# Define input data 
input_file = 'data_single_layer.txt' 
input_text = np.loadtxt(input_file) 
data = input_text[:, 0:2] 
labels = input_text[:, 2:]
  1. 让我们绘制输入数据:
# Plot input data 
plt.figure() 
plt.scatter(data[:,0], data[:,1]) 
plt.xlabel('X-axis') 
plt.ylabel('Y-axis') 
plt.title('Input data') 
  1. 让我们提取最小值和最大值:
# Min and max values for each dimension 
x_min, x_max = data[:,0].min(), data[:,0].max() 
y_min, y_max = data[:,1].min(), data[:,1].max() 
  1. 让我们定义一个具有隐藏层中两个神经元的单层神经网络:
# Define a single-layer neural network with 2 neurons; 
# Each element in the list (first argument) specifies the  
# min and max values of the inputs 
single_layer_net = nl.net.newp([[x_min, x_max], [y_min, y_max]], 2) 
  1. 训练神经网络 50 个周期:
# Train the neural network 
error = single_layer_net.train(data, labels, epochs=50, show=20, lr=0.01) 
  1. 按如下方式绘制结果:
# Plot results 
plt.figure() 
plt.plot(error) 
plt.xlabel('Number of epochs') 
plt.ylabel('Training error') 
plt.title('Training error progress') 
plt.grid() 
plt.show() 
  1. 让我们在新的测试数据上测试神经网络:
print(single_layer_net.sim([[0.3, 4.5]]))
print(single_layer_net.sim([[4.5, 0.5]]))
print(single_layer_net.sim([[4.3, 8]]))

如果你运行此代码,你将看到两个图表。第一个图表显示输入数据,如下所示:

图片

第二个图表显示训练错误进度,如下所示:

图片

你将在你的终端上看到以下打印信息,指示输入测试点属于何处:

    [[ 0\.  0.]]
    [[ 1\.  0.]]
    [[ 1\.  1.]]

你可以根据我们的标签验证输出是否正确。

它是如何工作的...

单层神经网络具有以下架构:输入形成输入层,执行处理的中间层称为隐藏层,输出形成输出层。隐藏层可以将输入转换为所需的输出。理解隐藏层需要了解权重、偏置和激活函数。

更多内容...

权重对于将输入转换为影响输出的因素至关重要;它们是监控所有神经元如何影响彼此的数值参数。相关的概念类似于线性回归中的斜率,其中权重乘以输入以相加并形成输出。

偏置类似于添加到线性方程中的截距。它也是一个用于调节输出以及神经元输入加权总和的附加参数。

激活函数是一个数学函数,它将输入转换为输出并确定神经元接收的总信号。没有激活函数,神经网络将表现得像线性函数。

参见

构建深度神经网络

我们现在准备好构建一个深度神经网络。深度神经网络由一个输入层、多个隐藏层和一个输出层组成。这看起来如下所示:

图片

上述图表描述了一个具有一个输入层、一个隐藏层和一个输出层的多层神经网络。在深度神经网络中,输入层和输出层之间有许多隐藏层。

准备工作

在这个菜谱中,我们将构建一个深度神经网络。深度学习形成了一个具有众多隐藏层的先进神经网络。深度学习是一个庞大的主题,并且在构建人工智能中是一个重要的概念。在这个菜谱中,我们将使用生成的训练数据和定义一个具有两个隐藏层的多层神经网络。

如何操作...

让我们看看如何构建一个深度神经网络:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在提供的deep_neural_network.py文件中):
import neurolab as nl 
import numpy as np 
import matplotlib.pyplot as plt
  1. 让我们定义参数以生成一些训练数据:
# Generate training data 
min_value = -12 
max_value = 12 
num_datapoints = 90
  1. 这组训练数据将包括我们定义的函数,该函数将转换值。我们期望神经网络能够根据我们提供的输入和输出值自行学习:
x = np.linspace(min_value, max_value, num_datapoints) 
y = 2 * np.square(x) + 7 
y /= np.linalg.norm(y) 
  1. 重新塑形数组:
data = x.reshape(num_datapoints, 1) 
labels = y.reshape(num_datapoints, 1) 
  1. 绘制输入数据:
plt.figure() 
plt.scatter(data, labels) 
plt.xlabel('X-axis') 
plt.ylabel('Y-axis') 
plt.title('Input data') 
  1. 定义一个具有两个隐藏层的深度神经网络,其中每个隐藏层包含 10 个神经元,输出层包含一个神经元:
multilayer_net = nl.net.newff([[min_value, max_value]], [10, 10, 1]) 
  1. 将训练算法设置为梯度下降:
multilayer_net.trainf = nl.train.train_gd 
  1. 训练网络:
error = multilayer_net.train(data, labels, epochs=800, show=100, goal=0.01) 
  1. 预测训练输入的输出以查看性能:
predicted_output = multilayer_net.sim(data)
  1. 绘制训练错误:
plt.figure() 
plt.plot(error) 
plt.xlabel('Number of epochs') 
plt.ylabel('Error') 
plt.title('Training error progress')
  1. 让我们创建一组新的输入并运行神经网络以查看其性能:
x2 = np.linspace(min_value, max_value, num_datapoints * 2) 
y2 = multilayer_net.sim(x2.reshape(x2.size,1)).reshape(x2.size) 
y3 = predicted_output.reshape(num_datapoints) 
  1. 绘制输出:
plt.figure() 
plt.plot(x2, y2, '-', x, y, '.', x, y3, 'p') 
plt.title('Ground truth vs predicted output') 
plt.show() 

如果你运行此代码,你将看到三个图表。第一个图表显示了输入数据,如下所示:

图片

第二个图表显示了训练错误进度,如下所示:

图片

第三个图表显示了神经网络的输出,如下所示:

图片

你将在你的终端上看到以下内容:

Epoch: 100; Error: 4.634764957565494;
Epoch: 200; Error: 0.7675153737786798;
Epoch: 300; Error: 0.21543996465118723;
Epoch: 400; Error: 0.027738499953293118;
Epoch: 500; Error: 0.019145948877988192;
Epoch: 600; Error: 0.11296232736352653;
Epoch: 700; Error: 0.03446237629842832;
Epoch: 800; Error: 0.03022668735279662;
The maximum number of train epochs is reached

它是如何工作的...

在这个菜谱中,我们将使用生成的训练数据来训练一个具有两个隐藏层的多层深度神经网络。为了训练模型,使用了梯度下降算法。梯度下降是一种迭代方法,用于任何学习模型的错误校正。梯度下降方法是一个迭代过程,通过更新权重和偏差的误差乘以激活函数的导数(反向传播)。在这个方法中,最陡下降步长被替换为上一步的类似大小。梯度是曲线的斜率,因为它就是激活函数的导数。

更多内容...

每一步寻找梯度下降的目标是找到全局成本最小值,其中错误最低。这正是模型与数据拟合良好,预测更准确的地方。

参考以下内容

创建一个向量量化器

你也可以使用神经网络进行向量量化。向量量化N维度的舍入。这在计算机视觉、NLP 和机器学习等多个领域都非常常用。

准备工作

在之前的菜谱中,我们已经讨论了向量量化的概念:使用向量量化压缩图像使用视觉代码簿和向量量化创建特征。在这个菜谱中,我们将定义一个具有两层的人工神经网络——输入层有 10 个神经元,输出层有 4 个神经元。然后我们将使用这个网络将空间划分为四个区域。

在开始之前,你需要进行更改以修复库中的错误。你需要打开以下文件:neurolab | net.py。然后找到以下内容:

inx = np.floor (cn0 * pc.cumsum ()). astype (int)

将前面的行替换为以下内容:

inx = np.floor (cn0 * pc.cumsum ())

如何操作...

让我们看看如何创建一个向量量化器:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在提供的vector_quantization.py文件中给出):
import numpy as np 
import matplotlib.pyplot as plt 
import neurolab as nl 
  1. 让我们加载data_vq.txt文件中的输入数据:
input_file = 'data_vq.txt' 
input_text = np.loadtxt(input_file) 
data = input_text[:, 0:2] 
labels = input_text[:, 2:]
  1. 定义一个学习向量量化LVQ)神经网络,具有两层。最后一个参数中的数组指定了每个输出的百分比权重(它们应该加起来为 1):
net = nl.net.newlvq(nl.tool.minmax(data), 10, [0.25, 0.25, 0.25, 0.25]) 
  1. 训练 LVQ 神经网络:
error = net.train(data, labels, epochs=100, goal=-1) 
  1. 创建一个用于测试和可视化的值网格:
xx, yy = np.meshgrid(np.arange(0, 8, 0.2), np.arange(0, 8, 0.2)) 
xx.shape = xx.size, 1 
yy.shape = yy.size, 1 
input_grid = np.concatenate((xx, yy), axis=1) 
  1. 在这个网格上评估网络:
output_grid = net.sim(input_grid) 
  1. 定义我们数据中的四个类别:
class1 = data[labels[:,0] == 1] 
class2 = data[labels[:,1] == 1] 
class3 = data[labels[:,2] == 1] 
class4 = data[labels[:,3] == 1] 
  1. 定义所有这些类别的网格:
grid1 = input_grid[output_grid[:,0] == 1] 
grid2 = input_grid[output_grid[:,1] == 1] 
grid3 = input_grid[output_grid[:,2] == 1] 
grid4 = input_grid[output_grid[:,3] == 1] 
  1. 绘制输出:
plt.plot(class1[:,0], class1[:,1], 'ko', class2[:,0], class2[:,1], 'ko',  
                class3[:,0], class3[:,1], 'ko', class4[:,0], class4[:,1], 'ko') 
plt.plot(grid1[:,0], grid1[:,1], 'b.', grid2[:,0], grid2[:,1], 'gx', 
                grid3[:,0], grid3[:,1], 'cs', grid4[:,0], grid4[:,1], 'ro') 
plt.axis([0, 8, 0, 8]) 
plt.xlabel('X-axis') 
plt.ylabel('Y-axis') 
plt.title('Vector quantization using neural networks') 
plt.show() 

如果你运行这段代码,你会看到以下图表,其中空间被划分为区域。每个区域对应于空间中向量量化区域列表中的一个桶:

图片

它是如何工作的...

在这个菜谱中,我们定义了一个具有两层的人工神经网络:输入层有 10 个神经元,输出层有 4 个神经元。这个神经网络首先被训练,然后用来将空间划分为四个区域。每个区域对应于空间中向量量化区域列表中的一个桶。

更多内容...

向量量化基于将大量点(向量)划分为显示相同数量点更接近它们的组。每个组由其质心点标识,这与大多数聚类算法类似。

参考以下内容

构建用于序列数据分析的循环神经网络

循环神经网络在分析序列和时间序列数据方面非常出色。循环神经网络RNN)是一种信息双向流动的神经网络模型。换句话说,在前馈网络中,信号的传播仅以连续的方式进行,从输入到输出,而循环网络则不同。在它们中,这种传播也可以发生在前一个神经层之后的神经层之间,或者在同一层内的神经元之间,甚至是一个神经元与其自身之间。

准备工作

当我们处理序列和时间序列数据时,我们不能仅仅扩展通用模型。数据中的时间依赖性非常重要,我们需要在我们的模型中考虑这一点。让我们使用neurolab库构建一个循环神经网络。

如何操作...

让我们看看如何构建用于序列数据分析的循环神经网络:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在提供的recurrent_network.py文件中,请查阅):
import numpy as np 
import matplotlib.pyplot as plt 
import neurolab as nl 
  1. 定义一个基于输入参数创建波形的函数:
def create_waveform(num_points): 
    # Create train samples 
    data1 = 1 * np.cos(np.arange(0, num_points)) 
    data2 = 2 * np.cos(np.arange(0, num_points)) 
    data3 = 3 * np.cos(np.arange(0, num_points)) 
    data4 = 4 * np.cos(np.arange(0, num_points)) 
  1. 为每个区间创建不同的振幅以创建随机波形:
    # Create varying amplitudes 
    amp1 = np.ones(num_points) 
    amp2 = 4 + np.zeros(num_points)  
    amp3 = 2 * np.ones(num_points)  
    amp4 = 0.5 + np.zeros(num_points)  
  1. 将数组组合以创建输出数组。这些数据对应于输入,振幅对应于标签:
    data = np.array([data1, data2, data3, data4]).reshape(num_points * 4, 1) 
    amplitude = np.array([[amp1, amp2, amp3, amp4]]).reshape(num_points * 4, 1) 

    return data, amplitude  
  1. 定义一个函数,用于在数据通过训练好的神经网络后绘制输出:
# Draw the output using the network 
def draw_output(net, num_points_test): 
    data_test, amplitude_test = create_waveform(num_points_test) 
    output_test = net.sim(data_test) 
    plt.plot(amplitude_test.reshape(num_points_test * 4)) 
    plt.plot(output_test.reshape(num_points_test * 4))
  1. 定义main函数,并开始创建样本数据:
if __name__=='__main__': 
    # Get data 
    num_points = 30 
    data, amplitude = create_waveform(num_points) 
  1. 创建一个具有两层结构的循环神经网络:
    # Create network with 2 layers 
    net = nl.net.newelm([[-2, 2]], [10, 1], [nl.trans.TanSig(), nl.trans.PureLin()]) 
  1. 为每一层设置初始化函数:
    # Set initialized functions and init 
    net.layers[0].initf = nl.init.InitRand([-0.1, 0.1], 'wb') 
    net.layers[1].initf= nl.init.InitRand([-0.1, 0.1], 'wb') 
    net.init() 
  1. 训练循环神经网络:
    # Training the recurrent neural network 
    error = net.train(data, amplitude, epochs=1000, show=100, goal=0.01) 
  1. 从网络中计算训练数据的输出:
    # Compute output from network 
    output = net.sim(data) 
  1. 绘制训练误差图:
    # Plot training results 
    plt.subplot(211) 
    plt.plot(error) 
    plt.xlabel('Number of epochs') 
    plt.ylabel('Error (MSE)') 
  1. 绘制结果:
    plt.subplot(212) 
    plt.plot(amplitude.reshape(num_points * 4)) 
    plt.plot(output.reshape(num_points * 4)) 
    plt.legend(['Ground truth', 'Predicted output'])
  1. 创建一个随机长度的波形,并查看网络能否预测它:
    # Testing on unknown data at multiple scales 
    plt.figure() 

    plt.subplot(211) 
    draw_output(net, 74) 
    plt.xlim([0, 300]) 
  1. 创建另一个较短长度的波形,并查看网络能否预测它:
    plt.subplot(212) 
    draw_output(net, 54) 
    plt.xlim([0, 300]) 

    plt.show() 

如果您运行此代码,您将看到两个图表。第一个图表显示了训练误差和训练数据的性能,如下所示:

第二个图显示了训练好的循环神经网络在任意长度序列上的表现,如下所示:

在您的终端上,您将看到以下内容:

Epoch: 100; Error: 1.2635865600014597;
Epoch: 200; Error: 0.4001584483592344;
Epoch: 300; Error: 0.06438997423142029;
Epoch: 400; Error: 0.03772354900253485;
Epoch: 500; Error: 0.031996105192696744;
Epoch: 600; Error: 0.011933337009068408;
Epoch: 700; Error: 0.012385370178600663;
Epoch: 800; Error: 0.01116995004102195;
Epoch: 900; Error: 0.011191016373572612;
Epoch: 1000; Error: 0.010584255803264013;
The maximum number of train epochs is reached

它是如何工作的...

在这个菜谱中,首先,我们创建了一个具有波形特性的合成信号,即表示给定时间波形形状的曲线。然后我们构建了一个循环神经网络,以查看网络能否预测随机长度的波形。

更多内容...

递归网络与前馈网络的区别在于它们与过去决策相关的反馈循环,因此将它们的输出暂时作为输入接受。可以说,递归网络具有记忆功能。序列中存在信息,并且这些信息被用来执行前馈网络无法执行的任务。

参考以下内容

可视化 OCR 数据库中的字符

我们现在将探讨如何使用神经网络进行光学字符识别(OCR)。这指的是在图像中识别手写字符的过程。我们一直特别关注自动识别书写的问题,以便实现人与机器之间更简单的交互。特别是在过去几年里,由于强大的经济利益和现代计算机处理数据能力的不断提高,这个问题已经得到了有趣的发展,并且越来越高效的解决方案。特别是,一些国家,如日本,以及亚洲国家,在研究和财务资源方面投入了大量资金,以实现最先进的 OCR。

准备工作

在这个菜谱中,我们将显示数据集中包含的手写数字。我们将使用在ai.stanford.edu/~btaskar/ocr可用的数据集。下载后的默认文件名是letter.data。首先,让我们看看如何与数据交互并可视化它。

如何操作...

让我们看看如何可视化 OCR 数据库中的字符:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在提供的visualize_characters.py文件中给出):
import cv2 
import numpy as np 
  1. 定义输入文件名:
# Load input data  
input_file = 'letter.data'  
  1. 定义可视化参数:
# Define visualization parameters  
scaling_factor = 10 
start_index = 6 
end_index = -1 
h, w = 16, 8 
  1. 保持循环读取文件,直到用户按下Escape键。将行分割为制表符分隔的字符:
# Loop until you encounter the Esc key 
with open(input_file, 'r') as f: 
    for line in f.readlines(): 
        data = np.array([255*float(x) for x in line.split('\t')[start_index:end_index]]) 
  1. 将数组重塑为所需的形状,调整大小,并显示:
        img = np.reshape(data, (h,w)) 
        img_scaled = cv2.resize(img, None, fx=scaling_factor, fy=scaling_factor) 
        cv2.imshow('Image', img_scaled) 
  1. 如果用户按下Escape,则中断循环:
        c = cv2.waitKey() 
        if c == 27: 
            break 

如果运行此代码,你将看到一个显示字符的窗口。

工作原理...

在这个菜谱中,我们展示了数据集中包含的手写数字。为此,执行以下任务:

  • 加载输入数据

  • 定义可视化参数

  • 循环直到遇到Escape

还有更多...

OCR 问题的方法基本上有两种类型:一种是基于模式匹配或模型比较,另一种是基于结构分析。通常,这两种技术会结合使用,并在识别和速度方面提供显著的结果。

参考以下内容

使用神经网络构建光学字符识别器

现在我们知道了如何与数据交互,让我们构建一个基于神经网络的 OCR 系统。图像分类和索引的操作基于对图像内容的自动分析,这构成了图像分析的主要应用领域。自动图像识别系统的目标是,通过数学模型和计算机实现,描述图像的内容,同时尽可能遵守人类视觉系统的原则。

准备工作

在这个食谱中,我们将构建一个基于神经网络的 OCR 系统。

如何做到这一点...

让我们看看如何使用神经网络构建光学字符识别器:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在提供的ocr.py文件中给出):
import numpy as np 
import neurolab as nl 
  1. 定义输入文件名:
input_file = 'letter.data' 
  1. 当我们处理涉及大量数据的神经网络时,训练需要花费很多时间。为了演示如何构建这个系统,我们将只使用20个数据点:
num_datapoints = 20 
  1. 如果你查看数据,你会看到有七个不同的字符在

    前二十行。让我们定义它们:

orig_labels = 'omandig' 
num_output = len(orig_labels) 
  1. 我们将使用 90%的数据进行训练,剩余的 10%用于测试。定义训练和测试参数:
num_train = int(0.9 * num_datapoints) 
num_test = num_datapoints - num_train 
  1. 数据集文件中每行的起始和结束索引被指定:
start_index = 6 
end_index = -1 
  1. 创建数据集:
data = [] 
labels = [] 
with open(input_file, 'r') as f: 
    for line in f.readlines(): 
        # Split the line tabwise 
        list_vals = line.split('\t')
  1. 添加一个错误检查来查看字符是否在我们的标签列表中(如果标签不在我们的地面真实标签中,则跳过它):
        if list_vals[1] not in orig_labels: 
            continue 
  1. 提取标签,并将其追加到主列表中:
        label = np.zeros((num_output, 1)) 
        label[orig_labels.index(list_vals[1])] = 1 
        labels.append(label) 
  1. 提取字符,并将其追加到主列表中:
        cur_char = np.array([float(x) for x in list_vals[start_index:end_index]]) 
        data.append(cur_char) 
  1. 一旦我们有足够的数据,就退出循环:
        if len(data) >= num_datapoints: 
            break 
  1. 将此数据转换为 NumPy 数组:
data = np.asfarray(data) 
labels = np.array(labels).reshape(num_datapoints, num_output) 
  1. 提取我们数据中的维数:
num_dims = len(data[0]) 
  1. 训练神经网络直到10,000个 epoch:
net = nl.net.newff([[0, 1] for _ in range(len(data[0]))], [128, 16, num_output]) 
net.trainf = nl.train.train_gd 
error = net.train(data[:num_train,:], labels[:num_train,:], epochs=10000,  
        show=100, goal=0.01) 
  1. 预测测试输入的输出:
predicted_output = net.sim(data[num_train:, :])
print("Testing on unknown data:")
for i in range(num_test):
    print("Original:", orig_labels[np.argmax(labels[i])])
    print("Predicted:", orig_labels[np.argmax(predicted_output[i])])
  1. 如果你运行此代码,你将在训练结束时在你的终端上看到以下内容:
Epoch: 5000; Error: 0.032178530603536336;
Epoch: 5100; Error: 0.023122560947574727;
Epoch: 5200; Error: 0.040615342668364626;
Epoch: 5300; Error: 0.01686314983574041;
The goal of learning is reached

神经网络的输出如下所示:

Testing on unknown data:
Original: o
Predicted: o
Original: m
Predicted: m

它是如何工作的...

在这个食谱中,我们使用神经网络来识别手写数字。为此,执行以下任务:

  • 加载和处理输入数据

  • 创建数据集

  • 将数据和标签转换为 NumPy 数组

  • 提取维数

  • 创建和训练神经网络

  • 预测测试输入的输出

更多内容...

术语手写识别HWR)指的是计算机接收并解释来自纸张文件、照片和触摸屏等来源的作为文本的清晰手写输入的能力。书写文本可以通过光学扫描(OCR)或智能文字识别在纸张上检测到。

参考以下内容

在人工神经网络中实现优化算法

到目前为止,我们已经构建了几个神经网络并获得了令人满意的总体性能。我们使用loss函数来评估模型的性能,这是一种数学方法,用来衡量我们的预测有多错误。为了提高基于神经网络的模型性能,在训练过程中,通过修改权重来尝试最小化loss函数,使我们的预测尽可能正确。为此,使用优化器:它们是调节模型参数的算法,根据loss函数返回的结果来更新模型。在实践中,优化器通过克服权重来以尽可能准确的形式塑造模型:loss函数告诉优化器它是在正确的还是错误的方向上移动。

准备工作

在这个菜谱中,我们将使用 Keras 库构建一个神经网络,并通过采用几个优化器来提高模型的性能。为此,将使用iris数据集。我指的是鸢尾花数据集,这是一个由英国统计学家和生物学家罗纳德·费希尔在 1936 年的论文《The use of multiple measurements in taxonomic problems as an example of linear discriminant analysis》中引入的多变量数据集。

如何做到这一点...

让我们看看如何在人工神经网络中实现优化算法:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在提供的IrisClassifier.py文件中):
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder
from keras.models import Sequential
from keras.layers import Dense 
  1. sklearn数据集导入数据:
IrisData = load_iris() 
  1. 将数据分为输入和目标:
X = IrisData.data
Y = IrisData.target.reshape(-1, 1) 

对于目标,数据被转换为单列。

  1. 让我们将类别标签编码为One Hot Encode
Encoder = OneHotEncoder(sparse=False)
YHE = Encoder.fit_transform(Y)
  1. 将数据拆分为训练和测试:
XTrain, XTest, YTrain, YTest = train_test_split(X, YHE, test_size=0.30)
  1. 让我们构建模型:
model = Sequential()
  1. 将添加三个层:输入层、隐藏层和输出层。
model.add(Dense(10, input_shape=(4,), activation='relu'))
model.add(Dense(10, activation='relu'))
model.add(Dense(3, activation='softmax'))
  1. 让我们编译模型:
model.compile(optimizer='SGD',loss='categorical_crossentropy', metrics=['accuracy'])

以下三个参数被传递:

  • optimizer='SGD':随机梯度下降优化器。包括对动量、学习率衰减和 Nesterov 动量的支持。

  • loss='categorical_crossentropy':我们在这里使用了categorical_crossentropy参数。当使用categorical_crossentropy时,你的目标应该以分类格式(我们有三个类别;每个样本的目标必须是一个三维向量,除了对应于样本类别的索引处的 1 之外,其余都是 0)。

  • metrics=['accuracy']metric是一个用于评估的函数

    在训练和测试期间评估你的模型性能。

  1. 让我们训练模型:
model.fit(XTrain, YTrain, verbose=2, batch_size=5, epochs=200)
  1. 最后,使用未见过的数据测试模型:
results = model.evaluate(XTest, YTest)
print('Final test set loss:' ,results[0])
print('Final test set accuracy:', results[1])

返回以下结果:

Final test set loss: 0.17724286781416998
Final test set accuracy: 0.9555555568801032
  1. 现在我们来看看如果我们使用不同的优化器会发生什么。为此,只需在编译方法中更改优化器参数,如下所示:
model.compile(optimizer='adam',loss='categorical_crossentropy', metrics=['accuracy'])

adam优化器是一种基于一阶、梯度优化随机目标函数的算法,它基于低阶矩的自适应估计。

返回以下结果:

Final test set loss: 0.0803464303414027
Final test set accuracy: 0.9777777777777777

它是如何工作的...

正如我们在构建深度神经网络配方中所说的,梯度下降是一种用于任何学习模型中错误校正的迭代方法。梯度下降方法是通过迭代更新权重和偏置与误差乘以激活函数的导数(反向传播)的过程。在此方法中,最陡下降步长被替换为上一步的类似大小。梯度是曲线的斜率,正如它是激活函数的导数一样。 SGD 优化器基于这种方法。

还有更多...

优化问题通常非常复杂,以至于无法通过解析方法确定解决方案。复杂性主要取决于变量的数量和约束条件,这些定义了问题的大小,然后是可能存在的非线性函数。只有当变量数量很少且函数极其简单时,才能找到解析解。在实践中,为了解决优化问题,必须求助于迭代算法,即给定当前解的近似值,通过适当的操作序列确定新的近似值。因此,从初始近似值开始,确定了一系列近似值。

参考以下内容

第十四章:无监督表示学习

在本章中,我们将涵盖以下食谱:

  • 使用降噪自编码器检测欺诈交易

  • 使用 CBOW 或 skipgram 表示生成词嵌入

  • 使用 PCA 和 t-SNE 可视化 MNIST 数据集

  • 使用词向量进行 Twitter 情感分析

  • 使用 scikit-learn 实现 LDA

  • 使用 LDA 对文本文档进行分类

  • 为 LDA 准备数据

技术要求

为了处理本章中的食谱,你需要以下文件(可在 GitHub 上找到):

  • CreditCardFraud.py

  • creditcard.csv

  • WordEmbeddings.py

  • MnistTSNE.py

  • TweetEmbeddings.py

  • Tweets.csv

  • LDA.py

  • TopicModellingLDA.py

  • PrepDataLDA.py

简介

在第四章《无监督学习聚类》中,我们已经讨论了无监督学习。我们说无监督学习是机器学习中的一个范例,我们在其中构建模型而不依赖于标记的训练数据。为什么回到这个话题?在这种情况下,我们将以无监督的方式讨论学习图像、视频和自然语言语料库等数据的表示问题。

使用降噪自编码器检测欺诈交易

在第四章《无监督学习聚类》中,我们处理了自编码器的主题。在自编码器重建手写数字图像食谱中,有一个神经网络,其目的是将其输入编码为小维度,并得到的结果能够重建输入本身。自编码器的目的不仅仅是执行一种输入的压缩或寻找身份函数的近似;还有一些技术可以让我们将模型(从减少维度的隐藏层开始)引导到给予某些数据属性更多的重要性。

准备工作

在这个食谱中,我们将以无监督模式训练一个自编码器来检测信用卡交易数据中的异常。为此,将使用信用卡欺诈检测数据集。这是一个包含标记为欺诈或真实的匿名信用卡交易的数据集。列出了 2013 年 9 月欧洲持卡人用信用卡进行的交易。该数据集中有 492 笔交易被标记为欺诈,占 284,807 笔交易中的 0.172%。该数据集高度不平衡,因为正类(欺诈)占所有交易的 0.172%。该数据集可在以下 URL 的 Kabble 上找到:www.kaggle.com/mlg-ulb/creditcardfraud

如何做到这一点…

让我们看看如何使用降噪自编码器来检测欺诈交易:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在已经提供给你的CreditCardFraud.py文件中):
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from keras.models import Model
from keras.layers import Input, Dense
from keras import regularizers
  1. 为了使实验可重复,即在每次重复时提供相同的结果,有必要设置种子:
SetSeed = 1
  1. 如前所述,我们将使用已经提供给你的信用卡欺诈检测数据集(creditcard.csv):
CreditCardData = pd.read_csv("creditcard.csv")
  1. 让我们计算两个类别(fraud= 1normal=0)的出现次数:
CountClasses = pd.value_counts(CreditCardData['Class'], sort = True)
print(CountClasses)

返回以下结果:

0 284315
1 492

如预期,数据集高度不平衡——正类(frauds)有492个,而总共有284315个。

  1. 在可用的变量中,交易金额(Amount)是最有趣的一个。让我们计算一些统计数据:
print(CreditCardData.Amount.describe())

返回以下结果:

count 284807.000000
mean      88.349619
std      250.120109
min        0.000000
25%        5.600000
50%       22.000000
75%       77.165000
max    25691.160000
  1. 如我们所见,这些值差异很大,标准差很高。建议对数据进行缩放。记住,在训练机器学习算法之前重新缩放数据是一个好的实践。通过缩放,消除了数据单位,使得你可以轻松地比较来自不同位置的数据。为此,我们将使用sklearnStandardScaler()函数。此函数移除了平均值并将值缩放到单位方差:
from sklearn.preprocessing import StandardScaler

Data = CreditCardData.drop(['Time'], axis=1)
Data['Amount'] = StandardScaler().fit_transform(Data['Amount'].values.reshape(-1, 1))

print(Data.Amount.describe())

返回以下结果:

count   2.848070e+05
mean    2.913952e-17
std     1.000002e+00
min    -3.532294e-01
25%    -3.308401e-01
50%    -2.652715e-01
75%    -4.471707e-02
max     1.023622e+02

因此,我们已经确认现在数据具有mean=0和单位方差。

  1. 现在,我们将起始数据分为两个集合:训练集(70%)和测试集(30%)。训练集将用于训练分类模型,测试集将用于测试模型性能:
XTrain, XTest = train_test_split(Data, test_size=0.3, random_state=SetSeed)
XTrain = XTrain[XTrain.Class == 0]
XTrain = XTrain.drop(['Class'], axis=1)

YTest = XTest['Class']
XTest = XTest.drop(['Class'], axis=1)

XTrain = XTrain.values
XTest = XTest.values 
  1. 我们可以按照以下方式构建 Keras 模型:
InputDim = XTrain.shape[1]

InputModel = Input(shape=(InputDim,))
EncodedLayer = Dense(16, activation='relu')(InputModel)
DecodedLayer = Dense(InputDim, activation='sigmoid')(EncodedLayer)
AutoencoderModel = Model(InputModel, DecodedLayer)
AutoencoderModel.summary()

下图显示了模型架构:

图片

  1. 因此,我们必须为训练配置模型。为此,我们将使用compile()方法,如下所示:
NumEpoch = 100
BatchSize = 32
AutoencoderModel.compile(optimizer='adam', 
                        loss='mean_squared_error', 
                             metrics=['accuracy'])
  1. 到这一点,我们可以训练模型:
history = AutoencoderModel.fit(XTrain, XTrain,
                    epochs=NumEpoch,
                    batch_size=BatchSize,
                    shuffle=True,
                    validation_data=(XTest, XTest),
                    verbose=1,
                    ).history
  1. 现在,我们可以绘制损失历史记录来评估模型收敛性:
plt.plot(history['loss'])
plt.plot(history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper right');
  1. 到这一点,我们使用模型来重建交易的预测结果:
PredData = AutoencoderModel.predict(XTest)
mse = np.mean(np.power(XTest - PredData, 2), axis=1)
ErrorCreditCardData = pd.DataFrame({'Error': mse,
                        'TrueClass': YTest})
ErrorCreditCardData.describe()

为了评估预测的质量,我们使用了均方误差MSE)损失函数。MSE 衡量误差平方的平均值——即估计值与实际估计值之间的平均平方差。MSE 是估计质量的一个度量——它总是非负的,并且值接近零。因此,我们计算了一些与误差和真实值相关的统计数据。以下结果如下:

图片

  1. 现在,我们可以比较分类结果与实际值。最好的方法是使用混淆矩阵。在混淆矩阵中,我们比较我们的结果与真实数据。混淆矩阵的好处是它确定了分类错误的性质以及它们的数量。在这个矩阵中,对角线单元格显示了正确分类的案例数量;所有其他单元格显示了错误分类的案例。为了计算混淆矩阵,我们可以使用包含在sklearn.metrics包中的confusion_matrix()函数,如下所示:
from sklearn.metrics import confusion_matrix

threshold = 3.
YPred = [1 if e > threshold else 0 for e in ErrorCreditCardData.Error.values]
ConfMatrix = confusion_matrix(ErrorCreditCardData.TrueClass, YPred)
print(ConfMatrix)

返回以下混淆矩阵:

[[83641  1667]
 [ 28     107]]
  1. 最后,我们将计算模型的准确度:
from sklearn.metrics import accuracy_score

print(accuracy_score(ErrorCreditCardData.TrueClass, YPred))

以下为获得的准确度:

0.9801622134054282

结果看起来很棒,但不幸的是,输入数据集高度不平衡。如果我们只评估欺诈交易的准确度,这些数据将显著减少。

它是如何工作的...

可用的自动编码器类型不同:

  • 香草自动编码器:这是最简单的形式,其特征是一个三层网络,即只有一个隐藏层的神经网络。输入和输出是相同的。

  • 多层自动编码器:如果只有一个隐藏层不够,我们可以沿着深度维度扩展自动编码器。例如,使用三个隐藏层以获得更好的泛化能力,但我们也必须使用中间层构建对称网络。

  • 卷积自动编码器:使用三维向量而不是一维向量。输入图像被采样以获得潜在表示,即降维,从而迫使自动编码器从图像的压缩版本中学习。

  • 正则化自动编码器:正则化自动编码器不是通过维持浅层的编码器和解码器架构以及强制减少来限制模型的能力,而是使用损失函数来鼓励模型假设一些超出简单复制输入到输出的能力的属性。

更多内容...

实际上,我们发现有两种不同类型:

  • 稀疏自动编码器:这通常用于分类。通过训练自动编码器,中间层的隐藏单元被激活得太频繁。为了避免这种情况,我们需要通过将其限制为训练数据的一部分来降低它们的激活率。这种约束称为稀疏性约束,因为每个单元只被预定义类型的一种输入激活。

  • 去噪自动编码器:我们不是对损失函数添加惩罚,而是让对象发生变化,向输入图像添加噪声,并让自动编码器自主地学习去除它。这意味着网络将只提取最相关的信息,并从数据的一个鲁棒表示中学习。

参考内容

使用 CBOW 和 skipgram 表示生成词嵌入

在第七章“分析文本数据”中,我们已经处理了这个主题。在使用 gensim 的 Word2Vec配方中,我们使用了gensim库来构建 word2vec 模型。现在,我们将深入探讨这个主题。词嵌入允许计算机从未知语料库开始记住单词的语义和句法信息,并构建一个向量空间,其中单词的向量如果它们在相同的语言环境中出现,即如果它们被认为是语义上更相似的话,则彼此更接近。Word2vec是一组模板,用于生成词嵌入。

准备工作

在这个配方中,我们将使用gensim库生成词嵌入。我们还将分析两种实现此目的的技术:CBOW 和 skip gram 表示。

如何做...

让我们看看如何使用 CBOW 和 skip gram 表示来生成词嵌入:

  1. 创建一个新的 Python 文件,并导入以下包(完整代码在已提供的WordEmbeddings.py文件中):
import gensim
  1. 让我们定义训练数据:
sentences = [['my', 'first', 'book', 'with', 'Packt', 'is', 'on','Matlab'],
      ['my', 'second', 'book', 'with', 'Packt', 'is', 'on','R'],
      ['my', 'third', 'book', 'with', 'Packt', 'is', 'on','Python'],
      ['one', 'more', 'book'],
      ['is', 'on', 'Python', 'too']]
  1. 现在,我们可以训练第一个模型:
Model1 = gensim.models.Word2Vec(sentences, min_count=1, sg=0)

使用了三个参数:

  • sentences: 训练数据

  • min_count=1: 训练模型时考虑的单词的最小计数

  • sg=0: 训练算法,CBOW(0)或 skip gram(1)

  1. 让我们打印出模型的摘要:
print(Model1)

返回以下结果:

Word2Vec(vocab=15, size=100, alpha=0.025)
  1. 让我们列出并打印词汇表的摘要:
wordsM1 = list(Model1.wv.vocab)
print(wordsM1)

返回以下结果:

['my', 'first', 'book', 'with', 'Packt', 'is', 'on', 'Matlab', 'second', 'R', 'third', 'Python', 'one', 'more', 'too']
  1. 最后,我们将访问一个单词的向量(book):
print(Model1.wv['book'])

返回以下结果:

  1. 要使用skipgram算法,我们需要执行类似的步骤,但我们将参数sg设置为 1,如下所示:
Model2 = gensim.models.Word2Vec(sentences, min_count=1, sg=1)

它是如何工作的...

Word2vec 使用连续词袋CBOW)和 skip gram 进行词嵌入。在 CBOW 算法中,模型从周围上下文词的窗口中预测当前词。上下文词的顺序不影响预测。在 skip gram 算法中,模型使用当前词来预测周围窗口的上下文词。

还有更多…

根据作者的说法,CBOW 和 skip-gram 都是好的,但对于不常见的单词来说,skip-gram 做得更好。

参见

使用 PCA 和 t-SNE 可视化 MNIST 数据集

在重要维度的数据集中,数据先前被转换成一系列表示函数的简化序列。将输入数据转换成一组功能的过程称为特征提取。这是因为特征提取是从一系列初始测量数据开始的,并产生导出的值,这些值可以保留原始数据集中的信息,但释放了冗余数据。

这样,后续的学习和泛化阶段将得到简化,在某些情况下,这会导致更好的解释。这是一个从原始特征中提取新特征的过程,从而降低了特征测量的成本,提高了分类器的效率。如果特征选择得当,假设特征集将以减少的表示形式运行所需的任务,而不是全尺寸的输入。

准备工作

在这个菜谱中,我们将使用主成分分析PCA)和t-分布随机邻域嵌入方法t-SNE)来执行特征提取过程。这样,我们将能够可视化一个非常大的数据集(如 MNIST)的不同元素是如何组合在一起的。

如何做到这一点...

让我们看看如何使用 PCA 和 t-SNE 可视化 MNIST 数据集:

  1. 创建一个新的 Python 文件,并导入以下包(完整代码在已提供的MnistTSNE.py文件中):
import numpy as np
import matplotlib.pyplot as plt
from keras.datasets import mnist
  1. 要导入mnist数据集,必须使用以下代码:
(XTrain, YTrain), (XTest, YTest) = mnist.load_data()

返回以下元组:

  • XTrainXTest:一个形状为(num_samples,28,28)的灰度图像数据的uint8数组

  • YTrainYTest:一个形状为(num_samples)的数字标签(0-9 范围内的整数)的uint8数组

  1. 为了降低维度,我们将 28 x 28 的图像展平成大小为 784 的向量:
XTrain = XTrain.reshape((len(XTrain), np.prod(XTrain.shape[1:])))
XTest = XTest.reshape((len(XTest), np.prod(XTest.shape[1:])))
  1. 我们只从这个大数据集中提取一部分数据以获得更好的可视化(仅 1,000 条记录):
from sklearn.utils import shuffle
XTrain, YTrain = shuffle(XTrain, YTrain)
XTrain, YTrain = XTrain[:1000], YTrain[:1000] 
  1. 让我们进行pca分析:
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
XPCATransformed = pca.fit_transform(XTrain)
  1. 我们显示新计划中可用的数据:
fig, plot = plt.subplots()
fig.set_size_inches(70, 50)
plt.prism()
plot.scatter(XPCATransformed[:, 0], XPCATransformed[:, 1], c=YTrain)
plot.legend()
plot.set_xticks(())
plot.set_yticks(())
plt.tight_layout()

返回以下结果:

  1. 在这一点上,我们将使用 t-SNE 方法重复该过程:
from sklearn.manifold import TSNE
TSNEModel = TSNE(n_components=2)
XTSNETransformed = TSNEModel.fit_transform(XTrain)
  1. 我们显示新计划中可用的数据:
fig, plot = plt.subplots()
fig.set_size_inches(70, 50)
plt.prism()
plot.scatter(XTSNETransformed[:, 0], XTSNETransformed[:, 1], c=YTrain)
plot.set_xticks(())
plot.set_yticks(())
plt.tight_layout()
plt.show()

返回以下结果:

比较获得的两个结果,很明显,第二种方法使我们能够更详细地识别代表不同数字的组。

它是如何工作的...

PCA 创建了一组新的变量,即主成分。每个主成分是原始变量的线性组合。所有主成分彼此正交,因此没有冗余信息。整体而言,主成分构成了数据空间的正交基。PCA 的目标是使用最少的几个主成分来解释最大的方差。PCA 是一种多维缩放技术。它将变量转换到一个低维空间,保留变量最多的细节。因此,主成分是原始变量经过线性变换后的组合。

t-SNE 是由 Geoffrey Hinton 和 Laurens van der Maaten 开发的一种降维算法,在许多研究领域被广泛用作自动学习工具。它是一种非线性降维技术,特别适合将高维数据集嵌入到二维或三维空间中,通过散点图进行可视化。该算法对点进行建模,使得原始空间中邻近的对象在降维后彼此靠近,而远离的对象则相隔较远,试图保留局部结构。

更多内容...

t-SNE 算法分为两个主要阶段。在第一阶段,构建一个概率分布,使得原始高维空间中的每对点,如果两点相似,则赋予高概率值,如果不相似,则赋予低概率值。然后,在小型空间中定义第二个类似的概率分布。算法随后通过下降梯度,最小化两个分布的 Kullback-Leibler 散度,重新组织小型空间中的点。

参见

使用词嵌入进行 Twitter 情感分析

在 第七章,分析文本数据 中,我们已经处理了情感分析。在 分析句子情感 的配方中,我们使用 movie_reviews 语料库中的数据,通过朴素贝叶斯分类器分析了句子的情感。当时,我们说情感分析是 NLP 最受欢迎的应用之一。情感分析 指的是确定给定文本片段是正面还是负面的过程。在某些变体中,我们将 "中性" 作为第三个选项。

准备工作

在这个菜谱中,我们将使用词嵌入方法来分析一些美国航空公司的客户 Twitter 帖子的情感。Twitter 数据是根据一些贡献者的意见进行分类的。他们首先被要求对正面、负面和中性的帖子进行分类,然后对负面帖子进行分类。数据集可在以下链接获取:www.kaggle.com/crowdflower/twitter-airline-sentiment

如何做到这一点…

让我们看看如何使用词嵌入进行 Twitter 情感分析:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在您已提供的TweetEmbeddings.py文件中):
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
from keras.utils.np_utils import to_categorical
from sklearn.preprocessing import LabelEncoder
from keras import models
from keras import layers
  1. 要导入Tweets数据集(您已提供的Tweets.csv文件),必须使用以下代码:
TweetData = pd.read_csv('Tweets.csv')
TweetData = TweetData.reindex(np.random.permutation(TweetData.index))
TweetData = TweetData[['text', 'airline_sentiment']]

只提取了两列:

  • text:Twitter 帖子

  • airline_sentiment:正面、中性或负面的分类

  1. 现在,我们将起始数据分为两个集合:训练集(70%)和测试集(30%)。训练集将用于训练分类模型,测试集将用于测试模型性能:
XTrain, XTest, YTrain, YTest = train_test_split(TweetData.text, TweetData.airline_sentiment, test_size=0.3, random_state=11)
  1. 现在,我们将单词转换为数字:
TkData = Tokenizer(num_words=1000,
                 filters='!"#$%&()*+,-./:;<=>?@[\]^_`{"}~\t\n',lower=True, split=" ")
TkData.fit_on_texts(XTrain)
XTrainSeq = TkData.texts_to_sequences(XTrain)
XTestSeq = TkData.texts_to_sequences(XTest)

要做到这一点并对XTrain数据集进行分词,使用了fit_on_textstexts_to_sequences方法。

  1. 为了将输入数据转换为与 Keras 兼容的格式,将使用pad_sequences模型。该方法将序列(标量列表)转换为二维 NumPy 数组,如下所示:
XTrainSeqTrunc = pad_sequences(XTrainSeq, maxlen=24)
XTestSeqTrunc = pad_sequences(XTestSeq, maxlen=24)
  1. 因此,我们将目标类别转换为数字:
LabelEnc = LabelEncoder()
YTrainLabelEnc = LabelEnc.fit_transform(YTrain)
YTestLabelEnc = LabelEnc.transform(YTest)
YTrainLabelEncCat = to_categorical(YTrainLabelEnc)
YTestLabelEncCat = to_categorical(YTestLabelEnc)
  1. 现在,我们将构建 Keras 模型:
EmbModel = models.Sequential()
EmbModel.add(layers.Embedding(1000, 8, input_length=24))
EmbModel.add(layers.Flatten())
EmbModel.add(layers.Dense(3, activation='softmax'))

嵌入层接受一个形状为(batch_sizesequence_length)的二维张量作为输入,其中每个条目都是一个整数序列。返回一个形状为(batch_sizesequence_lengthoutput_dim)的三维张量。

  1. 现在,我们将编译和拟合创建的模型:
EmbModel.compile(optimizer='rmsprop'
                 , loss='categorical_crossentropy'
                 , metrics=['accuracy'])

EmbHistory = EmbModel.fit(XTrainSeqTrunc
                 , YTrainLabelEncCat
                 , epochs=100
                 , batch_size=512
                 , validation_data=(XTestSeqTrunc, YTestLabelEncCat)
                 , verbose=1)
  1. 为了评估模型性能,让我们打印准确率:
print('Train Accuracy: ', EmbHistory.history['acc'][-1])
print('Validation Accuracy: ', EmbHistory.history['val_acc'][-1])

返回以下结果:

Train Accuracy: 0.9295472287275566
Validation Accuracy: 0.7625227688874486
  1. 最后,我们将绘制模型历史:
plt.plot(EmbHistory.history['acc'])
plt.plot(EmbHistory.history['val_acc'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'Validation'], loc='upper left')
plt.show()

返回以下图表:

分析验证损失的进展,我们发现模型过拟合了。为了处理过拟合,正如我们在第一章中学习到的Building a ridge regressor菜谱中,The Realm of Supervised Learning,我们需要使用正则化方法。

它是如何工作的...

术语情感分析指的是使用自然语言处理技术、文本分析和计算语言学来识别和提取书面或口头文本来源中的主观信息。情感分析可以通过不同的方法来解决。最常用的方法可以分为四个宏观类别(Collomb A, Costea C, Joyeux D, Hasan O, 和 Brunie L 的 2014 年论文《A Study and Comparison of Sentiment Analysis Methods for Reputation Evaluation》):

  • 基于词典的方法:这些方法检测情感关键词,并为可能代表特定情感的任意单词分配亲和力。

  • 基于规则的方法:这些方法根据情感词(如快乐悲伤无聊)的存在使用情感类别对文本进行分类。

  • 统计方法:在这里,我们试图识别情感的所有者,即主体是谁,以及目标,即情感所感受到的对象。为了在上下文中衡量观点并找到被评判的特征,我们检查文本中单词之间的语法关系。这是通过对文本进行彻底扫描获得的。

  • 机器学习方法:这些方法使用不同的学习算法通过使数据集分类(监督方法)来确定情感。学习过程不是即时的;事实上,必须构建将极性关联到不同类型评论的模型,以及必要时用于分析目的的主题。

还有更多…

正则化方法涉及修改性能函数,通常选择为训练集上回归误差平方和。当有大量变量可用时,线性模型的平方估计通常具有较低的偏差,但相对于变量较少的模型具有较高的方差。在这些条件下,存在过拟合问题。为了通过允许更大的偏差但较小的方差来提高预测精度,我们可以使用变量选择方法和降维,但这些方法在第一种情况下可能因计算负担而不吸引人,或者在另一种情况下可能难以解释。

相关内容

使用 scikit-learn 实现 LDA

潜在狄利克雷分配LDA)是一种生成模型,用于自然语言研究,它允许您从一组源文档中提取论点,并对文档各个部分的相似性提供逻辑解释。每个文档被视为一组单词,当它们结合在一起时,形成一个或多个潜在主题的子集。每个主题由特定术语分布的特征所表征。

准备工作

在这个菜谱中,我们将使用sklearn.decomposition.LatentDirichletAllocation函数生成一个标记计数特征矩阵,类似于CountVectorizer函数(在第七章的构建词袋模型菜谱中已使用)在文本上产生的结果。

如何操作…

让我们看看如何使用 scikit-learn 实现 LDA:

  1. 创建一个新的 Python 文件,并导入以下包(完整代码在已提供的LDA.py文件中):
from sklearn.decomposition import LatentDirichletAllocation
from sklearn.datasets import make_multilabel_classification
  1. 为了生成输入数据,我们将使用sklearn.datasets.make_multilabel_classification函数。此函数生成一个随机的多标签分类问题,如下所示:
X, Y = make_multilabel_classification(n_samples=100, n_features=20, n_classes=5, n_labels=2, random_state=1)

返回以下数据:

  • X:生成的样本,是一个形状为[n_samples, n_features]的数组

  • Y:标签集,是一个形状为[n_samples, n_classes]的数组或稀疏 CSR 矩阵

在我们的情况下,Y变量将不会为我们服务,因为我们将使用一种无监督方法,正如我们所知,这种方法不需要对数据标签的先验知识。

  1. 现在,我们可以构建LatentDirichletAllocation()模型(使用在线变分贝叶斯算法):
LDAModel = LatentDirichletAllocation(n_components=5, random_state=1)

只传递了两个参数:

  • n_components=5:这是主题的数量,5,因为我们使用了一个基于五个组构建的输入数据集。

  • random_state=1:这是随机数生成器使用的种子。

  1. 现在,我们将使用变分贝叶斯方法对数据X进行模型训练:
LDAModel.fit(X) 
  1. 最后,我们将获取X数据集最后 10 个样本的主题:
print(LDAModel.transform(X[-10:]))

返回以下结果:

图片

对于提供的每个输入示例,返回一个包含五个值的序列,表示该主题属于该组的概率。显然,最接近 1 的值代表最佳概率。

它是如何工作的...

LDA 算法的生成过程基于对文本中包含的数据的分析。将词组合视为随机变量。LDA 算法可以以下方式进行执行:

  • 每个主题都与一个词分布相关联

  • 每个文档都位于一个主题分布中

  • 对于文档中的每个单词,验证其归属到文档主题和主题的词分布

更多内容...

根据推理类型,LDA 算法允许我们在时间和空间复杂度方面达到一定水平的效果和成本(效率)。LDA 模型首次于 2003 年由 David Blei、Andrew Ng 和 Michael Jordan 在发表的一篇论文中提出。

参见

使用 LDA 对文本文档进行分类

LDA 是一种自然语言分析模型,它通过分析文档中术语分布与特定主题(主题)或实体分布的相似性,来理解文本的语义意义。最近,LDA 在语义 SEO 领域也因其可能是谷歌搜索引擎的排名因素而声名鹊起。

准备工作

在这个配方中,我们将使用sklearn.decomposition.LatentDirichletAllocation函数进行主题建模分析。

如何操作…

让我们看看如何使用 LDA 对文本文档进行分类:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在您已提供的TopicModellingLDA.py文件中):
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.decomposition import LatentDirichletAllocation
from sklearn.datasets import fetch_20newsgroups
  1. 为了导入数据,我们将使用sklearn库中的fetch_20newsgroups数据集:
NGData = fetch_20newsgroups(shuffle=True, random_state=7,
                             remove=('headers', 'footers', 'quotes'))

这是一个大约有 20,000 篇新闻组文档的集合,分为 20 个不同的新闻组。该数据集特别适用于处理文本分类问题。

  1. 现在,我们将打印可用的新闻组名称:
print(list(NGData.target_names))

以下结果返回:

['alt.atheism', 'comp.graphics', 'comp.os.ms-windows.misc', 'comp.sys.ibm.pc.hardware', 'comp.sys.mac.hardware', 'comp.windows.x', 'misc.forsale', 'rec.autos', 'rec.motorcycles', 'rec.sport.baseball', 'rec.sport.hockey', 'sci.crypt', 'sci.electronics', 'sci.med', 'sci.space', 'soc.religion.christian', 'talk.politics.guns', 'talk.politics.mideast', 'talk.politics.misc', 'talk.religion.misc']
  1. 数据中有 11,314 个样本。我们将只提取 2,000 个:
NGData = NGData.data[:2000]
  1. 现在,我们将提取一个文档词频矩阵。这基本上是一个计数文档中每个单词出现次数的矩阵。因此,我们将定义对象,并提取文档词频矩阵:
NGDataVect = CountVectorizer(max_df=0.93, min_df=2,
                                max_features=1000,
                                stop_words='english')

NGDataVectModel = NGDataVect.fit_transform(NGData)
  1. 现在,我们可以构建 LDA 模型(使用在线变分贝叶斯算法):
LDAModel = LatentDirichletAllocation(n_components=10, max_iter=5,
                                learning_method='online',
                                learning_offset=50.,
                                random_state=0)
  1. 我们将使用变分贝叶斯方法对NGDataVectModel数据进行模型训练:
LDAModel.fit(NGDataVectModel)
  1. 最后,我们将打印提取的主题:
NGDataVectModelFeatureNames = NGDataVect.get_feature_names()

for topic_idx, topic in enumerate(LDAModel.components_):
     message = "Topic #%d: " % topic_idx
     message += " ".join([NGDataVectModelFeatureNames[i]
     for i in topic.argsort()[:-20 - 1:-1]])
     print(message)

以下结果返回:

工作原理…

主题建模指的是在文本数据中识别隐藏模式的过程。目标是揭示文档集合中的一些隐藏主题结构。这将帮助我们更好地组织文档,以便我们可以用于分析。这是 NLP 研究的一个活跃领域。

LDA 分析自动允许通过与参考知识库KB)的共现关联,回溯到短语的议题,而无需解释句子的含义。

更多内容…

在这里,德·芬蒂定理表明,任何可变随机变量的集合都可以表示为分布的混合,因此如果您想要有可交换的单词和文档表示,就必须考虑同时捕捉两者可交换性的混合。LDA 模型思想方法的基础就是这种方法的根源。

参考以下内容

准备 LDA 数据

在之前的配方中,“使用 LDA 对文本文档进行分类”,我们看到了如何使用 LDA 算法进行主题建模。我们了解到,在构建算法之前,数据集必须经过适当的处理,以便将数据准备成与 LDA 模型提供的输入格式兼容的形式。在这个配方中,我们将详细分析这些程序。

准备工作

在这个配方中,我们将分析将特定数据集中包含的数据进行转换所需的程序。然后,这些数据将被用作基于 LDA 方法的算法的输入。

如何做...

让我们看看如何为 LDA 准备数据:

  1. 创建一个新的 Python 文件,并导入以下包(完整的代码在已经提供给你的PrepDataLDA.py文件中):
from nltk.tokenize import RegexpTokenizer
from stop_words import get_stop_words
from nltk.stem.porter import PorterStemmer
from gensim import corpora, models
  1. 我们定义了一系列我们想要从中提取主题的句子:
Doc1 = "Some doctors say that pizza is good for your health."
Doc2 = "The pizza is good to eat, my sister likes to eat a good pizza, but not to my brother."
Doc3 = "Doctors suggest that walking can cause a decrease in blood pressure."
Doc4 = "My brother likes to walk, but my sister don't like to walk."
Doc5 = "When my sister is forced to walk for a long time she feels an increase in blood pressure."
Doc6 = "When my brother eats pizza, he has health problems."

在我们刚刚定义的句子中,有一些主题以不同的含义重复出现。很难在这些主题之间建立联系。

  1. 我们将这些句子插入到一个列表中:
DocList = [Doc1, Doc2, Doc3, Doc4, Doc5, Doc6]
  1. 我们设置了在转换过程中将使用的元素:
Tokenizer = RegexpTokenizer(r'\w+')
EnStop = get_stop_words('en')
PStemmer = PorterStemmer()
Texts = []
  1. 要对所有短语进行转换,需要设置一个循环,只需遍历列表:
for i in DocList:
  1. 现在,我们可以开始准备数据。分词是将文本分割成一组有意义的片段的过程。这些片段被称为标记。例如,我们可以将一大块文本分割成单词,或者我们可以将其分割成句子。让我们从句子分词开始:
    raw = i.lower()
    Tokens = Tokenizer.tokenize(raw)
  1. 让我们继续到移除无意义词语。在典型的英语句子中,有些词对于主题模型的建设并不具有显著意义。例如,连词和冠词并不能帮助识别主题。这些术语被称为停用词,必须从我们的标记列表中移除。这些术语(停用词)根据我们操作的环境而变化。让我们移除停用词:
    StoppedTokens = [i for i in Tokens if not i in EnStop]
  1. 数据准备的最后阶段是词干提取。词干提取的目标是将这些不同形式归入一个共同的基形式。这使用一种启发式过程来截断词尾以提取基形式。让我们进行词干提取:
StemmedTokens = [PStemmer.stem(i) for i in StoppedTokens]
  1. 我们只需将获得的元素添加到文本列表中:
Texts.append(StemmedTokens)
  1. 到目前为止,我们必须将我们的标记列表转换成字典:
Dictionary = corpora.Dictionary(Texts)
  1. 因此,让我们使用标记文档构建一个文档-词矩阵:
CorpusMat = [Dictionary.doc2bow(text) for text in Texts]
  1. 最后,我们构建了一个 LDA 模型并打印出提取的主题:
LDAModel = models.ldamodel.LdaModel(CorpusMat, num_topics=3, id2word = Dictionary, passes=20)
print(LDAModel.print_topics(num_topics=3, num_words=3))

返回以下结果:

[(0, '0.079*"walk" + 0.079*"blood" + 0.079*"pressur"'), 
 (1, '0.120*"like" + 0.119*"eat" + 0.119*"brother"'), 
 (2, '0.101*"doctor" + 0.099*"health" + 0.070*"pizza"')]

它是如何工作的...

数据准备对于创建主题模型至关重要。数据准备要经过以下程序:

  • 分词:将文档转换为它的原子元素

  • 停用词:移除无意义的词

  • 词干提取:意义等效词的融合

还有更多...

数据准备取决于我们处理文本的类型。在某些情况下,在将数据提交给 LDA 算法之前,执行进一步操作是必要的。例如,可以包括去除标点符号,以及去除特殊字符。

参考以下内容

第十五章:自动化机器学习和迁移学习

在本章中,我们将介绍以下配方:

  • 使用 Auto-WEKA

  • 使用 AutoML 和 TPOT 生成机器学习管道

  • 使用 Auto-Keras

  • 使用 auto-sklearn

  • 使用 MLBox 进行选择和泄漏检测

  • 带有迁移学习的卷积神经网络

  • 迁移学习 – 使用 ResNet-50 预训练图像分类器

  • 迁移学习 – 使用 VGG16 模型进行特征提取

  • 使用重新训练的 GloVe 嵌入进行迁移学习

技术要求

为了处理本章中的配方,您需要以下文件(可在 GitHub 上找到):

  • TPOTIrisClassifier.py

  • AKClassifier.py

  • MLBoxRegressor.py

  • ASKLClassifier.py

  • ImageTransferLearning.py

  • PretrainedImageClassifier.py

  • ExtractFeatures.py

  • PTGloveEMB.py

简介

自动化机器学习AutoML)指的是那些能够自动化将机器学习应用于现实世界问题端到端过程的应用。通常,科学分析师必须在将数据提交给机器学习算法之前,通过一系列的初步程序来处理数据。在前几章中,您看到了通过这些算法进行适当数据分析的必要步骤。您看到了如何通过使用几个库来构建基于深度神经网络的模型是多么简单。在某些情况下,这些技能超出了分析师所拥有的,他们必须寻求行业专家的支持来解决该问题。

AutoML 的诞生源于创建一个能够自动化整个机器学习过程的应用的需求,以便用户可以利用这些服务。通常,机器学习专家必须执行以下任务:

  • 数据准备

  • 选择特征

  • 选择合适的模型类别

  • 选择和优化模型超参数

  • 后处理机器学习模型

  • 分析获得的结果

AutoML 自动化所有这些操作。它提供了产生更简单、创建更快且通常优于手工设计的解决方案的优势。存在许多 AutoML 框架;在接下来的几节中,我们将探讨其中的一些。

使用 Auto-WEKA

Weka 是一个完全用 Java 编写的软件环境。Weka,即Waikato 知识分析环境的缩写,是在新西兰的 Waikato 大学开发的机器学习软件。它是开源的,并按照 GNU 通用公共许可证进行分发。使用它,可以基于机器学习构建许多模型。

然而,每个算法都有自己的超参数,这些参数可能会极大地改变它们的性能。研究人员的任务是找到这些参数的正确组合,以最大化模型的性能。Auto-WEKA 自动解决了选择学习算法及其超参数设置的问题。

准备就绪

在这个菜谱中,你将学习如何在三个主要步骤中使用 Auto-WEKA。要使用这个库,必须先安装它。有关系统要求和安装程序的详细信息,请参阅www.cs.ubc.ca/labs/beta/Projects/autoweka/manual.pdf

如何操作...

让我们看看如何使用 Auto-WEKA,如下所示:

  1. 构建实验定义并实例化:在这个步骤中,你指定要使用哪个数据集以及要执行哪种类型的超参数搜索。然后,实验被完全实例化,以便 Auto-WEKA 可以识别要使用的分类器。在这个阶段,Auto-WEKA 将所有路径转换为绝对路径。

  2. 实验执行:Auto-WEKA 通过使用多个随机种子运行相同的实验来利用多个核心;唯一的要求是所有实验都有一个相似的文件系统。

  3. 分析阶段:当 Auto-WEKA 使用基于模型的优化方法时,它会生成一个超参数轨迹,这些超参数是由优化方法在特定时间识别为最佳的超参数。分析的最简单形式是检查在所有种子中找到的最佳超参数,并使用训练好的模型对新数据集进行预测。

它是如何工作的...

为了选择学习算法并设置其超参数,Auto-WEKA 使用一种完全自动化的方法,利用贝叶斯优化方面的最新创新。

更多内容...

Auto-WEKA 是第一个使用贝叶斯优化来自动实例化高度参数化机器学习框架的库。后来,AutoML 也被其他库所应用。

参考以下内容

使用 AutoML 和 TPOT 生成机器学习管道

TPOT是一个 Python 自动化机器学习工具,通过使用遗传编程优化机器学习管道。在人工智能中,遗传算法是进化算法类的一部分。进化算法的一个特点是使用从自然进化中借鉴的技术来寻找问题的解决方案。寻找问题解决方案的过程被委托给一个迭代过程,该过程通过选择和重组越来越精细的解决方案,直到达到最优性标准。在遗传算法中,通过进化压力将解决方案种群推向一个既定目标。

准备工作

在本教程中,你将学习如何使用 TPOT 构建最佳性能模型,从iris数据集对鸢尾花物种(setosa、virginica 和 versicolor)进行分类。要使用此库,必须安装它。有关系统要求和安装过程的信息,请参阅epistasislab.github.io/tpot/installing/

如何做到这一点...

让我们看看如何使用 AutoML 通过 TPOT 生成机器学习管道:

  1. 创建一个新的 Python 文件并导入以下包(完整代码已在提供的TPOTIrisClassifier.py文件中给出):
from tpot import TPOTClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import numpy as np
  1. 让我们按照以下方式导入鸢尾花数据集:
IrisData = load_iris()
  1. 让我们按照以下方式分割数据集:
XTrain, XTest, YTrain, YTest = train_test_split(IrisData.data.astype(np.float64),
    IrisData.target.astype(np.float64), train_size=0.70, test_size=0.30)
  1. 现在,我们可以构建分类器:
TpotCL = TPOTClassifier(generations=5, population_size=50, verbosity=2)
  1. 然后,我们可以训练模型:
TpotCL.fit(XTrain, YTrain)
  1. 然后,我们将使用未见过的数据(XTest)来评估模型性能:
print(TpotCL.score(XTest, YTest))
  1. 最后,我们将导出模型管道:
TpotCL.export('TPOTIrisPipeline.py')

如果你运行此代码,将返回一个大约 97%测试准确率的管道。

它是如何工作的...

TPOT 通过结合灵活的管道表达式树表示和随机搜索算法(如遗传编程)来自动化机器学习管道的构建。在本教程中,你学习了如何使用 TPOT 搜索最佳管道来从鸢尾花数据集对鸢尾花物种进行分类。

还有更多...

TPOT 是在scikit-learn的基础上构建的,因此,鉴于前几章广泛使用了scikit-learn库,生成的所有代码对我们来说都非常熟悉。TPOT 是一个处于积极开发中的平台,因此它将不断更新。

参见

  • TPOT 工具的官方文档:epistasislab.github.io/tpot/

  • 通过基于树的管道优化自动化生物医学数据科学,作者:Randal S. Olson, Ryan J. Urbanowicz, Peter C. Andrews, Nicole A. Lavender, La Creis Kidd, 和 Jason H. Moore (2016)

使用 Auto-Keras

Auto-Keras 是一个开源的 AutoML 软件库,旨在提供轻松访问深度学习模型。Auto-Keras 具有许多功能,允许您自动设置深度学习模型的架构和参数。其易用性、简单安装和大量示例使其成为一个非常受欢迎的框架。Auto-Keras 由德克萨斯 A&M 大学的 DATA 实验室和社区贡献者开发。

准备工作

在本食谱中,您将学习如何使用 Auto-Keras 库来分类手写数字。要安装 Auto-Keras 包,我们可以使用以下pip命令:

$ pip install autokeras 

在撰写本书时,Auto-Keras 仅兼容 Python 3.6。对于安装过程,请参考官方网站autokeras.com/

如何操作...

让我们看看如何使用 Auto-Keras:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码已包含在您已提供的AKClassifier.py文件中):
from keras.datasets import mnist
import autokeras as ak
  1. 让我们按照以下方式导入mnist数据集:
(XTrain, YTrain), (XTest, YTest) = mnist.load_data()
  1. 在定义分类器之前,我们必须给包含输入数据的数组赋予新的形式,而不改变其内容:
XTrain = XTrain.reshape(XTrain.shape + (1,))
XTest = XTest.reshape(XTest.shape + (1,))
  1. 现在,我们可以构建分类器:
AKClf = ak.ImageClassifier()
  1. 然后,我们可以训练模型:
AKClf.fit(XTrain, YTrain)
  1. 最后,我们将使用未见过的数据(XTest)来使用模型:
Results = AKClf.predict(XTest)

工作原理...

在本食谱中,我们仅用几行代码就构建了一个分类器,通过提供一系列手写数字的图像,可以正确地分类数字。

更多内容…

这是一个允许我们自动创建基于机器学习的算法的包,无需担心训练参数的设置,正如您在前几章中看到的,这些参数对于模型的成功至关重要。

参考信息

  • 参考 Auto-Keras 库的官方文档:autokeras.com/

  • 参考 Haifeng Jin、Qingquan Song 和 Xia Hu 合著的《Auto-Keras: Efficient Neural Architecture Search with Network Morphism》(arXiv:1806.10282)。

使用 auto-sklearn

Auto-sklearn 在scikit-learn机器学习库上工作。它代表了一个基于监督机器学习的平台,可直接使用。它自动为新数据集搜索正确的机器学习算法并优化其超参数。

准备工作

在本食谱中,您将学习如何使用 auto-sklearn 构建分类器。为了导入数据,将使用sklearn.datasets.load_digits函数。此函数加载并返回用于分类问题的数字数据集。每个数据点是数字的 8x8 图像。

如何操作...

让我们看看如何使用 auto-sklearn:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码已包含在您已提供的ASKLClassifier.py文件中):
import autosklearn.classification
import sklearn.model_selection
import sklearn.datasets
import sklearn.metrics
  1. 让我们按照以下方式导入digits数据集:
Input, Target = sklearn.datasets.load_digits()
  1. 让我们按照以下方式分割数据集:
XTrain, XTest, YTrain, YTest = sklearn.model_selection.train_test_split(Input, Target, random_state=3)
  1. 现在,我们可以构建分类器:
ASKModel = autosklearn.classification.AutoSklearnClassifier()
  1. 然后,我们可以训练模型:
ASKModel.fit(XTrain, YTrain)
  1. 最后,我们将使用未见过的数据(XTest)来使用模型:
YPred = ASKModel.predict(XTest)
print("Accuracy score", sklearn.metrics.accuracy_score(YTest, YPred))

它是如何工作的...

Auto-sklearn 使用贝叶斯优化来调整scikit-learn中实现的传统机器学习算法的超参数。自动搜索最佳的机器学习算法和优化的参数。

还有更多...

Auto-sklearn 是一个很好的选择来自动化选择和优化自动学习模型的过程,因为它创建了极其精确的机器学习模型,避免了选择、训练和测试不同模型的繁琐任务。

参见

  • auto-sklearn包的官方文档:automl.github.io/auto-sklearn/stable/

  • 由 Feurer 等人撰写的《高效且鲁棒的自动化机器学习》,发表于《神经信息处理系统进展》

使用 MLBox 进行选择和泄漏检测

MLBox 是一个机器学习的自动化库。它支持分布式数据处理、清理、格式化以及分类和回归的多种算法。它允许进行极其鲁棒的功能选择和泄漏检测。它还提供了堆叠模型,这意味着结合一组模型信息以生成一个旨在比单个模型表现更好的新模型。

准备工作

要使用这个库,必须先安装它。有关系统要求和安装过程的信息,请参阅mlbox.readthedocs.io/en/latest/installation.html

在这个菜谱中,你将学习设置使用 MLBox 管道的严格必要条件。通过使用已经在第一章,“监督学习领域”中使用的波士顿数据集,我们将解决回归问题。

如何操作...

让我们看看如何使用 MLBox 进行选择和泄漏检测:

  1. 导入以下包(完整代码在已经为你提供的MLBoxRegressor.py文件中):
from mlbox.preprocessing import *
from mlbox.optimisation import *
from mlbox.prediction import *
  1. 让我们导入数据,如下所示:
paths = ["train.csv","test.csv"] 
target_name = "SalePrice"

使用此代码,我们已经设置了数据集的路径列表以及我们试图预测的目标名称。

  1. 现在,我们将读取和预处理这些文件:
data = Reader(sep=",").train_test_split(paths, target_name)
data = Drift_thresholder().fit_transform(data)
  1. 为了评估模型,将使用以下代码:
Optimiser().evaluate(None, data)

在这种情况下,使用了默认配置。

  1. 最后,为了在测试集上进行预测,使用以下代码:
Predictor().fit_predict(None, data)

如果你想要配置管道(步骤、参数和值),必须使用以下可选步骤。

  1. 为了测试和优化整个管道,我们将使用以下代码:
space = {

       'ne__numerical_strategy' : {"space" : [0, 'mean']},

        'ce__strategy' : {"space" : ["label_encoding", "random_projection", "entity_embedding"]},

        'fs__strategy' : {"space" : ["variance", "rf_feature_importance"]},
        'fs__threshold': {"search" : "choice", "space" : [0.1, 0.2, 0.3]},

        'est__strategy' : {"space" : ["XGBoost"]},
        'est__max_depth' : {"search" : "choice", "space" : [5,6]},
        'est__subsample' : {"search" : "uniform", "space" : [0.6,0.9]}

        }

best = opt.optimise(space, data, max_evals = 5)
  1. 最后,为了在测试集上进行预测,我们将使用以下代码:
Predictor().fit_predict(best, data)

它是如何工作的...

MLBox 通过以下三个步骤构建整个管道:

  1. 预处理:与这一阶段相关的所有操作都使用了mlbox.preprocessing子包。在这一阶段,我们进行输入文件的读取和清理,然后移除漂移变量。

  2. 优化:与这一阶段相关的所有操作都使用了mlbox.mlbox.optimisation子包。在这一阶段,整个流程被优化。采用的超参数优化方法使用了hyperopt库。这个库为要优化的参数创建了一个高维空间,并选择最佳参数组合以降低验证分数。

  3. 预测:与这一阶段相关的所有操作都使用了mlbox.prediction子包。在这一阶段,我们使用测试数据和前一个阶段中确定的最佳超参数进行预测。

还有更多...

MLBox 提供了高级算法和技术,如超参数优化、堆叠、深度学习、泄漏检测、实体嵌入、并行处理等。目前,MLBox 的使用仅限于 Linux。MLBox 最初是用 Python 2 开发的,后来扩展到了 Python 3。

参考阅读

基于迁移学习的卷积神经网络

迁移学习是一种基于机器学习的方法,它利用在解决问题的过程中获得的知识记忆,并将其应用于不同(但相关)的问题。当训练数据供应有限时,就需要使用迁移学习。这可能是因为数据稀缺或收集、标记成本高昂,或者难以获取。随着大量数据的日益增多,迁移学习选项的使用频率越来越高。

卷积神经网络CNNs)本质上是一种人工神经网络ANNs)。实际上,就像后者一样,CNNs 由通过加权分支(权重)相互连接的神经元组成;网络的训练参数再次是权重和偏差。在 CNNs 中,神经元之间的连接模式受到了动物世界中视觉皮层结构的启发。大脑的这一部分(视觉皮层)中的单个神经元对观察到的某个狭窄区域的特定刺激做出反应,这个区域被称为感受野。不同神经元的感受野部分重叠,以覆盖整个视野。单个神经元对其感受野内发生的刺激的反应可以通过卷积运算进行数学近似。

准备工作

在这个菜谱中,你将学习如何使用 Keras 中的迁移学习来构建图像识别模型。为此,我们将使用 MobileNet 模型和 Keras 高级神经网络 API 来训练从Caltech256数据集中提取的模型图像,我们在第十章图像内容分析中已经使用过这个数据集。Caltech256在这个领域非常受欢迎!它包含 256 个类别的图像,每个类别包含数千个样本。

如何做到...

让我们使用 Keras 中的迁移学习来构建一个图像识别模型;在本节中,我们将逐步解释代码:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已经为你提供的ImageTransferLearning.py文件中):
from keras.layers import Dense,GlobalAveragePooling2D
from keras.applications import MobileNet
from keras.applications.mobilenet import preprocess_input
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Model
  1. 让我们导入MobileNet模型并丢弃最后的 1,000 个神经元层:
BasicModel=MobileNet(input_shape=(224, 224, 3), weights='imagenet',include_top=False)
  1. 让我们定义 Keras 模型架构:
ModelLayers=BasicModel.output
ModelLayers=GlobalAveragePooling2D()(ModelLayers)
ModelLayers=Dense(1024,activation='relu')(ModelLayers) 
ModelLayers=Dense(1024,activation='relu')(ModelLayers) 
ModelLayers=Dense(512,activation='relu')(ModelLayers) 
OutpModel=Dense(3,activation='softmax')(ModelLayers) 
  1. 现在,我们可以基于之前定义的架构构建一个模型:
ConvModel=Model(inputs=BasicModel.input,outputs=OutpModel)
  1. 现在,我们可以进入训练阶段。由于采用了基于迁移学习的方法,因此没有必要对整个模型进行训练。这是因为 MobileNet 已经训练好了。让我们定义最后的密集层为可训练层:
for layer in ConvModel.layers[:20]:
    layer.trainable=False
for layer in ConvModel.layers[20:]:
    layer.trainable=True
  1. 让我们将训练数据加载到ImageDataGenerator中:
TrainDataGen=ImageDataGenerator(preprocessing_function=preprocess_input)

ImageDataGenerator是一个内置的 Keras 类,它创建具有实时数据增强的 tensor 图像数据组。数据将以组的形式被卷绕。

  1. 让我们定义一些依赖项和训练数据的路径:
TrainGenerator=TrainDataGen.flow_from_directory('training_images/', #'train/'
                                                 target_size=(224,224),
                                                 color_mode='rgb',
                                                 batch_size=32,
                                                 class_mode='categorical',
                                                 shuffle=True)
  1. 让我们编译 Keras 模型:
ConvModel.compile(optimizer='Adam',loss='categorical_crossentropy',metrics=['accuracy'])

以下三个参数被传递:

  • optimizer='adam':这是一个基于自适应低阶矩估计的一阶、基于梯度的随机目标函数优化算法。

  • loss='categorical_crossentropy':我们在这里使用了categorical_crossentropy参数。当使用

    categorical_crossentropy,你的目标应该以分类格式(我们有 10 个类别;每个样本的目标必须是一个 10 维向量,除了对应于样本类别的索引处有一个 1 之外,其余都是 0)。

  • metrics=['accuracy']:一个指标是一个在训练和测试期间用于评估模型性能的函数。

  1. 最后,我们将定义训练的步长并拟合模型,如下所示:
StepSizeTrain=TrainGenerator.n//TrainGenerator.batch_size
ConvModel.fit_generator(generator=TrainGenerator,
 steps_per_epoch=StepSizeTrain,
 epochs=10)

打印以下结果:

Found 60 images belonging to 3 classes.
Epoch 1/10
1/1 [==============================] - 31s 31s/step - loss: 1.1935 - acc: 0.3125
Epoch 2/10
1/1 [==============================] - 21s 21s/step - loss: 2.7700 - acc: 0.5714
Epoch 3/10
1/1 [==============================] - 24s 24s/step - loss: 0.0639 - acc: 1.0000
Epoch 4/10
1/1 [==============================] - 21s 21s/step - loss: 0.2819 - acc: 0.7500
Epoch 5/10
1/1 [==============================] - 26s 26s/step - loss: 0.0012 - acc: 1.0000
Epoch 6/10
1/1 [==============================] - 21s 21s/step - loss: 0.0024 - acc: 1.0000
Epoch 7/10
1/1 [==============================] - 22s 22s/step - loss: 8.7767e-04 - acc: 1.0000
Epoch 8/10
1/1 [==============================] - 24s 24s/step - loss: 1.3191e-04 - acc: 1.0000
Epoch 9/10
1/1 [==============================] - 25s 25s/step - loss: 9.6636e-04 - acc: 1.0000
Epoch 10/10
1/1 [==============================] - 21s 21s/step - loss: 3.2019e-04 - acc: 1.0000

它是如何工作的...

在这个菜谱中,你学习了如何在图像识别问题中使用迁移学习。通过迁移学习,可以在大型且可访问的数据集上使用预训练模型,以找到具有可重用特征的输出层,这是通过使用这些输出作为输入来训练一个需要较少参数的较小网络来完成的。这个网络只需要知道从预训练模型获得的模式之间的关系以及要解决的特定问题。作为一个预训练模型,我们使用了 MobileNet 模型。

MobileNet 是由谷歌提出的一种架构,特别适合于基于视觉的应用。与具有相同深度网络的普通卷积相比,MobileNet 使用深度可分离卷积,显著减少了参数数量。因此,基于 MobileNet 模型的神经网络更轻。普通卷积被深度卷积替换,随后是一个称为深度可分离卷积的局部卷积。

迁移学习过程随后分为两个阶段:

  • 首先,几乎所有的神经网络层都在一个非常大且通用的数据集上进行了训练,以便获取全局概念

  • 后来,我们使用了特定的数据集来训练剩余的层,并决定是否通过微调传播错误

还有更多…

在这个配方中,我们使用了微调;实际上,我们并没有简单地替换最终层,我们还训练了一些之前的层。在我们使用的网络中,初始层被用来获取通用功能(利用 MobileNet 训练网络的潜力),而后续层被用来最终确定特定活动获得的经验。使用这个程序,我们冻结了前 20 层,同时追踪后续层以满足我们的需求。这种方法有助于在更少的训练时间内实现更好的性能。

微调可以通过以下步骤实现:

  1. 我们从一个在类似问题上预训练的网络开始,通过调整类别数量来替换输出层。

  2. 权重的初始值是预训练网络的值,除了连续层之间的连接,其权重是随机初始化的。

  3. 我们针对新数据集的独特性(不需要很大)进行新的训练迭代(SGD),以优化权重。

在微调过程中,模型参数将被精确调整以适应某些观察结果。

参考以下内容

使用 ResNet-50 预训练图像分类器进行迁移学习

残差网络ResNet)代表了一种架构,通过使用新的创新类型的块(称为残差块)和残差学习的概念,使得研究人员能够达到经典前馈模型因梯度退化问题而无法达到的深度。

预训练模型是在大量数据上训练的,因此它们允许我们获得优异的性能。因此,我们可以采用与我们要解决的问题相似的预训练模型,以避免数据不足的问题。由于形成此类模型的计算成本,它们以可用的格式提供。例如,Keras 库提供了 Xception、VGG16、VGG19、ResNet、ResNetV2、ResNeXt、InceptionV3、InceptionResNetV2、MobileNet、MobileNetV2、DenseNet 和 NASNet 等模型。

准备工作

在这个菜谱中,你将学习如何使用预训练模型来预测单个图像的类别。为此,将使用 ResNet-50 模型。此模型来自keras.applications库。

如何做...

现在,我们将使用一个预训练模型来对单个图像进行分类;在本节中,我们将逐步解释代码:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已经为你提供的PretrainedImageClassifier.py文件中):
from keras.applications.resnet50 import ResNet50
from keras.preprocessing import image
from keras.applications.resnet50 import preprocess_input, decode_predictions
import numpy as np
  1. 让我们定义预训练模型:
PTModel = ResNet50(weights='imagenet')
  1. 让我们定义要分类的图像:
ImgPath = 'airplane.jpg'
Img = image.load_img(ImgPath, target_size=(224, 224))
  1. 在这里,我们将一个图像实例转换为一个numpy数组,数据类型为float32
InputIMG = image.img_to_array(Img)
  1. 现在,我们将获得的numpy数组扩展到预训练模型所需的形状:
InputIMG = np.expand_dims(InputIMG, axis=0)
  1. 然后,我们将预处理数据:
InputIMG = preprocess_input(InputIMG)
  1. 最后,我们将使用预训练模型对输入图像进行分类:
PredData = PTModel.predict(InputIMG)
  1. 为了评估模型性能,我们将使用decode_predictions函数,如下所示:
print('Predicted:', decode_predictions(PredData, top=3)[0])

keras.applications.resnet50.decode_predictions函数将结果解码为一个包含元组的列表(类别、描述和概率)。以下结果被打印出来:

Predicted: [('n02690373', 'airliner', 0.80847234), ('n04592741', 'wing', 0.17411195), ('n04552348', 'warplane', 0.008112171)]

较高的概率(0.80847234)告诉我们它是一架飞机;事实上,以下就是作为输入提供的图像:

图片

它是如何工作的...

而不是试图估计一个函数G,该函数给定一个x,返回G(x),ResNet 学习这两个值之间的差异——一个称为残差的值。在网络中的残差层,发生了一个经典的卷积,并将输入加到结果上。如果输入和输出的大小不同,输入在添加到输出之前会通过另一个 1×1 滤波器卷积进行转换,以确保它具有相同的特征图数量。通过填充来保留特征图的大小。这种技术的优点是,L2 正则化,它倾向于将权重推向零,不会让我们忘记之前学到的内容,而只是简单地保留它。

还有更多…

有不同深度的 ResNet 实现;最深的达到 152 层。还有一个 1,202 层的原型,但由于过拟合,它实现了更差的结果。这个架构赢得了 2015 年 ILSVRC,错误率为 3.6%。为了理解这个结果的价值,只需考虑人类通常能达到的错误率大约在 5-10%,这取决于他们的技能和知识。多亏了这些结果,ResNet 模型目前在计算机视觉领域是当前最先进的。

相关内容

使用 VGG16 模型进行特征提取的迁移学习

如我们在第十四章的使用 PCA 和 t-SNE 可视化 MNIST 数据集食谱中所述,在重要维度的数据集中,数据被转换成一系列的表示函数。这个过程将输入数据转换成一系列功能,被称为特征提取。这是因为特征的提取是从一系列初始测量数据开始的,并产生导出的值,这些值可以保留原始数据集中的信息,但排除冗余数据。在图像的情况下,特征提取的目标是获取计算机可以识别的信息。

准备工作

在这个食谱中,你将学习如何从一系列图像中提取特征。然后,我们将使用这些特征通过 k-means 算法对图像进行分类。在这个食谱中,我们将使用 VGG16 预训练模型和klearn.cluster.KMeans函数。

如何操作...

让我们使用 VGG16 模型执行特征提取过程:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码在已经为你提供的ExtractFeatures.py文件中):
from keras.applications.vgg16 import VGG16
from keras.preprocessing import image
from keras.applications.vgg16 import preprocess_input
import numpy as np
from sklearn.cluster import KMeans
  1. 让我们定义预训练模型:
model = VGG16(weights='imagenet', include_top=False)
  1. 让我们初始化将要提取的特征列表:
VGG16FeatureList = []
  1. 对于数据集中的每个图像,我们必须进行特征提取:
import os
for path, subdirs, files in os.walk('training_images'):
    for name in files:
        img_path = os.path.join(path, name)
        print(img_path)

这样,我们已经恢复了文件夹中每个图像的路径。所使用的图像包含在training_images文件夹中,我们在使用迁移学习的卷积神经网络食谱中已经使用过它。这是一系列从Caltech256数据集中提取的图像。

  1. 让我们按照以下方式导入图像:
img = image.load_img(img_path, target_size=(224, 224))
  1. 我们将取一个图像实例并将其转换为float32类型的 NumPy 数组:
        img_data = image.img_to_array(img)
  1. 现在,我们将根据预训练模型所需形状扩展获得的 NumPy 数组:
        img_data = np.expand_dims(img_data, axis=0)
  1. 然后,我们将预处理数据:
        img_data = preprocess_input(img_data)
  1. 我们将使用预训练模型从输入图像中提取特征:
        VGG16Feature = model.predict(img_data)
  1. 在这一点上,我们将创建一个包含获取到的特征的数组:
        VGG16FeatureNp = np.array(VGG16Feature)
  1. 现在,我们将获得的数组添加到我们正在构建的特征列表中(每个图像一个元素):
        VGG16FeatureList.append(VGG16FeatureNp.flatten())
  1. 我们将最终列表转换为数组:
VGG16FeatureListNp = np.array(VGG16FeatureList)
  1. 现在,我们可以使用从图像中获得的特征按类型对它们进行分组。记住,这些图像来自三个类别:飞机、汽车和摩托车。因此,我们预计图像将被标记为三个不同的标签。为此,我们使用以下KMeans算法:
KmeansModel = KMeans(n_clusters=3, random_state=0)
  1. 在定义模型后,我们继续训练它:
KmeansModel.fit(VGG16FeatureListNp)
  1. 最后,我们打印出所使用图像的标签:
print(KmeansModel.labels_)

以下结果被打印出来:

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

如你所见,60 张图像已经正确地标记在三个可用的类别中。

它是如何工作的……

在这个菜谱中,你学习了如何从一系列图像中提取特征。由于我们可用的图像数量有限,我们使用了预训练模型(VGG16)来正确提取后续识别所需的信息。这个程序有助于理解如何通过无监督模型自动识别图像。在提取特征后,我们使用它们通过 KMeans 算法对图像进行分类。

还有更多……

VGG16 是由牛津大学的 K. Simonyan 和 A. Zisserman 提出的一种卷积神经网络模型,在论文非常深的卷积神经网络在大规模图像识别中的应用中进行了介绍。该模型在图像识别(准确率达到 92.7%)方面取得了优异的成绩。测试是在 ImageNet 数据集上进行的,该数据集包含超过 1400 万张属于 1000 个类别的图像。

参见

  • 参考第十四章中使用 PCA 和 t-SNE 可视化 Mnist 数据集的菜谱第十四章,无监督表示学习

  • 参考论文非常深的卷积神经网络在大规模图像识别中的应用arxiv.org/abs/1409.1556

使用预训练 GloVe 嵌入的迁移学习

GloVe是一种用于获取单词向量表示的无监督学习算法。训练是在从代码文件中的文本中提取的单词共现的全球统计聚合上进行的。这些表示在单词的向量空间中显示出有趣的线性子结构。在这个菜谱中,你将学习如何使用预训练的 GloVe 嵌入模型来对形容词进行分类,以描述一个人的正面或负面特征。

准备工作

要遵循此食谱,您需要下载 glove.6B.100d.txt 文件。此文件可在 nlp.stanford.edu/projects/glove/ 获取。预训练的词向量有多个版本:

  • glove.6B: 6B 令牌,400K 词汇量,不区分大小写,50d、100d、200d 和 300d 向量——822 MB

  • glove.42B.300d: 42B 令牌,1.9M 词汇量,不区分大小写,300d 向量——1.75 GB

  • glove.840B.300d: 840B 令牌,2.2M 词汇量,区分大小写,300d 向量——2.03 GB

  • Twitter: 27B 令牌,1.2M 词汇量,不区分大小写,25d、50d、100d 和 200d 向量——1.42 GB

如何做到这一点...

让我们分类用于描述人的积极和消极态度的形容词:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码已包含在您已提供的 PTGloveEMB.py 文件中):
from numpy import array
from numpy import zeros
from numpy import asarray
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers import Embedding
  1. 让我们定义用于描述人的 10 个积极和 10 个消极形容词:
Adjectives = ['Wonderful',
        'Heroic',
        'Glamorous',
 'Valuable',
        'Excellent',
        'Optimistic',
        'Peaceful',
        'Romantic',
        'Loving',
        'Faithful',
        'Aggressive',
        'Arrogant',
        'Bossy',
        'Boring',
        'Careless',
        'Selfish',
        'Deceitful',
        'Dishonest',
        'Greedy',
        'Impatient']
  1. 让我们定义之前定义的形容词的标签(1 = 积极,0 = 消极):
AdjLabels = array([1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0])
  1. 让我们分词形容词并准备词汇表:
TKN = Tokenizer()
TKN.fit_on_texts(Adjectives)
VocabSize = len(TKN.word_index) + 1
  1. 让我们将形容词编码为整数序列并将序列列表转换为二维 NumPy 数组:
EncodedAdjectives = TKN.texts_to_sequences(Adjectives)
PaddedAdjectives = pad_sequences(EncodedAdjectives, maxlen=4, padding='post')
  1. 让我们加载预训练模型:
EmbeddingsIndex = dict()
f = open('glove.6B.100d.txt',encoding="utf8")
for line in f:
  Values = line.split()
  Word = Values[0]
  Coefs = asarray(Values[1:], dtype='float32')
  EmbeddingsIndex[Word] = Coefs
f.close()
  1. 我们将为分词形容词创建一个权重矩阵:
EmbeddingMatrix = zeros((VocabSize, 100))
for word, i in TKN.word_index.items():
  EmbeddingVector = EmbeddingsIndex.get(word)
  if EmbeddingVector is not None:
    EmbeddingMatrix[i] = EmbeddingVector
  1. 现在,我们准备好定义 keras 顺序模型:
AdjModel = Sequential()
PTModel = Embedding(VocabSize, 100, weights=[EmbeddingMatrix], input_length=4, trainable=False)
AdjModel.add(PTModel)
AdjModel.add(Flatten())
AdjModel.add(Dense(1, activation='sigmoid'))
print(AdjModel.summary())

以下摘要将被打印:

_________________________________________________________________
Layer (type) Output Shape Param # 
=================================================================
embedding_13 (Embedding) (None, 4, 100) 2100 
_________________________________________________________________
flatten_10 (Flatten) (None, 400) 0 
_________________________________________________________________
dense_17 (Dense) (None, 1) 401 
=================================================================
Total params: 2,501
Trainable params: 401
Non-trainable params: 2,100

如您所见,只有部分参数已被训练。

  1. 让我们编译和拟合模型:
AdjModel.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])
AdjModel.fit(PaddedAdjectives, AdjLabels, epochs=50, verbose=1)
  1. 最后,我们将评估模型的表现:
loss, accuracy = AdjModel.evaluate(PaddedAdjectives, AdjLabels, verbose=1)
print('Model Accuracy: %f' % (accuracy*100))

以下结果将被返回:

Model Accuracy: 100.000000

它是如何工作的...

为了定量捕捉区分积极形容词和消极形容词所必需的细微差别,模型必须将多个数字与词组合相关联。一组词的简单方法就是两个词向量之间的向量差。GloVe 被设计成尽可能紧密地捕捉由几个词并置所指定的意义。

还有更多…

在迁移学习中,网络的权重被调整和转移,以便我们可以使用这些知识来追求多个不同的目标。为了从迁移学习中获得良好的性能,必须满足某些条件:初始数据和最终数据集之间不应相差太大,并且它们必须共享相同的预处理操作。

到目前为止,您已经看到了如何将迁移学习的概念应用于实际案例的几个示例。实际上,在实践中,迁移学习有多种类型:归纳迁移学习无监督迁移学习归纳推理迁移学习实例迁移学习。我们正在努力深化这些概念。

要了解这些方法之间的差异,我们将查看术语——领域和任务。通过术语领域,我们指的是网络使用的类型的数据,而通过术语任务,我们指的是网络打算做什么。我们还将使用术语目标来区分已经在大量数据上训练过的网络和我们打算构建的网络。

归纳迁移学习

监督机器学习中最简单的一种形式是归纳学习。它完全基于观察。给定一组初始的输入输出示例,智能体通过提出假设来重建迁移函数。智能体被设计用来观察与外部世界的交互。特别是,智能体会分析其决策的反馈。人工智能体的感知可以如下使用:

  • 做出决策(反应性智能体)

  • 提高智能体的决策能力(机器学习)

归纳迁移学习方法中,两个网络(源网络和目标网络)处理的信息类型相同(图像、声音等),而网络执行的任务不同。在这种情况下,迁移学习的目的是利用在源网络的训练中恢复的归纳偏差来提高目标网络的表现。通过术语归纳偏差,我们指的是算法在训练阶段恢复的一系列关于数据分布的假设。

无监督迁移学习

在无监督迁移学习中,两个网络(源网络和目标网络)处理的信息类型相同(图像、声音等),而网络执行的任务不同,就像归纳迁移学习一样。这两种方法实质性的区别在于无监督迁移学习中没有可用的标记数据。

归纳迁移学习

在归纳迁移学习中,两个网络(源网络和目标网络)处理的信息不同,而网络执行的任务相似。这种方法基于归纳推理的概念,它将推理从特定的(训练)案例带到特定的案例(测试)。与需要先解决更一般的问题再解决更具体问题的归纳不同,在归纳中,我们试图得到我们真正需要的答案,而不是更一般的答案。

实例迁移学习

在源域和目标域完全相似的场景中很难找到。更可能的是,找到一部分数据,这部分数据与目标域的数据更接近,但位于源域,而源域的大小远大于目标域。在实例迁移学习中,我们寻找源域中与目标域有强相关性的训练样本。一旦找到,它们将在目标活动的学习阶段被重新使用;这样,分类的准确性就得到了提高。

参考以下内容

第十六章:解锁生产问题

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

  • 处理非结构化数据

  • 部署机器学习模型

  • 跟踪生产中的更改

  • 跟踪准确性以优化模型缩放

技术要求

为了处理本章中的菜谱,你需要以下文件(可在 GitHub 上找到):

  • UNData.py

  • TextFile.txt

简介

在前面的章节中,我们已经广泛介绍了机器学习中使用的算法。我们看到了 Python 程序员可以使用的许多工具,以及他们如何构建能够预测或分类特定信息的算法。下一步是创建可以用于生产和随后营销的软件。

考虑到将软件用于营销需要解决包括硬件和软件方面的相当多的问题,这并不是一个小挑战。事实上,我们首先必须确定哪些类型的设备将托管该软件,然后选择最适合那种技术的编程平台。

处理非结构化数据

到目前为止,我们已经强调了在基于自动学习的模型创建中输入数据的重要性。特别是,我们已经看到在将数据提供给我们的算法之前,适当处理这些数据是多么重要。在我们开始生产工作之前,我们必须面对的另一个挑战是学习如何处理非结构化数据。非结构化数据是指没有任何方案的存储数据。一个例子是包含由最流行的文本编辑软件或多媒体文件生成的文本的文件,但这些非结构化数据也可以是电子邮件、PDF 等。非结构化数据与数据库的不同之处在于,它们可能有不规则性,这不允许你将它们编目或以特定过程存储。

准备工作

作为来源,我使用了马克·吐温的小说《哈克贝利·费恩历险记》中的一段,可以在 GitHub 上查看。

如您所见,这是一段非结构化文本。我们将在将结果以结构化形式保存之前处理此文本并删除不必要的元素。

如何做...

在这个菜谱中,我们将学习如何处理非结构化数据。按照以下步骤开始:

  1. 创建一个新的 Python 文件并导入以下包(完整的代码已在提供的UNData.py文件中):
import re
  1. 让我们定义输入文件名:
input_file = 'TextFile.txt'
  1. 我们需要初始化包含数据的字典:
data = {}
  1. 现在,我们可以加载并打印数据:
data['Twain'] = open(input_file,'r').read()
print(data['Twain'])
  1. 让我们将数据转换为小写:
for k in data:
    data[k] = data[k].lower()
  1. 让我们删除任何标点符号:
for k in data:
   data[k] = re.sub(r'[-./?!,":;()\']',' ',data[k])
  1. 让我们删除数字:
for k in data:
    data[k] = re.sub('[-|0-9]',' ',data[k])
  1. 让我们删除任何额外的空白字符:
for k in data:
    data[k] = re.sub(' +',' ',data[k]) 
  1. 最后,我们将打印并将结果保存到.csv文件中:
print('###########################')
print(data['Twain'])

with open('Twain.csv', 'w') as f:
    for key in data.keys():
        f.write("%s,%s\n"%(key,data[key]))

f.close()

以下截图显示了输入文件(左侧)和获得的结果(右侧):

图片

它是如何工作的...

在本食谱中,我们学习了如何处理非结构化数据。为此,使用了马克·吐温小说中的一段文字。在加载文本后,去除了标点符号、数字和额外的空白空间。此外,所有文本都被转换成了小写。最后,结果存储在一个.csv文件中。

更多内容...

在本食谱中,我们解决了文本分析的问题,这代表了将非结构化文本转换为后续分析阶段有意义数据的过程。可以用于文本分析的技术有很多,我们在第七章分析文本数据,分析文本数据中处理了其中的一些。

相关内容

部署机器学习模型

将基于机器学习的项目投入生产并不容易。实际上,只有少数公司设法做到了,至少对于大型项目来说是这样。困难在于人工智能不是通过成品软件生产的。需要一个起始平台来实现自己的软件模型,遇到的问题与开发者通常遇到的问题不相似。软件工程的经典方法导致抽象,从而得到可以修改和改进的简单代码。不幸的是,在机器学习应用中很难追求抽象,就像很难控制机器学习的复杂性一样。最好的办法是关注一个具有所需功能的平台,同时允许您从机器学习的数学基础中退出。在本食谱中,我们将介绍 Amazon SageMaker 平台。

准备工作

Amazon SageMaker 是一项付费服务,但多亏了 AWS 免费使用计划,您可以在注册后的前两个月内免费使用 Amazon SageMaker。有关可用计费计划的更多信息,请查看以下链接:aws.amazon.com

如何操作...

让我们看看如何利用 Amazon SageMaker:

  1. 首先,您需要登录到控制台:

图片

  1. 使用以下示例笔记本之一启动笔记本实例:

图片

  1. 通过连接到自定义数据源来更改该实例。

  2. 按照示例创建、形成和验证模型:

图片

  1. 最后,按照屏幕上的步骤在生产环境中分发结果。

它是如何工作的...

Amazon SageMaker 是一个用于创建、训练和分发基于机器学习模型的完全托管服务。Amazon SageMaker 包含三个模块——构建训练部署。构建模块允许我们处理数据,实验算法,并查看输出。训练模块在大型规模上训练模型并优化它。最后,是部署模块,它允许我们以低延迟轻松测试模型的推理。

还有更多…

Amazon SageMaker 允许我们创建用于智能和预测应用的机器学习模型。从安全角度来看,Amazon SageMaker 加密了所有基于机器学习的脚本。对 API 和 Amazon SageMaker 控制台的请求通过安全连接SSL)转发。我们可以使用 AWS 身份和访问管理自动分配训练和分发资源的访问权限。我们还可以使用 Bucket S3、Amazon SageMaker KMS 密钥来加密笔记本训练过程和端点的存储卷。

参见

跟踪生产中的变更

模型的分布不是终点——这只是开始。真正的问题从这里开始。我们无法控制真实环境中的数据。可能会发生变更,我们必须准备好在模型过时之前检测和更新我们的模型。监控对于确保我们的机器学习应用程序的可靠性、可用性和性能至关重要。在本食谱中,我们将讨论一些我们可以用来跟踪模型中发生变化的工具。

如何操作...

以下工具可用于监控 Amazon SageMaker 应用程序:

  • Amazon CloudWatch:这个工具在 AWS 中可用,监控实时运行的资源和应用。可以收集和跟踪参数,可以创建自定义控制面板,并可以设置警报,当指定的参数达到指定的阈值时通知或采取行动。以下截图显示了 Amazon CloudWatch 的概述:

图片

  • Amazon CloudWatch Logs:这个工具在 AWS 中可用,允许您监控、存储和访问来自 EC2、AWS CloudTrail 实例和其他来源的日志文件。CloudWatch 日志监控日志文件中的信息,并在达到某些阈值时发送通知。

  • AWS CloudTrail:这个工具在 AWS 中可用,检索由我们的账户创建的 API 调用和相关事件,并将日志文件返回到指定的 Amazon S3 存储桶。我们还可以检索有关调用服务的用户和账户的有用信息,并可以追踪调用发生的 IP 地址和时间。

它是如何工作的...

要监控 Amazon SageMaker,我们可以使用 Amazon CloudWatch,它实时收集原始数据并将其转换为可读的参数。这些统计数据保留 15 个月,以便您可以访问历史信息,并从更好的角度了解服务或 Web 应用程序的性能。然而,Amazon CloudWatch 控制台将搜索限制在最近两周内已更新的参数。这种限制允许您查看命名空间中最新的过程。还可以设置警报,控制某些阈值,并在达到这些阈值时发送通知或采取行动。

更多内容...

机器学习模型基于一组具有各种属性的输入训练数据。因此,检查模型训练所用的输入数据是否仍然适用于实际环境中的数据非常重要。数据变化可能是突然的,也可能是随着时间的推移逐渐变化。因此,识别变化模式并在事先纠正模型是至关重要的。一旦模型在生产环境中分发,就需要遵循下一道菜谱中提到的步骤,以保持我们的模型健康,并使其对最终用户有用。

参见

跟踪准确性以优化模型扩展

正如我们在第十五章,“自动化机器学习和迁移学习”中看到的,大多数机器学习算法使用一系列参数来控制底层算法的功能。这些参数通常被称为超参数;它们的值影响训练模型的质量。自动模型优化是寻找算法一组超参数的过程,这些超参数提供最优模型。在本菜谱中,我们将学习如何使用 Amazon SageMaker 工具自动优化我们的模型。

如何操作...

要自动优化我们的模型,请按照以下步骤操作:

  1. 打开 Amazon SageMaker 控制台。

  2. 在左下角的导航面板中选择“端点”项目。

图片

  1. 从可用的端点中选择您想要配置的端点。

  2. 选择您想要配置的变体并配置自动扩展。对于端点运行时设置,也要这样做。

  3. 输入每个实例每分钟的调用平均次数。为此,针对目标值进行操作。

  4. 输入每个冷却周期的秒数。

  5. 为了防止缩放策略删除变体实例,请选择禁用缩放选项。

  6. 点击 保存。

它是如何工作的...

超参数优化过程代表了回归的一个特殊情况。这个问题可以这样表述:有一组输入特征可用,然后这个过程优化一个针对采用参数的模型。只要它是由我们使用的算法定义的,参数的选择就是自由的。在亚马逊超参数优化过程中,SageMaker 试图找出哪些超参数组合更有可能产生最佳结果,并尝试执行训练过程来测试这些尝试。为此,首先测试这些超参数的第一组值,然后该过程使用回归来选择下一组要测试的值。

还有更多...

当你选择下一次训练过程的最佳超参数时,超参数优化会考虑你到目前为止对该问题的所有了解。在某些情况下,超参数优化过程可以选择一个点,该点可以产生迄今为止找到的最佳结果的增量改进。这样,该过程使用已知的结果。在其他情况下,你可以选择一组远离你已经测试过的超参数。这样,该过程探索空间并寻找尚未完全分析的新区域。在许多机器学习问题中,探索和利用之间的权衡是常见的。

参见

posted @ 2025-09-03 10:19  绝不原创的飞龙  阅读(3)  评论(0)    收藏  举报