排查机器学习管道问题

适用于:Python SDK azureml v1

在本文中,你将了解如何在 Azure 机器学习 SDKAzure 机器学习设计器中对在运行机器学习管道时出现的错误进行故障排除。

故障排除提示

下表包含管道开发期间出现的一些常见问题,以及可能的解决方法。

问题 可能的解决方法
无法将数据传递给 PipelineData 字典 确保已在脚本中创建了一个目录,该目录对应于管道预期步骤要将数据输出到的位置。 大多数情况下,输入参数将定义输出目录,然后你需要显式创建该目录。 使用 os.makedirs(args.output_dir, exist_ok=True) 创建输出目录。 有关演示此设计模式的评分脚本示例,请参阅该教程
依赖项 bug 如果在远程管道中看到在本地测试时未发生的依赖项错误,请确认远程环境依赖项和版本与测试环境中的依赖项和版本匹配。 请参阅生成、缓存和重复使用环境
计算目标出现不明确的错误 请尝试删除并重新创建计算目标。 重新创建计算目标是很快的,并且可以解决某些暂时性问题。
管道未重复使用步骤 默认已启用步骤重复使用,但是,请确保未在管道步骤中禁用它。 如果已禁用重用,则将步骤中的 allow_reuse 参数设置为 False
管道不必要地重新运行 为了确保步骤只在其基础数据或脚本发生更改时才重新运行,请分离每个步骤的源代码目录。 如果对多个步骤使用同一个源目录,则可能会遇到不必要的重新运行。 在管道步骤对象中使用 source_directory 参数以指向该步骤的隔离目录,并确保未对多个步骤使用同一个 source_directory 路径。
在训练时期或其他循环行为中逐步减速 尝试将任何文件写入操作(包括日志记录)从 as_mount() 切换到 as_upload()。 “装载”模式使用远程虚拟化文件系统,在每次将文件追加到该系统时上传整个文件。
计算目标启动时间过长 用于计算目标的 Docker 映像是从 Azure 容器注册表 (ACR) 加载的。 在默认情况下,Azure 机器学习会创建一个使用“基本”服务层级的 ACR。 将工作区的 ACR 更改为“标准”或“高级”层级可能会减少生成和加载映像所需的时间。 有关详细信息,请参阅 Azure 容器注册表服务层级

身份验证错误

如果通过远程作业对某个计算目标执行管理操作,会收到以下错误之一:

{"code":"Unauthorized","statusCode":401,"message":"Unauthorized","details":[{"code":"InvalidOrExpiredToken","message":"The request token was either invalid or expired. Please try again with a valid token."}]}
{"error":{"code":"AuthenticationFailed","message":"Authentication failed."}}

例如,如果尝试通过为实施远程执行而提交的机器学习管道创建或附加计算目标,将会收到错误。

ParallelRunStep 进行故障排除

ParallelRunStep 的脚本必须包含两个函数:

  • init():此函数适用于后续推理的任何成本高昂或常见的准备工作。 例如,使用它将模型加载到全局对象。 此函数仅在进程开始时调用一次。
  • run(mini_batch):函数将针对每个 mini_batch 函数运行。
    • mini_batchParallelRunStep 将调用 run 方法,并将列表或 pandas DataFrame 作为参数传递给该方法。 如果输入为 FileDataset,则 mini_batch 中的每个条目将是文件路径;如果输入为 TabularDataset,则为 pandas DataFrame
    • response:run() 方法应返回 pandas DataFrame 或数组。 对于 append_row output_action,这些返回的元素将追加到公共输出文件中。 对于 summary_only,将忽略元素的内容。 对于所有的输出操作,每个返回的输出元素都指示输入微型批处理中输入元素的一次成功运行。 确保运行结果中包含足够的数据,以便将输入映射到运行输出结果。 运行输出将写入输出文件中,并且不保证按顺序写入,你应使用输出中某个键将其映射到输入。
%%writefile digit_identification.py
# Snippets from a sample script.
# Refer to the accompanying digit_identification.py
# (https://github.com/Azure/MachineLearningNotebooks/tree/master/how-to-use-azureml/machine-learning-pipelines/parallel-run)
# for the implementation script.

import os
import numpy as np
import tensorflow as tf
from PIL import Image
from azureml.core import Model


def init():
    global g_tf_sess

    # Pull down the model from the workspace
    model_path = Model.get_model_path("mnist")

    # Construct a graph to execute
    tf.reset_default_graph()
    saver = tf.train.import_meta_graph(os.path.join(model_path, 'mnist-tf.model.meta'))
    g_tf_sess = tf.Session()
    saver.restore(g_tf_sess, os.path.join(model_path, 'mnist-tf.model'))


def run(mini_batch):
    print(f'run method start: {__file__}, run({mini_batch})')
    resultList = []
    in_tensor = g_tf_sess.graph.get_tensor_by_name("network/X:0")
    output = g_tf_sess.graph.get_tensor_by_name("network/output/MatMul:0")

    for image in mini_batch:
        # Prepare each image
        data = Image.open(image)
        np_im = np.array(data).reshape((1, 784))
        # Perform inference
        inference_result = output.eval(feed_dict={in_tensor: np_im}, session=g_tf_sess)
        # Find the best probability, and add it to the result list
        best_result = np.argmax(inference_result)
        resultList.append("{}: {}".format(os.path.basename(image), best_result))

    return resultList

如果推理脚本所在的同一目录中包含另一个文件或文件夹,可以通过查找当前工作目录来引用此文件或文件夹。

script_dir = os.path.realpath(os.path.join(__file__, '..',))
file_path = os.path.join(script_dir, "<file_name>")

ParallelRunConfig 的参数

ParallelRunConfigParallelRunStep 实例在 Azure 机器学习管道中的主要配置。 使用它来包装脚本并配置所需的参数,包括所有以下条目:

  • entry_script:作为将在多个节点上并行运行的本地文件路径的用户脚本。 如果 source_directory 存在,则使用相对路径。 否则,请使用计算机上可访问的任何路径。
  • mini_batch_size:传递给单个 run() 调用的微型批处理的大小。 (可选;默认值对于 FileDataset10 个文件,对应 TabularDataset1MB。)
    • 对于 FileDataset,它是最小值为 1 的文件数。 可以将多个文件合并成一个微型批处理。
    • 对于 TabularDataset,它是数据的大小。 示例值为 10241024KB10MB1GB。 建议值为 1MBTabularDataset 中的微批永远不会跨越文件边界。 例如,如果你有各种大小的 .csv 文件,最小的文件为 100 KB,最大的文件为 10 MB。 如果设置 mini_batch_size = 1MB,则大小不到 1 MB 的文件将被视为一个微型批处理。 大小不到 1 MB 的文件将会拆分为多个微型批处理。
  • error_threshold:在处理过程中应忽略的 TabularDataset 记录失败数和 FileDataset 文件失败数。 如果整个输入的错误计数超出此值,则作业将中止。 错误阈值适用于整个输入,而不适用于发送给 run() 方法的单个微型批处理。 范围为 [-1, int.max]-1 部分指示在处理过程中忽略所有失败。
  • output_action:以下值之一指示输出的组织方式:
    • summary_only:用户脚本可存储输出。 ParallelRunStep 仅将输出用于错误阈值计算。
    • append_row:对于所有输入,输出文件夹中将仅创建一个文件来追加所有按行分隔的输出。
  • append_row_file_name:用于自定义 append_row output_action 的输出文件名(可选;默认值为 parallel_run_step.txt)。
  • source_directory:文件夹的路径,这些文件夹包含要在计算目标上执行的所有文件(可选)。
  • compute_target:仅支持 AmlCompute
  • node_count:用于运行用户脚本的计算节点数。
  • process_count_per_node:每个节点的进程数。 最佳做法是设置为一个节点具有的 GPU 或 CPU 数量(可选;默认值为 1)。
  • environment:Python 环境定义。 可以将其配置为使用现有的 Python 环境或设置临时环境。 定义还负责设置所需的应用程序依赖项(可选)。
  • logging_level:日志详细程度。 递增详细程度的值为:WARNINGINFODEBUG。 (可选;默认值为 INFO
  • run_invocation_timeoutrun() 方法调用超时(以秒为单位)。 (可选;默认值为 60
  • run_max_try:微型批处理的 run() 的最大尝试次数。 如果引发异常,则 run() 失败;如果达到 run_invocation_timeout,则不返回任何内容(可选;默认值为 3)。

可以指定 mini_batch_sizenode_countprocess_count_per_nodelogging_levelrun_invocation_timeoutrun_max_try 作为 PipelineParameter 以便在重新提交管道运行时,可以微调参数值。 在此示例中,你将对 mini_batch_sizeProcess_count_per_node 使用 PipelineParameter,并在稍后重新提交运行时更改这些值。

用于创建 ParallelRunStep 的参数

使用脚本、环境配置和参数创建 ParallelRunStep。 将已附加到工作区的计算目标指定为推理脚本的执行目标。 使用 ParallelRunStep 创建批处理推理管道步骤,该步骤采用以下所有参数:

  • name:步骤的名称,但具有以下命名限制:唯一、3-32 个字符和正则表达式 ^[a-z]([-a-z0-9]*[a-z0-9])?$。
  • parallel_run_configParallelRunConfig 对象,如前文所述。
  • inputs:要分区以进行并行处理的一个或多个单类型 Azure 机器学习数据集。
  • side_inputs:无需分区就可以用作辅助输入的一个或多个参考数据或数据集。
  • output:与输出目录相对应的 OutputFileDatasetConfig 对象。
  • arguments:传递给用户脚本的参数列表。 使用 unknown_args 在入口脚本中检索它们(可选)。
  • allow_reuse:当使用相同的设置/输入运行时,该步骤是否应重用以前的结果。 如果此参数为 False,则在管道执行过程中将会为此步骤生成新的运行。 (可选;默认值为 True。)
from azureml.pipeline.steps import ParallelRunStep

parallelrun_step = ParallelRunStep(
    name="predict-digits-mnist",
    parallel_run_config=parallel_run_config,
    inputs=[input_mnist_ds_consumption],
    output=output_dir,
    allow_reuse=True
)

调试方法

调试管道有三种主要方法:

  • 在本地计算机上调试单个管道步骤
  • 使用日志记录和 Application Insights 来隔离并诊断问题根源
  • 将远程调试器附加到 Azure 中运行的管道

在本地调试脚本

管道中最常见的失败之一是域脚本未按预期运行,或者在难以调试的远程计算上下文中包含运行时错误。

管道本身无法在本地运行。 但通过在本地计算机上运行处于隔离状态的脚本,可以更快地进行调试,因为无需等待计算和环境生成过程。 执行此操作需要完成一些开发工作:

  • 如果数据位于云数据存储中,则需要下载并向脚本提供数据。 使用较小的数据样本能够很好地在运行时减少系统开销,并快速获取有关脚本行为的反馈
  • 如果正在尝试模拟某个中间管道步骤,则可能需要手动生成特定脚本预期从前一步骤获取的对象类型
  • 需要定义自己的环境,并复制在远程计算环境中定义的依赖项

在本地环境中运行脚本安装后,可以更轻松地执行调试任务,例如:

  • 附加自定义调试配置
  • 暂停执行和检查对象状态
  • 捕获运行时之前不会公开的类型或逻辑错误

提示

确认脚本按预期运行后,合理的下一步是在单步管道中运行该脚本,然后尝试在包含多个步骤的管道中运行该脚本。

配置、写入和查看管道日志

在本地测试脚本是在开始生成管道之前调试主要代码片段和复杂逻辑的好方法。 在某些时候,需要在实际管道自身运行期间调试脚本,特别是在诊断管道步骤之间的交互过程中发生的行为时。 我们建议在步骤脚本中充分使用 print() 语句,以便可以查看远程执行期间的对象状态和预期值,就像在调试 JavaScript 代码时一样。

日志记录选项和行为

下表提供了用于管道的不同调试选项的信息。 这不是一个详尽的列表,因为除了此处显示的 Azure 机器学习、Python 和 OpenCensus 以外,还有其他选项。

类型 示例 目标 资源
Azure 机器学习 SDK 指标 run.log(name, val) Azure 机器学习门户 UI 如何跟踪试验
azureml.core.Run 类
Python 打印/日志记录 日志 print(val)
logging.info(message)
驱动程序日志、Azure 机器学习设计器 如何跟踪试验

Python 日志记录
OpenCensus Python 日志 logger.addHandler(AzureLogHandler())
logging.log(message)
Application Insights - 跟踪 在 Application Insights 中调试管道

OpenCensus Azure Monitor Exporters(OpenCensus Azure Monitor 导出程序)
Python 日志记录指南

日志记录选项示例

import logging

from azureml.core.run import Run
from opencensus.ext.azure.log_exporter import AzureLogHandler

run = Run.get_context()

# Azure Machine Learning Scalar value logging
run.log("scalar_value", 0.95)

# Python print statement
print("I am a python print statement, I will be sent to the driver logs.")

# Initialize Python logger
logger = logging.getLogger(__name__)
logger.setLevel(args.log_level)

# Plain Python logging statements
logger.debug("I am a plain debug statement, I will be sent to the driver logs.")
logger.info("I am a plain info statement, I will be sent to the driver logs.")

handler = AzureLogHandler(connection_string='<connection string>')
logger.addHandler(handler)

# Python logging with OpenCensus AzureLogHandler
logger.warning("I am an OpenCensus warning statement, find me in Application Insights!")
logger.error("I am an OpenCensus error statement with custom dimensions", {'step_id': run.id})

Azure 机器学习设计器

对于在设计器中创建的管道,可以在创作页或管道运行详细信息页中找到 70_driver_log 文件。

为实时终结点启用日志记录

若要在设计器中排查和调试实时终结点问题,必须使用 SDK 启用 Application Insight 日志记录。 使用日志记录可排查和调试模型部署和使用问题。 有关详细信息,请参阅对部署的模型进行日志记录

从创作页获取日志

提交管道运行并停留在创作页时,可以找到在每个组件完成运行时为每个组件生成的日志文件。

  1. 选择已完成在创作画布中运行的组件。

  2. 在该组件的右窗格中,转到“输出 + 日志”选项卡。

  3. 展开右窗格,然后选择 70_driver_log.txt 并在浏览器中查看该文件。 还可以在本地下载日志。

    设计器中展开的输出窗格

从管道运行获取日志

还可以在管道运行详细信息页找到特定运行的日志文件,文件位于工作室的“管道”或“试验”部分。

  1. 选择在设计器中创建的一个管道运行。

    管道运行页

  2. 在预览窗格中选择一个组件。

  3. 在该组件的右窗格中,转到“输出 + 日志”选项卡。

  4. 展开右侧窗格以在浏览器中查看 std_log.txt 文件,或者选择该文件以将日志下载到本地。

重要

若要从管道运行详细信息页更新管道,必须将管道运行克隆到新管道草稿。 管道运行是管道的快照。 它类似于日志文件,并且无法更改。

Application Insights

有关以此方式使用 OpenCensus Python 库的详细信息,请参阅此指南:在 Application Insights 中对机器学习管道进行调试和故障排除

使用 Visual Studio Code 进行交互式调试

在某些情况下,可能需要以交互方式调试 ML 管道中使用的 Python 代码。 通过使用 Visual Studio Code (VS Code) 和 debugpy,可以在训练环境中运行代码时附加到该代码。 有关详细信息,请访问在 VS Code 指南中进行交互式调试

HyperdriveStep 和 AutoMLStep 因网络隔离而失败

使用 HyperdriveStep 和 AutoMLStep 后,尝试注册模型时可能会遇到错误。

  • 你使用的是 Azure 机器学习 SDK v1。

  • Azure 机器学习工作区配置为进行网络隔离 (VNet)。

  • 管道尝试注册上一步生成的模型。 例如,在以下示例中,inputs 参数是 HyperdriveStep 中的saved_model:

    register_model_step = PythonScriptStep(script_name='register_model.py',
                                       name="register_model_step01",
                                       inputs=[saved_model],
                                       compute_target=cpu_cluster,
                                       arguments=["--saved-model", saved_model],
                                       allow_reuse=True,
                                       runconfig=rcfg)
    

解决方法

重要

使用 Azure 机器学习 SDK v2 时不会发生此行为。

若要解决此错误,请使用 Run 类获取从 HyperdriveStep 或 AutoMLStep 创建的模型。 下面是从 HyperdriveStep 获取输出模型的示例脚本:

%%writefile $script_folder/model_download9.py
import argparse
from azureml.core import Run
from azureml.pipeline.core import PipelineRun
from azureml.core.experiment import Experiment
from azureml.train.hyperdrive import HyperDriveRun
from azureml.pipeline.steps import HyperDriveStepRun

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--hd_step_name', 
        type=str, dest='hd_step_name', 
        help='The name of the step that runs AutoML training within this pipeline')
        
        
    
    args = parser.parse_args()
    
    current_run = Run.get_context()

    pipeline_run = PipelineRun(current_run.experiment, current_run.experiment.name)

    hd_step_run = HyperDriveStepRun((pipeline_run.find_step_run(args.hd_step_name))[0])
    hd_best_run = hd_step_run.get_best_run_by_primary_metric()

    print(hd_best_run)
    hd_best_run.download_file("outputs/model/saved_model.pb", "saved_model.pb")
    
    
    print("Successfully downloaded model") 

然后,可以从 PythonScriptStep 使用该文件:

from azureml.pipeline.steps import PythonScriptStep
conda_dep = CondaDependencies()
conda_dep.add_pip_package("azureml-sdk")
conda_dep.add_pip_package("azureml-pipeline")

rcfg = RunConfiguration(conda_dependencies=conda_dep)

model_download_step = PythonScriptStep(
    name="Download Model 9",
    script_name="model_download9.py", 
    arguments=["--hd_step_name", hd_step_name],
    compute_target=compute_target,
    source_directory=script_folder,
    allow_reuse=False,
    runconfig=rcfg
)

后续步骤