Python-机器学习秘籍-全-
Python 机器学习秘籍(全)
零、前言
机器学习在现代数据驱动的世界中变得越来越普遍。它被广泛应用于许多领域,如搜索引擎、机器人、自动驾驶汽车等。在本书中,您将探索各种可以使用机器学习的现实场景。使用这份令人兴奋的基于食谱的指南,你会明白在给定的环境中应该使用什么算法。
这本书首先讲述了机器学习的各个领域,然后是实际的例子。然后,我们继续讨论更复杂的算法,如支持向量机、极随机森林、隐马尔可夫模型、条件随机场、深度神经网络等。这本书是为希望使用机器学习算法来创建现实世界应用的 Python 程序员编写的。这本书对 Python 初学者很友好,但是熟悉 Python 编程肯定会有助于玩转代码。对于希望实现机器学习技术的有经验的 Python 程序员来说,这也很有用。
您将学习如何对需要使用的算法类型做出明智的决定,以及如何实现这些算法以获得最佳结果。如果你在理解图像、文本、语音或其他形式的数据时遇到困难,这份关于将机器学习技术应用于其中每一项的指南一定会帮助你!
这本书涵盖了什么
第 1 章监督学习的领域,涵盖了回归的各种监督学习技术。我们将学习如何分析自行车共享模式和预测房价。
第 2 章、构建分类器,涵盖了用于数据分类的各种监督学习技术。我们将学习如何估计收入等级,并根据汽车的特点对其进行评估。
第 3 章、预测性建模,讨论了使用支持向量机的预测性建模技术。我们将学习如何应用这些技术来预测体育场馆附近的建筑和道路交通中发生的事件。
第 4 章、无监督学习聚类,解释无监督学习算法,包括 k 均值和 Mean Shift 聚类。我们将学习如何将这些算法应用于股票市场数据和客户细分。
第五章构建推荐引擎,教你关于我们用来构建推荐引擎的算法。我们将学习如何将这些算法应用于协同过滤和电影推荐。
第 6 章、分析文本数据解释了我们用来分析文本数据的技术,包括标记化、词干、词袋等等。我们将学习如何使用这些技术来执行情感分析和主题建模。
第 7 章、语音识别,涵盖了我们用来分析语音数据的算法。我们将学习如何建立语音识别系统。
第 8 章、剖析时间序列和序列数据,解释我们用来分析时间序列和序列数据的技术,包括隐马尔可夫模型和条件随机场。我们将学习如何将这些技术应用于文本序列分析和股市预测。
第 9 章、图像内容分析,涵盖了我们用于图像内容分析和对象识别的算法。我们将学习如何提取图像特征和建立物体识别系统。
第 10 章、生物特征人脸识别解释了我们用来检测和识别图像和视频中人脸的技术。我们将学习降维算法并构建一个人脸识别器。
第 11 章、深度神经网络,涵盖了我们用来构建深度神经网络的算法。我们将学习如何使用神经网络构建光学字符识别系统。
第 12 章、可视化数据,解释了我们在机器学习中用来可视化各种类型数据的技术。我们将学习如何构建不同类型的图表。
这本书你需要什么
Python 2.x 和 Python 3.x 之间有很多争论。虽然我们相信随着更好的版本出现,世界正在向前发展,但许多开发人员仍然喜欢使用 Python 2.x。许多操作系统内置了 Python 2.x。这本书侧重于 Python 中的机器学习,而不是 Python 本身。它还有助于保持与尚未移植到 Python 3.x 的库的兼容性。因此,本书中的代码面向 Python 2.x。本着这种精神,我们试图让所有代码尽可能与 Python 版本无关。我们认为,这将使我们的读者能够轻松理解代码,并在不同的场景中轻松使用它。
这本书是给谁的
这本书是为那些希望使用机器学习算法来创建现实世界应用的 Python 程序员准备的。这本书对 Python 初学者来说很友好,但是熟悉 Python 编程对于玩代码肯定很有用。
路段
在这本书里,你会发现几个经常出现的标题(准备,如何做,如何工作,还有更多,另见)。
为了给出如何完成配方的明确说明,我们使用以下部分:
做好准备
本节告诉您配方中的预期内容,并描述如何设置配方所需的任何软件或任何初步设置。
怎么做…
本节包含遵循配方所需的步骤。
它是如何工作的…
这一部分通常包括对前一部分发生的事情的详细解释。
还有更多…
本节包含关于配方的附加信息,以便读者更好地了解配方。
另见
本节提供了该配方的其他有用信息的有用链接。
惯例
在这本书里,你会发现许多区分不同种类信息的文本样式。以下是这些风格的一些例子和对它们的意义的解释。
文本中的码字、数据库表名、文件夹名、文件名、文件扩展名、路径名、伪 URL、用户输入和 Twitter 句柄如下所示:“这里,我们分配了 25%的数据用于测试,由test_size参数指定。”
代码块设置如下:
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)
任何命令行输入或输出都编写如下:
$ python object_recognizer.py --input-image imagefile.jpg --model-file erf.pkl --codebook-file codebook.pkl
新名词和重要词语以粗体显示。您在屏幕上看到的单词,例如菜单或对话框中的单词,会出现在如下文本中:“如果您将分解数组更改为(0,0.2,0,0,0),则它将突出显示草莓部分。”
注
警告或重要提示会出现在这样的框中。
型式
提示和技巧是这样出现的。
读者反馈
我们随时欢迎读者的反馈。让我们知道你对这本书的看法——你喜欢或不喜欢什么。读者反馈对我们来说很重要,因为它有助于我们开发出你真正能从中获益的标题。
要给我们发送一般反馈,只需发送电子邮件<[feedback@packtpub.com](mailto:feedback@packtpub.com)>,并在您的邮件主题中提及书名。
如果你对某个主题有专业知识,并且对写作或投稿感兴趣,请参见我们位于www.packtpub.com/authors的作者指南。
客户支持
现在,您已经自豪地拥有了一本书,我们有许多东西可以帮助您从购买中获得最大收益。
下载示例代码
你可以从你在http://www.packtpub.com的账户下载这本书的示例代码文件。如果您在其他地方购买了这本书,您可以访问http://www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。
您可以按照以下步骤下载代码文件:
- 使用您的电子邮件地址和密码登录或注册我们的网站。
- 将鼠标指针悬停在顶部的 SUPPORT 选项卡上。
- 点击代码下载&勘误表。
- 在搜索框中输入图书名称。
- 选择要下载代码文件的书籍。
- 从您购买这本书的下拉菜单中选择。
- 点击代码下载。
您也可以通过点击 Packt 出版网站图书网页上的代码文件按钮来下载代码文件。可以通过在搜索框中输入图书名称来访问该页面。请注意,您需要登录您的 Packt 帐户。
下载文件后,请确保使用最新版本的解压缩文件夹:
- 视窗系统的 WinRAR / 7-Zip
- zipeg/izp/un ARX for MAC
- 适用于 Linux 的 7-Zip / PeaZip
这本书的代码包也托管在 GitHub 上的https://GitHub . com/PacktPublishing/Python-机器学习-烹饪书。我们还有来自丰富的图书和视频目录的其他代码包,可在https://github.com/PacktPublishing/获得。看看他们!
下载本书的彩色图片
我们还为您提供了一个 PDF 文件,其中包含本书中使用的截图/图表的彩色图像。彩色图像将帮助您更好地理解输出中的变化。您可以从https://www . packtpub . com/sites/default/files/downloads/PitOnMachineLearningbook _ color images . pdf下载此文件。
勘误表
尽管我们尽了最大努力来确保我们内容的准确性,但错误还是会发生。如果你在我们的某本书里发现了错误——可能是文本或代码中的错误——如果你能向我们报告,我们将不胜感激。通过这样做,你可以让其他读者免受挫折,并帮助我们改进这本书的后续版本。如果您发现任何勘误表,请访问http://www.packtpub.com/submit-errata,选择您的书籍,点击勘误表提交表链接,并输入您的勘误表的详细信息。一旦您的勘误表得到验证,您的提交将被接受,勘误表将上传到我们的网站或添加到该标题勘误表部分下的任何现有勘误表列表中。
要查看之前提交的勘误表,请前往https://www.packtpub.com/books/content/support并在搜索栏中输入图书名称。所需信息将出现在勘误表部分。
盗版
互联网上版权材料的盗版是所有媒体的一个持续问题。在 Packt,我们非常重视版权和许可证的保护。如果您在互联网上遇到任何形式的我们作品的非法拷贝,请立即向我们提供位置地址或网站名称,以便我们寻求补救。
请通过<[copyright@packtpub.com](mailto:copyright@packtpub.com)>联系我们,获取疑似盗版资料的链接。
我们感谢您在保护我们的作者方面的帮助,以及我们为您带来有价值内容的能力。
问题
如果您对本书的任何方面有问题,可以在<[questions@packtpub.com](mailto:questions@packtpub.com)>联系我们,我们将尽最大努力解决问题。
一、监督学习的领域
在本章中,我们将介绍以下食谱:
- 使用不同技术预处理数据
- 标签编码
- 构建线性回归器
- 计算回归精度
- 实现模型持久性
- 建立山脊回归器
- 构建多项式回归器
- 估计房价
- 计算特征的相对重要性
- 估计自行车需求分布
简介
如果你熟悉机器学习的基础知识,你一定会知道监督学习是关于什么的。给你一个快速复习,监督学习是指建立一个基于标记样本的机器学习模型。例如,如果我们构建一个系统,根据各种参数(如大小、地点等)来估计房子的价格,我们首先需要创建一个数据库并对其进行标记。我们需要告诉我们的算法什么参数对应什么价格。基于这些数据,我们的算法将学习如何使用输入参数计算房子的价格。
无监督学习与我们刚才讨论的相反。这里没有可用的标记数据。让我们假设我们有一堆数据点,我们只是想把它们分成多个组。我们不知道分离的标准是什么。因此,无监督学习算法将尝试以最佳方式将给定数据集分成固定数量的组。我们将在接下来的章节中讨论无监督学习。
在本书的过程中,我们会使用各种 Python 包,如【NumPy】SciPyscikit-learn****matplotlib来构建各种东西。如果你使用 Windows,建议使用 SciPy-stack 兼容版本的 Python。您可以在http://www.scipy.org/install.html查看兼容版本列表。这些发行版已经安装了所有必要的软件包。如果你用的是 Mac OS X 或者 Ubuntu,安装这些软件包相当简单。以下是一些有用的安装和文档链接:
- num py:http://docs . scipy . org/doc/num py-1 . 10 . 1/user/install . html
- SciPy:http://www.scipy.org/install.html
- sci kit-学习:http://scikit-learn.org/stable/install.html
- matplotlib:http://matplotlib.org/1.4.2/users/installing.html
在继续之前,请确保您的计算机上安装了这些软件包。
使用不同技术预处理数据
在现实世界中,我们通常要处理大量的原始数据。这些原始数据不容易被机器学习算法吸收。为了给机器学习准备数据,我们必须在将其输入各种算法之前对其进行预处理。
做好准备
让我们看看如何用 Python 对数据进行预处理。首先,在您喜欢的文本编辑器中打开一个扩展名为.py的文件,例如preprocessor.py。向该文件添加以下行:
import numpy as np
from sklearn import preprocessing
我们刚刚进口了几个必要的包裹。让我们创建一些示例数据。向该文件添加以下行:
data = np.array([[3, -1.5, 2, -5.4], [0, 4, -0.3, 2.1], [1, 3.3, -1.9, -4.3]])
我们现在准备对这些数据进行操作。
怎么做…
数据可以通过多种方式进行预处理。我们将讨论一些最常用的预处理技术。
平均去除量
从每个特征中去除平均值,使其以零为中心,通常是有益的。这有助于我们消除特征中的任何偏差。向我们之前打开的文件中添加以下行:
data_standardized = preprocessing.scale(data)
print "\nMean =", data_standardized.mean(axis=0)
print "Std deviation =", data_standardized.std(axis=0)
我们现在准备运行代码。为此,请在您的终端上运行以下命令:
$ python preprocessor.py
您将在终端上看到以下输出:
Mean = [ 5.55111512e-17 -1.11022302e-16 -7.40148683e-17 -7.40148683e-17]
Std deviation = [ 1\. 1\. 1\. 1.]
可以看到均值几乎为0,标准差为1。
缩放
数据点中每个特征的值可以在随机值之间变化。所以,有时候扩大规模是很重要的,这样才能成为一个公平的竞争环境。向文件中添加以下行并运行代码:
data_scaler = preprocessing.MinMaxScaler(feature_range=(0, 1))
data_scaled = data_scaler.fit_transform(data)
print "\nMin max scaled data =", data_scaled
缩放后,所有要素值的范围都在指定值之间。将显示输出,如下所示:
Min max scaled data:
[[ 1\. 0\. 1\. 0\. ]
[ 0\. 1\. 0.41025641 1\. ]
[ 0.33333333 0.87272727 0\. 0.14666667]]
正常化
数据当你想调整特征向量中的值,以便它们可以在一个共同的尺度上测量时,使用归一化。机器学习中最常见的标准化形式之一是调整特征向量的值,使它们的总和为 1。在前一个文件中添加以下行:
data_normalized = preprocessing.normalize(data, norm='l1')
print "\nL1 normalized data =", data_normalized
如果运行 Python 文件,您将获得以下输出:
L1 normalized data:
[[ 0.25210084 -0.12605042 0.16806723 -0.45378151]
[ 0\. 0.625 -0.046875 0.328125 ]
[ 0.0952381 0.31428571 -0.18095238 -0.40952381]]
这被大量使用,以确保数据点不会因为其特性的基本性质而被人为提升。
二值化
二值化是当你想把你的数字特征向量转换成布尔向量时使用的。向 Python 文件中添加以下行:
data_binarized = preprocessing.Binarizer(threshold=1.4).transform(data)
print "\nBinarized data =", data_binarized
再次运行代码,您将看到以下输出:
Binarized data:
[[ 1\. 0\. 1\. 0.]
[ 0\. 1\. 0\. 1.]
[ 0\. 1\. 0\. 0.]]
这是一种非常有用的技术,通常在我们对数据有一些先验知识时使用。
一个热编码
很多时候我们处理的数值都是稀疏的,分散在各处。我们并不真的需要储存这些大的价值。这就是一个热编码进入画面的地方。我们可以把 One Hot Encoding 看作是收紧特征向量的工具。它查看每个特性,并确定不同值的总数。它使用“T3”k 之一“T4”方案对值进行编码。基于此对特征向量中的每个特征进行编码。这有助于我们在空间方面更有效率。例如,假设我们正在处理 4 维特征向量。要对特征向量中的第 n 个特征进行编码,编码器将遍历每个特征向量中的第 n 个特征,并计算不同值的数量。如果不同值的数量为 k ,则将特征转换为 k 维向量,其中只有一个值为 1 ,所有其他值为 0 。向 Python 文件中添加以下行:
encoder = preprocessing.OneHotEncoder()
encoder.fit([[0, 2, 1, 12], [1, 3, 5, 3], [2, 3, 2, 12], [1, 2, 4, 3]])
encoded_vector = encoder.transform([[2, 3, 5, 3]]).toarray()
print "\nEncoded vector =", encoded_vector
这是的预期输出:
Encoded vector:
[[ 0\. 0\. 1\. 0\. 1\. 0\. 0\. 0\. 1\. 1\. 0.]]
在上面的例子中,让我们考虑每个特征向量中的第三个特征。数值为1、5、2和4。这里有四个不同的值,这意味着单热编码向量的长度为 4。如果要对值5进行编码,它将是一个向量[0, 1, 0, 0]。这个向量中只有一个值可以是 1 。第二个元素为 1,表示数值为5。
标签编码
在有监督的学习中,我们通常会处理各种各样的标签。这些可以是数字或单词的形式。如果它们是数字,那么算法可以直接使用它们。然而,很多时候,标签需要是人类可读的形式。所以,人们通常用文字来标注训练数据。标签编码是指将单词标签转换成数字形式,以便算法能够理解如何对它们进行操作。让我们看看如何做到这一点。
怎么做…
-
新建一个 Python 文件,导入预处理包:
from sklearn import preprocessing -
这个包包含数据预处理所需的各种功能。让我们定义标签编码器,如下所示:
label_encoder = preprocessing.LabelEncoder() -
label_encoder对象知道如何理解单词标签。让我们创建一些标签:input_classes = ['audi', 'ford', 'audi', 'toyota', 'ford', 'bmw'] -
我们现在准备对这些标签进行编码:
label_encoder.fit(input_classes) print "\nClass mapping:" for i, item in enumerate(label_encoder.classes_): print item, '-->', i -
运行代码,您将在终端上看到以下输出:
Class mapping: audi --> 0 bmw --> 1 ford --> 2 toyota --> 3 -
As shown in the preceding output, the words have been transformed into 0-indexed numbers. Now, when you encounter a set of labels, you can simply transform them, as follows:
labels = ['toyota', 'ford', 'audi'] encoded_labels = label_encoder.transform(labels) print "\nLabels =", labels print "Encoded labels =", list(encoded_labels)以下是您将在终端上看到的输出:
Labels = ['toyota', 'ford', 'audi'] Encoded labels = [3, 2, 0] -
This is way easier than manually maintaining mapping between words and numbers. You can check the correctness by transforming numbers back to word labels:
encoded_labels = [2, 1, 0, 3, 1] decoded_labels = label_encoder.inverse_transform(encoded_labels) print "\nEncoded labels =", encoded_labels print "Decoded labels =", list(decoded_labels)以下是输出:
Encoded labels = [2, 1, 0, 3, 1] Decoded labels = ['ford', 'bmw', 'audi', 'toyota', 'bmw']如您所见,映射得到了完美的保留。
建立线性回归器
回归是估计输入数据和连续值输出数据之间关系的过程。这些数据通常是实数的形式,我们的目标是估计控制从输入到输出的映射的基础函数。让我们从一个非常简单的例子开始。考虑以下输入和输出之间的映射:
1 --> 2
3 --> 6
4.3 --> 8.6
7.1 --> 14.2
如果我要求您估计输入和输出之间的关系,您可以通过分析模式轻松做到这一点。我们可以看到,在每种情况下,输出都是输入值的两倍,因此变换如下:
f(x) = 2x
这是一个简单的函数,将输入值与输出值联系起来。然而,在现实世界中,情况通常并非如此。现实世界中的函数没那么简单!
做好准备
线性回归是指使用输入变量的线性组合来估计基础函数。前面的例子是一个由一个输入变量和一个输出变量组成的例子。
请考虑下图:

线性回归的目标是提取将输入变量与输出变量联系起来的基本线性模型。这旨在使用线性函数最小化实际输出和预测输出之间的差异平方和。这个方法叫做 普通最小二乘。
你可能会说可能有一条曲线更适合这些点,但线性回归不允许这样。线性回归的主要优点是它不复杂。如果你进入非线性回归,你可能会得到更精确的模型,但它们会更慢。如上图所示,模型试图用一条直线来近似输入数据点。让我们看看如何用 Python 构建线性回归模型。
怎么做…
已经为您提供了一个名为data_singlevar.txt的数据文件。这包含逗号分隔的行,其中第一个元素是输入值,第二个元素是对应于该输入值的输出值。您应该使用它作为输入参数:
-
Create a file called
regressor.py, and add the following lines:import sys import numpy as np filename = sys.argv[1] 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)我们只是将输入数据加载到
X和y中,其中X指的是数据,y指的是标签。在前面代码的循环中,我们解析每一行,并根据逗号运算符对其进行拆分。然后我们将其转换为浮点值,并分别保存在X和y中。 -
When we build a machine learning model, we need a way to validate our model and check whether the model is performing at a satisfactory level. To do this, we need to separate our data into two groups: a training dataset and a testing dataset. The training dataset will be used to build the model, and the testing dataset will be used to see how this trained model performs on unknown data. So, let's go ahead and split this data into training and testing datasets:
num_training = int(0.8 * len(X)) num_test = len(X) - num_training # 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%用于测试数据集。
-
我们现在准备训练模型。让我们创建一个回归对象,如下所示:
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) -
我们刚刚根据训练数据训练了线性回归器。拟合方法获取输入数据并训练模型。让我们看看它是如何配合的:
import matplotlib.pyplot as plt y_train_pred = linear_regressor.predict(X_train) 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() -
We are now ready to run the code using the following command:
$ python regressor.py data_singlevar.txt您应该会看到下图:
![How to do it…]()
-
在前面的代码中,我们使用训练好的模型来预测训练数据的输出。这不会告诉我们模型在未知数据上的表现,因为我们是在训练数据本身上运行它。这只是让我们对模型如何适应训练数据有了一个概念。就像你在上图中看到的那样,看起来一切正常!
-
Let's predict the test dataset output based on this model and plot it, as follows:
y_test_pred = linear_regressor.predict(X_test) 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()如果运行这段代码,您将看到如下图:
![How to do it…]()
计算回归精度
现在我们知道如何构建回归器,了解如何评估回归器的质量也很重要。在这种情况下,误差被定义为实际值和回归预测值之间的差值。
做好准备
让我们快速了解什么度量可以用来衡量回归器的质量。可以使用许多不同的指标来评估回归器,例如:
- 平均绝对误差:这个是给定数据集中所有数据点绝对误差的平均值。
- 均方误差:这个是给定数据集中所有数据点误差平方的平均值。这是目前最流行的衡量标准之一!
- 中值绝对误差:这个是给定数据集中所有误差的中值。这种度量的主要优点是它对异常值具有鲁棒性。与平均误差度量相反,测试数据集中的一个坏点不会扭曲整个误差度量。
- 解释方差得分:这个得分衡量了我们的模型能够在多大程度上解释数据集的变化。得分 1.0 表示我们的模型是完美的。
- R2 评分:这个发音为 R 平方,这个评分是指判定系数。这告诉我们,我们的模型能够很好地预测未知样本。最好的分数是 1.0,数值也可以是负数。
怎么做…
scikit-learn 中有一个模块,提供计算以下所有指标的功能。打开一个新的 Python 文件,并添加以下行:
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 "Explained 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)
跟踪每一个度量可能会变得乏味,所以我们选择一两个度量来评估我们的模型。一个好的做法是确保均方误差低,解释的方差得分高。
实现模型持久性
当我们训练一个模型的时候,如果能把它保存为文件,这样以后只需要重新加载就可以使用,那就太好了。
怎么做…
让我们看看如何通过编程实现模型持久性:
-
在
regressor.py中增加以下几行:import cPickle as pickle output_model_file = 'saved_model.pkl' with open(output_model_file, 'w') as f: pickle.dump(linear_regressor, f) -
回归器对象将保存在
saved_model.pkl文件中。我们来看看如何加载和使用,如下所示:with open(output_model_file, 'r') as f: model_linregr = pickle.load(f) y_test_pred_new = model_linregr.predict(X_test) print "\nNew mean absolute error =", round(sm.mean_absolute_error(y_test, y_test_pred_new), 2) -
这里,我们只是将文件中的回归器加载到
model_linregr变量中。您可以将前面的结果与前面的结果进行比较,以确认它是相同的。
建立一个山脊回归器
线性回归的主要问题之一是对异常值敏感。在现实世界的数据收集过程中,错误地测量输出是很常见的。线性回归使用普通的最小二乘法,该方法试图最小化误差的平方。异常值往往会导致问题,因为它们对总体误差贡献很大。这往往会破坏整个模型。
做好准备
让我们考虑下图:

底部的两个点显然是异常值,但这个模型试图拟合所有的点。因此,整体模型往往不准确。通过目测,我们可以看到下图是一个比较好的模型:

普通的最小二乘法在建立模型时考虑每一个数据点。因此,实际模型看起来像上图中的虚线。我们可以清楚地看到,这种模式是次优的。为了避免这种情况,我们使用正则化 对系数的大小进行惩罚。这个方法叫做 T5【岭回归】T6。
怎么做…
让我们看看如何在 Python 中构建一个岭回归器:
-
可以从
data_multi_variable.txt文件加载数据。该文件每行包含多个值。除最后一个值之外的所有值构成输入特征向量。 -
在
regressor.py处增加以下几行。让我们用一些参数初始化一个岭回归器:ridge_regressor = linear_model.Ridge(alpha=0.01, fit_intercept=True, max_iter=10000) -
alpha参数控制复杂度。随着alpha越来越接近0,岭回归器越来越像普通最小二乘法的线性回归器。所以,如果你想让它对异常值有鲁棒性,你需要给alpha分配一个更高的值。我们考虑了0.01的一个数值,这个数值是适中的。 -
让我们训练这个回归器,如下:
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)
运行此代码查看错误度量。您可以构建一个线性回归器来比较和对比相同数据的结果,以查看在模型中引入正则化的效果。
构建多项式回归器
线性回归模型的主要限制之一是它试图将线性函数拟合到输入数据。多项式回归模型通过允许函数为多项式克服了这个问题,从而提高了模型的精度。
做好准备
让我们考虑下图:

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

虚线代表线性回归模型,实线代表多项式回归模型。该模型的曲线性由多项式的次数控制。随着模型曲线的增加,它变得更加精确。然而,曲线性也增加了模型的复杂性,因此,使其更慢。这是一种权衡,在这种情况下,你必须在给定计算约束的情况下,决定模型的精确度。
怎么做…
-
在
regressor.py中增加以下几行:from sklearn.preprocessing import PolynomialFeatures polynomial = PolynomialFeatures(degree=3) -
We initialized a polynomial of the degree
3in the previous line. Now we have to represent the datapoints in terms of the coefficients of the polynomial:X_train_transformed = polynomial.fit_transform(X_train)这里,
X_train_transformed以多项式形式表示相同的输入。 -
Let's consider the first datapoint in our file and check whether it can predict the right output:
datapoint = [0.39,2.78,7.11] poly_datapoint = polynomial.fit_transform(datapoint) poly_linear_model = linear_model.LinearRegression() poly_linear_model.fit(X_train_transformed, y_train) print "\nLinear regression:", linear_regressor.predict(datapoint)[0] print "\nPolynomial regression:", poly_linear_model.predict(poly_datapoint)[0]变量数据点中的值是输入数据文件第一行中的值。我们仍然在这里拟合线性回归模型。唯一的区别是我们表示数据的方式。如果运行此代码,您将看到以下输出:
Linear regression: -11.0587294983 Polynomial regression: -10.9480782122可以看到,这已经接近输出值了。如果我们想让它更接近,我们需要增加多项式的次数。
-
Let's make it
10and see what happens:polynomial = PolynomialFeatures(degree=10)您应该会看到如下内容:
Polynomial regression: -8.20472183853
现在,你可以看到预测值更接近实际输出值。
估算房价
是时候将我们的知识应用于现实世界的问题了。让我们运用所有这些原则来估计房价。这是用来理解回归的最流行的例子之一,它是一个很好的切入点。这是直观的和相关的,因此在我们进行更复杂的机器学习之前更容易理解概念。我们将使用 决策树回归器和 AdaBoost 来解决这个问题。
做好准备
决策树是一种树,其中每个节点都做出有助于最终输出的简单决策。叶节点代表输出值,分支代表基于输入特征做出的中间决策。AdaBoost 代表自适应升压,这是一种用于提高另一个系统结果准确性的技术。这结合了不同版本算法的输出,称为 【弱学习者】,使用加权求和得到最终输出。在 AdaBoost 算法的每个阶段收集的信息被反馈到系统中,以便后期的学习者专注于难以分类的训练样本。这是提高系统精度的方法。
使用 AdaBoost,我们在数据集上拟合了一个回归器。我们计算误差,然后根据这个误差估计值在同一数据集上再次拟合回归。我们可以认为这是对回归器的微调,直到达到所需的精度。给你一个数据集,其中包含影响房价的各种参数。我们的目标是估计这些参数和房价之间的关系,以便在给定未知输入参数的情况下,我们可以用它来估计价格。
怎么做…
-
创建一个名为
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 -
There is a standard housing dataset that people tend to use to get started with machine learning. You can download it at https://archive.ics.uci.edu/ml/datasets/Housing. The good thing is that scikit-learn provides a function to directly load this dataset:
housing_data = datasets.load_boston()每个数据点有 13 个影响房价的输入参数。您可以使用
housing_data.data访问输入数据,使用housing_data.target访问相应的价格。 -
让我们把它分成输入和输出。为了使其独立于数据的顺序,让我们也对其进行洗牌:
X, y = shuffle(housing_data.data, housing_data.target, random_state=7) -
random_state参数控制我们如何打乱数据,以便我们可以获得可再现的结果。让我们把数据分为训练和测试。我们将分配 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:] -
我们现在准备好拟合决策树回归模型。让我们选择一棵最大深度为
4的树,这意味着我们没有让树变得任意深:dt_regressor = DecisionTreeRegressor(max_depth=4) dt_regressor.fit(X_train, y_train) -
Let's also fit decision tree regression model with AdaBoost:
ab_regressor = AdaBoostRegressor(DecisionTreeRegressor(max_depth=4), n_estimators=400, random_state=7) ab_regressor.fit(X_train, y_train)这将有助于我们比较结果,并了解 AdaBoost 如何真正提高决策树回归器的性能。
-
让我们评估决策树回归器的性能:
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 "\n#### Decision Tree performance ####" print "Mean squared error =", round(mse, 2) print "Explained variance score =", round(evs, 2) -
现在,我们来评估一下 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 "\n#### 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。
计算特征的相对重要性
所有的特征都同样重要吗?在本例中,我们使用了 13 个输入特征,它们都对模型做出了贡献。然而,这里的一个重要问题是,“我们如何知道哪些功能更重要?”显然,所有特性对输出的贡献并不相等。万一我们想以后丢弃其中的一些,我们需要知道哪些特性不太重要。我们在 scikit-learn 中提供了此功能。
怎么做…
-
Let's plot the relative importance of the features. Add the following lines to
housing.py:plot_feature_importances(dt_regressor.feature_importances_, 'Decision Tree regressor', housing_data.feature_names) plot_feature_importances(ab_regressor.feature_importances_, 'AdaBoost regressor', housing_data.feature_names)回归器对象有一个可调用的
feature_importances_方法,它给出了每个特征的相对重要性。 -
我们实际上需要定义我们的
plot_feature_importances函数来绘制条形图:def plot_feature_importances(feature_importances, title, feature_names): # Normalize the importance values feature_importances = 100.0 * (feature_importances / max(feature_importances)) # Sort the index values and flip them so that they are arranged in decreasing order of importance index_sorted = np.flipud(np.argsort(feature_importances)) # Center the location of the labels on the X-axis (for display purposes only) pos = np.arange(index_sorted.shape[0]) + 0.5 # Plot the bar graph plt.figure() plt.bar(pos, feature_importances[index_sorted], align='center') plt.xticks(pos, feature_names[index_sorted]) plt.ylabel('Relative Importance') plt.title(title) plt.show() -
We just take the values from the
feature_importances_method and scale it so that it ranges between 0 and 100. If you run the preceding code, you will see two figures. Let's see what we will get for a decision tree-based regressor in the following figure:![How to do it…]()
-
So, the decision tree regressor says that the most important feature is RM. Let's take a look at what AdaBoost has to say in the following figure:
![How to do it…]()
根据 AdaBoost,最重要的特性是 LSTAT。实际上,如果您在这些数据上构建各种回归,您会看到最重要的特性实际上是 LSTAT。这显示了使用 AdaBoost 和基于决策树的回归器的优势。
估算自行车需求分布
我们用一个不同的回归方法来解决自行车需求分配问题。我们将使用 随机森林回归器来估计输出值。随机森林是决策树的集合。这基本上使用了一组决策树,这些决策树是使用数据集的各种子集构建的,然后它使用平均来提高整体性能。
做好准备
我们将使用提供给您的bike_day.csv文件。这也可以在https://archive . ics . UCI . edu/ml/datasets/Bike+Sharing+Dataset找到。此数据集中有 16 列。前两列对应于序列号和实际日期,因此我们不会在分析中使用它们。最后三列对应不同类型的输出。最后一列只是第十四列和第十五列的值的总和,所以我们在构建模型时可以忽略这两列。
怎么做…
让我们继续,看看如何在 Python 中做到这一点。已经向您提供了一个名为bike_sharing.py的文件,其中包含完整的代码。我们将讨论其中的重要部分,如下所示:
-
我们首先需要导入几个新包,如下所示:
import csv from sklearn.ensemble import RandomForestRegressor from housing import plot_feature_importances -
We are processing a CSV file, so the CSV package is useful in handling these files. As it's a new dataset, we will have to define our own dataset loading function:
def load_dataset(filename): file_reader = csv.reader(open(filename, 'rb'), delimiter=',') X, y = [], [] for row in file_reader: X.append(row[2:13]) y.append(row[-1]) # Extract feature names feature_names = np.array(X[0]) # Remove the first row because they are feature names return np.array(X[1:]).astype(np.float32), np.array(y[1:]).astype(np.float32), feature_names在这个函数中,我们只是从 CSV 文件中读取所有数据。当我们将其显示在图形上时,特征名称非常有用。我们从输出值中分离数据并返回它们。
-
让我们读取数据并将其打乱,使其独立于数据在文件中的排列顺序:
X, y, feature_names = load_dataset(sys.argv[1]) X, y = shuffle(X, y, random_state=7) -
正如我们之前所做的,我们需要将数据分为培训和测试。这次我们用 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:] -
Let's go ahead and train the regressor:
rf_regressor = RandomForestRegressor(n_estimators=1000, max_depth=10, min_samples_split=1) rf_regressor.fit(X_train, y_train)这里,
n_estimators指的是估计量的数量,也就是我们要在我们的随机森林中使用的决策树的数量。max_depth参数是指每棵树的最大深度,min_samples_split参数是指拆分树中一个节点所需的数据样本数量。 -
我们来评估一下随机森林回归器的性能:
y_pred = rf_regressor.predict(X_test) mse = mean_squared_error(y_test, y_pred) evs = explained_variance_score(y_test, y_pred) print "\n#### Random Forest regressor performance ####" print "Mean squared error =", round(mse, 2) print "Explained variance score =", round(evs, 2) -
As we already have the function to plot the
importancesfeature, let's just call it directly:plot_feature_importances(rf_regressor.feature_importances_, 'Random Forest regressor', feature_names)运行此代码后,您将看到以下图表:
![How to do it…]()
看来温度是控制自行车租赁最重要的因素。
还有更多…
让我们看看当您在数据集中包括第十四列和第十五列时会发生什么。在特征重要性图中,除这两个特征之外的每个特征都必须为零。原因是输出可以通过简单地对第十四列和第十五列求和来获得,因此该算法不需要任何其他特征来计算输出。在load_dataset功能中,在 for 循环中进行以下更改:
X.append(row[2:15])
如果现在绘制要素重要性图,您将看到以下内容:

不出所料,它说只有这两个特性是重要的。这在直觉上是有意义的,因为最终输出是这两个特性的简单总和。所以,这两个变量和产值有直接的关系。因此,回归者说它不需要任何其他变量来预测输出。这是一个非常有用的工具,可以消除数据集中的冗余变量。
还有一个名为bike_hour.csv的文件,其中包含了每小时如何共享自行车的数据。我们需要考虑第 3 列到第 14 列,所以让我们在load_dataset函数中进行这个更改:
X.append(row[2:14])
如果你运行这个,你会看到回归器的性能显示,如下所示:
#### Random Forest regressor performance ####
Mean squared error = 2619.87
Explained variance score = 0.92
特征重要性图如下所示:

这说明一天中的小时是最重要的特征,仔细想想,直觉上说得通!下一个重要特征是温度,这与我们之前的分析一致。
二、构建分类器
在本章中,我们将介绍以下食谱:
- 构建简单的分类器
- 构建逻辑回归分类器
- 构建朴素贝叶斯分类器
- 分割数据集用于训练和测试
- 使用交叉验证评估准确性
- 可视化混淆矩阵
- 提取性能报告
- 根据汽车的特性对其进行评估
- 提取验证曲线
- 提取学习曲线
- 估计收入等级
简介
在机器学习的领域中,分类是指利用数据的特征将其分成一定数量的类的过程。这不同于我们在上一章中讨论的回归,在那里输出是一个实数。监督学习分类器使用标记的训练数据建立模型,然后使用该模型对未知数据进行分类。
分类器可以是实现分类的任何算法。在简单的情况下,这个分类器可以是一个简单的数学函数。在更真实的情况下,这个分类器可以采取非常复杂的形式。在学习过程中,我们会看到分类可以是二进制的,我们将数据分成两类,也可以是多类的,我们将数据分成两类以上。设计用来处理分类问题的数学技术倾向于处理两个类,所以我们用不同的方式扩展它们来处理多类问题。
评估分类器的精度是世界机器学习的重要一步。我们需要学习如何使用可用的数据来了解这个模型在现实世界中的表现。在这一章中,我们将看看处理所有这些事情的食谱。
构建简单的分类器
让我们看看如何使用一些训练数据构建一个简单的分类器。
怎么做…
-
我们将使用已经提供给您的
simple_classifier.py文件作为参考。假设您像上一章一样导入了numpy和matplotlib.pyplot包,让我们创建一些示例数据:X = np.array([[3,1], [2,5], [1,8], [6,4], [5,2], [3,5], [4,7], [4,-1]]) -
让我们给这些点分配一些标签:
y = [0, 1, 1, 0, 0, 1, 1, 0] -
由于我们只有两个类,
y列表包含 0 和 1。一般来说,如果你有 N 类,那么y中的值将从 0 到N1。让我们根据标签将数据分类: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]) -
To get an idea about our data, let's plot it, as follows:
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')这是一个散点图,我们用正方形和十字来画点。在这种情况下,
marker参数指定您想要使用的形状。我们用正方形表示class_0中的点,用十字表示class_1中的点。如果运行此代码,您将看到下图:![How to do it…]()
-
在前面两行的中,我们只是使用
X和y之间的映射来创建两个列表。如果让你目视检查数据点并画一条分隔线,你会怎么做?你只需在它们之间划一条线。让我们继续这样做:line_x = range(10) line_y = line_x -
我们刚刚用数学方程 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() -
If you run this code, you should see the following figure:
![How to do it…]()
还有更多…
我们使用以下规则构建了一个简单分类器:如果a大于等于b,则输入点(a,b)属于class_0;否则属于class_1。如果你逐一检查这些点,你会发现事实上这是真的。就是这里!您刚刚构建了一个线性分类器,可以对未知数据进行分类。这是一个线性分类器,因为分隔线是直线。如果它是一条曲线,那么它就变成了一个非线性分类器。
这种队形效果很好,因为只有有限的几个点,我们可以目视检查它们。如果有几千个点呢?我们如何概括这个过程?让我们在下一个食谱中讨论这个问题。
构建逻辑回归分类器
尽管名称中出现了回归一词,但逻辑回归实际上是用于分类目的。给定一组数据点,我们的目标是建立一个可以在类之间绘制线性边界的模型。它通过求解从训练数据导出的一组方程来提取这些边界。
怎么做…
-
Let's see how to do this in Python. We will use the
logistic_regression.pyfile that is provided to you as a reference. Assuming that you imported the necessary packages, let's create some sample data along with training labels: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])这里,我们假设我们有三个类。
-
Let's initialize the logistic regression classifier:
classifier = linear_model.LogisticRegression(solver='liblinear', C=100)可以为前面的功能指定许多输入参数,但是有几个重要的参数是
solver和C。solver参数指定算法将用于求解方程组的求解器类型。C参数控制正则化强度。较低的值表示较高的正则化强度。 -
让我们训练分类器:
classifier.fit(X, y) -
Let's draw datapoints and boundaries:
plot_classifier(classifier, X, y)我们需要定义这个函数,如下所示:
def plot_classifier(classifier, X, y): # 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前面的值表示我们要在图中使用的值的范围。这些值的范围通常从数据中的最小值到最大值。为了清楚起见,我们添加了一些缓冲区,例如前面几行中的 1.0。
-
In order to plot the boundaries, we need to evaluate the function across a grid of points and plot it. Let's go ahead and define the grid:
# 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_values和y_values变量包含将评估函数的点网格。 -
让我们计算所有这些点的分类器输出:
# 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) -
Let's plot the boundaries using colored regions:
# 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)这基本上是一个三维绘图仪,它采用 2D 点和相关联的值来使用颜色方案绘制不同的区域。你可以在上找到所有配色方案选项。
-
Let's 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绘制了 2D 图上的点。X[:, 0]指定我们应该取沿 0 轴(在我们的例子中是 X 轴)的所有值,X[:, 1]指定 1 轴(Y 轴)。c=y参数表示颜色顺序。我们使用目标标签通过cmap映射到颜色。基本上,我们想要基于目标标签的不同颜色。因此,我们使用y作为映射。使用plt.xlim和plt.ylim设置显示图形的限制。为了用数值标记轴,我们需要使用plt.xticks和plt.yticks。这些函数用值标记轴,以便我们更容易看到点的位置。在前面的代码中,我们希望刻度位于最小值和最大值之间,缓冲区为一个单位。另外,我们希望这些刻度是整数。因此,我们使用int()函数来舍入这些值。 -
If you run this code, you should see the following output:
![How to do it…]()
-
Let's see how the
Cparameter affects our model. TheCparameter indicates the penalty for misclassification. If we set it to1.0, we will get the following figure:

- If we set
Cto10000, we get the following figure:

随着我们增加`C`,误分类的惩罚会更高。因此,边界变得更加优化。
构建朴素贝叶斯分类器
朴素贝叶斯分类器是使用贝叶斯定理来建立模型的监督学习分类器。让我们继续构建一个简单的贝叶斯分类器。
怎么做…
-
我们将使用提供给您的 T3 作为参考。让我们导入一些东西:
from sklearn.naive_bayes import GaussianNB from logistic_regression import plot_classifier -
You were provided with a
data_multivar.txtfile. This contains data that we will use here. This contains comma-separated numerical data in each line. Let's load the data from this file: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中。 -
Let's build the Naive Bayes classifier:
classifier_gaussiannb = GaussianNB() classifier_gaussiannb.fit(X, y) y_pred = classifier_gaussiannb.predict(X)GaussianNB函数指定高斯朴素贝叶斯模型。 -
让我们计算分类器的精度:
accuracy = 100.0 * (y == y_pred).sum() / X.shape[0] print "Accuracy of the classifier =", round(accuracy, 2), "%" -
Let's plot the data and the boundaries:
plot_classifier(classifier_gaussiannb, X, y)您应该会看到下图:
![How to do it…]()
这里不限制边界为线性。在前面的例子中,我们使用了所有的数据进行训练。机器学习的一个很好的实践是为训练和测试准备不重叠的数据。理想情况下,我们需要一些未使用的数据进行测试,这样我们就可以准确估计模型在未知数据上的表现。scikit-learn 中有一个条款可以很好地处理这个问题,如下一个食谱所示。
分割数据集进行训练和测试
让我们看看如何将我们的数据正确地分割成训练和测试数据集。
怎么做…
-
Add the following code snippet into the same Python file as the previous recipe:
from sklearn import cross_validation X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=0.25, random_state=5) classifier_gaussiannb_new = GaussianNB() classifier_gaussiannb_new.fit(X_train, y_train)这里,我们分配了 25%的数据用于测试,由
test_size参数指定。剩下的 75%的数据将用于培训。 -
让我们在测试数据上评估分类器:
y_test_pred = classifier_gaussiannb_new.predict(X_test) -
让我们计算分类器的精度:
accuracy = 100.0 * (y_test == y_test_pred).sum() / X_test.shape[0] print "Accuracy of the classifier =", round(accuracy, 2), "%" -
让我们在测试数据上绘制数据点和边界:
plot_classifier(classifier_gaussiannb_new, X_test, y_test) -
You should see the following figure:
![How to do it…]()
使用交叉验证评估准确性
交叉验证 是机器学习中的一个重要概念。在前面的方法中,我们将数据分成训练和测试数据集。但是为了让它更健壮,我们需要用不同的子集重复这个过程。如果我们只是针对一个特定的子集对其进行微调,我们最终可能会过度拟合模型。过度拟合指的是我们对数据集进行了过多的模型微调,而它在未知数据上表现不佳的情况。我们希望我们的机器学习模型在未知数据上表现良好。
准备…
在我们讨论如何执行交叉验证之前,我们先来谈谈性能指标。当我们处理机器学习模型时,我们通常关心三件事:精度、召回率和 F1 分数。我们可以使用参数评分获得所需的性能指标。精度是指正确分类的项目数占列表中项目总数的百分比。召回指的是检索到的项目数占培训列表中项目总数的百分比。
让我们考虑一个包含 100 个项目的测试数据集,其中 82 个是我们感兴趣的。现在,我们希望我们的分类器为我们识别这 82 个项目。我们的分类器挑选出 73 个项目作为感兴趣的项目。在这 73 个项目中,只有 65 个实际上是感兴趣的项目,其余 8 个分类错误。我们可以通过以下方式计算精度:
- 正确标识的数量= 65
- 标识总数= 73
- 精度= 65 / 73 = 89.04%
为了计算召回,我们使用以下方法:
- 数据集中感兴趣的项目总数= 82
- 正确检索的项目数= 65
- 召回率= 65 / 82 = 79.26%
一个好的机器学习模型需要同时具有好的精度和好的召回率。很容易让其中一个达到 100%,但另一个指标会受到影响!我们需要同时保持这两个指标。为了量化这一点,我们使用了一个 F1 分数,它是精度和召回率的结合。这实际上是精确度和召回率的调和平均值:
F1 得分= 2 精度召回/(精度+召回)
在前一种情况下,F1 的分数如下:
F1 得分= 2 * 0.89 * 0.79/(0.89+0.79)= 0.8370
怎么做…
-
让我们看看如何执行交叉验证和提取性能指标。我们从精度开始:
num_validations = 5 accuracy = cross_validation.cross_val_score(classifier_gaussiannb, X, y, scoring='accuracy', cv=num_validations) print "Accuracy: " + str(round(100*accuracy.mean(), 2)) + "%" -
我们将使用前面的函数来计算精确度、召回率和 F1 分数:
f1 = cross_validation.cross_val_score(classifier_gaussiannb, X, y, scoring='f1_weighted', cv=num_validations) print "F1: " + str(round(100*f1.mean(), 2)) + "%" precision = cross_validation.cross_val_score(classifier_gaussiannb, X, y, scoring='precision_weighted', cv=num_validations) print "Precision: " + str(round(100*precision.mean(), 2)) + "%" recall = cross_validation.cross_val_score(classifier_gaussiannb, X, y, scoring='recall_weighted', cv=num_validations) print "Recall: " + str(round(100*recall.mean(), 2)) + "%"
可视化混淆矩阵
A 混淆矩阵是我们用来理解分类模型的性能的表格。这有助于我们理解如何将测试数据分为不同的类别。当我们想要微调我们的算法时,我们需要在做出这些改变之前了解数据是如何被错误分类的。有些课比其他课差,混淆矩阵会帮助我们理解这一点。让我们看看下图:

在之前的图表中,我们可以看到我们如何将数据分类到不同的类中。理想情况下,我们希望所有非对角元素都为 0。这将表明完美的分类!让我们考虑一下0 级。总体而言,52 件物品实际上属于0 级。如果我们把第一行的数字加起来,我们得到 52。现在,这些项目中有 45 个预测正确,但我们的分类器显示其中 4 个属于类 1 ,3 个属于类 2 。我们也可以对剩下的两行应用同样的分析。有趣的是1 类的 11 个项目被误分类为0 类。这约占该类数据点的 16%。这是一个我们可以用来优化模型的洞察力。
怎么做…
-
We will use the
confusion_matrix.pyfile that we already provided to you as a reference. Let's see how to extract the confusion matrix from our data:from sklearn.metrics import 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)我们在这里使用一些样本数据。我们有四个类,它们的值从 0 到 3 不等。我们也预测了标签。我们使用
confusion_matrix方法提取混淆矩阵并绘制出来。 -
Let's go ahead and define this function:
# 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,因为我们的数据集中有四个不同的标签。np.arange功能给了我们这个numpy数组。 -
If you run the preceding code, you will see the following figure:
![How to do it…]()
对角线颜色很强,我们希望它们很强。黑色表示零。在非对角线空间中有几个灰色,这表明分类错误。例如,当真实标签为 0 时,预测标签为 1,如我们在第一行中所见。事实上,所有的错误分类都属于类-1 ,因为第二列包含三个非零行。从图中很容易看出这一点。
提取绩效报告
我们在 scikit-learn 中还有一个功能,可以直接为我们打印精度、召回率、F1 成绩。让我们看看如何做到这一点。
怎么做…
-
向新的 Python 文件添加以下行:
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)) -
If you run this code, you will see the following on your Terminal:
![How to do it…]()
您可以直接使用这个函数从模型中提取统计数据,而不是单独计算这些指标。
根据汽车的特性对其进行评估
让我们看看如何将分类技术应用于现实世界的问题。我们将使用包含一些汽车细节的数据集,例如车门数量、行李箱空间、维护成本等。我们的目标是确定汽车的质量。出于分类的目的,质量可以采用四个值:不可接受、可接受、良好和非常好。
做好准备
可以在https://archive.ics.uci.edu/ml/datasets/Car+Evaluation下载数据集。
您需要将数据集中的每个值视为一个字符串。我们考虑数据集中的六个属性。这里是属性以及它们可以取的可能值:
buying:这些将是vhigh、high、med和lowmaint:这些将是vhigh、high、med和lowdoors:这些会是2、3、4、5等等persons:这些会是2、4,更多lug_boot:这些会是small、med和bigsafety:这些会是low、med和high
假设每一行都包含字符串,我们需要假设所有的特征都是字符串,并设计一个分类器。在前一章中,我们使用随机森林来构建回归器。在这个食谱中,我们将使用随机森林作为分类器。
怎么做…
-
我们将使用已经提供给您的
car.py文件作为参考。让我们继续导入几个包:from sklearn import preprocessing from sklearn.ensemble import RandomForestClassifier -
Let's load the dataset:
input_file = 'path/to/dataset/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 包只处理数字数据,所以我们需要将这些属性转换成那些包能够理解的东西。
-
In the previous chapter, we discussed label encoding. That is what we will use here to convert strings to numbers:
# 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变量。 -
Let's train the classifier:
# Build a Random Forest classifier params = {'n_estimators': 200, 'max_depth': 8, 'random_state': 7} classifier = RandomForestClassifier(**params) classifier.fit(X, y)你可以摆弄一下
n_estimators和max_depth参数,看看它们是如何影响分类精度的。实际上,我们很快就会以标准化的方式做到这一点。 -
Let's perform cross-validation:
# Cross validation from sklearn import cross_validation accuracy = cross_validation.cross_val_score(classifier, X, y, scoring='accuracy', cv=3) print "Accuracy of the classifier: " + str(round(100*accuracy.mean(), 2)) + "%"一旦我们训练了分类器,我们就需要看看它是如何运行的。我们使用三重交叉验证来计算这里的准确性。
-
One of the main goals of building a classifier is to use it on isolated and unknown data instances. Let's use a single datapoint and see how we can use this classifier to categorize it:
# Testing encoding on single data instance input_data = ['vhigh', 'vhigh', '2', '2', 'small', 'low'] 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)第一步是将数据转换成数字数据。我们需要使用训练中使用的标签编码器,因为我们希望它是一致的。如果输入数据点中有未知值,标签编码器会抱怨,因为它不知道如何处理这些数据。例如,如果您将列表中的第一个值从
vhigh更改为abcd,那么标签编码器将不起作用,因为它不知道如何解释该字符串。这就像一个检查输入数据点是否有效的错误检查。 -
We are now ready to predict the output class for this datapoint:
# 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方法将该标签转换回其原始形式,并打印出输出类。
提取验证曲线
在之前的配方中,我们使用了随机森林来构建分类器,但是我们不知道如何定义参数。在我们的例子中,我们处理了两个参数:n_estimators和max_depth。它们被称为超参数,分类器的性能取决于它们。当我们改变超参数时,看到性能如何受到影响会很好。这就是验证曲线出现的地方。这些曲线帮助我们理解每个超参数如何影响训练分数。基本上,所有其他参数保持不变,我们根据我们的范围改变感兴趣的超参数。然后,我们将能够想象这是如何影响分数的。
怎么做…
-
Add the following code to the same Python file, as in the previous recipe:
# Validation curves from sklearn.learning_curve 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 "\n##### 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 来提取训练和验证分数。 -
If you run it, you will see the following on your Terminal:
![How to do it…]()
-
让我们画出来:
# 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() -
Here is the figure that you'll get:
![How to do it…]()
-
Let's do the same for the
max_depthparameter: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而变化。这是终端上的输出:![How to do it…]()
-
让我们画出来:
# 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() -
If you run this code, you will get the following figure:
![How to do it…]()
提取学习曲线
学习曲线有助于我们理解训练数据集的大小如何影响机器学习模型。当您必须处理计算约束时,这非常有用。让我们通过改变训练数据集的大小来绘制学习曲线。
怎么做…
-
Add the following code to the same Python file, as in the previous recipe:
# Learning curves from sklearn.learning_curve import learning_curve classifier = RandomForestClassifier(random_state=7) parameter_grid = np.array([200, 500, 800, 1100]) train_sizes, train_scores, validation_scores = learning_curve(classifier, X, y, train_sizes=parameter_grid, cv=5) print "\n##### LEARNING CURVES #####" print "\nTraining scores:\n", train_scores print "\nValidation scores:\n", validation_scores我们希望使用大小为 200、500、800 和 1100 的训练数据集来评估性能指标。我们使用五重交叉验证,由
learning_curve方法中的cv参数指定。 -
If you run this code, you will get the following output on the Terminal:
![How to do it…]()
-
我们来绘制一下:
# 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() -
Here is the output figure:
![How to do it…]()
虽然较小的训练集似乎给出了更好的准确性,但它们容易过度拟合。如果我们选择更大的训练数据集,它会消耗更多的资源。因此,我们需要在这里进行权衡,以选择合适大小的训练数据集。
估算收入等级
我们将构建一个分类器,根据 14 个属性来估计一个人的收入档次。可能的输出等级为高于 50K 的或低于或等于 50K 的。从每个数据点都是数字和字符串的混合来看,这个数据集中有一点扭曲。数字数据是有价值的,我们不能在这些情况下使用标签编码器。我们需要设计一个可以同时处理数值和非数值数据的系统。我们将使用https://archive.ics.uci.edu/ml/datasets/Census+Income提供的人口普查收入数据集。**
**## 怎么做…
-
我们将使用已经提供给您的
income.py文件作为参考。我们将使用朴素贝叶斯分类器来实现这一点。让我们导入几个包:from sklearn import preprocessing from sklearn.naive_bayes import GaussianNB -
让我们加载数据集:
input_file = 'path/to/adult.data.txt' # Reading the data X = [] y = [] count_lessthan50k = 0 count_morethan50k = 0 num_images_threshold = 10000 -
We will use 20,000 datapoints from the datasets—10,000 for each class to avoid class imbalance. During training, if you use many datapoints that belong to a single class, the classifier tends to get biased toward this class. Therefore, it's better to use the same number of datapoints for each class:
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变量中。 -
We need to convert string attributes to numerical data while leaving out the original numerical data:
# 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()功能帮助我们识别数值数据。我们将字符串数据转换为数字数据,并将所有标签编码器存储在一个列表中,以便在我们想要对未知数据进行分类时使用它。 -
让我们训练分类器:
# Build a classifier classifier_gaussiannb = GaussianNB() classifier_gaussiannb.fit(X, y) -
让我们将数据分为训练和测试,以提取性能指标:
# Cross validation from sklearn import cross_validation X_train, X_test, y_train, y_test = cross_validation.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) -
让我们提取性能指标:
# compute F1 score of the classifier f1 = cross_validation.cross_val_score(classifier_gaussiannb, X, y, scoring='f1_weighted', cv=5) print "F1 score: " + str(round(100*f1.mean(), 2)) + "%" -
让我们看看如何对单个数据点进行分类。我们需要将数据点转换成我们的分类器能够理解的东西:
# 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) -
我们现在准备对其进行分类:
# 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方法得到输出类,用inverse_transform方法把这个标签转换回原来的形式,在终端上打印出来。**
三、预测性建模
在本章中,我们将介绍以下食谱:
- 利用支持向量机构建线性分类器
- 利用支持向量机构建非线性分类器
- 解决阶级不平衡
- 提取置信度度量
- 寻找最优超参数
- 构建事件预测器
- 估计流量
简介
预测性建模可能是数据分析中最令人兴奋的领域之一。近年来,由于大量数据在许多不同的垂直行业中可用,它受到了广泛关注。它非常常用于数据挖掘领域,用于预测未来趋势。
预测性建模是一种用于预测系统未来行为的分析技术。它是算法的集合,可以识别独立输入变量和目标响应之间的关系。我们根据观察建立一个数学模型,然后用这个模型来估计未来会发生什么。
在预测性建模中,我们需要收集具有已知响应的数据来训练我们的模型。一旦我们创建了这个模型,我们就使用一些指标来验证它,然后用它来预测未来的价值。我们可以使用许多不同类型的算法来创建预测模型。在本章中,我们将使用支持向量机来建立线性和非线性模型。
使用可能影响系统行为的许多特征来建立预测模型。例如,为了估计天气状况,我们可以使用各种类型的数据,如温度、气压、降水和其他大气过程。同样,当我们处理其他类型的系统时,在训练我们的模型之前,我们需要决定哪些因素可能会影响其行为,并将它们作为特征向量的一部分。
利用支持向量机构建线性分类器
支持向量机是监督学习模型,用于构建分类器和回归器。一个 SVM 通过求解一个数学方程组找到了两组点之间的最佳分离边界。如果你不熟悉支持向量机,这里有几个很好的教程可以让你开始:
- http://web.mit.edu/zoya/www/SVM.pdf
- http://www.support-vector.net/icml-tutorial.pdf
- http://www.svms.org/tutorials/Berwick2003.pdf
让我们看看如何使用 SVM 构建线性分类器。
做好准备
让我们可视化我们的数据来理解手头的问题。我们将使用已经提供给您的svm.py作为参考。在我们建造 SVM 之前,让我们先了解一下我们的数据。我们将使用已经提供给您的data_multivar.txt文件。让我们看看如何可视化数据。创建一个新的 Python 文件,并向其中添加以下行:
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)
我们只是导入了几个包并命名了输入文件。我们来看看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
我们需要将数据分成类,如下所示:
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])
现在我们已经分离了数据,让我们绘制它:
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()
如果运行此代码,您将看到下图:

上图中的由两种类型的点组成–实心方块 和空心方块。在机器学习行话中,我们说我们的数据由两类组成。我们的目标是建立一个模型,可以分离实心方块和空心方块。
怎么做…
-
我们需要将数据集分为训练数据集和测试数据集。在同一个 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) -
让我们使用线性内核初始化 SVM 对象。如果你不知道内核是什么,你可以查看http://www . Eric-Kim . net/Eric-Kim-net/post/1/kernel _ trick . html。在文件中添加以下几行:
params = {'kernel': 'linear'} classifier = SVC(**params) -
我们现在准备训练线性 SVM 分类器:
classifier.fit(X_train, y_train) -
我们现在可以看到分类器的表现:
utilities.plot_classifier(classifier, X_train, y_train, 'Training dataset') plt.show() -
If you run this code, you will get the following figure:
![How to do it…]()
plot_classifier功能与我们在上一章中讨论的相同。它有一些小的增加。您可以查看已经提供给您的utilities.py文件了解更多详情。 -
让我们看看在测试数据集上的表现。在同一文件中添加以下行:
y_test_pred = classifier.predict(X_test) utilities.plot_classifier(classifier, X_test, y_test, 'Test dataset') plt.show() -
If you run this code, you will see the following figure:
![How to do it…]()
-
让我们计算训练集的精确度。在同一文件中添加以下行:
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" -
If you run this code, you will see the following on your Terminal:
![How to do it…]()
-
最后让我们来看看测试数据集的分类报告:
```py
print "#"*30
print "\nClassification report on test dataset\n"
print classification_report(y_test, y_test_pred, target_names=target_names)
print "#"*30 + "\n"
```
- If you run this code, you will see the following on the Terminal:

从我们可视化数据的图中,我们可以看到实心方块被空的方块完全包围。这意味着数据不是线性可分的。我们不能画一条漂亮的直线来分隔两组点!因此,我们需要一个非线性分类器来分离这些数据点。
利用支持向量机构建非线性分类器
一个 SVM 提供了多种选项来构建一个非线性分类器。我们需要使用各种核构建一个非线性分类器。为了简单起见,我们在这里考虑两种情况。当我们想要表示两组点之间的曲线边界时,我们可以使用多项式函数或径向基函数来实现。
怎么做…
-
For the first case, let's use a polynomial kernel to build a nonlinear classifier. In the same Python file, search for the following line:
params = {'kernel': 'linear'}将这一行改为:
params = {'kernel': 'poly', 'degree': 3}这意味着我们使用一个 3 次多项式函数。如果你增加度数,这意味着我们允许多项式更有曲线。然而,曲线性是有代价的,因为它需要更多的时间来训练,因为它的计算成本更高。
-
If you run this code now, you will get the following figure:
![How to do it…]()
-
You will also see the following classification report printed on your Terminal:
![How to do it…]()
-
We can also use a radial basis function kernel to build a nonlinear classifier. In the same Python file, search for the following line:
params = {'kernel': 'poly', 'degree': 3}将此行替换为以下一行:
params = {'kernel': 'rbf'} -
If you run this code now, you will get the following figure:
![How to do it…]()
-
You will also see the following classification report printed on your Terminal:
![How to do it…]()
解决阶级不平衡
直到现在,我们处理的问题是我们所有的类中有相似数量的数据点。在现实世界中,我们可能无法以如此有序的方式获取数据。有时,一个类中数据点的数量比其他类中数据点的数量多得多。如果发生这种情况,那么分类器往往会产生偏差。边界不会反映数据的真实性质,因为两个类之间的数据点数量有很大差异。因此,解释这种差异并消除它以使我们的分类器保持不偏不倚变得很重要。
怎么做…
-
让我们加载数据:
input_file = 'data_multivar_imbalance.txt' X, y = utilities.load_data(input_file) -
Let's visualize the data. The code for visualization is exactly the same as it was in the previous recipe. You can also find it in the file named
svm_imbalance.pyalready provided to you. If you run it, you will see the following figure:![How to do it…]()
-
Let's build an SVM with a linear kernel. The code is the same as it was in the previous recipe. If you run it, you will see the following figure:
![How to do it…]()
-
You might wonder why there's no boundary here! Well, this is because the classifier is unable to separate the two classes at all, resulting in 0% accuracy for
Class-0. You will also see a classification report printed on your Terminal, as shown in the following screenshot:![How to do it…]()
如我们所料,
Class-0的精度为 0%。 -
Let's go ahead and fix this! In the Python file, search for the following line:
params = {'kernel': 'linear'}将前一行改为:
params = {'kernel': 'linear', 'class_weight': 'auto'}class_weight参数将计算每个类中的数据点数量,以调整权重,从而使不平衡不会对性能产生不利影响。 -
You will get the following figure once you run this code:
![How to do it…]()
-
Let's look at the classification report, as follows:
![How to do it…]()
如我们所见,Class-0现在以非零百分比精度被检测到。
提取置信度度量
知道我们对未知数据分类的置信度就好了。当一个新的数据点被归入一个已知的类别时,我们可以训练 SVM 来计算这个输出的置信水平。
怎么做…
-
完整的代码在已经提供给你的
svm_confidence.py文件中给出。我们在这里只讨论食谱的核心。让我们定义一些输入数据:# Measure distance from the boundary 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]]) -
让我们测量到边界的距离:
print "\nDistance from the boundary:" for i in input_datapoints: print i, '-->', classifier.decision_function(i)[0] -
You will see the following printed on your Terminal:
![How to do it…]()
-
Distance from the boundary gives us some information about the datapoint, but it doesn't exactly tell us how confident the classifier is about the output tag. To do this, we need Platt scaling. This is a method that converts the distance measure into probability measure between classes. You can check out the following tutorial to learn more about Platt scaling: http://fastml.com/classifier-calibration-with-platts-scaling-and-isotonic-regression. Let's go ahead and train an SVM using Platt scaling:
# Confidence measure params = {'kernel': 'rbf', 'probability': True} classifier = SVC(**params)probability参数告诉 SVM,它也应该训练计算概率。 -
让我们训练分类器:
classifier.fit(X_train, y_train) -
Let's compute the confidence measurements for these input datapoints:
print "\nConfidence measure:" for i in input_datapoints: print i, '-->', classifier.predict_proba(i)[0]predict_proba功能测量置信度值。 -
You will see the following on your Terminal:
![How to do it…]()
-
让我们看看点在哪里,期待边界:
utilities.plot_classifier(classifier, input_datapoints, [0]*len(input_datapoints), 'Input datapoints', 'True') -
If you run this, you will get the following figure:
![How to do it…]()
寻找最优超参数
如前一章所述,超参数在决定分类器的性能时非常重要。让我们看看如何为支持向量机提取最优超参数。
怎么做…
-
完整的代码在已经提供给你的
perform_grid_search.py文件中给出。我们在这里只讨论食谱的核心部分。我们将在这里使用交叉验证,这在前面的食谱中已经介绍过了。加载数据并将其拆分为训练和测试数据集后,将以下内容添加到文件中:# Set the parameters by cross-validation parameter_grid = [ {'kernel': ['linear'], 'C': [1, 10, 50, 600]}, {'kernel': ['poly'], 'degree': [2, 3]}, {'kernel': ['rbf'], 'gamma': [0.01, 0.001], 'C': [1, 10, 50, 600]}, ] -
让我们定义我们想要使用的指标:
metrics = ['precision', 'recall_weighted'] -
让我们开始搜索每个指标的最优超参数:
for metric in metrics: print "\n#### Searching optimal hyperparameters for", metric classifier = grid_search.GridSearchCV(svm.SVC(C=1), parameter_grid, cv=5, scoring=metric) classifier.fit(X_train, y_train) -
我们来看看分数:
print "\nScores across the parameter grid:" for params, avg_score, _ in classifier.grid_scores_: print params, '-->', round(avg_score, 3) -
让我们打印最佳参数集:
print "\nHighest scoring parameter set:", classifier.best_params_ -
If you run this code, you will see the following on your Terminal:
![How to do it…]()
-
正如我们在上图中可以看到的,它搜索所有的最优超参数。在这种情况下,超参数是核的类型、C 值和伽玛。它将尝试这些参数的各种组合,以找到最佳参数。让我们在测试数据集上测试一下:
y_true, y_pred = y_test, classifier.predict(X_test) print "\nFull performance report:\n" print classification_report(y_true, y_pred) -
If you run this code, you will see the following on your Terminal:
![How to do it…]()
构建事件预测器
让我们把所有这些知识应用到现实世界的问题中。我们将建造一个 SVM 来预测进出建筑物的人数。数据集可在获取。我们将使用这个数据集的稍加修改的版本,以便更容易分析。修改后的数据可以在已经提供给你的building_event_binary.txt和building_event_multiclass.txt文件中找到。
做好准备
在开始构建模型之前,让我们先了解一下数据格式。building_event_binary.txt中的每一行由六个逗号分隔的字符串组成。这六个字符串的顺序如下:
- 一天
- 日期
- 时间
- 走出大楼的人数
- 进入大楼的人数
- 指示它是否是事件的输出
第一个五个字符串组成输入数据,我们的任务是预测建筑内是否有事件发生。
building_event_multiclass.txt中的每一行由六个逗号分隔的字符串组成。从输出是建筑中正在发生的事件的确切类型的意义上来说,这比前一个文件更精细。这六个字符串的顺序如下:
- 一天
- 日期
- 时间
- 走出大楼的人数
- 进入大楼的人数
- 指示事件类型的输出
前五个字符串构成输入数据,我们的任务是预测建筑物中正在发生什么类型的事件。
怎么做…
-
We will use
event.pythat's already provided to you for reference. Create a new Python file, and add the following lines: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中。 -
让我们将数据转换成数字形式:
# 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) -
让我们使用径向基函数、普拉特缩放和类平衡来训练 SVM:
# Build SVM params = {'kernel': 'rbf', 'probability': True, 'class_weight': 'auto'} classifier = SVC(**params) classifier.fit(X, y) -
我们现在准备执行交叉验证:
# Cross validation from sklearn import cross_validation accuracy = cross_validation.cross_val_score(classifier, X, y, scoring='accuracy', cv=3) print "Accuracy of the classifier: " + str(round(100*accuracy.mean(), 2)) + "%" -
让我们在一个新的数据点上测试我们的 SVM
-
如果您运行该代码,您将在您的终端上看到以下输出:
Accuracy of the classifier: 89.88% Output class: event -
如果您使用
building_event_multiclass.txt文件代替building_event_binary.txt作为输入数据文件,您将在您的终端上看到以下输出:Accuracy of the classifier: 65.9% Output class: eventA
估计流量
支持向量机的一个有趣的应用是根据相关数据预测流量。在之前的食谱中,我们使用了 SVM 作为分类器。在这个食谱中,我们将使用它作为回归来估计流量。
做好准备
我们将使用https://archive.ics.uci.edu/ml/datasets/Dodgers+Loop+Sensor提供的数据集。这是一个数据集,统计了洛杉矶道奇主场体育场棒球比赛期间经过的汽车数量。我们将使用稍微修改的数据集形式,以便更容易分析。您可以使用已经提供给您的traffic_data.txt文件。该文件中的每一行都包含逗号分隔的字符串,格式如下:
- 一天
- 时间
- 对手队
- 无论棒球比赛是否正在进行
- 路过的汽车数量
怎么做…
-
Let's see how to build an SVM regressor. We will use
traffic.pythat's already provided to you as a reference. Create a new Python file, and add the following lines:# 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中。 -
让我们对这个数据进行编码:
# 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) -
Let's build and train the SVM regressor using the radial basis function:
# Build SVR params = {'kernel': 'rbf', 'C': 10.0, 'epsilon': 0.2} regressor = SVR(**params) regressor.fit(X, y)在前几行中,
C参数指定了错误分类的惩罚,epsilon指定了不应用惩罚的限制。 -
让我们执行交叉验证来检查回归器的性能:
# 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) -
让我们在数据点
# 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])上测试一下
-
如果您运行此代码,您将会在您的终端上看到以下内容:
Mean absolute error = 4.08 Predicted traffic: 29
四、无监督学习聚类
在本章中,我们将介绍以下食谱:
- 使用 k-均值算法对数据进行聚类
- 使用矢量量化压缩图像
- 建立均值漂移聚类模型
- 使用聚集聚类对数据进行分组
- 评估聚类算法的性能
- 使用 DBSCAN 算法自动估计集群数量
- 在股票市场数据中寻找模式
- 构建客户细分模型
简介
无监督学习是机器学习中的一种范式,在这种范式中,我们不依赖于有标签的训练数据来构建模型。在此之前,我们处理以某种方式标记的数据。这意味着学习算法可以查看这些数据,并学会根据标签对它们进行分类。在无监督学习的世界里,我们没有这种奢侈!当我们想要使用一些相似性度量在数据集中寻找子组时,使用这些算法。
最常见的方法之一是聚类。你一定听说过这个词经常被使用。我们主要使用它进行数据分析,我们希望在数据中找到聚类。这些聚类通常是使用某种相似性度量(如欧氏距离)找到的。无监督学习广泛应用于数据挖掘、医学影像、股市分析、计算机视觉、市场细分等领域。
使用 k-means 算法对数据进行聚类
k-means 算法是最流行的聚类算法之一。该算法用于使用数据的各种属性将输入数据划分为 k 子组。分组是使用优化技术来实现的,其中我们试图最小化数据点和聚类的相应质心之间的距离的平方和。如果您需要快速复习,您可以在http://www.onmyphd.com/?p=k-means.clustering&CK entry = 1了解更多 k-means。
怎么做…
-
该配方的完整代码在已经提供给您的
kmeans.py文件中给出。让我们看看它是如何建造的。新建一个 Python 文件,导入以下包:import numpy as np import matplotlib.pyplot as plt from sklearn import metrics from sklearn.cluster import KMeans import utilities -
让我们加载输入数据并定义集群的数量。我们将使用已经提供给您的
data_multivar.txt文件:data = utilities.load_data('data_multivar.txt') num_clusters = 4 -
We need to see what the input data looks like. Let's go ahead and add the following lines of the code to the Python file:
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(())如果运行这段代码,您将得到下图:
![How to do it…]()
-
我们现在准备训练模型。让我们初始化 k-means 对象并训练它:
kmeans = KMeans(init='k-means++', n_clusters=num_clusters, n_init=10) kmeans.fit(data) -
现在数据已经训练好了,我们需要可视化边界。让我们继续将以下代码行添加到 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()]) -
我们只是在一个点网格上评估了模型。让我们绘制这些结果来查看边界:
# 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) -
Let's overlay the centroids on top of it:
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()如果运行这段代码,您应该会看到下图:
![How to do it…]()
使用矢量量化压缩图像
k-means 聚类的主要应用之一是矢量量化。简单来说,矢量量化就是 N 维版本的“四舍五入”。当我们处理 1D 数据时,例如数字,我们使用舍入技术来减少存储该值所需的内存。例如,如果我们想要精确到小数点后第二位,我们只存储 23.73,而不是存储 23.73473572。或者,如果我们不在乎小数位数,我们可以只存储 24 位。这取决于我们的需求和我们愿意做出的权衡。
同样,当我们把这个概念扩展到 N 维数据时,就变成了矢量量化。当然还有更多的细微差别!你可以在http://www.data-compression.com/vq.shtml了解更多。矢量量化在图像压缩中被广泛使用,其中我们使用比原始图像更少的比特来存储每个像素,以实现压缩。
怎么做…
-
这个食谱的完整代码已经在提供给你的文件中给出了。让我们看看它是如何建造的。我们将从导入所需的包开始。创建一个新的 Python 文件,并添加以下几行:
import argparse import numpy as np from scipy import misc from sklearn import cluster import matplotlib.pyplot as plt -
让我们创建一个函数来解析输入参数。我们将能够传递图像和每个像素的位数作为输入参数:
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 -
让我们创建一个函数来压缩输入图像:
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 -
一旦我们压缩了图像,我们需要看看它是如何影响质量的。让我们定义一个函数来绘制输出图像:
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) -
我们现在准备使用所有这些功能。让我们定义
main函数,该函数接受输入参数,对其进行处理,并提取输出图像: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 "\nThe size of the image will be reduced by a factor of", 8.0/args.num_bits print "\nCompression rate = " + str(compression_rate) + "%" -
让我们加载输入图像:
# Load input image input_image = misc.imread(input_file, True).astype(np.uint8) # original image plot_image(input_image, 'Original image') -
让我们使用输入参数
# 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()来压缩这个图像
-
We are now ready to run the code. Run the following command on your Terminal:
$ python vector_quantization.py --input-file flower_image.jpg --num-bits 4输入图像如下所示:
![How to do it…]()
您应该获得以下压缩图像作为输出:
![How to do it…]()
-
Let's compress the image further by reducing the number of bits to 2. Run the following command on your Terminal:
$ python vector_quantization.py --input-file flower_image.jpg --num-bits 2您应该获得以下压缩图像作为输出:
![How to do it…]()
-
If you reduce the number of bits to 1, you can see that it will become a binary image with black and white as the only two colors. Run the following command:
```py
$ python vector_quantization.py --input-file flower_image.jpg --num-bits 1
```
您将获得以下输出:

建立均值漂移聚类模型
均值漂移是一种强大的无监督学习算法,用于聚类数据点。它将数据点的分布视为概率密度函数,并试图在特征空间中找到模式。这些模式基本都是局部极大值对应的点。均值漂移算法的主要优点是不需要我们事先知道聚类的数量。
假设我们有一组输入点,我们试图在其中寻找聚类,而不知道我们要寻找多少个聚类。均值漂移算法认为这些点是从概率密度函数中采样的。如果数据点中有簇,那么它们对应于概率密度函数的峰值。该算法从随机点开始,并向这些峰值迭代收敛。您可以在了解更多信息。
怎么做…
-
这个配方的完整代码在已经提供给你的
mean_shift.py文件中给出。让我们看看它是如何建造的。创建一个新的 Python 文件,并导入几个必需的包:import numpy as np from sklearn.cluster import MeanShift, estimate_bandwidth import utilities -
让我们从已经提供给您的
data_multivar.txt文件中加载输入数据:# Load data from input file X = utilities.load_data('data_multivar.txt') -
通过指定输入参数
# Estimating the bandwidth bandwidth = estimate_bandwidth(X, quantile=0.1, 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 -
让我们继续想象一下:
# Plot the points and centroids import matplotlib.pyplot as plt from itertools import cycle plt.figure() # specify marker shapes for different clusters markers = '.*xv' -
迭代数据点并绘制它们:
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], marker=marker, color='k') # plot the centroid of the current cluster centroid = centroids[i] plt.plot(centroid[0], centroid[1], marker='o', markerfacecolor='k', markeredgecolor='k', markersize=15) plt.title('Clusters and their centroids') plt.show() -
If you run this code, you will get the following output:
![How to do it…]()
使用凝聚聚类对数据进行分组
在我们谈论聚集聚类之前,我们需要了解层次聚类。层次聚类 是指一组通过连续拆分或合并来构建树状聚类的聚类算法。这种层次结构用树来表示。
层次聚类算法可以是自下而上,也可以是自上而下。这是什么意思?在自下而上的算法中,每个数据点都被视为一个单独的集群,只有一个对象。然后这些集群被连续合并,直到所有集群合并成一个巨大的集群。这就是所谓的 集聚集群。另一方面,自上而下的算法从一个巨大的集群开始,并连续分裂这些集群,直到到达单个数据点。您可以在http://NLP . Stanford . edu/IR-book/html/html mle dition/hierarchy-aggregate-clustering-1 . html了解更多。
怎么做…
-
这个食谱的完整代码在提供给你的
agglomerative.py文件中给出。让我们看看它是如何建造的。创建一个新的 Python 文件,并导入必要的包:import numpy as np import matplotlib.pyplot as plt from sklearn.cluster import AgglomerativeClustering from sklearn.neighbors import kneighbors_graph -
让我们定义执行聚集聚类所需的函数:
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) -
让我们提取标签并为图形指定标记的形状:
# extract labels labels = model.labels_ ` # specify marker shapes for different clusters markers = '.vx' -
迭代数据点,并使用不同的标记相应地绘制它们:
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) -
在中,为了展示聚集聚类的优势,我们需要在空间上链接但在空间上彼此靠近的数据点上运行它。我们希望链接的数据点属于同一个集群,而不是空间上彼此接近的数据点。让我们定义一个函数来获取螺旋上的一组数据点:
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) -
在前面的函数中,我们在曲线中添加了一些噪声,因为它增加了一些不确定性。我们来定义一下这个函数:
def add_noise(x, y, amplitude): X = np.concatenate((x, y)) X += amplitude * np.random.randn(2, X.shape[1]) return X.T -
让我们定义另一个函数来获取玫瑰曲线上的数据点:
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) -
为了增加更多的多样性,我们也来定义一个 次摆线:T0
-
我们现在准备定义
main函数: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() -
If you run this code, you will get the following image if we don't use any connectivity:

- The second output image looks like the following:

正如我们所见,使用连接特性,我们可以根据数据点的空间位置,对相互链接的数据点进行分组,而不是对它们进行聚类。
评估聚类算法的性能
到目前为止,我们构建了不同的聚类算法,但没有衡量它们的性能。在监督学习中,我们只是将预测值与原始标签进行比较,以计算它们的准确性。在无监督学习中,我们没有任何标签。因此,我们需要一种方法来衡量我们的算法的性能。
衡量聚类算法的一个好方法是看聚类的分离程度。集群分离得好吗?集群中的数据点是否足够紧密?我们需要一个可以量化这种行为的指标。我们将使用一个度量,称为轮廓系数分数。这个分数是为每个数据点定义的。该系数定义如下:
得分=(x–y)/最大值(x,y)
这里,x是当前数据点和同一簇中所有其他数据点之间的平均距离;y是当前数据点和下一个最近簇中所有数据点之间的平均距离。
怎么做…
-
这个配方的完整代码在已经提供给你的
performance.py文件中给出。让我们看看它是如何建造的。新建一个 Python 文件,导入以下包:import numpy as np import matplotlib.pyplot as plt from sklearn import metrics from sklearn.cluster import KMeans import utilities -
让我们从已经提供给您的
data_perf.txt文件中加载输入数据:# Load data data = utilities.load_data('data_perf.txt') -
为了确定最佳聚类数,让我们遍历一系列值,看看它的峰值在哪里:
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 "\nNumber of clusters =", i print "Silhouette score =", score scores.append(score) -
让我们绘制图表,看看它的峰值在哪里:
# 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() -
If you run this code, you will get the following output on the Terminal:
![How to do it…]()
-
The bar graph looks like the following:
![How to do it…]()
-
As per these scores, the best configuration is five clusters. Let's see what the data actually looks like:
![How to do it…]()
我们可以直观地确认数据实际上有五个集群。我们刚才举了一个包含五个不同聚类的小数据集的例子。当你在处理一个包含不容易可视化的高维数据的巨大数据集时,这个方法变得非常有用。
使用 DBSCAN 算法自动估计集群数量
当我们讨论 k-means 算法时,我们看到我们必须给出聚类的数量作为输入参数之一。在现实世界中,我们不会有这些信息。我们绝对可以使用轮廓系数分数来扫描参数空间以找出最佳聚类数,但是这将是一个昂贵的过程!如果有一种方法可以告诉我们数据中的聚类数,那不是很好吗?这就是带有噪声的应用的基于密度的空间聚类 ( DBSCAN )出现的地方。
这是通过将数据点视为密集集群组来实现的。如果一个点属于一个簇,那么应该有很多其他的点属于同一个簇。我们可以控制的参数之一是该点与其他点的最大距离。这叫做 T1【ε】T2。给定簇中的任何两点都不应比ε更远。你可以在了解更多。pdf 。这种方法的主要优点之一是可以处理异常值。如果有一些点单独位于低密度区域,DBSCAN 会将这些点检测为异常值,而不是强制它们成为一个簇。
怎么做…
-
这个配方的完整代码在已经提供给你的
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 from utilities import load_data -
从
data_perf.txt文件加载输入数据。这是我们在前面的配方中使用的同一个文件,它将帮助我们比较同一数据集上的方法:# Load input data input_file = 'data_perf.txt' X = load_data(input_file) -
我们需要找到最佳参数。让我们初始化几个变量:
# 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 -
我们来扫一下参数空间:
for eps in eps_grid: # Train DBSCAN clustering model model = DBSCAN(eps=eps, min_samples=5).fit(X) # Extract labels labels = model.labels_ -
对于每次迭代,我们需要提取性能度量:
# Extract performance metric silhouette_score = round(metrics.silhouette_score(X, labels), 4) silhouette_scores.append(silhouette_score) print "Epsilon:", eps, " --> silhouette score:", silhouette_score -
我们需要存储最佳分数及其相关的ε值:
if silhouette_score > silhouette_score_max: silhouette_score_max = silhouette_score eps_best = eps model_best = model labels_best = labels -
让我们绘制条形图:
# 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 "\nBest epsilon =", eps_best -
让我们存储最好的型号和标签:
# Associated model and labels for best epsilon model = model_best labels = labels_best -
一些数据点可能仍未分配。我们需要识别它们,如下所示:
# Check for unassigned datapoints in the labels offset = 0 if -1 in labels: offset = 1 -
提取聚类数:
```py
# Number of clusters in the data
num_clusters = len(set(labels)) - offset
print "\nEstimated number of clusters =", num_clusters
```
- 我们需要提取所有核心样本:
```py
# Extracts the core samples from the trained model
mask_core = np.zeros(labels.shape, dtype=np.bool)
mask_core[model.core_sample_indices_] = True
```
- 让我们想象一下最终的集群。我们将从提取唯一标签集并指定不同的标记开始:
```py
# Plot resultant clusters
plt.figure()
labels_uniq = set(labels)
markers = cycle('vo^s<>')
```
- 让我们遍历集群,并使用不同的标记绘制数据点:
```py
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()
```
- If you run this code, you will get the following on your Terminal:

- You will get the following bar graph:

- Let's look the labeled datapoints along with unassigned datapoints marked by solid points in the following figure:

在股市数据中寻找模式
让我们看看如何利用无监督学习进行股市分析。我们将在不知道有多少集群的假设下运行。由于我们不知道聚类的数量,我们将使用一种称为 亲和传播的算法来聚类。它试图在我们的数据中为每个集群找到一个有代表性的数据点。它试图找到数据点对之间的相似性度量,并将我们所有的数据点视为它们各自集群的潜在代表,也称为样本。您可以在http://www . cs . Columbia . edu/~ del Bert/docs/DDueck-thesis _ small . pdf了解更多
在这个食谱中,我们将分析公司在特定时间内的股票市场变化。我们的目标是找出随着时间的推移,哪些公司在报价方面表现相似。
怎么做…
-
这个配方的完整代码在已经提供给你的
stock_market.py文件中给出。让我们看看它是如何建造的。新建一个 Python 文件,导入以下包:import json import datetime import numpy as np import matplotlib.pyplot as plt from sklearn import covariance, cluster from matplotlib.finance import quotes_historical_yahoo_ochl as quotes_yahoo -
我们需要一个包含所有符号和相关名称的文件。该信息位于提供给您的
symbol_map.json文件中。让我们加载它,如下所示:# Input symbol file symbol_file = '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中读取数据
-
让我们为这个分析指定一个时间段。我们将使用这些开始和结束日期来加载输入数据:
# Choose a time period start_date = datetime.datetime(2004, 4, 5) end_date = datetime.datetime(2007, 6, 2) -
让我们读取输入数据:
quotes = [quotes_yahoo(symbol, start_date, end_date, asobject=True) for symbol in symbols] -
由于我们需要一些特征点进行分析,所以我们将每天使用开盘价和收盘价的差值来分析数据:
# 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 -
让我们建立一个图形模型:
# Build a graph model from the correlations edge_model = covariance.GraphLassoCV() -
我们需要在使用之前对数据进行标准化:
# Standardize the data X = delta_quotes.copy().T X /= X.std(axis=0) -
我们来用这个数据训练模型:
# Train the model with np.errstate(invalid='ignore'): edge_model.fit(X) -
我们现在准备构建集群模型:
```py
# 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])
```
- If you run this code, you will get the following output on the Terminal:

建立客户细分模型
无监督学习的主要应用之一是市场细分。这是我们没有随时可用的标签数据的时候,但细分市场很重要,这样人们就可以瞄准单个群体。这在广告、库存管理、实施分销策略、大众媒体等方面非常有用。让我们继续将无监督学习应用到这样一个案例中,看看它是如何有用的。
我们将与一个批发供应商及其客户打交道。我们将使用 https://archive.ics.uci.edu/ml/datasets/Wholesale+customers 的可用数据。该电子表格包含有关客户消费不同类型商品的数据,我们的目标是找到集群,以便他们能够优化销售和分销策略。
怎么做…
-
这个配方的完整代码在已经提供给你的
customer_segmentation.py文件中给出。让我们看看它是如何建造的。新建一个 Python 文件,导入以下包:import csv import numpy as np from sklearn import cluster, covariance, manifold from sklearn.cluster import MeanShift, estimate_bandwidth import matplotlib.pyplot as plt -
让我们从已经提供给您的
wholesale.csv文件中加载输入数据:# Load data from input file input_file = 'wholesale.csv' file_reader = csv.reader(open(input_file, 'rb'), 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) -
让我们建立一个均值漂移模型,就像我们在早期食谱中所做的那样:
# 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 "\nNumber of clusters in input data =", num_clusters -
让我们打印我们获得的簇的质心,如下所示:
print "\nCentroids 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]) -
让我们可视化几个特性来了解输出:
# 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() -
If you run this code, you will get the following output on the Terminal:
![How to do it…]()
-
You will get the following image that depicts the centroids for the features milk and groceries, where milk is on the X-axis and groceries is on the Y-axis:
![How to do it…]()
五、构建推荐引擎
在本章中,我们将介绍以下食谱:
- 为数据处理构建函数组合
- 构建机器学习管道
- 寻找最近的邻居
- 构建 k 近邻分类器
- 构造 k 近邻回归器
- 计算欧几里德距离分数
- 计算皮尔逊相关分数
- 在数据集中查找相似的用户
- 生成电影推荐
简介
推荐引擎是一个可以预测用户可能感兴趣的模型。当我们把这个应用到电影的上下文中,这就变成了一个电影推荐引擎。我们通过预测当前用户对项目的评价来过滤数据库中的项目。这有助于我们将用户与数据集中正确的内容联系起来。为什么这是相关的?如果你有一个庞大的目录,那么用户可能会也可能不会找到所有相关的内容。通过推荐合适的内容,你增加了消费。网飞等公司非常依赖推荐来吸引用户。
推荐引擎通常使用协作过滤或基于内容的过滤来产生一组推荐。这两种方法的区别在于挖掘建议的方式。协同过滤根据当前用户过去的行为以及其他用户给出的评分来构建模型。然后我们使用这个模型来预测这个用户可能感兴趣的东西。另一方面,基于内容的过滤使用项目本身的特征,以便向用户推荐更多的项目。项目之间的相似性是这里的主要驱动力。在这一章中,我们将重点讨论协同过滤。
构建用于数据处理的函数组合
任何机器学习系统的主要部分之一是数据处理管道。在将数据输入机器学习算法进行训练之前,我们需要以不同的方式对其进行处理,以使其适合该算法。拥有一个强大的数据处理管道对于构建一个精确且可扩展的机器学习系统大有帮助。有许多基本功能可用,数据处理管道通常由这些功能的组合组成。与其以嵌套或循环的方式调用这些函数,不如使用函数式编程范式来构建组合。让我们来看看如何将这些函数组合起来,形成一个可重用的函数组合。在这个食谱中,我们将创建三个基本函数,并看看如何组成一个管道。
怎么做…
-
新建一个 Python 文件,添加如下一行:
import numpy as np -
让我们定义一个函数,将
3添加到数组的每个元素中:def add3(input_array): return map(lambda x: x+3, input_array) -
让我们定义第二个函数将
2与数组的每个元素相乘:def mul2(input_array): return map(lambda x: x*2, input_array) -
让我们定义第三个函数,从数组的每个元素中减去【T0:
def sub5(input_array): return map(lambda x: x-5, input_array) -
Let's define a function composer that takes functions as input arguments and returns a composed function. This composed function is basically a function that applies all the input functions in sequence:
def function_composer(*args): return reduce(lambda f, g: lambda x: f(g(x)), args)我们使用
reduce函数,通过依次应用这些函数来组合所有的输入函数。 -
我们现在已经准备好使用这个函数编辑器了。让我们定义一些数据和一系列操作:
if __name__=='__main__': arr = np.array([2,5,4,7]) print "\nOperation: add3(mul2(sub5(arr)))" -
如果我们使用常规方法,我们将依次应用此方法,如下所示:
arr1 = add3(arr) arr2 = mul2(arr1) arr3 = sub5(arr2) print "Output using the lengthy way:", arr3 -
让我们用函数 composer 在一行中实现同样的事情:
func_composed = function_composer(sub5, mul2, add3) print "Output using function composition:", func_composed(arr) -
我们也可以用前面的方法在一行中做同样的事情,但是符号变得非常嵌套和不可读。还有,这是不能重复使用的!如果你想重用这个操作序列,你必须重新写一遍整件事:
print "\nOperation: sub5(add3(mul2(sub5(mul2(arr)))))\nOutput:", \ function_composer(mul2, sub5, mul2, add3, sub5)(arr) -
If you run this code, you will get the following output on the Terminal:

构建机器学习管道
scikit-learn 库提供了构建机器学习管道的功能。我们只需要指定函数,它将构建一个复合对象,使数据通过整个管道。该管道可以包括预处理、特征选择、监督学习、无监督学习等功能。在本食谱中,我们将构建一个管道来获取输入特征向量,选择顶部的 k 特征,然后使用随机森林分类器对它们进行分类。
怎么做…
-
新建一个 Python 文件,导入以下包:
from sklearn.datasets import samples_generator from sklearn.ensemble import RandomForestClassifier from sklearn.feature_selection import SelectKBest, f_regression from sklearn.pipeline import Pipeline -
Let's generate some sample data to play with:
# generate sample data X, y = samples_generator.make_classification( n_informative=4, n_features=20, n_redundant=0, random_state=5)这条线生成
20维特征向量,因为这是默认值。您可以使用上一行中的n_features参数进行更改。 -
管道的第一步是选择 k 最佳特征,然后进一步使用数据点。在这种情况下,让我们将
k设置为10:# Feature selector selector_k_best = SelectKBest(f_regression, k=10) -
下一步是使用随机森林分类器对数据进行分类:
# Random forest classifier classifier = RandomForestClassifier(n_estimators=50, max_depth=4) -
We are now ready to build the pipeline. The pipeline method allows us to use predefined objects to build the pipeline:
# Build the machine learning pipeline pipeline_classifier = Pipeline([('selector', selector_k_best), ('rf', classifier)])我们还可以为管道中的块指定名称。在前一行中,我们将
selector名称分配给我们的特征选择器,rf分配给我们的随机森林分类器。你可以在这里随意使用任何其他名字! -
我们还可以在进行的同时更新这些参数。我们可以使用上一步中指定的名称来设置参数。例如,如果我们想在特征选择器中将
k设置为6,在随机森林分类器中将n_estimators设置为25,我们可以像下面的代码中那样做。请注意,这些是上一步中给出的变量名:pipeline_classifier.set_params(selector__k=6, rf__n_estimators=25) -
让我们继续训练分类器:
# Training the classifier pipeline_classifier.fit(X, y) -
让我们预测训练数据的输出:
# Predict the output prediction = pipeline_classifier.predict(X) print "\nPredictions:\n", prediction -
让我们估计一下这个分类器的性能:
# Print score print "\nScore:", pipeline_classifier.score(X, y) -
我们还可以看到哪些功能被选中。让我们继续打印它们:
```py
# 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 "\nSelected features (0-indexed):", ', '.join([str(x) for x in selected_features])
```
- If you run this code, you will get the following output on your Terminal:

它是如何工作的…
选择 k 最佳特征的优势在于,我们将能够处理低维数据。这有助于降低计算复杂度。我们选择 k 最佳特征的方式是基于单变量特征选择。这将执行单变量统计测试,然后从特征向量中提取表现最好的特征。单变量统计检验是指涉及单个变量的分析技术。
一旦这些测试被执行,特征向量中的每个特征都被分配一个分数。基于这些分数,我们选择顶部的 k 特征。我们将此作为分类器管道中的预处理步骤。一旦我们提取了顶部的 k 特征,就形成了一个 k 维特征向量,并将其用作随机森林分类器的输入训练数据。
寻找最近的邻居
最近邻模型是指一类通用算法,旨在根据训练数据集中最近邻的数量做出决策。让我们看看如何找到最近的邻居。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt from sklearn.neighbors import NearestNeighbors -
让我们创建一些示例二维数据:
# 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]]) -
我们的目标是找到任意给定点的三个最近邻居。我们来定义这个参数:
# Number of neighbors we want to find num_neighbors = 3 -
让我们定义一个不在输入数据中的随机数据点:
# Input point input_point = [2.6, 1.7] -
我们需要看看这些数据是什么样的。我们来绘制一下,如下:
# Plot datapoints plt.figure() plt.scatter(X[:,0], X[:,1], marker='o', s=25, color='k') -
为了找到最近的邻居,我们需要用正确的参数定义
NearestNeighbors对象,并在输入数据上训练它:# Build nearest neighbors model knn = NearestNeighbors(n_neighbors=num_neighbors, algorithm='ball_tree').fit(X) -
我们现在可以找到输入点到输入数据中所有点的距离:
distances, indices = knn.kneighbors(input_point) -
We can print the k-nearest neighbors, as follows:
# Print the 'k' nearest neighbors print "\nk nearest neighbors" for rank, index in enumerate(indices[0][:num_neighbors]): print str(rank+1) + " -->", X[index]indices数组已经排序,所以我们只需要解析它并打印数据点。 -
让我们绘制输入数据点并突出显示
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() -
If you run this code, you will get the following output on your Terminal:

- Here is the plot of the input datapoints:

- The second output figure depicts the location of the test datapoint and the three nearest neighbors, as shown in the following screenshot:

构建 k 近邻分类器
k 最近邻是算法,利用训练数据集中的 k 最近邻来寻找未知物体的类别。当我们想要找到一个未知点所属的类时,我们会找到 k 近邻,并进行多数投票。让我们来看看如何构造这个。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt import matplotlib.cm as cm from sklearn import neighbors, datasets from utilities import load_data -
We will use the
data_nn_classifier.txtfile for input data. Let's load this input data:# Load input data input_file = 'data_nn_classifier.txt' data = load_data(input_file) X, y = data[:,:-1], data[:,-1].astype(np.int)前两列包含输入数据,最后一列包含标签。因此,我们将它们分为
X和y,如前面的代码所示。 -
Let's visualize the input data:
# 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')我们遍历所有的数据点,并使用适当的标记来分隔类。
-
为了构建分类器,我们需要指定我们想要考虑的最近邻居的数量。我们来定义这个参数:
# Number of nearest neighbors to consider num_neighbors = 10 -
为了可视化边界,我们需要定义一个网格,并评估该网格上的分类器。让我们定义步长:
# step size of the grid h = 0.01 -
我们现在准备构建 k 近邻分类器。让我们定义并训练它:
# Create a K-Neighbours Classifier model and train it classifier = neighbors.KNeighborsClassifier(num_neighbors, weights='distance') classifier.fit(X, y) -
我们需要创建一个网格来绘制边界。让我们对此进行定义,如下所示:
# 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)) -
让我们评估所有点的分类器输出:
# Compute the outputs for all the points on the mesh predicted_values = classifier.predict(np.c_[x_grid.ravel(), y_grid.ravel()]) -
让我们绘制一下,如下:
# 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) -
现在我们绘制了颜色网格,让我们覆盖训练数据点,看看它们相对于边界的位置:
```py
# 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')
```
- 现在,我们可以考虑一个测试数据点,看看分类器是否正确运行。让我们定义它并绘制出来:
```py
# 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')
```
- 我们需要使用以下模型提取 k 近邻:
```py
# Extract k nearest neighbors
dist, indices = classifier.kneighbors(test_datapoint)
```
- 让我们绘制 k 近邻并突出显示它们:
```py
# 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()
```
- 让我们在终端上打印分类器输出:
```py
print "Predicted output:", classifier.predict(test_datapoint)[0]
```
- If you run this code, the first output figure depicts the distribution of the input datapoints:

- The second output figure depicts the boundaries obtained using the k-nearest neighbors classifier:

- The third output figure depicts the location of the test datapoint:

- The fourth output figure depicts the location of the 10 nearest neighbors:

它是如何工作的…
k 近邻分类器存储所有可用的数据点,并基于相似性度量对新数据点进行分类。这种相似性度量通常以距离函数的形式出现。该算法是一种非参数技术,这意味着它在公式之前不需要找出任何底层参数。我们所需要做的就是选择一个对我们有用的k值。
一旦我们找到 k 最近的邻居,我们就获得多数票。一个新的数据点被 k 近邻的多数票分类。这个数据点被分配给 k 近邻中最常见的类。如果我们将k的值设置为1,那么这就变成了最近邻分类器的情况,我们只需将数据点分配给训练数据集中最近邻的类。你可以在上了解更多。html 。
构造 k 近邻回归器
我们学习了如何使用 k 近邻算法构建分类器。好的一点是,我们也可以用这个算法作为回归器。让我们看看如何用它作为回归器。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt from sklearn import neighbors -
让我们生成一些高斯分布的样本数据:
# Generate sample data amplitude = 10 num_points = 100 X = amplitude * np.random.rand(num_points, 1) - 0.5 * amplitude -
我们需要在数据中加入一些噪声,以引入一些随机性。添加噪声的目的是看我们的算法是否能够通过它,并且仍然以健壮的方式运行:
# Compute target and add noise y = np.sinc(X).ravel() y += 0.2 * (0.5 - np.random.rand(y.size)) -
让我们将其可视化如下:
# Plot input data plt.figure() plt.scatter(X, y, s=40, c='k', facecolors='none') plt.title('Input data') -
We just generated some data and evaluated a continuous-valued function on all these points. Let's define a denser grid of points:
# 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]我们定义了这个更密集的网格,因为我们想在所有这些点上评估我们的回归器,看看它与我们的函数有多接近。
-
让我们定义我们想要考虑的最近邻居的数量:
# Number of neighbors to consider n_neighbors = 8 -
让我们使用前面定义的参数初始化和训练 k 近邻回归器:
# Define and train the regressor knn_regressor = neighbors.KNeighborsRegressor(n_neighbors, weights='distance') y_values = knn_regressor.fit(X, y).predict(x_values) -
让我们看看回归器如何通过将输入和输出数据重叠在一起来执行:
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() -
If you run this code, the first figure depicts the input datapoints:
![How to do it…]()
-
The second figure depicts the predicted values by the regressor:

它是如何工作的…
回归器的目标是预测连续的有价值的产出。在这种情况下,我们没有固定数量的输出类别。我们只有一组实值输出值,我们希望我们的回归器预测未知数据点的输出值。在这种情况下,我们使用sinc函数来演示 k 近邻回归器。这也被称为基数正弦函数。一个sinc功能定义如下:
当 x 不为 0 时 sinc(x)= sin(x)/x
= x 为 0 时为 1
当x为0时, sin(x)/x 取 0/0 的不定形。因此,我们必须计算这个函数的极限,因为x趋向于0。我们使用一组值进行训练,并定义了一个更密集的网格进行测试。从上图中我们可以看到,输出曲线接近训练输出。
计算欧几里德距离得分
现在我们已经有了足够的机器学习管道和最近邻分类器的背景,让我们开始讨论推荐引擎。为了构建一个推荐引擎,我们需要定义一个相似性度量,这样我们就可以在数据库中找到与给定用户相似的用户。欧几里得距离分数就是这样一个度量,我们可以用它来计算数据点之间的距离。我们将集中讨论电影推荐引擎。让我们看看如何计算两个用户之间的欧几里得分数。
怎么做…
-
新建一个 Python 文件,导入以下包:
import json import numpy as np -
我们现在将定义一个函数来计算两个用户之间的欧几里得分数。第一步是检查数据库中是否有用户:
# 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') -
为了计算分数,我们需要提取两个用户都评分的电影:
# 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 -
如果没有共同的电影,那么用户之间就没有相似性(或者至少根据数据库中的评分我们无法计算出来):
# If there are no common movies, the score is 0 if len(rated_by_both) == 0: return 0 -
For each of the common ratings, we just compute the square root of the sum of squared differences and normalize it so that the score is between 0 and 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)))如果评级相似,那么平方差之和将非常低。因此,分数会变得很高,这也是我们希望从这个指标中得到的。
-
我们将使用
movie_ratings.json文件作为我们的数据文件。让我们加载它: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 "\nEuclidean score:" print euclidean_score(data, user1, user2) -
当您运行此代码时,您将看到终端上打印的欧几里德距离分数。
计算皮尔逊相关得分
欧几里德距离分数是一个很好的度量,但是它有一些缺点。因此,皮尔逊相关得分经常被用在推荐引擎中。让我们看看如何计算它。
怎么做…
-
新建一个 Python 文件,导入以下包:
import json import numpy as np -
我们将定义一个函数来计算数据库中两个用户之间的皮尔逊相关分数。我们的第一步是确认这些用户存在于数据库中:
# 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') -
下一步是获得这两个用户都评价的电影:
# 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) -
如果没有共同的电影,那么这些用户之间就没有可辨别的相似性;因此,我们返回
0:# If there are no common movies, the score is 0 if num_ratings == 0: return 0 -
我们需要计算常见电影评分的平方值之和:
# 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]) -
让我们计算所有常见电影评分的平方总和:
# 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]) -
让我们计算乘积的总和:
# 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]) -
我们现在准备计算皮尔逊相关分数所需的各种元素:
# 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 -
我们需要注意分母变成
0:if Sxx * Syy == 0: return 0的情况
-
如果一切正常,我们返回皮尔逊相关得分,如下所示:
```py
return Sxy / np.sqrt(Sxx * Syy)
```
- 让我们定义
main函数,计算两个用户之间的皮尔逊相关得分:
```py
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 "\nPearson score:"
print pearson_score(data, user1, user2)
```
- 如果您运行此代码,您将看到皮尔逊相关分数打印在终端上。
在数据集中寻找相似用户
构建推荐引擎最重要的任务之一是找到相似的用户。这将指导您创建将提供给这些用户的建议。让我们看看如何建立这个。
怎么做…
-
新建一个 Python 文件,导入以下包:
import json import numpy as np from pearson_score import pearson_score -
让我们定义一个函数来查找与输入用户相似的用户。它需要三个输入参数:数据库、输入用户和我们正在寻找的相似用户的数量。我们的第一步是检查用户是否在数据库中。如果该用户存在,我们需要计算该用户与数据库中所有其他用户之间的皮尔逊相关得分:
# 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]) -
下一步是按降序排列这些分数:
# 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] -
让我们提取 k 的最高分并返回:
# Extract top 'k' indices top_k = scored_sorted_dec[0:num_users] return scores[top_k] -
让我们定义
main函数并加载输入数据库:if __name__=='__main__': data_file = 'movie_ratings.json' with open(data_file, 'r') as f: data = json.loads(f.read()) -
我们想找三个类似的用户,比如
John Carson。我们使用以下步骤进行操作:user = 'John Carson' print "\nUsers 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) -
If you run this code, you will see the following printed on your Terminal:
![How to do it…]()
生成电影推荐
现在我们已经构建了推荐引擎的所有不同部分,我们已经准备好生成电影推荐了。我们将使用我们在前面的食谱中构建的所有功能来构建一个电影推荐引擎。让我们看看如何构建它。
怎么做…
-
新建一个 Python 文件,导入以下包:
import json import numpy as np from euclidean_score import euclidean_score from pearson_score import pearson_score from find_similar_users import find_similar_users -
我们将定义一个函数来为给定用户生成电影推荐。第一步是检查用户是否存在于数据集中:
# 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') -
让我们计算该用户与数据集中所有其他用户的皮尔逊得分:
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 -
我们需要找到这个用户没有评分的电影:
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}) -
如果用户已经观看了数据库中的每一部电影,那么我们不能向该用户推荐任何内容。我们来处理一下这个情况:
if len(total_scores) == 0: return ['No recommendations possible'] -
我们现在有这些分数的列表。让我们创建一个标准化的电影排名列表:
# Create the normalized list movie_ranks = np.array([[total/similarity_sums[item], item] for item, total in total_scores.items()]) -
我们需要根据分数降序排序:
# Sort in decreasing order based on the first column movie_ranks = movie_ranks[np.argsort(movie_ranks[:, 0])[::-1]] -
我们终于准备好提取电影推荐:
# Extract the recommended movies recommendations = [movie for _, movie in movie_ranks] return recommendations -
让我们定义
main函数并加载数据集:if __name__=='__main__': data_file = 'movie_ratings.json' with open(data_file, 'r') as f: data = json.loads(f.read()) -
让我们为
Michael Henry:
```py
user = 'Michael Henry'
print "\nRecommendations for " + user + ":"
movies = generate_recommendations(data, user)
for i, movie in enumerate(movies):
print str(i+1) + '. ' + movie
```
生成推荐
- 用户
John Carson已经看完了所有的电影。因此,如果我们试图为他生成推荐,它应该显示 0 个推荐。我们来看看是否会这样:
```py
user = 'John Carson'
print "\nRecommendations for " + user + ":"
movies = generate_recommendations(data, user)
for i, movie in enumerate(movies):
print str(i+1) + '. ' + movie
```
- If you run this code, you will see the following on your Terminal:

六、分析文本数据
在本章中,我们将介绍以下食谱:
- 使用标记化预处理数据
- 词干文本数据
- 使用引理化将文本转换为基本形式
- 使用分块分割文本
- 构建单词包模型
- 构建文本分类器
- 确定性别
- 分析句子的情感
- 使用主题建模识别文本中的模式
简介
文本分析和自然语言处理 ( NLP )是现代人工智能系统不可或缺的一部分。计算机擅长理解种类有限的严格结构化的数据。然而,当我们处理非结构化的自由格式文本时,事情开始变得困难。开发自然语言处理应用具有挑战性,因为计算机很难理解底层概念。我们交流事物的方式也有许多微妙的变化。这些可以是方言、上下文、俚语等形式。
为了解决这个问题,基于机器学习开发了自然语言处理应用。这些算法检测文本数据中的模式,以便我们可以从中提取见解。人工智能公司大量使用自然语言处理和文本分析来提供相关结果。自然语言处理最常见的应用包括搜索引擎、情感分析、主题建模、词性标注、实体识别等。自然语言处理的目标是开发一套算法,这样我们就可以用简单的英语与计算机交互。如果我们能做到这一点,那么我们就不需要编程语言来指导计算机应该做什么。在这一章中,我们将看看一些专注于文本分析的方法,以及我们如何从文本数据中提取有意义的信息。本章我们将大量使用名为自然语言工具包 ( NLTK )的 Python 包。确保安装后再继续。您可以在http://www.nltk.org/install.html找到安装步骤。您还需要安装 NLTK 数据,它包含许多语料库和训练好的模型。这是文本分析不可或缺的一部分!您可以在http://www.nltk.org/data.html找到安装步骤。
使用标记化预处理数据
标记化 是将文本分割成一组有意义的片段的过程。这些棋子被称为代币。举个的例子,我们可以把一大块文字分成单词,也可以把它分成句子。根据手头的任务,我们可以定义自己的条件,将输入文本分成有意义的标记。让我们看看如何做到这一点。
怎么做…
-
创建一个新的 Python 文件,并添加以下行。让我们定义一些示例文本进行分析:
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." -
让我们从句子标记化开始。NLTK 提供了一个句子标记器,所以让我们导入这个:
# Sentence tokenization from nltk.tokenize import sent_tokenize -
对输入文本运行句子标记器并提取标记:
sent_tokenize_list = sent_tokenize(text) -
打印句子列表,看看是否正确:
print "\nSentence tokenizer:" print sent_tokenize_list -
单词标记化在自然语言处理中非常常用。NLTK 附带了几个不同的单词标记器。让我们从基本单词标记器开始:
# Create a new word tokenizer from nltk.tokenize import word_tokenize print "\nWord tokenizer:" print word_tokenize(text) -
还有另一个单词标记器,叫做
PunktWord标记器。这会在标点符号上分割文本,但会将其保留在单词内:# Create a new punkt word tokenizer from nltk.tokenize import PunktWordTokenizer punkt_word_tokenizer = PunktWordTokenizer() print "\nPunkt word tokenizer:" print punkt_word_tokenizer.tokenize(text) -
如果你想把这些标点符号分成独立的符号,那么我们需要使用
WordPunct符号化器:# Create a new WordPunct tokenizer from nltk.tokenize import WordPunctTokenizer word_punct_tokenizer = WordPunctTokenizer() print "\nWord punct tokenizer:" print word_punct_tokenizer.tokenize(text) -
The full code is in the
tokenizer.pyfile. If you run this code, you will see the following output on your Terminal:![How to do it…]()
提取文本数据
当我们处理一个文本文档时,我们会遇到不同形式的单词。想想“玩”这个词。这个词可以以各种形式出现,比如“玩”“玩”“玩家”“玩”等等。这些基本上都是意义相似的词族。在文本分析过程中,提取这些单词的基本形式是很有用的。这将有助于我们提取一些统计数据来分析整个文本。词干的目标是将这些不同的形式简化为一个共同的基本形式。这使用启发式过程来切断单词的末端以提取基本形式。让我们看看如何在 Python 中做到这一点。
怎么做…
-
新建一个 Python 文件,导入以下包:
from nltk.stem.porter import PorterStemmer from nltk.stem.lancaster import LancasterStemmer from nltk.stem.snowball import SnowballStemmer -
我们来定义几个可以玩的词,如下:
words = ['table', 'probably', 'wolves', 'playing', 'is', 'dog', 'the', 'beaches', 'grounded', 'dreamt', 'envision'] -
我们将定义一个我们想要使用的词干分析器列表:
# Compare different stemmers stemmers = ['PORTER', 'LANCASTER', 'SNOWBALL'] -
初始化所有三个词干分析器所需的对象:
stemmer_porter = PorterStemmer() stemmer_lancaster = LancasterStemmer() stemmer_snowball = SnowballStemmer('english') -
为了以整洁的表格形式打印输出数据,我们需要以正确的方式格式化它:
formatted_row = '{:>16}' * (len(stemmers) + 1) print '\n', formatted_row.format('WORD', *stemmers), '\n' -
让我们遍历单词列表,并使用三个词干分析器对它们进行词干分析:
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) -
The full code is in the
stemmer.pyfile. If you run this code, you will see the following output on your Terminal. Observe how the Lancaster stemmer behaves differently for a couple of words:![How to do it…]()
它是如何工作的…
所有三种词干算法基本上都是为了达到同样的目的。三种词干算法之间的区别基本上是它们操作的严格程度。如果您观察输出,您会看到兰开斯特词干分析器比其他两个词干分析器更严格。就严格程度而言,搬运工是最少的,兰开斯特是最严格的。我们从兰开斯特词干师那里得到的词干往往会变得混乱和模糊。算法确实很快,但是会减少很多单词。因此,一个很好的经验法则是使用雪球茎杆。
使用引理化将文本转换为基本形式
引理化的目标也是将单词简化为它们的基本形式,但这是一种更结构化的方法。在前面的食谱中,我们看到我们使用词干分析器获得的基本单词并没有真正的意义。例如,“狼”这个词被简化为“wolv”,这不是一个真实的词。引理化通过使用词汇和单词的形态学分析来解决这个问题。它删除屈折词尾,如“ing”或“ed”,并返回单词的基本形式。这种基本形式被称为引理。如果你把“狼”这个词具体化,你会得到“狼”作为输出。输出取决于标记是动词还是名词。让我们看看如何在这个食谱中做到这一点。
怎么做…
-
创建一个新的 Python 文件,导入如下包:
from nltk.stem import WordNetLemmatizer -
让我们定义我们在词干制作过程中使用的同一组词:
words = ['table', 'probably', 'wolves', 'playing', 'is', 'dog', 'the', 'beaches', 'grounded', 'dreamt', 'envision'] -
我们将比较两个引理器,
NOUN和VERB引理器。让我们把它们列举如下:# Compare different lemmatizers lemmatizers = ['NOUN LEMMATIZER', 'VERB LEMMATIZER'] -
基于
WordNet引理创建对象:lemmatizer_wordnet = WordNetLemmatizer() -
为了以表格形式打印输出,我们需要以正确的方式格式化输出:
formatted_row = '{:>24}' * (len(lemmatizers) + 1) print '\n', formatted_row.format('WORD', *lemmatizers), '\n' -
遍历单词并将其引理:
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) -
The full code is in the
lemmatizer.pyfile. If you run this code, you will see the following output. Observe howNOUNandVERBlemmatizers differ when they lemmatize the word "is" in the following image:![How to do it…]()
使用分块分割文本
分块 是指将输入的文本分割成基于任意随机条件的片段。这不同于标记化,因为不存在任何约束,并且块根本不需要有意义。这在文本分析过程中经常使用。当您处理非常大的文本文档时,您需要将其分成几个块进行进一步分析。在这个食谱中,我们将输入的文本分成若干部分,其中每一部分都有固定数量的单词。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np from nltk.corpus import brown -
让我们定义一个函数来将文本分割成块。第一步是根据空格分割文本:
# Split a text into chunks def splitter(data, num_words): words = data.split(' ') output = [] -
初始化几个必需的变量:
cur_count = 0 cur_words = [] -
让我们遍历单词:
for word in words: cur_words.append(word) cur_count += 1 -
一旦你点击了所需的字数,重置变量:
if cur_count == num_words: output.append(' '.join(cur_words)) cur_words = [] cur_count = 0 -
将组块追加到输出变量,并返回:
output.append(' '.join(cur_words) ) return output -
我们现在可以定义
main函数。从布朗语料库中加载数据。我们将使用前一万个单词:if __name__=='__main__': # Read the data from the Brown corpus data = ' '.join(brown.words()[:10000]) -
定义每个组块的字数:
# Number of words in each chunk num_words = 1700 -
初始化几个相关变量:
chunks = [] counter = 0 -
对该文本数据调用
splitter功能,打印输出:
```py
text_chunks = splitter(data, num_words)
print "Number of text chunks =", len(text_chunks)
```
- 完整代码在
chunking.py文件中。如果您运行此代码,您将看到终端上打印出的块的数量。应该是 6!
构建单词包模型
当我们处理包含数百万个单词的文本文档时,我们需要将它们转换成某种数字表示。这样做的原因是为了使它们可用于机器学习算法。这些算法需要数字数据,以便分析它们并输出有意义的信息。这就是词汇袋的方法出现的地方。这基本上是一个模型从所有文档中的所有单词中学习一个词汇。之后,它通过构建文档中所有单词的直方图来为每个文档建模。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np from nltk.corpus import brown from chunking import splitter -
我们来定义
main函数。从布朗语料库中加载输入数据:if __name__=='__main__': # Read the data from the Brown corpus data = ' '.join(brown.words()[:10000]) -
将文本数据分成五大块:
# Number of words in each chunk num_words = 2000 chunks = [] counter = 0 text_chunks = splitter(data, num_words) -
创建一个基于这些文本块的字典:
for text in text_chunks: chunk = {'index': counter, 'text': text} chunks.append(chunk) counter += 1 -
下一步是提取文档术语矩阵。这基本上是一个计算文档中每个单词出现次数的矩阵。我们将使用 scikit-learn 来做到这一点,因为与 NLTK 相比,它在这个特定的任务中有更好的规定。导入以下包:
# Extract document term matrix from sklearn.feature_extraction.text import CountVectorizer -
定义对象,提取文档术语矩阵:
vectorizer = CountVectorizer(min_df=5, max_df=.95) doc_term_matrix = vectorizer.fit_transform([chunk['text'] for chunk in chunks]) -
从
vectorizer对象中提取词汇并打印出来:vocab = np.array(vectorizer.get_feature_names()) print "\nVocabulary:" print vocab -
打印文档术语矩阵:
print "\nDocument term matrix:" chunk_names = ['Chunk-0', 'Chunk-1', 'Chunk-2', 'Chunk-3', 'Chunk-4'] -
要以表格形式打印,我们需要将其格式化,如下所示:
formatted_row = '{:>12}' * (len(chunk_names) + 1) print '\n', formatted_row.format('Word', *chunk_names), '\n' -
遍历单词,打印每个单词在不同组块中出现的次数:
```py
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)
```
- The full code is in the
bag_of_words.pyfile. If you run this code, you will see two main things printed on the Terminal. The first output is the vocabulary as shown in the following image:

- The second thing is the document term matrix, which is a pretty long. The first few lines will look like the following:

它是如何工作的…
考虑以下句子:
- 第一句:棕色的狗在跑。
- 第二句:黑狗在黑屋子里。
- 第三句:禁止在室内跑步。
如果你考虑这三句话,我们有以下九个独特的词:
- 这
- 棕色
- 狗
- 是
- 运转
- 黑色
- 在
- 房间
- 被禁止的
现在,让我们使用每个句子中的字数将每个句子转换成直方图。每个特征向量都是 9 维的,因为我们有 9 个唯一的词:
- 第一句:【1,1,1,1,0,0,0,0】
- 第二句:【2,0,1,1,0,2,1,1,0】
- 第三句:【0,0,0,1,1,0,1,1,1】
一旦我们提取出这些特征向量,就可以使用机器学习算法进行分析。
构建文本分类器
文本分类的目标是将文本文档分类为不同的类别。这是自然语言处理中极其重要的分析技术。我们将使用一种技术,该技术基于一个名为 tf-idf 的统计数据,该统计数据代表 术语频率—反向文档频率。这是一个分析工具,帮助我们理解一个单词对于一组文档中的一个文档有多重要。这是用来对文档进行分类的特征向量。你可以在http://www.tfidf.com了解更多。
怎么做…
-
新建一个 Python 文件,导入如下包:
from sklearn.datasets import fetch_20newsgroups -
让我们选择一个类别列表,并使用字典映射来命名它们。这些类别是我们刚刚导入的新闻组数据集的一部分:
category_map = {'misc.forsale': 'Sales', 'rec.motorcycles': 'Motorcycles', 'rec.sport.baseball': 'Baseball', 'sci.crypt': 'Cryptography', 'sci.space': 'Space'} -
根据我们刚刚定义的类别加载训练数据:
training_data = fetch_20newsgroups(subset='train', categories=category_map.keys(), shuffle=True, random_state=7) -
导入特征提取器:
# Feature extraction from sklearn.feature_extraction.text import CountVectorizer -
使用训练数据提取特征:
vectorizer = CountVectorizer() X_train_termcounts = vectorizer.fit_transform(training_data.data) print "\nDimensions of training data:", X_train_termcounts.shape -
我们现在已经准备好训练分类器了。我们将使用多项式朴素贝叶斯分类器:
# Training a classifier from sklearn.naive_bayes import MultinomialNB from sklearn.feature_extraction.text import TfidfTransformer -
定义几个随机输入的句子:
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" ] -
定义 tf-idf 转换器对象并对其进行训练:
# tf-idf transformer tfidf_transformer = TfidfTransformer() X_train_tfidf = tfidf_transformer.fit_transform(X_train_termcounts) -
一旦我们有了特征向量,使用这个数据训练多项式朴素贝叶斯分类器:
# Multinomial Naive Bayes classifier classifier = MultinomialNB().fit(X_train_tfidf, training_data.target) -
使用字数转换输入数据:
```py
X_input_termcounts = vectorizer.transform(input_data)
```
- 使用 tf-idf 转换器转换输入数据:
```py
X_input_tfidf = tfidf_transformer.transform(X_input_termcounts)
```
- 使用训练好的分类器预测这些输入句子的输出类别:
```py
# Predict the output categories
predicted_categories = classifier.predict(X_input_tfidf)
```
- 打印输出,如下所示:
```py
# Print the outputs
for sentence, category in zip(input_data, predicted_categories):
print '\nInput:', sentence, '\nPredicted category:', \
category_map[training_data.target_names[category]]
```
- The full code is in the
tfidf.pyfile. If you run this code, you will see the following output printed on your Terminal:

它是如何工作的…
tf-idf 技术在信息检索中经常使用。目标是理解文档中每个单词的重要性。我们希望识别在文档中出现多次的单词。同时,像“是”和“是”这样的常用词并不能真正反映内容的本质。所以我们需要提取出真正的指标。每个单词的重要性随着字数的增加而增加。同时,随着它的大量出现,这个词的出现频率也增加了。这两件事往往会相互抵消。我们从每个句子中提取术语计数。一旦我们将其转换为特征向量,我们就训练分类器来对这些句子进行分类。
词频 ( TF ) 衡量一个单词在给定文档中出现的频率。由于多个文档的长度不同,直方图中的数字往往变化很大。因此,我们需要使之正常化,使之成为一个公平的竞争环境。为了实现规范化,我们将词频除以给定文档中的总字数。逆文档频率 ( IDF ) 衡量给定单词的重要性。当我们计算 TF 时,所有单词都被认为是同等重要的。为了平衡常用词的出现频率,我们需要把它们的权重降低,把稀有词的权重提高。我们需要计算带有给定单词的文档数量的比率,并将其除以文档总数。IDF 的计算方法是取这个比值的负算法。
例如,简单的单词,如“is”或“the”,往往会在各种文档中出现很多。然而,这并不意味着我们可以根据这些词来描述文档的特征。同时,如果一个单词出现一次,这也没有用。所以,我们寻找出现多次的单词,但不要太多,以免它们变得嘈杂。这是在 tf-idf 技术中制定的,用于对文档进行分类。搜索引擎经常使用这个工具来按相关性对搜索结果进行排序。
识别性别
在自然语言处理中,识别名字的性别是一项有趣的任务。我们将使用名称中最后几个字符是其定义特征的启发式方法。例如,如果名字以“la”结尾,则很可能是女性的名字,如“Angela”或“蕾拉”。另一方面,如果名字以“im”结尾,很可能是男性的名字,比如“Tim”或“Jim”。由于我们确定要使用的字符的确切数量,我们将对此进行实验。让我们看看怎么做。
怎么做…
-
新建一个 Python 文件,导入以下包:
import random from nltk.corpus import names from nltk import NaiveBayesClassifier from nltk.classify import accuracy as nltk_accuracy -
我们需要定义一个从输入单词中提取特征的函数:
# Extract features from the input word def gender_features(word, num_letters=2): return {'feature': word[-num_letters:].lower()} -
我们来定义
main函数。我们需要一些标注的训练数据: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')]) -
播种随机数发生器,洗牌训练数据:
random.seed(7) random.shuffle(labeled_names) -
定义一些可以玩的输入名称:
input_names = ['Leonardo', 'Amy', 'Sam'] -
由于不知道需要考虑多少结尾字符,我们将参数空间从
1扫至5。每次,我们都会提取特征,如下所示:# 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] -
将其分为训练和测试数据集:
train_set, test_set = featuresets[500:], featuresets[:500] -
我们将使用朴素贝叶斯分类器来做到这一点:
classifier = NaiveBayesClassifier.train(train_set) -
评估参数空间中每个值的分类器:
# 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)) -
The full code is in the
gender_identification.pyfile. If you run this code, you will see the following output printed on your Terminal:

分析句子的情绪
情感分析 是 NLP 最热门的应用之一。情感分析是指确定给定文本是积极的还是消极的过程。在一些变体中,我们认为“中性”是第三种选择。这种技术通常用于发现人们对某个特定话题的感受。这个用来分析用户各种形式的情绪,比如营销活动、社交媒体、电商客户等等。
怎么做…
-
新建一个 Python 文件,导入以下包:
import nltk.classify.util from nltk.classify import NaiveBayesClassifier from nltk.corpus import movie_reviews -
定义提取特征的函数:
def extract_features(word_list): return dict([(word, True) for word in word_list]) -
为此我们需要训练数据,所以我们将使用 NLTK 中的电影评论:
if __name__=='__main__': # Load positive and negative reviews positive_fileids = movie_reviews.fileids('pos') negative_fileids = movie_reviews.fileids('neg') -
让我们把这些分为正面和负面评论:
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] -
将数据分为训练和测试数据集:
# 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)) -
提取特征:
features_train = features_positive[:threshold_positive] + features_negative[:threshold_negative] features_test = features_positive[threshold_positive:] + features_negative[threshold_negative:] print "\nNumber of training datapoints:", len(features_train) print "Number of test datapoints:", len(features_test) -
我们将使用朴素贝叶斯分类器。定义对象并训练:
# Train a Naive Bayes classifier classifier = NaiveBayesClassifier.train(features_train) print "\nAccuracy of the classifier:", nltk.classify.util.accuracy(classifier, features_test) -
分类器对象包含在分析过程中获得的信息量最大的单词。这些词基本上在被归类为积极或消极的评论中有很强的发言权。让我们打印出来:
print "\nTop 10 most informative words:" for item in classifier.most_informative_features()[:10]: print item[0] -
创建几个随机输入的句子:
# 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" ] -
对这些输入句子运行分类器,获得预测:
```py
print "\nPredictions:"
for review in input_reviews:
print "\nReview:", review
probdist = classifier.prob_classify(extract_features(review.split()))
pred_sentiment = probdist.max()
```
- 打印输出:
```py
print "Predicted sentiment:", pred_sentiment
print "Probability:", round(probdist.prob(pred_sentiment), 2)
```
- The full code is in the
sentiment_analysis.pyfile. If you run this code, you will see three main things printed on the Terminal. The first is the accuracy, as shown in the following image:

- The next is a list of most informative words:

- The last is the list of predictions, which are based on the input sentences:

它是如何工作的…
我们在这里使用 NLTK 的朴素贝叶斯分类器来完成我们的任务。在特征提取器功能中,我们基本上提取所有唯一的单词。然而,NLTK 分类器需要数据以字典的形式排列。因此,我们以这样一种方式安排它,使得 NLTK 分类器对象可以摄取它。
一旦我们将数据划分为训练和测试数据集,我们就训练分类器将句子分为肯定和否定。如果你看一下顶部的信息词,你可以看到我们有“优秀”这样的词来表示正面评价,有“侮辱”这样的词来表示负面评价。这是一个有趣的信息,因为它告诉我们什么词被用来表示强烈的反应。
使用主题建模识别文本中的模式
主题建模 是指识别文本数据中隐藏的模式的过程。目标是在一组文档中发现一些隐藏的主题结构。这将帮助我们以更好的方式组织我们的文档,以便我们可以使用它们进行分析。这是自然语言处理中一个活跃的研究领域。你可以在http://www.cs.columbia.edu/~blei/topicmodeling.html了解更多。在这个食谱中,我们将使用一个名为gensim的库。在继续之前,请确保安装了此软件。安装步骤见https://radimrehurek.com/gensim/install.html。
怎么做…
-
创建一个新的 Python 文件并导入以下包:
from nltk.tokenize import RegexpTokenizer from nltk.stem.snowball import SnowballStemmer from gensim import models, corpora from nltk.corpus import stopwords -
定义一个函数来加载输入数据。我们将使用已经提供给您的
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 -
让我们定义一个类来预处理文本。这个预处理器负责创建所需的对象,并从输入文本中提取相关特征:
# Class to preprocess text class Preprocessor(object): # Initialize various operators def __init__(self): # Create a regular expression tokenizer self.tokenizer = RegexpTokenizer(r'\w+') -
我们需要一个停止单词的列表,这样我们就可以将它们排除在分析之外。这些都是常用词,如“在”、“该”、“是”等:
# get the list of stop words self.stop_words_english = stopwords.words('english') -
定义雪球茎干:
# Create a Snowball stemmer self.stemmer = SnowballStemmer('english') -
定义一个处理标记化、停止词移除和词干的处理器函数:
# Tokenizing, stop word removal, and stemming def process(self, input_text): # Tokenize the string tokens = self.tokenizer.tokenize(input_text.lower()) -
从文本中删除停止词:
# Remove the stop words tokens_stopwords = [x for x in tokens if not x in self.stop_words_english] -
对标记执行词干:
# Perform stemming on the tokens tokens_stemmed = [self.stemmer.stem(x) for x in tokens_stopwords] -
返回已处理的代币:
return tokens_stemmed -
我们现在准备定义
main函数。从文本文件加载输入数据:
```py
if __name__=='__main__':
# File containing linewise input data
input_file = 'data_topic_modeling.txt'
# Load data
data = load_data(input_file)
```
- 定义一个基于我们定义的类的对象:
```py
# Create a preprocessor object
preprocessor = Preprocessor()
```
- 我们需要处理文件中的文本,提取处理后的令牌:
```py
# Create a list for processed documents
processed_tokens = [preprocessor.process(x) for x in data]
```
- 创建一个基于标记化文档的字典,以便可以用于主题建模:
```py
# Create a dictionary based on the tokenized documents
dict_tokens = corpora.Dictionary(processed_tokens)
```
- 我们需要使用处理过的标记创建一个文档术语矩阵,如下所示:
```py
# Create a document-term matrix
corpus = [dict_tokens.doc2bow(text) for text in processed_tokens]
```
- 假设我们知道文本可以分为两个主题。我们将使用一种称为 潜在狄利克雷分配 ( LDA )的技术进行主题建模。定义所需参数,初始化 LDA 模型对象:
```py
# 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)
```
- 一旦确定了这两个主题,我们就可以通过查看贡献最大的单词来了解它是如何将这两个主题分开的:
```py
print "Most contributing words to the topics:"
for item in ldamodel.print_topics(num_topics=num_topics, num_words=num_words):
print "\nTopic", item[0], "==>", item[1]
```
- The full code is in the
topic_modeling.pyfile. If you run this code, you will see the following printed on your Terminal:

它是如何工作的…
主题建模通过识别文档中主题的重要单词来工作。这些词往往决定了的话题是什么。我们使用正则表达式标记器,因为我们只需要没有任何标点符号或其他类型标记的单词。因此,我们使用它来提取标记。停止单词删除是另一个重要的步骤,因为这有助于我们消除由于单词引起的噪音,例如“is”或“the”。在这之后,我们需要把单词词干,以得到它们的基本形式。这整个东西被打包成文本分析工具中的预处理块。这也是我们在这里做的!
我们使用一种称为潜在狄利克雷分配(LDA)的技术来建模主题。LDA 基本上将文档表示为倾向于吐出单词的不同主题的混合。这些话是以一定的概率说出来的。目标是找到这些话题!这是一个生成模型,试图找到负责生成给定文档集的主题集。你可以在http://blog . echen . me/2011/08/22/潜在狄利克雷分配介绍了解更多。
从输出中可以看到,我们有“天赋”和“训练”这样的词来表征体育话题,而我们有“加密”来表征密码学话题。我们正在处理一个非常小的文本文件,这就是为什么有些单词看起来不太相关的原因。显然,如果使用更大的数据集,精度将会提高。
七、语音识别
在本章中,我们将介绍以下食谱:
- 读取和绘制音频数据
- 将音频信号转换到频域
- 生成带有自定义参数的音频信号
- 合成音乐
- 提取频域特征
- 建立隐马尔可夫模型
- 构建语音识别器
简介
语音识别是指识别和理解口语的过程。输入以音频数据的形式出现,语音识别器将处理这些数据,从中提取有意义的信息。这有很多实际用途,例如语音控制设备、将口语转录成单词、安全系统等等。
语音信号在本质上是多用途的。同一种语言有许多不同的说法。言语有不同的要素,如语言、情感、语气、噪音、口音等等。很难严格定义一套可以构成言语的规则。即使有所有这些变化,人类也非常善于相对容易地理解所有这些。因此,我们需要机器以同样的方式理解语音。
在过去的几十年里,研究人员致力于语音的各个方面,如识别说话者、理解单词、识别口音、翻译语音等。在所有这些任务中,自动语音识别一直是许多研究者关注的焦点。在本章中,我们将学习如何构建语音识别器。
读取和绘制音频数据
让我们来看看如何读取音频文件并可视化信号。这将是一个很好的起点,它将让我们对音频信号的基本结构有一个很好的了解。在开始之前,我们需要了解音频文件是实际音频信号的数字化版本。实际的音频信号是复杂的连续值波。为了保存数字版本,我们对信号进行采样并将其转换为数字。例如,语音通常以 44100 赫兹采样。这意味着信号的每一秒都被分解成 44100 个部分,并且存储这些时间戳的值。换句话说,每 1/44100 秒存储一个值。由于采样率高,当我们在媒体播放器上收听时,我们会感觉到信号是连续的。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt from scipy.io import wavfile -
我们将使用
wavfile包从已经提供给您的input_read.wav输入文件中读取音频文件:# Read the input file sampling_freq, audio = wavfile.read('input_read.wav') -
让我们打印出这个信号的参数:
# Print the params print '\nShape:', audio.shape print 'Datatype:', audio.dtype print 'Duration:', round(audio.shape[0] / float(sampling_freq), 3), 'seconds' -
音频信号存储为 16 位有符号整数数据。我们需要标准化这些值:
# Normalize the values audio = audio / (2.**15) -
让我们提取前 30 个值进行绘图,如下所示:
# Extract first 30 values for plotting audio = audio[:30] -
X 轴是时间轴。让我们构建这个轴,考虑到它应该使用采样频率因子进行缩放的事实:
# Build the time axis x_values = np.arange(0, len(audio), 1) / float(sampling_freq) -
将单位转换为秒:
# Convert to seconds x_values *= 1000 -
让我们将绘制如下:
# 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() -
The full code is in the
read_plot.pyfile. If you run this code, you will see the following signal:![How to do it…]()
-
You will also see the following printed on your Terminal:

将音频信号转换到频域
音频信号由不同频率、振幅和相位的正弦波的复杂混合物组成。正弦波也被称为正弦波。音频信号的频率内容中隐藏着大量信息。事实上,音频信号的主要特征是其频率内容。整个的语言和音乐世界就是基于这个事实。在你继续之前,你需要一些关于傅立叶变换的知识。快速复习可以在http://www.thefouriertransform.com找到。现在,让我们看看如何将音频信号转换到频域。
怎么做…
-
新建一个 Python 文件,导入如下包:
import numpy as np from scipy.io import wavfile import matplotlib.pyplot as plt -
阅读已经提供给你的
input_freq.wav文件:# Read the input file sampling_freq, audio = wavfile.read('input_freq.wav') -
将信号标准化,如下所示:
# Normalize the values audio = audio / (2.**15) -
音频信号只是一个 NumPy 数组。因此,您可以使用以下代码提取长度:
# Extract length len_audio = len(audio) -
让我们应用傅立叶变换。傅立叶变换信号沿中心镜像,所以我们只需要取变换信号的前半部分。我们的最终目标是提取电力信号。因此,我们对信号中的值进行平方,为这个做准备:
# Apply Fourier transform transformed_signal = np.fft.fft(audio) half_length = np.ceil((len_audio + 1) / 2.0) transformed_signal = abs(transformed_signal[0:half_length]) transformed_signal /= float(len_audio) transformed_signal **= 2 -
提取信号的长度:
# Extract length of transformed signal len_ts = len(transformed_signal) -
我们需要根据信号的长度加倍信号:
# Take care of even/odd cases if len_audio % 2: transformed_signal[1:len_ts] *= 2 else: transformed_signal[1:len_ts-1] *= 2 -
使用以下公式提取功率信号:
# Extract power in dB power = 10 * np.log10(transformed_signal) -
X 轴是时间轴。我们需要根据采样频率进行缩放,然后将其转换为秒:
# Build the time axis x_values = np.arange(0, half_length, 1) * (sampling_freq / len_audio) / 1000.0 -
绘制信号,如下所示:
```py
# Plot the figure
plt.figure()
plt.plot(x_values, power, color='black')
plt.xlabel('Freq (in kHz)')
plt.ylabel('Power (in dB)')
plt.show()
```
- The full code is in the
freq_transform.pyfile. If you run this code, you will see the following figure:

生成带有自定义参数的音频信号
我们可以使用 NumPy 生成音频信号。如前所述,音频信号是正弦曲线的复杂混合物。因此,当我们生成自己的音频信号时,我们会记住这一点。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt from scipy.io.wavfile import write -
我们需要定义生成的音频将被存储的输出文件:
# File where the output will be saved output_file = 'output_generated.wav' -
让我们指定音频生成参数。我们想产生一个采样频率为
44100且音调频率为587Hz 的三秒长信号。时间轴上的值将从 -2pi* 到 2pi* :# Specify audio parameters duration = 3 # seconds sampling_freq = 44100 # Hz tone_freq = 587 min_val = -2 * np.pi max_val = 2 * np.pi -
让我们生成时间轴和音频信号。音频信号是一个简单的正弦曲线,具有前面提到的参数:
# Generate audio t = np.linspace(min_val, max_val, duration * sampling_freq) audio = np.sin(2 * np.pi * tone_freq * t) -
让我们给信号添加一些噪声:
# Add some noise noise = 0.4 * np.random.rand(duration * sampling_freq) audio += noise -
我们需要将这些值缩放到 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) -
将此信号写入输出文件:
# Write to output file write(output_file, sampling_freq, audio_scaled) -
使用前 100 个值绘制信号:
# Extract first 100 values for plotting audio = audio[:100] -
生成时间轴:
# Build the time axis x_values = np.arange(0, len(audio), 1) / float(sampling_freq) -
将时间轴转换为秒:
```py
# Convert to seconds
x_values *= 1000
```
- 绘制信号,如下所示:
```py
# 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()
```
- The full code is in the
generate.pyfile. If you run this code, you will get the following figure:

合成音乐
既然我们知道了如何生成音频,那就用这个原理来合成一些音乐吧。你可以查看这个链接,http://www.phy.mtu.edu/~suits/notefreqs.html。该链接列出了各种音符,如 A 、 G 、 D 等等以及它们对应的频率。我们将使用它来生成一些简单的音乐。
怎么做…
-
新建一个 Python 文件,导入以下包:
import json import numpy as np from scipy.io.wavfile import write import matplotlib.pyplot as plt -
根据输入参数
# Synthesize tone def synthesizer(freq, duration, amp=1.0, sampling_freq=44100):,定义合成音调的功能
-
构建时间轴值:
# Build the time axis t = np.linspace(0, duration, duration * sampling_freq) -
使用输入参数构建音频样本,例如振幅和频率:
# Construct the audio signal audio = amp * np.sin(2 * np.pi * freq * t) return audio.astype(np.int16) -
我们来定义
main函数。已经向您提供了一个名为tone_freq_map.json的 JSON 文件,其中包含一些音符及其频率:if __name__=='__main__': tone_map_file = 'tone_freq_map.json' -
加载文件:
# Read the frequency map with open(tone_map_file, 'r') as f: tone_freq_map = json.loads(f.read()) -
假设我们想要生成一个持续时间为
2秒的 G 音符:# Set input parameters to generate 'G' tone input_tone = 'G' duration = 2 # seconds amplitude = 10000 sampling_freq = 44100 # Hz -
用以下参数调用函数:
# Generate the tone synthesized_tone = synthesizer(tone_freq_map[input_tone], duration, amplitude, sampling_freq) -
将生成的信号写入输出文件:
# Write to the output file write('output_tone.wav', sampling_freq, synthesized_tone) -
在媒体播放器中打开此文件并收听。那就是 G 注!让我们做一些更有趣的事情。让我们按顺序生成一些音符,给它一种音乐的感觉。定义音符序列及其持续时间(以秒为单位):
```py
# Tone-duration sequence
tone_seq = [('D', 0.3), ('G', 0.6), ('C', 0.5), ('A', 0.3), ('Asharp', 0.7)]
```
- 遍历这个列表,为每个列表调用合成器函数:
```py
# 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)
```
- 将信号写入输出文件:
```py
# Write to the output file
write('output_tone_seq.wav', sampling_freq, output)
```
- 完整的代码在
synthesize_music.py文件中。您可以在媒体播放器中打开output_tone_seq.wav文件并收听。你能感受到音乐!
提取频域特征
我们前面讨论了如何将信号转换到频域。在大多数现代语音识别系统中,人们使用频域特征。将信号转换到频域后,需要将其转换成可用的形式。梅尔频率倒频谱系数 ( MFCC )是一个很好的方法。MFCC 提取信号的功率谱,然后使用滤波器组和离散余弦变换的组合来提取特征。如果您需要快速复习,可以查看http://practical ryphysicy . com/杂项/机器学习/指南-Mel-frequency-cepstral-coefficients-mfccs。开始前,确保安装了python_speech_features包。您可以在http://python-speech-features.readthedocs.org/en/latest找到安装说明。让我们来看看如何提取 MFCC 特征。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt from scipy.io import wavfile from features import mfcc, logfbank -
阅读已经提供给你的
input_freq.wav输入文件:# Read input sound file sampling_freq, audio = wavfile.read("input_freq.wav") -
提取 MFCC 和滤波器组特征:
# Extract MFCC and Filter bank features mfcc_features = mfcc(audio, sampling_freq) filterbank_features = logfbank(audio, sampling_freq) -
打印参数,查看生成了多少个窗口:
# Print parameters print '\nMFCC:\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] -
让我们想象一下 MFCC 的特色。我们需要变换矩阵,使时域水平:
# Plot the features mfcc_features = mfcc_features.T plt.matshow(mfcc_features) plt.title('MFCC') -
让我们想象一下滤波器组的特性。同样,我们需要变换矩阵,使时域水平:
filterbank_features = filterbank_features.T plt.matshow(filterbank_features) plt.title('Filter bank') plt.show() -
The full code is in the
extract_freq_features.pyfile. If you run this code, you will get the following figure for MFCC features:![How to do it…]()
-
The filter bank features will look like the following:
![How to do it…]()
-
You will get the following output on your Terminal:
![How to do it…]()
建立隐马尔可夫模型
我们现在准备讨论语音识别。我们将使用隐马尔可夫模型 ( HMMs )来执行语音识别。hmm 非常擅长建模时间序列数据。由于音频信号是时间序列信号,HMMs 非常适合我们的需求。隐马尔可夫模型是一种表示观测序列上概率分布的模型。我们假设输出由隐藏状态产生。所以,我们的目标是找到这些隐藏的状态,这样我们就可以对信号进行建模。你可以在https://www.robots.ox.ac.uk/~vgg/rg/slides/hmm.pdf了解更多。在继续之前,您需要安装hmmlearn包。您可以在http://hmmlearn.readthedocs.org/en/latest找到安装说明。让我们来看看如何构建 hmm。
怎么做…
-
创建一个新的 Python 文件。让我们定义一个类来建模 HMMs:
# Class to handle all HMM related processing class HMMTrainer(object): -
Let's initialize the class. We will use Gaussian HMMs to model our data. The
n_componentsparameter defines the number of hidden states. Thecov_typedefines the type of covariance in our transition matrix, andn_iterindicates the number of iterations it will go through before it stops training: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') -
输入的数据是一个 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))组成
-
根据模型定义一种提取分数的方法:
# Run the model on input data def get_score(self, input_data): return self.model.score(input_data) -
我们建立了一个类来处理隐马尔可夫模型的训练和预测,但是我们需要一些数据来看到它的实际应用。我们将在下一个食谱中使用它来构建一个语音识别器。完整代码在
speech_recognizer.py文件中。
构建语音识别器
我们需要一个语音文件的数据库来构建我们的语音识别器。我们将使用上的数据库。它包含七个不同的单词,每个单词有 15 个与之相关的音频文件。这是一个小数据集,但这足以理解如何构建一个可以识别七个不同单词的语音识别器。我们需要为每个类建立一个 HMM 模型。当我们想在一个新的输入文件中识别这个单词时,我们需要运行这个文件中的所有模型,并选择得分最高的那个。我们将使用我们在前面的食谱中构建的 HMM 类。
怎么做…
-
新建一个 Python 文件,导入以下包:
import os import argparse import numpy as np from scipy.io import wavfile from hmmlearn import hmm from features import mfcc -
定义一个函数来解析命令行中的输入参数:
# 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 -
定义
main函数,解析输入参数:if __name__=='__main__': args = build_arg_parser().parse_args() input_folder = args.input_folder -
启动保存所有隐马尔可夫模型的变量:
hmm_models = [] -
解析包含所有数据库音频文件的输入目录:
# Parse the input directory for dirname in os.listdir(input_folder): -
提取子文件夹的名称:
# Get the name of the subfolder subfolder = os.path.join(input_folder, dirname) if not os.path.isdir(subfolder): continue -
子文件夹的名称就是这个类的标签。使用以下方式提取:
# Extract the label label = subfolder[subfolder.rfind('/') + 1:] -
初始化变量进行训练:
# Initialize variables X = np.array([]) y_words = [] -
遍历每个子文件夹中的音频文件列表:
# 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]: -
读取每个音频文件,如下所示:
```py
# Read the input file
filepath = os.path.join(subfolder, filename)
sampling_freq, audio = wavfile.read(filepath)
```
- 提取 MFCC 特征:
```py
# Extract MFCC features
mfcc_features = mfcc(audio, sampling_freq)
```
- 继续将其附加到
X变量:
```py
# Append to the variable X
if len(X) == 0:
X = mfcc_features
else:
X = np.append(X, mfcc_features, axis=0)
```
- 也附上相应的标签:
```py
# Append the label
y_words.append(label)
```
- 一旦从当前类的所有文件中提取出特征,训练并保存隐马尔可夫模型。由于隐马尔可夫模型是无监督学习的生成模型,我们不需要标签来为每个类建立隐马尔可夫模型。我们明确假设将为每个类构建单独的 HMM 模型:
```py
# Train and save HMM model
hmm_trainer = HMMTrainer()
hmm_trainer.train(X)
hmm_models.append((hmm_trainer, label))
hmm_trainer = None
```
- 获取未用于培训的测试文件列表:
```py
# Test files
input_files = [
'data/pineapple/pineapple15.wav',
'data/orange/orange15.wav',
'data/apple/apple15.wav',
'data/kiwi/kiwi15.wav'
]
```
- 解析输入文件,如下所示:
```py
# Classify input data
for input_file in input_files:
```
- 读入每个音频文件:
```py
# Read input file
sampling_freq, audio = wavfile.read(input_file)
```
- 提取 MFCC 特征:
```py
# Extract MFCC features
mfcc_features = mfcc(audio, sampling_freq)
```
- 定义存储最高分的变量和输出标签:
```py
# Define variables
max_score = None
output_label = None
```
- 遍历所有的模型,并通过每个模型运行输入文件:
```py
# Iterate through all HMM models and pick
# the one with the highest score
for item in hmm_models:
hmm_model, label = item
```
- 提取分数并存储最高分:
```py
score = hmm_model.get_score(mfcc_features)
if score > max_score:
max_score = score
output_label = label
```
- 打印真实和预测标签:
```py
# Print the output
print "\nTrue:", input_file[input_file.find('/')+1:input_file.rfind('/')]
print "Predicted:", output_label
```
- The full code is in the
speech_recognizer.pyfile. If you run this code, you will see the following on your Terminal:

八、剖析时间序列和序列数据
在本章中,我们将介绍以下食谱:
- 将数据转换为时间序列格式
- 切片时间序列数据
- 对时间序列数据进行操作
- 从时间序列数据中提取统计数据
- 为序列数据建立隐马尔可夫模型
- 为连续文本数据构建条件随机字段
- 使用隐马尔可夫模型分析股票市场数据
简介
时间序列数据基本上是随时间收集的一系列测量值。这些测量是相对于预定变量并以规则的时间间隔进行的。时间序列数据的一个主要特征就是排序很重要!
我们收集的观察列表是按时间顺序排列的,它们出现的顺序说明了很多潜在的模式。如果你改变顺序,这将完全改变数据的意义。顺序数据是一个广义的概念,它包含任何以顺序形式出现的数据,包括时间序列数据。
我们在这里的目标是建立一个模型,描述时间序列或任何一般序列的模式。这种模型用于描述时间序列模式的重要特征。我们可以用这些模型来解释过去如何影响未来。我们还可以使用它们来查看两个数据集如何关联,预测未来值,或者控制基于某个指标的给定变量。
为了可视化时间序列数据,我们倾向于使用折线图或条形图来绘制它。时间序列数据分析经常用于金融、信号处理、天气预测、轨迹预测、地震预测或我们必须处理时间数据的任何领域。我们在时间序列和顺序数据分析中构建的模型应该考虑数据的顺序,并提取相邻数据之间的关系。让我们继续查看一些用 Python 分析时间序列和顺序数据的方法。
将数据转换为时间序列格式
我们将从了解如何将观测序列转换为时间序列数据并可视化开始。我们将使用名为 熊猫的库来分析时间序列数据。确保你安装熊猫,然后再继续。您可以在http://pandas.pydata.org/pandas-docs/stable/install.html找到安装说明。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import pandas as pd import matplotlib.pyplot as plt -
让我们定义一个函数来读取一个输入文件,该文件将顺序观察转换为时间索引数据:
def convert_data_to_timeseries(input_file, column, verbose=False): -
我们将使用由四列组成的文本文件。第一列表示年份,第二列表示月份,第三和第四列表示数据。让我们把它加载到一个 NumPy 数组中:
# Load the input file data = np.loadtxt(input_file, delimiter=',') -
按照时间顺序排列,第一行包含开始日期,最后一行包含结束日期。让我们提取这个数据集的开始和结束日期:
# 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)) -
这个函数还有一个详细模式。所以如果设置为 true,它会打印一些东西。让我们打印出开始和结束日期:
if verbose: print "\nStart date =", start_date print "End date =", end_date -
让我们创建一个熊猫变量,它包含每月间隔的日期序列:
# Create a date sequence with monthly intervals dates = pd.date_range(start_date, end_date, freq='M') -
我们的下一步是把给定的列转换成时间序列数据。您可以使用月份和年份(与索引相反)来访问这些数据:
# Convert the data into time series data data_timeseries = pd.Series(data[:,column], index=dates) -
使用详细模式打印出前十个元素:
if verbose: print "\nTime series data:\n", data_timeseries[:10] -
返回时间索引变量,如下所示:
return data_timeseries -
定义
main功能,如下所示:
```py
if __name__=='__main__':
```
- 我们将使用已经提供给您的
data_timeseries.txt文件:
```py
# Input file containing data
input_file = 'data_timeseries.txt'
```
- 从该文本文件加载第三列,并将其转换为时间序列数据:
```py
# Load input data
column_num = 2
data_timeseries = convert_data_to_timeseries(input_file, column_num)
```
- 熊猫库提供了一个很好的绘图功能,你可以直接在变量上运行:
```py
# Plot the time series data
data_timeseries.plot()
plt.title('Input data')
plt.show()
```
- The full code is given in the
convert_to_timeseries.pyfile that is provided to you. If you run the code, you will see the following image:

对时间序列数据进行切片
在这个食谱中,我们将学习如何使用熊猫对时间序列数据进行切片。这将有助于您从时间序列数据的不同间隔中提取信息。我们将学习如何使用日期来处理数据子集。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import pandas as pd import matplotlib.pyplot as plt from convert_to_timeseries import convert_data_to_timeseries -
我们将使用与上一个配方中相同的文本文件对数据进行切片和切割:
# Input file containing data input_file = 'data_timeseries.txt' -
我们将再次使用第三列:
# Load data column_num = 2 data_timeseries = convert_data_to_timeseries(input_file, column_num) -
让我们假设我们想要提取给定开始和结束年份之间的数据。让我们定义这些,如下所示:
# Plot within a certain year range start = '2008' end = '2015' -
绘制给定年份范围之间的数据:
plt.figure() data_timeseries[start:end].plot() plt.title('Data from ' + start + ' to ' + end) -
我们也可以根据某个月的范围对数据进行切片:
# Plot within a certain range of dates start = '2007-2' end = '2007-11' -
绘制数据,如下所示:
plt.figure() data_timeseries[start:end].plot() plt.title('Data from ' + start + ' to ' + end) plt.show() -
The full code is given in the
slicing_data.pyfile that is provided to you. If you run the code, you will see the following image:![How to do it…]()
-
The next figure will display a smaller time frame; hence, it looks like we have zoomed into it:
![How to do it…]()
根据时间序列数据运行
现在我们知道如何对数据进行切片,提取各种子集,下面我们来讨论一下如何对时间序列数据进行操作。您可以用许多不同的方式过滤数据。熊猫库允许你以任何你想要的方式对时间序列数据进行操作。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import pandas as pd import matplotlib.pyplot as plt from convert_to_timeseries import convert_data_to_timeseries -
我们将使用与上一个配方中相同的文本文件:
# Input file containing data input_file = 'data_timeseries.txt' -
我们将使用这个文本文件中的第三和第四列:
# Load data data1 = convert_data_to_timeseries(input_file, 2) data2 = convert_data_to_timeseries(input_file, 3) -
将数据转换成熊猫数据帧:
dataframe = pd.DataFrame({'first': data1, 'second': data2}) -
绘制给定年份范围内的数据:
# Plot data dataframe['1952':'1955'].plot() plt.title('Data overlapped on top of each other') -
让我们假设我们想要绘制在给定年份范围内刚刚加载的两列之间的差异。我们可以使用以下几行代码来做到这一点:
# Plot the difference plt.figure() difference = dataframe['1952':'1955']['first'] - dataframe['1952':'1955']['second'] difference.plot() plt.title('Difference (first - second)') -
如果我们想根据第一列和第二列的不同条件过滤数据,我们可以只指定这些条件并绘制如下:
# When 'first' is greater than a certain threshold # and 'second' is smaller than a certain threshold dataframe[(dataframe['first'] > 60) & (dataframe['second'] < 20)].plot() plt.title('first > 60 and second < 20') plt.show() -
The full code is in the
operating_on_data.pyfile that is already provided to you. If you run the code, the first figure will look like the following:![How to do it…]()
-
The second output figure denotes the difference, as follows:
![How to do it…]()
-
The third output figure denotes the filtered data, as follows:

从时间序列数据中提取统计数据
我们要分析时间序列数据的主要原因之一就是从中提取有趣的统计数据。这提供了大量关于数据性质的信息。在这个食谱中,我们将看看如何提取这些统计数据。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import pandas as pd import matplotlib.pyplot as plt from convert_to_timeseries import convert_data_to_timeseries -
我们将使用我们在前面的配方中使用的相同文本文件进行分析:
# Input file containing data input_file = 'data_timeseries.txt' -
加载两个数据列(第三列和第四列):
# Load data data1 = convert_data_to_timeseries(input_file, 2) data2 = convert_data_to_timeseries(input_file, 3) -
创建一个熊猫数据结构来保存这些数据。这个数据框就像一个字典,有键和值:
dataframe = pd.DataFrame({'first': data1, 'second': data2}) -
让我们现在开始提取一些统计数据。要提取最大值和最小值,请使用以下代码:
# Print max and min print '\nMaximum:\n', dataframe.max() print '\nMinimum:\n', dataframe.min() -
要打印数据的平均值或行平均值,请使用以下代码:
# Print mean print '\nMean:\n', dataframe.mean() print '\nMean row-wise:\n', dataframe.mean(1)[:10] -
滚动平均值是时间序列处理中经常使用的一个重要统计量。最著名的应用之一是平滑信号以消除噪声。滚动平均值是指计算在时间尺度上不断滑动的窗口中信号的平均值。让我们考虑
24的窗口大小,并绘制如下图:# Plot rolling mean pd.rolling_mean(dataframe, window=24).plot() -
相关系数有助于理解数据的性质,如下所示:
# Print correlation coefficients print '\nCorrelation coefficients:\n', dataframe.corr() -
让我们用
60:# Plot rolling correlation plt.figure() pd.rolling_corr(dataframe['first'], dataframe['second'], window=60).plot() plt.show()的窗口大小来绘制这个图
-
The full code is given in the
extract_stats.pyfile that is already provided to you. If you run the code, the rolling mean will look like the following:

- The second output figure indicates the rolling correlation:

- In the upper half of the Terminal, you will see max, min, and mean values printed, as shown in the following image:

- In the lower half of the Terminal, you will see the row-wise mean stats and correlation coefficients printed, as seen in the following image:

建立序列数据的隐马尔可夫模型
当涉及到顺序数据分析时,隐马尔可夫模型 ( HMMs )真的很强大。它们被广泛用于金融、语音分析、天气预报、单词排序等等。我们通常对揭示随时间出现的隐藏模式感兴趣。
任何产生一系列输出的数据源都可能产生模式。请注意,hmm 是生成模型,这意味着一旦他们了解了底层结构,他们就可以生成数据。hmm 不能区分基本形式的类。这与区分模型形成对比,区分模型可以学习区分类,但不能生成数据。
做好准备
例如,假设我们想预测明天天气是晴朗、寒冷还是下雨。为此,我们查看所有参数,如温度、压力等,而底层状态是隐藏的。这里,基础状态指的是三个可用的选项:晴天、冷天或雨天。如果你想了解更多关于头盔显示器的知识,请点击https://www.robots.ox.ac.uk/~vgg/rg/slides/hmm.pdf查看本教程。
我们将使用hmmlearn来构建和培训 hmm。在继续之前,请确保安装了此软件。您可以在http://hmmlearn.readthedocs.org/en/latest找到安装说明。
怎么做…
-
新建一个 Python 文件,导入以下包:
import datetime import numpy as np import matplotlib.pyplot as plt from hmmlearn.hmm import GaussianHMM from convert_to_timeseries import convert_data_to_timeseries -
我们将使用已经提供给您的名为
data_hmm.txt的文件中的数据。该文件包含逗号分隔的行。每行包含三个值:一年、一个月和一个浮点数据。让我们将它加载到一个 NumPy 数组中:# Load data from input file input_file = 'data_hmm.txt' data = np.loadtxt(input_file, delimiter=',') -
让我们按列堆叠数据进行分析。我们不需要在技术上对它进行列堆叠,因为它只有一列。但是,如果您有多个列要分析,您可以使用以下结构:
# Arrange data for training X = np.column_stack([data[:,2]]) -
使用四个组件创建和训练隐马尔可夫模型。成分的数量是我们必须选择的超参数。这里,通过选择四个,我们说数据是使用四个底层状态生成的。我们将很快看到性能如何随此参数变化:
# Create and train Gaussian HMM print "\nTraining HMM...." num_components = 4 model = GaussianHMM(n_components=num_components, covariance_type="diag", n_iter=1000) model.fit(X) -
运行预测器获取隐藏状态:
# Predict the hidden states of HMM hidden_states = model.predict(X) -
计算隐藏状态的均值和方差:
print "\nMeans and variances of hidden states:" for i in range(model.n_components): print "\nHidden state", i+1 print "Mean =", round(model.means_[i][0], 3) print "Variance =", round(np.diag(model.covars_[i])[0], 3) -
正如我们之前讨论的,hmm 是生成模型。因此,让我们生成例如
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() -
The full code is given in the
hmm.pyfile that is already provided to you. If you run the code, you will see the following figure:![How to do it…]()
-
You can experiment with the
n_componentsparameter to see how the curve gets nicer as you increase it. You can basically give it more freedom to train and customize by allowing a larger number of hidden states. If you increase it to8, you will see the following figure:![How to do it…]()
-
If you increase this to
12, it will get even smoother:

- In the Terminal, you will get the following output:

为连续文本数据构建条件随机字段
条件随机场 ( CRFs )是用于分析结构化数据的概率模型。它们经常用于标记和分割序列数据。通用报告格式是区别模型,而 hmm 是生成模型。通用报告格式被广泛用于分析序列、股票、语音、单词等。在这些模型中,给定一个特定的标记观察序列,我们定义了这个序列的条件概率分布。这与 hmm 形成对比,在 hmm 中,我们定义了标签和观察序列的联合分布。
做好准备
hmm 假设当前输出在统计上独立于以前的输出。这是 hmm 需要的,以确保推理以健壮的方式工作。然而,这个假设不一定总是正确的!时间序列设置中的当前输出通常取决于先前的输出。相对于 hmm,CRF 的主要优势之一是它们本质上是有条件的,这意味着我们不假设输出观测之间有任何独立性。使用通用报告格式比使用 hmm 还有其他一些优势。在许多应用中,如语言学、生物信息学、语音分析等,通用报告格式往往优于 hmm。在这个食谱中,我们将学习如何使用 CRFs 来分析字母序列。
我们将使用名为pystruct的库来构建和训练 CRF。在继续之前,请确保安装了此软件。您可以在https://pystruct.github.io/installation.html找到安装说明。
怎么做…
-
新建一个 Python 文件,导入以下包:
import os import argparse import cPickle as pickle import numpy as np import matplotlib.pyplot as plt from pystruct.datasets import load_letters from pystruct.models import ChainCRF from pystruct.learners import FrankWolfeSSVM -
定义一个参数解析器,将
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 -
定义一个类来处理所有与 CRF 相关的处理:
class CRFTrainer(object): -
定义一个
init函数来初始化值:def __init__(self, c_value, classifier_name='ChainCRF'): self.c_value = c_value self.classifier_name = classifier_name -
我们将使用链式 CRF 来分析数据。我们需要为此添加一个错误检查,如下所示:
if self.classifier_name == 'ChainCRF': model = ChainCRF() -
定义我们将在通用报告格式模型中使用的分类器。我们将使用一种类型的支持向量麻吉 ne 来实现这一点:
self.clf = FrankWolfeSSVM(model=model, C=self.c_value, max_iter=50) else: raise TypeError('Invalid classifier type') -
加载字母数据集。该数据集由分段字母及其相关特征向量组成。我们不会分析图像,因为我们已经有了特征向量。每个单词的第一个字母都被去掉了,所以我们只有小写字母:
def load_data(self): letters = load_letters() -
将数据和标签加载到各自的变量中:
X, y, folds = letters['data'], letters['labels'], letters['folds'] X, y = np.array(X), np.array(y) return X, y, folds -
定义一种训练方法,如下所示:
# 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) -
定义评估模型性能的方法:
```py
def evaluate(self, X_test, y_test):
return self.clf.score(X_test, y_test)
```
- 定义新数据的分类方法:
```py
# Run the classifier on input data
def classify(self, input_data):
return self.clf.predict(input_data)[0]
```
- 这些字母被编入一个编号数组。为了检查输出并使其可读,我们需要将这些数字转换成字母。为此定义一个函数:
```py
def decoder(arr):
```
```py
alphabets = 'abcdefghijklmnopqrstuvwxyz'
output = ''
for i in arr:
output += alphabets[i]
return output
```
- 定义函数并解析输入参数:
```py
if __name__=='__main__':
args = build_arg_parser().parse_args()
c_value = args.c_value
```
- 用类和
C值初始化变量:
```py
crf = CRFTrainer(c_value)
```
- 加载字母数据:
```py
X, y, folds = crf.load_data()
```
- 将数据分成训练和测试数据集:
```py
X_train, X_test = X[folds == 1], X[folds != 1]
y_train, y_test = y[folds == 1], y[folds != 1]
```
- 训练通用报告格式模型,如下所示:
```py
print "\nTraining the CRF model..."
crf.train(X_train, y_train)
```
- 评估通用报告格式模型的性能:
```py
score = crf.evaluate(X_test, y_test)
print "\nAccuracy score =", str(round(score*100, 2)) + '%'
```
- 让我们取一个随机测试向量,并使用模型预测输出:
```py
print "\nTrue label =", decoder(y_test[0])
predicted_output = crf.classify([X_test[0]])
print "Predicted output =", decoder(predicted_output)
```
- The full code is given in the
crf.pyfile that is already provided to you. If you run this code, you will get the following output on your Terminal. As we can see, the word is supposed to be "commanding". The CRF does a pretty good job of predicting all the letters:

利用隐马尔可夫模型分析股市数据
让我们使用隐马尔可夫模型分析股市数据。股市数据是时间序列数据的一个很好的例子,其中数据以日期的形式组织。在我们将要使用的数据集中,我们可以看到不同公司的股票价值是如何随着时间波动的。隐马尔可夫模型是用于分析这种时间序列数据的生成模型。在这个食谱中,我们将使用这些模型来分析股票价值。
怎么做…
-
新建一个 Python 文件,导入以下包:
import datetime import numpy as np import matplotlib.pyplot as plt from matplotlib.finance import quotes_historical_yahoo_ochl from hmmlearn.hmm import GaussianHMM -
从雅虎财经获取股票报价。
matplotlib中有一种方法可以直接加载:# Get quotes from Yahoo finance quotes = quotes_historical_yahoo_ochl("INTC", datetime.date(1994, 4, 5), datetime.date(2015, 7, 3)) -
每个报价中有六个值。让我们提取相关数据,如股票的收盘价和交易的股票数量,以及它们对应的日期:
# Extract the required values dates = np.array([quote[0] for quote in quotes], dtype=np.int) closing_values = np.array([quote[2] for quote in quotes]) volume_of_shares = np.array([quote[5] for quote in quotes])[1:] -
让我们计算每种类型数据的收盘价的百分比变化。我们将使用这作为特征之一:
# Take diff of closing values and computing rate of change diff_percentage = 100.0 * np.diff(closing_values) / closing_values[:-1] dates = dates[1:] -
按列堆叠两个数组进行训练:
# Stack the percentage diff and volume values column-wise for training X = np.column_stack([diff_percentage, volume_of_shares]) -
使用五个组件训练隐马尔可夫模型:
# Create and train Gaussian HMM print "\nTraining HMM...." model = GaussianHMM(n_components=5, covariance_type="diag", n_iter=1000) model.fit(X) -
使用训练好的隐马尔可夫模型生成
500样本并绘制出来,如下所示:# Generate data using model num_samples = 500 samples, _ = model.sample(num_samples) plt.plot(np.arange(num_samples), samples[:,0], c='black') plt.show() -
The full code is given in
hmm_stock.pythat is already provided to you. If you run this code, you will see the following figure:![How to do it…]()
九、图像内容分析
在本章中,我们将介绍以下食谱:
- 使用 OpenCV-Python 对图像进行操作
- 检测边缘
- 直方图均衡
- 检测拐角
- 检测 SIFT 特征点
- 建筑之星特征探测器
- 使用视觉码本和矢量量化创建特征
- 使用极随机森林训练图像分类器
- 构建对象识别器
简介
计算机视觉是一个研究如何处理、分析和理解视觉数据内容的领域。在图像内容分析中,我们使用大量的计算机视觉算法来建立我们对图像中对象的理解。计算机视觉涵盖了图像分析的各个方面,如目标识别、形状分析、姿态估计、三维建模、视觉搜索等。人类真的很擅长识别和认识周围的事物!计算机视觉的最终目标是利用计算机对人类视觉系统进行精确建模。
计算机视觉由不同层次的分析组成。在低级视觉中,我们处理像素处理任务,例如边缘检测、形态处理和光流。在中级和高级视觉中,我们处理事物,例如对象识别、3D 建模、运动分析以及视觉数据的各种其他方面。随着我们越走越高,我们倾向于更深入地研究我们视觉系统的概念方面,并试图基于活动和意图提取视觉数据的描述。需要注意的一点是,较高层往往依赖较低层的输出进行分析。
这里最常见的一个问题是,“计算机视觉和图像处理有什么不同?”图像处理研究像素级的图像变换。图像处理系统的输入和输出都是图像。一些常见的例子是边缘检测、直方图均衡化或图像压缩。计算机视觉算法在很大程度上依赖图像处理算法来履行其职责。在计算机视觉中,我们处理更复杂的事情,包括在概念层面理解视觉数据。这样做的原因是因为我们想要对图像中的对象进行有意义的描述。计算机视觉系统的输出是给定图像中三维场景的解释。这种解释可以有多种形式,取决于手头的任务。
在本章中,我们将使用一个名为【OpenCV】的库来分析图像。OpenCV 是世界上最受欢迎的计算机视觉库。由于它已经针对许多不同的平台进行了高度优化,它已经成为行业中事实上的标准。在继续之前,请确保安装了支持 Python 的库。您可以在http://opencv.org下载安装 OpenCV。有关各种操作系统的详细安装说明,您可以参考网站上的文档部分。
使用 OpenCV-Python 对图像进行操作
让我们来看看如何使用 OpenCV-Python 对图像进行操作。在这个食谱中,我们将看到如何加载和显示图像。我们还将了解如何裁剪、调整大小以及将图像保存到输出文件中。
怎么做…
-
新建一个 Python 文件,导入以下包:
import sys import cv2 import numpy as np -
将输入图像指定为文件的第一个参数,并使用图像读取函数读取它。我们将使用
forest.jpg,如下所示:# Load and display an image -- 'forest.jpg' input_file = sys.argv[1] img = cv2.imread(input_file) -
显示输入图像,如下所示:
cv2.imshow('Original', img) -
我们现在将裁剪这张图片。提取输入图像的高度和宽度,然后指定边界:
# 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) -
使用 NumPy 样式切片裁剪图像并显示:
img_cropped = img[start_row:end_row, start_col:end_col] cv2.imshow('Cropped', img_cropped) -
将图像调整到原始尺寸的
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) -
前面的方法将在两个维度上统一缩放图像。让我们假设我们想要基于特定的输出维度来扭曲图像。我们使用以下代码:
img_scaled = cv2.resize(img, (250, 400), interpolation=cv2.INTER_AREA) cv2.imshow('Skewed resizing', img_scaled) -
将图像保存到输出文件中:
# Save an image output_file = input_file[:-4] + '_cropped.jpg' cv2.imwrite(output_file, img_cropped) cv2.waitKey() -
waitKey()功能显示图像,直到你按下键盘上的一个键。 -
The full code is given in the
operating_on_images.pyfile that is already provided to you. If you run the code, you will see the following input image:

- The second output is the cropped image:

- The third output is the uniformly resized image:

- The fourth output is the skewed image:

检测边缘
边缘检测是计算机视觉中最流行的技术之一。它在许多应用中用作预处理步骤。让我们看看如何使用不同的边缘检测器来检测输入图像中的边缘。
怎么做…
-
新建一个 Python 文件,导入以下包:
import sys import cv2 import numpy as np -
加载输入图像。我们将使用
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) -
提取图像的高度和宽度:
h, w = img.shape -
索贝尔滤波器是一种类型的边缘检测器,使用 3x3 内核分别检测水平和垂直边缘。你可以在http://www.tutorialspoint.com/dip/sobel_operator.htm了解更多。让我们从水平探测器开始:
sobel_horizontal = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5) -
运行垂直索贝尔检测器:
sobel_vertical = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5) -
拉普拉斯边缘检测器在两个方向上检测边缘。你可以在http://homepages.inf.ed.ac.uk/rbf/HIPR2/log.htm了解更多。我们使用如下:
laplacian = cv2.Laplacian(img, cv2.CV_64F) -
即使拉普拉斯解决了 Sobel 的缺点,输出仍然非常嘈杂。 Canny 边缘检测器比都强,因为它处理问题的方式。这是一个多阶段的过程,它使用滞后来产生干净的边缘。你可以在http://homepages.inf.ed.ac.uk/rbf/HIPR2/canny.htm:
canny = cv2.Canny(img, 50, 240)了解更多
-
显示所有输出图像:
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() -
The full code is given in the
edge_detector.pyfile that is already provided to you. The original input image looks like the following:![How to do it…]()
-
Here is the horizontal Sobel edge detector output. Note how the detected lines tend to be vertical. This is due the fact that it's a horizontal edge detector, and it tends to detect changes in this direction:

- The vertical Sobel edge detector output looks like the following image:

- Here is the Laplacian edge detector output:

- Canny edge detector detects all the edges nicely, as shown in the following image:

直方图均衡化
直方图均衡化是修改图像像素强度以增强对比度的过程。人眼喜欢对比!这就是为什么几乎所有的相机系统都使用直方图均衡化来使图像看起来好看。有趣的是,直方图均衡化过程对于灰度和彩色图像是不同的。在处理彩色图像时有一个陷阱,我们将在这个食谱中看到它。让我们看看怎么做。
怎么做…
-
新建一个 Python 文件,导入以下包:
import sys import cv2 import numpy as np -
加载输入图像。我们将使用图像,
sunrise.jpg:# Load input image -- 'sunrise.jpg' input_file = sys.argv[1] img = cv2.imread(input_file) -
将图像转换为灰度并显示:
# Convert it to grayscale img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) cv2.imshow('Input grayscale image', img_gray) -
均衡灰度图像的直方图并显示:
# Equalize the histogram img_gray_histeq = cv2.equalizeHist(img_gray) cv2.imshow('Histogram equalized - grayscale', img_gray_histeq) -
为了均衡彩色图像的直方图,我们需要遵循不同的程序。直方图均衡仅适用于强度通道。一幅 RGB 图像由三个颜色通道组成,我们不能对这些通道分别应用直方图均衡化过程。在我们做任何事情之前,我们需要把强度信息和颜色信息分开。所以,我们先把它转换成 YUV 色彩空间,均衡 Y 通道,然后再转换回 RGB 得到输出。您可以在http://softpixel.com/~cwright/programming/colorspace/yuv了解更多关于 YUV 色彩空间的信息。OpenCV 默认加载 BGR 格式的图像,所以我们先把它从 BGR 转换成 YUV:
# Histogram equalization of color images img_yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV) -
均衡 Y 通道,如下所示:
img_yuv[:,:,0] = cv2.equalizeHist(img_yuv[:,:,0]) -
将其转换回 BGR:
img_histeq = cv2.cvtColor(img_yuv, cv2.COLOR_YUV2BGR) -
显示输入和输出图像:
cv2.imshow('Input color image', img) cv2.imshow('Histogram equalized - color', img_histeq) cv2.waitKey() -
The full code is given in the
histogram_equalizer.pyfile that is already provided to you. The input image is shown, as follows:![How to do it…]()
-
The histogram equalized image looks like the following:

检测拐角
角点检测是计算机视觉中的一个重要过程。它帮助我们识别图像中的显著点。这是最早用于开发图像分析系统的特征提取技术之一。
怎么做…
-
新建一个 Python 文件,导入以下包:
import sys import cv2 import numpy as np -
加载输入图像。我们将使用
box.png:# Load input image -- 'box.png' input_file = sys.argv[1] img = cv2.imread(input_file) cv2.imshow('Input image', img) -
将图像转换为灰度并将其转换为浮点值。我们需要角点检测器的浮点值来工作:
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img_gray = np.float32(img_gray) -
在灰度图像上运行 哈里斯角点检测器功能。您可以在了解更多关于哈里斯角点检测器的信息
-
为了标记拐角,我们需要放大图像,如下所示:
# Resultant image is dilated to mark the corners img_harris = cv2.dilate(img_harris, None) -
让我们对图像进行阈值化,以显示重要的点:
# Threshold the image img[img_harris > 0.01 * img_harris.max()] = [0, 0, 0] -
显示输出图像:
cv2.imshow('Harris Corners', img) cv2.waitKey() -
The full code is given in the
corner_detector.pyfile that is already provided to you. The input image is displayed, as follows:![How to do it…]()
-
The output image after detecting corners is as follows:
![How to do it…]()
检测 SIFT 特征点
尺度不变特征变换 ( SIFT )是计算机视觉领域最受欢迎的特征之一。大卫·劳在他的开创性论文中首次提出了这一点,该论文可在 https://www.cs.ubc.ca/~lowe/papers/ijcv04.pdf 获得。从那时起,它已经成为用于图像识别和内容分析的最有效的功能之一。它对规模、方向、强度等具有鲁棒性。这构成了我们物体识别系统的基础。让我们来看看如何检测这些特征点。
怎么做…
-
新建一个 Python 文件,导入以下包:
import sys import cv2 import numpy as np -
加载输入图像。我们将使用
table.jpg:# Load input image -- 'table.jpg' input_file = sys.argv[1] img = cv2.imread(input_file) -
将此图像转换为灰度:
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) -
初始化 SIFT 检测器对象并提取关键点:
sift = cv2.xfeatures2d.SIFT_create() keypoints = sift.detect(img_gray, None) -
关键点是显著点,但不是特征。这基本上给了我们突出点的位置。SIFT 也是一个非常有效的特征提取器,但是我们将在后面的食谱中看到它的这一方面。
-
在输入图像的顶部绘制关键点,如下所示:
img_sift = np.copy(img) cv2.drawKeypoints(img, keypoints, img_sift, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) -
显示输入和输出图像:
cv2.imshow('Input image', img) cv2.imshow('SIFT features', img_sift) cv2.waitKey() -
The full code is given in the
feature_detector.pyfile that is already provided to you. The input image is as follows:![How to do it…]()
-
The output image looks like the following:
![How to do it…]()
构建星特征检测器
SIFT 特征检测器在很多情况下都不错。然而,当我们构建对象识别系统时,我们可能希望在使用 SIFT 提取特征之前使用不同的特征检测器。这将使我们能够灵活地级联不同的块,以获得最佳性能。所以,在这种情况下,我们将使用恒星特征检测器来看看如何做到这一点。
怎么做…
-
新建一个 Python 文件,导入以下包:
import sys import cv2 import numpy as np -
定义一个类来处理所有与星特征检测相关的功能:
class StarFeatureDetector(object): def __init__(self): self.detector = cv2.xfeatures2d.StarDetector_create() -
定义在输入图像上运行检测器的功能:
def detect(self, img): return self.detector.detect(img) -
在
main功能中加载输入图像。我们将使用table.jpg:T2 -
将图像转换为灰度:
# Convert to grayscale img_gray = cv2.cvtColor(input_img, cv2.COLOR_BGR2GRAY) -
使用星形特征检测器检测特征:
# Detect features using Star feature detector keypoints = StarFeatureDetector().detect(input_img) -
在输入图像上绘制关键点:
cv2.drawKeypoints(input_img, keypoints, input_img, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) -
显示输出图像:
cv2.imshow('Star features', input_img) cv2.waitKey() -
The full code is given in the
star_detector.pyfile that is already provided to you. The output image looks like the following:![How to do it…]()
使用视觉码本和矢量量化创建特征
为了构建一个物体识别系统,我们需要从每个图像中提取特征向量。每个图像需要有一个签名,可以用于匹配。我们使用一个名为 的概念来构建图像签名。该码本基本上是字典,我们将使用它来为训练数据集中的图像提供表示。我们使用矢量量化来聚类许多特征点,并得出质心。这些质心将作为我们视觉码本的元素。您可以在http://mi . eng . cam . AC . uk/~ cipolla/讲座/PartIb/old/IB-visualcodebook . pdf了解更多信息。
在你开始之前,确保你有一些训练图像。给你提供了一个包含三个类的样本训练数据集,每个类有 20 个图像。这些图片是从http://www.vision.caltech.edu/html-files/archive.html下载的。
要建立一个健壮的物体识别系统,你需要成千上万的图像。有一个叫做Caltech256的数据集,在这个领域非常流行!它包含 256 类图像,每个类包含数千个样本。您可以在http://www.vision.caltech.edu/Image_Datasets/Caltech256下载该数据集。
怎么做…
-
这是一个冗长的食谱,所以我们只看重要的功能。完整的代码在已经提供给你的
build_features.py文件中给出。让我们来看看为提取特征而定义的类:class FeatureBuilder(object): -
定义从输入图像中提取特征的方法。我们将使用星检测器获取关键点,然后使用 SIFT 从这些位置提取描述符:
def extract_ features(self, img): keypoints = StarFeatureDetector().detect(img) keypoints, feature_vectors = compute_sift_features(img, keypoints) return feature_vectors -
我们需要从所有描述符中提取质心:
def get_codewords(self, input_map, scaling_size, max_samples=12): keypoints_all = [] count = 0 cur_label = '' -
每个图像将产生大量描述符。我们将只使用少量图像,因为质心在此之后不会有太大变化:
for item in input_map: if count >= max_samples: if cur_class != item['object_class']: count = 0 else: continue count += 1 -
打印进度如下:
if count == max_samples: print "Built centroids for", item['object_class'] -
提取当前标签:
cur_class = item['object_class'] -
读取图像并调整大小:
img = cv2.imread(item['image_path']) img = resize_image(img, scaling_size) -
将维数设置为 128,提取特征:
num_dims = 128 feature_vectors = self.extract_image_features(img) keypoints_all.extend(feature_vectors) -
使用矢量量化对特征点进行量化。矢量量化是N-维版本的“四舍五入”。你可以在http://www.data-compression.com/vq.shtml了解更多。
kmeans, centroids = BagOfWords().cluster(keypoints_all) return kmeans, centroids -
定义处理词包模型和矢量量化的类:
```py
class BagOfWords(object):
def __init__(self, num_clusters=32):
self.num_dims = 128
self.num_clusters = num_clusters
self.num_retries = 10
```
- 定义量化数据点的方法。我们将使用 k-means 聚类来实现这一点:
```py
def cluster(self, datapoints):
kmeans = KMeans(self.num_clusters,
n_init=max(self.num_retries, 1),
max_iter=10, tol=1.0)
```
- 提取质心,如下所示:
```py
res = kmeans.fit(datapoints)
centroids = res.cluster_centers_
return kmeans, centroids
```
- 定义一种数据归一化的方法:
```py
def normalize(self, input_data):
sum_input = np.sum(input_data)
if sum_input > 0:
return input_data / sum_input
else:
return input_data
```
- 定义一种获取特征向量的方法:
```py
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)
```
- 建立直方图并归一化:
```py
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)
```
- Define a method the extract the SIFT features:
```py
# 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。您应该以下列方式运行代码:
```py
$ python build_features.py –-data-folder /path/to/training_img/ --codebook-file codebook.pkl --feature-map-file feature_map.pkl
```
这将生成两个名为`codebook.pkl`和`feature_map.pkl`的文件。我们将在下一个食谱中使用这些文件。
使用极随机森林训练图像分类器
我们将使用 【极随机森林】 ( ERFs )来训练我们的图像分类器。物体识别系统使用图像分类器将图像分类成已知的类别。由于的速度和精度,ERFs 在机器学习领域非常受欢迎。我们基本上构建了一堆基于我们的图像签名的决策树,然后训练森林做出正确的决定。你可以在https://www . stat . Berkeley . edu/~ brei man/RandomForests/cc _ home . htm了解更多随机森林。您可以在http://www . Monte fiore . ulg . AC . be/~ Ernst/uploads/news/id63/extreme-random-trees . pdf了解 ERFs。
怎么做…
-
新建一个 Python 文件,导入以下包:
import argparse import cPickle as pickle import numpy as np from sklearn.ensemble import ExtraTreesClassifier from sklearn import preprocessing -
定义参数解析器:
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 -
定义一个类来处理电流变流体培训。我们将使用标签编码器来编码我们的训练标签:
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) -
编码标签并训练分类器:
y = self.encode_labels(label_words) self.clf.fit(np.asarray(X), y) -
定义一个函数来编码标签:
def encode_labels(self, label_words): self.le.fit(label_words) return np.array(self.le.transform(label_words), dtype=np.float32) -
定义一个函数对未知数据点进行分类:
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 -
定义
main函数并解析输入参数:if __name__=='__main__': args = build_arg_parser().parse_args() feature_map_file = args.feature_map_file model_file = args.model_file -
加载我们在上一个食谱中创建的特征图:
# Load the feature map with open(feature_map_file, 'r') as f: feature_map = pickle.load(f) -
提取特征向量:
# 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] -
培训电流变流体,基于培训数据:
```py
# Train the Extremely Random Forests classifier
erf = ERFTrainer(X, label_words)
```
- 保存训练好的电流变液模型,如下所示:
```py
if args.model_file:
with open(args.model_file, 'w') as f:
pickle.dump(erf, f)
```
- The full code is given in the
trainer.pyfile that is provided to you. You should run the code in the following way:
```py
$ python trainer.py --feature-map-file feature_map.pkl --model-file erf.pkl
```
这将生成一个名为`erf.pkl`的文件。我们将在下一个食谱中使用这个文件。
构建对象识别器
现在我们训练了一个 ERF 模型,让我们继续构建一个可以识别未知图像内容的对象识别器。
怎么做…
-
新建一个 Python 文件,导入以下包:
import argparse import cPickle as pickle import cv2 import numpy as np import build_features as bf from trainer import ERFTrainer -
定义参数解析器:
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 -
定义一个类来处理图像标签提取功能:
class ImageTagExtractor(object): def __init__(self, model_file, codebook_file): with open(model_file, 'r') as f: self.erf = pickle.load(f) with open(codebook_file, 'r') as f: self.kmeans, self.centroids = pickle.load(f) -
使用训练好的电流变流体模型定义一个函数来预测输出:
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 -
定义
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) -
适当缩放图像,如下所示:
scaling_size = 200 -
在终端上打印输出:
print "\nOutput:", ImageTagExtractor(model_file, codebook_file).predict(input_image, scaling_size) -
The full code is given in the
object_recognizer.pyfile that is already provided to you. You should run the code in the following way:$ python object_recognizer.py --input-image imagefile.jpg --model-file erf.pkl --codebook-file codebook.pkl您将看到终端上打印的输出类。
十、生物识别人脸识别
在本章中,我们将介绍以下食谱:
- 从网络摄像头捕捉和处理视频
- 使用哈尔级联构建人脸检测器
- 构建眼睛和鼻子探测器
- 执行主成分分析
- 执行核心主成分分析
- 执行盲源分离
- 利用局部二值模式直方图构建人脸识别器
简介
人脸识别是指在给定的图像中识别人的任务。这与人脸检测不同,在人脸检测中,我们在给定的图像中定位人脸。在人脸检测过程中,我们不在乎这个人是谁。我们只是识别图像中包含人脸的区域。因此,在典型的生物特征人脸识别系统中,我们需要确定人脸的位置才能识别它。
人脸识别对人类来说非常容易。我们似乎毫不费力地做到了,而且我们一直都在这样做!我们如何让机器做同样的事情?我们需要了解面部的哪些部位可以用来唯一地识别一个人。我们的大脑有一个内部结构,它似乎对特定的特征做出反应,比如边缘、角落、运动等等。人类视觉皮层将所有这些特征结合成一个连贯的推论。如果我们想让我们的机器准确地识别人脸,我们需要用类似的方式来表述这个问题。我们需要从输入图像中提取特征,并将其转换为有意义的表示。
从网络摄像头捕捉和处理视频
我们将在本章中使用网络摄像头来捕获视频数据。让我们看看如何使用 OpenCV-Python 从网络摄像头捕捉视频。
怎么做…
-
新建一个 Python 文件,导入以下包:
import cv2 -
OpenCV 提供了一个视频捕获对象,我们可以使用它从网络摄像头中捕获图像。
0输入参数指定网络摄像头的 ID。如果你连接一个 USB 摄像头,那么它会有一个不同的 ID:# Initialize video capture object cap = cv2.VideoCapture(0) -
定义使用网络摄像头拍摄的帧的比例因子:
# Define the image size scaling factor scaling_factor = 0.5 -
开始一个无限循环,并保持捕捉帧,直到您按下 Esc 键。从网络摄像头读取画面:
# Loop until you hit the Esc key while True: # Capture the current frame ret, frame = cap.read() -
调整框架大小是可选的,但在代码中仍然是有用的:
# Resize the frame frame = cv2.resize(frame, None, fx=scaling_factor, fy=scaling_factor, interpolation=cv2.INTER_AREA) -
显示画面:
# Display the image cv2.imshow('Webcam', frame) -
等待 1 ms,然后捕捉下一帧:
# Detect if the Esc key has been pressed c = cv2.waitKey(1) if c == 27: break -
释放视频拍摄对象:
# Release the video capture object cap.release() -
退出代码前关闭所有活动窗口:
# Close all active windows cv2.destroyAllWindows() -
The full code is given in the
video_capture.pyfile that's already provided to you for reference. If you run this code, you will see the video from the webcam, similar to the following screenshot:

使用哈尔级联构建人脸检测器
正如我们前面讨论的,人脸检测是确定人脸在输入图像中的位置的过程。我们将使用哈尔级联进行人脸检测。这是通过在多个尺度上从图像中提取大量简单特征来实现的。简单的特征基本上是非常容易计算的边、线和矩形特征。然后通过创建一系列简单的分类器来训练它。 自适应增压技术用于使该过程稳健。可以在http://docs . opencv . org/3 . 1 . 0/D7/d8b/tutorial _ py _ face _ detection . html # GSC . tab = 0了解更多。让我们来看看如何在网络摄像头拍摄的视频帧中确定人脸的位置。
怎么做…
-
新建一个 Python 文件,导入以下包:
import cv2 import numpy as np -
加载面探测器级联文件。这是一个训练好的模型,我们可以用作检测器:
# Load the face cascade file face_cascade = cv2.CascadeClassifier('cascade_files/haarcascade_frontalface_alt.xml') -
检查级联文件是否加载正确:
# Check if the face cascade file has been loaded if face_cascade.empty(): raise IOError('Unable to load the face cascade classifier xml file') -
创建视频采集对象:
# Initialize the video capture object cap = cv2.VideoCapture(0) -
定义图像下采样的比例因子:
# Define the scaling factor scaling_factor = 0.5 -
继续循环直到你按下 Esc 键:
# Loop until you hit the Esc key while True: # Capture the current frame and resize it ret, frame = cap.read() -
调整框架大小:
frame = cv2.resize(frame, None, fx=scaling_factor, fy=scaling_factor, interpolation=cv2.INTER_AREA) -
将图像转换为灰度。我们需要灰度图像来运行面部检测器:
# Convert to grayscale gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) -
对灰度图像运行面部检测器。
1.3参数是指每个阶段的比例乘数。5参数是指每个候选矩形应该具有的最小邻居数量,以便我们可以保留它。这个候选矩形基本上是一个有可能检测到人脸的潜在区域:# Run the face detector on the grayscale image face_rects = face_cascade.detectMultiScale(gray, 1.3, 5) -
对于每个检测到的人脸区域,在它周围画一个矩形:
```py
# Draw rectangles on the image
for (x,y,w,h) in face_rects:
cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0), 3)
```
- 显示输出图像:
```py
# Display the image
cv2.imshow('Face Detector', frame)
```
- 等待 1 ms,然后进行下一次迭代。如果用户按下 Esc 键,跳出循环:
```py
# Check if Esc key has been pressed
c = cv2.waitKey(1)
if c == 27:
break
```
- 退出代码前释放并销毁对象:
```py
# Release the video capture object and close all windows
cap.release()
cv2.destroyAllWindows()
```
- The full code is given in the
face_detector.pyfile that's already provided to you for reference. If you run this code, you will see the face being detected in the webcam video:

建造眼睛和鼻子探测器
哈尔级联方法可以扩展到检测所有类型的物体。让我们看看如何使用它来检测输入视频中的眼睛和鼻子。
怎么做…
-
新建一个 Python 文件,导入以下包:
import cv2 import numpy as np -
加载面部、眼睛和鼻子级联文件:
# 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') -
检查文件是否正确加载:
# 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') -
初始化视频采集对象:
# Initialize video capture object and define scaling factor cap = cv2.VideoCapture(0) -
定义比例因子:
scaling_factor = 0.5 -
继续循环,直到用户按下 Esc 键:
while True: # Read current frame, resize it, and convert it to grayscale ret, frame = cap.read() -
调整框架大小:
frame = cv2.resize(frame, None, fx=scaling_factor, fy=scaling_factor, interpolation=cv2.INTER_AREA) -
将图像转换为灰度:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) -
在灰度图像上运行人脸检测器:
# Run face detector on the grayscale image faces = face_cascade.detectMultiScale(gray, 1.3, 5) -
因为我们知道眼睛和鼻子总是在脸上,所以我们只能在面部区域运行这些检测器:
```py
# Run eye and nose detectors within each face rectangle
for (x,y,w,h) in faces:
```
- 提取人脸感兴趣区域:
```py
# 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]
```
- 运行眼睛检测器:
```py
# Run eye detector in the grayscale ROI
eye_rects = eye_cascade.detectMultiScale(roi_gray)
```
- 运行鼻子检测器:
```py
# Run nose detector in the grayscale ROI
nose_rects = nose_cascade.detectMultiScale(roi_gray, 1.3, 5)
```
- 在眼睛周围画圈:
```py
# 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)
```
- 在鼻子周围画一个矩形:
```py
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
```
- 显示图像:
```py
# Display the image
cv2.imshow('Eye and nose detector', frame)
```
- 等待 1 ms,然后进行下一次迭代。如果用户按下 Esc 键,则断开循环。
```py
# Check if Esc key has been pressed
c = cv2.waitKey(1)
if c == 27:
break
```
- 退出代码前释放并销毁对象。
```py
# Release video capture object and close all windows
cap.release()
cv2.destroyAllWindows()
```
- The full code is given in the
eye_nose_detector.pyfile that's already provided to you for reference. If you run this code, you will see the eyes and nose being detected in the webcam video:

进行主成分分析
主成分分析 ( 主成分分析)是一种降维技术,在计算机视觉和机器学习中使用非常频繁。当我们处理大维度的特征时,训练机器学习系统变得极其昂贵。因此,在训练一个系统之前,我们需要降低数据的维数。然而,当我们降低维度时,我们不想丢失数据中存在的信息。这就是 PCA 进入画面的地方!主成分分析识别数据的重要组成部分,并按重要性顺序排列。你可以在http://dai.fmph.uniba.sk/courses/ml/sl/PCA.pdf了解更多。它在人脸识别系统中被大量使用。让我们看看如何对输入数据执行 PCA。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np from sklearn import decomposition -
让我们为输入数据定义五个维度。前两个维度将是独立的,但后三个维度将依赖于前两个维度。这基本上意味着我们可以在没有最后三个维度的情况下生活,因为它们没有给我们任何新的信息:
# Define individual features x1 = np.random.normal(size=250) x2 = np.random.normal(size=250) x3 = 2*x1 + 3*x2 x4 = 4*x1 - x2 x5 = x3 + 2*x4 -
让我们用这些特征创建一个数据集。
# Create dataset with the above features X = np.c_[x1, x3, x2, x5, x4] -
创建主成分分析对象:
# Perform Principal Components Analysis pca = decomposition.PCA() -
在输入数据上拟合主成分分析模型:
pca.fit(X) -
打印尺寸差异:
# Print variances variances = pca.explained_variance_ print '\nVariances in decreasing order:\n', variances -
如果某个特定的维度是有用的,那么它将具有有意义的方差值。让我们设定一个阈值,并确定重要的维度:
# Find the number of useful dimensions thresh_variance = 0.8 num_useful_dims = len(np.where(variances > thresh_variance)[0]) print '\nNumber of useful dimensions:', num_useful_dims -
就像我们之前讨论的,主成分分析发现在这个数据集中只有两个维度是重要的:
# As we can see, only the 2 first components are useful pca.n_components = num_useful_dims -
让我们将数据集从五维集转换为二维集:
X_new = pca.fit_transform(X) print '\nShape before:', X.shape print 'Shape after:', X_new.shape -
The full code is given in the
pca.pyfile that's already provided to you for reference. If you run this code, you will see the following on your Terminal:

进行核主成分分析
主成分分析擅长减少维数,但它以线性方式工作。如果数据不是以线性方式组织的,主成分分析就不能完成要求的工作。这就是内核主成分分析进入画面的地方。你可以在上了解更多。让我们看看如何对输入数据执行内核主成分分析,并将其与主成分分析对相同数据的执行进行比较。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt from sklearn.decomposition import PCA, KernelPCA from sklearn.datasets import make_circles -
定义随机数生成器的种子值。这是生成数据样本进行分析所需要的:
# Set the seed for random number generator np.random.seed(7) -
生成同心圆分布的数据,以演示在这种情况下 PCA 如何不起作用:
# Generate samples X, y = make_circles(n_samples=500, factor=0.2, noise=0.04) -
对该数据进行主成分分析:
# Perform PCA pca = PCA() X_pca = pca.fit_transform(X) -
对该数据进行核主成分分析:
# 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) -
绘制原始输入数据:
# 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") -
绘制主成分分析转换数据:
# 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") -
绘制内核主成分分析转换数据:
# 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") -
使用 Kernel 方法将数据转换回原始空间,以显示保留了逆空间:
# 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() -
The full code is given in the
kpca.pyfile that's already provided to you for reference. If you run this code, you will see four figures. The first figure is the original data:

第二个图描述了使用主成分分析转换的数据:

第三幅图描绘了使用核主成分分析转换的数据。请注意图中右侧的点是如何聚集的:

第四张图描绘了数据到原始空间的逆变换:

执行盲源分离
盲源分离指的是从混合物中分离信号的过程。假设一堆不同的信号发生器产生信号,一个公共接收器接收所有这些信号。现在,我们的工作是利用这些信号的特性从混合物中分离出这些信号。我们将使用 独立分量分析 ( ICA )来实现这一点。你可以在http://www . MIT . edu/~ gari/teaching/6.555/LEASE _ NOtes/ch15 _ BSS . pdf了解更多。让我们看看怎么做。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt from scipy import signal from sklearn.decomposition import PCA, FastICA -
我们将使用已经提供给您的
mixture_of_signals.txt文件中的数据。让我们加载数据:# Load data input_file = 'mixture_of_signals.txt' X = np.loadtxt(input_file) -
创建独立分量分析对象:
# Compute ICA ica = FastICA(n_components=4) -
基于独立分量分析重构信号:
# Reconstruct the signals signals_ica = ica.fit_transform(X) -
提取混合矩阵:
# Get estimated mixing matrix mixing_mat = ica.mixing_ -
进行主成分分析比较:
# Perform PCA pca = PCA(n_components=4) signals_pca = pca.fit_transform(X) # Reconstruct signals based on orthogonal components -
定义要绘制的信号列表:
# Specify parameters for output plots models = [X, signals_ica, signals_pca] -
指定图的颜色:
colors = ['blue', 'red', 'black', 'green'] -
绘制输入信号:
# 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) -
绘制独立分量分析分离信号:
```py
# 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)
```
- 用不同的颜色绘制支线剧情:
```py
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)
```
- 绘制主成分分析分离信号:
```py
# 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)
```
- 在每个子剧情中使用不同的颜色:
```py
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()
```
- The full code is given in the
blind_source_separation.pyfile that's already provided to you for reference. If you run this code, you will see three figures. The first figure depicts the input, which is a mixture of signals:

第二幅图描绘了使用独立分量分析分离的信号:

第三幅图描绘了使用主成分分析分离的信号:

利用局部二值模式直方图构建人脸识别器
我们现在准备构建一个人脸识别器。我们需要一个人脸数据集进行训练,所以我们给你提供了一个名为faces_dataset的文件夹,里面包含了少量足以进行训练的图像。该数据集是可在上获得的数据集的子集。这个数据集包含了大量的图像,我们可以用来训练一个人脸识别系统。
我们将使用局部二值模式直方图来构建我们的人脸识别系统。在我们的数据集中,你会看到不同的人。我们的工作是建立一个系统,可以学会将这些人彼此分开。当我们看到一个未知的图像时,这个系统会把它分配给一个现有的类。您可以在上了解更多关于本地二进制模式直方图的信息。来看看如何构建人脸识别器。
怎么做…
-
新建一个 Python 文件,导入以下包:
import os import cv2 import numpy as np from sklearn import preprocessing -
让我们定义一个类来处理与类的标签编码相关的所有任务:
# Class to handle tasks related to label encoding class LabelEncoder(object): -
定义一种对标签进行编码的方法。在输入的训练数据中,标签由单词表示。然而,我们需要数字来训练我们的系统。该方法将定义一个预处理器对象,该对象可以通过维护向前和向后映射来以有组织的方式将单词转换为数字:
# Method to encode labels from words to numbers def encode_labels(self, label_words): self.le = preprocessing.LabelEncoder() self.le.fit(label_words) -
定义一种将单词转换成数字的方法:
# Convert input label from word to number def word_to_num(self, label_word): return int(self.le.transform([label_word])[0]) -
定义一种将数字转换回原始单词的方法:
# Convert input label from number to word def num_to_word(self, label_num): return self.le.inverse_transform([label_num])[0] -
定义从输入文件夹中提取图像和标签的方法:
# Extract images and labels from input path def get_images_and_labels(input_path): label_words = [] -
递归迭代输入文件夹,提取所有图像路径:
# 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]) -
初始化变量:
# Initialize variables images = [] le = LabelEncoder() le.encode_labels(label_words) labels = [] -
解析输入目录进行训练:
# 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) -
以灰度格式读取当前图像:
```py
# Read the image in grayscale format
image = cv2.imread(filepath, 0)
```
- 从文件夹路径中提取标签:
```py
# Extract the label
name = filepath.split('/')[-2]
```
- 对该图像进行人脸检测:
```py
# Perform face detection
faces = faceCascade.detectMultiScale(image, 1.1, 2, minSize=(100,100))
```
- 提取感兴趣区域并与标签编码器一起返回:
```py
# 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
```
- 定义
main功能,定义人脸级联文件的路径:
```py
if __name__=='__main__':
cascade_path = "cascade_files/haarcascade_frontalface_alt.xml"
path_train = 'faces_dataset/train'
path_test = 'faces_dataset/test'
```
- 加载人脸级联文件:
```py
# Load face cascade file
faceCascade = cv2.CascadeClassifier(cascade_path)
```
- 创建局部二进制模式直方图人脸识别器对象:
```py
# Initialize Local Binary Patterns Histogram face recognizer
recognizer = cv2.face.createLBPHFaceRecognizer()
```
- 提取该输入路径的图像、标签和标签编码器:
```py
# Extract images, labels, and label encoder from training dataset
images, labels, le = get_images_and_labels(path_train)
```
- 使用我们提取的数据训练人脸识别器:
```py
# Train the face recognizer
print "\nTraining..."
recognizer.train(images, np.array(labels))
```
- 在未知数据上测试人脸识别器:
```py
# 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)
```
- 加载图像:
```py
# Read the image
predict_image = cv2.imread(filepath, 0)
```
- 使用面部检测器确定面部位置:
```py
# Detect faces
faces = faceCascade.detectMultiScale(predict_image, 1.1,
2, minSize=(100,100))
```
- 对于每个人脸 ROI,运行人脸识别器:
```py
# 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])
```
- 将标签转换为单词:
```py
# Convert to word label
predicted_person = le.num_to_word(predicted_index)
```
- 将文本叠加在输出图像上并显示:
```py
# 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)
```
- 检查用户是否按下了 Esc 键。如果是,打破循环:
```py
c = cv2.waitKey(0)
if c == 27:
stop_flag = True
break
if stop_flag:
break
```
- The full code is in the
face_recognizer.pyfile that's already provided to you for reference. If you run this code, you will get an output window, which displays the predicted outputs for test images. You can press the Space button to keep looping. There are three different people in the test images. The output for the first person looks like the following:

第二个人的输出如下所示:

第三人称的输出如下图所示:

十一、深度神经网络
在本章中,我们将介绍以下食谱:
- 构建感知机
- 构建单层神经网络
- 构建深度神经网络
- 创建矢量量化器
- 为顺序数据分析建立循环神经网络
- 在光学字符识别数据库中可视化字符
- 利用神经网络构建光学字符识别器
简介
我们的大脑真的很擅长识别和认识事物。我们希望机器也能这样做。神经网络是模仿人脑模拟我们学习过程的框架。神经网络旨在从数据中学习并识别潜在的模式。与所有学习算法一样,神经网络处理数字。因此,如果我们想实现任何涉及图像、文本、传感器等的现实世界任务,我们必须先将它们转换成数字形式,然后再将其输入神经网络。我们可以使用神经网络进行分类、聚类、生成和许多其他相关任务。
神经网络由层层的 神经元组成。这些神经元模仿人脑中的生物神经元。每一层基本上都是一组独立的神经元,它们与相邻层的神经元相连。输入层对应于我们提供的输入数据,输出层由我们想要的输出组成。中间的所有图层都称为 隐藏图层。如果我们设计一个具有更多隐藏层的神经网络,那么我们就给它更多的自由来以更高的精度训练自己。
假设我们希望神经网络根据我们的需求对数据进行分类。为了使神经网络相应地工作,我们需要提供标记的训练数据。然后,神经网络将通过优化成本函数来训练自己。这个成本函数是实际标签和来自神经网络的预测标签之间的误差。我们不断迭代,直到误差低于某个阈值。
深层神经网络到底是什么?深度神经网络是由许多隐藏层组成的神经网络。总的来说,这属于深度学习的范畴。这是一个专门研究这些神经网络的领域,这些神经网络由跨多个垂直方向使用的多层组成。
您可以查看神经网络教程,了解更多关于http://pages.cs.wisc.edu/~bolo/shipyard/neural/local.html的信息。在本章中,我们将使用名为神经实验室 的库。在您继续之前,请确保您安装了它。您可以在https://pythonhosted.org/neurolab/install.html找到安装说明。让我们继续看看如何设计和开发这些神经网络。
构建感知器
让我们从感知器开始我们的神经网络冒险。一个感知器是一个执行所有计算的单神经元。这是一个非常简单的模型,但它构成了建立复杂神经网络的基础。这是它的样子:

神经元使用不同的权重组合输入,然后添加一个偏差值来计算输出。这是一个简单的线性方程,将输入值与感知器的输出联系起来。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import neurolab as nl import matplotlib.pyplot as plt -
定义一些输入数据及其对应的标签:
# 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]]) -
让我们绘制这个数据,看看数据点位于哪里:
# Plot input data plt.figure() plt.scatter(data[:,0], data[:,1]) plt.xlabel('X-axis') plt.ylabel('Y-axis') plt.title('Input data') -
让我们定义一个有两个输入的
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) -
让我们训练感知器。时代的数量指定了通过我们的训练数据集的完整次数。
show参数指定我们希望显示进度的频率。lr参数指定感知器的学习速率。它是算法在参数空间中搜索的步长。如果这个值很大,那么算法可能会移动得更快,但它可能会错过最佳值。如果这个很小,那么算法会达到最优值,但是会很慢。所以这是一个交易;因此,我们选择一个值0.01:# Train the perceptron error = perceptron.train(data, labels, epochs=50, show=15, lr=0.01) -
让我们绘制结果,如下所示:
# 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() -
The full code is given in the
perceptron.pyfile that's already provided to you. If you run this code, you will see two figures. The first figure displays the input data:![How to do it…]()
第二个图显示了训练错误进度:
![How to do it…]()
构建单层神经网络
现在我们知道如何创建感知器,让我们创建一个单层神经网络。单层神经网络由单层中的多个神经元组成。总的来说,我们将有一个输入层、一个隐藏层和一个输出层。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt import neurolab as nl -
我们将使用
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:] -
让我们绘制输入数据:
# Plot input data plt.figure() plt.scatter(data[:,0], data[:,1]) plt.xlabel('X-axis') plt.ylabel('Y-axis') plt.title('Input data') -
让我们提取最小值和最大值:
# 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() -
让我们定义一个在隐藏层有两个神经元的单层神经网络:
# 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) -
训练神经网络直到 50 个纪元:
# Train the neural network error = single_layer_net.train(data, labels, epochs=50, show=20, lr=0.01) -
绘制结果,如下所示:
# 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() -
让我们在新的测试数据上测试神经网络:
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]]) -
The full code is in the
single_layer.pyfile that's already provided to you. If you run this code, you will see two figures. The first figure displays the input data:![How to do it…]()
第二个数字显示训练错误进度:
![How to do it…]()
您将在您的终端上看到以下内容,指示输入测试点属于哪里:
[[ 0\. 0.]] [[ 1\. 0.]] [[ 1\. 1.]]您可以根据我们的标签验证输出是否正确。
构建深度神经网络
我们现在准备构建一个深度神经网络。深度神经网络由输入层、许多隐藏层和输出层组成。这看起来如下所示:

上图描绘了具有一个输入层、一个隐藏层和一个输出层的多层神经网络。在深度神经网络中,输入层和输出层之间有许多隐藏层。
怎么做…
-
新建一个 Python 文件,导入以下包:
import neurolab as nl import numpy as np import matplotlib.pyplot as plt -
让我们定义参数来生成一些训练数据:
# Generate training data min_value = -12 max_value = 12 num_datapoints = 90 -
该训练数据将由我们定义的函数组成,该函数将转换这些值。我们希望神经网络能够根据我们提供的输入和输出值,自行学习这一点:
x = np.linspace(min_value, max_value, num_datapoints) y = 2 * np.square(x) + 7 y /= np.linalg.norm(y) -
重塑数组:
data = x.reshape(num_datapoints, 1) labels = y.reshape(num_datapoints, 1) -
绘图输入数据:
# Plot input data plt.figure() plt.scatter(data, labels) plt.xlabel('X-axis') plt.ylabel('Y-axis') plt.title('Input data') -
定义一个具有两个隐藏层的深度神经网络,其中每个隐藏层由 10 个神经元组成:
# Define a multilayer neural network with 2 hidden layers; # Each hidden layer consists of 10 neurons and the output layer # consists of 1 neuron multilayer_net = nl.net.newff([[min_value, max_value]], [10, 10, 1]) -
将训练算法设置为 梯度下降(可在https://spin . atomicobject . com/2014/06/24/gradient-down-linear-relationship)了解更多:
# Change the training algorithm to gradient descent multilayer_net.trainf = nl.train.train_gd -
训练网络:
# Train the network error = multilayer_net.train(data, labels, epochs=800, show=100, goal=0.01) -
在训练数据上运行网络查看性能:
# Predict the output for the training inputs predicted_output = multilayer_net.sim(data) -
绘制训练误差:
```py
# Plot training error
plt.figure()
plt.plot(error)
plt.xlabel('Number of epochs')
plt.ylabel('Error')
plt.title('Training error progress')
```
- 让我们创建一组新的输入,并在这些输入上运行神经网络,看看它的表现:
```py
# Plot predictions
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)
```
- 绘制输出:
```py
plt.figure()
plt.plot(x2, y2, '-', x, y, '.', x, y3, 'p')
plt.title('Ground truth vs predicted output')
plt.show()
```
- The full code is in the
deep_neural_network.pyfile that's already provided to you. If you run this code, you will see three figures. The first figure displays the input data:

第二个图显示了训练错误进度:

第三个图形显示神经网络的输出:

您将在终端上看到以下内容:

创建矢量量化器
也可以使用神经网络进行矢量量化。矢量量化是 N 维版本的“四舍五入”。这在计算机视觉、自然语言处理和一般机器学习的多个领域中非常常用。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt import neurolab as nl -
让我们从
data_vq.txt文件加载输入数据:# Define input data input_file = 'data_vq.txt' input_text = np.loadtxt(input_file) data = input_text[:, 0:2] labels = input_text[:, 2:] -
定义一个两层的学习矢量量化 ( LVQ )神经网络。最后一个参数中的数组指定了每个输出的百分比权重(它们的总和应该是 1):
# Define a neural network with 2 layers: # 10 neurons in input layer and 4 neurons in output layer net = nl.net.newlvq(nl.tool.minmax(data), 10, [0.25, 0.25, 0.25, 0.25]) -
训练 LVQ 神经网络:
# Train the neural network error = net.train(data, labels, epochs=100, goal=-1) -
为测试和可视化创建一个值网格:
# Create the input grid 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) -
评估该网格上的网络:
# Evaluate the input grid of points output_grid = net.sim(input_grid) -
定义我们数据中的四个类:
# Define the 4 classes class1 = data[labels[:,0] == 1] class2 = data[labels[:,1] == 1] class3 = data[labels[:,2] == 1] class4 = data[labels[:,3] == 1] -
定义所有这些类的网格:
# Define grids for all the 4 classes 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] -
绘制输出:
# Plot outputs 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() -
The full code is in the
vector_quantization.pyfile that's already provided to you. If you run this code, you will see the following figure where the space is divided into regions. Each region corresponds to a bucket in the list of vector-quantized regions in the space:

构建用于序列数据分析的循环神经网络
循环神经网络真的很擅长分析序列和时间序列数据。你可以在上了解更多。当我们处理序列和时间序列数据时,我们不能仅仅扩展通用模型。数据中的时间依赖性非常重要,我们需要在模型中考虑这一点。让我们看看如何构建它们。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt import neurolab as nl -
根据输入参数
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)),定义一个函数来创建波形
-
为每个间隔创建不同的振幅,以创建随机波形:
# 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) -
组合数组以创建输出数组。该数据对应于输入,振幅对应于标签:
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 -
定义一个函数,将数据通过训练好的神经网络后绘制输出:
# 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)) -
定义
main功能,从创建样本数据开始:if __name__=='__main__': # Get data num_points = 30 data, amplitude = create_waveform(num_points) -
创建具有两层的循环神经网络:
# Create network with 2 layers net = nl.net.newelm([[-2, 2]], [10, 1], [nl.trans.TanSig(), nl.trans.PureLin()]) -
设置每层的初始化函数:
# 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() -
训练循环神经网络:
# Training the recurrent neural network error = net.train(data, amplitude, epochs=1000, show=100, goal=0.01) -
计算网络输出的训练数据:
```py
# Compute output from network
output = net.sim(data)
```
- 绘图训练错误:
```py
# Plot training results
plt.subplot(211)
plt.plot(error)
plt.xlabel('Number of epochs')
plt.ylabel('Error (MSE)')
```
- 绘制结果:
```py
plt.subplot(212)
plt.plot(amplitude.reshape(num_points * 4))
plt.plot(output.reshape(num_points * 4))
plt.legend(['Ground truth', 'Predicted output'])
```
- 创建一个随机长度的波形,看网络能否预测:
```py
# Testing on unknown data at multiple scales
plt.figure()
plt.subplot(211)
draw_output(net, 74)
plt.xlim([0, 300])
```
- 创建另一个较短长度的波形,看网络能否预测:
```py
plt.subplot(212)
draw_output(net, 54)
plt.xlim([0, 300])
plt.show()
```
- The full code is in the
recurrent_network.pyfile that's already provided to you. If you run this code, you will see two figures. The first figure displays training errors and the performance on the training data:

第二幅图显示了一个经过训练的循环神经网络如何在任意长度的序列上运行:

您将在您的终端上看到以下内容:

在光学字符识别数据库中可视化字符
我们将现在看看如何使用神经网络来执行光学字符识别。这是指识别图像中手写字符的过程。我们将使用http://ai.stanford.edu/~btaskar/ocr提供的数据集。下载后的默认文件名为letter.data。首先,让我们看看如何与数据交互并可视化它。
怎么做…
-
新建一个 Python 文件,导入以下包:
import os import sys import cv2 import numpy as np -
定义输入文件名:
# Load input data input_file = 'letter.data' -
定义可视化参数:
# Define visualization parameters scaling_factor = 10 start_index = 6 end_index = -1 h, w = 16, 8 -
继续循环浏览文件,直到用户按下 Esc 键。将该行拆分为制表符分隔的字符:
# 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]]) -
将数组重塑为所需的形状,调整其大小,并显示:
img = np.reshape(data, (h,w)) img_scaled = cv2.resize(img, None, fx=scaling_factor, fy=scaling_factor) cv2.imshow('Image', img_scaled) -
如果用户按下 Esc ,中断循环:
c = cv2.waitKey() if c == 27: break -
The full code is in the
visualize_characters.pyfile that's already provided to you. If you run this code, you will see a window displaying characters. For example, o looks like the following:![How to do it…]()
字符 i 看起来如下:
![How to do it…]()
利用神经网络构建光学字符识别器
现在我们知道了如何与数据交互,让我们构建一个基于神经网络的光学字符识别系统。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import neurolab as nl -
定义输入文件名:
# Input file input_file = 'letter.data' -
当我们使用处理大量数据的神经网络时,需要花费大量时间进行训练。为了演示如何构建这个系统,我们将只取
20数据点:# Number of datapoints to load from the input file num_datapoints = 20 -
如果您查看数据,您会发现前 20 行中有七个不同的字符。让我们定义它们:
# Distinct characters orig_labels = 'omandig' # Number of distinct characters num_output = len(orig_labels) -
我们将使用 90%的数据进行培训,其余 10%用于测试。定义训练和测试参数:
# Training and testing parameters num_train = int(0.9 * num_datapoints) num_test = num_datapoints - num_train -
数据集文件每行的开始和结束索引:
# Define dataset extraction parameters start_index = 6 end_index = -1 -
创建数据集:
# Creating the dataset data = [] labels = [] with open(input_file, 'r') as f: for line in f.readlines(): # Split the line tabwise list_vals = line.split('\t') -
添加错误检查,查看字符是否在我们的标签列表中:
# If the label is not in our ground truth labels, skip it if list_vals[1] not in orig_labels: continue -
提取标签,并将其附加到主列表中:
# Extract the label and append it to the main list label = np.zeros((num_output, 1)) label[orig_labels.index(list_vals[1])] = 1 labels.append(label) -
提取字符,并将其附加到主列表中:
```py
# Extract the character vector and append it to the main list
cur_char = np.array([float(x) for x in list_vals[start_index:end_index]])
data.append(cur_char)
```
- 一旦我们有足够的数据就退出循环:
```py
# Exit the loop once the required dataset has been loaded
if len(data) >= num_datapoints:
break
```
- 将该数据转换为 NumPy 数组:
```py
# Convert data and labels to numpy arrays
data = np.asfarray(data)
labels = np.array(labels).reshape(num_datapoints, num_output)
```
- 提取我们数据中的维数:
```py
# Extract number of dimensions
num_dims = len(data[0])
```
- 训练神经网络直到
10,000时期:
```py
# Create and train neural network
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)
```
- 预测测试输入的输出:
```py
# Predict the output for test inputs
predicted_output = net.sim(data[num_train:, :])
print "\nTesting on unknown data:"
for i in range(num_test):
print "\nOriginal:", orig_labels[np.argmax(labels[i])]
print "Predicted:", orig_labels[np.argmax(predicted_output[i])]
```
- The full code is in the
ocr.pyfile that's already provided to you. If you run this code, you will see the following on your Terminal at the end of training:

神经网络的输出如下图所示:

十二、可视化数据
在本章中,我们将介绍以下食谱:
- 绘制三维散点图
- 绘制气泡图
- 动画气泡图
- 绘制饼图
- 绘制日期格式的时间序列数据
- 绘制直方图
- 可视化热图
- 动态信号动画
简介
数据可视化是机器学习的重要支柱。它帮助我们制定正确的策略来理解数据。数据的可视化表示帮助我们选择正确的算法。数据可视化的主要目标之一是使用图形和图表清晰地交流。这些图表帮助我们清晰有效地交流信息。
在现实世界中,我们总是会遇到数字数据。我们希望使用图形、线条、点、条等对这些数字数据进行编码,以直观地显示这些数字中包含的信息。这使得复杂的数据分布更加易于理解和使用。这一过程用于各种情况,包括比较分析、跟踪增长、市场分布、民意调查和许多其他情况。
我们使用不同的图表来显示变量之间的模式或关系。我们使用直方图来显示数据的分布。当我们想要查找特定的测量值时,我们使用表格。在本章中,我们将查看各种场景,并讨论在这些情况下我们可以使用什么可视化。
绘制三维散点图
在这个食谱中,我们将学习如何绘制三维散点图并在三维空间中可视化它们。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D -
创建空图形:
# Create the figure fig = plt.figure() ax = fig.add_subplot(111, projection='3d') -
定义我们应该生成的值的数量:
# Define the number of values n = 250 -
创建一个
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) -
使用此函数生成 X、Y 和 Z 值:
# Generate the values x_vals = f(15, 41, n) y_vals = f(-10, 70, n) z_vals = f(-52, -37, n) -
绘制这些值:
# 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() -
The full code is in the
scatter_3d.pyfile that's already provided to you. If you run this code, you will see the following figure:![How to do it…]()
绘制气泡图
我们来看看如何绘制泡泡图。2D 气泡图中每个圆的大小代表该特定点的振幅。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt -
定义我们应该生成的值的数量:
# Define the number of values num_vals = 40 -
为
x和y生成随机值:# Generate random values x = np.random.rand(num_vals) y = np.random.rand(num_vals) -
定义气泡图中每个点的面积值:
# 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 -
定义颜色:
# Generate colors colors = np.random.rand(num_vals) -
绘制这些值:
# Plot the points plt.scatter(x, y, s=area, c=colors, alpha=1.0) plt.show() -
The full code is in the
bubble_plot.pyfile that's already provided to you. If you run this code, you will see the following figure:![How to do it…]()
制作气泡图动画
让我们看看如何制作气泡图的动画。当您想要可视化瞬态和动态数据时,这非常有用。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation -
让我们定义一个
tracker函数来动态更新气泡图:def tracker(cur_num): # Get the current index cur_index = cur_num % num_points -
定义颜色:
# Set the color of the datapoints datapoints['color'][:, 3] = 1.0 -
更新圆的大小:
# Update the size of the circles datapoints['size'] += datapoints['growth'] -
更新集合中最早数据点的位置:
# 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) -
更新散点图的参数:
# 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']) -
定义
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([]) -
定义在任何给定时间点将出现在图上的点数:
# Create and initialize the datapoints in random positions # and with random growth rates. num_points = 20 -
使用随机值定义数据点:
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) -
创建散点图,每帧更新一次:
```py
# 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')
```
- 使用
tracker功能启动动画:
```py
# Start the animation using the 'tracker' function
animation = FuncAnimation(fig, tracker, interval=10)
plt.show()
```
- The full code is in the
dynamic_bubble_plot.pyfile that's already provided to you. If you run this code, you will see the following figure:

绘制饼图
让我们看看如何绘制饼图。当您想要可视化组中一组标签的百分比时,这很有用。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt -
定义标签和值:
# Labels and corresponding values in counter clockwise direction data = {'Apple': 26, 'Mango': 17, 'Pineapple': 21, 'Banana': 29, 'Strawberry': 11} -
定义可视化的颜色:
# List of corresponding colors colors = ['orange', 'lightgreen', 'lightblue', 'gold', 'cyan'] -
定义一个变量,通过将饼图的一部分与其他部分分开来突出显示该部分。如果不想突出显示任何部分,请将所有值设置为
0:# Needed if we want to highlight a section explode = (0, 0, 0, 0, 0) -
绘制饼图。注意,如果使用 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() -
The full code is in the
pie_chart.pyfile that's already provided to you. If you run this code, you will see the following figure:![How to do it…]()
如果您将爆炸阵列更改为(
0, 0.2, 0, 0, 0,则它将突出显示草莓部分。您将看到下图:![How to do it…]()
绘制日期格式的时间序列数据
让我们看看如何使用日期格式绘制时间序列数据。这在随时间可视化股票数据时非常有用。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy import matplotlib.pyplot as plt from matplotlib.mlab import csv2rec import matplotlib.cbook as cbook from matplotlib.ticker import Formatter -
定义一个函数来格式化日期。
__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 -
在任何给定时间提取值,并以以下格式返回:
# Extact 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) -
定义
main功能。我们将使用 matplotlib 中提供的苹果股票报价 CSV 文件:if __name__=='__main__': # CSV file containing the stock quotes input_file = cbook.get_sample_data('aapl.csv', asfileobj=False) -
加载 CSV 文件:
# Load csv file into numpy record array data = csv2rec(input_file) -
提取这些值的子集来绘制它们:
# Take a subset for plotting data = data[-70:] -
创建格式化程序对象,并用日期初始化它:
# Create the date formatter object formatter = DataFormatter(data.date) -
定义 X 轴和 Y 轴:
# X axis x_vals = numpy.arange(len(data)) # Y axis values are the closing stock quotes y_vals = data.close -
绘制数据:
# Plot data fig, ax = plt.subplots() ax.xaxis.set_major_formatter(formatter) ax.plot(x_vals, y_vals, 'o-') fig.autofmt_xdate() plt.show() -
The full code is in the
time_series.pyfile that's already provided to you. If you run this code, you will see the following figure:

绘制直方图
让我们看看如何在这个食谱中绘制直方图。我们将比较两组数据,并建立一个比较直方图。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt -
我们来对比一下这个食谱中苹果和橘子的产量。让我们定义一些值:
# Input data apples = [30, 25, 22, 36, 21, 29] oranges = [24, 33, 19, 27, 35, 20] # Number of groups num_groups = len(apples) -
创建图形并定义其参数:
# 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 -
绘制直方图:
# 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') -
设置绘图参数:
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() -
The full code is in the
histogram.pyfile that's already provided to you. If you run this code, you will see the following figure:![How to do it…]()
可视化热图
让我们看看如何在这个食谱中可视化热图。这是数据的图形表示,其中两个组是逐点关联的。矩阵中包含的单个值在图中表示为颜色值。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt -
定义两组:
# Define the two groups group1 = ['France', 'Italy', 'Spain', 'Portugal', 'Germany'] group2 = ['Japan', 'China', 'Brazil', 'Russia', 'Australia'] -
生成随机 2D 矩阵:
# Generate some random values data = np.random.rand(5, 5) -
创建图形:
# Create a figure fig, ax = plt.subplots() -
创建热图:
# Create the heat map heatmap = ax.pcolor(data, cmap=plt.cm.gray) -
绘制这些值:
# 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() -
The full code is in the
heatmap.pyfile that's provided to you. If you run this code, you will see the following figure:![How to do it…]()
动态信号动画
当我们可视化实时信号时,很高兴看到波形是如何建立的。在这个食谱中,我们将看到如何制作动态信号的动画,并在实时遇到它们时可视化它们。
怎么做…
-
新建一个 Python 文件,导入以下包:
import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation -
创建一个生成阻尼正弦信号的函数:
# 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 -
定义一个
initializer函数来初始化图的参数:# Initializer function def initializer(): peak_val = 1.0 buffer_val = 0.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 -
定义一个函数来绘制值:
def draw(data): # update the data t, signal = data x_vals.append(t) y_vals.append(signal) x_min, x_max = ax.get_xlim() -
如果这些值超过了当前的 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 -
定义
main功能:if __name__=='__main__': # Create the figure fig, ax = plt.subplots() ax.grid() -
提取行:
# Extract the line line, = ax.plot([], [], lw=1.5) -
创建变量并将其初始化为空列表:
# Create the variables x_vals, y_vals = [], [] -
使用动画师对象
```py
# Define the animator object
animator = animation.FuncAnimation(fig, draw, generate_data,
blit=False, interval=10, repeat=False, init_func=initializer)
plt.show()
```
定义并开始动画
- The full code is in the
moving_wave_variable.pyfile that's already provided to you. If you run this code, you will see the following figure:




















































































浙公网安备 33010602011771号