Tutorial: Construir um pipeline de aprendizagem automática Azure para a classificação de imagem

APLICA-SE A:Python SDK azureml v1

Neste tutorial, aprende-se a construir um pipeline Azure Machine Learning para preparar dados e treinar um modelo de aprendizagem automática. Os oleodutos de aprendizagem automática otimizam o seu fluxo de trabalho com velocidade, portabilidade e reutilização, para que possa focar-se na aprendizagem automática em vez de infraestrutura e automação.

O exemplo treina uma pequena rede neural de Keras para classificar imagens no conjunto de dados do MNIST de moda .

Neste tutorial, vai concluir as seguintes tarefas:

  • Configurar a área de trabalho
  • Crie uma Experiência para manter o seu trabalho
  • Provision a ComputeTarget para fazer o trabalho
  • Criar um conjunto de dados para armazenar dados comprimidos
  • Criar um passo de oleoduto para preparar os dados para a formação
  • Definir um ambiente de tempo de execução para realizar treinos
  • Criar um passo de pipeline para definir a rede neural e realizar o treino
  • Componha um gasoduto a partir dos degraus do gasoduto
  • Executar o oleoduto na experiência
  • Reveja a saída dos passos e a rede neural treinada
  • Registar o modelo para posterior utilização

Se não tiver uma subscrição do Azure, crie uma conta gratuita antes de começar. Experimente hoje a versão gratuita ou paga do Azure Machine Learning .

Pré-requisitos

  • Complete o Quickstart: Começa com o Azure Machine Learning se ainda não tiveres um espaço de trabalho para aprendizagem automática Azure.
  • Um ambiente Python no qual instalou os azureml-core pacotes e azureml-pipeline os pacotes. Este ambiente destina-se a definir e controlar os seus recursos de Aprendizagem automática Azure e é separado do ambiente utilizado em tempo de funcionamento para treino.

Importante

Atualmente, o mais recente lançamento python compatível com azureml-pipeline é Python 3.8. Se tiver dificuldade em instalar o azureml-pipeline pacote, certifique-se de que python --version se trata de uma versão compatível. Consulte a documentação do seu gestor de ambiente virtual Python (venv, condae assim por diante) para obter instruções.

Inicie uma sessão interativa de Python

Este tutorial usa o Python SDK para Azure ML para criar e controlar um gasoduto Azure Machine Learning. O tutorial pressupõe que você estará executando os cortes de código interativamente em um ambiente Python REPL ou um caderno Jupyter.

  • Este tutorial baseia-se no image-classification.ipynb caderno encontrado no python-sdk/tutorial/using-pipelines diretório do repositório AzureML Examples . O código-fonte dos passos em si está na keras-mnist-fashion subdireção.

Tipos de importação

Importe todos os tipos de Aprendizagem automática Azure 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 ML SDK Version: ", azureml.core.VERSION)

A versão Azure ML SDK deve ser 1.37 ou maior. Se não for, atualize com pip install --upgrade azureml-core.

Configurar a área de trabalho

Crie um objeto de espaço de trabalho a partir do espaço de trabalho de Aprendizagem da Máquina de Azure existente.

workspace = Workspace.from_config()

Importante

Este código de corte espera que a configuração do espaço de trabalho seja guardada no diretório atual ou no seu progenitor. Para obter mais informações sobre a criação de um espaço de trabalho, consulte Criar recursos do espaço de trabalho. Para obter mais informações sobre a poupança da configuração para arquivar, consulte Criar um ficheiro de configuração do espaço de trabalho.

Crie a infraestrutura para o seu oleoduto

Crie um Experiment objeto para manter os resultados do seu gasoduto:

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

Crie um ComputeTarget que represente o recurso da máquina em que o seu oleoduto irá funcionar. A simples rede neural usada neste tutorial treina em poucos minutos, mesmo numa máquina baseada em CPU. Se desejar utilizar uma GPU para treinar, desafase use_gpu para True. O fornecimento de um alvo 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())

Nota

A disponibilidade de GPU depende da quota da sua subscrição Azure e da capacidade Azure. Ver Gerir e aumentar quotas para recursos com Azure Machine Learning.

Criar um conjunto de dados para os dados armazenados pelo Azure

Fashion-MNIST é um conjunto de imagens de moda dividido em 10 classes. Cada imagem é uma imagem em tons de cinza 28x28 e há 60.000 imagens de treino e 10.000 testes. Como um problema de classificação de imagem, o Fashion-MNIST é mais difícil do que a clássica base de dados de dígitos manuscritos do MNIST. É distribuído na mesma forma binária comprimido que a base de dados original de dígitos manuscritos .

Para criar um Dataset que faz referência aos dados baseados na Web, corra:

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

Este código completa-se rapidamente. Os dados subjacentes permanecem no recurso de armazenamento Azure especificado na data_urls matriz.

Criar o passo do pipeline de preparação de dados

O primeiro passo neste pipeline converterá os ficheiros de dados comprimidos de um conjunto de fashion_ds dados no seu próprio espaço de trabalho, composto por ficheiros CSV prontos para serem utilizados em formação. Uma vez registados no espaço de trabalho, os seus colaboradores podem aceder a estes dados para a sua própria análise, formação, e assim por diante

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 que se baseia na saída de um passo de pipeline. Os ficheiros processados subjacentes serão colocados no armazenamento de bolhas padrão da loja de dados no caminho especificado em destination. O conjunto de dados será registado no espaço de trabalho com o nome prepared_fashion_ds.

Criar a fonte do gasoduto

O código que executou até agora criou e controlou os recursos do Azure. Agora é hora de escrever código que dá o primeiro passo no domínio.

Se estiver a seguir o exemplo no repo AzureML Examples, o ficheiro de origem já está disponível como keras-mnist-fashion/prepare.py.

Se estiver a trabalhar do zero, crie uma subdirectória chamada keras-mnist-fashion/. Crie um novo ficheiro, adicione-lhe o seguinte código e nomeie o ficheiro 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 leva dois argumentos de linha de comando: o primeiro é atribuído mounted_input_path e o segundo para mounted_output_path. Se o subdiretório não existir, a chamada para os.makedirs a criar. Em seguida, o programa converte os dados de treino e teste e produz os ficheiros separados por vírgula para o mounted_output_path.

Especificar o passo do gasoduto

De volta ao ambiente Python que está a usar para especificar o oleoduto, executar este código para criar um PythonScriptStep para o seu 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 especifica PythonScriptStep que, quando o passo do gasoduto é executado:

  • Todos os ficheiros do script_folder diretório são enviados para o compute_target
  • Entre os ficheiros de origem carregados, o ficheiro prepare.py será executado
  • Os fashion_ds conjuntos e prepared_fashion_ds conjuntos de dados serão montados no compute_target e aparecerão como diretórios
  • O caminho para os fashion_ds ficheiros será o primeiro argumento para prepare.py. Em prepare.py, este argumento é atribuído a mounted_input_path
  • O caminho para o prepared_fashion_ds será o segundo argumento para prepare.py. Em prepare.py, este argumento é atribuído a mounted_output_path
  • Porque allow_reuse é True, não será reexame até que os seus ficheiros de origem ou entradas mudem
  • Isto PythonScriptStep será nomeado prepare step

A modularidade e a reutilização são os principais benefícios dos gasodutos. O Azure Machine Learning pode determinar automaticamente as alterações do código-fonte ou do conjunto de dados. A saída de um passo que não seja afetado será reutilizada sem repetir os passos se allow_reuse for True. Se um passo depender de uma fonte de dados externa à Azure Machine Learning que pode mudar (por exemplo, um URL que contém dados de vendas), definido allow_reuse para False e o passo do pipeline será executado cada vez que o pipeline é executado.

Criar o passo de treino

Uma vez que os dados foram convertidos do formato comprimido para ficheiros CSV, pode ser usado para treinar uma rede neural convolucional.

Criar a fonte do passo de treino

Com oleodutos maiores, é uma boa prática colocar o código fonte de cada passo num diretório separado (src/prepare/, src/train/e assim por diante) mas para este tutorial, basta usar ou criar o ficheiro train.py no mesmo keras-mnist-fashion/ diretório de origem.

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 maioria deste código deve ser familiar aos desenvolvedores de ML:

  • Os dados são divididos em conjuntos de comboio e validação para treino, e um subconjunto de teste separado para pontuação final
  • A forma de entrada é 28x28x1 (apenas 1 porque a entrada é em tons de cinza), haverá 256 entradas num lote, e há 10 classes
  • O número de épocas de formação será de 10
  • O modelo tem três camadas convolucionais, com piscina máxima e abandono, seguido de uma camada densa e cabeça de softmax
  • O modelo é equipado para 10 épocas e depois avaliado
  • A arquitetura modelo é escrita para outputs/model/model.json e os pesos para outputs/model/model.h5

Parte do código, no entanto, é específico para Azure Machine Learning. run = Run.get_context() recupera um Run objeto, que contém o contexto de serviço atual. A train.py fonte utiliza este run objeto para recuperar o conjunto de dados de entrada através do seu nome (uma alternativa ao código que prepare.py recuperou o conjunto de dados através da argv matriz de argumentos do script).

O run objeto também é usado para registar o progresso do treino no final de cada época e, no final do treino, para registar o gráfico de perda e precisão ao longo do tempo.

Criar o passo do pipeline de treino

O passo de treino tem uma configuração um pouco mais complexa do que o passo de preparação. O passo de preparação usou apenas bibliotecas python padrão. Mais comummente, terá de modificar o ambiente de tempo de funcionação em que o seu código fonte funciona.

Criar um ficheiro conda_dependencies.yml com os seguintes conteúdos:

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

A Environment classe representa o ambiente de tempo de funcionação em que funciona uma tarefa de aprendizagem automática. Associar a especificação acima com o código de formação com:

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 do próprio passo de treino utiliza código semelhante ao código utilizado para criar o passo 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 oleoduto

Agora que especificou entradas e saídas de dados e criou os passos do seu oleoduto, pode componhá-los num oleoduto e executá-los:

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

O Pipeline objeto que cria corre no seu workspace e é composto pelos passos de preparação e treino especificados.

Nota

Este gasoduto tem um simples gráfico de dependência: o passo de treino baseia-se no passo de preparação e o passo de preparação depende do fashion_ds conjunto de dados. Os gasodutos de produção terão muitas vezes dependências muito mais complexas. Os passos podem depender de várias etapas a montante, uma alteração do código de origem num primeiro passo pode ter consequências de longo alcance, e assim por diante. A Azure Machine Learning acompanha estas preocupações para si. Basta passar na matriz de steps E Azure Machine Learning cuida do cálculo do gráfico de execução.

A chamada para submit os Experiment completes 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...

Pode monitorizar o gasoduto funcionando abrindo o link ou pode bloquear até que este esteja concluído:

run.wait_for_completion(show_output=True)

Importante

O primeiro gasoduto demora cerca de 15 minutos. Todas as dependências devem ser descarregadas, uma imagem docker é criada, e o ambiente Python é provisionado e criado. A execução do gasoduto leva de novo significativamente menos tempo porque esses recursos são reutilizados em vez de serem criados. No entanto, o tempo total de funcionamento do pipeline depende da carga de trabalho dos seus scripts e dos processos que estão a decorrer em cada passo do pipeline.

Uma vez concluído o gasoduto, poderá recuperar as métricas que registou no passo de treino:

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

Se estiver satisfeito com as métricas, pode registar o modelo no seu espaço de trabalho:

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 complete esta secção se planeia executar outros tutoriais de Aprendizagem automática Azure.

Pare a instância computacional

Se usou uma instância computacional, pare o VM quando não estiver a usá-lo para reduzir o custo.

  1. No seu espaço de trabalho, selecione Compute.

  2. A partir da lista, selecione o nome da instância computacional.

  3. Selecione Stop.

  4. Quando estiver pronto para voltar a utilizar o servidor, selecione Iniciar.

Apagar tudo

Se não planeia utilizar os recursos que criou, elimine-os, para não incorrer em quaisquer 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 que criou.
  3. Selecione Eliminar grupo de recursos.
  4. Introduza o nome do grupo de recursos. Em seguida, selecione Delete.

Também pode manter o grupo de recursos, mas apagar um único espaço de trabalho. Mostrar as propriedades do espaço de trabalho e, em seguida, selecionar Delete.

Passos seguintes

Neste tutorial, utilizou os seguintes tipos:

  • Representa Workspace o seu espaço de trabalho de Aprendizagem de Máquinas Azure. Continha:
    • O Experiment que contém os resultados das corridas de treino do seu oleoduto
    • O Dataset que preguiçosamente carregou os dados detidos na loja de dados Fashion-MNIST
    • O ComputeTarget que representa a máquina(s) em que os degraus do gasoduto funcionam
    • O Environment que é o ambiente de tempo de execução em que os passos do gasoduto correm
    • O Pipeline que compõe os PythonScriptStep passos em um todo
    • O Model que registou depois de ter ficado satisfeito com o processo de treino

O Workspace objeto contém referências a outros recursos (cadernos, pontos finais, e assim por diante) que não foram usados neste tutorial. Para mais informações, veja o que é um espaço de trabalho de Aprendizagem automática Azure?.

O OutputFileDatasetConfig promove a produção de uma corrida para um conjunto de dados baseado em ficheiros. Para obter mais informações sobre conjuntos de dados e trabalhar com dados, consulte Como aceder aos dados.

Para obter mais informações sobre metas e ambientes de computação, veja quais são os alvos de computação em Azure Machine Learning?

Os ScriptRunConfig associados a ComputeTarget e Environment com os ficheiros de origem Python. A PythonScriptStep pega nisso ScriptRunConfig e define as suas entradas e saídas, que neste pipeline foi o conjunto de dados de ficheiros construído pelo OutputFileDatasetConfig.

Para mais exemplos de como construir oleodutos utilizando o SDK de aprendizagem automática, consulte o repositório de exemplo.