Compartilhar via


Tutorial: Criar um pipeline do Azure Machine Learning para classificação de imagem

APLICA-SE A: SDK do Python azureml v1

Observação

Para ver um tutorial que usa o SDK v2 para criar um pipeline, confira Tutorial: usar pipelines de ML para fluxos de trabalho de ML de produção com o SDK do Python v2 em um Jupyter Notebook.

Neste tutorial, você aprenderá a criar um pipeline do Azure Machine Learning para preparar dados e treinar um modelo de machine learning. 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.

Neste tutorial, você completa as seguintes tarefas:

  • Configurar o workspace
  • Criar um experimento para armazenar seu trabalho
  • Provisionar um ComputeTarget para fazer o trabalho
  • Criar um conjunto de dados no qual os dados compactados serão armazenados
  • Criar uma etapa de pipeline para preparar os dados para treinamento
  • Definir um ambiente de runtime no qual o treinamento será executado
  • Criar uma etapa de pipeline para definir a rede neural e executar o treinamento
  • Compor um pipeline com base nas etapas do pipeline
  • Executar o pipeline no experimento
  • Revisar a saída das etapas e a rede neural treinada
  • Registrar o modelo para uso posterior

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 Criar recursos para começar se você ainda não tiver um workspace do Azure Machine Learning.
  • Um ambiente do Python no qual você instalou os pacotes azureml-core e azureml-pipeline. Esse ambiente serve para definir e controlar seus recursos do Azure Machine Learning e é separado do ambiente usado em runtime para treinamento.

Importante

Atualmente, a versão mais recente do Python compatível com azureml-pipeline é o Python 3.8. Caso tenha dificuldade para instalar o pacote azureml-pipeline, verifique se python --version é uma versão compatível. Consulte a documentação do gerenciador de ambiente virtual do Python (venv, conda etc.) para obter instruções.

Iniciar uma sessão interativa do Python

Este tutorial usa o SDK do Python para o Azure Machine Learning para criar e controlar um pipeline do Azure Machine Learning. O tutorial pressupõe que você executará os snippets de código de maneira interativa em um ambiente REPL do Python ou em um notebook Jupyter.

  • Este tutorial se baseia no notebook image-classification.ipynb encontrado no diretório python-sdk/tutorial/using-pipelines do repositório Exemplos do Azure Machine Learning. O código-fonte das etapas em si está no subdiretório keras-mnist-fashion.

Tipos de importação

Importe todos os tipos do Azure Machine Learning que você precisará para este tutorial:

import os
import azureml.core
from azureml.core import (
    Workspace,
    Experiment,
    Dataset,
    Datastore,
    ComputeTarget,
    Environment,
    ScriptRunConfig
)
from azureml.data import OutputFileDatasetConfig
from azureml.core.compute import AmlCompute
from azureml.core.compute_target import ComputeTargetException
from azureml.pipeline.steps import PythonScriptStep
from azureml.pipeline.core import Pipeline

# check core SDK version number
print("Azure Machine Learning SDK Version: ", azureml.core.VERSION)

A versão do SDK do Azure Machine Learning deve ser 1.37 ou superior. Caso contrário, atualize-a com pip install --upgrade azureml-core.

Configurar o workspace

Crie um objeto de workspace do Workspace do Azure Machine Learning existente.

workspace = Workspace.from_config()

Importante

Esse snippet de código espera que a configuração do workspace seja salva no diretório atual ou no pai dele. 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.

Criar a infraestrutura para o pipeline

Crie um objeto Experiment para armazenar os resultados das execuções de pipeline:

exp = Experiment(workspace=workspace, name="keras-mnist-fashion")

Crie um ComputeTarget que representa o recurso do computador no qual o pipeline será executado. A rede neural simples usada neste tutorial faz o treinamento em apenas alguns minutos, mesmo em um computador baseado em CPU. Caso deseje usar uma GPU para treinamento, defina use_gpu como True. O provisionamento de um destino de computação geralmente leva cerca de cinco minutos.

use_gpu = False

# choose a name for your cluster
cluster_name = "gpu-cluster" if use_gpu else "cpu-cluster"

found = False
# Check if this compute target already exists in the workspace.
cts = workspace.compute_targets
if cluster_name in cts and cts[cluster_name].type == "AmlCompute":
    found = True
    print("Found existing compute target.")
    compute_target = cts[cluster_name]
if not found:
    print("Creating a new compute target...")
    compute_config = AmlCompute.provisioning_configuration(
        vm_size= "STANDARD_NC6" if use_gpu else "STANDARD_D2_V2"
        # vm_priority = 'lowpriority', # optional
        max_nodes=4,
    )

    # Create the cluster.
    compute_target = ComputeTarget.create(workspace, cluster_name, compute_config)

    # Can poll for a minimum number of nodes and for a specific timeout.
    # If no min_node_count is provided, it will use the scale settings for the cluster.
    compute_target.wait_for_completion(
        show_output=True, min_node_count=None, timeout_in_minutes=10
    )
# For a more detailed view of current AmlCompute status, use get_status().print(compute_target.get_status().serialize())

Observação

A disponibilidade da GPU depende da cota da sua assinatura do Azure e da capacidade do Azure. Confira Gerenciar e aumentar cotas para recursos com o Azure Machine Learning.

Criar um conjunto de dados para os dados armazenados no Azure

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 criar um Dataset que referencie os dados baseados na Web, execute:

data_urls = ["https://data4mldemo6150520719.blob.core.windows.net/demo/mnist-fashion"]
fashion_ds = Dataset.File.from_files(data_urls)

# list the files referenced by fashion_ds
print(fashion_ds.to_path())

Esse código é concluído rapidamente. Os dados subjacentes permanecem no recurso de armazenamento do Azure especificado na matriz data_urls.

Criar a etapa de pipeline de preparação de dados

A primeira etapa deste pipeline converterá os arquivos de dados compactados de fashion_ds em um conjunto de dados no seu workspace que consiste em arquivos CSV prontos para uso no treinamento. Depois de registrados no workspace, os colaboradores poderão acessar esses dados para análise, treinamento etc.

datastore = workspace.get_default_datastore()
prepared_fashion_ds = OutputFileDatasetConfig(
    destination=(datastore, "outputdataset/{run-id}")
).register_on_complete(name="prepared_fashion_ds")

O código acima especifica um conjunto de dados baseado na saída de uma etapa de pipeline. Os arquivos processados subjacentes serão colocados no armazenamento de blobs do armazenamento de dados padrão do workspace no caminho especificado em destination. O conjunto de dados será registrado no workspace com o nome prepared_fashion_ds.

Criar a origem da etapa de pipeline

O código que você executou até agora criou e controlou recursos do Azure. Agora é hora de escrever um código que execute a primeira etapa no domínio.

Se você estiver acompanhando o exemplo no repositório Exemplos do Azure Machine Learning, o arquivo de origem já estará disponível como keras-mnist-fashion/prepare.py.

Se estiver trabalhando do zero, crie um subdiretório chamado keras-mnist-fashion/. Crie um arquivo, adicione o código a seguir a ele e nomeie o arquivo prepare.py.

# prepare.py
# Converts MNIST-formatted files at the passed-in input path to a passed-in output path
import os
import sys

# Conversion routine for MNIST binary format
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()

# The MNIST-formatted source
mounted_input_path = sys.argv[1]
# The output directory at which the outputs will be written
mounted_output_path = sys.argv[2]

# Create the output directory
os.makedirs(mounted_output_path, exist_ok=True)

# Convert the training data
convert(
    os.path.join(mounted_input_path, "mnist-fashion/train-images-idx3-ubyte"),
    os.path.join(mounted_input_path, "mnist-fashion/train-labels-idx1-ubyte"),
    os.path.join(mounted_output_path, "mnist_train.csv"),
    60000,
)

# Convert the test data
convert(
    os.path.join(mounted_input_path, "mnist-fashion/t10k-images-idx3-ubyte"),
    os.path.join(mounted_input_path, "mnist-fashion/t10k-labels-idx1-ubyte"),
    os.path.join(mounted_output_path, "mnist_test.csv"),
    10000,
)

O código em prepare.py usa dois argumentos de linha de comando: o primeiro é atribuído a mounted_input_path e o segundo, a mounted_output_path. Se esse subdiretório não existir, a chamada a os.makedirs o criará. Em seguida, o programa converte os dados de treinamento e de teste e gera os arquivos separados por vírgula no mounted_output_path.

Especificar a etapa de pipeline

De volta ao ambiente do Python que você está usando para especificar o pipeline, execute este código para criar um PythonScriptStep para o código de preparação:

script_folder = "./keras-mnist-fashion"

prep_step = PythonScriptStep(
    name="prepare step",
    script_name="prepare.py",
    # On the compute target, mount fashion_ds dataset as input, prepared_fashion_ds as output
    arguments=[fashion_ds.as_named_input("fashion_ds").as_mount(), prepared_fashion_ds],
    source_directory=script_folder,
    compute_target=compute_target,
    allow_reuse=True,
)

A chamada a PythonScriptStep especifica que, quando a etapa de pipeline for executada:

  • Todos os arquivos do diretório script_folder serão carregados no compute_target
  • Entre esses arquivos de origem carregados, o arquivo prepare.py será executado
  • Os conjuntos de dados fashion_ds e prepared_fashion_ds serão montados no compute_target e serão exibidos como diretórios
  • O caminho para os arquivos fashion_ds será o primeiro argumento para prepare.py. Em prepare.py, esse argumento é atribuído a mounted_input_path
  • O caminho para o prepared_fashion_ds será o segundo argumento para prepare.py. Em prepare.py, esse argumento é atribuído a mounted_output_path
  • Como allow_reuse é True, ele só será executado novamente quando as entradas ou os respectivos arquivos de origem forem alterados
  • Esse PythonScriptStep será nomeado prepare step

A modularidade e a reutilização são os principais benefícios dos pipelines. O Azure Machine Learning pode determinar automaticamente o código-fonte ou as alterações no conjunto de dados. A saída de uma etapa que não é afetada será reutilizada sem uma nova execução das etapas se allow_reuse for True. Se uma etapa depender de uma fonte de dados externa ao Azure Machine Learning que possa ser alterada (por exemplo, uma URL que contenha dados de vendas), defina allow_reuse como False e a etapa de pipeline será executada sempre que o pipeline for executado.

Criar a etapa de treinamento

Depois que os dados são convertidos do formato compactado em arquivos CSV, eles podem ser usados para treinar uma rede neural convolucional.

Criar a origem da etapa de treinamento

Com pipelines maiores, é uma boa prática colocar o código-fonte de cada etapa em um diretório separado (src/prepare/, src/train/ etc.), mas para este tutorial, basta usar ou criar o arquivo train.py no mesmo diretório de origem keras-mnist-fashion/.

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

import numpy as np
import pandas as pd
import os
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from azureml.core import Run

# dataset object from the run
run = Run.get_context()
dataset = run.input_datasets["prepared_fashion_ds"]

# split dataset into train and test set
(train_dataset, test_dataset) = dataset.random_split(percentage=0.8, seed=111)

# load dataset into pandas dataframe
data_train = train_dataset.to_pandas_dataframe()
data_test = test_dataset.to_pandas_dataframe()

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

X = np.array(data_train.iloc[:, 1:])
y = to_categorical(np.array(data_train.iloc[:, 0]))

# here we split validation data to optimiza classifier during training
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=13)

# test data
X_test = np.array(data_test.iloc[:, 1:])
y_test = to_categorical(np.array(data_test.iloc[:, 0]))


X_train = (
    X_train.reshape(X_train.shape[0], img_rows, img_cols, 1).astype("float32") / 255
)
X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 1).astype("float32") / 255
X_val = X_val.reshape(X_val.shape[0], img_rows, img_cols, 1).astype("float32") / 255

batch_size = 256
num_classes = 10
epochs = 10

# construct neuron network
model = Sequential()
model.add(
    Conv2D(
        32,
        kernel_size=(3, 3),
        activation="relu",
        kernel_initializer="he_normal",
        input_shape=input_shape,
    )
)
model.add(MaxPooling2D((2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(64, (3, 3), activation="relu"))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(128, (3, 3), activation="relu"))
model.add(Dropout(0.4))
model.add(Flatten())
model.add(Dense(128, activation="relu"))
model.add(Dropout(0.3))
model.add(Dense(num_classes, activation="softmax"))

model.compile(
    loss=keras.losses.categorical_crossentropy,
    optimizer=keras.optimizers.Adam(),
    metrics=["accuracy"],
)

# start an Azure ML run
run = Run.get_context()


class LogRunMetrics(Callback):
    # callback at the end of every epoch
    def on_epoch_end(self, epoch, log):
        # log a value repeated which creates a list
        run.log("Loss", log["loss"])
        run.log("Accuracy", log["accuracy"])


history = model.fit(
    X_train,
    y_train,
    batch_size=batch_size,
    epochs=epochs,
    verbose=1,
    validation_data=(X_val, y_val),
    callbacks=[LogRunMetrics()],
)

score = model.evaluate(X_test, y_test, verbose=0)

# log a single value
run.log("Final test loss", score[0])
print("Test loss:", score[0])

run.log("Final test accuracy", score[1])
print("Test accuracy:", score[1])

plt.figure(figsize=(6, 3))
plt.title("Fashion MNIST with Keras ({} epochs)".format(epochs), fontsize=14)
plt.plot(history.history["accuracy"], "b-", label="Accuracy", lw=4, alpha=0.5)
plt.plot(history.history["loss"], "r--", label="Loss", lw=4, alpha=0.5)
plt.legend(fontsize=12)
plt.grid(True)

# log an image
run.log_image("Loss v.s. Accuracy", plot=plt)

# create a ./outputs/model folder in the compute target
# files saved in the "./outputs" folder are automatically uploaded into run history
os.makedirs("./outputs/model", exist_ok=True)

# serialize NN architecture to JSON
model_json = model.to_json()
# save model JSON
with open("./outputs/model/model.json", "w") as f:
    f.write(model_json)
# save model weights
model.save_weights("./outputs/model/model.h5")
print("model saved in ./outputs/model folder")

A maior parte desse código deve ser conhecida para os desenvolvedores de ML:

  • Os dados são particionados em conjuntos de treinamento e validação para o treinamento e em um subconjunto de teste separado para a pontuação final
  • A forma de entrada é 28x28x1 (apenas 1 porque a entrada é escala de cinza), haverá 256 entradas em um lote e há dez classes
  • O número de épocas de treinamento será dez
  • O modelo tem três camadas convolucionais, com o máximo de pools e abandono, seguido por uma camada densa e um cabeçalho softmax
  • O modelo é ajustado para dez épocas e avaliado
  • A arquitetura do modelo é gravada em outputs/model/model.json, e os pesos, em outputs/model/model.h5

No entanto, uma parte do código é específica do Azure Machine Learning. run = Run.get_context() recupera um objeto Run que contém o contexto de serviço atual. A origem train.py usa esse objeto run para recuperar o conjunto de dados de entrada por meio do nome (uma alternativa para o código em prepare.py que recuperou o conjunto de dados por meio da matriz argv de argumentos de script).

O objeto run também é usado para registrar o progresso do treinamento no final de cada época e, no final do treinamento, para registrar em log o grafo de perda e precisão ao longo do tempo.

Criar a etapa de pipeline de treinamento

A etapa de treinamento tem uma configuração um pouco mais complexa do que a etapa de preparação. A etapa de preparação usou apenas bibliotecas padrão do Python. Mais frequentemente, você precisará modificar o ambiente de runtime no qual o código-fonte é executado.

Crie um arquivo conda_dependencies.yml com o seguinte conteúdo:

dependencies:
- python=3.7
- pip:
  - azureml-core
  - azureml-dataset-runtime
  - keras==2.4.3
  - tensorflow==2.4.3
  - numpy
  - scikit-learn
  - pandas
  - matplotlib

A classe Environment representa o ambiente de runtime no qual uma tarefa de machine learning é executada. Associe a especificação acima ao código de treinamento a:

keras_env = Environment.from_conda_specification(
    name="keras-env", file_path="./conda_dependencies.yml"
)

train_cfg = ScriptRunConfig(
    source_directory=script_folder,
    script="train.py",
    compute_target=compute_target,
    environment=keras_env,
)

A criação da etapa de treinamento em si usa um código semelhante ao código usado para criar a etapa de preparação:

train_step = PythonScriptStep(
    name="train step",
    arguments=[
        prepared_fashion_ds.read_delimited_files().as_input(name="prepared_fashion_ds")
    ],
    source_directory=train_cfg.source_directory,
    script_name=train_cfg.script,
    runconfig=train_cfg.run_config,
)

Criar e executar o pipeline

Agora que você especificou as entradas e as saídas de dados e criou as etapas do pipeline, você pode compô-las em um pipeline e executá-las:

pipeline = Pipeline(workspace, steps=[prep_step, train_step])
run = exp.submit(pipeline)

O objeto Pipeline criado é executado no workspace e é composto pelas etapas de preparação e de treinamento que você especificou.

Observação

Esse pipeline tem um grafo de dependência simples: a etapa de treinamento depende da etapa de preparação e a etapa de preparação depende do conjunto de dados fashion_ds. Os pipelines de produção geralmente terão dependências muito mais complexas. As etapas podem depender de várias etapas upstream, uma alteração de código-fonte em uma etapa inicial pode ter consequências abrangentes etc. O Azure Machine Learning controla essas preocupações para você. Você só precisa transmitir a matriz de steps e o Azure Machine Learning cuida do cálculo do grafo de execução.

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

Submitted PipelineRun 5968530a-abcd-1234-9cc1-46168951b5eb
Link to Azure Machine Learning Portal: https://ml.azure.com/runs/abc-xyz...

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

run.wait_for_completion(show_output=True)

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.

Depois que o pipeline for concluído, você poderá recuperar as métricas registradas em log na etapa de treinamento:

run.find_step_run("train step")[0].get_metrics()

Se estiver satisfeito com as métricas, registre o modelo no seu workspace:

run.find_step_run("train step")[0].register_model(
    model_name="keras-model",
    model_path="outputs/model/",
    datasets=[("train test data", fashion_ds)],
)

Limpar os recursos

Não conclua esta seção se desejar executar outros tutoriais do Azure Machine Learning.

Parar a instância de computação

Caso tenha usado uma instância de computação, interrompa a VM quando ela não estiver sendo usada para reduzir o custo.

  1. No workspace, selecione Computação.

  2. Na lista, selecione o nome da instância de computação.

  3. Selecione Interromper.

  4. Quando estiver pronto para usar o servidor novamente, selecione Iniciar.

Excluir tudo

Se você não pretende usar os recursos criados, exclua-os para não gerar encargos:

  1. No portal do Azure, no menu esquerdo, selecione Grupos de recursos.
  2. Na lista de grupos de recursos, selecione o grupo de recursos criado.
  3. Selecione Excluir grupo de recursos.
  4. Insira o nome do grupo de recursos. Em seguida, selecione Excluir.

Você também pode manter o grupo de recursos, mas excluir um único workspace. Exiba as propriedades do workspace e, em seguida, selecione Excluir.

Próximas etapas

Neste tutorial, você usou os seguintes tipos:

  • O Workspace representa seu workspace do Azure Machine Learning. Ele continha:
    • O Experiment que contém os resultados das execuções de treinamento do pipeline
    • O Dataset que carregou lentamente os dados mantidos no armazenamento de dados Fashion-MNIST
    • O ComputeTarget que representa os computadores em que as etapas de pipeline são executadas
    • O Environment que é o ambiente de runtime no qual as etapas de pipeline são executadas
    • O Pipeline que compõe as etapas PythonScriptStep como um todo
    • O Model que você registrou depois de estar satisfeito com o processo de treinamento

O objeto Workspace contém referências a outros recursos (notebooks, pontos de extremidade etc.) que não foram usados neste tutorial. Para saber mais, confira O que é um workspace do Azure Machine Learning?.

A OutputFileDatasetConfig promove a saída de uma execução para um conjunto de dados baseado em arquivo. Para obter mais informações sobre conjuntos de dados e como usá-los, confira Como acessar dados.

Para saber mais sobre ambientes e destinos de computação, confira O que são destinos de computação no Azure Machine Learning? e O que são ambientes do Azure Machine Learning?

A ScriptRunConfig associa um ComputeTarget e Environment a arquivos de origem do Python. Uma PythonScriptStep usa essa ScriptRunConfig e define as entradas e as saídas, que nesse pipeline foi o conjunto de dados do arquivo criado pela OutputFileDatasetConfig.

Para ver mais exemplos de como criar pipelines usando o SDK de machine learning, confira o repositório de exemplos.