Sklearn 机器学习快速启动指南(二)
原文:
annas-archive.org/md5/88cc1599da7fc47c30a7a809cfd13bb1译者:飞龙
第七章:使用无监督机器学习进行数据聚类
您在实际应用中遇到的大部分数据都不会带有标签。如果数据没有标签,您无法应用有监督的机器学习技术。无监督机器学习通过将数据分组为聚类来解决此问题;然后我们可以基于这些聚类分配标签。
一旦数据被聚类成特定数量的组,我们就可以继续为这些组分配标签。无监督学习是您作为数据科学家需要实施的第一步,之后才能应用有监督的机器学习技术(如分类)进行有意义的预测。
无监督机器学习算法的一个常见应用是客户数据,这些数据可以在各行各业中找到。作为数据科学家,您的工作是找到可以细分的客户群体,并向其推送有针对性的产品和广告。
在本章中,您将学习以下主题:
-
k-means 算法及其内部工作原理,用于对无标签数据进行聚类
-
在 scikit-learn 中实现 k-means 算法
-
使用特征工程优化无监督机器学习
-
聚类可视化
-
从无监督学习到有监督学习
技术要求
您需要在系统中安装 Python 3.6 或更高版本,Pandas ≥ 0.23.4,Scikit-learn ≥ 0.20.0,NumPy ≥ 1.15.1,Matplotlib ≥ 3.0.0,Pydotplus ≥ 2.0.2,Image ≥ 3.1.2,Seaborn ≥ 0.9.0 和 SciPy ≥ 1.1.0。
本章的代码文件可以在 GitHub 上找到:
github.com/PacktPublishing/Machine-Learning-with-scikit-learn-Quick-Start-Guide/blob/master/Chapter_07.ipynb.
请观看以下视频,查看代码的实际应用:
k-means 算法
在本节中,您将学习 k-means 算法的工作原理,以便将数据聚类成有逻辑意义的组。
让我们考虑一组点,如下图所示:
一组随机点
质心分配
算法的第一步是分配一组随机质心。假设我们要找到两个不同的聚类或组,算法可以分配两个质心,如下图所示:
由星号表示的质心
在前面的图示中,星号代表算法的质心。请注意,在这种情况下,聚类的中心完美地符合两个不同的组。这是最理想的情况。实际上,均值(或质心)是随机分配的,并且在每次迭代中,聚类的质心都会向两个组的中心靠近。
这个算法被称为 k-means 算法,因为我们试图找到一组点的均值作为质心。由于均值只能针对一组数值点计算,因此这种聚类算法只能处理数值数据。
实际上,将这些点分组为两个不同的聚类并不像看起来那么简单。该过程的可视化表示可以如下所示:
k-means 算法中分配质心的过程
在前面的图示中,随机分配质心的过程从左上角开始。随着我们向下并朝右上角移动,请注意质心如何逐渐靠近两个不同组的中心。实际上,算法没有一个最佳的终止点来停止迭代。
算法什么时候停止迭代?
通常,算法会寻找两个度量标准,以停止迭代过程:
-
形成的不同组(或聚类)之间的距离
-
每个点与聚类质心之间的距离
聚类形成的最佳情况是,当不同组或聚类之间的距离尽可能大,而每个点与聚类质心之间的距离尽可能小。
在 scikit-learn 中实现 k-means 算法
现在你已经理解了 k-means 算法的内部工作原理,我们可以继续在 scikit-learn 中实现它。我们将使用在之前章节中使用的相同的欺诈检测数据集。关键的区别是,我们将丢弃包含标签的目标特征,并识别用于检测欺诈的两个聚类。
创建基础 k-means 模型
为了将数据集加载到工作空间并丢弃包含标签的目标特征,我们使用以下代码:
import pandas as pd
#Reading in the dataset
df = pd.read_csv('fraud_prediction.csv')
#Dropping the target feature & the index
df = df.drop(['Unnamed: 0', 'isFraud'], axis = 1)
接下来,我们可以实现具有两个聚类均值的 k-means 算法。选择使用两个聚类均值是任意的,因为我们知道应该有两个不同的聚类,分别对应两个标签:欺诈和非欺诈交易。我们可以通过以下代码来实现:
from sklearn.cluster import KMeans
#Initializing K-means with 2 clusters
k_means = KMeans(n_clusters = 2)
#Fitting the model on the data
k_means.fit(df)
在之前的代码中,首先,我们从 scikit-learn 中导入KMeans包并初始化一个具有两个聚类的模型。然后,我们使用.fit()函数将该模型拟合到数据上。这将产生一组标签作为输出。我们可以使用以下代码提取这些标签:
#Extracting labels
target_labels = k_means.predict(df)
#Printing the labels
target_labels
上述代码生成的输出是每个移动交易的标签数组,如下所示:
标签数组
现在我们有了一组标签,我们知道每个交易属于哪个聚类。标签为 0 的移动交易属于一组,而标签为 1 的交易属于第二组。
最佳的聚类数量
在解释 k-means 算法如何工作的过程中,我们提到过,当算法找到最佳的聚类数量时,它会终止。然而,当使用 scikit-learn 随机选择聚类时,这种情况并不总是成立。在这种情况下,我们需要找到最佳的聚类数量。
我们可以通过一种被称为惯性的度量来实现这一点。惯性度量的是聚类中数据点与其质心的接近程度。显然,较低的惯性意味着组或聚类紧密地聚集在一起,这样是比较好的。
为了计算模型的惯性值,我们使用以下代码:
# Inertia of present model
k_means.inertia_
上述代码产生了一个惯性值为 4.99 × 10 ^ 17,这个值相对于其他不同聚类数所产生的惯性值来说极大(后面会解释),因此不是一个好的惯性值。这表明个别数据点分布较广,并没有紧密地聚集在一起。
在大多数情况下,我们并不确切知道最佳的聚类数量,因此我们需要为不同的聚类数绘制惯性得分。我们可以通过以下代码来实现:
import matplotlib.pyplot as plt
import seaborn as sns
#Initialize a list of clusters from 1 to 10 clusters
clusters = [1,2,3,4,5,6,7,8,9,10]
#Create an empty list in order to store the inertia values
inertia_values = []
for cluster in clusters:
#Build a k-means model for each cluster value
k_means = KMeans(n_clusters = cluster)
#Fit the model to the data
k_means.fit(df)
# Store inertia value of each model into the empty list
inertia_values.append(k_means.inertia_)
# Plot the result
plt.lineplot(x = clusters, y = inertia_values)
plt.xlabel('Number of Clusters')
plt.ylabel('Inertia Value')
plt.title('Number of Clusters Vs. Inertia Values')
plt.show()
这将产生以下图表:
惯性作为聚类数的函数
在上面的代码中,首先,我们创建了一个包含 1 到 10 值的聚类列表。每个值表示将用于机器学习模型中的聚类数量。接着,我们创建了一个空列表,用来存储每个模型所产生的惯性值。
接下来,我们遍历聚类列表,并为列表中每个聚类值构建并评估一个 k-means 模型。每个模型现在会产生一个惯性值,该值会存储在我们在代码块开始时初始化的列表中。然后,使用 matplotlib 绘制一个简单的折线图,x 轴为聚类数,y 轴为相应的惯性值。
该图表告诉我们,当聚类数为 10 时,惯性值最低。然而,拥有过多的聚类也是我们需要避免的事情,因为过多的组别并不能帮助我们很好地进行泛化,而且每个组别的特征会变得非常具体。
因此,选择问题的最佳聚类数量的理想方式是,假设我们事先没有关于我们想要的组数的先验信息,找到图中的肘部点。
肘部点是惯性值减少速率减缓的那个点。肘部点在下图中得到了说明:
图表的肘部点
在前面的图表中,很明显肘部点对应于四个聚类。这可能意味着除了标准的“欺诈”和“非欺诈”分类外,存在四种不同类型的欺诈交易。然而,由于我们事先知道数据集有一个二元目标特征,包含两个类别,我们不会深入探讨为什么四个聚类是该数据集的理想聚类数。
优化的特征工程
数据集中的特征工程是一个基本概念,用于提高模型的性能。将特征调整到算法设计的最佳状态是有益的,因为它可以提高准确性,同时减少泛化误差。你将学习到的几种用于优化数据集的特征工程技术如下:
-
缩放
-
主成分分析
缩放
缩放是标准化数据的过程,使每个特征下的值落在某个特定范围内,如-1 到+1. 为了缩放数据,我们用某一特征的每个值减去该特征的均值,再除以该特征的方差。为了缩放我们欺诈检测数据集中的特征,我们使用以下代码:
from sklearn.preprocessing import StandardScaler
#Setting up the standard scaler
scale_data = StandardScaler()
#Scaling the data
scale_data.fit(df)
df_scaled = scale_data.transform(df)
#Applying the K-Means algorithm on the scaled data
#Initializing K-means with 2 clusters
k_means = KMeans(n_clusters = 2)
#Fitting the model on the data
k_means.fit(df_scaled)
# Inertia of present model
k_means.inertia_
在前面的代码中,我们使用StandardScalar()函数来缩放我们的数据框,然后我们在缩放后的数据上构建了一个包含两个聚类的 k-means 模型。评估模型的惯性后,输出的值为 295,000,明显优于没有缩放时模型输出的4.99 × 10¹⁷。
然后,我们可以使用与之前相同的代码绘制聚类数与惯性值的图表,唯一的不同是将原始数据框替换为缩放后的数据框:
#Initialize a list of clusters from 1 to 10 clusters
clusters = [1,2,3,4,5,6,7,8,9,10]
#Create an empty list in order to store the inertia values
inertia_values = []
for cluster in clusters:
#Build a k-means model for each cluster value
k_means = KMeans(n_clusters = cluster)
#Fit the model to the data
k_means.fit(df_scaled)
# Store inertia value of each model into the empty list
inertia_values.append(k_means.inertia_)
# Plot the result
sns.lineplot(x = clusters, y = inertia_values)
plt.xlabel('Number of Clusters')
plt.ylabel('Inertia Value')
plt.title('Number of Clusters Vs. Inertia Values')
plt.show()
这将产生以下输出:
缩放后的聚类最佳数量
我们注意到,前面的图表并没有一个非常明显的肘部点,在这个点上惯性值的下降速率较低。然而,如果我们仔细观察,可以在 8 个聚类处找到这个点。
主成分分析
主成分分析(PCA)是降维的一个子集。降维是指减少对预测模型没有预测价值的特征的过程。我们还优化并提高了算法处理的计算效率。这是因为一个特征较少的数据集会让算法更容易更快地检测到模式。
PCA 的第一步叫做去相关化。相互高度相关的特征对预测模型没有价值。因此,在去相关化步骤中,PCA 将两个高度相关的特征的数据点展开,使其在轴上对齐,并且不再相关。这个过程可以如下图所示:
去相关化过程
一旦特征被去相关化,主成分(或特征)就会从数据中提取出来。这些特征具有较高的方差,并且提供了对预测模型最有价值的信息。方差较低的特征会被丢弃,因此数据集的维度数量减少。
为了使用 PCA 进行降维,我们使用以下代码:
from sklearn.decomposition import PCA
#Initialize a PCA model with 5 features
pca_model = PCA(n_components = 5)
#Fit the model to the scaled dataframe
pca_model.fit(df_scaled)
#Transform the features so that it is de-correlated
pca_transform = pca_model.transform(df_scaled)
#Check to see if there are only 5 features
pca_transform.shape
在前面的代码中,首先,我们从 scikit-learn 导入PCA方法。接下来,我们初始化一个具有五个主成分的 PCA 模型。在这里,我们指定要将数据集减少到仅包含五个最重要的特征。
然后,我们将 PCA 模型拟合到数据框并进行转换,以获得去相关的特征。检查最终特征数组的形状,我们可以看到它只有五个特征。最后,我们使用仅包含主成分特征的 k-means 模型,如下所示的代码所示:
#Applying the K-Means algorithm on the scaled data
#Initializing K-means with 2 clusters
k_means = KMeans(n_clusters = 2)#Fitting the model on the data
k_means.fit(pca_transform)
# Inertia of present model
k_means.inertia_
评估新模型的惯性改善了其性能。我们得到了比缩放模型更低的惯性值。现在,让我们评估不同主成分或特征数量的惯性分数。为此,我们使用以下代码:
#Initialize a list of principal components
components = [1,2,3,4,5,6,7,8,9,10]
#Create an empty list in order to store the inertia values
inertia_values = []
for comp in components:
#Initialize a PCA model
pca_model = PCA(n_components = comp)
#Fit the model to the dataframe
pca_model.fit(df_scaled)
#Transform the features so that it is de-correlated
pca_transform = pca_model.transform(df_scaled)
#Build a k-means model
k_means = KMeans(n_clusters = 2)
#Fit the model to the data
k_means.fit(pca_transform)
# Store inertia value of each model into the empty list
inertia_values.append(k_means.inertia_)
# Plot the result
sns.lineplot(x = components, y = inertia_values)
plt.xlabel('Number of Principal Components')
plt.ylabel('Inertia Value')
plt.title('Number of Components Vs. Inertia Values')
plt.show()
在前面的代码中,适用以下内容:
-
首先,我们初始化一个列表,用于存储我们想用来构建模型的不同主成分值。这些值从 1 到 10。
-
接下来,我们初始化一个空的列表,用于存储每个模型的惯性值。
-
使用每个主成分值,我们构建一个新的 k-means 模型,并将该模型的惯性值附加到空列表中。
-
最后,绘制惯性值与不同主成分值之间的关系图。
该图像如下所示:
惯性值与主成分数量的关系
在前面的图中,可以清楚地看到,惯性值在一个主成分时最小。
聚类可视化
当数据集中的变量/维度非常多时,直观展示你的聚类是一个不容易的任务。有两种主要方法可以用来可视化聚类的分布,如下所示:
-
t-SNE:在二维空间中创建数据集的地图
-
层次聚类:使用基于树的可视化方式,称为树状图,来创建层次结构
在本节中,您将学习如何实现这些可视化技术,以创建引人注目的集群可视化效果。
t-SNE
t-SNE 是 t-分布随机邻域嵌入 的缩写。t-SNE 的基本概念是将高维度映射到二维空间。简单来说,如果您的数据集具有超过两个特征,t-SNE 将非常适合显示您的整个数据集如何在计算机屏幕上可视化!
第一步是实现 k-means 算法,并创建一组我们可以合并到未标记数据集中的预测标签。我们可以通过使用以下代码来实现这一点:
#Reading in the dataset
df = pd.read_csv('fraud_prediction.csv')
#Dropping the target feature & the index
df = df.drop(['Unnamed: 0', 'isFraud'], axis = 1)
#Initializing K-means with 2 clusters
k_means = KMeans(n_clusters = 2)
#Fitting the model on the data
k_means.fit(df)
#Extracting labels
target_labels = k_means.predict(df)
#Converting the labels to a series
target_labels = pd.Series(target_labels)
#Merging the labels to the dataset
df = pd.merge(df, pd.DataFrame(target_labels), left_index=True, right_index=True)
#Renaming the target
df['fraud'] = df[0]
df = df.drop([0], axis = 1)
暂时不要担心前面代码段的工作原理,因为在本章的后续部分中,我们将详细解释如何将无监督机器学习问题转换为监督学习问题。
接下来,我们将创建一个 t-SNE 对象,并将其拟合到我们仅包含特征的数据点数组中。然后,我们同时转换这些特征,以便可以在二维空间中查看所有特征。这在以下代码段中完成:
from sklearn.manifold import TSNE
#Creating the features
features = df.drop('fraud', axis = 1).values
target = df['fraud'].values
#Initialize a TSNE object
tsne_object = TSNE()
#Fit and transform the features using the TSNE object
transformed = tsne_object.fit_transform(features)
在前述代码中,以下内容适用:
-
首先,我们通过使用
TSNE()函数初始化 t-SNE 对象。 -
使用 t-SNE 对象,我们使用
fit_transform()方法对我们的特征数据进行拟合和转换。
接下来,我们使用以下代码创建 t-SNE 可视化:
#Creating a t-SNE visualization
x_axis = transformed[:,0]
y_axis = transformed[:,1]
plt.scatter(x_axis, y_axis, c = target)
plt.show()
在前述代码中,以下内容适用:
-
我们从转换特征集中提取第一和第二个特征,分别作为 x 轴和 y 轴。
-
然后,我们绘制散点图,并根据先前使用 k-means 算法生成的目标标签对其进行着色。这生成以下图表:
t-SNE 可视化
在前述图中,黄色表示被分配欺诈标签的交易,而紫色表示被分配非欺诈标签的交易。(请参考图像的彩色版本。)
层次聚类
如最初讨论的那样,层次聚类技术使用树状图来可视化集群或群组。为了解释树状图的工作原理,我们将考虑一个具有四个特征的数据集。
第一步 – 将每个特征作为单独的集群
在第一步中,数据集中的每个特征被认为是其自己的集群。这在以下图表中有所说明:
每个特征作为树状图中的单个集群
每个前面图表中的特征都是一个单独的集群,现阶段如此。此算法现在搜索找到彼此最接近的两个特征,并将它们合并成一个单独的集群。
第二步 – 合并
在这一步骤中,算法将两个最接近的特征中的数据点合并到一个聚类中。这个过程在下面的图示中有所体现:
特征合并为单一聚类的过程
在前面的图示中,很明显算法已经选择了特征 2和特征 3,并决定这两个特征下的数据是彼此最接近的。
第 3 步 – 迭代
现在,算法继续迭代合并特征,直到无法再形成任何聚类。最终形成的树状图如下所示:
在前面的图示中,特征 2和特征 3被归为一个单一的聚类。然后,算法决定特征 1和特征 2与特征 3的聚类是最接近的。因此,这三个特征被归为一个组。最后,特征 4与特征 3聚为一组。
实现层次聚类
现在你已经学习了层次聚类是如何工作的,我们可以实现这个概念。为了创建一个层次聚类,我们使用以下代码:
from scipy.cluster.hierarchy import linkage
from scipy.cluster.hierarchy import dendrogram
import numpy as np
import matplotlib.pyplot as plt
#Creating an array of 4 features
array = np.array([[1,2,3,4], [5,6,7,8], [2,3,4,5], [5,6,4,3]])
feature_names = ['a', 'b', 'c', 'd']
#Creating clusters
clusters = linkage(array, method = 'complete')
#Creating a dendrogram
dendrogram(clusters, labels = feature_names, leaf_rotation = 90)
plt.show()
前面的代码将生成一个树状图,如下面的图示所示:
树状图
在前面的代码中,以下内容适用:
-
首先,我们创建一个包含四列的数组。
-
然后,我们使用
linkage函数创建聚类。在函数中,我们将method参数设置为 complete,以表示我们想要整个树状图。 -
最后,我们使用
dendrogram函数创建带有聚类的树状图。我们将标签名称设置为之前在代码中创建的特征名称列表。
从无监督学习到监督学习
无监督学习的最终目标是获取一个没有标签的数据集,并为数据集的每一行分配标签,以便我们可以通过它运行监督学习算法。这使我们能够创建利用这些标签的预测。
在本节中,你将学习如何将无监督机器学习算法生成的标签转换为一个使用这些标签的决策树。
创建标注数据集
第一步是将无监督机器学习算法(如 k-means 算法)生成的标签转换,并将其附加到数据集中。我们可以使用以下代码来实现:
#Reading in the dataset
df = pd.read_csv('fraud_prediction.csv')
#Dropping the target feature & the index
df = df.drop(['Unnamed: 0', 'isFraud'], axis = 1)
在前面的代码中,我们读取了欺诈检测数据集,并删除了目标列和索引列:
#Initializing K-means with 2 clusters
k_means = KMeans(n_clusters = 2)
#Fitting the model on the data
k_means.fit(df)
接下来,在前面的代码中,我们初始化并拟合了一个具有两个聚类的 k-means 模型:
#Extracting labels
target_labels = k_means.predict(df)
#Converting the labels to a series
target_labels = pd.Series(target_labels)
#Merging the labels to the dataset
df = pd.merge(df, pd.DataFrame(target_labels), left_index=True, right_index=True)
最终,我们通过使用predict()方法创建目标标签,并将其转换为pandas系列。然后我们将这个系列合并到数据框中,以便创建我们的标注数据集。
构建决策树
既然我们已经有了标注的数据集,我们可以创建一个决策树,将无监督学习问题转化为有监督学习问题。
为了做到这一点,我们从所有必要的包导入开始,如下所示的代码所示:
from sklearn.tree import DecisionTreeClassifier
from sklearn.externals.six import StringIO
from IPython.display import Image
from sklearn.tree import export_graphviz
import pydotplus
from sklearn import tree
接下来,我们将目标列重命名为适当的名称(当我们合并由 k-means 算法创建的目标标签时,默认生成了 0 作为名称)。我们可以通过以下代码来实现:
#Renaming the target
df['fraud'] = df[0]
df = df.drop([0], axis = 1)
接下来,我们使用以下代码构建决策树分类算法:
#Creating the features
features = df.drop('fraud', axis = 1).values
target = df['fraud'].values
#Initializing an empty DT classifier with a random state value of 42
dt_classifier = DecisionTreeClassifier(criterion = 'gini', random_state = 42)
#Fitting the classifier on the training data
dt_classifier.fit(features, target)
在前面的代码中,首先,我们创建了特征和目标变量,并初始化了一个决策树分类器。然后,我们将分类器拟合到特征和目标上。
最后,我们希望可视化决策树。我们可以通过以下代码来实现:
#Creating a data frame with the features only
features = df.drop('fraud', axis = 1)
dot_data = tree.export_graphviz(dt_classifier, out_file=None, feature_names= features.columns)
# Draw graph
graph = pydotplus.graph_from_dot_data(dot_data)
#Show graph
Image(graph.create_png())
这导致了下图所示的决策树:
创建的决策树的一部分
总结
在本章中,你了解了 k-means 算法的工作原理,以便将未标记的数据点聚类到不同的群组中。接着,你学会了如何使用 scikit-learn 实现这一点,并扩展了实现中的特征工程部分。
在学习如何使用层次聚类和 t-SNE 可视化聚类之后,你又学会了如何将多维数据集映射到二维空间。最后,你学习了如何使用决策树将无监督学习问题转化为有监督学习问题。
在接下来的(也是最后一章),你将学习如何正式评估到目前为止你所构建的所有机器学习算法的性能!
第八章:性能评估方法
性能评估的方法会根据你选择实现的机器学习算法类型有所不同。一般来说,针对分类、回归和无监督机器学习算法,会有不同的评估指标来衡量你的模型在特定任务上的表现。
在本章中,我们将探索不同的性能评估方法,帮助你更好地理解模型。章节将分为以下三个部分:
-
分类算法的性能评估
-
回归算法的性能评估
-
无监督算法的性能评估
技术要求
你需要在系统中安装 Python 3.6 或更高版本、Pandas ≥ 0.23.4、Scikit-learn ≥ 0.20.0、NumPy ≥ 1.15.1、Matplotlib ≥ 3.0.0,以及 Scikit-plot ≥ 0.3.7。
本章的代码文件可以在 GitHub 上找到:
查看以下视频,看看代码的实际效果:
为什么性能评估至关重要?
理解为什么我们首先需要评估模型的性能是非常关键的。以下是一些可能的原因,说明为什么性能评估至关重要:
-
它防止过拟合:过拟合发生在算法过度拟合数据,并做出仅针对一个数据集的特定预测。换句话说,模型无法将预测推广到它未接触过的数据。
-
它防止欠拟合:这恰恰与过拟合相反。在这种情况下,模型的性质非常通用。
-
理解预测:性能评估方法将帮助你更详细地了解模型是如何做出预测的,以及这些预测的性质和其他有用信息,例如模型的准确性。
分类算法的性能评估
为了评估分类算法的性能,我们可以考虑在本书中构建的两种分类算法:k 最近邻和逻辑回归。
第一步是将这两种算法实现到欺诈检测数据集中。我们可以通过以下代码来实现:
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn import linear_model
#Reading in the fraud detection dataset
df = pd.read_csv('fraud_prediction.csv')
#Creating the features
features = df.drop('isFraud', axis = 1).values
target = df['isFraud'].values
#Splitting the data into training and test sets
X_train, X_test, y_train, y_test = train_test_split(features, target, test_size = 0.3, random_state = 42, stratify = target)
# Building the K-NN Classifier
knn_classifier = KNeighborsClassifier(n_neighbors=3)
knn_classifier.fit(X_train, y_train)
#Initializing an logistic regression object
logistic_regression = linear_model.LogisticRegression()
#Fitting the model to the training and test sets
logistic_regression.fit(X_train, y_train)
在前面的代码中,我们将欺诈检测数据集读取到笔记本中,并将数据分成特征和目标变量,和往常一样。然后,我们将数据分为训练集和测试集,并在训练数据中构建 k 最近邻和逻辑回归模型。
在本节中,您将学习如何评估单一模型的性能:k-最近邻。您还将学习如何比较和对比多个模型。因此,您将学习以下内容:
-
混淆矩阵
-
规范化混淆矩阵
-
曲线下面积(
auc分数) -
累积增益曲线
-
提升曲线
-
K-S 统计量图
-
校准图
-
学习曲线
-
交叉验证箱线图
本节中的一些可视化图表将需要一个名为 scikit-plot 的包。scikit-plot 包非常有效,专门用于可视化机器学习模型的各种性能指标。它特别为使用 scikit-learn 构建的模型而设计。
为了在本地机器上安装 scikit-plot,可以在终端使用 pip 安装,命令如下:
pip3 install scikit-plot
如果您使用的是 Anaconda 发行版来管理您的 Python 包,可以通过以下代码安装 scikit-plot:
conda install -c conda-forge scikit-plot
混淆矩阵
直到现在,我们一直使用准确率作为唯一的模型性能衡量标准。这是可以的,因为我们有一个平衡的数据集。平衡数据集是指每个类别的标签数量几乎相等。在我们正在处理的数据集中,8,000 个标签属于欺诈交易,12,000 个标签属于非欺诈交易。
假设有这样一种情况:90%的数据是非欺诈交易,只有 10%的交易是欺诈案件。如果分类器报告的准确率为 90%,这就没有意义,因为它所看到的大部分数据是非欺诈案件,而它看到的欺诈案件非常少。所以,即使它准确地分类了 90%的案件,也意味着它所分类的大多数案件将属于非欺诈案件。这对我们没有任何价值。
混淆矩阵是一种性能评估技术,可以用于处理数据集不平衡的情况。我们数据集的混淆矩阵如下所示:
欺诈交易的混淆矩阵
混淆矩阵的目标是最大化真正例和真负例的数量,因为这能给出正确的预测;它还最小化假阴性和假阳性的数量,因为它们给出的是错误的预测。
根据您的问题,假阳性可能比假阴性更为问题(反之亦然),因此,构建正确分类器的目标应该是以最佳的方式解决您的问题。
为了在 scikit-learn 中实现混淆矩阵,我们使用以下代码:
from sklearn.metrics import confusion_matrix
#Creating predictions on the test set
prediction = knn_classifier.predict(X_test)
#Creating the confusion matrix
print(confusion_matrix(y_test, prediction))
这将产生如下输出:
我们分类器输出的欺诈交易混淆矩阵
在前面的代码中,我们使用 .predict() 方法对测试训练数据生成一组预测结果,然后对目标变量的测试集和之前创建的预测结果使用 confusion_matrix() 函数。
前面的混淆矩阵看起来几乎完美,因为大多数情况都被分类为真正例和真反例,沿着主对角线排列。只有 46 个案例被错误分类,而且这个数字几乎相等。这意味着假阳性和假阴性的数量最小且平衡,两者之间没有明显的偏向。这是理想分类器的一个例子。
从混淆矩阵中可以推导出的另外三个度量标准是精准率、召回率和F1 分数。较高的精准率表示较少的非欺诈交易被误分类为欺诈交易,而较高的召回率则表示大部分欺诈交易被正确预测。
F1 分数是精准率和召回率的加权平均值。
我们可以使用以下代码计算精准率和召回率:
from sklearn.metrics import classification_report
#Creating the classification report
print(classification_report(y_test, prediction))
这将产生以下输出:
分类报告
在前面的代码中,我们使用 classification_report() 函数,传入两个参数:目标变量的测试集和我们之前为混淆矩阵创建的预测变量。
在输出中,精准率、召回率和 F1 分数都很高,因为我们已经构建了理想的机器学习模型。这些值的范围从 0 到 1,1 为最高。
标准化混淆矩阵
标准化混淆矩阵使数据科学家更容易直观地理解标签是如何被预测的。为了构建标准化混淆矩阵,我们使用以下代码:
import matplotlib.pyplot as plt
import scikitplot as skplt
#Normalized confusion matrix for the K-NN model
prediction_labels = knn_classifier.predict(X_test)
skplt.metrics.plot_confusion_matrix(y_test, prediction_labels, normalize=True)
plt.show()
这将产生以下标准化混淆矩阵:
K-NN 模型的标准化混淆矩阵
在前面的图中,预测标签位于 x 轴,而真实(或实际)标签位于 y 轴。我们可以看到,该模型对欺诈交易的预测错误率为 0.01,即 1%,而 99%的欺诈交易预测正确。我们还可以看到,K-NN 模型对所有非欺诈交易的预测准确率达到了 100%。
现在,我们可以通过使用标准化混淆矩阵来比较逻辑回归模型的表现,如下所示:
#Normalized confusion matrix for the logistic regression model
prediction_labels = logistic_regression.predict(X_test)
skplt.metrics.plot_confusion_matrix(y_test, prediction_labels, normalize=True)
plt.show()
这将产生以下标准化混淆矩阵:
逻辑回归模型的标准化混淆矩阵
在前面的混淆矩阵中,可以明显看出,逻辑回归模型仅正确预测了 42%的非欺诈交易。这几乎立刻表明,K-NN 模型的表现更好。
曲线下的面积
这个曲线在本例中是接收者操作特征(ROC)曲线。这是一个表示真实正例率与假正例率之间关系的图。我们可以通过以下方式绘制该曲线:
from sklearn.metrics import roc_curve
from sklearn.metrics import roc_auc_score
import matplotlib.pyplot as plt
#Probabilities for each prediction output
target_prob = knn_classifier.predict_proba(X_test)[:,1]
#Plotting the ROC curve
fpr, tpr, thresholds = roc_curve(y_test, target_prob)
plt.plot([0,1], [0,1], 'k--')
plt.plot(fpr, tpr)
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve')
plt.show()
这会生成如下曲线:
ROC 曲线
在前面的代码中,首先,我们为每个预测标签创建一组概率。例如,预测标签 1 会有一组与之相关的概率,而标签 0 则有另一组概率。利用这些概率,我们使用roc_curve()函数,并结合目标测试集,来生成 ROC 曲线。
上面的曲线是一个完美的 ROC 曲线示例。该曲线的真实正例率为 1.0,表示预测准确,而假正例率为 0,表示没有错误预测。
这样的曲线通常具有最大的曲线下方区域,与准确度较低的模型曲线相比。为了计算曲线下面积得分,我们使用以下代码:
#Computing the auc score
roc_auc_score(y_test, target_prob)
这会产生 0.99 的得分。较高的auc得分表示模型表现更好。
累积增益曲线
在构建多个机器学习模型时,了解哪个模型产生了你期望的预测类型非常重要。累积增益曲线可以帮助你进行模型比较,通过告诉你某个类别/类在特定模型的样本数据中占据的百分比。
简单来说,在欺诈检测数据集中,我们可能希望选择一个能够预测更多欺诈交易的模型,而不是一个无法做到这一点的模型。为了构建 k 最近邻模型的累积增益图,我们使用以下代码:
import scikitplot as skplt
target_prob = knn_classifier.predict_proba(X_test)
skplt.metrics.plot_cumulative_gain(y_test, target_prob)
plt.show()
这会生成以下图形:
k 最近邻模型的累积增益图
在前面的代码中,以下内容适用:
-
首先,我们导入
scikit-plot包,它用于生成前面的图。然后,我们计算目标变量的概率,在本例中,这些概率表示某个特定移动交易是否为欺诈交易的可能性,基于测试数据。 -
最后,我们在这些概率和测试数据的目标标签上使用
plot_cumulative_gain()函数,以生成前面的图。
我们如何解读前面的图形?我们只需寻找一个点,在该点上,数据中的某个百分比包含了 100%的目标类别。下图为此示意:
100%目标类别存在的点
前图中的点对应于x轴上的 0.3 和y轴上的 1.0。这意味着 30%到 100%的数据将包含目标类 1,即欺诈交易。
这也可以这样解释:如果你使用 K 最近邻模型,70%的数据将包含 100%的欺诈交易预测。
现在,让我们计算逻辑回归模型的累积增益曲线,看看它是否有所不同。为此,我们使用以下代码:
#Cumulative gains plot for the logistic regression model
target_prob = logistic_regression.predict_proba(X_test)
skplt.metrics.plot_cumulative_gain(y_test, target_prob)
plt.show()
这产生了以下图形:
逻辑回归模型的累积增益图
前面的图与 K-NN 模型先前生成的累积增益图相似,都是 70%的数据包含 100%的目标类。因此,使用 K-NN 模型或逻辑回归模型都会得到类似的结果。
然而,使用累积增益图来比较不同模型的表现是一个好的实践,这样可以从根本上理解模型如何进行预测。
提升曲线
提升曲线可以告诉你,通过使用机器学习模型进行预测的效果如何,相较于不使用模型的情况。为了构建 K 最近邻模型的提升曲线,我们使用以下代码:
# Lift curve for the K-NN model
target_prob = knn_classifier.predict_proba(X_test)
skplt.metrics.plot_lift_curve(y_test, target_prob)
plt.show()
这产生了以下图形:
K-NN 模型的提升曲线
我们如何解读前面的提升曲线呢?我们需要找到曲线下滑的点。以下图为您展示了这个点:
提升曲线中的兴趣点
在前面的图中,突出显示的点是我们在任何提升曲线中都要寻找的点。这个点告诉我们,当使用 K-NN 预测模型时,0.3 或 30%的数据比完全不使用任何模型预测欺诈交易时,表现要好 3.5 倍。
现在,我们可以构建逻辑回归模型的提升曲线,以便比较两种模型的表现。我们可以通过使用以下代码来实现:
#Cumulative gains plot for the logistic regression model
target_prob = logistic_regression.predict_proba(X_test)
skplt.metrics.plot_lift_curve(y_test, target_prob)
plt.show()
这产生了以下图形:
逻辑回归模型的提升曲线
尽管该图告诉我们 30%的数据会看到性能改善(类似于我们之前构建的 K-NN 模型来预测欺诈交易),但在预测非欺诈交易时(蓝线)存在差异。
对于少部分数据,非欺诈交易的提升曲线实际上低于基线(虚线)。这意味着在预测非欺诈交易时,逻辑回归模型在少数数据上的表现甚至不如不使用预测模型。
K-S 统计图
K-S 统计图,或称科尔莫哥洛夫-斯米尔诺夫统计图,是一种能够告诉你模型是否在预测数据集中不同标签时产生混淆的图。为了说明在这种情况下“混淆”是什么意思,我们将通过以下代码为 K-NN 模型构建 K-S 统计图:
#KS plot for the K-NN model
target_proba = knn_classifier.predict_proba(X_test)
skplt.metrics.plot_ks_statistic(y_test, target_proba)
plt.show()
这将产生以下图:
K-NN 模型的 K-S 统计图
在前面的图中,以下内容适用:
-
虚线表示欺诈交易(底部的黄色线)和非欺诈交易(顶部的蓝色线)预测之间的距离。这一距离为 0.985,如图所示。
-
K-S 统计得分接近 1 通常是一个很好的指标,表明模型在预测这两种不同的目标标签时没有混淆,并且在预测标签时可以清楚地区分它们。
-
在前面的图中,可以观察到 0.985 的得分为两类预测之间的差异,最大可达 70%(0.7)的数据。这个差异可以在X轴上看到,因为 0.7 的阈值仍然具有最大分离距离。
现在我们可以计算逻辑回归模型的 K-S 统计图,以便比较这两个模型在预测两个类别标签之间的区分能力。我们可以通过以下代码来实现:
#KS plot for the logistic regression model
target_proba = logistic_regression.predict_proba(X_test)
skplt.metrics.plot_ks_statistic(y_test, target_proba)
plt.show()
这将产生以下图:
逻辑回归模型的 K-S 统计图
尽管两个模型的分离分数相同,都是 0.985,但分离发生的阈值却有所不同。在逻辑回归模型中,这一距离仅出现在数据的下 43%部分,因为最大分离开始的阈值为 0.57,沿着X轴。
这意味着 K 近邻模型对于大约 70%的总数据,其距离较大,在预测欺诈交易时要比其他模型更为准确。
校准图
校准图,顾名思义,是用来告诉你模型的校准情况。一个校准良好的模型,其预测得分应该等于正类的比例(在此例中为欺诈交易)。为了绘制校准图,我们使用以下代码:
#Extracting the probabilites that the positive class will be predicted
knn_proba = knn_classifier.predict_proba(X_test)
log_proba = logistic_regression.predict_proba(X_test)
#Storing probabilities in a list
probas = [knn_proba, log_proba]
# Storing the model names in a list
model_names = ["k_nn", "Logistic Regression"]
#Creating the calibration plot
skplt.metrics.plot_calibration_curve(y_test, probas, model_names)
plt.show()
这将产生以下校准图:
两个模型的校准图
在前面的代码中,以下内容适用:
-
首先,我们计算每个模型预测正类(欺诈交易)的概率。
-
然后,我们将这些概率和模型名称存储在一个列表中。
-
最后,我们使用
scikit-plot包中的plot_calibration_curve()函数,结合这些概率、测试标签和模型名称,来创建校准图。
这将生成前面的校准图,解释如下:
-
虚线代表完美的校准图。这是因为在每个点,平均预测值的准确度与正类的比例完全一致。
-
从图中可以看出,k 近邻模型的校准效果明显优于逻辑回归模型的校准图。
-
这是因为 k 近邻模型的校准图比逻辑回归模型的校准图更接近理想的校准图。
学习曲线
学习曲线是一个图表,用于比较随着样本/行数的增加,训练准确率和测试准确率的变化。为了构建 K 近邻模型的学习曲线,我们使用以下代码:
skplt.estimators.plot_learning_curve(knn_classifier, features, target)
plt.show()
这将生成以下图表:
K-NN 模型的学习曲线
在前面的曲线中,以下内容适用:
-
当样本数为 15,000 时,训练得分和测试得分才是最高的。这表明即使我们只有 15,000 个样本(而不是 17,500 个),我们仍然能够得到最好的结果。
-
任何少于 15,000 个样本的情况都会导致测试的交叉验证得分远低于训练得分,表明模型出现过拟合。
交叉验证箱形图
在这张图中,我们通过使用箱形图比较多个模型的交叉验证准确率得分。为此,我们使用以下代码:
from sklearn import model_selection
#List of models
models = [('k-NN', knn_classifier), ('LR', logistic_regression)]
#Initializing empty lists in order to store the results
cv_scores = []
model_name_list = []
for name, model in models:
#5-fold cross validation
cv_5 = model_selection.KFold(n_splits= 5, random_state= 50)
# Evaluating the accuracy scores
cv_score = model_selection.cross_val_score(model, X_test, y_test, cv = cv_5, scoring= 'accuracy')
cv_scores.append(cv_score)
model_name_list.append(name)
# Plotting the cross-validated box plot
fig = plt.figure()
fig.suptitle('Boxplot of 5-fold cross validated scores for all the models')
ax = fig.add_subplot(111)
plt.boxplot(cv_scores)
ax.set_xticklabels(model_name_list)
plt.show()
这将生成以下图表:
交叉验证箱形图
在前面的代码中,以下内容适用:
-
首先,我们将要比较的模型存储在一个列表中。
-
然后,我们初始化两个空列表,用于存储交叉验证准确率得分和模型名称的结果,以便后续使用,以便生成箱形图。
-
然后,我们遍历模型列表中的每个模型,使用
model_selection.KFold()函数将数据划分为五折交叉验证集。 -
接下来,我们通过使用
model_selection.cross_val_scores()函数提取五折交叉验证得分,并将得分和模型名称添加到我们在代码开头初始化的列表中。使用
model_selection.cross_val_scores()函数,并将得分与模型名称一起追加到我们在代码开头初始化的列表中。 -
最后,创建了一个箱形图,展示了交叉验证得分的箱形图。
我们创建的列表由五个交叉验证得分和模型名称组成。箱线图将这五个得分应用于每个模型,计算最小值、最大值、中位数、第一个四分位数和第三个四分位数,并以箱线图的形式展示。
在前面的图中,以下内容适用:
-
很明显,K-NN 模型的准确度最高,并且最小值与最大值之间的差异最小。
-
另一方面,逻辑回归模型在最小值和最大值之间的差异最大,并且在其准确度得分中也存在异常值。
回归算法的性能评估
有三个主要指标可以用来评估你构建的回归算法的性能,具体如下:
-
均方绝对误差 (MAE)
-
均方误差 (MSE)
-
均方根误差 (RMSE)
在本节中,您将了解这三种指标是什么,它们是如何工作的,以及如何使用 scikit-learn 实现它们。第一步是构建线性回归算法。我们可以通过使用以下代码来实现:
## Building a simple linear regression model
#Reading in the dataset
df = pd.read_csv('fraud_prediction.csv')
#Define the feature and target arrays
feature = df['oldbalanceOrg'].values
target = df['amount'].values
#Initializing a linear regression model
linear_reg = linear_model.LinearRegression()
#Reshaping the array since we only have a single feature
feature = feature.reshape(-1, 1)
target = target.reshape(-1, 1)
#Fitting the model on the data
linear_reg.fit(feature, target)
predictions = linear_reg.predict(feature)
均方绝对误差
均方绝对误差的公式如下:
MAE 公式
在前面的公式中, 代表输出的真实值(或实际值),而
代表预测的输出值。因此,通过计算每一行数据中真实值和预测值之间的差异总和,然后将其除以观测数据的总数,得到绝对误差的均值。
为了在 scikit-learn 中实现 MAE,我们使用以下代码:
from sklearn import metrics
metrics.mean_absolute_error(target, predictions)
在前面的代码中,scikit-learn 中 metrics 模块的 mean_absolute_error() 函数用于计算 MAE。它接受两个参数:真实输出(目标值)和预测输出(预测值)。
均方误差
均方误差的公式如下:
MSE 公式
前面的公式与我们看到的均方绝对误差公式相似,不同之处在于,我们不是计算真实值和预测值之间的绝对差异,而是计算它们差异的平方。
为了在 scikit-learn 中实现 MSE,我们使用以下代码:
metrics.mean_squared_error(target, predictions)
我们使用 metrics 模块中的 mean_squared_error() 函数,传入真实输出值和预测值作为参数。均方误差对于检测较大的误差更有效,因为我们对误差进行了平方,而不仅仅依赖于差异。
均方根误差
均方根误差的公式如下:
前面的公式与均方误差(MSE)非常相似,唯一不同的是我们对 MSE 公式进行了平方根处理。
为了在 scikit-learn 中计算 RMSE,我们使用以下代码:
import numpy as np
np.sqrt(metrics.mean_squared_error(target, predictions))
在前面的代码中,我们使用了mean_squared_error()函数来计算真实输出与预测结果之间的误差,然后通过使用numpy包中的np.sqrt()函数计算该值的平方根。
相较于 MAE 和 MSE,RMSE 是评估线性回归模型时最合适的指标,因为它能够检测到较大的误差,并且以输出单位表示该值。使用这三种指标中的任何一个时,关键的结论是,这些metrics给出的值应该尽可能低,表明模型的误差较小。
无监督算法的性能评估
在本节中,你将学习如何评估无监督机器学习算法的性能,例如 k-means 算法。第一步是构建一个简单的 k-means 模型。我们可以通过使用以下代码来完成:
#Reading in the dataset
df = pd.read_csv('fraud_prediction.csv')
#Dropping the target feature & the index
df = df.drop(['Unnamed: 0', 'isFraud'], axis = 1)
#Initializing K-means with 2 clusters
k_means = KMeans(n_clusters = 2)
现在我们已经有了一个简单的 k-means 模型,其中包含两个聚类,我们可以继续评估该模型的性能。可以使用的不同可视化性能图表如下:
-
肘部图
-
轮廓分析图
在本节中,你将学习如何创建和解释上述每一个图表。
肘部图
为了构建肘部图,我们使用以下代码:
skplt.cluster.plot_elbow_curve(k_means, df, cluster_ranges=range(1, 20))
plt.show()
这会生成如下图所示的图表:
肘部图
肘部图是一个图表,表示模型考虑的聚类数在x轴上,而平方误差总和则在y轴上。
在前面的代码中,以下内容适用:
-
我们使用
plot_elbow_curve()函数,输入 k-means 模型、数据和我们想要评估的聚类数。 -
在这种情况下,我们定义了 1 到 19 个聚类的范围。
在前面的图表中,以下内容适用:
-
很明显,肘部点,或者说平方误差总和(y轴)开始非常缓慢下降的点,出现在聚类数为 4 时。
-
该图表还在y轴(右侧)提供了一个有趣的指标,即聚类时长(以秒为单位)。这表示算法创建聚类所花费的时间,单位为秒。
总结
在本章中,你学会了如何评估三种不同类型的机器学习算法的性能:分类、回归和无监督学习。
对于分类算法,你学会了如何通过一系列可视化技术评估模型的性能,例如混淆矩阵、归一化混淆矩阵、曲线下面积、K-S 统计图、累计增益图、提升曲线、校准图、学习曲线和交叉验证箱线图。
对于回归算法,你学习了如何通过三种度量标准来评估模型的性能:均方误差、平均绝对误差和均方根误差。
最后,对于无监督学习算法,你学习了如何通过使用肘部法则图来评估模型的性能。
恭喜你!你已经顺利完成了使用 scikit-learn 的机器学习之旅。你已经通过了八章内容,这些内容为你提供了快速入门的途径,帮助你进入了机器学习的奇妙世界,并使用了全球最受欢迎的机器学习框架之一:scikit-learn。
在本书中,你学习了以下主题:
-
什么是机器学习(简而言之),以及机器学习的不同类型和应用
-
监督学习算法,如 K-NN、逻辑回归、朴素贝叶斯、支持向量机和线性回归
-
无监督学习算法,如 k-means 算法
-
既能执行分类任务又能进行回归的算法,如决策树、随机森林和梯度提升树
我希望你能充分利用本书所提供的知识,应用这些知识,通过机器学习作为工具,解决许多现实世界中的问题!