Solucionar problemas de pipelines de aprendizado de máquina

APLICA-SE A:azureml do SDK do Python v1

Neste artigo, você aprenderá a solucionar problemas ao obter erros ao executar um pipeline de machine learning no SDK do Azure Machine Learning e no designer do Azure Machine Learning.

Dicas de solução de problemas

A tabela a seguir contém problemas comuns durante o desenvolvimento de pipeline, com possíveis soluções.

Problema Solução possível
Não é possível passar dados para o diretório PipelineData Verifique se você criou um diretório no script que corresponde ao local em que seu pipeline espera os dados de saída da etapa. Na maioria dos casos, um argumento de entrada define o diretório de saída e, em seguida, você cria o diretório explicitamente. Use os.makedirs(args.output_dir, exist_ok=True) para criar o diretório de saída. Consulte o tutorial para obter um exemplo de script de pontuação que mostra esse padrão de design.
Bugs de dependência Se você vir erros de dependência em seu pipeline remoto que não ocorreram durante o teste local, confirme se as suas dependências e versões de ambiente remoto correspondem àquelas em seu ambiente de teste. (Confira Criação de ambiente, cache e reutilização)
Erros ambíguos com destinos de computação Tente excluir e recriar os destinos de computação. Recriar destinos de computação é rápido e pode resolver alguns problemas transitórios.
O pipeline não está reutilizando as etapas A reutilização de etapa é habilitada por padrão, mas certifique-se de que você não a desabilitou em uma etapa de pipeline. Se a reutilização estiver desabilitada, o allow_reuse parâmetro na etapa será definido como False.
O pipeline está sendo executado desnecessariamente Para garantir que as etapas sejam executadas somente quando seus dados ou scripts subjacentes forem alterados, desassocie os diretórios de código-fonte para cada etapa. Se usar o mesmo diretório de origem para várias etapas, poderá ocorrer uma reexecução desnecessária. Use o parâmetro source_directory em um objeto etapa de pipeline para apontar para seu diretório isolado para essa etapa e verifique se você não está usando o mesmo caminho source_directory para várias etapas.
Etapa reduzindo as épocas de treinamento ou outro comportamento de looping Tente alternar qualquer gravação de arquivo, incluindo registro em log, de as_mount() para as_upload(). O modo de montagem usa um sistema de arquivos virtualizado remoto e carrega todo o arquivo cada vez que é anexado.
O destino de computação leva muito tempo para iniciar As imagens do Docker para destinos de computação são carregadas do ACR (Registro de Contêiner do Azure). Por padrão, o Azure Machine Learning cria um ACR que usa a camada de serviço básica. Alterar o ACR para o espaço de trabalho para a camada Standard ou Premium pode reduzir o tempo necessário para criar e carregar imagens. Para obter mais informações, confira Níveis de serviço do Registro de Contêiner do Azure.

Erros de autenticação

Se executar uma operação de gerenciamento em um destino de computação de um trabalho remoto, você receberá um dos seguintes erros:

{"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."}}

Por exemplo, você receberá um erro se tentar criar ou anexar um destino de computação de um Pipeline de ML que é enviado para execução remota.

Solução de problemas ParallelRunStep

O script de um ParallelRunStepdeve conter duas funções:

  • init(): Use essa função para qualquer preparação dispendiosa ou comum para inferência posterior. Por exemplo, use-a para carregar o modelo em um objeto global. Essa função é chamada apenas uma vez no início do processo.
  • run(mini_batch): a função é executada para cada instância de mini_batch.
    • mini_batch: ParallelRunStep invoca o método run e transmitirá uma lista ou o DataFrame Pandas como um argumento para o método. Cada entrada em min_batch é um caminho de arquivo se a entrada for um FileDataset ou um DataFrame Pandas se a entrada for um TabularDataset.
    • response: o método run() deve retornar um DataFrame Pandas ou uma matriz. Para append_row output_action, esses elementos retornados são acrescentados ao arquivo de saída comum. Para summary_only, o conteúdo dos elementos é ignorado. Para todas as ações de saída, cada elemento de saída retornado indica uma execução bem-sucedida do elemento de entrada no minilote de entrada. Verifique se dados suficientes foram incluídos no resultado da execução para mapear a entrada para o resultado da saída da execução. A saída de execução é gravada no arquivo de saída e não é garantido que esteja em ordem; você deverá usar uma chave na saída para mapeá-la para a entrada.
%%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

Se você tiver outro arquivo ou pasta no mesmo diretório que o script de inferência, poderá consultá-lo localizando o diretório de trabalho atual.

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

Parâmetros para ParallelRunConfig

ParallelRunConfig é a principal configuração para a instância ParallelRunStep no pipeline do Azure Machine Learning. Use-a para encapsular o script e configurar os parâmetros necessários, incluindo todas as seguintes entradas:

  • entry_script: um script de usuário como um caminho de arquivo local que é executado em paralelo em vários nós. Se source_directory estiver presente, use um caminho relativo. Caso contrário, use qualquer caminho que seja acessível no computador.
  • mini_batch_size: O tamanho do minilote passado para uma única chamada de run(). (opcional; o valor padrão são arquivos 10 para FileDataset e 1MB para TabularDataset.)
    • Para FileDataset, é o número de arquivos com um valor mínimo de 1. Você pode combinar vários arquivos em um minilote.
    • Para TabularDataset, é o tamanho dos dados. Os valores de exemplo são 1024, 1024KB, 10MB e 1GB. O valor recomendado é 1MB. O minilote de TabularDataset nunca ultrapassará os limites do arquivo. Por exemplo, se você tiver arquivos .csv com vários tamanhos, o menor arquivo será de 100 KB, e o maior será de 10 MB. Se você definir mini_batch_size = 1MB, os arquivos com um tamanho menor que 1 MB são tratados como um minilote. Arquivos com um tamanho maior que 1 MB são divididos em vários minilotes.
  • error_threshold: O número de falhas de registro para TabularDataset e falhas de arquivo para FileDataset que devem ser ignorados durante o processamento. Se a contagem de erros de toda a entrada ficar acima desse valor, o trabalho é anulado. O limite de erro é para toda a entrada, não para um minilote individual enviado ao método run(). O intervalo é [-1, int.max]. A parte -1 indica que é para ignorar todas as falhas durante o processamento.
  • output_action: um dos seguintes valores indica como a saída é organizada:
    • summary_only: O script de usuário armazena a saída. ParallelRunStep usa a saída somente para o cálculo do limite de erro.
    • append_row: para todas as entradas, somente um arquivo é criado na pasta de saída para acrescentar todas as saídas separadas por linha.
  • append_row_file_name: para personalizar o nome do arquivo de saída para append_row output_action (opcional; o valor padrão é parallel_run_step.txt).
  • source_directory: Caminhos para pastas que contêm todos os arquivos a serem executados no destino de computação (opcional).
  • compute_target: Apenas AmlCompute tem suporte.
  • node_count: O número de nós de computação a serem usados para executar o script do usuário.
  • process_count_per_node: O número de processos por nó. A melhor prática é definir como o número de GPU ou CPU que um nó tem (opcional; o valor padrão é 1).
  • environment: A definição de ambiente Python. Você pode configurá-lo para usar um ambiente Python existente ou para configurar um ambiente temporário. A definição também é responsável por configurar as dependências de aplicativo necessárias (opcional).
  • logging_level: Detalhamento do log. Os valores no detalhamento crescente são: WARNING, INFO e DEBUG. (opcional; o valor padrão é INFO)
  • run_invocation_timeout: O tempo limite de invocação do método run() em segundos. (opcional; o valor padrão é 60)
  • run_max_try: contagem máxima de tentativas de run() para um minilote. Um run() falhará se uma exceção for gerada ou nada será retornado quando run_invocation_timeout for atingido (opcional; o valor padrão é 3).

Especifique mini_batch_size, node_count, process_count_per_node, logging_level, run_invocation_timeout e run_max_try como PipelineParameter, de modo que, ao reenviar uma execução de pipeline, você possa ajustar os valores de parâmetro. Nesse exemplo, você usa PipelineParameter para mini_batch_size e Process_count_per_node, e altera esses valores quando reenviar uma execução posteriormente.

Parâmetros para criar o ParallelRunStep

Crie o ParallelRunStep usando o script, a configuração do ambiente e os parâmetros. Especifique o destino de computação que você já anexou ao seu workspace como o destino de execução do seu script de inferência. Use ParallelRunStep para criar a etapa do pipeline de inferência de lote, que usa todos os seguintes parâmetros:

  • name: O nome da etapa, com as seguintes restrições de nomenclatura: unique, 3-32 characters e regex ^[a-z]([-a-z0-9]*[a-z0-9])?$.
  • parallel_run_config: Um objeto ParallelRunConfig, conforme definido anteriormente.
  • inputs: um ou mais conjuntos de dados do Azure Machine Learning de tipo único a serem particionados para processamento paralelo.
  • side_inputs: um ou mais dados de referência ou conjuntos de dados usados como entradas laterais sem a necessidade de partição.
  • output: um objeto OutputFileDatasetConfig que corresponde ao diretório de saída.
  • arguments: uma lista de argumentos passados para o script do usuário. Use unknown_args para recuperá-los em seu script de entrada (opcional).
  • allow_reuse: Se a etapa deve reutilizar os resultados anteriores quando executada com as mesmas configurações/entradas. Se esse parâmetro for False, uma nova execução será gerada para essa etapa durante a execução do pipeline. (opcional; o valor padrão é 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
)

Técnicas de depuração

Há três técnicas principais para a depuração de pipelines:

  • Depurar etapas de pipeline individuais no computador local
  • Usar registro em log e Application Insights para isolar e diagnosticar a origem do problema
  • Anexar um depurador remoto a um pipeline em execução no Azure

Testar scripts localmente

Uma das falhas mais comuns em um pipeline é que o script de domínio não é executado conforme o esperado, ou contém erros de tempo de execução no contexto de computação remota, que são difíceis de depurar.

Os pipelines propriamente ditos não podem ser executados localmente. No entanto, a execução dos scripts em isolamento no computador local permite depurar mais rápido porque não precisa esperar o processo de compilação do ambiente e da computação. É necessário algum trabalho de desenvolvimento para fazer isso:

  • Se seus dados estiverem em um armazenamento em nuvem, será necessário baixar os dados e disponibilizá-los para o script. Usar uma pequena amostra de seus dados é uma boa maneira de reduzir o tempo de execução e obter rapidamente comentários sobre o comportamento do script
  • Se estiver tentando simular uma etapa intermediária do pipeline, talvez seja necessário criar manualmente os tipos de objeto que o script específico está esperando na etapa anterior
  • Você precisa definir seu próprio ambiente e replicar as dependências definidas em seu ambiente de computação remota

Quando tiver uma configuração de script para ser executada no seu ambiente local, é mais fácil fazer tarefas de depuração, tais como:

  • Anexar uma configuração de depuração personalizada
  • Pausar a execução e inspecionar o estado do objeto
  • Capturar o tipo ou erros lógicos que só serão expostos em tempo de execução

Dica

Depois que confirmar que o script está sendo executado conforme o esperado, uma boa próxima etapa é executar o script em um pipeline de etapa única antes de tentar executá-lo em um pipeline com várias etapas.

Configurar, gravar e examinar logs de pipeline

Testar scripts localmente é uma ótima maneira de depurar os principais fragmentos de código e a lógica complexa antes de começar a criar um pipeline. Em algum momento, você precisará depurar scripts durante a execução real do pipeline, principalmente ao diagnosticar o comportamento que ocorre durante a interação entre as etapas do pipeline. É recomendável liberal o uso de instruções print() nos scripts de etapa para que seja possível ver o estado do objeto e os valores esperados durante a execução remota, semelhante a como seria a depuração de código JavaScript.

Opções e comportamento de registro em log

A tabela a seguir fornece informações para diferentes opções de depuração para pipelines. Não é uma lista completa, já que existem outras opções, além de Azure Machine Learning, Python e OpenCensus, mostradas aqui.

Biblioteca Type Exemplo Destino Recursos
SDK do Azure Machine Learning Métrica run.log(name, val) Portal de Interface do usuário do Azure Machine Learning Como acompanhar experimentos
azureml.core.Run class
Impressão/registro em log do Python Registro print(val)
logging.info(message)
Logs de driver, designer do Azure Machine Learning Como acompanhar experimentos

Registro em log do Python
OpenCensus Python Registro logger.addHandler(AzureLogHandler())
logging.log(message)
Application Insights – rastreamento Depurar pipelines no Application Insights

Exportadores OpenCensus Azure Monitor
Guia de registro em log do Python

Exemplo de opções de registro em log

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})

Designer do Azure Machine Learning

Para pipelines criados no designer, é possível encontrar o arquivo 70_driver_log na página de criação ou na página de detalhes de execução de pipeline.

Habilitar o registro em log para pontos de extremidade em tempo real

Para solucionar problemas e depurar pontos de extremidade em tempo real no designer, é necessário habilitar o registro em log do Application Insight usando o SDK. O registro em log permite solucionar problemas e depurar questões de uso e implantação de modelo. Para obter mais informações, consulte Logging for deployed Models.

Obter logs da página de criação

Ao enviar uma execução de pipeline e permanecer na página de criação, será possível encontrar os arquivos de log gerados para cada componente, à medida que cada componente terminar a execução.

  1. Selecione um componente que concluiu a execução na tela de criação.

  2. No painel direito do componente, acesse a guia Saídas e logs.

  3. Expanda o painel direito e selecione o 70_driver_log.txt para exibir o arquivo no navegador. Também é possível baixar os logs localmente.

    Painel de saída expandido no designer

Obter logs de execuções de pipeline

Também é possível encontrar os arquivos de log para execuções específicas na página de detalhes de execução do pipeline, que pode ser encontrada na seção Pipelines ou Experimentos do estúdio.

  1. Selecione uma execução de pipeline criada no designer.

    Página de execução do pipeline

  2. Selecione um componente no painel de visualização.

  3. No painel direito do componente, acesse a guia Saídas e logs.

  4. Expanda o painel direito para ver o arquivo std_log.txt no navegador ou selecione o arquivo para baixar os logs localmente.

Importante

Para atualizar um pipeline da página de detalhes de execução do pipeline, é necessário clonar a execução do pipeline para um novo rascunho do pipeline. Uma execução de pipeline é um instantâneo do pipeline. Ele é semelhante a um arquivo de log e não pode ser alterado.

Application Insights

Para obter mais informações sobre como usar a biblioteca do OpenCensus Python dessa maneira, consulte este guia: Depurar e solucionar problemas de pipelines de machine learning no Application Insights

Depuração interativa com o Visual Studio Code

Em alguns casos, talvez seja necessário depurar interativamente o código Python usado no pipeline de ML. Usando Visual Studio Code (VS Code) e debugpy, é possível anexar ao código conforme ele é executado no ambiente de treinamento. Para obter mais informações, acesse a Depuração interativa no guia VS Code.

HyperdriveStep e AutoMLStep falham com o isolamento de rede

Depois de usar o HyperdriveStep e o AutoMLStep, quando você tentar registrar o modelo, poderá receber um erro.

  • Você está usando o SDK do Azure Machine Learning v1.

  • Seu workspace do Azure Machine Learning está configurado para o isolamento de rede (VNet).

  • Seu pipeline tentará registrar o modelo gerado pela etapa anterior. No exemplo a seguir, o parâmetro inputs é o modelo_salvo de um HyperdriveStep:

    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)
    

Solução alternativa

Importante

Esse comportamento não ocorre ao utilizar o SDK do Azure Machine Learning v2.

Para contornar esse erro, utilize a classe Executar para obter o modelo criado a partir do HyperdriveStep ou AutoMLStep. A seguir está um exemplo de script que obtém o modelo de saída de um 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") 

O arquivo pode ser utilizado a partir de um 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
)

Próximas etapas