无标签的数据指南-全-
无标签的数据指南(全)
原文:
zh.annas-archive.org/md5/01b792d0206be04c143ec164b07b9015译者:飞龙
第一部分 基础
欢迎来到第一部分。
机器学习和人工智能并非魔法。它们也不是只有少数人才能理解的秘密艺术。它们的本质,仅仅是我们帮助算法吸收历史数据集并为我们生成洞察的一种方式。这些洞察帮助我们启动更好、更快、更有影响力的商业效果。我们给出清晰、逻辑的指令,引导算法完成我们想要的事情。
但是,就像任何艺术一样,学习机器学习和人工智能需要练习。这并不关于记忆 Python 或 R 或编程语言语法,或者学习一些运行代码或复制粘贴代码的命令。这是关于解决实际商业问题,批判性地思考商业目标,并将那些复杂任务细致地分解成更小、更易管理的步骤,从而实现商业目标。
这本书不仅仅关于编写代码;它是关于学习如何像数据科学家一样思考。
如果你从未学习过无监督学习,或者你从未编写过一行 Python 代码,那完全没问题。这比你想象的要容易得多。我们从简单的无监督学习算法开始。
在这个旅程中,祝您一切顺利。让我们一步一步从基础开始。
第一章:机器学习简介
本章涵盖
-
数据、数据集类型、质量和来源的介绍
-
机器学习和机器学习算法类型
-
不同类型算法的概述
只存在模式,模式之上的模式,影响其他模式的模式。被模式隐藏的模式。模式中的模式。——查克·帕拉纽克
有一种说法在流传:“数据是新的电力。”数据确实正在改变我们的世界,就像电力一样;没有人能否认这一点。但就像电力一样,我们必须记住,数据必须得到适当的利用才能发挥其价值。我们必须清理数据、分析和可视化它,然后才能从中发展出洞察力。数据科学、机器学习(ML)和人工智能领域正在帮助我们更好地利用数据,提取趋势和模式,以便我们能在我们的活动和业务中做出更深入和平衡的决策。
在这本书中,我们将解开数据的谜团,看看我们如何找到隐藏在其中的模式。我们将研究被称为无监督学习的机器学习的一个分支。无监督学习解决方案是最有影响力的方法之一,正在改变行业的面貌。它们被用于银行和金融、零售、保险、制造、航空、医学科学、电信以及几乎每个其他行业。
在整本书中,我们将侧重于无监督学习来讨论机器学习的概念——算法的构建块、它们的细节、后台过程和数学基础。我们将检查概念、研究最佳实践、分析常见错误和陷阱,并采用基于案例研究的方法来补充学习。同时,我们将开发实际的 Python 代码来解决这些问题。所有代码都附有逐步解释和注释。
当你完成这本书时,你将对基于无监督技术的机器学习、各种算法、算法所依赖的数学和统计基础、商业用例、Python 实现和最佳实践有一个非常好的理解。
这第一章旨在介绍机器学习的概念。我们将从讨论所有数据分析与机器学习的基础概念开始:数据本身、数据的管理方式以及构成高质量数据的要素。然后,我们将转向在机器学习和深度学习的背景下讨论数据分析,考虑不同类型的机器学习算法,并以考虑为实际操作本书内容所推荐的工具集来结束。欢迎来到第一章,并祝您一切顺利!
1.1 技术工具集
以下工具用于项目的不同方面:
-
数据工程——Hadoop、Spark、Scala、Java、C++、SQL、Redshift、Azure、PySpark
-
数据分析——SQL、R、Python、Excel
-
ML——SQL、R、Python、Excel、Weka、Julia、MATLAB、SPSS、SAS
-
可视化——Tableau、Power BI、Qlik、COGNOS
-
模型部署—Docker、Flask、Amazon S3
-
云服务—Azure、AWS、GCP
在这本书中,我们将使用 Python。建议你在系统上安装 Python 的最新版本。至少建议使用 3.5+版本,尽管截至本书写作时的最新版本是 3.13。我们还将使用 Jupyter Notebook,因此建议你在系统上安装 Anaconda。
注意:所有代码和数据集都将存放在 GitHub 仓库中:github.com/vverdhan/DataWithoutLabels。你被期望复制它们并尝试重现结果。
1.2 数据、数据类型、数据管理和质量
我们首先介绍本书的主角:数据。数据可以被看作是为了执行任何类型的分析或研究而收集的事实和统计数据。但数据也有其自身的特性、属性、质量指标和管理原则。它被存储、导出、加载、转换和度量。从这个意义上说,数据在其自身范围内是一个有形的“东西”,并且必须妥善处理才能正确利用它。为了做到这一点,我们必须正确理解数据。
让我们从基础开始:数据的定义。一旦我们定义了数据,我们将继续讨论不同类型的数据、它们的相应示例以及使数据有用和高质量的数据属性。
1.2.1 什么是数据?
数据无处不在。你使用移动网络打电话;在这个过程中,你正在生成数据。你预订即将到来的假期机票和酒店;数据正在被创建。我们日常活动生成的数据可能包括进行银行交易、浏览社交媒体或在网上购物。这些数据从一种形式转换成另一种形式,被存储、清理、管理和分析。那么,它实际上是什么呢?
正式来说,数据是一系列事实、观察、度量、文本、数字、图像和视频的集合。数据集可能是干净的(即,组织得没有错误、不一致和不相关信息)或不干净的,可能是有序的(例如,按字母顺序)或无序的,或者包含混合的数据类型或全部是同一类型。正如提到的,数据本身在没有经过清理、整理、分析和从中提取见解之前是没有用的。我们可以在图 1.1 中可视化从原始数据到更有用形式的转变。

图 1.1 我们如何将原始数据转换为信息、知识,最终成为可用于商业以驱动决策和行动的见解
当我们能在数据中找到区别时,原始数据就转化为信息。当我们关联术语并“连接点”时,同一块信息就变成了知识。洞察力是我们可以找到主要中心和重要点的阶段。一个洞察力应该是可操作的、简洁的、直接的。例如,如果一个电信运营商的客户保留团队被告知,九天未打电话的客户比打电话的客户有 30%更高的流失率,这将是一个他们可以着手解决的有用洞察力。同样,如果一个制造工厂的线路技术人员被告知,使用模具 X 比使用模具 Y 导致 60%更多的缺陷,他们将来将避免使用表现不佳的模具。洞察力对业务团队非常有用,因为他们可以考虑到它并采取纠正措施。
1.2.2 各种类型的数据
正如我们之前讨论的,数据是由我们日常生活中的许多活动产生的。我们可以将这些数据大致分为不同的类型,如图 1.2 所示。

图 1.2 数据的划分和细分
数据可以分为定量和定性类别,这些类别进一步细分为子类别:
-
定性数据是那种无法测量或称重的数据类型——例如,味道、颜色、气味、健康、名称等。它们只能主观地观察。正式地说,当我们对某物进行分类或为其做出分类时,产生的数据就是定性的。例如,彩虹中的颜色、一个国家中的城市、产品的质量、性别等。它们也被称为分类变量。定性数据可以进一步细分为二元、名义和有序数据集:
-
二元数据,正如其名所示,只有两个相互排斥的类别。例如,是/否、干/湿、硬/软、好/坏、真/假等。
-
名义数据可以描述为一种类型的数据,尽管被分类,但没有任何顺序或顺序。例如,一个国家中使用的不同语言、彩虹中的颜色、提供给客户的各类服务、一个国家中的城市等。
-
有序数据与名义数据相似,但我们可以在一个序列中对其进行排序。例如,快/中/慢、积极/中性/消极等。
-
-
定量数据是所有可以测量、称重、缩放、记录等的数据点类型。例如,身高、收入、客户数量、需求量、面积、体积等。它们是最常见的数据形式,允许进行数学和统计操作。定量数据进一步细分为离散和连续:
-
离散数据是精确的、直接的,并以整数表示。例如,飞机上的乘客数量或一个城市的总人口不能是小数。
-
连续数据点可以取任何值,通常在一个范围内。例如,身高可以取小数值,或者产品的价格不必是整数。
-
任何数据点通常都会根据其属性落入这些类别之一。还可以通过来源和使用方式进一步进行一种逻辑分组,这在解决商业问题时非常有意义。这种分组使我们能够设计出针对数据类型的定制化解决方案。
根据来源和使用方式,我们还可以将数据分为两大类:结构化数据和非结构化数据。可以轻松表示为行列结构的数据集是结构化数据集。例如,一家零售店五名客户的交易可以像表 1.1 所示那样存储。
表 1.1 一个具有金额、日期、城市、项目等属性的示例结构化数据集。
| 客户 ID | 交易日期 | 金额($) | 项目数量 | 支付方式 | 城市 |
|---|---|---|---|---|---|
| 1001 | 01-June-2024 | 100 | 5 | 现金 | 新德里 |
| 1002 | 02-June-2024 | 101 | 6 | 卡 | 纽约 |
| 1003 | 03-June-2024 | 102 | 7 | 卡 | 伦敦 |
| 1004 | 04-June-2024 | 103 | 8 | 现金 | 都柏林 |
| 1005 | 05-June-2024 | 104 | 9 | 现金 | 东京 |
在表 1.1 中,对于每个唯一的客户 ID,我们有交易日期、花费的美元金额、购买的项目数量、支付方式和交易发生的城市。这种数据类型可以扩展到员工详情、通话记录、银行交易等。
注意:在分析和模型构建中使用的多数数据是有结构的。结构化数据更容易存储、分析和以图表和图形的形式可视化。
许多算法和技术针对结构化数据——在正常现实世界的语言中,我们主要指的是结构化数据。非结构化数据不容易排序到行列结构中。它可以是有文本、音频、图像或视频。图 1.3 显示了非结构化数据及其相应的来源,以及非结构化数据的主要类型:文本、图像、音频和视频及其示例。

图 1.3 非结构化数据及其各种类型和示例。这类数据通常复杂,通常需要基于深度学习的算法。
计算机和处理器只理解二进制数。因此,这些非结构化数据点仍然需要以数字的形式表示,以便我们可以在其上进行数学和统计计算。例如,一张图片由像素组成。如果是一张彩色图片,每个像素将具有 RGB(红、绿、蓝)值,每个 RGB 可以取值(0–255)。因此,我们可以将图片表示为一个矩阵,可以在其上进行进一步的数学计算。文本、音频和视频可以类似地表示。
注意:一般来说,卷积神经网络(CNN)和循环神经网络(RNN)等基于深度学习的解决方案用于非结构化数据。我们将在本书的后续阶段研究文本,并探讨 CNN 和 RNN。
通过一个例子可以理解非结构化数据:考虑一个吸尘器的图片,如图 1.4 所示。图像的一部分可以表示为一个矩阵,看起来就像图中的矩阵。这个例子只是为了说明目的,并不显示实际值。

图 1.4 如何将非结构化数据表示为矩阵以进行分析的示例。右侧的矩阵仅是示意,并非实际数字。
同样,我们可以有文本、音频或视频数据的表示。由于这类数据通常具有较大的尺寸和大量的维度,这种非结构化数据处理和建模比较复杂,因此,通常基于深度学习的模型可以满足这一需求。
除了我们之前讨论的广泛类型的数据外,我们还可以有更多类别,如比率或刻度,可以用来定义一个变量与另一个变量的关系。所有这些数据点(无论是结构化还是非结构化)都是根据它们在现实生活中的生成方式来定义的。
所有这些数据点都需要被捕捉、存储和管理。有许多工具可用于管理数据,我们将在适当的时候讨论。但在那之前,让我们检查一个最关键但通常讨论较少的主题:数据质量。
1.2.3 数据质量
“垃圾输入,垃圾输出”——这一原则总结了高质量数据的重要性。如果数据是脏的或错误的,并且变量之间缺乏任何业务关系,我们就无法解决手头的业务问题。但“高质量”的含义是什么?想象一下,你想根据去年的每日降雨量预测今年的降雨量。为此任务,一个高质量的数据集应该是尽可能完整的(非常少的降雨量测量缺失日)。它应该是相关和有效的(例如,覆盖你预测的同一地区),测量应该是准确的,数据应该容易访问和使用,而无需处理权限问题。相反,一个差劲的数据集可能有很多“漏洞”,可能在远离你希望研究地点的地区进行测量(使其相关性降低),或者可能难以访问。正如你可以无疑地推断出的,高质量数据有助于产生高质量的结果,而差的数据质量会积极阻碍你的工作,并可能导致结果不佳。数据质量的主要组成部分如图 1.5 所示。让我们逐一探讨它们。

图 1.5 数据质量至关重要;展示了高质量数据的属性。
良好质量数据的重大属性包括
-
完整性—我们希望我们的数据集是完整的,不缺少任何值。例如,如果我们正在处理一年的销售数据,良好的数据将包含所有 12 个月的所有值。然后它将是一个完整的数据源。数据集的完整性确保我们没有遗漏重要的变量或数据点。
-
有效性—数据的有效性是指其符合我们在用例中分析存在的属性、特征和变化。有效性表明我们捕获的观察和测量是否可靠和有效。例如,如果研究的范围是 2015-2019 年,那么使用 2014 年的数据将是无效的。
-
准确性—准确性是一个关注数据正确性的属性。如果我们有不准确的数据,我们将生成不准确的见解,行动将出错。一个好的做法是在项目开始时生成关键绩效指标(KPIs),并将它们与业务报告的数字进行比较,以检查我们可用的数据的真实性。
-
代表性—这是数据最重要的属性之一,通常也是最受忽视的。数据的代表性意味着正在使用的数据真正捕捉到业务需求,并且没有偏见。如果数据集有偏见或代表性不足,生成的模型将无法对新数据和未见数据做出预测,整个努力将付诸东流。
-
可用性—数据不可用是我们经常面临的一个挑战。对于商业问题,数据可能不可用,然后我们面临是否继续使用用例的困境。有时我们面临运营挑战,无法访问数据库或存在权限问题,或者由于未捕获,特定变量的数据可能根本不可用。在这种情况下,我们必须使用可用的数据并使用代理变量。例如,假设我们正在处理一个需求生成问题。我们希望预测在即将到来的销售季节中,特定商店可以预期有多少客户。但我们没有记录过去几个月访问的客户数量。然后我们可以使用收入作为代理字段并综合缺失的数据点。
-
一致性—在这里,我们检查数据点是否在系统和接口之间保持一致。不应该出现一个系统报告不同的收入数字,而另一个系统显示完全不同的值的情况。面对这样的问题时,我们将根据可用的数据生成相应的 KPIs,并寻求业务团队的指导。
-
及时性—及时性简单地说就是我们现在拥有所有所需的数据。如果数据集现在不可用,但将来可能会可用,那么等待可能是明智的。
-
完整性——我们所拥有的数据表和变量是相互关联和相互关联的。例如,员工的详细信息可能分布在多个表中,这些表通过员工的 ID 相互链接。数据完整性解决了这一需求,并确保所有此类表与相应实体之间的关系都是一致的。
数据质量至关重要。在务实的日常业务中,我们往往得不到高质量的数据。由于多种挑战,高质量、干净、可访问、一致、具有代表性且完整的数据很少找到。
质量下降可能是由于数据捕获和收集、导出或加载、转换等过程中的挑战。以下是一些可能性:
-
我们可以在一些列中获取整数作为名称,或者像“#$!&”这样的特殊字符,或者某些值可能是空值、空白或非数字(NaN)。
-
记录中可能有重复项。
-
可能会出现异常值。这是我们经常需要处理的一个麻烦。例如,假设一个在线零售商的平均每日交易量是 1,000。有一天,由于服务器问题,没有进行任何交易。这是一个异常情况。或者,有一天,交易量达到了 1,000,000。这又是一个异常的例子。异常值可能会偏颇我们创建的算法。
-
可能存在关于一天中的时间和一周中的日子的季节性变化和移动——所有这些都应该在数据集中有足够的代表性。
-
日期格式的不一致性可能导致我们在尝试合并多个数据源时遇到多个挑战。例如,源 1 可能使用 DD/MM/YYYY,而另一个可能使用 MM/DD/YYYY。这已经在数据加载步骤中得到了处理。
所有这些异常和质量问题都应该得到解决并彻底清理。我们将在整本书中解决这些数据问题,并分享应遵循的最佳实践。
注意:您原始数据的质量以及清理过程中的严谨性直接影响到您最终分析的质量和解决方案的成熟度。
我们现在已经定义了数据的主要属性。接下来,我们将研究用于数据工程和管理的广泛流程和技术。
1.2.4 数据工程和管理
强大的数据工程流程和成熟的数据管理实践是成功机器学习模型解决方案的先决条件。无论您来自数据工程还是数据科学背景,两者都相辅相成;数据工程师通过了解数据科学的基础知识将受益匪浅,反之亦然。图 1.6 提供了一个高级概述,说明了工程流程和管理实践可能的样子。数据从捕获、数据管道到加载,直到准备分析的全过程都被描述了。

图 1.6 数据工程为数据分析铺平了道路。它涉及数据加载、转换、丰富、清洗、准备等,从而产生了适合分析的数据。
在数据工程步骤中,数据被清洗、规范、重塑、转换和摄取。通常,我们有一个服务器,最终数据托管在那里,并准备好供访问。最常用的过程是创建一个导出、转换、加载(ETL)过程。然后我们使数据准备好进行分析。我们创建新的变量,处理空值,用方法丰富数据,然后最终进入分析/模型构建阶段。
许多时候,我们发现像数据分析、数据科学、机器学习、数据挖掘、人工智能、商业智能、大数据等术语在商业中经常被互换使用。明确它们是一个好主意,这也是下一节的主题。针对我们讨论的每个相应功能,都有大量的工具可用。我们还将了解软件工程在整个过程中的作用。
1.3 数据分析、机器学习、人工智能和商业智能
机器学习(ML)和人工智能(AI)是相对较新的领域,因此,它们的工作范围在标准化和区分上很少。这导致了这些领域的定义和界限不明确。我们考察了这些领域——它们的重叠部分、差异部分以及一个如何增强另一个。每个功能都增强并补充了另一个,如图 1.7 所示。

图 1.7 各个领域如何相互联系以及它们如何相互依赖
在业务问题被正确定义和范围界定之后,我们开始技术流程。数据挖掘和数据工程通过提供分析所需的数据来启动整个过程。它还导出、转换、清洗和加载数据,以便所有相关功能都可以使用。商业智能和可视化使用这些数据生成报告和仪表板。数据分析使用数据生成洞察和趋势。数据科学建立在数据分析、统计学、商业智能、数据可视化、机器学习和数据挖掘的基础上。机器学习创建统计和数学模型,而人工智能进一步扩展了这些能力。
机器学习使用传统的编码。编码是在传统语言(如 Python)中进行的,因此计算机科学和软件工程的逻辑和规则在机器学习中同样有效。机器学习帮助我们理解我们通常无法理解的数据。机器学习最迷人的优势是它能够处理非常复杂和高维度的数据点,如视频、音频、图像、文本或由传感器生成的复杂数据集。它使我们能够超越显而易见的事物。现在,人工智能可以完成以前被认为不可能的壮举。这种模式识别和学习水平导致了自动驾驶汽车、像人类一样交谈的聊天机器人、语音转文本转换和翻译到另一种语言、自动批改论文、照片标题等技术的突破。随着生成式人工智能的出现,使用像 ChatGPT 这样的大型语言模型,我们可以根据用户提供的提示创建图像、视频和文本。而这只是开始!
1.4 机器学习的要点
考虑这样一个例子:如果一个孩子需要学习如何开门,我们会多次展示给他确切的步骤。孩子尝试开门但失败了。他们再次尝试,但再次失败。但随着每一次后续尝试,孩子都会改进他们的方法。经过一段时间,孩子就能打开门了。另一个例子是我们学习开车:我们犯错,从错误中学习,并改进。机器学习的工作方式与此类似,其中统计算法分析历史数据,寻找模式和洞察力。算法揭示关系和异常、趋势和偏差、相似性和差异性——然后与我们分享可操作的结果。
正式来说,机器学习(ML)可以称为计算机算法的一个分支或研究领域,它通过分析历史数据来生成洞察力,并帮助进行数据驱动的决策。这些算法基于统计学和数学基础,因此具有坚实的逻辑解释。机器学习算法需要编码,这可以在任何可用的语言和工具中进行,例如 Python、R、SPSS、SAS、MATLAB、Weka、Julia、Java 等。它还需要对业务领域的理解。
无论何时你在网上购物买衣服,网站推荐与之搭配的配饰,或者你预订机票,旅行运营商根据你的需求和计划展示定制套餐,大多数情况下,机器学习在幕后工作。它已经学会了你的偏好,并将它们与你的历史趋势进行比较。它还在寻找与其他行为几乎相同的客户之间的相似性。基于所有这些分析,算法为你做出智能推荐。是不是很神奇?
为什么机器学习在发现模式方面如此出色?我们人类只能同时分析两个或三个维度;例如,我们可以在两个或三个相互作用的变量之间发现模式。但如果所有 50 个不同的变量都在相互作用呢?我们就不会有机会。机器学习算法可以同时处理 50、60 或可能 100 多个维度。它可以处理任何类型的数据,无论是结构化还是非结构化,并且可以帮助自动化任务。因此,它生成的模式和洞察对于人类思维来说相当难以可视化。
机器学习(ML)就像任何其他项目一样,需要一个紧密合作并相互补充技能的专家团队。如图 1.8 所示,一个 ML 项目需要以下角色:
-
业务团队—业务利益相关者和领域专家为项目定义业务问题。他们拥有解决方案,对需求有清晰的理解,并有一个明确可衡量的目标。在出现困惑时,他们指导团队进行方向调整,并作为对业务流程和运营有深刻理解的专家。他们是市场营销经理、产品负责人、流程工程师、质量专家、风险分析师、投资组合负责人等。从第一天开始,业务利益相关者必须紧密融入团队。他们帮助调整整体方向。
-
运营团队—这个团队包括 Scrum 大师、项目经理、业务分析师等。团队的角色可以与典型的项目管理团队相比较,该团队跟踪进度、维护记录、报告日常活动,并确保整个项目按计划进行。他们创建用户故事,并在业务团队和数据团队之间充当桥梁。

图 1.8 数据科学项目所需的团队及其相互之间的互动——真正是一个团队的努力
-
数据团队—创建解决方案、进行编码并以模型、仪表板、报告和洞察的形式生成输出的核心团队是数据团队。它由三个主要支柱组成:数据工程团队、UI/可视化团队和数据科学团队。他们的职能如下:
-
数据工程团队负责构建、维护、集成和摄取所有数据点。他们进行定期数据刷新,并作为数据的主要保管人。他们使用 ETL、SQL、AWS、Kafka、PySpark 等。
-
UI/可视化团队负责构建仪表板、报告、交互模块和 Web 应用程序。他们使用 SQL、Tableau、Qlik、Power BI 和其他工具。
-
数据科学团队负责所有的数据分析建模任务。他们发现模式和见解,测试假设,并生成最终输出,最终将被所有人消费。最终输出可以是一个用于解决业务问题的 ML 模型。在 ML 模型不可能的情况下,团队可能会生成对业务有用的可操作见解。这个团队需要 SQL、Python、R、SAS、SPSS 等来完成他们的工作。
-
DevOps 团队通常是数据工程团队的一部分,或者它们可以作为一个独立的实体存在。他们专注于 ML 模型的运营化。记住:如果你的 ML 模型没有被使用,它只是放在架子上的一块闪亮的软件。UI/UX 团队将领导最终产品层的开发,其中基于 ML 的输出将呈现给最终用户。用户体验往往被忽视,没有互动和吸引人的用户体验,ML 将无法发挥其全部潜力。
-
团队有时还有一个测试团队来评估功能、各种用例以及应用程序的整体外观和感觉。
在讨论了数据科学项目的典型团队结构之后,我们现在将检查数据科学项目中涉及的广泛步骤。
数据科学项目就像任何有截止日期、阶段、测试、阶段等的项目一样运行。原材料是数据,它通过各个阶段,被清理、分析并建模。
图 1.9 展示了数据科学项目阶段的一个示意图。它从项目的业务问题定义开始。业务问题必须简洁、清晰、可衡量和可实现。表 1.2 描述了一个不良(定义不明确)和良好的业务问题的例子。

图 1.9 数据科学项目就像任何其他项目一样,有阶段和截止日期、依赖关系和流程。
表 1.2 如何定义业务问题以使其清晰、简洁和可衡量的例子
| 例子 |
|---|
| 定义不明确的业务问题 |
| --- |
| 提高产量 降低成本
| 优化各种成本头(A、B、C 和 D)并确定最优化组合,在接下来的六个月内降低 1.2%的成本 |
|---|
| 在一个月内提高 80%的收益 自动化整个流程
| 从流程中各种缺陷因素(X、Y、Z)中,确定最显著的因素,在接下来的三个月内降低 1.8%的缺陷率 |
|---|
然后我们进入数据发现阶段,在这个阶段,我们列出所有数据源并将它们托管。所有各种数据集,如客户详情、购买历史、社交媒体数据、投资组合等,都被识别和访问。在此步骤中,确定要使用的数据表,并且大多数情况下,我们会创建一个数据库供我们工作、测试和学习。
然后,我们继续进行数据预处理。这包括清除数据,如删除空值、异常值、重复值、垃圾值等。前一步和这一步可能占项目时间的 60%到 70%。
在探索性数据分析阶段,我们创建了一些报告并生成了初步见解。这些见解与业务利益相关者讨论,并指导课程修正。
数据现在已准备好进行建模。测试了相当多的解决方案版本。然后,根据需求,我们选择最佳版本。通常,准确度等参数和精确度、召回率等统计指标驱动着模型的选择。我们将在本书的后续章节中探讨选择最佳模型的过程以及精确度和召回率等术语。一旦我们选择了最终模型,我们就可以在生产环境中部署模型,它将在未见过的数据上工作。
这些是机器学习项目中的基本步骤。像任何其他项目一样,有一个代码库、最佳实践、编码标准、常见错误、陷阱等,我们将在本书中讨论。
1.5 机器学习算法类型
机器学习模型影响决策并采用统计方法来解决商业问题。它们在历史数据上工作,并从中发现模式和趋势。原材料是历史数据,这些数据被分析和建模以生成预测算法。可用的历史数据和需要解决的问题类型决定了应采用的机器学习方法。机器学习算法可以大致分为四类:监督学习、无监督学习、半监督学习和强化学习,如图 1.10 所示。我们将详细探讨这四种类型,重点是无监督学习——本书的主题。
你可能在新闻中听说过生成式人工智能(GenAI)。基于 GenAI 的解决方案通常从无监督学习开始,可能包括监督学习或强化学习以专门针对某些任务优化模型。我们将在本书中进一步讨论 GenAI。

图 1.10 机器学习算法可以分为监督学习算法、无监督学习算法、半监督学习算法和强化学习算法。
1.5.1 监督学习
正式来说,监督模型是使用输入数据和期望的输出数据来预测未来的统计模型。输出是我们希望预测的值,被称为目标变量,用于做出预测的数据被称为训练数据。目标变量有时也被称为标签。数据中存在的各种属性或变量被称为自变量。每个历史数据点或训练示例都包含这些自变量和相应的目标变量。监督学习算法对未见过的未来数据进行预测。解决方案的准确性取决于所进行的训练和从标记的历史数据中学到的模式。下一个部分将描述一个例子。
监督学习问题用于需求预测、信用卡欺诈检测、客户流失预测、保费估算等。它们在零售、电信、银行和金融、航空、保险等多个领域以及营销、客户关系管理(CRM)、质量、供应链、定价等功能中被广泛使用。
监督学习算法可以进一步分为回归算法和分类算法。让我们依次考虑这些。
回归算法
回归算法是监督学习算法——也就是说,它们需要需要预测的目标变量。这些算法用于预测连续变量的值。例如包括收入、降雨量、交易数量、生产产量等。在监督分类问题中,我们预测一个分类变量,比如是否会下雨(是/否),信用卡交易是欺诈还是真实,等等。这是分类和回归问题之间的主要区别。
让我们通过一个例子来理解回归问题。比如说,我们假设一个人的体重只依赖于身高,而不依赖于其他参数,如性别、种族、饮食等。在这种情况下,我们想要根据身高预测一个人的体重。数据集和为同一数据绘制的图表将类似于图 1.11。
一个回归模型能够找到数据中的内在模式,并拟合一个描述这种关系的数学方程。然后,它可以以身高作为输入来预测体重。在这里,身高是自变量,而体重是因变量或目标变量或我们想要预测的标签。

图 1.11 用于回归问题的身高与体重关系的数据和图表
对于回归问题,有相当多的算法可用。以下是一些主要的算法(尽管这个列表肯定不是详尽的):
-
线性回归
-
决策树
-
随机森林
-
k-最近邻算法
-
提升算法
-
神经网络
我们可以使用任何算法来解决这个问题。我们将通过使用线性回归来解决问题来进一步探索。
线性回归算法通过假设因变量和目标变量之间存在线性关系来建模因变量和目标变量之间的关系。线性回归算法将为问题产生一个数学方程,如图 1.1 所示:
(1.1)
重量 = β[0] * 高度 + β[1]
通常来说,线性回归用于拟合描述因变量和自变量之间关系的数学方程,如图 1.2 所示:
(1.2)
Y = β[0] + β[1] x[1] + β[2]x[2] + ….+ ε
在这里,Y是我们想要预测的目标变量;x[1]是第一个自变量;x[2]是第二个自变量;ε是方程中的误差项;β[0]是方程的截距。
线性回归问题的简单可视化如图 1.12 所示。这里我们有 x 和 Y 变量,其中 x 是自变量,Y 是目标变量。线性回归问题的目标是找到最佳拟合线,它可以解释数据中的随机性。

图 1.12 需要建模的原始数据(左侧)。使用回归,确定了最佳拟合线(右侧)。
方程式 1.2 用于对未见数据做出预测。在线性回归中也有多种变化,如简单线性回归、多元线性回归、非线性回归等。根据手头的数据,我们选择正确的算法。复杂的数据库可能需要各种变量之间的非线性关系。
我们将要探索的下一类回归算法是树形解决方案。对于决策树、随机森林等基于树的算法,算法将从顶部开始,然后像if/else块一样,迭代地分裂以创建节点和子节点,直到我们达到终端节点(见图 1.13)。在决策树图中,我们从顶部的根节点开始,然后执行分裂,直到达到终点,即终端节点。
决策树易于理解和实现,并且训练速度快。它们的可用性在于它们足够直观,无需太多技术背景即可理解。

图 1.13 决策树有一个根节点,在分裂后,我们得到一个决策节点和一个终端节点,这是最终的节点,不能再进一步分裂。
还有其他著名的回归算法,如 k 近邻、梯度提升和基于深度学习的解决方案。不同的回归算法最适合特定的环境。
为了理解回归用例的效果,让我们考虑一些在行业中实施的业务相关用例:
-
一个机场运营团队正在评估人员需求,并希望估计预期的客流量。这个估计将帮助团队制定关于未来资源需求的计划,并有助于优化所需资源。回归算法可以帮助预测乘客数量。
-
一家零售商希望了解即将到来的销售季节的预期需求,以便规划库存。这将导致成本节约并避免缺货。回归算法可以帮助进行此类规划。
-
一家制造厂希望提高现有各种模具和原材料的使用效率。回归解决方案可以建议最佳的模具组合并预测预期的产量。
-
一家银行向其客户提供信用卡。考虑一下为新客户提供信用额度的计算方式。基于客户的属性,如年龄、职业、收入和之前的交易历史,回归算法可以帮助在客户层面建议信用额度。
-
一家保险公司希望根据历史索赔数据为其客户制定一个保费表。风险可以根据驾驶员详情、汽车信息等历史数据来评估。回归分析当然可以帮助解决这类问题。
回归问题构成了监督学习问题的基础,并在行业中得到了广泛的应用。与分类算法一起,它们成为现实世界中大多数预测问题的首选解决方案。
分类算法
分类算法用于预测分类变量的值,该变量是因变量。这个目标变量可以是二元的(如是/否、好/坏、欺诈/真实、通过/失败等)或者多类的(例如正面/负面/中性或是/否/不知道)。分类算法将通过为目标变量生成一个概率分数来确定目标事件是否会发生。
在模型在历史数据上训练完毕后,分类算法将为未见数据集生成一个概率分数,这可以用于做出最终决策。根据目标变量中存在的类别数量,我们的业务决策将有所不同。让我们看看分类问题的用例。
考虑这种情况:一家电信运营商正面临其客户基础减少的问题。现有客户数量正在减少,电信运营商希望阻止这种客户流失。为此,设想了一个机器学习模型。
在这种情况下,用于模型构建的历史数据或训练数据可能看起来像表 1.3。这些数据点仅用于说明目的,并不全面。可能还有许多其他重要的变量可用。
表 1.3 电信运营商的示例结构化数据集,显示多个数据属性
| ID | 收入($) | 服务时长(年) | 平均成本 | 月使用天数 | 流失(Y/N) |
|---|---|---|---|---|---|
| 1001 | 100 | 1.1 | 0.10 | 10 | Y |
| 1002 | 200 | 4.1 | 0.09 | 25 | N |
| 1003 | 300 | 5.2 | 0.05 | 28 | N |
| 1004 | 200 | 0.9 | 0.25 | 11 | Y |
| 1005 | 100 | 0.5 | 0.45 | 12 | Y |
在表 1.3 的例子中,数据集包括订阅者的过去使用数据。最后一列(流失)描述了该订阅者是否从系统中流失。例如,订阅者 1001 流失了,而 1002 没有。因此,业务问题是基于这些历史数据构建机器学习模型,并预测新未见客户是否会流失。
在这里,流失状态(是/否)是目标变量。它也被称为因变量。其他属性,如收入、服务时长、平均成本、月使用量等,是独立变量,用于创建机器学习模型。历史数据被称为训练数据。在模型训练之前,训练好的监督学习模型将为新客户生成预测概率。
对于分类问题,有相当多的算法可用;主要的有以下几种:
-
逻辑回归
-
决策树
-
随机森林
-
k-最近邻
-
Naïve Bayes
-
支持向量机
-
提升算法
-
神经网络
最受欢迎的分类算法之一是逻辑回归。逻辑回归使用对数函数来建模分类问题。如果我们解决的是二元分类问题,它将是二元逻辑回归或多元逻辑回归。与线性回归类似,逻辑回归也拟合一个方程,尽管它使用 Sigmoid 函数来生成事件发生的概率分数。
Sigmoid 函数是一种具有特征 S 形曲线或 Sigmoid 曲线的数学函数。Sigmoid 函数的数学方程式在方程式 3.1 中显示:
(1.3)
S(x) = 1/(1 + e–(x))
这可以重写为方程式 1.4
(1.4)
S(x) = e(*x*)/(*e*(x) + 1)
逻辑回归使用 Sigmoid 函数。逻辑回归问题中使用的方程式在方程式 1.5 中显示:
(1.5)
log (p/1 – p) = β[0] + β[1] x[1]
其中 p 是事件发生的概率;β[0] 是截距项;β[1] 是独立变量 x[1] 的系数;log(p/1 – p) 被称为 logit;(p/1 – p) 是赔率。如图 1.14 所示,如果我们尝试为概率函数拟合线性回归方程,它将做得不好。我们希望获得概率分数(即介于 0 和 1 之间的值)。线性回归不仅会返回介于 0 和 1 之间的值,还会返回大于 1 或小于 0 的概率分数。因此,我们在图中右侧有一个 Sigmoid 函数,它只为我们生成介于 0 和 1 之间的概率分数。

图 1.14 线性回归模型无法做到公正(左侧);因此,我们有了逻辑回归来进行分类。线性回归也可以生成大于 1 或小于 0 的概率分数,这在数学上是错误的,而 sigmoid 函数只生成介于 0 和 1 之间的概率分数(右侧)。
逻辑回归算法是分类问题中最广泛使用的技巧之一。它易于训练和部署,并且通常是我们开始任何监督分类学习项目时的基准算法。
基于树的算法,如决策树和随机森林,也可以用于分类问题。其他算法也根据需求使用。
1.5.2 无监督算法
想象一下,你被给了一些纸标签,如图 1.15 所示。任务是按相似性排列它们。现在,有多个方法可以解决这个问题。你可以使用颜色、形状或大小。在这里,我们没有任何标签来指导这种排列。这就是无监督算法与众不同的地方。

图 1.15 使用不同参数可以一起分组的各种形状示例
形式上讲,无监督学习只接受输入数据,然后在其中发现模式,而不参考目标变量。因此,无监督学习算法根据数据集中模式的存在与否做出反应。
因此,无监督学习用于模式检测、探索数据集中的洞察力以及理解其结构、细分和异常检测。
我们可以通过查看图 1.16 来理解无监督学习算法。左侧的图显示了以向量空间图表示的原始数据点。右侧是聚类,将使用无监督学习算法来完成。

图 1.16 无监督学习算法在左侧数据中找到模式,并在右侧产生集群。
无监督算法的一些用例如下:
-
一家零售集团希望更好地了解其客户。任务是提高客户的粘性、收入、访问次数、购物篮大小等。在这里可以使用无监督学习进行客户细分。根据客户的属性,如收入、访问次数、最后访问日期、加入以来的年龄、人口统计属性等,细分将产生可以个性化定位的集群。结果将是改善客户体验、增加客户终身价值等。
-
一个网络提供商需要创建一个异常检测系统。历史数据将作为异常数据。无监督学习算法将能够找到模式,算法将给出异常值。需要解决的显著异常将是那些。
-
一家医疗产品公司希望在其患者的图像数据中找到任何潜在的规律。如果有任何模式和因素,这些患者可以得到更好的治疗,也许他们需要不同的方法。无监督学习可以帮助图像数据,这将有助于更好地满足患者的需求。
-
一家数字营销公司希望了解 incoming 客户数据中的“未知”信息,如社交媒体互动、页面点击、评论、星级等。这种理解将有助于改善客户推荐和整体购买体验。
无监督学习算法在寻找模式时提供了灵活性和性能。它们适用于所有类型的数据——本书的核心主题,包括结构化数据、文本或图像。
主要的无监督学习算法包括
-
聚类算法
-
k-means 聚类
-
层次聚类
-
DBSCAN 聚类
-
谱聚类
-
主成分分析
-
单值分解
-
关联规则
-
t-distributed 随机邻域嵌入
-
自动编码器
我们将在接下来的章节中详细介绍所有这些算法。我们将检查数学概念、隐藏过程、Python 实现以及整本书中的最佳实践。让我们首先通过案例研究来了解基本过程。
一家零售商希望对其消费者基础有更深入的了解,然后希望提供个性化的推荐、促销、折扣、优惠等。整个客户数据集应使用如人格、以往购买、响应、外部数据等属性进行细分(见图 1.17)。

图 1.17 无监督学习算法中的步骤,从数据源到最终部署的解决方案
对于用例,无监督学习项目的步骤如下:
-
我们通过定义业务问题开始项目。我们希望更好地了解客户基础。客户细分方法可以是一个好的解决方案。我们希望使用数学 KPI 区分的细分。
-
这是数据发现阶段。所有各种数据集,如客户详情、购买历史、社交媒体数据、投资组合等,都被识别和访问。在此步骤中确定要使用的数据表。然后,通常将所有数据表加载到公共数据库中,我们将使用它来分析、测试和学习。
-
现在我们已经可以访问数据了。下一步是清理它并使其可用。我们处理所有空值、NaN、垃圾值、重复值等。
-
一旦数据清理完毕并准备好使用,我们就对其进行探索性数据分析。通常,在探索性分析期间,我们识别模式、周期性、异常值、最大最小范围、标准差等。探索性数据分析阶段的输出将是洞察和理解。我们还将生成一些图表,如图 1.18 所示。
-
我们现在开始使用无监督方法。我们想要实现聚类方法,因此我们可以尝试一些聚类方法,如 k-means、层次聚类等。聚类算法将根据客户的各种属性产生同质化的客户群体。

图 1.18 数据探索性分析中的图表示例
在案例研究中,我们将处理过去两到三年的数据,这些数据是训练数据。由于我们使用的是无监督方法,这里没有目标变量。算法将使用客户的交易模式、人口统计模式和购买偏好来合并表现相似的客户群体。结果将类似于图 1.19 所示。

图 1.19 聚类算法的输出,我们可以使用各种属性来细分客户
-
6.我们现在检查各种算法的表现;换句话说,我们将比较每个算法的准确性。最终选择的聚类算法将导致客户群体同质化,这些群体可以被定位并提供定制化优惠。
-
7.我们与业务利益相关者讨论结果,并根据反馈进行迭代。
-
8.我们将解决方案部署到生产环境中,并准备好处理新的未见数据集。
这些是在无监督问题中的基本步骤。算法创建和选择是繁琐的任务。我们将在本书的后面详细研究这些内容。
GenAI 通常从无监督阶段开始。这个阶段使模型能够在没有明确标签的情况下学习模式、结构和关系。有时也被称为预训练阶段。一旦模型完成预训练,我们就进入监督阶段。在这里,预训练的模型使用标记数据集针对特定任务或领域进行调整。
1.5.3 半监督算法
半监督学习是监督和无监督方法之间的中间道路。采用半监督方法的主要原因是缺乏完整的标记数据集用于训练。正式地说,半监督方法结合了监督和无监督方法:监督用于分类数据点,无监督用于将它们分组在一起。
在半监督学习中,我们最初使用监督算法在少量标记数据点上训练。然后我们使用它来标记或伪标记新的数据点。这两个数据集(标记和伪标记)被合并,我们使用这个数据集进行进一步分析。
半监督算法在数据集部分可用的情况下使用,例如医疗行业中的图像。如果我们通过分析患者的图像来创建癌症检测解决方案,我们可能不会有足够的训练图像样本集。在这种情况下,半监督方法可能是有帮助的。
1.5.4 强化学习
想象你正在和一台电脑下棋,过程是这样的:
-
第一轮—经过 5 步后你获胜。
-
第二轮—经过 8 步后你获胜。
-
第三轮—经过 14 步后你获胜。
-
第四轮—经过 21 步后你获胜。
-
第五轮—电脑获胜!
发生在这里的是,算法根据每次交互迭代地训练自己,然后进行纠正/改进。
形式上,强化学习解决方案是自我维持的解决方案,它使用一系列的尝试和错误来训练自己。一个序列跟随另一个。强化学习的核心是奖励信号。如果动作是积极的,奖励是积极的,表示继续。如果动作是消极的,奖励将惩罚活动。因此,解决方案将始终纠正自己并向前推进,从而迭代地改进自己。
自动驾驶汽车是强化学习算法的最佳例子。它们能够检测何时应该左转或右转,何时移动,何时停车。现代视频游戏也采用了强化学习算法。强化学习使我们能够突破技术的障碍,想象之前被认为不可能的事情。
通过这样,我们已经涵盖了不同类型的机器学习算法。它们共同利用数据的真正力量,并对我们的生活产生持久的影响。但解决方案的核心是技术,我们还没有讨论。我们现在转向制作这些解决方案所需的技术堆栈。
练习 1.1
使用这些问题来检查你的理解:
-
为什么机器学习(ML)如此强大,以至于现在被广泛使用?
-
不同的机器学习算法有哪些,它们之间有何不同?
-
机器学习项目中包含哪些步骤?
-
数据工程的作用是什么,为什么它很重要?
-
可用于机器学习的各种工具有哪些?
1.6 总结性思考
一个常见的问题是:R 语言和 Python 哪个更好?两者都是优秀的语言。两者都被广泛使用。但是随着 TensorFlow 的引入,Keras 在人工智能领域的库使得平衡略微偏向 Python。
你现在已经在学习无监督机器学习技术的旅途中迈出了第一步。现在是时候总结了。
机器学习和人工智能确实是开创性的。它们正在改变我们的旅行方式、订餐、规划、购物、看医生、订购处方——它们无处不在。机器学习确实是一种强大的能力,正在为未来铺平道路,并且在模式识别、异常检测、定制和任务自动化方面证明比现有的技术堆栈要好得多。自动驾驶、癌症检测、欺诈识别、面部识别、图像标题和聊天机器人只是机器学习和人工智能优于传统技术的几个例子。现在正是进入这个领域的最佳时机。该领域几乎吸引了所有商业功能的投资。该领域在整个范围内创造了数千个就业机会。
同时,该领域缺乏受过培训的专业人士:数据分析师、数据工程师、可视化专家、数据科学家和数据从业者。他们现在都是稀有品种。该领域需要定期供应新的人才,他们将成为未来的领导者,并将做出数据驱动的决策。我们对数据力量的理解还只是触及表面——还有很长的路要走。
在下一章中,我们将更深入地探讨聚类的不监督学习概念。我们将讨论数学和统计基础、实用的案例研究和 Python 实现。讨论包括简单的聚类算法:k 均值聚类、层次聚类和 DBSCAN。在本书的后续章节中,我们将研究更复杂的聚类主题,如高斯混合模型聚类、时间序列聚类、模糊聚类等。
摘要
-
数据可以被视为一个相互关联的事实和统计数据集合,用于分析,具有独特的特征,并受特定管理原则的约束。
-
现实世界的活动,如手机通话、在线交易和社交媒体互动,持续产生数据,凸显了其在现代生活中的无处不在。
-
原始数据需要清洗、组织和分析,才能有效地转化为可以驱动商业决策和行动的信息和洞察。
-
数据可以广泛地分为结构化数据集,它们遵循清晰的行列格式,以及非结构化数据集,如文本和图像,它们需要更高级的分析技术。
-
为了分析非结构化数据,我们通常将其转换为数值表示,通常利用深度学习模型,如卷积神经网络(CNNs)和循环神经网络(RNNs)。
-
一个清晰、简洁、可实现且可衡量的商业问题是确保数据科学项目成功的关键步骤。
-
高质量的数据对于可靠的分析至关重要,其特征包括完整性、有效性、准确性、代表性、可用性、一致性、及时性和完整性。
-
有效的数据工程和管理对于准备涉及 ETL 流程和数据清洗等技术分析的数据至关重要。
-
UI/UX 的作用对于确保最终消费者采用和使用至关重要;否则,机器学习将只是放在架子上的一件闪亮的东西。
-
数据分析、机器学习、人工智能和商业智能等相互关联的领域在处理和从数据中提取洞察力方面各自发挥着关键作用。
-
监督学习是一种机器学习方法,它使用现有数据来预测未来的结果,在需求预测和欺诈检测等任务中很常见。
-
监督学习分为回归和分类任务,每个任务都有许多可用的算法来分别建模定量或分类结果。
-
无监督学习算法独立于预定义的目标变量发现数据中的模式和关系,在细分和异常检测等活动中有用。
-
无监督学习算法的变体包括聚类技术和降低数据维度的方法,在模式识别中提供灵活性和性能。
-
半监督学习连接了监督学习和无监督学习方法,在处理部分标记的数据集时非常有效。
-
强化学习涉及通过试错来学习的系统,奖励期望的结果,并应用于动态决策任务,如自动驾驶车辆导航。
-
技术解决方案是现代数据驱动策略的核心,理解技术堆栈对于最大化数据解决方案的效果和好处至关重要。
第二章:聚类技术
本章涵盖
-
聚类技术及其在行业中的显著应用案例
-
简单的 k-means、层次和基于密度的空间聚类算法
-
Python 中算法的实现
-
关于聚类分析的案例研究
简单即复杂。——达芬奇
自然喜欢简单,并教导我们遵循相同的道路。大多数时候,我们的决策都是简单的选择。简单的解决方案更容易理解,耗时更少,维护和思考起来也更轻松。机器学习世界也不例外。优雅的机器学习解决方案不是最复杂的算法,而是解决业务问题的解决方案。稳健的机器学习解决方案足够简单,可以轻松解码,并且足够实用,可以实施。聚类解决方案通常更容易理解。
在上一章中,我们定义了无监督学习并讨论了可用的各种无监督算法。随着我们阅读本书,我们将涵盖这些算法中的每一个;在这一章中,我们专注于这些中的第一个:聚类算法。
我们首先定义聚类,然后研究不同的聚类技术类型。我们将检查每个算法的数学基础、准确度测量以及优缺点。我们将使用 Python 代码在数据集上实现这三种算法,以补充理论知识。本章以聚类技术在实际业务场景中的各种应用案例结束,为实际商业世界做准备。本书中始终遵循这一技术——我们首先研究概念,然后实现实际代码以增强 Python 技能,最后深入现实世界的商业问题。
本章我们研究基本的聚类算法,包括 k-means 聚类、层次聚类和基于密度的空间聚类应用噪声(DBSCAN)聚类。这些聚类算法通常是我们想要研究聚类时的起点。在本书的后续章节中,我们将探索更复杂的算法,如谱聚类、高斯混合模型、时间序列聚类、模糊聚类等。如果您对 k-means 聚类、层次聚类和 DBSCAN 有很好的理解,您可以跳到下一章。然而,建议您至少阅读这一章——您可能会发现一些有用的内容来刷新您的概念!
让我们先了解我们所说的聚类是什么。祝您在掌握基于无监督学习的聚类技术之路上好运!
2.1 技术工具箱
在本章中,我们使用 Python 的最新版本。预期您对 Python 和代码执行有基本了解。建议您复习面向对象编程和 Python 的知识。
在整本书中,我们使用 Jupyter Notebook 来执行代码。Jupyter 在执行和调试方面提供了灵活性。它非常用户友好,并且与平台或操作系统无关。因此,如果你使用 Windows、macOS 或 Linux,Jupyter 应该可以正常工作。
所有数据集和代码文件都已提交到 GitHub 仓库mng.bz/lYq2。你需要安装以下 Python 库来执行代码:numpy、pandas、matplotlib、scipy和sklearn。CPU 足以执行,但如果你遇到一些计算延迟并且想要加快执行速度,切换到 GPU 或 Google Collaboratory(Colab)。Google Colab 为机器学习解决方案提供免费计算。我建议更多地了解 Google Colab 以及如何用它来训练机器学习算法。
2.2 聚类
考虑以下场景:一群孩子被要求将房间里的物品分组到不同的段。每个孩子可以使用自己的逻辑。有些人可能会根据重量分组物体;其他孩子可能会使用材料或颜色;而还有一些孩子可能会使用所有三个:重量、材料和颜色。有无数种排列组合,并且它们取决于分组所使用的参数。在这里,一个孩子是根据选择的逻辑对物体进行分段或聚类的。
正式来说,聚类是用来将具有相似属性的物体分组到同一段,而将具有不同属性的物体分组到不同段。结果簇在自身内部具有相似性,而在彼此之间则更加异质。我们可以通过查看图 2.1 来更好地理解这一点。

图 2.1 聚类是将具有相似属性的物体分组到逻辑段。分组基于不同观察结果共享的相似特征,因此它们被聚集到一组。在这里,我们使用形状作为聚类的变量。
聚类分析不是一个单独的算法或解决方案;相反,它在实际商业场景中用作解决问题的机制。它是无监督学习下的一类算法,是一个遵循逻辑方法和定性商业输入的迭代过程。它导致对数据及其逻辑模式的深入理解,模式发现和信息检索。作为一种无监督方法,聚类不需要目标变量。它通过分析数据集中的潜在模式进行分段,这些模式通常是多维的,因此难以用传统方法分析。
理想情况下,我们希望聚类算法具有以下属性:
-
输出的聚类应该易于解释和理解,易于使用,并且具有商业意义。聚类数量不应过多或过少。例如,只有两个聚类并不理想,且划分不清晰和决断。另一方面,如果我们有 20 个聚类,处理起来将变得具有挑战性。
-
算法不应过于敏感于异常值、缺失值或数据集中的噪声。一般来说,一个好的解决方案将能够处理多种数据类型。
-
对于数据分析师/科学家来说,掌握业务领域是明智的,尽管一个好的聚类解决方案可能允许对领域理解较少的分析员训练聚类算法。
-
算法应独立于输入参数的顺序。如果顺序很重要,聚类将受到顺序的偏差,从而给过程增加更多混乱。
-
随着我们持续生成新的数据集,聚类应该能够扩展到新的训练示例,并且不应是耗时的过程。
如同想象中的那样,聚类输出将取决于用于分组的属性。在图 2.2 中,对于同一数据集,可能存在两种逻辑分组,且两者都是有效的。因此,明智的做法是谨慎选择用于聚类的属性或变量,通常这个决定取决于手头的业务问题。

图 2.2 使用不同的属性进行聚类会导致同一数据集产生不同的聚类结果。因此,选择正确的属性集定义了我们最终将获得的结果集。
除了用于聚类的属性外,实际使用的技术也会产生很大的影响。实际上,有相当多的(实际上超过 80 种)聚类技术。对于感兴趣的读者,我们在附录中提供了所有聚类算法的列表。
聚类可以通过多种算法实现。这些算法使用不同的方法来定义对象之间的相似性——例如,基于密度的聚类、基于质心的聚类、基于分布的方法等。有多种技术,如欧几里得距离、曼哈顿距离等,可用于测量对象之间的距离。距离测量的选择会导致不同的相似度得分。我们将在后面的章节中研究这些相似度测量参数。
从高层次来看,我们可以识别两种广泛的聚类方法:硬聚类和软聚类(见图 2.3)。当决策非常明确,一个对象属于某个类别或聚类时,它被称为硬聚类。在硬聚类中,算法对对象的类别非常确定。另一方面,软聚类为对象属于特定聚类分配一个可能性分数。因此,软聚类方法不会将对象放入一个聚类中;相反,一个对象可以属于多个聚类。软聚类有时也被称为模糊聚类。

图 2.3 硬聚类有明显的聚类,而在软聚类的案例中,一个数据点可以属于多个聚类,我们得到一个数据点属于聚类的可能性分数。左边的图是硬聚类,右边的图是软聚类。
我们可以将聚类技术大致分类,如表 2.1 所示。描述的方法并非唯一。我们可以有基于图模型、重叠聚类、子空间模型等。
表 2.1 聚类方法分类、简要描述和示例
| 序号 | 聚类方法 | 方法的简要描述 | 示例 |
|---|---|---|---|
| 1 | 基于质心的聚类 | 与定义的质心的距离 | k-means |
| 2 | 基于密度的模型 | 数据点在向量空间中的密集区域连接 | DBSCAN, OPTICS |
| 3 | 基于连接性的聚类 | 连接距离是操作方式 | 层次聚类、平衡迭代减少和基于层次结构的聚类 |
| 4 | 分布模型 | 建模基于统计分布 | 高斯混合模型 |
| 5 | 深度学习模型 | 基于无监督神经网络的 | 自组织映射 |
通常,在工业界中用于聚类的最流行的六个算法如下:
-
k-means 聚类(包括 k-medians、k-medoids 等变体)
-
聚类层次聚类或层次聚类
-
DBSCAN
-
谱聚类
-
高斯混合模型
-
平衡迭代减少和基于层次结构的聚类
还有许多其他算法可用,如中国 whispers、 canopy 聚类、SUBCLU、FLAME 等。我们将在本章中研究前三种算法,并在本书的后续章节中研究一些高级算法。
练习 2.1
使用以下问题来检查你的理解:
-
DBSCAN 聚类是一种基于质心的聚类技术。对还是错?
-
聚类是一种具有固定目标变量的监督学习技术。对还是错?
-
硬聚类和软聚类之间有什么区别?
2.3 基于质心的聚类
基于质心的算法根据对象到簇质心的距离来衡量它们的相似度(有关质心的更多信息,请参阅附录)。距离是测量特定数据点到簇质心的距离。距离越小,相似度越高。我们可以通过查看图 2.4 来理解这个概念。右侧的图表示每个簇组的相应质心。
TIP 为了更清楚地了解质心和其他数学概念,请参阅附录。

图 2.4 基于质心的聚类方法为各自的簇创建一个质心,相似度是根据质心的距离来衡量的。在这种情况下,我们有五个质心,因此我们有五个不同的簇。
在聚类中,距离起着核心作用,因为许多算法将其用作度量来衡量相似度。在基于质心的聚类中,距离是在点与点之间以及点与质心之间测量的。有多种方法可以测量距离。最常用的如下:
- 欧几里得距离——这是最常用的距离度量。它表示空间中两点之间的直线距离,是两点之间的最短路径。例如,如果我们想计算点 P[1] 和 P[2] 之间的距离,其中 P[1] 的坐标是 (x[1], y[1]),P[2] 的坐标是 (x[2], y[2]),欧几里得距离由方程 2.1 给出。几何表示如图 2.5 所示:
(2.1)
距离 = √(y[2] – y[1])[²] + (x[2] – x[1])[²]
- 切比雪夫距离——以俄罗斯数学家帕夫努提·切比雪夫的名字命名,定义为两点之间的距离,其差异是任何坐标维度的最大值。数学上,我们可以用方程 2.2 表示切比雪夫距离,如图 2.5 所示:
(2.2)
切比雪夫距离 = max(|y[2] – y[1]|, |x[2] – x[1]|)

图 2.5 欧几里得距离、切比雪夫距离、曼哈顿距离和余弦距离是主要的距离度量。注意使用这些度量时两点之间的距离如何不同。在欧几里得距离中,两点之间的直接距离是测量的,如左图的第一幅图所示。
- 曼哈顿距离——这是一个非常容易理解的概念。它简单地计算两个点在网格状路径上的距离,因此距离是沿着垂直于轴线的方向测量的。因此,有时它也被称为街区距离或出租车度量。数学上,我们可以用方程 2.3 表示曼哈顿距离,如图 2.5 所示:
(2.3)
曼哈顿距离 = (|y[2] – y[1]| + |x[2] – x[1]|)
曼哈顿距离是 L1 范数形式,而欧几里得距离是 L2 范数形式。请参阅附录以详细了解 L1 范数和 L2 范数。如果我们有大量维度或变量在数据集中,曼哈顿距离比欧几里得距离更好。这是由于“维度诅咒”,我们将在第三章中研究。
- 余弦距离——余弦距离用于测量向量空间图中两点之间的相似度。在三角学中,0 度的余弦值为 1,90 度的余弦值为 0。因此,如果两点彼此相似,它们之间的角度将是零;因此,余弦值为 1,这意味着两点非常相似,反之亦然。数学上,余弦相似度在方程 2.4 中显示。如果我们想测量向量A和B之间的余弦值,那么余弦值是
(2.4)
余弦距离 = (A . B) / (||A|| ||B||)
小贴士:如果您想刷新关于向量分解概念的知识,请参阅附录。
其他距离度量标准,如汉明距离、杰卡德距离等,也是可用的。在大多数情况下,我们在实际业务问题中使用欧几里得距离,但有时也会使用其他距离度量标准。
注意:这些距离度量标准也适用于其他聚类算法。我建议您使用书中的 Python 代码测试不同的距离度量标准,并比较性能。
现在我们已经了解了各种距离度量标准,我们继续研究 k-means 聚类,这是最广泛使用的算法。
2.3.1 K-means 聚类
K-means 聚类是一种简单直接的方法。它可以说是最广泛使用的聚类方法,用于分割数据点并创建非重叠的聚类。我们必须指定我们希望创建的聚类数量k作为输入,算法将把每个观测值与 k 个聚类中的确切一个关联起来。
注意:K-means 聚类有时会与 k 近邻(KNN)分类器混淆。尽管两者之间有一些关系,但 KNN 用于分类和回归问题。
K-means 聚类是一种相当优雅的方法,它从一些初始聚类中心开始,然后迭代地将每个观测值分配到最近的中心。在这个过程中,中心点被重新计算为聚类中点的平均值。让我们通过使用图 2.6 中的图表以逐步的方式研究这种方法。为了简化,我们假设数据集中有三个聚类。
步骤如下:
-
假设我们已经拥有了所有数据点,如图 1 所示。
-
三个中心点被随机初始化,如图中三个正方形所示:蓝色、红色和绿色。这三个输入值是我们希望拥有的最终聚类数量。

图 2.6 步骤 1 表示原始数据集。在步骤 2 中,算法根据我们输入的聚类数量为三个,初始化了三个随机质心。在步骤 3 中,所有质心的邻近点被分配到相同的簇。
-
- 所有数据点到中心的距离被计算,并将点分配到最近的中心。请注意,这些点已经根据它们各自最近的中心获得了蓝色、红色和绿色,因为它们离这些中心最近。(在打印版本中颜色不可区分;因此我们将它们分组在一起。)
-
- 在这个步骤中,三个中心被重新调整。中心被重新计算为该簇中点的平均值,如图 2.7 所示。我们可以看到,在步骤 4 中,与步骤 3 相比,三个正方形已经改变了它们的位置。

图 2.7 在步骤 4 中重新计算了质心。在步骤 5 中,数据点再次被分配到新的中心。在步骤 6 中,根据新的计算再次调整了质心。
-
- 所有数据点到新中心的距离被重新计算,并且点再次被分配到最近的中心。请注意,在这个步骤中,两个蓝色数据点变成了红色,而一个红色点变成了绿色。
-
- 中心再次调整,就像步骤 4 中一样。
-
- 数据点再次被分配到一个新的簇,如图 2.8 所示。
-
- 过程将继续,直到收敛。换句话说,过程将继续,直到没有更多的数据点重新分配;因此,我们不能再进一步改善聚类,最终聚类完成。

图 2.8 质心被重新计算,这个过程会一直持续到我们不能再改善聚类效果。然后,过程停止,如图 8 所示。
k-means 聚类的目标是确保簇内变异尽可能小,而簇间的差异尽可能大。换句话说,同一簇的成员之间最为相似,而不同簇的成员之间则不相似。一旦结果不再变化,我们可以得出结论,已经达到了局部最优,聚类可以停止。因此,最终的簇在自身内部是同质的,而与其他簇是异质的。
这里有两个要点至关重要:
-
K-means 聚类随机初始化中心;因此,它找到一个局部最优解而不是全局最优解。因此,建议多次迭代解决方案,并从所有结果中选择最佳输出。通过迭代,我们指的是重复这个过程多次,因为在每次迭代中,随机选择的质心都会不同。
-
我们必须输入我们希望拥有的最终聚类数k,这将极大地改变输出结果。与数据量相比,k的值非常小会导致冗余聚类,因为它们将没有任何用处。换句话说,如果我们相对于大量数据有非常小的k值,具有不同特征的数据点将被组合在几个组中。k的值非常高将创建彼此之间细微差异的聚类。
此外,拥有一个非常高的聚类数在长期内将难以管理和更新。让我们研究一个例子。如果一个电信运营商有 100 万用户,如果我们把聚类数定为两个或三个,结果聚类的大小将会非常大。这也可能导致同一细分市场中的不同客户被分类。另一方面,如果我们把聚类数定为 50 或 60,由于聚类数量庞大,输出结果将难以使用、分析和维护。
随着不同的k值,我们得到不同的结果;因此,了解我们如何为数据集选择最佳聚类数是必要的。现在让我们来检查衡量聚类解决方案准确性的过程。
2.3.2 聚类准确性的度量
聚类的一个目标是要找到最干净的聚类。理论上(尽管不是理想情况),如果我们有与观察数相同的聚类数,结果将完全准确。换句话说,如果我们有 100 万客户,最纯的聚类将会有 100 万个聚类,其中每个客户都在一个单独的聚类中。但这不是最佳方法,也不是一个实际的解决方案。聚类旨在在一个聚类中创建一组相似的观察值,我们使用相同的原理来衡量我们解决方案的准确性。其他选项包括以下内容:
- 聚类内平方和(WCSS)或凝聚力——这个指标衡量数据点相对于它们与聚类质心的距离的变异性。这个度量是每个数据点到聚类质心的平均距离,对每个数据点重复进行。如果值太大,表明数据分布范围很大,而较小的值则表明数据点相当相似和同质,因此聚类是紧凑的。
有时,这种聚类内的距离也被称为该聚类的惯性。它只是所有距离的总和。惯性值越低,聚类越好。
- 簇间平方和——这个指标用于衡量所有簇的质心之间的距离。为了得到它,我们测量所有簇的质心之间的距离,并将其除以簇的数量以得到平均值。它的值越大,表示聚类越好,表明簇是异质且彼此可区分的,如图 2.9 所示。

图 2.9 簇内距离与簇间距离。两者都用于衡量最终簇的纯度和聚类解决方案的性能。
- 轮廓值——这是衡量聚类成功的一个指标。它介于-1 到+1 之间,值越高越好。它衡量一个数据点与其簇内其他数据点的相似性,与簇外其他簇相比。作为第一步,对于每个观测值,我们计算其与同一簇中所有数据点的平均距离;让我们称它为x[i]。然后我们计算其与最近簇中所有数据点的平均距离;让我们称它为y[i]。然后我们将通过方程 2.5 计算系数:
(2.5)
轮廓系数 = (y[i] – x[i])/ max (y[i], x[i])
如果系数的值为-1,则表示观测值位于错误的簇中。如果值为 0,则表示观测值非常接近邻近簇。如果系数的值为+1,则表示观测值与邻近簇的距离较远。因此,我们期望系数的值越高,表示聚类解决方案越好。
- Dunn 指数——这也可以用来衡量聚类的有效性。它使用之前定义的簇间和簇内距离测量值,并由方程 2.6 给出:
(2.6)
Dunn 指数 = 最小(簇间距离)/最大(簇内距离)
显然,我们会努力最大化 Dunn 指数的值。为了实现这一点,分子应该尽可能大,这意味着簇彼此之间距离较远,而分母应该尽可能低,这表示簇非常稳健且紧密排列。
2.3.3 寻找 k 的最佳值
选择最佳簇数并不容易。正如我之前所说的,最佳的聚类是簇数等于观测数的情况,但正如我们在上一节所研究的,这在实际中是不可能的。因此,我们应该将簇数k作为算法的输入。
寻找最优值 k 最广泛使用的方法可能是 肘部方法。在此方法中,我们计算不同 k 值的簇内平方和或 WCSS。过程与上一节讨论的相同。然后,将 WCSS 绘制在图表中,与不同的 k 值对应。无论何时观察到如图 2.10 所示的拐点或肘部,我们就能找到数据集的最佳簇数。注意尖锐的边缘。

图 2.10:寻找最佳簇数的肘部方法。圆圈显示了拐点。然而,最终的簇数取决于业务逻辑,我们通常根据这个逻辑合并/拆分簇。簇的维护难度也起着至关重要的作用。
练习 2.2
回答以下问题以检查你的理解:
-
k-means 聚类不需要输入簇数。对或错?
-
KNN 和 k-means 聚类是同一件事。对或错?
-
描述一种寻找最优值 k 的可能过程。
但这并不意味着这是我们为业务问题建议的最终簇数。基于每个簇中观察到的数量,一些簇可能需要合并或拆分成子簇。我们还考虑了创建簇所需的计算成本。簇的数量越多,计算成本和时间需求就越大。我们还可以使用之前讨论过的轮廓系数来找到最佳的簇数。
注意:探索合并几个簇或拆分几个簇的业务逻辑是至关重要的。最终,解决方案必须在实际业务场景中实施。
通过这种方式,我们已经探讨了 k-means 聚类的核心——数学概念和过程、各种距离度量以及确定最佳值 k。
2.3.4 k-means 聚类的优缺点
k-means 算法是一个非常流行且广泛实施的聚类解决方案。该解决方案提供了以下优点:
-
与其他算法相比,它易于理解且相对容易实现。距离测量计算使得即使是非统计学背景的用户也能很容易地理解。
-
如果维度数量很大,k-means 算法比其他聚类算法更快,并且创建的簇更紧密。因此,当维度数量相当高时,它更受欢迎。
-
它能迅速适应新的观察结果,并且能够很好地推广到各种形状和大小的簇。
-
该解决方案通过一系列迭代重新计算来产生结果。大多数情况下,使用欧几里得距离度量,这使得计算成本较低。它还确保算法一定收敛并产生结果。
K-means 在现实生活中的商业问题中得到了广泛应用。尽管 k-means 聚类有明显的优势,但我们确实面临着算法的一些挑战:
-
选择最佳聚类数量并不容易。我们应该将其作为输入提供。随着k的不同值,结果将完全不同。选择最佳k值的过程在前一节中进行了探讨。
-
该解决方案依赖于质心的初始值。由于质心是随机初始化的,每次迭代的输出将不同。因此,建议运行多个版本的解决方案,并选择最佳方案。
-
该算法对异常值非常敏感。异常值可能会破坏最终结果,因此在开始聚类之前处理异常值至关重要。我们还可以实现 k-means 算法的其他变体,如 k-modes 聚类,来处理异常值问题。我们将在第十一章的第 11.4.4 节中讨论处理异常值。如果您想了解如何处理异常值,可以参考它。
-
由于 k-means 聚类的根本原理是计算距离,因此该解决方案不能直接应用于分类变量。换句话说,我们不能直接使用分类变量,因为我们可以计算数值之间的距离,但不能对分类变量进行数学计算。为了解决这个问题,我们可以使用 one-hot 编码将分类变量转换为数值。我们将在第十一章的第 11.4.2 节中讨论处理分类变量。如果您想了解如何处理分类变量,可以参考它。
尽管存在这些问题,k-means 聚类由于其简单性和易于实现,仍然是使用最广泛的聚类解决方案之一。k-means 算法有不同的实现,如 k-中位数、k-medoids 等,有时用于解决面临的难题:
-
如其名所示,k-中位数聚类是基于数据集的中位数,而不是 k-means 中的质心。这增加了计算时间,因为中位数只能在数据排序后找到。但与此同时,k-means 对异常值敏感,而 k-中位数则受其影响较小。
-
K-medoids 聚类是 k-means 算法的一种变体。Medoids 类似于均值,但它们总是来自同一数据集,并在难以获取均值时实现,例如图像。Medoid 可以被视为簇中最中心且与其他簇成员最不相似的点。与 k-means 不同,k-medoids 选择实际观测值作为中心,而 k-means 的质心可能甚至不是数据的一部分。与 k-means 聚类算法相比,它对异常值的影响较小。
还有其他版本,包括 k-means++、mini-batch k-means 等。通常,在工业界,k-means 用于大多数聚类解决方案。如果结果不理想或计算花费时间过多,您可以探索其他选项,如 k-means++、mini-batch k-means 等。此外,不同的距离度量指标可能会为 k-means 算法产生不同的结果。
本节总结了我们对 k-means 聚类算法的讨论。现在是时候进入实验室并开发实际的 Python 代码了!
2.3.5 使用 Python 实现 K-means 聚类
我们现在将为 k-means 聚类创建一个 Python 解决方案。在这种情况下,我们使用 GitHub 上链接的数据集mng.bz/lYq2。这个数据集包含关于四种车型特征的信息。基于车型的特征,我们将它们分组到不同的簇中:
- 将库和数据集导入到 dataframe 中。在这里,
vehicles.csv是输入数据文件。如果数据文件不在 Jupyter 笔记本的同一文件夹中,您必须提供文件的完整路径。dropna用于删除任何缺失值:
import pandas as pd
vehicle_df = pd.read_csv('vehicle.csv').dropna()
-
- 对数据进行一些初步检查,如形状、信息、前五行、类别的分布等。这是为了确保我们已经加载了完整的数据集,并且在加载数据集时没有损坏。
Shape命令将给出数据中的行数和列数,info将描述所有变量及其相应的类型,而head将显示前五行。value_counts显示了class变量的分布。或者换句话说,value_counts返回唯一值的计数:
- 对数据进行一些初步检查,如形状、信息、前五行、类别的分布等。这是为了确保我们已经加载了完整的数据集,并且在加载数据集时没有损坏。
vehicle_df.shape
vehicle_df.info()
vehicle_df.head()
pd.value_counts(vehicle_df['class'])
-
- 为变量
class生成两个图表。数据集包含来自car的更多示例,而bus和van的数据是平衡的。我使用了matplotlib库来绘制这些图表。图表的输出如下(见图 2.11):
- 为变量
import matplotlib.pyplot as plt
%matplotlib inline
pd.value_counts(vehicle_df["class"]).plot(kind='bar')
pd.value_counts(vehicle_df['class']).hist(bins=300)

图 2.11 变量class的两个图表
-
- 检查数据集中是否有缺失的数据点。在我们的数据集中没有缺失的数据点,因为我们已经处理了它们:
vehicle_df.isna().sum()
注意:我们在第十一章的第 11.4.3 节中介绍了处理缺失值的方法,因为删除缺失值通常不是最佳方法。
-
- 标准化数据集。对于聚类来说,标准化数据集是一个好的实践。这是很重要的,因为不同的维度可能处于不同的尺度上,如果一个维度的值自然比其他维度大得多,那么它可能会主导距离的计算。这是通过使用
StandardScaler()函数来完成的。请参考附录以检查不同的缩放技术:
- 标准化数据集。对于聚类来说,标准化数据集是一个好的实践。这是很重要的,因为不同的维度可能处于不同的尺度上,如果一个维度的值自然比其他维度大得多,那么它可能会主导距离的计算。这是通过使用
vehicle_df_1 = vehicle_df.drop('class', axis=1)
from scipy.stats import zscore
vehicle_df_1_z = vehicle_df_1.apply(zscore)
from sklearn.preprocessing import StandardScaler
import umpy as np
sc = StandardScaler()
X_standard = sc.fit_transform(vehicle_df_1)
-
- 通过生成散点图快速查看数据集。该图表显示了我们在上一步创建的
X_standard中的所有数据点的分布(见图 2.12):
- 通过生成散点图快速查看数据集。该图表显示了我们在上一步创建的
plt.scatter(X_standard[:,0], X_standard[:,1])
plt.show()

图 2.12 数据集的散点图
-
- 执行 k-means 聚类。首先,我们必须使用肘部方法选择最佳聚类数量。从
sklearn库中导入KMeans。在一个for循环中,我们遍历从 1 到 10 的聚类值。换句话说,算法将创建 1 到 10 个聚类,然后为我们生成结果以选择最优的k值。
- 执行 k-means 聚类。首先,我们必须使用肘部方法选择最佳聚类数量。从
在下面的代码片段中,模型对象包含了 k-means 算法的输出,然后将其拟合到上一步生成的X_standard。在这里,已经使用了欧几里得距离作为距离度量(见图 2.13):
from sklearn.cluster import KMeans
from scipy.spatial.distance import cdist
clusters=range(1,10)
meanDistortions=[]
for k in clusters:
model=KMeans(n_clusters=k)
model.fit(X_standard)
prediction=model.predict(X_standard)
meanDistortions.append(sum(np.min(cdist(X_standard,
model.cluster_centers_, 'euclidean'), axis=1)) / X_standard
.shape[0])
plt.plot(clusters, meanDistortions, 'bx-')
plt.xlabel('k')
plt.ylabel('Average distortion')
plt.title('Selecting k with the Elbow Method')

图 2.13 k-means 聚类
-
- 如我们所观察到的,最佳聚类数量是三个。这是我们可以观察到图形中尖锐拐点的点。我们将继续使用三个聚类数量的 k-means 聚类。在这里,数字 3 并没有什么特殊之处,但它最适合这个数据集。
random_state是一个用于确定质心初始化随机数的参数。我们将其设置为一个值以使随机性确定。如果您想再次重复相同的结果,请使用相同的随机状态数字。它就像一个种子数字:
- 如我们所观察到的,最佳聚类数量是三个。这是我们可以观察到图形中尖锐拐点的点。我们将继续使用三个聚类数量的 k-means 聚类。在这里,数字 3 并没有什么特殊之处,但它最适合这个数据集。
kmeans = KMeans(n_clusters=3, n_init = 15, random_state=2345)
kmeans.fit(X_standard)
-
- 获取聚类的
centroids:
- 获取聚类的
centroids = kmeans.cluster_centers_
centroids
-
- 现在我们使用
centroids,这样它们就可以通过columns进行配置:
- 现在我们使用
centroid_df = pd.DataFrame(centroids, columns = list(X_standard) )
-
- 现在我们只为创建
labels的目的创建一个dataframe,然后将其转换为分类变量:
- 现在我们只为创建
dataframe_labels = pd.DataFrame(kmeans.labels_ , columns = list(['labels']))
dataframe_labels['labels'] = dataframe_labels['labels'].astype('category')
-
- 在这一步,我们将两个
dataframes连接起来:
- 在这一步,我们将两个
dataframe_labeled = vehicle_df_1.join(dataframe_labels)
-
- 进行
groupby操作以创建分析所需的数据框:
- 进行
dataframe_analysis = (dataframe_labeled.groupby(['labels'] , axis=0)).head(1234)
dataframe_labeled['labels'].value_counts()
-
- 现在我们为定义的聚类创建一个可视化。这是使用
mpl_toolkits库完成的。逻辑简单易懂。数据点根据相应的标签着色。其余步骤与调整标签、标题、刻度等以显示图表有关。由于无法在图表中绘制所有 18 个变量,我们选择了 3 个变量在图表中显示(见图 2.14):
- 现在我们为定义的聚类创建一个可视化。这是使用
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(8, 6))
ax = Axes3D(fig, rect=[0, 0, .95, 1], elev=20, azim=60)
kmeans.fit(vehicle_df_1_z)
labels = kmeans.labels_
ax.scatter(vehicle_df_1_z.iloc[:, 0], vehicle_df_1_z.iloc[:, 1], vehicle_df_1_z.iloc[:, 3],c=labels.astype(np.float), edgecolor='k')
ax.w_xaxis.set_ticklabels([])
ax.w_yaxis.set_ticklabels([])
ax.w_zaxis.set_ticklabels([])
ax.set_xlabel('Length')
ax.set_ylabel('Height')
ax.set_zlabel('Weight')
ax.set_title('3D plot of KMeans Clustering on vehicles dataset')
我们也可以用多个其他的k值测试前面的代码。我们已经为不同的k值创建了代码。为了节省空间,我们将测试不同k值的代码放在 GitHub 位置。

图 2.14 车辆数据集的 k-means 聚类
注意:探索性数据分析是稳健的机器学习解决方案和成功项目的关键。在随后的章节中,我们将为数据集创建详细的数据探索分析。
在先前的例子中,我们首先对数据集进行了小规模的探索性分析。随后,我们确定了最佳簇的数量,在这个例子中是三个。然后我们实现了 k-means 聚类。你被期望迭代 k-means 解决方案,使用不同的初始化并比较结果,使用不同的k值进行迭代,并可视化以分析数据点的移动。
由于其逻辑简单、易于实现、灵活且易于维护,基于质心的聚类是推荐的最解决方案之一。每当我们需要聚类作为解决方案时,我们通常首先创建一个 k-means 聚类解决方案作为基准。该算法非常受欢迎,通常也是用于聚类的第一个解决方案之一。然后我们测试并迭代其他算法。
2.4 基于连接性的聚类
“物以类聚”是连接性聚类所遵循的原则。其核心概念是相互连接的对象彼此相似。因此,基于这些对象之间的连接性,它们被分组到不同的簇中。图 2.15 展示了这种表示的一个例子,其中我们可以迭代地分组观察。例如,我们首先从所有事物开始,将其分为生物和非生物等。这种表示被称为树状图。由于存在树状结构,基于连接性的聚类有时也被称为层次聚类。

图 2.15 展示了层次聚类利用迭代分组相似对象的表示。这种表示被称为树状图。
层次聚类与人类的直觉相吻合,因此易于理解。与 k-means 聚类不同,在层次聚类中,我们不需要输入最终簇的数量,但该方法确实需要一个终止条件(即聚类何时停止)。同时,层次聚类也不建议簇的最佳数量。从生成的层次/树状图中,我们必须自己选择最佳的簇数量。我们将在后续章节中创建 Python 代码时进一步探讨这一点。
图 2.16 展示了层次聚类的一个例子。在这里,第一个节点是根节点,然后它被迭代地分割成节点和子节点。每当一个节点不能再分割时,它被称为终端节点或叶节点。

图 2.16 展示了层次聚类的结构,其根部分裂成节点和子节点。不能再进一步分割的节点被称为叶节点。在自底向上的方法中,叶节点将进行合并。
由于存在多个将观察结果合并到簇中的过程或逻辑,我们可以生成大量的树状图,这由方程 2.7 给出:
(2.7)
树状图的数目 = (2n – 3)!/[2((n)^(–2)) (n – 2)!]
其中 n 是观测的数量或叶子节点。因此,如果我们只有两个观测,我们只能有一个树状图。如果我们有 5 个观测,我们可以有 105 个树状图。因此,根据观测的数量,我们可以生成大量的树状图。
层次聚类可以根据创建观测分组所使用的流程进一步分类,我们将在下一节探讨。
2.4.1 层次聚类的类型
基于分组策略,层次聚类可以分为两种类型:聚类聚类和分裂聚类(见表 2.2)。
表 2.2 不同类型的层次聚类
| 序号 | 聚类方法 | 分裂方法 |
|---|---|---|
| 1 | 自底向上的方法 | 自顶向下的方法 |
| 2 | 每个观测创建自己的簇,然后随着算法向上进行,合并发生。 | 我们从一个簇开始,然后迭代地分裂观测以创建树状结构。 |
| 3 | 采用贪婪方法进行合并(贪婪方法将在下面描述)。 | 采用贪婪方法进行分裂。 |
| 4 | 观测将找到最佳的合并配对,并且当所有观测都相互合并时,过程完成。 | 所有观测在开始时都被考虑,然后根据分裂条件进行分裂,直到所有观测被耗尽或满足终止条件。 |
首先,让我们探讨贪婪方法的意义。贪婪方法或贪婪算法是任何在每一步都做出最佳选择而不考虑对未来状态影响的算法。换句话说,我们活在当下,并从当时可用的选择中做出最佳选择。当前的选择独立于未来的选择,算法将在以后解决子问题。贪婪方法可能不会提供最优化解,但通常在合理的时间内提供接近最佳解的局部最优解。层次聚类在合并或分裂节点时遵循这种贪婪方法。
接下来,我们检查层次聚类方法中遵循的步骤:
-
如图 2.17 所示,假设我们的数据集中有五个观测:1、2、3、4 和 5。
-
在这一步,观测 1 和 2 被归为一组,4 和 5 也被归为一组;3 没有被归入任何一组。
-
在这一步,我们将上一步的 4、5 步的输出和观测 3 合并到一个簇中。
-
第 3 步的输出与第 1、2 步的输出合并为一个单独的簇。

图 2.17 层次聚类所遵循的步骤。从左到右,我们看到了聚类(节点合并),而从右到左,我们看到了分裂聚类(节点分裂)。
在这种方法中,从左到右,我们有一个聚合方法,而从右到左,则表示一个分裂方法。在聚合方法中,我们合并观测值,而在分裂方法中,我们分割观测值。我们可以使用聚合和分裂方法进行层次聚类。分裂聚类是一种穷举方法,有时可能比其他方法花费更多时间。
与 k 均值聚类类似,用于测量的距离度量在这里起着重要作用。我们了解并理解如何测量数据点之间的距离,但定义该距离的方法有很多,我们将在下一节中研究。
2.4.2 距离测量的连接标准
我们可以使用欧几里得距离、曼哈顿距离、切比雪夫距离等来测量两个观测值之间的距离。同时,我们可以采用各种方法来定义该距离。基于这个输入标准,生成的簇将不同。定义距离度量的各种方法如下:
-
最近邻或单连接使用不同簇中两个最近点之间的距离。计算不同簇中最近邻之间的距离,并以此确定下一个分割/合并。这是通过在所有成对中进行的穷举搜索来完成的。
-
最远邻或完全连接是与最近邻方法相反的。在这里,我们不是选择最近的邻居,而是关注不同簇中最远的邻居。换句话说,簇之间的距离是通过两个对象之间最大的距离来计算的。
-
组平均连接计算两个不同簇中所有可能成对对象之间的距离的平均值。
-
Ward 连接方法旨在最小化合并到一个簇中的簇的变异性。
在我们开发层次聚类的实际代码时,我们可以使用这些距离度量的选项,并比较准确性以确定数据集的最佳距离度量。在算法训练期间,算法合并观测值,这将最小化选择的连接标准。我们可以在图 2.18 中可视化各种连接方式。
注意:算法中的这些输入被称为超参数。这些是我们输入到算法中的值,以生成符合我们要求的结果,并且它们作为我们对算法的控制。k 均值聚类中的一个超参数示例是k。

图 2.18:单连接用于最近邻(左);完全连接用于最远邻(中);组平均用于簇间距离的平均(右)。
通过这一点,我们已经理解了层次聚类的工作机制。但我们还没有解决使用层次聚类确定最佳簇数量的机制,这将在下一部分进行探讨。
2.4.3 最佳簇数量
回想一下,在 k-means 聚类中,我们必须将簇的数量作为输入提供给算法。我们使用肘部方法来确定最佳簇数量。在层次聚类的案例中,我们不需要将簇的数量指定给算法,但我们仍然需要确定我们希望拥有的最终簇数量。我们使用树状图来回答这个问题。
让我们假设图表底部总共有 10 个数据点,如图 2.19 所示。簇会迭代合并,直到我们得到顶部的最终簇。树状图中两个簇合并在一起的高度代表了在向量空间图中这两个簇之间的相应距离。

图 2.19 树状图以确定最佳簇数量。X 和 Y 之间的距离比 A 和 B 以及 P 和 Q 之间的距离要远;因此,我们选择它作为切割以创建簇,选择的簇数量是五个。x 轴代表簇,而 y 轴代表两个簇之间的距离(相似度)。
从树状图中,簇的数量由被水平线切割的垂直线的数量给出。最佳簇数量由树状图中被水平线切割的垂直线的数量给出,这样它就与最高的垂直线相交。或者,如果切割从垂直线的这一端移动到另一端,覆盖的长度是最大的。树状图利用簇的分支来显示各种数据点之间是如何相互关联的。在树状图中,位于相同高度水平的簇比位于不同高度水平的簇更紧密相关。
在图 2.19 中显示的例子中,我们展示了三个潜在的切割:AB、PQ 和 XY。如果我们对 AB 进行切割,它将导致两个非常宽泛的簇,而低于 PQ 将导致九个簇,这将变得难以进一步分析。
在这里,X 和 Y 之间的距离比 A 和 B 以及 P 和 Q 之间的距离要远。因此,我们可以得出结论,X 和 Y 之间的距离是最大的,因此,我们可以将其最终确定为最佳切割。这个切割与五个不同的点相交;因此,我们应该有五个簇。树状图上切割的高度与 k-means 聚类中的k值相似。在 k-means 聚类中,k决定了簇的数量。在层次聚类中,最佳切割决定了我们希望拥有的簇的数量。
与 k-means 聚类类似,最终的聚类数量不仅取决于算法的选择。商业敏锐度和实用逻辑在确定最终聚类数量中起着至关重要的作用。回想一下,簇的一个重要属性是它们的可用性,我们在第 2.2 节中讨论了这一点。
有时我们也会使用柯普仁尼克相关系数来衡量树状图如何很好地表示点之间的实际成对距离。它比较了柯普仁尼克距离,即两个点在树状图中首次合并的高度,与点之间的原始差异。
还有一个称为 Calinski-Haranasz 指数的指标。它衡量了簇间分散与簇内分散的比率。值越高表示聚类越好,因此我们选择最大化这个指数的最佳聚类数量。
练习 2.3
回答这些问题以检查你的理解:
-
层次聚类中使用的贪婪方法是什么?
-
完全连接用于寻找最近邻的距离。对或错?
-
群连接和 ward 连接之间的区别是什么?
-
描述寻找最优化k值的过程。
2.4.4 层次聚类的优缺点
层次聚类是一种强大的聚类技术,也非常受欢迎。与 k-means 类似,它也使用距离作为度量标准来衡量相似性。同时,算法存在一些挑战。层次聚类的优点如下:
-
层次聚类的最大优势可能是结果的再现性。回想一下 k-means 聚类,过程从质心的随机初始化开始,给出不同的结果。在层次聚类中,我们可以再现结果。
-
在层次聚类中,我们不需要输入聚类的数量来分割数据。
-
实现起来既容易又易懂。因为它遵循树状结构,所以可以解释给非技术背景的用户。
-
生成的树状图可以通过可视化来解释,从而对数据有一个很好的理解。
同时,我们确实面临一些层次聚类算法的挑战,如下所示:
- 我们在层次聚类中面临的最大挑战是收敛所需的时间。k-means 的时间复杂度是线性的,而层次聚类是二次的。例如,如果我们有“n”个数据点,那么对于 k-means 聚类,时间复杂度将是O(n),而对于层次聚类,它是O(n³)。
TIP 如果你想研究O(n),请参考附录。
-
由于时间复杂度为 O(n³),这是一个耗时的任务。此外,计算所需的内存至少为 O(n²),这使得层次聚类成为一个耗时且内存密集的过程。即使数据集是中等规模,所需的计算也可能不是挑战,但对于普通计算机来说,这确实可能是一个问题。
-
有时对树状图的解释可能是主观的;因此,在解释树状图时需要尽职尽责。解释树状图的关键是关注任何两个数据点连接的高度。这可能具有主观性,因为不同的分析师可以解读不同的切割并试图证明他们的方法。因此,建议在数学和现实世界业务问题的背景下解释结果。
-
层次聚类无法撤销它已经完成的先前步骤。即使我们觉得建立的联系不合适,应该回滚,也没有机制来删除连接。
-
该算法对异常值和杂乱的数据集非常敏感。异常值、NULL、缺失值、重复值等的存在使得数据集变得杂乱。因此,结果可能不正确或不是我们所期望的。
尽管存在所有挑战,层次聚类仍然是使用最广泛的聚类算法之一。通常,我们为同一数据集创建 k-means 聚类和层次聚类来比较两种算法的结果。如果建议的聚类数量和相应聚类的分布看起来相似,我们就更有信心关于所使用的聚类方法。
我们已经涵盖了层次聚类的理论背景。现在是时候采取行动,跳入 Python 进行编码了。
2.4.5 使用 Python 进行层次聚类案例研究
我们现在将使用与 k-means 聚类相同的相同数据集为层次聚类创建一个 Python 解决方案:
- 加载所需的库和数据集。为此,遵循我们为 k-means 算法遵循的步骤 1 到 6。

图 2.20 使用平均、ward 和完全链接方法进行层次聚类(从上到下分别为)
-
- 接下来,我们使用三种链接方法:平均、ward 和完全来创建层次聚类。然后绘制聚类。该方法输入是
X_Standard变量,使用的链接方法和距离度量。然后,使用matplotlib库绘制树状图。在下面的代码片段中,只需将方法从“平均”更改为“ward”和“完全”,即可获得相应的结果(见图 2.20):
- 接下来,我们使用三种链接方法:平均、ward 和完全来创建层次聚类。然后绘制聚类。该方法输入是
from scipy.cluster.hierarchy import dendrogram, linkage
Z_df_average = linkage(X_standard, 'average', metric='euclidean')
Z_df_average.shape
plt.figure(figsize=(30, 12))
dendrogram(Z_df_average)
plt.show()
-
- 我们现在想要选择我们希望拥有的聚类数量。为此,让我们通过子集最后合并的 10 个聚类来重新创建树状图。我们选择 10 是因为它通常是一个最佳选择;我建议你也用其他值进行测试(见图 2.21):

图 2.21 显示最后 10 个合并聚类的树状图
dendrogram(
Z_df_complete,
truncate_mode='lastp', p=10,)
plt.show()
-
- 我们观察到最佳距离是 10。
-
- 将数据聚类到不同的组中。通过使用上一节中描述的逻辑,最佳聚类的数量将是四个:
from scipy.cluster.hierarchy import fcluster
hier_clusters = fcluster(Z_df_complete, max_distance, criterion='distance')
hier_clusters
len(set(hier_clusters))
-
- 使用
matplotlib库绘制不同的聚类。在书的打印版本中,您将看不到不同的颜色。Python 代码的输出将包含颜色;我建议您运行代码以欣赏输出效果。相同的输出可以在 GitHub 仓库中找到(见图 2.22):
- 使用
plt.scatter(X_standard[:,0], X_standard[:,1], c=hier_clusters)
plt.show()

图 2.22 使用matplotlib库绘制的不同聚类图
-
- 对于不同的距离值,聚类的数量将改变,图表看起来也会不同。我们展示了距离为 5、15 和 20 时以及每次迭代生成的不同数量的聚类结果。图 2.23 显示,当我们从左到右移动时,对于不同的距离值,我们得到完全不同的结果。在选择距离值时,我们应该谨慎,有时我们可能需要迭代几次才能得到最佳值。

图 2.23 使用不同距离值的聚类数量
使用层次聚类,我们将图 2.24 左侧的数据分割到右侧。左侧表示原始数据,而右侧我们有聚类数据集的表示。在书的打印版本中,您将看不到不同的颜色。Python 代码的输出将包含颜色。相同的输出可以在 GitHub 仓库中找到。

图 2.24 使用层次聚类分割数据
层次聚类是一种稳健的方法,并且非常推荐。与 k-means 一起,它为基于聚类的解决方案提供了一个很好的基础。大多数时候,至少我们在创建聚类解决方案时使用这两种技术,然后我们继续使用其他方法进行迭代。
2.5 基于密度的聚类
我们在前面章节中学习了 k-means。回想一下它如何使用基于质心的方法将每个数据点分配到一个聚类中。如果一个观测值是异常值,异常点会将质心拉向自己,并且像正常观测值一样被分配到一个聚类中。这些异常值不一定向聚类提供信息,并且可能不成比例地影响其他数据点,但它们仍然被作为聚类的一部分。此外,如图 2.25 所示,使用 k-means 算法获取任意形状的聚类是一个挑战。基于密度的聚类方法解决了这个问题。

图 2.25 DBSCAN 非常适合不规则形状的聚类。与 k-means 相比,我们通常得到球形聚类;DBSCAN 可以解决这个问题。
在基于密度的方法中,簇被识别为密度高于数据集其余部分的区域。换句话说,在一个表示数据点的向量空间图中,簇被定义为高密度点的相邻区域或邻近区域。这个簇将由低密度点的区域与其他簇分开。稀疏区域或分隔区域中的观测被认为是数据集中的噪声或异常值。图 2.25 展示了基于密度的聚类的一些示例。
我们提到了两个术语:邻域和密度。为了理解基于密度的聚类,我们将在下一节中研究这些术语。
2.5.1 邻域和密度
假设我们用向量空间表示数据观测,并且有一个点 P。我们现在定义这个点 P 的邻域。表示如图 2.26 所示。对于点 P,我们定义了一个ε邻域,即与 P 等距离的点。在二维空间中,它表示为一个圆;在三维空间中,它是一个球体;而对于n维空间,它是以 P 为中心、半径为ε的n球体。这定义了邻域的概念。

图 2.26 向量空间图中数据点的表示。在右侧有一个点 P,画出的圆的半径为ε。因此,对于ε > 0,点 P 的邻域由距离点 P 小于或等于ε的点集定义。
现在我们来探讨一下密度这个术语。回想一下,密度是质量除以体积(质量/体积)。质量越高,密度越高;质量越低,密度越低。相反,体积越低,密度越高,反之亦然。
在前面的上下文中,质量是邻域中点的数量。在图 2.26 中,我们可以观察到ε对数据点数量或质量的影响。当涉及到体积时,在二维空间中,体积是πr²,而对于三维的球体,它是 4/3 πr³。对于n维的球体,我们可以根据维数计算相应的体积,这将是一个数值常数乘以维数的幂。
因此,在图 2.27 所示的两种情况下,对于点 P,我们可以得到点的数量(质量)和体积,然后我们可以计算出各自的密度。但是,这些密度的绝对值对我们来说没有任何意义;相反,它们与附近区域的相似性(或不同性)才是重要的。位于同一区域且具有相似密度的点可以被归入一个簇。

图 2.27 半径ε的影响。在左侧,点的数量多于右侧。因此,右侧的质量较小,因为它包含的数据点较少。
在理想情况下,我们希望拥有高度密集的聚类,具有最大数量的点。在图 2.28 所示的两种情况下,左侧显示了一个较稀疏的聚类,而右侧显示了一个高密度的聚类。

图 2.28 较密集的聚类比较稀疏的聚类更受欢迎。理想情况下,一个具有最大数据点的密集聚类是我们从聚类中希望实现的目标。
从前面的讨论中,我们可以得出结论:
-
如果我们增加ε的值,我们将获得更大的体积,但不一定是更多的点数(质量)。这取决于数据点的分布。
-
如果我们减少ε的值,我们将获得较小的体积,但不一定是较少的点数(质量)。
这些是我们坚持的基本原则。因此,我们必须选择具有高密度并覆盖最多邻近点的聚类。
2.5.2 DBSCAN 聚类
DBSCAN 聚类是一种高度推荐的基于密度的算法。它将密集区域中紧密排列的数据观察值聚类,但不考虑低密度区域的异常值。与 k-means 不同,我们不需要指定聚类数量,该算法能够识别不规则形状的聚类,而 k-means 通常提出球形聚类。类似于层次聚类,它通过连接数据点来工作,但与满足密度标准或阈值值的观察值相关。
注意:DBSCAN 由 Martin Ester、Hans-Peter Kriegal、Jörg Sander 和 Xiaowei Xu 于 1996 年提出。该算法在 2014 年 ACM SIGKDD 的测试时间奖中获奖。论文可在mng.bz/BXv1访问。
DBSCAN 基于我们在上一节讨论的邻域概念。现在我们将深入探讨 DBSCAN 的工作方法和构建模块。
DBSCAN 聚类的要点
现在我们来检查 DBSCAN 聚类的核心构建模块。我们知道它是一种基于密度的聚类算法,因此邻域概念在这里适用。
假设我们有一些需要聚类的数据观察值。我们也定位了一个数据点 P。然后我们可以轻松地定义两个超参数术语:
-
P 点周围的邻域半径,称为ε,这是我们上一节讨论的内容。
-
我们希望在 P 点邻域中拥有的最小点数,换句话说,创建密集区域所需的最小点数。这被称为最小点数(minPts)。这是我们可以在 minPts 上应用阈值输入的参数之一。
基于这些概念,我们可以将观察值分为三个广泛的类别:核心点、边界或可达点,和异常值:
-
核心点—任何数据点 x,如果至少有 minPts 个点在它(包括 x 本身)的ε距离范围内,则可以称为核心点,在图 2.29 中用方块表示。它们是我们聚类的构建块,被称为核心点。我们对每个点使用相同的半径值(ε),因此每个邻域的体积保持不变。但点的数量会变化,因此质量也会变化。因此,密度也会变化。由于我们使用 minPts 设置了一个阈值,我们实际上是在对密度设置一个限制。因此,我们可以得出结论,核心点满足最小密度阈值要求。必须注意的是,我们可以选择不同的ε和 minPts 值来迭代和微调聚类。
-
边界点或可达点—在聚类中不是核心点的点称为边界点,在图 2.29 中用实心圆表示。

图 2.29 核心点用方块表示;边界点用实心圆表示,而噪声用空心圆表示。这三个共同构成了 DBSCAN 聚类的构建块。
如果点 y 在核心点 x 的ε距离内,则 y 可以从 x 直接到达。一个点只能从一个核心点接近,这是需要遵循的主要条件或规则。只有核心点可以到达非核心点,反之则不成立。换句话说,一个非核心点只能被其他核心点到达;它不能到达任何人。在图 2.29 中,边界点用深色圆圈表示。
为了更好地理解这个过程,我们必须理解术语密度可达或连通性。在图 2.30 中,我们有两个核心点:X 和 Y。我们可以直接从 X 到 Y。点 Z 不在 X 的邻域内,但在 Y 的邻域内。因此,我们不能直接从 X 到达 Z,但我们可以肯定地通过 Y 或换句话说,通过 Y 的邻域从 X 到达 Z。相反,我们不能从 Z 到 X,因为 Z 是边界点,如前所述,我们不能从边界点进行旅行。

图 2.30 中,X 和 Y 是核心点,我们可以从 X 到 Y 旅行。尽管 Z 不在 X 的最近邻域内,但我们仍然可以通过 Y 从 X 到达 Z。这是 DBSCAN 聚类中使用的密度连通点的核心概念。
- 异常值—所有其他点都是异常值。换句话说,如果不是核心点或不是可达点,它就是异常值,在图 2.29 中用空心圆表示。它们没有被分配到任何聚类中。
DBSCAN 聚类的步骤
DBSCAN 聚类的步骤如下:
-
我们从为创建聚类所需的ε和 minPts 赋值开始。
-
我们从选择一个随机点开始,比如说 P,它还没有被赋予任何标签(即,它还没有被分析并分配到任何聚类中)。
-
然后我们分析 P 的邻域。如果它包含足够多的点(即,高于 minPts),那么满足开始簇的条件。如果是这样,我们将点 P 标记为核心点。如果一个点不能被识别为核心点,我们将将其标记为异常点或噪声。我们应该注意这个点以后可以成为不同簇的一部分。然后我们回到步骤 2。
-
一旦找到这个核心点 P,我们就开始通过添加从 P 直接可达的所有点来创建簇,然后通过添加更多从 P 直接可达的点来增加这个簇的大小。然后我们通过迭代所有这些点,将所有点添加到簇中,这些点可以通过邻域包含。如果我们向簇中添加一个异常点,则将异常点的标签更改为边界点。
-
这个过程会一直持续到密度簇完成。然后我们找到一个新未分配的点,并重复这个过程。
-
一旦所有点都被分配到一个簇或称为异常点,我们就停止我们的聚类过程。
在这个过程中有迭代。然后,一旦聚类完成,我们利用业务逻辑来合并或拆分几个簇。
练习 2.4
回答这些问题以检查你的理解:
-
比较和对比 DBSCAN 聚类与 k-means 聚类的重要性。
-
非核心点可以到达核心点,反之亦然。对还是错?
-
解释邻域和 minPts 的重要性。
-
描述寻找k的最优值的过程。
现在我们已经清楚 DBSCAN 聚类的过程。在创建 Python 解决方案之前,我们将检查 DBSCAN 算法的优点和缺点。
DBSCAN 聚类的优缺点
DBSCAN 有以下优点:
-
与 k-means 不同,我们不需要指定 DBSCAN 的簇数。
-
该算法是对不干净数据集相当鲁棒的解决方案。与其他算法不同,它可以有效地处理异常值。
-
我们也可以确定不规则形状的簇。可以说,这是 DBSCAN 聚类最大的优点。
-
算法只需要输入半径和 minPts。
DBSCAN 有以下挑战:
-
使用 DBSCAN 有时无法清楚地区分簇。根据处理观察值的顺序,一个点可以改变其簇。换句话说,如果一个边界点 P 可以被多个簇访问,P 可以属于任何一个簇,这取决于处理数据的顺序。
-
如果数据集不同区域之间的密度差异非常大,那么确定ε和 minPts 的最佳组合将非常困难,因此 DBSCAN 将无法生成有效结果。
-
使用的距离度量在聚类算法中起着高度重要的作用,包括 DBSCAN。可以说,最常用的度量是欧几里得距离,但如果维度数量相当大,那么计算就成为一个挑战。
-
该算法对ε和 minPts 的不同值非常敏感。有时找到最优化值成为一个挑战。
DBSCAN 聚类的 Python 解决方案
我们将使用与 k-means 和层次聚类相同的相同数据集:
-
在 k-means 算法的第 6 步之前,加载库和数据集。
-
导入额外的库:
from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import normalize
from sklearn.neighbors import NearestNeighbors
这里我们使用最小距离和半径的值来拟合模型:
db_default = DBSCAN(eps = 0.0375, min_samples = 6).fit(X_standard)
labels = db_default.labels_
独特聚类的数量是 1:
list(set(labels))
-
- 在这里我们没有得到任何关于聚类的结果。换句话说,由于我们没有提供 minPts 和ε的最佳值,因此不会有任何逻辑上的聚类结果。
-
- 现在,我们将找到ε的最佳值(见图 2.31)。为此,我们将计算每个点到最近点的距离,然后对结果进行排序和绘图。曲线最大的地方就是ε的最佳值。对于 minPts,通常 minPts ≥ d + 1,其中d是数据集中的维度数:
neigh = NearestNeighbors(n_neighbors=2)
nbrs = neigh.fit(X_standard)
distances, indices = nbrs.kneighbors(X_standard)
distances = np.sort(distances, axis=0)
distances = distances[:,1]
plt.plot(distances)

图 2.31 寻找ε的最佳值
注意:有关如何选择 DBSCAN 半径值的进一步研究,请参阅iopscience.iop.org/article/10.1088/1755-1315/31/1/012012/pdf。
-
- 观察到缺陷点时,最佳值是 1.5,我们将使用它并将 minPts 设置为 5,这通常被视为标准:
db_default = DBSCAN(eps=1.5, min_samples=5)
db_default.fit(X_standard)
clusters = db_default.labels_
-
- 现在,我们可以观察到我们得到了多个聚类:
list(set(clusters))
-
- 让我们绘制聚类(见图 2.32)。在书的打印版本中,您将看不到不同的颜色。Python 代码的输出将包含颜色。相同的输出可在 GitHub 仓库中找到:
colors = ['blue', 'red', 'orange', 'green', 'purple', 'black', 'brown', 'cyan', 'yellow', 'pink']
vectorizer = np.vectorize(lambda x: colors[x % len(colors)])
plt.scatter(X_standard[:,0], X_standard[:,1], c=vectorizer(clusters))

图 2.32 绘制聚类
因此,我们使用 DBSCAN 创建了一个解决方案。我建议您比较所有三个算法的结果。在现实世界的场景中,我们使用多个算法测试解决方案,迭代超参数,然后选择最佳解决方案。
基于密度的聚类是一个相当有效的解决方案,并且在某种程度上也是一个非常有效的解决方案。如果怀疑聚类的形状是不规则的,则强烈推荐使用。
通过这一点,我们结束了 DBSCAN 聚类的讨论。在下一节中,我们将解决一个关于聚类的商业案例。在案例研究中,重点较少放在技术概念上,而更多放在商业理解和解决方案生成上。
2.6 使用聚类进行案例研究
我们现在将定义一个案例研究,该研究将聚类作为解决方案之一。案例研究的目的是让您了解实际和现实生活中的商业世界。这种基于案例研究的方法也适用于与工作相关的面试,其中在面试阶段讨论案例。我强烈建议您了解我们如何在务实商业场景中实施机器学习解决方案。
案例研究通常包括业务问题、可用数据集、可用的各种解决方案、面临的挑战以及最终选择的解决方案。我们还讨论了在现实世界业务中实施解决方案时遇到的问题。
因此,让我们从使用无监督学习的聚类案例研究开始。在案例研究中,我们关注解决案例研究的步骤,而不是技术算法,因为对于特定问题可能有多种技术解决方案。
2.6.1 业务背景
我们考虑的行业可以是零售业;电信业;银行、金融服务和保险业;航空业;医疗保健业;或任何拥有客户基础的行业。对于任何业务,目标是为业务创造更多收入,并最终增加业务的总体利润。为了增加收入,业务希望拥有越来越多的新客户。业务也希望现有消费者购买更多,并且更频繁地购买。因此,业务总是努力保持消费者的参与和满意度,并增加他们与业务的交易价值。
为了实现这一点,业务必须对其消费者基础有深入的了解;它必须了解他们的口味、价格点、类别偏好、亲和力、首选的营销/沟通渠道等。一旦业务仔细检查并理解了消费者基础,那么
-
产品团队可以根据消费者的需求改进产品功能。
-
定价团队可以通过将产品与客户偏好的价格对齐来提高产品的价格。可以为客户定制价格,或者提供忠诚度折扣。
-
市场营销团队和客户关系团队可以针对消费者提供定制化优惠。
-
团队可以挽回即将流失或停止购买业务的消费者,提高他们的消费,增加粘性,并提高客户终身价值。
-
总体而言,不同的团队可以根据对消费者生成的理解来调整他们的产品。最终消费者将更加满意,更加投入,并对业务更加忠诚,从而实现更有成效的消费者互动。
因此,业务应深入挖掘消费者数据,并生成对基础的理解。客户数据可能如下节所示。
2.6.2 分析用数据集
我们以一个拥有忠诚度计划并保存客户交易详情的服装零售商为例。各种(非详尽)数据源如图 2.33 所示。

图 2.33 服装零售店的数据源
我们可以拥有商店详情,如商店 ID、商店名称、城市、区域、员工数量等。我们可以有一个项目层级表,其中包含所有项目的详细信息,如价格、类别等。然后我们可以有客户人口统计详情,如年龄、性别、城市和客户交易历史。显然,通过连接这些表,我们将能够创建一个包含所有详细信息的主表。
注意:我建议你培养良好的 SQL 技能。在几乎所有的与数据相关的领域都需要 SQL——无论是数据科学、数据工程还是数据可视化,SQL 都无处不在。
图 2.34 是主表的一个示例。这不是变量的完整列表,变量的数量可能比显示的要多得多。主表包含一些原始变量,如收入、发票等,以及一些派生变量,如平均交易价值、平均购物篮大小等。

图 2.34 主表
我们也可以举一个电信运营商的例子。在那个订阅者使用情况中,通话费率、收入、网络使用天数、数据使用等将是我们要分析的属性。因此,根据手头的业务领域,数据集将有所不同。
一旦我们有了数据集,我们通常从中创建派生属性。例如,平均交易价值属性是总收入除以发票数量。我们在已有的原始变量之外创建这样的属性。
2.6.3 建议的解决方案
对于这个问题可能有多个解决方案,其中一些我们将在以下内容中包括:
-
我们可以创建一个仪表板来展示主要的关键绩效指标。这将使我们能够分析历史数据并据此采取必要的行动。但仪表板只会显示我们已知(在一定程度上)的信息。
-
我们可以使用在前面章节中使用的某些技术进行数据分析。这将解决部分问题,而且同时考虑多个维度是困难的。
-
我们可以创建预测模型来预测客户在接下来的几个月内是否会购物,或者在未来 X 天内是否会流失,但这并不能完全解决问题。为了明确,“流失”在这里的意思是客户在接下来的 X 天内不再与零售商购物。在这里,持续时间 X 是根据业务领域定义的。例如,在电信领域,X 将小于在保险领域。这是因为人们每天都在使用手机,而在保险领域,大多数客户每年支付一次保费。因此,保险领域的客户互动较少。
-
我们可以创建客户细分解决方案,根据客户的历史趋势和属性将客户分组。这是我们用来解决这个业务问题的解决方案。
2.6.4 解决方案
回想第一章中的图 1.9,我们讨论了在机器学习算法中遵循的步骤。一切从定义业务问题开始,然后我们继续进行数据发现、预处理等。在我们的案例研究中,我们将采用类似的策略。我们已经定义了业务问题;数据发现已完成,我们已经完成了探索性数据分析和数据的预处理。要使用聚类创建分段解决方案,请遵循以下步骤:
- 我们从确定要输入聚类算法的数据集开始。我们可能已经创建了某些派生变量,处理了一些缺失值或异常值等。在案例研究中,我们想知道交易、发票、购买物品等的最大/最小/平均值。我们会对性别和年龄分布感兴趣。我们还想知道这些变量之间的相互关系,例如,女性客户是否比男性客户更倾向于使用在线模式。所有这些问题都是这一步骤的一部分。
TIP 在 GitHub 仓库中检查了 Python Jupyter 笔记本,其中提供了探索性数据分析和数据预处理的详细步骤和代码。查看它!
-
- 我们使用 k-means 聚类和层次聚类创建第一个解决方案。对于每个算法,通过改变超参数进行迭代。在案例研究中,我们将选择如访问次数、总收入、独特购买类别、在线/离线交易比率、性别、年龄等作为聚类的参数。
-
- 选择算法的最终版本及其相应的超参数。在业务理解的基础上进一步分析聚类。
-
- 更常见的是,根据观察值的大小和其中属性的性质,聚类会被合并或拆分。例如,如果总客户群为 100 万,对 100 人的聚类采取行动将非常困难。同时,管理一个 700,000 人的聚类也将同样困难。
-
- 然后我们分析我们最终拥有的聚类。检查变量的聚类分布,了解它们的区分因素,并为聚类给出逻辑名称。我们可以预期看到如图 2.35 所示的聚类输出。

图 2.35 基于响应、生命周期、参与度和消费模式等少数维度的分段。这些维度并不全面,在现实世界的业务问题中,维度的数量可能更多。
在示例聚类中,我们展示了消费模式、对先前活动的响应、生命周期和整体参与度作为几个维度。这些维度的相应子分类也显示出来。聚类将是这些维度的逻辑组合。实际的维度可能更多。
图 2.35 中展示的分割可以用于多个领域和企业。参数和属性可能会变化,业务背景可能不同,可用的数据量可能有所不同——但整体方法保持相似。
除了上一节中我们看到的应用之外,让我们在这里考察一些其他用例:
-
市场研究利用聚类将消费者群体分割成市场细分;然后可以更好地分析这些群体的偏好。产品定位可以改进,定价可以更紧密,地理选择将更加科学。
-
在生物信息学和医疗行业,聚类算法可以用来将基因分组到不同的类别中。通过分析这些基因组的属性,可以对基因组进行分割,并评估比较。
-
在我们使用监督学习解决方案创建算法之前,聚类被用作一个有效的数据预处理步骤。它还可以通过关注属于某个聚类的数据点来减少数据量。
-
聚类被用于在结构化和非结构化数据集中进行模式检测。我们已经研究了结构化数据集的案例。对于文本数据,它可以用来将类似类型的文档、期刊、新闻等进行分组。我们还可以使用聚类来处理和开发图像解决方案。我们将在后面的章节中研究文本和图像的无监督学习解决方案。
-
由于算法基于相似度测量,聚类可以用来将传入的数据集分割为欺诈或真实,这可以用来减少犯罪活动的数量。
聚类算法的应用场景非常多样。我们在这里只讨论了其中一些突出的例子。聚类算法是那些能够改变工作方法并围绕数据产生大量洞察力的算法之一。它在电信、零售、银行、金融服务和保险、航空以及其他领域得到广泛应用。
同时,算法也存在一些问题。接下来,我们将考察我们在聚类中面临的常见问题。
2.7 聚类中常见的挑战
聚类并不是一个完全没有挑战的简单解决方案。像世界上任何其他解决方案一样,聚类也有其问题。我们在聚类中面临的最常见挑战如下:
-
数据量过大——有时数据的规模相当大,有很多维度可用。在这种情况下,管理数据集变得困难。计算能力可能有限,而且像任何项目一样,时间有限。为了克服这个问题,我们可以
-
尝试通过使用基于监督学习的回归方法或决策树算法等,找到最显著的变量来减少维度数量。
-
通过使用主成分分析或奇异值分解等方法来减少维度数量。
-
-
噪声数据集——“垃圾输入,垃圾输出”这个陈词滥调对于聚类也是适用的。如果数据集很乱,就会产生很多问题。这些问题可能包括
-
缺失值(即 NULL、NA、?、空白等)。
-
数据集中的异常值。
-
数据集中存在的垃圾值,如#€¶§^等。
-
数据中的错误条目。例如,如果在收入字段中输入了一个名称,那么它就是一个错误的条目。
-
我们将在后面的章节中讨论解决这些问题的步骤和过程。在本章中,我们正在探讨如何处理分类变量。
- 分类变量——在讨论时,回想一下 k-means 无法使用分类变量的问题。我们将在下一节解决这个问题。
为了将分类变量转换为数值变量,我们可以使用独热编码。这种技术会添加额外的列,数量等于不同类别的数量,如下面的图所示。变量 city 有独特的值,如伦敦和新德里。我们可以观察到,已经创建了两个额外的列,用 0 或 1 填充了值(见图 2.36)。

图 2.36 使用独热编码将分类变量转换为数值变量
使用独热编码并不总是能确保有效的解决方案。想象一下,如果这个例子中的城市数量是 100,那么数据集中将会有 100 个额外的列,其中大部分值将被填充为 0。因此,在这种情况下,建议将一些值分组。
-
距离度量——使用不同的距离度量,我们可能会得到不同的结果。尽管没有“一刀切”的方法,但欧几里得距离通常用于测量距离。
-
主观解释——对于聚类的解释相当主观。通过使用不同的属性,可以对相同的数据集进行完全不同的聚类。如前所述,重点应放在解决当前的业务问题上。这是选择超参数和最终算法的关键。
-
耗时——由于同时处理了很多维度,有时算法收敛需要花费很多时间。
尽管存在所有这些挑战,聚类仍然是一种被广泛认可和使用的技巧。
2.8 总结思考
无监督学习不是一个容易的任务。但它确实是一个非常吸引人的任务。它不需要任何目标变量,解决方案会自己识别模式,这是无监督学习算法最大的优点之一。并且这些解决方案已经在商业世界中产生了巨大的影响。在本章中,我们研究了这些解决方案类别中的一个,称为聚类。
聚类是一种无监督学习解决方案,它对于模式识别、探索性分析和当然,数据点的分割都是非常有用的。组织机构大量使用聚类算法,并继续深入理解消费者数据。可以提供更好的价格,提出更相关的优惠,提高消费者参与度,并使整体客户体验变得更好。毕竟,让消费者满意是任何企业的目标。聚类不仅可以用于结构化数据,还可以用于文本数据、图像、视频和音频。由于其能够在多个数据集和大量维度中找到模式的能力,聚类是我们想要一起分析多个维度时的首选解决方案。
在本书的第二章中,我们介绍了基于无监督的聚类方法的概念。我们检查了不同类型的聚类算法——k-means 聚类、层次聚类和 DBSCAN 聚类——以及它们的数学概念、相应的用例和优缺点,并着重于为这些数据集创建实际的 Python 代码。
在下一章中,我们将研究降维技术,如主成分分析和奇异值分解。我们将讨论技术的基础构件、它们的数学基础、优缺点以及用例,并执行实际的 Python 实现。
2.9 实践下一步行动和建议阅读
以下提供了一些下一步行动的建议和一些有用的阅读材料:
-
从
mng.bz/dXqo获取在线零售数据。这个数据集包含了 2010 年 1 月 12 日至 2011 年 9 月 12 日之间一个英国零售商的所有在线交易。应用章节中描述的三个算法来识别公司应该针对哪些客户以及为什么。 -
从
www.kaggle.com/uciml/iris获取 IRIS 数据集。它包括三种鸢尾花物种,每个物种有 50 个样本,每个样本都有一些花的属性。使用 k-means 和 DBSCAN 算法,并比较结果。 -
在 UCI 探索聚类数据集
archive.ics.uci.edu/ml/index.php。 -
研究以下关于 k-means 聚类、层次聚类和 DBSCAN 聚类的论文:
摘要
-
聚类在所有行业中都有多种用途,例如零售、电信、金融和制药。聚类解决方案被用于客户和市场营销细分,以更好地理解客户基础,这进一步提高了定位。
-
聚类将具有相似属性的物体分组到段中,有助于数据理解和模式发现,而无需目标变量。
-
通过聚类,我们可以在数据集中找到潜在的规律,并识别数据的自然分组。
-
根据方法论,可以有多种聚类技术。一些例子包括 k-means 聚类、层次聚类、DBSCAN 和模糊聚类。
-
不同的聚类算法(k-means、层次、DBSCAN)提供不同的优缺点,每种算法都适用于不同的数据特征和目的。
-
聚类分为硬聚类,其中对象属于单个聚类,和软聚类,其中对象可以属于多个聚类。
-
不同的聚类属性和技术,如基于质心的、基于密度的和分布模型,会导致不同的聚类结果。
-
有效的聚类算法能够生成可理解、可扩展和独立的聚类,通过最小领域输入处理异常值和多种数据类型。
-
聚类的距离度量包括欧几里得、切比雪夫、曼哈顿和余弦距离。
-
基于质心的聚类通过到聚类质心的距离来衡量相似性。
-
K-means 聚类通过指定聚类数量,k,以及迭代地将数据点分配到最近的中心来创建非重叠的聚类。
-
肘部方法是一种常用的技术,用于确定 k-means 聚类中的最佳聚类数量。
-
K-means 聚类基于聚类的质心。
-
层次聚类基于连通性创建聚类,不需要预定义的聚类数量。
-
层次聚类可以是聚类的(自下而上)或分级的(自上而下),并使用链接标准来衡量距离。
-
DBSCAN 根据点密度识别聚类,并有效地区分异常值。
-
DBSCAN 不需要指定聚类数量,适用于不规则形状的聚类。
-
测量聚类精度涉及 WCSS、簇间平方和、轮廓值和 Dunn 指数等指标。
第三章:. 降维
本章涵盖
-
维度诅咒及其不利之处
-
减少维度的各种方法
-
主成分分析
-
奇异值分解
-
Python 解决主成分分析和奇异值分解的方法
-
关于降维的案例研究
知识是堆积事实的过程;智慧在于它们的简化。——马丁·H·费舍尔
在生活中,我们面临复杂的情况。生活向我们抛出多种选择,我们从中选择几个可行的。这个筛选决策基于每个选项的重要性、可行性、效用和预期的利润。符合条件的选择被选中。一个完美的例子就是选择你的度假目的地。基于天气、旅行时间、安全性、食物、预算和其他几个选项,我们选择几个我们愿意在那里度过下一个假期的地方。在本章中,我们研究的是如何减少选项的数量——尽管是在数据科学和机器学习的世界中。
在上一章中,我们介绍了主要的聚类算法,并进行了案例研究。我们在这些现实世界例子中生成和使用的数据集有很多变量。有时,数据中可能有超过 100 个变量或维度。但并非所有这些变量都是重要的。数据集中有很多维度被称为维度诅咒。为了进行任何进一步的分析,我们从所有维度或变量的列表中选择一些。在本章中,我们研究降维的需求、各种降维技术以及相应的优缺点。我们将更深入地探讨主成分分析(PCA)和奇异值分解(SVD)的概念及其数学基础,并用 Python 实现来补充。此外,继续我们上一章的结构,我们将研究电信行业的实际案例研究。还有其他高级的降维技术,如 t 分布随机邻域嵌入(t-SNE)和线性判别分析(LDA),我们将在后面的章节中探讨。
聚类和降维是无监督学习的主要类别。我们在上一章研究了主要的聚类方法,在本章中我们将讨论降维。有了这两种解决方案,我们在无监督学习领域覆盖了大量的内容。但还有更多高级主题需要探讨,这些主题是本书后几章的内容。
让我们先了解“维度诅咒”的含义。
3.1 技术工具包
我们在本章中使用的 Python 版本与上一章相同。本章也将使用 Jupyter Notebook。
所有数据集和代码文件都可以在 GitHub 仓库中找到(mng.bz/ZlBR)。您需要安装以下 Python 库来执行代码:numpy、pandas、matplotlib、scipy和sklearn。由于您在上一个章节中已经使用了相同的包,因此您不需要再次安装它们。CPU 足以执行,但如果您遇到一些计算问题,请切换到 GPU 或 Google Colab。如果您在安装这些包的任何包时遇到问题,请参阅附录。
3.2 维度诅咒
让我们继续之前介绍的度假目的地示例。目的地的选择取决于几个参数:安全性、可用性、食物、夜生活、天气、预算、健康等等。参数太多会令人困惑。让我们通过一个现实生活中的例子来理解。
考虑这种情况:一个零售商希望在市场上推出一系列新鞋,为此,需要选择一个目标客户群。这个目标客户群将通过电子邮件、短信、新闻通讯等方式接触到。业务目标是吸引这些客户购买新推出的鞋子。从整个客户群中,可以根据客户年龄、性别、预算、偏好类别、平均消费、购物频率等变量选择目标客户群。这些许多变量或维度使得基于可靠的数据分析技术筛选客户变得困难。我们将同时分析太多参数,检查每个参数对客户购物概率的影响,因此这项任务变得既繁琐又令人困惑。这就是我们在现实世界的数据科学项目中面临的维度诅咒问题。我们还会在另一种情况下遇到维度诅咒,即观察数少于变量数。考虑一个观察数为X而变量数多于X的数据集——在这种情况下,我们面临维度诅咒。
理解任何数据集的一个简单方法是可视化。让我们在向量空间图中可视化一个数据集。如果我们只有一个属性或特征在数据集中,我们可以在一维中表示它(见图 3.1 左图)。例如,我们可能只想用一个维度来捕捉一个物体的高度。如果我们有两个属性,我们需要两个维度,如图 3.1 中间的图所示,要得到一个物体的面积,我们需要长度和宽度。如果我们有三个属性,例如,为了计算体积,这需要长度、宽度和高度,我们需要一个三维空间,如图 3.1 右边的图所示。这个需求将根据属性的数量继续增长。

图 3.1 只需要一个维度来表示数据点——例如,表示一个物体的高度(左)。我们需要两个维度来表示一个数据点。每个数据点可以对应于物体的长度和宽度,这可以用来计算面积(中间)。需要三个维度来表示一个点(右)。在这里,可以是长度、宽度和高度,这些是计算物体体积所需的。这个过程基于数据中存在的维度数量继续进行。
考虑一个数据集,其中你有一个数据点的属性——例如,性别。然后我们添加年龄,然后是教育、地址等。为了表示这些属性,维度的数量将持续增加。因此,我们可以很容易地得出结论,随着维度数量的增加,表示所需的空间量会大幅增加。这被称为维度诅咒。这个术语是由理查德·E·贝尔曼提出的,用来指代数据集中变量过多的问题——其中一些变量很重要,而许多其他变量可能不太重要。
另有一个著名的理论称为哈格里斯现象,如图 3.2 所示。通常,在数据科学和机器学习中,我们希望有尽可能多的变量来训练我们的模型。监督学习分类算法的性能将增加到一定极限,并随着最优变量数量的增加而达到峰值。但是,使用相同数量的训练数据和增加的维度,监督分类算法的性能会下降。换句话说,如果变量没有对解决方案的准确性做出贡献,那么在数据集中拥有这些变量是不明智的。我们应该从数据集中移除这些变量。

图 3.2 哈格里斯现象表明,随着维度数量的增加,机器学习模型的性能最初会提高。但进一步的增加会导致模型性能下降。
维度数量的增加对机器学习模型有以下影响:
-
当模型处理越来越多的变量时,数学复杂性会增加。例如,在上一章中我们讨论的 k-means 聚类方法中,当我们有更多的变量时,各点之间的距离计算将变得复杂。因此,整体模型变得更加复杂。
-
在更大维度空间中生成的数据集可能比变量数量较少的数据集稀疏得多。数据集将更加稀疏,因为一些变量将具有缺失值、NULL 等。因此,空间更加空旷,数据集密度更低,与它们关联的变量数量更少。
-
随着模型复杂性的增加,所需的处理时间也会增加。系统感受到处理这么多维度的压力。
-
整体解决方案在理解和执行上变得更加复杂。回想一下第一章,我们讨论了监督学习算法。由于维度数量高,我们可能会在监督学习模型中遇到过拟合的问题。
定义:当监督学习模型在训练数据上具有良好的准确率但在未见数据上准确率较低时,这被称为过拟合。过拟合是一个麻烦,因为机器学习模型的根本目的是在未见数据集上表现良好,而过拟合则违背了这一目的。
让我们用一个现实世界的例子来关联这些内容。考虑一家提供不同类型保险政策的保险公司,如人寿保险、车辆保险、健康保险、房屋保险等。该公司希望使用数据科学和执行聚类用例来扩大客户群和销售的总政策数。他们有客户详细信息,如年龄、性别、职业、保额、历史交易、持有的政策数量、年收入、政策类型、历史违约次数等。同时,让我们假设客户是否为左撇子或右撇子、他们是否穿黑色或棕色鞋子、他们使用的洗发水品牌、头发的颜色以及他们最喜欢的餐厅等信息也被记录。如果我们把所有变量都包含在数据集中,最终数据集中的变量总数将会相当高。对于 k-means 聚类算法,距离计算将更加复杂,处理时间将增加,整体解决方案也将相当复杂。
还必须注意的是,并非所有维度或变量都是重要的。因此,从所有变量中筛选出重要的变量至关重要。记住,自然界总是偏好更简单的解决方案!在之前讨论的案例中,变量如发色和最喜欢的餐厅等,很可能不会影响输出。因此,减少维度以简化复杂性和减少计算时间是我们的最佳利益。同时,还必须注意的是,维度降低并不总是期望的。它取决于我们希望解决的类型数据集和业务问题。我们将在本章后续部分的案例研究中进一步探讨这一点。
练习 3.1
回答以下问题以检查你的理解:
-
维度诅咒指的是拥有大量数据。对还是错?
-
变量数量高总是会增加解决方案的准确率。对还是错?
-
数据集中大量变量是如何影响模型的?
我们已经确定拥有很多维度对我们来说是一个挑战。接下来,我们将探讨各种降低维度数量的方法。
3.3 维度降低方法
在上一节中,我们研究了具有高维数据的缺点。更少的维度可能会导致我们的数据结构更简单,这将具有计算效率。同时,我们在减少变量数量时应该小心。降维方法的输出应该足够完整,以表示原始数据,并且不应导致任何信息丢失。换句话说,如果我们最初有,例如,500 个变量,我们将它们减少到 120 个重要的变量,那么这 120 个变量应该足够稳健,以捕捉几乎所有的信息。让我们用一个简单的例子来理解。
考虑这种情况:我们希望预测一个城市下个月将接收到的降雨量。该城市的降雨预测可能依赖于一段时间内的温度、风速测量、气压、距离海洋的距离、海拔高度等因素。如果我们希望预测降雨量,这些变量是有意义的。同时,像城市中电影院的数量、该城市是否是国家的首都,或者城市中红色汽车的数量等因素不会影响降雨量的预测。在这种情况下,如果我们不使用城市中电影院的数量来预测降雨量,这不会降低系统的能力。很可能会,解决方案仍然能够表现得相当好。因此,在这种情况下,删除这样的变量不会丢失任何信息,我们当然可以从数据集中删除它。另一方面,删除诸如温度或距离海洋等因素可能会非常有可能地负面影响预测精度。这是一个简单的例子,强调了减少变量数量的必要性。
维度或变量的数量可以通过手动和基于算法的方法的组合来减少。但在详细研究它们之前,有一些数学术语和组成部分我们应该了解,我们将在下一节中讨论。
3.3.1 数学基础
要对降维方法有一个彻底的理解,必须知道很多数学术语。我们试图减少数据集的维度。数据集不过是一个值矩阵——因此,许多概念都与矩阵操作方法、它们的几何表示以及在这些矩阵上执行变换有关。数学概念在附录中讨论。您还需要了解特征值和特征向量。这些概念将在整本书中重复使用;它们被放在附录中以便快速参考。建议您在继续之前阅读它们。
3.4 手动降维方法
为了解决维度灾难,我们希望减少数据集中的变量数量。这种减少可以通过从数据集中移除变量来实现。或者,一个非常简单的降维解决方案是将可以逻辑分组或可以使用共同数学运算表示的变量组合起来。
例如,如图 3.3 所示,数据可以来自一家零售店,不同客户产生了不同的交易。我们将得到在一定时期内每个客户的销售额、发票数量和购买的项目数量。在表中,客户 1 产生了两个发票,总共购买了五个项目,并产生了 100 的总销售额。

图 3.3 在第一张表中,我们有销售、发票和项目数量作为变量。在第二张表中,它们已经被组合成新的变量。
如果我们希望减少变量的数量,我们可能会将三个变量合并成两个变量。在这里,我们引入了平均交易价值(ATV)和平均购物篮大小(ABS)这两个变量,其中 ATV = 销售额/发票数,ABS = 项目数量/发票数。
因此,在客户 1 的第二张表中,我们有 ATV 为 50 和 ABS 为 2.5。因此,变量的数量已从三个减少到两个。这里的过程只是说明我们可以如何组合各种变量的一个例子。这并不意味着我们应该用 ATV 代替销售作为变量。
这个过程可以继续减少变量的数量。同样,对于一个电信用户,比如说我们有一个月内 30 天内手机通话的分钟数。我们可以将它们加起来创建一个单一变量——一个月内使用的分钟数。这些例子是非常基础的,作为开始。使用手动过程,我们可以采用两种其他常用的方法:手动选择和使用相关系数。
3.4.1 手动特征选择
继续讨论上一节中提到的降雨预测示例,数据科学家可能能够删除一些变量。这将是基于对当前业务问题的深入理解和所使用的相应数据集。然而,一个基本的假设是数据集对数据科学家来说是相当可理解的,并且他们很好地理解了业务领域。大多数时候,业务利益相关者将能够指导这些方法。变量也必须是唯一的,并且不应存在太多的依赖性。如图 3.4 所示,我们可以移除一些可能对预测降雨不有用的变量。

图 3.4 在第一张表中,我们有数据集中所有的变量。使用业务逻辑,第二张表中已经丢弃了一些可能不太有用的变量。但这是需要谨慎进行的;最好的方式是获得业务利益相关者的指导。
有时,特征选择方法也被称为包装方法。在这里,机器学习模型被包裹或拟合到变量子集。在每次迭代中,我们将得到一组不同的结果。产生最佳结果的那组被选为最终模型。
3.4.2 相关系数
两个变量之间的相关性简单地说就是它们彼此之间有相互关系。一个变量的值的变化将影响另一个变量的值,这意味着在一个变量中具有相似值的点在另一个变量中也有相似的值。彼此高度相关的变量提供相似的信息,因此可以删除其中一个。
注意:相关性在附录中有详细描述。
例如,对于一家零售店,一天内生成的发票数量将与产生的销售额高度相关,因此可以删除其中一个。另一个例子是,学习时间更长的大学生将比学习时间较短的学生(大多数情况下)获得更好的成绩。
但我们在删除变量时应该小心,不要仅仅依赖相关性。在做出任何决定之前,应该彻底了解变量的业务背景。
注意:在删除任何变量之前与业务利益相关者讨论是个好主意。
基于相关性的方法有时被称为过滤方法。使用相关系数,我们可以过滤和选择最显著的变量。
练习 3.2
回答以下问题以检查你的理解:
-
如果我们认为某个变量不是必需的,我们可以简单地删除它。对还是错?
-
如果两个变量相关,总是删除其中一个。对还是错?
手动方法是更简单的解决方案,可以相当高效地执行。数据集的大小减少了,我们可以继续分析。但是,手动方法有时是主观的,并且很大程度上取决于手头的业务问题。很多时候,也不可能使用手动方法进行降维。在这种情况下,我们有基于算法的方法,我们将在下一节中研究。
3.4.3 降低维度的基于算法的方法
在上一节中,我们探讨了手动方法。在此基础上,本节我们将探讨基于算法的方法。基于算法的技术建立在更数学的基础上,因此证明是更科学的方法。在现实世界的商业问题中,我们使用手动和基于算法技术的组合。与基于算法的技术相比,手动方法执行起来更为直接。此外,由于它们基于不同的基础,我们无法对这两种技术的比较做出评论。但是,同时,在实施基于算法的技术时,进行充分的尽职调查是至关重要的。
降维中使用的的主要技术如下。我们将在本书中探讨其中的一些:
-
PCA
-
SVD
-
LDA
-
广义判别分析
-
非负矩阵分解
-
多维尺度
-
局部线性嵌入
-
IsoMaps
-
自动编码器
-
t-SNE
这些技术被用于一个共同的目标:将数据从高维空间转换到低维空间。其中一些数据转换是线性的,而另一些是非线性的。
我们在本章中详细讨论 PCA 和 SVD。在本书的后续章节中,将探索其他主要技术。PCA 可能是被引用最多的降维方法,将在下一节中进行探讨。
3.5 主成分分析
考虑这种情况:你正在处理一个包含 250 个变量的数据集。几乎不可能可视化这样一个高维空间。其中一些变量可能相互关联,而另一些可能不相关,并且需要在不丢失太多信息的情况下减少变量的数量。PCA 使我们能够从数学上选择最重要的特征,并保留其余部分。PCA 确实减少了维度数量,但同时也保留了变量之间以及数据集中重要结构之间最重要的关系。因此,变量的数量减少了,但数据集中的重要信息得到了安全保留。
PCA 是将高维数据投影到低维空间的过程。用更简单的话说,我们正在将一个n-维空间减少到一个m-维空间,其中n > m,同时保持原始数据集的性质和本质。在这个过程中,旧变量被减少到新的变量,同时保持原始数据集的核心。因此创建的新变量被称为主成分。主成分是原始变量的线性组合。这种变换的结果是,第一个主成分捕捉到数据集中的最大随机性或最高方差。创建的第二个主成分与第一个成分正交。
注意:如果两条直线相互垂直,这意味着它们之间的角度是 90˚。
该过程继续到第三成分,依此类推。正交性使我们能够保持后续主成分之间没有相关性。
注意:PCA 利用数据集的线性变换,这类方法有时被称为特征投影。得到的或投影的数据集用于进一步分析。
让我们用一个例子更好地理解这一点。在图 3.5 中,我们使用一些变量表示了房屋的总感知价值。这些变量包括面积(平方米)、卧室数量、阳台数量、机场距离、火车站距离等;我们有 100 多个变量。

图 3.5 估计房价的变量
我们可以在数学和逻辑上组合一些变量。主成分分析(PCA)将创建一个新变量,它是某些变量的线性组合,如下面的示例所示。它将得到原始变量的最佳线性组合,以便新变量能够捕捉到数据集的最大方差。方程式 3.1 仅是一个示例,用于说明目的,其中我们展示了由其他变量的组合创建的新变量。
(3.1)
new_variable = *a面积 – *b卧室数 + *c距离 – *d学校数
现在我们从视觉上理解这个概念。在向量空间图中,我们可以表示数据集,如图 3.6 所示。左图表示原始数据,其中我们可以在 x-y 图中可视化变量。如前所述,我们希望创建变量的线性组合。换句话说,我们希望创建一个数学方程,该方程能够解释 x 和 y 之间的关系。

图 3.6 数据集可以用向量空间图表示(左)。直线可以称为最佳拟合线,因为它包含了所有数据点的投影(中间)。实际值与投影之间的差异是误差项(右)。
此过程的结果将是一条直线,如图 3.6 中间图所示。这条直线有时被称为最佳拟合线。使用这条最佳拟合线,我们可以预测给定 x 值的 y 值。这些预测不过是数据点在直线上的投影。
实际值与投影之间的差异是误差,如图 3.6 右图所示。这些误差的总和称为总投影误差。
对于这条直线,可能有多种选择,如图 3.7 所示。这些不同的直线将具有不同的误差和不同的方差值。

图 3.7 数据集可以通过几条直线来捕捉,但并非所有直线都能捕捉到最大方差。给出最小误差的方程将被选中。
能够捕捉最大方差的直线将被选中。换句话说,它给出了最小的误差。它将是第一个主成分,最大扩散的方向将是主轴。
第二个主成分将以类似的方式推导出来。由于我们知道第一个主成分轴,我们可以从这个主成分轴的方差中减去总方差,以得到剩余方差。换句话说,使用第一个主成分,我们会捕捉到数据集中的一些方差。但数据集中的总方差中仍有一部分未被第一个主成分解释。未被解释的总方差部分是剩余方差。使用第二个主成分,我们希望尽可能多地捕捉到方差。
使用相同的过程来捕捉最大方差的方向,我们将得到第二个主成分。第二个主成分可以相对于第一个主成分以几个不同的角度存在,如图 3.8 所示。数学上已经证明,如果第二个主成分与第一个主成分正交(即 90˚),这将使我们能够通过两个主成分捕捉到最大方差。在图 3.8 中,我们可以观察到两个主成分之间的角度是 90˚。

图 3.8 左侧的第一个图是第一个主成分。第二个主成分可以相对于第一个主成分以不同的角度存在(中间)。我们应该找到第二个主成分,使我们能够捕捉到最大方差。为了捕捉最大方差,第二个主成分应该与第一个主成分正交,从而使得捕捉到的总方差最大化(右侧)。
该过程继续应用于第三和第四主成分,依此类推。随着主成分数量的增加,向量空间中的表示变得难以可视化。你可以想象一个具有三个以上轴的向量空间图。一旦所有主成分都被推导出来,数据集就被投影到这些轴上。这个转换后的数据集的列是主成分。创建的主成分数量将少于原始变量的数量,并将捕捉到数据集中存在的最大信息。
在我们深入探讨 PCA 的过程之前,让我们研究其重要特性:
-
PCA 旨在减少结果数据集的维度数。
-
PCA 产生主成分,旨在通过最大化特征方差来减少数据集中的噪声。
-
同时,主成分减少了数据集中的冗余。这是通过最小化特征对之间的协方差来实现的。
-
原始变量不再存在于新创建的数据集中。相反,使用这些变量创建了新变量。
-
主成分不一定与数据集中所有变量一一对应。它们是现有变量的新组合。因此,它们可以是一个主成分中几个不同变量的组合(如方程 3.1 所示)。
-
从数据集中创建的新特征不共享相同的列名。
-
原始变量可能彼此相关,但新创建的变量彼此无关。
-
新创建的变量数量少于原始变量的数量。选择主成分数量的过程已在 3.5.2 节中描述。毕竟,这就是降维的全部目的。
-
如果 PCA 已用于减少训练数据集中的变量数量,则应使用 PCA 减少测试/验证数据集。
-
主成分分析(PCA)不仅仅等同于降维。它还可以用于降维以外的多种用途,如特征提取、数据可视化、多重共线性检测、预处理等。仅将 PCA 用于降维肯定是一个误称。
现在我们将检查实现 PCA 时使用的方法,然后我们将开发一个使用 PCA 的 Python 解决方案。在我们编写代码时,不需要应用所有步骤,因为重头戏已经被包和库完成了。这里给出的步骤由包处理,但仍然,理解这些步骤对于正确欣赏 PCA 的工作原理至关重要:
-
在 PCA 中,我们首先将数据集标准化作为第一步。这确保了所有变量都有共同的表现形式并变得可比较。Python 中有执行标准化的方法,我们将在编写代码时学习。要了解更多关于数据集标准化的信息,请参阅附录。
-
在标准化后的数据集中获取协方差。这使我们能够研究变量之间的关系。我们通常创建一个协方差矩阵,如下一节中的 Python 示例所示。
-
我们可以计算协方差矩阵的特征向量和特征值。特征向量的数学概念在附录中有介绍。
-
我们然后按特征值降序排列特征值。我们选择与特征值最大值对应的特征向量。所选的成分将能够捕获数据集中的最大方差。还有其他方法来筛选主成分,我们将在开发 Python 代码时探讨。
练习 3.3
回答这些问题以检查你的理解:
-
PCA 将导致数据集中的变量数量相同。对或错?
-
PCA 将能够捕获数据集中的 100%信息。对或错?
-
PCA 中主成分选择的逻辑是什么?
因此,本质上,主成分是原始变量的线性组合。在这个线性组合中的权重是满足最小二乘法误差标准的特征向量。
3.5.1 特征值分解
在 PCA 的上下文中,特征向量将代表向量的方向,特征值将是沿着该特征向量捕获的方差。参见图 3.9,其中我们将原始 n x n 矩阵分解成组件。

图 3.9 使用特征值分解,原始矩阵可以被分解为一个特征向量矩阵、一个特征值矩阵和一个特征向量的逆矩阵。我们使用这种方法实现 PCA。
从数学上,我们可以用方程 3.2 展示其关系
(3.2)
Av = λv
其中 A 是一个方阵,v 是特征向量,λ 是特征值。在这里,重要的是要注意特征向量矩阵是正交矩阵,其列是特征向量。特征值矩阵是对角矩阵,其对角元素是特征值。最后一个组件是特征向量矩阵的逆。一旦我们有了特征值和特征向量,我们可以选择重要的特征向量来获取主成分。
在本书中,我们将 PCA 和 SVD 作为两种独立的方法进行介绍。这两种方法都用于将高维数据降低到低维数据,并在过程中保留数据集中的最大信息。两者的区别在于 SVD 适用于任何类型的矩阵(矩形或方形),而特征值分解仅适用于方形矩阵。在我们后面章节中介绍了 SVD 之后,你会更好地理解这一点。
3.5.2 使用 PCA 的 Python 解决方案
我们已经研究了 PCA 的概念和特征值分解的过程。现在是时候深入 Python,在数据集上开发 PCA 解决方案了。我将向你展示如何在数据集上创建特征向量和特征值。为了实现 PCA 算法,我们将使用sklearn库。库和包提供了实现算法的更快解决方案。
我们使用 Iris 数据集来解决这个问题。这是机器学习问题中最常用的数据集之一。该数据集包含三种鸢尾花物种的数据,每种物种有 50 个样本,并具有每个花朵的属性,如花瓣长度、萼片长度等。问题的目标是使用花朵的属性来预测物种。因此,独立变量是花朵属性,而“物种”变量是目标变量。数据集和代码已存入 GitHub 仓库。在这里,我们使用内置的 PCA 函数,这减少了实现 PCA 所需的工作量。步骤如下:
- 加载所有必要的库。我们将使用
numpy、pandas、seaborn、matplotlib和sklearn。请注意,我们已经从sklearn中导入了 PCA。
备注:以下是一些标准库。你会发现几乎所有的机器学习解决方案都会在解决方案笔记本中导入这些库:
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
-
- 现在加载数据集。它是一个.csv 文件:
iris_df = pd.read_csv('IRIS.csv')
-
- 我们现在将对数据集进行基本检查,查看前五行,数据的形状,变量的分布等。我们在这里不进行广泛的数据探索分析,因为这些步骤已在第二章中介绍。该数据集有 150 行和 6 列(见图 3.10)。
iris_df.head()

iris_df.describe()
iris_df.shape

图 3.10 代码输出
-
- 在这里,我们应该将数据集分为独立变量和目标变量。
X_variables在这里代表独立变量,它们位于数据集的 2-5 列,而y_variable是目标变量,在这种情况下是“物种”,它是数据集的最后一列。回想一下,我们希望使用其他属性来预测花的物种。因此,我们已经将目标变量“物种”和其他独立变量分开:
- 在这里,我们应该将数据集分为独立变量和目标变量。
X_variables = iris_df.iloc[:,1:5]
X_variables
y_variable = iris_df.iloc[:,5]
-
- 规范化数据集。
StandardScalar()的内置方法非常容易地完成了这项工作。
- 规范化数据集。
备注:StandardScalar()方法为我们规范化了数据集。它从变量中减去平均值,然后除以标准差。有关规范化的更多详细信息,请参阅附录。
我们调用该方法,然后将其应用于我们的数据集以获取转换后的数据集。由于我们正在处理独立变量,所以我们在这里使用X_variables。首先,我们调用StandardScalar()方法。然后我们使用fit_transform方法。fit_transform方法首先将转换器拟合到X和Y,然后返回X的转换版本:
sc = StandardScaler()
transformed_df = sc.fit_transform(X_variables)
-
- 计算协方差矩阵并打印出来。输出显示在图 3.11 中。使用
numpy获取协方差矩阵非常直接:
- 计算协方差矩阵并打印出来。输出显示在图 3.11 中。使用
covariance_matrix = np.cov(transformed_df.T)
covariance_matrix

图 3.11 协方差矩阵
-
- 计算特征值。在
numpy库中,我们有内置的功能来计算特征值。然后我们将特征值按降序排序。为了筛选主成分,我们可以选择大于 1 的特征值。这个标准被称为凯撒标准。我们也在探索其他方法。
- 计算特征值。在
备注:特征值表示一个成分作为数据摘要的好坏。如果特征值为 1,这意味着该成分包含与单个变量相同数量的信息;因此,我们选择大于 1 的特征值。
在此代码中,首先我们获取eigen_values和eigen_vectors,然后按降序排列(见图 3.12):
eigen_values, eigen_vectors = np.linalg.eig(covariance_matrix)
eigen_pairs = [(np.abs(eigen_values[i]), eigen_vectors[:,i]) for i in range(len(eigen_values))]
print('Eigenvalues arranged in descending order:')
for i in eigen_pairs:
print(i[0])

图 3.12 按降序排列的特征值
-
- 从
sklearn库中调用 PCA 方法。该方法用于此处拟合数据。注意,我们尚未确定在这个问题中希望使用的主成分数量:
- 从
pca = PCA()
pca = pca.fit(transformed_df)
-
- 现在主成分已经设置好了。让我们看看它们所解释的方差。我们可以观察到第一个成分捕捉到了 72.77%的变异,第二个成分捕捉到了 23.03%的变异,等等(见图 3.13):
explained_variance = pca.explained_variance_ratio_
explained_variance

图 3.13 主成分的方差程度
-
- 我们现在将分量绘制成条形图以获得更好的可视化(见图 3.14):
dataframe = pd.DataFrame({'var':pca.explained_variance_ratio_,
'PC':['PC1','PC2','PC3','PC4']})
sns.barplot(x='PC',y="var",
data=dataframe, color="b");

图 3.14 主成分条形图
-
- 在这里,我们绘制一个斯克里普图来可视化主成分所解释的累积方差(见图 3.15):
plt.plot(np.cumsum(pca.explained_variance_ratio_))
plt.xlabel('number of components')
plt.ylabel('cumulative explained variance')
plt.show()

图 3.15 累积方差斯克里普图
-
- 在这个案例研究中,我们选择前两个主成分作为最终解决方案,因为这两个成分捕捉到了数据集中 95.08%的总方差:
pca_2 = PCA(n_components =2 )
pca_2 = pca_2.fit(transformed_df)
pca_2d = pca_2.transform(X_variables)
-
- 我们现在将数据集绘制在两个主成分上。为此,物种必须回溯到物种变量的实际值,即
Iris-setosa、Iris-versicolor和Iris-virginica。在这里,0映射到Iris-setosa,1是Iris-versicolor,2是Iris-virginica。在下面的代码中,首先使用前面讨论的映射替换物种变量的值:
- 我们现在将数据集绘制在两个主成分上。为此,物种必须回溯到物种变量的实际值,即
iris_df['Species'] = iris_df['Species'].replace({'Iris-setosa':0, 'Iris-
versicolor':1, 'Iris-virginica':2})
-
- 我们现在将结果绘制在两个主成分上。该图显示了将数据集减少到我们刚刚创建的两个主成分。这些主成分可以捕捉到数据集 95.08%的方差。第一个主成分代表图中的 x 轴,而第二个主成分代表图中的 y 轴(见图 3.16)。颜色代表物种的不同类别。书籍的印刷版不会显示不同的颜色,但 Python 代码的输出会显示。相同的输出也可以在 GitHub 仓库中找到:
plt.figure(figsize=(8,6))
plt.scatter(pca_2d[:,0], pca_2d[:,1],c=iris_df['Species'])
plt.show()

图 3.16 两个主成分的结果
此解决方案已将成分数量从四个减少到两个,同时仍能保留大部分信息。在这里,我们检查了基于凯撒标准、捕获的方差和斯克里普图的三种选择主成分的方法。
让我们快速分析一下我们使用 PCA 所取得的成果。图 3.17 显示了同一数据集的两个表示。左边是 X_variables 的原始数据集。它有四个变量和 150 行。右边是 PCA 的输出。它有 150 行,但只有两个变量。回想一下,我们已经将维度数量从四个减少到两个。因此,观测数保持 150 个,而变量数量从四个减少到两个。

图 3.17 左侧的图显示了原始数据集,它有 150 行和四个变量。在右侧实施 PCA 之后,变量的数量已减少到两个。行数保持不变,为 150,这由 pca_2d 的长度表示。
一旦我们减少了成分的数量,我们就可以继续实施监督学习或无监督学习解决方案。我们可以为任何其他旨在减少维度数的目标现实世界问题实施前面的解决方案。我们将在第 3.8 节中进一步探讨这一点。
通过这种方式,我们已经涵盖了主成分分析(PCA)。GitHub 仓库包含一个非常有意思的带有变量和相应图表的 PCA 分解。
3.6 奇异值分解
主成分分析(PCA)线性变换数据并生成彼此不相关的主成分。但特征值分解过程中遵循的步骤只能应用于 方阵,而奇异值分解(SVD)可以应用于任何 m × n 矩阵。
假设我们有一个矩阵 A。A 的形状是 m × n,或者它包含 m 行和 n 列。A 的转置可以表示为 A^(T)。
我们可以使用 A 和 A^(T) 创建另外两个矩阵,即 A A^(T) 和 A^(T)A。这些结果矩阵 A A^(T) 和 A^(T)A 具有一些特殊性质,如下(这些性质的数学证明超出了本书的范围):
-
它们是对称的方阵。
-
它们的特征值要么是正的,要么是零。
-
A A^(T) 和 A^(T)A 与原始矩阵 A 有相同的特征值。
-
A A^(T) 和 A^(T)A 与原始矩阵 A 有相同的秩。
矩阵 A A^(T) 和 A^(T)A 的特征向量被称为 A 的奇异向量。它们的特征值的平方根被称为奇异值。
由于这两个矩阵(A A^(T) 和 A^(T)A)都是对称的,它们的特征向量彼此正交。换句话说,因为它们是对称的,所以特征向量相互垂直,并且可以是单位长度。
现在,有了这种数学理解,我们可以定义奇异值分解(SVD)。根据奇异值分解方法,可以分解任何矩阵 A,如方程 3.3 所示:
(3.3)
A = U * S * V^(T)
这里,A 是原始矩阵,U 和 V 分别是从 A A^(T) 和 A^(T)A 中取出的正交矩阵,具有正交特征向量,S 是具有 r 个等于奇异值的对角矩阵。简单来说,奇异值分解可以看作是使用特征值分解增强的 PCA 方法。
注意:奇异值比特征值分解更好,并且在数值上更稳健。
PCA 被定义为使用主成分对输入变量进行线性变换。所有这些线性变换的概念,如选择最佳组件等,都保持不变。主要过程步骤也保持相似,只是在 SVD 中,我们使用了一种稍微不同的方法,其中特征值分解被奇异向量和奇异值所取代。在稀疏数据集的情况下,通常建议使用 SVD;在密集数据集的情况下,可以使用 PCA。
练习 3.4
回答这些问题以检查你的理解:
-
SVD 基于特征值分解技术。对或错?
-
PCA 比 SVD 是一个更稳健的方法。对或错?
-
SVD 中的奇异值和奇异向量是什么?
3.6.1 使用 SVD 的 Python 解决方案
在本案例研究中,我们使用的是mushrooms数据集。这个数据集包含了 23 种烤蘑菇的描述。有两种类别:要么蘑菇是e,这意味着它是可食用的,要么蘑菇是p,这意味着它是有毒的。步骤如下:
- 导入库:
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelEncoder, StandardScaler
-
- 导入数据集并检查形状、头部等(见图 3.18):
mushrooms_df = pd.read_csv('mushrooms.csv')
mushrooms_df.shape
mushrooms_df.head()

图 3.18 代码输出
-
- 如我们所观察到的,数据集中的值在本质上属于分类类型。它们首先应该被编码成数值。这不是处理分类变量的唯一方法。还有其他技术,我们将在整本书中探讨。
首先,调用LabelEncoder并将其应用于数据集中的所有列。LabelEncoder使用独热编码方法将分类变量转换为数值:
encoder = LabelEncoder()
for col in mushrooms_df.columns:
mushrooms_df[col] = encoder.fit_transform(mushrooms_df[col])
-
- 再次查看数据集。所有的分类值都已转换为数值(见图 3.19):
mushrooms_df.head()

图 3.19 代码输出
-
- 下两个步骤与上一个案例研究相同,其中我们将数据集分为
X_variables和y_label。然后对数据集进行归一化:
- 下两个步骤与上一个案例研究相同,其中我们将数据集分为
X_variables = mushrooms_df.iloc[:,1:23]
y_label = mushrooms_df.iloc[:, 0]
scaler = StandardScaler()
X_features = scaler.fit_transform(X_variables)
-
- 实现 SVD。
numpy中有一个实现 SVD 的方法。输出是u、s和v,其中u和v是奇异向量,s是奇异值。如果您愿意,可以分析它们的形状和维度:
- 实现 SVD。
u, s, v = np.linalg.svd(X_features, full_matrices=True)
-
- 我们知道奇异值使我们能够计算每个奇异向量解释的方差。现在我们将分析每个奇异向量解释的百分比方差,并将其绘制出来(见图 3.20)。结果保留到小数点后三位。然后我们将结果绘制成直方图。在 x 轴上,我们有奇异向量,而在 y 轴上,我们有解释的百分比方差:
variance_explained = np.round(s**2/np.sum(s**2), decimals=3)
variance_explained
sns.barplot(x=list(range(1,len(variance_explained)+1)),
y=variance_explained, color="blue")
plt.xlabel('SVs', fontsize=16)
plt.ylabel('Percent of the variance explained', fontsize=15)

图 3.20 代码输出
-
- 创建一个数据框(见图 3.21)。这个新的数据框 svd_df 包含前两个奇异向量和元数据。然后我们使用 head 命令打印前五行:
col_labels= ['SV'+str(i) for i in range(1,3)]
svd_df = pd.DataFrame(u[:,0:2], index=mushrooms_df["class"].tolist(),
columns=col_labels)
svd_df=svd_df.reset_index()
svd_df.rename(columns={'index':'Class'}, inplace=True)
svd_df.head()

图 3.21 包含前两个奇异向量和元数据的 DataFrame
-
- 与上一个案例研究类似,我们将数值替换为实际的类别标签;
1表示可食用,而0表示有毒:
- 与上一个案例研究类似,我们将数值替换为实际的类别标签;
svd_df['Class'] = svd_df['Class'].replace({1:'Edible', 0:'Poison'})
-
- 我们现在绘制了两个成分解释的方差(见图 3.22)。在这里,我们只选择了前两个成分。建议您使用上一节中描述的方法选择最佳成分数量,并绘制相应的散点图。在这里,x 轴上显示了第一个奇异向量 SV1,y 轴上显示了第二个奇异向量 SV2。印刷版的书没有显示不同的颜色,但 Python 代码的输出显示了。相同的输出也可以在 GitHub 仓库中找到:
color_dict = dict({'Edible':'Black',
'Poison': 'Red'})
sns.scatterplot(x="SV1", y="SV2", hue="Class",
palette=color_dict,
data=svd_df, s=105,
alpha=0.5)
plt.xlabel('SV 1: {0}%'.format(variance_explained[0]*100), fontsize=15)
plt.ylabel('SV 2: {0}%'.format(variance_explained[1]*100), fontsize=15)

图 3.22 两个成分解释的方差图
我们可以观察到两个类别相对于两个成分的分布。这两个类别——可食用和有毒——分别用黑色和红色编码。正如我们之前所提到的,我们只选择了两个成分来使用可视化图来展示效果。您应该使用上一案例研究中描述的方法选择最佳成分数量,然后使用不同的奇异向量可视化结果。这种解决方案可以用于在现实世界数据集中降低维度。
3.7 维度降低的优缺点
在本章的初始部分,我们讨论了维度灾难的缺点。在最后几节中,我们发现了 PCA 和 SVD,并使用 Python 实现了它们。现在我们将检查这些技术的优点和挑战。实现 PCA 或 SVD 的主要优点是
-
减少的维度数量导致数据集的复杂性降低。相关的特征被移除并转换。手动处理相关变量是一项艰巨的任务,相当繁琐且令人沮丧。PCA 和 SVD 等技术为我们轻松完成这项工作。相关特征的数量最小化,总体维度减少。
-
如果维度较少,数据集的可视化更好。非常难以可视化和描述一个高维数据集。
-
如果移除了相关变量,机器学习模型的准确性会提高。这些变量不会对模型的性能增加任何东西。
-
由于数据集较为简单,训练时间减少。因此,所需的计算能力和时间也较少。
-
过度拟合是监督机器学习模型中的一个麻烦。这是一种条件,即模型在训练数据集上表现良好,但在测试/验证数据集上表现不佳。这意味着模型可能无法在现实世界中的未见数据集上表现良好。这违背了构建机器学习模型的整个目的。PCA/SVD 通过减少变量的数量来帮助解决过度拟合问题。
同时,我们在降维技术中面临一些挑战,如下所述:
-
PCA/SVD 创建的新成分通常难以解释。它们是数据集中独立变量的组合,并不真正与现实世界相关;因此,将它们与现实世界场景联系起来可能很困难。
-
PCA/SVD 需要数值变量。因此,所有分类变量都应该以数值形式表示。
-
在实施解决方案之前,需要对数据集进行归一化/标准化。
-
当我们使用 PCA 或 SVD 时,可能会出现信息丢失。主成分不能替代原始数据集,因此在我们实施这些方法时可能会丢失一些信息。
然而,尽管有一些挑战,PCA 和 SVD 仍然被用于数据集的降维。它们是最受欢迎的方法之一,并且被广泛使用。请注意,这些是线性方法;我们在本书的后面部分介绍了降维的非线性方法。
我们现在已经介绍了在降维中使用的两种最重要的技术。在后面的章节中,我们将探讨更高级的技术。现在是时候转向案例研究了。
3.8 降维案例研究
让我们探索一个现实世界的案例,以了解 PCA 和 SVD 在现实商业场景中的应用。考虑以下情况:你为一家电信服务提供商工作。你有一个订阅者基础,你希望根据多个参数对消费者进行聚类。挑战在于可供分析的大量维度。
目标将是使用降维算法减少属性数量。消费者数据集可能包括以下内容:
- 订阅者的人口统计详情,包括年龄、性别、职业、家庭规模、婚姻状况等(见图 3.23)。

图 3.23 订阅者的人口统计详情,如年龄、性别、婚姻状况、家庭规模、城市等。
- 消费者的订阅详情,可能看起来像图 3.24。

图 3.24 订阅详情,如服务期限、后付费/预付费连接等。
- 消费者使用情况,如通话分钟数、通话费率、数据使用、服务等(见图 3.25)。

图 3.25 订阅者使用情况指定了使用的分钟数、发送的短信、使用的数据、在网络中花费的天数、国内或国际使用等。
- 订阅者的支付和交易详情,可能包括各种交易、支付方式、支付频率、上次支付以来的天数等(见图 3.26)。

图 3.26 展示了订阅者交易详情,包括金额、方式等所有详细信息。
- 许多更多的属性。到目前为止,我们已经确定涉及的变量数量确实很高。一旦我们将所有这些数据点合并,最终数据中的维度数量可以非常大(见图 3.27)。

图 3.27 最终数据集是上述所有数据集的组合。它将是一个大型的、真正高维的数据集,需要进行分析。
在我们进行任何监督或无监督解决方案之前,我们应该减少属性的数量。在本章中,我们专注于降维技术,因此步骤涵盖了该过程的这一方面。在后面的章节中,我们将更详细地检查探索性分析。
作为第一步,我们将对数据集进行合理性检查并进行数据清洗。我们将检查数据点的数量、缺失值的数量、重复项、垃圾值等。这将使我们能够删除任何可能非常稀疏或包含信息不多的变量。例如,如果性别只对客户基础的 0.01%可用,那么删除该变量可能是个好主意。或者如果所有客户都声称他们的性别是男性,该变量没有为我们提供任何新信息,因此可以被丢弃。有时,使用业务逻辑,可能会从数据集中删除变量。一个例子在第 3.4 节中已讨论。在这一步中,我们可能会合并几个变量。例如,我们可能会创建一个新的变量,作为平均交易价值,通过将总支出除以总交易次数来计算。这样,我们将能够减少一些维度。
注意:GitHub 仓库中有一个 Python Jupyter 笔记本,其中我们提供了数据清洗步骤的非常详细的解决方案。
一旦完成数据的基本清洗,我们就开始进行探索性数据分析。作为探索性分析的一部分,我们检查变量的分布、其分布情况、数值变量的均值/中位数/众数等。这有时被称为单变量分析。这一步骤使我们能够测量变量的分布范围,了解中心趋势,检查分类变量的不同类别的分布,并寻找任何值中的异常。例如,使用前面提到的数据集,我们将对分析最大/最小/平均数据使用量或性别或年龄的百分比分布感兴趣。我们想知道进行交易的最受欢迎的方法,我们也想了解交易的最大/最小/平均金额。列表还在继续。
然后,我们探索变量之间的关系,这被称为双变量分析。交叉表,或数据的分布,是双变量分析的一部分。在这一步中会创建一个相关矩阵。高度相关的变量将被彻底检查。根据业务逻辑,其中一个变量可能会被删除。这一步有助于可视化并理解一个变量在存在其他变量时的行为。我们可以检查它们之间的相互关系以及关系的强度。在本案例研究中,我们将回答诸如,“与发送更多短信的用户相比,使用更多数据的用户在网络上花费的时间更多吗?”,“使用在线模式进行交易的用户比使用现金的用户产生更多的收入吗?”,或者“性别/年龄与数据使用之间是否存在关系?”等问题。在项目这一阶段,我们将回答许多此类问题。
注意:GitHub 仓库中有一个 Python Jupyter 笔记本,其中提供了单变量和双变量阶段的详细步骤和代码。查看它!
在这个阶段,我们有一个具有大量维度的数据集,我们希望减少维度的数量。现在是实施 PCA 或 SVD 的好时机。这些技术将减少维度的数量,并将数据集准备好用于流程的下一步,如图 3.28 所示。该图仅具有代表性,以描述降维方法的效果。注意,左图中的大量黑色线条在右图中减少到了更少的红色线条。

图 3.28 通过使用捕捉数据集中最大方差的主成分,一个非常高维的数据集将被降低到低维。
维度降低方法的结果将是一个变量数量较少的数据集。该数据集可以用于监督学习或无监督学习。我们已经在章节的前几节中查看了一些使用 Python 的示例。
这标志着我们对电信用户的案例研究的结束。该案例可以扩展到任何其他领域,如零售;银行、金融服务和保险;航空;医疗保健;制造业;以及其他领域。
3.9 结论性思考
数据无处不在,以各种形式、水平和维度存在,并且具有不同的复杂程度。经常提到“数据越多,越好。”这在一定程度上确实是正确的。但是,当维度数量非常高时,理解它就变得非常困难。分析可能会变得有偏见,并且处理起来非常复杂。我们在本章中探讨了维度的诅咒。我们发现主成分分析(PCA)和奇异值分解(SVD)可以帮助减少这种复杂性。它们使数据集为下一步做好准备。
降维并不像看起来那么简单。这不是一项容易的任务,但绝对是一项非常有价值的任务。它需要商业洞察力、逻辑和常识的结合。结果数据集可能还需要一些额外的工作。但它是构建机器学习模型的一个非常好的起点。
这标志着第三章节的结束,也标志着本书第一部分的结束。在本部分,我们介绍了一些核心算法。我们从本书的第一章开始,探讨了机器学习的根本和基础。在第二章中,我们检查了三种聚类算法。在这一章中,我们探讨了 PCA 和 SVD。
在本书的第二部分,我们转换方向,研究更高级的主题。下一章我们将从关联规则开始。然后我们将探讨时间序列聚类、模糊聚类、高斯混合模型聚类等高级聚类方法。接下来是关于 t-SNE 和 LDA 等高级降维算法的章节。为了结束第二部分,我们检查文本数据集上的无监督学习。本书的第三部分更加高级,所以还有很长的路要走。请保持关注!
3.10 实际下一步行动和建议阅读材料
以下提供下一步行动的建议,并提供一些有用的阅读材料:
-
使用上一章中使用的车辆数据集进行聚类,并在其上实现 PCA 和 SVD。比较实现 PCA 和 SVD 前后聚类的性能。
-
从
mng.bz/2y9g获取数据集。你可以找到许多数据集。比较这些数据集上 PCA 和 SVD 的性能。 -
阅读以下关于 PCA 的论文:
-
阅读以下关于 SVD 的研究论文:
摘要
-
“维度诅咒”指的是由具有太多变量的高维数据集引起的问题,这些问题使分析和模型性能复杂化。
-
高维可能导致稀疏数据集、增加数学复杂性、更长的处理时间,以及机器学习模型中潜在的过拟合。
-
霍奇斯现象表明,增加变量只能提高模型性能到一定程度,之后性能会下降。
-
并非所有维度都是重要的;一些可能不会对模型精度做出有意义的贡献,应该被删除以减少复杂性。
-
数据可视化可以通过将数据集减少到仍能捕捉到重要信息的较少维度来帮助解释数据集。
-
手动降维包括删除不显著的变量或将它们逻辑组合以减少数据集维度。
-
基于算法的降维方法包括 PCA、SVD、LDA 和 t-SNE 等,它们将高维数据转换到低维空间。
-
PCA 通过创建能够捕捉最大方差的同时最小化冗余和噪声的主成分来降低维度。
-
SVD 增强了 PCA,可以处理任何矩阵形状,并将它们分解为奇异值和向量,以保持数据集信息。
-
每种降维技术都需要对数据进行归一化处理,并将分类变量转换为数值形式。
-
降维简化了数据集,提高了可视化效果和模型精度,减少了计算时间,并减轻了过拟合风险。
-
降维的挑战包括可解释性的丧失、信息损失以及对数值数据的要求。
-
主成分分析(PCA)和奇异值分解(SVD)被广泛用于有效地降低维度,并且每种方法都适用于不同数据集的密度。
-
这些技术可以应用于各种行业,如零售业;银行业、金融服务和保险业;以及医疗保健业,以简化用于分析的高维数据集。
-
降维过程包括初步数据清洗和探索性数据分析,然后应用降维技术。
第二部分 中级水平
恭喜你完成了第一部分,欢迎来到第二部分。
将本书中的旅程视为你的工作坊,在这里,原始概念和基础知识被转化为使用 Python 的案例研究和实际解决方案。我们在这里涵盖的每个概念、研究的每个算法和解决的每个案例研究都是一个构建块,但如何以创新的方式将它们组合在一起并在你的实际业务中实施,则取决于你。这种实施应该帮助你以逻辑和创造性的方式解决商业问题。你正在学习的算法、工具和技术将允许你逐步创建功能强大、有效的解决方案。
机器学习的真正艺术不在于熟记所有算法或死记硬背最深奥的数学概念,而在于知道如何解决问题、有效地使用可用数据集,并最终解决问题。在向最终用户揭示洞察力时,你不应该忽视用户体验。
你在第一部分已经学习了无监督学习的基础知识;现在是时候转向稍微更高级的主题了。在本部分,我们将深入探讨关联规则、高级聚类和降维技术。
第四章:关联规则
本章涵盖
-
关联规则
-
关联规则的多种算法类型
-
关联规则不同算法的实现
-
使用 SPADE 进行序列学习
关联的力量比美的力量更强;因此,关联的力量是美的力量。——约翰·拉斯金
恭喜你完成了本书的第一部分!你探索了无监督学习的基础以及 k-means 聚类、层次聚类、DBSCAN、主成分分析等算法。预期你已经掌握了第一部分中的数学概念,并创建了 Python 代码来解决每章末尾给出的练习题。
欢迎来到本书的第二部分,我们将在这里应用第一部分学到的概念,并探索一些稍微复杂的话题。本章我们将从关联规则开始。
下次当你访问附近的杂货店时,环顾店内,注意各种商品的摆放。你会发现牛奶、鸡蛋、面包、糖、洗衣粉、肥皂、水果、蔬菜、饼干以及各种其他商品整齐地堆叠。你是否曾想过这些摆放的逻辑以及这些商品是如何布局的?为什么某些产品被放在彼此附近,而其他产品则相隔甚远?显然,这种摆放不能是随机的,背后必须有科学的推理。或者你可能会想:Netflix 是如何根据你的观影历史像序列一样向你推荐电影的?我们将在本章中找到这些问题的答案。像往常一样,我们首先研究概念。我们通过不同算法的数学逻辑、每个算法的优缺点以及使用 Python 的实际应用来学习。本章末尾提供了一个商业案例研究来补充知识。欢迎来到第四章,祝大家一切顺利!
4.1 技术工具包
我们将继续使用迄今为止所使用的相同版本的 Python 和 Jupyter Notebook。本章中使用的代码和数据集已在相同的 GitHub 位置进行检查。
为了本章的学习,你需要安装几个 Python 库,包括apyori、pyECLAT、fpgrowth_py和pyspade。除此之外,你还需要numpy和pandas。使用这些库,我们可以非常快速地实现算法。否则,从头开始编写这些算法将是一项耗时且费力的任务。
让我们从关联规则开始吧。
4.2 关联规则概述
你可能听说过著名的“啤酒和尿布故事”。根据这个轶事,在超市购买尿布的顾客(大多是年轻人)也会在同一张发票上购买啤酒。换句话说,为婴儿购买尿布的年轻人有相当高的概率在同一笔交易中购买啤酒。我们不会对故事的真实性发表评论,但关联规则学习可以归因于从这个故事中得出的逻辑。
正式来说,关联规则可以用来发现数据集中存在的变量之间的有力关系。我们可以使用关联规则来衡量数据集中变量之间的相关性和共现性。在给出的例子中(假设故事是真实的),可以分析每日顾客交易。如果啤酒和尿布之间出现关系,这对超市来说是一个非常强烈的洞察,它可以使超市定制啤酒和尿布的摆放,调整营销策略,甚至改变价格。
我们可以通过超市中的另一个例子来理解。假设通过分析超市生成的五张发票,我们得到如表 4.1 所示的数据。在这个例子中,发票编号 1001 中购买了牛奶,因此其值为 1,而奶酪没有购买,因此为 0。
表 4.1 超市生成的发票示例
| 发票编号 | 牛奶 | 鸡蛋 | 面包 | 奶酪 |
|---|---|---|---|---|
| 1001 | 1 | 1 | 1 | 0 |
| 1002 | 0 | 0 | 0 | 1 |
| 1003 | 1 | 1 | 1 | 0 |
| 1004 | 0 | 1 | 0 | 1 |
| 1005 | 1 | 1 | 0 | 1 |
因此,在发票编号 1001 中购买了牛奶、鸡蛋和面包,而在发票编号 1002 中只购买了奶酪。从这里我们可以看到,每当牛奶和鸡蛋一起购买时,面包总是在同一张发票中购买。这确实是一个重要的发现。
现在将这种理解扩展到一天内进行的数千笔交易。这将导致人类眼睛通常无法察觉的非常强烈的关系,但关联规则算法可以为我们揭示它们。这可以导致更好的产品摆放,更好的产品价格,以及更多优化的营销支出。这样的模式将增强客户体验,并证明对提高整体客户满意度非常有帮助。
我们可以将关联规则可视化,如图 4.1 所示。这里有一些表示为节点 1、2、3、4 等的输入变量。这些节点通过箭头相互关联。它们之间的关系产生了规则 A 和规则 B。如果我们回顾本节开头提到的啤酒/尿布故事,规则 A 可以是当年轻男性顾客购买尿布时,他们通常也会购买啤酒,而规则 B 可以是当购买牛奶和鸡蛋时,通常也会购买面包。

图 4.1 关联规则可以可视化成数据集中各种变量之间的关系。这些变量相互关联,并在它们之间建立了显著的关系。
超市的例子有时被称为市场篮子分析。但关联规则不仅适用于杂货零售。它们在其他领域如生物信息学、医疗行业、入侵检测等也得到了证明。Netflix 或 Spotify 可以利用这些规则来分析历史用户行为,然后推荐用户最可能喜欢的内。网站开发者可以分析客户在其网站上的历史点击和使用情况。通过识别模式,他们可以找出用户倾向于点击哪些内容,以及哪些功能将最大化他们的参与度。医疗从业者可以使用关联规则来更好地诊断患者。医生可以比较症状与其他症状之间的概率关系,并提供更准确的诊断。这些用例发生在多个商业领域和商业功能中。
4.3 关联规则的基本构建块
我们在上一个章节中介绍了关联规则的定义。现在让我们来理解关联规则背后的数学概念。假设我们有一个零售店中的以下数据集:
-
设 X = {x[1], x[2], x[3], x[4], x[5]……,x[n]}是零售店中可用的n个商品。例如,它们可以是牛奶、鸡蛋、面包、奶酪、苹果等等。
-
设 Y = {y[1], y[2], y[3], y[4], y[5]……,y[m]}是零售店中生成的m个交易。每个交易可能包含零售店的所有或部分商品。
显然,交易中的每个商品都只会从零售店购买。换句话说,集合 Y 中的每个商品都将属于集合 X 的子集。同时,每个商品都会附有一个唯一的标识符,每个交易都会附有一个唯一的发票号码。
现在我们对分析模式和发现关系感兴趣。这将用于生成任何规则或洞察。所以让我们首先定义规则的意义。
假设我们找到一个规则,即每当列表 P 中的商品被购买时,列表 Q 中的商品也会被购买。这个规则可以写成以下形式:
-
规则是 P -> Q。这意味着每当 P 中定义的商品被购买时,也会导致 Q 中的购买。
-
P 中的商品将是 X 的子集或 P Í X。
-
同样,Q 中的商品也将是 X 的子集或 Q Í X。
-
P 和 Q 不能有任何共同元素或 P Ç Q = 0
现在让我们通过一个现实世界的例子来理解这些数学概念。假设 X = {牛奶,香蕉,鸡蛋,奶酪,苹果,面包,盐,糖,饼干,黄油,冷饮,水}。这些都是零售店中可用的全部商品。
Y = {1001, 1002, 1003, 1004, 1005} 是在那个零售店生成的五个发票。在这些发票中分别购买的项目在图 4.2 中给出。注意,对于每个发票,每个项目都关联着 0 和 1。这些发票只是为了说明目的。在实际的发票中,项目的数量可能要多得多。使用这个数据集,我们假设创建了两个规则:{牛奶,香蕉} -> {鸡蛋} 和 {牛奶,香蕉} -> {面包}。

图 4.2 零售店生成的五个发票示例
第一条规则意味着每当牛奶和香蕉一起购买时,鸡蛋也会在同一笔交易中购买。第二条规则意味着每当牛奶和香蕉一起购买时,面包也会在同一笔交易中购买。通过分析数据集,我们可以清楚地看到规则 1 总是正确的,而规则 2 则不是。
注意:规则左侧的项目被称为前件或 LHS,而规则右侧的项目被称为后件或 RHS。
在现实世界中,对于任何这样的规则要具有意义,相同的模式必须在数百甚至数千笔交易中重复出现。只有这样,我们才能得出结论,该规则确实是正确的,并且可以在整个数据库中泛化。
同时,可能存在许多这样的规则。在一个每天生成数千张发票的零售店中,可能有数百条这样的规则。我们如何找出哪些规则是重要的,哪些不是?这可以通过使用 支持度、置信度、提升度 和 确信度 的概念来理解,我们将在下一节中学习这些概念。
4.3.1 支持度、置信度、提升度和确信度
在上一个章节中,我们探讨了关联规则中规则的意义。我们也了解到,基于事务数据集可能会有数百条规则。在本节中,我们将探讨如何衡量这类规则的有效性,并筛选出最有趣的规则。这可以通过使用支持度、置信度、提升度和确信度的概念来实现。
回想一下,在上一个章节中我们讨论了规则的泛化。支持度、置信度、提升度和确信度允许我们衡量泛化的程度。简单来说,通过使用这四个参数,我们可以确定规则在我们实际应用中的实用性。毕竟,如果一个规则没有用或者不够强大,那么它就不需要被实施。支持度、置信度、提升度和确信度是检查规则有效性的参数。我们将在下一节中详细探讨这些概念。
我们将使用表 4.2 中的数据集来理解支持度、置信度、提升度和确信度的概念。第一张发票,1001,有牛奶、鸡蛋和面包,而奶酪没有购买。为了这个例子,我们总共只选取了四个项目。
表 4.2:用于理解支持度、置信度、提升和确信度的数据集
| 发票编号 | 牛奶 | 鸡蛋 | 面包 | 奶酪 |
|---|---|---|---|---|
| 1001 | 1 | 1 | 1 | 0 |
| 1002 | 0 | 1 | 1 | 1 |
| 1003 | 1 | 1 | 1 | 0 |
| 1004 | 0 | 1 | 0 | 1 |
| 1005 | 0 | 1 | 1 | 0 |
这里,对于一张发票,1 表示该发票中是否有项目,而 0 表示在该特定发票中没有购买该项目。例如,发票编号 1001 有牛奶、鸡蛋和面包,而 1002 有鸡蛋、面包和奶酪。
支持度
支持度衡量数据集中项目的频率百分比。简单来说,它衡量在数据集中发生项目的交易百分比。
支持度可以表示如下:

参考表 4.2。假设我们对规则{牛奶,鸡蛋} -> {面包}感兴趣。在这种情况下,有两个交易包含这三个项目(牛奶、鸡蛋和面包)。交易总数是五个。这意味着该规则的支持度为 2/5,即 0.4 或 40%。
现在假设我们对规则{面包,鸡蛋} -> {奶酪}感兴趣。在这种情况下,只有一个交易包含这三个项目。交易总数是五个。这意味着该规则的支持度为 1/5,即 0.2 或 20%。
备注:规则的支持度越高,越好。通常,我们会设定一个最低阈值以获得支持。最低阈值通常是在与业务利益相关者协商后确定的。
置信度
置信度衡量规则为真的频率;也就是说,它衡量包含前件也包含后件的交易的百分比。
因此,如果我们想测量规则 A -> B 的置信度:

这里,分子是在交易中同时存在 A 和 B 时支持的,而分母是指仅对 A 的支持。
参考表 4.2。再次假设我们对规则{牛奶,鸡蛋} -> {面包}感兴趣。在这种情况下,有两个交易包含牛奶和鸡蛋。因此,支持度为 2/5 = 0.4。这是分母。有两个交易包含所有三个(牛奶、鸡蛋、面包)。因此,支持度为 2/5 = 0.4,这是分子。将它们放入前面的方程中,规则{牛奶,鸡蛋} -> {面包}的置信度为 0.4/0.4 = 1。
现在假设我们对规则{鸡蛋,面包} -> {奶酪}感兴趣。在这种情况下,有四个交易包含(鸡蛋,面包)。交易总数是五个。这意味着支持度为 4/5,即 0.8。只有一个交易包含所有三个项目(鸡蛋、面包、奶酪)。因此,支持度为 1/5 = 0.2。因此,规则{鸡蛋,面包} -> {奶酪}的置信度为 0.2/0.8 = 0.25。
备注:规则的置信度越高,越好。和支撑度一样,我们对置信度也设定了一个最小阈值。
有时这也被称为 A 在 B 上的 条件概率。它可以理解为在 A 已经发生的情况下 B 发生的概率,可以表示为 P(A|B)。所以,在前面的例子中,在已经购买了鸡蛋和面包的情况下购买奶酪的概率是 25%,而购买面包的概率,在已经购买了牛奶和鸡蛋的情况下是 100%。
提升和说服力
提升是规则的一个重要测量标准。规则 A -> B 的提升可以定义为

在这里,当交易中同时存在 A 和 B 时,分子得到支持,而分母指的是对 A 的支持乘以对 B 的支持。
再次,参考表 4.2,假设我们感兴趣的规则是 {milk, eggs} -> {bread}。在这种情况下,有两个交易同时包含所有三个(牛奶、鸡蛋、面包)。因此,支撑度再次是 2/5 = 0.4,这是分子。有两个交易只包含(牛奶、鸡蛋),所以支撑度是 2/5 = 0.4。有四个交易包含面包,因此支撑度是 4/5 = 0.8。将前面的方程代入,规则 {milk, eggs} -> {bread} 的提升是 0.4/(0.4 x 0.8) = 1.25。
然后,假设我们感兴趣的规则是 {eggs, bread} -> {cheese}。在这种情况下,只有一个交易包含(鸡蛋、面包、奶酪)。交易总数是五个。这意味着支撑度是 1/5,即 0.2。有两个交易包含(奶酪),所以支撑度是 2/5 = 0.4。有四个交易包含(鸡蛋、面包),所以支撑度是 4/5 = 0.8。将前面的方程代入,规则 {eggs, bread} -> {cheese} 的提升是 0.2/(0.4 x 0.8) = 0.625。
如果提升的值 等于 1,这意味着前件和后件彼此独立,从中无法得出任何规则。
如果提升的值 大于 1,这意味着前件和后件彼此依赖。这个规则可以用于预测未来交易中的前件。这是我们想要从数据集中得出的见解。
如果提升的值 小于 1,这意味着前件和后件是彼此的替代品。一个的存在可能对另一个产生负面影响。这也是商业团队可以用于战略规划的重要见解。
当我们使用提升度评估任何规则时,应用领域知识是至关重要的。例如,如果我们评估规则{鸡蛋,面包} -> {奶酪},并且我们发现鸡蛋和面包可以是奶酪的替代品,我们知道在现实生活中这不是真的。因此,在这种情况下,我们不能对这个规则做出任何决定。我们必须使用领域知识来为这个规则得出任何结论。
同时,规则{牛奶,鸡蛋} -> {面包}可能是一个可以多次成立的规则。对于许多客户来说,当他们一起购买牛奶和鸡蛋时,购买面包的可能性非常高。因此,这个规则对这类客户来说更有意义。目标是有一个强大的业务逻辑来支持或反对使用算法识别出的规则。
确信度是另一个重要参数,由以下公式给出:

参考表 4.2。再次,假设我们感兴趣的规则是{鸡蛋,面包} -> {奶酪}。在这种情况下,只有一个交易中包含(奶酪)。总交易数为五。所以,这意味着支持度为 1/5,即 0.2,将用于分子。我们已经计算出置信度为 0.625。将这个值代入公式,我们可以计算出确信度为(1 – 0.2)/(1 – 0.625) = 2.13
我们可以将确信度解释为:规则{鸡蛋,面包} -> {奶酪}如果{鸡蛋,面包,奶酪}之间的关联完全是随机选择的,那么这个规则将错误地出现 2.13 倍。
在大多数商业场景中,提升度是使用的测量标准。还有其他测量参数,例如杠杆、集体强度等。但大多数情况下,置信度、支持度、提升度和确信度用于衡量任何规则的有效性。
练习 4.1
回答这些问题以检查你的理解:
-
支持度衡量规则在数据集中出现的频率。对或错?
-
如果提升度大于 1,这意味着两个项目是相互独立的。对或错?
-
置信度值越低,规则越好。对或错?
当我们在分析数据集时评估任何规则时,大多数情况下,我们会为置信度、支持度、提升度和确信度设置一个阈值。这使我们能够减少规则的数目并过滤掉不相关的规则。换句话说,我们只对非常频繁的规则感兴趣。当我们为数据集创建 Python 解决方案时,我们将更详细地研究这个问题。
4.4 Apriori 算法
Apriori 算法是用于关联规则的最流行算法之一。它在 1994 年由 Agrawal 和 Shrikant 提出。论文链接在章节末尾给出。
Apriori 用于理解和分析事务数据库中的频繁项。它采用“自下而上”的方法,首先根据子集的频率生成候选项。让我们通过一个例子来理解整个过程。我们将使用之前讨论过的相同数据集(见表 4.2)。Apriori 算法中使用的流程将类似于图 4.3。

图 4.3 Apriori 算法过程
假设我们希望分析数据集中面包与其他所有商品之间的关系。在这种情况下,第一级是面包,我们找到其出现的频率。
然后我们转向下一层,即第二层。现在我们找到面包与第二层中每个其他商品的关系:牛奶、鸡蛋和奶酪。在这里,我们再次找到所有可能组合的相应频率,这些组合是 {面包, 牛奶}、{面包, 鸡蛋} 和 {面包, 奶酪}。见图 4.4。

图 4.4 我们在第一级放置面包,而其他商品(牛奶、鸡蛋和奶酪)保持在第二级。面包保持在第一级,因为我们希望分析面包与其他所有商品的关系。
在分析完第二层之后,我们转向第三层和第四层,依此类推。这个过程一直持续到我们达到最后一层,其中所有商品都已耗尽。
通过这个过程,我们可以计算出所有可能组合的支持度。例如,我们会知道
{面包} -> {牛奶},
{面包} -> {鸡蛋},并且
{面包} -> {奶酪}。
对于下一级,我们也会得到支持度
{面包, 牛奶} -> {鸡蛋},
{面包, 鸡蛋} -> {牛奶},
{面包, 牛奶} -> {奶酪},
{面包, 奶酪} -> {牛奶},
{面包, 奶酪} -> {鸡蛋},并且
{面包, 鸡蛋} -> {奶酪}。
现在,使用相同的过程,计算下一级的所有可能组合。例如,{面包, 鸡蛋, 牛奶} -> {奶酪},{面包, 鸡蛋, 奶酪} -> {牛奶},依此类推。
当所有商品集都耗尽后,过程将停止。完整的架构可以像图 4.5 那样。
现在我们可以很容易地理解,可能的组合数量相当高,这是 Apriori 的一大挑战。但 Apriori 是一个非常强大的算法,也非常受欢迎。现在是时候使用 Python 实现 Apriori 了。

图 4.5 Apriori 算法的完整架构。在这里,我们将计算所有可能组合的支持度。探索所有商品之间的关系,由于整个数据库扫描,Apriori 的速度会受到影响。
4.4.1 Python 实现
我们现在将进行 Apriori 算法的 Python 实现。数据集和 Python Jupyter Notebook 已存入 GitHub 仓库。你可能需要安装 apyori。
要安装库,只需执行以下操作:
import sys
!{sys.executable} -m pip install apyori
步骤如下:
- 导入使用案例所需的库。我们正在导入
numpy和pandas。为了实现 Apriori,我们有一个名为apyori的库,也进行了导入:
import numpy as np
import pandas as pd
from apyori import apriori
-
- 导入数据集文件
store_data.csv:
- 导入数据集文件
store_dataset = pd.read_csv('store_data.csv')
还建议您通过打开 .csv 文件查看数据集。它将看起来像图 4.6 中的截图。截图显示了前 25 行。每一行代表一张发票。

图 4.6 .csv 文件的截图
-
- 接下来,我们通过
.info和.head命令对数据进行一些基本检查(见图 4.7):
- 接下来,我们通过
store_dataset.info()

store_dataset.head()

图 4.7 .info 和 .head 命令的输出
-
- 在这里,我们可以看到代码已经将第一个事务视为标题。因此,我们将再次导入数据,但这次我们会指定标题等于
None:
- 在这里,我们可以看到代码已经将第一个事务视为标题。因此,我们将再次导入数据,但这次我们会指定标题等于
store_dataset = pd.read_csv('store_data.csv', header=None)
-
- 让我们再次查看头部(见图 4.8)。这次看起来是正确的:
store_dataset.head()

图 4.8 .head() 的正确结果
-
- 我们使用的库接受数据集作为列表的列表。整个数据集必须是一个大列表,而每个事务都是大列表中的一个内部列表。因此,为了实现这一点,我们首先将我们的
store_dataset数据框转换为列表:
- 我们使用的库接受数据集作为列表的列表。整个数据集必须是一个大列表,而每个事务都是大列表中的一个内部列表。因此,为了实现这一点,我们首先将我们的
all_records = []
for i in range(0, 7501):
all_records.append([str(store_dataset.values[i,j]) for j in range(0, 20)])
-
- 接下来,我们实现 Apriori 算法。
对于算法,我们正在使用我们在第 6 步中创建的 all_records 列表。指定的最小支持度为 0.5 或 50%,最小置信度为 25%,最小提升度为 4,规则的最小长度为 2。
此步骤的输出是 apriori_rules 类对象。然后,我们将此对象转换为我们可以理解的列表。最后,我们打印这个列表:
apriori_rules = apriori(all_records, min_support=0.5, min_confidence=0.25, min_lift=4, min_length=2)
apriori_rules = list(apriori_rules)
print(len(apriori_rules))
代码的输出将是 0。这意味着不存在满足我们设定的规则条件的规则。
我们再次尝试执行相同的代码,尽管将最小支持度降低到 25%:
apriori_rules = apriori(all_records, min_support=0.25, min_confidence=0.25,
min_lift=4, min_length=2)
apriori_rules = list(apriori_rules)
print(len(apriori_rules))
再次,没有生成规则,输出为 0。即使将最小支持度降低到 10% 也不会产生任何规则:
apriori_rules = apriori(all_records, min_support=0.1, min_confidence=0.25,
min_lift=4, min_length=2)
apriori_rules = list(apriori_rules)
print(len(apriori_rules))
现在,我们将最小提升度降低到 2。这次输出为 200。这意味着有 200 条这样的规则满足标准:
apriori_rules = apriori(all_records, min_support=0.25, min_confidence=0.25,
min_lift=2, min_length=2)
apriori_rules = list(apriori_rules)
print(len(apriori_rules))
-
- 让我们查看第一条规则(见图 4.9):
print(apriori_rules[0])

图 4.9 print(apriori_rules[0]) 的输出
该规则解释了杏仁和汉堡之间的关系。支持度为 .005,置信度为 0.25。提升度,为 2.92,表明此规则相当强大。
-
- 现在,我们将详细查看所有规则。为此,遍历规则并从每个迭代中提取信息。每个规则都有构成规则的项以及支持度、置信度、提升度和确信度的相应值。我们在第 8 步中展示了一个示例。现在,在第 9 步中,我们只是使用
for循环提取所有规则的信息:
- 现在,我们将详细查看所有规则。为此,遍历规则并从每个迭代中提取信息。每个规则都有构成规则的项以及支持度、置信度、提升度和确信度的相应值。我们在第 8 步中展示了一个示例。现在,在第 9 步中,我们只是使用
for rule in apriori_rules:
item_pair = rule[0]
items = [x for x in item_pair]
print("The apriori rule is: " + items[0] + " -> " + items[1])
print("The support for the rule is: " + str(rule[1]))
print("The confidence for the rule is: " + str(rule[2][0][2]))
print("The lift for the rule is: " + str(rule[2][0][3]))
print("************************")
此步骤的输出如图 4.10 所示。在这里,我们可以观察到每条规则及其相应的支持度、置信度、升值和确信度。

图 4.10 第 9 步的输出
我们可以很容易地解释这些规则。例如,规则杏仁 -> 汉堡的升值为 2.92,置信度为 25.49%,支持度为 0.51%。这标志着我们使用 Python 实现的结束。这个例子可以扩展到任何其他现实世界的商业数据集。
注意:并非所有生成的规则都值得使用。当我们处理本章最后部分的案例研究时,我们将探讨如何从所有生成的规则中获取最佳规则。
Apriori 算法是一个强大且非常有洞察力的算法。但,像任何其他解决方案一样,它也有一些缺点。
4.4.2 Apriori 算法的挑战
正如我们所见,Apriori 算法生成的子集数量相当高(见图 4.5)。生成候选项集非常繁琐,因此分析数据集变得相当麻烦。Apriori 算法多次扫描整个数据集,因此需要将数据库加载到内存中。我们可以安全地推断,这需要大量的时间来计算。当我们处理非常大的数据集时,这个问题会变得更加严重。实际上,对于生成数百万笔交易的现实世界问题,会生成大量的候选项集,使用 Apriori 在整个数据集上运行会非常耗时。
正因如此,通常,我们设置一个最小支持度值以减少可能的规则数量。在先前的例子中,我们可以计算 1 级组合的支持度,如表 4.3 所示。在这里,如果我们设置最小支持度值为 0.5,则只有一条规则会被筛选出来。支持度会计算每个物品组合。例如,对于牛奶和面包,交易数量为 2,而总交易数量为 5。因此,支持度为 2/5,即 0.4。
表 4.3 1 级组合的支持度
| 组合 | 交易数量 | 总交易数量 | 支持度 |
|---|---|---|---|
| 牛奶,鸡蛋 | 2 | 5 | 0.4 |
| 牛奶,面包 | 2 | 5 | 0.4 |
| 牛奶,奶酪 | 0 | 5 | 0 |
| 鸡蛋,面包 | 4 | 5 | 0.8 |
| 鸡蛋,奶酪 | 2 | 5 | 0.4 |
| 面包,奶酪 | 1 | 5 | 0.2 |
因此,设置一个最小支持度值是一种明智的策略,可以使规则更容易管理。它减少了时间并生成了更具意义的规则。毕竟,从分析中生成的规则应该足够通用,以便可以在整个数据库中实施。
练习 4.2
回答以下问题以检查你的理解:
-
Apriori 算法只扫描数据库一次。对还是错?
-
如果香蕉在总共 12 笔交易中的 5 笔交易中出现,这意味着香蕉的支持度是 5/12。对还是错?
但 Apriori 算法确实是一个伟大的解决方案。它仍然非常受欢迎,并且在讨论关联规则时通常是最先提出的算法之一。
注意:数据准备是关键步骤之一,也是一个相当大的挑战。我们将在第 4.8 节中的案例研究中探讨这个挑战。
4.5 等价类聚类和自底向上的格遍历
我们现在将研究等价类聚类和自底向上格遍历算法(ECLAT),它在速度和易于实现方面有时被认为比 Apriori 更好。ECLAT 使用深度优先搜索方法。这意味着 ECLAT 在整个数据集上以垂直方式执行搜索。它从根节点开始,然后深入一层,并继续直到达到第一个终端节点。假设终端节点在级别X。一旦达到终端节点,算法就后退一步,达到级别(X – 1),并继续直到再次找到终端节点。让我们通过图 4.11 所示的树状图来理解这个过程。

图 4.11 展示了理解 ECLAT 算法过程的树状图。它从 1 开始,到 16 结束。
ECLAT 将采取以下步骤:
-
算法从根节点 1 开始。
-
然后,它深入一层到达根节点 2。
-
然后,它将再深入一层,直到达到终端节点 11。
-
一旦达到终端节点 11,它就后退一步,到达节点 5。
-
算法接着搜索是否有可用的节点。在节点 5 我们可以看到没有这样的节点可用。
-
因此,算法再次后退一步,达到节点 2。
-
在节点 2,算法再次探索。它发现可以进入节点 6。
-
因此,算法进入节点 6 并再次开始探索,直到达到终端节点 12。
-
此过程会继续进行,直到所有组合都被耗尽。
显然,计算速度取决于数据集中不同项目的总数。这是因为不同项目的数量定义了树的宽度。每个交易中购买的项目将定义每个节点之间的关系。
在 ECLAT 的执行时间内,每个项目(无论是单独还是成对)都会被分析。让我们使用我们之前为 Apriori 使用的相同示例来更好地理解 ECLAT。参见表 4.2。
ECLAT 将采取以下步骤来分析数据集:
- 在第一次运行中,ECLAT 将找到所有单个项目的发票号码。换句话说,它将找到所有项目的单独发票号码。这显示在表 4.4 中,其中牛奶出现在发票号码 1001 和 1003 中,而鸡蛋出现在所有五张发票中。
表 4.4 包含每个项目各自的发票
| 项目 | 发票编号 |
|---|---|
| 牛奶 | 1001, 1003 |
| 鸡蛋 | 1001, 1002, 1003, 1004, 1005 |
| 面包 | 1001, 1002, 1003, 1005 |
| 奶酪 | 1002, 1004 |
-
- 在下一步中,所有包含两个项目的数据集都按表 4.5 所示进行探索。例如,牛奶和鸡蛋存在于发票编号 1001 和 1003 中,而牛奶和奶酪不存在于任何发票中。
表 4.5 两项数据集
| 项目 | 发票编号 |
|---|---|
| 牛奶,鸡蛋 | 1001 ,1003 |
| 牛奶,面包 | 1001, 1003 |
| 牛奶,奶酪 | — |
| 鸡蛋,面包 | 1001, 1002, 1003, 1005 |
| 鸡蛋,奶酪 | 1002, 1004 |
| 面包,奶酪 | 1002 |
-
- 在下一步中,所有包含三个项目的数据集都按表 4.6 所示进行探索。这里我们只有两种组合。
表 4.6 三项数据集
| 项目 | 发票编号 |
|---|---|
| 牛奶,鸡蛋,面包 | 1001, 1003 |
| 鸡蛋,面包,奶酪 | 1002 |
-
- 在我们的数据集中没有包含四个项目的发票。
-
- 现在,根据我们为支持计数值设定的阈值,我们可以选择规则。所以,如果我们想规则为真的最小交易数量是三个,那么只有一个规则符合条件,即 {鸡蛋,面包}。如果我们决定最小交易数量的阈值是两个,那么像 {牛奶,鸡蛋,面包},{牛奶,鸡蛋},{牛奶,面包},{鸡蛋,面包} 和 {鸡蛋,奶酪} 这样的规则都符合规则。
我们现在将为 ECLAT 创建一个 Python 解决方案。
4.5.1 Python 实现
我们现在将使用 Python 执行 ECLAT。在这里,我们使用 pyECLAT 库。数据集看起来像图 4.12。

图 4.12 使用 Python 的 pyECLAT 库的 ECLAT
步骤如下:
- 导入库:
import numpy as np
import pandas as pd
from pyECLAT import ECLAT
-
- 导入数据集:
data_frame = pd.read_csv('Data_ECLAT.csv', header = None)
-
- 生成 ECLAT 实例:
eclat = ECLAT(data=data_frame)
在上一步生成的 ECLAT 实例 eclat 中有一些属性,如 eclat.df_bin,它是一个二进制数据框,以及 eclat.uniq_,它是一个包含所有唯一项目的列表。
-
- 拟合模型。我们这里给出最小支持度为 0.02。之后,我们打印支持度:
get_ECLAT_indexes, get_ECLAT_supports = eclat.fit(min_support=0.02,
min_combination=1,
max_combination=3,
separator=' & ')
get_ECLAT_supports
输出如图 4.13 所示。

图 4.13 第 4 步的输出
我们可以根据支持度解释提供的结果。对于每个项目和项目的组合,我们都会得到支持度的值。例如,对于薯条和鸡蛋,支持度的值是 3.43%。
与 Apriori 算法相比,ECLAT 有一些优势。因为它使用深度搜索方法,所以比 Apriori 快,并且计算所需的内存更少。它不迭代地扫描数据集,这使得它甚至比 Apriori 更快。在我们研究完最后一个算法之后,我们将再次比较这些算法。
4.6 F-P 算法
F-P 算法是本章讨论的第三个算法。它是对 Apriori 算法的改进。回想一下,在 Apriori 算法中,我们面临着耗时和昂贵的计算挑战。F-P 通过将数据库表示为一种称为频繁模式树或FP 树的树形结构来解决这些问题。由于这种频繁模式,我们不需要像在 Apriori 算法中那样生成候选项。现在让我们详细讨论 F-P 算法。
F-P 树是一种树形结构,它挖掘数据集中的最频繁项目。这在图 4.14 中得到了可视化。

图 4.14 F-P 算法可以用树形图结构表示。每个节点代表一个独特的项目。根节点是NULL。
每个节点代表数据集中的独特项目。树的根节点通常保持为NULL。树中的其他节点是数据集中的项目。如果它们在同一发票中,则节点之间相互连接。我们将逐步研究整个过程。
假设我们正在使用表 4.7 中所示的数据集。因此,我们有苹果,牛奶,鸡蛋,奶酪和面包这些独特项目。有九个事务,每个事务中的相应项目在表 4.7 中显示。
表 4.7 用于理解 F-P 算法的数据集
| 事务 | 项目集 |
|---|---|
| T1 | 苹果,牛奶,鸡蛋 |
| T2 | 牛奶,奶酪 |
| T3 | 牛奶,面包 |
| T4 | 苹果,牛奶,奶酪 |
| T5 | 苹果,面包 |
| T6 | 牛奶,面包 |
| T7 | 苹果,面包 |
| T8 | 苹果,牛奶,面包,鸡蛋 |
| T9 | 苹果,牛奶,面包 |
现在我们将 F-P 算法应用于这个数据集。步骤如下:
- 与 Apriori 类似,首先扫描整个数据集。计算每个项目的出现次数,并生成频率。结果在表 4.8 中建议。我们已按整个数据集中频率或相应支持计数的降序排列项目。例如,苹果在六次交易中被购买。
表 4.8 各项目集的相应频率
| 项目 | 频率或支持计数 |
|---|---|
| 牛奶 | 7 |
| 苹果 | 6 |
| 面包 | 6 |
| 奶酪 | 2 |
| 鸡蛋 | 2 |
如果两个项目的频率完全相同,则可以按任意顺序排列。在这个例子中,面包和苹果的频率相同。因此,我们可以保持面包或苹果作为第一个项目。
- 2. 开始构建 F-P 树。我们从创建根节点开始,这在图 4.15 中通常是
NULL节点。

图 4.15 树的根节点通常保持为NULL。
- 3. 分析第一个事务,T1。在这里,第一个事务中有苹果,牛奶和鸡蛋。在这三个中,牛奶的支持计数最高,为 7。因此,从根节点延伸出一条连接到牛奶的路径,我们将其表示为 Milk:1(见图 4.16)。

图 4.16 从根节点到牛奶的连接。牛奶具有最高的支持度;因此我们选择了牛奶。
-
- 现在看看 T1 中的其他项目。苹果的支持计数为 6,鸡蛋的支持计数为 2。因此,我们将从牛奶扩展到苹果的连接,并命名为苹果:1,然后从苹果到鸡蛋,并命名为鸡蛋:1(见图 4.17)。
-
- 现在看看 T2。它有牛奶和奶酪。牛奶已经连接到根节点。因此,牛奶的计数变为 2,变为牛奶:2。接下来,我们将从牛奶创建一个分支到奶酪,并命名为奶酪:1。添加情况如图 4.18 所示。

图 4.17 过程的第 4 步,我们已经完成了 T1 中的所有项目。所有项目——牛奶、苹果和鸡蛋——现在都已连接。

图 4.18 过程的第 5 步,我们开始分析 T2。牛奶已经连接,所以其计数增加 2,而奶酪被添加到树中。
-
- 考虑 T3。T3 有牛奶和面包。因此,类似于第 5 步,牛奶的计数为 3,变为牛奶:3。同样,类似于第 5 步,我们添加另一个从牛奶到面包的连接,并命名为面包:1。更新的树如图 4.19 所示。

图 4.19 在第 6 步中,分析 T3。牛奶的计数增加了 1,变为 3,同时面包作为新的连接被添加。
-
- 在 T4 中,我们有苹果、牛奶和奶酪。牛奶的计数变为 4;苹果现在是 2。然后我们创建一个从苹果到奶酪的分支,命名为奶酪:1(见图 4.20)。

图 4.20 在过程的第 7 步中,正在分析 T4。牛奶的计数变为 4,苹果增加到 2,并添加了一个从苹果到奶酪的新分支。
-
- 在 T5 中,我们发现我们有苹果和面包。它们都没有直接连接到根节点,并且频率相等,都是 6。因此,我们可以选择任何一个连接到根节点。图被更新为图 4.21。

图 4.21 分析 T5 后,图表发生变化,如图所示。我们添加了苹果和面包到树中。
-
- 这个过程会继续进行,直到我们耗尽所有事务,最终得到如图 4.22 所示的最终图。

图 4.22 当我们耗尽所有可能的组合后得到的最终树
到目前为止,做得很好!但之后还有更多步骤。到目前为止,我们只创建了树。现在我们需要生成如表 4.9 所示的数据集。这是我们希望生成的输出。
表 4.9 F-P 算法表
| 项目 | 条件模式基 | 条件 F-P 树 | 生成频繁模式 |
|---|---|---|---|
| 奶酪 | |||
| 面包 | |||
| 鸡蛋 | |||
| 苹果 |
你可能想知道为什么只列出了四个项目。由于牛奶直接来自根节点,没有其他方式到达它,因此我们不需要为牛奶单独列出行。
-
- 在继续之前,我们必须将任何规则的最低支持计数设置为 2,以便该规则可接受。我们这样做是为了简化,因为数据集相当小。
注意:对于现实生活中的商业问题,建议您测试支持计数的多重甚至更高的值;否则,生成的规则数量可能会非常高。
让我们从奶酪作为第一个项目开始。我们可以通过{NULL-牛奶-奶酪}和{NULL-牛奶-苹果-奶酪}到达奶酪。对于这两条路径,奶酪的计数都是 1。因此,(如果我们忽略 NULL)我们的条件模式基是{牛奶-奶酪}或{牛奶:1}和{牛奶-苹果-奶酪}或{牛奶-苹果:1}。完整的条件模式基变为{{牛奶:1}, {牛奶-苹果:1}}。此信息被添加到表 4.10 的第二列中。
表 4.10:过程步骤 10,我们已填充奶酪的第一个单元格
| 项目 | 条件模式基 | 条件 F-P 树 | 生成的频繁模式 |
|---|---|---|---|
| 奶酪 | {{牛奶:1}, {牛奶-苹果:1}} | ||
| 面包 | |||
| 鸡蛋 | |||
| 苹果 |
-
- 现在如果我们将条件模式基中的两个值相加,我们会得到牛奶为 2,苹果为 1。由于我们已经为频率计数设置了 2 的阈值,我们将忽略苹果的计数。条件 F-P 树的价值,即表中的第三列,变为{牛奶:2}。现在我们只需将原始项添加到其中,这样就变成了生成的频繁模式或第 4 列。见表 4.11。
表 4.11:过程步骤 11,我们已完成奶酪的详细信息
| 项目 | 条件模式基 | 条件 F-P 树 | 生成的频繁模式 |
|---|---|---|---|
| 奶酪 | {{牛奶:1}, {牛奶-苹果:1}} | ||
| 面包 | |||
| 鸡蛋 | |||
| 苹果 |
-
- 以类似的方式,表中所有其他单元格都被填充,从而得到最终的表格(表 4.12)。
表 4.12:分析所有项目组合后得到的最终表格
| 项目 | 条件模式基 | 条件 F-P 树 | 生成的频繁模式 |
|---|---|---|---|
| 奶酪 | {{牛奶:1}, {牛奶-苹果:1}} | ||
| 面包 | {{牛奶-苹果:2}, {牛奶:2}, {苹果:2}} | {{牛奶:4, 苹果:2}, {苹果:2}} | {{牛奶-面包:4}, {苹果-面包:4}, {牛奶-苹果-面包:2}} |
| 鸡蛋 | {{牛奶-苹果:1}, {牛奶-苹果-面包:1}} | {{牛奶-鸡蛋:2}, {牛奶-苹果:2}, {牛奶-苹果:2}} | |
| 苹果 |
这确实是一个复杂的过程。但一旦步骤清晰,它就很简单了。
作为这个练习的结果,我们得到了最终的一组规则,如最终列“生成的频繁模式”所示。
注意事项:请注意,没有任何一条规则是彼此相似的。
我们将使用最后一列“频繁模式生成”作为我们数据集的规则。
F-P 增长算法的 Python 实现相当简单,并且可以使用库轻松计算。为了节省空间,我们已经将 Jupyter 笔记本上传到本章的 GitHub 仓库。
我们现在将探讨另一个有趣的主题:序列规则挖掘。这是一个非常强大的解决方案,允许企业根据客户定制其营销策略和产品推荐。
4.7 序列规则挖掘
考虑这一点:Netflix 有一个所有客户随时间订购的电影的交易数据库。如果它分析和发现 65%的客户在接下来的一个月观看了战争电影 X,也观看了浪漫喜剧 Y,那么这将是非常有见地和可操作的信息。它将允许 Netflix 向客户推荐其产品并定制其营销策略。
到目前为止,在本章中,我们已经介绍了三种关联规则算法。但所有数据点都限于同一个数据集,并且没有涉及序列。序列模式挖掘允许我们分析发生事件序列的数据集。通过分析数据集,我们可以找到具有统计学意义的模式,这使我们能够解码整个事件序列。显然,事件序列是有特定顺序的,这是在序列规则挖掘中需要考虑的一个重要属性。
注意事项:序列规则挖掘与时间序列分析不同。要了解更多关于时间序列分析的信息,请参阅附录。
序列规则挖掘被广泛应用于多个领域和功能。它可以用于生物学中提取 DNA 测序过程中的信息,或者可以用来理解用户的在线搜索模式。序列规则挖掘将帮助我们了解用户接下来将要搜索的内容。在讨论关联规则时,我们使用了牛奶、面包和鸡蛋在同一笔交易中购买的事务。序列规则挖掘是对此的扩展,其中我们分析连续的事务并试图解码存在的任何序列。
在研究使用等价性进行序列模式发现(SPADE)算法时,我们涵盖了构成算法基础的数学概念。这些概念有点棘手,可能需要阅读多次才能掌握。
4.7.1 使用等价性进行序列模式发现
我们现在使用 SPADE 探索序列规则挖掘。它是由 Mohammed J. Zaki 提出的;论文链接在本章末尾。
因此,我们希望分析一个事件序列。例如,一位顾客购买了一部手机和充电器。一周后,他们购买了耳机,两周后,他们购买了手机壳和屏幕保护膜。所以,在每个交易中都有购买的项目。每个交易都可以称为一个事件。让我们更详细地了解它。
让我们假设我们有一个用于讨论的项目完整列表。它将包含像 i[1],i[2],i[3],i[4],i[5] 等项目。因此,我们可以写出 I = {i[1],i[2],i[3],i[4],i[5]………,i[n]},其中我们总共有 n 个不同的项目。
项目可以是任何东西。如果我们考虑同样的杂货店例子,项目可以是牛奶、鸡蛋、奶酪、面包等等。
一个事件将是同一交易中的项目集合。一个事件可以包含像 (i[1], i[5], i[4], i[8]) 这样的项目。例如,一个事件可以包含在同一交易中购买的项目(牛奶,糖,奶酪,面包)。我们将事件表示为 ⍺。
接下来,让我们了解一个序列。序列不过是按顺序排列的事件。换句话说,⍺[1] -> ⍺[2] -> ⍺[3] -> ⍺[4] 可以被称作事件序列。例如,(牛奶,奶酪) -> (面包,鸡蛋) -> (奶酪,面包,糖) -> (牛奶,面包) 是一个事务序列。这意味着在第一个事务中,购买了牛奶和奶酪。在随后的交易中,购买了面包和鸡蛋,依此类推。
包含 k 项的序列是一个 k-项序列。例如,序列(牛奶,面包)-> (鸡蛋)包含三个项。现在让我们一步一步地探索 SPADE 算法。
假设我们生成了以下序列。在第一个序列中,ID 1001,在第一个交易中就购买了牛奶。在第二个序列中,购买了牛奶、鸡蛋和面包。随后又购买了牛奶和面包。在第四个序列中,只购买了糖。在序列 1001 的最后一个交易中,购买了面包和苹果;这适用于所有相应的序列。例如,在序列 ID 1001 中,我们有多个事件。在第一次购买中,购买了牛奶。然后购买了(牛奶,鸡蛋,面包)等等。参见表 4.13。
表 4.13 序列挖掘的数据集
| 序列 ID | 序列 |
|---|---|
| 1001 | <(牛奶) (牛奶,鸡蛋,面包) (牛奶,面包) (糖) (面包,苹果)> |
| 1002 | <(牛奶,糖) (面包) (鸡蛋,面包) (牛奶,奶酪)> |
| 1003 | <(奶酪,苹果) (牛奶,鸡蛋) (糖,苹果) (面包) (鸡蛋)> |
| 1004 | <(奶酪,香蕉) (牛奶,苹果) (面包) (鸡蛋) (面包)> |
表 4.13 可以转换为如图表 4.14 所示的垂直数据格式。在这一步中,我们计算了一序列项的频率,这些是一序列项只有一项的序列。为此,只需要进行一次数据库扫描。我们只需为每个项目有序列 ID 和元素 ID。
表 4.14 表 4.13 的垂直格式
| 序列 ID | 元素 ID | 项目 |
|---|---|---|
| 1001 | 1 | 牛奶 |
| 1001 | 2 | 牛奶,鸡蛋,面包 |
| 1001 | 3 | 牛奶,面包 |
| 1001 | 4 | 糖 |
| 1001 | 5 | 面包,苹果 |
| 1002 | 1 | 牛奶,糖 |
| 1002 | 2 | 面包 |
| 1002 | 3 | 鸡蛋,面包 |
| 1002 | 4 | 牛奶,奶酪 |
| 1003 | 1 | 奶酪,苹果 |
| 1003 | 2 | 牛奶,鸡蛋 |
| 1003 | 3 | 糖,苹果 |
| 1003 | 4 | 面包 |
| 1003 | 5 | 鸡蛋 |
| 1004 | 1 | 奶酪,香蕉 |
| 1004 | 2 | 牛奶,苹果 |
| 1004 | 3 | 面包 |
| 1004 | 4 | 鸡蛋 |
| 1004 | 5 | 面包 |
表 4.14 实际上是表 4.13 的垂直表格表示。例如,在序列 ID 1001 中,元素 ID 1 我们有牛奶。对于序列 ID 1001,元素 ID 2 我们有牛奶、鸡蛋、面包等等。
为了解释的目的,我们只考虑两个项目——0 牛奶和鸡蛋——以及支持阈值 2。
然后,在下一步中,我们将对每个项目进行分解。例如,牛奶出现在序列 ID 1001 和元素 ID 1,序列 ID 1001 和元素 ID 2,序列 ID 1001 和元素 ID 3,序列 ID 1002 和元素 ID 1,等等。这导致了一个像表 4.15 那样的表格,其中我们展示了牛奶和鸡蛋。这需要应用于数据集中的所有项目。
表 4.15 牛奶和鸡蛋的相应序列 ID
| 牛奶 | 鸡蛋 |
|---|---|
| 序列 ID | 元素 ID |
| --- | --- |
| 1001 | 1 |
| 1001 | 2 |
| 1001 | 3 |
| 1002 | 1 |
| 1002 | 4 |
| 1003 | 2 |
| 1004 | 2 |
现在我们希望计算两个序列或具有两个项目序列的序列。我们可以有两个序列:要么是牛奶 -> 鸡蛋,要么是鸡蛋 -> 牛奶。让我们先从牛奶 -> 鸡蛋开始。
对于牛奶 -> 鸡蛋,我们需要在鸡蛋前面有牛奶。对于相同的序列 ID,如果牛奶的元素 ID 小于鸡蛋的元素 ID,则它是一个合格的序列。在上面的例子中,对于序列 ID 1001,牛奶的元素 ID 是 1,而鸡蛋的元素 ID 是 2。因此,我们可以将其作为第一个合格的配对添加到表 4.16 的第一行中。对于序列 ID 1002 也是如此。在表 4.15 的第 4 行中,我们有序列 ID 1002。牛奶的元素 ID 是 1,而第 2 行中鸡蛋的元素 ID 是 3。再次,牛奶的元素 ID 小于鸡蛋的元素 ID,因此它成为第二个条目,过程继续。关键是比较牛奶和鸡蛋的相应元素 ID 时要有相同的序列 ID。
表 4.16 牛奶和鸡蛋的序列
| 牛奶和鸡蛋 |
|---|
| 序列 ID |
| --- |
| 1001 |
| 1002 |
| 1003 |
| 1004 |
通过使用相同的逻辑,我们可以创建鸡蛋 -> 牛奶的表格,如表 4.17 所示。同样,关键是比较牛奶和鸡蛋的相应元素 ID 时要有相同的序列 ID。
表 4.17 鸡蛋和牛奶的序列
| 鸡蛋和牛奶 |
|---|
| 序列 ID |
| --- |
| 1001 |
| 1002 |
这可以应用于所有可能的组合。我们现在转向创建三项序列,我们将创建牛奶、鸡蛋 -> 牛奶。为此,我们必须合并两个表。参见表 4.18。
表 4.18 将牛奶 -> 鸡蛋和鸡蛋 -> 牛奶的序列组合以合并表
| 牛奶和鸡蛋 | 鸡蛋和牛奶 | |
|---|---|---|
| 序列 ID | 元素 ID(牛奶) | 元素 ID(鸡蛋) |
| --- | --- | --- |
| 1001 | 1 | 2 |
| 1002 | 1 | 3 |
| 1003 | 2 | 5 |
| 1004 | 2 | 4 |
连接的逻辑是匹配序列 ID 和元素 ID。我们分别用红色和绿色突出显示匹配项,尽管这不会在打印的书中显示出来。对于序列 ID 1001,左表中的鸡蛋元素 ID 与右表中的鸡蛋元素 ID 相匹配,这成为表 4.19 的第一条记录,显示了结果。同样,对于序列 ID 1002,元素 ID 3 相匹配。
表 4.19 分析所有商品组合后的最终表
| 牛奶,鸡蛋 -> 牛奶 |
|---|
| 序列 ID |
| --- |
| 1001 |
| 1002 |
此过程会继续进行。算法在找不到频繁序列时停止。
我们现在将使用 Python 在数据集上实现 SPADE。我们使用pyspade库,因此我们必须加载数据集并调用函数。它为我们生成结果。支持度保持为 0.6,然后我们打印结果(见图 4.23):
from pycspade.helpers import spade, print_result
spade_result = spade(filename='SPADE_dataset.txt', support=0.6, parse=True)
print_result(spade_result)

图 4.23 使用 Python 在pyspade库上实现的 SPADE
4.8 关联规则案例研究
关联规则挖掘是一种非常有帮助且强大的解决方案。接下来,我们将使用关联规则解决一个实际的案例研究。回想一下,在本章开头,我们建议你研究杂货店的模式。商店中这种安排的逻辑是什么?
考虑这种情况:你正在为像沃尔玛、特易购、Spar、玛莎百货等杂货零售商工作,并正在规划一家新店的视觉布局。显然,零售店必须明智地利用店内空间并达到最大容量。同时,确保顾客的流动不受阻碍也是至关重要的。顾客应该能够访问所有展示的商品,并且能够轻松地导航。你可能经历过一些商店,你感觉被展示品堵塞和轰炸,而另一些则堆叠得井井有条。
我们如何解决这个问题?可能有多个解决方案。一些零售商可能希望根据商品类别对商品进行分组。例如,他们可能希望将所有烘焙产品放在一个货架上,或者使用其他条件。我们在这里研究机器学习示例。
使用市场篮子分析,我们可以生成指示各种商品之间相应关系的规则。我们可以预测哪些商品经常一起购买,并将它们一起放在商店里。例如,如果我们知道牛奶和面包经常一起购买,那么面包就可以放在牛奶柜台附近。购买牛奶的顾客可以轻松找到面包并继续购物。
但这并不像听起来那么简单。让我们一步一步解决这个问题:
- 业务问题定义—第一步是定义业务问题,这对我们来说很清楚。我们希望发现各种商品之间的关系,以便改善商店的布局。在这里,商品陈列图就出现了。商品陈列图帮助零售商明智地规划商店空间的利用,以便顾客也能轻松地导航和获取产品。它可以被视为商店的视觉布局。一个例子在图 4.24 中展示。

图 4.24 商品陈列图示例。商品陈列图对视觉营销非常有用。
在图中,我们可以看到每个商品类别都有特定的区域。关联规则非常有洞察力,有助于为商品陈列图提供方向。
-
- 数据发现—下一步是数据发现,其中历史交易被搜索并加载到数据库中。通常,一笔交易可以看起来像表 4.20。请注意,将这种数据格式转换为关联规则算法可以消费的格式是一项相当大的挑战。
表 4.20 实际零售店生成的发票示例
| 发票号码 | 日期 | 商品 | 金额 |
|---|---|---|---|
| 1001 | 01-Jun-21 | 牛奶、鸡蛋、奶酪、面包 | $10 |
| 1002 | 01-Jun-21 | 面包、香蕉、苹果、黄油 | $15 |
| 1003 | 01-Jun-21 | 黄油、胡萝卜、奶酪、鸡蛋、面包、牛奶、香蕉 | $19 |
| 1004 | 01-Jun-21 | 牛奶 | $1 |
| 1005 | 01-Jun-21 | 面包 | $0.80 |
-
- 数据准备—这一步可能是最困难的步骤。正如我们所见,关联规则模型的创建是一个非常简单的任务。我们有库可以为我们完成繁重的工作。但它们期望的数据集具有特定的格式。这是一项繁琐的任务;它相当耗时,需要大量的数据预处理技能。
在准备数据集时,你应该注意以下几点:
-
-
有时在数据准备阶段我们会得到 NULL 或空白值。数据集中的缺失值在计算时可能会导致问题。在其他机器学习解决方案中,我们建议处理缺失值。在关联规则的情况下,我们建议忽略相应的交易,并在最终数据集中不考虑它们。
-
许多时候,我们在数据中会得到垃圾值。数据集中发现了像!@%^&*()_ 这样的特殊字符。这可以归因于系统中的错误输入。因此,需要数据清洗。我们在第十一章详细介绍了数据预处理步骤,其中我们处理 NULL 值和垃圾值。
-
将表格转换为可以被关联规则学习算法理解和消费的格式是一个必要但艰巨的步骤。通过了解 SQL 透视的概念来更好地理解这一概念。
-
-
- 模型准备—也许这是所有步骤中最简单的。我们已经为不同的算法解决了 Python 解决方案,所以你应该对此相当熟悉。
-
- 模型解释—创建模型可能很简单,但规则的解释并不容易。有时,你会遇到像#NA -> (Milk, Cheese)这样的规则。这样的规则显然是不可用的,也没有任何意义。它表明数据准备不正确,数据集中仍然存在一些垃圾值。另一个例子是(Some items) -> (Packaging material);这可能是最明显的规则,但同样不可用。这个规则表明,每次购物时,包装材料也会被购买。这不是很明显吗?最后一个例子是(Potatoes, Tomatoes) -> (Onions)。这种规则看起来可能是正确的,但常识告诉我们,零售商已经知道这一点。显然,大多数购买蔬菜的顾客都会一起购买土豆、番茄和洋葱。这样的规则可能不会给业务带来太多价值。
支持度、置信度、提升和确信度的阈值使我们能够过滤出最重要的规则。我们可以按提升的降序对规则进行排序,然后移除最明显的规则。
在每个步骤中都让业务利益相关者和领域专家参与至关重要。在本案例研究中,运营团队、视觉营销团队、产品团队和营销团队是关键参与者,每个步骤都应紧密协调。
-
- 改进商品陈列图—一旦生成了并接受了规则,我们就可以使用它们来改进零售空间的商品陈列图。零售商可以使用这些规则来改善营销策略和提升产品促销。例如,如果接受了一个像(A, B) -> (C)这样的规则,零售商可能希望创建一个产品组合并将其作为一个单一实体出售。这将增加同一交易中购买的平均商品数量。
这个案例研究可以扩展到任何其他领域或业务功能。例如,如果我们希望检查用户在网页间的移动,可以使用相同的步骤。网站开发者可以分析客户在其网站上的历史点击和使用情况。通过识别模式,他们可以找出用户倾向于点击哪些内容,以及哪些功能将最大化他们的参与度。医疗从业者可以使用关联规则来更好地诊断患者。医生可以比较症状与其他症状之间的概率,并提供更准确的诊断。
4.9 结论性思考
我们研究的关联规则和序列规则中存在一些假设和限制:
-
在生成规则时,我们忽略了项目的相对重要性。例如,如果客户在一次交易中购买了五罐牛奶和 1 公斤的苹果,这被处理得与购买一罐牛奶和 5 公斤苹果的发票类似。因此,我们应该记住,项目的相对权重没有被考虑。
-
项目的成本表明了产品的感知价值。一些昂贵的商品更重要,因此,如果客户购买这些商品,可以产生更多的收入。在分析发票时,我们忽略了与项目相关的成本。
-
在分析序列时,我们没有考虑两次交易之间的相应时间段。例如,如果在 T1 和 T2 之间有 10 天,而在 T2 和 T3 之间有 40 天,这两者都被视为相同。
-
在所有分析中,我们都将不同的类别视为相同。易腐物品和非易腐物品被以类似的方式处理。例如,保质期为两到三天的鲜奶与保质期较长的洗衣粉被同样对待。
-
许多时候,我们在分析后收到不有趣的规则。这些结果来自常识(土豆,番茄)->(洋葱)。这样的规则用处不大。我们经常面临这样的问题。
-
虽然不有趣的规则是一个挑战,但发现的大量规则又是问题之一。我们得到数百条规则,理解和分析每一条都变得困难。在这里,阈值变得很有用。
-
计算的时间和内存需求巨大。算法需要多次扫描数据集,因此这是一项相当费时的练习。
-
生成的规则依赖于用于分析的数据库。例如,如果我们只分析夏季生成的数据库,我们无法使用冬季的规则,因为消费者在不同天气条件下的偏好会发生变化。此外,我们应该随着时间的推移刷新算法,因为随着时间的推移,宏观经济和微观经济因素会发生变化,因此算法也应该刷新。
还有一些其他算法也非常有趣。对于关联规则,我们可以有多关系关联规则、k-最优模式发现、近似频繁数据集、广义关联规则、高阶模式发现等。对于序列挖掘,我们有广义序列模式、FreeSpan、PrefixSpan、挖掘关联模式等。这些算法非常有趣,可以用于知识增强。
关联规则和序列挖掘是非常有趣的话题。各个商业领域和功能越来越多地使用关联规则来理解事件模式。这些洞察力使团队能够做出明智和科学的决策,以改善客户体验和整体参与度。在本章中,我们探讨了关联规则和序列挖掘。这些研究使用了 Apriori、F-P 和 ECLAT 算法,而对于序列挖掘,我们使用了 SPADE。
4.10 实际下一步行动和建议阅读
以下提供了一些下一步行动的建议和一些有用的阅读材料:
-
阅读以下关于关联规则算法的研究论文:
-
快速发现关联规则:
mng.bz/eyqv -
快速挖掘关联规则的算法:
mng.bz/64GZ -
模式和关联规则挖掘方法的效率分析:
arxiv.org/pdf/1402.2892.pdf -
关于关联规则挖掘技术的隐私保护能力的综述:
mng.bz/0Q0N
-
-
对于序列挖掘,阅读以下研究论文:
-
SPADE:挖掘频繁序列的高效算法:
mng.bz/9YG7 -
序列挖掘:模式和算法分析:
arxiv.org/pdf/1311.0350.pdf -
基于兴趣度的序列挖掘算法:
ieeexplore.ieee.org/document/8567170 -
解决序列模式挖掘问题的新方法:
mng.bz/jpxr
-
摘要
-
关联规则学习识别数据集中变量之间的关系,例如啤酒和尿布的例子。
-
通过数据分析,这些关联可以告知营销策略、超市中的产品摆放和定价。
-
零售业中的购物篮分析使用关联规则来发现购买模式,并适用于其他行业,如生物信息学。
-
关联规则由导致后果的前件组成,表示为 P -> Q,它们之间没有共同元素。
-
规则的重要性取决于支持度(频率)、置信度(准确性)、提升度(依赖度测量)和确信度。
-
高支持度、置信度、提升度和确信度表明规则更强、更有用。
-
Apriori 算法使用“自下而上”的方法生成关联规则的项集,但在处理大数据集时面临挑战。
-
ECLAT 算法使用深度优先搜索以实现频繁项集的快速、内存高效的计算。
-
F-P 增长算法通过使用频繁模式树来消除候选生成,在 Apriori 算法的基础上进行了改进。
-
序列规则挖掘有助于解释用户随时间的行为,与时间序列分析不同。
-
SPADE 算法分析事件序列和随时间变化的依赖关系,以进行序列规则挖掘。
-
使用适当的库可以实现 Apriori、ECLAT、F-P 增长和 SPADE 算法的 Python 实现。
-
对于支持度、置信度和提升度的评估指标和阈值设置对于高效规则生成至关重要。
-
序列规则挖掘在市场营销、生物信息学和用户交互分析中具有应用,允许获得可操作的见解。
第五章:聚类
本章涵盖
-
谱聚类
-
模糊聚类
-
高斯混合模型聚类
从复杂中寻找简单。——爱因斯坦
有时生活非常简单,有时我们却会经历相当复杂的情况。我们可以在这两种情况下航行,并根据需要改变我们的方法。
在第一部分,我们介绍了基础知识,为您的旅程做准备。我们现在处于第二部分,它比第一部分稍微复杂一些。第三部分将比前两部分更高级。因此,请仔细关注即将到来的章节,因为在这里获得的技能和知识将为本书后面的章节做好准备。
在开始本章之前,我们应该回顾一下我们在第二章中学到的内容。在本书的第一部分,我们研究了聚类算法。在第二章中,我们了解到聚类是一种无监督学习技术,我们希望通过在数据集中发现有趣的模式来对数据点进行分组。我们探讨了聚类解决方案的含义以及不同类别的聚类算法,并查看了一个案例研究。在那个章节中,我们深入探讨了 k-means 聚类、层次聚类和 DBSCAN 聚类。我们了解了每个算法的数学背景、过程和 Python 实现,以及每个算法的优缺点。
您可能会遇到不符合简单形状和形式的数据库。此外,在做出选择最终要实现的算法之前,我们必须找到最佳匹配。在这种情况下,我们可能需要更复杂的聚类算法的帮助——这正是本章的主题。在本章中,我们将再次研究三种这样的复杂聚类算法:谱聚类、模糊聚类和高斯混合模型(GMM)聚类。像往常一样,Python 实现将遵循数学和理论概念。本章在数学概念上稍微有些繁重。不需要成为高级数学学生,但有时理解算法在背后的工作方式是很重要的。同时,您可能会惊讶地发现,这些算法的 Python 实现并不繁琐。本章没有案例研究。
欢迎来到第五章,祝您一切顺利!
5.1 技术工具箱
我们将继续使用迄今为止我们所使用的相同版本的 Python 和 Jupyter Notebook。本章中使用的代码和数据集已在 GitHub 上检查过(mng.bz/6epo)。
我们将使用迄今为止我们已经使用的常规 Python 库:numpy、pandas、sklearn、seaborn、matplotlib等。在本章中,您还需要安装两个其他的 Python 库:skfuzzy和network。使用这些库,我们可以非常快速地实现算法。否则,编写这些算法是一项相当耗时且费力的任务。
让我们从对聚类的回顾开始吧!
5.2 聚类:简要回顾
从第二章回顾,聚类用于将相似的对象或数据点分组。这是一种无监督学习技术,我们的目的是在数据中找到自然的分组,如图 5.1 所示。
在这里,我们可以观察到左侧是未分组的数据,而右侧数据点已经被分组到逻辑组中。我们还可以观察到有两种方法可以进行分组或聚类,并且两者都会产生不同的簇。作为一项技术,聚类在商业解决方案中得到了广泛的应用,如客户细分、市场细分等。
我们在第二章学习了 k-means、层次聚类和 DBSCAN 聚类。我们还介绍了各种距离测量技术以及衡量聚类算法性能的指标。建议您重新回顾这些概念。
在本章中,我们专注于高级聚类方法。下一节我们将从谱聚类开始。

图 5.1 对象结果聚类成自然分组
5.3 谱聚类
谱聚类是一种独特的聚类算法,该领域已经进行了大量的研究。尊敬的研究者包括杨安德鲁教授、迈克尔·乔丹教授、亚伊尔·维斯教授、石建波教授和吉滕德拉·马利克教授等。我们在本章末尾提供了他们一些论文的链接。
谱聚类在聚类数据点时考虑的是亲和力而非数据点的绝对位置。当我们考虑点的绝对位置时,相似性仅仅是基于点之间的距离,而亲和力考虑的是点之间的相似性。如果点之间的亲和力为 0,则它们是不相似的,而如果亲和力为 1,则它们是非常相似的。因此,无论数据处于何种复杂形状(即数据点之间存在某种特殊关系),谱聚类都是解决方案。我们在图 5.2 中展示了几个谱聚类可以提供逻辑解决方案的示例。

图 5.2 谱聚类可以聚类的各种复杂数据形状示例
对于图 5.2,我们也可以使用其他算法,如 k-means 聚类。但它们可能无法公正地处理这样复杂的数据形状。您可以从图 5.2 中看到,各种数据点遵循某种模式。像 k-means 聚类这样的算法利用数据点的紧凑性,并由各个簇的中心点驱动。换句话说,点之间的接近性和对簇中心的紧凑性驱动了 k-means 聚类。另一方面,在谱聚类中,连通性是驱动逻辑。在连通性中,数据点要么彼此立即接近,要么以某种方式连接。图 5.2 中展示了基于连通性的聚类的一些示例。内圈中的点属于一个簇,而外圈中的点属于另一个簇。
现在看看图 5.3 中的第一个图,其中数据点呈环形模式。可能有遵循这种环形模式的数据点。我们需要聚类这些数据,这确实是一个复杂的模式。想象一下,通过使用聚类方法,方框内的圆圈被纳入同一个簇,如图 5.3 中间的图所示。毕竟,它们彼此接近。但如果我们仔细观察,点呈圆形和某种模式,因此实际的簇应该如图 5.3 最右边的图所示。

图 5.3 我们可以对需要聚类的数据点进行复杂表示。观察左边的环形形状。一种解释可以是,方框内的点基于距离应该是同一个簇的一部分,但显然它们不是同一个簇的一部分(中间)。这里有两个圆圈。内圈中的点属于一个簇,而外圈中的点属于另一个簇(右边)。
图 5.3 所示的示例展示了谱聚类相对于 k-means 聚类的优势。在第二幅图中,红色点(印刷书中方框内的点)将被错误地聚类到不同的簇中,而在第三幅图中显示了正确的聚类。谱聚类可能会将内圈的数据分组到单独的簇中。
如我们之前所述,谱聚类利用了连通性方法。在谱聚类中,在图中识别出相邻的数据点。这些数据点有时被称为节点。然后,这些数据点或节点被映射到低维空间。低维空间是指具有较少输入特征的空间。在这个过程中,谱聚类使用从数据集中派生的特征值、亲和矩阵、拉普拉斯矩阵和度矩阵。然后,低维空间可以被分割成簇。
注意:谱聚类利用连接方法进行聚类。它依赖于图论,其中我们根据连接它们的边来识别节点簇。
我们将详细研究这个过程。但首先,有一些重要的数学概念构成了谱聚类的基础,我们将现在介绍这些概念。
5.3.1 谱聚类的构建块
我们知道,聚类的目标是把相似的数据点分组到一个簇中,把不相似的数据点分组到另一个簇中。一个重要的数学概念是相似性图,它是数据点的表示。
相似性图
图形是表示数据点的直观方法之一。图 5.4 中的第一个图表展示了一个图形的例子,它仅仅是表示数据点的边之间的连接。如果两个数据点之间的相似性为正或者超过某个阈值,这两个数据点就是相连的,这在第二个图表中有展示。我们不仅可以使用相似性的绝对值,还可以使用权重。因此,在图 5.4 的第二个图表中,由于点 1 和点 2 比点 1 和点 3 更相似,点 1 和点 2 之间的连接权重比点 1 和点 3 之间的连接权重更高。

图 5.4 图形是数据点的简单表示。如果点或节点非常相似,它们将通过边连接(左)。如果数据点之间的相似性高,权重就高;对于不相似的数据点,权重就低(右)。
因此,我们可以这样说,使用相似性图,我们希望将数据点聚类,使得数据点的边具有
-
较高的权重值因此彼此相似,并且因此属于同一个簇
-
较低的权重值,因此彼此不相似,并且因此属于不同的簇
除了相似性图之外,我们还应该了解特征值和特征向量的概念,这些我们在上一章中已经详细介绍了。如果你需要的话,建议你复习一下。
邻接矩阵
仔细观察图 5.5。我们可以看到从 1 到 5 的这些不同点都是相连的。我们用矩阵来表示这种连接。这个矩阵被称为邻接矩阵。在邻接矩阵中,行和列分别是相应的节点。矩阵内的值代表连接:如果值是 0,这意味着没有连接,如果值是 1,这意味着有连接。

图 5.5 邻接矩阵表示了各个节点之间的连接。节点 1 和节点 5 之间存在连接;因此,对应的值是 1。节点 1 和节点 4 之间没有连接;因此,相应的值是 0。
因此,对于邻接矩阵,我们只关心两个数据点之间是否有连接。按照我们定义边的方式(作为非定向的),矩阵总是对称的。这是因为如果从 1 到 2 有连接,那么从 2 到 1 也必须有连接,如果没有 3 和 1 之间的连接,那么 1 和 3 之间也没有连接。如果我们扩展邻接矩阵的概念,我们得到度矩阵,这是我们接下来要讨论的主题。
度矩阵
度矩阵是一个对角矩阵,其中对角线上的节点度数是与之相连的边的数量。如果我们使用之前相同的例子,我们得到图 5.6 所示的度矩阵。节点 3 和 5 各有三个连接,所以它们在对角线上的值是 3;其他节点每个只有两个连接,所以它们在对角线上的值是 2。

图 5.6 虽然邻接矩阵表示了各个节点之间的连接,但度矩阵是表示每个节点连接数的。它显示在矩阵的对角线上。例如,节点 5 有三个连接,因此在邻接矩阵中的值是 3,而节点 1 只有两个连接,所以它的值是 2。
你可能会想:我们为什么要使用这些矩阵呢?矩阵提供了数据的一种优雅的表示形式,并且可以清楚地描绘两点之间的关系。此外,计算机更容易处理矩阵表示,而不是其他操作图的方法。
现在我们已经涵盖了邻接矩阵和度矩阵,我们可以转向拉普拉斯矩阵。
拉普拉斯矩阵
拉普拉斯矩阵有许多变体,但如果我们采用最简单的形式,它不过是邻接矩阵从度矩阵中减去——换句话说,L = D – A。我们可以像图 5.7 中所示的那样演示它。

图 5.7 拉普拉斯矩阵非常容易理解。要得到拉普拉斯矩阵,我们可以简单地从度矩阵中减去邻接矩阵,如图例所示。在这里,D 代表度矩阵,A 是邻接矩阵,L 是拉普拉斯矩阵。
拉普拉斯矩阵是一个重要的概念,我们利用 L 的特征值来发展谱聚类。一旦我们得到了特征值和特征向量,我们还可以定义另外两个值:谱间隙和 Fielder 值。第一个非零特征值是谱间隙,它定义了图的密度。Fielder 值是第二个特征值;它提供了将图分割成两个部分所需的最小割的近似。与 Fielder 值对应的向量被称为Fielder 向量。
注意:Fielder 向量既有负分量也有正分量,它们的代数和为零。
在我们详细研究下一节中光谱聚类的过程时,我们将使用这个概念。在继续到光谱聚类的过程之前,我们再介绍一个概念——亲和矩阵。
亲和矩阵
在邻接矩阵中,如果我们用权重的相似度代替连接的数量,我们将得到亲和矩阵。如果点完全不相似,亲和度将为 0;如果它们完全相似,亲和度将为 1。矩阵中的值表示数据点之间相似度的不同级别。
练习 5.1
回答这些问题以检查你的理解:
-
度矩阵是通过计算连接的数量来创建的。对还是错?
-
拉普拉斯矩阵是度矩阵和邻接矩阵除法的转置。对还是错?
-
在纸上画一个图,然后推导出其邻接矩阵和度矩阵。
5.3.2 光谱聚类过程
现在我们已经涵盖了光谱聚类的所有构建块。从高层次来看,各个步骤可以总结如下:
-
我们获取数据集并计算其度矩阵和邻接矩阵。
-
使用它们,我们计算拉普拉斯矩阵。
-
然后,我们计算拉普拉斯矩阵的前 k 个特征向量。k 个特征向量对应于 k 个最小的特征值。
-
形成的矩阵用于在 k 维空间中对数据点进行聚类。
注意:关于特征值、亲和矩阵和拉普拉斯矩阵的更多清晰信息,请参阅附录。
我们通过一个示例来介绍光谱聚类的过程,如图 5.8 所示。这些步骤在现实世界的实现中通常不会执行,因为我们有包和库来实现这些功能,但在这里介绍它们是为了让你了解算法是如何从头开始开发的,以及它是如何工作的,以便你更好地理解如何有效地利用它。对于 Python 解决方案,我们将仅使用库和包。虽然从头开始开发实现是可能的,但重新发明轮子并不节省时间。

图 5.8 考虑图 5.8 所示的示例,其中我们有一些数据点,并且它们是连接的。我们将对此数据进行光谱聚类。
当我们希望对此数据进行光谱聚类时,我们将遵循以下步骤:
-
创建邻接矩阵和度矩阵。我们将把这个步骤留给你。
-
创建拉普拉斯矩阵(见图 5.9)。

图 5.9 数据的拉普拉斯矩阵。建议你创建度矩阵和邻接矩阵,并检查输出。
-
- 创建 Fielder 向量,如图 5.10 所示,用于前面的拉普拉斯矩阵。我们创建 Fielder 向量,如拉普拉斯矩阵部分所述。观察矩阵的总和为零。

图 5.10 Fielder 向量是拉普拉斯矩阵的输出。
-
- 我们可以看到有一些正值和一些负值。基于这些正值或负值,我们可以创建两个不同的簇。图 5.11 展示了谱聚类的过程。

图 5.11 识别出两个簇。这是一个非常简单的例子,用于说明谱聚类的过程。
谱聚类在图像分割、语音分析、文本分析、实体解析等方面非常有用。这种方法不对数据的形状做出任何假设。像 k-means 这样的方法假设点围绕簇的中心呈球形分布,而谱聚类中并没有这样的强假设。
另一个显著的不同之处在于,与那些以紧凑性驱动聚类的其他方法相比,在谱聚类中,数据点不需要有凸边界。由于需要计算各种矩阵及其特征值、拉普拉斯矩阵等,谱聚类有时会较慢。对于大数据集,复杂性增加,因此谱聚类可能会变慢,但当我们拥有稀疏数据集时,它是一种快速的方法。
谱聚类需要构建一个矩阵,其大小理论上等于数据集中元素数量的平方,因为每个元素都有一个列和一个行。例如,一个包含几百万个元素的中等数据集将需要一个包含数万亿元素的矩阵!直接存储这样一个矩阵需要数 TB 的 RAM,这是非常强大且昂贵的服务器所能做到的边缘。有一些技术可以减轻内存需求(例如,不单独存储每个元素),但它们使得与矩阵的工作变得更加复杂。此外,找到这样一个大矩阵的特征值甚至一个特征向量都是非常耗时的。因此,对于小数据集,谱聚类是一种可行的解决方案。
我们现在将进入对谱聚类算法的 Python 解决方案。
5.4 谱聚类的 Python 实现
我们已经涵盖了谱聚类的细节——现在是时候进入代码部分了。为此,我们将创建一个人工数据集,运行 k-means 算法和谱聚类来比较结果。步骤如下:
- 导入所有必要的库。这些库是标准的,除了我们将要介绍的一些。
sklearn是最著名和最受欢迎的库之一,我们从sklearn中导入SpectralClustering、make_blobs和make_circles:
from sklearn.cluster import SpectralClustering
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
from sklearn.datasets import make_circles
from numpy import random
import numpy as np
from sklearn.cluster import SpectralClustering, KMeans
from sklearn.metrics import pairwise_distances
from matplotlib import pyplot as plt
import networkx as nx
import seaborn as sns
-
- 整理数据集。我们将使用
make_circles方法。在这里,我们取 2000 个样本并将它们表示为一个圆圈。输出如下(见图 5.12):
- 整理数据集。我们将使用
data, clusters = make_circles(n_samples=2000, noise=.01, factor=.3, random_state=5)
plt.scatter(data[:,0], data[:,1])

图 5.12 使用make_circles方法整理数据集
-
- 使用 k-means 聚类测试这个数据集。两种颜色表示两个不同的簇,它们相互重叠。本书的打印版不会显示颜色,但 Python 代码的输出会显示。相同的输出可以在 GitHub 仓库中找到(见图 5.13):
kmeans = KMeans(init='k-means++', n_clusters=2)
km_clustering = kmeans.fit(data)
plt.scatter(data[:,0], data[:,1], c=km_clustering.labels_, cmap='prism',
alpha=0.5, edgecolors='g')

图 5.13 使用 k-means 聚类测试数据集
-
- 使用谱聚类运行相同的数据。我们发现这里(见图 5.14)两个簇被分别处理:
spectral = SpectralClustering(n_clusters=2, affinity='nearest_neighbors', random_state=5)
sc_clustering = spectral.fit(data)
plt.scatter(data[:,0], data[:,1], c=sc_clustering.labels_, cmap='prism', alpha=0.5, edgecolors='g')

图 5.14 在使用谱聚类时,两个簇被分别处理。
我们可以观察到,两个算法以不同的方式处理相同的数据集。谱聚类在处理数据集方面更有优势,因为分离的圆被单独表示。
-
- 通过改变数据集中的值并运行算法来模拟各种情况。观察不同的输出以进行比较。
5.5 模糊聚类
到目前为止,我们已经介绍了很多聚类算法。你是否想知道为什么数据点应该只属于一个簇?为什么数据点不能属于多个簇?看看图 5.15:右侧图像中的红色点(打印版本中用 x 表示)可以属于多个簇。

图 5.15 左侧的图表示所有数据点。红色点(打印版本中带有 x 的点)可以属于多个簇。实际上,我们可以为每个点分配多个簇。可以为点分配一个概率分数,以表示它属于特定的簇。
我们知道,聚类是根据相似性将项目分组到凝聚性组中的。相似的项目在一个簇中,而不相似的项目在不同的簇中。聚类的想法是确保同一簇中的项目相似。当项目只能属于一个簇时,这被称为硬聚类。K-means 聚类是硬聚类的经典例子。但如果我们回顾图 5.15,我们可以观察到项目可以属于多个簇。这被称为软聚类。
注意:创建模糊边界比创建硬簇在计算上更便宜。
在模糊聚类中,一个项目可以被分配到多个簇中。与位于簇边缘的点相比,靠近簇中心的点将对该簇有更强的归属感。这被称为隶属度。它使用最小二乘算法来找到项目最理想的位置。我们从最小二乘算法中得出的理想位置将是两个或多个簇之间的概率空间。我们将在稍后详细探讨这个概念。
5.5.1 模糊聚类的类型
模糊聚类可以进一步分为经典模糊算法和基于形状的模糊算法。见图 5.16。

图 5.16 模糊算法可以分为经典模糊算法和基于形状的模糊算法。
我们将在下一节详细介绍模糊 c-均值(FCM)算法,但首先我们将简要回顾其他算法:
-
Gustafson-Kessel 算法,有时称为 GK 算法,通过将一个项目与一个簇和一个矩阵关联起来来工作。GK 导致椭圆形簇,为了根据数据集中的不同结构进行修改,GK 使用协方差矩阵。它允许算法捕捉簇的椭圆形特性。GK 可以导致更窄的簇,并且当项目数量更多时,这些区域可以更薄。
-
Gath-Geva 算法不是基于目标函数的。簇可以形成任何形状,因为它是对统计估计量的模糊化。
-
基于形状的聚类算法根据其名称是自解释的。一个圆形模糊聚类算法将导致圆形簇,依此类推。
FCM 算法是最受欢迎的模糊聚类算法。它最初由 J.C. Dunn 在 1973 年开发,并且已经多次改进。它与 k-means 聚类非常相似。
参考图 5.17。在图的第一个部分(左侧),我们有某些项目或数据点。这些数据点可以是聚类数据集的一部分,如客户交易等。在图的第二部分(中间),我们为这些数据点创建一个簇。在创建这个簇的同时,为每个数据点分配隶属度等级。这些隶属度等级表明数据点属于簇的程度或水平。我们将很快检查计算这些值的数学函数。
TIP 不要被度数和概率弄混淆。如果我们把这些度数加起来,我们可能不会得到 1,因为这些值对于所有项目都是归一化的,介于 0 和 1 之间。
在图的三部分(右侧)中,我们可以看到点 1 更接近簇中心,因此比点 2 更有可能属于簇,点 2 更接近簇的边界或边缘。

图 5.17 可聚类的数据点(左侧)。数据点可以分成两个簇。对于第一个簇,簇中心用加号表示(中间)。与点 2 相比,点 1 更接近簇中心。因此,我们可以得出结论,点 1 比簇 2 更有可能属于这个簇。
我们现在将深入探讨算法的技术细节。这可能会变得有点数学化。
考虑我们有一组 n 个项目(方程式 5.1):
(5.1)
x = {x[1], x[2], x[3], x[4], x[5], . . ., x[n]}
我们将这些项目应用于 FCM 算法。这些 n 个项目根据某些标准被聚类成 c 个模糊簇。比如说,我们将从算法中得到一个包含 c 个簇中心的列表(方程 5.2):
(5.2)
c = {c[1], c[2], c[3], c[4], c[5], . . ., c[c]}
算法还返回一个划分矩阵,它可以定义为方程 5.3:

(5.3)
在这里,w[i][,][j] 中的每个元素是 X 中每个元素属于簇 c[j] 的程度。这就是划分矩阵的目的。
从数学上讲,我们可以根据方程 5.4 得到 w[i][,][j]。方程的证明超出了本书的范围。

(5.4)
算法也会为簇生成质心。簇的质心是该簇中所有点的平均值,平均值由它们各自属于该簇的程度加权。如果我们用数学方式表示,我们可以写成方程 5.5 中的样子:

(5.5)
在方程 5.4 和 5.5 中,我们有一个非常重要的项:m。m 是用于控制簇的模糊度的超参数。m 的值 ≥ 1,通常可以保持为 2(一个常用的值)。
NOTE:m 的值越高,簇的模糊度就越大。
现在我们检查 FCM 算法的逐步过程:
-
就像在 k-means 聚类中开始一样,选择我们希望在输出中拥有的簇的数量。
-
将权重随机分配给每个数据点。
-
算法会迭代直到收敛。回想一下 k-means 算法是如何收敛的,其中我们通过随机分配簇的质心来开始这个过程。然后我们迭代地细化每个簇的质心,直到我们得到收敛。这就是 k-means 算法的工作原理。对于 FCM,我们将利用一个类似的过程,尽管有一些细微的差别。我们添加了一个成员值 w[i][,][j] 和 m。
-
对于 FCM,为了算法收敛,我们根据方程 5.6 计算每个簇的中心:

(5.6)
-
- 对于每个数据点,我们也计算其属于该特定簇的相应系数。我们将使用方程 5.4。
-
- 现在我们应该迭代,直到 FCM 算法收敛。我们希望最小化的成本函数由方程 5.7 给出:

(5.7)
一旦这个函数被最小化,我们就可以得出结论,FCM 算法已经收敛。换句话说,我们可以停止这个过程,因为算法已经完成了处理。
现在是对比 k-means 算法的好时机。在 k-means 算法中,我们有一个严格的客观函数,它只允许一个簇成员资格,而对于 FCM 聚类,我们可以根据概率分数得到不同的聚类成员资格。
FCM 对于聚类边界不清晰且严格的商业案例非常有用。考虑生物信息学领域,其中基因可以属于多个基因簇。另一个例子是我们有重叠数据集,如市场营销分析或图像分割领域,我们可能有很多复杂、重叠和令人困惑的数据集。FCM 比 k-means 可以得到更稳健的结果。
我们现在将使用库来求解 FCM 聚类问题的 Python 解决方案。
练习 5.2
回答这些问题以检查你的理解:
-
模糊聚类使我们能够创建重叠簇。对还是错?
-
一个数据点只能属于一个簇。对还是错?
-
如果m的值较低,我们得到的簇边界更精确。对还是错?
5.5.2 FCM 的 Python 实现
我们已经涵盖了 FCM 的过程。现在我们将按照以下步骤在 Python 中实现 FCM:
- 导入必要的库:
import skfuzzy as fuzz
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
-
- 声明一个调色板,稍后将用于对簇进行着色:
color_pallete = ['r','m','y','c', 'brown', 'orange','m','k',
'gray','purple','seagreen']
-
- 定义聚类中心:
cluster_centers = [[1, 1],
[2, 4],
[5, 8]]
-
- 分配权重:
sigmas = [[0.5, 0.6],
[0.4, 0.5],
[0.1, 0.6]]
-
- 设置随机种子,然后遍历聚类中心:
np.random.seed(5)
xpts = np.zeros(1)
ypts = np.zeros(1)
labels = np.zeros(1)
for i, ((xmu, ymu), (xsigma, ysigma)) in enumerate(zip(cluster_centers,
sigmas)):
xpts = np.hstack((xpts, np.random.standard_normal(500) * xsigma + xmu))
ypts = np.hstack((ypts, np.random.standard_normal(500) * ysigma + ymu))
labels = np.hstack((labels, np.ones(500) * i))
-
- 我们首先表示数据点。见图 5.18:
fig0, ax0 = plt.subplots()
for label in range(5):
ax0.plot(xpts[labels == label], ypts[labels == label], '.')
ax0.set_title('Data set having 500 points.')
plt.show()

图 5.18 数据点的表示
-
- 使用不同的聚类值和 FPC(见图 5.19)迭代不同的输出:

图 5.19 FCM 算法的输出
fig1, axes1 = plt.subplots(3, 3, figsize=(10, 10))
alldata = np.vstack((xpts, ypts))
fpcs = []
for ncenters, ax in enumerate(axes1.reshape(-1), 2):
cntr, u, u0, d, jm, p, fpc = fuzz.cluster.cmeans(
alldata, ncenters, 2, error=0.005, maxiter=1000, init=None)
# Store fpc values for later
fpcs.append(fpc)
# Plot assigned clusters, for each data point in training set
cluster_membership = np.argmax(u, axis=0)
for j in range(ncenters):
ax.plot(xpts[cluster_membership == j],
ypts[cluster_membership == j], '.', color=colors[j])
# Mark the center of each fuzzy cluster
for pt in cntr:
ax.plot(pt[0], pt[1], 'rs')
ax.set_title('cluster_centers = {0}; FPC = {1:.2f}'.format(ncenters,
fpc), size=12)
ax.axis('off')
fig1.tight_layout()
观察代码的输出,对于相同的数据集,你可以看到不同位置的中心的不同簇。要欣赏颜色,你必须运行代码。
5.6 高斯混合模型
接下来,我们继续讨论软聚类。回想一下,我们在本章开头介绍了 GMM。现在我们将研究这个概念,并查看其 Python 实现。
首先,让我们了解高斯分布,有时也称为正态分布。你可能认识它为钟形曲线;它通常指的是同一件事。
在图 5.20 中,观察均值μ为 0 且标准差σ²为 1 的分布。这是一个完美的正态分布曲线。比较这里不同曲线的分布。

图 5.20 高斯分布是最著名的分布之一。观察均值和标准差的变化及其对相应曲线的影响。
高斯分布的数学表达式是


(5.8)
该方程也称为概率密度函数。在图 5.20 中,观察µ为 0 和σ²为 1 的概率分布的形状。这是一个完美的正态分布曲线。比较图 5.20 中不同曲线的分布,通过改变均值和标准分布的值,我们得到不同的图形。
你可能想知道为什么我们在这里使用高斯分布。有一个非常著名的统计定理叫做中心极限定理。该定理表明,如果数据的变异性是由大量无关的原因引起的,那么分布可以被近似为高斯曲线。此外,随着收集的数据越来越多,近似变得越来越准确;也就是说,我们收集的数据越多,分布就越接近高斯分布。这种正态分布可以在生活的各个方面以及化学、物理、数学、生物学或任何其他科学分支中观察到。这就是高斯分布的美丽之处。
图 5.20 中所示的是 2D 图。我们也可以有多维高斯分布。在多维高斯分布的情况下,我们将得到如图 5.21 所示的 3D 图。我们的输入是一维的标量。现在,我们的输入不再是标量,而是一个向量;均值也是一个向量,代表数据的中心。因此,均值具有与输入数据相同的维度。方差现在是对角矩阵Σ。这个矩阵不仅告诉我们输入中的方差,还评论了不同变量之间的关系——例如,如果改变x的值,y的值会如何受到影响。看看图 5.21。我们可以在这里理解x和y变量之间的关系。

图 5.21 高斯分布的 3D 表示
注意:协方差在这里起着重要的作用。K-means 没有考虑数据集的协方差,这在 GMM 模型中是使用的。
让我们考察 GMM 聚类的过程。想象我们有一个包含n个项目的数据集。当我们使用 GMM 聚类时,我们不是使用质心方法来找到簇;相反,我们将一组k个高斯分布拟合到当前的数据集中。换句话说,我们有k个簇。我们应该确定每个高斯分布的参数,这些参数是簇的均值、方差和权重。一旦确定了每个分布的参数,我们就可以找到每个n个项目属于k个簇的相应概率。
从数学上讲,我们可以根据方程 5.9 计算概率。该方程用于我们知道一个特定的点x是k个高斯函数的线性组合。术语f[j]用于表示高斯函数的强度,可以在第二个方程中看到,这种强度的总和等于 1。


(5.9)
对于谱聚类,我们必须确定f、∑和µ的值。正如你可以想象的那样,获取这些参数的值可能很棘手。这确实是一个稍微复杂的过程,称为期望最大化(EM)技术,我们将在下一节中介绍。本节数学概念较多,是可选的。建议对理解技术深层工作原理感兴趣的读者阅读。
5.6.1 EM 技术
EM 是一种确定模型正确参数的统计方法。有许多技术很受欢迎;最大似然估计可能是最著名的。但与此同时,最大似然可能也有一些挑战。数据集可能存在缺失值,或者换句话说,是不完整的。或者,数据集中的某个点可能是由两个不同的高斯分布生成的。因此,很难确定哪个分布生成了该数据点。在这里,EM 可能会有所帮助。
注意:K-means 只使用均值,而 GMM 利用数据的均值和方差。
在过程中生成的变量被称为潜在变量。由于我们不知道这些潜在变量的确切值,EM 首先使用当前数据估计它们的最佳值。一旦完成,然后估计模型参数。使用这些模型参数,再次确定潜在变量。然后,使用这些新的潜在变量,推导出新的模型参数。这个过程会一直持续,直到获得一组足够好的潜在值和模型参数,使得数据拟合良好。现在让我们更详细地研究一下。我们将使用与上一节相同的例子。
假设我们有一个包含n个项目的数据集。如前所述,当我们使用 GMM 聚类时,我们不是使用质心方法找到簇;相反,我们将一组k个高斯分布拟合到当前的数据集中。换句话说,我们有k个簇。我们确定每个高斯分布的参数(均值、方差和权重)。比如说,均值是µ[1]、µ[2]、µ[3]、µ[4]……µ[k],协方差是∑[1]、∑[2]、∑[3]、∑[4]……∑[k]。我们还可以有一个额外的参数来表示分布的密度或强度,它可以表示为f。
我们从期望,或者说 E 步骤开始。在这个步骤中,每个数据点以概率分配到簇中。因此,对于每个点,我们计算它属于簇的概率;如果这个值很高,那么这个点就在正确的簇中;否则,这个点就在错误的簇中。换句话说,我们计算每个数据点是由每个k个高斯分布生成的概率。
注意:由于我们正在计算概率,这些被称为软分配。
概率是使用方程 5.10 中的公式计算的。如果我们仔细观察,分子是概率,然后我们通过分母进行归一化。

(5.10)
在期望步骤中,对于一个数据点 x[i][,][j],其中 i 是行,j 是列,我们得到一个矩阵,其中行由数据点表示,列是它们各自的高斯值。
当期望步骤完成后,我们将执行最大化步骤或 M 步骤。在这个步骤中,我们将使用方程 5.7 中的公式更新 µ、∑ 和 f 的值。回想一下,在 k-means 聚类中,我们只是简单地取数据点的均值然后继续。这里我们做的是类似的事情,尽管我们使用了上一步计算的概率或期望。
这三个值可以使用以下方程计算。方程 5.7 是计算所有点的协方差 ∑[j],然后按照方程 5.11 中所示,由该点由高斯 j 生成的概率进行加权。数学证明超出了本书的范围。

(5.11)
均值 µ[j],由方程 5.12 确定。在这里,我们确定所有点的均值,并按照该点由高斯 j 生成的概率进行加权。

(5.12)
同样,密度或强度是通过方程 5.13 计算的,其中我们将每个点由高斯 j 生成的所有概率相加,然后除以点的总数 N。

(5.13)
根据这些值,推导出 ∑、µ 和 f 的新值,这个过程会一直持续到模型收敛。当我们能够最大化似然函数时停止。
这是一个复杂的过程。我们介绍了它,以便您深入了解统计算法背后的发生情况。Python 实现比数学概念要简单得多。
练习 5.3
回答这些问题来检查你的理解:
-
高斯分布的均值等于 1,标准差等于 0。对还是错?
-
GMM 模型不考虑数据的协方差。对还是错?
5.6.2 GMM 的 Python 实现
我们首先导入数据,然后使用 k-means 和 GMM 进行比较。我们遵循以下步骤:
- 导入所有库和数据集:
import pandas as pd
data = pd.read_csv('vehicle.csv')
import matplotlib.pyplot as plt
-
- 从数据集中删除任何 NA 值:
data = data.dropna()
-
- 配置一个
kmeans算法。我们将簇的数量保持为 5。请注意,我们并没有说这是簇的理想数量。簇的数量只是为了说明目的。我们声明一个变量 k-means 并使用五个簇。接下来拟合数据集:
- 配置一个
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=5)
kmeans.fit(data)
-
- 绘制聚类。首先,对数据集进行预测,然后将值添加到数据框作为一个新列。然后,用不同颜色表示不同聚类的数据绘制。书的打印版不会显示不同的颜色,但 Python 代码的输出会。同样的输出也可以在 GitHub 仓库中找到。
输出如下(见图 5.22):
pred = kmeans.predict(data)
frame = pd.DataFrame(data)
frame['cluster'] = pred
color=['red','blue','orange', 'brown', 'green']
for k in range(0,5):
data = frame[frame["cluster"]==k]
plt.scatter(data["compactness"],data["circularity"],c=color[k])
plt.show()

图 5.22 配置kmeans算法后绘制聚类的结果
-
- 配置一个 GMM 模型。请注意,代码与 k-means 算法相同,只是算法的名称从 k-means 更改为
GaussianMixture:
- 配置一个 GMM 模型。请注意,代码与 k-means 算法相同,只是算法的名称从 k-means 更改为
from sklearn.mixture import GaussianMixture
gmm = GaussianMixture(n_components=5)
gmm.fit(data)
#predictions from gmm
labels = gmm.predict(data)
frame = pd.DataFrame(data)
frame['cluster'] = labels
-
- 绘制结果。输出如下(见图 5.23):
color=['red','blue','orange', 'brown', 'green']
for k in range(0,5):
data = frame[frame["cluster"]==k]
plt.scatter(data["compactness"],data["circularity"],c=color[k])
plt.show()

图 5.23 配置 GMM 算法后绘制聚类的结果
-
- 使用不同的聚类值运行代码以观察差异。在下面的图中,左边的是具有两个聚类的 k-means,而右边的是具有两个聚类的 GMM。在这两种聚类方法中,有几个点被分类为不同的类别。书的打印版不会显示不同的颜色,但 Python 代码的输出会。同样的输出也可以在 GitHub 仓库中找到(见图 5.24)。


图 5.24 具有两个聚类的 k-means(左)和具有两个聚类的 GMM(右)
高斯分布是使用最广泛的数据分布之一。如果我们比较 k-means 和 GMM 模型,我们会看到 k-means 不考虑数据的正态分布。在 k-means 中,也没有考虑各种数据点之间的关系。
注意:k-means 是一种基于距离的算法;GMM 是一种基于分布的算法。
简而言之,使用 GMM 模型创建聚类是有利的,尤其是在我们拥有重叠数据集时。它对于金融和价格建模、基于自然语言处理解决方案等都是一种有用的技术。
5.7 总结思考
在本章中,我们探讨了三种复杂的聚类算法。你可能觉得数学概念有点沉重。确实如此,但它们提供了对过程的更深入理解。这些算法并不一定是每个问题的最佳选择。理想情况下,在现实世界的商业问题中,我们首先从经典的聚类算法(k-means、层次和 DBSCAN)开始。如果我们没有得到可接受的结果,我们可以尝试更复杂的算法。
许多时候,数据科学问题被等同于算法的选择,这其实并不正确。算法确实是整个解决方案的重要组成部分,但并非唯一。在现实世界的数据集中,有很多变量,数据量也相当大。数据中有很多噪声。在选择算法时,我们应该考虑到所有这些因素。算法的维护和更新也是需要考虑的因素。所有这些方面都在本书的最后一章中详细讨论。
5.8 实践步骤和推荐阅读
以下提供了一些下一步行动的建议和一些有用的阅读材料:
-
在第二章中,我们使用了各种技术进行聚类。使用那里的数据集,执行谱聚类、GMM 和 FCM 聚类以比较结果。第二章末尾提供的数据集可用于聚类。
-
从 Kaggle 获取聚类用信用卡数据集(
mng.bz/oKwd)和之前使用过的著名 Iris 数据集的数据(www.kaggle.com/uciml/iris)。 -
参考亨利·赫克斯莫尔的书籍《计算网络科学》来学习数学概念。
-
从以下链接获取谱聚类论文并学习:
-
关于谱聚类:分析和算法:
mng.bz/nRwa -
基于特征值选择的谱聚类:
mng.bz/vKw7 -
谱聚类的数学原理及其与主成分分析的等价性:
arxiv.org/pdf/2103.00733v1.pdf
-
-
从以下链接获取 GMM 论文并探索:
-
“高维面板数据模型的 GMM 估计法”:
mng.bz/4agw -
“复合高斯混合模型在数据流中的应用”:
ieeexplore.ieee.org/document/5620507
-
-
从以下链接获取 FCM 论文并学习:
-
“FCM:模糊 c 均值聚类算法”:
mng.bz/QDXG -
模糊 c 均值聚类技术综述:
www.ijedr.org/papers/IJEDR1704186.pdf -
“模糊 C 均值和可能性 C 均值聚类算法的实现、聚类趋势分析和聚类验证”:
arxiv.org/pdf/1809.08417.pdf
-
摘要
-
谱聚类关注聚类中的数据点亲和力而非位置。它适用于复杂的数据形状,而传统的算法如 k-means 可能不足以满足需求。
-
谱聚类利用图理论和连通性,依赖于特征值、拉普拉斯矩阵和亲和矩阵。
-
该过程包括计算度、邻接矩阵、拉普拉斯矩阵和 Fielder 向量以进行聚类。
-
K-means 聚类使用质心,而谱聚类的重点是连通性和数据点相似性。
-
由于矩阵运算,谱聚类可能需要大量的计算资源,因此适用于较小的数据集。
-
模糊聚类允许数据点属于多个簇,为数据项引入了“隶属度”。
-
FCM 是模糊聚类中的一个关键算法,它通过隶属度和通过超参数m来控制模糊度。
-
GMM 使用高斯分布进行软聚类,并考虑数据集的协方差。
-
GMM 适用于重叠数据集,并且与 k-means 不同,它考虑数据点之间的关系。
-
在 GMM 中,EM 技术被用于迭代估计参数。
-
GMM 模型在金融建模、自然语言处理以及重叠数据的案例中具有优势。
-
模糊聚类和 GMM 是软聚类方法,允许对数据点进行详细的隶属度和概率分配。
-
谱聚类支持图像分割、语音分析和文本分析等应用,而不需要假设数据形状约束。
第六章:维度降低
本章涵盖
-
t 分布随机邻域嵌入
-
多维尺度
-
均匀流形近似与投影
-
算法的 Python 实现
生活其实很简单,但我们却坚持让它变得复杂。——孔子
简单是一种美德——在生活中和在数据科学中。我们迄今为止讨论了很多算法。其中一些足够简单,而一些则稍微复杂。在本书的第一部分,我们研究了简单的聚类算法,在最后一章,我们考察了高级聚类算法。同样,我们在第三章研究了几个维度降低算法,如主成分分析(PCA)。继续同样的思路,我们将在本章研究三种高级维度降低技术。
我们在本章和下一部分书中涵盖的高级主题旨在为你准备复杂问题。虽然你可以应用这些高级解决方案,但始终建议从经典的解决方案开始,如主成分分析(PCA)进行维度降低。如果该解决方案不能适当地解决该问题,那么你可以尝试高级解决方案。
维度降低是人们最渴望寻求的解决方案之一,尤其是在我们拥有大量变量时。回想一下我们在第三章讨论的“维度诅咒”。如果需要,建议你在继续前进之前复习第三章的内容。在本章中,我们将介绍 t 分布随机邻域嵌入(t-SNE)、多维尺度(MDS)和均匀流形近似与投影(UMAP)。本章将涵盖一些数学概念,这些概念构成了我们将讨论的高级技术的基石。一如既往,概念讨论之后将跟随一个 Python 解决方案。本章还包括一个简短的案例研究。我们还将使用图像数据集开发一个解决方案。
你心中可能存在一个困境:需要多少数学水平,深入统计知识是否是先决条件?答案是既是也是。虽然拥有数学理解能力将使你能够理解算法并更深入地欣赏过程;同时,对于现实世界的商业实施,有时人们可能想跳过数学,直接转向 Python 中的示例。我们建议至少要有基本的数学理解,以便完全掌握概念。在这本书中,我们提供了这种水平的数学支持,而不深入探讨,而是呈现了实际世界和数学概念的优化组合。
欢迎来到第六章,祝一切顺利!
6.1 技术工具箱
我们将继续使用迄今为止使用的相同版本的 Python 和 Jupyter Notebook。本章中使用的代码和数据集已在mng.bz/XxOv检查过。
在本章中,您需要安装Keras作为额外的 Python 库。此外,您还需要以下常规模块:numpy、pandas、matplotlib、seaborn和sklearn。
6.2 多维尺度
如你所知,地图在旅行中非常有用。现在想象你接到了一个任务。你收到了世界各地一些城市之间的距离——例如,伦敦和纽约、伦敦和巴黎、巴黎和新德里等等。然后你被要求重新创建一个地图,这些距离就是从这个地图中得出的。如果我们必须重新创建那个二维地图,那将是通过试错;我们将做一些假设并继续这个过程。这肯定是一项既费时又容易出错的任务。
注意:在思考前面的例子时,忽略地球不是平的这一事实,并假设距离测量指标是恒定的——例如,在英里或千米之间没有混淆。
为了说明,考虑图 6.1。正式地说,如果我们有x个数据点,MDS 可以帮助我们将这些x点之间的成对距离信息转换为笛卡尔空间中点的配置。或者简单地说,MDS 将高维数据集转换为低维数据集,在这个过程中保持点之间的距离或相似性不变。

图 6.1 展示了城市之间的距离以及它们在地图上的表示。该图仅用于帮助理解,并不代表实际结果。
为了简化,考虑图 6.2。这里我们有三个点:A、B 和 C。我们在三维空间中表示这些点。然后我们在二维空间中表示这三个点,最后在一维空间中表示。图中的图例中点之间的距离没有按比例。这个例子表示了降低维度数量的意义。

图 6.2 三个点的表示
因此,在 MDS 中,多维数据被降低到更少的维度。
MDS 算法有三种类型:
-
经典 MDS
-
度量多维尺度
-
非度量多维尺度
6.2.1 经典 MDS
我们将在书中详细检查度量 MDS 的过程,而我们将简要介绍经典和非度量 MDS。想象我们有两个点:i和j。让我们假设两点之间的原始距离是d[i][j],而在低维空间中的对应距离是d[i][j]。
在经典的多维尺度分析(MDS)中,点之间的距离被视为欧几里得距离,原始距离和拟合距离表示在相同的度量下。这意味着如果使用欧几里得方法在更高维空间中计算原始距离,那么在低维空间中计算的拟合距离也将使用欧几里得距离。我们已经知道如何计算欧几里得距离。例如,我们需要找到点 i 和 j 之间的距离,假设距离为 d[i][j]。在二维空间中,距离可以通过方程 6.1 给出的欧几里得距离公式给出:

(6.1)
回想第二章,我们讨论了其他距离函数,如曼哈顿距离、欧几里得距离等。建议您复习第二章的内容。
6.2.2 非度量 MDS
我们刚才提到,欧几里得距离可以用来计算两点之间的距离。有时无法获取距离的实际值,例如当 d[i][j] 是实验结果,其中进行了主观评估,换句话说,为各种数据参数分配了排名时。例如,如果点 2 和点 5 之间的距离在原始数据中排名为 4,在这种情况下,使用 d[i][j] 的绝对值是不明智的,因此必须使用相对值或排名值。在这里,距离可以指一种排名——例如,谁在比赛中排名第一。这是非度量多维尺度分析的过程。例如,想象我们有四个点:A、B、C 和 D。我们希望对这些四个点之间的相应距离进行排名。点的相应组合可以是 A 和 B、A 和 C、A 和 D、B 和 C、B 和 D 以及 C 和 D。它们的距离可以按照表 6.1 所示进行排名。
表 6.1 四点之间的相应距离及其距离的排名
| 点对 | 距离 | 相应距离的排名 |
|---|---|---|
| A 和 B | 100 | 3 |
| A 和 C | 105 | 4 |
| A 和 D | 95 | 2 |
| B 和 C | 205 | 6 |
| B 和 D | 150 | 5 |
| C 和 D | 55 | 1 |
因此,在非度量 MDS 方法中,我们不是使用实际距离,而是使用相应距离的排名。接下来,我们将转向度量 MDS 方法。
我们知道,在经典的多维尺度分析(MDS)中,原始距离和拟合距离表示在相同的度量下。在度量 MDS 中,假设 d[i][j] 的值可以通过对数据集应用某些参数变换而转换为欧几里得距离。在某些文章中,您可能会发现经典和度量 MDS 被交替使用。
在 MDS 中,作为第一步,计算点之间的相应距离。一旦计算了相应的距离,MDS 将尝试在低维空间中表示高维数据点。为了执行此操作,必须进行优化过程,以便选择最佳的结果维度数。因此,必须优化损失函数或成本函数。
成本函数
我们使用算法来预测变量的值。例如,我们可能使用某些算法来预测明年产品的预期需求。我们希望算法尽可能准确地预测。成本函数是检查算法性能的简单方法。
成本函数是一种简单的技术,用于衡量我们算法的有效性。它是衡量预测模型性能最常用的方法。它通过比较算法预测的原始值和预测值来计算模型在预测中的错误程度。
正如你所想象的那样,在理想解决方案中,我们希望预测值与实际值相同,这非常难以实现。如果预测值与实际值差异很大,成本函数的输出就更高。如果预测值更接近实际值,那么成本函数的值就较低。一个稳健的解决方案是具有最低成本函数值的解决方案。因此,优化任何算法的目标将是使成本函数的值最小化。成本函数也被称为损失函数;这两个术语可以互换使用。
在度量 MDS 中,我们也可以将成本函数称为应力。这只是成本函数的另一个名称。应力的公式在方程 6.2 中给出:

(6.2)
在方程中,
-
术语应力[D] 是 MDS 函数需要最小化的值。
-
在低维空间中具有新坐标集的数据点表示为 x[1],x[2],x[3]… x[N]。
-
术语 ||x[i]– x[j]|| 表示它们在低维空间中两点之间的距离。
-
术语 d[i][j] 是两点在原始多维空间中的原始距离。
通过观察方程,我们可以看到,如果 ||x[i]– x[j]|| 和 d[i][j] 的值彼此接近,则结果应力的值将很小。
注意:最小化应力的值是损失函数的目标。
为了优化这个损失函数,我们可以使用多种方法。其中最著名的方法是使用 Kruskal 和 Wish 在 1978 年最初提出的梯度下降法。梯度下降法非常容易理解,可以用一个简单的类比来解释。
想象你站在山顶上,你想下山。你想要选择最快的路径,因为你想要尽可能快地下山(不,你不能跳下去!)。所以,为了迈出第一步,你四处张望,然后选择最陡峭的路径,朝那个方向迈出一步,到达一个新的点。然后,你再次朝最陡峭的方向迈出一步。这个过程在图 6.3 的第一幅图中展示。

图 6.3 一个站在山顶上试图下山的人。梯度下降的过程遵循这种方法(左)。在梯度下降过程中成本函数优化的实际过程。注意,在收敛点上,成本函数的值是最小的(右)。
现在假设一个算法必须完成类似的壮举;这个过程在图 6.3 的右图中表示,其中损失函数从一个点开始,最终达到收敛点。在这个收敛点上,成本函数是最小的。
MDS 与其他降维技术不同。与 PCA 等技术相比,MDS 对数据集没有任何假设,因此可以用于更多的数据集。此外,MDS 允许使用任何距离度量指标。与 PCA 不同,MDS 不是一个特征值-特征向量技术。回想一下,在 PCA 中,第一条轴捕捉最大的方差,第二条轴有下一个最好的方差,依此类推。在 MDS 中,没有这样的条件。MDS 中的轴可以根据需要反转或旋转。此外,在大多数其他降维方法中,算法确实计算了很多轴,但它们无法被查看。在 MDS 中,在开始时明确选择了一个较小的维度数。因此,在解决方案中存在较少的歧义。进一步来说,在其他算法中,通常只有一个唯一的解,而 MDS 试图迭代地找到最可接受的解。这意味着在 MDS 中,对于同一数据集可以有多个解。
但与此同时,对于更大的数据集,MDS 所需的计算时间更长——并且在用于优化的梯度下降法中存在一个陷阱(见图 6.4)。让我们回顾一下我们之前提到的山岳例子。想象一下,当你从山顶下来时,起点是 A,山顶的底部是点 C。在你下坡的过程中,你到达了点 B。如图中左图所示,点 B 周围有一个轻微的隆起。在这个点 B,你可能会错误地认为你已经到达了山的底部。换句话说,你会认为你已经完成了任务。这就是局部最小值的问题。

图 6.4 虽然第一个图表示的是收敛点,代表了梯度下降法,但请注意,在第二个图中,全局最小值位于别处,而算法可能会陷入局部最小值。算法可能会检查它已经优化了成本函数并达到了全局最小值,而实际上它只达到了局部最小值。在局部最小值处,没有上升的方向;所有方向都是下降的。如果算法纯粹是局部的,它对存在在可能的小山之外的更深最小值没有信息。
有一种可能性,损失函数可能不是全局最小值,而是陷入局部最小值。算法可能会认为它已经达到了收敛点,而完整的收敛可能还没有实现,我们处于局部最小值。
关于 MDS 解决方案的有效性仍有一个问题需要回答。我们如何衡量解决方案的有效性?在原始论文中,Kruskal 推荐使用应力值来衡量解决方案的拟合优度,这些应力值显示在表 6.2 中。这些推荐主要基于 Kruskal 的经验。这些应力值是基于 Kruskal 的经验。
表 6.2 应力值及其拟合优度
| 应力值 | 拟合优度 |
|---|---|
| 0.200 | 差 |
| 0.100 | 一般 |
| 0.050 | 好 |
| 0.025 | 优秀 |
| 0.000 | 完美 |
下一个逻辑问题是:我们应该选择多少个最终维度?散点图提供了答案,如图 6.5 所示。回想一下,在第二章中,我们使用类似的手肘法来选择 k-means 聚类的最佳聚类数。对于 MDS,我们也可以使用手肘法来确定表示数据的最佳成分数。

图 6.5 找到最佳成分数的散点图。它与 k-means 解决方案类似;我们必须在图中寻找手肘点。
练习 6.1
回答这些问题以检查你的理解:
-
度量空间 MDS 和非度量空间 MDS 算法之间的区别是什么?
-
梯度下降用于最大化成本。对还是错?
-
使用一个简单的例子解释梯度下降法。
6.3 MDS 的 Python 实现
对于 MDS 方法的 Python 实现,我们将使用著名的 Iris 数据集,我们之前已经使用过。由于scikit learn包中提供了库,使用算法相当简单。
备注:实现通常很简单,因为重活是由库完成的。
步骤如下:
- 加载库。常用的库有
sklearn、matplotlib和numpy,我们还从sklearn中加载了MDS:
import numpy as np
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
from sklearn.manifold import MDS
from sklearn.preprocessing import MinMaxScaler
import pandas as pd
import warnings
warnings.filterwarnings("ignore")
-
- 加载数据集。Iris 数据集在
sklearn库中可用,因此我们在这里不需要导入 Excel 或.csv 文件:
- 加载数据集。Iris 数据集在
raw_data = load_iris()
dataset = raw_data.data
-
- MDS 的要求是在实际可视化之前对数据集进行缩放。我们使用
MixMaxScalar()函数来实现这一点。MinMax 缩放简单地使用方程 6.3 中的公式缩放数据:
- MDS 的要求是在实际可视化之前对数据集进行缩放。我们使用

(6.3)
d_scaler = MinMaxScaler()
dataset_scaled = d_scaler.fit_transform(dataset)
作为这一步骤的输出,数据已缩放并准备好进行下一阶段的建模。
-
- 从
sklearn库中调用 MDS 方法。random_state值允许我们重现结果。我们在此示例中将组件数量选为 3:
- 从
mds_output = MDS(3,random_state=5)
-
- 使用 MDS 模型拟合之前创建的缩放数据:
data_3d = mds_output.fit_transform(dataset_scaled)
-
- 声明我们希望用于可视化的颜色。接下来,数据点在散点图中进行可视化:
mds_colors = ['purple','blue', 'yellow']
for i in np.unique(raw_data.target):
d_subset = data_3d[raw_data.target == i]
x = [row[0] for row in d_subset]
y = [row[1] for row in d_subset]
plt.scatter(x,y,c=mds_colors[i],label=raw_data.target_names[i])
plt.legend()
plt.show()
前面代码的输出显示在图 6.6 中。

图 6.6 Iris 数据的输出
这个 Python 实现的例子是 Iris 数据的可视化。这是一个相当简单的例子,因为它不涉及应力和组件数量的优化。换句话说,我们需要一个更复杂的数据集才能真正优化 MDS。我们现在将使用一个精选的数据集来实施 MDS(见图 6.7)。

图 6.7 各个城市及其相互之间的距离
假设我们拥有五个城市,它们之间的距离在图 6.7 中给出。步骤如下:
- 我们已经在上一段代码中导入了库:
import numpy as np
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
from sklearn.manifold import MDS
from sklearn.preprocessing import MinMaxScaler
import pandas as pd
import warnings
warnings.filterwarnings("ignore")
-
- 创建数据集。虽然我们在这里创建了一个数据集,但在实际业务场景中,它将以距离的形式存在(见图 6.8):
data_dummy_cities = {'A':[0,40,50,30,40],
'B':[40,0,40,50,20],
'C':[50,40,0,20,50],
'D':[30,50,20,0,20],
'E':[40,20,50,20,0],
}
cities_dataframe = pd.DataFrame(data_dummy_cities, index
=['A','B','C','D','E'])
cities_dataframe

图 6.8 创建数据集
-
- 使用
MinMaxScalar()函数缩放数据集,就像我们在上一个编码练习中所做的那样:
- 使用
scaler = MinMaxScaler()
df_scaled = scaler.fit_transform(cities_dataframe)
现在我们致力于寻找最优的组件数量。我们将对不同的组件数量进行迭代。对于每个组件数量的值,我们将得到应力的值。观察到拐点的点即为最优的组件数量。
作为第一步,我们将声明一个空的数据框,它可以用来存储组件数量及其对应应力值的值。然后我们在for循环中从 1 迭代到 10。最后,对于组件的每个值(1 到 10),我们得到相应的应力值:
MDS_stress = []
for i in range(1, 10):
mds = MDS(n_components=i)
pts = mds.fit_transform(df_scaled)
MDS_stress.append(mds.stress_)
-
- 现在我们已经得到了应力的值,我们将这些值绘制在图表中。每个坐标轴的相应标签也已给出。请看图 6.9 中值 2 和 3 处的拐点。这些可能是组件数量的最优值:
plt.plot(range(1, 10), MDS_stress)
plt.xticks(range(1, 5, 2))
plt.title('Plot of stress')
plt.xlabel('Number of components')
plt.ylabel('Stress values')
plt.show()

图 6.9 切片图以选择优化的组件数量
-
- 运行组件数量为 3 的解决方案。如果我们查看应力的值,组件数量为 3 时,它生成了最小的应力值 0.00665(见图 6.10):
mds = MDS(n_components=3)
x = mds.fit_transform(df_scaled)
cities = ['A','B','C','D','E']
plt.figure(figsize=(5,5))
plt.scatter(x[:,0],x[:,1])
plt.title('MDS with Sklearn')
for label, x, y in zip(cities, x[:, 0], x[:, 1]):
plt.annotate(
label,
xy = (x, y),
xytext = (-10, 10),
textcoords = 'offset points'
)
plt.show()
print(mds.stress_)
这就结束了我们对 MDS 算法的讨论。我们讨论了其基础和概念、优缺点、算法评估以及 MDS 的 Python 实现。作为非线性降维方法之一,它对于可视化和降维是一个很好的解决方案。

图 6.10 MDS 数据集的输出:图中展示了五个城市的表示
6.4 t 分布随机近邻嵌入
如果数据集的维度非常高,分析就会变得繁琐。可视化甚至更加混乱。我们在第三章的维度诅咒部分已经详细讨论了这一点。如果你需要复习,建议在继续之前重新回顾这个概念。
这样一个真正高维度的数据集可以是图像数据。我们发现,由于超过 3 维度的内容对我们来说越来越难以直观理解,因此我们很难理解这类数据。
你可能已经在你的智能手机上使用过面部识别软件。对于这样的解决方案,面部图像需要被分析,机器学习模型需要被训练。看看图 6.11 中的图片:我们有一个人脸、一辆自行车、一个吸尘器和一部手机的屏幕截图。
图像是复杂的数据类型。每张图像由像素组成,每个像素可以由 RGB(红色、绿色、蓝色)值组成。RGB 的值可以从 0 到 255。生成的数据集将是一个非常高维的数据集。

图 6.11 中的图像对于算法来说非常复杂,难以解读。图像可以是任何形式,可以是人物、设备,甚至是手机屏幕。
现在回想一下我们在第三章学习的 PCA。PCA 是一个线性算法。因此,它在解决非线性复杂多项式函数方面的能力有限。此外,当需要将高维数据集表示在低维空间中时,算法应该将相似的数据点保持得尽可能接近,这在线性算法中可能是一个挑战。作为线性降维技术,PCA 试图尽可能地将不同的数据点分开,并试图最大化数据中捕获的方差。这种分析的结果可能不够稳健,可能不适合进一步的使用和可视化。因此,我们有非线性算法如 t-SNE 来帮助。
t-SNE 是一种非线性降维技术,对于高维数据来说非常实用。它基于随机近邻嵌入,由 Sam Roweis 和 Geoffrey Hinton 开发。t 分布的变体是由 Lauren van der Maaten 提出的。因此,t-SNE 是 SNE 算法的改进。
在高层次上,SNE 测量高维空间和低维空间中实例对之间的相似度。一个好的解决方案是这些相似度度量之间的差异最小,然后 SNE 使用类似于我们之前讨论的 MDS 的成本函数来优化这些相似度度量。
我们接下来检查 t-SNE 的逐步过程。描述的过程在数学上有点复杂:
-
考虑一个高维空间及其中的某些点。
-
测量上一条中提到的高维空间中各个点之间的相似度。对于点 x[i],我们将创建一个以该点为中心的高斯分布。我们已经在第二章中学习了高斯或正态分布。高斯分布如图 6.12 所示。

图 6.12 高斯或正态分布。
-
- 测量位于该高斯分布下的点(比如说 x[j])的密度,然后重新归一化它们以获得相应的条件概率(p[j][|][i])。对于附近且相似的点,这个条件概率将很高,而对于远离且不相似的点,条件概率(p[j][|][i])的值将非常小。这些概率值是在高维空间中的。对于好奇的读者,这个条件概率的数学公式以方程 6.4 表示。

(6.4)
其中 σ 是以 x[i] 为中心的高斯分布的方差。数学证明超出了本书的范围。
-
- 在低维空间中测量另一组概率。对于这组测量,我们使用下一节中描述的柯西分布。我们使用 Kullback-Liebler(KL)散度来测量两个概率分布之间的差异。
6.4.1 柯西分布
柯西分布属于连续概率分布族。尽管它与我们在图 6.13 中表示的正态分布有相似之处,但柯西分布的峰值更窄,扩散得更慢。这意味着,与正态分布相比,获得远离峰值的值的概率更高。有时,柯西分布也被称为洛伦兹分布。值得注意的是,柯西分布没有明确定义的平均值,但中位数是对称中心。

图 6.13 高斯分布与柯西分布的比较。(图片来源:Quora)
- 假设我们得到了 y[i] 和 y[j],它们是高维数据点 x[i] 和 x[j] 的低维对应物。因此,我们可以像上一步一样计算概率得分。使用高斯分布,我们还可以得到第二组概率 q[j][|][i]。数学公式在方程 6.5 中展示:

(6.5)
-
- 到目前为止,我们已经计算了两组概率 (p[j][|][i]) 和 (q[j][|][i])。在这一步,我们比较这两个分布并测量它们之间的差异。换句话说,在计算 (p[j][|][i]) 时,我们测量了高维空间中相似性的概率,而对于 (q[j][|][i]),我们在低维空间中做了同样的操作。理想情况下,两个空间的映射应该是相似的,为此,(p[j][|][i]) 和 (q[j][|][i]) 之间不应该有任何差异。因此,SNE 算法试图最小化条件概率 (p[j][|][i]) 和 (q[j][|][i]) 之间的差异,这与我们在高维和低维空间中使用 MDS 进行距离测量的做法类似。
-
- 使用 KL 散度来测量两个概率分布之间的差异。
-
- 为了最小化 KL 代价函数,我们使用梯度下降方法。我们已经在第 6.2 节中讨论了梯度下降方法,当时我们讨论了 MDS 算法。
定义:KL 散度或相对熵用于测量两个概率分布之间的差异。通常,一个概率分布是数据或测量的得分,而第二个概率分布是对原始概率分布的近似或预测——例如,如果原始概率分布是 X,而近似的是 Y。KL 散度可以用来测量 X 和 Y 概率分布之间的差异。在绝对意义上,如果值为 0,则意味着两个分布是相同的。KL 散度适用于神经科学、统计学和流体力学等领域。
在我们进行 t-SNE 工作时,还有一个重要的因素我们应该意识到,那就是 困惑度。困惑度是一个超参数,它允许我们控制和优化每个数据点拥有的邻近点的数量。
备注:根据官方论文,困惑度的典型值介于 5 到 50 之间。
可能还有一个额外的细微差别:t-SNE 算法的输出可能在连续运行中永远不会相同。我们必须优化超参数的值以获得最佳输出。
练习 6.2
回答这些问题以检查你的理解:
-
用你自己的话解释高斯分布。
-
PCA 是一个非线性算法。对或错?
-
KL 散度用于测量两个概率分布之间的差异。对或错?
6.4.2 t-SNE 的 Python 实现
在本例中,我们将使用两个数据集。第一个是 Iris 数据集,我们已经在本书中多次使用过。第二个数据集非常有趣:MNIST 数据集是一个手写数字数据库。它是用于训练图像处理解决方案中最著名的数据库之一,通常被认为是图像检测解决方案的“Hello World”程序。图 6.14 显示了图像表示。

图 6.14 MNIST 数据集
Iris 数据集的步骤如下:
- 导入必要的库。请注意,我们已经从
keras库中导入了 MNIST 数据集。
rom sklearn.manifold import TSNE
from keras.datasets import mnist
from sklearn.datasets import load_iris
from numpy import reshape
import seaborn as sns
import pandas as pd
TIP 如果您无法在 Python 代码中安装模块,请参阅附录,我们提供了解决方案。
-
- 加载 Iris 数据集。该数据集包含两部分:一部分是“数据”,另一部分是相应的标签或“目标”。这意味着“数据”是数据的描述,“目标”是鸢尾花的类型。我们使用代码打印特征和标签:
iris = load_iris()
iris_data = iris.data
iris_target = iris.target
iris.feature_names
iris.target_names
-
- 调用 t-SNE 算法。我们使用
n_components=2、verbose=1和random_state=5来重现结果。然后使用该算法拟合数据(见图 6.15):
- 调用 t-SNE 算法。我们使用
tsne = TSNE(n_components=2, verbose=1, random_state=5)
fitted_data = tsne.fit_transform(iris_data)

图 6.15 代码拟合算法的输出
-
- 绘制数据。这一步使我们能够可视化上一步中算法拟合的数据。
首先,我们将初始化一个空的数据框。我们将逐个添加三个列。我们首先从 iris_target 开始,然后是 tSNE_first_component 和 tSNE_second_component。tSNE_first_component 是 fitted_data 数据框的第一列,因此索引是 0。tSNE_second_component 是 fitted_data 数据框的第二列,因此索引是 1。最后,我们在图 6.16 中以散点图的形式表示数据:
iris_df = pd.DataFrame()
iris_df["iris_target"] = iris_target
iris_df["tSNE_first_component"] = fitted_data[:,0]
iris_df["tSNE_second_component"] = fitted_data[:,1]
sns.scatterplot(x="tSNE_first_component", y="tSNE_second_component",
hue=iris_df.iris_target.tolist(),
palette=sns.color_palette("hls", 3),
data=iris_df).set(title="Iris data tSNE projection")

图 6.16 Iris 数据集的 t-SNE 投影。注意我们如何得到数据集中三个类别的三个单独的簇。
要实现 MNIST 数据集的算法,加载库和数据集。库已经在上一个代码示例中加载。现在加载数据集。数据集需要 reshape,这里进行了操作(见图 6.17):
(digit, digit_label), (_ , _) = mnist.load_data()
digit = reshape(digit, [digit.shape[0], digit.shape[1]*digit.shape[2]])
Step 2: the subsequent steps are exactly same to the last example we used.
tsne_MNIST = TSNE(n_components=2, verbose=1, random_state=5)
fitted_data = tsne_MNIST.fit_transform(digit)
mnist_df = pd.DataFrame()
mnist_df["digit_label"] = digit_label
mnist_df["tSNE_first_component"] = fitted_data[:,0]
mnist_df["tSNE_second_component"] = fitted_data[:,1]
sns.scatterplot(x="tSNE_first_component", y="tSNE_second_component", hue=mnist_df.digit_label.tolist(),
palette=sns.color_palette("hls", 10),
data=mnist_df).set(title="MNIST data T-SNE projection")

图 6.17 t-SNE 对不同灰度表示的 10 个数字类别的输出
在运行 t-SNE 时,有几个重要点需要注意:
-
在确定解决方案之前,使用不同的超参数值运行算法。
-
理想情况下,困惑度应该在 5 到 50 之间,对于优化的解决方案,困惑度的值应该小于点的数量。
-
T-SNE 为每个点猜测接近邻居的数量。因此,密集的数据集将需要更高的困惑度值。
-
注意,困惑度是平衡数据局部和全局方面的超参数。
t-SNE 是一个广泛流行的算法。它可以用于研究一个区域的拓扑结构,但单个 t-SNE 不能用于做出最终评估。相反,应该创建多个 t-SNE 图来做出任何最终建议。有时有人抱怨 t-SNE 是一个黑盒算法。这在一定程度上可能是正确的。使 t-SNE 的采用变得更困难的是,它不会在连续迭代中产生相同的结果。因此,你可能会发现 t-SNE 仅被推荐用于探索性分析。
6.5 统一流形近似投影
UMAP 是一种强大且流行的降维技术。它旨在在降低高维数据集的复杂性和维度到低维数据集的同时,保留数据集的局部和全局结构。
UMAP 是由 Lealand McInnes、John Healy 和 James Melville 于 2018 年提出的。UMAP 使数据更适合可视化与分析。这与拓扑和流形理论的概念相关。UMAP 假设高维数据集通常位于一个流形上,这意味着低维结构嵌入在更高维的空间中。因此,它试图将这个流形投影到低维空间,同时保留最近邻关系,这仅仅是局部结构,以及更大的关系,即全局结构。
6.5.1 与 UMAP 一起工作
UMAP 方法使用模糊简单集的概念。这些集合代表不同数据点之间距离的概率分布,并捕捉潜在的流形结构。
UMAP 的第一步是构建一个加权图,其中每个数据点根据距离度量与其最近邻连接。通常,欧几里得距离被用作距离度量。这个图构建是对高维数据结构的一种抽象表示。
下一步是优化图。通过最小化原始高维关系和新创建的低维关系之间的交叉熵损失,在低维空间中优化图。这使用了随机梯度下降,产生了 UMAP 嵌入。我们将在第九章研究随机梯度下降。
UMAP 有两个关键参数:
-
n_neighbours——考虑每个点的最近邻数量。使用此参数,我们平衡局部数据结构和全局数据结构之间的保留。 -
min_dist——这个参数用于控制点之间的簇聚紧密程度。较小的最小距离值会使点更靠近,从而创建更深的簇。较大的最小距离值将创建较稀疏的簇,这些簇分布较广。
6.5.2 使用 UMAP
UMAP 的用途如下:
-
UMAP 在生物信息学领域的高维数据集可视化中是最受欢迎的应用之一。基因数据集相当复杂且多维,每个数据点可能由数百或数千个属性表示。使用 UMAP,研究人员可以虚拟地检查数据集中的簇和潜在关系。这种解决方案帮助他们识别细胞类型、发育阶段和基因表达模式。
-
UMAP 还应用于自然语言处理领域,通过降低嵌入的维度。它有助于可视化单词、句子或文档之间的关系,使理解相似性变得更容易。
-
UMAP 还可以应用于图像。它有助于基于各种相似性可视化图像的叠加,因此对于理解如何将相似图像聚类在一起,在竞争视觉任务中非常有用。
-
UMAP 可以与其他聚类算法(如 k-means 或 DBSCAN)一起使用。它可以揭示大型数据集中的隐藏模式,并且由于它保留了局部和全局结构,因此在低维表示中找到的簇通常比原始高维数据集提供更重要的分组。
除了帮助可视化外,UMAP 还可以用作预处理步骤来降低数据的维度。它可以作为 PCA 或其他解决方案的替代品。通过减少数据集中的维度数量,可以提高模型的性能并减少计算时间。
在 Python 中使用 UMAP 非常简单。umap-learn库允许我们使用 UMAP 的力量。
6.5.3 UMAP 的关键点
现在我们来介绍 UMAP 的关键点,并将其与其他算法进行比较:
-
由于 UMAP 是一种非线性解法,它比 PCA 能够捕捉更复杂的数据集和模式。回想一下,PCA 是一种线性降维技术,所以当数据不在简单的线性流形上时,UMAP 证明更加准确。
-
PCA 的目标是解释整个数据集的最大方差。另一方面,UMAP 平衡了局部和全局结构,因此在异常检测等任务上更加灵活。
-
与 PCA 相比,UMAP 可以用于更大的数据集。
-
UMAP 比其他非线性解法 t-SNE 更快。t-SNE 可以保留数据的局部结构,但在保留全局结构方面存在困难,可能会导致对簇的误导性解释。UMAP 做得更好,因为它保留了局部和全局结构。
-
UMAP 的结果在多次迭代中更加稳定和一致。对于其他算法,结果可能不稳定,并且可能随着随机种子不同值的变化而变化。
UMAP 最近获得了大量的关注,并已成为机器学习和 AI 解决方案的首选工具。它速度快,可以保留局部和全局数据结构。因此,与其他降维解决方案(如 PCA、t-SNE 和自动编码器)相比,它是一个强大的选择。
6.6 案例研究
在第三章中,我们探讨了电信行业减少维度的案例研究。在本章中,我们将考察一个小型案例研究,其中可以使用 t-SNE 或 MDS 进行降维。
你听说过高光谱图像吗?正如你所知,我们人类在大多数情况下在三个波段中看到可见光的颜色:长波、中波和短波。长波被感知为红色,中波为绿色,短波为蓝色。人类感知到的所有其他颜色都是这三种颜色的混合,这就是为什么屏幕和打印机只需要三种颜色就能工作。另一方面,光谱成像将光谱分成更多的波段,这种技术可以扩展到可见光之外,因此它在生物学、物理学、地球科学、天文学、农业等多个领域都有应用。高光谱成像收集和处理整个电磁谱的信息。它为图像中的每个像素获取光谱。见图 6.18。

图 6.18 “糖端”土豆条的高光谱图像显示了不可见的缺陷(来源:SortingExpert,CC BY-SA 3.0)
其中一个这样的数据集是帕维亚大学数据集。该数据集由意大利帕维亚的 ROSIS 传感器整理。接下来将给出数据集的详细信息,并且可以从mng.bz/nRVa下载数据集。
在这个数据集中有 103 个光谱波段。HIS 的大小是 610 * 340 像素,包含九个类别。这种类型的数据可以用于作物分析、矿物检验和勘探等。由于这些数据还包含有关地质模式的信息,因此在科学目的上非常有用。在开发任何图像识别解决方案之前,我们必须减少这个数据集的维度数量。如果我们有大量的维度,计算成本将会更高。因此,我们希望有一个更低的代表性维度数量。图 6.19 展示了几个示例波段。建议您下载数据集(该数据集也已在 GitHub 仓库中推送),并在数据集上使用各种降维技术来减少维度数量。可能会有许多其他图像数据集和复杂的企业问题,其中 t-SNE 和 MDS 可以具有实际的应用价值。

图 6.19 数据集中的波段示例。这些只是随机示例。
6.7 总结思考
降维是一个既有趣又实用的领域。它使机器学习更经济、更节省时间。想象一下,你有一个具有数千个属性或特征的数据库。你对数据不太了解,业务理解有限,同时你还得在数据库中找到模式。你甚至不确定变量是否都相关,或者只是随机噪声。在这样的时刻,当我们想要使数据库更简单以便破解并减少计算时间时,降维就是解决方案。
我们在本书的前面部分已经介绍了降维技术。本章涵盖了三种高级技术:t-SNE、MDS 和 UMAP。这三种技术不应被视为我们之前讨论的其他更简单技术的替代品。相反,如果我们使用基本算法没有得到有意义的结果,它们可能是有用的。始终建议首先使用 PCA,然后尝试使用高级技术。
本书的内容复杂性正在增加。本章从图像开始——但我们只是浅尝辄止。在下一章中,我们将处理文本数据。也许你会发现它非常有趣和有用。
6.8 实践下一步行动和建议阅读
以下提供了一些下一步行动的建议和一些有用的阅读材料:
-
使用第二章中使用的车辆数据集进行聚类,并在其上实现 MDS。比较实现 MDS 前后的聚类性能。
-
获取第二章中使用的 Python 示例数据集,并使用它们来实现 MDS。
-
对于 MDS,请参考以下研究论文:
-
“降维:比较综述”,作者 Lauren van der Maaten、Eric Postma 和 H. Japp Van Den Herik:
mng.bz/eyxQ -
“基于多维尺度数据降维方法在短期城市道路网络交通流量预测中的应用”,作者 Satish V. Ukkusuri 和 Jian Lu:
mng.bz/pKmz
-
-
从以下链接获取 t-SNE 研究论文并学习它们:
-
“使用 t-SNE 可视化数据”,作者 Laurens van der Maaten 和 Geoffrey Hinton:
mng.bz/OBaE -
“使用 t-SNE 进行单细胞转录组学”,作者 Laurens van der Maaten 和 Geoffrey Hinton:
mng.bz/YD9A
-
-
请参阅以下论文“t-SNE 和 MDS 降维技术在 KNN、SNN 和 SVM 分类器上的性能评估”:
arxiv.org/pdf/2007.13487.pdf
摘要
-
MDS 是一种降维技术,它将高维数据转换为低维空间,同时保留距离。
-
有三种类型的 MDS:经典、指标和非指标。
-
经典 MDS 使用欧几里得距离,使原始距离和拟合距离对齐。
-
非指标 MDS 对距离进行排序,而不是使用绝对值。
-
指标多维尺度(Metric MDS)将距离转换为适合低维空间。
-
MDS 涉及计算距离和通过梯度下降优化应力成本函数,尽管它可能计算密集且容易陷入局部最小值问题。
-
MDS 通过迭代工作,不对数据分布做出假设,这使得它在选择距离度量方面比 PCA 更灵活。
-
t-SNE 是一种非线性降维技术,特别适用于高维和复杂的数据集,如图像。
-
t-SNE 使用柯西分布和 KL 散度优化高维和低维空间中数据点的相似性。
-
由于其非线性特性,t-SNE 在 PCA 上具有优势,尽管它涉及超参数如困惑度。
-
UMAP 是另一种高效保留局部和全局数据结构的降维方法,比 t-SNE 更快、更稳定。
-
Python 实现了 MDS 和 t-SNE。
-
MDS 是一种高级降维技术,需要优化损失函数或成本函数。
第七章:文本数据的无监督学习
本章涵盖
-
文本数据分析:用例和挑战
-
文本数据的预处理和清洗
-
文本数据的向量表示方法
-
使用 Python 进行情感分析和文本聚类
-
文本数据的生成式 AI 应用
所有人都用同一种语言微笑。——乔治·卡林
我们的世界有如此多的语言。这些语言是我们表达思想和情感的最常见沟通媒介。这些词可以写成文本。在本章中,我们探讨可以对文本数据进行哪些分析。文本数据属于非结构化数据,并携带大量有用信息,因此是商业洞察的有用来源。我们使用自然语言处理(NLP)来分析文本数据。
同时,为了分析文本数据,我们必须使数据分析就绪。或者用非常简单的话说,由于我们的算法和处理器只能理解数字,我们必须用数字或向量来表示文本数据。我们将在本章中探讨所有这些步骤。文本数据是许多重要用例的关键,例如情感分析、文档分类和语言翻译,仅举几例。我们将通过案例研究来涵盖这些用例,并在同一平台上开发 Python 解决方案。
本章首先定义文本数据、文本数据的来源以及文本数据的各种用例。然后,我们将继续讨论清洗和处理文本数据的步骤和过程。我们将涵盖 NLP 的概念、数学基础以及将文本数据表示为向量的方法。我们将为用例编写 Python 代码。在结尾部分,我们将分享一个关于文本数据的案例研究。最后,我们还将探讨基于生成式 AI(GenAI)的解决方案。由于它们在第三部分有所涉及,本书中尚未涵盖 GenAI 的概念。但在这里,我们将根据文本数据介绍这些概念。
欢迎来到第七章,祝大家一切顺利!
7.1 技术工具包
我们将继续使用迄今为止相同的 Python 和 Jupyter Notebook 版本。本章中使用的代码和数据集已在相同的 GitHub 位置进行检查。
为了本章,你需要安装以下 Python 库:re、string、nltk、lxml、requests、pandas、textblob、matplotlib、sys、sklearn、scikitlearn和warnings。除了这些,你还需要numpy和pandas。有了这些库,我们可以非常快速地使用算法。
7.2 文本数据无处不在
在本书的第一章,我们探讨了结构化和非结构化数据集。非结构化数据可以是文本、音频、图像或视频。图 7.1 中给出了非结构化数据及其相应来源的示例,其中我们解释了非结构化数据的主要类型——文本、图像、音频和视频——以及示例。本章的重点是文本数据。

图 7.1 非结构化数据可以是文本、图像、音频或视频。在本章中,我们处理文本数据。这个列表并不全面。
语言可能是我们沟通的最强大工具。当以书面形式存在时,它变成了文本数据。如今,得益于广泛可用的电脑和智能手机,文本无处不在。它通过撰写博客、社交媒体帖子、推文、评论、故事、评论等方式生成。文本数据通常比图像更直接,并且可以表达情感。对于企业来说,解锁文本数据的潜力并从中获得洞察力是有用的。他们可以更好地理解客户,探索业务流程,并评估所提供服务的质量。
你是否曾在亚马逊上评论过产品或服务?你给产品打星;同时,你也可以输入自由文本。去亚马逊看看一些评论。你可能会发现一些评论有大量的文本作为反馈。这些文本对产品/服务提供商来说很有用,可以帮助他们提升产品或服务。此外,你可能参与过一些要求你分享反馈的调查。此外,随着 Alexa、Siri、Cortona 等语音助手的出现,语音命令充当了人类与机器之间的接口——这又是一个丰富的数据来源。甚至我们打给客服中心的电话也可以转录,这样它们就变成了文本数据。这些电话可以被录音,并且使用语音转文本转换,我们可以生成大量的文本数据。
7.3 文本数据的应用案例
本节中讨论的并非所有用例都可以实现无监督学习。一些还需要监督学习。然而,为了增加你的知识,我们根据监督学习和无监督学习分享这两种类型的用例:
-
情感分析——你可能参加过调查或对产品/调查提供反馈。这些调查产生了大量的文本数据。这些文本数据可以进行分析,我们可以确定评论中的情感是正面还是负面。简单来说,情感分析衡量文本数据的积极或消极。因此,我们可以看到客户对产品或服务的看法。我们可以使用监督学习和无监督学习进行情感分析。
-
新闻分类或文档分类——查看谷歌新闻网页,你会发现每条新闻都被分类到体育、政治、科学、商业或其他类别。进入的新闻是根据新闻内容进行分类的,即实际的文本。想象一下以这种方式排序的成千上万份文件。在这个用例中,很明显,鉴于手动排序此类项目所需的时间和精力不可行,机器学习是理想的。监督学习解决方案对这类问题效果良好。
-
语言翻译—将文本从一种语言翻译成另一种语言是一个非常有趣的用例。使用自然语言处理(NLP),我们可以在不同语言之间进行翻译。语言翻译非常棘手,因为不同的语言有不同的语法规则。通常,基于深度学习的解决方案最适合语言翻译。
-
垃圾邮件过滤—可以使用自然语言处理(NLP)和监督机器学习来设置电子邮件垃圾邮件过滤器。一个监督学习算法可以分析传入的邮件参数,并预测该邮件是否属于垃圾邮件文件夹。预测可以基于各种参数,如发件人电子邮件 ID、主题行、邮件正文、附件、邮件时间等。通常,在这里使用监督学习算法。
-
词性标注—这是其中一个流行的用例。这意味着我们可以在句子中区分名词、形容词、动词、副词等。命名实体识别也是自然语言处理(NLP)的著名应用之一。它涉及在句子中识别人、地点、组织、时间或数字。例如,约翰住在伦敦,在谷歌工作。命名实体识别可以生成类似[John][Person]住在[London][Location]并为[Google][organization]工作的理解。
-
句子生成、图像标题、语音转文本或文本转语音任务以及手写识别—这些都是一些其他重要且流行的用例。
这里列出的用例并不全面。还有许多其他可以用自然语言处理(NLP)实现的用例。自然语言处理(NLP)也是一个非常流行的研究领域。我们在本章末尾分享了一些重要的论文。
你可能也听说过像 ChatGPT、Bard 和 Claude 这样的大型语言模型(LLMs)。它们是处理自然语言输入并基于它们已经看到的预测下一个单词的算法。在通用人工智能(GenAI)的背景下,许多用例可以通过简单地调用 API 来解决。ChatGPT 可以像有记忆的人类一样进行交流,并为许多服务提供客户支持。LLMs 可以总结数百份 PDF 文档。你甚至可以创建可以用于从多个文档和网站上获取答案的应用程序。当然,通用人工智能在这里增强了这种能力。
虽然文本数据非常重要,但同时分析起来也相当困难。记住,我们的计算机和处理器只理解数字。因此,文本需要被表示为数字,这样我们才能对其进行数学和统计计算。在深入准备文本数据之前,我们讨论了我们在处理文本数据集时面临的挑战。
7.4 文本数据面临的挑战
文本是一种难以处理的数据类型。表达相同思想有多种排列组合。例如,我可能会问,“嘿,伙计,你多大了?”或者“你好,我可以知道你多大了?”——它们的意思是一样的,对吧?这两个问题的答案是一样的,对人类来说很容易理解,但对机器来说可能是一项艰巨的任务。
在这个领域,我们面临的一些最常见的挑战如下:
-
文本数据可能难以处理。文本中可能存在大量的垃圾字符,如$^%*&。
-
随着现代通信的出现,我们开始使用单词的缩写形式;例如,“u”可以表示“你”,“brb”表示“马上回来”,等等。此外,挑战在于同一个词对不同的人来说可能有不同的含义,或者一个字母的拼写错误可能会改变整个词的意义。
-
语言正在变化,没有界限,并且不断进化。它每天都在变化,新的词汇被添加到语言中。如果你进行简单的谷歌搜索,你会发现每年都有很多词汇被添加到词典中。
-
世界上有近 6500 种语言,每一种都承载着其独特的特征。每一种语言都完善了我们的世界。每一种语言都遵循其自身的规则和语法,这些规则和模式在用法上都是独特的。甚至书写方式也可能不同:有的从左到右书写,有的从右到左书写,有的甚至垂直书写。同样的情感在不同的语言中可能需要更少或更多的词汇。
-
一个词的意义取决于上下文。一个词可以是形容词也可以是名词,这取决于上下文。考虑以下例子:
-
“这本书是必读之作”和“请为我预订一个房间。”
-
“Tommy”可以是一个名字,但用作“Tommy Hilfiger”时,其用法完全改变。
-
“苹果”既是水果也是公司。
-
“四月”是一个月份,也可以是一个名字。
-
-
看一个更例子:“马克从英国旅行到法国,并在那里与约翰一起工作。他想念他的朋友们。”人类可以轻易地理解第二个句子中的“他”指的是马克而不是约翰,这可能对机器来说并不那么简单。
-
对于同一个词,可能有多个同义词,例如,“好”可以用“积极”、“精彩”、“出色”或“卓越”在不同的场景中替换。像“学习”、“勤奋”和“学习”这样的词与同一个词根“学习”相关。
-
文本数据的大小也可能令人畏惧。管理文本数据集,存储它,清理它,并更新它是一项艰巨的任务。
就像任何其他机器学习项目一样,文本分析遵循机器学习的原则,尽管精确的过程略有不同。回想一下第一章我们考察了机器学习项目的过程,如图 7.2 所示。如果你需要,建议你回顾一下第一章中的过程。

图 7.2 数据科学项目中整体步骤对于文本数据是相同的。文本数据的预处理与结构化数据集非常不同。
定义业务问题、数据收集和监控等保持不变。主要区别在于文本的处理,这涉及到数据清理、特征创建、文本数据表示等。我们将在下一节中介绍。
练习 7.1
回答这些问题以检查你的理解:
-
注意文本数据最有效的三个用例。
-
为什么处理文本数据如此繁琐?
7.5 预处理文本数据
文本数据,就像任何其他数据源一样,可能会很混乱和嘈杂。我们在数据发现阶段清理一些,在预处理阶段清理很多。同时,我们应该从我们的数据集中提取特征。清理过程中的某些步骤是常见的,可以在大多数文本数据集中实现。某些文本数据集可能需要定制的方法。我们首先从清理原始文本数据开始。
7.6 数据清理
与任何形式的数据分析一样,确保良好的数据质量至关重要。文本数据越干净,分析就越好。同时,预处理不是一个简单的任务,而是一个复杂且耗时的任务。
文本数据必须被清理,因为它包含大量的垃圾字符、无关的词、噪声和标点符号、URL 等。清理文本数据的主要方式包括
- 停用词去除——在所有任何语言中使用的词中,有一些是最常见的。停用词是在词汇中最常见的词,它们的重要性低于关键词。例如,“是”、“一个”、“这”、“一个”、“是”、“有”、“曾经”、“它”等。一旦我们从文本中去除停用词,数据的维度就减少了,因此解决方案的复杂性也减少了。
我们可以定义一个定制的停用词列表并以此方式去除它们,或者有标准库可以去除停用词。
同时,在去除停用词时,我们必须非常了解上下文。例如,如果我们问一个问题“是否在下雨?”那么答案“是的”本身就是完整的答案。当我们处理上下文信息重要的解决方案时,我们不去除停用词。
-
基于频率的词去除——有时你可能希望去除你文本中最常见的词或者非常独特的词。这个过程是获取文本中词的频率,然后设置一个频率阈值。我们可以去除最常见的词。或者你可能希望去除在整个数据集中只出现一次/两次的词。根据需求,你将做出决定。同时,我们在去除词的时候应该谨慎并尽职。
-
基于库的清理——当我们希望使用预定义和定制的库来清理数据时,会进行此操作。我们可以创建一个包含我们不希望出现在文本中的单词的仓库,并迭代地从文本数据中移除它们。这种方法使我们能够灵活地实施我们自己的选择来清理。
-
垃圾或不想要的字符——文本数据,尤其是推文、评论等,可能包含大量的 URL、标签、数字、标点符号、社交媒体提及、特殊字符等。我们可能需要从文本中清理它们。同时,我们应该小心,因为在一个领域不重要的单词可能在另一个领域是必需的。如果数据是从网站或 HTML/XML 源抓取的,我们需要去除所有的 HTML 实体、标点符号、非字母字符等。
TIP 在清理文本数据时,始终牢记业务背景。
正如我们所知,许多新的表达方式已经进入语言中——例如,lol,hahahaha,brb,rofl 等。这些表达应该转换为它们的原始含义。甚至像:-) ,😉 这样的表情符号也应该转换为它们的原始含义。
-
数据编码——有一些数据编码可供选择,如 ISO/IEC,UTF-8 等。通常,UTF-8 是最受欢迎的。但这并不是一个硬性规定,总是只能使用 UTF-8。
-
词典规范化——根据上下文和用法,同一个词可能会有不同的表示方式。在词典规范化过程中,我们清理这些歧义。基本思想是将单词还原到其词根形式。因此,来自彼此的单词可以映射到中心词,前提是它们有相同的核心意义。
图 7.3 显示,相同的单词“eat”被用于各种形式。词根是“eat”,但这些不同的形式展示了“eat”的许多不同表示形式。

图 7.3 “Ate”,“eaten”,“eats”和“eating”都有相同的词根:“eat”。词干提取和词形还原都可以用来获取词根。
在这里,我们希望将这些像“eating”,“eaten”等单词映射到它们的中心词“eat”,因为它们有相同的核心意义。处理这个问题有两种主要方法:
-
-
词干提取是一种基本的基于规则的将单词映射到其核心词的方法。它从单词的末尾移除“es”,“ing”,“ly”,“ed”等。例如,“studies”将变成“studi”,“studying”将变成“study”。作为一个基于规则的方法,输出拼写可能并不总是准确的。
-
词形还原是一种有组织的步骤,它将单词还原到它们的词典形式。一个单词的词元是其词典或规范形式。例如,“eats”,“eating”,“eaten”等,都有相同的词根“eat”。词形还原比词干提取提供更好的结果,但它需要更多的时间。
-
这些只是清理文本数据的一些方法。这些技术会有所帮助,但还需要商业洞察力来进一步理解数据集。我们将通过开发 Python 解决方案来使用这些方法清理文本数据。
数据清理完毕后,我们开始处理数据的表示,以便它可以被机器学习算法处理,这是我们下一个话题。
7.7 从文本数据集中提取特征
我们已经探讨了清理杂乱文本数据的概念和技术。现在我们已经清理了数据,它已经准备好被使用。下一步是将这些数据以算法可以理解的形式表示出来。正如我们所知,我们的算法只能理解数字。
一种非常简单的将文本数据编码成对机器学习有用的方式是简单地对我们单词进行独热编码,并以矩阵的形式表示它们——但如果你有一个完整的文档,这肯定不是一个可扩展的方法。
注意:独热编码在附录中有介绍。
首先将单词转换为小写,然后按字母顺序排序。然后为它们分配一个数字标签。最后,将单词转换为二进制向量。让我们用一个例子来理解。
如果文本是“它正在下大雨”,我们将使用以下步骤:
-
将单词转换为小写,以便输出结果为“it is raining heavily。”
-
按字母顺序排列它们。结果是 heavily, is, it, raining。
-
将每个单词分配一个位置值,例如 heavily:0, is:1, it:2, raining:3。
-
将它们转换为如这里所示的二进制向量:
[0. 0. 1. 0.] #it
[0. 1. 0. 0.] #is
[0. 0. 0. 1.] #raining
[1. 0. 0. 0.]] #heavily
如我们所见,我们能够用二进制向量表示每个单词,其中 0 或 1 是每个单词的表示。尽管这种方法非常直观且易于理解,但在我们有一个庞大的语料库和词汇表时,在实践上是不可能的。
注意:语料库指的是一组文本。它在拉丁语中意为“身体”。它可以是一组书面文字或口头文字,可以用来进行语言分析。
此外,处理具有如此多维度的海量数据将非常昂贵。因此创建的矩阵也将非常稀疏。因此,我们应该考虑其他方法和手段来表示我们的文本数据。
与独热编码相比,有更好的替代方案。这些技术关注单词的频率或单词被使用的上下文。这种文本表示的科学方法更加准确、健壮和具有解释性。有多个这样的技术,如词频-逆文档频率(TF-IDF)、词袋方法等。我们将在本章后面讨论这些技术中的几个。首先,我们需要检查分词这个重要概念。
7.8 分词
分词简单来说就是将文本或一组文本分解成单个标记。它是自然语言处理的基石。看看图 7.4 中的例子,我们为句子中的每个单词创建了单个标记。分词是一个重要的步骤,因为它允许我们为每个单词分配唯一的标识符或标记。一旦我们为每个单词分配了特定的标记,分析就会变得不那么复杂。

图 7.4 分词可以将句子分解成不同的单词标记。
标记通常用于单个单词,但这并不总是必要的。我们允许对单词或单词中的子词或字符进行分词。在子词的情况下,同一个句子可以有子词标记,如“rain-ing”(即“rain”和“ing”作为单独的子标记)。
如果我们希望在字符级别进行分词,那可能就是“r-a-i-n-i-n-g”。事实上,在上一个章节讨论的一热编码方法的第一步中,对单词进行了分词。在字符级别进行分词不一定总是需要。
注意:分词是自然语言处理解决方案的基石。
一旦我们获得了标记,就可以使用这些标记来准备词汇表。词汇表是语料库中所有唯一标记的集合。
有多个库用于分词。正则表达式分词使用给定的模式参数来匹配标记或标记之间的分隔符。空白符分词将任何空白字符序列视为分隔符。然后我们有空白行,它使用空白行序列作为分隔符。最后,单词和标点符号通过匹配一系列字母字符和一系列非字母和非空白字符进行分词。当我们在文本数据中创建 Python 解决方案时,我们将执行分词操作。
接下来,我们将探讨更多表示文本数据的方法。第一种方法是词袋(BOW)方法。
7.9 词袋方法
正如其名所示,语料库中的所有单词都被使用。在词袋方法中,对语料库中的每个单词进行分词,然后计算每个标记的相应频率。在这个过程中,我们忽略了单词的语法、顺序和上下文。我们只关注简单性。因此,我们将每个文本(句子或文档)表示为其“自己的单词袋”。
在整个文档的词袋方法中,我们将语料库的词汇定义为语料库中出现的所有唯一单词。请注意,我们使用语料库中的所有唯一单词。如果我们想,我们也可以设置一个阈值(即所选单词频率的上限和下限)。一旦我们有了唯一单词,每个句子都可以用一个与基本词汇向量相同维度的向量来表示。这种向量表示包含了句子中词汇中每个单词的频率。这可能听起来很复杂,但实际上这是一个简单直接的方法。
让我们用一个例子来理解这种方法。假设我们有两个句子:“It is raining heavily”和“We should eat fruits.”为了表示这两个句子,我们计算这些句子中每个单词的频率,如图 7.5 所示。

图 7.5 已计算每个单词的频率。在这个例子中,我们有两个句子。
现在如果我们假设这两个句子中的单词代表整个词汇表,我们可以将第一个句子表示如图 7.6 所示。请注意,表中包含所有单词,但句子中不存在的单词已收到 0 的值。

图 7.6 我们假设词汇表中只有两个句子,第一个句子将按如下所示表示。
在这个例子中,我们考察了如何使用 BOW 方法将句子表示为一个向量。但是,BOW 方法并没有考虑单词的顺序或上下文。它只关注单词的频率。因此,它是一种非常快速的数据表示方法,与同类方法相比,计算成本更低。由于它是基于频率的,它通常用于文档分类。
但是,由于其纯粹基于频率的计算和表示,使用 BOW 方法的解决方案准确性可能会受到影响。在语言中,单词的上下文起着重要作用。正如我们之前看到的,苹果既是水果也是一个著名的品牌和组织。这就是为什么我们有其他更先进的方法,这些方法考虑的参数比频率更多。其中一种方法就是 TF-IDF,我们将在下一节学习。
练习 7.2
回答这些问题以检查你的理解:
-
用简单的话解释分词,就像你向一个不知道 NLP 的人解释一样。
-
词袋模型方法使用单词的上下文,而不仅仅是频率。对还是错?
-
词形还原比词干提取方法不那么严格。对还是错?
7.10 术语频率和逆文档频率
在 BOW 方法中,我们只重视单词的频率。但是,频率较高的单词并不总是比罕见但更重要单词提供的信息更有意义。例如,假设我们有一组医学文档,我们希望比较两个单词:“疾病”和“糖尿病”。由于语料库由医学文档组成,单词“疾病”肯定更频繁,而单词“糖尿病”将较少出现但更重要,以识别处理糖尿病的文档。术语频率和逆文档频率(TF-IDF)方法使我们能够解决这个问题,并从更重要的单词中提取信息。
在 TF-IDF 中,我们考虑单词的相对重要性。TF 代表术语频率,IDF 代表逆文档频率。我们可以这样定义 TF-IDF:
-
TF 是整个文档中一个术语的计数(例如,单词“a”在文档“D”中的计数)。
-
IDF 是整个语料库中总文档数(N)与包含单词“a”的文档数(d**f)之比的对数。
因此,TF-IDF 公式将给我们整个语料库中单词的相对重要性。数学公式是 TF 和 IDF 的乘积,由方程 7.1 给出:

(7.1)
其中N是语料库中文档的总数,tf[i][,][j]是单词在文档中的频率,df[i]是语料库中包含该单词的文档数。
这个概念可能听起来很复杂。让我们用一个例子来理解这个概念。假设我们有一百万篇体育期刊的集合。这些体育期刊包含各种长度的文章。我们还假设所有文章都只使用英语。所以,在这些文档中,我们想要计算“地面”和“反手”这两个词的 TF-IDF 值。
假设我们有一篇包含 100 个单词的文档,其中“地面”这个词出现了 5 次,而“反手”只出现了 2 次。因此,“地面”的 TF 是 5/100 = 0.05,而“反手”的 TF 是 2/100 = 0.02。
我们了解到,“地面”这个词在体育中相当常见,而“反手”这个词则不太常用。现在我们假设在 100 万份文档中有 10 万份包含“地面”,而“反手”只出现在 10 份中。因此,“地面”的 IDF 是 log (1,000,000/100,000) = log (10) = 1。对于“反手”,它将是 log (1,000,000/10) = log (100,000) = 5。
要得到“地面”的最终值,我们需要将 TF 和 IDF 相乘,即 0.05 × 1 = 0.05。要得到“反手”的最终值,我们需要将 TF 和 IDF 相乘,即 0.02 × 5 = 0.1。
在这个例子中,我们可以观察到“反手”这个词的相对重要性比“地面”这个词的相对重要性更高。这是 TF-IDF 相对于基于频率的 BOW 方法的优点。但是,与 BOW 相比,TF-IDF 的计算需要更多的时间,因为所有 TF 和 IDF 都必须计算。尽管如此,TF-IDF 在这种情况下提供了一个更好、更成熟的解决方案。因此,在讨论单词相对重要性的情况下,我们可以使用 TF-IDF。例如,如果任务是筛选关于心脏病学的医学文档,那么“血管造影”这个词的重要性会更高,因为它与心脏病学的关系更为密切。
到目前为止,我们已经介绍了 BOW 和 TF-IDF 方法。但在这些方法中,我们都没有考虑单词的顺序,这是语言模型所涵盖的内容。我们将在下一节介绍语言模型。
7.11 语言模型
语言模型为单词序列分配概率。N-gram 是语言模型中最简单的。我们知道,为了分析文本数据,它们必须被转换为特征向量。N-gram 模型创建特征向量,以便文本可以以可以进一步分析的形式表示。
N-gram 是一种概率语言模型。在 n-gram 模型中,我们计算给定 (N – 1) 个单词序列的第 N 个单词的概率。更具体地说,n-gram 模型将根据单词 x[i][–(][n–][1][)],x[i][–(][n–][2][)]…x[i][–1] 来预测下一个单词 x[i]。如果我们希望使用概率术语,我们可以将它们表示为给定先前单词的 x[i] 的条件概率,可以表示为 P(x[i] | x[i][–(][n–][1][)], x[i][–(][n–][2][)]…x[i][–1])。概率是通过使用文本语料库中出现的序列的相对频率来计算的。
注意:如果项目是单词,n-gram 可以被称为 shingles。
让我们用一个例子来研究这个问题。我们将取一个句子,然后通过使用句子中的单词来分解其含义。考虑我们有一个句子“它正在下大雨。”我们通过在图 7.6 中使用不同的 n 值来展示这个句子的相应表示。你应该注意,对于不同的 n 值,单词序列及其相应的组合是如何变化的。如果我们希望使用 n = 1 或单个单词来进行预测,表示将如图 7.7 所示。注意,这里每个单词都是单独使用的。它们被称为 unigrams。

图 7.7 单词、二元组和三元组可以用来表示同一个句子。这个概念也可以扩展到 n-gram。
如果我们希望使用 n = 2,使用的单词数量将变为两个。它们被称为 bigrams。如果我们使用 n = 3,单词数量变为三个,它们被称为 trigrams,依此类推。
因此,如果我们有一个 unigram,它是一个单词的序列;对于两个单词,它是一个 bigram;对于三个单词,它是一个 trigram;依此类推。所以,一个 trigram 模型将通过使用前两个单词的条件概率来近似给定所有先前单词的单词概率,而一个 bigram 将通过只考虑前一个单词来完成同样的工作。这确实是一个有效的假设,即单词的概率将只取决于前一个单词,这被称为 Markov 假设。通常,n > 1 被认为比 unigrams 更有信息量。但显然,计算时间也会增加。
n-gram 方法对 n 的选择非常敏感。它还显著依赖于所使用的训练语料库,这使得概率高度依赖于训练语料库。因此,如果遇到未知单词,模型在处理这个新单词时将非常困难。
接下来,我们创建一个 Python 示例。我们将展示一些使用 Python 进行文本清理的示例。
7.12 使用 Python 进行文本清理
您可能需要安装几个库。我们将展示一些小的代码片段。建议您按照示例使用它们。我们还包括代码片段及其结果的相应截图:
代码 1:删除文本中的空白空间。导入库re;它被称为正则表达式(Regex)。文本是“外面在下雨”,其中包含很多空白空间(见图 7.8):
import re
doc = "It is raining outside"
new_doc = re.sub("\s+"," ", doc)
print(new_doc)

图 7.8 删除空白空间
代码 2:现在我们将从文本数据中删除标点符号(见图 7.9):
text_d = "Hey!!! How are you doing? And how is your health! Bye, take care."
re.sub("[^-9A-Za-z ]", "" , text_d)

图 7.9 删除标点符号
代码 3:这里还有一种删除标点的方法(见图 7.10):
import string
text_d = "Hey!!! How are you doing? And how is your health! Bye, take care."
cleaned_text = "".join([i for i in text_d if i not in string.punctuation])
cleaned_text

图 7.10 删除标点的另一种方法
代码 4:我们现在将删除标点符号并将文本转换为小写(见图 7.11):
text_d = "Hey!!! How are you doing? And how is your health! Bye, take care."
cleaned_text = "".join([i.lower() for i in text_d if i not in
string.punctuation])
cleaned_text

图 7.11 将文本转换为小写
代码 5:这里使用标准的nltk库进行标记化(见图 7.12):
import nltk
text_d = "Hey!!! How are you doing? And how is your health! Bye, take care."
nltk.tokenize.word_tokenize(text_d)

图 7.12 标记化
注意,在代码的输出中,我们包含了所有的单词,包括标点符号,作为不同的标记。如果您希望排除标点符号,可以使用之前分享的代码片段进行清理。
代码 6:接下来是停用词。我们将使用nltk库来删除停用词。之后,我们对单词进行标记化(见图 7.13):
stopwords = nltk.corpus.stopwords.words('english')
text_d = "Hey!!! How are you doing? And how is your health! Bye, take care."
text_new = "".join([i for i in text_d if i not in string.punctuation])
print(text_new)
words = nltk.tokenize.word_tokenize(text_new)
print(words)
words_new = [i for i in words if i not in stopwords]
print(words_new)

图 7.13 删除停用词并进行单词标记化
代码 7:我们现在将对一个文本示例进行词干提取。我们使用nltk库来完成这项工作。首先对单词进行标记化,然后应用词干提取(见图 7.14):
import nltk
from nltk.stem import PorterStemmer
stem = PorterStemmer()
text = "eats eating studies study"
tokenization = nltk.word_tokenize(text)
for word in tokenization:
print("Stem for {} is {}".format(word, stem.stem(word)))

图 7.14 标记化然后词干提取单词
代码 8:我们现在对一个文本示例进行词形还原。我们使用nltk库来完成这项工作。首先对单词进行标记化,然后应用词形还原(见图 7.15):
import nltk
from nltk.stem import WordNetLemmatizer
wordnet_lemmatizer = WordNetLemmatizer()
text = "eats eating studies study"
tokenization = nltk.word_tokenize(text)
for word in tokenization:
print("Lemma for {} is {}".format(word, wordnet_lemmatizer.lemmatize(word)))

图 7.15 标记化然后词形还原单词
观察并比较词干提取和词形还原两种输出的差异。对于“studies”和“studying”,词干提取生成了“studi”的输出,而词形还原生成了正确的“study”输出。
到目前为止,我们已经介绍了 BOW、TF-IDF 和 n-gram 方法。但在所有这些技术中,都忽略了单词之间的关系。这种关系在词嵌入中得到了应用,这是我们下一个话题。
7.13 词嵌入
一个词的特点在于它所伴随的词。——约翰·鲁伯特·费思
到目前为止,我们已经研究了多种方法,但所有技术都忽略了单词之间的上下文关系。让我们用一个例子来更仔细地看看。
假设我们的词汇量中有 100,000 个单词,从“aa”(玄武岩熔岩)到“zoom。”现在,如果我们执行独热编码,所有这些单词都可以以向量形式表示。每个单词都将有一个唯一的向量。例如,如果单词“king”的位置是 21,000,那么向量将具有以下形状,第 21,000 个位置为 1,其余位置为 0:
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0…………………1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
这种方法有几个明显的问题:
-
维度数量非常高,计算起来很复杂。
-
数据在本质上非常稀疏。
-
如果必须输入n个新单词,词汇量将增加n,因此每个向量的维度也将增加n。
-
这种方法忽略了单词之间的关系。我们知道“ruler”、“king”和“monarch”有时可以互换使用。在独热编码方法中,任何此类关系都被忽略。
如果我们希望执行语言翻译或生成聊天机器人,我们需要将这些知识传递给机器学习解决方案。词嵌入提供了解决方案。它们将高维度的单词特征转换为低维度,同时保持上下文关系。词嵌入使我们能够创建更通用的模型。我们可以通过查看示例来理解意义。
注意:在启用 LLM 的解决方案中,你可能不需要执行很多这些步骤。
在图 7.16 所示的示例中,“man”与“woman”的关系类似于“king”与“queen”;“good”与“nice”的关系类似于“bad”与“awful”;或者“UK”与“London”的关系类似于“Japan”与“Tokyo”。

图 7.16 词嵌入可以用来表示单词之间的关系。例如,存在从“men”到“women”的关系,这与“king”到“queen”的关系相似,因为“men-women”和“king-queen”都代表了男女性别关系。
简而言之,使用词嵌入,我们可以表示具有相似意义的单词。词嵌入可以被视为一类技术,其中我们在预定义的向量空间中表示每个单独的单词。语料库中的每个单词都映射到一个向量。基于单词的使用情况来理解分布式表示。因此,可以类似使用的单词具有相似的表现。这使得解决方案能够捕捉单词及其关系的潜在意义。因此,单词的意义起着重要作用。这种表示比 BOW 方法更智能,在 BOW 方法中,每个单词都被单独对待,不考虑其使用情况。此外,与独热编码相比,维度数量更少。每个单词由 10s 或 100s 个维度表示,这比独热编码方法中用于表示的 1000s 个维度要少得多。
我们将在下一节中介绍两种最流行的技术——Word2Vec 和用于词表示的全局向量(GloVe)。Word2Vec 和 GloVe 的数学基础超出了本书的范围。我们提供了对解决方案工作原理的理解,然后使用 Word2Vec 和 GloVe 开发 Python 代码。这一节涉及更多的技术性内容,所以如果您只对解决方案的应用感兴趣,可以跳过下一节。
7.14 Word2Vec 和 GloVe
Word2Vec 首次发表于 2013 年。它由 Google 的 Tomas Mikolov 等人开发。我们将在本章末尾分享论文的链接。如果您想详细了解更技术性的元素,建议您仔细研究这篇论文。
Word2Vec 是一组用于生成词嵌入的模型。输入是一个大型文本语料库。输出是一个具有非常多个维度的向量空间。在这个输出中,语料库中的每个词都被分配了一个独特且对应的向量。最重要的是,在语料库中具有相似或共同上下文的词在产生的向量空间中彼此靠近。
在 Word2Vec 中,研究人员引入了两种不同的学习模型——连续词袋(CBOW)和连续跳字模型:
-
在 CBOW 中,模型从周围上下文词的窗口中预测当前词。因此,CBOW 模型根据文本中周围词的上下文来预测目标词。回想一下,在 BOW 方法中,词的顺序不起任何作用。相比之下,在 CBOW 中,词的顺序是重要的。
-
连续跳字模型使用当前词来预测周围窗口的上下文词。在这个过程中,它相对于远距离词,给邻近词分配更多的权重。
GloVe 是一种用于生成词向量表示的无监督学习算法。它由斯坦福大学的 Pennington 等人开发,并于 2014 年推出。它是两种技术的结合:矩阵分解技术和 Word2Vec 中使用的基于局部上下文的学习。GloVe 可以用来找到诸如邮编和城市、同义词等关系。它为具有相同形态结构的词生成单个向量集。
Word2Vec 和 GloVe 都从它们的词的共现信息中学习和理解它们的词向量表示。共现意味着词在一个大型语料库中一起出现的频率。主要区别在于 Word2Vec 是一个基于预测的模型,而 GloVe 是一个基于频率的模型。Word2Vec 在给定一个词的情况下预测上下文,而 GloVe 通过创建一个共现矩阵来学习一个词在给定上下文中出现的频率。
练习 7.3
回答以下问题以检查您的理解:
-
BOW 比 TF-IDF 方法更严格。对或错?
-
区分 Word2Vec 和 GloVe。
现在我们将转向案例研究和 Python 实现。
7.15 使用 Python 实现的情感分析案例研究
到目前为止,我们已经讨论了许多关于自然语言处理和文本数据的概念。在本节中,我们首先探讨一个商业案例,然后基于此开发一个 Python 解决方案。在这里,我们正在进行情感分析。
产品评论是客户和组织的信息宝库。每当我们要购买任何新产品或服务时,我们往往会查看其他客户的评论。你可能自己已经评论过产品和服务。这些评论可以在亚马逊、博客、调查等地方找到。
让我们考虑一个案例。一家电信运营商收到了来自其客户的投诉、关于服务的评论以及关于整体体验的评论。这些流可以是产品质量、定价、入网体验、注册便捷性、支付流程、一般评论、客户服务等等。我们想要确定评论的一般背景——是正面、负面还是中性。这些评论包括分配的星级数量、实际文本评论、关于产品/服务的优缺点、属性等。然而,存在一些商业问题——例如,
-
有时产品/服务收到的星级数量非常高,而实际评论却相当负面。
-
组织和产品负责人需要知道哪些功能受到客户的喜爱,哪些功能不受客户的喜爱。然后团队可以针对不受欢迎的功能进行改进。
-
需要衡量和关注竞争!组织需要了解其竞争对手的流行产品的属性。
-
产品负责人希望更好地规划他们未来希望发布的即将到来的功能。
因此,业务团队将能够回答这些重要问题:
-
我们的产品和服务的客户满意度如何?
-
客户的主要痛点和不满意是什么?
-
什么驱使了客户的参与度?
-
哪些服务复杂且耗时,哪些是最受欢迎的服务/产品?
这个业务用例将带来以下业务效益:
-
最令人满意和最受欢迎的产品和服务应该继续进行。
-
不受欢迎且收到负面评分的产品应该得到改进,挑战得到缓解。
-
相关团队,如财务、运营、投诉、CRM 等,可以被通知,并且他们可以单独工作以改善客户体验。
-
对于喜欢或不喜欢服务的确切原因将有助于相关团队朝着正确的方向工作。
-
总体而言,它将为衡量客户基础的净推荐分数提供一个基准。企业可以努力提高整体客户体验。
我们可能希望通过仪表板来展示这些发现。这个仪表板将按照定期周期刷新,比如每月或每季度。
为了解决这个商业问题,团队可以从网站、调查、亚马逊、博客等地方收集相关数据。然后可以对数据集进行分析。分析结构化数据相对容易。在这个例子中,我们处理文本数据。
Python Jupyter 笔记本已推送到 GitHub 位置。建议您使用 GitHub 位置的 Jupyter 笔记本,因为它包含更多步骤。步骤如下:
- 导入所有库:
#### Loading all the required libraries here
from lxml import html
import requests
import pandas as pd
from nltk.corpus import stopwords
from textblob import TextBlob
import matplotlib.pyplot as plt
import sys
import numpy as np
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
import sklearn
import scikitplot as skplt
import nltk
#to ignore warnings
import warnings
warnings.filterwarnings("ignore")
nltk.download('stopwords')
nltk.download('punkt')
nltk.download('wordnet')
-
- 定义标签。这些标签用于从评论中获取产品的属性:
xpath_reviews = '//div[@data-hook="review"]'
reviews = parser.xpath(xpath_reviews)
xpath_rating = './/i[@data-hook="review-star-rating"]//text()'
xpath_title = './/a[@data-hook="review-title"]//text()'
xpath_author = './/a[@data-hook="review-author"]//text()'
xpath_date = './/span[@data-hook="review-date"]//text()'
xpath_body = './/span[@data-hook="review-body"]//text()'
xpath_helpful = './/span[@data-hook="helpful-vote-statement"]//text()'
-
- 准备好提取数据。我们创建一个数据框来存储客户评论。然后我们遍历所有评论并提取信息:
# Create a dataframe here.
reviews_df = pd.DataFrame()
for review in reviews:
rating = review.xpath(xpath_rating)
title = review.xpath(xpath_title)
author = review.xpath(xpath_author)
date = review.xpath(xpath_date)
body = review.xpath(xpath_body)
helpful = review.xpath(xpath_helpful)
review_dict = {'rating': rating,
'title': title,
'author': author,
'date': date,
'body': body,
'helpful': helpful}
reviews_df = reviews_df.append(review_dict, ignore_index=True)
all_reviews = pd.DataFrame()
-
- 遍历评论并填写详细信息:
# Fill the values of the reviews here.
for i in range(1,90):
amazon_url = 'https://www.amazon.co.uk/Hive-Heating-Thermostat-Professional-Installation/product-reviews/B011B3J6KY/ref=cm_cr_othr_d_show_all?ie=UTF8&reviewerType=all_revie
ws&pageNumber='+str(i)
headers = {'User-Agent': user_agent}
page = requests.get(amazon_url, headers = headers)
parser = html.fromstring(page.content)
xpath_reviews = '//div[@data-hook="review"]'
reviews = parser.xpath(xpath_reviews)
reviews_df = pd.DataFrame()
xpath_rating = './/i[@data-hook="review-star-rating"]//text()'
xpath_title = './/a[@data-hook="review-title"]//text()'
xpath_author = './/a[@data-hook="review-author"]//text()'
xpath_date = './/span[@data-hook="review-date"]//text()'
xpath_body = './/span[@data-hook="review-body"]//text()'
xpath_helpful = './/span[@data-hook="helpful-vote-statement"]//text()'
#print(i)
for review in reviews:
rating = review.xpath(xpath_rating)
title = review.xpath(xpath_title)
author = review.xpath(xpath_author)
date = review.xpath(xpath_date)
body = review.xpath(xpath_body)
helpful = review.xpath(xpath_helpful)
review_dict = {'rating': rating,
'title': title,
'author': author,
'date': date,
'body': body,
'helpful': helpful}
reviews_df = reviews_df.append(review_dict, ignore_index=True)
#print(reviews_df)
all_reviews = all_reviews.append(reviews_df)
-
- 查看我们生成的输出:
all_reviews.head()
-
- 将输出保存到路径。你可以指定自己的路径:
out_folder = '/Users/Data/'
all_reviews.to_csv(out_folder + 'Reviews.csv')
-
- 加载数据并进行分析:
#Load the data now and analyse it
data_path = '/Users/vaibhavverdhan/Book/UnsupervisedLearningBookFinal/'
reviewDataCSV = 'Reviews.csv'
reviewData = (pd.read_csv(data_path+reviewDataCSV,index_col=0,))
-
- 查看数据集的基本信息:
reviewData.shape
reviewData.rating.unique()
reviewData.rating.value_counts()
-
- 查看评论中给出的星级分布。这将使我们能够理解客户给出的评论:
labels = '5 Stars', '1 Star', '4 Stars', '3 Stars', '2 Stars'
sizes = [reviewData.rating.value_counts()[0], reviewData.rating.value_counts()[1],reviewData.rating.value_counts()[2],rev
iewData.rating.value_counts()[3],reviewData.rating.value_counts()[4]]
colors = ['green', 'yellowgreen', 'coral', 'lightblue', 'grey']
explode = (0, 0, 0, 0, 0) # explode 1st slice
# Plot
plt.pie(sizes, explode=explode, labels=labels, colors=colors,
autopct='%1.1f%%', shadow=True, startangle=140)
plt.axis('equal')
plt.show()
-
- 将文本转换为小写,然后移除停用词和频率最高的词:
reviewData.body = reviewData.body.str.lower()
reviewData.body = reviewData.body.str.replace('[^\w\s]','')
stop = stopwords.words('english')
reviewData.body = reviewData.body.apply(lambda x: " ".join(x for x in
x.split() if x not in stop))
freq = list(freq.index)
reviewData.body = reviewData.body.apply(lambda x: " ".join(x for x in x.split() if x not in freq))
freq = pd.Series(' '.join(reviewData.body).split()).value_counts()[-10:]
freq = list(freq.index)
reviewData.body = reviewData.body.apply(lambda x: " ".join(x for x in
x.split() if x not in freq))
-
- 对数据进行分词:
from nltk.tokenize import word_tokenize
tokens = word_tokenize(reviewData.iloc[1,1])
print(tokens)
-
- 进行词元还原:
from textblob import Word
reviewData.body = reviewData.body.apply(lambda x: " ".join([Word(word).lemmatize() for word in x.split()]))
reviewData.body.head()
-
- 将所有评论追加到字符串:
sentimentString = reviewData.iloc[1,1]
# append to this string
for i in range(2,len(reviewData)):
sentimentString = sentimentString + reviewData.iloc[i,1]
-
- 进行情感分析。从
textblob中获取情感方法。它为情感生成极性和主观性。一个元素的 sentiment 极性是情感在表达中的方向;也就是说,它告诉我们文本是否表达了负面的、正面的或中性的情感。它主观地衡量并量化文本中观点和事实信息量。如果主观性高,这意味着文本中包含的观点比事实多:
- 进行情感分析。从
# the functions generates polarity and subjectivity here, subsetting the
polarity only here
allReviewsSentiment = reviewData.body[:900].apply(lambda x:
TextBlob(x).sentiment[0])
# this contains boths subjectivity and polarity
allReviewsSentimentComplete = reviewData.body[:900].apply(lambda x:
TextBlob(x).sentiment)
allReviewsSentimentComplete.head()
-
- 将情感保存到.csv 文件:
allReviewsSentiment.to_csv(out_folder + 'ReviewsSentiment.csv')
-
- 为情感分配一个含义或标签。我们将每个评分从极度满意到极度不满意进行分类:
allReviewsSentimentDF = allReviewsSentiment.to_frame()
# Create a list to store the data
grades = []
# For each row in the column,
for row in allReviewsSentimentDF['body']:
# if more than a value,
if row >= 0.75:
grades.append('Extremely Satisfied')
elif (row >= 0.5) & (row < 0.75):
grades.append('Satisfied')
elif (row >= 0.2) & (row < 0.5):
grades.append('Nice')
elif (row >= -0.2) & (row < 0.2):
grades.append('Neutral')
elif (row > -0.5) & (row <= -0.2):
grades.append('Bad')
elif (row >= -0.75) & (row < -0.5):
grades.append('Dis-satisfied')
elif row < -0.75:
grades.append('Extremely Dis-satisfied')
else:
# Append a failing grade
grades.append('No Sentiment')
# Create a column from the list
allReviewsSentimentDF['SentimentScore'] = grades
allReviewsSentimentDF.head()
-
- 查看情感评分并绘制它们。最后,我们将它们与主数据集合并:
allReviewsSentimentDF.SentimentScore.value_counts()
allReviewsSentimentDF['SentimentScore'].value_counts().plot(kind='bar')
#### Merge the review data with Sentiment generated
reviewData['polarityScore'] = allReviewsSentimentDF['body'] #1
1 添加列 polarityScore
在这个案例研究中,你不仅从网站上抓取了评论,还分析了数据集。如果我们比较情感,我们可以看到产品得到的星级并不代表真实情况。
图 7.17 比较了实际星级和情感分析的结果。我们可以观察到 73%的客户给出了五星评价,7%给出了四星评价,而在情感分析中,大多数评论都被归类为中性。这正是情感分析的真实力量!

图 7.17 比较了左侧原始星级分布和右侧情感分析的真实结果。
情感分析是一个非常重要的用例。它对商业和产品团队非常有用。前面的代码可以扩展到任何手头的商业问题。
现在我们转向第二个案例研究,即使用 Python 进行文档分类。
7.16 使用 Python 进行文本聚类
考虑这种情况:我们有一批文本数据集或文档,但它们都是混合在一起的。我们不知道哪些文本属于哪个类别。在这种情况下,我们将假设我们有两种类型的文本数据集:一种包含所有与足球相关的数据,另一种与旅行相关。我们将开发一个可以将这两个类别分开的模型。为此,我们遵循以下步骤:
- 导入所有库:
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import KMeans
import numpy as np
import pandas as pd
-
- 创建一个虚拟数据集。这些文本数据包含我们亲自写的几句话。有两个类别:
text = ["It is a good place to travel",
"Football is a nice game", "Lets go for holidays and travel to
Egypt",
"It is a goal, a great game.", "Enjoy your journey and forget
the rest", "The teams are ready for the same" ]
-
- 使用 TF-IDF 对数据进行向量化:
tfidf_vectorizer = TfidfVectorizer(stop_words='english')
X = tfidf_vectorizer.fit_transform(text)
-
- 进行聚类:
k = 2
model = KMeans(n_clusters=k, init='k-means++', max_iter=10, n_init=2)
model.fit(X)
-
- 表示质心并打印输出(见图 7.18):
centroids = model.cluster_centers_.argsort()[:, ::-1]
features = vectorizer.get_feature_names()
for i in range(k):
print("Cluster %d:" % i),
for ind in centroids[i, :10]:
print("%s" % terms[ind])

图 7.18 打印输出
您可以将此示例扩展到其他数据集。从互联网上获取数据集,并在前面的示例中复制代码。
我们已经将代码推送到本书的 GitHub 位置。建议您使用它。这确实是一个表示文本数据的重要来源。
7.17 GenAI 用于文本数据
GenAI 解决方案是一种新的无监督解决方案。你肯定听说过 ChatGPT 和 LLMs。它们已经改变了世界。GenAI 用于文本数据使用机器学习模型来创建类似人类的文本。它在大规模数据模式上训练,因此可以生成各种内容片段——例如,文章、技术报告和书籍摘要——并且可以像人类聊天界面一样操作。即使是复杂的多语言翻译,GenAI 也能使其变得简单。
GenAI 用于文本数据涉及使用高级算法,如 transformers,来生成连贯、上下文适当的文本。这些算法在庞大的数据集上训练。想象一下,我们将互联网上存在的海量内容输入到算法中。通过学习单词和句子之间的模式和关系,使用的语法、句法和语义,它们可以创建类似人类的响应。这些模型,如 OpenAI 的 GPT 或 Google 的 BERT,在撰写带有正确语言和语法的电子邮件、创建详细报告、用 Java/C++等语言编写代码模块以及许多其他任务方面非常强大。利用这种力量,内容创作者、作家和版权所有者、品牌经理和营销人员以及企业主可以以更可扩展和高效的方式生产高质量的文本。
尽管 GenAI 具有惊人的潜力,但仍有一些需要改进的领域。有时它会生成不准确的信息,也称为幻觉。确保输出保持无偏见和道德是另一个挑战,因为模型可能会无意中反映它们在训练数据中存在的 societal biases。AI 生成的文本越来越多地用于客户服务,在保持个性化语调的同时自动化响应。研究人员也在探索其在医疗和法律领域的应用,它可以帮助文档编制和起草。虽然 GenAI 正在改变文本生成的方式,但人类监督的需求仍然至关重要,以确保质量、准确性和公平性。
7.18 总结性思考
文本数据是最有用的数据集之一。文本中隐藏着大量的智慧:日志、博客、评论、帖子、推文、投诉、评论、文章等等——文本数据的来源很多。组织正在投资建立访问和存储文本数据的基础设施。分析文本数据需要比我们标准笔记本电脑更好的处理能力和更好的机器。它需要特殊的技能集和对概念的深入理解。自然语言处理(NLP)是一个不断发展的领域,正在进行大量的研究。同时,我们也不能忽视良好的商业洞察力和知识的重要性。
数据分析和机器学习并不容易。我们必须理解围绕数据清理、探索、表示和建模的许多概念。但分析非结构化数据可能比分析结构化数据集更复杂。我们在上一章中处理了一个图像数据集,而在本章中,我们处理了文本数据。
文本数据是分析起来最困难的数据集之一。文本数据有如此多的排列组合。清理文本数据是一项困难和复杂的工作。在本章中,我们讨论了几种清理文本数据的重要技术。我们还介绍了一些将文本数据表示为向量形式的方法。建议您练习这些方法,并通过应用每种技术来比较性能。我们还介绍了针对文本数据的 GenAI 概念。
这样,我们就结束了第七章。这也标志着第二部分的结束。在下一部分,复杂性会增加。我们将研究无监督学习算法的更深入概念。
7.19 实践下一步行动和推荐阅读
以下提供了一些下一步行动的建议和一些有用的阅读材料:
-
从以下链接获取数据集。您在这里会发现很多文本数据集。建议您实现聚类和降维解决方案:
- 50 个免费机器学习数据集:自然语言处理:
mng.bz/ZljO
- 50 个免费机器学习数据集:自然语言处理:
-
您在 Kaggle 上也会找到很多有用的数据集:
www.kaggle.com/datasets?search=text -
查看以下研究论文:
-
Mikolov, T., Chen, K., Corrado, G., and Dean, J. (2013). 在向量空间中高效估计词表示。
arxiv.org/pdf/1301.3781.pdf -
Pennington, J., Socher, R., and Manning, C. D. (2014). GloVe:全局词向量表示。
nlp.stanford.edu/pubs/glove.pdf -
Das, B., and Chakraborty, S. (2018). 使用 TF-IDF 和下一词否定改进文本情感分类模型。
arxiv.org/pdf/1806.06407.pdf
-
-
考虑以下广为人知的论文:
-
Blum, A., and Mitchell, T. (1998). 结合有标签和无标签数据与协同训练。
dl.acm.org/doi/10.1145/279943.279962 -
Knight, K. (2009). 哭泣的贝叶斯推理。
mng.bz/RVp0 -
Hofmann, T. (1999). 概率潜在语义索引。
dl.acm.org/doi/10.1145/312624.312649 -
Hindle, D., and Rooth, M. (1993). 结构歧义和词汇关系。
aclanthology.org/J93-1005.pdf -
Collins 和 Singer. (1999). 命名实体分类的无监督模型。
aclanthology.org/W99-0613.pdf
-
-
查看关于 TF-IDF 特征加权的全面研究:Das, M., Selvakumar, K., and Alphonse, J. P. A. (2023). TF-IDF 特征加权方法比较研究及其在非结构化数据集上的分析。
arxiv.org/abs/2308.04037
摘要
-
文本数据在博客、社交媒体、调查等领域的无处不在,以及其表达情感的能力,强调了这种形式数据的重要性。
-
文本分析的应用包括情感分析、文档分类、语言翻译、垃圾邮件过滤和命名实体识别。
-
文本数据面临的挑战包括处理垃圾字符、多种语言、演化的语言、同义词和基于上下文的意义。
-
数据预处理和清洗包括去除停用词和不需要的字符,并通过词干提取和词形还原来规范化文本。
-
在文本表示技术中,独热编码是基本的但不可扩展;高级技术考虑频率和上下文。
-
分词涉及将文本分解成标记,这是创建分析准备好的数据集的基础。
-
BOW 方法是一种快速、基于频率的方法,它忽略了单词顺序和上下文。
-
TF-IDF 根据重要性而非单纯频率来权衡单词,提供了比 BOW 更深入的分析。
-
语言模型和 n-gram 使用单词序列进行概率预测,包括单词、双词和三词等变体。
-
使用 Python 进行文本解析展示了如何使用 Python 库如
nltk进行文本数据的清理和预处理。 -
类似于 Word2Vec 和 GloVe 的技术,通过维护词语之间的上下文关系,以实现更好的语义理解。
-
Word2Vec 是基于预测的,而 GloVe 是基于频率的;两者都创建了紧凑且具有意义的词语表示。
-
大型语言模型(LLMs)已经彻底改变了文本数据集的整个格局。
第三部分 高级概念
欢迎来到本书的最后一部分。
您已经完成了本书的前两部分:您已经构建了程序,解决了案例研究,并导航了无监督学习解决方案的基础挑战。但机器学习,就像任何其他学科、艺术或运动一样,没有终点线。这是一个不断发展的领域,其中需要不断的升级,而要真正成为大师,您必须适应和改进,创新和学习,并推动您所知边界的扩展。
在本书的最后一部分,我们将深入探讨无监督学习的更细微的方面。我们将涵盖更多高级主题,这些主题将优秀的数据科学家与卓越的数据科学家区分开来:深度学习、自编码器、生成式 AI 以及适用于大型应用的通用模式。我们还将涵盖机器学习项目的端到端生命周期,包括部署和维护。
但不要被误导——这一部分并不是关于您可以剪切和粘贴的快速 Python 代码。这些高级技术是关于开发一个更复杂的系统,它可以用于文本、图像和视频等数据集。这是关于创建更定制化以及可扩展的特定解决方案。这些解决方案不仅今天有效,明天也将有效。
您准备好将您的技能提升到下一个层次了吗?让我们更深入地挖掘。
第八章:深度学习:基础概念
本章涵盖
-
深度学习的基本构建块
-
监督和无监督学习方法
-
卷积神经网络和循环神经网络
-
霍尔兹曼学习规则和深度信念网络
-
使用 TensorFlow 和 Keras 进行 Python 编码
-
深度学习库概述
简单的艺术是复杂性的谜题。——道格拉斯·霍顿
欢迎来到本书的第三部分。到目前为止,你已经涵盖了大量的概念、案例研究和 Python 代码。从本章开始,复杂度将会更高。
在本书的前两部分中,我们介绍了各种无监督学习算法,如聚类、降维等。我们讨论了简单和高级算法,并在本书的第二部分中介绍了在文本数据上的工作。从本书的第三部分开始,我们将开始深度学习的旅程。
深度学习和神经网络已经改变了世界和商业领域。你可能已经听说过深度学习和神经网络。它们的实现和复杂性导致了更好的癌症检测、自动驾驶汽车、改进的灾害管理系统、更好的污染控制系统、减少交易欺诈等。
在本书的第三部分,我们将探讨使用深度学习进行无监督学习。我们将研究深度学习是什么,神经网络的基础,以及神经网络中的层、激活函数、深度学习的过程和各个库。然后我们将转向自编码器和生成对抗网络(GANs)以及生成人工智能(GenAI)。这些主题确实复杂,有时数学性相当强。我们将使用不同种类的数据集来解决问题,但主要数据集将是非结构化的。像往常一样,Python 将用于生成解决方案。我们还分享了许多外部资源来补充这些概念。请注意,这些是高级主题,对这些主题的研究仍在进行中。
我们将本书的第三部分分为四章。本章涵盖了深度学习和神经网络的基础概念。接下来的两章专注于自编码器、GAN 和 GenAI。本书的最后一章讨论了这些模型的部署。
在本章中,我们讨论了神经网络和深度学习的概念。我们讨论了神经网络是什么,它的激活函数,不同的优化函数,神经网络训练过程等。本章涵盖的概念构成了神经网络和深度学习以及下一章后续学习的基础。因此,了解这些概念至关重要。在章节末尾提供了最佳外部资源,以更详细地学习这些概念。
欢迎来到第八章,祝您一切顺利!
8.1 技术工具包
我们将继续使用迄今为止所使用的相同版本的 Python 和 Jupyter Notebook。本章中使用的代码和数据集已在相同的 GitHub 位置进行检查。你将需要在本章中安装几个 Python 库:tensorflow和keras。
8.1.1 深度学习:它是什么?它能做什么?
在过去几年中,深度学习积累了大量的动力。神经网络正在推动机器学习解决方案的边界。深度学习仅仅是机器学习。深度学习基于神经网络。它利用了类似的概念——即使用历史数据,理解收集到的属性和智能,以寻找模式或预测未来,尽管深度学习比我们迄今为止所覆盖的算法更复杂。
回想第一章,我们介绍了结构化和非结构化数据集的概念。非结构化数据集包括文本、图像、音频、视频等。图 8.1 描述了文本、图像、音频和视频数据集的主要来源。

图 8.1 使用深度学习可以分析非结构化数据集,如文本、音频、图像和视频。此类数据集有多个来源。
虽然深度学习也可以用于结构化数据集,但它主要在非结构化数据集上产生了奇迹。其中一个主要原因是,经典机器学习算法有时对图像、文本、音频和视频等非结构化数据集的效果并不那么有效。深度学习在各个领域提供的突破性解决方案中的一些如下:
-
医疗领域和制药业—深度学习在识别骨骼和关节问题或确定动脉或静脉中是否有血栓等领域的应用。在制药领域,它加速了临床试验,并有助于更快地达到目标药物。
-
银行和金融行业—基于深度学习的算法用于检测交易中的潜在欺诈。通过基于图像识别的算法,我们还可以区分支票上的伪造签名。
-
汽车行业—你可能听说过自动驾驶(也称为自动驾驶)汽车。通过深度学习,算法可以检测交通信号、行人、道路上的其他车辆,以及它们相应的距离等。
-
零售—在零售行业,通过使用基于深度学习的算法,人类可以提高客户定位并开发高级和定制化的营销策略。通过深度学习改进的推荐模型可以为客户提供更好的产品。我们可以获得更好的投资回报并改善交叉销售和升级销售策略。
此外,深度学习还使得自动语音识别成为可能。通过复杂的神经网络,人类可以创建语音识别算法。这些解决方案正在被 Siri、Alexa、翻译器、百度等广泛应用。
图像识别也在进步。神经网络正在改进图像识别技术。这可以通过卷积神经网络来实现,它们正在改善计算机视觉。用例包括以下内容:
-
深度学习在区分癌细胞和良性细胞方面非常有效。可以通过使用癌细胞和良性细胞的图像来实现区分。
-
已经开发了一个使用神经网络的自动车牌识别系统。
-
可以使用深度学习开发目标检测方法和监控传感与跟踪系统。
-
在灾害管理系统,深度学习可以检测受影响区域的人类存在。想象一下,在救援行动中,如何通过更好的检测来拯救人类生命。
通用人工智能(GenAI)正在迅速改变世界。用例包括自动化内容创作,如撰写文章、论文和社交媒体帖子,以及生成图像和视频。它通过提供即时、个性化的聊天机器人来响应客户的查询,从而改善客户服务和客户体验。它可以应用于任何行业。在数据密集型行业,它通过总结复杂和冗长的文档,并从仪表板和报告中生成见解,产生涟漪效应。例如,这些报告可以是 Power BI/Tableau 仪表板、PowerPoint 或 pdf 文件。它还帮助软件开发者在代码生成和调试中,并提高了软件开发效率。用例很多,从零售;电信;医疗保健;研发;银行、金融和保险等,在提高销售额、降低成本、节省时间和提高准确性方面。
列出的用例当然不是详尽的。使用深度学习,我们可以改进用于衡量客户情绪、语言翻译、文本分类、命名实体识别等自然语言处理解决方案。在生物信息学、军事、移动广告、技术、供应链等领域,深度学习正在为未来铺平道路。
8.2 神经网络的基本构建块
人工神经网络(ANNs)据说受到人类大脑工作方式的启发。人类大脑是我们目前能接触到的最佳机器。当我们看到一张图片或一张面孔或听到一段曲调时,我们会将其与一个标签或名称关联起来。这使我们能够训练我们的大脑和感官,以便在再次看到/听到时识别图片或面孔或曲调。ANNs 通过学习或接受训练来学习执行类似任务。
练习 8.1
回答这些问题以检查你的理解:
-
深度学习的意义是什么?
-
神经网络不能用于无监督学习。对还是错?
-
探索在非常规商业领域深度学习的更多用例。
8.2.1 用于解决方案的神经网络
在深度学习中,监督学习和无监督学习的概念同样适用。我们将涵盖网络的两种训练类型:监督和无监督。这将为您提供一个完整的图景。同时,为了充分理解无监督深度学习,您应该对监督深度学习过程有清晰的认识。
让我们通过一个例子来理解深度学习过程。考虑这种情况:我们希望创建一个可以识别面部——一个可以通过为面部分配名字来区分面部并识别人的解决方案。为了训练模型,我们将使用一个包含人脸图像和相应名字的数据集。人工神经网络(ANN)将从对图像数据集或属性的无先验理解开始。在训练过程中,它将从训练数据中学习属性和识别特征。这些学到的属性随后被用来区分面部。在此阶段,我们只覆盖了这一过程的高级概述;我们将在后续章节中更详细地介绍这一过程。图 8.2 展示了神经网络的表示。

图 8.2 一个典型的神经网络,包含神经元和各个层次
神经网络中的过程相当复杂。我们首先将涵盖神经网络的所有构建块,如神经元、激活函数、权重、偏置项等,然后继续讨论神经网络中的过程。让我们从主角开始:一个神经元。
8.2.2 人工神经元和感知器
人类大脑包含数十亿个神经元。这些神经元是我们大脑中相互连接的细胞。它们接收信号,处理它们,并生成结果。人工神经元仅基于生物神经元,可以被认为是生物神经元的简化计算模型。
在 1943 年,研究人员沃伦·麦克洛克和沃尔特·皮茨提出了一个简化脑细胞的理念,称为麦克洛克-皮茨神经元。它可以被认为是一个具有二进制输出的简单逻辑门。
人工神经元的工作方法与生物神经元相似,尽管人工神经元远比生物神经元简单。感知器是生物神经元的数学模型。在实际的生物神经元中,树突从其他神经元的轴突接收电信号。在感知器中,这些电信号被表示为数值。
人工神经元从前面的神经元接收输入或可以接收输入数据。然后它处理这些输入信息并共享输出。输入可以是原始数据或来自前一个神经元的处理信息。然后神经元将其输入与其自身的内部状态相结合,分别加权,并通过非线性函数传递接收到的输出以生成输出。这些非线性函数也称为激活函数(我们将在后面介绍)。你可以将激活函数视为一个数学函数。一个神经元可以表示如图 8.3 所示。

图 8.3 一个神经元接收输入,使用数学函数处理它们,然后生成输出。
简而言之,一个神经元可以称为一个计算其输入数据集加权平均的数学函数;然后这个总和通过激活函数。神经元的输出然后可以成为下一个神经元的输入,该神经元将再次处理接收到的输入。让我们进一步探讨。
在感知器中,每个输入值都乘以一个称为 权重 的因子。生物神经元在输入信号的总强度超过一定阈值时才会触发。感知器遵循类似的格式。在感知器中,计算输入的加权总和以获得输入数据的总强度,然后对每个输出应用激活函数。然后每个输出可以馈送到下一个感知器层。
假设有一个感知器 X 的两个输入值,a 和 b,为了简化,它只有一个输出。设 a 和 b 的相应权重为 P 和 Q。因此,加权总和可以计算为 P * X + Q * b。只有当加权总和超过某个特定阈值时,感知器才会触发或产生非零输出。我们可以称这个阈值为 C。因此,我们可以这样说:
当 P * X + Q * y <= C 时,X 的输出将为 0。
如果 P * S + Q * y > C,则 X 的输出将为 1。
如果我们将这种理解进行推广,我们可以将其表示如下。将感知器表示为一个函数,它将输入 x 映射为以下函数:

其中 x 是输入值的向量,w 代表权重的向量,b 是偏置项。我们将在下面解释偏置和权重项。
回忆一下线性方程:y = mx + c,其中 m 是直线的斜率,c 是常数项。偏置和权重都可以使用相同的线性方程来定义。
权重的作用类似于线性方程中直线的斜率。它定义了 f(x) 的值随 x 值的单位变化而变化。
偏置的作用类似于线性函数中常数的作用。如果没有偏置,激活函数的输入是 x 乘以权重。
注意:权重和偏差项是网络中需要训练的参数。
函数的输出将取决于所使用的激活函数。在介绍完网络中的不同层之后,我们将在下一节中介绍各种类型的激活函数。
8.2.3 网络中的不同层
组织神经元的简单有效方法是以下所述。而不是允许任意神经元与任意其他神经元连接,神经元被组织成层。一个层的神经元所有输入都只来自前一层,所有输出都只流向下一层。没有其他连接,例如,同一层的神经元之间或属于不同层的神经元之间的连接(对于特殊情况有小的例外,这超出了本书的范围)。
我们知道信息通过神经网络流动。这些信息在网络中从一层传递到另一层进行处理。神经网络中有三层,如图 8.4 所示。

图 8.4 一个典型的神经网络,包含神经元和输入、隐藏和输出层
图 8.4 中所示的神经网络有三个输入单元,两个包含四个神经元的隐藏层和一个最终的输出层:
-
输入层—正如其名所示,它接收输入数据并将其与隐藏层共享。
-
隐藏层—这是网络的核心和灵魂。隐藏层的数量取决于具体问题;层数可以从几个到几百个不等。所有的处理、特征提取和属性学习都在这些层中完成。在隐藏层中,所有输入的原始数据都被分解为属性和特征。这种学习对后续的决策阶段是有用的。
-
输出层—这是网络中的决策层和最后一部分。它接受前面隐藏层的输出,然后做出预测。
例如,输入训练数据可能包含原始图像或处理后的图像。这些图像将被馈送到输入层。然后数据传递到隐藏层,所有计算都在这里完成。这些计算是由每一层的神经元完成的。输出是需要完成的任务——例如,识别一个对象或对图像进行分类等。
ANN 由各种连接组成。每个连接的目的是接收输入并为下一个神经元提供输出。这个输出将作为下一个神经元的输入。此外,如前所述,每个连接都被分配了一个权重,这代表了它各自的重要性。需要注意的是,一个神经元可以有多个输入和输出连接,这意味着它可以接收输入并产生多个输出。
练习 8.2
回答以下问题以检查你的理解:
-
输入数据被馈送到神经网络的隐藏层。对或错?
-
偏置项类似于线性方程的斜率。对或错?
-
寻找并探索训练过的最深的神经网络。
那么,层的作用是什么?层接收输入,处理它们,并将输出传递到下一层。技术上讲,层的实现必须由其权重参数化,这些权重也被称为层的参数。简单来说,为了确保神经网络“训练”到特定任务,网络中必须有所改变。结果证明,改变网络的架构(即神经元如何连接)只有微小的影响。另一方面,正如我们将在本章后面看到的那样,改变权重是“学习”过程的关键。
我们现在转向非常重要的激活函数主题。
8.2.4 激活函数
我们已经提到了激活函数。激活函数的主要作用是决定神经元/感知器是否应该激活。这些函数在网络训练的后期阶段扮演着核心角色。它们有时也被称为 传递函数。了解为什么我们需要非线性激活函数也很重要。如果我们只使用线性激活函数,输出也将是线性的。同时,线性函数的导数将是常数。因此,将不会有太多的学习。因此,我们更喜欢非线性激活函数。我们接下来研究最常见的激活函数。
Sigmoid 函数
Sigmoid 是一个有界的单调数学函数。当输入值增加时,它总是增加其输出值。其输出值总是在 -1 和 1 之间。
Sigmoid 函数是一个可微分的 S 形曲线函数,其第一导数函数呈钟形。它具有非负导数函数,并且对所有实数输入值都有定义。当神经元的输出值在 0 到 1 之间时,会使用 Sigmoid 函数。
从数学上讲,Sigmoid 函数可以用方程 8.1 表示:

(8.1)
图 8.5 展示了 Sigmoid 函数的图形。Sigmoid 函数在复杂学习系统中找到其应用。它通常用于二元分类和网络的最終输出层。

图 8.5 Sigmoid 函数。注意函数的形状和最小/最大值。
TANH 函数
在数学中,双曲正切(TANH)函数是一个可微分的双曲函数。它是一个平滑函数,其输入值在 -1 到 +1 的范围内。
TANH 函数可以表示为方程 8.2:

(8.2)
TANH 的图形表示如图 8.6 所示。它是 Sigmoid 函数的缩放版本,因此可以从 Sigmoid 函数导出 TANH 函数,反之亦然。

图 8.6 TANH 函数,它是 sigmoid 函数的缩放版本
TANH 函数通常用于隐藏层。它使均值更接近零,这使得网络中下一层的训练更容易。这也被称为数据居中。
矩形线性单元
矩形线性单元(ReLU)是一个定义参数正值的激活函数。方程 8.3 展示了 ReLU 函数。请注意,即使是负值,其值也为 0,并且从 0 开始值开始上升。
(8.3)
F(x) = max (0, x)
如果输入为正,则输出为x,否则为 0。
ReLU 是一个简单的函数,因此计算成本较低,速度更快。它是无界的,且不在零点居中。除了零点外,它可以在所有地方进行微分。由于 ReLU 函数较为简单,计算成本较低,因此广泛用于隐藏层以加速网络训练。图 8.7 是 ReLU 函数的图形表示。

图 8.7 ReLU 函数。它是神经网络隐藏层中首选的激活函数之一。ReLU 简单易用,训练成本低。
Softmax 函数
Softmax 函数用于神经网络的最外层,以生成网络的输出。它是一个对多类分类问题有用的激活函数,并迫使神经网络输出总和为 1。
例如,假设图像的不同类别为汽车、自行车或卡车。Softmax 函数将为每个类别生成三个概率。具有最高概率的类别将被预测为类别。
还有其他激活函数,如 ELU、PeLU 等,这些超出了本书的范围。我们在本章末尾提供了各种激活函数的总结。
接下来,我们将介绍超参数,这些是我们网络训练过程中的控制杠杆。
8.2.5 超参数
在训练网络的过程中,算法不断学习原始输入数据的属性。同时,网络不能自己学习所有内容;对于一些参数,必须提供初始设置。这些是确定神经网络结构和有助于训练网络的相应变量。
超参数的一些例子包括网络中的隐藏层数量、每层的神经元数量、层中使用的激活函数、权重初始化等。我们必须选择超参数的最佳值。为此,我们为超参数选择一些合理的值,训练网络,测量网络性能,调整超参数并重新训练网络,重新评估并再次调整,如此循环。
注意:超参数由我们控制,因为我们输入超参数以提高性能。
我们现在转向神经网络中下一个重要组件:优化函数。
8.2.6 优化函数
在深度学习中,优化器起着关键作用。它们通过调整模型参数(权重和偏差)来最小化损失函数,从而加速收敛并提高网络的总体性能。以下将讨论一些最常用的优化函数。
批量梯度下降、随机梯度下降和迷你批随机梯度下降
在任何基于预测的解决方案中,我们希望尽可能准确地预测;换句话说,我们希望尽可能减少误差。误差是实际值与预测值之间的差异。机器学习解决方案的目的是找到我们函数的最优值。我们希望减少误差或最大化准确性。梯度下降可以帮助实现这一目的。
批量梯度下降技术是一种用于寻找函数全局最小值的优化技术。我们迭代地朝着最陡下降方向前进,这由梯度的负值定义。
但是,批量梯度下降在处理非常大的数据集或具有非常高维度的数据集时可能运行缓慢。这是因为梯度下降算法的一次迭代会预测训练数据集中的每个实例。因此,如果我们有数千条记录,很明显这将花费很多时间。对于这种情况,我们有随机梯度下降(SGD)。
在 SGD 中,而不是在数据批次的末尾,系数会为每个训练实例更新,因此它花费的时间更少。
图 8.8 展示了梯度下降的工作方式。注意我们如何可以向下朝着全局最小值前进。

图 8.8 梯度下降的概念。它是最小化损失函数的机制。
迷你批梯度下降通过使用数据的小子集将批量梯度下降和 SGD 结合起来。它们被称为迷你批。以这种方式,它可以平衡速度和准确性。同时,它增加了一个超参数,我们必须仔细调整批大小。通常,它保持在 2 的幂次方(32、64、128、256 等)。
自适应优化算法
研究人员观察到,对于图像、文本、视频或音频分析等更复杂的任务,需要优化算法。因此,开发了如动量、Nesterov 加速梯度(NAG)、Adagrad 等自适应优化解决方案。我们简要总结这些解决方案:
- 动量—这个优化器将前一次梯度的部分加到当前梯度上。这种想法是相对于前一次更新,给予最近更新更多的权重。它加速了收敛并实现了更好的准确性

因此,权重通过θ = θ – V(t)更新。
通常,动量项(γ)的值设置为 0.9。有了动量,收敛会更快,但与此同时,我们必须为每个更新计算一个额外的变量。
- NAG——这是对动量的改进。在动量中,如果值变得太大,优化器可能会错过局部最小值。因此,开发了 NAG。这是一种前瞻性方法,其中权重被修改以确定未来的位置。
接下来,我们将讨论工业界最广泛使用的优化算法。
学习与学习率
对于一个网络,我们采取各种步骤来提高解决方案的性能:学习率是其中之一。学习率将定义模型为减少错误所采取的纠正步骤的大小。学习率定义了我们应该调整网络权重的值相对于损失梯度的量(关于这个过程后面会详细介绍)。如果我们有一个较高的学习率,准确性会较低。如果我们有一个非常低的学习率,训练时间会增加。
练习 8.3
回答这些问题以检查你的理解:
-
比较和对比 sigmoid 和 TANH 函数。
-
ReLU 通常用于网络的输出层。对还是错?
-
梯度下降是一种优化技术。对还是错?
我们已经研究了深度学习的主要概念。现在让我们研究神经网络是如何工作的。你将了解各个层如何相互作用以及信息是如何从一个层传递到另一个层的。
8.3 深度学习在监督模式下是如何工作的?
我们已经介绍了神经网络的主要组成部分。现在是时候让所有部件汇集在一起,协调整个学习过程了。神经网络的训练是一个相当复杂的过程,可以逐步检查。
你可能想知道“神经网络的学习”是什么意思。学习是一个寻找网络所有层中权重和偏置的最佳和最优化值的过程,以便我们可以达到最佳的准确性。由于深度神经网络在权重和偏置项上几乎有无限的可能性,我们必须找到所有参数的最优值。考虑到改变一个值会影响其他值,这似乎是一项艰巨的任务,而实际上,它是一个网络的各种参数不断变化的过程。
回想一下,在第一章我们介绍了监督学习的基础。在这里我们将刷新这一理解。原因是确保你能够完全欣赏神经网络训练的过程。
8.3.1 监督学习算法
监督学习算法有一个“指导”或“监督”来指导向业务目标——对未来进行预测。正式地说,监督模型是使用输入数据和期望的输出来预测未来的统计模型。输出是我们希望预测的值,被称为目标变量,用于进行预测的数据被称为训练数据。目标变量有时也被称为标签。数据中存在的各种属性或变量被称为自变量。每个历史数据点或训练示例都包含这些自变量和相应的目标变量。监督学习算法对未见的未来数据进行预测。解决方案的准确性取决于训练和从标记的历史数据中学到的模式。
备注:大多数深度学习解决方案都是基于监督学习的。然而,无监督深度学习正在迅速获得关注,因为未标记的数据集比标记的数据集要多得多。
监督学习问题用于需求预测、信用卡欺诈检测、客户流失预测、保费估算等。它们在零售、电信、银行和金融、航空、保险和其他领域被广泛使用。
我们现在已经刷新了监督学习的概念。我们现在继续到神经网络训练的第一步:前向传播。
8.3.2 步骤 1:前向传播
让我们开始一个神经网络中发生的过程(见图 8.9)。这是我们创建的基本网络骨架,用于解释这个过程。假设我们有一些输入数据点和输入数据层,它将消耗输入数据。信息从输入层流向数据转换层(隐藏层)。在隐藏层中,数据通过激活函数进行处理,并基于权重和偏置项。然后对数据集进行预测。这被称为前向传播,因为在整个过程中,输入变量是按照从输入层到输出层的顺序计算的。

图 8.9 神经网络训练过程的基本骨架。我们有输入层和数据转换层。
例如,假设我们希望创建一个可以识别人脸的解决方案。在这种情况下,我们将有训练数据,即从不同角度拍摄的人脸的不同图像,以及一个目标变量,即人的名字。
这个训练数据集可以输入到算法中。然后,算法将理解各种面部特征,换句话说,就是学习这些特征。基于所做的训练,算法可以对面部进行预测。如果预测是 Mr. X 的面部,预测将是一个概率分数。如果概率足够高,我们可以安全地说这个面部属于 Mr. X。
8.3.3 步骤 2:添加损失函数
输出在第一步生成。现在我们必须评估这个网络的准确性。我们希望我们的网络在识别面部时具有尽可能高的准确性。使用算法做出的预测,我们将控制和提高网络的准确性。
网络中的准确性测量可以通过损失函数实现,也称为目标函数。损失函数比较实际值和预测值。损失函数计算差异分数,因此能够衡量网络做得有多好以及误差率是多少。让我们通过添加一个损失函数和相应的损失分数来更新我们在第一步创建的图表,如图 8.10 所示。

图 8.10 已添加损失函数来衡量准确性。
8.3.4 步骤 3:计算误差
我们在网络的第一步生成了预测。在第二步,我们将输出与实际值进行比较,以获得预测误差。我们解决方案的目标是使这个误差最小化,这等同于最大化准确性。
为了不断降低误差,损失分数(预测值 - 实际值)随后被用作反馈来调整权重值。这个任务由反向传播算法完成。
8.4 反向传播
在最后一节的第三步中,我们提到我们使用优化器不断更新权重以减少误差。虽然学习率定义了减少误差的校正步骤的大小,但反向传播用于调整连接权重。这些权重根据误差反向更新。随后,重新计算误差,计算梯度下降,并相应地调整权重。因此,反向传播有时被称为深度学习中的核心算法。
反向传播最初在 20 世纪 70 年代被提出。然后,在 1986 年,David Rumelhart、Geoffrey Hinton 和 Ronald Williams 的论文受到了很多赞誉。如今,反向传播是深度学习解决方案的骨干。
图 8.11 显示了反向传播的过程,其中信息从输出层流向隐藏层。请注意,与正向传播相比,信息流是反向的,在正向传播中,信息是从左到右流动的。

图 8.11 反向传播作为过程:信息从最终层流向初始层
首先,我们以非常高的层次描述这个过程。记住,在步骤 1 中,在训练过程的开始,一些随机值被分配给了权重。使用这些随机值,生成了一个初始输出。由于这是第一次尝试,接收到的输出可能与真实值有很大不同,损失分数因此非常高。但这种情况将会改善。在训练神经网络时,权重(和偏差)会朝着正确的方向进行微调,随后损失分数会降低。我们重复这个训练循环多次,最终得到最小化损失函数的最优权重值。
备注:反向传播使我们能够在网络训练过程中迭代地减少误差。
下面的部分数学内容较多。如果你不热衷于理解这个过程背后的数学,你可以跳过这部分。
8.4.1 反向传播背后的数学
当我们训练一个神经网络时,我们计算一个损失函数。损失函数告诉我们预测值与实际值之间的差异。反向传播计算损失函数相对于每个权重的梯度。有了这些信息,每个权重都可以在迭代中单独更新,这会逐渐减少损失。
在反向传播中,梯度是向后计算的——也就是说,从网络的最后一层通过隐藏层到最第一层。所有层的梯度通过微积分链式法则组合起来,以得到任何特定层的梯度。
我们接下来更详细地介绍这个过程。首先,让我们定义一些数学符号:
-
h((i)^)——隐藏层 i 的输出
-
g((i)^)——隐藏层 i 的激活函数
-
w((i)^)——层 i 中的隐藏权重矩阵
-
b((i)^)——层 i 中的偏差
-
x——输入向量
-
N——网络中的总层数
-
W((i)^)[jk]——网络从层 (i–1) 中的节点 j 到层 i 中的节点 k 的权重
-
δ**A/δ**B——A 对 B 的偏导数
在网络的训练过程中,输入 x 被送入网络,并经过层生成输出 ŷ。期望的输出是 y。因此,比较 y 和 ŷ 的损失函数或损失函数是 C(y, ŷ)。此外,网络的任何隐藏层的输出也可以用方程 8.4 表示

(8.4)
其中 i(索引)可以是网络中的任何一层。
最终层的输出是
(8.5)
y(x) = W((N))(T) h((N)^–¹) + b((N)^)
在网络的训练过程中,我们调整网络的权重,以减少 C。因此,我们计算 C 对网络中每个权重的导数。以下是 C 对网络中每个权重的导数:

现在我们知道神经网络有很多层。反向传播算法从计算网络最后一层的导数开始,即第 N 层。然后这些导数被反向传递。因此,第 N 层的导数将传递到网络的第 (N – 1) 层,依此类推。
C 的导数的每个分量都是单独使用微积分链式法则计算的。根据链式法则,对于依赖于 b 的函数 c,其中 b 依赖于 a,c 对 a 的导数可以写成方程 8.6:

(8.6)
因此,在反向传播中,层 N 的导数用于层 (N – 1),以便它们被保存并再次用于 (N – 2) 层。我们从网络的最后一层开始,通过所有层到第一层,每次我们使用最后计算的导数来得到当前层的导数。因此,反向传播与我们会单独计算网络中每个权重的常规方法相比,效率极高。
一旦我们计算了梯度,我们就更新网络中的所有权重。目标是使成本函数最小化。我们已经在上一节研究了像梯度下降这样的方法。我们现在继续到神经网络训练过程的下一步。
8.4.2 步骤 4:优化
反向传播使我们能够优化我们的网络并达到最佳准确度(见图 8.12)。注意优化器,它提供常规和连续的反馈以找到最佳解决方案。

图 8.12 优化是使损失函数最小化的过程。
一旦我们找到了网络中权重和偏差的最佳值,我们就说我们的网络已经训练好了。现在我们可以用它来对未用于训练网络的未见数据集进行预测。
8.5 无监督方式下深度学习的工作原理
我们知道无监督学习解决方案适用于未标记的数据集;因此,在无监督设置中的深度学习,训练数据集是无标签的。
与我们有标签的监督数据集相比,无监督方法必须自我组织以获取密度、概率分布、偏好和分组。我们可以使用监督和无监督方法解决类似的问题。例如,可以使用监督深度学习方法来识别狗与猫,而无监督深度学习方法可能用于将狗和猫的图片聚类到不同的组中。在机器学习中,许多最初被认为是监督学习解决方案的解决方案,在一段时间内,采用了无监督学习方法来丰富数据,从而提高监督学习解决方案。
在无监督深度学习的学习阶段,预期网络将模仿数据,然后根据错误进行自我改进。在监督学习算法中,其他方法与反向传播算法起着相同的作用。这包括,但不仅限于,
-
Boltzmann 学习规则
-
对比散度
-
最大似然
-
Hopfield 学习规则
-
GAN
-
深度信念网络(DBN)
在本书中,我们将在单独的章节中深入探讨自编码器和 GAN。其余的方法在本章中介绍。
接下来,我们研究在监督学习设置中最广泛使用的两种神经网络类型:卷积神经网络(CNN)和循环神经网络(RNN)。
练习 8.4
回答这些问题以检查你的理解:
-
以简单形式写出反向传播技术的主要步骤。
-
在无监督学习中,反向传播算法是首选。对或错?
-
深度学习的目标是最大化损失函数。对或错?
8.6 卷积神经网络
CNN 是一类主要用于图像和视频处理任务的深度学习模型。由于它们能够自动从原始图像中检测和学习模式,因此已成为计算机视觉领域的一种强大工具。因此,它们被用于多个领域和功能中的多个用例。我们只提供简要概述,因为关于不同类型的 CNN 解决方案可以有一整本书。
8.6.1 CNN 的关键概念
以下为 CNN 的关键概念:
-
输入层——CNN 的输入通常是一个表示图像的张量。正如我们所知,图像由像素组成,每个像素由 RGB 通道组成。图像由一个 3D 矩阵表示,即宽度 × 高度通道。
-
卷积层——这是 CNN 的核心构建层。它对输入数据应用滤波器,扫描图像以检测线条、曲线、纹理、边缘等模式。滤波器大小通常较小,通常是 3 × 3 或 5 × 5。当核在输入上滑动时,它执行逐元素乘法和求和,创建一个特征图。可以应用多个滤波器来学习不同的特征,生成多个特征图。整个过程如图 8.13 所示。

图 8.13 CNN 过程。原始数据是 6 × 6,应用的是 3 × 3 的滤波器,结果输出为 4 × 4。
-
ReLU 激活函数——这是用来增加非线性。它帮助网络理解和建模数据中存在的更复杂和困难的模式。
-
池化层——这是用来减少图像的空间维度同时保留最重要的细节。最常见的一种池化称为最大池化。它从一个输入区域中取最大值。池化层的主要功能是减少计算负载,并通过提供一种形式上的平移不变性来减少过拟合。
-
输出—在我们创建了几个卷积层和池化层之后,我们得到输出。输出通常被展平成一个一维向量,然后输出被传递到全连接层。全连接层的主要任务是根据前一层提取的特征对图像进行高级分类。
-
输出层—如果解决方案是用于数据点的分类,输出层将包含一个如 softmax 之类的函数。softmax 函数为不同的类别提供相应的概率。例如,如果你试图预测一个给定的图片是猫还是狗,softmax 函数将给出图片是狗或猫的概率。
在 CNN 中,相同的过滤器被应用于图像的不同区域。因此,与传统的全连接网络相比,参数数量减少了。卷积层中的每个神经元仅连接到输入的一个小区域,因此网络的复杂性也降低了。网络还自动训练和学习检测低级模式。低级模式的一个例子是边缘。网络随后进步到学习更复杂的模式,如深层中的形状。
8.6.2 CNN 的使用
网络调用是现代竞争解决方案的基本和基础。它们被大量用于图像分类、图像处理、语音识别、开发计算机棋类游戏以及各种其他视频处理解决方案。许多解决方案都是使用 CNN 开发的——例如,自动检测车辆牌照、从扫描中检测癌细胞、从 X 光片中检测骨折、面部识别解决方案、自动输入手写识别解决方案,以及许多对我们生活产生巨大影响的解决方案。
可用的 CNN 架构有很多,如 Inception、ResNet、LeNet、VGG-16 等,这些架构对于创建计算机视觉解决方案非常有用。我们现在转向第二种常见的神经网络类型:RNN。
8.7 循环神经网络
RNN 是一类相当流行的网络,旨在识别数据序列中的模式——例如,时间服务数据或视频、自然语言或任何其他具有这种信息序列的数据。在这里,RNN 非常有用。RNN 最显著的特征是它们能够维持对先前输入的记忆,它们通过时间依赖性和数据集中的顺序来捕捉这种记忆。这增强了它们在顺序数据集中识别模式的能力,因此 RNN 被发现是多个领域的解决方案。
8.7.1 RNN 的关键概念
RNN 特别设计用于顺序数据集,在这里输入显示的顺序起着关键作用。因此,RNN 是处理顺序数据的首选解决方案。
与常规神经网络(也称为前馈神经网络)不同,RNNs 具有循环连接。这意味着一个时间步的输出被反馈作为下一个时间步的输入。这种信息在序列中是持续的。同时,相同的权重在不同的时间步中使用。这使得它们在参数数量方面非常高效,因为相同的网络可以应用于输入序列的每个时间步。
RNNs 以以下方式工作:
-
输入数据是顺序处理的。在每个时间步 t,网络接收一个输入 x[t],然后与隐藏状态 h[t][–1] 结合。这个隐藏状态是前一个时间步的输出,并作为记忆,携带信息从一个时间步传递到下一个时间步。
-
隐藏状态 h[t] 然后使用非线性函数更新:

- 每个时间步的最终输出可以计算并用于每个单独的时间步或仅在最终时间步。
图 8.14 说明了 RNN 过程。

图 8.14 RNN 过程。RNNs 具有内部记忆,这使得它们能够使用先前输入的信息来影响当前输入和输出。
RNN 最基本的形式是简单的循环网络,但它难以处理长期依赖,因为梯度可能消失或爆炸,使得网络难以记住序列中远处的信息;因此,它不能用于像聊天机器人这样的解决方案。长短期记忆(LSTM)在这里更有用。LSTM 是一种特殊的网络,旨在减轻梯度消失问题,并且比普通的 RNN 更好地处理长期依赖。它们通过引入门控机制实现这一壮举。有三种类型的门:输入门、遗忘门和输出门。这些门控制网络中信息的流动,并允许网络在更长的时间内维持重要信息。门控循环单元是另一种类型的 RNN,但 LSTM 和门控循环单元超出了本书的范围。
RNNs 在处理序列方面非常强大,它们建模时间依赖性的能力使它们在自然语言处理和时间序列分析领域变得不可或缺。它们的使用为许多创新解决方案开辟了道路——例如,预测句子中的下一个单词;将文本从一种语言翻译成另一种语言;处理视频帧序列以理解随时间的行为;建模如音频信号这样的时间依赖性,这些信号可以用来识别随时间变化的语音模式;等等。RNNs 是 GenAI 解决方案背后的动力引擎。
8.8 鲍尔兹曼学习规则
玻尔兹曼学习规则是一种在神经网络中使用的无监督学习规则。它基于物理系统的统计力学原理。在玻尔兹曼机的背景下很少使用。它通过最小化系统的能量来调整神经网络的权重,从而确保网络达到稳定状态。
8.8.1 霍尔兹曼学习规则的概念
以下为玻尔兹曼学习规则的关键概念:
-
它是一种概率性循环神经网络(RNN),其中神经元在一个全连接图中连接。
-
玻尔兹曼机中的神经元是随机单元,它们根据概率分布进行放电。因此,我们可以使用玻尔兹曼学习规则进行降维、模式识别、特征提取和优化任务。
-
玻尔兹曼机有一个能量函数 E(v,h),其中 v 是可见输入单元,而 h 是隐藏单元。能量函数决定了网络给定状态的代价。在网络训练过程中,我们旨在调整权重,使系统的能量最小化。
-
网络使用玻尔兹曼分布来模拟特定状态 (v,h) 的概率。它取决于状态的能量,由方程 8.7 给出:

(8.7)
在这里,Z 是配分函数,它确保概率之和等于 1。
- 该规则旨在调整权重,在网络训练过程中持续降低系统的能量,并且这一过程是随时间发生的。权重通过从能量函数相对于权重的梯度推导出的规则进行更新。权重更新规则在方程 8.8 中给出:

(8.8)
在这里,h 是学习率,而 (v[i]h[j])[data] 是可见单元 v[i] 和隐藏单元 h[j] 之间的校正,它是从数据分布中计算得出的。它表示它们在隐藏单元中一起活跃的频率。(v[i]h[j])[model] 是从模型分布中计算得出的校正,它表示可见单元 v[i] 和隐藏单元 h[j] 在网络生成的状态中一起活跃的频率。
在模型的训练过程中,遵循一个学习规则,即使数据分布与模型分布相匹配。因此,它降低了系统的能量,从而提高了整体性能。
8.8.2 关键点
我们应该牢记一些关键点。基于能量的模型,如玻尔兹曼机,使用玻尔兹曼学习规则通过调整网络的权重来最小化能量函数:
-
网络努力模拟其输入的概率分布。这里的核心目标是将高能量与不太可能的配置相关联。同样,低能量与更可能的配置相关联。
-
玻尔兹曼学习是一种无监督和概率方法。它基于对比模型分布和数据分布的概念。
-
基本形式的规则计算成本较高;因此,为了提高训练速度,有时我们会利用对比散度等方法。我们将在下一节介绍对比散度。
-
玻尔兹曼学习规则主要用于无监督学习任务,如降维、特征提取和生成建模。
-
模型训练有时比预期慢。
总结来说,玻尔兹曼学习规则是一种通过调整权重以最小化能量函数来训练神经网络的概率方法,并为生成模型如玻尔兹曼机提供了基础。然而,由于计算挑战,通常使用对比散度等近似方法使其适用于实际应用。
8.9 深度信念网络
DBN 是一种由多层随机二进制潜在变量(隐藏单元)组成的 GAN,其中每一层都是受限玻尔兹曼机(RBM)或其变体。DBN 在 2000 年代中期由杰弗里·辛顿(2024 年获得诺贝尔物理学奖,与约翰·霍普菲尔德共享)及其合作者推广,用于以无监督方式预训练深度网络。
8.9.1 DBN 的关键点
DBN 的关键点如下:
-
RBM
-
DBN 由多个 RBMs 层组成。一个 RBM 包含一个可见层和一个隐藏层。可见层代表观察到的数据,而隐藏层则捕捉隐藏特征。
-
每个 DBN 都是独立训练的,目的是模拟数据的潜在结构。
-
-
DBN 训练的目的是优化网络生成模型下数据的对数似然。对于每一层,使用对比散度算法来近似对数似然相对于权重的梯度。这允许网络为每一层学习一组良好的权重。
-
对比散度算法是一种用于估计模型对数似然梯度的随机近似方法。算法从可见层的一个样本开始,然后执行吉布斯采样以迭代地更新隐藏层和可见层。对比散度确保网络能够有效地学习输入数据分布。
-
基于层的预训练:
-
DBN 通常以分层的方式进行训练,其中每个层都作为 RBM 进行预训练。第一个 RBM 的目标是从数据中学习捕捉低级特征。
-
基于这一知识,每个后续的 RBM 随后从先前层学习到的表示中学习越来越复杂、抽象的特征。以此类推,循环继续。
-
这个阶段涉及使用对比散度分别训练每个 RBM。
-
此过程调整权重以捕捉输入数据中的相关模式和特征,无需标记数据。
-
由于每一层都在不断抽象和复杂化的层次上学习特征,这使得整体解决方案对于复杂任务(如图像或语音识别)足够好。
-
-
监督微调:
-
预训练完成后,整个网络进行微调。这通过反向传播或带有优化网络目标的有标签数据集以监督方式进行。
-
监督系统调整网络权重以最小化预测误差,如分类或回归任务中所做的那样。
-
无监督预训练阶段有助于以这种方式初始化权重,使得网络在监督微调期间不太可能过拟合,因为它从对数据的更好理解开始。
-
它们在计算上成本高昂且耗时,尤其是在处理大型数据集或深层架构时。
-
使用 RBMs 进行预训练是有用的,但有时微调整个 DBN 可能很困难,尤其是当我们处理一个非常深的神经网络时。这可能需要细致的超参数训练和大量的标记数据集。
-
与其他深度学习架构类似,DBNs 也容易受到梯度消失问题的影响,即当梯度在反向传播通过多层时减小。这进一步复杂化了整个训练过程。
-
DBNs 通常用于无监督学习、降维和特征学习,但它们也可以微调以用于监督任务,如分类。DBNs 通过学习对噪声和其他失真不敏感的声音特征来提高语音识别系统的性能。作为生成模型,DBNs 可以用来创建类似于训练数据的新数据实例。例如,DBNs 已被用于生成艺术,其中创建了类似于一组输入图像的新图像。
DBNs 是深度学习技术发展中的一个重要里程碑。它们结合了生成模型(如 RBMs)的优点和深度学习原理,创造了一种学习数据复杂表示的强大方法。虽然已经出现了新的架构并获得了突出地位,但 DBNs 仍然是现代 AI 的关键历史和理论组成部分,影响了众多先进模型的发展。通过利用无监督学习,DBNs 在降维、生成建模和分类等任务上可以非常有效。然而,与训练复杂性和微调相关的挑战仍然是广泛采用的重要障碍。
8.10 流行的深度学习库
在过去的几章中,我们使用了大量库和包来实现解决方案。在行业中,有相当多的库可用于深度学习。这些包加速了解决方案的构建,并减少了工作量,因为大部分重活都是由这些库完成的。
最受欢迎的深度学习库有
- TensorFlow (TF)**—由 Google 开发,这可能是最受欢迎和最广泛使用的深度学习框架之一。它于 2015 年推出,自那时起已被全球众多企业和品牌使用。
Python 主要用于 TF,但 C++、Java、C#、JavaScript 和 Julia 也可以使用。您必须在您的系统上安装 TF 库并导入该库。
备注:访问 www.tensorflow.org/install 并按照说明安装 TF。
TF 是最受欢迎的库之一,可以在移动设备如 iOS 和 Android 上运行。
- Keras—Keras 是一个成熟的 API 驱动解决方案,非常易于使用。它是初学者的最佳选择之一,也是快速以简单和快捷的方式原型设计简单概念的佼佼者。Keras 最初于 2015 年发布,是最推荐的库之一。
备注:访问 keras.io 并按照说明安装 Keras。Tf.keras 可以用作 API。
使用 Python 生成器进行序列化/反序列化 API、回调和数据流非常成熟。Keras 中的大规模模型简化为单行函数,这使得它成为一个不太可配置的环境,因此非常方便且易于使用。
-
PyTorch—Facebook 的 PyTorch 于 2016 年发布,是另一个流行的框架。PyTorch 使用动态更新的图,并允许数据并行和分布式学习模型。PyTorch 中有如 pdb 或 PyCharm 这样的调试器。对于小型项目和原型设计,PyTorch 可以是一个不错的选择。
-
Sonnet—DeepMind 的 Sonnet 是在 TF 的基础上开发的。Sonnet 专为复杂的神经网络应用和架构设计。它通过创建与神经网络特定部分相对应的初级 Python 对象来实现。然后,这些 Python 对象独立连接到计算 TF 图。由于这种分离(创建 Python 对象并将它们关联到图中),设计得到了简化。
备注:拥有高级面向对象库非常有帮助,因为当我们开发机器学习解决方案时,允许进行抽象。
- MXNet—Apache 的 MXNet 是一个高度可扩展且易于使用的深度学习工具,具有详细的文档。MXNet 支持大量语言,如 C++、Python、R、Julia、JavaScript、Scala、Go 和 Perl。
还有其他框架,如 Swift、Gluon、Chainer、DL4J 等;然而,我们在这里只讨论流行的框架。我们现在检查 TF 和 Keras 中的简短代码,只是为了测试您是否已正确安装了这些库。您可以在www.tensorflow.org了解更多关于 TF 的信息,在keras.io了解更多关于 Keras 的信息。
8.10.1 Python 代码用于 Keras 和 TF
我们在 TF 中实现了一段非常简单的代码。我们只是导入 TF 库并打印“hello”。我们还检查了 TF 的版本:
import tensorflow as tf
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))
print("TensorFlow version:", tf.__version__)
如果这段代码能为您运行并打印出 TF 的版本,这意味着您已正确安装了tensorflow:
from tensorflow import keras
from keras import models
如果这段代码能为您运行并打印出 Keras 的版本,这意味着您已正确安装了keras。
8.11 结论性思考
深度学习正在改变我们生活的世界。它使我们能够训练和创建以前只是想法的真正复杂的解决方案。深度学习的影响可以在多个领域和行业中观察到。或许没有任何行业能逃脱深度学习奇迹的影响。
深度学习是研究和开发中最受欢迎的领域之一。每年,都有许多关于深度学习的期刊和论文发表。世界各地的著名机构(如牛津、斯坦福等)的研究人员都在努力寻找改进的神经网络架构。同时,在知名组织(如 Google、Facebook 等)的专业人士和工程师也在努力创造复杂的架构以提高性能。
深度学习使我们的系统和机器能够解决通常被认为仅属于人类领域的难题。我们改进了制药行业的临床试验流程、欺诈检测软件、自动语音检测系统以及各种图像识别解决方案;并创建了更稳健的自然语言处理解决方案、针对营销解决方案,这些解决方案可以改善客户关系管理和推荐系统,更好的安全流程等等。这个列表相当长,而且每天都在增长。
同时,仍然存在一些挑战。对深度学习的期望持续增加。深度学习并不是解决所有问题的银弹或魔杖。它确实是一种更复杂的解决方案,但绝对不是解决所有商业问题的 100%解决方案。我们需要提供给算法的数据集并不总是可用。缺乏高质量的、能代表商业问题的数据集。通常,像 Google、Meta 或 Amazon 这样的大组织能够负担得起收集如此庞大的数据集。但很多时候,我们在数据中确实发现了很多质量问题。拥有训练这些复杂算法的处理能力也是一个挑战。然而,随着云计算的出现,这个问题在某种程度上已经得到了解决。
在本章中,我们探讨了神经网络和深度学习的基础。我们涵盖了关于神经元、激活函数、网络的不同层以及损失函数的细节。我们还详细介绍了反向传播算法——用于训练监督深度学习解决方案的核心算法。然后我们简要介绍了无监督深度学习算法。我们将在后面的章节中更详细地介绍这些无监督深度学习解决方案。图 8.15 显示了主要的激活函数。

图 8.15 一瞥主要激活函数(来源:towardsdatascience)
8.12 实践下一步行动和推荐阅读
以下提供了一些下一步行动的建议和一些有用的阅读材料:
-
François Chollet 所著的《Python 深度学习》一书是阐明深度学习概念的最好资源之一。它涵盖了深度学习和神经网络的全部概念,并由 Keras 的创造者撰写。
-
阅读以下研究论文:
-
Hinton, G., Vinyals, O., and Dean, J. (2015). 神经网络中的知识蒸馏。
arxiv.org/pdf/1503.02531.pdf -
Srivastava, R., Greff, K., and Schmidhuber, J. (2015). 训练非常深的网络。
arxiv.org/pdf/1507.06228 -
Mikolov, T., Sutskever, I., Chen, K., Corrado, G., and Dean, J. (2013). 词和短语的分布式表示及其组合性。
arxiv.org/abs/1310.4546 -
Goodfellow, I. J., Pouget-Abadie, J., Mirza, M., et al. (2014). 生成对抗网络。
arxiv.org/abs/1406.2661 -
He, K., Zhang, X., Ren, S., and Sun, J. (2015). 用于图像识别的深度残差学习。
arxiv.org/abs/1512.03385
-
摘要
-
深度学习是基于神经网络的先进机器学习形式,它特别适用于文本、图像、音频和视频等非结构化数据。
-
深度学习在各个领域都有应用,例如
-
医疗领域和制药业——用于诊断医疗状况和加速药物开发
-
银行和金融——检测欺诈并区分伪造签名
-
汽车行业——通过识别交通元素来推动自动驾驶
-
语音和图像识别——使 Siri 和基于图像的医疗诊断和安全系统等技术成为可能
-
-
神经网络的关键概念包括
-
人工神经元(感知器)——生物神经元的简化模型。权重和偏差在感知器的功能中起着至关重要的作用。
-
层——网络由输入层、隐藏层和输出层组成。隐藏层提取并学习对决策至关重要的特征。
-
激活函数——对神经网络性能至关重要,包括 sigmoid、TANH、LeLU 和 softmax。
-
-
训练神经网络涉及正向传播、计算损失和利用反向传播进行权重调整以最大化预测准确性的过程。
-
虽然无监督学习依赖于未标记的数据,但像霍尔兹曼学习和 DBNs 这样的技术对于在这样环境中改善数据组织至关重要。
-
CNNs(卷积神经网络)主要用于图像和视频处理。CNNs 因其架构而擅长识别模式,具有卷积层和池化层等用于特征提取的层。
-
RNNs(循环神经网络)适用于序列数据。RNNs 在输入之间保持信息,并通过 LSTMs(长短期记忆网络)增强以解决长期依赖问题。它们在自然语言处理和时间序列分析中至关重要。
-
霍尔兹曼学习规则是一种无监督、概率性的方法,用于神经网络中通过最小化能量函数来调整权重,通常有助于降维和特征提取等任务,但计算挑战需要如对比散度这样的近似方法。
-
DBNs 是包含 RBMs(限制性玻尔兹曼机)层的 GANs(生成对抗网络),利用无监督预训练来学习复杂的数据表示,并通过监督微调进行分类等任务,但它们面临着包括计算成本和潜在过拟合在内的挑战。
-
DBNs 使用分层预训练来捕捉抽象特征,使它们适用于图像或语音识别等复杂应用;然而,梯度消失问题和复杂的微调过程可能会阻碍性能。
-
尽管新的深度学习架构越来越受欢迎,但 DBNs(深度信念网络)仍然是人工智能发展的重要组成部分,在包括降维、生成建模和分类等任务模型的发展中扮演着关键角色,尽管训练复杂性仍然是一个障碍。
第九章:自动编码器
本章涵盖
-
自动编码器的介绍
-
自动编码器的训练
-
自动编码器的类型
-
使用 TensorFlow 和 Keras 的 Python 代码
从复杂的混乱中,简单的真理浮现出来。——温斯顿·丘吉尔
在上一章中,我们探讨了深度学习的概念。在这一章中,我们将从无监督深度学习开始。自动编码器是第一个主题。我们将首先介绍自动编码器的基础知识,它们是什么,以及我们如何训练它们。然后我们将探讨不同类型的自动编码器,并随后提供一段 Python 代码以展示其实现。欢迎来到第九章,祝大家一切顺利!
9.1 技术工具箱
我们将继续使用迄今为止所使用的相同版本的 Python 和 Jupyter Notebook。本章中使用的代码和数据集已在 GitHub 位置进行检查。您需要在本章中安装几个 Python 库:tensorflow和keras。
9.2 特征学习
预测建模是一个非常有趣的话题。在各个领域和商业功能中,预测建模被用于各种目的,如预测下一年企业的销售额、预期的降雨量、即将到来的信用卡交易是否为欺诈、客户是否会进行购买等。用例很多,上述所有用例都属于监督学习算法。
注意:我们使用的数据集具有变量或属性。它们也被称为特征或属性。
当我们希望创建这些预测模型时,我们也对理解对预测有用的变量感兴趣。让我们考虑一个案例,其中一家银行想要预测一笔即将到来的交易是否为欺诈。在这种情况下,银行将希望知道哪些因素对于识别一笔即将到来的交易是否为欺诈是重要的。可能考虑的因素包括交易金额、交易时间、交易的来源/来源等。对于做出预测的重要变量被称为显著变量。
要创建基于机器学习的预测模型,需要使用特征工程。特征工程,也称为特征提取,是从原始数据中提取特征以改善模型整体质量并提高准确性的过程,与仅向机器学习模型提供原始数据的模型相比。
特征工程可以通过领域理解、各种手动方法以及一些自动化方法来完成。其中一种方法被称为特征学习。特征学习是一组帮助解决方案自动发现特征检测所需表示的技术。借助特征学习,不需要手动特征工程。特征学习的效果对于使用图像、文本、音频和视频的数据集来说更为相关。
特征学习可以是监督的也可以是无监督的。对于监督特征学习,神经网络是最好的例子。对于无监督特征学习,我们有矩阵分解、聚类算法和自编码器等例子。我们已经涵盖了聚类和矩阵分解。在本章中,我们从自编码器的介绍开始。
9.3 自编码器的介绍
当我们开始处理任何数据科学问题时,数据扮演着最显著的角色。一个包含大量噪声的数据集是数据科学和机器学习中最具挑战性的问题之一。现在有相当多的解决方案,其中之一就是自编码器。
简而言之,自编码器是一种人工神经网络,用于学习数据编码。自编码器通常用于降维方法。它们也可以用作生成模型,可以创建类似于旧数据的合成数据。例如,如果我们没有足够的数据来训练机器学习,我们可以使用生成的合成数据来训练模型。
自编码器是前馈神经网络,它们将输入压缩成一个低维代码,然后尝试从这个表示中重建输出。自编码器的目标是学习高维数据集的低维表示(有时也称为编码)。回想一下前几章中的主成分分析(PCA)。自编码器可以被视为 PCA 的推广。PCA 是一种线性方法,而自编码器可以学习非线性关系。因此,自编码器对于需要捕获输入数据中最显著属性的低维化解决方案是必需的。
9.4 自编码器的组成部分
自编码器的架构非常简单易懂。自编码器由三部分组成:编码器、瓶颈或代码和解码器,如图 9.1 所示。简单来说,编码器压缩输入数据,瓶颈或代码包含这些压缩信息,解码器解压缩知识,从而将数据重建回其原始形式。一旦完成解压缩并将数据重建为编码形式,就可以比较输入和输出。

图 9.1 自编码器的结构,包括编码器、瓶颈和解码器
让我们更详细地研究这些组件:
-
编码器—输入数据通过编码器。编码器不过是一个全连接的人工神经网络。它将输入数据压缩成一个编码表示,在这个过程中生成的输出被减小了尺寸。编码器将输入数据压缩成一个称为瓶颈的压缩模块。
-
窄带—窄带可以被认为是编码器的“大脑”。它包含压缩的信息表示,并且窄带的任务是只允许最重要的信息通过。
-
解码器—从窄带接收到的信息由解码器解压缩。它将数据重新创建成其原始或编码形式。一旦解码器的任务完成,实际值与解码器创建的解压缩值进行比较。
关于自动编码器有几个重要点需要考虑:
-
与原始输入相比,在自动编码器中,当进行解压缩时会有信息损失。因此,当压缩数据被解压缩时,与原始数据相比会有损失。
-
自动编码器是针对数据集特定的。这意味着在花卉图像上训练的算法不会在交通信号图像上工作,反之亦然。这是因为自动编码器学习的特征将仅限于花卉。因此,我们可以说自动编码器只能压缩与训练数据相似的数据。
-
训练专门实例的算法以在特定类型的输入上表现良好相对容易。我们只需要代表性的训练数据集来训练自动编码器。
9.5 自动编码器的训练
需要注意的是,如果数据中的变量之间没有相关性,那么压缩和随后解压缩输入数据将非常困难。为了创建一个有意义的解决方案,输入数据中的变量之间应该存在某种程度的关系或相关性。要创建自动编码器,我们需要编码方法、解码方法以及一个损失函数来比较实际值和解压缩值。
流程如下:
-
输入数据通过编码器模块。
-
编码器将模型的输入压缩成一个紧凑的窄带。
-
窄带限制了信息的流动,只允许重要信息通过;因此,窄带有时被称为知识表示。
-
解码器解压缩信息,并将数据重新创建成其原始或编码形式。这种编码器-解码器架构在从输入数据中获取最重要的属性方面非常高效。
解决方案的目标是生成与输入相同的输出。通常,解码器架构是编码器架构的镜像。这并非强制性的,但通常是遵循的。我们确保输入和输出的维度相同。
注意:如果您不知道超参数的含义,请参阅附录。
我们需要定义四个超参数来训练自动编码器:
-
代码大小—这可能是最重要的超参数。它表示中间层的节点数。这决定了数据的压缩程度,也可以作为正则化项。代码大小的值越小,数据就越压缩。
-
参数—这表示自动编码器的深度。具有更多深度的模型显然更复杂,并且处理时间会更长。
-
每层的节点数—这是每层使用的权重。随着输入在层间变得越来越小,它通常在后续的每一层中都会减少。在解码器中又重新增加。
-
使用的损失函数—如果输入值在[0,1]范围内,则首选二元交叉熵;否则,使用均方误差。
我们已经介绍了在训练自动编码器时使用的超参数。训练过程与反向传播类似,我们已经讨论过了。
9.6 自动编码器的应用
自动编码器能够解决无监督学习中的一些固有问题的解决方案。自动编码器的主要应用包括
-
降维—有时自动编码器可以学习比 PCA 和其他技术更复杂的数据投影。
-
异常检测—错误或重建误差(实际数据与重建数据之间的误差)可以用来检测异常。
-
数据压缩—通过训练算法来击败像 JPEG 这样的基本解决方案是困难的。此外,由于自动编码器是数据特定的,它们只能使用它们被训练过的数据集类型。如果我们希望提高包括更多数据类型的容量并使其更通用,那么所需的训练数据量将非常高,显然,所需的时间也会很高。
-
其他应用—这些包括药物发现、机器翻译、图像去噪等。
在现实世界中,自动编码器的实际应用仍然不多。这有多种原因,如数据集不可用、基础设施、各种系统的准备就绪等。
9.7 自动编码器的类型
自动编码器主要有五种类型。接下来将简要介绍不同类型的编码器。我们尽量使这一部分数学简单,并跳过了背后的数学,因为它相当复杂,难以理解。对于好奇的读者,第 9.10 节中列出的论文可以解释数学原理:
- 欠完备自编码器——欠完备自编码器是自编码器最简单的形式。它只是接受一个输入数据集,然后从压缩的瓶颈区域重新构建相同的数据集。通过根据重建误差惩罚神经网络,模型将学习数据的最显著属性。通过学习最重要的属性,模型将能够从压缩状态重建原始数据。正如我们所知,当压缩数据重建时会有损失;这种损失被称为重建损失。
欠完备自编码器本质上是无监督的,因为它们没有目标标签进行训练。这类自编码器用于降维。回想在第二章中我们讨论了降维(PCA),在第六章中我们讨论了高级降维算法(t 分布随机邻域嵌入和多维尺度)。见图 9.2。

图 9.2 随着维度的增加,性能开始提高,但过了一段时间后开始下降。当涉及到创建良好的数据科学解决方案时,维度诅咒是一个真正的问题。
使用欠完备自编码器作为瓶颈,可以降低维度,因为瓶颈是输入数据的压缩形式。这些压缩数据可以在网络的辅助下重新解压缩。回想在第三章中我们解释了 PCA 提供了输入变量的线性组合。有关更多细节和 PCA 的复习,请参阅第三章。我们知道 PCA 试图通过一个低维超平面来描述原始数据集;欠完备自编码器也可以学习非线性关系。这种差异在图 9.3 中显示。

图 9.3 PCA 本质上是线性的,而自编码器是非线性的。这是两种算法之间的核心区别。
有趣的是,如果从欠完备自编码器中移除所有非线性激活函数,只使用线性层,那么自编码器就相当于 PCA。为了使自编码器泛化并不过度记忆训练数据,欠完备自编码器通过瓶颈的大小进行调节和微调。这允许解决方案不记忆训练数据,并且泛化得非常好。
备注:如果一个机器学习模型在训练数据上表现非常好,但在未见过的测试数据上表现不佳,那么它被称为过拟合。
-
稀疏自编码器——稀疏自编码器与欠完备自编码器类似,但它们使用不同的方法来处理过拟合问题。从概念上讲,稀疏自编码器在每个隐藏层改变节点的数量,并保持其灵活性。由于不可能有一个能够灵活调整神经元数量的神经网络,因此为它定制了损失函数。在损失函数中,引入了一个捕捉激活神经元数量的项。惩罚项与激活神经元数量成正比。激活神经元数量越多,惩罚越高。这个惩罚被称为稀疏函数。使用这个惩罚,可以减少激活神经元的数量;因此,惩罚较低,网络能够处理过拟合问题。
-
收缩自编码器——收缩自编码器与其他自编码器的工作原理类似。它们认为非常相似的输入应该被编码成相同的。因此,它们应该有相同的潜在空间表示。这意味着输入数据和潜在空间之间不应该有太大的差异。
-
去噪自编码器——去噪意味着去除噪声,这正是去噪自编码器的精确任务。它们不接收图像作为输入;相反,它们接收如图 9.4 所示的图像的噪声版本作为输入。

图 9.4 原始图像、带噪声的输出和自编码器的输出
自编码器去噪的过程在图 9.5 中展示。原始图像通过添加噪声而改变。这个带噪声的图像被输入到编码器-解码器架构中,接收到的输出与原始图像进行比较。自编码器学习图像的表示,用于去除噪声;这是通过将输入图像映射到一个低维流形上实现的。

图 9.5 自编码器中的去噪过程。它从原始图像开始;添加噪声,结果产生一个带噪声的图像,然后将其输入到自编码器中。
我们可以使用去噪自编码器进行非线性降维。
- 变分自编码器——标准自编码器模型使用瓶颈以压缩形式表示输入。变分是概率生成模型(通常是高斯分布)在潜在变量上,它们只需要神经网络作为其整体结构的一部分。它们使用期望最大化元算法进行训练。数学细节超出了本书的范围。
9.8 自编码器的 Python 实现
让我们创建两个自编码器的版本。代码是从 Keras 网站(blog.keras.io/building-autoencoders-in-keras.html)的官方来源中取出的,并为了我们的使用进行了修改。步骤如下:
- 导入必要的库:
import keras
from keras import layers
-
- 创建我们的网络架构:
# This is the size of our encoded representations
encoding_dim = 32 # 32 floats -> compression of factor 24.5, assuming
the input is 784 floats
# This is our input image
input_img = keras.Input(shape=(784,))
# "encoded" is the encoded representation of the input
encoded = layers.Dense(encoding_dim, activation='relu')(input_img)
# "decoded" is the lossy reconstruction of the input
decoded = layers.Dense(784, activation='sigmoid')(encoded)
# This model maps an input to its reconstruction
autoencoder = keras.Model(input_img, decoded)
-
- 向模型添加更多细节:
# This model maps an input to its encoded representation
encoder = keras.Model(input_img, encoded)
# This is our encoded (32-dimensional) input
encoded_input = keras.Input(shape=(encoding_dim,))
# Retrieve the last layer of the autoencoder model
decoder_layer = autoencoder.layers[-1]
# Create the decoder model
decoder = keras.Model(encoded_input, decoder_layer(encoded_input))
autoencoder.compile(optimizer='adam', loss='binary_crossentropy')
-
- 加载数据集:
(x_train, _), (x_test, _) = mnist.load_data()
-
- 创建训练和测试数据集:
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
x_train = x_train.reshape((len(x_train), np.prod(x_train.shape[1:])))
x_test = x_test.reshape((len(x_test), np.prod(x_test.shape[1:])))
print(x_train.shape)
print(x_test.shape)
-
- 拟合模型(见图 9.6):
autoencoder.fit(x_train, x_train,
epochs=5,
batch_size=128,
shuffle=True,
validation_data=(x_test, x_test))

图 9.6 模型拟合
-
- 在测试数据集上测试它:
# Encode and decode some digits
# Note that we take them from the *test* set
encoded_imgs = encoder.predict(x_test)
decoded_imgs = decoder.predict(encoded_imgs)
-
- 绘制结果。您可以看到原始图像和最终输出(见图 9.7):
# Use Matplotlib (don't ask)
import matplotlib.pyplot as plt
n = 10 # How many digits we will display
plt.figure(figsize=(20, 4))
for i in range(n):
# Display original
ax = plt.subplot(2, n, i + 1)
plt.imshow(x_test[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
# Display reconstruction
ax = plt.subplot(2, n, i + 1 + n)
plt.imshow(decoded_imgs[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.show()

图 9.7 原始图像(底部)和最终结果(顶部)
9.9 总结性思考
深度学习是一个强大的工具。有了合理的企业问题和高质量的数据集,我们可以创造许多创新解决方案。自动编码器只是这类解决方案中的一种。
在本章中,我们从特征工程开始,这使我们能够从数据集中提取最重要的特征。然后我们转向自动编码器。自动编码器是一种仅用于学习未标记数据集有效编码的神经网络。自动编码器可以应用于许多商业问题,如人脸识别、异常检测、图像识别、药物发现、机器翻译等。
9.10 实践步骤和推荐阅读
以下提供了一些下一步行动的建议和一些有用的阅读材料:
-
阅读博客
mng.bz/qxaw。 -
研究以下论文:
-
Hinton, G. E., Krizhevsky, A., and Wang, S. D. (2011). Transforming Auto-encoders.
mng.bz/7p99 -
Bank, D., Koenigstein, N., and Giryes, R. (2020). Autoencoders.
arxiv.org/abs/2003.05991 -
Michelucci, U. (2020). An Introduction to Autoencoders.
arxiv.org/abs/2201.03898
-
-
请参阅 TensorFlow 官方页面上的良好代码和数据集。
mng.bz/mGQr。
总结
-
预测建模在各个领域使用,通过监督学习算法进行未来预测。
-
预测建模的关键方面包括识别对准确预测有重要影响的变量或特征。
-
特征工程通过从原始数据中提取有用特征来提高模型精度。
-
特征学习自动化特征检测,适用于图像、文本和音频等数据集。
-
自动编码器是一种用于数据编码、降维和生成合成数据的神经网络。
-
自动编码器的架构包括编码器、瓶颈和解码器组件,用于数据压缩和重建。
-
自动编码器面临信息损失、数据集特定,适用于精确应用。
-
训练自动编码器需要编码、解码以及定义超参数,如代码大小和损失函数。
-
主要应用包括降维、异常检测和数据压缩等。
-
自动编码器的类型包括欠完备、稀疏、收缩、去噪和变分。
-
稀疏和收缩自动编码器通过不同的方法来解决过拟合问题。
-
Python 实现基本自动编码器架构涉及使用 Keras 库进行编码和解码数据。
第十章:生成对抗网络、生成式人工智能和 ChatGPT
本章涵盖
-
生成对抗网络
-
生成式人工智能
-
ChatGPT 和 BERT
现实是由心灵创造的。我们可以通过改变我们的心灵来改变我们的现实。——柏拉图
在上一章中,我们讨论了自编码器。我们现在转向最近一段时间内的一些最革命性的技术进步。你可能已经在新闻中听说过生成对抗网络(GANs)、生成式人工智能(GenAI)和 ChatGPT。这些无疑是行业的变革者。在这本书的最后一章中,我们将讨论这些创新。欢迎来到第十章,祝大家一切顺利!
10.1 人工智能:一场变革
人工智能是计算机科学中的一个变革性领域。它的目标是创造能够模仿人类智能的机器和解决方案。自从人工智能诞生以来,它确实已经取得了长足的进步,并以多种方式改变着我们的生活。
人工智能的概念可以追溯到 20 世纪中叶。1956 年,约翰·麦卡锡、马文·明斯基、纳撒尼尔·罗切斯特和克劳德·香农组织了达特茅斯研讨会,这次研讨会通常被认为是人工智能的诞生,因为在这次研讨会上,“人工智能”一词被创造出来。研究人员想要看看机器如何模仿人类智能并被用于日常生活。在最初的几年里,研究人员专注于符号人工智能。这种方法涉及使用符号和逻辑来表示知识和解决问题。在 20 世纪 70 年代和 80 年代,由于资金减少,人工智能的进步放缓。20 世纪末和 21 世纪初,随着机器学习技术如神经网络和深度学习的发展,人工智能再次兴起。新的使人工智能系统开始通过学习历史数据来进行预测和决策。随着云计算、更好的服务和更多处理能力的可用性,算法的训练变得更快、更简单、更便宜,算法从基于规则的转向基于数据的算法。随着 TensorFlow 和 Keras 等库的推出,创建深度学习网络变得任何有互联网连接的人都能做到的事情。
人工智能对日常生活产生了重大影响。例如,我们有 Siri 和 Alexa 这样的虚拟助手,在流媒体平台和电子商务网站上提供推荐。人工智能已经应用于金融、零售、航空、生命科学、制造业以及许多其他行业和商业功能,提高了效率和决策过程,增加了客户满意度,并降低了成本。人工智能与机器人的集成导致了自动驾驶汽车、无人机、自动化和数字孪生的出现。我们现在拥有非常智能的机器人系统,它们能够执行非常复杂的工作。到目前为止,人工智能对人类来说是一大福音,并且随着负责任的使用,它可以提供巨大的利益。
人工智能持续增长,这种增长带来了一组独特的机遇和挑战。AI 系统中存在偏见和伦理问题;许多活动家也提出了关于自动化可能导致的就业流失的担忧。政策制定者、政府以及研究人员正在不懈努力,确保 AI 技术被负责任地使用,并开发出来服务于人类,而不是与他们作对。
10.2 GenAI 及其重要性
GenAI 是人工智能更广泛领域中的一个变革性领域。它是我们在机器学习领域取得的令人瞩目的成就之一,导致了计算机处理能力和新内容生成的改进。您无疑已经看到了生成预训练变换器 3(GPT-3)及其高级版本的应用实例,这些版本正在多个行业和功能中应用。
传统人工智能(AI)和通用人工智能(GenAI)之间的显著区别在于,GenAI 解决方案可以生成数据,而传统 AI 系统执行预测、推荐或分类等任务。GenAI 解决方案通常基于生成对抗网络(GANs)——如变换器架构这样的自回归模型,这赋予了 GPT 等解决方案能力。
GenAI 对多个商业领域和功能都很有用。以下是一些例子:
-
基于自然语言处理(NLP)的解决方案从 GenAI 模型中获得了巨大益处。GenAI 使得智能聊天机器人、虚拟助手、文本摘要、查询引擎和定制内容的发展成为可能。这些解决方案在品牌和营销、客户服务、研发、优化和学术等领域都非常有用。GenAI 在自然语言处理(NLP)中的应用非常广泛,并且每天都在扩展和改进。
-
生命科学和医疗保健行业通过 GenAI 工具经历了革命。借助这些工具,新药发现、医疗报告生成、医疗场景模拟、医疗专业人员培训、医学期刊搜索以及整个医学研究职业的整体水平都得到了显著提升。例如,AI 可以识别出可以重新用于新治疗用途的现有药物。通过分析大量数据集,AI 可以发现药物和疾病之间的联系,这些联系以前并未被认识到。AI 驱动的虚拟筛选可以预测小分子与靶蛋白的结合亲和力。这通过减少需要在实验室中合成和测试的化合物数量,节省了时间和资源。GenAI 在医疗保健行业中的应用对人类极为有益。
-
机器学习和数据分析完全依赖于可用数据的数量和质量。很多时候,高质量的 dataset 很稀缺。GenAI 在创建合成数据以增强和扩展较小 dataset 方面发挥着重要作用。这个过程提高了训练 dataset 的整体质量,从而提高了模型的性能。使用合成数据,模型变得更加具体,过拟合的风险降低。
-
使用通用人工智能(GenAI),客户体验正在改善。通过 GenAI 算法,企业能够创建定制的推荐、体验、内容和解决方案。有了这种增强的体验,整体用户参与度得到提升,客户满意度更高,从而提高了客户终身价值。当然,GenAI 正在改变客户的个性化体验。它可以扩展到任何业务领域,如零售、金融、电信或航空。
-
GenAI 创建艺术、音乐、文本、视频和图像等内容的能里非常实用。它通过自动化他们工作的多个步骤,帮助创意领域的专业人士。现在,作者可以使用 GenAI 来获得创新想法,图像设计师可以使用它来创建设计,音乐导演可以使用它来创作音乐。
-
在研究和科学领域,GenAI 正在帮助科学家和研究人员进行实验模拟。它可以模拟多个场景,模拟非常复杂的物理系统,并预测实验的结果。当然,它减少了整个实验的时间和成本。研究人员和科学家现在可以更快地得出结果。
这些只是 GenAI 重要性的几个例子;可能性是巨大的。GenAI 无疑是一个变革者,具有未来应用。
接下来,我们将比较判别模型和生成模型。我们在整本书中讨论了判别模型。现在,我们将阐明判别模型和 GenAI 模型之间的区别。
10.3 判别模型和 GenAI
在机器学习和人工智能领域,判别模型和生成模型是两种基本方法。两者都可以用于分类、估计和生成目的。它们有相似之处,也有不同之处。
判别模型创建了区分不同数据集类别或类别的边界。这类模型通常有助于进行预测和解决数据分类问题。判别模型的一些属性如下:
-
判别模型通常用于监督学习解决方案。正如你所知,监督学习用于标记数据集,其中我们有一个目标变量来训练算法。使用监督学习解决方案对分类变量进行预测,我们可以预测事件发生或不会发生的概率——例如,客户是否会流失,即将到来的信用卡交易是否为欺诈,等等。同样,使用监督学习解决方案对数值变量进行预测,我们可以预测一个数值变量的估计值——例如,下个月商店的销售额或下周呼叫中心的预期通话量。判别模型根据输入值预测输出的条件概率,因此它们是任何分类任务的绝佳解决方案。
-
判别模型最常见的例子包括逻辑回归、决策树、随机森林、支持向量机以及用于图像和文本分类的基于深度学习的网络。我们有众多可用的判别模型。
对于生成模型,我们的目的是捕捉它们所训练的数据的底层分布。它们寻求学习数据是如何生成的,以及如何利用这种智能生成与训练数据集相似的新数据点。生成模型的一些显著特性如下:
-
生成模型在整个数据空间上提供概率分布;它们可以生成与训练数据相似的新数据点。这使得它们在合成文本和图像生成等解决方案中非常有帮助。
-
生成模型对于无监督学习解决方案,如降维和聚类,非常有帮助。这是因为它们不依赖于显式标签的存在,因此可以揭示数据集中存在的潜在模式。一些例子包括隐马尔可夫模型、GANs 和变分自编码器。
如果我们将判别模型和生成模型进行比较,会发现以下情况:
-
生成模型通常需要更大的数据集进行训练,因为它们必须学习整个数据分布。然而,判别模型也可以使用较小的标记数据集。
-
生成模型通常比判别模型复杂得多。生成模型使用数据的底层结构,需要更多计算时间和资源来实现解决方案。
-
生成模型已被用于内容生成和密度估计;另一方面,判别模型是为更广泛的分类和预测而设计的。因此,在当前情况下,你会发现判别模型比生成模型更受欢迎。
-
判别模型更高效,计算成本和内存需求更低。因此,在当前场景中,它们在工业界更受欢迎。
生成模型和判别模型都有自己的优缺点。选择取决于手头的业务问题和可用的数据集。虽然判别模型在分类和预测方面更加有效和高效,但生成模型在数据生成和探索方面更加灵活和有用。作为用户,我们需要深入了解这些模型及其特性。只有这样,我们才能为手头的业务问题选择正确的解决方案。
10.4 生成对抗网络
GANs 代表了一种革命性的深度学习架构,为生成建模领域做出了重大贡献。GANs 由 Ian Goodfellow 及其同事在 2014 年引入,并已成为包括图像生成、风格迁移、数据增强等在内的各种应用的基础。
在其核心,GANs 由两个神经网络组成:生成器和判别器。生成器负责创建合成数据,如图像或文本,而判别器的角色是区分真实数据和生成器产生的数据。在我们的深入解释中,我们剖析了 GAN 架构,提供了对其关键组件、训练过程和实际应用的详细了解。
10.4.1 生成器网络
生成器网络是 GANs 背后的创造力源泉。其主要作用是产生合成数据,尽可能地模仿真实数据。生成器网络以随机噪声为输入,通常是从高斯或均匀分布等简单分布中抽取的。然后,这个噪声向量通过一系列层,通常是在图像生成的情况下由卷积或转置卷积层组成,或在文本生成的情况下由循环层组成。生成器的目的是将输入噪声转换为与真实数据分布相似的数据。见图 10.1。
让我们更详细地看看生成器网络是如何运作的:
-
输入噪声—生成器通过一个输入噪声向量启动这个过程。这个噪声向量作为生成数据的种子。噪声向量通常是从简单的概率分布中抽取的,例如高斯分布。
-
转换—输入噪声通过生成器内部的多个层。每一层都以使输入越来越接近真实数据分布的方式转换输入。这些转换是通过训练过程学习的。
-
生成—随着输入噪声在网络中传播,它逐渐呈现出目标数据的特征。这种转换过程一直持续到生成器产生的数据被呈现为最终输出。
-
损失函数—使用损失函数来衡量生成数据的品质,该函数量化了生成数据与真实数据之间的相似程度。生成器的目标是最小化这种损失,从而创建尽可能逼真的数据。

图 10.1 GAN 的表示
生成器的最终目标是产生几乎无法与真实数据区分的数据。然而,达到这种真实水平是一个复杂任务,并且它严重依赖于与判别网络的对抗关系。
我们现在转向生成网络的对应物,即判别网络。
10.4.2 判别网络
判别网络作为生成器的对应物,在 GAN 中起着至关重要的作用。其目的是区分真实数据和假数据。判别器是一个二元分类器,经过训练,将其高概率(接近 1)分配给真实数据,将低概率(接近 0)分配给假数据。
让我们更详细地探讨判别网络:
-
训练数据—通常,判别网络会接触到包含真实数据的数据库。这个数据库主要用于清洗判别器,使其能够区分真实数据和合成数据。
-
判别—当判别器经过训练后,我们可以用它来评估数据集。它接受来自训练数据集的真实数据和生成器产生的合成数据作为输入。
-
损失计算—现在判别器计算一个损失。这个损失或错误基于判别器区分真实数据和合成数据的能力。如果判别器正确地将真实数据识别为真实数据,将合成数据识别为合成数据,这意味着性能良好,因此损失最小化。然而,如果判别器犯了错误,损失会增加。
-
参数更新—判别器的参数被调整以最小化计算出的损失。这些更新有助于判别器提高其准确性。
在理解了 GAN 背后的结构之后,我们现在转向整个过程的核心:网络的训练。
10.4.3 对抗训练
对抗训练过程是 GAN 架构的核心。整体训练过程如下:
-
初始时,生成器和判别器都从随机权重开始。
-
生成器从随机噪声中产生合成数据,并将其与真实数据集一起呈现给判别器。
-
判别器分析、评估并对每个输入分配概率。这是尝试正确区分真实数据和合成数据的一种尝试。
-
生成器根据判别器的反馈进行更新。目标是生成判别器无法区分的数据。
-
判别器被更新以提高其区分真实和合成数据的能力。
-
此过程持续迭代。生成器和判别器不断改进其能力。生成器越来越擅长生成逼真的数据集,而判别器在识别过程中变得更加熟练。这种迭代且有趣的竞争推动了整体解决方案,使生成的数据几乎无法与真实数据集区分开来。
整个训练过程依赖于两个关键损失函数:
-
生成器损失—此函数旨在最小化判别器区分真实和合成数据集的能力。常用的损失函数示例是二元交叉熵损失,它允许生成器产生判别器更有可能将其分类为真实的数据。
-
判别器损失—判别器损失函数的目的是最大化其区分真实数据集与合成或伪造数据集的能力。它旨在最小化二元交叉熵损失,在评估真实数据时最大化,而在处理生成或合成数据集时最大化。
GANs 在这个训练过程中非常出色。我们现在转向 GAN 的一些变体和一些应用。
10.4.4 GANs 的变体和应用
GANS 对于特定的挑战和问题很有用。这也导致了以下一些突出的变体:
-
条件 GAN—这些模型将额外的信息(例如,类别标签)作为输入,以控制生成数据的属性。
-
深度卷积 GANs—针对图像生成优化,深度卷积 GANs 使用卷积层生成高质量图像。
-
CycleGANs—用于风格转换和图像到图像的翻译,这些模型学习将图像从一个域映射到另一个域。
-
BigGAN 和 StyleGAN—这些模型生成高分辨率图像,并提供了对图像风格和属性的先进控制。
接下来,我们简要介绍目前可用的最新技术解决方案——例如,双向编码器表示(BERT)、GPT-3 以及其他。
10.4.5 BERT、GPT-3 和其他
BERT、GPT-3 和其他模型是高级自然语言处理技术的突出例子,这些技术已经彻底改变了人工智能领域。这些模型在理解和生成类似人类的文本以及实现语言理解、翻译、文本生成等多种应用方面取得了重大进展。
由 Google 在 2018 年开发的 BERT 是一个基于 transformer 的模型,旨在理解句子中单词的上下文。与之前按顺序读取文本的模型不同,BERT 可以通过双向处理文本来考虑每个单词的上下文。BERT 在大量文本数据上进行了预训练,并且可以针对特定的 NLP 任务进行微调,如情感分析、问答和命名实体识别。BERT 的预训练显著提高了许多 NLP 任务的性能,使其成为该领域的基石模型。
由 OpenAI 开发的 GPT-3 是其中最著名的语言模型之一。它在 2020 年发布,是 GPT 系列的第三个迭代版本。GPT-3 是一个能够生成类似人类文本的生成模型。它在庞大的文本语料库上进行了预训练,并在给出提示时能够生成连贯且与上下文相关的文本。它还可以执行广泛的 NLP 任务,包括文本补全、语言翻译、文本摘要,甚至可以进行基于文本的对话。
文本到文本迁移转换器(T5)是另一个基于 transformer 的模型,由 Google 在 2019 年开发。它独特之处在于将所有 NLP 任务框架为文本到文本问题。T5 在多种文本数据上进行了预训练,并且可以针对各种 NLP 任务进行微调,包括文本分类、翻译和摘要,使其成为 NLP 任务的通用模型。
XLNet 作为 BERT 的后继者被开发出来,引入了一种基于排列的培训方法。它在训练过程中考虑了句子中单词的所有可能的排列,使其能够更有效地建模复杂的语言依赖关系。XLNet 在各种 NLP 基准测试和任务上表现出色。
RoBERTa 是另一个基于 BERT 架构构建的模型,由 Facebook AI 在 2019 年开发。它优化了 BERT 的预训练方法,并在多个 NLP 基准测试中取得了最先进的成果。
Vaswani 等人发表的论文“Attention Is All You Need”中最初引入的 transformer 架构(arxiv.org/abs/1706.03762)构成了许多这些模型的基础。它依赖于自注意力机制来处理和生成文本数据。
10.5 ChatGPT 及其细节
ChatGPT 是一个高级 AI 模型,旨在与用户进行自然和动态的对话,是 AI 领域的一个关键发展。由 OpenAI 开发的 ChatGPT 建立在 GPT-3.5 架构之上,该架构以其理解和生成类似人类文本的能力而闻名。
10.5.1 ChatGPT 的关键特性
ChatGPT 的关键特性如下:
-
自然语言理解—ChatGPT 以与人类交流相似的方式理解和生成文本,使其与用户的互动更加直观和吸引人。
-
情境意识——该模型可以在整个对话中保持情境,记住之前的消息并提供连贯的回复,从而实现更有意义和流畅的对话。
-
多语言能力——ChatGPT 可以以多种语言进行交流,扩大其效用和可访问性,使其对全球受众更加有用。
-
定制化——它还可以进行微调以执行特定任务,例如撰写电子邮件、回答常见问题或提供辅导,使其适用于各种应用。
10.5.2 ChatGPT 的应用
ChatGPT 的应用包括以下方面:
-
客户支持**——ChatGPT 可用于提供全天候的客户支持,回答查询,解决问题,并确保用户满意度高。因此,它可以作为一个聊天机器人,并可作为虚拟助手,帮助用户安排日程、设置提醒和信息检索。
-
研究和开发——研究人员可以使用 ChatGPT 筛选大量数据并生成报告或摘要,节省时间和精力。
-
内容生成——它可以协助内容创作者生成博客文章、营销材料或创意写作提示。
-
教育——它还可以提供个性化辅导并回答学生的问题,增强学习体验。
虽然 ChatGPT 有许多应用,但也存在伦理考量。ChatGPT 的使用必须优先考虑用户隐私,并采取措施保护在对话中共享的敏感信息。监控和监督 ChatGPT 的交互可能是必要的,以确保其负责任的使用。开发者必须努力减少偏见,并减少在回复中生成虚假或有害信息的能力。开发者、组织和用户应共同对 ChatGPT 的行为和输出负责。
接下来,我们将讨论 GenAI 在现实世界商业应用中的整合。这将让您了解如何在务实的企业世界中应用这些技术。
10.6 GenAI 的整合
将通用人工智能(GenAI)整合到现实世界的商业活动中涉及一个需要仔细规划和考虑的系统过程。以下是如何有效整合 GenAI 的逐步指南:
-
设定目标和业务问题定义。 首先,我们应该确定 GenAI 在我们业务优先事项中的具体目标和用例。这需要确定它能在哪里提供最大价值——无论是客户支持和解决方案、数据分析/可视化、个性化、内容生成还是其他方面。
-
评估可用数据和基础设施。 接下来,我们应该检查可用数据并评估其质量和数量。高质量的数据对于训练和维护 GenAI 模型至关重要。我们还必须确保我们的 IT 基础设施能够支持 AI 系统的整合。
-
选择模型。 我们接下来选择开发定制的 GenAI 模型或使用现有的预训练模型。如果我们决定构建定制模型,我们将不得不考虑与具有该领域专业知识的 AI 开发团队或外部供应商合作。这是一个关键步骤,因为我们应选择具有开发模型所需技能的团队。最好听取该领域专家的建议。
-
执行数据收集、预处理和准备。 数据是这里的主体,下一步是收集和预处理训练 GenAI 模型所需的数据。这可能涉及清理、标记和结构化数据以供训练。数据预处理是提高模型准确性的关键步骤。数据应代表手头的业务问题。
-
训练模型。 我们接下来使用预处理的数据来训练 GenAI 模型。这个过程可能需要强大的硬件和深度学习专业知识。可能需要对模型进行一些迭代以符合我们特定的业务需求。这一步骤可能需要大量时间,具体取决于数据的数量、基础设施的质量和解决方案的复杂性。
-
测试、验证和调整。 我们随后测试 GenAI 系统以确保其按预期工作。这包括在真实世界数据和用例上验证其性能。需要考虑的一些变量包括准确性、响应时间和用户体验。
-
执行用户教育和培训。 GenAI 将被员工、客户或其他利益相关者使用;因此,我们必须提供关于如何有效使用 AI 系统的培训和教学材料。
-
考虑合规性和隐私。 制定关于 GenAI 负责任使用的指南和政策至关重要,以解决隐私、偏见和符合相关法规等问题。我们必须确保 AI 系统符合我们组织的道德标准。
-
执行维护。 随着我们业务的增长,对 GenAI 的需求可能会增加。我们必须定期用新数据更新模型,以保持其准确性和有效性。我们应该始终计划可扩展性和持续维护。实施监控系统以跟踪 GenAI 的性能和用户反馈非常重要。这些信息可以帮助我们进行持续改进并解决出现的问题。
-
适应、创新和改进。 我们应通过确定预期收益是否得到实现并相应调整,来持续评估 GenAI 集成的投资回报。重要的是我们要了解 AI 技术的进步,并不断适应和创新我们的 GenAI 集成,以保持竞争力和效率。
将 GenAI 整合到您的业务中是一个复杂的过程,涉及多个步骤和持续的努力。成功的整合需要明确的策略,对负责任 AI 使用的承诺,并关注为我们的组织和其利益相关者创造价值。
10.7 总结性思考
GenAI 是人工智能研究中的一个令人兴奋和雄心勃勃的前沿领域。虽然它代表了一个长期目标,但追求创建高度适应性和多功能的 AI 系统有可能彻底改变我们与技术互动的方式,并解决广泛的问题。然而,这也伴随着我们在 AI 发展中需要仔细考虑和监管的伦理和社会责任。
ChatGPT 是一个具有革命性潜力的 AI 模型,有可能彻底改变人机交互。随着其不断进化,ChatGPT 的负责任使用和发展将至关重要,以便充分利用其全部潜力,同时解决伦理和实际关注的问题。无论是在客户服务、内容生成、教育还是研究方面,ChatGPT 都准备改变我们与 AI 互动的方式,使我们更接近直观和无缝的机器通信。
10.8 实践下一步行动和推荐阅读
以下提供下一步行动的建议和一些有用的阅读材料:
-
查看 GANs 的第一篇论文:Goodfellow, I.,Pouget-Abadie, J.,Mirza, M. 等. (2014). 生成对抗网络。
arxiv.org/abs/1406.2661 -
学习以下论文:
-
Kingma, D. P. 和 Welling, M. (2013). 自动编码变分贝叶斯。
arxiv.org/abs/1312.6114 -
Arici, T. 和 Celikyilmax, A. (2016). 关联对抗网络。
arxiv.org/abs/1611.06953
-
-
如果你想研究贝叶斯 GAN,请参阅 Saatchi, Y. 和 Wilson, A. J. (2014). 贝叶斯 GAN。
arxiv.org/abs/1705.09558。
摘要
-
AI 寻求模仿人类智能,自 1956 年达特茅斯研讨会提出“人工智能”一词以来,已经发生了显著的变化。
-
最初专注于符号 AI,该领域在 20 世纪 70 年代和 80 年代期间放缓,但在 20 世纪末随着机器学习的发展而复兴。
-
云计算和 TensorFlow 等库的兴起将 AI 从基于规则的算法转变为基于数据的算法,增强了其可访问性。
-
AI 影响包括金融、航空和制造在内的各个行业,提高效率、决策和成本降低。
-
GenAI 通过生成数据、支撑 GPT 等技术,并在 NLP 和医疗保健等领域受益,从而区别于其他技术。
-
GenAI 通过扩展数据集质量和降低过拟合风险来创建合成数据,从而增强机器学习模型。
-
判别模型是数据分类器,而生成模型学习数据分布以创建新的、相似的数据点。
-
GANs(生成对抗网络),包含生成器和判别器网络,通过对抗训练逐步提升数据的真实感。
-
GAN 的变体,如 CycleGAN 和 StyleGAN,解决了风格迁移和高分辨率图像生成等任务。
-
类似 BERT 和 GPT-3 这样的自然语言模型具有先进的 NLP 能力,为翻译和对话式 AI 提供了解决方案。
-
基于 GPT-3.5 的 ChatGPT 在生成类似人类的对话文本方面表现出色,被应用于客户支持和内容生成。
-
将生成式 AI 融入商业需要周密的规划、数据准备、模型训练和持续的评估,以确保成功。
第十一章:端到端模型部署
本章涵盖了
-
端到端模型部署过程
-
部署后模型的维护
-
每个步骤的 Python 代码
旅程本身就是目的地。——丹·艾尔登
学习之路永无止境。学习某样东西需要很大的勇气、耐心和努力。我们必须坚持不懈、富有创造力,并始终寻找学习和提升的机会。
在到目前为止的所有章节中,你已经涵盖了大量的概念、技术和算法。在本书的最后一章,我们将讨论端到端模型部署过程。我们将涵盖从业务问题定义、数据清洗、探索性数据分析(EDA)到模型部署和维护的各个方面。这一端到端的旅程对于你理解整个过程至关重要。我们将在所有相关位置讨论 Python 代码。
欢迎来到最后一章,祝您一切顺利!
11.1 机器学习建模过程
回想第一章我们简要讨论了端到端模型开发。在本节中,我们将详细涵盖每个相应的步骤以及我们面临的最常见问题以及如何解决这些问题。最终将引导到模型部署阶段。图 11.1 显示了我们所遵循的模型开发过程。

图 11.1 完整的机器学习建模过程
模型开发过程中的步骤如下:
-
业务问题定义
-
数据发现和可行性分析
-
数据清洗和预处理
-
探索性数据分析
-
建模过程和业务审批
-
模型部署
-
模型文档
-
模型维护和模型更新
在本章中,我们将详细涵盖这些过程中的每一个。这些都是建模过程的相关内容。
11.2 业务问题定义
您的业务问题定义是第一步。确保业务问题简洁、明确、可衡量和可实现至关重要。在实践中,很多时候业务问题定义得模糊,例如“降低成本或增加收入”,这通常会导致整个过程中出现不良结果。一个良好的业务问题应该明确地定义,包括关键绩效指标(KPIs)和可以用来衡量效果的参数。一个良好的业务问题确保没有歧义,目标明确,我们可以在可用资源和时间范围内实现它。
关于业务问题的一些最重要的考虑因素如下:
-
如果业务问题定义得模糊不清,将会引起问题,应该避免。例如,所有企业和各种职能都希望增加收入和利润,降低成本,优化各种流程等等。在模糊的业务问题下,我们将对过程缺乏清晰的认识,这会导致模糊性。
-
商业目标应该是实际可实现的。不应设定像翻倍收入或减半成本这样的不切实际的目标。不切实际的目标意味着良好的结果可能会被拒绝,因为它们不符合业务目标。
-
如果可能的话,商业问题应该是可衡量的。如果商业问题只是定性的,那么它将只有有限的帮助。我们将无法理解所创建的机器学习模型的实际效果。
-
范围蔓延是我们有时面临的问题之一。范围蔓延发生在项目开始时,在项目构建过程中,范围发生剧烈变化,改变了项目的要求和时间需求,而没有相应地改变资源和截止日期。
一个有效的商业问题被正确、完整地定义,并与业务团队进行讨论。它简洁,具有可衡量的 KPI,并在给定的时间内可实现。
注意:商业利益相关者和主题专家应参与定义商业问题。他们应从项目开始就是团队的一部分,并拥有整个流程。
一些好的商业问题的例子如下:
-
组织中的营销团队旨在优化各种成本并最大化投资回报。他们希望确定营销努力(电子邮件、电话、电视广告和会议)的最佳组合,以在接下来的六个月内将投资回报率提高 1.5%。
-
一个制造团队在过去三个月面临缺陷数量增加的问题。商业问题可能是确定导致缺陷增加的所有潜在原因。团队还希望了解是否存在趋势或模式。业务目标可能是筛选出最显著的缺陷原因,并在接下来的六个月内将其减少 2.5%。
我们已经描述了商业问题的属性。我们现在进入下一阶段,即数据发现和可行性。
11.3 数据发现和可行性分析
数据发现阶段是整个模型构建过程中最重要的步骤之一。如果数据在数量和质量上都不足,可能非常难以创建我们想要的解决方案。同时,能够访问这些数据至关重要。
在此过程中,我们还对项目进行可行性分析:
-
数据是主角。第一步是确定用于商业问题用例所需的数据集以及所有利益相关者访问这些数据集的机制。因此,建议
-
数据集可以从服务器或云中获取,并且已正确设置相关权限供需要访问的人员使用。服务器可以从数据库(如 SQL/MySQL/NoSQL/MongoDB)中访问数据。
-
如果数据存储在 Excel/.csv/text 文件中,将其放在服务器上会有所帮助。近年来,像 AWS、Azure、Google Cloud 等云服务器被用于存储数据。
-
-
确保数据集完整且与业务问题相关至关重要。数据集应该足够代表手头的业务问题,并捕捉业务中的所有变化。数据的时间和持续时间是我们应该牢记的另一个重要维度。例如,如果我们想分析电信运营商或零售公司的业务,我们应该有足够的数据(至少是去年的数据,以便捕捉季节性)以及围绕销售、交易、折扣、购买的产品/服务、营销行为、历史行为、线下/在线购买等变量。
-
在这个阶段计划数据刷新是明智的。毕竟,一旦模型建立,我们就需要维护和更新它。
在这个阶段,我们可能面临的最常见问题如下:
-
我们可能会发现数据集中存在某些缺失值、异常值等。我们将在下一节中详细说明。
-
我们还必须确保在数据集上应用正确的业务规则。确保这一点的步骤如下
-
获取与业务问题相关的数据集。
-
进行一些基本分析,如总销售额、客户数量、按月趋势、折扣等。
-
让这些关键绩效指标(KPIs)由业务利益相关者进行验证。如果数字错误,则需细化业务规则。
-
只有当数据正确且数字准确时,我们才能继续进行用例的可行性分析。对于可行性分析,我们执行以下操作:
-
详细检查数据质量。我们将在下一节中涵盖各个方面。
-
分析数据以寻找任何模式,例如季节性等。我们还会检查是否存在各种变量之间的相关性,以确保哪些变量彼此相关。
-
检查业务问题与数据集之间的关系。这之后是探索性分析,以确定是否存在不同客户群体之间的显著差异。
在此步骤之后,我们将进入数据清洗、预处理和准备阶段。这是我们必须要做的耗时步骤之一。
11.4 数据清洗和预处理
在最后一步,我们筛选出与业务问题相关的数据。现在我们将进入建模过程中的数据清洗和预处理阶段。
数据在其原始形式可能不足以被输入到机器学习模型中。我们必须创建一些额外的变量并处理一些其他数据。在现实商业世界中,数据集通常是“脏”的。数据中可能存在许多问题,如下所述:
-
重复值
-
分类别变量(可能对某些算法造成问题)
-
缺失值、NULL 或非数字(NaN)等。
-
异常值
-
其他问题(如前几章所述)
让我们逐一处理这些问题。本章的代码已提交至mng.bz/vKY7。您可以在那里访问代码和数据集。我们现在将研究如何处理数据集中的重复值。
11.5 数据中的重复值
重复项在数据集中通常是一个问题。如果数据集中有两行是完全相同的,它们在本质上就是重复的。这个问题可能发生在数据捕获时。重复项的问题在于会影响统计数据——例如,使某些事件看起来比实际更频繁。在删除重复项时,需要注意不要删除真正发生两次的事件的真实数据——例如,一个客户在两个不同时间购买同一商品,或者一个客户在同一时间购买两个相同商品,而购买交易被记录了两次。
以下是一个简单的 Python 程序删除重复项的步骤(见图 11.2):
-
导入
numpy和pandas。 -
定义一个包含一些虚拟变量的数据框。
-
打印数据框。
-
有一个内置方法:
drop_duplicates()。使用它来删除重复项。 -
打印数据框并发现重复行已被删除。

图 11.2 在简单的 Python 程序中删除重复项
11.6 分类变量
下一步是处理分类变量。让我们回顾一下分类变量的定义。性别、城市、产品类别、邮编等变量是分类变量的例子。分类变量可能不是数据中的严格问题,但它们可能会给某些算法(如 k-means 聚类)带来问题。回想一下,对于 k-means 聚类,需要在数据点之间计算距离。
在某些数据集中,一个分类变量可能几乎所有值都相同。例如,如果整个数据集都是关于英国的,而一个变量是“城市”,由于很大一部分人口居住在伦敦,那么这个变量可能作用有限。它不会在数据集中创建任何变化,也不会有用。同样,像“邮编”这样的分类变量可能所有值都是独特的,也不会对分析增加太多价值。
处理分类变量最常见的方法可能是使用独热编码。在独热编码中,正如 Python 代码书中所示,变量会被转换:
-
使用我们在上一段代码中使用的相同数据集。
-
pandas 中有一个内置方法
get_dummies(),可以用来将分类变量转换为数值型。见图 11.3。

图 11.3 执行代码的输出
11.7 数据集中的缺失值
在现实世界的数据集中,最常见的一个挑战是缺失值,这些值可能是空白、NULL、NaN 等。这可能是由于数据捕获问题或数据转换造成的。缺失值应该被处理以确保解决方案的稳健性。缺失值可能有几个原因:
-
在数据捕获过程中,值没有被正确记录。这可能是由于设备故障或记录数据时的手动错误。
-
许多时候,非必填字段没有被填写。例如,在填写零售忠诚度表格时,顾客可能不会填写年龄。
-
调查响应可能没有完全填写——例如,薪资详情。
为了减轻缺失值,有几个选项:
-
首先,我们应该检查数据是否是故意缺失的,以及是否是一个需要解决的问题。例如,一个传感器可能不会记录超过一定压力范围的任何温度值。在这种情况下,温度的缺失值是正确的。
-
我们还应该检查缺失值是否与其他独立变量以及目标变量之间存在任何模式。例如,在下一个示例中使用的数据集中,我们可以推断出,每当温度值为 NULL 时,设备就出现了故障。在这种情况下,温度和故障设备之间有明显的模式。因此,删除温度或处理温度变量将是错误的一步。
-
处理缺失值可能最简单的方法是删除包含缺失值的行。尽管这很简单且快速,但它会减少人口规模,并可能删除前面描述的非常重要的信息,例如,如果一个人的合法姓氏不可用。因此,我们应该小心删除行。
-
我们可以通过均值、中位数或众数来填充缺失值。均值或中位数仅适用于连续变量。众数可以用于连续和分类变量。
-
还有其他一些流行的缺失值填充方法,例如使用 k 最近邻和链式方程的多变量填充。
我们现在使用 Python 来填充缺失值。我们将使用内置方法SimpleImputer并用均值来填充缺失值。第二个解决方案是用于分类变量,其中使用众数来替换缺失值。见图 11.4。

图 11.4 代码执行后的输出
在接下来的解决方案中,我们将使用IterativeImputer和 k 最近邻算法。
11.8 数据中的异常值
异常值在数据中可能是一个大问题。考虑以下情况:假设一个城市的平均降雨量为 50 厘米。但有一年,由于大雨,平均降雨量达到了 100 厘米。这个数据点将是一个异常值,如果包含在内,将完全改变分析结果。在例子中,根据是否将重降雨年份包含在统计分析中,结果(比如可能的保险索赔)可能会有很大不同。
因此,就像缺失值一样,异常值不一定是一个错误。我们应该运用商业敏锐度来推断数据点是否真的是研究中的异常值。
我们可以通过以下方式检测异常值:
-
如果一个数据点位于 5 百分位数和 95 百分位数之外,或者 1 百分位数和 99 百分位数之外,它可以被认为是异常值。
-
一个超出-1.5 倍四分位距(IQR)和+1.5 倍 IQR 的值也可以被认为是异常值。这里 IQR 由(第 75 百分位数的值)-(第 25 百分位数的值)给出。
-
距离均值一个、两个或三个标准差之外的价值可以被称为异常值。
我们可以创建图表并可视化异常值。我们可以通过以下方法处理异常值:
-
一个位于 5 百分位数和 95 百分位数之外的数据点可以分别被限制在 5 百分位数和 95 百分位数。或者一个位于 1 百分位数和 99 百分位数之外的数据点可以分别被限制在 1 百分位数和 99 百分位数。
-
有时也会使用均值、中位数或众数进行替换。
-
有时对变量取自然对数可以减少异常值的影响。但由于自然对数会改变实际值,我们应该使用合适的数学模型来确保它是适当的。
异常值对我们的数据集提出了很大的挑战。它们扭曲了我们从数据中得出的见解。有时这种扭曲是合适的(例如,异常重降雨年份的保险索赔,保险公司需要考虑)。在任何情况下,我们至少要突出显示数据集中的异常值,有时还需要修改它们。
11.9 探索性数据分析
探索性数据分析(EDA)在我们开始建模之前是最关键的步骤之一。通过 EDA,我们生成对业务非常有用的见解。从 EDA 中生成的见解也符合建模输出。
在 EDA 中,我们检查所有变量并了解它们的模式、相互依赖性、关系和趋势。在 EDA 阶段,我们了解数据应该如何表现。在这个阶段,我们从数据中揭示见解和建议。强大的可视化可以补充完整的 EDA。
注意:EDA 是成功的关键;很多时候,良好的 EDA 可以解决业务问题。
接下来,我们使用 Python 对数据集进行详细的 EDA 分析。整个代码对于一本书来说相当庞大;因此,带有完整解释和注释的 Python 笔记本已提交到 GitHub 仓库(mng.bz/vKY7)。
11.10 模型开发和业务批准
我们已经在整本书中详细介绍了建模过程。这包括创建模型的第一个版本,然后使用不同的超参数和不同的算法进行迭代。
在整本书中,我们介绍了许多关于聚类和降维方法的算法。我们还涵盖了文本数据集的建模。在模型开发阶段,根据手头的业务问题和数据集,我们选择候选算法。我们总是努力根据我们在前面章节中讨论的准确度测量参数选择最佳算法。
建模过程的输出是最终算法,它为手头的业务问题提供最佳输出。找到性能令人满意的模型后,我们应该与业务利益相关者进行讨论,以获取他们的最终反馈。可能需要几次迭代来进一步改进模型。
现在,你已经有一个在统计上显著、有用且得到业务利益相关者认可的模型。我们可以进入模型部署阶段。
11.11 模型部署
模型部署是人工智能和机器学习模型开发中的一个关键阶段。它是开发和生产环境之间的转换点,模型在此用于现实世界的商业目的。有许多方面需要考虑,如基础设施问题、部署方法、监控和维护。我们讨论了与模型部署相关的挑战和推荐步骤,以及一个系统化和有组织的策略,以将模型投入生产。
11.12 模型部署的目的
模型部署是一个关键过程。模型部署的主要原因如下:
-
模型的部署将洞察力转化为可操作和实用的目的。模型用于进行预测、优化、推荐和建议。
-
部署的模型与业务流程和工作流程集成。这促进了基于模型提出的洞察和建议的各种流程和业务功能的自动化。
-
实时预测确保业务能够快速响应不断变化的商业条件。实时预测在交易中的信用卡欺诈检测、动态定价等场景中特别有用。
-
优化和自动化得到增强。模型部署通过自动化业务功能减少了员工的工作量。借助部署的模型,硬件使用得到优化,业务功能和流程变得更加高效,整体投资回报率提高。
-
部署模型后,可以对模型进行版本控制。这确保了组织可以跟踪变化,执行 A/B 测试,并在需要时进行回滚。
总结来说,模型部署的目的是将机器学习模型的潜力转化为实际应用,使它们成为业务运营和决策过程中的重要组成部分。部署使组织能够利用 AI 和数据科学的威力,从现实世界场景中获取模型的实际价值。
11.13 模型部署类型
模型部署有几种类型。根据需求和战略目标,我们可以在它们之间进行选择。不同的部署策略包括
-
批量部署—当我们在一段时间内收集了大量数据集,并需要使用机器学习模型在离线模式下评估这些数据并做出预测时,使用这种方法。通常,处理是在大批次中完成的。例如,如果我们想根据 k-means 聚类对零售店的客户进行聚类,我们可以取他们过去两年的属性,并为每个客户生成相应的聚类。我们可以在一个月后刷新底层数据,因此我们可以重新分配这些聚类。
-
实时部署—考虑这种情况:我们想要检查 incoming credit card transaction 是否为真实或欺诈。在这种情况下,我们使用实时检查。预测是基于最新信息实时生成的。通常,为了支持实时预测,我们应该采用多线程处理,以便同时处理多个预测请求。例如,可能会有数百个信用卡请求同时提交,我们的系统需要以非常低的延迟对它们进行分类。
-
边缘部署—如今,人们期望智能手机或物联网设备具备适合机器学习或 AI 算法的复杂功能。在这种情况下,云部署是可行的,但在没有互联网连接的情况下,也会使用边缘部署。边缘部署的前提是机器学习模型应该体积小,计算需求低,以便在资源有限的设备上运行。
-
金丝雀部署—在金丝雀部署中,我们在对所有用户进行全面部署之前,先将模型发布给一部分用户。这样可以确保不稳定版本不会发布给所有用户,因为我们将在第一阶段从测试用户那里获得反馈。这通常由拥有大量用户并通过云提供服务的公司,如谷歌或 Facebook,来完成。
-
A/B 测试—A/B 测试实际上并不是一种模型部署技术,但它可以用作一种技术,这就是为什么它被列在这里的原因。在 A/B 测试中,组织希望测试一个解决方案/服务/产品与另一个相比如何。例如,如果产品团队希望测试两个方案哪个能带来更好的盈利能力,他们将使用 A/B 测试。两个方案的例子可以是“花费 100 美元获得 15%的折扣”或“花费 50 美元获得 10%的折扣”。在这种情况下,可能会有两组相似的客户群体将收到这些优惠,我们将检查哪个能带来更好的盈利能力。在 A/B 测试部署中,两个不同的模型(或具有不同超参数的相同模型)将相互测试。
11.14 部署模型时的注意事项
在部署模型时,我们应该注意相当多的因素,以确保模型从开发到部署的平稳和有效过渡:
-
准确性监控—我们应该持续监控模型的性能,并在性能低于阈值时进行改进。我们应该涵盖关键指标,如准确性、资源利用率、时间和准确性。
-
可扩展性—解决方案应该可扩展到其他部门或品牌。随着时间的推移,数据的量也可能增加。
-
安全和合规性—这是一个绝对不能妥协的考虑因素。任何类型的部署都应该完全安全,免受任何威胁,并且完全符合现有的最佳实践、政策和要求。
-
模型漂移和数据漂移—这些应该被监控,因为整体业务场景可能会发生变化。客户、他们的偏好、市场以及整体经济可能会发生变化。有像 COVID、战争、洪水等事件,因此存在数据漂移。这也会导致模型性能的变化。因此,我们应该提前规划模型漂移。
-
可重现性—当我们部署模型时,结果的可重现性是一个重要因素。我们应该能够复制这些结果。
-
持续集成和持续部署—这些管道需要自动化测试和部署过程。这减少了错误的风险,并确保了平稳的部署。
-
用户反馈和迭代—这些对于项目的成功非常重要。在规划部署时,我们应该充分考虑将用户的反馈和模型的迭代纳入其中。
-
版本控制和回滚——没有模型是最终的。它有连续的迭代。在基础设施中,应该有回滚到上一个版本的规定,如果新版本有任何问题,或者基于业务需求的理由。
通过这一点,我们已经涵盖了模型部署的所有考虑因素。现在我们将使用 Flask 部署一个模型。整个代码已经上传到 GitHub 仓库(mng.bz/vKY7),并附有完整的注释和解释。
11.15 文档
我们已经部署了模型。现在我们确保所有代码片段都已清理,注释得当,并遵循最佳实践。代码文件应被检查并妥善记录。不幸的是,文档编写往往没有得到足够的时间,但它是一个非常重要的步骤,不应被忽视。如果在编写文档时需要设定优先级,应优先考虑那些可能发生变化以及需要与外部利益相关者理解和互动的方面。
代码版本控制有许多工具。Git 可能是最常见的一个。确保我们所有的代码都定期检查入库,以保护我们免受任何潜在计算机故障的影响,这是一个非常好的实践。对于文档,我们确实在行业内有很多选择,从 Word 到 PowerPoint 到 Confluence 页面,具体取决于我们工作的行业。
11.16 模型维护和更新
到目前为止,我们已经涵盖了模型开发和部署的所有阶段。但是一旦模型投入生产,就需要持续监控。我们必须确保模型始终以期望的准确度水平运行。为了实现这一点,建议设置一个仪表板或监控系统来定期评估模型的性能。如果不存在这样的系统,可以对模型进行每月或每季度的检查。
模型部署后,我们可以对模型进行每月的健康检查。这意味着我们将模型的性能与预期的准确度进行比较。如果性能不佳,模型需要更新。即使模型可能没有恶化,但在不断创建和保存的新数据点上刷新模型仍然是一个好的实践。模型更新通常基于业务问题以及模型所构建的业务领域。例如,在电信领域,由于客户每天使用手机,数据更新速度更快。另一方面,对于零售服装,我们并不期望客户每天购买衣服。因此,电信领域的模型可以每周或每两周刷新一次,而对于服装,我们可以每季度或每六个月刷新一次。
模型刷新是一个相当重要的现象。我们的业务场景在本质上总是动态变化的。客户的偏好和生活方式会发生变化,而且竞争对手总是在进行某些活动。有一些场景是我们无法控制的,比如战争、COVID 等。因此,我们始终应该努力调整我们的模型以适应我们业务中的最新场景。
模型刷新意味着我们正在根据我们收集到的新数据点重新训练模型。这确保了我们能够捕捉到数据中的最新趋势、背景和新兴关系,因此我们的模型能够预测、优化和加速最新的数据点。
有了这些,我们已经完成了设计机器学习系统的所有步骤:如何从头开始开发它,如何部署它,以及如何维护它。这是一个相当繁琐且需要团队合作的长过程。
11.17 总结性思考
从头到尾的机器学习开发是一个相当耗时的工作。从零开始到维护,它需要大量的规划、团队合作、业务知识和努力。在本章中,我们涵盖了这些步骤中的许多。也可能有其他可能的解决方案,这些解决方案取决于业务领域和需求。
有了这些,我们这本书就接近尾声了。我们都阅读并感受到在这个新时代,数据是新石油、新电力、新力量和新货币。这个领域正在迅速发展,并在全球范围内产生影响力。增强和改进的速度创造了新的工作机会,如数据工程师、数据科学家、可视化专家、机器学习工程师、MLOps、DevOps、GenAI 专家等,需求每天都在增加。但是,满足这些职位描述严格标准的专业人士却很少。当务之急是需要有数据艺术家,他们能够将业务目标与分析问题相结合,预见解决方案以解决动态的业务问题,适应不断变化的技术环境,并且还能提供成本效益高的业务解决方案。
每天都在创建更复杂的系统。我们可以看到自动驾驶汽车、人类聊天机器人、欺诈检测系统、面部识别解决方案、目标检测解决方案、优化和监控解决方案等例子。GenAI 的使用进一步增强了效果。
同时,也存在一些风险,我们应该有所警觉。人类有责任如何利用数据的力量。有些情况下(如果我们相信所声称的),AI 被用于操纵选举结果,或者 DeepFake 被用于改变人们的照片或基于种族/颜色等进行人物画像。我们可以使用机器学习和 AI 来传播爱与恨——这是我们的选择。就像俗语所说:能力越大,责任越大!
我们真诚地希望您喜欢这本书。恭喜您,祝您下一步一切顺利!
11.18 实践下一步行动和建议阅读
以下提供下一步行动的建议和一些有用的阅读材料:
-
阅读以下关于模型部署的两篇研究论文:
-
Paleyes, A., Urma, R-G., and Lawrence, N. D. (2020). 部署机器学习面临的挑战:案例研究综述。
arxiv.org/abs/2011.09926v2 -
Sculley, D., Holt, G., Golovin, D., et al. (2015). 机器学习系统中的隐藏技术债务。
mng.bz/4azw
-
-
使用我们在前几章中开发的数据集,并对这些数据集进行 EDA。
摘要
-
学习之旅是持续的,需要勇气、耐心和勤奋;理解从概念化到模型部署的整个过程对于掌握机器学习至关重要。
-
端到端模型部署过程包括业务问题定义、数据清洗、EDA 以及最终模型部署和维护等关键步骤。
-
机器学习建模过程包括不同的阶段,如业务问题定义、数据发现和可行性分析、数据预处理、EDA、建模、部署、文档和运维。
-
明确且可达成业务问题定义对于有效地对齐目标、防止范围蔓延以及确保 KPI 可衡量以评估模型效果至关重要。
-
数据发现涉及识别必要的数据集,确保访问和完整性,并分析可行性,特别关注数据的相关性、质量和表示。
-
数据清洗和预处理解决常见问题,如重复数据、分类变量、缺失数据和异常值,利用各种技术为有效建模准备数据集。
-
EDA 对于理解数据模式和关系以及生成可操作见解至关重要,为成功的模型开发奠定基础。
-
模型开发阶段使用适合业务问题的算法,并需要利益相关者的协作以进行细化。
-
模型部署连接了开发和生产,需要考虑基础设施、实时应用、扩展、安全和持续集成以优化模型效用。
-
模型部署的类型包括批量、实时、边缘、金丝雀和 A/B 测试,每种类型根据战略目标和应用环境提供不同的优势。
-
有效的部署涉及准确性监控、检测模型和数据漂移、确保合规性和数据安全,以及确保可重复性和可扩展性。
-
部署后,详尽的文档和版本控制对于代码完整性至关重要,并在必要时促进未来的迭代或回滚。
-
模型维护包括定期性能检查和更新,适应动态的商业环境,并确保与不断变化的数据趋势保持一致。
-
数据驱动解决方案具有巨大的潜力,但也同样需要高度的责任使用。我们通过强调道德应用的重要性来结束这本书。
附录 A 数学基础
A.1 聚类算法列表
A.1.1 基于划分的算法
-
k 均值
-
k-medoids(PAM)
-
CLARA(聚类大型应用)
-
CLARANS(基于随机搜索的聚类大型应用)
-
小批量 k 均值
-
模糊 C 均值(FCM)
-
k-modes
-
k-prototypes
A.1.2 层次聚类
-
聚类层次结构
-
分裂层次聚类
-
BIRCH(使用层次结构的平衡迭代减少和聚类)
-
CURE(使用代表的聚类)
-
骆驼
-
ROCK(使用链接的鲁棒聚类)
-
HIERDENC(基于密度的层次聚类)
-
HAC-S(具有空间约束的层次聚合聚类)
-
EAC(集成聚合聚类)
A.1.3 基于密度的算法
-
DBSCAN(基于密度的空间聚类应用噪声)
-
OPTICS(用于识别聚类结构的点排序)
-
HDBSCAN(层次 DBSCAN)
-
DENCLUE(基于密度的聚类)
-
均值漂移
-
VDBSCAN(基于可变密度空间聚类)
-
DBCLASD(大型空间数据库的基于分布的聚类)
-
LDBSCAN(标记 DBSCAN)
A.1.4 基于网格的算法
-
STING(统计信息网格)
-
WaveCluster
-
SUBCLU(子空间聚类)
-
GRIDCLUS(基于网格的聚类算法)
-
OptiGrid
-
CLIQUE(在查询中的聚类)
A.1.5 基于模型的算法
-
高斯混合模型(GMM)
-
EM(期望最大化)算法
-
DBEM(基于密度的 EM)
-
贝叶斯高斯混合模型
-
隐藏马尔可夫模型(HMM)聚类
-
X-Means(扩展 k 均值)
-
G-Means(高斯均值)
-
MCLUST(使用 EM 的基于模型的聚类)
-
AUTOCLASS(基于贝叶斯模型聚类)
-
Mixmod(聚类混合模型)
A.1.6 谱聚类
-
比率切割聚类
-
归一化切割聚类
-
多路谱聚类
-
谱双聚类
-
Shi-Malik 聚类
-
聚类用的拉普拉斯特征图
A.1.7 基于图的聚类
-
连通分量聚类
-
马尔可夫聚类(MCL)
-
Girvan-Newman 聚类
-
社区检测的 Louvain 方法
-
Infomap 算法
-
Walktrap 算法
-
边界介数聚类
-
中国 whispers 聚类
-
SPICi(聚类中的速度和性能)
-
SCPS(Perona & Shi 图上的谱聚类)
A.1.8 子空间和高维聚类
-
PROCLUS(投影聚类)
-
SUBCLU(子空间聚类)
-
ENCLUS(基于熵的子空间聚类)
-
ORCLUS(正交子空间聚类)
-
FSSC(快速子空间聚类)
-
P3C(基于模式的子空间聚类)
-
FIRES(频繁项集聚类)
-
SNN(共享最近邻聚类)
-
高维谱聚类
-
LAC(局部自适应聚类)
A.1.9 模糊和软聚类
-
模糊 C 均值
-
Gustafson-Kessel 算法
-
模糊最小-最大聚类
-
可能性 C 均值(PCM)
-
FCM-GA(带遗传算法的模糊 C 均值)
-
带空间约束的模糊 C 均值(FCM-SC)
-
模糊子空间聚类
-
模糊 SOM(自组织映射)
A.1.10 基于约束的聚类
-
COP-k 均值(约束 k 均值)
-
约束 DBSCAN
-
C-DBSCAN(基于约束的 DBSCAN)
-
PCKMeans (成对约束 k-means)
-
半监督 k-means
-
带有必须链接和不能链接约束的 FCM
-
带约束的硬 k-means
A.1.11 进化遗传聚类
-
基于遗传算法的聚类
-
GA-KMeans (带有 k-means 的遗传算法)
-
AGCT (聚合遗传聚类)
-
MEPSO (用于聚类的多精英粒子群优化)
-
NSGA-II (用于聚类的非支配排序遗传算法 II)
-
ACO-CLUSTER (用于聚类的蚁群优化)
-
GCUK (带有无监督 k-means 的遗传聚类)
A.1.12 基于神经网络的聚类
-
自组织映射 (SOM)
-
神经气
-
增长神经气
-
基于自动编码器的聚类
-
深度嵌入聚类 (DEC)
-
生成拓扑映射 (GTM)
-
DeepCluster (聚类深度学习)
A.1.13 其他算法
-
相似性传播
-
二分 k-means
-
混合 BIRCH-k-means
A.2 什么是中心点?
聚类中心是聚类中的中心点。在几何学中,它是形状中所有点的算术平均值或平均值。例如,在三角形中,中心点是所有中线相交的点(见图 A.1)。在任何其他形状中,它将是所有点坐标的平均值。

图 A.1 中心点的示例
A.3 L1 与 L2 范数
L1 范数是向量中所有项的绝对值之和;另一方面,L2 范数是向量中所有项平方和的平方根。这是 L1 和 L2 范数之间的核心区别。
A.4 工业中使用的不同缩放技术
我们得到的数据可以有不同的单位和值。一个数据集中的一个变量可以从 1 到 10 变化,而同一数据集中的另一个变量可以从 1,000 到 100,000 变化。数据归一化允许我们将数据归一化或限制在某个范围内。这使我们能够更好地将机器学习应用于归一化后的数据集。
我们归一化数据集以调整不同变量值,这些变量值在相当不同的尺度上。一个例子如图 A.2 所示。

图 A.2 在第一表中,我们有每个变量的均值和标准差。一旦数据归一化,均值和标准差就变为零,如第二表所示。
归一化数据集的方法有很多种。其中最流行的是
- 标准化—这涉及使用均值和标准差来归一化数据集。它也被称为Z 变换。它标准化所有变量;数据变为正态分布,所有特征都变得可比较。使用的方程式如图 A.1 所示:

(A.1)
其中 μ 是均值,σ 是标准差。
正如我们在图 A.2 右侧观察到的,现在所有变量的均值都是 0,标准差都是 1。
- 最小-最大缩放—这使用方程 A.2 中的最大值和最小值来利用变量的值:

(A.2)
在机器学习过程中,归一化数据集是重要的步骤之一。
A.5 时间复杂度 O(n)
时间复杂度是一个计算概念,用于衡量和估计算法完成所需的时间,作为输入长度的函数。通常使用大 O 符号表示,时间复杂度用于根据算法的最坏情况或平均情况运行时间性能对算法进行分类。
时间复杂度的主要方面包括
-
常数时间 [O(1)]—算法的运行时间不随输入大小而改变。
-
对数时间 [O(log n)]—随着输入大小的增加,运行时间呈对数增长。这通常发生在每一步都将问题规模减半的算法中,如二分搜索。
-
线性时间 [O(n)]—运行时间随着输入大小的增加而线性增加。
-
对数时间 [O(n log n)]—这在高效的排序算法如归并排序和堆排序中很常见。
-
二次时间 [O(n²)]—运行时间随着输入大小的平方增长,通常出现在具有嵌套循环的算法中。
-
指数时间 [O(2^n)]—随着输入中每个额外元素的添加,运行时间翻倍,这在一些递归算法中很典型。
理解时间复杂度有助于评估算法的效率,并为给定问题选择正确的算法。
A.6 如何在 Python 中安装包
在 Python 中,通常使用 pip 命令来安装包。步骤如下:
-
打开你的命令行界面(终端、命令提示符或 PowerShell)。输入
pipinstallpackage_name。例如,如果你想安装numpy,输入pip installnumpy。 -
如果你想要安装特定版本,使用
pipinstallpackage_name== version_number。例如,如果你想安装numpy1.21.0,输入pipinstall numpy==1.21.0。 -
从需求文件安装,你可以创建一个包含所有包信息的
requirements.text文件,然后安装它:pipinstall-rrequirements.text。 -
有时候你可能需要升级一个包。那么命令是
pip install–upgradepackage_name。例如,pip install-–upgradenumpy。
A.7 相关系数
相关系数是衡量两个变量之间相关程度的统计和数学关键性能指标。它用于解码变量之间的关系,表明一个变量的增加是否倾向于导致另一个变量的增加(正相关)或减少(负相关)。
关键的相关类型包括
-
正相关—当一个变量增加时,另一个变量也增加。例如,身高和体重通常显示出正相关。
-
负相关—当一个变量增加时,另一个变量减少。例如,很多时候,当商品的价格增加时,需求减少,这就是负相关。
-
无相关性**—这是指两个变量之间没有明显的关系。例如,冰淇淋的销量和电视的销量可能没有相关性。
A.7.1 相关系数
相关系的强度和方向通过相关系数量化,通常表示为r。它介于-1 到 1 之间:
-
r = 1—*完全正相关性
-
r = –1**—完全负相关性
-
r = 0**—无相关性
介于-1 和 1 之间的值表示相关性的不同程度。
A.7.2 相关性的用途
相关性在许多领域得到应用,包括以下:
-
数据分析—相关性有助于分析师识别变量之间的关系,有助于进一步的分析或研究。
-
预测建模—在机器学习和预测建模中,如果我们理解变量之间的关系,可以提高模型的表现。
-
金融—投资者和财务顾问使用相关性分析来评估资产价格、不同因素和投资原因之间的关系,这有助于投资和投资组合多元化策略。
-
医疗保健—健康领域的研究人员收集生活方式因素(如饮食、吸烟、运动)和人口统计数据,并检查这些因素与健康结果之间的相关性,以识别潜在的风险因素,如心脏病发作、糖尿病等。
-
社会科学—在心理学和社会学等领域的学科中,相关性被用来探索消费者行为、人口态度和人口统计因素之间的关系。这些研究有助于揭示购买模式、评论和反馈中的关系。
A.7.3 重要注意事项
在考虑相关性时,请记住以下观点:
-
相关性并不表示因果关系。仅仅因为两个变量相关,并不意味着一个导致另一个。可能还涉及其他因素,或者可能只是巧合。例如,我们可能会发现冰淇淋的销量与鲨鱼攻击的数量呈正相关。因此,我们推断冰淇淋销量影响鲨鱼攻击——这是荒谬的。真正的原因是冰淇淋销量在夏季季节增加,而这是更多人访问海滩的时候。
-
可能存在异常值。极端值可能会扭曲相关系数,因此对异常值保持警惕很重要。很多时候,如果我们只是用散点图可视化数据,我们可能会得到真实的关系。
-
可能存在非线性关系。相关系数衡量线性关系。如果两个变量之间的真实关系是非线性的,相关性可能无法捕捉到这一点。
理解相关性在各个领域都是基础性的,通常也是第一步。它有助于揭示有助于进一步推动战略决策和整体路径的见解。
A.8 时间序列分析
时间序列分析涉及对在特定时间间隔(如每小时/每日/每周/每月/每年或其他)收集或记录的数据点的研究。它用于检查和理解趋势和行为、季节性模式和关系,以及随时间周期变化的周期性行为,因此理解这些模式将有助于预测。例如,如果我们想预测温度或降雨量,或者如果我们想预测某项商品的需求,这可能涉及时间序列分析。
时间序列分析在营销、金融、环境研究和地理经济预测等领域被广泛使用,用于根据历史数据预测未来值。尽管有相当多的技术,但最常见的是移动平均、指数平滑和 ARIMA。可视化方法,如折线图,对于识别数据中的模式和异常至关重要。总的来说,时间序列分析是一种理解数据中基于时间模式的实用技术,并且对于做出明智的预测和预报很有帮助。
A.9 数据表示的数学基础
为了全面理解算法,必须理解一些数学术语。它们对于理解概念和数学基础很有用,对于第三章中探讨的降维方法(如主成分分析和奇异值分解)至关重要。这些数学运算足够直观,你可能已经在早期的数学课程中接触过它们,但在这里刷新概念是很重要的。所考察的概念并不新颖,但有时难以解释和理解。
注意:有时在 Python 中编码这些概念可能会很棘手。幸运的是,有相当多的稳健的库和包提供了更简单的解决方案,因此我们不必担心这些概念在 Python 中的实现。
我们试图减少数据集的维度。数据集不过是一个值矩阵;因此,许多概念都与矩阵操作方法、它们的几何表示以及在这些矩阵上执行变换有关。接下来将研究主要概念。
A.9.1 标量和向量
用简单的话说,如果你走了 5 公里的距离,它是标量;如果你朝某个方向(比如北)走了 5 公里,它就是一个向量。因此,我们可以这样说,向量是一个具有大小和方向的数学对象。没有方向,它只是一个标量值。我们在表 A.1 中列出了一些每个类别的例子。
表 A.1 标量和向量量的例子
| 标量量的例子 | 向量量的例子 |
|---|---|
| 长度、宽度、高度、距离 | 位移 |
| 质量、面积、密度、体积 | 重力、力 |
| 压力、温度、能量、熵 | 升力、阻力、推力 |
| 速度,时间,工作,功率 | 速度,加速度,动量 |
简而言之,我们可以得出结论,向量是一个具有方向的标量。
A.9.2 标准差和方差
标准差和方差的目的是衡量数据的分散程度。标准差由方程 A.3 给出

(A.3)
其中 x[i] 是来自总体的每个值,μ 是总体的均值,n 是总体大小或观测值的数量,σ 是总体的标准差。方差由方程 A.4 给出

(A.4)
其中 x[i] 是来自总体的每个值,xbar 是观测值的平均值,n 是观测值的数量,S^([2]) 是样本方差。
假设我们有一个班级里有五个孩子,他们的身高分别是 50 英寸、51 英寸、52 英寸、53 英寸和 54 英寸。平均身高是 (50+51+52+53+54)/5 = 52 英寸。见表 A.2。
表 A.2 儿童身高和平均身高与身高的计算差值
| 儿童 | 身高 | 平均身高与身高的差值 |
|---|---|---|
| A | 50 | 52 – 50 = 2 |
| B | 51 | 52 – 51 = 1 |
| C | 52 | 52 – 52 = 0 |
| D | 53 | 52 – 53 = –1 |
| E | 54 | 52 – 54 = –2 |
A.9.3 协方差和相关系数
协方差和相关系数是衡量两个变量之间关系和相互依赖性的度量。协方差是线性关系的方向,而相关系数衡量关系的强度和方向。见图 A.3。

图 A.3 如果 X 在增加,那么 Y 的值也在增加(左)。如果 X 在增加,Y 在减少(中间)。X 和 Y 之间没有观察到的关系(右)。
图 A.3 左侧显示,当 X 减少,Y 增加,反之亦然在中间,而在右侧,似乎两个变量之间没有关系。在这里,协方差将简单地表示变量之间存在正相关、负相关或无相关关系。协方差的大小将难以理解,因为它不是一个归一化的结果。另一方面,相关系数将能够提供关系强度的量度。相关系数可以通过将两个变量的协方差除以标准差的乘积来计算。
最常用的相关系数是皮尔逊相关系数,它只考虑两个变量之间的线性关系。另一个广泛使用的系数是斯皮尔曼秩相关系数,它对非线性关系更敏感。我们可以如图 A.4 所示可视化相关关系。
相关性并不意味着因果关系。这是分析过程中最常见的错误。例如,考虑以下陈述:“鞋的销售量有所增加,同时溺水死亡率有所下降”。如果做出的推断是鞋销售量的增加导致溺水死亡率的下降,那么结果是完全不合逻辑的。这证明了相关性不意味着因果关系。

图 A.4 在第一种情况下,两个变量之间存在正相关。在第二种情况下,两个变量之间存在负相关。在第三种情况下,两个变量之间没有观察到关系。
指标用于测试数据集中两个变量之间是否存在任何关系。这个概念在数据科学中经常被使用和引用。我们分析关系的强度并决定是否存在逻辑趋势。
A.9.4 矩阵分解、特征向量和特征值
有时候在线性代数中,我们希望将一个矩阵分解为矩阵的乘积;这个过程称为矩阵分解。如果我们想要将矩阵表示为矩阵的乘积,我们会使用矩阵分解方法。
特征向量和特征值是矩阵分解的组成部分。如果我们有一个方阵A,那么理解如图 A.5 所示方程 A.5。
(A.5)
A*v = *λ**v
其中v是特征向量,λ是特征值。
例如,假设我们有一个矩阵,如图 A.5 所示,我们想要得到特征向量。在这里,-2 是特征值,[1 -2 1]是特征向量。特征向量是一个在变换过程中不改变方向的非零向量。它只将原始矩阵按因子λ进行缩放。特征向量和特征值被用于主成分分析(PCA)的实现。

图 A.5 寻找特征向量和特征值
A.9.5 特殊矩阵
我们接下来定义几个特殊的矩阵。
一个对角矩阵的所有非对角元素都是零,如图 A.6 所示。

图 A.6 对角矩阵的例子
一个正交矩阵是一个满足以下条件的方阵,如图 A.6 所示方程 A.6。
(A.6)
Q^(T)Q = Q**Q^(T) = I
其中Q是原始矩阵,Q^(T)是其转置,I是单位矩阵,如图 A.7 所示。

图 A.7 正交矩阵的例子
一个矩阵如果是其转置等于自身的(即,Q^(T) = Q),则称为对称矩阵。
A.10 超参数与参数的比较
参数是模型从机器学习模型的训练中学习的内部值。例如,它们是回归模型中的系数或在神经网络中的权重/偏差。它们在机器学习模型的训练过程中自动设置。
另一方面,超参数在训练开始前就预先定义,并控制机器学习模型。例如,k-means 聚类中的聚类数量(k)或使用的距离度量。它们是手动选择的,可以使用各种技术如网格搜索 CV 或随机搜索 CV 进行优化。
索引
符号
<强调>计算网络科学<默认段落字体>(Hexmoor)
A
亲和矩阵
基于自编码器的聚类
亲和传播
自编码器, 2nd
应用
组件
特征学习
实际下一步行动和建议阅读材料
类型
关联规则, 2nd
Apriori 算法, 2nd
的构建块
案例研究, 2nd
等价类聚类和自下而上的网格遍历, 2nd
实际下一步行动和建议阅读材料
技术工具包
邻接矩阵
Apriori 算法
的挑战
AI(人工智能)
先行词
异常检测
所有记录列表
A/B 测试
ACO-CLUSTER(蚁群优化聚类)
ATV(平均交易价值)
准确性监控
AGCT(层次聚类)
AUTOCLASS(基于贝叶斯模型的聚类)
B
BIRCH(使用层次结构的平衡迭代减少和聚类)
贝叶斯高斯混合模型
二元组
二进制数据
业务问题定义, 2nd
批量部署
反向传播
背后的数学
瓶颈
业务利益相关者和主题专家
边界点
Bisecting k-means
双变量分析
C
基于约束的聚类
类变量
持续部署和集成
CNNs (卷积神经网络), 第 2 次
关键概念
使用
中心极限定理
相关性
系数
重要考虑事项
用法
定制
核心要点, 第 2 次
Chameleon
聚类算法
聚类技术
案例研究, 第 2 次
基于连接性的聚类, 第 2 次
实际下一步行动和建议阅读材料
压缩自编码器
连续数据
CLIQUE (Quest 中的聚类)
基于质心的聚类
测量聚类准确度
上下文感知
对比散度算法
中国 whispers 聚类
CBOW (连续词袋)
约束 DBSCAN
Calinski-Haranasz 指数
代码大小
质心
协方差
分类变量
CLARA (聚类大应用)
ChatGPT
应用和关键特性
CURE (使用代表的聚类)
成本函数
C-DBSCAN (基于约束的 DBSCAN)
条件概率
金丝雀部署
余弦距离
CLARANS (基于随机搜索的聚类大应用)
维度诅咒, 第 2 次
置信度
概述
切比雪夫距离
聚类, 第 2 次
基于质心的
面临的挑战
实际下一步行动和建议阅读材料
技术工具包, 第 2 版
技术
D
药物发现
数据清洗
DBSCAN(带噪声的基于密度的空间聚类应用), 第 2 版
Python 解决方案
细节
优缺点
步骤
数据艺术家
基于密度的
算法
聚类
DBCLASD(基于分布的大空间数据库聚类)
DBEM(基于密度的 EM)
数据编码
DevOps 团队
判别模型, 第 2 版
数据帧
数据科学团队
DENCLUE(基于密度的聚类)
深度学习, 第 2 版
玻尔兹曼学习规则, 第 2 版
反向传播, 第 2 版
神经网络
实际下一步行动和建议阅读材料
去噪自编码器
数据预处理
数据压缩
Dunn 指数
降维, 第 2 版, 第 3 版
案例研究
案例研究, 第 2 版
维度诅咒, 第 2 版
数学基础
方法
实际下一步行动和建议阅读材料, 第 2 版
优缺点
技术工具包, 第 2 版
数据发现阶段
划分聚类
离散数据
维度
数据
数据工程与管理
数据质量, 第 2 版
定义
类型, 第 2 版
DEC(深度嵌入聚类)
DBN(深度信念网络),第 2 版
关键点
数据工程团队
解码器
度矩阵
对角矩阵
DeepCluster(深度学习聚类)
距离测量
密度可达
E
EDA(探索性数据分析),第 2 版,第 3 版
边介数聚类
欧几里得距离,第 2 版
肘部方法
ENCLUS(基于熵的子空间聚类)
ETL(导出、转换、加载)过程
编码器
EM(期望最大化)算法,第 2 版
边缘部署
EAC(集成层次聚类)
F
FCM-SC(带空间约束的模糊 C 均值)
模糊 C 均值(FCM)
模糊聚类,第 2 版
类型,第 2 版
基于频率的词去除
滤波方法
最远邻点
模糊子空间聚类
模糊 SOM(自组织映射)
FSSC(快速子空间聚类)
FCM-GA(带遗传算法的模糊 C 均值)
FIRES(频繁项集聚类)
for 循环,第 2 版
带必须链接和不能链接约束的 FCM
F-P 算法,第 2 版
模糊最小-最大聚类
FCM(模糊 c 均值)算法,第 2 版
fit_transform 方法
G
遗传聚类
基于图的聚类
基于网格的算法
groupby
get_dummies()方法
GRIDCLUS(基于网格的聚类算法)
组平均链接
GA-k-means(遗传算法与 k-means)
增长神经气
生成网络
GenAI (生成式人工智能), 2nd, 3rd, 4th
判别模型和
整合
GTM (生成式拓扑映射)
GCUK (无监督 k-means 遗传聚类)
贪婪方法
GPT-3 (生成式预训练变换器 3)
Gath-Geva 算法
GANs (生成对抗网络), 2nd, 3rd, 4th
对抗训练
实际下一步和推荐阅读
变体和应用
Girvan-Newman 聚类
高斯分布
GMM (高斯混合模型), 2nd, 3rd
EM 技术
GK (Gaustafson-Kessel) 算法, 2nd
G-Means (高斯均值)
H
带约束的硬 k-means
HDBSCAN (层次 DBSCAN)
层次聚类
最佳聚类数量
优缺点
类型
高光谱图像
硬聚类, 2nd
高维谱聚类
HIERDENC (基于密度的层次聚类)
HMM (隐马尔可夫模型) 聚类
HAC-S (带空间约束的层次聚合聚类)
混合 BIRCH-k-means
休斯现象
隐藏层
I
if/else 块
簇内平方和
输入层, 2nd
惯性
Infomap 算法
IQR (四分位数范围)
J
垃圾或不需要的字符
K
KPIs (关键绩效指标)
Keras, 2nd
KNN (k 近邻)
k-means 聚类, 2nd
寻找
优缺点, 第 2 版
k 均值算法
K-medoids (PAM)
k 中值聚类
Kaiser 标准
知识表示
k-medoids 聚类
keras 库
K-Modes
KL (Kullback-Liebler)散度, 第 2 版
K-Prototypes
L
LSTM (长短期记忆)
LLMs (大型语言模型)
拉普拉斯矩阵
基于层的预训练
词典归一化
lift
概述, 第 2 版
LabelEncoder
LAC (局部自适应聚类)
基于库的清理
潜在变量
库,深度学习, 第 2 版
LDBSCAN (标记 DBSCAN)
引理,定义
最佳拟合线, 第 2 版
损失函数
社区检测的 Louvain 方法
链接标准
聚类用的拉普拉斯特征图
M
市场篮子分析
MCL (马尔可夫聚类)
马尔可夫假设
MinMaxScalar()函数
MCLUST (基于 EM 的模型聚类)
模型部署
数据中的重复值
端到端, 第 2 版, 第 3 版, 第 4 版, 第 5 版, 第 6 版, 第 7 版
类型
MEPSO (用于聚类的多精英粒子群优化)
模型漂移
Mixmod (聚类混合模型)
mpl_toolkits 库
MDS (多维度缩放), 第 2 版
Python 实现, 第 2 版
经典
非度量
matplotlib 库, 2nd
模型维护和刷新
MXNet
多路谱聚类
基于模型的算法
降维的手动方法, 2nd
基于算法的降维方法
相关系数
手动特征选择
数学
L1 范数与 L2 范数
数据表示, 2nd
成员资格
模型开发和业务审批
机器学习(ML)
人工智能和商业智能
算法, 2nd
数据, 2nd
概述, 2nd
过程
技术工具包
均值漂移
矩阵
分解
曼哈顿距离
make_circles 方法
多语言能力
小批量 k-means
最小-最大缩放
N
有噪声的数据集
每层的节点数
邻域
NSGA-II(用于聚类的非支配排序遗传算法 II)
自然语言理解
nltk 库, 2nd, 3rd, 4th
神经网络
激活函数, 2nd
构建块
解决方案
超参数
层, 2nd
优化函数, 2nd
归一化切割聚类
最近邻
n-gram 模型
名义数据
无相关性
基于神经网络的聚类
神经气
NaN(非数字)
numpy 库, 第 2 版, 第 3 版, 第 4 版
负相关
NLP (自然语言处理), 第 2 版
O
正交矩阵
过拟合
独热编码
优化
序数数据
OPTICS (有序点聚类结构识别)
优化函数
自适应优化算法
学习和学习率
目标函数
ORCLUS (正交子空间聚类)
OptiGrid
运营团队
输出层, 第 2 版
异常值, 第 2 版
P
正相关
PyTorch
P3C (基于模式的子空间聚类)
PCA (主成分分析), 第 2 版, 第 3 版, 第 4 版, 第 5 版, 第 6 版
使用 Python 解决方案, 第 2 版
特征值分解
帕维亚大学数据集
参数, 第 2 版
困惑度
主成分
PCM (可能性 C 均值)
池化层
PCKMeans (成对约束 k 均值)
pyspade 库
Python
Apriori 算法, 第 2 版
FCM (模糊 c 均值)算法
等价类聚类和自下而上格遍历
使用分层聚类案例研究
实现 GMM
实现自动编码器
安装包
使用 k 均值聚类实现, 第 2 版
主轴
基于划分的算法
平面图
感知器, 第 2 版
准备,数据清洗
Q
定性数据
定量数据
R
ROCK (Robust Clustering using Links)
正则表达式标记化
ReLU (修正线性单元), 2nd
正则表达式 (Regex)
RNNs (循环神经网络), 2nd
关键概念
random_state 参数
强化学习
比率切分聚类
RGB (红,绿,蓝)
可重现性
RBM (受限玻尔兹曼机)
实时部署
S
SVD (奇异值分解)
使用 Python 解决方案, 2nd
监督微调
相似性图
序列规则挖掘
SNE (随机邻域嵌入)
SimpleImputer 方法
SGD (随机梯度下降), 2nd
结构化数据集
谱间隙
安全和合规性
形状命令
轮廓值
拼花
主观解释
谱聚类, 2nd
Python 实现
构建模块
过程, 2nd
半监督 k-means
特殊矩阵
缩放技术
SPADE (使用等价类的顺序模式发现) 算法, 2nd
支持, 2nd
概述
softmax 函数
SNN (共享最近邻聚类)
Shi-Malik 聚类
标准化
可扩展性
半监督学习
软聚类
重要变量
SCPS (Perona & Shi 图上的谱聚类)
十四行诗
随机梯度下降
停用词去除
监督学习
添加损失函数
算法
计算误差
分类算法, 第 2 版
深度学习, 第 2 版
前馈传播
回归算法, 第 2 版
稀疏自编码器
SUBCLU (子空间聚类), 第 2 版
Sigmoid 函数, 第 2 版
子空间和高维聚类
SPICi (聚类速度和性能)
sklearn 库, 第 2 版, 第 3 版, 第 4 版, 第 5 版
T
分词
文本数据
GenAI 用于
预处理
无监督学习, 第 2 版, 第 3 版
工具包
耗时
tanh 函数
技术工具包
tSNE_first_component
训练
自编码器
时间复杂度
数据过多
TF (TensorFlow)
Python 代码
t-SNE (t-分布随机邻域嵌入), 第 2 版
柯西分布
Python 实现, 第 2 版
时间序列分析
TF-IDF (词频-逆文档频率)
U
无监督学习, 第 2 版, 第 3 版
文本数据挑战
深度学习
从文本数据集中提取特征
用于文本数据, 第 2 次, 第 3 次, 第 4 次, 第 5 次, 第 6 次, 第 7 次, 第 8 次
语言模型
技术工具包
使用 Python 进行文本聚类
文本数据, 第 2 次, 第 3 次
文本数据的用例
UMAP (均匀流形近似与投影), 第 2 次
...的关键点
与...一起工作
UI/可视化团队
用户反馈
单变量分析
unigrams
欠完备自编码器
V
版本控制
VDBSCAN (基于变量密度的空间聚类)
变分自编码器
向量
方差
W
Walktrap 算法
WaveCluster
包装方法
WCSS (簇内平方和)
词嵌入
Word2Vec
Ward 链接方法
权重
X
X 变量
X-Means (扩展 k-means)


浙公网安备 33010602011771号