原文:
annas-archive.org/md5/41ac68e0c7679ad41531572416d7e2b8
译者:飞龙
第十一章:第十一章:使用管道
在本章中,您将学习如何创建可重复使用的过程,定义由多个步骤组成的管道。您可以使用这些管道来创建训练管道,转换数据并训练模型,或者使用它们执行批量推断,使用预训练的模型。一旦注册了这些管道,您可以通过 HTTP 端点或 SDK 调用它们,甚至可以将其配置为按计划执行。掌握这些知识后,您将能够通过使用 Azure 机器学习(AzureML)SDK 来实现和使用管道。
在本章中,我们将涵盖以下主要主题:
-
了解 AzureML 管道
-
创建管道
-
发布管道以将其暴露为端点
-
安排定期执行管道
技术要求
您需要拥有一个 Azure 订阅。在该订阅中,您需要一个 packt-azureml-rg
。您需要拥有 Contributor
或 Owner
角色的 packt-learning-mlw
,如在 第二章 中的 部署 Azure 机器学习工作区资源 所描述的。
您还需要具备 Python 语言的基本知识。代码片段针对的是 Python 3.6 或更高版本。您还应该熟悉在 AzureML studio 中使用笔记本体验,这一点在 第八章 中的 Python 代码实验 部分有涉及。
本章假设您已经注册了在 第十章 中生成的 loans 数据集,理解模型结果。同时假设您已经创建了名为 cpu-sm-cluster 的计算集群,如在 第七章 中的 使用计算目标 部分所描述的那样,AzureML Python SDK。
您可以在 GitHub 上找到本章的所有笔记本和代码片段,网址如下:bit.ly/dp100-ch11
。
了解 AzureML 管道
在 第六章 中,可视化模型训练与发布,您已经看到如何使用构建块设计训练过程。类似于这些工作流,AzureML SDK 允许您创建 Pipelines
来协调多个步骤。例如,在本章中,您将创建一个由两步组成的 Pipeline
。第一步对 Environment
进行预处理。
重要提示
不要将 Pipelines
与您在 第十章 中阅读的 Pipelines
混淆,理解模型结果。Pipelines
是围绕您要训练并用于推断的实际模型类的包装器。
AzureML SDK 提供了许多构建模块,你可以使用它们来构建Pipeline
。图 11.1包含了你可能在考试和实际代码中遇到的最常用的类:
图 11.1 – 用于编写管道的 AzureML SDK 中的可用类
Pipeline是定义工作流的核心类,它将多个步骤串联在一起。你可以通过使用PipelineParameter类来定义管道参数,并将它们传递给管道。这些参数可以在Pipeline中的一个或多个步骤中引用。一旦定义了管道,你可以将其发布以将其注册到 AzureML 工作区,作为一个版本化对象,并可以通过PublishedPipeline类进行引用。此已发布的管道具有一个端点,你可以使用它来触发其执行。如果需要,你可以定义一个Schedule,并让这个PublishedPipeline类在特定时间间隔触发执行。PipelineData定义了临时存储,允许一个步骤将一些文件放入其中,供下一个步骤使用。这两个步骤之间的数据依赖关系在Pipeline中创建了一个隐式执行顺序,意味着依赖步骤将在第一个步骤完成后等待执行。你将在本章中使用所有这些类。
在azureml.pipeline.steps模块中,你将找到所有可用的步骤。最常用的步骤如下:
-
PythonScriptStep:此步骤允许你执行 Python 脚本。你将在本章中使用此步骤。
-
你在第九章中看到的
AutoMLConfig
对象,优化机器学习模型。 -
你在第九章中看到的
HyperDriveConfig
参数,优化机器学习模型。 -
DataTransferStep:这是一个Pipeline步骤,允许你在 AzureML 支持的存储选项之间传输数据。
-
DatabricksStep:此步骤允许你在附加的 DataBricks 集群中执行 DataBricks 笔记本、Python 脚本或 JAR 文件。
-
Estimator
类代表一个通用的训练脚本。一些框架特定的估算器从这个通用的Estimator
类继承,例如TensorFlow
和PyTorch
。要将这些估算器之一纳入你的管道中,你会使用EstimatorStep
。整个Estimator
类及其派生类已被弃用,取而代之的是ScriptRunConfig
,你在前几章中已使用过。如果在考试中看到一个已弃用的EstimatorStep
引用,你可以将其视为PythonScriptStep
。
Pipeline的最后一个重要组成部分是流经其中的数据。
-
(dstore,"/samples/diabetes")
元组用于指示在调用register_pandas_dataframe
方法时想要存储数据的位置,方法适用于TabularDataset
。你也可以使用等效的DataPath(datastore=dstore, path_on_datastore="/samples/diabetes")
来代替这个元组。 -
outputs
文件夹会自动上传到Run
执行中。与该文件夹类似,你可以定义额外的本地文件夹,这些文件夹会自动上传到目标数据存储区中的目标路径。在本章中,你将使用这个类将生成的模型存储在默认 Blob 存储帐户中的特定位置。 -
DataReference 表示数据存储区中的路径,并可用于描述如何以及在哪里在运行时提供数据。它不再是 AzureML 中推荐的数据引用方法。如果你在过时的考试问题中遇到它,你可以将其视为 DataPath 对象。
在这一节中,你了解了可以用来构建 AzureML Pipeline 的构建模块。在下一节中,你将亲自体验使用这些类。
创建管道
假设你需要创建一个可重复的工作流,其中包含两个步骤:
-
它从已注册的数据集中加载数据,并将其拆分为训练数据集和测试数据集。这些数据集被转换成
step01
所需的特殊结构。 -
它加载预处理过的数据,并训练存储在 AzureML 工作区的默认数据存储区
/models/loans/
文件夹中的模型。你将在名为step02
的文件夹中编写此步骤的代码。每个步骤将是一个单独的 Python 文件,接受一些参数来指定从哪里读取数据以及将数据写入何处。这些脚本将利用你在 第八章 中编写的相同机制,使用 Python 代码进行实验。本章不同之处在于,你不会单独调用每个 Python 脚本,而是会创建一个
Pipeline
,依次调用这些步骤。在 图 11.2 中,你可以看到每个脚本将具有的整体输入和输出,以及你需要为每个步骤配置的参数,以便执行:
图 11.2 – 每个管道步骤的输入和输出
根据图 11.2,对于每个步骤,你需要定义用于执行特定 Python 脚本的计算目标和Environment
。虽然每个步骤可以指定一个单独的计算目标和单独的Environment
,但为了简化代码,你将使用相同的Environment
和相同的计算目标来运行这两个步骤。你将使用现成的Environment
,它包含了标准的数据科学包,包括你的脚本所需的LightGBM库。你将在你在第七章中创建的cpu-sm-cluster集群上执行这些步骤,AzureML Python SDK。
你将首先编写Pipeline
,然后编写每个步骤所需的实际 Python 脚本。导航到chapter11
,然后创建一个名为chapter11.ipynb
的笔记本,如图 11.3所示:
图 11.3 – 将 chapter11 笔记本添加到你的工作文件中
打开新创建的笔记本,按照步骤使用 AzureML SDK 编写 AzureML 流水线:
-
你将从获取对工作区的引用开始。然后,你将获取对
loans
数据集和cpu-sm-cluster
的引用。在你的笔记本中的一个单元格里添加以下代码:from azureml.core import Workspace ws = Workspace.from_config() loans_ds = ws.datasets['loans'] compute_target = ws.compute_targets['cpu-sm-cluster']
如果你在理解这段代码时遇到困难,请回顾第七章,AzureML Python SDK。
-
你需要创建一个配置对象,它将在每个步骤执行时决定使用哪个
Environment
。为此,你需要使用以下代码创建一个RunConfiguration
:from azureml.core import RunConfiguration runconfig = RunConfiguration() runconfig.environment = ws.environments['RunConfiguration object, and you assign the predefined Environment to its environment attribute. To help you understand how this RunConfiguration object relates to the work you have been doing in *Chapter 8*, *Experimenting with Python Code*, the ScriptRunConfig you have been using in that chapter had an optional run_config parameter where you could have passed this RunConfiguration object you defined in this cell.
-
接下来,你需要定义一个临时存储文件夹,用于存放第一个步骤的输出文件。你将使用
PipelineData
类,采用以下代码:from azureml.pipeline.core import PipelineData step01_output = PipelineData( "training_data", datastore= ws.get_default_datastore(), is_directory=True)
在这段代码中,你正在创建一个名为
training_data
的中间数据位置,它被存储为一个文件夹,位于你 AzureML 工作区中注册的默认数据存储中。你不需要关心这个临时数据的实际路径,但如果你感兴趣的话,那个文件夹在默认存储容器中的实际路径类似于azureml/{step01_run_id}/training_data
。 -
现在你已经为流水线的第一步准备好所有的前提条件,是时候定义它了。在一个新的单元格中,添加以下代码:
from azureml.pipeline.steps import PythonScriptStep step_01 = PythonScriptStep( 'prepare_data.py', source_directory='step01', arguments = [ "--dataset", loans_ds.as_named_input('loans'), "--output-path", step01_output], name='Prepare data', runconfig=runconfig, compute_target=compute_target, outputs=[step01_output], allow_reuse=True )
这段代码定义了一个
PythonScriptStep
,它将使用step01
文件夹中的源代码。它将执行名为prepare_data.py
的脚本,并传递以下参数:-
--dataset
: 此参数将loans_ds
数据集 ID 传递给该变量。此数据集 ID 是一个唯一的as_named_input
方法。此方法在FileDataset
和TabularDataset
中均可用,并且仅适用于在 AzureML 工作区内执行Run
时。要调用该方法,必须提供一个名称,本例中为loans
,这个名称可以在脚本内部用于检索数据集。AzureML SDK 将在prepare_data.py
脚本中的run
对象的input_datasets
字典中提供TabularDataset
对象的引用。在prepare_data.py
脚本中,您可以使用以下代码获取对该数据集的引用:run = Run.get_context() loans_dataset = run.input_datasets["loans"]
-
--output-path
: 此参数传递了您在步骤 3中创建的PipelineData
对象。该参数将是一个表示脚本可以存储其输出文件的路径字符串。数据存储位置挂载到即将执行特定步骤的计算节点的本地存储上。这个挂载路径被传递给脚本,使您的脚本能够直接将输出透明地写入数据存储。回到您传递给
PythonScriptStep
初始化的参数,您定义了一个名称,该名称将在图 11.6中可见的管道的可视表示中显示。在runconfig
参数中,您传递了在步骤 2中定义的RunConfiguration
对象。在compute_target
参数中,您传递了指向您在步骤 1中获取的cpu-sm-cluster
群集的引用。在
outputs
参数中,您传递了一个输出数组,该数组将用于此步骤将要发布的数据。这是一个非常重要的参数,用于定义管道中步骤的正确执行顺序。尽管您将PipelineData
对象作为参数传递给脚本,但 AzureML SDK 不知道您的脚本是否将从该位置写入或读取数据。通过显式将PipelineData
对象添加到outputs
参数中,您将此步骤标记为数据存储在PipelineData
对象中的生产者。因此,任何在相应inputs
参数中引用相同对象的人都需要在此PythonScriptStep
之后执行。allow_reuse
布尔参数允许你在脚本的输入和step01
文件夹中的源代码自上次执行管道以来没有更改的情况下重用此PythonScriptStep
的输出。由于此步骤的唯一输入是特定版本的TabularDataset
,它是不能更改的。尽管你在引用TabularDataset
时未指定特定版本,但系统会自动选择最新版本。此版本在管道创建时被固定到管道定义中。即使你创建了TabularDataset
的新版本,管道也会继续在固定的版本上执行。此外,由于allow_reuse
参数设置为True
,此步骤将只执行一次,从那时起,结果将自动重用。在本节的末尾,你将看到这如何影响重新运行相同管道时的管道执行时间。重要提示
如果你想强制管道读取
loans_ds
变量的最新版本,它将引用TabularDataset
的最新版本。在本节的末尾,你还将学习如何将训练数据集作为PipelineParameter
传递。
-
-
现在你已经定义了
PythonScriptStep
,是时候将缺失的 Python 脚本添加到你的文件中了。在你当前工作的chapter11
文件夹下,与你的笔记本并列,添加一个名为step01
的新文件夹。在该文件夹内,添加一个名为prepare_data.py
的新 Python 脚本文件。最终的文件夹结构应类似于图 11.4所示:https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/az-ds-ass-cert-gd/img/B16777_11_004.jpg图 11.4 – 准备数据脚本 prepare_data.py 的文件夹结构,该脚本将在你的管道中执行
-
在
prepare_data.py
文件中添加以下代码块。你可以直接从本章的技术要求部分提到的 GitHub 仓库下载这些代码,而无需手动输入所有代码:import argparse from azureml.core.run import Run from sklearn.model_selection import train_test_split import lightgbm as lgb import os
这些是你在脚本文件中需要的所有导入。你将使用
train_test_split
方法为lightgbm
库(使用lgb
作为短别名)创建训练和测试数据集:parser = argparse.ArgumentParser() parser.add_argument("--dataset", type=str, dest="dataset") parser.add_argument("--output-path", type=str, dest="output_path", help="Directory to store datasets") args = parser.parse_args()
该脚本创建了一个
ArgumentParser
,用于解析你在步骤 4中定义PythonScriptStep
时传递的参数。提醒一下,--dataset
参数将包含脚本需要处理的数据集 ID,而--output-path
参数将是脚本应该写入转换后的数据集的本地路径位置:run = Run.get_context() loans_dataset = run.input_datasets["loans"]
在解析完参数后,你会获得对
Run
上下文的引用。之后,你会得到对loans
数据集的引用,这一点是因为你调用了as_named_input
方法,正如第 4 步中所讨论的那样。在本节后续部分,你将了解如何重写这段代码,以便能够在没有Run
上下文的本地计算机上运行相同的脚本:print(f"Dataset id: {loans_dataset.id}")
这段代码打印了你的代码选择为参考的数据集的 ID。如果你打印传递给
--dataset
参数的 ID,并且它存储在args.dataset
变量中,你会注意到这两个值是相同的:loans_df = loans_dataset.to_pandas_dataframe() x = loans_df[["income", "credit_cards", "age"]] y = loans_df["approved_loan"].values feature_names = x.columns.to_list() x_train, x_test, y_train, y_test = train_test_split( x, y, test_size=0.2, random_state=42, stratify=y )
在这段代码中,你将数据集加载到内存中,并使用
train_test_split
方法将数据集拆分为训练和测试特征(x_train
和x_test
)以及训练和测试标签(y_train
和y_test
):train_data = lgb.Dataset(x_train, label=y_train, feature_name=feature_names) test_data = lgb.Dataset(x_test, label=y_test, reference=train_data)
特征和标签随后被转换为
train_data
和test_data
,它们是Dataset
对象。用于训练和验证的Dataset
格式。请注意,存储在test_data
变量中的验证数据集需要引用训练数据集(train_data
)。这是由output_path
文件夹嵌入的一个安全机制,如果文件夹不存在,它会先创建,然后使用Dataset
的原生save_binary
方法将数据集序列化为二进制文件,优化用于存储和加载。与在第八章中创建的脚本不同,使用 Python 代码实验,
prepare_data.py
文件无法作为_OfflineRun
在本地计算机上执行。这是因为你依赖于input_datasets
字典,而这个字典只有在Run
在 AzureML 工作区内执行时才可用。如果你想在将该文件用于Pipeline
之前在本地测试它,你可以使用以下代码代替:run = Run.get_context() loans_dataset = None if type(run) == _OfflineRun: from azureml.core import Workspace, Dataset ws = Workspace.from_config() if args.dataset in ws.datasets: loans_dataset = ws.datasets[args.dataset] else: loans_dataset = Dataset.get_by_id(ws, args.dataset) else: loans_dataset = run.input_datasets["loans"]
这段代码检查是否是离线运行。如果是,它首先获取对工作区的引用,正如你在第八章中所看到的那样,使用 Python 代码实验,然后检查存储在
args.dataset
变量中的--dataset
参数是否是数据集名称。如果是,最新版本的数据集会被分配给loans_dataset
变量。如果不是名称,脚本会认为它是 GUID,这应该表示特定数据集版本的 ID。在这种情况下,脚本会尝试使用get_by_id
方法来检索特定数据集,或者如果传递的值不是已知的数据集 ID,则抛出错误。如果是在线运行,你仍然可以使用input_datasets
字典来检索数据集引用。 -
回到你的笔记本,你将开始定义第二步的前提条件,即
Pipeline
的模型训练阶段。在图 11.2中,你看到此步骤需要一个名为learning_rate
的参数。你将不再将PipelineParameter
的学习率超参数硬编码到代码中,而是将在Pipeline
级别定义此参数,并将其作为参数传递给训练脚本,正如你在步骤 9中将看到的那样。要创建这样的参数,请使用以下代码:from azureml.pipeline.core import PipelineParameter learning_rate_param = PipelineParameter( name="learning_rate", default_value=0.05)
这段代码定义了一个名为
learning_rate
的新PipelineParameter
。默认值为0.05
,这意味着在执行管道时可以省略传递此参数,系统将使用默认值。稍后在步骤 13中,你将看到如何执行Pipeline
并指定一个不同于默认值的值。 -
你将把训练好的模型存储在附加到 AzureML 工作区的默认数据存储的
/models/loans/
文件夹中。要指定你希望存储文件的确切位置,你将使用OutputFileDatasetConfig
类。在一个新的笔记本单元格中,添加以下代码:from azureml.data import OutputFileDatasetConfig datastore = ws.get_default_datastore() step02_output = OutputFileDatasetConfig( name = "model_store", destination = (datastore, '/models/loans/'))
在此脚本中,你首先获取对默认数据存储的引用。然后,创建一个
OutputFileDatasetConfig
对象,并将一个元组传递给destination
参数。这个元组包含了你选择的数据存储和该数据存储中的路径。你本可以选择任何已附加到 AzureML 工作区的数据存储。这个OutputFileDatasetConfig
对象定义了输出的目标位置。如果你没有指定destination
参数,系统将使用默认的/dataset/{run-id}/{output-name}
值。请注意,destination
允许在定义路径时使用占位符。默认值使用了{run-id}
和{output-name}
两个当前支持的占位符。到适当的时刻,这些占位符将被相应的值替换。 -
现在你已经定义了所有前提条件,可以定义
Pipeline
的第二步了。在笔记本的新单元格中,添加以下代码:step_02 = PythonScriptStep( 'train_model.py', source_directory='step02', arguments = [ "--learning-rate", learning_rate_param, "--input-path", step01_output, "--output-path", step02_output], name='Train model', runconfig=runconfig, compute_target=compute_target, inputs=[step01_output], outputs=[step02_output] )
类似于你在步骤 4中创建的
step_01
文件夹;这段代码定义了一个PythonScriptStep
,它将调用位于step02
文件夹中的train_model.py
脚本。它将使用你在步骤 7中定义的PipelineParameter
传递的值来填充--learning-rate
参数。它还会将step_01
的输出传递给--input-path
参数。请注意,step01_output
也被添加到此PythonScriptStep
的输入列表中。这意味着step_02
必须等待step_01
完成,以便消费存储在step01_output
中的数据。最后一个脚本参数是--output-path
,你将在这里传递你在上一步创建的OutputFileDatasetConfig
对象。此对象也被添加到此PythonScriptStep
的输出列表中。 -
让我们创建一个 Python 脚本,该脚本将在
step_02
中执行。在你的笔记本旁边,在你当前工作的chapter11
文件夹下,创建一个名为step02
的新文件夹。在该文件夹内,创建一个名为train_model.py
的 Python 脚本文件。最终的文件夹结构应该类似于图 11.5所示:https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/az-ds-ass-cert-gd/img/B16777_11_005.jpg图 11.5 – 将在管道的 step02 文件夹中执行的训练脚本
-
打开
train_model.py
文件并添加以下代码块。你可以直接从本章技术要求部分提到的 GitHub 仓库下载这些代码,而不是手动输入。import argparse import os import lightgbm as lgb import joblib parser = argparse.ArgumentParser()
这段代码导入了文件中需要的所有模块,并创建了一个
ArgumentParser
来读取你将传递给该脚本的参数。如果你愿意,你也可以使用另一个著名的库来解析脚本参数,称为learning_rate
参数。请注意,这是一个浮动值,默认值与你在步骤 7中定义的值不同,这表明这两个默认值不需要相同。在执行管道时,PipelineParameter
将定义实际值:parser.add_argument( "--input-path", type=str, dest="input_path", help="Directory containing the datasets", default="../data", ) parser.add_argument( "--output-path", type=str, dest="output_path", help="Directory to store model", default="./model", ) args = parser.parse_args()
然后你解析
input_path
和output_path
,它们是指向执行此脚本的计算机上本地文件夹的字符串值。最后一行解析传入的参数,并将结果分配给args
变量:print(f"Loading data from {args.input_path}") train_data = lgb.Dataset(os.path.join(args.input_path, "train_dataset.bin")) validation_data = lgb.Dataset(os.path.join(args.input_path, "validation_dataset.bin"))
解析脚本参数后,训练和验证数据集被加载:
param = { "task": "train", "objective": "binary", "metric": "auc", "num_leaves": 5, "learning_rate": args.learning_rate } model = lgb.train( param, train_set = train_data, valid_sets = validation_data, early_stopping_rounds = 5 )
在这段代码块中,配置了一个二分类训练过程,它将使用
auc
指标来评估训练进度。early_stopping_rounds
参数:output_path = args.output_path if not os.path.exists(output_path): os.makedirs(output_path) joblib.dump(value=model, filename=os.path.join(output_path, "model.joblib"))
训练完成后,模型会使用
joblib
库进行序列化,并存储在output_path
文件夹中。 -
回到笔记本,是时候定义你目前为止构建的实际
Pipeline
了。在一个新的单元格中,添加以下代码:from azureml.pipeline.core import Pipeline pipeline = Pipeline(workspace=ws, steps=[step_01, step_02])
你定义一个新的
Pipeline
对象,传入包含你要包括的所有步骤的列表。请注意,步骤的顺序并不重要,因为实际的执行顺序是由你在这两个步骤之间指定的step01_output
PipelineData
依赖关系定义的。 -
要执行管道,你需要将其提交到
Experiment
中。在一个新的笔记本单元格中,添加以下代码:from azureml.core import Experiment experiment = Experiment(ws, "chapter-11-runs") pipeline_run = experiment.submit( pipeline, pipeline_parameters= { "learning_rate" : 0.5 } ) pipeline_run.wait_for_completion()
这段代码定义了一个新的
Experiment
,名为chapter-11-runs
,并提交管道运行,将0.5
的值传递给你在步骤 7中定义的learning_rate
参数。管道执行的第一个输出之一是指向 AzureML 门户的链接。点击该链接将进入管道执行运行页面,如图 11.6所示:
图 11.6 – 本节中你所编写的管道的图形表示
假设你尝试通过第二次执行你在第 13 步中编写的代码来重新运行管道。在这种情况下,你会注意到执行几乎是即时的(相比第一次执行需要的几分钟,这次只需要几秒钟)。管道检测到没有输入发生变化,并重新使用了先前执行步骤的输出。这展示了第 4 步中allow_reuse=True
的作用,并且证明了即使我们在第 9 步中没有指定该参数,默认值为True
。这意味着,默认情况下,如果输入和代码文件与之前的执行相同,所有步骤都会重用先前的执行结果。如果你想强制重新训练,即使传递给管道的learning_rate
变量相同,你可以在第 9 步中指定allow_reuse=False
。
重要提示
如果你想将训练数据集作为PipelineParameter
传递,你需要使用以下代码:
from azureml.data.dataset_consumption_config import DatasetConsumptionConfig
ds_pipeline_param = PipelineParameter(name="dataset ", default_value=loans_ds)
dataset_consumption = DatasetConsumptionConfig("loans", ds_pipeline_param)
使用此代码,并在第 4 步中传递dataset_consumption
对象,而不是loans_ds.as_named_input('loans')
,将允许你在提交管道执行时选择输入数据集及其版本。
到目前为止,你已经定义了一个执行两个 Python 脚本的管道。step_01
预处理训练数据并将其存储在一个中间数据存储中,供step_02
使用。之后,第二步将在附加到 AzureML 工作区的默认数据存储的/models/loans/
文件夹中训练模型。如果你准确地按照步骤操作,管道应该会成功完成。然而,在实际操作中,编码问题常常会出现,管道可能会未能完成。在下一节中,你将学习如何排查潜在的管道运行时问题。
排查代码问题
到目前为止,你的代码运行得非常顺利。如果某个脚本出现了编码问题或缺少依赖项怎么办?在这种情况下,管道将会失败。在你在图 11.6中看到的图形表示中,你将能够识别出失败的步骤。如果你想获取特定子步骤的详细信息,你必须首先使用pipeline_run
对象中的find_step_run
来定位它。你可以在笔记本中的新单元格中添加以下代码:
train_step_run = pipeline_run.find_step_run("Train model")[0]
train_step_run.get_details_with_logs()
这段代码查找所有名称为 0
索引的步骤。这将检索一个 StepRun
对象,该对象用于你在上一节中定义的 step_02
文件夹。StepRun
继承自基类 Run
,暴露了 get_details_with_logs
方法,该方法也可在你在 第八章 中使用的 ScriptRun
类中找到,使用 Python 代码进行实验。这个方法在排查依赖关系或脚本代码的潜在问题时非常有用。它提供了关于脚本执行的很多有用信息,包括日志文件。
如果你更喜欢 AzureML Studio Web 体验,可以导航到 Pipeline
运行。在管道的图形表示中,选择你想查看日志的步骤。在 输出 + 日志 标签页中查看日志,如 图 11.7 所示:
图 11.7 – 在 Web 门户中查看训练模型步骤的日志
到目前为止,你已经学习了如何编写 Pipeline
和如何排查潜在的运行时错误。你创建的 Pipeline
尚未在工作区中注册,接下来你将在下一节进行注册。
将管道发布为端点
到目前为止,你已经使用 AzureML SDK 定义了一个管道。如果你需要重新启动 Jupyter notebook 的内核,你将失去对已定义管道的引用,并且你需要重新运行所有单元格以重新创建管道对象。AzureML SDK 允许你发布管道,从而有效地将其注册为工作区内的版本化对象。一旦管道被发布,就可以在不需要构建它的 Python 代码的情况下提交该管道。
在 notebook 中的新单元格中,添加以下代码:
published_pipeline = pipeline.publish(
"Loans training pipeline",
description="A pipeline to train a LightGBM model")
这段代码发布管道并返回一个 PublishedPipeline
对象,即在工作区中注册的版本化对象。该对象最有趣的属性是 endpoint
,它返回用于触发特定管道执行的 REST 端点 URL。
要调用已发布的管道,你将需要一个认证头。要获取此安全头,你可以使用 InteractiveLoginAuthentication
类,如以下代码片段所示:
from azureml.core.authentication import InteractiveLoginAuthentication
auth = InteractiveLoginAuthentication()
aad_token = auth.get_authentication_header()
然后,你可以使用 Python requests
包向特定端点发出 POST
请求,使用以下代码:
import requests
response = requests.post(published_pipeline.endpoint,
headers=aad_token,
json={"ExperimentName": "chapter-11-runs",
"ParameterAssignments": {"learning_rate" : 0.02}})
print(f"Made a POST request to {published_pipeline.endpoint} and got {response.status_code}.")
print(f"The portal url for the run is {response.json()['RunUrl']}")
这段代码只需要 URL,而不需要实际的管道代码。如果你丢失了端点 URL,可以通过 PublishedPipeline
类的 list
方法恢复它,该方法列举了工作区中所有已发布的管道。前面的脚本使用 HTTP POST 方法调用 REST
端点,并传递值 0.02
作为 learning_rate
参数。
重要说明
如果你不熟悉 POST
方法(也称为动词),可以在 进一步阅读 部分了解更多信息。
这个 HTTP 请求的结果对象包含有关管道执行的信息,包括RunUrl
,它允许你访问 AzureML Studio 门户以监控管道执行。
当你发布管道时,注册的对象会在 AzureML Studio 门户中可用。如果你导航到终端 | 管道终端,你将看到所有已发布管道终端的列表,如图 11.8所示:
图 11.8 – 已发布管道的终端
一旦你选择了一个管道,你可以通过图形向导来触发它,向导允许你指定管道参数和管道将要执行的实验。
在本节中,你了解了如何发布管道,以便在不需要管道定义代码的情况下重用它。你看到了如何通过REST
终端触发已注册的管道。在下一节中,你将学习如何调度管道以进行每月的再训练。
调度一个重复的管道
通过已发布的REST
终端调用管道是非常棒的,尤其是在你有第三方系统需要在特定事件发生后触发训练过程时。例如,假设你正在使用Azure Data Factory从本地数据库复制数据。你可以使用Machine Learning Execute Pipeline活动,触发一个已发布的管道,如图 11.9所示:
图 11.9 – 示例 Azure Data Factory 管道触发一个跟随复制活动的 AzureML 已发布管道
如果你想调度管道按月触发,你需要像前一节那样发布管道,获取已发布管道的 ID,创建一个ScheduleRecurrence
,然后创建Schedule
。返回到你的笔记本,在那里你已经有了published_pipeline
的引用。添加一个新单元格,并输入以下代码:
from azureml.pipeline.core.schedule import ScheduleRecurrence, Schedule
from datetime import datetime
recurrence = ScheduleRecurrence(frequency="Month",
interval=1,
start_time = datetime.now())
schedule = Schedule.create(workspace=ws,
name="chapter-11-schedule",
pipeline_id=published_pipeline.id,
experiment_name="chapter-11-scheduled-run",
recurrence=recurrence,
wait_for_provisioning=True,
description="Schedule to retrain model")
print("Created schedule with id: {}".format(schedule.id))
在这段代码中,你定义了一个具有每月频率的ScheduleRecurrence
。通过指定start_time = datetime.now()
,你防止了管道的立即执行,这是创建新Schedule
时的默认行为。一旦你设置了想要的重复模式,可以通过调用Schedule
类的create
方法来调度管道执行。你将传入你想触发的published_pipeline
的 ID,并指定每次执行时所使用的实验名称。
重要提示
安排特定流水线的执行没有任何意义,因为由于两个步骤都设置了 allow_reuse=True
,将不会发生任何额外的训练。如果你希望每个月都重新训练,你可能希望将这个设置改为 False
,并强制在触发流水线调度时执行两个步骤。此外,在一个定期执行的流水线中,通常第一个步骤会从附加到 AzureML 工作区的多个来源获取新数据,然后转换数据并训练模型。
如果你想禁用定期执行,你可以使用 Schedule
类的 disable
方法。以下代码会禁用工作区中的所有定期流水线:
from azureml.pipeline.core.schedule import Schedule
schedules = Schedule.list(ws, active_only=True)
print("Your workspace has the following schedules set up:")
for schedule in schedules:
print(f"Disabling {schedule.id} (Published pipeline: {schedule.pipeline_id}")
schedule.disable(wait_for_provisioning=True)
这段代码列出了工作区中所有活动的调度,并逐个禁用它们。确保你不会意外禁用本应在工作区中定期执行的流水线。
总结
在本章中,你学习了如何使用 AzureML SDK 定义 AzureML 流水线。这些流水线允许你以可重复的方式协调各种步骤。你首先定义了一个包含两个步骤的训练流水线。接着你学习了如何触发流水线以及如何排查潜在的代码问题。然后你将流水线发布到 AzureML 工作区注册,并获取了一个 HTTP 端点,第三方软件系统可以用来触发流水线执行。在最后一节中,你学习了如何安排已发布流水线的定期执行。
在下一章中,你将学习如何将你迄今为止在书中训练的模型投入生产。你将利用本章所学的知识编写批量推理流水线,这些流水线可以发布并通过 HTTP 触发,或者像本章所学的那样安排执行。
问题
在每一章中,你将找到一些问题来验证你对本章讨论主题的理解。
-
什么因素会影响流水线步骤的执行顺序?
a. 在构建
Pipeline
对象时定义步骤的顺序。b. 步骤之间的数据依赖关系。
c. 所有步骤并行执行,且你无法影响执行顺序。
-
判断对错:流水线中的所有步骤都需要在同一个计算目标和
Environment
中执行。 -
判断对错:
PythonScriptStep
默认情况下会重新使用之前执行的结果,如果参数或代码文件没有发生变化。 -
你正在尝试调试一个子任务运行执行问题。你应该在
StepRun
对象中调用以下哪个方法?a.
get_file_names
b.
get_details_with_logs
c.
get_metrics
d.
get_details
-
你刚刚在 Python 代码中定义了一个流水线。你需要做哪些步骤来安排该流水线的每日执行?
深入阅读
本节提供了一些有用的网络资源,以帮助你扩展对 AzureML SDK 和本章中使用的各种代码片段的知识。
-
本章节中使用的LightGBM框架的文档:
lightgbm.readthedocs.io
-
用于发起 HTTP 请求的 Requests Python 库:
docs.Python-requests.org
-
通过Azure Data Factory执行 AzureML 管道:
docs.microsoft.com/en-us/azure/data-factory/transform-data-machine-learning-service
-
用于脚本参数解析和创建命令行界面(CLI)应用程序的click Python 库:
click.palletsprojects.com/
第十二章:第十二章:使用代码将模型操作化
在本章中,您将学习如何将您到目前为止在本书中训练的机器学习模型进行操作化。您将探索两种方法:通过托管 REST API 暴露实时端点,您可以用它进行推理;并扩展您的管道编写知识,以高效地在大数据上进行并行推理。您将首先在工作区中注册一个模型以跟踪该工件。然后,您将发布一个 REST API;这将使您的模型能够与第三方应用程序(如Power BI)集成。接下来,您将编写一个管道,以非常具成本效益的方式在几分钟内处理五十万条记录。
在本章中,我们将涵盖以下主题:
-
了解各种部署选项
-
在工作区注册模型
-
部署实时端点
-
创建批量推理管道
技术要求
您需要访问 Azure 订阅。在该订阅中,您需要一个packt-azureml-rg
。您还需要有Contributor
或Owner
权限的packt-learning-mlw
。如果您按照第二章,部署 Azure 机器学习工作区资源的说明进行操作,这些资源应该已经为您准备好。
此外,您需要具备基本的Python语言知识。本章中的代码片段适用于 Python 3.6 及更高版本。您还应当熟悉在 AzureML Studio 中使用笔记本的操作;这部分内容在第七章,The AzureML Python SDK中已有介绍。
本章假设您已在第十章,理解模型结果中生成并注册了loans数据集。同时也假设您已按照第七章,The AzureML Python SDK中的“与计算目标协作”部分的说明,创建了一个名为cpu-sm-cluster的计算集群。
重要提示
AzureML 不断更新。如果您在使用本书中的代码示例时遇到任何问题,请尝试通过在新笔记本单元格中添加以下代码来升级 AzureML SDK:
!pip install --upgrade azureml-core azureml-sdk[notebooks]
然后,重新启动 Jupyter 内核,如第十章,理解模型结果中“训练贷款审批模型”部分所述。此外,尝试从本书的 GitHub 页面下载最新版本的笔记本。如果问题仍然存在,可以在本书的 GitHub 页面上打开问题。
您可以在 GitHub 上的bit.ly/dp100-ch12
找到本章所有的笔记本和代码片段。
了解各种部署选项
从第八章开始,我们一直在使用 Python 代码,实验 Python 代码。到目前为止,你已经训练了各种模型,基于指标对其进行了评估,并使用joblib库的dump
方法保存了训练好的模型。AzureML 工作区允许你通过将它们注册到模型注册表中来存储和管理这些工件,正如我们在第五章中讨论的,让机器进行模型训练。注册模型允许你为保存的模型和与特定模型相关的元数据(如根据各种指标评估的性能)版本化。你将通过 SDK 学习如何在工作区中注册模型部分注册模型。
一旦模型被注册,你需要决定如何操作化模型,是通过部署实时端点还是创建批处理过程,如图 12.1所示:
图 12.1 – 从训练到操作化的路径
在模型处理传入数据的方式上,主要有两种类别:
-
你训练的分类器的
predict_proba
方法。你将在本章的实时端点部署部分深入了解这一场景。 -
批量推理:在第五章中,让机器进行模型训练,你训练了一个 AutoML 模型来预测客户流失。该模型使用了消费者过去 6 到 12 个月的活动等特征。假设你想评估所有客户并为可能流失的客户开展市场营销活动。你需要执行一次性处理过程,读取所有客户信息,计算所需特征,然后调用模型为每个客户生成预测结果。结果可以存储在 CSV 文件中,供营销部门使用。在这种方法中,你只需要模型短暂的使用时间,即仅在进行预测时。你不需要实时端点,如在第五章中部署的端点,因为你不需要模型进行临时推理。你可以在本章的创建批量推理管道部分了解更多关于这个场景的信息。
所有模型都可以用于实时推理或批处理推理。你可以决定是否需要临时的模型推理,或是一个定期生成并存储推理结果的过程。批处理模式下的模型运营化通常更具成本效益,因为你可以使用低优先级计算集群来进行推理。在这种情况下,你不需要支付实时端点基础设施的费用以等待进行实时推理。
在下一节中,你将通过训练和注册模型,开始迈向模型运营化的道路,该模型将贯穿本章的其余部分。
在工作区中注册模型
注册模型可以让你保留不同版本的训练模型。每个模型版本都包含工件和元数据。在这些元数据中,你可以保留对实验运行和数据集的引用。这使你能够追踪训练模型所使用的数据、训练该模型的运行 ID,以及模型工件本身的谱系,如 图 12.2 所示:
图 12.2 – 从训练数据集到已注册模型的谱系构建
在本节中,你将训练一个模型并将其注册到你的 AzureML 工作区中。请执行以下步骤:
-
进入你的 AzureML Studio Web 界面中的Notebooks部分。
-
创建一个名为
chapter12
的文件夹,然后创建一个名为chapter12.ipynb
的笔记本,如 图 12.3 所示:https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/az-ds-ass-cert-gd/img/B16777_12_003.jpg图 12.3 – 将 chapter12 笔记本添加到你的工作文件中
-
在单独的笔记本单元格中添加并执行以下代码片段。你将从获取工作区资源的引用开始:
from azureml.core import Workspace, Experiment ws = Workspace.from_config() loans_ds = ws.datasets['loans'] experiment = Experiment(ws, "chapter-12-train")
在前面的代码中,你获取了工作区的引用,即
chapter-12-train
。 -
使用以下代码将数据集拆分为训练集和验证集:
training_data, validation_data = loans_ds.random_split( percentage = 0.8, seed=42) X_train = training_data.drop_columns('approved_loan') \ .to_pandas_dataframe() y_train = training_data.keep_columns('approved_loan') \ .to_pandas_dataframe().values.ravel() X_validate = validation_data.drop_columns('approved_loan') \ .to_pandas_dataframe() y_validate = validation_data.keep_columns('approved_loan') \ .to_pandas_dataframe().values.ravel()
代码将数据集拆分为 80% 的训练数据和 20% 的验证数据。
seed
参数初始化random_split
方法的内部随机状态,允许你硬编码数据拆分,并确保每次调用此代码时生成相同的training_data
和validation_data
。在这里,
X_train
是一个pandas
DataFrame
,包含income
、credit_cards
和age
特征(即除approved_loan
外的所有列)。相比之下,
y_train
包含的是你想要预测的值。首先,你加载一个只包含approved_loan
列的pandas
DataFrame
。然后,你将该DataFrame
转换为values
属性。这个数组对每一行都有一个单一元素数组。例如,[[0],[1]]表示两个记录:一个未批准的贷款值为0,一个已批准的贷款值为1。接着,你调用ravel
方法来扁平化这个数组,这样给定的例子就变成了*[0, 1]*。尽管你本可以直接使用pandas
DataFrame
来训练模型,但系统会发出警告消息,提示你已进行自动转换,并建议你使用ravel
方法,正如在这个单元中所看到的那样。对于将用于评估模型性能的
X_validate
DataFrame
和y_validate
数组,重复相同的过程。 -
使用以下代码训练一个模型并记录得到的准确性:
from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score run = experiment.start_logging() sk_model = LogisticRegression() sk_model.fit(X_train, y_train) y_predicted = sk_model.predict(X_validate) accuracy = accuracy_score(y_validate, y_predicted) print(accuracy) run.log("accuracy", accuracy) run.complete()
在这里,你从实验中的一次运行开始,如步骤 3所定义。你将使用这次运行来注册模型训练过程中的指标、日志和工件。然后,你训练一个
LogisticRegression
模型,并使用accuracy_score
函数来计算训练后模型的准确性。接着,你打印出计算得到的准确性,并将其作为指标记录到运行中。最后,你complete
该运行,以结束其执行。 -
现在你有一个由
sk_model
变量引用的训练模型,你将使用以下代码将其保存:import os import joblib os.makedirs('./model', exist_ok=True) joblib.dump(value=sk_model, filename= os.path.join('./model/','model.joblib'))
首先,你创建一个名为
model
的文件夹。文件夹的名称不重要。在该文件夹中,你使用joblib
库将训练好的模型dump
到一个名为model.joblib
的文件中。重要提示
.joblib
文件扩展名不是标准的,你可以根据自己的需要使用任何名称,只要保持一致即可。有些人使用.pkl
作为文件扩展名,这在过去是因为我们使用 Python 的内置pickle
模块序列化 Python 对象结构。如今,scikit-learn推荐使用joblib
库,因为它在序列化大型 NumPy 数组时更加高效,而这在训练模型时非常常见。 -
现在你已经准备好工件,可以使用以下代码注册模型:
from sklearn import __version__ as sk_version from azureml.core import Model run.upload_folder("model", "./model") model = run.register_model( model_name="chapter12-loans", model_path="./model/", tags={ "accuracy": accuracy}, properties={ "accuracy": accuracy}, model_framework= Model.Framework.SCIKITLEARN, model_framework_version= sk_version, datasets=[("training", loans_ds)] )
在第一行,你导入
sklearn
包的__version__
变量,它是一个字符串,表示当前环境中加载的版本。然后,你为该变量创建一个别名(使用as
语句),并在代码中将其作为sk_version
引用。这就是你用来训练模型的sklearn
库的版本。此外,你还从 AzureML SDK 导入Model
类,以便在后续的代码中使用它作为参考。导入引用之后,您上传本地
./model
文件夹的内容,该文件夹在Step 6中创建,到运行的输出中,位于名为model
的文件夹下。这样 AzureML 可以访问您即将注册的工件;否则,您将收到ModelPathNotFoundException
错误。准备好所有先决条件后,您可以注册模型。模型将以
chapter12-loans
(model_name
参数)命名,使用刚刚上传到运行输出的model
文件夹中的工件(model_path
参数)。您将精度指定为模型的标签(tags
参数)和属性(properties
参数)。您指示使用SCIKITLEARN
框架(model_framework
参数)训练模型,并指定您使用的框架版本(model_framework_version
参数)。在最后一行,您指定使用loans_ds
数据集作为training
数据集(datasets
参数)。重要说明
如果尝试重新运行相同单元格,则会发生Resource Conflict错误,因为无法覆盖已存在于运行输出文件夹中的文件。如果使用
#
作为行前缀注释掉upload_folder
行并重新运行单元格,则会注册同一模型的新版本,使用已存在于特定运行中的工件。 -
导航至
Model.Framework.SCIKITLEARN
。这种部署类型被认为是无代码部署,这是 AzureML 为支持的框架提供的能力。否则,您需要指定一个评分文件;这是我们将在Deploying real-time endpoints部分中涵盖的内容。 -
如果要注册从互联网下载的预训练模型,则没有
Run
对象来调用register_model
方法。您可以使用Model
类的register
方法,如下面的代码片段所示:from azureml.core import Model offline_model = Model.register( ws, model_name="chapter12-pre-trained-loans", model_path="./model/", properties={"accuracy": 0.828}, model_framework= "ScikitLearn", model_framework_version= "0.22.2.post1" )
在上述代码中,在 AzureML 工作区(
ws
变量)中注册位于localmodel
文件夹内部的工件(model_path
参数)作为名为chapter12-pre-trained-loans
的模型(model_name
参数)。这是使用sklearn
库的版本0.22.2.post1
(model_framework_version
参数)训练的模型。另外,其精度为0.828
,存储为模型属性。 -
如果有一个训练新模型的流程,例如您在Chapter 11中创建的计划管道,您需要验证新训练的模型是否比已注册的模型具有更好的指标。如果更好,则继续注册模型。为此,您可以使用类似以下代码的代码:
from sklearn.linear_model import RidgeClassifier new_model = RidgeClassifier(solver='svd') new_model.fit(X_train, y_train) y_predicted = new_model.predict(X_validate) accuracy = accuracy_score(y_validate, y_predicted) registered_model = Model(ws, name="chapter12-loans") r_version = registered_model.version r_acc = float(registered_model.properties['accuracy']) if accuracy > r_acc: print(f"New model has better accuracy {accuracy}") else: print(f"Registered model with version {r_version}" \ " has better accuracy {r_acc}")
在前面的代码中,你训练了一个基于
RidgeClassifier
的模型,使用的是你在 第 7 步 中注册的chapter12-loans
。registered_model
变量与 第 7 步 中获得的model
变量具有相同的引用;不过,这次你是通过Model
类创建该引用,而不是通过注册模型来创建。你从该模型中读取version
属性和accuracy
属性。你可以从模型的tags
字典中检索准确度,而不是从properties
字典中。由于标签和属性以字符串形式存储值,因此你需要将准确度值转换为浮动类型。接下来,你将新模型的准确度与已注册模型的准确度进行比较(已注册模型的准确度存储在r_acc
变量中)。如果新模型优于已注册的模型,则会打印一条消息。在这种情况下,你需要重复 第 6 步 和 第 7 步 来存储模型,并注册新版本的改进模型。重要提示
要注册一个新版本的模型,你只需使用相同的名称注册新模型。通过使用相同的名称注册新模型,AzureML 会自动为你创建一个新版本。
-
可选地,作为最后一步,使用以下代码删除本地存储的模型:
import shutil shutil.rmtree('./model',ignore_errors=True)
这段代码会删除你在 第 6 步 中创建的
model
文件夹,包括你不再需要的序列化模型。ignore_errors
参数允许你即使文件夹不存在也能运行此单元,而不会引发错误。
在本节中,你在 Jupyter 内核中训练了一个模型。然后,你在工作区内注册了该模型。你本可以在 train_model.py
脚本中使用相同的注册代码,这个脚本是你在第十一章中的 创建管道 部分的 第 11 步 中编写的,与管道一起工作,用来注册 run=Run.get_context()
方法,然后你需要上传序列化的模型并注册模型,就像你在 第 7 步 中所做的那样。作为额外的活动,尝试修改 train_model.py
脚本和 chapter11.ipynb
,创建一个管道,将正在训练的模型在管道中进行注册。此活动的潜在解决方案可以在 train_model_and_register.py
脚本中找到,位于 GitHub 仓库的 step02
文件夹中,链接为:bit.ly/dp100-ch11
。
在下一节中,你将开始将本节中注册的模型转化为可操作的模型,通过将其部署为一个 Web 服务来提供实时推断服务。
部署实时端点
假设你有一个电子银行解决方案,其中包含一个让客户申请贷款的流程。你希望适当地设定客户的预期,并为可能的拒绝做准备。当客户提交贷款申请表时,你希望调用你在在工作区注册模型部分中注册的模型,即名为chapter12-loans的模型,并传入客户在申请表上填写的信息。如果模型预测贷款将不会被批准,贷款请求的确认页面将出现一条消息,提醒客户贷款请求可能会被拒绝。
图 12.5展示了一个过于简化的架构,用以描绘从客户到模型实时端点的请求流向:
图 12.5 – 一个过于简化的电子银行架构,展示了从客户到模型的请求流向
部署模型最简单的方法是通过 AzureML 提供的无代码部署方式,适用于特定的机器学习框架,包括你在上一部分中使用的sklearn
库。请按照以下步骤操作:
-
转到
chapter12.ipynb
笔记本,添加以下代码以获取对你在上一部分中创建的chapter12-loans模型的最新版本的引用:from azureml.core import Workspace, Model ws = Workspace.from_config() model = Model(ws, name="chapter12-loans")
-
要部署实时端点,请使用以下代码:
no_code_service = Model.deploy(ws, "no-code-loans", [model]) no_code_service.wait_for_deployment(show_output=True)
该代码部署一个名为
no-code-loans
的新实时端点服务,然后等待部署完成。 -
要获取新部署的端点的评分 URI,请使用以下代码:
print(no_code_service.scoring_uri)
这是一个格式为
guid.region.azurecontainer.io/score
的 URL,接受带有JSON负载的POST请求,如下所示:{"data": [[2000,2,45]]}
该负载将触发一个推理请求,针对一位月收入 2000 美元、拥有 2 张信用卡并且 45 岁的客户。你可以使用Postman或curl等工具来构造这样的 HTTP 请求,并调用端点。
-
与其使用如
curl
之类的工具发起 HTTP 请求,你还可以使用no_code_service
引用,并通过传入通常会发送给服务的 JSON 负载来调用run
方法:import json input_payload = json.dumps({ 'data': [[2000, 2, 45], [2000, 9, 45]], 'method': 'predict' }) output = no_code_service.run(input_payload) print(output)
上述代码导入了
json
库,它帮助你将对象序列化为 JSON 字符串。你使用dumps
方法创建负载。请注意,这个负载与步骤 3中你看到的简单版本略有不同。在这个示例中,你传递了两个客户的信息:一个是之前传递的客户,另一个拥有9张信用卡,而不是2张。此外,你还指定了要调用的方法。默认情况下,模型的方法名是predict
,这是你在前几章中用来进行推理的方法。最后,你打印输出,应该看到类似以下的结果:{'predict': [0, 1]}
上述结果显示,第一个贷款将被拒绝,而第二个贷款将被批准。
大多数分类模型提供了另一种方法,叫做
predict_proba
,它返回一个包含每个标签概率的数组。在loans
审批的情况下,这个数组只包含两个概率,且总和为 1,也就是贷款被批准的概率和贷款被拒绝的概率。如果你将方法名从predict
更改为predict_proba
并重新执行单元格,你将得到以下结果:{'predict_proba': [[0.998, 0.002], [0.173, 0.827]]}
上述结果显示,模型 99.8%确定第一个贷款将被拒绝,82.7%确定第二个贷款将被批准。
-
可选地,导航到
chapter12-demanding-loans
。你指定它需要1
个 CPU 和1.5
GB 的 RAM。请注意,如果你在步骤 11中的在工作区注册模型部分删除了model
文件夹,那么这段代码将无法注册新模型,因为它找不到模型文件。 -
为了节省成本,你应该使用以下代码删除服务:
no_code_service.delete()
到目前为止,你已经使用无代码方法部署了一个实时端点,这种方法将模型作为容器实例进行部署。只有当模型使用特定的支持模型进行训练时,这种方法才可行。在下一部分,你将学习如何使用更高级的选项来部署模型。
理解模型部署选项
在前面的部分,你使用无代码的方法部署了一个模型。在幕后,AzureML 使用了一个包含所有必需模型依赖项的environment
,在我们的例子中是sklearn
,生成了一个 Python 脚本,用于加载模型并在数据到达端点时进行推理,并使用AciServiceDeploymentConfiguration
类发布了一个 ACI 服务。
如果你有一个使用不支持的框架训练的模型,或者你想更好地控制部署模型,你可以使用 AzureML SDK 类来部署模型,如图 12.7所示:
图 12.7 – 实时端点部署所需的组件
这里,InferenceConfig类指定了模型的依赖项。它需要一个入口脚本,该脚本将加载模型并处理传入的请求,以及一个脚本执行环境。该环境包含模型加载和进行推断所需的所有依赖项。
入口脚本应包含以下两个方法:
-
Init
:在此步骤中,脚本将训练好的模型加载到内存中。根据您将模型的状态存储到磁盘的方式,您可以使用相应的方法将模型反序列化。例如,如果您使用joblib
库序列化了模型,则可以使用相同库的load
方法将其加载到内存中。一些模型提供自己的序列化和反序列化方法,但过程保持一致;训练好的模型的状态保存在一个或多个文件中,您可以稍后使用这些文件将模型加载到内存中。根据模型的大小,初始化阶段可能需要相当长的时间。较小的sklearn
模型通常在几毫秒内加载到内存中,而较大的神经网络可能需要几秒钟的时间来加载。 -
run
:这是实时端点接收数据集以进行推断时调用的方法。在此方法中,您必须使用init
代码中加载的模型,调用它提供的预测方法,对传入的数据进行推断。如前所述,大多数模型提供predict
方法,您可以调用并将要进行推断的数据传入。大多数分类模型还提供一个附加方法,称为predict_proba
,它返回每个类别的概率。AutoML 预测模型提供forecast
方法,而不是predict
方法。在神经网络中,进行预测的方法有所不同。例如,在 TensorFlow 的第一个版本中,您必须通过session.run()
方法调用来进行预测。
一旦配置了模型依赖项,您需要决定将模型部署到何处。AzureML SDK 提供了三种类:LocalWebserviceDeploymentConfiguration
、AciServiceDeploymentConfiguration
和AksServiceDeploymentConfiguration
。这些类允许您将模型部署到本地机器、ACI 或Azure Kubernetes 服务(AKS),如图 12.8所示:
图 12.8 – 为您的模型选择合适的计算目标
正如您可能已经理解的那样,在图 12.8中,您可以通过指定您希望服务监听的端口来部署到本地计算机。这是一种很好的方法,可以调试模型加载的潜在问题,或验证与本地计算机上其他系统的集成。下一个选项是使用 ACI,它用于测试环境或小规模的生产环境。在AciServiceDeploymentConfiguration
类中,您只能使用 CPU,而不能使用 GPU。您可以通过将auth_enabled
参数设置为True
来使用基于密钥的认证保护端点。此认证方法要求您将静态密钥作为Authorization头部传递到 HTTP 请求中。
另一方面,AksServiceDeploymentConfiguration
将在 AKS 集群内部署服务。这使得您可以使用 GPU,前提是您的模型能够利用 GPU,并且您要部署的集群有支持 GPU 的节点。该部署配置允许您选择基于密钥的认证或基于令牌的认证。基于令牌的认证要求最终用户从保护 AzureML 工作区的Azure Active Directory获取访问令牌,这样就可以访问部署在其中的端点。与基于密钥的认证不同,基于令牌的认证令牌生命周期较短,并隐藏了调用者的身份,而基于密钥的认证是 ACI 中唯一可用的选项。AKS 部署的另一个生产就绪特性是能够根据传入请求数量的波动动态扩展和缩减。在当前的电子银行场景中,客户倾向于在工作时间访问电子银行解决方案,而在夜间系统几乎处于空闲状态。此外,在月底,传入流量会达到峰值。在这种工作负载下,您希望能够根据需要扩展端点,以适应流量的增加。当流量显著增加时,AKS 可以自动启动多个模型容器,并在它们之间进行负载均衡。当流量恢复正常时,它可以仅保留一个容器作为潜在流量的热备份。
现在,您对部署选项有了更好的理解,接下来您将使用在图 12.7中看到的类,在 ACI 中部署相同的模型:
-
首先,您需要创建的是入口脚本。在chapter12文件夹下,创建一个名为script的新文件夹,并将一个score.py文件放入其中,如图 12.9所示:https://github.com/OpenDocCN/freelearn-ds-pt4-zh/raw/master/docs/az-ds-ass-cert-gd/img/B16777_12_009.jpg
图 12.9 – 为实时端点添加 score.py 文件
-
在
init
方法中,你通过AZUREML_MODEL_DIR
环境变量获取序列化模型的路径。当 AzureML 启动用于提供模型的 Docker 镜像时,该变量指向模型所在的文件夹;例如,/tmp/azureml_umso8bpm/chapter12-loans/1
可能是你找到chapter12-loans
模型第一版的地方。在该文件夹中,实际的文件model.joblib
位于你在部署实时端点部分的步骤 5中上传的model
文件夹中。你使用os.path.join
来获取模型的最终路径,然后将模型加载到名为model
的global
变量中。如果你想使用 AzureML SDK 来获取模型的位置,你可以使用model_path = Model.get_model_path(model_name, version=version)
,该方法在底层使用相同的环境变量。然而,请注意,你需要在环境中安装 AzureML SDK,以便能够导入其中的Model
类;而前面的代码不需要这么做。重要说明
请注意,你正在使用
print
将模型路径和传入的raw_data
写入控制台。你将在通过应用程序洞察进行监控部分学习如何查看这些消息。在
run
方法中,你使用try
except
块来捕捉在尝试读取请求输入时可能发生的错误。如果发生这样的错误,异常会被序列化为字符串(使用str()
方法),并返回给最终用户。请注意,将异常返回给调用者是一种安全反模式,因为你可能会不小心向潜在的攻击者暴露有价值的信息,但在调试时这非常有用。你可以使用print
语句或者更高级的库来代替返回错误消息,例如try
块,在该块中,你会反序列化传入的 JSON 负载,如部署实时端点部分的步骤 3所示。然后,你调用通过init
方法加载到内存中的model
对象的predict
方法。接着,你将模型结果作为一个列表返回,并将其序列化为数组。重要说明
你永远不会直接调用
init
方法或run
方法。AzureML 会在最终的 Docker 镜像中放入另一段代码,那就是 HTTP 推理服务器。该服务器负责在服务器启动时调用你的init
方法,并将传入的 HTTP 数据传递到run
方法中。此外,你在run
方法中返回的结果将被序列化为JSON并返回给调用者。 -
接下来你需要的是一个包含所有必要依赖项的
Environment
,用于运行你创建的score.py
脚本。打开你的chapter12.ipynb
笔记本并在新单元格中添加以下代码:from azureml.core import Environment from azureml.core.conda_dependencies import CondaDependencies import sklearn myEnv= Environment(name="sklearn-inference") myEnv.Python.conda_dependencies = CondaDependencies() myEnv.Python.conda_dependencies.add_conda_package( f"scikit-learn=={sklearn.__version__}") myEnv.Python.conda_dependencies.add_pip_package( "azureml-defaults>=1.0.45")
在前面的代码中,你创建了一个
Environment
,如在第八章中演示的,使用 Python 代码进行实验。你添加了scikit-learn
、azureml-defaults
、pip
包,这些包包含了将模型作为 Web 服务托管所需的功能。由于你正在构建自己的Environment
,因此需要添加此包,并至少使用 1.0.45 版本。这是运行评分脚本时你可以使用的最低环境。此外,AzureML 还提供了一个已策划的环境,例如AzureML-sklearn-0.24.1-ubuntu18.04-py37-cpu-inference
,它包含了使用训练过的sklearn
模型(版本 0.24.1)进行推理请求所需的一切。 -
你已经定义了
InferenceConfig
类所需的一切。添加一个新的单元格,并输入以下代码以将所有内容组合起来:from azureml.core.model import InferenceConfig inference_config = InferenceConfig( source_directory= "./script", entry_script='score.py', environment=myEnv)
这段代码创建了你在模型推理时所需要的配置。它使用位于
script
文件夹中的score.py
文件,并在步骤 3中定义的myEnv
环境中执行该文件。 -
现在你已经完成了图 12.7中所示的三个组件中的两个。在此步骤中,你将创建一个
AciServiceDeploymentConfiguration
类,并将模型部署到 ACI。在新单元格中,添加以下代码:from azureml.core.webservice import AciWebservice deployment_config = AciWebservice.deploy_configuration( cpu_cores=1, memory_gb=1) service = Model.deploy(ws, "aci-loans", [model], inference_config, deployment_config) service.wait_for_deployment(show_output=True)
在这里,我们使用
AciWebservice
类来获取你想要部署的容器实例的部署配置。在前面的代码中,你指定了需要 1 个 CPU 核心和 1GB 的内存。然后,你将模型部署到一个名为aci-loans
的新服务中,并等待部署完成。重要提示
如果在尝试部署容器时遇到问题,你可以查看打印输出中的错误信息,或使用
service.get_logs()
方法。很可能是score.py
脚本中的代码库出现了问题。你可以通过安装azureml-inference-server-http
pip 包并运行以下命令来在本地测试代码:5001
。调试这种情况的另一种方法是使用LocalWebservice
,稍后我们将讨论。如果你的代码没有问题,那么可能是内存问题。这应该可以在服务日志中看到。在这种情况下,请参阅下一节,了解如何对模型进行分析,以确定其资源需求。 -
要测试已部署的服务,你可以使用以下代码,这与前一节中使用的代码类似:
import json input_payload = json.dumps({ 'data': [[2000, 2, 45]] }) output = service.run(input_payload) print(output)
请注意,负载中的
method
属性(你在步骤 4中使用的部署实时端点部分)不会对这次部署产生任何影响,并且会从负载中省略。如果你想支持此属性,则需要在score.py
文件的run
方法中编写代码来读取该属性并调用模型的相应方法。 -
为了节省成本,在完成测试后使用以下代码删除服务:
service.delete()
-
如果你想在本地计算机上部署相同的服务,可以使用以下代码:
from azureml.core.webservice import LocalWebservice deployment_config = LocalWebservice.deploy_configuration(port=1337) service = Model.deploy(ws, "local-loans", [model], inference_config, deployment_config) service.wait_for_deployment()
你不再使用
AciWebservice
类,而是使用LocalWebservice
来创建一个监听端口1337
的本地服务。如果你在本地计算机上运行笔记本,你需要访问http://localhost:1337
并查看服务端点的健康状况。现在,在 AzureML 笔记本中运行了这段代码,本地计算机就是你正在使用的计算实例。要查看名为service.delete()
的计算实例的端口1337
,请参见步骤 7。
类似于AciWebservice
和LocalWebservice
,你还可以使用AksWebservice
来创建AksServiceDeploymentConfiguration
。在部署时,你需要在Model.deploy
方法中指定一个额外的参数,即deployment_target
参数。这个参数允许你指定想要将模型部署到的AksCompute
推理集群。
除了你之前看到的本地计算机、ACI 和 AKS 部署选项,AzureML 还提供了多种其他部署选项。例如,Azure Functions允许你在无服务器架构中运行模型,Azure App Services将模型托管为传统的 Web 应用程序,随时准备处理传入的请求。另一方面,你可以使用IoT Edge,它允许你将服务部署到边缘设备,如树莓派或基于 GPU 的 Jetson Nano。最后,你甚至可以将模型打包到 Docker 容器镜像中,这可以在隔离的空气间数据中心内运行。
在本节中,你已部署了一个 ACI 实时推理端点,要求 1 个 CPU 核心和 1 GB 的内存。在接下来的章节中,你将探讨如何通过对模型性能的分析来优化资源需求。
对模型资源需求的分析
在将某些东西投入生产之前,执行压力测试是非常常见的做法。本质上,这种测试会向实时端点发送大量请求,并衡量该端点的响应能力和性能。你可以对模型进行类似的操作,了解它们在预期的性能下需要什么类型的资源。例如,你可能需要确保所有推理都在 200 毫秒内完成。
在本节中,你将创建一个测试数据集,该数据集将用于压力测试实时端点并观察其性能。数据集中的每一行将包含一个单独的推理请求。
打开你的chapter12.ipynb
笔记本,并执行以下步骤:
-
在一个新单元格中,添加以下代码:
loans_ds = ws.datasets['loans'] prof_df = loans_ds.drop_columns('approved_loan') \ .to_pandas_dataframe() prof_df['sample_request'] = \ "{'data':[[" + prof_df['income'].map(str) \ + ","+ prof_df['credit_cards'].map(str) \ + "," + prof_df['age'].map(str) + "]]}" prof_df = prof_df[['sample_request']] prof_df.head()
这段代码加载
loans
数据集,删除我们不需要的approved_loan
列,并将其加载到pandas
的DataFrame
中。接着,你创建一个名为sample_request
的新列,将各个列连接起来,生成如下字符串:{"data": [[2000,2,45]]}
然后,你只保留该列并打印前五行,以验证请求是否符合预期。请注意,数据是否为我们用来训练模型的数据并不重要。它甚至可以是随机记录。我们关心的仅仅是将要发出的请求数量,而不是推断结果的样子。
-
使用以下代码将新创建的数据集存储在工作区中:
from azureml.core import Dataset dstore = ws.get_default_datastore() loan_req_ds = Dataset.Tabular.register_pandas_dataframe( dataframe=prof_df, target=(dstore,"/samples/loans-requests"), name="loans-requests", description="Sample requests for the loans model")
上述代码将 DataFrame 注册为
loans-requests
数据集。数据存储在默认数据存储中的/samples/loans-requests
目录下。loans_req_ds
变量引用了新注册的tabular
数据集。 -
现在你已经拥有了必要的数据,可以使用以下代码开始模型分析过程:
profile = Model.profile(ws, 'chapter12-loan', [model], inference_config, input_dataset=loan_req_ds, cpu=2, memory_in_gb=1) profile.wait_for_completion(True) print(profile.get_details())
请注意,profile 方法需要在前一部分模型部署时使用的
model
和inference_config
。此外,你还需要指定用于分析的 ACI 大小。在前面的代码中,你请求了 2 个 CPU 和 1 GB 的 RAM。分析可能需要较长时间,有时超过 20 分钟。分析完成后,你将查看结果,包括作为recommendedCpu
的 1 个 CPU 和作为recommendedMemoryInGB
值的 0.5 GB RAM。重要说明
模型分析的名称在工作区内应是唯一的。如果你在不更改名称的情况下重新运行步骤 3的代码,将会发生错误。
在后台,执行了一个名为ModelProfile
的实验,它会部署一个带有模型的 ACI 服务。服务启动并运行后,过程会发送你在loan_req_ds数据集中指定的 500 个请求,并记录模型的响应时间,同时监控已部署容器实例的 CPU 和内存使用情况。根据这些统计数据,AzureML 可以建议你为实时端点配置的推荐 CPU 和内存。
在下一部分,你将使用这些值来部署 ACI 服务。随后,你将探讨如何在部署后监控其性能,并使用Application Insights记录传入的数据。
使用 Application Insights 进行监控
正如你在第二章中学到的,部署 Azure 机器学习工作区资源,当你部署 AzureML 工作区时,会在同一资源组中部署一个名为packtlearningm<random_number>
的应用程序洞察账户。这个 Azure 资源使你能够监控应用程序的性能。特别是对于像你正在部署的实时端点这样的 Web 应用程序,应用程序洞察可以让你监控请求和响应时间、端点的失败率、代码中可能引发的任何异常,甚至是你希望从代码库中输出的日志痕迹。
在前面的理解模型部署选项部分中,你创建了一个包含几个print
语句的score.py
文件。这些信息被写入端点的控制台中,可以通过调用service.get_logs()
方法或者导航到部署日志标签页来查看,如图 12.10所示:
图 12.10 – 容器实例控制台中记录的模型路径和传入的原始数据
这种方法的问题在于日志不会持久化。如果你重新部署容器实例,日志将会丢失。而且,如果你部署了多个模型,你将需要一个集中式的地方来监控它们。应用程序洞察为你的解决方案带来了这些及更多的好处。
返回你的chapter12.ipynb
笔记本,重新部署 ACI 容器并为其启用应用程序洞察。在一个新的单元格中,添加以下代码:
from azureml.core.webservice import AciWebservice
deployment_config = AciWebservice.deploy_configuration(
cpu_cores=1, memory_gb=0.5, enable_app_insights= True)
service = Model.deploy(ws, "aci-loans", [model], inference_config, deployment_config)
service.wait_for_deployment(show_output=True)
注意,你正在使用在分析模型资源需求部分中推荐的1
个 CPU 核心和0.5
GB 的内存。另外,注意你正在通过传递enable_app_insights=True
参数来在部署配置中启用应用程序洞察。如果你已经部署了服务并希望启用应用程序洞察,你可以使用以下代码更新其配置:
service.update(enable_app_insights=True)
让我们向服务发送几个请求,以便更好地理解应用程序洞察能为你做些什么。在一个新的单元格中,添加以下代码:
import json
input_payload = json.dumps({'data': [[2000, 2, 45], [2000, 9, 45]]})
for x in range(10):
print(service.run(input_payload))
这段代码向服务发送了10个相同的请求,一个接一个地生成一些人工流量,这些流量应该会被记录在应用程序洞察中。找到指向 Azure 门户并直接进入应用程序洞察资源的 URL 的最简单方法是访问端点的信息页面,如图 12.11所示:
图 12.11 – 与你的 AzureML 工作区关联的应用程序洞察 URL
请注意,这个应用程序洞察 URL链接并不是针对aci-loans部署的特定链接。这个链接对于你所有的实时端点都是相同的,允许你集中监控所有的实时端点。点击这个链接将带你进入应用程序洞察,如图 12.12所示:
图 12.12 – 应用程序洞察显示你用最后一段代码发送的 10 个请求
在此仪表板上,你可以点击图表并深入查看信号详情;或者你可以查看控制台中应用程序写入的所有追踪。要查看它们,请导航到监视 | 日志,点击追踪,选择你想调查的时间范围,然后点击运行按钮。你应该能在结果中看到所有STDOUT消息,并可以深入查看详细信息,如图 12.13所示:
图 12.13 – 阅读你模型实时端点在应用程序洞察中发出的所有追踪
你可以在日志部分使用一种强大的类似 SQL 的语言——Kusto,来创建复杂的查询。你甚至可以根据这些查询创建自动化警报,例如,当过去 30 分钟内你的贷款拒绝次数超过 100 次时,系统会通知你。
重要提示
应用程序洞察支持每次最多 64 KB 的小负载日志。如果你计划记录超过 64 KB 的数据,例如,一个包含超过 64 KB 数据的小批量输入,你应该考虑使用 AzureML SDK 中的DataCollector
类。该类允许你将数据直接记录到存储帐户中;但是,只有在 AKS 中部署时,才可使用此类。
在进入下一部分之前,请不要忘记删除已部署的服务,以防止 ACI 服务产生意外的费用。你可以在工作室体验中的资产 | 端点列表中删除该服务,或者通过以下代码行删除服务:
service.delete()
在本节中,你学习了如何在将实时端点部署到生产环境后进行监控。在图 12.12中,你可能注意到有几个swagger
文件。在下一部分中,你将学习如何修复那些失败的请求,并实现与希望消费你模型结果的第三方应用程序的丰富集成。
与第三方应用程序集成
到目前为止,你已经部署了一个接受数组数组作为输入的 Web 服务。这是一个晦涩的输入,你需要向任何想要使用你实时端点的人解释它。在第五章《让机器进行模型训练》中,你了解了可以用来生成代码以自动消费你的端点的swagger
文件。为了生成这样的文件,你可以使用开源的inference-schema
包,并用元数据装饰你的代码,从而驱动swagger.json
文件的生成。
为了使你的模型更容易被第三方应用程序消费,你应该接受以下有效负载:
{"data":[{"income": 2000, "credit_cards": 2, "age": 45}]}
在这里,你需要创建一个新的评分文件版本。与其克隆并编辑现有的评分文件,不如直接从 GitHub 页面下载修改后的score_v2.py
版本,正如技术要求部分所提到的那样。在Notebooks部分,右键点击位于script文件夹中的score.py文件,选择Duplicate命令,复制该文件,如图 12.14所示:
图 12.14 – 创建入口脚本的 v2 文件
将克隆文件命名为score_v2.py
,并修改代码,使其如下所示:
import os
import joblib
from inference_schema.schema_decorators import input_schema, output_schema
import pandas as pd
from inference_schema.parameter_types.pandas_parameter_type import PandasParameterType
import numpy as np
from inference_schema.parameter_types.numpy_parameter_type import NumpyParameterType
在脚本文件的开始部分,你导入了额外的辅助类,这些类稍后会在代码中使用。请注意,你不再需要json
模块:
def init():
global model
model_path = os.path.join(os.getenv(\
"AZUREML_MODEL_DIR"), "model/model.joblib")
model = joblib.load(model_path)
你不会修改init
方法:
data_sample = pd.DataFrame(
{
"income": pd.Series([2000.0], dtype="float64"),
"credit_cards": pd.Series([1], dtype="int"),
"age": pd.Series([25], dtype="int")
}
)
output_sample = np.array([0])
在前面的代码块中,你创建了一个pandas
DataFrame
,它将作为传入请求的data
属性中包含的对象的示例。这个data_sample
对象有一个income
特征,它是float64
类型,以及credit_cards
和age
特征,它们是整数类型。类似地,对于输出,你将output_sample
定义为一个 NumPy 数组或数值。你可以在以下代码块的装饰器中使用data_sample
和output_sample
对象:
@input_schema("data", PandasParameterType(data_sample))
@output_schema(NumpyParameterType(output_sample))
def run(data):
try:
result = model.predict(data)
return result.tolist()
except Exception as e:
error = str(e)
return error
在这里,你使用data_sample
对象和@input_schema
装饰器。此外,你使用PandasParameterType
,这表示紧随其后的名为pandas
DataFrame
的参数遵循由data_sample
示例定义的模式。你使用@output_schema
装饰器指定你的服务返回一个 NumPy 数组作为输出,类似于output_sample
。一旦你配置了这些模式,你会注意到在run
方法中不需要再反序列化传入的有效负载。data
对象已经是一个反序列化后的pandas
DataFrame
。
如果你想处理二进制文件而不是表格数据,例如处理图像,你可以使用@rawhttp
指令,它会将原始的 HTTP 请求传递给你的run
方法。使用纯 HTTP 请求可以给你更大的灵活性,包括设置响应头;这是配置诸如跨域资源共享(CORS)等安全功能时所必需的。你可以在本章的进一步阅读部分找到更多关于这些高级场景的资源。
现在你已经准备好score_v2.py
脚本文件的代码,你需要发布实时端点。要为新的评分功能创建实时端点,请在笔记本中的一个单元格内添加以下代码:
from azureml.core.model import InferenceConfig
from azureml.core.webservice import AciWebservice
myEnv.Python.conda_dependencies.add_pip_package("inference_schema[pandas-support]>=1.1.0")
inference_config = InferenceConfig(source_directory= "./script", entry_script='score_v2.py', environment=myEnv)
deployment_config = AciWebservice.deploy_configuration( cpu_cores=1, memory_gb=0.5)
service = Model.deploy(ws, "aci-loans", [model], inference_config, deployment_config)
service.wait_for_deployment(show_output=True)
在前面的代码中,你将inference_schema
pip 包附加到你在理解模型部署选项部分中定义的myEnv
依赖项中。请注意,你安装该包时使用了pandas-support
扩展,这会包括pandas
包。你的score_v2.py
文件依赖的numpy
依赖项会由 pip 自动安装,因为它是pandas
包的依赖项。
接下来,你需要指定使用score_v2.py
入口脚本并部署新服务。新服务将提供一个swagger.json
文件,供第三方应用程序(如 Power BI)读取并自动了解如何调用你的模型。你可以在端点页面上获取指向该文件的 Swagger URI,如图 12.11所示。在端点页面上,你应该注意到测试选项卡已增强,指导你提供调用模型所需的字段。在代码方面,你可以使用以下负载调用模型:
import json
service = ws.webservices['aci-loans']
input_payload = json.dumps({"data":[
{"income": 2000,"credit_cards": 2,"age": 45},
{"income": 2000, "credit_cards": 9,"age": 45}
]})
print(service.run(input_payload))
input_payload = json.dumps({'data': [
[2000, 2, 45], [2000, 9, 45]
]})
print(service.run(input_payload))
在进入下一节之前,确保使用以下代码删除你刚刚部署的 ACI 服务:
service.delete()
到目前为止,你一直在部署能够通过 REST API 处理临时推理请求的实时推理端点。在下一节中,你将学习如何部署一个批量推理管道,它能够使用ParallelRunStep
并行处理大数据。
创建批量推理管道
在第十一章《使用管道》中,你学会了如何创建多个步骤的管道并进行编排。这些管道可以通过 REST API 进行调用,类似于你在上一节中创建的实时端点。一个关键的区别是,在实时端点中,基础设施是持续开启的,等待请求到达,而在发布的管道中,集群仅在管道被触发后才会启动。
您可以使用这些管道来协调位于数据集中的数据的批处理推理。例如,假设您刚刚训练了本章中使用的loans
模型。您希望对所有待处理的贷款请求运行该模型,并存储结果;这是为了便于您实施一个电子邮件营销活动,针对那些可能会被拒绝贷款的客户。最简单的方法是创建一个单独的PythonScriptStep
,按顺序处理每条记录并将结果存储在输出文件夹中,正如您在第十一章中学到的那样,工作与管道。但是,您也可以将数据集拆分为多个批次,然后让它们在您的集群中每个节点内部的多个进程中并行处理,如图 12.15所示:
图 12.15 – 通过将大数据集拆分成较小的批次并并行处理它们来进行并行处理
在本节中,您将创建一个批处理处理管道,该管道将使用您在本章中训练的chapter12-loans
模型进行推理。您已经有一个名为loans
的数据集,但它太小,无法展示ParallelRunStep
如何通过并行化推理来加速处理。您将通过重复复制相同的 DataFrame 生成一个新的数据集,大小是原来的 1,024 倍。然后,您将创建一个类似于在第十一章中创建的管道,工作与管道。这一次,您将使用ParallelRunConfig
和ParallelRunStep
类来并行化数据集的处理。配置类需要一个入口脚本,类似于您在上一节中看到的入口脚本。此外,您还需要定义以下两个方法:
-
init()
: 此方法加载模型并为即将到来的批次准备处理过程。此方法不期望有任何输出。 -
run(mini_batch)
: 此方法执行实际的数据处理。此方法将被多次调用,每次传递不同的mini_batch
参数。您必须返回一个数组,其中包含每个成功处理的项目的行。例如,如果mini_batch
参数包含 100 行,而您返回了 98 项数据,则表示您未能处理其中 2 条记录。如果您处理的是TabularDataset
,则mini_batch
参数可以是一个pandas
DataFrame
,如果您处理的是FileDataset
,则可以是包含您需要处理的文件路径的数组。
导航到您的chapter12.ipynb
笔记本,并执行以下步骤:
-
从获取对工作区、数据集和您将用于管道的计算集群的引用开始:
from azureml.core import Workspace ws = Workspace.from_config() loans_ds = ws.datasets['loans'] compute_target = ws.compute_targets['cpu-sm-cluster']
代码应该是自解释的,因为您已经在第十一章《与管道一起工作》中使用过它。
-
基于
loans
数据集创建一个新的、更大的数据集:from azureml.core import Dataset loans_df = loans_ds.drop_columns('approved_loan') \ .to_pandas_dataframe() for x in range(10): loans_df = loans_df.append(loans_df) dstore = ws.get_default_datastore() pending_loans_ds =\ Dataset.Tabular.register_pandas_dataframe( dataframe=loans_df, target=(dstore,"/samples/pending-loans"), name="pending-loans", description="Pending loans to be processed")
在前面的代码中,您正在将
loans
的DataFrame
加载到内存中,但不包含approved_loan
列。这个数据集只有 500 行。然后,您将数据集追加到自身 10 次。这将创建一个包含 512,000 行的更大数据集,您将其注册为pending-loans
。 -
现在,是时候创建处理该数据集的脚本了。在
chapter12
文件夹中,添加一个pipeline_step
文件夹,然后添加一个名为tabular_batch.py
的文件,内容如下:from azureml.core import Model import joblib def init(): global model model_path = Model.get_model_path("chapter12-loans") model = joblib.load(model_path) def run(mini_batch): print(mini_batch.info()) mini_batch["approved"] = model.predict(mini_batch) return mini_batch.values.tolist()
这个脚本有两个方法,如前所述。在
init
方法中,您使用Model
类的get_model_path
方法来获取您至今使用的模型的路径。从脚本的角度来看,模型将存储在脚本运行的同一台计算机的文件夹中。然后,您使用joblib
将模型加载到名为model
的global
变量中。在run
方法中,您打印传入 DataFrame 的大小,然后创建一个名为approved的新列,存储所有模型推断的结果。您返回一个包含每行处理记录的四元素数组的列表,类似于以下记录:[7298, 2, 35, 1] [4698, 7, 70, 0]
如果您处理的是
FileDataset
而不是本节中处理的TabularDataset
,那么相应的file_batch.py
文件会如下所示:def init(): print('Load model here') def run(mini_batch): output = [] for file_path in mini_batch: output.append([file_path, 0]) return output
您像往常一样在
init
方法中加载模型,例如一个用于实现图像分类的神经网络。在run
方法中,mini_batch
参数是一个包含您需要处理的文件路径的数组。您可以遍历这些文件并使用模型进行推断。作为输出,您返回文件名和模型的结果,示例如下:['/path/sample_cat.jpg', 0] ['/path/sample_dog.jpg', 1]
在步骤 5中,您将观察到,这些结果将聚合成一个在
ParallelRunConfig
中定义的单一文件。 -
您需要创建一个环境来执行管道步骤。将以下代码添加到一个单元格中:
from azureml.core import Environment from azureml.core.conda_dependencies import CondaDependencies import sklearn pEnv= Environment(name="sklearn-parallel") pEnv.Python.conda_dependencies = CondaDependencies() pEnv.Python.conda_dependencies.add_conda_package(f"scikit-learn=={sklearn.__version__}") pEnv.Python.conda_dependencies.add_pip_package("azureml-core") pEnv.Python.conda_dependencies.add_pip_package("azureml-dataset-runtime[pandas,fuse]")
您需要安装
scikit-learn
的 conda 包,就像之前一样。为了使ParallelRunConfig
正常工作,您还需要包括azureml-core
和azureml-dataset-runtime[pandas,fuse]
的pip
包。 -
接下来,创建
ParallelRunConfig
类,配置如何拆分工作负载以及使用哪个脚本进行数据处理。将以下代码添加到新的笔记本单元中:from azureml.pipeline.steps import ParallelRunConfig parallel_run_config = ParallelRunConfig( source_directory='pipeline_step', entry_script='tabular_batch.py', mini_batch_size='100Kb', error_threshold=-1, output_action='append_row', append_row_file_name="loans_outputs.txt", environment=pEnv, compute_target=compute_target, node_count=1, process_count_per_node=2 )
在这里,您将运行位于
pipeline_step
文件夹中的tabular_batch.py
脚本。您将把数据集拆分成大约 100 KB 的小批次。如果处理的是FileDataset
,则需要指定每个批次中包含的文件数量。这里,error_threshold
指定在处理数据时应该忽略的记录或文件失败的数量。-1
表示您可以接受任何数量的处理错误。output_action
参数接受append_row
值或summary_only
值。使用append_row
值,您可以要求将所有run
方法调用的输出附加到一个名为parallel_run_step.txt
的单一输出文件中,除非通过append_row_file_name
参数覆盖该文件名,正如前面的示例所演示的那样。由于记录是并行处理的,因此文件中的记录顺序无法保证。通常,您会返回客户 ID 或贷款申请 ID,以及模型的推断结果。通过该 ID,您可以将原始记录与模型的预测结果关联。在当前示例中,我们没有任何 ID;因此,我们返回整个行,就像在步骤 3中的tabular_batch.py
脚本一样。接下来,您需要指定执行此流水线步骤的环境和集群。最后,您需要指定此流水线步骤将在单个节点上运行,并且每个参与节点将启动两个进程。如果使用两个节点,则会有四个进程并行运行。在当前示例中,两个并行进程足以在几分钟内完成处理。
如果您的处理脚本需要超过 60 秒来处理您指定的
mini_batch_size
参数,您可以通过设置run_invocation_timeout
参数来增加超时值。 -
下一步,您将定义之前指定的
append_row_file_name
的输出位置:from azureml.data import OutputFileDatasetConfig datastore = ws.get_default_datastore() step_output = OutputFileDatasetConfig( name= "results_store", destination=(datastore, '/inferences/loans/'))
您将把该聚合文件存储在默认数据存储区下的
/inferences/loans/
文件夹中。 -
现在是时候创建流水线的第一个也是唯一的步骤——
ParallelRunStep
:from azureml.pipeline.steps import ParallelRunStep parallel_step = ParallelRunStep( name='chapter12-parallel-loans', inputs=[pending_loans_ds.as_named_input('loans')], output=step_output, parallel_run_config=parallel_run_config, allow_reuse=False )
将此步骤命名为
chapter12-parallel-loans
,并传递您在步骤 2中注册的pending_loans_ds
数据集。输出存储在您在步骤 6中创建的OutputFileDatasetConfig
中。指定此步骤不应被重用(allow_reuse
);这允许您多次触发流水线,每次都获取数据集中的最新数据以及最新注册的模型。 -
使用以下代码创建并执行流水线:
from azureml.core import Experiment from azureml.pipeline.core import Pipeline pipeline = Pipeline(workspace=ws, steps=[parallel_step]) pipeline_run = Experiment(ws, 'chapter12-parallel-run').submit(pipeline)
-
您可以使用以下代码通过
RunDetails
小部件查看执行日志:from azureml.widgets import RunDetails RunDetails(pipeline_run).show()
或者,您可以使用以下代码等待执行完成:
pipeline_run.wait_for_completion(show_output=True)
-
从那时起,你可以根据第十一章,使用管道,发布甚至安排流水线。
您可以访问 AzureML 工作室中的管道,观察它生成的输出和日志,如图 12.16所示。请注意,您将找到一个单节点和两个进程。每个进程都有多个run
方法调用。每次调用run
方法时,都会传入一个占用 117.3 KB 内存的 DataFrame,这接近您在步骤 5中请求的 100 KB:
图 12.16 – 并行执行日志显示 mini_batch DataFrame 信息
在这一节中,您学习了如何创建一个可以并行处理大量数据的批处理流水线。这些是您在考试中需要了解的操作选项,涵盖实时和批处理模式。
摘要
在本章中,您探索了在本书中训练的机器学习模型的各种使用方式。您可以进行实时推断,也可以以经济高效的方式批处理大量记录。您开始注册用于推断的模型。从那里,您可以为测试部署 ACI 中的实时端点,或者为需要高可用性和自动扩展的生产工作负载部署到 AKS 中。您探索了如何配置您的模型以确定托管实时端点所需的推荐容器大小。接着,您发现了应用程序洞察,它允许您监视生产端点并识别潜在的生产问题。通过应用程序洞察,您注意到您生成的实时端点未公开一个swagger.json
文件,这是第三方应用程序(如 Power BI)自动消费您的端点所需的。您修改了评分函数,以包含有关您的模型输入和输出的元数据,从而完成了本章的实时推断部分。
然后,您转向批处理推断部分,您在那里编写了一个可以并行处理 50 万条记录的流水线,仅用几分钟。将此并行化与低优先级计算结合使用时,处理更大数据量时能实现很大的成本节省。
恭喜!您已完成发现 AzureML 工作区基本功能的旅程。现在,您可以在工作区中进行机器学习实验,并可以根据适合解决业务问题的选项将生成的模型进行操作化。掌握了这些知识,您应该能够以优异的成绩通过DP-100考试,在 Azure 上设计和实施数据科学解决方案。
问题
在每一章中,您将找到一系列问题来验证您对已讨论主题的理解:
-
您想要部署一个实时端点,处理来自一个在线博彩网站的交易。该网站的流量在比赛期间会有激增,夜间则非常低。您应该使用以下哪个计算目标?
a. ACI
b. 计算实例
c. 计算集群
d. AKS
-
您想要监控部署在 AKS 中的实时端点,并确定服务的平均响应时间。您应该使用哪个监控解决方案?
a. ACI
b. Azure 容器注册表
c. 应用程序洞察
-
您有一个计算机视觉模型,并且想要并行处理 100 张图片。您编写了一个包含并行步骤的管道,您希望每次处理 10 张图片。您应该设置以下哪个
ParallelRunConfig
参数?a.
mini_batch_size=10
b.
error_threshold=10
c.
node_count=10
d.
process_count_per_node=10
进一步阅读
本节提供了一些有用的网络资源列表,帮助您增强对 AzureML SDK 及本章中使用的各种代码片段的了解:
-
来自 scikit-learn 的模型持久化指导:
scikit-learn.org/stable/modules/model_persistence.html
-
使用 Postman 测试 REST API:
www.postman.com/product/api-client/
-
curl 命令行工具,用于发起网页请求:
curl.se/
-
使用 OpenCensus 监控 Python 应用程序:
docs.microsoft.com/azure/azure-monitor/app/opencensus-python
-
如何使用推理服务器在本地测试您的入口脚本:
docs.microsoft.com/azure/machine-learning/how-to-inference-server-http
-
将模型打包到自治 Docker 容器中:
docs.microsoft.com/azure/machine-learning/how-to-deploy-package-models
-
用于存储可以在多个平台加载的模型的 ONNX 机器学习格式:
docs.microsoft.com/azure/machine-learning/concept-onnx
-
应用程序洞察简介:
docs.microsoft.com/azure/azure-monitor/app/app-insights-overview
-
Kusto 查询语言简介:
docs.microsoft.com/azure/data-explorer/kusto/concepts/
-
高级实时端点入口脚本编写指南:
docs.microsoft.com/azure/machine-learning/how-to-deploy-advanced-entry-script
-
在 Power BI 中集成 AzureML 模型:
docs.microsoft.com/power-bi/transform-model/dataflows/dataflows-machine-learning-integration#azure-machine-learning-integration-in-power-bi
-
使用
ParallelRunStep
类训练数百个模型:github.com/microsoft/solution-accelerator-many-models
订阅我们的在线数字图书馆,全面访问超过 7,000 本书籍和视频,以及领先的行业工具,帮助你规划个人发展并推进职业生涯。欲了解更多信息,请访问我们的网站。
第十三章:为什么要订阅?
-
通过来自超过 4,000 名行业专家的实用电子书和视频,减少学习时间,增加编码时间。
-
通过专为你定制的 Skill Plans 提升你的学习。
-
每月获得一本免费的电子书或视频。
-
可完全搜索,轻松访问重要信息。
-
复制并粘贴、打印和收藏内容。
你知道 Packt 提供所有出版书籍的电子书版本吗?PDF 和 ePub 格式的文件可用。你可以在packt.com升级到电子书版本,作为印刷版书籍的客户,你可以享受电子书版本的折扣。更多详情请通过 customercare@packtpub.com 联系我们。
在www.packt.com,你还可以阅读一系列免费的技术文章,注册各种免费的电子通讯,并获得 Packt 图书和电子书的独家折扣和优惠。
你可能会喜欢的其他书籍。
如果你喜欢这本书,可能会对 Packt 的其他书籍感兴趣:
https://www.packtpub.com/product/cloud-analytics-with-microsoft-azure/9781839216404
使用 Microsoft Azure 进行云分析。
包括 Altaiar、Jack Lee 和 Michael Peña。
ISBN:9781839216404
-
探索现代数据仓库和数据管道的概念。
-
在应用云分析解决方案时,了解不同的设计考虑因素。
-
设计一个端到端的云分析管道。
-
区分结构化数据、半结构化数据和非结构化数据。
-
为你的数据分析解决方案选择一个基于云的服务。
使用 Azure 服务来获取、存储和分析任何规模的数据https://www.packtpub.com/product/cloud-scale-analytics-with-azure-data-services/9781800562936
使用 Azure 数据服务进行云规模分析。
Patrik Borosch。
ISBN:9781800562936
-
使用 Azure 服务实施数据治理。
-
在 Azure 门户中使用集成监控,并将 Azure Data Lake Storage 集成到 Azure Monitor 中。
-
探索无服务器功能,进行临时数据发现、逻辑数据仓储和数据清洗。
-
使用 Synapse Analytics 和 Spark 池实施网络连接。
-
使用 Databricks 集群创建并运行 Spark 作业。
-
使用 Azure Functions(Azure 上的无服务器运行时环境)实现流式处理。
-
探索 Azure 中的预定义机器学习服务,并在你的应用中使用它们。
Packt 正在寻找像你这样的作者。
如果您有兴趣成为 Packt 的作者,请访问authors.packtpub.com并立即申请。我们已与成千上万的开发人员和技术专业人员合作,就像您一样,帮助他们与全球技术社区分享见解。您可以进行一般申请,申请我们正在招聘作者的特定热门主题,或提交您自己的想法。
分享您的想法
现在您已经完成了Azure 数据科学家认证指南,我们很乐意听取您的想法!如果您从亚马逊购买了这本书,请点击此处直接转到亚马逊评论页面并分享您的反馈或在购买网站上留下评论。
您的评论对我们和技术社区至关重要,将帮助我们确保我们提供卓越质量的内容。