Creare ed eseguire pipeline di Machine Learning usando componenti con Azure Machine Learning SDK v2

SI APPLICA A: Python SDK azure-ai-ml v2 (corrente)

Questo articolo illustra come creare una pipeline di Azure Machine Learning usando Python SDK v2 per completare un'attività di classificazione delle immagini contenente tre passaggi: preparare i dati, eseguire il training di un modello di classificazione delle immagini e assegnare un punteggio al modello. Le pipeline di Machine Learning ottimizzano il flusso di lavoro offrendo velocità, portabilità e possibilità di riutilizzo per consentire così di concentrarsi su Machine Learning anziché sull'infrastruttura e l'automazione.

L'esempio esegue il training di una piccola rete neurale convoluzionale Keras per classificare le immagini nel set di dati Fashion MNIST. La pipeline ha un aspetto simile al seguente.

Screenshot che mostra il grafico della pipeline dell'esempio keras di classificazione delle immagini.

In questo articolo vengono completate le attività seguenti:

  • Preparare i dati di input per il processo della pipeline
  • Creare tre componenti per preparare i dati, eseguire il training e assegnare il punteggio
  • Comporre una pipeline dai componenti
  • Ottenere l'accesso all'area di lavoro con l'ambiente di calcolo
  • Inviare il processo della pipeline
  • Esaminare l'output dei componenti e della rete neurale sottoposta a training
  • (Facoltativo) Registrare il componente per riutilizzare e condividere ulteriormente all'interno dell'area di lavoro

Se non si ha una sottoscrizione di Azure, creare un account gratuito prima di iniziare. Provare la versione gratuita o a pagamento di Azure Machine Learning.

Prerequisiti

  • Area di lavoro di Azure Machine Learning: se non ne è disponibile una, completare l'esercitazione Creare risorse.

  • Un ambiente Python in cui è stato installato Azure Machine Learning Python SDK v2 - istruzioni di installazione - Controllare la sezione introduttiva. Questo ambiente viene usato per definire e controllare le risorse di Azure Machine Learning ed è separato dall'ambiente usato in fase di esecuzione per il training.

  • Clonare il repository di esempi

    Per eseguire gli esempi di training, clonare prima di tutto il repository di esempi e passare alla directory sdk:

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

Avviare una sessione Interattiva di Python

Questo articolo usa Python SDK per Azure Machine Learning per creare e controllare una pipeline di Azure Machine Learning. L'articolo presuppone che i frammenti di codice vengano eseguiti in modo interattivo in un ambiente Python REPL o in un notebook Jupyter.

Questo articolo si basa sul notebook image_classification_keras_minist_convnet.ipynb disponibile nella directory sdk/python/jobs/pipelines/2e_image_classification_keras_minist_convnet del repository Azure Machine Learning Examples.

Importare le librerie obbligatorie

Importare tutte le librerie necessarie di Azure Machine Learning richieste per questo articolo:

# 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

Preparare i dati di input per il processo della pipeline

È necessario preparare i dati di input per questa pipeline di classificazione delle immagini.

Fashion-MNIST è un set di dati di immagini di moda suddivise in 10 classi. Ogni immagine è un'immagine in scala di grigi 28x28 ed esistono 60.000 immagini per il training e 10.000 immagini per i test. Come problema di classificazione delle immagini, Fashion-MNIST è più difficile rispetto al database classico MNIST con cifre scritte a mano. È distribuito nella stessa forma binaria compressa del database di cifre scritto a mano originale.

Importare tutte le librerie necessarie di Azure Machine Learning necessarie.

Definendo un Input, si crea un riferimento al percorso dell'origine dati. I dati rimangono nell'attuale posizione, quindi non si incorre in costi aggiuntivi di archiviazione.

Creare componenti per la compilazione della pipeline

L'attività di classificazione delle immagini può essere suddivisa in tre passaggi: preparare i dati, eseguire il training del modello e assegnare un punteggio al modello.

Un componente di Azure Machine Learning è una parte autonoma di codice che esegue un passaggio in una pipeline di Machine Learning. In questo articolo verranno creati tre componenti per l'attività di classificazione delle immagini:

  • Preparare i dati per il training e il test
  • Eseguire il training di una rete neurale per la classificazione delle immagini usando i dati di training
  • Assegnare un punteggio al modello usando i dati di test

Per ogni componente è necessario preparare quanto segue:

  1. Preparare lo script Python contenente la logica di esecuzione

  2. Definire l'interfaccia del componente

  3. Aggiungere altri metadati del componente, incluso l'ambiente di runtime, il comando per eseguire il componente e così via.

La sezione successiva mostrerà due modi diversi per creare i componenti: i primi due componenti tramite la funzione Python e il terzo componente tramite la definizione YAML.

Creare il componente di preparazione dei dati

Il primo componente di questa pipeline convertirà i file di dati compressi di fashion_ds in due file CSV, uno per il training e l'altro per l'assegnazione dei punteggi. Si userà la funzione Python per definire questo componente.

Se si segue l'esempio nel repository di esempi di Azure Machine Learning, i file di origine sono già disponibili nella cartella prep/. Questa cartella contiene due file per costruire il componente prep_component.py, che definisce il componente e conda.yaml, che definisce l'ambiente di runtime del componente.

Definire un componente usando la funzione Python

Usando la funzione command_component() come elemento Decorator, è possibile definire facilmente l'interfaccia, i metadati e il codice del componente da eseguire da una funzione Python. Ogni funzione Python decorata verrà trasformata in una singola specifica statica (YAML) che il servizio pipeline può elaborare.

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

Il codice precedente definisce un componente con nome visualizzato Prep Data usando l'elemento Decorator @command_component:

  • name è l'identificatore univoco del componente.

  • version è la versione corrente del componente. Un componente può avere più versioni.

  • display_name è un nome visualizzato descrittivo del componente nell'interfaccia utente, che non è univoco.

  • description descrive in genere l'attività che questo componente può completare.

  • environment specifica l'ambiente di runtime per questo componente. L'ambiente di questo componente specifica un'immagine Docker e fa riferimento al file conda.yaml.

    Il file conda.yaml contiene tutti i pacchetti usati per il componente come segue:

    name: imagekeras_prep_conda_env
    channels:
      - defaults
    dependencies:
      - python=3.7.11
      - pip=20.0
      - pip:
        - mldesigner==0.1.0b4
    
  • La funzione prepare_data_component definisce un input per input_data e due output per training_data e test_data. input_data è il percorso dei dati di input. training_data e test_data sono percorsi di dati di output per i dati di training e i dati di test.

  • Questo componente converte i dati da input_data in un file CSV di dati di training in training_data e un file CSV di dati di test in test_data.

Di seguito è riportato l'aspetto di un componente nell'interfaccia utente dello studio.

  • Un componente è un blocco in un grafico della pipeline.
  • input_data, training_data e test_data sono porte del componente, che si connette ad altri componenti per lo streaming dei dati.

Screenshot del componente Prep Data nell'interfaccia utente e nel codice.

A questo punto sono stati preparati tutti i file di origine per il componente Prep Data.

Creare il componente train-model

In questa sezione si creerà un componente per il training del modello di classificazione delle immagini nella funzione Python, ad esempio il componente Prep Data.

La differenza è che poiché la logica di training è più complessa, è possibile inserire il codice di training originale in un file Python separato.

I file di origine di questo componente si trovano nella cartella train/ nel repository di esempi di Azure Machine Learning. Questa cartella contiene tre file per costruire il componente:

  • train.py: contiene la logica effettiva per il training del modello.
  • train_component.py: definisce l'interfaccia del componente e importa la funzione in train.py.
  • conda.yaml: definisce l'ambiente di runtime del componente.

Ottenere uno script contenente la logica di esecuzione

Il file train.py contiene una normale funzione Python, che esegue la logica del modello di training per eseguire il training di una rete neurale Keras per la classificazione delle immagini. Per visualizzare il codice, vedere il file train.py in GitHub.

Definire un componente usando la funzione Python

Dopo aver definito correttamente la funzione di training, è possibile usare @command_component in Azure Machine Learning SDK v2 per eseguire il wrapping della funzione come componente, che può essere usato nelle pipeline di 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)

Il codice precedente definisce un componente con nome visualizzato Train Image Classification Keras usando @command_component:

  • La funzione keras_train_component definisce un input_data di input da cui provengono i dati di training, un epochs di input che specifica periodi durante il training e un output_model di output in cui restituisce il file del modello. Il valore predefinito di epochs è 10. La logica di esecuzione di questo componente proviene dalla funzione train() in train.py precedente.

Il componente train-model ha una configurazione leggermente più complessa rispetto al componente prep-data. conda.yaml è simile al seguente:

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

A questo punto sono stati preparati tutti i file di origine per il componente Train Image Classification Keras.

Creare il componente score-model

In questa sezione, oltre ai componenti precedenti, si creerà un componente per assegnare un punteggio al modello sottoposto a training tramite la specifica e lo script YAML.

Se si segue l'esempio nel repository di esempi di Azure Machine Learning, i file di origine sono già disponibili nella cartella score/. Questa cartella contiene tre file per costruire il componente:

  • score.py: contiene il codice sorgente del componente.
  • score.yaml: definisce l'interfaccia e altri dettagli del componente.
  • conda.yaml: definisce l'ambiente di runtime del componente.

Ottenere uno script contenente la logica di esecuzione

Il file score.py contiene una normale funzione Python, che esegue la logica del modello di training.

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)

Il codice in score.py accetta tre argomenti della riga di comando: input_data, input_model e output_result. Il programma assegna un punteggio al modello di input usando i dati di input e quindi restituisce il risultato dell'assegnazione dei punteggi.

Definire il componente tramite YAML

In questa sezione si apprenderà come creare una specifica del componente nel formato di specifica del componente YAML valido. Questo file specifica le informazioni seguenti:

  • Metadati: nome, display_name, versione, tipo e così via.
  • Interfaccia: input e output
  • Comando, codice e ambiente: comando, codice e ambiente usati per eseguire il 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 è l'identificatore univoco del componente. Il nome visualizzato è Score Image Classification Keras.
  • Questo componente ha due input e un output.
  • Il percorso del codice sorgente è definito nella sezione code e quando il componente viene eseguito nel cloud, tutti i file di tale percorso verranno caricati come snapshot di questo componente.
  • La sezione command specifica il comando da eseguire durante l'esecuzione di questo componente.
  • La sezione environment contiene un'immagine Docker e un file YAML conda. Il file di origine si trova nel repository di esempio.

Ora sono disponibili tutti i file di origine per il componente score-model.

Caricare i componenti nella pipeline di compilazione

Per il componente prep-data e il componente train-model definiti dalla funzione Python è possibile importare i componenti esattamente come le normali funzioni Python.

Nel codice seguente si importano rispettivamente le funzioni prepare_data_component() e keras_train_component() dal file prep_component.py nella cartella prep e dal file train_component nella cartella train.

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

Per il componente score definito da YAML, è possibile usare la funzione load_component() per il caricamento.

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

Creare la pipeline

Sono stati ora creati e caricati tutti i componenti e i dati di input per compilare la pipeline. È possibile comporli in una pipeline:

Nota

Per usare l'elaborazione serverless, aggiungere from azure.ai.ml.entities import ResourceConfiguration all'inizio. Sostituire quindi:

  • default_compute=cpu_compute_target, con default_compute="serverless",
  • train_node.compute = gpu_compute_target con 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)

La pipeline ha un ambiente di calcolo predefinito cpu_compute_target, ovvero se non si specifica l'ambiente di calcolo per un nodo specifico, tale nodo verrà eseguito nell'ambiente di calcolo predefinito.

La pipeline ha un input a livello di pipeline pipeline_input_data. È possibile assegnare valore all'input della pipeline quando si invia un processo della pipeline.

La pipeline contiene tre nodi, prepare_data_node, train_node e score_node.

  • input_data di prepare_data_node usa il valore di pipeline_input_data.

  • input_data di train_node proviene dall'output training_data di prepare_data_node.

  • input_data di score_node proviene dall'output test_data di prepare_data_node e input_model proviene da output_model di train_node.

  • Poiché train_node eseguirà il training di un modello CNN, è possibile specificarne l'ambiente di calcolo come gpu_compute_target, che può migliorare le prestazioni di training.

Inviare il processo della pipeline

Dopo aver costruito la pipeline, è possibile eseguire l'invio all'area di lavoro. Per inviare un processo, è necessario innanzitutto connettersi a un'area di lavoro.

Ottenere l'accesso all'area di lavoro

Configurare le credenziali

Si userà DefaultAzureCredential per ottenere l'accesso all'area di lavoro. DefaultAzureCredential deve essere in grado di gestire la maggior parte degli scenari di autenticazione di Azure SDK.

Se non funziona, vedere i riferimenti per altre credenziali disponibili: configure credential example, azure-identity reference doc.

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

Ottenere un handle in un'area di lavoro con ambiente di calcolo

Creare un oggetto MLClient per gestire i servizi di Azure Machine Learning. Se si usa l'elaborazione serverless, non è necessario creare questi ambienti di calcolo.

# 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

Questo frammento di codice prevede che il file JSON di configurazione dell'area di lavoro venga salvato nella directory corrente o nel relativo elemento padre. Per altre informazioni sulla creazione di un'area di lavoro, vedere Creare risorse dell'area di lavoro. Per altre informazioni sul salvataggio della configurazione in un file, vedere Creare un file di configurazione dell'area di lavoro.

Inviare un processo della pipeline all'area di lavoro

A questo punto si ottiene un handle per l'area di lavoro ed è possibile inviare il processo della pipeline.

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

Il codice precedente invia questo processo della pipeline di classificazione delle immagini all'esperimento denominato pipeline_samples. Se non esiste, l'esperimento verrà creato automaticamente. pipeline_input_data usa fashion_ds.

La chiamata a pipeline_job produce un output simile al seguente:

La chiamata a submit per Experiment viene completata rapidamente e produce un output simile al seguente:

Esperimento Nome Type Status Pagina dettagli
pipeline_samples sharp_pipe_4gvqx6h1fb pipeline Preparazione Collegamento allo studio di Azure Machine Learning.

È possibile monitorare l'esecuzione della pipeline aprendo il collegamento oppure è possibile bloccarlo fino al completamento eseguendo:

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

Importante

La prima esecuzione della pipeline richiede circa 15 minuti. Devono essere scaricate tutte le dipendenze, quindi viene creata un'immagine Docker e viene effettuato il provisioning o la creazione dell'ambiente Python. Una seconda esecuzione della pipeline richiede molto meno tempo, perché queste risorse vengono riutilizzate e non create. Tuttavia, il tempo di esecuzione totale dipende dal carico di lavoro degli script e dai processi in esecuzione in ogni passaggio della pipeline.

Estrarre output ed eseguire il debug della pipeline nell'interfaccia utente

È possibile aprire Link to Azure Machine Learning studio, ovvero la pagina dei dettagli del processo della pipeline. Il grafico della pipeline sarà simile al seguente.

Screenshot della pagina dei dettagli del processo della pipeline.

È possibile controllare i log e gli output di ogni componente facendo clic con il pulsante destro del mouse sul componente oppure selezionare il componente per aprire il relativo riquadro dei dettagli. Per altre informazioni su come eseguire il debug della pipeline nell'interfaccia utente, vedere Come usare la pagina per eseguire il debug degli errori della pipeline.

(Facoltativo) Registrare i componenti nell'area di lavoro

Nella sezione precedente è stata compilata una pipeline usando tre componenti per completare end-to-end un'attività di classificazione delle immagini. È anche possibile registrare i componenti nell'area di lavoro in modo che possano essere condivisi e riutilizzati all'interno dell'area di lavoro. Di seguito è riportato un esempio per registrare il 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() è possibile ottenere un componente registrato in base al nome e alla versione. Usando ml_client.components.create_or_update() è possibile registrare un componente caricato in precedenza dalla funzione Python o da YAML.

Passaggi successivi