精通-Azure-机器学习第二版-二-

39 阅读1小时+

精通 Azure 机器学习第二版(二)

原文:annas-archive.org/md5/4810ad92d2f87002f854999a7a373fce

译者:飞龙

协议:CC BY-NC-SA 4.0

第二部分:数据摄取、准备、特征工程和管道化

在本节中,我们将学习如何在 Azure 中加载数据和存储数据,以及如何从 Azure 机器学习工作区管理这些数据。然后,我们将研究预处理和可视化数据的技术,以及如何从高维数据集中获得洞察。从那时起,我们将专注于如何通过创建和转换特征以及为监督建模创建标签来优化我们的数据集。我们将使用复杂语义词嵌入来执行自然语言处理的高级特征提取。最后,我们将利用 Azure 机器学习管道将所学知识整合到自动化的预处理和训练管道中。

本节包括以下章节:

  • 第四章, 摄取数据和管理数据集

  • 第五章, 执行数据分析与可视化

  • 第六章, 特征工程和标签化

  • 第七章, 使用 NLP 的高级特征提取

  • 第八章, Azure 机器学习管道

第四章:第四章:导入数据和管理数据集

在上一章中,我们设置了 Azure 机器学习工作区,进行了数据实验,并安排了在 Azure 机器学习中远程计算目标上运行的脚本。在本章中,我们将学习如何连接数据存储,创建、探索、访问和跟踪 Azure 机器学习中的数据。

首先,我们将通过了解数据存储和数据集的概念来探讨 Azure 机器学习中数据是如何管理的。我们将查看不同类型的数据存储,并学习在 Azure 中组织和管理数据以用于机器学习(ML)的最佳实践。

接下来,我们将创建一个Azure Blob 存储账户,并将其作为数据存储连接到 Azure 机器学习。我们将介绍使用流行的 CLI 工具以及Azure Data FactoryAzure Synapse Spark服务将数据导入 Azure 的最佳实践。

在下一节中,我们将学习如何从 Azure 中的数据创建数据集,访问和探索这些数据集,并将数据有效地传递到 Azure 机器学习工作区中的计算环境中。最后,我们将讨论如何通过第三方数据源访问 Azure 开放数据集,以改善模型性能。

本章将涵盖以下主题:

  • 选择 Azure 机器学习的数据存储解决方案

  • 创建数据存储和导入数据

  • 在 Azure 机器学习中使用数据集

技术要求

在本章中,我们将使用以下 Python 库和版本来创建和管理数据存储和数据集:

  • azureml-core 1.34.0

  • azureml-sdk 1.34.0

与前几章类似,您可以使用本地 Python 解释器或 Azure 机器学习中的笔记本环境运行此代码。

本章中所有的代码示例都可以在本书的 GitHub 仓库中找到:github.com/PacktPublishing/Mastering-Azure-Machine-Learning-Second-Edition/tree/main/chapter04

选择 Azure 机器学习的数据存储解决方案

当在本地开发机器上运行 ML 实验或训练脚本时,您通常不会考虑管理您的数据集。您可能将训练数据存储在本地硬盘、外部存储设备或文件共享中。在这种情况下,访问实验或训练数据不会成为问题,您也不必担心数据位置、访问权限、最大吞吐量、并行访问、存储和出口成本、数据版本等问题。

然而,一旦你开始在远程计算目标上训练机器学习模型,例如云中的 VM 或 Azure 机器学习内部,你必须确保所有可执行文件都能有效地访问训练数据。如果你与其他人合作,他们也需要从多个环境和多台机器并行访问数据以进行实验、标记和训练,那么这一点尤为重要。如果你部署了一个需要访问这些数据的模型——例如,查找分类结果的标签、根据用户的评分历史进行评分推荐等——那么这个环境也需要访问数据。

在本节中,我们将学习如何在 Azure 中管理不同用例的数据。我们将首先了解 Azure 机器学习提供的抽象,以方便数据访问进行机器学习实验、训练和部署。

在 Azure 机器学习中组织数据

在 Azure 机器学习中,数据以数据集的形式进行管理,数据存储以数据存储的形式进行管理。这种抽象隐藏了数据集和数据存储对象背后的位置、数据格式、数据传输协议和访问权限的细节,因此让 Azure 机器学习用户能够专注于探索、转换和管理数据,而无需担心底层存储系统。

数据存储是物理数据存储系统的抽象,用于将现有的存储系统连接到 Azure 机器学习工作区。为了通过创建数据存储将现有存储连接到工作区,你需要提供存储系统的连接和身份验证详情。一旦创建,数据存储就可以通过数据存储对象被用户访问,该对象将自动使用数据存储定义中提供的凭据。这使得向在 Azure 机器学习工作区中协作的开发人员、数据工程师和科学家提供数据存储访问变得容易。目前,以下服务可以作为数据存储连接到工作区:

  • Azure Blob 容器

  • Azure 文件共享

  • Azure Data Lake

  • Azure Data Lake Gen2

  • Azure SQL 数据库

  • Azure Database for PostgreSQL

  • Databricks 文件系统

  • Azure Database for MySQL

虽然数据存储是数据存储系统的抽象,但数据集是数据的通用抽象——例如,以远程服务器上的文件形式存在的数据,这些文件可以通过公共 URL 访问,或者是在数据存储中的文件和表。Azure 机器学习支持两种数据格式抽象,即表格数据集文件数据集。前者用于定义表格数据——例如,来自逗号或分隔符分隔的文件、Parquet 和 JSON 文件,或来自 SQL 查询——而后者用于指定来自文件和文件夹的任何二进制数据,例如图像、音频和视频数据。

可以直接从公开可用的 URL 定义和使用表格数据集,这被称为 pandasrequests。表格数据集和文件数据集都可以在您的工作区中注册。我们将把这些数据集称为 注册数据集。注册数据集将在您的 Azure Machine Learning Studio 下的 数据集 中显示。

理解 Azure 机器学习的默认存储账户

在 Azure Machine Learning 中存在一个特殊的数据存储,用于在执行实验运行时内部存储所有快照、日志、图表、模型等。这被称为 默认数据存储,是一个 Azure Blob 存储账户,并在您设置初始工作区时自动创建。在创建工作区期间,您可以选择自己的 Blob 存储作为默认数据存储,或者在 Azure Machine Learning Studio 中连接您的存储账户并将其标记为默认。

图 4.1 展示了 Azure Machine Learning Studio 中的数据存储列表。默认数据存储被标记为 默认,并在设置 Azure Machine Learning 工作区时自动生成。要访问此视图,只需在 Azure Machine Learning Studio 左侧菜单的 管理 类别下点击 数据存储。要查看现有数据集,请在 资产 类别下点击 数据集

图 4.1 – Azure 机器学习中的默认数据存储

图 4.1 – Azure 机器学习中的默认数据存储

当没有定义其他数据存储时,默认数据存储由 Azure Machine Learning 内部使用来存储所有资产和工件。您可以通过创建数据存储引用,以与自定义数据存储相同的方式访问和使用默认数据存储。以下代码片段显示了如何获取默认数据存储的引用:

from azureml.core import Datastore
default_datastore = Datastore.get_default(ws)

默认数据存储由 Azure Machine Learning 内部使用,用于在机器学习生命周期中存储所有资产和工件。使用前面的代码片段,您可以访问默认数据存储以存储自定义数据集和文件。

一旦我们访问了默认的数据存储并连接了自定义数据存储,我们就需要考虑一个策略来高效地存储不同机器学习用例的数据。让我们在下一节中解决这个问题。

探索在 Azure 中存储训练数据的选项

Azure 支持多种不同的数据存储解决方案和技术,以在云中存储数据——正如我们在上一节中看到的,其中许多都是 Azure Machine Learning 支持的数据存储。在本节中,我们将探讨一些这些服务和技术,以了解哪些可以用于机器学习用例。

数据库系统可以根据数据类型和访问数据的类型进行广泛分类,分为以下两类:

  • 关系数据库管理系统RDBMSs)通常用于存储使用基于 B 树的有序索引的规范化事务数据。典型的查询通过连接多个表中的多行来过滤、分组和聚合结果。Azure 支持不同的 RDBMS,例如 Azure SQL 数据库、Azure Database for PostgreSQL 和 MySQL。

  • NoSQL:基于键值存储的系统通常用于存储使用基于哈希或有序索引的非规范化数据。典型的查询通过基于分区键分布的集合访问单个记录。Azure 支持不同的基于 NoSQL 的服务,如 Azure Cosmos DB 和 Azure 表存储。

如您所见,根据您的用例,您可以使用这两种数据库技术来存储机器学习数据。虽然 RDBMS 是存储机器学习训练数据的优秀技术,但 NoSQL 系统非常适合存储查找数据,例如训练标签,或机器学习结果,如推荐、预测或特征向量。

除了选择数据库服务之外,机器学习的另一种流行选择是使用数据存储系统。在磁盘上,大多数数据库服务以数据页的形式持久化在 文件blob 存储系统 上。由于它们的可扩展性、性能、吞吐量和成本,blob 存储系统是存储各种数据和资产以供机器学习使用的非常流行的选择。Azure 机器学习广泛使用 blob 存储系统,特别是用于存储所有操作资产和日志。

流行的 Azure Blob 存储服务包括 Azure Blob 存储和 Azure Data Lake Storage,它们通过不同的数据格式选择提供了极大的灵活性,以实现高效的数据存储和访问解决方案。虽然 Azure Blob 存储支持大多数基于 blob 的文件系统操作,但 Azure Data Lake Storage 实现了高效的目录服务,这使得它成为横向可扩展文件系统的流行通用存储解决方案。它是存储大型机器学习训练数据集的流行选择。

虽然表格数据可以在 RDBMS 系统中有效地存储,但在 blob 存储系统上存储数据时,通过选择正确的数据格式和嵌入的聚类索引也可以实现类似的功能。选择正确的数据格式将允许您的文件系统有效地存储、读取、解析和聚合信息。

常见的数据格式选择可以分为文本格式(CSV、JSON 等)以及二进制格式(图像、音频、视频等)。用于存储表格数据的二进制格式通常分为行压缩格式(Protobuf、Avro、SequenceFiles 等)或列压缩格式(Parquet、ORC 等)。另一种流行的选择是使用 Gzip、Snappy 或其他压缩算法压缩整个文件。

大多数数据存储系统都共同拥有一种结构,即分层路径或目录结构来组织数据块。对于存储机器学习训练数据的一个流行选择是实施数据分区策略。这意味着数据被组织在多个目录中,每个目录包含特定键的所有数据,也称为分区键。

云服务提供商提供各种不同的存储解决方案,可以通过选择不同的索引、分区、格式和压缩技术进一步定制。对于存储机器学习表格训练数据的一个常见选择是使用列压缩的二进制格式,如 Parquet,按摄取日期分区,存储在 Azure 数据湖存储上,以实现高效的管理操作和可扩展的访问。

创建数据存储和导入数据

在查看 Azure 中用于 ML 处理的数据存储选项之后,我们现在将创建一个存储账户,我们将在整个书中使用它来存储原始数据和 ML 数据集。此外,我们还将探讨如何手动将一些数据传输到我们的存储账户,以及如何通过利用 Azure 中可用的集成引擎自动执行此任务。

创建 Blob 存储并将其与 Azure 机器学习工作区连接

首先,让我们创建一个存储账户。任何存储账户都将附带一个文件共享、一个队列以及表格存储,以便您在其他场景中利用。除了这三个之外,根据您在创建时提供的设置,您最终可能会得到 Blob 存储或数据湖。默认情况下,将创建 Blob 存储账户。如果我们想创建数据湖账户,我们必须将enable-hierarchical-namespace设置设置为True,因为数据湖提供了一个实际的分层文件夹结构,而不是一个扁平的命名空间。

创建 Blob 存储

记住这一点,让我们创建一个 Blob 存储账户:

  1. 导航到您选择的终端,登录 Azure,并确认您正在正确的订阅中工作,正如我们在第三章,“准备 Azure 机器学习工作区”中学到的。

  2. 由于我们要创建一个存储账户,让我们通过运行以下命令来查看创建账户的选项和所需设置:

    $ az storage account create -h
    

查看结果,您将看到一个非常长的可能参数列表,但唯一必需的是nameresource-group。尽管如此,我们仍然应该进一步查看,因为许多其他设置仍然设置为某些默认值,这些值可能不适合我们的情况。

在查看列表时,您会发现许多关于网络或安全设置的选项。大多数选项的默认设置是至少允许从任何地方访问。在这个时候,我们不太关心虚拟网络集成或处理 Azure Key Vault 中的自己的管理密钥。

除了所有这些选项之外,还有一些定义了我们设置的存储账户类型的选项,即enable-hierarchical-namespacekindlocationsku

我们已经讨论了第一个选项,并且默认值为False,因此我们可以忽略它。

查看kind,您会看到一个存储类型的列表。您可能会认为我们需要选择BlobStorage,但不幸的是,这是一个遗留设置,留在了仍在运行第一版(V1)的任何存储账户中。对于我们的场景,默认的(StorageV2)是最佳选项。

查看location,我们看到我们可以为所有部署设置一个默认位置,因此它没有被标记为必需。由于我们迄今为止还没有这样做,我们将在部署存储账户时提供它。

最后,查看sku,我们看到这是一个关于所使用的磁盘技术类型(Standard/Premium)的选项组合,其中Standard表示 HDD 存储,Premium表示 SSD 存储,以及一个定义数据冗余方案(LRS/ZRS/GRS/RAGRS/GZRS)的选项。如果您想了解更多关于冗余选项的信息,请点击此链接:docs.microsoft.com/en-us/azure/storage/common/storage-redundancy。由于两者都会增加成本,您可以保留默认值(Standard_RAGRS)或选择本地冗余(Standard_LRS)。

  1. 让我们创建我们的存储账户。请注意,您选择的名称必须是全局唯一的,因此您不能选择在以下命令中将要读取的名称:

    az storage account create \
         --name mldemoblob8765 \
         --resource-group mldemo \
         --location westus \
         --sku Standard_LRS \
         --kind StorageV2
    

该命令生成的输出将显示创建的存储账户的详细设置。

  1. 作为最后一步,让我们在我们的新 Blob 存储中创建一个容器。为此,请使用适当的账户名称运行以下命令:

    az storage container create \
        --name mlfiles \
        --account-name mldemoblob8765
    

结果将在最后显示True,但在之前会给出一些警告,类似于以下内容:

There are no credentials provided in your command and environment, we will query for account key for your storage account. It is recommended to provide --connection-string, --account-key or --sas-token in your command as credentials.

命令成功执行,因为它通过我们的会话自动拉取了存储账户的密钥。通常,要访问存储账户,我们需要一个 AD 身份、访问整个账户的密钥(account-key)或共享访问密钥(sas-token)以访问特定子目录或容器。当从 ML 工作区连接时,我们将会回到这一点。

要检查结果,请运行以下命令:

az storage container list \
    --account-name mldemoblob8765 \
    --auth-mode login

现在我们已经有了存储账户,让我们将其连接到我们的 Azure 机器学习工作区。

在 Azure 机器学习中创建数据存储

为了在处理我们的 ML 脚本时不再烦恼存储账户本身,我们现在将创建一个永久连接到存储账户中的容器,并将其定义为 Azure 机器学习工作区中的一个数据存储。

以下步骤将指导您完成此过程:

  1. 首先,让我们通过运行以下命令来了解创建数据存储所需的内容:

    az ml datastore create -h
    

通过查看输出,我们了解到需要资源组的名称、ML 工作区的名称和一个 YAML 文件。我们有两个这样的东西。因此,让我们了解 YAML 文件应该是什么样子。

  1. 导航至docs.microsoft.com/en-us/azure/machine-learning/reference-yaml-datastore-blob,在那里你可以找到我们文件的所需架构和一些示例。通过查看示例,你会发现它们主要在认证存储账户的方式上有所不同。其中最安全的是通过 SAS 令牌进行限制访问,因此我们将选择这条路径。

  2. 请从 GitHub 仓库中下载第四章,“数据摄取和管理数据集”的blobdatastore.yml文件,或者创建一个具有相同名称和以下内容的文件:

    $schema: https://azuremlschemas.azureedge.net/latest/azureBlob.schema.json
    name: mldemoblob
    type: azure_blob
    description: main ML blob storage
    account_name: mldemoblob8765
    container_name: mlfiles
    credentials:
      sas_token: <your_token>
    

请输入适合你情况的适当账户名称。现在唯一缺少的是 SAS 令牌,我们需要为我们的mlfiles容器创建它。

  1. 运行以下命令为我们的容器创建一个 SAS 令牌:

    az storage container generate-sas \
        --account-name mldemoblob8765 \
        --name mlfiles \
        --expiry 2023-01-01 \
        --permissions acdlrw
    

此命令生成一个 SAS 令牌,有效期为 2023 年 1 月 1 日,并具有对mlfiles容器的权限。选择一个足够远的未来日期,以便你可以使用这本书。在正常情况下,你会选择一个更短的过期日期,并相应地旋转此密钥。

结果应该是这种格式:

xx=XXXX-XX-XX&xx=xxxx&xxx=xxx&xx=xxxxxxxxxxx&xx=XXXX-XX-XXXXX:XX:XXX&xx=XXXX-XX-XXXXX:XX:XXX&xxx=xxxxx&xxx=XXxXXXxxxxxXXXXXXXxXxxxXXXXXxxXXXXXxXXXXxXXXxXXxXX

将此结果(不带引号)输入 YAML 文件中的sas_token字段。

  1. 导航到 YAML 文件所在的目录,这样我们就可以通过运行以下命令在 Azure Machine Learning 工作区中最终创建数据存储:

    az ml datastore create \
        --workspace-name mldemows \
        --resource-group mldemo \
        --file ./blobdatastore.yml
    

结果应该看起来像以下这样:

"account_name": "mldemoblob8765",
"container_name": "mlfiles",
"credentials": {},
"description": "main ML blob storage",
"endpoint": "core.windows.net",
"id": <yourid>,
"name": "mldemoblob",
"protocol": "https",
"resourceGroup": "mldemo",
"tags": {},
"type": "azure_blob"

通过这些步骤,我们已经使用 SAS 令牌注册了一个连接到我们的 blob 存储的数据存储。

重要提示

当连接到数据湖存储时,你可以遵循相同的步骤,但请注意,要访问数据湖,你需要创建一个服务主体。有关此内容的详细描述,请参阅此处:docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal

如前所述,我们可以在 Azure 门户中的向导中创建一个 blob 存储,为该容器创建一个 SAS 令牌,并在 Azure Machine Learning Studio 的数据存储创建向导中输入它。我们使用了 Azure CLI,这样你可以熟悉这个过程,因为这在将来自动化此类步骤时是必需的,尤其是在我们谈论基础设施即代码和 DevOps 环境时。

在任何情况下,请随意导航到 Azure Machine Learning Studio 中的数据存储选项卡。图 4.2显示了我们的新创建的工作区:

图 4.2 – 创建的数据存储

图 4.2 – 创建的数据存储

保持此标签页打开,以便我们可以在下一节中通过 mlfiles 容器进行验证。

将数据导入 Azure

我们创建了一个 Azure Blob 存储帐户,并学习了如何为常见的机器学习用例组织和格式化文件和表格数据。然而,一个常被忽视的步骤是如何将这些数据存储或 Azure 中的数据有效地导入。针对不同的数据集和用例有不同的解决方案,从临时、自动化、并行化解决方案等。在本节中,我们将探讨将数据手动或自动上传到关系数据库(SQL、MySQL 或 PostgreSQL)或 Azure 中的存储帐户的方法。最后,我们将上传一个数据集文件到之前创建的 blob 存储。

理解手动导入数据的相关工具

如果您处理少量数据集和文件,并且不需要从其他现有源传输数据,手动上传数据是首选选项。

以下列表显示了将数据带入您的数据存储或直接带入您的机器学习管道的可能选项:

在我们测试这些选项之前,让我们看看在 Azure 中创建自动化数据流和转换数据的选择。

理解自动化数据摄取和转换的工具

对于小型测试来说,手动复制数据是完全可行的,甚至可能是我们将在本书中执行的大多数任务,但在现实世界的场景中,我们不仅需要与许多不同的源进行集成,还需要一个不涉及人员手动将数据从 A 移动到 B 的过程。

因此,我们现在将查看允许我们以自动化方式转换和移动数据的服务,并且这些服务与 Azure 机器学习中的管道和 MLOps 集成得非常好。

Azure Data Factory

Azure Data Factory 是 Azure 中用于移动和转换数据的企业级解决方案。它提供了连接到数百个不同源的能力,并能够创建管道以转换集成数据,同时调用 Azure 中的多个其他服务。

运行以下命令以创建数据工厂:

az datafactory create \
    --location "West US 2" \
    --name "mldemoDF8765" \
    --resource-group "mldemo"

请注意,名称必须再次是全球唯一的。此外,在部署之前,CLI 将要求您安装datafactory扩展。

完成后,导航到 Azure 门户中的资源,在 概览 选项卡上点击 打开 Azure 数据工厂工作室,这将带您到您的数据工厂实例的工作台。您应该看到一个如 图 4.3 所示的视图:

图 4.3 – 数据工厂资源视图

图 4.3 – 数据工厂资源视图

从此视图,您可以创建管道、数据集、数据流和 Power Query。让我们简要讨论一下它们是什么:

  • 管道:管道是 Azure 数据工厂的主要明星。您可以通过创建复杂的管道,调用多个服务从源拉取数据,对其进行转换,并将其存储在目标中。

  • 数据集:数据集在管道中用作源或目标。因此,在构建管道之前,您可以定义一个连接到您最终想要从中读取或写入特定数据的数据存储的连接。

  • 数据流:数据流允许您在数据工厂内部本身进行实际的数据处理或转换,而不是调用不同的服务来完成繁重的工作。

  • Power Query:Power Query 允许您在数据工厂内部使用 DAX 进行数据探索,这在其他情况下通常只有使用 Power BI 或 Excel 才能实现。

如果您点击 Pipeline 旁边的三个点,可以创建一个新的,这将导致如 图 4.4 所示的以下视图:

图 4.4 – 创建数据工厂管道

图 4.4 – 创建数据工厂管道

查看可能的活动,您会发现一种从 A 到 B 复制数据(Copy Data)、在 Azure Functions 中执行脚本(Azure Function)、在 SQL 数据库中调用存储过程(Stored Procedure)、在 Databricks 中执行笔记本(Notebook)以及执行 ML 管道(Machine Learning Execute Pipeline)等方法。通过这些活动和您在 通用迭代与条件 中找到的控制工具,您可以构建非常复杂的数据管道来移动和转换您的数据。

如您可能已经注意到的,Azure Synapse 在活动列表中缺失。原因是 Synapse 在平台中集成了自己的数据工厂版本。因此,如果您在 Synapse 中使用 SQL 池或 Spark 池,您可以使用 Synapse 的集成工具,这将为您提供在 Synapse Spark 池中运行笔记本或在 SQL 池上调用存储过程的访问权限。

如果您正在寻找 Azure 数据工厂的深入概述,请查看 Catherine Wilhelmsen 的 Azure 数据工厂入门指南www.cathrinewilhelmsen.net/series/beginners-guide-azure-data-factory/

现在,我们需要理解的是,有两种方法可以将此数据工厂管道集成到 Azure Machine Learning 中:

  • 从存储帐户读取结果:我们可以在数据工厂中运行转换管道,转换我们的数据,然后将结果存储在存储帐户中。然后,我们可以通过我们学习的方式访问数据,即通过一个 ML 数据存储。在这种情况下,我们在 Azure 机器学习中的任何管道都与数据工厂中的转换管道断开连接,这可能不是 MLOps 的最佳方式。

  • 从数据工厂调用 Azure 机器学习:我们可以创建一个转换管道,并将实际的 Azure 机器学习管道作为数据工厂管道的一部分来调用。如果我们开始构建端到端的 MLOps 工作流程,这是首选方式。

关于这方面的更多信息,请阅读以下文章:docs.microsoft.com/en-us/azure/machine-learning/how-to-data-ingest-adf

Azure Synapse Spark 池

如我们在第二章中讨论的,在 Azure 中选择合适的机器学习服务,Azure Databricks 和 Azure Synapse 提供了在 Spark 池中运行 Spark 作业的选项。Apache Spark 可以通过利用节点池的分布式特性来帮助您转换和预处理极其庞大的数据集。因此,这个工具在开始实际的机器学习过程之前,可以帮助我们分解和过滤数据集。

我们已经看到,我们可以从 Azure Data Factory 或 Azure Synapse 的集成引擎中运行笔记本,因此已经可以访问这些服务。除此之外,我们还有选项将 Synapse Spark 池作为所谓的链接服务添加到 Azure 机器学习工作区中(请参阅 Azure 机器学习工作室中的链接服务选项卡)。执行此步骤后,我们不仅可以访问 ML 计算目标,还可以通过 Azure 机器学习 SDK 将 Spark 池作为计算目标。

您可以通过 Azure 机器学习工作室或 Azure 机器学习 Python SDK 创建此链接,这两者都在以下文章中进行了描述:docs.microsoft.com/en-us/azure/machine-learning/how-to-link-synapse-ml-workspaces

通过这种直接集成,我们可以在我们的 ML 管道中通过 Spark 集群运行转换步骤,因此又得到了构建干净端到端 MLOps 工作流程的另一个好选择。

将数据复制到 Blob 存储

现在,我们已经对大多数移动和转换数据的选项有了很好的理解,让我们将数据集上传到我们的存储帐户。

第五章中,执行数据分析与可视化,我们将开始分析和预处理数据。为此,让我们上传本章中将使用的数据集。

我们将使用由 Anthony Pino 创建的墨尔本住房数据集,您可以在以下链接找到:www.kaggle.com/anthonypino/melbourne-housing-market。选择这个数据集的原因是它覆盖的领域,因为每个人都理解住房,以及数据的合理清洁度。如果你继续通过处理数据来推进你的旅程,你会发现有很多数据集,但只有少数是干净且具有教育意义的。

此外,为了使我们在下一章分析数据集时生活更加便利,我们将实际上只使用这个数据集的一个子集。

按照以下步骤操作,以便我们可以将此文件放入我们的mldemoblob数据存储中:

  1. www.kaggle.com/dansbecker/melbourne-housing-snapshot下载melb_data.csv文件,并将其存储在你的设备上的一个合适的文件夹中。

  2. 导航到该文件夹,并在 CLI 中运行以下命令,将存储账户名称替换为您自己的名称:

    az storage blob upload \
        --account-name mldemoblob8765 \
        --file ./melb_data.csv \
        --container-name mlfiles \
    --name melb_data.csv 
    
  3. 为了验证这一点,让我们看看另一种移动此文件的方法。安装 Azure 存储资源管理器,并在该应用程序中登录到您的 Azure 账户。导航到您的存储账户并打开mlfiles容器。它应该显示如图 4.5 所示的视图:

图 4.5 – Azure 存储资源管理器

图 4.5 – Azure 存储资源管理器

如您所见,我们的文件就在它应该的位置。我们也可以直接将文件拖放到这里,自动创建一个 blob 文件。从现在开始,请随意使用您觉得更舒适的方法。

  1. 为了完成这个步骤,请查看应用程序本身。例如,如果你在容器上右键点击,你可以选择一个名为获取共享访问签名的选项,这将打开一个向导,允许你直接在这里创建 SAS 令牌,而不是像我们通过命令行那样做。

通过之前的步骤,我们已经将原始数据集文件放入我们的存储账户中,因此也放入了我们的机器学习数据存储中。在下一节中,我们将探讨如何从这些原始文件创建 Azure 机器学习数据集,以及它们提供了哪些功能来支持我们当前的机器学习之旅。

在 Azure 机器学习中使用数据集

在本章的前几节中,我们讨论了如何将数据放入云端,将数据存储在数据存储中,以及如何通过数据存储和数据集将数据连接到 Azure 机器学习工作区。我们集中管理数据和数据访问,以便在所有计算环境中使用数据,无论是用于实验、训练还是推理。在本节中,我们将重点介绍如何在训练期间创建、探索和访问这些数据集。

一旦数据被管理为数据集,我们就可以在 Azure Machine Learning 中跟踪每个实验或训练运行所使用的数据。这将使我们能够了解特定训练运行和训练模型所使用的数据,这是创建可重复端到端机器学习工作流程的关键步骤。

将您的数据组织到数据集中还有另一个好处,那就是您可以通过直接访问下载挂载轻松地将管理数据集传递到您的实验或训练脚本中。直接访问方法适用于公开可用的数据源,下载方法适用于小型数据集,挂载方法适用于大型数据集。在 Azure Machine Learning 训练集群中,这完全透明,数据将自动提供。然而,我们可以使用相同的技巧通过访问数据集对象来访问任何其他 Python 环境中的数据。

在本节的最后部分,我们将探讨 Azure Open Datasets——一组您可以直接从 Azure Machine Learning 工作区中消费的精选 Azure Machine Learning 数据集。

创建新的数据集

创建新的数据集有多种方法,但大多数方法会区分表格数据和文件数据集。您需要根据您想要创建的数据集类型使用不同的构造函数:

  • Dataset.Tabular.from_* 用于表格数据集

  • Dataset.File.from_* 用于基于文件的数据集(例如,图像、音频等)

对于表格数据集,我们也会区分从原始位置通过公开 URL 访问的数据——称为直接数据集——或存储在默认或自定义数据存储中。

Dataset对象可以通过其对象引用在当前环境中访问或传递。然而,数据集也可以注册(并版本化),因此可以通过数据集名称(和版本)访问——这被称为注册数据集

让我们看看一个简单的直接数据集示例,它被定义为表格数据集,并包含一个公开可用的 URL,该 URL 包含包含数据的分隔符文件:

from azureml.core import Dataset
path = 'https://...windows.net/demo/Titanic.csv'
ds = Dataset.Tabular.from_delimited_files(path)

如您在代码中所见,我们可以通过传递公开可访问的分隔符文件 URL 来创建一个直接数据集。当内部传递此数据集时,每个消费者都会尝试从其 URL 获取数据集。

图 4.6 – 直接数据集

图 4.6 – 直接数据集

一旦我们有一个数据存储的引用,我们就可以访问其中的数据。在以下示例中,我们从mldata数据存储的目录中创建一个文件数据集:

from azureml.core import Dataset, Datastore
datastore_name = "mldata"
datastore = Datastore.get(ws, datastore_name)
ds = Dataset.File.from_files((datastore, "cifar10/"))

如示例所示,我们可以将数据存储中的数据注册为数据集。在这个例子中,我们将文件夹中的所有文件定义为文件数据集,但我们也可以将 Blob 存储中的分隔符文件定义为表格数据集。

图 4.7 – 文件数据集

图 4.7 – 文件数据集

在下一步中,我们使用以下代码片段在工作空间中注册此数据集以创建一个 已注册的数据集

ds = ds.register(ws, name="titanic",
                 create_new_version=True)

之前的代码将直接数据集注册到你的工作空间,并返回一个已注册的数据集。已注册的数据集列在 Azure Machine Learning Studio 中,可以通过数据集名称而不是 Dataset Python 对象访问。

create_new_version 参数控制我们是否想要创建现有数据集的新版本。一旦创建了一个新的数据集版本,就可以通过数据集名称访问数据集 - 这将隐式访问最新版本 - 或者通过其名称和特定版本。数据集版本对于管理工作空间内数据集的不同迭代非常有用。

探索数据集中的数据

在 Azure Machine Learning 中探索已注册数据集有多种选择。对于表格数据集,最方便的方法是在 Azure Machine Learning 工作空间中以编程方式加载和分析数据集。为此,你可以简单地通过其名称和版本引用数据集,如下面的代码片段所示:

from azureml.core import Dataset
ds = Dataset.get_by_name(ws, name="titanic", version=1)

一旦你有了数据集的引用,你可以将数据集引用转换为实际的内存中 pandas DataFrame 或懒加载的 SparkDask DataFrame。为此,你可以调用以下方法之一:

  • to_pandas_dataframe() 用于创建内存中的 pandas DataFrame

  • to_spark_dataframe() 用于创建一个懒加载的 Spark DataFrame

  • to_dask_dataframe() 用于创建懒加载的 Dask DataFrame

让我们看看三个命令的实际操作,从内存中的 pandas DataFrame 开始。以下代码片段将所有数据加载到 pandas DataFrame 中,然后返回 DataFrame 的前五行:

panads_df = ds.to_pandas_dataframe()
pandas_df.head()

在加载 DataFrame 之后,你可以运行你喜欢的 pandas 方法来探索数据集。例如,开始时使用 info() 命令查看列和数据类型,以及使用 describe() 命令查看 DataFrame 中数值的统计信息。

懒加载数据集是仅在需要时才将部分数据加载到内存中的数据集,例如,当需要计算结果时。非懒加载数据集将所有数据加载到内存中,因此受可用内存的限制。

如果你更熟悉 PySpark,你也可以使用以下代码片段将数据集转换为 Spark DataFrame。与上一个示例相比,此代码实际上不会将所有数据加载到内存中,而只会获取执行 show() 命令所需的数据 - 这使其成为分析大型数据集的绝佳选择:

spark_df = ds.to_spark_dataframe()
spark_df.show()

另一种选择是返回数据集的 Dask DataFrame。Dask 是一个支持懒加载数据集的 Python 库,具有类似 pandas 和 NumPy 的 API。因此,你可以运行以下代码以懒加载方式返回 DataFrame 的前五行:

dask_df = ds.to_dask_dataframe()
dask_df.head()

一旦你获得了访问你喜欢的数值或统计库中数据的编程权限,你就可以根据需要对你的数据集进行切片和切块。虽然编程访问对于可重复性和定制化来说很棒,但用户通常只想了解数据是如何结构的,并查看一些示例记录。Azure Machine Learning 也提供了在 Data Studio UI 中探索数据集的可能性。

要进入此视图,请转到 数据集,选择一个数据集,然后点击 探索 选项卡。第一页显示了你的数据预览,包括前 n 行以及一些关于数据的基本信息——例如行数和列数。以下截图是一个示例:

图 4.8 – 带有数据预览的数据集

图 4.8 – 带有数据预览的数据集

如果你点击第二个选项卡,你可以生成并查看数据概要。此概要类似于在 pandas DataFrame 上调用 describe()——对数据集中每一列的统计分析,但支持分类数据和一些更有用的信息。如图 4.9 所示,它还显示了每个列的数据分布图:

图 4.9 – 带有数据概要的数据集

图 4.9 – 带有数据概要的数据集

如前图所示,这是数据集的一个非常有用的总结。从这个视图中获得的见解对于所有使用此数据集的人来说都很重要。

在本节中,我们看到了多种访问和分析 Azure Machine Learning 数据集中存储的数据的方法——通过 Python 和你喜欢的数值库进行编程访问,或者通过 UI。

Azure Machine Learning 中的数据集跟踪

对最终生产模型中所有资产的端到端跟踪对于可重复性、可解释性、审计和跟踪都是必不可少的。机器学习模型是一个通过迭代和从你的训练数据中采样实验来最小化损失函数的函数。因此,训练数据本身应被视为模型的一部分,因此应通过端到端的机器学习过程进行管理、版本控制和跟踪。

我们想利用数据集来为我们的实验添加数据跟踪。了解数据跟踪能力差异的一个好方法是通过两个示例:首先,从 URL 加载 CSV 数据集,然后通过 Azure Machine Learning 中的数据集抽象从同一 URL 加载数据。然而,我们不仅想加载数据,还想将其从编写脚本传递到训练脚本作为参数。

我们将首先使用 pandas 直接从 URL 加载 CSV 文件,并将其作为 URL 传递给训练脚本。在下一步中,我们将通过使用直接的数据集来增强此方法,这样我们就可以方便地将数据集配置传递给训练脚本,并跟踪 Azure Machine Learning 中实验运行的数据集。

将外部数据作为 URL 传递

我们以从远程 URL 可用的 CSV 文件数据开始我们的示例,这是分发公共数据集的常见方式。在第一个没有 Azure Machine Learning 数据集跟踪的示例中,我们将使用 pandas 库来获取和解析 CSV 文件:

  1. 让我们从使用 pandas 的 read_csv() 方法作为示例的第一个代码片段开始,通过公共 URL 从远程服务器获取数据。然而,这只是一个示例——你可以用任何其他方法从远程位置获取数据:

    import pandas as pd
    path ='https://...windows.net/demo/Titanic.csv'
    df = pd.read_csv(path)
    print(df.head())
    

我们的目的是将数据从编写脚本传递到训练脚本,以便将来可以轻松跟踪和更新。为了实现这一点,我们不能直接传递 DataFrame,而必须传递 CSV 文件的 URL 并在训练脚本中使用相同的方法获取数据。让我们编写一个小型训练脚本,其唯一任务是解析命令行参数并从 URL 获取数据:

code/access_data_from_path.py

import argparse
import pandas as pd
parser = argparse.ArgumentParser()
parser.add_argument("--input", type=str)
args = parser.parse_args()
df = pd.read_csv(args.input)
print(df.head())

如前述代码所示,我们通过命令行的 --input 参数传递数据路径,然后使用 pandas 的 read_csv() 从位置加载数据。

  1. 接下来,我们创建一个 ScriptRunConfig 构造函数,将实验运行提交给 Azure Machine Learning,以执行从 步骤 1 开始的训练脚本。现在,我们不做任何训练,只想了解编写和执行运行时之间传递的数据:

Access_data_from_path.ipynb

src = ScriptRunConfig(
  source_directory="code",
  script='access_data_from_path.py',
  arguments=['--input', path],
  environment=get_current_env())
  1. 让我们执行运行配置以运行实验并跟踪 Azure Machine Learning 中的运行详情。一旦实验运行完成,我们导航到 Azure Machine Learning 并检查这次运行的详细信息。正如我们在 图 4.10 中可以看到的,Azure Machine Learning 将按预期跟踪 script 参数,但不能将参数关联到数据集:

图 4.10 – 实验运行详情

图 4.10 – 实验运行详情

让我们总结一下这种方法的缺点:

  • 我们不能将 pandas DataFrame 或 DataFrame 标识符传递给训练脚本;我们必须通过 URL 将数据传递到其位置。如果文件路径发生变化,我们必须更新训练脚本的参数。

  • 训练脚本不知道输入路径指的是训练脚本的输入数据,它只是训练脚本的一个字符串参数。虽然我们可以在 Azure Machine Learning 中跟踪参数,但我们不能自动跟踪数据。

将外部数据作为直接数据集传递

如承诺,我们现在将使用 Azure Machine Learning 中的数据集增强先前的示例。这将允许我们将数据集作为命名配置传递——抽象 URL 和数据的物理位置访问。它还自动为实验启用数据集跟踪:

  1. 我们从编写脚本开始,从路径加载数据 - 只不过这次,我们使用 Azure Machine Learning 的 TabularDataset,通过 from_delimited_files() 工厂方法创建:

    from azureml.core import Dataset
    path ='https://...windows.net/demo/Titanic.csv'
    ds = Dataset.Tabular.from_delimited_files(path)
    print(ds.to_pandas_dataframe().head())
    

这将在 pandas 中输出与上一个示例相同的行集 - 所以除了使用不同的方法创建 DataFrame 之外,几乎没有区别。然而,现在我们已经创建了一个 直接数据集,我们可以轻松地将数据集传递给训练脚本作为命名数据集配置 - 这将在底层使用数据集 ID。

  1. 与 pandas 示例类似,我们编写了一个简化的训练脚本,该脚本将访问数据集并通过解析命令行参数中的输入数据集来打印前几条记录。在训练脚本中,我们可以使用 Dataset.get_by_id() 方法通过其 ID 从工作区获取数据集:

code/access_data_from_dataset.py

import argparse
from azureml.core import Dataset, Run
parser = argparse.ArgumentParser()
parser.add_argument("--input", type=str)
args = parser.parse_args()
run = Run.get_context()
ws = run.experiment.workspace
ds = Dataset.get_by_id(ws, id=args.input)
print(ds.to_pandas_dataframe().head())

如前述代码所示,我们稍微修改了之前的代码,并添加了代码来检索当前的运行上下文、实验和工作区。这使得我们可以通过将数据集 ID 传递给 Dataset.get_by_id() 方法从工作区访问直接数据集。

  1. 接下来,我们编写一个运行配置,将前面的代码作为实验提交给 Azure Machine Learning。首先,我们需要将数据集转换为命令行参数,并将其传递给训练脚本,以便在执行运行时自动检索。我们可以通过在数据集实例上使用 as_named_input(name) 方法来实现这一点,这将数据集转换为命名的 DatasetConsumptionConfig 参数,允许数据集传递到其他环境。

在这种情况下,数据集将以直接模式传递,并在运行时环境中作为 name 环境变量提供,或在命令行参数中作为数据集 ID。数据集也将作为训练脚本的输入参数在 Azure Machine Learning 中进行跟踪。

然而,如前述代码片段所示,我们在训练脚本中使用 Dataset.get_by_id() 方法从数据集 ID 获取数据集。我们不需要手动创建或访问数据集 ID,因为当 Azure Machine Learning 使用直接数据集调用训练脚本时,DatasetConsumptionConfig 参数将自动展开为数据集 ID。

Access_data_from_dataset.ipynb

src = ScriptRunConfig(
  source_directory="code",
  script='access_data_from_dataset.py',
  arguments=['--input', ds.as_named_input('titanic')],
  environment=get_current_env())

如前述代码所示,数据集被转换为可以通过 as_named_input(name) 方法简单传递给训练脚本的配置。如果我们提交实验并检查运行日志,我们可以看到 Azure Machine Learning 将数据集 ID 传递给了训练脚本:

70_driver_log.txt

...
After variable expansion, calling script [access_data_from_dataset.py] with arguments:['--input', '04f8ad60-5a51-4319-92fe-cdfa7f6c9adc']

该实验的运行详情如图 图 4.11 所示。如果您查看输入参数,您可以看到我们传递了 DatasetConsumptionConfig 对象给脚本,该对象随后自动转换为数据集 ID。不仅输入参数传递时没有关于底层数据位置的任何信息,输入数据集也被识别为训练数据的输入:

图 4.11 – 实验运行详情

图 4.11 – 实验运行详情

通过将数据集传递给训练脚本,Azure Machine Learning 会自动跟踪实验运行中的数据集。如图 图 4.11 所示,数据集 ID 是追踪数据集的链接。当在 Azure Machine Learning 中点击数据集 ID 时,它将打开一个页面,显示追踪数据集的详细信息,例如描述、URL、大小和数据集类型,如图 图 4.12 所示。与已注册的数据集一样,您也可以探索原始数据,查看数据集列统计信息——称为概要——或查看从这些数据派生的任何已注册模型。通过点击 注册 操作或从代码中,可以轻松地将追踪数据集注册——从而进行版本控制和管理工作:

图 4.12 – Azure Machine Learning 中追踪的直接数据集

图 4.12 – Azure Machine Learning 中追踪的直接数据集

正如本节所示,将输入数据作为数据集参数传递给训练脚本有许多重要好处。这将自动跟踪您工作区中的数据集,并将数据集与实验运行连接起来。

在本节的代码片段中,我们以 直接数据集 的形式传递了数据,这意味着训练脚本必须再次从外部 URL 获取数据。这并不总是最优的,尤其是在处理大量数据或数据应在 Azure Machine Learning 中管理时。在下一节中,我们将探讨将数据传递给训练脚本的不同方法。

训练过程中的数据访问

在前一节中,我们隐式地将原始数据集的 URL 传递给训练脚本。虽然这对于小型公共数据集来说是一个实用且快速的方法,但对于私有或更大的数据集来说,通常不是首选的方法。想象一下,您的数据存储在 SQL 服务器、Blob 存储或文件共享上,并且受密码保护。想象一下,您的数据集包含许多千兆字节的文件。在本节中,我们将看到适用于这两种情况的技术。

当通过 URL 可达的外部公共数据以 直接数据集 的形式创建并传递时,所有其他数据集都可以通过 下载挂载 的方式访问。对于大数据数据集,Azure Machine Learning 还提供了一个将数据集挂载为 Hadoop 分布式文件系统HDFS)的选项。

在本节中,我们将看到创作脚本,这些脚本将以下载和挂载的方式传递数据集。让我们首先在创作脚本中创建对 cifar10 数据集的引用,该数据集我们在上一节中注册过。以下代码片段从 Azure Machine Learning 工作区通过名称检索数据集:

from azureml.core import Dataset
dataset = Dataset.get_by_name(ws, "cifar10")

接下来,我们希望将数据集传递给训练脚本,以便我们可以从脚本中访问训练数据。使用数据集的好处不仅在于跟踪,还在于我们可以简单地选择适合每个数据集的适当数据消费配置。它还将帮助我们分离训练脚本和训练数据,使得将新数据、更新数据或增强数据传递给相同的训练脚本变得容易,而无需更新训练脚本。

不论是哪种消费方式,训练脚本都可以始终从目录路径加载数据,该路径是数据下载或挂载的位置。在底层,Azure Machine Learning 会检查 ScriptRunConfig 的命令行参数,检测数据集引用,将数据传递到计算环境,并用本地文件系统中数据集的路径替换参数。

Azure Machine Learning 使用参数扩展将数据集引用替换为磁盘上实际数据的路径。为了使这一点更加明显,我们将编写一个单独的训练文件,该文件将简单地列出传递给它的所有训练文件。以下代码片段实现了这个训练脚本:

code/access_dataset.py

import os
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--input", type=str)
args = parser.parse_args()
print("Dataset path: {}".format(args.input))
print(os.listdir(args.input))

在前面的脚本中,我们定义了一个单一的 --input 参数,我们将使用它来传递训练数据。然后我们将输出此参数并列出目录中的所有文件。我们将使用此脚本通过不同的挂载技术传递数据,并将看到数据始终可用在文件夹中。

在拥有数据集引用和简单的训练脚本之后,我们现在可以查看不同的 ScriptRunConfig,使用不同的数据消费配置传递 cifar10 数据集。虽然代码在调用训练脚本之前由 Azure Machine Learning 下载或挂载,但我们将探索底层发生了什么——这样我们就可以将相同的技巧应用于在 Azure Machine Learning 管理的计算环境之外加载训练数据。

以下载方式访问数据

我们首先将查看将数据下载到训练实例的过程。为此,我们将在创作环境中创建一个 ScriptRunConfig 构造函数,并将数据传递给 as_download()。我们将安排一个代码片段,该片段将访问并输出传递给脚本的文件:

Access_dataset_as_download.ipynb

from azureml.core import ScriptRunConfig
src = ScriptRunConfig(
  source_directory="code",
  script='access_dataset.py',
  arguments=['--input',
    dataset.as_named_input('cifar10').as_download()],
  environment=get_current_env())

Azure 会将 input 参数传递的数据集进行插值,并用磁盘上数据集的位置替换它。如果数据集是通过 Dataset.as_download() 方法传递的,数据将自动下载到训练环境中。

如果你运行此脚本配置,access_dataset.py脚本将输出数据集的临时位置,该数据集已自动下载到磁盘。你可以在你的创作环境中复制 Azure Machine Learning 在内部所执行的确切过程。为此,你可以简单地调用以下代码:

folder = '/tmp/cifar10-data'
paths = dataset.download(folder)

以下载方式传递数据对于小型数据集或使用大量消费者且对数据有高吞吐量需求的情况来说很方便。然而,如果你处理的是大型数据集,你也可以将它们作为挂载传递。

以挂载方式访问数据

在本例中,我们将数据挂载到训练环境中。为此,我们将在创作环境中再次创建一个ScriptRunConfig构造函数,这次我们将调用as_mount()。我们将安排一个代码片段,该片段将访问并输出传递给脚本的文件:

Access_dataset_as_mount.ipynb

from azureml.core import ScriptRunConfig
src = ScriptRunConfig(
  source_directory="code",
  script='access_dataset.py',
  arguments=['--input',
    dataset.as_named_input('cifar10').as_mount()],
  environment=get_current_env())

如你所见,前面的示例与之前将数据下载到磁盘的示例非常相似。事实上,我们正在重用完全相同的计划脚本access_dataset.py,该脚本将输出数据在磁盘上的位置。然而,在这个示例中,数据并没有下载到这个位置,而是挂载到文件路径。

Azure Machine Learning 会将通过输入参数传递的数据集与磁盘上的挂载路径进行插值。类似于上一个示例,你可以在 Azure Machine Learning 内部复制所发生的事情,并在你的创作环境中挂载数据:

import os
folder = '/tmp/cifar10-data'
# Or you can also use the start and stop methods
mount_context = dataset.mount(folder)
try:
  mount_context.start() 
  print(os.listdir(folder))
finally:
  mount_context.stop()

如你从前面的代码片段中看到的,数据集是通过挂载上下文的startstop方法挂载和释放的。你还可以使用 Python 的with语句简化代码片段,自动挂载和卸载数据,如下面的代码片段所示:

with dataset.mount() as mount_context:
  print(os.listdir(mount_context.mount_point))

因此,根据用例的不同,我们有不同的选项将数据集引用传递给计划中的脚本。独立于数据传输,Azure Machine Learning 将在内部实现正确的方法,并插值输入参数,这样训练脚本就不需要知道数据集是如何配置的。对于执行的脚本,数据通过文件系统中的路径简单地提供。

使用外部数据集与公开数据集

提高任何机器学习模型预测性能的最有效方法之一是向你的训练数据中添加额外的信息。实现这一目标的一种常见方式是将外部数据集与训练数据合并。一个很好的迹象是,在你的数据集中存在流行的合并键,例如日期、位置、国家等。

当您处理包含日期的交易数据时,您可以轻松地将外部数据连接起来,为训练数据集创建额外的特征,从而提高预测性能。常见的日期衍生特征包括工作日、周末、到或自周末的时间、假日、到或自假日的时间、体育赛事、音乐会等。当处理国家信息时,您通常可以连接额外的特定国家数据,例如人口数据、经济数据、社会学数据、健康数据、劳动数据等。当处理地理位置时,您可以连接到兴趣点的距离、天气数据、交通数据等。每个额外的数据集都为您提供了额外的见解,因此可以显著提升模型性能。

开放数据集是一个提供访问精选数据集的服务,这些数据集可用于交通、健康和基因组学、劳动和经济、人口以及安全等类别和常用数据集,您可以使用这些数据集来提升模型性能。让我们看看三个例子。

重要提示

在使用特定数据集为商业服务之前,请确保您的应用程序受许可证覆盖。如有疑问,请联系微软的 aod@microsoft.com

在第一个例子中,我们将研究全球公共假日的数据集。数据涵盖了 1970 年至 2099 年近 40 个国家和地区或地区的假日。这些数据来自维基百科和holidays Python 包。您可以将它们导入到您的环境中,并使用以下示例中的opendatasets库访问这些假日:

from azureml.opendatasets import PublicHolidays
from dateutil import parser
end_date = parser.parse("Jan 10 2000 12:00AM")
start_date = parser.parse("Jan 10 2010 12:00AM")
ds = PublicHolidays(start_date=start_date, 
                    end_date=end_date)
df = ds.to_pandas_dataframe()

正如我们在代码中看到的,我们可以从azureml-opendatasets包中访问数据集,并将其用作 Azure Machine Learning 数据集。这意味着我们可以返回 pandas 或 Spark DataFrame 以进行进一步处理。

另一个流行的数据集是按县划分的2000 年和 2010 年美国人口。它按性别和种族细分,并来源于美国人口普查局:

from azureml.opendatasets import UsPopulationZip
population = UsPopulationZip()
population_df = population.to_pandas_dataframe()

另一个公开数据集的例子是美国劳工统计局BLS)发布的当前就业统计。它包含了美国工资单上工人的就业、小时数和收入估计:

from azureml.opendatasets import UsLaborEHENational
ds = UsLaborEHENational()
df = ds.to_pandas_dataframe()

正如您在本节中看到的,Azure Open Datasets 为您提供了一个方便的选项,可以直接在您的 Azure Machine Learning 工作区中访问以 Azure Machine Learning 数据集形式提供的精选数据集。虽然可用的数据集数量仍然可控,但您可以预期可用的数据集数量会随着时间的推移而增长。

摘要

在本章中,我们学习了如何使用数据存储和数据集在 Azure Machine Learning 中管理数据。我们看到了如何配置负责在 Azure Machine Learning 中存储所有资产、日志、模型等默认数据存储,以及可用于不同类型数据的其他服务。

在创建 Azure Blob 存储账户并将其配置为 Azure Machine Learning 中的数据存储后,我们看到了将数据导入 Azure 的不同工具,例如 Azure Storage Explorer、Azure CLI 和 AzCopy,以及针对数据导入和转换优化的服务,如 Azure Data Factory 和 Azure Synapse Spark。

在随后的部分,我们实际操作了数据集。我们创建了文件和表格数据集,并了解了直接和注册数据集。数据集可以作为下载或挂载传递给执行脚本,这将自动跟踪 Azure Machine Learning 中的数据集。

最后,我们学习了如何通过将 Azure Open Datasets 的第三方数据集加入我们的机器学习流程中来提高预测性能。在下一章中,我们将学习如何通过执行数据分析与可视化来探索数据。

第五章:第五章:执行数据分析和可视化

在上一章中,我们学习了如何将我们的数据集带到云端,在 Azure 机器学习工作区中定义数据存储以访问它们,并在 Azure 机器学习数据集注册表中注册数据集,从而为从数据预处理开始打下良好的基础。在本章中,我们将学习如何探索这些原始数据。

首先,您将了解可以帮助您探索表格和文件数据集的技术。我们还将讨论如何处理缺失值,如何交叉关联特征以了解它们之间的统计关系,以及如何将领域知识应用到这个过程中,以改善我们对上下文和数据清洗质量的了解。此外,我们将学习如何使用机器学习算法,不是为了训练,而是为了探索我们的数据集。

之后,我们将将这些方法应用于实际数据集,同时学习如何使用 pandas DataFrame 以及如何可视化数据集的特性。

最后,我们将探讨可以将高维数据映射到低维平面的方法,这将帮助我们看到数据点之间的相似性和关系。此外,这些方法还可以给我们提供清晰的线索,了解我们的数据有多干净,以及所选机器学习算法在数据集上的效果如何。

在本章中,我们将涵盖以下主题:

  • 理解数据探索技术

  • 在表格数据集上执行数据分析

  • 理解降维技术

技术要求

在本章中,我们将使用以下 Python 库和版本来执行数据预处理和高维可视化:

  • azureml-sdk 1.34.0

  • azureml-widgets 1.34.0

  • azureml-dataprep 2.20.0

  • pandas 1.3.2

  • numpy 1.19.5

  • scikit-learn 0.24.2

  • seaborn 0.11.2

  • plotly 5.3.1

  • umap_learn 0.5.1

  • statsmodels 0.13.0

  • missingno 0.5.0

与前几章类似,您可以使用本地 Python 解释器或 Azure 机器学习中的笔记本环境执行此代码。

本章中的所有代码示例都可以在本书的 GitHub 仓库中找到:github.com/PacktPublishing/Mastering-Azure-Machine-Learning-Second-Edition/tree/main/chapter05

理解数据探索技术

描述性数据探索无疑是机器学习项目中最重要的步骤之一。如果您想清理数据、构建派生特征或选择机器学习算法来预测数据集中的目标变量,那么您首先需要了解您的数据。您的数据将定义许多必要的清理和预处理步骤;它将定义您可以选择哪些算法,并最终定义您预测模型的性能。

因此,数据探索应该被视为理解数据是否足够信息以首先构建机器学习模型的重要分析步骤。通过分析步骤,我们是指探索应该作为一个结构化的分析过程来完成,而不是一系列实验任务。因此,我们将通过一系列数据探索任务清单,这些任务可以作为每个机器学习项目的初始步骤来执行——在你开始任何数据清洗、预处理、特征工程或模型选择之前。

我们可以执行的可能任务与我们正在处理的数据集类型相关。许多数据集将以表格数据的形式出现,这意味着我们为数据集的每个实例定义了连续或分类特征。这些数据集可以表示为表格,我们可以在它们上执行基本的和复杂的数学运算。我们可能遇到的另一种一般类型的数据集将以媒体文件的形式出现。这包括图像、视频、声音文件、文档以及任何其他不能放入表格结构中的数据点。

为了表示这些不同类型的数据集,Azure 机器学习提供了将我们的数据保存为以下对象之一的选项:

这两种类型的数据集对象都可以注册到 Azure 机器学习数据集注册表中,以便在预处理后进一步使用。

仅从那些两个类别中可用的方法来判断,很明显,我们可以在表格数据集和文件数据集之间执行的可能任务和操作有很大差异。在接下来的几节中,我们将探讨这两种类型以及我们如何准备它们以影响我们机器学习模型的结果。

探索和分析表格数据集

表格数据集允许我们利用数学和统计函数的全谱来分析和转换我们的数据集,但在大多数情况下,我们没有时间或资源来随机将每个数据集通过我们工具箱中所有可能的技术。

选择正确的方法不仅需要分析大量不同数据集的经验,还需要我们所在领域的专业知识。有些领域每个人都有一些一般的专长(例如,房价的影响因素),但还有很多领域需要专业知识来理解手头的数据。想象一下,你想要提高炼钢高炉的产量。在这种情况下,为了理解数据,你需要对高炉中的化学过程有深入了解,或者你需要一个领域专家来支持你。在探索和分析的每一步中,我们需要应用领域知识来解释我们看到的结果和关系。

除了理解领域之外,我们还需要理解数据集中的特征及其目标或标签。想象一下,有一个由某个城市房屋特征组成的数据集,但没有它们的市场价格。为了预测房价,我们需要每个房屋的价格标签或目标值。另一方面,如果我们想要预测一封电子邮件是否为垃圾邮件,而我们有一个包含大量元数据的电子邮件数据集,这可能足以通过无监督学习训练模型。

因此,为了对数据集有一个良好的理解,我们需要彻底探索其内容,尽可能多地从特征和可能的靶标中获取洞察力,以便做出明智的决策。

重要提示

请记住,不仅特征维度需要预处理和分析,目标变量也需要。

为了实现这一点,我们将首先查看数据集中每个特征和目标向量的以下方面:

  • datetimestringintobject?我们需要进行数据类型转换吗?

  • 缺失数据:是否存在任何缺失条目?我们如何处理它们?

  • 不一致的数据:日期和时间是以不同的方式存储的吗?相同的类别是否以不同的方式书写?在给定上下文中,是否存在具有相同意义的不同类别?

  • 唯一值:对于一个分类特征,存在多少唯一值?是否太多?我们应该创建它们的子集吗?

  • 统计特性:一个特征的均值、中位数和方差是什么?是否存在任何异常值?最小值和最大值是什么?最常见的值(众数)是什么?

  • 统计分布:值是如何分布的?是否存在数据偏斜?标准化或缩放是否有用?

  • 相关性:不同的特征之间是如何相互关联的?是否存在包含相似信息可以被省略的特征?我的特征与目标的相关性有多大?

分析具有超过 100 个特征维度的数据集的每个维度是一项极其耗时的工作。然而,您可以通过按特征重要性排序的维度来分析,而不是随机探索特征维度,从而显著减少您处理数据的时间。像计算机科学的许多其他领域一样,在初始数据探索中使用 80/20 原则是好的,这意味着只使用 20%的特征来实现 80%的性能。这将为您提供一个良好的起点,您可以在需要时随时返回添加更多维度。

因此,了解特征对建模的重要性是明智的。我们可以通过查看特征与目标变量之间的关系来实现这一点。有许多方法可以做到这一点,以下是一些方法:

  • 回归系数:用于回归

  • 特征重要性:用于分类

  • 分类值的错误率较高:用于二元分类

通过应用这些步骤,您可以了解数据,并获取有关数据、特征和目标变量的预处理任务的知识。此外,它将为您提供对预测任务中可能遇到的困难的良好估计,这对于判断所需的算法和验证策略至关重要。您还将深入了解可能应用于数据集的特征工程方法,并更好地理解如何选择一个好的误差度量。

重要提示

您可以使用数据的代表性子集,并将您的假设和见解外推到整个数据集。

一旦数据已上传到 Azure 的存储服务中,我们就可以启动笔记本环境并开始探索数据。目标是通过对数据的深入分析过程,了解数据每个维度的分布。我们将在“在表格数据集上执行数据分析”部分执行这些步骤中的一些。

但首先,我们将回顾我们讨论过的某些技术,并快速查看文件数据集。

处理缺失值和异常值

在新的数据集中,首先要寻找的是每个特征和目标维度的缺失值。这将帮助您更深入地了解数据集以及可以采取哪些措施来解决这些问题。在项目开始时删除缺失值或用零填充它们并不罕见——然而,这种方法存在风险,即最初未能正确分析缺失值,并丢失大量数据点。

重要提示

缺失值可能被伪装成有效的数值或分类值。典型的例子是最小值或最大值,-1,0,或 NaN。因此,如果你在一个整数数据列中多次发现 32,767(= 215-1)或 65,535(= 216-1)这样的值,它们可能正是伪装成最大有符号或无符号 16 位整数表示的缺失值。始终假设你的数据包含不同形状和表示的缺失值和异常值。你的任务是揭示、找到并清理它们。

关于数据或领域的任何先验知识都会在你处理数据时给你带来竞争优势。这是因为你将能够理解数据和相关领域的缺失值异常值极端值,这将帮助你更好地进行插补、清理或转换。作为下一步,你应该在你的数据中寻找这些异常值,特别是以下方面的绝对数量或百分比:

  • 空值(查找Null"Null"""NaN等)

  • 最小值和最大值

  • 最常见的值(MODE

  • 0

  • 任何唯一值

一旦你确定了这些值,你可以使用不同的预处理技术来填补缺失值和归一化或排除维度。

处理缺失值的典型选项如下:

  • 删除:从数据集中删除整个行或列。这可能导致偏差或训练数据不足。

  • 对于分类特征,查找Missing

  • 列平均值:根据与其他特征的关系,填充整个数据列或列子集的均值、中位数或众数。

  • 插值:根据列的数据填充一个插值值。

  • 热补丁插补:从数据列的排序记录中填充逻辑前一个值(在时间序列数据集中很有用)。

处理异常值的典型选项如下:

  • 错误观测值:如果值是错误的,可以删除整个列,或者用列的均值替换异常值。

  • 保留原样:如果它包含重要信息,并且模型不会因此受到扭曲。

  • 上限或下限:将值限制在均值最大偏差内(例如,三个标准差)。

为了在选择处理缺失值和异常值的方法时获得更多上下文,统计分析列分布和相关性是有用的。我们将在以下章节中这样做。

计算统计属性和可视化数据分布

现在你已经知道了异常值,你可以开始探索你的数据集特征的值分布。这将帮助你了解在数据准备过程中应该应用哪些转换和归一化技术。在连续变量中寻找的一些常见分布统计量如下:

  • 均值或中位数

  • 最小值和最大值

  • 方差和标准差

  • 第 25、50(中位数)和第 75 百分位数

  • 数据倾斜

可视化这些分布的常用技术包括使用箱线图密度图直方图。以下截图显示了针对多类识别数据集的每个目标类别的不同可视化技术。每种方法都有其优缺点——箱线图显示了所有相关指标,但阅读起来稍微有点困难,密度图显示了非常平滑的形状,但隐藏了一些异常值,而直方图则不让你容易地找到中位数和百分位数,同时给你一个数据倾斜的良好估计:

图 5.1 – 箱线图(左),密度图(中),直方图(右)

图 5.1 – 箱线图(左),密度图(中),直方图(右)

在这里,我们可以看到,对于分类数据(无论是名义的还是序数的),只有直方图工作得很好。然而,你可以查看每个类别的值数量。你可以在本书的 GitHub 仓库中的 01_data_distribution.ipynb 文件中找到创建这些图表的代码。

在二元分类任务中,另一种显示值分布与目标率关系的好方法是。以下图表显示了来自 Microsoft 恶意软件检测数据集www.kaggle.com/c/microsoft-malware-prediction/data)的 Windows Defender 的 版本号 与恶意软件 检测率(针对非触摸设备):

图 5.2 – Windows Defender 版本号与检测率的关系

图 5.2 – Windows Defender 版本号与检测率的关系

许多统计机器学习算法需要数据呈正态分布,因此需要归一化或标准化。了解数据分布有助于你决定在数据准备期间需要应用哪些转换。在实践中,数据通常需要转换、缩放或归一化。

寻找相关维度

数据探索中的另一个常见任务是寻找数据集中的相关性。这将帮助你排除高度相关的特征维度,从而可能影响你的机器学习模型。例如,在线性回归模型中,两个高度相关的独立变量将导致具有相反符号的大系数,最终相互抵消。通过移除一个相关维度,可以找到一个更稳定的回归模型。因此,不仅需要查看特征和目标之间的相关性,还需要查看特征之间的相关性。

-1(强负相关)到1(强正相关)。0表示两个变量之间没有线性关系。

以下图表显示了加利福尼亚住房数据集www.dcc.fc.up.pt/~ltorgo/Regression/cal_housing.html)的相关矩阵示例,该数据集仅包含连续变量。相关系数的范围从-11,并相应着色,其中红色表示负相关,蓝色表示正相关。最后一行显示了每个特征维度与目标变量(MedHouseVal)之间的线性相关性。我们可以立即看出,LongitudeLatitude之间存在相关性,MedHouseValMedInc之间存在相关性,以及AveRoomsAveBedrms之间存在相关性。所有这些关系都是相对不出意外的:

图 5.3 – 加利福尼亚住房数据集的相关矩阵

图 5.3 – 加利福尼亚住房数据集的相关矩阵

你可以在本书 GitHub 仓库中的02_correlation.ipynb文件中找到创建此相关矩阵的代码。

值得注意的是,许多相关系数只能存在于数值之间。有序变量可以通过整数编码进行编码,也可以计算出一个有意义的相关系数。对于名义数据,你需要回退到不同的方法,例如Cramér's V来计算相关性。值得注意的是,在计算相关系数之前,输入数据不需要进行归一化(线性缩放)。

测量回归中的特征和目标变量依赖性

一旦我们分析了缺失值、数据分布和相关性,我们就可以开始分析特征与目标变量之间的关系。这将为我们提供预测问题难度的良好指示,从而确定预期的基线性能,这对于优先考虑特征工程努力和选择合适的机器学习模型至关重要。测量这种依赖性的另一个巨大好处是按特征维度对目标变量的影响进行排名,你可以将其用作数据探索和预处理优先级列表。

在回归任务中,目标变量是数值或有序的。因此,我们可以计算单个特征与目标变量之间的相关系数,以计算特征与目标变量之间的线性依赖性。高相关性,即高绝对相关系数,表明存在强烈的线性关系。这为我们进一步探索提供了一个很好的起点。然而,在许多实际问题中,很少看到特征与目标变量之间存在高(线性)相关性。

您还可以使用散点图回归图来可视化特征和目标变量之间的依赖关系。以下图表显示了来自波士顿住房数据集的平均每户住宅房间数(RM)与业主自住房屋的中位数价值(MEDV)之间的回归图。如果回归线在 45 度角,则表示存在完美的线性相关性:

图 5.4 – 特征与目标之间的散点图,带有回归线

图 5.4 – 特征与目标之间的散点图,带有回归线

确定这种依赖关系的另一种有效方法是,将线性或逻辑回归模型拟合到训练数据中。得到的模型系数应该能为你提供对关系的良好解释——系数越高,对目标变量的线性(对于线性回归)或边际(对于逻辑回归)依赖性就越大。因此,按系数排序将得到一个按重要性排序的特征列表。根据回归类型,输入数据应进行归一化或标准化。

以下截图显示了拟合的普通最小二乘法OLS)回归模型的关联系数(第一列):

图 5.5 – OLS 回归模型的关联系数

图 5.5 – OLS 回归模型的关联系数

您可以在本书 GitHub 仓库中的03_regression.ipynb文件中找到创建图表和系数的代码。

虽然得到的R 平方指标(未显示)可能不足以作为基线模型,但系数的排序可以帮助我们优先考虑进一步的数据探索、预处理和特征工程。

可视化特征和标签之间的分类依赖关系

在具有多类名义目标变量的分类任务中,我们不能在不进一步预处理数据的情况下使用回归系数。另一种流行的、开箱即用的有效方法是,将简单的基于树的分类器拟合到训练数据中。根据训练数据的大小,我们可以使用决策树或基于树的集成分类器,如随机森林梯度提升树。这样做将根据选择的分割标准对特征维度进行特征重要性排序。在以熵分割的情况下,特征将按信息增益排序,这将表明哪些变量携带关于目标变量最多的信息。

以下图表显示了使用来自UCI 葡萄酒识别数据集archive.ics.uci.edu/ml/datasets/wine)的熵标准由基于树的集成分类器拟合的特征重要性:

图 5.6 – 基于树的集成分类器的特征重要性

图 5.6 – 基于树的集成分类器的特征重要性

这些线条代表单个树之间特征信息增益的变化。这个输出是进一步数据分析和按特征重要性顺序探索的绝佳第一步。您可以在本书 GitHub 仓库中的04_feature_importance.ipynb文件中找到计算特征重要性并可视化的代码。

这里是另一种发现数据集可分性的流行方法。以下截图显示了一个包含三个类别的数据集,其中一个是线性可分的,另一个不是:

图 5.7 – 线性可分数据集(左)与非线性可分数据集(右)的比较

图 5.7 – 线性可分数据集(左)与非线性可分数据集(右)的比较

您可以在本书 GitHub 仓库中的05_separability.ipynb文件中找到创建这些可分性图的代码。

通过观察三个簇以及这些簇之间的重叠,您可以发现,如果簇是分离的,那么训练好的分类模型将在这个数据集上表现非常好。另一方面,当我们知道数据不是线性可分的时候,我们知道这项任务将需要高级的特征工程和建模来产生良好的结果。

探索和分析文件数据集

由媒体文件组成的数据集完全是另一回事。如果我们以图像为例,我们可以将每个像素视为信息向量,并将其视为图像的一个特征。但在探索和数据处理方面我们能做什么呢?可能对单个特征来说并不多。大多数时候,我们需要关注的是大量像素或整个图像本身。从广义上讲,我们可以考虑以下方面:

  • 一致性:数据集中的所有图像都应该具有相同的大小。如果不是,它们需要被重新缩放,这可能涉及到每个通道的像素值居中,可能还伴随着某种形式的归一化。

  • 增强:这涉及到在不获取新数据(新图像)的情况下使数据集多样化。如果我们有一个小的数据集,这通常涉及到水平翻转、裁剪和旋转等变换。

观察这些选项,很明显我们正在尝试修复图像数据集中可能在我们最初拍摄图像时已经基本解决的问题。因此,现实情况是,当我们处理大多数类型的媒体文件时,将更高的注意力集中在为数据集采集好的训练样本上,比在预处理阶段拼命修复它们至关重要。

让我们想象一下,我们是一家制造商,想要拍摄他们生产的在传送带上通过的产品照片,以找出有缺陷的产品并丢弃它们。假设我们在全球各地都有生产基地。你将如何确保照片尽可能均匀地拍摄,同时覆盖许多不同的场景?以下是一些需要考虑的方面:

  • 相机类型:我们可能需要全球各地都使用相同类型的相机以相同的格式拍照。

  • 环境条件:所有地方的光照是否相似?温度和湿度是否在所有地方都相似?这可能会影响相机的电子设备。

  • 定位:是否使用了相同的拍摄角度?我们能否从非常不同的角度拍照以增加多样性?

这些只是你在拍照时需要考虑的一些点。

现在,让我们看看文件数据的一种另一种形式——声音文件。假设我们想要构建一个语音转文本模型,将我们说的话转换成书面文本。这类模型例如在语音助手中被用来将请求映射到一系列要执行的操作。

在这个背景下,我们可以使用傅里叶变换等方法来分解我们的声音文件。然而,我们可能需要考虑我们想要训练的样本或训练数据,以及如何在考虑以下方面的同时提高它们的质量:

  • 录音硬件:如果我们家里有语音助手,可能每个人都在使用同一个麦克风。但如果我们为手机构建语音助手呢?那么,我们就有了非常不同的麦克风。

  • 环境:我们可能需要在不同环境中录制声音。当我们站在有轨电车上时,与我们在录音棚中时,声音频谱肯定不同。

  • 发音:你大脑中的机器学习算法可能难以解析不同的发音——尤其是方言。一个实际的机器学习模型如何处理这个问题?

这些只是处理声音文件时需要考虑的一些点。关于发音,如果你查看Azure 语音服务,你很快就会意识到后台运行着两个模型——一个用于声学,一个用于语言。在构建自定义模型时查看样本的要求(docs.microsoft.com/en-us/azure/cognitive-services/speech-service/how-to-custom-speech-test-and-train),因为这可以给你一个很好的想法,当你从头开始构建这样的模型时需要什么。

总结来说,对于文件数据集,我们没有太多选项来从统计上消除问题,因此我们应该专注于采集好的、干净的样本,这些样本模拟了模型在生产环境中运行时可能遇到的现实环境。

现在我们已经熟悉了探索和分析不同类型数据集的方法,让我们尝试在一个实际的表格数据集上应用这些方法。

在表格数据集上执行数据分析

如果您没有遵循第四章“数据摄取与管理数据集”中的步骤,从melb_data.csv下载墨尔本住房数据集的快照,在您的存储账户的mlfiles容器中,并将其连接到 Azure Machine Learning 工作区中的数据存储mldemoblob

在接下来的章节中,我们将探索数据集,进行一些基本的统计分析,查找缺失值和异常值,找出特征之间的相关性,并在使用随机森林模型的同时,对特征的重要性进行初步测量,正如我们在本章的“可视化特征和标签依赖性以进行分类”部分所看到的。您可以选择创建一个新的 Jupyter 笔记本并跟随本书进行,或者打开 GitHub 仓库中本章的06_ dataprep_melbhousing.ipynb文件。

注意,我们现在要执行的步骤并不全面。如数据集网页所示,我们有 21 个特征可以工作。因此,为了彻底分析,你必须分析每一个。

本节应该能让你对可以执行的任务类型有一个很好的理解,但我们将会留下很多问题供你寻找答案。如果你需要一些灵感,可以查看 Kaggle 网站上的这个数据集。你将找到许多用户尝试分析这个数据集的笔记本。

最后,我们在此处不会完全转换实际数据,因为我们将在第六章“特征工程与标注”中回到这个问题,在那里我们将学习如何根据通过即将到来的过程获得的分析和知识来选择特征和创建新的特征。

初始探索和清洗墨尔本住房数据集

在本节中,我们将从在 Azure Machine Learning 中注册的数据存储中加载数据,并查看其内容。之后,我们将开始进行一些关于原始数据的基本清洗:

  1. 通过 Python PIP 单独下载以下包或使用本书 GitHub 仓库中可找到的需求文件:pandasseabornplotlyscikit-learnnumpymissingnoumap-learnstatsmodels

  2. 创建一个新的 Jupyter 笔记本或跟随之前提到的笔记本进行。

  3. 通过配置文件连接到您的 ML 工作区,正如我们之前所学的。

  4. 使用以下代码将数据集拉取到您的本地计算机:

    from azureml.core import Datastore, Dataset
    import pandas as pd
    import seaborn as sns
    import numpy as np
    import plotly.express as px
    import matplotlib.pyplot as plt
    # retrieve an existing datastore in the workspace by name
    datastore_name = 'mldemoblob'
    datastore = Datastore.get(ws, datastore_name)
    # create a TabularDataset from the file path in datastore
    datastore_path = [(datastore, 'melb_data.csv')]
    tabdf = Dataset.Tabular.from_delimited_files
           (path=datastore_path)
    

在这里,我们正在从您定义的 ML 数据存储yourname中检索数据,并将数据集加载到一个表格数据集对象中。根据您数据存储中的文件夹结构,调整第二行最后面的文件路径和名称。

  1. 在表格数据集对象上可用的方法不如在 pandas DataFrame 上那么多。所以,让我们将其转换为 pandas DataFrame,并首次查看数据:

    # increase display of all columns of rows for pandas datasets
    pd.set_option('display.max_columns', None)
    pd.set_option('display.max_rows', None)
    # create pandas dataframe
    raw_df = tabdf.to_pandas_dataframe()
    raw_df.head()
    

pd.set_option()方法让你可以访问 pandas 操作的通用设置。在这种情况下,我们希望所有列和行都在可视化中显示,而不是被截断。你可以将其设置为对你有用的任何值。

head()函数将让你先看看数据集的前五行。看看它们。

你将看到很多有意义的特征,比如SuburbAddressBathroom。但有些其他特征可能不太清楚,比如TypeMethodDistance

通常,与任何数据集一样,对于随附的字段,都有某种形式的数据定义。查看数据集的网站以找到它们。

  1. 现在我们已经查看了定义,让我们看看数据集的所谓“形状”,这将显示数据集包含多少列(特征和标签)以及多少行(样本):

    raw_df.shape
    

前面的命令显示了一个包含 13,580 个样本和 21 个特征/标签的数据集。

  1. 最后,运行以下代码,以便我们可以查看每个特征的唯一值数量、缺失值数量和数据类型:

    stats = []
    for cl in raw_df.columns:
        stats.append((cl,
                      raw_df[cl].nunique(), 
                      raw_df[cl].isnull().sum(),
                      raw_df[cl].isnull().sum() * 100 / 
                                       raw_df.shape[0],
                      raw_df[cl].value_counts(
                           normalize=True, 
                            dropna=False).values[0] * 100,
                      raw_df[cl].dtype))
    # create new dataframe from stats   
    stats_df = pd.DataFrame(stats, columns=[
                  'Feature', 
                  'Unique Values',
                  'Missing Values',
                  'Missing Values [%]', 
                  'Values in the biggest category [%]', 
                  'Datatype'])
    stats_df.sort_values('Missing Values [%]',
                         ascending=False)
    

运行前面的代码后,你应该会看到以下类似的内容:

图 5.8 – 墨尔本住房数据集特征概述

图 5.8 – 墨尔本住房数据集特征概述

看这张表格,我们可以得出以下观察结果:

  • 有四个特征似乎有缺失值(BuildingAreaYearBuiltCouncilAreaCar)。

  • 许多数值(如float64类型。这并不一定是问题,但既然每个值可能都适合int8int16int32,所以这是一种空间上的浪费。

  • 有七个object类型的特征,它们很可能都是字符串值。我们很快会详细查看它们。

  • 有一个名为Price的特征,这可能是监督学习(如分类)的一个很好的标签/目标。

  • 有一个名为Postcode的特征和一个名为Suburb的特征。我们可能不需要两者都保留。从唯一值的数量来看,Suburb似乎更细粒度。

  • 有一个名为Address的特征和一个名为SellerG的特征。尽管物业的卖家可能对价格有一定的影响,但我们现在可以为了简单起见先删除它们。同样,地址也是极其精确的。几乎每个样本都有一个唯一的地址。

通过查看object类型的七个特征,我们可以看到以下情况:

  • 类型:这有3个不同的值;我们的数据定义显示6。我们需要检查这个差异。

  • 方法:这有5个不同的值;我们的数据定义显示11。我们也需要检查这一点。

  • SellerG:这有268个不同的卖家名称。

  • Address:这有 13378 个不同的值,但我们有 13580 个样本,所以似乎有多个地址相同的地方。尽管如此,我们在这里有极端的多样性,这使得这个特征相当不重要。

  • Regionname:这有 8 个不同的值——即墨尔本的区域。

  • Suburb:这有 314 个不同的值——即墨尔本的郊区。

  • CouncilArea:这有 33 个不同的值,并且是唯一具有缺失值的类别特征。

到目前为止,我们已经找到了一些有趣的信息和一些线索,表明我们下一步需要查看的地方。现在,让我们深入到特征的内容并进行一些初步的数据集清理。

  1. 让我们从删除一些不太重要的特征开始:

    df = raw_df.drop(['Address', 'SellerG'],axis=1)
    

如您所见,我们保留了原始的 DataFrame,称为 raw_df,并创建了一个新的 DataFrame,称为 df。通过这样做,我们可以在任何时间添加已删除的特征。DataFrame 中的每一行都有一个索引,因此即使我们过滤掉行,我们仍然可以匹配原始值。

  1. 接下来,我们将重命名一些列以增加我们对它们的理解:

    df = df.rename(columns={'Bedroom2': 'Bedrooms', 
                            'Bathroom': 'Bathrooms',
                            'Regionname': 'Region',
                            'Car': 'Parking',
                            'Propertycount':  
                            'SuburbPropCount'})
    df.head()
    
  2. 在这一点上,寻找重复项可能是个好主意。让我们运行以下代码片段来查找重复项:

    s = df.duplicated(keep = False)
    s = s[s == True]
    s
    

keep 设置为 False 将显示每个具有重复项的行。在这里,我们可以看到有两行是相同的。我们可以通过以下命令查看它们:

df.loc[[7769,7770]]

如您所见,这些表示相同的条目。所以,让我们使用以下命令删除其中一个:

df.drop([7769], inplace=True)

由于这只是一个示例,我们可以通过其行索引来删除它。通常,这类操作会返回一个新的 DataFrame,但在许多操作中,我们可以使用一个名为 inplace 的属性来直接覆盖当前的 DataFrame。

  1. 现在,让我们看看似乎有缺失类别的分类特征,从 Method 开始:

    df['Method'].unique()
    

我们数据集中的类别是 SSPPIVBSA。从数据定义中的列表来看,我们可以看到数据集中只指定了房产的销售地点以及我们知道的销售价格。有人已经为我们清理了这些信息。

通过查看 Type,我们可以看到单卧室、开发用地和其他住宅区域也被删除了,留下了房屋、单元和联排别墅:

df['Type'].unique()

为了使这些条目更清晰,让我们将单个字母替换为全名:

df = df.replace({'Type':  
               {'h':'house','u':'unit','t':'townhouse'}})
df = df.replace({'Method': {'S':'Property Sold',
                            'SP':'Property Sold Prior',
                            'PI':'Property Passed In',
                            'VB':'Vendor Bid', 
                            'SA':'Sold After Auction'}})
df.head()
  1. 现在,让我们专注于包含大量条目的分类特征。以下代码显示了该列中唯一值的列表:

    df['CouncilArea'].unique()
    

我们将得到以下结果集:

array(['Yarra', 'Moonee Valley', 'Port Phillip', 'Darebin', 'Hobsons Bay', 'Stonnington', 'Boroondara', 'Monash', 'Glen Eira', 'Whitehorse', 'Maribyrnong', 'Bayside', 'Moreland', 'Manningham', 'Banyule', 'Melbourne', 'Kingston', 'Brimbank', 'Hume', None, 'Knox', 'Maroondah', 'Casey', 'Melton', 'Greater Dandenong', 'Nillumbik', 'Whittlesea', 'Frankston', 'Macedon Ranges', 'Yarra Ranges', 'Wyndham', 'Cardinia', 'Unavailable', 'Moorabool'], dtype=object)

在这里,我们可以看到有一个名为 None 的类别,其中包含我们的缺失值,还有一个名为 Unavailable 的类别。否则,似乎每个其他条目都定义得很好,似乎没有具有相同意义的重复条目;它们只是由于打字错误或空格而有所不同。这类错误通常被称为 结构错误

通过对 Suburb 特征运行相同的命令,我们得到一个更大的结果集。在这个阶段,要看到结构错误变得非常复杂,因此我们需要采取程序化的方法来检查这个类别。这里可以使用诸如模式匹配或模糊匹配之类的技术,但我们现在先不考虑这一点。您可以自由地查找有关模糊匹配Levenshtein 距离的主题,这些可以在结果集中找到相似词组的组。

  1. 最后,我们剩下最后一个问题,即关于邮编和郊区之间的关系,以及我们是否可以去掉其中一个。那么,让我们看看有多少邮编针对多个郊区:

    postcodes_df = df.groupby(
        'Postcode', as_index=False).Suburb.nunique()
    postcodes_df.columns = ['Postcode', 
                            '#Assigned Suburbs']
    postcodes_df.loc[postcodes_df['#Assigned Suburbs'] > 1]
    

在这里,我们创建了一个新的 DataFrame,显示了邮编和分配的郊区数量。通过搜索那些被映射到多个郊区的邮编,我们可以找到相应的列表。让我们来计数:

postcodes_df.loc[postcodes_df['#Assigned Suburbs'] > 1].count()

在这里,我们可以看到 198 个邮编中有 73 个指向多个郊区。尽管如此,每个郊区都有一个邮编,所以让我们保留郊区,并将邮编从 DataFrame 中删除:

df = df.drop(['Postcode'],axis=1)
df.head()

这已经看起来相当好了。作为最后一步,我们可以将数据类型从 float64 改为整数类型之一(int8int16int32int64),但我们还不足以了解数据点的分布情况,并且我们无法对有缺失值的列进行此操作。我们稍后再来处理这个问题。

到目前为止,我们已经对数据集进行了一些基本的探索和基础剪枝。现在,让我们更多地了解统计特性。

对数据集进行统计分析

是时候查看我们数值特征的统计特性了。为了做到这一点,运行以下代码片段:

dist_df = df.describe().T.apply(lambda s: s.apply(lambda x: format(x, 'g')))
dist_df

在这里,describe() 方法将为您提供数据集数值特征的典型统计特性表。T 将表进行转置,而 apply()lambda() 方法将帮助将数据点格式化为常规数值表示。您可以自由地移除 apply 方法并查看差异。

结果将显示一些信息,但我们还想添加一些额外的统计值,包括偏度众数以及等于众数、最大值和最小值的特征中的值的数量。通过以下代码,我们可以实现这一点:

from pandas.api.types import is_numeric_dtype
max_count=[]
min_count=[]
mode_count=[]
mode=[]
skew=[]
for cl in df.columns:
    if (is_numeric_dtype(df[cl])):
        max_count.append(df[cl].value_counts(
                         dropna=False).loc[df[cl].max()])
        min_count.append(df[cl].value_counts(
                         dropna=False).loc[df[cl].min()])
        mode_count.append(df[cl].value_counts(
                     dropna=False).loc[df[cl].mode()[0]])
        skew.append(df[cl].skew())
        mode.append(int(df[cl].mode()[0]))
dist_df['mode'] = mode
dist_df['skew'] = skew
dist_df['#values(min)'] = min_count
dist_df['#values(max)'] = max_count
dist_df['#values(mode)'] = mode_count
dist_df

在这里,我们创建了一系列列表,并将我们基础 DataFrame 中每个列的计算值追加到每个列表中。我们还为每个我们计算过的属性列表添加了一个新列到我们的分布 DataFrame,dist_df。为了便于您理解代码,我们在这里使用了 Python 列对象。您可以通过使用另一个 pandas DataFrame 来缩短这段代码,这留作您的练习。

运行前面的代码后,你应该看到以下类似的输出:

图片

图 5.9 – 墨尔本住房数据集的统计特性

让我们通过查看这个表格来了解每个特征的推断:

  • 价格: 这个值向右偏斜。在这里,我们可能会看到一些高价,这并不奇怪。最高的房价是 900 万。

  • 距离: 这个值向右偏斜,可能是由于一个样本距离墨尔本 CBD 有 48.1 公里。有趣的是,有6个样本的距离为0。有时,0 是一个虚拟值,所以我们应该检查这些样本。根据11这个众数被设置了739次的事实,距离可能不是城市中心的精确距离,而是郊区到城市中心的平均距离。我们也应该弄清楚这一点。

  • 卧室: 由于某些地方的卧室很多,这个值向右偏斜。奇怪的是,有16个样本的卧室数为0,需要核实。

  • 浴室: 这与卧室特征的分布相似,有34个样本没有浴室,这又很奇怪。

  • 停车位: 这与卧室特征的分布相似。有1026个样本没有停车位,这听起来是合理的。

  • 土地面积: 这个值极度向右偏斜(95.24)。最大值是433014。如果我们假设这里使用的是平方米,那么大约有 43 公顷的土地。这并不是不可能的,但显然这是一个异常值,可能会扭曲我们的模型。

  • 建筑面积: 由于最大值为44515平方米,这个值极度向右偏斜。这听起来相当不可能,所以我们可能想要去掉这个值。此外,还有17个样本的面积为0平方米,需要检查。

  • 建造年份: 由于有一个建筑是在1196年建造的,这个值向左偏斜。我们可能想要丢弃这个值。

  • 经度/纬度: 这些值似乎分布得相当合理,但奇怪的是,1721的值分别相同,具体是**-37144**。这让我们有了一些想法,即坐标可能没有我们想象的那么精确。

  • 郊区房产数量: 这个值略微向右偏斜。我们必须分析这个值有多有帮助。

现在,让我们考虑我们期望的关系,并查看这些特征之间的关系:

  • 房间与浴室/卧室: 如果你看一下这些值的分布,就会变得清楚,我们并不完全清楚房间的含义。房间的最大值是10,而卧室的最大值是20。查看数据定义,我们可以看到卧室是从多个不同来源获取的,所以我们可能在那些数据点之间有差异。

  • 建筑面积与房间/浴室/卧室: 我们预计会有某种正相关性,但仅凭现有数据我们无法判断。

如我们所见,仅从这张表格中我们就可以获得一些非常有价值的见解,并对下一步要查看的内容有一个很好的了解。我们现在将检查价格BuildingArea特征,但在现实中,我们可能需要遵循所有这些途径。请随意自己尝试,并查看提供的笔记本以获取更多想法。

首先,让我们看看seabornplotly库。了解它们是如何工作的以及它们之间的区别。为了简单起见,我们现在将使用plotly。使用以下代码来绘制一个箱线图,并在旁边显示数据点的分布:

fig = px.box(df, x="Price",points="all")
fig.show()

你应该看到以下图表:

图 5.10 – 价格目标的箱线图

图 5.10 – 价格目标的箱线图

悬停在箱线图上,你可以看到价格向量的log值,然后再看一眼。

要做到这一点,让我们在我们的 DataFrame 中添加一个新的列,包含价格log值,并再次运行可视化:

df["Price_log"] = np.log(df['Price']) 
fig = px.box(df, x="Price_log",points="all")
fig.show()

这将得到以下图表:

图 5.11 – log (价格) 目标的箱线图

图 5.11 – log (价格) 目标的箱线图

这样做似乎是个好主意,因为它的分布更好。请随意检查这个分布的偏斜。

现在,让我们看看BuildingArea特征。再次,让我们使用以下代码创建一个箱线图:

fig = px.box(df, y="BuildingArea",points="all")
fig.show()

这将得到以下图表:

图 5.12 – BuildingArea 特征的箱线图

图 5.12 – BuildingArea 特征的箱线图

我们看到一个非常扭曲的箱线图。悬停在它上面,我们可以看到上界295平方米,而最大值44515平方米。有一个主要异常值和一些小异常值。

让我们使用以下代码查看有多少样本高于295

df.loc[raw_df['BuildingArea'] > 295]['BuildingArea'].count()

结果仍然显示有353个样本高于此阈值。从箱线图来看,这可能很快就会减少到 2,000 平方米。因此,让我们使用以下代码检查超过 2,000 平方米的结果集:

df.loc[raw_df['BuildingArea'] > 2000]

这将给出以下输出:

图 5.13 – 按 BuildingArea 大小排名前四的样本

图 5.13 – 按 BuildingArea 大小排名前四的样本

如我们所见,最大的房产距离市中心 48.1 公里,因此在这个范围内的LandsizeBuildingArea是可行的。然而,如果我们想了解墨尔本的房子价格,这可能并不那么重要。它位于北维多利亚地区,而不是大都市地区。我们可以进一步探讨这些特定房屋与正常情况外的其他特征之间的联系,但我们将就此搁置。

让我们使用以下代码从我们的数据集中删除主要异常值:

df.drop([13245], inplace=True)

由于它只包含一个样本,我们可以通过行 ID 将其删除。

在这一点上,我们可以继续用其他特征进行此类分析,但我们将把它留给你作为一个练习,以便更深入地了解其他特征及其统计依赖性。现在,让我们继续看看之后我们会做什么。

在我们继续之前,让我们使用以下函数将我们的数据集保存到 Azure 机器学习:

Dataset.Tabular.register_pandas_dataframe(
        dataframe = df, 
        target = datastore, 
        name ='Melbourne Housing Dataset', 
        description = 'Data Cleansing 1 - removed address,    
                       postcode, duplicates and outliers')

在这个练习中,我们将继续这样做,以便以后可以拥有不同的版本。

寻找和处理缺失值

我们接下来的任务是处理数据集中的缺失值。我们可以使用一个非常好的扩展missingno来获取一些关于缺失值的有趣可视化。

但在那之前,让我们运行以下代码,看看如果我们删除所有具有缺失值的样本会发生什么:

df.dropna(how='any').shape

如我们所见,结果 DataFrame 将包含6196个样本,这将少于数据集的一半。所以,处理缺失值可能是一个好主意。

现在,运行以下代码:

import missingno as msno
msno.matrix(df);

这将产生以下输出:

图 5.14 – DataFrame 及其缺失值的结构可视化

图 5.14 – DataFrame 及其缺失值的结构可视化

如我们所见,CouncilArea特征在 DataFrame 的后部样本中只有缺失值,Parking在后部样本中只有非常小的一部分缺失,而BuildingAreaYearBuilt在整个 DataFrame 中都有缺失。

正如我们已经学到的,我们可以通过为缺失的类别数据发明一个新的类别或用缺失的连续数据的平均值来替换它们来进行替换。

让我们从Unavailable开始,看看具有此类别的样本,通过选择具有该特性的任何样本:

df.loc[df.CouncilArea.isin(['Unavailable'])]

如我们所见,只有一个条目属于这个类别。这似乎是一个有效的条目;它只是缺少议会区域的名字。所以,让我们使用以下代码用一个新的类别Missing替换这个条目和缺失值:

df['CouncilArea'].fillna(value = "Missing", inplace = True)
df['CouncilArea'].replace(to_replace="Unavailable", value="Missing", inplace=True)

在特征之后检查唯一值显示,NoneUnavailable类别中不再有任何值:

df['CouncilArea'].unique()

这是替换特征的最简单方法。由于这些是墨尔本的议会区域,每所房子都应该分配到一个区域,所以更好的想法是找到另一个匹配郊区或地址到议会区域的数据库,并进行交叉引用。您可以自由搜索并执行此操作。

继续使用三个连续特征,我们可以使用以下代码将任何缺失值替换为该列的平均值,并在之后检查是否还有剩余的缺失值:

BA_mean = df['BuildingArea'].mean()
df['BuildingArea'].replace(to_replace=np.nan, value=BA_mean, inplace=True)
df['BuildingArea'].isnull().sum()

最终命令的结果显示我们填充的平均值是145.749。将此代码修改为对YearBuiltParking执行相同的操作。然而,您可能希望对这些使用中位数而不是平均值

目前,这解决了缺失值的问题,从统计学的角度来看,这是一种合理的方法。然而,正如我们讨论的那样,这是做这件事的最简单方法之一。更好的方法是在特征之间找到关系,并使用它们来填充缺失值。我们不仅可以使用整个数据集的平均值,还可以集中寻找与缺失值样本具有相似特征的数据子集。例如,我们可以找到一侧停车位数量与房屋内房间数量或房屋大小另一侧之间的依赖关系。然后,我们可以定义一个函数,根据这些其他特征给出 Parking 的值。

因此,为了更好地处理缺失值,我们需要找出关系,我们将在下一节中查看这些关系。

但在那之前,让我们再次用这个描述注册这个数据集:数据清洗 2 - 替换缺失值

计算相关性和特征重要性

到目前为止,我们已经研究了单个特征、它们的内容以及它们的分布。现在,让我们看看它们之间的关系。

使用以下代码生成我们特征和目标之间的相关矩阵:

# compute the correlation matrix
corr = df.corr()
# define and create seaborn plot
mask = np.triu(np.ones_like(corr, dtype=np.bool))
f, ax = plt.subplots(figsize=(11, 9))
cmap = sns.diverging_palette(220, 10, as_cmap=True)
sns.heatmap(corr, mask=mask, cmap=cmap, vmax=.3,
            center=0, square=True, linewidths=.5, 
            cbar_kws={"shrink": .5})
plt.show()

生成的矩阵将显示我们 13 个特征的相关性,但不是全部。如果你检查可见的,你会看到我们缺少所有 objectdatetime 类型的数据。

因此,在我们分析矩阵之前,让我们通过从我们的 DataFrame 中开始挖掘剩余的 object 类型列来添加缺失的特征:

obj_df = df.select_dtypes(include=['object']).copy()
obj_df.head()

在这里,我们可以看到剩余的列是 category,我们现在将我们的列转换为:

for cl in obj_df.columns:
    obj_df[cl] = obj_df[cl].astype('category')
obj_df.dtypes

因此,我们创建了一个名为 obj_df 的 DataFrame,其中包含五个 category 类型的特征。现在,让我们为每个类别分配一个数值。为此,我们将使用 cat.codes 方法,并在我们的 DataFrame 中创建五个新列,列名扩展为 _cat

for cl in obj_df.columns:
     obj_df[cl+"_cat"] = obj_df[cl].cat.codes
obj_df.head()

完美!我们已经创建了一个包含编码类别的 DataFrame。我们将将这些新特征与我们的原始 DataFrame,df,合并到一个新的 DataFrame 中,称为 cont_df

column_replacement = {'Type':'Type_cat','Suburb':'Suburb_cat','Method':'Method_cat','CouncilArea':'CouncilArea_cat','Region':'Region_cat'}
cont_df = df.copy()
for key in column_replacement:
     cont_df[key] = obj_df[column_replacement[key]]
cont_df.dtypes

上述代码的输出显示了新数据集中所有列的数据类型。我们仍然可以看到 datetime 类型和一些应该为 int 类型的原始列。在再次创建相关矩阵之前,让我们纠正这个问题。

首先,让我们创建一个名为 Date_Epoch 的新列,该列包含一个表示自纪元(docs.python.org/3/library/time.html)以来的秒数的整数,并删除原始的 Date 列:

cont_df['Date_Epoch'] = cont_df['Date'].apply(lambda x: x.timestamp())
cont_df.drop(['Date'], axis=1, inplace=True)
cont_df.dtypes

我们还可以将 Date 分解为 Month 列和 Year 列,因为它们可能产生影响。请随意添加它们。

现在,让我们将所有 float64 列转换为整数,除了那些浮点数是正确的情况:

for cl in cont_df.columns:
    if (cont_df[cl].dtype == np.float64 and cl not in    
                                   ['Lattitude', 'Longtitude', 
                                    'Price_log', 'Distance']):
       cont_df[cl] = cont_df[cl].astype('int')
cont_df.dtypes

上述代码显示,我们的 DataFrame 现在由最优化大小和格式的数值数据类型组成(一些特征每个值只占用 8 位内存)。

现在,是时候再次运行相关性矩阵了。使用我们之前使用的相同代码 – 只需将 df 替换为我们的新 cont_df。结果应该如下所示:

图 5.15 – 所有特征及其目标的相关矩阵

图 5.15 – 所有特征及其目标的相关矩阵

强烈的红色表示正相关,而强烈的蓝色表示负相关。基于此,我们可以得出以下结论:

  • 房间数量价格价格对数距离卧室数量浴室数量停车位建筑面积 强烈相关。

  • 类型价格价格对数卧室数量建造年份房间数量 强烈相关。

  • 价格房间数量类型卧室数量浴室数量停车位建筑面积 强烈相关。

  • 郊区方法土地面积郊区房产数量似乎在当前状态下对其他特征或目标没有太大的影响。

观察这些结果,它们并不令人惊讶。郊区有太多的类别,无法精确地用于任何事物,方法也不应该有太大的影响,土地面积可能也不是最大的因素,而郊区房产数量可能也有太多的变化。可能的转换包括要么删除郊区郊区房产数量,要么将它们映射到一个变化较少的类别。

在我们继续之前,让我们将 cont_df 注册为具有描述:“数据清洗 3 - 所有特征转换为数值类型”的数据集版本。

作为最后的任务,让我们使用 06_dataprep_melbhousing.ipynb 文件来双重检查我们到目前为止所了解的内容。在那里,你会看到我们计算了 价格价格对数 目标特征的重要性。两者的结果都显示在这里:

图 5.16 – 价格(左侧)和价格对数(右侧)的特征重要性

图 5.16 – 价格(左侧)和价格对数(右侧)的特征重要性

如我们所见,房产类型明显影响其价格。这种影响可能看起来并不那么巨大,但请注意,我们正在查看对数形式的房价。

我们到目前为止所了解的内容与这些结果相符。通过观察图表之间的差异,我们可以看到,将对数缩放添加到我们的目标变量中,增强了最有影响力的特征。类型特征似乎对我们的目标有很强的影响。

让我们通过以下代码来结束这个练习,看看这种关系:

fig = px.box(df, y="Price_log",x='Type', color = 'Type', 
                 category_orders={"Type": ["house",
                                  "townhouse", "unit"]})
fig.show()

这些结果如下:

图 5.17 – 类型与价格对数之间的相关性

图 5.17 – 类型与价格对数之间的相关性

这样,我们就完成了这个练习。我们能够清理我们的数据集,发现一些非常好的初步见解,并发现我们的目标变量与一个特征之间有非常强的相关性。

还有很多开放性问题,我们仍然处于完全理解这个数据集的初期。例如,除了价格目标之外,我们没有查看特征缩放或归一化,这是某些算法的另一个可能要求。

我们将在第六章特征工程和标记中继续使用这个数据集。在此之前,请随意深入挖掘这个数据集的秘密,或者尝试将您新获得的知识应用于不同的数据集。

在 Azure 机器学习中跟踪探索中的图表

在我们的数据探索过程中,我们创建了大量的不同图表和可视化。让我们学习如何使用 Azure 机器学习跟踪它们,以便它们不仅仅存在于我们的 Jupyter 笔记本中。

第三章准备 Azure 机器学习工作区中,我们学习了如何使用 Azure 机器学习跟踪 ML 实验的指标和文件。您数据转换和 ML 脚本的其他重要输出包括可视化、数据分布的图表、关于模型的见解以及结果。因此,Azure 机器学习提供了一种类似的方式来跟踪图像、图表和matplotlib引用的指标。

让我们想象一下,我们使用以下代码创建了一个流行的Iris 花卉数据集archive.ics.uci.edu/ml/datasets/iris)的pairplot

import seaborn as sns
sns.set(style="ticks")
df = sns.load_dataset("iris")
sns.pairplot(df, hue="species")

通过几行代码,我们可以跟踪所有的matplotlib图表并将它们附加到我们的实验运行中。为此,我们只需将matplotlib引用传递给run.log_image()方法,并给它一个合适的名称。以下代码显示了在实验中这会是什么样子:

with exp.start_logging() as run:
  fig = sns.pairplot(df, hue="species")
  run.log_image("pairplot", plot=fig)

现在,这是最令人惊奇的部分。通过调用带有matplotlib引用的函数,Azure 机器学习将渲染图表,保存它,并将其附加到实验运行中。以下截图显示了 Azure 机器学习工作室,其中包含了我们刚刚创建并注册的pairplot图像:

图 5.18 – 在 Azure 机器学习工作室中跟踪并显示的 Pairplot

图 5.18 – 在 Azure 机器学习工作室中跟踪并显示的 Pairplot

这似乎是一个微不足道的功能,但在现实世界的实验中非常有用。习惯于自动生成数据、模型和结果图表,并将它们附加到您的运行中。当您稍后回顾实验时,您将已经将所有可视化附加到您的运行、指标和配置中。

当您在训练回归模型时,考虑存储回归图;当训练分类模型时,存储混淆矩阵和 ROC 曲线。当您在训练基于树的集成和神经网络的激活时,存储特征重要性。您可以一次性实现这一点,并将大量有用的信息添加到您的数据和机器学习管道中。

重要提示

当您使用 AutoML 和 HyperDrive 优化参数、预处理、特征工程和模型选择时,您将获得大量生成的可视化,以帮助您理解数据、模型和结果。

现在我们已经知道了如何在 Azure 机器学习工作区中存储可视化,让我们学习如何创建表示高维数据的可视化。

理解降维技术

在前面的章节中,我们探讨了多种可视化数据的方法,但高维数据在二维中难以准确可视化。为了实现这一点,我们需要某种类型的投影或嵌入技术来将特征空间嵌入到二维中。您可以使用许多线性和非线性嵌入技术来生成数据的二维投影。以下是最常见的几种:

  • 主成分分析 (PCA)

  • 线性判别分析 (LDA)

  • t 分布随机邻域嵌入 (t-SNE)

  • 均匀流形近似和投影 (UMAP)

下图显示了 13 维 UCI 葡萄酒识别数据集archive.ics.uci.edu/ml/datasets/wine)的 LDAt-SNE 嵌入。在 LDA 嵌入中,我们可以看到所有类别应该是线性可分的。这就是我们在开始模型选择或训练过程之前,仅用两行代码绘制嵌入所学到的东西:

图 5.19 – 监督 LDA(左)与无监督 t-SNE(右)

图 5.19 – 监督 LDA(左)与无监督 t-SNE(右)

LDAt-SNE 嵌入对于判断单个类别的可分性和因此分类任务的难度非常有帮助。在开始选择和训练特定算法之前,始终评估特定模型在您的数据上的表现总是好的。

通过可视化数据来快速获得洞察力和对数据的良好理解是一个很好的方法。这也有助于您识别数据中的聚类、不规则性和异常情况——所有这些都是在所有进一步的数据处理中都需要考虑的因素。但是,您如何可视化具有 10、100 或 1,000 个特征维度的数据集?您应该在哪里保存分析?

在本节中,我们将回答所有这些问题。首先,我们将探讨线性嵌入技术——PCA,一种无监督技术,以及LDA,一种监督技术。然后,我们将比较这两种技术与两种流行的无监督非线性嵌入技术,t-SNEUMAP,后者是 t-SNE 的通用和更快版本。拥有这四种技术将有助于你理解数据集并创建有意义的可视化。我们将对越来越复杂的数据集运行所有这些技术,具体如下:

  • Iris 花卉数据集:这个数据集包含三个类别和四个特征维度。

  • UCI 葡萄酒识别数据集:这个数据集包含三个类别和十三个特征维度。

  • MNIST 手写数字数据集:这个数据集包含 10 个类别和 784 个特征维度(28 x 28 像素图像)。

为了简洁起见,本节中省略了生成嵌入的代码,但可以在本书 GitHub 仓库中的07_dimensionality_reduction.ipynb文件中找到。

使用 PCA 进行无监督降维

最流行的线性降维技术是 PCA。这是因为,由于它是一种无监督方法,它不需要任何训练标签。PCA 将数据集线性转换,使得结果投影是不相关的。这个投影的轴被称为主成分,并且以这种方式计算,使得每个成分都有下一个最高的方差。

主成分是数据中最高方差的方向。这意味着主成分或特征向量描述了数据集的最强方向,下一个维度显示了与前一个方向的正交差异。在 NLP 中,主成分对应于高级概念——在推荐引擎中,它们对应于用户或项目特征。

PCA 可以通过协方差矩阵或相关矩阵的特征值分解来计算,或者通过使用 SVD 在非方阵上计算。PCA 和特征值分解通常用作数据实验步骤以进行可视化,而 SVD 通常用于稀疏数据集的降维;例如,NLP 中的词袋模型。我们将在第七章,“使用 NLP 的高级特征提取”中看到 SVD 在实际中的应用。

嵌入技术可以通过仅保留前x个成分来作为一种降维形式,因为这些第一个——也是最大的——成分解释了数据集一定比例的方差。因此,我们必须移除方差较低的数据,以获得低维数据集。

在二维(或执行任何嵌入技术后)执行 PCA 后的数据可视化,就是可视化变换数据集的前两个成分——两个最大的主成分。结果数据沿着轴——主成分——缩放,并居中于零。以下图表显示了前两个数据集的 PCA 结果。如您所见,所有可视化都有最高方差投影在x轴上,第二高方差在y轴上,依此类推:

图 5.20 – 鸢尾花数据集(左侧)和 UCI 葡萄酒识别数据集(右侧)的 PCA

图 5.20 – 鸢尾花数据集(左侧)和 UCI 葡萄酒识别数据集(右侧)的 PCA

在这里,我们应该承认,我们能够仅用两个维度展示这三个数据集是一个很好的第一步,并且可以立即识别簇。

通过将数据投影到前两个主成分上,并在左侧查看鸢尾花数据集,我们可以看到所有簇看起来都是线性可分的(在二维空间中)。然而,当我们查看右侧的 UCI 葡萄酒识别数据集时,我们可以已经看出簇不再那么明显了。现在,13 个特征维度与前两个主成分一起投影,其中最高方差沿着x轴,第二高方差沿着y轴。在 PCA 中,簇的形状通常与x轴对齐,因为算法就是这样工作的。

现在,让我们对最复杂的数据集——MNIST 手写数字数据集——运行 PCA。这样做的结果可以在以下图表中看到:

图 5.21 – MNIST 手写数字数据集的 PCA 结果

图 5.21 – MNIST 手写数字数据集的 PCA 结果

当我们查看更复杂的 MNIST 手写数字数据集的嵌入时,除了可能位于顶部的0簇外,我们看不到很多簇。数据围绕零中心并缩放到**-3030**的范围内。因此,我们已能看出 PCA 的缺点——它不考虑任何目标标签,这意味着它不针对可分类别进行优化。

在下一节中,我们将探讨一种考虑目标标签的技术。

使用 LDA 进行监督降维

在 LDA 中,我们线性变换输入数据——类似于 PCA——并优化变换,使得结果方向具有最高的簇间方差和最低的簇内方差。这意味着优化尝试使同一簇的样本靠近簇的平均值,同时尝试使簇的平均值尽可能远。

在 LDA 中,我们还收到一个作为结果变换的线性加权方向集。数据以 0 为中心,方向按其最高簇间方差排序。因此,从这个意义上说,LDA 类似于 PCA,因为它考虑了目标标签。LDA 和 PCA 都没有真正的调整旋钮,除了我们希望在投影中保留的组件数量,可能还有一个随机初始化种子。

下面的图表显示了我们对前两个数据集执行 LDA 的结果:

图 5.22 – 红花数据集(左)和 UCI 葡萄酒识别数据集(右)的 LDA 结果

图 5.22 – 红花数据集(左)和 UCI 葡萄酒识别数据集(右)的 LDA 结果

在这里,我们可以看到数据被转换成二维,使得聚类均值在x轴上彼此之间距离最远。对于红花和 UCI 葡萄酒识别数据集,我们都可以看到同样的效果。在两个嵌入中,我们还可以观察到另一个有趣的事实,即数据也变得线性可分。我们几乎可以在两个可视化中画两条直线来将聚类分开。

对于这两个数据集,LDA 嵌入在数据按类别分离方面看起来相当不错。据此,我们可以有信心认为这两个数据集的线性分类器应该会取得很好的性能——例如,超过 95%的准确率。虽然这可能只是一个粗略的估计,但我们已经知道从线性分类器中可以期待什么,即使是最小化的分析和数据预处理。

不幸的是,大多数现实世界的嵌入看起来都更像下面所示图表中的那种,我们在这个最终数据集上使用了 LDA。这是因为大多数现实世界的数据集通常具有超过 10 个甚至 100 个特征维度:

图 5.23 – MNIST 手写数字数据集的 LDA 结果

图 5.23 – MNIST 手写数字数据集的 LDA 结果

在这里,我们还可以看到包含底部0数字的簇与左侧的四和六的两个簇之间有很好的分离。所有其他簇都重叠在一起,看起来并不线性可分。

因此,我们可以判断线性分类器不会表现良好,可能只有大约 30%的准确率——这仍然比我们随机做要好得多。然而,我们无法预测复杂非线性模型(甚至基于决策树集成分类器的非参数模型)的性能。

如我们所见,LDA 在考虑类别标签方面比 PCA 表现得更好。因此,在优化结果时考虑数据标注是值得考虑的。我们将在第六章,“特征工程和标注”中学习如何进行高效的标注。

LDA 是一种非常适合线性可分数据集的嵌入技术,这些数据集具有不到 100 个维度和分类目标变量。LDA 的一个扩展是二次判别分析QDA),它使用两个变量的组合进行非线性投影。如果你处理的是连续目标变量,你可以使用一个非常类似的技术,称为方差分析ANOVA),来建模簇之间的方差。ANOVA 变换的结果表明,数据集中的方差是否归因于不同组件方差的组合。

正如我们所见,无论是 PCA 还是 LDA 在分离高维数据,如图像数据时表现都不佳。在 Handwritten Digits 数据集中,我们处理的是来自 28 x 28 像素图像的仅有 784 个特征维度。想象一下,如果你的数据集由 1,024 x 1,024 像素的图像组成——你的数据集将会有超过一百万个维度。因此,我们需要一种更好的嵌入技术来处理非常高维的数据集。

使用 t-SNE 进行非线性降维

几年前,将高维数据集投影到二维或三维空间非常困难且繁琐。如果你想在二维图表上可视化图像数据,可以使用之前讨论过的任何技术——如果它们能计算出结果——或者尝试使用自组织映射等异国情调的嵌入。

尽管 t-SNE 在 2008 年由 Laurence van der Maaten 和 Geoffrey Hinton 发表在论文中(lvdmaaten.github.io/publications/papers/JMLR_2008.pdf),但直到 2012 年才有人将其应用于主要数据集。它在 Merck Viz Kaggle 竞赛中排名第一的团队中被使用——这是一种非常不寻常的方式,首次将一个伟大的嵌入算法应用于实践。然而,自从那次竞赛结束以来,t-SNE 已经在其他 Kaggle 竞赛和大型公司中定期用于嵌入高维数据集,并取得了巨大的成功。

t-SNE 将高维特征投影到二维或三维空间,同时最小化高维和低维空间中相似点的差异。因此,彼此靠近的高维特征向量在二维嵌入中也可能彼此靠近。

下图显示了 t-SNE 应用于爱丽丝花和 UCI 葡萄酒识别数据集。正如我们所见,复杂的非线性嵌入并没有比简单的 PCA 或 LDA 技术表现得更好。然而,它的真正力量在包含高达 3000 万个特征维度的非常大型和高维数据集中得到了体现:

图 5.24 – 爱丽丝花数据集(左)和 UCI 葡萄酒识别数据集(右)的 t-SNE 结果

图 5.24 – 爱丽丝花数据集(左)和 UCI 葡萄酒识别数据集(右)的 t-SNE 结果

在下面的图中,你可以看到 t-SNE 在 MNIST 数据集上的表现:

图 5.25 – MNIST 手写数字数据集的 t-SNE 结果

图 5.25 – MNIST 手写数字数据集的 t-SNE 结果

如我们所见,t-SNE 在 MNIST 数据集上表现非常好,轻松地分离了 10 个手写数字的簇。这表明可能达到 99% 的准确率。

这种可视化类型的美妙之处不仅在于我们可以看到数据是可分离的,而且我们可以通过查看前面的可视化来想象当分类器在数据上训练时,混淆矩阵将是什么样子。以下是我们仅从查看嵌入中可以得出的关于数据的观察:

将此项目符号列表替换为以下列表:

  • 有三个包含数字 1 样本的簇,其中一个簇离平均值更远。

  • 有三个包含数字 9 样本的簇,其中在少数情况下,这些样本非常接近数字 1 和数字 7 样本的簇。

  • 中间有一个包含数字 3 样本的簇,这些样本靠近数字 8 样本的簇。

  • 有一个包含数字 2 样本的微小簇,这些样本靠近数字 8 样本的簇。

  • 包含数字 3 和 9 样本的簇彼此非常接近,所以它们可能看起来很相似。

  • 包含数字 0、4 和 6 样本的簇与其他簇的距离非常远,这表明它们相当可分离。

这些是卓越的见解,因为当你手动探索样本时,你知道应该期待什么,以及应该寻找什么。这也帮助你调整特征工程,例如,尝试区分数字 179 的图像,因为它们将在建模后期导致最多的误分类。

使用 UMAP 推广 t-SNE

UMAP 是一种用于通用流形学习和降维的算法。它是基于黎曼几何和代数拓扑的 t-SNE 的一般化。

通常,UMAP 以拓扑方法提供与 t-SNE 相似的结果,具有更好的特征维度的可扩展性,以及运行时的更快计算。由于它更快,在拓扑结构方面表现略好,因此它迅速获得了人气。

如果我们再次查看 Iris 花朵和 UCI 酒类识别数据集的嵌入,我们会看到与 t-SNE 相似的效果。结果展示在下面的图中:

图 5.26 – Iris 花朵数据集(左)和 UCI 酒类识别数据集(右)的 UMAP 结果

图 5.26 – Iris 花朵数据集(左)和 UCI 酒类识别数据集(右)的 UMAP 结果

结果嵌入看起来合理,但它们并不优于 LDA 的线性可分结果。然而,我们不能仅仅通过比较结果来衡量计算性能,这正是 UMAP 的亮点所在。

当涉及到更高维度的数据,如 MNIST 手写数字数据集时,UMAP 作为二维嵌入技术在表现上非常出色。以下图表显示了 MNIST 手写数字数据集上的 UMAP 结果:

图 5.26 – MNIST 手写数字数据集的 UMAP 结果

图 5.26 – MNIST 手写数字数据集的 UMAP 结果

如我们所见,UMAP 将簇减少为在嵌入中完全可分实体,簇之间的重叠最小,簇本身的距离很大。做出与之前类似的观察,例如,关于数字19的簇,仍然是可能的,但簇看起来要明显可分得多。

从这些数据实验和可视化技术中,我们希望你能记住以下关键点:

  • 执行 PCA 以尝试分析特征向量

  • 执行 LDA 或 ANOVA 以了解你数据的方差

  • 如果你有复杂的高维数据,请执行 t-SNE 或 UMAP 嵌入

带着这些知识,我们可以直接进入特征工程,因为我们知道哪些数据样本容易处理,哪些样本会在生产中导致高误分类率。

摘要

在本章的前两部分,你学习了有哪些技术可以用来探索和统计分析原始数据集,以及如何在真实数据集上实际应用它们。

之后,你学习了可以使用哪些降维技术来可视化高维数据集。在那里,你了解了对你理解数据、其主成分、判别方向和可分性非常有用的技术。

此外,你在这章中学到的所有内容都可以在你的 Azure 机器学习工作区中的计算集群上执行,通过它可以跟踪生成的所有图表和输出。

在下一章中,利用你迄今为止所获得的所有知识,你将深入探讨特征工程这一主题,在那里你将学习如何选择和转换数据集中的特征,以便为 ML 训练做准备。此外,你将更深入地了解标记以及 Azure 机器学习如何帮助完成这项繁琐的任务。

第六章:第六章:特征工程和标注

在上一章中,我们学习了如何清理我们的数据并进行基本统计分析。在本章中,我们将深入探讨在开始我们的机器学习训练之前必须执行的两种更多类型的操作。这两个步骤是所有步骤中最重要的,除了高效地清理数据集之外,而且要擅长它们,你需要有大量的经验。本章将为你提供一个基础来构建。

在第一部分,我们将学习特征工程。我们将了解这个过程,如何从我们的数据集中选择预测特征,以及将我们的数据集中的特征转换为可用于我们的机器学习算法的方法。

在第二部分,我们将探讨数据标注。大多数机器学习算法属于监督学习类别,这意味着它们需要标注的训练数据。我们将探讨一些需要标签的典型场景,并学习 Azure 机器学习如何帮助完成这项繁琐的任务。

在本章中,我们将涵盖以下主题:

  • 理解和应用特征工程

  • 处理数据标注

技术要求

在本章中,我们将使用以下 Python 库和版本来对不同的数据集进行特征工程。

  • azureml-sdk 1.34.0

  • azureml-widgets 1.34.0

  • azureml-dataprep 2.20.0

  • pandas 1.3.2

  • numpy 1.19.5

  • scikit-learn 0.24.2

  • seaborn 0.11.2

  • plotly 5.3.1

  • umap_learn 0.5.1

  • statsmodels 0.13.0

  • missingno 0.5.0

与前几章类似,你可以使用本地 Python 解释器或 Azure 机器学习中的笔记本环境执行此代码。

本章中所有的代码示例都可以在本书的 GitHub 仓库中找到:github.com/PacktPublishing/Mastering-Azure-Machine-Learning-Second-Edition/tree/main/chapter06

理解和应用特征工程

特征工程是一个通用术语,描述了将我们数据集中的现有特征进行转换、创建缺失特征,并最终从我们的数据集中选择最具有预测性的特征以使用给定的机器学习算法开始机器学习训练过程的过程。这些不能仅仅被视为我们必须应用于我们的数据的某些数学函数。这是一种艺术形式,做得好可以区分一个平庸和高度表现的预测模型。如果你想知道你应该在哪里投入时间,特征工程是你可以对最终机器学习模型的质量产生最大影响的步骤。为了产生这种影响并提高效率,我们必须考虑以下因素:

  • 机器学习算法要求:特征是否需要特定的格式或范围?我如何最好地避免模型过拟合和欠拟合?

  • 领域知识:给定的特征是否足够用于我们的模型?我们能否创建包含更多预测信息的附加特征或派生特征?

在本节中,我们将定义不同的特征工程技术类别,然后探讨一些应用于不同类型数据集的最显著方法。

重要提示

请记住,特定特征工程方法的有用性取决于所使用的特征类型(分类、连续、文本、图像、音频)以及所选的机器学习算法。

特征工程技术分类

广义而言,特征工程方法可以归纳为以下类别:

  • 特征创建:从给定的特征集或额外的信息源中创建新的特征。

  • 特征转换:转换单个特征,使其对所使用的机器学习算法有用且稳定。

  • 特征提取:从原始数据中创建派生特征。

  • 特征选择:选择最突出和最具预测性的特征。

让我们看看这些类别及其包含的内容。

特征创建

特征工程的第一步是找到模型中应包含的所有特征。要擅长这一点,你必须对相关领域有深入了解,或者知道该领域的领域专家(SME)。最后,我们想要确保我们考虑了任何具有预测性且在合理时间内可以获取的数据点。

反过来,我们必须理解所有可以帮助我们在数据集中创建新特征的方法,无论是来自额外来源还是初始数据集。通常,这些方法可以按以下方式分类:

  • 添加缺失的预测特征:我们添加外部缺失信息,以实现更具有预测性的模型。

  • 结合可用特征:我们通过结合数据集中已有的特征来创建新的特征。

为什么我们必须更改数据集中已经存在的特征?

原因在于,我们理解的许多特征与标签之间的联系可能对所使用的机器学习算法来说并不明显。因此,考虑哪些特征或可用特征的表示我们认为对于使机器学习算法更容易把握内在联系是很有帮助的。

让我们看看一些例子,以便更好地理解这一点。

想象一下,你有一个用于预测房价的数据集,就像我们在第五章“执行数据分析与可视化”中考察的那样。此外,想象一下我们拥有的特征是房屋或公寓的长度宽度。在这种情况下,将这两个特征结合起来创建一个名为面积的新特征可能是有用的。此外,如果缺少建筑类型(房屋、公寓、联排别墅等),我们可能需要从其他来源添加这个信息,因为我们知道类型会影响房产的价格。

重要提示

如果你从现有特征中创建新特征,通常明智的做法是只保留新创建的特征,从数据集中删除那些初始特征。

现在,想象一下一个人在其一生中花费的金额。年轻时,这可能会非常少。随着年龄的增长,他们可能会有抵押贷款和子女,最终,当他们的子女搬出家时,他们的支出可能会下降,他们接近退休。由于这会在年龄生活成本之间形成某种抛物线关系,因此,对于机器学习算法来说,可能不容易掌握这一点。因此,一个可能的选择是将生活成本特征的值平方,以强调更高的成本,并降低较低的成本的重要性。

在前两个例子中,我们使用了我们的领域知识来创建新的特征。但如果我们没有这种知识怎么办?

有一种方法可以通过所谓的多项式扩展在数学上创建新特征。这个想法是通过将一个特征的值提升到一定的幂,并乘以一个或多个其他特征来创建新特征。在这里,我们定义为单个特征可以提升到的最大幂,我们定义顺序为我们允许相互乘积的特征的数量。以下图表显示了左侧阶数为 2,顺序为 2 的所有可能组合,以及右侧阶数为 3,顺序为 3 的所有可能组合:

图 6.1 – 多项式扩展的可能组合(左侧为阶数=2,顺序=2;右侧为阶数=2,顺序=3)

图 6.1 – 多项式扩展的可能组合(左侧为阶数=2,顺序=2;右侧为阶数=3,顺序=3)

你应该只考虑最大阶数为 3,因为,如图所示,即使阶数为 2,这个操作也已经产生了太多的组合。然而,这个自动过程可能比原始的特征产生更好的预测特征。

要尝试这种方法,你可以使用sklearn库中的PolynomialFeatures类(scikit-learn.org/stable/modules/generated/sklearn.preprocessing.PolynomialFeatures.html)。

在考虑了所有这些方法之后,我们可以在我们的数据集中创建新的特征,这些特征可能更容易被我们的机器学习算法处理,并且包含更精确、更具预测性的信息。

接下来,让我们看看一些让我们可以通过转换其值或其表示来改变单个特征的方法。

特征转换

特征转换是关于操纵特征以改变其值或创建相同特征的新表示。以下列表涵盖了我们可以对单个特征执行的转换类型:

  • 离散化:将特征值划分为不同的组或区间以降低复杂性。这可以在数值或分类特征上完成。

  • 拆分:将特征拆分为多个元素。这通常是在日期时间和字符串值上进行的。

  • 分类编码:通过创建新的数值特征并遵循特定方法来数值化分类特征。

  • 缩放:将连续特征转换为一个适合特定值范围的值。

  • 标准化:将连续特征转换为一个具有均值为 0 和标准差为 1 的正态分布。

  • 归一化:将多个连续特征的向量(行)分别转换为一个所谓的单位范数(单位大小)。

  • squaresquare rootexplog等。

第五章,“执行数据分析与可视化”中,我们使用了log函数来计算所有房价值的对数。我们这样做是为了减少少数异常值对我们机器学习训练的影响。因此,转换特征的主要原因是使特征适应给定机器学习算法的可能数学要求。通常,你可能会遇到以下机器学习算法的要求:

  • 数值格式:算法要求所有特征都是数值的。

  • 相同尺度:算法要求所有预测特征都在相同的尺度上,甚至可能具有均值为 0 和标准差为 1。

  • 数学理论:域本身可能需要根据数学理论进行某些转换。例如,对于涉及经济理论的预测,价格特征几乎总是需要用自然对数进行转换。

  • [-1,1].

  • 复杂性:大多数算法都需要非常精确的特征。因此,降低特征可能取值的复杂性通常是有价值的。

例如,离散化特征。其中一种方法称为分箱,它将数值连续值转换为少量离散值。我们将在第七章,“使用 NLP 的高级特征提取”中看到这一方法的应用。

另一个例子是将日期时间特征分割。想象一下,我们想要预测一天中特定时间某条道路上的交通量。假设我们得到了一个表示我们记录的日期和时间以及在那个点追踪的汽车数量的特征。为了做出更好的预测,一个想法是创建三个新的特征,表示是否是工作日周末假日。与工作日上午 7 点相比,星期天上午 7 点的交通量会更少。

让我们学习如何执行这种转换。以下截图显示了我们的初始小型数据库和添加星期几的第一个转换:

图 6.2 – 包含新工作日特征的数据库

图 6.2 – 包含新工作日特征的数据库

在下一步中,我们必须通过添加一个名为daytype的新分类特征来丰富数据,该特征表示一天是工作日、周末还是假日:

图 6.3 – 数据库丰富化

图 6.3 – 数据库丰富化

理论上,我们已经完成了。但我们的机器学习算法可能在这里有不同的看法。我们的机器学习模型可能会为我们的分类数据创建一个不存在的自然顺序,或者它简单地无法处理分类数据。在这种情况下,明智的做法是将我们的分类数据用数值进行编码。一种这样的方法称为独热编码,它通过为每个现有类别创建一个具有两个有效值(0 或 1)的新特征,将分类特征转换为多个数值特征。以下截图显示了我们对示例的这种编码:

图 6.4 – 对新特征进行独热编码

图 6.4 – 对新特征进行独热编码

在这里,我们创建了三个新的特征,分别命名为holidayweekdayweekend,每个特征代表我们的初始类别。如果一个样本具有这个初始类别,那么该特征的值设置为1;否则,设置为0

在这个例子中,我们做了什么?我们通过分割特征,添加外部知识通过特征创建,并在创建的特征上执行分类编码,将一个非常不直观的日期时间特征转换成具有更多预测力的特征。

现在我们已经很好地掌握了特征转换,让我们看看什么是特征提取的范畴。

特征提取

通过特征提取,我们将所有不通过简单手段操纵特征但能从高维数据集中提取有用信息的方法分组在一起。这通常是通过使用复杂的数学算法或机器学习算法来完成的。

当底层数据集过于复杂而难以处理时,通常需要提取,同时保持其预测价值,将其转化为简化的形式。

以下是一些不同场景下的典型提取类型:

  • 高维降维:基于 n 维数据集创建代表性特征。

  • 特征检测:在图像数据集中的每张图像中找到感兴趣点。

  • 词嵌入:为文本数据集中的单词创建数值编码。

  • 信号处理:从音频数据集中提取声音波的特征。

我们在第五章“执行数据分析和可视化”中讨论了高维降维方法,当时我们探讨了可视化高维数据集。在主成分分析(PCA)这样的过程中,数据集通过创建主成分向量被投影到二维或三维空间。我们不仅可以使用这种方法进行可视化,还可以使用这些计算向量作为派生和更简单的特征,这些特征代表我们的数据集。

重要提示

高维降维技术可用于特征提取,但请注意,我们失去了对特征的内禀理解。我们最终得到的不是称为郊区或房间的特征,而是称为主成分 1 和主成分 2 的特征。

观察其他场景,似乎提取通常发生在我们处理由文本、图像或音频数据组成的复杂数据集时。在这些所有情况下,当我们从原始数据中提取信息时,都有特定的方法需要考虑。

在图像数据集的情况下,我们可能对关键区域或感兴趣点感兴趣,包括寻找边缘和对象。在第十章“在 Azure 上训练深度神经网络”中,你会看到这样的图像提取步骤是由深度神经网络自动完成的,从而消除了在许多情况下对图像进行手动特征提取的需要。

在文本数据的情况下,我们可以使用诸如词袋模型TF-IDF之类的提取方法,这两种方法都有助于创建文本的数值表示,捕捉意义和语义关系。我们将在第七章“使用 NLP 的高级特征提取”中深入探讨这些方法。

在音频数据的情况下,我们可以使用信号处理从源数据中提取信息和新的特征。在这种情况下,也存在两个领域——时域和频域——我们可以从中提取信息。从时域来看,我们通常会提取诸如幅度包络这样的内容,它是每帧信号的峰值幅度,均方根能量,它暗示了信号的响度,以及过零率,即波穿越水平时间轴的次数。如果你必须处理来自这个领域的数据,请让自己熟悉这样的处理技术。

重要提示

许多特征提取和特征转换技术已经嵌入到常见的机器学习框架和算法中,无需您手动触摸特征。通过理解算法本身做什么以及您在预处理时需要手动做什么,来获得良好的理解。

到目前为止,我们已经学习了如何创建新特征、转换特征以及从我们的数据集中提取特征。现在,让我们看看一些可以帮助我们从特征集中选择最具预测性的特征的方法。

特征选择

通过特征选择,我们定义了所有帮助我们理解特征对目标有价值性和预测性的方法,以便我们可以选择有用的特征变量子集进行训练。减少复杂性的原因有两个。一方面,我们希望简单性使模型可解释;另一方面,我们希望避免模型过拟合。当输入信息过多时,我们最终会得到一个模型,在大多数情况下,这个模型会完美地拟合我们的训练数据,但除了这些之外,它在未见过的数据上的表现会很差。

通常,有三种不同类型的特征选择方法,如下所示:

  • 基于过滤的方法:这些方法定义了一个派生指标,即不是目标错误率,来衡量特征子集的质量。

  • 基于包装的方法:这些方法使用贪婪搜索算法在不同的特征子集组合上运行预测模型。

  • 嵌入式方法:这些是已经嵌入到我们最终机器学习模型中的特定选择方法。

基于过滤的方法在计算资源方面可以非常高效,但仅与一个更简单的过滤方法进行评估。通常,这些方法中使用统计指标,如相关性、互信息和熵作为度量标准。

另一方面,基于包装的方法计算密集。同时,它们可以找到性能极佳的特征集,因为用于特征选择的错误函数或指标与实际模型训练中使用的相同。这种方法的不利之处在于,如果没有独立的指标,选定的子集仅对所选的机器学习训练算法有用。通常,这是通过执行以下过程之一来完成的:

  • 逐步前进特征选择:根据每个特征的训练结果逐个添加特征,直到模型不再提高其性能。

  • 逐步后退特征选择:使用完整特征集评估模型。然后,这些特征被逐一移除,直到达到预定义的特征数量。这种移除是循环进行的。

  • 穷举特征选择:评估所有特征子集,这是最昂贵的方法。

最后,当选择步骤是模型学习算法本身的一部分时,选择方法被称为嵌入式方法。嵌入式方法通常通过学习算法利用其选择过程,同时进行选择和训练,从而结合过滤器和包装方法的特性。嵌入式方法的典型例子是集成模型、LassoRidge

你可能现在已经意识到了,我们在第五章执行数据分析与可视化中使用了这样的方法。我们用于生成相关矩阵的皮尔逊相关系数是一个派生指标,因此它属于基于过滤器的选择方法。此外,我们还使用了一个集成决策树模型来计算数据集的特征重要性。这有助于我们清楚地了解哪些特征可能比其他特征对目标有更大的影响。这种集成方法利用了随机森林方法。随机森林不仅实现了所谓的袋装技术,随机选择样本子集进行训练,而且还随机选择特征,而不是使用所有特征来生长每一棵树。因此,对于特征选择,随机森林属于嵌入式类别。

我们将在第九章使用 Azure 机器学习构建 ML 模型中更详细地查看基于树的集成分类器,以及袋装和提升。

除了所有这些特征选择的数学方法之外,有时,更手动的方法可能更优越。例如,当我们从第五章执行数据分析与可视化中的墨尔本住房数据集中删除邮政编码时,我们这样做是因为我们理解邮政编码和郊区包含相同的信息,这使得它们是冗余的。我们这样做是因为我们具有领域知识,并了解邮政编码和郊区之间的关系。请注意,这种额外的知识减轻了模型自己学习这些联系的压力。

重要提示

对于特征工程,对数据或领域了解的更多外部知识,可以使许多预处理步骤变得更加简单,或者完全避免。

我们将在本书中反复阐述这一概念,因为它需要融入你做的每一件事,以便你更高效、更擅长处理数据。

我们现在对可以执行的一般特征工程类型有了总体了解。在下一节中,我们将概述最显著的方法,并深入探讨其中的一些方法。

发现特征转换和提取方法

现在我们已经很好地掌握了我们可以应用于特征的特征工程动作类型,让我们来看看一些最突出的特征工程技术和它们的名称。以下表格提供了我们所学不同类别中大多数已知方法的良好概述:

图 6.5 – 不同特征工程方法的概述

图 6.5 – 不同特征工程方法的概述

请记住,这个列表远非详尽无遗,正如我们之前提到的,其中一些方法已经作为特定机器学习算法的一部分得到实现。

在接下来的章节中,我们将探讨其中的一些。您可以自由下载 GitHub 仓库中该章节的01_feateng_examples.ipynb文件,其中包含即将到来的示例的代码。如果您想了解更多关于我们将要介绍的一些特征提取方法,我们将在接下来的章节中回到它们。对于我们将不介绍的方法,请自由研究它们。

缩放、标准化和归一化

由于所有缩放和归一化方法彼此之间非常相似,我们在这里将详细讨论它们。

让我们从所谓的StandardScaler开始。这种缩放将我们的特征值转换,使得结果值分布的均值(µ)为 0,标准差(s)为 1。应用于每个值的公式看起来如下:

在这里,µ是给定分布的均值,s 是给定分布的标准差。有了这个,我们可以将每个值,,转换成一个新的缩放值,

下图展示了该缩放器如何改变多个分布的形状:

图 6.6 – StandardScaler 分布(左:缩放前,右:缩放后)

图 6.6 – StandardScaler 分布(左:缩放前,右:缩放后)

只有当底层分布是正态分布时,才应使用此缩放器,因为这符合要求。

接下来,我们将探讨MinMaxScaler。这种缩放方法与标准化非常相似,只是我们不是在处理值分布的均值或标准差;相反,我们将值缩放到[0,1]或[-1,1](如果存在负值)的范围内。以这种方式缩放特征通常会提高机器学习算法的性能,因为它们通常更擅长处理小规模值。

从数学上讲,这种缩放定义为以下:

在这里,定义了初始分布的最小值,而定义了初始分布的最大值。

如果最小值和最大值定义良好,MinMaxScaler 是一个不错的选择 – 想想 RGB 图片中的颜色强度。此外,我们可以改变公式以影响结果的值范围。

重要提示

StandardScaler 和 MinMaxScaler 都对分布中的异常值非常敏感,这反过来又可能扭曲某些机器学习算法。

许多机器学习算法更关注大值,因此它们存在异常值的问题。为了解决这个问题,定义了一个名为RobustScaler的缩放器。这个缩放器使用四分位距IQR)而不是标准差作为离散度的度量,并使用分布的中位数而不是平均值作为集中趋势的度量。四分位距表示分布中间的 50%,这意味着它是第 75 百分位数和第 25 百分位数之间的差值。

因此,数学缩放函数看起来是这样的:

在这里,表示分布的中位数,表示第一四分位数开始的位置,表示第三四分位数开始的位置。

为什么这个缩放器对异常值更有效?

在前面的公式中,最大的异常值仍然会落在预定义的区间内,因为最大的异常值会是。因此,异常值离数据点群越远,中心值缩向 0 的程度就越大。另一方面,使用 RobustScaler,中间 50%的所有数据点都会缩放到单位距离,而高于或低于这个值的数据点会被缩放到主要区间之外适当的值,同时保持分布中间值之间的相对距离不变。

简而言之,中位数和四分位距受异常值的影响不大,因此这个缩放器受异常值的影响也不大。

让我们看看这些缩放器在一个样本分布上的表现。为此,我们将取Price列的Price列和应用我们讨论的每种缩放方法得到的分布:

图 6.7 – 使用多种缩放方法缩放的分布

图 6.7 – 使用多种缩放方法缩放的分布

如我们所见,StandardScaler创建了一个均值为 0、标准差为 1 的分布,MinMaxScaler将值缩放到 0 到 1 之间,而RobustScaler将均值设置为 0。查看图 6.8图 6.9中的箱线图,我们可以看到它们分布的差异。请注意y轴的刻度:

图 6.8 – StandardScaler 和 RobustScaler 的箱线图

图 6.8 – StandardScaler 和 RobustScaler 的箱线图

将下面的箱线图与图 6.8进行比较,我们可以看到它们分布的差异:

图 6.9 – MinMaxScaler 的箱线图

图 6.9 – MinMaxScaler 的箱线图

现在我们已经对如何缩放一个特征有了些了解,让我们来谈谈归一化。

归一化是将特征值向量(行)缩放到单位模长的过程,通常是为了简化如余弦相似度这样的数学过程。

让我们先了解一个可以从中受益的归一化步骤。余弦相似度描述了两个不同向量之间的相似程度。在一个 n 维空间中,它们是否指向同一方向,是否相互垂直,或者是否面向相反方向?

例如,这样的计算可以帮助我们理解文本文档之间的相似性,通过取词频向量或类似信息并比较它们来实现。

因此,为了理解文档相似性,我们必须使用以下公式计算向量之间的余弦值:

公式 _06_12.png

如您所见,为了进行这个计算,我们必须计算每个向量的模——例如,公式 _06_13.png。这个模定义为以下内容:

公式 _06_14.png

这个单独的向量模长计算相当昂贵。现在,假设我们有一个包含数十万个文档的数据集。我们每次都必须为数据集中每个向量的组合(样本)计算这个值。如果所有这些向量模长都等于 1,不是会更容易吗?这将极大地简化余弦的计算。

因此,我们的想法是通过适当缩放所有样本,将数据集中的所有样本归一化到单位模长,如下所示:

公式 _06_15.png

在这个方程中,公式 _06_16.png表示我们的初始向量,公式 _06_17.png表示初始向量的模,公式 _06_18.png表示我们缩放到单位模长的缩放向量。

这种归一化称为L2 范数,是三种典型归一化方法之一。让我们看看在这个以及其他所有度量中如何计算向量的模:

  • L1 范数:这个计算将向量的模定义为向量各分量绝对值的和。

  • L2 范数:这个计算的是传统的向量模长(如上所述)。

  • 最大范数:这个计算的是向量的元素绝对值的模。

L1 范数和最大范数不能用于余弦相似度,因为它们没有计算数学上定义的向量模。所以,让我们看看这两个是如何计算的。

L1 范数在数学上定义为以下内容:

公式 _06_19.png

L1 范数常用于在拟合机器学习算法时正则化数据集中的值。它保持系数较小,这使得模型训练过程更简单。

最大范数在数学上定义为以下内容:

公式 _06_20.png

最大范数也用于正则化,通常在神经网络中用于保持神经元之间连接的权重低,这也有助于执行更少的极端反向传播运行以稳定机器学习算法的学习。

到目前为止,你应该已经很好地掌握了缩放和归一化的有用性。接下来,我们将探讨一些可以将分类值转换为数值表示的方法。

分类编码

当我们将特征转换作为一个概念来考虑时,我们查看了一个应用了独热编码的例子。这种方法为初始分类特征中的每个可用类别创建具有两个可能值(0,1)的新特征。这可能很有帮助,但高基数分类特征会极大地膨胀特征空间。因此,在使用这种方法时,我们必须弄清楚每个类别是否具有预测性。

在我们之前的例子中,我们不是使用一周中每天(周一至周六)的类别,而是选择了只有三个类别,即工作日、周末和假日。在这种情况下,独热编码非常有帮助。

除了这种方法之外,还有其他方法可以编码分类特征。其中最基本的方法是标签编码。在标签编码中,我们将每个类别替换为一个数值标签(0,..,n),从而使其成为一个数值特征。通过这种方式,我们没有向这个特征添加任何额外的信息。

接下来的想法是将整个数据集的一些内在信息添加到我们必须编码的值中,并使其融入其中。这个想法的一些选项如下:

  • 计数编码:将每个类别替换为整个数据集中该类别观察值的绝对数量。

  • 频率编码:将每个类别替换为整个数据集中该类别观察值的相对数量(百分比)。

  • 目标编码:将每个类别替换为从整个数据集中该类别的每个条目计算出的目标平均值。

为了理解这些方法,让我们假设我们有一个包含 25 个人的最爱零食项作为特征之一,以及他们购买公司生产的新零食产品的可能性的数据集。以下表格显示了原始值和我们所讨论的所有三种编码:

图 6.10 – 计数、频率和目标编码示例

图 6.10 – 计数、频率和目标编码示例

使用这些方法,我们可以将额外的信息融入特征中,使机器学习算法更容易理解关系。

最后,让我们谈谈Rare,因此将它们归为一类。这有助于降低整体复杂性,特别是如果Rare类别仍然只是整体类别分布的一小部分时,更应该这样做。你可以将这比作在选举图中将小党派归入其他标签,而主要展示大党派。

到目前为止,你应该对不同的编码技术有了很好的理解。在下一节中,我们将讨论我们如何在真实数据集上尝试这些技术。

在表格数据集上测试特征工程技术

第五章《执行数据分析与可视化》中,我们对墨尔本住房数据集进行了一些清理和统计分析。在上一节查看了一系列可能的特征工程方法之后,你可能已经意识到我们在处理数据集时使用了其中的一些方法。

作为练习,思考我们之前停在了哪里,并考虑到特征工程选项,我们现在可以做什么来创建新的有用特征,转换给定的特征,并最终在我们的数据集中选择最突出和最具预测性的特征。

为了获得灵感,请查看 GitHub 仓库中本章的02_fe_melbhousing.ipynb文件。

在本章的最后部分,我们将放下特征空间,专注于我们的机器学习训练的目标或标签——更准确地说,是那些缺少标签的情况。

处理数据标注

在本节中,我们将探讨在为机器学习训练预处理数据集时最耗时且最重要的任务之一:数据标注。正如我们在第一章《理解端到端机器学习流程》中学习到的那样,对于大多数场景,将标签附加到我们的样本上至关重要。正如我们在第五章《执行数据分析与可视化》中查看高维降维和其他机器学习技术时讨论的,在大多数情况下,我们希望使用监督模型,这意味着我们需要标签。

在接下来的几节中,我们将讨论哪些场景需要我们进行手动标注,以及 Azure 机器学习如何帮助我们尽可能高效地完成这项单调的任务。

分析需要标签的场景

我们将首先查看我们迄今为止讨论过的数据集类型,以及我们需要在哪些场景下进行手动标注。

数值数据和分类数据

正如我们在处理墨尔本住房数据集时所见,对于表格数据集,我们可能经常有一个可以用作标签的列。在我们的案例中,我们可以用作标签的是价格列,因为我们的机器学习目标是根据特定的特征输入预测房价。

即使这个列缺失了,我们也可以纳入其他数据集,例如显示墨尔本不同郊区的房屋平均价格的那些数据集,来为我们的数据集样本中的每一个计算一个合理的价值。

因此,与其他我们将讨论的任何场景相比,主要优势在于,在由具有明确意义(不是图像的像素值)的数值和分类特征组成的数据集中,我们可以使用逻辑和数学函数来创建数值标签,或者我们可以自动将样本分类到分类标签。这意味着我们不必手动查看每个样本来定义其标签。

自然语言处理

让我们先看看文本数据。你可能认为分类条目在某种程度上也是文本,但通常,分类数据也可以用数学值交换,而不会损失太多。

另一方面,文本数据表示单词块,例如这本书中的那些,因此它们要复杂得多。看看以下两个句子或话语:

我想预订 2020 年 12 月 23 日从迪拜到巴黎的机票。

房间没有打扫,暖气也不工作。

我们将如何标记这些话语?这非常取决于我们的训练目标。也许我们只想将这些话语分组,例如订单、问候或陈述。在这种情况下,每个话语都会收到一个标签。另一方面,我们可能想要深入挖掘句子中单词的意义。对于我们的第一个话语,我们可能想要理解订单的意义,通过展示可能的航班选项来提供答案。对于第二个话语,我们可能想要理解情感,因为它是对酒店房间质量的陈述。

因此,我们需要在话语本身开始标记单个单词或短语,同时寻找其语义意义。

我们将在第七章中回到这个话题,使用 NLP 的高级特征提取

计算机视觉

当我们谈论图像的机器学习建模时,我们通常试图理解和学习以下之一:

  • 图像分类:将图像分类到一类或多类。典型用例包括图像搜索、图书馆管理和对人的情感分析。

  • 目标检测:在图像中定位特定对象。典型用例包括行人检测、交通流量分析和对象计数。

  • 图像分割:将图像的每个像素分配到特定的区域。典型用例包括自动驾驶汽车的精确环境分析和 X 射线或 MRI 图像中的像素级异常检测。

以下图示展示了这三种类型的示例:

图 6.11 – 不同的图像处理方法

图 6.11 – 不同的图像处理方法

对于这些方法,随着我们向下查看列表,标注的过程变得更加复杂。对于分类,我们只需在图像上放置一个或多个标签。对于目标检测,我们在图像上开始绘制所谓的边界框或多边形。最后,图像分割变得非常复杂,因为我们必须为图像的每个像素分配标签。为此,需要高度专业的工具。

如我们很快将看到的,我们可以使用 Azure Machine Learning Studio 中的数据标注工具来进行分类、目标检测,并在一定程度上进行图像标注任务的分割。

音频标注

最后,让我们来谈谈音频数据的标注。当涉及到音频数据的机器学习建模时,以下场景是可能的:

  • 语音转文本:运行实时转录、语音助手、发音评估和类似解决方案。

  • 语音翻译:将语音翻译为触发应用程序或设备中的操作。

  • 说话人识别:通过声音特征验证和识别说话人。

因此,标注音频数据意味着我们必须从音频文件中提取片段,并相应地标注这些片段。以下图示展示了这个过程的简单示例:

图 6.12 – 音频标注过程

图 6.12 – 音频标注过程

如您所想,这个标注任务也不是非常直接,需要专门的工具。

我们已经看到了很多标注至关重要的场景。现在,让我们尝试自己标注一些图像。

使用 Azure Machine Learning 标注服务进行图像分类的数据标注

在本节中,我们将使用 Azure Machine Learning Studio 中的数据标注服务来标注一些资产。正如我们在第三章,“准备 Azure Machine Learning 工作区”中学习的,导航到 Azure Machine Learning Studio 并在菜单底部点击数据标注,如图下截图所示:

图 6.13 – Azure Machine Learning Studio

图 6.13 – Azure Machine Learning Studio

在下一个屏幕上,点击添加项目,这将带您到以下视图:

图 6.14 – 标注项目创建向导

图 6.14 – 标注项目创建向导

在我们开始练习之前,让我们看看我们可以使用这个服务执行哪些类型的标注任务。如图中所示,我们可以使用图像和文本数据作为数据源。在屏幕上的图像文本选项之间切换,我们有以下选择:

  • 图像分类多类别:给每张图像附加一个标签。

  • 图像分类多标签:给每张图像附加多个标签。

  • 目标检测(边界框):在图像上的一个对象周围绘制一个或多个框。

  • 实例分割(多边形):在图像上的一个对象周围绘制复杂的多边形。

  • 文本分类多类别:给一段文本附加一个标签。

  • 文本分类多标签:给一段文本附加一个或多个标签。

如我们所见,在图像数据方面有很多有用的选项。我们可以通过使用边界框多边形来突出显示和标记图像中的非常具体的部分。使用多边形,您在技术上能够进行完整的图像分割,但使用这个工具将每个像素分配到类别中相当困难。

然而,对于文本数据,有一些限制。我们没有选择在一段文本中标注特定单词或短语,正如我们在上一节中讨论的那样。在撰写本文时,唯一的选择是对文本块进行单标签或多标签。

因此,我们将使用图像。为了不让第一次使用这个工具变得过于复杂,我们将从给图像数据集中的图像附加单个标签开始。在接下来的步骤中,我们将创建一个图像数据集和一个相应的标注项目:

  1. 在通过向导之前,让我们寻找一个合适的图像数据集来使用。我们将使用STL-10 数据集(cs.stanford.edu/~acoates/stl10/)。这个数据集包含大量的小型 96x96 图像,可以分成 10 个类别(飞机汽车鹿猴子卡车)。这 10 个类别将成为我们的标签。由于原始页面只提供给我们二进制格式的图像,我们需要找到不同的来源。在Kaggle上,您经常可以找到这些类型的数据集以不同的格式准备。

  2. 访问www.kaggle.com/jessicali9530/stl10并下载test_images,这是一个包含 8,000 个png格式文件的集合。通常,我们会使用unlabeled_images集合,但由于有 10 万个,我们暂时将其保留。

  3. 如果您还没有这样做,请将本章的文件下载到您的设备上,并在chapter06文件夹下创建一个名为images的新文件夹。

  4. 将所有 8,000 张图片提取到images文件夹中。之后,打开03_reg_unlabeled_data.ipynb文件。在这个文件中,你会发现我们迄今为止用来连接到我们的工作空间和数据存储的代码。请将datastore_name替换为你 ML 工作空间中给出的名称。第一个单元格的最后一段代码如下:

    file_ds = Dataset.File.upload_directory(
                       src_dir='./images',
                       target=DataPath (datastore,
                               'mldata/STL10_unlabelled'),
                       show_progress=True)
    

upload_directory方法将一次性上传images文件夹中的所有文件到你在目标中定义的数据存储位置,并创建一个名为file_ds的文件数据集对象。一旦上传完成,我们可以使用以下代码注册我们的新数据集:

file_ds = file_ds.register(workspace=ws,
                           name='STL10_unlabeled',
                           description='8000 unlabeled 
                           STL-10 images')

如果你导航到 Azure Machine Learning Studio 中的数据集选项卡,你会看到我们新注册的数据集。在探索选项卡下,你会看到图像的子集,包括图像元数据和图像预览。

  1. 现在我们已经注册了我们的数据集,我们可以设置我们的标注项目。回到向导,如图 6.14 所示,将项目名称输入为STL10_Labeling,并选择多类图像分类作为类型。点击下一步

  2. 在下一屏,Microsoft 将提供从Azure Marketplace雇佣劳动力来完成你的标注工作的选项。这将是一个有用的工具,因为你很快就会了解到这项任务有多么繁琐。现在,我们不需要额外的帮助。点击下一步

  3. 现在,我们可以选择要工作的数据集。选择我们新创建的数据集,命名为STL10_unlabeled,然后点击下一步

  4. 我们将看到一个名为增量刷新的选项。此功能如果底层数据集中添加了新图像,则每天更新一次项目。我们目前不打算这样做,所以保持原样并点击下一步

  5. 下一屏要求我们定义我们的标签。标签为飞机汽车鹿猴子卡车。然后,点击下一步

  6. 倒数第二屏允许我们输入标注说明。如果我们不是单独在这个项目上工作,或者我们已经订购了劳动力来完成这项工作,这些说明将很有用。在这里,我们可以给他们下指令。对我们来说,因为我们单独工作,这就不必要了。所以,点击下一步

  7. 最后,我们有选择使用ML 辅助标注的选项。如果我们不激活此选项,我们就必须自己标注所有 8,000 张图片,而不需要帮助。请注意,激活此选项需要运行 GPU 计算集群,每次辅助 ML 模型重新训练时都会运行几分钟。我们将选择使用默认选项,这将为我们创建一个合适的集群。点击创建项目。这将带我们回到概览页。当集群创建完成后,点击项目的名称以获取概览页面。

你将看到一个类似于以下仪表板的界面:

图 6.15 – 标注项目的仪表板

图 6.15 – 标注项目的仪表板

仪表板分为以下视图:

  • 进度:这显示了正在标注的资产数量。在我们的案例中,我们正在处理 8,000 张图像。它还显示了每个资产的状态(完成跳过需要审查不完整)。

  • 标签类别分布:此视图将显示一个条形图,显示哪些标签被使用以及分类图像的次数。

  • 标注员性能:此视图显示每个标注员处理了多少资产。在我们的案例中,只会显示我们的名字。

  • 任务队列:此视图显示管道中的任务。目前,我们需要在下一个训练阶段或下一次检查之前手动标注 150 张图像。

  • 机器学习辅助标注实验:此视图显示了辅助机器学习模型的运行或已运行的训练实验。

如果你切换到数据标签页,你会看到一些图像预览,你可以查看已经标注的图像。当你在一个团队中工作时,这很有帮助,因为一些人正在标注图像,而另一些人正在审查他们的标注工作。

最后,如果你查看DefLabelNC6

以下截图显示了此集群的概览页面:

图 6.16 – 标注集群仪表板

图 6.16 – 标注集群仪表板

如你所见,用于节点的机器具有 6 个核心,56 GB 的 RAM 和一个 Tesla K80 GPU。在 Azure 上创建任何类型的计算实例时,请始终检查定价页面(azure.microsoft.com/en-us/pricing/details/virtual-machines/ml-server-ubuntu/)。如该页面所示,我们使用的节点称为NC6,每小时大约花费 3 美元。集群节点显示集群是空闲的,因此没有费用。稍后,你可以检查运行标签页以了解训练运行的持续时间,从而了解定价影响。目前,一个合理的估计是,我们将在我们的标注项目中需要 2 到 4 小时的机器学习辅助支持。

因此,在我们开始标注图像之前,让我们了解机器学习辅助标注能做什么。当你切换回我们的标注项目仪表板时,你会在任务队列下看到三个选项,如下所示:

  • 手动:这表示在任何时候都必须处理的资产,没有任何支持。

  • 集群:这表示在已经标注的资产上使用了聚类模型。当你处理这些资产时,它们将以模型认为属于同一类的图像组的形式显示给你。

  • 预标注:这表示在已经标注的资产上训练了分类模型的资产。在这种情况下,它为未标注的资产预测了标签。当你处理这些图像时,你会看到建议的标签并需要检查模型是否正确。

现在,让我们开始标记。当您点击标记数据时,您将看到以下视图:

图 6.17 – 标记任务视图

图 6.17 – 标记任务视图

从这个视图,您可以看到中间的资产。通过顶部的控件,您可以放大并更改图像的亮度对比度属性。如果您对这些选项不确定,您可以暂时选择跳过。在右侧,您可以选择适当的标签。如果您对您的选择满意,您可以点击提交

对几幅图像进行标记,以便掌握情况。之后,查看右上角的控件。在这里,我们可以更改同时显示给我们多少资产(1、4、6 或 9)。我建议同时显示 6 个资产。此外,为了标记图片,您可以多选它们,并使用键盘上的数字 1 到 9(如前一张截图所示)来更快地进行标记。

现在,为了看到机器学习辅助标记的触发,您需要手动标记大约 400 到 600 张图像。您可以决定这是否是您时间的良好利用,但这是一个很好的练习,因为它让您了解了这项任务的繁琐程度。

最终,训练将被触发,如下面的截图所示:

图 6.18 – 触发的标记训练运行

图 6.18 – 触发的标记训练运行

在第一次标记训练触发之前,我不得不手动标记 616 个资产。正如我们所见,该工具显示了在标记过程中遇到的标签类别的分布。与其他任何训练一样,这创建了一个带有运行的实验。您可以在 ML 工作区的“实验”下找到这些,如下面的截图所示:

图 6.19 – 使用机器学习辅助标记的实验运行

图 6.19 – 使用机器学习辅助标记的实验运行

在这一点上,只需继续标记资产。最终,您将看到由页面顶部的聚类任务定义的聚类图像(参见图 6.20):

图 6.20 – 显示聚类图像的数据标记

图 6.20 – 显示聚类图像的数据标记

或者,您将看到预先标记的图像,这些图像由页面顶部的预标记任务定义(参见图 6.21):

图 6.21 – 显示预标记图像的数据标记

图 6.21 – 显示预标记图像的数据标记

通过以上内容,您已经了解了如何利用机器学习建模来标记您的资产,以及 Azure 机器学习工作室如何使这一过程更加简便。正如您现在应该理解的那样,这是一项耗时的工作,但如果您希望在未来的机器学习训练中取得更好的结果,这项工作必须完成。

摘要

在本章中,我们探讨了如何通过特征工程来准备我们的特征,以及如何通过标记来准备我们的标签。

在第一部分,我们了解到特征工程包括创建新的和缺失的特征、转换现有特征、从高维数据集中提取特征,以及使用方法来选择对机器学习训练最有预测性的特征。

在第二部分,我们了解到标记是必不可少的且繁琐的。因此,像 Azure Machine Learning 数据标记这样的工具可以是一种祝福,可以减轻这项耗时的工作。

本章的关键要点是,创建、转换和选择预测性特征对机器学习模型的质量影响最大。在机器学习管道中的其他任何步骤都不会对其结果产生更大的影响。

要完成高质量的特征工程,你必须对领域有深入了解(或者你必须认识一个有这种知识的人),并且清楚地掌握所选机器学习算法的内部工作方式。这包括理解数学理论、算法期望作为输入的数据结构,以及当你拟合模型时自动应用的特征工程方法。

在下一章中,我们将看到特征工程的实际应用。我们将探讨如何对文本数据进行特征提取,以用于自然语言处理。