Crie e execute pipelines de machine learning usando componentes com o SDK v2 do Azure Machine Learning (versão prévia)

APLICA-SE A: azure-ai-ml do SDK do Pythonv2 (versão prévia)

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

  • Conclua o Guia de início rápido: introdução ao Azure Machine Learning se você ainda não tiver um Workspace do Azure Machine Learning.

  • 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 --branch sdk-preview
    cd azureml-examples/sdk
    

Iniciar uma sessão interativa do Python

Este artigo usa o SDK do Python para Azure ML 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/jobs/pipelines/2e_image_classification_keras_minist_convnet do repositório de Exemplos do AzureML.

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.

Para definir os dados de entrada de um trabalho que faz referência aos dados baseados na Web, execute:

from azure.ai.ml import Input

fashion_ds = Input(
    path="wasbs://demo@data4mldemo6150520719.blob.core.windows.net/mnist-fashion/"
)

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 usando dados de treinamento
  • Pontuar o modelo usando dados de teste

Para cada componente, será necessário preparar a seguinte equipe:

  1. Prepare o script do Python contendo a lógica de execução

  2. Defina 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á como criar componentes de duas maneiras diferentes: os dois primeiros componentes usando a função do 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 seguindo o exemplo no repositório de exemplos do AzureML, 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/openmpi3.1.2-ubuntu18.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.
  • 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.

Especificar o ambiente de runtime do componente

Será necessário modificar o ambiente de runtime em que o componente executa.



@command_component(
    name="prep_data",
    version="1",
    display_name="Prep Data",

O código acima cria um objeto de classe Environment, que representa o ambiente de runtime em que o componente executa.

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

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 AzureML. 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. Você pode encontrar o código aqui.

Definir o componente usando a função do Python

Após definir a função de treinamento com êxito, você poderá usar @command_component no SDK v2 do Azure Machine Learning para encapsular a função como um componente, que poderá ser usado em pipelines do AzureML.

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/openmpi3.1.2-ubuntu18.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.

Especificar o ambiente de runtime do componente

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.0
  - pip:
    - mldesigner==0.1.0b4
    - azureml-mlflow
    - 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 seguindo o exemplo no repositório de exemplos do AzureML, 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 & 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/openmpi3.1.2-ubuntu18.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.

Especificar o ambiente de runtime do componente

O componente de pontuação usa a mesma imagem e arquivo conda.yaml que o componente de treinamento. 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 componente prep-data e componente train-model definido pela função do Python, você poderá importar os componentes, assim como as 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(path="./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:

# 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=fashion_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 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.

# 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 a interface do usuário do estúdio para compilar e depurar pipelines do Azure ML.

(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.compoennts.create_or_update(), você poderá registrar um componente carregado anteriormente da função do Python ou yaml.

Próximas etapas