谷歌机器学习和生成式人工智能的解决方案架构-二-

78 阅读1小时+

谷歌机器学习和生成式人工智能的解决方案架构(二)

原文:annas-archive.org/md5/90c02ac66d0c0f03912aac73223f0fbf

译者:飞龙

协议:CC BY-NC-SA 4.0

第六章:深入探讨 – 在 Google Cloud 上为 AI/ML 工作负载准备和处理数据

在上一章中,我们通过查看与我们的数据集相关的一些细节,使用诸如pandas.DataFrame.info()pandas.DataFrame.head()等函数,进行了一些非常基础的数据探索。在本章中,我们将更深入地探讨数据探索和准备领域,这在本章中由数据科学生命周期图中用蓝色突出显示的部分所代表。1*:

图 6.1:数据探索和处理

图 6.1:数据探索和处理

在典型数据科学项目的早期阶段,您可能会在 Jupyter 笔记本中执行许多数据探索和准备步骤,正如我们所看到的,这对于实验小数据集是有用的。然而,当您将工作负载投入生产时,您可能会使用更大的数据集,在这种情况下,您通常会需要使用不同的工具来处理您的数据。在大型规模数据处理、分析和 AI/ML 方面,Google 被视为行业领导者。例如,Google Cloud BigQuery 已成为行业中最受欢迎的数据仓库服务之一,Google Cloud 还拥有许多其他行业领先的数据处理和分析工作负载服务。

在本章中,我们将学习如何使用 Vertex AI、BigQuery、Dataproc 和 Cloud Composer 等工具探索、可视化和准备数据以供 ML 用例使用。此外,我们将深入了解实时处理流数据的 Dataflow,并介绍数据管道的基本原理。到本章结束时,您将能够创建、构建和运行 Google Cloud 上的数据管道,为您在当今快节奏、数据驱动的世界中承担复杂数据处理任务提供必要的技能。

本章涵盖了以下主题:

  • 先决条件和基本概念

  • 将数据导入 Google Cloud

  • 探索和可视化数据

  • 清洗和准备数据以供 ML 工作负载使用

  • 数据管道简介

  • 处理批量和流数据

  • 在 Google Cloud 上构建和运行数据管道

让我们先来讨论本章的先决条件。

本章的先决条件

在我们可以开始执行本章的主要活动之前,本节中的活动需要完成。

启用 API

除了我们在前几章中讨论的用于启用 Google Cloud API 的方法,例如 Google Cloud Shell 或 Google Cloud 控制台中被提示,您还可以主动搜索一个 API 以在控制台中启用它。为此,您需要执行以下步骤,其中*[服务/API 名称]*是您希望启用的服务/API 的名称:

  1. 在 Google Cloud 控制台中,导航到Google Cloud 服务菜单 → APIs & ServicesLibrary

  2. 在搜索框中搜索*[服务名称]*。

  3. 从结果列表中选择 API。

  4. 在显示 API 信息的页面上,点击启用

为以下每个服务/API 名称执行前面的步骤:

  • Compute Engine API

  • Cloud Scheduler API

  • Dataflow API

  • 数据管道 API

  • Cloud Dataproc API

  • Cloud Composer API

  • Cloud Pub/Sub API

在您启用所需的 API 之后,我们就可以继续下一节了。

IAM 权限

在本节中,我们将设置启用本章后续活动所需的身份和访问管理IAM)权限。

服务帐户

在前面的章节中,我提到过有多种方式可以与 Google Cloud 服务进行身份验证,您在第四章中使用的 API 密钥是最简单的身份验证方法。现在,随着我们向更复杂的使用案例迈进,我们将开始使用一种更高级的身份验证形式,称为服务帐户。

Google Cloud 服务帐户是 Google Cloud 服务、应用程序和虚拟机VM)用于与其他 Google Cloud 资源交互和验证的特殊帐户。它们是一个有趣的概念,因为除了是资源外,它们还被认为是有身份或主体,就像人一样,就像人一样,它们有自己的电子邮件地址和与它们相关的权限。然而,这些电子邮件地址和权限适用于使用它们的机器和应用程序,而不是人。服务帐户提供了一种方式,让机器和应用程序在系统想要执行需要通过 Google Cloud API 和资源进行身份验证的活动时拥有身份。

我们将创建一个服务帐户,用于在本章中执行的活动所需的权限。

数据处理服务帐户

考虑到在本章中我们将使用多个 Google Cloud 服务以不同的方式处理数据,我们将创建一个具有主要与 Google Cloud 数据处理服务相关的权限的服务帐户,例如 BigQuery、Cloud Composer、Dataflow、Dataproc、Google Cloud StorageGCS)和 Pub/Sub。

执行以下步骤以创建所需的服务帐户:

  1. 在 Google Cloud 控制台中,导航到Google Cloud 服务菜单→ IAM & Admin服务帐户

  2. 选择创建 服务帐户

  3. 对于服务帐户名称,输入data-processing-sa

  4. 在标题为授予此服务帐户访问项目的章节中,添加*图 6**.2 中显示的角色:

图 6.2:Dataflow 工作服务帐户权限

图 6.2:Dataflow 工作服务帐户权限

  1. 选择完成

我们的服务帐户现在已准备好在本章的后续部分使用。

注意

在我们刚刚创建的服务账户中,我们还添加了服务账户用户权限。这是因为,在我们将要实施的某些用例中,我们的服务账户也需要暂时代表或“充当”其他服务账户或身份。有关代表服务账户的概念的更多信息,请参阅以下文档:cloud.google.com/iam/docs/service-account-permissions#directly-impersonate

云存储桶文件夹

我们将使用云存储桶来存储本章后面活动所需的数据。我们已经在第四章中创建了一个桶,因此我们可以简单地向桶中添加一些文件夹来存储我们的数据。执行以下步骤以创建文件夹:

  1. 在 Google Cloud 控制台中,导航到Google Cloud 服务菜单→云存储

  2. 点击第四章中创建的桶的名称。

  3. 选择创建文件夹

  4. 将其命名为data

  5. 选择创建

重复前面的步骤,使用以下额外的文件夹名称:

  • code

  • dataflow

  • pyspark-airbnb

我们的数据文件夹现在已准备好存储数据。

上传数据

我们将使用名为AB_NYC_2019.csv的文件作为本章某些活动的数据集。在第四章中您在本地机器上创建的 GitHub 仓库克隆副本中,您将在名为data的目录中找到该文件,该目录位于名为Chapter-06的目录内。

因此,您应该在您的本地机器上找到以下路径处的文件(如果您使用的是 Microsoft Windows,斜杠将被反转):

[您克隆我们的 GitHub 仓库的位置]/``Chapter-06``/data/AB_NYC_2019.csv

为了上传此文件,请执行以下步骤:

  1. 导航到上一节中在 GCS 中创建的data文件夹。

  2. 点击上传文件

  3. 现在,通过导航到您在本地机器上创建的 GitHub 仓库克隆副本中的Chapter-06/data目录,选择AB_NYC_2019.csv文件。

现在我们已经完成了先决条件,让我们讨论一些重要的行业概念,我们将在本章中深入探讨。

本章的基本概念

在本书中,我们的目标是为您提供有关如何使用相关 Google Cloud 服务来处理各种工作负载的知识,同时也提供与每个相关技术相关的重要行业概念。在本节中,我们简要介绍了为本章的学习活动提供额外背景的概念。

将数据摄入 Google Cloud

在前面的章节中,您已经执行了上传数据到 GCS 和谷歌云大数据查询(BigQuery)的步骤。除了对 GCS 和 BigQuery 执行批量上传外,还可以将这些服务中的数据流式传输。您将在本章中看到这一过程的具体操作。

本节提供了对谷歌云数据摄取选项的更全面概述。在这里,我们只涵盖谷歌云的服务,但您还可以在谷歌云上运行无数第三方数据库和数据管理服务,例如 MongoDB、Cassandra、Neo4j 以及通过谷歌云市场提供的许多其他服务。

对于流式数据处理用例,我们将在本章后面更详细地描述,您可以使用 Cloud Pub/Sub 从物联网设备或网站点击流源摄取数据。Dataflow 也可以用于从各种来源摄取数据,对其进行转换,并将其写入其他谷歌云服务,如 BigQuery、Bigtable 或云存储。正如我们将在本章后面讨论的,Dataflow 还支持批量数据处理用例。

谷歌云 Dataproc 可用于从兼容Hadoop 分布式文件系统HDFS)的源或其他分布式存储系统摄取数据,谷歌云数据融合也可以用于从各种来源摄取数据,对其进行转换,并将其写入大多数谷歌云存储和数据库服务。

对于关系型数据库数据,您可以使用谷歌云数据库迁移服务DMS)将数据摄取到谷歌云 SQL 中,这是一个为 MySQL、PostgreSQL 和 SQL Server 提供的完全托管的关系型数据库服务。您还可以使用标准 SQL 客户端、导入/导出工具和第三方数据库迁移工具将数据摄取到云 SQL 实例中。

对于非关系型数据库数据,有多种选择,包括以下内容:

  • 谷歌云 Bigtable,这是一个为大规模、低延迟工作负载提供的完全托管、可扩展的 NoSQL 数据库。您可以使用 Bigtable HBase API 或 Cloud Bigtable 客户端库将数据摄取到 Bigtable 中。

  • 谷歌云 Firestore,这是一个为 Web 和移动应用提供的完全托管、无服务器的 NoSQL 文档数据库。Firestore 提供了客户端库、REST API 或 gRPC API 来摄取数据。

  • 一些您可能也熟悉谷歌云数据存储(Google Cloud Datastore),它曾经是独立的谷歌云非关系型数据库服务,但已经与谷歌云 Firestore 有所合并。要了解更多关于这些数据库选项之间如何相互关联的详细信息,请参阅以下链接的谷歌云文档:cloud.google.com/datastore/docs/firestore-or-datastore

现在我们已经介绍了将数据导入 Google Cloud 的许多选项,让我们讨论在导入数据后我们可能想要对数据进行哪些操作,从数据转换方法开始,例如提取、转换、加载ETL)和提取、加载、转换ELT)。

ETL 和 ELT

ETL 和 ELT 是数据集成、处理和存储的两种方法。在 ETL 中,数据首先从源系统中提取出来,然后转换成另一种格式。这些转换可能包括清洗、丰富、聚合或去重。这些转换通常在一个中间处理引擎或暂存区域中进行。一旦数据被转换,它就被加载到目标系统中,这通常是数据仓库或数据湖。ETL 是一种传统方法,非常适合数据一致性和质量至关重要的环境。然而,由于在数据加载之前发生处理步骤,它可能既耗时又消耗资源。

另一方面,ELT 颠倒了最后两个步骤的顺序。数据首先从源系统中提取出来,然后直接加载到目标系统中,例如现代数据仓库或数据湖。转换步骤在目标系统内部执行,使用目标系统的处理能力。近年来,由于现代基于云的数据仓库的可扩展性和处理能力的提高,ELT 因其能够更快地加载并允许用户按需执行复杂转换而越来越受欢迎。

在 ETL 和 ELT 之间进行选择取决于数据处理环境的具体要求、数据质量需求以及目标系统的能力。

批量和流数据处理

从高层次来看,我们可以以两种主要方式处理数据:批量处理和流处理。在本节中,我们将解释这些方法,这将为我们提供本章后续活动中所需的知识。

批量数据处理

批量数据处理通常指的是以批量方式对大量数据集进行一系列转换。这类管道适用于需要在一小时或甚至几天内并行处理大量数据的用例。例如,想象一下您的公司在不同的地理区域运行在线零售业务,例如在北美有一个*www.example.com*网站,在英国有一个*www.example.co.uk*网站,在中国有一个*www.example.cn*网站。每个晚上,您可能希望运行一个工作负载,该工作负载将当天每个区域所有客户购买的所有商品汇总起来,将数据跨区域合并,然后将这些数据输入到机器学习模型中。这将是批量工作负载的一个例子。其他批量数据处理用例的例子包括以下内容:

  • 处理每日销售交易以生成业务决策报告

  • 分析来自 Web 服务器的日志文件以了解特定时间窗口内的用户行为(例如,一天或一周)

  • 运行大规模数据转换作业,例如将原始数据转换为下游数据系统所需的结构化格式

除了需要定期执行的数据处理任务之外,公司可能还需要实时或接近实时地处理数据,这使我们来到了流数据处理的主题。

流数据处理

流数据处理,正如其名所示,涉及对持续流入系统的数据流进行工作,通常是在持续的基础上。而不是将庞大的数据集作为一个单独的作业进行处理,流数据处理用例中的数据通常由在飞行中的小数据块组成。流数据处理的例子包括以下内容:

  • 分析社交媒体流以识别趋势或检测事件(例如,情感分析(SA),标签跟踪)

  • 实时监控和分析物联网传感器数据以检测异常、触发警报或优化流程

  • 实时处理金融交易以进行欺诈检测和预防

现在我们已经讨论了数据处理的两个主要高级用例类别,让我们开始深入了解我们如何实际实现这些用例。

数据管道

数据管道是我们自动化的概念,例如大规模 ETL/ELT 或流数据转换。对于处理大量数据或需要复杂数据处理工作负载的组织来说,数据管道是必不可少的,因为它们有助于在规模上简化数据管理。如果没有这样的自动化管道,员工将花费大量时间进行枯燥且重复但复杂且易出错的数据处理活动。Google Cloud 提供了多种服务,可用于批处理和流数据管道,你将在本章中学习如何使用这些服务。

现在我们已经介绍了一些基本概念,是时候开始深入了解一些实际的数据处理活动了。

然而,在我们开始构建自动化的数据处理管道之前,我们首先需要探索我们的数据,以便我们了解我们希望在管道中实施哪些类型的转换。

注意

在之前的章节中,我们将代码直接包含在本书的页面中,但现在我们正在转向更复杂的使用案例,这些使用案例需要大量不适合直接包含在本书页面中的代码。请查阅 GitHub 仓库中与本章节相关的代码工件,以了解我们用于实现这些步骤的代码:github.com/PacktPublishing/Google-Machine-Learning-for-Solutions-Architects

在本书的其余部分,我将继续在适当的地方直接包含代码。

探索、可视化和准备数据

用例:我们计划去纽约市旅行,并想了解最佳的住宿选项。我们不会逐个浏览和评估大量的 Airbnb 帖子,而是会下载大量的评论,并进行批量数据分析和数据处理,以获得一些见解。

我们可以使用在第五章中创建的 Vertex AI Workbench 笔记本来完成这个目的。请打开该笔记本实例上的 JupyterLab。在屏幕左侧的目录浏览器中,导航到Chapter-6目录并打开Chapter-6-Airbnb.ipynb笔记本。您可以选择**Python (Local)**作为内核。正如您在第五章中所做的那样,通过选择单元格并在键盘上按Shift + Enter来运行笔记本中的每个单元格。

在笔记本中,我们使用 Markdown 单元格详细描述每个步骤,以便您理解过程中的每个步骤。我们使用pandasmatplotlibseaborn等库来总结和可视化数据集的内容,然后我们执行数据清理和准备活动,例如填充缺失值、移除异常值以及移除对训练预测住宿价格的回归模型可能不实用的特征。图 6*.3*展示了我们数据可视化图表的一个示例,其中我们查看数据集中列表价格的范围和分布:

图 6.3:数据集中价格分布

图 6.3:数据集中价格分布

如我们所见,大多数住宿选项每晚的费用低于 200 美元,但也有一些数据点(尽管不多)每晚在 600 至 1000 美元之间。这些可能是非常昂贵的住宿选项,或者它们可能是数据中的潜在异常值/错误。您可以在笔记本中查看更多的数据可视化图表。

关于数据清理活动,例如,为了清理潜在的价格异常值,我们使用以下代码片段来设置 800 美元的限制(尽管仍然很高)并移除任何超过该每晚价格的列表:

price_range = (data_cleaned['price'] >= 10) & (
    data_cleaned['price'] <= 800)
data_cleaned = data_cleaned.loc[price_range]

为了移除对训练预测住宿价格的回归模型可能不实用的特征,我们使用以下代码片段:

columns_to_drop = ['id', 'name', 'host_name', 'last_review', 
    'reviews_per_month']
data_cleaned = data.drop(columns=columns_to_drop)

这些只是我们在笔记本中执行的数据准备步骤的几个示例。

当您完成笔记本中所有活动的执行后,我们将继续探讨如何将这些活动转化为生产环境中的自动化流程。我们将首先实现批量数据处理流程。

批量数据处理流程

现在我们已经使用我们的 Jupyter 笔记本来探索我们的数据,并确定我们想在数据集上执行哪些类型的转换,让我们设想一下,我们想要将这个转换成一个可以自动在非常大的文件上运行的生产工作负载,而不需要任何进一步的人工努力。正如我之前提到的,这对于任何实施大规模数据分析和 AI/ML 工作负载的公司来说都是至关重要的。让某个人每次都手动执行这些转换是不切实际的,而对于非常大的数据量,这些转换无法在笔记本实例上执行。例如,想象一下,我们每天都会收到数千条新的帖子,我们想要自动为 ML 模型准备这些数据。我们可以通过创建一个自动化的管道来每晚(或我们希望的时间间隔)执行数据转换来实现这一点。

批量数据处理管道的概念和工具

在我们开始深入构建我们的批量数据处理管道之前,让我们首先介绍这个领域的一些重要概念和工具。

Apache Spark

Apache Spark 是一个非常流行的开发和执行框架,可以用于实现非常大的数据处理工作负载以及其他类型的大规模计算用例,如 ML。它的力量既在于其内存处理能力,也在于其能够并行实现多个大型计算和数据处理任务的能力。

虽然 Spark 可以用于批处理和流式(或微批处理)数据处理工作负载,但我们在本章中将使用它来执行我们的批量数据转换。

Google Cloud Dataproc

正如我们在 第三章 中讨论的那样,Google Cloud Dataproc 是一个完全托管、快速且易于使用的服务,可以在 GCP 上运行 Apache Spark 和 Apache Hadoop 集群。在本章中,我们将使用它来执行我们的 Spark 处理作业。

Apache Airflow

Apache Airflow 是一个开源平台,用于编排复杂的数据工作流。它由 Airbnb 创建,后来贡献给了 Apache 软件基金会ASF)。Airflow 设计用于帮助开发人员和数据工程师创建、安排、监控和管理工作流,使得处理相互依赖的任务变得更加容易。它通常用于数据工程和数据科学项目中的任务,如 ETL、ML 管道和数据分析,并且被各个行业的组织广泛使用,使其成为管理复杂数据工作流的热门选择。

有向无环图

Airflow 将工作流表示为 有向无环图DAGs),它由任务及其依赖关系组成。工作流中的每个任务都表示为一个节点,任务之间的依赖关系由有向边表示。这种结构确保了任务按照特定的顺序执行,不会创建循环,如图 6*.4* 所示:

图 6.4:一个简单的 DAG(来源:https://www.flickr.com/photos/dullhunk/4647369097)

图 6.4:一个简单的 DAG(来源:www.flickr.com/photos/dull…

图 6.4中,我们可以看到任务bcde都依赖于任务a。同样,任务d也依赖于任务bc,任务e也依赖于任务cd

在 Airflow 中,DAG 通过 Python 脚本定义,它将任务及其依赖关系表示为代码。

Google Cloud Composer

Google Cloud ComposerGCC)是基于 Apache Airflow 构建的完全托管的工作流程编排服务。它允许您在多个 Google Cloud 服务、本地或多云环境中创建、安排和监控数据工作流。

云作曲家通过提供易于使用的界面并自动化基础设施管理来简化设置和管理 Apache Airflow 的过程。这允许您专注于创建和维护您的流程,而 Google 则负责底层基础设施、扩展和更新。

现在我们已经涵盖了实现批量数据管道的重要概念,让我们开始构建我们的管道。

构建我们的批量数据管道

在本节中,我们将创建我们的 Spark 作业并在 Google Cloud Dataproc 上运行它,并将使用 GCC 来编排我们的作业。这意味着我们可以让 GCC 每天自动运行我们的作业。每次运行作业时,它将创建一个 Dataproc 集群,执行我们的 Spark 作业,然后在我们的作业完成后删除 Dataproc 集群。这是公司用来节省资金的标准最佳实践,因为当你不使用计算资源时,你不应该有它们在运行。我们 Google Cloud 上的管道架构在图 6.5中显示:

图 6.5:批量数据管道架构

图 6.5:批量数据管道架构

让我们从设置云作曲家开始。

云作曲家

在本节中,我们将设置云作曲家以安排和运行我们的批量数据处理管道。

云作曲家环境

在云作曲家中,我们做的所有事情都在云作曲家环境中进行。要设置我们的云作曲家环境,请执行以下步骤:

  1. 在 Google Cloud 控制台中,导航到Google Cloud 服务菜单→作曲家环境

  2. 选择创建环境

  3. 如果需要,选择作曲家 2

  4. 在出现的屏幕上,输入您的 Composer 环境名称。参见图 6.6以获取参考:

图 6.6:创建 Composer 环境

图 6.6:创建 Composer 环境

  1. 选择您首选的区域。(记住,如果可能的话,在整个本书中为每个活动使用相同的区域会更好。)

  2. 选择最新的镜像版本。参见图 6.6以获取参考。

  3. 重要提示:选择您在本章中创建的服务帐户(如果您使用了建议的名称,那么名称中将包含 data-processing-sa)。

  4. 环境资源部分,选择一个小型环境。

  5. 将所有其他选项保留在默认值并选择创建

  6. 环境启动可能需要 25 分钟。

在等待环境启动的同时,让我们继续下一节。

Cloud Composer Python 代码

在本节中,我们将审查和准备用于我们的 Cloud Composer Spark 工作负载的 Python 代码。我们将使用两个代码资源与 Cloud Composer 一起,这些资源可以在我们的 GitHub 仓库中找到(github.com/PacktPublishing/Google-Machine-Learning-for-Solutions-Architects/tree/main/Chapter-06):

  • composer-dag.py,其中包含定义我们的 Cloud Composer Airflow DAG 的 Python 代码

  • chapter-6-pyspark.py,其中包含定义我们的 Spark 作业的 PySpark 代码

执行以下步骤以开始准备这些文件用于与 Cloud Composer 一起使用:

  1. 在您在本地机器上创建的我们的 GitHub 仓库的克隆中找到这些文件,并打开它们进行编辑。

  2. chapter-6-pyspark.py 文件中,您只需更新源和目标数据集的存储位置。为此,在文件中搜索 GCS-BUCKET-NAME 字符串,并将其替换为您之前创建的自己的 GCS 存储桶名称。

重要

GCS-BUCKET-NAME 字符串在文件中有两个位置(一次在开头附近,一次在结尾附近)。一个位置指定源数据集,另一个位置指定 Spark 作业将保存处理后的数据的目标位置。将字符串的所有出现替换为您自己的 GCS 存储桶名称。

  1. composer-dag.py 文件中,您将在文件开头附近看到以下变量块:

    PROJECT_ID = "YOUR PROJECT ID"
    REGION = "us-central1"
    ZONE = "us-central1-a"
    SERVICE_ACCOUNT_EMAIL = "data-processing-sa@YOUR-PROJECT-ID.iam.gserviceaccount.com"
    PYSPARK_URI = "gs://GCS-BUCKET-NAME/code/chapter-6-pyspark.py"
    

    所有这些变量都需要使用您 GCP 项目中的特定值进行更新。文件中的注释提供了关于替换的额外细节。

  2. 除了做出上述更改外,请审查代码内容,以了解 Cloud Composer 将如何执行我们的作业。代码中包含注释,描述了每个部分正在做什么,以便您了解其工作原理。

  3. 当您完成前面的步骤后,我们就可以上传 Cloud Composer 将要使用的资源了。

  4. Cloud Composer 需要将前面的代码资源存储在 GCS 中,但需要在两个不同的位置:

    • 对于chapter-6-pyspark.py:将此文件上传到您之前在 GCS 中创建的code文件夹中。为此,导航到您创建的code文件夹,选择chapter-6-pyspark.pycomposer-dag.py:此文件将被上传到 Cloud Composer 为您创建的特定文件夹。当您的 Cloud Composer 环境完全创建后,在 Cloud Composer 控制台中点击您新创建的环境名称,您的环境详细信息屏幕将打开。在屏幕顶部附近,从您在本地机器上创建的 GitHub 仓库克隆中(即您在之前步骤中编辑的文件)选择composer-dag.py文件。

就这样!一旦composer-dag.py文件上传完毕,Cloud Composer 将为您完全自动化一切。这可能需要几分钟,但 Cloud Composer 将创建您的 DAG,您将在 Cloud Composer 控制台中看到它出现。然后它将执行 DAG,这意味着它将创建一个 Dataproc 集群,执行 Spark 作业以执行我们指定的数据转换,并在作业完成后删除 Dataproc 集群。

您可以在 Composer 和 Dataproc 控制台中看到正在进行的各种任务(每种情况都给一些时间),最终的测试将是验证处理后的数据是否出现在 PySpark 代码中指定的 GCS 目标中。

当您完成所有上述步骤并且不再需要您的 Composer 环境时,您可以删除该环境。

删除 Cloud Composer 环境

执行以下步骤以删除 Composer 环境:

  1. 在 Google Cloud 控制台中,导航到Google Cloud 服务菜单→Composer环境

  2. 选择您环境名称旁边的复选框。

  3. 在屏幕顶部选择删除。参见图 6.7以获取参考:

图 6.7:删除 Composer 环境

图 6.7:删除 Composer 环境

  1. 在出现的确认屏幕中选择删除

环境删除可能需要几分钟,之后它将从您的环境列表中消失。

太棒了!您已经在 Google Cloud 上正式创建了您的第一个数据处理管道!

注意,本章中我们使用的方法是使用多个 Google Cloud 服务实现数据管道(一个非常流行的)模式的一个示例。Google Cloud 还提供其他产品,可用于实现类似的结果,例如 Google Cloud Data Fusion,它允许您使用可视化用户界面创建管道。我们将在本书的后续章节中探索其他服务,而 Google Cloud 在 2023 年推出的另一个重要服务是无服务器 Spark,我们将在下一节中简要讨论。

Google Cloud 无服务器 Spark

Google Cloud 无服务器 Spark 是一个完全托管的无服务器 Apache Spark 服务,它使得在不配置或管理任何基础设施的情况下运行 Spark 作业变得简单。它还会根据需求自动扩展您的作业,因此您只需为使用的资源付费,这使得它是一种成本效益高的运行 Spark 作业的方式,即使是对于短期或间歇性工作负载也是如此。

它还与 Google Cloud 的其他服务集成,例如 BigQuery、Dataflow、Dataproc 和 Vertex AI,以及流行的开源工具如 Zeppelin 和 Jupyter Notebook,这使得使用这些服务直接探索和分析数据以及构建和运行 端到端E2E)数据管道变得容易。

例如,在 Dataproc 上的无服务器 Spark 中,您可以从预制的模板中选择,轻松执行常见任务,如将数据在 Java 数据库连接JDBC)或 Apache Hive 数据存储与 GCS 或 BigQuery 之间移动和转换,或者您可以为无服务器 Spark 构建自己的 Docker 容器,以便实现自定义数据处理工作负载。有关如何开发此类自定义容器的更多信息,请参阅以下 Google Cloud 文档:cloud.google.com/dataproc-serverless/docs/guides/custom-containers

现在我们已经学会了如何构建批量数据处理管道,我们将继续实施流式数据处理管道。

流式数据处理管道

在本节中,我们将处理不同类型的数据源,并了解实时数据处理与我们在前几节中使用的面向批量的方法的差异。

流式数据处理管道的概念和工具

同样,在我们开始构建流式数据处理管道之前,有一些重要的概念和工具我们需要介绍和理解。

Apache Beam

Apache Beam 是一个开源的、统一的编程模型,用于批处理和流处理模式下的大规模数据处理。它最初由 Google 开发,作为其内部数据处理工具的一部分,后来捐赠给了 ASF。Beam 提供了一种统一的方式来编写可以在各种分布式处理后端上执行的数据处理管道,例如 Apache Flink、Apache Samza、Apache Spark、Google Cloud Dataflow 等。它支持多种编程语言,包括 Java、Python 和 Go,并允许开发者使用单个 API 编写批处理和流数据处理管道,从而简化了开发过程并实现了批处理和流处理模式之间的无缝切换。它还提供了一套丰富的内置 I/O 连接器,用于各种数据源和接收器,包括 Kafka、Hadoop、Google Cloud Pub/Sub、BigQuery 等。此外,如果需要,开发者可以构建自己的自定义连接器。在本章中,我们将使用 Apache Beam 在 Google Cloud Dataflow 上创建一个管道,以实时处理数据。

Apache Beam 概念

在本节中,我们讨论了 Apache Beam 编程模型的一些基本概念,包括以下内容:

  • Pipelines:就像我们在本章前面使用的 Apache Airflow 中的管道概念一样,Apache Beam 管道是一个 DAG,它表示 Apache Beam 工作负载中的数据处理步骤序列或整体数据处理工作流程。

  • PCollections并行集合PCollection)是一个不可变的分布式数据集,表示数据元素集合。它是 Apache Beam 管道中用于存储和操作数据的主要数据结构。

  • PTransforms并行转换PTransform)是一个用户定义的操作,它接受一个或多个 PCollections 作为输入,处理数据,并产生一个或多个 PCollections 作为输出。PTransforms 是管道的构建块,并定义了数据处理逻辑。

  • Windowing:窗口化是一种机制,允许根据时间戳或其他标准对 PCollection 中的数据元素进行分组。这个概念对于处理流应用程序中的无界数据集特别有用,在这些应用程序中,数据元素需要以有限窗口进行处理。

  • Watermarks:水印是估计流处理管道中时间进度的一种方式,并有助于确定何时可以安全地发出特定窗口的结果。

  • 触发器:触发器根据某些因素(如到达一定数量的数据元素、经过一定的时间或水印的推进)确定何时聚合每个窗口的结果。

  • Runners:运行器是负责在特定执行引擎或分布式处理平台上执行 Beam 管道的组件。

Beam 模型将管道定义与底层执行引擎解耦,使用户能够为他们的用例选择最合适的平台。

Google Cloud Dataflow

我们在 第三章 中介绍了 Dataflow,现在我们将更深入地探讨它。Dataflow 是一个 Google Cloud 服务,你可以在其上运行 Apache Beam。换句话说,它提供了 Apache Beam 工作负载可以运行的执行环境或运行器之一。

Dataflow 为各种类型的用例提供了多个功能,在本节中,我们将简要讨论各种选项及其应用。

Dataflow 数据管道和 Dataflow 作业

人们经常对 Dataflow 数据管道和 Dataflow 作业之间的区别感到困惑。最好的看待方式是,Dataflow 数据管道指的是数据管道的定义,这可以定期执行,而 Dataflow 作业则指单个数据管道的执行。这种混淆的原因是,你可以在 Dataflow 作业控制台或 Dataflow 数据管道控制台中创建新的管道定义。

在任何情况下,当创建管道定义时,我们都有使用预定义模板的选项,这些模板涵盖了人们经常希望使用 Dataflow 完成的常见任务类型,例如将数据从 BigQuery 转移到 Bigtable,或从 Cloud Spanner 转移到 Pub/Sub,而且有大量的不同模板可供选择,覆盖了广泛的数据源和目的地。这些模板使我们能够非常容易地实现数据传输工作负载,而无需我们进行很多或任何开发工作。或者,如果我们有更复杂的数据处理需求,而这些需求不包括在任何标准模板中,那么我们可以创建自己的自定义管道定义。我们将在本章后面探讨这两种选项。

Dataflow Workbench 笔记本

我们可以通过使用 Dataflow Workbench 笔本来开发自定义数据处理管道。这可能听起来有些熟悉,因为你可能还记得在 第五章 中创建和使用 Vertex AI Workbench 笔记本。在 Dataflow Workbench 控制台中,我们可以创建已经预装 Apache Beam 的笔记本。我们将在本章后面创建一个笔记本。

Dataflow 快照

Dataflow 快照保存了流管道的状态,这使得你可以在不丢失状态的情况下启动 Dataflow 作业的新版本。这对于备份和恢复、测试以及回滚流管道的更新非常有用。

SQL 工作区

Dataflow 控制台还包括一个内置的 SQL 工作区,它允许你直接从控制台运行 SQL 查询,并将结果发送到 BigQuery 或 Pub/Sub。这对于你只想简单地运行一个 SQL 查询以从给定源获取信息并将结果存储在支持的某个目的地的情况非常有用。

现在我们已经涵盖了实现流数据管道的重要概念,让我们开始构建我们的管道。

构建我们的流数据管道

坚持我们计划去纽约市的主题,本章前几节的活动让我们对可用的住宿选项有了很好的了解,现在我们想要评估我们的交通选项;具体来说,我们想知道在纽约市乘坐出租车旅行的费用可能多少。

我们的流数据管道将从 Google Cloud Pub/Sub 获取输入数据,在 Dataflow 中进行一些处理,并将输出放入 BigQuery 以进行分析。我们的管道在 Google Cloud 上的架构如 图 6.8 所示:

图 6.8:流数据管道

图 6.8:流数据管道

Google Cloud 提供了一个公共数据流,可以用于测试这些类型的流处理工作负载,其中包含与纽约市出租车行程相关的信息,我们将在本节的示例中使用它。让我们首先为我们的流数据创建一个目的地,也称为我们的管道的

创建 BigQuery 数据集

我们将使用 Google Cloud BigQuery 作为我们的数据存储系统。要开始,我们首先需要在 BigQuery 中定义一个数据集,我们将将其用作我们的管道目的地。为此,执行以下步骤:

  1. 在 Google Cloud 控制台中,导航到 Google Cloud 服务 菜单→ BigQuery

  2. 在屏幕的左上角,你会看到你的项目名称。点击项目名称右侧的三个垂直点符号(参见 图 6.9 以获取参考):

图 6.9:BigQuery 项目菜单

图 6.9:BigQuery 项目菜单

  1. 在显示的菜单中,选择 创建数据集

  2. 给你的数据集起个名字:taxirides

  3. 选择你首选的区域,并选择 创建数据集

现在我们已经创建了数据集,我们需要在该数据集中创建一个表。

创建 BigQuery 表

一个 BigQuery 数据集通常包含一个或多个包含实际数据的表。让我们创建一个我们将数据流到其中的表。为此,执行以下步骤:

  1. 在 BigQuery 编辑器中,点击你刚刚创建的 taxirides 数据集,并选择 创建表

  2. 将表名设置为 realtime

  3. 模式 部分中,点击加号(+)添加一个新字段。我们的第一个字段具有以下属性(将每个字段的其余选项保留为其默认值):

字段名称类型模式
ride_idSTRINGNULLABLE

表 6.1:BigQuery 表中第一个字段的属性

  1. 重复步骤 3以添加更多字段,直到架构看起来像图 6.10中所示:

图 6.10:表架构

图 6.10:表架构

  1. 选择创建表

现在我们的表已经准备好接收数据流,让我们继续创建我们的数据流管道。

从模板创建 Dataflow 作业

我们将使用 Dataflow 模板来创建我们的第一个数据流管道。为此,请执行以下步骤:

  1. 在 Google Cloud 控制台中,导航到Google Cloud 服务菜单→Dataflow作业

  2. 选择从模板创建作业

  3. 对于taxi-data-raw

  4. 选择您首选的区域。

  5. Dataflow 模板下拉菜单中,选择Pub/Sub 到****BigQuery模板。

注意

Pub/Sub 还发布了与 BigQuery 的直接集成,但考虑到我们想要说明如何使用 Dataflow 模板,我们本章使用 Dataflow 连接方法。

  1. 接下来,在输入 Pub/Sub 主题字段中,选择手动输入主题

  2. 输入以下主题:

    projects/pubsub-public-data/topics/taxirides-realtime
    
  3. 在上一节中创建的realtime表中。

  4. 在屏幕底部单击选择

  5. 临时位置字段中,输入以下格式的所需存储位置路径(将[BUCKET-NAME]替换为您的桶名称):gs://[BUCKET-NAME]/dataflow。

  6. 展开可选参数部分,并向下滚动,直到找到服务帐户****电子邮件字段。

  7. 在该字段中,选择您在本章中较早创建的服务帐户(如果您使用了建议的名称,名称中将包含data-processing-sa)。

  8. 将所有其他选项保留在默认值。

  9. 选择运行作业

  10. 几分钟后,您将看到作业详情出现,一个类似于图 6.11所示的图形:

图 6.11:Dataflow 执行图

图 6.11:Dataflow 执行图

  1. 尝试在图中每个步骤和子步骤上点击,以更好地了解每个步骤的作用。

经过一段时间后,您可以前往 BigQuery 控制台并验证数据是否正在流入表中。继续下一节以执行此操作。但是,请不要关闭 Dataflow 控制台,因为您将在验证 BigQuery 中的数据后返回这里。

验证 BigQuery 中的数据

为了验证 BigQuery 中的数据,请执行以下步骤:

  1. 在 Google Cloud 控制台中,导航到Google Cloud 服务菜单→BigQuery

  2. 在屏幕的左上角,您将看到您的项目名称。单击项目名称左侧的箭头符号以展开它(参见图 6.12以供参考)。

  3. 然后,单击数据集名称(taxirides)左侧的箭头符号以展开它。

  4. 选择您的realtime表。

  5. 选择预览选项卡。

  6. 然后,你应该会看到一个类似于图 6.12所示的屏幕:

图 6.12:BigQuery 数据

图 6.12:BigQuery 数据

  1. 当您在 BigQuery 中验证了数据后,您可以返回 Dataflow 控制台,通过点击屏幕顶部的停止来停止 Dataflow 作业。

现在您已经看到了从模板设置 Dataflow 作业是多么容易,让我们继续探讨更复杂的 Dataflow 使用案例。

创建数据流笔记本

因为我们想使用定制的 Apache Beam 笔记本,所以我们将创建一个在 Dataflow Workbench 控制台中的笔记本。按照以下步骤创建笔记本:

  1. 在 Google Cloud 控制台中,导航到Google Cloud 服务菜单→DataflowWorkbench

  2. 在屏幕顶部,选择实例标签。

  3. 现在,在屏幕顶部,选择创建新

  4. 在出现的屏幕中(参见图 6.13以获取参考),您可以选择接受默认的笔记本名称或创建您偏好的名称:

图 6.13:创建用户管理的笔记本

图 6.13:创建用户管理的笔记本

  1. 选择您首选的区域和区域。在这种情况下,区域选择并不重要,但我建议选择您在此书之前活动中一直使用的相同区域。

  2. 选择继续,然后再次继续

  3. 选择 E2 作为计算实例类型。

  4. 您还可以配置一个空闲超时周期,在此周期后,如果机器空闲这么长时间,它将自动关闭。这有助于节省成本。

  5. 选择继续多次,直到达到IAM安全屏幕

  6. IAM 和安全屏幕中,选择单用户选项。参见图 6.14以获取参考:

图 6.14:数据流笔记本 – IAM 和安全

图 6.14:数据流笔记本 – IAM 和安全

注意

您可能记得在第五章中创建我们的托管笔记本时执行了类似的步骤(即选择单用户身份验证选项)。这允许我们直接使用笔记本,而无需事先将其作为服务账户进行身份验证。

  1. 出现的用户电子邮件框应自动填充您的登录电子邮件地址。如果不是,请输入您用于登录 Google Cloud 控制台的电子邮件地址。

  2. 此外,我们希望我们的笔记本实例使用我们在本章早期创建的服务账户,所以取消选中表示在 VM 上使用默认 Compute Engine 服务账户调用 Google Cloud API的选项。

  3. data中,你应该会在可用的服务账户列表中看到您在本章早期创建的服务账户的名称(假设您将其命名为data-processing-sa,如该部分中建议的)。

  4. 在列表中选择该服务账户。

  5. 此外,请注意屏幕右上角的定价摘要选项。这是如果您整个月都保持笔记本运行,它将花费的估计金额。幸运的是,您在本章中只会使用很短的时间。如果您在创建笔记本时没有配置空闲关闭时间段,请记住在您完成使用后关闭它。

  6. 您可以将所有其他选项保留在默认值,并在屏幕底部选择创建

  7. 创建笔记本实例需要几分钟。当实例创建完成后,您将在用户管理的笔记本列表中看到它,并且将出现一个打开 JupyterLab 的选项。

  8. 选择打开 Jupyterlab

  9. 当 JupyterLab 屏幕打开时,是时候将我们的仓库克隆到您的笔记本中了。这个过程与您在第五章中执行的过程类似,但您在本章中创建了一个单独的笔记本实例,因此我们需要将仓库克隆到这个实例中。克隆仓库的步骤如下。

  10. 点击屏幕左侧菜单中的Git符号。该符号看起来就像图 6**.15中所示的那样:

图 6.15:Git 符号

图 6.15:Git 符号

  1. 选择克隆仓库

  2. 输入我们的仓库 URL:github.com/PacktPublishing/Google-Machine-Learning-for-Solutions-Architects.

  3. 如果显示任何选项,请保留它们的默认值。

  4. 选择克隆

  5. 您应该会在您的笔记本中看到一个名为 Google-Machine-Learning-for-Solutions-Architects 的新文件夹。

  6. 双击该文件夹,然后双击其中的 Chapter-06 文件夹,最后双击 Streaming_NYC_Taxi_Data.ipynb 文件以打开它。

  7. 在出现的选择内核屏幕中,选择 Apache Beam 的最新版本。在撰写本文时,启动器中可用的最新选项是 Apache Beam 2.4.6(参见图 6**.16以获取参考):

图 6.16:选择笔记本内核

图 6.16:选择笔记本内核

  1. 我们打开的笔记本包含大量的 Apache Beam Python 代码,我们可以使用这些代码来处理从公共 Pub/Sub 主题流进的数据。

  2. 运行笔记本中的每个单元格,并阅读 markdown 和注释中的说明,以了解我们在做什么。我们正在使用 Apache Beam 定义一个将在 Google Cloud Dataflow 中运行的数据流处理管道。

当你完成笔记本中单元格的运行后,你可以转到 Dataflow 作业控制台,你将看到你的新管道在那里运行。给管道启动和数据处理一些时间,就像之前一样,我建议你点击管道执行图的各种部分,以便更好地理解管道的结构。接下来,让我们在 BigQuery 中验证这个新管道的数据,但同样,不要关闭 Dataflow 控制台,因为你在验证 BigQuery 中的数据后将会回到这里。

验证 BigQuery 中的数据

要验证 BigQuery 中的数据,请打开 BigQuery 控制台,在taxirides数据集下,你会看到一个由我们的自定义 Dataflow 管道创建的新表,称为run_rates。点击由我们的管道计算出的run_rates值。

当你在 BigQuery 中验证了数据后,你可以回到 Dataflow 控制台,通过点击屏幕顶部的停止来停止 Dataflow 作业。

现在我们已经完成了本节的活动,你可以通过以下步骤关闭你的用户管理的笔记本:

  1. 在 Google Cloud 控制台中,导航到Google Cloud 服务菜单 → Dataflow工作台

  2. 在屏幕顶部,选择用户管理笔记本标签。

  3. 选择你的笔记本名称旁边的复选框,并在屏幕顶部(在用户管理笔记本标签上方)点击停止

笔记本将在几分钟后关闭。

如果一切按预期进行,你现在已经成功创建了一个自定义管道,该管道可以处理和转换飞行中的流数据!

摘要

在本章中,你学习了如何从各种来源将数据导入 Google Cloud,并且你发现了在 Google Cloud 中处理数据的重要概念。

你随后学习了如何使用 Vertex AI 和 BigQuery 探索和可视化数据。接下来,你学习了如何使用 Jupyter 笔记本清理和准备数据以供 ML 工作负载使用,然后是如何在 Google Cloud Dataproc 上使用 Apache Spark 以批量方法创建自动数据管道,以在生产规模上执行相同的转换,以及如何使用 Apache Airflow 在 GCC 中自动编排整个流程。

我们随后介绍了与处理流数据相关的重要概念和工具,而你最终使用 Apache Beam 在 Google Cloud Dataflow 上构建了自己的流数据处理管道。

在下一章中,我们将花更多的时间在数据处理和准备上,特别关注特征工程的概念。

第七章:特征工程和降维

在本章中,我们将逐步深入探讨许多数据科学项目中常见的数据处理步骤,以及如何使用 Google Cloud 中的 Vertex AI 来执行这些步骤。我们将从更详细地查看特征在机器学习工作负载中的使用方式以及与特征使用相关的常见挑战开始本章。

然后,我们将讨论如何解决这些挑战,以及如何在 Google Cloud 中有效地使用我们的机器学习特征。

本章涵盖了以下主题:

  • 与机器学习中的维度或特征相关的基本概念

  • 维度灾难的介绍

  • 降维

  • 特征工程

  • Vertex AI 特征存储

本章的基本概念

在本节中,我们将简要介绍为本章的学习活动提供额外背景的概念。

维度和特征

我们在第一章中介绍了特征的概念,并使用金县房屋销售数据集作为示例描述了特征。为了简要回顾,特征是我们数据集中观察到的单个、可度量的属性或特征。它们是我们数据集的方面,机器学习算法从中学习以创建模型。换句话说,模型可以被视为算法从我们数据集中的特征中学习到的模式的表示。

例如,房屋的特征包括诸如房间数量、建造年份、位置以及其他描述房屋的因素等信息,如表 7.1所示:

表 7.1:金县房屋销售特征

表 7.1:金县房屋销售特征

当我们处理表格数据时,特征通常以数据集中的列的形式表示,每一行代表一个单独的数据点或观察结果,有时也被称为实例

特征也被称为变量、属性或维度。因此,当我们谈论数据集的维度时,它与我们数据集中有多少个特征或维度有关,以及这如何影响我们的机器学习工作负载有关。

过拟合、欠拟合和正则化

我们在第二章中简要讨论了过拟合和欠拟合的概念,由于这些概念对于机器学习过程至关重要,因此在本书中我们将继续更详细地回顾这些主题。在本节中,我们将讨论数据集中特征的数量如何影响我们的算法从数据中学习的方式。一个需要记住的关键概念是,过拟合和欠拟合可以受到我们数据集中观察到的数量以及每个观察到的特征数量的强烈影响。

我们通常需要在数据集的这两个方面之间找到合适的平衡。例如,如果我们对每个观察到的数据点有非常少的观测值和很多特征,那么我们的模型很可能会过度拟合数据集,因为它为这些观测值及其特征学习到了非常具体的模式,但它无法很好地泛化到新的观测值。相反,如果我们有大量的观测值,但每个观测值只有很少的信息(即特征),那么我们的模型可能无法学习到任何有价值的模式,这意味着它将欠拟合我们的数据集。正因为如此,减少特征的数量可以帮助减少过度拟合,但只能到一定程度——去除过多的特征可能会导致欠拟合。此外,我们不想去除那些对模型学习有用的信息,因此,我们可以在保持许多特征的同时,通过使用一种称为正则化的机制来解决过度拟合问题。我们也在第二章中简要提到了这一点,我们将在这里更详细地讨论它。

正则化

要开始我们关于正则化的讨论,我们需要再次提及机器学习中的损失函数的概念,这是我们已经在第一章中介绍过的。记住,许多机器学习算法通过尝试找到每个特征的最好系数(或权重),以实现对目标特征的最近似来工作。因此,过度拟合受到特征与其系数之间的数学关系的影响。如果我们发现模型对特定特征过度拟合,我们可以使用正则化来减少这些特征及其系数对模型的影响。

由于过度拟合通常发生在模型过于复杂的情况下,例如相对于观测值的数量有太多的特征,正则化通过向损失函数中添加惩罚来解决这一问题,这会阻止模型对任何特征赋予过多的重视。这有助于提高模型的泛化能力。

在机器学习中实现正则化的方法有很多,但我会解释两种最常见类型——即L1L2正则化——以及这两种方法的组合,称为弹性网络

L1 正则化

这种类型的正则化也称为Lasso正则化,它通过以下公式通过添加与成本函数中系数或权重的 L1 范数(或绝对值)相等的惩罚来实现:

成本函数 + λ * |权重|

在这里,λ是正则化参数,它控制惩罚的强度,可以被认为是一个超参数,其最佳值可能因问题而异。请注意,如果惩罚太强,可能会导致欠拟合,因此在这方面找到合适的平衡很重要。

L1 正则化的作用是将模型的一些系数缩小到正好为零,从而有效地排除相应的特征,这使得 L1 正则化在处理高维数据时对于特征选择(我们将在稍后更详细地介绍)非常有用。

L2 正则化

这种正则化也称为 正则化。这种方法通过以下公式在成本函数中添加一个相当于 L2 范数(或平方)的惩罚:

成本函数 + λ * (权重²)

与 L1 正则化不同,L2 正则化不会导致特征的排除,而是将系数推向零附近,在特征之间均匀分配权重。当我们处理相关特征时,这可能是有益的,因为它允许模型考虑所有这些特征。

弹性网络

弹性网络作为 L1 和 L2 正则化的组合,旨在在这两种方法之间提供折衷方案,结合两者的优点。与 L1 和 L2 正则化一样,弹性网络向损失函数添加惩罚,但它不是添加 L1 惩罚或 L2 惩罚,而是通过以下公式添加两者的加权总和:

成本函数 + λ1 * |权重| + λ2 * (权重²)

在这里,λ1 和 λ2 是控制 L1 和 L2 惩罚强度的超参数。如果 λ1 为零,弹性网络回归将退化为岭回归,而如果 λ2 为零,则退化为 Lasso 回归。

弹性网络具有 L1 正则化的特征选择能力(因为它可以将系数缩小到零),以及 L2 正则化的正则化强度(因为它可以在相关特征之间均匀分配权重)。与弹性网络相关的权衡是,它有两个超参数需要调整,而不是 Lasso 或岭回归中只有一个,这可能会使模型更复杂,训练起来计算量更大。

现在我们已经更详细地介绍了正则化这个重要话题,让我们深入探讨特征选择和特征工程。

特征选择和特征工程

在本书的前几章中,我们简要地讨论了特征工程,但在这里我们将更详细地探讨这些概念。在第二章中,我们通过将每栋房子的总成本除以该房子的总面积(平方英尺)来创建一个新的特征 price-per-square-foot,作为我们的住房数据示例。在本章中,我们将探讨许多额外的特征工程示例。

然而,从现有特征中创建新特征并不是我们在为训练机器学习模型准备数据集时需要在特征上进行的唯一活动。我们还需要选择我们认为对实现我们希望模型完成的任务(例如预测房价)最重要的特征。正如我们在第六章中看到的,我们可能还需要对特征进行转换,例如确保它们都表示在共同的、标准化的尺度上。

在选择和构建特征时的目标是,以最易于消化的格式为我们模型提供最相关的信息,以便它可以从数据中有效地学习。

维度诅咒

高维数据集包含许多维度或特征,每个数据点都有很多。我们可能会假设,我们为每个数据点包含的特征越多,我们的模型学到的信息就越多,因此,我们的模型就越准确。然而,请注意,并非所有特征都同等有用。一些可能对我们的模型几乎没有或没有有用的信息,其他可能包含冗余信息,甚至可能对模型学习的能力有害。机器学习艺术和科学的一部分就是确定使用哪些特征以及如何准备它们,以便模型能够发挥最佳性能。

此外,请记住,我们数据集中包含的信息越多,我们的模型需要处理的信息就越多。这直接导致我们的机器学习算法处理数据集时需要更多的计算资源,这反过来又直接导致模型训练时间更长,成本增加。数据集中过多的无关数据或噪声也可能使算法更难识别(即学习)数据中的模式。因此,理想的情况是找到提供最大有用信息的最小特征数量。例如,如果我们可以用三个特征或十个特征达到相同的结果,那么通常选择使用三个特征会更好。“最大有用信息”可以通过方差来衡量,其中具有高相对方差的特征对我们的模型结果影响最为显著,而相对方差小的特征在训练模型识别模式时通常不太有用。

“维度诅咒”是数据科学行业用来描述处理包含更多维度的数据集时出现的挑战的术语。让我们看看这些挑战中的一些。在随后的章节中,我们将讨论解决这些挑战的机制。

数据探索挑战

这是我们可以引入数据集中的“维度”与物理空间维度之间联系的重要点。众所周知,正如本书前面提到的,人类只能感知到最多三个维度的物理世界(宽度、高度和深度),而“时间”被视为我们物理现实的第四维度。对于具有两个或三个维度的数据集,我们可以轻松创建表示那些数据集各个方面的可视化,但我们无法创建更高维数据集的图表或其他视觉表示。在这种情况下,如果我们能尝试找到其他方法来视觉解释这些数据集,比如将它们投影到低维表示中,这会很有帮助,我们将在稍后更详细地探讨这一点。

特征稀疏性

在高维空间中,数据集中的点(即实例或样本)往往彼此相距甚远,导致稀疏性。一般来说,随着特征数量相对于数据集中观察值的数量的增加,特征空间变得越来越稀疏,这种稀疏性使得算法从数据中学习变得更加困难,因为它们在任意给定点的附近有更少的例子可以学习。

例如,让我们假设我们的数据集包含有关公司客户的信息,在这种情况下,数据集中的每个实例代表一个人,每个特征代表一个人的某种特征。如果我们的数据集存储了数百甚至数千个特征,那么不太可能每个特征都会被填充。因此,我们数据集的整体特征空间将是稀疏的。另一方面,如果我们有较少的特征,这种情况发生的可能性较小。

距离测量

在前面的章节中,我们讨论了欧几里得距离在许多机器学习算法中的应用,用于寻找数据集中数据点之间潜在的关联或差异。我们已经探讨的这种概念的最直接例子之一是 K-means 聚类算法。在高维空间中,距离有时可能变得不那么有意义,因为最大可能距离和最小可能距离之间的差异变得越来越小。这意味着传统的距离度量,如欧几里得距离,也变得不那么有意义,这可能会特别影响依赖于距离的算法,例如k 近邻kNN)或聚类算法。

过度拟合和增加的数据需求

回顾我们之前关于过拟合或欠拟合如何受数据集中观察值与特征比例影响的讨论,高维数据集更容易过拟合,除非我们有大量的观察值来帮助我们的模型泛化。记住我们在本章前面提到的关于算法处理数据量与训练模型成本之间关系的内容。具有大量观察值和大量特征的数据集将更昂贵且更难训练和管理。

可解释性和可解释性

可解释性和可解释性指的是我们理解和解释我们的机器学习模型如何工作的能力。这有多个原因,我们将在下面简要讨论。首先,对我们模型工作原理的不理解阻碍了我们改进这些模型的能力。然而,更重要的是,我们需要确保我们的模型尽可能公平和无偏见,这就是可解释性发挥关键作用的地方。如果我们不能解释我们的模型为何产生特定的结果,那么我们就无法充分评估其公平性。一般来说,我们的数据集维度越高,我们的模型往往越复杂,这会直接影响(即减少)可解释性和可解释性。

现在我们已经回顾了一些与高维数据集相关联的常见挑战,让我们来看看一些解决这些挑战的机制。

维度降低

如你所想,解决维度过多挑战的第一种方法就是减少维度数量,为此我们可以使用两种主要的技术:特征选择和特征投影。

特征选择

这涉及到从原始特征中选择一个子集。特征选择有几种策略,包括以下几种:

  • 过滤方法,这些方法根据统计指标对特征进行排序,并选择排名最高的特征子集

  • 包装方法,这些方法使用不同的输入特征子集评估多个模型,并选择导致最高模型性能的子集

  • 嵌入方法,这些方法使用具有内置特征选择方法的机器学习算法(例如 Lasso 正则化)

同样重要的是要理解,我们将讨论的特征投影方法可以用来帮助我们选择数据集中最重要的特征子集,因此这些概念之间有一些重叠。

特征投影

在特征投影中,我们使用数学变换将特征投影到低维空间。在本节中,我们将介绍三种流行的特征投影技术:主成分分析PCA)、线性判别分析LDA)和t-分布随机邻域嵌入t-SNE)。

PCA

主成分分析(PCA)是一种无监督算法,旨在在尽可能保持原始数据方差的同时,降低我们的特征空间的维度。在高维数据空间中,PCA 识别出数据变化最大的轴(主成分)。这些主成分是正交的,意味着在这个多维空间中它们彼此之间成直角。第一个主成分PC1)捕捉数据中最大方差的方向。第二个主成分PC2)在正交于 PC1 的同时,捕捉剩余的最大方差,依此类推。PCA 的过程通常包括以下步骤:

  1. 如果特征具有不同的尺度,则对数据进行标准化。这是很重要的,因为 PCA 对特征尺度很敏感,尺度较大的特征可能会被错误地认为是更占主导地位的。

  2. 计算协方差矩阵以了解不同特征是如何一起变化的。协方差矩阵是一个方阵,包含了每对特征之间的协方差。两个特征之间的协方差衡量了这些特征是如何一起变化的:正协方差表示特征一起增加或减少,而负协方差表示一个特征增加而另一个减少。

  3. 计算协方差矩阵的特征值特征向量。特征向量代表新空间的方向或成分,特征值代表每个成分的大小或解释方差。特征向量通常被称为数据的主成分,它们构成了新特征空间的一个基。

  4. 对特征值及其对应的特征向量进行排序。在计算了特征值及其相关的特征向量之后,下一步是对特征值按降序排序。具有最高对应特征值的特征向量是 PC1。具有第二高对应特征值的特征向量是 PC2,依此类推。这种排序的原因是每个特征向量的重要性由其特征值的大小给出。

  5. 选择主成分的子集。PCA 创建与原始数据集中变量数量一样多的主成分。然而,由于 PCA 的目标是降维,我们通常选择主成分的子集,称为前 k 个主成分,这些主成分捕捉了数据中的最大方差。这一步是降低维度的原因,因为较小的特征值及其向量被丢弃。

  6. 转换原始数据。PCA 的最后一步是将原始数据转换成由所选主成分定义的降维子空间,这是通过将原始数据矩阵乘以前k个特征向量的矩阵来实现的。

转换后的数据现在可以用于进一步的分析和可视化(如图图 7.1所示),或者作为机器学习算法的输入。重要的是,这个减少的数据集尽可能地保留了原始数据中的方差(考虑到减少的维度数):

图 7.1:欧洲遗传结构的 PCA 可视化(来源:https://commons.wikimedia.org/wiki/File:PCA_plot_of_European_individuals.png)

图 7.1:欧洲遗传结构的 PCA 可视化(来源:commons.wikimedia.org/wiki/File:P…

PCA 是一种强大的技术,用途广泛,但它也有局限性。例如,它假设主成分是原始特征的线性组合。如果这不是这种情况(即,如果数据中的潜在结构是非线性的),那么 PCA 可能不是最佳降维技术。还值得注意的是,主成分比原始特征更难以解释——它们在原始特征方面没有直观的意义。

LDA

LDA 是一种监督算法,旨在找到最佳分离对象类别的特征线性组合。然后可以使用这个组合进行降维。它涉及以下步骤:

  1. 计算类均值。对于数据集中的每个类别,计算均值向量,这仅仅是该类别中所有向量的平均值。

  2. 计算类内协方差矩阵,它衡量各个类别如何围绕各自的均值分散。

  3. 计算类间协方差矩阵,它衡量类别均值在数据总体均值周围如何分散。

  4. 计算线性判别方向,这些方向是特征空间中类别最佳分离的方向。

  5. 对线性判别方向进行排序。就像在 PCA 中一样,特征向量按照它们对应的特征值降序排列。特征值表示每个判别所解释的数据方差量。前几个线性判别方向,对应于最大的特征值,是解释最大方差的方向。

  6. 最后,数据被投影到由前几个线性判别方向张成的空间中。

这导致数据在低维表示中类别最大化分离。我们可以如下可视化:

图 7.2:葡萄酒品种的 LDA 图

图 7.2:葡萄酒品种的 LDA 图

重要的是要注意,LDA 的主要假设是类别具有相同的协方差矩阵。如果这个假设不成立,LDA 可能表现不佳。

t-SNE

这种方法可能拥有所有方法中最酷的名字。它是一个无监督的非线性降维算法,特别适合将高维数据嵌入到二维或三维空间中,同时旨在使相似实例靠近,不同实例分离。它是通过将高维数据映射到低维空间,以保留点之间的大部分相对距离来实现这一点的。它包括以下步骤:

  1. 在高维空间中计算相似性:t-SNE 首先计算高维空间中数据点对相似的概率。彼此靠近的点有更高的被选中概率,而彼此远离的点有更低的被选中概率。

  2. 在低维空间中计算相似性:t-SNE 随后计算低维表示中点对相似性的概率。

  3. 优化:最后,t-SNE 使用梯度下降来最小化高维和低维空间中概率的差异。目标是使相似对象由低维空间中的邻近点表示,不同对象由低维空间中的远点表示。

t-SNE 的结果是一张地图,以人类更容易理解的方式揭示了高维数据的结构。

应该注意的是,虽然 t-SNE 在可视化方面非常出色,可以揭示数据中的簇和结构(如图图 7.3所示),但它并不像 PCA 那样提供关于数据中特征重要性或含义的明确信息。它更多的是一种探索性工具,可以帮助进行降维,而不是一种正式的降维技术:

图 7.3:数字数据集的 t-SNE 可视化

图 7.3:数字数据集的 t-SNE 可视化

现在我们已经介绍了一些最受欢迎的特征投影技术,让我们再讨论一组关于我们数据集中的特征如何影响模型训练的重要概念。

使用 PCA 和 LDA 进行降维

我们将在本章中通过使用 PCA 和 LDA 进行降维来开始我们的动手活动。我们可以使用 scikit-learn 中的葡萄酒数据集作为例子。我总是希望我能通过成为一个葡萄酒专家来给我的朋友们留下深刻印象,但我几乎无法区分 10 美元一瓶的酒和 500 美元一瓶的酒,所以,我将使用数据科学来发展令人印象深刻的知识。

葡萄酒数据集是一个多变量数据集的例子,它包含了在意大利同一地区种植但来自三种不同葡萄品种(称为栽培品种)的葡萄酒的化学分析结果。分析的重点是量化三种葡萄酒类型中发现的 13 种成分。

使用 PCA 处理这个数据集将帮助我们理解重要的特征。通过观察原始特征在主成分中的权重,我们可以看到哪些特征对葡萄酒数据集中的变异性贡献最大。

再次,我们可以使用在第五章中创建的相同的 Vertex AI Workbench 笔记本实例来完成此目的。请在笔记本实例上打开 JupyterLab 并执行以下步骤:

  1. 在屏幕左侧的目录浏览器中,导航到Chapter-07目录并打开dimensionality-reduction.ipynb笔记本。

  2. 选择Python (Local)作为内核。

  3. 通过选择单元格并按键盘上的Shift + Enter来运行笔记本中的每个单元格。

笔记本中的代码执行以下活动:

  1. 首先,它导入必要的库。

  2. 然后,它加载数据集。

  3. 接下来,它标准化了葡萄酒数据集的特征。它应用主成分分析(PCA)将维度降低到二维(即前两个主成分)。这是通过使用fit_transform()方法完成的,该方法将 PCA 模型拟合到数据上,然后转换数据。

  4. 最后,它在两个主成分的空间中可视化数据,根据葡萄酒的类型对点进行着色:

    from sklearn.datasets import load_wine
    from sklearn.preprocessing import StandardScaler
    from sklearn.decomposition import PCA
    import matplotlib.pyplot as plt
    import pandas as pd
    # Load dataset
    data = load_wine()
    df = pd.DataFrame(data.data, columns=data.feature_names)
    # Standardize the features
    scaler = StandardScaler()
    df = scaler.fit_transform(df)
    # Apply PCA
    pca = PCA(n_components=2)
    principalComponents = pca.fit_transform(df)
    principalDf = pd.DataFrame(data = principalComponents, 
        columns = ['principal component 1', 
            'principal component 2'])
    # Visualize 2D Projection
    plt.figure(figsize=(8,6))
    plt.scatter(principalDf['principal component 1'], principalDf['principal component 2'], c=data.target)
    plt.xlabel('Principal Component 1')
    plt.ylabel('Principal Component 2')
    plt.show()
    

    结果可视化应类似于图 7**.4所示:

图 7.4:葡萄酒数据集的 PCA 散点图

图 7.4:葡萄酒数据集的 PCA 散点图

散点图应显示不同类型葡萄酒之间的清晰分离,这表明葡萄酒的类型与其化学成分密切相关。此外,PCA 对象(在我们的代码中命名为pca)存储了components_属性,其中包含每个特征与主成分的映射。通过检查这些,我们可以找出哪些特征在区分葡萄酒类型时最为重要。

可视化中的每个数据点代表我们数据集中的一个单独样本,但不是在原始的高维特征空间中绘制,而是在由主成分定义的较低维空间中绘制。

在葡萄酒数据集的上下文中,PCA 可视化中的每个数据点代表一个单独的葡萄酒样本。因为我们已经将原始的 13 个特征映射到两个 PCA 维度,所以每个点在XY轴上的位置对应于该葡萄酒样本的第一个和第二个主成分的值。

每个点的颜色代表葡萄酒样本的真实类别(来自三种不同的品种)。通过根据它们的真实类别着色点,你可以看到 PCA 转换在降维空间中如何将不同的类别分开。

记住,每个主成分是原始特征的线性组合,因此每个点的位置仍然由其原始特征值决定。这样,PCA 使我们能够可视化高维数据,并突出显示最大方差维度,这些维度通常是最具信息量的。

然后,我们可以访问拟合的 PCA 对象的components_属性来查看各个成分。该属性返回一个矩阵,其中每一行对应一个主成分,每一列对应一个原始特征。

因此,以下代码将打印一个表格,其中表格中的值代表每个成分中每个特征的权重:

components_df = pd.DataFrame(pca.components_, 
    columns=data.feature_names, index=['Component 1', 'Component 2'])
print(components_df)

结果应该类似于表 7.2中所示:

表 7.2:葡萄酒数据集的 PCA 成分和特征

表 7.2:葡萄酒数据集的 PCA 成分和特征

通过查看这些权重的绝对值,我们可以确定哪些特征对每个主成分最重要。大的绝对值对应于在捕捉该主成分变化中起重要作用的特征,权重的符号(正或负)可以告诉我们特征与主成分之间关系的方向。

接下来,让我们看看如何使用 LDA 来识别导致不同类型葡萄酒之间差异最大的成分。同样,我们首先导入必要的库并标准化数据。然后,我们将对标准化特征应用 LDA,指定n_components=2以获得二维投影,然后拟合 LDA 模型到数据并对前两个 LDA 成分进行数据转换。最后,我们将可视化转换后的数据:

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
# Apply LDA
lda = LDA(n_components=2)
lda_components = lda.fit_transform(df, data.target)
lda_df = pd.DataFrame(data = lda_components, 
    columns = ['LDA 1', 'LDA 2'])
# Visualize 2D Projection
plt.figure(figsize=(8,6))
plt.scatter(lda_df['LDA 1'], lda_df['LDA 2'], c=data.target)
plt.xlabel('LDA 1')
plt.ylabel('LDA 2')
plt.show()

结果可视化应该类似于图 7**.5中所示:

图 7.5:葡萄酒数据集的 LDA 散点图

图 7.5:葡萄酒数据集的 LDA 散点图

在这种情况下,散点图显示了第一二个 LDA 成分空间中的数据点,点再次根据葡萄酒类型着色。我们还应该看到不同类型葡萄酒之间的清晰分离,这表明它们具有不同的化学成分分布。

就像我们检查拟合的 PCA 对象的components_属性一样,我们可以查看拟合的 LDA 对象的coef_属性来查看最具判别性的特征,如下面的代码及其相应的输出所示:

# Create and print a DataFrame with the LDA coefficients and feature names
coef_df = pd.DataFrame(lda.coef_, columns=data.feature_names, 
    index=['Class 1 vs Rest', 'Class 2 vs Rest', 'Class 3 vs Rest'])
print(coef_df)

结果应该类似于表 7.3中所示:

表 7.3:葡萄酒数据集的 LDA 类别和特征

表 7.3:葡萄酒数据集的 LDA 类别和特征

在生成的表中,每一行对应一个类别(与其他类别相比),每一列对应一个原始特征。因此,表中的值代表线性判别分析中每个特征的系数。类似于我们的 PCA 评估,绝对值大的特征表示对区分类别有显著贡献的特征。

现在我们已经了解了如何降低数据集的维度,让我们假设我们已经确定了所需的特征,并开始探索我们如何进一步工程特征以确保我们有最佳可能的特征集来训练我们的模型。

特征工程

特征工程可以构成数据科学家活动的大部分内容,它对他们成功的重要性可能与其选择正确的机器学习算法一样重要,有时甚至更重要。在本节中,我们将更深入地探讨特征工程,这可以被视为一种艺术和科学。

我们将使用 OpenML 上可用的 Titanic 数据集(www.openml.org/search?type=data&sort=runs&id=40945)在本节中的示例。此数据集包含关于泰坦尼克号乘客的信息,包括人口统计数据、票务等级、票价以及他们是否在船沉没中幸存。

在您的 Vertex AI Workbench Notebook 实例的 JupyterLab 中的 Chapter-07 目录下,打开 feature-eng-titanic.ipynb 笔记本,并将内核选择为 Python (Local)。再次,通过选择单元格并在键盘上按下 Shift + Enter 来运行笔记本中的每个单元格。

在这个笔记本中,代码执行以下步骤:

  1. 首先,它导入必要的库。

  2. 然后,它加载数据集。

  3. 然后,它执行一些初步探索,以查看我们的数据集看起来如何。

  4. 最后,它工程新的特征。

让我们更详细地查看每个步骤,从导入所需的库、加载数据集和探索数据集开始。我们使用以下代码来完成这些任务:

import pandas as pd
import numpy as np
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OneHotEncoder
# Load the data
titanic_raw = pd.read_csv('./data/titanic_train.csv')
titanic_raw.head()

head() 方法的输出结果应类似于 表 7.4 中所示:

表 7.4:Titanic 数据集 head() 输出

表 7.4:Titanic 数据集 head() 输出

数据集中的字段如下:

  • survived:表示乘客是否幸存。这是一个二进制特征,其中 1 表示幸存,0 表示未幸存。

  • pclass(乘客等级):表示乘客票的等级。它有三个类别:1 表示头等舱,2 表示二等舱,3 表示三等舱。这也可以表示乘客的社会经济状况。

  • name:乘客的名字。

  • sex:乘客的性别;男性或女性。

  • age:乘客的年龄。有些年龄是分数,对于年龄小于 1 岁的乘客,年龄估计为分数。

  • sibsp: 乘客在泰坦尼克号上的兄弟姐妹和配偶总数。

  • parch: 乘客在泰坦尼克号上的父母和子女总数。

  • ticket: 乘客的票号。

  • fare: 乘客票价——即票的成本。

  • cabin: 乘客所住的船舱号。一些条目是 NaN,表示数据中缺少船舱号。

  • embarked: 乘客登船的港口。C 代表瑟堡;Q 代表昆士敦;S 代表南安普顿。

  • boat: 乘客被分配到的救生艇(如果乘客幸存)。

  • body: 身体编号(如果乘客未幸存且其尸体被找回)。

  • home.dest: 乘客的家乡和目的地。

假设我们想要使用这个数据集来构建一个模型,该模型可以根据乘客记录的信息预测乘客生存的可能性,让我们看看我们是否可以使用一些领域知识来评估哪些特征可能是对生存或死亡结果影响最大的贡献者,以及我们是否可以使用任何数据操作技术来构建更有用的特征。

Passenger Class 是一个可能的重要特征,我们可以从它开始考虑,因为头等舱和二等舱乘客的船舱位于更高的船层,这些船舱更靠近救生艇。

乘客的名字不太可能影响结果,他们的票号或登船港口也是如此。然而,我们可以构建一个名为 Title 的新特征,该特征从乘客的名字中提取出来,并可能提供有关社会地位、职业、婚姻状况和年龄的有价值信息,这些信息可能不会立即从其他特征中明显看出。我们还可以通过合并类似头衔,如 MissMs,并将高级头衔标识为 Distinguished 来清理这个新特征。执行此操作的代码如下:

# We first define a function to extract titles from passenger names
def get_title(name):
    if '.' in name:
        return name.split(',')[1].split('.')[0].strip()
    else:
        return 'Unknown'
# Create a new "Title" feature
titanic['Title'] = titanic['Name'].apply(get_title)
# Simplify the titles, merge less common titles into the same category
titanic['Title'] = titanic['Title'].replace(['Lady', 'Countess', 
    'Capt', 'Col', 'Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 
    'Dona'], 'Distinguished')
titanic['Title'] = titanic['Title'].replace('Mlle', 'Miss')
titanic['Title'] = titanic['Title'].replace('Ms', 'Miss')
titanic['Title'] = titanic['Title'].replace('Mme', 'Mrs')

接下来,让我们考虑 FareCabin 特征。这些特征可能与阶级有些相关,但我们将更详细地探讨这些特征。对于 Cabin 特征,我们可以提取另一个名为 CabinClass 的新特征,它更清楚地表示与每个条目相关的阶级。例如,我们可以通过从船舱号中提取第一个字母,用它来表示船舱阶级(例如,A、B、C 等),并将其存储在新的 CabinClass 特征中。执行此操作的代码如下:

# Create "CabinClass" feature
titanic['CabinClass'] = titanic['Cabin'].apply(lambda x: x[0])

让我们确保尽可能准确地表示票价,考虑到人们可能作为一家人一起旅行购买了票价。为此,我们可以创建一个名为 FamilySize 的新特征,它是 SibSpParch 特征的组合(为当前乘客添加一个额外的“1”),然后通过以下代码通过将 Fare 特征除以 FamilySize 特征来计算 FarePerPerson

titanic['FamilySize'] = titanic['SibSp'] + titanic['Parch'] + 1
# Create "FarePerPerson" feature
titanic['FarePerPerson'] = titanic['Fare'] / titanic['FamilySize']

一个人是独自旅行还是与家人同行也可能影响他们的生存机会。例如,家庭成员在试图到达救生艇时可以互相帮助。因此,让我们从 FamilySize 特征中创建一个特征,以确定乘客是否独自旅行:

# Create new feature "IsAlone" from "FamilySize"
titanic['IsAlone'] = 0
titanic.loc[titanic['FamilySize'] == 1, 'IsAlone'] = 1

接下来,让我们考虑年龄如何影响生存的可能性。非常年轻或年长的人,不幸的是,如果没有他人的帮助,生存的可能性可能较小。然而,在考虑年龄的这种情况下,我们可能不需要按年或按分数年进行粒度划分,也许将人们分组到年龄组可能更有效。在这种情况下,我们可以使用以下代码创建一个名为 AgeGroup 的新特征,该特征将按十年分组,例如 0-9、10-19、20-29 等:

# Create "AgeGroup" feature
bins = [0, 10, 20, 30, 40, 50, 60, 70, np.inf]
labels = ['0-9', '10-19', '20-29', '30-39', '40-49', '50-59', '60-69', 
    '70+']
titanic['AgeGroup'] = pd.cut(titanic['Age'], bins=bins, labels=labels)

我们还希望将分类特征转换为数值,使用独热编码,因为机器学习模型通常需要数值。我们可以这样做(我们需要为所有分类特征都这样做):

# Convert "Title" into numerical values using one-hot encoding
one_hot = OneHotEncoder()
title_encoded = one_hot.fit_transform(titanic[['Title']]).toarray()
title_encoded_df = pd.DataFrame(title_encoded, 
    columns=one_hot.get_feature_names_out(['Title']))
titanic = pd.concat([titanic, title_encoded_df], axis=1)

现在,让我们删除那些我们知道对预测生存可能性(以及我们编码的原始特征)没有价值的特征:

titanic = titanic.drop(['name', 'ticket', 'Title', 'cabin', 'sex', 
    'embarked', 'AgeGroup', 'CabinClass', 'home.dest'], axis=1)

然后,我们可以快速查看我们的更新数据集看起来像什么:

titanic.head()

head() 方法的输出结果应类似于 表 7.5 中所示:

表 7.5:更新数据集的 head() 方法输出

表 7.5:更新数据集的 head() 方法输出

在这一点上,我们有一个增强的数据集,其中包含可用于训练模型的工程化特征。重要的是要记住,我们在源数据集上执行的任何特征工程步骤,在用我们的模型进行预测时也需要考虑。这种在机器学习中的常见需求促使 Google Cloud 开发了一个名为特征存储的服务。我们将在下一节中探讨这一点。

Vertex AI 特征存储

在本章中,我们进行了大量的特征工程工作。请注意,我们执行了数据转换并创建了新的特征,因为我们有理由相信原始数据不足以训练一个适合我们业务案例的机器学习模型。这意味着模型在现实世界中看到的原始数据通常不会包含我们在训练期间对数据进行增强的部分。在完成所有这些工作之后,我们通常会保存我们已工程化的更新特征,以便模型在需要做出预测时可以引用它们。Vertex AI 特征存储就是为了这个目的而创建的。我们在 第三章 中简要提到了 Vertex AI 特征存储,在本节中,我们将更详细地探讨它是什么以及我们如何使用它来存储和提供训练和推理所需的特征。

Vertex AI Feature Store 简介

这里是来自 Google Cloud 文档的官方定义:

Vertex AI Feature Store 是一个托管、云原生的特征存储服务,它是 Vertex AI 的核心组成部分。它通过允许你在 BigQuery 表或视图中管理你的特征数据,简化了你的机器学习特征管理和在线服务流程。然后,你可以直接从 BigQuery 数据源在线提供特征。Vertex AI Feature Store 提供资源,允许你通过指定你的特征数据源来设置在线服务。然后,它作为元数据层与 BigQuery 数据源接口,以低延迟直接从 BigQuery 为在线预测提供最新的特征值。

除了存储和提供我们的特征外,Vertex AI Feature Store 还与 Google Cloud Dataplex 集成,提供特征治理能力,包括跟踪特征元数据(如特征标签和版本)的能力。在第十三章中,我们将深入探讨数据治理的重要性,并讨论如何将 Dataplex 用作构建强大治理框架的重要组件。

到目前为止,重要的是要强调,Vertex AI Feature Store 在 2023 年推出了一个全新的版本。因此,现在在 Google Cloud 中我们可以选择两种不同的特征存储服务版本,先前版本被称为 Vertex AI Feature Store(Legacy),而新版本则简单地称为 Vertex AI Feature Store。我们将在本章中讨论这两种版本,以及它们之间的一些主要区别。为了为后续章节的内容提供背景,我将简要描述在线与离线特征服务的话题。

在线与离线特征服务

简而言之,在线服务指的是交互发生在实时的情况——也就是说,请求实体或客户端发送请求并同步等待响应。在这种情况下,需要尽可能减少延迟。另一方面是离线服务,它指的是请求实体或客户端不同步等待响应,操作允许在更长的时间内发生。在这种情况下,延迟通常不是主要关注的问题。这个概念与在线和离线推理的主题密切相关,我们将在第十章中详细讨论。

在离线特征服务的情况下,Vertex AI Feature Store 允许我们直接在 Google Cloud BigQuery 数据集中存储和提供特征。这是一个相当方便的选项,因为许多 Google Cloud 客户已经使用 BigQuery 来存储和分析大量数据。

在在线特征服务的情况下,现在有两种方式可以在 Vertex AI 特征存储中提供我们的特征。第一种选择使用 Google Cloud Bigtable 来提供我们的特征。Google Cloud Bigtable 是一个专为服务大量数据(数以兆字节计的数据)而设计的强大服务。

在线特征服务的第二种选择,被称为优化在线服务,作为 Vertex AI 特征存储新版本的一部分被添加,允许我们创建一个专门针对以极低延迟提供特征数据的在线商店。

选择哪种选项取决于你的用例需求,特别是你是否需要处理非常大的数据量,或者你是否需要以极低延迟提供你的特征。成本也是这个决定的一个考虑因素,考虑到 Bigtable 解决方案通常比优化在线服务解决方案成本低。

在本章和随附的 Jupyter Notebook 中,我们将主要关注优化在线服务方法。以下部分将更深入地探讨在 Vertex AI 特征存储中设置在线特征服务的过程。

在线特征服务

从高层次来看,以下步骤是使用 Vertex AI 特征存储设置在线服务所需的。我们将在后续章节中详细阐述这些步骤:

  1. 在 BigQuery 中准备数据源。

    可选:通过创建特征组和特征在特征注册表中注册数据源。

  2. 设置在线商店并展示资源以呈现特征数据源。

  3. 从特征视图中提供最新的在线特征值。

让我们更详细地看看这些概念。

特征注册

当使用优化的在线服务方法时,我们可以选择一个步骤来在我们的 Vertex AI 特征注册表中注册我们的特征,该注册表也已被添加为 Vertex AI 特征存储新版本的一个组件。

这涉及到创建称为特征组的资源的过程,这些特征组代表特征列的逻辑分组,并与特定的 BigQuery 源表或视图相关联。反过来,特征组包含称为特征的资源,这些特征代表特征组所表示的数据源中包含特征值的特定列。

即使我们不将我们的 BigQuery 数据源添加到特征注册表中,我们仍然可以在网上提供特征,但请注意,特征注册表提供了额外的功能,例如存储与你的特征相关的历史时间序列数据。因此,我们将在这个章节的实践练习中使用特征注册表。现在,让我们更详细地看看设置在线特征服务的过程。

在线特征存储和特征视图

在我们设置好 BigQuery 中的特征数据后,并可选地在特征注册表中注册特征组和特征,我们需要设置两种主要类型的资源来启用在线特征服务:

  • 一个在线服务集群实例,也称为在线存储。请记住,我们可以使用 Bigtable 进行在线特征服务或新发布的优化在线特征服务选项。

  • 一个或多个特征视图实例,其中每个特征视图都与一个特征数据源相关联,例如在我们的特征注册表中的特征组(如果我们选择了在特征注册表中注册我们的特征选项),或者一个 BigQuery 源表或视图。

在创建特征视图后,我们可以配置同步设置,以确保我们的 BigQuery 中的特征数据与我们的特征视图同步。我们可以手动触发同步,但如果我们的源数据预计会随时间更新,我们还可以配置一个计划,定期从数据源刷新我们的特征视图内容。

现在我们已经涵盖了与 Vertex AI Feature Store 相关的许多重要概念,是时候深入其中,构建我们自己的特征存储库了!

构建我们的特征存储库

在本节中,我们将进行实际练习,以实现我们在前一节中学到的概念。

使用我们的 Vertex AI 笔记本构建特征存储库

第五章中我们创建的 Vertex AI 笔记本实例中,我们可以执行以下步骤来构建特征存储库:

  1. 选择打开 JupyterLab以打开我们在第五章中创建的 Vertex AI 笔记本实例。

  2. 当 JupyterLab 打开时,您应该会在笔记本中看到一个名为 Google-Machine-Learning-for-Solutions-Architects 的文件夹。

  3. 双击该文件夹,然后在该文件夹内双击 Chapter-07 文件夹,最后双击 feature-store.ipynb 文件以打开它。

  4. 在出现的选择内核屏幕上,选择Python (Local)

  5. Shift + Enter运行笔记本中的每个单元格,并阅读 Markdown 和注释中的说明,以了解我们在做什么。

现在您已经按照笔记本中的步骤执行了特征选择和工程,构建了特征存储库,并使用了一些特征来训练模型,让我们看看这些特征在推理时间如何被使用。在后面的章节中,您将学习如何部署模型进行在线推理并向这些模型发送推理请求,但就目前而言,我将从概念层面解释这个过程。

特征在在线推理过程中的使用

在本节中,我将使用我们在配套的 Jupyter Notebook 中构建的出租车费用预测模型用例作为示例,来解释我们如何在推理过程中使用特征存储库中的特征。我们将查看过程中的每个步骤。

结合实时和预计算特征

例如,当前接车时间(pickup_datetime)、pickup_locationpassenger_count等特征可以在每次出租车行程开始时实时获取。

我们的特征存储还包含预计算的特征,例如历史行程距离、每英里费用、接车时间和位置。这些特征可以根据当前行程的上下文从可用的实时特征中选择。

为了获取预计算的特征,处理出租车行程的应用程序可以向我们的特征存储发送请求,传递标识符,如当前时间和位置,之后特征存储可以返回这些标识符的相关特征值。

预测数据组装

在这一点上,我们可以将实时数据和获取到的特征组装成一个与模型期望格式匹配的特征向量,然后将组装好的特征向量传递给模型。模型随后处理这个向量,并输出一个费用预测,该预测随后可以在应用程序中显示。

干得好!你已经成功在 Google Cloud 上构建了一个特征存储。让我们总结一下本章中我们讨论的所有内容。

摘要

在本章中,我们讨论了我们的特征质量以及数据集中特征与观察值的比率如何影响我们的算法从数据中学习。我们讨论了当我们的数据集中包含许多特征时可能出现的挑战,以及如何通过使用诸如降维等机制来解决这些挑战。我们深入探讨了降维技术,如特征选择和特征投影,包括 PCA、LDA 和 t-SNE 等算法,并查看了一些使用这些算法的实例,通过实际操作活动来实现。

接下来,我们深入探讨了特征工程技术,其中我们增强了一个源数据集以创建包含原始数据集中不易获得的信息的新特征。最后,我们深入探讨了 Vertex AI 特征存储,了解我们如何使用该服务来存储和提供我们的工程特征集。

在下一章中,我们将把我们的关注点从模型所学习的数据集和参数转移到讨论影响模型学习方式的不同类型的参数。在那里,我们将探讨超参数和超参数优化的概念。

第八章:超参数和优化

我们在 第二章 中介绍了超参数和超参数优化(或调整)的概念。在本章中,我们将更详细地探讨这些概念,并使用 Google Cloud 产品,如 Vertex AI Vizier,来定义和运行超参数调整作业。

按照我们建立的模式,我们将首先介绍本章实践活动中所需的先决条件。然后,我们将介绍与本章内容相关的一些重要基本概念,最后,我们将进行实践操作,教您如何在现实场景中应用这些概念。

本章涵盖了以下主题:

  • 先决条件和基本概念

  • 什么是超参数?

  • 超参数优化

  • 实践:在 Vertex AI 中进行超参数调整

让我们首先回顾本章的先决条件。

先决条件

在本章中执行主要活动之前,需要完成本节中的步骤。

启用 Artifact Registry API

我们将创建 Docker 镜像,以便与 Google Cloud Vertex AI Vizier 服务一起运行我们的自定义代码。Google Cloud Artifact Registry 是一个完全管理的工件存储库,我们可以用它来存储我们的容器镜像。它可以被视为下一代 Google Cloud Container Registry (GCR),可以用来存储诸如 Java JAR 文件、Node.js 模块、Python 轮子、Go 模块、Maven 工件和 npm 包(除了已经支持在 GCR 中的 Docker 镜像)等工件。

要启用 Artifact Registry API,请执行以下步骤:

  1. 在 Google Cloud 控制台中,导航到 Google Cloud 服务菜单APIs & ServicesLibrary

  2. 在搜索框中搜索 Artifact Registry

  3. 在结果列表中选择 API。

  4. 在显示 API 信息的页面上,点击 启用

接下来,让我们设置本章步骤所需的权限。

创建 AI/ML 服务账户

第六章 中,我们创建了一个服务账户来使用 Google Cloud 的数据处理服务。在本章中,我们将创建一个服务账户,该账户将用于在 Google Cloud Vertex AI 中管理资源时的超参数调整作业。

执行以下步骤以创建所需的服务账户:

  1. 在 Google Cloud 控制台中,导航到 Google Cloud 服务菜单IAM & Admin服务账户

  2. 选择 创建 服务账户

  3. 对于服务账户名称,输入 ai-ml-sa

  4. 点击 创建 并继续

  5. 在标题为 授予此服务账户访问项目 的部分中,添加 图 8*.1 中显示的角色。

图 8.1:AI/ML 服务账户权限

图 8.1:AI/ML 服务账户权限

  1. 选择 完成

我们的服务账户现在已准备好在本章的后续部分使用。

现在我们已经涵盖了先决条件,让我们讨论一些在执行本章中的动手活动之前我们需要理解的概念。

概念

本节描述了支撑我们在本章中将要讨论的实践活动的概念。

本章中使用的模型评估指标

我们已经在之前的章节中讨论了模型评估指标的话题。我们首先在第一章中介绍了这个概念,其中我们简要讨论了如回归用例的均方误差MSE)和分类用例的准确率等指标。在第五章中,我们使用了 scikit-learn 中的函数来计算我们创建的模型的一些这些指标,并在该章节的末尾建议将查找更多指标作为补充学习活动。

在本章中,我们将为分类用例训练模型,并介绍一些额外的指标来评估我们的模型。我们将使用的主要指标是称为AUC ROC的东西,它代表接收者操作特征曲线下的面积。听起来可能很多,但别担心,我们将在本节中更详细地解释这个指标。为了做到这一点,我们首先需要介绍一些概念和用于计算 AUC ROC 的更简单的指标。

注意,在二元分类用例中,模型需要预测数据集中每个数据点的两种可能结果之一,即真或假,也称为积极消极。它们通常用 1 和 0 表示。

模型很少完美,所以它们有时会犯错误。让我们看看二元分类模型预测的可能结果。

真阳性、假阳性、真阴性和假阴性

二元分类模型的预测通常有四种可能的结果:

  • 当我们的模型预测某事物为真(或积极)而实际上它是真(或积极)时,我们称之为真阳性TP

  • 当我们的模型预测某事物为真(或积极)但实际上它是假的(或消极)时,我们称之为假阳性FP

  • 当我们的模型预测某事物为假(或消极)而实际上它是假(或消极)时,我们称之为真阴性TN

  • 当我们的模型预测某事物为假(或消极)但实际上它是真的(或积极)时,我们称之为假阴性FN

让我们更详细地看看这些结果是如何相互关联的。

混淆矩阵

前述概念可以通过称为混淆矩阵的视觉方式来表示,这在表 8.1中有演示。

预测为消极预测为积极
实际消极TNFP
实际积极FNTP

表 8.1:混淆矩阵

我们可以使用前面的概念来计算衡量模型在尝试准确识别数据集中的正或负数据点时表现如何的指标。我们将在下面定义这些指标。

真阳性率

真阳性率TPR)表示模型正确预测为正例的数据点总数与数据集中所有正例数据点的总数之比,包括模型错误地预测为负的数据点(即模型说它们是负的,尽管它们是正的,这意味着它是假阴性)。

计算 TPR 的公式是 TPR = TP / (TP + FN)

TPR也被称作召回率灵敏度

假阳性率

假阳性率FPR)是假阳性(模型错误地将负例预测为正例的数量)与假阳性加上真负例(模型正确预测为负例的数量)之和的比率。换句话说,它是实际负例中被错误地识别为正例的比例。

计算 FPR 的公式是 FPR = FP / (FP + TN)

TPR也被称作漏报率

真阴性率

真阴性率TNR)的描述方式与 TPR 类似,只是将正负进行了交换。也就是说,TNR 表示模型正确预测为负的数据点总数与数据集中所有负例数据点的总数之比,包括模型错误地预测为正的数据点(即模型说它们是正的,尽管它们是负的,这意味着它是假阳性)。

计算 TNR 的公式是 TNR = TN / (TN + FP)

TPR也被称作特异性

假阴性率

假阴性率FNR)的描述方式与 FPR 类似,只是将正负进行了交换。它是错误预测的负观察值与实际正例的比率。换句话说,它是实际正例中被错误地识别为负例的比例。

计算 FNR 的公式是 FNR = FN / (FN + TP)

精确度

精确度是正确预测的正观察值与总预测正例的比率。换句话说,在模型预测为正的所有实例中,有多少实际上是正的?

计算 FNR 的公式是 P = TP / (TP + FP)

当我开始学习所有这些内容时,我 wonder 为什么有这么多不同的指标来衡量二元分类模型性能的略微不同的方面。

至少有以下几个原因:

  • 统计基础:这些指标是二元分类用例中统计分析的自然结果

  • 试错法:每个指标可能比其他指标更重要,这取决于用例的预期结果。

考虑以下第二个点的例子。如果你试图预测信用卡欺诈,你可能希望最大化你模型的敏感性,这将尽可能减少错误否定数,即使这最终导致更多的错误肯定。换句话说,即使交易并非欺诈,错误地将交易标记为欺诈也比错误地允许欺诈交易发生要好。

另一方面,如果你正在创建垃圾邮件过滤器,你可能更愿意允许一些垃圾邮件意外地进入你的收件箱(错误否定),而不是将有效邮件标记为垃圾邮件(错误肯定)。

前述指标通常过于简单,无法独立使用,因此你通常会想要找到一个更复杂的指标组合,以提供更平衡的结果。即使在信用卡欺诈用例中,太多的错误肯定也会对信用卡客户造成干扰和挫败。这种平衡取决于你指定的阈值(介于 0 和 1 之间),以确定某物是正还是负。例如,低阈值会导致更多的正例,而高阈值会导致更多的负例。这使我们转向更高级的指标,如 F1 分数和 AUC ROC,我们将在下面描述。

F1 分数

F1 分数定义为精确率和召回率的调和平均数,其计算公式如下:

F1 = 2 * (精确率 * 召回率) / (精确率 + 召回率)

F1 分数特别有用,当你更关心正类,并且想要平衡精确率和召回率时。

AUC ROC

要理解 AUC ROC,我们首先分解其名称。接收者操作特征(ROC)是一个相当复杂的名称,指的是通过在不同分类阈值设置下绘制 TPR 与 FPR 的曲线生成的曲线,如图图 8.2所示。

图 8.2:AUC ROC

图 8.2:AUC ROC

曲线下的面积(AUC)是 ROC 曲线下从(0, 0)到(1, 1)的整个二维面积,如图图 8.2中的蓝色区域所示。AUC 提供了对所有可能的分类阈值的性能的汇总度量,目标是最大化曲线下的面积,因此,在最佳情况下,曲线会延伸到左上角,填满整个图表。

让我们更详细地看看如何解释 AUC ROC 分数值:

  • AUC ROC 分数为 1.0 表示模型能够完美地区分所有正负数据点,在这种情况下,它没有错误否定和错误肯定(即没有错误)。

  • AUC ROC 得分为 0.5 意味着模型无法准确区分正负数据点,其表现不如随机猜测。

  • AUC ROC 得分低于 0.5 意味着模型的表现不如随机猜测,将负样本预测为正样本,将正样本预测为负样本。

理解这些指标很重要,因为它们通常是我们的机器学习算法试图优化的目标。在下一节中,我们将讨论超参数和超参数调整,我们将看到这些目标指标构成了我们调整工作的基本目标。

什么是超参数?

正如我们在第二章中讨论的那样,超参数是定义我们的模型训练作业如何运行的参数。它们不是模型从数据集中学习的参数,而是与模型训练过程执行相关的外部配置选项。它们影响最终模型的性能,并代表模型的高级属性,如复杂性或它应该学习的速度。

以下是我们在这本书中已经讨论过的超参数的例子:

  • 在我们第二章关于超参数的讨论中,我们介绍了学习率、训练轮数等例子。

  • 第五章中,我们将聚类数量作为 K-means 算法的超参数,并为基于树的模型配置了超参数,例如树的最大深度。

  • 我们在第七章中讨论了正则化,正则化参数是超参数的另一个例子。

对于不同类型的算法,还有许多其他类型的超参数,随着我们阅读本书的进展,我们将遇到更多。

超参数优化

我们如何知道应该使用哪些类型的超参数以及它们的值是多少?超参数可以根据领域知识、经验或试错来选择,但为了最有效地选择最佳超参数,我们可以使用称为超参数优化或超参数调整的过程,这是一个可以通过不同的机制实现的系统过程,我们将在下一节中讨论。最终,超参数优化的目标是调整模型超参数,以实现最佳性能,这通过在验证集上运行模型来衡量,验证集是我们源数据集的一个子集。

优化超参数值的方法

第二章中,我们描述了超参数调整机制,如网格搜索、随机搜索和贝叶斯优化,这里简要回顾一下:

  • 网格搜索:这是对整个超参数空间(即尝试所有可能的超参数值组合)的穷举搜索。这通常是不切实际的,并且计算上过于昂贵。

  • 随机搜索:随机搜索方法使用一种子采样技术,其中为每个训练作业实验随机选择超参数值。这不会导致测试每个超参数的所有可能值,但它通常是一种非常有效的方法来找到一组有效的超参数值。

  • 贝叶斯优化:这使用一种优化算法,并且是 Google Cloud Vertex AI 提供的一项托管服务。

以下是一些在行业中存在的额外超参数调整机制:

  • 基于梯度的优化:这种方法使用梯度下降,我们已经在本书的早期部分对其进行了深入探讨。这些方法通常用于训练神经网络。本书后面将提供单独的部分,详细描述如何训练神经网络。

  • 进化算法:这些是基于种群的优化算法,其模型松散地基于自然选择的过程。术语“基于种群”指的是构建一个潜在候选者池(或种群)的做法。在这种情况下,种群中的每个候选者代表一组不同的超参数,候选者根据其验证性能进行评估。表现最好的候选者随后被选中以产生下一代的“后代”。这些算法也更有可能被用于高级用例,如神经网络,其中超参数搜索空间可能很大且复杂,评估单个解决方案的性能可能很昂贵。

  • 自动化机器学习AutoML系统:我们在前面的章节中讨论了 AutoML 的过程。它可以用于自动化整个机器学习生命周期,包括超参数调整。

在任何情况下,一般的调整过程如下:

  1. 将源数据集分为三个子集:

    1. 训练数据集:用于训练模型

    2. 验证数据集:用于在调整过程中评估每个超参数组合

    3. 测试数据集:用于测试最终模型

  2. 选择我们想要创建的机器学习模型类型(例如,线性回归,决策树,神经网络)。这决定了哪些特定的超参数可以被调整。

  3. 设置初始的超参数范围或网格及其值。这可以基于领域知识或研究,或者我们可以从随机广泛的范围开始,并在一段时间内对其进行细化。

  4. 选择一种搜索模型超参数空间的方法(例如,随机搜索,贝叶斯优化)。

  5. 对于每个超参数组合,将模型拟合到训练数据,并通过测试它来评估其性能,与验证数据比较,并测量所选模型类型的适当目标指标(例如,回归的 MSE,二分类的 AUC ROC)。

  6. 一旦评估了所有组合,选择导致最佳模型性能的超参数值组合。

  7. 使用这些超参数训练一个最终模型,并使用test数据集测试该模型,以确认模型对未见数据的泛化能力。

注意,找到最佳的超参数和值组合可能需要迭代数百次甚至数千次概述的步骤,这将非常耗时,或者可能无法手动执行。这就是为什么通常需要自动化步骤的超参数调优作业。

现在我们已经涵盖了与超参数调优相关的许多重要理论概念,是时候将我们的重点转移到这些概念的实际应用上了。

动手实践:在 Vertex AI 中进行超参数调优

考虑到 Google Cloud Vertex AI 提供了使我们能够轻松实现数据科学项目生命周期中每一步的工具,这为我们提供了一个完美的环境来将我们的知识付诸实践并开始实施超参数调优作业。实际上,正如我们之前提到的,Vertex AI 提供了一个名为 Vizier 的工具,专门用于自动化超参数调优作业,我们将在下一部分更详细地探讨。

Vertex AI Vizier

Vertex AI Vizier 是 Google Cloud 中的一项服务,它自动化了我们在本章前一部分概述的超参数调优过程。在本节中,我们将讨论 Vertex AI Vizier 服务使用的某些术语,并描述其工作的一些细节。然后,我们将在我们的动手活动中实际使用它来实施超参数调优作业。

Vertex AI Vizier 术语

Google Cloud 使用一些特定于 Vertex AI Vizier 服务的术语。我们在此简要描述一些重要术语,并将它们与我们本章早期讨论的通用概念联系起来。

研究、研究配置和试验

在 Vertex AI Vizier 中,一个研究代表我们试图实现的总体目标以及实现该目标所涉及的所有步骤和其他细节。例如,如果我们查看本章“优化超参数值的方法”部分概述的一般调优过程步骤,一个研究封装了所有这些步骤。一个研究配置是包含我们研究所有细节的实际配置对象,例如我们希望研究优化的目标指标、要测试的参数以及要使用的参数搜索方法。

试验是我们研究中的一个单独实验,或者在调整过程中的单个迭代(即使用特定超参数值的单个训练和评估作业)。当朝着我们指定的目标努力时,研究将运行许多试验。

在您创建了一个研究之后,Vertex AI Vizier 将自动开始运行试验。在每次测试中,将使用不同的一组超参数。Vertex AI Vizier 将跟踪每次运行的成果,并利用这些知识来选择最佳的超参数集(它将在找到最佳的超参数集后自动停止运行试验)。Vizier 还将总结所有试验,并根据与目标指标相关的表现对它们进行排名。然后,我们可以使用排名最高的试验中的超参数来训练我们的机器学习模型。

既然我们已经了解了术语,让我们深入到实际操作活动中!

用例和数据集

在本节中,我们将使用 Kaggle 上可用的信用卡欺诈检测数据集(www.kaggle.com/datasets/mlg-ulb/creditcardfraud)开发一个 XGBoost 模型来检测信用卡欺诈。

实现

我们将在本章的实际操作活动中使用 Jupyter Notebook,并将自定义笔记本的内容,因此我们将使用一个用户管理的笔记本实例。我们可以使用在第七章中创建的相同的 Vertex AI Workbench 用户管理笔记本实例。请在该笔记本实例上打开 JupyterLab。在屏幕左侧的目录浏览器中,导航到Chapter-08目录并打开vizier-hpo.ipynb笔记本。您可以选择Python (Local)作为内核。同样,您可以通过选择单元格并在键盘上按Shift + Enter来运行笔记本中的每个单元格。除了相关代码外,笔记本还包含 Markdown 文本,描述了代码的功能。

我们的超参数调整作业是如何工作的

使用 Vertex AI Vizier 进行超参数调整涉及在模型中实现的几个步骤。让我们看看这个过程中的关键步骤:

  1. 首先,我们创建一个训练应用程序,它由一个 Python 脚本组成,该脚本使用给定的超参数训练我们的模型。此脚本还必须跟踪和报告在验证集上测试模型时的性能,以便 Vertex AI Vizier 可以使用这些性能指标来确定最佳的超参数。因此,我们在代码中使用cloudml-hypertune Python 库定期将超参数调整指标报告回 Vertex AI。

  2. 接下来,我们为超参数调优作业创建一个配置对象,该对象指定了要调优的超参数及其可能值的范围,以及我们想要优化的目标指标(在我们的案例中,我们使用 AUC ROC,在代码中简单地称为auc)。在此阶段需要注意的一个重要事项是,我们包含的超参数越多,需要运行的试验组合就越多。这可能会导致我们的调优作业需要额外的时问和计算资源(因此成本也会增加)。因此,在可能的情况下,最好使用领域知识来确定我们希望调优作业关注的超参数。我们还可以使用超参数调优作业配置中的maxTrials变量来控制试验的数量。

    可以理解的是,并不总是可以使用领域知识来缩小参数搜索空间,我们通常需要在超参数调优作业输出的质量、运行它们所需的时间和成本之间找到一个平衡点。例如,长时间运行调优作业可能会让我们尽可能接近找到完美的超参数值集合,但缩短运行时间可能会得到满足我们用例需求的结果。

  3. 我们超参数调优实现中的最后阶段是使用 Vertex AI Vizier 客户端库将超参数调优作业提交给 Vertex AI,然后 Vertex AI 使用不同的超参数值集合运行我们的训练应用程序,并找到最佳值。

使用我们的超参数调优作业的结果

当然,我们运行超参数调优作业并不是为了好玩(尽管这也是很有趣的!)!当我们的调优作业找到最佳的超参数集合时,我们将想要访问和审查它们,并且通常我们希望使用它们来训练我们模型的最终版本。

通过 Google Cloud 控制台访问结果

当我们在笔记本中运行超参数调优作业时,我们的代码输出将显示一个链接,该链接将使我们能够查看 Google Cloud 控制台中调优作业的状态。在该链接中最重要的内容是调优作业执行的试验列表,它看起来类似于图 8**.3

图 8.3:超参数调优试验

图 8.3:超参数调优试验

在 Google Cloud 控制台中我们的超参数调优试验列表中,我们可以看到每个试验的 AUC 指标,以及相关的试验 ID(如图 8.3 左边的框内所示),我们还可以看到每个试验使用的超参数值(如图 8.3 右边的框内所示)。我们可以点击auc列标题中的箭头符号来按升序或降序排序该列。在我们的情况下,我们希望按降序排序,因为我们希望最高分出现在顶部。这告诉我们哪个试验具有导致性能最佳模型的超参数。在图 8.3 中,您可能会注意到至少前五个试验都具有相同的 AUC 分数。这是常见的,因为可能有多个不同的超参数值组合可以产生相同的指标分数。您可以使用屏幕右下角的箭头浏览额外的试验页面,您将看到其他导致 AUC 分数较低的试验。

编程访问结果

虽然在 Google Cloud 控制台中查看我们的超参数调优作业结果很有用且有趣,但我们可能不希望手动复制粘贴它们到最终的训练作业中,以创建我们的结果模型。

幸运的是,我们可以通过 Vertex API 编程访问所有这些细节,并且我们可以使用 Vertex 客户端库从我们的开发环境中完成这项操作。在我们的笔记本中,在调优作业完成后,我们可以继续进行笔记本中的其他活动,这将向您展示如何访问和使用调优作业产生的最佳超参数值集。然后我们使用这些超参数值在我们的笔记本中训练一个新的模型,然后我们最终将该模型与测试数据集进行测试,计算并显示最终的 AUC 分数。请注意,当我运行这个程序时,我得到了 0.9188 的 ROC-AUC 分数,这相当不错!

干得好,您现在已经学到了很多关于超参数调优主题的知识,您应该准备好将所学知识应用到其他类型的机器学习问题中。让我们总结一下本章我们学到了什么。

概述

在本章中,我们深入探讨了机器学习中重要的目标指标概念。我们详细介绍了用于评估二元分类模型的最受欢迎的许多指标,例如精确率、召回率、F1 分数和 ROC AUC。然后我们转向讨论超参数优化,包括该领域的一些重要理论信息,例如可以用来搜索最佳超参数集及其相关值的多种方法。这也提供了一些关于为什么由于可能需要大量试验,手动高效地进行超参数调优可能非常困难或甚至不可能的见解。

然后,我们深入了解了 Google Cloud Vertex AI Vizier 服务,该服务可以用来自动为我们进行超参数调优。我们在 Vertex AI 上的 Jupyter Notebook 中进行了实际操作,并使用 Vizier 自动找到用于训练信用卡欺诈检测模型的最佳超参数集。

接下来,我们使用超参数调优作业的输出训练了我们模型的最终版本,然后我们用测试数据集评估了该模型。

在下一章中,我们将开始探索比线性回归和决策树等更简单的机器学习算法更深入的内容,并深入到人工神经网络(ANNs)的领域。让我们继续前进,发现这个概念和技术类别中的迷人之处。

第九章:神经网络与深度学习

在本章中,我们将讨论机器学习ML)中的神经网络NN),通常被称为人工神经网络ANNs。我们将介绍这个科学领域中的许多重要主题,包括导致人工神经网络发展的基本概念,以及它们应用的相应用例。在此阶段,重要的是要注意,术语深度学习DL)指的是使用深度神经网络DNNs)实现的机器学习。我们将在本章后面解释“DNN”这个术语。

我们还将介绍一些工具和框架,这些工具和框架使我们更容易创建神经网络,例如 TensorFlow 和 Keras,我们将在本章后面的动手活动中使用这些工具来构建神经网络。最后,我们将讨论不同类型的神经网络架构、神经网络实现中的常见挑战以及优化我们神经网络架构的一些实践。

作为一个旁注,我第一次开始学习关于人工神经网络(ANNs)是在大学期间,我记得我对这个概念非常着迷,因为我也有浓厚的兴趣了解人脑是如何工作的。尽管神经网络的概念确实是基于人类大脑理论运作的,但在本章中,我们将区分炒作和现实,并专注于这项技术的实际、数学描述。让我们首先覆盖这个领域的一些重要概念。

本章涵盖了以下主题:

  • 神经网络和深度学习概念

  • 在 TensorFlow 中实现多层感知器MLP

  • 神经网络架构、挑战和优化

神经网络和深度学习概念

在本节中,我们讨论在神经网络和深度学习背景下理解的重要概念。我们首先讨论人工神经网络是如何与我们理解人脑相联系的。

神经元和感知器

虽然人工神经元和生物神经元(如人类大脑中发现的)之间的联系经常被过分强调,但它们之间存在一个概念上的联系,这有助于我们形成它们如何工作的心理模型。生物神经元通常由三个主要部分组成,如图图 9.1所示:

图 9.1:神经元(来源:https://www.flickr.com/photos/187096960@N06/51173238594)

图 9.1:神经元(来源:www.flickr.com/photos/1870…

细胞体是神经元的核心部分,其中包含细胞核和其他重要组件。树突(来自希腊语单词“dendron”,意为“树”)是从细胞体分支出来的结构。它们接收来自其他神经元的信息并将这些信息传输到细胞体。最后,轴突是从细胞体延伸出来的长管状结构,将信息发送到其他神经元的树突(通过称为突触的界面)。关于神经元的生物学,我们就讲到这里;我在这里简化了很多,因为我们只需要为与人工神经网络(ANNs)进行比较提供高级背景,但接下来我们需要了解的是它们是如何传输信息的,这在大体上如下(再次,为了相关背景简化)。

当一个神经元从另一个神经元接收信号时,这会导致神经元细胞膜(神经元内外电压差)中所谓的电势发生变化,从而触发所谓的动作电位,这是一种沿着轴突传播的电气脉冲。当它到达轴突的末端时,它会触发神经递质的释放,这些神经递质是化学信使。这些神经递质穿过所谓的突触间隙(神经元之间的微小空间)并绑定到下一个神经元的树突上的受体,这种结合可以触发或抑制第二个神经元中的新动作电位。

好的——在前两段中,我们刚刚介绍了很多生物学术语,但当我们想要将概念与人工神经网络(ANNs)进行比较时,这些概念是非常重要的。考虑到这一点,让我们继续前进,讨论人工神经网络是如何构建的,从它们最基本的概念,即感知器开始,我在第一章中简要提到了感知器,当时我总结了机器学习(ML)演变的各个里程碑。

感知器可以被视为最简单类型的人工神经网络之一,也是更大、更复杂网络的构建块。它在 20 世纪 50 年代末由弗兰克·罗森布拉特(Frank Rosenblatt)开发,基本上是一个二元分类器,它使用一组应用于输入特征的权重将输入 X(一个向量)映射到一个输出值 f(x)(一个单一的二元值)。

为了更详细地了解这个过程,让我们深入探讨感知器是如何工作的,这可以通过以下步骤来概括:

  1. 感知器接收输入值,这些值可以是数据集中的特征或属性。

  2. 每个输入都有一个与之相关的权重,它表示其重要性。权重通常在训练开始时随机给出,然后在训练过程中进行细化。我们将在稍后详细讨论这一点。

  3. 偏置单元也被添加到感知器模型中,以增加模型的灵活性。这与我们在本书后续章节中讨论的公平性背景下的偏差主题无关;这仅仅是一个数学技巧,它为在尝试产生所需输出时改进我们模型性能提供了一个额外的控制机制。

  4. 接下来,每个输入乘以其相应的权重,并将所有这些乘法的结果(以及偏置)相加(结果是一个加权总和),所以这仅仅是基于权重和偏置值的输入的线性变换

  5. 这个加权的总和随后通过一个激活函数,该函数产生一个二进制输出。我们将在稍后更详细地解释激活函数,但从高层次来看,对加权总和执行非线性变换,并将该变换的结果作为感知器的输出。对于单个感知器来说,一个简单的例子是,如果输入的加权总和大于一个阈值值,感知器将输出 1,或者如果加权总和小于或等于阈值,它将输出 0,所以这基本上是逻辑回归过程的实现。

    从数学上讲,这可以写成以下形式:

    • 如果 ∑ (权重 * 输入) + 偏置 > 0,输出 1

    • 如果 ∑ (权重 * 输入) + 偏置 ≤ 0,输出 0

图 9.2提供了感知器工作原理的视觉表示:

图 9.2:感知器(来源:https://commons.wikimedia.org/wiki/File:Perceptron-unit.svg#file)

图 9.2:感知器(来源:commons.wikimedia.org/wiki/File:P…

图 9.2 中,图的最左侧的 x 值代表感知器的输入。x0 输入是偏置,而 x1 到 xn 代表来自我们数据集的输入特征。w 值代表权重,绿色圆圈内的希腊字符(sigma 和 phi)代表激活函数,而最右侧的希腊字符(omicron)代表输出。

需要理解的重要概念是,权重和偏置的值是我们感知器模型试图学习的。也就是说,我们的模型试图找出每个特征的最佳权重,从而在执行我们描述的线性和非线性变换(结合偏置)后,得到一个尽可能接近目标结果的模式。如果我们回想一下我们在前几章中创建的更传统的 ML 模型,例如线性回归,我们可能会记得我们的模型试图找出每个特征的系数的最优值,以实现预期的结果。在感知器和 ANN 中,权重(以及偏置)是我们模型试图优化的系数。

在理解了感知器的工作原理之后,让我们讨论如何使用它们来构建更复杂的 NN。

MLPs 和 NNs

虽然感知器是一个简单而强大的算法,但它只能模拟线性可分函数。这意味着如果我们的数据不是线性可分的(也就是说,我们无法画一条直线来分离类别),感知器将无法准确地区分数据集中的类别。为了克服这一限制,可以通过层将多个感知器组合起来形成 MLP,它有可能解决非线性问题。MLP 是 NN 的一种形式,所以基本上,当我们以顺序方式(即某些感知器的输出成为其他感知器的输入)组合多个感知器时,我们形成了一种 ANN。

考虑到感知器可以被看作是一种人工神经元,从现在开始我们将“感知器”和“人工神经元”(有时简称“神经元”)这两个术语交替使用。

总结与生物神经活动的比较

正如我们之前所讨论的,神经元从感觉器官或其他神经元接收输入,并且根据产生的电势,动作电位会导致神经元向其他神经元发送(或不发送)消息。

同样,感知器(或人工神经元)从我们的数据集接收输入,或者如果我们是在 NN 中将感知器串联起来,则从其他人工神经元接收输入。然后,根据这些输入及其权重和偏置的线性组合,激活函数将影响感知器的输出,该输出可以用作网络中另一个感知器的输入。

接下来,让我们更详细地探讨 NN 的典型结构,并介绍 NN 中重要的概念。

NN 中的层

当人工神经元组合在一起形成 NN 时,它们不是随机连接的,而是使用层这一概念以结构化的方式进行连接,如图图 9.3所示:

图 9.3:NN 层

图 9.3:NN 层

图 9**.3所示,神经网络的层通常分为三种不同类型:

  1. 输入层,正如其名称所暗示的,是我们输入进入神经网络的方式。它是网络中的第一层,当然是。

  2. 隐藏层,位于输入层和输出层之间。它们的工作是将输入转换为输出层可以使用的东西。术语“隐藏”只是意味着它们不与外界接口(它们既不是网络的输入也不是输出)。我们通常无法控制或直接与它们交互;它们学会独立表示数据。隐藏层的数量以及每个隐藏层中的神经元数量定义了神经网络的复杂性和结构。

  3. 输出层,正如其名称所暗示的,展示了我们的神经网络的输出,这通常是某种预测。

除了隐藏层的数量和每个隐藏层中的神经元数量外,层之间的确切连接方式取决于神经网络的架构。我们将在本章后面讨论不同类型的常见神经网络架构,但通常发生的情况是,我们的输入数据被输入到神经网络的输入层后,网络后续层中的每个神经元的行为类似于我们在本章前面描述的感知器。

例如,每个输入都被分配一个表示其重要性的权重。这些权重通常用随机值初始化,然后在学习过程中进行细化。输入乘以其相应的权重,并将结果相加,再加上一个偏差值。然后将总和作为后续层(即从第一个隐藏层开始)中神经元的激活函数的输入。重要的是要记住,每个神经元的权重和偏差将是不同的。因此,尽管每个神经元在第一个隐藏层看到的是完全相同的数据输入,但每个神经元对数据的反应将因各种权重和偏差的不同影响而有所不同。

需要理解的是,每一层的激活函数的输出将作为网络后续层中再次执行的过程的输入。因此,我们刚才描述的过程将在每一后续层中执行,但与我们的原始数据集在每一层作为输入不同,每一后续层将使用前一层的激活值作为输入。这意味着在信息通过我们的网络传递时,正在实施多个转换,这也是神经网络之所以强大的原因。

让我们确保我们对这个过程有清晰的理解。以第二个隐藏层为例,过程如下:

第一层的每个激活函数输出都被分配一个表示其重要性的权重。这些权重通常用随机值初始化,然后在学习过程中进行优化。激活函数的输出乘以相应的权重,并将结果相加,再加上偏差值。然后将这个总和作为下一层神经网络中神经元的激活函数的输入。这个过程在每个层中重复,直到我们到达网络的最终输出层。

注意

不同的神经网络架构可以使用不同的方式在网络中传播信息。在本章中,我们将更详细地讨论一些常见的神经网络架构类型,但我们之前描述的内容可以被认为是人工神经网络工作原理的基石。

你也可能听到“DNN”这个术语。传统上,任何至少有两个隐藏层的神经网络都被认为是深度神经网络(DNN)。

网络中某一层的神经元激活对下一层神经元激活的影响,使我们回到了与人类大脑的类比,其中某些神经元的放电可以引起其他神经元的放电,产生各种不同的交互组合,从而产生更复杂的高级功能。然而,我们必须对这个类比持保留态度,因为即使是最复杂的 ANN 也包含成千上万的神经元,而人类大脑有数十亿个神经元,每个神经元能够执行比人工神经元相对简单的数学变换更复杂的函数。

现在我们已经讨论了信息如何在人工神经网络(ANN)中传播,让我们更深入地探讨 ANN 是如何学习的,为此,我们必须引入反向传播的概念。

反向传播

我们在上一节中描述的内容可以被称为前向传播,其中信息在我们的神经网络(NN)中从一层传播到另一层。为了讨论反向传播,让我们回顾一下在这本书中我们之前学到的关于监督机器学习(SML)算法是如何工作的内容。记住,我们使用标签来描述数据集中每个数据点的特征。当我们训练一个模型时,模型试图学习数据集中特征之间的模式,这将帮助它准确地预测每个数据点的标签。然后我们使用损失函数来计算我们的模型预测与正确标签之间的差距;模型训练活动的首要目的是最小化损失函数(即最小化模型产生的错误),我们可以使用梯度下降等技术来最小化损失函数。

以在表格数据上训练的基本线性回归模型为例。你可能记得,在这种情况下,我们的数据集表中的每一行代表一个数据点或观察值,而表中的每一列代表一个特征。线性回归模型试图猜测它可以为每个特征使用的系数,这样将每个特征乘以其系数并将所有结果相加,可以得到尽可能接近目标标签的结果。

在线性回归的情况下,每次模型预测错误时,我们会使用损失函数来计算误差,然后计算损失函数相对于每个系数的梯度,然后使用梯度下降来确定如何相应地调整系数,这个过程会重复多次,直到模型改进或由于某种原因停止。

这就是神经网络比我们在本书前面实现的简单回归模型更复杂的地方。在神经网络的情况下,我们没有输入特征到系数的一对一映射。相反,我们的数据通过一个由多个层组成的复杂网络传播,每个层包含多个神经元,每个层的每个神经元都有不同的权重集(以及偏差值)。因此,当我们的模型做出预测并使用损失函数来计算误差时,它不再仅仅是计算损失函数相对于每个输入特征系数的梯度,然后更新每个特征的系数并再次尝试的情况。相反,我们必须为神经网络每一层的所有权重执行此过程。完成此过程的一种方法被称为“误差反向传播”或“反向传播”。

使用反向传播,我们从最后一层开始更新每一层的权重(即最接近我们的输出层的那一层,它代表我们的模型预测),然后逐层反向通过我们的网络。

由于我们的神经网络损失函数由几个嵌套函数组成(由于网络中的层),反向传播步骤中的梯度计算使用了一种称为链式法则的技术,这是微积分中计算损失函数相对于每一层权重的导数的技术。然后,这些结果用于确定如何在网络中的每一遍中更新权重。

我们将在本章后面回到反向传播和链式法则的话题,但首先,让我们更详细地探讨一下我们可以在每次训练过程中使用哪些算法来优化我们的成本函数。

成本函数优化算法

我们已经讨论了在模型训练期间使用梯度下降等机制来优化我们的成本函数。在本节中,我们将简要讨论一些我们可以使用的其他常见优化算法。

动量

这可以被视为基本梯度下降算法的升级。当我们讨论成本函数优化中的梯度下降时,我们经常使用在山区下山(或至少到达山谷底部,这可能是“局部最小值”)的类比。在随机梯度下降SGD)的情况下,类比更像是随机跳跃,在这种情况下,我们有时会向上跳一点(即,朝错误的方向),但总体上,我们通常最终会向下走(即,朝正确的方向)。这种在不同方向跳跃的情况被称为振荡。动量算法通过在正确的方向上导航更明显并减少错误方向上的振荡来加速 SGD。它是通过平均每一步的更新梯度来实现的,这导致沿着误差梯度的下降更加平滑,通常会导致更快地到达底部。在这种情况下使用的类比是一个球沿着山滚动,在这种情况下,运动比偶尔跳跃更平滑。请注意,球也可以获得动量,这有助于它在梯度斜率较小时(小梯度斜率会导致传统梯度下降的学习速度较慢)表现得更好。在实践中,动量几乎总是优于基本梯度下降。

自适应梯度算法(Adagrad)

如其名所示,Adagrad 是一种自适应优化算法。也就是说,在每次优化周期中,Adagrad 会根据每个单独的参数调整学习率。对于梯度大的参数,它执行较小的更新;对于梯度小的参数,它执行较大的更新,这使得它在处理稀疏数据和具有数百万个参数的深度学习模型时特别有用。尽管它可能是一个有用的算法,但它可能导致学习率迅速变得过小,从而有效停止学习。这在长时间训练场景(如深度学习)中可能是一个问题,因为学习过程可能会过早停止。为了解决这个问题,最近开发了如均方根传播RMSProp)和自适应矩估计Adam)等更近期的变体,我们将在下一节讨论这些内容。

RMSProp

RMSProp 通过将学习率除以平方梯度的移动平均MA)来解决 Adagrad 的学习率迅速减小的问题。基本上,它比 Adagrad 更快,通常也更好。

Adam

Adam 结合了动量和 RMSProp 的优点。它平均梯度(像动量一样)并使用平方梯度(像 RMSProp 一样)。它通常是优化器的最佳选择,尤其是在深度学习中。

除了本节中我们已介绍的那些优化算法之外,还有许多其他的优化算法,我们可能在后面的章节中使用其他优化器,但本章我们将使用 Adam,所以现在我们只是介绍 Adam 所基于的流行算法。

在我们开始动手活动之前,我们将更深入地探讨一个重要概念,即激活函数的概念。

激活函数

到目前为止,我们已经触及了激活函数及其在高级别上如何工作的主题。在本节中,我们将更深入地探讨这个主题,并讨论一些我们可以在我们的神经网络中使用的一些常见的激活函数类型。

线性(恒等)激活函数

这个激活函数简单地返回我们提供给它的任何输入,不改变,它通常用于简单任务,或者它经常用作回归用例的输出层。

它可以用数学公式表示为 f(x) = x。

这通常不是我们会用于我们网络中的隐藏层的,因为它不允许学习任何类型的复杂关系。

注意

在神经网络中明确指出非线性变换的重要性是很重要的,因为神经网络的主要力量在于它们能够组合多个非线性变换,以学习数据中的复杂关系。因此,非线性激活函数是复杂神经网络的一个重要组成部分。

即使我们将许多层组合到我们的网络中,如果它们都只是实现了线性变换,我们的整个网络也只会执行一个大的线性变换,这我们可以不使用神经网络来实现。

Sigmoid 激活函数

Sigmoid 函数是逻辑回归的一种实现,它将任何输入映射到 0 和 1 之间的范围。

它可以用数学公式表示为 f(x) = 1 / (1 + exp(-x))。

图 9.4 以了解此函数的视觉表示:

图 9.4:Sigmoid 函数(来源:https://commons.wikimedia.org/wiki/File:Sigmoid-function-2.svg)

图 9.4:Sigmoid 函数(来源:commons.wikimedia.org/wiki/File:S…

Sigmoid 函数是较简单的激活函数之一,它通常已被我们接下来将要讨论的新函数所取代。它的一个局限性是它容易受到一个称为梯度消失问题的困扰,我们将在本章后面描述这个问题。然而,在二分类问题的输出神经元中,它仍然可以是有用的,其中我们将输出解释为输入属于某一类或另一类的概率。

双曲正切(tanh)激活函数

tanh 函数类似于 sigmoid 函数,但它将任何输入映射到 -1 和 1 之间的值。

它可以用数学公式表示为 f(x) = (exp(x) - exp(-x)) / (exp(x) + exp(-x))。

图 9.5 以了解此函数的视觉表示:

图 9.5:tanh 函数(来源:https://commons.wikimedia.org/wiki/File:Mplwp_tanh.svg)

图 9.5:tanh 函数(来源:commons.wikimedia.org/wiki/File:M…

与 sigmoid 函数类似,tanh 也存在梯度消失问题,但在实际应用中仍然有用。

矩形线性单元(ReLU)激活函数

ReLU 函数在最近几年变得非常流行。简单来说,它将任何正数映射到自身,任何负数映射到零。

它可以用数学公式表示为 f(x) = max(0, x)。

图 9*.6* 中该函数的直观表示:

图 9.6:ReLU 函数

图 9.6:ReLU 函数

ReLU 的一个主要优点是它计算效率高,因为该函数本质上只是检查输入是否大于零,如果大于零则直接返回输入,如果不大于零则返回零。这是一个简单的数学计算,这种简单性导致训练速度大大加快。

另一个主要优点是它不受梯度消失问题的影响。然而,它还受到另一个称为“死亡 ReLU”问题的困扰,这是一种神经元由于持续输出零而变得无用的现象。这是因为当输入为零或负数时,函数的梯度变为零。这意味着在反向传播过程中,当权重更新时,该神经元的权重将不会调整。这种情况导致神经元“卡住”,并持续输出零——实际上导致神经元“死亡”,在区分输入时不起任何作用。

Leaky ReLU 激活函数

该函数试图通过在输入小于零时输出小的负值来解决“死亡 ReLU”问题。

它可以用数学公式表示为 f(x) = max(0.01x, x)。

在这种情况下,0.01 的值代表 x 的小的非零梯度,它是一个可以改变的超参数。

图 9*.7* 中该函数的直观表示:

图 9.7:Leaky ReLU

图 9.7:Leaky ReLU

图 9*.7* 所示,Leaky ReLU 避免了输出变为零的问题,即使输入是负数。还有一个 Leaky ReLU 的扩展,称为 参数 ReLUPReLU),它允许在训练过程中通过反向传播学习 x 的小的非零梯度,而不是通过超参数指定为静态数字。

Softmax 激活函数

Softmax 函数通常用于多类分类用例中的神经网络输出层,它将网络的原始输出转换为概率向量(即为类别创建概率分布)。

它可以用数学公式表示为 f(xi) = exp(xi) / Σ(exp(xj)),其中 j 遍历输出层中的神经元集合。

它是 sigmoid 函数的扩展;sigmoid 函数可以用来提供输入属于某一类或另一类的概率(在两个类别之间的选择中),而 softmax 函数可以提供输入属于多个类别的概率范围。例如,如果我们的网络试图识别 1 到 10 之间的数字图像,那么有 10 个可能的类别可以选择。如果我们提供一个数字 1 的图像,并在网络的输出层使用 softmax,那么它可能会确定图像中的数字有很高的概率是 1,而其他潜在类别的概率较低(即 2 到 10)。

除了本节中提到的激活函数之外,还有更多的激活函数,但我们在这里提到的这些是最为知名和广泛使用的。我们选择的激活函数可以取决于特定的用例和其他因素。

现在我们已经覆盖了深度学习领域中的许多重要理论概念,让我们通过构建我们的第一个神经网络来将所学知识付诸实践。为此,我们将介绍一些重要的库。

在本节中,我们描述了我们将在本章中使用的库,例如 TensorFlow 和 Keras。

TensorFlow

TensorFlow 是由 Google Brain 团队开发的用于机器学习和深度神经网络研究的开源软件库(OSS)。然而,它也可以在从移动设备到多 GPU 设置的广泛系统上运行,并且它可以在机器学习之外有众多应用。在本节中,我们将讨论其一些重要方面,例如以下内容:

  • 张量是向量矩阵在多维度的推广(我们可以把它们想象成多维数组或列表)。它们是 TensorFlow 的基本构建块。

  • 数据流图DFGs),其中图中的节点代表数学运算,边代表在这些节点之间传输的数据(张量)。这种方法使得 TensorFlow 能够在多个设备上实现并行计算,使其适合训练大型神经网络。

  • 模型部署有多种选择,例如 TensorFlow Serving 用于服务器端部署或 TensorFlow Lite 用于移动和物联网设备。

  • 通过自动计算损失相对于模型权重的梯度来优化反向传播。

对机器学习和深度学习感兴趣的人应该熟悉 TensorFlow,因为它在业界得到了广泛的应用。

Keras

Keras 是一个用 Python 编写的用于在 TensorFlow、Theano 和认知工具包CNTK)等底层框架之上运行的神经网络高级 API。它是为了实现快速实验而开发的,并已成为 TensorFlow 的官方高级 API(截至 TensorFlow 2.0)。它的一些主要特性包括以下内容:

  • 用户友好性:它有一个简单且一致的界面,针对常见用例进行了优化,并提供清晰的错误消息、有用的文档和开发者指南。

  • 模块化:Keras 模型是通过连接可配置的构建块组装而成的。例如,我们可以通过堆叠多个层轻松构建神经网络。

  • 可扩展性:我们可以编写自定义构建块来表达新的研究想法,并创建新的层、损失函数和模型。

Keras 的核心数据结构是模型,这是一种组织层的方式。主要模型类型是 Sequential 模型,它是一系列层的线性堆叠,但对于更复杂的架构,我们可以使用 Keras 功能 API,这允许我们构建自己的自定义层图。Keras 中的层是一个实现常见神经网络操作的类,Keras 包括一系列预定义的层,我们可以使用它们来构建模型。它还允许我们在训练阶段指定我们想要评估的损失函数和度量标准,并提供许多预定义的损失函数,如mean_squared_error和度量标准如accuracy。Keras 还包括许多优化算法,如 SGD。总的来说,它包括许多有用的工具,使我们能够轻松创建神经网络。

现在我们已经介绍了相关的库,让我们深入探讨并构建我们的第一个神经网络!

在 TensorFlow 中实现 MLP

在本节中,我们将使用 TensorFlow 构建一个 MLP。我们将使用 Keras 作为与 TensorFlow 交互的高级 API。我们可以使用在第五章中创建的相同的 Vertex AI Workbench 笔记本来完成此目的。在那个笔记本中,执行以下步骤:

  1. 导航到名为Google-Machine-Learning-for-Solutions-Architects的文件夹。

  2. 在其中双击Chapter-09文件夹,然后双击Chapter-9-TF-Keras.ipynb文件以打开它。

  3. 当提示选择内核时,选择TensorFlow

  4. 我们打开的笔记本中包含一些 Python 代码,使用 Keras 和 TensorFlow 创建和测试了一个 MLP。

  5. 通过点击每个单元格并在键盘上按Shift + Enter来运行笔记本中的每个单元格。如果您看到任何与 CUDA 相关的错误,请忽略它们,因为我们在这个笔记本中不使用 GPU。

笔记本的第一单元格中的代码导入必要的库和模块,使用sklearn.datasets中的make_moons函数加载数据集,然后使用matplotlib可视化数据。

在这种情况下,我们使用moons数据集,这是一个用于二分类的数学生成数据集,常被用作 ML 算法的简单测试案例,尤其是那些设计用于处理非线性数据(如 NNs)的算法。该数据集由两个特征的两维数组组成(通常在 X-Y 平面上可视化)以及每个样本的二元标签(0 或 1),样本生成的方式使得当绘制时形成两个新月形形状(因此得名moons),每个“月亮”对应一个类别。参见图 9**.**8以获取参考:

图 9.8:moons 数据集

图 9.8:moons 数据集

注意,数据集的主要特征是其非线性(即,分隔两个类别的决策边界不是一条直线)。

我们 Jupyter 笔记本第二个单元格中的代码执行以下操作:

  • 将数据集分为训练集和测试集

  • 定义一个 Sequential 模型(这意味着层是堆叠在一起的)

  • 添加一个输入层和第一个隐藏层,包含 32 个神经元和relu激活函数

  • 添加一个第二个隐藏层,包含 32 个神经元和relu激活函数

  • 添加一个输出层,包含一个神经元(用于二分类)和sigmoid激活函数

  • 使用adam优化器和binary_crossentropy损失函数(适用于二分类)编译模型

  • 对模型进行 50 个周期的训练

当代码运行时,你应该会看到每个周期的输出,如图图 9**.10所示:

图 9.9:训练周期输出

图 9.9:训练周期输出

注意,随着训练的进行,lossval_loss(验证损失)应该减少,而accuracyval_accuracy(验证准确率)应该增加。

接下来,我们第三个单元格中的代码执行以下操作:

  • 使用model.evaluate方法评估我们的模型,该方法在测试模式下返回模型的损失值和度量值(在这种情况下,accuracy)。

  • 使用model.predict方法从我们的模型中获得一些预测,该方法输出每个输入样本属于正类的概率。

  • 为了将其视为二分类用例,我们的代码随后将这些概率转换为基于 0.5 阈值的二元类别标签(即,任何概率超过 0.5 的东西都被认为是正类成员)。

  • 最后,我们打印出前 10 个预测结果以进行快速检查。这些输出将以 0 和 1 的形式呈现,表示预测的类别标签。

就这样!你已经创建了一个神经网络!它可能没有人类那么聪明,但这在 TensorFlow 中使用 Keras 的 MLP(多层感知器)的基本示例。我们可以通过调整诸如层数、神经元类型、激活函数类型、优化器类型、损失函数以及训练配置(如 epoch 数量、批量大小等)等方式,将其扩展到更高级的深度学习用例。做得好!

接下来,我们将深入研究额外的深度学习概念,例如不同类型的神经网络架构、深度神经网络应用中的挑战,以及优化考虑。

神经网络架构、挑战和优化

到目前为止,本章我们已经主要介绍了神经网络的基础知识,在本节中,我们将扩展我们的讨论,包括不同类型的神经网络架构,这些架构可以用于不同类型的实际应用场景,以及训练它们时经常遇到的挑战。最后,我们将讨论如何优化我们的神经网络以解决这些挑战。

常见的神经网络架构

神经网络的“架构”指的是其结构,包括它包含的层数、每层的神经元数量,以及任何影响信息通过网络传播的特殊特性。在本章中我们描述的神经网络架构是 ANN(人工神经网络)的最简单形式,被称为前馈神经网络(FFNN)。这些网络中的信息仅沿一个方向传播,从输入层,通过隐藏层,到输出层。接下来,让我们看看一些其他常用的神经网络架构。在这里,我们将从高层次介绍它们,并在后面的章节中深入探讨更多细节。

注意

当我们在本节中谈论信息通过网络传播时,我们并不是指反向传播步骤,因为那是在迭代学习过程中实现的单独步骤。我们只是简单地指数据在每次训练遍历或推理时间通过我们的网络的方式。

卷积神经网络(CNN)

CNN 在计算机视觉(CV)中常用,用于诸如物体识别和图像分类等用例。考虑这样一个场景,我们希望训练我们的模型来识别猫的图像,同时考虑到这些图像可能以多种不同的形式出现,例如在不同的距离和视角下被捕捉。我们的模型需要建立一种对猫的视觉理解,例如它的面部形状、耳朵、身体和尾巴,以便在图像中正确识别猫。

卷积神经网络(CNNs)通过将图片分解成更小的组件或“特征”并分别学习每个特征来实现这一点。这样,网络学会在更大的图像中检测小细节,例如边缘或曲线,然后将这些细节组合成更大的特征,如单根胡须或猫耳朵的一部分,无论它们在图像中的位置如何,然后将这些特征组合起来以识别猫。卷积神经网络(CNNs)使用卷积层池化层全连接(FC)层的概念来完成这项工作。在这本书的后续章节中,我们将进一步探讨这些概念及其工作原理。

循环神经网络(RNNs)和长短期记忆(LSTM)网络

循环神经网络(RNNs)被设计用来在序列数据中寻找模式,如语言或时间序列。在 RNNs 中,网络包含循环,这使得信息可以从一步持续到下一步,这也是 RNNs 能够创建一种记忆的原因,与假设所有输入(和输出)都是相互独立的基本神经网络不同。通过这种方式,网络在每个步骤中将当前数据与早期阶段输入的数据混合,这对于语言理解等活动非常重要,在这些活动中,模型需要理解每个词与输入中其他词的关系(即,词不是完全独立的)。

然而,循环神经网络(RNNs)的一个问题是,由于我们将在后面讨论的原因,它们在处理长序列时会“忘记”先前的输入。为了解决这些问题,已经发明了诸如长短期记忆(LSTM)网络和门控循环单元GRU)网络等变体,它们使用门和其他技术来保持记忆。

自编码器(AEs)

自编码器(AEs)用于学习未标记数据的有效编码,通常用于降低数据的维度。自编码器(AEs)的基本思想相当简单:它被训练来尝试复制其输入到输出。尽管这可能看起来像是一个简单(且冗余)的操作,但我们施加给网络的限制迫使它发现数据中的有趣方面。最常见的是,我们限制隐藏层中的节点数量,迫使网络学习数据的压缩视图。

自编码器(AEs)由一个编码器和一个解码器组成。编码器将输入数据编码为在低维空间中的压缩表示,解码器则尝试从低维表示中重建输入(即“解码”压缩表示)。在训练过程中的目标是创建一个重建输出,使其尽可能接近输入,以便网络能够学会从压缩表示中重建输入。

在现实世界中,自编码器(AEs)被用于异常检测和推荐系统等应用。自编码器(AEs)的一个特别流行的应用是在生成式人工智能GenAI)模型中。在这种情况下,一旦自编码器被训练,解码器就可以生成新的数据,这些数据模仿了训练数据。

生成对抗网络(GANs)

GANs(生成对抗网络)的基本目标是创建与“真实”数据(由训练数据确定)非常接近的新伪造数据。GANs 由两个相互竞争的神经网络组成,一个称为生成器,另一个称为判别器。这两个网络通过一种最小-最大游戏协同训练,这是一种生成器试图欺骗判别器,而判别器试图可靠地区分真实数据和生成数据的游戏。在训练过程中,生成器在提供看似真实的数据方面变得越来越准确,而判别器在识别伪造方面变得越来越熟练。

Transformer 网络

Transformer 网络是 Google 发明的一种模型架构,其突破性创新是使用自注意力机制或多头注意力,这使得模型在生成输出时能够考虑短语中各种词语的相对重要性。例如,在句子“the dog tried to jump over the pond, but it was too wide”中,“it”这个词可能指的是池塘或狗。对人类来说,这似乎非常明显,因为它指的是池塘,这是因为我们正在使用上下文意识来直观地判断什么最有意义。然而,这对机器学习模型来说并不明显,自注意力机制是允许模型更好地理解句子中每个词语的上下文意义的机制。Transformer 架构还包括编码器和解码器的概念,它们都由一系列相同的层组成。此外,由于自注意力机制本身并不考虑输入序列中词语的位置,因此 Transformer 设计还包含一个位置编码系统来跟踪词语的位置。我们将在后面的章节中更详细地探讨所有这些组件。

Transformer 模型提供的另一个优点是,它们可以并行处理所有输入,而不同于基于序列的模型,如 RNN 或 LSTM 网络,这可以显著加快训练和推理速度。

Transformer 网络已被证明在自然语言处理(NLP)任务中非常有用,包括情感分析(SA)、文本摘要和机器翻译,Transformer 架构是生成预训练 Transformer(GPT)、双向编码器表示来自 Transformer(BERT)和 T5 等模型的基础。如果您想了解更多关于这项突破性技术的信息,我建议阅读首次介绍 Transformer 概念的标志性研究论文(Vaswani, A. et al., 2017),该论文可以在以下网址找到:arxiv.org/abs/1706.03762

除了本节中提到的神经网络架构之外,还有更多类型的架构,但我们在这里讨论的这些是其中最知名和最广泛使用的。研究人员不断创造和实验新的神经网络配置,网络配置的选择取决于我们试图解决的问题,因为每种网络类型都有其优势和劣势。

接下来,让我们讨论一些人们在训练和使用神经网络时遇到的一些常见挑战。

常见神经网络挑战

除了我们在前面章节中讨论的传统机器学习实现的所有挑战之外,DNNs 还引入了它们自己的一套挑战,例如可解释性、成本以及梯度消失或爆炸。

可解释性

可解释性指的是我们理解模型内部工作原理以及它们做出决策背后的原因的难易程度。例如,线性回归模型通常很容易理解和解释,因为它们的输出只是对模型提供的任何输入的简单数学变换。

然而,深度神经网络(DNNs)可能极其复杂,拥有数千个神经元和数十亿个参数,这些参数会影响它们的输出。此外,它们的输出通常不仅仅是输入的线性变换,而是本质上是非线性的。

在后面的章节中,我们将更详细地讨论可解释性的重要性,并介绍可以帮助我们更好地理解模型工作原理的机制。

成本

DNNs 在训练和托管时可能需要大量的计算资源,这可能导致货币支出。如果我们考虑具有数十亿参数的高度复杂模型,这些模型可能需要数周甚至数月的时间来训练,使用大量最新一代的尖端 GPU 的非常强大的服务器。这些资源并不便宜,因此我们需要确保我们的模型尽可能高效地使用计算资源。

梯度消失问题

我们在本章的前几节简要提到了这个话题,所以让我们更详细地探讨这个概念。当损失函数的梯度变得非常小,以至于几乎消失时,就会产生梯度消失问题,导致网络第一层的权重更新缓慢。记住,反向传播使用微分链式法则,这涉及到乘以一系列的导数(或梯度)。这意味着如果导数的值小于 1,我们在通过网络反向传播时,实际上是以指数方式将它们分成越来越小的值。正因为如此,早期层的学习速度比后期层慢得多。

当使用 sigmoid 或 tanh 等将输入压缩到小范围的激活函数时,这个问题变得更加明显。例如,sigmoid 函数将输入值压缩到零到一的范围,这意味着即使输入值很大(无论是正还是负),sigmoid 函数的输出也介于零和一之间,结果是梯度减少到极小的值,导致基于反向传播的学习速度显著减慢。

梯度爆炸问题

另一方面,当梯度变得过大时,会导致网络中的权重通过过大的增量更新,这会导致网络的性能剧烈波动,从而使模型训练过程失败。

梯度爆炸问题在 RNN 中更为常见,尤其是在长序列中,但它可以发生在任何类型的网络中。

防止梯度消失或梯度爆炸的优化

现在我们对梯度消失和梯度爆炸问题发生的方式有了更好的理解,以下考虑因素可以帮助降低它们发生的可能性。

权重初始化

有效的权重初始化可以帮助减轻梯度消失和梯度爆炸的问题。例如,Xavier(Glorot)初始化和 He 初始化等技术可以帮助将初始权重设置为防止梯度在训练初期变得过小或过大的值。

激活函数的选择

正如我们讨论的,使用压缩其输入的激活函数,如 sigmoid 或 tanh,会增加遇到梯度消失和梯度爆炸问题的可能性。因此,在容易遇到这些问题的场合最好避免使用这些激活函数。我们可以改用 Leaky ReLU 或 PReLU 等激活函数,因为这些函数不会压缩它们的输入。

批标准化

使用这种技术,我们可以将层的输出归一化,以稳定每个层输入的均值和方差。这有助于控制梯度的规模,减轻梯度消失和梯度爆炸问题。

梯度裁剪

这种技术对梯度设置了一个预定义的限制或阈值,以防止它们变得过大,这对于解决梯度爆炸问题特别有用。

架构方法

在本节前面讨论 RNN 时,我们提到它们在处理长序列时会“忘记”先前的输入。梯度消失问题是导致这种情况的一个因素,这也是为什么某些架构,如 LSTM 和 GRU,被设计用来在 RNN 的上下文中通过在其结构中使用一种门控机制来解决这些问题的原因之一。因此,有时选择 NN 架构可以降低遇到梯度消失和梯度爆炸问题的可能性。

这些问题和它们的解决方案是理解深度学习模型如何工作以及如何有效地训练深度神经网络的主要因素。

本章我们介绍了许多新的概念和术语。让我们花点时间总结一下我们所学的所有内容。

摘要

在本章中,我们首先探讨了人工神经元(以感知器形式)与人类大脑中的生物神经元之间的比较。然后,我们将这一想法扩展到描述神经网络中多个神经元的活动,包括将多个感知器组合在一起以及我们大脑中的微小神经元如何协同工作以产生极其复杂的高级功能。

然后,我们更深入地探讨了人工神经网络的内幕和工作组件,包括诸如激活函数和反向传播等概念。我们讨论了许多不同类型的激活函数,包括它们的工作原理以及最适合它们的用例。

在反向传播的背景下,我们学习了各种常用的成本函数优化算法,例如动量法和 Adam 法,然后我们介绍了两个非常重要的深度学习库:TensorFlow 和 Keras。

接下来,我们使用这些库构建了我们第一个神经网络,并通过基于moons数据集成功获得预测来测试了这个网络,我们也在本章中对其进行了详细探讨。

在构建了我们第一个简单的神经网络之后,我们扩展了讨论范围,涵盖了更高级的神经网络架构及其用例,并探讨了人们在训练和使用神经网络时经常遇到的常见挑战,以及我们可以用来优化网络以减少遇到这些问题的可能性的方法。

这些是机器学习领域的一些更高级的概念,所以如果你已经理解了本章所涵盖的内容,那么你已经为我们在后续章节中对这些概念进行更深入探讨奠定了重要的基础。

在下一章中,让我们探讨如何将训练好的模型投入生产,以便为现实世界的用例提供服务。

第十章:生产环境中的部署、监控和扩展

有些人可能会从头到尾阅读这本书,以尽可能多地了解 Google Cloud 在 AI/ML 领域的概念,而其他人可能会将其作为参考,当他们需要作为项目或客户合作的一部分处理特定主题时,他们会挑选并阅读某些章节。如果您从本书的开头就开始阅读,那么您已经走了很长的路,我们已经一起走过了ML 模型开发生命周期MDLC)的大部分旅程。虽然模型训练通常是媒体关注的焦点——这也是许多魔法发生的地方——但现在您已经知道,训练只是整个生命周期的一部分。

当我们训练和测试了我们的模型,并且相信它们已经准备好向我们的客户展示时,我们需要找到一种方式来托管它们,以便它们可以被相应地使用。在本章中,我们将更详细地探讨这一过程的部分,包括在托管和管理模型以及持续监控它们以确保它们保持相关性并持续优化性能时存在的挑战。我们将从讨论我们如何托管我们的模型开始。

本章涵盖了以下主题:

  • 我如何使我的模型可供我的应用程序使用?

  • 服务的模型的基本概念

  • A/B 测试

  • 生产环境中服务的模型常见挑战

  • 监控生产环境中的模型

  • 在边缘优化 AI/ML

我如何使我的模型可供我的应用程序使用?

我们在第一章中介绍了这个概念,并讨论了您需要执行的各种操作来在自己的服务器上托管模型,例如设置所有必需的基础设施,包括负载均衡器、路由器、交换机、电缆、服务器和存储等,然后持续管理这些基础设施。这需要您投入大量的时间和资源。

幸运的是,所有这些都在过去,您现在不再需要做任何这些事情。这是因为 Google Cloud 提供了 Vertex AI 预测服务,它使您能够在几分钟内使用由 Google 为您管理的基础设施在生产环境中托管模型。

为了完整性,我还会提到,如果您想在 Google Cloud 上托管模型而不使用 Vertex,还有许多其他 Google Cloud 服务可以用于此目的,例如Google Compute EngineGCE)、Google Kubernetes EngineGKE)、Google App EngineGAE)、Cloud Run 和 Cloud Functions。我们在第三章中描述了所有这些服务,以及一些关于如何在这之间做出选择的提示。

记住,选择合适的平台来托管你的机器学习模型取决于你的具体用例和需求。可扩展性、延迟、成本、开发努力和运营管理都在选择最佳解决方案中发挥作用。你还可以根据你用来构建机器学习模型的框架做出某些决定。例如,如果你在 TensorFlow 中构建模型,你可能想使用 TensorFlow Serving;如果你在 PyTorch 中构建模型,你可能想使用 TorchServe。

在大多数情况下,我的建议是开始使用一个专门且针对当前任务进行优化的服务,在构建和托管机器学习模型的情况下,Vertex AI 就是这样的服务。在本章中,我们将使用 Vertex AI 部署我们的第一个模型,但在我们深入实际操作之前,我们将介绍一些重要概念。

模型服务的根本概念

在本节中,我们将介绍一些与我们如何托管模型以便我们的客户与之交互相关的重要主题。

在线与离线模型服务

在机器学习中,我们有两种从模型中提供预测的选项:在线服务(也称为实时服务)和离线服务(也称为批量服务)。与每种方法相关的高级用例分别是在线(或实时)推理离线(或批量)推理。让我们花几分钟时间介绍这些方法并了解它们的用例。

在线/实时模型服务

正如其名所示,在实时模型服务的情况下,模型需要“实时”响应预测请求,这通常意味着客户端(可能是客户或某些其他系统)需要尽可能快地收到推理响应,并且可能正在同步等待模型的响应。一个这样的例子就是信用卡交易的反欺诈系统。正如你可以想象的那样,信用卡公司希望尽可能快地检测可能的欺诈交易——理想情况下是在交易过程中,这样他们就可以在可能的情况下阻止交易完全处理。在之后的某个任意时间点检查欺诈交易对他们来说可能就不那么有用。

考虑到在线推理请求通常需要尽快返回响应,确保低延迟、处理高请求量以及提供可靠且始终可用的服务是该领域的一些主要挑战。

在这个例子中,当我们提到低延迟时,我们指的是预测响应时间通常在毫秒级别,或者最多几秒钟。实际的响应时间要求将取决于业务用例。例如,一些用户可能可以接受等待几秒钟来批准或拒绝他们的信用卡交易,但另一个实时推理的例子是自动驾驶汽车中机器学习模型的使用,在这种情况下,例如,如果汽车需要突然采取某种行动,比如避开突然出现在其路径上的意外障碍物,它必须能够在毫秒级别对其环境做出反应。图 10.1展示了我们将在本章的实践练习中实施的批预测工作流程的示例。我们将详细解释图中展示的每个组件。在图 10.1中,实线代表我们在实践练习中明确执行的步骤,而虚线代表 Vertex AI 将代表我们自动执行的步骤:

图 10.1:在线预测

图 10.1:在线预测

图 10.1中概述的步骤如下:

  1. 在我们的笔记本中训练模型。

  2. 将生成的模型工件保存到 Google Cloud Storage。

  3. 在 Vertex AI 模型注册表中注册模型详细信息(在本章中更详细地解释)。

  4. 创建一个端点来托管和提供我们的模型。

  5. Vertex AI 在线预测服务(在本章中更详细地解释)从 Vertex AI 模型注册表中检索我们模型的详细信息。

  6. Vertex AI 在线预测服务从 Google Cloud Storage 检索我们保存的模型。

  7. 我们向我们的模型发送预测请求并接收响应。在这种情况下,我们是从我们的 Vertex AI Workbench 笔记本发送请求的,但重要的是要注意,当我们的模型托管在端点时,预测请求可以来自任何可以访问该端点的客户端应用程序。

  8. Vertex AI 在线预测服务将预测输入、输出和其他详细信息保存到 Google Cloud BigQuery。这是一个可选功能,我们可以启用它,以便我们可以对输入、输出以及与我们模型预测相关的其他细节执行分析查询。

还应注意的是,在在线模型服务的情况下,预测通常是按需进行的,通常针对单个实例或一小批实例。在这种情况下,您的模型及其服务基础设施需要能够快速应对推理流量量的突然——可能是意外的——变化。

离线/批处理模型服务

根据我们对在线模型服务的描述,可能已经很明显,离线服务意味着没有客户端在实时等待即时响应。实际上,而不是我们的模型从单个客户端接收按需推理请求,我们可以将许多输入观测值以大批次的形式输入到我们的模型中,它可以处理更长的时间;可能是几个小时甚至几天,具体取决于业务案例。我们的模型产生的预测可以随后存储并用于以后,而不是立即采取行动。批量推理用例的例子包括预测第二天股票价格或根据预测的偏好向用户发送定向电子邮件。图 10*.2* 展示了我们将在本章的实践练习中实施的批量预测工作流程的示例。在 图 10*.2* 中,实线表示我们将明确在实践练习中执行的步骤,而虚线表示 Vertex AI 将代表我们自动执行的步骤:

图 10.2:批量预测

图 10.2:批量预测

图 10*.2* 中概述的步骤如下:

  1. 在我们的笔记本中训练模型。

  2. 将生成的模型工件保存到 Google Cloud Storage。

  3. 在 Vertex AI 模型注册表中注册模型详细信息。

  4. 将测试数据保存到 Google Cloud Storage。这将在我们后续的批量预测作业中用作输入数据。

  5. 创建一个批量预测作业。

  6. Vertex AI 批量预测服务(在本章中更详细地解释)从 Vertex AI 模型注册表中获取我们模型的详细信息。

  7. Vertex AI 批量预测服务从 Google Cloud Storage 获取我们保存的模型。

  8. Vertex AI 批量预测服务从 Google Cloud Storage 获取我们的输入数据。

  9. Vertex AI 批量预测运行一个批量预测作业,使用我们的模型和输入数据。

  10. Vertex AI 批量预测服务将预测输出保存到 Google Cloud Storage。

与优化低延迟不同,批量预测系统通常优化以一次性处理大量实例(即高吞吐量),因此通常属于大规模分布式计算用例,可以从并行执行中受益,并且可以安排定期自动执行(例如,每天一次),或者可以由事件触发(例如,当有新的数据批次可用时)。

重要的是要注意,在线和离线服务的决策并不总是严格二元的,你可能会发现在线和离线服务的组合最能满足你的需求。例如,你可能会使用离线服务生成大规模报告,同时使用在线服务在面向用户的应用中进行实时预测。在任一情况下,在线和离线服务都需要将模型部署在服务基础设施中。该基础设施负责加载模型、接收预测请求、使用模型进行预测,并返回预测结果。在 Google Cloud 中,有各种工具和平台可用于协助此任务,例如 TensorFlow Serving 和 Vertex AI 预测服务,我们将在本章后续部分详细讨论。然而,首先,让我们介绍另一个作为我们端到端模型开发生命周期一部分的重要工具。

Vertex AI 模型注册库

在本书的早期部分,我们将传统的软件开发生命周期SDLC)与 MDLC 进行了类比。为了更深入地探讨这个类比,让我们考虑 SDLC 过程中的几个重要工具:

  • 共享仓库:作为 SDLC 过程的一部分,我们通常希望将我们的代码和相关工件存储在注册库或仓库中,以便多个需要协作进行特定开发项目的贡献者可以访问。这样的仓库通常包括帮助描述代码资产某些方面的元数据,以便人们可以轻松理解这些资产是如何开发的,以及它们是如何被使用的。

  • 版本控制:当贡献者对特定项目中的代码资产进行更改时,我们希望确保我们正在跟踪这些更改和贡献,并且所有贡献者都可以轻松访问这些信息。这也使我们能够在发现软件新部署版本中的问题时回滚到之前的版本。

经验告诉我们,当我们想要高效地部署和管理机器学习模型时,需要类似的工具,尤其是在大规模部署时(记住,一些公司可能拥有成千上万的机器学习模型,由数百个不同的团队拥有)。

此外,请记住,数据科学项目通常高度实验性,数据科学团队可能会尝试许多不同的算法、数据集和超参数值,训练许多不同的模型,以查看哪些选项能产生最佳结果。这在数据科学项目的早期阶段尤其如此,但这种情况也常常持续存在,数据科学家即使在模型部署到生产环境之后,也会不断努力改进模型。他们这样做是为了跟上行业的新趋势,并产生更好的结果。

Google Cloud 的 Vertex AI 平台包括一个模型注册服务,它允许我们在一个集中的位置管理我们的机器学习模型,这使得我们能够更容易地跟踪模型的发展情况,即使有多个团队在贡献这些模型的发展。让我们来看看模型注册的一些重要功能:

  • 模型版本控制:模型注册允许我们创建多个版本的模型,其中每个版本可以对应不同的训练参数集或不同的训练数据集。这有助于我们跟踪不同的实验或部署。

  • 模型元数据:对于每个模型,我们可以记录元数据,例如模型的描述、输入和输出模式、标签(用于分类的有用信息)和指标(用于比较模型的有用信息)。对于每个版本,我们可以记录额外的元数据,例如描述、运行时版本(对应于 Vertex AI 平台服务的版本)、Python 版本、用于服务的机器类型以及服务设置。

  • 模型工件:这些是用于生成模型的工件。它们可以存储在 Google Cloud Storage 中,并链接到注册表中的模型。

  • 访问控制:我们可以通过 Google Cloud 的 身份和访问管理IAM)系统来控制谁可以查看、编辑和部署注册表中的模型。

重要的是要理解模型注册与其他 Vertex AI 组件的良好集成。例如,我们可以使用 Vertex AI 训练服务来训练一个模型,然后自动将训练好的模型上传到注册表中,之后我们可以将模型部署到 Google Cloud Vertex AI 预测服务,我们将在下一节中描述。我们可以比较模型版本,并轻松更改部署到生产中的版本。我们还可以使用 Vertex AI Pipelines 自动化所有这些步骤,我们将在下一章中探讨这一点。

Vertex AI 预测服务

Google Cloud Vertex AI 预测服务是 Vertex AI 生态系统中的一个服务,它使我们能够轻松托管机器学习模型并向我们的客户提供服务,从而支持批量模型服务和在线模型服务用例。这是一个托管服务,因此当我们用它来托管我们的模型时,我们不需要担心管理所需的服务器和基础设施;该服务将根据发送到我们模型的流量量自动扩展所需的基础设施和计算资源。

如我们在上一节中提到的,它集成了 Vertex AI 模型注册,使我们能够轻松控制哪些版本的模型被部署到生产中,并且它还集成了许多其他 Google Cloud 服务,例如 Vertex AI Pipelines,它允许我们自动化模型的开发和部署,以及 Google Cloud Operations Suite,它提供集成的日志记录和监控功能。

我们将在 Google Cloud 中使用 Vertex AI 模型注册和 Vertex AI 预测服务进行实际操作,以存储和提供模型,但首先,让我们再覆盖一个在模型部署和管理方面非常重要的概念:A/B 测试

A/B 测试

A/B 测试是通过测试一个模型(模型 A)与另一个模型(模型 B)的性能来比较哪个模型表现更好。虽然这个术语在技术上可以应用于测试和比较任何模型,但通常的场景是测试模型的新版本,以改善模型在业务目标方面的性能。

Vertex AI 允许我们将多个模型部署到单个端点,并通过使用 traffic_split 变量来控制每个模型所服务的流量量,如图 图 10.3 所示:

图 10.3:使用 Vertex AI 的 traffic_split 进行 A/B 配置

图 10.3:使用 Vertex AI 的 traffic_split 进行 A/B 配置

正如您在本章的实际练习中将会看到的,如果我们不对 traffic_split 变量设置任何值,默认行为是将所有流量都导向已经部署到我们端点的原始模型。这是一个安全机制,可以防止我们的模型在服务客户流量方面出现意外行为。traffic_split 配置使我们能够非常细致地控制我们希望发送到每个已部署模型或模型版本的流量量。例如,我们可以设置 traffic_split 配置,使其突然将所有流量发送到我们的新模型,通过将 100%的流量分配给该模型,从而有效地替换我们的模型。然而,我们可能希望在完全替换先前的模型版本之前,用我们生产流量的一小部分来测试我们的新模型版本,这在软件开发中相当于金丝雀测试的概念。

当我们确定我们的新模型表现符合预期时,我们可以逐渐(或根据业务需求突然)更改 traffic_split 变量,将更多(或全部)流量发送到新模型。

现在我们已经涵盖了与在生产环境中托管和提供模型相关的许多重要概念,让我们通过使用 Vertex AI 部署一个模型来看看这在现实世界中是如何工作的。

我们已经准备了一个 Vertex AI Workbench 笔记本,它会指导你完成所有必要的步骤。再次强调,我们可以使用我们在 第五章 中创建的相同的 Vertex AI Workbench 管理笔记本实例来完成这个任务。请在笔记本实例上打开 JupyterLab。在屏幕左侧的目录浏览器中,导航到 Chapter-10 目录并打开 deployment-prediction.ipynb 笔记本。你可以选择 TensorFlow 2 (Local) 作为内核。同样,你可以通过选择单元格并按键盘上的 Shift + Enter 来运行笔记本中的每个单元格。除了相关的代码外,笔记本还包含描述代码正在做什么的 Markdown 文本。我建议只执行模型训练和部署部分,以及 A/B 测试,然后在继续进行笔记本中的其他活动之前,阅读本章的一些更多主题。

我们还必须在笔记本中启用一个名为 prediction-request-response-logging 的功能,该功能将记录我们模型对收到的预测请求的响应。我们可以将这些响应保存到 Google Cloud BigQuery 表中,这样我们就可以对每个模型的预测响应进行分析,并查看它们的性能。

一旦你完成了笔记本中的模型训练和部署部分(或者如果你现在只想继续阅读),你就可以进入下一部分,我们将讨论公司在生产中部署、提供和管理模型时通常会遇到哪些挑战。

在生产中部署模型时的常见挑战

在生产中部署和托管机器学习模型通常伴随着许多挑战。如果你只是开发和提供单个模型,你可能会遇到一些这些挑战,但如果你正在开发和提供数十、数百或数千个模型,那么你很可能会遇到这些挑战和担忧的大多数。

部署基础设施

选择合适的托管机器学习模型的基础设施、设置和管理它可能很复杂,尤其是在混合或多云环境中。再次强调,Google Cloud Vertex AI 会自动为我们处理所有这些,但没有这样的云服务,许多公司发现这可能是有数据科学项目中最具挑战性的方面之一。

模型在生产和扩展中的可用性

这是部署基础设施管理的扩展。随着需求的增加,我们的模型需要提供更多的预测。根据需求进行服务扩展和缩减的能力至关重要,并且可能很难手动管理。Vertex AI 自动扩展功能使我们能够轻松地做到这一点。我们只需指定我们希望为每个模型运行的机器的最小和最大数量即可。

例如,如果我们知道我们始终需要至少运行三个节点来处理我们通常预期的流量,但有时流量会增加到正常水平的两倍,我们可以指定我们希望 Vertex AI 总是运行至少三台机器,并在需要时自动扩展到最多六台机器。

我们可以通过配置 minReplicaCountmaxReplicaCount 变量来指定我们的自动扩展偏好,这些变量是我们部署的模型机器规格的元素。我们已经在与本章相关的 Jupyter Notebook 中提供了如何操作的步骤。请注意,我们可以按模型指定这些细节,而不仅仅是按端点。这使我们能够独立扩展我们的每个模型,这为我们提供了灵活性,取决于我们的需求。

Vertex AI 也为我们提供了灵活性,可以选择将多个模型部署到同一个端点,或者为每个模型创建独立的、专门的端点。如果我们针对不同的用例有完全不同类型的模型,那么我们通常会把这些模型部署到独立的端点。在这个时候,你可能想知道为什么我们想要将多个模型部署到单个端点。这样做最常见的原因是我们想要实施 A/B 测试。同样,我们已经在伴随本章的 Jupyter Notebook 中介绍了实施 A/B 测试用例的步骤。

数据质量

通常,在生产环境中使用的数据可能包含错误,或者可能没有用于模型训练的数据那么干净,这可能导致预测不准确。因此,我们可能需要在生产中实施数据预处理步骤,在预测时“实时”准备和清理数据。一般来说,我们在准备训练数据时可能应用的任何数据转换技术也需要在推理时应用。

模型/数据/概念漂移

除了准备和清理我们的数据之外,如果你回想一下本书前几章中我们执行的一些数据探索活动,你可能记得我们数据的一个重要方面是数据集中变量的统计分布。例如,包括数据集中每个变量的平均值、每个变量的最小值和最大值之间的观察范围,或者每个变量的值类型,如离散或连续。总的来说,我们可以将这些特征称为我们数据的“形状”。图 10.4 展示了一些不同类型的数据分布的示例:

图 10.4:数据分布  (来源:https://commons.wikimedia.org/wiki/File:Cauchy_pdf.svg)

图 10.4:数据分布 (来源:commons.wikimedia.org/wiki/File:C…)

在一个理想的世界里,用于训练我们模型的 数据形状 应该与模型在生产中预期遇到的数据形状相同。因此,我们通常希望使用现实世界的数据(即,在生产中之前观察到的数据)来训练我们的模型。然而,随着时间的推移,生产中观察到的数据形状可能会开始偏离用于训练模型的数据形状。这种情况可能突然发生,例如,当全球大流行在短时间内急剧改变消费者的购买行为(例如,每个人都突然购买卫生纸和洗手液,而不是时尚和化妆品),或者季节性变化,或者它可能由于特定业务领域或市场的自然进化变化而逐渐发生。

在任何情况下,这种偏差通常被称为“漂移”,或者更具体地说,是模型漂移数据漂移概念漂移。模型漂移指的是模型目标性能随时间整体下降,它可能由数据漂移或概念漂移等因素引起。数据漂移是指生产中数据的统计分布与用于训练我们模型的数据的统计分布不同。另一方面,概念漂移是指模型试图预测的输入特征与目标变量之间的关系发生变化。这意味着即使输入数据保持不变,变量之间的潜在关系也可能随时间变化,这可能会影响我们模型的准确性。大流行期间消费者行为变化的例子是概念漂移的一个实例。

无论漂移的类型如何,它都可能导致模型性能下降。因此,我们需要持续评估我们模型的性能,以确保它们始终满足我们的业务需求。如果我们发现我们模型的性能正在下降,我们需要评估这是否是由于漂移造成的,如果我们检测到它是,我们需要相应地采取纠正措施。我们将在稍后更详细地讨论这些措施。

安全和隐私

确保我们模型使用的数据符合安全和隐私法规可能很复杂,尤其是在处理敏感数据的行业中。根据行业和用例,这可以被认为是模型开发和管理的最重要方面之一。

模型可解释性

通常,模型被视为“黑盒”实现,在这种情况下,我们无法深入了解模型内部的工作方式,这使得理解它们是如何进行预测的变得困难。这可能会引起问题,尤其是在需要解释的监管行业中。我们将在本书的稍后部分更详细地探讨这个话题。

跟踪机器学习模型元数据

正如我们之前提到的,机器学习模型开发通常涉及一些实验,数据科学家会评估他们数据集的不同版本,以及不同的算法、超参数值和其他开发过程组件。此外,开发过程通常涉及多个团队成员或多个独立团队之间的协作。考虑到一些公司开发和部署了数百甚至数千个机器学习模型,跟踪所有这些实验和开发迭代是很重要的。

例如,如果我们有一个在生产中部署的模型,并且观察到该模型正在做出不准确或不适当的预测,那么我们需要了解为什么该模型会这样表现。为了做到这一点,我们需要知道创建该特定模型版本所执行的所有步骤,以及与该模型开发相关的所有输入和输出,例如用于训练模型的输入数据集版本,以及训练过程中使用的算法和超参数值。我们将这些信息称为模型的元数据。没有这些信息,很难理解我们的模型为什么会这样表现。

这个概念与模型可解释性的主题有些关联,但也包括机器学习模型开发的其他方面。

与现有系统的集成

大多数公司都有各种软件系统,这些系统是在多年的时间里开发和采购的,将机器学习模型集成到这些系统中可能很复杂。

监控

如果你自己管理基础设施,为生产中的机器学习模型设置稳健的监控可能是一个挑战。我们需要持续监控模型的预测性能,以确保它仍然有效,并且随着时间的推移没有退化。

这是对模型管理的一个重要方面,因此我们将在本章的下一节专门讨论这个主题。

监控生产中的模型

我们在开发和部署模型后的工作还没有结束——我们需要随着时间的推移跟踪模型的表现和整体健康状况,并在观察到模型性能下降时进行调整。在本节中,我们将讨论我们通常需要监控的一些模型特征。

目标模型性能

毫不奇怪,我们需要监控的我们模型最突出的方面是它在其创建目标方面的表现。我们在本书的前几章讨论了目标指标,例如均方误差MSE)、准确率、F1 分数和 AUC-ROC 等。AUC-ROC 的一个例子在图 10.5中展示,供参考:

图 10.5:AUC-ROC

图 10.5:AUC-ROC

目标指标告诉我们我们的模型在实现其主要目的方面表现如何,例如预测房价或识别照片中的猫。如果我们注意到这些指标在某个我们认为对业务需求可接受的阈值以上持续下降,那么我们通常需要采取纠正措施。

模型性能下降的常见原因是数据漂移,我们已在上一节中讨论过。如果我们发现发生了数据漂移,纠正措施通常是使用与模型在生产中当前观察到的数据形状或分布相匹配的更新数据重新训练我们的模型。这可能需要从我们的生产系统或其他适当的来源收集新鲜数据。

专门监测数据漂移

监测数据漂移涉及比较传入数据的统计属性与训练数据的统计属性。在这种情况下,我们分析数据中的每个特征或变量,并将传入数据的分布与训练数据的分布进行比较。除了比较简单的描述性统计量,如均值、中位数、众数和标准差之外,还有一些特定的统计检验我们可以评估,例如 Kullback-Leibler 散度、Kolmogorov-Smirnov 检验或卡方检验。我们将在本书的后面部分更详细地讨论这些机制。

Vertex AI 模型监控提供了自动测试,可以检测不同类型的漂移。具体来说,它可以检查特征偏斜和漂移以及归因偏斜和漂移,这两者我们将在下文中进行描述。它还提供了模型解释功能,我们将在本书的后面部分详细探讨。

偏斜也被称为训练-服务偏斜,它指的是生产环境中特征数据的分布与用于训练模型的特征数据分布不同的情况。为了检测这种偏斜,我们通常需要访问原始训练数据,因为 Vertex AI 模型监控会将训练数据的分布与发送到我们生产中模型的推理请求中看到的分布进行比较。

预测漂移指的是生产中的特征数据随时间变化的情况。在没有访问原始训练数据的情况下,我们仍然可以开启漂移检测来检查输入数据随时间的变化。

需要注意的是,一些程度的漂移和偏斜可能是可容忍的,但通常我们需要确定和配置阈值,超过这些阈值我们就需要采取纠正措施。这些阈值取决于我们特定用例的业务需求。

异常模型行为

异常可能包括预测请求的峰值、输入特征的异常值或异常的预测响应值。例如,如果一个用于欺诈检测的模型开始标记异常高数量的交易为欺诈,这可能是一个异常,需要进一步调查。异常也可能由于数据漂移或环境中的临时变化而发生。

资源利用率

这包括监控 CPU 使用率、内存使用率、网络 I/O 等方面,以确保模型的服务基础设施在其容量内运行。这些指标是确定何时根据当前发送到我们模型的流量量等因素增加或减少资源的重要指标。

模型偏差和公平性

我们需要确保我们的模型持续进行公平的预测。这可能包括跟踪公平性指标和检查模型预测中的偏差。我们将在下一章中更详细地探讨这个话题。

这些是我们需要监控的模型的主要方面。具体需要优先考虑的项目取决于我们的业务案例。

现在我们已经讨论了模型监控领域中最常见的许多话题,让我们讨论一下如果检测到我们的模型性能下降,我们应该怎么做。

解决模型性能下降问题

如果我们发现我们监控的模型指标值显示出性能下降,我们可以采取一些措施来纠正这种情况,并可能防止未来发生此类情况。本节讨论了一些相关的纠正措施。

确保我们的模型保持最新并表现最佳的最有效方法可能是实施一个健壮的 MLOps 管道。这包括机器学习模型的持续集成和持续部署CI/CD),其主要组成部分是定期使用生产中看到的新数据训练我们的模型——这被称为持续训练。为此,我们需要实施一个机制来捕获生产中的数据,然后自动更新我们的模型。

我们可以定期更新我们的模型(例如,每晚或每月一次),或者根据 Vertex AI 模型监控作业的输出触发重新训练。例如,如果模型监控作业检测到漂移超出了可接受的阈值,可以发送自动通知,并自动启动一个管道来训练和部署我们模型的新版本。

下一章将完全致力于 MLOps 的话题,因此我们将更详细地探讨这些概念。

同时,如果您想从理论转向实践学习,现在执行本章附带的 Vertex AI Workbench 笔记本中的模型监控部分将是一个好时机。否则,让我们继续本章的其余部分,其中包括在边缘优化 AI/ML 用例。

在边缘优化 AI/ML

在边缘提供 ML 模型指的是直接在用户设备上运行您的模型,例如智能手机或物联网设备。术语“边缘”基于传统的网络架构术语,其中网络的核心位于网络所有者的数据中心,而网络的边缘是用户设备连接到网络的地方。在边缘运行模型和其他类型的系统可以提供诸如降低延迟、增加隐私和减少服务器成本等好处。然而,边缘设备通常计算能力有限,因此我们可能需要对模型进行一些调整,以便它们在这些设备上高效运行。我们可以做几件事情来优化我们的模型,以便它们在边缘运行,所有这些内容我们将在本节中讨论。

模型优化

让我们先讨论一下我们可以采取哪些措施来优化我们的模型,以便它们可以在边缘使用。

模型选择

首先,我们应该尝试选择轻量级的模型,这些模型仍然能在我们的目标上提供良好的性能。例如,决策树和线性模型通常比深度学习模型需要的内存和计算能力更少。当然,我们的模型选择过程也取决于我们的业务需求。有时,我们需要更大的模型来实现所需的目标。因此,这个关于在边缘优化 AI/ML 工作负载的建议只是一个初步的指导方针。我们将在下一节讨论更高级的策略。

模型剪枝

剪枝是一种通过移除对模型性能贡献最小的参数(例如,“权重剪枝”)或整个神经元(例如,“神经元剪枝”)来减小模型尺寸的技术。图 10.6 中展示了神经元剪枝的一个例子,其中每个隐藏层都移除了一个神经元(如图中覆盖每个被移除神经元的红色 X 所示):

图 10.6:神经元剪枝

图 10.6:神经元剪枝

结果剪枝后的模型需要更少的内存和计算资源。如果我们移除太多的权重或神经元,那么可能会影响我们模型的准确性,因此,当然,我们的想法是找到一个平衡点,以最小化对准确性的影响,同时减少所需的计算资源。

模型量化

量化是一种降低模型权重数值精度的方法。例如,权重在训练期间可能以 32 位浮点数存储,但在推理时通常可以量化为 8 位整数,而不会显著降低性能。这减少了模型的内存需求和计算成本。这在大型语言模型LLMs)的背景下尤其有用,这些模型可能拥有数百亿个权重。我们将在本书的“生成式 AI”部分详细讨论这一点。

知识蒸馏

这种技术涉及训练一个较小的模型,有时被称为“学生”模型,以模仿一个较大、被称为“教师”模型或模型集合的行为。较小的模型被训练以产生尽可能接近较大模型的输出,以便它能执行类似的任务,或许在模型精度上有所可接受的降低。再次强调,我们需要在模型尺寸减小和精度降低之间找到平衡。在 LLMs 的背景下,蒸馏技术尤其有用。

利用高效模型架构

一些模型架构被设计为在边缘设备上高效。例如,MobileNet 和 EfficientNet 是卷积神经网络CNNs)的高效变体,适合移动设备。

现在我们已经讨论了一些我们可以对模型进行的修改,以优化它们在边缘用例上的性能,让我们看看我们还可以使用哪些其他类型的机制来实现这一目的。

模型技术之外的优化

所有的先前优化技术都涉及对我们的模型进行修改,使其在边缘设备上运行得更高效。我们还可以采取其他措施,例如将训练好的模型转换为针对边缘设备优化的其他格式,或者优化在边缘运行我们的模型的硬件。让我们更详细地讨论这些机制。

硬件特定优化

根据边缘设备上的特定硬件(例如,CPU、GPU、张量处理单元TPU)等),可以使用不同的优化策略。例如,一些库提供了基于特定硬件优化计算图的工具。

专用库

如 TensorFlow Lite 和开放神经网络交换ONNX)运行时之类的库可以将模型转换为针对边缘设备优化的格式,进一步减少模型的内存占用并提高其速度。我们将在本节中更详细地讨论这些库。

TensorFlow Lite

TensorFlow Lite 是 TensorFlow 提供的一套工具,帮助我们运行移动、嵌入式和物联网设备上的模型。它是通过将我们的 TensorFlow 模型转换为更高效的格式,以便在边缘设备上使用来实现的。它还包括优化模型大小和性能的工具,以及实现硬件加速的工具。我们已经使用 TensorFlow Lite 转换我们的模型;这可以在本章所附的 Jupyter Notebook 中找到。

Edge TPU Compiler

Google 的 Edge TPU Compiler 是一种用于将模型编译到 Google 的 Edge TPUs 上运行的工具,这些 Edge TPUs 是为在边缘设备上运行 TensorFlow Lite 模型而设计的。

ONNX Runtime

ONNX 是一种用于表示机器学习模型的开放格式,它使得模型能够在各种机器学习框架之间进行迁移。它还提供了一个跨平台的推理引擎,称为 ONNX Runtime,它包括对各种硬件加速器的支持,旨在提供快速的推理并降低机器学习模型对资源的需求。

TensorRT

TensorRT 是 NVIDIA 开发的一种深度学习模型优化器和运行时库,用于在 GPU 上部署神经网络模型,尤其是在 NVIDIA 的嵌入式平台 Jetson 上。

TVM

Apache TVM 是一个开源的机器学习编译器堆栈,旨在使机器学习模型在各种硬件平台上高效部署成为可能。TVM 支持从各种深度学习框架中获取模型输入,包括 TensorFlow、Keras、PyTorch、ONNX 以及其他框架。

这些只是优化机器学习模型以便在边缘运行的一些工具。随着新型技术设备的不断涌现,边缘优化是一个活跃的研究领域,新的工具和机制仍在不断开发中。

在本章所附的 Jupyter Notebook 的动手活动中,我们使用了 TensorFlow Lite 来优化我们的模型,之后我们将优化后的模型存储在 Google Cloud Storage 中。从那里,我们可以轻松地将我们的模型部署到任何支持 TensorFlow Lite 解释器的设备。TensorFlow Lite 文档中提供了一份支持平台列表,其中还包含大量关于 TensorFlow Lite 如何在更详细层面工作的有用信息:www.tensorflow.org/lite/guide/inference#supported_platforms

到目前为止,我们已经涵盖了与模型部署相关的许多不同主题。让我们花些时间回顾一下我们学到了什么。

摘要

在本章中,我们讨论了各种用于托管机器学习模型的 Google Cloud 服务,例如 Vertex AI、Cloud Functions、GKE 和 Cloud Run。我们区分了在线和离线模型服务,其中在线服务用于实时预测,而离线服务用于批量预测。然后,我们探讨了部署机器学习模型时常见的挑战,例如数据/模型漂移、扩展、监控、性能以及保持模型更新。我们还介绍了 Vertex AI 的特定组件,这些组件使我们的模型部署和管理更加容易,例如 Vertex AI 模型注册表、Vertex AI 预测服务和 Vertex AI 模型监控。

具体来说,我们深入探讨了生产环境中监控模型,重点关注数据漂移和模型漂移。我们讨论了对抗这些漂移的机制,例如自动连续训练。

接下来,我们解释了 A/B 测试,用于比较模型的两个版本,并讨论了使用模型剪枝和量化等方法优化边缘部署的机器学习模型,以及用于优化我们的模型的库和工具,例如 TensorFlow Lite。

到目前为止,我们已经涵盖了 MDLC 中的所有主要步骤。我们接下来关注的主题将是如何使用 MLOps 自动化整个生命周期。请加入我们,在下一章中,你将继续你的旅程,成为一名专家 AI/ML 解决方案架构师,在这个过程中你已经走了很长的路,并且取得了巨大的进步!

第十一章:使用 Google Cloud 进行机器学习工程和 MLOps

通常估计,几乎 90% 的数据科学项目从未进入生产阶段。数据科学家在实验室花费大量时间训练和实验模型,但往往无法成功地将这些工作负载带入现实世界。主要原因是我们已经在本书的前几章中讨论过,在模型开发生命周期中的每一步都存在困难挑战。继我们上一章的内容之后,我们现在将更详细地探讨部署概念和挑战,并描述 机器学习运维MLOps)在解决这些针对大规模生产 AI/ML 工作负载的挑战中的重要性。

具体来说,本章将涵盖以下主题:

  • MLOps 简介

  • 为什么需要 MLOps 来部署大规模机器学习工作负载

  • MLOps 工具

  • 在 Google Cloud 上使用 Vertex AI Pipelines 实施 MLOps

尽管我们已经在本书的早期部分触及了一些这些概念,但我们将以对 MLOps 的更正式介绍开始本章。

MLOps 简介

MLOps 是软件开发行业 DevOps 概念的扩展,但专注于管理和自动化机器学习模型和项目生命周期。它超越了创建机器学习模型所需的战术步骤,并解决了当公司需要以规模管理数据科学用例的整个生命周期时出现的需求。现在是一个反思我们在本书前几章中概述的机器学习模型生命周期阶段的好时机,如图 图 11.1 所示。

图 11.1:数据科学生命周期阶段

图 11.1:数据科学生命周期阶段

从高层次来看,MLOps 的目标是自动化机器学习模型生命周期的所有各种步骤,例如数据收集、数据清洗和预处理、模型训练、模型评估、模型部署和模型监控。因此,它可以被视为一种旨在统一机器学习系统开发和日常运营的工程文化。这包括在机器学习项目中所有相关利益相关者之间的协作和沟通实践,例如公司的数据科学家、机器学习工程师、数据工程师、业务分析师和运营人员,以帮助持续管理整个机器学习生命周期。

在这个背景下,管理机器学习生命周期包括定义、实施、测试、部署、监控和管理机器学习模型,以确保它们在生产环境中可靠且高效地运行。

与 DevOps 的另一个相似之处在于,MLOps 实践也涵盖了持续集成、持续交付和持续部署(CI/CD)的概念,但重点在于如何开发机器学习模型。这确保了模型的新更改被正确集成、彻底测试,并且可以以系统化、可靠和可重复的方式部署到生产环境中。在这方面,目标是确保机器学习生命周期的任何部分(如数据预处理、训练等)都可以在以后以相同的结果重复。正如在软件 DevOps 的 CI/CD 环境中一样,这包括自动化步骤,如验证检查和集成测试,但在机器学习模型开发的情况下,它还增加了额外的组件,例如数据质量检查和模型质量评估。

正如我们在前面的章节中讨论的那样,将模型部署到生产环境是一项巨大的成就,但工作并没有就此结束。一旦机器学习模型被部署到生产环境中,就需要持续监控它们,以确保由于底层数据或其他因素的变化,其性能不会随时间退化。如果检测到任何问题,模型需要更新或用更新的模型替换。MLOps 还包括自动化该过程的工具,例如自动重新训练(例如,使用新鲜数据)、验证和部署新的模型版本。

这其中的另一个重要组成部分是跟踪我们在数据科学项目各个阶段产生的各种工件版本(例如,数据、模型和超参数值)。这不仅使得在需要时可以轻松回滚到以前的版本,而且还提供了合规性审计跟踪,有助于模型的可解释性和透明度,以及适当的治理机制,有助于确保机器学习模型被负责任和道德地使用。

现在我们已经了解了 MLOps 是什么,让我们更深入地探讨为什么它在开发、部署和管理大规模模型时尤为重要。

注意

在本书的后续部分,我们将讨论生成式人工智能/机器学习。在生成式人工智能/机器学习中,有一些特定的机器学习模型被用于其中,被称为大型语言模型LLMs)。这些模型还与一些额外的资源和工件相关联,并且行业中出现了一个新术语 LLMOps,用来指代这些工作负载的运营化。目前,我们将专注于传统的 MLOps。虽然许多这些概念也适用于 LLMOps 工作负载,但我们将在后面的章节中讨论 LLMOps 的额外考虑因素。

为什么需要 MLOps 来部署大规模机器学习工作负载

MLOps 最重要的方面是它帮助组织以更快、更高效和更可靠的方式开发机器学习模型,并允许数据科学团队在满足运营要求的同时进行实验和创新。

到现在为止,我们知道机器学习已经成为许多行业和部门的必要组成部分,提供了宝贵的见解和决策能力,但部署机器学习模型,尤其是在大规模上,面临着许多挑战。其中一些挑战只能通过 MLOps 来解决,我们将在本节中深入探讨这些挑战,并提供 MLOps 如何帮助解决这些挑战的例子。

在我们深入探讨之前,我要指出,本节中我们将讨论的这类挑战实际上适用于任何大规模生产产品的行业,无论这些产品是汽车、安全别针、玩具还是机器学习模型。

我将从一个汽车行业的类比入手,以突出大规模生产任何类型产品所需的概念。考虑在汽车发明之前,主要的交通工具可能是马车,而这种马车可能是在类似图 11.2中展示的工坊中制造的。

图 11.2:锻造车间(来源:https://www.hippopx.com/en/middle-ages-forge-workshop-old-castle-wagon-wheel-297217)

图 11.2:锻造车间(来源:www.hippopx.com/en/middle-a…

注意图 11.2中展示的生产环境的特征:

  • 这是一个小环境,只能容纳少数人一起工作。

  • 在这里工作的每个人周围都散落着许多随机工具,似乎并没有实施太多的标准化。

具有这些特征,任何类型的大规模协作都是不可能的,最多只能有三四个人一起工作来创造产品。在这种情况下,公司不可能每个月都生产大量产品(例如)。记住,大多数公司就是这样开始实施数据科学项目的,数据科学家在自己的电脑上执行各种实验并开发模型,使用基于个人偏好的任何随机工具,并试图以非标准化和非系统化的方式与同事分享学习和成果。

为了克服这些挑战,汽车行业发明了流水线的概念,一些最早的流水线可能看起来与图 11.3中展示的相似。

图 11.3:流水线(来源:https://pxhere.com/en/photo/798929)

图 11.3:流水线(来源:pxhere.com/en/photo/79…

注意图 11.3中展示的生产环境的特征:

  • 环境可以容纳很多人一起工作。

  • 工具和制造工艺已经标准化。

这种环境能够实现大规模协作,并且在任何给定的时间框架内可以生产出更多的产品。

随着汽车公司继续发展,它们继续将标准化和自动化应用于流程的每个步骤,从而提高了流程的效率和可重复性,直到今天的装配线看起来就像图 11**.4中所示的那样。

图 11.4:现代装配线(来源:https://www.rawpixel.com/image/12417375/photo-image-technology-factory-green)

图 11.4:现代装配线(来源:www.rawpixel.com/image/12417…

正如我们在图 11**.4中可以看到的,生产过程已经变得高度自动化,并且在大规模操作上更加高效。带着这个类比,让我们看看这个想法如何映射到机器学习模型开发生命周期的概念和步骤。

模型管理和版本控制

随着组织扩大其机器学习努力,正在运行中的模型数量可以呈指数增长。管理这些模型、跟踪它们的版本以及知道何时更新或淘汰它们可能变得相当困难。如果没有适当的版本控制和管理系统,机器学习模型及其相应的数据集可能会变得混乱,这会导致模型开发过程中的混淆和错误。事实上,当我们想到许多大型公司有数百甚至数千个模型在生产中,并且不断开发这些模型的新版本以改进其性能时,没有专业工具管理所有这些模型基本上是不可能的。MLOps 工具提供了促进更轻松模型管理和版本控制的机制,使团队能够以有组织和高效的方式跟踪和控制他们的模型。

生产力和自动化

训练、测试、部署和重新训练模型通常涉及许多重复性任务,随着机器学习工作负载规模的增加,手动管理这些过程所需的时间和精力可能会变得难以承受。考虑到 MLOps 在机器学习生命周期的各个阶段引入了自动化,例如数据预处理、模型训练、测试、部署和监控,这使得数据科学团队能够专注于更有价值的任务。自动化的重要方面之一是它减少了人为错误的可能性,这有助于避免影响业务的错误,并提高生产力。

可重复性

可重现性可以说是任何大规模生产产品的行业中最重要的因素之一。想象一下,每次你尝试从一家公司购买相同的产品时,你都会收到一些略有不同的事物。很可能会失去对该公司的信任,并停止购买他们的产品。如果没有在生产过程中建立良好的可重现性,公司根本无法实现规模化。在机器学习的背景下,当我们拥有大量模型和数据集,以及不同类型的开发环境时,确保实验的可重现性可能是一个重大的挑战。由于数据、模型和代码缺乏版本控制,可能很难重现之前实验的精确条件。这可能导致结果不一致,并使基于先前工作的构建变得困难。MLOps 框架提供了维护所有实验记录的工具和实践,包括使用的数据、设置的参数、模型架构和结果。这使得实验可以轻松重复、比较和审计。

持续集成/持续部署(CI/CD)

我们应该注意的是,CI/CD 是 DevOps 的一个如此重要的组成部分,以至于这两个术语经常几乎可以互换使用。

更有趣的是,我们还可以在标准的 DevOps CI/CD 管道内管理 MLOps 管道。例如,当使用 Vertex AI Pipelines 时,我们可以使用代码定义我们的管道,并将该代码存储在代码仓库中,如 Google Cloud Source Repositories,然后通过 Google Cloud Build 中的 DevOps CI/CD 管道进行管理。这使得我们可以将 DevOps 的所有好处,如代码版本控制和自动化测试,应用于定义我们管道的代码,从而控制对管道定义的更新方式。

模型验证和测试

由于机器学习模型的概率性质,测试机器学习模型涉及独特的挑战,传统的 DevOps 软件测试技术可能不足以应对。MLOps 引入了专门用于自动化验证和测试 ML 模型的实践和方法,确保在部署到生产之前,它们的表现符合预期,并在部署后的整个生产生命周期中保持如此。此外,能够可靠地重现产品,在大规模开发产品时也要求能够随着时间的推移逐步改进产品。CI/CD 管道自动化了模型的测试和部署,确保新更改能够无缝、高效地集成和部署,并尽可能减少错误。这使得数据科学家能够快速、轻松、以符合公司要求标准的方式实验和尝试不同的更新。

监控和维护

正如我们讨论的,一旦 ML 模型部署,它们的性能需要持续监控,以确保它们始终提供准确的预测。我们也讨论了现实世界数据的变化如何导致模型性能随时间下降,因此,我们需要工具和流程来进行持续监控和警报,使团队能够快速响应任何性能下降,并在必要时重新训练模型。这是任何 MLOps 框架的重要组件。

协作与沟通

随着 ML 项目的扩展,它们通常需要跨各种团队和角色进行协作,包括数据科学家、ML 工程师、数据工程师、业务分析师和 IT 运维人员。在许多组织中,开发 ML 模型的数据科学家和在生产中部署和维护这些模型的 IT 运维团队之间存在差距。没有共同的平台和标准实践,不同的团队成员可能会使用不一致的方法、工具和数据,导致工作流程低效和潜在的错误。MLOps 促进了这些利益相关者之间的有效协作和沟通,使他们能够更有效地合作,避免误解或错位。

监管合规与治理

在医疗保健和金融等行业,ML 模型必须符合特定的监管要求。MLOps 提供了确保模型透明度、可解释性和可审计性的机制,因此有助于维护监管合规。此外,没有 MLOps,跨不同团队和业务单元管理多个模型可能会成为一个挑战。MLOps 允许集中式模型治理,这使得跟踪各种模型的表现、状态和所有者变得更加容易。

既然我们已经深入探讨了为什么 MLOps 是必要的,尤其是在管理大规模 ML 工作负载的背景下,让我们来看看行业内为实施这些概念而开发的一些流行工具。

MLOps 工具

到目前为止,我们已经讨论了 MLOps 的目标和好处,但在现实世界中我们如何实现这些目标和好处呢?许多工具已经开发出来,以促进 MLOps 的各个方面,从数据版本化到模型部署和监控。在本节中,我们讨论使 MLOps 成为现实的具体工具。

管道编排工具

当我们想要标准化流程中的步骤并自动运行它们时,我们通常将它们配置为一系列顺序动作。这个顺序动作集通常被称为管道。然而,仅仅配置步骤的顺序是不够的;我们还需要某种类型的系统来“编排”(即执行和管理)管道,我们可以使用一些流行的流程编排工具来完成这个任务,例如 Kubeflow、Airflow 和TensorFlow ExtendedTFX)。我们已经在第六章中介绍了 Airflow,但让我们更详细地看看 Kubeflow 和 TFX。

Kubeflow

这是一个由谷歌创建的开源项目,现在由一个开源贡献者社区维护,旨在使在 Kubernetes 上运行机器学习工作流程更加容易。Kubeflow 的基本原则包括可移植性、可扩展性和可扩展性。可移植性指的是“一次编写,到处运行”的概念,这是基于容器化的核心原则,即您可以在不同的计算环境中以一致的方式运行您的作业。可扩展性指的是使用 Kubernetes 轻松扩展您的模型训练和预测工作负载的能力,而可扩展性意味着您可以通过添加与 Kubeflow 轻松集成的流行工具和库来实现定制化用例。在这种情况下,Kubeflow 不仅仅是一个工具,而是一个框架和生态系统,我们可以用它来编排和管理我们的机器学习工作流程。以下是 Kubeflow 生态系统的一些核心组件:

  • Kubeflow PipelinesKFP):用于定义、部署和管理机器学习工作流程

  • Katib:用于超参数调整

  • KFServing:用于以可扩展的方式提供模型

  • TensorFlow 和 PyTorch 操作符:用于运行 TensorFlow 和 PyTorch 作业

  • 训练操作符:用于分布式训练

在本章相关的实践练习中,我们将特别深入探讨 Kubeflow Pipelines,我们将构建一个管道并在 Google Cloud Vertex AI 中执行它。除了 KFP 之外,Vertex AI 还支持使用 TFX 来构建和管理机器学习管道。让我们看看下一个。

TensorFlow Extended(TFX)

TFX 是一个端到端平台,也是由谷歌创建的,用于管理和部署生产级机器学习流水线。正如其名所示,它被设计为 TensorFlow 的扩展,旨在使将训练好的模型投入生产变得更加容易。就像 KFP 一样,TFX 提供了实现机器学习生命周期每个阶段的组件,涵盖了从数据摄取和验证到模型训练、调优、服务以及监控的各个方面。同样,TFX 的核心原则包括可扩展性和当然的扩展性。TFX 的其他核心原则还包括可重复性和模块化。可重复性,正如我们在本章前面讨论的,是大规模生产几乎所有内容的关键要求。在这个上下文中,模块化指的是 TFX 的各个组件可以单独使用或组合使用,从而实现定制化。说到这里,TFX 的各个组件和职责包括以下内容:

  • ExampleGen,将数据导入 TFX 流水线

  • StatisticsGen,计算导入数据的统计信息,这对于理解数据和特征工程至关重要

  • SchemaGen,检查数据集统计信息并为数据集创建模式

  • ExampleValidator,用于识别和分析数据集中的异常值和缺失值

  • Transform,可用于在数据集上进行特征工程

  • Trainer,使用 TensorFlow 定义和训练模型

  • Tuner,可用于使用 Keras Tuner 优化超参数

  • InfraValidator,确保模型可以在生产环境中加载和提供服务

  • Evaluator,使用 TensorFlow 模型分析库评估训练模型的指标

  • BulkInferrer,对模型进行批量处理

  • 祝福和部署:如果模型经过验证,它可以被“祝福”,然后使用如 TensorFlow Serving 之类的服务基础设施进行部署。

考虑到 TFX 和 KFP 都可以用于在 Google Cloud Vertex AI 中构建和运行流水线,选择使用哪一个可能会有些挑战。关于这个问题,官方的 Google Cloud 文档建议如下:

“如果你在处理 TB 级结构化数据或文本数据的 ML 工作流中使用 TensorFlow,我们建议你使用 TFX 构建你的流水线……对于其他用例,我们建议你使用 Kubeflow 的 Pipelines SDK 构建你的流水线。”

我们在本章中不会过多地关注 TFX 的细节,但如果你想了解如何构建 TFX 流水线,TensorFlow 文档中有一个有用的教程,链接如下:www.tensorflow.org/tfx/tutorials/tfx/gcp/vertex_pipelines_simple

此外,还有许多其他开源工具可用于实现 MLOps 流程,例如 MLFlow,它提供了跟踪实验、将代码打包成可重复运行以及共享和部署模型的接口。

虽然 KFP 和 TFX 可以用来编排您的整个 MLOps 流程,但也有专注于 ML 生命周期更具体组件的工具,并且可以与 KFP 和 TFX 集成,以便定制您的整体流程。接下来,让我们看看这些工具中的一些。

实验和谱系跟踪工具

正如我们在本章以及前几章中讨论的那样,在运行大规模 ML 工作负载时,跟踪每个模型的每个开发步骤非常重要,这样您就可以轻松理解任何给定模型是如何创建的。这对于可重复性、可解释性和合规性等特性是必需的。想象一下,如果您有数千个模型在生产中运行,合规监管机构要求您解释某个特定模型是如何创建的。如果没有一个良好的系统来跟踪每个模型的每个创建方面的详细信息,例如用于训练模型的数据集版本、使用了哪些类型的算法和超参数、初始评估指标是什么,以及谁将模型部署到生产中,这将是一项不可能完成的任务。这就是实验和谱系跟踪工具发挥作用的地方。让我们考虑一下在这方面我们有哪些工具可以利用。

Vertex AI 实验服务

Vertex AI 实验服务是一个托管服务,帮助您跟踪、比较和分析您的机器学习实验。它提供了一个集中管理实验的地方,并使得比较不同的模型和超参数变得容易。一旦创建了一个实验(使用 Vertex AI SDK 或 Vertex AI 用户界面),您就可以开始跟踪您的训练运行,Vertex AI 实验服务将自动收集训练运行中的指标,如准确率、损失和训练时间。

一旦训练运行完成,您可以查看指标图和表格,并可以使用统计测试来确定哪个模型是最好的。您还可以使用实验仪表板来可视化您的结果,并可以使用实验 API 将结果导出到电子表格或笔记本中。

TensorBoard

TensorBoard 是 TensorFlow 附带的一个基于网页的工具,旨在帮助可视化和理解机器学习模型,以及调试和优化它们。它为机器学习模型和训练过程的各个方面提供了一个交互式界面,并可以轻松创建如我们在前几章中介绍的 ROC 曲线等指标图。

模型部署和监控工具

在这个背景下,我们有 Vertex AI Prediction 和 Model Monitoring 等工具,我们在第十章中对其进行了详细审查。还有像TensorFlow Serving(简称TFServing)这样的开源工具,用于 TensorFlow 模型,以及 TorchServe。

模型可解释性和可解释性工具

如我们之前所讨论的,模型可解释性和可解释性是机器学习中的极其重要的概念。它们与可重复性、合规性和性能相关。例如,如果你对你的模型工作原理没有很好的理解,那么在系统性地持续改进它们时将会更加困难。这些概念也与公平性相关。也就是说,为了确保模型做出公平和道德的预测,你需要详细了解其工作原理。幸运的是,有一些工具可以帮助我们在这方面,我们在这里讨论这些工具。

Google Cloud Vertex AI 提供了诸如可解释 AI 和公平性指标等工具,这些工具可以帮助我们了解我们的机器学习模型是如何工作的,以及它们在不同条件下会如何表现。还有像SHAP(代表SHapley Additive exPlanations)这样的开源工具,它使用博弈论方法来解释任何机器学习模型的输出,以及LIME(代表Local Interpretable Model-agnostic Explanations),这是一个 Python 库,允许我们解释任何机器学习分类器的预测。

本书下一章将致力于介绍偏差、公平性、可解释性和谱系等概念,因此我们将更详细地探讨这些概念和工具。

现在我们已经介绍了许多可以用于实现 MLOps 工作负载的不同类型的工具,让我们深入探讨如何在 Google Cloud Vertex AI 上具体实现这一点。

使用 Vertex AI Pipelines 在 Google Cloud 上实现 MLOps

在本节中,我们将介绍在 Google Cloud 上使用 Vertex AI Pipelines 构建 MLOps 管道的步骤。

前提条件:IAM 权限

在本节中,我们将使用我们在第五章中创建的相同的 Vertex AI Workbench 笔记本实例。该用户管理的笔记本使用默认的 Compute Engine 服务账户,该账户默认被授予 IAM 基本编辑器角色。当我们在我们笔记本中构建和执行我们的管道时,我们决定让我们的管道继承笔记本使用的相同权限。这是通过不指定不同角色给我们的管道执行所做的决定。如果我们想的话,我们可以指定不同的角色,但为了简单起见,我们将采用让我们的管道使用默认 Compute Engine 服务账户的方法。默认情况下,编辑器角色(默认 Compute Engine 服务账户使用的角色)将允许我们在本章的 Vertex AI 中执行所有必需的活动,但我们的管道还需要在 Dataproc 上运行一些步骤。因此,我们将 Dataproc Worker 和 Dataproc 编辑器角色添加到默认 Compute Engine 服务账户中。为此,执行以下步骤:

  1. 在 Google Cloud 控制台中,导航到Google Cloud 服务IAM & AdminIAM

  2. 在主体列表中,点击默认 Compute Engine 服务账户的铅笔符号(服务账户名称将具有格式-[项目编号]-compute@developer.gserviceaccount.com;参见图 11**.5以供参考)。

图 11.5:编辑服务账户

图 11.5:编辑服务账户

  1. 在出现的屏幕上,选择Dataproc,并从角色列表中选择Dataproc 编辑器

  2. 重复步骤 3,并选择Dataproc Worker

  3. 更新后的角色将如图图 11**.6所示。

图 11.6:更新后的角色

图 11.6:更新后的角色

  1. 点击保存

就这样——你已经成功添加了所需的角色。

实现

如前节所述,我们可以使用我们在第五章中创建的相同的 Vertex AI Workbench 笔记本实例来构建我们的 MLOps 管道。请在该笔记本实例上打开 JupyterLab。在屏幕左侧的目录浏览器中,导航到Chapter-11目录并打开mlops.ipynb笔记本。您可以选择**Python (Local)**作为内核。同样,您可以通过选择单元格并在键盘上按Shift + Enter来运行笔记本中的每个单元格。除了相关代码外,笔记本还包含描述代码正在做什么的 Markdown 文本。

在实际练习中,我们将构建并运行一个 MLOps 管道,该管道将执行我们在本书中迄今为止所涵盖的 ML 模型开发生命周期的所有阶段。我们的管道如图图 11**.5所示。

图 11.7:Vertex AI 管道

图 11.7:Vertex AI 管道

在高层次上,我们的管道将执行以下步骤:

  1. 数据采集:首先,我们需要将我们的数据导入 Google Cloud 环境。我们使用 Google Cloud Storage 来存储我们的数据,当我们的流程启动数据处理和模型训练作业时,我们的数据将从那里读取。

  2. 预处理我们的数据:Google Cloud 提供了多种数据预处理工具,包括 Dataflow、Dataprep 和 Dataproc,我们已在 第六章 中进行了探讨。最近,Google Cloud 还发布了一项名为 Serverless Spark 的服务,它使我们能够在不配置和管理所需的基础设施的情况下运行 Spark 作业。这就是我们在实际练习中在流程中实现数据预处理作业所使用的工具。

  3. 开发和训练我们的模型:我们的流程在 Vertex AI 中使用前一步创建的已处理数据训练 TensorFlow 模型。

  4. 在 Vertex AI 模型注册表中注册我们的模型。

  5. 部署我们的模型:我们的流程进入下一步,即将我们的模型部署到生产环境中。在这种情况下,我们的流程创建一个 Vertex AI 端点,并在该端点上托管我们的模型。

让我们通过检查我们刚刚创建的解决方案架构来更详细地了解我们的流程正在做什么,如图 图 11.6 所示。

图 11.8:Vertex AI 中的 MLOps 流程

图 11.8:Vertex AI 中的 MLOps 流程

图 11.6 中,横跨整个图的水平矩形部分代表我们在 Vertex AI Pipelines 中运行的流程。流程中的每个步骤都进行了编号,编号代表以下操作:

  1. 我们流程中的数据处理步骤向 Dataproc Serverless 提交一个 Spark 作业以执行我们的 PySpark 处理脚本。

  2. Dataproc 从 Google Cloud Storage 获取我们的 PySpark 脚本和原始数据,并执行 PySpark 脚本来处理原始数据。

  3. Dataproc 将处理后的数据存储在 Google Cloud Storage 中。

  4. 数据处理作业状态已完成。

  5. 流程中的下一步——模型训练步骤——被调用。

  6. Vertex AI Pipelines 向 Vertex AI 训练服务提交一个模型训练作业。

  7. 为了执行我们的自定义训练作业,Vertex AI 训练服务从 Google Artifact Registry 获取我们的自定义 Docker 容器,并从 Google Cloud Storage 获取训练数据。这些数据就是我们的数据处理作业存储在 Google Cloud Storage 中的数据(即,这是由我们的数据处理作业创建的已处理数据)。

  8. 当我们的模型训练完成后,训练好的模型工件被保存在 Google Cloud Storage 中。

  9. 模型训练作业状态已完成。

  10. 我们管道的下一步——模型导入步骤——被调用。这是一个中间步骤,为我们的管道的后续组件准备模型元数据。在这种情况下,相关的元数据包括模型工件在 Google Cloud Storage 中的位置以及用于提供我们模型的 Docker 容器镜像在 Google Artifact Registry 中的规范。

  11. 我们管道的下一步——模型上传步骤——被调用。此步骤引用了模型导入步骤中的元数据。

  12. 模型元数据用于在 Vertex AI 模型注册表中注册模型。这使得在 Vertex AI 中部署我们的模型以处理流量变得容易。

  13. 模型上传作业状态已完成。

  14. 我们管道的下一步——端点创建步骤——被调用。

  15. 在 Vertex AI 预测服务中创建了一个端点。这个端点将用于托管我们的模型。

  16. 端点创建作业状态已完成。

  17. 我们管道的下一步——模型部署步骤——被调用。

  18. 我们的模式已部署到 Vertex AI 预测服务中的端点。此步骤引用了由我们的管道刚刚创建的端点的元数据,以及 Vertex AI 模型注册表中我们的模型的元数据。

  19. 模型部署作业状态已完成。

除了我们管道明确执行的上述所有步骤之外,Vertex AI Pipelines 服务还将在 Vertex ML Metadata 服务中注册与我们的管道执行相关的元数据。

我们的模式现在已准备好处理推理请求!是不是很令人印象深刻,所有这些跨越多个 Google Cloud 服务的活动和 API 调用都是由我们的管道自动编排的?在我们定义了管道之后,它可以在我们希望的时候自动运行,在整个过程中不需要任何人工交互,除非我们认为有必要让人类参与任何步骤。

如果你已经完成了笔记本中的活动,那么你现在正式成为 AI/ML 大师!说真的,你刚刚实现了一个端到端的 MLOps 管道。这是 ML 行业中的一个极其复杂和高级的任务。

在取得所有这些成功之后,让我们花些时间来反思我们在本章中学到了什么。

摘要

在本章中,我们从高层次介绍了 MLOps,这基本上是机器学习、DevOps 和数据工程的结合,其主要目标是自动化 ML 生命周期,从而改善工作流程和科学家与工程师之间的协作。我们讨论了 MLOps 如何使组织简化其 ML 操作,加快部署速度,并在生产中保持高质量模型,从而实现更高效、有效和可靠的 ML 工作流程,并最大限度地提高组织从其 ML 项目中获得的价值。

我们讨论了 MLOps 解决的各种痛点,包括但不限于与管理和版本控制模型、确保可重复性和一致性、监控和维护模型以及促进不同团队间协作相关的挑战。

然后,我们深入探讨了为什么 MLOps 对于部署大规模机器学习工作负载至关重要。它解决了机器学习系统中可能出现的一系列挑战,从管理和版本控制模型到确保实验的可重复性。它还促进了模型的持续集成、部署、监控和验证,并促进了团队间的更好协作。

然后,我们讨论了各种 MLOps 工具,如 Kubeflow Pipelines 和 TensorFlow Extended 等。这些工具中的每一个都提供独特的功能,满足 ML 生命周期不同阶段的需求,包括数据版本控制、实验跟踪和模型部署。

然后,我们使用 Google Cloud 上的 Vertex AI Pipelines 实现了 MLOps,这涉及多个步骤,包括管理数据集、预处理数据、训练模型和监控模型。

接下来,我们将探讨机器学习行业中的四个重要且相互关联的主题,即偏差、可解释性、公平性和溯源。让我们进入下一章,详细探讨这些概念。