机器学习研讨会(一)
原文:
annas-archive.org/md5/434f45a68c082426533e009b496c8b55译者:飞龙
前言
关于本书
机器学习算法几乎是所有现代应用程序的核心部分。为了加快学习过程并提高准确性,你需要一个足够灵活和强大的工具,帮助你快速、轻松地构建机器学习算法。通过*《机器学习工作坊(第二版)》*,你将掌握 scikit-learn 库,并成为开发巧妙机器学习算法的高手。
*《机器学习工作坊(第二版)》*首先通过分析批发客户的真实数据集,展示了无监督和监督学习算法的工作原理。在掌握基础知识后,你将使用 scikit-learn 开发一个人工神经网络,并通过调整超参数来提高其性能。在工作坊的后期,你将研究一家银行的营销活动数据集,并构建可以列出可能订阅定期存款的客户的机器学习模型。你还将学习如何比较这些模型并选择最优模型。
到了*《机器学习工作坊(第二版)》*的结尾,你不仅会学到监督学习和无监督学习模型之间的区别及其在现实世界中的应用,还将掌握开始编程自己机器学习算法所需的技能。
读者群体
*《机器学习工作坊(第二版)》*是机器学习初学者的理想之选。你需要有 Python 编程经验,但无需具备 scikit-learn 和机器学习的先前知识。
关于章节
第一章,Scikit-Learn 简介,介绍了本书的两个主要主题:机器学习和 scikit-learn。它解释了输入数据预处理的关键步骤,如何分离特征与目标,如何处理杂乱数据,以及如何重新调整数据值的尺度。
第二章,无监督学习——真实生活应用,通过讲解三种最常见的聚类算法,解释了机器学习中聚类的概念。
第三章,监督学习——关键步骤,描述了通过监督学习算法可以解决的不同任务:分类和回归。
第四章,监督学习算法:预测年收入,教授解决监督学习数据问题的不同概念和步骤。
第五章,人工神经网络:预测年收入,展示了如何使用神经网络解决监督学习分类问题,并通过执行误差分析来分析结果。
第六章,构建你自己的程序,解释了开发全面机器学习解决方案所需的所有步骤。
约定
文本中的代码词、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 用户名如下所示:
使用seaborn库加载titanic数据集。
您在屏幕上看到的单词(例如,在菜单或对话框中)以相同的格式显示。
代码块设置如下:
import seaborn as sns
titanic = sns.load_dataset('titanic')
titanic.head(10)
新术语和重要单词以这种方式显示:
“缺失信息或包含异常值或噪声的数据被认为是杂乱数据。”
代码展示
跨多行的代码使用反斜杠(\)分割。当代码执行时,Python 会忽略反斜杠,并将下一行代码视为当前行的直接延续。
例如:
history = model.fit(X, y, epochs=100, batch_size=5, verbose=1, \
validation_split=0.2, shuffle=False)
注释被添加到代码中,用于帮助解释特定的逻辑部分。单行注释使用#符号表示,如下所示:
# Print the sizes of the dataset
print("Number of Examples in the Dataset = ", X.shape[0])
print("Number of Features for each example = ", X.shape[1])
多行注释用三引号括起来,如下所示:
"""
Define a seed for the random number generator to ensure the
result will be reproducible
"""
seed = 1
np.random.seed(seed)
random.set_seed(seed)
设置您的开发环境
在我们详细探讨这本书之前,我们需要安装一些特定的软件和工具。在接下来的章节中,我们将看到如何操作。
在 Windows 和 MacOS 上安装 Python
按照以下步骤安装 Python 3.7 在 Windows 和 macOS 上:
-
访问
www.python.org/downloads/release/python-376/下载 Python 3.7。 -
在页面底部,找到标题为
Files的表格:对于 Windows,点击
Windows x86-64 executable installer以安装 64 位版本,或点击Windows x86 executable installer以安装 32 位版本。对于 macOS,点击
macOS 64-bit/32-bit installer以安装适用于 macOS 10.6 及更高版本的版本,或点击macOS 64-bit installer以安装适用于 macOS 10.9 及更高版本的版本。 -
运行您已下载的安装程序。
在 Linux 上安装 Python
-
打开终端并输入以下命令:
sudo apt-get install python3.7
安装 pip
pip默认会随 Python 3.7 的安装一同安装。然而,也有可能它没有被安装。要检查是否已安装,请在终端或命令提示符中执行以下命令:
pip --version
由于您计算机上以前版本的pip已经使用了pip命令,您可能需要使用pip3命令。
如果pip命令(或pip3)未被您的机器识别,请按照以下步骤安装它:
-
要安装
pip,访问pip.pypa.io/en/stable/installing/并下载get-pip.py文件。 -
然后,在终端或命令提示符中,使用以下命令安装它:
python get-pip.py
由于您机器上已安装的旧版本 Python 可能仍在使用python命令,您可能需要使用python3 get-pip.py命令。
安装库
pip随 Anaconda 预安装。一旦在机器上安装了 Anaconda,所有需要的库可以通过pip安装,例如,pip install numpy。另外,您也可以使用pip install –r requirements.txt安装所有需要的库。您可以在packt.live/2Ar1i3v找到requirements.txt文件。
练习和活动将在 Jupyter Notebook 中执行。Jupyter 是一个 Python 库,可以像其他 Python 库一样安装——即使用 pip install jupyter,但幸运的是,它已经随 Anaconda 预装。要打开一个 notebook,只需在终端或命令提示符中运行命令 jupyter notebook。
打开 Jupyter Notebook
-
打开终端/命令提示符。
-
在终端/命令提示符中,进入你克隆的书籍仓库所在的目录。
-
通过输入以下命令来打开 Jupyter Notebook:
jupyter notebook -
执行前面的命令后,你将能够通过机器的默认浏览器使用 Jupyter Notebook。
访问代码文件
你可以在packt.live/2wkiC8d找到本书的完整代码文件。你还可以通过使用packt.live/3cYbopv上的交互式实验环境直接在网页浏览器中运行许多活动和练习。
我们已尽力支持所有活动和练习的交互式版本,但我们也建议进行本地安装,以应对在没有此支持的情况下的使用需求。
本书中使用的高质量彩色图片可以在packt.live/3exaFfJ找到。
如果你在安装过程中遇到任何问题或有任何疑问,请通过电子邮件联系我们:workshops@packt.com。
第一章:1. Scikit-Learn 简介
概述
本章介绍了本书的两个主要主题:机器学习和 scikit-learn。通过阅读本书,你将学习到机器学习的概念和应用。你还将了解数据在机器学习中的重要性,以及数据预处理的关键方面,以解决各种数据问题。本章还将涵盖 scikit-learn 的基本语法。通过本章的学习,你将对 scikit-learn 的语法有一个坚实的理解,从而能够解决简单的数据问题,这将成为开发机器学习解决方案的起点。
简介
机器学习(ML)无疑是当今最重要的技术之一,因为它旨在将信息(数据)转化为可以用于做出明智决策的知识。在本章中,你将学习到机器学习在当今世界中的不同应用,以及数据在其中所扮演的角色。这将成为本书中介绍不同数据问题的起点,你将能够通过使用 scikit-learn 来解决这些问题。
Scikit-learn 是一个文档齐全且易于使用的库,通过使用简单的方法促进了机器学习算法的应用,最终使初学者能够在无需深入了解算法背后的数学知识的情况下进行数据建模。此外,得益于这个库的易用性,它使得用户能够为数据问题实现不同的近似方法(即创建不同的模型)。更重要的是,通过去除编写算法代码的任务,scikit-learn 使得团队能够将注意力集中在分析模型结果上,以得出关键结论。
Spotify,作为全球领先的音乐流媒体公司,使用 scikit-learn,因为它允许他们为数据问题实施多个模型,并且这些模型可以轻松地与现有开发系统进行连接。这个过程改进了获得有用模型的过程,同时使公司能够以极少的努力将这些模型接入现有应用中。
另一方面, booking.com 使用 scikit-learn,是因为该库提供了种类繁多的算法,使他们能够完成公司依赖的各种数据分析任务,例如构建推荐引擎、检测欺诈活动以及管理客户服务团队。
鉴于上述要点,本章还将解释 scikit-learn 及其主要用途和优点,并简要介绍 scikit-learn 应用程序接口(API)的语法和功能。此外,还将展示数据表示、可视化和标准化的过程。上述信息将帮助我们理解开发机器学习模型所需采取的不同步骤。
在本书的接下来的章节中,你将探索可以用于解决现实数据问题的主要机器学习算法。你还将学习不同的技术,用于衡量算法的性能,并了解如何相应地改进它们。最后,你将探索如何通过保存、加载模型并创建 API 来使用训练好的模型。
机器学习简介
机器学习(ML)是人工智能(AI)的一个子集,包含了多种能够从输入数据中学习的算法,而不需要为特定任务进行编程。这种从数据中学习的能力使得算法能够创建模型,通过在历史数据中寻找模式并随着新数据的输入不断改进模型,从而解决复杂的数据问题。
这些不同的机器学习算法使用不同的近似方法来解决任务(例如概率函数),但关键是它们能够考虑大量变量来解决特定的数据问题,使得最终的模型在解决任务时比人类更有效。通过机器学习算法创建的模型旨在从输入数据中寻找模式,以便在未来做出更有根据的预测。
机器学习的应用
一些常见的可以通过机器学习算法解决的任务包括价格/需求预测、产品/服务推荐以及数据过滤等。以下是这些任务的现实生活中的一些例子:
-
按需价格预测:服务价格随着需求变化的公司可以使用机器学习算法预测未来的需求,并确定是否有能力满足这一需求。例如,在交通运输行业,如果未来的需求低(淡季),航班价格将下降。相反,如果需求高(旺季),航班价格可能会增加。
-
娱乐推荐:利用你当前使用的音乐以及与自己相似的人的音乐,机器学习算法能够构建模型,建议你可能喜欢的新专辑。这同样适用于视频流媒体应用程序和在线书店。
-
电子邮件过滤:机器学习已经被应用于过滤来信,将垃圾邮件与你想要的邮件分开。最近,它还能够将不需要的邮件分类为更多的类别,如社交邮件和促销邮件。
选择合适的机器学习算法
在开发机器学习解决方案时,需要强调的是,往往没有单一的解决方案可以解决所有的数据问题,就像没有适用于所有数据问题的算法一样。基于这一点,考虑到机器学习领域存在大量算法,选择适合特定数据问题的算法往往是区分优秀模型和普通模型的转折点。
以下步骤可以帮助将算法范围缩小到几个:
-
了解你的数据:鉴于数据是能够开发任何机器学习解决方案的关键,第一步始终应该是理解数据,以便能够筛选掉无法处理这些数据的算法。
例如,考虑到数据集中特征和观察值的数量,可以确定是否需要一种能够在小型数据集上产生优秀结果的算法。判断数据集是否较小,取决于数据问题、输出的数量等因素。此外,通过理解数据集中字段的类型,你还可以判断是否需要能够处理分类数据的算法。
-
A) 算法。另一方面,没有目标特征的数据集被称为无标签数据,并通过无监督学习算法(B)解决。此外,输出数据(你期望从模型中获得的输出形式)也在决定使用哪些算法时发挥着关键作用。如果模型的输出需要是一个连续的数值,则任务是回归问题(
C)。另一方面,如果输出是一个离散值(例如一组类别),则任务是分类问题(D)。最后,如果输出是一组观察值的子集,那么需要执行的是聚类任务(E):图 1.1:展示任务划分
本章的监督学习与无监督学习部分将对任务的划分进行更详细的探讨。
-
选择一组算法:一旦完成前述步骤,就可以筛选出那些在输入数据上表现良好并能够达到预期结果的算法。根据你的资源和时间限制,你应该从这些合适的算法中选择一些进行测试,考虑到尝试多种算法始终是一个好习惯。
这些步骤将在下一章中通过一个实际数据问题作为例子进行更详细的解释。
Scikit-Learn
scikit-learn 由 David Cournapeau 于 2007 年作为 Google Summer of Code 项目的一部分创建,是一个开源的 Python 库,旨在简化基于内置机器学习和统计算法构建模型的过程,无需手动编写代码。它广受欢迎的主要原因是其完整的文档、易于使用的 API 以及众多致力于每天改进该库的合作者。
注意
你可以在scikit-learn.org找到 scikit-learn 的文档。
Scikit-learn 主要用于对数据建模,而不是用于处理或总结数据。它为用户提供了一个易于使用、统一的 API,可以用很少的学习成本应用不同的模型,而无需深入了解背后的数学原理。
注意
要理解这些模型,你需要了解一些数学知识,包括线性代数、概率论和多元微积分。有关这些模型的更多信息,请访问towardsdatascience.com/the-mathematics-of-machine-learning-894f046c568。
在 scikit-learn 库中可用的模型分为两类,即监督学习和无监督学习,本文稍后将对这两类进行深入解释。这种分类方式有助于确定在特定数据集上使用哪种模型,以获取最大的信息量。
除了在监督学习问题中用于预测未来行为以及在无监督学习问题中进行数据聚类,scikit-learn 还用于以下几个方面:
-
进行交叉验证和性能指标分析,以了解从模型中获得的结果,并从而提升其性能。
-
获取样本数据集以在其上测试算法
-
进行特征提取,从图像或文本数据中提取特征
尽管 scikit-learn 被认为是机器学习领域初学者首选的 Python 库,但全球有许多大公司也在使用它,因为它能通过将模型应用于现有开发来改进产品或服务。同时,它也允许公司快速对新想法进行测试。
一些主要使用 scikit-learn 的公司如下:
-
Spotify:作为最受欢迎的音乐流媒体应用之一,Spotify 主要使用 scikit-learn,原因是该框架提供了丰富的算法选择,同时也很容易将新模型集成到现有开发中。scikit-learn 已被应用于其音乐推荐模型。
-
Booking.com:从开发推荐系统到防止欺诈活动,及许多其他解决方案,这个旅行元搜索引擎能够利用 scikit-learn 探索大量算法,从而创建最先进的模型。
-
Evernote:这款笔记记录和管理应用程序使用 scikit-learn 来处理训练分类模型所需的多个步骤,从数据探索到模型评估。
-
Change.org:由于该框架易于使用且算法种类繁多,这家非营利组织能够创建电子邮件营销活动,触及全球数百万读者。
注意
你可以访问
scikit-learn.org/stable/testimonials/testimonials.html以了解其他使用 scikit-learn 的公司,并查看它们如何使用该库。
总之,scikit-learn 是一个开源的 Python 库,它通过 API 将大多数机器学习任务(包括有监督和无监督)应用于数据问题。它的主要用途是对数据建模,从而可以对未见过的观察数据做出预测;然而,它的功能不限于此,因为该库还允许用户根据正在训练的模型预测结果,并分析模型的性能等。
Scikit-learn 的优势
以下是使用 scikit-learn 进行机器学习的主要优势:
-
易用性:与其他库(如 TensorFlow 或 Keras)相比,scikit-learn 以简洁的 API 和较小的学习曲线为特点。该 API 以其统一性和直接性而受到欢迎。使用 scikit-learn 的用户不一定需要理解模型背后的数学原理。
-
统一性:它统一的 API 使得从一个模型切换到另一个模型非常容易,因为不同模型所需的基本语法是相同的。
-
文档/教程:该库完全由文档支持,文档易于访问且易于理解。此外,它还提供了逐步教程,涵盖了开发任何机器学习项目所需的所有主题。
-
可靠性和协作:作为一个开源库,scikit-learn 受益于多个合作者的贡献,他们每天都在为其性能改进而努力。来自不同背景的专家的参与,不仅帮助开发了一个更完整的库,也使得库更加可靠。
-
覆盖范围:当你浏览该库所包含的组件时,你会发现它涵盖了大多数机器学习任务,从有监督的回归任务到无监督的聚类任务。更重要的是,由于拥有众多合作者,新模型通常会在相对较短的时间内加入。
Scikit-learn 的缺点
以下是使用 scikit-learn 进行机器学习的主要缺点:
-
不灵活性:由于易于使用,这个库往往缺乏灵活性。这意味着用户在参数调优或模型架构方面的自由度较低,例如在梯度提升算法和神经网络中。这对于初学者在处理更复杂项目时会成为一个问题。
-
不适合深度学习:当处理复杂的机器学习项目时,该库的表现不足。尤其是在深度学习方面,因为 scikit-learn 不支持具有必要架构或计算能力的深度神经网络。
注意
深度学习是机器学习的一部分,基于人工神经网络的概念。它通过一系列层次从输入数据中提取有价值的信息(特征)。在本书的后续章节中,您将学习到神经网络,这是能够开发深度学习解决方案的起点。
总的来说,scikit-learn 是一个优秀的入门级库,因为它学习使用起来几乎不需要任何努力,并且有许多辅助材料来帮助其应用。由于多位贡献者的努力,该库保持更新并适用于大多数当前的数据问题。
另一方面,它是一个简单的库,不适合处理更复杂的数据问题,例如深度学习。同样,它也不推荐给那些希望通过调整每个模型中可用的不同参数来提升能力的用户。
其他框架
其他流行的机器学习框架如下:
-
TensorFlow:谷歌的开源机器学习框架,至今仍是数据科学家中最受欢迎的框架。它通常与 Python 集成,并且非常适合开发深度学习解决方案。由于其广泛的流行性,网络上关于该框架的信息使得开发不同的解决方案变得非常容易,更不用说它有 Google 的支持。
-
PyTorch:该框架由 Facebook 的 AI 研究实验室主要开发,是一个开源深度学习框架。尽管它是一个相对较新的框架(发布于 2017 年),但由于其易用性和 Python 特性,它的流行度逐渐上升。得益于动态图计算,它使得代码调试变得非常简单。
-
Keras:这是一个开源深度学习框架,通常适合刚入门的人。由于其简洁性,灵活性较差,但非常适合原型设计简单的概念。与 scikit-learn 类似,它也有自己易于使用的 API。
数据表示
机器学习的主要目标是通过解释数据来构建模型。为了做到这一点,必须以计算机能够读取的方式输入数据。为了将数据输入到 scikit-learn 模型中,数据必须以表格或矩阵的形式表示,并且具有所需的维度,这将在接下来的章节中讨论。
数据表
输入到机器学习问题中的大多数表格是二维的,这意味着它们包含行和列。通常,每一行代表一个观测值(一个实例),而每一列代表每个观测值的一个特征。
以下表格是一个 scikit-learn 示例数据集的片段。该数据集的目的是根据植物的特征区分三种不同类型的鸢尾花。因此,在下表中,每一行代表一棵植物,每一列表示该植物在每个特征上的值:
图 1.2:展示鸢尾花数据集前 10 个实例的表格
从前面的说明来看,通过查看前表的第一行,可以确定该观测值对应的是一棵花萼长度为5.1、花萼宽度为3.5、花瓣长度为1.4、花瓣宽度为0.2的植物。这棵植物属于setosa种类。
注意
当将图像输入到模型时,表格变为三维,其中行和列表示图像的像素维度,而深度则表示其颜色模式。如果你感兴趣,可以了解更多关于卷积神经网络的内容。
表格中的数据也称为结构化数据。另一方面,非结构化数据指的是无法存储在类似表格的数据库中的所有其他数据(即,不是以行和列的形式)。这包括图像、音频、视频和文本(如电子邮件或评论)。为了能够将非结构化数据输入到机器学习算法中,第一步应该是将其转化为算法可以理解的格式(即表格数据)。例如,图像被转换为像素矩阵,文本被编码为数值。
特征和目标矩阵
对于许多数据问题,数据集中的一个特征将作为setosa种类来使用。因此,学习如何将目标矩阵与特征矩阵分开非常重要。
[n_i, n_f],其中n_i表示实例的数量(例如数据集中的人群),n_f表示特征的数量(例如每个人的基本信息)。通常,特征矩阵存储在一个名为X的变量中。
注意
Pandas 是一个为 Python 构建的开源库。它的创建目的是解决与数据操作和分析相关的各种任务。同样,NumPy 是一个开源的 Python 库,用于操作大型多维数组。它还为这些数组提供了大量的数学函数。
n_i(实例的数量)。然而,在某些情况下,需要多个目标,因此矩阵的维度变为[n_i, n_t],其中n_t是需要考虑的目标数量。
与特征矩阵类似,目标矩阵通常创建为 NumPy 数组或 Pandas 系列。目标数组的值可以是离散的或连续的。通常,目标矩阵存储在名为Y的变量中。
练习 1.01:加载示例数据集并创建特征和目标矩阵
注意
本书中的所有练习和活动将主要在 Jupyter Notebooks 中进行。建议为不同的作业保持单独的 Notebook,除非另有说明。另外,要加载一个示例数据集,将使用seaborn库,它会将数据以表格形式显示。其他加载数据的方法将在后续章节中讲解。
在本练习中,我们将从seaborn库加载tips数据集,并使用它创建特征和目标矩阵。按照以下步骤完成此练习:
注意
本章的练习和活动要求系统中安装 Python 3.7、Seaborn 0.9、Jupyter 6.0、Matplotlib 3.1、NumPy 1.18 和 Pandas 0.25。
-
打开一个 Jupyter Notebook 来完成这个练习。在命令提示符或终端中,导航到所需路径并使用以下命令:
jupyter notebook -
使用
seaborn库加载tips数据集。为此,你需要导入seaborn库,然后使用load_dataset()函数,如下代码所示:import seaborn as sns tips = sns.load_dataset('tips')如我们从前面的代码中看到的,导入库后,会给它起一个别名,以方便在脚本中使用。
load_dataset()函数从在线库加载数据集。数据集中的数据存储在名为tips的变量中。 -
创建一个变量
X,用于存储特征。使用drop()函数包含除目标以外的所有特征,在本例中目标列为tip。然后,打印出该变量的前 10 个实例:X = tips.drop('tip', axis=1) X.head(10) axis = 0) or columns (axis = 1).输出结果应该如下所示:
图 1.3:显示特征矩阵前 10 个实例的表格
-
使用
X.shape命令打印出新变量的形状:X.shape输出结果如下所示:
(244, 6)第一个值表示数据集中的实例数(
244),第二个值表示特征数(6)。 -
创建一个变量
Y,用于存储目标值。无需使用函数来完成此操作。通过索引来提取所需的列。索引允许你访问更大元素的某一部分。在这个例子中,我们想要提取名为tip的列。然后,我们需要打印出该变量的前 10 个值:Y = tips['tip'] Y.head(10)输出结果应该如下所示:
图 1.4:显示目标矩阵前 10 个实例的截图
-
使用
Y.shape命令打印出新变量的形状:Y.shape输出结果如下所示:
(244,)形状应该是一维的,长度等于实例的数量(
244)。注意
若要访问此特定部分的源代码,请参考
packt.live/2Y5dgZH。你也可以在线运行这个示例,网址是
packt.live/3d0Hsco。你必须执行整个 Notebook 才能得到期望的结果。
到此,你已经成功创建了数据集的特征矩阵和目标矩阵。
通常,表示数据的首选方式是使用二维表格,其中行代表观察的数量,也称为实例,列代表这些实例的特征,通常称为特征。
对于需要目标标签的数据问题,数据表需要分为特征矩阵和目标矩阵。特征矩阵将包含每个实例的所有特征值,但不包括目标值,因此它是一个二维矩阵。另一方面,目标矩阵将仅包含所有条目的目标特征值,因此它是一个一维矩阵。
活动 1.01:选择目标特征并创建目标矩阵
你想分析泰坦尼克号数据集,查看不同甲板上乘客的生存率,并验证是否可以证明下层甲板的乘客更难幸存。此活动中,我们将尝试加载数据集并通过选择合适的目标特征来创建特征矩阵和目标矩阵,以实现我们的目标。
注意
选择目标特征时,请记住目标应该是我们想要解释数据的结果。例如,如果我们想知道哪些特征在确定植物种类中起作用,那么物种应该是目标值。
按照以下步骤完成此活动:
-
使用
seaborn库加载titanic数据集。前几行应该是这样的:图 1.5:展示泰坦尼克号数据集前 10 个实例的表格
-
选择你偏好的目标特征以完成本活动的目标。
-
创建特征矩阵和目标矩阵。确保将特征矩阵的数据存储在变量
X中,将目标矩阵的数据存储在另一个变量Y中。 -
打印出每个矩阵的形状,应该与以下值匹配:
Features matrix: (891, 14) Target matrix: (891,)注意
本活动的解决方案可以在第 210 页找到。
数据预处理
数据预处理是开发机器学习解决方案中的关键步骤,因为它有助于确保模型不会在偏差数据上训练。它能够提高模型的性能,而且通常是同一算法在同一数据问题上能为进行良好数据预处理的程序员带来更好效果的原因。
为了让计算机能够有效理解数据,不仅需要以标准化的方式输入数据,还需要确保数据中不包含异常值或噪声数据,甚至没有缺失条目。这一点很重要,因为如果做不到这一点,算法可能会做出与数据不符的假设。这将导致模型训练速度变慢,并且由于对数据的误导性解释,准确性较低。
此外,数据预处理不仅仅止于此。模型的工作方式各不相同,每个模型有不同的假设。这意味着我们需要根据将要使用的模型来预处理数据。例如,有些模型仅接受数值型数据,而其他模型则可以处理名义数据和数值数据。
为了在数据预处理中获得更好的结果,一种良好的做法是以不同的方式转换(预处理)数据,然后在不同的模型中测试这些不同的转换。这样,你就能选择适合特定模型的正确转换方式。值得一提的是,数据预处理有可能帮助解决任何数据问题和任何机器学习算法,考虑到仅仅通过标准化数据集,就能提高训练速度。
杂乱数据
缺少信息或包含异常值或噪音的数据被视为杂乱数据。如果未进行任何预处理来转换数据,可能会导致数据模型构建不佳,因为引入了偏差和信息丢失。这里将解释一些应避免的数据问题。
缺失值
数据集的特征和实例可能都有缺失值。对于某些特征,只有少数实例有值,而对于某些实例,所有特征的值都缺失,这些都被视为缺失数据:
图 1.6:缺失值示例
上图展示了一个实例(实例 8),该实例在所有特征上都没有值,因此它没有任何用处;另外还有一个特征(特征 8),它在 10 个实例中有 7 个缺失值,这意味着该特征无法用于发现实例之间的模式,因为大多数实例没有该特征的值。
通常情况下,缺失超过 5% 到 10% 的值的特征被认为是缺失数据(也称为高缺失率特征),因此需要处理。另一方面,所有特征的值都缺失的实例应被删除,因为它们没有为模型提供任何信息,反而可能引入偏差。
处理具有高缺失率的特征时,建议要么删除该特征,要么用值填充它。替换缺失值的最常见方法如下:
-
均值填充:用特征的可用值的均值或中位数来替代缺失值
-
回归插补:用从回归函数中获得的预测值替代缺失值。
注意
回归函数是指用于估计因变量与一个或多个自变量之间关系的统计模型。回归函数可以是线性、逻辑回归、 polynomial 等。
尽管均值插补是一种更简单的实现方法,但它可能会引入偏差,因为它会使所有实例趋于一致。另一方面,虽然回归方法将数据与其预测值匹配,但它可能会导致模型过拟合(即创建一个过于适应训练数据、无法处理新数据的模型),因为所有引入的值都遵循一个函数。
最后,当缺失值出现在如性别等文本特征中时,最好的做法是要么删除它们,要么用标记为 未分类 或类似的类别来替代它们。这主要是因为无法对文本应用均值插补或回归插补。
将缺失值标记为新类别(未分类)通常是在删除缺失值会去除数据集中重要部分时采取的做法,因此不适合删除它们。在这种情况下,尽管新标签可能会对模型产生影响,但根据标记缺失值时使用的理由,留空可能是一个更差的选择,因为它会导致模型自行做出假设。
注意
要了解更多关于如何检测和处理缺失值的信息,请访问以下页面:towardsdatascience.com/how-to-handle-missing-data-8646b18db0d4。
异常值
异常值是指远离均值的值。这意味着如果一个特征的值符合高斯分布,则异常值位于分布的尾部。
注意
高斯分布(也称为正态分布)具有钟形曲线,因为该分布中大于和小于均值的数值数量相等。
异常值可以是全球性(global)或局部性(local)。前者代表那些与特征值集相距较远的值。例如,当分析某个邻里所有成员的数据时,一个全球异常值可能是一个 180 岁的人(如下面的图示 (A) 所示)。而局部异常值则代表那些与某个特征的子集相距较远的值。以我们之前看到的例子为例,一个局部异常值可能是一个 70 岁的大学生(B),这通常会与该邻里中的其他大学生不同:
图 1.7:展示数据集中全球性和局部异常值的图像
考虑到上述两个例子,异常值并不会评估值是否可能。虽然一个 180 岁的人不现实,但一个 70 岁的大学生是有可能的,然而这两者都被归类为异常值,因为它们都可能影响模型的表现。
检测异常值的一个直接方法是通过可视化数据来判断它是否符合高斯分布,如果符合,则将落在距离均值三到六个标准差以外的值归类为异常值。然而,并没有一个确定的规则来判断一个异常值,选择标准差数量的决定是主观的,并且会根据不同问题有所变化。
例如,如果通过设置三个标准差为参数来排除值,数据集减少了 40%,那么将标准差的数量调整为四个可能更为合适。
另一方面,在处理文本特征时,检测异常值变得更加棘手,因为没有标准差可以使用。在这种情况下,计算每个类别值的出现次数将有助于判断某个类别是否不可或缺。例如,在服装尺码中,如果 XXS 尺码的比例小于整个数据集的 5%,可能就不需要它。
一旦异常值被检测出来,处理它们的三种常见方法如下:
-
删除异常值:对于真实的异常值,最好将它们完全删除,以避免扭曲分析。对于那些错误的异常值,如果它们的数量太大,以至于无法进行进一步分析来赋予新值,删除它们也是一个好主意。
-
定义一个上限:为真实值定义一个上限也可能是有用的。例如,如果你意识到所有超过某个阈值的值都表现得相同,你可以考虑将该值设置为一个上限。
-
赋予一个新值:如果异常值显然是错误的,你可以使用我们讨论过的处理缺失值的方法之一(均值或回归插补)为其赋予一个新值。
使用上述每种方法的决策取决于异常值的类型和数量。大多数时候,如果异常值的数量占数据集总量的比例较小,那么除了删除它们之外,别无他法。
注意
噪声数据指的是那些不正确或不可能的值。这包括数值型(错误的异常值)和名义型值(例如,一个人的性别被拼写成 "fimale")。像异常值一样,噪声数据可以通过完全删除这些值或赋予它们一个新值来处理。
练习 1.02:处理杂乱数据
在这个练习中,我们将使用 seaborn 的 tips 数据集作为示例,演示如何处理杂乱的数据。按照以下步骤完成这个练习:
-
打开一个 Jupyter Notebook 来实现这个练习。
-
导入所有必需的元素。接下来,加载
tips数据集并将其存储在名为tips的变量中。使用以下代码:import seaborn as sns import numpy as np import matplotlib.pyplot as plt tips = sns.load_dataset('tips') -
接下来,创建一个名为
size的变量来存储数据集中该特征的值。由于此数据集不包含任何缺失数据,我们将size变量的前 16 个值转换为缺失值。打印age变量的前 20 个值:size = tips["size"] size.loc[:15] = np.nan size.head(20) NaN), which is the representation of a missing value. Finally, it prints the top 20 values of the variable.输出将如下所示:
图 1.8:展示了年龄变量前 20 个实例的截图
如您所见,该特征包含我们引入的
NaN值。 -
检查
size变量的形状:size.shape输出如下所示:
(244,) -
现在,计算
NaN值的数量以确定如何处理它们。使用isnull()函数找到NaN值,并使用sum()函数将它们全部加起来:size.isnull().sum()输出如下:
16NaN值在变量总大小中的参与率为 6.55%,可以通过将缺失值数量除以特征长度(16/244)来计算。虽然这个比例不高到足以考虑移除整个特征,但确实需要处理缺失值。 -
让我们选择均值插补方法来替换缺失值。为此,计算可用值的均值,如下所示:
mean = size.mean() mean = round(mean) print(mean)均值为
3。注意
由于
size特征是一个衡量参加餐厅的人数的指标,因此将均值值2.55四舍五入到最接近的整数。 -
用均值替换所有缺失值。使用
fillna()函数,该函数接受每个缺失值并用括号内定义的值替换它。再次打印前 10 个值来检查是否已替换:size.fillna(mean, inplace=True) size.head(20)注意
当
inplace设置为True时,原始 DataFrame 将被修改。如果未将参数设置为True,则将保留原始数据集的未修改状态。根据此设置inplace为True,可以替换NaN值为均值。打印输出如下所示:
图 1.9:展示了年龄变量前 20 个实例的截图
如前面的截图所示,顶部实例的值已从
NaN更改为先前计算的均值3。 -
使用 Matplotlib 绘制
age变量的直方图。使用 Matplotlib 的hist()函数,如下所示的代码:plt.hist(size) plt.show()直方图应如下所示。正如我们所见,其分布类似于高斯分布:
图 1.10:展示了大小变量的直方图的截图
-
发现数据中的异常值。让我们使用三个标准偏差作为度量标准来计算最小和最大值。
如前所述,最小值是通过计算所有值的均值并从中减去三倍标准差来确定的。使用以下代码设置最小值,并将其存储在名为
min_val的变量中:min_val = size.mean() - (3 * size.std()) print(min_val)最小值约为
-0.1974。根据最小值,左尾的高斯分布没有异常值。这是有道理的,因为分布略微偏向左侧。与最小值相反,对于最大值,标准差被加到均值上,以计算更高的阈值。计算最大值,如下所示的代码,并将其存储在名为
max_val的变量中:max_val = size.mean() + (3 * size.std()) print(max_val)最大值约为
5.3695,它确定了大小大于 5.36 的实例为异常值。正如你在前面的图中所看到的,这也很有意义,因为这些实例远离高斯分布的钟形曲线。 -
统计大于最大值的实例数量,以决定如何处理它们,按照这里给出的指示进行。
使用索引获取
size中高于最大阈值的值,并将其存储在名为outliers的变量中。然后,使用count()计算异常值的数量:outliers = size[size > max_val] outliers.count()输出显示有
4个异常值。 -
打印出异常值并检查是否正确存储了相应的值,如下所示:
print(outliers)输出结果如下:
图 1.11:打印异常值
由于异常值的数量较少,并且它们确实是异常值,因此可以将其删除。
注意
对于这个练习,我们将删除
size变量中的实例,以了解处理异常值的完整过程。然而,稍后在考虑所有特征时,将处理异常值的删除,以便删除整个实例,而不仅仅是删除大小值。 -
通过使用索引重新定义
size中存储的值,只包含低于最大阈值的值。然后,打印size的形状:age = size[size <= max_val] age.shape输出结果如下:
(240,)如你所见,
size的形状(在步骤 4中计算)已经减少了四个,这正是异常值的数量。注意
要访问此特定部分的源代码,请参考
packt.live/30Egk0o。你也可以在
packt.live/3d321ow在线运行这个示例。你必须执行整个 Notebook 才能获得期望的结果。
你已经成功清理了一个 Pandas 系列。这一过程为稍后的数据集清理提供了指南。
总结一下,我们讨论了数据预处理的重要性,因为如果没有预处理,可能会在模型中引入偏差,进而影响模型的训练时间和性能。数据杂乱的主要表现形式包括缺失值、异常值和噪声。
缺失值,顾名思义,是指那些空缺或为 null 的值。在处理大量缺失值时,重要的是通过删除或分配新值来处理它们。两种分配新值的方法也已讨论过:均值插补和回归插补。
离群值是指与某一特征所有值的均值相差很远的值。检测离群值的一种方法是选择所有超出均值加/减三倍/六倍标准差的值。离群值可能是错误值(不可能的值)或真实值,它们应当采取不同的处理方式。真实的离群值可能会被删除或修正,而错误值则应尽可能用其他值替换。
最后,噪声数据是指那些无论与均值的接近程度如何,都是数据中的错误或拼写错误的值。它们可以是数值型、序数型或名义型的。
注意
请记住,数值数据总是由可以测量的数字表示,名义数据是指不遵循顺序的文本数据,而有序数据是指遵循某种顺序或等级的文本数据。
处理类别特征
类别特征是包含离散值的特征,这些值通常属于有限的类别集合。类别数据可以是名义的或有序的。名义数据指的是不遵循特定顺序的类别,例如音乐类型或城市名称,而有序数据指的是具有顺序感的类别,例如服装尺码或教育水平。
特征工程
尽管许多机器学习算法的改进使得算法能够理解类别数据类型(如文本),但将它们转换为数值值的过程有助于模型的训练,从而提高运行速度和性能。这主要是因为消除了每个类别中可用的语义信息,以及将数据转换为数值值后,可以平等地缩放数据集中的所有特征,正如本章后续部分所解释的那样。
它是如何工作的?特征工程生成一个标签编码,将一个数字值分配给每个类别;然后该值将替换数据集中的类别。例如,一个名为genre的变量,具有pop、rock和country类别,可以按如下方式转换:
图 1.12:一张展示特征工程如何工作的图片
练习 1.03:将特征工程应用于文本数据
在本练习中,我们将把tips数据集的文本特征转换为数值数据。
注意
使用你为之前的练习创建的相同 Jupyter Notebook。
按照以下步骤完成本练习:
-
导入 scikit-learn 的
LabelEncoder()类,以及pandas库,如下所示:from sklearn.preprocessing import LabelEncoder import pandas as pd -
使用之前导入的类(
LabelEncoder)将每个文本特征转换为数值:enc = LabelEncoder() tips["sex"] = enc.fit_transform(tips['sex'].astype('str')) tips["smoker"] = enc.fit_transform(tips['smoker'].astype('str')) tips["day"] = enc.fit_transform(tips['day'].astype('str')) tips["time"] = enc.fit_transform(tips['time'].astype('str'))根据前面的代码片段,第一步是通过键入第一行代码实例化
LabelEncoder类。第二步,对于每个类别特征,我们使用该类的内置fit_transform()方法,该方法将为每个类别分配一个数值并输出结果。 -
输出
tips数据集的前几个值:tips.head()输出如下:
图 1.13:显示 tips 数据集前五个实例的截图
如你所见,类别特征的文本类别已被转换为数值。
注意
若要访问此部分的源代码,请参见packt.live/30GWJgb。
你也可以在packt.live/3e2oaVu上在线运行此示例。你必须执行整个 Notebook,才能得到所需的结果。
你已经成功地将文本数据转换为数值。
尽管机器学习的进展使得一些算法处理文本特征变得更加容易,但最好将其转换为数值。这一点尤其重要,因为它消除了处理语义时的复杂性,更不用说它为我们提供了从一个模型切换到另一个模型的灵活性,且没有任何限制。
文本数据的转换是通过特征工程完成的,其中每个文本类别都被分配了一个替代它的数值。此外,尽管可以手动完成此过程,但也有一些强大的内置类和方法可以简化这个过程。一个例子就是使用 scikit-learn 的LabelEncoder类。
数据重缩放
数据重缩放很重要,因为即使数据可以用不同的尺度传递给模型,每个特征的尺度不一致也可能导致算法失去从数据中发现模式的能力,因为它需要做出假设来理解数据,从而使得训练过程变慢并且对模型的性能产生负面影响。
数据重缩放帮助模型更快地运行,且无需为学习数据集中的不变性承担任何负担或责任。此外,在均匀缩放数据上训练的模型会对所有参数分配相同的权重(重要性等级),这使得算法能够泛化到所有特征,而不仅仅是那些值较高的特征,无论它们的含义如何。
一个具有不同尺度的数据集示例是包含不同特征的集合,其中一个特征以千克为单位,另一个测量温度,另一个则统计孩子数量。尽管每个属性的数值都是正确的,但它们的尺度差异很大。例如,千克的数值可以超过 100,而孩子的数量通常不会超过 10。
两种最常用的重缩放数据方法是 数据归一化 和 数据标准化。没有固定的规则来选择数据转换的重缩放方法,因为所有数据集的表现不同。最佳实践是使用两种或三种重缩放方法转换数据,并在每种方法上测试算法,从而选择最适合数据的算法,依据其性能表现。
重缩放方法应该单独使用。在测试不同的重缩放方法时,数据的转换应独立进行。每个转换可以在模型上进行测试,然后选择最适合的一个用于后续步骤。
归一化:机器学习中的数据归一化是指将所有特征的值重缩放,使其落在 0 到 1 之间,并且最大长度为 1。这样做的目的是将不同尺度的属性统一化。
以下公式允许你对特征值进行归一化:
图 1.14:归一化公式
其中,zi 代表第 i 个归一化值,x 代表所有值。
标准化:这是一种重缩放技术,它将数据转换为均值为 0,标准差为 1 的高斯分布。
标准化特征的一种简单方法如下公式所示:
图 1.15:标准化公式
其中,zi 代表第 i 个标准化值,x 代表所有值。
练习 1.04:数据归一化与标准化
本练习涉及数据的归一化和标准化,以 tips 数据集为例。
注意
使用你为前一个练习创建的相同 Jupyter Notebook。
按照以下步骤完成此练习:
-
使用包含整个数据集的
tips变量,采用归一化公式对数据进行归一化,并将结果存储在一个名为tips_normalized的新变量中。打印前 10 个值:tips_normalized = (tips - tips.min())/(tips.max()-tips.min()) tips_normalized.head(10)输出如下:
图 1.16:显示
tips_normalized变量前 10 个实例的截图如前面的截图所示,所有的数值都已转换为介于 0 和 1 之间的等效值。通过对所有特征进行归一化,模型将在相同尺度的特征上进行训练。
-
再次使用
tips变量,使用标准化公式对数据进行标准化,并将其存储在名为tips_standardized的变量中。打印出前 10 个值:tips_standardized = (tips - tips.mean())/tips.std() tips_standardized.head(10)输出结果如下:
图 1.17:显示 tips_standardized 变量前 10 个实例的截图
与归一化相比,标准化中,数值会围绕零正态分布。
注意
要访问此部分的源代码,请参考 packt.live/30FKsbD。
你也可以在网上运行这个示例,网址是 packt.live/3e3cW2O。你必须执行整个 Notebook 才能获得预期结果。
你已成功地将重缩放方法应用于数据。
总结来说,我们已经涵盖了数据预处理中的最后一步,即数据的重缩放。这个过程是在具有不同尺度特征的数据集上进行的,目的是统一数据的表示方式,以便模型能够更好地理解数据。
如果不对数据进行重缩放,模型将以更慢的速度进行训练,并可能会负面影响模型的性能。
本主题中解释了两种数据重缩放方法:归一化和标准化。一方面,归一化将数据转换为一个长度为 1 的范围(从 0 到 1)。另一方面,标准化将数据转换为具有均值 0 和标准差 1 的高斯分布。
由于没有选择适当重缩放方法的固定规则,推荐的做法是独立地使用两到三种重缩放方法对数据进行转换,然后用每种转换训练模型,评估哪种方法表现最佳。
活动 1.02:对整个数据集进行预处理
你继续为一家邮轮公司的安全部门工作。由于你在选择理想的目标特征以开展研究方面表现出色,部门决定委托你负责数据集的预处理工作。为此,你需要使用之前学习的所有技术来预处理数据集,并将其准备好用于模型训练。以下步骤将指导你朝着这个方向前进:
-
导入
seaborn和来自 scikit-learn 的LabelEncoder类。接下来,加载 Titanic 数据集,并创建包含以下特征的特征矩阵:sex、age、fare、class、embark_town和alone。注意
对于这个活动,特征矩阵仅使用了六个特征,因为其他一些特征对于本研究是冗余的。例如,不需要同时保留
sex和gender。 -
检查特征矩阵(
X)中所有特征的缺失值和异常值。选择一种方法来处理它们。 -
将所有文本特征转换为它们的数值表示。
-
对数据进行重新缩放,方法是通过归一化或标准化。
注意
该活动的解决方案可以在第 211 页找到。
预期输出:结果可能会有所不同,取决于你的选择。然而,你必须确保最终得到一个没有缺失值、异常值或文本特征的数据集,并且数据已重新缩放。
Scikit-Learn API
scikit-learn API 的目标是提供高效且统一的语法,使机器学习变得更加易于非机器学习专家使用,同时促进和普及其在多个行业中的应用。
它是如何工作的?
尽管有许多合作者,scikit-learn API 是在考虑一组原则的基础上构建并持续更新的,这些原则防止了框架代码的过度繁殖,即不同的代码执行相似的功能。相反,它提倡简单的约定和一致性。因此,scikit-learn API 在所有模型中保持一致,一旦掌握了主要功能,就可以广泛应用。
scikit-learn API 被分为三个互补的接口,它们共享相同的语法和逻辑:估算器、预测器和转换器。估算器接口用于创建模型并将数据拟合到模型中;预测器,顾名思义,用于基于已训练的模型进行预测;最后,转换器用于转换数据。
估算器
这被认为是整个 API 的核心,因为它负责将模型拟合到输入数据中。它通过实例化要使用的模型,然后应用 fit() 方法来触发学习过程,从而根据数据构建模型。
fit() 方法接收两个单独的变量作为训练数据的参数:特征矩阵和目标矩阵(通常分别称为 X_train 和 Y_train)。对于无监督模型,该方法仅接收第一个参数(X_train)。
此方法创建经过训练的模型,用于后续的预测。
一些模型除了训练数据外,还需要其他参数,这些参数也被称为 超参数。这些超参数最初设置为默认值,但可以调整以提高模型的性能,具体内容将在后续章节中讨论。
以下是一个模型训练的示例:
from sklearn.naive_bayes import GaussianNB
model = GaussianNB()
model.fit(X_train, Y_train)
首先,需要从 scikit-learn 导入要使用的算法类型;例如,用于分类的高斯朴素贝叶斯算法(将在第四章,监督学习算法:预测年收入中进一步探讨)。通常最好只导入需要使用的算法,而不是整个库,因为这样可以确保代码运行得更快。
注意
要查找导入不同模型的语法,请参阅 scikit-learn 的文档。访问以下链接,点击你想实现的算法,你将在那里找到相关说明:scikit-learn.org/stable/user_guide.html。
第二行代码负责实例化模型并将其存储在一个变量中。最后,将模型拟合到输入数据上。
除此之外,估算器还提供其他辅助任务,如下所示:
-
特征提取,涉及将输入数据转换为可用于机器学习目的的数值特征。
-
特征选择,用于选择对模型预测结果有贡献的数据特征。
-
降维技术,将高维数据转换为低维数据。
预测器
如前所述,预测器使用估算器创建的模型,并利用该模型对未见过的数据进行预测。一般而言,对于监督模型,它将新的数据集(通常称为 X_test)输入模型,以便根据训练模型时学到的参数得到相应的目标或标签。
此外,一些无监督模型也可以从预测器中受益。虽然该方法不会输出特定的目标值,但它可以用于将一个新实例分配到一个聚类中。
根据前面的示例,预测器的实现如下所示:
Y_pred = model.predict(X_test)
我们将 predict() 方法应用于之前训练好的模型,并将新的数据作为参数输入该方法。
除了预测外,预测器还可以实现一些方法,这些方法负责量化预测的置信度(即,代表模型性能水平的数值)。这些性能度量因模型而异,但其主要目的是确定预测与现实之间的差距。通过使用带有相应 Y_test 的 X_test 数据,并将其与使用相同 X_test 进行的预测进行比较,来实现这一目标。
转换器
正如我们之前看到的,数据通常在输入模型之前进行转换。考虑到这一点,API 包含一个 transform() 方法,允许你执行一些预处理技术。
它可以作为转换模型输入数据(X_train)的起点,也可以进一步用于修改将提供给模型进行预测的数据。后者的应用至关重要,因为它确保新数据遵循与训练模型时相同的分布,从而获得准确的结果。
以下是一个转换器的示例,用于对训练数据的值进行归一化处理:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(X_train)
X_train = scaler.transform(X_train)
StandardScaler 类对其接收到的数据进行标准化。如您所见,在导入并实例化转换器(即 StandardScaler)后,需要对数据进行拟合,以便有效地进行转换:
X_test = scaler.transform(X_test)
转换器的优点在于,一旦它被应用到训练数据集上,它会存储用于转换训练数据的值;这些值可以用于将测试数据集转换为相同的分布,正如前面的代码片段所示。
总之,我们讨论了使用 scikit-learn 的主要优势之一——其 API。这个 API 遵循一致的结构,使得非专家也能轻松应用机器学习算法。
在 scikit-learn 上建模的第一步是实例化模型的类,并通过估算器将其拟合到输入数据,通常是通过调用类的 fit() 方法来完成的。最后,一旦模型训练完成,就可以通过调用类的 predict() 方法,使用预测器来预测新值。
此外,scikit-learn 还提供了一个转换器接口,允许根据需要转换数据。这对于在训练数据上执行预处理方法非常有用,预处理后,测试数据也可以按相同的分布进行转换。
监督学习与无监督学习
机器学习分为两大类:监督学习和无监督学习。
监督学习
监督学习的核心是理解给定特征集与目标值之间的关系,这个目标值也称为标签或类别。例如,它可以用于建模一个人的人口统计信息与其偿还贷款能力之间的关系,如下表所示:
图 1.18:一个人的人口统计信息与偿还贷款能力之间的关系
通过训练来预测这些关系的模型可以应用于预测新数据的标签。正如我们从前面的例子中看到的,建立了这样一个模型的银行可以将贷款申请者的数据输入到模型中,从而确定他们是否可能按时偿还贷款。
这些模型可以进一步分为分类任务和回归任务,具体解释如下。
分类任务用于从具有离散类别标签的数据中构建模型;例如,分类任务可以用于预测一个人是否会偿还贷款。你可以有多个离散类别,比如预测赛马的排名,但这些类别必须是有限的。
大多数分类任务将预测输出为实例属于每个输出标签的概率。分配的标签是具有最高概率的标签,如下图所示:
图 1.19:分类算法的示意图
一些最常见的分类算法如下:
-
决策树:该算法遵循树状架构,模拟通过一系列决策来做出决策的过程,每次考虑一个变量。
-
朴素贝叶斯分类器:该算法依赖于一组基于贝叶斯定理的概率方程,假设特征之间是独立的。它能够考虑多个属性。
-
人工神经网络(ANN):这些模拟生物神经网络的结构和性能,以执行模式识别任务。人工神经网络由相互连接的神经元组成,按照一定的架构布局。它们相互传递信息,直到得到结果。
回归任务则用于具有连续数量标签的数据;例如,回归任务可以用于预测房价。这意味着输出值由一个数量表示,而不是一组可能的输出。输出标签可以是整数或浮动类型:
-
最常用的回归任务算法是线性回归。它仅包含一个独立特征(x),其与依赖特征(y)之间的关系是线性的。由于其简单性,通常被忽视,尽管它在处理简单数据问题时表现得非常好。
-
其他更复杂的回归算法包括回归树、支持向量回归,以及再次使用的人工神经网络(ANN)。
无监督学习
无监督学习是指将模型拟合到数据中,而不与输出标签存在任何关系,这也被称为无标签数据。这意味着该类别的算法试图理解数据并发现其中的模式。例如,无监督学习可以用来了解属于某个社区的人的特征,如下图所示:
图 1.20:无监督算法如何用于了解人群特征的示意图
在应用预测器到这些算法时,输出中没有给出目标标签。预测只对某些模型可用,它将新实例放入已创建的数据子组中。无监督学习进一步划分为不同的任务,但最受欢迎的任务是聚类,接下来将讨论这一点。
聚类任务涉及创建数据组(簇),同时遵守这样一个条件:一个组中的实例与其他组中的实例在视觉上有明显区别。任何聚类算法的输出是一个标签,将实例分配到该标签的簇中:
图 1.21:一个表示多种大小簇的图示
上面的图示展示了一组簇,每个簇的大小不同,基于属于每个簇的实例数量。考虑到这一点,尽管簇不需要具有相同数量的实例,但可以设置每个簇的最小实例数量,以避免将数据过拟合到非常具体的小簇中。
一些最受欢迎的聚类算法如下:
-
k 均值:该算法通过最小化两个点之间的平方距离之和,将实例分成 n 个方差相等的簇。
-
均值漂移聚类:通过使用质心来创建簇。每个实例都成为质心的候选者,质心是该簇中所有点的均值。
-
基于密度的空间聚类(DBSCAN):它通过密度较高的点区域来确定簇,簇之间由密度较低的区域分隔。
总结
机器学习(ML)包括构建能够将数据转化为可以用来做决策的知识的模型,其中一些模型基于复杂的数学概念来理解数据。Scikit-learn 是一个开源的 Python 库,旨在简化将这些模型应用于数据问题的过程,无需过多的复杂数学知识。
本章解释了预处理输入数据的关键步骤,从将特征与目标分开,到处理杂乱数据以及重新缩放数据值。在开始训练模型之前,应该执行所有这些步骤,因为它们有助于提高训练速度以及模型的表现。
接下来,解释了 scikit-learn API 的不同组件:估计器、预测器和转换器。最后,本章讲解了监督学习和无监督学习之间的区别,并介绍了每种学习类型中最受欢迎的算法。
考虑到这一点,在下一章中,我们将重点详细说明如何为实际数据集实现一个无监督算法。
第二章:2. 无监督学习 – 现实生活中的应用
概述
本章介绍机器学习中的聚类概念。它解释了三种最常见的聚类算法,并通过实际数据问题的近似解决方案进行了实际操作。通过本章的学习,您应该能够深入了解如何使用 k-means、均值漂移和 DBSCAN 算法从数据集中创建簇,以及如何衡量这些簇的准确性。
简介
在上一章中,我们学习了如何以表格格式表示数据,创建特征和目标矩阵,预处理数据,并学习了如何选择最适合问题的算法。我们还学习了 scikit-learn API 的工作原理以及为什么易于使用,以及监督学习和无监督学习之间的区别。
本章侧重于无监督学习领域中最重要的任务:聚类。考虑一种情况,您是一家店铺的所有者,希望通过定向社交媒体活动来促销特定产品给某些客户。使用聚类算法,您将能够创建客户的子群体,从而可以根据客户的特点来定位和目标客户。本章的主要目标是解决一个案例研究,您将在其中实施三种不同的无监督学习解决方案。这些不同的应用程序旨在演示 scikit-learn API 的一致性,以及解决机器学习问题所需的步骤。通过本章,您将能够了解使用无监督学习来理解数据以便做出明智决策。
聚类
聚类是一种无监督学习技术,其目标是基于未标记的输入数据中发现的模式得出结论。这种技术主要用于将大数据分成子群体,以便做出明智的决策。
例如,从一个城市的大型餐厅列表中,根据食物类型、客户数量和体验风格,将数据分成子群体(簇)将非常有用,以便为每个簇提供针对其特定需求配置的服务。
聚类算法将数据点划分为n个簇,使得同一簇内的数据点具有相似的特征,而与其他簇的数据点显著不同。
聚类类型
聚类算法可以使用硬或软的方法对数据点进行分类。前者将数据点完全指定给某一个簇,而后者则计算每个数据点属于每个簇的概率。例如,对于一个包含顾客过去订单的数据集,这些订单被分成八个子组(簇),硬聚类发生在每个顾客被放入八个簇中的其中一个。而软聚类则为每个顾客分配一个属于八个簇中每个簇的概率。
考虑到簇是基于数据点之间的相似性创建的,聚类算法可以根据用于衡量相似性的规则集进一步划分为几类。以下是四种最常见的规则集的解释:
-
基于连通性的模型:该模型的相似性方法基于数据空间中的接近度。簇的创建可以通过将所有数据点分配到一个簇中,然后随着数据点之间的距离增加,将数据划分为更小的簇来实现。同样,算法也可以从为每个数据点分配一个独立的簇开始,然后将接近的数据点合并在一起。基于连通性的模型的一个例子是层次聚类。
-
基于密度的模型:顾名思义,这些模型通过数据空间中的密度来定义簇。这意味着数据点密度较高的区域将形成簇,簇通常会被低密度区域分开。一个例子是 DBSCAN 算法,本章稍后将详细介绍。
-
基于分布的模型:属于此类别的模型基于所有来自同一簇的数据点遵循相同分布的概率,例如高斯分布。此类模型的一个例子是高斯混合算法,它假设所有数据点来自有限数量的高斯分布的混合体。
-
基于质心的模型:这些模型基于定义每个簇的质心的算法,该质心通过迭代过程不断更新。数据点会被分配到与其质心的距离最小的簇。此类模型的一个例子是 k 均值算法,本章稍后会讨论。
总之,数据点根据它们之间的相似性以及与其他簇中数据点的差异来分配到不同的簇中。这样的簇划分可以是绝对的,也可以通过确定每个数据点属于每个簇的概率来进行变动。
此外,并没有一套固定的规则来确定数据点之间的相似性,这也是不同聚类算法使用不同规则的原因。一些最常见的规则集包括基于连接性、基于密度、基于分布和基于质心的规则。
聚类的应用
与所有机器学习算法一样,聚类在不同领域有着广泛的应用,以下是其中的一些应用:
-
搜索引擎结果:聚类可以用于生成包含与用户搜索关键词相近的关键词的搜索引擎结果,并按与搜索结果的相似度进行排序。以谷歌为例,它不仅使用聚类来检索结果,还用聚类来建议新的可能搜索内容。
-
推荐程序:聚类也可以用于推荐程序,例如将具有相似特征的用户聚集在一起,然后根据每个成员的购买历史进行推荐。以亚马逊为例,它根据你的购买历史以及相似用户的购买记录推荐更多商品。
-
图像识别:聚类可用于将相似的图像分组。例如,Facebook 使用聚类来帮助识别图片中的人物。
-
市场细分:聚类还可以用于市场细分,将潜在客户或客户列表分成子群体,以提供定制化的体验或产品。例如,Adobe 利用聚类分析对客户进行细分,从而根据客户的消费意愿进行不同的定位。
上述示例表明,聚类算法可以用来解决不同行业中的不同数据问题,其主要目的是理解大量的历史数据,这些数据在某些情况下可以用来分类新的实例。
探索数据集 – 批发客户数据集
作为学习聚类算法的行为和应用的一部分,本章的以下部分将重点解决一个实际的数据问题,使用的是在 UC Irvine 机器学习仓库中提供的批发客户数据集。
注意
仓库中的数据集可能包含原始数据、部分预处理数据或已处理数据。在使用这些数据集时,请确保阅读可用数据的规格说明,以了解有效建模数据的过程,或者判断该数据集是否适合你的目的。
例如,当前数据集是一个来自更大数据集的提取,引用如下:
该数据集来源于一个更大的数据库,相关文献为:Abreu, N. (2011). 《客户 Recheio 的分析与促销系统的开发》。市场营销硕士,ISCTE-IUL,里斯本。
在接下来的部分,我们将分析数据集的内容,然后将其用于活动 2.01,使用数据可视化辅助预处理过程。要从 UC Irvine 机器学习库下载数据集,请执行以下步骤:
-
在数据集标题下方,找到下载部分并点击
Data Folder。 -
点击
Wholesale Customers data.csv文件以触发下载并将文件保存在当前 Jupyter Notebook 的相同路径下。注意
你也可以通过访问这本书的 GitHub 存储库来访问它:
packt.live/3c3hfKp
理解数据集
每一步都将以通用方式进行解释,然后会对其在当前案例研究(批发客户数据集)中的应用进行解释:
-
首先,理解负责收集和维护数据的人员呈现数据的方式至关重要。
考虑到案例研究的数据集是从在线资源库获取的,必须理解其呈现的格式。批发客户数据集包含批发分销商客户的历史数据片段。它包含总共 440 个实例(每一行)和八个特征(每一列)。
-
接下来,确定研究的目的非常重要,这取决于可用的数据。尽管这可能看起来像是一个冗余的陈述,但许多数据问题变得棘手,因为研究人员对研究目的没有清晰的了解,从而选择了错误的预处理方法、模型和性能指标。
在批发客户数据集上使用聚类算法的目的是理解每个客户的行为。这将使您能够将具有相似行为的客户分组到一个簇中。客户的行为将由他们在每个产品类别上的支出量、以及购买产品的渠道和地区来定义。
-
随后探索所有可用的特征。这主要是出于两个原因:首先,根据研究目的排除被认为具有低相关性的特征或被认为是多余的特征,其次,理解呈现值的方式以确定可能需要的一些预处理技术。
当前案例研究具有八个特征,每个特征都被认为与研究目的相关。下表对每个特征进行了解释:
图 2.1:案例研究中特征解释的表格
在前面的表格中,没有特征需要被忽略,且名义(分类)特征已经由数据集的作者处理。
总结一下,选择数据集或收到数据集时,首先需要了解初步可见的特征,这包括识别可用信息,然后确定项目的目的,最后修改特征,选择那些将用于研究的特征。之后,数据可以进行可视化,以便在预处理之前理解数据。
数据可视化
一旦数据被修改以确保能够用于预期目的,就可以加载数据集并使用数据可视化进一步理解数据。数据可视化并不是开发机器学习项目的必需步骤,尤其是在处理具有数百或数千个特征的数据集时。然而,它已成为机器学习的重要组成部分,主要用于可视化以下内容:
-
特定的导致问题的特征(例如,包含大量缺失值或异常值的特征)以及如何处理这些问题。
-
来自模型的结果,例如已创建的聚类或每个标签类别的预测实例数量。
-
模型的性能,以便查看不同迭代中的行为。
数据可视化在上述任务中的流行,可以通过人脑在图表或图形的呈现下容易处理信息来解释,这使得我们能够对数据有一个总体的理解。它还帮助我们识别需要注意的领域,比如异常值。
使用 pandas 加载数据集
一种便于管理数据集的存储方式是使用 pandas DataFrame。这些工作方式类似于具有标签轴的二维大小可变矩阵。它们便于使用不同的 pandas 函数来修改数据集以进行预处理。
大多数在线存储库中找到的数据集,或公司为数据分析收集的数据集,都存储在逗号分隔值(CSV)文件中。CSV 文件是以表格形式显示数据的文本文件。列由逗号(,)分隔,行位于不同的行上:
![图 2.2:CSV 文件的屏幕截图]
图 2.2:CSV 文件的屏幕截图
使用 pandas 的read_csv()函数加载存储在 CSV 文件中的数据集并将其放入 DataFrame 中是非常简单的。它接收文件路径作为参数。
注意
当数据集存储在不同格式的文件中时,如 Excel 或 SQL 数据库,分别使用 pandas 的read_xlsx()或read_sql()函数。
以下代码展示了如何使用pandas加载数据集:
import pandas as pd
file_path = "datasets/test.csv"
data = pd.read_csv(file_path)
print(type(data))
首先,导入 pandas 库。接下来,定义文件的路径,以便将其输入到read_csv()函数中。最后,打印data变量的类型,以验证已创建一个 Pandas DataFrame。
输出结果如下:
<class 'pandas.core.frame.DataFrame'>
如前面的代码片段所示,名为 data 的变量是 pandas DataFrame 类型。
可视化工具
目前有不同的开源可视化库,其中 seaborn 和 matplotlib 很突出。在上一章中,使用 seaborn 加载并显示数据;然而,从本节开始,我们将选择 matplotlib 作为我们的可视化库。这主要是因为 seaborn 是基于 matplotlib 构建的,目的是引入几种绘图类型并改善显示格式。因此,一旦你学习了 matplotlib,你也可以导入 seaborn 来提高绘图的视觉质量。
注意
有关 seaborn 库的更多信息,请访问以下链接:seaborn.pydata.org/。
一般来说,matplotlib 是一个易于使用的 Python 库,用于绘制 2D 高质量图形。对于简单的绘图,库的 pyplot 模块就足够了。
以下表格解释了一些最常用的绘图类型:
图 2.3:列出常用绘图类型的表格(*)
第三列中的函数在导入 matplotlib 及其 pyplot 模块后可以使用。
注意
访问 matplotlib 的文档,了解您希望使用的绘图类型:matplotlib.org/,以便您可以尝试不同的参数和函数来编辑绘图结果。
练习 2.01:绘制 Circles 数据集中的一个特征的直方图
在本练习中,我们将绘制 circles 数据集中的一个特征的直方图。执行以下步骤以完成此练习:
注意
对本章中的所有练习使用相同的 Jupyter Notebook。circles.csv 文件可以在 packt.live/2xRg3ea 下载。
本章中的所有练习和活动,您需要在系统中安装 Python 3.7、matplotlib、NumPy、Jupyter 和 pandas。
-
打开一个 Jupyter Notebook 来实现此练习。
-
首先,通过输入以下代码来导入你将要使用的所有库:
import pandas as pd import numpy as np import matplotlib.pyplot as pltpandas库用于将数据集保存为 DataFrame,matplotlib用于可视化,NumPy 在本章后面的练习中会使用,但由于使用的是相同的 Notebook,这里已经导入了。 -
使用 Pandas 的
read_csv函数加载 circles 数据集。输入以下代码:data = pd.read_csv("circles.csv") plt.scatter(data.iloc[:,0], data.iloc[:,1]) plt.show()创建一个名为
data的变量来存储 circles 数据集。最后,绘制一个散点图来显示数据空间中的数据点,其中第一个元素是数据集的第一列,第二个元素是数据集的第二列,从而创建一个二维图:注意
Matplotlib 的
show()函数用于触发图表的显示,考虑到前面的代码行只是创建了图表。在 Jupyter Notebooks 中,使用show()函数并非必需,但它是一个好习惯,因为在其他编程环境中,必须使用此函数才能显示图表。这也能提高代码的灵活性。另外,在 Jupyter Notebooks 中,使用此函数会使输出更干净。图 2.4: 圆形数据集的散点图
最终输出是一个包含两个特征和 1,500 个实例的数据集。在这里,点代表一个数据点(一个观察值),其位置由数据集中每个特征的值标记。
-
从两个特征中的一个创建直方图。使用切片选择你想要绘制的特征:
plt.hist(data.iloc[:,0]) plt.show()该图将类似于以下图表所示:
图 2.5: 显示使用第一个特征数据获得的直方图的截图
注意
要访问此特定部分的源代码,请参考packt.live/2xRg3ea。
你也可以在网上运行这个示例,链接:packt.live/2N0L0Rj。你必须执行整个 Notebook,才能获得期望的结果。
你已经成功地使用 matplotlib 创建了散点图和直方图。同样,使用 matplotlib 也可以创建不同类型的图表。
总结来说,数据可视化工具帮助你更好地理解数据集中可用的数据、模型的结果以及模型的性能。这是因为人类大脑更容易接受视觉形式,而不是大量的数据文件。
Matplotlib 已成为最常用的数据可视化库之一。在该库支持的不同类型的图表中,包括直方图、条形图和散点图。
活动 2.01: 使用数据可视化来辅助预处理过程
你公司市场团队希望了解客户的不同档案,以便能够将营销精力集中在每个档案的个人需求上。为此,团队提供了 440 条先前的销售数据给你们的团队。你的第一个任务是对数据进行预处理。你将使用数据可视化技术展示你的发现,以帮助同事理解你在此过程中做出的决策。你应该使用 pandas 加载 CSV 数据集,并使用数据可视化工具帮助预处理过程。以下步骤将指导你如何完成这一过程:
-
导入所有必要的元素以加载数据集并进行预处理。
-
使用 Pandas 的
read_csv()函数加载先前下载的数据集,假设数据集存储在 CSV 文件中。将数据集存储在一个名为data的 pandas DataFrame 中。 -
检查你的 DataFrame 中是否存在缺失值。如果存在,处理缺失值,并通过数据可视化支持你的决策。
注意
使用
data.isnull().sum()来一次性检查整个数据集中的缺失值,就像我们在上一章中学到的那样。 -
检查你的 DataFrame 中是否存在离群值。如果存在,处理离群值,并通过数据可视化支持你的决策。
注意
将所有偏离均值三个标准差的值标记为离群值。
-
使用归一化或标准化公式重新缩放数据。
注意
标准化通常在聚类目的上效果更好。注意,你可以在第 216 页找到这个活动的解决方案。
预期输出:检查 DataFrame 时,你应该发现数据集中没有缺失值,并且有六个特征包含离群值。
k-means 算法
k-means 算法用于建模没有标签类别的数据。它涉及将数据分成K个子组。数据点分类到每个组是基于相似性,如前所述(参见聚类类型部分),对于该算法,相似性通过数据点到簇的中心(质心)的距离来衡量。算法的最终输出是每个数据点与其所属簇的质心相关联,这可以用于为同一簇中的新数据进行标记。
每个簇的质心代表一组特征,可以用来定义属于该簇的数据点的性质。
理解算法
k-means 算法通过一个迭代过程工作,涉及以下步骤:
-
根据用户定义的簇数量,质心可以通过设置初始估计值或从数据点中随机选择来生成。这个步骤称为初始化。
-
所有数据点通过测量它们到质心的距离分配给数据空间中最近的簇,这被称为分配步骤。目标是最小化平方欧氏距离,公式如下:
min dist(c,x)2这里,
c表示质心,x表示数据点,dist()是欧氏距离。 -
质心通过计算属于同一簇的所有数据点的均值重新计算。这个步骤称为更新步骤。
步骤 2和步骤 3在迭代过程中重复执行,直到满足某个标准。这个标准可以是:
-
定义的迭代次数。
-
数据点不会在簇之间变化。
-
最小化欧氏距离。
算法设置为始终得出一个结果,尽管这个结果可能会收敛到局部或全局最优解。
k-means 算法接收多个参数作为输入以运行模型。最重要的参数是初始化方法(init)和聚类数目(K)。
注意
若要查看 scikit-learn 库中 k-means 算法的其他参数,请访问以下链接:scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html。
初始化方法
算法的一个重要输入是用于生成初始质心的初始化方法。scikit-learn 库允许的初始化方法如下所述:
-
k-means++:这是默认选项。质心是从数据点集中随机选择的,考虑到质心必须彼此远离。为此,该方法为那些距离其他质心较远的数据点分配更高的作为质心的概率。 -
random:此方法从数据点中随机选择 K 个观察值作为初始质心。
选择聚类数目
正如我们之前讨论的,数据被划分成的聚类数目是由用户设置的,因此选择合适的聚类数目非常重要。
用于衡量 k-means 算法性能的一个指标是数据点与其所属聚类质心之间的平均距离。然而,这个指标可能会适得其反,因为聚类数目越多,数据点与其质心之间的距离越小,这可能导致聚类数(K)与数据点的数量相同,从而破坏聚类算法的目的。
为避免这种情况,可以绘制数据点与聚类质心之间的平均距离与聚类数的关系图。适当的聚类数对应于图中的断点,即减少速率发生剧烈变化的地方。在下图中,虚线圆圈表示理想的聚类数:
图 2.6:展示如何估计断点的图表
练习 2.02:导入并在数据集上训练 k-means 算法
接下来的练习将使用与上一练习相同的数据集。考虑到这一点,请使用你在上一练习中使用的同一个 Jupyter Notebook。执行以下步骤以完成此练习:
-
打开你用于上一练习的 Jupyter Notebook。在这里,你应该已经导入了所有必需的库,并将数据集存储在名为
data的变量中。 -
如下所示,从 scikit-learn 导入 k-means 算法:
from sklearn.cluster import KMeans -
要选择K的值(即理想的簇数),请计算数据点到其簇质心的平均距离,并与簇的数量进行对比。此练习中使用 20 作为最大簇数。以下是此代码片段:
ideal_k = [] for i in range(1,21): est_kmeans = KMeans(n_clusters=i, random_state=0) est_kmeans.fit(data) ideal_k.append([i,est_kmeans.inertia_])注意
random_state参数用于确保结果的可重复性,确保算法的随机初始化保持一致。首先,创建一个变量用来存储值,并命名为
ideal_k。接下来,执行一个for循环,从 1 个簇开始,直到达到所需的数量(考虑到簇的最大数量不得超过实例的总数)。在前面的示例中,有一个限制,最多只能创建 20 个簇。由于这个限制,
for循环从 1 到 20 个簇。注意
记住,
range()是一个上界不包含的函数,这意味着范围会到达上界以下的一个值。当上界为 21 时,范围会到达 20。在
for循环中,实例化算法并设定要创建的簇的数量,然后将数据拟合到模型中。接下来,将数据对(簇的数量,平均距离到质心)追加到名为ideal_k的列表中。到质心的平均距离无需计算,因为模型会在
inertia_属性下输出它,可以通过[model_name].inertia_调用。 -
将
ideal_k列表转换为 NumPy 数组,以便绘制图形。使用以下代码片段:ideal_k = np.array(ideal_k) -
绘制你在前面步骤中计算的关系,找到理想的K值,以便输入到最终模型中:
plt.plot(ideal_k[:,0],ideal_k[:,1]) plt.show()输出如下:
图 2.7:显示绘图函数输出的截图
在前面的图表中,x 轴表示簇的数量,而y 轴表示每个簇中数据点到其质心的平均距离。
绘图的拐点大约在
5。 -
使用
K=5训练模型。使用以下代码:est_kmeans = KMeans(n_clusters=5, random_state=0) est_kmeans.fit(data) pred_kmeans = est_kmeans.predict(data)第一行使用
5作为簇的数量实例化模型。然后,数据被拟合到模型中。最后,模型用来为每个数据点分配一个簇。 -
绘制数据点聚类结果的图表:
plt.scatter(data.iloc[:,0], data.iloc[:,1], c=pred_kmeans) plt.show()输出如下:
图 2.8:显示绘图函数输出的截图
由于数据集仅包含两个特征,因此每个特征作为输入传递给散点图函数,这意味着每个特征由一个坐标轴表示。此外,从聚类过程中获得的标签用作显示数据点的颜色。因此,每个数据点根据两个特征的值定位于数据空间中,颜色代表形成的聚类。
注意
对于具有两个以上特征的数据集,聚类的可视化表示不像前述截图中那样直观。这主要是因为每个数据点(观察值)在数据空间中的位置是基于所有特征的集合,而在视觉上只能显示最多三个特征。
您已经成功导入并训练了 k-means 算法。
注意
要访问本练习的源代码,请参考packt.live/30GXWE1。
您也可以在packt.live/2B6N1c3上在线运行此示例。您必须执行整个 Notebook 才能获得所需的结果。
总之,k-means 算法旨在将数据分为K个聚类,K是由用户设置的参数。数据点根据其与聚类中心的接近程度被分组,聚类中心通过迭代过程计算得出。
初始中心点根据已定义的初始化方法设置。然后,所有数据点都会被分配到离它们在数据空间中的位置更近的聚类中心,使用欧氏距离作为度量。一旦数据点被分配到聚类中,每个聚类的中心点会重新计算为所有数据点的均值。这个过程会重复多次,直到满足停止标准。
活动 2.02:将 k-means 算法应用于数据集
在进行此活动之前,请确保您已完成活动 2.01,使用数据可视化辅助预处理过程。
在继续分析您公司过往订单的过程中,您现在负责将 k-means 算法应用于数据集。使用之前加载的批发客户数据集,对数据应用 k-means 算法并将数据分类为聚类。执行以下步骤完成此活动:
-
打开您在上一个活动中使用的 Jupyter Notebook。在那里,您应该已导入所有必需的库并执行了必要的步骤以预处理数据集。
-
计算数据点到其聚类中心的平均距离,并根据聚类数量来选择合适的聚类数以训练模型。
-
训练模型并为数据集中的每个数据点分配一个聚类。绘制结果。
注意
你可以使用 Matplotlib 的
subplots()函数同时绘制两个散点图。要了解更多关于此函数的信息,请访问 Matplotlib 的文档,网址如下:matplotlib.org/api/_as_gen/matplotlib.pyplot.subplots.html。你可以在第 220 页找到此活动的解决方案。
聚类的可视化将根据聚类的数量(k)和需要绘制的特征而有所不同。
均值漂移算法
均值漂移算法通过根据数据空间中数据点的密度为每个数据点分配一个簇,密度也称为分布函数中的模式。与 k-means 算法不同,均值漂移算法不需要你指定簇的数量作为参数。
该算法通过将数据点建模为分布函数来工作,其中高密度区域(数据点密集的区域)代表高峰值。然后,基本思路是将每个数据点移动到最近的峰值,从而形成一个簇。
理解算法
均值漂移算法的第一步是将数据点表示为一个密度分布。为此,算法基于核密度估计(KDE)的方法进行构建,KDE 是一种用于估算数据集分布的方法:
图 2.9:描述核密度估计(KDE)背后思想的图像
在前面的图中,形状底部的点代表用户输入的数据点,而锥形线条代表数据点的估算分布。峰值(高密度区域)将成为簇。为每个簇分配数据点的过程如下:
-
在每个数据点周围绘制一个指定大小(带宽)的窗口。
-
计算窗口内数据点的均值。
-
窗口的中心移动到均值位置。
步骤 2 和 步骤 3 会重复进行,直到数据点达到一个峰值,确定它所属的簇。
带宽值应与数据集中的数据点分布保持一致。例如,对于一个规范化在 0 到 1 之间的数据集,带宽值应该在该范围内,而对于一个所有值都在 1000 到 2000 之间的数据集,带宽值最好设置在 100 到 500 之间。
在下图中,估算的分布通过线条表示,而数据点则是点。在每个框中,数据点都会移动到最近的峰值。所有属于某个峰值的数据点都属于同一个簇:
图 2.10:展示均值漂移算法工作原理的一系列图像
数据点到达峰值所需的移动次数取决于其带宽(窗口大小)以及与峰值的距离。
注意
要探索 scikit-learn 中均值漂移算法的所有参数,请访问 scikit-learn.org/stable/modules/generated/sklearn.cluster.MeanShift.html。
练习 2.03:在数据集上导入并训练均值漂移算法
以下练习将使用我们在练习 2.01,绘制 Circles 数据集一个特征的直方图中加载的相同数据集。鉴于此,请使用你用来开发前面练习的相同 Jupyter Notebook。执行以下步骤来完成此练习:
-
打开你在上一个练习中使用的 Jupyter Notebook。
-
按如下方式从 scikit-learn 导入 k-means 算法类:
from sklearn.cluster import MeanShift -
使用带宽为
0.5训练模型:est_meanshift = MeanShift(0.5) est_meanshift.fit(data) pred_meanshift = est_meanshift.predict(data)首先,模型使用
0.5的带宽进行实例化。接下来,将模型拟合到数据。最后,使用该模型为每个数据点分配一个聚类。考虑到数据集包含的值范围从 −1 到 1,带宽值不应超过 1。
0.5这个值是在尝试其他值(如 0.1 和 0.9)后选择的。注意
请考虑带宽是算法的一个参数,并且作为参数,它可以进行微调,以实现最佳性能。这个微调过程将在第三章,有监督学习——关键步骤中讲解。
-
绘制将数据点聚类的结果:
plt.scatter(data.iloc[:,0], data.iloc[:,1], c=pred_meanshift) plt.show()输出如下:
图 2.11:使用前面代码获得的图
再次提醒,由于数据集仅包含两个特征,这两个特征都作为输入传递给散点图函数,并成为坐标轴的值。此外,从聚类过程中获得的标签被用作显示数据点的颜色。
创建的聚类总数是四个。
注意
要访问此练习的源代码,请参考 packt.live/37vBOOk。
你也可以在 packt.live/3e6uqM2 在线运行这个例子。你必须执行整个 Notebook 才能得到期望的结果。
你已经成功地导入并训练了均值漂移算法。
总结来说,均值漂移算法首先绘制出表示数据点集的分布函数。这个过程包括在高密度区域创建峰值,而在低密度区域保持平坦。
接下来,算法继续通过缓慢且迭代地移动每个数据点,直到它达到一个峰值,进而将其归为一个聚类。
活动 2.03:将均值漂移算法应用于数据集
在此活动中,你将应用均值迁移算法来处理数据集,以查看哪种算法更适合数据。因此,使用之前加载的批发消费者数据集,将均值迁移算法应用于数据并将数据分类为聚类。按照以下步骤完成此活动:
-
打开你在之前活动中使用的 Jupyter Notebook。
注意
考虑到你使用的是相同的 Jupyter Notebook,请小心不要覆盖任何先前的变量。
-
训练模型,并为数据集中的每个数据点分配一个聚类。绘制结果。
聚类的可视化将根据带宽和选择绘制的特征而有所不同。
注意
本活动的解决方案可以在第 223 页找到。
DBSCAN 算法
基于密度的空间聚类与噪声(DBSCAN)算法将彼此接近的点(具有许多邻居的点)分为一组,并将那些距离较远且没有接近邻居的点标记为离群点。
根据这一点,正如其名称所示,算法根据数据空间中所有数据点的密度来对数据点进行分类。
理解算法
DBSCAN 算法需要两个主要参数:epsilon 和最小观察数。
eps 是定义算法在其内搜索邻居的半径的最大距离。min_samples 参数是可选的,因为在 scikit-learn 中它的默认值为 5:
图 2.12:DBSCAN 算法如何将数据分类为聚类的示意图
在上图中,左侧的点被分配到聚类 A,而右上方的点被分配到聚类 B。此外,右下方的点(C)被认为是离群点,以及数据空间中任何其他不符合属于高密度区域所需参数的点(即未满足最小样本数要求,在这个例子中设定为 5)。
注意
类似于带宽参数,epsilon 值应与数据集中数据点的分布一致,因为它表示每个数据点周围的半径。
根据这一点,每个数据点可以按以下方式分类:
-
eps半径。 -
边界点:一个位于核心点的 eps 半径范围内,但在其自身的半径内没有满足要求数量的数据点。
-
噪声点:所有不符合前述描述的点。
注意
要探索 scikit-learn 中 DBSCAN 算法的所有参数,请访问
scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html。
练习 2.04:在数据集上导入并训练 DBSCAN 算法
本练习讲解如何在数据集上导入并训练 DBSCAN 算法。我们将使用前面练习中的圆形数据集。请执行以下步骤以完成此练习:
-
打开你用于前一个练习的 Jupyter Notebook。
-
如下所示,从 scikit-learn 导入 DBSCAN 算法类:
from sklearn.cluster import DBSCAN -
使用
0.1的 epsilon 训练模型:est_dbscan = DBSCAN(eps=0.1) pred_dbscan = est_dbscan.fit_predict(data)首先,模型通过
eps设置为0.1进行实例化。然后,我们使用fit_predict()函数将模型拟合到数据并为每个数据点分配一个聚类。这个包含fit和predict方法的封装函数被使用,因为 scikit-learn 中的 DBSCAN 算法并没有单独的predict()方法。同样,
0.1这个值是在尝试了所有其他可能的值后选择的。 -
绘制聚类过程的结果:
plt.scatter(data.iloc[:,0], data.iloc[:,1], c=pred_dbscan) plt.show()输出结果如下:
图 2.13:通过前面的代码获得的图表
与之前一样,两个特征被作为输入传递给 scatter 函数。此外,聚类过程中获得的标签被用作显示数据点颜色的依据。
创建的聚类总数为两个。
如你所见,每个算法创建的聚类总数不同。这是因为,如前所述,每个算法对相似性的定义不同,因此每个算法对数据的解释也不同。
因此,测试不同的算法来比较结果,并定义哪个算法对数据的泛化能力更强是至关重要的。接下来的内容将探讨一些我们可以用来评估性能的方法,以帮助选择算法。
注意
要访问这个练习的源代码,请参考packt.live/2Bcanxa。
你也可以在packt.live/2UKHFdp上在线运行这个示例。你必须执行整个 Notebook 才能得到预期的结果。
你已经成功导入并训练了 DBSCAN 算法。
总结来说,DBSCAN 算法通过数据空间中数据点的密度来进行聚类分类。这意味着聚类是由邻居较多的数据点组成的。通过以下方式实现:核心点是指在设定半径范围内包含最小数量邻居的数据点,边界点是指位于核心点半径范围内,但在自己的半径范围内没有最小数量邻居的数据点,噪声点则是指不符合任何规格的数据点。
活动 2.04:将 DBSCAN 算法应用于数据集
你还将对数据集应用 DBSCAN 算法。这基本上是因为在解决数据问题时,测试不同算法是一种良好的实践,以便选择最适合数据的算法,因为没有一种模型能在所有数据问题上表现良好。使用之前加载的批发消费者数据集,对数据应用 DBSCAN 算法并将数据分类到不同的聚类中。执行以下步骤:
-
打开你用于之前活动的 Jupyter Notebook。
-
训练模型并为数据集中的每个数据点分配一个聚类。绘制结果。
注意
本活动的解答可以在第 225 页找到。
聚类的可视化将根据选择的 epsilon 值和绘制的特征而有所不同。
评估聚类的表现
在应用聚类算法之后,必须评估算法的表现如何。特别是在难以直观评估聚类时,这一点尤为重要;例如,当数据有多个特征时。
通常,对于有监督的算法,通过将每个实例的预测值与其真实值(类别)进行比较,就很容易评估其性能。另一方面,处理无监督模型(如聚类算法)时,需要采用其他策略。
在聚类算法的具体情况下,可以通过衡量属于同一聚类的数据点的相似度来评估性能。
Scikit-Learn 中的可用度量
Scikit-learn 允许用户使用三种不同的得分来评估无监督聚类算法的表现。这些得分背后的主要思想是衡量聚类边界的清晰度,而不是衡量聚类内的分散度。因此,值得提到的是,这些得分并不考虑每个聚类的大小。
以下是用于衡量无监督聚类任务的两种最常用评分方法的解释:
-
**轮廓系数(Silhouette Coefficient Score)**计算每个点与同一聚类中所有其他点的平均距离(a),以及每个点与其最近聚类中所有其他点的平均距离(b)。它根据以下公式将二者联系起来:
s = (b - a) / max(a,b)得分结果的值介于-1 和 1 之间。值越低,算法的表现越差。接近 0 的值意味着聚类之间可能存在重叠。还需要澄清的是,这个得分在使用基于密度的算法(如 DBSCAN)时效果不太好。
-
Calinski-Harabasz 指数是为了衡量每个聚类的方差与所有聚类的方差之间的关系而创建的。更具体地说,每个聚类的方差是每个点相对于该聚类中心的均方误差。另一方面,所有聚类的方差是指聚类之间的总体方差。
Calinski–Harabasz 指数值越高,聚类的定义和分离度越好。没有可接受的截止值,因此,使用此指标的算法性能通过比较来评估,具有最高值的算法表现最好。与轮廓系数一样,该得分在基于密度的算法(如 DBSCAN)上表现不佳。
不幸的是,scikit-learn 库没有其他有效测量基于密度的聚类算法性能的方法,尽管这里提到的方法在某些情况下可以用来衡量这些算法的性能,但当它们不起作用时,除了通过人工评估外,没有其他办法来衡量此类算法的性能。
然而,值得一提的是,scikit-learn 中还有其他性能度量方法,适用于已知真实标签的情况,称为监督聚类;例如,当对已经选择专业或领域的新闻学学生进行聚类时,如果我们使用他们的个人信息以及一些学生记录来将他们分类为表示专业选择的聚类,就可以将预测分类与实际分类进行比较。
其中一些度量标准如下:
-
metrics模块,它接收真实聚类列表和预测聚类列表作为输入,格式如下:from sklearn.metrics import homogeneity_score score = homogeneity_score(true_labels, predicted_labels) -
完整性得分:与同质性得分相对,聚类任务满足完整性条件时,如果所有属于给定类别标签的数据点都属于同一聚类,则认为满足完整性要求。同样,输出的度量值在 0 到 1 之间,1 表示完美的完整性。该得分也是 scikit-learn 库中的度量标准之一,它接收真实标签和预测标签作为输入,格式如下:
from sklearn.metrics import completeness_score score = completeness_score(true_labels, predicted_labels)注意
要探索评估监督聚类任务性能的其他度量标准,请访问以下网址,在聚类部分查找:scikit-learn.org/stable/modu…
练习 2.05:评估轮廓系数得分和 Calinski–Harabasz 指数
在本次练习中,我们将学习如何计算前一节中讨论的、在 scikit-learn 中可用的两个得分。请按照以下步骤完成此练习:
-
从 scikit-learn 库中导入轮廓系数得分和 Calinski-Harabasz 指数:
from sklearn.metrics import silhouette_score from sklearn.metrics import calinski_harabasz_score -
计算我们在所有之前练习中建模的每个算法的轮廓系数得分。使用欧几里得距离作为度量标准,来衡量点与点之间的距离。
silhouette_score()函数的输入参数包括数据、模型的预测值(分配给每个数据点的聚类)以及距离度量方法:kmeans_score = silhouette_score(data, pred_kmeans, \ metric='euclidean') meanshift_score = silhouette_score(data, pred_meanshift, \ metric='euclidean') dbscan_score = silhouette_score(data, pred_dbscan, \ metric='euclidean') print(kmeans_score, meanshift_score, dbscan_score)前三行代码通过输入数据、预测结果和距离度量,调用
silhouette_score()函数计算每个模型(k-means、mean-shift 和 DBSCAN 算法)的评分。最后一行代码打印出每个模型的评分。k-means、mean-shift 和 DBSCAN 算法的评分分别为
0.359、0.3705和0.1139。你可以观察到,k-means 和 mean-shift 算法的评分相似,而 DBSCAN 的评分则接近零。这表明前两种算法的性能要好得多,因此,DBSCAN 算法不应被考虑用来解决数据问题。
然而,重要的是要记住,这种评分在评估 DBSCAN 算法时表现不佳。这主要是因为当一个簇围绕另一个簇时,评分可能会将其解释为重叠,而实际上这些簇是非常清晰定义的,就像当前数据集的情况一样。
-
计算我们在本章前面练习中所建模的每种算法的 Calinski-Harabasz 指数。
calinski_harabasz_score()函数的输入参数是数据和模型的预测值(分配给每个数据点的簇):kmeans_score = calinski_harabasz_score(data, pred_kmeans) meanshift_score = calinski_harabasz_score(data, pred_meanshift) dbscan_score = calinski_harabasz_score(data, pred_dbscan) print(kmeans_score, meanshift_score, dbscan_score)同样,前三行代码使用
calinski_harabasz_score()函数对三个模型进行计算,输入数据和预测结果,最后一行打印出结果。k-means、mean-shift 和 DBSCAN 算法的值分别约为
1379.7、1305.14和0.0017。再次强调,这些结果与我们使用轮廓系数评分时得到的结果相似,其中 k-means 和 mean-shift 算法表现良好,而 DBSCAN 算法则不行。此外,值得一提的是,每种方法的尺度(轮廓系数评分和 Calinski-Harabasz 指数)差异显著,因此它们不容易进行比较。
注意
要访问此特定部分的源代码,请参考
packt.live/3e3YIif。你也可以在网上运行这个示例,链接是
packt.live/2MXOQdZ。你必须执行整个 Notebook 才能得到期望的结果。
你已经成功地测量了三种不同聚类算法的性能。
总结来说,本主题中呈现的评分是一种评估聚类算法性能的方式。然而,重要的是要考虑,这些评分的结果并非决定性的,因为它们的性能因算法而异。
活动 2.05:测量和比较算法的性能
你可能会遇到一种情况,即无法图形化评估算法的性能,因此你无法确定算法的表现。在这种情况下,你需要使用数值指标来衡量算法的性能,并进行比较。对于之前训练的模型,计算轮廓系数分数和卡林斯基-哈拉巴兹指数来衡量算法的表现。以下步骤提供了有关如何进行此操作的提示:
-
打开你用于上一活动的 Jupyter Notebook。
-
计算你之前训练的所有模型的轮廓系数分数和卡林斯基-哈拉巴兹指数。
结果可能会因你在之前活动开发过程中做出的选择以及如何初始化每个算法中的某些参数而有所不同。然而,以下结果可以预期:将数据集分为六个簇的 k-means 算法,带有带宽为 0.4 的均值漂移算法,以及 epsilon 值为 0.8 的 DBSCAN 算法:
Silhouette Coefficient K-means = 0.3515 mean-shift = 0.0933 DBSCAN = 0.1685 Calinski-Harabasz Index K-means = 145.73 mean-shift = 112.90 DBSCAN = 42.45注意
这个活动的解答可以在第 226 页找到。
摘要
输入数据与标签输出无关的数据问题通过使用无监督学习模型来处理。此类数据问题的主要目标是通过寻找模式来理解数据,这些模式在某些情况下可以推广到新的实例。
在此背景下,本章介绍了聚类算法,这些算法通过将相似的数据点聚集成簇,同时将差异显著的数据点分开。
对数据集应用了三种不同的聚类算法,并比较了它们的表现,以便我们可以选择最适合数据的算法。我们还讨论了两种性能评估指标——轮廓系数(Silhouette Coefficient)和卡林斯基-哈拉巴兹指数(Calinski-Harabasz index),考虑到无法在图表中表示所有特征,因此无法通过图形化方式评估算法的表现。然而,重要的是要理解,指标评估的结果并非绝对的,因为某些指标(默认情况下)对某些算法比对其他算法表现得更好。
在下一章中,我们将了解使用监督学习算法解决数据问题的步骤,并学习如何进行误差分析。
第三章:3. 监督学习 – 关键步骤
概述
本章将介绍解决监督学习数据问题的关键概念。从数据集拆分开始,如何有效地创建不偏倚的模型,使其在未见数据上表现良好,你将学会如何衡量模型的性能,以便进行分析并采取必要的措施来改善模型。到本章结束时,你将牢固掌握如何拆分数据集、衡量模型性能和执行错误分析。
介绍
在前一章中,我们学习了如何使用无监督学习算法解决数据问题,并将所学的概念应用于真实数据集。我们还学习了如何比较不同算法的性能,并研究了两种性能评估指标。
本章将探讨解决监督学习问题的主要步骤。首先,本章将解释如何将数据拆分为训练集、验证集和测试集,以便对模型进行训练、验证和测试。接着,将解释最常见的评估指标。需要特别强调的是,在所有可用的评估指标中,应该仅选择一个作为研究的评估指标,并且其选择应基于研究的目的。最后,我们将学习如何进行错误分析,旨在了解采取何种措施来提高模型的结果。
前述概念适用于分类任务和回归任务,前者指的是输出对应有限数量标签的问题,而后者处理连续输出的数值。例如,一个用来判断某人是否会参加会议的模型属于分类任务组。另一方面,一个预测产品价格的模型则是在解决回归任务。
监督学习任务
与无监督学习算法不同,监督学习算法的特点是能够找到一组特征与目标值之间的关系(无论目标值是离散的还是连续的)。监督学习可以解决两种类型的任务:
-
分类:这些任务的目标是逼近一个函数,将一组特征映射到一个离散的结果集。这些结果通常称为类标签或类别。数据集中的每个观察值都应与一个类标签相关联,才能训练出能够为未来数据预测此类结果的模型。
一个分类任务的例子是使用人口统计数据来判断某人的婚姻状况。
-
回归:虽然在回归任务中也会创建一个函数来映射某些输入和某些目标之间的关系,但在回归任务中,结果是连续的。这意味着结果是一个实数值,可以是整数或浮动值。
回归任务的一个例子是使用产品的不同特征来预测其价格。
尽管许多算法可以被调整以解决这两项任务,但重要的是要强调,还是有些算法不能解决这两项任务,这就是为什么了解我们想要执行的任务是非常重要的,以便根据任务选择相应的算法。
接下来,我们将探讨一些对于执行任何监督学习任务至关重要的主题。
模型验证与测试
随着现在网上有了大量信息,几乎任何人都可以轻松开始一个机器学习项目。然而,当有许多选项可供选择时,为你的数据选择合适的算法是一项挑战。正因如此,选择一个算法而非另一个算法的决策是通过反复试验实现的,其中会测试不同的替代方案。
此外,做出一个优秀模型的决策过程不仅包括选择算法,还包括调整其超参数。为此,传统的方法是将数据划分为三部分(训练集、验证集和测试集),接下来会进一步解释。
数据划分
数据划分是一个过程,涉及将数据集划分为三个子集,以便每个子集可用于不同的目的。通过这种方式,模型的开发不受引入偏差的影响。以下是每个子集的解释:
-
训练集:顾名思义,这是用于训练模型的数据集部分。它由输入数据(观察值)与结果(标签类别)配对组成。这个集合可以用来训练任意数量的模型,使用不同的算法。然而,性能评估不会在此数据集上进行,因为由于该数据集用于训练模型,因此评估会有偏差。
-
验证集:也称为开发集,这个集合用于在微调超参数时对每个模型进行公正的评估。性能评估通常在这个数据集上进行,以测试不同的超参数配置。
尽管模型不会从这些数据中学习(它从训练集数据中学习),但由于它参与了决定超参数变化的过程,因此间接地受到了这个数据集的影响。
在基于模型在验证集上的表现运行不同的超参数配置后,将为每个算法选择一个最优模型。
-
测试集:用于在模型训练和验证后对未见数据进行最终评估。这有助于衡量模型在现实数据中的表现,为未来的预测提供参考。
测试集也用于比较不同的模型。考虑到训练集用于训练不同的模型,而验证集用于微调每个模型的超参数并选择最佳配置,测试集的目的是对最终模型进行无偏比较。
下图展示了选择理想模型并使用前述数据集的过程:
图 3.1:数据集划分目的
前面图示中显示的A–D部分描述如下:
-
Section
A指的是使用训练集中包含的数据训练所需算法的过程。 -
Section
B表示每个模型超参数的微调过程。最佳超参数配置的选择基于模型在验证集上的表现。 -
Section
C展示了通过比较每个算法在测试集上的表现,选择最终模型的过程。 -
最后,Section
D表示选定的模型,它将应用于实际数据进行预测。
最初,机器学习问题通过将数据划分为两部分来解决:训练集和测试集。这种方法与三部分数据集的方法相似,但测试集既用于微调超参数,也用于确定算法的最终性能。
尽管这种方法也能奏效,但使用这种方法创建的模型并不总是在未见过的实际数据上表现同样优秀。这主要是因为,如前所述,使用这些数据集来微调超参数间接地将偏差引入了模型中。
考虑到这一点,有一种方法可以在将数据集划分为两部分时实现更少偏差的模型,这就是所谓的交叉验证划分。我们将在本章的交叉验证部分深入探讨这一点。
划分比例
既然各种数据集的目的已经明确,接下来需要澄清数据划分的比例。虽然没有一种精确的科学方法来计算划分比例,但在进行划分时需要考虑以下几个因素:
-
数据集的大小:以前,由于数据不容易获取,数据集通常包含 100 到 100,000 个实例,公认的划分比例通常是 60/20/20%,分别用于训练集、验证集和测试集。
随着软件和硬件的不断改进,研究人员能够构建包含超过百万个实例的数据集。能够收集如此庞大的数据量使得分割比例可以达到 98/1/1%。这主要是因为数据集越大,能够用于训练模型的数据就越多,而不会影响留给验证集和测试集的数据量。
-
算法:需要考虑的是,一些算法可能需要更多的数据来训练模型,例如神经网络。在这种情况下,像前述方法一样,应该始终选择更大的训练集。
另一方面,一些算法不要求验证集和测试集必须平分。例如,一个具有较少超参数的模型可以容易地进行调整,这使得验证集可以小于测试集。然而,如果一个模型有许多超参数,则需要更大的验证集。
然而,尽管前述措施可以作为划分数据集的指南,始终需要考虑数据集的分布以及研究的目的。例如,若一个模型将用于预测与训练模型时使用的数据分布不同的结果,尽管实际数据有限,至少也必须确保实际数据的一部分包含在测试集中,以确保该模型能达到预期目的。
下图展示了数据集按比例划分为三子集。需要特别指出的是,训练集必须大于其他两个子集,因为它是用于训练模型的。此外,可以观察到,训练集和验证集都会对模型产生影响,而测试集主要用于验证模型在未知数据上的实际表现。考虑到这一点,训练集和验证集必须来自相同的分布:
图 3.2:分割比例可视化
练习 3.01:对样本数据集进行数据划分
在本次练习中,我们将使用分割比例法对wine数据集进行数据划分。此次练习的划分将采用三分法。按照以下步骤完成本次练习:
注
对于本章的练习和活动,您需要在系统上安装 Python 3.7、NumPy、Jupyter、Pandas 和 scikit-learn。
-
打开一个 Jupyter Notebook 以实现本次练习。导入所需的元素,以及 scikit-learn 的
datasets包中的load_wine函数:from sklearn.datasets import load_wine import pandas as pd from sklearn.model_selection import train_test_split第一行导入了将用于从 scikit-learn 加载数据集的函数。接下来,导入了
pandas库。最后,导入了train_test_split函数,它负责划分数据集。该函数将数据划分为两个子集(训练集和测试集)。由于本练习的目标是将数据划分为三个子集,因此该函数将被使用两次,以实现所需的结果。 -
加载
wine玩具数据集并将其存储在一个名为data的变量中。使用以下代码片段进行此操作:data = load_wine()load_wine函数加载的是 scikit-learn 提供的玩具数据集。注意
要查看数据集的特征,请访问以下链接:
scikit-learn.org/stable/modules/generated/sklearn.datasets.load_wine.html。前面函数的输出是一个类似字典的对象,将特征(可以作为数据调用)与目标(可以作为目标调用)分为两个属性。
-
将每个属性(数据和目标)转换为 Pandas DataFrame,以便进行数据操作。打印两个 DataFrame 的形状:
X = pd.DataFrame(data.data) Y = pd.DataFrame(data.target) print(X.shape,Y.shape)print函数的输出应如下所示:(178, 13) (178, 1)在这里,第一个括号中的值表示数据框
X(即特征矩阵)的形状,而第二个括号中的值表示数据框Y(即目标矩阵)的形状。 -
使用
train_test_split函数进行数据的首次拆分。使用以下代码片段进行此操作:X, X_test, Y, Y_test = train_test_split(X, Y, test_size = 0.2)train_test_split函数的输入是两个矩阵(X,Y)和测试集的大小,作为一个 0 到 1 之间的值,表示比例。print(X.shape, X_test.shape, Y.shape, Y_test.shape)通过打印所有四个矩阵的形状,按照前面的代码片段,可以确认测试子集的大小(
X和Y)是原始数据集总大小的 20%(150 * 0.2 = 35.6),四舍五入为整数,而训练集的大小是剩余的 80%:(142, 13) (36, 13) (142, 1) (36, 1) -
为了创建验证集(dev 集),我们将使用
train_test_split函数来拆分我们在前一步中获得的训练集。然而,为了得到与测试集相同形状的验证集,在创建验证集之前,需要计算测试集大小与训练集大小的比例。此比例将作为下一步的test_size:dev_size = 36/142 print(dev_size)在这里,
36是我们在前一步中创建的测试集的大小,而142是将进一步拆分的训练集的大小。此操作的结果大约是0.25,可以使用print函数进行验证。 -
使用
train_test_split函数将训练集分成两个子集(训练集和验证集)。使用前一步操作的结果作为test_size:X_train, X_dev, Y_train, Y_dev = train_test_split(X, Y, \ test_size = dev_size) print(X_train.shape, Y_train.shape, X_dev.shape, \ Y_dev.shape, X_test.shape, Y_test.shape)print函数的输出如下:(106, 13) (106, 1) (36, 13) (36, 1) (36, 13) (36, 1)注意
要访问该部分的源代码,请参阅
packt.live/2AtXAWS。你还可以在线运行这个示例,网址是
packt.live/2YECtsG。你必须执行整个 Notebook 才能获得所需的结果。
你已经成功地将数据集分成三个子集,以开发高效的机器学习项目。你可以自由地测试不同的划分比例。
总之,数据划分的比例并不是固定的,应该根据可用数据的数量、要使用的算法类型以及数据的分布来决定。
交叉验证
交叉验证也是一种通过重新抽样数据来划分数据的过程,这些数据用于训练和验证模型。它包含一个参数K,表示数据集将被划分为的组数。
由于此原因,该过程也称为 K 折交叉验证,其中K通常由你选择的数字代替。例如,使用 10 折交叉验证过程创建的模型意味着数据被分成 10 个子组。交叉验证过程在以下图表中进行了说明:
图 3.3:交叉验证过程
前面的图表展示了交叉验证过程中遵循的一般步骤:
-
数据会随机洗牌,考虑到交叉验证过程会被重复。
-
数据被划分为K个子组。
-
验证集/测试集被选为其中一个子组,其余子组构成训练集。
-
模型按常规在训练集上训练。模型使用验证集/测试集进行评估。
-
来自该迭代的结果已被保存。根据结果调整参数,并通过重新洗牌数据开始新的过程。这个过程重复进行 K 次。
根据前述步骤,数据集被分成K个子集,并且模型被训练K次。每次,选取一个子集作为验证集,剩余的子集用于训练过程。
交叉验证可以通过三分法或二分法来完成。对于前者,数据集首先被分成训练集和测试集,然后使用交叉验证将训练集进一步划分,创建不同的训练集和验证集配置。后者则是在整个数据集上使用交叉验证。
交叉验证之所以受欢迎,是因为它能够构建“无偏”的模型,因为它可以让我们衡量算法在数据集不同部分上的表现,这也让我们能了解算法在未见数据上的表现。它之所以流行,还因为它允许你在小数据集上构建高效的模型。
选择 K 值并没有准确的科学依据,但需要考虑的是,较小的 K 值倾向于降低方差并增加偏差,而较大的 K 值则会产生相反的效果。此外,K 值越小,过程的计算开销越小,从而运行时间更短。
注意
方差和偏差的概念将在偏差、方差与数据不匹配部分进行解释。
练习 3.02:使用交叉验证将训练集划分为训练集和验证集
在本练习中,我们将使用交叉验证方法对 wine 数据集进行数据划分。按照以下步骤完成此练习:
-
打开一个 Jupyter Notebook 来实现这个练习并导入所有需要的元素:
from sklearn.datasets import load_wine import pandas as pd from sklearn.model_selection import train_test_split from sklearn.model_selection import KFold前述代码的最后一行导入了
KFold类,它来自 scikit-learn,将用于划分数据集。 -
按照之前的练习加载
wine数据集,并创建包含特征和目标矩阵的 Pandas DataFrame:data = load_wine() X = pd.DataFrame(data.data) Y = pd.DataFrame(data.target) -
使用
train_test_split函数将数据分为训练集和测试集,该函数在之前的练习中已经学习过,test_size设置为 0.10:X, X_test, Y, Y_test = train_test_split(X, Y, \ test_size = 0.10) -
使用 10 折交叉验证配置实例化
KFold类:kf = KFold(n_splits = 10)注意
随意尝试不同的
K值,观察该练习输出的形状如何变化。 -
对
X中的数据应用split方法。此方法将输出用于训练和验证集的实例索引。该方法会创建 10 种不同的划分配置。将输出保存为名为splits的变量:splits = kf.split(X)请注意,不需要对
Y中的数据运行split方法,因为该方法只保存索引号,这在X和Y中是相同的。实际的划分将在后续处理。 -
执行一个
for循环,遍历不同的划分配置。在循环体内,创建用于存储训练集和验证集数据的变量。使用以下代码片段实现:for train_index, test_index in splits: X_train, X_dev = X.iloc[train_index,:], \ X.iloc[test_index,:] Y_train, Y_dev = Y.iloc[train_index,:], \ Y.iloc[test_index,:]for循环会遍历K次配置。在循环体内,数据将根据索引号进行划分:print(X_train.shape, Y_train.shape, X_dev.shape, \ Y_dev.shape, X_test.shape, Y_test.shape)通过打印所有子集的形状,按照前述代码段,输出如下:
(144, 13) (144, 1) (16, 13) (16, 1) (18, 13) (18, 1)注意
训练和评估模型的代码应该写在循环体内,因为交叉验证过程的目标是使用不同的划分配置来训练和验证模型。
你已经成功对一个样本数据集进行了交叉验证划分。
注意
若要访问此特定部分的源代码,请参考 packt.live/2N0lPi0。
你也可以在 packt.live/2Y290tK 在线运行此示例。必须执行整个 Notebook 才能获得预期结果。
总结来说,交叉验证是一种用于对数据进行洗牌并将其拆分为训练集和验证集的过程,每次训练和验证过程都会在不同的数据集上进行,从而获得一个具有低偏差的模型。
活动 3.01:手写数字数据集的数据分区
你的公司专注于识别手写字符。它希望提高数字的识别能力,因此他们收集了 1797 个从 0 到 9 的手写数字数据集。这些图像已经被转换为其数字表示,因此他们提供了该数据集供你将其拆分为训练/验证/测试集。你可以选择执行传统拆分或交叉验证。按照以下步骤完成此活动:
-
导入拆分数据集所需的所有元素,以及从 scikit-learn 中导入
load_digits函数以加载digits数据集。 -
加载
digits数据集并创建包含特征和目标矩阵的 Pandas 数据框。 -
采用传统的拆分方法,使用 60/20/20%的拆分比例。
-
使用相同的数据框,执行 10 折交叉验证拆分。
注意
本活动的解决方案可以在第 228 页找到。可以随意尝试不同的参数以得到不同的结果。
评估指标
模型评估对于创建有效模型是不可或缺的,这些模型不仅在用于训练模型的数据上表现良好,而且在未见过的数据上也能表现出色。评估模型的任务在处理监督学习问题时尤其容易,因为有一个可以与模型预测进行比较的真实值。
确定模型的准确度百分比对于其应用于没有标签类的数据至关重要。例如,具有 98%准确率的模型可能使用户认为预测准确的概率较高,因此应该信任该模型。
如前所述,性能评估应该在验证集(开发集)上进行,以微调模型,并在测试集上进行,以确定所选模型在未见过的数据上的预期表现。
分类任务的评估指标
分类任务指的是类别标签为离散值的模型,正如之前提到的那样。考虑到这一点,评估此类任务性能的最常见衡量标准是计算模型的准确度,即比较实际预测值与真实值。尽管在许多情况下,这可能是一个合适的度量标准,但在选择一个之前,还需要考虑其他几种标准。
现在,我们将查看不同的性能评估指标。
混淆矩阵
混淆矩阵是一个包含模型性能的表格,描述如下:
-
列表示属于预测类别的实例。
-
行表示实际属于该类别的实例(真实情况)。
混淆矩阵呈现的配置使得用户能够快速发现模型难度较大的领域。请看以下表格:
图 3.4:一个分类器的混淆矩阵,预测女性是否怀孕
从上述表格中可以观察到:
-
通过对第一行的值求和,可以知道有 600 个孕妇观察值。然而,在这 600 个观察值中,模型预测 556 个为孕妇,44 个为非孕妇。因此,模型预测女性怀孕的正确率为 92.6%。
-
关于第二行,也有 600 个非孕妇观察值。在这 600 个观察值中,模型预测 123 个为孕妇,477 个为非孕妇。模型成功预测非孕妇的准确率为 79.5%。
根据这些陈述,可以得出结论,模型在分类非孕妇观察值时表现最差。
考虑到混淆矩阵中的行表示事件的发生或未发生,列表示模型的预测,混淆矩阵中的值可以解释如下:
-
真正例(TP):指模型正确将事件分类为正例的实例——例如,正确分类为孕妇的实例。
-
假正例(FP):指模型错误地将事件分类为正例的实例——例如,错误分类为孕妇的非孕妇实例。
-
真反例(TN):指模型正确将事件分类为负例的实例——例如,正确分类为非孕妇的实例。
-
假反例(FN):指模型错误地将事件分类为负例的实例——例如,错误预测为非孕妇的孕妇实例。
混淆矩阵中的值可以如下表示:
图 3.5:显示混淆矩阵值的表格
准确率
如前所述,准确率衡量模型正确分类所有实例的能力。尽管这被认为是衡量性能的最简单方法之一,但在研究的目标是最小化/最大化某一类事件的发生,而与其他类的性能无关时,它可能并不是一个有用的指标。
图 3.4 中混淆矩阵的准确率计算如下:
图 3.6:显示准确率计算公式的方程式
这里,m是实例的总数。
86%的准确率指的是模型在分类两个类标签时的整体表现。
精确度
该指标衡量模型正确分类正类标签(代表事件发生的标签)的能力,通过与预测为正类的实例总数进行比较。它由真正例和真正例与假正例的和的比例表示,如下公式所示:
图 3.7:展示精确度计算的公式
精确度指标仅适用于二分类任务,其中只有两个类标签(例如,真或假)。它也可以应用于多类任务,前提是这些类被转换为两类(例如,预测手写数字是否为 6 或其他数字),其中一个类指的是具有某种条件的实例,另一个类则指不具有该条件的实例。
对于图 3.4中的示例,模型的精确度为 81.8%。
召回率
召回率(recall)衡量的是正确预测为正类标签的数量与所有正类标签的比例。它由真正例和假负例的和的比例表示:
图 3.8:展示召回率计算的公式
同样,这一度量应应用于两个类标签。对于图 3.4中的示例,召回率为 92.6%,与其他两个指标相比,表示该模型的最高表现。选择某个指标将取决于研究的目的,稍后会更详细地解释。
练习 3.03:在分类任务中计算不同的评估指标
在本次练习中,我们将使用乳腺癌玩具数据集,并使用 scikit-learn 库来计算评估指标。请按照以下步骤完成本次练习:
-
打开 Jupyter Notebook 以实现本练习,并导入所有所需的元素:
from sklearn.datasets import load_breast_cancer import pandas as pd from sklearn.model_selection import train_test_split from sklearn import tree from sklearn.metrics import confusion_matrix from sklearn.metrics import accuracy_score from sklearn.metrics import precision_score from sklearn.metrics import recall_score第四行导入了
tree模块,这是 scikit-learn 库中的一部分,将用于在本次练习的训练数据上训练决策树模型。下面的代码行将导入在本练习过程中计算的不同评估指标。 -
乳腺癌玩具数据集包含了对 569 名女性乳腺肿块分析的最终诊断(恶性或良性)。加载数据集并创建特征和目标 Pandas DataFrame,如下所示:
data = load_breast_cancer() X = pd.DataFrame(data.data) Y = pd.DataFrame(data.target) -
使用传统的拆分方法来划分数据集:
X_train, X_test, \ Y_train, Y_test = train_test_split(X,Y, test_size = 0.1, \ random_state = 0)请注意,数据集被分成了两个子集(训练集和测试集),因为本次练习的目的是学习如何使用 scikit-learn 包计算评估指标。
注意
random_state参数用于设置种子,确保每次运行代码时结果相同。这保证了你将获得与本练习中所示结果相同的结果。可以使用不同的数字作为种子;然而,为了获得与本章练习和活动中所示相同的结果,请使用建议的相同数字。 -
首先,从 scikit-learn 的
tree模块实例化DecisionTreeClassifier类。接着,在训练集上训练决策树。最后,使用该模型在测试集上预测类别标签。使用以下代码实现:model = tree.DecisionTreeClassifier(random_state = 0) model = model.fit(X_train, Y_train) Y_pred = model.predict(X_test)首先,使用
random_state实例化模型以设置种子。然后,使用fit方法通过训练集的数据(包括X和Y)训练模型。最后,使用predict方法对测试集中的数据(仅X)进行预测。Y_test中的数据将用于将预测结果与真实值进行比较。注意
训练监督学习模型的步骤将在第四章,监督学习算法:预测年收入,和第五章,人工神经网络:预测年收入中进一步讲解。
-
使用 scikit-learn 构建混淆矩阵,如下所示:
confusion_matrix(Y_test, Y_pred)结果如下,其中将真实值与预测值进行比较:
array([[21, 1], [6, 29]]) -
通过比较
Y_test和Y_pred,计算模型的准确率、精确度和召回率:accuracy = accuracy_score(Y_test, Y_pred) print("accuracy:", accuracy) precision = precision_score(Y_test, Y_pred) print("precision:", precision) recall = recall_score(Y_test, Y_pred) print("recall:", recall)结果如下所示:
accuracy: 0.8771 precision: 0.9666 recall: 0.8285由于正标签表示肿瘤为恶性,可以得出结论:模型预测为恶性的实例有 96.6%的高概率为恶性,但对于预测为良性的实例,模型有 17.15%(100%-82.85%)的错误概率。
注意
要访问此特定部分的源代码,请参考
packt.live/2Yw0hiu。你也可以在
packt.live/3e4rRtE在线运行此示例。你必须执行整个 Notebook,才能获得期望的结果。
你已经成功计算了分类任务的评估指标。
选择评估指标
有多种指标可用于衡量模型在分类任务中的表现,选择正确的指标是构建能够在研究目的上表现出色的模型的关键。
之前提到过,理解研究目的对于确定需要对数据集执行的预处理技术至关重要。此外,研究目的对于确定评估模型表现的理想指标也非常有帮助。
为什么研究的目的对选择评估度量标准很重要?因为通过了解研究的主要目标,可以决定是应该将注意力集中在模型的整体性能上,还是仅仅关注某一类标签的表现。
例如,一个用于识别图像中是否有鸟类的模型,如果它能够正确地识别出图像中是否有鸟类,而不是错误地将其他动物识别为鸟类,那么这个模型就是一个好模型。因此,模型只需要关注于提高正确分类鸟类的性能。
另一方面,对于一个用于识别手写字符的模型,任何一个字符都不比其他字符更重要,理想的度量标准应该是衡量模型整体准确率的度量。
如果选择了多个度量标准会怎样?就会很难得出模型的最佳性能,因为同时衡量两个度量可能需要采用不同的方法来改进结果。
回归任务的评估度量
考虑到回归任务的最终输出是连续的,没有固定数量的输出标签,因此真实值与预测值之间的比较是基于数值的接近程度,而不是它们是否完全相同。例如,在预测房价时,一个模型预测某房屋的价格为 299,846 美元,而真实价格为 300,000 美元,这个模型可以被认为是一个好的模型。
用于评估连续变量准确性的两种最常用度量标准是平均绝对误差(MAE)和均方根误差(RMSE),它们在这里得到了说明:
- 平均绝对误差(MAE):该度量衡量的是预测值与真实值之间的平均绝对差异,不考虑误差的方向。计算 MAE 的公式如下:
图 3.9:展示 MAE 计算的方程式
这里,m表示实例的总数,y是实际值,ŷ是预测值。
-
均方根误差(RMSE):这是一种二次度量,衡量的是真实值与预测值之间误差的平均大小。顾名思义,RMSE 是平方差的平均值的平方根,如下方公式所示:
](tos-cn-i-73owjymdk6/fe987978fd434a39ae3155937672bf35)
图 3.10:展示 RMSE 计算的方程式
这两种度量标准都表达了从 0 到无穷大的平均误差,其中数值越低,模型性能越好。它们之间的主要区别在于,MAE 对所有误差赋予相同的权重,而 RMSE 则对误差进行平方处理,从而赋予较大误差更高的权重。
考虑到这一点,RMSE 指标在较大误差应受到惩罚的情况下特别有用,这意味着在性能度量中会考虑异常值。例如,当偏差为 4 的值比偏差为 2 的值差了两倍时,可以使用 RMSE 指标。而 MAE 则在偏差为 4 的值仅是偏差为 2 的值两倍的情况下使用。
练习 3.04:计算回归任务中的评估指标
在本次练习中,我们将计算一个使用线性回归训练的模型的评估指标。我们将使用 boston 玩具数据集来完成此任务。按照以下步骤完成此练习:
-
打开一个 Jupyter Notebook 来实现此练习,并导入所有所需的元素,具体如下:
from sklearn.datasets import load_boston import pandas as pd from sklearn.model_selection import train_test_split from sklearn import linear_model from sklearn.metrics import mean_absolute_error from sklearn.metrics import mean_squared_error import numpy as np第四行导入了 scikit-learn 中的
linear_model模块,该模块将用于在训练数据集上训练一个线性回归模型。接下来的几行代码导入了将在本次练习中评估的两个性能指标。 -
本次练习将使用
boston玩具数据集。该数据集包含波士顿 506 个房价的数据。使用以下代码加载并拆分数据集,与我们之前的练习相同:data = load_boston() X = pd.DataFrame(data.data) Y = pd.DataFrame(data.target) X_train, X_test, Y_train, Y_test = train_test_split(X,Y, \ test_size = 0.1, random_state = 0) -
在训练集上训练一个线性回归模型。然后,使用该模型在测试集上预测类标签,具体如下:
model = linear_model.LinearRegression() model = model.fit(X_train, Y_train) Y_pred = model.predict(X_test)一般来说,首先会实例化 scikit-learn
linear_model模块中的LinearRegression类。然后,使用fit方法通过训练集的数据(包括X和Y)来训练模型。最后,使用predict方法对测试集中的数据(仅X)进行预测。Y_test中的数据将用于将预测结果与真实值进行比较。 -
计算 MAE 和 RMSE 两个指标:
MAE = mean_absolute_error(Y_test, Y_pred) print("MAE:", MAE) RMSE = np.sqrt(mean_squared_error(Y_test, Y_pred)) print("RMSE:", RMSE)结果如下所示:
MAE: 3.9357 RMSE: 6.4594注意
scikit-learn 库允许你直接计算 MSE。为了计算 RMSE,需要计算从
mean_squared_error()函数得到值的平方根。通过使用平方根,我们确保 MAE 和 RMSE 的值是可以比较的。从结果可以得出结论,模型在测试集上的表现良好,考虑到两个值都接近零。然而,这也意味着性能仍然可以进一步提升。
注意
若要访问此特定部分的源代码,请参考
packt.live/2YxVXiU。你也可以在线运行此示例,网址是
packt.live/2N0Elqy。你必须执行整个 Notebook 才能获得期望的结果。
你现在已经成功计算了回归任务中的评估指标,该任务旨在根据房屋的特征计算价格。在接下来的活动中,我们将计算一个分类模型的性能,该模型用于识别手写字符。
活动 3.02:评估在手写数据集上训练的模型的表现
你继续致力于创建一个识别手写数字的模型。团队已经构建了一个模型,他们希望你评估该模型的表现。在此活动中,你将计算训练模型的不同表现评估指标。按照以下步骤完成该活动:
-
导入所有必需的元素以加载并拆分数据集,用于训练模型并评估分类任务的表现。
-
从 scikit-learn 中加载
digits玩具数据集,并创建包含特征和目标矩阵的 Pandas 数据框。 -
将数据拆分为训练集和测试集。使用 20% 作为测试集的大小。
-
在训练集上训练一个决策树。然后,使用该模型预测测试集上的类别标签。
注意
要训练决策树,请回顾 练习 3.04,计算分类任务的不同评估指标。
-
使用 scikit-learn 构建混淆矩阵。
-
计算模型的准确率。
-
计算精确度和召回率。考虑到精确度和召回率只能在二分类问题上计算,我们假设只关注将实例分类为数字
6或其他任何数字。为了能够计算精确度和召回率,使用以下代码将
Y_test和Y_pred转换为一热向量。一个一热向量由仅包含零和一的向量组成。对于本活动,0 代表 数字 6,而 1 代表任何其他数字。这将类别标签(Y_test和Y_pred)转换为二进制数据,即只有两个可能的结果,而不是 10 个不同的结果。然后,使用新变量计算精确度和召回率:
Y_test_2 = Y_test[:] Y_test_2[Y_test_2 != 6] = 1 Y_test_2[Y_test_2 == 6] = 0 Y_pred_2 = Y_pred Y_pred_2[Y_pred_2 != 6] = 1 Y_pred_2[Y_pred_2 == 6] = 0你应该得到以下值作为输出:
Accuracy = 84.72% Precision = 98.41% Recall = 98.10%注意
本活动的解决方案可以在第 230 页找到。
错误分析
如前所述,通过使用 scikit-learn 库,构建一个平均模型是出乎意料的简单。构建一个优秀模型的关键方面来自研究人员的分析和决策。
如我们迄今所见,一些最重要的任务是选择和预处理数据集、确定研究目的以及选择合适的评估指标。在处理完这些内容并考虑到模型需要进行微调以达到最高标准后,大多数数据科学家建议训练一个简单的模型,无论超参数如何,以便开始研究。
错误分析 随后被引入,作为一种非常有用的方法论,将一个平均模型转化为一个卓越的模型。顾名思义,它包括分析数据集中不同子集的错误,以便定位影响模型的主要条件。
偏差、方差和数据不匹配
要了解可能影响机器学习模型的不同条件,重要的是要理解贝叶斯误差是什么。贝叶斯误差,也称为不可降低误差,是可以达到的最低可能错误率。
在技术和人工智能改进之前,贝叶斯误差被认为是人类可以达到的最低可能误差(人为误差)。例如,对于大多数人类以 0.1 的错误率完成的过程,但顶级专家以 0.05 的错误率完成的过程,贝叶斯误差将为 0.05。
然而,现在贝叶斯误差被重新定义为机器可以达到的最低可能误差,这是未知的,考虑到我们作为人类只能理解到人类误差的程度。因此,当使用贝叶斯误差来分析错误时,一旦模型低于人类误差,就不可能知道最低限度。
以下图表有助于分析不同数据集之间的错误率,并确定对模型影响最大的条件。该图的目的是找出彼此之间错误差异最大的错误,以便相应地诊断和改进模型。重要的是强调,每个集合的错误值是通过从该集合的评估指标(例如准确率)减去 100%来计算的:
图 3.11:错误分析方法论
考虑前述图表,执行错误分析的过程如下:
-
计算所有数据集的性能评估。此措施用于计算每个集合的错误。
-
从底部到顶部开始计算差异如下:
将开发集误差(12%)减去测试集误差(12%)。得到的数值(0%)保存。
将训练/开发集误差(9%)减去开发集误差(12%)。得到的数值(3%)保存。
将训练集误差(8%)减去训练/开发集误差(9%)。得到的数值(1%)保存。
将贝叶斯误差(2%)减去训练集误差(8%)。得到的数值(6%)保存。
-
更大的差异确定最严重影响模型的条件。在本例中,更大的差异出现在贝叶斯误差和训练集误差之间,如前述图表所示,这表明模型正遭受高偏差的影响。
注
训练/开发集是训练集和验证集(开发集)中数据的组合。它通常与开发集具有相同的形状,并包含来自两个集合的相同数量的数据。
下面解释每个条件及一些避免/修复它们的技术:
-
高偏差:也称为欠拟合,这种情况发生在模型没有从训练集中学习时,导致模型在所有三种数据集(训练集、验证集和测试集)上表现较差,且对未见过的数据也表现不佳。
欠拟合是最容易检测到的情况,通常需要更换不同的算法,以更好地适应可用数据。对于神经网络而言,通常可以通过构建更大的网络或训练更长时间来解决。
-
高方差:也称为过拟合,这种情况指的是模型在处理与训练集不同的数据时表现不佳。这基本上意味着模型通过学习数据的细节和离群点来过拟合训练数据,而没有进行任何概括。一个遭受过拟合的模型在开发集、测试集或未见过的数据上表现不佳。
过拟合可以通过调整算法的不同超参数来解决,通常目标是简化算法对数据的近似。例如,对于决策树,可以通过修剪树来删除一些从训练数据中学习到的细节来解决。另一方面,对于神经网络,可以通过添加正则化技术来减少神经元对整体结果的影响。
此外,向训练集添加更多数据也有助于模型避免高方差,即增加用于训练模型的数据集。
-
数据不匹配:当训练集和验证集不遵循相同的分布时,就会发生数据不匹配。这会影响模型的表现,因为虽然它基于训练数据进行概括,但这种概括并不描述验证集中出现的数据。例如,若一个模型是用来描述风景照片的,但它是用高清图像训练的,而实际使用的图像是非专业的,这时就会出现数据不匹配问题。
从逻辑上讲,避免数据不匹配的最佳方法是确保各数据集遵循相同的分布。例如,你可以通过将来自两个来源(专业和非专业图像)的图像一起打乱,然后将它们划分到不同的集合中来实现这一点。
然而,在数据不足以遵循与未来未见数据(将来用于训练的数据)相同分布的情况下,强烈建议完全从这些数据中创建开发集和测试集,并将剩余的数据添加到大型训练集中。从前面的示例中,应该使用非专业图像来创建开发集和测试集,将剩余的图像与专业图像一起添加到训练集中。这有助于使用包含足够图像以进行泛化的训练集来训练模型,但它使用与未见数据分布相同的数据来微调模型。
最后,如果所有集合的数据来自相同的分布,那么这个条件实际上指的是一个高方差问题,应按照这种方式进行处理。
-
对开发集的过拟合:最后,类似于方差问题,当模型没有进行泛化,而是过度拟合开发集时,会发生这种情况。
应使用与前面解释的高方差问题相同的方法来处理该问题。
在下一个练习中,我们将计算模型在不同数据集上的错误率,这可用于进行错误分析。
练习 3.05:计算不同数据集上的错误率
在本次练习中,我们将计算使用决策树训练的模型的错误率。我们将使用乳腺癌数据集来完成此任务。按照以下步骤完成本次练习:
-
打开 Jupyter Notebook 来实现本次练习,并导入所有需要的元素以加载和拆分数据集。这些将用于训练模型并评估其召回率:
from sklearn.datasets import load_breast_cancer import pandas as pd from sklearn.model_selection import train_test_split import numpy as np from sklearn import tree from sklearn.metrics import recall_score -
在本次练习中,将使用
乳腺癌数据集。使用以下代码加载数据集,并创建包含特征和目标矩阵的 Pandas DataFrame:breast_cancer = load_breast_cancer() X = pd.DataFrame(breast_cancer.data) Y = pd.DataFrame(breast_cancer.target) -
将数据集拆分为训练集、验证集和测试集:
X_new, X_test, Y_new, Y_test = train_test_split(X, Y, \ test_size = 0.1, random_state = 101) test_size = X_test.shape[0] / X_new.shape[0] X_train, X_dev, Y_train, Y_dev = train_test_split(X_new, Y_new, \ test_size = test_size, \ random_state = 101) print(X_train.shape, Y_train.shape, X_dev.shape, \ Y_dev.shape, X_test.shape, Y_test.shape)结果的形状如下:
(455, 30) (455, 1) (57, 30) (57, 1) (57, 30) (57, 1) -
创建一个结合了训练集和验证集数据的训练/开发集:
np.random.seed(101) indices_train = np.random.randint(0, len(X_train), 25) indices_dev = np.random.randint(0, len(X_dev), 25) X_train_dev = pd.concat([X_train.iloc[indices_train,:], \ X_dev.iloc[indices_dev,:]]) Y_train_dev = pd.concat([Y_train.iloc[indices_train,:], \ Y_dev.iloc[indices_dev,:]]) print(X_train_dev.shape, Y_train_dev.shape)首先,设置一个随机种子,以确保结果的可重复性。接下来,使用 NumPy
random.randint()函数从X_train集合中选择随机索引。为此,在 0 到X_train总长度之间生成 28 个随机整数。相同的过程用于生成开发集的随机索引。最后,创建一个新变量来存储从X_train和X_dev中选择的值,并创建一个变量来存储来自Y_train和Y_dev的相应值。已创建的变量包含来自训练集的 25 个实例/标签和来自开发集的 25 个实例/标签。
结果集的形状如下:
(50, 30) (50, 1) -
在训练集上训练决策树,如下所示:
model = tree.DecisionTreeClassifier(random_state = 101) model = model.fit(X_train, Y_train) -
使用
predict方法生成所有数据集(训练集、训练/开发集、开发集和测试集)的预测。接下来,考虑到本研究的目标是最大化模型预测所有恶性案例的能力,计算所有预测的召回率分数。将所有分数存储在名为scores的变量中:sets = ["Training", "Train/dev", "Validation", "Testing"] X_sets = [X_train, X_train_dev, X_dev, X_test] Y_sets = [Y_train, Y_train_dev, Y_dev, Y_test] scores = {} for i in range(0, len(X_sets)): pred = model.predict(X_sets[i]) score = recall_score(Y_sets[i], pred) scores[sets[i]] = score print(scores)所有数据集的误差率如下:
{'Training': 1.0, 'Train/dev': 0.9705882352941176, 'Validation': 0.9333333333333333, 'Testing': 0.9714285714285714}从上述值中,可以创建以下包含误差率的表格:
图 3.12:所有数据集的误差率
在这里,假设贝叶斯误差为 0,因为恶性肿瘤和良性肿瘤的分类是通过活检进行的。
从上表可以得出结论,考虑到所有的误差率接近 0,即最低可能的误差,该模型在研究目的上表现得非常出色。
最高的误差率差异出现在训练集/开发集和开发集之间,这意味着数据不匹配。然而,考虑到所有数据集来自相同的分布,这种情况被认为是一个高方差问题,增加更多的数据到训练集中应该有助于减少误差率。
注
要访问该部分的源代码,请参考 packt.live/3e4Toer。
你也可以在线运行这个示例,网址是 packt.live/2UJzDkW。你必须执行整个 Notebook 才能得到预期的结果。
你已经成功计算出了所有数据子集的误差率。在接下来的活动中,我们将进行误差分析,以定义改进已创建的手写数字识别模型性能的步骤。
活动 3.03:对训练手写数字识别模型进行误差分析
根据你提供给团队的不同指标,他们已选择准确率作为理想的度量标准。考虑到这一点,你的团队要求你进行误差分析,以确定如何改进模型。在此活动中,你将通过比较不同数据集的误差率来进行误差分析,以评估模型的准确度。请按照以下步骤进行:
-
导入加载和拆分数据集所需的元素。我们将这样做来训练模型并衡量其准确性。
-
从 scikit-learn 中加载
digits玩具数据集,并创建包含特征和目标矩阵的 Pandas DataFrame。 -
将数据拆分为训练集、验证集和测试集。使用 0.1 作为测试集的大小,并构建一个大小相同的验证集。
-
为特征和目标值创建一个包含 90 个训练集实例/标签和 90 个开发集实例/标签的训练/开发集。
-
在训练集数据上训练一个决策树模型。
-
计算所有数据集的错误率,评估模型的准确性,并确定哪些条件影响模型的表现。
完成此活动后,您应获得以下错误率:
图 3.13:预期的错误率
注意
本活动的解决方案可以在第 233 页找到。
摘要
本章解释了可以通过监督学习算法解决的不同任务:分类和回归。虽然这两种任务的目标都是近似一个将一组特征映射到输出的函数,但分类任务有离散的输出数量,而回归任务的输出可以是无限连续的值。
在开发机器学习模型以解决监督学习问题时,主要目标之一是让模型具有良好的泛化能力,从而能够应用于未来未见的数据,而不仅仅是学习一组实例并在新数据上表现不佳。因此,本章解释了一种验证和测试的方法论,涉及将数据划分为三组:训练集、开发集和测试集。这种方法消除了偏差的风险。
之后,我们介绍了如何评估分类和回归问题中模型的表现。最后,我们讨论了如何分析模型的表现并对每个数据集进行错误分析,以检测影响模型表现的条件。
在下一章,我们将重点应用不同的算法到实际的数据集,旨在将我们在本章学到的步骤应用于选择最适合此案例研究的算法。