Resolver problemas nos pipelines de aprendizagem automática

APLICA-SE A:SDK python azureml v1

Neste artigo, vai aprender a resolver problemas quando obtém erros ao executar um pipeline de machine learning no SDK do Azure Machine Learning e no estruturador do Azure Machine Learning.

Sugestões de resolução de problemas

A tabela seguinte apresenta problemas comuns durante o desenvolvimento de pipelines, com soluções possíveis.

Problema Solução possível
Não é possível transmitir dados para o diretório PipelineData Verifique se criou um diretório no script correspondente ao local onde o pipeline aguarda os dados de saída do passo. Na maioria dos casos, um argumento de entrada define o diretório de saída e, em seguida, cria o diretório explicitamente. Utilize os.makedirs(args.output_dir, exist_ok=True) para criar o diretório de saída. Veja o tutorial para obter um exemplo de script de classificação que mostra este padrão de design.
Erros de dependência Se vir erros de dependência no pipeline remoto que não ocorria durante o teste local, confirme se as dependências e versões do ambiente remoto correspondem às do seu ambiente de teste. (Veja Criação, colocação em cache e reutilização do ambiente
Erros ambíguos com destinos de computação Tente eliminar e recriar os destinos de computação. A recriação de destinos de computação é rápida e pode resolver alguns problemas transitórios.
O pipeline não está a reutilizar os passos A reutilização de passos está ativada por predefinição, mas garanta que não a desativou num passo do pipeline. Se a reutilização estiver desativada, o allow_reuse parâmetro no passo está definido como False.
O pipeline está a voltar a ser executado desnecessariamente Para garantir que os passos só voltam a ser executados quando os dados ou scripts subjacentes forem alterados, desassocie os diretórios de código de origem de cada passo. Se utilizar o mesmo diretório de origem para vários passos, poderão ocorrer repetições desnecessárias. Utilize o source_directory parâmetro num objeto de passo do pipeline para apontar para o diretório isolado desse passo e certifique-se de que não está a utilizar o mesmo source_directory caminho para vários passos.
Passo a abrandar ao longo das épocas da preparação ou outro comportamento em ciclo Tente mudar as escritas de ficheiros, incluindo o registo, de as_mount() para as_upload(). O modo de montagem utiliza um sistema de ficheiros virtualizado remoto e carrega todo o ficheiro sempre que este é anexado.
O destino de computação demora muito tempo a ser iniciado As imagens do Docker para destinos de computação são carregadas do Azure Container Registry (ACR). Por predefinição, o Azure Machine Learning cria um ACR que utiliza o escalão de serviço básico . Mudar o ACR da área de trabalho para o escalão standard ou premium pode reduzir o tempo de criação e carregamento de imagens. Para obter mais informações, veja Escalões de serviço do Azure Container Registry (ACR).

Erros de autenticação

Se executar uma operação de gestão num destino de computação a partir de uma tarefa remota, 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, receberá um erro se tentar criar ou anexar um destino de computação a partir de um Pipeline de ML submetido para execução remota.

Resolução de problemas ParallelRunStep

O script de um tem de ParallelRunStepconter duas funções:

  • init(): utilize esta função para qualquer preparação dispendiosa ou comum para inferência posterior. Por exemplo, utilize-o para carregar o modelo para um objeto global. Esta função é chamada apenas uma vez no início do processo.
  • run(mini_batch): a função é executada para cada mini_batch instância.
    • mini_batch: ParallelRunStep invoca o método run e transmite uma lista ou pandas DataFrame como um argumento para o método . Cada entrada no mini_batch é um caminho de ficheiro se a entrada for um FileDataset ou um pandas DataFrame se a entrada for um TabularDataset.
    • response: o método run() deve devolver um pandas DataFrame ou uma matriz. Por append_row output_action, estes elementos devolvidos são acrescentados ao ficheiro de saída comum. Por summary_only, os conteúdos dos elementos são ignorados. Para todas as ações de saída, cada elemento de saída devolvido indica uma execução bem-sucedida do elemento de entrada no mini-lote de entrada. Confirme que estão incluídos dados suficientes no resultado da execução para mapear a entrada para executar o resultado da saída. A saída de execução é escrita no ficheiro de saída e não é garantido que esteja por ordem. Deve utilizar alguma chave na saída para mapeá-la para 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 tiver outro ficheiro ou pasta no mesmo diretório que o script de inferência, pode referenciá-lo ao localizar 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 configuração principal, por ParallelRunStep exemplo, no pipeline do Azure Machine Learning. Utilize-o para encapsular o script e configurar os parâmetros necessários, incluindo todas as seguintes entradas:

  • entry_script: um script de utilizador como um caminho de ficheiro local que é executado em paralelo em vários nós. Se source_directory estiver presente, utilize um caminho relativo. Caso contrário, utilize qualquer caminho acessível no computador.
  • mini_batch_size: o tamanho do mini-lote transmitido para uma única run() chamada. (opcional; o valor predefinido é 10 ficheiros para FileDataset e 1MB para TabularDataset.)
    • Para FileDataset, é o número de ficheiros com um valor mínimo de 1. Pode combinar múltiplos ficheiros num mini-lote.
    • Para TabularDataset, é o tamanho dos dados. Os valores de exemplo são 1024, 1024KB, 10MBe 1GB. O valor recomendado é 1MB. O mini-lote de TabularDataset nunca irá ultrapassar os limites dos ficheiros. Por exemplo, se tiver .csv ficheiros com vários tamanhos, o ficheiro mais pequeno é de 100 KB e o maior é de 10 MB. Se definir mini_batch_size = 1MB, os ficheiros com um tamanho inferior a 1 MB são tratados como um mini-lote. Os ficheiros com um tamanho superior a 1 MB são divididos em vários mini-lotes.
  • error_threshold: o número de falhas de registo para TabularDataset e falhas de ficheiros para FileDataset o mesmo deve ser ignorado durante o processamento. Se a contagem de erros de toda a entrada for superior a este valor, a tarefa será abortada. O limiar de erro destina-se a toda a entrada e não ao mini-lote individual enviado para o run() método . O intervalo é [-1, int.max]. A -1 parte indica ignorar todas as falhas durante o processamento.
  • output_action: um dos seguintes valores indica como o resultado está organizado:
    • summary_only: o script de utilizador armazena a saída. ParallelRunStep utiliza a saída apenas para o cálculo do limiar de erro.
    • append_row: para todas as entradas, só é criado um ficheiro na pasta de saída para acrescentar todas as saídas separadas por linha.
  • append_row_file_name: para personalizar o nome do ficheiro de saída para append_row output_action (opcional; o valor predefinido é parallel_run_step.txt).
  • source_directory: caminhos para pastas que contêm todos os ficheiros a executar no destino de computação (opcional).
  • compute_target: só AmlCompute é suportado.
  • node_count: o número de nós de computação a utilizar para executar o script de utilizador.
  • process_count_per_node: o número de processos por nó. A melhor prática é definir para o número de GPU ou CPU que um nó tem (opcional; o valor predefinido é 1).
  • environment: a definição de ambiente python. Pode configurá-lo para utilizar um ambiente Python existente ou para configurar um ambiente temporário. A definição também é responsável por definir as dependências de aplicação necessárias (opcional).
  • logging_level: verbosidade do registo. Os valores no aumento da verbosidade são: WARNING, INFOe DEBUG. (opcional; o valor predefinido é INFO)
  • run_invocation_timeout: o run() tempo limite de invocação do método em segundos. (opcional; o valor predefinido é 60)
  • run_max_try: número máximo de tentativas de run() para um mini-lote. A run() falha se for emitida uma exceção ou se não for devolvida nada quando run_invocation_timeout for atingido (opcional; o valor predefinido é 3).

Pode especificar mini_batch_size, node_count, process_count_per_node, logging_level, run_invocation_timeoute run_max_try como PipelineParameter, para que, quando submeter novamente uma execução de pipeline, possa ajustar os valores dos parâmetros. Neste exemplo, utiliza PipelineParameter e mini_batch_sizeProcess_count_per_node altera estes valores quando submeter novamente uma execução mais tarde.

Parâmetros para criar o ParallelRunStep

Crie o ParallelRunStep com o script, a configuração do ambiente e os parâmetros. Especifique o destino de computação que já anexou à área de trabalho como o destino da execução do script de inferência. Utilize ParallelRunStep para criar o passo do pipeline de inferência de lotes, que utiliza todos os seguintes parâmetros:

  • name: o nome do passo, com as seguintes restrições de nomenclatura: exclusivo, 3-32 carateres e regex ^[a-z]([-a-z0-9]*[a-z0-9])?$.
  • parallel_run_config: um ParallelRunConfig objeto, 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 utilizados como entradas laterais sem necessidade de particionar.
  • output: um OutputFileDatasetConfig objeto que corresponde ao diretório de saída.
  • arguments: uma lista de argumentos transmitidos ao script de utilizador. Utilize unknown_args para os obter no script de entrada (opcional).
  • allow_reuse: se o passo deve reutilizar os resultados anteriores quando executado com as mesmas definições/entradas. Se este parâmetro for False, é gerada uma nova execução para este passo durante a execução do pipeline. (opcional; o valor predefinido é 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

Existem três técnicas principias para depurar pipelines:

  • Depurar os passos de pipeline individuais no computador local
  • Utilizar o registo e o Application Insights para isolar e diagnosticar a origem do problema
  • Anexar um depurador remoto a um pipeline em execução no Azure

Depurar scripts localmente

Uma das falhas mais comuns num pipeline é que o script de domínio não é executado conforme pretendido ou contém erros de runtime no contexto de computação remota que são difíceis de depurar.

Os próprios pipelines não podem ser executados localmente. No entanto, executar os scripts isoladamente no computador local permite-lhe depurar mais rapidamente porque não tem de esperar pelo processo de compilação de ambiente e computação. É necessário algum trabalho de desenvolvimento para o fazer:

  • Se os seus dados estiverem num arquivo de dados na cloud, terá de transferir dados e disponibilizá-lo para o script. Utilizar uma pequena amostra dos seus dados é uma boa forma de reduzir o runtime e obter rapidamente feedback sobre o comportamento do script
  • Se estiver a tentar simular um passo de pipeline intermédio, poderá ter de criar manualmente os tipos de objeto que o script específico espera do passo anterior
  • Tem de definir o seu próprio ambiente e replicar as dependências definidas no seu ambiente de computação remota

Assim que tiver uma configuração de script para ser executada no seu ambiente local, é mais fácil efetuar a depuração de tarefas como:

  • Anexar uma configuração de depuração personalizada
  • Colocar em pausa a execução e inspecionar o estado do objeto
  • Captura de tipo ou erros lógicos que não serão expostos até ao runtime

Dica

Assim que puder verificar se o script está em execução conforme esperado, um bom passo seguinte é executar o script num pipeline de um único passo antes de tentar executá-lo num pipeline com vários passos.

Configurar, escrever e rever registos de pipelines

Testar scripts localmente é uma excelente forma de depurar fragmentos de código principais e lógica complexa antes de começar a criar um pipeline. A dada altura, tem de depurar scripts durante a execução real do pipeline, especialmente quando diagnosticar o comportamento que ocorre durante a interação entre os passos do pipeline. Recomendamos a utilização liberal de print() instruções nos seus scripts de passo para que possa ver o estado do objeto e os valores esperados durante a execução remota, semelhante à forma como depuraria o código JavaScript.

Opções de registo e comportamento

A tabela seguinte fornece informações para diferentes opções de depuração para pipelines. Não é uma lista exaustiva, uma vez que existem outras opções para além das opções do Azure Machine Learning, Python e OpenCensus apresentadas aqui.

Biblioteca Tipo Exemplo Destino Recursos
Azure Machine Learning SDK Metric run.log(name, val) IU do Portal do Azure Machine Learning Como monitorizar as experimentações
azureml.core.Run class
Registo/impressão Python Registo print(val)
logging.info(message)
Registos do controlador, estruturador do Azure Machine Learning Como monitorizar as experimentações

Registo Python
OpenCensus Python Registo logger.addHandler(AzureLogHandler())
logging.log(message)
Application Insights – rastreios Depurar pipelines no Application Insights

OpenCensus Azure Monitor Exporters (Exportadores do Azure Monitor do OpenCensus)
Guia detalhado do registo Python

Exemplo de opções de registo

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

Estruturador do Azure Machine Learning

Para pipelines criados no estruturador, pode encontrar o ficheiro 70_driver_log na página de criação ou na página de detalhes da execução do pipeline.

Ativar o registo para pontos finais em tempo real

Para resolver problemas e depurar pontos finais em tempo real no estruturador, tem de ativar o registo do Application Insight com o SDK. O registo permite-lhe resolver problemas de implementação e utilização de modelos de depuração. Para obter mais informações, veja Registo de modelos implementados.

Obter registos da página de criação

Quando submete uma execução de pipeline e permanece na página de criação, pode encontrar os ficheiros de registo gerados para cada componente à medida que cada componente termina a execução.

  1. Selecione um componente que tenha terminado de ser executado na tela de criação.

  2. No painel direito do componente, aceda ao separador Saídas + registos .

  3. Expanda o painel direito e selecione o 70_driver_log.txt para ver o ficheiro no browser. Também pode transferir registos localmente.

    Painel de saída expandido no estruturador

Obter registos de execuções de pipelines

Também pode encontrar os ficheiros de registo para execuções específicas na página de detalhes da execução do pipeline, que pode ser encontrada na secção Pipelines ou Experiments do estúdio.

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

    Página de execução do pipeline

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

  3. No painel direito do componente, aceda ao separador Saídas + registos .

  4. Expanda o painel direito para ver o ficheiro std_log.txt no browser ou selecione o ficheiro para transferir os registos localmente.

Importante

Para atualizar um pipeline a partir da página de detalhes da execução do pipeline, tem de clonar a execução do pipeline para um novo rascunho de pipeline. Uma execução de pipeline é um instantâneo do pipeline. É semelhante a um ficheiro de registo e não pode ser alterado.

Application Insights

Para obter mais informações sobre como utilizar a biblioteca Python do OpenCensus desta forma, veja este guia: Depurar e resolver problemas de pipelines de machine learning no Application Insights

Depuração interativa com o Visual Studio Code

Em determinados casos, poderá ter de depurar interativamente o código do Python utilizado no pipeline do ML. Ao utilizar o Visual Studio Code (VS Code) e debugpy, pode anexar ao código tal como é executado no ambiente de preparação. Para obter mais informações, veja a depuração interativa no guia do VS Code.

HyperdriveStep e AutoMLStep falham com isolamento de rede

Depois de utilizar HyperdriveStep e AutoMLStep, quando tentar registar o modelo, poderá receber um erro.

  • Está a utilizar o SDK do Azure Machine Learning v1.

  • A área de trabalho do Azure Machine Learning está configurada para isolamento de rede (VNet).

  • O pipeline tenta registar o modelo gerado pelo passo anterior. Por exemplo, no exemplo seguinte, o inputs parâmetro é o saved_model 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

Importante

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

Para contornar este erro, utilize a classe Executar para criar o modelo a partir do HyperdriveStep ou autoMLStep. Segue-se um script de exemplo 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") 

Em seguida, o ficheiro 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
)

Passos seguintes