Crie e execute pipelines de machine learning usando componentes com o SDK v2 do Azure Machine Learning

APLICA-SE A: SDK do Python azure-ai-ml v2 (atual)

Neste artigo, você saberá como criar um pipeline do Azure Machine Learning usando o SDK v2 do Python para concluir uma tarefa de classificação de imagem contendo três etapas: preparar dados, treinar um modelo de classificação de imagem e pontuar o modelo. Os pipelines de aprendizado de máquina otimizam o fluxo de trabalho com velocidade, portabilidade e reutilização, de modo que você possa se concentrar no aprendizado de máquina em vez de na infraestrutura e na automação.

O exemplo treina uma pequena rede neural convolucional Keras para classificar imagens no conjunto de dados Fashion MNIST. O pipeline é semelhante ao seguinte.

Captura de tela mostrando o grafo de pipeline do exemplo Keras de classificação de imagem.

Neste artigo, você concluirá as seguintes tarefas:

  • Preparar os dados de entrada para o trabalho de pipeline
  • Criar três componentes para preparar os dados, treinar e pontuar
  • Compor um pipeline a partir dos componentes
  • Obter acesso ao workspace com computação
  • Enviar o trabalho de pipeline
  • Revisar a saída dos componentes e a rede neural treinada
  • (Opcional) Registre o componente para reutilização e compartilhamento adicionais no workspace

Caso não tenha uma assinatura do Azure, crie uma conta gratuita antes de começar. Experimente hoje mesmo a versão gratuita ou paga do Azure Machine Learning.

Pré-requisitos

  • Workspace do Azure Machine Learning – se você não tiver um, conclua o tutorial Criar recursos.

  • Um ambiente do Python no qual você instalou o SDK v2 do Python do Azure Machine Learning – instruções de instalação – verifique a seção de introdução. Esse ambiente serve para definir e controlar seus recursos do Azure Machine Learning e é separado do ambiente usado em runtime para treinamento.

  • Clonar repositório de exemplos

    Para executar os exemplos de treinamento, primeiro clone o repositório de exemplos e altere para o diretório sdk:

    git clone --depth 1 https://github.com/Azure/azureml-examples
    cd azureml-examples/sdk
    

Iniciar uma sessão interativa do Python

Este artigo usa o SDK do Python para Azure Machine Learning para criar e controlar um pipeline do Azure Machine Learning. Este artigo assume que você executará os trechos de código interativamente em um ambiente REPL do Python ou em um Jupyter Notebook.

Este artigo é baseado no notebook image_classification_keras_minist_convnet.ipynb encontrado no diretório sdk/python/jobs/pipelines/2e_image_classification_keras_minist_convnet do repositório Exemplos do Azure Machine Learning.

Importe as bibliotecas necessárias

Importe todas as bibliotecas necessárias do Azure Machine Learning que você precisará para este artigo:

# import required libraries
from azure.identity import DefaultAzureCredential, InteractiveBrowserCredential

from azure.ai.ml import MLClient
from azure.ai.ml.dsl import pipeline
from azure.ai.ml import load_component

Preparar dados de entrada para o trabalho de pipeline

É necessário preparar os dados de entrada para esse pipeline de classificação de imagem.

O Fashion-MNIST é um conjunto de dados de imagens de moda dividido em dez classes. Cada imagem é uma imagem de escala de cinza 28x28, e há 60 mil imagens de treinamento e dez mil imagens de teste. Como um problema de classificação de imagem, o Fashion-MNIST é mais difícil do que o banco de dados de dígito manuscrito clássico do MNIST. Ele é distribuído no mesmo formato binário compactado do banco de dados de dígito manuscrito original.

Importe todas as bibliotecas necessárias do Azure Machine Learning que você precisará.

Ao definir um Input, você criará uma referência ao local da fonte de dados. Os dados permanecem na localização existente, portanto, nenhum custo de armazenamento extra é gerado.

Criar componentes para a construção de pipeline

A tarefa de classificação de imagem pode ser dividida em três etapas: preparar os dados, treinar o modelo e o modelo de pontuação.

O componente do Azure Machine Learning é uma peça independente do código que realiza uma etapa em um pipeline de machine learning. Neste artigo, você criará três componentes para a tarefa de classificação de imagem:

  • Preparar dados para treinamento e teste
  • Treinar uma rede neural para classificação de imagens por meio de dados de treinamento
  • Pontuar o modelo usando dados de teste

Para cada componente, será necessário preparar os seguintes itens:

  1. Prepare o script do Python que contém a lógica de execução

  2. Definir a interface do componente

  3. Adicione outros metadados do componente, incluindo o ambiente de runtime, o comando para executar o componente e etc.

A próxima seção mostrará os componentes de criação de duas maneiras diferentes: os dois primeiros componentes usando a função Python e o terceiro componente usando a definição YAML.

Criar o componente de preparação de dados

O primeiro componente nesse pipeline converterá os arquivos de dados compactados de fashion_ds em dois arquivos csv, um para treinamento e outro para pontuação. Você usará a função do Python para definir esse componente.

Se você estiver acompanhando o exemplo no repositório de exemplos do Azure Machine Learning, os arquivos de origem já estarão disponíveis na pasta prep/. Essa pasta contém dois arquivos para construir o componente: prep_component.py, que define o componente e conda.yaml, que define o ambiente de runtime do componente.

Definir o componente usando a função do Python

Usando a função command_component() como um decorador, você poderá definir facilmente a interface, os metadados e o código do componente para executar a partir de uma função do Python. Cada função do Python decorada será transformada em uma única especificação estática (YAML) que o serviço de pipeline poderá processar.

# Converts MNIST-formatted files at the passed-in input path to training data output path and test data output path
import os
from pathlib import Path
from mldesigner import command_component, Input, Output


@command_component(
    name="prep_data",
    version="1",
    display_name="Prep Data",
    description="Convert data to CSV file, and split to training and test data",
    environment=dict(
        conda_file=Path(__file__).parent / "conda.yaml",
        image="mcr.microsoft.com/azureml/openmpi4.1.0-ubuntu20.04",
    ),
)
def prepare_data_component(
    input_data: Input(type="uri_folder"),
    training_data: Output(type="uri_folder"),
    test_data: Output(type="uri_folder"),
):
    convert(
        os.path.join(input_data, "train-images-idx3-ubyte"),
        os.path.join(input_data, "train-labels-idx1-ubyte"),
        os.path.join(training_data, "mnist_train.csv"),
        60000,
    )
    convert(
        os.path.join(input_data, "t10k-images-idx3-ubyte"),
        os.path.join(input_data, "t10k-labels-idx1-ubyte"),
        os.path.join(test_data, "mnist_test.csv"),
        10000,
    )


def convert(imgf, labelf, outf, n):
    f = open(imgf, "rb")
    l = open(labelf, "rb")
    o = open(outf, "w")

    f.read(16)
    l.read(8)
    images = []

    for i in range(n):
        image = [ord(l.read(1))]
        for j in range(28 * 28):
            image.append(ord(f.read(1)))
        images.append(image)

    for image in images:
        o.write(",".join(str(pix) for pix in image) + "\n")
    f.close()
    o.close()
    l.close()

O código acima define um componente com nome de exibição Prep Data usando o decorador @command_component:

  • name é o identificador exclusivo do componente.

  • version é a versão atual do componente. Um componente pode ter várias versões.

  • display_name é um nome de exibição amigável do componente na interface do usuário, que não é exclusivo.

  • description geralmente descreve qual tarefa esse componente poderá concluir.

  • environment especifica o ambiente de runtime para esse componente. O ambiente desse componente especifica uma imagem do docker e se refere ao arquivo conda.yaml.

    O arquivo conda.yaml contém todos os pacotes usados para o componente da seguinte maneira:

    name: imagekeras_prep_conda_env
    channels:
      - defaults
    dependencies:
      - python=3.7.11
      - pip=20.0
      - pip:
        - mldesigner==0.1.0b4
    
  • A função prepare_data_component define uma entrada para input_data e duas saídas para training_data e test_data. input_data é o caminho dos dados de entrada. training_data e test_data são caminhos de dados de saída para dados de treinamento e dados de teste.

  • Esse componente converte os dados de input_data em um csv de dados de treinamento para training_data e um csv de dados de teste para test_data.

Veja a seguir a aparência de um componente na interface do usuário do estúdio.

  • Um componente é um bloco em um gráfico de pipeline.
  • input_data, training_data e test_data são portas do componente que conectam outros componentes para transmissão de dados.

Captura de tela do componente Preparação de Dados no código e na interface do usuário.

Agora, você preparou todos os arquivos de origem para o componente Prep Data.

Criar o componente train-model

Nesta seção, você criará um componente para treinar o modelo de classificação de imagem na função do Python como o componente Prep Data.

A diferença é que, como a lógica de treinamento é mais complicada, você poderá colocar o código de treinamento original em um arquivo do Python separado.

Os arquivos de origem desse componente estão na pasta train/ no repositório de exemplos do Azure Machine Learning. Esta pasta contém três arquivos para construir o componente:

  • train.py: contém a lógica real para treinar o modelo.
  • train_component.py: define a interface do componente e importa a função em train.py.
  • conda.yaml: define o ambiente de runtime do componente.

Obter um script contendo a lógica de execução

O arquivo train.py contém uma função do Python normal, que executa a lógica do modelo de treinamento para treinar uma rede neural Keras para a classificação de imagem. Para exibir o código, confira o arquivo train.py no GitHub.

Definir o componente usando a função do Python

Depois de definir a função de treinamento com sucesso, use @command_component no SDK v2 do Azure Machine Learning para encapsular a função como um componente, que poderá ser usado nos pipelines do Azure Machine Learning.

import os
from pathlib import Path
from mldesigner import command_component, Input, Output


@command_component(
    name="train_image_classification_keras",
    version="1",
    display_name="Train Image Classification Keras",
    description="train image classification with keras",
    environment=dict(
        conda_file=Path(__file__).parent / "conda.yaml",
        image="mcr.microsoft.com/azureml/openmpi4.1.0-ubuntu20.04",
    ),
)
def keras_train_component(
    input_data: Input(type="uri_folder"),
    output_model: Output(type="uri_folder"),
    epochs=10,
):
    # avoid dependency issue, execution logic is in train() func in train.py file
    from train import train

    train(input_data, output_model, epochs)

O código acima define um componente com nome de exibição Train Image Classification Keras usando @command_component:

  • A funçãokeras_train_component define uma entrada input_data, da qual os dados de treinamento vêm, uma entrada epochs especificando as épocas durante o treinamento e uma saída output_model, em que o arquivo de modelo é gerado. O valor padrão de epochs é 10. A lógica de execução desse componente é da função train() acima train.py.

O componente train-model tem uma configuração um pouco mais complexa do que o componente prep-data. O conda.yaml é semelhante ao seguinte:

name: imagekeras_train_conda_env
channels:
  - defaults
dependencies:
  - python=3.7.11
  - pip=20.2
  - pip:
    - mldesigner==0.1.0b12
    - azureml-mlflow==1.50.0
    - tensorflow==2.7.0
    - numpy==1.21.4
    - scikit-learn==1.0.1
    - pandas==1.3.4
    - matplotlib==3.2.2
    - protobuf==3.20.0

Agora, você preparou todos os arquivos de origem para o componente Train Image Classification Keras.

Criar o componente score-model

Nesta seção, além dos componentes anteriores, você criará um componente para pontuar o modelo treinado por meio de especificação e script do Yaml.

Se você estiver acompanhando o exemplo no repositório de exemplos do Azure Machine Learning, os arquivos de origem já estarão disponíveis na pasta score/. Esta pasta contém três arquivos para construir o componente:

  • score.py: contém o código-fonte do componente.
  • score.yaml: define a interface e outros detalhes do componente.
  • conda.yaml: define o ambiente de runtime do componente.

Obter um script contendo a lógica de execução

O arquivo score.py contém uma função do Python normal, que executa a lógica do modelo de treinamento.

from tensorflow import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from tensorflow.keras.layers import BatchNormalization
from tensorflow.keras.utils import to_categorical
from keras.callbacks import Callback
from keras.models import load_model

import argparse
from pathlib import Path
import numpy as np
import pandas as pd
import os
import matplotlib.pyplot as plt
import mlflow


def get_file(f):

    f = Path(f)
    if f.is_file():
        return f
    else:
        files = list(f.iterdir())
        if len(files) == 1:
            return files[0]
        else:
            raise Exception("********This path contains more than one file*******")


def parse_args():
    # setup argparse
    parser = argparse.ArgumentParser()

    # add arguments
    parser.add_argument(
        "--input_data", type=str, help="path containing data for scoring"
    )
    parser.add_argument(
        "--input_model", type=str, default="./", help="input path for model"
    )

    parser.add_argument(
        "--output_result", type=str, default="./", help="output path for model"
    )

    # parse args
    args = parser.parse_args()

    # return args
    return args


def score(input_data, input_model, output_result):

    test_file = get_file(input_data)
    data_test = pd.read_csv(test_file, header=None)

    img_rows, img_cols = 28, 28
    input_shape = (img_rows, img_cols, 1)

    # Read test data
    X_test = np.array(data_test.iloc[:, 1:])
    y_test = to_categorical(np.array(data_test.iloc[:, 0]))
    X_test = (
        X_test.reshape(X_test.shape[0], img_rows, img_cols, 1).astype("float32") / 255
    )

    # Load model
    files = [f for f in os.listdir(input_model) if f.endswith(".h5")]
    model = load_model(input_model + "/" + files[0])

    # Log metrics of the model
    eval = model.evaluate(X_test, y_test, verbose=0)

    mlflow.log_metric("Final test loss", eval[0])
    print("Test loss:", eval[0])

    mlflow.log_metric("Final test accuracy", eval[1])
    print("Test accuracy:", eval[1])

    # Score model using test data
    y_predict = model.predict(X_test)
    y_result = np.argmax(y_predict, axis=1)

    # Output result
    np.savetxt(output_result + "/predict_result.csv", y_result, delimiter=",")


def main(args):
    score(args.input_data, args.input_model, args.output_result)


# run script
if __name__ == "__main__":
    # parse args
    args = parse_args()

    # call main function
    main(args)

O código em score.py usa três argumentos de linha de comando: input_data, input_model e output_result. O programa pontua o modelo de entrada usando os dados de entrada e, em seguida, gera o resultado da pontuação.

Definir componente via Yaml

Nesta seção, você saberá como criar uma especificação de componente no formato de especificação de componente YAML válido. Esse arquivo especifica as seguintes informações:

  • Metadados: name, display_name, version, type, e outros.
  • Interface: entradas e saídas
  • Comando, código e ambiente: o comando, o código e o ambiente usados para executar o componente
$schema: https://azuremlschemas.azureedge.net/latest/commandComponent.schema.json
type: command

name: score_image_classification_keras
display_name: Score Image Classification Keras
inputs:
  input_data: 
    type: uri_folder
  input_model:
    type: uri_folder
outputs:
  output_result:
    type: uri_folder
code: ./
command: python score.py --input_data ${{inputs.input_data}} --input_model ${{inputs.input_model}} --output_result ${{outputs.output_result}}
environment:
  conda_file: ./conda.yaml
  image: mcr.microsoft.com/azureml/openmpi4.1.0-ubuntu20.04
  • name é o identificador exclusivo do componente. O nome de exibição é Score Image Classification Keras.
  • Este componente tem duas entradas e uma saída.
  • O caminho do código-fonte dele é definido na seção code e, quando o componente é executado na nuvem, todos os arquivos desse caminho são carregados como o instantâneo desse componente.
  • A seção command especifica o comando a ser executado durante a execução deste componente.
  • A seção environment contém uma imagem do Docker e um arquivo conda yaml. O arquivo de origem está no repositório de exemplo.

Agora, você tem todos os arquivos de origem para o componente score-model.

Carregar componentes para pipeline de build

Para componentes prep-data e componentes train-model definidos pela função do Python, você poderá importar os componentes como funções normais do Python.

No código a seguir, você importa a função prepare_data_component() e keras_train_component() do arquivo prep_component.py na pasta prep e do arquivo train_component na pasta train, respectivamente.

%load_ext autoreload
%autoreload 2

# load component function from component python file
from prep.prep_component import prepare_data_component
from train.train_component import keras_train_component

# print hint of components
help(prepare_data_component)
help(keras_train_component)

Para o componente de pontuação definido pelo yaml, você poderá usar a função load_component() para carregar.

# load component function from yaml
keras_score_component = load_component(source="./score/score.yaml")

Compilar o pipeline

Agora que você criou e carregou todos os componentes e dados de entrada para compilar o pipeline. É possível compô-los em um pipeline:

Observação

Para usar a computação sem servidor, adicione from azure.ai.ml.entities import ResourceConfiguration à parte superior. Em seguida, substitua:

  • default_compute=cpu_compute_target, com default_compute="serverless",
  • train_node.compute = gpu_compute_target com train_node.resources = "ResourceConfiguration(instance_type="Standard_NC6s_v3",instance_count=2)
# define a pipeline containing 3 nodes: Prepare data node, train node, and score node
@pipeline(
    default_compute=cpu_compute_target,
)
def image_classification_keras_minist_convnet(pipeline_input_data):
    """E2E image classification pipeline with keras using python sdk."""
    prepare_data_node = prepare_data_component(input_data=pipeline_input_data)

    train_node = keras_train_component(
        input_data=prepare_data_node.outputs.training_data
    )
    train_node.compute = gpu_compute_target

    score_node = keras_score_component(
        input_data=prepare_data_node.outputs.test_data,
        input_model=train_node.outputs.output_model,
    )


# create a pipeline
pipeline_job = image_classification_keras_minist_convnet(pipeline_input_data=mnist_ds)

O pipeline tem uma computação cpu_compute_target padrão, o que significa que se você não especificar a computação para um nó específico, esse nó executará na computação padrão.

O pipeline tem uma entrada de nível de pipeline pipeline_input_data. Você pode atribuir valor à entrada do pipeline ao enviar um trabalho de pipeline.

O pipeline contém três nós, prepare_data_node, train_node e score_node.

  • O input_data de prepare_data_node usa o valor de pipeline_input_data.

  • O input_data de train_node é da saída training_data do prepare_data_node.

  • O input_data de score_node é da saída test_data de prepare_data_node e input_model é de output_model train_node.

  • Como train_node treinará um modelo CNN, você poderá especificar a computação como gpu_compute_target, o que poderá melhorar o desempenho do treinamento.

Enviar o trabalho de pipeline

Agora que você construiu o pipeline, poderá enviar para o workspace. Para enviar um trabalho, primeiro será necessário conectar um workspace.

Obter acesso ao workspace

Configurar credencial

Usaremos DefaultAzureCredential para obter acesso ao workspace. DefaultAzureCredential deve poder lidar com a maioria dos cenários de autenticação do SDK do Azure.

Se isso não funcionar para você, a seguir está a referência para obter mais credenciais disponíveis: configurar exemplo de credencial, documento de referência azure-identity.

try:
    credential = DefaultAzureCredential()
    # Check if given credential can get token successfully.
    credential.get_token("https://management.azure.com/.default")
except Exception as ex:
    # Fall back to InteractiveBrowserCredential in case DefaultAzureCredential not work
    credential = InteractiveBrowserCredential()

Obter um identificador para um workspace com computação

Crie um objeto MLClient para gerenciar os serviços do Machine Learning. Se você usar a computação sem servidor, não será necessário criar essas computações.

# Get a handle to workspace
ml_client = MLClient.from_config(credential=credential)

# Retrieve an already attached Azure Machine Learning Compute.
cpu_compute_target = "cpu-cluster"
print(ml_client.compute.get(cpu_compute_target))
gpu_compute_target = "gpu-cluster"
print(ml_client.compute.get(gpu_compute_target))

Importante

Este snippet de código espera que o arquivo json de configuração do workspace seja salvo no diretório atual ou no diretório pai. Para obter mais informações sobre como criar um workspace, consulte Criar recursos de workspace. Para obter mais informações sobre como salvar a configuração no arquivo, confira Criar um arquivo de configuração de workspace.

Enviar o trabalho de pipeline para o workspace

Agora que você tem um identificador para o workspace, você poderá enviar o trabalho de pipeline.

pipeline_job = ml_client.jobs.create_or_update(
    pipeline_job, experiment_name="pipeline_samples"
)
pipeline_job

O código acima envia esse trabalho de pipeline de classificação de imagem para o experimento chamado pipeline_samples. Ele criará automaticamente o experimento, se não existir. O pipeline_input_data usa fashion_ds.

A chamada para pipeline_jobproduz uma saída semelhante a:

A chamada para submit o Experiment é concluída rapidamente e produz uma saída semelhante a:

Experimento Nome Type Status Página de Detalhes
pipeline_samples sharp_pipe_4gvqx6h1fb pipeline Preparando Link para o estúdio do Azure Machine Learning

Monitore a execução do pipeline abrindo o link ou bloqueie-a até que ela seja concluída executando:

# wait until the job completes
ml_client.jobs.stream(pipeline_job.name)

Importante

A primeira execução do pipeline leva aproximadamente 15 minutos. Todas as dependências precisam ser baixadas, uma imagem do Docker é criada e o ambiente do Python é provisionado e criado. Executar o pipeline novamente leva significativamente menos tempo, porque esses recursos são reutilizados, em vez de serem criados. No entanto, o tempo de execução total do pipeline depende da carga de trabalho dos seus scripts e dos processos sendo executados em cada etapa de pipeline.

Fazer check-out e depurar o pipeline na interface do usuário

Você poderá abrir Link to Azure Machine Learning studio, que é a página de detalhes do trabalho do pipeline. Você verá o gráfico do pipeline como a seguir.

Captura de tela da página de detalhes do trabalho de pipeline.

Você poderá verificar os logs e as saídas de cada componente clicando com o botão direito do mouse no componente ou selecionando o componente para abrir o painel de detalhes. Para saber mais sobre como depurar o pipeline na interface do usuário, consulte Como usar o comando depurar falha de pipeline.

(Opcional) Registrar componentes no workspace

Na seção anterior, você criou um pipeline usando três componentes para o E2E concluir uma tarefa de classificação de imagem. Você também poderá registrar componentes no workspace para que possam ser compartilhados e reutilizados no workspace. A seguir, está um exemplo para registrar o componente prep-data.

try:
    # try get back the component
    prep = ml_client.components.get(name="prep_data", version="1")
except:
    # if not exists, register component using following code
    prep = ml_client.components.create_or_update(prepare_data_component)

# list all components registered in workspace
for c in ml_client.components.list():
    print(c)

Usando ml_client.components.get(), você poderá obter um componente registrado por nome e versão. Usando ml_client.components.create_or_update(), você poderá registrar um componente carregado anteriormente da função do Python ou yaml.

Próximas etapas