Python 数据分析第三版(四)
原文:
annas-archive.org/md5/74a7b24994c40ad3a90c290c07b529df译者:飞龙
第十二章:无监督学习 - PCA 和聚类
无监督学习是机器学习中最重要的分支之一。它使我们能够在没有目标标签的情况下进行预测。在无监督学习中,模型仅通过特征进行学习,因为数据集没有目标标签列。大多数机器学习问题从某些能够自动化过程的事物开始。例如,当你想要开发一个预测模型来检测糖尿病患者时,你需要为数据集中的每个患者设置目标标签。在初期阶段,为任何机器学习问题安排目标标签并非易事,因为这需要改变业务流程来获得标签,无论是通过手动内部标注还是再次收集带标签的数据。
在本章中,我们的重点是学习无监督学习技术,这些技术可以处理没有目标标签的情况。我们将特别介绍降维技术和聚类技术。当我们有大量特征时,降维技术将被使用,以减少这些特征的数量。这将减少模型复杂性和训练成本,因为这意味着我们可以仅通过少量特征就能实现我们想要的结果。
聚类技术根据相似性在数据中找到组。这些组本质上代表了无监督分类。在聚类中,特征观察的类或标签是以无监督的方式找到的。聚类在各种业务操作中非常有用,例如认知搜索、推荐、细分和文档聚类。
本章的主题如下:
-
无监督学习
-
降低数据的维度
-
主成分分析
-
聚类
-
使用 K-means 聚类对数据进行划分
-
层次聚类
-
DBSCAN 聚类
-
谱聚类
-
评估聚类性能
技术要求
本章有以下技术要求:
-
你可以通过以下 GitHub 链接找到代码和数据集:
github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter11。 -
所有代码块都可以在
ch11.ipynb文件中找到。 -
本章仅使用一个 CSV 文件(
diabetes.csv)进行实践。 -
在本章中,我们将使用 pandas 和 scikit-learn Python 库。
无监督学习
无监督学习意味着通过观察学习,而不是通过示例学习。这种学习类型适用于无标签的数据。降维和聚类就是这种学习的例子。降维用于将大量特征减少到只有少数几个特征,但能产生相同的结果。有几种方法可以减少数据的维度,例如主成分分析(PCA)、t-SNE、小波变换和特征子集选择。
术语“聚类”指的是一组相似的项目,它们彼此密切相关。聚类是一种生成相似单元或项目组的方法。此相似性是基于项目的某些特征或特性计算的。我们可以说,聚类是一组数据点,它们与其聚类中的其他数据点相似,并且与其他聚类的数据点不相似。聚类具有许多应用,例如搜索文档、业务智能、信息安全和推荐系统:
在上图中,我们可以看到聚类如何将数据记录或观察结果分成少数几组,而降维则减少了特征或属性的数量。让我们在接下来的部分详细讨论每个主题。
减少数据的维度
减少维度,即降维,意味着将大量属性或列(特征)缩减为较少数量的属性。该技术的主要目标是获得最佳的特征数用于分类、回归和其他无监督方法。在机器学习中,我们面临一个称为维度灾难的问题。这意味着有大量属性或特征。这意味着更多的数据,导致复杂的模型和过拟合问题。
降低数据的维度有助于应对维度灾难。它可以线性和非线性地转换数据。线性转换技术包括 PCA、线性判别分析和因子分析。非线性转换包括 t-SNE、Hessian 特征映射、谱嵌入和等距特征映射等技术。降维提供以下好处:
-
过滤冗余和不重要的特征。
-
减少模型复杂性,使用较少维度的数据。
-
减少模型生成的内存和计算成本。
-
它可视化高维数据。
在接下来的部分中,我们将专注于一种重要且流行的降维技术之一,PCA。
PCA
在机器学习中,认为拥有大量数据意味着拥有预测模型的高质量,但大型数据集也带来了更高维度的挑战(或维度灾难)。由于属性数量众多,这导致了预测模型复杂度的增加。PCA 是最常用的降维方法,帮助我们识别原始数据集中的模式和相关性,将其转换为一个低维数据集,同时不丢失信息。
PCA 的主要概念是发现原始数据集中属性之间未见的关系和关联。高度相关的属性是如此相似,以至于它们是冗余的。因此,PCA 去除了这些冗余的属性。例如,如果我们的数据中有 200 个属性或列,那么面对这么多属性时,我们将难以继续处理。在这种情况下,我们需要将这些属性的数量减少到 10 或 20 个变量。PCA 的另一个目标是减少维度,同时不影响重要信息。对于p维数据,PCA 的方程可以写成如下:
主成分是所有属性的加权和。这里,是原始数据集中的属性,
是属性的权重。
让我们举个例子。假设我们将一个城市的街道作为属性,并且假设你想参观这个城市。那么问题是,你会参观多少条街道?显然,你会想参观城市中的热门或主要街道,假设这些街道是 50 条中的 10 条。这 10 条街道将为你提供对这座城市的最佳了解。这些街道就是主成分,因为它们解释了数据(城市街道)中的大部分方差。
执行 PCA
让我们从头开始在 Python 中执行 PCA:
-
计算给定数据集的相关或协方差矩阵。
-
求解相关或协方差矩阵的特征值和特征向量。
-
将特征向量矩阵与原始数据集相乘,你将得到主成分矩阵。
让我们从头开始实现 PCA:
- 我们将首先导入库并定义数据集:
# Import numpy
import numpy as np
# Import linear algebra module
from scipy import linalg as la
# Create dataset
data=np.array([[7., 4., 3.],
[4., 1., 8.],
[6., 3., 5.],
[8., 6., 1.],
[8., 5., 7.],
[7., 2., 9.],
[5., 3., 3.],
[9., 5., 8.],
[7., 4., 5.],
[8., 2., 2.]])
- 计算协方差矩阵:
# Calculate the covariance matrix
# Center your data
data -= data.mean(axis=0)
cov = np.cov(data, rowvar=False)
- 计算协方差矩阵的特征值和特征向量:
# Calculate eigenvalues and eigenvector of the covariance matrix
evals, evecs = la.eig(cov)
- 将原始数据矩阵与特征向量矩阵相乘:
# Multiply the original data matrix with Eigenvector matrix.
# Sort the Eigen values and vector and select components
num_components=2
sorted_key = np.argsort(evals)[::-1][:num_components]
evals, evecs = evals[sorted_key], evecs[:, sorted_key]
print("Eigenvalues:", evals)
print("Eigenvector:", evecs)
print("Sorted and Selected Eigen Values:", evals)
print("Sorted and Selected Eigen Vector:", evecs)
# Multiply original data and Eigen vector
principal_components=np.dot(data,evecs)
print("Principal Components:", principal_components)
这将产生如下输出:
Eigenvalues: [0.74992815+0.j 3.67612927+0.j 8.27394258+0.j]
Eigenvector: [[-0.70172743 0.69903712 -0.1375708 ]
[ 0.70745703 0.66088917 -0.25045969]
[ 0.08416157 0.27307986 0.95830278]]
Sorted and Selected Eigen Values: [8.27394258+0.j 3.67612927+0.j]
Sorted and Selected Eigen Vector: [[-0.1375708 0.69903712]
[-0.25045969 0.66088917]
[ 0.95830278 0.27307986]]
Principal Components: [[-2.15142276 -0.17311941]
[ 3.80418259 -2.88749898]
[ 0.15321328 -0.98688598]
[-4.7065185 1.30153634]
[ 1.29375788 2.27912632]
[ 4.0993133 0.1435814 ]
[-1.62582148 -2.23208282]
[ 2.11448986 3.2512433 ]
[-0.2348172 0.37304031]
[-2.74637697 -1.06894049]]
在这里,我们从头开始计算了主成分矩阵。首先,我们对数据进行了中心化并计算了协方差矩阵。计算协方差矩阵后,我们求得了特征值和特征向量。最后,我们选择了两个主成分(主成分的数量应等于特征值大于 1 的数量),并将原始数据与排序和选择的特征向量相乘。我们也可以使用 scikit-learn 库来执行 PCA。
让我们在 Python 中使用 scikit-learn 进行 PCA:
# Import pandas and PCA
import pandas as pd
# Import principal component analysis
from sklearn.decomposition import PCA
# Create dataset
data=np.array([[7., 4., 3.],
[4., 1., 8.],
[6., 3., 5.],
[8., 6., 1.],
[8., 5., 7.],
[7., 2., 9.],
[5., 3., 3.],
[9., 5., 8.],
[7., 4., 5.],
[8., 2., 2.]])
# Create and fit_transformed PCA Model
pca_model = PCA(n_components=2)
components = pca_model.fit_transform(data)
components_df = pd.DataFrame(data = components,
columns = ['principal_component_1', 'principal_component_2'])
print(components_df)
这将产生如下输出:
principal_component_1 principal_component_2
0 2.151423 -0.173119
1 -3.804183 -2.887499
2 -0.153213 -0.986886
3 4.706518 1.301536
4 -1.293758 2.279126
5 -4.099313 0.143581
6 1.625821 -2.232083
7 -2.114490 3.251243
8 0.234817 0.373040
9 2.746377 -1.068940
在上面的代码中,我们使用 scikit-learn 库执行了 PCA。首先,我们创建了数据集并实例化了 PCA 对象。之后,我们执行了fit_transform()并生成了主成分。
以上就是 PCA 的内容。现在是时候了解另一个无监督学习的概念——聚类。
聚类
聚类是将相似的项目进行分组。将相似的产品分组、将相似的文章或文档分组、以及将相似的客户进行市场细分,都是聚类的例子。聚类的核心原理是最小化簇内距离并最大化簇间距离。簇内距离是同一组内数据项之间的距离,而簇间距离是不同组之间的距离。由于数据点没有标签,因此聚类是一个无监督问题。聚类有多种方法,每种方法使用不同的方式将数据点分组。下图展示了如何使用聚类将数据观察值分组:
当我们将相似的数据点进行合并时,出现的问题是如何找到两个数据点之间的相似性,从而将相似的数据对象归为同一个簇。为了衡量数据点之间的相似性或差异性,我们可以使用距离度量,如欧几里得距离、曼哈顿距离和闵可夫斯基距离:
在这里,距离公式计算的是两个 k 维向量 x[i]和 y[i]之间的距离。
现在我们知道了什么是聚类,但最重要的问题是,在对数据进行分组时,应该考虑多少个簇?这是大多数聚类算法面临的最大挑战。有很多方法可以决定簇的数量。让我们在接下来的章节中讨论这些方法。
查找簇数
在本节中,我们将重点讨论聚类算法中的最基本问题,即发现数据集中的簇数——这个问题没有明确的答案。然而,并非所有的聚类算法都需要预定义簇数。在层次聚类和 DBSCAN 聚类中,不需要定义簇数,但在 k-means、k-medoids 和谱聚类中,我们需要定义簇数。选择簇数的正确值是非常棘手的,因此我们来看看几种确定最佳簇数的方法:
-
肘部法则
-
轮廓法
让我们详细了解这些方法。
肘部法则
肘部法则是一种广为人知的确定最佳簇数的方法。在这种方法中,我们关注不同簇数下的方差百分比。该方法的核心概念是选择一个簇数,使得再增加一个簇时,方差不会发生巨大的变化。我们可以使用簇数绘制一个图,表示簇内平方和,以找到最佳值。簇内平方和也被称为簇内平方和(WCSS)或惯性:
如图所示,在 k = 3 时,图形开始显著变平,因此我们会选择 3 作为聚类数。
让我们使用肘部法则在 Python 中找到最佳聚类数:
# import pandas
import pandas as pd
# import matplotlib
import matplotlib.pyplot as plt
# import K-means
from sklearn.cluster import KMeans
# Create a DataFrame
data=pd.DataFrame({"X":[12,15,18,10,8,9,12,20],
"Y":[6,16,17,8,7,6,9,18]})
wcss_list = []
# Run a loop for different value of number of cluster
for i in range(1, 6):
# Create and fit the KMeans model
kmeans_model = KMeans(n_clusters = i, random_state = 123)
kmeans_model.fit(data)
# Add the WCSS or inertia of the clusters to the score_list
wcss_list.append(kmeans_model.inertia_)
# Plot the inertia(WCSS) and number of clusters
plt.plot(range(1, 6), wcss_list, marker='*')
# set title of the plot
plt.title('Selecting Optimum Number of Clusters using Elbow Method')
# Set x-axis label
plt.xlabel('Number of Clusters K')
# Set y-axis label
plt.ylabel('Within-Cluster Sum of the Squares(Inertia)')
# Display plot
plt.show()
这将产生以下输出:
在前面的示例中,我们创建了一个包含 X 和 Y 两列的 DataFrame。我们使用 K-means 生成了聚类并计算了 WCSS。之后,我们绘制了聚类数量和惯性图。如图所示,在 k = 2 时,图形开始显著变平,因此我们会选择 2 作为最佳聚类数。
轮廓法
轮廓法评估并验证聚类数据。它找出每个数据点的分类效果。轮廓分数的图表帮助我们可视化和解释数据点在各自聚类内部的紧密程度,以及与其他聚类的分离程度。它帮助我们评估聚类的数量。其得分范围从 -1 到 +1。正值表示聚类分离良好,负值表示数据点被错误地分配。值越正,数据点与最近的聚类的距离越远;零值表示数据点位于两个聚类之间的分隔线上。让我们看看轮廓分数的公式:
a[i] 是第 i 个数据点与聚类内其他点的平均距离。
b[i] 是第 i 个数据点与其他聚类点的平均距离。
这意味着我们可以轻松地说 S(i) 的值将在[-1, 1]之间。所以,S(i) 要接近 1,a[i] 必须相较于 b[i,即]e,a[i] << b[i]。
让我们使用轮廓分数在 Python 中找到最佳聚类数:
# import pandas
import pandas as pd
# import matplotlib for data visualization
import matplotlib.pyplot as plt
# import k-means for performing clustering
from sklearn.cluster import KMeans
# import silhouette score
from sklearn.metrics import silhouette_score
# Create a DataFrame
data=pd.DataFrame({"X":[12,15,18,10,8,9,12,20],
"Y":[6,16,17,8,7,6,9,18]})
score_list = []
# Run a loop for different value of number of cluster
for i in range(2, 6):
# Create and fit the KMeans model
kmeans_model = KMeans(n_clusters = i, random_state = 123)
kmeans_model.fit(data)
# Make predictions
pred=kmeans_model.predict(data)
# Calculate the Silhouette Score
score = silhouette_score (data, pred, metric='euclidean')
# Add the Silhouette score of the clusters to the score_list
score_list.append(score)
# Plot the Silhouette score and number of cluster
plt.bar(range(2, 6), score_list)
# Set title of the plot
plt.title('Silhouette Score Plot')
# Set x-axis label
plt.xlabel('Number of Clusters K')
# Set y-axis label
plt.ylabel('Silhouette Scores')
# Display plot
plt.show()
这将产生以下输出:
在前面的示例中,我们创建了一个包含 X 和 Y 两列的 DataFrame。我们使用 K-means 在创建的 DataFrame 上生成了不同数量的聚类并计算了轮廓分数。之后,我们使用条形图绘制了聚类数量和轮廓分数。如图所示,在 k = 2 时,轮廓分数达到最高值,因此我们会选择 2 个聚类。接下来,我们进入 k-means 聚类技术。
使用 k-means 聚类对数据进行分区
k-means 是最简单、最流行且最著名的聚类算法之一。它是一种划分聚类方法。它通过定义一个基于给定聚类数目的随机初始聚类中心来划分输入数据。在下一次迭代中,它使用欧氏距离将数据项与最近的聚类中心关联。在该算法中,初始聚类中心可以手动选择或随机选择。k-means 以数据和聚类数目作为输入,执行以下步骤:
-
选择k个随机数据项作为聚类的初始中心。
-
将数据项分配到最近的聚类中心。
-
通过计算其他聚类项的平均值来选择新的聚类中心。
-
重复步骤 2 和 3,直到聚类不再发生变化。
该算法旨在最小化平方误差之和:
k-means 是同类算法中最快且最稳健的算法之一。它在数据集具有明显且分离的数据项时效果最佳。它生成球形聚类。k-means 在开始时需要聚类数作为输入。如果数据项高度重叠,它的表现不佳。它捕捉到的是平方误差函数的局部最优解。它在处理噪声和非线性数据时表现不佳。它也不适用于非球形聚类。让我们使用 k-means 聚类创建一个聚类模型:
# import pandas
import pandas as pd
# import matplotlib for data visualization
import matplotlib.pyplot as plt
# Import K-means
from sklearn.cluster import KMeans
# Create a DataFrame
data=pd.DataFrame({"X":[12,15,18,10,8,9,12,20],
"Y":[6,16,17,8,7,6,9,18]})
# Define number of clusters
num_clusters = 2
# Create and fit the KMeans model
km = KMeans(n_clusters=num_clusters)
km.fit(data)
# Predict the target variable
pred=km.predict(data)
# Plot the Clusters
plt.scatter(data.X,data.Y,c=pred, marker="o", cmap="bwr_r")
# Set title of the plot
plt.title('K-Means Clustering')
# Set x-axis label
plt.xlabel('X-Axis Values')
# Set y-axis label
plt.ylabel('Y-Axis Values')
# Display the plot
plt.show()
这将产生以下输出:
在前面的代码示例中,我们导入了KMeans类并创建了它的对象或模型。该模型将在数据集(无标签)上进行拟合。训练后,模型已经准备好使用predict()方法进行预测。在预测结果后,我们使用散点图绘制了聚类结果。在本节中,我们已经了解了 k-means 的工作原理以及如何使用 scikit-learn 库实现它。在下一节中,我们将介绍层次聚类。
层次聚类
层次聚类根据不同的层次结构级别对数据项进行分组。它使用自顶向下或自底向上的策略,根据不同的层次结构级别将数据项组合成组。根据所使用的策略,层次聚类可以分为两种类型——凝聚型或分裂型:
-
聚合型层次聚类是最广泛使用的层次聚类技术。它基于相似性将相似的数据项分组成层次结构。这种方法也叫做聚合嵌套(AGNES)。该算法从将每个数据项视为单独的聚类开始,并根据相似性合并聚类。它迭代地收集小的聚类,并将其合并为一个大的聚类。该算法的结果以树形结构呈现。它以自底向上的方式工作;也就是说,每个项最初被视为一个单独的元素聚类,在算法的每次迭代中,两个最相似的聚类会被合并,形成一个更大的聚类。
-
分裂式层次聚类是一种自上而下的策略算法。它也被称为分裂分析(DIANA)。它从将所有数据项视为一个大的聚类开始,并递归地进行划分。在每次迭代中,聚类被分成两个非相似或异质的子聚类:
为了决定哪些聚类应该被组合或拆分,我们使用各种距离和链接标准,例如单链、全链、平均链和质心链。这些标准决定了聚类的形状。两种类型的层次聚类(聚合型和分裂型层次聚类)都需要预定义的聚类数量或距离阈值作为输入,以终止递归过程。由于很难确定距离阈值,因此最简单的选项是通过树状图检查聚类数量。树状图帮助我们理解层次聚类的过程。让我们来看一下如何使用scipy库创建树状图:
# import pandas
import pandas as pd
# import matplotlib for data visualization
import matplotlib.pyplot as plt
# Import dendrogram
from scipy.cluster.hierarchy import dendrogram
from scipy.cluster.hierarchy import linkage
# Create a DataFrame
data=pd.DataFrame({"X":[12,15,18,10,8,9,12,20],
"Y":[6,16,17,8,7,6,9,18]})
# create dendrogram using ward linkage
dendrogram_plot = dendrogram(linkage(data, method = 'ward'))
# Set title of the plot
plt.title('Hierarchical Clustering: Dendrogram')
# Set x-axis label
plt.xlabel('Data Items')
# Set y-axis label
plt.ylabel('Distance')
# Display the plot
plt.show()
这将产生以下输出:
在前面的代码示例中,我们创建了数据集,并使用 ward 链接生成了树状图。对于树状图,我们使用了scipy.cluster.hierarchy模块。为了设置图表标题和轴标签,我们使用了matplotlib。为了选择聚类数量,我们需要画一条横线,使其不与聚类相交,并计算垂直线的数量以找出聚类数。让我们使用聚合型聚类创建一个聚类模型:
# import pandas
import pandas as pd
# import matplotlib for data visualization
import matplotlib.pyplot as plt
# Import Agglomerative Clustering
from sklearn.cluster import AgglomerativeClustering
# Create a DataFrame
data=pd.DataFrame({"X":[12,15,18,10,8,9,12,20],
"Y":[6,16,17,8,7,6,9,18]})
# Specify number of clusters
num_clusters = 2
# Create agglomerative clustering model
ac = AgglomerativeClustering(n_clusters = num_clusters, linkage='ward')
# Fit the Agglomerative Clustering model
ac.fit(data)
# Predict the target variable
pred=ac.labels_
# Plot the Clusters
plt.scatter(data.X,data.Y,c=pred, marker="o")
# Set title of the plot
plt.title('Agglomerative Clustering')
# Set x-axis label
plt.xlabel('X-Axis Values')
# Set y-axis label
plt.ylabel('Y-Axis Values')
# Display the plot
plt.show()
这将产生以下输出:
在前面的代码示例中,我们导入了AgglomerativeClustering类,并创建了它的对象或模型。该模型将适应没有标签的数据集。训练后,模型准备好使用predict()方法进行预测。预测结果之后,我们使用散点图绘制了聚类结果。在这一部分中,我们已经看到层次聚类的工作原理及其使用scipy和 scikit-learn 库的实现。在下一部分中,我们将介绍基于密度的聚类。
DBSCAN 聚类
分区聚类方法,如 k-means,和层次聚类方法,如凝聚聚类,适用于发现球形或凸形的簇。这些算法对噪声或离群点较为敏感,并且适用于分离良好的簇:
直观地说,我们可以认为基于密度的聚类方法最类似于我们人类可能本能地对物品进行分组的方式。在所有前面的图中,我们可以通过物品的密度快速看到不同组或簇的数量。
基于密度的空间聚类法(带噪声) (DBSCAN) 基于群组和噪声的思想。其核心思想是每个群组或簇中的每个数据项在给定半径内都有最少数量的数据项。
DBSCAN 的主要目标是发现密集区域,该区域可以通过最少数量的对象(minPoints)和给定半径(eps)来计算。DBSCAN 能够生成随机形状的簇,并处理数据集中的噪声。此外,DBSCAN 不需要输入簇的数量。它会自动识别数据中的簇数。
让我们使用 Python 中的 DBSCAN 聚类方法来创建一个聚类模型:
# import pandas
import pandas as pd
# import matplotlib for data visualization
import matplotlib.pyplot as plt
# Import DBSCAN clustering model
from sklearn.cluster import DBSCAN
# import make_moons dataset
from sklearn.datasets import make_moons
# Generate some random moon data
features, label = make_moons(n_samples = 2000)
# Create DBSCAN clustering model
db = DBSCAN()
# Fit the Spectral Clustering model
db.fit(features)
# Predict the target variable
pred_label=db.labels_
# Plot the Clusters
plt.scatter(features[:, 0], features[:, 1], c=pred_label,
marker="o",cmap="bwr_r")
# Set title of the plot
plt.title('DBSCAN Clustering')
# Set x-axis label
plt.xlabel('X-Axis Values')
# Set y-axis label
plt.ylabel('Y-Axis Values')
# Display the plot
plt.show()
这将生成以下输出:
首先,我们导入 DBSCAN 类并创建月亮数据集。之后,我们创建 DBSCAN 模型并将其拟合到数据集上。DBSCAN 不需要簇的数量。训练完成后,模型可以使用 predict() 方法进行预测。预测结果后,我们通过散点图绘制了聚类结果。在本节中,我们了解了 DBSCAN 聚类的工作原理及其在 scikit-learn 库中的实现。下一节中,我们将介绍谱聚类技术。
谱聚类
谱聚类是一种利用相似度矩阵谱的方法。矩阵的谱代表其特征值的集合,而相似度矩阵由每个数据点之间的相似度分数组成。它在聚类前进行数据的降维。换句话说,谱聚类创建一个数据点图,这些点被映射到较低的维度,并分成不同的簇。
相似度矩阵将数据转换,以克服分布中缺乏凸性的难题。对于任何数据集,数据点可能是n维的,且这里可能有m个数据点。从这m个点中,我们可以创建一个图,其中点是节点,边则是节点之间的相似度加权。定义相似度的一种常见方式是使用高斯核,它是欧几里得距离的非线性函数:
这个函数的距离范围从 0 到 1。它被限制在零和一之间是一个很好的性质。欧几里得距离中的绝对距离(它可以是任何值)可能导致不稳定性和建模困难。你可以将高斯核视为欧几里得距离的归一化函数。
在获得图形后,创建一个邻接矩阵,并在矩阵的每个单元格中填入边的权重 。这是一个对称矩阵。我们将邻接矩阵称为 A。我们还可以创建一个“度数”对角矩阵 D,在每个
元素中填入与节点 i 相连的所有边的权重之和。我们将这个矩阵称为 D。对于一个给定的图 G,假设它有 n 个顶点,它的 n×n 拉普拉斯矩阵可以定义如下:
这里 D 是度数矩阵,A 是图的邻接矩阵。
现在我们有了图(G)的拉普拉斯矩阵。我们可以计算矩阵的特征向量谱。如果我们取 k 个最不显著的特征向量,就可以得到一个 k 维度的表示。最不显著的特征向量与最小的特征值相关。每个特征向量提供有关图的连通性的信息。
谱聚类的思想是使用这 k 个特征向量来对点进行聚类。所以,你取 k 个最不显著的特征向量,你就有了 k 维度下的 m 个点。然后,你运行一个聚类算法,比如 k-means,最后得到结果。在谱聚类中,这个 k 与高斯核 k-means 密切相关。你也可以将其看作是一种聚类方法,其中你的点被投影到一个无限维的空间中,在那里进行聚类,然后你使用这些结果作为聚类点的结果。
当 k-means 聚类效果不好时,通常使用谱聚类,因为原始空间中的簇无法线性区分。我们还可以尝试其他聚类方法,如层次聚类或基于密度的聚类,来解决这个问题。让我们在 Python 中使用谱聚类创建一个聚类模型:
# import pandas
import pandas as pd
# import matplotlib for data visualization
import matplotlib.pyplot as plt
# Import Spectral Clustering
from sklearn.cluster import SpectralClustering
# Create a DataFrame
data=pd.DataFrame({"X":[12,15,18,10,8,9,12,20],
"Y":[6,16,17,8,7,6,9,18]})
# Specify number of clusters
num_clusters = 2
# Create Spectral Clustering model
sc=SpectralClustering(num_clusters, affinity='rbf', n_init=100, assign_labels='discretize')
# Fit the Spectral Clustering model
sc.fit(data)
# Predict the target variable
pred=sc.labels_
# Plot the Clusters
plt.scatter(data.X,data.Y,c=pred, marker="o")
# Set title of the plot
plt.title('Spectral Clustering')
# Set x-axis label
plt.xlabel('X-Axis Values')
# Set y-axis label
plt.ylabel('Y-Axis Values')
# Display the plot
plt.show()
这会产生以下输出:
在前面的代码示例中,我们导入了 SpectralClustering 类,并使用 pandas 创建了一个虚拟数据集。之后,我们创建了模型并将其拟合到数据集上。训练完成后,模型已经准备好使用 predict() 方法进行预测。在本节中,我们已经了解了谱聚类的工作原理及其在 scikit-learn 库中的实现。在下一节中,我们将学习如何评估聚类算法的性能。
评估聚类性能
评估聚类性能是评估聚类算法在给定数据集上强度的必要步骤。在无监督环境中评估性能并非易事,但文献中有许多可用的方法。我们可以将这些方法分为两大类:内部评估和外部评估。让我们详细了解这两类评估方法。
内部性能评估
在内部性能评估中,聚类是仅基于特征数据进行评估的。这种方法不使用任何目标标签信息。这些评估指标为生成良好分离聚类的聚类方法分配更高的分数。在这里,高分数并不保证有效的聚类结果。
内部性能评估有助于我们比较多个聚类算法,但这并不意味着得分更高的算法会比其他算法生成更好的结果。以下内部性能评估指标可以用来估计生成聚类的质量:
Davies-Bouldin 指数
Davies-Bouldin 指数 (BDI) 是聚类内距离与聚类间距离的比值。较低的 DBI 值意味着更好的聚类结果。计算公式如下:
在此,以下公式适用:
-
n:聚类的数量
-
c[i]:聚类 i 的质心
-
σ[i]:聚类内距离或所有聚类项到质心 c[i]的平均距离
-
d(c[i], c[j]):两个聚类质心 c[i]和 c[j]之间的聚类间距离
轮廓系数
轮廓系数用于找出一个项目与其自身聚类项目和其他最近聚类之间的相似度。它也用于确定聚类的数量,如我们在本章其他部分看到的那样。较高的轮廓系数意味着更好的聚类结果。计算公式如下:
a[i] 是第 i 个数据点与聚类内其他点的平均距离。
b[i] 是第 i 个数据点与其他聚类点之间的平均距离。
所以,我们可以说 S(i) 的值会在[-1, 1]之间。为了让 S(i) 接近 1,a[i]必须比 b[i]小很多,即 a[i] << b[i]。
外部性能评估
在外部性能评估中,生成的聚类是通过与聚类过程中未使用的实际标签进行评估的。这类似于监督学习评估过程;也就是说,我们可以使用相同的混淆矩阵来评估性能。以下外部评估指标用于评估生成聚类的质量。
Rand 得分
Rand 得分表示一个聚类与基准分类的相似度,并计算正确决策的百分比。较低的值更可取,因为这表示聚类更加分明。计算公式如下:
这里,以下内容适用:
-
TP:真阳性的总数
-
TN:真阴性的总数
-
FP:假阳性的总数
-
FN:假阴性的总数
Jaccard 分数
Jaccard 分数计算两个数据集之间的相似性。它的取值范围是 0 到 1,1 表示数据集完全相同,0 表示数据集没有共同元素。较低的值更可取,因为它表示簇之间的区别。这可以按以下方式计算:
这里 A 和 B 是两个数据集。
F-度量或 F1 分数
F-度量是精度和召回率值的调和平均数。它衡量了聚类算法的精度和鲁棒性。它还试图通过β的值平衡假阴性的参与。可以按以下方式计算:
这里β是非负值。β=1 表示精度和召回率赋予相等的权重,β=0.5 表示精度的权重是召回率的两倍,而β=0 则表示不考虑召回率。
Fowlkes-Mallows 分数
Fowlkes-Mallows 分数是精度和召回率的几何平均数。较高的值表示簇之间相似。可以按以下方式计算:
让我们使用 k-means 聚类创建一个聚类模型,并使用 Python 中的内部和外部评估指标评估性能,使用 Pima Indian Diabetes 数据集(https://github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/blob/master/Chapter11/diabetes.csv):
# Import libraries
import pandas as pd
# read the dataset
diabetes = pd.read_csv("diabetes.csv")
# Show top 5-records
diabetes.head()
这将得到以下输出:
首先,我们需要导入 pandas 并读取数据集。在前面的例子中,我们读取的是 Pima Indian Diabetes 数据集:
# split dataset in two parts: feature set and target label
feature_set = ['pregnant', 'insulin', 'bmi', 'age','glucose','bp','pedigree']
features = diabetes[feature_set]
target = diabetes.label
在加载数据集后,我们需要将数据集分为依赖/标签列(目标)和独立/特征列(feature_set)。之后,数据集将被拆分为训练集和测试集。现在,依赖列和独立列会通过train_test_split()拆分成训练集和测试集(feature_train、feature_test、target_train 和 target_test)。我们来拆分数据集为训练集和测试集:
# partition data into training and testing set
from sklearn.model_selection import train_test_split
feature_train, feature_test, target_train, target_test = train_test_split(features, target, test_size=0.3, random_state=1)
这里,train_test_split()接收依赖和独立的 DataFrame、test_size和random_state。其中,test_size决定了训练集和测试集的比例(test_size值为0.3意味着 30%的数据将分配给测试集,其余 70%将作为训练集),而random_state则作为种子值,用于每次生成相同的数据分割。如果random_state为None,那么每次会随机分割记录,这将导致不同的性能评估结果:
# Import K-means Clustering
from sklearn.cluster import KMeans
# Import metrics module for performance evaluation
from sklearn.metrics import davies_bouldin_score
from sklearn.metrics import silhouette_score
from sklearn.metrics import adjusted_rand_score
from sklearn.metrics import jaccard_score
from sklearn.metrics import f1_score
from sklearn.metrics import fowlkes_mallows_score
# Specify the number of clusters
num_clusters = 2
# Create and fit the KMeans model
km = KMeans(n_clusters=num_clusters)
km.fit(feature_train)
# Predict the target variable
predictions = km.predict(feature_test)
# Calculate internal performance evaluation measures
print("Davies-Bouldin Index:", davies_bouldin_score(feature_test, predictions))
print("Silhouette Coefficient:", silhouette_score(feature_test, predictions))
# Calculate External performance evaluation measures
print("Adjusted Rand Score:", adjusted_rand_score(target_test, predictions))
print("Jaccard Score:", jaccard_score(target_test, predictions))
print("F-Measure(F1-Score):", f1_score(target_test, predictions))
print("Fowlkes Mallows Score:", fowlkes_mallows_score(target_test, predictions))
这将导致以下输出:
Davies-Bouldin Index: 0.7916877512521091
Silhouette Coefficient: 0.5365443098840619
Adjusted Rand Score: 0.03789319261940484
Jaccard Score: 0.22321428571428573
F-Measure(F1-Score): 0.36496350364963503
Fowlkes Mallows Score: 0.6041244457314743
首先,我们导入了KMeans和metrics模块。我们创建了一个 k-means 对象或模型,并将其拟合到训练数据集(没有标签)。训练完成后,模型进行预测,并使用内部评估指标(如 DBI 和轮廓系数)和外部评估指标(如 Rand 得分、Jaccard 得分、F-Measure 和 Fowlkes-Mallows 得分)来评估这些预测。
摘要
在本章中,我们探讨了无监督学习及其技术,如降维和聚类。主要内容包括 PCA(主成分分析)降维技术以及几种聚类方法,如 k-means 聚类、层次聚类、DBSCAN 和谱聚类。本章从降维和 PCA 开始,PCA 之后,我们的主要焦点是聚类技术及如何确定聚类的数量。在后续章节中,我们介绍了聚类性能评估指标,如 DBI 和轮廓系数,这些是内部评估指标。接着,我们又探讨了外部评估指标,如 Rand 得分、Jaccard 得分、F-Measure 和 Fowlkes-Mallows 指数。
下一章,第十二章,文本数据分析,将专注于文本分析,包括使用 NLTK、SpaCy 和 scikit-learn 进行文本预处理和文本分类。本章首先探讨文本数据的基本操作,如通过分词、去除停用词、词干提取和词形还原、词性标注、实体识别、依存句法分析以及词云等进行文本标准化。在后续章节中,重点将放在特征工程方法上,如词袋模型、术语存在、TF-IDF、情感分析、文本分类和文本相似度。
第四部分:自然语言处理、图像分析与并行计算
本节的主要目标是概述自然语言处理(NLP)、图像分析和并行计算。NLP 技能包括使用 NLTK 和 SpaCy 进行文本预处理、情感分析和文本相似度计算。图像分析则涉及使用 OpenCV 进行图像处理和人脸检测。本节还重点讨论 DataFrame、数组和机器学习算法的并行计算。
本节包括以下章节:
-
第十二章,文本数据分析
-
第十三章,图像数据分析
-
第十四章,使用 Dask 进行并行计算
第十三章:分析文本数据
在信息化时代,数据以惊人的速度和体量产生。产生的数据不仅限于结构化或表格类型,也可以是多种非结构化类型,如文本数据、图像或图形数据、语音数据和视频数据。文本是非常常见且丰富的数据类型。文章、博客、教程、社交媒体帖子和网站内容都会生成非结构化的文本数据。每分钟都会有成千上万的电子邮件、消息、评论和推文发送出去。如此大量的文本数据需要被挖掘。文本分析为商业人士提供了大量机会;例如,亚马逊可以解读关于特定产品的客户反馈,新闻分析师可以分析 Twitter 上的新闻趋势和最新问题,Netflix 也可以解读每部电影和网络剧集的评论。商业分析师可以通过 NLP 和文本分析解读客户活动、评论、反馈和情感,从而有效推动业务发展。
本章将从基本的文本分析操作开始,如分词、移除停用词、词干提取、词形还原、词性标注和实体识别。之后,我们将学习如何使用 WordCloud 可视化文本分析结果。我们将看到如何基于评论,通过情感分析找出客户对某个产品的看法。在这里,我们将使用文本分类进行情感分析,并通过准确率、精确度、召回率和 F1 分数评估模型的性能。最后,我们将重点关注通过 Jaccard 和余弦相似度衡量两句话之间的文本相似性。
本章的主题如下所示:
-
安装 NLTK 和 SpaCy
-
文本规范化
-
分词
-
移除停用词
-
词干提取与词形还原
-
词性标注
-
实体识别
-
依存句法分析
-
创建词云
-
词袋模型
-
TF-IDF
-
使用文本分类进行情感分析
-
文本相似度
技术要求
本章的技术要求如下:
-
你可以通过以下 GitHub 链接找到代码和数据集:
github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter12 -
所有代码块都可以在
ch12.ipynb文件中找到。 -
本章仅使用一个 TSV 文件(
amazon_alexa.tsv)进行练习。 -
在本章中,我们将使用 NLTK、SpaCy、WordCloud、matplotlib、seaborn 和 scikit-learn Python 库。
安装 NLTK 和 SpaCy
NLTK 是一个流行且重要的 Python 自然语言处理库。它提供所有基础和高级的 NLP 操作。它包括常见的算法,如分词、词干提取、词形还原、词性标注和命名实体识别。NLTK 库的主要特点是开源、易于学习、易于使用、拥有活跃的社区以及完善的文档。NLTK 库可以通过在命令行中运行以下pip install命令来安装:
pip install nltk
NLTK 不是 Anaconda 中预安装的库。我们可以直接在 Jupyter Notebook 中安装nltk。我们可以在单元格中的命令前加上感叹号 (!):
!pip install nltk
SpaCy 是另一个重要且强大的 Python NLP 库。它提供了常见的 NLP 算法和高级功能。它旨在用于生产目的,并为大规模数据开发应用程序。可以使用以下命令在命令行中通过pip install安装 SpaCy 库:
pip install spacy
安装完 spaCy 后,我们需要安装一个spacy英语语言模型。我们可以使用以下命令安装:
python -m spacy download en
SpaCy 及其英语模型没有预安装在 Anaconda 中。我们可以使用以下代码直接安装spacy。我们可以在单元格中的命令前加上感叹号 (!):
!pip install spacy
!python -m spacy download en
使用上述语法,我们可以在 Jupyter Notebook 中安装spacy及其英语模型。
文本标准化
文本标准化将文本转换为标准或规范形式。它确保一致性,并有助于处理和分析。标准化过程没有单一的方法。标准化的第一步是将所有文本转换为小写。这是最简单、最适用且最有效的文本预处理方法。另一种方法可能是处理拼写错误的单词、缩写词、简写和使用超出词汇表的单词;例如,“super”,“superb”和“superrrr”可以转换为“super”。文本标准化处理测试数据中的噪声和干扰,准备无噪声数据。我们还会应用词干提取和词形还原来标准化文本中存在的单词。
让我们通过将文本转换为小写来执行基本的标准化操作:
# Input text
paragraph="""Taj Mahal is one of the beautiful monuments. It is one of the wonders of the world. It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal."""
# Converting paragraph in lowercase
print(paragraph.lower())
这将输出以下内容:
taj mahal is one of the beautiful monuments. it is one of the wonders of the world. it was built by shah jahan in 1631 in memory of his third beloved wife mumtaj mahal.
在前面的代码块中,我们通过使用lower()方法将给定的输入段落转换为小写。
在 NLP 中,文本标准化处理随机性,并将文本转换为标准形式,从而提高 NLP 解决方案的整体性能。它还通过将单词转换为根词来减少文档词项矩阵的大小。在接下来的部分中,我们将重点介绍基本的文本预处理操作。
分词
分词是文本分析中的初步步骤。分词被定义为将文本段落分解成更小的部分或词元,如句子或单词,并忽略标点符号。分词可以分为两种类型:句子分词和单词分词。句子分词器将段落拆分为句子,而单词分词器将文本拆分为单词或词元。
让我们使用 NLTK 和 spaCy 对段落进行分词:
- 在分词之前,导入 NLTK 并下载所需的文件:
# Loading NLTK module
import nltk
# downloading punkt
nltk.download('punkt')
# downloading stopwords
nltk.download('stopwords')
# downloading wordnet
nltk.download('wordnet')
# downloading average_perception_tagger
nltk.download('averaged_perceptron_tagger')
- 现在,我们将使用 NLTK 的
sent_tokenize()方法将段落分割成句子:
# Sentence Tokenization
from nltk.tokenize import sent_tokenize
paragraph="""Taj Mahal is one of the beautiful monuments. It is one of the wonders of the world. It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal."""
tokenized_sentences=sent_tokenize(paragraph)
print(tokenized_sentences)
这将产生以下输出:
['Taj Mahal is one of the beautiful monument.', 'It is one of the wonders of the world.', 'It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal.']
在前面的例子中,我们将段落作为参数传递给 sent_tokenize() 方法。此方法的输出将是一个句子列表。
让我们使用 spaCy 将段落分割成句子:
# Import spacy
import spacy
# Loading english language model
nlp = spacy.load("en")
# Build the nlp pipe using 'sentencizer'
sent_pipe = nlp.create_pipe('sentencizer')
# Append the sentencizer pipe to the nlp pipeline
nlp.add_pipe(sent_pipe)
paragraph = """Taj Mahal is one of the beautiful monuments. It is one of the wonders of the world. It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal."""
# Create nlp Object to handle linguistic annotations in a documents.
nlp_doc = nlp(paragraph)
# Generate list of tokenized sentence
tokenized_sentences = []
for sentence in nlp_doc.sents:
tokenized_sentences.append(sentence.text)
print(tokenized_sentences)
这将产生以下输出:
['Taj Mahal is one of the beautiful monument.', 'It is one of the wonders of the world.', 'It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal.']
在前面的例子中,我们首先导入了英语语言模型并实例化它。之后,我们使用 sentencizer 创建了 NLP 管道,并将其添加到管道中。最后,我们创建了 NLP 对象,并通过迭代 NLP 对象的 sents 属性来生成一个分词后的句子列表。
让我们使用 NLTK 的 word_tokenize() 函数将段落分词:
# Import nltk word_tokenize method
from nltk.tokenize import word_tokenize
# Split paragraph into words
tokenized_words=word_tokenize(paragraph)
print(tokenized_words)
这将产生以下输出:
['Taj', 'Mahal', 'is', 'one', 'of', 'the', 'beautiful', 'monument', '.', 'It', 'is', 'one', 'of', 'the', 'wonders', 'of', 'the', 'world', '.', 'It', 'was', 'built', 'by', 'Shah', 'Jahan', 'in', '1631', 'in', 'memory', 'of', 'his', 'third', 'beloved', 'wife', 'Mumtaj', 'Mahal', '.']
在前面的例子中,我们将段落作为参数传递给 word_tokenize() 方法。此方法的输出将是一个单词列表。
让我们使用 spaCy 将段落分词为单词:
# Import spacy
import spacy
# Loading english language model
nlp = spacy.load("en")
paragraph = """Taj Mahal is one of the beautiful monuments. It is one of the wonders of the world. It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal."""
# Create nlp Object to handle linguistic annotations in a documents.
my_doc = nlp(paragraph)
# tokenize paragraph into words
tokenized_words = []
for token in my_doc:
tokenized_words.append(token.text)
print(tokenized_words)
这将产生以下输出:
['Taj', 'Mahal', 'is', 'one', 'of', 'the', 'beautiful', 'monument', '.', 'It', 'is', 'one', 'of', 'the', 'wonders', 'of', 'the', 'world', '.', 'It', 'was', 'built', 'by', 'Shah', 'Jahan', 'in', '1631', 'in', 'memory', 'of', 'his', 'third', 'beloved', 'wife', 'Mumtaj', 'Mahal', '.']
在前面的例子中,我们首先导入了英语语言模型并实例化它。之后,我们创建了一个文本段落。最后,我们使用文本段落创建了一个 NLP 对象,并通过迭代它来生成一个分词后的单词列表。
让我们创建分词后的单词的频率分布:
# Import frequency distribution
from nltk.probability import FreqDist
# Find frequency distribution of paragraph
fdist = FreqDist(tokenized_words)
# Check top 5 common words
fdist.most_common(5)
这将产生以下输出:
[('of', 4), ('the', 3), ('.', 3), ('Mahal', 2), ('is', 2)]
让我们使用 matplotlib 创建一个频率分布图:
# Import matplotlib
import matplotlib.pyplot as plt
# Plot Frequency Distribution
fdist.plot(20, cumulative=False)
plt.show()
这将产生以下输出:
在前面的例子中,我们使用 FreqDist 类生成了词元的频率分布。在句子和单词分词后,我们将学习如何从给定文本中移除停用词。
移除停用词
停用词被视为文本分析中的噪声。任何文本段落中都必须包含动词、冠词和介词。这些都被视为停用词。停用词对于人类对话是必要的,但在文本分析中贡献不大。从文本中移除停用词称为噪声消除。
让我们看看如何使用 NLTK 移除停用词:
# import the nltk stopwords
from nltk.corpus import stopwords
# Load english stopwords list
stopwords_set=set(stopwords.words("english"))
# Removing stopwords from text
filtered_word_list=[]
for word in tokenized_words:
# filter stopwords
if word not in stopwords_set:
filtered_word_list.append(word)
# print tokenized words
print("Tokenized Word List:", tokenized_words)
# print filtered words
print("Filtered Word List:", filtered_word_list)
这将产生以下输出:
Tokenized Word List: ['Taj', 'Mahal', 'is', 'one', 'of', 'the', 'beautiful', 'monuments', '.', 'It', 'is', 'one', 'of', 'the', 'wonders', 'of', 'the', 'world', '.', 'It', 'was', 'built', 'by', 'Shah', 'Jahan', 'in', '1631', 'in', 'memory', 'of', 'his', 'third', 'beloved', 'wife', 'Mumtaj', 'Mahal', '.']
Filtered Word List: ['Taj', 'Mahal', 'one', 'beautiful', 'monuments', '.', 'It', 'one', 'wonders', 'world', '.', 'It', 'built', 'Shah', 'Jahan', '1631', 'memory', 'third', 'beloved', 'wife', 'Mumtaj', 'Mahal', '.']
在前面的示例中,首先,我们导入了停用词并加载了英文单词列表。之后,我们使用for循环迭代在上一节中生成的分词单词列表,并使用if条件从停用词列表中过滤分词单词。我们将过滤后的单词保存在fltered_word_list列表对象中。
让我们看看如何使用 spaCy 去除停用词:
# Import spacy
import spacy
# Loading english language model
nlp = spacy.load("en")
# text paragraph
paragraph = """Taj Mahal is one of the beautiful monuments. It is one of the wonders of the world. It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal."""
# Create nlp Object to handle linguistic annotations in a documents.
my_doc = nlp(paragraph)
# Removing stopwords from text
filtered_token_list = []
for token in my_doc:
# filter stopwords
if token.is_stop==False:
filtered_token_list.append(token)
print("Filtered Word List:",filtered_token_list)
这会产生以下输出:
Filtered Sentence: [Taj, Mahal, beautiful, monument, ., wonders, world, ., built, Shah, Jahan, 1631, memory, beloved, wife, Mumtaj, Mahal, .]
在前面的示例中,首先,我们导入了停用词并将英文单词列表加载到停用词变量中。之后,我们使用for循环迭代 NLP 对象,并通过if条件从停用词列表中过滤每个单词,使用属性"is_stop"。我们将过滤后的单词附加到fltered_token_list列表对象中。在这一部分,我们了解了如何去除停用词。现在,轮到我们学习词干提取和词形还原,找到根词了。
词干提取和词形还原
词干提取(Stemming)是文本分析中语言层面的另一步规范化。词干提取过程将单词替换为其词根。它会去除单词的前缀和后缀。例如,单词“connect”是“connecting”、“connected”和“connection”的词根。所有这些单词都有一个共同的词根:connect。这种单词拼写的差异使得文本数据分析变得困难。
词形还原(Lemmatization)是另一种词汇规范化方法,它将一个单词转换为其词根。它与词干提取(stemming)密切相关。主要区别在于,词形还原在执行规范化时考虑了单词的上下文,而词干提取则没有考虑单词的上下文知识。词形还原比词干提取更为复杂。例如,单词“geese”经过词形还原后变成“goose”。词形还原通过使用词典将单词简化为有效的词元。词形还原会根据单词的词性进行规范化;这就是为什么它难以实现且速度较慢,而词干提取则容易实现且速度较快,但准确度较低的原因。
让我们看看如何使用 NLTK 进行词干提取和词形还原:
# Import Lemmatizer
from nltk.stem.wordnet import WordNetLemmatizer
# Import Porter Stemmer
from nltk.stem.porter import PorterStemmer
# Create lemmatizer object
lemmatizer = WordNetLemmatizer()
# Create stemmer object
stemmer = PorterStemmer()
# take a sample word
sample_word = "crying"
print("Lemmatized Sample Word:", lemmatizer.lemmatize(sample_word, "v"))
print("Stemmed Sample Word:", stemmer.stem(sample_word))
这会产生以下输出:
Lemmatized Sample Word: cry
Stemmed Sample Word: cri
在前面的示例中,首先,我们导入了WordNetLemmatizer进行词形还原并实例化了它的对象。类似地,我们导入了PorterStemmer进行词干提取并实例化了它的对象。之后,我们通过lemmatize()函数获得了词形,还通过stem()函数得到了词干。
让我们看看如何使用 spaCy 进行词形还原:
# Import english language model
import spacy
# Loading english language model
nlp = spacy.load("en")
# Create nlp Object to handle linguistic annotations in documents.
words = nlp("cry cries crying")
# Find lemmatized word
for w in words:
print('Original Word: ', w.text)
print('Lemmatized Word: ',w.lemma_)
这会产生以下输出:
Original Word: cry
Lemmatized Word: cry
Original Word: cries
Lemmatized Word: cry
Original Word: crying
Lemmatized Word: cry
在前面的示例中,首先,我们导入了英文语言模型并实例化了它。之后,我们创建了 NLP 对象并使用for循环对其进行迭代。在循环中,我们通过text和lemma_属性获取了文本值及其词形还原值。在这一部分,我们学习了词干提取和词形还原。现在,我们将学习给定文档中的词性标注(PoS tagging)。
词性标注(POS tagging)
PoS 代表词性。PoS 标签的主要目标是发现单词的句法类型,如名词、代词、形容词、动词、副词和介词。PoS 标签能够找到句子中单词之间的关系。
让我们看看如何使用 NLTK 获取单词的 PoS 标签:
# import Word Tokenizer and PoS Tagger
from nltk.tokenize import word_tokenize
from nltk import pos_tag
# Sample sentence
sentence = "Taj Mahal is one of the beautiful monument."
# Tokenize the sentence
sent_tokens = word_tokenize(sentence)
# Create PoS tags
sent_pos = pos_tag(sent_tokens)
# Print tokens with PoS
print(sent_pos)
这将产生以下输出:
[('Taj', 'NNP'), ('Mahal', 'NNP'), ('is', 'VBZ'), ('one', 'CD'), ('of', 'IN'), ('the', 'DT'), ('beautiful', 'JJ'), ('monument', 'NN'), ('.', '.')]
在前面的示例中,首先,我们导入了word_tokenize和pos_tag。接着,我们获取了一段文本并将其作为参数传递给word_tokenize()方法。此方法的输出将是一个单词列表。接下来,使用pos_tag()函数为每个标记生成 PoS 标签。
让我们看看如何使用 spaCy 获取单词的 PoS 标签:
# Import spacy
import spacy
# Loading small english language model
nlp = spacy.load("en_core_web_sm")
# Create nlp Object to handle linguistic annotations in a documents.
sentence = nlp(u"Taj Mahal is one of the beautiful monument.")
for token in sentence:
print(token.text, token.pos_)
这将产生以下输出:
Taj PROPN
Mahal PROPN
is VERB
one NUM
of ADP
the DET
beautiful ADJ
monument NOUN
. PUNCT
在前面的示例中,首先,我们导入了英语语言模型并实例化了它。接着,我们创建了 NLP 对象,并使用for循环对其进行迭代。在循环中,我们通过text和pos_属性获取文本值及其词干值。在这一部分中,我们已经研究了 PoS 标签。现在,是时候跳转到识别文本中的命名实体了。
识别实体
实体识别是指从给定文本中提取或检测实体。它也被称为命名实体识别(NER)。实体可以定义为一个对象,如地点、人物、组织或日期。实体识别是 NLP 的高级主题之一,它用于从文本中提取重要信息。
让我们看看如何使用 spaCy 从文本中提取实体:
# Import spacy
import spacy
# Load English model for tokenizer, tagger, parser, and NER
nlp = spacy.load('en')
# Sample paragraph
paragraph = """Taj Mahal is one of the beautiful monuments. It is one of the wonders of the world. It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal."""
# Create nlp Object to handle linguistic annotations in documents.
docs=nlp(paragraph)
entities=[(i.text, i.label_) for i in docs.ents]
print(entities)
这将产生以下输出:
[('Taj Mahal', 'PERSON'), ('Shah Jahan', 'PERSON'), ('1631', 'DATE'), ('third', 'ORDINAL'), ('Mumtaj Mahal', 'PERSON')]
在前面的示例中,首先,我们导入了 spaCy 并加载了英语语言模型。接着,我们创建了 NLP 对象,并使用for循环对其进行迭代。在循环中,我们通过text和label_属性获取文本值及其实体类型值。现在,让我们使用 spaCy 的display类来可视化文本中的实体:
# Import display for visualizing the Entities
from spacy import displacy
# Visualize the entities using render function
displacy.render(docs, style = "ent",jupyter = True)
这将产生以下输出:
在前面的示例中,我们导入了display类,并使用一个 NLP 文本对象调用了它的render()方法,style设置为ent,jupyter设置为True。
依存解析
依存解析用于发现单词之间的关系——即单词是如何彼此关联的。它帮助计算机理解句子以便进行分析;例如,“泰姬陵是最美丽的纪念碑之一。”我们不能仅通过分析单词来理解这个句子。我们需要深入分析并理解单词顺序、句子结构以及词性:
# Import spacy
import spacy
# Load English model for tokenizer, tagger, parser, and NER
nlp = spacy.load('en')
# Create nlp Object to handle linguistic annotations in a documents.
docs=nlp(sentence)
# Visualize the using render function
displacy.render(docs, style="dep", jupyter= True, options={'distance': 150})
这将产生以下输出:
在前面的示例中,我们导入了display类,并使用一个 NLP 文本对象调用了它的render()方法,style设置为'dep',jupyter设置为True,并将options设置为一个包含distance键和值 150 的字典。接下来,我们将看看如何根据文本中单词的频率,使用词云可视化文本数据。
创建词云
作为数据分析师,你需要识别最常见的单词,并以图形形式向高层管理层呈现它们。词云用于表示单词频率图。它通过单词的大小来表示频率,也就是说,频率越高的单词看起来越大,频率较低的单词看起来越小。它也被称为标签云。我们可以使用 Python 中的wordcloud库来创建词云。我们可以使用以下命令安装它:
pip install wordcloud
或者,也可以使用这个:
conda install -c conda-forge wordcloud
让我们学习如何创建词云:
- 导入库并加载停用词列表:
# importing all necessary modules
from wordcloud import WordCloud
from wordcloud import STOPWORDS
import matplotlib.pyplot as plt
stopword_list = set(STOPWORDS)
paragraph="""Taj Mahal is one of the beautiful monuments. It is one of the wonders of the world. It was built by Shah Jahan in 1631 in memory of his third beloved wife Mumtaj Mahal."""
在前面的示例中,我们导入了WordCloud、STOPWORDS和matplotlib.pyplot类。我们还创建了停用词集合并定义了段落文本。
- 创建并生成词云:
word_cloud = WordCloud(width = 550, height = 550,
background_color ='white',
stopwords = stopword_list,
min_font_size = 10).generate(paragraph)
之后,创建并生成一个WordCloud对象,设置width、height、background_color、stopwords和min_font_size等参数,并在段落文本字符串上生成词云。
- 可视化词云:
0.# Visualize the WordCloud Plot
# Set wordcloud figure size
plt.figure(figsize = (8, 6))
# Show image
plt.imshow(word_cloud)
# Remove Axis
plt.axis("off")
# show plot
plt.show()
这将产生以下输出:
在前面的示例中,我们使用matplotlib.pyplot可视化了词云。让我们学习如何使用词袋模型将文本文档转换为数值向量。
词袋模型
词袋模型(BoW)是最基本、最简单和最流行的特征工程技术之一,用于将文本转换为数值向量。它分为两个步骤:收集词汇表中的单词,并计算它们在文本中的出现频率或存在。它不考虑文档结构和上下文信息。让我们以以下三个文档为例,理解 BoW:
文档 1:我喜欢披萨。
文档 2:我不喜欢汉堡。
文档 3:披萨和汉堡都属于垃圾食品。
现在,我们将创建文档词矩阵(DTM)。该矩阵由文档(行)、单词(列)以及单词频率(单元格值)组成。
| I | like | pizza | do | not | burgers | and | both | are | junk | food | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Doc-1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Doc-2 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
| Doc-3 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 |
在前面的示例中,我们使用了单一的关键词生成了 DTM,这称为单元法(unigram)。我们还可以使用连续的两个关键词组合,这被称为二元模型(bigram),或者三个关键词的组合,称为三元模型(trigram)。这种通用形式被称为 n 元模型(n-gram)。
在 Python 中,scikit-learn 提供了CountVectorizer来生成 BoW 的文档词矩阵(DTM)。我们将在使用文本分类进行情感分析章节中看到如何使用 scikit-learn 生成它。
TF-IDF
TF-IDF 代表 词频-逆文档频率。它有两个部分:词频 (TF) 和 逆文档频率 (IDF)。TF 仅计算每个文档中单词的出现次数,等同于 BoW(词袋模型)。TF 不考虑单词的上下文,且偏向较长的文档。IDF 计算值,表示某个词保留的信息量。
TF-IDF 是 TF 和 IDF 两个部分的点积。TF-IDF 会对文档权重进行归一化处理。TF-IDF 值越高,表示某个词在该文档中的出现频率越高。让我们看看以下三个文档:
文档 1:我喜欢比萨。
文档 2:我不喜欢汉堡。
文档 3:比萨和汉堡都是垃圾食品。
现在,我们将创建 DTM(文档-词项矩阵)。该矩阵的行头是文档名称,列头是词汇,单元格中则是 TF-IDF 值:
| I | like | pizza | do | not | burgers | and | both | are | junk | food | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Doc-1 | 0.58 | 0.58 | 0.58 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Doc-2 | 0.58 | 0.58 | 0 | 1.58 | 1.58 | 0.58 | 0 | 0 | 0 | 0 | 0 |
| Doc-3 | 0 | 0 | 0.58 | 0 | 0 | 0.58 | 1.58 | 1.58 | 1.58 | 1.58 | 1.58 |
在 Python 中,scikit-learn 提供了 TfidfVectorizer 用于生成 TF-IDF DTM。我们将在接下来的章节中看到如何使用 scikit-learn 来生成它。
使用文本分类进行情感分析
一个业务或数据分析师需要了解客户对某一特定产品的反馈和评价。客户喜欢或不喜欢什么?销售情况如何?作为一名业务分析师,你需要以合理的准确性分析这些内容,并量化客户的评论、反馈、意见和推文,从而了解目标受众。情感分析提取文本中的核心信息,并提供人们对产品、服务、品牌以及政治和社会话题的看法。情感分析用于了解客户和人们的心态。它不仅应用于营销领域,我们还可以在政治、公共管理、政策制定、信息安全和研究中使用它。它帮助我们理解人们反馈的极性。情感分析还涵盖了词汇、语气和写作风格。
文本分类可以作为情感分析的一种方法。它是一种监督学习方法,用于检测网页内容、新闻文章、博客、推文和情感类别。文本分类在营销、金融、电子商务和安全等领域有着广泛的应用。首先,我们对文本进行预处理,然后找出预处理文本的特征,再将这些特征和标签输入机器学习算法进行分类。以下图解展示了使用文本分类进行情感分析的完整过程:
让我们对 Amazon Alexa 产品评论进行情感分类。我们可以从 Kaggle 网站获取数据(www.kaggle.com/sid321axn/amazon-alexa-reviews)。
Alexa 产品评论数据是一个制表符分隔的值文件(TSV 文件)。该数据有五列或属性——评分,日期,变体,已验证评论,和 反馈。
评分 列表示用户对 Alexa 产品的评分。日期列是用户给出评论的日期。变体 列表示产品型号。已验证评论 包含用户对产品的实际评论。
评分表示每个用户对产品给出的评分。日期表示评论的日期,变体描述了型号名称。已验证评论 包含用户撰写的文本评论,反馈列表示情感分数,其中 1 表示正面情感,0 表示负面情感。
使用 BoW 进行分类
在本小节中,我们将基于 BoW 执行情感分析和文本分类。这里,我们使用 scikit-learn 库生成词袋。接下来,让我们看看如何在以下步骤中使用 BoW 特征执行情感分析:
- 加载数据集:
构建机器学习模型的第一步是加载数据集。让我们首先使用 pandas 的 read_csv() 函数读取数据:
# Import libraries
import pandas as pd
# read the dataset
df=pd.read_csv('amazon_alexa.tsv', sep='\t')
# Show top 5-records
df.head()
这将生成以下输出:
在之前的输出数据框中,我们看到 Alexa 评论数据集有五个列:评分,日期,变体,已验证评论,和 反馈。
- 探索数据集。
让我们绘制 反馈 列的计数,以查看数据集中的正面和负面评论数量:
# Import seaborn
import seaborn as sns
import matplotlib.pyplot as plt
# Count plot
sns.countplot(x='feedback', data=df)
# Set X-axis and Y-axis labels
plt.xlabel('Sentiment Score')
plt.ylabel('Number of Records')
# Show the plot using show() function
plt.show()
这将生成以下输出:
在前面的代码中,我们使用 seaborn 的 countplot() 函数绘制了反馈列的条形图。该函数会计算并绘制 反馈 列的值。在此图中,我们可以看到 2,900 条评论为正面评论,250 条评论为负面评论。
- 使用
CountVectorizer生成特征:
让我们使用 scikit-learn 的 CountVectorizer 为客户评论生成一个 BoW 矩阵:
# Import CountVectorizer and RegexTokenizer
from nltk.tokenize import RegexpTokenizer
from sklearn.feature_extraction.text import CountVectorizer
# Create Regex tokenizer for removing special symbols and numeric values
regex_tokenizer = RegexpTokenizer(r'[a-zA-Z]+')
# Initialize CountVectorizer object
count_vectorizer = CountVectorizer(lowercase=True,
stop_words='english',
ngram_range = (1,1),
tokenizer = regex_tokenizer.tokenize)
# Fit and transform the dataset
count_vectors = count_vectorizer.fit_transform( df['verified_reviews'])
在前面的代码中,我们创建了一个RegexTokenizer对象,并使用输入的正则表达式来去除特殊字符和符号。之后,创建了CountVectorizer对象,并对已验证的评论执行了 fit 和 transform 操作。这里,CountVectorizer接受一些参数,如lowercase用于将关键字转换为小写,stop_words用于指定特定语言的停用词列表,ngram_range用于指定单词一元组、二元组或三元组,而tokenizer则用来传递tokenizer对象。RegexTokenizer对象被传递给tokenizer参数。最后,我们调用了fit_transform()函数,该函数根据指定的参数将文本评论转换为文档-词项矩阵(DTM)。
- 分割训练集和测试集:
让我们使用train_test_split()将特征集和目标列分割为feature_train、feature_test、target_train和target_test。train_test_split()接受依赖和独立数据帧、test_size和random_state作为参数。这里,test_size决定了训练集与测试集的比例(即,test_size 0.3表示 30%的数据作为测试集,剩下的 70%作为训练集),而random_state则用作种子值,以便每次能重复相同的数据分割。如果random_state为None,则会每次随机分割记录,这会导致不同的性能度量:
# Import train_test_split
from sklearn.model_selection import train_test_split
# Partition data into training and testing set
feature_train, feature_test, target_train, target_test = train_test_split(count_vectors, df['feedback'], test_size=0.3, random_state=1)
在前面的代码中,我们使用train_test_split()方法将特征集和目标列分割为feature_train、feature_test、target_train和target_test。
- 使用逻辑回归构建分类模型:
在这一部分,我们将构建逻辑回归模型,通过 BoW(或CountVectorizer)对评论情感进行分类。让我们创建逻辑回归模型:
# import logistic regression scikit-learn model
from sklearn.linear_model import LogisticRegression
# Create logistic regression model object
logreg = LogisticRegression(solver='lbfgs')
# fit the model with data
logreg.fit(feature_train,target_train)
# Forecast the target variable for given test dataset
predictions = logreg.predict(feature_test)
在前面的代码中,我们导入了LogisticRegression并创建了LogisticRegression对象。创建模型对象后,我们对训练数据执行了fit()操作,并使用predict()对测试数据集进行情感预测。
- 评估分类模型:
让我们使用metrics类及其方法——accuracy_score、precision_score和recall_score来评估分类模型:
# Import metrics module for performance evaluation
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
# Assess model performance using accuracy measure
print("Logistic Regression Model Accuracy:",accuracy_score(target_test, predictions))
# Calculate model precision
print("Logistic Regression Model Precision:",precision_score(target_test, predictions))
# Calculate model recall
print("Logistic Regression Model Recall:",recall_score(target_test, predictions))
# Calculate model f1 score
print("Logistic Regression Model F1-Score:",f1_score(target_test, predictions))
这会产生以下输出:
Logistic Regression Model Accuracy: 0.9428571428571428
Logistic Regression Model Precision: 0.952433628318584
Logistic Regression Model Recall: 0.9873853211009175
Logistic Regression Model F1-Score: 0.9695945945945945
在前面的代码中,我们使用scikit-learn metrics函数评估了模型的性能,指标包括准确率、精确度、召回率和 F1 值。所有这些度量都大于 94%,所以我们可以说我们的模型表现良好,能够准确分类情感级别,并且精确度和召回率都很高。
使用 TF-IDF 进行分类
在这一小节中,我们将基于 TF-IDF 执行情感分析和文本分类。这里,TF-IDF 是通过scikit-learn库生成的。让我们看看如何使用 TF-IDF 特征执行情感分析,具体步骤如下:
- 加载数据集:
构建机器学习模型的第一步是加载数据集。
首先,使用 pandas 的read_csv()函数读取数据:
# Import libraries
import pandas as pd
# read the dataset
df=pd.read_csv('amazon_alexa.tsv', sep='\t')
# Show top 5-records
df.head()
这将产生以下输出:
在前面的输出数据帧中,我们看到 Alexa 评论数据集包含五列:rating、date、variation、verified_reviews和feedback。
- 使用
TfidfVectorizer生成特征:
让我们使用 scikit-learn 的TfidfVectorizer生成客户评论的 TF-IDF 矩阵:
# Import TfidfVectorizer and RegexTokenizer
from nltk.tokenize import RegexpTokenizer
from sklearn.feature_extraction.text import TfidfVectorizer
# Create Regex tokenizer for removing special symbols and numeric values
regex_tokenizer = RegexpTokenizer(r'[a-zA-Z]+')
# Initialize TfidfVectorizer object
tfidf = TfidfVectorizer(lowercase=True, stop_words ='english',ngram_range = (1,1),tokenizer = regex_tokenizer.tokenize)
# Fit and transform the dataset
text_tfidf = tfidf.fit_transform(df['verified_reviews'])
在前面的代码中,我们创建了一个RegexTokenizer对象,并使用一个输入的正则表达式来去除特殊字符和符号。之后,我们创建了TfidfVectorizer对象,并对已验证的评论进行了拟合和转换操作。在这里,TfidfVectorizer接受一些参数,比如lowercase,用于将关键词转换为小写;stop_words,指定语言特定的停用词列表;ngram_range,指定单词组(unigram)、双词组(bigram)或三词组(trigram);tokenizer则用于传递tokenizer对象。RegexTokenizer对象被传递给tokenizer参数。最后,我们调用了fit_transform()函数,根据指定的参数将文本评论转换为文档-词项矩阵(DTM)。
- 分割训练集和测试集:
我们使用train_test_split()将特征集和目标列分割成feature_train、feature_test、target_train和target_test。train_test_split()接受依赖变量和独立数据帧、test_size和random_state作为参数。让我们将数据集分割为训练集和测试集:
# Import train_test_split
from sklearn.model_selection import train_test_split
# Partition data into training and testing set
from sklearn.model_selection import train_test_split
feature_train, feature_test, target_train, target_test = train_test_split(text_tfidf, df['feedback'], test_size=0.3, random_state=1)
在前面的代码中,我们使用train_test_split()方法将特征集和目标列分割成feature_train、feature_test、target_train和target_test。
- 使用逻辑回归构建分类模型:
在本节中,我们将构建逻辑回归模型,以使用 TF-IDF 对评论情感进行分类。让我们创建逻辑回归模型:
# import logistic regression scikit-learn model
from sklearn.linear_model import LogisticRegression
# instantiate the model
logreg = LogisticRegression(solver='lbfgs')
# fit the model with data
logreg.fit(feature_train,target_train)
# Forecast the target variable for given test dataset
predictions = logreg.predict(feature_test)
在前面的代码中,我们导入了LogisticRegression并创建了LogisticRegression对象。在创建模型对象之后,我们对训练数据进行了fit()操作,并使用predict()对测试数据集进行情感预测。
- 评估分类模型:
让我们使用metrics类及其方法——accuracy_score、precision_score和recall_score来评估分类模型:
# Import metrics module for performance evaluation
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
# Assess model performance using accuracy measure
print("Logistic Regression Model Accuracy:",accuracy_score(target_test, predictions))
# Calculate model precision
print("Logistic Regression Model Precision:",precision_score(target_test, predictions))
# Calculate model recall
print("Logistic Regression Model Recall:",recall_score(target_test, predictions))
# Calculate model f1 score
print("Logistic Regression Model F1-Score:",f1_score(target_test, predictions))
这将产生以下输出:
Logistic Regression Model Accuracy: 0.9238095238095239
Logistic Regression Model Precision: 0.923728813559322
Logistic Regression Model Recall: 1.0
Logistic Regression Model F1-Score: 0.960352422907489
在前面的代码中,我们使用 scikit-learn 的metrics函数,通过准确率、精确率、召回率和 F1 分数评估了模型的性能。所有指标都大于 94%,因此我们可以说我们的模型表现良好,并且能够以较好的精度和召回率对两种情感类别进行分类。在本节中,我们了解了如何通过文本分类进行情感分析。文本分类使用了 BoW 和 TF-IDF 特征。在下一节中,我们将学习如何找出两段文本之间的相似性,比如句子或段落。
文本相似度
文本相似度是确定两个最接近文本的过程。文本相似度对于查找相似文档、问题和查询非常有帮助。例如,像 Google 这样的搜索引擎使用相似度来查找文档的相关性,而像 StackOverflow 这样的问答系统或客户服务系统则使用类似问题。文本相似度通常使用两种度量标准,即 Jaccard 相似度和余弦相似度。
我们还可以使用 spaCy 中可用的相似度方法。nlp 对象的 similarity 方法返回两个句子之间的分数。我们来看以下示例:
# Import spacy
import spacy
# Load English model for tokenizer, tagger, parser, and NER
nlp = spacy.load('en')
# Create documents
doc1 = nlp(u'I love pets.')
doc2 = nlp(u'I hate pets')
# Find similarity
print(doc1.similarity(doc2))
这将产生以下输出:
0.724494176985974
<ipython-input-32-f157deaa344d>:12: UserWarning: [W007] The model you're using has no word vectors loaded, so the result of the Doc.similarity method will be based on the tagger, parser and NER, which may not give useful similarity judgements. This may happen if you're using one of the small models, e.g. `en_core_web_sm`, which don't ship with word vectors and only use context-sensitive tensors. You can always add your own word vectors, or use one of the larger models instead if available.
在前面的代码块中,我们使用 spaCy 的 similarity() 函数计算了两个句子之间的相似度。spaCy 的相似度函数在使用较小模型(如 en_core_web_sm 和 en 模型)时效果不佳;因此你会收到一个警告:UserWarning: [W007]。要去除此警告,请使用更大的模型,如 en_core_web_lg。
Jaccard 相似度
Jaccard 相似度通过两个集合中共同单词(交集)与完全独特单词(并集)的比例来计算相似度。它会列出每个句子或文档中的唯一单词列表。这个方法适用于单词重复不重要的场景。Jaccard 相似度的范围是 0-100%;百分比越高,两个集合的相似度越大:
让我们看一个 Jaccard 相似度的示例:
def jaccard_similarity(sent1, sent2):
"""Find text similarity using jaccard similarity"""
# Tokenize sentences
token1 = set(sent1.split())
token2 = set(sent2.split())
# intersection between tokens of two sentences
intersection_tokens = token1.intersection(token2)
# Union between tokens of two sentences
union_tokens=token1.union(token2)
# Cosine Similarity
sim_= float(len(intersection_tokens) / len(union_tokens))
return sim_
jaccard_similarity('I love pets.','I hate pets.')
这将产生以下输出:
0.5
在上述示例中,我们创建了一个函数 jaccard_similarity(),该函数接受两个参数 sent1 和 sent2。它将计算两个句子中关键词的交集与并集之间的比例。
余弦相似度
余弦相似度计算两个多维投影向量之间的夹角余弦值。它表示两个文档之间的关系。两个向量可以由词袋模型、TF-IDF 或任何等效的文档向量组成。它适用于单词重复重要的情况。余弦相似度可以测量文本相似度,而不考虑文档的大小。
让我们看一个余弦相似度的示例:
# Let's import text feature extraction TfidfVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer
docs=['I love pets.','I hate pets.']
# Initialize TfidfVectorizer object
tfidf= TfidfVectorizer()
# Fit and transform the given data
tfidf_vector = tfidf.fit_transform(docs)
# Import cosine_similarity metrics
from sklearn.metrics.pairwise import cosine_similarity
# compute similarity using cosine similarity
cos_sim=cosine_similarity(tfidf_vector, tfidf_vector)
print(cos_sim)
这将产生以下输出:
[[1\. 0.33609693]
[0.33609693 1\. ]]
在上述示例中,首先我们导入 TfidfVectorizer 并生成给定文档的 TF-IDF 向量。之后,我们应用 cosine_similarity() 度量标准对文档列表进行相似度计算。
总结
本章探讨了使用 NLTK 和 spaCy 进行文本分析。主要内容包括文本预处理、情感分析和文本相似度。章节从文本预处理任务入手,如文本标准化、分词、去除停用词、词干提取和词形还原。我们还重点讲解了如何创建词云、识别给定文本中的实体并找到词元之间的依赖关系。在后续部分,我们将重点讨论 BoW、TFIDF、情感分析和文本分类。
下一章,第十三章,图像数据分析,主要讲解图像处理、基本图像处理操作以及使用 OpenCV 进行人脸检测。该章节从图像颜色模型开始,并介绍了图像操作,如在图像上绘制、调整图像大小、翻转和模糊图像。在后续部分,将重点讨论如何在人脸检测输入图像中进行分析。
第十四章:分析图像数据
我们正处在信息时代,每一个动作都会生成各种格式的数据,如文本、图像、地理空间数据和视频。智能手机已经进入世界各地的乡村,人们正在捕捉活动,特别是图像和视频,并将它们分享到社交媒体平台。这就是大量数据产生的方式,而且大部分数据以图像和视频的形式存在。工业和研究机构希望分析图像和视频数据集,以创造价值并制定自动化解决方案以降低成本。图像处理和计算机视觉是探索和开发基于图像和视频的解决方案的领域。在计算机视觉领域,有许多研究、创新和初创企业的机会。在本章中,我们将重点介绍图像处理的基础知识,帮助你建立计算机视觉领域的基本知识。
图像处理是计算机视觉的一个子集。计算机视觉是机器学习和人工智能领域内的一个高级且更强大的领域。计算机视觉提供了大量的应用,例如物体检测、图像和物体分类、图像字幕生成和图像分割。图像可以在信号处理中定义为二维信号,在几何学中定义为二维或三维的点集,在 Python 中则是二维或三维的 NumPy 数组。图像处理是指处理图像数据,并执行绘制、书写、调整大小、翻转、模糊、改变亮度和检测人脸等操作。在本章中,我们将详细探讨所有这些图像处理操作。
本章将涵盖以下主题:
-
安装 OpenCV
-
理解图像数据
-
色彩模型
-
在图像上绘制
-
在图像上写字
-
调整图像大小
-
翻转图像
-
改变亮度
-
模糊图像
-
人脸检测
技术要求
本章具有以下技术要求:
-
你可以通过以下 Github 链接找到代码、人脸分类器文件以及数据集:
github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter13. -
所有代码块均可在
ch13.ipynb文件中找到。 -
本章使用
.jpg/.jpeg文件(google.jpg、image.jpg、messi.png、nature.jpeg、barcelona.jpeg和tajmahal.jpg)进行练习。 -
本章使用一个人脸分类器 XML 文件(
haarcascade_frontalface_default.xml)。 -
在本章中,我们将使用 OpenCV、NumPy 和 matplotlib Python 库。
安装 OpenCV
OpenCV 是一个开源库,提供计算机视觉操作,如图像和视频分析。OpenCV 主要由 Intel 使用 C++开发,并提供 Python、Java 和 Matlab 的接口。OpenCV 具有以下特点:
-
它是一个开源的图像处理 Python 库。
-
OpenCV 是图像处理和计算机视觉的核心 Python 库。
-
OpenCV 易于学习和在网页和移动应用程序中部署。
-
OpenCV 在 Python 中是一个 API 和它 C++核心实现的包装器。
-
它因为后台 C++代码而运行速度很快。
我们可以使用以下命令安装 OpenCV:
pip install opencv-python
使用前面提到的 pip 命令,我们可以轻松安装 OpenCV。OpenCV 是最受欢迎的图像处理和计算机视觉库。它提供了多种与图像分析操作相关的用例,例如提高图像质量、过滤和转换图像、在图像上绘制、改变颜色、检测人脸和物体、识别人类动作、跟踪物体、分析运动以及查找相似图像。安装完 OpenCV 库后,接下来就是了解图像处理的基础。
理解图像数据
图像数据是一个二维数组或函数f(x,y),带有空间坐标。coordinate(x,y)的幅度称为强度。在 Python 中,图像是一个二维或三维的 NumPy 数组,包含像素值。像素是最小的图像单元,决定图像的质量。像素数量越多,分辨率越高。此外,还有多种图像格式可用,例如.jpeg、.png、.gif和.tiff。这些文件格式有助于组织和管理数字图像文件。在分析图像数据之前,我们需要了解图像的类型。图像数据有三种类型:
-
二值图像
-
灰度
-
颜色
二值图像
二值图像像素只有两种颜色,一般是黑色和白色。二值图像像素仅使用二进制值 0 或 1。
上面的图像是二值图像的示例。它只有两种颜色,黑色和白色,且不使用黑白的不同阴影。
灰度图像
灰度图像看起来像黑白图像。它由每个像素 8 位表示,也就是说有 256 个强度值或色调,范围从 0 到 255。这 256 种色调从纯黑到纯白过渡;0 代表纯黑,而 255 代表白色。
上面的图像是灰度图像。它是一个黑白图像,其中的色调从纯黑过渡到纯白。
彩色图像
彩色图像是由红色、蓝色和绿色三种基本颜色混合而成。这些基本颜色具有按一定比例混合形成新颜色的能力。每种颜色使用 8 位(强度值在 0 到 255 之间),即每个像素 24 位。让我们看一个彩色图像的例子:
在前面的图像文件中,我们可以看到不同强度的各种颜色阴影。了解了图像类型之后,接下来就是了解 RGB、CMYK、HSV、HSL 和灰度等颜色模型。让我们来看看颜色模型。
颜色模型
颜色模型是一种用于处理和测量原色组合的结构。它们帮助我们解释颜色在计算机屏幕或纸张上的显示方式。颜色模型可以分为两种类型:加法模型和减法模型。加法模型用于计算机屏幕,例如 RGB(红色、绿色和蓝色)模型,减法模型用于图像打印,例如 CMYK(青色、品红色、黄色和黑色)模型:
除了 RGB 和 CMYK,还有许多其他模型,如 HSV、HSL 和灰度模型。HSV 是色调、饱和度和明度的缩写。它是一个三维颜色模型,是 RGB 模型的改进版本。在 HSV 模型中,中心轴的顶部是白色,底部是黑色,剩余的颜色位于其间。在这里,色调是角度,饱和度是离中心轴的距离,明度是离轴底部的距离。
HSL 是色调、饱和度和亮度的缩写。HSV 和 HSL 的主要区别在于亮度的量和颜色在中心轴上的值。
让我们学习如何读取和显示图像文件:
# Import cv2 latest version of OpenCV library
import cv2
# Import numeric python (NumPy) library
import numpy as np
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# magic function to render the figure in a notebook
%matplotlib inline
# Read image using imread() function
image = cv2.imread('google.jpg')
# Let's check image data type
print('Image Type:',type(image))
# Let's check dimension of image
print('Image Dimension:',image.shape)
# Let's show the image
plt.imshow(image)
plt.show()
这将产生以下输出:
在前面的示例中,我们导入了cv2、NumPy 和matplotlib.pyplot。cv2用于图像处理,NumPy 用于数组,matplotlib.pyplot用于显示图像。我们使用imread()函数读取图像,并返回一个图像数组。我们可以使用type()函数检查其类型,并通过 NumPy 数组的shape属性查看其形状。我们可以使用matpltlib.pyplot模块的show()函数显示图像。前面的图像未显示 Google 徽标图像的正确颜色,这是因为imread()函数以 BGR 颜色模型读取图像。让我们使用cvtColor()函数将 BGR 转换为 RGB 颜色模型,并传递标志cv2.COLOR_BGR2RGB:
# Convert image color space BGR to RGB
rgb_image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# Display the image
plt.imshow(rgb_image)
plt.show()
这将产生以下输出:
在这里,您可以看到 RGB 格式下正确的图像。
让我们使用imwrite()函数将图像文件写入本地磁盘:
# Write image using imwrite()
cv2.imwrite('image.jpg',image)
Output: True
在前面的代码块中,我们已经将图像文件写入本地磁盘,文件名为image.jpg。理解了颜色模型之后,接下来就是学习如何在图像上绘制元素。
在图像上绘制
让我们学习如何在图像上使用 OpenCV 绘制不同的图形形状,如线条、正方形或三角形。当我们在图像上绘制任何形状时,我们需要注意形状的坐标、颜色和粗细。首先,我们创建一个背景为白色或黑色的空白图像:
# Import cv2 latest version of OpenCV library
import cv2
# Import numeric python (NumPy) library
import numpy as np
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# Magic function to render the figure in a notebook
%matplotlib inline
# Let's create a black image
image_shape=(600,600,3)
black_image = np.zeros(shape=image_shape,dtype=np.int16)
# Show the image
plt.imshow(black_image)
这将产生以下输出:
在前面的示例中,我们使用 NumPy 模块的zeros()函数创建了一个背景为黑色的空白图像。zeros()函数创建一个给定大小的数组,并用零填充矩阵。
让我们创建一个背景为白色的空白图像:
# Create a white image
image_shape=(600,600,3)
white_image = np.zeros(shape=image_shape,dtype=np.int16)
# Set every pixel of the image to 255
white_image.fill(255)
# Show the image
plt.imshow(white_image)
这将产生以下输出:
在前面的示例中,我们使用 NumPy 模块的zeros()函数创建了一个背景为白色的空白图像,并将每个像素的值填充为 255。zeros()函数创建一个给定大小的数组,并用零填充矩阵。fill()函数将给定的值赋给矩阵中的所有元素。接下来,我们将在一张黑色图像上使用 OpenCV 绘制一条线:
# Draw a line on black image
line = cv2.line(black_image,(599,0),(0,599),(0,255,0),4)
# Show image
plt.imshow(line)
这将产生以下输出:
在前面的示例中,我们使用line()函数在黑色图像上绘制了绿色线条。line()函数接受以下参数:图像文件,start_point,end_point,颜色和粗细。在我们的示例中,起点和终点分别是 (599,0) 和 (0,599),颜色元组是 (0,255,0),粗细为 4。同样,我们可以在白色图像上创建一条线。让我们看看以下示例:
# Let's draw a blue line on white image
line = cv2.line(white_image,(599,0),(0,599),(0,0,255),4)
# Show the image
plt.imshow(line)
这将产生以下输出:
让我们看一个在白色图像上绘制圆形的示例:
# Let's create a white image
img_shape=(600,600,3)
white_image = np.zeros(shape=image_shape,dtype=np.int16)
# Set every pixel of the image to 255
white_image.fill(255)
# Draw a red circle on white image
circle=cv2.circle(white_image,(300, 300), 100, (255,0,0),6)
# Show the image
plt.imshow(circle)
这将产生以下输出:
在前面的示例中,我们创建了一个白色图像并使用circle()函数绘制了一个圆。circle()函数接受以下参数:图像,center_coordinates,半径,颜色和粗细。在我们的示例中,圆心是 (300, 300),半径是 100,颜色元组是 (255,0,0),粗细是 6。
让我们看一个在黑色图像上绘制矩形的示例:
# Let's create a black image
img_shape=(600,600,3)
black_image = np.zeros(shape=image_shape,dtype=np.int16)
# Draw a green rectangle on black image
rectangle= cv2.rectangle(black_image,(200,200),(400,500),(0,255,0),5)
# Show the image
plt.imshow(rectangle)
这将产生以下输出:
在前面的示例中,我们创建了一个黑色图像,并使用rectangle()函数绘制了一个矩形。rectangle()函数接受以下参数:图像,start_point,end_point,颜色和粗细。这里,粗细还可以接受一个参数-1,-1像素值将填充矩形形状并指定颜色。让我们看一个填充矩形的示例:
# Let's create a black image
img_shape=(600,600,3)
black_image = np.zeros(shape=image_shape,dtype=np.int16)
# Draw a green filled rectangle on black image
rectangle= cv2.rectangle(black_image,(200,200),(400,500),(0,255,0),-1)
# Show the image
plt.imshow(rectangle)
这将产生以下输出:
在前面的例子中,我们通过传递厚度值为-1 px 来填充矩形。简而言之,我们可以说,线条主要以起始点和终点为输入,矩形以左上角和右下角坐标为输入,圆形则以中心坐标和半径值为输入。
在图像上写字
在前一节中,我们在图像上创建了各种形状。现在,我们将学习如何在图像上写文字。在图像上写文字类似于绘制形状。让我们看看在图像上写字的例子:
# Let's create a black image
img_shape=(600,800,3)
black_image = np.zeros(shape=image_shape,dtype=np.int16)
# Write on black image
text = cv2.putText(black_image,'Thanksgiving',(10,500),
cv2.FONT_HERSHEY_SIMPLEX, 3,(255,0,0),2,cv2.LINE_AA)
# Display the image
plt.imshow(text)
这将得到以下输出:
在前面的例子中,我们创建了一个颜色为黑色的空白图像。我们使用putText()函数在图像上写了文字。putText()函数将接受以下参数:图像、文字、左下角坐标、字体、fontScale、颜色、厚度和linetype。
调整图像大小
调整图像大小是指改变给定图像的尺寸或缩放。缩放或调整大小可以通过宽度、高度或两者进行。调整图像大小的应用之一是训练深度学习模型,在这种情况下,缩小的图像尺寸可以加速训练。本书不涉及训练深度学习模型。如果你感兴趣,可以参考 Packt 出版的任何深度学习书籍。让我们看看调整图像大小的例子:
# Import cv2 module
import cv2
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# magic function to render the figure in a notebook
%matplotlib inline
# read image
image = cv2.imread('tajmahal.jpg')
# Convert image color space BGR to RGB
rgb_image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# Display the image
plt.imshow(rgb_image)
这将得到以下输出:
在前面的代码中,我们读取了图像并将其从 BGR 空间转换为 RGB 空间。现在让我们使用resize()函数来调整图像大小:
# Resize the image
image_resized = cv2.resize(rgb_image, (200, 200))
interpolation = cv2.INTER_NEAREST
# Display the image
plt.imshow(image_resized)
这将得到以下输出:
在前面的例子中,我们读取了图像,使用cvtColor()函数将 BGR 转换为 RGB 颜色,并使用resize()函数调整了图像大小。resize()函数将接受以下参数:图像、尺寸和插值。插值用于缩放无摩尔纹的图像。插值接受以下标志之一:INTER_NEAREST(最近邻插值)、INTER_LINEAR(双线性插值)和INTER_AREA(使用像素区域关系进行重采样)。
翻转图像
翻转图像相当于镜像效果。让我们学习如何沿* x 轴(垂直翻转)、 y *轴(水平翻转)或两个轴翻转图像。OpenCV 提供了flip()函数来翻转图像。flip()函数将接受两个参数:图像和 flipcode。图像是像素值的 NumPy 数组,flipcode 定义了翻转类型,如水平翻转、垂直翻转或两者。以下 flipcode 值表示不同类型的翻转:
-
Flipcode > 0 表示水平翻转。
-
Flipcode = 0 表示垂直翻转。
-
Flipcode < 0 表示水平和垂直翻转。
让我们看看翻转图像的例子:
# Import OpenCV module
import cv2
# Import NumPy
import numpy as np
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# magic function to render the figure in a notebook
%matplotlib inline
# Read image
image = cv2.imread('messi.png')
# Convert image color space BGR to RGB
rgb_image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# Display the image
plt.imshow(rgb_image)
这将得到以下输出:
这是原始图像,展示了莱昂内尔·梅西的照片。让我们使用flip()函数通过将 1 作为 flipcode 传递给flip()函数,将图像水平翻转:
# Flipping image (Horizontal flipping)
image_flip = cv2.flip(rgb_image, 1)
# Display the image
plt.imshow(image_flip)
这将产生以下输出:
这是水平翻转后的图像。让我们将原始图像垂直翻转:
# Flipping image (Vertical flipping)
image_flip = cv2.flip(rgb_image,0)
# Display the image
plt.imshow(image_flip)
这将产生以下输出:
你可以看到垂直翻转后的图像。让我们在两个轴上翻转原始图像:
# Flipping image (Horizontal and vertical flipping)
image_flip = cv2.flip(rgb_image, -1)
# Display the image
plt.imshow(image_flip)
这将产生以下输出:
你可以看到垂直和水平翻转后的图像。在翻转图像后,接下来让我们学习如何更改图像的亮度。
改变亮度
亮度是一个由视觉感知决定的相对概念。有时候很难感知亮度。像素强度的值可以帮助我们找到更亮的图像。例如,如果两个像素的强度值分别是 110 和 230,那么后者更亮。
在 OpenCV 中,调整图像亮度是一个非常基础的操作。亮度可以通过改变图像中每个像素的强度来控制:
# Import cv2 latest version of OpenCV library
import cv2
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# Magic function to render the figure in a notebook
%matplotlib inline
# Read image
image = cv2.imread('nature.jpeg')
# Convert image color space BGR to RGB
rgb_image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# Display the image
plt.imshow(rgb_image)
这将产生以下输出:
在前面的代码示例中,我们读取了图像并将基于 BGR 色彩模型的图像转换为基于 RGB 色彩模型的图像。接下来,让我们在以下代码块中更改图像的亮度:
# set weightage for alpha and betaboth the matrix
alpha_=1
beta_=50
# Add weight to the original image to change the brightness
image_change=cv2.addWeighted(rgb_image, alpha_,
np.zeros(image.shape,image.dtype),0, beta_)
# Display the image
plt.imshow(image_change)
这将产生以下输出:
在前面的示例中,我们使用addWeighted()函数将两个矩阵按给定的权重(alpha 和 beta)相加。addWeighted()函数需要以下参数:first_image、alpha、second_image、gamma和beta。在我们的示例中,first_image是输入图像,而second_image是空矩阵。alpha和beta的值是两个矩阵的权重,gamma为 0。
图像模糊
模糊是图像预处理中的一个关键步骤。在预处理过程中,去除噪声对算法的表现有很大影响。模糊是减少图像数据噪声的过程,以实现更好的准确性。模糊也帮助我们处理像素强度。
让我们看一个模糊图像的示例:
# Import OpenCV module
import cv2
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# Magic function to render the figure in a notebook
%matplotlib inline
# Read image
image = cv2.imread('tajmahal.jpg')
# Convert image color space BGR to RGB
rgb_image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# Display the image
plt.imshow(rgb_image)
这将产生以下输出:
在前面的代码示例中,我们读取了图像并将其从 BGR 转换为 RGB 图像。现在,让我们使用blur()函数对其进行模糊处理。blur()函数需要两个参数:图像和内核大小。blur()函数使用平均模糊方法:
# Blur the image using blur() function
image_blur = cv2.blur(rgb_image,(15,15))
# Display the image
plt.imshow(image_blur)
这将产生以下输出:
在前面的示例中,我们读取了图像,使用cvtColor()函数将 BGR 转换为 RGB 颜色,并显示了图像。在这里,我们使用blur()函数对图像进行了模糊处理。blur()函数应用了均值模糊,使用了归一化的盒式滤波器。blur()函数接受以下参数:图像和内核大小。
我们已经看到了使用均值模糊处理的图像。让我们探索使用高斯模糊进行模糊处理。在这种模糊处理中,使用了高斯核而不是盒式滤波器。GaussianBlur()将接受图像和内核大小。内核大小将是一个由宽度和高度组成的元组。宽度和高度必须是正数且为奇数:
# Import cv2 module
import cv2
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# magic function to render the figure in a notebook
%matplotlib inline
# read image
image = cv2.imread('tajmahal.jpg')
# Convert image color space BGR to RGB
rgb_image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# Blurring the image using Gaussian Blur
image_blur = cv2.GaussianBlur(rgb_image, (7,7), 0)
# Display the image
plt.imshow(image_blur)
这将产生以下输出:
让我们探索给定图像的中值模糊。中值模糊将内核区域的像素取出,并将中央元素替换为中位值。medianBlur()将接受图像和内核大小作为参数。建议内核大小应该是一个大于 1 的奇数,例如 3、5、7、9、11 等:
# Import cv2 module
import cv2
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# Convert image color space BGR to RGB
%matplotlib inline
# read image
image = cv2.imread('tajmahal.jpg')
# Convert image color space BGR to RGB
rgb_image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# Blurring the image using Median blurring
image_blur = cv2.medianBlur(rgb_image,11)
# Display the image
plt.imshow(image_blur)
这将产生以下输出:
在前面的代码块中,我们使用中值模糊对图像进行了模糊处理。在这里,我们使用了medianBlur()方法进行中值模糊处理,并且可以在输出中观察到模糊后的图像。在本节中,我们讨论了均值模糊、高斯模糊和中值模糊技术。在下一节中,我们将学习如何在图像中检测人脸。
人脸检测
现在,大家都在使用 Facebook,你们一定都见过 Facebook 上的图像人脸识别。人脸识别能够识别出一张脸属于谁,而人脸检测仅仅是找出图像中的人脸,也就是说,人脸检测并不确定被检测到的脸属于谁。人脸检测在许多应用中都很常见;例如,统计图像中的人数。在人脸检测中,算法试图在数字图像中找到人脸。
人脸检测是一个分类问题。我们可以将图像分为两类,脸或非脸。我们需要大量的图像来训练这样的分类模型。幸运的是,OpenCV 提供了预训练的模型,如 Haar 特征级联分类器和局部二值模式(LBP)分类器,这些分类器在数千张图像上进行了训练。在我们的示例中,我们将使用 Haar 特征提取来检测人脸。让我们看看如何使用 OpenCV 在图像中捕捉人脸:
- 读取图像并将其转换为灰度图:
# Import cv2 latest version of OpenCV library
import cv2
# Import numeric python (NumPy) library
import numpy as np
# Import matplotlib for showing the image
import matplotlib.pyplot as plt
# magic function to render the figure in a notebook
%matplotlib inline
# Read image
image= cv2.imread('messi.png')
# Convert image color space BGR to grayscale
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Displaying the grayscale image
plt.imshow(image_gray, cmap='gray')
这将产生以下输出:
在前面的代码示例中,我们读取了莱昂内尔·梅西的图像,并使用cvtColor()函数将其转换为灰度图像。
让我们在生成的灰度图像中找到人脸:
- 加载 Haar 级联人脸分类器文件:
# Load the haar cascade face classifier file
haar_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
- 获取图像中所有人脸的坐标:
# Get the faces coordinates for all the faces in the image
faces_cordinates = haar_cascade.detectMultiScale(image_gray, scaleFactor = 1.3, minNeighbors = 7);
- 在检测到的面部上绘制矩形:
# Draw rectangle on detected faces
for (p,q,r,s) in faces_cordinates:
cv2.rectangle(image, (p, q), (p+r, q+s), (255,255,0), 2)
- 将图像颜色空间从 BGR 转换为 RGB 并显示图像:
# Convert image color space BGR to RGB
image_rgb=cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# Display face detected image
plt.imshow(image_rgb)
这将得到以下输出:
在前面的示例中,我们将 BGR 图像转换为灰度图像。OpenCV 已经预先训练了面部、眼睛和微笑检测的分类器。我们可以使用预训练的面部级联分类器 XML 文件(haarcascade_frontalface_default.xml)。你可以从官方 Git 仓库获取分类器文件(haarcascade_frontalface_default.xml):github.com/opencv/opencv/tree/master/data/haarcascades,或者你也可以从我们的 GitHub 仓库获取: github.com/PacktPublishing/Python-Data-Analysis-Third-Edition/tree/master/Chapter13。
完成后,我们可以将图像传递给级联分类器,并获取图像中的面部坐标。我们已经使用rectangle()函数在这些面部坐标上绘制了矩形。显示输出之前,我们需要将 RGB 图像转换为 BGR,以便正确显示它。让我们在一张包含多个面部的图像上尝试这个示例:
# Read the image
image= cv2.imread('barcelona.jpeg')
# Convert image BGR to grayscale
image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Load the haar cascade face classifier file
haar_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
# Get the faces coordinates for all the faces in the image
faces_cordinates = haar_cascade.detectMultiScale(image_gray, scaleFactor = 1.3, minNeighbors = 5);
# Draw rectangle on detected faces
for (x1,y1,x2,y2) in faces_cordinates:
cv2.rectangle(image, (x1, y1), (x1+x2, y1+y2), (255,255,0), 2)
# Convert image color space BGR to RGB
image_rgb=cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# Display face detected the image
plt.imshow(image_rgb)
这将得到以下输出:
在前面的示例中,我们可以看到程序已经检测到图像中的所有面部。
总结
本章讨论了使用 OpenCV 进行图像处理。本章的主要内容是基础的图像处理操作和面部检测。章节开始时介绍了图像类型和图像颜色模型。在后续部分,重点讨论了图像操作,例如绘制、调整大小、翻转和模糊图像。在最后部分,我们讨论了在给定输入图像中的面部检测。
下一章,第十四章,使用 Dask 进行并行计算,将重点讨论使用 Dask 对基本的数据科学 Python 库(如 Pandas、NumPy 和 scikit-learn)进行并行计算。章节将从 Dask 的数据类型(如数据框、数组和袋子)开始。在后续部分,我们将把重点从数据框和数组转移到使用 Dask 进行并行延迟、预处理和机器学习算法。